@rocicorp/zero 0.25.0-canary.0 → 0.25.0-canary.2

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 (394) hide show
  1. package/out/{chunk-MXPHMVU7.js → chunk-COKJ5W7V.js} +2527 -540
  2. package/out/chunk-COKJ5W7V.js.map +7 -0
  3. package/out/{chunk-4RB4OYLQ.js → chunk-TJFNGO7E.js} +3 -2
  4. package/out/{chunk-BJ2CGCME.js → chunk-YWU2DZ23.js} +20 -4
  5. package/out/chunk-YWU2DZ23.js.map +7 -0
  6. package/out/{lazy-inspector-2SW772W4.js → lazy-inspector-OXIFYSSQ.js} +2 -2
  7. package/out/react.js +59 -28
  8. package/out/react.js.map +4 -4
  9. package/out/replicache/src/dag/lazy-store.d.ts +1 -1
  10. package/out/replicache/src/dag/lazy-store.d.ts.map +1 -1
  11. package/out/replicache/src/log-options.d.ts +1 -1
  12. package/out/replicache/src/log-options.d.ts.map +1 -1
  13. package/out/replicache/src/persist/collect-idb-databases.d.ts +3 -3
  14. package/out/replicache/src/persist/collect-idb-databases.d.ts.map +1 -1
  15. package/out/replicache/src/replicache-options.d.ts +1 -1
  16. package/out/replicache/src/replicache-options.d.ts.map +1 -1
  17. package/out/shared/src/error.d.ts +4 -0
  18. package/out/shared/src/error.d.ts.map +1 -0
  19. package/out/shared/src/error.js +70 -0
  20. package/out/shared/src/error.js.map +1 -0
  21. package/out/shared/src/options.d.ts +1 -1
  22. package/out/shared/src/options.d.ts.map +1 -1
  23. package/out/shared/src/promise-race.d.ts +17 -0
  24. package/out/shared/src/promise-race.d.ts.map +1 -0
  25. package/out/solid.js +61 -22
  26. package/out/solid.js.map +4 -4
  27. package/out/z2s/src/compiler.d.ts +1 -1
  28. package/out/z2s/src/compiler.d.ts.map +1 -1
  29. package/out/z2s/src/compiler.js +1 -1
  30. package/out/z2s/src/compiler.js.map +1 -1
  31. package/out/z2s/src/sql.d.ts.map +1 -1
  32. package/out/z2s/src/sql.js +1 -1
  33. package/out/z2s/src/sql.js.map +1 -1
  34. package/out/zero/package.json +6 -4
  35. package/out/zero/src/zero-out.d.ts +3 -0
  36. package/out/zero/src/zero-out.d.ts.map +1 -0
  37. package/out/zero/src/zero-out.js +13 -0
  38. package/out/zero/src/zero-out.js.map +1 -0
  39. package/out/zero-cache/src/config/zero-config.d.ts +12 -0
  40. package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
  41. package/out/zero-cache/src/config/zero-config.js +36 -0
  42. package/out/zero-cache/src/config/zero-config.js.map +1 -1
  43. package/out/zero-cache/src/custom/fetch.d.ts +3 -1
  44. package/out/zero-cache/src/custom/fetch.d.ts.map +1 -1
  45. package/out/zero-cache/src/custom/fetch.js +104 -27
  46. package/out/zero-cache/src/custom/fetch.js.map +1 -1
  47. package/out/zero-cache/src/custom-queries/transform-query.d.ts +2 -6
  48. package/out/zero-cache/src/custom-queries/transform-query.d.ts.map +1 -1
  49. package/out/zero-cache/src/custom-queries/transform-query.js +42 -41
  50. package/out/zero-cache/src/custom-queries/transform-query.js.map +1 -1
  51. package/out/zero-cache/src/db/create.d.ts +4 -0
  52. package/out/zero-cache/src/db/create.d.ts.map +1 -1
  53. package/out/zero-cache/src/db/create.js +7 -9
  54. package/out/zero-cache/src/db/create.js.map +1 -1
  55. package/out/zero-cache/src/db/lite-tables.js +2 -2
  56. package/out/zero-cache/src/db/lite-tables.js.map +1 -1
  57. package/out/zero-cache/src/db/pg-to-lite.d.ts.map +1 -1
  58. package/out/zero-cache/src/db/pg-to-lite.js +7 -6
  59. package/out/zero-cache/src/db/pg-to-lite.js.map +1 -1
  60. package/out/zero-cache/src/db/specs.d.ts +12 -12
  61. package/out/zero-cache/src/scripts/decommission.d.ts +50 -0
  62. package/out/zero-cache/src/scripts/decommission.d.ts.map +1 -0
  63. package/out/zero-cache/src/scripts/decommission.js +51 -0
  64. package/out/zero-cache/src/scripts/decommission.js.map +1 -0
  65. package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
  66. package/out/zero-cache/src/server/change-streamer.js +1 -1
  67. package/out/zero-cache/src/server/change-streamer.js.map +1 -1
  68. package/out/zero-cache/src/server/inspector-delegate.d.ts.map +1 -1
  69. package/out/zero-cache/src/server/inspector-delegate.js +6 -1
  70. package/out/zero-cache/src/server/inspector-delegate.js.map +1 -1
  71. package/out/zero-cache/src/server/syncer.d.ts.map +1 -1
  72. package/out/zero-cache/src/server/syncer.js +1 -1
  73. package/out/zero-cache/src/server/syncer.js.map +1 -1
  74. package/out/zero-cache/src/services/change-source/pg/change-source.js +1 -0
  75. package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
  76. package/out/zero-cache/src/services/change-source/pg/logical-replication/binary-reader.d.ts +1 -1
  77. package/out/zero-cache/src/services/change-source/pg/logical-replication/binary-reader.d.ts.map +1 -1
  78. package/out/zero-cache/src/services/change-source/pg/logical-replication/binary-reader.js +1 -1
  79. package/out/zero-cache/src/services/change-source/pg/logical-replication/binary-reader.js.map +1 -1
  80. package/out/zero-cache/src/services/change-source/pg/schema/ddl.d.ts +25 -25
  81. package/out/zero-cache/src/services/change-source/pg/schema/published.d.ts +10 -10
  82. package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts +5 -5
  83. package/out/zero-cache/src/services/change-source/protocol/current/data.d.ts +16 -16
  84. package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts +24 -24
  85. package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts +2 -1
  86. package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts.map +1 -1
  87. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +18 -2
  88. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
  89. package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts +8 -8
  90. package/out/zero-cache/src/services/mutagen/mutagen.d.ts.map +1 -1
  91. package/out/zero-cache/src/services/mutagen/mutagen.js +9 -7
  92. package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
  93. package/out/zero-cache/src/services/mutagen/pusher.d.ts +21 -25
  94. package/out/zero-cache/src/services/mutagen/pusher.d.ts.map +1 -1
  95. package/out/zero-cache/src/services/mutagen/pusher.js +78 -60
  96. package/out/zero-cache/src/services/mutagen/pusher.js.map +1 -1
  97. package/out/zero-cache/src/services/view-syncer/client-handler.d.ts +3 -1
  98. package/out/zero-cache/src/services/view-syncer/client-handler.d.ts.map +1 -1
  99. package/out/zero-cache/src/services/view-syncer/client-handler.js +10 -6
  100. package/out/zero-cache/src/services/view-syncer/client-handler.js.map +1 -1
  101. package/out/zero-cache/src/services/view-syncer/client-schema.d.ts.map +1 -1
  102. package/out/zero-cache/src/services/view-syncer/client-schema.js +6 -3
  103. package/out/zero-cache/src/services/view-syncer/client-schema.js.map +1 -1
  104. package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts +4 -4
  105. package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts.map +1 -1
  106. package/out/zero-cache/src/services/view-syncer/cvr-store.js +15 -6
  107. package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
  108. package/out/zero-cache/src/services/view-syncer/cvr.d.ts.map +1 -1
  109. package/out/zero-cache/src/services/view-syncer/cvr.js +4 -2
  110. package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
  111. package/out/zero-cache/src/services/view-syncer/drain-coordinator.d.ts +1 -0
  112. package/out/zero-cache/src/services/view-syncer/drain-coordinator.d.ts.map +1 -1
  113. package/out/zero-cache/src/services/view-syncer/drain-coordinator.js +5 -0
  114. package/out/zero-cache/src/services/view-syncer/drain-coordinator.js.map +1 -1
  115. package/out/zero-cache/src/services/view-syncer/key-columns.d.ts.map +1 -1
  116. package/out/zero-cache/src/services/view-syncer/key-columns.js +4 -2
  117. package/out/zero-cache/src/services/view-syncer/key-columns.js.map +1 -1
  118. package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts +1 -1
  119. package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts.map +1 -1
  120. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +19 -2
  121. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
  122. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts +7 -0
  123. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
  124. package/out/zero-cache/src/services/view-syncer/view-syncer.js +91 -32
  125. package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
  126. package/out/zero-cache/src/types/error-with-level.d.ts +9 -0
  127. package/out/zero-cache/src/types/error-with-level.d.ts.map +1 -0
  128. package/out/zero-cache/src/types/error-with-level.js +24 -0
  129. package/out/zero-cache/src/types/error-with-level.js.map +1 -0
  130. package/out/zero-cache/src/types/lite.d.ts +15 -13
  131. package/out/zero-cache/src/types/lite.d.ts.map +1 -1
  132. package/out/zero-cache/src/types/lite.js +17 -5
  133. package/out/zero-cache/src/types/lite.js.map +1 -1
  134. package/out/zero-cache/src/types/pg-data-type.d.ts +73 -0
  135. package/out/zero-cache/src/types/pg-data-type.d.ts.map +1 -0
  136. package/out/zero-cache/src/types/pg-data-type.js +76 -0
  137. package/out/zero-cache/src/types/pg-data-type.js.map +1 -0
  138. package/out/zero-cache/src/types/pg.d.ts +1 -73
  139. package/out/zero-cache/src/types/pg.d.ts.map +1 -1
  140. package/out/zero-cache/src/types/pg.js +0 -77
  141. package/out/zero-cache/src/types/pg.js.map +1 -1
  142. package/out/zero-cache/src/types/processes.d.ts +1 -1
  143. package/out/zero-cache/src/types/processes.d.ts.map +1 -1
  144. package/out/zero-cache/src/types/processes.js.map +1 -1
  145. package/out/zero-cache/src/types/schema-versions.d.ts +7 -3
  146. package/out/zero-cache/src/types/schema-versions.d.ts.map +1 -1
  147. package/out/zero-cache/src/types/schema-versions.js +7 -5
  148. package/out/zero-cache/src/types/schema-versions.js.map +1 -1
  149. package/out/zero-cache/src/types/streams.d.ts +1 -1
  150. package/out/zero-cache/src/types/streams.d.ts.map +1 -1
  151. package/out/zero-cache/src/types/streams.js +19 -9
  152. package/out/zero-cache/src/types/streams.js.map +1 -1
  153. package/out/zero-cache/src/types/subscription.d.ts +17 -2
  154. package/out/zero-cache/src/types/subscription.d.ts.map +1 -1
  155. package/out/zero-cache/src/types/subscription.js +42 -6
  156. package/out/zero-cache/src/types/subscription.js.map +1 -1
  157. package/out/zero-cache/src/types/websocket-handoff.d.ts +3 -3
  158. package/out/zero-cache/src/types/websocket-handoff.d.ts.map +1 -1
  159. package/out/zero-cache/src/types/websocket-handoff.js +6 -4
  160. package/out/zero-cache/src/types/websocket-handoff.js.map +1 -1
  161. package/out/zero-cache/src/workers/connection.d.ts +6 -1
  162. package/out/zero-cache/src/workers/connection.d.ts.map +1 -1
  163. package/out/zero-cache/src/workers/connection.js +26 -9
  164. package/out/zero-cache/src/workers/connection.js.map +1 -1
  165. package/out/zero-cache/src/workers/syncer-ws-message-handler.d.ts.map +1 -1
  166. package/out/zero-cache/src/workers/syncer-ws-message-handler.js +7 -1
  167. package/out/zero-cache/src/workers/syncer-ws-message-handler.js.map +1 -1
  168. package/out/zero-cache/src/workers/syncer.d.ts.map +1 -1
  169. package/out/zero-cache/src/workers/syncer.js +22 -4
  170. package/out/zero-cache/src/workers/syncer.js.map +1 -1
  171. package/out/zero-client/src/client/client-error-kind-enum.d.ts +8 -0
  172. package/out/zero-client/src/client/client-error-kind-enum.d.ts.map +1 -1
  173. package/out/zero-client/src/client/connection-manager.d.ts +82 -14
  174. package/out/zero-client/src/client/connection-manager.d.ts.map +1 -1
  175. package/out/zero-client/src/client/connection-status-enum.d.ts +4 -0
  176. package/out/zero-client/src/client/connection-status-enum.d.ts.map +1 -1
  177. package/out/zero-client/src/client/connection.d.ts +53 -0
  178. package/out/zero-client/src/client/connection.d.ts.map +1 -0
  179. package/out/zero-client/src/client/error.d.ts +51 -24
  180. package/out/zero-client/src/client/error.d.ts.map +1 -1
  181. package/out/zero-client/src/client/ivm-branch.d.ts +2 -2
  182. package/out/zero-client/src/client/ivm-branch.d.ts.map +1 -1
  183. package/out/zero-client/src/client/metrics.d.ts +9 -0
  184. package/out/zero-client/src/client/metrics.d.ts.map +1 -1
  185. package/out/zero-client/src/client/mutation-tracker.d.ts +5 -4
  186. package/out/zero-client/src/client/mutation-tracker.d.ts.map +1 -1
  187. package/out/zero-client/src/client/options.d.ts +15 -18
  188. package/out/zero-client/src/client/options.d.ts.map +1 -1
  189. package/out/zero-client/src/client/query-manager.d.ts +5 -4
  190. package/out/zero-client/src/client/query-manager.d.ts.map +1 -1
  191. package/out/zero-client/src/client/zero-rep.d.ts +3 -1
  192. package/out/zero-client/src/client/zero-rep.d.ts.map +1 -1
  193. package/out/zero-client/src/client/zero.d.ts +27 -5
  194. package/out/zero-client/src/client/zero.d.ts.map +1 -1
  195. package/out/zero-client/src/mod.d.ts +12 -1
  196. package/out/zero-client/src/mod.d.ts.map +1 -1
  197. package/out/zero-client/src/types/query-result.d.ts +23 -0
  198. package/out/zero-client/src/types/query-result.d.ts.map +1 -0
  199. package/out/zero-protocol/src/application-error.d.ts +33 -0
  200. package/out/zero-protocol/src/application-error.d.ts.map +1 -0
  201. package/out/zero-protocol/src/application-error.js +37 -0
  202. package/out/zero-protocol/src/application-error.js.map +1 -0
  203. package/out/zero-protocol/src/custom-queries.d.ts +56 -53
  204. package/out/zero-protocol/src/custom-queries.d.ts.map +1 -1
  205. package/out/zero-protocol/src/custom-queries.js +16 -14
  206. package/out/zero-protocol/src/custom-queries.js.map +1 -1
  207. package/out/zero-protocol/src/down.d.ts +8 -10
  208. package/out/zero-protocol/src/down.d.ts.map +1 -1
  209. package/out/zero-protocol/src/error-kind-enum.d.ts +65 -0
  210. package/out/zero-protocol/src/error-kind-enum.d.ts.map +1 -1
  211. package/out/zero-protocol/src/error-kind-enum.js +2 -0
  212. package/out/zero-protocol/src/error-kind-enum.js.map +1 -1
  213. package/out/zero-protocol/src/error-origin-enum.d.ts +7 -0
  214. package/out/zero-protocol/src/error-origin-enum.d.ts.map +1 -0
  215. package/out/zero-protocol/src/error-origin-enum.js +4 -0
  216. package/out/zero-protocol/src/error-origin-enum.js.map +1 -0
  217. package/out/zero-protocol/src/error-origin.d.ts +5 -0
  218. package/out/zero-protocol/src/error-origin.d.ts.map +1 -0
  219. package/out/zero-protocol/src/error-origin.js +3 -0
  220. package/out/zero-protocol/src/error-origin.js.map +1 -0
  221. package/out/zero-protocol/src/error-reason-enum.d.ts +15 -0
  222. package/out/zero-protocol/src/error-reason-enum.d.ts.map +1 -0
  223. package/out/zero-protocol/src/error-reason-enum.js +8 -0
  224. package/out/zero-protocol/src/error-reason-enum.js.map +1 -0
  225. package/out/zero-protocol/src/error-reason.d.ts +5 -0
  226. package/out/zero-protocol/src/error-reason.d.ts.map +1 -0
  227. package/out/zero-protocol/src/error-reason.js +3 -0
  228. package/out/zero-protocol/src/error-reason.js.map +1 -0
  229. package/out/zero-protocol/src/error.d.ts +181 -1
  230. package/out/zero-protocol/src/error.d.ts.map +1 -1
  231. package/out/zero-protocol/src/error.js +71 -2
  232. package/out/zero-protocol/src/error.js.map +1 -1
  233. package/out/zero-protocol/src/mutation-id.d.ts +7 -0
  234. package/out/zero-protocol/src/mutation-id.d.ts.map +1 -0
  235. package/out/zero-protocol/src/mutation-id.js +6 -0
  236. package/out/zero-protocol/src/mutation-id.js.map +1 -0
  237. package/out/zero-protocol/src/mutations-patch.d.ts +6 -3
  238. package/out/zero-protocol/src/mutations-patch.d.ts.map +1 -1
  239. package/out/zero-protocol/src/mutations-patch.js +2 -1
  240. package/out/zero-protocol/src/mutations-patch.js.map +1 -1
  241. package/out/zero-protocol/src/poke.d.ts +2 -0
  242. package/out/zero-protocol/src/poke.d.ts.map +1 -1
  243. package/out/zero-protocol/src/protocol-version.d.ts +1 -1
  244. package/out/zero-protocol/src/protocol-version.d.ts.map +1 -1
  245. package/out/zero-protocol/src/protocol-version.js +3 -1
  246. package/out/zero-protocol/src/protocol-version.js.map +1 -1
  247. package/out/zero-protocol/src/push.d.ts +145 -13
  248. package/out/zero-protocol/src/push.d.ts.map +1 -1
  249. package/out/zero-protocol/src/push.js +35 -13
  250. package/out/zero-protocol/src/push.js.map +1 -1
  251. package/out/zero-react/src/mod.d.ts +7 -2
  252. package/out/zero-react/src/mod.d.ts.map +1 -1
  253. package/out/zero-react/src/use-query.d.ts +1 -21
  254. package/out/zero-react/src/use-query.d.ts.map +1 -1
  255. package/out/zero-react/src/use-zero-connection-state.d.ts +9 -0
  256. package/out/zero-react/src/use-zero-connection-state.d.ts.map +1 -0
  257. package/out/zero-react/src/use-zero-online.d.ts +2 -0
  258. package/out/zero-react/src/use-zero-online.d.ts.map +1 -1
  259. package/out/zero-react/src/zero-provider.d.ts.map +1 -1
  260. package/out/zero-server/src/mod.d.ts +5 -4
  261. package/out/zero-server/src/mod.d.ts.map +1 -1
  262. package/out/zero-server/src/mod.js +5 -4
  263. package/out/zero-server/src/mod.js.map +1 -1
  264. package/out/zero-server/src/process-mutations.d.ts +3 -3
  265. package/out/zero-server/src/process-mutations.d.ts.map +1 -1
  266. package/out/zero-server/src/process-mutations.js +181 -68
  267. package/out/zero-server/src/process-mutations.js.map +1 -1
  268. package/out/zero-server/src/queries/process-queries.d.ts +4 -3
  269. package/out/zero-server/src/queries/process-queries.d.ts.map +1 -1
  270. package/out/zero-server/src/queries/process-queries.js +85 -19
  271. package/out/zero-server/src/queries/process-queries.js.map +1 -1
  272. package/out/zero-server/src/schema.js +1 -1
  273. package/out/zero-server/src/schema.js.map +1 -1
  274. package/out/zero-server/src/zql-database.d.ts +1 -1
  275. package/out/zero-server/src/zql-database.d.ts.map +1 -1
  276. package/out/zero-server/src/zql-database.js.map +1 -1
  277. package/out/zero-solid/src/mod.d.ts +11 -2
  278. package/out/zero-solid/src/mod.d.ts.map +1 -1
  279. package/out/zero-solid/src/solid-view.d.ts +3 -23
  280. package/out/zero-solid/src/solid-view.d.ts.map +1 -1
  281. package/out/zero-solid/src/use-query.d.ts +1 -1
  282. package/out/zero-solid/src/use-query.d.ts.map +1 -1
  283. package/out/zero-solid/src/use-zero-connection-state.d.ts +10 -0
  284. package/out/zero-solid/src/use-zero-connection-state.d.ts.map +1 -0
  285. package/out/zero-solid/src/use-zero-online.d.ts +1 -7
  286. package/out/zero-solid/src/use-zero-online.d.ts.map +1 -1
  287. package/out/zero-solid/src/use-zero.d.ts +1 -0
  288. package/out/zero-solid/src/use-zero.d.ts.map +1 -1
  289. package/out/zero.js +15 -3
  290. package/out/zql/src/builder/builder.d.ts +2 -1
  291. package/out/zql/src/builder/builder.d.ts.map +1 -1
  292. package/out/zql/src/builder/builder.js +5 -1
  293. package/out/zql/src/builder/builder.js.map +1 -1
  294. package/out/zql/src/ivm/data.d.ts +1 -1
  295. package/out/zql/src/ivm/data.d.ts.map +1 -1
  296. package/out/zql/src/ivm/data.js.map +1 -1
  297. package/out/zql/src/ivm/filter-push.d.ts +1 -1
  298. package/out/zql/src/ivm/filter-push.d.ts.map +1 -1
  299. package/out/zql/src/ivm/filter-push.js.map +1 -1
  300. package/out/zql/src/ivm/memory-source.d.ts +2 -2
  301. package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
  302. package/out/zql/src/ivm/memory-source.js.map +1 -1
  303. package/out/zql/src/ivm/source.d.ts +1 -1
  304. package/out/zql/src/ivm/source.d.ts.map +1 -1
  305. package/out/zql/src/ivm/take.d.ts +1 -1
  306. package/out/zql/src/ivm/take.d.ts.map +1 -1
  307. package/out/zql/src/ivm/take.js.map +1 -1
  308. package/out/zql/src/planner/planner-builder.d.ts +15 -0
  309. package/out/zql/src/planner/planner-builder.d.ts.map +1 -0
  310. package/out/zql/src/planner/planner-builder.js +180 -0
  311. package/out/zql/src/planner/planner-builder.js.map +1 -0
  312. package/out/zql/src/planner/planner-connection.d.ts +125 -0
  313. package/out/zql/src/planner/planner-connection.d.ts.map +1 -0
  314. package/out/zql/src/planner/planner-connection.js +257 -0
  315. package/out/zql/src/planner/planner-connection.js.map +1 -0
  316. package/out/zql/src/planner/planner-constraint.d.ts +14 -0
  317. package/out/zql/src/planner/planner-constraint.d.ts.map +1 -0
  318. package/out/zql/src/planner/planner-constraint.js +12 -0
  319. package/out/zql/src/planner/planner-constraint.js.map +1 -0
  320. package/out/zql/src/planner/planner-debug.d.ts +148 -0
  321. package/out/zql/src/planner/planner-debug.d.ts.map +1 -0
  322. package/out/zql/src/planner/planner-debug.js +176 -0
  323. package/out/zql/src/planner/planner-debug.js.map +1 -0
  324. package/out/zql/src/planner/planner-fan-in.d.ts +39 -0
  325. package/out/zql/src/planner/planner-fan-in.d.ts.map +1 -0
  326. package/out/zql/src/planner/planner-fan-in.js +167 -0
  327. package/out/zql/src/planner/planner-fan-in.js.map +1 -0
  328. package/out/zql/src/planner/planner-fan-out.d.ts +23 -0
  329. package/out/zql/src/planner/planner-fan-out.d.ts.map +1 -0
  330. package/out/zql/src/planner/planner-fan-out.js +62 -0
  331. package/out/zql/src/planner/planner-fan-out.js.map +1 -0
  332. package/out/zql/src/planner/planner-graph.d.ts +103 -0
  333. package/out/zql/src/planner/planner-graph.d.ts.map +1 -0
  334. package/out/zql/src/planner/planner-graph.js +380 -0
  335. package/out/zql/src/planner/planner-graph.js.map +1 -0
  336. package/out/zql/src/planner/planner-join.d.ts +136 -0
  337. package/out/zql/src/planner/planner-join.d.ts.map +1 -0
  338. package/out/zql/src/planner/planner-join.js +329 -0
  339. package/out/zql/src/planner/planner-join.js.map +1 -0
  340. package/out/zql/src/planner/planner-node.d.ts +48 -0
  341. package/out/zql/src/planner/planner-node.d.ts.map +1 -0
  342. package/out/zql/src/planner/planner-node.js +2 -0
  343. package/out/zql/src/planner/planner-node.js.map +1 -0
  344. package/out/zql/src/planner/planner-source.d.ts +11 -0
  345. package/out/zql/src/planner/planner-source.d.ts.map +1 -0
  346. package/out/zql/src/planner/planner-source.js +13 -0
  347. package/out/zql/src/planner/planner-source.js.map +1 -0
  348. package/out/zql/src/planner/planner-terminus.d.ts +17 -0
  349. package/out/zql/src/planner/planner-terminus.d.ts.map +1 -0
  350. package/out/zql/src/planner/planner-terminus.js +28 -0
  351. package/out/zql/src/planner/planner-terminus.js.map +1 -0
  352. package/out/zql/src/query/error.d.ts +4 -0
  353. package/out/zql/src/query/error.d.ts.map +1 -0
  354. package/out/zql/src/query/error.js +9 -0
  355. package/out/zql/src/query/error.js.map +1 -0
  356. package/out/zql/src/query/expression.d.ts +2 -2
  357. package/out/zql/src/query/expression.d.ts.map +1 -1
  358. package/out/zql/src/query/expression.js.map +1 -1
  359. package/out/zql/src/query/named.d.ts.map +1 -1
  360. package/out/zql/src/query/named.js +9 -1
  361. package/out/zql/src/query/named.js.map +1 -1
  362. package/out/zql/src/query/query-delegate.d.ts +3 -3
  363. package/out/zql/src/query/query-delegate.d.ts.map +1 -1
  364. package/out/zql/src/query/query-impl.d.ts +8 -8
  365. package/out/zql/src/query/query-impl.d.ts.map +1 -1
  366. package/out/zql/src/query/query-impl.js.map +1 -1
  367. package/out/zql/src/query/query.d.ts +3 -3
  368. package/out/zql/src/query/query.d.ts.map +1 -1
  369. package/out/zql/src/query/static-query.d.ts +1 -1
  370. package/out/zql/src/query/static-query.d.ts.map +1 -1
  371. package/out/zql/src/query/static-query.js.map +1 -1
  372. package/out/zql/src/query/typed-view.d.ts +1 -1
  373. package/out/zql/src/query/typed-view.d.ts.map +1 -1
  374. package/out/zqlite/src/query-delegate.d.ts +1 -1
  375. package/out/zqlite/src/query-delegate.d.ts.map +1 -1
  376. package/out/zqlite/src/query-delegate.js.map +1 -1
  377. package/out/zqlite/src/sqlite-cost-model.d.ts +17 -0
  378. package/out/zqlite/src/sqlite-cost-model.d.ts.map +1 -0
  379. package/out/zqlite/src/sqlite-cost-model.js +134 -0
  380. package/out/zqlite/src/sqlite-cost-model.js.map +1 -0
  381. package/out/zqlite/src/table-source.d.ts +1 -1
  382. package/out/zqlite/src/table-source.d.ts.map +1 -1
  383. package/out/zqlite/src/table-source.js.map +1 -1
  384. package/package.json +6 -4
  385. package/out/chunk-BJ2CGCME.js.map +0 -7
  386. package/out/chunk-MXPHMVU7.js.map +0 -7
  387. package/out/zero-cache/src/types/error-for-client.d.ts +0 -23
  388. package/out/zero-cache/src/types/error-for-client.d.ts.map +0 -1
  389. package/out/zero-cache/src/types/error-for-client.js +0 -28
  390. package/out/zero-cache/src/types/error-for-client.js.map +0 -1
  391. package/out/zero-client/src/client/ping-result-enum.d.ts +0 -5
  392. package/out/zero-client/src/client/ping-result-enum.d.ts.map +0 -1
  393. /package/out/{chunk-4RB4OYLQ.js.map → chunk-TJFNGO7E.js.map} +0 -0
  394. /package/out/{lazy-inspector-2SW772W4.js.map → lazy-inspector-OXIFYSSQ.js.map} +0 -0
@@ -96,6 +96,7 @@ import {
96
96
  normalizeAST,
97
97
  once,
98
98
  parse,
99
+ planIdSymbol,
99
100
  primaryKeySchema,
100
101
  primaryKeyValueRecordSchema,
101
102
  readFromDefaultHead,
@@ -123,7 +124,7 @@ import {
123
124
  withWrite,
124
125
  withWriteNoImplicitCommit,
125
126
  wrapIterable
126
- } from "./chunk-4RB4OYLQ.js";
127
+ } from "./chunk-TJFNGO7E.js";
127
128
  import {
128
129
  assert,
129
130
  assertArray,
@@ -1511,58 +1512,102 @@ function rejectIfClosed(tx) {
1511
1512
  return tx.closed ? Promise.reject(new TransactionClosedError()) : void 0;
1512
1513
  }
1513
1514
 
1514
- // ../zero-protocol/src/custom-queries.ts
1515
- var transformRequestBodySchema = valita_exports.array(
1516
- valita_exports.object({
1517
- id: valita_exports.string(),
1518
- name: valita_exports.string(),
1519
- args: readonly(valita_exports.array(jsonSchema))
1520
- })
1521
- );
1522
- var transformedQuerySchema = valita_exports.object({
1523
- id: valita_exports.string(),
1524
- name: valita_exports.string(),
1525
- ast: astSchema
1526
- });
1527
- var appQueryErrorSchema = valita_exports.object({
1528
- error: valita_exports.literal("app"),
1529
- id: valita_exports.string(),
1530
- name: valita_exports.string(),
1531
- details: jsonSchema
1532
- });
1533
- var zeroErrorSchema = valita_exports.object({
1534
- error: valita_exports.literal("zero"),
1535
- id: valita_exports.string(),
1536
- name: valita_exports.string(),
1537
- details: jsonSchema
1538
- });
1539
- var httpQueryErrorSchema = valita_exports.object({
1540
- error: valita_exports.literal("http"),
1541
- id: valita_exports.string(),
1542
- name: valita_exports.string(),
1543
- status: valita_exports.number(),
1544
- details: jsonSchema
1545
- });
1546
- var erroredQuerySchema = valita_exports.union(
1547
- appQueryErrorSchema,
1548
- httpQueryErrorSchema,
1549
- zeroErrorSchema
1550
- );
1551
- var transformResponseBodySchema = valita_exports.array(
1552
- valita_exports.union(transformedQuerySchema, erroredQuerySchema)
1553
- );
1554
- var transformRequestMessageSchema = valita_exports.tuple([
1555
- valita_exports.literal("transform"),
1556
- transformRequestBodySchema
1557
- ]);
1558
- var transformErrorMessageSchema = valita_exports.tuple([
1559
- valita_exports.literal("transformError"),
1560
- valita_exports.array(erroredQuerySchema)
1561
- ]);
1562
- var transformResponseMessageSchema = valita_exports.tuple([
1563
- valita_exports.literal("transformed"),
1564
- transformResponseBodySchema
1565
- ]);
1515
+ // ../shared/src/error.ts
1516
+ function getErrorMessage(error) {
1517
+ return getErrorMessageInternal(error, /* @__PURE__ */ new Set());
1518
+ }
1519
+ function getErrorMessageInternal(error, seen) {
1520
+ if (typeof error === "string") {
1521
+ return error;
1522
+ }
1523
+ if (typeof error === "object" && error !== null) {
1524
+ if (seen.has(error)) {
1525
+ return "Circular error reference detected while extracting the error message.";
1526
+ }
1527
+ seen.add(error);
1528
+ }
1529
+ if (error instanceof Error) {
1530
+ if (error.message) {
1531
+ return error.message;
1532
+ }
1533
+ if ("cause" in error) {
1534
+ const cause = error.cause;
1535
+ if (cause !== void 0) {
1536
+ const causeMessage = getErrorMessageInternal(cause, seen);
1537
+ if (causeMessage) {
1538
+ return causeMessage;
1539
+ }
1540
+ }
1541
+ }
1542
+ }
1543
+ if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
1544
+ const message = error.message;
1545
+ if (message) {
1546
+ return message;
1547
+ }
1548
+ }
1549
+ try {
1550
+ const json2 = jsonSchema.parse(error);
1551
+ return `Parsed message: ${JSON.stringify(json2)}`;
1552
+ } catch (_e) {
1553
+ }
1554
+ return `Unknown error of type ${typeof error} was thrown and the message could not be determined. See cause for details.`;
1555
+ }
1556
+ function getErrorDetails(error) {
1557
+ if (error instanceof Error) {
1558
+ if ("details" in error) {
1559
+ try {
1560
+ return jsonSchema.parse(error?.details);
1561
+ } catch (_e) {
1562
+ }
1563
+ }
1564
+ if (error.name && error.name !== "Error") {
1565
+ return { name: error.name };
1566
+ }
1567
+ return void 0;
1568
+ }
1569
+ if (typeof error === "object" && error !== null && "details" in error) {
1570
+ try {
1571
+ return jsonSchema.parse(error?.details);
1572
+ } catch (_e) {
1573
+ }
1574
+ }
1575
+ try {
1576
+ return jsonSchema.parse(error);
1577
+ } catch (_e) {
1578
+ }
1579
+ return void 0;
1580
+ }
1581
+
1582
+ // ../zero-protocol/src/application-error.ts
1583
+ var ApplicationError = class extends Error {
1584
+ /**
1585
+ * This maps onto errors for transform and push app-level failures.
1586
+ */
1587
+ #details;
1588
+ constructor(message, options) {
1589
+ super(message, { cause: options?.cause });
1590
+ this.name = "ApplicationError";
1591
+ this.#details = options?.details ?? void 0;
1592
+ }
1593
+ get details() {
1594
+ return this.#details;
1595
+ }
1596
+ };
1597
+ function isApplicationError(error) {
1598
+ return error instanceof ApplicationError;
1599
+ }
1600
+ function wrapWithApplicationError(error) {
1601
+ if (isApplicationError(error)) {
1602
+ return error;
1603
+ }
1604
+ const message = getErrorMessage(error);
1605
+ const details = getErrorDetails(error);
1606
+ return new ApplicationError(message, {
1607
+ cause: error,
1608
+ details
1609
+ });
1610
+ }
1566
1611
 
1567
1612
  // ../zero-protocol/src/error-kind-enum.ts
1568
1613
  var error_kind_enum_exports = {};
@@ -1578,10 +1623,12 @@ __export(error_kind_enum_exports, {
1578
1623
  InvalidPush: () => InvalidPush,
1579
1624
  MutationFailed: () => MutationFailed,
1580
1625
  MutationRateLimited: () => MutationRateLimited,
1626
+ PushFailed: () => PushFailed,
1581
1627
  Rebalance: () => Rebalance,
1582
1628
  Rehome: () => Rehome,
1583
1629
  SchemaVersionNotSupported: () => SchemaVersionNotSupported,
1584
1630
  ServerOverloaded: () => ServerOverloaded,
1631
+ TransformFailed: () => TransformFailed,
1585
1632
  Unauthorized: () => Unauthorized,
1586
1633
  VersionNotSupported: () => VersionNotSupported
1587
1634
  });
@@ -1593,16 +1640,255 @@ var InvalidConnectionRequestLastMutationID = "InvalidConnectionRequestLastMutati
1593
1640
  var InvalidConnectionRequestClientDeleted = "InvalidConnectionRequestClientDeleted";
1594
1641
  var InvalidMessage = "InvalidMessage";
1595
1642
  var InvalidPush = "InvalidPush";
1643
+ var PushFailed = "PushFailed";
1596
1644
  var MutationFailed = "MutationFailed";
1597
1645
  var MutationRateLimited = "MutationRateLimited";
1598
1646
  var Rebalance = "Rebalance";
1599
1647
  var Rehome = "Rehome";
1648
+ var TransformFailed = "TransformFailed";
1600
1649
  var Unauthorized = "Unauthorized";
1601
1650
  var VersionNotSupported = "VersionNotSupported";
1602
1651
  var SchemaVersionNotSupported = "SchemaVersionNotSupported";
1603
1652
  var ServerOverloaded = "ServerOverloaded";
1604
1653
  var Internal = "Internal";
1605
1654
 
1655
+ // ../zero-protocol/src/error-origin-enum.ts
1656
+ var error_origin_enum_exports = {};
1657
+ __export(error_origin_enum_exports, {
1658
+ Client: () => Client,
1659
+ Server: () => Server,
1660
+ ZeroCache: () => ZeroCache
1661
+ });
1662
+ var Client = "client";
1663
+ var Server = "server";
1664
+ var ZeroCache = "zeroCache";
1665
+
1666
+ // ../zero-protocol/src/error-reason-enum.ts
1667
+ var error_reason_enum_exports = {};
1668
+ __export(error_reason_enum_exports, {
1669
+ Database: () => Database,
1670
+ HTTP: () => HTTP,
1671
+ Internal: () => Internal2,
1672
+ OutOfOrderMutation: () => OutOfOrderMutation,
1673
+ Parse: () => Parse,
1674
+ Timeout: () => Timeout,
1675
+ UnsupportedPushVersion: () => UnsupportedPushVersion
1676
+ });
1677
+ var Database = "database";
1678
+ var Parse = "parse";
1679
+ var OutOfOrderMutation = "oooMutation";
1680
+ var UnsupportedPushVersion = "unsupportedPushVersion";
1681
+ var Internal2 = "internal";
1682
+ var HTTP = "http";
1683
+ var Timeout = "timeout";
1684
+
1685
+ // ../zero-protocol/src/mutation-id.ts
1686
+ var mutationIDSchema = valita_exports.object({
1687
+ id: valita_exports.number(),
1688
+ clientID: valita_exports.string()
1689
+ });
1690
+
1691
+ // ../zero-protocol/src/error.ts
1692
+ var basicErrorKindSchema = literalUnion(
1693
+ error_kind_enum_exports.AuthInvalidated,
1694
+ error_kind_enum_exports.ClientNotFound,
1695
+ error_kind_enum_exports.InvalidConnectionRequest,
1696
+ error_kind_enum_exports.InvalidConnectionRequestBaseCookie,
1697
+ error_kind_enum_exports.InvalidConnectionRequestLastMutationID,
1698
+ error_kind_enum_exports.InvalidConnectionRequestClientDeleted,
1699
+ error_kind_enum_exports.InvalidMessage,
1700
+ error_kind_enum_exports.InvalidPush,
1701
+ error_kind_enum_exports.MutationRateLimited,
1702
+ error_kind_enum_exports.MutationFailed,
1703
+ error_kind_enum_exports.Unauthorized,
1704
+ error_kind_enum_exports.VersionNotSupported,
1705
+ error_kind_enum_exports.SchemaVersionNotSupported,
1706
+ error_kind_enum_exports.Internal
1707
+ );
1708
+ var basicErrorBodySchema = valita_exports.object({
1709
+ kind: basicErrorKindSchema,
1710
+ message: valita_exports.string(),
1711
+ // this is optional for backwards compatibility
1712
+ origin: literalUnion(error_origin_enum_exports.Server, error_origin_enum_exports.ZeroCache).optional()
1713
+ });
1714
+ var backoffErrorKindSchema = literalUnion(
1715
+ error_kind_enum_exports.Rebalance,
1716
+ error_kind_enum_exports.Rehome,
1717
+ error_kind_enum_exports.ServerOverloaded
1718
+ );
1719
+ var backoffBodySchema = valita_exports.object({
1720
+ kind: backoffErrorKindSchema,
1721
+ message: valita_exports.string(),
1722
+ minBackoffMs: valita_exports.number().optional(),
1723
+ maxBackoffMs: valita_exports.number().optional(),
1724
+ // Query parameters to send in the next reconnect. In the event of
1725
+ // a conflict, these will be overridden by the parameters used by
1726
+ // the client; it is the responsibility of the server to avoid
1727
+ // parameter name conflicts.
1728
+ //
1729
+ // The parameters will only be added to the immediately following
1730
+ // reconnect, and not after that.
1731
+ reconnectParams: valita_exports.record(valita_exports.string()).optional(),
1732
+ origin: valita_exports.literal(error_origin_enum_exports.ZeroCache).optional()
1733
+ });
1734
+ var pushFailedErrorKindSchema = valita_exports.literal(error_kind_enum_exports.PushFailed);
1735
+ var transformFailedErrorKindSchema = valita_exports.literal(error_kind_enum_exports.TransformFailed);
1736
+ var errorKindSchema = valita_exports.union(
1737
+ basicErrorKindSchema,
1738
+ backoffErrorKindSchema,
1739
+ pushFailedErrorKindSchema,
1740
+ transformFailedErrorKindSchema
1741
+ );
1742
+ var pushFailedBaseSchema = valita_exports.object({
1743
+ kind: pushFailedErrorKindSchema,
1744
+ details: jsonSchema.optional(),
1745
+ /**
1746
+ * The mutationIDs of the mutations that failed to process.
1747
+ * This can be a subset of the mutationIDs in the request.
1748
+ */
1749
+ mutationIDs: valita_exports.array(mutationIDSchema),
1750
+ message: valita_exports.string()
1751
+ });
1752
+ var pushFailedBodySchema = valita_exports.union(
1753
+ pushFailedBaseSchema.extend({
1754
+ origin: valita_exports.literal(error_origin_enum_exports.Server),
1755
+ reason: literalUnion(
1756
+ error_reason_enum_exports.Database,
1757
+ error_reason_enum_exports.Parse,
1758
+ error_reason_enum_exports.OutOfOrderMutation,
1759
+ error_reason_enum_exports.UnsupportedPushVersion,
1760
+ error_reason_enum_exports.Internal
1761
+ )
1762
+ }),
1763
+ pushFailedBaseSchema.extend({
1764
+ origin: valita_exports.literal(error_origin_enum_exports.ZeroCache),
1765
+ reason: valita_exports.literal(error_reason_enum_exports.HTTP),
1766
+ status: valita_exports.number(),
1767
+ bodyPreview: valita_exports.string().optional()
1768
+ }),
1769
+ pushFailedBaseSchema.extend({
1770
+ origin: valita_exports.literal(error_origin_enum_exports.ZeroCache),
1771
+ reason: literalUnion(
1772
+ error_reason_enum_exports.Timeout,
1773
+ error_reason_enum_exports.Parse,
1774
+ error_reason_enum_exports.Internal
1775
+ )
1776
+ })
1777
+ );
1778
+ var transformFailedBaseSchema = valita_exports.object({
1779
+ kind: transformFailedErrorKindSchema,
1780
+ details: jsonSchema.optional(),
1781
+ /**
1782
+ * The queryIDs of the queries that failed to transform.
1783
+ */
1784
+ queryIDs: valita_exports.array(valita_exports.string()),
1785
+ message: valita_exports.string()
1786
+ });
1787
+ var transformFailedBodySchema = valita_exports.union(
1788
+ transformFailedBaseSchema.extend({
1789
+ origin: valita_exports.literal(error_origin_enum_exports.Server),
1790
+ reason: literalUnion(
1791
+ error_reason_enum_exports.Database,
1792
+ error_reason_enum_exports.Parse,
1793
+ error_reason_enum_exports.Internal
1794
+ )
1795
+ }),
1796
+ transformFailedBaseSchema.extend({
1797
+ origin: valita_exports.literal(error_origin_enum_exports.ZeroCache),
1798
+ reason: valita_exports.literal(error_reason_enum_exports.HTTP),
1799
+ status: valita_exports.number(),
1800
+ bodyPreview: valita_exports.string().optional()
1801
+ }),
1802
+ transformFailedBaseSchema.extend({
1803
+ origin: valita_exports.literal(error_origin_enum_exports.ZeroCache),
1804
+ reason: literalUnion(
1805
+ error_reason_enum_exports.Timeout,
1806
+ error_reason_enum_exports.Parse,
1807
+ error_reason_enum_exports.Internal
1808
+ )
1809
+ })
1810
+ );
1811
+ var errorBodySchema = valita_exports.union(
1812
+ basicErrorBodySchema,
1813
+ backoffBodySchema,
1814
+ pushFailedBodySchema,
1815
+ transformFailedBodySchema
1816
+ );
1817
+ var errorMessageSchema = valita_exports.tuple([
1818
+ valita_exports.literal("error"),
1819
+ errorBodySchema
1820
+ ]);
1821
+ var ProtocolError = class extends Error {
1822
+ errorBody;
1823
+ constructor(errorBody, options) {
1824
+ super(errorBody.message, options);
1825
+ this.name = "ProtocolError";
1826
+ this.errorBody = errorBody;
1827
+ }
1828
+ get kind() {
1829
+ return this.errorBody.kind;
1830
+ }
1831
+ };
1832
+ function isProtocolError(error) {
1833
+ return error instanceof ProtocolError;
1834
+ }
1835
+
1836
+ // ../zero-protocol/src/custom-queries.ts
1837
+ var transformRequestBodySchema = valita_exports.array(
1838
+ valita_exports.object({
1839
+ id: valita_exports.string(),
1840
+ name: valita_exports.string(),
1841
+ args: readonly(valita_exports.array(jsonSchema))
1842
+ })
1843
+ );
1844
+ var transformedQuerySchema = valita_exports.object({
1845
+ id: valita_exports.string(),
1846
+ name: valita_exports.string(),
1847
+ ast: astSchema
1848
+ });
1849
+ var appErroredQuerySchema = valita_exports.object({
1850
+ error: valita_exports.literal("app"),
1851
+ id: valita_exports.string(),
1852
+ name: valita_exports.string(),
1853
+ // optional for backwards compatibility
1854
+ message: valita_exports.string().optional(),
1855
+ details: jsonSchema.optional()
1856
+ });
1857
+ var parseErroredQuerySchema = valita_exports.object({
1858
+ error: valita_exports.literal("parse"),
1859
+ id: valita_exports.string(),
1860
+ name: valita_exports.string(),
1861
+ message: valita_exports.string(),
1862
+ details: jsonSchema.optional()
1863
+ });
1864
+ var erroredQuerySchema = valita_exports.union(
1865
+ appErroredQuerySchema,
1866
+ parseErroredQuerySchema
1867
+ );
1868
+ var transformResponseBodySchema = valita_exports.array(
1869
+ valita_exports.union(transformedQuerySchema, erroredQuerySchema)
1870
+ );
1871
+ var transformRequestMessageSchema = valita_exports.tuple([
1872
+ valita_exports.literal("transform"),
1873
+ transformRequestBodySchema
1874
+ ]);
1875
+ var transformErrorMessageSchema = valita_exports.tuple([
1876
+ valita_exports.literal("transformError"),
1877
+ valita_exports.array(erroredQuerySchema)
1878
+ ]);
1879
+ var transformFailedMessageSchema = valita_exports.tuple([
1880
+ valita_exports.literal("transformFailed"),
1881
+ transformFailedBodySchema
1882
+ ]);
1883
+ var transformOkMessageSchema = valita_exports.tuple([
1884
+ valita_exports.literal("transformed"),
1885
+ transformResponseBodySchema
1886
+ ]);
1887
+ var transformResponseMessageSchema = valita_exports.union(
1888
+ transformOkMessageSchema,
1889
+ transformFailedMessageSchema
1890
+ );
1891
+
1606
1892
  // ../zero-schema/src/builder/table-builder.ts
1607
1893
  function table(name) {
1608
1894
  return new TableBuilder({
@@ -1611,14 +1897,14 @@ function table(name) {
1611
1897
  primaryKey: []
1612
1898
  });
1613
1899
  }
1614
- function string2() {
1900
+ function string4() {
1615
1901
  return new ColumnBuilder({
1616
1902
  type: "string",
1617
1903
  optional: false,
1618
1904
  customType: null
1619
1905
  });
1620
1906
  }
1621
- function number2() {
1907
+ function number3() {
1622
1908
  return new ColumnBuilder({
1623
1909
  type: "number",
1624
1910
  optional: false,
@@ -2172,37 +2458,207 @@ function makeID(row, schema) {
2172
2458
  return JSON.stringify(schema.primaryKey.map((k) => row[k]));
2173
2459
  }
2174
2460
 
2175
- // ../zero-client/src/client/update-needed-reason-type-enum.ts
2176
- var update_needed_reason_type_enum_exports = {};
2177
- __export(update_needed_reason_type_enum_exports, {
2178
- NewClientGroup: () => NewClientGroup,
2179
- SchemaVersionNotSupported: () => SchemaVersionNotSupported2,
2180
- VersionNotSupported: () => VersionNotSupported2
2461
+ // ../zero-client/src/client/client-error-kind-enum.ts
2462
+ var client_error_kind_enum_exports = {};
2463
+ __export(client_error_kind_enum_exports, {
2464
+ AbruptClose: () => AbruptClose,
2465
+ CleanClose: () => CleanClose,
2466
+ ClientClosed: () => ClientClosed,
2467
+ ConnectTimeout: () => ConnectTimeout,
2468
+ DisconnectTimeout: () => DisconnectTimeout,
2469
+ Hidden: () => Hidden,
2470
+ Internal: () => Internal3,
2471
+ InvalidMessage: () => InvalidMessage2,
2472
+ NoSocketOrigin: () => NoSocketOrigin,
2473
+ PingTimeout: () => PingTimeout,
2474
+ PullTimeout: () => PullTimeout,
2475
+ UnexpectedBaseCookie: () => UnexpectedBaseCookie,
2476
+ UserDisconnect: () => UserDisconnect
2181
2477
  });
2182
- var NewClientGroup = "NewClientGroup";
2183
- var VersionNotSupported2 = "VersionNotSupported";
2184
- var SchemaVersionNotSupported2 = "SchemaVersionNotSupported";
2185
-
2186
- // ../zero-client/src/client/zero.ts
2187
- import "@rocicorp/logger";
2188
- import { resolver as resolver11 } from "@rocicorp/resolver";
2478
+ var AbruptClose = "AbruptClose";
2479
+ var CleanClose = "CleanClose";
2480
+ var ClientClosed = "ClientClosed";
2481
+ var ConnectTimeout = "ConnectTimeout";
2482
+ var DisconnectTimeout = "DisconnectTimeout";
2483
+ var UnexpectedBaseCookie = "UnexpectedBaseCookie";
2484
+ var PingTimeout = "PingTimeout";
2485
+ var PullTimeout = "PullTimeout";
2486
+ var Hidden = "Hidden";
2487
+ var NoSocketOrigin = "NoSocketOrigin";
2488
+ var InvalidMessage2 = "InvalidMessage";
2489
+ var UserDisconnect = "UserDisconnect";
2490
+ var Internal3 = "Internal";
2189
2491
 
2190
- // ../replicache/src/replicache-impl.ts
2191
- import { Lock } from "@rocicorp/lock";
2192
- import { consoleLogSink as consoleLogSink2 } from "@rocicorp/logger";
2193
- import { resolver as resolver6 } from "@rocicorp/resolver";
2492
+ // ../zero-client/src/client/connection-status-enum.ts
2493
+ var connection_status_enum_exports = {};
2494
+ __export(connection_status_enum_exports, {
2495
+ Closed: () => Closed,
2496
+ Connected: () => Connected,
2497
+ Connecting: () => Connecting,
2498
+ Disconnected: () => Disconnected,
2499
+ Error: () => Error2,
2500
+ NeedsAuth: () => NeedsAuth
2501
+ });
2502
+ var Disconnected = "disconnected";
2503
+ var Connecting = "connecting";
2504
+ var Connected = "connected";
2505
+ var NeedsAuth = "needs-auth";
2506
+ var Error2 = "error";
2507
+ var Closed = "closed";
2194
2508
 
2195
- // ../shared/src/document-visible.ts
2196
- import { resolver as resolver3 } from "@rocicorp/resolver";
2197
- function getDocumentVisibilityWatcher(doc, hiddenIntervalMS, signal) {
2198
- return doc ? new DocumentVisibilityWatcherImpl(doc, hiddenIntervalMS, signal) : new DocumentVisibilityWatcherNoDoc();
2199
- }
2200
- var DocumentVisibilityWatcherImpl = class {
2201
- #doc;
2202
- #hiddenIntervalMS;
2203
- #timeoutID = 0;
2204
- // This trails doc.visibilityState by hiddenIntervalMS when being hidden. This
2205
- // is because we want to wait for the tab to be hidden for a while before
2509
+ // ../zero-client/src/client/error.ts
2510
+ var ClientError = class extends Error {
2511
+ errorBody;
2512
+ constructor(errorBody, options) {
2513
+ super(errorBody.message, options);
2514
+ this.name = "ClientError";
2515
+ this.errorBody = { ...errorBody, origin: error_origin_enum_exports.Client };
2516
+ }
2517
+ get kind() {
2518
+ return this.errorBody.kind;
2519
+ }
2520
+ };
2521
+ function isServerError(ex) {
2522
+ return isProtocolError(ex) && (ex.errorBody.origin === error_origin_enum_exports.Server || ex.errorBody.origin === error_origin_enum_exports.ZeroCache);
2523
+ }
2524
+ function isAuthError(ex) {
2525
+ if (isServerError(ex)) {
2526
+ if (ex.kind === error_kind_enum_exports.AuthInvalidated || ex.kind === error_kind_enum_exports.Unauthorized) {
2527
+ return true;
2528
+ }
2529
+ if ((ex.errorBody.kind === error_kind_enum_exports.PushFailed || ex.errorBody.kind === error_kind_enum_exports.TransformFailed) && ex.errorBody.reason === error_reason_enum_exports.HTTP && (ex.errorBody.status === 401 || ex.errorBody.status === 403)) {
2530
+ return true;
2531
+ }
2532
+ }
2533
+ return false;
2534
+ }
2535
+ function getBackoffParams(error) {
2536
+ if (isServerError(error)) {
2537
+ switch (error.errorBody.kind) {
2538
+ case error_kind_enum_exports.Rebalance:
2539
+ case error_kind_enum_exports.Rehome:
2540
+ case error_kind_enum_exports.ServerOverloaded:
2541
+ return error.errorBody;
2542
+ }
2543
+ }
2544
+ return void 0;
2545
+ }
2546
+ function isClientError(ex) {
2547
+ return ex instanceof ClientError && ex.errorBody.origin === error_origin_enum_exports.Client;
2548
+ }
2549
+ var NO_STATUS_TRANSITION = "NO_STATUS_TRANSITION";
2550
+ function getErrorConnectionTransition(ex) {
2551
+ if (isAuthError(ex)) {
2552
+ return {
2553
+ status: connection_status_enum_exports.NeedsAuth,
2554
+ reason: ex
2555
+ };
2556
+ }
2557
+ if (isClientError(ex)) {
2558
+ switch (ex.kind) {
2559
+ // Connecting errors that should continue in the current state
2560
+ case client_error_kind_enum_exports.AbruptClose:
2561
+ case client_error_kind_enum_exports.CleanClose:
2562
+ case client_error_kind_enum_exports.ConnectTimeout:
2563
+ case client_error_kind_enum_exports.PingTimeout:
2564
+ case client_error_kind_enum_exports.PullTimeout:
2565
+ case client_error_kind_enum_exports.Hidden:
2566
+ case client_error_kind_enum_exports.NoSocketOrigin:
2567
+ return { status: NO_STATUS_TRANSITION, reason: ex };
2568
+ // Fatal errors that should transition to error state
2569
+ case client_error_kind_enum_exports.UnexpectedBaseCookie:
2570
+ case client_error_kind_enum_exports.Internal:
2571
+ case client_error_kind_enum_exports.InvalidMessage:
2572
+ case client_error_kind_enum_exports.UserDisconnect:
2573
+ return { status: connection_status_enum_exports.Error, reason: ex };
2574
+ // Disconnected error (this should already result in a disconnected state)
2575
+ case client_error_kind_enum_exports.DisconnectTimeout:
2576
+ return { status: connection_status_enum_exports.Disconnected, reason: ex };
2577
+ // Closed error (this should already result in a closed state)
2578
+ case client_error_kind_enum_exports.ClientClosed:
2579
+ return { status: connection_status_enum_exports.Closed, reason: ex };
2580
+ default:
2581
+ unreachable(ex.kind);
2582
+ }
2583
+ }
2584
+ if (isServerError(ex)) {
2585
+ switch (ex.kind) {
2586
+ // Errors that should transition to error state
2587
+ case error_kind_enum_exports.ClientNotFound:
2588
+ case error_kind_enum_exports.InvalidConnectionRequest:
2589
+ case error_kind_enum_exports.InvalidConnectionRequestBaseCookie:
2590
+ case error_kind_enum_exports.InvalidConnectionRequestLastMutationID:
2591
+ case error_kind_enum_exports.InvalidConnectionRequestClientDeleted:
2592
+ case error_kind_enum_exports.InvalidMessage:
2593
+ case error_kind_enum_exports.InvalidPush:
2594
+ case error_kind_enum_exports.VersionNotSupported:
2595
+ case error_kind_enum_exports.SchemaVersionNotSupported:
2596
+ case error_kind_enum_exports.Internal:
2597
+ // PushFailed and TransformFailed can be auth errors (401/403)
2598
+ // or other errors - handle non-auth cases here
2599
+ case error_kind_enum_exports.PushFailed:
2600
+ case error_kind_enum_exports.TransformFailed:
2601
+ return { status: connection_status_enum_exports.Error, reason: ex };
2602
+ // Errors that should continue with backoff/retry
2603
+ case error_kind_enum_exports.Rebalance:
2604
+ case error_kind_enum_exports.Rehome:
2605
+ case error_kind_enum_exports.ServerOverloaded:
2606
+ return { status: NO_STATUS_TRANSITION, reason: ex };
2607
+ // Auth errors are handled above by isAuthError check
2608
+ case error_kind_enum_exports.AuthInvalidated:
2609
+ case error_kind_enum_exports.Unauthorized:
2610
+ return {
2611
+ status: connection_status_enum_exports.NeedsAuth,
2612
+ reason: ex
2613
+ };
2614
+ // Mutation-specific errors don't affect connection state
2615
+ case error_kind_enum_exports.MutationRateLimited:
2616
+ case error_kind_enum_exports.MutationFailed:
2617
+ return { status: NO_STATUS_TRANSITION, reason: ex };
2618
+ default:
2619
+ unreachable(ex.kind);
2620
+ }
2621
+ }
2622
+ return {
2623
+ status: connection_status_enum_exports.Error,
2624
+ reason: new ClientError({
2625
+ kind: client_error_kind_enum_exports.Internal,
2626
+ message: "Unexpected internal error: " + (ex instanceof Error ? ex.message : typeof ex === "string" ? ex : String(ex ?? "Unknown error"))
2627
+ })
2628
+ };
2629
+ }
2630
+
2631
+ // ../zero-client/src/client/update-needed-reason-type-enum.ts
2632
+ var update_needed_reason_type_enum_exports = {};
2633
+ __export(update_needed_reason_type_enum_exports, {
2634
+ NewClientGroup: () => NewClientGroup,
2635
+ SchemaVersionNotSupported: () => SchemaVersionNotSupported2,
2636
+ VersionNotSupported: () => VersionNotSupported2
2637
+ });
2638
+ var NewClientGroup = "NewClientGroup";
2639
+ var VersionNotSupported2 = "VersionNotSupported";
2640
+ var SchemaVersionNotSupported2 = "SchemaVersionNotSupported";
2641
+
2642
+ // ../zero-client/src/client/zero.ts
2643
+ import "@rocicorp/logger";
2644
+ import { resolver as resolver11 } from "@rocicorp/resolver";
2645
+
2646
+ // ../replicache/src/replicache-impl.ts
2647
+ import { Lock } from "@rocicorp/lock";
2648
+ import { consoleLogSink as consoleLogSink2 } from "@rocicorp/logger";
2649
+ import { resolver as resolver6 } from "@rocicorp/resolver";
2650
+
2651
+ // ../shared/src/document-visible.ts
2652
+ import { resolver as resolver3 } from "@rocicorp/resolver";
2653
+ function getDocumentVisibilityWatcher(doc, hiddenIntervalMS, signal) {
2654
+ return doc ? new DocumentVisibilityWatcherImpl(doc, hiddenIntervalMS, signal) : new DocumentVisibilityWatcherNoDoc();
2655
+ }
2656
+ var DocumentVisibilityWatcherImpl = class {
2657
+ #doc;
2658
+ #hiddenIntervalMS;
2659
+ #timeoutID = 0;
2660
+ // This trails doc.visibilityState by hiddenIntervalMS when being hidden. This
2661
+ // is because we want to wait for the tab to be hidden for a while before
2206
2662
  // considering as hidden.
2207
2663
  visibilityState;
2208
2664
  #promises = /* @__PURE__ */ new Set();
@@ -6283,6 +6739,24 @@ function validateOptions(options) {
6283
6739
  }
6284
6740
  }
6285
6741
 
6742
+ // ../shared/src/promise-race.ts
6743
+ var NO_PROMISES_MESSAGE = "No promises to race";
6744
+ var wrapPromise = (key, promise) => Promise.resolve(promise).then((result) => ({
6745
+ key,
6746
+ status: "fulfilled",
6747
+ result
6748
+ }));
6749
+ async function promiseRace(promises) {
6750
+ const keys = Object.keys(promises);
6751
+ if (keys.length === 0) {
6752
+ throw new Error(NO_PROMISES_MESSAGE);
6753
+ }
6754
+ const wrapped = keys.map(
6755
+ (key) => wrapPromise(key, promises[key])
6756
+ );
6757
+ return await Promise.race(wrapped);
6758
+ }
6759
+
6286
6760
  // ../shared/src/sentinels.ts
6287
6761
  function emptyFunction() {
6288
6762
  }
@@ -6292,38 +6766,6 @@ function identity(x) {
6292
6766
  return x;
6293
6767
  }
6294
6768
 
6295
- // ../shared/src/subscribable.ts
6296
- var Subscribable = class {
6297
- _listeners = /* @__PURE__ */ new Set();
6298
- /**
6299
- * Subscribe to the subscribable.
6300
- *
6301
- * @param listener - The listener to subscribe to.
6302
- * @returns A function to unsubscribe from the subscribable.
6303
- */
6304
- subscribe = (listener) => {
6305
- this._listeners.add(listener);
6306
- return () => {
6307
- this._listeners.delete(listener);
6308
- };
6309
- };
6310
- /**
6311
- * Notify all listeners.
6312
- *
6313
- * @param update - The update to notify listeners with.
6314
- */
6315
- notify = (update) => {
6316
- this._listeners.forEach((listener) => listener(update));
6317
- };
6318
- hasListeners = () => this._listeners.size > 0;
6319
- /**
6320
- * Unsubscribe all listeners.
6321
- */
6322
- cleanup = () => {
6323
- this._listeners.clear();
6324
- };
6325
- };
6326
-
6327
6769
  // ../zero-protocol/src/delete-clients.ts
6328
6770
  var deleteClientsBodySchema = valita_exports.union(
6329
6771
  readonlyObject({
@@ -6401,56 +6843,6 @@ function encodeSecProtocols(initConnectionMessage, authToken) {
6401
6843
  return encodeURIComponent(btoa(s));
6402
6844
  }
6403
6845
 
6404
- // ../zero-protocol/src/error.ts
6405
- var basicErrorKindSchema = literalUnion(
6406
- error_kind_enum_exports.AuthInvalidated,
6407
- error_kind_enum_exports.ClientNotFound,
6408
- error_kind_enum_exports.InvalidConnectionRequest,
6409
- error_kind_enum_exports.InvalidConnectionRequestBaseCookie,
6410
- error_kind_enum_exports.InvalidConnectionRequestLastMutationID,
6411
- error_kind_enum_exports.InvalidConnectionRequestClientDeleted,
6412
- error_kind_enum_exports.InvalidMessage,
6413
- error_kind_enum_exports.InvalidPush,
6414
- error_kind_enum_exports.MutationRateLimited,
6415
- error_kind_enum_exports.MutationFailed,
6416
- error_kind_enum_exports.Unauthorized,
6417
- error_kind_enum_exports.VersionNotSupported,
6418
- error_kind_enum_exports.SchemaVersionNotSupported,
6419
- error_kind_enum_exports.Internal
6420
- );
6421
- var basicErrorBodySchema = valita_exports.object({
6422
- kind: basicErrorKindSchema,
6423
- message: valita_exports.string()
6424
- });
6425
- var backoffErrorKindSchema = literalUnion(
6426
- error_kind_enum_exports.Rebalance,
6427
- error_kind_enum_exports.Rehome,
6428
- error_kind_enum_exports.ServerOverloaded
6429
- );
6430
- var backoffBodySchema = valita_exports.object({
6431
- kind: backoffErrorKindSchema,
6432
- message: valita_exports.string(),
6433
- minBackoffMs: valita_exports.number().optional(),
6434
- maxBackoffMs: valita_exports.number().optional(),
6435
- // Query parameters to send in the next reconnect. In the event of
6436
- // a conflict, these will be overridden by the parameters used by
6437
- // the client; it is the responsibility of the server to avoid
6438
- // parameter name conflicts.
6439
- //
6440
- // The parameters will only be added to the immediately following
6441
- // reconnect, and not after that.
6442
- reconnectParams: valita_exports.record(valita_exports.string()).optional()
6443
- });
6444
- var errorKindSchema = valita_exports.union(
6445
- basicErrorKindSchema,
6446
- backoffErrorKindSchema
6447
- );
6448
- var errorBodySchema = valita_exports.union(basicErrorBodySchema, backoffBodySchema);
6449
- var errorMessageSchema = valita_exports.tuple([
6450
- valita_exports.literal("error"),
6451
- errorBodySchema
6452
- ]);
6453
-
6454
6846
  // ../zero-protocol/src/mutation-type-enum.ts
6455
6847
  var CRUD = "crud";
6456
6848
  var Custom = "custom";
@@ -6522,24 +6914,25 @@ var pushBodySchema = valita_exports.object({
6522
6914
  requestID: valita_exports.string()
6523
6915
  });
6524
6916
  var pushMessageSchema = valita_exports.tuple([valita_exports.literal("push"), pushBodySchema]);
6525
- var mutationIDSchema = valita_exports.object({
6526
- id: valita_exports.number(),
6527
- clientID: valita_exports.string()
6528
- });
6529
6917
  var appErrorSchema = valita_exports.object({
6530
6918
  error: valita_exports.literal("app"),
6531
6919
  // The user can return any additional data here
6920
+ message: valita_exports.string().optional(),
6532
6921
  details: jsonSchema.optional()
6533
6922
  });
6534
- var zeroErrorSchema2 = valita_exports.object({
6535
- error: literalUnion("oooMutation", "alreadyProcessed"),
6923
+ var zeroErrorSchema = valita_exports.object({
6924
+ error: valita_exports.union(
6925
+ /** @deprecated push oooMutation errors are now represented as ['error', { ... }] messages */
6926
+ valita_exports.literal("oooMutation"),
6927
+ valita_exports.literal("alreadyProcessed")
6928
+ ),
6536
6929
  details: jsonSchema.optional()
6537
6930
  });
6538
6931
  var mutationOkSchema = valita_exports.object({
6539
6932
  // The user can return any additional data here
6540
6933
  data: jsonSchema.optional()
6541
6934
  });
6542
- var mutationErrorSchema = valita_exports.union(appErrorSchema, zeroErrorSchema2);
6935
+ var mutationErrorSchema = valita_exports.union(appErrorSchema, zeroErrorSchema);
6543
6936
  var mutationResultSchema = valita_exports.union(
6544
6937
  mutationOkSchema,
6545
6938
  mutationErrorSchema
@@ -6552,28 +6945,33 @@ var pushOkSchema = valita_exports.object({
6552
6945
  mutations: valita_exports.array(mutationResponseSchema)
6553
6946
  });
6554
6947
  var unsupportedPushVersionSchema = valita_exports.object({
6948
+ /** @deprecated */
6555
6949
  error: valita_exports.literal("unsupportedPushVersion"),
6556
- // optional for backwards compatibility
6557
- // This field is included so the client knows which mutations
6558
- // were not processed by the server.
6950
+ /** @deprecated */
6559
6951
  mutationIDs: valita_exports.array(mutationIDSchema).optional()
6560
6952
  });
6561
6953
  var unsupportedSchemaVersionSchema = valita_exports.object({
6954
+ /** @deprecated */
6562
6955
  error: valita_exports.literal("unsupportedSchemaVersion"),
6563
- // optional for backwards compatibility
6564
- // This field is included so the client knows which mutations
6565
- // were not processed by the server.
6956
+ /** @deprecated */
6566
6957
  mutationIDs: valita_exports.array(mutationIDSchema).optional()
6567
6958
  });
6568
6959
  var httpErrorSchema = valita_exports.object({
6960
+ /** @deprecated */
6569
6961
  error: valita_exports.literal("http"),
6962
+ /** @deprecated */
6570
6963
  status: valita_exports.number(),
6964
+ /** @deprecated */
6571
6965
  details: valita_exports.string(),
6966
+ /** @deprecated */
6572
6967
  mutationIDs: valita_exports.array(mutationIDSchema).optional()
6573
6968
  });
6574
6969
  var zeroPusherErrorSchema = valita_exports.object({
6970
+ /** @deprecated */
6575
6971
  error: valita_exports.literal("zeroPusher"),
6972
+ /** @deprecated */
6576
6973
  details: valita_exports.string(),
6974
+ /** @deprecated */
6577
6975
  mutationIDs: valita_exports.array(mutationIDSchema).optional()
6578
6976
  });
6579
6977
  var pushErrorSchema = valita_exports.union(
@@ -6582,10 +6980,14 @@ var pushErrorSchema = valita_exports.union(
6582
6980
  httpErrorSchema,
6583
6981
  zeroPusherErrorSchema
6584
6982
  );
6585
- var pushResponseSchema = valita_exports.union(pushOkSchema, pushErrorSchema);
6983
+ var pushResponseBodySchema = valita_exports.union(pushOkSchema, pushErrorSchema);
6984
+ var pushResponseSchema = valita_exports.union(
6985
+ pushResponseBodySchema,
6986
+ pushFailedBodySchema
6987
+ );
6586
6988
  var pushResponseMessageSchema = valita_exports.tuple([
6587
6989
  valita_exports.literal("pushResponse"),
6588
- pushResponseSchema
6990
+ pushResponseBodySchema
6589
6991
  ]);
6590
6992
  var ackMutationResponsesMessageSchema = valita_exports.tuple([
6591
6993
  valita_exports.literal("ackMutationResponses"),
@@ -6750,7 +7152,7 @@ var downstreamSchema = valita_exports.union(
6750
7152
  );
6751
7153
 
6752
7154
  // ../zero-protocol/src/protocol-version.ts
6753
- var PROTOCOL_VERSION = 37;
7155
+ var PROTOCOL_VERSION = 39;
6754
7156
  var MIN_SERVER_SUPPORTED_SYNC_PROTOCOL = 18;
6755
7157
  assert(MIN_SERVER_SUPPORTED_SYNC_PROTOCOL < PROTOCOL_VERSION);
6756
7158
 
@@ -9227,80 +9629,1385 @@ var UnionFanIn = class {
9227
9629
  return;
9228
9630
  }
9229
9631
  }
9230
- assert(hadMatch, "Pusher was not one of the inputs to union-fan-in!");
9231
- this.#output.push(change, this);
9632
+ assert(hadMatch, "Pusher was not one of the inputs to union-fan-in!");
9633
+ this.#output.push(change, this);
9634
+ }
9635
+ fanOutStartedPushing() {
9636
+ assert(this.#fanOutPushStarted === false);
9637
+ this.#fanOutPushStarted = true;
9638
+ }
9639
+ fanOutDonePushing(fanOutChangeType) {
9640
+ assert(this.#fanOutPushStarted);
9641
+ this.#fanOutPushStarted = false;
9642
+ if (this.#inputs.length === 0) {
9643
+ return;
9644
+ }
9645
+ if (this.#accumulatedPushes.length === 0) {
9646
+ return;
9647
+ }
9648
+ pushAccumulatedChanges(
9649
+ this.#accumulatedPushes,
9650
+ this.#output,
9651
+ this,
9652
+ fanOutChangeType,
9653
+ mergeRelationships,
9654
+ makeAddEmptyRelationships(this.#schema)
9655
+ );
9656
+ }
9657
+ setOutput(output) {
9658
+ this.#output = output;
9659
+ }
9660
+ };
9661
+
9662
+ // ../zql/src/ivm/union-fan-out.ts
9663
+ var UnionFanOut = class {
9664
+ #destroyCount = 0;
9665
+ #unionFanIn;
9666
+ #input;
9667
+ #outputs = [];
9668
+ constructor(input) {
9669
+ this.#input = input;
9670
+ input.setOutput(this);
9671
+ }
9672
+ setFanIn(fanIn) {
9673
+ assert(!this.#unionFanIn, "FanIn already set for this FanOut");
9674
+ this.#unionFanIn = fanIn;
9675
+ }
9676
+ push(change) {
9677
+ must(this.#unionFanIn).fanOutStartedPushing();
9678
+ for (const output of this.#outputs) {
9679
+ output.push(change, this);
9680
+ }
9681
+ must(this.#unionFanIn).fanOutDonePushing(change.type);
9682
+ }
9683
+ setOutput(output) {
9684
+ this.#outputs.push(output);
9685
+ }
9686
+ getSchema() {
9687
+ return this.#input.getSchema();
9688
+ }
9689
+ fetch(req) {
9690
+ return this.#input.fetch(req);
9691
+ }
9692
+ cleanup(_req) {
9693
+ return [];
9694
+ }
9695
+ destroy() {
9696
+ if (this.#destroyCount < this.#outputs.length) {
9697
+ ++this.#destroyCount;
9698
+ if (this.#destroyCount === this.#outputs.length) {
9699
+ this.#input.destroy();
9700
+ }
9701
+ } else {
9702
+ throw new Error("FanOut already destroyed once for each output");
9703
+ }
9704
+ }
9705
+ };
9706
+
9707
+ // ../zql/src/planner/planner-fan-in.ts
9708
+ var PlannerFanIn = class {
9709
+ kind = "fan-in";
9710
+ #type;
9711
+ #output;
9712
+ #inputs;
9713
+ constructor(inputs) {
9714
+ this.#type = "FI";
9715
+ this.#inputs = inputs;
9716
+ }
9717
+ get type() {
9718
+ return this.#type;
9719
+ }
9720
+ closestJoinOrSource() {
9721
+ return "join";
9722
+ }
9723
+ setOutput(node) {
9724
+ this.#output = node;
9725
+ }
9726
+ get output() {
9727
+ assert(this.#output !== void 0, "Output not set");
9728
+ return this.#output;
9729
+ }
9730
+ reset() {
9731
+ this.#type = "FI";
9732
+ }
9733
+ convertToUFI() {
9734
+ this.#type = "UFI";
9735
+ }
9736
+ /**
9737
+ * Propagate unlimiting when a parent join is flipped.
9738
+ * Fan-in propagates to all of its inputs.
9739
+ */
9740
+ propagateUnlimitFromFlippedJoin() {
9741
+ for (const input of this.#inputs) {
9742
+ if ("propagateUnlimitFromFlippedJoin" in input && typeof input.propagateUnlimitFromFlippedJoin === "function") {
9743
+ input.propagateUnlimitFromFlippedJoin();
9744
+ }
9745
+ }
9746
+ }
9747
+ estimateCost(downstreamChildSelectivity, branchPattern, planDebugger) {
9748
+ let totalCost = {
9749
+ returnedRows: 0,
9750
+ cost: 0,
9751
+ scanEst: 0,
9752
+ startupCost: 0,
9753
+ selectivity: 0,
9754
+ limit: void 0
9755
+ };
9756
+ if (this.#type === "FI") {
9757
+ const updatedPattern = [0, ...branchPattern];
9758
+ let maxrows = 0;
9759
+ let maxRunningCost = 0;
9760
+ let maxStartupCost = 0;
9761
+ let maxScanEst = 0;
9762
+ let noMatchProb = 1;
9763
+ for (const input of this.#inputs) {
9764
+ const cost = input.estimateCost(
9765
+ downstreamChildSelectivity,
9766
+ updatedPattern,
9767
+ planDebugger
9768
+ );
9769
+ if (cost.returnedRows > maxrows) {
9770
+ maxrows = cost.returnedRows;
9771
+ }
9772
+ if (cost.cost > maxRunningCost) {
9773
+ maxRunningCost = cost.cost;
9774
+ }
9775
+ if (cost.startupCost > maxStartupCost) {
9776
+ maxStartupCost = cost.startupCost;
9777
+ }
9778
+ if (cost.scanEst > maxScanEst) {
9779
+ maxScanEst = cost.scanEst;
9780
+ }
9781
+ noMatchProb *= 1 - cost.selectivity;
9782
+ assert(
9783
+ totalCost.limit === void 0 || cost.limit === totalCost.limit,
9784
+ "All FanIn inputs should have the same limit"
9785
+ );
9786
+ totalCost.limit = cost.limit;
9787
+ }
9788
+ totalCost.returnedRows = maxrows;
9789
+ totalCost.cost = maxRunningCost;
9790
+ totalCost.selectivity = 1 - noMatchProb;
9791
+ totalCost.startupCost = maxStartupCost;
9792
+ totalCost.scanEst = maxScanEst;
9793
+ } else {
9794
+ let i = 0;
9795
+ let noMatchProb = 1;
9796
+ for (const input of this.#inputs) {
9797
+ const updatedPattern = [i, ...branchPattern];
9798
+ const cost = input.estimateCost(
9799
+ downstreamChildSelectivity,
9800
+ updatedPattern,
9801
+ planDebugger
9802
+ );
9803
+ totalCost.returnedRows += cost.returnedRows;
9804
+ totalCost.cost += cost.cost;
9805
+ totalCost.scanEst += cost.scanEst;
9806
+ totalCost.startupCost = totalCost.startupCost + cost.startupCost;
9807
+ noMatchProb *= 1 - cost.selectivity;
9808
+ assert(
9809
+ totalCost.limit === void 0 || cost.limit === totalCost.limit,
9810
+ "All FanIn inputs should have the same limit"
9811
+ );
9812
+ totalCost.limit = cost.limit;
9813
+ i++;
9814
+ }
9815
+ totalCost.selectivity = 1 - noMatchProb;
9816
+ }
9817
+ planDebugger?.log({
9818
+ type: "node-cost",
9819
+ nodeType: "fan-in",
9820
+ node: this.#type,
9821
+ branchPattern,
9822
+ downstreamChildSelectivity,
9823
+ costEstimate: totalCost
9824
+ });
9825
+ return totalCost;
9826
+ }
9827
+ propagateConstraints(branchPattern, constraint, from, planDebugger) {
9828
+ planDebugger?.log({
9829
+ type: "node-constraint",
9830
+ nodeType: "fan-in",
9831
+ node: this.#type,
9832
+ branchPattern,
9833
+ constraint,
9834
+ from: from?.kind ?? "unknown"
9835
+ });
9836
+ if (this.#type === "FI") {
9837
+ const updatedPattern = [0, ...branchPattern];
9838
+ for (const input of this.#inputs) {
9839
+ input.propagateConstraints(
9840
+ updatedPattern,
9841
+ constraint,
9842
+ this,
9843
+ planDebugger
9844
+ );
9845
+ }
9846
+ return;
9847
+ }
9848
+ let i = 0;
9849
+ for (const input of this.#inputs) {
9850
+ input.propagateConstraints(
9851
+ [i, ...branchPattern],
9852
+ constraint,
9853
+ this,
9854
+ planDebugger
9855
+ );
9856
+ i++;
9857
+ }
9858
+ }
9859
+ };
9860
+
9861
+ // ../zql/src/planner/planner-fan-out.ts
9862
+ var PlannerFanOut = class {
9863
+ kind = "fan-out";
9864
+ #type;
9865
+ #outputs = [];
9866
+ #input;
9867
+ constructor(input) {
9868
+ this.#type = "FO";
9869
+ this.#input = input;
9870
+ }
9871
+ get type() {
9872
+ return this.#type;
9873
+ }
9874
+ addOutput(node) {
9875
+ this.#outputs.push(node);
9876
+ }
9877
+ get outputs() {
9878
+ return this.#outputs;
9879
+ }
9880
+ closestJoinOrSource() {
9881
+ return this.#input.closestJoinOrSource();
9882
+ }
9883
+ propagateConstraints(branchPattern, constraint, from, planDebugger) {
9884
+ planDebugger?.log({
9885
+ type: "node-constraint",
9886
+ nodeType: "fan-out",
9887
+ node: "FO",
9888
+ branchPattern,
9889
+ constraint,
9890
+ from: from?.kind ?? "unknown"
9891
+ });
9892
+ this.#input.propagateConstraints(
9893
+ branchPattern,
9894
+ constraint,
9895
+ this,
9896
+ planDebugger
9897
+ );
9898
+ }
9899
+ estimateCost(downstreamChildSelectivity, branchPattern, planDebugger) {
9900
+ const ret = this.#input.estimateCost(
9901
+ downstreamChildSelectivity,
9902
+ branchPattern,
9903
+ planDebugger
9904
+ );
9905
+ planDebugger?.log({
9906
+ type: "node-cost",
9907
+ nodeType: "fan-out",
9908
+ node: "FO",
9909
+ branchPattern,
9910
+ downstreamChildSelectivity,
9911
+ costEstimate: ret
9912
+ });
9913
+ return ret;
9914
+ }
9915
+ convertToUFO() {
9916
+ this.#type = "UFO";
9917
+ }
9918
+ reset() {
9919
+ this.#type = "FO";
9920
+ }
9921
+ /**
9922
+ * Propagate unlimiting when a parent join is flipped.
9923
+ * Fan-out propagates to its input.
9924
+ */
9925
+ propagateUnlimitFromFlippedJoin() {
9926
+ if ("propagateUnlimitFromFlippedJoin" in this.#input && typeof this.#input.propagateUnlimitFromFlippedJoin === "function") {
9927
+ this.#input.propagateUnlimitFromFlippedJoin();
9928
+ }
9929
+ }
9930
+ };
9931
+
9932
+ // ../zql/src/planner/planner-constraint.ts
9933
+ function mergeConstraints(a, b) {
9934
+ if (!a) return b;
9935
+ if (!b) return a;
9936
+ return { ...a, ...b };
9937
+ }
9938
+
9939
+ // ../zql/src/planner/planner-connection.ts
9940
+ var PlannerConnection = class {
9941
+ kind = "connection";
9942
+ // ========================================================================
9943
+ // IMMUTABLE STRUCTURE (set during construction, never changes)
9944
+ // ========================================================================
9945
+ #sort;
9946
+ #filters;
9947
+ #model;
9948
+ table;
9949
+ name;
9950
+ // Human-readable name for debugging (defaults to table name)
9951
+ #baseConstraints;
9952
+ // Constraints from parent correlation
9953
+ #baseLimit;
9954
+ // Original limit from query structure (never modified)
9955
+ selectivity;
9956
+ // Fraction of rows passing filters (1.0 = no filtering)
9957
+ #output;
9958
+ // Set once during graph construction
9959
+ // ========================================================================
9960
+ // MUTABLE PLANNING STATE (changes during plan search)
9961
+ // ========================================================================
9962
+ /**
9963
+ * Current limit during planning. Can be cleared (set to undefined) when a
9964
+ * parent join is flipped, indicating this connection is now in an outer loop
9965
+ * and should not be limited by EXISTS semantics.
9966
+ */
9967
+ limit;
9968
+ /**
9969
+ * Constraints accumulated from parent joins during planning.
9970
+ * Key is a path through the graph (e.g., "0,1" for branch pattern [0,1]).
9971
+ *
9972
+ * Undefined constraints are possible when a FO converts to UFO and only
9973
+ * a single join in the UFO is flipped - other branches report undefined.
9974
+ */
9975
+ #constraints;
9976
+ #isRoot;
9977
+ /**
9978
+ * Cached per-constraint costs to avoid redundant cost model calls.
9979
+ * Maps constraint key (branch pattern string) to computed cost.
9980
+ * Invalidated when constraints change.
9981
+ */
9982
+ #cachedConstraintCosts = /* @__PURE__ */ new Map();
9983
+ constructor(table2, model, sort, filters, isRoot, baseConstraints, limit, name) {
9984
+ this.table = table2;
9985
+ this.name = name ?? table2;
9986
+ this.#sort = sort;
9987
+ this.#filters = filters;
9988
+ this.#model = model;
9989
+ this.#baseConstraints = baseConstraints;
9990
+ this.#baseLimit = limit;
9991
+ this.limit = limit;
9992
+ this.#constraints = /* @__PURE__ */ new Map();
9993
+ this.#isRoot = isRoot;
9994
+ if (limit !== void 0 && filters) {
9995
+ const costWithFilters = model(table2, sort, filters, void 0);
9996
+ const costWithoutFilters = model(table2, sort, void 0, void 0);
9997
+ this.selectivity = costWithoutFilters.rows > 0 ? costWithFilters.rows / costWithoutFilters.rows : 1;
9998
+ } else {
9999
+ this.selectivity = 1;
10000
+ }
10001
+ }
10002
+ setOutput(node) {
10003
+ this.#output = node;
10004
+ }
10005
+ get output() {
10006
+ assert(this.#output !== void 0, "Output not set");
10007
+ return this.#output;
10008
+ }
10009
+ closestJoinOrSource() {
10010
+ return "connection";
10011
+ }
10012
+ /**
10013
+ * Constraints are uniquely identified by their path through the
10014
+ * graph.
10015
+ *
10016
+ * FO represents all sub-joins as a single path.
10017
+ * UFO represents each sub-join as a separate path.
10018
+ * The first branch in a UFO will match the path of FO so no re-set needs to happen
10019
+ * when swapping from FO to UFO.
10020
+ *
10021
+ * FO swaps to UFO when a join inside FO-FI gets flipped.
10022
+ *
10023
+ * The max of the last element of the paths is the number of
10024
+ * root branches.
10025
+ */
10026
+ propagateConstraints(path, c, from, planDebugger) {
10027
+ const key = path.join(",");
10028
+ this.#constraints.set(key, c);
10029
+ this.#cachedConstraintCosts.clear();
10030
+ planDebugger?.log({
10031
+ type: "node-constraint",
10032
+ nodeType: "connection",
10033
+ node: this.name,
10034
+ branchPattern: path,
10035
+ constraint: c,
10036
+ from: from?.kind ?? "unknown"
10037
+ });
10038
+ }
10039
+ estimateCost(downstreamChildSelectivity, branchPattern, planDebugger) {
10040
+ const key = branchPattern.join(",");
10041
+ let cost = this.#cachedConstraintCosts.get(key);
10042
+ if (cost !== void 0) {
10043
+ return cost;
10044
+ }
10045
+ const constraint = this.#constraints.get(key);
10046
+ const mergedConstraint = mergeConstraints(
10047
+ this.#baseConstraints,
10048
+ constraint
10049
+ );
10050
+ const { startupCost, rows } = this.#model(
10051
+ this.table,
10052
+ this.#sort,
10053
+ this.#filters,
10054
+ mergedConstraint
10055
+ );
10056
+ cost = {
10057
+ startupCost,
10058
+ scanEst: this.limit === void 0 ? rows : Math.min(rows, this.limit / downstreamChildSelectivity),
10059
+ cost: 0,
10060
+ returnedRows: rows,
10061
+ selectivity: this.selectivity,
10062
+ limit: this.limit
10063
+ };
10064
+ this.#cachedConstraintCosts.set(key, cost);
10065
+ planDebugger?.log({
10066
+ type: "node-cost",
10067
+ nodeType: "connection",
10068
+ node: this.name,
10069
+ branchPattern,
10070
+ downstreamChildSelectivity,
10071
+ costEstimate: cost,
10072
+ filters: this.#filters
10073
+ });
10074
+ return cost;
10075
+ }
10076
+ /**
10077
+ * Remove the limit from this connection.
10078
+ * Called when a parent join is flipped, making this connection part of an
10079
+ * outer loop that should produce all rows rather than stopping at the limit.
10080
+ */
10081
+ unlimit() {
10082
+ if (this.#isRoot) {
10083
+ return;
10084
+ }
10085
+ if (this.limit !== void 0) {
10086
+ this.limit = void 0;
10087
+ }
10088
+ }
10089
+ /**
10090
+ * Propagate unlimiting when a parent join is flipped.
10091
+ * For connections, we simply remove the limit.
10092
+ */
10093
+ propagateUnlimitFromFlippedJoin() {
10094
+ this.unlimit();
10095
+ }
10096
+ reset() {
10097
+ this.#constraints.clear();
10098
+ this.limit = this.#baseLimit;
10099
+ this.#cachedConstraintCosts.clear();
10100
+ }
10101
+ /**
10102
+ * Capture constraint state for snapshotting.
10103
+ * Used by PlannerGraph to save/restore planning state.
10104
+ */
10105
+ captureConstraints() {
10106
+ return new Map(this.#constraints);
10107
+ }
10108
+ /**
10109
+ * Restore constraint state from a snapshot.
10110
+ * Used by PlannerGraph to restore planning state.
10111
+ */
10112
+ restoreConstraints(constraints) {
10113
+ this.#constraints.clear();
10114
+ for (const [key, value] of constraints) {
10115
+ this.#constraints.set(key, value);
10116
+ }
10117
+ this.#cachedConstraintCosts.clear();
10118
+ }
10119
+ /**
10120
+ * Get current constraints for debugging.
10121
+ * Returns a copy of the constraints map.
10122
+ */
10123
+ getConstraintsForDebug() {
10124
+ return new Map(this.#constraints);
10125
+ }
10126
+ /**
10127
+ * Get filters for debugging.
10128
+ * Returns the filters applied to this connection.
10129
+ */
10130
+ getFiltersForDebug() {
10131
+ return this.#filters;
10132
+ }
10133
+ /**
10134
+ * Get estimated cost for each constraint branch.
10135
+ * Returns a map of constraint key to cost estimate.
10136
+ * Forces cost calculation if not already cached.
10137
+ */
10138
+ getConstraintCostsForDebug() {
10139
+ return new Map(this.#cachedConstraintCosts);
10140
+ }
10141
+ };
10142
+
10143
+ // ../zql/src/planner/planner-source.ts
10144
+ var PlannerSource = class {
10145
+ name;
10146
+ #model;
10147
+ constructor(name, model) {
10148
+ this.name = name;
10149
+ this.#model = model;
10150
+ }
10151
+ connect(sort, filters, isRoot, baseConstraints, limit) {
10152
+ return new PlannerConnection(
10153
+ this.name,
10154
+ this.#model,
10155
+ sort,
10156
+ filters,
10157
+ isRoot,
10158
+ baseConstraints,
10159
+ limit
10160
+ );
10161
+ }
10162
+ };
10163
+
10164
+ // ../zql/src/planner/planner-graph.ts
10165
+ var MAX_FLIPPABLE_JOINS = 13;
10166
+ var PlannerGraph = class {
10167
+ // Sources indexed by table name
10168
+ #sources = /* @__PURE__ */ new Map();
10169
+ // The final output node where constraint propagation starts
10170
+ #terminus = void 0;
10171
+ // Collections of nodes with mutable planning state
10172
+ joins = [];
10173
+ fanOuts = [];
10174
+ fanIns = [];
10175
+ connections = [];
10176
+ /**
10177
+ * Reset all planning state back to initial values for another planning pass.
10178
+ * Resets only mutable planning state - graph structure is unchanged.
10179
+ *
10180
+ * This allows replanning the same query graph with different strategies.
10181
+ */
10182
+ resetPlanningState() {
10183
+ for (const j of this.joins) j.reset();
10184
+ for (const fo of this.fanOuts) fo.reset();
10185
+ for (const fi of this.fanIns) fi.reset();
10186
+ for (const c of this.connections) c.reset();
10187
+ }
10188
+ /**
10189
+ * Create and register a source (table) in the graph.
10190
+ */
10191
+ addSource(name, model) {
10192
+ assert(
10193
+ !this.#sources.has(name),
10194
+ `Source ${name} already exists in the graph`
10195
+ );
10196
+ const source = new PlannerSource(name, model);
10197
+ this.#sources.set(name, source);
10198
+ return source;
10199
+ }
10200
+ /**
10201
+ * Get a source by table name.
10202
+ */
10203
+ getSource(name) {
10204
+ const source = this.#sources.get(name);
10205
+ assert(source !== void 0, `Source ${name} not found in the graph`);
10206
+ return source;
10207
+ }
10208
+ /**
10209
+ * Check if a source exists by table name.
10210
+ */
10211
+ hasSource(name) {
10212
+ return this.#sources.has(name);
10213
+ }
10214
+ /**
10215
+ * Set the terminus (final output) node of the graph.
10216
+ * Constraint propagation starts from this node.
10217
+ */
10218
+ setTerminus(terminus) {
10219
+ this.#terminus = terminus;
10220
+ }
10221
+ /**
10222
+ * Initiate constraint propagation from the terminus node.
10223
+ * This sends constraints up through the graph to update
10224
+ * connection cost estimates.
10225
+ */
10226
+ propagateConstraints(planDebugger) {
10227
+ assert(
10228
+ this.#terminus !== void 0,
10229
+ "Cannot propagate constraints without a terminus node"
10230
+ );
10231
+ this.#terminus.propagateConstraints(planDebugger);
10232
+ }
10233
+ /**
10234
+ * Calculate total cost of the current plan.
10235
+ * Total cost includes both startup cost (one-time, e.g., sorting) and running cost.
10236
+ */
10237
+ getTotalCost(planDebugger) {
10238
+ const estimate = must(this.#terminus).estimateCost(planDebugger);
10239
+ return estimate.cost + estimate.startupCost;
10240
+ }
10241
+ /**
10242
+ * Capture a lightweight snapshot of the current planning state.
10243
+ * Used for backtracking during multi-start greedy search.
10244
+ *
10245
+ * Captures mutable state including pinned flags, join types, and
10246
+ * constraint maps to avoid needing repropagation on restore.
10247
+ *
10248
+ * @returns A snapshot that can be restored via restorePlanningSnapshot()
10249
+ */
10250
+ capturePlanningSnapshot() {
10251
+ return {
10252
+ connections: this.connections.map((c) => ({
10253
+ limit: c.limit
10254
+ })),
10255
+ joins: this.joins.map((j) => ({ type: j.type })),
10256
+ fanOuts: this.fanOuts.map((fo) => ({ type: fo.type })),
10257
+ fanIns: this.fanIns.map((fi) => ({ type: fi.type })),
10258
+ connectionConstraints: this.connections.map((c) => c.captureConstraints())
10259
+ };
10260
+ }
10261
+ /**
10262
+ * Restore planning state from a previously captured snapshot.
10263
+ * Used for backtracking when a planning attempt fails.
10264
+ *
10265
+ * Restores pinned flags, join types, and constraint maps, eliminating
10266
+ * the need for repropagation.
10267
+ *
10268
+ * @param state - Snapshot created by capturePlanningSnapshot()
10269
+ */
10270
+ restorePlanningSnapshot(state) {
10271
+ this.#validateSnapshotShape(state);
10272
+ this.#restoreConnections(state);
10273
+ this.#restoreJoins(state);
10274
+ this.#restoreFanNodes(state);
10275
+ }
10276
+ /**
10277
+ * Validate that snapshot shape matches current graph structure.
10278
+ */
10279
+ #validateSnapshotShape(state) {
10280
+ assert(
10281
+ this.connections.length === state.connections.length,
10282
+ "Plan state mismatch: connections"
10283
+ );
10284
+ assert(
10285
+ this.joins.length === state.joins.length,
10286
+ "Plan state mismatch: joins"
10287
+ );
10288
+ assert(
10289
+ this.fanOuts.length === state.fanOuts.length,
10290
+ "Plan state mismatch: fanOuts"
10291
+ );
10292
+ assert(
10293
+ this.fanIns.length === state.fanIns.length,
10294
+ "Plan state mismatch: fanIns"
10295
+ );
10296
+ assert(
10297
+ this.connections.length === state.connectionConstraints.length,
10298
+ "Plan state mismatch: connectionConstraints"
10299
+ );
10300
+ }
10301
+ /**
10302
+ * Restore connection pinned flags, limits, and constraint maps.
10303
+ */
10304
+ #restoreConnections(state) {
10305
+ for (let i = 0; i < this.connections.length; i++) {
10306
+ this.connections[i].limit = state.connections[i].limit;
10307
+ this.connections[i].restoreConstraints(state.connectionConstraints[i]);
10308
+ }
10309
+ }
10310
+ /**
10311
+ * Restore join types and pinned flags.
10312
+ */
10313
+ #restoreJoins(state) {
10314
+ for (let i = 0; i < this.joins.length; i++) {
10315
+ const join = this.joins[i];
10316
+ const targetState = state.joins[i];
10317
+ join.reset();
10318
+ if (targetState.type === "flipped") {
10319
+ join.flip();
10320
+ }
10321
+ }
10322
+ }
10323
+ /**
10324
+ * Restore FanOut and FanIn types.
10325
+ */
10326
+ #restoreFanNodes(state) {
10327
+ for (let i = 0; i < this.fanOuts.length; i++) {
10328
+ const fo = this.fanOuts[i];
10329
+ const targetType = state.fanOuts[i].type;
10330
+ if (targetType === "UFO" && fo.type === "FO") {
10331
+ fo.convertToUFO();
10332
+ }
10333
+ }
10334
+ for (let i = 0; i < this.fanIns.length; i++) {
10335
+ const fi = this.fanIns[i];
10336
+ const targetType = state.fanIns[i].type;
10337
+ if (targetType === "UFI" && fi.type === "FI") {
10338
+ fi.convertToUFI();
10339
+ }
10340
+ }
10341
+ }
10342
+ /**
10343
+ * Main planning algorithm using exhaustive join flip enumeration.
10344
+ *
10345
+ * Enumerates all possible flip patterns for flippable joins (2^n for n flippable joins).
10346
+ * Each pattern represents a different query execution plan. We evaluate the cost of each
10347
+ * plan and select the one with the lowest cost.
10348
+ *
10349
+ * Connections are used only for cost estimation - the flip patterns determine the plan.
10350
+ * FanOut/FanIn states (FO/UFO and FI/UFI) are automatically derived from join flip states.
10351
+ *
10352
+ * @param planDebugger - Optional debugger to receive structured events during planning
10353
+ */
10354
+ plan(planDebugger) {
10355
+ const flippableJoins = this.joins.filter((j) => j.isFlippable());
10356
+ if (flippableJoins.length > MAX_FLIPPABLE_JOINS) {
10357
+ throw new Error(
10358
+ `Query has ${flippableJoins.length} EXISTS checks in a single RELATED call (or in the top level query), which would require ${2 ** flippableJoins.length} plan evaluations. This may be very slow. Consider simplifying the query or increasing MAX_FLIPPABLE_JOINS (currently set to ${MAX_FLIPPABLE_JOINS}).`
10359
+ );
10360
+ }
10361
+ const fofiCache = buildFOFICache(this);
10362
+ const numPatterns = 2 ** flippableJoins.length;
10363
+ let bestCost = Infinity;
10364
+ let bestPlan = void 0;
10365
+ let bestAttemptNumber = -1;
10366
+ const forcePattern = void 0;
10367
+ for (let pattern = 0; pattern < numPatterns; pattern++) {
10368
+ if (forcePattern !== void 0 && pattern !== forcePattern) {
10369
+ continue;
10370
+ }
10371
+ this.resetPlanningState();
10372
+ if (planDebugger) {
10373
+ planDebugger.log({
10374
+ type: "attempt-start",
10375
+ attemptNumber: pattern,
10376
+ totalAttempts: numPatterns
10377
+ });
10378
+ }
10379
+ try {
10380
+ for (let i = 0; i < flippableJoins.length; i++) {
10381
+ if (pattern & 1 << i) {
10382
+ flippableJoins[i].flip();
10383
+ }
10384
+ }
10385
+ checkAndConvertFOFI(fofiCache);
10386
+ propagateUnlimitForFlippedJoins(this);
10387
+ this.propagateConstraints(planDebugger);
10388
+ if (planDebugger) {
10389
+ planDebugger.log({
10390
+ type: "constraints-propagated",
10391
+ attemptNumber: pattern,
10392
+ connectionConstraints: this.connections.map((c) => ({
10393
+ connection: c.name,
10394
+ constraints: c.getConstraintsForDebug(),
10395
+ constraintCosts: c.getConstraintCostsForDebug()
10396
+ }))
10397
+ });
10398
+ }
10399
+ const totalCost = this.getTotalCost(planDebugger);
10400
+ if (planDebugger) {
10401
+ planDebugger.log({
10402
+ type: "plan-complete",
10403
+ attemptNumber: pattern,
10404
+ totalCost,
10405
+ flipPattern: pattern,
10406
+ // Bitmask of which joins are flipped
10407
+ // TODO: we'll need a different way to collect these
10408
+ // nodeCosts: this.#collectNodeCosts(),
10409
+ joinStates: this.joins.map((j) => {
10410
+ const info = j.getDebugInfo();
10411
+ return {
10412
+ join: info.name,
10413
+ type: info.type
10414
+ };
10415
+ })
10416
+ });
10417
+ }
10418
+ if (totalCost < bestCost) {
10419
+ bestCost = totalCost;
10420
+ bestPlan = this.capturePlanningSnapshot();
10421
+ bestAttemptNumber = pattern;
10422
+ }
10423
+ } catch (e) {
10424
+ if (planDebugger) {
10425
+ planDebugger.log({
10426
+ type: "plan-failed",
10427
+ attemptNumber: pattern,
10428
+ reason: `Flip pattern ${pattern.toString(2)} failed: ${e instanceof Error ? e.message : String(e)}`
10429
+ });
10430
+ }
10431
+ continue;
10432
+ }
10433
+ }
10434
+ if (bestPlan) {
10435
+ this.restorePlanningSnapshot(bestPlan);
10436
+ this.propagateConstraints(planDebugger);
10437
+ if (planDebugger) {
10438
+ planDebugger.log({
10439
+ type: "best-plan-selected",
10440
+ bestAttemptNumber,
10441
+ totalCost: bestCost,
10442
+ flipPattern: bestAttemptNumber,
10443
+ // The best attempt number is also the flip pattern
10444
+ joinStates: this.joins.map((j) => ({
10445
+ join: j.getName(),
10446
+ type: j.type
10447
+ }))
10448
+ });
10449
+ }
10450
+ } else {
10451
+ throw new Error(
10452
+ "No valid query plan found. This should not happen - check query structure."
10453
+ );
10454
+ }
10455
+ }
10456
+ };
10457
+ function buildFOFICache(graph) {
10458
+ const cache = /* @__PURE__ */ new Map();
10459
+ for (const fo of graph.fanOuts) {
10460
+ const info = findFIAndJoins(fo);
10461
+ cache.set(fo, info);
10462
+ }
10463
+ return cache;
10464
+ }
10465
+ function checkAndConvertFOFI(fofiCache) {
10466
+ for (const [fo, info] of fofiCache) {
10467
+ const hasFlippedJoin = info.joinsBetween.some((j) => j.type === "flipped");
10468
+ if (info.fi && hasFlippedJoin) {
10469
+ fo.convertToUFO();
10470
+ info.fi.convertToUFI();
10471
+ }
10472
+ }
10473
+ }
10474
+ function findFIAndJoins(fo) {
10475
+ const joinsBetween = [];
10476
+ let fi = void 0;
10477
+ const queue = [...fo.outputs];
10478
+ const visited = /* @__PURE__ */ new Set();
10479
+ while (queue.length > 0) {
10480
+ const node = must(queue.shift());
10481
+ if (visited.has(node)) continue;
10482
+ visited.add(node);
10483
+ switch (node.kind) {
10484
+ case "join":
10485
+ joinsBetween.push(node);
10486
+ queue.push(node.output);
10487
+ break;
10488
+ case "fan-out":
10489
+ queue.push(...node.outputs);
10490
+ break;
10491
+ case "fan-in":
10492
+ fi = node;
10493
+ break;
10494
+ case "connection":
10495
+ break;
10496
+ case "terminus":
10497
+ break;
10498
+ }
10499
+ }
10500
+ return { fi, joinsBetween };
10501
+ }
10502
+ function propagateUnlimitForFlippedJoins(graph) {
10503
+ for (const join of graph.joins) {
10504
+ if (join.type === "flipped") {
10505
+ join.propagateUnlimit();
10506
+ }
10507
+ }
10508
+ }
10509
+
10510
+ // ../zql/src/planner/planner-join.ts
10511
+ function translateConstraintsForFlippedJoin(incomingConstraint, parentConstraint, childConstraint) {
10512
+ if (!incomingConstraint) return void 0;
10513
+ const parentKeys = Object.keys(parentConstraint);
10514
+ const childKeys = Object.keys(childConstraint);
10515
+ const translated = {};
10516
+ for (const [key, value] of Object.entries(incomingConstraint)) {
10517
+ const index = parentKeys.indexOf(key);
10518
+ if (index !== -1) {
10519
+ translated[childKeys[index]] = value;
10520
+ }
10521
+ }
10522
+ return Object.keys(translated).length > 0 ? translated : void 0;
10523
+ }
10524
+ var PlannerJoin = class {
10525
+ kind = "join";
10526
+ #parent;
10527
+ #child;
10528
+ #parentConstraint;
10529
+ #childConstraint;
10530
+ #flippable;
10531
+ planId;
10532
+ #output;
10533
+ // Set once during graph construction
10534
+ // Reset between planning attempts
10535
+ #type;
10536
+ constructor(parent, child, parentConstraint, childConstraint, flippable, planId) {
10537
+ this.#type = "semi";
10538
+ this.#parent = parent;
10539
+ this.#child = child;
10540
+ this.#childConstraint = childConstraint;
10541
+ this.#parentConstraint = parentConstraint;
10542
+ this.#flippable = flippable;
10543
+ this.planId = planId;
10544
+ }
10545
+ setOutput(node) {
10546
+ this.#output = node;
10547
+ }
10548
+ get output() {
10549
+ assert(this.#output !== void 0, "Output not set");
10550
+ return this.#output;
10551
+ }
10552
+ closestJoinOrSource() {
10553
+ return "join";
10554
+ }
10555
+ flipIfNeeded(input) {
10556
+ if (input === this.#child) {
10557
+ this.flip();
10558
+ } else {
10559
+ assert(
10560
+ input === this.#parent,
10561
+ "Can only flip a join from one of its inputs"
10562
+ );
10563
+ }
10564
+ }
10565
+ flip() {
10566
+ assert(this.#type === "semi", "Can only flip a semi-join");
10567
+ if (!this.#flippable) {
10568
+ throw new UnflippableJoinError(
10569
+ "Cannot flip a non-flippable join (e.g., NOT EXISTS)"
10570
+ );
10571
+ }
10572
+ this.#type = "flipped";
10573
+ }
10574
+ get type() {
10575
+ return this.#type;
10576
+ }
10577
+ isFlippable() {
10578
+ return this.#flippable;
10579
+ }
10580
+ /**
10581
+ * Propagate unlimiting when this join is flipped.
10582
+ * When a join is flipped:
10583
+ * 1. Child becomes outer loop → produces all rows (unlimited)
10584
+ * 2. Parent is fetched once per child row → effectively unlimited
10585
+ *
10586
+ * Example: If child produces 896 rows, parent is fetched 896 times.
10587
+ * Even if each fetch returns 1 row, parent produces 896 total rows.
10588
+ *
10589
+ * Propagation rules:
10590
+ * - Connection: call unlimit()
10591
+ * - Semi-join: continue to parent (outer loop)
10592
+ * - Flipped join: stop (already unlimited when it was flipped)
10593
+ * - Fan-out/Fan-in: propagate to all inputs
10594
+ */
10595
+ propagateUnlimit() {
10596
+ assert(this.#type === "flipped", "Can only unlimit a flipped join");
10597
+ this.#parent.propagateUnlimitFromFlippedJoin();
10598
+ this.#child.propagateUnlimitFromFlippedJoin();
10599
+ }
10600
+ /**
10601
+ * Called when a parent join is flipped and this join is part of its child subgraph.
10602
+ * - Semi-join: continue propagation to parent (the outer loop)
10603
+ * - Flipped join: stop propagation (already unlimited when it was flipped)
10604
+ */
10605
+ propagateUnlimitFromFlippedJoin() {
10606
+ if (this.#type === "semi") {
10607
+ this.#parent.propagateUnlimitFromFlippedJoin();
10608
+ }
10609
+ }
10610
+ propagateConstraints(branchPattern, constraint, from, planDebugger) {
10611
+ planDebugger?.log({
10612
+ type: "node-constraint",
10613
+ nodeType: "join",
10614
+ node: this.getName(),
10615
+ branchPattern,
10616
+ constraint,
10617
+ from: from ? getNodeName(from) : "unknown"
10618
+ });
10619
+ if (this.#type === "semi") {
10620
+ this.#child.propagateConstraints(
10621
+ branchPattern,
10622
+ this.#childConstraint,
10623
+ this,
10624
+ planDebugger
10625
+ );
10626
+ this.#parent.propagateConstraints(
10627
+ branchPattern,
10628
+ constraint,
10629
+ this,
10630
+ planDebugger
10631
+ );
10632
+ } else if (this.#type === "flipped") {
10633
+ const translatedConstraint = translateConstraintsForFlippedJoin(
10634
+ constraint,
10635
+ this.#parentConstraint,
10636
+ this.#childConstraint
10637
+ );
10638
+ this.#child.propagateConstraints(
10639
+ branchPattern,
10640
+ translatedConstraint,
10641
+ this,
10642
+ planDebugger
10643
+ );
10644
+ this.#parent.propagateConstraints(
10645
+ branchPattern,
10646
+ mergeConstraints(constraint, this.#parentConstraint),
10647
+ this,
10648
+ planDebugger
10649
+ );
10650
+ }
9232
10651
  }
9233
- fanOutStartedPushing() {
9234
- assert(this.#fanOutPushStarted === false);
9235
- this.#fanOutPushStarted = true;
10652
+ reset() {
10653
+ this.#type = "semi";
9236
10654
  }
9237
- fanOutDonePushing(fanOutChangeType) {
9238
- assert(this.#fanOutPushStarted);
9239
- this.#fanOutPushStarted = false;
9240
- if (this.#inputs.length === 0) {
9241
- return;
9242
- }
9243
- if (this.#accumulatedPushes.length === 0) {
9244
- return;
9245
- }
9246
- pushAccumulatedChanges(
9247
- this.#accumulatedPushes,
9248
- this.#output,
9249
- this,
9250
- fanOutChangeType,
9251
- mergeRelationships,
9252
- makeAddEmptyRelationships(this.#schema)
10655
+ estimateCost(downstreamChildSelectivity, branchPattern, planDebugger) {
10656
+ const child = this.#child.estimateCost(1, branchPattern, planDebugger);
10657
+ const parent = this.#parent.estimateCost(
10658
+ // Selectivity flows up the graph from child to parent
10659
+ // so we can determine the total selectivity of all ANDed exists checks.
10660
+ child.selectivity * downstreamChildSelectivity,
10661
+ branchPattern,
10662
+ planDebugger
9253
10663
  );
10664
+ let costEstimate;
10665
+ if (this.type === "semi") {
10666
+ costEstimate = {
10667
+ startupCost: parent.startupCost,
10668
+ scanEst: parent.limit === void 0 ? parent.returnedRows : Math.min(
10669
+ parent.returnedRows,
10670
+ parent.limit / downstreamChildSelectivity
10671
+ ),
10672
+ cost: parent.cost + parent.scanEst * (child.startupCost + child.scanEst),
10673
+ returnedRows: parent.returnedRows * child.selectivity,
10674
+ selectivity: child.selectivity * parent.selectivity,
10675
+ limit: parent.limit
10676
+ };
10677
+ } else {
10678
+ costEstimate = {
10679
+ startupCost: child.startupCost,
10680
+ scanEst: parent.limit === void 0 ? parent.returnedRows : Math.min(
10681
+ parent.returnedRows * child.returnedRows,
10682
+ parent.limit / downstreamChildSelectivity
10683
+ ),
10684
+ cost: child.cost + child.scanEst * (parent.startupCost + parent.scanEst),
10685
+ returnedRows: parent.returnedRows * child.returnedRows * child.selectivity,
10686
+ selectivity: parent.selectivity * child.selectivity,
10687
+ limit: parent.limit
10688
+ };
10689
+ }
10690
+ planDebugger?.log({
10691
+ type: "node-cost",
10692
+ nodeType: "join",
10693
+ node: this.getName(),
10694
+ branchPattern,
10695
+ downstreamChildSelectivity,
10696
+ costEstimate,
10697
+ joinType: this.#type
10698
+ });
10699
+ return costEstimate;
9254
10700
  }
9255
- setOutput(output) {
9256
- this.#output = output;
10701
+ /**
10702
+ * Get a human-readable name for this join for debugging.
10703
+ * Format: "parentName ⋈ childName"
10704
+ */
10705
+ getName() {
10706
+ const parentName = getNodeName(this.#parent);
10707
+ const childName = getNodeName(this.#child);
10708
+ return `${parentName} \u22C8 ${childName}`;
10709
+ }
10710
+ /**
10711
+ * Get debug information about this join's state.
10712
+ */
10713
+ getDebugInfo() {
10714
+ return {
10715
+ name: this.getName(),
10716
+ type: this.#type,
10717
+ planId: this.planId
10718
+ };
10719
+ }
10720
+ };
10721
+ var UnflippableJoinError = class extends Error {
10722
+ constructor(message) {
10723
+ super(message);
10724
+ this.name = "UnflippableJoinError";
9257
10725
  }
9258
10726
  };
10727
+ function getNodeName(node) {
10728
+ switch (node.kind) {
10729
+ case "connection":
10730
+ return node.name;
10731
+ case "join":
10732
+ return node.getName();
10733
+ case "fan-out":
10734
+ return "FO";
10735
+ case "fan-in":
10736
+ return "FI";
10737
+ case "terminus":
10738
+ return "terminus";
10739
+ }
10740
+ }
9259
10741
 
9260
- // ../zql/src/ivm/union-fan-out.ts
9261
- var UnionFanOut = class {
9262
- #destroyCount = 0;
9263
- #unionFanIn;
10742
+ // ../zql/src/planner/planner-terminus.ts
10743
+ var PlannerTerminus = class {
10744
+ kind = "terminus";
9264
10745
  #input;
9265
- #outputs = [];
9266
10746
  constructor(input) {
9267
10747
  this.#input = input;
9268
- input.setOutput(this);
9269
10748
  }
9270
- setFanIn(fanIn) {
9271
- assert(!this.#unionFanIn, "FanIn already set for this FanOut");
9272
- this.#unionFanIn = fanIn;
10749
+ get pinned() {
10750
+ return true;
9273
10751
  }
9274
- push(change) {
9275
- must(this.#unionFanIn).fanOutStartedPushing();
9276
- for (const output of this.#outputs) {
9277
- output.push(change, this);
10752
+ closestJoinOrSource() {
10753
+ return this.#input.closestJoinOrSource();
10754
+ }
10755
+ propagateConstraints(planDebugger) {
10756
+ this.#input.propagateConstraints([], void 0, this, planDebugger);
10757
+ }
10758
+ estimateCost(planDebugger) {
10759
+ return this.#input.estimateCost(1, [], planDebugger);
10760
+ }
10761
+ /**
10762
+ * Propagate unlimiting when a parent join is flipped.
10763
+ * Terminus doesn't participate in unlimiting.
10764
+ */
10765
+ propagateUnlimitFromFlippedJoin() {
10766
+ }
10767
+ };
10768
+
10769
+ // ../zql/src/planner/planner-builder.ts
10770
+ function wireOutput(from, to) {
10771
+ switch (from.kind) {
10772
+ case "connection":
10773
+ case "join":
10774
+ case "fan-in":
10775
+ from.setOutput(to);
10776
+ break;
10777
+ case "fan-out":
10778
+ from.addOutput(to);
10779
+ break;
10780
+ case "terminus":
10781
+ assert(false, "Terminus nodes cannot have outputs");
10782
+ }
10783
+ }
10784
+ function buildPlanGraph(ast, model, isRoot, baseConstraints) {
10785
+ const graph = new PlannerGraph();
10786
+ let nextPlanId = 0;
10787
+ const source = graph.addSource(ast.table, model);
10788
+ const connection = source.connect(
10789
+ ast.orderBy ?? [],
10790
+ ast.where,
10791
+ isRoot,
10792
+ baseConstraints,
10793
+ ast.limit
10794
+ );
10795
+ graph.connections.push(connection);
10796
+ let end = connection;
10797
+ if (ast.where) {
10798
+ end = processCondition(
10799
+ ast.where,
10800
+ end,
10801
+ graph,
10802
+ model,
10803
+ ast.table,
10804
+ () => nextPlanId++
10805
+ );
10806
+ }
10807
+ const terminus = new PlannerTerminus(end);
10808
+ wireOutput(end, terminus);
10809
+ graph.setTerminus(terminus);
10810
+ const subPlans = {};
10811
+ if (ast.related) {
10812
+ for (const csq of ast.related) {
10813
+ const alias = must(
10814
+ csq.subquery.alias,
10815
+ "Related subquery must have alias"
10816
+ );
10817
+ const childConstraints = extractConstraint(
10818
+ csq.correlation.childField,
10819
+ csq.subquery.table
10820
+ );
10821
+ subPlans[alias] = buildPlanGraph(
10822
+ csq.subquery,
10823
+ model,
10824
+ true,
10825
+ childConstraints
10826
+ );
9278
10827
  }
9279
- must(this.#unionFanIn).fanOutDonePushing(change.type);
9280
10828
  }
9281
- setOutput(output) {
9282
- this.#outputs.push(output);
10829
+ return { plan: graph, subPlans };
10830
+ }
10831
+ function processCondition(condition, input, graph, model, parentTable, getPlanId) {
10832
+ switch (condition.type) {
10833
+ case "simple":
10834
+ return input;
10835
+ case "and":
10836
+ return processAnd(condition, input, graph, model, parentTable, getPlanId);
10837
+ case "or":
10838
+ return processOr(condition, input, graph, model, parentTable, getPlanId);
10839
+ case "correlatedSubquery":
10840
+ return processCorrelatedSubquery(
10841
+ condition,
10842
+ input,
10843
+ graph,
10844
+ model,
10845
+ parentTable,
10846
+ getPlanId
10847
+ );
9283
10848
  }
9284
- getSchema() {
9285
- return this.#input.getSchema();
10849
+ }
10850
+ function processAnd(condition, input, graph, model, parentTable, getPlanId) {
10851
+ let end = input;
10852
+ for (const subCondition of condition.conditions) {
10853
+ end = processCondition(
10854
+ subCondition,
10855
+ end,
10856
+ graph,
10857
+ model,
10858
+ parentTable,
10859
+ getPlanId
10860
+ );
9286
10861
  }
9287
- fetch(req) {
9288
- return this.#input.fetch(req);
10862
+ return end;
10863
+ }
10864
+ function processOr(condition, input, graph, model, parentTable, getPlanId) {
10865
+ const subqueryConditions = condition.conditions.filter(
10866
+ (c) => c.type === "correlatedSubquery" || hasCorrelatedSubquery(c)
10867
+ );
10868
+ if (subqueryConditions.length === 0) {
10869
+ return input;
9289
10870
  }
9290
- cleanup(_req) {
9291
- return [];
10871
+ const fanOut = new PlannerFanOut(input);
10872
+ graph.fanOuts.push(fanOut);
10873
+ wireOutput(input, fanOut);
10874
+ const branches = [];
10875
+ for (const subCondition of subqueryConditions) {
10876
+ const branch = processCondition(
10877
+ subCondition,
10878
+ fanOut,
10879
+ graph,
10880
+ model,
10881
+ parentTable,
10882
+ getPlanId
10883
+ );
10884
+ branches.push(branch);
10885
+ fanOut.addOutput(branch);
9292
10886
  }
9293
- destroy() {
9294
- if (this.#destroyCount < this.#outputs.length) {
9295
- ++this.#destroyCount;
9296
- if (this.#destroyCount === this.#outputs.length) {
9297
- this.#input.destroy();
10887
+ const fanIn = new PlannerFanIn(branches);
10888
+ graph.fanIns.push(fanIn);
10889
+ for (const branch of branches) {
10890
+ wireOutput(branch, fanIn);
10891
+ }
10892
+ return fanIn;
10893
+ }
10894
+ function processCorrelatedSubquery(condition, input, graph, model, parentTable, getPlanId) {
10895
+ const { related } = condition;
10896
+ const childTable = related.subquery.table;
10897
+ const childSource = graph.hasSource(childTable) ? graph.getSource(childTable) : graph.addSource(childTable, model);
10898
+ const childConnection = childSource.connect(
10899
+ related.subquery.orderBy ?? [],
10900
+ related.subquery.where,
10901
+ false,
10902
+ void 0,
10903
+ // no base constraints for EXISTS/NOT EXISTS
10904
+ condition.op === "EXISTS" ? 1 : void 0
10905
+ );
10906
+ graph.connections.push(childConnection);
10907
+ let childEnd = childConnection;
10908
+ if (related.subquery.where) {
10909
+ childEnd = processCondition(
10910
+ related.subquery.where,
10911
+ childEnd,
10912
+ graph,
10913
+ model,
10914
+ childTable,
10915
+ getPlanId
10916
+ );
10917
+ }
10918
+ const parentConstraint = extractConstraint(
10919
+ related.correlation.parentField,
10920
+ parentTable
10921
+ );
10922
+ const childConstraint = extractConstraint(
10923
+ related.correlation.childField,
10924
+ childTable
10925
+ );
10926
+ const planId = getPlanId();
10927
+ condition[planIdSymbol] = planId;
10928
+ const join = new PlannerJoin(
10929
+ input,
10930
+ childEnd,
10931
+ parentConstraint,
10932
+ childConstraint,
10933
+ condition.op !== "NOT EXISTS",
10934
+ planId
10935
+ );
10936
+ graph.joins.push(join);
10937
+ wireOutput(input, join);
10938
+ wireOutput(childEnd, join);
10939
+ return join;
10940
+ }
10941
+ function hasCorrelatedSubquery(condition) {
10942
+ if (condition.type === "correlatedSubquery") {
10943
+ return true;
10944
+ }
10945
+ if (condition.type === "and" || condition.type === "or") {
10946
+ return condition.conditions.some(hasCorrelatedSubquery);
10947
+ }
10948
+ return false;
10949
+ }
10950
+ function extractConstraint(fields, _tableName) {
10951
+ return Object.fromEntries(fields.map((field) => [field, void 0]));
10952
+ }
10953
+ function planRecursively(plans, planDebugger) {
10954
+ for (const subPlan of Object.values(plans.subPlans)) {
10955
+ planRecursively(subPlan, planDebugger);
10956
+ }
10957
+ plans.plan.plan(planDebugger);
10958
+ }
10959
+ function planQuery(ast, model, planDebugger) {
10960
+ const plans = buildPlanGraph(ast, model, true);
10961
+ planRecursively(plans, planDebugger);
10962
+ return applyPlansToAST(ast, plans);
10963
+ }
10964
+ function applyToCondition(condition, flippedIds) {
10965
+ if (condition.type === "simple") {
10966
+ return condition;
10967
+ }
10968
+ if (condition.type === "correlatedSubquery") {
10969
+ const planId = condition[planIdSymbol];
10970
+ const shouldFlip = planId !== void 0 && flippedIds.has(planId);
10971
+ return {
10972
+ ...condition,
10973
+ flip: shouldFlip ? true : condition.flip,
10974
+ related: {
10975
+ ...condition.related,
10976
+ subquery: {
10977
+ ...condition.related.subquery,
10978
+ where: condition.related.subquery.where ? applyToCondition(condition.related.subquery.where, flippedIds) : void 0
10979
+ }
9298
10980
  }
9299
- } else {
9300
- throw new Error("FanOut already destroyed once for each output");
10981
+ };
10982
+ }
10983
+ return {
10984
+ ...condition,
10985
+ conditions: condition.conditions.map((c) => applyToCondition(c, flippedIds))
10986
+ };
10987
+ }
10988
+ function applyPlansToAST(ast, plans) {
10989
+ const flippedIds = /* @__PURE__ */ new Set();
10990
+ for (const join of plans.plan.joins) {
10991
+ if (join.type === "flipped" && join.planId !== void 0) {
10992
+ flippedIds.add(join.planId);
9301
10993
  }
9302
10994
  }
9303
- };
10995
+ return {
10996
+ ...ast,
10997
+ where: ast.where ? applyToCondition(ast.where, flippedIds) : void 0,
10998
+ related: ast.related?.map((csq) => {
10999
+ const alias = must(
11000
+ csq.subquery.alias,
11001
+ "Related subquery must have alias"
11002
+ );
11003
+ const subPlan = plans.subPlans[alias];
11004
+ return {
11005
+ ...csq,
11006
+ subquery: subPlan ? applyPlansToAST(csq.subquery, subPlan) : csq.subquery
11007
+ };
11008
+ })
11009
+ };
11010
+ }
9304
11011
 
9305
11012
  // ../zql/src/query/expression.ts
9306
11013
  var ExpressionBuilder = class {
@@ -9461,8 +11168,8 @@ var negateOperatorMap = {
9461
11168
  function negateOperator(op) {
9462
11169
  return must(negateOperatorMap[op]);
9463
11170
  }
9464
- function filterUndefined(array9) {
9465
- return array9.filter((e) => e !== void 0);
11171
+ function filterUndefined(array10) {
11172
+ return array10.filter((e) => e !== void 0);
9466
11173
  }
9467
11174
  function filterTrue(conditions) {
9468
11175
  return conditions.filter((c) => !isAlwaysTrue(c));
@@ -9666,8 +11373,11 @@ function transformFilters(filters) {
9666
11373
  }
9667
11374
 
9668
11375
  // ../zql/src/builder/builder.ts
9669
- function buildPipeline(ast, delegate, queryID) {
11376
+ function buildPipeline(ast, delegate, queryID, costModel) {
9670
11377
  ast = delegate.mapAst ? delegate.mapAst(ast) : ast;
11378
+ if (costModel) {
11379
+ ast = planQuery(ast, costModel);
11380
+ }
9671
11381
  return buildPipelineInternal(ast, delegate, queryID, "");
9672
11382
  }
9673
11383
  var EXISTS_LIMIT = 3;
@@ -11097,22 +12807,45 @@ var MockClientLockManager = class {
11097
12807
  // ../zero-client/src/client/connection-manager.ts
11098
12808
  import { resolver as resolver9 } from "@rocicorp/resolver";
11099
12809
 
11100
- // ../zero-client/src/client/connection-status-enum.ts
11101
- var connection_status_enum_exports = {};
11102
- __export(connection_status_enum_exports, {
11103
- Closed: () => Closed,
11104
- Connected: () => Connected,
11105
- Connecting: () => Connecting,
11106
- Disconnected: () => Disconnected
11107
- });
11108
- var Disconnected = "disconnected";
11109
- var Connecting = "connecting";
11110
- var Connected = "connected";
11111
- var Closed = "closed";
12810
+ // ../shared/src/subscribable.ts
12811
+ var Subscribable = class {
12812
+ _listeners = /* @__PURE__ */ new Set();
12813
+ /**
12814
+ * Subscribe to the subscribable.
12815
+ *
12816
+ * @param listener - The listener to subscribe to.
12817
+ * @returns A function to unsubscribe from the subscribable.
12818
+ */
12819
+ subscribe = (listener) => {
12820
+ this._listeners.add(listener);
12821
+ return () => {
12822
+ this._listeners.delete(listener);
12823
+ };
12824
+ };
12825
+ /**
12826
+ * Notify all listeners.
12827
+ *
12828
+ * @param update - The update to notify listeners with.
12829
+ */
12830
+ notify = (update) => {
12831
+ this._listeners.forEach((listener) => listener(update));
12832
+ };
12833
+ hasListeners = () => this._listeners.size > 0;
12834
+ /**
12835
+ * Unsubscribe all listeners.
12836
+ */
12837
+ cleanup = () => {
12838
+ this._listeners.clear();
12839
+ };
12840
+ };
11112
12841
 
11113
12842
  // ../zero-client/src/client/connection-manager.ts
11114
12843
  var DEFAULT_TIMEOUT_CHECK_INTERVAL_MS = 1e3;
11115
- var ConnectionManager = class extends Subscribable {
12844
+ var TERMINAL_STATES = [
12845
+ connection_status_enum_exports.NeedsAuth,
12846
+ connection_status_enum_exports.Error
12847
+ ];
12848
+ var ConnectionManager = class _ConnectionManager extends Subscribable {
11116
12849
  #state;
11117
12850
  /**
11118
12851
  * The timestamp when we first started trying to connect.
@@ -11160,14 +12893,34 @@ var ConnectionManager = class extends Subscribable {
11160
12893
  is(status) {
11161
12894
  return this.#state.name === status;
11162
12895
  }
12896
+ /**
12897
+ * Returns true if the current state is a terminal state
12898
+ * that can be recovered from by calling connect().
12899
+ */
12900
+ isInTerminalState() {
12901
+ return _ConnectionManager.isTerminalState(this.#state);
12902
+ }
12903
+ /**
12904
+ * Returns true if the given status is a terminal state
12905
+ * that can be recovered from by calling connect().
12906
+ */
12907
+ static isTerminalState(state) {
12908
+ return TERMINAL_STATES.includes(
12909
+ state.name
12910
+ );
12911
+ }
11163
12912
  /**
11164
12913
  * Returns true if the run loop should continue.
11165
- * The run loop continues in disconnected, connecting, and connected states.
11166
- * It stops in closed state.
12914
+ * The run loop continues in all states except closed.
12915
+ * In needs-auth and error states, the run loop pauses and waits for connect() to be called.
11167
12916
  */
11168
12917
  shouldContinueRunLoop() {
11169
12918
  return this.#state.name !== connection_status_enum_exports.Closed;
11170
12919
  }
12920
+ /**
12921
+ * Waits for the next state change.
12922
+ * @returns A promise that resolves when the next state change occurs.
12923
+ */
11171
12924
  waitForStateChange() {
11172
12925
  return this.#nextStatePromise();
11173
12926
  }
@@ -11217,42 +12970,88 @@ var ConnectionManager = class extends Subscribable {
11217
12970
  *
11218
12971
  * @returns An object containing a promise that resolves on the next state change.
11219
12972
  */
11220
- connected() {
12973
+ connected() {
12974
+ if (this.#state.name === connection_status_enum_exports.Closed) {
12975
+ return { nextStatePromise: this.#nextStatePromise() };
12976
+ }
12977
+ if (this.#state.name === connection_status_enum_exports.Connected) {
12978
+ return { nextStatePromise: this.#nextStatePromise() };
12979
+ }
12980
+ this.#connectingStartedAt = void 0;
12981
+ this.#maybeStopTimeoutInterval();
12982
+ this.#state = {
12983
+ name: connection_status_enum_exports.Connected
12984
+ };
12985
+ const nextStatePromise = this.#publishStateAndGetPromise();
12986
+ return { nextStatePromise };
12987
+ }
12988
+ /**
12989
+ * Transition to disconnected state.
12990
+ * This is called when the 5-minute timeout expires, or when we're intentionally
12991
+ * disconnecting due to an error (this will eventually be a separate state, error).
12992
+ * The run loop will continue trying to reconnect.
12993
+ *
12994
+ * @returns An object containing a promise that resolves on the next state change.
12995
+ */
12996
+ disconnected(reason) {
12997
+ if (this.#state.name === connection_status_enum_exports.Closed) {
12998
+ return { nextStatePromise: this.#nextStatePromise() };
12999
+ }
13000
+ if (this.#state.name === connection_status_enum_exports.Disconnected) {
13001
+ return { nextStatePromise: this.#nextStatePromise() };
13002
+ }
13003
+ if (this.#state.name === connection_status_enum_exports.Connected) {
13004
+ this.#connectingStartedAt = void 0;
13005
+ }
13006
+ this.#maybeStopTimeoutInterval();
13007
+ this.#state = {
13008
+ name: connection_status_enum_exports.Disconnected,
13009
+ reason
13010
+ };
13011
+ const nextStatePromise = this.#publishStateAndGetPromise();
13012
+ return { nextStatePromise };
13013
+ }
13014
+ /**
13015
+ * Transition to needs-auth state.
13016
+ * This pauses the run loop until connect() is called with new credentials.
13017
+ * Resets the retry window and attempt counter.
13018
+ *
13019
+ * @returns An object containing a promise that resolves on the next state change.
13020
+ */
13021
+ needsAuth(reason) {
11221
13022
  if (this.#state.name === connection_status_enum_exports.Closed) {
11222
13023
  return { nextStatePromise: this.#nextStatePromise() };
11223
13024
  }
11224
- if (this.#state.name === connection_status_enum_exports.Connected) {
13025
+ if (this.#state.name === connection_status_enum_exports.NeedsAuth) {
11225
13026
  return { nextStatePromise: this.#nextStatePromise() };
11226
13027
  }
11227
13028
  this.#connectingStartedAt = void 0;
11228
13029
  this.#maybeStopTimeoutInterval();
11229
13030
  this.#state = {
11230
- name: connection_status_enum_exports.Connected
13031
+ name: connection_status_enum_exports.NeedsAuth,
13032
+ reason
11231
13033
  };
11232
13034
  const nextStatePromise = this.#publishStateAndGetPromise();
11233
13035
  return { nextStatePromise };
11234
13036
  }
11235
13037
  /**
11236
- * Transition to disconnected state.
11237
- * This is called when the 5-minute timeout expires, or when we're intentionally
11238
- * disconnecting due to an error (this will eventually be a separate state, error).
11239
- * The run loop will continue trying to reconnect.
13038
+ * Transition to error state.
13039
+ * This pauses the run loop until connect() is called.
13040
+ * Resets the retry window and attempt counter.
11240
13041
  *
11241
13042
  * @returns An object containing a promise that resolves on the next state change.
11242
13043
  */
11243
- disconnected(reason) {
13044
+ error(reason) {
11244
13045
  if (this.#state.name === connection_status_enum_exports.Closed) {
11245
13046
  return { nextStatePromise: this.#nextStatePromise() };
11246
13047
  }
11247
- if (this.#state.name === connection_status_enum_exports.Disconnected) {
13048
+ if (this.#state.name === connection_status_enum_exports.Error) {
11248
13049
  return { nextStatePromise: this.#nextStatePromise() };
11249
13050
  }
11250
- if (this.#state.name === connection_status_enum_exports.Connected) {
11251
- this.#connectingStartedAt = void 0;
11252
- }
13051
+ this.#connectingStartedAt = void 0;
11253
13052
  this.#maybeStopTimeoutInterval();
11254
13053
  this.#state = {
11255
- name: connection_status_enum_exports.Disconnected,
13054
+ name: connection_status_enum_exports.Error,
11256
13055
  reason
11257
13056
  };
11258
13057
  const nextStatePromise = this.#publishStateAndGetPromise();
@@ -11261,27 +13060,30 @@ var ConnectionManager = class extends Subscribable {
11261
13060
  /**
11262
13061
  * Transition to closed state.
11263
13062
  * This is terminal - no further transitions are allowed.
11264
- *
11265
- * @returns An object containing a promise that resolves on the next state change.
11266
13063
  */
11267
13064
  closed() {
11268
13065
  if (this.#state.name === connection_status_enum_exports.Closed) {
11269
- return { nextStatePromise: this.#nextStatePromise() };
13066
+ return;
11270
13067
  }
11271
13068
  this.#connectingStartedAt = void 0;
11272
13069
  this.#maybeStopTimeoutInterval();
11273
13070
  this.#state = {
11274
- name: connection_status_enum_exports.Closed
13071
+ name: connection_status_enum_exports.Closed,
13072
+ reason: new ClientError({
13073
+ kind: client_error_kind_enum_exports.ClientClosed,
13074
+ message: "Zero was explicitly closed by calling zero.close()"
13075
+ })
11275
13076
  };
11276
- const nextStatePromise = this.#publishStateAndGetPromise();
11277
- return { nextStatePromise };
13077
+ this.#publishState();
13078
+ this.cleanup();
13079
+ return;
11278
13080
  }
11279
13081
  cleanup = () => {
11280
13082
  this._listeners.clear();
11281
13083
  this.#resolveNextStateWaiters();
11282
13084
  };
11283
13085
  #resolveNextStateWaiters() {
11284
- this.#stateChangeResolver.resolve();
13086
+ this.#stateChangeResolver.resolve(this.#state);
11285
13087
  this.#stateChangeResolver = resolver9();
11286
13088
  }
11287
13089
  #publishState() {
@@ -11305,7 +13107,12 @@ var ConnectionManager = class extends Subscribable {
11305
13107
  }
11306
13108
  const now = Date.now();
11307
13109
  if (now >= this.#state.disconnectAt) {
11308
- this.disconnected();
13110
+ this.disconnected(
13111
+ new ClientError({
13112
+ kind: client_error_kind_enum_exports.DisconnectTimeout,
13113
+ message: `Zero was unable to connect for ${Math.floor(this.#disconnectTimeoutMs / 1e3)} seconds and was disconnected`
13114
+ })
13115
+ );
11309
13116
  return true;
11310
13117
  }
11311
13118
  return false;
@@ -11326,6 +13133,62 @@ var ConnectionManager = class extends Subscribable {
11326
13133
  this.#timeoutInterval = void 0;
11327
13134
  }
11328
13135
  };
13136
+ var throwIfConnectionError = (state) => {
13137
+ if (ConnectionManager.isTerminalState(state) || state.name === connection_status_enum_exports.Closed || (state.name === connection_status_enum_exports.Connecting || state.name === connection_status_enum_exports.Disconnected) && state.reason) {
13138
+ if (isClientError(state.reason) && (state.reason.kind === client_error_kind_enum_exports.ConnectTimeout || state.reason.kind === client_error_kind_enum_exports.AbruptClose || state.reason.kind === client_error_kind_enum_exports.CleanClose)) {
13139
+ return;
13140
+ }
13141
+ throw state.reason;
13142
+ }
13143
+ };
13144
+
13145
+ // ../zero-client/src/client/connection.ts
13146
+ var ConnectionImpl = class {
13147
+ #connectionManager;
13148
+ #lc;
13149
+ #source;
13150
+ #setAuth;
13151
+ constructor(connectionManager, lc, setAuth) {
13152
+ this.#connectionManager = connectionManager;
13153
+ this.#lc = lc;
13154
+ this.#source = new ConnectionSource(connectionManager);
13155
+ this.#setAuth = setAuth;
13156
+ }
13157
+ get state() {
13158
+ return this.#source;
13159
+ }
13160
+ async connect(opts) {
13161
+ const lc = this.#lc.withContext("connect");
13162
+ if (opts && "auth" in opts) {
13163
+ lc.debug?.("Updating auth credential from connect()");
13164
+ this.#setAuth(opts.auth);
13165
+ }
13166
+ if (!this.#connectionManager.isInTerminalState()) {
13167
+ lc.debug?.(
13168
+ "connect() called but not in a terminal state. Current state:",
13169
+ this.#connectionManager.state.name
13170
+ );
13171
+ return;
13172
+ }
13173
+ lc.info?.(
13174
+ `Resuming connection from state: ${this.#connectionManager.state.name}`
13175
+ );
13176
+ const { nextStatePromise } = this.#connectionManager.connecting();
13177
+ await nextStatePromise;
13178
+ }
13179
+ };
13180
+ var ConnectionSource = class {
13181
+ #connectionManager;
13182
+ constructor(connectionManager) {
13183
+ this.#connectionManager = connectionManager;
13184
+ }
13185
+ get current() {
13186
+ return this.#connectionManager.state;
13187
+ }
13188
+ get subscribe() {
13189
+ return this.#connectionManager.subscribe;
13190
+ }
13191
+ };
11329
13192
 
11330
13193
  // ../zql/src/ivm/memory-storage.ts
11331
13194
  import { compareUTF8 as compareUTF83 } from "compare-utf8";
@@ -13267,7 +15130,7 @@ function makeMessage(message, context, logLevel) {
13267
15130
  }
13268
15131
 
13269
15132
  // ../zero-client/src/client/version.ts
13270
- var version2 = "0.25.0-canary.0";
15133
+ var version2 = "0.25.0-canary.2";
13271
15134
 
13272
15135
  // ../zero-client/src/client/log-options.ts
13273
15136
  var LevelFilterLogSink = class {
@@ -13323,74 +15186,6 @@ function createLogOptions(options, createDatadogLogSink = (options2) => new Data
13323
15186
  };
13324
15187
  }
13325
15188
 
13326
- // ../zero-client/src/client/client-error-kind-enum.ts
13327
- var client_error_kind_enum_exports = {};
13328
- __export(client_error_kind_enum_exports, {
13329
- AbruptClose: () => AbruptClose,
13330
- CleanClose: () => CleanClose,
13331
- ClientClosed: () => ClientClosed,
13332
- ConnectTimeout: () => ConnectTimeout,
13333
- DisconnectTimeout: () => DisconnectTimeout,
13334
- Hidden: () => Hidden,
13335
- NoSocketOrigin: () => NoSocketOrigin,
13336
- PingTimeout: () => PingTimeout,
13337
- UnexpectedBaseCookie: () => UnexpectedBaseCookie
13338
- });
13339
- var AbruptClose = "AbruptClose";
13340
- var CleanClose = "CleanClose";
13341
- var ClientClosed = "ClientClosed";
13342
- var ConnectTimeout = "ConnectTimeout";
13343
- var DisconnectTimeout = "DisconnectTimeout";
13344
- var UnexpectedBaseCookie = "UnexpectedBaseCookie";
13345
- var PingTimeout = "PingTimeout";
13346
- var Hidden = "Hidden";
13347
- var NoSocketOrigin = "NoSocketOrigin";
13348
-
13349
- // ../zero-client/src/client/error.ts
13350
- var BaseError = class extends Error {
13351
- errorBody;
13352
- constructor(errorBody, options) {
13353
- super(errorBody.kind + ": " + errorBody.message, options);
13354
- this.errorBody = errorBody;
13355
- }
13356
- get kind() {
13357
- return this.errorBody.kind;
13358
- }
13359
- };
13360
- var ServerError = class extends BaseError {
13361
- get name() {
13362
- return "ServerError";
13363
- }
13364
- };
13365
- var ClientError = class extends BaseError {
13366
- get name() {
13367
- return "ClientError";
13368
- }
13369
- };
13370
- function isServerError(ex) {
13371
- return ex instanceof ServerError;
13372
- }
13373
- function isAuthError(ex) {
13374
- return isServerError(ex) && isAuthErrorKind(ex.kind);
13375
- }
13376
- function isAuthErrorKind(kind) {
13377
- return kind === error_kind_enum_exports.AuthInvalidated || kind === error_kind_enum_exports.Unauthorized;
13378
- }
13379
- function isBackoffError(ex) {
13380
- if (isServerError(ex)) {
13381
- switch (ex.errorBody.kind) {
13382
- case error_kind_enum_exports.Rebalance:
13383
- case error_kind_enum_exports.Rehome:
13384
- case error_kind_enum_exports.ServerOverloaded:
13385
- return ex.errorBody;
13386
- }
13387
- }
13388
- return void 0;
13389
- }
13390
- function isClientError(ex) {
13391
- return ex instanceof ClientError;
13392
- }
13393
-
13394
15189
  // ../zero-client/src/client/metric-name-enum.ts
13395
15190
  var metric_name_enum_exports = {};
13396
15191
  __export(metric_name_enum_exports, {
@@ -13417,6 +15212,21 @@ function getLastConnectErrorValue(reason) {
13417
15212
  function camelToSnake(kind) {
13418
15213
  return kind.split(/\.?(?=[A-Z])/).join("_").toLowerCase();
13419
15214
  }
15215
+ function shouldReportConnectError(reason) {
15216
+ if (!isClientError(reason)) {
15217
+ return true;
15218
+ }
15219
+ switch (reason.kind) {
15220
+ case client_error_kind_enum_exports.Hidden:
15221
+ case client_error_kind_enum_exports.ClientClosed:
15222
+ case client_error_kind_enum_exports.UserDisconnect:
15223
+ case client_error_kind_enum_exports.CleanClose:
15224
+ case client_error_kind_enum_exports.AbruptClose:
15225
+ return false;
15226
+ default:
15227
+ return true;
15228
+ }
15229
+ }
13420
15230
  var MetricManager = class {
13421
15231
  #reportIntervalMs;
13422
15232
  #host;
@@ -13647,10 +15457,11 @@ var MutationTracker = class {
13647
15457
  #allMutationsAppliedListeners;
13648
15458
  #lc;
13649
15459
  #ackMutations;
15460
+ #onFatalError;
13650
15461
  #clientID;
13651
15462
  #largestOutstandingMutationID;
13652
15463
  #currentMutationID;
13653
- constructor(lc, ackMutations) {
15464
+ constructor(lc, ackMutations, onFatalError) {
13654
15465
  this.#lc = lc.withContext("MutationTracker");
13655
15466
  this.#outstandingMutations = /* @__PURE__ */ new Map();
13656
15467
  this.#ephemeralIDsByMutationID = /* @__PURE__ */ new Map();
@@ -13658,6 +15469,7 @@ var MutationTracker = class {
13658
15469
  this.#largestOutstandingMutationID = 0;
13659
15470
  this.#currentMutationID = 0;
13660
15471
  this.#ackMutations = ackMutations;
15472
+ this.#onFatalError = onFatalError;
13661
15473
  }
13662
15474
  setClientIDAndWatch(clientID, experimentalWatch) {
13663
15475
  assert(this.#clientID === void 0, "clientID already set");
@@ -13698,7 +15510,7 @@ var MutationTracker = class {
13698
15510
  rejectMutation(id, e) {
13699
15511
  const entry = this.#outstandingMutations.get(id);
13700
15512
  if (entry) {
13701
- this.#settleMutation(id, entry, "reject", e);
15513
+ this.#settleMutation(id, entry, "reject", wrapWithApplicationError(e));
13702
15514
  }
13703
15515
  }
13704
15516
  /**
@@ -13745,10 +15557,53 @@ var MutationTracker = class {
13745
15557
  "Received an error response when pushing mutations",
13746
15558
  response
13747
15559
  );
15560
+ const fatalError = this.#fatalErrorFromPushError(response);
15561
+ if (fatalError) {
15562
+ this.#onFatalError(fatalError);
15563
+ }
13748
15564
  } else {
13749
15565
  this.#processPushOk(response);
13750
15566
  }
13751
15567
  }
15568
+ #fatalErrorFromPushError(error) {
15569
+ switch (error.error) {
15570
+ case "unsupportedPushVersion":
15571
+ return new ProtocolError({
15572
+ kind: error_kind_enum_exports.PushFailed,
15573
+ origin: error_origin_enum_exports.ZeroCache,
15574
+ reason: error_reason_enum_exports.Internal,
15575
+ message: `Unsupported push version`,
15576
+ mutationIDs: []
15577
+ });
15578
+ case "unsupportedSchemaVersion":
15579
+ return new ProtocolError({
15580
+ kind: error_kind_enum_exports.PushFailed,
15581
+ origin: error_origin_enum_exports.ZeroCache,
15582
+ reason: error_reason_enum_exports.Internal,
15583
+ message: `Unsupported schema version`,
15584
+ mutationIDs: []
15585
+ });
15586
+ case "http":
15587
+ return new ProtocolError({
15588
+ kind: error_kind_enum_exports.PushFailed,
15589
+ origin: error_origin_enum_exports.ZeroCache,
15590
+ reason: error_reason_enum_exports.HTTP,
15591
+ status: error.status,
15592
+ message: `Fetch from API server returned non-OK status ${error.status}: ${error.details ?? "unknown"}`,
15593
+ mutationIDs: []
15594
+ });
15595
+ case "zeroPusher":
15596
+ return new ProtocolError({
15597
+ kind: error_kind_enum_exports.PushFailed,
15598
+ origin: error_origin_enum_exports.ZeroCache,
15599
+ reason: error_reason_enum_exports.Internal,
15600
+ message: `ZeroPusher error: ${error.details ?? "unknown"}`,
15601
+ mutationIDs: []
15602
+ });
15603
+ default:
15604
+ unreachable(error);
15605
+ }
15606
+ }
13752
15607
  /**
13753
15608
  * DEPRECATED: to be removed when we switch to fully driving
13754
15609
  * mutation resolution via poke.
@@ -13841,7 +15696,28 @@ var MutationTracker = class {
13841
15696
  );
13842
15697
  const entry = this.#outstandingMutations.get(ephemeralID);
13843
15698
  assert(entry && entry.mutationID === mid);
13844
- this.#settleMutation(ephemeralID, entry, "reject", error);
15699
+ this.#settleMutation(
15700
+ ephemeralID,
15701
+ entry,
15702
+ "reject",
15703
+ error.error === "app" ? new ApplicationError(
15704
+ error.message ?? `Unknown application error: ${error.error}`,
15705
+ error.details ? { details: error.details } : void 0
15706
+ ) : new Error(
15707
+ error.error === "alreadyProcessed" ? "Mutation already processed" : error.error === "oooMutation" ? "Server reported an out-of-order mutation" : `Unknown fallback error with mutation ID ${mid}: ${error.error}`
15708
+ )
15709
+ );
15710
+ if (error.error === "oooMutation") {
15711
+ this.#onFatalError(
15712
+ new ProtocolError({
15713
+ kind: error_kind_enum_exports.InvalidPush,
15714
+ origin: error_origin_enum_exports.Server,
15715
+ reason: error_reason_enum_exports.Internal,
15716
+ message: "Server reported an out-of-order mutation",
15717
+ details: error.details
15718
+ })
15719
+ );
15720
+ }
13845
15721
  }
13846
15722
  #processMutationOk(clientID, mid, result) {
13847
15723
  assert(
@@ -13891,16 +15767,13 @@ var MutationTracker = class {
13891
15767
  }
13892
15768
  };
13893
15769
 
13894
- // ../zero-client/src/client/ping-result-enum.ts
13895
- var TimedOut = 0;
13896
- var Success = 1;
13897
-
13898
15770
  // ../zero-client/src/client/query-manager.ts
13899
15771
  var QueryManager = class {
13900
15772
  #clientID;
13901
15773
  #clientToServer;
13902
15774
  #serverToClient;
13903
15775
  #send;
15776
+ #onFatalError;
13904
15777
  #queries = /* @__PURE__ */ new Map();
13905
15778
  #recentQueriesMaxSize;
13906
15779
  #recentQueries = /* @__PURE__ */ new Set();
@@ -13914,7 +15787,7 @@ var QueryManager = class {
13914
15787
  #metrics = newMetrics();
13915
15788
  #queryMetrics = /* @__PURE__ */ new Map();
13916
15789
  #slowMaterializeThreshold;
13917
- constructor(lc, mutationTracker, clientID, tables, send2, experimentalWatch, recentQueriesMaxSize, queryChangeThrottleMs, slowMaterializeThreshold) {
15790
+ constructor(lc, mutationTracker, clientID, tables, send2, experimentalWatch, recentQueriesMaxSize, queryChangeThrottleMs, slowMaterializeThreshold, onFatalError) {
13918
15791
  this.#lc = lc.withContext("QueryManager");
13919
15792
  this.#clientID = clientID;
13920
15793
  this.#clientToServer = clientToServer(tables);
@@ -13924,6 +15797,7 @@ var QueryManager = class {
13924
15797
  this.#mutationTracker = mutationTracker;
13925
15798
  this.#queryChangeThrottleMs = queryChangeThrottleMs;
13926
15799
  this.#slowMaterializeThreshold = slowMaterializeThreshold;
15800
+ this.#onFatalError = onFatalError;
13927
15801
  this.#mutationTracker.onAllMutationsApplied(() => {
13928
15802
  if (this.#pendingRemovals.length === 0) {
13929
15803
  return;
@@ -14027,8 +15901,21 @@ var QueryManager = class {
14027
15901
  for (const error of errors) {
14028
15902
  const queryId = error.id;
14029
15903
  const entry = this.#queries.get(queryId);
14030
- if (entry) {
15904
+ if (!entry) {
15905
+ continue;
15906
+ }
15907
+ if (error.error === "app" || error.error === "parse") {
14031
15908
  entry.gotCallbacks.forEach((callback) => callback(false, error));
15909
+ } else {
15910
+ this.#onFatalError(
15911
+ new ProtocolError({
15912
+ kind: error_kind_enum_exports.TransformFailed,
15913
+ origin: error_origin_enum_exports.ZeroCache,
15914
+ reason: error_reason_enum_exports.Internal,
15915
+ message: `Unknown error transforming queries: ${JSON.stringify(error)}`,
15916
+ queryIDs: []
15917
+ })
15918
+ );
14032
15919
  }
14033
15920
  }
14034
15921
  }
@@ -14689,13 +16576,17 @@ function rafFallback(callback) {
14689
16576
  }
14690
16577
 
14691
16578
  // ../zero-client/src/client/zero-rep.ts
16579
+ var REPLICACHE_NO_AUTH_TOKEN = "";
16580
+ var toReplicacheAuthToken = (auth) => auth ?? REPLICACHE_NO_AUTH_TOKEN;
16581
+ var fromReplicacheAuthToken = (auth) => !auth ? void 0 : auth;
14692
16582
  var ZeroRep = class {
14693
16583
  #context;
14694
16584
  #ivmMain;
14695
16585
  #customMutatorsEnabled;
14696
16586
  #mutationTracker;
14697
16587
  #store;
14698
- #auth;
16588
+ // matches replicache's auth token type
16589
+ #auth = REPLICACHE_NO_AUTH_TOKEN;
14699
16590
  constructor(context, ivmMain, customMutatorsEnabled, mutationTracker) {
14700
16591
  this.#context = context;
14701
16592
  this.#ivmMain = ivmMain;
@@ -14703,11 +16594,7 @@ var ZeroRep = class {
14703
16594
  this.#mutationTracker = mutationTracker;
14704
16595
  }
14705
16596
  set auth(auth) {
14706
- if (auth === "") {
14707
- this.#auth = void 0;
14708
- } else {
14709
- this.#auth = auth;
14710
- }
16597
+ this.#auth = auth;
14711
16598
  }
14712
16599
  async init(hash, store) {
14713
16600
  const diffs = [];
@@ -14733,7 +16620,7 @@ var ZeroRep = class {
14733
16620
  }
14734
16621
  return this.#ivmMain.forkToHead(must(this.#store), desiredHead, readOptions).then((branch) => ({
14735
16622
  ivmSources: branch,
14736
- token: this.#auth
16623
+ token: fromReplicacheAuthToken(this.#auth)
14737
16624
  }));
14738
16625
  };
14739
16626
  advance = (expectedHash, newHash, diffs) => {
@@ -14845,20 +16732,21 @@ var Zero = class _Zero {
14845
16732
  };
14846
16733
  #zeroContext;
14847
16734
  queryDelegate;
14848
- #connectResolver = resolver11();
14849
16735
  #pendingPullsByRequestID = /* @__PURE__ */ new Map();
14850
16736
  #lastMutationIDReceived = 0;
14851
16737
  #socket = void 0;
14852
16738
  #socketResolver = resolver11();
14853
16739
  /**
14854
- * This resolver is only used for rejections. It is awaited in the connected
14855
- * state (including when waiting for a pong). It is rejected when we get an
14856
- * invalid message or an 'error' message.
16740
+ * Utility promise that resolves when the socket transitions to connected.
16741
+ * It rejects if we hit an error or timeout before the connected message.
16742
+ * Used by push/pull helpers to queue work until the connection is usable.
14857
16743
  */
14858
- #rejectMessageError = void 0;
16744
+ #connectResolver = resolver11();
14859
16745
  #closeAbortController = new AbortController();
14860
16746
  #visibilityWatcher;
14861
16747
  #connectionManager;
16748
+ #connection;
16749
+ #unsubscribeConnectionState = void 0;
14862
16750
  #activeClientsManager;
14863
16751
  #inspector;
14864
16752
  #connectStart = void 0;
@@ -14894,7 +16782,10 @@ var Zero = class _Zero {
14894
16782
  slowMaterializeThreshold = 5e3
14895
16783
  } = options;
14896
16784
  if (!userID) {
14897
- throw new Error("ZeroOptions.userID must not be empty.");
16785
+ throw new ClientError({
16786
+ kind: client_error_kind_enum_exports.Internal,
16787
+ message: "ZeroOptions.userID must not be empty."
16788
+ });
14898
16789
  }
14899
16790
  const server = getServer(options.server);
14900
16791
  this.#enableAnalytics = shouldEnableAnalytics(
@@ -14912,9 +16803,10 @@ var Zero = class _Zero {
14912
16803
  }
14913
16804
  }
14914
16805
  if (hiddenTabDisconnectDelay < 0) {
14915
- throw new Error(
14916
- "ZeroOptions.hiddenTabDisconnectDelay must not be negative."
14917
- );
16806
+ throw new ClientError({
16807
+ kind: client_error_kind_enum_exports.Internal,
16808
+ message: "ZeroOptions.hiddenTabDisconnectDelay must not be negative."
16809
+ });
14918
16810
  }
14919
16811
  this.#onlineManager = new OnlineManager();
14920
16812
  if (onOnlineChange) {
@@ -14931,9 +16823,19 @@ var Zero = class _Zero {
14931
16823
  this.#connectionManager = new ConnectionManager({
14932
16824
  disconnectTimeoutMs: DEFAULT_DISCONNECT_TIMEOUT_MS
14933
16825
  });
16826
+ const syncOnlineState = (state) => {
16827
+ this.#onlineManager.setOnline(state.name === connection_status_enum_exports.Connected);
16828
+ };
16829
+ syncOnlineState(this.#connectionManager.state);
16830
+ this.#unsubscribeConnectionState = this.#connectionManager.subscribe(syncOnlineState);
14934
16831
  const { enableLegacyMutators = true, enableLegacyQueries = true } = schema;
14935
16832
  const replicacheMutators = {
14936
- [CRUD_MUTATION_NAME]: enableLegacyMutators ? makeCRUDMutator(schema) : () => Promise.reject(new Error("Zero CRUD mutators are not enabled."))
16833
+ [CRUD_MUTATION_NAME]: enableLegacyMutators ? makeCRUDMutator(schema) : () => Promise.reject(
16834
+ new ClientError({
16835
+ kind: client_error_kind_enum_exports.Internal,
16836
+ message: "Zero CRUD mutators are not enabled."
16837
+ })
16838
+ )
14937
16839
  };
14938
16840
  this.#ivmMain = new IVMSourceBranch(schema.tables);
14939
16841
  function assertUnique(key) {
@@ -14959,7 +16861,10 @@ var Zero = class _Zero {
14959
16861
  const lc = new ZeroLogContext(logOptions.logLevel, {}, logSink);
14960
16862
  this.#mutationTracker = new MutationTracker(
14961
16863
  lc,
14962
- (upTo) => this.#send(["ackMutationResponses", upTo])
16864
+ (upTo) => this.#send(["ackMutationResponses", upTo]),
16865
+ (error) => {
16866
+ this.#disconnect(lc, error);
16867
+ }
14963
16868
  );
14964
16869
  if (options.mutators) {
14965
16870
  for (const [namespaceOrKey, mutatorOrMutators] of Object.entries(
@@ -15065,6 +16970,11 @@ var Zero = class _Zero {
15065
16970
  this.#server = server;
15066
16971
  this.userID = userID;
15067
16972
  this.#lc = lc.withContext("clientID", rep.clientID);
16973
+ this.#connection = new ConnectionImpl(
16974
+ this.#connectionManager,
16975
+ this.#lc,
16976
+ (auth) => this.#setAuth(auth)
16977
+ );
15068
16978
  this.#mutationTracker.setClientIDAndWatch(
15069
16979
  rep.clientID,
15070
16980
  rep.experimentalWatch.bind(rep)
@@ -15135,7 +17045,10 @@ var Zero = class _Zero {
15135
17045
  rep.experimentalWatch.bind(rep),
15136
17046
  maxRecentQueries,
15137
17047
  options.queryChangeThrottleMs ?? DEFAULT_QUERY_CHANGE_THROTTLE_MS,
15138
- slowMaterializeThreshold
17048
+ slowMaterializeThreshold,
17049
+ (error) => {
17050
+ this.#disconnect(lc, error);
17051
+ }
15139
17052
  );
15140
17053
  this.#clientToServer = clientToServer(schema.tables);
15141
17054
  this.#deleteClientsManager = new DeleteClientsManager(
@@ -15326,6 +17239,25 @@ var Zero = class _Zero {
15326
17239
  * will throw an error.
15327
17240
  */
15328
17241
  mutateBatch;
17242
+ /**
17243
+ * The connection API for managing Zero's connection lifecycle.
17244
+ *
17245
+ * Use this to monitor connection state and manually control connections.
17246
+ *
17247
+ * @example
17248
+ * ```ts
17249
+ * // Subscribe to connection state changes
17250
+ * z.connection.state.subscribe(state => {
17251
+ * console.log('Connection state:', state.name);
17252
+ * });
17253
+ *
17254
+ * // Manually resume connection from error state
17255
+ * await z.connection.connect();
17256
+ * ```
17257
+ */
17258
+ get connection() {
17259
+ return this.#connection;
17260
+ }
15329
17261
  /**
15330
17262
  * Whether this Zero instance has been closed.
15331
17263
  *
@@ -15349,6 +17281,8 @@ var Zero = class _Zero {
15349
17281
  return;
15350
17282
  }
15351
17283
  lc.debug?.("Closing Zero instance. Stack:", new Error().stack);
17284
+ this.#unsubscribeConnectionState?.();
17285
+ this.#unsubscribeConnectionState = void 0;
15352
17286
  this.#onlineManager.cleanup();
15353
17287
  if (!this.#connectionManager.is(connection_status_enum_exports.Disconnected)) {
15354
17288
  this.#disconnect(
@@ -15371,7 +17305,6 @@ var Zero = class _Zero {
15371
17305
  throw e;
15372
17306
  } finally {
15373
17307
  this.#connectionManager.closed();
15374
- this.#connectionManager.cleanup();
15375
17308
  }
15376
17309
  }
15377
17310
  #onMessage = (e) => {
@@ -15381,11 +17314,6 @@ var Zero = class _Zero {
15381
17314
  lc.debug?.("ignoring message because already closed");
15382
17315
  return;
15383
17316
  }
15384
- const rejectInvalidMessage = (e2) => this.#rejectMessageError?.reject(
15385
- new Error(
15386
- `Invalid message received from server: ${e2 instanceof Error ? e2.message + ". " : ""}${data}`
15387
- )
15388
- );
15389
17317
  let downMessage;
15390
17318
  const { data } = e;
15391
17319
  try {
@@ -15395,7 +17323,11 @@ var Zero = class _Zero {
15395
17323
  "passthrough"
15396
17324
  );
15397
17325
  } catch (e2) {
15398
- rejectInvalidMessage(e2);
17326
+ const invalidMessageError = new ClientError({
17327
+ kind: client_error_kind_enum_exports.InvalidMessage,
17328
+ message: `Invalid message received from server: ${e2 instanceof Error ? e2.message + ". " : ""}${data}`
17329
+ });
17330
+ this.#disconnect(lc, invalidMessageError);
15399
17331
  return;
15400
17332
  }
15401
17333
  this.#messageCount++;
@@ -15430,9 +17362,14 @@ var Zero = class _Zero {
15430
17362
  break;
15431
17363
  case "inspect":
15432
17364
  break;
15433
- default:
15434
- msgType;
15435
- rejectInvalidMessage();
17365
+ default: {
17366
+ const invalidMessageError = new ClientError({
17367
+ kind: client_error_kind_enum_exports.InvalidMessage,
17368
+ message: `Invalid message received from server: ${data}`
17369
+ });
17370
+ this.#disconnect(lc, invalidMessageError);
17371
+ return;
17372
+ }
15436
17373
  }
15437
17374
  };
15438
17375
  #onOpen = () => {
@@ -15481,11 +17418,10 @@ var Zero = class _Zero {
15481
17418
  return;
15482
17419
  }
15483
17420
  lc.info?.(`${kind}: ${message}}`);
15484
- const error = new ServerError(downMessage[1]);
17421
+ const error = new ProtocolError(downMessage[1]);
15485
17422
  lc.error?.(`${error.kind}:
15486
17423
 
15487
17424
  ${error.errorBody.message}`, error);
15488
- this.#rejectMessageError?.reject(error);
15489
17425
  lc.debug?.("Rejecting connect resolver due to error", error);
15490
17426
  this.#connectResolver.reject(error);
15491
17427
  this.#disconnect(lc, error);
@@ -15607,15 +17543,19 @@ ${error.errorBody.message}`, error);
15607
17543
  * attempt times out.
15608
17544
  */
15609
17545
  async #connect(lc, additionalConnectParams) {
15610
- assert(this.#server);
17546
+ if (this.closed) {
17547
+ return;
17548
+ }
17549
+ assert(this.#server, "No server provided");
15611
17550
  assert(
15612
- this.#connectionManager.is(connection_status_enum_exports.Disconnected) || this.#connectionManager.is(connection_status_enum_exports.Connecting)
17551
+ this.#connectionManager.is(connection_status_enum_exports.Disconnected) || this.#connectionManager.is(connection_status_enum_exports.Connecting),
17552
+ "connect() called from invalid state: " + this.#connectionManager.state.name
15613
17553
  );
15614
17554
  const wsid = nanoid();
15615
17555
  lc = addWebSocketIDToLogContext(wsid, lc);
15616
17556
  lc.info?.("Connecting...", { navigatorOnline: localNavigator?.onLine });
15617
17557
  this.#connectionManager.connecting();
15618
- assert(this.#connectStart === void 0);
17558
+ assert(this.#connectStart === void 0, "connect start time is defined");
15619
17559
  const now = Date.now();
15620
17560
  this.#connectStart = now;
15621
17561
  if (this.#totalToConnectStart === void 0) {
@@ -15655,7 +17595,7 @@ ${error.errorBody.message}`, error);
15655
17595
  await this.clientGroupID,
15656
17596
  this.#clientSchema,
15657
17597
  this.userID,
15658
- this.#rep.auth,
17598
+ fromReplicacheAuthToken(this.#rep.auth),
15659
17599
  this.#lastMutationIDReceived,
15660
17600
  wsid,
15661
17601
  this.#options.logLevel === "debug",
@@ -15691,8 +17631,14 @@ ${error.errorBody.message}`, error);
15691
17631
  }
15692
17632
  }
15693
17633
  #disconnect(lc, reason, closeCode) {
15694
- if (this.#connectionManager.is(connection_status_enum_exports.Connecting)) {
17634
+ if (shouldReportConnectError(reason)) {
15695
17635
  this.#connectErrorCount++;
17636
+ this.#metrics.lastConnectError.set(getLastConnectErrorValue(reason));
17637
+ this.#metrics.timeToConnectMs.set(DID_NOT_CONNECT_VALUE);
17638
+ this.#metrics.setConnectError(reason);
17639
+ if (this.#connectErrorCount % CHECK_CONNECTIVITY_ON_ERROR_FREQUENCY === 1) {
17640
+ this.#checkConnectivity(`connectErrorCount=${this.#connectErrorCount}`);
17641
+ }
15696
17642
  }
15697
17643
  lc.info?.("disconnecting", {
15698
17644
  navigatorOnline: localNavigator?.onLine,
@@ -15705,8 +17651,8 @@ ${error.errorBody.message}`, error);
15705
17651
  connectionState: this.#connectionManager.state,
15706
17652
  connectErrorCount: this.#connectErrorCount
15707
17653
  });
15708
- const connectionState = this.#connectionManager.state.name;
15709
- switch (connectionState) {
17654
+ const connectionStatus = this.#connectionManager.state.name;
17655
+ switch (connectionStatus) {
15710
17656
  case connection_status_enum_exports.Connected: {
15711
17657
  if (this.#connectStart !== void 0) {
15712
17658
  lc.error?.(
@@ -15715,26 +17661,16 @@ ${error.errorBody.message}`, error);
15715
17661
  }
15716
17662
  break;
15717
17663
  }
15718
- case connection_status_enum_exports.Disconnected:
15719
- case connection_status_enum_exports.Connecting: {
15720
- this.#metrics.lastConnectError.set(getLastConnectErrorValue(reason));
15721
- this.#metrics.timeToConnectMs.set(DID_NOT_CONNECT_VALUE);
15722
- this.#metrics.setConnectError(reason);
15723
- if (this.#connectErrorCount % CHECK_CONNECTIVITY_ON_ERROR_FREQUENCY === 1) {
15724
- this.#checkConnectivity(
15725
- `connectErrorCount=${this.#connectErrorCount}`
15726
- );
15727
- }
15728
- if (this.#connectStart === void 0) {
15729
- lc.error?.(
15730
- "disconnect() called while connecting but connect start time is undefined."
15731
- );
15732
- }
15733
- break;
15734
- }
15735
17664
  case connection_status_enum_exports.Closed:
15736
- lc.error?.("disconnect() called while closed");
17665
+ lc.debug?.("disconnect() called while closed");
15737
17666
  return;
17667
+ case connection_status_enum_exports.Disconnected:
17668
+ case connection_status_enum_exports.Connecting:
17669
+ case connection_status_enum_exports.NeedsAuth:
17670
+ case connection_status_enum_exports.Error:
17671
+ break;
17672
+ default:
17673
+ unreachable(connectionStatus);
15738
17674
  }
15739
17675
  this.#socketResolver = resolver11();
15740
17676
  lc.debug?.("Creating new connect resolver");
@@ -15749,11 +17685,25 @@ ${error.errorBody.message}`, error);
15749
17685
  this.#socket = void 0;
15750
17686
  this.#lastMutationIDSent = NULL_LAST_MUTATION_ID_SENT;
15751
17687
  this.#pokeHandler.handleDisconnect();
15752
- const isStillConnecting = reason instanceof ClientError && (reason.kind === client_error_kind_enum_exports.ConnectTimeout || reason.kind === client_error_kind_enum_exports.Hidden);
15753
- if (isStillConnecting) {
15754
- this.#connectionManager.connecting(reason);
15755
- } else {
15756
- this.#connectionManager.disconnected(reason);
17688
+ const transition = getErrorConnectionTransition(reason);
17689
+ switch (transition.status) {
17690
+ case connection_status_enum_exports.NeedsAuth:
17691
+ this.#connectionManager.needsAuth(transition.reason);
17692
+ break;
17693
+ case connection_status_enum_exports.Error:
17694
+ this.#connectionManager.error(transition.reason);
17695
+ break;
17696
+ case connection_status_enum_exports.Disconnected:
17697
+ this.#connectionManager.disconnected(transition.reason);
17698
+ break;
17699
+ case connection_status_enum_exports.Closed:
17700
+ this.#connectionManager.closed();
17701
+ break;
17702
+ case NO_STATUS_TRANSITION:
17703
+ this.#connectionManager.connecting(transition.reason);
17704
+ break;
17705
+ default:
17706
+ unreachable(transition);
15757
17707
  }
15758
17708
  }
15759
17709
  #handlePokeStart(_lc, pokeMessage) {
@@ -15860,19 +17810,11 @@ ${error.errorBody.message}`, error);
15860
17810
  }
15861
17811
  };
15862
17812
  }
15863
- async #updateAuthToken(lc, error) {
15864
- const { auth: authOption } = this.#options;
15865
- const auth = await (typeof authOption === "function" ? authOption(error) : authOption);
15866
- if (auth) {
15867
- lc.debug?.("Got auth token");
15868
- this.#rep.auth = auth;
15869
- }
15870
- }
15871
17813
  async #runLoop() {
15872
17814
  this.#lc.info?.(`Starting Zero version: ${this.version}`);
15873
17815
  if (this.#server === null) {
15874
17816
  this.#lc.info?.("No socket origin provided, not starting connect loop.");
15875
- this.#connectionManager.disconnected(
17817
+ this.#connectionManager.error(
15876
17818
  new ClientError({
15877
17819
  kind: client_error_kind_enum_exports.NoSocketOrigin,
15878
17820
  message: "No server socket origin provided"
@@ -15889,43 +17831,40 @@ ${error.errorBody.message}`, error);
15889
17831
  }
15890
17832
  return lc.withContext("runLoopCounter", runLoopCounter);
15891
17833
  };
15892
- await this.#updateAuthToken(bareLogContext);
15893
- let needsReauth = false;
15894
- let lastReauthAttemptAt;
15895
- let gotError = false;
15896
- let backoffMs = RUN_LOOP_INTERVAL_MS;
17834
+ const { auth } = this.#options;
17835
+ this.#setAuth(auth);
17836
+ let backoffMs;
15897
17837
  let additionalConnectParams;
15898
17838
  while (this.#connectionManager.shouldContinueRunLoop()) {
15899
17839
  runLoopCounter++;
15900
17840
  let lc = getLogContext();
15901
17841
  backoffMs = RUN_LOOP_INTERVAL_MS;
15902
17842
  try {
15903
- switch (this.#connectionManager.state.name) {
17843
+ const currentState = this.#connectionManager.state;
17844
+ switch (currentState.name) {
15904
17845
  case connection_status_enum_exports.Connecting:
15905
17846
  case connection_status_enum_exports.Disconnected: {
15906
17847
  if (this.#visibilityWatcher.visibilityState === "hidden") {
15907
17848
  this.#metrics.setDisconnectedWaitingForVisible();
15908
17849
  this.#totalToConnectStart = void 0;
15909
17850
  }
15910
- await this.#visibilityWatcher.waitForVisible();
15911
- if (needsReauth) {
15912
- lastReauthAttemptAt = Date.now();
15913
- await this.#updateAuthToken(lc, "invalid-token");
17851
+ const visibilityResult = await promiseRace({
17852
+ visible: this.#visibilityWatcher.waitForVisible(),
17853
+ stateChange: this.#connectionManager.waitForStateChange()
17854
+ });
17855
+ if (visibilityResult.key === "stateChange") {
17856
+ throwIfConnectionError(visibilityResult.result);
17857
+ break;
15914
17858
  }
15915
17859
  if (reloadScheduled()) {
15916
17860
  break;
15917
17861
  }
15918
17862
  await this.#connect(lc, additionalConnectParams);
15919
17863
  additionalConnectParams = void 0;
15920
- if (this.closed) {
15921
- break;
15922
- }
17864
+ throwIfConnectionError(this.#connectionManager.state);
15923
17865
  assert(this.#socket);
15924
17866
  lc = getLogContext();
15925
17867
  lc.debug?.("Connected successfully");
15926
- gotError = false;
15927
- needsReauth = false;
15928
- this.#setOnline(true);
15929
17868
  break;
15930
17869
  }
15931
17870
  case connection_status_enum_exports.Connected: {
@@ -15935,51 +17874,59 @@ ${error.errorBody.message}`, error);
15935
17874
  PING_INTERVAL_MS,
15936
17875
  controller.signal
15937
17876
  );
15938
- this.#rejectMessageError = resolver11();
15939
- const PING = 0;
15940
- const HIDDEN = 2;
15941
- const raceResult = await promiseRace([
15942
- pingTimeoutPromise,
15943
- pingTimeoutAborted,
15944
- this.#visibilityWatcher.waitForHidden(),
15945
- this.#connectionManager.waitForStateChange(),
15946
- this.#rejectMessageError.promise
15947
- ]);
15948
- if (this.closed) {
15949
- this.#rejectMessageError = void 0;
15950
- break;
15951
- }
15952
- switch (raceResult) {
15953
- case PING: {
15954
- const pingResult = await this.#ping(
15955
- lc,
15956
- this.#rejectMessageError.promise
15957
- );
15958
- if (pingResult === TimedOut) {
15959
- gotError = true;
15960
- }
17877
+ const raceResult = await promiseRace({
17878
+ waitForPing: pingTimeoutPromise,
17879
+ waitForPingAborted: pingTimeoutAborted,
17880
+ tabHidden: this.#visibilityWatcher.waitForHidden(),
17881
+ stateChange: this.#connectionManager.waitForStateChange()
17882
+ });
17883
+ switch (raceResult.key) {
17884
+ case "waitForPing": {
17885
+ await this.#ping(lc);
17886
+ break;
17887
+ }
17888
+ case "waitForPingAborted":
17889
+ break;
17890
+ case "tabHidden": {
17891
+ const hiddenError = new ClientError({
17892
+ kind: client_error_kind_enum_exports.Hidden,
17893
+ message: "Connection closed because tab was hidden"
17894
+ });
17895
+ this.#disconnect(lc, hiddenError);
15961
17896
  break;
15962
17897
  }
15963
- case HIDDEN:
15964
- this.#disconnect(
15965
- lc,
15966
- new ClientError({
15967
- kind: client_error_kind_enum_exports.Hidden,
15968
- message: "Connection closed because tab was hidden"
15969
- })
15970
- );
15971
- this.#setOnline(false);
17898
+ case "stateChange":
17899
+ throwIfConnectionError(raceResult.result);
15972
17900
  break;
17901
+ default:
17902
+ unreachable(raceResult);
15973
17903
  }
15974
- this.#rejectMessageError = void 0;
17904
+ break;
17905
+ }
17906
+ case connection_status_enum_exports.NeedsAuth: {
17907
+ lc.info?.(
17908
+ `Run loop paused in needs-auth state. Call zero.connection.connect({auth}) to resume.`,
17909
+ currentState.reason
17910
+ );
17911
+ await this.#connectionManager.waitForStateChange();
17912
+ break;
17913
+ }
17914
+ case connection_status_enum_exports.Error: {
17915
+ lc.info?.(
17916
+ `Run loop paused in error state. Call zero.connection.connect() to resume.`,
17917
+ currentState.reason
17918
+ );
17919
+ await this.#connectionManager.waitForStateChange();
15975
17920
  break;
15976
17921
  }
15977
17922
  case connection_status_enum_exports.Closed:
15978
- this.#rejectMessageError = void 0;
15979
17923
  break;
17924
+ default:
17925
+ unreachable(currentState);
15980
17926
  }
15981
17927
  } catch (ex) {
15982
- if (!this.#connectionManager.is(connection_status_enum_exports.Connected)) {
17928
+ const isClientClosedError = isClientError(ex) && ex.kind === client_error_kind_enum_exports.ClientClosed;
17929
+ if (!this.#connectionManager.is(connection_status_enum_exports.Connected) && !isClientClosedError) {
15983
17930
  const level = isAuthError(ex) ? "warn" : "error";
15984
17931
  const kind = isServerError(ex) ? ex.kind : "Unknown Error";
15985
17932
  lc[level]?.("Failed to connect", ex, kind, {
@@ -15994,38 +17941,51 @@ ${error.errorBody.message}`, error);
15994
17941
  "exception:",
15995
17942
  ex
15996
17943
  );
15997
- if (isAuthError(ex)) {
15998
- const now = Date.now();
15999
- const msSinceLastReauthAttempt = lastReauthAttemptAt === void 0 ? Number.POSITIVE_INFINITY : now - lastReauthAttemptAt;
16000
- needsReauth = true;
16001
- if (msSinceLastReauthAttempt > RUN_LOOP_INTERVAL_MS) {
16002
- continue;
17944
+ const transition = getErrorConnectionTransition(ex);
17945
+ switch (transition.status) {
17946
+ case NO_STATUS_TRANSITION: {
17947
+ const backoffParams = getBackoffParams(transition.reason);
17948
+ if (backoffParams) {
17949
+ if (backoffParams.minBackoffMs !== void 0) {
17950
+ backoffMs = Math.max(backoffMs, backoffParams.minBackoffMs);
17951
+ }
17952
+ if (backoffParams.maxBackoffMs !== void 0) {
17953
+ backoffMs = Math.min(backoffMs, backoffParams.maxBackoffMs);
17954
+ }
17955
+ additionalConnectParams = backoffParams.reconnectParams;
17956
+ }
17957
+ lc.debug?.(
17958
+ "Sleeping",
17959
+ backoffMs,
17960
+ "ms before reconnecting due to error, state:",
17961
+ this.#connectionManager.state
17962
+ );
17963
+ await sleep(backoffMs);
17964
+ break;
16003
17965
  }
16004
- }
16005
- if (isServerError(ex) || isClientError(ex) && (ex.kind === client_error_kind_enum_exports.ConnectTimeout || ex.kind === client_error_kind_enum_exports.AbruptClose || ex.kind === client_error_kind_enum_exports.CleanClose)) {
16006
- gotError = true;
16007
- }
16008
- const backoffError = isBackoffError(ex);
16009
- if (backoffError) {
16010
- if (backoffError.minBackoffMs !== void 0) {
16011
- backoffMs = Math.max(backoffMs, backoffError.minBackoffMs);
17966
+ case connection_status_enum_exports.NeedsAuth: {
17967
+ lc.debug?.(
17968
+ "Auth error encountered, transitioning to needs-auth state"
17969
+ );
17970
+ this.#connectionManager.needsAuth(transition.reason);
17971
+ break;
17972
+ }
17973
+ case connection_status_enum_exports.Error: {
17974
+ lc.debug?.("Fatal error encountered, transitioning to error state");
17975
+ this.#connectionManager.error(transition.reason);
17976
+ break;
17977
+ }
17978
+ case connection_status_enum_exports.Disconnected: {
17979
+ this.#connectionManager.disconnected(transition.reason);
17980
+ break;
16012
17981
  }
16013
- if (backoffError.maxBackoffMs !== void 0) {
16014
- backoffMs = Math.min(backoffMs, backoffError.maxBackoffMs);
17982
+ case connection_status_enum_exports.Closed: {
17983
+ break;
16015
17984
  }
16016
- additionalConnectParams = backoffError.reconnectParams;
17985
+ default:
17986
+ unreachable(transition);
16017
17987
  }
16018
17988
  }
16019
- if (gotError) {
16020
- this.#setOnline(false);
16021
- lc.debug?.(
16022
- "Sleeping",
16023
- backoffMs,
16024
- "ms before reconnecting due to error, state:",
16025
- this.#connectionManager.state
16026
- );
16027
- await sleep(backoffMs);
16028
- }
16029
17989
  }
16030
17990
  }
16031
17991
  async #puller(req, requestID) {
@@ -16061,17 +18021,18 @@ ${error.errorBody.message}`, error);
16061
18021
  const pullResponseResolver = resolver11();
16062
18022
  this.#pendingPullsByRequestID.set(requestID, pullResponseResolver);
16063
18023
  try {
16064
- const TIMEOUT = 0;
16065
- const RESPONSE = 1;
16066
- const raceResult = await promiseRace([
16067
- sleep(PULL_TIMEOUT_MS),
16068
- pullResponseResolver.promise
16069
- ]);
16070
- switch (raceResult) {
16071
- case TIMEOUT:
18024
+ const raceResult = await promiseRace({
18025
+ timeout: sleep(PULL_TIMEOUT_MS),
18026
+ success: pullResponseResolver.promise
18027
+ });
18028
+ switch (raceResult.key) {
18029
+ case "timeout":
16072
18030
  lc.debug?.("Mutation recovery pull timed out");
16073
- throw new Error("Pull timed out");
16074
- case RESPONSE: {
18031
+ throw new ClientError({
18032
+ kind: client_error_kind_enum_exports.PullTimeout,
18033
+ message: "Pull timed out"
18034
+ });
18035
+ case "success": {
16075
18036
  lc.debug?.("Returning mutation recovery pull response");
16076
18037
  const response = await pullResponseResolver.promise;
16077
18038
  return {
@@ -16087,19 +18048,31 @@ ${error.errorBody.message}`, error);
16087
18048
  };
16088
18049
  }
16089
18050
  default:
16090
- unreachable();
18051
+ unreachable(raceResult);
16091
18052
  }
16092
18053
  } finally {
16093
- pullResponseResolver.reject("timed out");
18054
+ pullResponseResolver.reject(
18055
+ new ClientError({
18056
+ kind: client_error_kind_enum_exports.PullTimeout,
18057
+ message: "Pull timed out"
18058
+ })
18059
+ );
16094
18060
  this.#pendingPullsByRequestID.delete(requestID);
16095
18061
  }
16096
18062
  }
16097
- #setOnline(online) {
16098
- this.#onlineManager.setOnline(online);
18063
+ /**
18064
+ * Sets the authentication token on the replicache instance.
18065
+ *
18066
+ * @param auth - The authentication token to set.
18067
+ */
18068
+ #setAuth(auth) {
18069
+ this.#rep.auth = toReplicacheAuthToken(auth);
16099
18070
  }
16100
18071
  /**
16101
18072
  * A rough heuristic for whether the client is currently online and
16102
18073
  * authenticated.
18074
+ *
18075
+ * @deprecated Use `connection` instead, which provides more detailed connection state.
16103
18076
  */
16104
18077
  get online() {
16105
18078
  return this.#onlineManager.online;
@@ -16111,15 +18084,14 @@ ${error.errorBody.message}`, error);
16111
18084
  *
16112
18085
  * @param listener - The listener to subscribe to.
16113
18086
  * @returns A function to unsubscribe the listener.
18087
+ *
18088
+ * @deprecated Use `connection` instead, which provides more detailed connection state.
16114
18089
  */
16115
18090
  onOnline = (listener) => this.#onlineManager.subscribe(listener);
16116
18091
  /**
16117
18092
  * Starts a ping and waits for a pong.
16118
- *
16119
- * If it takes too long to get a pong we disconnect and this returns
16120
- * {@linkcode PingResult.TimedOut}.
16121
18093
  */
16122
- async #ping(lc, messageErrorRejectionPromise) {
18094
+ async #ping(lc) {
16123
18095
  lc.debug?.("pinging");
16124
18096
  const { promise, resolve } = resolver11();
16125
18097
  this.#onPong = resolve;
@@ -16127,25 +18099,37 @@ ${error.errorBody.message}`, error);
16127
18099
  const t0 = performance.now();
16128
18100
  assert(this.#socket);
16129
18101
  send(this.#socket, pingMessage);
16130
- const connected = await promiseRace([
16131
- promise,
16132
- sleep(PING_TIMEOUT_MS),
16133
- messageErrorRejectionPromise
16134
- ]) === 0;
18102
+ const raceResult = await promiseRace({
18103
+ waitForPong: promise,
18104
+ pingTimeout: sleep(PING_TIMEOUT_MS),
18105
+ stateChange: this.#connectionManager.waitForStateChange()
18106
+ });
16135
18107
  const delta = performance.now() - t0;
16136
- if (!connected) {
16137
- lc.info?.("ping failed in", delta, "ms - disconnecting");
16138
- this.#disconnect(
16139
- lc,
16140
- new ClientError({
18108
+ switch (raceResult.key) {
18109
+ case "waitForPong": {
18110
+ lc.debug?.("ping succeeded in", delta, "ms");
18111
+ return;
18112
+ }
18113
+ case "pingTimeout": {
18114
+ lc.info?.("ping failed in", delta, "ms - disconnecting");
18115
+ const pingTimeoutError = new ClientError({
16141
18116
  kind: client_error_kind_enum_exports.PingTimeout,
16142
- message: "Server ping request timed out"
16143
- })
16144
- );
16145
- return TimedOut;
18117
+ message: "Server ping request failed"
18118
+ });
18119
+ this.#disconnect(lc, pingTimeoutError);
18120
+ throw pingTimeoutError;
18121
+ }
18122
+ case "stateChange": {
18123
+ lc.debug?.(
18124
+ "ping aborted due to connection state change",
18125
+ raceResult.result
18126
+ );
18127
+ throwIfConnectionError(raceResult.result);
18128
+ break;
18129
+ }
18130
+ default:
18131
+ unreachable(raceResult);
16146
18132
  }
16147
- lc.debug?.("ping succeeded in", delta, "ms");
16148
- return Success;
16149
18133
  }
16150
18134
  // Sends a set of metrics to the server. Throws unless the server
16151
18135
  // returns 200.
@@ -16304,9 +18288,6 @@ function addWebSocketIDFromSocketToLogContext({ url }, lc) {
16304
18288
  function addWebSocketIDToLogContext(wsid, lc) {
16305
18289
  return lc.withContext("wsid", wsid);
16306
18290
  }
16307
- function promiseRace(ps) {
16308
- return Promise.race(ps.map((p, i) => p.then(() => i)));
16309
- }
16310
18291
  function assertValidRunOptions2(_options) {
16311
18292
  }
16312
18293
  async function makeActiveClientsManager(clientGroupID, clientID, signal, onDelete) {
@@ -16326,12 +18307,15 @@ export {
16326
18307
  dropDatabase,
16327
18308
  dropAllDatabases,
16328
18309
  TransactionClosedError,
18310
+ ApplicationError,
18311
+ error_kind_enum_exports,
18312
+ error_origin_enum_exports,
18313
+ error_reason_enum_exports,
16329
18314
  transformRequestMessageSchema,
16330
18315
  transformResponseMessageSchema,
16331
- error_kind_enum_exports,
16332
18316
  table,
16333
- string2 as string,
16334
- number2 as number,
18317
+ string4 as string,
18318
+ number3 as number,
16335
18319
  boolean,
16336
18320
  json,
16337
18321
  enumeration,
@@ -16344,7 +18328,10 @@ export {
16344
18328
  newQuerySymbol,
16345
18329
  AbstractQuery,
16346
18330
  clientToServer,
18331
+ client_error_kind_enum_exports,
18332
+ connection_status_enum_exports,
18333
+ ClientError,
16347
18334
  update_needed_reason_type_enum_exports,
16348
18335
  Zero
16349
18336
  };
16350
- //# sourceMappingURL=chunk-MXPHMVU7.js.map
18337
+ //# sourceMappingURL=chunk-COKJ5W7V.js.map