@rocicorp/zero 1.12.0-canary.3 → 1.12.0-canary.4

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 (785) hide show
  1. package/out/_virtual/{_@oxc-project_runtime@0.130.0/helpers → _@oxc-project_runtime@0.154.0/helpers/esm}/usingCtx.js +2 -2
  2. package/out/analyze-query/src/analyze-cli.js +4 -3
  3. package/out/analyze-query/src/analyze-cli.js.map +1 -1
  4. package/out/analyze-query/src/bin-analyze.js +5 -3
  5. package/out/analyze-query/src/bin-analyze.js.map +1 -1
  6. package/out/analyze-query/src/bin-transform.js +3 -2
  7. package/out/analyze-query/src/bin-transform.js.map +1 -1
  8. package/out/ast-to-zql/src/ast-to-zql.js.map +1 -1
  9. package/out/ast-to-zql/src/bin.js +3 -2
  10. package/out/ast-to-zql/src/bin.js.map +1 -1
  11. package/out/ast-to-zql/src/format.js.map +1 -1
  12. package/out/datadog/src/datadog-log-sink.js +5 -4
  13. package/out/datadog/src/datadog-log-sink.js.map +1 -1
  14. package/out/otel/src/log-options.js.map +1 -1
  15. package/out/replicache/src/btree/node.js +13 -11
  16. package/out/replicache/src/btree/node.js.map +1 -1
  17. package/out/replicache/src/btree/read.js +4 -2
  18. package/out/replicache/src/btree/read.js.map +1 -1
  19. package/out/replicache/src/btree/splice.js.map +1 -1
  20. package/out/replicache/src/btree/write.js +9 -5
  21. package/out/replicache/src/btree/write.js.map +1 -1
  22. package/out/replicache/src/config.js +1 -2
  23. package/out/replicache/src/connection-loop.js.map +1 -1
  24. package/out/replicache/src/dag/chunk.js +1 -1
  25. package/out/replicache/src/dag/gc.js.map +1 -1
  26. package/out/replicache/src/dag/lazy-store.js +8 -7
  27. package/out/replicache/src/dag/lazy-store.js.map +1 -1
  28. package/out/replicache/src/dag/store-impl.js +1 -1
  29. package/out/replicache/src/dag/store-impl.js.map +1 -1
  30. package/out/replicache/src/dag/visitor.js.map +1 -1
  31. package/out/replicache/src/db/commit.js +5 -3
  32. package/out/replicache/src/db/commit.js.map +1 -1
  33. package/out/replicache/src/db/read.js +1 -1
  34. package/out/replicache/src/db/read.js.map +1 -1
  35. package/out/replicache/src/db/rebase.js.map +1 -1
  36. package/out/replicache/src/db/write.js +5 -2
  37. package/out/replicache/src/db/write.js.map +1 -1
  38. package/out/replicache/src/deleted-clients.js +3 -2
  39. package/out/replicache/src/deleted-clients.js.map +1 -1
  40. package/out/replicache/src/expo-sqlite.js +1 -2
  41. package/out/replicache/src/frozen-json.js +1 -1
  42. package/out/replicache/src/frozen-json.js.map +1 -1
  43. package/out/replicache/src/get-default-puller.js +1 -1
  44. package/out/replicache/src/hash.js +1 -1
  45. package/out/replicache/src/hash.js.map +1 -1
  46. package/out/replicache/src/impl.js +1 -2
  47. package/out/replicache/src/index-defs.js +5 -3
  48. package/out/replicache/src/index-defs.js.map +1 -1
  49. package/out/replicache/src/kv/expo-sqlite/store.js +1 -1
  50. package/out/replicache/src/kv/expo-sqlite/store.js.map +1 -1
  51. package/out/replicache/src/kv/idb-store.js +1 -1
  52. package/out/replicache/src/kv/idb-store.js.map +1 -1
  53. package/out/replicache/src/kv/mem-fallback-store.js +1 -1
  54. package/out/replicache/src/kv/mem-fallback-store.js.map +1 -1
  55. package/out/replicache/src/kv/mem-store.js.map +1 -1
  56. package/out/replicache/src/kv/op-sqlite/store.js.map +1 -1
  57. package/out/replicache/src/kv/op-sqlite/types.js.map +1 -1
  58. package/out/replicache/src/kv/read-impl.js.map +1 -1
  59. package/out/replicache/src/kv/sqlite-store.js +1 -1
  60. package/out/replicache/src/kv/sqlite-store.js.map +1 -1
  61. package/out/replicache/src/kv/write-impl-base.js +4 -1
  62. package/out/replicache/src/kv/write-impl-base.js.map +1 -1
  63. package/out/replicache/src/kv/write-impl.js.map +1 -1
  64. package/out/replicache/src/log-options.js +2 -1
  65. package/out/replicache/src/log-options.js.map +1 -1
  66. package/out/replicache/src/make-idb-name.js +1 -1
  67. package/out/replicache/src/new-client-channel.js +9 -7
  68. package/out/replicache/src/new-client-channel.js.map +1 -1
  69. package/out/replicache/src/on-persist-channel.js +1 -1
  70. package/out/replicache/src/on-persist-channel.js.map +1 -1
  71. package/out/replicache/src/op-sqlite.js +1 -2
  72. package/out/replicache/src/pending-mutations.js +2 -1
  73. package/out/replicache/src/pending-mutations.js.map +1 -1
  74. package/out/replicache/src/persist/client-gc.js +2 -2
  75. package/out/replicache/src/persist/client-gc.js.map +1 -1
  76. package/out/replicache/src/persist/client-group-gc.js +2 -2
  77. package/out/replicache/src/persist/client-group-gc.js.map +1 -1
  78. package/out/replicache/src/persist/client-groups.js +1 -1
  79. package/out/replicache/src/persist/client-groups.js.map +1 -1
  80. package/out/replicache/src/persist/clients.js +1 -1
  81. package/out/replicache/src/persist/clients.js.map +1 -1
  82. package/out/replicache/src/persist/collect-idb-databases.js +5 -4
  83. package/out/replicache/src/persist/collect-idb-databases.js.map +1 -1
  84. package/out/replicache/src/persist/gather-mem-only-visitor.js.map +1 -1
  85. package/out/replicache/src/persist/gather-not-cached-visitor.js.map +1 -1
  86. package/out/replicache/src/persist/heartbeat.js +2 -2
  87. package/out/replicache/src/persist/heartbeat.js.map +1 -1
  88. package/out/replicache/src/persist/idb-databases-store.js +1 -1
  89. package/out/replicache/src/persist/idb-databases-store.js.map +1 -1
  90. package/out/replicache/src/persist/make-client-id.js +1 -1
  91. package/out/replicache/src/persist/make-client-id.js.map +1 -1
  92. package/out/replicache/src/persist/persist.js +3 -2
  93. package/out/replicache/src/persist/persist.js.map +1 -1
  94. package/out/replicache/src/persist/refresh.js +8 -5
  95. package/out/replicache/src/persist/refresh.js.map +1 -1
  96. package/out/replicache/src/process-scheduler.js.map +1 -1
  97. package/out/replicache/src/replicache-impl.js +3 -3
  98. package/out/replicache/src/replicache-impl.js.map +1 -1
  99. package/out/replicache/src/scan-iterator.js.map +1 -1
  100. package/out/replicache/src/scan-options.js +7 -6
  101. package/out/replicache/src/scan-options.js.map +1 -1
  102. package/out/replicache/src/sqlite.js +1 -2
  103. package/out/replicache/src/subscriptions.js +1 -1
  104. package/out/replicache/src/subscriptions.js.map +1 -1
  105. package/out/replicache/src/sync/diff.js +3 -1
  106. package/out/replicache/src/sync/diff.js.map +1 -1
  107. package/out/replicache/src/sync/ids.js.map +1 -1
  108. package/out/replicache/src/sync/patch.js +2 -4
  109. package/out/replicache/src/sync/patch.js.map +1 -1
  110. package/out/replicache/src/sync/pull.js +3 -1
  111. package/out/replicache/src/sync/pull.js.map +1 -1
  112. package/out/replicache/src/sync/push.js.map +1 -1
  113. package/out/replicache/src/sync/request-id.js +1 -1
  114. package/out/replicache/src/sync/request-id.js.map +1 -1
  115. package/out/replicache/src/transactions.js +1 -1
  116. package/out/replicache/src/transactions.js.map +1 -1
  117. package/out/shared/src/arrays.js +4 -1
  118. package/out/shared/src/arrays.js.map +1 -1
  119. package/out/shared/src/asserts.js +1 -1
  120. package/out/shared/src/bigint-json.d.ts +9 -0
  121. package/out/shared/src/bigint-json.d.ts.map +1 -1
  122. package/out/shared/src/bigint-json.js +27 -2
  123. package/out/shared/src/bigint-json.js.map +1 -1
  124. package/out/shared/src/broadcast-channel.js +1 -1
  125. package/out/shared/src/btree-set.js +11 -8
  126. package/out/shared/src/btree-set.js.map +1 -1
  127. package/out/shared/src/cache.js.map +1 -1
  128. package/out/shared/src/config.js +1 -1
  129. package/out/shared/src/custom-key-map.js.map +1 -1
  130. package/out/shared/src/custom-key-set.js.map +1 -1
  131. package/out/shared/src/deep-clone.js +1 -1
  132. package/out/shared/src/document-visible.js.map +1 -1
  133. package/out/shared/src/dotenv.js.map +1 -1
  134. package/out/shared/src/error.js +1 -1
  135. package/out/shared/src/error.js.map +1 -1
  136. package/out/shared/src/hash.js +2 -2
  137. package/out/shared/src/hash.js.map +1 -1
  138. package/out/shared/src/iterables.js.map +1 -1
  139. package/out/shared/src/json-schema.js.map +1 -1
  140. package/out/shared/src/json.js.map +1 -1
  141. package/out/shared/src/logging-test-utils.js +1 -1
  142. package/out/shared/src/logging.js +1 -1
  143. package/out/shared/src/logging.js.map +1 -1
  144. package/out/shared/src/navigator.js +1 -1
  145. package/out/shared/src/options.js +9 -12
  146. package/out/shared/src/options.js.map +1 -1
  147. package/out/shared/src/promise-race.js.map +1 -1
  148. package/out/shared/src/queue.js.map +1 -1
  149. package/out/shared/src/record-proxy.js.map +1 -1
  150. package/out/shared/src/ring-buffer.js.map +1 -1
  151. package/out/shared/src/size-of-value.js +7 -10
  152. package/out/shared/src/size-of-value.js.map +1 -1
  153. package/out/shared/src/sorted-entries.js.map +1 -1
  154. package/out/shared/src/tdigest-schema.js.map +1 -1
  155. package/out/shared/src/tdigest.js.map +1 -1
  156. package/out/shared/src/valita.js +1 -1
  157. package/out/shared/src/valita.js.map +1 -1
  158. package/out/z2s/src/compiler.js +2 -4
  159. package/out/z2s/src/compiler.js.map +1 -1
  160. package/out/z2s/src/sql.js +1 -1
  161. package/out/z2s/src/sql.js.map +1 -1
  162. package/out/zero/package.js +212 -189
  163. package/out/zero/package.js.map +1 -1
  164. package/out/zero/src/adapters/drizzle.js +1 -2
  165. package/out/zero/src/adapters/kysely.js +1 -2
  166. package/out/zero/src/adapters/pg.js +1 -2
  167. package/out/zero/src/adapters/postgresjs.js +1 -2
  168. package/out/zero/src/adapters/prisma.js +1 -2
  169. package/out/zero/src/analyze.js +1 -2
  170. package/out/zero/src/change-protocol/v0.js +1 -2
  171. package/out/zero/src/expo-sqlite.js +1 -2
  172. package/out/zero/src/op-sqlite.js +1 -2
  173. package/out/zero/src/react-native.js.map +1 -1
  174. package/out/zero/src/sqlite.js +1 -2
  175. package/out/zero/src/zero-cache-dev.js +10 -9
  176. package/out/zero/src/zero-cache-dev.js.map +1 -1
  177. package/out/zero/src/zero-out.js +2 -1
  178. package/out/zero/src/zero-out.js.map +1 -1
  179. package/out/zero-cache/src/auth/auth.js +1 -1
  180. package/out/zero-cache/src/auth/auth.js.map +1 -1
  181. package/out/zero-cache/src/auth/jwt.js +4 -1
  182. package/out/zero-cache/src/auth/jwt.js.map +1 -1
  183. package/out/zero-cache/src/auth/load-permissions.js.map +1 -1
  184. package/out/zero-cache/src/auth/read-authorizer.js +3 -2
  185. package/out/zero-cache/src/auth/read-authorizer.js.map +1 -1
  186. package/out/zero-cache/src/auth/write-authorizer.js +5 -16
  187. package/out/zero-cache/src/auth/write-authorizer.js.map +1 -1
  188. package/out/zero-cache/src/config/network.js +1 -1
  189. package/out/zero-cache/src/config/network.js.map +1 -1
  190. package/out/zero-cache/src/config/server-context.js.map +1 -1
  191. package/out/zero-cache/src/config/zero-config.d.ts +5 -0
  192. package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
  193. package/out/zero-cache/src/config/zero-config.js +13 -2
  194. package/out/zero-cache/src/config/zero-config.js.map +1 -1
  195. package/out/zero-cache/src/custom/fetch.js +1 -1
  196. package/out/zero-cache/src/custom/fetch.js.map +1 -1
  197. package/out/zero-cache/src/custom-queries/transform-query.js.map +1 -1
  198. package/out/zero-cache/src/db/create.js.map +1 -1
  199. package/out/zero-cache/src/db/index-predicate.js.map +1 -1
  200. package/out/zero-cache/src/db/lite-tables.d.ts +2 -2
  201. package/out/zero-cache/src/db/lite-tables.d.ts.map +1 -1
  202. package/out/zero-cache/src/db/lite-tables.js.map +1 -1
  203. package/out/zero-cache/src/db/migration-lite.js +1 -1
  204. package/out/zero-cache/src/db/migration-lite.js.map +1 -1
  205. package/out/zero-cache/src/db/migration.js +5 -3
  206. package/out/zero-cache/src/db/migration.js.map +1 -1
  207. package/out/zero-cache/src/db/pg-copy-binary.js +4 -3
  208. package/out/zero-cache/src/db/pg-copy-binary.js.map +1 -1
  209. package/out/zero-cache/src/db/pg-copy.js +0 -1
  210. package/out/zero-cache/src/db/pg-copy.js.map +1 -1
  211. package/out/zero-cache/src/db/pg-to-lite.js +1 -1
  212. package/out/zero-cache/src/db/pg-to-lite.js.map +1 -1
  213. package/out/zero-cache/src/db/run-transaction.js.map +1 -1
  214. package/out/zero-cache/src/db/specs.js +7 -5
  215. package/out/zero-cache/src/db/specs.js.map +1 -1
  216. package/out/zero-cache/src/db/sqlite-corruption.js.map +1 -1
  217. package/out/zero-cache/src/db/statements.d.ts.map +1 -1
  218. package/out/zero-cache/src/db/statements.js +9 -2
  219. package/out/zero-cache/src/db/statements.js.map +1 -1
  220. package/out/zero-cache/src/db/transaction-pool.js +5 -3
  221. package/out/zero-cache/src/db/transaction-pool.js.map +1 -1
  222. package/out/zero-cache/src/db/warmup.js.map +1 -1
  223. package/out/zero-cache/src/observability/events.js.map +1 -1
  224. package/out/zero-cache/src/observability/log-throttle.js.map +1 -1
  225. package/out/zero-cache/src/observability/metrics.js.map +1 -1
  226. package/out/zero-cache/src/scripts/deploy-permissions.js +4 -2
  227. package/out/zero-cache/src/scripts/deploy-permissions.js.map +1 -1
  228. package/out/zero-cache/src/scripts/permissions.js.map +1 -1
  229. package/out/zero-cache/src/server/anonymous-otel-start.js +2 -1
  230. package/out/zero-cache/src/server/anonymous-otel-start.js.map +1 -1
  231. package/out/zero-cache/src/server/change-streamer.js.map +1 -1
  232. package/out/zero-cache/src/server/inspector-delegate.js.map +1 -1
  233. package/out/zero-cache/src/server/logging.js +1 -1
  234. package/out/zero-cache/src/server/logging.js.map +1 -1
  235. package/out/zero-cache/src/server/main.js.map +1 -1
  236. package/out/zero-cache/src/server/otel-log-sink.js.map +1 -1
  237. package/out/zero-cache/src/server/otel-start.js.map +1 -1
  238. package/out/zero-cache/src/server/reaper.js +2 -2
  239. package/out/zero-cache/src/server/reaper.js.map +1 -1
  240. package/out/zero-cache/src/server/replicator.js.map +1 -1
  241. package/out/zero-cache/src/server/runner/run-worker.js +2 -1
  242. package/out/zero-cache/src/server/runner/run-worker.js.map +1 -1
  243. package/out/zero-cache/src/server/runner/runtime.js +2 -1
  244. package/out/zero-cache/src/server/runner/runtime.js.map +1 -1
  245. package/out/zero-cache/src/server/runner/zero-dispatcher.js.map +1 -1
  246. package/out/zero-cache/src/server/shadow-syncer.js +1 -1
  247. package/out/zero-cache/src/server/shadow-syncer.js.map +1 -1
  248. package/out/zero-cache/src/server/slot-keeper.js.map +1 -1
  249. package/out/zero-cache/src/server/syncer-assigner.js.map +1 -1
  250. package/out/zero-cache/src/server/syncer.d.ts.map +1 -1
  251. package/out/zero-cache/src/server/syncer.js +6 -1
  252. package/out/zero-cache/src/server/syncer.js.map +1 -1
  253. package/out/zero-cache/src/server/worker-dispatcher.js +1 -1
  254. package/out/zero-cache/src/server/worker-dispatcher.js.map +1 -1
  255. package/out/zero-cache/src/server/worker-urls.js.map +1 -1
  256. package/out/zero-cache/src/services/analyze.js +1 -1
  257. package/out/zero-cache/src/services/analyze.js.map +1 -1
  258. package/out/zero-cache/src/services/change-source/common/backfill-manager.js +1 -1
  259. package/out/zero-cache/src/services/change-source/common/backfill-manager.js.map +1 -1
  260. package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.js.map +1 -1
  261. package/out/zero-cache/src/services/change-source/common/init-cleanup.js.map +1 -1
  262. package/out/zero-cache/src/services/change-source/common/replica-restore.js.map +1 -1
  263. package/out/zero-cache/src/services/change-source/common/replica-schema.js +3 -3
  264. package/out/zero-cache/src/services/change-source/common/replica-schema.js.map +1 -1
  265. package/out/zero-cache/src/services/change-source/custom/change-source.js +3 -3
  266. package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
  267. package/out/zero-cache/src/services/change-source/pg/backfill-metadata.js.map +1 -1
  268. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js +3 -3
  269. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js.map +1 -1
  270. package/out/zero-cache/src/services/change-source/pg/change-source-init.js +1 -1
  271. package/out/zero-cache/src/services/change-source/pg/change-source-init.js.map +1 -1
  272. package/out/zero-cache/src/services/change-source/pg/change-source.js +4 -6
  273. package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
  274. package/out/zero-cache/src/services/change-source/pg/decommission.js +2 -1
  275. package/out/zero-cache/src/services/change-source/pg/decommission.js.map +1 -1
  276. package/out/zero-cache/src/services/change-source/pg/initial-sync.js +5 -3
  277. package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
  278. package/out/zero-cache/src/services/change-source/pg/logical-replication/binary-reader.js.map +1 -1
  279. package/out/zero-cache/src/services/change-source/pg/logical-replication/pgoutput-parser.js.map +1 -1
  280. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js +3 -2
  281. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js.map +1 -1
  282. package/out/zero-cache/src/services/change-source/pg/lsn.js.map +1 -1
  283. package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.js +2 -2
  284. package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.js.map +1 -1
  285. package/out/zero-cache/src/services/change-source/pg/replication-slot-health.js +1 -1
  286. package/out/zero-cache/src/services/change-source/pg/replication-slot-health.js.map +1 -1
  287. package/out/zero-cache/src/services/change-source/pg/replication-slots.js +3 -3
  288. package/out/zero-cache/src/services/change-source/pg/replication-slots.js.map +1 -1
  289. package/out/zero-cache/src/services/change-source/pg/schema/ddl.js +1 -1
  290. package/out/zero-cache/src/services/change-source/pg/schema/ddl.js.map +1 -1
  291. package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.js +13 -12
  292. package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.js.map +1 -1
  293. package/out/zero-cache/src/services/change-source/pg/schema/init.js.map +1 -1
  294. package/out/zero-cache/src/services/change-source/pg/schema/published.js +1 -1
  295. package/out/zero-cache/src/services/change-source/pg/schema/published.js.map +1 -1
  296. package/out/zero-cache/src/services/change-source/pg/schema/replica-stage-enum.js.map +1 -1
  297. package/out/zero-cache/src/services/change-source/pg/schema/shard.js +9 -6
  298. package/out/zero-cache/src/services/change-source/pg/schema/shard.js.map +1 -1
  299. package/out/zero-cache/src/services/change-source/pg/schema/validation.js +1 -1
  300. package/out/zero-cache/src/services/change-source/pg/schema/validation.js.map +1 -1
  301. package/out/zero-cache/src/services/change-source/protocol/backfill-progress.js +1 -1
  302. package/out/zero-cache/src/services/change-source/protocol/backfill-progress.js.map +1 -1
  303. package/out/zero-cache/src/services/change-source/protocol/current/control.js.map +1 -1
  304. package/out/zero-cache/src/services/change-source/protocol/current/data.js.map +1 -1
  305. package/out/zero-cache/src/services/change-source/protocol/current/downstream.js.map +1 -1
  306. package/out/zero-cache/src/services/change-source/protocol/current/status.js.map +1 -1
  307. package/out/zero-cache/src/services/change-source/protocol/current/upstream.js.map +1 -1
  308. package/out/zero-cache/src/services/change-source/protocol/current.js +1 -1
  309. package/out/zero-cache/src/services/change-source/protocol/mod.js +1 -2
  310. package/out/zero-cache/src/services/change-streamer/backfill-state-tracker.js.map +1 -1
  311. package/out/zero-cache/src/services/change-streamer/backfill-state.js +1 -1
  312. package/out/zero-cache/src/services/change-streamer/backfill-state.js.map +1 -1
  313. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.js.map +1 -1
  314. package/out/zero-cache/src/services/change-streamer/backup-monitor.js.map +1 -1
  315. package/out/zero-cache/src/services/change-streamer/broadcast.js +1 -1
  316. package/out/zero-cache/src/services/change-streamer/broadcast.js.map +1 -1
  317. package/out/zero-cache/src/services/change-streamer/change-log-codec.d.ts +1 -1
  318. package/out/zero-cache/src/services/change-streamer/change-log-codec.d.ts.map +1 -1
  319. package/out/zero-cache/src/services/change-streamer/change-log-codec.js +2 -3
  320. package/out/zero-cache/src/services/change-streamer/change-log-codec.js.map +1 -1
  321. package/out/zero-cache/src/services/change-streamer/change-log-initializer.js +2 -2
  322. package/out/zero-cache/src/services/change-streamer/change-log-initializer.js.map +1 -1
  323. package/out/zero-cache/src/services/change-streamer/change-log-transaction-hash.js.map +1 -1
  324. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +5 -3
  325. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
  326. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +21 -21
  327. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
  328. package/out/zero-cache/src/services/change-streamer/change-streamer.js +1 -1
  329. package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
  330. package/out/zero-cache/src/services/change-streamer/drain-tracker.js.map +1 -1
  331. package/out/zero-cache/src/services/change-streamer/forwarder.js +11 -10
  332. package/out/zero-cache/src/services/change-streamer/forwarder.js.map +1 -1
  333. package/out/zero-cache/src/services/change-streamer/litestream-sync-requester.js +1 -1
  334. package/out/zero-cache/src/services/change-streamer/litestream-sync-requester.js.map +1 -1
  335. package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.js +3 -3
  336. package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.js.map +1 -1
  337. package/out/zero-cache/src/services/change-streamer/replica-poller.js +1 -1
  338. package/out/zero-cache/src/services/change-streamer/replica-poller.js.map +1 -1
  339. package/out/zero-cache/src/services/change-streamer/restore-admission.js +12 -1
  340. package/out/zero-cache/src/services/change-streamer/restore-admission.js.map +1 -1
  341. package/out/zero-cache/src/services/change-streamer/schema/init.js +1 -1
  342. package/out/zero-cache/src/services/change-streamer/schema/init.js.map +1 -1
  343. package/out/zero-cache/src/services/change-streamer/schema/tables.js +1 -1
  344. package/out/zero-cache/src/services/change-streamer/schema/tables.js.map +1 -1
  345. package/out/zero-cache/src/services/change-streamer/snapshot-message.js +1 -1
  346. package/out/zero-cache/src/services/change-streamer/snapshot-message.js.map +1 -1
  347. package/out/zero-cache/src/services/change-streamer/snapshot-reservations.js.map +1 -1
  348. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.js +1 -1
  349. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.js.map +1 -1
  350. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-comparator.js +2 -2
  351. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-comparator.js.map +1 -1
  352. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-purge-scheduler.js.map +1 -1
  353. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-read-router.js.map +1 -1
  354. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.d.ts.map +1 -1
  355. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.js +4 -6
  356. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.js.map +1 -1
  357. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.js +1 -3
  358. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.js.map +1 -1
  359. package/out/zero-cache/src/services/change-streamer/storer.d.ts.map +1 -1
  360. package/out/zero-cache/src/services/change-streamer/storer.js +2 -2
  361. package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
  362. package/out/zero-cache/src/services/change-streamer/subscribe.js +1 -1
  363. package/out/zero-cache/src/services/change-streamer/subscribe.js.map +1 -1
  364. package/out/zero-cache/src/services/change-streamer/subscriber.js +2 -2
  365. package/out/zero-cache/src/services/change-streamer/subscriber.js.map +1 -1
  366. package/out/zero-cache/src/services/change-streamer/upstream-acker.js +0 -1
  367. package/out/zero-cache/src/services/change-streamer/upstream-acker.js.map +1 -1
  368. package/out/zero-cache/src/services/heapz.js +2 -1
  369. package/out/zero-cache/src/services/heapz.js.map +1 -1
  370. package/out/zero-cache/src/services/http-service.js.map +1 -1
  371. package/out/zero-cache/src/services/life-cycle.js +1 -1
  372. package/out/zero-cache/src/services/life-cycle.js.map +1 -1
  373. package/out/zero-cache/src/services/limiter/sliding-window-limiter.js +4 -2
  374. package/out/zero-cache/src/services/limiter/sliding-window-limiter.js.map +1 -1
  375. package/out/zero-cache/src/services/litestream/commands.js +1 -1
  376. package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
  377. package/out/zero-cache/src/services/litestream/litestream-checkpointer.js +2 -2
  378. package/out/zero-cache/src/services/litestream/litestream-checkpointer.js.map +1 -1
  379. package/out/zero-cache/src/services/litestream/litestream-controller.js.map +1 -1
  380. package/out/zero-cache/src/services/litestream/metrics.js.map +1 -1
  381. package/out/zero-cache/src/services/litestream/restore-progress.js.map +1 -1
  382. package/out/zero-cache/src/services/mutagen/mutagen.js +1 -1
  383. package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
  384. package/out/zero-cache/src/services/mutagen/pusher.js +3 -2
  385. package/out/zero-cache/src/services/mutagen/pusher.js.map +1 -1
  386. package/out/zero-cache/src/services/plannerz.js +4 -3
  387. package/out/zero-cache/src/services/plannerz.js.map +1 -1
  388. package/out/zero-cache/src/services/profz.js +5 -3
  389. package/out/zero-cache/src/services/profz.js.map +1 -1
  390. package/out/zero-cache/src/services/replicator/change-log-cookies.js.map +1 -1
  391. package/out/zero-cache/src/services/replicator/change-log-db.js +3 -2
  392. package/out/zero-cache/src/services/replicator/change-log-db.js.map +1 -1
  393. package/out/zero-cache/src/services/replicator/change-log-stream-writer.js +1 -1
  394. package/out/zero-cache/src/services/replicator/change-log-stream-writer.js.map +1 -1
  395. package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
  396. package/out/zero-cache/src/services/replicator/drain-handler.js.map +1 -1
  397. package/out/zero-cache/src/services/replicator/incremental-sync.js +1 -1
  398. package/out/zero-cache/src/services/replicator/incremental-sync.js.map +1 -1
  399. package/out/zero-cache/src/services/replicator/notifier.js.map +1 -1
  400. package/out/zero-cache/src/services/replicator/replication-status.js +4 -3
  401. package/out/zero-cache/src/services/replicator/replication-status.js.map +1 -1
  402. package/out/zero-cache/src/services/replicator/replicator.js.map +1 -1
  403. package/out/zero-cache/src/services/replicator/reporter/recorder.js +1 -1
  404. package/out/zero-cache/src/services/replicator/reporter/recorder.js.map +1 -1
  405. package/out/zero-cache/src/services/replicator/reporter/report-schema.js +1 -1
  406. package/out/zero-cache/src/services/replicator/reporter/report-schema.js.map +1 -1
  407. package/out/zero-cache/src/services/replicator/schema/backfilling.js +1 -1
  408. package/out/zero-cache/src/services/replicator/schema/backfilling.js.map +1 -1
  409. package/out/zero-cache/src/services/replicator/schema/change-log.d.ts +19 -7
  410. package/out/zero-cache/src/services/replicator/schema/change-log.d.ts.map +1 -1
  411. package/out/zero-cache/src/services/replicator/schema/change-log.js +31 -15
  412. package/out/zero-cache/src/services/replicator/schema/change-log.js.map +1 -1
  413. package/out/zero-cache/src/services/replicator/schema/column-metadata.js +1 -1
  414. package/out/zero-cache/src/services/replicator/schema/column-metadata.js.map +1 -1
  415. package/out/zero-cache/src/services/replicator/schema/replication-state.js +10 -7
  416. package/out/zero-cache/src/services/replicator/schema/replication-state.js.map +1 -1
  417. package/out/zero-cache/src/services/replicator/schema/table-metadata.js.map +1 -1
  418. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js +1 -1
  419. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js.map +1 -1
  420. package/out/zero-cache/src/services/replicator/sqlite-change-log-purger.js +1 -1
  421. package/out/zero-cache/src/services/replicator/sqlite-change-log-purger.js.map +1 -1
  422. package/out/zero-cache/src/services/replicator/write-worker-client.js +1 -1
  423. package/out/zero-cache/src/services/replicator/write-worker-client.js.map +1 -1
  424. package/out/zero-cache/src/services/replicator/write-worker.js.map +1 -1
  425. package/out/zero-cache/src/services/run-ast.js.map +1 -1
  426. package/out/zero-cache/src/services/runner.js.map +1 -1
  427. package/out/zero-cache/src/services/running-state.js.map +1 -1
  428. package/out/zero-cache/src/services/shadow-sync/shadow-sync-service.js.map +1 -1
  429. package/out/zero-cache/src/services/statz.js +2 -1
  430. package/out/zero-cache/src/services/statz.js.map +1 -1
  431. package/out/zero-cache/src/services/view-syncer/active-users-gauge.js +2 -2
  432. package/out/zero-cache/src/services/view-syncer/active-users-gauge.js.map +1 -1
  433. package/out/zero-cache/src/services/view-syncer/advancement-metrics.js.map +1 -1
  434. package/out/zero-cache/src/services/view-syncer/batch-pacer.js.map +1 -1
  435. package/out/zero-cache/src/services/view-syncer/client-handler.d.ts.map +1 -1
  436. package/out/zero-cache/src/services/view-syncer/client-handler.js +24 -3
  437. package/out/zero-cache/src/services/view-syncer/client-handler.js.map +1 -1
  438. package/out/zero-cache/src/services/view-syncer/client-schema.d.ts +15 -2
  439. package/out/zero-cache/src/services/view-syncer/client-schema.d.ts.map +1 -1
  440. package/out/zero-cache/src/services/view-syncer/client-schema.js +46 -6
  441. package/out/zero-cache/src/services/view-syncer/client-schema.js.map +1 -1
  442. package/out/zero-cache/src/services/view-syncer/connection-context-manager.js.map +1 -1
  443. package/out/zero-cache/src/services/view-syncer/cvr-purger.js +2 -2
  444. package/out/zero-cache/src/services/view-syncer/cvr-purger.js.map +1 -1
  445. package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts.map +1 -1
  446. package/out/zero-cache/src/services/view-syncer/cvr-store.js +10 -5
  447. package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
  448. package/out/zero-cache/src/services/view-syncer/cvr.js +12 -4
  449. package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
  450. package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.js +1 -1
  451. package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.js.map +1 -1
  452. package/out/zero-cache/src/services/view-syncer/drain-coordinator.js.map +1 -1
  453. package/out/zero-cache/src/services/view-syncer/e2e-serving-lag.js.map +1 -1
  454. package/out/zero-cache/src/services/view-syncer/hydration-budget.js.map +1 -1
  455. package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.js +2 -2
  456. package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.js.map +1 -1
  457. package/out/zero-cache/src/services/view-syncer/inspect-handler.js +5 -3
  458. package/out/zero-cache/src/services/view-syncer/inspect-handler.js.map +1 -1
  459. package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts.map +1 -1
  460. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +8 -5
  461. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
  462. package/out/zero-cache/src/services/view-syncer/plan-warnings.js.map +1 -1
  463. package/out/zero-cache/src/services/view-syncer/push-cost.js.map +1 -1
  464. package/out/zero-cache/src/services/view-syncer/push-cutoff.js.map +1 -1
  465. package/out/zero-cache/src/services/view-syncer/query-covering.js.map +1 -1
  466. package/out/zero-cache/src/services/view-syncer/query-shape.js.map +1 -1
  467. package/out/zero-cache/src/services/view-syncer/query-stats.js +1 -1
  468. package/out/zero-cache/src/services/view-syncer/query-stats.js.map +1 -1
  469. package/out/zero-cache/src/services/view-syncer/row-record-cache.js.map +1 -1
  470. package/out/zero-cache/src/services/view-syncer/schema/cvr.js +1 -1
  471. package/out/zero-cache/src/services/view-syncer/schema/types.js +1 -1
  472. package/out/zero-cache/src/services/view-syncer/schema/types.js.map +1 -1
  473. package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.js +7 -1
  474. package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.js.map +1 -1
  475. package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts +8 -1
  476. package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts.map +1 -1
  477. package/out/zero-cache/src/services/view-syncer/snapshotter.js +6 -5
  478. package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
  479. package/out/zero-cache/src/services/view-syncer/ttl-clock.js.map +1 -1
  480. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
  481. package/out/zero-cache/src/services/view-syncer/view-syncer.js +13 -9
  482. package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
  483. package/out/zero-cache/src/types/error-with-level.js.map +1 -1
  484. package/out/zero-cache/src/types/lexi-version.js.map +1 -1
  485. package/out/zero-cache/src/types/lite.js +1 -1
  486. package/out/zero-cache/src/types/lite.js.map +1 -1
  487. package/out/zero-cache/src/types/pg-data-type.js +1 -1
  488. package/out/zero-cache/src/types/pg-data-type.js.map +1 -1
  489. package/out/zero-cache/src/types/pg.js +6 -4
  490. package/out/zero-cache/src/types/pg.js.map +1 -1
  491. package/out/zero-cache/src/types/poke-chunk.js +2 -2
  492. package/out/zero-cache/src/types/poke-chunk.js.map +1 -1
  493. package/out/zero-cache/src/types/processes.js +1 -1
  494. package/out/zero-cache/src/types/processes.js.map +1 -1
  495. package/out/zero-cache/src/types/profiler.js.map +1 -1
  496. package/out/zero-cache/src/types/row-key.js.map +1 -1
  497. package/out/zero-cache/src/types/sql.js.map +1 -1
  498. package/out/zero-cache/src/types/streams.js +10 -9
  499. package/out/zero-cache/src/types/streams.js.map +1 -1
  500. package/out/zero-cache/src/types/strings.js.map +1 -1
  501. package/out/zero-cache/src/types/subscription.js.map +1 -1
  502. package/out/zero-cache/src/types/timeout.js +1 -1
  503. package/out/zero-cache/src/types/websocket-handoff.js +1 -1
  504. package/out/zero-cache/src/types/ws.js +1 -1
  505. package/out/zero-cache/src/workers/connection.js +5 -6
  506. package/out/zero-cache/src/workers/connection.js.map +1 -1
  507. package/out/zero-cache/src/workers/mutator.js.map +1 -1
  508. package/out/zero-cache/src/workers/replicator.js +5 -4
  509. package/out/zero-cache/src/workers/replicator.js.map +1 -1
  510. package/out/zero-cache/src/workers/syncer-ws-message-handler.d.ts.map +1 -1
  511. package/out/zero-cache/src/workers/syncer-ws-message-handler.js +9 -1
  512. package/out/zero-cache/src/workers/syncer-ws-message-handler.js.map +1 -1
  513. package/out/zero-cache/src/workers/syncer.js +4 -4
  514. package/out/zero-cache/src/workers/syncer.js.map +1 -1
  515. package/out/zero-client/src/client/active-clients-manager.js +1 -1
  516. package/out/zero-client/src/client/active-clients-manager.js.map +1 -1
  517. package/out/zero-client/src/client/bindings.js +11 -11
  518. package/out/zero-client/src/client/connection-manager.js.map +1 -1
  519. package/out/zero-client/src/client/connection.js +1 -1
  520. package/out/zero-client/src/client/connection.js.map +1 -1
  521. package/out/zero-client/src/client/context.js.map +1 -1
  522. package/out/zero-client/src/client/crud-impl.js +1 -1
  523. package/out/zero-client/src/client/crud-impl.js.map +1 -1
  524. package/out/zero-client/src/client/crud.js +1 -1
  525. package/out/zero-client/src/client/crud.js.map +1 -1
  526. package/out/zero-client/src/client/custom.js +1 -1
  527. package/out/zero-client/src/client/custom.js.map +1 -1
  528. package/out/zero-client/src/client/delete-clients-manager.js.map +1 -1
  529. package/out/zero-client/src/client/enable-analytics.js +1 -1
  530. package/out/zero-client/src/client/enable-analytics.js.map +1 -1
  531. package/out/zero-client/src/client/error.js.map +1 -1
  532. package/out/zero-client/src/client/inspector/client-group.js.map +1 -1
  533. package/out/zero-client/src/client/inspector/client.js.map +1 -1
  534. package/out/zero-client/src/client/inspector/html-dialog-prompt.js +1 -1
  535. package/out/zero-client/src/client/inspector/html-dialog-prompt.js.map +1 -1
  536. package/out/zero-client/src/client/inspector/inspector.js.map +1 -1
  537. package/out/zero-client/src/client/inspector/lazy-inspector.js.map +1 -1
  538. package/out/zero-client/src/client/inspector/query.js.map +1 -1
  539. package/out/zero-client/src/client/ivm-branch.js +1 -3
  540. package/out/zero-client/src/client/ivm-branch.js.map +1 -1
  541. package/out/zero-client/src/client/keys.js +2 -1
  542. package/out/zero-client/src/client/keys.js.map +1 -1
  543. package/out/zero-client/src/client/log-options.js.map +1 -1
  544. package/out/zero-client/src/client/make-replicache-mutators.js +5 -3
  545. package/out/zero-client/src/client/make-replicache-mutators.js.map +1 -1
  546. package/out/zero-client/src/client/metrics.js +3 -5
  547. package/out/zero-client/src/client/metrics.js.map +1 -1
  548. package/out/zero-client/src/client/mutation-tracker.js.map +1 -1
  549. package/out/zero-client/src/client/mutator-proxy.js.map +1 -1
  550. package/out/zero-client/src/client/options.js.map +1 -1
  551. package/out/zero-client/src/client/query-manager.js +2 -2
  552. package/out/zero-client/src/client/query-manager.js.map +1 -1
  553. package/out/zero-client/src/client/reload-error-handler.js +4 -3
  554. package/out/zero-client/src/client/reload-error-handler.js.map +1 -1
  555. package/out/zero-client/src/client/version.js +1 -1
  556. package/out/zero-client/src/client/zero-poke-handler.js +1 -1
  557. package/out/zero-client/src/client/zero-poke-handler.js.map +1 -1
  558. package/out/zero-client/src/client/zero-rep.js.map +1 -1
  559. package/out/zero-client/src/client/zero.js +19 -11
  560. package/out/zero-client/src/client/zero.js.map +1 -1
  561. package/out/zero-client/src/mod.js +23 -23
  562. package/out/zero-client/src/util/nanoid.js.map +1 -1
  563. package/out/zero-permissions/src/permissions.js +3 -2
  564. package/out/zero-permissions/src/permissions.js.map +1 -1
  565. package/out/zero-pg/src/mod.js +9 -9
  566. package/out/zero-protocol/src/analyze-query-result.js +1 -1
  567. package/out/zero-protocol/src/analyze-query-result.js.map +1 -1
  568. package/out/zero-protocol/src/application-error.js.map +1 -1
  569. package/out/zero-protocol/src/ast.js +19 -9
  570. package/out/zero-protocol/src/ast.js.map +1 -1
  571. package/out/zero-protocol/src/change-desired-queries.js.map +1 -1
  572. package/out/zero-protocol/src/client-schema.js +1 -1
  573. package/out/zero-protocol/src/client-schema.js.map +1 -1
  574. package/out/zero-protocol/src/close-connection.js +1 -1
  575. package/out/zero-protocol/src/close-connection.js.map +1 -1
  576. package/out/zero-protocol/src/connect.js +1 -1
  577. package/out/zero-protocol/src/connect.js.map +1 -1
  578. package/out/zero-protocol/src/custom-queries.js +1 -1
  579. package/out/zero-protocol/src/custom-queries.js.map +1 -1
  580. package/out/zero-protocol/src/data.js +4 -2
  581. package/out/zero-protocol/src/data.js.map +1 -1
  582. package/out/zero-protocol/src/delete-clients.js.map +1 -1
  583. package/out/zero-protocol/src/down.d.ts +27 -27
  584. package/out/zero-protocol/src/down.js.map +1 -1
  585. package/out/zero-protocol/src/error.js.map +1 -1
  586. package/out/zero-protocol/src/inspect-down.d.ts +81 -81
  587. package/out/zero-protocol/src/inspect-down.js +1 -1
  588. package/out/zero-protocol/src/inspect-down.js.map +1 -1
  589. package/out/zero-protocol/src/inspect-up.js +1 -1
  590. package/out/zero-protocol/src/inspect-up.js.map +1 -1
  591. package/out/zero-protocol/src/mutate-server.js +1 -1
  592. package/out/zero-protocol/src/mutate-server.js.map +1 -1
  593. package/out/zero-protocol/src/mutation-id.js.map +1 -1
  594. package/out/zero-protocol/src/mutation.js +1 -1
  595. package/out/zero-protocol/src/mutation.js.map +1 -1
  596. package/out/zero-protocol/src/mutations-patch.js +1 -1
  597. package/out/zero-protocol/src/mutations-patch.js.map +1 -1
  598. package/out/zero-protocol/src/ping.js +1 -1
  599. package/out/zero-protocol/src/ping.js.map +1 -1
  600. package/out/zero-protocol/src/poke.d.ts +6 -6
  601. package/out/zero-protocol/src/poke.js +1 -1
  602. package/out/zero-protocol/src/poke.js.map +1 -1
  603. package/out/zero-protocol/src/pong.js +1 -1
  604. package/out/zero-protocol/src/pong.js.map +1 -1
  605. package/out/zero-protocol/src/primary-key.js.map +1 -1
  606. package/out/zero-protocol/src/protocol-version.js +1 -0
  607. package/out/zero-protocol/src/protocol-version.js.map +1 -1
  608. package/out/zero-protocol/src/pull.js +1 -1
  609. package/out/zero-protocol/src/pull.js.map +1 -1
  610. package/out/zero-protocol/src/push.js +1 -1
  611. package/out/zero-protocol/src/push.js.map +1 -1
  612. package/out/zero-protocol/src/queries-patch.js +1 -1
  613. package/out/zero-protocol/src/queries-patch.js.map +1 -1
  614. package/out/zero-protocol/src/query-hash-visitor.js.map +1 -1
  615. package/out/zero-protocol/src/query-hash.js +2 -2
  616. package/out/zero-protocol/src/query-server.js +1 -1
  617. package/out/zero-protocol/src/query-server.js.map +1 -1
  618. package/out/zero-protocol/src/row-patch.js.map +1 -1
  619. package/out/zero-protocol/src/up.d.ts +60 -60
  620. package/out/zero-protocol/src/up.js.map +1 -1
  621. package/out/zero-protocol/src/update-auth.js.map +1 -1
  622. package/out/zero-protocol/src/version.js.map +1 -1
  623. package/out/zero-react/src/bindings.js +11 -11
  624. package/out/zero-react/src/mod.js +5 -5
  625. package/out/zero-react/src/use-query.js +1 -1
  626. package/out/zero-react/src/use-query.js.map +1 -1
  627. package/out/zero-react/src/zero-provider.d.ts +1 -1
  628. package/out/zero-react/src/zero-provider.d.ts.map +1 -1
  629. package/out/zero-react/src/zero-provider.js +2 -1
  630. package/out/zero-react/src/zero-provider.js.map +1 -1
  631. package/out/zero-react/src/zero.js +23 -23
  632. package/out/zero-schema/src/builder/schema-builder.js +3 -2
  633. package/out/zero-schema/src/builder/schema-builder.js.map +1 -1
  634. package/out/zero-schema/src/builder/table-builder.js +1 -1
  635. package/out/zero-schema/src/builder/table-builder.js.map +1 -1
  636. package/out/zero-schema/src/compiled-permissions.js.map +1 -1
  637. package/out/zero-schema/src/name-mapper.js +7 -5
  638. package/out/zero-schema/src/name-mapper.js.map +1 -1
  639. package/out/zero-schema/src/schema-config.js +1 -1
  640. package/out/zero-schema/src/schema-config.js.map +1 -1
  641. package/out/zero-server/src/adapters/drizzle.js.map +1 -1
  642. package/out/zero-server/src/adapters/kysely.js.map +1 -1
  643. package/out/zero-server/src/adapters/pg.js.map +1 -1
  644. package/out/zero-server/src/adapters/postgresjs.js.map +1 -1
  645. package/out/zero-server/src/adapters/prisma.js.map +1 -1
  646. package/out/zero-server/src/closable-db-transaction.js.map +1 -1
  647. package/out/zero-server/src/custom.js +1 -1
  648. package/out/zero-server/src/custom.js.map +1 -1
  649. package/out/zero-server/src/mod.js +8 -8
  650. package/out/zero-server/src/process-mutations.js.map +1 -1
  651. package/out/zero-server/src/push-processor.js.map +1 -1
  652. package/out/zero-server/src/queries/process-queries.js +2 -1
  653. package/out/zero-server/src/queries/process-queries.js.map +1 -1
  654. package/out/zero-server/src/schema.js +7 -7
  655. package/out/zero-server/src/schema.js.map +1 -1
  656. package/out/zero-server/src/zql-database.js.map +1 -1
  657. package/out/zero-solid/src/bindings.js +11 -11
  658. package/out/zero-solid/src/mod.js +5 -5
  659. package/out/zero-solid/src/solid-view.js +1 -1
  660. package/out/zero-solid/src/solid-view.js.map +1 -1
  661. package/out/zero-solid/src/use-connection-state.js +2 -1
  662. package/out/zero-solid/src/use-connection-state.js.map +1 -1
  663. package/out/zero-solid/src/use-query.js.map +1 -1
  664. package/out/zero-solid/src/use-zero-online.js +2 -1
  665. package/out/zero-solid/src/use-zero-online.js.map +1 -1
  666. package/out/zero-solid/src/use-zero.js +3 -2
  667. package/out/zero-solid/src/use-zero.js.map +1 -1
  668. package/out/zero-solid/src/zero.js +23 -23
  669. package/out/zero-types/src/name-mapper.js.map +1 -1
  670. package/out/zql/src/builder/builder.js +15 -13
  671. package/out/zql/src/builder/builder.js.map +1 -1
  672. package/out/zql/src/builder/correlated-predicate-pushdown.js.map +1 -1
  673. package/out/zql/src/builder/debug-delegate.js.map +1 -1
  674. package/out/zql/src/builder/like.js +0 -1
  675. package/out/zql/src/builder/like.js.map +1 -1
  676. package/out/zql/src/ivm/array-view.js.map +1 -1
  677. package/out/zql/src/ivm/cap.js.map +1 -1
  678. package/out/zql/src/ivm/constraint.js +1 -1
  679. package/out/zql/src/ivm/constraint.js.map +1 -1
  680. package/out/zql/src/ivm/data.js.map +1 -1
  681. package/out/zql/src/ivm/deferred-input.js.map +1 -1
  682. package/out/zql/src/ivm/exists.js +20 -18
  683. package/out/zql/src/ivm/exists.js.map +1 -1
  684. package/out/zql/src/ivm/fan-in.js.map +1 -1
  685. package/out/zql/src/ivm/fan-out.js.map +1 -1
  686. package/out/zql/src/ivm/filter-operators.js +1 -1
  687. package/out/zql/src/ivm/filter-operators.js.map +1 -1
  688. package/out/zql/src/ivm/filter-push.js.map +1 -1
  689. package/out/zql/src/ivm/filter.js.map +1 -1
  690. package/out/zql/src/ivm/flipped-join.js +1 -3
  691. package/out/zql/src/ivm/flipped-join.js.map +1 -1
  692. package/out/zql/src/ivm/join-index-tap.js.map +1 -1
  693. package/out/zql/src/ivm/join-index.js.map +1 -1
  694. package/out/zql/src/ivm/join-utils.js +11 -13
  695. package/out/zql/src/ivm/join-utils.js.map +1 -1
  696. package/out/zql/src/ivm/join.js.map +1 -1
  697. package/out/zql/src/ivm/maybe-split-and-push-edit-change.js.map +1 -1
  698. package/out/zql/src/ivm/memory-source.js +26 -22
  699. package/out/zql/src/ivm/memory-source.js.map +1 -1
  700. package/out/zql/src/ivm/memory-storage.js.map +1 -1
  701. package/out/zql/src/ivm/operator.js +2 -2
  702. package/out/zql/src/ivm/push-accumulated.js +0 -1
  703. package/out/zql/src/ivm/push-accumulated.js.map +1 -1
  704. package/out/zql/src/ivm/skip-yields.js.map +1 -1
  705. package/out/zql/src/ivm/skip.js.map +1 -1
  706. package/out/zql/src/ivm/take-gate.js.map +1 -1
  707. package/out/zql/src/ivm/take.js +1 -1
  708. package/out/zql/src/ivm/take.js.map +1 -1
  709. package/out/zql/src/ivm/union-fan-in.js +1 -1
  710. package/out/zql/src/ivm/union-fan-in.js.map +1 -1
  711. package/out/zql/src/ivm/union-fan-out.js.map +1 -1
  712. package/out/zql/src/ivm/view-apply-change.js +1 -1
  713. package/out/zql/src/ivm/view-apply-change.js.map +1 -1
  714. package/out/zql/src/mutate/crud.js +3 -2
  715. package/out/zql/src/mutate/crud.js.map +1 -1
  716. package/out/zql/src/planner/planner-builder.js +1 -1
  717. package/out/zql/src/planner/planner-builder.js.map +1 -1
  718. package/out/zql/src/planner/planner-connection.js.map +1 -1
  719. package/out/zql/src/planner/planner-debug.js.map +1 -1
  720. package/out/zql/src/planner/planner-fan-in.js.map +1 -1
  721. package/out/zql/src/planner/planner-fan-out.js.map +1 -1
  722. package/out/zql/src/planner/planner-filter.js.map +1 -1
  723. package/out/zql/src/planner/planner-graph.js +1 -5
  724. package/out/zql/src/planner/planner-graph.js.map +1 -1
  725. package/out/zql/src/planner/planner-join.js +1 -1
  726. package/out/zql/src/planner/planner-join.js.map +1 -1
  727. package/out/zql/src/planner/planner-source.js.map +1 -1
  728. package/out/zql/src/planner/planner-terminus.js.map +1 -1
  729. package/out/zql/src/query/create-builder.js +2 -1
  730. package/out/zql/src/query/create-builder.js.map +1 -1
  731. package/out/zql/src/query/expression.js +1 -1
  732. package/out/zql/src/query/expression.js.map +1 -1
  733. package/out/zql/src/query/measure-push-operator.js.map +1 -1
  734. package/out/zql/src/query/named.js +2 -1
  735. package/out/zql/src/query/named.js.map +1 -1
  736. package/out/zql/src/query/query-delegate-base.js +1 -1
  737. package/out/zql/src/query/query-delegate-base.js.map +1 -1
  738. package/out/zql/src/query/query-impl.js +3 -2
  739. package/out/zql/src/query/query-impl.js.map +1 -1
  740. package/out/zql/src/query/query-registry.js +1 -1
  741. package/out/zql/src/query/query-registry.js.map +1 -1
  742. package/out/zql/src/query/query-transitions.js +1 -1
  743. package/out/zql/src/query/query-transitions.js.map +1 -1
  744. package/out/zql/src/query/runnable-query-impl.js +3 -2
  745. package/out/zql/src/query/runnable-query-impl.js.map +1 -1
  746. package/out/zql/src/query/static-query.js.map +1 -1
  747. package/out/zql/src/query/ttl.js +8 -8
  748. package/out/zql/src/query/ttl.js.map +1 -1
  749. package/out/zql/src/query/validate-input.js.map +1 -1
  750. package/out/zqlite/src/database-storage.d.ts +27 -8
  751. package/out/zqlite/src/database-storage.d.ts.map +1 -1
  752. package/out/zqlite/src/database-storage.js +190 -53
  753. package/out/zqlite/src/database-storage.js.map +1 -1
  754. package/out/zqlite/src/db.d.ts +47 -1
  755. package/out/zqlite/src/db.d.ts.map +1 -1
  756. package/out/zqlite/src/db.js +121 -21
  757. package/out/zqlite/src/db.js.map +1 -1
  758. package/out/zqlite/src/explain-queries.js.map +1 -1
  759. package/out/zqlite/src/fetch-sql.js.map +1 -1
  760. package/out/zqlite/src/index-usage.js.map +1 -1
  761. package/out/zqlite/src/internal/sql-inline.js.map +1 -1
  762. package/out/zqlite/src/internal/sql.js.map +1 -1
  763. package/out/zqlite/src/internal/statement-cache.d.ts +7 -0
  764. package/out/zqlite/src/internal/statement-cache.d.ts.map +1 -1
  765. package/out/zqlite/src/internal/statement-cache.js +11 -4
  766. package/out/zqlite/src/internal/statement-cache.js.map +1 -1
  767. package/out/zqlite/src/mod.js +5 -5
  768. package/out/zqlite/src/pending-delta.js.map +1 -1
  769. package/out/zqlite/src/query-builder.js +1 -1
  770. package/out/zqlite/src/query-builder.js.map +1 -1
  771. package/out/zqlite/src/query-delegate.js.map +1 -1
  772. package/out/zqlite/src/resolve-scalar-subqueries.js +2 -5
  773. package/out/zqlite/src/resolve-scalar-subqueries.js.map +1 -1
  774. package/out/zqlite/src/sqlite-cost-model.js +3 -2
  775. package/out/zqlite/src/sqlite-cost-model.js.map +1 -1
  776. package/out/zqlite/src/sqlite-stat-fanout.js.map +1 -1
  777. package/out/zqlite/src/table-source.d.ts +11 -0
  778. package/out/zqlite/src/table-source.d.ts.map +1 -1
  779. package/out/zqlite/src/table-source.js +62 -25
  780. package/out/zqlite/src/table-source.js.map +1 -1
  781. package/package.json +17 -15
  782. package/out/otel/src/maybe-time.d.ts +0 -3
  783. package/out/otel/src/maybe-time.d.ts.map +0 -1
  784. package/out/otel/src/maybe-time.js +0 -15
  785. package/out/otel/src/maybe-time.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"push-accumulated.js","names":[],"sources":["../../../../../zql/src/ivm/push-accumulated.ts"],"sourcesContent":["import {assert, unreachable} from '../../../shared/src/asserts.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport {emptyArray} from '../../../shared/src/sentinels.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport {ChangeType} from './change-type.ts';\nimport {\n makeAddChange,\n makeChildChange,\n makeEditChange,\n makeRemoveChange,\n type Change,\n} from './change.ts';\nimport type {Node} from './data.ts';\nimport type {InputBase, Output} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport type {Stream} from './stream.ts';\n\n/**\n * # pushAccumulatedChanges\n *\n * Pushes the changes that were accumulated by\n * [fan-out, fan-in] or [ufo, ufi] sub-graphs.\n *\n * This function is called at the end of the sub-graph.\n *\n * The sub-graphs represents `OR`s.\n *\n * Changes that can enter the subgraphs:\n * 1. child (due to exist joins being above the sub-graph)\n * 2. add\n * 3. remove\n * 4. edit\n *\n * # Changes that can exit into `pushAccumulatedChanges`:\n *\n * ## Child\n * If a `child` change enters a sub-graph, it will flow to all branches.\n * Each branch will either:\n * - preserve the `child` change\n * - stop the `child` change (e.g., filter)\n * - convert it to an `add` or `remove` (e.g., exists filter)\n *\n * ## Add\n * If an `add` change enters a sub-graph, it will flow to all branches.\n * Each branch will either:\n * - preserve the `add` change\n * - hide the change (e.g., filter)\n *\n * ## Remove\n * If a `remove` change enters a sub-graph, it will flow to all branches.\n * Each branch will either:\n * - preserve the `remove` change\n * - hide the change (e.g., filter)\n *\n * ## Edit\n * If an `edit` change enters a sub-graph, it will flow to all branches.\n * Each branch will either:\n * - preserve the `edit` change\n * - convert it to an `add` (e.g., filter where old didn't match but new does)\n * - convert it to a `remove` (e.g., filter where old matched but new doesn't)\n *\n * This results in some invariants:\n * - an add coming in will only create adds coming out\n * - a remove coming in will only create removes coming out\n * - an edit coming in can create adds, removes, and edits coming out\n * - a child coming in can create adds, removes, and children coming out\n *\n * # Return of `pushAccumulatedChanges`\n *\n * This function will only push a single change.\n * Given the above invariants, how is this possible?\n *\n * An add that becomes many `adds` results in a single add\n * as the `add` is the same row across all adds. Branches do not change the row.\n *\n * A remove that becomes many `removes` results in a single remove\n * for the same reason.\n *\n * If a child enters and exits, it takes precedence over all other changes.\n * If a child enters and is converted only to add and remove it exits as an edit.\n * If a child enters and is converted to only add or only remove, it exits as that change.\n *\n * If an edit enters and is converted to add and remove it exits as an edit.\n * If an edit enters and is converted to only add or only remove, it exits as that change.\n * If an edit enters and exits as edits only, it exits as a single edit.\n */\nexport function* pushAccumulatedChanges(\n accumulatedPushes: Change[],\n output: Output,\n pusher: InputBase,\n fanOutChangeType: ChangeType,\n mergeRelationships: (existing: Change, incoming: Change) => Change,\n addEmptyRelationships: (change: Change) => Change,\n // Relationships a branch adds (a flipped join's witnesses). Only the union\n // fan has any; the filter fan passes nothing and behaves as before.\n isBranchRel?: (name: string) => boolean,\n): Stream<'yield'> {\n if (accumulatedPushes.length === 0) {\n // It is possible for no forks to pass along the push.\n // E.g., if no filters match in any fork.\n return;\n }\n\n // A union nested in a branch keeps the row but reports its witness changes\n // as child changes on its own branch relationships, around the change it\n // pushes for the row. (A child change entering through the fan-out is on\n // one of the fan-out's relationships, never a branch's.) Those are not\n // candidates to collapse: pass them through around this fan-in's change,\n // removals (old row) before and additions (new row) after, as the nested\n // union ordered them.\n const witnessesLeft: Change[] = [];\n const witnessesEntered: Change[] = [];\n\n // collapse down to a single change per type\n const candidatesToPush = new Map<ChangeType, Change>();\n for (const change of accumulatedPushes) {\n if (\n isBranchRel &&\n change[ChangeIndex.TYPE] === ChangeType.CHILD &&\n isBranchRel(change[ChangeIndex.CHILD_DATA].relationshipName)\n ) {\n (change[ChangeIndex.CHILD_DATA].change[ChangeIndex.TYPE] ===\n ChangeType.REMOVE\n ? witnessesLeft\n : witnessesEntered\n ).push(change);\n continue;\n }\n if (\n fanOutChangeType === ChangeType.CHILD &&\n change[ChangeIndex.TYPE] !== ChangeType.CHILD\n ) {\n assert(\n candidatesToPush.has(change[ChangeIndex.TYPE]) === false,\n () =>\n `Fan-in:child expected at most one ${change[ChangeIndex.TYPE]} when fan-out is of type child`,\n );\n }\n\n const existing = candidatesToPush.get(change[ChangeIndex.TYPE]);\n let mergedChange = change;\n if (existing) {\n // merge in relationships\n mergedChange = mergeRelationships(existing, change);\n }\n candidatesToPush.set(change[ChangeIndex.TYPE], mergedChange);\n }\n\n accumulatedPushes.length = 0;\n\n assert(\n candidatesToPush.size > 0,\n 'Fan-in: witness changes without a change for their row',\n );\n for (const change of witnessesLeft) {\n yield* output.push(change, pusher);\n }\n yield* pushCollapsed(\n candidatesToPush,\n output,\n pusher,\n fanOutChangeType,\n mergeRelationships,\n addEmptyRelationships,\n isBranchRel,\n );\n for (const change of witnessesEntered) {\n yield* output.push(change, pusher);\n }\n}\n\nfunction* pushCollapsed(\n candidatesToPush: Map<ChangeType, Change>,\n output: Output,\n pusher: InputBase,\n fanOutChangeType: ChangeType,\n mergeRelationships: (existing: Change, incoming: Change) => Change,\n addEmptyRelationships: (change: Change) => Change,\n isBranchRel: ((name: string) => boolean) | undefined,\n): Stream<'yield'> {\n const types = [...candidatesToPush.keys()];\n /**\n * Based on the received `fanOutChangeType` only certain output types are valid.\n *\n * - remove must result in all removes\n * - add must result in all adds\n * - edit must result in add or removes or edits\n * - child must result in a single add or single remove or many child changes\n * - Single add or remove because the relationship will be unique to one exist check within the fan-out,fan-in sub-graph\n * - Many child changes because other operators may preserve the child change\n */\n switch (fanOutChangeType) {\n case ChangeType.REMOVE:\n assert(\n types.length === 1 && types[0] === ChangeType.REMOVE,\n 'Fan-in:remove expected all removes',\n );\n yield* output.push(\n addEmptyRelationships(must(candidatesToPush.get(ChangeType.REMOVE))),\n pusher,\n );\n return;\n case ChangeType.ADD:\n assert(\n types.length === 1 && types[0] === ChangeType.ADD,\n 'Fan-in:add expected all adds',\n );\n yield* output.push(\n addEmptyRelationships(must(candidatesToPush.get(ChangeType.ADD))),\n pusher,\n );\n return;\n case ChangeType.EDIT: {\n assert(\n types.every(\n type =>\n type === ChangeType.ADD ||\n type === ChangeType.REMOVE ||\n type === ChangeType.EDIT,\n ),\n 'Fan-in:edit expected all adds, removes, or edits',\n );\n const addChange = candidatesToPush.get(ChangeType.ADD);\n const removeChange = candidatesToPush.get(ChangeType.REMOVE);\n let editChange = candidatesToPush.get(ChangeType.EDIT);\n\n // The row stays downstream (a branch kept the edit, or it moved from\n // one branch to another). Branches it left take their witnesses with\n // them; branches it entered bring theirs. An edit syncs one row, so\n // those must travel as child changes around it.\n if (isBranchRel && (editChange || (addChange && removeChange))) {\n if (removeChange) {\n yield* pushWitnessDeltas(\n removeChange[ChangeIndex.NODE],\n ChangeType.REMOVE,\n isBranchRel,\n output,\n pusher,\n );\n }\n const strip = (n: Node): Node => ({\n row: n.row,\n relationships: Object.fromEntries(\n Object.entries(n.relationships).filter(([k]) => !isBranchRel(k)),\n ),\n });\n yield* output.push(\n addEmptyRelationships(\n editChange ??\n makeEditChange(\n strip(must(addChange)[ChangeIndex.NODE]),\n strip(must(removeChange)[ChangeIndex.NODE]),\n ),\n ),\n pusher,\n );\n if (addChange) {\n yield* pushWitnessDeltas(\n addChange[ChangeIndex.NODE],\n ChangeType.ADD,\n isBranchRel,\n output,\n pusher,\n );\n }\n return;\n }\n\n // If an `edit` is present, it supersedes `add` and `remove`\n // as it semantically represents both.\n if (editChange) {\n if (addChange) {\n editChange = mergeRelationships(editChange, addChange);\n }\n if (removeChange) {\n editChange = mergeRelationships(editChange, removeChange);\n }\n yield* output.push(addEmptyRelationships(editChange), pusher);\n return;\n }\n\n // If `edit` didn't make it through but both `add` and `remove` did,\n // convert back to an edit.\n //\n // When can this happen?\n //\n // EDIT old: a=1, new: a=2\n // |\n // FanOut\n // / \\\n // a=1 a=2\n // | |\n // remove add\n // \\ /\n // FanIn\n //\n // The left filter converts the edit into a remove.\n // The right filter converts the edit into an add.\n if (addChange && removeChange) {\n yield* output.push(\n addEmptyRelationships(\n makeEditChange(\n addChange[ChangeIndex.NODE],\n removeChange[ChangeIndex.NODE],\n ),\n ),\n pusher,\n );\n return;\n }\n\n yield* output.push(\n addEmptyRelationships(must(addChange ?? removeChange)),\n pusher,\n );\n return;\n }\n case ChangeType.CHILD: {\n assert(\n types.every(\n type =>\n type === ChangeType.ADD || // exists can change child to add or remove\n type === ChangeType.REMOVE || // exists can change child to add or remove\n type === ChangeType.CHILD, // other operators may preserve the child change\n ),\n 'Fan-in:child expected all adds, removes, or children',\n );\n assert(\n types.length <= 2,\n 'Fan-in:child expected at most 2 types on a child change from fan-out',\n );\n\n // If any branch preserved the original child change, that takes precedence over all other changes.\n const childChange = candidatesToPush.get(ChangeType.CHILD);\n if (childChange) {\n const left = candidatesToPush.get(ChangeType.REMOVE);\n const entered = candidatesToPush.get(ChangeType.ADD);\n if (isBranchRel && left) {\n yield* pushWitnessDeltas(\n left[ChangeIndex.NODE],\n ChangeType.REMOVE,\n isBranchRel,\n output,\n pusher,\n );\n }\n yield* output.push(childChange, pusher);\n if (isBranchRel && entered) {\n yield* pushWitnessDeltas(\n entered[ChangeIndex.NODE],\n ChangeType.ADD,\n isBranchRel,\n output,\n pusher,\n );\n }\n return;\n }\n\n const addChange = candidatesToPush.get(ChangeType.ADD);\n const removeChange = candidatesToPush.get(ChangeType.REMOVE);\n\n assert(\n addChange === undefined || removeChange === undefined,\n 'Fan-in:child expected either add or remove, not both',\n );\n\n yield* output.push(\n addEmptyRelationships(must(addChange ?? removeChange)),\n pusher,\n );\n return;\n }\n default:\n fanOutChangeType satisfies never;\n }\n}\n\n/**\n * The row stays downstream, but it gained (`ADD`) or lost (`REMOVE`) every\n * witness in `node`'s branch relationships. One child change per witness.\n */\nexport function* pushWitnessDeltas(\n node: Node,\n type: ChangeType.ADD | ChangeType.REMOVE,\n isBranchRel: (name: string) => boolean,\n output: Output,\n pusher: InputBase,\n): Stream<'yield'> {\n for (const [relationshipName, children] of Object.entries(\n node.relationships,\n )) {\n if (!isBranchRel(relationshipName)) {\n continue;\n }\n for (const child of children()) {\n if (child === 'yield') {\n yield child;\n continue;\n }\n yield* output.push(\n makeChildChange(node, {\n relationshipName,\n change:\n type === ChangeType.ADD\n ? makeAddChange(child)\n : makeRemoveChange(child),\n }),\n pusher,\n );\n }\n }\n}\n\n/**\n * Puts relationships from `right` into `left` if they don't already exist in `left`.\n */\nexport function mergeRelationships(left: Change, right: Change): Change {\n // change types will always match\n // unless we have an edit on the left\n // then the right could be edit, add, or remove\n if (left[ChangeIndex.TYPE] === right[ChangeIndex.TYPE]) {\n switch (left[ChangeIndex.TYPE]) {\n case ChangeType.ADD: {\n return makeAddChange({\n row: left[ChangeIndex.NODE].row,\n relationships: {\n ...right[ChangeIndex.NODE].relationships,\n ...left[ChangeIndex.NODE].relationships,\n },\n });\n }\n case ChangeType.REMOVE: {\n return makeRemoveChange({\n row: left[ChangeIndex.NODE].row,\n relationships: {\n ...right[ChangeIndex.NODE].relationships,\n ...left[ChangeIndex.NODE].relationships,\n },\n });\n }\n case ChangeType.EDIT: {\n assert(\n right[ChangeIndex.TYPE] === ChangeType.EDIT,\n () =>\n `mergeRelationships: when left.type is edit and types match, right.type must be edit, got ${right[ChangeIndex.TYPE]}`,\n );\n // merge edits into a single edit\n return makeEditChange(\n {\n row: left[ChangeIndex.NODE].row,\n relationships: {\n ...right[ChangeIndex.NODE].relationships,\n ...left[ChangeIndex.NODE].relationships,\n },\n },\n {\n row: left[ChangeIndex.OLD_NODE].row,\n relationships: {\n ...right[ChangeIndex.OLD_NODE].relationships,\n ...left[ChangeIndex.OLD_NODE].relationships,\n },\n },\n );\n }\n case ChangeType.CHILD: {\n // Multiple branches may preserve the same child change, each adding\n // different relationships. Merge the relationships, keeping the child\n // (which should be identical across branches).\n assert(\n right[ChangeIndex.TYPE] === ChangeType.CHILD,\n () =>\n `mergeRelationships: when left.type is child and types match, right.type must be child, got ${right[ChangeIndex.TYPE]}`,\n );\n return makeChildChange(\n {\n row: left[ChangeIndex.NODE].row,\n relationships: {\n ...right[ChangeIndex.NODE].relationships,\n ...left[ChangeIndex.NODE].relationships,\n },\n },\n left[ChangeIndex.CHILD_DATA],\n );\n }\n }\n }\n\n // left is always an edit here\n assert(\n left[ChangeIndex.TYPE] === ChangeType.EDIT,\n () =>\n `mergeRelationships: when types differ, left.type must be edit, got left.type=${left[ChangeIndex.TYPE]}, right.type=${right[ChangeIndex.TYPE]}`,\n );\n switch (right[ChangeIndex.TYPE]) {\n case ChangeType.ADD: {\n return makeEditChange(\n {\n ...left[ChangeIndex.NODE],\n relationships: {\n ...right[ChangeIndex.NODE].relationships,\n ...left[ChangeIndex.NODE].relationships,\n },\n },\n left[ChangeIndex.OLD_NODE],\n );\n }\n case ChangeType.REMOVE: {\n return makeEditChange(left[ChangeIndex.NODE], {\n ...left[ChangeIndex.OLD_NODE],\n relationships: {\n ...right[ChangeIndex.NODE].relationships,\n ...left[ChangeIndex.OLD_NODE].relationships,\n },\n });\n }\n }\n\n unreachable();\n}\n\nexport function makeAddEmptyRelationships(\n schema: SourceSchema,\n): (change: Change) => Change {\n return (change: Change): Change => {\n if (Object.keys(schema.relationships).length === 0) {\n return change;\n }\n\n switch (change[ChangeIndex.TYPE]) {\n case ChangeType.ADD: {\n const relationships = {...change[ChangeIndex.NODE].relationships};\n mergeEmpty(relationships, Object.keys(schema.relationships));\n return makeAddChange({\n row: change[ChangeIndex.NODE].row,\n relationships,\n });\n }\n case ChangeType.REMOVE: {\n const relationships = {...change[ChangeIndex.NODE].relationships};\n mergeEmpty(relationships, Object.keys(schema.relationships));\n return makeRemoveChange({\n row: change[ChangeIndex.NODE].row,\n relationships,\n });\n }\n case ChangeType.EDIT: {\n const nodeRelationships = {...change[ChangeIndex.NODE].relationships};\n const oldNodeRelationships = {\n ...change[ChangeIndex.OLD_NODE].relationships,\n };\n mergeEmpty(nodeRelationships, Object.keys(schema.relationships));\n mergeEmpty(oldNodeRelationships, Object.keys(schema.relationships));\n return makeEditChange(\n {row: change[ChangeIndex.NODE].row, relationships: nodeRelationships},\n {\n row: change[ChangeIndex.OLD_NODE].row,\n relationships: oldNodeRelationships,\n },\n );\n }\n case ChangeType.CHILD:\n return change; // children only have relationships along the path to the change\n }\n };\n}\n\n/**\n * For each relationship in `schema` that does not exist\n * in `relationships`, add it with an empty stream.\n *\n * This modifies the `relationships` object in place.\n */\nexport function mergeEmpty(\n relationships: Record<string, () => Stream<Node | 'yield'>>,\n relationshipNames: string[],\n) {\n for (const relName of relationshipNames) {\n if (relationships[relName] === undefined) {\n relationships[relName] = () => emptyArray;\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"push-accumulated.js","names":[],"sources":["../../../../../zql/src/ivm/push-accumulated.ts"],"sourcesContent":["import {assert, unreachable} from '../../../shared/src/asserts.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport {emptyArray} from '../../../shared/src/sentinels.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport {ChangeType} from './change-type.ts';\nimport {\n makeAddChange,\n makeChildChange,\n makeEditChange,\n makeRemoveChange,\n type Change,\n} from './change.ts';\nimport type {Node} from './data.ts';\nimport type {InputBase, Output} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport type {Stream} from './stream.ts';\n\n/**\n * # pushAccumulatedChanges\n *\n * Pushes the changes that were accumulated by\n * [fan-out, fan-in] or [ufo, ufi] sub-graphs.\n *\n * This function is called at the end of the sub-graph.\n *\n * The sub-graphs represents `OR`s.\n *\n * Changes that can enter the subgraphs:\n * 1. child (due to exist joins being above the sub-graph)\n * 2. add\n * 3. remove\n * 4. edit\n *\n * # Changes that can exit into `pushAccumulatedChanges`:\n *\n * ## Child\n * If a `child` change enters a sub-graph, it will flow to all branches.\n * Each branch will either:\n * - preserve the `child` change\n * - stop the `child` change (e.g., filter)\n * - convert it to an `add` or `remove` (e.g., exists filter)\n *\n * ## Add\n * If an `add` change enters a sub-graph, it will flow to all branches.\n * Each branch will either:\n * - preserve the `add` change\n * - hide the change (e.g., filter)\n *\n * ## Remove\n * If a `remove` change enters a sub-graph, it will flow to all branches.\n * Each branch will either:\n * - preserve the `remove` change\n * - hide the change (e.g., filter)\n *\n * ## Edit\n * If an `edit` change enters a sub-graph, it will flow to all branches.\n * Each branch will either:\n * - preserve the `edit` change\n * - convert it to an `add` (e.g., filter where old didn't match but new does)\n * - convert it to a `remove` (e.g., filter where old matched but new doesn't)\n *\n * This results in some invariants:\n * - an add coming in will only create adds coming out\n * - a remove coming in will only create removes coming out\n * - an edit coming in can create adds, removes, and edits coming out\n * - a child coming in can create adds, removes, and children coming out\n *\n * # Return of `pushAccumulatedChanges`\n *\n * This function will only push a single change.\n * Given the above invariants, how is this possible?\n *\n * An add that becomes many `adds` results in a single add\n * as the `add` is the same row across all adds. Branches do not change the row.\n *\n * A remove that becomes many `removes` results in a single remove\n * for the same reason.\n *\n * If a child enters and exits, it takes precedence over all other changes.\n * If a child enters and is converted only to add and remove it exits as an edit.\n * If a child enters and is converted to only add or only remove, it exits as that change.\n *\n * If an edit enters and is converted to add and remove it exits as an edit.\n * If an edit enters and is converted to only add or only remove, it exits as that change.\n * If an edit enters and exits as edits only, it exits as a single edit.\n */\nexport function* pushAccumulatedChanges(\n accumulatedPushes: Change[],\n output: Output,\n pusher: InputBase,\n fanOutChangeType: ChangeType,\n mergeRelationships: (existing: Change, incoming: Change) => Change,\n addEmptyRelationships: (change: Change) => Change,\n // Relationships a branch adds (a flipped join's witnesses). Only the union\n // fan has any; the filter fan passes nothing and behaves as before.\n isBranchRel?: (name: string) => boolean,\n): Stream<'yield'> {\n if (accumulatedPushes.length === 0) {\n // It is possible for no forks to pass along the push.\n // E.g., if no filters match in any fork.\n return;\n }\n\n // A union nested in a branch keeps the row but reports its witness changes\n // as child changes on its own branch relationships, around the change it\n // pushes for the row. (A child change entering through the fan-out is on\n // one of the fan-out's relationships, never a branch's.) Those are not\n // candidates to collapse: pass them through around this fan-in's change,\n // removals (old row) before and additions (new row) after, as the nested\n // union ordered them.\n const witnessesLeft: Change[] = [];\n const witnessesEntered: Change[] = [];\n\n // collapse down to a single change per type\n const candidatesToPush = new Map<ChangeType, Change>();\n for (const change of accumulatedPushes) {\n if (\n isBranchRel &&\n change[ChangeIndex.TYPE] === ChangeType.CHILD &&\n isBranchRel(change[ChangeIndex.CHILD_DATA].relationshipName)\n ) {\n (change[ChangeIndex.CHILD_DATA].change[ChangeIndex.TYPE] ===\n ChangeType.REMOVE\n ? witnessesLeft\n : witnessesEntered\n ).push(change);\n continue;\n }\n if (\n fanOutChangeType === ChangeType.CHILD &&\n change[ChangeIndex.TYPE] !== ChangeType.CHILD\n ) {\n assert(\n candidatesToPush.has(change[ChangeIndex.TYPE]) === false,\n () =>\n `Fan-in:child expected at most one ${change[ChangeIndex.TYPE]} when fan-out is of type child`,\n );\n }\n\n const existing = candidatesToPush.get(change[ChangeIndex.TYPE]);\n let mergedChange = change;\n if (existing) {\n // merge in relationships\n mergedChange = mergeRelationships(existing, change);\n }\n candidatesToPush.set(change[ChangeIndex.TYPE], mergedChange);\n }\n\n accumulatedPushes.length = 0;\n\n assert(\n candidatesToPush.size > 0,\n 'Fan-in: witness changes without a change for their row',\n );\n for (const change of witnessesLeft) {\n yield* output.push(change, pusher);\n }\n yield* pushCollapsed(\n candidatesToPush,\n output,\n pusher,\n fanOutChangeType,\n mergeRelationships,\n addEmptyRelationships,\n isBranchRel,\n );\n for (const change of witnessesEntered) {\n yield* output.push(change, pusher);\n }\n}\n\nfunction* pushCollapsed(\n candidatesToPush: Map<ChangeType, Change>,\n output: Output,\n pusher: InputBase,\n fanOutChangeType: ChangeType,\n mergeRelationships: (existing: Change, incoming: Change) => Change,\n addEmptyRelationships: (change: Change) => Change,\n isBranchRel: ((name: string) => boolean) | undefined,\n): Stream<'yield'> {\n const types = [...candidatesToPush.keys()];\n /**\n * Based on the received `fanOutChangeType` only certain output types are valid.\n *\n * - remove must result in all removes\n * - add must result in all adds\n * - edit must result in add or removes or edits\n * - child must result in a single add or single remove or many child changes\n * - Single add or remove because the relationship will be unique to one exist check within the fan-out,fan-in sub-graph\n * - Many child changes because other operators may preserve the child change\n */\n switch (fanOutChangeType) {\n case ChangeType.REMOVE:\n assert(\n types.length === 1 && types[0] === ChangeType.REMOVE,\n 'Fan-in:remove expected all removes',\n );\n yield* output.push(\n addEmptyRelationships(must(candidatesToPush.get(ChangeType.REMOVE))),\n pusher,\n );\n return;\n case ChangeType.ADD:\n assert(\n types.length === 1 && types[0] === ChangeType.ADD,\n 'Fan-in:add expected all adds',\n );\n yield* output.push(\n addEmptyRelationships(must(candidatesToPush.get(ChangeType.ADD))),\n pusher,\n );\n return;\n case ChangeType.EDIT: {\n assert(\n types.every(\n type =>\n type === ChangeType.ADD ||\n type === ChangeType.REMOVE ||\n type === ChangeType.EDIT,\n ),\n 'Fan-in:edit expected all adds, removes, or edits',\n );\n const addChange = candidatesToPush.get(ChangeType.ADD);\n const removeChange = candidatesToPush.get(ChangeType.REMOVE);\n let editChange = candidatesToPush.get(ChangeType.EDIT);\n\n // The row stays downstream (a branch kept the edit, or it moved from\n // one branch to another). Branches it left take their witnesses with\n // them; branches it entered bring theirs. An edit syncs one row, so\n // those must travel as child changes around it.\n if (isBranchRel && (editChange || (addChange && removeChange))) {\n if (removeChange) {\n yield* pushWitnessDeltas(\n removeChange[ChangeIndex.NODE],\n ChangeType.REMOVE,\n isBranchRel,\n output,\n pusher,\n );\n }\n const strip = (n: Node): Node => ({\n row: n.row,\n relationships: Object.fromEntries(\n Object.entries(n.relationships).filter(([k]) => !isBranchRel(k)),\n ),\n });\n yield* output.push(\n addEmptyRelationships(\n editChange ??\n makeEditChange(\n strip(must(addChange)[ChangeIndex.NODE]),\n strip(must(removeChange)[ChangeIndex.NODE]),\n ),\n ),\n pusher,\n );\n if (addChange) {\n yield* pushWitnessDeltas(\n addChange[ChangeIndex.NODE],\n ChangeType.ADD,\n isBranchRel,\n output,\n pusher,\n );\n }\n return;\n }\n\n // If an `edit` is present, it supersedes `add` and `remove`\n // as it semantically represents both.\n if (editChange) {\n if (addChange) {\n editChange = mergeRelationships(editChange, addChange);\n }\n if (removeChange) {\n editChange = mergeRelationships(editChange, removeChange);\n }\n yield* output.push(addEmptyRelationships(editChange), pusher);\n return;\n }\n\n // If `edit` didn't make it through but both `add` and `remove` did,\n // convert back to an edit.\n //\n // When can this happen?\n //\n // EDIT old: a=1, new: a=2\n // |\n // FanOut\n // / \\\n // a=1 a=2\n // | |\n // remove add\n // \\ /\n // FanIn\n //\n // The left filter converts the edit into a remove.\n // The right filter converts the edit into an add.\n if (addChange && removeChange) {\n yield* output.push(\n addEmptyRelationships(\n makeEditChange(\n addChange[ChangeIndex.NODE],\n removeChange[ChangeIndex.NODE],\n ),\n ),\n pusher,\n );\n return;\n }\n\n yield* output.push(\n addEmptyRelationships(must(addChange ?? removeChange)),\n pusher,\n );\n return;\n }\n case ChangeType.CHILD: {\n assert(\n types.every(\n type =>\n type === ChangeType.ADD || // exists can change child to add or remove\n type === ChangeType.REMOVE || // exists can change child to add or remove\n type === ChangeType.CHILD, // other operators may preserve the child change\n ),\n 'Fan-in:child expected all adds, removes, or children',\n );\n assert(\n types.length <= 2,\n 'Fan-in:child expected at most 2 types on a child change from fan-out',\n );\n\n // If any branch preserved the original child change, that takes precedence over all other changes.\n const childChange = candidatesToPush.get(ChangeType.CHILD);\n if (childChange) {\n const left = candidatesToPush.get(ChangeType.REMOVE);\n const entered = candidatesToPush.get(ChangeType.ADD);\n if (isBranchRel && left) {\n yield* pushWitnessDeltas(\n left[ChangeIndex.NODE],\n ChangeType.REMOVE,\n isBranchRel,\n output,\n pusher,\n );\n }\n yield* output.push(childChange, pusher);\n if (isBranchRel && entered) {\n yield* pushWitnessDeltas(\n entered[ChangeIndex.NODE],\n ChangeType.ADD,\n isBranchRel,\n output,\n pusher,\n );\n }\n return;\n }\n\n const addChange = candidatesToPush.get(ChangeType.ADD);\n const removeChange = candidatesToPush.get(ChangeType.REMOVE);\n\n assert(\n addChange === undefined || removeChange === undefined,\n 'Fan-in:child expected either add or remove, not both',\n );\n\n yield* output.push(\n addEmptyRelationships(must(addChange ?? removeChange)),\n pusher,\n );\n return;\n }\n default:\n fanOutChangeType satisfies never;\n }\n}\n\n/**\n * The row stays downstream, but it gained (`ADD`) or lost (`REMOVE`) every\n * witness in `node`'s branch relationships. One child change per witness.\n */\nexport function* pushWitnessDeltas(\n node: Node,\n type: ChangeType.ADD | ChangeType.REMOVE,\n isBranchRel: (name: string) => boolean,\n output: Output,\n pusher: InputBase,\n): Stream<'yield'> {\n for (const [relationshipName, children] of Object.entries(\n node.relationships,\n )) {\n if (!isBranchRel(relationshipName)) {\n continue;\n }\n for (const child of children()) {\n if (child === 'yield') {\n yield child;\n continue;\n }\n yield* output.push(\n makeChildChange(node, {\n relationshipName,\n change:\n type === ChangeType.ADD\n ? makeAddChange(child)\n : makeRemoveChange(child),\n }),\n pusher,\n );\n }\n }\n}\n\n/**\n * Puts relationships from `right` into `left` if they don't already exist in `left`.\n */\nexport function mergeRelationships(left: Change, right: Change): Change {\n // change types will always match\n // unless we have an edit on the left\n // then the right could be edit, add, or remove\n if (left[ChangeIndex.TYPE] === right[ChangeIndex.TYPE]) {\n switch (left[ChangeIndex.TYPE]) {\n case ChangeType.ADD: {\n return makeAddChange({\n row: left[ChangeIndex.NODE].row,\n relationships: {\n ...right[ChangeIndex.NODE].relationships,\n ...left[ChangeIndex.NODE].relationships,\n },\n });\n }\n case ChangeType.REMOVE: {\n return makeRemoveChange({\n row: left[ChangeIndex.NODE].row,\n relationships: {\n ...right[ChangeIndex.NODE].relationships,\n ...left[ChangeIndex.NODE].relationships,\n },\n });\n }\n case ChangeType.EDIT: {\n assert(\n right[ChangeIndex.TYPE] === ChangeType.EDIT,\n () =>\n `mergeRelationships: when left.type is edit and types match, right.type must be edit, got ${right[ChangeIndex.TYPE]}`,\n );\n // merge edits into a single edit\n return makeEditChange(\n {\n row: left[ChangeIndex.NODE].row,\n relationships: {\n ...right[ChangeIndex.NODE].relationships,\n ...left[ChangeIndex.NODE].relationships,\n },\n },\n {\n row: left[ChangeIndex.OLD_NODE].row,\n relationships: {\n ...right[ChangeIndex.OLD_NODE].relationships,\n ...left[ChangeIndex.OLD_NODE].relationships,\n },\n },\n );\n }\n case ChangeType.CHILD: {\n // Multiple branches may preserve the same child change, each adding\n // different relationships. Merge the relationships, keeping the child\n // (which should be identical across branches).\n assert(\n right[ChangeIndex.TYPE] === ChangeType.CHILD,\n () =>\n `mergeRelationships: when left.type is child and types match, right.type must be child, got ${right[ChangeIndex.TYPE]}`,\n );\n return makeChildChange(\n {\n row: left[ChangeIndex.NODE].row,\n relationships: {\n ...right[ChangeIndex.NODE].relationships,\n ...left[ChangeIndex.NODE].relationships,\n },\n },\n left[ChangeIndex.CHILD_DATA],\n );\n }\n }\n }\n\n // left is always an edit here\n assert(\n left[ChangeIndex.TYPE] === ChangeType.EDIT,\n () =>\n `mergeRelationships: when types differ, left.type must be edit, got left.type=${left[ChangeIndex.TYPE]}, right.type=${right[ChangeIndex.TYPE]}`,\n );\n switch (right[ChangeIndex.TYPE]) {\n case ChangeType.ADD: {\n return makeEditChange(\n {\n ...left[ChangeIndex.NODE],\n relationships: {\n ...right[ChangeIndex.NODE].relationships,\n ...left[ChangeIndex.NODE].relationships,\n },\n },\n left[ChangeIndex.OLD_NODE],\n );\n }\n case ChangeType.REMOVE: {\n return makeEditChange(left[ChangeIndex.NODE], {\n ...left[ChangeIndex.OLD_NODE],\n relationships: {\n ...right[ChangeIndex.NODE].relationships,\n ...left[ChangeIndex.OLD_NODE].relationships,\n },\n });\n }\n }\n\n unreachable();\n}\n\nexport function makeAddEmptyRelationships(\n schema: SourceSchema,\n): (change: Change) => Change {\n return (change: Change): Change => {\n if (Object.keys(schema.relationships).length === 0) {\n return change;\n }\n\n switch (change[ChangeIndex.TYPE]) {\n case ChangeType.ADD: {\n const relationships = {...change[ChangeIndex.NODE].relationships};\n mergeEmpty(relationships, Object.keys(schema.relationships));\n return makeAddChange({\n row: change[ChangeIndex.NODE].row,\n relationships,\n });\n }\n case ChangeType.REMOVE: {\n const relationships = {...change[ChangeIndex.NODE].relationships};\n mergeEmpty(relationships, Object.keys(schema.relationships));\n return makeRemoveChange({\n row: change[ChangeIndex.NODE].row,\n relationships,\n });\n }\n case ChangeType.EDIT: {\n const nodeRelationships = {...change[ChangeIndex.NODE].relationships};\n const oldNodeRelationships = {\n ...change[ChangeIndex.OLD_NODE].relationships,\n };\n mergeEmpty(nodeRelationships, Object.keys(schema.relationships));\n mergeEmpty(oldNodeRelationships, Object.keys(schema.relationships));\n return makeEditChange(\n {row: change[ChangeIndex.NODE].row, relationships: nodeRelationships},\n {\n row: change[ChangeIndex.OLD_NODE].row,\n relationships: oldNodeRelationships,\n },\n );\n }\n case ChangeType.CHILD:\n return change; // children only have relationships along the path to the change\n }\n };\n}\n\n/**\n * For each relationship in `schema` that does not exist\n * in `relationships`, add it with an empty stream.\n *\n * This modifies the `relationships` object in place.\n */\nexport function mergeEmpty(\n relationships: Record<string, () => Stream<Node | 'yield'>>,\n relationshipNames: string[],\n) {\n for (const relName of relationshipNames) {\n if (relationships[relName] === undefined) {\n relationships[relName] = () => emptyArray;\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsFA,UAAiB,uBACf,mBACA,QACA,QACA,kBACA,oBACA,uBAGA,aACiB;CACjB,IAAI,kBAAkB,WAAW,GAG/B;CAUF,MAAM,gBAA0B,CAAC;CACjC,MAAM,mBAA6B,CAAC;CAGpC,MAAM,mCAAmB,IAAI,IAAwB;CACrD,KAAK,MAAM,UAAU,mBAAmB;EACtC,IACE,eACA,OAAO,OAAsB,KAC7B,YAAY,OAAO,EAAuB,CAAC,gBAAgB,GAC3D;GACA,CAAC,OAAO,EAAuB,CAAC,OAAO,OACvC,IACI,gBACA,iBAAA,CACF,KAAK,MAAM;GACb;EACF;EACA,IACE,qBAAqB,KACrB,OAAO,OAAsB,GAE7B,OACE,iBAAiB,IAAI,OAAO,EAAiB,MAAM,aAEjD,qCAAqC,OAAO,GAAkB,+BAClE;EAGF,MAAM,WAAW,iBAAiB,IAAI,OAAO,EAAiB;EAC9D,IAAI,eAAe;EACnB,IAAI,UAEF,eAAe,mBAAmB,UAAU,MAAM;EAEpD,iBAAiB,IAAI,OAAO,IAAmB,YAAY;CAC7D;CAEA,kBAAkB,SAAS;CAE3B,OACE,iBAAiB,OAAO,GACxB,wDACF;CACA,KAAK,MAAM,UAAU,eACnB,OAAO,OAAO,KAAK,QAAQ,MAAM;CAEnC,OAAO,cACL,kBACA,QACA,QACA,kBACA,oBACA,uBACA,WACF;CACA,KAAK,MAAM,UAAU,kBACnB,OAAO,OAAO,KAAK,QAAQ,MAAM;AAErC;AAEA,UAAU,cACR,kBACA,QACA,QACA,kBACA,oBACA,uBACA,aACiB;CACjB,MAAM,QAAQ,CAAC,GAAG,iBAAiB,KAAK,CAAC;;;;;;;;;;;CAWzC,QAAQ,kBAAR;EACE,KAAK;GACH,OACE,MAAM,WAAW,KAAK,MAAM,OAAO,GACnC,oCACF;GACA,OAAO,OAAO,KACZ,sBAAsB,KAAK,iBAAiB,IAAI,CAAiB,CAAC,CAAC,GACnE,MACF;GACA;EACF,KAAK;GACH,OACE,MAAM,WAAW,KAAK,MAAM,OAAO,GACnC,8BACF;GACA,OAAO,OAAO,KACZ,sBAAsB,KAAK,iBAAiB,IAAI,CAAc,CAAC,CAAC,GAChE,MACF;GACA;EACF,KAAK,GAAiB;GACpB,OACE,MAAM,OACJ,SACE,SAAS,KACT,SAAS,KACT,SAAS,CACb,GACA,kDACF;GACA,MAAM,YAAY,iBAAiB,IAAI,CAAc;GACrD,MAAM,eAAe,iBAAiB,IAAI,CAAiB;GAC3D,IAAI,aAAa,iBAAiB,IAAI,CAAe;GAMrD,IAAI,gBAAgB,cAAe,aAAa,eAAgB;IAC9D,IAAI,cACF,OAAO,kBACL,aAAa,IACb,GACA,aACA,QACA,MACF;IAEF,MAAM,SAAS,OAAmB;KAChC,KAAK,EAAE;KACP,eAAe,OAAO,YACpB,OAAO,QAAQ,EAAE,aAAa,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CACjE;IACF;IACA,OAAO,OAAO,KACZ,sBACE,cACE,eACE,MAAM,KAAK,SAAS,CAAC,CAAC,EAAiB,GACvC,MAAM,KAAK,YAAY,CAAC,CAAC,EAAiB,CAC5C,CACJ,GACA,MACF;IACA,IAAI,WACF,OAAO,kBACL,UAAU,IACV,GACA,aACA,QACA,MACF;IAEF;GACF;GAIA,IAAI,YAAY;IACd,IAAI,WACF,aAAa,mBAAmB,YAAY,SAAS;IAEvD,IAAI,cACF,aAAa,mBAAmB,YAAY,YAAY;IAE1D,OAAO,OAAO,KAAK,sBAAsB,UAAU,GAAG,MAAM;IAC5D;GACF;GAmBA,IAAI,aAAa,cAAc;IAC7B,OAAO,OAAO,KACZ,sBACE,eACE,UAAU,IACV,aAAa,EACf,CACF,GACA,MACF;IACA;GACF;GAEA,OAAO,OAAO,KACZ,sBAAsB,KAAK,aAAa,YAAY,CAAC,GACrD,MACF;GACA;EACF;EACA,KAAK,GAAkB;GACrB,OACE,MAAM,OACJ,SACE,SAAS,KACT,SAAS,KACT,SAAS,CACb,GACA,sDACF;GACA,OACE,MAAM,UAAU,GAChB,sEACF;GAGA,MAAM,cAAc,iBAAiB,IAAI,CAAgB;GACzD,IAAI,aAAa;IACf,MAAM,OAAO,iBAAiB,IAAI,CAAiB;IACnD,MAAM,UAAU,iBAAiB,IAAI,CAAc;IACnD,IAAI,eAAe,MACjB,OAAO,kBACL,KAAK,IACL,GACA,aACA,QACA,MACF;IAEF,OAAO,OAAO,KAAK,aAAa,MAAM;IACtC,IAAI,eAAe,SACjB,OAAO,kBACL,QAAQ,IACR,GACA,aACA,QACA,MACF;IAEF;GACF;GAEA,MAAM,YAAY,iBAAiB,IAAI,CAAc;GACrD,MAAM,eAAe,iBAAiB,IAAI,CAAiB;GAE3D,OACE,cAAc,KAAA,KAAa,iBAAiB,KAAA,GAC5C,sDACF;GAEA,OAAO,OAAO,KACZ,sBAAsB,KAAK,aAAa,YAAY,CAAC,GACrD,MACF;GACA;EACF;CAGF;AACF;;;;;AAMA,UAAiB,kBACf,MACA,MACA,aACA,QACA,QACiB;CACjB,KAAK,MAAM,CAAC,kBAAkB,aAAa,OAAO,QAChD,KAAK,aACP,GAAG;EACD,IAAI,CAAC,YAAY,gBAAgB,GAC/B;EAEF,KAAK,MAAM,SAAS,SAAS,GAAG;GAC9B,IAAI,UAAU,SAAS;IACrB,MAAM;IACN;GACF;GACA,OAAO,OAAO,KACZ,gBAAgB,MAAM;IACpB;IACA,QACE,SAAS,IACL,cAAc,KAAK,IACnB,iBAAiB,KAAK;GAC9B,CAAC,GACD,MACF;EACF;CACF;AACF;;;;AAKA,SAAgB,mBAAmB,MAAc,OAAuB;CAItE,IAAI,KAAK,OAAsB,MAAM,IACnC,QAAQ,KAAK,IAAb;EACE,KAAK,GACH,OAAO,cAAc;GACnB,KAAK,KAAK,EAAiB,CAAC;GAC5B,eAAe;IACb,GAAG,MAAM,EAAiB,CAAC;IAC3B,GAAG,KAAK,EAAiB,CAAC;GAC5B;EACF,CAAC;EAEH,KAAK,GACH,OAAO,iBAAiB;GACtB,KAAK,KAAK,EAAiB,CAAC;GAC5B,eAAe;IACb,GAAG,MAAM,EAAiB,CAAC;IAC3B,GAAG,KAAK,EAAiB,CAAC;GAC5B;EACF,CAAC;EAEH,KAAK;GACH,OACE,MAAM,OAAsB,SAE1B,4FAA4F,MAAM,IACtG;GAEA,OAAO,eACL;IACE,KAAK,KAAK,EAAiB,CAAC;IAC5B,eAAe;KACb,GAAG,MAAM,EAAiB,CAAC;KAC3B,GAAG,KAAK,EAAiB,CAAC;IAC5B;GACF,GACA;IACE,KAAK,KAAK,EAAqB,CAAC;IAChC,eAAe;KACb,GAAG,MAAM,EAAqB,CAAC;KAC/B,GAAG,KAAK,EAAqB,CAAC;IAChC;GACF,CACF;EAEF,KAAK;GAIH,OACE,MAAM,OAAsB,SAE1B,8FAA8F,MAAM,IACxG;GACA,OAAO,gBACL;IACE,KAAK,KAAK,EAAiB,CAAC;IAC5B,eAAe;KACb,GAAG,MAAM,EAAiB,CAAC;KAC3B,GAAG,KAAK,EAAiB,CAAC;IAC5B;GACF,GACA,KAAK,EACP;CAEJ;CAIF,OACE,KAAK,OAAsB,SAEzB,gFAAgF,KAAK,GAAkB,eAAe,MAAM,IAChI;CACA,QAAQ,MAAM,IAAd;EACE,KAAK,GACH,OAAO,eACL;GACE,GAAG,KAAK;GACR,eAAe;IACb,GAAG,MAAM,EAAiB,CAAC;IAC3B,GAAG,KAAK,EAAiB,CAAC;GAC5B;EACF,GACA,KAAK,EACP;EAEF,KAAK,GACH,OAAO,eAAe,KAAK,IAAmB;GAC5C,GAAG,KAAK;GACR,eAAe;IACb,GAAG,MAAM,EAAiB,CAAC;IAC3B,GAAG,KAAK,EAAqB,CAAC;GAChC;EACF,CAAC;CAEL;CAEA,YAAY;AACd;AAEA,SAAgB,0BACd,QAC4B;CAC5B,QAAQ,WAA2B;EACjC,IAAI,OAAO,KAAK,OAAO,aAAa,CAAC,CAAC,WAAW,GAC/C,OAAO;EAGT,QAAQ,OAAO,IAAf;GACE,KAAK,GAAgB;IACnB,MAAM,gBAAgB,EAAC,GAAG,OAAO,EAAiB,CAAC,cAAa;IAChE,WAAW,eAAe,OAAO,KAAK,OAAO,aAAa,CAAC;IAC3D,OAAO,cAAc;KACnB,KAAK,OAAO,EAAiB,CAAC;KAC9B;IACF,CAAC;GACH;GACA,KAAK,GAAmB;IACtB,MAAM,gBAAgB,EAAC,GAAG,OAAO,EAAiB,CAAC,cAAa;IAChE,WAAW,eAAe,OAAO,KAAK,OAAO,aAAa,CAAC;IAC3D,OAAO,iBAAiB;KACtB,KAAK,OAAO,EAAiB,CAAC;KAC9B;IACF,CAAC;GACH;GACA,KAAK,GAAiB;IACpB,MAAM,oBAAoB,EAAC,GAAG,OAAO,EAAiB,CAAC,cAAa;IACpE,MAAM,uBAAuB,EAC3B,GAAG,OAAO,EAAqB,CAAC,cAClC;IACA,WAAW,mBAAmB,OAAO,KAAK,OAAO,aAAa,CAAC;IAC/D,WAAW,sBAAsB,OAAO,KAAK,OAAO,aAAa,CAAC;IAClE,OAAO,eACL;KAAC,KAAK,OAAO,EAAiB,CAAC;KAAK,eAAe;IAAiB,GACpE;KACE,KAAK,OAAO,EAAqB,CAAC;KAClC,eAAe;IACjB,CACF;GACF;GACA,KAAK,GACH,OAAO;EACX;CACF;AACF;;;;;;;AAQA,SAAgB,WACd,eACA,mBACA;CACA,KAAK,MAAM,WAAW,mBACpB,IAAI,cAAc,aAAa,KAAA,GAC7B,cAAc,iBAAiB;AAGrC"}
@@ -1 +1 @@
1
- {"version":3,"file":"skip-yields.js","names":["#stream","#it"],"sources":["../../../../../zql/src/ivm/skip-yields.ts"],"sourcesContent":["import type {Node} from './data.ts';\nimport type {Stream} from './stream.ts';\n\n// Implemented as a custom IterableIterator rather than a generator function to\n// reduce allocations. A generator creates a new state-machine object each time\n// it is called. By returning `this` from [Symbol.iterator](), the same object\n// acts as both the Iterable and the Iterator, so iterating the stream incurs\n// only one allocation (the SkipYieldsStream instance itself) instead of two.\n// Additionally, the IteratorResult objects ({value, done}) from the inner\n// iterator are returned directly rather than being recreated, avoiding further\n// per-item allocations.\nclass SkipYieldsStream implements IterableIterator<Node> {\n readonly #stream: Stream<Node | 'yield'>;\n #it: Iterator<Node | 'yield'> | undefined = undefined;\n\n constructor(stream: Stream<Node | 'yield'>) {\n this.#stream = stream;\n }\n\n [Symbol.iterator](): IterableIterator<Node> {\n this.#it = this.#stream[Symbol.iterator]();\n return this;\n }\n\n next(): IteratorResult<Node> {\n // #it is always set before next() is called, as [Symbol.iterator]() must\n // be called first (e.g. by a for-of loop).\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const it = this.#it!;\n for (;;) {\n const r = it.next();\n if (r.done || r.value !== 'yield') {\n return r as IteratorResult<Node>;\n }\n }\n }\n\n return(value?: undefined): IteratorResult<Node> {\n this.#it?.return?.(value);\n return {done: true, value: undefined};\n }\n}\n\nexport function skipYields(stream: Stream<Node | 'yield'>): Stream<Node> {\n return new SkipYieldsStream(stream);\n}\n"],"mappings":";AAWA,IAAM,mBAAN,MAAyD;CACvD;CACA,MAA4C,KAAA;CAE5C,YAAY,QAAgC;EAC1C,KAAKA,UAAU;CACjB;CAEA,CAAC,OAAO,YAAoC;EAC1C,KAAKC,MAAM,KAAKD,QAAQ,OAAO,UAAU;EACzC,OAAO;CACT;CAEA,OAA6B;EAI3B,MAAM,KAAK,KAAKC;EAChB,SAAS;GACP,MAAM,IAAI,GAAG,KAAK;GAClB,IAAI,EAAE,QAAQ,EAAE,UAAU,SACxB,OAAO;EAEX;CACF;CAEA,OAAO,OAAyC;EAC9C,KAAKA,KAAK,SAAS,KAAK;EACxB,OAAO;GAAC,MAAM;GAAM,OAAO,KAAA;EAAS;CACtC;AACF;AAEA,SAAgB,WAAW,QAA8C;CACvE,OAAO,IAAI,iBAAiB,MAAM;AACpC"}
1
+ {"version":3,"file":"skip-yields.js","names":[],"sources":["../../../../../zql/src/ivm/skip-yields.ts"],"sourcesContent":["import type {Node} from './data.ts';\nimport type {Stream} from './stream.ts';\n\n// Implemented as a custom IterableIterator rather than a generator function to\n// reduce allocations. A generator creates a new state-machine object each time\n// it is called. By returning `this` from [Symbol.iterator](), the same object\n// acts as both the Iterable and the Iterator, so iterating the stream incurs\n// only one allocation (the SkipYieldsStream instance itself) instead of two.\n// Additionally, the IteratorResult objects ({value, done}) from the inner\n// iterator are returned directly rather than being recreated, avoiding further\n// per-item allocations.\nclass SkipYieldsStream implements IterableIterator<Node> {\n readonly #stream: Stream<Node | 'yield'>;\n #it: Iterator<Node | 'yield'> | undefined = undefined;\n\n constructor(stream: Stream<Node | 'yield'>) {\n this.#stream = stream;\n }\n\n [Symbol.iterator](): IterableIterator<Node> {\n this.#it = this.#stream[Symbol.iterator]();\n return this;\n }\n\n next(): IteratorResult<Node> {\n // #it is always set before next() is called, as [Symbol.iterator]() must\n // be called first (e.g. by a for-of loop).\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const it = this.#it!;\n for (;;) {\n const r = it.next();\n if (r.done || r.value !== 'yield') {\n return r as IteratorResult<Node>;\n }\n }\n }\n\n return(value?: undefined): IteratorResult<Node> {\n this.#it?.return?.(value);\n return {done: true, value: undefined};\n }\n}\n\nexport function skipYields(stream: Stream<Node | 'yield'>): Stream<Node> {\n return new SkipYieldsStream(stream);\n}\n"],"mappings":";AAWA,IAAM,mBAAN,MAAyD;CACvD;CACA,MAA4C,KAAA;CAE5C,YAAY,QAAgC;EAC1C,KAAK,UAAU;CACjB;CAEA,CAAC,OAAO,YAAoC;EAC1C,KAAK,MAAM,KAAK,QAAQ,OAAO,SAAS,CAAC;EACzC,OAAO;CACT;CAEA,OAA6B;EAI3B,MAAM,KAAK,KAAK;EAChB,SAAS;GACP,MAAM,IAAI,GAAG,KAAK;GAClB,IAAI,EAAE,QAAQ,EAAE,UAAU,SACxB,OAAO;EAEX;CACF;CAEA,OAAO,OAAyC;EAC9C,KAAK,KAAK,SAAS,KAAK;EACxB,OAAO;GAAC,MAAM;GAAM,OAAO,KAAA;EAAS;CACtC;AACF;AAEA,SAAgB,WAAW,QAA8C;CACvE,OAAO,IAAI,iBAAiB,MAAM;AACpC"}
@@ -1 +1 @@
1
- {"version":3,"file":"skip.js","names":["#input","#bound","#comparator","#getStart","#shouldBePresent","#output"],"sources":["../../../../../zql/src/ivm/skip.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport type {Row} from '../../../zero-protocol/src/data.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport {ChangeType} from './change-type.ts';\nimport {\n type AddChange,\n type Change,\n type ChildChange,\n type RemoveChange,\n} from './change.ts';\nimport type {Comparator, Node} from './data.ts';\nimport {maybeSplitAndPushEditChange} from './maybe-split-and-push-edit-change.ts';\nimport {\n throwOutput,\n type FetchRequest,\n type Input,\n type InputBase,\n type Operator,\n type Output,\n type Start,\n} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport type {Stream} from './stream.ts';\n\nexport type Bound = {\n row: Row;\n exclusive: boolean;\n};\n\n/**\n * Skip sets the start position for the pipeline. No rows before the bound will\n * be output.\n */\nexport class Skip implements Operator {\n readonly #input: Input;\n readonly #bound: Bound;\n readonly #comparator: Comparator;\n\n #output: Output = throwOutput;\n\n constructor(input: Input, bound: Bound) {\n const {sort} = input.getSchema();\n assert(sort !== undefined, 'Skip requires sorted input');\n this.#input = input;\n this.#bound = bound;\n this.#comparator = input.getSchema().compareRows;\n input.setOutput(this);\n }\n\n getSchema(): SourceSchema {\n return this.#input.getSchema();\n }\n\n *fetch(req: FetchRequest): Stream<Node | 'yield'> {\n const start = this.#getStart(req);\n if (start === 'empty') {\n return;\n }\n const nodes = this.#input.fetch({...req, start});\n if (!req.reverse) {\n yield* nodes;\n return;\n }\n for (const node of nodes) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n if (!this.#shouldBePresent(node.row)) {\n return;\n }\n yield node;\n }\n }\n\n setOutput(output: Output): void {\n this.#output = output;\n }\n\n destroy(): void {\n this.#input.destroy();\n }\n\n #shouldBePresent(row: Row): boolean {\n const cmp = this.#comparator(this.#bound.row, row);\n return cmp < 0 || (cmp === 0 && !this.#bound.exclusive);\n }\n\n *push(change: Change): Stream<'yield'> {\n const shouldBePresent = (row: Row) => this.#shouldBePresent(row);\n if (change[ChangeIndex.TYPE] === ChangeType.EDIT) {\n yield* maybeSplitAndPushEditChange(\n change,\n shouldBePresent,\n this.#output,\n this,\n );\n return;\n }\n\n change satisfies AddChange | RemoveChange | ChildChange;\n\n if (shouldBePresent(change[ChangeIndex.NODE].row)) {\n yield* this.#output.push(change, this);\n }\n }\n\n *reconcile(_pusher: InputBase): Stream<'yield'> {\n if (this.#output.reconcile) {\n yield* this.#output.reconcile(this);\n }\n }\n\n #getStart(req: FetchRequest): Start | undefined | 'empty' {\n const boundStart = {\n row: this.#bound.row,\n basis: this.#bound.exclusive ? 'after' : 'at',\n } as const;\n\n if (!req.start) {\n if (req.reverse) {\n return undefined;\n }\n return boundStart;\n }\n\n const cmp = this.#comparator(this.#bound.row, req.start.row);\n\n if (!req.reverse) {\n // The skip bound is after the requested bound. The requested bound cannot\n // be relevant because even if it was basis: 'after', the skip bound is\n // itself after the requested bound. Return the skip bound.\n if (cmp > 0) {\n return boundStart;\n }\n\n // The skip bound and requested bound are equal. If either is exclusive,\n // return that bound with exclusive. Otherwise, return the skip bound.\n if (cmp === 0) {\n if (this.#bound.exclusive || req.start.basis === 'after') {\n return {\n row: this.#bound.row,\n basis: 'after',\n };\n }\n return boundStart;\n }\n\n return req.start;\n }\n\n req.reverse satisfies true;\n\n // bound is after the start, but request is for reverse so results\n // must be empty\n if (cmp > 0) {\n return 'empty';\n }\n\n if (cmp === 0) {\n // if both are inclusive, the result can be the single row at bound\n // return it as start\n if (!this.#bound.exclusive && req.start.basis === 'at') {\n return boundStart;\n }\n // otherwise the results must be empty, one or both are exclusive\n // in opposite directions\n return 'empty';\n }\n\n // bound is before the start, return start\n return req.start;\n }\n}\n"],"mappings":";;;;;;;;AAiCA,IAAa,OAAb,MAAsC;CACpC;CACA;CACA;CAEA,UAAkB;CAElB,YAAY,OAAc,OAAc;EACtC,MAAM,EAAC,SAAQ,MAAM,UAAU;EAC/B,OAAO,SAAS,KAAA,GAAW,4BAA4B;EACvD,KAAKA,SAAS;EACd,KAAKC,SAAS;EACd,KAAKC,cAAc,MAAM,UAAU,EAAE;EACrC,MAAM,UAAU,IAAI;CACtB;CAEA,YAA0B;EACxB,OAAO,KAAKF,OAAO,UAAU;CAC/B;CAEA,CAAC,MAAM,KAA2C;EAChD,MAAM,QAAQ,KAAKG,UAAU,GAAG;EAChC,IAAI,UAAU,SACZ;EAEF,MAAM,QAAQ,KAAKH,OAAO,MAAM;GAAC,GAAG;GAAK;EAAK,CAAC;EAC/C,IAAI,CAAC,IAAI,SAAS;GAChB,OAAO;GACP;EACF;EACA,KAAK,MAAM,QAAQ,OAAO;GACxB,IAAI,SAAS,SAAS;IACpB,MAAM;IACN;GACF;GACA,IAAI,CAAC,KAAKI,iBAAiB,KAAK,GAAG,GACjC;GAEF,MAAM;EACR;CACF;CAEA,UAAU,QAAsB;EAC9B,KAAKC,UAAU;CACjB;CAEA,UAAgB;EACd,KAAKL,OAAO,QAAQ;CACtB;CAEA,iBAAiB,KAAmB;EAClC,MAAM,MAAM,KAAKE,YAAY,KAAKD,OAAO,KAAK,GAAG;EACjD,OAAO,MAAM,KAAM,QAAQ,KAAK,CAAC,KAAKA,OAAO;CAC/C;CAEA,CAAC,KAAK,QAAiC;EACrC,MAAM,mBAAmB,QAAa,KAAKG,iBAAiB,GAAG;EAC/D,IAAI,OAAO,OAAsB,GAAiB;GAChD,OAAO,4BACL,QACA,iBACA,KAAKC,SACL,IACF;GACA;EACF;EAIA,IAAI,gBAAgB,OAAO,GAAkB,GAAG,GAC9C,OAAO,KAAKA,QAAQ,KAAK,QAAQ,IAAI;CAEzC;CAEA,CAAC,UAAU,SAAqC;EAC9C,IAAI,KAAKA,QAAQ,WACf,OAAO,KAAKA,QAAQ,UAAU,IAAI;CAEtC;CAEA,UAAU,KAAgD;EACxD,MAAM,aAAa;GACjB,KAAK,KAAKJ,OAAO;GACjB,OAAO,KAAKA,OAAO,YAAY,UAAU;EAC3C;EAEA,IAAI,CAAC,IAAI,OAAO;GACd,IAAI,IAAI,SACN;GAEF,OAAO;EACT;EAEA,MAAM,MAAM,KAAKC,YAAY,KAAKD,OAAO,KAAK,IAAI,MAAM,GAAG;EAE3D,IAAI,CAAC,IAAI,SAAS;GAIhB,IAAI,MAAM,GACR,OAAO;GAKT,IAAI,QAAQ,GAAG;IACb,IAAI,KAAKA,OAAO,aAAa,IAAI,MAAM,UAAU,SAC/C,OAAO;KACL,KAAK,KAAKA,OAAO;KACjB,OAAO;IACT;IAEF,OAAO;GACT;GAEA,OAAO,IAAI;EACb;EAEA,IAAI;EAIJ,IAAI,MAAM,GACR,OAAO;EAGT,IAAI,QAAQ,GAAG;GAGb,IAAI,CAAC,KAAKA,OAAO,aAAa,IAAI,MAAM,UAAU,MAChD,OAAO;GAIT,OAAO;EACT;EAGA,OAAO,IAAI;CACb;AACF"}
1
+ {"version":3,"file":"skip.js","names":[],"sources":["../../../../../zql/src/ivm/skip.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport type {Row} from '../../../zero-protocol/src/data.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport {ChangeType} from './change-type.ts';\nimport {\n type AddChange,\n type Change,\n type ChildChange,\n type RemoveChange,\n} from './change.ts';\nimport type {Comparator, Node} from './data.ts';\nimport {maybeSplitAndPushEditChange} from './maybe-split-and-push-edit-change.ts';\nimport {\n throwOutput,\n type FetchRequest,\n type Input,\n type InputBase,\n type Operator,\n type Output,\n type Start,\n} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport type {Stream} from './stream.ts';\n\nexport type Bound = {\n row: Row;\n exclusive: boolean;\n};\n\n/**\n * Skip sets the start position for the pipeline. No rows before the bound will\n * be output.\n */\nexport class Skip implements Operator {\n readonly #input: Input;\n readonly #bound: Bound;\n readonly #comparator: Comparator;\n\n #output: Output = throwOutput;\n\n constructor(input: Input, bound: Bound) {\n const {sort} = input.getSchema();\n assert(sort !== undefined, 'Skip requires sorted input');\n this.#input = input;\n this.#bound = bound;\n this.#comparator = input.getSchema().compareRows;\n input.setOutput(this);\n }\n\n getSchema(): SourceSchema {\n return this.#input.getSchema();\n }\n\n *fetch(req: FetchRequest): Stream<Node | 'yield'> {\n const start = this.#getStart(req);\n if (start === 'empty') {\n return;\n }\n const nodes = this.#input.fetch({...req, start});\n if (!req.reverse) {\n yield* nodes;\n return;\n }\n for (const node of nodes) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n if (!this.#shouldBePresent(node.row)) {\n return;\n }\n yield node;\n }\n }\n\n setOutput(output: Output): void {\n this.#output = output;\n }\n\n destroy(): void {\n this.#input.destroy();\n }\n\n #shouldBePresent(row: Row): boolean {\n const cmp = this.#comparator(this.#bound.row, row);\n return cmp < 0 || (cmp === 0 && !this.#bound.exclusive);\n }\n\n *push(change: Change): Stream<'yield'> {\n const shouldBePresent = (row: Row) => this.#shouldBePresent(row);\n if (change[ChangeIndex.TYPE] === ChangeType.EDIT) {\n yield* maybeSplitAndPushEditChange(\n change,\n shouldBePresent,\n this.#output,\n this,\n );\n return;\n }\n\n change satisfies AddChange | RemoveChange | ChildChange;\n\n if (shouldBePresent(change[ChangeIndex.NODE].row)) {\n yield* this.#output.push(change, this);\n }\n }\n\n *reconcile(_pusher: InputBase): Stream<'yield'> {\n if (this.#output.reconcile) {\n yield* this.#output.reconcile(this);\n }\n }\n\n #getStart(req: FetchRequest): Start | undefined | 'empty' {\n const boundStart = {\n row: this.#bound.row,\n basis: this.#bound.exclusive ? 'after' : 'at',\n } as const;\n\n if (!req.start) {\n if (req.reverse) {\n return undefined;\n }\n return boundStart;\n }\n\n const cmp = this.#comparator(this.#bound.row, req.start.row);\n\n if (!req.reverse) {\n // The skip bound is after the requested bound. The requested bound cannot\n // be relevant because even if it was basis: 'after', the skip bound is\n // itself after the requested bound. Return the skip bound.\n if (cmp > 0) {\n return boundStart;\n }\n\n // The skip bound and requested bound are equal. If either is exclusive,\n // return that bound with exclusive. Otherwise, return the skip bound.\n if (cmp === 0) {\n if (this.#bound.exclusive || req.start.basis === 'after') {\n return {\n row: this.#bound.row,\n basis: 'after',\n };\n }\n return boundStart;\n }\n\n return req.start;\n }\n\n req.reverse satisfies true;\n\n // bound is after the start, but request is for reverse so results\n // must be empty\n if (cmp > 0) {\n return 'empty';\n }\n\n if (cmp === 0) {\n // if both are inclusive, the result can be the single row at bound\n // return it as start\n if (!this.#bound.exclusive && req.start.basis === 'at') {\n return boundStart;\n }\n // otherwise the results must be empty, one or both are exclusive\n // in opposite directions\n return 'empty';\n }\n\n // bound is before the start, return start\n return req.start;\n }\n}\n"],"mappings":";;;;;;;;AAiCA,IAAa,OAAb,MAAsC;CACpC;CACA;CACA;CAEA,UAAkB;CAElB,YAAY,OAAc,OAAc;EACtC,MAAM,EAAC,SAAQ,MAAM,UAAU;EAC/B,OAAO,SAAS,KAAA,GAAW,4BAA4B;EACvD,KAAK,SAAS;EACd,KAAK,SAAS;EACd,KAAK,cAAc,MAAM,UAAU,CAAC,CAAC;EACrC,MAAM,UAAU,IAAI;CACtB;CAEA,YAA0B;EACxB,OAAO,KAAK,OAAO,UAAU;CAC/B;CAEA,CAAC,MAAM,KAA2C;EAChD,MAAM,QAAQ,KAAK,UAAU,GAAG;EAChC,IAAI,UAAU,SACZ;EAEF,MAAM,QAAQ,KAAK,OAAO,MAAM;GAAC,GAAG;GAAK;EAAK,CAAC;EAC/C,IAAI,CAAC,IAAI,SAAS;GAChB,OAAO;GACP;EACF;EACA,KAAK,MAAM,QAAQ,OAAO;GACxB,IAAI,SAAS,SAAS;IACpB,MAAM;IACN;GACF;GACA,IAAI,CAAC,KAAK,iBAAiB,KAAK,GAAG,GACjC;GAEF,MAAM;EACR;CACF;CAEA,UAAU,QAAsB;EAC9B,KAAK,UAAU;CACjB;CAEA,UAAgB;EACd,KAAK,OAAO,QAAQ;CACtB;CAEA,iBAAiB,KAAmB;EAClC,MAAM,MAAM,KAAK,YAAY,KAAK,OAAO,KAAK,GAAG;EACjD,OAAO,MAAM,KAAM,QAAQ,KAAK,CAAC,KAAK,OAAO;CAC/C;CAEA,CAAC,KAAK,QAAiC;EACrC,MAAM,mBAAmB,QAAa,KAAK,iBAAiB,GAAG;EAC/D,IAAI,OAAO,OAAsB,GAAiB;GAChD,OAAO,4BACL,QACA,iBACA,KAAK,SACL,IACF;GACA;EACF;EAIA,IAAI,gBAAgB,OAAO,EAAiB,CAAC,GAAG,GAC9C,OAAO,KAAK,QAAQ,KAAK,QAAQ,IAAI;CAEzC;CAEA,CAAC,UAAU,SAAqC;EAC9C,IAAI,KAAK,QAAQ,WACf,OAAO,KAAK,QAAQ,UAAU,IAAI;CAEtC;CAEA,UAAU,KAAgD;EACxD,MAAM,aAAa;GACjB,KAAK,KAAK,OAAO;GACjB,OAAO,KAAK,OAAO,YAAY,UAAU;EAC3C;EAEA,IAAI,CAAC,IAAI,OAAO;GACd,IAAI,IAAI,SACN;GAEF,OAAO;EACT;EAEA,MAAM,MAAM,KAAK,YAAY,KAAK,OAAO,KAAK,IAAI,MAAM,GAAG;EAE3D,IAAI,CAAC,IAAI,SAAS;GAIhB,IAAI,MAAM,GACR,OAAO;GAKT,IAAI,QAAQ,GAAG;IACb,IAAI,KAAK,OAAO,aAAa,IAAI,MAAM,UAAU,SAC/C,OAAO;KACL,KAAK,KAAK,OAAO;KACjB,OAAO;IACT;IAEF,OAAO;GACT;GAEA,OAAO,IAAI;EACb;EAEA,IAAI;EAIJ,IAAI,MAAM,GACR,OAAO;EAGT,IAAI,QAAQ,GAAG;GAGb,IAAI,CAAC,KAAK,OAAO,aAAa,IAAI,MAAM,UAAU,MAChD,OAAO;GAIT,OAAO;EACT;EAGA,OAAO,IAAI;CACb;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"take-gate.js","names":["#input","#comparator","#openDepth","#boundProvider","#output"],"sources":["../../../../../zql/src/ivm/take-gate.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport type {Row} from '../../../zero-protocol/src/data.ts';\nimport type {Change} from './change.ts';\nimport {constraintMatchesRow, type Constraint} from './constraint.ts';\nimport type {Comparator, Node} from './data.ts';\nimport {\n throwOutput,\n type FetchRequest,\n type Input,\n type InputBase,\n type Operator,\n type Output,\n} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport type {Stream} from './stream.ts';\n\nexport interface TakeBoundProvider {\n getBound(constraint?: Constraint): Row | undefined;\n}\n\n/**\n * The bound that a TakeGate caps one parent fetch of a join's child push at.\n * `undefined` means the fetch is not capped.\n */\nexport type ParentFetchBound = {\n readonly constraint: Constraint;\n readonly bound: Row | undefined;\n};\n\n/**\n * Reads the bounds for the parent fetches of a child push before the join\n * starts them. TakeGate reads its bound once, when a fetch starts, so the set\n * of parents that get the push stays fixed even when Take moves its bound\n * during the push. Split-push overlays must use these bounds, not the bound\n * Take reports later in the push.\n */\nexport function readParentFetchBounds(\n boundProvider: TakeBoundProvider,\n constraints: readonly Constraint[],\n): ParentFetchBound[] {\n return constraints.map(constraint => ({\n constraint,\n bound: boundProvider.getBound(constraint),\n }));\n}\n\n/**\n * Returns true if one of the parent fetches of the push yields `row`: `row`\n * matches the fetch's constraint and is not after the fetch's bound.\n */\nexport function isInParentFetch(\n bounds: readonly ParentFetchBound[],\n row: Row,\n compareRows: Comparator,\n): boolean {\n for (const {constraint, bound} of bounds) {\n if (constraintMatchesRow(constraint, row)) {\n return bound === undefined || compareRows(row, bound) <= 0;\n }\n }\n return false;\n}\n\n/**\n * TakeGate bounds upstream parent fetches using the active bound from a\n * downstream Take operator.\n *\n * When a child table changes, joins fetch matching parent rows. Without\n * TakeGate, this fetch scans all matching parent rows (which may be thousands)\n * even if downstream Take only needs a few. TakeGate intercepts this fetch and\n * stops the upstream stream once rows exceed Take's bound.\n */\nexport class TakeGate implements Operator, TakeBoundProvider {\n readonly #input: Input;\n readonly #comparator: Comparator;\n #boundProvider: TakeBoundProvider | undefined;\n #output: Output = throwOutput;\n #openDepth = 0;\n\n constructor(input: Input) {\n const {sort, compareRows} = input.getSchema();\n assert(sort !== undefined, 'TakeGate requires sorted input');\n this.#input = input;\n this.#comparator = compareRows;\n input.setOutput(this);\n }\n\n open(): void {\n this.#openDepth++;\n }\n\n close(): void {\n assert(this.#openDepth > 0, 'TakeGate.close called without matching open');\n this.#openDepth--;\n }\n\n isOpen(): boolean {\n return this.#openDepth > 0;\n }\n\n setBoundProvider(provider: TakeBoundProvider): void {\n this.#boundProvider = provider;\n }\n\n getBoundProvider(): TakeBoundProvider | undefined {\n return this.#boundProvider;\n }\n\n getBound(constraint?: Constraint): Row | undefined {\n if (this.#openDepth > 0) {\n return undefined;\n }\n return this.#boundProvider?.getBound(constraint);\n }\n\n getSchema(): SourceSchema {\n return this.#input.getSchema();\n }\n\n setOutput(output: Output): void {\n this.#output = output;\n }\n\n destroy(): void {\n this.#input.destroy();\n }\n\n *push(change: Change, _pusher: InputBase): Stream<'yield'> {\n yield* this.#output.push(change, this);\n }\n\n *reconcile(_pusher: InputBase): Stream<'yield'> {\n if (this.#output.reconcile) {\n yield* this.#output.reconcile(this);\n }\n }\n\n *fetch(req: FetchRequest): Stream<Node | 'yield'> {\n if (this.#openDepth > 0 || req.reverse) {\n yield* this.#input.fetch(req);\n return;\n }\n\n const bound = this.#boundProvider?.getBound(req.constraint);\n if (!bound) {\n yield* this.#input.fetch(req);\n return;\n }\n\n for (const node of this.#input.fetch(req)) {\n if (node === 'yield') {\n yield 'yield';\n continue;\n }\n if (this.#comparator(node.row, bound) > 0) {\n return;\n }\n yield node;\n }\n }\n}\n"],"mappings":";;;;;;;;;;;AAoCA,SAAgB,sBACd,eACA,aACoB;CACpB,OAAO,YAAY,KAAI,gBAAe;EACpC;EACA,OAAO,cAAc,SAAS,UAAU;CAC1C,EAAE;AACJ;;;;;AAMA,SAAgB,gBACd,QACA,KACA,aACS;CACT,KAAK,MAAM,EAAC,YAAY,WAAU,QAChC,IAAI,qBAAqB,YAAY,GAAG,GACtC,OAAO,UAAU,KAAA,KAAa,YAAY,KAAK,KAAK,KAAK;CAG7D,OAAO;AACT;;;;;;;;;;AAWA,IAAa,WAAb,MAA6D;CAC3D;CACA;CACA;CACA,UAAkB;CAClB,aAAa;CAEb,YAAY,OAAc;EACxB,MAAM,EAAC,MAAM,gBAAe,MAAM,UAAU;EAC5C,OAAO,SAAS,KAAA,GAAW,gCAAgC;EAC3D,KAAKA,SAAS;EACd,KAAKC,cAAc;EACnB,MAAM,UAAU,IAAI;CACtB;CAEA,OAAa;EACX,KAAKC;CACP;CAEA,QAAc;EACZ,OAAO,KAAKA,aAAa,GAAG,6CAA6C;EACzE,KAAKA;CACP;CAEA,SAAkB;EAChB,OAAO,KAAKA,aAAa;CAC3B;CAEA,iBAAiB,UAAmC;EAClD,KAAKC,iBAAiB;CACxB;CAEA,mBAAkD;EAChD,OAAO,KAAKA;CACd;CAEA,SAAS,YAA0C;EACjD,IAAI,KAAKD,aAAa,GACpB;EAEF,OAAO,KAAKC,gBAAgB,SAAS,UAAU;CACjD;CAEA,YAA0B;EACxB,OAAO,KAAKH,OAAO,UAAU;CAC/B;CAEA,UAAU,QAAsB;EAC9B,KAAKI,UAAU;CACjB;CAEA,UAAgB;EACd,KAAKJ,OAAO,QAAQ;CACtB;CAEA,CAAC,KAAK,QAAgB,SAAqC;EACzD,OAAO,KAAKI,QAAQ,KAAK,QAAQ,IAAI;CACvC;CAEA,CAAC,UAAU,SAAqC;EAC9C,IAAI,KAAKA,QAAQ,WACf,OAAO,KAAKA,QAAQ,UAAU,IAAI;CAEtC;CAEA,CAAC,MAAM,KAA2C;EAChD,IAAI,KAAKF,aAAa,KAAK,IAAI,SAAS;GACtC,OAAO,KAAKF,OAAO,MAAM,GAAG;GAC5B;EACF;EAEA,MAAM,QAAQ,KAAKG,gBAAgB,SAAS,IAAI,UAAU;EAC1D,IAAI,CAAC,OAAO;GACV,OAAO,KAAKH,OAAO,MAAM,GAAG;GAC5B;EACF;EAEA,KAAK,MAAM,QAAQ,KAAKA,OAAO,MAAM,GAAG,GAAG;GACzC,IAAI,SAAS,SAAS;IACpB,MAAM;IACN;GACF;GACA,IAAI,KAAKC,YAAY,KAAK,KAAK,KAAK,IAAI,GACtC;GAEF,MAAM;EACR;CACF;AACF"}
1
+ {"version":3,"file":"take-gate.js","names":[],"sources":["../../../../../zql/src/ivm/take-gate.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport type {Row} from '../../../zero-protocol/src/data.ts';\nimport type {Change} from './change.ts';\nimport {constraintMatchesRow, type Constraint} from './constraint.ts';\nimport type {Comparator, Node} from './data.ts';\nimport {\n throwOutput,\n type FetchRequest,\n type Input,\n type InputBase,\n type Operator,\n type Output,\n} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport type {Stream} from './stream.ts';\n\nexport interface TakeBoundProvider {\n getBound(constraint?: Constraint): Row | undefined;\n}\n\n/**\n * The bound that a TakeGate caps one parent fetch of a join's child push at.\n * `undefined` means the fetch is not capped.\n */\nexport type ParentFetchBound = {\n readonly constraint: Constraint;\n readonly bound: Row | undefined;\n};\n\n/**\n * Reads the bounds for the parent fetches of a child push before the join\n * starts them. TakeGate reads its bound once, when a fetch starts, so the set\n * of parents that get the push stays fixed even when Take moves its bound\n * during the push. Split-push overlays must use these bounds, not the bound\n * Take reports later in the push.\n */\nexport function readParentFetchBounds(\n boundProvider: TakeBoundProvider,\n constraints: readonly Constraint[],\n): ParentFetchBound[] {\n return constraints.map(constraint => ({\n constraint,\n bound: boundProvider.getBound(constraint),\n }));\n}\n\n/**\n * Returns true if one of the parent fetches of the push yields `row`: `row`\n * matches the fetch's constraint and is not after the fetch's bound.\n */\nexport function isInParentFetch(\n bounds: readonly ParentFetchBound[],\n row: Row,\n compareRows: Comparator,\n): boolean {\n for (const {constraint, bound} of bounds) {\n if (constraintMatchesRow(constraint, row)) {\n return bound === undefined || compareRows(row, bound) <= 0;\n }\n }\n return false;\n}\n\n/**\n * TakeGate bounds upstream parent fetches using the active bound from a\n * downstream Take operator.\n *\n * When a child table changes, joins fetch matching parent rows. Without\n * TakeGate, this fetch scans all matching parent rows (which may be thousands)\n * even if downstream Take only needs a few. TakeGate intercepts this fetch and\n * stops the upstream stream once rows exceed Take's bound.\n */\nexport class TakeGate implements Operator, TakeBoundProvider {\n readonly #input: Input;\n readonly #comparator: Comparator;\n #boundProvider: TakeBoundProvider | undefined;\n #output: Output = throwOutput;\n #openDepth = 0;\n\n constructor(input: Input) {\n const {sort, compareRows} = input.getSchema();\n assert(sort !== undefined, 'TakeGate requires sorted input');\n this.#input = input;\n this.#comparator = compareRows;\n input.setOutput(this);\n }\n\n open(): void {\n this.#openDepth++;\n }\n\n close(): void {\n assert(this.#openDepth > 0, 'TakeGate.close called without matching open');\n this.#openDepth--;\n }\n\n isOpen(): boolean {\n return this.#openDepth > 0;\n }\n\n setBoundProvider(provider: TakeBoundProvider): void {\n this.#boundProvider = provider;\n }\n\n getBoundProvider(): TakeBoundProvider | undefined {\n return this.#boundProvider;\n }\n\n getBound(constraint?: Constraint): Row | undefined {\n if (this.#openDepth > 0) {\n return undefined;\n }\n return this.#boundProvider?.getBound(constraint);\n }\n\n getSchema(): SourceSchema {\n return this.#input.getSchema();\n }\n\n setOutput(output: Output): void {\n this.#output = output;\n }\n\n destroy(): void {\n this.#input.destroy();\n }\n\n *push(change: Change, _pusher: InputBase): Stream<'yield'> {\n yield* this.#output.push(change, this);\n }\n\n *reconcile(_pusher: InputBase): Stream<'yield'> {\n if (this.#output.reconcile) {\n yield* this.#output.reconcile(this);\n }\n }\n\n *fetch(req: FetchRequest): Stream<Node | 'yield'> {\n if (this.#openDepth > 0 || req.reverse) {\n yield* this.#input.fetch(req);\n return;\n }\n\n const bound = this.#boundProvider?.getBound(req.constraint);\n if (!bound) {\n yield* this.#input.fetch(req);\n return;\n }\n\n for (const node of this.#input.fetch(req)) {\n if (node === 'yield') {\n yield 'yield';\n continue;\n }\n if (this.#comparator(node.row, bound) > 0) {\n return;\n }\n yield node;\n }\n }\n}\n"],"mappings":";;;;;;;;;;;AAoCA,SAAgB,sBACd,eACA,aACoB;CACpB,OAAO,YAAY,KAAI,gBAAe;EACpC;EACA,OAAO,cAAc,SAAS,UAAU;CAC1C,EAAE;AACJ;;;;;AAMA,SAAgB,gBACd,QACA,KACA,aACS;CACT,KAAK,MAAM,EAAC,YAAY,WAAU,QAChC,IAAI,qBAAqB,YAAY,GAAG,GACtC,OAAO,UAAU,KAAA,KAAa,YAAY,KAAK,KAAK,KAAK;CAG7D,OAAO;AACT;;;;;;;;;;AAWA,IAAa,WAAb,MAA6D;CAC3D;CACA;CACA;CACA,UAAkB;CAClB,aAAa;CAEb,YAAY,OAAc;EACxB,MAAM,EAAC,MAAM,gBAAe,MAAM,UAAU;EAC5C,OAAO,SAAS,KAAA,GAAW,gCAAgC;EAC3D,KAAK,SAAS;EACd,KAAK,cAAc;EACnB,MAAM,UAAU,IAAI;CACtB;CAEA,OAAa;EACX,KAAK;CACP;CAEA,QAAc;EACZ,OAAO,KAAK,aAAa,GAAG,6CAA6C;EACzE,KAAK;CACP;CAEA,SAAkB;EAChB,OAAO,KAAK,aAAa;CAC3B;CAEA,iBAAiB,UAAmC;EAClD,KAAK,iBAAiB;CACxB;CAEA,mBAAkD;EAChD,OAAO,KAAK;CACd;CAEA,SAAS,YAA0C;EACjD,IAAI,KAAK,aAAa,GACpB;EAEF,OAAO,KAAK,gBAAgB,SAAS,UAAU;CACjD;CAEA,YAA0B;EACxB,OAAO,KAAK,OAAO,UAAU;CAC/B;CAEA,UAAU,QAAsB;EAC9B,KAAK,UAAU;CACjB;CAEA,UAAgB;EACd,KAAK,OAAO,QAAQ;CACtB;CAEA,CAAC,KAAK,QAAgB,SAAqC;EACzD,OAAO,KAAK,QAAQ,KAAK,QAAQ,IAAI;CACvC;CAEA,CAAC,UAAU,SAAqC;EAC9C,IAAI,KAAK,QAAQ,WACf,OAAO,KAAK,QAAQ,UAAU,IAAI;CAEtC;CAEA,CAAC,MAAM,KAA2C;EAChD,IAAI,KAAK,aAAa,KAAK,IAAI,SAAS;GACtC,OAAO,KAAK,OAAO,MAAM,GAAG;GAC5B;EACF;EAEA,MAAM,QAAQ,KAAK,gBAAgB,SAAS,IAAI,UAAU;EAC1D,IAAI,CAAC,OAAO;GACV,OAAO,KAAK,OAAO,MAAM,GAAG;GAC5B;EACF;EAEA,KAAK,MAAM,QAAQ,KAAK,OAAO,MAAM,GAAG,GAAG;GACzC,IAAI,SAAS,SAAS;IACpB,MAAM;IACN;GACF;GACA,IAAI,KAAK,YAAY,KAAK,KAAK,KAAK,IAAI,GACtC;GAEF,MAAM;EACR;CACF;AACF"}
@@ -439,6 +439,6 @@ function makePartitionKeyComparator(partitionKey) {
439
439
  };
440
440
  }
441
441
  //#endregion
442
- export { Take, constraintMatchesPartitionKey, makePartitionKeyComparator };
442
+ export { Take, constraintContainsPartitionKey, constraintMatchesPartitionKey, makePartitionKeyComparator };
443
443
 
444
444
  //# sourceMappingURL=take.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"take.js","names":["#input","#storage","#limit","#partitionKey","#partitionKeyComparator","#dirtyPartitions","#takeGate","#output","#initialFetch","#rowHiddenFromFetch","#setTakeState","#pushEditChange","#getStateAndConstraint","#pushWithRowHiddenFromFetch"],"sources":["../../../../../zql/src/ivm/take.ts"],"sourcesContent":["import {assert, unreachable} from '../../../shared/src/asserts.ts';\nimport {hasOwn} from '../../../shared/src/has-own.ts';\nimport type {Row, Value} from '../../../zero-protocol/src/data.ts';\nimport type {PrimaryKey} from '../../../zero-protocol/src/primary-key.ts';\nimport {assertOrderingIncludesPK} from '../query/complete-ordering.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport {ChangeType} from './change-type.ts';\nimport {\n makeAddChange,\n makeRemoveChange,\n type Change,\n type EditChange,\n} from './change.ts';\nimport type {Constraint} from './constraint.ts';\nimport {compareValues, type Comparator, type Node} from './data.ts';\nimport {\n throwOutput,\n type FetchRequest,\n type Input,\n type InputBase,\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 type {TakeBoundProvider, TakeGate} from './take-gate.ts';\n\ntype TakeState = {\n size: number;\n bound: Row | undefined;\n};\n\ninterface TakeStorage {\n get(key: string): TakeState | undefined;\n set(key: string, value: TakeState): void;\n del(key: string): void;\n destroy(): void;\n}\n\nexport type PartitionKey = PrimaryKey;\n\ntype DirtyPartitionState = {\n constraint: Constraint | undefined;\n /**\n * The bound to report to upstream TakeGate via getBound() until Phase 2\n * reconciliation completes.\n *\n * When removals reduce partition size below limit in Phase 1, normal getBound()\n * would return undefined (unbounded), which would cause upstream TakeGate to open\n * and allow unbounded parent pushes through. Preserving gateBound keeps TakeGate\n * capped at the pre-removal boundary until Phase 2 refills the deficit.\n */\n gateBound: Row | undefined;\n};\n\n/**\n * The Take operator is for implementing limit queries. It takes the first n\n * nodes of its input as determined by the input’s comparator. It then keeps\n * a *bound* of the last item it has accepted so that it can evaluate whether\n * new incoming pushes should be accepted or rejected.\n *\n * Take can count rows globally or by unique value of some field.\n *\n * Maintains the invariant that its output size is always <= limit, even\n * mid processing of a push.\n */\nexport class Take implements Operator, TakeBoundProvider {\n readonly #input: Input;\n readonly #storage: TakeStorage;\n readonly #limit: number;\n readonly #partitionKey: PartitionKey | undefined;\n readonly #partitionKeyComparator: Comparator | undefined;\n // Fetch overlay needed for some split push cases.\n #rowHiddenFromFetch: Row | undefined;\n\n #takeGate: TakeGate | undefined;\n /**\n * Partitions that incurred removals in Phase 1 and require deficit refills in\n * Phase 2 (reconcile). Keyed by `takeStateKey` (see `getTakeStateKey`), matching\n * `#storage` keys: `'[\"take\"]'` when unpartitioned, or `'[\"take\", ...partitionValues]'`\n * when partitioned.\n */\n readonly #dirtyPartitions = new Map<string, DirtyPartitionState>();\n\n #output: Output = throwOutput;\n\n setTakeGate(gate: TakeGate): void {\n this.#takeGate = gate;\n }\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 const {sort} = input.getSchema();\n assert(sort !== undefined, 'Take requires sorted input');\n assertOrderingIncludesPK(sort, input.getSchema().primaryKey);\n input.setOutput(this);\n this.#input = input;\n this.#storage = storage as TakeStorage;\n this.#limit = limit;\n this.#partitionKey = partitionKey;\n this.#partitionKeyComparator =\n partitionKey && makePartitionKeyComparator(partitionKey);\n }\n\n setOutput(output: Output): void {\n this.#output = output;\n }\n\n getSchema(): SourceSchema {\n return this.#input.getSchema();\n }\n\n getBound(constraint?: Constraint): Row | undefined {\n if (\n this.#partitionKey &&\n !constraintContainsPartitionKey(constraint, this.#partitionKey)\n ) {\n return undefined;\n }\n const takeStateKey = getTakeStateKey(this.#partitionKey, constraint);\n const dirty = this.#dirtyPartitions.get(takeStateKey);\n if (dirty) {\n // While dirty in Phase 1, return gateBound (the bound prior to removals) so\n // upstream operators (e.g. TakeGate) remain capped rather than unbounding during push.\n return dirty.gateBound;\n }\n const takeState = this.#storage.get(takeStateKey);\n if (!takeState || takeState.size < this.#limit) {\n return undefined;\n }\n return takeState.bound;\n }\n\n *fetch(req: FetchRequest): Stream<Node | 'yield'> {\n assert(\n !this.#partitionKey ||\n (req.constraint !== undefined &&\n constraintContainsPartitionKey(req.constraint, this.#partitionKey)),\n 'Partitioned take does not allow unpartitioned fetches',\n );\n\n const takeStateKey = getTakeStateKey(this.#partitionKey, req.constraint);\n const takeState = this.#storage.get(takeStateKey);\n if (!takeState) {\n if (constraintMatchesPartitionKey(req.constraint, this.#partitionKey)) {\n yield* this.#initialFetch(req, takeStateKey);\n }\n return;\n }\n if (takeState.bound === undefined) {\n return;\n }\n let count = 0;\n for (const inputNode of this.#input.fetch(req)) {\n if (inputNode === 'yield') {\n yield inputNode;\n continue;\n }\n if (this.getSchema().compareRows(takeState.bound, inputNode.row) < 0) {\n return;\n }\n if (\n this.#rowHiddenFromFetch &&\n this.getSchema().compareRows(\n this.#rowHiddenFromFetch,\n inputNode.row,\n ) === 0\n ) {\n continue;\n }\n yield inputNode;\n count++;\n if (count >= this.#limit) {\n return;\n }\n }\n }\n\n *#initialFetch(\n req: FetchRequest,\n takeStateKey: string,\n ): Stream<Node | 'yield'> {\n assert(req.start === undefined, 'Start should be undefined');\n assert(!req.reverse, 'Reverse should be false');\n\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 // The caller (fetch) has already checked that there is no state for\n // takeStateKey.\n\n let size = 0;\n let bound: Row | undefined;\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 bound = inputNode.row;\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.#setTakeState(takeStateKey, size, bound);\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 takeState is properly hydrated.\n assert(\n !downstreamEarlyReturn,\n 'Unexpected early return prevented full hydration',\n );\n }\n }\n }\n\n #getStateAndConstraint(row: Row) {\n const takeStateKey = getTakeStateKey(this.#partitionKey, row);\n const takeState = this.#storage.get(takeStateKey);\n let constraint: Constraint | undefined;\n if (takeState) {\n constraint =\n this.#partitionKey &&\n Object.fromEntries(\n this.#partitionKey.map(key => [key, row[key]] as const),\n );\n }\n\n return {takeState, takeStateKey, constraint} as\n | {\n takeState: undefined;\n takeStateKey: string;\n constraint: undefined;\n }\n | {\n takeState: TakeState;\n takeStateKey: string;\n constraint: Constraint | undefined;\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 {takeState, takeStateKey, constraint} = this.#getStateAndConstraint(\n change[ChangeIndex.NODE].row,\n );\n if (!takeState) {\n return;\n }\n\n const {compareRows} = this.getSchema();\n\n if (change[ChangeIndex.TYPE] === ChangeType.ADD) {\n if (takeState.size < this.#limit) {\n if (this.#dirtyPartitions.has(takeStateKey)) {\n // While dirty with an unresolved deficit in Phase 1, only admit rows that\n // sort strictly before the current window boundary. We refuse to expand the\n // bound forward during Phase 1 so that Phase 2 can backfill earlier candidate\n // rows from storage in sort order.\n if (\n takeState.bound === undefined ||\n compareRows(change[ChangeIndex.NODE].row, takeState.bound) >= 0\n ) {\n return;\n }\n }\n const nextSize = takeState.size + 1;\n if (nextSize === this.#limit) {\n this.#dirtyPartitions.delete(takeStateKey);\n }\n this.#setTakeState(\n takeStateKey,\n nextSize,\n takeState.bound === undefined ||\n compareRows(takeState.bound, change[ChangeIndex.NODE].row) < 0\n ? change[ChangeIndex.NODE].row\n : takeState.bound,\n );\n yield* this.#output.push(change, this);\n return;\n }\n // size === limit\n if (\n takeState.bound === undefined ||\n compareRows(change[ChangeIndex.NODE].row, takeState.bound) >= 0\n ) {\n return;\n }\n // added row < bound\n let beforeBoundNode: Node | undefined;\n let boundNode: Node | undefined;\n if (this.#limit === 1) {\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n boundNode = node;\n break;\n }\n } else {\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n reverse: true,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n } else if (boundNode === undefined) {\n boundNode = node;\n } else {\n beforeBoundNode = node;\n break;\n }\n }\n }\n assert(\n boundNode !== undefined,\n 'Take: boundNode must be found during fetch',\n );\n const removeChange = makeRemoveChange(boundNode);\n // Remove before add to maintain invariant that\n // output size <= limit.\n this.#setTakeState(\n takeStateKey,\n takeState.size,\n beforeBoundNode === undefined ||\n compareRows(change[ChangeIndex.NODE].row, beforeBoundNode.row) > 0\n ? change[ChangeIndex.NODE].row\n : beforeBoundNode.row,\n );\n yield* this.#pushWithRowHiddenFromFetch(\n change[ChangeIndex.NODE].row,\n removeChange,\n );\n yield* this.#output.push(change, this);\n } else if (change[ChangeIndex.TYPE] === ChangeType.REMOVE) {\n if (takeState.bound === undefined) {\n // change is after bound\n return;\n }\n const compToBound = compareRows(\n change[ChangeIndex.NODE].row,\n takeState.bound,\n );\n if (compToBound > 0 || (compToBound < 0 && this.#limit === 1)) {\n // change is not in window\n return;\n }\n let beforeBoundNode: Node | undefined;\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'after',\n },\n constraint,\n reverse: true,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n beforeBoundNode = node;\n break;\n }\n\n const finalBound =\n takeState.size - 1 === 0\n ? undefined\n : compToBound < 0\n ? takeState.bound\n : beforeBoundNode?.row;\n if (!this.#dirtyPartitions.has(takeStateKey)) {\n // Snapshot the pre-removal bound as the gate bound on the first removal that\n // dirties this partition. Subsequent getBound() calls throughout Phase 1 will return\n // this bound to keep upstream TakeGate bounded until Phase 2 refills deficits.\n this.#dirtyPartitions.set(takeStateKey, {\n constraint,\n gateBound: takeState.bound,\n });\n }\n this.#setTakeState(takeStateKey, takeState.size - 1, finalBound);\n yield* this.#output.push(change, this);\n } else if (change[ChangeIndex.TYPE] === ChangeType.CHILD) {\n // A 'child' change should be pushed to output if its row\n // is <= bound.\n if (\n takeState.bound &&\n compareRows(change[ChangeIndex.NODE].row, takeState.bound) <= 0\n ) {\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\n const {takeState, takeStateKey, constraint} = this.#getStateAndConstraint(\n change[ChangeIndex.OLD_NODE].row,\n );\n if (!takeState) {\n return;\n }\n\n assert(takeState.bound, 'Bound should be set');\n const {compareRows} = this.getSchema();\n const oldCmp = compareRows(\n change[ChangeIndex.OLD_NODE].row,\n takeState.bound,\n );\n const newCmp = compareRows(change[ChangeIndex.NODE].row, takeState.bound);\n\n const replaceBoundAndForwardChange = () => {\n this.#setTakeState(\n takeStateKey,\n takeState.size,\n change[ChangeIndex.NODE].row,\n );\n return this.#output.push(change, this);\n };\n\n // The bounds row was changed.\n if (oldCmp === 0) {\n // The new row is the new bound.\n if (newCmp === 0) {\n // no need to update the state since we are keeping the bounds\n yield* this.#output.push(change, this);\n return;\n }\n\n if (newCmp < 0) {\n if (this.#limit === 1) {\n yield* replaceBoundAndForwardChange();\n return;\n }\n\n // New row will be in the result but it might not be the bounds any\n // more. We need to find the row before the bounds to determine the new\n // bounds.\n\n let beforeBoundNode: Node | undefined;\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'after',\n },\n constraint,\n reverse: true,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n beforeBoundNode = node;\n break;\n }\n assert(\n beforeBoundNode !== undefined,\n 'Take: beforeBoundNode must be found during fetch',\n );\n\n this.#setTakeState(takeStateKey, takeState.size, beforeBoundNode.row);\n yield* this.#output.push(change, this);\n return;\n }\n\n assert(newCmp > 0, 'New comparison must be greater than 0');\n // Find the first item at the old bounds. This will be the new bounds.\n let newBoundNode: Node | undefined;\n this.#takeGate?.open();\n try {\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n newBoundNode = node;\n break;\n }\n } finally {\n this.#takeGate?.close();\n }\n assert(\n newBoundNode !== undefined,\n 'Take: newBoundNode must be found during fetch',\n );\n\n // The next row is the new row. We can replace the bounds and keep the\n // edit change.\n if (compareRows(newBoundNode.row, change[ChangeIndex.NODE].row) === 0) {\n yield* replaceBoundAndForwardChange();\n return;\n }\n\n // The new row is now outside the bounds, so we need to remove the old\n // row and add the new bounds row.\n this.#setTakeState(takeStateKey, takeState.size, newBoundNode.row);\n yield* this.#pushWithRowHiddenFromFetch(\n newBoundNode.row,\n makeRemoveChange(change[ChangeIndex.OLD_NODE]),\n );\n yield* this.#output.push(makeAddChange(newBoundNode), this);\n return;\n }\n\n if (oldCmp > 0) {\n assert(newCmp !== 0, 'Invalid state. Row has duplicate primary key');\n\n // Both old and new outside of bounds\n if (newCmp > 0) {\n return;\n }\n\n // old was outside, new is inside. Pushing out the old bounds\n assert(newCmp < 0, 'New comparison must be less than 0');\n\n let oldBoundNode: Node | undefined;\n let newBoundNode: Node | undefined;\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n reverse: true,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n } else if (oldBoundNode === undefined) {\n oldBoundNode = node;\n } else {\n newBoundNode = node;\n break;\n }\n }\n assert(\n oldBoundNode !== undefined,\n 'Take: oldBoundNode must be found during fetch',\n );\n assert(\n newBoundNode !== undefined,\n 'Take: newBoundNode must be found during fetch',\n );\n\n // Remove before add to maintain invariant that\n // output size <= limit.\n this.#setTakeState(takeStateKey, takeState.size, newBoundNode.row);\n yield* this.#pushWithRowHiddenFromFetch(\n change[ChangeIndex.NODE].row,\n makeRemoveChange(oldBoundNode),\n );\n yield* this.#output.push(makeAddChange(change[ChangeIndex.NODE]), this);\n\n return;\n }\n\n if (oldCmp < 0) {\n assert(newCmp !== 0, 'Invalid state. Row has duplicate primary key');\n\n // Both old and new inside of bounds\n if (newCmp < 0) {\n yield* this.#output.push(change, this);\n return;\n }\n\n // old was inside, new is larger than old bound\n\n assert(newCmp > 0, 'New comparison must be greater than 0');\n\n // at this point we need to find the row after the bound and use that or\n // the newRow as the new bound.\n let afterBoundNode: Node | undefined;\n this.#takeGate?.open();\n try {\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'after',\n },\n constraint,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n afterBoundNode = node;\n break;\n }\n } finally {\n this.#takeGate?.close();\n }\n assert(\n afterBoundNode !== undefined,\n 'Take: afterBoundNode must be found during fetch',\n );\n\n // The new row is the new bound. Use an edit change.\n if (compareRows(afterBoundNode.row, change[ChangeIndex.NODE].row) === 0) {\n yield* replaceBoundAndForwardChange();\n return;\n }\n\n yield* this.#output.push(\n makeRemoveChange(change[ChangeIndex.OLD_NODE]),\n this,\n );\n this.#setTakeState(takeStateKey, takeState.size, afterBoundNode.row);\n yield* this.#output.push(makeAddChange(afterBoundNode), this);\n return;\n }\n\n unreachable();\n }\n\n *#pushWithRowHiddenFromFetch(row: Row, change: Change) {\n this.#rowHiddenFromFetch = row;\n try {\n yield* this.#output.push(change, this);\n } finally {\n this.#rowHiddenFromFetch = undefined;\n }\n }\n\n #setTakeState(takeStateKey: string, size: number, bound: Row | undefined) {\n this.#storage.set(takeStateKey, {\n size,\n bound,\n });\n }\n\n destroy(): void {\n this.#input.destroy();\n this.#storage.destroy();\n }\n\n *reconcile(_pusher?: InputBase): Stream<'yield'> {\n if (this.#dirtyPartitions.size > 0) {\n const dirty = [...this.#dirtyPartitions.entries()];\n this.#dirtyPartitions.clear();\n\n for (const [takeStateKey, {constraint}] of dirty) {\n const takeState = this.#storage.get(takeStateKey);\n assert(\n takeState !== undefined,\n 'Take: dirty partition must exist in storage',\n );\n const deficit = this.#limit - takeState.size;\n assert(deficit > 0, 'Take: dirty partition must have a deficit');\n\n const toPush: Node[] = [];\n this.#takeGate?.open();\n try {\n const stream = this.#input.fetch({\n start: takeState.bound\n ? {\n row: takeState.bound,\n basis: 'after',\n }\n : undefined,\n constraint,\n });\n\n for (const node of stream) {\n if (node === 'yield') {\n yield 'yield';\n continue;\n }\n toPush.push(node);\n if (toPush.length === deficit) {\n break;\n }\n }\n } finally {\n this.#takeGate?.close();\n }\n\n let currentSize = takeState.size;\n for (const node of toPush) {\n currentSize++;\n this.#setTakeState(takeStateKey, currentSize, node.row);\n yield* this.#output.push(makeAddChange(node), this);\n }\n }\n }\n\n if (this.#output.reconcile) {\n yield* this.#output.reconcile(this);\n }\n }\n}\n\nfunction getTakeStateKey(\n partitionKey: PartitionKey | undefined,\n rowOrConstraint: Row | Constraint | undefined,\n): string {\n // The order must be consistent. We always use the order as defined by the\n // partition key.\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(['take', ...partitionValues]);\n}\n\nexport function constraintMatchesPartitionKey(\n constraint: Constraint | undefined,\n partitionKey: PartitionKey | undefined,\n): boolean {\n if (constraint === undefined || partitionKey === undefined) {\n return constraint === partitionKey;\n }\n if (partitionKey.length !== Object.keys(constraint).length) {\n return false;\n }\n for (const key of partitionKey) {\n if (!hasOwn(constraint, key)) {\n return false;\n }\n }\n return true;\n}\n\nexport function constraintContainsPartitionKey(\n constraint: Constraint | undefined,\n partitionKey: PartitionKey | undefined,\n): boolean {\n if (constraint === undefined || partitionKey === undefined) {\n return false;\n }\n for (const key of partitionKey) {\n if (!hasOwn(constraint, key)) {\n return false;\n }\n }\n return true;\n}\n\nexport function makePartitionKeyComparator(\n partitionKey: PartitionKey,\n): Comparator {\n return (a, b) => {\n for (const key of partitionKey) {\n const cmp = compareValues(a[key], b[key]);\n if (cmp !== 0) {\n return cmp;\n }\n }\n return 0;\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"take.js","names":[],"sources":["../../../../../zql/src/ivm/take.ts"],"sourcesContent":["import {assert, unreachable} from '../../../shared/src/asserts.ts';\nimport {hasOwn} from '../../../shared/src/has-own.ts';\nimport type {Row, Value} from '../../../zero-protocol/src/data.ts';\nimport type {PrimaryKey} from '../../../zero-protocol/src/primary-key.ts';\nimport {assertOrderingIncludesPK} from '../query/complete-ordering.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport {ChangeType} from './change-type.ts';\nimport {\n makeAddChange,\n makeRemoveChange,\n type Change,\n type EditChange,\n} from './change.ts';\nimport type {Constraint} from './constraint.ts';\nimport {compareValues, type Comparator, type Node} from './data.ts';\nimport {\n throwOutput,\n type FetchRequest,\n type Input,\n type InputBase,\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 type {TakeBoundProvider, TakeGate} from './take-gate.ts';\n\ntype TakeState = {\n size: number;\n bound: Row | undefined;\n};\n\ninterface TakeStorage {\n get(key: string): TakeState | undefined;\n set(key: string, value: TakeState): void;\n del(key: string): void;\n destroy(): void;\n}\n\nexport type PartitionKey = PrimaryKey;\n\ntype DirtyPartitionState = {\n constraint: Constraint | undefined;\n /**\n * The bound to report to upstream TakeGate via getBound() until Phase 2\n * reconciliation completes.\n *\n * When removals reduce partition size below limit in Phase 1, normal getBound()\n * would return undefined (unbounded), which would cause upstream TakeGate to open\n * and allow unbounded parent pushes through. Preserving gateBound keeps TakeGate\n * capped at the pre-removal boundary until Phase 2 refills the deficit.\n */\n gateBound: Row | undefined;\n};\n\n/**\n * The Take operator is for implementing limit queries. It takes the first n\n * nodes of its input as determined by the input’s comparator. It then keeps\n * a *bound* of the last item it has accepted so that it can evaluate whether\n * new incoming pushes should be accepted or rejected.\n *\n * Take can count rows globally or by unique value of some field.\n *\n * Maintains the invariant that its output size is always <= limit, even\n * mid processing of a push.\n */\nexport class Take implements Operator, TakeBoundProvider {\n readonly #input: Input;\n readonly #storage: TakeStorage;\n readonly #limit: number;\n readonly #partitionKey: PartitionKey | undefined;\n readonly #partitionKeyComparator: Comparator | undefined;\n // Fetch overlay needed for some split push cases.\n #rowHiddenFromFetch: Row | undefined;\n\n #takeGate: TakeGate | undefined;\n /**\n * Partitions that incurred removals in Phase 1 and require deficit refills in\n * Phase 2 (reconcile). Keyed by `takeStateKey` (see `getTakeStateKey`), matching\n * `#storage` keys: `'[\"take\"]'` when unpartitioned, or `'[\"take\", ...partitionValues]'`\n * when partitioned.\n */\n readonly #dirtyPartitions = new Map<string, DirtyPartitionState>();\n\n #output: Output = throwOutput;\n\n setTakeGate(gate: TakeGate): void {\n this.#takeGate = gate;\n }\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 const {sort} = input.getSchema();\n assert(sort !== undefined, 'Take requires sorted input');\n assertOrderingIncludesPK(sort, input.getSchema().primaryKey);\n input.setOutput(this);\n this.#input = input;\n this.#storage = storage as TakeStorage;\n this.#limit = limit;\n this.#partitionKey = partitionKey;\n this.#partitionKeyComparator =\n partitionKey && makePartitionKeyComparator(partitionKey);\n }\n\n setOutput(output: Output): void {\n this.#output = output;\n }\n\n getSchema(): SourceSchema {\n return this.#input.getSchema();\n }\n\n getBound(constraint?: Constraint): Row | undefined {\n if (\n this.#partitionKey &&\n !constraintContainsPartitionKey(constraint, this.#partitionKey)\n ) {\n return undefined;\n }\n const takeStateKey = getTakeStateKey(this.#partitionKey, constraint);\n const dirty = this.#dirtyPartitions.get(takeStateKey);\n if (dirty) {\n // While dirty in Phase 1, return gateBound (the bound prior to removals) so\n // upstream operators (e.g. TakeGate) remain capped rather than unbounding during push.\n return dirty.gateBound;\n }\n const takeState = this.#storage.get(takeStateKey);\n if (!takeState || takeState.size < this.#limit) {\n return undefined;\n }\n return takeState.bound;\n }\n\n *fetch(req: FetchRequest): Stream<Node | 'yield'> {\n assert(\n !this.#partitionKey ||\n (req.constraint !== undefined &&\n constraintContainsPartitionKey(req.constraint, this.#partitionKey)),\n 'Partitioned take does not allow unpartitioned fetches',\n );\n\n const takeStateKey = getTakeStateKey(this.#partitionKey, req.constraint);\n const takeState = this.#storage.get(takeStateKey);\n if (!takeState) {\n if (constraintMatchesPartitionKey(req.constraint, this.#partitionKey)) {\n yield* this.#initialFetch(req, takeStateKey);\n }\n return;\n }\n if (takeState.bound === undefined) {\n return;\n }\n let count = 0;\n for (const inputNode of this.#input.fetch(req)) {\n if (inputNode === 'yield') {\n yield inputNode;\n continue;\n }\n if (this.getSchema().compareRows(takeState.bound, inputNode.row) < 0) {\n return;\n }\n if (\n this.#rowHiddenFromFetch &&\n this.getSchema().compareRows(\n this.#rowHiddenFromFetch,\n inputNode.row,\n ) === 0\n ) {\n continue;\n }\n yield inputNode;\n count++;\n if (count >= this.#limit) {\n return;\n }\n }\n }\n\n *#initialFetch(\n req: FetchRequest,\n takeStateKey: string,\n ): Stream<Node | 'yield'> {\n assert(req.start === undefined, 'Start should be undefined');\n assert(!req.reverse, 'Reverse should be false');\n\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 // The caller (fetch) has already checked that there is no state for\n // takeStateKey.\n\n let size = 0;\n let bound: Row | undefined;\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 bound = inputNode.row;\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.#setTakeState(takeStateKey, size, bound);\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 takeState is properly hydrated.\n assert(\n !downstreamEarlyReturn,\n 'Unexpected early return prevented full hydration',\n );\n }\n }\n }\n\n #getStateAndConstraint(row: Row) {\n const takeStateKey = getTakeStateKey(this.#partitionKey, row);\n const takeState = this.#storage.get(takeStateKey);\n let constraint: Constraint | undefined;\n if (takeState) {\n constraint =\n this.#partitionKey &&\n Object.fromEntries(\n this.#partitionKey.map(key => [key, row[key]] as const),\n );\n }\n\n return {takeState, takeStateKey, constraint} as\n | {\n takeState: undefined;\n takeStateKey: string;\n constraint: undefined;\n }\n | {\n takeState: TakeState;\n takeStateKey: string;\n constraint: Constraint | undefined;\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 {takeState, takeStateKey, constraint} = this.#getStateAndConstraint(\n change[ChangeIndex.NODE].row,\n );\n if (!takeState) {\n return;\n }\n\n const {compareRows} = this.getSchema();\n\n if (change[ChangeIndex.TYPE] === ChangeType.ADD) {\n if (takeState.size < this.#limit) {\n if (this.#dirtyPartitions.has(takeStateKey)) {\n // While dirty with an unresolved deficit in Phase 1, only admit rows that\n // sort strictly before the current window boundary. We refuse to expand the\n // bound forward during Phase 1 so that Phase 2 can backfill earlier candidate\n // rows from storage in sort order.\n if (\n takeState.bound === undefined ||\n compareRows(change[ChangeIndex.NODE].row, takeState.bound) >= 0\n ) {\n return;\n }\n }\n const nextSize = takeState.size + 1;\n if (nextSize === this.#limit) {\n this.#dirtyPartitions.delete(takeStateKey);\n }\n this.#setTakeState(\n takeStateKey,\n nextSize,\n takeState.bound === undefined ||\n compareRows(takeState.bound, change[ChangeIndex.NODE].row) < 0\n ? change[ChangeIndex.NODE].row\n : takeState.bound,\n );\n yield* this.#output.push(change, this);\n return;\n }\n // size === limit\n if (\n takeState.bound === undefined ||\n compareRows(change[ChangeIndex.NODE].row, takeState.bound) >= 0\n ) {\n return;\n }\n // added row < bound\n let beforeBoundNode: Node | undefined;\n let boundNode: Node | undefined;\n if (this.#limit === 1) {\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n boundNode = node;\n break;\n }\n } else {\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n reverse: true,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n } else if (boundNode === undefined) {\n boundNode = node;\n } else {\n beforeBoundNode = node;\n break;\n }\n }\n }\n assert(\n boundNode !== undefined,\n 'Take: boundNode must be found during fetch',\n );\n const removeChange = makeRemoveChange(boundNode);\n // Remove before add to maintain invariant that\n // output size <= limit.\n this.#setTakeState(\n takeStateKey,\n takeState.size,\n beforeBoundNode === undefined ||\n compareRows(change[ChangeIndex.NODE].row, beforeBoundNode.row) > 0\n ? change[ChangeIndex.NODE].row\n : beforeBoundNode.row,\n );\n yield* this.#pushWithRowHiddenFromFetch(\n change[ChangeIndex.NODE].row,\n removeChange,\n );\n yield* this.#output.push(change, this);\n } else if (change[ChangeIndex.TYPE] === ChangeType.REMOVE) {\n if (takeState.bound === undefined) {\n // change is after bound\n return;\n }\n const compToBound = compareRows(\n change[ChangeIndex.NODE].row,\n takeState.bound,\n );\n if (compToBound > 0 || (compToBound < 0 && this.#limit === 1)) {\n // change is not in window\n return;\n }\n let beforeBoundNode: Node | undefined;\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'after',\n },\n constraint,\n reverse: true,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n beforeBoundNode = node;\n break;\n }\n\n const finalBound =\n takeState.size - 1 === 0\n ? undefined\n : compToBound < 0\n ? takeState.bound\n : beforeBoundNode?.row;\n if (!this.#dirtyPartitions.has(takeStateKey)) {\n // Snapshot the pre-removal bound as the gate bound on the first removal that\n // dirties this partition. Subsequent getBound() calls throughout Phase 1 will return\n // this bound to keep upstream TakeGate bounded until Phase 2 refills deficits.\n this.#dirtyPartitions.set(takeStateKey, {\n constraint,\n gateBound: takeState.bound,\n });\n }\n this.#setTakeState(takeStateKey, takeState.size - 1, finalBound);\n yield* this.#output.push(change, this);\n } else if (change[ChangeIndex.TYPE] === ChangeType.CHILD) {\n // A 'child' change should be pushed to output if its row\n // is <= bound.\n if (\n takeState.bound &&\n compareRows(change[ChangeIndex.NODE].row, takeState.bound) <= 0\n ) {\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\n const {takeState, takeStateKey, constraint} = this.#getStateAndConstraint(\n change[ChangeIndex.OLD_NODE].row,\n );\n if (!takeState) {\n return;\n }\n\n assert(takeState.bound, 'Bound should be set');\n const {compareRows} = this.getSchema();\n const oldCmp = compareRows(\n change[ChangeIndex.OLD_NODE].row,\n takeState.bound,\n );\n const newCmp = compareRows(change[ChangeIndex.NODE].row, takeState.bound);\n\n const replaceBoundAndForwardChange = () => {\n this.#setTakeState(\n takeStateKey,\n takeState.size,\n change[ChangeIndex.NODE].row,\n );\n return this.#output.push(change, this);\n };\n\n // The bounds row was changed.\n if (oldCmp === 0) {\n // The new row is the new bound.\n if (newCmp === 0) {\n // no need to update the state since we are keeping the bounds\n yield* this.#output.push(change, this);\n return;\n }\n\n if (newCmp < 0) {\n if (this.#limit === 1) {\n yield* replaceBoundAndForwardChange();\n return;\n }\n\n // New row will be in the result but it might not be the bounds any\n // more. We need to find the row before the bounds to determine the new\n // bounds.\n\n let beforeBoundNode: Node | undefined;\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'after',\n },\n constraint,\n reverse: true,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n beforeBoundNode = node;\n break;\n }\n assert(\n beforeBoundNode !== undefined,\n 'Take: beforeBoundNode must be found during fetch',\n );\n\n this.#setTakeState(takeStateKey, takeState.size, beforeBoundNode.row);\n yield* this.#output.push(change, this);\n return;\n }\n\n assert(newCmp > 0, 'New comparison must be greater than 0');\n // Find the first item at the old bounds. This will be the new bounds.\n let newBoundNode: Node | undefined;\n this.#takeGate?.open();\n try {\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n newBoundNode = node;\n break;\n }\n } finally {\n this.#takeGate?.close();\n }\n assert(\n newBoundNode !== undefined,\n 'Take: newBoundNode must be found during fetch',\n );\n\n // The next row is the new row. We can replace the bounds and keep the\n // edit change.\n if (compareRows(newBoundNode.row, change[ChangeIndex.NODE].row) === 0) {\n yield* replaceBoundAndForwardChange();\n return;\n }\n\n // The new row is now outside the bounds, so we need to remove the old\n // row and add the new bounds row.\n this.#setTakeState(takeStateKey, takeState.size, newBoundNode.row);\n yield* this.#pushWithRowHiddenFromFetch(\n newBoundNode.row,\n makeRemoveChange(change[ChangeIndex.OLD_NODE]),\n );\n yield* this.#output.push(makeAddChange(newBoundNode), this);\n return;\n }\n\n if (oldCmp > 0) {\n assert(newCmp !== 0, 'Invalid state. Row has duplicate primary key');\n\n // Both old and new outside of bounds\n if (newCmp > 0) {\n return;\n }\n\n // old was outside, new is inside. Pushing out the old bounds\n assert(newCmp < 0, 'New comparison must be less than 0');\n\n let oldBoundNode: Node | undefined;\n let newBoundNode: Node | undefined;\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n reverse: true,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n } else if (oldBoundNode === undefined) {\n oldBoundNode = node;\n } else {\n newBoundNode = node;\n break;\n }\n }\n assert(\n oldBoundNode !== undefined,\n 'Take: oldBoundNode must be found during fetch',\n );\n assert(\n newBoundNode !== undefined,\n 'Take: newBoundNode must be found during fetch',\n );\n\n // Remove before add to maintain invariant that\n // output size <= limit.\n this.#setTakeState(takeStateKey, takeState.size, newBoundNode.row);\n yield* this.#pushWithRowHiddenFromFetch(\n change[ChangeIndex.NODE].row,\n makeRemoveChange(oldBoundNode),\n );\n yield* this.#output.push(makeAddChange(change[ChangeIndex.NODE]), this);\n\n return;\n }\n\n if (oldCmp < 0) {\n assert(newCmp !== 0, 'Invalid state. Row has duplicate primary key');\n\n // Both old and new inside of bounds\n if (newCmp < 0) {\n yield* this.#output.push(change, this);\n return;\n }\n\n // old was inside, new is larger than old bound\n\n assert(newCmp > 0, 'New comparison must be greater than 0');\n\n // at this point we need to find the row after the bound and use that or\n // the newRow as the new bound.\n let afterBoundNode: Node | undefined;\n this.#takeGate?.open();\n try {\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'after',\n },\n constraint,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n afterBoundNode = node;\n break;\n }\n } finally {\n this.#takeGate?.close();\n }\n assert(\n afterBoundNode !== undefined,\n 'Take: afterBoundNode must be found during fetch',\n );\n\n // The new row is the new bound. Use an edit change.\n if (compareRows(afterBoundNode.row, change[ChangeIndex.NODE].row) === 0) {\n yield* replaceBoundAndForwardChange();\n return;\n }\n\n yield* this.#output.push(\n makeRemoveChange(change[ChangeIndex.OLD_NODE]),\n this,\n );\n this.#setTakeState(takeStateKey, takeState.size, afterBoundNode.row);\n yield* this.#output.push(makeAddChange(afterBoundNode), this);\n return;\n }\n\n unreachable();\n }\n\n *#pushWithRowHiddenFromFetch(row: Row, change: Change) {\n this.#rowHiddenFromFetch = row;\n try {\n yield* this.#output.push(change, this);\n } finally {\n this.#rowHiddenFromFetch = undefined;\n }\n }\n\n #setTakeState(takeStateKey: string, size: number, bound: Row | undefined) {\n this.#storage.set(takeStateKey, {\n size,\n bound,\n });\n }\n\n destroy(): void {\n this.#input.destroy();\n this.#storage.destroy();\n }\n\n *reconcile(_pusher?: InputBase): Stream<'yield'> {\n if (this.#dirtyPartitions.size > 0) {\n const dirty = [...this.#dirtyPartitions.entries()];\n this.#dirtyPartitions.clear();\n\n for (const [takeStateKey, {constraint}] of dirty) {\n const takeState = this.#storage.get(takeStateKey);\n assert(\n takeState !== undefined,\n 'Take: dirty partition must exist in storage',\n );\n const deficit = this.#limit - takeState.size;\n assert(deficit > 0, 'Take: dirty partition must have a deficit');\n\n const toPush: Node[] = [];\n this.#takeGate?.open();\n try {\n const stream = this.#input.fetch({\n start: takeState.bound\n ? {\n row: takeState.bound,\n basis: 'after',\n }\n : undefined,\n constraint,\n });\n\n for (const node of stream) {\n if (node === 'yield') {\n yield 'yield';\n continue;\n }\n toPush.push(node);\n if (toPush.length === deficit) {\n break;\n }\n }\n } finally {\n this.#takeGate?.close();\n }\n\n let currentSize = takeState.size;\n for (const node of toPush) {\n currentSize++;\n this.#setTakeState(takeStateKey, currentSize, node.row);\n yield* this.#output.push(makeAddChange(node), this);\n }\n }\n }\n\n if (this.#output.reconcile) {\n yield* this.#output.reconcile(this);\n }\n }\n}\n\nfunction getTakeStateKey(\n partitionKey: PartitionKey | undefined,\n rowOrConstraint: Row | Constraint | undefined,\n): string {\n // The order must be consistent. We always use the order as defined by the\n // partition key.\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(['take', ...partitionValues]);\n}\n\nexport function constraintMatchesPartitionKey(\n constraint: Constraint | undefined,\n partitionKey: PartitionKey | undefined,\n): boolean {\n if (constraint === undefined || partitionKey === undefined) {\n return constraint === partitionKey;\n }\n if (partitionKey.length !== Object.keys(constraint).length) {\n return false;\n }\n for (const key of partitionKey) {\n if (!hasOwn(constraint, key)) {\n return false;\n }\n }\n return true;\n}\n\nexport function constraintContainsPartitionKey(\n constraint: Constraint | undefined,\n partitionKey: PartitionKey | undefined,\n): boolean {\n if (constraint === undefined || partitionKey === undefined) {\n return false;\n }\n for (const key of partitionKey) {\n if (!hasOwn(constraint, key)) {\n return false;\n }\n }\n return true;\n}\n\nexport function makePartitionKeyComparator(\n partitionKey: PartitionKey,\n): Comparator {\n return (a, b) => {\n for (const key of partitionKey) {\n const cmp = compareValues(a[key], b[key]);\n if (cmp !== 0) {\n return cmp;\n }\n }\n return 0;\n };\n}\n"],"mappings":"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"}
@@ -222,6 +222,6 @@ function* mergeFetches(fetches, comparator) {
222
222
  }
223
223
  }
224
224
  //#endregion
225
- export { UnionFanIn };
225
+ export { UnionFanIn, mergeFetches };
226
226
 
227
227
  //# sourceMappingURL=union-fan-in.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"union-fan-in.js","names":["#inputs","#schema","#branchRels","#fanOutPushStarted","#pushInternalChange","#accumulatedPushes","#output","#fanOutReconcileStarted"],"sources":["../../../../../zql/src/ivm/union-fan-in.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport {emptyArray} from '../../../shared/src/sentinels.ts';\nimport type {Writable} from '../../../shared/src/writable.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport {ChangeType} from './change-type.ts';\nimport type {Change} from './change.ts';\nimport type {Constraint} from './constraint.ts';\nimport type {Node} from './data.ts';\nimport {\n throwOutput,\n type FetchRequest,\n type Input,\n type InputBase,\n type Operator,\n type Output,\n} from './operator.ts';\nimport {\n makeAddEmptyRelationships,\n mergeEmpty,\n mergeRelationships,\n pushAccumulatedChanges,\n pushWitnessDeltas,\n} from './push-accumulated.ts';\nimport type {SourceSchema} from './schema.ts';\nimport type {Stream} from './stream.ts';\nimport type {UnionFanOut} from './union-fan-out.ts';\n\nexport class UnionFanIn implements Operator {\n readonly #inputs: readonly Input[];\n readonly #schema: SourceSchema;\n #fanOutPushStarted: boolean = false;\n #fanOutReconcileStarted: boolean = false;\n #output: Output = throwOutput;\n #accumulatedPushes: Change[] = [];\n readonly #branchRels: ReadonlySet<string>;\n\n constructor(fanOut: UnionFanOut, inputs: Input[]) {\n this.#inputs = inputs;\n const fanOutSchema = fanOut.getSchema();\n fanOut.setFanIn(this);\n assert(fanOutSchema.sort !== undefined, 'UnionFanIn requires sorted input');\n\n const schema: Writable<SourceSchema> = {\n tableName: fanOutSchema.tableName,\n columns: fanOutSchema.columns,\n primaryKey: fanOutSchema.primaryKey,\n relationships: {\n ...fanOutSchema.relationships,\n },\n isHidden: fanOutSchema.isHidden,\n system: fanOutSchema.system,\n compareRows: fanOutSchema.compareRows,\n sort: fanOutSchema.sort,\n };\n\n // now go through inputs and merge relationships\n const relationshipsFromBranches: Set<string> = new Set();\n for (const input of inputs) {\n const inputSchema = input.getSchema();\n assert(\n schema.tableName === inputSchema.tableName,\n `Table name mismatch in union fan-in: ${schema.tableName} !== ${inputSchema.tableName}`,\n );\n assert(\n schema.primaryKey === inputSchema.primaryKey,\n `Primary key mismatch in union fan-in`,\n );\n assert(\n schema.system === inputSchema.system,\n `System mismatch in union fan-in: ${schema.system} !== ${inputSchema.system}`,\n );\n assert(\n schema.compareRows === inputSchema.compareRows,\n `compareRows mismatch in union fan-in`,\n );\n assert(schema.sort === inputSchema.sort, `Sort mismatch in union fan-in`);\n\n for (const [relName, relSchema] of Object.entries(\n inputSchema.relationships,\n )) {\n if (relName in fanOutSchema.relationships) {\n continue;\n }\n\n // All branches will have unique relationship names except for relationships\n // that come in from `fanOut`.\n assert(\n !relationshipsFromBranches.has(relName),\n `Relationship ${relName} exists in multiple upstream inputs to union fan-in`,\n );\n schema.relationships[relName] = relSchema;\n relationshipsFromBranches.add(relName);\n }\n\n input.setOutput(this);\n }\n\n this.#schema = schema;\n this.#inputs = inputs;\n this.#branchRels = relationshipsFromBranches;\n }\n\n destroy(): void {\n for (const input of this.#inputs) {\n input.destroy();\n }\n }\n\n fetch(req: FetchRequest): Stream<Node | 'yield'> {\n const iterables = this.#inputs.map(input => input.fetch(req));\n const compareRows = this.#schema.compareRows;\n const compare = req.reverse\n ? (l: Node, r: Node) => compareRows(r.row, l.row)\n : (l: Node, r: Node) => compareRows(l.row, r.row);\n const relNames = Object.keys(this.#schema.relationships);\n return mapNodes(mergeFetches(iterables, compare), node => {\n const relationships = {...node.relationships};\n mergeEmpty(relationships, relNames);\n return {row: node.row, relationships};\n });\n }\n\n getSchema(): SourceSchema {\n return this.#schema;\n }\n\n push(change: Change, pusher: InputBase): Stream<'yield'> {\n if (!this.#fanOutPushStarted) {\n return this.#pushInternalChange(change, pusher);\n }\n this.#accumulatedPushes.push(change);\n return emptyArray;\n }\n\n /**\n * An internal change means that a change was received inside the fan-out/fan-in sub-graph.\n *\n * These changes always come from children of a flip-join as no other push generating operators\n * currently exist between union-fan-in and union-fan-out. All other pushes\n * enter into union-fan-out before reaching union-fan-in.\n *\n * - normal joins for `exists` come before `union-fan-out`\n * - joins for `related` come after `union-fan-out`\n * - take comes after `union-fan-out`\n *\n * The algorithm for deciding whether or not to forward a push that came from inside the ufo/ufi sub-graph:\n * 1. If the change is a `child` change we can forward it. This is because all child branches in the ufo/ufi sub-graph are unique.\n * 2. If the change is `add` we can forward it iff no `fetches` for the row return any results.\n * If another branch has it, the add was already emitted in the past.\n * 3. If the change is `remove` we can forward it iff no `fetches` for the row return any results.\n * If no other branches have the change, the remove can be sent as the value is no longer present.\n * If other branches have it, the last branch the processes the remove will send the remove.\n * 4. Edits will always come through as child changes as flip join will flip them into children.\n * An edit that would result in a remove or add will have been split into an add/remove pair rather than being an edit.\n */\n *#pushInternalChange(change: Change, pusher: InputBase): Stream<'yield'> {\n if (change[ChangeIndex.TYPE] === ChangeType.CHILD) {\n yield* this.#output.push(change, this);\n return;\n }\n\n assert(\n change[ChangeIndex.TYPE] === ChangeType.ADD ||\n change[ChangeIndex.TYPE] === ChangeType.REMOVE,\n () =>\n `UnionFanIn: expected add or remove change type, got ${change[ChangeIndex.TYPE]}`,\n );\n\n let hadMatch = false;\n for (const input of this.#inputs) {\n if (input === pusher) {\n hadMatch = true;\n continue;\n }\n\n const constraint: Writable<Constraint> = {};\n for (const key of this.#schema.primaryKey) {\n constraint[key] = change[ChangeIndex.NODE].row[key];\n }\n const fetchResult = input.fetch({\n constraint,\n });\n\n // `fetch` interleaves 'yield' sentinels for cooperative multitasking.\n // They must be forwarded, not just skipped: this probe runs inside a\n // push, and the sentinel is the source offering the scheduler a breath.\n // They must also not be mistaken for rows -- reading one as a row is\n // what broke this before, since an empty branch that happened to yield\n // looked like a branch holding the row, silently dropping the\n // add/remove and desyncing a downstream `Take`'s push and fetch paths.\n let otherBranchHasRow = false;\n for (const node of fetchResult) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n otherBranchHasRow = true;\n break;\n }\n\n if (otherBranchHasRow) {\n // Another branch has the row, so it stays. What changed is that it\n // gained or lost the pusher's witnesses: say so as child changes.\n yield* pushWitnessDeltas(\n change[ChangeIndex.NODE],\n change[ChangeIndex.TYPE],\n name => this.#branchRels.has(name),\n this.#output,\n this,\n );\n return;\n }\n }\n\n assert(hadMatch, 'Pusher was not one of the inputs to union-fan-in!');\n\n // No other branches have the row, so we can push the change.\n yield* this.#output.push(change, this);\n }\n\n fanOutStartedPushing() {\n assert(\n this.#fanOutPushStarted === false,\n 'UnionFanIn: fanOutStartedPushing called while already pushing',\n );\n this.#fanOutPushStarted = true;\n }\n\n fanOutDonePushing(fanOutChangeType: ChangeType): Stream<'yield'> {\n assert(\n this.#fanOutPushStarted,\n 'UnionFanIn: fanOutDonePushing called without fanOutStartedPushing',\n );\n this.#fanOutPushStarted = false;\n if (this.#inputs.length === 0) {\n return emptyArray;\n }\n\n if (this.#accumulatedPushes.length === 0) {\n // It is possible for no forks to pass along the push.\n // E.g., if no filters match in any fork.\n return emptyArray;\n }\n\n return pushAccumulatedChanges(\n this.#accumulatedPushes,\n this.#output,\n this,\n fanOutChangeType,\n mergeRelationships,\n makeAddEmptyRelationships(this.#schema),\n name => this.#branchRels.has(name),\n );\n }\n\n fanOutStartedReconciling() {\n assert(\n this.#fanOutReconcileStarted === false,\n 'UnionFanIn: fanOutStartedReconciling called while already reconciling',\n );\n this.#fanOutReconcileStarted = true;\n }\n\n *fanOutDoneReconciling(): Stream<'yield'> {\n assert(\n this.#fanOutReconcileStarted,\n 'UnionFanIn: fanOutDoneReconciling called without fanOutStartedReconciling',\n );\n this.#fanOutReconcileStarted = false;\n if (this.#inputs.length === 0) {\n return;\n }\n if (this.#output.reconcile) {\n yield* this.#output.reconcile(this);\n }\n }\n\n setOutput(output: Output): void {\n this.#output = output;\n }\n\n *reconcile(_pusher: InputBase): Stream<'yield'> {\n if (this.#fanOutReconcileStarted) {\n return;\n }\n if (this.#output.reconcile) {\n yield* this.#output.reconcile(this);\n }\n }\n}\n\nfunction* mapNodes(\n nodes: Iterable<Node | 'yield'>,\n f: (n: Node) => Node,\n): Stream<Node | 'yield'> {\n for (const n of nodes) {\n yield n === 'yield' ? n : f(n);\n }\n}\n\nexport function* mergeFetches(\n fetches: Iterable<Node | 'yield'>[],\n comparator: (l: Node, r: Node) => number,\n): IterableIterator<Node | 'yield'> {\n const iterators = fetches.map(i => i[Symbol.iterator]());\n let threw = false;\n try {\n const current: (Node | null)[] = [];\n let lastNodeYielded: Node | undefined;\n for (let i = 0; i < iterators.length; i++) {\n const iter = iterators[i];\n let result = iter.next();\n // yield yields when initializing\n while (!result.done && result.value === 'yield') {\n yield result.value;\n result = iter.next();\n }\n current[i] = result.done ? null : (result.value as Node);\n }\n while (current.some(c => c !== null)) {\n const min = current.reduce(\n (acc: [Node, number] | undefined, c, i): [Node, number] | undefined => {\n if (c === null) {\n return acc;\n }\n if (acc === undefined || comparator(c, acc[0]) < 0) {\n return [c, i];\n }\n return acc;\n },\n undefined,\n );\n\n assert(min !== undefined, 'min is undefined');\n const [minNode, minIndex] = min;\n const iter = iterators[minIndex];\n let result = iter.next();\n while (!result.done && result.value === 'yield') {\n yield result.value;\n result = iter.next();\n }\n current[minIndex] = result.done ? null : (result.value as Node);\n if (\n lastNodeYielded !== undefined &&\n comparator(lastNodeYielded, minNode) === 0\n ) {\n continue;\n }\n // The same row from another branch is at the head of that branch's\n // stream. Union its relationships in (first branch wins a conflict)\n // instead of dropping it, so a fetched row carries what a pushed one does.\n let merged = minNode;\n for (let j = 0; j < iterators.length; j++) {\n let head = current[j];\n while (head !== null && comparator(head, minNode) === 0) {\n merged = {\n row: merged.row,\n relationships: {...head.relationships, ...merged.relationships},\n };\n let r = iterators[j].next();\n while (!r.done && r.value === 'yield') {\n yield r.value;\n r = iterators[j].next();\n }\n head = current[j] = r.done ? null : (r.value as Node);\n }\n }\n lastNodeYielded = minNode;\n yield merged;\n }\n } catch (e) {\n threw = true;\n for (const iter of iterators) {\n try {\n iter.throw?.(e);\n } catch (_cleanupError) {\n // error in the iter.throw cleanup,\n // catch so other iterators are cleaned up\n }\n }\n throw e;\n } finally {\n if (!threw) {\n for (const iter of iterators) {\n try {\n iter.return?.();\n } catch (_cleanupError) {\n // error in the iter.return cleanup,\n // catch so other iterators are cleaned up\n }\n }\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"union-fan-in.js","names":[],"sources":["../../../../../zql/src/ivm/union-fan-in.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport {emptyArray} from '../../../shared/src/sentinels.ts';\nimport type {Writable} from '../../../shared/src/writable.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport {ChangeType} from './change-type.ts';\nimport type {Change} from './change.ts';\nimport type {Constraint} from './constraint.ts';\nimport type {Node} from './data.ts';\nimport {\n throwOutput,\n type FetchRequest,\n type Input,\n type InputBase,\n type Operator,\n type Output,\n} from './operator.ts';\nimport {\n makeAddEmptyRelationships,\n mergeEmpty,\n mergeRelationships,\n pushAccumulatedChanges,\n pushWitnessDeltas,\n} from './push-accumulated.ts';\nimport type {SourceSchema} from './schema.ts';\nimport type {Stream} from './stream.ts';\nimport type {UnionFanOut} from './union-fan-out.ts';\n\nexport class UnionFanIn implements Operator {\n readonly #inputs: readonly Input[];\n readonly #schema: SourceSchema;\n #fanOutPushStarted: boolean = false;\n #fanOutReconcileStarted: boolean = false;\n #output: Output = throwOutput;\n #accumulatedPushes: Change[] = [];\n readonly #branchRels: ReadonlySet<string>;\n\n constructor(fanOut: UnionFanOut, inputs: Input[]) {\n this.#inputs = inputs;\n const fanOutSchema = fanOut.getSchema();\n fanOut.setFanIn(this);\n assert(fanOutSchema.sort !== undefined, 'UnionFanIn requires sorted input');\n\n const schema: Writable<SourceSchema> = {\n tableName: fanOutSchema.tableName,\n columns: fanOutSchema.columns,\n primaryKey: fanOutSchema.primaryKey,\n relationships: {\n ...fanOutSchema.relationships,\n },\n isHidden: fanOutSchema.isHidden,\n system: fanOutSchema.system,\n compareRows: fanOutSchema.compareRows,\n sort: fanOutSchema.sort,\n };\n\n // now go through inputs and merge relationships\n const relationshipsFromBranches: Set<string> = new Set();\n for (const input of inputs) {\n const inputSchema = input.getSchema();\n assert(\n schema.tableName === inputSchema.tableName,\n `Table name mismatch in union fan-in: ${schema.tableName} !== ${inputSchema.tableName}`,\n );\n assert(\n schema.primaryKey === inputSchema.primaryKey,\n `Primary key mismatch in union fan-in`,\n );\n assert(\n schema.system === inputSchema.system,\n `System mismatch in union fan-in: ${schema.system} !== ${inputSchema.system}`,\n );\n assert(\n schema.compareRows === inputSchema.compareRows,\n `compareRows mismatch in union fan-in`,\n );\n assert(schema.sort === inputSchema.sort, `Sort mismatch in union fan-in`);\n\n for (const [relName, relSchema] of Object.entries(\n inputSchema.relationships,\n )) {\n if (relName in fanOutSchema.relationships) {\n continue;\n }\n\n // All branches will have unique relationship names except for relationships\n // that come in from `fanOut`.\n assert(\n !relationshipsFromBranches.has(relName),\n `Relationship ${relName} exists in multiple upstream inputs to union fan-in`,\n );\n schema.relationships[relName] = relSchema;\n relationshipsFromBranches.add(relName);\n }\n\n input.setOutput(this);\n }\n\n this.#schema = schema;\n this.#inputs = inputs;\n this.#branchRels = relationshipsFromBranches;\n }\n\n destroy(): void {\n for (const input of this.#inputs) {\n input.destroy();\n }\n }\n\n fetch(req: FetchRequest): Stream<Node | 'yield'> {\n const iterables = this.#inputs.map(input => input.fetch(req));\n const compareRows = this.#schema.compareRows;\n const compare = req.reverse\n ? (l: Node, r: Node) => compareRows(r.row, l.row)\n : (l: Node, r: Node) => compareRows(l.row, r.row);\n const relNames = Object.keys(this.#schema.relationships);\n return mapNodes(mergeFetches(iterables, compare), node => {\n const relationships = {...node.relationships};\n mergeEmpty(relationships, relNames);\n return {row: node.row, relationships};\n });\n }\n\n getSchema(): SourceSchema {\n return this.#schema;\n }\n\n push(change: Change, pusher: InputBase): Stream<'yield'> {\n if (!this.#fanOutPushStarted) {\n return this.#pushInternalChange(change, pusher);\n }\n this.#accumulatedPushes.push(change);\n return emptyArray;\n }\n\n /**\n * An internal change means that a change was received inside the fan-out/fan-in sub-graph.\n *\n * These changes always come from children of a flip-join as no other push generating operators\n * currently exist between union-fan-in and union-fan-out. All other pushes\n * enter into union-fan-out before reaching union-fan-in.\n *\n * - normal joins for `exists` come before `union-fan-out`\n * - joins for `related` come after `union-fan-out`\n * - take comes after `union-fan-out`\n *\n * The algorithm for deciding whether or not to forward a push that came from inside the ufo/ufi sub-graph:\n * 1. If the change is a `child` change we can forward it. This is because all child branches in the ufo/ufi sub-graph are unique.\n * 2. If the change is `add` we can forward it iff no `fetches` for the row return any results.\n * If another branch has it, the add was already emitted in the past.\n * 3. If the change is `remove` we can forward it iff no `fetches` for the row return any results.\n * If no other branches have the change, the remove can be sent as the value is no longer present.\n * If other branches have it, the last branch the processes the remove will send the remove.\n * 4. Edits will always come through as child changes as flip join will flip them into children.\n * An edit that would result in a remove or add will have been split into an add/remove pair rather than being an edit.\n */\n *#pushInternalChange(change: Change, pusher: InputBase): Stream<'yield'> {\n if (change[ChangeIndex.TYPE] === ChangeType.CHILD) {\n yield* this.#output.push(change, this);\n return;\n }\n\n assert(\n change[ChangeIndex.TYPE] === ChangeType.ADD ||\n change[ChangeIndex.TYPE] === ChangeType.REMOVE,\n () =>\n `UnionFanIn: expected add or remove change type, got ${change[ChangeIndex.TYPE]}`,\n );\n\n let hadMatch = false;\n for (const input of this.#inputs) {\n if (input === pusher) {\n hadMatch = true;\n continue;\n }\n\n const constraint: Writable<Constraint> = {};\n for (const key of this.#schema.primaryKey) {\n constraint[key] = change[ChangeIndex.NODE].row[key];\n }\n const fetchResult = input.fetch({\n constraint,\n });\n\n // `fetch` interleaves 'yield' sentinels for cooperative multitasking.\n // They must be forwarded, not just skipped: this probe runs inside a\n // push, and the sentinel is the source offering the scheduler a breath.\n // They must also not be mistaken for rows -- reading one as a row is\n // what broke this before, since an empty branch that happened to yield\n // looked like a branch holding the row, silently dropping the\n // add/remove and desyncing a downstream `Take`'s push and fetch paths.\n let otherBranchHasRow = false;\n for (const node of fetchResult) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n otherBranchHasRow = true;\n break;\n }\n\n if (otherBranchHasRow) {\n // Another branch has the row, so it stays. What changed is that it\n // gained or lost the pusher's witnesses: say so as child changes.\n yield* pushWitnessDeltas(\n change[ChangeIndex.NODE],\n change[ChangeIndex.TYPE],\n name => this.#branchRels.has(name),\n this.#output,\n this,\n );\n return;\n }\n }\n\n assert(hadMatch, 'Pusher was not one of the inputs to union-fan-in!');\n\n // No other branches have the row, so we can push the change.\n yield* this.#output.push(change, this);\n }\n\n fanOutStartedPushing() {\n assert(\n this.#fanOutPushStarted === false,\n 'UnionFanIn: fanOutStartedPushing called while already pushing',\n );\n this.#fanOutPushStarted = true;\n }\n\n fanOutDonePushing(fanOutChangeType: ChangeType): Stream<'yield'> {\n assert(\n this.#fanOutPushStarted,\n 'UnionFanIn: fanOutDonePushing called without fanOutStartedPushing',\n );\n this.#fanOutPushStarted = false;\n if (this.#inputs.length === 0) {\n return emptyArray;\n }\n\n if (this.#accumulatedPushes.length === 0) {\n // It is possible for no forks to pass along the push.\n // E.g., if no filters match in any fork.\n return emptyArray;\n }\n\n return pushAccumulatedChanges(\n this.#accumulatedPushes,\n this.#output,\n this,\n fanOutChangeType,\n mergeRelationships,\n makeAddEmptyRelationships(this.#schema),\n name => this.#branchRels.has(name),\n );\n }\n\n fanOutStartedReconciling() {\n assert(\n this.#fanOutReconcileStarted === false,\n 'UnionFanIn: fanOutStartedReconciling called while already reconciling',\n );\n this.#fanOutReconcileStarted = true;\n }\n\n *fanOutDoneReconciling(): Stream<'yield'> {\n assert(\n this.#fanOutReconcileStarted,\n 'UnionFanIn: fanOutDoneReconciling called without fanOutStartedReconciling',\n );\n this.#fanOutReconcileStarted = false;\n if (this.#inputs.length === 0) {\n return;\n }\n if (this.#output.reconcile) {\n yield* this.#output.reconcile(this);\n }\n }\n\n setOutput(output: Output): void {\n this.#output = output;\n }\n\n *reconcile(_pusher: InputBase): Stream<'yield'> {\n if (this.#fanOutReconcileStarted) {\n return;\n }\n if (this.#output.reconcile) {\n yield* this.#output.reconcile(this);\n }\n }\n}\n\nfunction* mapNodes(\n nodes: Iterable<Node | 'yield'>,\n f: (n: Node) => Node,\n): Stream<Node | 'yield'> {\n for (const n of nodes) {\n yield n === 'yield' ? n : f(n);\n }\n}\n\nexport function* mergeFetches(\n fetches: Iterable<Node | 'yield'>[],\n comparator: (l: Node, r: Node) => number,\n): IterableIterator<Node | 'yield'> {\n const iterators = fetches.map(i => i[Symbol.iterator]());\n let threw = false;\n try {\n const current: (Node | null)[] = [];\n let lastNodeYielded: Node | undefined;\n for (let i = 0; i < iterators.length; i++) {\n const iter = iterators[i];\n let result = iter.next();\n // yield yields when initializing\n while (!result.done && result.value === 'yield') {\n yield result.value;\n result = iter.next();\n }\n current[i] = result.done ? null : (result.value as Node);\n }\n while (current.some(c => c !== null)) {\n const min = current.reduce(\n (acc: [Node, number] | undefined, c, i): [Node, number] | undefined => {\n if (c === null) {\n return acc;\n }\n if (acc === undefined || comparator(c, acc[0]) < 0) {\n return [c, i];\n }\n return acc;\n },\n undefined,\n );\n\n assert(min !== undefined, 'min is undefined');\n const [minNode, minIndex] = min;\n const iter = iterators[minIndex];\n let result = iter.next();\n while (!result.done && result.value === 'yield') {\n yield result.value;\n result = iter.next();\n }\n current[minIndex] = result.done ? null : (result.value as Node);\n if (\n lastNodeYielded !== undefined &&\n comparator(lastNodeYielded, minNode) === 0\n ) {\n continue;\n }\n // The same row from another branch is at the head of that branch's\n // stream. Union its relationships in (first branch wins a conflict)\n // instead of dropping it, so a fetched row carries what a pushed one does.\n let merged = minNode;\n for (let j = 0; j < iterators.length; j++) {\n let head = current[j];\n while (head !== null && comparator(head, minNode) === 0) {\n merged = {\n row: merged.row,\n relationships: {...head.relationships, ...merged.relationships},\n };\n let r = iterators[j].next();\n while (!r.done && r.value === 'yield') {\n yield r.value;\n r = iterators[j].next();\n }\n head = current[j] = r.done ? null : (r.value as Node);\n }\n }\n lastNodeYielded = minNode;\n yield merged;\n }\n } catch (e) {\n threw = true;\n for (const iter of iterators) {\n try {\n iter.throw?.(e);\n } catch (_cleanupError) {\n // error in the iter.throw cleanup,\n // catch so other iterators are cleaned up\n }\n }\n throw e;\n } finally {\n if (!threw) {\n for (const iter of iterators) {\n try {\n iter.return?.();\n } catch (_cleanupError) {\n // error in the iter.return cleanup,\n // catch so other iterators are cleaned up\n }\n }\n }\n }\n}\n"],"mappings":";;;;;AA2BA,IAAa,aAAb,MAA4C;CAC1C;CACA;CACA,qBAA8B;CAC9B,0BAAmC;CACnC,UAAkB;CAClB,qBAA+B,CAAC;CAChC;CAEA,YAAY,QAAqB,QAAiB;EAChD,KAAK,UAAU;EACf,MAAM,eAAe,OAAO,UAAU;EACtC,OAAO,SAAS,IAAI;EACpB,OAAO,aAAa,SAAS,KAAA,GAAW,kCAAkC;EAE1E,MAAM,SAAiC;GACrC,WAAW,aAAa;GACxB,SAAS,aAAa;GACtB,YAAY,aAAa;GACzB,eAAe,EACb,GAAG,aAAa,cAClB;GACA,UAAU,aAAa;GACvB,QAAQ,aAAa;GACrB,aAAa,aAAa;GAC1B,MAAM,aAAa;EACrB;EAGA,MAAM,4CAAyC,IAAI,IAAI;EACvD,KAAK,MAAM,SAAS,QAAQ;GAC1B,MAAM,cAAc,MAAM,UAAU;GACpC,OACE,OAAO,cAAc,YAAY,WACjC,wCAAwC,OAAO,UAAU,OAAO,YAAY,WAC9E;GACA,OACE,OAAO,eAAe,YAAY,YAClC,sCACF;GACA,OACE,OAAO,WAAW,YAAY,QAC9B,oCAAoC,OAAO,OAAO,OAAO,YAAY,QACvE;GACA,OACE,OAAO,gBAAgB,YAAY,aACnC,sCACF;GACA,OAAO,OAAO,SAAS,YAAY,MAAM,+BAA+B;GAExE,KAAK,MAAM,CAAC,SAAS,cAAc,OAAO,QACxC,YAAY,aACd,GAAG;IACD,IAAI,WAAW,aAAa,eAC1B;IAKF,OACE,CAAC,0BAA0B,IAAI,OAAO,GACtC,gBAAgB,QAAQ,oDAC1B;IACA,OAAO,cAAc,WAAW;IAChC,0BAA0B,IAAI,OAAO;GACvC;GAEA,MAAM,UAAU,IAAI;EACtB;EAEA,KAAK,UAAU;EACf,KAAK,UAAU;EACf,KAAK,cAAc;CACrB;CAEA,UAAgB;EACd,KAAK,MAAM,SAAS,KAAK,SACvB,MAAM,QAAQ;CAElB;CAEA,MAAM,KAA2C;EAC/C,MAAM,YAAY,KAAK,QAAQ,KAAI,UAAS,MAAM,MAAM,GAAG,CAAC;EAC5D,MAAM,cAAc,KAAK,QAAQ;EACjC,MAAM,UAAU,IAAI,WACf,GAAS,MAAY,YAAY,EAAE,KAAK,EAAE,GAAG,KAC7C,GAAS,MAAY,YAAY,EAAE,KAAK,EAAE,GAAG;EAClD,MAAM,WAAW,OAAO,KAAK,KAAK,QAAQ,aAAa;EACvD,OAAO,SAAS,aAAa,WAAW,OAAO,IAAG,SAAQ;GACxD,MAAM,gBAAgB,EAAC,GAAG,KAAK,cAAa;GAC5C,WAAW,eAAe,QAAQ;GAClC,OAAO;IAAC,KAAK,KAAK;IAAK;GAAa;EACtC,CAAC;CACH;CAEA,YAA0B;EACxB,OAAO,KAAK;CACd;CAEA,KAAK,QAAgB,QAAoC;EACvD,IAAI,CAAC,KAAK,oBACR,OAAO,KAAK,oBAAoB,QAAQ,MAAM;EAEhD,KAAK,mBAAmB,KAAK,MAAM;EACnC,OAAO;CACT;;;;;;;;;;;;;;;;;;;;;;CAuBA,CAAC,oBAAoB,QAAgB,QAAoC;EACvE,IAAI,OAAO,OAAsB,GAAkB;GACjD,OAAO,KAAK,QAAQ,KAAK,QAAQ,IAAI;GACrC;EACF;EAEA,OACE,OAAO,OAAsB,KAC3B,OAAO,OAAsB,SAE7B,uDAAuD,OAAO,IAClE;EAEA,IAAI,WAAW;EACf,KAAK,MAAM,SAAS,KAAK,SAAS;GAChC,IAAI,UAAU,QAAQ;IACpB,WAAW;IACX;GACF;GAEA,MAAM,aAAmC,CAAC;GAC1C,KAAK,MAAM,OAAO,KAAK,QAAQ,YAC7B,WAAW,OAAO,OAAO,EAAiB,CAAC,IAAI;GAEjD,MAAM,cAAc,MAAM,MAAM,EAC9B,WACF,CAAC;GASD,IAAI,oBAAoB;GACxB,KAAK,MAAM,QAAQ,aAAa;IAC9B,IAAI,SAAS,SAAS;KACpB,MAAM;KACN;IACF;IACA,oBAAoB;IACpB;GACF;GAEA,IAAI,mBAAmB;IAGrB,OAAO,kBACL,OAAO,IACP,OAAO,KACP,SAAQ,KAAK,YAAY,IAAI,IAAI,GACjC,KAAK,SACL,IACF;IACA;GACF;EACF;EAEA,OAAO,UAAU,mDAAmD;EAGpE,OAAO,KAAK,QAAQ,KAAK,QAAQ,IAAI;CACvC;CAEA,uBAAuB;EACrB,OACE,KAAK,uBAAuB,OAC5B,+DACF;EACA,KAAK,qBAAqB;CAC5B;CAEA,kBAAkB,kBAA+C;EAC/D,OACE,KAAK,oBACL,mEACF;EACA,KAAK,qBAAqB;EAC1B,IAAI,KAAK,QAAQ,WAAW,GAC1B,OAAO;EAGT,IAAI,KAAK,mBAAmB,WAAW,GAGrC,OAAO;EAGT,OAAO,uBACL,KAAK,oBACL,KAAK,SACL,MACA,kBACA,oBACA,0BAA0B,KAAK,OAAO,IACtC,SAAQ,KAAK,YAAY,IAAI,IAAI,CACnC;CACF;CAEA,2BAA2B;EACzB,OACE,KAAK,4BAA4B,OACjC,uEACF;EACA,KAAK,0BAA0B;CACjC;CAEA,CAAC,wBAAyC;EACxC,OACE,KAAK,yBACL,2EACF;EACA,KAAK,0BAA0B;EAC/B,IAAI,KAAK,QAAQ,WAAW,GAC1B;EAEF,IAAI,KAAK,QAAQ,WACf,OAAO,KAAK,QAAQ,UAAU,IAAI;CAEtC;CAEA,UAAU,QAAsB;EAC9B,KAAK,UAAU;CACjB;CAEA,CAAC,UAAU,SAAqC;EAC9C,IAAI,KAAK,yBACP;EAEF,IAAI,KAAK,QAAQ,WACf,OAAO,KAAK,QAAQ,UAAU,IAAI;CAEtC;AACF;AAEA,UAAU,SACR,OACA,GACwB;CACxB,KAAK,MAAM,KAAK,OACd,MAAM,MAAM,UAAU,IAAI,EAAE,CAAC;AAEjC;AAEA,UAAiB,aACf,SACA,YACkC;CAClC,MAAM,YAAY,QAAQ,KAAI,MAAK,EAAE,OAAO,SAAS,CAAC,CAAC;CACvD,IAAI,QAAQ;CACZ,IAAI;EACF,MAAM,UAA2B,CAAC;EAClC,IAAI;EACJ,KAAK,IAAI,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK;GACzC,MAAM,OAAO,UAAU;GACvB,IAAI,SAAS,KAAK,KAAK;GAEvB,OAAO,CAAC,OAAO,QAAQ,OAAO,UAAU,SAAS;IAC/C,MAAM,OAAO;IACb,SAAS,KAAK,KAAK;GACrB;GACA,QAAQ,KAAK,OAAO,OAAO,OAAQ,OAAO;EAC5C;EACA,OAAO,QAAQ,MAAK,MAAK,MAAM,IAAI,GAAG;GACpC,MAAM,MAAM,QAAQ,QACjB,KAAiC,GAAG,MAAkC;IACrE,IAAI,MAAM,MACR,OAAO;IAET,IAAI,QAAQ,KAAA,KAAa,WAAW,GAAG,IAAI,EAAE,IAAI,GAC/C,OAAO,CAAC,GAAG,CAAC;IAEd,OAAO;GACT,GACA,KAAA,CACF;GAEA,OAAO,QAAQ,KAAA,GAAW,kBAAkB;GAC5C,MAAM,CAAC,SAAS,YAAY;GAC5B,MAAM,OAAO,UAAU;GACvB,IAAI,SAAS,KAAK,KAAK;GACvB,OAAO,CAAC,OAAO,QAAQ,OAAO,UAAU,SAAS;IAC/C,MAAM,OAAO;IACb,SAAS,KAAK,KAAK;GACrB;GACA,QAAQ,YAAY,OAAO,OAAO,OAAQ,OAAO;GACjD,IACE,oBAAoB,KAAA,KACpB,WAAW,iBAAiB,OAAO,MAAM,GAEzC;GAKF,IAAI,SAAS;GACb,KAAK,IAAI,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK;IACzC,IAAI,OAAO,QAAQ;IACnB,OAAO,SAAS,QAAQ,WAAW,MAAM,OAAO,MAAM,GAAG;KACvD,SAAS;MACP,KAAK,OAAO;MACZ,eAAe;OAAC,GAAG,KAAK;OAAe,GAAG,OAAO;MAAa;KAChE;KACA,IAAI,IAAI,UAAU,EAAE,CAAC,KAAK;KAC1B,OAAO,CAAC,EAAE,QAAQ,EAAE,UAAU,SAAS;MACrC,MAAM,EAAE;MACR,IAAI,UAAU,EAAE,CAAC,KAAK;KACxB;KACA,OAAO,QAAQ,KAAK,EAAE,OAAO,OAAQ,EAAE;IACzC;GACF;GACA,kBAAkB;GAClB,MAAM;EACR;CACF,SAAS,GAAG;EACV,QAAQ;EACR,KAAK,MAAM,QAAQ,WACjB,IAAI;GACF,KAAK,QAAQ,CAAC;EAChB,SAAS,eAAe,CAGxB;EAEF,MAAM;CACR,UAAU;EACR,IAAI,CAAC,OACH,KAAK,MAAM,QAAQ,WACjB,IAAI;GACF,KAAK,SAAS;EAChB,SAAS,eAAe,CAGxB;CAGN;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"union-fan-out.js","names":["#input","#outputs","#unionFanIn","#destroyCount"],"sources":["../../../../../zql/src/ivm/union-fan-out.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport type {Change} from './change.ts';\nimport type {Node} from './data.ts';\nimport type {\n FetchRequest,\n Input,\n InputBase,\n Operator,\n Output,\n} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport type {Stream} from './stream.ts';\nimport type {UnionFanIn} from './union-fan-in.ts';\n\nexport class UnionFanOut implements Operator {\n #destroyCount: number = 0;\n #unionFanIn?: UnionFanIn;\n readonly #input: Input;\n readonly #outputs: Output[] = [];\n\n constructor(input: Input) {\n this.#input = input;\n input.setOutput(this);\n }\n\n setFanIn(fanIn: UnionFanIn) {\n assert(!this.#unionFanIn, 'FanIn already set for this FanOut');\n this.#unionFanIn = fanIn;\n }\n\n *push(change: Change): Stream<'yield'> {\n must(this.#unionFanIn).fanOutStartedPushing();\n for (const output of this.#outputs) {\n yield* output.push(change, this);\n }\n yield* must(this.#unionFanIn).fanOutDonePushing(change[ChangeIndex.TYPE]);\n }\n\n *reconcile(_pusher: InputBase): Stream<'yield'> {\n must(this.#unionFanIn).fanOutStartedReconciling();\n for (const output of this.#outputs) {\n if (output.reconcile) {\n yield* output.reconcile(this);\n }\n }\n yield* must(this.#unionFanIn).fanOutDoneReconciling();\n }\n\n setOutput(output: Output): void {\n this.#outputs.push(output);\n }\n\n getSchema(): SourceSchema {\n return this.#input.getSchema();\n }\n\n fetch(req: FetchRequest): Stream<Node | 'yield'> {\n return this.#input.fetch(req);\n }\n\n destroy(): void {\n if (this.#destroyCount < this.#outputs.length) {\n ++this.#destroyCount;\n if (this.#destroyCount === this.#outputs.length) {\n this.#input.destroy();\n }\n } else {\n throw new Error('FanOut already destroyed once for each output');\n }\n }\n}\n"],"mappings":";;;AAgBA,IAAa,cAAb,MAA6C;CAC3C,gBAAwB;CACxB;CACA;CACA,WAA8B,CAAC;CAE/B,YAAY,OAAc;EACxB,KAAKA,SAAS;EACd,MAAM,UAAU,IAAI;CACtB;CAEA,SAAS,OAAmB;EAC1B,OAAO,CAAC,KAAKE,aAAa,mCAAmC;EAC7D,KAAKA,cAAc;CACrB;CAEA,CAAC,KAAK,QAAiC;EACrC,KAAK,KAAKA,WAAW,EAAE,qBAAqB;EAC5C,KAAK,MAAM,UAAU,KAAKD,UACxB,OAAO,OAAO,KAAK,QAAQ,IAAI;EAEjC,OAAO,KAAK,KAAKC,WAAW,EAAE,kBAAkB,OAAO,EAAiB;CAC1E;CAEA,CAAC,UAAU,SAAqC;EAC9C,KAAK,KAAKA,WAAW,EAAE,yBAAyB;EAChD,KAAK,MAAM,UAAU,KAAKD,UACxB,IAAI,OAAO,WACT,OAAO,OAAO,UAAU,IAAI;EAGhC,OAAO,KAAK,KAAKC,WAAW,EAAE,sBAAsB;CACtD;CAEA,UAAU,QAAsB;EAC9B,KAAKD,SAAS,KAAK,MAAM;CAC3B;CAEA,YAA0B;EACxB,OAAO,KAAKD,OAAO,UAAU;CAC/B;CAEA,MAAM,KAA2C;EAC/C,OAAO,KAAKA,OAAO,MAAM,GAAG;CAC9B;CAEA,UAAgB;EACd,IAAI,KAAKG,gBAAgB,KAAKF,SAAS,QAAQ;GAC7C,EAAE,KAAKE;GACP,IAAI,KAAKA,kBAAkB,KAAKF,SAAS,QACvC,KAAKD,OAAO,QAAQ;EAExB,OACE,MAAM,IAAI,MAAM,+CAA+C;CAEnE;AACF"}
1
+ {"version":3,"file":"union-fan-out.js","names":[],"sources":["../../../../../zql/src/ivm/union-fan-out.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport type {Change} from './change.ts';\nimport type {Node} from './data.ts';\nimport type {\n FetchRequest,\n Input,\n InputBase,\n Operator,\n Output,\n} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport type {Stream} from './stream.ts';\nimport type {UnionFanIn} from './union-fan-in.ts';\n\nexport class UnionFanOut implements Operator {\n #destroyCount: number = 0;\n #unionFanIn?: UnionFanIn;\n readonly #input: Input;\n readonly #outputs: Output[] = [];\n\n constructor(input: Input) {\n this.#input = input;\n input.setOutput(this);\n }\n\n setFanIn(fanIn: UnionFanIn) {\n assert(!this.#unionFanIn, 'FanIn already set for this FanOut');\n this.#unionFanIn = fanIn;\n }\n\n *push(change: Change): Stream<'yield'> {\n must(this.#unionFanIn).fanOutStartedPushing();\n for (const output of this.#outputs) {\n yield* output.push(change, this);\n }\n yield* must(this.#unionFanIn).fanOutDonePushing(change[ChangeIndex.TYPE]);\n }\n\n *reconcile(_pusher: InputBase): Stream<'yield'> {\n must(this.#unionFanIn).fanOutStartedReconciling();\n for (const output of this.#outputs) {\n if (output.reconcile) {\n yield* output.reconcile(this);\n }\n }\n yield* must(this.#unionFanIn).fanOutDoneReconciling();\n }\n\n setOutput(output: Output): void {\n this.#outputs.push(output);\n }\n\n getSchema(): SourceSchema {\n return this.#input.getSchema();\n }\n\n fetch(req: FetchRequest): Stream<Node | 'yield'> {\n return this.#input.fetch(req);\n }\n\n destroy(): void {\n if (this.#destroyCount < this.#outputs.length) {\n ++this.#destroyCount;\n if (this.#destroyCount === this.#outputs.length) {\n this.#input.destroy();\n }\n } else {\n throw new Error('FanOut already destroyed once for each output');\n }\n }\n}\n"],"mappings":";;;AAgBA,IAAa,cAAb,MAA6C;CAC3C,gBAAwB;CACxB;CACA;CACA,WAA8B,CAAC;CAE/B,YAAY,OAAc;EACxB,KAAK,SAAS;EACd,MAAM,UAAU,IAAI;CACtB;CAEA,SAAS,OAAmB;EAC1B,OAAO,CAAC,KAAK,aAAa,mCAAmC;EAC7D,KAAK,cAAc;CACrB;CAEA,CAAC,KAAK,QAAiC;EACrC,KAAK,KAAK,WAAW,CAAC,CAAC,qBAAqB;EAC5C,KAAK,MAAM,UAAU,KAAK,UACxB,OAAO,OAAO,KAAK,QAAQ,IAAI;EAEjC,OAAO,KAAK,KAAK,WAAW,CAAC,CAAC,kBAAkB,OAAO,EAAiB;CAC1E;CAEA,CAAC,UAAU,SAAqC;EAC9C,KAAK,KAAK,WAAW,CAAC,CAAC,yBAAyB;EAChD,KAAK,MAAM,UAAU,KAAK,UACxB,IAAI,OAAO,WACT,OAAO,OAAO,UAAU,IAAI;EAGhC,OAAO,KAAK,KAAK,WAAW,CAAC,CAAC,sBAAsB;CACtD;CAEA,UAAU,QAAsB;EAC9B,KAAK,SAAS,KAAK,MAAM;CAC3B;CAEA,YAA0B;EACxB,OAAO,KAAK,OAAO,UAAU;CAC/B;CAEA,MAAM,KAA2C;EAC/C,OAAO,KAAK,OAAO,MAAM,GAAG;CAC9B;CAEA,UAAgB;EACd,IAAI,KAAK,gBAAgB,KAAK,SAAS,QAAQ;GAC7C,EAAE,KAAK;GACP,IAAI,KAAK,kBAAkB,KAAK,SAAS,QACvC,KAAK,OAAO,QAAQ;EAExB,OACE,MAAM,IAAI,MAAM,+CAA+C;CAEnE;AACF"}
@@ -370,6 +370,6 @@ function setProperty(mutate, parentEntry, key, value) {
370
370
  }
371
371
  var setRelation = setProperty;
372
372
  //#endregion
373
- export { applyChange, idSymbol, refCountSymbol };
373
+ export { applyChange, applyChangeInternal, idSymbol, refCountSymbol };
374
374
 
375
375
  //# sourceMappingURL=view-apply-change.js.map