@rocicorp/zero 1.11.0-canary.2 → 1.11.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 (832) hide show
  1. package/out/analyze-query/src/bin-analyze.js +9 -8
  2. package/out/analyze-query/src/bin-analyze.js.map +1 -1
  3. package/out/ast-to-zql/src/ast-to-zql.d.ts +8 -1
  4. package/out/ast-to-zql/src/ast-to-zql.d.ts.map +1 -1
  5. package/out/ast-to-zql/src/ast-to-zql.js +25 -25
  6. package/out/ast-to-zql/src/ast-to-zql.js.map +1 -1
  7. package/out/otel/src/log-options.d.ts +16 -0
  8. package/out/otel/src/log-options.d.ts.map +1 -1
  9. package/out/otel/src/log-options.js +54 -1
  10. package/out/otel/src/log-options.js.map +1 -1
  11. package/out/replicache/src/bg-interval.d.ts +2 -1
  12. package/out/replicache/src/bg-interval.d.ts.map +1 -1
  13. package/out/replicache/src/bg-interval.js +13 -4
  14. package/out/replicache/src/bg-interval.js.map +1 -1
  15. package/out/replicache/src/btree/node.d.ts.map +1 -1
  16. package/out/replicache/src/btree/node.js +3 -6
  17. package/out/replicache/src/btree/node.js.map +1 -1
  18. package/out/replicache/src/dag/gc.d.ts.map +1 -1
  19. package/out/replicache/src/dag/gc.js +8 -11
  20. package/out/replicache/src/dag/gc.js.map +1 -1
  21. package/out/replicache/src/dag/invalid-ref-count-error.d.ts +12 -0
  22. package/out/replicache/src/dag/invalid-ref-count-error.d.ts.map +1 -0
  23. package/out/replicache/src/dag/invalid-ref-count-error.js +19 -0
  24. package/out/replicache/src/dag/invalid-ref-count-error.js.map +1 -0
  25. package/out/replicache/src/dag/lazy-store.d.ts +17 -4
  26. package/out/replicache/src/dag/lazy-store.d.ts.map +1 -1
  27. package/out/replicache/src/dag/lazy-store.js +35 -9
  28. package/out/replicache/src/dag/lazy-store.js.map +1 -1
  29. package/out/replicache/src/dag/store-impl.d.ts +12 -2
  30. package/out/replicache/src/dag/store-impl.d.ts.map +1 -1
  31. package/out/replicache/src/dag/store-impl.js +29 -9
  32. package/out/replicache/src/dag/store-impl.js.map +1 -1
  33. package/out/replicache/src/db/rebase.d.ts +14 -2
  34. package/out/replicache/src/db/rebase.d.ts.map +1 -1
  35. package/out/replicache/src/db/rebase.js +27 -5
  36. package/out/replicache/src/db/rebase.js.map +1 -1
  37. package/out/replicache/src/get-kv-store-provider.d.ts +1 -1
  38. package/out/replicache/src/get-kv-store-provider.d.ts.map +1 -1
  39. package/out/replicache/src/get-kv-store-provider.js +4 -4
  40. package/out/replicache/src/get-kv-store-provider.js.map +1 -1
  41. package/out/replicache/src/kv/expo-sqlite/store.d.ts.map +1 -1
  42. package/out/replicache/src/kv/expo-sqlite/store.js +28 -5
  43. package/out/replicache/src/kv/expo-sqlite/store.js.map +1 -1
  44. package/out/replicache/src/kv/idb-store.d.ts +18 -0
  45. package/out/replicache/src/kv/idb-store.d.ts.map +1 -1
  46. package/out/replicache/src/kv/idb-store.js +30 -2
  47. package/out/replicache/src/kv/idb-store.js.map +1 -1
  48. package/out/replicache/src/kv/mem-fallback-store.d.ts +54 -0
  49. package/out/replicache/src/kv/mem-fallback-store.d.ts.map +1 -0
  50. package/out/replicache/src/kv/mem-fallback-store.js +177 -0
  51. package/out/replicache/src/kv/mem-fallback-store.js.map +1 -0
  52. package/out/replicache/src/kv/mem-store.d.ts +1 -0
  53. package/out/replicache/src/kv/mem-store.d.ts.map +1 -1
  54. package/out/replicache/src/kv/mem-store.js +3 -0
  55. package/out/replicache/src/kv/mem-store.js.map +1 -1
  56. package/out/replicache/src/kv/op-sqlite/store.d.ts.map +1 -1
  57. package/out/replicache/src/kv/op-sqlite/store.js +4 -4
  58. package/out/replicache/src/kv/op-sqlite/store.js.map +1 -1
  59. package/out/replicache/src/kv/sqlite-store.d.ts +22 -1
  60. package/out/replicache/src/kv/sqlite-store.d.ts.map +1 -1
  61. package/out/replicache/src/kv/sqlite-store.js +159 -14
  62. package/out/replicache/src/kv/sqlite-store.js.map +1 -1
  63. package/out/replicache/src/kv/store.d.ts +6 -0
  64. package/out/replicache/src/kv/store.d.ts.map +1 -1
  65. package/out/replicache/src/new-client-channel.js.map +1 -1
  66. package/out/replicache/src/persist/client-gc.d.ts +2 -1
  67. package/out/replicache/src/persist/client-gc.d.ts.map +1 -1
  68. package/out/replicache/src/persist/client-gc.js +2 -2
  69. package/out/replicache/src/persist/client-gc.js.map +1 -1
  70. package/out/replicache/src/persist/client-group-gc.d.ts +2 -1
  71. package/out/replicache/src/persist/client-group-gc.d.ts.map +1 -1
  72. package/out/replicache/src/persist/client-group-gc.js +2 -2
  73. package/out/replicache/src/persist/client-group-gc.js.map +1 -1
  74. package/out/replicache/src/persist/collect-idb-databases.d.ts +28 -8
  75. package/out/replicache/src/persist/collect-idb-databases.d.ts.map +1 -1
  76. package/out/replicache/src/persist/collect-idb-databases.js +97 -20
  77. package/out/replicache/src/persist/collect-idb-databases.js.map +1 -1
  78. package/out/replicache/src/persist/heartbeat.d.ts +2 -1
  79. package/out/replicache/src/persist/heartbeat.d.ts.map +1 -1
  80. package/out/replicache/src/persist/heartbeat.js +2 -2
  81. package/out/replicache/src/persist/heartbeat.js.map +1 -1
  82. package/out/replicache/src/persist/idb-databases-store-db-name.js +1 -1
  83. package/out/replicache/src/persist/idb-databases-store-db-name.js.map +1 -1
  84. package/out/replicache/src/persist/persist.d.ts.map +1 -1
  85. package/out/replicache/src/persist/persist.js +18 -8
  86. package/out/replicache/src/persist/persist.js.map +1 -1
  87. package/out/replicache/src/persist/refresh.js +6 -2
  88. package/out/replicache/src/persist/refresh.js.map +1 -1
  89. package/out/replicache/src/replicache-impl.d.ts +85 -3
  90. package/out/replicache/src/replicache-impl.d.ts.map +1 -1
  91. package/out/replicache/src/replicache-impl.js +321 -29
  92. package/out/replicache/src/replicache-impl.js.map +1 -1
  93. package/out/replicache/src/replicache-options.d.ts +9 -0
  94. package/out/replicache/src/replicache-options.d.ts.map +1 -1
  95. package/out/replicache/src/storage-failure.d.ts +37 -0
  96. package/out/replicache/src/storage-failure.d.ts.map +1 -0
  97. package/out/replicache/src/storage-failure.js +31 -0
  98. package/out/replicache/src/storage-failure.js.map +1 -0
  99. package/out/shared/src/arrays.d.ts +5 -0
  100. package/out/shared/src/arrays.d.ts.map +1 -1
  101. package/out/shared/src/arrays.js +8 -10
  102. package/out/shared/src/arrays.js.map +1 -1
  103. package/out/shared/src/error.d.ts +11 -0
  104. package/out/shared/src/error.d.ts.map +1 -1
  105. package/out/shared/src/error.js +35 -1
  106. package/out/shared/src/error.js.map +1 -1
  107. package/out/shared/src/iterables.d.ts +2 -0
  108. package/out/shared/src/iterables.d.ts.map +1 -1
  109. package/out/shared/src/iterables.js +18 -1
  110. package/out/shared/src/iterables.js.map +1 -1
  111. package/out/shared/src/map.d.ts +24 -2
  112. package/out/shared/src/map.d.ts.map +1 -1
  113. package/out/shared/src/map.js +18 -4
  114. package/out/shared/src/map.js.map +1 -1
  115. package/out/shared/src/random-values.js +12 -1
  116. package/out/shared/src/random-values.js.map +1 -1
  117. package/out/shared/src/ring-buffer.d.ts +10 -0
  118. package/out/shared/src/ring-buffer.d.ts.map +1 -1
  119. package/out/shared/src/ring-buffer.js +26 -1
  120. package/out/shared/src/ring-buffer.js.map +1 -1
  121. package/out/z2s/src/compiler.js +1 -1
  122. package/out/z2s/src/sql.d.ts.map +1 -1
  123. package/out/z2s/src/sql.js +2 -2
  124. package/out/z2s/src/sql.js.map +1 -1
  125. package/out/zero/package.js +3 -4
  126. package/out/zero/package.js.map +1 -1
  127. package/out/zero/src/zero.js +2 -1
  128. package/out/zero-cache/src/auth/write-authorizer.d.ts.map +1 -1
  129. package/out/zero-cache/src/auth/write-authorizer.js +1 -0
  130. package/out/zero-cache/src/auth/write-authorizer.js.map +1 -1
  131. package/out/zero-cache/src/config/normalize.d.ts.map +1 -1
  132. package/out/zero-cache/src/config/normalize.js +26 -2
  133. package/out/zero-cache/src/config/normalize.js.map +1 -1
  134. package/out/zero-cache/src/config/zero-config.d.ts +102 -6
  135. package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
  136. package/out/zero-cache/src/config/zero-config.js +254 -57
  137. package/out/zero-cache/src/config/zero-config.js.map +1 -1
  138. package/out/zero-cache/src/db/lite-tables.d.ts.map +1 -1
  139. package/out/zero-cache/src/db/lite-tables.js +3 -4
  140. package/out/zero-cache/src/db/lite-tables.js.map +1 -1
  141. package/out/zero-cache/src/observability/events.js +4 -1
  142. package/out/zero-cache/src/observability/events.js.map +1 -1
  143. package/out/zero-cache/src/observability/log-throttle.d.ts +44 -0
  144. package/out/zero-cache/src/observability/log-throttle.d.ts.map +1 -0
  145. package/out/zero-cache/src/observability/log-throttle.js +76 -0
  146. package/out/zero-cache/src/observability/log-throttle.js.map +1 -0
  147. package/out/zero-cache/src/observability/metrics.d.ts.map +1 -1
  148. package/out/zero-cache/src/observability/metrics.js +2 -7
  149. package/out/zero-cache/src/observability/metrics.js.map +1 -1
  150. package/out/zero-cache/src/scripts/permissions.d.ts +16 -0
  151. package/out/zero-cache/src/scripts/permissions.d.ts.map +1 -1
  152. package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
  153. package/out/zero-cache/src/server/change-streamer.js +27 -10
  154. package/out/zero-cache/src/server/change-streamer.js.map +1 -1
  155. package/out/zero-cache/src/server/inspector-delegate.d.ts +28 -2
  156. package/out/zero-cache/src/server/inspector-delegate.d.ts.map +1 -1
  157. package/out/zero-cache/src/server/inspector-delegate.js +37 -17
  158. package/out/zero-cache/src/server/inspector-delegate.js.map +1 -1
  159. package/out/zero-cache/src/server/main.d.ts.map +1 -1
  160. package/out/zero-cache/src/server/main.js +8 -1
  161. package/out/zero-cache/src/server/main.js.map +1 -1
  162. package/out/zero-cache/src/server/mutator.js +2 -0
  163. package/out/zero-cache/src/server/mutator.js.map +1 -1
  164. package/out/zero-cache/src/server/reaper.d.ts.map +1 -1
  165. package/out/zero-cache/src/server/reaper.js +2 -0
  166. package/out/zero-cache/src/server/reaper.js.map +1 -1
  167. package/out/zero-cache/src/server/replicator.d.ts +3 -0
  168. package/out/zero-cache/src/server/replicator.d.ts.map +1 -1
  169. package/out/zero-cache/src/server/replicator.js +79 -15
  170. package/out/zero-cache/src/server/replicator.js.map +1 -1
  171. package/out/zero-cache/src/server/runner/zero-dispatcher.d.ts.map +1 -1
  172. package/out/zero-cache/src/server/runner/zero-dispatcher.js +5 -0
  173. package/out/zero-cache/src/server/runner/zero-dispatcher.js.map +1 -1
  174. package/out/zero-cache/src/server/syncer-assigner.d.ts +14 -0
  175. package/out/zero-cache/src/server/syncer-assigner.d.ts.map +1 -0
  176. package/out/zero-cache/src/server/syncer-assigner.js +72 -0
  177. package/out/zero-cache/src/server/syncer-assigner.js.map +1 -0
  178. package/out/zero-cache/src/server/syncer.d.ts.map +1 -1
  179. package/out/zero-cache/src/server/syncer.js +24 -2
  180. package/out/zero-cache/src/server/syncer.js.map +1 -1
  181. package/out/zero-cache/src/server/worker-dispatcher.d.ts.map +1 -1
  182. package/out/zero-cache/src/server/worker-dispatcher.js +21 -4
  183. package/out/zero-cache/src/server/worker-dispatcher.js.map +1 -1
  184. package/out/zero-cache/src/services/analyze.d.ts.map +1 -1
  185. package/out/zero-cache/src/services/analyze.js +11 -9
  186. package/out/zero-cache/src/services/analyze.js.map +1 -1
  187. package/out/zero-cache/src/services/change-source/common/backfill-manager.d.ts.map +1 -1
  188. package/out/zero-cache/src/services/change-source/common/backfill-manager.js +198 -24
  189. package/out/zero-cache/src/services/change-source/common/backfill-manager.js.map +1 -1
  190. package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.d.ts.map +1 -1
  191. package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.js +15 -3
  192. package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.js.map +1 -1
  193. package/out/zero-cache/src/services/change-source/common/replica-schema.d.ts +1 -0
  194. package/out/zero-cache/src/services/change-source/common/replica-schema.d.ts.map +1 -1
  195. package/out/zero-cache/src/services/change-source/common/replica-schema.js +15 -3
  196. package/out/zero-cache/src/services/change-source/common/replica-schema.js.map +1 -1
  197. package/out/zero-cache/src/services/change-source/pg/backfill-stream.d.ts +9 -0
  198. package/out/zero-cache/src/services/change-source/pg/backfill-stream.d.ts.map +1 -1
  199. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js +204 -18
  200. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js.map +1 -1
  201. package/out/zero-cache/src/services/change-source/pg/change-source-init.d.ts +25 -0
  202. package/out/zero-cache/src/services/change-source/pg/change-source-init.d.ts.map +1 -0
  203. package/out/zero-cache/src/services/change-source/pg/change-source-init.js +202 -0
  204. package/out/zero-cache/src/services/change-source/pg/change-source-init.js.map +1 -0
  205. package/out/zero-cache/src/services/change-source/pg/change-source.d.ts +14 -16
  206. package/out/zero-cache/src/services/change-source/pg/change-source.d.ts.map +1 -1
  207. package/out/zero-cache/src/services/change-source/pg/change-source.js +64 -143
  208. package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
  209. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts +37 -2
  210. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts.map +1 -1
  211. package/out/zero-cache/src/services/change-source/pg/initial-sync.js +53 -21
  212. package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
  213. package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.d.ts +16 -0
  214. package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.d.ts.map +1 -0
  215. package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.js +113 -0
  216. package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.js.map +1 -0
  217. package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts +21 -15
  218. package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts.map +1 -1
  219. package/out/zero-cache/src/services/change-source/pg/replication-slots.js +86 -23
  220. package/out/zero-cache/src/services/change-source/pg/replication-slots.js.map +1 -1
  221. package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.d.ts.map +1 -1
  222. package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.js +68 -8
  223. package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.js.map +1 -1
  224. package/out/zero-cache/src/services/change-source/pg/schema/init.d.ts.map +1 -1
  225. package/out/zero-cache/src/services/change-source/pg/schema/init.js +32 -1
  226. package/out/zero-cache/src/services/change-source/pg/schema/init.js.map +1 -1
  227. package/out/zero-cache/src/services/change-source/pg/schema/replica-stage-enum.d.ts +10 -0
  228. package/out/zero-cache/src/services/change-source/pg/schema/replica-stage-enum.d.ts.map +1 -0
  229. package/out/zero-cache/src/services/change-source/pg/schema/replica-stage-enum.js +6 -0
  230. package/out/zero-cache/src/services/change-source/pg/schema/replica-stage-enum.js.map +1 -0
  231. package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts +50 -27
  232. package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts.map +1 -1
  233. package/out/zero-cache/src/services/change-source/pg/schema/shard.js +100 -34
  234. package/out/zero-cache/src/services/change-source/pg/schema/shard.js.map +1 -1
  235. package/out/zero-cache/src/services/change-source/protocol/backfill-progress.d.ts +68 -0
  236. package/out/zero-cache/src/services/change-source/protocol/backfill-progress.d.ts.map +1 -0
  237. package/out/zero-cache/src/services/change-source/protocol/backfill-progress.js +141 -0
  238. package/out/zero-cache/src/services/change-source/protocol/backfill-progress.js.map +1 -0
  239. package/out/zero-cache/src/services/change-source/protocol/current/data.d.ts +43 -0
  240. package/out/zero-cache/src/services/change-source/protocol/current/data.d.ts.map +1 -1
  241. package/out/zero-cache/src/services/change-source/protocol/current/data.js +10 -1
  242. package/out/zero-cache/src/services/change-source/protocol/current/data.js.map +1 -1
  243. package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts +57 -0
  244. package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts.map +1 -1
  245. package/out/zero-cache/src/services/change-source/protocol/current/upstream.d.ts +20 -6
  246. package/out/zero-cache/src/services/change-source/protocol/current/upstream.d.ts.map +1 -1
  247. package/out/zero-cache/src/services/change-source/protocol/current/upstream.js +17 -7
  248. package/out/zero-cache/src/services/change-source/protocol/current/upstream.js.map +1 -1
  249. package/out/zero-cache/src/services/change-source/protocol/current.js +2 -1
  250. package/out/zero-cache/src/services/change-streamer/backfill-state-tracker.d.ts +66 -0
  251. package/out/zero-cache/src/services/change-streamer/backfill-state-tracker.d.ts.map +1 -0
  252. package/out/zero-cache/src/services/change-streamer/backfill-state-tracker.js +154 -0
  253. package/out/zero-cache/src/services/change-streamer/backfill-state-tracker.js.map +1 -0
  254. package/out/zero-cache/src/services/change-streamer/backfill-state.d.ts +50 -0
  255. package/out/zero-cache/src/services/change-streamer/backfill-state.d.ts.map +1 -0
  256. package/out/zero-cache/src/services/change-streamer/backfill-state.js +268 -0
  257. package/out/zero-cache/src/services/change-streamer/backfill-state.js.map +1 -0
  258. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.d.ts.map +1 -1
  259. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.js +2 -0
  260. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.js.map +1 -1
  261. package/out/zero-cache/src/services/change-streamer/backup-monitor.d.ts.map +1 -1
  262. package/out/zero-cache/src/services/change-streamer/backup-monitor.js +58 -7
  263. package/out/zero-cache/src/services/change-streamer/backup-monitor.js.map +1 -1
  264. package/out/zero-cache/src/services/change-streamer/broadcast.d.ts +8 -2
  265. package/out/zero-cache/src/services/change-streamer/broadcast.d.ts.map +1 -1
  266. package/out/zero-cache/src/services/change-streamer/broadcast.js +24 -4
  267. package/out/zero-cache/src/services/change-streamer/broadcast.js.map +1 -1
  268. package/out/zero-cache/src/services/change-streamer/change-log-codec.d.ts +19 -4
  269. package/out/zero-cache/src/services/change-streamer/change-log-codec.d.ts.map +1 -1
  270. package/out/zero-cache/src/services/change-streamer/change-log-codec.js +28 -8
  271. package/out/zero-cache/src/services/change-streamer/change-log-codec.js.map +1 -1
  272. package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts +48 -6
  273. package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts.map +1 -1
  274. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +130 -34
  275. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
  276. package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts +11 -0
  277. package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts.map +1 -1
  278. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +118 -68
  279. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
  280. package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts +37 -15
  281. package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts.map +1 -1
  282. package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
  283. package/out/zero-cache/src/services/change-streamer/forwarder.d.ts +3 -3
  284. package/out/zero-cache/src/services/change-streamer/forwarder.d.ts.map +1 -1
  285. package/out/zero-cache/src/services/change-streamer/forwarder.js +36 -5
  286. package/out/zero-cache/src/services/change-streamer/forwarder.js.map +1 -1
  287. package/out/zero-cache/src/services/change-streamer/litestream-sync-requester.d.ts +51 -0
  288. package/out/zero-cache/src/services/change-streamer/litestream-sync-requester.d.ts.map +1 -0
  289. package/out/zero-cache/src/services/change-streamer/litestream-sync-requester.js +132 -0
  290. package/out/zero-cache/src/services/change-streamer/litestream-sync-requester.js.map +1 -0
  291. package/out/zero-cache/src/services/change-streamer/schema/tables.d.ts +1 -1
  292. package/out/zero-cache/src/services/change-streamer/schema/tables.d.ts.map +1 -1
  293. package/out/zero-cache/src/services/change-streamer/schema/tables.js +19 -8
  294. package/out/zero-cache/src/services/change-streamer/schema/tables.js.map +1 -1
  295. package/out/zero-cache/src/services/change-streamer/snapshot-message.d.ts +92 -0
  296. package/out/zero-cache/src/services/change-streamer/snapshot-message.d.ts.map +1 -0
  297. package/out/zero-cache/src/services/change-streamer/snapshot-message.js +42 -0
  298. package/out/zero-cache/src/services/change-streamer/snapshot-message.js.map +1 -0
  299. package/out/zero-cache/src/services/change-streamer/snapshot-reservations.d.ts +1 -1
  300. package/out/zero-cache/src/services/change-streamer/snapshot-reservations.d.ts.map +1 -1
  301. package/out/zero-cache/src/services/change-streamer/snapshot-reservations.js +16 -4
  302. package/out/zero-cache/src/services/change-streamer/snapshot-reservations.js.map +1 -1
  303. package/out/zero-cache/src/services/change-streamer/snapshot.d.ts +7 -69
  304. package/out/zero-cache/src/services/change-streamer/snapshot.d.ts.map +1 -1
  305. package/out/zero-cache/src/services/change-streamer/snapshot.js +21 -53
  306. package/out/zero-cache/src/services/change-streamer/snapshot.js.map +1 -1
  307. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.d.ts +6 -1
  308. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.d.ts.map +1 -1
  309. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.js +27 -9
  310. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.js.map +1 -1
  311. package/out/zero-cache/src/services/change-streamer/storer.d.ts +2 -2
  312. package/out/zero-cache/src/services/change-streamer/storer.d.ts.map +1 -1
  313. package/out/zero-cache/src/services/change-streamer/storer.js +9 -6
  314. package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
  315. package/out/zero-cache/src/services/change-streamer/subscribe.d.ts +446 -0
  316. package/out/zero-cache/src/services/change-streamer/subscribe.d.ts.map +1 -0
  317. package/out/zero-cache/src/services/change-streamer/subscribe.js +76 -0
  318. package/out/zero-cache/src/services/change-streamer/subscribe.js.map +1 -0
  319. package/out/zero-cache/src/services/change-streamer/subscriber.d.ts +33 -1
  320. package/out/zero-cache/src/services/change-streamer/subscriber.d.ts.map +1 -1
  321. package/out/zero-cache/src/services/change-streamer/subscriber.js +76 -0
  322. package/out/zero-cache/src/services/change-streamer/subscriber.js.map +1 -1
  323. package/out/zero-cache/src/services/change-streamer/upstream-acker.d.ts +17 -1
  324. package/out/zero-cache/src/services/change-streamer/upstream-acker.d.ts.map +1 -1
  325. package/out/zero-cache/src/services/change-streamer/upstream-acker.js +19 -3
  326. package/out/zero-cache/src/services/change-streamer/upstream-acker.js.map +1 -1
  327. package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.d.ts +5 -0
  328. package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.d.ts.map +1 -1
  329. package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.js +35 -5
  330. package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.js.map +1 -1
  331. package/out/zero-cache/src/services/life-cycle.d.ts.map +1 -1
  332. package/out/zero-cache/src/services/life-cycle.js +3 -1
  333. package/out/zero-cache/src/services/life-cycle.js.map +1 -1
  334. package/out/zero-cache/src/services/litestream/commands.d.ts +15 -3
  335. package/out/zero-cache/src/services/litestream/commands.d.ts.map +1 -1
  336. package/out/zero-cache/src/services/litestream/commands.js +24 -7
  337. package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
  338. package/out/zero-cache/src/services/litestream/config-v5.yml +11 -4
  339. package/out/zero-cache/src/services/litestream/litestream-checkpointer.js +51 -24
  340. package/out/zero-cache/src/services/litestream/litestream-checkpointer.js.map +1 -1
  341. package/out/zero-cache/src/services/litestream/litestream-controller.d.ts +10 -2
  342. package/out/zero-cache/src/services/litestream/litestream-controller.d.ts.map +1 -1
  343. package/out/zero-cache/src/services/litestream/litestream-controller.js +3 -2
  344. package/out/zero-cache/src/services/litestream/litestream-controller.js.map +1 -1
  345. package/out/zero-cache/src/services/litestream/restore-progress.d.ts +24 -0
  346. package/out/zero-cache/src/services/litestream/restore-progress.d.ts.map +1 -0
  347. package/out/zero-cache/src/services/litestream/restore-progress.js +91 -0
  348. package/out/zero-cache/src/services/litestream/restore-progress.js.map +1 -0
  349. package/out/zero-cache/src/services/mutagen/mutagen.d.ts +5 -0
  350. package/out/zero-cache/src/services/mutagen/mutagen.d.ts.map +1 -1
  351. package/out/zero-cache/src/services/mutagen/mutagen.js +7 -1
  352. package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
  353. package/out/zero-cache/src/services/mutagen/pusher.d.ts.map +1 -1
  354. package/out/zero-cache/src/services/mutagen/pusher.js +5 -6
  355. package/out/zero-cache/src/services/mutagen/pusher.js.map +1 -1
  356. package/out/zero-cache/src/services/plannerz.d.ts +163 -0
  357. package/out/zero-cache/src/services/plannerz.d.ts.map +1 -0
  358. package/out/zero-cache/src/services/plannerz.js +267 -0
  359. package/out/zero-cache/src/services/plannerz.js.map +1 -0
  360. package/out/zero-cache/src/services/profz.d.ts +30 -0
  361. package/out/zero-cache/src/services/profz.d.ts.map +1 -0
  362. package/out/zero-cache/src/services/profz.js +145 -0
  363. package/out/zero-cache/src/services/profz.js.map +1 -0
  364. package/out/zero-cache/src/services/replicator/change-log-cookies.js +1 -1
  365. package/out/zero-cache/src/services/replicator/change-log-cookies.js.map +1 -1
  366. package/out/zero-cache/src/services/replicator/change-log-stream-writer.d.ts +5 -4
  367. package/out/zero-cache/src/services/replicator/change-log-stream-writer.d.ts.map +1 -1
  368. package/out/zero-cache/src/services/replicator/change-log-stream-writer.js +20 -22
  369. package/out/zero-cache/src/services/replicator/change-log-stream-writer.js.map +1 -1
  370. package/out/zero-cache/src/services/replicator/change-processor.d.ts.map +1 -1
  371. package/out/zero-cache/src/services/replicator/change-processor.js +49 -5
  372. package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
  373. package/out/zero-cache/src/services/replicator/incremental-sync.d.ts +3 -2
  374. package/out/zero-cache/src/services/replicator/incremental-sync.d.ts.map +1 -1
  375. package/out/zero-cache/src/services/replicator/incremental-sync.js +50 -20
  376. package/out/zero-cache/src/services/replicator/incremental-sync.js.map +1 -1
  377. package/out/zero-cache/src/services/replicator/replication-status.d.ts +35 -1
  378. package/out/zero-cache/src/services/replicator/replication-status.d.ts.map +1 -1
  379. package/out/zero-cache/src/services/replicator/replication-status.js +96 -6
  380. package/out/zero-cache/src/services/replicator/replication-status.js.map +1 -1
  381. package/out/zero-cache/src/services/replicator/replicator.d.ts +2 -1
  382. package/out/zero-cache/src/services/replicator/replicator.d.ts.map +1 -1
  383. package/out/zero-cache/src/services/replicator/replicator.js +2 -2
  384. package/out/zero-cache/src/services/replicator/replicator.js.map +1 -1
  385. package/out/zero-cache/src/services/replicator/schema/backfilling.d.ts +23 -2
  386. package/out/zero-cache/src/services/replicator/schema/backfilling.d.ts.map +1 -1
  387. package/out/zero-cache/src/services/replicator/schema/backfilling.js +54 -2
  388. package/out/zero-cache/src/services/replicator/schema/backfilling.js.map +1 -1
  389. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.d.ts +6 -6
  390. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.d.ts.map +1 -1
  391. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js +19 -14
  392. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js.map +1 -1
  393. package/out/zero-cache/src/services/replicator/write-worker-client.d.ts +11 -3
  394. package/out/zero-cache/src/services/replicator/write-worker-client.d.ts.map +1 -1
  395. package/out/zero-cache/src/services/replicator/write-worker-client.js.map +1 -1
  396. package/out/zero-cache/src/services/replicator/write-worker.js +5 -1
  397. package/out/zero-cache/src/services/replicator/write-worker.js.map +1 -1
  398. package/out/zero-cache/src/services/run-ast.d.ts +12 -0
  399. package/out/zero-cache/src/services/run-ast.d.ts.map +1 -1
  400. package/out/zero-cache/src/services/run-ast.js +26 -14
  401. package/out/zero-cache/src/services/run-ast.js.map +1 -1
  402. package/out/zero-cache/src/services/runner.d.ts +2 -1
  403. package/out/zero-cache/src/services/runner.d.ts.map +1 -1
  404. package/out/zero-cache/src/services/runner.js +10 -2
  405. package/out/zero-cache/src/services/runner.js.map +1 -1
  406. package/out/zero-cache/src/services/statz.d.ts +4 -0
  407. package/out/zero-cache/src/services/statz.d.ts.map +1 -1
  408. package/out/zero-cache/src/services/statz.js +22 -5
  409. package/out/zero-cache/src/services/statz.js.map +1 -1
  410. package/out/zero-cache/src/services/view-syncer/client-handler.d.ts +19 -2
  411. package/out/zero-cache/src/services/view-syncer/client-handler.d.ts.map +1 -1
  412. package/out/zero-cache/src/services/view-syncer/client-handler.js +42 -30
  413. package/out/zero-cache/src/services/view-syncer/client-handler.js.map +1 -1
  414. package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts +19 -1
  415. package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts.map +1 -1
  416. package/out/zero-cache/src/services/view-syncer/cvr-store.js +34 -11
  417. package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
  418. package/out/zero-cache/src/services/view-syncer/cvr.d.ts +51 -2
  419. package/out/zero-cache/src/services/view-syncer/cvr.d.ts.map +1 -1
  420. package/out/zero-cache/src/services/view-syncer/cvr.js +142 -6
  421. package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
  422. package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.d.ts +81 -0
  423. package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.d.ts.map +1 -0
  424. package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.js +128 -0
  425. package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.js.map +1 -0
  426. package/out/zero-cache/src/services/view-syncer/drain-coordinator.d.ts +14 -1
  427. package/out/zero-cache/src/services/view-syncer/drain-coordinator.d.ts.map +1 -1
  428. package/out/zero-cache/src/services/view-syncer/drain-coordinator.js +30 -4
  429. package/out/zero-cache/src/services/view-syncer/drain-coordinator.js.map +1 -1
  430. package/out/zero-cache/src/services/view-syncer/hydration-budget.d.ts +33 -0
  431. package/out/zero-cache/src/services/view-syncer/hydration-budget.d.ts.map +1 -0
  432. package/out/zero-cache/src/services/view-syncer/hydration-budget.js +62 -0
  433. package/out/zero-cache/src/services/view-syncer/hydration-budget.js.map +1 -0
  434. package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.d.ts +51 -0
  435. package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.d.ts.map +1 -0
  436. package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.js +78 -0
  437. package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.js.map +1 -0
  438. package/out/zero-cache/src/services/view-syncer/inspect-handler.d.ts +1 -1
  439. package/out/zero-cache/src/services/view-syncer/inspect-handler.d.ts.map +1 -1
  440. package/out/zero-cache/src/services/view-syncer/inspect-handler.js +3 -3
  441. package/out/zero-cache/src/services/view-syncer/inspect-handler.js.map +1 -1
  442. package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts +50 -2
  443. package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts.map +1 -1
  444. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +471 -62
  445. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
  446. package/out/zero-cache/src/services/view-syncer/plan-warnings.d.ts +7 -0
  447. package/out/zero-cache/src/services/view-syncer/plan-warnings.d.ts.map +1 -0
  448. package/out/zero-cache/src/services/view-syncer/plan-warnings.js +35 -0
  449. package/out/zero-cache/src/services/view-syncer/plan-warnings.js.map +1 -0
  450. package/out/zero-cache/src/services/view-syncer/query-covering.d.ts.map +1 -1
  451. package/out/zero-cache/src/services/view-syncer/query-covering.js +2 -6
  452. package/out/zero-cache/src/services/view-syncer/query-covering.js.map +1 -1
  453. package/out/zero-cache/src/services/view-syncer/query-shape.d.ts +25 -0
  454. package/out/zero-cache/src/services/view-syncer/query-shape.d.ts.map +1 -0
  455. package/out/zero-cache/src/services/view-syncer/query-shape.js +28 -0
  456. package/out/zero-cache/src/services/view-syncer/query-shape.js.map +1 -0
  457. package/out/zero-cache/src/services/view-syncer/query-stats.d.ts +77 -0
  458. package/out/zero-cache/src/services/view-syncer/query-stats.d.ts.map +1 -0
  459. package/out/zero-cache/src/services/view-syncer/query-stats.js +206 -0
  460. package/out/zero-cache/src/services/view-syncer/query-stats.js.map +1 -0
  461. package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.d.ts +95 -0
  462. package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.d.ts.map +1 -0
  463. package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.js +155 -0
  464. package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.js.map +1 -0
  465. package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts +78 -4
  466. package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts.map +1 -1
  467. package/out/zero-cache/src/services/view-syncer/snapshotter.js +158 -28
  468. package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
  469. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts +11 -1
  470. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
  471. package/out/zero-cache/src/services/view-syncer/view-syncer.js +587 -103
  472. package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
  473. package/out/zero-cache/src/types/processes.d.ts +39 -0
  474. package/out/zero-cache/src/types/processes.d.ts.map +1 -1
  475. package/out/zero-cache/src/types/processes.js +43 -4
  476. package/out/zero-cache/src/types/processes.js.map +1 -1
  477. package/out/zero-cache/src/types/profiler.d.ts +12 -0
  478. package/out/zero-cache/src/types/profiler.d.ts.map +1 -1
  479. package/out/zero-cache/src/types/profiler.js +46 -1
  480. package/out/zero-cache/src/types/profiler.js.map +1 -1
  481. package/out/zero-cache/src/types/row-key.d.ts.map +1 -1
  482. package/out/zero-cache/src/types/row-key.js +5 -5
  483. package/out/zero-cache/src/types/row-key.js.map +1 -1
  484. package/out/zero-cache/src/types/streams.d.ts +49 -7
  485. package/out/zero-cache/src/types/streams.d.ts.map +1 -1
  486. package/out/zero-cache/src/types/streams.js +190 -58
  487. package/out/zero-cache/src/types/streams.js.map +1 -1
  488. package/out/zero-cache/src/types/subscription.d.ts +15 -0
  489. package/out/zero-cache/src/types/subscription.d.ts.map +1 -1
  490. package/out/zero-cache/src/types/subscription.js +123 -31
  491. package/out/zero-cache/src/types/subscription.js.map +1 -1
  492. package/out/zero-cache/src/types/websocket-handoff.d.ts +1 -1
  493. package/out/zero-cache/src/types/websocket-handoff.d.ts.map +1 -1
  494. package/out/zero-cache/src/types/websocket-handoff.js +18 -9
  495. package/out/zero-cache/src/types/websocket-handoff.js.map +1 -1
  496. package/out/zero-cache/src/types/ws.d.ts.map +1 -1
  497. package/out/zero-cache/src/types/ws.js +1 -16
  498. package/out/zero-cache/src/types/ws.js.map +1 -1
  499. package/out/zero-cache/src/workers/connect-params.d.ts +1 -0
  500. package/out/zero-cache/src/workers/connect-params.d.ts.map +1 -1
  501. package/out/zero-cache/src/workers/connect-params.js.map +1 -1
  502. package/out/zero-cache/src/workers/syncer-ws-message-handler.d.ts.map +1 -1
  503. package/out/zero-cache/src/workers/syncer-ws-message-handler.js +21 -16
  504. package/out/zero-cache/src/workers/syncer-ws-message-handler.js.map +1 -1
  505. package/out/zero-cache/src/workers/syncer.d.ts.map +1 -1
  506. package/out/zero-cache/src/workers/syncer.js +55 -4
  507. package/out/zero-cache/src/workers/syncer.js.map +1 -1
  508. package/out/zero-client/src/client/active-clients-manager.d.ts.map +1 -1
  509. package/out/zero-client/src/client/active-clients-manager.js +4 -1
  510. package/out/zero-client/src/client/active-clients-manager.js.map +1 -1
  511. package/out/zero-client/src/client/connection-manager.d.ts +20 -0
  512. package/out/zero-client/src/client/connection-manager.d.ts.map +1 -1
  513. package/out/zero-client/src/client/connection-manager.js +77 -13
  514. package/out/zero-client/src/client/connection-manager.js.map +1 -1
  515. package/out/zero-client/src/client/connection-status-enum.d.ts +2 -0
  516. package/out/zero-client/src/client/connection-status-enum.d.ts.map +1 -1
  517. package/out/zero-client/src/client/connection-status-enum.js +3 -1
  518. package/out/zero-client/src/client/connection-status-enum.js.map +1 -1
  519. package/out/zero-client/src/client/connection.d.ts +6 -0
  520. package/out/zero-client/src/client/connection.d.ts.map +1 -1
  521. package/out/zero-client/src/client/connection.js +2 -1
  522. package/out/zero-client/src/client/connection.js.map +1 -1
  523. package/out/zero-client/src/client/context.d.ts +22 -12
  524. package/out/zero-client/src/client/context.d.ts.map +1 -1
  525. package/out/zero-client/src/client/context.js +92 -25
  526. package/out/zero-client/src/client/context.js.map +1 -1
  527. package/out/zero-client/src/client/custom.js +2 -2
  528. package/out/zero-client/src/client/custom.js.map +1 -1
  529. package/out/zero-client/src/client/error.js +1 -1
  530. package/out/zero-client/src/client/ivm-branch.d.ts.map +1 -1
  531. package/out/zero-client/src/client/ivm-branch.js +20 -3
  532. package/out/zero-client/src/client/ivm-branch.js.map +1 -1
  533. package/out/zero-client/src/client/mutator-proxy.d.ts.map +1 -1
  534. package/out/zero-client/src/client/mutator-proxy.js +5 -2
  535. package/out/zero-client/src/client/mutator-proxy.js.map +1 -1
  536. package/out/zero-client/src/client/options.d.ts +33 -0
  537. package/out/zero-client/src/client/options.d.ts.map +1 -1
  538. package/out/zero-client/src/client/options.js.map +1 -1
  539. package/out/zero-client/src/client/query-manager.d.ts.map +1 -1
  540. package/out/zero-client/src/client/query-manager.js +44 -11
  541. package/out/zero-client/src/client/query-manager.js.map +1 -1
  542. package/out/zero-client/src/client/version.js +1 -1
  543. package/out/zero-client/src/client/zero-rep.d.ts +1 -0
  544. package/out/zero-client/src/client/zero-rep.d.ts.map +1 -1
  545. package/out/zero-client/src/client/zero-rep.js +8 -6
  546. package/out/zero-client/src/client/zero-rep.js.map +1 -1
  547. package/out/zero-client/src/client/zero.d.ts +18 -3
  548. package/out/zero-client/src/client/zero.d.ts.map +1 -1
  549. package/out/zero-client/src/client/zero.js +83 -46
  550. package/out/zero-client/src/client/zero.js.map +1 -1
  551. package/out/zero-client/src/mod.d.ts +1 -0
  552. package/out/zero-client/src/mod.d.ts.map +1 -1
  553. package/out/zero-client/src/mod.js +1 -0
  554. package/out/zero-client/src/types/query-result.d.ts +2 -0
  555. package/out/zero-client/src/types/query-result.d.ts.map +1 -1
  556. package/out/zero-events/src/status.d.ts +58 -0
  557. package/out/zero-events/src/status.d.ts.map +1 -1
  558. package/out/zero-protocol/src/ast.d.ts.map +1 -1
  559. package/out/zero-protocol/src/ast.js +13 -14
  560. package/out/zero-protocol/src/ast.js.map +1 -1
  561. package/out/zero-protocol/src/down.d.ts +29 -29
  562. package/out/zero-protocol/src/query-hash.d.ts.map +1 -1
  563. package/out/zero-protocol/src/query-hash.js +2 -5
  564. package/out/zero-protocol/src/query-hash.js.map +1 -1
  565. package/out/zero-react/src/use-query.d.ts +5 -4
  566. package/out/zero-react/src/use-query.d.ts.map +1 -1
  567. package/out/zero-react/src/use-query.js +62 -4
  568. package/out/zero-react/src/use-query.js.map +1 -1
  569. package/out/zero-react/src/zero.js +1 -0
  570. package/out/zero-server/src/adapters/drizzle.d.ts +2 -1
  571. package/out/zero-server/src/adapters/drizzle.d.ts.map +1 -1
  572. package/out/zero-server/src/adapters/drizzle.js +12 -7
  573. package/out/zero-server/src/adapters/drizzle.js.map +1 -1
  574. package/out/zero-server/src/adapters/kysely.d.ts +2 -1
  575. package/out/zero-server/src/adapters/kysely.d.ts.map +1 -1
  576. package/out/zero-server/src/adapters/kysely.js +8 -2
  577. package/out/zero-server/src/adapters/kysely.js.map +1 -1
  578. package/out/zero-server/src/adapters/pg.d.ts +1 -0
  579. package/out/zero-server/src/adapters/pg.d.ts.map +1 -1
  580. package/out/zero-server/src/adapters/pg.js +8 -2
  581. package/out/zero-server/src/adapters/pg.js.map +1 -1
  582. package/out/zero-server/src/adapters/postgresjs.d.ts +1 -0
  583. package/out/zero-server/src/adapters/postgresjs.d.ts.map +1 -1
  584. package/out/zero-server/src/adapters/postgresjs.js +7 -1
  585. package/out/zero-server/src/adapters/postgresjs.js.map +1 -1
  586. package/out/zero-server/src/adapters/prisma.d.ts +3 -2
  587. package/out/zero-server/src/adapters/prisma.d.ts.map +1 -1
  588. package/out/zero-server/src/adapters/prisma.js +8 -2
  589. package/out/zero-server/src/adapters/prisma.js.map +1 -1
  590. package/out/zero-server/src/custom.d.ts +5 -1
  591. package/out/zero-server/src/custom.d.ts.map +1 -1
  592. package/out/zero-server/src/custom.js +6 -3
  593. package/out/zero-server/src/custom.js.map +1 -1
  594. package/out/zero-server/src/logging.d.ts +2 -2
  595. package/out/zero-server/src/logging.d.ts.map +1 -1
  596. package/out/zero-server/src/logging.js +2 -2
  597. package/out/zero-server/src/logging.js.map +1 -1
  598. package/out/zero-server/src/mod.d.ts +1 -1
  599. package/out/zero-server/src/mod.d.ts.map +1 -1
  600. package/out/zero-server/src/pg-query-executor.d.ts +2 -2
  601. package/out/zero-server/src/pg-query-executor.d.ts.map +1 -1
  602. package/out/zero-server/src/pg-query-executor.js +2 -2
  603. package/out/zero-server/src/pg-query-executor.js.map +1 -1
  604. package/out/zero-server/src/process-mutations.d.ts +12 -3
  605. package/out/zero-server/src/process-mutations.d.ts.map +1 -1
  606. package/out/zero-server/src/process-mutations.js +13 -8
  607. package/out/zero-server/src/process-mutations.js.map +1 -1
  608. package/out/zero-server/src/push-processor.d.ts +8 -2
  609. package/out/zero-server/src/push-processor.d.ts.map +1 -1
  610. package/out/zero-server/src/push-processor.js +12 -4
  611. package/out/zero-server/src/push-processor.js.map +1 -1
  612. package/out/zero-server/src/schema.d.ts +2 -2
  613. package/out/zero-server/src/schema.d.ts.map +1 -1
  614. package/out/zero-server/src/schema.js +2 -2
  615. package/out/zero-server/src/schema.js.map +1 -1
  616. package/out/zero-server/src/zql-database.d.ts +21 -1
  617. package/out/zero-server/src/zql-database.d.ts.map +1 -1
  618. package/out/zero-server/src/zql-database.js +33 -2
  619. package/out/zero-server/src/zql-database.js.map +1 -1
  620. package/out/zero-solid/src/solid-view.d.ts +17 -0
  621. package/out/zero-solid/src/solid-view.d.ts.map +1 -1
  622. package/out/zero-solid/src/solid-view.js +39 -1
  623. package/out/zero-solid/src/solid-view.js.map +1 -1
  624. package/out/zero-solid/src/use-query.js +1 -2
  625. package/out/zero-solid/src/use-query.js.map +1 -1
  626. package/out/zero-solid/src/zero.js +1 -0
  627. package/out/zql/src/builder/builder.d.ts +25 -0
  628. package/out/zql/src/builder/builder.d.ts.map +1 -1
  629. package/out/zql/src/builder/builder.js +69 -19
  630. package/out/zql/src/builder/builder.js.map +1 -1
  631. package/out/zql/src/builder/correlated-predicate-pushdown.d.ts +43 -0
  632. package/out/zql/src/builder/correlated-predicate-pushdown.d.ts.map +1 -0
  633. package/out/zql/src/builder/correlated-predicate-pushdown.js +161 -0
  634. package/out/zql/src/builder/correlated-predicate-pushdown.js.map +1 -0
  635. package/out/zql/src/builder/debug-delegate.d.ts +6 -1
  636. package/out/zql/src/builder/debug-delegate.d.ts.map +1 -1
  637. package/out/zql/src/builder/debug-delegate.js +14 -2
  638. package/out/zql/src/builder/debug-delegate.js.map +1 -1
  639. package/out/zql/src/ivm/array-view.d.ts +29 -0
  640. package/out/zql/src/ivm/array-view.d.ts.map +1 -1
  641. package/out/zql/src/ivm/array-view.js +58 -9
  642. package/out/zql/src/ivm/array-view.js.map +1 -1
  643. package/out/zql/src/ivm/cap.d.ts +2 -1
  644. package/out/zql/src/ivm/cap.d.ts.map +1 -1
  645. package/out/zql/src/ivm/cap.js +14 -4
  646. package/out/zql/src/ivm/cap.js.map +1 -1
  647. package/out/zql/src/ivm/connection-index.d.ts +68 -0
  648. package/out/zql/src/ivm/connection-index.d.ts.map +1 -0
  649. package/out/zql/src/ivm/connection-index.js +149 -0
  650. package/out/zql/src/ivm/connection-index.js.map +1 -0
  651. package/out/zql/src/ivm/data.d.ts.map +1 -1
  652. package/out/zql/src/ivm/data.js +8 -3
  653. package/out/zql/src/ivm/data.js.map +1 -1
  654. package/out/zql/src/ivm/deferred-input.d.ts.map +1 -1
  655. package/out/zql/src/ivm/deferred-input.js +3 -2
  656. package/out/zql/src/ivm/deferred-input.js.map +1 -1
  657. package/out/zql/src/ivm/exists.d.ts +3 -1
  658. package/out/zql/src/ivm/exists.d.ts.map +1 -1
  659. package/out/zql/src/ivm/exists.js +4 -1
  660. package/out/zql/src/ivm/exists.js.map +1 -1
  661. package/out/zql/src/ivm/fan-in.d.ts +6 -2
  662. package/out/zql/src/ivm/fan-in.d.ts.map +1 -1
  663. package/out/zql/src/ivm/fan-in.js +13 -5
  664. package/out/zql/src/ivm/fan-in.js.map +1 -1
  665. package/out/zql/src/ivm/fan-out.d.ts +5 -2
  666. package/out/zql/src/ivm/fan-out.d.ts.map +1 -1
  667. package/out/zql/src/ivm/fan-out.js +4 -0
  668. package/out/zql/src/ivm/fan-out.js.map +1 -1
  669. package/out/zql/src/ivm/filter-operators.d.ts +8 -6
  670. package/out/zql/src/ivm/filter-operators.d.ts.map +1 -1
  671. package/out/zql/src/ivm/filter-operators.js +19 -8
  672. package/out/zql/src/ivm/filter-operators.js.map +1 -1
  673. package/out/zql/src/ivm/filter.d.ts +5 -2
  674. package/out/zql/src/ivm/filter.d.ts.map +1 -1
  675. package/out/zql/src/ivm/filter.js +10 -4
  676. package/out/zql/src/ivm/filter.js.map +1 -1
  677. package/out/zql/src/ivm/flipped-join.d.ts +9 -4
  678. package/out/zql/src/ivm/flipped-join.d.ts.map +1 -1
  679. package/out/zql/src/ivm/flipped-join.js +97 -46
  680. package/out/zql/src/ivm/flipped-join.js.map +1 -1
  681. package/out/zql/src/ivm/join-index-tap.d.ts +33 -0
  682. package/out/zql/src/ivm/join-index-tap.d.ts.map +1 -0
  683. package/out/zql/src/ivm/join-index-tap.js +70 -0
  684. package/out/zql/src/ivm/join-index-tap.js.map +1 -0
  685. package/out/zql/src/ivm/join-index.d.ts +35 -0
  686. package/out/zql/src/ivm/join-index.d.ts.map +1 -0
  687. package/out/zql/src/ivm/join-index.js +141 -0
  688. package/out/zql/src/ivm/join-index.js.map +1 -0
  689. package/out/zql/src/ivm/join-utils.d.ts +6 -0
  690. package/out/zql/src/ivm/join-utils.d.ts.map +1 -1
  691. package/out/zql/src/ivm/join-utils.js +22 -1
  692. package/out/zql/src/ivm/join-utils.js.map +1 -1
  693. package/out/zql/src/ivm/join.d.ts +19 -1
  694. package/out/zql/src/ivm/join.d.ts.map +1 -1
  695. package/out/zql/src/ivm/join.js +107 -15
  696. package/out/zql/src/ivm/join.js.map +1 -1
  697. package/out/zql/src/ivm/maybe-split-and-push-edit-change.d.ts +1 -1
  698. package/out/zql/src/ivm/maybe-split-and-push-edit-change.d.ts.map +1 -1
  699. package/out/zql/src/ivm/maybe-split-and-push-edit-change.js +6 -4
  700. package/out/zql/src/ivm/maybe-split-and-push-edit-change.js.map +1 -1
  701. package/out/zql/src/ivm/memory-source.d.ts +28 -3
  702. package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
  703. package/out/zql/src/ivm/memory-source.js +69 -33
  704. package/out/zql/src/ivm/memory-source.js.map +1 -1
  705. package/out/zql/src/ivm/memory-storage.d.ts +1 -0
  706. package/out/zql/src/ivm/memory-storage.d.ts.map +1 -1
  707. package/out/zql/src/ivm/memory-storage.js +1 -0
  708. package/out/zql/src/ivm/memory-storage.js.map +1 -1
  709. package/out/zql/src/ivm/operator.d.ts +45 -10
  710. package/out/zql/src/ivm/operator.d.ts.map +1 -1
  711. package/out/zql/src/ivm/operator.js +9 -4
  712. package/out/zql/src/ivm/operator.js.map +1 -1
  713. package/out/zql/src/ivm/push-accumulated.d.ts +6 -1
  714. package/out/zql/src/ivm/push-accumulated.d.ts.map +1 -1
  715. package/out/zql/src/ivm/push-accumulated.js +47 -2
  716. package/out/zql/src/ivm/push-accumulated.js.map +1 -1
  717. package/out/zql/src/ivm/skip.d.ts +2 -1
  718. package/out/zql/src/ivm/skip.d.ts.map +1 -1
  719. package/out/zql/src/ivm/skip.js +3 -0
  720. package/out/zql/src/ivm/skip.js.map +1 -1
  721. package/out/zql/src/ivm/take-gate.d.ts +57 -0
  722. package/out/zql/src/ivm/take-gate.d.ts.map +1 -0
  723. package/out/zql/src/ivm/take-gate.js +106 -0
  724. package/out/zql/src/ivm/take-gate.js.map +1 -0
  725. package/out/zql/src/ivm/take.d.ts +8 -2
  726. package/out/zql/src/ivm/take.d.ts.map +1 -1
  727. package/out/zql/src/ivm/take.js +139 -103
  728. package/out/zql/src/ivm/take.js.map +1 -1
  729. package/out/zql/src/ivm/union-fan-in.d.ts +3 -0
  730. package/out/zql/src/ivm/union-fan-in.d.ts.map +1 -1
  731. package/out/zql/src/ivm/union-fan-in.js +63 -10
  732. package/out/zql/src/ivm/union-fan-in.js.map +1 -1
  733. package/out/zql/src/ivm/union-fan-out.d.ts +2 -1
  734. package/out/zql/src/ivm/union-fan-out.d.ts.map +1 -1
  735. package/out/zql/src/ivm/union-fan-out.js +5 -0
  736. package/out/zql/src/ivm/union-fan-out.js.map +1 -1
  737. package/out/zql/src/ivm/view-apply-change.d.ts.map +1 -1
  738. package/out/zql/src/ivm/view-apply-change.js +9 -4
  739. package/out/zql/src/ivm/view-apply-change.js.map +1 -1
  740. package/out/zql/src/ivm/view.d.ts +15 -0
  741. package/out/zql/src/ivm/view.d.ts.map +1 -1
  742. package/out/zql/src/mutate/custom.d.ts +24 -1
  743. package/out/zql/src/mutate/custom.d.ts.map +1 -1
  744. package/out/zql/src/mutate/custom.js.map +1 -1
  745. package/out/zql/src/mutate/mutator-registry.d.ts.map +1 -1
  746. package/out/zql/src/mutate/mutator-registry.js +2 -2
  747. package/out/zql/src/mutate/mutator-registry.js.map +1 -1
  748. package/out/zql/src/planner/planner-builder.d.ts +23 -3
  749. package/out/zql/src/planner/planner-builder.d.ts.map +1 -1
  750. package/out/zql/src/planner/planner-builder.js +34 -17
  751. package/out/zql/src/planner/planner-builder.js.map +1 -1
  752. package/out/zql/src/planner/planner-connection.d.ts +58 -2
  753. package/out/zql/src/planner/planner-connection.d.ts.map +1 -1
  754. package/out/zql/src/planner/planner-connection.js +103 -11
  755. package/out/zql/src/planner/planner-connection.js.map +1 -1
  756. package/out/zql/src/planner/planner-debug.d.ts.map +1 -1
  757. package/out/zql/src/planner/planner-debug.js +3 -8
  758. package/out/zql/src/planner/planner-debug.js.map +1 -1
  759. package/out/zql/src/planner/planner-graph.d.ts +10 -0
  760. package/out/zql/src/planner/planner-graph.d.ts.map +1 -1
  761. package/out/zql/src/planner/planner-graph.js +14 -0
  762. package/out/zql/src/planner/planner-graph.js.map +1 -1
  763. package/out/zql/src/planner/planner-source.d.ts +2 -2
  764. package/out/zql/src/planner/planner-source.d.ts.map +1 -1
  765. package/out/zql/src/planner/planner-source.js +2 -2
  766. package/out/zql/src/planner/planner-source.js.map +1 -1
  767. package/out/zql/src/planner/planner-warnings.d.ts +83 -0
  768. package/out/zql/src/planner/planner-warnings.d.ts.map +1 -0
  769. package/out/zql/src/planner/planner-warnings.js +78 -0
  770. package/out/zql/src/planner/planner-warnings.js.map +1 -0
  771. package/out/zql/src/query/measure-push-operator.d.ts +2 -1
  772. package/out/zql/src/query/measure-push-operator.d.ts.map +1 -1
  773. package/out/zql/src/query/measure-push-operator.js +33 -4
  774. package/out/zql/src/query/measure-push-operator.js.map +1 -1
  775. package/out/zql/src/query/query-delegate-base.d.ts +4 -2
  776. package/out/zql/src/query/query-delegate-base.d.ts.map +1 -1
  777. package/out/zql/src/query/query-delegate-base.js +66 -33
  778. package/out/zql/src/query/query-delegate-base.js.map +1 -1
  779. package/out/zql/src/query/query-delegate.d.ts +33 -6
  780. package/out/zql/src/query/query-delegate.d.ts.map +1 -1
  781. package/out/zql/src/query/query-impl.d.ts +1 -0
  782. package/out/zql/src/query/query-impl.d.ts.map +1 -1
  783. package/out/zql/src/query/query-impl.js +6 -23
  784. package/out/zql/src/query/query-impl.js.map +1 -1
  785. package/out/zql/src/query/query-transitions.d.ts.map +1 -1
  786. package/out/zql/src/query/query-transitions.js +2 -5
  787. package/out/zql/src/query/query-transitions.js.map +1 -1
  788. package/out/zql/src/query/query.d.ts +11 -2
  789. package/out/zql/src/query/query.d.ts.map +1 -1
  790. package/out/zql/src/query/runnable-query-impl.d.ts +1 -0
  791. package/out/zql/src/query/runnable-query-impl.d.ts.map +1 -1
  792. package/out/zql/src/query/runnable-query-impl.js +2 -10
  793. package/out/zql/src/query/runnable-query-impl.js.map +1 -1
  794. package/out/zql/src/query/typed-view.d.ts +1 -1
  795. package/out/zql/src/query/typed-view.d.ts.map +1 -1
  796. package/out/zqlite/src/database-storage.d.ts.map +1 -1
  797. package/out/zqlite/src/database-storage.js +9 -1
  798. package/out/zqlite/src/database-storage.js.map +1 -1
  799. package/out/zqlite/src/db.d.ts +9 -1
  800. package/out/zqlite/src/db.d.ts.map +1 -1
  801. package/out/zqlite/src/db.js +31 -27
  802. package/out/zqlite/src/db.js.map +1 -1
  803. package/out/zqlite/src/pending-delta.d.ts +110 -0
  804. package/out/zqlite/src/pending-delta.d.ts.map +1 -0
  805. package/out/zqlite/src/pending-delta.js +278 -0
  806. package/out/zqlite/src/pending-delta.js.map +1 -0
  807. package/out/zqlite/src/query-builder.d.ts +17 -1
  808. package/out/zqlite/src/query-builder.d.ts.map +1 -1
  809. package/out/zqlite/src/query-builder.js +111 -20
  810. package/out/zqlite/src/query-builder.js.map +1 -1
  811. package/out/zqlite/src/query-delegate.d.ts.map +1 -1
  812. package/out/zqlite/src/query-delegate.js +10 -7
  813. package/out/zqlite/src/query-delegate.js.map +1 -1
  814. package/out/zqlite/src/sqlite-cost-model.d.ts.map +1 -1
  815. package/out/zqlite/src/sqlite-cost-model.js +52 -1
  816. package/out/zqlite/src/sqlite-cost-model.js.map +1 -1
  817. package/out/zqlite/src/sqlite-stat-fanout.d.ts.map +1 -1
  818. package/out/zqlite/src/sqlite-stat-fanout.js +5 -31
  819. package/out/zqlite/src/sqlite-stat-fanout.js.map +1 -1
  820. package/out/zqlite/src/sqlite-stat4-sample.d.ts +37 -0
  821. package/out/zqlite/src/sqlite-stat4-sample.d.ts.map +1 -0
  822. package/out/zqlite/src/sqlite-stat4-sample.js +61 -0
  823. package/out/zqlite/src/sqlite-stat4-sample.js.map +1 -0
  824. package/out/zqlite/src/table-source.d.ts +83 -2
  825. package/out/zqlite/src/table-source.d.ts.map +1 -1
  826. package/out/zqlite/src/table-source.js +264 -38
  827. package/out/zqlite/src/table-source.js.map +1 -1
  828. package/package.json +3 -4
  829. package/out/replicache/src/kv/idb-store-with-mem-fallback.d.ts +0 -25
  830. package/out/replicache/src/kv/idb-store-with-mem-fallback.d.ts.map +0 -1
  831. package/out/replicache/src/kv/idb-store-with-mem-fallback.js +0 -83
  832. package/out/replicache/src/kv/idb-store-with-mem-fallback.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"syncer-ws-message-handler.js","names":["#viewSyncer","#mutagen","#mutationLock","#lc","#clientGroupID","#connectionSelector","#connContextManager","#pusher"],"sources":["../../../../../zero-cache/src/workers/syncer-ws-message-handler.ts"],"sourcesContent":["import {ROOT_CONTEXT, context, propagation, trace} from '@opentelemetry/api';\nimport {Lock} from '@rocicorp/lock';\nimport type {LogContext} from '@rocicorp/logger';\nimport {startAsyncSpan, startSpan} from '../../../otel/src/span.ts';\nimport {version} from '../../../otel/src/version.ts';\nimport {assert, unreachable} from '../../../shared/src/asserts.ts';\nimport {ErrorKind} from '../../../zero-protocol/src/error-kind.ts';\nimport {ErrorOrigin} from '../../../zero-protocol/src/error-origin.ts';\nimport type {ErrorBody} from '../../../zero-protocol/src/error.ts';\nimport type {UpstreamWithUnparsedAnalyzeQuery} from '../../../zero-protocol/src/up.ts';\nimport type {Mutagen} from '../services/mutagen/mutagen.ts';\nimport type {Pusher} from '../services/mutagen/pusher.ts';\nimport {\n type ConnectionContextManager,\n type ConnectionSelector,\n} from '../services/view-syncer/connection-context-manager.ts';\nimport {type ViewSyncer} from '../services/view-syncer/view-syncer.ts';\nimport type {ConnectParams} from './connect-params.ts';\nimport type {HandlerResult, MessageHandler} from './connection.ts';\n\nconst tracer = trace.getTracer('syncer-ws-server', version);\n\n/**\n * Wraps a function in an OTEL context extracted from a W3C traceparent header.\n * This enables distributed tracing from the client through zero-cache to\n * the user's API server.\n */\nfunction withTraceparent<T>(traceparent: string | undefined, fn: () => T): T {\n if (!traceparent) {\n return fn();\n }\n const extracted = propagation.extract(ROOT_CONTEXT, {traceparent});\n return context.with(extracted, fn);\n}\n\nexport class SyncerWsMessageHandler implements MessageHandler {\n readonly #viewSyncer: ViewSyncer;\n readonly #mutagen: Mutagen | undefined;\n readonly #mutationLock: Lock;\n readonly #lc: LogContext;\n readonly #clientGroupID: string;\n readonly #connectionSelector: ConnectionSelector;\n readonly #connContextManager: ConnectionContextManager;\n readonly #pusher: Pusher | undefined;\n\n constructor(\n lc: LogContext,\n connectParams: ConnectParams,\n connContextManager: ConnectionContextManager,\n viewSyncer: ViewSyncer,\n mutagen: Mutagen | undefined,\n pusher: Pusher | undefined,\n ) {\n const {clientGroupID, clientID, wsID} = connectParams;\n this.#viewSyncer = viewSyncer;\n this.#mutagen = mutagen;\n this.#connContextManager = connContextManager;\n this.#mutationLock = new Lock();\n this.#lc = lc\n .withContext('connection')\n .withContext('clientID', clientID)\n .withContext('clientGroupID', clientGroupID)\n .withContext('wsID', wsID);\n this.#clientGroupID = clientGroupID;\n this.#pusher = pusher;\n this.#connectionSelector = {\n clientID,\n wsID,\n };\n }\n\n async handleMessage(\n msg: UpstreamWithUnparsedAnalyzeQuery,\n ): Promise<HandlerResult[]> {\n const lc = this.#lc;\n const msgType = msg[0];\n const viewSyncer = this.#viewSyncer;\n switch (msgType) {\n case 'ping':\n lc.error?.('Ping is not supported at this layer by Zero');\n break;\n case 'pull':\n lc.error?.('Pull is not supported by Zero');\n break;\n case 'push': {\n return withTraceparent(msg[1].traceparent, () =>\n startAsyncSpan<HandlerResult[]>(\n tracer,\n 'connection.push',\n async () => {\n const {clientGroupID, mutations} = msg[1];\n if (clientGroupID !== this.#clientGroupID) {\n return [\n {\n type: 'fatal',\n error: {\n kind: ErrorKind.InvalidPush,\n message:\n `clientGroupID in mutation \"${clientGroupID}\" does not match ` +\n `clientGroupID of connection \"${this.#clientGroupID}`,\n origin: ErrorOrigin.ZeroCache,\n },\n } satisfies HandlerResult,\n ];\n }\n\n if (mutations.length === 0) {\n return [\n {\n type: 'ok',\n },\n ];\n }\n\n // The client only ever sends 1 mutation per push.\n // #pusher will throw if it sees a CRUD mutation.\n // #mutagen will throw if it see a custom mutation.\n if (mutations[0].type === 'custom') {\n if (!this.#pusher) {\n return [\n {\n type: 'fatal',\n error: {\n kind: ErrorKind.InvalidPush,\n message:\n 'A ZERO_MUTATE_URL must be set in order to process custom mutations.',\n origin: ErrorOrigin.ZeroCache,\n },\n } satisfies HandlerResult,\n ];\n }\n return [\n this.#pusher.enqueuePush(this.#connectionSelector, msg[1]),\n ];\n }\n\n const mutagen = this.#mutagen;\n if (!mutagen) {\n return [\n {\n type: 'fatal',\n error: {\n kind: ErrorKind.InvalidPush,\n message: `Support for legacy CRUD mutations is disabled`,\n origin: ErrorOrigin.ZeroCache,\n },\n } satisfies HandlerResult,\n ];\n }\n\n const auth = this.#connContextManager.mustGetConnectionContext(\n this.#connectionSelector,\n ).auth;\n assert(\n auth?.type !== 'opaque',\n 'Only JWT auth is supported for CRUD mutations',\n );\n\n // Hold a connection-level lock while processing mutations so that:\n // 1. Mutations are processed in the order in which they are received and\n // 2. A single view syncer connection cannot hog multiple upstream connections.\n const ret = await this.#mutationLock.withLock(async () => {\n const errors: ErrorBody[] = [];\n for (const mutation of mutations) {\n const maybeError = await mutagen.processMutation(\n mutation,\n auth?.decoded,\n this.#pusher !== undefined,\n );\n if (maybeError !== undefined) {\n errors.push({\n kind: maybeError[0],\n message: maybeError[1],\n origin: ErrorOrigin.ZeroCache,\n });\n }\n }\n if (errors.length > 0) {\n return {type: 'transient', errors} satisfies HandlerResult;\n }\n return {type: 'ok'} satisfies HandlerResult;\n });\n return [ret];\n },\n ),\n );\n }\n case 'changeDesiredQueries':\n await withTraceparent(msg[1].traceparent, () =>\n startAsyncSpan(tracer, 'connection.changeDesiredQueries', () =>\n viewSyncer.changeDesiredQueries(this.#connectionSelector, msg),\n ),\n );\n break;\n case 'updateAuth':\n await startAsyncSpan(tracer, 'connection.updateAuth', async () => {\n const initialConnCtx =\n this.#connContextManager.mustGetConnectionContext(\n this.#connectionSelector,\n );\n const updatedConnCtx = await this.#connContextManager.updateAuth(\n this.#connectionSelector,\n msg[1],\n );\n const authRevisionChanged =\n updatedConnCtx.revision !== initialConnCtx.revision;\n\n await viewSyncer.updateAuth(\n this.#connectionSelector,\n msg,\n authRevisionChanged,\n );\n });\n break;\n case 'deleteClients': {\n const deletedClientIDs = await startAsyncSpan(\n tracer,\n 'connection.deleteClients',\n () => viewSyncer.deleteClients(this.#connectionSelector, msg),\n );\n if (this.#pusher && deletedClientIDs.length > 0) {\n await this.#pusher.deleteClientMutations(\n this.#connectionSelector,\n deletedClientIDs,\n );\n }\n break;\n }\n case 'initConnection': {\n this.#connContextManager.initConnection(\n this.#connectionSelector,\n msg[1],\n );\n return withTraceparent(msg[1].traceparent, () => {\n const ret: HandlerResult[] = [\n {\n type: 'stream',\n source: 'viewSyncer',\n stream: startSpan(tracer, 'connection.initConnection', () =>\n viewSyncer.initConnection(this.#connectionSelector, msg),\n ),\n },\n ];\n\n // Given we support both CRUD and Custom mutators,\n // we do not initialize the `pusher` unless the user has opted\n // into custom mutations. We detect that by checking\n // if the pushURL has been set.\n if (this.#pusher) {\n ret.push({\n type: 'stream',\n source: 'pusher',\n stream: this.#pusher.initConnection(this.#connectionSelector),\n });\n }\n\n return ret;\n });\n }\n case 'closeConnection':\n // This message is deprecated and no longer used.\n break;\n\n case 'inspect':\n await startAsyncSpan(tracer, 'connection.inspect', () =>\n viewSyncer.inspect(this.#connectionSelector, msg),\n );\n break;\n\n case 'ackMutationResponses':\n if (this.#pusher) {\n await this.#pusher.ackMutationResponses(\n this.#connectionSelector,\n msg[1],\n );\n }\n break;\n\n default:\n unreachable(msgType);\n }\n\n return [{type: 'ok'}];\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"syncer-ws-message-handler.js","names":["#viewSyncer","#mutagen","#mutationLock","#lc","#clientGroupID","#connectionSelector","#connContextManager","#pusher"],"sources":["../../../../../zero-cache/src/workers/syncer-ws-message-handler.ts"],"sourcesContent":["import {ROOT_CONTEXT, context, propagation, trace} from '@opentelemetry/api';\nimport {Lock} from '@rocicorp/lock';\nimport type {LogContext} from '@rocicorp/logger';\nimport {startAsyncSpan, startSpan} from '../../../otel/src/span.ts';\nimport {version} from '../../../otel/src/version.ts';\nimport {assert, unreachable} from '../../../shared/src/asserts.ts';\nimport {ErrorKind} from '../../../zero-protocol/src/error-kind.ts';\nimport {ErrorOrigin} from '../../../zero-protocol/src/error-origin.ts';\nimport type {ErrorBody} from '../../../zero-protocol/src/error.ts';\nimport type {UpstreamWithUnparsedAnalyzeQuery} from '../../../zero-protocol/src/up.ts';\nimport type {Mutagen} from '../services/mutagen/mutagen.ts';\nimport type {Pusher} from '../services/mutagen/pusher.ts';\nimport {\n type ConnectionContextManager,\n type ConnectionSelector,\n} from '../services/view-syncer/connection-context-manager.ts';\nimport {type ViewSyncer} from '../services/view-syncer/view-syncer.ts';\nimport type {ConnectParams} from './connect-params.ts';\nimport type {HandlerResult, MessageHandler} from './connection.ts';\n\nconst tracer = trace.getTracer('syncer-ws-server', version);\n\n/**\n * Wraps a function in an OTEL context extracted from a W3C traceparent header.\n * This enables distributed tracing from the client through zero-cache to\n * the user's API server.\n */\nfunction withTraceparent<T>(traceparent: string | undefined, fn: () => T): T {\n if (!traceparent) {\n return fn();\n }\n const extracted = propagation.extract(ROOT_CONTEXT, {traceparent});\n return context.with(extracted, fn);\n}\n\nexport class SyncerWsMessageHandler implements MessageHandler {\n readonly #viewSyncer: ViewSyncer;\n readonly #mutagen: Mutagen | undefined;\n readonly #mutationLock: Lock;\n readonly #lc: LogContext;\n readonly #clientGroupID: string;\n readonly #connectionSelector: ConnectionSelector;\n readonly #connContextManager: ConnectionContextManager;\n readonly #pusher: Pusher | undefined;\n\n constructor(\n lc: LogContext,\n connectParams: ConnectParams,\n connContextManager: ConnectionContextManager,\n viewSyncer: ViewSyncer,\n mutagen: Mutagen | undefined,\n pusher: Pusher | undefined,\n ) {\n const {clientGroupID, clientID, wsID} = connectParams;\n this.#viewSyncer = viewSyncer;\n this.#mutagen = mutagen;\n this.#connContextManager = connContextManager;\n this.#mutationLock = new Lock();\n this.#lc = lc\n .withContext('connection')\n .withContext('clientID', clientID)\n .withContext('clientGroupID', clientGroupID)\n .withContext('wsID', wsID);\n this.#clientGroupID = clientGroupID;\n this.#pusher = pusher;\n this.#connectionSelector = {\n clientID,\n wsID,\n };\n }\n\n async handleMessage(\n msg: UpstreamWithUnparsedAnalyzeQuery,\n ): Promise<HandlerResult[]> {\n const lc = this.#lc;\n const msgType = msg[0];\n const viewSyncer = this.#viewSyncer;\n switch (msgType) {\n case 'ping':\n lc.error?.('Ping is not supported at this layer by Zero');\n break;\n case 'pull':\n lc.error?.('Pull is not supported by Zero');\n break;\n case 'push': {\n return withTraceparent(msg[1].traceparent, () =>\n startAsyncSpan<HandlerResult[]>(\n tracer,\n 'connection.push',\n async () => {\n const {clientGroupID, mutations} = msg[1];\n if (clientGroupID !== this.#clientGroupID) {\n return [\n {\n type: 'fatal',\n error: {\n kind: ErrorKind.InvalidPush,\n message:\n `clientGroupID in mutation \"${clientGroupID}\" does not match ` +\n `clientGroupID of connection \"${this.#clientGroupID}`,\n origin: ErrorOrigin.ZeroCache,\n },\n } satisfies HandlerResult,\n ];\n }\n\n if (mutations.length === 0) {\n return [\n {\n type: 'ok',\n },\n ];\n }\n\n // The client only ever sends 1 mutation per push.\n // #pusher will throw if it sees a CRUD mutation.\n // #mutagen will throw if it see a custom mutation.\n if (mutations[0].type === 'custom') {\n if (!this.#pusher) {\n return [\n {\n type: 'fatal',\n error: {\n kind: ErrorKind.InvalidPush,\n message:\n 'A ZERO_MUTATE_URL must be set in order to process custom mutations.',\n origin: ErrorOrigin.ZeroCache,\n },\n } satisfies HandlerResult,\n ];\n }\n return [\n this.#pusher.enqueuePush(this.#connectionSelector, msg[1]),\n ];\n }\n\n const mutagen = this.#mutagen;\n if (!mutagen) {\n return [\n {\n type: 'fatal',\n error: {\n kind: ErrorKind.InvalidPush,\n message: `Support for legacy CRUD mutations is disabled`,\n origin: ErrorOrigin.ZeroCache,\n },\n } satisfies HandlerResult,\n ];\n }\n\n const auth = this.#connContextManager.mustGetConnectionContext(\n this.#connectionSelector,\n ).auth;\n assert(\n auth?.type !== 'opaque',\n 'Only JWT auth is supported for CRUD mutations',\n );\n\n // Hold a ref on the mutagen for the duration of the push so that\n // the connection closing mid-push does not stop the service\n // (closing its replica handle) between two of its mutations.\n // (The connection holds a ref while open, and messages are not\n // dispatched after it has closed, so the service is running\n // here.)\n mutagen.ref();\n try {\n // Hold a connection-level lock while processing mutations so that:\n // 1. Mutations are processed in the order in which they are received and\n // 2. A single view syncer connection cannot hog multiple upstream connections.\n const ret = await this.#mutationLock.withLock(async () => {\n const errors: ErrorBody[] = [];\n for (const mutation of mutations) {\n const maybeError = await mutagen.processMutation(\n mutation,\n auth?.decoded,\n this.#pusher !== undefined,\n );\n if (maybeError !== undefined) {\n errors.push({\n kind: maybeError[0],\n message: maybeError[1],\n origin: ErrorOrigin.ZeroCache,\n });\n }\n }\n if (errors.length > 0) {\n return {type: 'transient', errors} satisfies HandlerResult;\n }\n return {type: 'ok'} satisfies HandlerResult;\n });\n return [ret];\n } finally {\n mutagen.unref();\n }\n },\n ),\n );\n }\n case 'changeDesiredQueries':\n await withTraceparent(msg[1].traceparent, () =>\n startAsyncSpan(tracer, 'connection.changeDesiredQueries', () =>\n viewSyncer.changeDesiredQueries(this.#connectionSelector, msg),\n ),\n );\n break;\n case 'updateAuth':\n await startAsyncSpan(tracer, 'connection.updateAuth', async () => {\n const initialConnCtx =\n this.#connContextManager.mustGetConnectionContext(\n this.#connectionSelector,\n );\n const updatedConnCtx = await this.#connContextManager.updateAuth(\n this.#connectionSelector,\n msg[1],\n );\n const authRevisionChanged =\n updatedConnCtx.revision !== initialConnCtx.revision;\n\n await viewSyncer.updateAuth(\n this.#connectionSelector,\n msg,\n authRevisionChanged,\n );\n });\n break;\n case 'deleteClients': {\n const deletedClientIDs = await startAsyncSpan(\n tracer,\n 'connection.deleteClients',\n () => viewSyncer.deleteClients(this.#connectionSelector, msg),\n );\n if (this.#pusher && deletedClientIDs.length > 0) {\n await this.#pusher.deleteClientMutations(\n this.#connectionSelector,\n deletedClientIDs,\n );\n }\n break;\n }\n case 'initConnection': {\n this.#connContextManager.initConnection(\n this.#connectionSelector,\n msg[1],\n );\n return withTraceparent(msg[1].traceparent, () => {\n const ret: HandlerResult[] = [\n {\n type: 'stream',\n source: 'viewSyncer',\n stream: startSpan(tracer, 'connection.initConnection', () =>\n viewSyncer.initConnection(this.#connectionSelector, msg),\n ),\n },\n ];\n\n // Given we support both CRUD and Custom mutators,\n // we do not initialize the `pusher` unless the user has opted\n // into custom mutations. We detect that by checking\n // if the pushURL has been set.\n if (this.#pusher) {\n ret.push({\n type: 'stream',\n source: 'pusher',\n stream: this.#pusher.initConnection(this.#connectionSelector),\n });\n }\n\n return ret;\n });\n }\n case 'closeConnection':\n // This message is deprecated and no longer used.\n break;\n\n case 'inspect':\n await startAsyncSpan(tracer, 'connection.inspect', () =>\n viewSyncer.inspect(this.#connectionSelector, msg),\n );\n break;\n\n case 'ackMutationResponses':\n if (this.#pusher) {\n await this.#pusher.ackMutationResponses(\n this.#connectionSelector,\n msg[1],\n );\n }\n break;\n\n default:\n unreachable(msgType);\n }\n\n return [{type: 'ok'}];\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"syncer.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/workers/syncer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"syncer.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/workers/syncer.ts"],"names":[],"mappings":"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"}
@@ -14,7 +14,7 @@ import { DrainCoordinator } from "../services/view-syncer/drain-coordinator.js";
14
14
  import { Connection, sendError } from "./connection.js";
15
15
  import { SyncerWsMessageHandler } from "./syncer-ws-message-handler.js";
16
16
  import { resolver } from "@rocicorp/resolver";
17
- import { WebSocketServer } from "ws";
17
+ import { WebSocket, WebSocketServer } from "ws";
18
18
  import { pid } from "node:process";
19
19
  //#region ../zero-cache/src/workers/syncer.ts
20
20
  function computePipelineDedupStats(viewSyncers) {
@@ -153,6 +153,8 @@ var Syncer = class {
153
153
  #mutagens;
154
154
  #pushers;
155
155
  #connections = /* @__PURE__ */ new Map();
156
+ #pendingConnections = /* @__PURE__ */ new Map();
157
+ #clientGroupGenerations = /* @__PURE__ */ new Map();
156
158
  #drainCoordinator = new DrainCoordinator();
157
159
  #parent;
158
160
  #wss;
@@ -178,12 +180,30 @@ var Syncer = class {
178
180
  const notifier = createNotifierFrom(lc, parent, (state) => this.#recordReplicaReadyState(state));
179
181
  subscribeTo(lc, parent);
180
182
  this.#lc = lc;
181
- this.#viewSyncers = new ServiceRunner(lc, (id) => viewSyncerFactory(id, notifier.subscribe(), this.#drainCoordinator), (v) => v.keepalive());
183
+ this.#parent = parent;
184
+ this.#viewSyncers = new ServiceRunner(lc, (id) => viewSyncerFactory(id, notifier.subscribe(), this.#drainCoordinator), (v) => v.keepalive(), (id) => {
185
+ if ((this.#pendingConnections.get(id) ?? 0) === 0) {
186
+ const generation = this.#clientGroupGenerations.get(id);
187
+ this.#parent.send(["clientGroupStatus", {
188
+ clientGroupID: id,
189
+ active: false,
190
+ ...generation !== void 0 ? { generation } : {}
191
+ }]);
192
+ this.#clientGroupGenerations.delete(id);
193
+ }
194
+ });
182
195
  if (mutagenFactory) this.#mutagens = new ServiceRunner(lc, mutagenFactory, (m) => m.hasRefs());
183
196
  if (pusherFactory) this.#pushers = new ServiceRunner(lc, (id) => pusherFactory(id, this.#viewSyncers.getService(id).connContextManager), (p) => p.hasRefs());
184
- this.#parent = parent;
185
197
  this.#wss = new WebSocketServer(getWebSocketServerOptions(config));
186
- installWebSocketReceiver(lc, this.#wss, this.#createConnection, this.#parent);
198
+ installWebSocketReceiver(lc, this.#wss, this.#receiveConnection, this.#parent, (params) => {
199
+ this.#releasePending(params.clientGroupID, params.generation);
200
+ }, (params) => {
201
+ this.#pendingConnections.set(params.clientGroupID, (this.#pendingConnections.get(params.clientGroupID) ?? 0) + 1);
202
+ if (params.generation !== void 0) {
203
+ const current = this.#clientGroupGenerations.get(params.clientGroupID);
204
+ if (current === void 0 || params.generation > current) this.#clientGroupGenerations.set(params.clientGroupID, params.generation);
205
+ }
206
+ });
187
207
  setActiveClientGroupsGetter(() => this.#viewSyncers.size);
188
208
  getOrCreateGauge("sync", "active-client-groups", "Number of active client groups").addCallback((result) => result.observe(this.#viewSyncers.size));
189
209
  getOrCreateGauge("sync", "queries", "Active queries (pipelines) across all client groups").addCallback((result) => {
@@ -341,6 +361,15 @@ var Syncer = class {
341
361
  recordConnectionFailure("internal");
342
362
  throw e;
343
363
  }
364
+ if (ws.readyState !== WebSocket.OPEN) {
365
+ this.#lc.debug?.("websocket closed while resolving auth", {
366
+ clientGroupID,
367
+ clientID,
368
+ readyState: ws.readyState
369
+ });
370
+ recordConnectionFailure("closed_during_auth");
371
+ return;
372
+ }
344
373
  const viewSyncer = this.#viewSyncers.getService(clientGroupID);
345
374
  const connContextManager = viewSyncer.connContextManager;
346
375
  const group = connContextManager.getGroupState();
@@ -399,6 +428,28 @@ var Syncer = class {
399
428
  await connection.handleInitConnection(JSON.stringify(params.initConnectionMsg));
400
429
  }
401
430
  };
431
+ #receiveConnection = async (ws, params) => {
432
+ try {
433
+ await this.#createConnection(ws, params);
434
+ } finally {
435
+ this.#releasePending(params.clientGroupID, params.generation);
436
+ }
437
+ };
438
+ #releasePending(clientGroupID, generation) {
439
+ const remaining = (this.#pendingConnections.get(clientGroupID) ?? 1) - 1;
440
+ if (remaining <= 0) {
441
+ this.#pendingConnections.delete(clientGroupID);
442
+ if (!this.#viewSyncers.hasService(clientGroupID)) {
443
+ const gen = this.#clientGroupGenerations.get(clientGroupID) ?? generation;
444
+ this.#parent.send(["clientGroupStatus", {
445
+ clientGroupID,
446
+ active: false,
447
+ ...gen !== void 0 ? { generation: gen } : {}
448
+ }]);
449
+ this.#clientGroupGenerations.delete(clientGroupID);
450
+ }
451
+ } else this.#pendingConnections.set(clientGroupID, remaining);
452
+ }
402
453
  run() {
403
454
  return this.#stopped.promise;
404
455
  }
@@ -1 +1 @@
1
- {"version":3,"file":"syncer.js","names":["#lc","#viewSyncers","#mutagens","#pushers","#connections","#drainCoordinator","#parent","#wss","#stopped","#config","#validateLegacyJWT","#replicaReadyStates","#webSocketOpenConnections","#webSocketConnectionAttempts","#webSocketConnectionSuccesses","#webSocketConnectionFailures","#viewSyncerLag","#recordReplicaReadyState","#createConnection","#computeServingLagDistribution","#computePipelineDedupStats","#viewSyncerLagSampleInterval","#recordViewSyncerLagSamples","#eligibilityLogInterval","#logSharedAdvanceEligibility","#servingLagDistributionCache","#servingLagDistributionCacheClearQueued"],"sources":["../../../../../zero-cache/src/workers/syncer.ts"],"sourcesContent":["import {pid} from 'node:process';\nimport type {MessagePort} from 'node:worker_threads';\nimport type {LogContext} from '@rocicorp/logger';\nimport {resolver} from '@rocicorp/resolver';\nimport {WebSocketServer, type ServerOptions, type WebSocket} from 'ws';\nimport {promiseVoid} from '../../../shared/src/resolved-promises.ts';\nimport {ErrorKind} from '../../../zero-protocol/src/error-kind.ts';\nimport {ErrorOrigin} from '../../../zero-protocol/src/error-origin.ts';\nimport {\n isProtocolError,\n ProtocolError,\n} from '../../../zero-protocol/src/error.ts';\nimport {resolveAuth, type Auth, type ValidateLegacyJWT} from '../auth/auth.ts';\nimport {tokenConfigOptions} from '../auth/jwt.ts';\nimport {type ZeroConfig} from '../config/zero-config.ts';\nimport {\n getOrCreateCounter,\n getOrCreateGauge,\n getOrCreateNativeHistogram,\n getOrCreateUpDownCounter,\n} from '../observability/metrics.ts';\nimport {\n recordConnectionAttempted,\n recordConnectionSuccess,\n setActiveClientGroupsGetter,\n} from '../server/anonymous-otel-start.ts';\nimport type {Mutagen} from '../services/mutagen/mutagen.ts';\nimport type {Pusher} from '../services/mutagen/pusher.ts';\nimport type {ReplicaState} from '../services/replicator/replicator.ts';\nimport {ServiceRunner} from '../services/runner.ts';\nimport type {\n ActivityBasedService,\n Service,\n SingletonService,\n} from '../services/service.ts';\nimport type {ConnectionContextManager} from '../services/view-syncer/connection-context-manager.ts';\nimport {DrainCoordinator} from '../services/view-syncer/drain-coordinator.ts';\nimport type {ViewSyncer} from '../services/view-syncer/view-syncer.ts';\nimport type {Worker} from '../types/processes.ts';\nimport type {Subscription} from '../types/subscription.ts';\nimport {installWebSocketReceiver} from '../types/websocket-handoff.ts';\nimport type {ConnectParams} from './connect-params.ts';\nimport {Connection, sendError} from './connection.ts';\nimport {createNotifierFrom, subscribeTo} from './replicator.ts';\nimport {SyncerWsMessageHandler} from './syncer-ws-message-handler.ts';\n\nexport type SyncerWorkerData = {\n replicatorPort: MessagePort;\n};\n\nexport type ReplicaReadyState = {\n readonly watermark: string;\n readonly replicaReadyTimeMs: number;\n};\n\nexport type ServingLagViewSyncer = Pick<\n ViewSyncer,\n 'createdAtMs' | 'servedVersion' | 'servingLagEligible'\n>;\n\nexport type ServingLagStats = {\n readonly activeClientGroups: number;\n readonly laggingClientGroups: number;\n readonly minMs: number;\n readonly p50Ms: number;\n readonly p75Ms: number;\n readonly p99Ms: number;\n readonly maxMs: number;\n};\n\ntype ServingLagDistribution = ServingLagStats & {\n readonly lagsMs: readonly number[];\n};\n\nexport type PipelineDedupViewSyncer = Pick<\n ViewSyncer,\n 'pipelineHashes' | 'clientSchemaKey'\n>;\n\nexport type PipelineDedupStats = {\n readonly clientTotal: number;\n readonly internalTotal: number;\n // (clientSchemaKey, transformationHash) -> occurrence count across client\n // groups, for client/custom queries.\n readonly clientHashes: ReadonlyMap<\n string,\n {count: number; queryName: string | undefined}\n >;\n readonly internalUnique: number;\n readonly clientSchemas: number;\n};\n\nexport function computePipelineDedupStats(\n viewSyncers: Iterable<PipelineDedupViewSyncer>,\n): PipelineDedupStats {\n let clientTotal = 0;\n let internalTotal = 0;\n const clientHashes = new Map<\n string,\n {count: number; queryName: string | undefined}\n >();\n const internalHashes = new Set<string>();\n const schemaKeys = new Set<string>();\n let vsIndex = 0;\n for (const vs of viewSyncers) {\n const schemaKey = vs.clientSchemaKey;\n if (schemaKey !== undefined) {\n schemaKeys.add(schemaKey);\n }\n // Without a client schema the pipeline's dedup identity is unknown, so key\n // it uniquely per view-syncer; otherwise schema-less pipelines from\n // unrelated client groups would collapse under a shared `undefined/<hash>`\n // key and overstate deduplication.\n const dedupSchemaKey = schemaKey ?? `#vs${vsIndex}`;\n vsIndex++;\n for (const {\n transformationHash,\n internal,\n queryName,\n } of vs.pipelineHashes()) {\n // Pipelines only share when both the client schema (which determines\n // row keys) and the transformed AST are identical.\n const key = `${dedupSchemaKey}/${transformationHash}`;\n if (internal) {\n internalTotal++;\n internalHashes.add(key);\n } else {\n clientTotal++;\n const entry = clientHashes.get(key);\n if (entry) {\n entry.count++;\n } else {\n clientHashes.set(key, {count: 1, queryName});\n }\n }\n }\n }\n return {\n clientTotal,\n internalTotal,\n clientHashes,\n internalUnique: internalHashes.size,\n clientSchemas: schemaKeys.size,\n };\n}\n\nexport const MAX_REPLICA_READY_STATES = 10_000;\nconst VIEW_SYNCER_LAG_SAMPLE_INTERVAL_MS = 60_000;\nconst SHARED_ADVANCE_ELIGIBILITY_LOG_INTERVAL_MS = 5 * 60_000;\n\nconst PROTOCOL_VERSION_ATTRIBUTE = 'protocol.version';\nconst FAILURE_REASON_ATTRIBUTE = 'reason';\n\nfunction boundReplicaReadyStates(\n replicaReadyStates: ReplicaReadyState[],\n): void {\n if (replicaReadyStates.length > MAX_REPLICA_READY_STATES) {\n replicaReadyStates.splice(\n 0,\n replicaReadyStates.length - MAX_REPLICA_READY_STATES,\n );\n }\n}\n\nfunction pruneReplicaReadyStates(\n replicaReadyStates: ReplicaReadyState[],\n firstNeededIndex: number,\n): void {\n if (firstNeededIndex > 0) {\n replicaReadyStates.splice(0, firstNeededIndex);\n }\n\n boundReplicaReadyStates(replicaReadyStates);\n}\n\nfunction lowerBoundReplicaReadyTimeMs(\n replicaReadyStates: readonly ReplicaReadyState[],\n replicaReadyTimeMs: number,\n): number {\n let low = 0;\n let high = replicaReadyStates.length;\n while (low < high) {\n const mid = low + Math.floor((high - low) / 2);\n if (replicaReadyStates[mid].replicaReadyTimeMs < replicaReadyTimeMs) {\n low = mid + 1;\n } else {\n high = mid;\n }\n }\n return low;\n}\n\nfunction upperBoundWatermark(\n replicaReadyStates: readonly ReplicaReadyState[],\n watermark: string,\n): number {\n let low = 0;\n let high = replicaReadyStates.length;\n while (low < high) {\n const mid = low + Math.floor((high - low) / 2);\n if (replicaReadyStates[mid].watermark <= watermark) {\n low = mid + 1;\n } else {\n high = mid;\n }\n }\n return low;\n}\n\nfunction findFirstUnservedIndex(\n replicaReadyStates: readonly ReplicaReadyState[],\n viewSyncer: ServingLagViewSyncer,\n): number {\n const firstReadyAfterCreation = lowerBoundReplicaReadyTimeMs(\n replicaReadyStates,\n viewSyncer.createdAtMs,\n );\n const firstAfterServedVersion =\n viewSyncer.servedVersion === null\n ? 0\n : upperBoundWatermark(replicaReadyStates, viewSyncer.servedVersion);\n\n const firstUnservedIndex = Math.max(\n firstReadyAfterCreation,\n firstAfterServedVersion,\n );\n return firstUnservedIndex < replicaReadyStates.length\n ? firstUnservedIndex\n : -1;\n}\n\nfunction percentileNearestRank(\n sortedValues: readonly number[],\n percentile: number,\n): number {\n if (sortedValues.length === 0) {\n return 0;\n }\n const index = Math.min(\n sortedValues.length - 1,\n Math.max(0, Math.ceil((percentile / 100) * sortedValues.length) - 1),\n );\n return sortedValues[index];\n}\n\nfunction computeServingLagDistributionMs(\n now: number,\n replicaReadyStates: ReplicaReadyState[],\n viewSyncers: Iterable<ServingLagViewSyncer>,\n): ServingLagDistribution {\n const lags: number[] = [];\n let laggingClientGroups = 0;\n let firstNeededIndex = replicaReadyStates.length;\n\n for (const viewSyncer of viewSyncers) {\n if (!viewSyncer.servingLagEligible) {\n continue;\n }\n\n const firstUnservedIndex = findFirstUnservedIndex(\n replicaReadyStates,\n viewSyncer,\n );\n if (firstUnservedIndex === -1) {\n lags.push(0);\n continue;\n }\n\n firstNeededIndex = Math.min(firstNeededIndex, firstUnservedIndex);\n const lagMs = Math.max(\n 0,\n now - replicaReadyStates[firstUnservedIndex].replicaReadyTimeMs,\n );\n lags.push(lagMs);\n if (lagMs > 0) {\n laggingClientGroups++;\n }\n }\n\n pruneReplicaReadyStates(replicaReadyStates, firstNeededIndex);\n\n lags.sort((a, b) => a - b);\n return {\n activeClientGroups: lags.length,\n laggingClientGroups,\n // The percentile fields are for the legacy serving_lag_stats gauge.\n // The native view_syncer_lag histogram records lagsMs and computes\n // percentiles in Prometheus/AMP.\n minMs: lags[0] ?? 0,\n p50Ms: percentileNearestRank(lags, 50),\n p75Ms: percentileNearestRank(lags, 75),\n p99Ms: percentileNearestRank(lags, 99),\n maxMs: lags.at(-1) ?? 0,\n lagsMs: lags,\n };\n}\n\nexport function computeServingLagStatsMs(\n now: number,\n replicaReadyStates: ReplicaReadyState[],\n viewSyncers: Iterable<ServingLagViewSyncer>,\n): ServingLagStats {\n const distribution = computeServingLagDistributionMs(\n now,\n replicaReadyStates,\n viewSyncers,\n );\n return {\n activeClientGroups: distribution.activeClientGroups,\n laggingClientGroups: distribution.laggingClientGroups,\n minMs: distribution.minMs,\n p50Ms: distribution.p50Ms,\n p75Ms: distribution.p75Ms,\n p99Ms: distribution.p99Ms,\n maxMs: distribution.maxMs,\n };\n}\n\nexport function computeMaxServingLagMs(\n now: number,\n replicaReadyStates: ReplicaReadyState[],\n viewSyncers: Iterable<ServingLagViewSyncer>,\n): number {\n return computeServingLagStatsMs(now, replicaReadyStates, viewSyncers).maxMs;\n}\n\nfunction getWebSocketServerOptions(config: ZeroConfig): ServerOptions {\n const options: ServerOptions = {\n noServer: true,\n maxPayload: config.websocketMaxPayloadBytes,\n };\n\n if (config.websocketCompression) {\n options.perMessageDeflate = true;\n\n if (config.websocketCompressionOptions) {\n try {\n const compressionOptions = JSON.parse(\n config.websocketCompressionOptions,\n );\n options.perMessageDeflate = compressionOptions;\n } catch (e) {\n throw new Error(\n `Failed to parse ZERO_WEBSOCKET_COMPRESSION_OPTIONS: ${String(e)}. Expected valid JSON.`,\n );\n }\n }\n }\n\n return options;\n}\n\n/**\n * The Syncer worker receives websocket handoffs for \"/sync\" connections\n * from the Dispatcher in the main thread, and creates websocket\n * {@link Connection}s with a corresponding {@link ViewSyncer}, {@link Mutagen},\n * and {@link Subscription} to version notifications from the Replicator\n * worker.\n */\nexport class Syncer implements SingletonService {\n readonly id = `syncer-${pid}`;\n readonly #lc: LogContext;\n readonly #viewSyncers: ServiceRunner<ViewSyncer & ActivityBasedService>;\n readonly #mutagens: ServiceRunner<Mutagen & Service> | undefined;\n readonly #pushers: ServiceRunner<Pusher & Service> | undefined;\n readonly #connections = new Map<string, Connection>();\n readonly #drainCoordinator = new DrainCoordinator();\n readonly #parent: Worker;\n readonly #wss: WebSocketServer;\n readonly #stopped = resolver();\n readonly #config: ZeroConfig;\n readonly #validateLegacyJWT: ValidateLegacyJWT | undefined;\n readonly #replicaReadyStates: ReplicaReadyState[] = [];\n readonly #webSocketOpenConnections = getOrCreateUpDownCounter(\n 'sync',\n 'websocket.open_connections',\n 'Open client WebSocket connections.',\n );\n readonly #webSocketConnectionAttempts = getOrCreateCounter(\n 'sync',\n 'websocket.connection_attempts',\n 'Client WebSocket connection attempts.',\n );\n readonly #webSocketConnectionSuccesses = getOrCreateCounter(\n 'sync',\n 'websocket.connection_successes',\n 'Client WebSocket connections successfully initialized.',\n );\n readonly #webSocketConnectionFailures = getOrCreateCounter(\n 'sync',\n 'websocket.connection_failures',\n 'Client WebSocket connection attempts that failed before initialization.',\n );\n readonly #viewSyncerLag = getOrCreateNativeHistogram(\n 'sync',\n 'view_syncer_lag',\n {\n description:\n 'Lag from replica-ready change to ViewSyncer output for active ' +\n 'client groups. A change is output after IVM advancement, CVR flush, ' +\n 'and pokeEnd.',\n unit: 's',\n },\n );\n #servingLagDistributionCache: ServingLagDistribution | undefined;\n #servingLagDistributionCacheClearQueued = false;\n #viewSyncerLagSampleInterval: ReturnType<typeof setInterval> | undefined;\n #eligibilityLogInterval: ReturnType<typeof setInterval> | undefined;\n\n constructor(\n lc: LogContext,\n config: ZeroConfig,\n viewSyncerFactory: (\n id: string,\n sub: Subscription<ReplicaState>,\n drainCoordinator: DrainCoordinator,\n ) => ViewSyncer & ActivityBasedService,\n mutagenFactory: ((id: string) => Mutagen & Service) | undefined,\n pusherFactory:\n | ((\n id: string,\n connContextManager: ConnectionContextManager,\n ) => Pusher & Service)\n | undefined,\n parent: Worker,\n validateLegacyJWT: ValidateLegacyJWT | undefined,\n ) {\n this.#config = config;\n this.#validateLegacyJWT = validateLegacyJWT;\n // Relays notifications from the parent thread subscription\n // to ViewSyncers within this thread.\n const notifier = createNotifierFrom(lc, parent, state =>\n this.#recordReplicaReadyState(state),\n );\n subscribeTo(lc, parent);\n\n this.#lc = lc;\n this.#viewSyncers = new ServiceRunner(\n lc,\n id => viewSyncerFactory(id, notifier.subscribe(), this.#drainCoordinator),\n v => v.keepalive(),\n );\n if (mutagenFactory) {\n this.#mutagens = new ServiceRunner(lc, mutagenFactory, m => m.hasRefs());\n }\n if (pusherFactory) {\n this.#pushers = new ServiceRunner(\n lc,\n id =>\n pusherFactory(\n id,\n this.#viewSyncers.getService(id).connContextManager,\n ),\n p => p.hasRefs(),\n );\n }\n this.#parent = parent;\n this.#wss = new WebSocketServer(getWebSocketServerOptions(config));\n\n installWebSocketReceiver(\n lc,\n this.#wss,\n this.#createConnection,\n this.#parent,\n );\n\n setActiveClientGroupsGetter(() => this.#viewSyncers.size);\n\n getOrCreateGauge(\n 'sync',\n 'active-client-groups',\n 'Number of active client groups',\n ).addCallback(result => result.observe(this.#viewSyncers.size));\n\n getOrCreateGauge(\n 'sync',\n 'queries',\n 'Active queries (pipelines) across all client groups',\n ).addCallback(result => {\n let total = 0;\n for (const vs of this.#viewSyncers.getServices()) {\n total += vs.queryCount;\n }\n result.observe(total);\n });\n\n getOrCreateGauge(\n 'sync',\n 'rows',\n 'Tracked rows across all client groups',\n ).addCallback(result => {\n let total = 0;\n for (const vs of this.#viewSyncers.getServices()) {\n total += vs.rowCount;\n }\n result.observe(total);\n });\n\n getOrCreateGauge('sync', 'serving_lag', {\n description:\n 'Maximum time active ViewSyncer client groups have had unserved ' +\n 'replica changes. A change is served after IVM advancement, CVR flush, ' +\n 'and pokeEnd.',\n unit: 'millisecond',\n }).addCallback(result => {\n result.observe(this.#computeServingLagDistribution().maxMs);\n });\n\n getOrCreateGauge('sync', 'serving_lag_stats', {\n description:\n 'Distribution of time active ViewSyncer client groups have had ' +\n 'unserved replica changes. A change is served after IVM advancement, ' +\n 'CVR flush, and pokeEnd.',\n unit: 'millisecond',\n }).addCallback(result => {\n const stats = this.#computeServingLagDistribution();\n result.observe(stats.minMs, {stat: 'min'});\n result.observe(stats.p50Ms, {stat: 'p50'});\n result.observe(stats.p75Ms, {stat: 'p75'});\n result.observe(stats.p99Ms, {stat: 'p99'});\n result.observe(stats.maxMs, {stat: 'max'});\n });\n\n getOrCreateGauge(\n 'sync',\n 'serving_lagging_client_groups',\n 'Number of active client groups with unserved replica changes',\n ).addCallback(result => {\n result.observe(this.#computeServingLagDistribution().laggingClientGroups);\n });\n\n getOrCreateGauge(\n 'sync',\n 'pipelines_total',\n 'Query pipelines across all client groups on this worker, by query ' +\n 'type. The ratio to sync.pipelines_unique is the dedup factor ' +\n 'available to shared pipeline advancement.',\n ).addCallback(result => {\n const stats = this.#computePipelineDedupStats();\n result.observe(stats.clientTotal, {type: 'client'});\n result.observe(stats.internalTotal, {type: 'internal'});\n });\n\n getOrCreateGauge(\n 'sync',\n 'pipelines_unique',\n 'Distinct (clientSchema, transformationHash) pipelines across all ' +\n 'client groups on this worker, by query type',\n ).addCallback(result => {\n const stats = this.#computePipelineDedupStats();\n result.observe(stats.clientHashes.size, {type: 'client'});\n result.observe(stats.internalUnique, {type: 'internal'});\n });\n\n getOrCreateGauge(\n 'sync',\n 'client_schemas',\n 'Distinct client schemas across all client groups on this worker',\n ).addCallback(result => {\n result.observe(this.#computePipelineDedupStats().clientSchemas);\n });\n\n this.#viewSyncerLagSampleInterval = setInterval(\n () => this.#recordViewSyncerLagSamples(),\n VIEW_SYNCER_LAG_SAMPLE_INTERVAL_MS,\n );\n this.#viewSyncerLagSampleInterval.unref?.();\n\n this.#eligibilityLogInterval = setInterval(\n () => this.#logSharedAdvanceEligibility(),\n SHARED_ADVANCE_ELIGIBILITY_LOG_INTERVAL_MS,\n );\n this.#eligibilityLogInterval.unref?.();\n }\n\n #computeServingLagDistribution(): ServingLagDistribution {\n if (this.#servingLagDistributionCache) {\n return this.#servingLagDistributionCache;\n }\n\n const distribution = computeServingLagDistributionMs(\n Date.now(),\n this.#replicaReadyStates,\n this.#viewSyncers.getServices(),\n );\n this.#servingLagDistributionCache = distribution;\n if (!this.#servingLagDistributionCacheClearQueued) {\n this.#servingLagDistributionCacheClearQueued = true;\n queueMicrotask(() => {\n this.#servingLagDistributionCache = undefined;\n this.#servingLagDistributionCacheClearQueued = false;\n });\n }\n return distribution;\n }\n\n #recordViewSyncerLagSamples(): void {\n const {lagsMs} = this.#computeServingLagDistribution();\n for (const lagMs of lagsMs) {\n this.#viewSyncerLag.recordMs(lagMs);\n }\n }\n\n #computePipelineDedupStats(): PipelineDedupStats {\n return computePipelineDedupStats(this.#viewSyncers.getServices());\n }\n\n #logSharedAdvanceEligibility(): void {\n const stats = this.#computePipelineDedupStats();\n if (stats.clientTotal === 0) {\n return;\n }\n const unique = stats.clientHashes.size;\n const topDuplicated = [...stats.clientHashes.entries()]\n .filter(([, {count}]) => count > 1)\n .sort((a, b) => b[1].count - a[1].count)\n .slice(0, 10)\n .map(([key, {count, queryName}]) => ({key, count, queryName}));\n this.#lc.info?.('shared-advance-eligibility', {\n clientPipelines: stats.clientTotal,\n uniqueClientPipelines: unique,\n dedupFactor: Number((stats.clientTotal / Math.max(unique, 1)).toFixed(2)),\n internalPipelines: stats.internalTotal,\n clientSchemas: stats.clientSchemas,\n topDuplicated,\n });\n }\n\n #recordReplicaReadyState(state: ReplicaState): void {\n if (\n state.watermark === undefined ||\n state.replicaReadyTimeMs === undefined\n ) {\n return;\n }\n const last = this.#replicaReadyStates.at(-1);\n if (last && last.watermark >= state.watermark) {\n return;\n }\n this.#replicaReadyStates.push({\n watermark: state.watermark,\n replicaReadyTimeMs: state.replicaReadyTimeMs,\n });\n if (this.#viewSyncers.size === 0) {\n this.#replicaReadyStates.length = 0;\n return;\n }\n boundReplicaReadyStates(this.#replicaReadyStates);\n }\n\n readonly #createConnection = async (ws: WebSocket, params: ConnectParams) => {\n const metricAttributes = {\n [PROTOCOL_VERSION_ATTRIBUTE]: params.protocolVersion,\n };\n let connectionResultRecorded = false;\n const recordConnectionFailure = (reason: string) => {\n if (connectionResultRecorded) {\n return;\n }\n connectionResultRecorded = true;\n this.#webSocketConnectionFailures.add(1, {\n ...metricAttributes,\n [FAILURE_REASON_ATTRIBUTE]: reason,\n });\n };\n const recordConnectionSuccessMetric = () => {\n if (connectionResultRecorded) {\n return;\n }\n connectionResultRecorded = true;\n this.#webSocketConnectionSuccesses.add(1, metricAttributes);\n };\n\n this.#webSocketConnectionAttempts.add(1, metricAttributes);\n this.#webSocketOpenConnections.add(1, metricAttributes);\n ws.once('close', () => {\n this.#webSocketOpenConnections.add(-1, metricAttributes);\n });\n\n this.#lc.debug?.(\n 'creating connection',\n params.clientGroupID,\n params.clientID,\n );\n recordConnectionAttempted();\n const {clientID, clientGroupID, auth, userID} = params;\n const hasProvidedAuth = auth !== undefined && auth !== '';\n const incomingUserID = userID ?? null;\n\n if (hasProvidedAuth) {\n const tokenOptions = tokenConfigOptions(this.#config.auth ?? {});\n\n const hasPushOrMutate =\n this.#config?.push?.url !== undefined ||\n this.#config?.mutate?.url !== undefined;\n const hasQueries =\n this.#config?.query?.url !== undefined ||\n this.#config?.getQueries?.url !== undefined;\n\n // must either have one of the token options set or have custom mutations & queries enabled\n const hasExactlyOneTokenOption = tokenOptions.length === 1;\n const hasCustomEndpoints = hasPushOrMutate && hasQueries;\n if (!hasExactlyOneTokenOption && !hasCustomEndpoints) {\n recordConnectionFailure('configuration');\n throw new Error(\n 'Exactly one of jwk, secret, or jwksUrl must be set in order to verify tokens but actually the following were set: ' +\n JSON.stringify(tokenOptions) +\n '. You may also set both ZERO_MUTATE_URL and ZERO_QUERY_URL to enable custom mutations and queries without passing token verification options.',\n );\n }\n }\n\n let initialAuth: Auth | undefined;\n\n // Verify JWT BEFORE touching existing connections - prevents unauthenticated\n // attackers from force-disconnecting legitimate users via DoS.\n try {\n initialAuth = await resolveAuth(\n this.#lc\n .withContext('clientGroupID', clientGroupID)\n .withContext('clientID', clientID),\n // no previous auth, since this is a new connection, and resolveAuth is\n // connection scoped, not client group scoped\n undefined,\n incomingUserID,\n auth,\n this.#validateLegacyJWT,\n );\n } catch (e) {\n if (isProtocolError(e)) {\n this.#lc.warn?.(\n 'Rejecting sync connection during initial auth resolution',\n {\n clientGroupID,\n clientID,\n incomingUserID,\n hasProvidedAuth,\n errorKind: e.message,\n },\n );\n recordConnectionFailure('auth');\n sendError(this.#lc, ws, e.errorBody);\n ws.close(3000, e.errorBody.message);\n return;\n }\n recordConnectionFailure('internal');\n throw e;\n }\n\n const viewSyncer = this.#viewSyncers.getService(clientGroupID);\n const connContextManager = viewSyncer.connContextManager;\n const group = connContextManager.getGroupState();\n\n // TODO(0xcadams): we only check for user ID mismatch here if the group is\n // already validated. This prevents wrong-user reconnects from evicting a\n // healthy connection, but it does not protect against same-user reconnects\n // with an invalid opaque token. The long-term fix is to keep the replacement\n // connection pending until its auth is fully validated, and only then replace\n // the existing socket.\n if (\n group.pinnedUser !== undefined &&\n group.pinnedUser.id !== incomingUserID\n ) {\n const error = new ProtocolError({\n kind: ErrorKind.Unauthorized,\n message:\n 'Client groups are pinned to a single userID. Connection userID does not match existing client group userID.',\n origin: ErrorOrigin.ZeroCache,\n });\n recordConnectionFailure('user_mismatch');\n sendError(this.#lc, ws, error.errorBody);\n ws.close(3000, error.message);\n return;\n }\n\n // Check for and close existing connections AFTER auth is validated\n const existing = this.#connections.get(clientID);\n if (existing) {\n this.#lc.debug?.(\n `client ${clientID} already connected, closing existing connection`,\n );\n existing.close(`replaced by ${params.wsID}`);\n }\n\n connContextManager.registerConnection(\n {clientID, wsID: params.wsID},\n params,\n initialAuth,\n );\n\n const mutagen = this.#mutagens?.getService(clientGroupID);\n const pusher = this.#pushers?.getService(clientGroupID);\n // a new connection is using the mutagen and pusher. Bump their ref counts.\n mutagen?.ref();\n pusher?.ref();\n\n let connection: Connection;\n try {\n connection = new Connection(\n this.#lc,\n params,\n ws,\n this.#config.allowLegacyQueries,\n new SyncerWsMessageHandler(\n this.#lc,\n params,\n connContextManager,\n viewSyncer,\n mutagen,\n pusher,\n ),\n () => {\n connContextManager.closeConnection({\n clientID,\n wsID: params.wsID,\n });\n if (this.#connections.get(clientID) === connection) {\n this.#connections.delete(clientID);\n }\n // Connection is closed. We can unref the mutagen and pusher.\n // If their ref counts are zero, they will stop themselves and set themselves invalid.\n mutagen?.unref();\n pusher?.unref();\n },\n );\n } catch (e) {\n recordConnectionFailure('internal');\n connContextManager.closeConnection({clientID, wsID: params.wsID});\n mutagen?.unref();\n pusher?.unref();\n throw e;\n }\n\n this.#connections.set(clientID, connection);\n\n if (connection.init()) {\n recordConnectionSuccessMetric();\n recordConnectionSuccess();\n } else {\n recordConnectionFailure('protocol_version');\n }\n\n if (params.initConnectionMsg) {\n this.#lc.debug?.(\n 'handling init connection message from sec header',\n params.clientGroupID,\n params.clientID,\n );\n await connection.handleInitConnection(\n JSON.stringify(params.initConnectionMsg),\n );\n }\n };\n\n run() {\n return this.#stopped.promise;\n }\n\n /**\n * Graceful shutdown involves shutting down view syncers one at a time, pausing\n * for the duration of view syncer's hydration between each one. This paces the\n * disconnects to avoid creating a backlog of hydrations in the receiving server\n * when the clients reconnect.\n */\n async drain() {\n const start = Date.now();\n this.#lc.info?.(`draining ${this.#viewSyncers.size} view-syncers`);\n\n this.#drainCoordinator.drainNextIn(0);\n\n while (this.#viewSyncers.size) {\n await this.#drainCoordinator.forceDrainTimeout;\n\n // Pick an arbitrary view syncer to force drain.\n for (const vs of this.#viewSyncers.getServices()) {\n this.#lc.debug?.(`draining view-syncer ${vs.id} (forced)`);\n // When this drain or an elective drain completes, the forceDrainTimeout will\n // resolve after the next drain interval.\n void vs.stop();\n break;\n }\n }\n this.#lc.info?.(`finished draining (${Date.now() - start} ms)`);\n }\n\n stop() {\n clearInterval(this.#viewSyncerLagSampleInterval);\n clearInterval(this.#eligibilityLogInterval);\n this.#wss.close();\n this.#stopped.resolve();\n return promiseVoid;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"syncer.js","names":["#lc","#viewSyncers","#mutagens","#pushers","#connections","#pendingConnections","#clientGroupGenerations","#drainCoordinator","#parent","#wss","#stopped","#config","#validateLegacyJWT","#replicaReadyStates","#webSocketOpenConnections","#webSocketConnectionAttempts","#webSocketConnectionSuccesses","#webSocketConnectionFailures","#viewSyncerLag","#recordReplicaReadyState","#receiveConnection","#releasePending","#computeServingLagDistribution","#computePipelineDedupStats","#viewSyncerLagSampleInterval","#recordViewSyncerLagSamples","#eligibilityLogInterval","#logSharedAdvanceEligibility","#servingLagDistributionCache","#servingLagDistributionCacheClearQueued","#createConnection"],"sources":["../../../../../zero-cache/src/workers/syncer.ts"],"sourcesContent":["import {pid} from 'node:process';\nimport type {MessagePort} from 'node:worker_threads';\nimport type {LogContext} from '@rocicorp/logger';\nimport {resolver} from '@rocicorp/resolver';\nimport {WebSocket, WebSocketServer, type ServerOptions} from 'ws';\nimport {promiseVoid} from '../../../shared/src/resolved-promises.ts';\nimport {ErrorKind} from '../../../zero-protocol/src/error-kind.ts';\nimport {ErrorOrigin} from '../../../zero-protocol/src/error-origin.ts';\nimport {\n isProtocolError,\n ProtocolError,\n} from '../../../zero-protocol/src/error.ts';\nimport {resolveAuth, type Auth, type ValidateLegacyJWT} from '../auth/auth.ts';\nimport {tokenConfigOptions} from '../auth/jwt.ts';\nimport {type ZeroConfig} from '../config/zero-config.ts';\nimport {\n getOrCreateCounter,\n getOrCreateGauge,\n getOrCreateNativeHistogram,\n getOrCreateUpDownCounter,\n} from '../observability/metrics.ts';\nimport {\n recordConnectionAttempted,\n recordConnectionSuccess,\n setActiveClientGroupsGetter,\n} from '../server/anonymous-otel-start.ts';\nimport type {Mutagen} from '../services/mutagen/mutagen.ts';\nimport type {Pusher} from '../services/mutagen/pusher.ts';\nimport type {ReplicaState} from '../services/replicator/replicator.ts';\nimport {ServiceRunner} from '../services/runner.ts';\nimport type {\n ActivityBasedService,\n Service,\n SingletonService,\n} from '../services/service.ts';\nimport type {ConnectionContextManager} from '../services/view-syncer/connection-context-manager.ts';\nimport {DrainCoordinator} from '../services/view-syncer/drain-coordinator.ts';\nimport type {ViewSyncer} from '../services/view-syncer/view-syncer.ts';\nimport type {ClientGroupStatusMessage, Worker} from '../types/processes.ts';\nimport type {Subscription} from '../types/subscription.ts';\nimport {installWebSocketReceiver} from '../types/websocket-handoff.ts';\nimport type {ConnectParams} from './connect-params.ts';\nimport {Connection, sendError} from './connection.ts';\nimport {createNotifierFrom, subscribeTo} from './replicator.ts';\nimport {SyncerWsMessageHandler} from './syncer-ws-message-handler.ts';\n\nexport type SyncerWorkerData = {\n replicatorPort: MessagePort;\n};\n\nexport type ReplicaReadyState = {\n readonly watermark: string;\n readonly replicaReadyTimeMs: number;\n};\n\nexport type ServingLagViewSyncer = Pick<\n ViewSyncer,\n 'createdAtMs' | 'servedVersion' | 'servingLagEligible'\n>;\n\nexport type ServingLagStats = {\n readonly activeClientGroups: number;\n readonly laggingClientGroups: number;\n readonly minMs: number;\n readonly p50Ms: number;\n readonly p75Ms: number;\n readonly p99Ms: number;\n readonly maxMs: number;\n};\n\ntype ServingLagDistribution = ServingLagStats & {\n readonly lagsMs: readonly number[];\n};\n\nexport type PipelineDedupViewSyncer = Pick<\n ViewSyncer,\n 'pipelineHashes' | 'clientSchemaKey'\n>;\n\nexport type PipelineDedupStats = {\n readonly clientTotal: number;\n readonly internalTotal: number;\n // (clientSchemaKey, transformationHash) -> occurrence count across client\n // groups, for client/custom queries.\n readonly clientHashes: ReadonlyMap<\n string,\n {count: number; queryName: string | undefined}\n >;\n readonly internalUnique: number;\n readonly clientSchemas: number;\n};\n\nexport function computePipelineDedupStats(\n viewSyncers: Iterable<PipelineDedupViewSyncer>,\n): PipelineDedupStats {\n let clientTotal = 0;\n let internalTotal = 0;\n const clientHashes = new Map<\n string,\n {count: number; queryName: string | undefined}\n >();\n const internalHashes = new Set<string>();\n const schemaKeys = new Set<string>();\n let vsIndex = 0;\n for (const vs of viewSyncers) {\n const schemaKey = vs.clientSchemaKey;\n if (schemaKey !== undefined) {\n schemaKeys.add(schemaKey);\n }\n // Without a client schema the pipeline's dedup identity is unknown, so key\n // it uniquely per view-syncer; otherwise schema-less pipelines from\n // unrelated client groups would collapse under a shared `undefined/<hash>`\n // key and overstate deduplication.\n const dedupSchemaKey = schemaKey ?? `#vs${vsIndex}`;\n vsIndex++;\n for (const {\n transformationHash,\n internal,\n queryName,\n } of vs.pipelineHashes()) {\n // Pipelines only share when both the client schema (which determines\n // row keys) and the transformed AST are identical.\n const key = `${dedupSchemaKey}/${transformationHash}`;\n if (internal) {\n internalTotal++;\n internalHashes.add(key);\n } else {\n clientTotal++;\n const entry = clientHashes.get(key);\n if (entry) {\n entry.count++;\n } else {\n clientHashes.set(key, {count: 1, queryName});\n }\n }\n }\n }\n return {\n clientTotal,\n internalTotal,\n clientHashes,\n internalUnique: internalHashes.size,\n clientSchemas: schemaKeys.size,\n };\n}\n\nexport const MAX_REPLICA_READY_STATES = 10_000;\nconst VIEW_SYNCER_LAG_SAMPLE_INTERVAL_MS = 60_000;\nconst SHARED_ADVANCE_ELIGIBILITY_LOG_INTERVAL_MS = 5 * 60_000;\n\nconst PROTOCOL_VERSION_ATTRIBUTE = 'protocol.version';\nconst FAILURE_REASON_ATTRIBUTE = 'reason';\n\nfunction boundReplicaReadyStates(\n replicaReadyStates: ReplicaReadyState[],\n): void {\n if (replicaReadyStates.length > MAX_REPLICA_READY_STATES) {\n replicaReadyStates.splice(\n 0,\n replicaReadyStates.length - MAX_REPLICA_READY_STATES,\n );\n }\n}\n\nfunction pruneReplicaReadyStates(\n replicaReadyStates: ReplicaReadyState[],\n firstNeededIndex: number,\n): void {\n if (firstNeededIndex > 0) {\n replicaReadyStates.splice(0, firstNeededIndex);\n }\n\n boundReplicaReadyStates(replicaReadyStates);\n}\n\nfunction lowerBoundReplicaReadyTimeMs(\n replicaReadyStates: readonly ReplicaReadyState[],\n replicaReadyTimeMs: number,\n): number {\n let low = 0;\n let high = replicaReadyStates.length;\n while (low < high) {\n const mid = low + Math.floor((high - low) / 2);\n if (replicaReadyStates[mid].replicaReadyTimeMs < replicaReadyTimeMs) {\n low = mid + 1;\n } else {\n high = mid;\n }\n }\n return low;\n}\n\nfunction upperBoundWatermark(\n replicaReadyStates: readonly ReplicaReadyState[],\n watermark: string,\n): number {\n let low = 0;\n let high = replicaReadyStates.length;\n while (low < high) {\n const mid = low + Math.floor((high - low) / 2);\n if (replicaReadyStates[mid].watermark <= watermark) {\n low = mid + 1;\n } else {\n high = mid;\n }\n }\n return low;\n}\n\nfunction findFirstUnservedIndex(\n replicaReadyStates: readonly ReplicaReadyState[],\n viewSyncer: ServingLagViewSyncer,\n): number {\n const firstReadyAfterCreation = lowerBoundReplicaReadyTimeMs(\n replicaReadyStates,\n viewSyncer.createdAtMs,\n );\n const firstAfterServedVersion =\n viewSyncer.servedVersion === null\n ? 0\n : upperBoundWatermark(replicaReadyStates, viewSyncer.servedVersion);\n\n const firstUnservedIndex = Math.max(\n firstReadyAfterCreation,\n firstAfterServedVersion,\n );\n return firstUnservedIndex < replicaReadyStates.length\n ? firstUnservedIndex\n : -1;\n}\n\nfunction percentileNearestRank(\n sortedValues: readonly number[],\n percentile: number,\n): number {\n if (sortedValues.length === 0) {\n return 0;\n }\n const index = Math.min(\n sortedValues.length - 1,\n Math.max(0, Math.ceil((percentile / 100) * sortedValues.length) - 1),\n );\n return sortedValues[index];\n}\n\nfunction computeServingLagDistributionMs(\n now: number,\n replicaReadyStates: ReplicaReadyState[],\n viewSyncers: Iterable<ServingLagViewSyncer>,\n): ServingLagDistribution {\n const lags: number[] = [];\n let laggingClientGroups = 0;\n let firstNeededIndex = replicaReadyStates.length;\n\n for (const viewSyncer of viewSyncers) {\n if (!viewSyncer.servingLagEligible) {\n continue;\n }\n\n const firstUnservedIndex = findFirstUnservedIndex(\n replicaReadyStates,\n viewSyncer,\n );\n if (firstUnservedIndex === -1) {\n lags.push(0);\n continue;\n }\n\n firstNeededIndex = Math.min(firstNeededIndex, firstUnservedIndex);\n const lagMs = Math.max(\n 0,\n now - replicaReadyStates[firstUnservedIndex].replicaReadyTimeMs,\n );\n lags.push(lagMs);\n if (lagMs > 0) {\n laggingClientGroups++;\n }\n }\n\n pruneReplicaReadyStates(replicaReadyStates, firstNeededIndex);\n\n lags.sort((a, b) => a - b);\n return {\n activeClientGroups: lags.length,\n laggingClientGroups,\n // The percentile fields are for the legacy serving_lag_stats gauge.\n // The native view_syncer_lag histogram records lagsMs and computes\n // percentiles in Prometheus/AMP.\n minMs: lags[0] ?? 0,\n p50Ms: percentileNearestRank(lags, 50),\n p75Ms: percentileNearestRank(lags, 75),\n p99Ms: percentileNearestRank(lags, 99),\n maxMs: lags.at(-1) ?? 0,\n lagsMs: lags,\n };\n}\n\nexport function computeServingLagStatsMs(\n now: number,\n replicaReadyStates: ReplicaReadyState[],\n viewSyncers: Iterable<ServingLagViewSyncer>,\n): ServingLagStats {\n const distribution = computeServingLagDistributionMs(\n now,\n replicaReadyStates,\n viewSyncers,\n );\n return {\n activeClientGroups: distribution.activeClientGroups,\n laggingClientGroups: distribution.laggingClientGroups,\n minMs: distribution.minMs,\n p50Ms: distribution.p50Ms,\n p75Ms: distribution.p75Ms,\n p99Ms: distribution.p99Ms,\n maxMs: distribution.maxMs,\n };\n}\n\nexport function computeMaxServingLagMs(\n now: number,\n replicaReadyStates: ReplicaReadyState[],\n viewSyncers: Iterable<ServingLagViewSyncer>,\n): number {\n return computeServingLagStatsMs(now, replicaReadyStates, viewSyncers).maxMs;\n}\n\nfunction getWebSocketServerOptions(config: ZeroConfig): ServerOptions {\n const options: ServerOptions = {\n noServer: true,\n maxPayload: config.websocketMaxPayloadBytes,\n };\n\n if (config.websocketCompression) {\n options.perMessageDeflate = true;\n\n if (config.websocketCompressionOptions) {\n try {\n const compressionOptions = JSON.parse(\n config.websocketCompressionOptions,\n );\n options.perMessageDeflate = compressionOptions;\n } catch (e) {\n throw new Error(\n `Failed to parse ZERO_WEBSOCKET_COMPRESSION_OPTIONS: ${String(e)}. Expected valid JSON.`,\n );\n }\n }\n }\n\n return options;\n}\n\n/**\n * The Syncer worker receives websocket handoffs for \"/sync\" connections\n * from the Dispatcher in the main thread, and creates websocket\n * {@link Connection}s with a corresponding {@link ViewSyncer}, {@link Mutagen},\n * and {@link Subscription} to version notifications from the Replicator\n * worker.\n */\nexport class Syncer implements SingletonService {\n readonly id = `syncer-${pid}`;\n readonly #lc: LogContext;\n readonly #viewSyncers: ServiceRunner<ViewSyncer & ActivityBasedService>;\n readonly #mutagens: ServiceRunner<Mutagen & Service> | undefined;\n readonly #pushers: ServiceRunner<Pusher & Service> | undefined;\n readonly #connections = new Map<string, Connection>();\n readonly #pendingConnections = new Map<string, number>();\n readonly #clientGroupGenerations = new Map<string, number>();\n readonly #drainCoordinator = new DrainCoordinator();\n readonly #parent: Worker;\n readonly #wss: WebSocketServer;\n readonly #stopped = resolver();\n readonly #config: ZeroConfig;\n readonly #validateLegacyJWT: ValidateLegacyJWT | undefined;\n readonly #replicaReadyStates: ReplicaReadyState[] = [];\n readonly #webSocketOpenConnections = getOrCreateUpDownCounter(\n 'sync',\n 'websocket.open_connections',\n 'Open client WebSocket connections.',\n );\n readonly #webSocketConnectionAttempts = getOrCreateCounter(\n 'sync',\n 'websocket.connection_attempts',\n 'Client WebSocket connection attempts.',\n );\n readonly #webSocketConnectionSuccesses = getOrCreateCounter(\n 'sync',\n 'websocket.connection_successes',\n 'Client WebSocket connections successfully initialized.',\n );\n readonly #webSocketConnectionFailures = getOrCreateCounter(\n 'sync',\n 'websocket.connection_failures',\n 'Client WebSocket connection attempts that failed before initialization.',\n );\n readonly #viewSyncerLag = getOrCreateNativeHistogram(\n 'sync',\n 'view_syncer_lag',\n {\n description:\n 'Lag from replica-ready change to ViewSyncer output for active ' +\n 'client groups. A change is output after IVM advancement, CVR flush, ' +\n 'and pokeEnd.',\n unit: 's',\n },\n );\n #servingLagDistributionCache: ServingLagDistribution | undefined;\n #servingLagDistributionCacheClearQueued = false;\n #viewSyncerLagSampleInterval: ReturnType<typeof setInterval> | undefined;\n #eligibilityLogInterval: ReturnType<typeof setInterval> | undefined;\n\n constructor(\n lc: LogContext,\n config: ZeroConfig,\n viewSyncerFactory: (\n id: string,\n sub: Subscription<ReplicaState>,\n drainCoordinator: DrainCoordinator,\n ) => ViewSyncer & ActivityBasedService,\n mutagenFactory: ((id: string) => Mutagen & Service) | undefined,\n pusherFactory:\n | ((\n id: string,\n connContextManager: ConnectionContextManager,\n ) => Pusher & Service)\n | undefined,\n parent: Worker,\n validateLegacyJWT: ValidateLegacyJWT | undefined,\n ) {\n this.#config = config;\n this.#validateLegacyJWT = validateLegacyJWT;\n // Relays notifications from the parent thread subscription\n // to ViewSyncers within this thread.\n const notifier = createNotifierFrom(lc, parent, state =>\n this.#recordReplicaReadyState(state),\n );\n subscribeTo(lc, parent);\n\n this.#lc = lc;\n this.#parent = parent;\n this.#viewSyncers = new ServiceRunner(\n lc,\n id => viewSyncerFactory(id, notifier.subscribe(), this.#drainCoordinator),\n v => v.keepalive(),\n id => {\n if ((this.#pendingConnections.get(id) ?? 0) === 0) {\n const generation = this.#clientGroupGenerations.get(id);\n this.#parent.send<ClientGroupStatusMessage>([\n 'clientGroupStatus',\n {\n clientGroupID: id,\n active: false,\n ...(generation !== undefined ? {generation} : {}),\n },\n ]);\n this.#clientGroupGenerations.delete(id);\n }\n },\n );\n if (mutagenFactory) {\n this.#mutagens = new ServiceRunner(lc, mutagenFactory, m => m.hasRefs());\n }\n if (pusherFactory) {\n this.#pushers = new ServiceRunner(\n lc,\n id =>\n pusherFactory(\n id,\n this.#viewSyncers.getService(id).connContextManager,\n ),\n p => p.hasRefs(),\n );\n }\n this.#wss = new WebSocketServer(getWebSocketServerOptions(config));\n\n installWebSocketReceiver(\n lc,\n this.#wss,\n this.#receiveConnection,\n this.#parent,\n params => {\n this.#releasePending(params.clientGroupID, params.generation);\n },\n params => {\n this.#pendingConnections.set(\n params.clientGroupID,\n (this.#pendingConnections.get(params.clientGroupID) ?? 0) + 1,\n );\n if (params.generation !== undefined) {\n const current = this.#clientGroupGenerations.get(\n params.clientGroupID,\n );\n if (current === undefined || params.generation > current) {\n this.#clientGroupGenerations.set(\n params.clientGroupID,\n params.generation,\n );\n }\n }\n },\n );\n\n setActiveClientGroupsGetter(() => this.#viewSyncers.size);\n\n getOrCreateGauge(\n 'sync',\n 'active-client-groups',\n 'Number of active client groups',\n ).addCallback(result => result.observe(this.#viewSyncers.size));\n\n getOrCreateGauge(\n 'sync',\n 'queries',\n 'Active queries (pipelines) across all client groups',\n ).addCallback(result => {\n let total = 0;\n for (const vs of this.#viewSyncers.getServices()) {\n total += vs.queryCount;\n }\n result.observe(total);\n });\n\n getOrCreateGauge(\n 'sync',\n 'rows',\n 'Tracked rows across all client groups',\n ).addCallback(result => {\n let total = 0;\n for (const vs of this.#viewSyncers.getServices()) {\n total += vs.rowCount;\n }\n result.observe(total);\n });\n\n getOrCreateGauge('sync', 'serving_lag', {\n description:\n 'Maximum time active ViewSyncer client groups have had unserved ' +\n 'replica changes. A change is served after IVM advancement, CVR flush, ' +\n 'and pokeEnd.',\n unit: 'millisecond',\n }).addCallback(result => {\n result.observe(this.#computeServingLagDistribution().maxMs);\n });\n\n getOrCreateGauge('sync', 'serving_lag_stats', {\n description:\n 'Distribution of time active ViewSyncer client groups have had ' +\n 'unserved replica changes. A change is served after IVM advancement, ' +\n 'CVR flush, and pokeEnd.',\n unit: 'millisecond',\n }).addCallback(result => {\n const stats = this.#computeServingLagDistribution();\n result.observe(stats.minMs, {stat: 'min'});\n result.observe(stats.p50Ms, {stat: 'p50'});\n result.observe(stats.p75Ms, {stat: 'p75'});\n result.observe(stats.p99Ms, {stat: 'p99'});\n result.observe(stats.maxMs, {stat: 'max'});\n });\n\n getOrCreateGauge(\n 'sync',\n 'serving_lagging_client_groups',\n 'Number of active client groups with unserved replica changes',\n ).addCallback(result => {\n result.observe(this.#computeServingLagDistribution().laggingClientGroups);\n });\n\n getOrCreateGauge(\n 'sync',\n 'pipelines_total',\n 'Query pipelines across all client groups on this worker, by query ' +\n 'type. The ratio to sync.pipelines_unique is the dedup factor ' +\n 'available to shared pipeline advancement.',\n ).addCallback(result => {\n const stats = this.#computePipelineDedupStats();\n result.observe(stats.clientTotal, {type: 'client'});\n result.observe(stats.internalTotal, {type: 'internal'});\n });\n\n getOrCreateGauge(\n 'sync',\n 'pipelines_unique',\n 'Distinct (clientSchema, transformationHash) pipelines across all ' +\n 'client groups on this worker, by query type',\n ).addCallback(result => {\n const stats = this.#computePipelineDedupStats();\n result.observe(stats.clientHashes.size, {type: 'client'});\n result.observe(stats.internalUnique, {type: 'internal'});\n });\n\n getOrCreateGauge(\n 'sync',\n 'client_schemas',\n 'Distinct client schemas across all client groups on this worker',\n ).addCallback(result => {\n result.observe(this.#computePipelineDedupStats().clientSchemas);\n });\n\n this.#viewSyncerLagSampleInterval = setInterval(\n () => this.#recordViewSyncerLagSamples(),\n VIEW_SYNCER_LAG_SAMPLE_INTERVAL_MS,\n );\n this.#viewSyncerLagSampleInterval.unref?.();\n\n this.#eligibilityLogInterval = setInterval(\n () => this.#logSharedAdvanceEligibility(),\n SHARED_ADVANCE_ELIGIBILITY_LOG_INTERVAL_MS,\n );\n this.#eligibilityLogInterval.unref?.();\n }\n\n #computeServingLagDistribution(): ServingLagDistribution {\n if (this.#servingLagDistributionCache) {\n return this.#servingLagDistributionCache;\n }\n\n const distribution = computeServingLagDistributionMs(\n Date.now(),\n this.#replicaReadyStates,\n this.#viewSyncers.getServices(),\n );\n this.#servingLagDistributionCache = distribution;\n if (!this.#servingLagDistributionCacheClearQueued) {\n this.#servingLagDistributionCacheClearQueued = true;\n queueMicrotask(() => {\n this.#servingLagDistributionCache = undefined;\n this.#servingLagDistributionCacheClearQueued = false;\n });\n }\n return distribution;\n }\n\n #recordViewSyncerLagSamples(): void {\n const {lagsMs} = this.#computeServingLagDistribution();\n for (const lagMs of lagsMs) {\n this.#viewSyncerLag.recordMs(lagMs);\n }\n }\n\n #computePipelineDedupStats(): PipelineDedupStats {\n return computePipelineDedupStats(this.#viewSyncers.getServices());\n }\n\n #logSharedAdvanceEligibility(): void {\n const stats = this.#computePipelineDedupStats();\n if (stats.clientTotal === 0) {\n return;\n }\n const unique = stats.clientHashes.size;\n const topDuplicated = [...stats.clientHashes.entries()]\n .filter(([, {count}]) => count > 1)\n .sort((a, b) => b[1].count - a[1].count)\n .slice(0, 10)\n .map(([key, {count, queryName}]) => ({key, count, queryName}));\n this.#lc.info?.('shared-advance-eligibility', {\n clientPipelines: stats.clientTotal,\n uniqueClientPipelines: unique,\n dedupFactor: Number((stats.clientTotal / Math.max(unique, 1)).toFixed(2)),\n internalPipelines: stats.internalTotal,\n clientSchemas: stats.clientSchemas,\n topDuplicated,\n });\n }\n\n #recordReplicaReadyState(state: ReplicaState): void {\n if (\n state.watermark === undefined ||\n state.replicaReadyTimeMs === undefined\n ) {\n return;\n }\n const last = this.#replicaReadyStates.at(-1);\n if (last && last.watermark >= state.watermark) {\n return;\n }\n this.#replicaReadyStates.push({\n watermark: state.watermark,\n replicaReadyTimeMs: state.replicaReadyTimeMs,\n });\n if (this.#viewSyncers.size === 0) {\n this.#replicaReadyStates.length = 0;\n return;\n }\n boundReplicaReadyStates(this.#replicaReadyStates);\n }\n\n readonly #createConnection = async (ws: WebSocket, params: ConnectParams) => {\n const metricAttributes = {\n [PROTOCOL_VERSION_ATTRIBUTE]: params.protocolVersion,\n };\n let connectionResultRecorded = false;\n const recordConnectionFailure = (reason: string) => {\n if (connectionResultRecorded) {\n return;\n }\n connectionResultRecorded = true;\n this.#webSocketConnectionFailures.add(1, {\n ...metricAttributes,\n [FAILURE_REASON_ATTRIBUTE]: reason,\n });\n };\n const recordConnectionSuccessMetric = () => {\n if (connectionResultRecorded) {\n return;\n }\n connectionResultRecorded = true;\n this.#webSocketConnectionSuccesses.add(1, metricAttributes);\n };\n\n this.#webSocketConnectionAttempts.add(1, metricAttributes);\n this.#webSocketOpenConnections.add(1, metricAttributes);\n ws.once('close', () => {\n this.#webSocketOpenConnections.add(-1, metricAttributes);\n });\n\n this.#lc.debug?.(\n 'creating connection',\n params.clientGroupID,\n params.clientID,\n );\n recordConnectionAttempted();\n const {clientID, clientGroupID, auth, userID} = params;\n const hasProvidedAuth = auth !== undefined && auth !== '';\n const incomingUserID = userID ?? null;\n\n if (hasProvidedAuth) {\n const tokenOptions = tokenConfigOptions(this.#config.auth ?? {});\n\n const hasPushOrMutate =\n this.#config?.push?.url !== undefined ||\n this.#config?.mutate?.url !== undefined;\n const hasQueries =\n this.#config?.query?.url !== undefined ||\n this.#config?.getQueries?.url !== undefined;\n\n // must either have one of the token options set or have custom mutations & queries enabled\n const hasExactlyOneTokenOption = tokenOptions.length === 1;\n const hasCustomEndpoints = hasPushOrMutate && hasQueries;\n if (!hasExactlyOneTokenOption && !hasCustomEndpoints) {\n recordConnectionFailure('configuration');\n throw new Error(\n 'Exactly one of jwk, secret, or jwksUrl must be set in order to verify tokens but actually the following were set: ' +\n JSON.stringify(tokenOptions) +\n '. You may also set both ZERO_MUTATE_URL and ZERO_QUERY_URL to enable custom mutations and queries without passing token verification options.',\n );\n }\n }\n\n let initialAuth: Auth | undefined;\n\n // Verify JWT BEFORE touching existing connections - prevents unauthenticated\n // attackers from force-disconnecting legitimate users via DoS.\n try {\n initialAuth = await resolveAuth(\n this.#lc\n .withContext('clientGroupID', clientGroupID)\n .withContext('clientID', clientID),\n // no previous auth, since this is a new connection, and resolveAuth is\n // connection scoped, not client group scoped\n undefined,\n incomingUserID,\n auth,\n this.#validateLegacyJWT,\n );\n } catch (e) {\n if (isProtocolError(e)) {\n this.#lc.warn?.(\n 'Rejecting sync connection during initial auth resolution',\n {\n clientGroupID,\n clientID,\n incomingUserID,\n hasProvidedAuth,\n errorKind: e.message,\n },\n );\n recordConnectionFailure('auth');\n sendError(this.#lc, ws, e.errorBody);\n ws.close(3000, e.errorBody.message);\n return;\n }\n recordConnectionFailure('internal');\n throw e;\n }\n\n // Resolving auth can take a while (e.g. a remote JWKS fetch), during\n // which the client may have disconnected. A Connection created for a\n // closed socket never receives the 'close' event and is thus never\n // cleaned up, leaking its timer, its registrations in the connection\n // context manager and its refs on the mutagen and pusher services.\n if (ws.readyState !== WebSocket.OPEN) {\n this.#lc.debug?.('websocket closed while resolving auth', {\n clientGroupID,\n clientID,\n readyState: ws.readyState,\n });\n recordConnectionFailure('closed_during_auth');\n return;\n }\n\n const viewSyncer = this.#viewSyncers.getService(clientGroupID);\n const connContextManager = viewSyncer.connContextManager;\n const group = connContextManager.getGroupState();\n\n // TODO(0xcadams): we only check for user ID mismatch here if the group is\n // already validated. This prevents wrong-user reconnects from evicting a\n // healthy connection, but it does not protect against same-user reconnects\n // with an invalid opaque token. The long-term fix is to keep the replacement\n // connection pending until its auth is fully validated, and only then replace\n // the existing socket.\n if (\n group.pinnedUser !== undefined &&\n group.pinnedUser.id !== incomingUserID\n ) {\n const error = new ProtocolError({\n kind: ErrorKind.Unauthorized,\n message:\n 'Client groups are pinned to a single userID. Connection userID does not match existing client group userID.',\n origin: ErrorOrigin.ZeroCache,\n });\n recordConnectionFailure('user_mismatch');\n sendError(this.#lc, ws, error.errorBody);\n ws.close(3000, error.message);\n return;\n }\n\n // Check for and close existing connections AFTER auth is validated\n const existing = this.#connections.get(clientID);\n if (existing) {\n this.#lc.debug?.(\n `client ${clientID} already connected, closing existing connection`,\n );\n existing.close(`replaced by ${params.wsID}`);\n }\n\n connContextManager.registerConnection(\n {clientID, wsID: params.wsID},\n params,\n initialAuth,\n );\n\n const mutagen = this.#mutagens?.getService(clientGroupID);\n const pusher = this.#pushers?.getService(clientGroupID);\n // a new connection is using the mutagen and pusher. Bump their ref counts.\n mutagen?.ref();\n pusher?.ref();\n\n let connection: Connection;\n try {\n connection = new Connection(\n this.#lc,\n params,\n ws,\n this.#config.allowLegacyQueries,\n new SyncerWsMessageHandler(\n this.#lc,\n params,\n connContextManager,\n viewSyncer,\n mutagen,\n pusher,\n ),\n () => {\n connContextManager.closeConnection({\n clientID,\n wsID: params.wsID,\n });\n if (this.#connections.get(clientID) === connection) {\n this.#connections.delete(clientID);\n }\n // Connection is closed. We can unref the mutagen and pusher.\n // If their ref counts are zero, they will stop themselves and set themselves invalid.\n mutagen?.unref();\n pusher?.unref();\n },\n );\n } catch (e) {\n recordConnectionFailure('internal');\n connContextManager.closeConnection({clientID, wsID: params.wsID});\n mutagen?.unref();\n pusher?.unref();\n throw e;\n }\n\n this.#connections.set(clientID, connection);\n\n if (connection.init()) {\n recordConnectionSuccessMetric();\n recordConnectionSuccess();\n } else {\n recordConnectionFailure('protocol_version');\n }\n\n if (params.initConnectionMsg) {\n this.#lc.debug?.(\n 'handling init connection message from sec header',\n params.clientGroupID,\n params.clientID,\n );\n await connection.handleInitConnection(\n JSON.stringify(params.initConnectionMsg),\n );\n }\n };\n\n readonly #receiveConnection = async (\n ws: WebSocket,\n params: ConnectParams,\n ) => {\n try {\n await this.#createConnection(ws, params);\n } finally {\n this.#releasePending(params.clientGroupID, params.generation);\n }\n };\n\n #releasePending(clientGroupID: string, generation: number | undefined): void {\n const remaining = (this.#pendingConnections.get(clientGroupID) ?? 1) - 1;\n if (remaining <= 0) {\n this.#pendingConnections.delete(clientGroupID);\n if (!this.#viewSyncers.hasService(clientGroupID)) {\n const gen =\n this.#clientGroupGenerations.get(clientGroupID) ?? generation;\n this.#parent.send<ClientGroupStatusMessage>([\n 'clientGroupStatus',\n {\n clientGroupID,\n active: false,\n ...(gen !== undefined ? {generation: gen} : {}),\n },\n ]);\n this.#clientGroupGenerations.delete(clientGroupID);\n }\n } else {\n this.#pendingConnections.set(clientGroupID, remaining);\n }\n }\n\n run() {\n return this.#stopped.promise;\n }\n\n /**\n * Graceful shutdown involves shutting down view syncers one at a time, pausing\n * for the duration of view syncer's hydration between each one. This paces the\n * disconnects to avoid creating a backlog of hydrations in the receiving server\n * when the clients reconnect.\n */\n async drain() {\n const start = Date.now();\n this.#lc.info?.(`draining ${this.#viewSyncers.size} view-syncers`);\n\n this.#drainCoordinator.drainNextIn(0);\n\n while (this.#viewSyncers.size) {\n await this.#drainCoordinator.forceDrainTimeout;\n\n // Pick an arbitrary view syncer to force drain.\n for (const vs of this.#viewSyncers.getServices()) {\n this.#lc.debug?.(`draining view-syncer ${vs.id} (forced)`);\n // When this drain or an elective drain completes, the forceDrainTimeout will\n // resolve after the next drain interval.\n void vs.stop();\n break;\n }\n }\n this.#lc.info?.(`finished draining (${Date.now() - start} ms)`);\n }\n\n stop() {\n clearInterval(this.#viewSyncerLagSampleInterval);\n clearInterval(this.#eligibilityLogInterval);\n this.#wss.close();\n this.#stopped.resolve();\n return promiseVoid;\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"active-clients-manager.d.ts","sourceRoot":"","sources":["../../../../../zero-client/src/client/active-clients-manager.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,aAAa,EACb,QAAQ,EACT,MAAM,qCAAqC,CAAC;AA0C7C;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAAa,oBAAoB;;IAC/B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAK1B;;;OAGG;IACH,KAAK,EAAE,CAAC,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAEhD;;;OAGG;IACH,QAAQ,EACJ,CAAC,CAAC,QAAQ,EAAE,QAAQ,EAAE,aAAa,EAAE,aAAa,KAAK,IAAI,CAAC,GAC5D,SAAS,CAAC;IAEd;;;;;OAKG;IACH,OAAa,MAAM,CACjB,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,oBAAoB,CAAC,CAI/B;IAED,OAAO,eASN;IAiDD,IAAI,aAAa,IAAI,WAAW,CAAC,MAAM,CAAC,CAEvC;CAyCF"}
1
+ {"version":3,"file":"active-clients-manager.d.ts","sourceRoot":"","sources":["../../../../../zero-client/src/client/active-clients-manager.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,aAAa,EACb,QAAQ,EACT,MAAM,qCAAqC,CAAC;AA0C7C;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAAa,oBAAoB;;IAC/B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAQ1B;;;OAGG;IACH,KAAK,EAAE,CAAC,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAEhD;;;OAGG;IACH,QAAQ,EACJ,CAAC,CAAC,QAAQ,EAAE,QAAQ,EAAE,aAAa,EAAE,aAAa,KAAK,IAAI,CAAC,GAC5D,SAAS,CAAC;IAEd;;;;;OAKG;IACH,OAAa,MAAM,CACjB,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,oBAAoB,CAAC,CAI/B;IAED,OAAO,eAUN;IAiDD,IAAI,aAAa,IAAI,WAAW,CAAC,MAAM,CAAC,CAEvC;CA0CF"}
@@ -55,6 +55,7 @@ var ActiveClientsManager = class ActiveClientsManager {
55
55
  #resolver = resolver();
56
56
  #lockManager;
57
57
  #activeClients = /* @__PURE__ */ new Set();
58
+ #seenClients = /* @__PURE__ */ new Set();
58
59
  /**
59
60
  * A callback that is called when a client is added to the client group.
60
61
  * It receives the client ID of the added client.
@@ -80,6 +81,7 @@ var ActiveClientsManager = class ActiveClientsManager {
80
81
  this.clientGroupID = clientGroupID;
81
82
  this.clientID = clientID;
82
83
  this.#lockManager = getClientLockManager(signal);
84
+ this.#seenClients.add(clientID);
83
85
  this.#activeClients.add(clientID);
84
86
  }
85
87
  async #init(signal) {
@@ -120,7 +122,8 @@ var ActiveClientsManager = class ActiveClientsManager {
120
122
  this.#lockManager.request(name, "shared", () => this.#removeClient(clientID)).catch(ignoreAbortError);
121
123
  }
122
124
  #addClient(clientID) {
123
- if (!this.#activeClients.has(clientID)) {
125
+ if (!this.#seenClients.has(clientID)) {
126
+ this.#seenClients.add(clientID);
124
127
  this.#activeClients.add(clientID);
125
128
  this.#addSharedLockForOtherClient(clientID);
126
129
  this.onAdd?.(clientID);
@@ -1 +1 @@
1
- {"version":3,"file":"active-clients-manager.js","names":["#resolver","#lockManager","#activeClients","#init","#addClient","#getActiveClients","#removeClient","#addSharedLockForOtherClient","#locks","#signal","#listeners"],"sources":["../../../../../zero-client/src/client/active-clients-manager.ts"],"sourcesContent":["import {resolver} from '@rocicorp/resolver';\nimport type {\n ClientGroupID,\n ClientID,\n} from '../../../replicache/src/sync/ids.ts';\nimport {BroadcastChannel} from '../../../shared/src/broadcast-channel.ts';\nimport {getBrowserGlobal} from '../../../shared/src/browser-env.ts';\nimport type {MaybePromise} from '../../../shared/src/types.ts';\n\n/**\n * The prefix for the keys used for the locks and the broadcast channels.\n */\nconst keyPrefix = 'zero-active';\n\nfunction toLockName(clientGroupID: string, clientID: string): string {\n return `${keyPrefix}/${clientGroupID}/${clientID}`;\n}\n\nfunction toBroadcastChannelName(clientGroupID: string): string {\n return `${keyPrefix}/${clientGroupID}`;\n}\n\nfunction fromLockName(\n lockKey: string | undefined,\n): {clientGroupID: string; clientID: string} | undefined {\n if (!lockKey || !lockKey.startsWith(keyPrefix)) {\n return undefined;\n }\n const parts = lockKey.slice(keyPrefix.length).split('/');\n if (parts.length !== 3) {\n return undefined;\n }\n return {\n clientGroupID: parts[1],\n clientID: parts[2],\n };\n}\n\nfunction ignoreAbortError(e: unknown) {\n if (e instanceof Error && e.name === 'AbortError') {\n // Ignore the AbortError, it is expected when the signal is aborted.\n return;\n }\n throw e;\n}\n\n/**\n * A class that lists the active clients in a client group. It uses the\n * `navigator.locks` API to manage locks for each client. The class is designed\n * to be used in a browser environment where the `navigator.locks` API is\n * available.\n *\n * When navigator.locks is not available, it will return a set only containing\n * the clients in the current scripting context (window, worker, etc).\n *\n * It uses one exclusive lock per client, identified by a combination of\n * `clientGroupID` and `clientID`. Then the `query` method is used to get the\n * list of all clients that hold or are waiting for locks in the same client\n * group.\n *\n * It also tries to get a shared lock for each client in the group, so that it\n * can be notified when the exclusive lock is released. This allows the class to\n * keep track of the active clients in the group and notify when an existing\n * client is removed.\n *\n * The class also uses a `BroadcastChannel` to notify other clients in the\n * same client group when a new client is added. This allows the class to keep\n * track of the active clients in the group and notify when a new client is\n * added.\n */\nexport class ActiveClientsManager {\n readonly clientGroupID: string;\n readonly clientID: string;\n readonly #resolver = resolver<void>();\n readonly #lockManager: ClientLockManager;\n readonly #activeClients: Set<string> = new Set();\n\n /**\n * A callback that is called when a client is added to the client group.\n * It receives the client ID of the added client.\n */\n onAdd: ((clientID: string) => void) | undefined;\n\n /**\n * A callback that is called when a client is deleted from the client group.\n * It receives the client ID of the deleted client.\n */\n onDelete:\n | ((clientID: ClientID, clientGroupID: ClientGroupID) => void)\n | undefined;\n\n /**\n * Creates an instance of `ActiveClientsManager` for the specified client\n * group and client ID. It will return a promise that resolves when the\n * instance is ready to use, which means that it has successfully acquired the\n * exclusive lock for the client and has retrieved the list of active clients.\n */\n static async create(\n clientGroupID: string,\n clientID: string,\n signal: AbortSignal,\n ): Promise<ActiveClientsManager> {\n const instance = new ActiveClientsManager(clientGroupID, clientID, signal);\n await instance.#init(signal);\n return instance;\n }\n\n private constructor(\n clientGroupID: string,\n clientID: string,\n signal: AbortSignal,\n ) {\n this.clientGroupID = clientGroupID;\n this.clientID = clientID;\n this.#lockManager = getClientLockManager(signal);\n this.#activeClients.add(clientID);\n }\n\n async #init(signal: AbortSignal): Promise<void> {\n const {clientGroupID, clientID} = this;\n const name = toLockName(clientGroupID, clientID);\n\n // The BroadcastChannel is used to notify other clients in the same client\n // group when a new client is added. It listens for messages that contain\n // the lock name, which is used to identify the client. When a message is\n // received, it checks if the client belongs to the same client group and\n // adds it to the list of active clients. It also adds a shared lock for\n // the client, so that it can be notified when the exclusive lock is\n // released.\n const channel = new BroadcastChannel(toBroadcastChannelName(clientGroupID));\n channel.addEventListener(\n 'message',\n e => {\n const client = fromLockName(e.data);\n if (client?.clientGroupID === this.clientGroupID) {\n this.#addClient(client.clientID);\n }\n },\n {signal},\n );\n\n this.#lockManager\n .request(name, 'exclusive', () => this.#resolver.promise)\n .catch(ignoreAbortError);\n\n signal.addEventListener(\n 'abort',\n () => {\n this.#lockManager.release(name, () => this.#resolver.resolve());\n channel.close();\n },\n {once: true},\n );\n\n for (const clientID of await this.#getActiveClients()) {\n if (clientID !== this.clientID) {\n this.#addClient(clientID);\n }\n }\n\n if (!signal.aborted) {\n channel.postMessage(name);\n }\n }\n\n get activeClients(): ReadonlySet<string> {\n return this.#activeClients;\n }\n\n async #getActiveClients(): Promise<Set<string>> {\n const activeClients: Set<string> = new Set();\n\n for await (const lockName of this.#lockManager.queryExclusive()) {\n const client = fromLockName(lockName);\n if (client?.clientGroupID === this.clientGroupID) {\n activeClients.add(client.clientID);\n }\n }\n\n return activeClients;\n }\n\n /**\n * This gets called when a new client is added to the client group.\n *\n * It will request a shared lock for the client, and when the exclusive lock\n * is released, it will notify that the client has been deactivated.\n */\n #addSharedLockForOtherClient(clientID: string): void {\n const name = toLockName(this.clientGroupID, clientID);\n this.#lockManager\n .request(name, 'shared', () => this.#removeClient(clientID))\n .catch(ignoreAbortError);\n }\n\n #addClient(clientID: string): void {\n if (!this.#activeClients.has(clientID)) {\n this.#activeClients.add(clientID);\n this.#addSharedLockForOtherClient(clientID);\n this.onAdd?.(clientID);\n }\n }\n\n #removeClient(clientID: string): void {\n if (this.#activeClients.delete(clientID)) {\n this.onDelete?.(clientID, this.clientGroupID);\n }\n }\n}\n\nfunction getClientLockManager(signal: AbortSignal): ClientLockManager {\n const locks = getBrowserGlobal('navigator')?.locks;\n if (locks) {\n return new NativeClientLockManager(locks, signal);\n }\n return new MockClientLockManager();\n}\n\ninterface ClientLockManager {\n request(\n name: string,\n mode: 'exclusive' | 'shared',\n fn: () => MaybePromise<void>,\n ): Promise<void>;\n release(name: string, fn: () => void): void;\n queryExclusive(): AsyncIterable<string>;\n}\n\nclass NativeClientLockManager implements ClientLockManager {\n readonly #locks: LockManager;\n readonly #signal: AbortSignal;\n\n constructor(locks: LockManager, signal: AbortSignal) {\n this.#locks = locks;\n this.#signal = signal;\n }\n\n async request(\n name: string,\n mode: 'exclusive' | 'shared',\n fn: () => Promise<void>,\n ): Promise<void> {\n await this.#locks.request(name, {mode, signal: this.#signal}, fn);\n }\n\n release(_name: string, fn: () => void): void {\n fn();\n }\n\n async *queryExclusive(): AsyncIterable<string> {\n const snapshot = await this.#locks.query();\n for (const lock of [\n ...(snapshot.held ?? []),\n ...(snapshot.pending ?? []),\n ]) {\n if (lock.mode === 'exclusive' && lock.name) {\n yield lock.name;\n }\n }\n }\n}\n\nconst mockLockNames = new Set<string>();\n\nconst mockListeners: Set<(name: string) => void> = new Set();\n\nclass MockClientLockManager implements ClientLockManager {\n readonly #listeners: Set<(name: string) => void> = new Set();\n\n request(\n name: string,\n mode: 'exclusive' | 'shared',\n fn: () => void | Promise<void>,\n ): Promise<void> {\n if (mode === 'exclusive') {\n mockLockNames.add(name);\n } else {\n mode satisfies 'shared';\n\n // For the mock locks we will add a listener that will notify us when the\n // lock is deleted from the `allMockLocks` set.\n const listener = (removed: string) => {\n if (removed === name) {\n mockListeners.delete(listener);\n return fn();\n }\n };\n mockListeners.add(listener);\n this.#listeners.add(listener);\n }\n return Promise.resolve();\n }\n\n release(name: string, fn: () => void): void {\n mockLockNames.delete(name);\n for (const listener of mockListeners) {\n listener(name);\n }\n for (const listener of this.#listeners) {\n mockListeners.delete(listener);\n }\n fn();\n }\n\n async *queryExclusive(): AsyncIterable<string> {\n yield* mockLockNames;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"active-clients-manager.js","names":["#resolver","#lockManager","#activeClients","#seenClients","#init","#addClient","#getActiveClients","#removeClient","#addSharedLockForOtherClient","#locks","#signal","#listeners"],"sources":["../../../../../zero-client/src/client/active-clients-manager.ts"],"sourcesContent":["import {resolver} from '@rocicorp/resolver';\nimport type {\n ClientGroupID,\n ClientID,\n} from '../../../replicache/src/sync/ids.ts';\nimport {BroadcastChannel} from '../../../shared/src/broadcast-channel.ts';\nimport {getBrowserGlobal} from '../../../shared/src/browser-env.ts';\nimport type {MaybePromise} from '../../../shared/src/types.ts';\n\n/**\n * The prefix for the keys used for the locks and the broadcast channels.\n */\nconst keyPrefix = 'zero-active';\n\nfunction toLockName(clientGroupID: string, clientID: string): string {\n return `${keyPrefix}/${clientGroupID}/${clientID}`;\n}\n\nfunction toBroadcastChannelName(clientGroupID: string): string {\n return `${keyPrefix}/${clientGroupID}`;\n}\n\nfunction fromLockName(\n lockKey: string | undefined,\n): {clientGroupID: string; clientID: string} | undefined {\n if (!lockKey || !lockKey.startsWith(keyPrefix)) {\n return undefined;\n }\n const parts = lockKey.slice(keyPrefix.length).split('/');\n if (parts.length !== 3) {\n return undefined;\n }\n return {\n clientGroupID: parts[1],\n clientID: parts[2],\n };\n}\n\nfunction ignoreAbortError(e: unknown) {\n if (e instanceof Error && e.name === 'AbortError') {\n // Ignore the AbortError, it is expected when the signal is aborted.\n return;\n }\n throw e;\n}\n\n/**\n * A class that lists the active clients in a client group. It uses the\n * `navigator.locks` API to manage locks for each client. The class is designed\n * to be used in a browser environment where the `navigator.locks` API is\n * available.\n *\n * When navigator.locks is not available, it will return a set only containing\n * the clients in the current scripting context (window, worker, etc).\n *\n * It uses one exclusive lock per client, identified by a combination of\n * `clientGroupID` and `clientID`. Then the `query` method is used to get the\n * list of all clients that hold or are waiting for locks in the same client\n * group.\n *\n * It also tries to get a shared lock for each client in the group, so that it\n * can be notified when the exclusive lock is released. This allows the class to\n * keep track of the active clients in the group and notify when an existing\n * client is removed.\n *\n * The class also uses a `BroadcastChannel` to notify other clients in the\n * same client group when a new client is added. This allows the class to keep\n * track of the active clients in the group and notify when a new client is\n * added.\n */\nexport class ActiveClientsManager {\n readonly clientGroupID: string;\n readonly clientID: string;\n readonly #resolver = resolver<void>();\n readonly #lockManager: ClientLockManager;\n readonly #activeClients: Set<string> = new Set();\n // Client IDs are never reused, so a client that has been seen is never added\n // again (e.g. by a late BroadcastChannel message after it was removed).\n readonly #seenClients: Set<string> = new Set();\n\n /**\n * A callback that is called when a client is added to the client group.\n * It receives the client ID of the added client.\n */\n onAdd: ((clientID: string) => void) | undefined;\n\n /**\n * A callback that is called when a client is deleted from the client group.\n * It receives the client ID of the deleted client.\n */\n onDelete:\n | ((clientID: ClientID, clientGroupID: ClientGroupID) => void)\n | undefined;\n\n /**\n * Creates an instance of `ActiveClientsManager` for the specified client\n * group and client ID. It will return a promise that resolves when the\n * instance is ready to use, which means that it has successfully acquired the\n * exclusive lock for the client and has retrieved the list of active clients.\n */\n static async create(\n clientGroupID: string,\n clientID: string,\n signal: AbortSignal,\n ): Promise<ActiveClientsManager> {\n const instance = new ActiveClientsManager(clientGroupID, clientID, signal);\n await instance.#init(signal);\n return instance;\n }\n\n private constructor(\n clientGroupID: string,\n clientID: string,\n signal: AbortSignal,\n ) {\n this.clientGroupID = clientGroupID;\n this.clientID = clientID;\n this.#lockManager = getClientLockManager(signal);\n this.#seenClients.add(clientID);\n this.#activeClients.add(clientID);\n }\n\n async #init(signal: AbortSignal): Promise<void> {\n const {clientGroupID, clientID} = this;\n const name = toLockName(clientGroupID, clientID);\n\n // The BroadcastChannel is used to notify other clients in the same client\n // group when a new client is added. It listens for messages that contain\n // the lock name, which is used to identify the client. When a message is\n // received, it checks if the client belongs to the same client group and\n // adds it to the list of active clients. It also adds a shared lock for\n // the client, so that it can be notified when the exclusive lock is\n // released.\n const channel = new BroadcastChannel(toBroadcastChannelName(clientGroupID));\n channel.addEventListener(\n 'message',\n e => {\n const client = fromLockName(e.data);\n if (client?.clientGroupID === this.clientGroupID) {\n this.#addClient(client.clientID);\n }\n },\n {signal},\n );\n\n this.#lockManager\n .request(name, 'exclusive', () => this.#resolver.promise)\n .catch(ignoreAbortError);\n\n signal.addEventListener(\n 'abort',\n () => {\n this.#lockManager.release(name, () => this.#resolver.resolve());\n channel.close();\n },\n {once: true},\n );\n\n for (const clientID of await this.#getActiveClients()) {\n if (clientID !== this.clientID) {\n this.#addClient(clientID);\n }\n }\n\n if (!signal.aborted) {\n channel.postMessage(name);\n }\n }\n\n get activeClients(): ReadonlySet<string> {\n return this.#activeClients;\n }\n\n async #getActiveClients(): Promise<Set<string>> {\n const activeClients: Set<string> = new Set();\n\n for await (const lockName of this.#lockManager.queryExclusive()) {\n const client = fromLockName(lockName);\n if (client?.clientGroupID === this.clientGroupID) {\n activeClients.add(client.clientID);\n }\n }\n\n return activeClients;\n }\n\n /**\n * This gets called when a new client is added to the client group.\n *\n * It will request a shared lock for the client, and when the exclusive lock\n * is released, it will notify that the client has been deactivated.\n */\n #addSharedLockForOtherClient(clientID: string): void {\n const name = toLockName(this.clientGroupID, clientID);\n this.#lockManager\n .request(name, 'shared', () => this.#removeClient(clientID))\n .catch(ignoreAbortError);\n }\n\n #addClient(clientID: string): void {\n if (!this.#seenClients.has(clientID)) {\n this.#seenClients.add(clientID);\n this.#activeClients.add(clientID);\n this.#addSharedLockForOtherClient(clientID);\n this.onAdd?.(clientID);\n }\n }\n\n #removeClient(clientID: string): void {\n if (this.#activeClients.delete(clientID)) {\n this.onDelete?.(clientID, this.clientGroupID);\n }\n }\n}\n\nfunction getClientLockManager(signal: AbortSignal): ClientLockManager {\n const locks = getBrowserGlobal('navigator')?.locks;\n if (locks) {\n return new NativeClientLockManager(locks, signal);\n }\n return new MockClientLockManager();\n}\n\ninterface ClientLockManager {\n request(\n name: string,\n mode: 'exclusive' | 'shared',\n fn: () => MaybePromise<void>,\n ): Promise<void>;\n release(name: string, fn: () => void): void;\n queryExclusive(): AsyncIterable<string>;\n}\n\nclass NativeClientLockManager implements ClientLockManager {\n readonly #locks: LockManager;\n readonly #signal: AbortSignal;\n\n constructor(locks: LockManager, signal: AbortSignal) {\n this.#locks = locks;\n this.#signal = signal;\n }\n\n async request(\n name: string,\n mode: 'exclusive' | 'shared',\n fn: () => Promise<void>,\n ): Promise<void> {\n await this.#locks.request(name, {mode, signal: this.#signal}, fn);\n }\n\n release(_name: string, fn: () => void): void {\n fn();\n }\n\n async *queryExclusive(): AsyncIterable<string> {\n const snapshot = await this.#locks.query();\n for (const lock of [\n ...(snapshot.held ?? []),\n ...(snapshot.pending ?? []),\n ]) {\n if (lock.mode === 'exclusive' && lock.name) {\n yield lock.name;\n }\n }\n }\n}\n\nconst mockLockNames = new Set<string>();\n\nconst mockListeners: Set<(name: string) => void> = new Set();\n\nclass MockClientLockManager implements ClientLockManager {\n readonly #listeners: Set<(name: string) => void> = new Set();\n\n request(\n name: string,\n mode: 'exclusive' | 'shared',\n fn: () => void | Promise<void>,\n ): Promise<void> {\n if (mode === 'exclusive') {\n mockLockNames.add(name);\n } else {\n mode satisfies 'shared';\n\n // For the mock locks we will add a listener that will notify us when the\n // lock is deleted from the `allMockLocks` set.\n const listener = (removed: string) => {\n if (removed === name) {\n mockListeners.delete(listener);\n return fn();\n }\n };\n mockListeners.add(listener);\n this.#listeners.add(listener);\n }\n return Promise.resolve();\n }\n\n release(name: string, fn: () => void): void {\n mockLockNames.delete(name);\n for (const listener of mockListeners) {\n listener(name);\n }\n for (const listener of this.#listeners) {\n mockListeners.delete(listener);\n }\n fn();\n }\n\n async *queryExclusive(): AsyncIterable<string> {\n yield* mockLockNames;\n }\n}\n"],"mappings":"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"}
@@ -2,6 +2,8 @@ import { Subscribable } from '../../../shared/src/subscribable.ts';
2
2
  import { ConnectionStatus } from './connection-status.ts';
3
3
  import { type AuthError, type ClosedError, type DisconnectedReason, type ZeroError } from './error.ts';
4
4
  export type ConnectionManagerState = {
5
+ name: ConnectionStatus.Initializing;
6
+ } | {
5
7
  name: ConnectionStatus.Disconnected;
6
8
  reason: DisconnectedReason;
7
9
  } | {
@@ -75,6 +77,24 @@ export declare class ConnectionManager extends Subscribable<ConnectionManagerSta
75
77
  * @returns true if a pending request was handled.
76
78
  */
77
79
  resumeFromConnectRequest(): boolean;
80
+ /**
81
+ * Transition from initializing to connecting, once the local store is
82
+ * loaded.
83
+ *
84
+ * This is where the connecting window starts. Loading the local store can
85
+ * take tens of seconds on a slow device with a large replica, and none of
86
+ * that says anything about whether the server is reachable, so it is not
87
+ * charged against the disconnect timeout. No connect attempt has been made
88
+ * yet, so `attempt` is 0.
89
+ *
90
+ * A no-op in any other state: something else, like `close()`, has already
91
+ * moved the state on.
92
+ *
93
+ * @returns An object containing a promise that resolves on the next state change.
94
+ */
95
+ initialized(): {
96
+ nextStatePromise: Promise<ConnectionManagerState>;
97
+ };
78
98
  /**
79
99
  * Transition to connecting state.
80
100
  *
@@ -1 +1 @@
1
- {"version":3,"file":"connection-manager.d.ts","sourceRoot":"","sources":["../../../../../zero-client/src/client/connection-manager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"connection-manager.d.ts","sourceRoot":"","sources":["../../../../../zero-client/src/client/connection-manager.ts"],"names":[],"mappings":"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"}