@rocicorp/zero 0.24.2025102200 → 0.25.0-canary.1

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 (383) hide show
  1. package/out/{chunk-PIXC6O7W.js → chunk-55BOUNXO.js} +3 -3
  2. package/out/chunk-55BOUNXO.js.map +7 -0
  3. package/out/{chunk-JS2ABYXG.js → chunk-AIPM77UE.js} +2762 -674
  4. package/out/chunk-AIPM77UE.js.map +7 -0
  5. package/out/{chunk-VHLCXJ6Q.js → chunk-TJFNGO7E.js} +4 -2
  6. package/out/{chunk-VHLCXJ6Q.js.map → chunk-TJFNGO7E.js.map} +2 -2
  7. package/out/{lazy-inspector-NCBESZMS.js → lazy-inspector-OXIFYSSQ.js} +2 -2
  8. package/out/react.js +16 -4
  9. package/out/react.js.map +4 -4
  10. package/out/replicache/src/dag/lazy-store.d.ts +1 -1
  11. package/out/replicache/src/dag/lazy-store.d.ts.map +1 -1
  12. package/out/replicache/src/log-options.d.ts +1 -1
  13. package/out/replicache/src/log-options.d.ts.map +1 -1
  14. package/out/replicache/src/persist/collect-idb-databases.d.ts +3 -3
  15. package/out/replicache/src/persist/collect-idb-databases.d.ts.map +1 -1
  16. package/out/replicache/src/replicache-options.d.ts +1 -1
  17. package/out/replicache/src/replicache-options.d.ts.map +1 -1
  18. package/out/shared/src/options.d.ts +1 -1
  19. package/out/shared/src/options.d.ts.map +1 -1
  20. package/out/shared/src/promise-race.d.ts +17 -0
  21. package/out/shared/src/promise-race.d.ts.map +1 -0
  22. package/out/solid.js +19 -6
  23. package/out/solid.js.map +4 -4
  24. package/out/z2s/src/compiler.d.ts +1 -1
  25. package/out/z2s/src/compiler.d.ts.map +1 -1
  26. package/out/z2s/src/compiler.js +1 -1
  27. package/out/z2s/src/compiler.js.map +1 -1
  28. package/out/z2s/src/sql.d.ts.map +1 -1
  29. package/out/z2s/src/sql.js +1 -1
  30. package/out/z2s/src/sql.js.map +1 -1
  31. package/out/zero/package.json +5 -4
  32. package/out/zero-cache/src/config/zero-config.d.ts +12 -0
  33. package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
  34. package/out/zero-cache/src/config/zero-config.js +36 -0
  35. package/out/zero-cache/src/config/zero-config.js.map +1 -1
  36. package/out/zero-cache/src/custom/fetch.d.ts +3 -1
  37. package/out/zero-cache/src/custom/fetch.d.ts.map +1 -1
  38. package/out/zero-cache/src/custom/fetch.js +101 -24
  39. package/out/zero-cache/src/custom/fetch.js.map +1 -1
  40. package/out/zero-cache/src/custom-queries/transform-query.d.ts +2 -6
  41. package/out/zero-cache/src/custom-queries/transform-query.d.ts.map +1 -1
  42. package/out/zero-cache/src/custom-queries/transform-query.js +42 -41
  43. package/out/zero-cache/src/custom-queries/transform-query.js.map +1 -1
  44. package/out/zero-cache/src/db/create.d.ts +4 -0
  45. package/out/zero-cache/src/db/create.d.ts.map +1 -1
  46. package/out/zero-cache/src/db/create.js +7 -9
  47. package/out/zero-cache/src/db/create.js.map +1 -1
  48. package/out/zero-cache/src/db/lite-tables.d.ts.map +1 -1
  49. package/out/zero-cache/src/db/lite-tables.js +38 -17
  50. package/out/zero-cache/src/db/lite-tables.js.map +1 -1
  51. package/out/zero-cache/src/db/pg-to-lite.d.ts.map +1 -1
  52. package/out/zero-cache/src/db/pg-to-lite.js +7 -6
  53. package/out/zero-cache/src/db/pg-to-lite.js.map +1 -1
  54. package/out/zero-cache/src/db/specs.d.ts +24 -19
  55. package/out/zero-cache/src/db/specs.d.ts.map +1 -1
  56. package/out/zero-cache/src/db/specs.js.map +1 -1
  57. package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
  58. package/out/zero-cache/src/server/change-streamer.js +1 -1
  59. package/out/zero-cache/src/server/change-streamer.js.map +1 -1
  60. package/out/zero-cache/src/server/inspector-delegate.d.ts.map +1 -1
  61. package/out/zero-cache/src/server/inspector-delegate.js +5 -1
  62. package/out/zero-cache/src/server/inspector-delegate.js.map +1 -1
  63. package/out/zero-cache/src/server/syncer.d.ts.map +1 -1
  64. package/out/zero-cache/src/server/syncer.js +1 -1
  65. package/out/zero-cache/src/server/syncer.js.map +1 -1
  66. package/out/zero-cache/src/services/change-source/column-metadata.d.ts +65 -0
  67. package/out/zero-cache/src/services/change-source/column-metadata.d.ts.map +1 -0
  68. package/out/zero-cache/src/services/change-source/column-metadata.js +198 -0
  69. package/out/zero-cache/src/services/change-source/column-metadata.js.map +1 -0
  70. package/out/zero-cache/src/services/change-source/pg/change-source.js +1 -0
  71. package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
  72. package/out/zero-cache/src/services/change-source/pg/logical-replication/binary-reader.d.ts +1 -1
  73. package/out/zero-cache/src/services/change-source/pg/logical-replication/binary-reader.d.ts.map +1 -1
  74. package/out/zero-cache/src/services/change-source/pg/logical-replication/binary-reader.js +1 -1
  75. package/out/zero-cache/src/services/change-source/pg/logical-replication/binary-reader.js.map +1 -1
  76. package/out/zero-cache/src/services/change-source/pg/schema/ddl.d.ts +25 -25
  77. package/out/zero-cache/src/services/change-source/pg/schema/published.d.ts +10 -10
  78. package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts +5 -5
  79. package/out/zero-cache/src/services/change-source/protocol/current/data.d.ts +16 -16
  80. package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts +24 -24
  81. package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts +2 -1
  82. package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts.map +1 -1
  83. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +18 -2
  84. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
  85. package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts +8 -8
  86. package/out/zero-cache/src/services/mutagen/mutagen.d.ts.map +1 -1
  87. package/out/zero-cache/src/services/mutagen/mutagen.js +9 -7
  88. package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
  89. package/out/zero-cache/src/services/mutagen/pusher.d.ts +4 -4
  90. package/out/zero-cache/src/services/mutagen/pusher.d.ts.map +1 -1
  91. package/out/zero-cache/src/services/mutagen/pusher.js +74 -56
  92. package/out/zero-cache/src/services/mutagen/pusher.js.map +1 -1
  93. package/out/zero-cache/src/services/view-syncer/client-handler.d.ts +3 -1
  94. package/out/zero-cache/src/services/view-syncer/client-handler.d.ts.map +1 -1
  95. package/out/zero-cache/src/services/view-syncer/client-handler.js +10 -6
  96. package/out/zero-cache/src/services/view-syncer/client-handler.js.map +1 -1
  97. package/out/zero-cache/src/services/view-syncer/client-schema.d.ts.map +1 -1
  98. package/out/zero-cache/src/services/view-syncer/client-schema.js +7 -4
  99. package/out/zero-cache/src/services/view-syncer/client-schema.js.map +1 -1
  100. package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts +4 -4
  101. package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts.map +1 -1
  102. package/out/zero-cache/src/services/view-syncer/cvr-store.js +15 -6
  103. package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
  104. package/out/zero-cache/src/services/view-syncer/cvr.d.ts.map +1 -1
  105. package/out/zero-cache/src/services/view-syncer/cvr.js +4 -2
  106. package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
  107. package/out/zero-cache/src/services/view-syncer/key-columns.d.ts.map +1 -1
  108. package/out/zero-cache/src/services/view-syncer/key-columns.js +4 -2
  109. package/out/zero-cache/src/services/view-syncer/key-columns.js.map +1 -1
  110. package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts +2 -2
  111. package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts.map +1 -1
  112. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +57 -27
  113. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
  114. package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts +11 -6
  115. package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts.map +1 -1
  116. package/out/zero-cache/src/services/view-syncer/snapshotter.js +34 -22
  117. package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
  118. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts +4 -4
  119. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
  120. package/out/zero-cache/src/services/view-syncer/view-syncer.js +111 -51
  121. package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
  122. package/out/zero-cache/src/types/error-with-level.d.ts +9 -0
  123. package/out/zero-cache/src/types/error-with-level.d.ts.map +1 -0
  124. package/out/zero-cache/src/types/error-with-level.js +24 -0
  125. package/out/zero-cache/src/types/error-with-level.js.map +1 -0
  126. package/out/zero-cache/src/types/lite.d.ts +15 -13
  127. package/out/zero-cache/src/types/lite.d.ts.map +1 -1
  128. package/out/zero-cache/src/types/lite.js +17 -5
  129. package/out/zero-cache/src/types/lite.js.map +1 -1
  130. package/out/zero-cache/src/types/pg-data-type.d.ts +73 -0
  131. package/out/zero-cache/src/types/pg-data-type.d.ts.map +1 -0
  132. package/out/zero-cache/src/types/pg-data-type.js +76 -0
  133. package/out/zero-cache/src/types/pg-data-type.js.map +1 -0
  134. package/out/zero-cache/src/types/pg.d.ts +1 -73
  135. package/out/zero-cache/src/types/pg.d.ts.map +1 -1
  136. package/out/zero-cache/src/types/pg.js +0 -77
  137. package/out/zero-cache/src/types/pg.js.map +1 -1
  138. package/out/zero-cache/src/types/processes.d.ts +1 -1
  139. package/out/zero-cache/src/types/processes.d.ts.map +1 -1
  140. package/out/zero-cache/src/types/processes.js.map +1 -1
  141. package/out/zero-cache/src/types/schema-versions.d.ts +7 -3
  142. package/out/zero-cache/src/types/schema-versions.d.ts.map +1 -1
  143. package/out/zero-cache/src/types/schema-versions.js +7 -5
  144. package/out/zero-cache/src/types/schema-versions.js.map +1 -1
  145. package/out/zero-cache/src/types/subscription.d.ts +1 -1
  146. package/out/zero-cache/src/types/subscription.js +1 -1
  147. package/out/zero-cache/src/types/websocket-handoff.d.ts +3 -3
  148. package/out/zero-cache/src/types/websocket-handoff.d.ts.map +1 -1
  149. package/out/zero-cache/src/types/websocket-handoff.js +6 -4
  150. package/out/zero-cache/src/types/websocket-handoff.js.map +1 -1
  151. package/out/zero-cache/src/workers/connection.d.ts +6 -1
  152. package/out/zero-cache/src/workers/connection.d.ts.map +1 -1
  153. package/out/zero-cache/src/workers/connection.js +26 -9
  154. package/out/zero-cache/src/workers/connection.js.map +1 -1
  155. package/out/zero-cache/src/workers/syncer-ws-message-handler.d.ts.map +1 -1
  156. package/out/zero-cache/src/workers/syncer-ws-message-handler.js +7 -1
  157. package/out/zero-cache/src/workers/syncer-ws-message-handler.js.map +1 -1
  158. package/out/zero-cache/src/workers/syncer.d.ts.map +1 -1
  159. package/out/zero-cache/src/workers/syncer.js +21 -1
  160. package/out/zero-cache/src/workers/syncer.js.map +1 -1
  161. package/out/zero-client/src/client/client-error-kind-enum.d.ts +27 -0
  162. package/out/zero-client/src/client/client-error-kind-enum.d.ts.map +1 -0
  163. package/out/zero-client/src/client/client-error-kind.d.ts +5 -0
  164. package/out/zero-client/src/client/client-error-kind.d.ts.map +1 -0
  165. package/out/zero-client/src/client/connection-manager.d.ts +138 -0
  166. package/out/zero-client/src/client/connection-manager.d.ts.map +1 -0
  167. package/out/zero-client/src/client/connection-status-enum.d.ts +11 -0
  168. package/out/zero-client/src/client/connection-status-enum.d.ts.map +1 -0
  169. package/out/zero-client/src/client/connection-status.d.ts +5 -0
  170. package/out/zero-client/src/client/connection-status.d.ts.map +1 -0
  171. package/out/zero-client/src/client/connection.d.ts +45 -0
  172. package/out/zero-client/src/client/connection.d.ts.map +1 -0
  173. package/out/zero-client/src/client/crud.d.ts.map +1 -1
  174. package/out/zero-client/src/client/error.d.ts +192 -0
  175. package/out/zero-client/src/client/error.d.ts.map +1 -0
  176. package/out/zero-client/src/client/ivm-branch.d.ts +2 -2
  177. package/out/zero-client/src/client/ivm-branch.d.ts.map +1 -1
  178. package/out/zero-client/src/client/metric-name.d.ts +5 -0
  179. package/out/zero-client/src/client/metric-name.d.ts.map +1 -0
  180. package/out/zero-client/src/client/metrics.d.ts +12 -9
  181. package/out/zero-client/src/client/metrics.d.ts.map +1 -1
  182. package/out/zero-client/src/client/mutation-tracker.d.ts +5 -4
  183. package/out/zero-client/src/client/mutation-tracker.d.ts.map +1 -1
  184. package/out/zero-client/src/client/options.d.ts +6 -2
  185. package/out/zero-client/src/client/options.d.ts.map +1 -1
  186. package/out/zero-client/src/client/query-manager.d.ts +5 -4
  187. package/out/zero-client/src/client/query-manager.d.ts.map +1 -1
  188. package/out/zero-client/src/client/zero-rep.d.ts +1 -1
  189. package/out/zero-client/src/client/zero-rep.d.ts.map +1 -1
  190. package/out/zero-client/src/client/zero.d.ts +55 -11
  191. package/out/zero-client/src/client/zero.d.ts.map +1 -1
  192. package/out/zero-client/src/mod.d.ts +9 -1
  193. package/out/zero-client/src/mod.d.ts.map +1 -1
  194. package/out/zero-protocol/src/ast.d.ts +2 -0
  195. package/out/zero-protocol/src/ast.d.ts.map +1 -1
  196. package/out/zero-protocol/src/ast.js +1 -0
  197. package/out/zero-protocol/src/ast.js.map +1 -1
  198. package/out/zero-protocol/src/custom-queries.d.ts +75 -4
  199. package/out/zero-protocol/src/custom-queries.d.ts.map +1 -1
  200. package/out/zero-protocol/src/custom-queries.js +18 -1
  201. package/out/zero-protocol/src/custom-queries.js.map +1 -1
  202. package/out/zero-protocol/src/down.d.ts +1 -1
  203. package/out/zero-protocol/src/error-kind-enum.d.ts +65 -0
  204. package/out/zero-protocol/src/error-kind-enum.d.ts.map +1 -1
  205. package/out/zero-protocol/src/error-kind-enum.js +2 -0
  206. package/out/zero-protocol/src/error-kind-enum.js.map +1 -1
  207. package/out/zero-protocol/src/error-origin-enum.d.ts +7 -0
  208. package/out/zero-protocol/src/error-origin-enum.d.ts.map +1 -0
  209. package/out/zero-protocol/src/error-origin-enum.js +4 -0
  210. package/out/zero-protocol/src/error-origin-enum.js.map +1 -0
  211. package/out/zero-protocol/src/error-origin.d.ts +5 -0
  212. package/out/zero-protocol/src/error-origin.d.ts.map +1 -0
  213. package/out/zero-protocol/src/error-origin.js +3 -0
  214. package/out/zero-protocol/src/error-origin.js.map +1 -0
  215. package/out/zero-protocol/src/error-reason-enum.d.ts +15 -0
  216. package/out/zero-protocol/src/error-reason-enum.d.ts.map +1 -0
  217. package/out/zero-protocol/src/error-reason-enum.js +8 -0
  218. package/out/zero-protocol/src/error-reason-enum.js.map +1 -0
  219. package/out/zero-protocol/src/error-reason.d.ts +5 -0
  220. package/out/zero-protocol/src/error-reason.d.ts.map +1 -0
  221. package/out/zero-protocol/src/error-reason.js +3 -0
  222. package/out/zero-protocol/src/error-reason.js.map +1 -0
  223. package/out/zero-protocol/src/error.d.ts +139 -1
  224. package/out/zero-protocol/src/error.d.ts.map +1 -1
  225. package/out/zero-protocol/src/error.js +64 -2
  226. package/out/zero-protocol/src/error.js.map +1 -1
  227. package/out/zero-protocol/src/mutation-id.d.ts +7 -0
  228. package/out/zero-protocol/src/mutation-id.d.ts.map +1 -0
  229. package/out/zero-protocol/src/mutation-id.js +6 -0
  230. package/out/zero-protocol/src/mutation-id.js.map +1 -0
  231. package/out/zero-protocol/src/mutations-patch.d.ts +3 -3
  232. package/out/zero-protocol/src/mutations-patch.d.ts.map +1 -1
  233. package/out/zero-protocol/src/mutations-patch.js +2 -1
  234. package/out/zero-protocol/src/mutations-patch.js.map +1 -1
  235. package/out/zero-protocol/src/protocol-version.d.ts +1 -1
  236. package/out/zero-protocol/src/protocol-version.d.ts.map +1 -1
  237. package/out/zero-protocol/src/protocol-version.js +2 -1
  238. package/out/zero-protocol/src/protocol-version.js.map +1 -1
  239. package/out/zero-protocol/src/push.d.ts +138 -13
  240. package/out/zero-protocol/src/push.d.ts.map +1 -1
  241. package/out/zero-protocol/src/push.js +34 -13
  242. package/out/zero-protocol/src/push.js.map +1 -1
  243. package/out/zero-react/src/mod.d.ts +2 -1
  244. package/out/zero-react/src/mod.d.ts.map +1 -1
  245. package/out/zero-react/src/use-zero-connection-state.d.ts +9 -0
  246. package/out/zero-react/src/use-zero-connection-state.d.ts.map +1 -0
  247. package/out/zero-react/src/use-zero-online.d.ts +2 -0
  248. package/out/zero-react/src/use-zero-online.d.ts.map +1 -1
  249. package/out/zero-schema/src/builder/relationship-builder.d.ts +20 -18
  250. package/out/zero-schema/src/builder/relationship-builder.d.ts.map +1 -1
  251. package/out/zero-schema/src/builder/relationship-builder.js.map +1 -1
  252. package/out/zero-server/src/adapters/drizzle.d.ts +2 -3
  253. package/out/zero-server/src/adapters/drizzle.d.ts.map +1 -1
  254. package/out/zero-server/src/adapters/drizzle.js.map +1 -1
  255. package/out/zero-server/src/adapters/pg.d.ts +1 -0
  256. package/out/zero-server/src/adapters/pg.d.ts.map +1 -1
  257. package/out/zero-server/src/adapters/pg.js.map +1 -1
  258. package/out/zero-server/src/adapters/postgresjs.d.ts +1 -0
  259. package/out/zero-server/src/adapters/postgresjs.d.ts.map +1 -1
  260. package/out/zero-server/src/adapters/postgresjs.js.map +1 -1
  261. package/out/zero-server/src/process-mutations.d.ts +3 -3
  262. package/out/zero-server/src/process-mutations.d.ts.map +1 -1
  263. package/out/zero-server/src/process-mutations.js.map +1 -1
  264. package/out/zero-server/src/schema.js +1 -1
  265. package/out/zero-server/src/schema.js.map +1 -1
  266. package/out/zero-server/src/zql-database.d.ts +1 -1
  267. package/out/zero-server/src/zql-database.d.ts.map +1 -1
  268. package/out/zero-server/src/zql-database.js.map +1 -1
  269. package/out/zero-solid/src/mod.d.ts +3 -2
  270. package/out/zero-solid/src/mod.d.ts.map +1 -1
  271. package/out/zero-solid/src/use-zero-connection-state.d.ts +10 -0
  272. package/out/zero-solid/src/use-zero-connection-state.d.ts.map +1 -0
  273. package/out/zero-solid/src/use-zero-online.d.ts +1 -7
  274. package/out/zero-solid/src/use-zero-online.d.ts.map +1 -1
  275. package/out/zero.js +13 -3
  276. package/out/zql/src/builder/builder.d.ts +14 -1
  277. package/out/zql/src/builder/builder.d.ts.map +1 -1
  278. package/out/zql/src/builder/builder.js +33 -2
  279. package/out/zql/src/builder/builder.js.map +1 -1
  280. package/out/zql/src/ivm/data.d.ts +1 -1
  281. package/out/zql/src/ivm/data.d.ts.map +1 -1
  282. package/out/zql/src/ivm/data.js.map +1 -1
  283. package/out/zql/src/ivm/filter-push.d.ts +1 -1
  284. package/out/zql/src/ivm/filter-push.d.ts.map +1 -1
  285. package/out/zql/src/ivm/filter-push.js.map +1 -1
  286. package/out/zql/src/ivm/memory-source.d.ts +5 -5
  287. package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
  288. package/out/zql/src/ivm/memory-source.js +0 -16
  289. package/out/zql/src/ivm/memory-source.js.map +1 -1
  290. package/out/zql/src/ivm/source.d.ts +2 -6
  291. package/out/zql/src/ivm/source.d.ts.map +1 -1
  292. package/out/zql/src/ivm/take.d.ts +1 -1
  293. package/out/zql/src/ivm/take.d.ts.map +1 -1
  294. package/out/zql/src/ivm/take.js.map +1 -1
  295. package/out/zql/src/planner/planner-builder.d.ts +14 -0
  296. package/out/zql/src/planner/planner-builder.d.ts.map +1 -0
  297. package/out/zql/src/planner/planner-builder.js +180 -0
  298. package/out/zql/src/planner/planner-builder.js.map +1 -0
  299. package/out/zql/src/planner/planner-connection.d.ts +119 -0
  300. package/out/zql/src/planner/planner-connection.d.ts.map +1 -0
  301. package/out/zql/src/planner/planner-connection.js +301 -0
  302. package/out/zql/src/planner/planner-connection.js.map +1 -0
  303. package/out/zql/src/planner/planner-constraint.d.ts +14 -0
  304. package/out/zql/src/planner/planner-constraint.d.ts.map +1 -0
  305. package/out/zql/src/planner/planner-constraint.js +12 -0
  306. package/out/zql/src/planner/planner-constraint.js.map +1 -0
  307. package/out/zql/src/planner/planner-debug.d.ts +118 -0
  308. package/out/zql/src/planner/planner-debug.d.ts.map +1 -0
  309. package/out/zql/src/planner/planner-debug.js +125 -0
  310. package/out/zql/src/planner/planner-debug.js.map +1 -0
  311. package/out/zql/src/planner/planner-fan-in.d.ts +37 -0
  312. package/out/zql/src/planner/planner-fan-in.d.ts.map +1 -0
  313. package/out/zql/src/planner/planner-fan-in.js +149 -0
  314. package/out/zql/src/planner/planner-fan-in.js.map +1 -0
  315. package/out/zql/src/planner/planner-fan-out.d.ts +21 -0
  316. package/out/zql/src/planner/planner-fan-out.d.ts.map +1 -0
  317. package/out/zql/src/planner/planner-fan-out.js +45 -0
  318. package/out/zql/src/planner/planner-fan-out.js.map +1 -0
  319. package/out/zql/src/planner/planner-graph.d.ts +103 -0
  320. package/out/zql/src/planner/planner-graph.d.ts.map +1 -0
  321. package/out/zql/src/planner/planner-graph.js +411 -0
  322. package/out/zql/src/planner/planner-graph.js.map +1 -0
  323. package/out/zql/src/planner/planner-join.d.ts +81 -0
  324. package/out/zql/src/planner/planner-join.d.ts.map +1 -0
  325. package/out/zql/src/planner/planner-join.js +246 -0
  326. package/out/zql/src/planner/planner-join.js.map +1 -0
  327. package/out/zql/src/planner/planner-node.d.ts +21 -0
  328. package/out/zql/src/planner/planner-node.d.ts.map +1 -0
  329. package/out/zql/src/planner/planner-node.js +2 -0
  330. package/out/zql/src/planner/planner-node.js.map +1 -0
  331. package/out/zql/src/planner/planner-source.d.ts +11 -0
  332. package/out/zql/src/planner/planner-source.d.ts.map +1 -0
  333. package/out/zql/src/planner/planner-source.js +13 -0
  334. package/out/zql/src/planner/planner-source.js.map +1 -0
  335. package/out/zql/src/planner/planner-terminus.d.ts +16 -0
  336. package/out/zql/src/planner/planner-terminus.d.ts.map +1 -0
  337. package/out/zql/src/planner/planner-terminus.js +28 -0
  338. package/out/zql/src/planner/planner-terminus.js.map +1 -0
  339. package/out/zql/src/query/expression.d.ts +2 -2
  340. package/out/zql/src/query/expression.d.ts.map +1 -1
  341. package/out/zql/src/query/expression.js.map +1 -1
  342. package/out/zql/src/query/query-delegate.d.ts +3 -3
  343. package/out/zql/src/query/query-delegate.d.ts.map +1 -1
  344. package/out/zql/src/query/query-impl.d.ts +8 -8
  345. package/out/zql/src/query/query-impl.d.ts.map +1 -1
  346. package/out/zql/src/query/query-impl.js +0 -6
  347. package/out/zql/src/query/query-impl.js.map +1 -1
  348. package/out/zql/src/query/query.d.ts +3 -3
  349. package/out/zql/src/query/query.d.ts.map +1 -1
  350. package/out/zql/src/query/static-query.d.ts +1 -1
  351. package/out/zql/src/query/static-query.d.ts.map +1 -1
  352. package/out/zql/src/query/static-query.js.map +1 -1
  353. package/out/zql/src/query/typed-view.d.ts +1 -1
  354. package/out/zql/src/query/typed-view.d.ts.map +1 -1
  355. package/out/zqlite/src/query-delegate.d.ts +1 -1
  356. package/out/zqlite/src/query-delegate.d.ts.map +1 -1
  357. package/out/zqlite/src/query-delegate.js.map +1 -1
  358. package/out/zqlite/src/sqlite-cost-model.d.ts +17 -0
  359. package/out/zqlite/src/sqlite-cost-model.d.ts.map +1 -0
  360. package/out/zqlite/src/sqlite-cost-model.js +134 -0
  361. package/out/zqlite/src/sqlite-cost-model.js.map +1 -0
  362. package/out/zqlite/src/table-source.d.ts +4 -4
  363. package/out/zqlite/src/table-source.d.ts.map +1 -1
  364. package/out/zqlite/src/table-source.js +0 -16
  365. package/out/zqlite/src/table-source.js.map +1 -1
  366. package/package.json +5 -4
  367. package/out/chunk-JS2ABYXG.js.map +0 -7
  368. package/out/chunk-PIXC6O7W.js.map +0 -7
  369. package/out/zero-cache/src/types/error-for-client.d.ts +0 -23
  370. package/out/zero-cache/src/types/error-for-client.d.ts.map +0 -1
  371. package/out/zero-cache/src/types/error-for-client.js +0 -28
  372. package/out/zero-cache/src/types/error-for-client.js.map +0 -1
  373. package/out/zero-client/src/client/connection-state-enum.d.ts +0 -7
  374. package/out/zero-client/src/client/connection-state-enum.d.ts.map +0 -1
  375. package/out/zero-client/src/client/ping-result-enum.d.ts +0 -5
  376. package/out/zero-client/src/client/ping-result-enum.d.ts.map +0 -1
  377. package/out/zero-client/src/client/server-error.d.ts +0 -15
  378. package/out/zero-client/src/client/server-error.d.ts.map +0 -1
  379. package/out/zql/src/query/assert-no-not-exists.d.ts +0 -17
  380. package/out/zql/src/query/assert-no-not-exists.d.ts.map +0 -1
  381. package/out/zql/src/query/assert-no-not-exists.js +0 -40
  382. package/out/zql/src/query/assert-no-not-exists.js.map +0 -1
  383. /package/out/{lazy-inspector-NCBESZMS.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-VHLCXJ6Q.js";
127
+ } from "./chunk-TJFNGO7E.js";
127
128
  import {
128
129
  assert,
129
130
  assertArray,
@@ -1511,6 +1512,223 @@ function rejectIfClosed(tx) {
1511
1512
  return tx.closed ? Promise.reject(new TransactionClosedError()) : void 0;
1512
1513
  }
1513
1514
 
1515
+ // ../zero-protocol/src/error-kind-enum.ts
1516
+ var error_kind_enum_exports = {};
1517
+ __export(error_kind_enum_exports, {
1518
+ AuthInvalidated: () => AuthInvalidated,
1519
+ ClientNotFound: () => ClientNotFound,
1520
+ Internal: () => Internal,
1521
+ InvalidConnectionRequest: () => InvalidConnectionRequest,
1522
+ InvalidConnectionRequestBaseCookie: () => InvalidConnectionRequestBaseCookie,
1523
+ InvalidConnectionRequestClientDeleted: () => InvalidConnectionRequestClientDeleted,
1524
+ InvalidConnectionRequestLastMutationID: () => InvalidConnectionRequestLastMutationID,
1525
+ InvalidMessage: () => InvalidMessage,
1526
+ InvalidPush: () => InvalidPush,
1527
+ MutationFailed: () => MutationFailed,
1528
+ MutationRateLimited: () => MutationRateLimited,
1529
+ PushFailed: () => PushFailed,
1530
+ Rebalance: () => Rebalance,
1531
+ Rehome: () => Rehome,
1532
+ SchemaVersionNotSupported: () => SchemaVersionNotSupported,
1533
+ ServerOverloaded: () => ServerOverloaded,
1534
+ TransformFailed: () => TransformFailed,
1535
+ Unauthorized: () => Unauthorized,
1536
+ VersionNotSupported: () => VersionNotSupported
1537
+ });
1538
+ var AuthInvalidated = "AuthInvalidated";
1539
+ var ClientNotFound = "ClientNotFound";
1540
+ var InvalidConnectionRequest = "InvalidConnectionRequest";
1541
+ var InvalidConnectionRequestBaseCookie = "InvalidConnectionRequestBaseCookie";
1542
+ var InvalidConnectionRequestLastMutationID = "InvalidConnectionRequestLastMutationID";
1543
+ var InvalidConnectionRequestClientDeleted = "InvalidConnectionRequestClientDeleted";
1544
+ var InvalidMessage = "InvalidMessage";
1545
+ var InvalidPush = "InvalidPush";
1546
+ var PushFailed = "PushFailed";
1547
+ var MutationFailed = "MutationFailed";
1548
+ var MutationRateLimited = "MutationRateLimited";
1549
+ var Rebalance = "Rebalance";
1550
+ var Rehome = "Rehome";
1551
+ var TransformFailed = "TransformFailed";
1552
+ var Unauthorized = "Unauthorized";
1553
+ var VersionNotSupported = "VersionNotSupported";
1554
+ var SchemaVersionNotSupported = "SchemaVersionNotSupported";
1555
+ var ServerOverloaded = "ServerOverloaded";
1556
+ var Internal = "Internal";
1557
+
1558
+ // ../zero-protocol/src/error-origin-enum.ts
1559
+ var error_origin_enum_exports = {};
1560
+ __export(error_origin_enum_exports, {
1561
+ Client: () => Client,
1562
+ Server: () => Server,
1563
+ ZeroCache: () => ZeroCache
1564
+ });
1565
+ var Client = "client";
1566
+ var Server = "server";
1567
+ var ZeroCache = "zeroCache";
1568
+
1569
+ // ../zero-protocol/src/error-reason-enum.ts
1570
+ var error_reason_enum_exports = {};
1571
+ __export(error_reason_enum_exports, {
1572
+ Database: () => Database,
1573
+ HTTP: () => HTTP,
1574
+ Internal: () => Internal2,
1575
+ OutOfOrderMutation: () => OutOfOrderMutation,
1576
+ Parse: () => Parse,
1577
+ Timeout: () => Timeout,
1578
+ UnsupportedPushVersion: () => UnsupportedPushVersion
1579
+ });
1580
+ var Database = "database";
1581
+ var Parse = "parse";
1582
+ var OutOfOrderMutation = "oooMutation";
1583
+ var UnsupportedPushVersion = "unsupportedPushVersion";
1584
+ var Internal2 = "internal";
1585
+ var HTTP = "http";
1586
+ var Timeout = "timeout";
1587
+
1588
+ // ../zero-protocol/src/mutation-id.ts
1589
+ var mutationIDSchema = valita_exports.object({
1590
+ id: valita_exports.number(),
1591
+ clientID: valita_exports.string()
1592
+ });
1593
+
1594
+ // ../zero-protocol/src/error.ts
1595
+ var basicErrorKindSchema = literalUnion(
1596
+ error_kind_enum_exports.AuthInvalidated,
1597
+ error_kind_enum_exports.ClientNotFound,
1598
+ error_kind_enum_exports.InvalidConnectionRequest,
1599
+ error_kind_enum_exports.InvalidConnectionRequestBaseCookie,
1600
+ error_kind_enum_exports.InvalidConnectionRequestLastMutationID,
1601
+ error_kind_enum_exports.InvalidConnectionRequestClientDeleted,
1602
+ error_kind_enum_exports.InvalidMessage,
1603
+ error_kind_enum_exports.InvalidPush,
1604
+ error_kind_enum_exports.MutationRateLimited,
1605
+ error_kind_enum_exports.MutationFailed,
1606
+ error_kind_enum_exports.Unauthorized,
1607
+ error_kind_enum_exports.VersionNotSupported,
1608
+ error_kind_enum_exports.SchemaVersionNotSupported,
1609
+ error_kind_enum_exports.Internal
1610
+ );
1611
+ var basicErrorBodySchema = valita_exports.object({
1612
+ kind: basicErrorKindSchema,
1613
+ message: valita_exports.string(),
1614
+ // this is optional for backwards compatibility
1615
+ origin: literalUnion(error_origin_enum_exports.Server, error_origin_enum_exports.ZeroCache).optional()
1616
+ });
1617
+ var backoffErrorKindSchema = literalUnion(
1618
+ error_kind_enum_exports.Rebalance,
1619
+ error_kind_enum_exports.Rehome,
1620
+ error_kind_enum_exports.ServerOverloaded
1621
+ );
1622
+ var backoffBodySchema = valita_exports.object({
1623
+ kind: backoffErrorKindSchema,
1624
+ message: valita_exports.string(),
1625
+ minBackoffMs: valita_exports.number().optional(),
1626
+ maxBackoffMs: valita_exports.number().optional(),
1627
+ // Query parameters to send in the next reconnect. In the event of
1628
+ // a conflict, these will be overridden by the parameters used by
1629
+ // the client; it is the responsibility of the server to avoid
1630
+ // parameter name conflicts.
1631
+ //
1632
+ // The parameters will only be added to the immediately following
1633
+ // reconnect, and not after that.
1634
+ reconnectParams: valita_exports.record(valita_exports.string()).optional(),
1635
+ origin: valita_exports.literal(error_origin_enum_exports.ZeroCache).optional()
1636
+ });
1637
+ var pushFailedErrorKindSchema = valita_exports.literal(error_kind_enum_exports.PushFailed);
1638
+ var transformFailedErrorKindSchema = valita_exports.literal(error_kind_enum_exports.TransformFailed);
1639
+ var errorKindSchema = valita_exports.union(
1640
+ basicErrorKindSchema,
1641
+ backoffErrorKindSchema,
1642
+ pushFailedErrorKindSchema,
1643
+ transformFailedErrorKindSchema
1644
+ );
1645
+ var pushFailedBaseSchema = valita_exports.object({
1646
+ kind: pushFailedErrorKindSchema,
1647
+ details: jsonSchema.optional(),
1648
+ mutationIDs: valita_exports.array(mutationIDSchema),
1649
+ message: valita_exports.string()
1650
+ });
1651
+ var pushFailedBodySchema = valita_exports.union(
1652
+ pushFailedBaseSchema.extend({
1653
+ origin: valita_exports.literal(error_origin_enum_exports.Server),
1654
+ reason: literalUnion(
1655
+ error_reason_enum_exports.Database,
1656
+ error_reason_enum_exports.Parse,
1657
+ error_reason_enum_exports.OutOfOrderMutation,
1658
+ error_reason_enum_exports.UnsupportedPushVersion,
1659
+ error_reason_enum_exports.Internal
1660
+ )
1661
+ }),
1662
+ pushFailedBaseSchema.extend({
1663
+ origin: valita_exports.literal(error_origin_enum_exports.ZeroCache),
1664
+ reason: valita_exports.literal(error_reason_enum_exports.HTTP),
1665
+ status: valita_exports.number(),
1666
+ bodyPreview: valita_exports.string().optional()
1667
+ }),
1668
+ pushFailedBaseSchema.extend({
1669
+ origin: valita_exports.literal(error_origin_enum_exports.ZeroCache),
1670
+ reason: literalUnion(
1671
+ error_reason_enum_exports.Timeout,
1672
+ error_reason_enum_exports.Parse,
1673
+ error_reason_enum_exports.Internal
1674
+ )
1675
+ })
1676
+ );
1677
+ var transformFailedBaseSchema = valita_exports.object({
1678
+ kind: transformFailedErrorKindSchema,
1679
+ details: jsonSchema.optional(),
1680
+ queryIDs: valita_exports.array(valita_exports.string()),
1681
+ message: valita_exports.string()
1682
+ });
1683
+ var transformFailedBodySchema = valita_exports.union(
1684
+ transformFailedBaseSchema.extend({
1685
+ origin: valita_exports.literal(error_origin_enum_exports.Server),
1686
+ reason: literalUnion(
1687
+ error_reason_enum_exports.Database,
1688
+ error_reason_enum_exports.Parse,
1689
+ error_reason_enum_exports.Internal
1690
+ )
1691
+ }),
1692
+ transformFailedBaseSchema.extend({
1693
+ origin: valita_exports.literal(error_origin_enum_exports.ZeroCache),
1694
+ reason: valita_exports.literal(error_reason_enum_exports.HTTP),
1695
+ status: valita_exports.number(),
1696
+ bodyPreview: valita_exports.string().optional()
1697
+ }),
1698
+ transformFailedBaseSchema.extend({
1699
+ origin: valita_exports.literal(error_origin_enum_exports.ZeroCache),
1700
+ reason: literalUnion(
1701
+ error_reason_enum_exports.Timeout,
1702
+ error_reason_enum_exports.Parse,
1703
+ error_reason_enum_exports.Internal
1704
+ )
1705
+ })
1706
+ );
1707
+ var errorBodySchema = valita_exports.union(
1708
+ basicErrorBodySchema,
1709
+ backoffBodySchema,
1710
+ pushFailedBodySchema,
1711
+ transformFailedBodySchema
1712
+ );
1713
+ var errorMessageSchema = valita_exports.tuple([
1714
+ valita_exports.literal("error"),
1715
+ errorBodySchema
1716
+ ]);
1717
+ var ProtocolError = class extends Error {
1718
+ errorBody;
1719
+ constructor(errorBody, options) {
1720
+ super(errorBody.message, options);
1721
+ this.name = "ProtocolError";
1722
+ this.errorBody = errorBody;
1723
+ }
1724
+ get kind() {
1725
+ return this.errorBody.kind;
1726
+ }
1727
+ };
1728
+ function isProtocolError(error) {
1729
+ return error instanceof ProtocolError;
1730
+ }
1731
+
1514
1732
  // ../zero-protocol/src/custom-queries.ts
1515
1733
  var transformRequestBodySchema = valita_exports.array(
1516
1734
  valita_exports.object({
@@ -1531,16 +1749,25 @@ var appQueryErrorSchema = valita_exports.object({
1531
1749
  details: jsonSchema
1532
1750
  });
1533
1751
  var zeroErrorSchema = valita_exports.object({
1752
+ /** @deprecated */
1534
1753
  error: valita_exports.literal("zero"),
1754
+ /** @deprecated */
1535
1755
  id: valita_exports.string(),
1756
+ /** @deprecated */
1536
1757
  name: valita_exports.string(),
1758
+ /** @deprecated */
1537
1759
  details: jsonSchema
1538
1760
  });
1539
1761
  var httpQueryErrorSchema = valita_exports.object({
1762
+ /** @deprecated */
1540
1763
  error: valita_exports.literal("http"),
1764
+ /** @deprecated */
1541
1765
  id: valita_exports.string(),
1766
+ /** @deprecated */
1542
1767
  name: valita_exports.string(),
1768
+ /** @deprecated */
1543
1769
  status: valita_exports.number(),
1770
+ /** @deprecated */
1544
1771
  details: jsonSchema
1545
1772
  });
1546
1773
  var erroredQuerySchema = valita_exports.union(
@@ -1559,49 +1786,18 @@ var transformErrorMessageSchema = valita_exports.tuple([
1559
1786
  valita_exports.literal("transformError"),
1560
1787
  valita_exports.array(erroredQuerySchema)
1561
1788
  ]);
1562
- var transformResponseMessageSchema = valita_exports.tuple([
1789
+ var transformFailedMessageSchema = valita_exports.tuple([
1790
+ valita_exports.literal("transformFailed"),
1791
+ transformFailedBodySchema
1792
+ ]);
1793
+ var transformOkMessageSchema = valita_exports.tuple([
1563
1794
  valita_exports.literal("transformed"),
1564
1795
  transformResponseBodySchema
1565
1796
  ]);
1566
-
1567
- // ../zero-protocol/src/error-kind-enum.ts
1568
- var error_kind_enum_exports = {};
1569
- __export(error_kind_enum_exports, {
1570
- AuthInvalidated: () => AuthInvalidated,
1571
- ClientNotFound: () => ClientNotFound,
1572
- Internal: () => Internal,
1573
- InvalidConnectionRequest: () => InvalidConnectionRequest,
1574
- InvalidConnectionRequestBaseCookie: () => InvalidConnectionRequestBaseCookie,
1575
- InvalidConnectionRequestClientDeleted: () => InvalidConnectionRequestClientDeleted,
1576
- InvalidConnectionRequestLastMutationID: () => InvalidConnectionRequestLastMutationID,
1577
- InvalidMessage: () => InvalidMessage,
1578
- InvalidPush: () => InvalidPush,
1579
- MutationFailed: () => MutationFailed,
1580
- MutationRateLimited: () => MutationRateLimited,
1581
- Rebalance: () => Rebalance,
1582
- Rehome: () => Rehome,
1583
- SchemaVersionNotSupported: () => SchemaVersionNotSupported,
1584
- ServerOverloaded: () => ServerOverloaded,
1585
- Unauthorized: () => Unauthorized,
1586
- VersionNotSupported: () => VersionNotSupported
1587
- });
1588
- var AuthInvalidated = "AuthInvalidated";
1589
- var ClientNotFound = "ClientNotFound";
1590
- var InvalidConnectionRequest = "InvalidConnectionRequest";
1591
- var InvalidConnectionRequestBaseCookie = "InvalidConnectionRequestBaseCookie";
1592
- var InvalidConnectionRequestLastMutationID = "InvalidConnectionRequestLastMutationID";
1593
- var InvalidConnectionRequestClientDeleted = "InvalidConnectionRequestClientDeleted";
1594
- var InvalidMessage = "InvalidMessage";
1595
- var InvalidPush = "InvalidPush";
1596
- var MutationFailed = "MutationFailed";
1597
- var MutationRateLimited = "MutationRateLimited";
1598
- var Rebalance = "Rebalance";
1599
- var Rehome = "Rehome";
1600
- var Unauthorized = "Unauthorized";
1601
- var VersionNotSupported = "VersionNotSupported";
1602
- var SchemaVersionNotSupported = "SchemaVersionNotSupported";
1603
- var ServerOverloaded = "ServerOverloaded";
1604
- var Internal = "Internal";
1797
+ var transformResponseMessageSchema = valita_exports.union(
1798
+ transformOkMessageSchema,
1799
+ transformFailedMessageSchema
1800
+ );
1605
1801
 
1606
1802
  // ../zero-schema/src/builder/table-builder.ts
1607
1803
  function table(name) {
@@ -1611,14 +1807,14 @@ function table(name) {
1611
1807
  primaryKey: []
1612
1808
  });
1613
1809
  }
1614
- function string2() {
1810
+ function string4() {
1615
1811
  return new ColumnBuilder({
1616
1812
  type: "string",
1617
1813
  optional: false,
1618
1814
  customType: null
1619
1815
  });
1620
1816
  }
1621
- function number2() {
1817
+ function number4() {
1622
1818
  return new ColumnBuilder({
1623
1819
  type: "number",
1624
1820
  optional: false,
@@ -2172,6 +2368,170 @@ function makeID(row, schema) {
2172
2368
  return JSON.stringify(schema.primaryKey.map((k) => row[k]));
2173
2369
  }
2174
2370
 
2371
+ // ../zero-client/src/client/client-error-kind-enum.ts
2372
+ var client_error_kind_enum_exports = {};
2373
+ __export(client_error_kind_enum_exports, {
2374
+ AbruptClose: () => AbruptClose,
2375
+ CleanClose: () => CleanClose,
2376
+ ClientClosed: () => ClientClosed,
2377
+ ConnectTimeout: () => ConnectTimeout,
2378
+ DisconnectTimeout: () => DisconnectTimeout,
2379
+ Hidden: () => Hidden,
2380
+ Internal: () => Internal3,
2381
+ InvalidMessage: () => InvalidMessage2,
2382
+ NoSocketOrigin: () => NoSocketOrigin,
2383
+ PingTimeout: () => PingTimeout,
2384
+ PullTimeout: () => PullTimeout,
2385
+ UnexpectedBaseCookie: () => UnexpectedBaseCookie,
2386
+ UserDisconnect: () => UserDisconnect
2387
+ });
2388
+ var AbruptClose = "AbruptClose";
2389
+ var CleanClose = "CleanClose";
2390
+ var ClientClosed = "ClientClosed";
2391
+ var ConnectTimeout = "ConnectTimeout";
2392
+ var DisconnectTimeout = "DisconnectTimeout";
2393
+ var UnexpectedBaseCookie = "UnexpectedBaseCookie";
2394
+ var PingTimeout = "PingTimeout";
2395
+ var PullTimeout = "PullTimeout";
2396
+ var Hidden = "Hidden";
2397
+ var NoSocketOrigin = "NoSocketOrigin";
2398
+ var InvalidMessage2 = "InvalidMessage";
2399
+ var UserDisconnect = "UserDisconnect";
2400
+ var Internal3 = "Internal";
2401
+
2402
+ // ../zero-client/src/client/connection-status-enum.ts
2403
+ var connection_status_enum_exports = {};
2404
+ __export(connection_status_enum_exports, {
2405
+ Closed: () => Closed,
2406
+ Connected: () => Connected,
2407
+ Connecting: () => Connecting,
2408
+ Disconnected: () => Disconnected,
2409
+ Error: () => Error2
2410
+ });
2411
+ var Disconnected = "disconnected";
2412
+ var Connecting = "connecting";
2413
+ var Connected = "connected";
2414
+ var Error2 = "error";
2415
+ var Closed = "closed";
2416
+
2417
+ // ../zero-client/src/client/error.ts
2418
+ var ClientError = class extends Error {
2419
+ errorBody;
2420
+ constructor(errorBody, options) {
2421
+ super(errorBody.message, options);
2422
+ this.name = "ClientError";
2423
+ this.errorBody = { ...errorBody, origin: error_origin_enum_exports.Client };
2424
+ }
2425
+ get kind() {
2426
+ return this.errorBody.kind;
2427
+ }
2428
+ };
2429
+ function isServerError(ex) {
2430
+ return isProtocolError(ex) && (ex.errorBody.origin === error_origin_enum_exports.Server || ex.errorBody.origin === error_origin_enum_exports.ZeroCache);
2431
+ }
2432
+ function isAuthError(ex) {
2433
+ if (isServerError(ex)) {
2434
+ if (isAuthErrorKind(ex.errorBody.kind)) {
2435
+ return true;
2436
+ }
2437
+ 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)) {
2438
+ return true;
2439
+ }
2440
+ }
2441
+ return false;
2442
+ }
2443
+ function isAuthErrorKind(kind) {
2444
+ return kind === error_kind_enum_exports.AuthInvalidated || kind === error_kind_enum_exports.Unauthorized;
2445
+ }
2446
+ function getBackoffParams(error) {
2447
+ if (isServerError(error)) {
2448
+ switch (error.errorBody.kind) {
2449
+ case error_kind_enum_exports.Rebalance:
2450
+ case error_kind_enum_exports.Rehome:
2451
+ case error_kind_enum_exports.ServerOverloaded:
2452
+ return error.errorBody;
2453
+ }
2454
+ }
2455
+ return void 0;
2456
+ }
2457
+ function isClientError(ex) {
2458
+ return ex instanceof ClientError && ex.errorBody.origin === error_origin_enum_exports.Client;
2459
+ }
2460
+ var NO_STATUS_TRANSITION = "NO_STATUS_TRANSITION";
2461
+ function getErrorConnectionTransition(ex) {
2462
+ if (isClientError(ex)) {
2463
+ switch (ex.kind) {
2464
+ // Connecting errors that should continue in the current state
2465
+ case client_error_kind_enum_exports.AbruptClose:
2466
+ case client_error_kind_enum_exports.CleanClose:
2467
+ case client_error_kind_enum_exports.ConnectTimeout:
2468
+ case client_error_kind_enum_exports.PingTimeout:
2469
+ case client_error_kind_enum_exports.PullTimeout:
2470
+ case client_error_kind_enum_exports.Hidden:
2471
+ case client_error_kind_enum_exports.NoSocketOrigin:
2472
+ return { status: NO_STATUS_TRANSITION, reason: ex };
2473
+ // Fatal errors that should transition to error state
2474
+ case client_error_kind_enum_exports.UnexpectedBaseCookie:
2475
+ case client_error_kind_enum_exports.Internal:
2476
+ case client_error_kind_enum_exports.InvalidMessage:
2477
+ case client_error_kind_enum_exports.UserDisconnect:
2478
+ return { status: connection_status_enum_exports.Error, reason: ex };
2479
+ // Disconnected error (this should already result in a disconnected state)
2480
+ case client_error_kind_enum_exports.DisconnectTimeout:
2481
+ return { status: connection_status_enum_exports.Disconnected, reason: ex };
2482
+ // Closed error (this should already result in a closed state)
2483
+ case client_error_kind_enum_exports.ClientClosed:
2484
+ return { status: connection_status_enum_exports.Closed, reason: ex };
2485
+ default:
2486
+ unreachable(ex.kind);
2487
+ }
2488
+ }
2489
+ if (isAuthError(ex)) {
2490
+ return { status: NO_STATUS_TRANSITION, reason: ex };
2491
+ }
2492
+ if (isServerError(ex)) {
2493
+ switch (ex.kind) {
2494
+ // Errors that should transition to error state
2495
+ case error_kind_enum_exports.ClientNotFound:
2496
+ case error_kind_enum_exports.InvalidConnectionRequest:
2497
+ case error_kind_enum_exports.InvalidConnectionRequestBaseCookie:
2498
+ case error_kind_enum_exports.InvalidConnectionRequestLastMutationID:
2499
+ case error_kind_enum_exports.InvalidConnectionRequestClientDeleted:
2500
+ case error_kind_enum_exports.InvalidMessage:
2501
+ case error_kind_enum_exports.InvalidPush:
2502
+ case error_kind_enum_exports.VersionNotSupported:
2503
+ case error_kind_enum_exports.SchemaVersionNotSupported:
2504
+ case error_kind_enum_exports.Internal:
2505
+ case error_kind_enum_exports.PushFailed:
2506
+ case error_kind_enum_exports.TransformFailed:
2507
+ return { status: connection_status_enum_exports.Error, reason: ex };
2508
+ // Errors that should continue with backoff/retry
2509
+ case error_kind_enum_exports.Rebalance:
2510
+ case error_kind_enum_exports.Rehome:
2511
+ case error_kind_enum_exports.ServerOverloaded:
2512
+ return { status: NO_STATUS_TRANSITION, reason: ex };
2513
+ // Auth errors will eventually transition to needs-auth state
2514
+ // For now, treat them as non-fatal so we can retry
2515
+ case error_kind_enum_exports.AuthInvalidated:
2516
+ case error_kind_enum_exports.Unauthorized:
2517
+ return { status: NO_STATUS_TRANSITION, reason: ex };
2518
+ // Mutation-specific errors don't affect connection state
2519
+ case error_kind_enum_exports.MutationRateLimited:
2520
+ case error_kind_enum_exports.MutationFailed:
2521
+ return { status: NO_STATUS_TRANSITION, reason: ex };
2522
+ default:
2523
+ unreachable(ex.kind);
2524
+ }
2525
+ }
2526
+ return {
2527
+ status: connection_status_enum_exports.Error,
2528
+ reason: new ClientError({
2529
+ kind: client_error_kind_enum_exports.Internal,
2530
+ message: "Unexpected internal error: " + (ex instanceof Error ? ex.message : typeof ex === "string" ? ex : String(ex ?? "Unknown error"))
2531
+ })
2532
+ };
2533
+ }
2534
+
2175
2535
  // ../zero-client/src/client/update-needed-reason-type-enum.ts
2176
2536
  var update_needed_reason_type_enum_exports = {};
2177
2537
  __export(update_needed_reason_type_enum_exports, {
@@ -2185,7 +2545,7 @@ var SchemaVersionNotSupported2 = "SchemaVersionNotSupported";
2185
2545
 
2186
2546
  // ../zero-client/src/client/zero.ts
2187
2547
  import "@rocicorp/logger";
2188
- import { resolver as resolver10 } from "@rocicorp/resolver";
2548
+ import { resolver as resolver11 } from "@rocicorp/resolver";
2189
2549
 
2190
2550
  // ../replicache/src/replicache-impl.ts
2191
2551
  import { Lock } from "@rocicorp/lock";
@@ -6283,6 +6643,24 @@ function validateOptions(options) {
6283
6643
  }
6284
6644
  }
6285
6645
 
6646
+ // ../shared/src/promise-race.ts
6647
+ var NO_PROMISES_MESSAGE = "No promises to race";
6648
+ var wrapPromise = (key, promise) => Promise.resolve(promise).then((result) => ({
6649
+ key,
6650
+ status: "fulfilled",
6651
+ result
6652
+ }));
6653
+ async function promiseRace(promises) {
6654
+ const keys = Object.keys(promises);
6655
+ if (keys.length === 0) {
6656
+ throw new Error(NO_PROMISES_MESSAGE);
6657
+ }
6658
+ const wrapped = keys.map(
6659
+ (key) => wrapPromise(key, promises[key])
6660
+ );
6661
+ return await Promise.race(wrapped);
6662
+ }
6663
+
6286
6664
  // ../shared/src/sentinels.ts
6287
6665
  function emptyFunction() {
6288
6666
  }
@@ -6292,38 +6670,6 @@ function identity(x) {
6292
6670
  return x;
6293
6671
  }
6294
6672
 
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
6673
  // ../zero-protocol/src/delete-clients.ts
6328
6674
  var deleteClientsBodySchema = valita_exports.union(
6329
6675
  readonlyObject({
@@ -6401,56 +6747,6 @@ function encodeSecProtocols(initConnectionMessage, authToken) {
6401
6747
  return encodeURIComponent(btoa(s));
6402
6748
  }
6403
6749
 
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
6750
  // ../zero-protocol/src/mutation-type-enum.ts
6455
6751
  var CRUD = "crud";
6456
6752
  var Custom = "custom";
@@ -6522,17 +6818,17 @@ var pushBodySchema = valita_exports.object({
6522
6818
  requestID: valita_exports.string()
6523
6819
  });
6524
6820
  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
6821
  var appErrorSchema = valita_exports.object({
6530
6822
  error: valita_exports.literal("app"),
6531
6823
  // The user can return any additional data here
6532
6824
  details: jsonSchema.optional()
6533
6825
  });
6534
6826
  var zeroErrorSchema2 = valita_exports.object({
6535
- error: literalUnion("oooMutation", "alreadyProcessed"),
6827
+ error: valita_exports.union(
6828
+ /** @deprecated push oooMutation errors are now represented as ['error', { ... }] messages */
6829
+ valita_exports.literal("oooMutation"),
6830
+ valita_exports.literal("alreadyProcessed")
6831
+ ),
6536
6832
  details: jsonSchema.optional()
6537
6833
  });
6538
6834
  var mutationOkSchema = valita_exports.object({
@@ -6552,28 +6848,33 @@ var pushOkSchema = valita_exports.object({
6552
6848
  mutations: valita_exports.array(mutationResponseSchema)
6553
6849
  });
6554
6850
  var unsupportedPushVersionSchema = valita_exports.object({
6851
+ /** @deprecated */
6555
6852
  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.
6853
+ /** @deprecated */
6559
6854
  mutationIDs: valita_exports.array(mutationIDSchema).optional()
6560
6855
  });
6561
6856
  var unsupportedSchemaVersionSchema = valita_exports.object({
6857
+ /** @deprecated */
6562
6858
  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.
6859
+ /** @deprecated */
6566
6860
  mutationIDs: valita_exports.array(mutationIDSchema).optional()
6567
6861
  });
6568
6862
  var httpErrorSchema = valita_exports.object({
6863
+ /** @deprecated */
6569
6864
  error: valita_exports.literal("http"),
6865
+ /** @deprecated */
6570
6866
  status: valita_exports.number(),
6867
+ /** @deprecated */
6571
6868
  details: valita_exports.string(),
6869
+ /** @deprecated */
6572
6870
  mutationIDs: valita_exports.array(mutationIDSchema).optional()
6573
6871
  });
6574
6872
  var zeroPusherErrorSchema = valita_exports.object({
6873
+ /** @deprecated */
6575
6874
  error: valita_exports.literal("zeroPusher"),
6875
+ /** @deprecated */
6576
6876
  details: valita_exports.string(),
6877
+ /** @deprecated */
6577
6878
  mutationIDs: valita_exports.array(mutationIDSchema).optional()
6578
6879
  });
6579
6880
  var pushErrorSchema = valita_exports.union(
@@ -6582,10 +6883,14 @@ var pushErrorSchema = valita_exports.union(
6582
6883
  httpErrorSchema,
6583
6884
  zeroPusherErrorSchema
6584
6885
  );
6585
- var pushResponseSchema = valita_exports.union(pushOkSchema, pushErrorSchema);
6886
+ var pushResponseBodySchema = valita_exports.union(pushOkSchema, pushErrorSchema);
6887
+ var pushResponseSchema = valita_exports.union(
6888
+ pushResponseBodySchema,
6889
+ pushFailedBodySchema
6890
+ );
6586
6891
  var pushResponseMessageSchema = valita_exports.tuple([
6587
6892
  valita_exports.literal("pushResponse"),
6588
- pushResponseSchema
6893
+ pushResponseBodySchema
6589
6894
  ]);
6590
6895
  var ackMutationResponsesMessageSchema = valita_exports.tuple([
6591
6896
  valita_exports.literal("ackMutationResponses"),
@@ -6750,7 +7055,7 @@ var downstreamSchema = valita_exports.union(
6750
7055
  );
6751
7056
 
6752
7057
  // ../zero-protocol/src/protocol-version.ts
6753
- var PROTOCOL_VERSION = 37;
7058
+ var PROTOCOL_VERSION = 38;
6754
7059
  var MIN_SERVER_SUPPORTED_SYNC_PROTOCOL = 18;
6755
7060
  assert(MIN_SERVER_SUPPORTED_SYNC_PROTOCOL < PROTOCOL_VERSION);
6756
7061
 
@@ -9302,200 +9607,1450 @@ var UnionFanOut = class {
9302
9607
  }
9303
9608
  };
9304
9609
 
9305
- // ../zql/src/query/expression.ts
9306
- var ExpressionBuilder = class {
9307
- #exists;
9308
- constructor(exists) {
9309
- this.#exists = exists;
9310
- this.exists = this.exists.bind(this);
9610
+ // ../zql/src/planner/planner-fan-in.ts
9611
+ var PlannerFanIn = class {
9612
+ kind = "fan-in";
9613
+ #type;
9614
+ #output;
9615
+ #inputs;
9616
+ constructor(inputs) {
9617
+ this.#type = "FI";
9618
+ this.#inputs = inputs;
9311
9619
  }
9312
- get eb() {
9313
- return this;
9620
+ get type() {
9621
+ return this.#type;
9314
9622
  }
9315
- cmp(field, opOrValue, value) {
9316
- return cmp(field, opOrValue, value);
9623
+ closestJoinOrSource() {
9624
+ return "join";
9317
9625
  }
9318
- cmpLit(left, op, right) {
9319
- return {
9320
- type: "simple",
9321
- left: isParameterReference(left) ? left[toStaticParam]() : { type: "literal", value: left },
9322
- right: isParameterReference(right) ? right[toStaticParam]() : { type: "literal", value: right },
9323
- op
9324
- };
9626
+ setOutput(node) {
9627
+ this.#output = node;
9325
9628
  }
9326
- and = and;
9327
- or = or;
9328
- not = not;
9329
- exists = (relationship, cb, options) => this.#exists(relationship, cb, options);
9330
- };
9331
- function and(...conditions) {
9332
- const expressions = filterTrue(filterUndefined(conditions));
9333
- if (expressions.length === 1) {
9334
- return expressions[0];
9629
+ get output() {
9630
+ assert(this.#output !== void 0, "Output not set");
9631
+ return this.#output;
9335
9632
  }
9336
- if (expressions.some(isAlwaysFalse)) {
9337
- return FALSE;
9633
+ reset() {
9634
+ this.#type = "FI";
9338
9635
  }
9339
- return { type: "and", conditions: expressions };
9340
- }
9341
- function or(...conditions) {
9342
- const expressions = filterFalse(filterUndefined(conditions));
9343
- if (expressions.length === 1) {
9344
- return expressions[0];
9636
+ convertToUFI() {
9637
+ this.#type = "UFI";
9345
9638
  }
9346
- if (expressions.some(isAlwaysTrue)) {
9347
- return TRUE;
9639
+ /**
9640
+ * Propagate unlimiting when a parent join is flipped.
9641
+ * Fan-in propagates to all of its inputs.
9642
+ */
9643
+ propagateUnlimitFromFlippedJoin() {
9644
+ for (const input of this.#inputs) {
9645
+ if ("propagateUnlimitFromFlippedJoin" in input && typeof input.propagateUnlimitFromFlippedJoin === "function") {
9646
+ input.propagateUnlimitFromFlippedJoin();
9647
+ }
9648
+ }
9348
9649
  }
9349
- return { type: "or", conditions: expressions };
9350
- }
9351
- function not(expression) {
9352
- switch (expression.type) {
9353
- case "and":
9354
- return {
9355
- type: "or",
9356
- conditions: expression.conditions.map(not)
9357
- };
9358
- case "or":
9359
- return {
9360
- type: "and",
9361
- conditions: expression.conditions.map(not)
9362
- };
9363
- case "correlatedSubquery":
9364
- return {
9365
- type: "correlatedSubquery",
9366
- related: expression.related,
9367
- op: negateOperator(expression.op)
9368
- };
9369
- case "simple":
9370
- return {
9371
- type: "simple",
9372
- op: negateOperator(expression.op),
9373
- left: expression.left,
9374
- right: expression.right
9375
- };
9650
+ estimateCost(branchPattern) {
9651
+ let totalCost = {
9652
+ rows: 0,
9653
+ runningCost: 0,
9654
+ startupCost: 0,
9655
+ selectivity: 0,
9656
+ limit: void 0
9657
+ };
9658
+ branchPattern = branchPattern ?? [];
9659
+ if (this.#type === "FI") {
9660
+ const updatedPattern = [0, ...branchPattern];
9661
+ let maxrows = 0;
9662
+ let maxRunningCost = 0;
9663
+ let maxStartupCost = 0;
9664
+ let noMatchProb = 1;
9665
+ for (const input of this.#inputs) {
9666
+ const cost = input.estimateCost(updatedPattern);
9667
+ if (cost.rows > maxrows) {
9668
+ maxrows = cost.rows;
9669
+ }
9670
+ if (cost.runningCost > maxRunningCost) {
9671
+ maxRunningCost = cost.runningCost;
9672
+ }
9673
+ if (cost.startupCost > maxStartupCost) {
9674
+ maxStartupCost = cost.startupCost;
9675
+ }
9676
+ noMatchProb *= 1 - cost.selectivity;
9677
+ assert(
9678
+ totalCost.limit === void 0 || cost.limit === totalCost.limit,
9679
+ "All FanIn inputs should have the same limit"
9680
+ );
9681
+ totalCost.limit = cost.limit;
9682
+ }
9683
+ totalCost.rows = maxrows;
9684
+ totalCost.runningCost = maxRunningCost;
9685
+ totalCost.startupCost = maxStartupCost;
9686
+ totalCost.selectivity = 1 - noMatchProb;
9687
+ } else {
9688
+ let i = 0;
9689
+ let noMatchProb = 1;
9690
+ for (const input of this.#inputs) {
9691
+ const updatedPattern = [i, ...branchPattern];
9692
+ const cost = input.estimateCost(updatedPattern);
9693
+ totalCost.rows += cost.rows;
9694
+ totalCost.runningCost += cost.runningCost;
9695
+ totalCost.startupCost += cost.startupCost;
9696
+ noMatchProb *= 1 - cost.selectivity;
9697
+ assert(
9698
+ totalCost.limit === void 0 || cost.limit === totalCost.limit,
9699
+ "All FanIn inputs should have the same limit"
9700
+ );
9701
+ totalCost.limit = cost.limit;
9702
+ i++;
9703
+ }
9704
+ totalCost.selectivity = 1 - noMatchProb;
9705
+ }
9706
+ return totalCost;
9376
9707
  }
9377
- }
9378
- function cmp(field, opOrValue, value) {
9379
- let op;
9380
- if (value === void 0) {
9381
- value = opOrValue;
9382
- op = "=";
9383
- } else {
9384
- op = opOrValue;
9708
+ propagateConstraints(branchPattern, constraint, from) {
9709
+ if (this.#type === "FI") {
9710
+ const updatedPattern = [0, ...branchPattern];
9711
+ for (const input of this.#inputs) {
9712
+ input.propagateConstraints(updatedPattern, constraint, from);
9713
+ }
9714
+ return;
9715
+ }
9716
+ let i = 0;
9717
+ for (const input of this.#inputs) {
9718
+ input.propagateConstraints([i, ...branchPattern], constraint, from);
9719
+ i++;
9720
+ }
9385
9721
  }
9386
- return {
9387
- type: "simple",
9388
- left: { type: "column", name: field },
9389
- right: isParameterReference(value) ? value[toStaticParam]() : { type: "literal", value },
9390
- op
9391
- };
9392
- }
9393
- function isParameterReference(value) {
9394
- return value !== null && typeof value === "object" && value[toStaticParam];
9395
- }
9396
- var TRUE = {
9397
- type: "and",
9398
- conditions: []
9399
- };
9400
- var FALSE = {
9401
- type: "or",
9402
- conditions: []
9403
9722
  };
9404
- function isAlwaysTrue(condition) {
9405
- return condition.type === "and" && condition.conditions.length === 0;
9406
- }
9407
- function isAlwaysFalse(condition) {
9408
- return condition.type === "or" && condition.conditions.length === 0;
9409
- }
9410
- function simplifyCondition(c) {
9411
- if (c.type === "simple" || c.type === "correlatedSubquery") {
9412
- return c;
9723
+
9724
+ // ../zql/src/planner/planner-fan-out.ts
9725
+ var PlannerFanOut = class {
9726
+ kind = "fan-out";
9727
+ #type;
9728
+ #outputs = [];
9729
+ #input;
9730
+ constructor(input) {
9731
+ this.#type = "FO";
9732
+ this.#input = input;
9413
9733
  }
9414
- if (c.conditions.length === 1) {
9415
- return simplifyCondition(c.conditions[0]);
9734
+ get type() {
9735
+ return this.#type;
9416
9736
  }
9417
- const conditions = flatten(c.type, c.conditions.map(simplifyCondition));
9418
- if (c.type === "and" && conditions.some(isAlwaysFalse)) {
9419
- return FALSE;
9737
+ addOutput(node) {
9738
+ this.#outputs.push(node);
9420
9739
  }
9421
- if (c.type === "or" && conditions.some(isAlwaysTrue)) {
9422
- return TRUE;
9740
+ get outputs() {
9741
+ return this.#outputs;
9423
9742
  }
9424
- return {
9425
- type: c.type,
9426
- conditions
9427
- };
9428
- }
9429
- function flatten(type, conditions) {
9430
- const flattened = [];
9431
- for (const c of conditions) {
9432
- if (c.type === type) {
9433
- flattened.push(...c.conditions);
9434
- } else {
9435
- flattened.push(c);
9743
+ closestJoinOrSource() {
9744
+ return this.#input.closestJoinOrSource();
9745
+ }
9746
+ propagateConstraints(branchPattern, constraint, _from) {
9747
+ this.#input.propagateConstraints(branchPattern, constraint, this);
9748
+ }
9749
+ estimateCost(branchPattern) {
9750
+ return this.#input.estimateCost(branchPattern);
9751
+ }
9752
+ convertToUFO() {
9753
+ this.#type = "UFO";
9754
+ }
9755
+ reset() {
9756
+ this.#type = "FO";
9757
+ }
9758
+ /**
9759
+ * Propagate unlimiting when a parent join is flipped.
9760
+ * Fan-out propagates to its input.
9761
+ */
9762
+ propagateUnlimitFromFlippedJoin() {
9763
+ if ("propagateUnlimitFromFlippedJoin" in this.#input && typeof this.#input.propagateUnlimitFromFlippedJoin === "function") {
9764
+ this.#input.propagateUnlimitFromFlippedJoin();
9436
9765
  }
9437
9766
  }
9438
- return flattened;
9439
- }
9440
- var negateSimpleOperatorMap = {
9441
- ["="]: "!=",
9442
- ["!="]: "=",
9443
- ["<"]: ">=",
9444
- [">"]: "<=",
9445
- [">="]: "<",
9446
- ["<="]: ">",
9447
- ["IN"]: "NOT IN",
9448
- ["NOT IN"]: "IN",
9449
- ["LIKE"]: "NOT LIKE",
9450
- ["NOT LIKE"]: "LIKE",
9451
- ["ILIKE"]: "NOT ILIKE",
9452
- ["NOT ILIKE"]: "ILIKE",
9453
- ["IS"]: "IS NOT",
9454
- ["IS NOT"]: "IS"
9455
- };
9456
- var negateOperatorMap = {
9457
- ...negateSimpleOperatorMap,
9458
- ["EXISTS"]: "NOT EXISTS",
9459
- ["NOT EXISTS"]: "EXISTS"
9460
9767
  };
9461
- function negateOperator(op) {
9462
- return must(negateOperatorMap[op]);
9463
- }
9464
- function filterUndefined(array9) {
9465
- return array9.filter((e) => e !== void 0);
9466
- }
9467
- function filterTrue(conditions) {
9468
- return conditions.filter((c) => !isAlwaysTrue(c));
9469
- }
9470
- function filterFalse(conditions) {
9471
- return conditions.filter((c) => !isAlwaysFalse(c));
9472
- }
9473
9768
 
9474
- // ../zql/src/builder/like.ts
9475
- function getLikePredicate(pattern, flags) {
9476
- const op = getLikeOp(String(pattern), flags);
9477
- return (lhs) => {
9478
- assertString(lhs);
9479
- return op(String(lhs));
9480
- };
9769
+ // ../zql/src/planner/planner-constraint.ts
9770
+ function mergeConstraints(a, b) {
9771
+ if (!a) return b;
9772
+ if (!b) return a;
9773
+ return { ...a, ...b };
9481
9774
  }
9482
- function getLikeOp(pattern, flags) {
9483
- if (!/_|%|\\/.test(pattern)) {
9484
- if (flags === "i") {
9485
- const rhsLower = pattern.toLowerCase();
9486
- return (lhs) => lhs.toLowerCase() === rhsLower;
9775
+
9776
+ // ../zql/src/planner/planner-connection.ts
9777
+ var PlannerConnection = class {
9778
+ kind = "connection";
9779
+ // ========================================================================
9780
+ // IMMUTABLE STRUCTURE (set during construction, never changes)
9781
+ // ========================================================================
9782
+ #sort;
9783
+ #filters;
9784
+ #model;
9785
+ table;
9786
+ name;
9787
+ // Human-readable name for debugging (defaults to table name)
9788
+ #baseConstraints;
9789
+ // Constraints from parent correlation
9790
+ #baseLimit;
9791
+ // Original limit from query structure (never modified)
9792
+ selectivity;
9793
+ // Fraction of rows passing filters (1.0 = no filtering)
9794
+ #output;
9795
+ // Set once during graph construction
9796
+ // ========================================================================
9797
+ // MUTABLE PLANNING STATE (changes during plan search)
9798
+ // ========================================================================
9799
+ /**
9800
+ * Current limit during planning. Can be cleared (set to undefined) when a
9801
+ * parent join is flipped, indicating this connection is now in an outer loop
9802
+ * and should not be limited by EXISTS semantics.
9803
+ */
9804
+ limit;
9805
+ /**
9806
+ * Constraints accumulated from parent joins during planning.
9807
+ * Key is a path through the graph (e.g., "0,1" for branch pattern [0,1]).
9808
+ *
9809
+ * Undefined constraints are possible when a FO converts to UFO and only
9810
+ * a single join in the UFO is flipped - other branches report undefined.
9811
+ */
9812
+ #constraints;
9813
+ /**
9814
+ * Cached total cost (sum of all branches) to avoid redundant calculations.
9815
+ * Invalidated when constraints change.
9816
+ */
9817
+ #cachedTotalCost = void 0;
9818
+ /**
9819
+ * Cached per-constraint costs to avoid redundant cost model calls.
9820
+ * Maps constraint key (branch pattern string) to computed cost.
9821
+ * Invalidated when constraints change.
9822
+ */
9823
+ #cachedConstraintCosts = /* @__PURE__ */ new Map();
9824
+ #costDirty = true;
9825
+ constructor(table2, model, sort, filters, baseConstraints, limit, name) {
9826
+ this.table = table2;
9827
+ this.name = name ?? table2;
9828
+ this.#sort = sort;
9829
+ this.#filters = filters;
9830
+ this.#model = model;
9831
+ this.#baseConstraints = baseConstraints;
9832
+ this.#baseLimit = limit;
9833
+ this.limit = limit;
9834
+ this.#constraints = /* @__PURE__ */ new Map();
9835
+ if (limit !== void 0 && filters) {
9836
+ const costWithFilters = model(table2, sort, filters, void 0);
9837
+ const costWithoutFilters = model(table2, sort, void 0, void 0);
9838
+ this.selectivity = costWithoutFilters.rows > 0 ? costWithFilters.rows / costWithoutFilters.rows : 1;
9839
+ } else {
9840
+ this.selectivity = 1;
9487
9841
  }
9488
- return (lhs) => lhs === pattern;
9489
9842
  }
9490
- const re = patternToRegExp(pattern, flags);
9491
- return (lhs) => re.test(lhs);
9492
- }
9493
- var specialCharsRe = /[$()*+.?[\]\\^{|}]/;
9494
- function patternToRegExp(source, flags = "") {
9495
- let pattern = "^";
9496
- for (let i = 0; i < source.length; i++) {
9497
- let c = source[i];
9498
- switch (c) {
9843
+ setOutput(node) {
9844
+ this.#output = node;
9845
+ }
9846
+ get output() {
9847
+ assert(this.#output !== void 0, "Output not set");
9848
+ return this.#output;
9849
+ }
9850
+ closestJoinOrSource() {
9851
+ return "connection";
9852
+ }
9853
+ /**
9854
+ * Constraints are uniquely identified by their path through the
9855
+ * graph.
9856
+ *
9857
+ * FO represents all sub-joins as a single path.
9858
+ * UFO represents each sub-join as a separate path.
9859
+ * The first branch in a UFO will match the path of FO so no re-set needs to happen
9860
+ * when swapping from FO to UFO.
9861
+ *
9862
+ * FO swaps to UFO when a join inside FO-FI gets flipped.
9863
+ *
9864
+ * The max of the last element of the paths is the number of
9865
+ * root branches.
9866
+ */
9867
+ propagateConstraints(path, c) {
9868
+ const key = path.join(",");
9869
+ this.#constraints.set(key, c);
9870
+ this.#cachedTotalCost = void 0;
9871
+ this.#cachedConstraintCosts.clear();
9872
+ this.#costDirty = true;
9873
+ }
9874
+ estimateCost(branchPattern) {
9875
+ if (branchPattern === void 0) {
9876
+ if (!this.#costDirty && this.#cachedTotalCost !== void 0) {
9877
+ return this.#cachedTotalCost;
9878
+ }
9879
+ let totalRows = 0;
9880
+ let maxStartupCost = 0;
9881
+ if (this.#constraints.size === 0) {
9882
+ const key2 = "";
9883
+ let cost2 = this.#cachedConstraintCosts.get(key2);
9884
+ if (cost2 === void 0) {
9885
+ const { startupCost: startupCost2, rows: rows2 } = this.#model(
9886
+ this.table,
9887
+ this.#sort,
9888
+ this.#filters,
9889
+ this.#baseConstraints
9890
+ );
9891
+ cost2 = {
9892
+ rows: rows2,
9893
+ runningCost: rows2,
9894
+ startupCost: startupCost2,
9895
+ selectivity: this.selectivity,
9896
+ limit: this.limit
9897
+ };
9898
+ this.#cachedConstraintCosts.set(key2, cost2);
9899
+ }
9900
+ totalRows = cost2.rows;
9901
+ maxStartupCost = cost2.startupCost;
9902
+ } else {
9903
+ for (const [key2, constraint2] of this.#constraints.entries()) {
9904
+ let cost2 = this.#cachedConstraintCosts.get(key2);
9905
+ if (cost2 === void 0) {
9906
+ const mergedConstraint2 = mergeConstraints(
9907
+ this.#baseConstraints,
9908
+ constraint2
9909
+ );
9910
+ const { startupCost: startupCost2, rows: rows2 } = this.#model(
9911
+ this.table,
9912
+ this.#sort,
9913
+ this.#filters,
9914
+ mergedConstraint2
9915
+ );
9916
+ cost2 = {
9917
+ rows: rows2,
9918
+ runningCost: rows2,
9919
+ startupCost: startupCost2,
9920
+ selectivity: this.selectivity,
9921
+ limit: this.limit
9922
+ };
9923
+ this.#cachedConstraintCosts.set(key2, cost2);
9924
+ }
9925
+ totalRows += cost2.rows;
9926
+ maxStartupCost = Math.max(maxStartupCost, cost2.startupCost);
9927
+ }
9928
+ }
9929
+ const ret = {
9930
+ rows: totalRows,
9931
+ runningCost: totalRows,
9932
+ startupCost: maxStartupCost,
9933
+ selectivity: this.selectivity,
9934
+ limit: this.limit
9935
+ };
9936
+ this.#cachedTotalCost = ret;
9937
+ this.#costDirty = false;
9938
+ return ret;
9939
+ }
9940
+ const key = branchPattern.join(",");
9941
+ let cost = this.#cachedConstraintCosts.get(key);
9942
+ if (cost !== void 0) {
9943
+ return cost;
9944
+ }
9945
+ const constraint = this.#constraints.get(key);
9946
+ const mergedConstraint = mergeConstraints(
9947
+ this.#baseConstraints,
9948
+ constraint
9949
+ );
9950
+ const { startupCost, rows } = this.#model(
9951
+ this.table,
9952
+ this.#sort,
9953
+ this.#filters,
9954
+ mergedConstraint
9955
+ );
9956
+ cost = {
9957
+ rows,
9958
+ runningCost: rows,
9959
+ startupCost,
9960
+ selectivity: this.selectivity,
9961
+ limit: this.limit
9962
+ };
9963
+ this.#cachedConstraintCosts.set(key, cost);
9964
+ return cost;
9965
+ }
9966
+ /**
9967
+ * Remove the limit from this connection.
9968
+ * Called when a parent join is flipped, making this connection part of an
9969
+ * outer loop that should produce all rows rather than stopping at the limit.
9970
+ */
9971
+ unlimit() {
9972
+ if (this.limit !== void 0) {
9973
+ this.limit = void 0;
9974
+ }
9975
+ }
9976
+ /**
9977
+ * Propagate unlimiting when a parent join is flipped.
9978
+ * For connections, we simply remove the limit.
9979
+ */
9980
+ propagateUnlimitFromFlippedJoin() {
9981
+ this.unlimit();
9982
+ }
9983
+ reset() {
9984
+ this.#constraints.clear();
9985
+ this.limit = this.#baseLimit;
9986
+ this.#cachedTotalCost = void 0;
9987
+ this.#cachedConstraintCosts.clear();
9988
+ this.#costDirty = true;
9989
+ }
9990
+ /**
9991
+ * Capture constraint state for snapshotting.
9992
+ * Used by PlannerGraph to save/restore planning state.
9993
+ */
9994
+ captureConstraints() {
9995
+ return new Map(this.#constraints);
9996
+ }
9997
+ /**
9998
+ * Restore constraint state from a snapshot.
9999
+ * Used by PlannerGraph to restore planning state.
10000
+ */
10001
+ restoreConstraints(constraints) {
10002
+ this.#constraints.clear();
10003
+ for (const [key, value] of constraints) {
10004
+ this.#constraints.set(key, value);
10005
+ }
10006
+ this.#cachedTotalCost = void 0;
10007
+ this.#cachedConstraintCosts.clear();
10008
+ this.#costDirty = true;
10009
+ }
10010
+ /**
10011
+ * Get current constraints for debugging.
10012
+ * Returns a copy of the constraints map.
10013
+ */
10014
+ getConstraintsForDebug() {
10015
+ return new Map(this.#constraints);
10016
+ }
10017
+ /**
10018
+ * Get estimated cost for each constraint branch.
10019
+ * Returns a map of constraint key to cost estimate.
10020
+ * Forces cost calculation if not already cached.
10021
+ */
10022
+ getConstraintCostsForDebug() {
10023
+ this.estimateCost(void 0);
10024
+ return new Map(this.#cachedConstraintCosts);
10025
+ }
10026
+ };
10027
+
10028
+ // ../zql/src/planner/planner-source.ts
10029
+ var PlannerSource = class {
10030
+ name;
10031
+ #model;
10032
+ constructor(name, model) {
10033
+ this.name = name;
10034
+ this.#model = model;
10035
+ }
10036
+ connect(sort, filters, baseConstraints, limit) {
10037
+ return new PlannerConnection(
10038
+ this.name,
10039
+ this.#model,
10040
+ sort,
10041
+ filters,
10042
+ baseConstraints,
10043
+ limit
10044
+ );
10045
+ }
10046
+ };
10047
+
10048
+ // ../zql/src/planner/planner-graph.ts
10049
+ var MAX_FLIPPABLE_JOINS = 13;
10050
+ var PlannerGraph = class {
10051
+ // Sources indexed by table name
10052
+ #sources = /* @__PURE__ */ new Map();
10053
+ // The final output node where constraint propagation starts
10054
+ #terminus = void 0;
10055
+ // Collections of nodes with mutable planning state
10056
+ joins = [];
10057
+ fanOuts = [];
10058
+ fanIns = [];
10059
+ connections = [];
10060
+ /**
10061
+ * Reset all planning state back to initial values for another planning pass.
10062
+ * Resets only mutable planning state - graph structure is unchanged.
10063
+ *
10064
+ * This allows replanning the same query graph with different strategies.
10065
+ */
10066
+ resetPlanningState() {
10067
+ for (const j of this.joins) j.reset();
10068
+ for (const fo of this.fanOuts) fo.reset();
10069
+ for (const fi of this.fanIns) fi.reset();
10070
+ for (const c of this.connections) c.reset();
10071
+ }
10072
+ /**
10073
+ * Create and register a source (table) in the graph.
10074
+ */
10075
+ addSource(name, model) {
10076
+ assert(
10077
+ !this.#sources.has(name),
10078
+ `Source ${name} already exists in the graph`
10079
+ );
10080
+ const source = new PlannerSource(name, model);
10081
+ this.#sources.set(name, source);
10082
+ return source;
10083
+ }
10084
+ /**
10085
+ * Get a source by table name.
10086
+ */
10087
+ getSource(name) {
10088
+ const source = this.#sources.get(name);
10089
+ assert(source !== void 0, `Source ${name} not found in the graph`);
10090
+ return source;
10091
+ }
10092
+ /**
10093
+ * Check if a source exists by table name.
10094
+ */
10095
+ hasSource(name) {
10096
+ return this.#sources.has(name);
10097
+ }
10098
+ /**
10099
+ * Set the terminus (final output) node of the graph.
10100
+ * Constraint propagation starts from this node.
10101
+ */
10102
+ setTerminus(terminus) {
10103
+ this.#terminus = terminus;
10104
+ }
10105
+ /**
10106
+ * Initiate constraint propagation from the terminus node.
10107
+ * This sends constraints up through the graph to update
10108
+ * connection cost estimates.
10109
+ */
10110
+ propagateConstraints() {
10111
+ assert(
10112
+ this.#terminus !== void 0,
10113
+ "Cannot propagate constraints without a terminus node"
10114
+ );
10115
+ this.#terminus.propagateConstraints();
10116
+ }
10117
+ /**
10118
+ * Calculate total cost of the current plan.
10119
+ * Total cost includes both startup cost (one-time, e.g., sorting) and running cost.
10120
+ */
10121
+ getTotalCost() {
10122
+ const estimate = must(this.#terminus).estimateCost();
10123
+ return estimate.startupCost + estimate.runningCost;
10124
+ }
10125
+ /**
10126
+ * Capture a lightweight snapshot of the current planning state.
10127
+ * Used for backtracking during multi-start greedy search.
10128
+ *
10129
+ * Captures mutable state including pinned flags, join types, and
10130
+ * constraint maps to avoid needing repropagation on restore.
10131
+ *
10132
+ * @returns A snapshot that can be restored via restorePlanningSnapshot()
10133
+ */
10134
+ capturePlanningSnapshot() {
10135
+ return {
10136
+ connections: this.connections.map((c) => ({
10137
+ limit: c.limit
10138
+ })),
10139
+ joins: this.joins.map((j) => ({ type: j.type })),
10140
+ fanOuts: this.fanOuts.map((fo) => ({ type: fo.type })),
10141
+ fanIns: this.fanIns.map((fi) => ({ type: fi.type })),
10142
+ connectionConstraints: this.connections.map((c) => c.captureConstraints())
10143
+ };
10144
+ }
10145
+ /**
10146
+ * Restore planning state from a previously captured snapshot.
10147
+ * Used for backtracking when a planning attempt fails.
10148
+ *
10149
+ * Restores pinned flags, join types, and constraint maps, eliminating
10150
+ * the need for repropagation.
10151
+ *
10152
+ * @param state - Snapshot created by capturePlanningSnapshot()
10153
+ */
10154
+ restorePlanningSnapshot(state) {
10155
+ this.#validateSnapshotShape(state);
10156
+ this.#restoreConnections(state);
10157
+ this.#restoreJoins(state);
10158
+ this.#restoreFanNodes(state);
10159
+ }
10160
+ /**
10161
+ * Collect cost estimates from all nodes in the graph for debugging.
10162
+ */
10163
+ #collectNodeCosts() {
10164
+ const costs = [];
10165
+ for (const c of this.connections) {
10166
+ costs.push({
10167
+ node: c.name,
10168
+ nodeType: "connection",
10169
+ costEstimate: c.estimateCost(void 0)
10170
+ });
10171
+ }
10172
+ for (const j of this.joins) {
10173
+ costs.push({
10174
+ node: j.getName(),
10175
+ nodeType: "join",
10176
+ costEstimate: j.estimateCost(void 0)
10177
+ });
10178
+ }
10179
+ for (const fo of this.fanOuts) {
10180
+ costs.push({
10181
+ node: "FO",
10182
+ nodeType: "fan-out",
10183
+ costEstimate: fo.estimateCost(void 0)
10184
+ });
10185
+ }
10186
+ for (const fi of this.fanIns) {
10187
+ costs.push({
10188
+ node: "FI",
10189
+ nodeType: "fan-in",
10190
+ costEstimate: fi.estimateCost(void 0)
10191
+ });
10192
+ }
10193
+ return costs;
10194
+ }
10195
+ /**
10196
+ * Validate that snapshot shape matches current graph structure.
10197
+ */
10198
+ #validateSnapshotShape(state) {
10199
+ assert(
10200
+ this.connections.length === state.connections.length,
10201
+ "Plan state mismatch: connections"
10202
+ );
10203
+ assert(
10204
+ this.joins.length === state.joins.length,
10205
+ "Plan state mismatch: joins"
10206
+ );
10207
+ assert(
10208
+ this.fanOuts.length === state.fanOuts.length,
10209
+ "Plan state mismatch: fanOuts"
10210
+ );
10211
+ assert(
10212
+ this.fanIns.length === state.fanIns.length,
10213
+ "Plan state mismatch: fanIns"
10214
+ );
10215
+ assert(
10216
+ this.connections.length === state.connectionConstraints.length,
10217
+ "Plan state mismatch: connectionConstraints"
10218
+ );
10219
+ }
10220
+ /**
10221
+ * Restore connection pinned flags, limits, and constraint maps.
10222
+ */
10223
+ #restoreConnections(state) {
10224
+ for (let i = 0; i < this.connections.length; i++) {
10225
+ this.connections[i].limit = state.connections[i].limit;
10226
+ this.connections[i].restoreConstraints(state.connectionConstraints[i]);
10227
+ }
10228
+ }
10229
+ /**
10230
+ * Restore join types and pinned flags.
10231
+ */
10232
+ #restoreJoins(state) {
10233
+ for (let i = 0; i < this.joins.length; i++) {
10234
+ const join = this.joins[i];
10235
+ const targetState = state.joins[i];
10236
+ join.reset();
10237
+ if (targetState.type === "flipped") {
10238
+ join.flip();
10239
+ }
10240
+ }
10241
+ }
10242
+ /**
10243
+ * Restore FanOut and FanIn types.
10244
+ */
10245
+ #restoreFanNodes(state) {
10246
+ for (let i = 0; i < this.fanOuts.length; i++) {
10247
+ const fo = this.fanOuts[i];
10248
+ const targetType = state.fanOuts[i].type;
10249
+ if (targetType === "UFO" && fo.type === "FO") {
10250
+ fo.convertToUFO();
10251
+ }
10252
+ }
10253
+ for (let i = 0; i < this.fanIns.length; i++) {
10254
+ const fi = this.fanIns[i];
10255
+ const targetType = state.fanIns[i].type;
10256
+ if (targetType === "UFI" && fi.type === "FI") {
10257
+ fi.convertToUFI();
10258
+ }
10259
+ }
10260
+ }
10261
+ /**
10262
+ * Main planning algorithm using exhaustive join flip enumeration.
10263
+ *
10264
+ * Enumerates all possible flip patterns for flippable joins (2^n for n flippable joins).
10265
+ * Each pattern represents a different query execution plan. We evaluate the cost of each
10266
+ * plan and select the one with the lowest cost.
10267
+ *
10268
+ * Connections are used only for cost estimation - the flip patterns determine the plan.
10269
+ * FanOut/FanIn states (FO/UFO and FI/UFI) are automatically derived from join flip states.
10270
+ *
10271
+ * @param planDebugger - Optional debugger to receive structured events during planning
10272
+ */
10273
+ plan(planDebugger) {
10274
+ const flippableJoins = this.joins.filter((j) => j.isFlippable());
10275
+ if (flippableJoins.length > MAX_FLIPPABLE_JOINS) {
10276
+ throw new Error(
10277
+ `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}).`
10278
+ );
10279
+ }
10280
+ const fofiCache = buildFOFICache(this);
10281
+ const numPatterns = 2 ** flippableJoins.length;
10282
+ let bestCost = Infinity;
10283
+ let bestPlan = void 0;
10284
+ let bestAttemptNumber = -1;
10285
+ for (let pattern = 0; pattern < numPatterns; pattern++) {
10286
+ this.resetPlanningState();
10287
+ if (planDebugger) {
10288
+ planDebugger.log({
10289
+ type: "attempt-start",
10290
+ attemptNumber: pattern,
10291
+ totalAttempts: numPatterns
10292
+ });
10293
+ }
10294
+ try {
10295
+ for (let i = 0; i < flippableJoins.length; i++) {
10296
+ if (pattern & 1 << i) {
10297
+ flippableJoins[i].flip();
10298
+ }
10299
+ }
10300
+ checkAndConvertFOFI(fofiCache);
10301
+ propagateUnlimitForFlippedJoins(this);
10302
+ this.propagateConstraints();
10303
+ if (planDebugger) {
10304
+ planDebugger.log({
10305
+ type: "constraints-propagated",
10306
+ attemptNumber: pattern,
10307
+ connectionConstraints: this.connections.map((c) => ({
10308
+ connection: c.name,
10309
+ constraints: c.getConstraintsForDebug(),
10310
+ constraintCosts: c.getConstraintCostsForDebug()
10311
+ }))
10312
+ });
10313
+ }
10314
+ const totalCost = this.getTotalCost();
10315
+ if (planDebugger) {
10316
+ planDebugger.log({
10317
+ type: "plan-complete",
10318
+ attemptNumber: pattern,
10319
+ totalCost,
10320
+ nodeCosts: this.#collectNodeCosts(),
10321
+ joinStates: this.joins.map((j) => {
10322
+ const info = j.getDebugInfo();
10323
+ return {
10324
+ join: info.name,
10325
+ type: info.type
10326
+ };
10327
+ })
10328
+ });
10329
+ }
10330
+ if (totalCost < bestCost) {
10331
+ bestCost = totalCost;
10332
+ bestPlan = this.capturePlanningSnapshot();
10333
+ bestAttemptNumber = pattern;
10334
+ }
10335
+ } catch (e) {
10336
+ if (planDebugger) {
10337
+ planDebugger.log({
10338
+ type: "plan-failed",
10339
+ attemptNumber: pattern,
10340
+ reason: `Flip pattern ${pattern.toString(2)} failed: ${e instanceof Error ? e.message : String(e)}`
10341
+ });
10342
+ }
10343
+ continue;
10344
+ }
10345
+ }
10346
+ if (bestPlan) {
10347
+ this.restorePlanningSnapshot(bestPlan);
10348
+ this.propagateConstraints();
10349
+ if (planDebugger) {
10350
+ planDebugger.log({
10351
+ type: "best-plan-selected",
10352
+ bestAttemptNumber,
10353
+ totalCost: bestCost,
10354
+ joinStates: this.joins.map((j) => ({
10355
+ join: j.getName(),
10356
+ type: j.type
10357
+ }))
10358
+ });
10359
+ }
10360
+ } else {
10361
+ throw new Error(
10362
+ "No valid query plan found. This should not happen - check query structure."
10363
+ );
10364
+ }
10365
+ }
10366
+ };
10367
+ function buildFOFICache(graph) {
10368
+ const cache = /* @__PURE__ */ new Map();
10369
+ for (const fo of graph.fanOuts) {
10370
+ const info = findFIAndJoins(fo);
10371
+ cache.set(fo, info);
10372
+ }
10373
+ return cache;
10374
+ }
10375
+ function checkAndConvertFOFI(fofiCache) {
10376
+ for (const [fo, info] of fofiCache) {
10377
+ const hasFlippedJoin = info.joinsBetween.some((j) => j.type === "flipped");
10378
+ if (info.fi && hasFlippedJoin) {
10379
+ fo.convertToUFO();
10380
+ info.fi.convertToUFI();
10381
+ }
10382
+ }
10383
+ }
10384
+ function findFIAndJoins(fo) {
10385
+ const joinsBetween = [];
10386
+ let fi = void 0;
10387
+ const queue = [...fo.outputs];
10388
+ const visited = /* @__PURE__ */ new Set();
10389
+ while (queue.length > 0) {
10390
+ const node = must(queue.shift());
10391
+ if (visited.has(node)) continue;
10392
+ visited.add(node);
10393
+ switch (node.kind) {
10394
+ case "join":
10395
+ joinsBetween.push(node);
10396
+ queue.push(node.output);
10397
+ break;
10398
+ case "fan-out":
10399
+ queue.push(...node.outputs);
10400
+ break;
10401
+ case "fan-in":
10402
+ fi = node;
10403
+ break;
10404
+ case "connection":
10405
+ break;
10406
+ case "terminus":
10407
+ break;
10408
+ }
10409
+ }
10410
+ return { fi, joinsBetween };
10411
+ }
10412
+ function propagateUnlimitForFlippedJoins(graph) {
10413
+ for (const join of graph.joins) {
10414
+ if (join.type === "flipped") {
10415
+ join.propagateUnlimit();
10416
+ }
10417
+ }
10418
+ }
10419
+
10420
+ // ../zql/src/planner/planner-join.ts
10421
+ var SEMI_JOIN_OVERHEAD_MULTIPLIER = 1.5;
10422
+ var PlannerJoin = class {
10423
+ kind = "join";
10424
+ #parent;
10425
+ #child;
10426
+ #parentConstraint;
10427
+ #childConstraint;
10428
+ #flippable;
10429
+ planId;
10430
+ #output;
10431
+ // Set once during graph construction
10432
+ // Reset between planning attempts
10433
+ #type;
10434
+ constructor(parent, child, parentConstraint, childConstraint, flippable, planId) {
10435
+ this.#type = "semi";
10436
+ this.#parent = parent;
10437
+ this.#child = child;
10438
+ this.#childConstraint = childConstraint;
10439
+ this.#parentConstraint = parentConstraint;
10440
+ this.#flippable = flippable;
10441
+ this.planId = planId;
10442
+ }
10443
+ setOutput(node) {
10444
+ this.#output = node;
10445
+ }
10446
+ get output() {
10447
+ assert(this.#output !== void 0, "Output not set");
10448
+ return this.#output;
10449
+ }
10450
+ closestJoinOrSource() {
10451
+ return "join";
10452
+ }
10453
+ flipIfNeeded(input) {
10454
+ if (input === this.#child) {
10455
+ this.flip();
10456
+ } else {
10457
+ assert(
10458
+ input === this.#parent,
10459
+ "Can only flip a join from one of its inputs"
10460
+ );
10461
+ }
10462
+ }
10463
+ flip() {
10464
+ assert(this.#type === "semi", "Can only flip a semi-join");
10465
+ if (!this.#flippable) {
10466
+ throw new UnflippableJoinError(
10467
+ "Cannot flip a non-flippable join (e.g., NOT EXISTS)"
10468
+ );
10469
+ }
10470
+ this.#type = "flipped";
10471
+ }
10472
+ get type() {
10473
+ return this.#type;
10474
+ }
10475
+ isFlippable() {
10476
+ return this.#flippable;
10477
+ }
10478
+ /**
10479
+ * Propagate unlimiting through the child subgraph when this join is flipped.
10480
+ * When a join is flipped, the child becomes the outer loop and should produce
10481
+ * all rows rather than stopping at an EXISTS limit.
10482
+ *
10483
+ * Propagation rules:
10484
+ * - Connection: call unlimit()
10485
+ * - Semi-join: continue to parent (outer loop)
10486
+ * - Flipped join: stop (already unlimited when it was flipped)
10487
+ * - Fan-out/Fan-in: propagate to all inputs
10488
+ */
10489
+ propagateUnlimit() {
10490
+ assert(this.#type === "flipped", "Can only unlimit a flipped join");
10491
+ propagateUnlimitToNode(this.#child);
10492
+ }
10493
+ /**
10494
+ * Called when a parent join is flipped and this join is part of its child subgraph.
10495
+ * - Semi-join: continue propagation to parent (the outer loop)
10496
+ * - Flipped join: stop propagation (already unlimited when it was flipped)
10497
+ */
10498
+ propagateUnlimitFromFlippedJoin() {
10499
+ if (this.#type === "semi") {
10500
+ propagateUnlimitToNode(this.#parent);
10501
+ }
10502
+ }
10503
+ propagateConstraints(branchPattern, constraint) {
10504
+ if (this.#type === "semi") {
10505
+ this.#child.propagateConstraints(
10506
+ branchPattern,
10507
+ this.#childConstraint,
10508
+ this
10509
+ );
10510
+ this.#parent.propagateConstraints(branchPattern, constraint, this);
10511
+ } else if (this.#type === "flipped") {
10512
+ this.#child.propagateConstraints(branchPattern, void 0, this);
10513
+ this.#parent.propagateConstraints(
10514
+ branchPattern,
10515
+ mergeConstraints(constraint, this.#parentConstraint),
10516
+ this
10517
+ );
10518
+ }
10519
+ }
10520
+ reset() {
10521
+ this.#type = "semi";
10522
+ }
10523
+ estimateCost(branchPattern) {
10524
+ const parentCost = this.#parent.estimateCost(branchPattern);
10525
+ const childCost = this.#child.estimateCost(branchPattern);
10526
+ let scanEst = parentCost.rows;
10527
+ if (this.#type === "semi" && parentCost.limit !== void 0) {
10528
+ if (childCost.selectivity !== 0) {
10529
+ scanEst = Math.min(scanEst, parentCost.limit / childCost.selectivity);
10530
+ }
10531
+ }
10532
+ if (this.#parent.closestJoinOrSource() === "join") {
10533
+ const pipelineCost = this.#type === "flipped" ? childCost.startupCost + childCost.runningCost * (parentCost.startupCost + parentCost.runningCost) : parentCost.runningCost + SEMI_JOIN_OVERHEAD_MULTIPLIER * scanEst * (childCost.startupCost + childCost.runningCost);
10534
+ return {
10535
+ rows: parentCost.rows,
10536
+ runningCost: pipelineCost,
10537
+ startupCost: parentCost.startupCost,
10538
+ selectivity: parentCost.selectivity,
10539
+ limit: parentCost.limit
10540
+ };
10541
+ }
10542
+ const nestedLoopCost = this.#type === "flipped" ? childCost.runningCost * (parentCost.startupCost + parentCost.runningCost) : SEMI_JOIN_OVERHEAD_MULTIPLIER * scanEst * (childCost.startupCost + childCost.runningCost);
10543
+ return {
10544
+ rows: parentCost.rows,
10545
+ runningCost: nestedLoopCost,
10546
+ startupCost: parentCost.startupCost,
10547
+ selectivity: parentCost.selectivity,
10548
+ limit: parentCost.limit
10549
+ };
10550
+ }
10551
+ /**
10552
+ * Get a human-readable name for this join for debugging.
10553
+ * Format: "parentName ⋈ childName"
10554
+ */
10555
+ getName() {
10556
+ const parentName = getNodeName(this.#parent);
10557
+ const childName = getNodeName(this.#child);
10558
+ return `${parentName} \u22C8 ${childName}`;
10559
+ }
10560
+ /**
10561
+ * Get debug information about this join's state.
10562
+ */
10563
+ getDebugInfo() {
10564
+ return {
10565
+ name: this.getName(),
10566
+ type: this.#type,
10567
+ planId: this.planId
10568
+ };
10569
+ }
10570
+ };
10571
+ var UnflippableJoinError = class extends Error {
10572
+ constructor(message) {
10573
+ super(message);
10574
+ this.name = "UnflippableJoinError";
10575
+ }
10576
+ };
10577
+ function getNodeName(node) {
10578
+ switch (node.kind) {
10579
+ case "connection":
10580
+ return node.name;
10581
+ case "join":
10582
+ return node.getName();
10583
+ case "fan-out":
10584
+ return "FO";
10585
+ case "fan-in":
10586
+ return "FI";
10587
+ case "terminus":
10588
+ return "terminus";
10589
+ }
10590
+ }
10591
+ function propagateUnlimitToNode(node) {
10592
+ if ("propagateUnlimitFromFlippedJoin" in node && typeof node.propagateUnlimitFromFlippedJoin === "function") {
10593
+ node.propagateUnlimitFromFlippedJoin();
10594
+ }
10595
+ }
10596
+
10597
+ // ../zql/src/planner/planner-terminus.ts
10598
+ var PlannerTerminus = class {
10599
+ kind = "terminus";
10600
+ #input;
10601
+ constructor(input) {
10602
+ this.#input = input;
10603
+ }
10604
+ get pinned() {
10605
+ return true;
10606
+ }
10607
+ closestJoinOrSource() {
10608
+ return this.#input.closestJoinOrSource();
10609
+ }
10610
+ propagateConstraints() {
10611
+ this.#input.propagateConstraints([], void 0, this);
10612
+ }
10613
+ estimateCost() {
10614
+ return this.#input.estimateCost([]);
10615
+ }
10616
+ /**
10617
+ * Propagate unlimiting when a parent join is flipped.
10618
+ * Terminus doesn't participate in unlimiting.
10619
+ */
10620
+ propagateUnlimitFromFlippedJoin() {
10621
+ }
10622
+ };
10623
+
10624
+ // ../zql/src/planner/planner-builder.ts
10625
+ function wireOutput(from, to) {
10626
+ switch (from.kind) {
10627
+ case "connection":
10628
+ case "join":
10629
+ case "fan-in":
10630
+ from.setOutput(to);
10631
+ break;
10632
+ case "fan-out":
10633
+ from.addOutput(to);
10634
+ break;
10635
+ case "terminus":
10636
+ assert(false, "Terminus nodes cannot have outputs");
10637
+ }
10638
+ }
10639
+ function buildPlanGraph(ast, model, baseConstraints) {
10640
+ const graph = new PlannerGraph();
10641
+ let nextPlanId = 0;
10642
+ const source = graph.addSource(ast.table, model);
10643
+ const connection = source.connect(
10644
+ ast.orderBy ?? [],
10645
+ ast.where,
10646
+ baseConstraints,
10647
+ ast.limit
10648
+ );
10649
+ graph.connections.push(connection);
10650
+ let end = connection;
10651
+ if (ast.where) {
10652
+ end = processCondition(
10653
+ ast.where,
10654
+ end,
10655
+ graph,
10656
+ model,
10657
+ ast.table,
10658
+ () => nextPlanId++
10659
+ );
10660
+ }
10661
+ const terminus = new PlannerTerminus(end);
10662
+ wireOutput(end, terminus);
10663
+ graph.setTerminus(terminus);
10664
+ const subPlans = {};
10665
+ if (ast.related) {
10666
+ for (const csq of ast.related) {
10667
+ const alias = must(
10668
+ csq.subquery.alias,
10669
+ "Related subquery must have alias"
10670
+ );
10671
+ const childConstraints = extractConstraint(
10672
+ csq.correlation.childField,
10673
+ csq.subquery.table
10674
+ );
10675
+ subPlans[alias] = buildPlanGraph(csq.subquery, model, childConstraints);
10676
+ }
10677
+ }
10678
+ return { plan: graph, subPlans };
10679
+ }
10680
+ function processCondition(condition, input, graph, model, parentTable, getPlanId) {
10681
+ switch (condition.type) {
10682
+ case "simple":
10683
+ return input;
10684
+ case "and":
10685
+ return processAnd(condition, input, graph, model, parentTable, getPlanId);
10686
+ case "or":
10687
+ return processOr(condition, input, graph, model, parentTable, getPlanId);
10688
+ case "correlatedSubquery":
10689
+ return processCorrelatedSubquery(
10690
+ condition,
10691
+ input,
10692
+ graph,
10693
+ model,
10694
+ parentTable,
10695
+ getPlanId
10696
+ );
10697
+ }
10698
+ }
10699
+ function processAnd(condition, input, graph, model, parentTable, getPlanId) {
10700
+ let end = input;
10701
+ for (const subCondition of condition.conditions) {
10702
+ end = processCondition(
10703
+ subCondition,
10704
+ end,
10705
+ graph,
10706
+ model,
10707
+ parentTable,
10708
+ getPlanId
10709
+ );
10710
+ }
10711
+ return end;
10712
+ }
10713
+ function processOr(condition, input, graph, model, parentTable, getPlanId) {
10714
+ const subqueryConditions = condition.conditions.filter(
10715
+ (c) => c.type === "correlatedSubquery" || hasCorrelatedSubquery(c)
10716
+ );
10717
+ if (subqueryConditions.length === 0) {
10718
+ return input;
10719
+ }
10720
+ const fanOut = new PlannerFanOut(input);
10721
+ graph.fanOuts.push(fanOut);
10722
+ wireOutput(input, fanOut);
10723
+ const branches = [];
10724
+ for (const subCondition of subqueryConditions) {
10725
+ const branch = processCondition(
10726
+ subCondition,
10727
+ fanOut,
10728
+ graph,
10729
+ model,
10730
+ parentTable,
10731
+ getPlanId
10732
+ );
10733
+ branches.push(branch);
10734
+ fanOut.addOutput(branch);
10735
+ }
10736
+ const fanIn = new PlannerFanIn(branches);
10737
+ graph.fanIns.push(fanIn);
10738
+ for (const branch of branches) {
10739
+ wireOutput(branch, fanIn);
10740
+ }
10741
+ return fanIn;
10742
+ }
10743
+ function processCorrelatedSubquery(condition, input, graph, model, parentTable, getPlanId) {
10744
+ const { related } = condition;
10745
+ const childTable = related.subquery.table;
10746
+ const childSource = graph.hasSource(childTable) ? graph.getSource(childTable) : graph.addSource(childTable, model);
10747
+ const childConnection = childSource.connect(
10748
+ related.subquery.orderBy ?? [],
10749
+ related.subquery.where,
10750
+ void 0,
10751
+ // no base constraints for EXISTS/NOT EXISTS
10752
+ condition.op === "EXISTS" ? 1 : void 0
10753
+ );
10754
+ graph.connections.push(childConnection);
10755
+ let childEnd = childConnection;
10756
+ if (related.subquery.where) {
10757
+ childEnd = processCondition(
10758
+ related.subquery.where,
10759
+ childEnd,
10760
+ graph,
10761
+ model,
10762
+ childTable,
10763
+ getPlanId
10764
+ );
10765
+ }
10766
+ const parentConstraint = extractConstraint(
10767
+ related.correlation.parentField,
10768
+ parentTable
10769
+ );
10770
+ const childConstraint = extractConstraint(
10771
+ related.correlation.childField,
10772
+ childTable
10773
+ );
10774
+ const planId = getPlanId();
10775
+ condition[planIdSymbol] = planId;
10776
+ const join = new PlannerJoin(
10777
+ input,
10778
+ childEnd,
10779
+ parentConstraint,
10780
+ childConstraint,
10781
+ condition.op !== "NOT EXISTS",
10782
+ planId
10783
+ );
10784
+ graph.joins.push(join);
10785
+ wireOutput(input, join);
10786
+ wireOutput(childEnd, join);
10787
+ return join;
10788
+ }
10789
+ function hasCorrelatedSubquery(condition) {
10790
+ if (condition.type === "correlatedSubquery") {
10791
+ return true;
10792
+ }
10793
+ if (condition.type === "and" || condition.type === "or") {
10794
+ return condition.conditions.some(hasCorrelatedSubquery);
10795
+ }
10796
+ return false;
10797
+ }
10798
+ function extractConstraint(fields, _tableName) {
10799
+ return Object.fromEntries(fields.map((field) => [field, void 0]));
10800
+ }
10801
+ function planRecursively(plans, planDebugger) {
10802
+ for (const subPlan of Object.values(plans.subPlans)) {
10803
+ planRecursively(subPlan, planDebugger);
10804
+ }
10805
+ plans.plan.plan(planDebugger);
10806
+ }
10807
+ function planQuery(ast, model, planDebugger) {
10808
+ const plans = buildPlanGraph(ast, model);
10809
+ planRecursively(plans, planDebugger);
10810
+ return applyPlansToAST(ast, plans);
10811
+ }
10812
+ function applyToCondition(condition, flippedIds) {
10813
+ if (condition.type === "simple") {
10814
+ return condition;
10815
+ }
10816
+ if (condition.type === "correlatedSubquery") {
10817
+ const planId = condition[planIdSymbol];
10818
+ const shouldFlip = planId !== void 0 && flippedIds.has(planId);
10819
+ return {
10820
+ ...condition,
10821
+ flip: shouldFlip ? true : condition.flip,
10822
+ related: {
10823
+ ...condition.related,
10824
+ subquery: {
10825
+ ...condition.related.subquery,
10826
+ where: condition.related.subquery.where ? applyToCondition(condition.related.subquery.where, flippedIds) : void 0
10827
+ }
10828
+ }
10829
+ };
10830
+ }
10831
+ return {
10832
+ ...condition,
10833
+ conditions: condition.conditions.map((c) => applyToCondition(c, flippedIds))
10834
+ };
10835
+ }
10836
+ function applyPlansToAST(ast, plans) {
10837
+ const flippedIds = /* @__PURE__ */ new Set();
10838
+ for (const join of plans.plan.joins) {
10839
+ if (join.type === "flipped" && join.planId !== void 0) {
10840
+ flippedIds.add(join.planId);
10841
+ }
10842
+ }
10843
+ return {
10844
+ ...ast,
10845
+ where: ast.where ? applyToCondition(ast.where, flippedIds) : void 0,
10846
+ related: ast.related?.map((csq) => {
10847
+ const alias = must(
10848
+ csq.subquery.alias,
10849
+ "Related subquery must have alias"
10850
+ );
10851
+ const subPlan = plans.subPlans[alias];
10852
+ return {
10853
+ ...csq,
10854
+ subquery: subPlan ? applyPlansToAST(csq.subquery, subPlan) : csq.subquery
10855
+ };
10856
+ })
10857
+ };
10858
+ }
10859
+
10860
+ // ../zql/src/query/expression.ts
10861
+ var ExpressionBuilder = class {
10862
+ #exists;
10863
+ constructor(exists) {
10864
+ this.#exists = exists;
10865
+ this.exists = this.exists.bind(this);
10866
+ }
10867
+ get eb() {
10868
+ return this;
10869
+ }
10870
+ cmp(field, opOrValue, value) {
10871
+ return cmp(field, opOrValue, value);
10872
+ }
10873
+ cmpLit(left, op, right) {
10874
+ return {
10875
+ type: "simple",
10876
+ left: isParameterReference(left) ? left[toStaticParam]() : { type: "literal", value: left },
10877
+ right: isParameterReference(right) ? right[toStaticParam]() : { type: "literal", value: right },
10878
+ op
10879
+ };
10880
+ }
10881
+ and = and;
10882
+ or = or;
10883
+ not = not;
10884
+ exists = (relationship, cb, options) => this.#exists(relationship, cb, options);
10885
+ };
10886
+ function and(...conditions) {
10887
+ const expressions = filterTrue(filterUndefined(conditions));
10888
+ if (expressions.length === 1) {
10889
+ return expressions[0];
10890
+ }
10891
+ if (expressions.some(isAlwaysFalse)) {
10892
+ return FALSE;
10893
+ }
10894
+ return { type: "and", conditions: expressions };
10895
+ }
10896
+ function or(...conditions) {
10897
+ const expressions = filterFalse(filterUndefined(conditions));
10898
+ if (expressions.length === 1) {
10899
+ return expressions[0];
10900
+ }
10901
+ if (expressions.some(isAlwaysTrue)) {
10902
+ return TRUE;
10903
+ }
10904
+ return { type: "or", conditions: expressions };
10905
+ }
10906
+ function not(expression) {
10907
+ switch (expression.type) {
10908
+ case "and":
10909
+ return {
10910
+ type: "or",
10911
+ conditions: expression.conditions.map(not)
10912
+ };
10913
+ case "or":
10914
+ return {
10915
+ type: "and",
10916
+ conditions: expression.conditions.map(not)
10917
+ };
10918
+ case "correlatedSubquery":
10919
+ return {
10920
+ type: "correlatedSubquery",
10921
+ related: expression.related,
10922
+ op: negateOperator(expression.op)
10923
+ };
10924
+ case "simple":
10925
+ return {
10926
+ type: "simple",
10927
+ op: negateOperator(expression.op),
10928
+ left: expression.left,
10929
+ right: expression.right
10930
+ };
10931
+ }
10932
+ }
10933
+ function cmp(field, opOrValue, value) {
10934
+ let op;
10935
+ if (value === void 0) {
10936
+ value = opOrValue;
10937
+ op = "=";
10938
+ } else {
10939
+ op = opOrValue;
10940
+ }
10941
+ return {
10942
+ type: "simple",
10943
+ left: { type: "column", name: field },
10944
+ right: isParameterReference(value) ? value[toStaticParam]() : { type: "literal", value },
10945
+ op
10946
+ };
10947
+ }
10948
+ function isParameterReference(value) {
10949
+ return value !== null && typeof value === "object" && value[toStaticParam];
10950
+ }
10951
+ var TRUE = {
10952
+ type: "and",
10953
+ conditions: []
10954
+ };
10955
+ var FALSE = {
10956
+ type: "or",
10957
+ conditions: []
10958
+ };
10959
+ function isAlwaysTrue(condition) {
10960
+ return condition.type === "and" && condition.conditions.length === 0;
10961
+ }
10962
+ function isAlwaysFalse(condition) {
10963
+ return condition.type === "or" && condition.conditions.length === 0;
10964
+ }
10965
+ function simplifyCondition(c) {
10966
+ if (c.type === "simple" || c.type === "correlatedSubquery") {
10967
+ return c;
10968
+ }
10969
+ if (c.conditions.length === 1) {
10970
+ return simplifyCondition(c.conditions[0]);
10971
+ }
10972
+ const conditions = flatten(c.type, c.conditions.map(simplifyCondition));
10973
+ if (c.type === "and" && conditions.some(isAlwaysFalse)) {
10974
+ return FALSE;
10975
+ }
10976
+ if (c.type === "or" && conditions.some(isAlwaysTrue)) {
10977
+ return TRUE;
10978
+ }
10979
+ return {
10980
+ type: c.type,
10981
+ conditions
10982
+ };
10983
+ }
10984
+ function flatten(type, conditions) {
10985
+ const flattened = [];
10986
+ for (const c of conditions) {
10987
+ if (c.type === type) {
10988
+ flattened.push(...c.conditions);
10989
+ } else {
10990
+ flattened.push(c);
10991
+ }
10992
+ }
10993
+ return flattened;
10994
+ }
10995
+ var negateSimpleOperatorMap = {
10996
+ ["="]: "!=",
10997
+ ["!="]: "=",
10998
+ ["<"]: ">=",
10999
+ [">"]: "<=",
11000
+ [">="]: "<",
11001
+ ["<="]: ">",
11002
+ ["IN"]: "NOT IN",
11003
+ ["NOT IN"]: "IN",
11004
+ ["LIKE"]: "NOT LIKE",
11005
+ ["NOT LIKE"]: "LIKE",
11006
+ ["ILIKE"]: "NOT ILIKE",
11007
+ ["NOT ILIKE"]: "ILIKE",
11008
+ ["IS"]: "IS NOT",
11009
+ ["IS NOT"]: "IS"
11010
+ };
11011
+ var negateOperatorMap = {
11012
+ ...negateSimpleOperatorMap,
11013
+ ["EXISTS"]: "NOT EXISTS",
11014
+ ["NOT EXISTS"]: "EXISTS"
11015
+ };
11016
+ function negateOperator(op) {
11017
+ return must(negateOperatorMap[op]);
11018
+ }
11019
+ function filterUndefined(array10) {
11020
+ return array10.filter((e) => e !== void 0);
11021
+ }
11022
+ function filterTrue(conditions) {
11023
+ return conditions.filter((c) => !isAlwaysTrue(c));
11024
+ }
11025
+ function filterFalse(conditions) {
11026
+ return conditions.filter((c) => !isAlwaysFalse(c));
11027
+ }
11028
+
11029
+ // ../zql/src/builder/like.ts
11030
+ function getLikePredicate(pattern, flags) {
11031
+ const op = getLikeOp(String(pattern), flags);
11032
+ return (lhs) => {
11033
+ assertString(lhs);
11034
+ return op(String(lhs));
11035
+ };
11036
+ }
11037
+ function getLikeOp(pattern, flags) {
11038
+ if (!/_|%|\\/.test(pattern)) {
11039
+ if (flags === "i") {
11040
+ const rhsLower = pattern.toLowerCase();
11041
+ return (lhs) => lhs.toLowerCase() === rhsLower;
11042
+ }
11043
+ return (lhs) => lhs === pattern;
11044
+ }
11045
+ const re = patternToRegExp(pattern, flags);
11046
+ return (lhs) => re.test(lhs);
11047
+ }
11048
+ var specialCharsRe = /[$()*+.?[\]\\^{|}]/;
11049
+ function patternToRegExp(source, flags = "") {
11050
+ let pattern = "^";
11051
+ for (let i = 0; i < source.length; i++) {
11052
+ let c = source[i];
11053
+ switch (c) {
9499
11054
  case "%":
9500
11055
  pattern += ".*";
9501
11056
  break;
@@ -9666,18 +11221,45 @@ function transformFilters(filters) {
9666
11221
  }
9667
11222
 
9668
11223
  // ../zql/src/builder/builder.ts
9669
- function buildPipeline(ast, delegate, queryID) {
11224
+ function buildPipeline(ast, delegate, queryID, costModel) {
9670
11225
  ast = delegate.mapAst ? delegate.mapAst(ast) : ast;
11226
+ if (costModel) {
11227
+ ast = planQuery(ast, costModel);
11228
+ }
9671
11229
  return buildPipelineInternal(ast, delegate, queryID, "");
9672
11230
  }
9673
11231
  var EXISTS_LIMIT = 3;
9674
11232
  var PERMISSIONS_EXISTS_LIMIT = 1;
11233
+ function assertNoNotExists(condition) {
11234
+ switch (condition.type) {
11235
+ case "simple":
11236
+ return;
11237
+ case "correlatedSubquery":
11238
+ if (condition.op === "NOT EXISTS") {
11239
+ throw new Error(
11240
+ "not(exists()) is not supported on the client - see https://bugs.rocicorp.dev/issue/3438"
11241
+ );
11242
+ }
11243
+ return;
11244
+ case "and":
11245
+ case "or":
11246
+ for (const c of condition.conditions) {
11247
+ assertNoNotExists(c);
11248
+ }
11249
+ return;
11250
+ default:
11251
+ unreachable(condition);
11252
+ }
11253
+ }
9675
11254
  function buildPipelineInternal(ast, delegate, queryID, name, partitionKey) {
9676
11255
  const source = delegate.getSource(ast.table);
9677
11256
  if (!source) {
9678
11257
  throw new Error(`Source not found: ${ast.table}`);
9679
11258
  }
9680
11259
  ast = uniquifyCorrelatedSubqueryConditionAliases(ast);
11260
+ if (!delegate.enableNotExists && ast.where) {
11261
+ assertNoNotExists(ast.where);
11262
+ }
9681
11263
  const csqConditions = gatherCorrelatedSubqueryQueryConditions(ast.where);
9682
11264
  const splitEditKeys = partitionKey ? new Set(partitionKey) : /* @__PURE__ */ new Set();
9683
11265
  const aliases = /* @__PURE__ */ new Set();
@@ -10200,32 +11782,6 @@ var defaultFormat = {
10200
11782
  relationships: {}
10201
11783
  };
10202
11784
 
10203
- // ../zql/src/query/assert-no-not-exists.ts
10204
- function assertNoNotExists(condition) {
10205
- switch (condition.type) {
10206
- case "simple":
10207
- return;
10208
- case "correlatedSubquery":
10209
- if (condition.op === "NOT EXISTS") {
10210
- throw new Error(
10211
- "not(exists()) is not supported on the client - see https://bugs.rocicorp.dev/issue/3438"
10212
- );
10213
- }
10214
- if (condition.related.subquery.where) {
10215
- assertNoNotExists(condition.related.subquery.where);
10216
- }
10217
- return;
10218
- case "and":
10219
- case "or":
10220
- for (const c of condition.conditions) {
10221
- assertNoNotExists(c);
10222
- }
10223
- return;
10224
- default:
10225
- unreachable(condition);
10226
- }
10227
- }
10228
-
10229
11785
  // ../zql/src/query/query.ts
10230
11786
  var delegateSymbol = Symbol("delegate");
10231
11787
 
@@ -10489,9 +12045,6 @@ var AbstractQuery = class {
10489
12045
  cond = and(existingWhere, cond);
10490
12046
  }
10491
12047
  const where = simplifyCondition(cond);
10492
- if (this.#system === "client") {
10493
- assertNoNotExists(where);
10494
- }
10495
12048
  return this[newQuerySymbol](
10496
12049
  this._delegate,
10497
12050
  this.#schema,
@@ -11063,47 +12616,398 @@ var NativeClientLockManager = class {
11063
12616
  }
11064
12617
  }
11065
12618
  }
11066
- };
11067
- var mockLockNames = /* @__PURE__ */ new Set();
11068
- var mockListeners = /* @__PURE__ */ new Set();
11069
- var MockClientLockManager = class {
11070
- #listeners = /* @__PURE__ */ new Set();
11071
- request(name, mode, fn) {
11072
- if (mode === "exclusive") {
11073
- mockLockNames.add(name);
11074
- } else {
11075
- mode;
11076
- const listener = (removed) => {
11077
- if (removed === name) {
11078
- mockListeners.delete(listener);
11079
- return fn();
11080
- }
11081
- };
11082
- mockListeners.add(listener);
11083
- this.#listeners.add(listener);
12619
+ };
12620
+ var mockLockNames = /* @__PURE__ */ new Set();
12621
+ var mockListeners = /* @__PURE__ */ new Set();
12622
+ var MockClientLockManager = class {
12623
+ #listeners = /* @__PURE__ */ new Set();
12624
+ request(name, mode, fn) {
12625
+ if (mode === "exclusive") {
12626
+ mockLockNames.add(name);
12627
+ } else {
12628
+ mode;
12629
+ const listener = (removed) => {
12630
+ if (removed === name) {
12631
+ mockListeners.delete(listener);
12632
+ return fn();
12633
+ }
12634
+ };
12635
+ mockListeners.add(listener);
12636
+ this.#listeners.add(listener);
12637
+ }
12638
+ return Promise.resolve();
12639
+ }
12640
+ release(name, fn) {
12641
+ mockLockNames.delete(name);
12642
+ for (const listener of mockListeners) {
12643
+ listener(name);
12644
+ }
12645
+ for (const listener of this.#listeners) {
12646
+ mockListeners.delete(listener);
12647
+ }
12648
+ fn();
12649
+ }
12650
+ async *queryExclusive() {
12651
+ yield* mockLockNames;
12652
+ }
12653
+ };
12654
+
12655
+ // ../zero-client/src/client/connection-manager.ts
12656
+ import { resolver as resolver9 } from "@rocicorp/resolver";
12657
+
12658
+ // ../shared/src/subscribable.ts
12659
+ var Subscribable = class {
12660
+ _listeners = /* @__PURE__ */ new Set();
12661
+ /**
12662
+ * Subscribe to the subscribable.
12663
+ *
12664
+ * @param listener - The listener to subscribe to.
12665
+ * @returns A function to unsubscribe from the subscribable.
12666
+ */
12667
+ subscribe = (listener) => {
12668
+ this._listeners.add(listener);
12669
+ return () => {
12670
+ this._listeners.delete(listener);
12671
+ };
12672
+ };
12673
+ /**
12674
+ * Notify all listeners.
12675
+ *
12676
+ * @param update - The update to notify listeners with.
12677
+ */
12678
+ notify = (update) => {
12679
+ this._listeners.forEach((listener) => listener(update));
12680
+ };
12681
+ hasListeners = () => this._listeners.size > 0;
12682
+ /**
12683
+ * Unsubscribe all listeners.
12684
+ */
12685
+ cleanup = () => {
12686
+ this._listeners.clear();
12687
+ };
12688
+ };
12689
+
12690
+ // ../zero-client/src/client/connection-manager.ts
12691
+ var DEFAULT_TIMEOUT_CHECK_INTERVAL_MS = 1e3;
12692
+ var TERMINAL_STATES = [
12693
+ connection_status_enum_exports.Error
12694
+ ];
12695
+ var ConnectionManager = class _ConnectionManager extends Subscribable {
12696
+ #state;
12697
+ /**
12698
+ * The timestamp when we first started trying to connect.
12699
+ * This is used to track the retry window.
12700
+ * Reset to undefined when we successfully connect or when we transition to disconnected.
12701
+ */
12702
+ #connectingStartedAt;
12703
+ /**
12704
+ * The amount of time we allow for continuous connecting attempts before
12705
+ * transitioning to disconnected state.
12706
+ */
12707
+ #disconnectTimeoutMs;
12708
+ /**
12709
+ * Handle for the timeout interval that periodically checks whether we've
12710
+ * exceeded the allowed connecting window.
12711
+ */
12712
+ #timeoutInterval;
12713
+ /**
12714
+ * Interval duration for checking whether the connecting timeout has elapsed.
12715
+ */
12716
+ #timeoutCheckIntervalMs;
12717
+ /**
12718
+ * Resolver used to signal waiting callers when the state changes.
12719
+ */
12720
+ #stateChangeResolver = resolver9();
12721
+ constructor(options) {
12722
+ super();
12723
+ const now = Date.now();
12724
+ this.#disconnectTimeoutMs = options.disconnectTimeoutMs;
12725
+ this.#timeoutCheckIntervalMs = options.timeoutCheckIntervalMs ?? DEFAULT_TIMEOUT_CHECK_INTERVAL_MS;
12726
+ this.#state = {
12727
+ name: connection_status_enum_exports.Connecting,
12728
+ attempt: 0,
12729
+ disconnectAt: now + this.#disconnectTimeoutMs
12730
+ };
12731
+ this.#connectingStartedAt = now;
12732
+ this.#maybeStartTimeoutInterval();
12733
+ }
12734
+ get state() {
12735
+ return this.#state;
12736
+ }
12737
+ /**
12738
+ * Returns true if the current state is equal to the given status.
12739
+ */
12740
+ is(status) {
12741
+ return this.#state.name === status;
12742
+ }
12743
+ /**
12744
+ * Returns true if the current state is a terminal state
12745
+ * that can be recovered from by calling connect().
12746
+ */
12747
+ isInTerminalState() {
12748
+ return _ConnectionManager.isTerminalState(this.#state);
12749
+ }
12750
+ /**
12751
+ * Returns true if the given status is a terminal state
12752
+ * that can be recovered from by calling connect().
12753
+ */
12754
+ static isTerminalState(state) {
12755
+ return TERMINAL_STATES.includes(
12756
+ state.name
12757
+ );
12758
+ }
12759
+ /**
12760
+ * Returns true if the run loop should continue.
12761
+ * The run loop continues in disconnected, connecting, connected, and error states.
12762
+ * It stops in closed state.
12763
+ */
12764
+ shouldContinueRunLoop() {
12765
+ return this.#state.name !== connection_status_enum_exports.Closed;
12766
+ }
12767
+ /**
12768
+ * Waits for the next state change.
12769
+ * @returns A promise that resolves when the next state change occurs.
12770
+ */
12771
+ waitForStateChange() {
12772
+ return this.#nextStatePromise();
12773
+ }
12774
+ /**
12775
+ * Transition to connecting state.
12776
+ *
12777
+ * This starts the 5-minute timeout timer, but if we've entered disconnected state,
12778
+ * we stay there and continue retrying.
12779
+ *
12780
+ * @returns An object containing a promise that resolves on the next state change.
12781
+ */
12782
+ connecting(reason) {
12783
+ if (this.#state.name === connection_status_enum_exports.Closed) {
12784
+ return { nextStatePromise: this.#nextStatePromise() };
12785
+ }
12786
+ if (this.#state.name === connection_status_enum_exports.Disconnected) {
12787
+ return { nextStatePromise: this.#nextStatePromise() };
12788
+ }
12789
+ const now = Date.now();
12790
+ if (this.#state.name === connection_status_enum_exports.Connecting) {
12791
+ this.#state = {
12792
+ ...this.#state,
12793
+ attempt: this.#state.attempt + 1,
12794
+ reason
12795
+ };
12796
+ const nextStatePromise2 = this.#publishStateAndGetPromise();
12797
+ this.#maybeStartTimeoutInterval();
12798
+ return { nextStatePromise: nextStatePromise2 };
12799
+ }
12800
+ if (this.#connectingStartedAt === void 0) {
12801
+ this.#connectingStartedAt = now;
12802
+ }
12803
+ const disconnectAt = this.#connectingStartedAt + this.#disconnectTimeoutMs;
12804
+ this.#state = {
12805
+ name: connection_status_enum_exports.Connecting,
12806
+ attempt: 1,
12807
+ disconnectAt,
12808
+ reason
12809
+ };
12810
+ const nextStatePromise = this.#publishStateAndGetPromise();
12811
+ this.#maybeStartTimeoutInterval();
12812
+ return { nextStatePromise };
12813
+ }
12814
+ /**
12815
+ * Transition to connected state.
12816
+ * This resets the connecting timeout timer.
12817
+ *
12818
+ * @returns An object containing a promise that resolves on the next state change.
12819
+ */
12820
+ connected() {
12821
+ if (this.#state.name === connection_status_enum_exports.Closed) {
12822
+ return { nextStatePromise: this.#nextStatePromise() };
12823
+ }
12824
+ if (this.#state.name === connection_status_enum_exports.Connected) {
12825
+ return { nextStatePromise: this.#nextStatePromise() };
12826
+ }
12827
+ this.#connectingStartedAt = void 0;
12828
+ this.#maybeStopTimeoutInterval();
12829
+ this.#state = {
12830
+ name: connection_status_enum_exports.Connected
12831
+ };
12832
+ const nextStatePromise = this.#publishStateAndGetPromise();
12833
+ return { nextStatePromise };
12834
+ }
12835
+ /**
12836
+ * Transition to disconnected state.
12837
+ * This is called when the 5-minute timeout expires, or when we're intentionally
12838
+ * disconnecting due to an error (this will eventually be a separate state, error).
12839
+ * The run loop will continue trying to reconnect.
12840
+ *
12841
+ * @returns An object containing a promise that resolves on the next state change.
12842
+ */
12843
+ disconnected(reason) {
12844
+ if (this.#state.name === connection_status_enum_exports.Closed) {
12845
+ return { nextStatePromise: this.#nextStatePromise() };
12846
+ }
12847
+ if (this.#state.name === connection_status_enum_exports.Disconnected) {
12848
+ return { nextStatePromise: this.#nextStatePromise() };
12849
+ }
12850
+ if (this.#state.name === connection_status_enum_exports.Connected) {
12851
+ this.#connectingStartedAt = void 0;
12852
+ }
12853
+ this.#maybeStopTimeoutInterval();
12854
+ this.#state = {
12855
+ name: connection_status_enum_exports.Disconnected,
12856
+ reason
12857
+ };
12858
+ const nextStatePromise = this.#publishStateAndGetPromise();
12859
+ return { nextStatePromise };
12860
+ }
12861
+ /**
12862
+ * Transition to error state.
12863
+ * This pauses the run loop until connect() is called.
12864
+ * Resets the 5-minute retry window and attempt counter.
12865
+ *
12866
+ * @returns An object containing a promise that resolves on the next state change.
12867
+ */
12868
+ error(reason) {
12869
+ if (this.#state.name === connection_status_enum_exports.Closed) {
12870
+ return { nextStatePromise: this.#nextStatePromise() };
12871
+ }
12872
+ if (this.#state.name === connection_status_enum_exports.Error) {
12873
+ return { nextStatePromise: this.#nextStatePromise() };
12874
+ }
12875
+ this.#connectingStartedAt = void 0;
12876
+ this.#maybeStopTimeoutInterval();
12877
+ this.#state = {
12878
+ name: connection_status_enum_exports.Error,
12879
+ reason
12880
+ };
12881
+ const nextStatePromise = this.#publishStateAndGetPromise();
12882
+ return { nextStatePromise };
12883
+ }
12884
+ /**
12885
+ * Transition to closed state.
12886
+ * This is terminal - no further transitions are allowed.
12887
+ */
12888
+ closed() {
12889
+ if (this.#state.name === connection_status_enum_exports.Closed) {
12890
+ return;
12891
+ }
12892
+ this.#connectingStartedAt = void 0;
12893
+ this.#maybeStopTimeoutInterval();
12894
+ this.#state = {
12895
+ name: connection_status_enum_exports.Closed,
12896
+ reason: new ClientError({
12897
+ kind: client_error_kind_enum_exports.ClientClosed,
12898
+ message: "Zero was explicitly closed by calling zero.close()"
12899
+ })
12900
+ };
12901
+ this.#publishState();
12902
+ this.cleanup();
12903
+ return;
12904
+ }
12905
+ cleanup = () => {
12906
+ this._listeners.clear();
12907
+ this.#resolveNextStateWaiters();
12908
+ };
12909
+ #resolveNextStateWaiters() {
12910
+ this.#stateChangeResolver.resolve(this.#state);
12911
+ this.#stateChangeResolver = resolver9();
12912
+ }
12913
+ #publishState() {
12914
+ this.notify(this.#state);
12915
+ this.#resolveNextStateWaiters();
12916
+ }
12917
+ #nextStatePromise() {
12918
+ return this.#stateChangeResolver.promise;
12919
+ }
12920
+ #publishStateAndGetPromise() {
12921
+ this.#publishState();
12922
+ return this.#nextStatePromise();
12923
+ }
12924
+ /**
12925
+ * Check if we should transition from connecting to disconnected due to timeout.
12926
+ * Returns true if the transition happened.
12927
+ */
12928
+ #checkTimeout() {
12929
+ if (this.#state.name !== connection_status_enum_exports.Connecting) {
12930
+ return false;
12931
+ }
12932
+ const now = Date.now();
12933
+ if (now >= this.#state.disconnectAt) {
12934
+ this.disconnected(
12935
+ new ClientError({
12936
+ kind: client_error_kind_enum_exports.DisconnectTimeout,
12937
+ message: `Zero was unable to connect for ${Math.floor(this.#disconnectTimeoutMs / 1e3)} seconds and was disconnected`
12938
+ })
12939
+ );
12940
+ return true;
12941
+ }
12942
+ return false;
12943
+ }
12944
+ #maybeStartTimeoutInterval() {
12945
+ if (this.#timeoutInterval !== void 0) {
12946
+ return;
12947
+ }
12948
+ this.#timeoutInterval = setInterval(() => {
12949
+ this.#checkTimeout();
12950
+ }, this.#timeoutCheckIntervalMs);
12951
+ }
12952
+ #maybeStopTimeoutInterval() {
12953
+ if (this.#timeoutInterval === void 0) {
12954
+ return;
11084
12955
  }
11085
- return Promise.resolve();
12956
+ clearInterval(this.#timeoutInterval);
12957
+ this.#timeoutInterval = void 0;
11086
12958
  }
11087
- release(name, fn) {
11088
- mockLockNames.delete(name);
11089
- for (const listener of mockListeners) {
11090
- listener(name);
12959
+ };
12960
+ var throwIfConnectionError = (state) => {
12961
+ 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) {
12962
+ 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)) {
12963
+ return;
11091
12964
  }
11092
- for (const listener of this.#listeners) {
11093
- mockListeners.delete(listener);
12965
+ throw state.reason;
12966
+ }
12967
+ };
12968
+
12969
+ // ../zero-client/src/client/connection.ts
12970
+ var ConnectionImpl = class {
12971
+ #connectionManager;
12972
+ #lc;
12973
+ #source;
12974
+ constructor(connectionManager, lc) {
12975
+ this.#connectionManager = connectionManager;
12976
+ this.#lc = lc;
12977
+ this.#source = new ConnectionSource(connectionManager);
12978
+ }
12979
+ get state() {
12980
+ return this.#source;
12981
+ }
12982
+ async connect() {
12983
+ const lc = this.#lc.withContext("connect");
12984
+ if (!this.#connectionManager.isInTerminalState()) {
12985
+ lc.debug?.(
12986
+ "connect() called but not in a terminal state. Current state:",
12987
+ this.#connectionManager.state.name
12988
+ );
12989
+ return;
11094
12990
  }
11095
- fn();
12991
+ lc.info?.(
12992
+ `Resuming connection from state: ${this.#connectionManager.state.name}`
12993
+ );
12994
+ const { nextStatePromise } = this.#connectionManager.connecting();
12995
+ await nextStatePromise;
11096
12996
  }
11097
- async *queryExclusive() {
11098
- yield* mockLockNames;
12997
+ };
12998
+ var ConnectionSource = class {
12999
+ #connectionManager;
13000
+ constructor(connectionManager) {
13001
+ this.#connectionManager = connectionManager;
13002
+ }
13003
+ get current() {
13004
+ return this.#connectionManager.state;
13005
+ }
13006
+ get subscribe() {
13007
+ return this.#connectionManager.subscribe;
11099
13008
  }
11100
13009
  };
11101
13010
 
11102
- // ../zero-client/src/client/connection-state-enum.ts
11103
- var Disconnected = 0;
11104
- var Connecting = 1;
11105
- var Connected = 2;
11106
-
11107
13011
  // ../zql/src/ivm/memory-storage.ts
11108
13012
  import { compareUTF8 as compareUTF83 } from "compare-utf8";
11109
13013
 
@@ -11866,21 +13770,6 @@ var MemorySource = class _MemorySource {
11866
13770
  const exists = (row) => data.has(row);
11867
13771
  const setOverlay = (o) => this.#overlay = o;
11868
13772
  const writeChange = (c) => this.#writeChange(c);
11869
- if (change.type === "set") {
11870
- const existing = data.get(change.row);
11871
- if (existing !== void 0) {
11872
- change = {
11873
- type: "edit",
11874
- row: change.row,
11875
- oldRow: existing
11876
- };
11877
- } else {
11878
- change = {
11879
- type: "add",
11880
- row: change.row
11881
- };
11882
- }
11883
- }
11884
13773
  yield* genPushAndWriteWithSplitEdit(
11885
13774
  this.#connections,
11886
13775
  change,
@@ -12579,16 +14468,10 @@ async function upsertImpl(tx, arg, schema, ivmBranch) {
12579
14468
  schema.tables[arg.tableName].primaryKey,
12580
14469
  arg.value
12581
14470
  );
12582
- const val = defaultOptionalFieldsToNull(
12583
- schema.tables[arg.tableName],
12584
- arg.value
12585
- );
12586
- await tx.set(key, val);
12587
- if (ivmBranch) {
12588
- must(ivmBranch.getSource(arg.tableName)).push({
12589
- type: "set",
12590
- row: arg.value
12591
- });
14471
+ if (await tx.has(key)) {
14472
+ await updateImpl(tx, { ...arg, op: "update" }, schema, ivmBranch);
14473
+ } else {
14474
+ await insertImpl(tx, { ...arg, op: "insert" }, schema, ivmBranch);
12592
14475
  }
12593
14476
  }
12594
14477
  async function updateImpl(tx, arg, schema, ivmBranch) {
@@ -13065,7 +14948,7 @@ function makeMessage(message, context, logLevel) {
13065
14948
  }
13066
14949
 
13067
14950
  // ../zero-client/src/client/version.ts
13068
- var version2 = "0.24.2025102200";
14951
+ var version2 = "0.25.0-canary.1";
13069
14952
 
13070
14953
  // ../zero-client/src/client/log-options.ts
13071
14954
  var LevelFilterLogSink = class {
@@ -13122,6 +15005,15 @@ function createLogOptions(options, createDatadogLogSink = (options2) => new Data
13122
15005
  }
13123
15006
 
13124
15007
  // ../zero-client/src/client/metric-name-enum.ts
15008
+ var metric_name_enum_exports = {};
15009
+ __export(metric_name_enum_exports, {
15010
+ LastConnectError: () => LastConnectError,
15011
+ LastConnectErrorV2: () => LastConnectErrorV2,
15012
+ NotConnected: () => NotConnected,
15013
+ TimeToConnectMs: () => TimeToConnectMs,
15014
+ TimeToConnectMsV2: () => TimeToConnectMsV2,
15015
+ TotalTimeToConnectMs: () => TotalTimeToConnectMs
15016
+ });
13125
15017
  var TimeToConnectMs = "time_to_connect_ms";
13126
15018
  var LastConnectError = "last_connect_error";
13127
15019
  var TimeToConnectMsV2 = "time_to_connect_ms_v2";
@@ -13133,13 +15025,25 @@ var NotConnected = "not_connected";
13133
15025
  var DID_NOT_CONNECT_VALUE = 100 * 1e3;
13134
15026
  var REPORT_INTERVAL_MS = 5e3;
13135
15027
  function getLastConnectErrorValue(reason) {
13136
- if ("server" in reason) {
13137
- return `server_${camelToSnake(reason.server)}`;
13138
- }
13139
- return `client_${camelToSnake(reason.client)}`;
15028
+ return `${isServerError(reason) ? "server_" : "client_"}${camelToSnake(reason.kind)}`;
15029
+ }
15030
+ function camelToSnake(kind) {
15031
+ return kind.split(/\.?(?=[A-Z])/).join("_").toLowerCase();
13140
15032
  }
13141
- function camelToSnake(s) {
13142
- return s.split(/\.?(?=[A-Z])/).join("_").toLowerCase();
15033
+ function shouldReportConnectError(reason) {
15034
+ if (!isClientError(reason)) {
15035
+ return true;
15036
+ }
15037
+ switch (reason.kind) {
15038
+ case client_error_kind_enum_exports.Hidden:
15039
+ case client_error_kind_enum_exports.ClientClosed:
15040
+ case client_error_kind_enum_exports.UserDisconnect:
15041
+ case client_error_kind_enum_exports.CleanClose:
15042
+ case client_error_kind_enum_exports.AbruptClose:
15043
+ return false;
15044
+ default:
15045
+ return true;
15046
+ }
13143
15047
  }
13144
15048
  var MetricManager = class {
13145
15049
  #reportIntervalMs;
@@ -13180,7 +15084,7 @@ var MetricManager = class {
13180
15084
  // be encapsulated with the ConnectionState. This will probably happen as part
13181
15085
  // of https://github.com/rocicorp/reflect-server/issues/255.
13182
15086
  timeToConnectMs = this.#register(
13183
- new Gauge(TimeToConnectMs)
15087
+ new Gauge(metric_name_enum_exports.TimeToConnectMs)
13184
15088
  );
13185
15089
  // lastConnectError records the last error that occurred when connecting,
13186
15090
  // if any. It is cleared when connecting successfully or when reported, so this
@@ -13188,28 +15092,28 @@ var MetricManager = class {
13188
15092
  // we are still not connected.
13189
15093
  lastConnectError = this.#register(
13190
15094
  new State(
13191
- LastConnectError,
15095
+ metric_name_enum_exports.LastConnectError,
13192
15096
  true
13193
15097
  // clearOnFlush
13194
15098
  )
13195
15099
  );
13196
15100
  // notConnected records the reason why the client is not currently connected.
13197
15101
  // It is cleared when the client successfully connects.
13198
- #notConnected = this.#register(new State(NotConnected));
15102
+ #notConnected = this.#register(new State(metric_name_enum_exports.NotConnected));
13199
15103
  // The time from the call to connect() to receiving the 'connected' ws message
13200
15104
  // for the current connection. Cleared when the client is not connected.
13201
15105
  // TODO: Not actually currently cleared on disconnect untill there is a
13202
15106
  // connect error, or client reports disconnected and waiting for visible.
13203
15107
  // Should have a value iff _notConnected has no value.
13204
15108
  #timeToConnectMsV2 = this.#register(
13205
- new Gauge(TimeToConnectMsV2)
15109
+ new Gauge(metric_name_enum_exports.TimeToConnectMsV2)
13206
15110
  );
13207
15111
  // lastConnectErrorV2 records the last error that occurred when connecting,
13208
15112
  // if any. It is cleared when the client successfully connects or
13209
15113
  // stops trying to connect due to being hidden.
13210
15114
  // Should have a value iff notConnected state is NotConnectedReason.Error.
13211
15115
  #lastConnectErrorV2 = this.#register(
13212
- new State(LastConnectErrorV2)
15116
+ new State(metric_name_enum_exports.LastConnectErrorV2)
13213
15117
  );
13214
15118
  // The total time it took to connect across retries for the current
13215
15119
  // connection. Cleared when the client is not connected.
@@ -13218,7 +15122,7 @@ var MetricManager = class {
13218
15122
  // See Zero.#totalToConnectStart for details of how this total is computed.
13219
15123
  // Should have a value iff _notConnected has no value.
13220
15124
  #totalTimeToConnectMs = this.#register(
13221
- new Gauge(TotalTimeToConnectMs)
15125
+ new Gauge(metric_name_enum_exports.TotalTimeToConnectMs)
13222
15126
  );
13223
15127
  #setNotConnectedReason(reason) {
13224
15128
  this.#notConnected.set(reason);
@@ -13360,7 +15264,7 @@ var State = class {
13360
15264
  };
13361
15265
 
13362
15266
  // ../zero-client/src/client/mutation-tracker.ts
13363
- import { resolver as resolver9 } from "@rocicorp/resolver";
15267
+ import { resolver as resolver10 } from "@rocicorp/resolver";
13364
15268
  var currentEphemeralID = 0;
13365
15269
  function nextEphemeralID() {
13366
15270
  return ++currentEphemeralID;
@@ -13371,10 +15275,11 @@ var MutationTracker = class {
13371
15275
  #allMutationsAppliedListeners;
13372
15276
  #lc;
13373
15277
  #ackMutations;
15278
+ #onFatalError;
13374
15279
  #clientID;
13375
15280
  #largestOutstandingMutationID;
13376
15281
  #currentMutationID;
13377
- constructor(lc, ackMutations) {
15282
+ constructor(lc, ackMutations, onFatalError) {
13378
15283
  this.#lc = lc.withContext("MutationTracker");
13379
15284
  this.#outstandingMutations = /* @__PURE__ */ new Map();
13380
15285
  this.#ephemeralIDsByMutationID = /* @__PURE__ */ new Map();
@@ -13382,6 +15287,7 @@ var MutationTracker = class {
13382
15287
  this.#largestOutstandingMutationID = 0;
13383
15288
  this.#currentMutationID = 0;
13384
15289
  this.#ackMutations = ackMutations;
15290
+ this.#onFatalError = onFatalError;
13385
15291
  }
13386
15292
  setClientIDAndWatch(clientID, experimentalWatch) {
13387
15293
  assert(this.#clientID === void 0, "clientID already set");
@@ -13398,7 +15304,7 @@ var MutationTracker = class {
13398
15304
  }
13399
15305
  trackMutation() {
13400
15306
  const id = nextEphemeralID();
13401
- const mutationResolver = resolver9();
15307
+ const mutationResolver = resolver10();
13402
15308
  this.#outstandingMutations.set(id, {
13403
15309
  resolver: mutationResolver
13404
15310
  });
@@ -13469,10 +15375,53 @@ var MutationTracker = class {
13469
15375
  "Received an error response when pushing mutations",
13470
15376
  response
13471
15377
  );
15378
+ const fatalError = this.#fatalErrorFromPushError(response);
15379
+ if (fatalError) {
15380
+ this.#onFatalError(fatalError);
15381
+ }
13472
15382
  } else {
13473
15383
  this.#processPushOk(response);
13474
15384
  }
13475
15385
  }
15386
+ #fatalErrorFromPushError(error) {
15387
+ switch (error.error) {
15388
+ case "unsupportedPushVersion":
15389
+ return new ProtocolError({
15390
+ kind: error_kind_enum_exports.PushFailed,
15391
+ origin: error_origin_enum_exports.ZeroCache,
15392
+ reason: error_reason_enum_exports.Internal,
15393
+ message: `Unsupported push version`,
15394
+ mutationIDs: []
15395
+ });
15396
+ case "unsupportedSchemaVersion":
15397
+ return new ProtocolError({
15398
+ kind: error_kind_enum_exports.PushFailed,
15399
+ origin: error_origin_enum_exports.ZeroCache,
15400
+ reason: error_reason_enum_exports.Internal,
15401
+ message: `Unsupported schema version`,
15402
+ mutationIDs: []
15403
+ });
15404
+ case "http":
15405
+ return new ProtocolError({
15406
+ kind: error_kind_enum_exports.PushFailed,
15407
+ origin: error_origin_enum_exports.ZeroCache,
15408
+ reason: error_reason_enum_exports.HTTP,
15409
+ status: error.status,
15410
+ message: `Fetch from API server returned non-OK status ${error.status}: ${error.details ?? "unknown"}`,
15411
+ mutationIDs: []
15412
+ });
15413
+ case "zeroPusher":
15414
+ return new ProtocolError({
15415
+ kind: error_kind_enum_exports.PushFailed,
15416
+ origin: error_origin_enum_exports.ZeroCache,
15417
+ reason: error_reason_enum_exports.Internal,
15418
+ message: `ZeroPusher error: ${error.details ?? "unknown"}`,
15419
+ mutationIDs: []
15420
+ });
15421
+ default:
15422
+ unreachable(error);
15423
+ }
15424
+ }
13476
15425
  /**
13477
15426
  * DEPRECATED: to be removed when we switch to fully driving
13478
15427
  * mutation resolution via poke.
@@ -13566,6 +15515,17 @@ var MutationTracker = class {
13566
15515
  const entry = this.#outstandingMutations.get(ephemeralID);
13567
15516
  assert(entry && entry.mutationID === mid);
13568
15517
  this.#settleMutation(ephemeralID, entry, "reject", error);
15518
+ if (error.error === "oooMutation") {
15519
+ this.#onFatalError(
15520
+ new ProtocolError({
15521
+ kind: error_kind_enum_exports.InvalidPush,
15522
+ origin: error_origin_enum_exports.Server,
15523
+ reason: error_reason_enum_exports.Internal,
15524
+ message: "Server reported an out-of-order mutation",
15525
+ details: error.details
15526
+ })
15527
+ );
15528
+ }
13569
15529
  }
13570
15530
  #processMutationOk(clientID, mid, result) {
13571
15531
  assert(
@@ -13615,16 +15575,13 @@ var MutationTracker = class {
13615
15575
  }
13616
15576
  };
13617
15577
 
13618
- // ../zero-client/src/client/ping-result-enum.ts
13619
- var TimedOut = 0;
13620
- var Success = 1;
13621
-
13622
15578
  // ../zero-client/src/client/query-manager.ts
13623
15579
  var QueryManager = class {
13624
15580
  #clientID;
13625
15581
  #clientToServer;
13626
15582
  #serverToClient;
13627
15583
  #send;
15584
+ #onFatalError;
13628
15585
  #queries = /* @__PURE__ */ new Map();
13629
15586
  #recentQueriesMaxSize;
13630
15587
  #recentQueries = /* @__PURE__ */ new Set();
@@ -13638,7 +15595,7 @@ var QueryManager = class {
13638
15595
  #metrics = newMetrics();
13639
15596
  #queryMetrics = /* @__PURE__ */ new Map();
13640
15597
  #slowMaterializeThreshold;
13641
- constructor(lc, mutationTracker, clientID, tables, send2, experimentalWatch, recentQueriesMaxSize, queryChangeThrottleMs, slowMaterializeThreshold) {
15598
+ constructor(lc, mutationTracker, clientID, tables, send2, experimentalWatch, recentQueriesMaxSize, queryChangeThrottleMs, slowMaterializeThreshold, onFatalError) {
13642
15599
  this.#lc = lc.withContext("QueryManager");
13643
15600
  this.#clientID = clientID;
13644
15601
  this.#clientToServer = clientToServer(tables);
@@ -13648,6 +15605,7 @@ var QueryManager = class {
13648
15605
  this.#mutationTracker = mutationTracker;
13649
15606
  this.#queryChangeThrottleMs = queryChangeThrottleMs;
13650
15607
  this.#slowMaterializeThreshold = slowMaterializeThreshold;
15608
+ this.#onFatalError = onFatalError;
13651
15609
  this.#mutationTracker.onAllMutationsApplied(() => {
13652
15610
  if (this.#pendingRemovals.length === 0) {
13653
15611
  return;
@@ -13754,6 +15712,26 @@ var QueryManager = class {
13754
15712
  if (entry) {
13755
15713
  entry.gotCallbacks.forEach((callback) => callback(false, error));
13756
15714
  }
15715
+ if (error.error !== "app") {
15716
+ this.#onFatalError(
15717
+ new ProtocolError(
15718
+ error.error === "http" ? {
15719
+ kind: error_kind_enum_exports.TransformFailed,
15720
+ origin: error_origin_enum_exports.ZeroCache,
15721
+ reason: error_reason_enum_exports.HTTP,
15722
+ message: `HTTP ${error.status} transforming queries`,
15723
+ status: error.status,
15724
+ queryIDs: []
15725
+ } : {
15726
+ kind: error_kind_enum_exports.TransformFailed,
15727
+ origin: error_origin_enum_exports.ZeroCache,
15728
+ reason: error_reason_enum_exports.Internal,
15729
+ message: `Unknown error transforming queries`,
15730
+ queryIDs: []
15731
+ }
15732
+ )
15733
+ );
15734
+ }
13757
15735
  }
13758
15736
  }
13759
15737
  addCustom(ast, { name, args }, ttl, gotCallback) {
@@ -14052,39 +16030,6 @@ function nextBackoff(lc, now) {
14052
16030
  };
14053
16031
  }
14054
16032
 
14055
- // ../zero-client/src/client/server-error.ts
14056
- var ServerError = class extends Error {
14057
- name = "ServerError";
14058
- errorBody;
14059
- get kind() {
14060
- return this.errorBody.kind;
14061
- }
14062
- constructor(errorBody) {
14063
- super(errorBody.kind + ": " + errorBody.message);
14064
- this.errorBody = errorBody;
14065
- }
14066
- };
14067
- function isServerError(ex) {
14068
- return ex instanceof ServerError;
14069
- }
14070
- function isAuthError(ex) {
14071
- return isServerError(ex) && isAuthErrorKind(ex.kind);
14072
- }
14073
- function isAuthErrorKind(kind) {
14074
- return kind === error_kind_enum_exports.AuthInvalidated || kind === error_kind_enum_exports.Unauthorized;
14075
- }
14076
- function isBackoffError(ex) {
14077
- if (isServerError(ex)) {
14078
- switch (ex.errorBody.kind) {
14079
- case error_kind_enum_exports.Rebalance:
14080
- case error_kind_enum_exports.Rehome:
14081
- case error_kind_enum_exports.ServerOverloaded:
14082
- return ex.errorBody;
14083
- }
14084
- }
14085
- return void 0;
14086
- }
14087
-
14088
16033
  // ../zero-client/src/client/server-option.ts
14089
16034
  function validateServerParam(paramName, server) {
14090
16035
  const expectedProtocol = "http";
@@ -14508,7 +16453,6 @@ var ZeroRep = class {
14508
16453
  };
14509
16454
 
14510
16455
  // ../zero-client/src/client/zero.ts
14511
- var onSetConnectionStateSymbol = Symbol();
14512
16456
  var exposedToTestingSymbol = Symbol();
14513
16457
  var createLogOptionsSymbol = Symbol();
14514
16458
  var RUN_LOOP_INTERVAL_MS = 5e3;
@@ -14516,6 +16460,7 @@ var PING_INTERVAL_MS = 5e3;
14516
16460
  var PING_TIMEOUT_MS = 5e3;
14517
16461
  var PULL_TIMEOUT_MS = 5e3;
14518
16462
  var DEFAULT_DISCONNECT_HIDDEN_DELAY_MS = 5e3;
16463
+ var DEFAULT_DISCONNECT_TIMEOUT_MS = 5 * 60 * 1e3;
14519
16464
  var CONNECT_TIMEOUT_MS = 1e4;
14520
16465
  var CHECK_CONNECTIVITY_ON_ERROR_FREQUENCY = 6;
14521
16466
  var NULL_LAST_MUTATION_ID_SENT = { clientID: "", id: -1 };
@@ -14602,35 +16547,22 @@ var Zero = class _Zero {
14602
16547
  };
14603
16548
  #zeroContext;
14604
16549
  queryDelegate;
14605
- #connectResolver = resolver10();
14606
16550
  #pendingPullsByRequestID = /* @__PURE__ */ new Map();
14607
16551
  #lastMutationIDReceived = 0;
14608
16552
  #socket = void 0;
14609
- #socketResolver = resolver10();
14610
- #connectionStateChangeResolver = resolver10();
16553
+ #socketResolver = resolver11();
14611
16554
  /**
14612
- * This resolver is only used for rejections. It is awaited in the connected
14613
- * state (including when waiting for a pong). It is rejected when we get an
14614
- * invalid message or an 'error' message.
16555
+ * Utility promise that resolves when the socket transitions to connected.
16556
+ * It rejects if we hit an error or timeout before the connected message.
16557
+ * Used by push/pull helpers to queue work until the connection is usable.
14615
16558
  */
14616
- #rejectMessageError = void 0;
16559
+ #connectResolver = resolver11();
14617
16560
  #closeAbortController = new AbortController();
14618
16561
  #visibilityWatcher;
14619
- // We use an accessor pair to allow the subclass to override the setter.
14620
- #connectionState = Disconnected;
16562
+ #connectionManager;
16563
+ #connection;
14621
16564
  #activeClientsManager;
14622
16565
  #inspector;
14623
- #setConnectionState(state) {
14624
- if (state === this.#connectionState) {
14625
- return;
14626
- }
14627
- this.#connectionState = state;
14628
- this.#connectionStateChangeResolver.resolve(state);
14629
- this.#connectionStateChangeResolver = resolver10();
14630
- if (false) {
14631
- asTestZero(this)[onSetConnectionStateSymbol]?.(state);
14632
- }
14633
- }
14634
16566
  #connectStart = void 0;
14635
16567
  // Set on connect attempt if currently undefined.
14636
16568
  // Reset to undefined when
@@ -14664,7 +16596,10 @@ var Zero = class _Zero {
14664
16596
  slowMaterializeThreshold = 5e3
14665
16597
  } = options;
14666
16598
  if (!userID) {
14667
- throw new Error("ZeroOptions.userID must not be empty.");
16599
+ throw new ClientError({
16600
+ kind: client_error_kind_enum_exports.Internal,
16601
+ message: "ZeroOptions.userID must not be empty."
16602
+ });
14668
16603
  }
14669
16604
  const server = getServer(options.server);
14670
16605
  this.#enableAnalytics = shouldEnableAnalytics(
@@ -14682,9 +16617,10 @@ var Zero = class _Zero {
14682
16617
  }
14683
16618
  }
14684
16619
  if (hiddenTabDisconnectDelay < 0) {
14685
- throw new Error(
14686
- "ZeroOptions.hiddenTabDisconnectDelay must not be negative."
14687
- );
16620
+ throw new ClientError({
16621
+ kind: client_error_kind_enum_exports.Internal,
16622
+ message: "ZeroOptions.hiddenTabDisconnectDelay must not be negative."
16623
+ });
14688
16624
  }
14689
16625
  this.#onlineManager = new OnlineManager();
14690
16626
  if (onOnlineChange) {
@@ -14698,9 +16634,17 @@ var Zero = class _Zero {
14698
16634
  enableAnalytics: this.#enableAnalytics
14699
16635
  });
14700
16636
  const logOptions = this.#logOptions;
16637
+ this.#connectionManager = new ConnectionManager({
16638
+ disconnectTimeoutMs: DEFAULT_DISCONNECT_TIMEOUT_MS
16639
+ });
14701
16640
  const { enableLegacyMutators = true, enableLegacyQueries = true } = schema;
14702
16641
  const replicacheMutators = {
14703
- [CRUD_MUTATION_NAME]: enableLegacyMutators ? makeCRUDMutator(schema) : () => Promise.reject(new Error("Zero CRUD mutators are not enabled."))
16642
+ [CRUD_MUTATION_NAME]: enableLegacyMutators ? makeCRUDMutator(schema) : () => Promise.reject(
16643
+ new ClientError({
16644
+ kind: client_error_kind_enum_exports.Internal,
16645
+ message: "Zero CRUD mutators are not enabled."
16646
+ })
16647
+ )
14704
16648
  };
14705
16649
  this.#ivmMain = new IVMSourceBranch(schema.tables);
14706
16650
  function assertUnique(key) {
@@ -14726,7 +16670,10 @@ var Zero = class _Zero {
14726
16670
  const lc = new ZeroLogContext(logOptions.logLevel, {}, logSink);
14727
16671
  this.#mutationTracker = new MutationTracker(
14728
16672
  lc,
14729
- (upTo) => this.#send(["ackMutationResponses", upTo])
16673
+ (upTo) => this.#send(["ackMutationResponses", upTo]),
16674
+ (error) => {
16675
+ this.#disconnect(lc, error);
16676
+ }
14730
16677
  );
14731
16678
  if (options.mutators) {
14732
16679
  for (const [namespaceOrKey, mutatorOrMutators] of Object.entries(
@@ -14832,6 +16779,7 @@ var Zero = class _Zero {
14832
16779
  this.#server = server;
14833
16780
  this.userID = userID;
14834
16781
  this.#lc = lc.withContext("clientID", rep.clientID);
16782
+ this.#connection = new ConnectionImpl(this.#connectionManager, this.#lc);
14835
16783
  this.#mutationTracker.setClientIDAndWatch(
14836
16784
  rep.clientID,
14837
16785
  rep.experimentalWatch.bind(rep)
@@ -14902,7 +16850,10 @@ var Zero = class _Zero {
14902
16850
  rep.experimentalWatch.bind(rep),
14903
16851
  maxRecentQueries,
14904
16852
  options.queryChangeThrottleMs ?? DEFAULT_QUERY_CHANGE_THROTTLE_MS,
14905
- slowMaterializeThreshold
16853
+ slowMaterializeThreshold,
16854
+ (error) => {
16855
+ this.#disconnect(lc, error);
16856
+ }
14906
16857
  );
14907
16858
  this.#clientToServer = clientToServer(schema.tables);
14908
16859
  this.#deleteClientsManager = new DeleteClientsManager(
@@ -14946,7 +16897,7 @@ var Zero = class _Zero {
14946
16897
  logOptions: this.#logOptions,
14947
16898
  connectStart: () => this.#connectStart,
14948
16899
  socketResolver: () => this.#socketResolver,
14949
- connectionState: () => this.#connectionState
16900
+ connectionManager: () => this.#connectionManager
14950
16901
  };
14951
16902
  }
14952
16903
  }
@@ -14978,7 +16929,7 @@ var Zero = class _Zero {
14978
16929
  }
14979
16930
  }
14980
16931
  #send(msg) {
14981
- if (this.#socket && this.#connectionState === Connected) {
16932
+ if (this.#socket && this.#connectionManager.is(connection_status_enum_exports.Connected)) {
14982
16933
  send(this.#socket, msg);
14983
16934
  }
14984
16935
  }
@@ -15025,6 +16976,27 @@ var Zero = class _Zero {
15025
16976
  get schemaVersion() {
15026
16977
  return this.#rep.schemaVersion;
15027
16978
  }
16979
+ /**
16980
+ * The schema passed into Zero when it was constructed.
16981
+ *
16982
+ * This can be paired with the inspector API to explore the client cache for
16983
+ * debugging or tooling. The inspector exposes the raw key/value map as well
16984
+ * as the per-table rows that back `zero.query[tableName].run()`.
16985
+ *
16986
+ * ```ts
16987
+ * const inspector = __zero.inspector;
16988
+ * const client = inspector.client;
16989
+ *
16990
+ * console.log('client map:', await client.map());
16991
+ *
16992
+ * for (const tableName of Object.keys(__zero.schema.tables)) {
16993
+ * console.table(await client.rows(tableName));
16994
+ * }
16995
+ * ```
16996
+ */
16997
+ get schema() {
16998
+ return this.#options.schema;
16999
+ }
15028
17000
  /**
15029
17001
  * The client ID for this instance of Zero. Each instance
15030
17002
  * gets a unique client ID.
@@ -15072,6 +17044,25 @@ var Zero = class _Zero {
15072
17044
  * will throw an error.
15073
17045
  */
15074
17046
  mutateBatch;
17047
+ /**
17048
+ * The connection API for managing Zero's connection lifecycle.
17049
+ *
17050
+ * Use this to monitor connection state and manually control connections.
17051
+ *
17052
+ * @example
17053
+ * ```ts
17054
+ * // Subscribe to connection state changes
17055
+ * z.connection.state.subscribe(state => {
17056
+ * console.log('Connection state:', state.name);
17057
+ * });
17058
+ *
17059
+ * // Manually resume connection from error state
17060
+ * await z.connection.connect();
17061
+ * ```
17062
+ */
17063
+ get connection() {
17064
+ return this.#connection;
17065
+ }
15075
17066
  /**
15076
17067
  * Whether this Zero instance has been closed.
15077
17068
  *
@@ -15079,7 +17070,7 @@ var Zero = class _Zero {
15079
17070
  * longer query or mutate data with it, and its query views stop updating.
15080
17071
  */
15081
17072
  get closed() {
15082
- return this.#rep.closed;
17073
+ return this.#connectionManager.is(connection_status_enum_exports.Closed);
15083
17074
  }
15084
17075
  /**
15085
17076
  * Closes this Zero instance.
@@ -15089,23 +17080,35 @@ var Zero = class _Zero {
15089
17080
  */
15090
17081
  async close() {
15091
17082
  const lc = this.#lc.withContext("close");
15092
- lc.debug?.("Closing Zero instance. Stack:", new Error().stack);
15093
- this.#onlineManager.cleanup();
15094
- if (this.#connectionState !== Disconnected) {
15095
- this.#disconnect(
15096
- lc,
15097
- {
15098
- client: "ClientClosed"
15099
- },
15100
- CLOSE_CODE_NORMAL
15101
- );
17083
+ try {
17084
+ if (this.closed) {
17085
+ lc.debug?.("close() called on already closed instance");
17086
+ return;
17087
+ }
17088
+ lc.debug?.("Closing Zero instance. Stack:", new Error().stack);
17089
+ this.#onlineManager.cleanup();
17090
+ if (!this.#connectionManager.is(connection_status_enum_exports.Disconnected)) {
17091
+ this.#disconnect(
17092
+ lc,
17093
+ new ClientError({
17094
+ kind: client_error_kind_enum_exports.ClientClosed,
17095
+ message: "Zero instance closed by user"
17096
+ }),
17097
+ CLOSE_CODE_NORMAL
17098
+ );
17099
+ }
17100
+ lc.debug?.("Aborting closeAbortController due to close()");
17101
+ this.#closeAbortController.abort();
17102
+ this.#metrics.stop();
17103
+ const ret = await this.#rep.close();
17104
+ this.#unexpose();
17105
+ return ret;
17106
+ } catch (e) {
17107
+ lc.error?.("Error closing Zero instance", e);
17108
+ throw e;
17109
+ } finally {
17110
+ this.#connectionManager.closed();
15102
17111
  }
15103
- lc.debug?.("Aborting closeAbortController due to close()");
15104
- this.#closeAbortController.abort();
15105
- this.#metrics.stop();
15106
- const ret = await this.#rep.close();
15107
- this.#unexpose();
15108
- return ret;
15109
17112
  }
15110
17113
  #onMessage = (e) => {
15111
17114
  const lc = this.#lc;
@@ -15114,11 +17117,6 @@ var Zero = class _Zero {
15114
17117
  lc.debug?.("ignoring message because already closed");
15115
17118
  return;
15116
17119
  }
15117
- const rejectInvalidMessage = (e2) => this.#rejectMessageError?.reject(
15118
- new Error(
15119
- `Invalid message received from server: ${e2 instanceof Error ? e2.message + ". " : ""}${data}`
15120
- )
15121
- );
15122
17120
  let downMessage;
15123
17121
  const { data } = e;
15124
17122
  try {
@@ -15128,7 +17126,11 @@ var Zero = class _Zero {
15128
17126
  "passthrough"
15129
17127
  );
15130
17128
  } catch (e2) {
15131
- rejectInvalidMessage(e2);
17129
+ const invalidMessageError = new ClientError({
17130
+ kind: client_error_kind_enum_exports.InvalidMessage,
17131
+ message: `Invalid message received from server: ${e2 instanceof Error ? e2.message + ". " : ""}${data}`
17132
+ });
17133
+ this.#disconnect(lc, invalidMessageError);
15132
17134
  return;
15133
17135
  }
15134
17136
  this.#messageCount++;
@@ -15163,9 +17165,14 @@ var Zero = class _Zero {
15163
17165
  break;
15164
17166
  case "inspect":
15165
17167
  break;
15166
- default:
15167
- msgType;
15168
- rejectInvalidMessage();
17168
+ default: {
17169
+ const invalidMessageError = new ClientError({
17170
+ kind: client_error_kind_enum_exports.InvalidMessage,
17171
+ message: `Invalid message received from server: ${data}`
17172
+ });
17173
+ this.#disconnect(lc, invalidMessageError);
17174
+ return;
17175
+ }
15169
17176
  }
15170
17177
  };
15171
17178
  #onOpen = () => {
@@ -15193,9 +17200,17 @@ var Zero = class _Zero {
15193
17200
  wasClean
15194
17201
  });
15195
17202
  }
15196
- const closeKind = wasClean ? "CleanClose" : "AbruptClose";
15197
- this.#connectResolver.reject(new CloseError(closeKind));
15198
- this.#disconnect(lc, { client: closeKind });
17203
+ const closeError2 = new ClientError(
17204
+ wasClean ? {
17205
+ kind: client_error_kind_enum_exports.CleanClose,
17206
+ message: "WebSocket connection closed cleanly"
17207
+ } : {
17208
+ kind: client_error_kind_enum_exports.AbruptClose,
17209
+ message: "WebSocket connection closed abruptly"
17210
+ }
17211
+ );
17212
+ this.#connectResolver.reject(closeError2);
17213
+ this.#disconnect(lc, closeError2);
15199
17214
  };
15200
17215
  // An error on the connection is fatal for the connection.
15201
17216
  async #handleErrorMessage(lc, downMessage) {
@@ -15206,14 +17221,13 @@ var Zero = class _Zero {
15206
17221
  return;
15207
17222
  }
15208
17223
  lc.info?.(`${kind}: ${message}}`);
15209
- const error = new ServerError(downMessage[1]);
17224
+ const error = new ProtocolError(downMessage[1]);
15210
17225
  lc.error?.(`${error.kind}:
15211
17226
 
15212
17227
  ${error.errorBody.message}`, error);
15213
- this.#rejectMessageError?.reject(error);
15214
17228
  lc.debug?.("Rejecting connect resolver due to error", error);
15215
17229
  this.#connectResolver.reject(error);
15216
- this.#disconnect(lc, { server: kind });
17230
+ this.#disconnect(lc, error);
15217
17231
  if (kind === error_kind_enum_exports.VersionNotSupported) {
15218
17232
  this.#onUpdateNeeded({ type: kind, message });
15219
17233
  } else if (kind === error_kind_enum_exports.SchemaVersionNotSupported) {
@@ -15311,7 +17325,7 @@ ${error.errorBody.message}`, error);
15311
17325
  }
15312
17326
  this.#initConnectionQueries = void 0;
15313
17327
  maybeSendDeletedClients();
15314
- this.#setConnectionState(Connected);
17328
+ this.#connectionManager.connected();
15315
17329
  this.#connectResolver.resolve();
15316
17330
  }
15317
17331
  /**
@@ -15319,8 +17333,9 @@ ${error.errorBody.message}`, error);
15319
17333
  * request to the server.
15320
17334
  *
15321
17335
  * {@link #connect} will throw an assertion error if the
15322
- * {@link #connectionState} is not {@link ConnectionState.Disconnected}.
15323
- * Callers MUST check the connection state before calling this method and log
17336
+ * {@link #connectionManager} status is not {@link ConnectionState.Disconnected}
17337
+ * or {@link ConnectionState.Connecting}.
17338
+ * Callers MUST check the connection status before calling this method and log
15324
17339
  * an error as needed.
15325
17340
  *
15326
17341
  * The function will resolve once the socket is connected. If you need to know
@@ -15332,11 +17347,13 @@ ${error.errorBody.message}`, error);
15332
17347
  */
15333
17348
  async #connect(lc, additionalConnectParams) {
15334
17349
  assert(this.#server);
15335
- assert(this.#connectionState === Disconnected);
17350
+ assert(
17351
+ this.#connectionManager.is(connection_status_enum_exports.Disconnected) || this.#connectionManager.is(connection_status_enum_exports.Connecting)
17352
+ );
15336
17353
  const wsid = nanoid();
15337
17354
  lc = addWebSocketIDToLogContext(wsid, lc);
15338
17355
  lc.info?.("Connecting...", { navigatorOnline: localNavigator?.onLine });
15339
- this.#setConnectionState(Connecting);
17356
+ this.#connectionManager.connecting();
15340
17357
  assert(this.#connectStart === void 0);
15341
17358
  const now = Date.now();
15342
17359
  this.#connectStart = now;
@@ -15356,10 +17373,12 @@ ${error.errorBody.message}`, error);
15356
17373
  }
15357
17374
  const timeoutID = setTimeout(() => {
15358
17375
  lc.debug?.("Rejecting connect resolver due to timeout");
15359
- this.#connectResolver.reject(new TimedOutError("Connect"));
15360
- this.#disconnect(lc, {
15361
- client: "ConnectTimeout"
17376
+ const timeoutError = new ClientError({
17377
+ kind: client_error_kind_enum_exports.ConnectTimeout,
17378
+ message: `Connection attempt timed out after ${CONNECT_TIMEOUT_MS / 1e3} seconds`
15362
17379
  });
17380
+ this.#connectResolver.reject(timeoutError);
17381
+ this.#disconnect(lc, timeoutError);
15363
17382
  }, CONNECT_TIMEOUT_MS);
15364
17383
  const abortHandler = () => {
15365
17384
  clearTimeout(timeoutID);
@@ -15411,22 +17430,29 @@ ${error.errorBody.message}`, error);
15411
17430
  }
15412
17431
  }
15413
17432
  #disconnect(lc, reason, closeCode) {
15414
- if (this.#connectionState === Connecting) {
17433
+ if (shouldReportConnectError(reason)) {
15415
17434
  this.#connectErrorCount++;
17435
+ this.#metrics.lastConnectError.set(getLastConnectErrorValue(reason));
17436
+ this.#metrics.timeToConnectMs.set(DID_NOT_CONNECT_VALUE);
17437
+ this.#metrics.setConnectError(reason);
17438
+ if (this.#connectErrorCount % CHECK_CONNECTIVITY_ON_ERROR_FREQUENCY === 1) {
17439
+ this.#checkConnectivity(`connectErrorCount=${this.#connectErrorCount}`);
17440
+ }
15416
17441
  }
15417
17442
  lc.info?.("disconnecting", {
15418
17443
  navigatorOnline: localNavigator?.onLine,
15419
- reason,
17444
+ reason: reason.kind,
15420
17445
  connectStart: this.#connectStart,
15421
17446
  totalToConnectStart: this.#totalToConnectStart,
15422
17447
  connectedAt: this.#connectedAt,
15423
17448
  connectionDuration: this.#connectedAt ? Date.now() - this.#connectedAt : 0,
15424
17449
  messageCount: this.#messageCount,
15425
- connectionState: this.#connectionState,
17450
+ connectionState: this.#connectionManager.state,
15426
17451
  connectErrorCount: this.#connectErrorCount
15427
17452
  });
15428
- switch (this.#connectionState) {
15429
- case Connected: {
17453
+ const connectionStatus = this.#connectionManager.state.name;
17454
+ switch (connectionStatus) {
17455
+ case connection_status_enum_exports.Connected: {
15430
17456
  if (this.#connectStart !== void 0) {
15431
17457
  lc.error?.(
15432
17458
  "disconnect() called while connected but connect start time is defined."
@@ -15434,30 +17460,19 @@ ${error.errorBody.message}`, error);
15434
17460
  }
15435
17461
  break;
15436
17462
  }
15437
- case Connecting: {
15438
- this.#metrics.lastConnectError.set(getLastConnectErrorValue(reason));
15439
- this.#metrics.timeToConnectMs.set(DID_NOT_CONNECT_VALUE);
15440
- this.#metrics.setConnectError(reason);
15441
- if (this.#connectErrorCount % CHECK_CONNECTIVITY_ON_ERROR_FREQUENCY === 1) {
15442
- this.#checkConnectivity(
15443
- `connectErrorCount=${this.#connectErrorCount}`
15444
- );
15445
- }
15446
- if (this.#connectStart === void 0) {
15447
- lc.error?.(
15448
- "disconnect() called while connecting but connect start time is undefined."
15449
- );
15450
- }
15451
- break;
15452
- }
15453
- case Disconnected:
15454
- lc.error?.("disconnect() called while disconnected");
17463
+ case connection_status_enum_exports.Closed:
17464
+ lc.debug?.("disconnect() called while closed");
17465
+ return;
17466
+ case connection_status_enum_exports.Disconnected:
17467
+ case connection_status_enum_exports.Connecting:
17468
+ case connection_status_enum_exports.Error:
15455
17469
  break;
17470
+ default:
17471
+ unreachable(connectionStatus);
15456
17472
  }
15457
- this.#socketResolver = resolver10();
17473
+ this.#socketResolver = resolver11();
15458
17474
  lc.debug?.("Creating new connect resolver");
15459
- this.#connectResolver = resolver10();
15460
- this.#setConnectionState(Disconnected);
17475
+ this.#connectResolver = resolver11();
15461
17476
  this.#messageCount = 0;
15462
17477
  this.#connectStart = void 0;
15463
17478
  this.#connectedAt = 0;
@@ -15468,6 +17483,23 @@ ${error.errorBody.message}`, error);
15468
17483
  this.#socket = void 0;
15469
17484
  this.#lastMutationIDSent = NULL_LAST_MUTATION_ID_SENT;
15470
17485
  this.#pokeHandler.handleDisconnect();
17486
+ const transition = getErrorConnectionTransition(reason);
17487
+ switch (transition.status) {
17488
+ case connection_status_enum_exports.Error:
17489
+ this.#connectionManager.error(transition.reason);
17490
+ break;
17491
+ case connection_status_enum_exports.Disconnected:
17492
+ this.#connectionManager.disconnected(transition.reason);
17493
+ break;
17494
+ case connection_status_enum_exports.Closed:
17495
+ this.#connectionManager.closed();
17496
+ break;
17497
+ case NO_STATUS_TRANSITION:
17498
+ this.#connectionManager.connecting(transition.reason);
17499
+ break;
17500
+ default:
17501
+ unreachable(transition);
17502
+ }
15471
17503
  }
15472
17504
  #handlePokeStart(_lc, pokeMessage) {
15473
17505
  this.#abortPingTimeout();
@@ -15490,12 +17522,16 @@ ${error.errorBody.message}`, error);
15490
17522
  const lc = this.#lc;
15491
17523
  lc.info?.(
15492
17524
  "poke error, disconnecting?",
15493
- this.#connectionState !== Disconnected
17525
+ !this.#connectionManager.is(connection_status_enum_exports.Disconnected)
15494
17526
  );
15495
- if (this.#connectionState !== Disconnected) {
15496
- this.#disconnect(lc, {
15497
- client: "UnexpectedBaseCookie"
15498
- });
17527
+ if (!this.#connectionManager.is(connection_status_enum_exports.Disconnected)) {
17528
+ this.#disconnect(
17529
+ lc,
17530
+ new ClientError({
17531
+ kind: client_error_kind_enum_exports.UnexpectedBaseCookie,
17532
+ message: "Server returned unexpected base cookie during sync"
17533
+ })
17534
+ );
15499
17535
  }
15500
17536
  }
15501
17537
  #handlePullResponse(lc, pullResponseMessage) {
@@ -15503,12 +17539,12 @@ ${error.errorBody.message}`, error);
15503
17539
  const body = pullResponseMessage[1];
15504
17540
  lc = lc.withContext("requestID", body.requestID);
15505
17541
  lc.debug?.("Handling pull response", body);
15506
- const resolver11 = this.#pendingPullsByRequestID.get(body.requestID);
15507
- if (!resolver11) {
17542
+ const resolver12 = this.#pendingPullsByRequestID.get(body.requestID);
17543
+ if (!resolver12) {
15508
17544
  lc.debug?.("No resolver found");
15509
17545
  return;
15510
17546
  }
15511
- resolver11.resolve(pullResponseMessage[1]);
17547
+ resolver12.resolve(pullResponseMessage[1]);
15512
17548
  }
15513
17549
  async #pusher(req, requestID) {
15514
17550
  assert(req.pushVersion === 1);
@@ -15581,6 +17617,12 @@ ${error.errorBody.message}`, error);
15581
17617
  this.#lc.info?.(`Starting Zero version: ${this.version}`);
15582
17618
  if (this.#server === null) {
15583
17619
  this.#lc.info?.("No socket origin provided, not starting connect loop.");
17620
+ this.#connectionManager.error(
17621
+ new ClientError({
17622
+ kind: client_error_kind_enum_exports.NoSocketOrigin,
17623
+ message: "No server socket origin provided"
17624
+ })
17625
+ );
15584
17626
  return;
15585
17627
  }
15586
17628
  let runLoopCounter = 0;
@@ -15595,21 +17637,29 @@ ${error.errorBody.message}`, error);
15595
17637
  await this.#updateAuthToken(bareLogContext);
15596
17638
  let needsReauth = false;
15597
17639
  let lastReauthAttemptAt;
15598
- let gotError = false;
15599
- let backoffMs = RUN_LOOP_INTERVAL_MS;
17640
+ let backoffMs;
15600
17641
  let additionalConnectParams;
15601
- while (!this.closed) {
17642
+ while (this.#connectionManager.shouldContinueRunLoop()) {
15602
17643
  runLoopCounter++;
15603
17644
  let lc = getLogContext();
15604
17645
  backoffMs = RUN_LOOP_INTERVAL_MS;
15605
17646
  try {
15606
- switch (this.#connectionState) {
15607
- case Disconnected: {
17647
+ const currentState = this.#connectionManager.state;
17648
+ switch (currentState.name) {
17649
+ case connection_status_enum_exports.Connecting:
17650
+ case connection_status_enum_exports.Disconnected: {
15608
17651
  if (this.#visibilityWatcher.visibilityState === "hidden") {
15609
17652
  this.#metrics.setDisconnectedWaitingForVisible();
15610
17653
  this.#totalToConnectStart = void 0;
15611
17654
  }
15612
- await this.#visibilityWatcher.waitForVisible();
17655
+ const visibilityResult = await promiseRace({
17656
+ visible: this.#visibilityWatcher.waitForVisible(),
17657
+ stateChange: this.#connectionManager.waitForStateChange()
17658
+ });
17659
+ if (visibilityResult.key === "stateChange") {
17660
+ throwIfConnectionError(visibilityResult.result);
17661
+ break;
17662
+ }
15613
17663
  if (needsReauth) {
15614
17664
  lastReauthAttemptAt = Date.now();
15615
17665
  await this.#updateAuthToken(lc, "invalid-token");
@@ -15619,65 +17669,67 @@ ${error.errorBody.message}`, error);
15619
17669
  }
15620
17670
  await this.#connect(lc, additionalConnectParams);
15621
17671
  additionalConnectParams = void 0;
15622
- if (this.closed) {
15623
- break;
15624
- }
17672
+ throwIfConnectionError(this.#connectionManager.state);
15625
17673
  assert(this.#socket);
15626
17674
  lc = getLogContext();
15627
17675
  lc.debug?.("Connected successfully");
15628
- gotError = false;
15629
17676
  needsReauth = false;
15630
17677
  this.#setOnline(true);
15631
17678
  break;
15632
17679
  }
15633
- case Connecting:
15634
- lc.error?.("unreachable");
15635
- gotError = true;
15636
- break;
15637
- case Connected: {
17680
+ case connection_status_enum_exports.Connected: {
15638
17681
  const controller = new AbortController();
15639
17682
  this.#abortPingTimeout = () => controller.abort();
15640
17683
  const [pingTimeoutPromise, pingTimeoutAborted] = sleepWithAbort(
15641
17684
  PING_INTERVAL_MS,
15642
17685
  controller.signal
15643
17686
  );
15644
- this.#rejectMessageError = resolver10();
15645
- const PING = 0;
15646
- const HIDDEN = 2;
15647
- const raceResult = await promiseRace([
15648
- pingTimeoutPromise,
15649
- pingTimeoutAborted,
15650
- this.#visibilityWatcher.waitForHidden(),
15651
- this.#connectionStateChangeResolver.promise,
15652
- this.#rejectMessageError.promise
15653
- ]);
15654
- if (this.closed) {
15655
- this.#rejectMessageError = void 0;
15656
- break;
15657
- }
15658
- switch (raceResult) {
15659
- case PING: {
15660
- const pingResult = await this.#ping(
15661
- lc,
15662
- this.#rejectMessageError.promise
15663
- );
15664
- if (pingResult === TimedOut) {
15665
- gotError = true;
15666
- }
17687
+ const raceResult = await promiseRace({
17688
+ waitForPing: pingTimeoutPromise,
17689
+ waitForPingAborted: pingTimeoutAborted,
17690
+ tabHidden: this.#visibilityWatcher.waitForHidden(),
17691
+ stateChange: this.#connectionManager.waitForStateChange()
17692
+ });
17693
+ switch (raceResult.key) {
17694
+ case "waitForPing": {
17695
+ await this.#ping(lc);
15667
17696
  break;
15668
17697
  }
15669
- case HIDDEN:
15670
- this.#disconnect(lc, {
15671
- client: "Hidden"
17698
+ case "waitForPingAborted":
17699
+ break;
17700
+ case "tabHidden": {
17701
+ const hiddenError = new ClientError({
17702
+ kind: client_error_kind_enum_exports.Hidden,
17703
+ message: "Connection closed because tab was hidden"
15672
17704
  });
17705
+ this.#disconnect(lc, hiddenError);
15673
17706
  this.#setOnline(false);
15674
17707
  break;
17708
+ }
17709
+ case "stateChange":
17710
+ throwIfConnectionError(raceResult.result);
17711
+ break;
17712
+ default:
17713
+ unreachable(raceResult);
15675
17714
  }
15676
- this.#rejectMessageError = void 0;
17715
+ break;
17716
+ }
17717
+ case connection_status_enum_exports.Error: {
17718
+ lc.info?.(
17719
+ `Run loop paused in error state. Call zero.connection.connect() to resume.`,
17720
+ currentState.reason
17721
+ );
17722
+ await this.#connectionManager.waitForStateChange();
17723
+ break;
15677
17724
  }
17725
+ case connection_status_enum_exports.Closed:
17726
+ break;
17727
+ default:
17728
+ unreachable(currentState);
15678
17729
  }
15679
17730
  } catch (ex) {
15680
- if (this.#connectionState !== Connected) {
17731
+ const isClientClosedError = isClientError(ex) && ex.kind === client_error_kind_enum_exports.ClientClosed;
17732
+ if (!this.#connectionManager.is(connection_status_enum_exports.Connected) && !isClientClosedError) {
15681
17733
  const level = isAuthError(ex) ? "warn" : "error";
15682
17734
  const kind = isServerError(ex) ? ex.kind : "Unknown Error";
15683
17735
  lc[level]?.("Failed to connect", ex, kind, {
@@ -15688,42 +17740,60 @@ ${error.errorBody.message}`, error);
15688
17740
  lc.debug?.(
15689
17741
  "Got an exception in the run loop",
15690
17742
  "state:",
15691
- this.#connectionState,
17743
+ this.#connectionManager.state,
15692
17744
  "exception:",
15693
17745
  ex
15694
17746
  );
15695
- if (isAuthError(ex)) {
15696
- const now = Date.now();
15697
- const msSinceLastReauthAttempt = lastReauthAttemptAt === void 0 ? Number.POSITIVE_INFINITY : now - lastReauthAttemptAt;
15698
- needsReauth = true;
15699
- if (msSinceLastReauthAttempt > RUN_LOOP_INTERVAL_MS) {
15700
- continue;
17747
+ const transition = getErrorConnectionTransition(ex);
17748
+ switch (transition.status) {
17749
+ case NO_STATUS_TRANSITION: {
17750
+ const backoffParams = getBackoffParams(transition.reason);
17751
+ if (backoffParams) {
17752
+ if (backoffParams.minBackoffMs !== void 0) {
17753
+ backoffMs = Math.max(backoffMs, backoffParams.minBackoffMs);
17754
+ }
17755
+ if (backoffParams.maxBackoffMs !== void 0) {
17756
+ backoffMs = Math.min(backoffMs, backoffParams.maxBackoffMs);
17757
+ }
17758
+ additionalConnectParams = backoffParams.reconnectParams;
17759
+ }
17760
+ if (isAuthError(transition.reason)) {
17761
+ const now = Date.now();
17762
+ const msSinceLastReauthAttempt = lastReauthAttemptAt === void 0 ? Number.POSITIVE_INFINITY : now - lastReauthAttemptAt;
17763
+ needsReauth = true;
17764
+ if (msSinceLastReauthAttempt > RUN_LOOP_INTERVAL_MS) {
17765
+ continue;
17766
+ }
17767
+ }
17768
+ this.#setOnline(false);
17769
+ lc.debug?.(
17770
+ "Sleeping",
17771
+ backoffMs,
17772
+ "ms before reconnecting due to error, state:",
17773
+ this.#connectionManager.state
17774
+ );
17775
+ await sleep(backoffMs);
17776
+ break;
15701
17777
  }
15702
- }
15703
- if (isServerError(ex) || ex instanceof TimedOutError || ex instanceof CloseError) {
15704
- gotError = true;
15705
- }
15706
- const backoffError = isBackoffError(ex);
15707
- if (backoffError) {
15708
- if (backoffError.minBackoffMs !== void 0) {
15709
- backoffMs = Math.max(backoffMs, backoffError.minBackoffMs);
17778
+ case connection_status_enum_exports.Error: {
17779
+ lc.debug?.("Fatal error encountered, transitioning to error state");
17780
+ this.#setOnline(false);
17781
+ this.#connectionManager.error(transition.reason);
17782
+ break;
17783
+ }
17784
+ case connection_status_enum_exports.Disconnected: {
17785
+ this.#setOnline(false);
17786
+ this.#connectionManager.disconnected(transition.reason);
17787
+ break;
15710
17788
  }
15711
- if (backoffError.maxBackoffMs !== void 0) {
15712
- backoffMs = Math.min(backoffMs, backoffError.maxBackoffMs);
17789
+ case connection_status_enum_exports.Closed: {
17790
+ this.#setOnline(false);
17791
+ break;
15713
17792
  }
15714
- additionalConnectParams = backoffError.reconnectParams;
17793
+ default:
17794
+ unreachable(transition);
15715
17795
  }
15716
17796
  }
15717
- if (gotError) {
15718
- this.#setOnline(false);
15719
- lc.debug?.(
15720
- "Sleeping",
15721
- backoffMs,
15722
- "ms before reconnecting due to error, state:",
15723
- this.#connectionState
15724
- );
15725
- await sleep(backoffMs);
15726
- }
15727
17797
  }
15728
17798
  }
15729
17799
  async #puller(req, requestID) {
@@ -15756,20 +17826,21 @@ ${error.errorBody.message}`, error);
15756
17826
  }
15757
17827
  ];
15758
17828
  send(socket, pullRequestMessage);
15759
- const pullResponseResolver = resolver10();
17829
+ const pullResponseResolver = resolver11();
15760
17830
  this.#pendingPullsByRequestID.set(requestID, pullResponseResolver);
15761
17831
  try {
15762
- const TIMEOUT = 0;
15763
- const RESPONSE = 1;
15764
- const raceResult = await promiseRace([
15765
- sleep(PULL_TIMEOUT_MS),
15766
- pullResponseResolver.promise
15767
- ]);
15768
- switch (raceResult) {
15769
- case TIMEOUT:
17832
+ const raceResult = await promiseRace({
17833
+ timeout: sleep(PULL_TIMEOUT_MS),
17834
+ success: pullResponseResolver.promise
17835
+ });
17836
+ switch (raceResult.key) {
17837
+ case "timeout":
15770
17838
  lc.debug?.("Mutation recovery pull timed out");
15771
- throw new Error("Pull timed out");
15772
- case RESPONSE: {
17839
+ throw new ClientError({
17840
+ kind: client_error_kind_enum_exports.PullTimeout,
17841
+ message: "Pull timed out"
17842
+ });
17843
+ case "success": {
15773
17844
  lc.debug?.("Returning mutation recovery pull response");
15774
17845
  const response = await pullResponseResolver.promise;
15775
17846
  return {
@@ -15785,10 +17856,15 @@ ${error.errorBody.message}`, error);
15785
17856
  };
15786
17857
  }
15787
17858
  default:
15788
- unreachable();
17859
+ unreachable(raceResult);
15789
17860
  }
15790
17861
  } finally {
15791
- pullResponseResolver.reject("timed out");
17862
+ pullResponseResolver.reject(
17863
+ new ClientError({
17864
+ kind: client_error_kind_enum_exports.PullTimeout,
17865
+ message: "Pull timed out"
17866
+ })
17867
+ );
15792
17868
  this.#pendingPullsByRequestID.delete(requestID);
15793
17869
  }
15794
17870
  }
@@ -15798,6 +17874,8 @@ ${error.errorBody.message}`, error);
15798
17874
  /**
15799
17875
  * A rough heuristic for whether the client is currently online and
15800
17876
  * authenticated.
17877
+ *
17878
+ * @deprecated Use `connection` instead, which provides more detailed connection state.
15801
17879
  */
15802
17880
  get online() {
15803
17881
  return this.#onlineManager.online;
@@ -15809,37 +17887,52 @@ ${error.errorBody.message}`, error);
15809
17887
  *
15810
17888
  * @param listener - The listener to subscribe to.
15811
17889
  * @returns A function to unsubscribe the listener.
17890
+ *
17891
+ * @deprecated Use `connection` instead, which provides more detailed connection state.
15812
17892
  */
15813
17893
  onOnline = (listener) => this.#onlineManager.subscribe(listener);
15814
17894
  /**
15815
17895
  * Starts a ping and waits for a pong.
15816
- *
15817
- * If it takes too long to get a pong we disconnect and this returns
15818
- * {@linkcode PingResult.TimedOut}.
15819
17896
  */
15820
- async #ping(lc, messageErrorRejectionPromise) {
17897
+ async #ping(lc) {
15821
17898
  lc.debug?.("pinging");
15822
- const { promise, resolve } = resolver10();
17899
+ const { promise, resolve } = resolver11();
15823
17900
  this.#onPong = resolve;
15824
17901
  const pingMessage = ["ping", {}];
15825
17902
  const t0 = performance.now();
15826
17903
  assert(this.#socket);
15827
17904
  send(this.#socket, pingMessage);
15828
- const connected = await promiseRace([
15829
- promise,
15830
- sleep(PING_TIMEOUT_MS),
15831
- messageErrorRejectionPromise
15832
- ]) === 0;
17905
+ const raceResult = await promiseRace({
17906
+ waitForPong: promise,
17907
+ pingTimeout: sleep(PING_TIMEOUT_MS),
17908
+ stateChange: this.#connectionManager.waitForStateChange()
17909
+ });
15833
17910
  const delta = performance.now() - t0;
15834
- if (!connected) {
15835
- lc.info?.("ping failed in", delta, "ms - disconnecting");
15836
- this.#disconnect(lc, {
15837
- client: "PingTimeout"
15838
- });
15839
- return TimedOut;
17911
+ switch (raceResult.key) {
17912
+ case "waitForPong": {
17913
+ lc.debug?.("ping succeeded in", delta, "ms");
17914
+ return;
17915
+ }
17916
+ case "pingTimeout": {
17917
+ lc.info?.("ping failed in", delta, "ms - disconnecting");
17918
+ const pingTimeoutError = new ClientError({
17919
+ kind: client_error_kind_enum_exports.PingTimeout,
17920
+ message: "Server ping request failed"
17921
+ });
17922
+ this.#disconnect(lc, pingTimeoutError);
17923
+ throw pingTimeoutError;
17924
+ }
17925
+ case "stateChange": {
17926
+ lc.debug?.(
17927
+ "ping aborted due to connection state change",
17928
+ raceResult.result
17929
+ );
17930
+ throwIfConnectionError(raceResult.result);
17931
+ break;
17932
+ }
17933
+ default:
17934
+ unreachable(raceResult);
15840
17935
  }
15841
- lc.debug?.("ping succeeded in", delta, "ms");
15842
- return Success;
15843
17936
  }
15844
17937
  // Sends a set of metrics to the server. Throws unless the server
15845
17938
  // returns 200.
@@ -15998,16 +18091,6 @@ function addWebSocketIDFromSocketToLogContext({ url }, lc) {
15998
18091
  function addWebSocketIDToLogContext(wsid, lc) {
15999
18092
  return lc.withContext("wsid", wsid);
16000
18093
  }
16001
- function promiseRace(ps) {
16002
- return Promise.race(ps.map((p, i) => p.then(() => i)));
16003
- }
16004
- var TimedOutError = class extends Error {
16005
- constructor(m) {
16006
- super(`${m} timed out`);
16007
- }
16008
- };
16009
- var CloseError = class extends Error {
16010
- };
16011
18094
  function assertValidRunOptions2(_options) {
16012
18095
  }
16013
18096
  async function makeActiveClientsManager(clientGroupID, clientID, signal, onDelete) {
@@ -16027,12 +18110,14 @@ export {
16027
18110
  dropDatabase,
16028
18111
  dropAllDatabases,
16029
18112
  TransactionClosedError,
18113
+ error_kind_enum_exports,
18114
+ error_origin_enum_exports,
18115
+ error_reason_enum_exports,
16030
18116
  transformRequestMessageSchema,
16031
18117
  transformResponseMessageSchema,
16032
- error_kind_enum_exports,
16033
18118
  table,
16034
- string2 as string,
16035
- number2 as number,
18119
+ string4 as string,
18120
+ number4 as number,
16036
18121
  boolean,
16037
18122
  json,
16038
18123
  enumeration,
@@ -16045,7 +18130,10 @@ export {
16045
18130
  newQuerySymbol,
16046
18131
  AbstractQuery,
16047
18132
  clientToServer,
18133
+ client_error_kind_enum_exports,
18134
+ connection_status_enum_exports,
18135
+ ClientError,
16048
18136
  update_needed_reason_type_enum_exports,
16049
18137
  Zero
16050
18138
  };
16051
- //# sourceMappingURL=chunk-JS2ABYXG.js.map
18139
+ //# sourceMappingURL=chunk-AIPM77UE.js.map