@rocicorp/zero 1.6.0-canary.11 → 1.6.0-canary.13

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 (659) hide show
  1. package/README.md +3 -28
  2. package/out/_virtual/{_@oxc-project_runtime@0.130.0 → _@oxc-project_runtime@0.122.0}/helpers/usingCtx.js +1 -1
  3. package/out/_virtual/_rolldown/runtime.js +1 -12
  4. package/out/analyze-query/src/analyze-cli.js.map +1 -1
  5. package/out/analyze-query/src/bin-analyze.js +1 -6
  6. package/out/analyze-query/src/bin-analyze.js.map +1 -1
  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.map +1 -1
  10. package/out/ast-to-zql/src/format.js.map +1 -1
  11. package/out/datadog/src/datadog-log-sink.js.map +1 -1
  12. package/out/otel/src/enabled.js.map +1 -1
  13. package/out/otel/src/log-options.js.map +1 -1
  14. package/out/otel/src/maybe-time.js.map +1 -1
  15. package/out/otel/src/span.js.map +1 -1
  16. package/out/replicache/src/async-iterable-to-array.js.map +1 -1
  17. package/out/replicache/src/bg-interval.js.map +1 -1
  18. package/out/replicache/src/btree/diff.js.map +1 -1
  19. package/out/replicache/src/btree/node.js.map +1 -1
  20. package/out/replicache/src/btree/read.js.map +1 -1
  21. package/out/replicache/src/btree/splice.js.map +1 -1
  22. package/out/replicache/src/btree/write.js +3 -6
  23. package/out/replicache/src/btree/write.js.map +1 -1
  24. package/out/replicache/src/call-default-fetch.js.map +1 -1
  25. package/out/replicache/src/connection-loop-delegates.js.map +1 -1
  26. package/out/replicache/src/connection-loop.js.map +1 -1
  27. package/out/replicache/src/cookies.js.map +1 -1
  28. package/out/replicache/src/dag/chunk.js.map +1 -1
  29. package/out/replicache/src/dag/gc.js.map +1 -1
  30. package/out/replicache/src/dag/key.js.map +1 -1
  31. package/out/replicache/src/dag/lazy-store.js.map +1 -1
  32. package/out/replicache/src/dag/store-impl.js.map +1 -1
  33. package/out/replicache/src/dag/store.js.map +1 -1
  34. package/out/replicache/src/dag/visitor.js.map +1 -1
  35. package/out/replicache/src/db/commit.js.map +1 -1
  36. package/out/replicache/src/db/index.js.map +1 -1
  37. package/out/replicache/src/db/read.js.map +1 -1
  38. package/out/replicache/src/db/rebase.js.map +1 -1
  39. package/out/replicache/src/db/write.js.map +1 -1
  40. package/out/replicache/src/deleted-clients.js.map +1 -1
  41. package/out/replicache/src/error-responses.js.map +1 -1
  42. package/out/replicache/src/frozen-json.js.map +1 -1
  43. package/out/replicache/src/get-default-puller.js.map +1 -1
  44. package/out/replicache/src/get-default-pusher.js.map +1 -1
  45. package/out/replicache/src/get-kv-store-provider.js.map +1 -1
  46. package/out/replicache/src/hash.js.map +1 -1
  47. package/out/replicache/src/http-request-info.js.map +1 -1
  48. package/out/replicache/src/index-defs.js.map +1 -1
  49. package/out/replicache/src/kv/expo-sqlite/store.js.map +1 -1
  50. package/out/replicache/src/kv/idb-store-with-mem-fallback.js.map +1 -1
  51. package/out/replicache/src/kv/idb-store.js.map +1 -1
  52. package/out/replicache/src/kv/mem-store.js.map +1 -1
  53. package/out/replicache/src/kv/op-sqlite/store.js.map +1 -1
  54. package/out/replicache/src/kv/read-impl.js.map +1 -1
  55. package/out/replicache/src/kv/sqlite-store.d.ts.map +1 -1
  56. package/out/replicache/src/kv/sqlite-store.js +1 -4
  57. package/out/replicache/src/kv/sqlite-store.js.map +1 -1
  58. package/out/replicache/src/kv/throw-if-closed.js.map +1 -1
  59. package/out/replicache/src/kv/write-impl-base.js.map +1 -1
  60. package/out/replicache/src/kv/write-impl.js.map +1 -1
  61. package/out/replicache/src/lazy.js.map +1 -1
  62. package/out/replicache/src/log-options.js.map +1 -1
  63. package/out/replicache/src/make-idb-name.js.map +1 -1
  64. package/out/replicache/src/new-client-channel.js.map +1 -1
  65. package/out/replicache/src/on-persist-channel.js.map +1 -1
  66. package/out/replicache/src/patch-operation.js.map +1 -1
  67. package/out/replicache/src/pending-mutations.js.map +1 -1
  68. package/out/replicache/src/persist/client-gc.js.map +1 -1
  69. package/out/replicache/src/persist/client-group-gc.js.map +1 -1
  70. package/out/replicache/src/persist/client-groups.js +0 -40
  71. package/out/replicache/src/persist/client-groups.js.map +1 -1
  72. package/out/replicache/src/persist/clients.js +0 -28
  73. package/out/replicache/src/persist/clients.js.map +1 -1
  74. package/out/replicache/src/persist/collect-idb-databases.js.map +1 -1
  75. package/out/replicache/src/persist/gather-mem-only-visitor.js.map +1 -1
  76. package/out/replicache/src/persist/gather-not-cached-visitor.js.map +1 -1
  77. package/out/replicache/src/persist/heartbeat.js.map +1 -1
  78. package/out/replicache/src/persist/idb-databases-store-db-name.js.map +1 -1
  79. package/out/replicache/src/persist/idb-databases-store.js.map +1 -1
  80. package/out/replicache/src/persist/make-client-id.js.map +1 -1
  81. package/out/replicache/src/persist/persist.js.map +1 -1
  82. package/out/replicache/src/persist/refresh.js.map +1 -1
  83. package/out/replicache/src/process-scheduler.js.map +1 -1
  84. package/out/replicache/src/pusher.js.map +1 -1
  85. package/out/replicache/src/replicache-impl.js.map +1 -1
  86. package/out/replicache/src/report-error.js.map +1 -1
  87. package/out/replicache/src/request-idle.js.map +1 -1
  88. package/out/replicache/src/scan-iterator.js.map +1 -1
  89. package/out/replicache/src/scan-options.js.map +1 -1
  90. package/out/replicache/src/set-interval-with-signal.js.map +1 -1
  91. package/out/replicache/src/subscriptions.js.map +1 -1
  92. package/out/replicache/src/sync/diff.js.map +1 -1
  93. package/out/replicache/src/sync/ids.js.map +1 -1
  94. package/out/replicache/src/sync/patch.js.map +1 -1
  95. package/out/replicache/src/sync/pull-error.js.map +1 -1
  96. package/out/replicache/src/sync/pull.js.map +1 -1
  97. package/out/replicache/src/sync/push.js.map +1 -1
  98. package/out/replicache/src/sync/request-id.js.map +1 -1
  99. package/out/replicache/src/to-error.js.map +1 -1
  100. package/out/replicache/src/transaction-closed-error.js.map +1 -1
  101. package/out/replicache/src/transactions.js.map +1 -1
  102. package/out/replicache/src/with-transactions.js.map +1 -1
  103. package/out/shared/src/abort-error.js.map +1 -1
  104. package/out/shared/src/arrays.js.map +1 -1
  105. package/out/shared/src/asserts.js.map +1 -1
  106. package/out/shared/src/bigint-json.js.map +1 -1
  107. package/out/shared/src/binary-search.js.map +1 -1
  108. package/out/shared/src/broadcast-channel.js.map +1 -1
  109. package/out/shared/src/browser-env.js.map +1 -1
  110. package/out/shared/src/btree-set.js.map +1 -1
  111. package/out/shared/src/cache.js.map +1 -1
  112. package/out/shared/src/centroid.js.map +1 -1
  113. package/out/shared/src/custom-key-map.js.map +1 -1
  114. package/out/shared/src/custom-key-set.js.map +1 -1
  115. package/out/shared/src/deep-clone.js.map +1 -1
  116. package/out/shared/src/deep-merge.js.map +1 -1
  117. package/out/shared/src/document-visible.js.map +1 -1
  118. package/out/shared/src/dotenv.js.map +1 -1
  119. package/out/shared/src/error.js.map +1 -1
  120. package/out/shared/src/hash.js.map +1 -1
  121. package/out/shared/src/iterables.js.map +1 -1
  122. package/out/shared/src/json-schema.js.map +1 -1
  123. package/out/shared/src/json.js.map +1 -1
  124. package/out/shared/src/logging-test-utils.js.map +1 -1
  125. package/out/shared/src/logging.js.map +1 -1
  126. package/out/shared/src/map.js.map +1 -1
  127. package/out/shared/src/must.js.map +1 -1
  128. package/out/shared/src/object-traversal.js.map +1 -1
  129. package/out/shared/src/objects.js.map +1 -1
  130. package/out/shared/src/options.js.map +1 -1
  131. package/out/shared/src/parse-big-int.js.map +1 -1
  132. package/out/shared/src/promise-race.js.map +1 -1
  133. package/out/shared/src/queue.d.ts.map +1 -1
  134. package/out/shared/src/queue.js +21 -15
  135. package/out/shared/src/queue.js.map +1 -1
  136. package/out/shared/src/rand.js.map +1 -1
  137. package/out/shared/src/random-uint64.js.map +1 -1
  138. package/out/shared/src/random-values.js.map +1 -1
  139. package/out/shared/src/record-proxy.js.map +1 -1
  140. package/out/shared/src/resolved-promises.js.map +1 -1
  141. package/out/shared/src/sentinels.js.map +1 -1
  142. package/out/shared/src/set-utils.js.map +1 -1
  143. package/out/shared/src/size-of-value.js.map +1 -1
  144. package/out/shared/src/sleep.js.map +1 -1
  145. package/out/shared/src/sorted-entries.js.map +1 -1
  146. package/out/shared/src/string-compare.js.map +1 -1
  147. package/out/shared/src/subscribable.js.map +1 -1
  148. package/out/shared/src/tdigest-schema.js.map +1 -1
  149. package/out/shared/src/tdigest.js.map +1 -1
  150. package/out/shared/src/valita.js.map +1 -1
  151. package/out/z2s/src/compiler.js.map +1 -1
  152. package/out/z2s/src/sql.js.map +1 -1
  153. package/out/zero/package.js +23 -23
  154. package/out/zero/package.js.map +1 -1
  155. package/out/zero/src/build-schema.js.map +1 -1
  156. package/out/zero/src/zero-cache-dev.js.map +1 -1
  157. package/out/zero/src/zero-out.js.map +1 -1
  158. package/out/zero-cache/src/auth/auth.js.map +1 -1
  159. package/out/zero-cache/src/auth/jwt.js.map +1 -1
  160. package/out/zero-cache/src/auth/load-permissions.js.map +1 -1
  161. package/out/zero-cache/src/auth/read-authorizer.js.map +1 -1
  162. package/out/zero-cache/src/auth/write-authorizer.js.map +1 -1
  163. package/out/zero-cache/src/config/network.js.map +1 -1
  164. package/out/zero-cache/src/config/normalize.js.map +1 -1
  165. package/out/zero-cache/src/config/server-context.js.map +1 -1
  166. package/out/zero-cache/src/config/zero-config.js +0 -5
  167. package/out/zero-cache/src/config/zero-config.js.map +1 -1
  168. package/out/zero-cache/src/custom/fetch.js.map +1 -1
  169. package/out/zero-cache/src/custom-queries/transform-query.js.map +1 -1
  170. package/out/zero-cache/src/db/create.js.map +1 -1
  171. package/out/zero-cache/src/db/delete-lite-db.js.map +1 -1
  172. package/out/zero-cache/src/db/lite-tables.js.map +1 -1
  173. package/out/zero-cache/src/db/migration-lite.js +0 -19
  174. package/out/zero-cache/src/db/migration-lite.js.map +1 -1
  175. package/out/zero-cache/src/db/migration.js +0 -19
  176. package/out/zero-cache/src/db/migration.js.map +1 -1
  177. package/out/zero-cache/src/db/pg-copy-binary.js.map +1 -1
  178. package/out/zero-cache/src/db/pg-copy.js.map +1 -1
  179. package/out/zero-cache/src/db/pg-to-lite.js.map +1 -1
  180. package/out/zero-cache/src/db/pg-type-parser.js.map +1 -1
  181. package/out/zero-cache/src/db/run-transaction.js.map +1 -1
  182. package/out/zero-cache/src/db/specs.js.map +1 -1
  183. package/out/zero-cache/src/db/statements.js.map +1 -1
  184. package/out/zero-cache/src/db/transaction-pool.js.map +1 -1
  185. package/out/zero-cache/src/db/warmup.js.map +1 -1
  186. package/out/zero-cache/src/observability/events.js.map +1 -1
  187. package/out/zero-cache/src/observability/metrics.js.map +1 -1
  188. package/out/zero-cache/src/scripts/decommission.js.map +1 -1
  189. package/out/zero-cache/src/scripts/deploy-permissions.js.map +1 -1
  190. package/out/zero-cache/src/scripts/permissions.js.map +1 -1
  191. package/out/zero-cache/src/server/anonymous-otel-start.js +10 -11
  192. package/out/zero-cache/src/server/anonymous-otel-start.js.map +1 -1
  193. package/out/zero-cache/src/server/change-streamer.js.map +1 -1
  194. package/out/zero-cache/src/server/inspector-delegate.js.map +1 -1
  195. package/out/zero-cache/src/server/logging.js.map +1 -1
  196. package/out/zero-cache/src/server/main.js.map +1 -1
  197. package/out/zero-cache/src/server/mutator.js.map +1 -1
  198. package/out/zero-cache/src/server/otel-diag-logger.js.map +1 -1
  199. package/out/zero-cache/src/server/otel-log-sink.js.map +1 -1
  200. package/out/zero-cache/src/server/otel-start.js +1 -1
  201. package/out/zero-cache/src/server/otel-start.js.map +1 -1
  202. package/out/zero-cache/src/server/priority-op.js.map +1 -1
  203. package/out/zero-cache/src/server/reaper.js.map +1 -1
  204. package/out/zero-cache/src/server/replicator.js.map +1 -1
  205. package/out/zero-cache/src/server/runner/main.js.map +1 -1
  206. package/out/zero-cache/src/server/runner/run-worker.js.map +1 -1
  207. package/out/zero-cache/src/server/runner/runtime.js.map +1 -1
  208. package/out/zero-cache/src/server/runner/zero-dispatcher.js.map +1 -1
  209. package/out/zero-cache/src/server/shadow-syncer.js.map +1 -1
  210. package/out/zero-cache/src/server/syncer.js.map +1 -1
  211. package/out/zero-cache/src/server/worker-dispatcher.js.map +1 -1
  212. package/out/zero-cache/src/server/worker-urls.js.map +1 -1
  213. package/out/zero-cache/src/services/analyze.d.ts.map +1 -1
  214. package/out/zero-cache/src/services/analyze.js +2 -5
  215. package/out/zero-cache/src/services/analyze.js.map +1 -1
  216. package/out/zero-cache/src/services/change-source/common/backfill-manager.js.map +1 -1
  217. package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.js.map +1 -1
  218. package/out/zero-cache/src/services/change-source/common/replica-schema.js.map +1 -1
  219. package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
  220. package/out/zero-cache/src/services/change-source/pg/backfill-metadata.js.map +1 -1
  221. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js.map +1 -1
  222. package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
  223. package/out/zero-cache/src/services/change-source/pg/decommission.js.map +1 -1
  224. package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
  225. package/out/zero-cache/src/services/change-source/pg/logical-replication/binary-reader.js.map +1 -1
  226. package/out/zero-cache/src/services/change-source/pg/logical-replication/pgoutput-parser.js.map +1 -1
  227. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js.map +1 -1
  228. package/out/zero-cache/src/services/change-source/pg/lsn.js.map +1 -1
  229. package/out/zero-cache/src/services/change-source/pg/replication-slots.js.map +1 -1
  230. package/out/zero-cache/src/services/change-source/pg/schema/ddl.js.map +1 -1
  231. package/out/zero-cache/src/services/change-source/pg/schema/init.js.map +1 -1
  232. package/out/zero-cache/src/services/change-source/pg/schema/published.js.map +1 -1
  233. package/out/zero-cache/src/services/change-source/pg/schema/shard.js.map +1 -1
  234. package/out/zero-cache/src/services/change-source/pg/schema/validation.js.map +1 -1
  235. package/out/zero-cache/src/services/change-source/protocol/current/control.js.map +1 -1
  236. package/out/zero-cache/src/services/change-source/protocol/current/data.js +0 -2
  237. package/out/zero-cache/src/services/change-source/protocol/current/data.js.map +1 -1
  238. package/out/zero-cache/src/services/change-source/protocol/current/downstream.js.map +1 -1
  239. package/out/zero-cache/src/services/change-source/protocol/current/json.js.map +1 -1
  240. package/out/zero-cache/src/services/change-source/protocol/current/status.js.map +1 -1
  241. package/out/zero-cache/src/services/change-source/protocol/current/upstream.js.map +1 -1
  242. package/out/zero-cache/src/services/change-streamer/backup-monitor.js.map +1 -1
  243. package/out/zero-cache/src/services/change-streamer/broadcast.js.map +1 -1
  244. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
  245. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
  246. package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
  247. package/out/zero-cache/src/services/change-streamer/forwarder.js.map +1 -1
  248. package/out/zero-cache/src/services/change-streamer/replica-monitor.js.map +1 -1
  249. package/out/zero-cache/src/services/change-streamer/schema/init.js +25 -21
  250. package/out/zero-cache/src/services/change-streamer/schema/init.js.map +1 -1
  251. package/out/zero-cache/src/services/change-streamer/schema/tables.js.map +1 -1
  252. package/out/zero-cache/src/services/change-streamer/snapshot.js +0 -15
  253. package/out/zero-cache/src/services/change-streamer/snapshot.js.map +1 -1
  254. package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
  255. package/out/zero-cache/src/services/change-streamer/subscriber.js.map +1 -1
  256. package/out/zero-cache/src/services/heapz.js.map +1 -1
  257. package/out/zero-cache/src/services/http-service.js.map +1 -1
  258. package/out/zero-cache/src/services/life-cycle.js.map +1 -1
  259. package/out/zero-cache/src/services/limiter/sliding-window-limiter.js.map +1 -1
  260. package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
  261. package/out/zero-cache/src/services/mutagen/error.js.map +1 -1
  262. package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
  263. package/out/zero-cache/src/services/mutagen/pusher.js.map +1 -1
  264. package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
  265. package/out/zero-cache/src/services/replicator/incremental-sync.js.map +1 -1
  266. package/out/zero-cache/src/services/replicator/notifier.js.map +1 -1
  267. package/out/zero-cache/src/services/replicator/replication-status.js.map +1 -1
  268. package/out/zero-cache/src/services/replicator/replicator.js.map +1 -1
  269. package/out/zero-cache/src/services/replicator/reporter/recorder.js.map +1 -1
  270. package/out/zero-cache/src/services/replicator/reporter/report-schema.js.map +1 -1
  271. package/out/zero-cache/src/services/replicator/schema/change-log.js.map +1 -1
  272. package/out/zero-cache/src/services/replicator/schema/column-metadata.js.map +1 -1
  273. package/out/zero-cache/src/services/replicator/schema/replication-state.js.map +1 -1
  274. package/out/zero-cache/src/services/replicator/schema/table-metadata.js.map +1 -1
  275. package/out/zero-cache/src/services/replicator/write-worker-client.js.map +1 -1
  276. package/out/zero-cache/src/services/replicator/write-worker.js.map +1 -1
  277. package/out/zero-cache/src/services/run-ast.d.ts.map +1 -1
  278. package/out/zero-cache/src/services/run-ast.js +0 -1
  279. package/out/zero-cache/src/services/run-ast.js.map +1 -1
  280. package/out/zero-cache/src/services/runner.js.map +1 -1
  281. package/out/zero-cache/src/services/running-state.js.map +1 -1
  282. package/out/zero-cache/src/services/shadow-sync/shadow-sync-service.js.map +1 -1
  283. package/out/zero-cache/src/services/statz.js.map +1 -1
  284. package/out/zero-cache/src/services/view-syncer/active-users-gauge.js.map +1 -1
  285. package/out/zero-cache/src/services/view-syncer/client-handler.js.map +1 -1
  286. package/out/zero-cache/src/services/view-syncer/client-schema.js.map +1 -1
  287. package/out/zero-cache/src/services/view-syncer/connection-context-manager.js.map +1 -1
  288. package/out/zero-cache/src/services/view-syncer/cvr-purger.d.ts.map +1 -1
  289. package/out/zero-cache/src/services/view-syncer/cvr-purger.js +1 -2
  290. package/out/zero-cache/src/services/view-syncer/cvr-purger.js.map +1 -1
  291. package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
  292. package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
  293. package/out/zero-cache/src/services/view-syncer/drain-coordinator.js.map +1 -1
  294. package/out/zero-cache/src/services/view-syncer/inspect-handler.d.ts +14 -0
  295. package/out/zero-cache/src/services/view-syncer/inspect-handler.d.ts.map +1 -1
  296. package/out/zero-cache/src/services/view-syncer/inspect-handler.js +25 -2
  297. package/out/zero-cache/src/services/view-syncer/inspect-handler.js.map +1 -1
  298. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
  299. package/out/zero-cache/src/services/view-syncer/row-record-cache.js.map +1 -1
  300. package/out/zero-cache/src/services/view-syncer/row-set-signature.js.map +1 -1
  301. package/out/zero-cache/src/services/view-syncer/schema/cvr.js.map +1 -1
  302. package/out/zero-cache/src/services/view-syncer/schema/init.js +113 -97
  303. package/out/zero-cache/src/services/view-syncer/schema/init.js.map +1 -1
  304. package/out/zero-cache/src/services/view-syncer/schema/types.js +1 -103
  305. package/out/zero-cache/src/services/view-syncer/schema/types.js.map +1 -1
  306. package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
  307. package/out/zero-cache/src/services/view-syncer/tracer.js.map +1 -1
  308. package/out/zero-cache/src/services/view-syncer/ttl-clock.js.map +1 -1
  309. package/out/zero-cache/src/services/view-syncer/view-syncer.js +1 -4
  310. package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
  311. package/out/zero-cache/src/types/configuration-error.js.map +1 -1
  312. package/out/zero-cache/src/types/error-with-level.js.map +1 -1
  313. package/out/zero-cache/src/types/http.js.map +1 -1
  314. package/out/zero-cache/src/types/lexi-version.js.map +1 -1
  315. package/out/zero-cache/src/types/lite.js.map +1 -1
  316. package/out/zero-cache/src/types/names.js.map +1 -1
  317. package/out/zero-cache/src/types/pg-data-type.js.map +1 -1
  318. package/out/zero-cache/src/types/pg.js.map +1 -1
  319. package/out/zero-cache/src/types/processes.js.map +1 -1
  320. package/out/zero-cache/src/types/profiler.js.map +1 -1
  321. package/out/zero-cache/src/types/row-key.js.map +1 -1
  322. package/out/zero-cache/src/types/shards.js.map +1 -1
  323. package/out/zero-cache/src/types/sql.js.map +1 -1
  324. package/out/zero-cache/src/types/state-version.js.map +1 -1
  325. package/out/zero-cache/src/types/streams.js.map +1 -1
  326. package/out/zero-cache/src/types/strings.js.map +1 -1
  327. package/out/zero-cache/src/types/subscription.js.map +1 -1
  328. package/out/zero-cache/src/types/timeout.js.map +1 -1
  329. package/out/zero-cache/src/types/url-params.js.map +1 -1
  330. package/out/zero-cache/src/types/websocket-handoff.js.map +1 -1
  331. package/out/zero-cache/src/types/ws.js.map +1 -1
  332. package/out/zero-cache/src/workers/connect-params.js.map +1 -1
  333. package/out/zero-cache/src/workers/connection.js.map +1 -1
  334. package/out/zero-cache/src/workers/mutator.js.map +1 -1
  335. package/out/zero-cache/src/workers/replicator.js.map +1 -1
  336. package/out/zero-cache/src/workers/syncer-ws-message-handler.js.map +1 -1
  337. package/out/zero-cache/src/workers/syncer.js.map +1 -1
  338. package/out/zero-client/src/client/active-clients-manager.js.map +1 -1
  339. package/out/zero-client/src/client/connection-manager.js +1 -2
  340. package/out/zero-client/src/client/connection-manager.js.map +1 -1
  341. package/out/zero-client/src/client/connection.js.map +1 -1
  342. package/out/zero-client/src/client/context.js.map +1 -1
  343. package/out/zero-client/src/client/crud-impl.js.map +1 -1
  344. package/out/zero-client/src/client/crud.js.map +1 -1
  345. package/out/zero-client/src/client/custom.js +1 -2
  346. package/out/zero-client/src/client/custom.js.map +1 -1
  347. package/out/zero-client/src/client/delete-clients-manager.js.map +1 -1
  348. package/out/zero-client/src/client/enable-analytics.js.map +1 -1
  349. package/out/zero-client/src/client/error.js.map +1 -1
  350. package/out/zero-client/src/client/http-string.js.map +1 -1
  351. package/out/zero-client/src/client/inspector/client-group.js.map +1 -1
  352. package/out/zero-client/src/client/inspector/client.js.map +1 -1
  353. package/out/zero-client/src/client/inspector/html-dialog-prompt.js.map +1 -1
  354. package/out/zero-client/src/client/inspector/inspector.js.map +1 -1
  355. package/out/zero-client/src/client/inspector/lazy-inspector.js.map +1 -1
  356. package/out/zero-client/src/client/inspector/query.js.map +1 -1
  357. package/out/zero-client/src/client/ivm-branch.js.map +1 -1
  358. package/out/zero-client/src/client/keys.js.map +1 -1
  359. package/out/zero-client/src/client/log-options.js.map +1 -1
  360. package/out/zero-client/src/client/make-mutate-property.js.map +1 -1
  361. package/out/zero-client/src/client/make-replicache-mutators.js.map +1 -1
  362. package/out/zero-client/src/client/metrics.js.map +1 -1
  363. package/out/zero-client/src/client/mutation-tracker.js.map +1 -1
  364. package/out/zero-client/src/client/mutator-proxy.js.map +1 -1
  365. package/out/zero-client/src/client/options.js.map +1 -1
  366. package/out/zero-client/src/client/query-manager.js.map +1 -1
  367. package/out/zero-client/src/client/reload-error-handler.js.map +1 -1
  368. package/out/zero-client/src/client/server-option.js.map +1 -1
  369. package/out/zero-client/src/client/version.js +1 -1
  370. package/out/zero-client/src/client/zero-poke-handler.js.map +1 -1
  371. package/out/zero-client/src/client/zero-rep.js.map +1 -1
  372. package/out/zero-client/src/client/zero.d.ts.map +1 -1
  373. package/out/zero-client/src/client/zero.js +32 -58
  374. package/out/zero-client/src/client/zero.js.map +1 -1
  375. package/out/zero-client/src/util/nanoid.js.map +1 -1
  376. package/out/zero-client/src/util/socket.d.ts +3 -0
  377. package/out/zero-client/src/util/socket.d.ts.map +1 -0
  378. package/out/zero-client/src/util/socket.js +8 -0
  379. package/out/zero-client/src/util/socket.js.map +1 -0
  380. package/out/zero-protocol/src/analyze-query-result.js +0 -3
  381. package/out/zero-protocol/src/analyze-query-result.js.map +1 -1
  382. package/out/zero-protocol/src/application-error.js.map +1 -1
  383. package/out/zero-protocol/src/ast.js.map +1 -1
  384. package/out/zero-protocol/src/change-desired-queries.js +0 -1
  385. package/out/zero-protocol/src/change-desired-queries.js.map +1 -1
  386. package/out/zero-protocol/src/client-schema.js.map +1 -1
  387. package/out/zero-protocol/src/close-connection.js.map +1 -1
  388. package/out/zero-protocol/src/connect.js +0 -7
  389. package/out/zero-protocol/src/connect.js.map +1 -1
  390. package/out/zero-protocol/src/custom-queries.js.map +1 -1
  391. package/out/zero-protocol/src/data.js.map +1 -1
  392. package/out/zero-protocol/src/delete-clients.js.map +1 -1
  393. package/out/zero-protocol/src/down.js.map +1 -1
  394. package/out/zero-protocol/src/error.js +0 -7
  395. package/out/zero-protocol/src/error.js.map +1 -1
  396. package/out/zero-protocol/src/inspect-down.js.map +1 -1
  397. package/out/zero-protocol/src/inspect-up.js +0 -1
  398. package/out/zero-protocol/src/inspect-up.js.map +1 -1
  399. package/out/zero-protocol/src/mutate-server.js.map +1 -1
  400. package/out/zero-protocol/src/mutation-id.js.map +1 -1
  401. package/out/zero-protocol/src/mutation.js.map +1 -1
  402. package/out/zero-protocol/src/mutations-patch.js.map +1 -1
  403. package/out/zero-protocol/src/ping.js.map +1 -1
  404. package/out/zero-protocol/src/poke.js +0 -4
  405. package/out/zero-protocol/src/poke.js.map +1 -1
  406. package/out/zero-protocol/src/pong.js.map +1 -1
  407. package/out/zero-protocol/src/primary-key.js.map +1 -1
  408. package/out/zero-protocol/src/protocol-version.js.map +1 -1
  409. package/out/zero-protocol/src/pull.js.map +1 -1
  410. package/out/zero-protocol/src/push.js +0 -16
  411. package/out/zero-protocol/src/push.js.map +1 -1
  412. package/out/zero-protocol/src/queries-patch.js.map +1 -1
  413. package/out/zero-protocol/src/query-hash.js.map +1 -1
  414. package/out/zero-protocol/src/query-server.js.map +1 -1
  415. package/out/zero-protocol/src/row-patch.js.map +1 -1
  416. package/out/zero-protocol/src/up.js.map +1 -1
  417. package/out/zero-protocol/src/update-auth.js.map +1 -1
  418. package/out/zero-protocol/src/version.js.map +1 -1
  419. package/out/zero-react/src/use-connection-state.js +2 -4
  420. package/out/zero-react/src/use-connection-state.js.map +1 -1
  421. package/out/zero-react/src/use-query.js +4 -6
  422. package/out/zero-react/src/use-query.js.map +1 -1
  423. package/out/zero-react/src/use-zero-online.js +2 -4
  424. package/out/zero-react/src/use-zero-online.js.map +1 -1
  425. package/out/zero-react/src/zero-provider.js +12 -15
  426. package/out/zero-react/src/zero-provider.js.map +1 -1
  427. package/out/zero-schema/src/builder/relationship-builder.js.map +1 -1
  428. package/out/zero-schema/src/builder/schema-builder.js.map +1 -1
  429. package/out/zero-schema/src/builder/table-builder.js.map +1 -1
  430. package/out/zero-schema/src/compiled-permissions.js.map +1 -1
  431. package/out/zero-schema/src/name-mapper.js.map +1 -1
  432. package/out/zero-schema/src/permissions.js.map +1 -1
  433. package/out/zero-schema/src/schema-config.js.map +1 -1
  434. package/out/zero-server/src/adapters/drizzle.js.map +1 -1
  435. package/out/zero-server/src/adapters/kysely.js.map +1 -1
  436. package/out/zero-server/src/adapters/pg.js +1 -1
  437. package/out/zero-server/src/adapters/pg.js.map +1 -1
  438. package/out/zero-server/src/adapters/postgresjs.js.map +1 -1
  439. package/out/zero-server/src/adapters/prisma.js.map +1 -1
  440. package/out/zero-server/src/custom.js +1 -2
  441. package/out/zero-server/src/custom.js.map +1 -1
  442. package/out/zero-server/src/logging.js.map +1 -1
  443. package/out/zero-server/src/pg-query-executor.js.map +1 -1
  444. package/out/zero-server/src/process-mutations.js.map +1 -1
  445. package/out/zero-server/src/push-processor.js.map +1 -1
  446. package/out/zero-server/src/queries/process-queries.js.map +1 -1
  447. package/out/zero-server/src/schema.js.map +1 -1
  448. package/out/zero-server/src/zql-database.js.map +1 -1
  449. package/out/zero-solid/src/solid-view.js +1 -1
  450. package/out/zero-solid/src/solid-view.js.map +1 -1
  451. package/out/zero-solid/src/use-connection-state.js +1 -1
  452. package/out/zero-solid/src/use-connection-state.js.map +1 -1
  453. package/out/zero-solid/src/use-query.js +2 -2
  454. package/out/zero-solid/src/use-query.js.map +1 -1
  455. package/out/zero-solid/src/use-zero-online.js +1 -1
  456. package/out/zero-solid/src/use-zero-online.js.map +1 -1
  457. package/out/zero-solid/src/use-zero.js +1 -1
  458. package/out/zero-solid/src/use-zero.js.map +1 -1
  459. package/out/zero-types/src/format.js.map +1 -1
  460. package/out/zero-types/src/name-mapper.js.map +1 -1
  461. package/out/zql/src/builder/builder.js.map +1 -1
  462. package/out/zql/src/builder/debug-delegate.d.ts +0 -5
  463. package/out/zql/src/builder/debug-delegate.d.ts.map +1 -1
  464. package/out/zql/src/builder/debug-delegate.js +1 -10
  465. package/out/zql/src/builder/debug-delegate.js.map +1 -1
  466. package/out/zql/src/builder/filter.js.map +1 -1
  467. package/out/zql/src/builder/like.js.map +1 -1
  468. package/out/zql/src/error.js.map +1 -1
  469. package/out/zql/src/ivm/array-view.js.map +1 -1
  470. package/out/zql/src/ivm/cap.js.map +1 -1
  471. package/out/zql/src/ivm/change.js.map +1 -1
  472. package/out/zql/src/ivm/constraint.js +1 -1
  473. package/out/zql/src/ivm/constraint.js.map +1 -1
  474. package/out/zql/src/ivm/data.js.map +1 -1
  475. package/out/zql/src/ivm/exists.js.map +1 -1
  476. package/out/zql/src/ivm/fan-in.js.map +1 -1
  477. package/out/zql/src/ivm/fan-out.js.map +1 -1
  478. package/out/zql/src/ivm/filter-operators.js.map +1 -1
  479. package/out/zql/src/ivm/filter-push.js.map +1 -1
  480. package/out/zql/src/ivm/filter.js.map +1 -1
  481. package/out/zql/src/ivm/flipped-join.d.ts +8 -4
  482. package/out/zql/src/ivm/flipped-join.d.ts.map +1 -1
  483. package/out/zql/src/ivm/flipped-join.js +63 -59
  484. package/out/zql/src/ivm/flipped-join.js.map +1 -1
  485. package/out/zql/src/ivm/join-utils.js.map +1 -1
  486. package/out/zql/src/ivm/join.js.map +1 -1
  487. package/out/zql/src/ivm/maybe-split-and-push-edit-change.js.map +1 -1
  488. package/out/zql/src/ivm/memory-source.js.map +1 -1
  489. package/out/zql/src/ivm/memory-storage.js.map +1 -1
  490. package/out/zql/src/ivm/operator.d.ts +1 -1
  491. package/out/zql/src/ivm/operator.js.map +1 -1
  492. package/out/zql/src/ivm/push-accumulated.js.map +1 -1
  493. package/out/zql/src/ivm/schema.d.ts +8 -0
  494. package/out/zql/src/ivm/schema.d.ts.map +1 -1
  495. package/out/zql/src/ivm/skip-yields.js.map +1 -1
  496. package/out/zql/src/ivm/skip.js.map +1 -1
  497. package/out/zql/src/ivm/source.js.map +1 -1
  498. package/out/zql/src/ivm/stream.js.map +1 -1
  499. package/out/zql/src/ivm/take.js.map +1 -1
  500. package/out/zql/src/ivm/union-fan-in.js.map +1 -1
  501. package/out/zql/src/ivm/union-fan-out.js.map +1 -1
  502. package/out/zql/src/ivm/view-apply-change.js.map +1 -1
  503. package/out/zql/src/mutate/crud.js.map +1 -1
  504. package/out/zql/src/mutate/custom.js.map +1 -1
  505. package/out/zql/src/mutate/mutator-registry.js.map +1 -1
  506. package/out/zql/src/mutate/mutator.js.map +1 -1
  507. package/out/zql/src/planner/planner-builder.js.map +1 -1
  508. package/out/zql/src/planner/planner-connection.js.map +1 -1
  509. package/out/zql/src/planner/planner-constraint.js.map +1 -1
  510. package/out/zql/src/planner/planner-debug.js.map +1 -1
  511. package/out/zql/src/planner/planner-fan-in.js.map +1 -1
  512. package/out/zql/src/planner/planner-fan-out.js.map +1 -1
  513. package/out/zql/src/planner/planner-graph.js.map +1 -1
  514. package/out/zql/src/planner/planner-join.d.ts.map +1 -1
  515. package/out/zql/src/planner/planner-join.js +1 -2
  516. package/out/zql/src/planner/planner-join.js.map +1 -1
  517. package/out/zql/src/planner/planner-node.js.map +1 -1
  518. package/out/zql/src/planner/planner-source.js.map +1 -1
  519. package/out/zql/src/planner/planner-terminus.js.map +1 -1
  520. package/out/zql/src/query/complete-ordering.js.map +1 -1
  521. package/out/zql/src/query/create-builder.js.map +1 -1
  522. package/out/zql/src/query/error.js.map +1 -1
  523. package/out/zql/src/query/escape-like.js.map +1 -1
  524. package/out/zql/src/query/expression.js.map +1 -1
  525. package/out/zql/src/query/measure-push-operator.js.map +1 -1
  526. package/out/zql/src/query/metrics-delegate.js.map +1 -1
  527. package/out/zql/src/query/named.js.map +1 -1
  528. package/out/zql/src/query/query-delegate-base.js.map +1 -1
  529. package/out/zql/src/query/query-impl.js +1 -1
  530. package/out/zql/src/query/query-impl.js.map +1 -1
  531. package/out/zql/src/query/query-internals.js.map +1 -1
  532. package/out/zql/src/query/query-registry.js.map +1 -1
  533. package/out/zql/src/query/runnable-query-impl.js.map +1 -1
  534. package/out/zql/src/query/static-query.js.map +1 -1
  535. package/out/zql/src/query/ttl.js.map +1 -1
  536. package/out/zql/src/query/validate-input.js.map +1 -1
  537. package/out/zqlite/src/database-storage.js.map +1 -1
  538. package/out/zqlite/src/db.js.map +1 -1
  539. package/out/zqlite/src/explain-queries.js.map +1 -1
  540. package/out/zqlite/src/internal/sql-inline.js.map +1 -1
  541. package/out/zqlite/src/internal/sql.js.map +1 -1
  542. package/out/zqlite/src/internal/statement-cache.js.map +1 -1
  543. package/out/zqlite/src/query-builder.js.map +1 -1
  544. package/out/zqlite/src/query-delegate.js.map +1 -1
  545. package/out/zqlite/src/resolve-scalar-subqueries.js.map +1 -1
  546. package/out/zqlite/src/sqlite-cost-model.js.map +1 -1
  547. package/out/zqlite/src/sqlite-stat-fanout.js.map +1 -1
  548. package/out/zqlite/src/table-source.d.ts.map +1 -1
  549. package/out/zqlite/src/table-source.js +6 -6
  550. package/out/zqlite/src/table-source.js.map +1 -1
  551. package/package.json +23 -23
  552. package/out/_virtual/__vite-optional-peer-dep_pg-native_pg.js +0 -13
  553. package/out/_virtual/__vite-optional-peer-dep_pg-native_pg.js.map +0 -1
  554. package/out/node_modules/.pnpm/@opentelemetry_semantic-conventions@1.41.1/node_modules/@opentelemetry/semantic-conventions/build/esm/stable_attributes.js +0 -12
  555. package/out/node_modules/.pnpm/@opentelemetry_semantic-conventions@1.41.1/node_modules/@opentelemetry/semantic-conventions/build/esm/stable_attributes.js.map +0 -1
  556. package/out/node_modules/.pnpm/pg-cloudflare@1.3.0/node_modules/pg-cloudflare/dist/empty.js +0 -11
  557. package/out/node_modules/.pnpm/pg-cloudflare@1.3.0/node_modules/pg-cloudflare/dist/empty.js.map +0 -1
  558. package/out/node_modules/.pnpm/pg-connection-string@2.12.0/node_modules/pg-connection-string/index.js +0 -130
  559. package/out/node_modules/.pnpm/pg-connection-string@2.12.0/node_modules/pg-connection-string/index.js.map +0 -1
  560. package/out/node_modules/.pnpm/pg-int8@1.0.1/node_modules/pg-int8/index.js +0 -62
  561. package/out/node_modules/.pnpm/pg-int8@1.0.1/node_modules/pg-int8/index.js.map +0 -1
  562. package/out/node_modules/.pnpm/pg-pool@3.13.0_pg@8.20.0/node_modules/pg-pool/index.js +0 -353
  563. package/out/node_modules/.pnpm/pg-pool@3.13.0_pg@8.20.0/node_modules/pg-pool/index.js.map +0 -1
  564. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/buffer-reader.js +0 -60
  565. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/buffer-reader.js.map +0 -1
  566. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/buffer-writer.js +0 -81
  567. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/buffer-writer.js.map +0 -1
  568. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/index.js +0 -35
  569. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/index.js.map +0 -1
  570. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/messages.js +0 -167
  571. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/messages.js.map +0 -1
  572. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/parser.js +0 -288
  573. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/parser.js.map +0 -1
  574. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/serializer.js +0 -177
  575. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/serializer.js.map +0 -1
  576. package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/index.js +0 -46
  577. package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/index.js.map +0 -1
  578. package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/lib/arrayParser.js +0 -16
  579. package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/lib/arrayParser.js.map +0 -1
  580. package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/lib/binaryParsers.js +0 -165
  581. package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/lib/binaryParsers.js.map +0 -1
  582. package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/lib/builtins.js +0 -81
  583. package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/lib/builtins.js.map +0 -1
  584. package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/lib/textParsers.js +0 -167
  585. package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/lib/textParsers.js.map +0 -1
  586. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/esm/index.js +0 -19
  587. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/esm/index.js.map +0 -1
  588. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/client.js +0 -508
  589. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/client.js.map +0 -1
  590. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/connection-parameters.js +0 -104
  591. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/connection-parameters.js.map +0 -1
  592. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/connection.js +0 -160
  593. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/connection.js.map +0 -1
  594. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/cert-signatures.js +0 -97
  595. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/cert-signatures.js.map +0 -1
  596. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/sasl.js +0 -131
  597. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/sasl.js.map +0 -1
  598. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/utils-legacy.js +0 -39
  599. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/utils-legacy.js.map +0 -1
  600. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/utils-webcrypto.js +0 -89
  601. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/utils-webcrypto.js.map +0 -1
  602. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/utils.js +0 -13
  603. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/utils.js.map +0 -1
  604. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/defaults.js +0 -46
  605. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/defaults.js.map +0 -1
  606. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/index.js +0 -71
  607. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/index.js.map +0 -1
  608. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/native/client.js +0 -226
  609. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/native/client.js.map +0 -1
  610. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/native/index.js +0 -11
  611. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/native/index.js.map +0 -1
  612. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/native/query.js +0 -117
  613. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/native/query.js.map +0 -1
  614. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/query.js +0 -151
  615. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/query.js.map +0 -1
  616. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/result.js +0 -76
  617. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/result.js.map +0 -1
  618. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/stream.js +0 -73
  619. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/stream.js.map +0 -1
  620. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/type-overrides.js +0 -35
  621. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/type-overrides.js.map +0 -1
  622. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/utils.js +0 -118
  623. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/utils.js.map +0 -1
  624. package/out/node_modules/.pnpm/pgpass@1.0.5/node_modules/pgpass/lib/helper.js +0 -147
  625. package/out/node_modules/.pnpm/pgpass@1.0.5/node_modules/pgpass/lib/helper.js.map +0 -1
  626. package/out/node_modules/.pnpm/pgpass@1.0.5/node_modules/pgpass/lib/index.js +0 -21
  627. package/out/node_modules/.pnpm/pgpass@1.0.5/node_modules/pgpass/lib/index.js.map +0 -1
  628. package/out/node_modules/.pnpm/postgres-array@2.0.0/node_modules/postgres-array/index.js +0 -84
  629. package/out/node_modules/.pnpm/postgres-array@2.0.0/node_modules/postgres-array/index.js.map +0 -1
  630. package/out/node_modules/.pnpm/postgres-bytea@1.0.1/node_modules/postgres-bytea/index.js +0 -28
  631. package/out/node_modules/.pnpm/postgres-bytea@1.0.1/node_modules/postgres-bytea/index.js.map +0 -1
  632. package/out/node_modules/.pnpm/postgres-date@1.0.7/node_modules/postgres-date/index.js +0 -65
  633. package/out/node_modules/.pnpm/postgres-date@1.0.7/node_modules/postgres-date/index.js.map +0 -1
  634. package/out/node_modules/.pnpm/postgres-interval@1.2.0/node_modules/postgres-interval/index.js +0 -107
  635. package/out/node_modules/.pnpm/postgres-interval@1.2.0/node_modules/postgres-interval/index.js.map +0 -1
  636. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react-jsx-runtime.development.js +0 -696
  637. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react-jsx-runtime.development.js.map +0 -1
  638. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react-jsx-runtime.production.min.js +0 -44
  639. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react-jsx-runtime.production.min.js.map +0 -1
  640. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.development.js +0 -1585
  641. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.development.js.map +0 -1
  642. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.production.min.js +0 -329
  643. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.production.min.js.map +0 -1
  644. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/index.js +0 -13
  645. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/index.js.map +0 -1
  646. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/jsx-runtime.js +0 -13
  647. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/jsx-runtime.js.map +0 -1
  648. package/out/node_modules/.pnpm/solid-js@1.9.13/node_modules/solid-js/dist/server.js +0 -131
  649. package/out/node_modules/.pnpm/solid-js@1.9.13/node_modules/solid-js/dist/server.js.map +0 -1
  650. package/out/node_modules/.pnpm/solid-js@1.9.13/node_modules/solid-js/store/dist/server.js +0 -96
  651. package/out/node_modules/.pnpm/solid-js@1.9.13/node_modules/solid-js/store/dist/server.js.map +0 -1
  652. package/out/node_modules/.pnpm/split2@4.2.0/node_modules/split2/index.js +0 -95
  653. package/out/node_modules/.pnpm/split2@4.2.0/node_modules/split2/index.js.map +0 -1
  654. package/out/node_modules/.pnpm/xtend@4.0.2/node_modules/xtend/mutable.js +0 -18
  655. package/out/node_modules/.pnpm/xtend@4.0.2/node_modules/xtend/mutable.js.map +0 -1
  656. package/out/shared/src/ring-buffer.d.ts +0 -32
  657. package/out/shared/src/ring-buffer.d.ts.map +0 -1
  658. package/out/shared/src/ring-buffer.js +0 -109
  659. package/out/shared/src/ring-buffer.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"flipped-join.js","names":["#parent","#child","#parentKey","#childKey","#relationshipName","#schema","#pushParent","#pushChild","#output","#inprogressChildChange","#fetchBatched","#fetchChunked","#yieldParentWithOverlay","#inprogressChildChangePosition","#pushChildChange"],"sources":["../../../../../zql/src/ivm/flipped-join.ts"],"sourcesContent":["import {assert, unreachable} from '../../../shared/src/asserts.ts';\nimport {binarySearch} from '../../../shared/src/binary-search.ts';\nimport type {CompoundKey, System} from '../../../zero-protocol/src/ast.ts';\nimport type {Row, Value} from '../../../zero-protocol/src/data.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 {constraintsAreCompatible, type Constraint} from './constraint.ts';\nimport type {Node} from './data.ts';\nimport {\n buildJoinConstraint,\n generateWithOverlayNoYield,\n isJoinMatch,\n rowEqualsForCompoundKey,\n} from './join-utils.ts';\nimport {mergeSortedStreams} from './memory-source.ts';\nimport {\n throwOutput,\n type FetchRequest,\n type Input,\n type MultiConstraint,\n type Output,\n} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport {type Stream} from './stream.ts';\n\n/**\n * Maximum number of entries sent in a single batched `parent.fetch`\n * call. Larger child-node sets are split into multiple fetches whose\n * sorted streams are merged in JS.\n *\n * Why bound this:\n * - **Bounded fetch on early termination.** `mergeSortedStreams` primes\n * one row from every chunk before yielding the first output, so all\n * chunks open their cursors up front. Smaller chunks cap the\n * worst-case overfetch when downstream `Take` consumes only a few\n * rows — at chunk N, we may waste up to ~N index seeks before\n * `.return()` propagates.\n * - **Parameter limit.** Well under SQLite's default\n * `SQLITE_MAX_VARIABLE_NUMBER` (32766). Compound keys multiply the\n * parameter count by key length, so we leave headroom.\n *\n * Tested 64/128/256; 256 had the best worst-case across planner suites\n * and perf tests.\n *\n * We should, however, start doing shadow tests of the planner\n * against cloudzero queries.\n */\nconst MULTI_CONSTRAINT_CHUNK_SIZE = 256;\n\n// Mutable test seam — production code reads this via the getter.\nlet multiConstraintChunkSize: number = MULTI_CONSTRAINT_CHUNK_SIZE;\n\nexport function getMultiConstraintChunkSize(): number {\n return multiConstraintChunkSize;\n}\n\n/** Test only. Returns a restore function. */\nexport function setMultiConstraintChunkSizeForTest(size: number): () => void {\n const prev = multiConstraintChunkSize;\n multiConstraintChunkSize = size;\n return () => {\n multiConstraintChunkSize = prev;\n };\n}\n\ntype Args = {\n parent: Input;\n child: Input;\n // The nth key in childKey corresponds to the nth key in parentKey.\n parentKey: CompoundKey;\n childKey: CompoundKey;\n\n relationshipName: string;\n hidden: boolean;\n system: System;\n};\n\n/**\n * An *inner* join which fetches nodes from its child input first and then\n * fetches their related nodes from its parent input. Output nodes are the\n * nodes from parent input (in parent input order), which have at least one\n * related child. These output nodes have a new relationship added to them,\n * which has the name `relationshipName`. The value of the relationship is a\n * stream of related nodes from the child input (in child input order).\n */\nexport class FlippedJoin implements Input {\n readonly #parent: Input;\n readonly #child: Input;\n readonly #parentKey: CompoundKey;\n readonly #childKey: CompoundKey;\n readonly #relationshipName: string;\n readonly #schema: SourceSchema;\n\n #output: Output = throwOutput;\n\n #inprogressChildChange: Change | undefined;\n #inprogressChildChangePosition: Row | undefined;\n\n constructor({\n parent,\n child,\n parentKey,\n childKey,\n relationshipName,\n hidden,\n system,\n }: Args) {\n assert(parent !== child, 'Parent and child must be different operators');\n assert(\n parentKey.length === childKey.length,\n 'The parentKey and childKey keys must have same length',\n );\n this.#parent = parent;\n this.#child = child;\n this.#parentKey = parentKey;\n this.#childKey = childKey;\n this.#relationshipName = relationshipName;\n\n const parentSchema = parent.getSchema();\n const childSchema = child.getSchema();\n this.#schema = {\n ...parentSchema,\n relationships: {\n ...parentSchema.relationships,\n [relationshipName]: {\n ...childSchema,\n isHidden: hidden,\n system,\n },\n },\n };\n\n parent.setOutput({\n push: (change: Change) => this.#pushParent(change),\n });\n child.setOutput({\n push: (change: Change) => this.#pushChild(change),\n });\n }\n\n destroy(): void {\n this.#child.destroy();\n this.#parent.destroy();\n }\n\n setOutput(output: Output): void {\n this.#output = output;\n }\n\n getSchema(): SourceSchema {\n return this.#schema;\n }\n\n *fetch(req: FetchRequest): Stream<Node | 'yield'> {\n // Translate constraints for the parent on parts of the join key to\n // constraints for the child.\n const childConstraint: Record<string, Value> = {};\n let hasChildConstraint = false;\n if (req.constraint) {\n for (const [key, value] of Object.entries(req.constraint)) {\n const index = this.#parentKey.indexOf(key);\n if (index !== -1) {\n hasChildConstraint = true;\n childConstraint[this.#childKey[index]] = value;\n }\n }\n }\n\n const childNodes: Node[] = [];\n for (const node of this.#child.fetch(\n hasChildConstraint ? {constraint: childConstraint} : {},\n )) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n childNodes.push(node);\n }\n\n // FlippedJoin's split-push change overlay logic is largely\n // the same as Join's with the exception of remove. For remove,\n // the change is undone here, and then re-applied to parents with order\n // less than or equal to change.position below. This is necessary\n // because if the removed node was the last related child, the\n // related parents with position greater than change.position\n // (which should not yet have the node removed), would not even\n // be fetched here, and would be absent from the output all together.\n if (this.#inprogressChildChange?.[ChangeIndex.TYPE] === ChangeType.REMOVE) {\n const removedNode = this.#inprogressChildChange[ChangeIndex.NODE];\n const compare = this.#child.getSchema().compareRows;\n const insertPos = binarySearch(childNodes.length, i =>\n compare(removedNode.row, childNodes[i].row),\n );\n childNodes.splice(insertPos, 0, removedNode);\n }\n\n yield* this.#fetchBatched(req, childNodes);\n }\n\n /**\n * Fetches parents for `childNodes` in batched calls, using\n * `multiConstraint` so the source can issue one query per chunk (e.g.\n * SQL `IN` with index-aware seek) instead of N per-child cursors.\n *\n * Multi-constraint values are split into chunks of `CHUNK_SIZE`, so\n * SQL `IN` lists stay bounded — predictable plans, statement-cache\n * hits across calls of the same chunk size, well below SQLite's\n * parameter limit.\n *\n * Within each chunk, the source returns parents in `compareRows` order.\n * Across chunks, we merge with `mergeSortedStreams` so the overall\n * stream is also in order. Note: the merge primes one row from every\n * chunk before yielding the first output, so all chunks open their\n * cursors up front. Early termination downstream then prevents any\n * further work on un-advanced chunks (cursors get `.return()`'d via\n * `mergeSortedStreams`'s finally block).\n *\n * Replaces the previous split between `#fetchMergeSort` and\n * `#fetchQuicksort`. The unique-vs-not distinction is no longer needed:\n * the source handles cardinality (single index seek for each value) and\n * ordering (SQL `ORDER BY` / index walk).\n */\n *#fetchBatched(\n req: FetchRequest,\n childNodes: Node[],\n ): Stream<Node | 'yield'> {\n const parentReqConstraint = req.constraint;\n const parentKey = this.#parentKey;\n const childKey = this.#childKey;\n\n // Build (deduped) multi-constraint and a key→child-indexes map. Same\n // parent-key value across multiple children groups them together.\n const computedMulti: Constraint[] = [];\n const childIndexesByKey = new Map<string, number[]>();\n for (let i = 0; i < childNodes.length; i++) {\n const constraintFromChild = buildJoinConstraint(\n childNodes[i].row,\n childKey,\n parentKey,\n );\n if (\n !constraintFromChild ||\n (parentReqConstraint &&\n !constraintsAreCompatible(constraintFromChild, parentReqConstraint))\n ) {\n continue;\n }\n const key = canonicalKey(constraintFromChild, parentKey);\n const existing = childIndexesByKey.get(key);\n if (existing === undefined) {\n childIndexesByKey.set(key, [i]);\n computedMulti.push(constraintFromChild);\n } else {\n existing.push(i);\n }\n }\n\n if (computedMulti.length === 0) {\n return;\n }\n\n // Source returns parents in compareRows order within each chunk.\n // Merge across chunks to yield a globally ordered stream.\n const compareRows = this.#schema.compareRows;\n const compare: (a: Node, b: Node) => number = req.reverse\n ? (a, b) => compareRows(b.row, a.row)\n : (a, b) => compareRows(a.row, b.row);\n\n // Append our computed multi to whatever req.multiConstraints already\n // contained — chained FlippedJoins each contribute one entry, so the\n // source ANDs them all (e.g. `assigneeID IN (…) AND creatorID IN (…)`).\n const incoming = req.multiConstraints ?? [];\n const parentStream =\n computedMulti.length <= multiConstraintChunkSize\n ? this.#parent.fetch({\n ...req,\n multiConstraints: [...incoming, computedMulti],\n })\n : this.#fetchChunked(req, incoming, computedMulti, compare);\n\n for (const node of parentStream) {\n if (node === 'yield') {\n yield 'yield';\n continue;\n }\n const key = canonicalKey(node.row, parentKey);\n const idxs = childIndexesByKey.get(key);\n if (idxs === undefined) {\n // This row's parent-key doesn't match any of our computed\n // multi-constraint entries. Happens when our parent is an\n // intermediate operator (e.g. a chained FlippedJoin) that passes\n // multiConstraints through unchanged instead of filtering — see\n // FetchRequest.multiConstraints contract. The lookup miss here\n // performs the required filter, so just skip the row.\n continue;\n }\n // Children retain their original input order within the group\n // because we appended to `idxs` in iteration order.\n const relatedChildNodes: Node[] = idxs.map(i => childNodes[i]);\n yield* this.#yieldParentWithOverlay(node, relatedChildNodes);\n }\n }\n\n *#fetchChunked(\n req: FetchRequest,\n incomingMultis: readonly MultiConstraint[],\n computedMulti: MultiConstraint,\n compare: (a: Node, b: Node) => number,\n ): Stream<Node | 'yield'> {\n const chunkStreams: Stream<Node | 'yield'>[] = [];\n for (let i = 0; i < computedMulti.length; i += multiConstraintChunkSize) {\n chunkStreams.push(\n this.#parent.fetch({\n ...req,\n multiConstraints: [\n ...incomingMultis,\n computedMulti.slice(i, i + multiConstraintChunkSize),\n ],\n }),\n );\n }\n yield* mergeSortedStreams(chunkStreams, compare);\n }\n\n *#yieldParentWithOverlay(\n minParentNode: Node,\n relatedChildNodes: Node[],\n ): Stream<Node> {\n let overlaidRelatedChildNodes = relatedChildNodes;\n if (\n this.#inprogressChildChange &&\n this.#inprogressChildChangePosition &&\n isJoinMatch(\n this.#inprogressChildChange[ChangeIndex.NODE].row,\n this.#childKey,\n minParentNode.row,\n this.#parentKey,\n )\n ) {\n const hasInprogressChildChangeBeenPushedForMinParentNode =\n this.#parent\n .getSchema()\n .compareRows(\n minParentNode.row,\n this.#inprogressChildChangePosition,\n ) <= 0;\n if (this.#inprogressChildChange[ChangeIndex.TYPE] === ChangeType.REMOVE) {\n if (hasInprogressChildChangeBeenPushedForMinParentNode) {\n // Remove from relatedChildNodes since the removed child\n // was inserted into childNodes above.\n overlaidRelatedChildNodes = relatedChildNodes.filter(\n n => n !== this.#inprogressChildChange?.[ChangeIndex.NODE],\n );\n }\n } else if (!hasInprogressChildChangeBeenPushedForMinParentNode) {\n overlaidRelatedChildNodes = [\n ...generateWithOverlayNoYield(\n relatedChildNodes,\n this.#inprogressChildChange,\n this.#child.getSchema(),\n ),\n ];\n }\n }\n\n // yield node if after the overlay it still has relationship nodes\n if (overlaidRelatedChildNodes.length > 0) {\n yield {\n ...minParentNode,\n relationships: {\n ...minParentNode.relationships,\n [this.#relationshipName]: () => overlaidRelatedChildNodes,\n },\n };\n }\n }\n\n *#pushChild(change: Change): Stream<'yield'> {\n switch (change[ChangeIndex.TYPE]) {\n case ChangeType.ADD:\n case ChangeType.REMOVE:\n yield* this.#pushChildChange(change);\n break;\n case ChangeType.EDIT: {\n assert(\n rowEqualsForCompoundKey(\n change[ChangeIndex.OLD_NODE].row,\n change[ChangeIndex.NODE].row,\n this.#childKey,\n ),\n `Child edit must not change relationship.`,\n );\n yield* this.#pushChildChange(change, true);\n break;\n }\n case ChangeType.CHILD:\n yield* this.#pushChildChange(change, true);\n break;\n }\n }\n\n *#pushChildChange(change: Change, exists?: boolean): Stream<'yield'> {\n this.#inprogressChildChange = change;\n this.#inprogressChildChangePosition = undefined;\n try {\n const constraint = buildJoinConstraint(\n change[ChangeIndex.NODE].row,\n this.#childKey,\n this.#parentKey,\n );\n const parentNodeStream = constraint\n ? this.#parent.fetch({constraint})\n : [];\n for (const parentNode of parentNodeStream) {\n if (parentNode === 'yield') {\n yield 'yield';\n continue;\n }\n this.#inprogressChildChange = change;\n this.#inprogressChildChangePosition = parentNode.row;\n const childNodeStream = () => {\n const constraint = buildJoinConstraint(\n parentNode.row,\n this.#parentKey,\n this.#childKey,\n );\n return constraint ? this.#child.fetch({constraint}) : [];\n };\n if (!exists) {\n for (const childNode of childNodeStream()) {\n if (childNode === 'yield') {\n yield 'yield';\n continue;\n }\n if (\n this.#child\n .getSchema()\n .compareRows(childNode.row, change[ChangeIndex.NODE].row) !== 0\n ) {\n exists = true;\n break;\n }\n }\n }\n if (exists) {\n yield* this.#output.push(\n makeChildChange(\n {\n ...parentNode,\n relationships: {\n ...parentNode.relationships,\n [this.#relationshipName]: childNodeStream,\n },\n },\n {\n relationshipName: this.#relationshipName,\n change,\n },\n ),\n this,\n );\n } else {\n const newNode = {\n ...parentNode,\n relationships: {\n ...parentNode.relationships,\n [this.#relationshipName]: () => [change[ChangeIndex.NODE]],\n },\n };\n yield* this.#output.push(\n change[ChangeIndex.TYPE] === ChangeType.ADD\n ? makeAddChange(newNode)\n : makeRemoveChange(newNode),\n this,\n );\n }\n }\n } finally {\n this.#inprogressChildChange = undefined;\n }\n }\n\n *#pushParent(change: Change): Stream<'yield'> {\n const childNodeStream = (node: Node) => () => {\n const constraint = buildJoinConstraint(\n node.row,\n this.#parentKey,\n this.#childKey,\n );\n return constraint ? this.#child.fetch({constraint}) : [];\n };\n\n const flip = (node: Node) => ({\n ...node,\n relationships: {\n ...node.relationships,\n [this.#relationshipName]: childNodeStream(node),\n },\n });\n\n // If no related child don't push as this is an inner join.\n let hasRelatedChild = false;\n for (const node of childNodeStream(change[ChangeIndex.NODE])()) {\n if (node === 'yield') {\n yield 'yield';\n continue;\n } else {\n hasRelatedChild = true;\n break;\n }\n }\n if (!hasRelatedChild) {\n return;\n }\n\n switch (change[ChangeIndex.TYPE]) {\n case ChangeType.ADD:\n yield* this.#output.push(\n makeAddChange(flip(change[ChangeIndex.NODE])),\n this,\n );\n break;\n case ChangeType.REMOVE:\n yield* this.#output.push(\n makeRemoveChange(flip(change[ChangeIndex.NODE])),\n this,\n );\n break;\n case ChangeType.CHILD: {\n yield* this.#output.push(\n makeChildChange(\n flip(change[ChangeIndex.NODE]),\n change[ChangeIndex.CHILD_DATA],\n ),\n this,\n );\n break;\n }\n case ChangeType.EDIT: {\n assert(\n rowEqualsForCompoundKey(\n change[ChangeIndex.OLD_NODE].row,\n change[ChangeIndex.NODE].row,\n this.#parentKey,\n ),\n `Parent edit must not change relationship.`,\n );\n yield* this.#output.push(\n makeEditChange(\n flip(change[ChangeIndex.NODE]),\n flip(change[ChangeIndex.OLD_NODE]),\n ),\n this,\n );\n break;\n }\n default:\n unreachable(change);\n }\n }\n}\n\n// Test seam with a widened record type — canonicalValue handles bigint\n// at runtime (zqlite's safeIntegers) but `Value` doesn't list it.\nexport function canonicalKeyForTest(\n record: Record<string, Value | bigint | undefined>,\n keys: CompoundKey,\n): string {\n return canonicalKey(record as Record<string, Value | undefined>, keys);\n}\n\n/**\n * Canonical string key over `keys` of `record`, used by `#fetchBatched`\n * both to dedupe `multiConstraint` entries (record = Constraint) and to\n * map each returned parent row back to the children that referenced its\n * parent-key tuple (record = Row).\n */\nfunction canonicalKey(\n record: Record<string, Value | undefined>,\n keys: CompoundKey,\n): string {\n if (keys.length === 1) {\n return canonicalValue(record[keys[0]]);\n }\n let s = '';\n for (let i = 0; i < keys.length; i++) {\n if (i > 0) s += '\\x00';\n s += canonicalValue(record[keys[i]]);\n }\n return s;\n}\n\nfunction canonicalValue(v: Value | bigint | undefined): string {\n // Tag by type so we don't conflate e.g. `1` (number) with `\"1\"` (string).\n // Bigint shows up at runtime when zqlite's safeIntegers is on, even\n // though the static `Value` type doesn't list it.\n if (v === null || v === undefined) return 'n';\n const t = typeof v;\n if (t === 'string') return 's' + (v as string);\n if (t === 'number') return 'd' + (v as number);\n if (t === 'bigint') return 'b' + (v as bigint).toString();\n if (t === 'boolean') return v ? 't' : 'f';\n return 'j' + JSON.stringify(v);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"flipped-join.js","names":["#parent","#child","#parentKey","#childKey","#relationshipName","#schema","#parentKeyIsUnique","#pushParent","#pushChild","#output","#inprogressChildChange","#fetchQuicksort","#fetchMergeSort","#yieldParentWithOverlay","#inprogressChildChangePosition","#pushChildChange"],"sources":["../../../../../zql/src/ivm/flipped-join.ts"],"sourcesContent":["import {assert, unreachable} from '../../../shared/src/asserts.ts';\nimport {binarySearch} from '../../../shared/src/binary-search.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport type {CompoundKey, System} from '../../../zero-protocol/src/ast.ts';\nimport type {Row, Value} from '../../../zero-protocol/src/data.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 {\n constraintsAreCompatible,\n keyMatchesPrimaryKey,\n type Constraint,\n} from './constraint.ts';\nimport type {Node} from './data.ts';\nimport {\n buildJoinConstraint,\n generateWithOverlayNoYield,\n isJoinMatch,\n rowEqualsForCompoundKey,\n} from './join-utils.ts';\nimport {mergeSortedStreams} from './memory-source.ts';\nimport {\n throwOutput,\n type FetchRequest,\n type Input,\n type Output,\n} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport {type Stream} from './stream.ts';\n\ntype Args = {\n parent: Input;\n child: Input;\n // The nth key in childKey corresponds to the nth key in parentKey.\n parentKey: CompoundKey;\n childKey: CompoundKey;\n\n relationshipName: string;\n hidden: boolean;\n system: System;\n};\n\n/**\n * An *inner* join which fetches nodes from its child input first and then\n * fetches their related nodes from its parent input. Output nodes are the\n * nodes from parent input (in parent input order), which have at least one\n * related child. These output nodes have a new relationship added to them,\n * which has the name `relationshipName`. The value of the relationship is a\n * stream of related nodes from the child input (in child input order).\n */\nexport class FlippedJoin implements Input {\n readonly #parent: Input;\n readonly #child: Input;\n readonly #parentKey: CompoundKey;\n readonly #childKey: CompoundKey;\n readonly #relationshipName: string;\n readonly #schema: SourceSchema;\n readonly #parentKeyIsUnique: boolean;\n\n #output: Output = throwOutput;\n\n #inprogressChildChange: Change | undefined;\n #inprogressChildChangePosition: Row | undefined;\n\n constructor({\n parent,\n child,\n parentKey,\n childKey,\n relationshipName,\n hidden,\n system,\n }: Args) {\n assert(parent !== child, 'Parent and child must be different operators');\n assert(\n parentKey.length === childKey.length,\n 'The parentKey and childKey keys must have same length',\n );\n this.#parent = parent;\n this.#child = child;\n this.#parentKey = parentKey;\n this.#childKey = childKey;\n this.#relationshipName = relationshipName;\n\n const parentSchema = parent.getSchema();\n const childSchema = child.getSchema();\n this.#parentKeyIsUnique =\n keyMatchesPrimaryKey(parentKey, parentSchema.primaryKey) ||\n (parentSchema.uniqueIndexes?.some(idx =>\n keyMatchesPrimaryKey(parentKey, idx),\n ) ??\n false);\n this.#schema = {\n ...parentSchema,\n relationships: {\n ...parentSchema.relationships,\n [relationshipName]: {\n ...childSchema,\n isHidden: hidden,\n system,\n },\n },\n };\n\n parent.setOutput({\n push: (change: Change) => this.#pushParent(change),\n });\n child.setOutput({\n push: (change: Change) => this.#pushChild(change),\n });\n }\n\n destroy(): void {\n this.#child.destroy();\n this.#parent.destroy();\n }\n\n setOutput(output: Output): void {\n this.#output = output;\n }\n\n getSchema(): SourceSchema {\n return this.#schema;\n }\n\n *fetch(req: FetchRequest): Stream<Node | 'yield'> {\n // Translate constraints for the parent on parts of the join key to\n // constraints for the child.\n const childConstraint: Record<string, Value> = {};\n let hasChildConstraint = false;\n if (req.constraint) {\n for (const [key, value] of Object.entries(req.constraint)) {\n const index = this.#parentKey.indexOf(key);\n if (index !== -1) {\n hasChildConstraint = true;\n childConstraint[this.#childKey[index]] = value;\n }\n }\n }\n\n const childNodes: Node[] = [];\n for (const node of this.#child.fetch(\n hasChildConstraint ? {constraint: childConstraint} : {},\n )) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n childNodes.push(node);\n }\n\n // FlippedJoin's split-push change overlay logic is largely\n // the same as Join's with the exception of remove. For remove,\n // the change is undone here, and then re-applied to parents with order\n // less than or equal to change.position below. This is necessary\n // because if the removed node was the last related child, the\n // related parents with position greater than change.position\n // (which should not yet have the node removed), would not even\n // be fetched here, and would be absent from the output all together.\n if (this.#inprogressChildChange?.[ChangeIndex.TYPE] === ChangeType.REMOVE) {\n const removedNode = this.#inprogressChildChange[ChangeIndex.NODE];\n const compare = this.#child.getSchema().compareRows;\n const insertPos = binarySearch(childNodes.length, i =>\n compare(removedNode.row, childNodes[i].row),\n );\n childNodes.splice(insertPos, 0, removedNode);\n }\n\n if (this.#parentKeyIsUnique) {\n yield* this.#fetchQuicksort(req, childNodes);\n } else {\n yield* this.#fetchMergeSort(req, childNodes);\n }\n }\n\n // When parentKey matches a unique index on the parent (primary or\n // otherwise) each child -> parent fetch returns at most one row, so the\n // merge-sort degenerates to N simultaneous prepared-statement iterators\n // each holding a single-row cursor. Instead, fetch sequentially (letting\n // the statement cache reuse a single prepared statement) and sort the\n // resulting parents into order.\n *#fetchQuicksort(\n req: FetchRequest,\n childNodes: Node[],\n ): Stream<Node | 'yield'> {\n const pairs: {childNode: Node; parentNode: Node}[] = [];\n for (const childNode of childNodes) {\n const constraintFromChild = buildJoinConstraint(\n childNode.row,\n this.#childKey,\n this.#parentKey,\n );\n if (\n !constraintFromChild ||\n (req.constraint &&\n !constraintsAreCompatible(constraintFromChild, req.constraint))\n ) {\n continue;\n }\n const stream = this.#parent.fetch({\n ...req,\n constraint: {\n ...req.constraint,\n ...constraintFromChild,\n },\n });\n // parentKey matches a unique index, so this fetch returns at most\n // one row under the Source contract. Iterate to completion rather\n // than breaking to preserve yield propagation and to avoid silently\n // changing behavior if a source ever returns more.\n for (const node of stream) {\n if (node === 'yield') {\n yield 'yield';\n continue;\n }\n pairs.push({childNode, parentNode: node});\n }\n }\n\n const compareRows = this.#schema.compareRows;\n const dir = req.reverse ? -1 : 1;\n pairs.sort((a, b) => compareRows(a.parentNode.row, b.parentNode.row) * dir);\n\n // Group consecutive pairs with equal parent rows. Array.sort is stable,\n // and childNodes was already in child order, so children within each\n // group retain child order.\n let i = 0;\n while (i < pairs.length) {\n const minParentNode = pairs[i].parentNode;\n const relatedChildNodes: Node[] = [];\n while (\n i < pairs.length &&\n compareRows(pairs[i].parentNode.row, minParentNode.row) === 0\n ) {\n relatedChildNodes.push(pairs[i].childNode);\n i++;\n }\n yield* this.#yieldParentWithOverlay(minParentNode, relatedChildNodes);\n }\n }\n\n *#fetchMergeSort(\n req: FetchRequest,\n childNodes: Node[],\n ): Stream<Node | 'yield'> {\n // Group children by parent-key value so children sharing a value\n // share one fetch (and one cursor). Without this, two children with\n // the same parent-key value would each open their own iterator that\n // re-fetches the same parent rows — wasted IO.\n const parentKey = this.#parentKey;\n const computedKeys: Constraint[] = [];\n const childIndexesByKey = new Map<string, number[]>();\n for (let i = 0; i < childNodes.length; i++) {\n const constraintFromChild = buildJoinConstraint(\n childNodes[i].row,\n this.#childKey,\n parentKey,\n );\n if (\n !constraintFromChild ||\n (req.constraint &&\n !constraintsAreCompatible(constraintFromChild, req.constraint))\n ) {\n continue;\n }\n const key = canonicalKey(constraintFromChild, parentKey);\n const existing = childIndexesByKey.get(key);\n if (existing === undefined) {\n childIndexesByKey.set(key, [i]);\n computedKeys.push(constraintFromChild);\n } else {\n existing.push(i);\n }\n }\n\n if (computedKeys.length === 0) {\n return;\n }\n\n const compareRows = this.#schema.compareRows;\n const compare: (a: Node, b: Node) => number = req.reverse\n ? (a, b) => compareRows(b.row, a.row)\n : (a, b) => compareRows(a.row, b.row);\n\n // One stream per unique parent-key value. Each stream returns its\n // matching parent rows in compareRows order; the heap merges them\n // into a globally ordered stream. Distinct rows can't compare equal\n // (compareRows includes the primary key), so no tie handling needed\n // — every emit maps back to exactly one entry in childIndexesByKey.\n const streams: Stream<Node | 'yield'>[] = computedKeys.map(c =>\n this.#parent.fetch({\n ...req,\n constraint: req.constraint ? {...req.constraint, ...c} : c,\n }),\n );\n\n for (const node of mergeSortedStreams(streams, compare)) {\n if (node === 'yield') {\n yield 'yield';\n continue;\n }\n // Every fetched parent row matches the constraint for one entry\n // in `computedKeys`, whose canonical key was inserted into the\n // map — so the lookup is guaranteed to hit. Children retain\n // their original input order within the group because we\n // appended to the indexes array in iteration order.\n const idxs = must(\n childIndexesByKey.get(canonicalKey(node.row, parentKey)),\n );\n const relatedChildNodes: Node[] = idxs.map(i => childNodes[i]);\n yield* this.#yieldParentWithOverlay(node, relatedChildNodes);\n }\n }\n\n *#yieldParentWithOverlay(\n minParentNode: Node,\n relatedChildNodes: Node[],\n ): Stream<Node> {\n let overlaidRelatedChildNodes = relatedChildNodes;\n if (\n this.#inprogressChildChange &&\n this.#inprogressChildChangePosition &&\n isJoinMatch(\n this.#inprogressChildChange[ChangeIndex.NODE].row,\n this.#childKey,\n minParentNode.row,\n this.#parentKey,\n )\n ) {\n const hasInprogressChildChangeBeenPushedForMinParentNode =\n this.#parent\n .getSchema()\n .compareRows(\n minParentNode.row,\n this.#inprogressChildChangePosition,\n ) <= 0;\n if (this.#inprogressChildChange[ChangeIndex.TYPE] === ChangeType.REMOVE) {\n if (hasInprogressChildChangeBeenPushedForMinParentNode) {\n // Remove from relatedChildNodes since the removed child\n // was inserted into childNodes above.\n overlaidRelatedChildNodes = relatedChildNodes.filter(\n n => n !== this.#inprogressChildChange?.[ChangeIndex.NODE],\n );\n }\n } else if (!hasInprogressChildChangeBeenPushedForMinParentNode) {\n overlaidRelatedChildNodes = [\n ...generateWithOverlayNoYield(\n relatedChildNodes,\n this.#inprogressChildChange,\n this.#child.getSchema(),\n ),\n ];\n }\n }\n\n // yield node if after the overlay it still has relationship nodes\n if (overlaidRelatedChildNodes.length > 0) {\n yield {\n ...minParentNode,\n relationships: {\n ...minParentNode.relationships,\n [this.#relationshipName]: () => overlaidRelatedChildNodes,\n },\n };\n }\n }\n\n *#pushChild(change: Change): Stream<'yield'> {\n switch (change[ChangeIndex.TYPE]) {\n case ChangeType.ADD:\n case ChangeType.REMOVE:\n yield* this.#pushChildChange(change);\n break;\n case ChangeType.EDIT: {\n assert(\n rowEqualsForCompoundKey(\n change[ChangeIndex.OLD_NODE].row,\n change[ChangeIndex.NODE].row,\n this.#childKey,\n ),\n `Child edit must not change relationship.`,\n );\n yield* this.#pushChildChange(change, true);\n break;\n }\n case ChangeType.CHILD:\n yield* this.#pushChildChange(change, true);\n break;\n }\n }\n\n *#pushChildChange(change: Change, exists?: boolean): Stream<'yield'> {\n this.#inprogressChildChange = change;\n this.#inprogressChildChangePosition = undefined;\n try {\n const constraint = buildJoinConstraint(\n change[ChangeIndex.NODE].row,\n this.#childKey,\n this.#parentKey,\n );\n const parentNodeStream = constraint\n ? this.#parent.fetch({constraint})\n : [];\n for (const parentNode of parentNodeStream) {\n if (parentNode === 'yield') {\n yield 'yield';\n continue;\n }\n this.#inprogressChildChange = change;\n this.#inprogressChildChangePosition = parentNode.row;\n const childNodeStream = () => {\n const constraint = buildJoinConstraint(\n parentNode.row,\n this.#parentKey,\n this.#childKey,\n );\n return constraint ? this.#child.fetch({constraint}) : [];\n };\n if (!exists) {\n for (const childNode of childNodeStream()) {\n if (childNode === 'yield') {\n yield 'yield';\n continue;\n }\n if (\n this.#child\n .getSchema()\n .compareRows(childNode.row, change[ChangeIndex.NODE].row) !== 0\n ) {\n exists = true;\n break;\n }\n }\n }\n if (exists) {\n yield* this.#output.push(\n makeChildChange(\n {\n ...parentNode,\n relationships: {\n ...parentNode.relationships,\n [this.#relationshipName]: childNodeStream,\n },\n },\n {\n relationshipName: this.#relationshipName,\n change,\n },\n ),\n this,\n );\n } else {\n const newNode = {\n ...parentNode,\n relationships: {\n ...parentNode.relationships,\n [this.#relationshipName]: () => [change[ChangeIndex.NODE]],\n },\n };\n yield* this.#output.push(\n change[ChangeIndex.TYPE] === ChangeType.ADD\n ? makeAddChange(newNode)\n : makeRemoveChange(newNode),\n this,\n );\n }\n }\n } finally {\n this.#inprogressChildChange = undefined;\n }\n }\n\n *#pushParent(change: Change): Stream<'yield'> {\n const childNodeStream = (node: Node) => () => {\n const constraint = buildJoinConstraint(\n node.row,\n this.#parentKey,\n this.#childKey,\n );\n return constraint ? this.#child.fetch({constraint}) : [];\n };\n\n const flip = (node: Node) => ({\n ...node,\n relationships: {\n ...node.relationships,\n [this.#relationshipName]: childNodeStream(node),\n },\n });\n\n // If no related child don't push as this is an inner join.\n let hasRelatedChild = false;\n for (const node of childNodeStream(change[ChangeIndex.NODE])()) {\n if (node === 'yield') {\n yield 'yield';\n continue;\n } else {\n hasRelatedChild = true;\n break;\n }\n }\n if (!hasRelatedChild) {\n return;\n }\n\n switch (change[ChangeIndex.TYPE]) {\n case ChangeType.ADD:\n yield* this.#output.push(\n makeAddChange(flip(change[ChangeIndex.NODE])),\n this,\n );\n break;\n case ChangeType.REMOVE:\n yield* this.#output.push(\n makeRemoveChange(flip(change[ChangeIndex.NODE])),\n this,\n );\n break;\n case ChangeType.CHILD: {\n yield* this.#output.push(\n makeChildChange(\n flip(change[ChangeIndex.NODE]),\n change[ChangeIndex.CHILD_DATA],\n ),\n this,\n );\n break;\n }\n case ChangeType.EDIT: {\n assert(\n rowEqualsForCompoundKey(\n change[ChangeIndex.OLD_NODE].row,\n change[ChangeIndex.NODE].row,\n this.#parentKey,\n ),\n `Parent edit must not change relationship.`,\n );\n yield* this.#output.push(\n makeEditChange(\n flip(change[ChangeIndex.NODE]),\n flip(change[ChangeIndex.OLD_NODE]),\n ),\n this,\n );\n break;\n }\n default:\n unreachable(change);\n }\n }\n}\n\n/**\n * Canonical string key over `keys` of `record`, used by `#fetchMergeSort`\n * both to dedupe per-child fetches and to map each returned parent row\n * back to the children that referenced its parent-key tuple.\n *\n * Exported for testing.\n */\nexport function canonicalKey(\n record: Record<string, Value | undefined>,\n keys: CompoundKey,\n): string {\n if (keys.length === 1) {\n return canonicalValue(record[keys[0]]);\n }\n let s = '';\n for (let i = 0; i < keys.length; i++) {\n if (i > 0) s += '\\x00';\n s += canonicalValue(record[keys[i]]);\n }\n return s;\n}\n\nfunction canonicalValue(v: Value | bigint | undefined): string {\n // Tag by type so we don't conflate e.g. `1` (number) with `\"1\"` (string).\n // Bigint shows up at runtime when zqlite's safeIntegers is on, even\n // though the static `Value` type doesn't list it.\n if (v === null || v === undefined) return 'n';\n const t = typeof v;\n if (t === 'string') return 's' + (v as string);\n if (t === 'number') return 'd' + (v as number);\n if (t === 'bigint') return 'b' + (v as bigint).toString();\n if (t === 'boolean') return v ? 't' : 'f';\n return 'j' + JSON.stringify(v);\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"join-utils.js","names":[],"sources":["../../../../../zql/src/ivm/join-utils.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport type {CompoundKey} from '../../../zero-protocol/src/ast.ts';\nimport type {Row, Value} from '../../../zero-protocol/src/data.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport {ChangeType} from './change-type.ts';\nimport type {Change} from './change.ts';\nimport {compareValues, valuesEqual, type Node} from './data.ts';\nimport type {SourceSchema} from './schema.ts';\nimport type {Stream} from './stream.ts';\n\nexport function generateWithOverlayNoYield(\n stream: Stream<Node>,\n overlay: Change,\n schema: SourceSchema,\n): Stream<Node> {\n return generateWithOverlay(stream, overlay, schema) as Stream<Node>;\n}\n\nexport function* generateWithOverlay(\n stream: Stream<Node | 'yield'>,\n overlay: Change,\n schema: SourceSchema,\n): Stream<Node | 'yield'> {\n let applied = false;\n let editOldApplied = false;\n let editNewApplied = false;\n for (const node of stream) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n let yieldNode = true;\n if (!applied) {\n switch (overlay[ChangeIndex.TYPE]) {\n case ChangeType.ADD: {\n if (\n schema.compareRows(overlay[ChangeIndex.NODE].row, node.row) === 0\n ) {\n applied = true;\n yieldNode = false;\n }\n break;\n }\n case ChangeType.REMOVE: {\n if (schema.compareRows(overlay[ChangeIndex.NODE].row, node.row) < 0) {\n applied = true;\n yield overlay[ChangeIndex.NODE];\n }\n break;\n }\n case ChangeType.EDIT: {\n if (\n !editOldApplied &&\n schema.compareRows(overlay[ChangeIndex.OLD_NODE].row, node.row) < 0\n ) {\n editOldApplied = true;\n if (editNewApplied) {\n applied = true;\n }\n yield overlay[ChangeIndex.OLD_NODE];\n }\n if (\n !editNewApplied &&\n schema.compareRows(overlay[ChangeIndex.NODE].row, node.row) === 0\n ) {\n editNewApplied = true;\n if (editOldApplied) {\n applied = true;\n }\n yieldNode = false;\n }\n break;\n }\n case ChangeType.CHILD: {\n if (\n schema.compareRows(overlay[ChangeIndex.NODE].row, node.row) === 0\n ) {\n applied = true;\n yield {\n row: node.row,\n relationships: {\n ...node.relationships,\n [overlay[ChangeIndex.CHILD_DATA].relationshipName]: () =>\n generateWithOverlay(\n node.relationships[\n overlay[ChangeIndex.CHILD_DATA].relationshipName\n ](),\n overlay[ChangeIndex.CHILD_DATA].change,\n schema.relationships[\n overlay[ChangeIndex.CHILD_DATA].relationshipName\n ],\n ),\n },\n };\n yieldNode = false;\n }\n break;\n }\n }\n }\n if (yieldNode) {\n yield node;\n }\n }\n if (!applied) {\n if (overlay[ChangeIndex.TYPE] === ChangeType.REMOVE) {\n applied = true;\n yield overlay[ChangeIndex.NODE];\n } else if (overlay[ChangeIndex.TYPE] === ChangeType.EDIT) {\n assert(\n editNewApplied,\n 'edit overlay: new node must be applied before old node',\n );\n editOldApplied = true;\n applied = true;\n yield overlay[ChangeIndex.OLD_NODE];\n }\n }\n\n assert(\n applied,\n 'overlayGenerator: overlay was never applied to any fetched node',\n );\n}\n\nexport function generateWithOverlayNoYieldUnordered(\n stream: Stream<Node>,\n overlay: Change,\n schema: SourceSchema,\n): Stream<Node> {\n return generateWithOverlayUnordered(stream, overlay, schema) as Stream<Node>;\n}\n\nexport function* generateWithOverlayUnordered(\n stream: Stream<Node | 'yield'>,\n overlay: Change,\n schema: SourceSchema,\n): Stream<Node | 'yield'> {\n // Eager inject\n if (overlay[ChangeIndex.TYPE] === ChangeType.REMOVE) {\n yield overlay[ChangeIndex.NODE];\n } else if (overlay[ChangeIndex.TYPE] === ChangeType.EDIT) {\n yield overlay[ChangeIndex.OLD_NODE];\n }\n\n // Stream with inline suppress\n let suppressed = false;\n for (const node of stream) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n if (!suppressed) {\n if (\n overlay[ChangeIndex.TYPE] === ChangeType.ADD ||\n overlay[ChangeIndex.TYPE] === ChangeType.EDIT\n ) {\n if (\n rowEqualsForCompoundKey(\n overlay[ChangeIndex.NODE].row,\n node.row,\n schema.primaryKey,\n )\n ) {\n suppressed = true;\n continue;\n }\n }\n if (overlay[ChangeIndex.TYPE] === ChangeType.CHILD) {\n if (\n rowEqualsForCompoundKey(\n overlay[ChangeIndex.NODE].row,\n node.row,\n schema.primaryKey,\n )\n ) {\n suppressed = true;\n yield {\n row: node.row,\n relationships: {\n ...node.relationships,\n [overlay[ChangeIndex.CHILD_DATA].relationshipName]: () =>\n generateWithOverlay(\n node.relationships[\n overlay[ChangeIndex.CHILD_DATA].relationshipName\n ](),\n overlay[ChangeIndex.CHILD_DATA].change,\n schema.relationships[\n overlay[ChangeIndex.CHILD_DATA].relationshipName\n ],\n ),\n },\n };\n continue;\n }\n }\n }\n yield node;\n }\n assert(\n suppressed || overlay[ChangeIndex.TYPE] === ChangeType.REMOVE,\n 'overlayGenerator: overlay was never applied to any fetched node',\n );\n}\n\nexport function rowEqualsForCompoundKey(\n a: Row,\n b: Row,\n key: CompoundKey,\n): boolean {\n for (let i = 0; i < key.length; i++) {\n if (compareValues(a[key[i]], b[key[i]]) !== 0) {\n return false;\n }\n }\n return true;\n}\n\nexport function isJoinMatch(\n parent: Row,\n parentKey: CompoundKey,\n child: Row,\n childKey: CompoundKey,\n) {\n for (let i = 0; i < parentKey.length; i++) {\n if (!valuesEqual(parent[parentKey[i]], child[childKey[i]])) {\n return false;\n }\n }\n return true;\n}\n\n/**\n * Builds a constraint object by mapping values from `sourceRow` using `sourceKey`\n * to keys specified by `targetKey`. Returns `undefined` if any source value is `null`,\n * since null foreign keys cannot match any rows.\n */\nexport function buildJoinConstraint(\n sourceRow: Row,\n sourceKey: CompoundKey,\n targetKey: CompoundKey,\n): Record<string, Value> | undefined {\n const constraint: Record<string, Value> = {};\n for (let i = 0; i < targetKey.length; i++) {\n const value = sourceRow[sourceKey[i]];\n if (value === null) {\n return undefined;\n }\n constraint[targetKey[i]] = value;\n }\n return constraint;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"join-utils.js","names":[],"sources":["../../../../../zql/src/ivm/join-utils.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport type {CompoundKey} from '../../../zero-protocol/src/ast.ts';\nimport type {Row, Value} from '../../../zero-protocol/src/data.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport {ChangeType} from './change-type.ts';\nimport type {Change} from './change.ts';\nimport {compareValues, valuesEqual, type Node} from './data.ts';\nimport type {SourceSchema} from './schema.ts';\nimport type {Stream} from './stream.ts';\n\nexport function generateWithOverlayNoYield(\n stream: Stream<Node>,\n overlay: Change,\n schema: SourceSchema,\n): Stream<Node> {\n return generateWithOverlay(stream, overlay, schema) as Stream<Node>;\n}\n\nexport function* generateWithOverlay(\n stream: Stream<Node | 'yield'>,\n overlay: Change,\n schema: SourceSchema,\n): Stream<Node | 'yield'> {\n let applied = false;\n let editOldApplied = false;\n let editNewApplied = false;\n for (const node of stream) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n let yieldNode = true;\n if (!applied) {\n switch (overlay[ChangeIndex.TYPE]) {\n case ChangeType.ADD: {\n if (\n schema.compareRows(overlay[ChangeIndex.NODE].row, node.row) === 0\n ) {\n applied = true;\n yieldNode = false;\n }\n break;\n }\n case ChangeType.REMOVE: {\n if (schema.compareRows(overlay[ChangeIndex.NODE].row, node.row) < 0) {\n applied = true;\n yield overlay[ChangeIndex.NODE];\n }\n break;\n }\n case ChangeType.EDIT: {\n if (\n !editOldApplied &&\n schema.compareRows(overlay[ChangeIndex.OLD_NODE].row, node.row) < 0\n ) {\n editOldApplied = true;\n if (editNewApplied) {\n applied = true;\n }\n yield overlay[ChangeIndex.OLD_NODE];\n }\n if (\n !editNewApplied &&\n schema.compareRows(overlay[ChangeIndex.NODE].row, node.row) === 0\n ) {\n editNewApplied = true;\n if (editOldApplied) {\n applied = true;\n }\n yieldNode = false;\n }\n break;\n }\n case ChangeType.CHILD: {\n if (\n schema.compareRows(overlay[ChangeIndex.NODE].row, node.row) === 0\n ) {\n applied = true;\n yield {\n row: node.row,\n relationships: {\n ...node.relationships,\n [overlay[ChangeIndex.CHILD_DATA].relationshipName]: () =>\n generateWithOverlay(\n node.relationships[\n overlay[ChangeIndex.CHILD_DATA].relationshipName\n ](),\n overlay[ChangeIndex.CHILD_DATA].change,\n schema.relationships[\n overlay[ChangeIndex.CHILD_DATA].relationshipName\n ],\n ),\n },\n };\n yieldNode = false;\n }\n break;\n }\n }\n }\n if (yieldNode) {\n yield node;\n }\n }\n if (!applied) {\n if (overlay[ChangeIndex.TYPE] === ChangeType.REMOVE) {\n applied = true;\n yield overlay[ChangeIndex.NODE];\n } else if (overlay[ChangeIndex.TYPE] === ChangeType.EDIT) {\n assert(\n editNewApplied,\n 'edit overlay: new node must be applied before old node',\n );\n editOldApplied = true;\n applied = true;\n yield overlay[ChangeIndex.OLD_NODE];\n }\n }\n\n assert(\n applied,\n 'overlayGenerator: overlay was never applied to any fetched node',\n );\n}\n\nexport function generateWithOverlayNoYieldUnordered(\n stream: Stream<Node>,\n overlay: Change,\n schema: SourceSchema,\n): Stream<Node> {\n return generateWithOverlayUnordered(stream, overlay, schema) as Stream<Node>;\n}\n\nexport function* generateWithOverlayUnordered(\n stream: Stream<Node | 'yield'>,\n overlay: Change,\n schema: SourceSchema,\n): Stream<Node | 'yield'> {\n // Eager inject\n if (overlay[ChangeIndex.TYPE] === ChangeType.REMOVE) {\n yield overlay[ChangeIndex.NODE];\n } else if (overlay[ChangeIndex.TYPE] === ChangeType.EDIT) {\n yield overlay[ChangeIndex.OLD_NODE];\n }\n\n // Stream with inline suppress\n let suppressed = false;\n for (const node of stream) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n if (!suppressed) {\n if (\n overlay[ChangeIndex.TYPE] === ChangeType.ADD ||\n overlay[ChangeIndex.TYPE] === ChangeType.EDIT\n ) {\n if (\n rowEqualsForCompoundKey(\n overlay[ChangeIndex.NODE].row,\n node.row,\n schema.primaryKey,\n )\n ) {\n suppressed = true;\n continue;\n }\n }\n if (overlay[ChangeIndex.TYPE] === ChangeType.CHILD) {\n if (\n rowEqualsForCompoundKey(\n overlay[ChangeIndex.NODE].row,\n node.row,\n schema.primaryKey,\n )\n ) {\n suppressed = true;\n yield {\n row: node.row,\n relationships: {\n ...node.relationships,\n [overlay[ChangeIndex.CHILD_DATA].relationshipName]: () =>\n generateWithOverlay(\n node.relationships[\n overlay[ChangeIndex.CHILD_DATA].relationshipName\n ](),\n overlay[ChangeIndex.CHILD_DATA].change,\n schema.relationships[\n overlay[ChangeIndex.CHILD_DATA].relationshipName\n ],\n ),\n },\n };\n continue;\n }\n }\n }\n yield node;\n }\n assert(\n suppressed || overlay[ChangeIndex.TYPE] === ChangeType.REMOVE,\n 'overlayGenerator: overlay was never applied to any fetched node',\n );\n}\n\nexport function rowEqualsForCompoundKey(\n a: Row,\n b: Row,\n key: CompoundKey,\n): boolean {\n for (let i = 0; i < key.length; i++) {\n if (compareValues(a[key[i]], b[key[i]]) !== 0) {\n return false;\n }\n }\n return true;\n}\n\nexport function isJoinMatch(\n parent: Row,\n parentKey: CompoundKey,\n child: Row,\n childKey: CompoundKey,\n) {\n for (let i = 0; i < parentKey.length; i++) {\n if (!valuesEqual(parent[parentKey[i]], child[childKey[i]])) {\n return false;\n }\n }\n return true;\n}\n\n/**\n * Builds a constraint object by mapping values from `sourceRow` using `sourceKey`\n * to keys specified by `targetKey`. Returns `undefined` if any source value is `null`,\n * since null foreign keys cannot match any rows.\n */\nexport function buildJoinConstraint(\n sourceRow: Row,\n sourceKey: CompoundKey,\n targetKey: CompoundKey,\n): Record<string, Value> | undefined {\n const constraint: Record<string, Value> = {};\n for (let i = 0; i < targetKey.length; i++) {\n const value = sourceRow[sourceKey[i]];\n if (value === null) {\n return undefined;\n }\n constraint[targetKey[i]] = value;\n }\n return constraint;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"join.js","names":["#parent","#child","#parentKey","#childKey","#relationshipName","#schema","#pushParent","#pushChild","#output","#processParentNode","#pushChildChange","#inprogressChildChange","#inprogressChildChangePosition"],"sources":["../../../../../zql/src/ivm/join.ts"],"sourcesContent":["import {assert, unreachable} from '../../../shared/src/asserts.ts';\nimport type {CompoundKey, System} from '../../../zero-protocol/src/ast.ts';\nimport type {Row} from '../../../zero-protocol/src/data.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 {\n buildJoinConstraint,\n generateWithOverlay,\n generateWithOverlayUnordered,\n isJoinMatch,\n rowEqualsForCompoundKey,\n} from './join-utils.ts';\nimport {\n throwOutput,\n type FetchRequest,\n type Input,\n type Output,\n} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport {type Stream} from './stream.ts';\n\ntype Args = {\n parent: Input;\n child: Input;\n // The nth key in parentKey corresponds to the nth key in childKey.\n parentKey: CompoundKey;\n childKey: CompoundKey;\n relationshipName: string;\n hidden: boolean;\n system: System;\n};\n\n/**\n * The Join operator joins the output from two upstream inputs. Zero's join\n * is a little different from SQL's join in that we output hierarchical data,\n * not a flat table. This makes it a lot more useful for UI programming and\n * avoids duplicating tons of data like left join would.\n *\n * The Nodes output from Join have a new relationship added to them, which has\n * the name #relationshipName. The value of the relationship is a stream of\n * child nodes which are the corresponding values from the child source.\n */\nexport class Join implements Input {\n readonly #parent: Input;\n readonly #child: Input;\n readonly #parentKey: CompoundKey;\n readonly #childKey: CompoundKey;\n readonly #relationshipName: string;\n readonly #schema: SourceSchema;\n\n #output: Output = throwOutput;\n\n #inprogressChildChange: Change | undefined;\n #inprogressChildChangePosition: Row | undefined;\n\n constructor({\n parent,\n child,\n parentKey,\n childKey,\n relationshipName,\n hidden,\n system,\n }: Args) {\n assert(parent !== child, 'Parent and child must be different operators');\n assert(\n parentKey.length === childKey.length,\n 'The parentKey and childKey keys must have same length',\n );\n this.#parent = parent;\n this.#child = child;\n this.#parentKey = parentKey;\n this.#childKey = childKey;\n this.#relationshipName = relationshipName;\n\n const parentSchema = parent.getSchema();\n const childSchema = child.getSchema();\n this.#schema = {\n ...parentSchema,\n relationships: {\n ...parentSchema.relationships,\n [relationshipName]: {\n ...childSchema,\n isHidden: hidden,\n system,\n },\n },\n };\n\n parent.setOutput({\n push: (change: Change) => this.#pushParent(change),\n });\n child.setOutput({\n push: (change: Change) => this.#pushChild(change),\n });\n }\n\n destroy(): void {\n this.#parent.destroy();\n this.#child.destroy();\n }\n\n setOutput(output: Output): void {\n this.#output = output;\n }\n\n getSchema(): SourceSchema {\n return this.#schema;\n }\n\n *fetch(req: FetchRequest): Stream<Node | 'yield'> {\n for (const parentNode of this.#parent.fetch(req)) {\n if (parentNode === 'yield') {\n yield parentNode;\n continue;\n }\n yield this.#processParentNode(parentNode.row, parentNode.relationships);\n }\n }\n\n *#pushParent(change: Change): Stream<'yield'> {\n switch (change[ChangeIndex.TYPE]) {\n case ChangeType.ADD:\n yield* this.#output.push(\n makeAddChange(\n this.#processParentNode(\n change[ChangeIndex.NODE].row,\n change[ChangeIndex.NODE].relationships,\n ),\n ),\n this,\n );\n break;\n case ChangeType.REMOVE:\n yield* this.#output.push(\n makeRemoveChange(\n this.#processParentNode(\n change[ChangeIndex.NODE].row,\n change[ChangeIndex.NODE].relationships,\n ),\n ),\n this,\n );\n break;\n case ChangeType.CHILD:\n yield* this.#output.push(\n makeChildChange(\n this.#processParentNode(\n change[ChangeIndex.NODE].row,\n change[ChangeIndex.NODE].relationships,\n ),\n change[ChangeIndex.CHILD_DATA],\n ),\n this,\n );\n break;\n case ChangeType.EDIT: {\n // Assert the edit could not change the relationship.\n assert(\n rowEqualsForCompoundKey(\n change[ChangeIndex.OLD_NODE].row,\n change[ChangeIndex.NODE].row,\n this.#parentKey,\n ),\n `Parent edit must not change relationship.`,\n );\n yield* this.#output.push(\n makeEditChange(\n this.#processParentNode(\n change[ChangeIndex.NODE].row,\n change[ChangeIndex.NODE].relationships,\n ),\n this.#processParentNode(\n change[ChangeIndex.OLD_NODE].row,\n change[ChangeIndex.OLD_NODE].relationships,\n ),\n ),\n this,\n );\n break;\n }\n default:\n unreachable(change);\n }\n }\n\n *#pushChild(change: Change): Stream<'yield'> {\n switch (change[ChangeIndex.TYPE]) {\n case ChangeType.ADD:\n case ChangeType.REMOVE:\n yield* this.#pushChildChange(change[ChangeIndex.NODE].row, change);\n break;\n case ChangeType.CHILD:\n yield* this.#pushChildChange(change[ChangeIndex.NODE].row, change);\n break;\n case ChangeType.EDIT: {\n const childRow = change[ChangeIndex.NODE].row;\n const oldChildRow = change[ChangeIndex.OLD_NODE].row;\n // Assert the edit could not change the relationship.\n assert(\n rowEqualsForCompoundKey(oldChildRow, childRow, this.#childKey),\n 'Child edit must not change relationship.',\n );\n yield* this.#pushChildChange(childRow, change);\n break;\n }\n\n default:\n unreachable(change);\n }\n }\n\n *#pushChildChange(childRow: Row, change: Change): Stream<'yield'> {\n this.#inprogressChildChange = change;\n this.#inprogressChildChangePosition = undefined;\n try {\n const constraint = buildJoinConstraint(\n childRow,\n this.#childKey,\n this.#parentKey,\n );\n if (constraint) {\n for (const parentNode of this.#parent.fetch({constraint})) {\n if (parentNode === 'yield') {\n yield parentNode;\n continue;\n }\n this.#inprogressChildChangePosition = parentNode.row;\n const childChange = makeChildChange(\n this.#processParentNode(parentNode.row, parentNode.relationships),\n {\n relationshipName: this.#relationshipName,\n change,\n },\n );\n yield* this.#output.push(childChange, this);\n }\n }\n } finally {\n this.#inprogressChildChange = undefined;\n }\n }\n\n #processParentNode(\n parentNodeRow: Row,\n parentNodeRelations: Record<string, () => Stream<Node | 'yield'>>,\n ): Node {\n const childStream = () => {\n const constraint = buildJoinConstraint(\n parentNodeRow,\n this.#parentKey,\n this.#childKey,\n );\n const stream = constraint ? this.#child.fetch({constraint}) : [];\n\n if (\n this.#inprogressChildChange &&\n isJoinMatch(\n parentNodeRow,\n this.#parentKey,\n this.#inprogressChildChange[ChangeIndex.NODE].row,\n this.#childKey,\n ) &&\n this.#inprogressChildChangePosition &&\n this.#schema.compareRows(\n parentNodeRow,\n this.#inprogressChildChangePosition,\n ) > 0\n ) {\n const childSchema = this.#child.getSchema();\n if (childSchema.sort === undefined) {\n return generateWithOverlayUnordered(\n stream,\n this.#inprogressChildChange,\n childSchema,\n );\n }\n return generateWithOverlay(\n stream,\n this.#inprogressChildChange,\n childSchema,\n );\n }\n return stream;\n };\n\n return {\n row: parentNodeRow,\n relationships: {\n ...parentNodeRelations,\n [this.#relationshipName]: childStream,\n },\n };\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"join.js","names":["#parent","#child","#parentKey","#childKey","#relationshipName","#schema","#pushParent","#pushChild","#output","#processParentNode","#pushChildChange","#inprogressChildChange","#inprogressChildChangePosition"],"sources":["../../../../../zql/src/ivm/join.ts"],"sourcesContent":["import {assert, unreachable} from '../../../shared/src/asserts.ts';\nimport type {CompoundKey, System} from '../../../zero-protocol/src/ast.ts';\nimport type {Row} from '../../../zero-protocol/src/data.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 {\n buildJoinConstraint,\n generateWithOverlay,\n generateWithOverlayUnordered,\n isJoinMatch,\n rowEqualsForCompoundKey,\n} from './join-utils.ts';\nimport {\n throwOutput,\n type FetchRequest,\n type Input,\n type Output,\n} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport {type Stream} from './stream.ts';\n\ntype Args = {\n parent: Input;\n child: Input;\n // The nth key in parentKey corresponds to the nth key in childKey.\n parentKey: CompoundKey;\n childKey: CompoundKey;\n relationshipName: string;\n hidden: boolean;\n system: System;\n};\n\n/**\n * The Join operator joins the output from two upstream inputs. Zero's join\n * is a little different from SQL's join in that we output hierarchical data,\n * not a flat table. This makes it a lot more useful for UI programming and\n * avoids duplicating tons of data like left join would.\n *\n * The Nodes output from Join have a new relationship added to them, which has\n * the name #relationshipName. The value of the relationship is a stream of\n * child nodes which are the corresponding values from the child source.\n */\nexport class Join implements Input {\n readonly #parent: Input;\n readonly #child: Input;\n readonly #parentKey: CompoundKey;\n readonly #childKey: CompoundKey;\n readonly #relationshipName: string;\n readonly #schema: SourceSchema;\n\n #output: Output = throwOutput;\n\n #inprogressChildChange: Change | undefined;\n #inprogressChildChangePosition: Row | undefined;\n\n constructor({\n parent,\n child,\n parentKey,\n childKey,\n relationshipName,\n hidden,\n system,\n }: Args) {\n assert(parent !== child, 'Parent and child must be different operators');\n assert(\n parentKey.length === childKey.length,\n 'The parentKey and childKey keys must have same length',\n );\n this.#parent = parent;\n this.#child = child;\n this.#parentKey = parentKey;\n this.#childKey = childKey;\n this.#relationshipName = relationshipName;\n\n const parentSchema = parent.getSchema();\n const childSchema = child.getSchema();\n this.#schema = {\n ...parentSchema,\n relationships: {\n ...parentSchema.relationships,\n [relationshipName]: {\n ...childSchema,\n isHidden: hidden,\n system,\n },\n },\n };\n\n parent.setOutput({\n push: (change: Change) => this.#pushParent(change),\n });\n child.setOutput({\n push: (change: Change) => this.#pushChild(change),\n });\n }\n\n destroy(): void {\n this.#parent.destroy();\n this.#child.destroy();\n }\n\n setOutput(output: Output): void {\n this.#output = output;\n }\n\n getSchema(): SourceSchema {\n return this.#schema;\n }\n\n *fetch(req: FetchRequest): Stream<Node | 'yield'> {\n for (const parentNode of this.#parent.fetch(req)) {\n if (parentNode === 'yield') {\n yield parentNode;\n continue;\n }\n yield this.#processParentNode(parentNode.row, parentNode.relationships);\n }\n }\n\n *#pushParent(change: Change): Stream<'yield'> {\n switch (change[ChangeIndex.TYPE]) {\n case ChangeType.ADD:\n yield* this.#output.push(\n makeAddChange(\n this.#processParentNode(\n change[ChangeIndex.NODE].row,\n change[ChangeIndex.NODE].relationships,\n ),\n ),\n this,\n );\n break;\n case ChangeType.REMOVE:\n yield* this.#output.push(\n makeRemoveChange(\n this.#processParentNode(\n change[ChangeIndex.NODE].row,\n change[ChangeIndex.NODE].relationships,\n ),\n ),\n this,\n );\n break;\n case ChangeType.CHILD:\n yield* this.#output.push(\n makeChildChange(\n this.#processParentNode(\n change[ChangeIndex.NODE].row,\n change[ChangeIndex.NODE].relationships,\n ),\n change[ChangeIndex.CHILD_DATA],\n ),\n this,\n );\n break;\n case ChangeType.EDIT: {\n // Assert the edit could not change the relationship.\n assert(\n rowEqualsForCompoundKey(\n change[ChangeIndex.OLD_NODE].row,\n change[ChangeIndex.NODE].row,\n this.#parentKey,\n ),\n `Parent edit must not change relationship.`,\n );\n yield* this.#output.push(\n makeEditChange(\n this.#processParentNode(\n change[ChangeIndex.NODE].row,\n change[ChangeIndex.NODE].relationships,\n ),\n this.#processParentNode(\n change[ChangeIndex.OLD_NODE].row,\n change[ChangeIndex.OLD_NODE].relationships,\n ),\n ),\n this,\n );\n break;\n }\n default:\n unreachable(change);\n }\n }\n\n *#pushChild(change: Change): Stream<'yield'> {\n switch (change[ChangeIndex.TYPE]) {\n case ChangeType.ADD:\n case ChangeType.REMOVE:\n yield* this.#pushChildChange(change[ChangeIndex.NODE].row, change);\n break;\n case ChangeType.CHILD:\n yield* this.#pushChildChange(change[ChangeIndex.NODE].row, change);\n break;\n case ChangeType.EDIT: {\n const childRow = change[ChangeIndex.NODE].row;\n const oldChildRow = change[ChangeIndex.OLD_NODE].row;\n // Assert the edit could not change the relationship.\n assert(\n rowEqualsForCompoundKey(oldChildRow, childRow, this.#childKey),\n 'Child edit must not change relationship.',\n );\n yield* this.#pushChildChange(childRow, change);\n break;\n }\n\n default:\n unreachable(change);\n }\n }\n\n *#pushChildChange(childRow: Row, change: Change): Stream<'yield'> {\n this.#inprogressChildChange = change;\n this.#inprogressChildChangePosition = undefined;\n try {\n const constraint = buildJoinConstraint(\n childRow,\n this.#childKey,\n this.#parentKey,\n );\n if (constraint) {\n for (const parentNode of this.#parent.fetch({constraint})) {\n if (parentNode === 'yield') {\n yield parentNode;\n continue;\n }\n this.#inprogressChildChangePosition = parentNode.row;\n const childChange = makeChildChange(\n this.#processParentNode(parentNode.row, parentNode.relationships),\n {\n relationshipName: this.#relationshipName,\n change,\n },\n );\n yield* this.#output.push(childChange, this);\n }\n }\n } finally {\n this.#inprogressChildChange = undefined;\n }\n }\n\n #processParentNode(\n parentNodeRow: Row,\n parentNodeRelations: Record<string, () => Stream<Node | 'yield'>>,\n ): Node {\n const childStream = () => {\n const constraint = buildJoinConstraint(\n parentNodeRow,\n this.#parentKey,\n this.#childKey,\n );\n const stream = constraint ? this.#child.fetch({constraint}) : [];\n\n if (\n this.#inprogressChildChange &&\n isJoinMatch(\n parentNodeRow,\n this.#parentKey,\n this.#inprogressChildChange[ChangeIndex.NODE].row,\n this.#childKey,\n ) &&\n this.#inprogressChildChangePosition &&\n this.#schema.compareRows(\n parentNodeRow,\n this.#inprogressChildChangePosition,\n ) > 0\n ) {\n const childSchema = this.#child.getSchema();\n if (childSchema.sort === undefined) {\n return generateWithOverlayUnordered(\n stream,\n this.#inprogressChildChange,\n childSchema,\n );\n }\n return generateWithOverlay(\n stream,\n this.#inprogressChildChange,\n childSchema,\n );\n }\n return stream;\n };\n\n return {\n row: parentNodeRow,\n relationships: {\n ...parentNodeRelations,\n [this.#relationshipName]: childStream,\n },\n };\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"maybe-split-and-push-edit-change.js","names":[],"sources":["../../../../../zql/src/ivm/maybe-split-and-push-edit-change.ts"],"sourcesContent":["import type {Row} from '../../../zero-protocol/src/data.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport {makeAddChange, makeRemoveChange, type EditChange} from './change.ts';\nimport type {InputBase, Output} from './operator.ts';\n\n/**\n * This takes an {@linkcode EditChange} and a predicate that determines if a row\n * should be present based on the row's data. It then splits the change and\n * pushes the appropriate changes to the output based on the predicate.\n */\nexport function* maybeSplitAndPushEditChange(\n change: EditChange,\n predicate: (row: Row) => boolean,\n output: Output,\n pusher: InputBase,\n) {\n const oldWasPresent = predicate(change[ChangeIndex.OLD_NODE].row);\n const newIsPresent = predicate(change[ChangeIndex.NODE].row);\n\n if (oldWasPresent && newIsPresent) {\n yield* output.push(change, pusher);\n } else if (oldWasPresent && !newIsPresent) {\n yield* output.push(makeRemoveChange(change[ChangeIndex.OLD_NODE]), pusher);\n } else if (!oldWasPresent && newIsPresent) {\n yield* output.push(makeAddChange(change[ChangeIndex.NODE]), pusher);\n }\n}\n"],"mappings":";;;;;;;AAUA,UAAiB,4BACf,QACA,WACA,QACA,QACA;CACA,MAAM,gBAAgB,UAAU,OAAO,GAAsB,GAAG;CAChE,MAAM,eAAe,UAAU,OAAO,GAAkB,GAAG;CAE3D,IAAI,iBAAiB,cACnB,OAAO,OAAO,KAAK,QAAQ,MAAM;MAC5B,IAAI,iBAAiB,CAAC,cAC3B,OAAO,OAAO,KAAK,iBAAiB,OAAO,EAAqB,GAAG,MAAM;MACpE,IAAI,CAAC,iBAAiB,cAC3B,OAAO,OAAO,KAAK,cAAc,OAAO,EAAiB,GAAG,MAAM;AAEtE"}
1
+ {"version":3,"file":"maybe-split-and-push-edit-change.js","names":[],"sources":["../../../../../zql/src/ivm/maybe-split-and-push-edit-change.ts"],"sourcesContent":["import type {Row} from '../../../zero-protocol/src/data.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport {makeAddChange, makeRemoveChange, type EditChange} from './change.ts';\nimport type {InputBase, Output} from './operator.ts';\n\n/**\n * This takes an {@linkcode EditChange} and a predicate that determines if a row\n * should be present based on the row's data. It then splits the change and\n * pushes the appropriate changes to the output based on the predicate.\n */\nexport function* maybeSplitAndPushEditChange(\n change: EditChange,\n predicate: (row: Row) => boolean,\n output: Output,\n pusher: InputBase,\n) {\n const oldWasPresent = predicate(change[ChangeIndex.OLD_NODE].row);\n const newIsPresent = predicate(change[ChangeIndex.NODE].row);\n\n if (oldWasPresent && newIsPresent) {\n yield* output.push(change, pusher);\n } else if (oldWasPresent && !newIsPresent) {\n yield* output.push(makeRemoveChange(change[ChangeIndex.OLD_NODE]), pusher);\n } else if (!oldWasPresent && newIsPresent) {\n yield* output.push(makeAddChange(change[ChangeIndex.NODE]), pusher);\n }\n}\n"],"mappings":";;;;;;;AAUA,UAAiB,4BACf,QACA,WACA,QACA,QACA;CACA,MAAM,gBAAgB,UAAU,OAAO,GAAsB,IAAI;CACjE,MAAM,eAAe,UAAU,OAAO,GAAkB,IAAI;AAE5D,KAAI,iBAAiB,aACnB,QAAO,OAAO,KAAK,QAAQ,OAAO;UACzB,iBAAiB,CAAC,aAC3B,QAAO,OAAO,KAAK,iBAAiB,OAAO,GAAsB,EAAE,OAAO;UACjE,CAAC,iBAAiB,aAC3B,QAAO,OAAO,KAAK,cAAc,OAAO,GAAkB,EAAE,OAAO"}
@@ -1 +1 @@
1
- {"version":3,"file":"memory-source.js","names":["#tableName","#columns","#primaryKey","#primaryIndexSort","#indexes","#connections","#getPrimaryIndex","#fetch","#disconnect","#getSchema","#fetchMulti","#getOrCreateIndex","#overlay","#writeChange","#pushEpoch"],"sources":["../../../../../zql/src/ivm/memory-source.ts"],"sourcesContent":["import {assert, unreachable} from '../../../shared/src/asserts.ts';\nimport {BTreeSet} from '../../../shared/src/btree-set.ts';\nimport {hasOwn} from '../../../shared/src/has-own.ts';\nimport {once, toSorted} from '../../../shared/src/iterables.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport type {\n Condition,\n Ordering,\n OrderPart,\n} from '../../../zero-protocol/src/ast.ts';\nimport type {Row, Value} from '../../../zero-protocol/src/data.ts';\nimport type {PrimaryKey} from '../../../zero-protocol/src/primary-key.ts';\nimport type {SchemaValue} from '../../../zero-types/src/schema-value.ts';\nimport type {DebugDelegate} from '../builder/debug-delegate.ts';\nimport {\n createPredicate,\n transformFilters,\n type NoSubqueryCondition,\n} from '../builder/filter.ts';\nimport {assertOrderingIncludesPK} from '../query/complete-ordering.ts';\nimport {ChangeType} from './change-type.ts';\nimport type {Change} from './change.ts';\nimport {makeAddChange, makeEditChange, makeRemoveChange} from './change.ts';\nimport {\n constraintMatchesPrimaryKey,\n constraintMatchesRow,\n primaryKeyConstraintFromFilters,\n type Constraint,\n} from './constraint.ts';\nimport {\n compareValues,\n makeComparator,\n valuesEqual,\n type Comparator,\n type Node,\n} from './data.ts';\nimport {filterPush} from './filter-push.ts';\nimport {\n skipYields,\n type FetchRequest,\n type Input,\n type MultiConstraint,\n type Output,\n type Start,\n} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport {SourceChangeIndex} from './source-change-index.ts';\nimport type {\n Source,\n SourceChange,\n SourceChangeAdd,\n SourceChangeEdit,\n SourceChangeRemove,\n SourceInput,\n} from './source.ts';\nimport {makeSourceChangeAdd, makeSourceChangeRemove} from './source.ts';\nimport type {Stream} from './stream.ts';\n\nexport type Overlay = {\n epoch: number;\n change: SourceChange;\n};\n\nexport type Overlays = {\n add: Row | undefined;\n remove: Row | undefined;\n};\n\ntype Index = {\n comparator: Comparator;\n data: BTreeSet<Row>;\n usedBy: Set<Connection>;\n};\n\nexport type Connection = {\n input: Input;\n output: Output | undefined;\n sort?: Ordering | undefined;\n splitEditKeys: Set<string> | undefined;\n compareRows: Comparator;\n filters:\n | {\n condition: NoSubqueryCondition;\n predicate: (row: Row) => boolean;\n }\n | undefined;\n readonly debug?: DebugDelegate | undefined;\n lastPushedEpoch: number;\n};\n\n/**\n * A `MemorySource` is a source that provides data to the pipeline from an\n * in-memory data source.\n *\n * This data is kept in sorted order as downstream pipelines will always expect\n * the data they receive from `pull` to be in sorted order.\n */\nexport class MemorySource implements Source {\n readonly #tableName: string;\n readonly #columns: Record<string, SchemaValue>;\n readonly #primaryKey: PrimaryKey;\n readonly #primaryIndexSort: Ordering;\n readonly #indexes: Map<string, Index> = new Map();\n readonly #connections: Connection[] = [];\n\n #overlay: Overlay | undefined;\n #pushEpoch = 0;\n\n constructor(\n tableName: string,\n columns: Record<string, SchemaValue>,\n primaryKey: PrimaryKey,\n primaryIndexData?: BTreeSet<Row>,\n ) {\n this.#tableName = tableName;\n this.#columns = columns;\n this.#primaryKey = primaryKey;\n this.#primaryIndexSort = primaryKey.map(k => [k, 'asc']);\n const comparator = makeBoundComparator(this.#primaryIndexSort);\n this.#indexes.set(JSON.stringify(this.#primaryIndexSort), {\n comparator,\n data: primaryIndexData ?? new BTreeSet<Row>(comparator),\n usedBy: new Set(),\n });\n }\n\n get tableSchema() {\n return {\n name: this.#tableName,\n columns: this.#columns,\n primaryKey: this.#primaryKey,\n };\n }\n\n fork() {\n const primaryIndex = this.#getPrimaryIndex();\n return new MemorySource(\n this.#tableName,\n this.#columns,\n this.#primaryKey,\n primaryIndex.data.clone(),\n );\n }\n\n get data(): BTreeSet<Row> {\n return this.#getPrimaryIndex().data;\n }\n\n #getSchema(connection: Connection, unordered: boolean): SourceSchema {\n return {\n tableName: this.#tableName,\n columns: this.#columns,\n primaryKey: this.#primaryKey,\n sort: unordered ? undefined : connection.sort,\n system: 'client',\n relationships: {},\n isHidden: false,\n compareRows: connection.compareRows,\n };\n }\n\n connect(\n sort: Ordering | undefined,\n filters?: Condition,\n splitEditKeys?: Set<string>,\n ): SourceInput {\n const transformedFilters = transformFilters(filters);\n const unordered = sort === undefined;\n const internalSort = sort ?? this.#primaryIndexSort;\n\n const input: SourceInput = {\n getSchema: () => schema,\n fetch: req => this.#fetch(req, connection),\n setOutput: output => {\n connection.output = output;\n },\n destroy: () => {\n this.#disconnect(input);\n },\n fullyAppliedFilters: !transformedFilters.conditionsRemoved,\n };\n\n const connection: Connection = {\n input,\n output: undefined,\n sort: internalSort,\n splitEditKeys,\n compareRows: makeComparator(internalSort),\n filters: transformedFilters.filters\n ? {\n condition: transformedFilters.filters,\n predicate: createPredicate(transformedFilters.filters),\n }\n : undefined,\n lastPushedEpoch: 0,\n };\n const schema = this.#getSchema(connection, unordered);\n if (!unordered) {\n assertOrderingIncludesPK(internalSort, this.#primaryKey);\n }\n this.#connections.push(connection);\n return input;\n }\n\n #disconnect(input: Input): void {\n const idx = this.#connections.findIndex(c => c.input === input);\n assert(idx !== -1, 'Connection not found');\n this.#connections.splice(idx, 1);\n\n // TODO: We used to delete unused indexes here. But in common cases like\n // navigating into issue detail pages it caused a ton of constantly\n // building and destroying indexes.\n //\n // Perhaps some intelligent LRU or something is needed here but for now,\n // the opposite extreme of keeping all indexes for the lifetime of the\n // page seems better.\n }\n\n #getPrimaryIndex(): Index {\n const index = this.#indexes.get(JSON.stringify(this.#primaryIndexSort));\n assert(index, 'Primary index not found');\n return index;\n }\n\n #getOrCreateIndex(sort: Ordering, usedBy: Connection): Index {\n const key = JSON.stringify(sort);\n const index = this.#indexes.get(key);\n // Future optimization could use existing index if it's the same just sorted\n // in reverse of needed.\n if (index) {\n index.usedBy.add(usedBy);\n return index;\n }\n\n const comparator = makeBoundComparator(sort);\n\n // When creating these synchronously becomes a problem, a few options:\n // 1. Allow users to specify needed indexes up front\n // 2. Create indexes in a different thread asynchronously (this would require\n // modifying the BTree to be able to be passed over structured-clone, or using\n // a different library.)\n // 3. We could even theoretically do (2) on multiple threads and then merge the\n // results!\n const rows = toSorted(this.#getPrimaryIndex().data, comparator);\n const data = BTreeSet.fromSorted(comparator, rows);\n\n const newIndex = {comparator, data, usedBy: new Set([usedBy])};\n this.#indexes.set(key, newIndex);\n return newIndex;\n }\n\n // For unit testing that we correctly clean up indexes.\n getIndexKeys(): string[] {\n return [...this.#indexes.keys()];\n }\n\n *#fetch(req: FetchRequest, conn: Connection): Stream<Node | 'yield'> {\n // multiConstraints is handled by driving sub-fetches off the first\n // entry's values and post-filtering matches against any remaining\n // entries. TableSource implements multi-IN natively via SQL `AND` of\n // `IN` clauses; the in-memory path is correct but not specially\n // optimized — it pays the index-seek cost per primary value, then\n // walks each result against the rest.\n if (\n req.multiConstraints &&\n req.multiConstraints.some(mc => mc.length > 0)\n ) {\n yield* this.#fetchMulti(req, conn);\n return;\n }\n const requestedSort = must(conn.sort);\n const {compareRows} = conn;\n // Avoid allocating a new closure when not reversing (the common case).\n const connectionComparator: Comparator = req.reverse\n ? (r1, r2) => compareRows(r2, r1)\n : compareRows;\n\n const pkConstraint = primaryKeyConstraintFromFilters(\n conn.filters?.condition,\n this.#primaryKey,\n );\n // The primary key constraint will be more limiting than the constraint\n // so swap out to that if it exists.\n const fetchOrPkConstraint = pkConstraint ?? req.constraint;\n\n // If there is a constraint, we need an index sorted by it first.\n const indexSort: OrderPart[] = [];\n if (fetchOrPkConstraint) {\n for (const key of Object.keys(fetchOrPkConstraint)) {\n indexSort.push([key, 'asc']);\n }\n }\n\n // For the special case of constraining by PK, we don't need to worry about\n // any requested sort since there can only be one result. Otherwise we also\n // need the index sorted by the requested sort.\n if (\n this.#primaryKey.length > 1 ||\n !fetchOrPkConstraint ||\n !constraintMatchesPrimaryKey(fetchOrPkConstraint, this.#primaryKey)\n ) {\n indexSort.push(...requestedSort);\n }\n\n const index = this.#getOrCreateIndex(indexSort, conn);\n const {data, comparator: compare} = index;\n // Avoid allocating a new closure when not reversing (the common case).\n const indexComparator: Comparator = req.reverse\n ? (r1, r2) => compare(r2, r1)\n : compare;\n\n const startAt = req.start?.row;\n\n // If there is a constraint, we want to start our scan at the first row that\n // matches the constraint. But because the next OrderPart can be `desc`,\n // it's not true that {[constraintKey]: constraintValue} is the first\n // matching row. Because in that case, the other fields will all be\n // `undefined`, and in Zero `undefined` is always less than any other value.\n // So if the second OrderPart is descending then `undefined` values will\n // actually be the *last* row. We need a way to stay \"start at the first row\n // with this constraint value\". RowBound with the corresponding compareBound\n // comparator accomplishes this. The right thing is probably to teach the\n // btree library to support this concept.\n let scanStart: RowBound | undefined;\n\n if (fetchOrPkConstraint) {\n scanStart = {};\n for (const [key, dir] of indexSort) {\n if (hasOwn(fetchOrPkConstraint, key)) {\n scanStart[key] = fetchOrPkConstraint[key];\n } else {\n if (req.reverse) {\n scanStart[key] = dir === 'asc' ? maxValue : minValue;\n } else {\n scanStart[key] = dir === 'asc' ? minValue : maxValue;\n }\n }\n }\n } else {\n scanStart = startAt;\n }\n\n const rowsIterable = generateRows(data, scanStart, req.reverse);\n const withOverlay = generateWithOverlay(\n startAt,\n pkConstraint ? once(rowsIterable) : rowsIterable,\n // use `req.constraint` here and not `fetchOrPkConstraint` since `fetchOrPkConstraint` could be the\n // primary key constraint. The primary key constraint comes from filters and is acting as a filter\n // rather than as the fetch constraint.\n req.constraint,\n this.#overlay,\n conn.lastPushedEpoch,\n // Use indexComparator, generateWithOverlayInner has a subtle dependency\n // on this. Since generateWithConstraint is done after\n // generateWithOverlay, the generator consumed by generateWithOverlayInner\n // does not end when the constraint stops matching and so the final\n // check to yield an add overlay if not yet yielded is not reached.\n // However, using the indexComparator the add overlay will be less than\n // the first row that does not match the constraint, and so any\n // not yet yielded add overlay will be yielded when the first row\n // not matching the constraint is reached.\n indexComparator,\n conn.filters?.predicate,\n );\n\n const withConstraint = generateWithConstraint(\n skipYields(\n generateWithStart(withOverlay, req.start, connectionComparator),\n ),\n // we use `req.constraint` and not `fetchOrPkConstraint` here because we need to\n // AND the constraint with what could have been the primary key constraint\n req.constraint,\n );\n\n yield* conn.filters\n ? generateWithFilter(withConstraint, conn.filters.predicate)\n : withConstraint;\n }\n\n *#fetchMulti(req: FetchRequest, conn: Connection): Stream<Node | 'yield'> {\n // Caller (`#fetch`) guards entry on `req.multiConstraints.some(mc =>\n // mc.length > 0)`, so `multis` is guaranteed non-empty after the\n // empty-entry filter. Per the MultiConstraint contract (operator.ts),\n // entries are non-empty, share key shape, are unique, and are\n // key-compatible with `req.constraint`.\n const multis = must(req.multiConstraints).filter(mc => mc.length > 0);\n const primary = multis[0];\n const rest = multis.slice(1);\n const baseConstraint = req.constraint;\n\n // Drive sub-fetches off `primary`. Within each sub-fetch the source\n // can use its index for `primary`; we then post-filter rows by\n // checking they also match every entry in `rest`. Per the\n // MultiConstraint contract (see operator.ts), entries are unique and\n // key-compatible with `baseConstraint`, so we don't dedupe or check\n // compatibility here.\n const subStreams: Stream<Node | 'yield'>[] = primary.map(c => {\n const merged: Constraint = baseConstraint ? {...baseConstraint, ...c} : c;\n return this.#fetch(\n {...req, constraint: merged, multiConstraints: undefined},\n conn,\n );\n });\n const merged = mergeSortedStreams(\n subStreams,\n req.reverse\n ? (a, b) => conn.compareRows(b.row, a.row)\n : (a, b) => conn.compareRows(a.row, b.row),\n );\n\n if (rest.length === 0) {\n yield* merged;\n return;\n }\n\n for (const node of merged) {\n if (node === 'yield') {\n yield 'yield';\n continue;\n }\n let matchesAll = true;\n for (const mc of rest) {\n let any = false;\n for (const c of mc) {\n if (constraintMatchesRow(c, node.row)) {\n any = true;\n break;\n }\n }\n if (!any) {\n matchesAll = false;\n break;\n }\n }\n if (matchesAll) yield node;\n }\n }\n\n *push(change: SourceChange): Stream<'yield'> {\n for (const result of this.genPush(change)) {\n if (result === 'yield') {\n yield result;\n }\n }\n }\n\n *genPush(change: SourceChange) {\n const primaryIndex = this.#getPrimaryIndex();\n const {data} = primaryIndex;\n const exists = (row: Row) => data.has(row);\n const setOverlay = (o: Overlay | undefined) => (this.#overlay = o);\n const writeChange = (c: SourceChange) => this.#writeChange(c);\n yield* genPushAndWriteWithSplitEdit(\n this.#connections,\n change,\n exists,\n setOverlay,\n writeChange,\n () => ++this.#pushEpoch,\n );\n }\n\n #writeChange(change: SourceChange) {\n for (const {data} of this.#indexes.values()) {\n switch (change[SourceChangeIndex.TYPE]) {\n case ChangeType.ADD: {\n const added = data.add(change[SourceChangeIndex.ROW]);\n // must succeed since we checked has() above.\n assert(\n added,\n 'MemorySource: add must succeed since row existence was already checked',\n );\n break;\n }\n case ChangeType.REMOVE: {\n const removed = data.delete(change[SourceChangeIndex.ROW]);\n // must succeed since we checked has() above.\n assert(\n removed,\n 'MemorySource: remove must succeed since row existence was already checked',\n );\n break;\n }\n case ChangeType.EDIT: {\n // TODO: We could see if the PK (form the index tree's perspective)\n // changed and if not we could use set.\n // We cannot just do `set` with the new value since the `oldRow` might\n // not map to the same entry as the new `row` in the index btree.\n const removed = data.delete(change[SourceChangeIndex.OLD_ROW]);\n // must succeed since we checked has() above.\n assert(\n removed,\n 'MemorySource: edit remove must succeed since row existence was already checked',\n );\n data.add(change[SourceChangeIndex.ROW]);\n break;\n }\n default:\n unreachable(change);\n }\n }\n }\n}\n\nfunction* generateWithConstraint(\n it: Stream<Node>,\n constraint: Constraint | undefined,\n) {\n for (const node of it) {\n if (constraint && !constraintMatchesRow(constraint, node.row)) {\n break;\n }\n yield node;\n }\n}\n\nfunction* generateWithFilter(it: Stream<Node>, filter: (row: Row) => boolean) {\n for (const node of it) {\n if (filter(node.row)) {\n yield node;\n }\n }\n}\n\nexport function* genPushAndWriteWithSplitEdit(\n connections: readonly Connection[],\n change: SourceChange,\n exists: (row: Row) => boolean,\n setOverlay: (o: Overlay | undefined) => Overlay | undefined,\n writeChange: (c: SourceChange) => void,\n getNextEpoch: () => number,\n) {\n let shouldSplitEdit = false;\n if (change[SourceChangeIndex.TYPE] === ChangeType.EDIT) {\n for (const {splitEditKeys} of connections) {\n if (splitEditKeys) {\n for (const key of splitEditKeys) {\n if (\n !valuesEqual(\n change[SourceChangeIndex.ROW][key],\n change[SourceChangeIndex.OLD_ROW][key],\n )\n ) {\n shouldSplitEdit = true;\n break;\n }\n }\n }\n }\n }\n\n if (change[SourceChangeIndex.TYPE] === ChangeType.EDIT && shouldSplitEdit) {\n yield* genPushAndWrite(\n connections,\n makeSourceChangeRemove(change[SourceChangeIndex.OLD_ROW]),\n exists,\n setOverlay,\n writeChange,\n getNextEpoch(),\n );\n yield* genPushAndWrite(\n connections,\n makeSourceChangeAdd(change[SourceChangeIndex.ROW]),\n exists,\n setOverlay,\n writeChange,\n getNextEpoch(),\n );\n } else {\n yield* genPushAndWrite(\n connections,\n change,\n exists,\n setOverlay,\n writeChange,\n getNextEpoch(),\n );\n }\n}\n\nfunction* genPushAndWrite(\n connections: readonly Connection[],\n change: SourceChangeAdd | SourceChangeRemove | SourceChangeEdit,\n exists: (row: Row) => boolean,\n setOverlay: (o: Overlay | undefined) => Overlay | undefined,\n writeChange: (c: SourceChange) => void,\n pushEpoch: number,\n) {\n for (const x of genPush(connections, change, exists, setOverlay, pushEpoch)) {\n yield x;\n }\n writeChange(change);\n}\n\nfunction* genPush(\n connections: readonly Connection[],\n change: SourceChange,\n exists: (row: Row) => boolean,\n setOverlay: (o: Overlay | undefined) => void,\n pushEpoch: number,\n) {\n switch (change[SourceChangeIndex.TYPE]) {\n case ChangeType.ADD:\n assert(\n !exists(change[SourceChangeIndex.ROW]),\n () => `Row already exists ${stringify(change)}`,\n );\n break;\n case ChangeType.REMOVE:\n assert(\n exists(change[SourceChangeIndex.ROW]),\n () => `Row not found ${stringify(change)}`,\n );\n break;\n case ChangeType.EDIT:\n assert(\n exists(change[SourceChangeIndex.OLD_ROW]),\n () => `Row not found ${stringify(change)}`,\n );\n break;\n default:\n unreachable(change);\n }\n\n for (const conn of connections) {\n const {output, filters, input} = conn;\n if (output) {\n conn.lastPushedEpoch = pushEpoch;\n setOverlay({epoch: pushEpoch, change});\n const outputChange: Change =\n change[SourceChangeIndex.TYPE] === ChangeType.EDIT\n ? makeEditChange(\n {row: change[SourceChangeIndex.ROW], relationships: {}},\n {row: change[SourceChangeIndex.OLD_ROW], relationships: {}},\n )\n : change[SourceChangeIndex.TYPE] === ChangeType.ADD\n ? makeAddChange({\n row: change[SourceChangeIndex.ROW],\n relationships: {},\n })\n : makeRemoveChange({\n row: change[SourceChangeIndex.ROW],\n relationships: {},\n });\n yield* filterPush(outputChange, output, input, filters?.predicate);\n yield undefined;\n }\n }\n\n setOverlay(undefined);\n}\n\nexport function* generateWithStart(\n nodes: Iterable<Node | 'yield'>,\n start: Start | undefined,\n compare: (r1: Row, r2: Row) => number,\n): Stream<Node | 'yield'> {\n if (!start) {\n yield* nodes;\n return;\n }\n let started = false;\n for (const node of nodes) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n if (!started) {\n if (start.basis === 'at') {\n if (compare(node.row, start.row) >= 0) {\n started = true;\n }\n } else if (start.basis === 'after') {\n if (compare(node.row, start.row) > 0) {\n started = true;\n }\n }\n }\n if (started) {\n yield node;\n }\n }\n}\n\n/**\n * Takes an iterator and overlay.\n * Splices the overlay into the iterator at the correct position.\n *\n * @param startAt - if there is a lower bound to the stream. If the lower bound of the stream\n * is above the overlay, the overlay will be skipped.\n * @param rows - the stream into which the overlay should be spliced\n * @param constraint - constraint that was applied to the rowIterator and should\n * also be applied to the overlay.\n * @param overlay - the overlay values to splice in\n * @param compare - the comparator to use to find the position for the overlay\n */\nexport function* generateWithOverlay(\n startAt: Row | undefined,\n rows: Iterable<Row>,\n constraint: Constraint | undefined,\n overlay: Overlay | undefined,\n lastPushedEpoch: number,\n compare: Comparator,\n filterPredicate?: (row: Row) => boolean | undefined,\n multiConstraints?: readonly MultiConstraint[] | undefined,\n) {\n let overlayToApply: Overlay | undefined = undefined;\n if (overlay && lastPushedEpoch >= overlay.epoch) {\n overlayToApply = overlay;\n }\n const overlays = computeOverlays(\n startAt,\n constraint,\n overlayToApply,\n compare,\n filterPredicate,\n multiConstraints,\n );\n yield* generateWithOverlayInner(rows, overlays, compare);\n}\n\nfunction computeOverlays(\n startAt: Row | undefined,\n constraint: Constraint | undefined,\n overlay: Overlay | undefined,\n compare: Comparator,\n filterPredicate?: (row: Row) => boolean | undefined,\n multiConstraints?: readonly MultiConstraint[] | undefined,\n): Overlays {\n let overlays: Overlays = {\n add: undefined,\n remove: undefined,\n };\n switch (overlay?.change[SourceChangeIndex.TYPE]) {\n case ChangeType.ADD:\n overlays = {\n add: overlay.change[SourceChangeIndex.ROW],\n remove: undefined,\n };\n break;\n case ChangeType.REMOVE:\n overlays = {\n add: undefined,\n remove: overlay.change[SourceChangeIndex.ROW],\n };\n break;\n case ChangeType.EDIT:\n overlays = {\n add: overlay.change[SourceChangeIndex.ROW],\n remove: overlay.change[SourceChangeIndex.OLD_ROW],\n };\n break;\n }\n\n if (startAt) {\n overlays = overlaysForStartAt(overlays, startAt, compare);\n }\n\n if (constraint) {\n overlays = overlaysForConstraint(overlays, constraint);\n }\n\n overlays = applyMultiConstraintsToOverlays(overlays, multiConstraints);\n\n if (filterPredicate) {\n overlays = overlaysForFilterPredicate(overlays, filterPredicate);\n }\n\n return overlays;\n}\n\nfunction applyMultiConstraintsToOverlays(\n overlays: Overlays,\n multiConstraints: readonly MultiConstraint[] | undefined,\n): Overlays {\n if (!multiConstraints) return overlays;\n for (const mc of multiConstraints) {\n if (mc.length > 0) {\n overlays = overlaysForMultiConstraint(overlays, mc);\n }\n }\n return overlays;\n}\n\nexport {overlaysForMultiConstraint as overlaysForMultiConstraintForTest};\n\nfunction overlaysForMultiConstraint(\n {add, remove}: Overlays,\n multiConstraint: MultiConstraint,\n): Overlays {\n const matchesAny = (row: Row | undefined) => {\n if (row === undefined) return false;\n for (const c of multiConstraint) {\n if (constraintMatchesRow(c, row)) return true;\n }\n return false;\n };\n return {\n add: matchesAny(add) ? add : undefined,\n remove: matchesAny(remove) ? remove : undefined,\n };\n}\n\nexport {overlaysForStartAt as overlaysForStartAtForTest};\n\nfunction overlaysForStartAt(\n {add, remove}: Overlays,\n startAt: Row,\n compare: Comparator,\n): Overlays {\n const undefinedIfBeforeStartAt = (row: Row | undefined) =>\n row === undefined || compare(row, startAt) < 0 ? undefined : row;\n return {\n add: undefinedIfBeforeStartAt(add),\n remove: undefinedIfBeforeStartAt(remove),\n };\n}\n\nexport {overlaysForConstraint as overlaysForConstraintForTest};\n\nfunction overlaysForConstraint(\n {add, remove}: Overlays,\n constraint: Constraint,\n): Overlays {\n const undefinedIfDoesntMatchConstraint = (row: Row | undefined) =>\n row === undefined || !constraintMatchesRow(constraint, row)\n ? undefined\n : row;\n\n return {\n add: undefinedIfDoesntMatchConstraint(add),\n remove: undefinedIfDoesntMatchConstraint(remove),\n };\n}\n\nfunction overlaysForFilterPredicate(\n {add, remove}: Overlays,\n filterPredicate: (row: Row) => boolean | undefined,\n): Overlays {\n const undefinedIfDoesntMatchFilter = (row: Row | undefined) =>\n row === undefined || !filterPredicate(row) ? undefined : row;\n\n return {\n add: undefinedIfDoesntMatchFilter(add),\n remove: undefinedIfDoesntMatchFilter(remove),\n };\n}\n\nexport function* generateWithOverlayInner(\n rowIterator: Iterable<Row>,\n overlays: Overlays,\n compare: (r1: Row, r2: Row) => number,\n) {\n let addOverlayYielded = false;\n let removeOverlaySkipped = false;\n for (const row of rowIterator) {\n if (!addOverlayYielded && overlays.add) {\n const cmp = compare(overlays.add, row);\n if (cmp < 0) {\n addOverlayYielded = true;\n yield {row: overlays.add, relationships: {}};\n }\n }\n\n if (!removeOverlaySkipped && overlays.remove) {\n const cmp = compare(overlays.remove, row);\n if (cmp === 0) {\n removeOverlaySkipped = true;\n continue;\n }\n }\n yield {row, relationships: {}};\n }\n\n if (!addOverlayYielded && overlays.add) {\n yield {row: overlays.add, relationships: {}};\n }\n}\n\n/**\n * Like {@link generateWithOverlay} but for unordered streams.\n * No `startAt` or comparator needed. Injects remove/old-edit rows eagerly\n * at the start, and suppresses add/new-edit rows inline by PK match.\n */\nexport function* generateWithOverlayUnordered(\n rows: Iterable<Row>,\n constraint: Constraint | undefined,\n overlay: Overlay | undefined,\n lastPushedEpoch: number,\n primaryKey: PrimaryKey,\n filterPredicate?: (row: Row) => boolean,\n multiConstraints?: readonly MultiConstraint[] | undefined,\n) {\n let overlayToApply: Overlay | undefined = undefined;\n if (overlay && lastPushedEpoch >= overlay.epoch) {\n overlayToApply = overlay;\n }\n let overlays: Overlays = {add: undefined, remove: undefined};\n switch (overlayToApply?.change[SourceChangeIndex.TYPE]) {\n case ChangeType.ADD:\n overlays = {\n add: overlayToApply.change[SourceChangeIndex.ROW],\n remove: undefined,\n };\n break;\n case ChangeType.REMOVE:\n overlays = {\n add: undefined,\n remove: overlayToApply.change[SourceChangeIndex.ROW],\n };\n break;\n case ChangeType.EDIT:\n overlays = {\n add: overlayToApply.change[SourceChangeIndex.ROW],\n remove: overlayToApply.change[SourceChangeIndex.OLD_ROW],\n };\n break;\n }\n if (constraint) {\n overlays = overlaysForConstraint(overlays, constraint);\n }\n overlays = applyMultiConstraintsToOverlays(overlays, multiConstraints);\n if (filterPredicate) {\n overlays = overlaysForFilterPredicate(overlays, filterPredicate);\n }\n yield* generateWithOverlayInnerUnordered(rows, overlays, primaryKey);\n}\n\nexport function* generateWithOverlayInnerUnordered(\n rowIterator: Iterable<Row>,\n overlays: Overlays,\n primaryKey: PrimaryKey,\n) {\n // Eager inject: yield the add overlay at the start (row not yet in storage)\n if (overlays.add) {\n yield {row: overlays.add, relationships: {}};\n }\n // Stream with inline suppress: skip the remove overlay (row still in storage)\n let removeSkipped = false;\n for (const row of rowIterator) {\n if (\n !removeSkipped &&\n overlays.remove &&\n rowMatchesPK(overlays.remove, row, primaryKey)\n ) {\n removeSkipped = true;\n continue;\n }\n yield {row, relationships: {}};\n }\n}\n\nfunction rowMatchesPK(a: Row, b: Row, primaryKey: PrimaryKey): boolean {\n for (const key of primaryKey) {\n if (!valuesEqual(a[key], b[key])) {\n return false;\n }\n }\n return true;\n}\n\n/**\n * A location to begin scanning an index from. Can either be a specific value\n * or the min or max possible value for the type. This is used to start a scan\n * at the beginning of the rows matching a constraint.\n */\ntype Bound = Value | MinValue | MaxValue;\ntype RowBound = Record<string, Bound>;\nconst minValue = Symbol('min-value');\ntype MinValue = typeof minValue;\nconst maxValue = Symbol('max-value');\ntype MaxValue = typeof maxValue;\n\nfunction makeBoundComparator(sort: Ordering): Comparator {\n // Pre-extract the first two keys/directions to avoid per-call array access.\n // All paths share one function literal (single SFI) so the BTree comparator call site\n // stays monomorphic across indexes with different sort orderings, preventing V8 IC deopt.\n // Even a 2-SFI split (e.g. separate len=1 path) creates a polymorphic IC that\n // measurably regresses performance, so we keep a single return body.\n const len = sort.length;\n const k0 = sort[0][0];\n const a0 = sort[0][1] === 'asc';\n const k1 = len > 1 ? sort[1][0] : '';\n const a1 = len > 1 ? sort[1][1] === 'asc' : true;\n\n return (a: RowBound, b: RowBound) => {\n const c0 = a0 ? compareBounds(a[k0], b[k0]) : compareBounds(b[k0], a[k0]);\n if (len === 1 || c0 !== 0) return c0;\n const c1 = a1 ? compareBounds(a[k1], b[k1]) : compareBounds(b[k1], a[k1]);\n if (len === 2 || c1 !== 0) return c1;\n // Hot! Do not use destructuring\n for (let i = 2; i < len; i++) {\n const cmp = compareBounds(a[sort[i][0]], b[sort[i][0]]);\n if (cmp !== 0) return sort[i][1] === 'asc' ? cmp : -cmp;\n }\n return 0;\n };\n}\n\nfunction compareBounds(a: Bound, b: Bound): number {\n if (a === b) {\n return 0;\n }\n // Use typeof to guard the Symbol sentinel checks first. This gives V8 a\n // clear type discriminant so the common non-symbol path compiles as a\n // specialised numeric/string fast-path without a Smi deopt when the\n // minValue/maxValue sentinel symbols appear.\n if (typeof a === 'symbol') {\n return a === minValue ? -1 : 1;\n }\n if (typeof b === 'symbol') {\n return b === minValue ? 1 : -1;\n }\n return compareValues(a, b);\n}\n\nfunction* generateRows(\n data: BTreeSet<Row>,\n scanStart: RowBound | undefined,\n reverse: boolean | undefined,\n) {\n yield* data[reverse ? 'valuesFromReversed' : 'valuesFrom'](\n scanStart as Row | undefined,\n );\n}\n\nexport function stringify(change: SourceChange) {\n return JSON.stringify(change, (_, v) =>\n typeof v === 'bigint' ? v.toString() : v,\n );\n}\n\n/**\n * N-way merge of pre-sorted Node streams. Each input stream must yield\n * Nodes in `compare` order (and may interleave 'yield' values, which are\n * forwarded as-is). The merged stream yields Nodes in `compare` order.\n *\n * Implemented as a binary min-heap keyed by `compare(entry.row, ...)`, so\n * each emit costs O(log K) (K = number of streams) rather than the O(K)\n * a linear-scan-of-heads merge would. Matters when FlippedJoin chunks a\n * large `multiConstraints` IN-list into hundreds of sub-fetches.\n *\n * If the merged stream is closed early (e.g. downstream `Take` `break`s\n * after hitting its limit, prompting JS to call `.return()` on this\n * generator), the `finally` block propagates `.return()` to each\n * non-exhausted sub-iterator so the underlying sources (SQLite cursors,\n * etc.) can run their cleanup. Without this, mid-merge early-termination\n * leaves cursors open, causing later writes on the same connection to\n * fail with \"database connection is busy executing a query\".\n */\nexport function* mergeSortedStreams(\n streams: readonly Stream<Node | 'yield'>[],\n compare: (a: Node, b: Node) => number,\n): Stream<Node | 'yield'> {\n const iterators: Iterator<Node | 'yield'>[] = streams.map(s =>\n s[Symbol.iterator](),\n );\n // True while iterators[i] hasn't yet returned `done`. The finally\n // block uses this to skip already-exhausted streams when propagating\n // `.return()`.\n const active: boolean[] = new Array(iterators.length).fill(true);\n\n // Min-heap of entries; `idx` tells us which stream to refill from\n // after the entry's row is emitted.\n type Entry = {row: Node; idx: number};\n const heap: Entry[] = [];\n\n const siftUp = (start: number) => {\n let i = start;\n while (i > 0) {\n const p = (i - 1) >> 1;\n if (compare(heap[i].row, heap[p].row) >= 0) return;\n const t = heap[i];\n heap[i] = heap[p];\n heap[p] = t;\n i = p;\n }\n };\n\n const siftDown = (start: number) => {\n let i = start;\n const n = heap.length;\n while (true) {\n const l = (i << 1) + 1;\n const r = l + 1;\n let smallest = i;\n if (l < n && compare(heap[l].row, heap[smallest].row) < 0) smallest = l;\n if (r < n && compare(heap[r].row, heap[smallest].row) < 0) smallest = r;\n if (smallest === i) return;\n const t = heap[i];\n heap[i] = heap[smallest];\n heap[smallest] = t;\n i = smallest;\n }\n };\n\n // Pull the next Node from iterator `idx`, forwarding any 'yield's.\n // Returns the Node, or `undefined` once the stream is exhausted.\n const pullNext = function* (\n idx: number,\n ): Generator<'yield', Node | undefined, undefined> {\n while (true) {\n const r = iterators[idx].next();\n if (r.done) {\n active[idx] = false;\n return undefined;\n }\n if (r.value === 'yield') {\n yield 'yield';\n continue;\n }\n return r.value;\n }\n };\n\n try {\n // Prime: push the first row of each non-empty stream onto the heap.\n for (let i = 0; i < iterators.length; i++) {\n const row = yield* pullNext(i);\n if (row !== undefined) {\n heap.push({row, idx: i});\n siftUp(heap.length - 1);\n }\n }\n\n while (heap.length > 0) {\n // Root is the global min across all active streams.\n const top = heap[0];\n yield top.row;\n const next = yield* pullNext(top.idx);\n if (next !== undefined) {\n // Refill root in place (top === heap[0]) and sift down. The\n // already-yielded `top.row` value is captured by the yield, so\n // mutating it here doesn't affect what was emitted.\n top.row = next;\n siftDown(0);\n } else {\n // Stream exhausted. Move tail to root and shrink. Pop returns\n // the last entry; if the heap had only one entry it was the\n // root we just yielded, so we just leave the heap empty.\n const last = must(heap.pop());\n if (heap.length > 0) {\n heap[0] = last;\n siftDown(0);\n }\n }\n }\n } finally {\n // Close any iterators that aren't already exhausted so their\n // `finally` blocks (which release cursors / cached statements) run.\n for (let i = 0; i < iterators.length; i++) {\n if (active[i]) {\n iterators[i].return?.();\n }\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"memory-source.js","names":["#tableName","#columns","#primaryKey","#primaryIndexSort","#indexes","#connections","#getPrimaryIndex","#fetch","#disconnect","#getSchema","#fetchMulti","#getOrCreateIndex","#overlay","#writeChange","#pushEpoch"],"sources":["../../../../../zql/src/ivm/memory-source.ts"],"sourcesContent":["import {assert, unreachable} from '../../../shared/src/asserts.ts';\nimport {BTreeSet} from '../../../shared/src/btree-set.ts';\nimport {hasOwn} from '../../../shared/src/has-own.ts';\nimport {once, toSorted} from '../../../shared/src/iterables.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport type {\n Condition,\n Ordering,\n OrderPart,\n} from '../../../zero-protocol/src/ast.ts';\nimport type {Row, Value} from '../../../zero-protocol/src/data.ts';\nimport type {PrimaryKey} from '../../../zero-protocol/src/primary-key.ts';\nimport type {SchemaValue} from '../../../zero-types/src/schema-value.ts';\nimport type {DebugDelegate} from '../builder/debug-delegate.ts';\nimport {\n createPredicate,\n transformFilters,\n type NoSubqueryCondition,\n} from '../builder/filter.ts';\nimport {assertOrderingIncludesPK} from '../query/complete-ordering.ts';\nimport {ChangeType} from './change-type.ts';\nimport type {Change} from './change.ts';\nimport {makeAddChange, makeEditChange, makeRemoveChange} from './change.ts';\nimport {\n constraintMatchesPrimaryKey,\n constraintMatchesRow,\n primaryKeyConstraintFromFilters,\n type Constraint,\n} from './constraint.ts';\nimport {\n compareValues,\n makeComparator,\n valuesEqual,\n type Comparator,\n type Node,\n} from './data.ts';\nimport {filterPush} from './filter-push.ts';\nimport {\n skipYields,\n type FetchRequest,\n type Input,\n type MultiConstraint,\n type Output,\n type Start,\n} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport {SourceChangeIndex} from './source-change-index.ts';\nimport type {\n Source,\n SourceChange,\n SourceChangeAdd,\n SourceChangeEdit,\n SourceChangeRemove,\n SourceInput,\n} from './source.ts';\nimport {makeSourceChangeAdd, makeSourceChangeRemove} from './source.ts';\nimport type {Stream} from './stream.ts';\n\nexport type Overlay = {\n epoch: number;\n change: SourceChange;\n};\n\nexport type Overlays = {\n add: Row | undefined;\n remove: Row | undefined;\n};\n\ntype Index = {\n comparator: Comparator;\n data: BTreeSet<Row>;\n usedBy: Set<Connection>;\n};\n\nexport type Connection = {\n input: Input;\n output: Output | undefined;\n sort?: Ordering | undefined;\n splitEditKeys: Set<string> | undefined;\n compareRows: Comparator;\n filters:\n | {\n condition: NoSubqueryCondition;\n predicate: (row: Row) => boolean;\n }\n | undefined;\n readonly debug?: DebugDelegate | undefined;\n lastPushedEpoch: number;\n};\n\n/**\n * A `MemorySource` is a source that provides data to the pipeline from an\n * in-memory data source.\n *\n * This data is kept in sorted order as downstream pipelines will always expect\n * the data they receive from `pull` to be in sorted order.\n */\nexport class MemorySource implements Source {\n readonly #tableName: string;\n readonly #columns: Record<string, SchemaValue>;\n readonly #primaryKey: PrimaryKey;\n readonly #primaryIndexSort: Ordering;\n readonly #indexes: Map<string, Index> = new Map();\n readonly #connections: Connection[] = [];\n\n #overlay: Overlay | undefined;\n #pushEpoch = 0;\n\n constructor(\n tableName: string,\n columns: Record<string, SchemaValue>,\n primaryKey: PrimaryKey,\n primaryIndexData?: BTreeSet<Row>,\n ) {\n this.#tableName = tableName;\n this.#columns = columns;\n this.#primaryKey = primaryKey;\n this.#primaryIndexSort = primaryKey.map(k => [k, 'asc']);\n const comparator = makeBoundComparator(this.#primaryIndexSort);\n this.#indexes.set(JSON.stringify(this.#primaryIndexSort), {\n comparator,\n data: primaryIndexData ?? new BTreeSet<Row>(comparator),\n usedBy: new Set(),\n });\n }\n\n get tableSchema() {\n return {\n name: this.#tableName,\n columns: this.#columns,\n primaryKey: this.#primaryKey,\n };\n }\n\n fork() {\n const primaryIndex = this.#getPrimaryIndex();\n return new MemorySource(\n this.#tableName,\n this.#columns,\n this.#primaryKey,\n primaryIndex.data.clone(),\n );\n }\n\n get data(): BTreeSet<Row> {\n return this.#getPrimaryIndex().data;\n }\n\n #getSchema(connection: Connection, unordered: boolean): SourceSchema {\n return {\n tableName: this.#tableName,\n columns: this.#columns,\n primaryKey: this.#primaryKey,\n sort: unordered ? undefined : connection.sort,\n system: 'client',\n relationships: {},\n isHidden: false,\n compareRows: connection.compareRows,\n };\n }\n\n connect(\n sort: Ordering | undefined,\n filters?: Condition,\n splitEditKeys?: Set<string>,\n ): SourceInput {\n const transformedFilters = transformFilters(filters);\n const unordered = sort === undefined;\n const internalSort = sort ?? this.#primaryIndexSort;\n\n const input: SourceInput = {\n getSchema: () => schema,\n fetch: req => this.#fetch(req, connection),\n setOutput: output => {\n connection.output = output;\n },\n destroy: () => {\n this.#disconnect(input);\n },\n fullyAppliedFilters: !transformedFilters.conditionsRemoved,\n };\n\n const connection: Connection = {\n input,\n output: undefined,\n sort: internalSort,\n splitEditKeys,\n compareRows: makeComparator(internalSort),\n filters: transformedFilters.filters\n ? {\n condition: transformedFilters.filters,\n predicate: createPredicate(transformedFilters.filters),\n }\n : undefined,\n lastPushedEpoch: 0,\n };\n const schema = this.#getSchema(connection, unordered);\n if (!unordered) {\n assertOrderingIncludesPK(internalSort, this.#primaryKey);\n }\n this.#connections.push(connection);\n return input;\n }\n\n #disconnect(input: Input): void {\n const idx = this.#connections.findIndex(c => c.input === input);\n assert(idx !== -1, 'Connection not found');\n this.#connections.splice(idx, 1);\n\n // TODO: We used to delete unused indexes here. But in common cases like\n // navigating into issue detail pages it caused a ton of constantly\n // building and destroying indexes.\n //\n // Perhaps some intelligent LRU or something is needed here but for now,\n // the opposite extreme of keeping all indexes for the lifetime of the\n // page seems better.\n }\n\n #getPrimaryIndex(): Index {\n const index = this.#indexes.get(JSON.stringify(this.#primaryIndexSort));\n assert(index, 'Primary index not found');\n return index;\n }\n\n #getOrCreateIndex(sort: Ordering, usedBy: Connection): Index {\n const key = JSON.stringify(sort);\n const index = this.#indexes.get(key);\n // Future optimization could use existing index if it's the same just sorted\n // in reverse of needed.\n if (index) {\n index.usedBy.add(usedBy);\n return index;\n }\n\n const comparator = makeBoundComparator(sort);\n\n // When creating these synchronously becomes a problem, a few options:\n // 1. Allow users to specify needed indexes up front\n // 2. Create indexes in a different thread asynchronously (this would require\n // modifying the BTree to be able to be passed over structured-clone, or using\n // a different library.)\n // 3. We could even theoretically do (2) on multiple threads and then merge the\n // results!\n const rows = toSorted(this.#getPrimaryIndex().data, comparator);\n const data = BTreeSet.fromSorted(comparator, rows);\n\n const newIndex = {comparator, data, usedBy: new Set([usedBy])};\n this.#indexes.set(key, newIndex);\n return newIndex;\n }\n\n // For unit testing that we correctly clean up indexes.\n getIndexKeys(): string[] {\n return [...this.#indexes.keys()];\n }\n\n *#fetch(req: FetchRequest, conn: Connection): Stream<Node | 'yield'> {\n // multiConstraints is handled by driving sub-fetches off the first\n // entry's values and post-filtering matches against any remaining\n // entries. TableSource implements multi-IN natively via SQL `AND` of\n // `IN` clauses; the in-memory path is correct but not specially\n // optimized — it pays the index-seek cost per primary value, then\n // walks each result against the rest.\n if (\n req.multiConstraints &&\n req.multiConstraints.some(mc => mc.length > 0)\n ) {\n yield* this.#fetchMulti(req, conn);\n return;\n }\n const requestedSort = must(conn.sort);\n const {compareRows} = conn;\n // Avoid allocating a new closure when not reversing (the common case).\n const connectionComparator: Comparator = req.reverse\n ? (r1, r2) => compareRows(r2, r1)\n : compareRows;\n\n const pkConstraint = primaryKeyConstraintFromFilters(\n conn.filters?.condition,\n this.#primaryKey,\n );\n // The primary key constraint will be more limiting than the constraint\n // so swap out to that if it exists.\n const fetchOrPkConstraint = pkConstraint ?? req.constraint;\n\n // If there is a constraint, we need an index sorted by it first.\n const indexSort: OrderPart[] = [];\n if (fetchOrPkConstraint) {\n for (const key of Object.keys(fetchOrPkConstraint)) {\n indexSort.push([key, 'asc']);\n }\n }\n\n // For the special case of constraining by PK, we don't need to worry about\n // any requested sort since there can only be one result. Otherwise we also\n // need the index sorted by the requested sort.\n if (\n this.#primaryKey.length > 1 ||\n !fetchOrPkConstraint ||\n !constraintMatchesPrimaryKey(fetchOrPkConstraint, this.#primaryKey)\n ) {\n indexSort.push(...requestedSort);\n }\n\n const index = this.#getOrCreateIndex(indexSort, conn);\n const {data, comparator: compare} = index;\n // Avoid allocating a new closure when not reversing (the common case).\n const indexComparator: Comparator = req.reverse\n ? (r1, r2) => compare(r2, r1)\n : compare;\n\n const startAt = req.start?.row;\n\n // If there is a constraint, we want to start our scan at the first row that\n // matches the constraint. But because the next OrderPart can be `desc`,\n // it's not true that {[constraintKey]: constraintValue} is the first\n // matching row. Because in that case, the other fields will all be\n // `undefined`, and in Zero `undefined` is always less than any other value.\n // So if the second OrderPart is descending then `undefined` values will\n // actually be the *last* row. We need a way to stay \"start at the first row\n // with this constraint value\". RowBound with the corresponding compareBound\n // comparator accomplishes this. The right thing is probably to teach the\n // btree library to support this concept.\n let scanStart: RowBound | undefined;\n\n if (fetchOrPkConstraint) {\n scanStart = {};\n for (const [key, dir] of indexSort) {\n if (hasOwn(fetchOrPkConstraint, key)) {\n scanStart[key] = fetchOrPkConstraint[key];\n } else {\n if (req.reverse) {\n scanStart[key] = dir === 'asc' ? maxValue : minValue;\n } else {\n scanStart[key] = dir === 'asc' ? minValue : maxValue;\n }\n }\n }\n } else {\n scanStart = startAt;\n }\n\n const rowsIterable = generateRows(data, scanStart, req.reverse);\n const withOverlay = generateWithOverlay(\n startAt,\n pkConstraint ? once(rowsIterable) : rowsIterable,\n // use `req.constraint` here and not `fetchOrPkConstraint` since `fetchOrPkConstraint` could be the\n // primary key constraint. The primary key constraint comes from filters and is acting as a filter\n // rather than as the fetch constraint.\n req.constraint,\n this.#overlay,\n conn.lastPushedEpoch,\n // Use indexComparator, generateWithOverlayInner has a subtle dependency\n // on this. Since generateWithConstraint is done after\n // generateWithOverlay, the generator consumed by generateWithOverlayInner\n // does not end when the constraint stops matching and so the final\n // check to yield an add overlay if not yet yielded is not reached.\n // However, using the indexComparator the add overlay will be less than\n // the first row that does not match the constraint, and so any\n // not yet yielded add overlay will be yielded when the first row\n // not matching the constraint is reached.\n indexComparator,\n conn.filters?.predicate,\n );\n\n const withConstraint = generateWithConstraint(\n skipYields(\n generateWithStart(withOverlay, req.start, connectionComparator),\n ),\n // we use `req.constraint` and not `fetchOrPkConstraint` here because we need to\n // AND the constraint with what could have been the primary key constraint\n req.constraint,\n );\n\n yield* conn.filters\n ? generateWithFilter(withConstraint, conn.filters.predicate)\n : withConstraint;\n }\n\n *#fetchMulti(req: FetchRequest, conn: Connection): Stream<Node | 'yield'> {\n // Caller (`#fetch`) guards entry on `req.multiConstraints.some(mc =>\n // mc.length > 0)`, so `multis` is guaranteed non-empty after the\n // empty-entry filter. Per the MultiConstraint contract (operator.ts),\n // entries are non-empty, share key shape, are unique, and are\n // key-compatible with `req.constraint`.\n const multis = must(req.multiConstraints).filter(mc => mc.length > 0);\n const primary = multis[0];\n const rest = multis.slice(1);\n const baseConstraint = req.constraint;\n\n // Drive sub-fetches off `primary`. Within each sub-fetch the source\n // can use its index for `primary`; we then post-filter rows by\n // checking they also match every entry in `rest`. Per the\n // MultiConstraint contract (see operator.ts), entries are unique and\n // key-compatible with `baseConstraint`, so we don't dedupe or check\n // compatibility here.\n const subStreams: Stream<Node | 'yield'>[] = primary.map(c => {\n const merged: Constraint = baseConstraint ? {...baseConstraint, ...c} : c;\n return this.#fetch(\n {...req, constraint: merged, multiConstraints: undefined},\n conn,\n );\n });\n const merged = mergeSortedStreams(\n subStreams,\n req.reverse\n ? (a, b) => conn.compareRows(b.row, a.row)\n : (a, b) => conn.compareRows(a.row, b.row),\n );\n\n if (rest.length === 0) {\n yield* merged;\n return;\n }\n\n for (const node of merged) {\n if (node === 'yield') {\n yield 'yield';\n continue;\n }\n let matchesAll = true;\n for (const mc of rest) {\n let any = false;\n for (const c of mc) {\n if (constraintMatchesRow(c, node.row)) {\n any = true;\n break;\n }\n }\n if (!any) {\n matchesAll = false;\n break;\n }\n }\n if (matchesAll) yield node;\n }\n }\n\n *push(change: SourceChange): Stream<'yield'> {\n for (const result of this.genPush(change)) {\n if (result === 'yield') {\n yield result;\n }\n }\n }\n\n *genPush(change: SourceChange) {\n const primaryIndex = this.#getPrimaryIndex();\n const {data} = primaryIndex;\n const exists = (row: Row) => data.has(row);\n const setOverlay = (o: Overlay | undefined) => (this.#overlay = o);\n const writeChange = (c: SourceChange) => this.#writeChange(c);\n yield* genPushAndWriteWithSplitEdit(\n this.#connections,\n change,\n exists,\n setOverlay,\n writeChange,\n () => ++this.#pushEpoch,\n );\n }\n\n #writeChange(change: SourceChange) {\n for (const {data} of this.#indexes.values()) {\n switch (change[SourceChangeIndex.TYPE]) {\n case ChangeType.ADD: {\n const added = data.add(change[SourceChangeIndex.ROW]);\n // must succeed since we checked has() above.\n assert(\n added,\n 'MemorySource: add must succeed since row existence was already checked',\n );\n break;\n }\n case ChangeType.REMOVE: {\n const removed = data.delete(change[SourceChangeIndex.ROW]);\n // must succeed since we checked has() above.\n assert(\n removed,\n 'MemorySource: remove must succeed since row existence was already checked',\n );\n break;\n }\n case ChangeType.EDIT: {\n // TODO: We could see if the PK (form the index tree's perspective)\n // changed and if not we could use set.\n // We cannot just do `set` with the new value since the `oldRow` might\n // not map to the same entry as the new `row` in the index btree.\n const removed = data.delete(change[SourceChangeIndex.OLD_ROW]);\n // must succeed since we checked has() above.\n assert(\n removed,\n 'MemorySource: edit remove must succeed since row existence was already checked',\n );\n data.add(change[SourceChangeIndex.ROW]);\n break;\n }\n default:\n unreachable(change);\n }\n }\n }\n}\n\nfunction* generateWithConstraint(\n it: Stream<Node>,\n constraint: Constraint | undefined,\n) {\n for (const node of it) {\n if (constraint && !constraintMatchesRow(constraint, node.row)) {\n break;\n }\n yield node;\n }\n}\n\nfunction* generateWithFilter(it: Stream<Node>, filter: (row: Row) => boolean) {\n for (const node of it) {\n if (filter(node.row)) {\n yield node;\n }\n }\n}\n\nexport function* genPushAndWriteWithSplitEdit(\n connections: readonly Connection[],\n change: SourceChange,\n exists: (row: Row) => boolean,\n setOverlay: (o: Overlay | undefined) => Overlay | undefined,\n writeChange: (c: SourceChange) => void,\n getNextEpoch: () => number,\n) {\n let shouldSplitEdit = false;\n if (change[SourceChangeIndex.TYPE] === ChangeType.EDIT) {\n for (const {splitEditKeys} of connections) {\n if (splitEditKeys) {\n for (const key of splitEditKeys) {\n if (\n !valuesEqual(\n change[SourceChangeIndex.ROW][key],\n change[SourceChangeIndex.OLD_ROW][key],\n )\n ) {\n shouldSplitEdit = true;\n break;\n }\n }\n }\n }\n }\n\n if (change[SourceChangeIndex.TYPE] === ChangeType.EDIT && shouldSplitEdit) {\n yield* genPushAndWrite(\n connections,\n makeSourceChangeRemove(change[SourceChangeIndex.OLD_ROW]),\n exists,\n setOverlay,\n writeChange,\n getNextEpoch(),\n );\n yield* genPushAndWrite(\n connections,\n makeSourceChangeAdd(change[SourceChangeIndex.ROW]),\n exists,\n setOverlay,\n writeChange,\n getNextEpoch(),\n );\n } else {\n yield* genPushAndWrite(\n connections,\n change,\n exists,\n setOverlay,\n writeChange,\n getNextEpoch(),\n );\n }\n}\n\nfunction* genPushAndWrite(\n connections: readonly Connection[],\n change: SourceChangeAdd | SourceChangeRemove | SourceChangeEdit,\n exists: (row: Row) => boolean,\n setOverlay: (o: Overlay | undefined) => Overlay | undefined,\n writeChange: (c: SourceChange) => void,\n pushEpoch: number,\n) {\n for (const x of genPush(connections, change, exists, setOverlay, pushEpoch)) {\n yield x;\n }\n writeChange(change);\n}\n\nfunction* genPush(\n connections: readonly Connection[],\n change: SourceChange,\n exists: (row: Row) => boolean,\n setOverlay: (o: Overlay | undefined) => void,\n pushEpoch: number,\n) {\n switch (change[SourceChangeIndex.TYPE]) {\n case ChangeType.ADD:\n assert(\n !exists(change[SourceChangeIndex.ROW]),\n () => `Row already exists ${stringify(change)}`,\n );\n break;\n case ChangeType.REMOVE:\n assert(\n exists(change[SourceChangeIndex.ROW]),\n () => `Row not found ${stringify(change)}`,\n );\n break;\n case ChangeType.EDIT:\n assert(\n exists(change[SourceChangeIndex.OLD_ROW]),\n () => `Row not found ${stringify(change)}`,\n );\n break;\n default:\n unreachable(change);\n }\n\n for (const conn of connections) {\n const {output, filters, input} = conn;\n if (output) {\n conn.lastPushedEpoch = pushEpoch;\n setOverlay({epoch: pushEpoch, change});\n const outputChange: Change =\n change[SourceChangeIndex.TYPE] === ChangeType.EDIT\n ? makeEditChange(\n {row: change[SourceChangeIndex.ROW], relationships: {}},\n {row: change[SourceChangeIndex.OLD_ROW], relationships: {}},\n )\n : change[SourceChangeIndex.TYPE] === ChangeType.ADD\n ? makeAddChange({\n row: change[SourceChangeIndex.ROW],\n relationships: {},\n })\n : makeRemoveChange({\n row: change[SourceChangeIndex.ROW],\n relationships: {},\n });\n yield* filterPush(outputChange, output, input, filters?.predicate);\n yield undefined;\n }\n }\n\n setOverlay(undefined);\n}\n\nexport function* generateWithStart(\n nodes: Iterable<Node | 'yield'>,\n start: Start | undefined,\n compare: (r1: Row, r2: Row) => number,\n): Stream<Node | 'yield'> {\n if (!start) {\n yield* nodes;\n return;\n }\n let started = false;\n for (const node of nodes) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n if (!started) {\n if (start.basis === 'at') {\n if (compare(node.row, start.row) >= 0) {\n started = true;\n }\n } else if (start.basis === 'after') {\n if (compare(node.row, start.row) > 0) {\n started = true;\n }\n }\n }\n if (started) {\n yield node;\n }\n }\n}\n\n/**\n * Takes an iterator and overlay.\n * Splices the overlay into the iterator at the correct position.\n *\n * @param startAt - if there is a lower bound to the stream. If the lower bound of the stream\n * is above the overlay, the overlay will be skipped.\n * @param rows - the stream into which the overlay should be spliced\n * @param constraint - constraint that was applied to the rowIterator and should\n * also be applied to the overlay.\n * @param overlay - the overlay values to splice in\n * @param compare - the comparator to use to find the position for the overlay\n */\nexport function* generateWithOverlay(\n startAt: Row | undefined,\n rows: Iterable<Row>,\n constraint: Constraint | undefined,\n overlay: Overlay | undefined,\n lastPushedEpoch: number,\n compare: Comparator,\n filterPredicate?: (row: Row) => boolean | undefined,\n multiConstraints?: readonly MultiConstraint[] | undefined,\n) {\n let overlayToApply: Overlay | undefined = undefined;\n if (overlay && lastPushedEpoch >= overlay.epoch) {\n overlayToApply = overlay;\n }\n const overlays = computeOverlays(\n startAt,\n constraint,\n overlayToApply,\n compare,\n filterPredicate,\n multiConstraints,\n );\n yield* generateWithOverlayInner(rows, overlays, compare);\n}\n\nfunction computeOverlays(\n startAt: Row | undefined,\n constraint: Constraint | undefined,\n overlay: Overlay | undefined,\n compare: Comparator,\n filterPredicate?: (row: Row) => boolean | undefined,\n multiConstraints?: readonly MultiConstraint[] | undefined,\n): Overlays {\n let overlays: Overlays = {\n add: undefined,\n remove: undefined,\n };\n switch (overlay?.change[SourceChangeIndex.TYPE]) {\n case ChangeType.ADD:\n overlays = {\n add: overlay.change[SourceChangeIndex.ROW],\n remove: undefined,\n };\n break;\n case ChangeType.REMOVE:\n overlays = {\n add: undefined,\n remove: overlay.change[SourceChangeIndex.ROW],\n };\n break;\n case ChangeType.EDIT:\n overlays = {\n add: overlay.change[SourceChangeIndex.ROW],\n remove: overlay.change[SourceChangeIndex.OLD_ROW],\n };\n break;\n }\n\n if (startAt) {\n overlays = overlaysForStartAt(overlays, startAt, compare);\n }\n\n if (constraint) {\n overlays = overlaysForConstraint(overlays, constraint);\n }\n\n overlays = applyMultiConstraintsToOverlays(overlays, multiConstraints);\n\n if (filterPredicate) {\n overlays = overlaysForFilterPredicate(overlays, filterPredicate);\n }\n\n return overlays;\n}\n\nfunction applyMultiConstraintsToOverlays(\n overlays: Overlays,\n multiConstraints: readonly MultiConstraint[] | undefined,\n): Overlays {\n if (!multiConstraints) return overlays;\n for (const mc of multiConstraints) {\n if (mc.length > 0) {\n overlays = overlaysForMultiConstraint(overlays, mc);\n }\n }\n return overlays;\n}\n\nexport {overlaysForMultiConstraint as overlaysForMultiConstraintForTest};\n\nfunction overlaysForMultiConstraint(\n {add, remove}: Overlays,\n multiConstraint: MultiConstraint,\n): Overlays {\n const matchesAny = (row: Row | undefined) => {\n if (row === undefined) return false;\n for (const c of multiConstraint) {\n if (constraintMatchesRow(c, row)) return true;\n }\n return false;\n };\n return {\n add: matchesAny(add) ? add : undefined,\n remove: matchesAny(remove) ? remove : undefined,\n };\n}\n\nexport {overlaysForStartAt as overlaysForStartAtForTest};\n\nfunction overlaysForStartAt(\n {add, remove}: Overlays,\n startAt: Row,\n compare: Comparator,\n): Overlays {\n const undefinedIfBeforeStartAt = (row: Row | undefined) =>\n row === undefined || compare(row, startAt) < 0 ? undefined : row;\n return {\n add: undefinedIfBeforeStartAt(add),\n remove: undefinedIfBeforeStartAt(remove),\n };\n}\n\nexport {overlaysForConstraint as overlaysForConstraintForTest};\n\nfunction overlaysForConstraint(\n {add, remove}: Overlays,\n constraint: Constraint,\n): Overlays {\n const undefinedIfDoesntMatchConstraint = (row: Row | undefined) =>\n row === undefined || !constraintMatchesRow(constraint, row)\n ? undefined\n : row;\n\n return {\n add: undefinedIfDoesntMatchConstraint(add),\n remove: undefinedIfDoesntMatchConstraint(remove),\n };\n}\n\nfunction overlaysForFilterPredicate(\n {add, remove}: Overlays,\n filterPredicate: (row: Row) => boolean | undefined,\n): Overlays {\n const undefinedIfDoesntMatchFilter = (row: Row | undefined) =>\n row === undefined || !filterPredicate(row) ? undefined : row;\n\n return {\n add: undefinedIfDoesntMatchFilter(add),\n remove: undefinedIfDoesntMatchFilter(remove),\n };\n}\n\nexport function* generateWithOverlayInner(\n rowIterator: Iterable<Row>,\n overlays: Overlays,\n compare: (r1: Row, r2: Row) => number,\n) {\n let addOverlayYielded = false;\n let removeOverlaySkipped = false;\n for (const row of rowIterator) {\n if (!addOverlayYielded && overlays.add) {\n const cmp = compare(overlays.add, row);\n if (cmp < 0) {\n addOverlayYielded = true;\n yield {row: overlays.add, relationships: {}};\n }\n }\n\n if (!removeOverlaySkipped && overlays.remove) {\n const cmp = compare(overlays.remove, row);\n if (cmp === 0) {\n removeOverlaySkipped = true;\n continue;\n }\n }\n yield {row, relationships: {}};\n }\n\n if (!addOverlayYielded && overlays.add) {\n yield {row: overlays.add, relationships: {}};\n }\n}\n\n/**\n * Like {@link generateWithOverlay} but for unordered streams.\n * No `startAt` or comparator needed. Injects remove/old-edit rows eagerly\n * at the start, and suppresses add/new-edit rows inline by PK match.\n */\nexport function* generateWithOverlayUnordered(\n rows: Iterable<Row>,\n constraint: Constraint | undefined,\n overlay: Overlay | undefined,\n lastPushedEpoch: number,\n primaryKey: PrimaryKey,\n filterPredicate?: (row: Row) => boolean,\n multiConstraints?: readonly MultiConstraint[] | undefined,\n) {\n let overlayToApply: Overlay | undefined = undefined;\n if (overlay && lastPushedEpoch >= overlay.epoch) {\n overlayToApply = overlay;\n }\n let overlays: Overlays = {add: undefined, remove: undefined};\n switch (overlayToApply?.change[SourceChangeIndex.TYPE]) {\n case ChangeType.ADD:\n overlays = {\n add: overlayToApply.change[SourceChangeIndex.ROW],\n remove: undefined,\n };\n break;\n case ChangeType.REMOVE:\n overlays = {\n add: undefined,\n remove: overlayToApply.change[SourceChangeIndex.ROW],\n };\n break;\n case ChangeType.EDIT:\n overlays = {\n add: overlayToApply.change[SourceChangeIndex.ROW],\n remove: overlayToApply.change[SourceChangeIndex.OLD_ROW],\n };\n break;\n }\n if (constraint) {\n overlays = overlaysForConstraint(overlays, constraint);\n }\n overlays = applyMultiConstraintsToOverlays(overlays, multiConstraints);\n if (filterPredicate) {\n overlays = overlaysForFilterPredicate(overlays, filterPredicate);\n }\n yield* generateWithOverlayInnerUnordered(rows, overlays, primaryKey);\n}\n\nexport function* generateWithOverlayInnerUnordered(\n rowIterator: Iterable<Row>,\n overlays: Overlays,\n primaryKey: PrimaryKey,\n) {\n // Eager inject: yield the add overlay at the start (row not yet in storage)\n if (overlays.add) {\n yield {row: overlays.add, relationships: {}};\n }\n // Stream with inline suppress: skip the remove overlay (row still in storage)\n let removeSkipped = false;\n for (const row of rowIterator) {\n if (\n !removeSkipped &&\n overlays.remove &&\n rowMatchesPK(overlays.remove, row, primaryKey)\n ) {\n removeSkipped = true;\n continue;\n }\n yield {row, relationships: {}};\n }\n}\n\nfunction rowMatchesPK(a: Row, b: Row, primaryKey: PrimaryKey): boolean {\n for (const key of primaryKey) {\n if (!valuesEqual(a[key], b[key])) {\n return false;\n }\n }\n return true;\n}\n\n/**\n * A location to begin scanning an index from. Can either be a specific value\n * or the min or max possible value for the type. This is used to start a scan\n * at the beginning of the rows matching a constraint.\n */\ntype Bound = Value | MinValue | MaxValue;\ntype RowBound = Record<string, Bound>;\nconst minValue = Symbol('min-value');\ntype MinValue = typeof minValue;\nconst maxValue = Symbol('max-value');\ntype MaxValue = typeof maxValue;\n\nfunction makeBoundComparator(sort: Ordering): Comparator {\n // Pre-extract the first two keys/directions to avoid per-call array access.\n // All paths share one function literal (single SFI) so the BTree comparator call site\n // stays monomorphic across indexes with different sort orderings, preventing V8 IC deopt.\n // Even a 2-SFI split (e.g. separate len=1 path) creates a polymorphic IC that\n // measurably regresses performance, so we keep a single return body.\n const len = sort.length;\n const k0 = sort[0][0];\n const a0 = sort[0][1] === 'asc';\n const k1 = len > 1 ? sort[1][0] : '';\n const a1 = len > 1 ? sort[1][1] === 'asc' : true;\n\n return (a: RowBound, b: RowBound) => {\n const c0 = a0 ? compareBounds(a[k0], b[k0]) : compareBounds(b[k0], a[k0]);\n if (len === 1 || c0 !== 0) return c0;\n const c1 = a1 ? compareBounds(a[k1], b[k1]) : compareBounds(b[k1], a[k1]);\n if (len === 2 || c1 !== 0) return c1;\n // Hot! Do not use destructuring\n for (let i = 2; i < len; i++) {\n const cmp = compareBounds(a[sort[i][0]], b[sort[i][0]]);\n if (cmp !== 0) return sort[i][1] === 'asc' ? cmp : -cmp;\n }\n return 0;\n };\n}\n\nfunction compareBounds(a: Bound, b: Bound): number {\n if (a === b) {\n return 0;\n }\n // Use typeof to guard the Symbol sentinel checks first. This gives V8 a\n // clear type discriminant so the common non-symbol path compiles as a\n // specialised numeric/string fast-path without a Smi deopt when the\n // minValue/maxValue sentinel symbols appear.\n if (typeof a === 'symbol') {\n return a === minValue ? -1 : 1;\n }\n if (typeof b === 'symbol') {\n return b === minValue ? 1 : -1;\n }\n return compareValues(a, b);\n}\n\nfunction* generateRows(\n data: BTreeSet<Row>,\n scanStart: RowBound | undefined,\n reverse: boolean | undefined,\n) {\n yield* data[reverse ? 'valuesFromReversed' : 'valuesFrom'](\n scanStart as Row | undefined,\n );\n}\n\nexport function stringify(change: SourceChange) {\n return JSON.stringify(change, (_, v) =>\n typeof v === 'bigint' ? v.toString() : v,\n );\n}\n\n/**\n * N-way merge of pre-sorted Node streams. Each input stream must yield\n * Nodes in `compare` order (and may interleave 'yield' values, which are\n * forwarded as-is). The merged stream yields Nodes in `compare` order.\n *\n * Implemented as a binary min-heap keyed by `compare(entry.row, ...)`, so\n * each emit costs O(log K) (K = number of streams) rather than the O(K)\n * a linear-scan-of-heads merge would. Matters when FlippedJoin chunks a\n * large `multiConstraints` IN-list into hundreds of sub-fetches.\n *\n * If the merged stream is closed early (e.g. downstream `Take` `break`s\n * after hitting its limit, prompting JS to call `.return()` on this\n * generator), the `finally` block propagates `.return()` to each\n * non-exhausted sub-iterator so the underlying sources (SQLite cursors,\n * etc.) can run their cleanup. Without this, mid-merge early-termination\n * leaves cursors open, causing later writes on the same connection to\n * fail with \"database connection is busy executing a query\".\n */\nexport function* mergeSortedStreams(\n streams: readonly Stream<Node | 'yield'>[],\n compare: (a: Node, b: Node) => number,\n): Stream<Node | 'yield'> {\n const iterators: Iterator<Node | 'yield'>[] = streams.map(s =>\n s[Symbol.iterator](),\n );\n // True while iterators[i] hasn't yet returned `done`. The finally\n // block uses this to skip already-exhausted streams when propagating\n // `.return()`.\n const active: boolean[] = new Array(iterators.length).fill(true);\n\n // Min-heap of entries; `idx` tells us which stream to refill from\n // after the entry's row is emitted.\n type Entry = {row: Node; idx: number};\n const heap: Entry[] = [];\n\n const siftUp = (start: number) => {\n let i = start;\n while (i > 0) {\n const p = (i - 1) >> 1;\n if (compare(heap[i].row, heap[p].row) >= 0) return;\n const t = heap[i];\n heap[i] = heap[p];\n heap[p] = t;\n i = p;\n }\n };\n\n const siftDown = (start: number) => {\n let i = start;\n const n = heap.length;\n while (true) {\n const l = (i << 1) + 1;\n const r = l + 1;\n let smallest = i;\n if (l < n && compare(heap[l].row, heap[smallest].row) < 0) smallest = l;\n if (r < n && compare(heap[r].row, heap[smallest].row) < 0) smallest = r;\n if (smallest === i) return;\n const t = heap[i];\n heap[i] = heap[smallest];\n heap[smallest] = t;\n i = smallest;\n }\n };\n\n // Pull the next Node from iterator `idx`, forwarding any 'yield's.\n // Returns the Node, or `undefined` once the stream is exhausted.\n const pullNext = function* (\n idx: number,\n ): Generator<'yield', Node | undefined, undefined> {\n while (true) {\n const r = iterators[idx].next();\n if (r.done) {\n active[idx] = false;\n return undefined;\n }\n if (r.value === 'yield') {\n yield 'yield';\n continue;\n }\n return r.value;\n }\n };\n\n try {\n // Prime: push the first row of each non-empty stream onto the heap.\n for (let i = 0; i < iterators.length; i++) {\n const row = yield* pullNext(i);\n if (row !== undefined) {\n heap.push({row, idx: i});\n siftUp(heap.length - 1);\n }\n }\n\n while (heap.length > 0) {\n // Root is the global min across all active streams.\n const top = heap[0];\n yield top.row;\n const next = yield* pullNext(top.idx);\n if (next !== undefined) {\n // Refill root in place (top === heap[0]) and sift down. The\n // already-yielded `top.row` value is captured by the yield, so\n // mutating it here doesn't affect what was emitted.\n top.row = next;\n siftDown(0);\n } else {\n // Stream exhausted. Move tail to root and shrink. Pop returns\n // the last entry; if the heap had only one entry it was the\n // root we just yielded, so we just leave the heap empty.\n const last = must(heap.pop());\n if (heap.length > 0) {\n heap[0] = last;\n siftDown(0);\n }\n }\n }\n } finally {\n // Close any iterators that aren't already exhausted so their\n // `finally` blocks (which release cursors / cached statements) run.\n for (let i = 0; i < iterators.length; i++) {\n if (active[i]) {\n iterators[i].return?.();\n }\n }\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"memory-storage.js","names":["#data"],"sources":["../../../../../zql/src/ivm/memory-storage.ts"],"sourcesContent":["import {compareUTF8} from 'compare-utf8';\nimport {BTreeSet} from '../../../shared/src/btree-set.ts';\nimport type {JSONValue} from '../../../shared/src/json.ts';\nimport type {Storage} from './operator.ts';\nimport type {Stream} from './stream.ts';\n\ntype Entry = [key: string, value: JSONValue];\n\nfunction comparator(a: Entry, b: Entry): number {\n return compareUTF8(a[0], b[0]);\n}\n\n/**\n * MemoryStorage is a simple in-memory implementation of `Storage` for use\n * on the client and in tests.\n */\nexport class MemoryStorage implements Storage {\n #data: BTreeSet<Entry> = new BTreeSet(comparator);\n\n set(key: string, value: JSONValue) {\n this.#data.add([key, value]);\n }\n\n get(key: string, def?: JSONValue): JSONValue | undefined {\n const r = this.#data.get([key, null]);\n if (r !== undefined) {\n return r[1];\n }\n return def;\n }\n\n del(key: string) {\n this.#data.delete([key, null]);\n }\n\n *scan(options?: {prefix: string}): Stream<[string, JSONValue]> {\n for (const entry of this.#data.valuesFrom(\n options && [options.prefix, null],\n )) {\n if (options && !entry[0].startsWith(options.prefix)) {\n return;\n }\n yield entry;\n }\n }\n\n cloneData(): Record<string, JSONValue> {\n return structuredClone(Object.fromEntries(this.#data.values()));\n }\n}\n"],"mappings":";;;AAQA,SAAS,WAAW,GAAU,GAAkB;CAC9C,OAAO,YAAY,EAAE,IAAI,EAAE,EAAE;AAC/B;;;;;AAMA,IAAa,gBAAb,MAA8C;CAC5C,QAAyB,IAAI,SAAS,UAAU;CAEhD,IAAI,KAAa,OAAkB;EACjC,KAAKA,MAAM,IAAI,CAAC,KAAK,KAAK,CAAC;CAC7B;CAEA,IAAI,KAAa,KAAwC;EACvD,MAAM,IAAI,KAAKA,MAAM,IAAI,CAAC,KAAK,IAAI,CAAC;EACpC,IAAI,MAAM,KAAA,GACR,OAAO,EAAE;EAEX,OAAO;CACT;CAEA,IAAI,KAAa;EACf,KAAKA,MAAM,OAAO,CAAC,KAAK,IAAI,CAAC;CAC/B;CAEA,CAAC,KAAK,SAAyD;EAC7D,KAAK,MAAM,SAAS,KAAKA,MAAM,WAC7B,WAAW,CAAC,QAAQ,QAAQ,IAAI,CAClC,GAAG;GACD,IAAI,WAAW,CAAC,MAAM,GAAG,WAAW,QAAQ,MAAM,GAChD;GAEF,MAAM;EACR;CACF;CAEA,YAAuC;EACrC,OAAO,gBAAgB,OAAO,YAAY,KAAKA,MAAM,OAAO,CAAC,CAAC;CAChE;AACF"}
1
+ {"version":3,"file":"memory-storage.js","names":["#data"],"sources":["../../../../../zql/src/ivm/memory-storage.ts"],"sourcesContent":["import {compareUTF8} from 'compare-utf8';\nimport {BTreeSet} from '../../../shared/src/btree-set.ts';\nimport type {JSONValue} from '../../../shared/src/json.ts';\nimport type {Storage} from './operator.ts';\nimport type {Stream} from './stream.ts';\n\ntype Entry = [key: string, value: JSONValue];\n\nfunction comparator(a: Entry, b: Entry): number {\n return compareUTF8(a[0], b[0]);\n}\n\n/**\n * MemoryStorage is a simple in-memory implementation of `Storage` for use\n * on the client and in tests.\n */\nexport class MemoryStorage implements Storage {\n #data: BTreeSet<Entry> = new BTreeSet(comparator);\n\n set(key: string, value: JSONValue) {\n this.#data.add([key, value]);\n }\n\n get(key: string, def?: JSONValue): JSONValue | undefined {\n const r = this.#data.get([key, null]);\n if (r !== undefined) {\n return r[1];\n }\n return def;\n }\n\n del(key: string) {\n this.#data.delete([key, null]);\n }\n\n *scan(options?: {prefix: string}): Stream<[string, JSONValue]> {\n for (const entry of this.#data.valuesFrom(\n options && [options.prefix, null],\n )) {\n if (options && !entry[0].startsWith(options.prefix)) {\n return;\n }\n yield entry;\n }\n }\n\n cloneData(): Record<string, JSONValue> {\n return structuredClone(Object.fromEntries(this.#data.values()));\n }\n}\n"],"mappings":";;;AAQA,SAAS,WAAW,GAAU,GAAkB;AAC9C,QAAO,YAAY,EAAE,IAAI,EAAE,GAAG;;;;;;AAOhC,IAAa,gBAAb,MAA8C;CAC5C,QAAyB,IAAI,SAAS,WAAW;CAEjD,IAAI,KAAa,OAAkB;AACjC,QAAA,KAAW,IAAI,CAAC,KAAK,MAAM,CAAC;;CAG9B,IAAI,KAAa,KAAwC;EACvD,MAAM,IAAI,MAAA,KAAW,IAAI,CAAC,KAAK,KAAK,CAAC;AACrC,MAAI,MAAM,KAAA,EACR,QAAO,EAAE;AAEX,SAAO;;CAGT,IAAI,KAAa;AACf,QAAA,KAAW,OAAO,CAAC,KAAK,KAAK,CAAC;;CAGhC,CAAC,KAAK,SAAyD;AAC7D,OAAK,MAAM,SAAS,MAAA,KAAW,WAC7B,WAAW,CAAC,QAAQ,QAAQ,KAAK,CAClC,EAAE;AACD,OAAI,WAAW,CAAC,MAAM,GAAG,WAAW,QAAQ,OAAO,CACjD;AAEF,SAAM;;;CAIV,YAAuC;AACrC,SAAO,gBAAgB,OAAO,YAAY,MAAA,KAAW,QAAQ,CAAC,CAAC"}
@@ -46,7 +46,7 @@ export interface Input extends InputBase {
46
46
  * - **Unique entries.** TableSource gets set semantics for free from SQL
47
47
  * `IN`; MemorySource fans sub-fetches out per entry, so duplicates
48
48
  * would yield the same row N times. FlippedJoin groups by canonical
49
- * parent-key (`flipped-join.ts:#fetchBatched`) before emitting.
49
+ * parent-key (`flipped-join.ts:#fetchMergeSort`) before emitting.
50
50
  * - **Key-compatible with `req.constraint`.** Entries that contradict
51
51
  * `constraint` should be dropped upstream. FlippedJoin filters
52
52
  * incompatible children via `constraintsAreCompatible` before adding
@@ -1 +1 @@
1
- {"version":3,"file":"operator.js","names":[],"sources":["../../../../../zql/src/ivm/operator.ts"],"sourcesContent":["import type {JSONValue} from '../../../shared/src/json.ts';\nimport type {Row} from '../../../zero-protocol/src/data.ts';\nimport type {Change} from './change.ts';\nimport type {Constraint} from './constraint.ts';\nimport type {Node} from './data.ts';\nimport type {SourceSchema} from './schema.ts';\nimport type {Stream} from './stream.ts';\n\nexport {skipYields} from './skip-yields.ts';\n\n/**\n * Input to an operator.\n */\nexport interface InputBase {\n /** The schema of the data this input returns. */\n getSchema(): SourceSchema;\n\n /**\n * Completely destroy the input. Destroying an input\n * causes it to call destroy on its upstreams, fully\n * cleaning up a pipeline.\n */\n destroy(): void;\n}\n\nexport interface Input extends InputBase {\n /** Tell the input where to send its output. */\n setOutput(output: Output): void;\n\n /**\n * Fetch data. May modify the data in place.\n * Returns nodes sorted in order of `SourceSchema.compareRows`.\n *\n * The stream may contain 'yield' to yield control to the caller for purposes\n * of responsiveness.\n *\n * Contract:\n * - During fetch: If an input yields 'yield', 'yield' must be yielded to the\n * caller of fetch immediately.\n * - During push: If a fetch to an input consumed by the push logic yields\n * 'yield', it must be yielded to the caller of push immediately.\n */\n fetch(req: FetchRequest): Stream<Node | 'yield'>;\n}\n\n/**\n * A single multi-row IN clause: a non-empty list of Constraints all\n * sharing the same column shape. Sources treat it as\n * `(col_a, col_b, …) IN VALUES (…)`.\n *\n * Caller invariants (sources rely on these — they are not re-checked):\n * - **Unique entries.** TableSource gets set semantics for free from SQL\n * `IN`; MemorySource fans sub-fetches out per entry, so duplicates\n * would yield the same row N times. FlippedJoin groups by canonical\n * parent-key (`flipped-join.ts:#fetchBatched`) before emitting.\n * - **Key-compatible with `req.constraint`.** Entries that contradict\n * `constraint` should be dropped upstream. FlippedJoin filters\n * incompatible children via `constraintsAreCompatible` before adding\n * them to a batch.\n */\nexport type MultiConstraint = readonly Constraint[];\n\nexport type FetchRequest = {\n readonly constraint?: Constraint | undefined;\n\n /**\n * List of multi-row IN clauses, all ANDed together (and ANDed with\n * `constraint` if both are provided). Each entry is a `MultiConstraint`\n * over its own set of columns.\n *\n * Used by FlippedJoin to push child→parent fetches into a single\n * batched statement, and to AND together constraints contributed by\n * chained FlippedJoins (e.g. `assigneeID IN (…) AND creatorID IN (…)`).\n */\n readonly multiConstraints?: readonly MultiConstraint[] | undefined;\n\n /** If supplied, `start.row` must have previously been output by fetch or push. */\n readonly start?: Start | undefined;\n\n /** Whether to fetch in reverse order of the SourceSchema's sort. */\n readonly reverse?: boolean | undefined;\n};\n\nexport type Start = {\n readonly row: Row;\n readonly basis: 'at' | 'after';\n};\n\n/**\n * An output for an operator. Typically another Operator but can also be\n * the code running the pipeline.\n */\nexport interface Output {\n /**\n * Push incremental changes to data previously received with fetch().\n * Consumers must apply all pushed changes or incremental result will\n * be incorrect.\n * Callers must maintain some invariants for correct operation:\n * - Only add rows which do not already exist (by deep equality).\n * - Only remove rows which do exist (by deep equality).\n * Implmentation can yield 'yield' to yield control to the caller for purposes\n * of responsiveness.\n * Yield contract:\n * - During a push: If a push call to an output yields 'yield', it must be\n * yielded to the caller of push immediately.\n */\n push(change: Change, pusher: InputBase): Stream<'yield'>;\n}\n\n/**\n * An implementation of Output that throws if pushed to. It is used as the\n * initial value for for an operator's output before it is set.\n */\nexport const throwOutput: Output = {\n push(_change: Change): Stream<'yield'> {\n throw new Error('Output not set');\n },\n};\n\n/**\n * Operators are arranged into pipelines.\n * They are stateful.\n * Each operator is an input to the next operator in the chain and an output\n * to the previous.\n */\nexport interface Operator extends Input, Output {}\n\n/**\n * Operators get access to storage that they can store their internal\n * state in.\n */\nexport interface Storage {\n set(key: string, value: JSONValue): void;\n get(key: string, def?: JSONValue): JSONValue | undefined;\n /**\n * If options is not specified, defaults to scanning all entries.\n */\n scan(options?: {prefix: string}): Stream<[string, JSONValue]>;\n del(key: string): void;\n}\n"],"mappings":";;;;;;AAiHA,IAAa,cAAsB,EACjC,KAAK,SAAkC;CACrC,MAAM,IAAI,MAAM,gBAAgB;AAClC,EACF"}
1
+ {"version":3,"file":"operator.js","names":[],"sources":["../../../../../zql/src/ivm/operator.ts"],"sourcesContent":["import type {JSONValue} from '../../../shared/src/json.ts';\nimport type {Row} from '../../../zero-protocol/src/data.ts';\nimport type {Change} from './change.ts';\nimport type {Constraint} from './constraint.ts';\nimport type {Node} from './data.ts';\nimport type {SourceSchema} from './schema.ts';\nimport type {Stream} from './stream.ts';\n\nexport {skipYields} from './skip-yields.ts';\n\n/**\n * Input to an operator.\n */\nexport interface InputBase {\n /** The schema of the data this input returns. */\n getSchema(): SourceSchema;\n\n /**\n * Completely destroy the input. Destroying an input\n * causes it to call destroy on its upstreams, fully\n * cleaning up a pipeline.\n */\n destroy(): void;\n}\n\nexport interface Input extends InputBase {\n /** Tell the input where to send its output. */\n setOutput(output: Output): void;\n\n /**\n * Fetch data. May modify the data in place.\n * Returns nodes sorted in order of `SourceSchema.compareRows`.\n *\n * The stream may contain 'yield' to yield control to the caller for purposes\n * of responsiveness.\n *\n * Contract:\n * - During fetch: If an input yields 'yield', 'yield' must be yielded to the\n * caller of fetch immediately.\n * - During push: If a fetch to an input consumed by the push logic yields\n * 'yield', it must be yielded to the caller of push immediately.\n */\n fetch(req: FetchRequest): Stream<Node | 'yield'>;\n}\n\n/**\n * A single multi-row IN clause: a non-empty list of Constraints all\n * sharing the same column shape. Sources treat it as\n * `(col_a, col_b, …) IN VALUES (…)`.\n *\n * Caller invariants (sources rely on these — they are not re-checked):\n * - **Unique entries.** TableSource gets set semantics for free from SQL\n * `IN`; MemorySource fans sub-fetches out per entry, so duplicates\n * would yield the same row N times. FlippedJoin groups by canonical\n * parent-key (`flipped-join.ts:#fetchMergeSort`) before emitting.\n * - **Key-compatible with `req.constraint`.** Entries that contradict\n * `constraint` should be dropped upstream. FlippedJoin filters\n * incompatible children via `constraintsAreCompatible` before adding\n * them to a batch.\n */\nexport type MultiConstraint = readonly Constraint[];\n\nexport type FetchRequest = {\n readonly constraint?: Constraint | undefined;\n\n /**\n * List of multi-row IN clauses, all ANDed together (and ANDed with\n * `constraint` if both are provided). Each entry is a `MultiConstraint`\n * over its own set of columns.\n *\n * Used by FlippedJoin to push child→parent fetches into a single\n * batched statement, and to AND together constraints contributed by\n * chained FlippedJoins (e.g. `assigneeID IN (…) AND creatorID IN (…)`).\n */\n readonly multiConstraints?: readonly MultiConstraint[] | undefined;\n\n /** If supplied, `start.row` must have previously been output by fetch or push. */\n readonly start?: Start | undefined;\n\n /** Whether to fetch in reverse order of the SourceSchema's sort. */\n readonly reverse?: boolean | undefined;\n};\n\nexport type Start = {\n readonly row: Row;\n readonly basis: 'at' | 'after';\n};\n\n/**\n * An output for an operator. Typically another Operator but can also be\n * the code running the pipeline.\n */\nexport interface Output {\n /**\n * Push incremental changes to data previously received with fetch().\n * Consumers must apply all pushed changes or incremental result will\n * be incorrect.\n * Callers must maintain some invariants for correct operation:\n * - Only add rows which do not already exist (by deep equality).\n * - Only remove rows which do exist (by deep equality).\n * Implmentation can yield 'yield' to yield control to the caller for purposes\n * of responsiveness.\n * Yield contract:\n * - During a push: If a push call to an output yields 'yield', it must be\n * yielded to the caller of push immediately.\n */\n push(change: Change, pusher: InputBase): Stream<'yield'>;\n}\n\n/**\n * An implementation of Output that throws if pushed to. It is used as the\n * initial value for for an operator's output before it is set.\n */\nexport const throwOutput: Output = {\n push(_change: Change): Stream<'yield'> {\n throw new Error('Output not set');\n },\n};\n\n/**\n * Operators are arranged into pipelines.\n * They are stateful.\n * Each operator is an input to the next operator in the chain and an output\n * to the previous.\n */\nexport interface Operator extends Input, Output {}\n\n/**\n * Operators get access to storage that they can store their internal\n * state in.\n */\nexport interface Storage {\n set(key: string, value: JSONValue): void;\n get(key: string, def?: JSONValue): JSONValue | undefined;\n /**\n * If options is not specified, defaults to scanning all entries.\n */\n scan(options?: {prefix: string}): Stream<[string, JSONValue]>;\n del(key: string): void;\n}\n"],"mappings":";;;;;;AAiHA,IAAa,cAAsB,EACjC,KAAK,SAAkC;AACrC,OAAM,IAAI,MAAM,iBAAiB;GAEpC"}