@rocicorp/zero 1.10.0-canary.21 → 1.10.0-canary.23

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 (334) hide show
  1. package/out/analyze-query/src/bin-analyze.js +1 -1
  2. package/out/analyze-query/src/bin-analyze.js.map +1 -1
  3. package/out/replicache/src/dag/gc.d.ts.map +1 -1
  4. package/out/replicache/src/dag/gc.js +2 -1
  5. package/out/replicache/src/dag/gc.js.map +1 -1
  6. package/out/replicache/src/dag/invalid-ref-count-error.d.ts +12 -0
  7. package/out/replicache/src/dag/invalid-ref-count-error.d.ts.map +1 -0
  8. package/out/replicache/src/dag/invalid-ref-count-error.js +19 -0
  9. package/out/replicache/src/dag/invalid-ref-count-error.js.map +1 -0
  10. package/out/replicache/src/dag/store-impl.d.ts +12 -2
  11. package/out/replicache/src/dag/store-impl.d.ts.map +1 -1
  12. package/out/replicache/src/dag/store-impl.js +22 -8
  13. package/out/replicache/src/dag/store-impl.js.map +1 -1
  14. package/out/replicache/src/kv/expo-sqlite/store.d.ts.map +1 -1
  15. package/out/replicache/src/kv/expo-sqlite/store.js +1 -1
  16. package/out/replicache/src/kv/expo-sqlite/store.js.map +1 -1
  17. package/out/replicache/src/kv/idb-store-with-mem-fallback.d.ts +15 -5
  18. package/out/replicache/src/kv/idb-store-with-mem-fallback.d.ts.map +1 -1
  19. package/out/replicache/src/kv/idb-store-with-mem-fallback.js +25 -25
  20. package/out/replicache/src/kv/idb-store-with-mem-fallback.js.map +1 -1
  21. package/out/replicache/src/kv/idb-store.d.ts +9 -0
  22. package/out/replicache/src/kv/idb-store.d.ts.map +1 -1
  23. package/out/replicache/src/kv/idb-store.js +14 -2
  24. package/out/replicache/src/kv/idb-store.js.map +1 -1
  25. package/out/replicache/src/kv/mem-store.d.ts +1 -0
  26. package/out/replicache/src/kv/mem-store.d.ts.map +1 -1
  27. package/out/replicache/src/kv/mem-store.js +3 -0
  28. package/out/replicache/src/kv/mem-store.js.map +1 -1
  29. package/out/replicache/src/kv/op-sqlite/store.d.ts.map +1 -1
  30. package/out/replicache/src/kv/op-sqlite/store.js +1 -1
  31. package/out/replicache/src/kv/op-sqlite/store.js.map +1 -1
  32. package/out/replicache/src/kv/sqlite-store.d.ts +2 -1
  33. package/out/replicache/src/kv/sqlite-store.d.ts.map +1 -1
  34. package/out/replicache/src/kv/sqlite-store.js +6 -1
  35. package/out/replicache/src/kv/sqlite-store.js.map +1 -1
  36. package/out/replicache/src/kv/store.d.ts +6 -0
  37. package/out/replicache/src/kv/store.d.ts.map +1 -1
  38. package/out/replicache/src/persist/collect-idb-databases.d.ts +10 -5
  39. package/out/replicache/src/persist/collect-idb-databases.d.ts.map +1 -1
  40. package/out/replicache/src/persist/collect-idb-databases.js +16 -9
  41. package/out/replicache/src/persist/collect-idb-databases.js.map +1 -1
  42. package/out/replicache/src/replicache-impl.d.ts +14 -3
  43. package/out/replicache/src/replicache-impl.d.ts.map +1 -1
  44. package/out/replicache/src/replicache-impl.js +104 -9
  45. package/out/replicache/src/replicache-impl.js.map +1 -1
  46. package/out/shared/src/arrays.js +1 -15
  47. package/out/shared/src/arrays.js.map +1 -1
  48. package/out/shared/src/hash.d.ts +1 -0
  49. package/out/shared/src/hash.d.ts.map +1 -1
  50. package/out/shared/src/hash.js +10 -28
  51. package/out/shared/src/hash.js.map +1 -1
  52. package/out/shared/src/iterables.d.ts +2 -0
  53. package/out/shared/src/iterables.d.ts.map +1 -1
  54. package/out/shared/src/iterables.js +18 -1
  55. package/out/shared/src/iterables.js.map +1 -1
  56. package/out/shared/src/json.d.ts.map +1 -1
  57. package/out/shared/src/json.js +2 -2
  58. package/out/shared/src/json.js.map +1 -1
  59. package/out/shared/src/ring-buffer.d.ts +8 -0
  60. package/out/shared/src/ring-buffer.d.ts.map +1 -1
  61. package/out/shared/src/ring-buffer.js +22 -1
  62. package/out/shared/src/ring-buffer.js.map +1 -1
  63. package/out/shared/src/xxhash32.d.ts +36 -0
  64. package/out/shared/src/xxhash32.d.ts.map +1 -0
  65. package/out/shared/src/xxhash32.js +51 -0
  66. package/out/shared/src/xxhash32.js.map +1 -0
  67. package/out/z2s/src/compiler.js +1 -1
  68. package/out/zero/package.js +1 -1
  69. package/out/zero/package.js.map +1 -1
  70. package/out/zero-cache/src/auth/read-authorizer.js +1 -1
  71. package/out/zero-cache/src/config/zero-config.d.ts +8 -0
  72. package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
  73. package/out/zero-cache/src/config/zero-config.js +28 -1
  74. package/out/zero-cache/src/config/zero-config.js.map +1 -1
  75. package/out/zero-cache/src/custom-queries/transform-query.js +1 -1
  76. package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
  77. package/out/zero-cache/src/server/change-streamer.js +11 -3
  78. package/out/zero-cache/src/server/change-streamer.js.map +1 -1
  79. package/out/zero-cache/src/server/inspector-delegate.d.ts +18 -2
  80. package/out/zero-cache/src/server/inspector-delegate.d.ts.map +1 -1
  81. package/out/zero-cache/src/server/inspector-delegate.js +23 -9
  82. package/out/zero-cache/src/server/inspector-delegate.js.map +1 -1
  83. package/out/zero-cache/src/server/main.d.ts.map +1 -1
  84. package/out/zero-cache/src/server/main.js +8 -1
  85. package/out/zero-cache/src/server/main.js.map +1 -1
  86. package/out/zero-cache/src/server/mutator.js +2 -0
  87. package/out/zero-cache/src/server/mutator.js.map +1 -1
  88. package/out/zero-cache/src/server/reaper.d.ts.map +1 -1
  89. package/out/zero-cache/src/server/reaper.js +2 -0
  90. package/out/zero-cache/src/server/reaper.js.map +1 -1
  91. package/out/zero-cache/src/server/replicator.d.ts.map +1 -1
  92. package/out/zero-cache/src/server/replicator.js +2 -0
  93. package/out/zero-cache/src/server/replicator.js.map +1 -1
  94. package/out/zero-cache/src/server/runner/zero-dispatcher.d.ts.map +1 -1
  95. package/out/zero-cache/src/server/runner/zero-dispatcher.js +3 -0
  96. package/out/zero-cache/src/server/runner/zero-dispatcher.js.map +1 -1
  97. package/out/zero-cache/src/server/syncer.d.ts.map +1 -1
  98. package/out/zero-cache/src/server/syncer.js +2 -0
  99. package/out/zero-cache/src/server/syncer.js.map +1 -1
  100. package/out/zero-cache/src/services/analyze.d.ts.map +1 -1
  101. package/out/zero-cache/src/services/analyze.js +3 -2
  102. package/out/zero-cache/src/services/analyze.js.map +1 -1
  103. package/out/zero-cache/src/services/change-source/common/backfill-manager.d.ts.map +1 -1
  104. package/out/zero-cache/src/services/change-source/common/backfill-manager.js +14 -1
  105. package/out/zero-cache/src/services/change-source/common/backfill-manager.js.map +1 -1
  106. package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.d.ts.map +1 -1
  107. package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.js +15 -3
  108. package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.js.map +1 -1
  109. package/out/zero-cache/src/services/change-source/pg/initial-sync.js +1 -1
  110. package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts +3 -0
  111. package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts.map +1 -1
  112. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +3 -1
  113. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
  114. package/out/zero-cache/src/services/mutagen/mutagen.d.ts +5 -0
  115. package/out/zero-cache/src/services/mutagen/mutagen.d.ts.map +1 -1
  116. package/out/zero-cache/src/services/mutagen/mutagen.js +7 -1
  117. package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
  118. package/out/zero-cache/src/services/mutagen/pusher.js +3 -2
  119. package/out/zero-cache/src/services/mutagen/pusher.js.map +1 -1
  120. package/out/zero-cache/src/services/profz.d.ts +7 -0
  121. package/out/zero-cache/src/services/profz.d.ts.map +1 -0
  122. package/out/zero-cache/src/services/profz.js +103 -0
  123. package/out/zero-cache/src/services/profz.js.map +1 -0
  124. package/out/zero-cache/src/services/run-ast.d.ts +12 -0
  125. package/out/zero-cache/src/services/run-ast.d.ts.map +1 -1
  126. package/out/zero-cache/src/services/run-ast.js +27 -15
  127. package/out/zero-cache/src/services/run-ast.js.map +1 -1
  128. package/out/zero-cache/src/services/runner.d.ts.map +1 -1
  129. package/out/zero-cache/src/services/runner.js +1 -1
  130. package/out/zero-cache/src/services/runner.js.map +1 -1
  131. package/out/zero-cache/src/services/view-syncer/cvr.d.ts +45 -0
  132. package/out/zero-cache/src/services/view-syncer/cvr.d.ts.map +1 -1
  133. package/out/zero-cache/src/services/view-syncer/cvr.js +133 -1
  134. package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
  135. package/out/zero-cache/src/services/view-syncer/drain-coordinator.d.ts +14 -1
  136. package/out/zero-cache/src/services/view-syncer/drain-coordinator.d.ts.map +1 -1
  137. package/out/zero-cache/src/services/view-syncer/drain-coordinator.js +30 -4
  138. package/out/zero-cache/src/services/view-syncer/drain-coordinator.js.map +1 -1
  139. package/out/zero-cache/src/services/view-syncer/hydration-budget.d.ts +33 -0
  140. package/out/zero-cache/src/services/view-syncer/hydration-budget.d.ts.map +1 -0
  141. package/out/zero-cache/src/services/view-syncer/hydration-budget.js +62 -0
  142. package/out/zero-cache/src/services/view-syncer/hydration-budget.js.map +1 -0
  143. package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.d.ts +51 -0
  144. package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.d.ts.map +1 -0
  145. package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.js +78 -0
  146. package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.js.map +1 -0
  147. package/out/zero-cache/src/services/view-syncer/inspect-handler.d.ts +1 -1
  148. package/out/zero-cache/src/services/view-syncer/inspect-handler.d.ts.map +1 -1
  149. package/out/zero-cache/src/services/view-syncer/inspect-handler.js +2 -2
  150. package/out/zero-cache/src/services/view-syncer/inspect-handler.js.map +1 -1
  151. package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts +2 -1
  152. package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts.map +1 -1
  153. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +33 -18
  154. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
  155. package/out/zero-cache/src/services/view-syncer/query-covering.d.ts.map +1 -1
  156. package/out/zero-cache/src/services/view-syncer/query-covering.js.map +1 -1
  157. package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts +4 -1
  158. package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts.map +1 -1
  159. package/out/zero-cache/src/services/view-syncer/snapshotter.js +9 -3
  160. package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
  161. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts +11 -1
  162. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
  163. package/out/zero-cache/src/services/view-syncer/view-syncer.js +470 -90
  164. package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
  165. package/out/zero-cache/src/types/processes.d.ts +32 -0
  166. package/out/zero-cache/src/types/processes.d.ts.map +1 -1
  167. package/out/zero-cache/src/types/processes.js +43 -4
  168. package/out/zero-cache/src/types/processes.js.map +1 -1
  169. package/out/zero-cache/src/types/profiler.d.ts +12 -0
  170. package/out/zero-cache/src/types/profiler.d.ts.map +1 -1
  171. package/out/zero-cache/src/types/profiler.js +46 -1
  172. package/out/zero-cache/src/types/profiler.js.map +1 -1
  173. package/out/zero-cache/src/workers/syncer-ws-message-handler.d.ts.map +1 -1
  174. package/out/zero-cache/src/workers/syncer-ws-message-handler.js +21 -16
  175. package/out/zero-cache/src/workers/syncer-ws-message-handler.js.map +1 -1
  176. package/out/zero-cache/src/workers/syncer.d.ts.map +1 -1
  177. package/out/zero-cache/src/workers/syncer.js +10 -1
  178. package/out/zero-cache/src/workers/syncer.js.map +1 -1
  179. package/out/zero-client/src/client/connection-manager.d.ts +20 -0
  180. package/out/zero-client/src/client/connection-manager.d.ts.map +1 -1
  181. package/out/zero-client/src/client/connection-manager.js +32 -14
  182. package/out/zero-client/src/client/connection-manager.js.map +1 -1
  183. package/out/zero-client/src/client/connection-status-enum.d.ts +2 -0
  184. package/out/zero-client/src/client/connection-status-enum.d.ts.map +1 -1
  185. package/out/zero-client/src/client/connection-status-enum.js +3 -1
  186. package/out/zero-client/src/client/connection-status-enum.js.map +1 -1
  187. package/out/zero-client/src/client/connection.d.ts +6 -0
  188. package/out/zero-client/src/client/connection.d.ts.map +1 -1
  189. package/out/zero-client/src/client/connection.js +2 -1
  190. package/out/zero-client/src/client/connection.js.map +1 -1
  191. package/out/zero-client/src/client/context.d.ts +22 -12
  192. package/out/zero-client/src/client/context.d.ts.map +1 -1
  193. package/out/zero-client/src/client/context.js +92 -25
  194. package/out/zero-client/src/client/context.js.map +1 -1
  195. package/out/zero-client/src/client/custom.js +2 -2
  196. package/out/zero-client/src/client/custom.js.map +1 -1
  197. package/out/zero-client/src/client/error.js +1 -1
  198. package/out/zero-client/src/client/ivm-branch.d.ts.map +1 -1
  199. package/out/zero-client/src/client/ivm-branch.js +20 -3
  200. package/out/zero-client/src/client/ivm-branch.js.map +1 -1
  201. package/out/zero-client/src/client/mutator-proxy.d.ts.map +1 -1
  202. package/out/zero-client/src/client/mutator-proxy.js +2 -1
  203. package/out/zero-client/src/client/mutator-proxy.js.map +1 -1
  204. package/out/zero-client/src/client/options.d.ts +3 -0
  205. package/out/zero-client/src/client/options.d.ts.map +1 -1
  206. package/out/zero-client/src/client/options.js.map +1 -1
  207. package/out/zero-client/src/client/query-manager.d.ts +2 -0
  208. package/out/zero-client/src/client/query-manager.d.ts.map +1 -1
  209. package/out/zero-client/src/client/query-manager.js +48 -9
  210. package/out/zero-client/src/client/query-manager.js.map +1 -1
  211. package/out/zero-client/src/client/version.js +1 -1
  212. package/out/zero-client/src/client/zero-rep.d.ts.map +1 -1
  213. package/out/zero-client/src/client/zero-rep.js +1 -1
  214. package/out/zero-client/src/client/zero-rep.js.map +1 -1
  215. package/out/zero-client/src/client/zero.d.ts +18 -3
  216. package/out/zero-client/src/client/zero.d.ts.map +1 -1
  217. package/out/zero-client/src/client/zero.js +61 -14
  218. package/out/zero-client/src/client/zero.js.map +1 -1
  219. package/out/zero-client/src/types/query-result.d.ts +2 -0
  220. package/out/zero-client/src/types/query-result.d.ts.map +1 -1
  221. package/out/zero-protocol/src/ast.d.ts +64 -1
  222. package/out/zero-protocol/src/ast.d.ts.map +1 -1
  223. package/out/zero-protocol/src/ast.js +223 -51
  224. package/out/zero-protocol/src/ast.js.map +1 -1
  225. package/out/zero-protocol/src/error.d.ts +1 -1
  226. package/out/zero-protocol/src/query-hash-visitor.d.ts +81 -0
  227. package/out/zero-protocol/src/query-hash-visitor.d.ts.map +1 -0
  228. package/out/zero-protocol/src/query-hash-visitor.js +324 -0
  229. package/out/zero-protocol/src/query-hash-visitor.js.map +1 -0
  230. package/out/zero-protocol/src/query-hash.d.ts +7 -2
  231. package/out/zero-protocol/src/query-hash.d.ts.map +1 -1
  232. package/out/zero-protocol/src/query-hash.js +12 -9
  233. package/out/zero-protocol/src/query-hash.js.map +1 -1
  234. package/out/zero-react/src/use-query.d.ts +5 -4
  235. package/out/zero-react/src/use-query.d.ts.map +1 -1
  236. package/out/zero-react/src/use-query.js +86 -9
  237. package/out/zero-react/src/use-query.js.map +1 -1
  238. package/out/zero-solid/src/solid-view.d.ts +17 -0
  239. package/out/zero-solid/src/solid-view.d.ts.map +1 -1
  240. package/out/zero-solid/src/solid-view.js +39 -1
  241. package/out/zero-solid/src/solid-view.js.map +1 -1
  242. package/out/zero-solid/src/use-query.js +5 -3
  243. package/out/zero-solid/src/use-query.js.map +1 -1
  244. package/out/zql/src/builder/debug-delegate.d.ts +6 -1
  245. package/out/zql/src/builder/debug-delegate.d.ts.map +1 -1
  246. package/out/zql/src/builder/debug-delegate.js +14 -2
  247. package/out/zql/src/builder/debug-delegate.js.map +1 -1
  248. package/out/zql/src/ivm/array-view.d.ts +29 -0
  249. package/out/zql/src/ivm/array-view.d.ts.map +1 -1
  250. package/out/zql/src/ivm/array-view.js +58 -9
  251. package/out/zql/src/ivm/array-view.js.map +1 -1
  252. package/out/zql/src/ivm/cap.d.ts.map +1 -1
  253. package/out/zql/src/ivm/cap.js +5 -3
  254. package/out/zql/src/ivm/cap.js.map +1 -1
  255. package/out/zql/src/ivm/deferred-input.d.ts.map +1 -1
  256. package/out/zql/src/ivm/deferred-input.js +3 -2
  257. package/out/zql/src/ivm/deferred-input.js.map +1 -1
  258. package/out/zql/src/ivm/exists.d.ts +1 -1
  259. package/out/zql/src/ivm/exists.d.ts.map +1 -1
  260. package/out/zql/src/ivm/exists.js +1 -1
  261. package/out/zql/src/ivm/exists.js.map +1 -1
  262. package/out/zql/src/ivm/fan-in.d.ts +2 -2
  263. package/out/zql/src/ivm/fan-in.d.ts.map +1 -1
  264. package/out/zql/src/ivm/fan-in.js +6 -5
  265. package/out/zql/src/ivm/fan-in.js.map +1 -1
  266. package/out/zql/src/ivm/fan-out.d.ts +2 -2
  267. package/out/zql/src/ivm/fan-out.d.ts.map +1 -1
  268. package/out/zql/src/ivm/fan-out.js.map +1 -1
  269. package/out/zql/src/ivm/filter-operators.d.ts +4 -4
  270. package/out/zql/src/ivm/filter-operators.d.ts.map +1 -1
  271. package/out/zql/src/ivm/filter-operators.js +10 -8
  272. package/out/zql/src/ivm/filter-operators.js.map +1 -1
  273. package/out/zql/src/ivm/filter.d.ts +2 -2
  274. package/out/zql/src/ivm/filter.d.ts.map +1 -1
  275. package/out/zql/src/ivm/filter.js +7 -4
  276. package/out/zql/src/ivm/filter.js.map +1 -1
  277. package/out/zql/src/ivm/flipped-join.js +2 -2
  278. package/out/zql/src/ivm/flipped-join.js.map +1 -1
  279. package/out/zql/src/ivm/maybe-split-and-push-edit-change.d.ts +1 -1
  280. package/out/zql/src/ivm/maybe-split-and-push-edit-change.d.ts.map +1 -1
  281. package/out/zql/src/ivm/maybe-split-and-push-edit-change.js +6 -4
  282. package/out/zql/src/ivm/maybe-split-and-push-edit-change.js.map +1 -1
  283. package/out/zql/src/ivm/memory-source.d.ts +15 -3
  284. package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
  285. package/out/zql/src/ivm/memory-source.js +35 -9
  286. package/out/zql/src/ivm/memory-source.js.map +1 -1
  287. package/out/zql/src/ivm/take.d.ts.map +1 -1
  288. package/out/zql/src/ivm/take.js +3 -4
  289. package/out/zql/src/ivm/take.js.map +1 -1
  290. package/out/zql/src/ivm/union-fan-in.d.ts.map +1 -1
  291. package/out/zql/src/ivm/union-fan-in.js +9 -7
  292. package/out/zql/src/ivm/union-fan-in.js.map +1 -1
  293. package/out/zql/src/ivm/view-apply-change.d.ts.map +1 -1
  294. package/out/zql/src/ivm/view-apply-change.js +3 -3
  295. package/out/zql/src/ivm/view-apply-change.js.map +1 -1
  296. package/out/zql/src/ivm/view.d.ts +15 -0
  297. package/out/zql/src/ivm/view.d.ts.map +1 -1
  298. package/out/zql/src/mutate/mutator-registry.d.ts.map +1 -1
  299. package/out/zql/src/mutate/mutator-registry.js +2 -2
  300. package/out/zql/src/mutate/mutator-registry.js.map +1 -1
  301. package/out/zql/src/query/query-delegate-base.d.ts +4 -2
  302. package/out/zql/src/query/query-delegate-base.d.ts.map +1 -1
  303. package/out/zql/src/query/query-delegate-base.js +69 -36
  304. package/out/zql/src/query/query-delegate-base.js.map +1 -1
  305. package/out/zql/src/query/query-delegate.d.ts +33 -6
  306. package/out/zql/src/query/query-delegate.d.ts.map +1 -1
  307. package/out/zql/src/query/query-impl.d.ts +25 -14
  308. package/out/zql/src/query/query-impl.d.ts.map +1 -1
  309. package/out/zql/src/query/query-impl.js +414 -94
  310. package/out/zql/src/query/query-impl.js.map +1 -1
  311. package/out/zql/src/query/query-internals.d.ts +21 -11
  312. package/out/zql/src/query/query-internals.d.ts.map +1 -1
  313. package/out/zql/src/query/query-internals.js.map +1 -1
  314. package/out/zql/src/query/query-transitions.d.ts +171 -0
  315. package/out/zql/src/query/query-transitions.d.ts.map +1 -0
  316. package/out/zql/src/query/query-transitions.js +207 -0
  317. package/out/zql/src/query/query-transitions.js.map +1 -0
  318. package/out/zql/src/query/query.d.ts +11 -2
  319. package/out/zql/src/query/query.d.ts.map +1 -1
  320. package/out/zql/src/query/runnable-query-impl.d.ts +3 -2
  321. package/out/zql/src/query/runnable-query-impl.d.ts.map +1 -1
  322. package/out/zql/src/query/runnable-query-impl.js +23 -2
  323. package/out/zql/src/query/runnable-query-impl.js.map +1 -1
  324. package/out/zql/src/query/typed-view.d.ts +1 -1
  325. package/out/zql/src/query/typed-view.d.ts.map +1 -1
  326. package/out/zqlite/src/db.d.ts +9 -1
  327. package/out/zqlite/src/db.d.ts.map +1 -1
  328. package/out/zqlite/src/db.js +8 -2
  329. package/out/zqlite/src/db.js.map +1 -1
  330. package/out/zqlite/src/table-source.d.ts +1 -0
  331. package/out/zqlite/src/table-source.d.ts.map +1 -1
  332. package/out/zqlite/src/table-source.js +10 -5
  333. package/out/zqlite/src/table-source.js.map +1 -1
  334. package/package.json +1 -1
@@ -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,7 +23,36 @@ 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;
47
+ /**
48
+ * The store holds the server-confirmed complete result of this query from a
49
+ * previous sync (the persisted got-queries key exists, pre-authoritative).
50
+ * Never downgrades 'complete'/'error', and never resolves anything that
51
+ * waits on 'complete' — freshness stays a promise only the connection can
52
+ * keep.
53
+ */
54
+ markCached(): void;
55
+ /** The got key was deleted (eviction) while still pre-authoritative. */
56
+ unmarkCached(): void;
28
57
  }
29
58
  //# sourceMappingURL=array-view.d.ts.map
@@ -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;IA4B/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;CAGnB"}
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;
@@ -61,18 +65,11 @@ var ArrayView = class {
61
65
  else if ("error" in queryComplete) {
62
66
  this.#resultType = "error";
63
67
  this.#error = queryComplete;
64
- } else queryComplete.then(() => {
65
- this.#resultType = "complete";
66
- this.#fireListeners();
67
- }).catch((e) => {
68
- this.#resultType = "error";
69
- this.#error = e;
70
- this.#fireListeners();
71
- });
68
+ } else queryComplete.then(() => this.#setResultType("complete")).catch((e) => this.#setResultType("error", e));
72
69
  this.#hydrate();
73
70
  }
74
71
  get data() {
75
- return this.#root[""];
72
+ return (this.#committedRoot ?? this.#root)[""];
76
73
  }
77
74
  #getSchema() {
78
75
  return this.#schema ??= this.#input.getSchema();
@@ -89,6 +86,10 @@ var ArrayView = class {
89
86
  for (const listener of this.#listeners) this.#fireListener(listener);
90
87
  }
91
88
  #fireListener(listener) {
89
+ if (this.#committedRoot !== void 0) {
90
+ listener(this.data, this.#heldResultType, this.#heldError);
91
+ return;
92
+ }
92
93
  listener(this.data, this.#resultType, this.#error);
93
94
  }
94
95
  destroy() {
@@ -107,7 +108,35 @@ var ArrayView = class {
107
108
  this.#root = applyChange(this.#root, changeToViewChange(change), this.#getSchema(), "", this.#format, false, this.#txnDirty);
108
109
  return emptyArray;
109
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
+ }
110
137
  flush() {
138
+ this.#committedRoot = void 0;
139
+ this.#held = false;
111
140
  if (!this.#dirty) return;
112
141
  this.#dirty = false;
113
142
  this.#fireListeners();
@@ -116,6 +145,26 @@ var ArrayView = class {
116
145
  updateTTL(ttl) {
117
146
  this.#updateTTL(ttl);
118
147
  }
148
+ /**
149
+ * The store holds the server-confirmed complete result of this query from a
150
+ * previous sync (the persisted got-queries key exists, pre-authoritative).
151
+ * Never downgrades 'complete'/'error', and never resolves anything that
152
+ * waits on 'complete' — freshness stays a promise only the connection can
153
+ * keep.
154
+ */
155
+ markCached() {
156
+ if (this.#resultType === "unknown") this.#setResultType("cached");
157
+ }
158
+ /** The got key was deleted (eviction) while still pre-authoritative. */
159
+ unmarkCached() {
160
+ if (this.#resultType === "cached") this.#setResultType("unknown");
161
+ }
162
+ #setResultType(resultType, error) {
163
+ this.#resultType = resultType;
164
+ this.#error = error;
165
+ if (this.#held) this.#dirty = true;
166
+ else if (!this.#dirty) this.#fireListeners();
167
+ }
119
168
  };
120
169
  //#endregion
121
170
  export { ArrayView };
@@ -1 +1 @@
1
- {"version":3,"file":"array-view.js","names":["#input","#listeners","#format","#updateTTL","#root","#resultType","#error","#fireListeners","#hydrate","#schema","#fireListener","#dirty","#getSchema","#txnDirty"],"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(() => {\n this.#resultType = 'complete';\n this.#fireListeners();\n })\n .catch(e => {\n this.#resultType = 'error';\n this.#error = e;\n this.#fireListeners();\n });\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"],"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;GACV,KAAKD,cAAc;GACnB,KAAKE,eAAe;EACtB,CAAC,EACA,OAAM,MAAK;GACV,KAAKF,cAAc;GACnB,KAAKC,SAAS;GACd,KAAKC,eAAe;EACtB,CAAC;EAEL,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,KAAKJ,eAAe;EAIpB,KAAKM,4BAAY,IAAI,QAAQ;CAC/B;CAEA,UAAU,KAAU;EAClB,KAAKV,WAAW,GAAG;CACrB;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":";;;;;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;CAIT;CAGA,QAAQ;CAGR,kBAA8B;CAC9B;CACA,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,QAAQ,KAAKC,kBAAkB,KAAKL,OAAO;CAC7C;CAEA,aAA2B;EACzB,OAAQ,KAAKM,YAAY,KAAKV,OAAO,UAAU;CACjD;CAEA,YAAY,UAAuB;EACjC,OAAO,CAAC,KAAKC,WAAW,IAAI,QAAQ,GAAG,6BAA6B;EACpE,KAAKA,WAAW,IAAI,QAAQ;EAE5B,KAAKU,cAAc,QAAQ;EAE3B,aAAa;GACX,KAAKV,WAAW,OAAO,QAAQ;EACjC;CACF;CAEA,iBAAiB;EACf,KAAK,MAAM,YAAY,KAAKA,YAC1B,KAAKU,cAAc,QAAQ;CAE/B;CAEA,cAAc,UAAuB;EACnC,IAAI,KAAKF,mBAAmB,KAAA,GAAW;GAErC,SACE,KAAK,MACL,KAAKG,iBACL,KAAKC,UACP;GACA;EACF;EACA,SAAS,KAAK,MAAsB,KAAKR,aAAa,KAAKC,MAAM;CACnE;CAEA,UAAU;EACR,KAAK,YAAY;CACnB;CAEA,WAAW;EACT,KAAKQ,SAAS;EACd,KAAK,MAAM,QAAQ,WAAW,KAAKd,OAAO,MAAM,CAAC,CAAC,CAAC,GACjD,KAAKI,QAAQ,YACX,KAAKA,OACL;GAAC,MAAM;GAAO;EAAI,GAClB,KAAKW,WAAW,GAChB,IACA,KAAKb,SACL,OACA,IAIF;EAEF,KAAK,MAAM;CACb;CAEA,KAAK,QAAgB;EACnB,KAAKY,SAAS;EACd,KAAKV,QAAQ,YACX,KAAKA,OACL,mBAAmB,MAAM,GACzB,KAAKW,WAAW,GAChB,IACA,KAAKb,SACL,OACA,KAAKc,SACP;EACA,OAAO;CACT;;;;;;;;;;;;CAaA,WAAiB;EACf,KAAKP,iBAAiB,KAAKL;EAC3B,KAAKa,QAAQ;EACb,KAAKL,kBAAkB,KAAKP;EAC5B,KAAKQ,aAAa,KAAKP;CACzB;;;;;;;CAQA,cAAoB;EAClB,KAAKG,iBAAiB,KAAA;CACxB;CAEA,QAAQ;EACN,KAAKA,iBAAiB,KAAA;EACtB,KAAKQ,QAAQ;EACb,IAAI,CAAC,KAAKH,QACR;EAEF,KAAKA,SAAS;EACd,KAAKI,eAAe;EAIpB,KAAKF,4BAAY,IAAI,QAAQ;CAC/B;CAEA,UAAU,KAAU;EAClB,KAAKb,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;EAOd,IAAI,KAAKW,OAEP,KAAKH,SAAS;OACT,IAAI,CAAC,KAAKA,QACf,KAAKI,eAAe;CAExB;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"cap.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/cap.ts"],"names":[],"mappings":"AAKA,OAAO,EAAgB,KAAK,MAAM,EAAkB,MAAM,aAAa,CAAC;AAExE,OAAO,KAAK,EAAa,IAAI,EAAC,MAAM,WAAW,CAAC;AAChD,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,KAAK,EACV,KAAK,QAAQ,EACb,KAAK,MAAM,EACX,KAAK,OAAO,EACb,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAC,KAAK,MAAM,EAAC,MAAM,aAAa,CAAC;AACxC,OAAO,EAGL,KAAK,YAAY,EAClB,MAAM,WAAW,CAAC;AAanB;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,GAAI,YAAW,QAAQ;;gBAWhC,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,EACb,YAAY,CAAC,EAAE,YAAY;IAa7B,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAI/B,SAAS,IAAI,YAAY;IAIxB,KAAK,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAAC,IAAI,GAAG,OAAO,CAAC;IA0FhD,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC;IAsHtC,OAAO,IAAI,IAAI;CAGhB"}
1
+ {"version":3,"file":"cap.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/cap.ts"],"names":[],"mappings":"AAMA,OAAO,EAAgB,KAAK,MAAM,EAAkB,MAAM,aAAa,CAAC;AAExE,OAAO,KAAK,EAAa,IAAI,EAAC,MAAM,WAAW,CAAC;AAChD,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,KAAK,EACV,KAAK,QAAQ,EACb,KAAK,MAAM,EACX,KAAK,OAAO,EACb,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAC,KAAK,MAAM,EAAC,MAAM,aAAa,CAAC;AACxC,OAAO,EAGL,KAAK,YAAY,EAClB,MAAM,WAAW,CAAC;AAanB;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,GAAI,YAAW,QAAQ;;gBAWhC,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,EACb,YAAY,CAAC,EAAE,YAAY;IAa7B,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAI/B,SAAS,IAAI,YAAY;IAIxB,KAAK,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAAC,IAAI,GAAG,OAAO,CAAC;IA0FhD,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC;IAuHtC,OAAO,IAAI,IAAI;CAGhB"}
@@ -1,4 +1,5 @@
1
1
  import { assert } from "../../../shared/src/asserts.js";
2
+ import { emptyArray } from "../../../shared/src/sentinels.js";
2
3
  import { throwOutput } from "./operator.js";
3
4
  import { makeAddChange } from "./change.js";
4
5
  import { constraintMatchesPartitionKey, makePartitionKeyComparator } from "./take.js";
@@ -163,11 +164,11 @@ var Cap = class {
163
164
  if (new Set(capState.pks).has(pk)) yield* this.#output.push(change, this);
164
165
  }
165
166
  }
166
- *#pushEditChange(change) {
167
+ #pushEditChange(change) {
167
168
  assert(!this.#partitionKeyComparator || this.#partitionKeyComparator(change[2].row, change[1].row) === 0, "Unexpected change of partition key");
168
169
  const capStateKey = getCapStateKey(this.#partitionKey, change[2].row);
169
170
  const capState = this.#storage.get(capStateKey);
170
- if (!capState) return;
171
+ if (!capState) return emptyArray;
171
172
  const oldPK = serializePK(change[2].row, this.#primaryKey);
172
173
  if (new Set(capState.pks).has(oldPK)) {
173
174
  const newPK = serializePK(change[1].row, this.#primaryKey);
@@ -178,8 +179,9 @@ var Cap = class {
178
179
  pks
179
180
  });
180
181
  }
181
- yield* this.#output.push(change, this);
182
+ return this.#output.push(change, this);
182
183
  }
184
+ return emptyArray;
183
185
  }
184
186
  destroy() {
185
187
  this.#input.destroy();
@@ -1 +1 @@
1
- {"version":3,"file":"cap.js","names":["#input","#storage","#limit","#partitionKey","#partitionKeyComparator","#primaryKey","#output","#initialFetch","#pushEditChange"],"sources":["../../../../../zql/src/ivm/cap.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport type {Row, Value} from '../../../zero-protocol/src/data.ts';\nimport type {PrimaryKey} from '../../../zero-protocol/src/primary-key.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport {ChangeType} from './change-type.ts';\nimport {makeAddChange, type Change, type EditChange} from './change.ts';\nimport type {Constraint} from './constraint.ts';\nimport type {Comparator, Node} from './data.ts';\nimport {\n throwOutput,\n type FetchRequest,\n type Input,\n type Operator,\n type Output,\n type Storage,\n} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport {type Stream} from './stream.ts';\nimport {\n constraintMatchesPartitionKey,\n makePartitionKeyComparator,\n type PartitionKey,\n} from './take.ts';\n\ntype CapState = {\n size: number;\n pks: string[];\n};\n\ninterface CapStorage {\n get(key: string): CapState | undefined;\n set(key: string, value: CapState): void;\n del(key: string): void;\n}\n\n/**\n * The Cap operator is a count-based limiter for EXISTS subqueries that\n * does not require ordering. Unlike Take, it tracks membership by primary\n * key set rather than by a sorted bound. This means:\n *\n * - No comparator needed (no ordering requirement)\n * - No `start` or `reverse` fetch support\n * - No `#rowHiddenFromFetch` complexity (we can defer when adding to the pk set)\n *\n * Cap is used in EXISTS child pipelines where only the count of matching\n * rows matters, not their order. This allows SQLite to skip ORDER BY\n * entirely, enabling much faster query plans.\n *\n * Cap can count rows globally or by unique value of some partition key\n * (same as Take).\n */\nexport class Cap implements Operator {\n readonly #input: Input;\n readonly #storage: CapStorage;\n readonly #limit: number;\n readonly #partitionKey: PartitionKey | undefined;\n readonly #partitionKeyComparator: Comparator | undefined;\n readonly #primaryKey: PrimaryKey;\n\n #output: Output = throwOutput;\n\n constructor(\n input: Input,\n storage: Storage,\n limit: number,\n partitionKey?: PartitionKey,\n ) {\n assert(limit >= 0, 'Limit must be non-negative');\n input.setOutput(this);\n this.#input = input;\n this.#storage = storage as CapStorage;\n this.#limit = limit;\n this.#partitionKey = partitionKey;\n this.#partitionKeyComparator =\n partitionKey && makePartitionKeyComparator(partitionKey);\n this.#primaryKey = input.getSchema().primaryKey;\n }\n\n setOutput(output: Output): void {\n this.#output = output;\n }\n\n getSchema(): SourceSchema {\n return this.#input.getSchema();\n }\n\n *fetch(req: FetchRequest): Stream<Node | 'yield'> {\n assert(!req.start, 'Cap does not support start');\n assert(!req.reverse, 'Cap does not support reverse');\n\n // Cap is only built for non-flipped EXISTS subqueries, whose only\n // downstream consumer is a Join that always fetches with a constraint\n // built from the correlation's childField — which is Cap's partition\n // key. So either partitionKey is undefined, or constraint matches.\n assert(\n !this.#partitionKey ||\n (req.constraint !== undefined &&\n constraintMatchesPartitionKey(req.constraint, this.#partitionKey)),\n 'Cap fetch: constraint must match partition key when partitioned',\n );\n\n const capStateKey = getCapStateKey(this.#partitionKey, req.constraint);\n const capState = this.#storage.get(capStateKey);\n if (!capState) {\n yield* this.#initialFetch(req);\n return;\n }\n if (capState.size === 0) {\n return;\n }\n // PK-based point lookups: fetch each tracked row by its PK directly,\n // rather than scanning the partition and filtering.\n for (const pk of capState.pks) {\n const constraint = deserializePKToConstraint(pk, this.#primaryKey);\n for (const inputNode of this.#input.fetch({constraint})) {\n if (inputNode === 'yield') {\n yield inputNode;\n continue;\n }\n yield inputNode;\n }\n }\n }\n\n *#initialFetch(req: FetchRequest): Stream<Node | 'yield'> {\n if (this.#limit === 0) {\n return;\n }\n\n assert(\n constraintMatchesPartitionKey(req.constraint, this.#partitionKey),\n 'Constraint should match partition key',\n );\n\n const capStateKey = getCapStateKey(this.#partitionKey, req.constraint);\n assert(\n this.#storage.get(capStateKey) === undefined,\n 'Cap state should be undefined',\n );\n\n let size = 0;\n const pks: string[] = [];\n let downstreamEarlyReturn = true;\n let exceptionThrown = false;\n try {\n for (const inputNode of this.#input.fetch(req)) {\n if (inputNode === 'yield') {\n yield 'yield';\n continue;\n }\n yield inputNode;\n pks.push(serializePK(inputNode.row, this.#primaryKey));\n size++;\n if (size === this.#limit) {\n break;\n }\n }\n downstreamEarlyReturn = false;\n } catch (e) {\n exceptionThrown = true;\n throw e;\n } finally {\n if (!exceptionThrown) {\n this.#storage.set(capStateKey, {size, pks});\n // If it becomes necessary to support downstream early return, this\n // assert should be removed, and replaced with code that consumes\n // the input stream until limit is reached or the input stream is\n // exhausted so that capState is properly hydrated.\n assert(\n !downstreamEarlyReturn,\n 'Unexpected early return prevented full hydration',\n );\n }\n }\n }\n\n *push(change: Change): Stream<'yield'> {\n if (change[ChangeIndex.TYPE] === ChangeType.EDIT) {\n yield* this.#pushEditChange(change);\n return;\n }\n\n const capStateKey = getCapStateKey(\n this.#partitionKey,\n change[ChangeIndex.NODE].row,\n );\n const capState = this.#storage.get(capStateKey);\n if (!capState) {\n return;\n }\n\n const pk = serializePK(change[ChangeIndex.NODE].row, this.#primaryKey);\n\n if (change[ChangeIndex.TYPE] === ChangeType.ADD) {\n if (capState.size < this.#limit) {\n const pks = [...capState.pks, pk];\n this.#storage.set(capStateKey, {size: capState.size + 1, pks});\n yield* this.#output.push(change, this);\n return;\n }\n // Full — drop\n return;\n } else if (change[ChangeIndex.TYPE] === ChangeType.REMOVE) {\n const pkIndex = capState.pks.indexOf(pk);\n if (pkIndex === -1) {\n // Not in our set — drop\n return;\n }\n // Remove from set\n const pks = [...capState.pks];\n pks.splice(pkIndex, 1);\n const newSize = capState.size - 1;\n\n // Try to refill: fetch from input with partition constraint,\n // find first row NOT in PK set\n const pkSet = new Set(pks);\n const constraint = this.#partitionKey\n ? (Object.fromEntries(\n this.#partitionKey.map(\n key => [key, change[ChangeIndex.NODE].row[key]] as const,\n ),\n ) as Constraint)\n : undefined;\n\n let replacement: Node | undefined;\n for (const node of this.#input.fetch({constraint})) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n const nodePK = serializePK(node.row, this.#primaryKey);\n if (!pkSet.has(nodePK)) {\n replacement = node;\n break;\n }\n }\n\n if (replacement) {\n // Store state WITHOUT replacement during remove forward,\n // matching Take's pattern of hiding in-flight changes from re-fetches.\n this.#storage.set(capStateKey, {size: newSize, pks});\n yield* this.#output.push(change, this);\n // Now add replacement to set and forward the add.\n const replacementPK = serializePK(replacement.row, this.#primaryKey);\n pks.push(replacementPK);\n this.#storage.set(capStateKey, {size: newSize + 1, pks});\n yield* this.#output.push(makeAddChange(replacement), this);\n } else {\n this.#storage.set(capStateKey, {size: newSize, pks});\n yield* this.#output.push(change, this);\n }\n } else if (change[ChangeIndex.TYPE] === ChangeType.CHILD) {\n const pkSet = new Set(capState.pks);\n if (pkSet.has(pk)) {\n yield* this.#output.push(change, this);\n }\n }\n }\n\n *#pushEditChange(change: EditChange): Stream<'yield'> {\n assert(\n !this.#partitionKeyComparator ||\n this.#partitionKeyComparator(\n change[ChangeIndex.OLD_NODE].row,\n change[ChangeIndex.NODE].row,\n ) === 0,\n 'Unexpected change of partition key',\n );\n const capStateKey = getCapStateKey(\n this.#partitionKey,\n change[ChangeIndex.OLD_NODE].row,\n );\n const capState = this.#storage.get(capStateKey);\n if (!capState) {\n return;\n }\n\n const oldPK = serializePK(\n change[ChangeIndex.OLD_NODE].row,\n this.#primaryKey,\n );\n const pkSet = new Set(capState.pks);\n if (pkSet.has(oldPK)) {\n // Update the PK in our set if it changed\n const newPK = serializePK(change[ChangeIndex.NODE].row, this.#primaryKey);\n if (oldPK !== newPK) {\n const pks = capState.pks.map(p => (p === oldPK ? newPK : p));\n this.#storage.set(capStateKey, {size: capState.size, pks});\n }\n yield* this.#output.push(change, this);\n }\n // If not in our set, drop\n }\n\n destroy(): void {\n this.#input.destroy();\n }\n}\n\nfunction getCapStateKey(\n partitionKey: PartitionKey | undefined,\n rowOrConstraint: Row | Constraint | undefined,\n): string {\n const partitionValues: Value[] = [];\n\n if (partitionKey && rowOrConstraint) {\n for (const key of partitionKey) {\n partitionValues.push(rowOrConstraint[key]);\n }\n }\n\n return JSON.stringify(['cap', ...partitionValues]);\n}\n\nfunction serializePK(row: Row, primaryKey: PrimaryKey): string {\n return JSON.stringify(primaryKey.map(k => row[k]));\n}\n\nfunction deserializePKToConstraint(\n pk: string,\n primaryKey: PrimaryKey,\n): Constraint {\n const values = JSON.parse(pk) as Value[];\n const constraint: Record<string, Value> = {};\n for (let i = 0; i < primaryKey.length; i++) {\n constraint[primaryKey[i]] = values[i];\n }\n return constraint;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"cap.js","names":["#input","#storage","#limit","#partitionKey","#partitionKeyComparator","#primaryKey","#output","#initialFetch","#pushEditChange"],"sources":["../../../../../zql/src/ivm/cap.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport {emptyArray} from '../../../shared/src/sentinels.ts';\nimport type {Row, Value} from '../../../zero-protocol/src/data.ts';\nimport type {PrimaryKey} from '../../../zero-protocol/src/primary-key.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport {ChangeType} from './change-type.ts';\nimport {makeAddChange, type Change, type EditChange} from './change.ts';\nimport type {Constraint} from './constraint.ts';\nimport type {Comparator, Node} from './data.ts';\nimport {\n throwOutput,\n type FetchRequest,\n type Input,\n type Operator,\n type Output,\n type Storage,\n} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport {type Stream} from './stream.ts';\nimport {\n constraintMatchesPartitionKey,\n makePartitionKeyComparator,\n type PartitionKey,\n} from './take.ts';\n\ntype CapState = {\n size: number;\n pks: string[];\n};\n\ninterface CapStorage {\n get(key: string): CapState | undefined;\n set(key: string, value: CapState): void;\n del(key: string): void;\n}\n\n/**\n * The Cap operator is a count-based limiter for EXISTS subqueries that\n * does not require ordering. Unlike Take, it tracks membership by primary\n * key set rather than by a sorted bound. This means:\n *\n * - No comparator needed (no ordering requirement)\n * - No `start` or `reverse` fetch support\n * - No `#rowHiddenFromFetch` complexity (we can defer when adding to the pk set)\n *\n * Cap is used in EXISTS child pipelines where only the count of matching\n * rows matters, not their order. This allows SQLite to skip ORDER BY\n * entirely, enabling much faster query plans.\n *\n * Cap can count rows globally or by unique value of some partition key\n * (same as Take).\n */\nexport class Cap implements Operator {\n readonly #input: Input;\n readonly #storage: CapStorage;\n readonly #limit: number;\n readonly #partitionKey: PartitionKey | undefined;\n readonly #partitionKeyComparator: Comparator | undefined;\n readonly #primaryKey: PrimaryKey;\n\n #output: Output = throwOutput;\n\n constructor(\n input: Input,\n storage: Storage,\n limit: number,\n partitionKey?: PartitionKey,\n ) {\n assert(limit >= 0, 'Limit must be non-negative');\n input.setOutput(this);\n this.#input = input;\n this.#storage = storage as CapStorage;\n this.#limit = limit;\n this.#partitionKey = partitionKey;\n this.#partitionKeyComparator =\n partitionKey && makePartitionKeyComparator(partitionKey);\n this.#primaryKey = input.getSchema().primaryKey;\n }\n\n setOutput(output: Output): void {\n this.#output = output;\n }\n\n getSchema(): SourceSchema {\n return this.#input.getSchema();\n }\n\n *fetch(req: FetchRequest): Stream<Node | 'yield'> {\n assert(!req.start, 'Cap does not support start');\n assert(!req.reverse, 'Cap does not support reverse');\n\n // Cap is only built for non-flipped EXISTS subqueries, whose only\n // downstream consumer is a Join that always fetches with a constraint\n // built from the correlation's childField — which is Cap's partition\n // key. So either partitionKey is undefined, or constraint matches.\n assert(\n !this.#partitionKey ||\n (req.constraint !== undefined &&\n constraintMatchesPartitionKey(req.constraint, this.#partitionKey)),\n 'Cap fetch: constraint must match partition key when partitioned',\n );\n\n const capStateKey = getCapStateKey(this.#partitionKey, req.constraint);\n const capState = this.#storage.get(capStateKey);\n if (!capState) {\n yield* this.#initialFetch(req);\n return;\n }\n if (capState.size === 0) {\n return;\n }\n // PK-based point lookups: fetch each tracked row by its PK directly,\n // rather than scanning the partition and filtering.\n for (const pk of capState.pks) {\n const constraint = deserializePKToConstraint(pk, this.#primaryKey);\n for (const inputNode of this.#input.fetch({constraint})) {\n if (inputNode === 'yield') {\n yield inputNode;\n continue;\n }\n yield inputNode;\n }\n }\n }\n\n *#initialFetch(req: FetchRequest): Stream<Node | 'yield'> {\n if (this.#limit === 0) {\n return;\n }\n\n assert(\n constraintMatchesPartitionKey(req.constraint, this.#partitionKey),\n 'Constraint should match partition key',\n );\n\n const capStateKey = getCapStateKey(this.#partitionKey, req.constraint);\n assert(\n this.#storage.get(capStateKey) === undefined,\n 'Cap state should be undefined',\n );\n\n let size = 0;\n const pks: string[] = [];\n let downstreamEarlyReturn = true;\n let exceptionThrown = false;\n try {\n for (const inputNode of this.#input.fetch(req)) {\n if (inputNode === 'yield') {\n yield 'yield';\n continue;\n }\n yield inputNode;\n pks.push(serializePK(inputNode.row, this.#primaryKey));\n size++;\n if (size === this.#limit) {\n break;\n }\n }\n downstreamEarlyReturn = false;\n } catch (e) {\n exceptionThrown = true;\n throw e;\n } finally {\n if (!exceptionThrown) {\n this.#storage.set(capStateKey, {size, pks});\n // If it becomes necessary to support downstream early return, this\n // assert should be removed, and replaced with code that consumes\n // the input stream until limit is reached or the input stream is\n // exhausted so that capState is properly hydrated.\n assert(\n !downstreamEarlyReturn,\n 'Unexpected early return prevented full hydration',\n );\n }\n }\n }\n\n *push(change: Change): Stream<'yield'> {\n if (change[ChangeIndex.TYPE] === ChangeType.EDIT) {\n yield* this.#pushEditChange(change);\n return;\n }\n\n const capStateKey = getCapStateKey(\n this.#partitionKey,\n change[ChangeIndex.NODE].row,\n );\n const capState = this.#storage.get(capStateKey);\n if (!capState) {\n return;\n }\n\n const pk = serializePK(change[ChangeIndex.NODE].row, this.#primaryKey);\n\n if (change[ChangeIndex.TYPE] === ChangeType.ADD) {\n if (capState.size < this.#limit) {\n const pks = [...capState.pks, pk];\n this.#storage.set(capStateKey, {size: capState.size + 1, pks});\n yield* this.#output.push(change, this);\n return;\n }\n // Full — drop\n return;\n } else if (change[ChangeIndex.TYPE] === ChangeType.REMOVE) {\n const pkIndex = capState.pks.indexOf(pk);\n if (pkIndex === -1) {\n // Not in our set — drop\n return;\n }\n // Remove from set\n const pks = [...capState.pks];\n pks.splice(pkIndex, 1);\n const newSize = capState.size - 1;\n\n // Try to refill: fetch from input with partition constraint,\n // find first row NOT in PK set\n const pkSet = new Set(pks);\n const constraint = this.#partitionKey\n ? (Object.fromEntries(\n this.#partitionKey.map(\n key => [key, change[ChangeIndex.NODE].row[key]] as const,\n ),\n ) as Constraint)\n : undefined;\n\n let replacement: Node | undefined;\n for (const node of this.#input.fetch({constraint})) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n const nodePK = serializePK(node.row, this.#primaryKey);\n if (!pkSet.has(nodePK)) {\n replacement = node;\n break;\n }\n }\n\n if (replacement) {\n // Store state WITHOUT replacement during remove forward,\n // matching Take's pattern of hiding in-flight changes from re-fetches.\n this.#storage.set(capStateKey, {size: newSize, pks});\n yield* this.#output.push(change, this);\n // Now add replacement to set and forward the add.\n const replacementPK = serializePK(replacement.row, this.#primaryKey);\n pks.push(replacementPK);\n this.#storage.set(capStateKey, {size: newSize + 1, pks});\n yield* this.#output.push(makeAddChange(replacement), this);\n } else {\n this.#storage.set(capStateKey, {size: newSize, pks});\n yield* this.#output.push(change, this);\n }\n } else if (change[ChangeIndex.TYPE] === ChangeType.CHILD) {\n const pkSet = new Set(capState.pks);\n if (pkSet.has(pk)) {\n yield* this.#output.push(change, this);\n }\n }\n }\n\n #pushEditChange(change: EditChange): Stream<'yield'> {\n assert(\n !this.#partitionKeyComparator ||\n this.#partitionKeyComparator(\n change[ChangeIndex.OLD_NODE].row,\n change[ChangeIndex.NODE].row,\n ) === 0,\n 'Unexpected change of partition key',\n );\n const capStateKey = getCapStateKey(\n this.#partitionKey,\n change[ChangeIndex.OLD_NODE].row,\n );\n const capState = this.#storage.get(capStateKey);\n if (!capState) {\n return emptyArray;\n }\n\n const oldPK = serializePK(\n change[ChangeIndex.OLD_NODE].row,\n this.#primaryKey,\n );\n const pkSet = new Set(capState.pks);\n if (pkSet.has(oldPK)) {\n // Update the PK in our set if it changed\n const newPK = serializePK(change[ChangeIndex.NODE].row, this.#primaryKey);\n if (oldPK !== newPK) {\n const pks = capState.pks.map(p => (p === oldPK ? newPK : p));\n this.#storage.set(capStateKey, {size: capState.size, pks});\n }\n return this.#output.push(change, this);\n }\n // If not in our set, drop\n return emptyArray;\n }\n\n destroy(): void {\n this.#input.destroy();\n }\n}\n\nfunction getCapStateKey(\n partitionKey: PartitionKey | undefined,\n rowOrConstraint: Row | Constraint | undefined,\n): string {\n const partitionValues: Value[] = [];\n\n if (partitionKey && rowOrConstraint) {\n for (const key of partitionKey) {\n partitionValues.push(rowOrConstraint[key]);\n }\n }\n\n return JSON.stringify(['cap', ...partitionValues]);\n}\n\nfunction serializePK(row: Row, primaryKey: PrimaryKey): string {\n return JSON.stringify(primaryKey.map(k => row[k]));\n}\n\nfunction deserializePKToConstraint(\n pk: string,\n primaryKey: PrimaryKey,\n): Constraint {\n const values = JSON.parse(pk) as Value[];\n const constraint: Record<string, Value> = {};\n for (let i = 0; i < primaryKey.length; i++) {\n constraint[primaryKey[i]] = values[i];\n }\n return constraint;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"deferred-input.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/deferred-input.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,EAAC,YAAY,EAAE,KAAK,EAAE,MAAM,EAAC,MAAM,eAAe,CAAC;AAC/D,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAU,KAAK,MAAM,EAAC,MAAM,aAAa,CAAC;AAEjD;;;;;;;;;;;;GAYG;AACH,qBAAa,aAAc,YAAW,KAAK;;IAOzC;;;;;OAKG;gBACS,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,KAAK;IAKpD,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAK/B,SAAS,IAAI,YAAY;IAIxB,KAAK,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAAC,IAAI,GAAG,OAAO,CAAC;IAMjD,OAAO,IAAI,IAAI;IAKf;;;;;;;;OAQG;IACH,MAAM,IAAI,IAAI;CAwBf"}
1
+ {"version":3,"file":"deferred-input.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/deferred-input.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,EAAC,YAAY,EAAE,KAAK,EAAE,MAAM,EAAC,MAAM,eAAe,CAAC;AAC/D,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAU,KAAK,MAAM,EAAC,MAAM,aAAa,CAAC;AAEjD;;;;;;;;;;;;GAYG;AACH,qBAAa,aAAc,YAAW,KAAK;;IAOzC;;;;;OAKG;gBACS,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,KAAK;IAKpD,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAK/B,SAAS,IAAI,YAAY;IAIzB,KAAK,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAAC,IAAI,GAAG,OAAO,CAAC;IAIhD,OAAO,IAAI,IAAI;IAKf;;;;;;;;OAQG;IACH,MAAM,IAAI,IAAI;CAwBf"}
@@ -1,4 +1,5 @@
1
1
  import { assert } from "../../../shared/src/asserts.js";
2
+ import { emptyArray } from "../../../shared/src/sentinels.js";
2
3
  import { makeAddChange } from "./change.js";
3
4
  import { consume } from "./stream.js";
4
5
  //#region ../zql/src/ivm/deferred-input.ts
@@ -44,8 +45,8 @@ var DeferredInput = class {
44
45
  getSchema() {
45
46
  return this.#schema;
46
47
  }
47
- *fetch(req) {
48
- if (this.#input) yield* this.#input.fetch(req);
48
+ fetch(req) {
49
+ return this.#input ? this.#input.fetch(req) : emptyArray;
49
50
  }
50
51
  destroy() {
51
52
  this.#destroyed = true;
@@ -1 +1 @@
1
- {"version":3,"file":"deferred-input.js","names":["#schema","#build","#input","#destroyed","#output"],"sources":["../../../../../zql/src/ivm/deferred-input.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport {makeAddChange} from './change.ts';\nimport type {Node} from './data.ts';\nimport type {FetchRequest, Input, Output} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport {consume, type Stream} from './stream.ts';\n\n/**\n * A placeholder `Input` for a view whose pipeline has not been built yet.\n *\n * Until {@link attach} is called it behaves like an empty pipeline: `fetch`\n * yields nothing and pushes never happen. When the real pipeline is attached\n * the view is hydrated with a single `fetch`, whose rows are delivered to the\n * view as add changes, so the view sees the data exactly as it would see rows\n * arriving incrementally.\n *\n * This lets `materialize()` stay synchronous and return a view immediately\n * while the expensive pipeline construction and hydration are postponed, for\n * example until the client's replica has been loaded into the IVM sources.\n */\nexport class DeferredInput implements Input {\n readonly #schema: SourceSchema;\n readonly #build: () => Input;\n #output: Output | undefined;\n #input: Input | undefined;\n #destroyed = false;\n\n /**\n * @param schema The schema the built pipeline reports. Passed in so this\n * input can answer `getSchema()` unconditionally, as the `Input` contract\n * requires, before its pipeline exists.\n * @param build Builds the real pipeline. Called once, from {@link attach}.\n */\n constructor(schema: SourceSchema, build: () => Input) {\n this.#schema = schema;\n this.#build = build;\n }\n\n get attached(): boolean {\n return this.#input !== undefined;\n }\n\n get destroyed(): boolean {\n return this.#destroyed;\n }\n\n setOutput(output: Output): void {\n this.#output = output;\n this.#input?.setOutput(output);\n }\n\n getSchema(): SourceSchema {\n return this.#schema;\n }\n\n *fetch(req: FetchRequest): Stream<Node | 'yield'> {\n if (this.#input) {\n yield* this.#input.fetch(req);\n }\n }\n\n destroy(): void {\n this.#destroyed = true;\n this.#input?.destroy();\n }\n\n /**\n * Build the real pipeline and hydrate the output with its current contents.\n *\n * Must be called inside the delegate's `batchViewUpdates`; the delegate is\n * responsible for notifying commit listeners afterwards so views flush.\n *\n * If building or hydrating throws, the pipeline is torn down again and this\n * input stays unattached so the caller can report the failure.\n */\n attach(): void {\n assert(!this.#input, 'DeferredInput: pipeline already attached');\n assert(!this.#destroyed, 'DeferredInput: cannot attach after destroy');\n const input = this.#build();\n const output = this.#output;\n if (!output) {\n // The view never asked for output; there is nothing to hydrate.\n this.#input = input;\n return;\n }\n try {\n input.setOutput(output);\n for (const node of input.fetch({})) {\n if (node === 'yield') {\n continue;\n }\n consume(output.push(makeAddChange(node), input));\n }\n } catch (e) {\n input.destroy();\n throw e;\n }\n this.#input = input;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAoBA,IAAa,gBAAb,MAA4C;CAC1C;CACA;CACA;CACA;CACA,aAAa;;;;;;;CAQb,YAAY,QAAsB,OAAoB;EACpD,KAAKA,UAAU;EACf,KAAKC,SAAS;CAChB;CAEA,IAAI,WAAoB;EACtB,OAAO,KAAKC,WAAW,KAAA;CACzB;CAEA,IAAI,YAAqB;EACvB,OAAO,KAAKC;CACd;CAEA,UAAU,QAAsB;EAC9B,KAAKC,UAAU;EACf,KAAKF,QAAQ,UAAU,MAAM;CAC/B;CAEA,YAA0B;EACxB,OAAO,KAAKF;CACd;CAEA,CAAC,MAAM,KAA2C;EAChD,IAAI,KAAKE,QACP,OAAO,KAAKA,OAAO,MAAM,GAAG;CAEhC;CAEA,UAAgB;EACd,KAAKC,aAAa;EAClB,KAAKD,QAAQ,QAAQ;CACvB;;;;;;;;;;CAWA,SAAe;EACb,OAAO,CAAC,KAAKA,QAAQ,0CAA0C;EAC/D,OAAO,CAAC,KAAKC,YAAY,4CAA4C;EACrE,MAAM,QAAQ,KAAKF,OAAO;EAC1B,MAAM,SAAS,KAAKG;EACpB,IAAI,CAAC,QAAQ;GAEX,KAAKF,SAAS;GACd;EACF;EACA,IAAI;GACF,MAAM,UAAU,MAAM;GACtB,KAAK,MAAM,QAAQ,MAAM,MAAM,CAAC,CAAC,GAAG;IAClC,IAAI,SAAS,SACX;IAEF,QAAQ,OAAO,KAAK,cAAc,IAAI,GAAG,KAAK,CAAC;GACjD;EACF,SAAS,GAAG;GACV,MAAM,QAAQ;GACd,MAAM;EACR;EACA,KAAKA,SAAS;CAChB;AACF"}
1
+ {"version":3,"file":"deferred-input.js","names":["#schema","#build","#input","#destroyed","#output"],"sources":["../../../../../zql/src/ivm/deferred-input.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport {emptyArray} from '../../../shared/src/sentinels.ts';\nimport {makeAddChange} from './change.ts';\nimport type {Node} from './data.ts';\nimport type {FetchRequest, Input, Output} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport {consume, type Stream} from './stream.ts';\n\n/**\n * A placeholder `Input` for a view whose pipeline has not been built yet.\n *\n * Until {@link attach} is called it behaves like an empty pipeline: `fetch`\n * yields nothing and pushes never happen. When the real pipeline is attached\n * the view is hydrated with a single `fetch`, whose rows are delivered to the\n * view as add changes, so the view sees the data exactly as it would see rows\n * arriving incrementally.\n *\n * This lets `materialize()` stay synchronous and return a view immediately\n * while the expensive pipeline construction and hydration are postponed, for\n * example until the client's replica has been loaded into the IVM sources.\n */\nexport class DeferredInput implements Input {\n readonly #schema: SourceSchema;\n readonly #build: () => Input;\n #output: Output | undefined;\n #input: Input | undefined;\n #destroyed = false;\n\n /**\n * @param schema The schema the built pipeline reports. Passed in so this\n * input can answer `getSchema()` unconditionally, as the `Input` contract\n * requires, before its pipeline exists.\n * @param build Builds the real pipeline. Called once, from {@link attach}.\n */\n constructor(schema: SourceSchema, build: () => Input) {\n this.#schema = schema;\n this.#build = build;\n }\n\n get attached(): boolean {\n return this.#input !== undefined;\n }\n\n get destroyed(): boolean {\n return this.#destroyed;\n }\n\n setOutput(output: Output): void {\n this.#output = output;\n this.#input?.setOutput(output);\n }\n\n getSchema(): SourceSchema {\n return this.#schema;\n }\n\n fetch(req: FetchRequest): Stream<Node | 'yield'> {\n return this.#input ? this.#input.fetch(req) : emptyArray;\n }\n\n destroy(): void {\n this.#destroyed = true;\n this.#input?.destroy();\n }\n\n /**\n * Build the real pipeline and hydrate the output with its current contents.\n *\n * Must be called inside the delegate's `batchViewUpdates`; the delegate is\n * responsible for notifying commit listeners afterwards so views flush.\n *\n * If building or hydrating throws, the pipeline is torn down again and this\n * input stays unattached so the caller can report the failure.\n */\n attach(): void {\n assert(!this.#input, 'DeferredInput: pipeline already attached');\n assert(!this.#destroyed, 'DeferredInput: cannot attach after destroy');\n const input = this.#build();\n const output = this.#output;\n if (!output) {\n // The view never asked for output; there is nothing to hydrate.\n this.#input = input;\n return;\n }\n try {\n input.setOutput(output);\n for (const node of input.fetch({})) {\n if (node === 'yield') {\n continue;\n }\n consume(output.push(makeAddChange(node), input));\n }\n } catch (e) {\n input.destroy();\n throw e;\n }\n this.#input = input;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAqBA,IAAa,gBAAb,MAA4C;CAC1C;CACA;CACA;CACA;CACA,aAAa;;;;;;;CAQb,YAAY,QAAsB,OAAoB;EACpD,KAAKA,UAAU;EACf,KAAKC,SAAS;CAChB;CAEA,IAAI,WAAoB;EACtB,OAAO,KAAKC,WAAW,KAAA;CACzB;CAEA,IAAI,YAAqB;EACvB,OAAO,KAAKC;CACd;CAEA,UAAU,QAAsB;EAC9B,KAAKC,UAAU;EACf,KAAKF,QAAQ,UAAU,MAAM;CAC/B;CAEA,YAA0B;EACxB,OAAO,KAAKF;CACd;CAEA,MAAM,KAA2C;EAC/C,OAAO,KAAKE,SAAS,KAAKA,OAAO,MAAM,GAAG,IAAI;CAChD;CAEA,UAAgB;EACd,KAAKC,aAAa;EAClB,KAAKD,QAAQ,QAAQ;CACvB;;;;;;;;;;CAWA,SAAe;EACb,OAAO,CAAC,KAAKA,QAAQ,0CAA0C;EAC/D,OAAO,CAAC,KAAKC,YAAY,4CAA4C;EACrE,MAAM,QAAQ,KAAKF,OAAO;EAC1B,MAAM,SAAS,KAAKG;EACpB,IAAI,CAAC,QAAQ;GAEX,KAAKF,SAAS;GACd;EACF;EACA,IAAI;GACF,MAAM,UAAU,MAAM;GACtB,KAAK,MAAM,QAAQ,MAAM,MAAM,CAAC,CAAC,GAAG;IAClC,IAAI,SAAS,SACX;IAEF,QAAQ,OAAO,KAAK,cAAc,IAAI,GAAG,KAAK,CAAC;GACjD;EACF,SAAS,GAAG;GACV,MAAM,QAAQ;GACd,MAAM;EACR;EACA,KAAKA,SAAS;CAChB;AACF"}
@@ -14,7 +14,7 @@ export declare class Exists implements FilterOperator {
14
14
  setFilterOutput(output: FilterOutput): void;
15
15
  beginFilter(): void;
16
16
  endFilter(): void;
17
- filter(node: Node): Generator<'yield', boolean>;
17
+ filter(node: Node): IterableIterator<'yield', boolean>;
18
18
  destroy(): void;
19
19
  getSchema(): SourceSchema;
20
20
  push(change: Change): Stream<'yield'>;
@@ -1 +1 @@
1
- {"version":3,"file":"exists.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/exists.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,mCAAmC,CAAC;AAGnE,OAAO,EAAkC,KAAK,MAAM,EAAC,MAAM,aAAa,CAAC;AACzE,OAAO,EAAqB,KAAK,IAAI,EAAuB,MAAM,WAAW,CAAC;AAC9E,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,YAAY,EAClB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAC,KAAK,MAAM,EAAC,MAAM,aAAa,CAAC;AAExC;;;GAGG;AACH,qBAAa,MAAO,YAAW,cAAc;;gBAqBzC,KAAK,EAAE,WAAW,EAClB,gBAAgB,EAAE,MAAM,EACxB,aAAa,EAAE,WAAW,EAC1B,IAAI,EAAE,QAAQ,GAAG,YAAY,EAC7B,wBAAwB,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC;IAqBhD,eAAe,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI;IAI3C,WAAW;IAIX,SAAS;IAKR,MAAM,CAAC,IAAI,EAAE,IAAI,GAAG,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC;IAqBhD,OAAO,IAAI,IAAI;IAIf,SAAS,IAAI,YAAY;IAIxB,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC;CA4JvC"}
1
+ {"version":3,"file":"exists.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/exists.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,mCAAmC,CAAC;AAGnE,OAAO,EAAkC,KAAK,MAAM,EAAC,MAAM,aAAa,CAAC;AACzE,OAAO,EAAqB,KAAK,IAAI,EAAuB,MAAM,WAAW,CAAC;AAC9E,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,YAAY,EAClB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAC,KAAK,MAAM,EAAC,MAAM,aAAa,CAAC;AAExC;;;GAGG;AACH,qBAAa,MAAO,YAAW,cAAc;;gBAqBzC,KAAK,EAAE,WAAW,EAClB,gBAAgB,EAAE,MAAM,EACxB,aAAa,EAAE,WAAW,EAC1B,IAAI,EAAE,QAAQ,GAAG,YAAY,EAC7B,wBAAwB,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC;IAqBhD,eAAe,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI;IAI3C,WAAW;IAIX,SAAS;IAKR,MAAM,CAAC,IAAI,EAAE,IAAI,GAAG,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC;IAqBvD,OAAO,IAAI,IAAI;IAIf,SAAS,IAAI,YAAY;IAIxB,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC;CA4JvC"}
@@ -1,7 +1,7 @@
1
1
  import { assert, unreachable } from "../../../shared/src/asserts.js";
2
- import { areEqual } from "../../../shared/src/arrays.js";
3
2
  import { makeAddChange, makeRemoveChange } from "./change.js";
4
3
  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 +1 @@
1
- {"version":3,"file":"exists.js","names":["#input","#relationshipName","#not","#parentJoinKey","#noSizeReuse","#cache","#cacheHitCountsForTesting","#output","#inPush","#getCacheKey","#fetchExists","#filter","#pushWithFilter","#fetchSize"],"sources":["../../../../../zql/src/ivm/exists.ts"],"sourcesContent":["import {areEqual} from '../../../shared/src/arrays.ts';\nimport {assert, unreachable} from '../../../shared/src/asserts.ts';\nimport type {CompoundKey} from '../../../zero-protocol/src/ast.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport {ChangeType} from './change-type.ts';\nimport {makeAddChange, makeRemoveChange, type Change} from './change.ts';\nimport {normalizeUndefined, type Node, type NormalizedValue} from './data.ts';\nimport {\n throwFilterOutput,\n type FilterInput,\n type FilterOperator,\n type FilterOutput,\n} from './filter-operators.ts';\nimport type {SourceSchema} from './schema.ts';\nimport {type Stream} from './stream.ts';\n\n/**\n * The Exists operator filters data based on whether or not a relationship is\n * non-empty.\n */\nexport class Exists implements FilterOperator {\n readonly #input: FilterInput;\n readonly #relationshipName: string;\n readonly #not: boolean;\n readonly #parentJoinKey: CompoundKey;\n readonly #noSizeReuse: boolean;\n #cache: Map<string, boolean>;\n #cacheHitCountsForTesting: Map<string, number> | undefined;\n #output: FilterOutput = throwFilterOutput;\n\n /**\n * This instance variable is `true` when this operator is processing a `push`,\n * and is used to disable reuse of cached sizes across rows with the\n * same parent join key value.\n * This is necessary because during a push relationships can be inconsistent\n * due to push communicating changes (which may change multiple Nodes) one\n * Node at a time.\n */\n #inPush = false;\n\n constructor(\n input: FilterInput,\n relationshipName: string,\n parentJoinKey: CompoundKey,\n type: 'EXISTS' | 'NOT EXISTS',\n cacheHitCountsForTesting?: Map<string, number>,\n ) {\n this.#input = input;\n this.#relationshipName = relationshipName;\n this.#input.setFilterOutput(this);\n this.#cache = new Map();\n this.#cacheHitCountsForTesting = cacheHitCountsForTesting;\n assert(\n this.#input.getSchema().relationships[relationshipName],\n `Input schema missing ${relationshipName}`,\n );\n this.#not = type === 'NOT EXISTS';\n this.#parentJoinKey = parentJoinKey;\n\n // If the parentJoinKey is the primary key, no sense in trying to reuse.\n this.#noSizeReuse = areEqual(\n parentJoinKey,\n this.#input.getSchema().primaryKey,\n );\n }\n\n setFilterOutput(output: FilterOutput): void {\n this.#output = output;\n }\n\n beginFilter() {\n this.#output.beginFilter();\n }\n\n endFilter() {\n this.#cache = new Map();\n this.#output.endFilter();\n }\n\n *filter(node: Node): Generator<'yield', boolean> {\n let exists: boolean | undefined;\n if (!this.#noSizeReuse && !this.#inPush) {\n const key = this.#getCacheKey(node, this.#parentJoinKey);\n exists = this.#cache.get(key);\n if (exists === undefined) {\n exists = yield* this.#fetchExists(node);\n this.#cache.set(key, exists);\n } else if (this.#cacheHitCountsForTesting) {\n this.#cacheHitCountsForTesting.set(\n key,\n (this.#cacheHitCountsForTesting.get(key) ?? 0) + 1,\n );\n }\n }\n\n const result =\n (yield* this.#filter(node, exists)) && (yield* this.#output.filter(node));\n return result;\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): Stream<'yield'> {\n assert(!this.#inPush, 'Unexpected re-entrancy');\n this.#inPush = true;\n try {\n switch (change[ChangeIndex.TYPE]) {\n // add, remove and edit cannot change the size of the\n // this.#relationshipName relationship, so simply #pushWithFilter\n case ChangeType.ADD:\n case ChangeType.EDIT:\n case ChangeType.REMOVE: {\n yield* this.#pushWithFilter(change);\n return;\n }\n case ChangeType.CHILD:\n // Only add and remove child changes for the\n // this.#relationshipName relationship, can change the size\n // of the this.#relationshipName relationship, for other\n // child changes simply #pushWithFilter\n if (\n change[ChangeIndex.CHILD_DATA].relationshipName !==\n this.#relationshipName ||\n change[ChangeIndex.CHILD_DATA].change[ChangeIndex.TYPE] ===\n ChangeType.EDIT ||\n change[ChangeIndex.CHILD_DATA].change[ChangeIndex.TYPE] ===\n ChangeType.CHILD\n ) {\n yield* this.#pushWithFilter(change);\n return;\n }\n switch (change[ChangeIndex.CHILD_DATA].change[ChangeIndex.TYPE]) {\n case ChangeType.ADD: {\n const size = yield* this.#fetchSize(change[ChangeIndex.NODE]);\n if (size === 1) {\n if (this.#not) {\n // Since the add child change currently being processed is not\n // pushed to output, the added child needs to be excluded from\n // the remove being pushed to output (since the child has\n // never been added to the output).\n yield* this.#output.push(\n makeRemoveChange({\n row: change[ChangeIndex.NODE].row,\n relationships: {\n ...change[ChangeIndex.NODE].relationships,\n [this.#relationshipName]: () => [],\n },\n }),\n this,\n );\n } else {\n yield* this.#output.push(\n makeAddChange(change[ChangeIndex.NODE]),\n this,\n );\n }\n } else {\n yield* this.#pushWithFilter(change, size > 0);\n }\n return;\n }\n case ChangeType.REMOVE: {\n const size = yield* this.#fetchSize(change[ChangeIndex.NODE]);\n if (size === 0) {\n if (this.#not) {\n yield* this.#output.push(\n makeAddChange(change[ChangeIndex.NODE]),\n this,\n );\n } else {\n // Since the remove child change currently being processed is\n // not pushed to output, the removed child needs to be added to\n // the remove being pushed to output.\n yield* this.#output.push(\n makeRemoveChange({\n row: change[ChangeIndex.NODE].row,\n relationships: {\n ...change[ChangeIndex.NODE].relationships,\n [this.#relationshipName]: () => [\n change[ChangeIndex.CHILD_DATA].change[\n ChangeIndex.NODE\n ],\n ],\n },\n }),\n this,\n );\n }\n } else {\n yield* this.#pushWithFilter(change, size > 0);\n }\n return;\n }\n }\n return;\n default:\n unreachable(change);\n }\n } finally {\n this.#inPush = false;\n }\n }\n\n /**\n * Returns whether or not the node's this.#relationshipName\n * relationship passes the exist/not exists filter condition.\n * If the optional `size` is passed it is used.\n * Otherwise, if there is a stored size for the row it is used.\n * Otherwise the size is computed by streaming the node's\n * relationship with this.#relationshipName (this computed size is also\n * stored).\n */\n *#filter(node: Node, exists?: boolean): Generator<'yield', boolean> {\n exists = exists ?? (yield* this.#fetchExists(node));\n return this.#not ? !exists : exists;\n }\n\n #getCacheKey(node: Node, def: CompoundKey): string {\n const values: NormalizedValue[] = [];\n for (const key of def) {\n values.push(normalizeUndefined(node.row[key]));\n }\n return JSON.stringify(values);\n }\n\n /**\n * Pushes a change if this.#filter is true for its row.\n */\n *#pushWithFilter(change: Change, exists?: boolean): Stream<'yield'> {\n if (yield* this.#filter(change[ChangeIndex.NODE], exists)) {\n yield* this.#output.push(change, this);\n }\n }\n\n *#fetchExists(node: Node): Generator<'yield', boolean> {\n // While it seems like this should be able to fetch just 1 node\n // to check for exists, we can't because Take does not support\n // early return during initial fetch.\n return (yield* this.#fetchSize(node)) > 0;\n }\n\n *#fetchSize(node: Node): Generator<'yield', number> {\n const relationship = node.relationships[this.#relationshipName];\n assert(\n relationship,\n () =>\n `Exists: relationship \"${this.#relationshipName}\" not found on node`,\n );\n let size = 0;\n for (const n of relationship()) {\n if (n === 'yield') {\n yield 'yield';\n } else {\n size++;\n }\n }\n return size;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"exists.js","names":["#input","#relationshipName","#not","#parentJoinKey","#noSizeReuse","#cache","#cacheHitCountsForTesting","#output","#inPush","#getCacheKey","#fetchExists","#filter","#pushWithFilter","#fetchSize"],"sources":["../../../../../zql/src/ivm/exists.ts"],"sourcesContent":["import {areEqual} from '../../../shared/src/arrays.ts';\nimport {assert, unreachable} from '../../../shared/src/asserts.ts';\nimport type {CompoundKey} from '../../../zero-protocol/src/ast.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport {ChangeType} from './change-type.ts';\nimport {makeAddChange, makeRemoveChange, type Change} from './change.ts';\nimport {normalizeUndefined, type Node, type NormalizedValue} from './data.ts';\nimport {\n throwFilterOutput,\n type FilterInput,\n type FilterOperator,\n type FilterOutput,\n} from './filter-operators.ts';\nimport type {SourceSchema} from './schema.ts';\nimport {type Stream} from './stream.ts';\n\n/**\n * The Exists operator filters data based on whether or not a relationship is\n * non-empty.\n */\nexport class Exists implements FilterOperator {\n readonly #input: FilterInput;\n readonly #relationshipName: string;\n readonly #not: boolean;\n readonly #parentJoinKey: CompoundKey;\n readonly #noSizeReuse: boolean;\n #cache: Map<string, boolean>;\n #cacheHitCountsForTesting: Map<string, number> | undefined;\n #output: FilterOutput = throwFilterOutput;\n\n /**\n * This instance variable is `true` when this operator is processing a `push`,\n * and is used to disable reuse of cached sizes across rows with the\n * same parent join key value.\n * This is necessary because during a push relationships can be inconsistent\n * due to push communicating changes (which may change multiple Nodes) one\n * Node at a time.\n */\n #inPush = false;\n\n constructor(\n input: FilterInput,\n relationshipName: string,\n parentJoinKey: CompoundKey,\n type: 'EXISTS' | 'NOT EXISTS',\n cacheHitCountsForTesting?: Map<string, number>,\n ) {\n this.#input = input;\n this.#relationshipName = relationshipName;\n this.#input.setFilterOutput(this);\n this.#cache = new Map();\n this.#cacheHitCountsForTesting = cacheHitCountsForTesting;\n assert(\n this.#input.getSchema().relationships[relationshipName],\n `Input schema missing ${relationshipName}`,\n );\n this.#not = type === 'NOT EXISTS';\n this.#parentJoinKey = parentJoinKey;\n\n // If the parentJoinKey is the primary key, no sense in trying to reuse.\n this.#noSizeReuse = areEqual(\n parentJoinKey,\n this.#input.getSchema().primaryKey,\n );\n }\n\n setFilterOutput(output: FilterOutput): void {\n this.#output = output;\n }\n\n beginFilter() {\n this.#output.beginFilter();\n }\n\n endFilter() {\n this.#cache = new Map();\n this.#output.endFilter();\n }\n\n *filter(node: Node): IterableIterator<'yield', boolean> {\n let exists: boolean | undefined;\n if (!this.#noSizeReuse && !this.#inPush) {\n const key = this.#getCacheKey(node, this.#parentJoinKey);\n exists = this.#cache.get(key);\n if (exists === undefined) {\n exists = yield* this.#fetchExists(node);\n this.#cache.set(key, exists);\n } else if (this.#cacheHitCountsForTesting) {\n this.#cacheHitCountsForTesting.set(\n key,\n (this.#cacheHitCountsForTesting.get(key) ?? 0) + 1,\n );\n }\n }\n\n const result =\n (yield* this.#filter(node, exists)) && (yield* this.#output.filter(node));\n return result;\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): Stream<'yield'> {\n assert(!this.#inPush, 'Unexpected re-entrancy');\n this.#inPush = true;\n try {\n switch (change[ChangeIndex.TYPE]) {\n // add, remove and edit cannot change the size of the\n // this.#relationshipName relationship, so simply #pushWithFilter\n case ChangeType.ADD:\n case ChangeType.EDIT:\n case ChangeType.REMOVE: {\n yield* this.#pushWithFilter(change);\n return;\n }\n case ChangeType.CHILD:\n // Only add and remove child changes for the\n // this.#relationshipName relationship, can change the size\n // of the this.#relationshipName relationship, for other\n // child changes simply #pushWithFilter\n if (\n change[ChangeIndex.CHILD_DATA].relationshipName !==\n this.#relationshipName ||\n change[ChangeIndex.CHILD_DATA].change[ChangeIndex.TYPE] ===\n ChangeType.EDIT ||\n change[ChangeIndex.CHILD_DATA].change[ChangeIndex.TYPE] ===\n ChangeType.CHILD\n ) {\n yield* this.#pushWithFilter(change);\n return;\n }\n switch (change[ChangeIndex.CHILD_DATA].change[ChangeIndex.TYPE]) {\n case ChangeType.ADD: {\n const size = yield* this.#fetchSize(change[ChangeIndex.NODE]);\n if (size === 1) {\n if (this.#not) {\n // Since the add child change currently being processed is not\n // pushed to output, the added child needs to be excluded from\n // the remove being pushed to output (since the child has\n // never been added to the output).\n yield* this.#output.push(\n makeRemoveChange({\n row: change[ChangeIndex.NODE].row,\n relationships: {\n ...change[ChangeIndex.NODE].relationships,\n [this.#relationshipName]: () => [],\n },\n }),\n this,\n );\n } else {\n yield* this.#output.push(\n makeAddChange(change[ChangeIndex.NODE]),\n this,\n );\n }\n } else {\n yield* this.#pushWithFilter(change, size > 0);\n }\n return;\n }\n case ChangeType.REMOVE: {\n const size = yield* this.#fetchSize(change[ChangeIndex.NODE]);\n if (size === 0) {\n if (this.#not) {\n yield* this.#output.push(\n makeAddChange(change[ChangeIndex.NODE]),\n this,\n );\n } else {\n // Since the remove child change currently being processed is\n // not pushed to output, the removed child needs to be added to\n // the remove being pushed to output.\n yield* this.#output.push(\n makeRemoveChange({\n row: change[ChangeIndex.NODE].row,\n relationships: {\n ...change[ChangeIndex.NODE].relationships,\n [this.#relationshipName]: () => [\n change[ChangeIndex.CHILD_DATA].change[\n ChangeIndex.NODE\n ],\n ],\n },\n }),\n this,\n );\n }\n } else {\n yield* this.#pushWithFilter(change, size > 0);\n }\n return;\n }\n }\n return;\n default:\n unreachable(change);\n }\n } finally {\n this.#inPush = false;\n }\n }\n\n /**\n * Returns whether or not the node's this.#relationshipName\n * relationship passes the exist/not exists filter condition.\n * If the optional `size` is passed it is used.\n * Otherwise, if there is a stored size for the row it is used.\n * Otherwise the size is computed by streaming the node's\n * relationship with this.#relationshipName (this computed size is also\n * stored).\n */\n *#filter(node: Node, exists?: boolean): IterableIterator<'yield', boolean> {\n exists = exists ?? (yield* this.#fetchExists(node));\n return this.#not ? !exists : exists;\n }\n\n #getCacheKey(node: Node, def: CompoundKey): string {\n const values: NormalizedValue[] = [];\n for (const key of def) {\n values.push(normalizeUndefined(node.row[key]));\n }\n return JSON.stringify(values);\n }\n\n /**\n * Pushes a change if this.#filter is true for its row.\n */\n *#pushWithFilter(change: Change, exists?: boolean): Stream<'yield'> {\n if (yield* this.#filter(change[ChangeIndex.NODE], exists)) {\n yield* this.#output.push(change, this);\n }\n }\n\n *#fetchExists(node: Node): IterableIterator<'yield', boolean> {\n // While it seems like this should be able to fetch just 1 node\n // to check for exists, we can't because Take does not support\n // early return during initial fetch.\n return (yield* this.#fetchSize(node)) > 0;\n }\n\n *#fetchSize(node: Node): IterableIterator<'yield', number> {\n const relationship = node.relationships[this.#relationshipName];\n assert(\n relationship,\n () =>\n `Exists: relationship \"${this.#relationshipName}\" not found on node`,\n );\n let size = 0;\n for (const n of relationship()) {\n if (n === 'yield') {\n yield 'yield';\n } else {\n size++;\n }\n }\n return size;\n }\n}\n"],"mappings":"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"}
@@ -26,8 +26,8 @@ export declare class FanIn implements FilterOperator {
26
26
  getSchema(): SourceSchema;
27
27
  beginFilter(): void;
28
28
  endFilter(): void;
29
- filter(node: Node): Generator<'yield', boolean>;
29
+ filter(node: Node): IterableIterator<'yield', boolean>;
30
30
  push(change: Change): readonly never[];
31
- fanOutDonePushingToAllBranches(fanOutChangeType: ChangeType): Generator<"yield", void, any>;
31
+ fanOutDonePushingToAllBranches(fanOutChangeType: ChangeType): IterableIterator<any, undefined> | import("./stream.ts").Stream<"yield">;
32
32
  }
33
33
  //# sourceMappingURL=fan-in.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fan-in.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/fan-in.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAC,KAAK,MAAM,EAAC,MAAM,aAAa,CAAC;AACxC,OAAO,EAAC,KAAK,IAAI,EAAC,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,cAAc,CAAC;AACzC,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,YAAY,EAClB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;AAE9C;;;;;;;;;;;;;GAaG;AACH,qBAAa,KAAM,YAAW,cAAc;;gBAM9B,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE;IASjD,eAAe,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI;IAI3C,OAAO,IAAI,IAAI;IAMf,SAAS;IAIT,WAAW,IAAI,IAAI;IAInB,SAAS,IAAI,IAAI;IAIhB,MAAM,CAAC,IAAI,EAAE,IAAI,GAAG,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC;IAIhD,IAAI,CAAC,MAAM,EAAE,MAAM;IAKlB,8BAA8B,CAAC,gBAAgB,EAAE,UAAU;CAkB7D"}
1
+ {"version":3,"file":"fan-in.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/fan-in.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAC,KAAK,MAAM,EAAC,MAAM,aAAa,CAAC;AACxC,OAAO,EAAC,KAAK,IAAI,EAAC,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,cAAc,CAAC;AACzC,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,YAAY,EAClB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;AAE9C;;;;;;;;;;;;;GAaG;AACH,qBAAa,KAAM,YAAW,cAAc;;gBAM9B,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE;IASjD,eAAe,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI;IAI3C,OAAO,IAAI,IAAI;IAMf,SAAS;IAIT,WAAW,IAAI,IAAI;IAInB,SAAS,IAAI,IAAI;IAIjB,MAAM,CAAC,IAAI,EAAE,IAAI,GAAG,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC;IAItD,IAAI,CAAC,MAAM,EAAE,MAAM;IAKnB,8BAA8B,CAAC,gBAAgB,EAAE,UAAU;CAkB5D"}
@@ -1,4 +1,5 @@
1
1
  import { assert } from "../../../shared/src/asserts.js";
2
+ import { emptyIterator } from "../../../shared/src/iterables.js";
2
3
  import { emptyArray, identity } from "../../../shared/src/sentinels.js";
3
4
  import "./data.js";
4
5
  import { throwFilterOutput } from "./filter-operators.js";
@@ -46,19 +47,19 @@ var FanIn = class {
46
47
  endFilter() {
47
48
  this.#output.endFilter();
48
49
  }
49
- *filter(node) {
50
- return yield* this.#output.filter(node);
50
+ filter(node) {
51
+ return this.#output.filter(node);
51
52
  }
52
53
  push(change) {
53
54
  this.#accumulatedPushes.push(change);
54
55
  return emptyArray;
55
56
  }
56
- *fanOutDonePushingToAllBranches(fanOutChangeType) {
57
+ fanOutDonePushingToAllBranches(fanOutChangeType) {
57
58
  if (this.#inputs.length === 0) {
58
59
  assert(this.#accumulatedPushes.length === 0, "If there are no inputs then fan-in should not receive any pushes.");
59
- return;
60
+ return emptyIterator;
60
61
  }
61
- yield* pushAccumulatedChanges(this.#accumulatedPushes, this.#output, this, fanOutChangeType, identity, identity);
62
+ return pushAccumulatedChanges(this.#accumulatedPushes, this.#output, this, fanOutChangeType, identity, identity);
62
63
  }
63
64
  };
64
65
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"fan-in.js","names":["#inputs","#schema","#output","#accumulatedPushes"],"sources":["../../../../../zql/src/ivm/fan-in.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport {emptyArray, identity} from '../../../shared/src/sentinels.ts';\nimport type {ChangeType} from './change-type.ts';\nimport {type Change} from './change.ts';\nimport {type Node} from './data.ts';\nimport type {FanOut} from './fan-out.ts';\nimport {\n throwFilterOutput,\n type FilterInput,\n type FilterOperator,\n type FilterOutput,\n} from './filter-operators.ts';\nimport {pushAccumulatedChanges} from './push-accumulated.ts';\nimport type {SourceSchema} from './schema.ts';\n\n/**\n * The FanIn operator merges multiple streams into one.\n * It eliminates duplicates and must be paired with a fan-out operator\n * somewhere upstream of the fan-in.\n *\n * issue\n * |\n * fan-out\n * / \\\n * a b\n * \\ /\n * fan-in\n * |\n */\nexport class FanIn implements FilterOperator {\n readonly #inputs: readonly FilterInput[];\n readonly #schema: SourceSchema;\n #output: FilterOutput = throwFilterOutput;\n #accumulatedPushes: Change[] = [];\n\n constructor(fanOut: FanOut, inputs: FilterInput[]) {\n this.#inputs = inputs;\n this.#schema = fanOut.getSchema();\n for (const input of inputs) {\n input.setFilterOutput(this);\n assert(this.#schema === input.getSchema(), `Schema mismatch in fan-in`);\n }\n }\n\n setFilterOutput(output: FilterOutput): void {\n this.#output = output;\n }\n\n destroy(): void {\n for (const input of this.#inputs) {\n input.destroy();\n }\n }\n\n getSchema() {\n return this.#schema;\n }\n\n beginFilter(): void {\n this.#output.beginFilter();\n }\n\n endFilter(): void {\n this.#output.endFilter();\n }\n\n *filter(node: Node): Generator<'yield', boolean> {\n return yield* this.#output.filter(node);\n }\n\n push(change: Change) {\n this.#accumulatedPushes.push(change);\n return emptyArray;\n }\n\n *fanOutDonePushingToAllBranches(fanOutChangeType: ChangeType) {\n if (this.#inputs.length === 0) {\n assert(\n this.#accumulatedPushes.length === 0,\n 'If there are no inputs then fan-in should not receive any pushes.',\n );\n return;\n }\n\n yield* pushAccumulatedChanges(\n this.#accumulatedPushes,\n this.#output,\n this,\n fanOutChangeType,\n identity,\n identity,\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA6BA,IAAa,QAAb,MAA6C;CAC3C;CACA;CACA,UAAwB;CACxB,qBAA+B,CAAC;CAEhC,YAAY,QAAgB,QAAuB;EACjD,KAAKA,UAAU;EACf,KAAKC,UAAU,OAAO,UAAU;EAChC,KAAK,MAAM,SAAS,QAAQ;GAC1B,MAAM,gBAAgB,IAAI;GAC1B,OAAO,KAAKA,YAAY,MAAM,UAAU,GAAG,2BAA2B;EACxE;CACF;CAEA,gBAAgB,QAA4B;EAC1C,KAAKC,UAAU;CACjB;CAEA,UAAgB;EACd,KAAK,MAAM,SAAS,KAAKF,SACvB,MAAM,QAAQ;CAElB;CAEA,YAAY;EACV,OAAO,KAAKC;CACd;CAEA,cAAoB;EAClB,KAAKC,QAAQ,YAAY;CAC3B;CAEA,YAAkB;EAChB,KAAKA,QAAQ,UAAU;CACzB;CAEA,CAAC,OAAO,MAAyC;EAC/C,OAAO,OAAO,KAAKA,QAAQ,OAAO,IAAI;CACxC;CAEA,KAAK,QAAgB;EACnB,KAAKC,mBAAmB,KAAK,MAAM;EACnC,OAAO;CACT;CAEA,CAAC,+BAA+B,kBAA8B;EAC5D,IAAI,KAAKH,QAAQ,WAAW,GAAG;GAC7B,OACE,KAAKG,mBAAmB,WAAW,GACnC,mEACF;GACA;EACF;EAEA,OAAO,uBACL,KAAKA,oBACL,KAAKD,SACL,MACA,kBACA,UACA,QACF;CACF;AACF"}
1
+ {"version":3,"file":"fan-in.js","names":["#inputs","#schema","#output","#accumulatedPushes"],"sources":["../../../../../zql/src/ivm/fan-in.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport {emptyIterator} from '../../../shared/src/iterables.ts';\nimport {emptyArray, identity} from '../../../shared/src/sentinels.ts';\nimport type {ChangeType} from './change-type.ts';\nimport {type Change} from './change.ts';\nimport {type Node} from './data.ts';\nimport type {FanOut} from './fan-out.ts';\nimport {\n throwFilterOutput,\n type FilterInput,\n type FilterOperator,\n type FilterOutput,\n} from './filter-operators.ts';\nimport {pushAccumulatedChanges} from './push-accumulated.ts';\nimport type {SourceSchema} from './schema.ts';\n\n/**\n * The FanIn operator merges multiple streams into one.\n * It eliminates duplicates and must be paired with a fan-out operator\n * somewhere upstream of the fan-in.\n *\n * issue\n * |\n * fan-out\n * / \\\n * a b\n * \\ /\n * fan-in\n * |\n */\nexport class FanIn implements FilterOperator {\n readonly #inputs: readonly FilterInput[];\n readonly #schema: SourceSchema;\n #output: FilterOutput = throwFilterOutput;\n #accumulatedPushes: Change[] = [];\n\n constructor(fanOut: FanOut, inputs: FilterInput[]) {\n this.#inputs = inputs;\n this.#schema = fanOut.getSchema();\n for (const input of inputs) {\n input.setFilterOutput(this);\n assert(this.#schema === input.getSchema(), `Schema mismatch in fan-in`);\n }\n }\n\n setFilterOutput(output: FilterOutput): void {\n this.#output = output;\n }\n\n destroy(): void {\n for (const input of this.#inputs) {\n input.destroy();\n }\n }\n\n getSchema() {\n return this.#schema;\n }\n\n beginFilter(): void {\n this.#output.beginFilter();\n }\n\n endFilter(): void {\n this.#output.endFilter();\n }\n\n filter(node: Node): IterableIterator<'yield', boolean> {\n return this.#output.filter(node);\n }\n\n push(change: Change) {\n this.#accumulatedPushes.push(change);\n return emptyArray;\n }\n\n fanOutDonePushingToAllBranches(fanOutChangeType: ChangeType) {\n if (this.#inputs.length === 0) {\n assert(\n this.#accumulatedPushes.length === 0,\n 'If there are no inputs then fan-in should not receive any pushes.',\n );\n return emptyIterator;\n }\n\n return pushAccumulatedChanges(\n this.#accumulatedPushes,\n this.#output,\n this,\n fanOutChangeType,\n identity,\n identity,\n );\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AA8BA,IAAa,QAAb,MAA6C;CAC3C;CACA;CACA,UAAwB;CACxB,qBAA+B,CAAC;CAEhC,YAAY,QAAgB,QAAuB;EACjD,KAAKA,UAAU;EACf,KAAKC,UAAU,OAAO,UAAU;EAChC,KAAK,MAAM,SAAS,QAAQ;GAC1B,MAAM,gBAAgB,IAAI;GAC1B,OAAO,KAAKA,YAAY,MAAM,UAAU,GAAG,2BAA2B;EACxE;CACF;CAEA,gBAAgB,QAA4B;EAC1C,KAAKC,UAAU;CACjB;CAEA,UAAgB;EACd,KAAK,MAAM,SAAS,KAAKF,SACvB,MAAM,QAAQ;CAElB;CAEA,YAAY;EACV,OAAO,KAAKC;CACd;CAEA,cAAoB;EAClB,KAAKC,QAAQ,YAAY;CAC3B;CAEA,YAAkB;EAChB,KAAKA,QAAQ,UAAU;CACzB;CAEA,OAAO,MAAgD;EACrD,OAAO,KAAKA,QAAQ,OAAO,IAAI;CACjC;CAEA,KAAK,QAAgB;EACnB,KAAKC,mBAAmB,KAAK,MAAM;EACnC,OAAO;CACT;CAEA,+BAA+B,kBAA8B;EAC3D,IAAI,KAAKH,QAAQ,WAAW,GAAG;GAC7B,OACE,KAAKG,mBAAmB,WAAW,GACnC,mEACF;GACA,OAAO;EACT;EAEA,OAAO,uBACL,KAAKA,oBACL,KAAKD,SACL,MACA,kBACA,UACA,QACF;CACF;AACF"}
@@ -16,7 +16,7 @@ export declare class FanOut implements FilterOperator {
16
16
  getSchema(): import("./schema.ts").SourceSchema;
17
17
  beginFilter(): void;
18
18
  endFilter(): void;
19
- filter(node: Node): Generator<'yield', boolean>;
20
- push(change: Change): Generator<"yield", void, any>;
19
+ filter(node: Node): IterableIterator<'yield', boolean>;
20
+ push(change: Change): Generator<any, void, any>;
21
21
  }
22
22
  //# sourceMappingURL=fan-out.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fan-out.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/fan-out.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,aAAa,CAAC;AACxC,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,EAAC,KAAK,EAAC,MAAM,aAAa,CAAC;AACvC,OAAO,KAAK,EACV,WAAW,EACX,cAAc,EACd,YAAY,EACb,MAAM,uBAAuB,CAAC;AAE/B;;;;GAIG;AACH,qBAAa,MAAO,YAAW,cAAc;;gBAM/B,KAAK,EAAE,WAAW;IAK9B,QAAQ,CAAC,KAAK,EAAE,KAAK;IAIrB,eAAe,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI;IAI3C,OAAO,IAAI,IAAI;IAWf,SAAS;IAIT,WAAW,IAAI,IAAI;IAMnB,SAAS,IAAI,IAAI;IAMhB,MAAM,CAAC,IAAI,EAAE,IAAI,GAAG,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC;IAW/C,IAAI,CAAC,MAAM,EAAE,MAAM;CASrB"}
1
+ {"version":3,"file":"fan-out.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/fan-out.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,aAAa,CAAC;AACxC,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,EAAC,KAAK,EAAC,MAAM,aAAa,CAAC;AACvC,OAAO,KAAK,EACV,WAAW,EACX,cAAc,EACd,YAAY,EACb,MAAM,uBAAuB,CAAC;AAE/B;;;;GAIG;AACH,qBAAa,MAAO,YAAW,cAAc;;gBAM/B,KAAK,EAAE,WAAW;IAK9B,QAAQ,CAAC,KAAK,EAAE,KAAK;IAIrB,eAAe,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI;IAI3C,OAAO,IAAI,IAAI;IAWf,SAAS;IAIT,WAAW,IAAI,IAAI;IAMnB,SAAS,IAAI,IAAI;IAMhB,MAAM,CAAC,IAAI,EAAE,IAAI,GAAG,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC;IAWtD,IAAI,CAAC,MAAM,EAAE,MAAM;CASrB"}