@rocicorp/zero 1.10.0-canary.22 → 1.10.0-canary.24

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 (302) hide show
  1. package/out/analyze-query/src/analyze-cli.js +2 -2
  2. package/out/analyze-query/src/analyze-cli.js.map +1 -1
  3. package/out/analyze-query/src/bin-analyze.js +9 -8
  4. package/out/analyze-query/src/bin-analyze.js.map +1 -1
  5. package/out/replicache/src/btree/node.d.ts.map +1 -1
  6. package/out/replicache/src/btree/node.js +3 -6
  7. package/out/replicache/src/btree/node.js.map +1 -1
  8. package/out/replicache/src/dag/gc.d.ts.map +1 -1
  9. package/out/replicache/src/dag/gc.js +6 -10
  10. package/out/replicache/src/dag/gc.js.map +1 -1
  11. package/out/replicache/src/kv/sqlite-store.d.ts.map +1 -1
  12. package/out/replicache/src/kv/sqlite-store.js +24 -2
  13. package/out/replicache/src/kv/sqlite-store.js.map +1 -1
  14. package/out/replicache/src/persist/persist.js +1 -1
  15. package/out/shared/src/arrays.d.ts +5 -0
  16. package/out/shared/src/arrays.d.ts.map +1 -1
  17. package/out/shared/src/arrays.js +8 -10
  18. package/out/shared/src/arrays.js.map +1 -1
  19. package/out/shared/src/map.d.ts +19 -2
  20. package/out/shared/src/map.d.ts.map +1 -1
  21. package/out/shared/src/map.js +18 -4
  22. package/out/shared/src/map.js.map +1 -1
  23. package/out/z2s/src/compiler.js +1 -1
  24. package/out/z2s/src/sql.d.ts.map +1 -1
  25. package/out/z2s/src/sql.js +2 -2
  26. package/out/z2s/src/sql.js.map +1 -1
  27. package/out/zero/package.js +1 -1
  28. package/out/zero/package.js.map +1 -1
  29. package/out/zero-cache/src/auth/write-authorizer.d.ts.map +1 -1
  30. package/out/zero-cache/src/auth/write-authorizer.js +1 -0
  31. package/out/zero-cache/src/auth/write-authorizer.js.map +1 -1
  32. package/out/zero-cache/src/config/zero-config.d.ts +23 -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 +63 -1
  35. package/out/zero-cache/src/config/zero-config.js.map +1 -1
  36. package/out/zero-cache/src/db/lite-tables.d.ts.map +1 -1
  37. package/out/zero-cache/src/db/lite-tables.js +3 -4
  38. package/out/zero-cache/src/db/lite-tables.js.map +1 -1
  39. package/out/zero-cache/src/observability/metrics.d.ts.map +1 -1
  40. package/out/zero-cache/src/observability/metrics.js +2 -7
  41. package/out/zero-cache/src/observability/metrics.js.map +1 -1
  42. package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
  43. package/out/zero-cache/src/server/change-streamer.js +11 -3
  44. package/out/zero-cache/src/server/change-streamer.js.map +1 -1
  45. package/out/zero-cache/src/server/inspector-delegate.d.ts +18 -2
  46. package/out/zero-cache/src/server/inspector-delegate.d.ts.map +1 -1
  47. package/out/zero-cache/src/server/inspector-delegate.js +22 -15
  48. package/out/zero-cache/src/server/inspector-delegate.js.map +1 -1
  49. package/out/zero-cache/src/server/main.d.ts.map +1 -1
  50. package/out/zero-cache/src/server/main.js +8 -1
  51. package/out/zero-cache/src/server/main.js.map +1 -1
  52. package/out/zero-cache/src/server/mutator.js +2 -0
  53. package/out/zero-cache/src/server/mutator.js.map +1 -1
  54. package/out/zero-cache/src/server/reaper.d.ts.map +1 -1
  55. package/out/zero-cache/src/server/reaper.js +2 -0
  56. package/out/zero-cache/src/server/reaper.js.map +1 -1
  57. package/out/zero-cache/src/server/replicator.d.ts.map +1 -1
  58. package/out/zero-cache/src/server/replicator.js +2 -0
  59. package/out/zero-cache/src/server/replicator.js.map +1 -1
  60. package/out/zero-cache/src/server/runner/zero-dispatcher.d.ts.map +1 -1
  61. package/out/zero-cache/src/server/runner/zero-dispatcher.js +3 -0
  62. package/out/zero-cache/src/server/runner/zero-dispatcher.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 +5 -1
  65. package/out/zero-cache/src/server/syncer.js.map +1 -1
  66. package/out/zero-cache/src/services/analyze.d.ts.map +1 -1
  67. package/out/zero-cache/src/services/analyze.js +11 -9
  68. package/out/zero-cache/src/services/analyze.js.map +1 -1
  69. package/out/zero-cache/src/services/change-source/common/backfill-manager.d.ts.map +1 -1
  70. package/out/zero-cache/src/services/change-source/common/backfill-manager.js +14 -1
  71. package/out/zero-cache/src/services/change-source/common/backfill-manager.js.map +1 -1
  72. package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.d.ts.map +1 -1
  73. package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.js +15 -3
  74. package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.js.map +1 -1
  75. package/out/zero-cache/src/services/change-source/custom/change-source.js +2 -2
  76. package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
  77. package/out/zero-cache/src/services/change-source/pg/initial-sync.js +1 -1
  78. package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts +3 -0
  79. package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts.map +1 -1
  80. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +6 -4
  81. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
  82. package/out/zero-cache/src/services/mutagen/mutagen.d.ts +5 -0
  83. package/out/zero-cache/src/services/mutagen/mutagen.d.ts.map +1 -1
  84. package/out/zero-cache/src/services/mutagen/mutagen.js +7 -1
  85. package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
  86. package/out/zero-cache/src/services/mutagen/pusher.d.ts +32 -32
  87. package/out/zero-cache/src/services/mutagen/pusher.d.ts.map +1 -1
  88. package/out/zero-cache/src/services/mutagen/pusher.js +5 -6
  89. package/out/zero-cache/src/services/mutagen/pusher.js.map +1 -1
  90. package/out/zero-cache/src/services/profz.d.ts +7 -0
  91. package/out/zero-cache/src/services/profz.d.ts.map +1 -0
  92. package/out/zero-cache/src/services/profz.js +103 -0
  93. package/out/zero-cache/src/services/profz.js.map +1 -0
  94. package/out/zero-cache/src/services/run-ast.d.ts +12 -0
  95. package/out/zero-cache/src/services/run-ast.d.ts.map +1 -1
  96. package/out/zero-cache/src/services/run-ast.js +26 -14
  97. package/out/zero-cache/src/services/run-ast.js.map +1 -1
  98. package/out/zero-cache/src/services/runner.d.ts.map +1 -1
  99. package/out/zero-cache/src/services/runner.js +1 -1
  100. package/out/zero-cache/src/services/runner.js.map +1 -1
  101. package/out/zero-cache/src/services/view-syncer/cvr.d.ts +45 -0
  102. package/out/zero-cache/src/services/view-syncer/cvr.d.ts.map +1 -1
  103. package/out/zero-cache/src/services/view-syncer/cvr.js +133 -1
  104. package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
  105. package/out/zero-cache/src/services/view-syncer/drain-coordinator.d.ts +14 -1
  106. package/out/zero-cache/src/services/view-syncer/drain-coordinator.d.ts.map +1 -1
  107. package/out/zero-cache/src/services/view-syncer/drain-coordinator.js +30 -4
  108. package/out/zero-cache/src/services/view-syncer/drain-coordinator.js.map +1 -1
  109. package/out/zero-cache/src/services/view-syncer/hydration-budget.d.ts +33 -0
  110. package/out/zero-cache/src/services/view-syncer/hydration-budget.d.ts.map +1 -0
  111. package/out/zero-cache/src/services/view-syncer/hydration-budget.js +62 -0
  112. package/out/zero-cache/src/services/view-syncer/hydration-budget.js.map +1 -0
  113. package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.d.ts +51 -0
  114. package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.d.ts.map +1 -0
  115. package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.js +78 -0
  116. package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.js.map +1 -0
  117. package/out/zero-cache/src/services/view-syncer/inspect-handler.d.ts +1 -1
  118. package/out/zero-cache/src/services/view-syncer/inspect-handler.d.ts.map +1 -1
  119. package/out/zero-cache/src/services/view-syncer/inspect-handler.js +2 -2
  120. package/out/zero-cache/src/services/view-syncer/inspect-handler.js.map +1 -1
  121. package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts +2 -1
  122. package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts.map +1 -1
  123. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +42 -17
  124. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
  125. package/out/zero-cache/src/services/view-syncer/query-covering.d.ts.map +1 -1
  126. package/out/zero-cache/src/services/view-syncer/query-covering.js +2 -6
  127. package/out/zero-cache/src/services/view-syncer/query-covering.js.map +1 -1
  128. package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.d.ts +95 -0
  129. package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.d.ts.map +1 -0
  130. package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.js +155 -0
  131. package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.js.map +1 -0
  132. package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts +42 -4
  133. package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts.map +1 -1
  134. package/out/zero-cache/src/services/view-syncer/snapshotter.js +117 -28
  135. package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
  136. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts +11 -1
  137. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
  138. package/out/zero-cache/src/services/view-syncer/view-syncer.js +466 -95
  139. package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
  140. package/out/zero-cache/src/types/processes.d.ts +32 -0
  141. package/out/zero-cache/src/types/processes.d.ts.map +1 -1
  142. package/out/zero-cache/src/types/processes.js +43 -4
  143. package/out/zero-cache/src/types/processes.js.map +1 -1
  144. package/out/zero-cache/src/types/profiler.d.ts +12 -0
  145. package/out/zero-cache/src/types/profiler.d.ts.map +1 -1
  146. package/out/zero-cache/src/types/profiler.js +46 -1
  147. package/out/zero-cache/src/types/profiler.js.map +1 -1
  148. package/out/zero-cache/src/types/row-key.d.ts.map +1 -1
  149. package/out/zero-cache/src/types/row-key.js +5 -5
  150. package/out/zero-cache/src/types/row-key.js.map +1 -1
  151. package/out/zero-cache/src/workers/connection.js +3 -3
  152. package/out/zero-cache/src/workers/connection.js.map +1 -1
  153. package/out/zero-cache/src/workers/syncer-ws-message-handler.d.ts.map +1 -1
  154. package/out/zero-cache/src/workers/syncer-ws-message-handler.js +21 -16
  155. package/out/zero-cache/src/workers/syncer-ws-message-handler.js.map +1 -1
  156. package/out/zero-cache/src/workers/syncer.d.ts.map +1 -1
  157. package/out/zero-cache/src/workers/syncer.js +10 -1
  158. package/out/zero-cache/src/workers/syncer.js.map +1 -1
  159. package/out/zero-client/src/client/context.d.ts +16 -3
  160. package/out/zero-client/src/client/context.d.ts.map +1 -1
  161. package/out/zero-client/src/client/context.js +85 -14
  162. package/out/zero-client/src/client/context.js.map +1 -1
  163. package/out/zero-client/src/client/ivm-branch.d.ts.map +1 -1
  164. package/out/zero-client/src/client/ivm-branch.js +20 -3
  165. package/out/zero-client/src/client/ivm-branch.js.map +1 -1
  166. package/out/zero-client/src/client/query-manager.d.ts.map +1 -1
  167. package/out/zero-client/src/client/query-manager.js +2 -5
  168. package/out/zero-client/src/client/query-manager.js.map +1 -1
  169. package/out/zero-client/src/client/version.js +1 -1
  170. package/out/zero-client/src/client/zero-rep.d.ts.map +1 -1
  171. package/out/zero-client/src/client/zero-rep.js +1 -1
  172. package/out/zero-client/src/client/zero-rep.js.map +1 -1
  173. package/out/zero-client/src/client/zero.d.ts.map +1 -1
  174. package/out/zero-client/src/client/zero.js +2 -2
  175. package/out/zero-client/src/client/zero.js.map +1 -1
  176. package/out/zero-protocol/src/analyze-query-result.d.ts +5 -5
  177. package/out/zero-protocol/src/analyze-query-result.d.ts.map +1 -1
  178. package/out/zero-protocol/src/analyze-query-result.js +1 -1
  179. package/out/zero-protocol/src/analyze-query-result.js.map +1 -1
  180. package/out/zero-protocol/src/ast.d.ts.map +1 -1
  181. package/out/zero-protocol/src/ast.js +13 -14
  182. package/out/zero-protocol/src/ast.js.map +1 -1
  183. package/out/zero-protocol/src/down.d.ts +2 -2
  184. package/out/zero-protocol/src/error.d.ts +1 -1
  185. package/out/zero-protocol/src/inspect-down.d.ts +6 -6
  186. package/out/zero-protocol/src/protocol-version.d.ts.map +1 -1
  187. package/out/zero-protocol/src/protocol-version.js.map +1 -1
  188. package/out/zero-protocol/src/query-hash.d.ts.map +1 -1
  189. package/out/zero-protocol/src/query-hash.js +2 -5
  190. package/out/zero-protocol/src/query-hash.js.map +1 -1
  191. package/out/zero-solid/src/solid-view.d.ts +7 -0
  192. package/out/zero-solid/src/solid-view.d.ts.map +1 -1
  193. package/out/zero-solid/src/solid-view.js +13 -2
  194. package/out/zero-solid/src/solid-view.js.map +1 -1
  195. package/out/zql/src/builder/builder.d.ts +19 -0
  196. package/out/zql/src/builder/builder.d.ts.map +1 -1
  197. package/out/zql/src/builder/builder.js +28 -11
  198. package/out/zql/src/builder/builder.js.map +1 -1
  199. package/out/zql/src/builder/correlated-predicate-pushdown.d.ts +43 -0
  200. package/out/zql/src/builder/correlated-predicate-pushdown.d.ts.map +1 -0
  201. package/out/zql/src/builder/correlated-predicate-pushdown.js +161 -0
  202. package/out/zql/src/builder/correlated-predicate-pushdown.js.map +1 -0
  203. package/out/zql/src/builder/debug-delegate.d.ts +6 -1
  204. package/out/zql/src/builder/debug-delegate.d.ts.map +1 -1
  205. package/out/zql/src/builder/debug-delegate.js +14 -2
  206. package/out/zql/src/builder/debug-delegate.js.map +1 -1
  207. package/out/zql/src/ivm/array-view.d.ts +19 -0
  208. package/out/zql/src/ivm/array-view.d.ts.map +1 -1
  209. package/out/zql/src/ivm/array-view.js +39 -2
  210. package/out/zql/src/ivm/array-view.js.map +1 -1
  211. package/out/zql/src/ivm/connection-index.d.ts +68 -0
  212. package/out/zql/src/ivm/connection-index.d.ts.map +1 -0
  213. package/out/zql/src/ivm/connection-index.js +149 -0
  214. package/out/zql/src/ivm/connection-index.js.map +1 -0
  215. package/out/zql/src/ivm/exists.js +1 -1
  216. package/out/zql/src/ivm/filter-operators.d.ts +3 -2
  217. package/out/zql/src/ivm/filter-operators.d.ts.map +1 -1
  218. package/out/zql/src/ivm/filter-operators.js +19 -4
  219. package/out/zql/src/ivm/filter-operators.js.map +1 -1
  220. package/out/zql/src/ivm/memory-source.d.ts +13 -1
  221. package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
  222. package/out/zql/src/ivm/memory-source.js +43 -4
  223. package/out/zql/src/ivm/memory-source.js.map +1 -1
  224. package/out/zql/src/ivm/operator.d.ts +40 -0
  225. package/out/zql/src/ivm/operator.d.ts.map +1 -1
  226. package/out/zql/src/ivm/operator.js.map +1 -1
  227. package/out/zql/src/ivm/view.d.ts +15 -0
  228. package/out/zql/src/ivm/view.d.ts.map +1 -1
  229. package/out/zql/src/planner/planner-builder.d.ts +12 -3
  230. package/out/zql/src/planner/planner-builder.d.ts.map +1 -1
  231. package/out/zql/src/planner/planner-builder.js +40 -20
  232. package/out/zql/src/planner/planner-builder.js.map +1 -1
  233. package/out/zql/src/planner/planner-connection.d.ts +28 -2
  234. package/out/zql/src/planner/planner-connection.d.ts.map +1 -1
  235. package/out/zql/src/planner/planner-connection.js +108 -8
  236. package/out/zql/src/planner/planner-connection.js.map +1 -1
  237. package/out/zql/src/planner/planner-debug.d.ts +1 -1
  238. package/out/zql/src/planner/planner-debug.d.ts.map +1 -1
  239. package/out/zql/src/planner/planner-debug.js +3 -8
  240. package/out/zql/src/planner/planner-debug.js.map +1 -1
  241. package/out/zql/src/planner/planner-fan-out.d.ts +1 -0
  242. package/out/zql/src/planner/planner-fan-out.d.ts.map +1 -1
  243. package/out/zql/src/planner/planner-fan-out.js +3 -0
  244. package/out/zql/src/planner/planner-fan-out.js.map +1 -1
  245. package/out/zql/src/planner/planner-filter.d.ts +59 -0
  246. package/out/zql/src/planner/planner-filter.d.ts.map +1 -0
  247. package/out/zql/src/planner/planner-filter.js +116 -0
  248. package/out/zql/src/planner/planner-filter.js.map +1 -0
  249. package/out/zql/src/planner/planner-graph.d.ts +2 -0
  250. package/out/zql/src/planner/planner-graph.d.ts.map +1 -1
  251. package/out/zql/src/planner/planner-graph.js +2 -0
  252. package/out/zql/src/planner/planner-graph.js.map +1 -1
  253. package/out/zql/src/planner/planner-join.d.ts +2 -0
  254. package/out/zql/src/planner/planner-join.d.ts.map +1 -1
  255. package/out/zql/src/planner/planner-join.js +7 -0
  256. package/out/zql/src/planner/planner-join.js.map +1 -1
  257. package/out/zql/src/planner/planner-node.d.ts +2 -1
  258. package/out/zql/src/planner/planner-node.d.ts.map +1 -1
  259. package/out/zql/src/planner/planner-node.js.map +1 -1
  260. package/out/zql/src/planner/planner-source.d.ts +2 -2
  261. package/out/zql/src/planner/planner-source.d.ts.map +1 -1
  262. package/out/zql/src/planner/planner-source.js +2 -2
  263. package/out/zql/src/planner/planner-source.js.map +1 -1
  264. package/out/zql/src/query/query-delegate-base.d.ts +2 -2
  265. package/out/zql/src/query/query-delegate-base.d.ts.map +1 -1
  266. package/out/zql/src/query/query-delegate-base.js +16 -10
  267. package/out/zql/src/query/query-delegate-base.js.map +1 -1
  268. package/out/zql/src/query/query-delegate.d.ts +22 -5
  269. package/out/zql/src/query/query-delegate.d.ts.map +1 -1
  270. package/out/zql/src/query/query-impl.d.ts.map +1 -1
  271. package/out/zql/src/query/query-impl.js +6 -23
  272. package/out/zql/src/query/query-impl.js.map +1 -1
  273. package/out/zql/src/query/query-transitions.d.ts.map +1 -1
  274. package/out/zql/src/query/query-transitions.js +2 -5
  275. package/out/zql/src/query/query-transitions.js.map +1 -1
  276. package/out/zql/src/query/query.d.ts +4 -1
  277. package/out/zql/src/query/query.d.ts.map +1 -1
  278. package/out/zql/src/query/runnable-query-impl.d.ts.map +1 -1
  279. package/out/zql/src/query/runnable-query-impl.js +2 -10
  280. package/out/zql/src/query/runnable-query-impl.js.map +1 -1
  281. package/out/zqlite/src/db.d.ts +9 -1
  282. package/out/zqlite/src/db.d.ts.map +1 -1
  283. package/out/zqlite/src/db.js +8 -2
  284. package/out/zqlite/src/db.js.map +1 -1
  285. package/out/zqlite/src/query-builder.d.ts +1 -1
  286. package/out/zqlite/src/query-builder.d.ts.map +1 -1
  287. package/out/zqlite/src/query-builder.js +2 -1
  288. package/out/zqlite/src/query-builder.js.map +1 -1
  289. package/out/zqlite/src/query-delegate.d.ts.map +1 -1
  290. package/out/zqlite/src/query-delegate.js +5 -6
  291. package/out/zqlite/src/query-delegate.js.map +1 -1
  292. package/out/zqlite/src/sqlite-cost-model.d.ts.map +1 -1
  293. package/out/zqlite/src/sqlite-cost-model.js +2 -30
  294. package/out/zqlite/src/sqlite-cost-model.js.map +1 -1
  295. package/out/zqlite/src/sqlite-stat-fanout.d.ts.map +1 -1
  296. package/out/zqlite/src/sqlite-stat-fanout.js +3 -5
  297. package/out/zqlite/src/sqlite-stat-fanout.js.map +1 -1
  298. package/out/zqlite/src/table-source.d.ts +15 -2
  299. package/out/zqlite/src/table-source.d.ts.map +1 -1
  300. package/out/zqlite/src/table-source.js +28 -12
  301. package/out/zqlite/src/table-source.js.map +1 -1
  302. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"debug-delegate.d.ts","sourceRoot":"","sources":["../../../../../zql/src/builder/debug-delegate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,iBAAiB,EAEjB,YAAY,EACb,MAAM,oDAAoD,CAAC;AAC5D,OAAO,EAAC,KAAK,GAAG,EAAC,MAAM,oCAAoC,CAAC;AAE5D,eAAO,MAAM,iBAAiB;;;CAG7B,CAAC;AAEF,KAAK,UAAU,GAAG,MAAM,CAAC;AACzB,KAAK,GAAG,GAAG,MAAM,CAAC;AAElB,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC;AAEhD,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,GAAG,IAAI,CAAC;IAC/C,SAAS,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,GAAG,IAAI,CAAC;IACzD,kBAAkB,IAAI,iBAAiB,CAAC;IACxC,aAAa,IAAI,YAAY,CAAC;IAC9B,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAClE,eAAe,IAAI,iBAAiB,CAAC;IACrC,aAAa,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACnE,cAAc,IAAI,WAAW,CAAC;IAE9B,KAAK,IAAI,IAAI,CAAC;CACf;AAED,qBAAa,KAAM,YAAW,aAAa;;;IAazC,kBAAkB,IAAI,iBAAiB;IAIvC,aAAa,IAAI,YAAY;IAI7B,eAAe,IAAI,iBAAiB;IAIpC,cAAc,IAAI,WAAW;IAI7B,SAAS,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,GAAG,IAAI;IAS9C,KAAK,IAAI,IAAI;IAOb,SAAS,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,GAAG,IAAI;IAUxD,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAYjE,aAAa,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI;CAmBpE"}
1
+ {"version":3,"file":"debug-delegate.d.ts","sourceRoot":"","sources":["../../../../../zql/src/builder/debug-delegate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,iBAAiB,EAEjB,YAAY,EACb,MAAM,oDAAoD,CAAC;AAC5D,OAAO,EAAC,KAAK,GAAG,EAAC,MAAM,oCAAoC,CAAC;AAE5D,eAAO,MAAM,iBAAiB;;;CAG7B,CAAC;AAEF,KAAK,UAAU,GAAG,MAAM,CAAC;AACzB,KAAK,GAAG,GAAG,MAAM,CAAC;AAElB,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC;AAEhD,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,GAAG,IAAI,CAAC;IAC/C,SAAS,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,GAAG,IAAI,CAAC;IACzD,kBAAkB,IAAI,iBAAiB,CAAC;IACxC,aAAa,IAAI,YAAY,CAAC;IAC9B,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAClE,eAAe,IAAI,iBAAiB,CAAC;IACrC,aAAa,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACnE,cAAc,IAAI,WAAW,CAAC;IAE9B,KAAK,IAAI,IAAI,CAAC;CACf;AAED,qBAAa,KAAM,YAAW,aAAa;;IAQzC;;;;OAIG;gBACS,WAAW,EAAE,OAAO,EAAE,eAAe,SAAW;IAS5D,kBAAkB,IAAI,iBAAiB;IAIvC,aAAa,IAAI,YAAY;IAI7B,eAAe,IAAI,iBAAiB;IAIpC,cAAc,IAAI,WAAW;IAI7B,SAAS,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,GAAG,IAAI;IAS9C,KAAK,IAAI,IAAI;IAOb,SAAS,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,GAAG,IAAI;IAaxD,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAYjE,aAAa,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI;CAmBpE"}
@@ -9,7 +9,16 @@ var Debug = class {
9
9
  #rowsBySource;
10
10
  #nvisitBySource;
11
11
  #plans;
12
- constructor() {
12
+ #collectRows;
13
+ #maxRowsPerQuery;
14
+ /**
15
+ * @param collectRows Whether to keep the vended rows.
16
+ * @param maxRowsPerQuery Maximum number of rows kept per table/query. Rows
17
+ * past this are still counted but not kept.
18
+ */
19
+ constructor(collectRows, maxRowsPerQuery = Infinity) {
20
+ this.#collectRows = collectRows;
21
+ this.#maxRowsPerQuery = maxRowsPerQuery;
13
22
  this.#rowCountsBySource = {};
14
23
  this.#rowsBySource = {};
15
24
  this.#nvisitBySource = {};
@@ -42,7 +51,10 @@ var Debug = class {
42
51
  rowVended(table, query, row) {
43
52
  const { counts, rows } = this.#getRowStats(table);
44
53
  if (counts) counts[query] = (counts[query] ?? 0) + 1;
45
- if (rows) (rows[query] ??= []).push(row);
54
+ if (this.#collectRows) {
55
+ const queryRows = rows[query] ??= [];
56
+ if (queryRows.length < this.#maxRowsPerQuery) queryRows.push(row);
57
+ }
46
58
  }
47
59
  recordNVisit(table, query, nvisit) {
48
60
  let nvisitCounts = this.#nvisitBySource[table];
@@ -1 +1 @@
1
- {"version":3,"file":"debug-delegate.js","names":["#rowCountsBySource","#rowsBySource","#nvisitBySource","#plans","#getRowStats"],"sources":["../../../../../zql/src/builder/debug-delegate.ts"],"sourcesContent":["import type {\n RowCountsByQuery,\n RowCountsBySource,\n RowsByQuery,\n RowsBySource,\n} from '../../../zero-protocol/src/analyze-query-result.ts';\nimport {type Row} from '../../../zero-protocol/src/data.ts';\n\nexport const runtimeDebugFlags = {\n trackRowCountsVended: false,\n trackRowsVended: false,\n};\n\ntype SourceName = string;\ntype SQL = string;\n\nexport type SQLitePlans = Record<SQL, string[]>;\n\nexport interface DebugDelegate {\n initQuery(table: SourceName, query: SQL): void;\n rowVended(table: SourceName, query: SQL, row: Row): void;\n getVendedRowCounts(): RowCountsBySource;\n getVendedRows(): RowsBySource;\n recordNVisit(table: SourceName, query: SQL, nvisit: number): void;\n getNVisitCounts(): RowCountsBySource;\n recordExplain(table: SourceName, query: SQL, plan: string[]): void;\n getSQLitePlans(): SQLitePlans;\n // clears all internal state\n reset(): void;\n}\n\nexport class Debug implements DebugDelegate {\n #rowCountsBySource: RowCountsBySource;\n #rowsBySource: RowsBySource;\n #nvisitBySource: RowCountsBySource;\n #plans: SQLitePlans;\n\n constructor() {\n this.#rowCountsBySource = {};\n this.#rowsBySource = {};\n this.#nvisitBySource = {};\n this.#plans = {};\n }\n\n getVendedRowCounts(): RowCountsBySource {\n return this.#rowCountsBySource;\n }\n\n getVendedRows(): RowsBySource {\n return this.#rowsBySource;\n }\n\n getNVisitCounts(): RowCountsBySource {\n return this.#nvisitBySource;\n }\n\n getSQLitePlans(): SQLitePlans {\n return this.#plans;\n }\n\n initQuery(table: SourceName, query: SQL): void {\n const {counts} = this.#getRowStats(table);\n if (counts) {\n if (!counts[query]) {\n counts[query] = 0;\n }\n }\n }\n\n reset(): void {\n this.#rowCountsBySource = {};\n this.#rowsBySource = {};\n this.#nvisitBySource = {};\n this.#plans = {};\n }\n\n rowVended(table: SourceName, query: SQL, row: Row): void {\n const {counts, rows} = this.#getRowStats(table);\n if (counts) {\n counts[query] = (counts[query] ?? 0) + 1;\n }\n if (rows) {\n (rows[query] ??= []).push(row);\n }\n }\n\n recordNVisit(table: SourceName, query: SQL, nvisit: number): void {\n let nvisitCounts = this.#nvisitBySource[table];\n if (!nvisitCounts) {\n nvisitCounts = {};\n this.#nvisitBySource[table] = nvisitCounts;\n }\n if (!nvisitCounts[query]) {\n nvisitCounts[query] = 0;\n }\n nvisitCounts[query] += nvisit;\n }\n\n recordExplain(_table: SourceName, query: SQL, plan: string[]): void {\n this.#plans[query] = plan;\n }\n\n #getRowStats(source: SourceName) {\n let counts: RowCountsByQuery | undefined;\n let rows: RowsByQuery | undefined;\n counts = this.#rowCountsBySource[source];\n if (!counts) {\n counts = {};\n this.#rowCountsBySource[source] = counts;\n }\n rows = this.#rowsBySource[source];\n if (!rows) {\n rows = {};\n this.#rowsBySource[source] = rows;\n }\n return {counts, rows};\n }\n}\n"],"mappings":";;AAQA,IAAa,oBAAoB;CAC/B,sBAAsB;CACtB,iBAAiB;AACnB;AAoBA,IAAa,QAAb,MAA4C;CAC1C;CACA;CACA;CACA;CAEA,cAAc;EACZ,KAAKA,qBAAqB,CAAC;EAC3B,KAAKC,gBAAgB,CAAC;EACtB,KAAKC,kBAAkB,CAAC;EACxB,KAAKC,SAAS,CAAC;CACjB;CAEA,qBAAwC;EACtC,OAAO,KAAKH;CACd;CAEA,gBAA8B;EAC5B,OAAO,KAAKC;CACd;CAEA,kBAAqC;EACnC,OAAO,KAAKC;CACd;CAEA,iBAA8B;EAC5B,OAAO,KAAKC;CACd;CAEA,UAAU,OAAmB,OAAkB;EAC7C,MAAM,EAAC,WAAU,KAAKC,aAAa,KAAK;EACxC,IAAI;OACE,CAAC,OAAO,QACV,OAAO,SAAS;EAAA;CAGtB;CAEA,QAAc;EACZ,KAAKJ,qBAAqB,CAAC;EAC3B,KAAKC,gBAAgB,CAAC;EACtB,KAAKC,kBAAkB,CAAC;EACxB,KAAKC,SAAS,CAAC;CACjB;CAEA,UAAU,OAAmB,OAAY,KAAgB;EACvD,MAAM,EAAC,QAAQ,SAAQ,KAAKC,aAAa,KAAK;EAC9C,IAAI,QACF,OAAO,UAAU,OAAO,UAAU,KAAK;EAEzC,IAAI,MACF,CAAC,KAAK,WAAW,CAAC,GAAG,KAAK,GAAG;CAEjC;CAEA,aAAa,OAAmB,OAAY,QAAsB;EAChE,IAAI,eAAe,KAAKF,gBAAgB;EACxC,IAAI,CAAC,cAAc;GACjB,eAAe,CAAC;GAChB,KAAKA,gBAAgB,SAAS;EAChC;EACA,IAAI,CAAC,aAAa,QAChB,aAAa,SAAS;EAExB,aAAa,UAAU;CACzB;CAEA,cAAc,QAAoB,OAAY,MAAsB;EAClE,KAAKC,OAAO,SAAS;CACvB;CAEA,aAAa,QAAoB;EAC/B,IAAI;EACJ,IAAI;EACJ,SAAS,KAAKH,mBAAmB;EACjC,IAAI,CAAC,QAAQ;GACX,SAAS,CAAC;GACV,KAAKA,mBAAmB,UAAU;EACpC;EACA,OAAO,KAAKC,cAAc;EAC1B,IAAI,CAAC,MAAM;GACT,OAAO,CAAC;GACR,KAAKA,cAAc,UAAU;EAC/B;EACA,OAAO;GAAC;GAAQ;EAAI;CACtB;AACF"}
1
+ {"version":3,"file":"debug-delegate.js","names":["#collectRows","#maxRowsPerQuery","#rowCountsBySource","#rowsBySource","#nvisitBySource","#plans","#getRowStats"],"sources":["../../../../../zql/src/builder/debug-delegate.ts"],"sourcesContent":["import type {\n RowCountsByQuery,\n RowCountsBySource,\n RowsByQuery,\n RowsBySource,\n} from '../../../zero-protocol/src/analyze-query-result.ts';\nimport {type Row} from '../../../zero-protocol/src/data.ts';\n\nexport const runtimeDebugFlags = {\n trackRowCountsVended: false,\n trackRowsVended: false,\n};\n\ntype SourceName = string;\ntype SQL = string;\n\nexport type SQLitePlans = Record<SQL, string[]>;\n\nexport interface DebugDelegate {\n initQuery(table: SourceName, query: SQL): void;\n rowVended(table: SourceName, query: SQL, row: Row): void;\n getVendedRowCounts(): RowCountsBySource;\n getVendedRows(): RowsBySource;\n recordNVisit(table: SourceName, query: SQL, nvisit: number): void;\n getNVisitCounts(): RowCountsBySource;\n recordExplain(table: SourceName, query: SQL, plan: string[]): void;\n getSQLitePlans(): SQLitePlans;\n // clears all internal state\n reset(): void;\n}\n\nexport class Debug implements DebugDelegate {\n #rowCountsBySource: RowCountsBySource;\n #rowsBySource: RowsBySource;\n #nvisitBySource: RowCountsBySource;\n #plans: SQLitePlans;\n readonly #collectRows: boolean;\n readonly #maxRowsPerQuery: number;\n\n /**\n * @param collectRows Whether to keep the vended rows.\n * @param maxRowsPerQuery Maximum number of rows kept per table/query. Rows\n * past this are still counted but not kept.\n */\n constructor(collectRows: boolean, maxRowsPerQuery = Infinity) {\n this.#collectRows = collectRows;\n this.#maxRowsPerQuery = maxRowsPerQuery;\n this.#rowCountsBySource = {};\n this.#rowsBySource = {};\n this.#nvisitBySource = {};\n this.#plans = {};\n }\n\n getVendedRowCounts(): RowCountsBySource {\n return this.#rowCountsBySource;\n }\n\n getVendedRows(): RowsBySource {\n return this.#rowsBySource;\n }\n\n getNVisitCounts(): RowCountsBySource {\n return this.#nvisitBySource;\n }\n\n getSQLitePlans(): SQLitePlans {\n return this.#plans;\n }\n\n initQuery(table: SourceName, query: SQL): void {\n const {counts} = this.#getRowStats(table);\n if (counts) {\n if (!counts[query]) {\n counts[query] = 0;\n }\n }\n }\n\n reset(): void {\n this.#rowCountsBySource = {};\n this.#rowsBySource = {};\n this.#nvisitBySource = {};\n this.#plans = {};\n }\n\n rowVended(table: SourceName, query: SQL, row: Row): void {\n const {counts, rows} = this.#getRowStats(table);\n if (counts) {\n counts[query] = (counts[query] ?? 0) + 1;\n }\n if (this.#collectRows) {\n const queryRows = (rows[query] ??= []);\n if (queryRows.length < this.#maxRowsPerQuery) {\n queryRows.push(row);\n }\n }\n }\n\n recordNVisit(table: SourceName, query: SQL, nvisit: number): void {\n let nvisitCounts = this.#nvisitBySource[table];\n if (!nvisitCounts) {\n nvisitCounts = {};\n this.#nvisitBySource[table] = nvisitCounts;\n }\n if (!nvisitCounts[query]) {\n nvisitCounts[query] = 0;\n }\n nvisitCounts[query] += nvisit;\n }\n\n recordExplain(_table: SourceName, query: SQL, plan: string[]): void {\n this.#plans[query] = plan;\n }\n\n #getRowStats(source: SourceName) {\n let counts: RowCountsByQuery | undefined;\n let rows: RowsByQuery | undefined;\n counts = this.#rowCountsBySource[source];\n if (!counts) {\n counts = {};\n this.#rowCountsBySource[source] = counts;\n }\n rows = this.#rowsBySource[source];\n if (!rows) {\n rows = {};\n this.#rowsBySource[source] = rows;\n }\n return {counts, rows};\n }\n}\n"],"mappings":";;AAQA,IAAa,oBAAoB;CAC/B,sBAAsB;CACtB,iBAAiB;AACnB;AAoBA,IAAa,QAAb,MAA4C;CAC1C;CACA;CACA;CACA;CACA;CACA;;;;;;CAOA,YAAY,aAAsB,kBAAkB,UAAU;EAC5D,KAAKA,eAAe;EACpB,KAAKC,mBAAmB;EACxB,KAAKC,qBAAqB,CAAC;EAC3B,KAAKC,gBAAgB,CAAC;EACtB,KAAKC,kBAAkB,CAAC;EACxB,KAAKC,SAAS,CAAC;CACjB;CAEA,qBAAwC;EACtC,OAAO,KAAKH;CACd;CAEA,gBAA8B;EAC5B,OAAO,KAAKC;CACd;CAEA,kBAAqC;EACnC,OAAO,KAAKC;CACd;CAEA,iBAA8B;EAC5B,OAAO,KAAKC;CACd;CAEA,UAAU,OAAmB,OAAkB;EAC7C,MAAM,EAAC,WAAU,KAAKC,aAAa,KAAK;EACxC,IAAI;OACE,CAAC,OAAO,QACV,OAAO,SAAS;EAAA;CAGtB;CAEA,QAAc;EACZ,KAAKJ,qBAAqB,CAAC;EAC3B,KAAKC,gBAAgB,CAAC;EACtB,KAAKC,kBAAkB,CAAC;EACxB,KAAKC,SAAS,CAAC;CACjB;CAEA,UAAU,OAAmB,OAAY,KAAgB;EACvD,MAAM,EAAC,QAAQ,SAAQ,KAAKC,aAAa,KAAK;EAC9C,IAAI,QACF,OAAO,UAAU,OAAO,UAAU,KAAK;EAEzC,IAAI,KAAKN,cAAc;GACrB,MAAM,YAAa,KAAK,WAAW,CAAC;GACpC,IAAI,UAAU,SAAS,KAAKC,kBAC1B,UAAU,KAAK,GAAG;EAEtB;CACF;CAEA,aAAa,OAAmB,OAAY,QAAsB;EAChE,IAAI,eAAe,KAAKG,gBAAgB;EACxC,IAAI,CAAC,cAAc;GACjB,eAAe,CAAC;GAChB,KAAKA,gBAAgB,SAAS;EAChC;EACA,IAAI,CAAC,aAAa,QAChB,aAAa,SAAS;EAExB,aAAa,UAAU;CACzB;CAEA,cAAc,QAAoB,OAAY,MAAsB;EAClE,KAAKC,OAAO,SAAS;CACvB;CAEA,aAAa,QAAoB;EAC/B,IAAI;EACJ,IAAI;EACJ,SAAS,KAAKH,mBAAmB;EACjC,IAAI,CAAC,QAAQ;GACX,SAAS,CAAC;GACV,KAAKA,mBAAmB,UAAU;EACpC;EACA,OAAO,KAAKC,cAAc;EAC1B,IAAI,CAAC,MAAM;GACT,OAAO,CAAC;GACR,KAAKA,cAAc,UAAU;EAC/B;EACA,OAAO;GAAC;GAAQ;EAAI;CACtB;AACF"}
@@ -23,6 +23,25 @@ export declare class ArrayView<V extends View> implements Output, TypedView<V> {
23
23
  addListener(listener: Listener<V>): () => void;
24
24
  destroy(): void;
25
25
  push(change: Change): readonly never[];
26
+ /**
27
+ * Keep exposing the current snapshot, whatever is pushed in the meantime,
28
+ * until {@link releaseData} or the next {@link flush}. Result type
29
+ * notifications are held back until that flush.
30
+ *
31
+ * A transaction is normally one synchronous task, so nobody can look at a
32
+ * view between its pushes and its flush. Deferred pipelines are different:
33
+ * they are hydrated over several tasks and flushed together (see
34
+ * `QueryDelegate.onPipelinesReady`), and a view that already has its rows
35
+ * must not show them early.
36
+ */
37
+ holdData(): void;
38
+ /**
39
+ * Make `data` current again without notifying anyone yet; listeners are
40
+ * still only called by the next {@link flush}. Views released together get
41
+ * this called on all of them before any is flushed, so a listener of one
42
+ * that reads another sees its rows too.
43
+ */
44
+ releaseData(): void;
26
45
  flush(): void;
27
46
  updateTTL(ttl: TTL): void;
28
47
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"array-view.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/array-view.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,8CAA8C,CAAC;AAC/E,OAAO,KAAK,EAAC,GAAG,EAAC,MAAM,iBAAiB,CAAC;AACzC,OAAO,KAAK,EAAC,QAAQ,EAAc,SAAS,EAAC,MAAM,wBAAwB,CAAC;AAG5E,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,aAAa,CAAC;AACxC,OAAO,EAAa,KAAK,KAAK,EAAE,KAAK,MAAM,EAAC,MAAM,eAAe,CAAC;AAGlE,OAAO,KAAK,EAAQ,MAAM,EAAE,IAAI,EAAC,MAAM,WAAW,CAAC;AA0BnD;;;;;;;;;;GAUG;AACH,qBAAa,SAAS,CAAC,CAAC,SAAS,IAAI,CAAE,YAAW,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;;IAgBpE,SAAS,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC;gBAelC,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,IAAI,GAAG,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,EAClD,SAAS,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI;IAqB/B,IAAI,IAAI,IACmB,CAAC,CAC3B;IAMD,WAAW,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;IAqBjC,OAAO;IAuBP,IAAI,CAAC,MAAM,EAAE,MAAM;IAcnB,KAAK;IAYL,SAAS,CAAC,GAAG,EAAE,GAAG;IAIlB;;;;;;OAMG;IACH,UAAU,IAAI,IAAI;IAMlB,wEAAwE;IACxE,YAAY,IAAI,IAAI;CAiBrB"}
1
+ {"version":3,"file":"array-view.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/array-view.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,8CAA8C,CAAC;AAC/E,OAAO,KAAK,EAAC,GAAG,EAAC,MAAM,iBAAiB,CAAC;AACzC,OAAO,KAAK,EAAC,QAAQ,EAAc,SAAS,EAAC,MAAM,wBAAwB,CAAC;AAG5E,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,aAAa,CAAC;AACxC,OAAO,EAAa,KAAK,KAAK,EAAE,KAAK,MAAM,EAAC,MAAM,eAAe,CAAC;AAGlE,OAAO,KAAK,EAAQ,MAAM,EAAE,IAAI,EAAC,MAAM,WAAW,CAAC;AA0BnD;;;;;;;;;;GAUG;AACH,qBAAa,SAAS,CAAC,CAAC,SAAS,IAAI,CAAE,YAAW,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;;IAgBpE,SAAS,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC;gBA0BlC,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,IAAI,GAAG,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,EAClD,SAAS,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI;IAqB/B,IAAI,IAAI,IAC4C,CAAC,CACpD;IAMD,WAAW,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;IA8BjC,OAAO;IAuBP,IAAI,CAAC,MAAM,EAAE,MAAM;IAcnB;;;;;;;;;;OAUG;IACH,QAAQ,IAAI,IAAI;IAOhB;;;;;OAKG;IACH,WAAW,IAAI,IAAI;IAInB,KAAK;IAcL,SAAS,CAAC,GAAG,EAAE,GAAG;IAIlB;;;;;;OAMG;IACH,UAAU,IAAI,IAAI;IAMlB,wEAAwE;IACxE,YAAY,IAAI,IAAI;CAsBrB"}
@@ -47,6 +47,10 @@ var ArrayView = class {
47
47
  #root;
48
48
  onDestroy;
49
49
  #dirty = false;
50
+ #committedRoot;
51
+ #held = false;
52
+ #heldResultType = "unknown";
53
+ #heldError;
50
54
  #resultType = "unknown";
51
55
  #error;
52
56
  #updateTTL;
@@ -65,7 +69,7 @@ var ArrayView = class {
65
69
  this.#hydrate();
66
70
  }
67
71
  get data() {
68
- return this.#root[""];
72
+ return (this.#committedRoot ?? this.#root)[""];
69
73
  }
70
74
  #getSchema() {
71
75
  return this.#schema ??= this.#input.getSchema();
@@ -82,6 +86,10 @@ var ArrayView = class {
82
86
  for (const listener of this.#listeners) this.#fireListener(listener);
83
87
  }
84
88
  #fireListener(listener) {
89
+ if (this.#committedRoot !== void 0) {
90
+ listener(this.data, this.#heldResultType, this.#heldError);
91
+ return;
92
+ }
85
93
  listener(this.data, this.#resultType, this.#error);
86
94
  }
87
95
  destroy() {
@@ -100,7 +108,35 @@ var ArrayView = class {
100
108
  this.#root = applyChange(this.#root, changeToViewChange(change), this.#getSchema(), "", this.#format, false, this.#txnDirty);
101
109
  return emptyArray;
102
110
  }
111
+ /**
112
+ * Keep exposing the current snapshot, whatever is pushed in the meantime,
113
+ * until {@link releaseData} or the next {@link flush}. Result type
114
+ * notifications are held back until that flush.
115
+ *
116
+ * A transaction is normally one synchronous task, so nobody can look at a
117
+ * view between its pushes and its flush. Deferred pipelines are different:
118
+ * they are hydrated over several tasks and flushed together (see
119
+ * `QueryDelegate.onPipelinesReady`), and a view that already has its rows
120
+ * must not show them early.
121
+ */
122
+ holdData() {
123
+ this.#committedRoot = this.#root;
124
+ this.#held = true;
125
+ this.#heldResultType = this.#resultType;
126
+ this.#heldError = this.#error;
127
+ }
128
+ /**
129
+ * Make `data` current again without notifying anyone yet; listeners are
130
+ * still only called by the next {@link flush}. Views released together get
131
+ * this called on all of them before any is flushed, so a listener of one
132
+ * that reads another sees its rows too.
133
+ */
134
+ releaseData() {
135
+ this.#committedRoot = void 0;
136
+ }
103
137
  flush() {
138
+ this.#committedRoot = void 0;
139
+ this.#held = false;
104
140
  if (!this.#dirty) return;
105
141
  this.#dirty = false;
106
142
  this.#fireListeners();
@@ -126,7 +162,8 @@ var ArrayView = class {
126
162
  #setResultType(resultType, error) {
127
163
  this.#resultType = resultType;
128
164
  this.#error = error;
129
- if (!this.#dirty) this.#fireListeners();
165
+ if (this.#held) this.#dirty = true;
166
+ else if (!this.#dirty) this.#fireListeners();
130
167
  }
131
168
  };
132
169
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"array-view.js","names":["#input","#listeners","#format","#updateTTL","#root","#resultType","#error","#setResultType","#hydrate","#schema","#fireListener","#dirty","#getSchema","#txnDirty","#fireListeners"],"sources":["../../../../../zql/src/ivm/array-view.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport type {Immutable} from '../../../shared/src/immutable.ts';\nimport {emptyArray} from '../../../shared/src/sentinels.ts';\nimport type {ErroredQuery} from '../../../zero-protocol/src/custom-queries.ts';\nimport type {TTL} from '../query/ttl.ts';\nimport type {Listener, ResultType, TypedView} from '../query/typed-view.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport {ChangeType} from './change-type.ts';\nimport type {Change} from './change.ts';\nimport {skipYields, type Input, type Output} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport {applyChange, type ViewChange} from './view-apply-change.ts';\nimport type {Entry, Format, View} from './view.ts';\n\nfunction changeToViewChange(change: Change): ViewChange {\n switch (change[ChangeIndex.TYPE]) {\n case ChangeType.ADD:\n return {type: 'add', node: change[ChangeIndex.NODE]};\n case ChangeType.REMOVE:\n return {type: 'remove', node: change[ChangeIndex.NODE]};\n case ChangeType.CHILD:\n return {\n type: 'child',\n node: change[ChangeIndex.NODE],\n child: {\n relationshipName: change[ChangeIndex.CHILD_DATA].relationshipName,\n change: changeToViewChange(change[ChangeIndex.CHILD_DATA].change),\n },\n };\n case ChangeType.EDIT:\n return {\n type: 'edit',\n node: change[ChangeIndex.NODE],\n oldNode: change[ChangeIndex.OLD_NODE],\n };\n }\n}\n\n/**\n * Implements a materialized view of the output of an operator.\n *\n * It might seem more efficient to use an immutable b-tree for the\n * materialization, but it's not so clear. Inserts in the middle are\n * asymptotically slower in an array, but can often be done with zero\n * allocations, where changes to the b-tree will often require several allocs.\n *\n * Also the plain array view is more convenient for consumers since you can dump\n * it into console to see what it is, rather than having to iterate it.\n */\nexport class ArrayView<V extends View> implements Output, TypedView<V> {\n readonly #input: Input;\n readonly #listeners = new Set<Listener<V>>();\n // Read lazily: a DeferredInput has no schema until its pipeline is attached,\n // and the schema is only needed once there is a change to apply.\n #schema: SourceSchema | undefined;\n readonly #format: Format;\n\n // Synthetic \"root\" entry that has a single \"\" relationship, so that we can\n // treat all changes, including the root change, generically.\n //\n // applyChange is immutable: it returns a new root, preserving the references\n // of unchanged subtrees so consumers (React.memo / Solid) can skip them. We\n // therefore reassign #root on every change rather than mutating in place.\n #root: Entry;\n\n onDestroy: (() => void) | undefined;\n\n #dirty = false;\n #resultType: ResultType = 'unknown';\n #error: ErroredQuery | undefined;\n readonly #updateTTL: (ttl: TTL) => void;\n\n // Objects/arrays created or cloned during the current (un-flushed)\n // transaction. applyChange may mutate these in place rather than copying\n // again, since they are not yet observed by any listener. Replaced at\n // flush(), after which the committed snapshot must be copied-on-write again.\n // A WeakSet (not a Set) so it never extends the lifetime of transient clones.\n #txnDirty: WeakSet<object> = new WeakSet();\n\n constructor(\n input: Input,\n format: Format,\n queryComplete: true | ErroredQuery | Promise<true>,\n updateTTL: (ttl: TTL) => void,\n ) {\n this.#input = input;\n this.#format = format;\n this.#updateTTL = updateTTL;\n this.#root = {'': format.singular ? undefined : []};\n input.setOutput(this);\n\n if (queryComplete === true) {\n this.#resultType = 'complete';\n } else if ('error' in queryComplete) {\n this.#resultType = 'error';\n this.#error = queryComplete;\n } else {\n void queryComplete\n .then(() => this.#setResultType('complete'))\n .catch(e => this.#setResultType('error', e));\n }\n this.#hydrate();\n }\n\n get data() {\n return this.#root[''] as V;\n }\n\n #getSchema(): SourceSchema {\n return (this.#schema ??= this.#input.getSchema());\n }\n\n addListener(listener: Listener<V>) {\n assert(!this.#listeners.has(listener), 'Listener already registered');\n this.#listeners.add(listener);\n\n this.#fireListener(listener);\n\n return () => {\n this.#listeners.delete(listener);\n };\n }\n\n #fireListeners() {\n for (const listener of this.#listeners) {\n this.#fireListener(listener);\n }\n }\n\n #fireListener(listener: Listener<V>) {\n listener(this.data as Immutable<V>, this.#resultType, this.#error);\n }\n\n destroy() {\n this.onDestroy?.();\n }\n\n #hydrate() {\n this.#dirty = true;\n for (const node of skipYields(this.#input.fetch({}))) {\n this.#root = applyChange(\n this.#root,\n {type: 'add', node},\n this.#getSchema(),\n '',\n this.#format,\n false /* withIDs */,\n true /* mutate: #root is freshly created and not yet observed by any\n consumer, so build it in place to avoid O(N^2) array copies.\n Every later push() is immutable, preserving reference\n stability for unchanged subtrees. */,\n );\n }\n this.flush();\n }\n\n push(change: Change) {\n this.#dirty = true;\n this.#root = applyChange(\n this.#root,\n changeToViewChange(change),\n this.#getSchema(),\n '',\n this.#format,\n false /* withIDs */,\n this.#txnDirty /* mutate: copy-on-write within this transaction */,\n );\n return emptyArray;\n }\n\n flush() {\n if (!this.#dirty) {\n return;\n }\n this.#dirty = false;\n this.#fireListeners();\n // The snapshot just handed to listeners is now observed; the next\n // transaction must copy-on-write rather than mutate these objects. A fresh\n // WeakSet drops all \"owned\" marks (WeakSet has no clear()).\n this.#txnDirty = new WeakSet();\n }\n\n updateTTL(ttl: TTL) {\n this.#updateTTL(ttl);\n }\n\n /**\n * The store holds the server-confirmed complete result of this query from a\n * previous sync (the persisted got-queries key exists, pre-authoritative).\n * Never downgrades 'complete'/'error', and never resolves anything that\n * waits on 'complete' — freshness stays a promise only the connection can\n * keep.\n */\n markCached(): void {\n if (this.#resultType === 'unknown') {\n this.#setResultType('cached');\n }\n }\n\n /** The got key was deleted (eviction) while still pre-authoritative. */\n unmarkCached(): void {\n if (this.#resultType === 'cached') {\n this.#setResultType('unknown');\n }\n }\n\n #setResultType(resultType: ResultType, error?: ErroredQuery) {\n this.#resultType = resultType;\n this.#error = error;\n // A dirty view is mid-transaction: rows were pushed and not yet flushed,\n // and the objects in #txnDirty are still mutable. Firing now would hand\n // those to listeners and then fire again at flush() with the same data, so\n // let the pending flush deliver the new result type instead.\n if (!this.#dirty) {\n this.#fireListeners();\n }\n }\n}\n"],"mappings":";;;;;AAcA,SAAS,mBAAmB,QAA4B;CACtD,QAAQ,OAAO,IAAf;EACE,KAAK,GACH,OAAO;GAAC,MAAM;GAAO,MAAM,OAAO;EAAiB;EACrD,KAAK,GACH,OAAO;GAAC,MAAM;GAAU,MAAM,OAAO;EAAiB;EACxD,KAAK,GACH,OAAO;GACL,MAAM;GACN,MAAM,OAAO;GACb,OAAO;IACL,kBAAkB,OAAO,GAAwB;IACjD,QAAQ,mBAAmB,OAAO,GAAwB,MAAM;GAClE;EACF;EACF,KAAK,GACH,OAAO;GACL,MAAM;GACN,MAAM,OAAO;GACb,SAAS,OAAO;EAClB;CACJ;AACF;;;;;;;;;;;;AAaA,IAAa,YAAb,MAAuE;CACrE;CACA,6BAAsB,IAAI,IAAiB;CAG3C;CACA;CAQA;CAEA;CAEA,SAAS;CACT,cAA0B;CAC1B;CACA;CAOA,4BAA6B,IAAI,QAAQ;CAEzC,YACE,OACA,QACA,eACA,WACA;EACA,KAAKA,SAAS;EACd,KAAKE,UAAU;EACf,KAAKC,aAAa;EAClB,KAAKC,QAAQ,EAAC,IAAI,OAAO,WAAW,KAAA,IAAY,CAAC,EAAC;EAClD,MAAM,UAAU,IAAI;EAEpB,IAAI,kBAAkB,MACpB,KAAKC,cAAc;OACd,IAAI,WAAW,eAAe;GACnC,KAAKA,cAAc;GACnB,KAAKC,SAAS;EAChB,OACE,cACG,WAAW,KAAKC,eAAe,UAAU,CAAC,EAC1C,OAAM,MAAK,KAAKA,eAAe,SAAS,CAAC,CAAC;EAE/C,KAAKC,SAAS;CAChB;CAEA,IAAI,OAAO;EACT,OAAO,KAAKJ,MAAM;CACpB;CAEA,aAA2B;EACzB,OAAQ,KAAKK,YAAY,KAAKT,OAAO,UAAU;CACjD;CAEA,YAAY,UAAuB;EACjC,OAAO,CAAC,KAAKC,WAAW,IAAI,QAAQ,GAAG,6BAA6B;EACpE,KAAKA,WAAW,IAAI,QAAQ;EAE5B,KAAKS,cAAc,QAAQ;EAE3B,aAAa;GACX,KAAKT,WAAW,OAAO,QAAQ;EACjC;CACF;CAEA,iBAAiB;EACf,KAAK,MAAM,YAAY,KAAKA,YAC1B,KAAKS,cAAc,QAAQ;CAE/B;CAEA,cAAc,UAAuB;EACnC,SAAS,KAAK,MAAsB,KAAKL,aAAa,KAAKC,MAAM;CACnE;CAEA,UAAU;EACR,KAAK,YAAY;CACnB;CAEA,WAAW;EACT,KAAKK,SAAS;EACd,KAAK,MAAM,QAAQ,WAAW,KAAKX,OAAO,MAAM,CAAC,CAAC,CAAC,GACjD,KAAKI,QAAQ,YACX,KAAKA,OACL;GAAC,MAAM;GAAO;EAAI,GAClB,KAAKQ,WAAW,GAChB,IACA,KAAKV,SACL,OACA,IAIF;EAEF,KAAK,MAAM;CACb;CAEA,KAAK,QAAgB;EACnB,KAAKS,SAAS;EACd,KAAKP,QAAQ,YACX,KAAKA,OACL,mBAAmB,MAAM,GACzB,KAAKQ,WAAW,GAChB,IACA,KAAKV,SACL,OACA,KAAKW,SACP;EACA,OAAO;CACT;CAEA,QAAQ;EACN,IAAI,CAAC,KAAKF,QACR;EAEF,KAAKA,SAAS;EACd,KAAKG,eAAe;EAIpB,KAAKD,4BAAY,IAAI,QAAQ;CAC/B;CAEA,UAAU,KAAU;EAClB,KAAKV,WAAW,GAAG;CACrB;;;;;;;;CASA,aAAmB;EACjB,IAAI,KAAKE,gBAAgB,WACvB,KAAKE,eAAe,QAAQ;CAEhC;;CAGA,eAAqB;EACnB,IAAI,KAAKF,gBAAgB,UACvB,KAAKE,eAAe,SAAS;CAEjC;CAEA,eAAe,YAAwB,OAAsB;EAC3D,KAAKF,cAAc;EACnB,KAAKC,SAAS;EAKd,IAAI,CAAC,KAAKK,QACR,KAAKG,eAAe;CAExB;AACF"}
1
+ {"version":3,"file":"array-view.js","names":["#input","#listeners","#format","#updateTTL","#root","#resultType","#error","#setResultType","#hydrate","#committedRoot","#schema","#fireListener","#heldResultType","#heldError","#dirty","#getSchema","#txnDirty","#held","#fireListeners"],"sources":["../../../../../zql/src/ivm/array-view.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport type {Immutable} from '../../../shared/src/immutable.ts';\nimport {emptyArray} from '../../../shared/src/sentinels.ts';\nimport type {ErroredQuery} from '../../../zero-protocol/src/custom-queries.ts';\nimport type {TTL} from '../query/ttl.ts';\nimport type {Listener, ResultType, TypedView} from '../query/typed-view.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport {ChangeType} from './change-type.ts';\nimport type {Change} from './change.ts';\nimport {skipYields, type Input, type Output} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport {applyChange, type ViewChange} from './view-apply-change.ts';\nimport type {Entry, Format, View} from './view.ts';\n\nfunction changeToViewChange(change: Change): ViewChange {\n switch (change[ChangeIndex.TYPE]) {\n case ChangeType.ADD:\n return {type: 'add', node: change[ChangeIndex.NODE]};\n case ChangeType.REMOVE:\n return {type: 'remove', node: change[ChangeIndex.NODE]};\n case ChangeType.CHILD:\n return {\n type: 'child',\n node: change[ChangeIndex.NODE],\n child: {\n relationshipName: change[ChangeIndex.CHILD_DATA].relationshipName,\n change: changeToViewChange(change[ChangeIndex.CHILD_DATA].change),\n },\n };\n case ChangeType.EDIT:\n return {\n type: 'edit',\n node: change[ChangeIndex.NODE],\n oldNode: change[ChangeIndex.OLD_NODE],\n };\n }\n}\n\n/**\n * Implements a materialized view of the output of an operator.\n *\n * It might seem more efficient to use an immutable b-tree for the\n * materialization, but it's not so clear. Inserts in the middle are\n * asymptotically slower in an array, but can often be done with zero\n * allocations, where changes to the b-tree will often require several allocs.\n *\n * Also the plain array view is more convenient for consumers since you can dump\n * it into console to see what it is, rather than having to iterate it.\n */\nexport class ArrayView<V extends View> implements Output, TypedView<V> {\n readonly #input: Input;\n readonly #listeners = new Set<Listener<V>>();\n // Read lazily: a DeferredInput has no schema until its pipeline is attached,\n // and the schema is only needed once there is a change to apply.\n #schema: SourceSchema | undefined;\n readonly #format: Format;\n\n // Synthetic \"root\" entry that has a single \"\" relationship, so that we can\n // treat all changes, including the root change, generically.\n //\n // applyChange is immutable: it returns a new root, preserving the references\n // of unchanged subtrees so consumers (React.memo / Solid) can skip them. We\n // therefore reassign #root on every change rather than mutating in place.\n #root: Entry;\n\n onDestroy: (() => void) | undefined;\n\n #dirty = false;\n // Set by holdData(): the root that `data` and newly added listeners keep\n // seeing until releaseData() or the next flush, while pushes build up #root\n // behind it.\n #committedRoot: Entry | undefined;\n // From holdData() until the next flush. Outlasts #committedRoot, which\n // releaseData() drops first.\n #held = false;\n // The result type and error as of holdData(): what a listener added while\n // held is called with, so it never sees old rows paired with a new type.\n #heldResultType: ResultType = 'unknown';\n #heldError: ErroredQuery | undefined;\n #resultType: ResultType = 'unknown';\n #error: ErroredQuery | undefined;\n readonly #updateTTL: (ttl: TTL) => void;\n\n // Objects/arrays created or cloned during the current (un-flushed)\n // transaction. applyChange may mutate these in place rather than copying\n // again, since they are not yet observed by any listener. Replaced at\n // flush(), after which the committed snapshot must be copied-on-write again.\n // A WeakSet (not a Set) so it never extends the lifetime of transient clones.\n #txnDirty: WeakSet<object> = new WeakSet();\n\n constructor(\n input: Input,\n format: Format,\n queryComplete: true | ErroredQuery | Promise<true>,\n updateTTL: (ttl: TTL) => void,\n ) {\n this.#input = input;\n this.#format = format;\n this.#updateTTL = updateTTL;\n this.#root = {'': format.singular ? undefined : []};\n input.setOutput(this);\n\n if (queryComplete === true) {\n this.#resultType = 'complete';\n } else if ('error' in queryComplete) {\n this.#resultType = 'error';\n this.#error = queryComplete;\n } else {\n void queryComplete\n .then(() => this.#setResultType('complete'))\n .catch(e => this.#setResultType('error', e));\n }\n this.#hydrate();\n }\n\n get data() {\n return (this.#committedRoot ?? this.#root)[''] as V;\n }\n\n #getSchema(): SourceSchema {\n return (this.#schema ??= this.#input.getSchema());\n }\n\n addListener(listener: Listener<V>) {\n assert(!this.#listeners.has(listener), 'Listener already registered');\n this.#listeners.add(listener);\n\n this.#fireListener(listener);\n\n return () => {\n this.#listeners.delete(listener);\n };\n }\n\n #fireListeners() {\n for (const listener of this.#listeners) {\n this.#fireListener(listener);\n }\n }\n\n #fireListener(listener: Listener<V>) {\n if (this.#committedRoot !== undefined) {\n // Held rows go with the held type; after releaseData() both are current.\n listener(\n this.data as Immutable<V>,\n this.#heldResultType,\n this.#heldError,\n );\n return;\n }\n listener(this.data as Immutable<V>, this.#resultType, this.#error);\n }\n\n destroy() {\n this.onDestroy?.();\n }\n\n #hydrate() {\n this.#dirty = true;\n for (const node of skipYields(this.#input.fetch({}))) {\n this.#root = applyChange(\n this.#root,\n {type: 'add', node},\n this.#getSchema(),\n '',\n this.#format,\n false /* withIDs */,\n true /* mutate: #root is freshly created and not yet observed by any\n consumer, so build it in place to avoid O(N^2) array copies.\n Every later push() is immutable, preserving reference\n stability for unchanged subtrees. */,\n );\n }\n this.flush();\n }\n\n push(change: Change) {\n this.#dirty = true;\n this.#root = applyChange(\n this.#root,\n changeToViewChange(change),\n this.#getSchema(),\n '',\n this.#format,\n false /* withIDs */,\n this.#txnDirty /* mutate: copy-on-write within this transaction */,\n );\n return emptyArray;\n }\n\n /**\n * Keep exposing the current snapshot, whatever is pushed in the meantime,\n * until {@link releaseData} or the next {@link flush}. Result type\n * notifications are held back until that flush.\n *\n * A transaction is normally one synchronous task, so nobody can look at a\n * view between its pushes and its flush. Deferred pipelines are different:\n * they are hydrated over several tasks and flushed together (see\n * `QueryDelegate.onPipelinesReady`), and a view that already has its rows\n * must not show them early.\n */\n holdData(): void {\n this.#committedRoot = this.#root;\n this.#held = true;\n this.#heldResultType = this.#resultType;\n this.#heldError = this.#error;\n }\n\n /**\n * Make `data` current again without notifying anyone yet; listeners are\n * still only called by the next {@link flush}. Views released together get\n * this called on all of them before any is flushed, so a listener of one\n * that reads another sees its rows too.\n */\n releaseData(): void {\n this.#committedRoot = undefined;\n }\n\n flush() {\n this.#committedRoot = undefined;\n this.#held = false;\n if (!this.#dirty) {\n return;\n }\n this.#dirty = false;\n this.#fireListeners();\n // The snapshot just handed to listeners is now observed; the next\n // transaction must copy-on-write rather than mutate these objects. A fresh\n // WeakSet drops all \"owned\" marks (WeakSet has no clear()).\n this.#txnDirty = new WeakSet();\n }\n\n updateTTL(ttl: TTL) {\n this.#updateTTL(ttl);\n }\n\n /**\n * The store holds the server-confirmed complete result of this query from a\n * previous sync (the persisted got-queries key exists, pre-authoritative).\n * Never downgrades 'complete'/'error', and never resolves anything that\n * waits on 'complete' — freshness stays a promise only the connection can\n * keep.\n */\n markCached(): void {\n if (this.#resultType === 'unknown') {\n this.#setResultType('cached');\n }\n }\n\n /** The got key was deleted (eviction) while still pre-authoritative. */\n unmarkCached(): void {\n if (this.#resultType === 'cached') {\n this.#setResultType('unknown');\n }\n }\n\n #setResultType(resultType: ResultType, error?: ErroredQuery) {\n this.#resultType = resultType;\n this.#error = error;\n // A dirty view is mid-transaction: rows were pushed and not yet flushed,\n // and the objects in #txnDirty are still mutable. Firing now would hand\n // those to listeners and then fire again at flush() with the same data, so\n // let the pending flush deliver the new result type instead.\n // A held view is waiting to be exposed together with others at the next\n // flush; that goes for its result type as much as for its rows.\n if (this.#held) {\n // Dirty so that the flush notifies even when no rows were pushed.\n this.#dirty = true;\n } else if (!this.#dirty) {\n this.#fireListeners();\n }\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ import type { Row, Value } from '../../../zero-protocol/src/data.ts';
2
+ import type { NoSubqueryCondition } from '../builder/filter.ts';
3
+ import type { SourceChange } from './source.ts';
4
+ /**
5
+ * The static equality constraint a connection's filters place on a single
6
+ * column: the row is accepted only if `row[column]` is one of `values`.
7
+ * The push-time counterpart of the fetch-time `Constraint` in
8
+ * `constraint.ts`, which pins columns to single values.
9
+ */
10
+ export type StaticConstraint = {
11
+ readonly column: string;
12
+ readonly values: readonly Value[];
13
+ };
14
+ /**
15
+ * Indexes the connections of a source by a static equality constraint
16
+ * (`column = literal` or `column IN (literals)`) taken from each
17
+ * connection's filters, so that a push can determine in O(distinct indexed
18
+ * columns) whether any connection could possibly accept a row, before doing
19
+ * any per-connection work.
20
+ *
21
+ * The index is conservative: {@link mayAccept} returns `true` whenever a
22
+ * connection's filters could accept the row, and may return `true` when they
23
+ * do not (e.g. the connection has further conditions that reject it).
24
+ * It only ever returns `false` when every connection's filters reject the
25
+ * row. Connections without a static equality constraint (no filters, or
26
+ * filters whose top level is not an equality) count as unconstrained and
27
+ * make every row a candidate, as does an index with no connections at all.
28
+ *
29
+ * Values are matched with SameValueZero (`Map` key semantics), which agrees
30
+ * with the `=` / `IN` predicates (`===` / `Set#has`) for every value except
31
+ * `NaN`, where the index is merely more permissive.
32
+ */
33
+ export declare class ConnectionIndex<C> {
34
+ #private;
35
+ add(connection: C, filters: NoSubqueryCondition | undefined): void;
36
+ remove(connection: C): void;
37
+ get size(): number;
38
+ /**
39
+ * Returns `false` only if the filters of every indexed connection reject
40
+ * `row`. An empty index accepts every row: a source with no connections
41
+ * is still written to (e.g. to populate it).
42
+ */
43
+ mayAccept(row: Row): boolean;
44
+ /**
45
+ * Returns `false` only if the filters of every indexed connection reject
46
+ * the row(s) of `change`: the row for an add or remove, and both the old
47
+ * and the new row for an edit.
48
+ */
49
+ mayAcceptChange(change: SourceChange): boolean;
50
+ }
51
+ /**
52
+ * Extracts a static equality constraint that every row accepted by
53
+ * `condition` must satisfy, or `undefined` if there is none.
54
+ *
55
+ * - `column = literal`, `column IS literal` and `column IN (literals)`
56
+ * constrain the column. `column = null` (which the predicate folds to
57
+ * `false`) and an empty `IN` constrain it to the empty set, i.e. the
58
+ * connection never accepts a row.
59
+ * - An `and` is constrained by any of its constrained conditions; the one
60
+ * with the fewest values is chosen, so a never-matching condition wins.
61
+ * - An `or` is constrained only if all of its branches constrain the same
62
+ * column, by the union of their values. Never-matching branches cannot
63
+ * widen the result and are ignored.
64
+ * - Anything else (`!=`, `IS NOT`, `NOT IN`, ranges, `LIKE`, a literal on
65
+ * the left) is unconstrained.
66
+ */
67
+ export declare function staticConstraint(condition: NoSubqueryCondition | undefined): StaticConstraint | undefined;
68
+ //# sourceMappingURL=connection-index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection-index.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/connection-index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,GAAG,EAAE,KAAK,EAAC,MAAM,oCAAoC,CAAC;AACnE,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,sBAAsB,CAAC;AAG9D,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;AAE9C;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,CAAC;CACnC,CAAC;AAEF;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,eAAe,CAAC,CAAC;;IAM5B,GAAG,CAAC,UAAU,EAAE,CAAC,EAAE,OAAO,EAAE,mBAAmB,GAAG,SAAS,GAAG,IAAI;IAwBlE,MAAM,CAAC,UAAU,EAAE,CAAC,GAAG,IAAI;IA8B3B,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED;;;;OAIG;IACH,SAAS,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO;IAY5B;;;;OAIG;IACH,eAAe,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO;CAS/C;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,mBAAmB,GAAG,SAAS,GACzC,gBAAgB,GAAG,SAAS,CA4D9B"}
@@ -0,0 +1,149 @@
1
+ //#region ../zql/src/ivm/connection-index.ts
2
+ /**
3
+ * Indexes the connections of a source by a static equality constraint
4
+ * (`column = literal` or `column IN (literals)`) taken from each
5
+ * connection's filters, so that a push can determine in O(distinct indexed
6
+ * columns) whether any connection could possibly accept a row, before doing
7
+ * any per-connection work.
8
+ *
9
+ * The index is conservative: {@link mayAccept} returns `true` whenever a
10
+ * connection's filters could accept the row, and may return `true` when they
11
+ * do not (e.g. the connection has further conditions that reject it).
12
+ * It only ever returns `false` when every connection's filters reject the
13
+ * row. Connections without a static equality constraint (no filters, or
14
+ * filters whose top level is not an equality) count as unconstrained and
15
+ * make every row a candidate, as does an index with no connections at all.
16
+ *
17
+ * Values are matched with SameValueZero (`Map` key semantics), which agrees
18
+ * with the `=` / `IN` predicates (`===` / `Set#has`) for every value except
19
+ * `NaN`, where the index is merely more permissive.
20
+ */
21
+ var ConnectionIndex = class {
22
+ #byColumn = /* @__PURE__ */ new Map();
23
+ #constraints = /* @__PURE__ */ new Map();
24
+ #unconstrained = 0;
25
+ add(connection, filters) {
26
+ if (this.#constraints.has(connection)) throw new Error("connection is already indexed");
27
+ const constraint = staticConstraint(filters);
28
+ this.#constraints.set(connection, constraint);
29
+ if (!constraint) {
30
+ this.#unconstrained++;
31
+ return;
32
+ }
33
+ if (constraint.values.length === 0) return;
34
+ let byValue = this.#byColumn.get(constraint.column);
35
+ if (!byValue) {
36
+ byValue = /* @__PURE__ */ new Map();
37
+ this.#byColumn.set(constraint.column, byValue);
38
+ }
39
+ for (const value of constraint.values) byValue.set(value, (byValue.get(value) ?? 0) + 1);
40
+ }
41
+ remove(connection) {
42
+ if (!this.#constraints.has(connection)) throw new Error("connection is not indexed");
43
+ const constraint = this.#constraints.get(connection);
44
+ this.#constraints.delete(connection);
45
+ if (!constraint) {
46
+ this.#unconstrained--;
47
+ return;
48
+ }
49
+ if (constraint.values.length === 0) return;
50
+ const byValue = this.#byColumn.get(constraint.column);
51
+ if (!byValue) throw new Error(`no index for column ${constraint.column}`);
52
+ for (const value of constraint.values) {
53
+ const count = byValue.get(value) ?? 0;
54
+ if (count <= 1) byValue.delete(value);
55
+ else byValue.set(value, count - 1);
56
+ }
57
+ if (byValue.size === 0) this.#byColumn.delete(constraint.column);
58
+ }
59
+ get size() {
60
+ return this.#constraints.size;
61
+ }
62
+ /**
63
+ * Returns `false` only if the filters of every indexed connection reject
64
+ * `row`. An empty index accepts every row: a source with no connections
65
+ * is still written to (e.g. to populate it).
66
+ */
67
+ mayAccept(row) {
68
+ if (this.#unconstrained > 0 || this.#constraints.size === 0) return true;
69
+ for (const [column, byValue] of this.#byColumn) if (byValue.has(row[column])) return true;
70
+ return false;
71
+ }
72
+ /**
73
+ * Returns `false` only if the filters of every indexed connection reject
74
+ * the row(s) of `change`: the row for an add or remove, and both the old
75
+ * and the new row for an edit.
76
+ */
77
+ mayAcceptChange(change) {
78
+ if (this.mayAccept(change[1])) return true;
79
+ return change[0] === 2 && this.mayAccept(change[2]);
80
+ }
81
+ };
82
+ /**
83
+ * Extracts a static equality constraint that every row accepted by
84
+ * `condition` must satisfy, or `undefined` if there is none.
85
+ *
86
+ * - `column = literal`, `column IS literal` and `column IN (literals)`
87
+ * constrain the column. `column = null` (which the predicate folds to
88
+ * `false`) and an empty `IN` constrain it to the empty set, i.e. the
89
+ * connection never accepts a row.
90
+ * - An `and` is constrained by any of its constrained conditions; the one
91
+ * with the fewest values is chosen, so a never-matching condition wins.
92
+ * - An `or` is constrained only if all of its branches constrain the same
93
+ * column, by the union of their values. Never-matching branches cannot
94
+ * widen the result and are ignored.
95
+ * - Anything else (`!=`, `IS NOT`, `NOT IN`, ranges, `LIKE`, a literal on
96
+ * the left) is unconstrained.
97
+ */
98
+ function staticConstraint(condition) {
99
+ if (!condition) return;
100
+ switch (condition.type) {
101
+ case "simple": {
102
+ const { left, right, op } = condition;
103
+ if (left.type !== "column" || right.type !== "literal") return;
104
+ switch (op) {
105
+ case "=": return {
106
+ column: left.name,
107
+ values: right.value === null ? [] : [right.value]
108
+ };
109
+ case "IS": return {
110
+ column: left.name,
111
+ values: [right.value]
112
+ };
113
+ case "IN": return Array.isArray(right.value) ? {
114
+ column: left.name,
115
+ values: right.value
116
+ } : void 0;
117
+ default: return;
118
+ }
119
+ }
120
+ case "and": {
121
+ let best;
122
+ for (const c of condition.conditions) {
123
+ const constraint = staticConstraint(c);
124
+ if (constraint && (!best || constraint.values.length < best.values.length)) best = constraint;
125
+ }
126
+ return best;
127
+ }
128
+ case "or": {
129
+ let column;
130
+ const values = [];
131
+ for (const c of condition.conditions) {
132
+ const constraint = staticConstraint(c);
133
+ if (!constraint) return;
134
+ if (constraint.values.length === 0) continue;
135
+ if (column !== void 0 && constraint.column !== column) return;
136
+ column = constraint.column;
137
+ values.push(...constraint.values);
138
+ }
139
+ return {
140
+ column: column ?? "",
141
+ values
142
+ };
143
+ }
144
+ }
145
+ }
146
+ //#endregion
147
+ export { ConnectionIndex };
148
+
149
+ //# sourceMappingURL=connection-index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection-index.js","names":["#byColumn","#constraints","#unconstrained"],"sources":["../../../../../zql/src/ivm/connection-index.ts"],"sourcesContent":["import type {Row, Value} from '../../../zero-protocol/src/data.ts';\nimport type {NoSubqueryCondition} from '../builder/filter.ts';\nimport {ChangeType} from './change-type.ts';\nimport {SourceChangeIndex} from './source-change-index.ts';\nimport type {SourceChange} from './source.ts';\n\n/**\n * The static equality constraint a connection's filters place on a single\n * column: the row is accepted only if `row[column]` is one of `values`.\n * The push-time counterpart of the fetch-time `Constraint` in\n * `constraint.ts`, which pins columns to single values.\n */\nexport type StaticConstraint = {\n readonly column: string;\n readonly values: readonly Value[];\n};\n\n/**\n * Indexes the connections of a source by a static equality constraint\n * (`column = literal` or `column IN (literals)`) taken from each\n * connection's filters, so that a push can determine in O(distinct indexed\n * columns) whether any connection could possibly accept a row, before doing\n * any per-connection work.\n *\n * The index is conservative: {@link mayAccept} returns `true` whenever a\n * connection's filters could accept the row, and may return `true` when they\n * do not (e.g. the connection has further conditions that reject it).\n * It only ever returns `false` when every connection's filters reject the\n * row. Connections without a static equality constraint (no filters, or\n * filters whose top level is not an equality) count as unconstrained and\n * make every row a candidate, as does an index with no connections at all.\n *\n * Values are matched with SameValueZero (`Map` key semantics), which agrees\n * with the `=` / `IN` predicates (`===` / `Set#has`) for every value except\n * `NaN`, where the index is merely more permissive.\n */\nexport class ConnectionIndex<C> {\n // column -> value -> number of connections constrained to that value.\n readonly #byColumn = new Map<string, Map<Value, number>>();\n readonly #constraints = new Map<C, StaticConstraint | undefined>();\n #unconstrained = 0;\n\n add(connection: C, filters: NoSubqueryCondition | undefined): void {\n if (this.#constraints.has(connection)) {\n throw new Error('connection is already indexed');\n }\n const constraint = staticConstraint(filters);\n this.#constraints.set(connection, constraint);\n if (!constraint) {\n this.#unconstrained++;\n return;\n }\n if (constraint.values.length === 0) {\n // Never matches: indexed nowhere, so it is never a candidate.\n return;\n }\n let byValue = this.#byColumn.get(constraint.column);\n if (!byValue) {\n byValue = new Map();\n this.#byColumn.set(constraint.column, byValue);\n }\n for (const value of constraint.values) {\n byValue.set(value, (byValue.get(value) ?? 0) + 1);\n }\n }\n\n remove(connection: C): void {\n if (!this.#constraints.has(connection)) {\n throw new Error('connection is not indexed');\n }\n const constraint = this.#constraints.get(connection);\n this.#constraints.delete(connection);\n if (!constraint) {\n this.#unconstrained--;\n return;\n }\n if (constraint.values.length === 0) {\n return;\n }\n const byValue = this.#byColumn.get(constraint.column);\n if (!byValue) {\n throw new Error(`no index for column ${constraint.column}`);\n }\n for (const value of constraint.values) {\n const count = byValue.get(value) ?? 0;\n if (count <= 1) {\n byValue.delete(value);\n } else {\n byValue.set(value, count - 1);\n }\n }\n if (byValue.size === 0) {\n this.#byColumn.delete(constraint.column);\n }\n }\n\n get size(): number {\n return this.#constraints.size;\n }\n\n /**\n * Returns `false` only if the filters of every indexed connection reject\n * `row`. An empty index accepts every row: a source with no connections\n * is still written to (e.g. to populate it).\n */\n mayAccept(row: Row): boolean {\n if (this.#unconstrained > 0 || this.#constraints.size === 0) {\n return true;\n }\n for (const [column, byValue] of this.#byColumn) {\n if (byValue.has(row[column])) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Returns `false` only if the filters of every indexed connection reject\n * the row(s) of `change`: the row for an add or remove, and both the old\n * and the new row for an edit.\n */\n mayAcceptChange(change: SourceChange): boolean {\n if (this.mayAccept(change[SourceChangeIndex.ROW])) {\n return true;\n }\n return (\n change[SourceChangeIndex.TYPE] === ChangeType.EDIT &&\n this.mayAccept(change[SourceChangeIndex.OLD_ROW])\n );\n }\n}\n\n/**\n * Extracts a static equality constraint that every row accepted by\n * `condition` must satisfy, or `undefined` if there is none.\n *\n * - `column = literal`, `column IS literal` and `column IN (literals)`\n * constrain the column. `column = null` (which the predicate folds to\n * `false`) and an empty `IN` constrain it to the empty set, i.e. the\n * connection never accepts a row.\n * - An `and` is constrained by any of its constrained conditions; the one\n * with the fewest values is chosen, so a never-matching condition wins.\n * - An `or` is constrained only if all of its branches constrain the same\n * column, by the union of their values. Never-matching branches cannot\n * widen the result and are ignored.\n * - Anything else (`!=`, `IS NOT`, `NOT IN`, ranges, `LIKE`, a literal on\n * the left) is unconstrained.\n */\nexport function staticConstraint(\n condition: NoSubqueryCondition | undefined,\n): StaticConstraint | undefined {\n if (!condition) {\n return undefined;\n }\n switch (condition.type) {\n case 'simple': {\n const {left, right, op} = condition;\n if (left.type !== 'column' || right.type !== 'literal') {\n return undefined;\n }\n switch (op) {\n case '=':\n return {\n column: left.name,\n values: right.value === null ? [] : [right.value],\n };\n case 'IS':\n return {column: left.name, values: [right.value]};\n case 'IN':\n return Array.isArray(right.value)\n ? {column: left.name, values: right.value}\n : undefined;\n default:\n return undefined;\n }\n }\n case 'and': {\n let best: StaticConstraint | undefined;\n for (const c of condition.conditions) {\n const constraint = staticConstraint(c);\n if (\n constraint &&\n (!best || constraint.values.length < best.values.length)\n ) {\n best = constraint;\n }\n }\n return best;\n }\n case 'or': {\n let column: string | undefined;\n const values: Value[] = [];\n for (const c of condition.conditions) {\n const constraint = staticConstraint(c);\n if (!constraint) {\n return undefined;\n }\n if (constraint.values.length === 0) {\n continue; // a never-matching branch cannot widen the result\n }\n if (column !== undefined && constraint.column !== column) {\n return undefined;\n }\n column = constraint.column;\n values.push(...constraint.values);\n }\n // Every branch never matches: the whole `or` never matches.\n return {column: column ?? '', values};\n }\n }\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { assert, unreachable } from "../../../shared/src/asserts.js";
2
+ import { areEqual } from "../../../shared/src/arrays.js";
2
3
  import { makeAddChange, makeRemoveChange } from "./change.js";
3
4
  import { normalizeUndefined } from "./data.js";
4
- import { areEqual } from "../../../shared/src/arrays.js";
5
5
  import { throwFilterOutput } from "./filter-operators.js";
6
6
  //#region ../zql/src/ivm/exists.ts
7
7
  /**
@@ -1,4 +1,5 @@
1
1
  import type { BuilderDelegate } from '../builder/builder.ts';
2
+ import type { NoSubqueryCondition } from '../builder/filter.ts';
2
3
  import type { Change } from './change.ts';
3
4
  import { type Node } from './data.ts';
4
5
  import type { FetchRequest, Input, InputBase, Output } from './operator.ts';
@@ -41,7 +42,7 @@ export interface FilterOperator extends FilterInput, FilterOutput {
41
42
  export declare const throwFilterOutput: FilterOutput;
42
43
  export declare class FilterStart implements FilterInput, Output {
43
44
  #private;
44
- constructor(input: Input);
45
+ constructor(input: Input, condition?: NoSubqueryCondition | undefined);
45
46
  setFilterOutput(output: FilterOutput): void;
46
47
  destroy(): void;
47
48
  getSchema(): SourceSchema;
@@ -60,5 +61,5 @@ export declare class FilterEnd implements Input, FilterOutput {
60
61
  getSchema(): SourceSchema;
61
62
  push(change: Change): Stream<"yield">;
62
63
  }
63
- export declare function buildFilterPipeline(input: Input, delegate: BuilderDelegate, pipeline: (filterInput: FilterInput) => FilterInput): Input;
64
+ export declare function buildFilterPipeline(input: Input, delegate: BuilderDelegate, pipeline: (filterInput: FilterInput) => FilterInput, condition?: NoSubqueryCondition | undefined): Input;
64
65
  //# sourceMappingURL=filter-operators.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"filter-operators.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/filter-operators.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,aAAa,CAAC;AACxC,OAAO,EAAC,KAAK,IAAI,EAAC,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,EAAC,YAAY,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAC,MAAM,eAAe,CAAC;AAC1E,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAC,KAAK,MAAM,EAAC,MAAM,aAAa,CAAC;AAExC;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,WAAW,WAAY,SAAQ,SAAS;IAC5C,+CAA+C;IAC/C,eAAe,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAAC;CAC7C;AAED,MAAM,WAAW,YAAa,SAAQ,MAAM;IAI1C,WAAW,IAAI,IAAI,CAAC;IACpB,MAAM,CAAC,IAAI,EAAE,IAAI,GAAG,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACvD,SAAS,IAAI,IAAI,CAAC;CACnB;AAED,MAAM,WAAW,cAAe,SAAQ,WAAW,EAAE,YAAY;CAAG;AAEpE;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,EAAE,YAa/B,CAAC;AAEF,qBAAa,WAAY,YAAW,WAAW,EAAE,MAAM;;gBAIzC,KAAK,EAAE,KAAK;IAKxB,eAAe,CAAC,MAAM,EAAE,YAAY;IAIpC,OAAO,IAAI,IAAI;IAIf,SAAS,IAAI,YAAY;IAIzB,IAAI,CAAC,MAAM,EAAE,MAAM;IAIlB,KAAK,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAAC,IAAI,GAAG,OAAO,CAAC;CAkBlD;AAED,qBAAa,SAAU,YAAW,KAAK,EAAE,YAAY;;gBAMvC,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW;IAMlD,KAAK,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAAC,IAAI,GAAG,OAAO,CAAC;IAIhD,WAAW;IACX,SAAS;IAET,MAAM,CAAC,KAAK,EAAE,IAAI;IAIlB,SAAS,CAAC,MAAM,EAAE,MAAM;IAIxB,OAAO,IAAI,IAAI;IAIf,SAAS,IAAI,YAAY;IAIzB,IAAI,CAAC,MAAM,EAAE,MAAM;CAGpB;AAID,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,eAAe,EACzB,QAAQ,EAAE,CAAC,WAAW,EAAE,WAAW,KAAK,WAAW,GAClD,KAAK,CAQP"}
1
+ {"version":3,"file":"filter-operators.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/filter-operators.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,sBAAsB,CAAC;AAC9D,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,aAAa,CAAC;AACxC,OAAO,EAAC,KAAK,IAAI,EAAC,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,EAAC,YAAY,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAC,MAAM,eAAe,CAAC;AAC1E,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAC,KAAK,MAAM,EAAC,MAAM,aAAa,CAAC;AAExC;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,WAAW,WAAY,SAAQ,SAAS;IAC5C,+CAA+C;IAC/C,eAAe,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAAC;CAC7C;AAED,MAAM,WAAW,YAAa,SAAQ,MAAM;IAI1C,WAAW,IAAI,IAAI,CAAC;IACpB,MAAM,CAAC,IAAI,EAAE,IAAI,GAAG,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACvD,SAAS,IAAI,IAAI,CAAC;CACnB;AAED,MAAM,WAAW,cAAe,SAAQ,WAAW,EAAE,YAAY;CAAG;AAEpE;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,EAAE,YAa/B,CAAC;AAEF,qBAAa,WAAY,YAAW,WAAW,EAAE,MAAM;;gBAKzC,KAAK,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,mBAAmB,GAAG,SAAS;IAMrE,eAAe,CAAC,MAAM,EAAE,YAAY;IAIpC,OAAO,IAAI,IAAI;IAIf,SAAS,IAAI,YAAY;IAIzB,IAAI,CAAC,MAAM,EAAE,MAAM;IAIlB,KAAK,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAAC,IAAI,GAAG,OAAO,CAAC;CAqBlD;AAeD,qBAAa,SAAU,YAAW,KAAK,EAAE,YAAY;;gBAMvC,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW;IAMlD,KAAK,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAAC,IAAI,GAAG,OAAO,CAAC;IAIhD,WAAW;IACX,SAAS;IAET,MAAM,CAAC,KAAK,EAAE,IAAI;IAIlB,SAAS,CAAC,MAAM,EAAE,MAAM;IAIxB,OAAO,IAAI,IAAI;IAIf,SAAS,IAAI,YAAY;IAIzB,IAAI,CAAC,MAAM,EAAE,MAAM;CAGpB;AAID,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,eAAe,EACzB,QAAQ,EAAE,CAAC,WAAW,EAAE,WAAW,KAAK,WAAW,EACnD,SAAS,CAAC,EAAE,mBAAmB,GAAG,SAAS,GAC1C,KAAK,CAQP"}
@@ -18,9 +18,11 @@ var throwFilterOutput = {
18
18
  };
19
19
  var FilterStart = class {
20
20
  #input;
21
+ #condition;
21
22
  #output = throwFilterOutput;
22
- constructor(input) {
23
+ constructor(input, condition) {
23
24
  this.#input = input;
25
+ this.#condition = condition;
24
26
  input.setOutput(this);
25
27
  }
26
28
  setFilterOutput(output) {
@@ -36,9 +38,14 @@ var FilterStart = class {
36
38
  return this.#output.push(change, this);
37
39
  }
38
40
  *fetch(req) {
41
+ const mergedFilter = mergeFilters(req.filter, this.#condition);
42
+ const childReq = mergedFilter === req.filter ? req : {
43
+ ...req,
44
+ filter: mergedFilter
45
+ };
39
46
  this.#output.beginFilter();
40
47
  try {
41
- for (const node of this.#input.fetch(req)) {
48
+ for (const node of this.#input.fetch(childReq)) {
42
49
  if (node === "yield") {
43
50
  yield node;
44
51
  continue;
@@ -50,6 +57,14 @@ var FilterStart = class {
50
57
  }
51
58
  }
52
59
  };
60
+ function mergeFilters(reqFilter, ownCondition) {
61
+ if (!ownCondition) return reqFilter;
62
+ if (!reqFilter) return ownCondition;
63
+ return {
64
+ type: "and",
65
+ conditions: [reqFilter, ownCondition]
66
+ };
67
+ }
53
68
  var FilterEnd = class {
54
69
  #start;
55
70
  #input;
@@ -81,8 +96,8 @@ var FilterEnd = class {
81
96
  }
82
97
  };
83
98
  var returnTrueEmptyIterator = makeEmptyIteratorWithReturn(true);
84
- function buildFilterPipeline(input, delegate, pipeline) {
85
- const filterStart = new FilterStart(input);
99
+ function buildFilterPipeline(input, delegate, pipeline, condition) {
100
+ const filterStart = new FilterStart(input, condition);
86
101
  delegate.addEdge(input, filterStart);
87
102
  const middle = pipeline(filterStart);
88
103
  delegate.addEdge(filterStart, middle);
@@ -1 +1 @@
1
- {"version":3,"file":"filter-operators.js","names":["#input","#output","#start"],"sources":["../../../../../zql/src/ivm/filter-operators.ts"],"sourcesContent":["import {makeEmptyIteratorWithReturn} from '../../../shared/src/iterables.ts';\nimport type {BuilderDelegate} from '../builder/builder.ts';\nimport type {Change} from './change.ts';\nimport {type Node} from './data.ts';\nimport type {FetchRequest, Input, InputBase, Output} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport {type Stream} from './stream.ts';\n\n/**\n * The `where` clause of a ZQL query is implemented using a sub-graph of\n * `FilterOperators`. This sub-graph starts with a `FilterStart` operator,\n * that adapts from the normal `Operator` `Output`, to the\n * `FilterOperator` `FilterInput`, and ends with a `FilterEnd` operator that\n * adapts from a `FilterOperator` `FilterOutput` to a normal `Operator` `Input`.\n * `FilterOperator`'s do not have `fetch` instead they have a\n * `filter(node: Node): boolean` method.\n * They also have `push` which is just like normal `Operator` push.\n * Not having a `fetch` means these `FilterOperator`'s cannot modify\n * `Node` `row`s or `relationship`s, but they shouldn't, they should just\n * filter.\n *\n * This `FilterOperator` abstraction enables much more efficient processing of\n * `fetch` for `where` clauses containing OR conditions.\n *\n * See https://github.com/rocicorp/mono/pull/4339\n */\n\nexport interface FilterInput extends InputBase {\n /** Tell the input where to send its output. */\n setFilterOutput(output: FilterOutput): void;\n}\n\nexport interface FilterOutput extends Output {\n // Lets the operator know that we're in a loop of filtering\n // nodes. E.g., so the operator can cache results for the\n // duration of the loop.\n beginFilter(): void;\n filter(node: Node): IterableIterator<'yield', boolean>;\n endFilter(): void;\n}\n\nexport interface FilterOperator extends FilterInput, FilterOutput {}\n\n/**\n * An implementation of FilterOutput that throws if push or filter is called.\n * It is used as the initial value for for an operator's output before it is\n * set.\n */\nexport const throwFilterOutput: FilterOutput = {\n // oxlint-disable-next-line require-yield\n *push(_change: Change): Stream<'yield'> {\n throw new Error('Output not set');\n },\n\n // oxlint-disable-next-line require-yield\n *filter(_node: Node): IterableIterator<'yield', boolean> {\n throw new Error('Output not set');\n },\n\n beginFilter() {},\n endFilter() {},\n};\n\nexport class FilterStart implements FilterInput, Output {\n readonly #input: Input;\n #output: FilterOutput = throwFilterOutput;\n\n constructor(input: Input) {\n this.#input = input;\n input.setOutput(this);\n }\n\n setFilterOutput(output: FilterOutput) {\n this.#output = output;\n }\n\n destroy(): void {\n this.#input.destroy();\n }\n\n getSchema(): SourceSchema {\n return this.#input.getSchema();\n }\n\n push(change: Change) {\n return this.#output.push(change, this);\n }\n\n *fetch(req: FetchRequest): Stream<Node | 'yield'> {\n this.#output.beginFilter();\n try {\n for (const node of this.#input.fetch(req)) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n if (yield* this.#output.filter(node)) {\n yield node;\n }\n }\n } finally {\n // finally is important if an exception is thrown or\n // if the stream is not fully consumed.\n this.#output.endFilter();\n }\n }\n}\n\nexport class FilterEnd implements Input, FilterOutput {\n readonly #start: FilterStart;\n readonly #input: FilterInput;\n\n #output: Output = throwFilterOutput;\n\n constructor(start: FilterStart, input: FilterInput) {\n this.#start = start;\n this.#input = input;\n input.setFilterOutput(this);\n }\n\n fetch(req: FetchRequest): Stream<Node | 'yield'> {\n return this.#start.fetch(req);\n }\n\n beginFilter() {}\n endFilter() {}\n\n filter(_node: Node) {\n return returnTrueEmptyIterator;\n }\n\n setOutput(output: Output) {\n this.#output = output;\n }\n\n destroy(): void {\n this.#input.destroy();\n }\n\n getSchema(): SourceSchema {\n return this.#input.getSchema();\n }\n\n push(change: Change) {\n return this.#output.push(change, this);\n }\n}\n\nconst returnTrueEmptyIterator = makeEmptyIteratorWithReturn(true);\n\nexport function buildFilterPipeline(\n input: Input,\n delegate: BuilderDelegate,\n pipeline: (filterInput: FilterInput) => FilterInput,\n): Input {\n const filterStart = new FilterStart(input);\n delegate.addEdge(input, filterStart);\n const middle = pipeline(filterStart);\n delegate.addEdge(filterStart, middle);\n const filterEnd = new FilterEnd(filterStart, middle);\n delegate.addEdge(middle, filterEnd);\n return filterEnd;\n}\n"],"mappings":";;;;;;;;AAgDA,IAAa,oBAAkC;CAE7C,CAAC,KAAK,SAAkC;EACtC,MAAM,IAAI,MAAM,gBAAgB;CAClC;CAGA,CAAC,OAAO,OAAiD;EACvD,MAAM,IAAI,MAAM,gBAAgB;CAClC;CAEA,cAAc,CAAC;CACf,YAAY,CAAC;AACf;AAEA,IAAa,cAAb,MAAwD;CACtD;CACA,UAAwB;CAExB,YAAY,OAAc;EACxB,KAAKA,SAAS;EACd,MAAM,UAAU,IAAI;CACtB;CAEA,gBAAgB,QAAsB;EACpC,KAAKC,UAAU;CACjB;CAEA,UAAgB;EACd,KAAKD,OAAO,QAAQ;CACtB;CAEA,YAA0B;EACxB,OAAO,KAAKA,OAAO,UAAU;CAC/B;CAEA,KAAK,QAAgB;EACnB,OAAO,KAAKC,QAAQ,KAAK,QAAQ,IAAI;CACvC;CAEA,CAAC,MAAM,KAA2C;EAChD,KAAKA,QAAQ,YAAY;EACzB,IAAI;GACF,KAAK,MAAM,QAAQ,KAAKD,OAAO,MAAM,GAAG,GAAG;IACzC,IAAI,SAAS,SAAS;KACpB,MAAM;KACN;IACF;IACA,IAAI,OAAO,KAAKC,QAAQ,OAAO,IAAI,GACjC,MAAM;GAEV;EACF,UAAU;GAGR,KAAKA,QAAQ,UAAU;EACzB;CACF;AACF;AAEA,IAAa,YAAb,MAAsD;CACpD;CACA;CAEA,UAAkB;CAElB,YAAY,OAAoB,OAAoB;EAClD,KAAKC,SAAS;EACd,KAAKF,SAAS;EACd,MAAM,gBAAgB,IAAI;CAC5B;CAEA,MAAM,KAA2C;EAC/C,OAAO,KAAKE,OAAO,MAAM,GAAG;CAC9B;CAEA,cAAc,CAAC;CACf,YAAY,CAAC;CAEb,OAAO,OAAa;EAClB,OAAO;CACT;CAEA,UAAU,QAAgB;EACxB,KAAKD,UAAU;CACjB;CAEA,UAAgB;EACd,KAAKD,OAAO,QAAQ;CACtB;CAEA,YAA0B;EACxB,OAAO,KAAKA,OAAO,UAAU;CAC/B;CAEA,KAAK,QAAgB;EACnB,OAAO,KAAKC,QAAQ,KAAK,QAAQ,IAAI;CACvC;AACF;AAEA,IAAM,0BAA0B,4BAA4B,IAAI;AAEhE,SAAgB,oBACd,OACA,UACA,UACO;CACP,MAAM,cAAc,IAAI,YAAY,KAAK;CACzC,SAAS,QAAQ,OAAO,WAAW;CACnC,MAAM,SAAS,SAAS,WAAW;CACnC,SAAS,QAAQ,aAAa,MAAM;CACpC,MAAM,YAAY,IAAI,UAAU,aAAa,MAAM;CACnD,SAAS,QAAQ,QAAQ,SAAS;CAClC,OAAO;AACT"}
1
+ {"version":3,"file":"filter-operators.js","names":["#input","#condition","#output","#start"],"sources":["../../../../../zql/src/ivm/filter-operators.ts"],"sourcesContent":["import {makeEmptyIteratorWithReturn} from '../../../shared/src/iterables.ts';\nimport type {BuilderDelegate} from '../builder/builder.ts';\nimport type {NoSubqueryCondition} from '../builder/filter.ts';\nimport type {Change} from './change.ts';\nimport {type Node} from './data.ts';\nimport type {FetchRequest, Input, InputBase, Output} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport {type Stream} from './stream.ts';\n\n/**\n * The `where` clause of a ZQL query is implemented using a sub-graph of\n * `FilterOperators`. This sub-graph starts with a `FilterStart` operator,\n * that adapts from the normal `Operator` `Output`, to the\n * `FilterOperator` `FilterInput`, and ends with a `FilterEnd` operator that\n * adapts from a `FilterOperator` `FilterOutput` to a normal `Operator` `Input`.\n * `FilterOperator`'s do not have `fetch` instead they have a\n * `filter(node: Node): boolean` method.\n * They also have `push` which is just like normal `Operator` push.\n * Not having a `fetch` means these `FilterOperator`'s cannot modify\n * `Node` `row`s or `relationship`s, but they shouldn't, they should just\n * filter.\n *\n * This `FilterOperator` abstraction enables much more efficient processing of\n * `fetch` for `where` clauses containing OR conditions.\n *\n * See https://github.com/rocicorp/mono/pull/4339\n */\n\nexport interface FilterInput extends InputBase {\n /** Tell the input where to send its output. */\n setFilterOutput(output: FilterOutput): void;\n}\n\nexport interface FilterOutput extends Output {\n // Lets the operator know that we're in a loop of filtering\n // nodes. E.g., so the operator can cache results for the\n // duration of the loop.\n beginFilter(): void;\n filter(node: Node): IterableIterator<'yield', boolean>;\n endFilter(): void;\n}\n\nexport interface FilterOperator extends FilterInput, FilterOutput {}\n\n/**\n * An implementation of FilterOutput that throws if push or filter is called.\n * It is used as the initial value for for an operator's output before it is\n * set.\n */\nexport const throwFilterOutput: FilterOutput = {\n // oxlint-disable-next-line require-yield\n *push(_change: Change): Stream<'yield'> {\n throw new Error('Output not set');\n },\n\n // oxlint-disable-next-line require-yield\n *filter(_node: Node): IterableIterator<'yield', boolean> {\n throw new Error('Output not set');\n },\n\n beginFilter() {},\n endFilter() {},\n};\n\nexport class FilterStart implements FilterInput, Output {\n readonly #input: Input;\n readonly #condition: NoSubqueryCondition | undefined;\n #output: FilterOutput = throwFilterOutput;\n\n constructor(input: Input, condition?: NoSubqueryCondition | undefined) {\n this.#input = input;\n this.#condition = condition;\n input.setOutput(this);\n }\n\n setFilterOutput(output: FilterOutput) {\n this.#output = output;\n }\n\n destroy(): void {\n this.#input.destroy();\n }\n\n getSchema(): SourceSchema {\n return this.#input.getSchema();\n }\n\n push(change: Change) {\n return this.#output.push(change, this);\n }\n\n *fetch(req: FetchRequest): Stream<Node | 'yield'> {\n const mergedFilter = mergeFilters(req.filter, this.#condition);\n const childReq =\n mergedFilter === req.filter ? req : {...req, filter: mergedFilter};\n this.#output.beginFilter();\n try {\n for (const node of this.#input.fetch(childReq)) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n if (yield* this.#output.filter(node)) {\n yield node;\n }\n }\n } finally {\n // finally is important if an exception is thrown or\n // if the stream is not fully consumed.\n this.#output.endFilter();\n }\n }\n}\n\nfunction mergeFilters(\n reqFilter: NoSubqueryCondition | undefined,\n ownCondition: NoSubqueryCondition | undefined,\n): NoSubqueryCondition | undefined {\n if (!ownCondition) {\n return reqFilter;\n }\n if (!reqFilter) {\n return ownCondition;\n }\n return {type: 'and', conditions: [reqFilter, ownCondition]};\n}\n\nexport class FilterEnd implements Input, FilterOutput {\n readonly #start: FilterStart;\n readonly #input: FilterInput;\n\n #output: Output = throwFilterOutput;\n\n constructor(start: FilterStart, input: FilterInput) {\n this.#start = start;\n this.#input = input;\n input.setFilterOutput(this);\n }\n\n fetch(req: FetchRequest): Stream<Node | 'yield'> {\n return this.#start.fetch(req);\n }\n\n beginFilter() {}\n endFilter() {}\n\n filter(_node: Node) {\n return returnTrueEmptyIterator;\n }\n\n setOutput(output: Output) {\n this.#output = output;\n }\n\n destroy(): void {\n this.#input.destroy();\n }\n\n getSchema(): SourceSchema {\n return this.#input.getSchema();\n }\n\n push(change: Change) {\n return this.#output.push(change, this);\n }\n}\n\nconst returnTrueEmptyIterator = makeEmptyIteratorWithReturn(true);\n\nexport function buildFilterPipeline(\n input: Input,\n delegate: BuilderDelegate,\n pipeline: (filterInput: FilterInput) => FilterInput,\n condition?: NoSubqueryCondition | undefined,\n): Input {\n const filterStart = new FilterStart(input, condition);\n delegate.addEdge(input, filterStart);\n const middle = pipeline(filterStart);\n delegate.addEdge(filterStart, middle);\n const filterEnd = new FilterEnd(filterStart, middle);\n delegate.addEdge(middle, filterEnd);\n return filterEnd;\n}\n"],"mappings":";;;;;;;;AAiDA,IAAa,oBAAkC;CAE7C,CAAC,KAAK,SAAkC;EACtC,MAAM,IAAI,MAAM,gBAAgB;CAClC;CAGA,CAAC,OAAO,OAAiD;EACvD,MAAM,IAAI,MAAM,gBAAgB;CAClC;CAEA,cAAc,CAAC;CACf,YAAY,CAAC;AACf;AAEA,IAAa,cAAb,MAAwD;CACtD;CACA;CACA,UAAwB;CAExB,YAAY,OAAc,WAA6C;EACrE,KAAKA,SAAS;EACd,KAAKC,aAAa;EAClB,MAAM,UAAU,IAAI;CACtB;CAEA,gBAAgB,QAAsB;EACpC,KAAKC,UAAU;CACjB;CAEA,UAAgB;EACd,KAAKF,OAAO,QAAQ;CACtB;CAEA,YAA0B;EACxB,OAAO,KAAKA,OAAO,UAAU;CAC/B;CAEA,KAAK,QAAgB;EACnB,OAAO,KAAKE,QAAQ,KAAK,QAAQ,IAAI;CACvC;CAEA,CAAC,MAAM,KAA2C;EAChD,MAAM,eAAe,aAAa,IAAI,QAAQ,KAAKD,UAAU;EAC7D,MAAM,WACJ,iBAAiB,IAAI,SAAS,MAAM;GAAC,GAAG;GAAK,QAAQ;EAAY;EACnE,KAAKC,QAAQ,YAAY;EACzB,IAAI;GACF,KAAK,MAAM,QAAQ,KAAKF,OAAO,MAAM,QAAQ,GAAG;IAC9C,IAAI,SAAS,SAAS;KACpB,MAAM;KACN;IACF;IACA,IAAI,OAAO,KAAKE,QAAQ,OAAO,IAAI,GACjC,MAAM;GAEV;EACF,UAAU;GAGR,KAAKA,QAAQ,UAAU;EACzB;CACF;AACF;AAEA,SAAS,aACP,WACA,cACiC;CACjC,IAAI,CAAC,cACH,OAAO;CAET,IAAI,CAAC,WACH,OAAO;CAET,OAAO;EAAC,MAAM;EAAO,YAAY,CAAC,WAAW,YAAY;CAAC;AAC5D;AAEA,IAAa,YAAb,MAAsD;CACpD;CACA;CAEA,UAAkB;CAElB,YAAY,OAAoB,OAAoB;EAClD,KAAKC,SAAS;EACd,KAAKH,SAAS;EACd,MAAM,gBAAgB,IAAI;CAC5B;CAEA,MAAM,KAA2C;EAC/C,OAAO,KAAKG,OAAO,MAAM,GAAG;CAC9B;CAEA,cAAc,CAAC;CACf,YAAY,CAAC;CAEb,OAAO,OAAa;EAClB,OAAO;CACT;CAEA,UAAU,QAAgB;EACxB,KAAKD,UAAU;CACjB;CAEA,UAAgB;EACd,KAAKF,OAAO,QAAQ;CACtB;CAEA,YAA0B;EACxB,OAAO,KAAKA,OAAO,UAAU;CAC/B;CAEA,KAAK,QAAgB;EACnB,OAAO,KAAKE,QAAQ,KAAK,QAAQ,IAAI;CACvC;AACF;AAEA,IAAM,0BAA0B,4BAA4B,IAAI;AAEhE,SAAgB,oBACd,OACA,UACA,UACA,WACO;CACP,MAAM,cAAc,IAAI,YAAY,OAAO,SAAS;CACpD,SAAS,QAAQ,OAAO,WAAW;CACnC,MAAM,SAAS,SAAS,WAAW;CACnC,SAAS,QAAQ,aAAa,MAAM;CACpC,MAAM,YAAY,IAAI,UAAU,aAAa,MAAM;CACnD,SAAS,QAAQ,QAAQ,SAAS;CAClC,OAAO;AACT"}