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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (803) hide show
  1. package/out/_virtual/{_@oxc-project_runtime@0.130.0/helpers → _@oxc-project_runtime@0.154.0/helpers/esm}/usingCtx.js +2 -2
  2. package/out/analyze-query/src/analyze-cli.js +2 -1
  3. package/out/analyze-query/src/analyze-cli.js.map +1 -1
  4. package/out/analyze-query/src/bin-analyze.js +5 -3
  5. package/out/analyze-query/src/bin-analyze.js.map +1 -1
  6. package/out/analyze-query/src/bin-transform.js +3 -2
  7. package/out/analyze-query/src/bin-transform.js.map +1 -1
  8. package/out/ast-to-zql/src/ast-to-zql.js.map +1 -1
  9. package/out/ast-to-zql/src/bin.js +3 -2
  10. package/out/ast-to-zql/src/bin.js.map +1 -1
  11. package/out/ast-to-zql/src/format.js.map +1 -1
  12. package/out/datadog/src/datadog-log-sink.js +5 -4
  13. package/out/datadog/src/datadog-log-sink.js.map +1 -1
  14. package/out/otel/src/log-options.js.map +1 -1
  15. package/out/replicache/src/btree/node.js +13 -11
  16. package/out/replicache/src/btree/node.js.map +1 -1
  17. package/out/replicache/src/btree/read.js +4 -2
  18. package/out/replicache/src/btree/read.js.map +1 -1
  19. package/out/replicache/src/btree/splice.js.map +1 -1
  20. package/out/replicache/src/btree/write.js +9 -5
  21. package/out/replicache/src/btree/write.js.map +1 -1
  22. package/out/replicache/src/config.js +1 -2
  23. package/out/replicache/src/connection-loop.js.map +1 -1
  24. package/out/replicache/src/dag/chunk.js +1 -1
  25. package/out/replicache/src/dag/gc.js.map +1 -1
  26. package/out/replicache/src/dag/lazy-store.js +8 -7
  27. package/out/replicache/src/dag/lazy-store.js.map +1 -1
  28. package/out/replicache/src/dag/store-impl.js +1 -1
  29. package/out/replicache/src/dag/store-impl.js.map +1 -1
  30. package/out/replicache/src/dag/visitor.js.map +1 -1
  31. package/out/replicache/src/db/commit.js +5 -3
  32. package/out/replicache/src/db/commit.js.map +1 -1
  33. package/out/replicache/src/db/read.js +1 -1
  34. package/out/replicache/src/db/read.js.map +1 -1
  35. package/out/replicache/src/db/rebase.js.map +1 -1
  36. package/out/replicache/src/db/write.js +5 -2
  37. package/out/replicache/src/db/write.js.map +1 -1
  38. package/out/replicache/src/deleted-clients.js +3 -2
  39. package/out/replicache/src/deleted-clients.js.map +1 -1
  40. package/out/replicache/src/expo-sqlite.js +1 -2
  41. package/out/replicache/src/frozen-json.js +1 -1
  42. package/out/replicache/src/frozen-json.js.map +1 -1
  43. package/out/replicache/src/get-default-puller.js +1 -1
  44. package/out/replicache/src/hash.js +1 -1
  45. package/out/replicache/src/hash.js.map +1 -1
  46. package/out/replicache/src/impl.js +1 -2
  47. package/out/replicache/src/index-defs.js +5 -3
  48. package/out/replicache/src/index-defs.js.map +1 -1
  49. package/out/replicache/src/kv/expo-sqlite/store.js +1 -1
  50. package/out/replicache/src/kv/expo-sqlite/store.js.map +1 -1
  51. package/out/replicache/src/kv/idb-store.js +1 -1
  52. package/out/replicache/src/kv/idb-store.js.map +1 -1
  53. package/out/replicache/src/kv/mem-fallback-store.js +1 -1
  54. package/out/replicache/src/kv/mem-fallback-store.js.map +1 -1
  55. package/out/replicache/src/kv/mem-store.js.map +1 -1
  56. package/out/replicache/src/kv/op-sqlite/store.js.map +1 -1
  57. package/out/replicache/src/kv/op-sqlite/types.js.map +1 -1
  58. package/out/replicache/src/kv/read-impl.js.map +1 -1
  59. package/out/replicache/src/kv/sqlite-store.js +1 -1
  60. package/out/replicache/src/kv/sqlite-store.js.map +1 -1
  61. package/out/replicache/src/kv/write-impl-base.js +4 -1
  62. package/out/replicache/src/kv/write-impl-base.js.map +1 -1
  63. package/out/replicache/src/kv/write-impl.js.map +1 -1
  64. package/out/replicache/src/log-options.js +2 -1
  65. package/out/replicache/src/log-options.js.map +1 -1
  66. package/out/replicache/src/make-idb-name.js +1 -1
  67. package/out/replicache/src/new-client-channel.js +9 -7
  68. package/out/replicache/src/new-client-channel.js.map +1 -1
  69. package/out/replicache/src/on-persist-channel.js +1 -1
  70. package/out/replicache/src/on-persist-channel.js.map +1 -1
  71. package/out/replicache/src/op-sqlite.js +1 -2
  72. package/out/replicache/src/pending-mutations.js +2 -1
  73. package/out/replicache/src/pending-mutations.js.map +1 -1
  74. package/out/replicache/src/persist/client-gc.js +2 -2
  75. package/out/replicache/src/persist/client-gc.js.map +1 -1
  76. package/out/replicache/src/persist/client-group-gc.js +2 -2
  77. package/out/replicache/src/persist/client-group-gc.js.map +1 -1
  78. package/out/replicache/src/persist/client-groups.js +1 -1
  79. package/out/replicache/src/persist/client-groups.js.map +1 -1
  80. package/out/replicache/src/persist/clients.js +1 -1
  81. package/out/replicache/src/persist/clients.js.map +1 -1
  82. package/out/replicache/src/persist/collect-idb-databases.js +5 -4
  83. package/out/replicache/src/persist/collect-idb-databases.js.map +1 -1
  84. package/out/replicache/src/persist/gather-mem-only-visitor.js.map +1 -1
  85. package/out/replicache/src/persist/gather-not-cached-visitor.js.map +1 -1
  86. package/out/replicache/src/persist/heartbeat.js +2 -2
  87. package/out/replicache/src/persist/heartbeat.js.map +1 -1
  88. package/out/replicache/src/persist/idb-databases-store.js +1 -1
  89. package/out/replicache/src/persist/idb-databases-store.js.map +1 -1
  90. package/out/replicache/src/persist/make-client-id.js +1 -1
  91. package/out/replicache/src/persist/make-client-id.js.map +1 -1
  92. package/out/replicache/src/persist/persist.js +3 -2
  93. package/out/replicache/src/persist/persist.js.map +1 -1
  94. package/out/replicache/src/persist/refresh.js +8 -5
  95. package/out/replicache/src/persist/refresh.js.map +1 -1
  96. package/out/replicache/src/process-scheduler.js.map +1 -1
  97. package/out/replicache/src/replicache-impl.js +3 -3
  98. package/out/replicache/src/replicache-impl.js.map +1 -1
  99. package/out/replicache/src/scan-iterator.js.map +1 -1
  100. package/out/replicache/src/scan-options.js +7 -6
  101. package/out/replicache/src/scan-options.js.map +1 -1
  102. package/out/replicache/src/sqlite.js +1 -2
  103. package/out/replicache/src/subscriptions.js +1 -1
  104. package/out/replicache/src/subscriptions.js.map +1 -1
  105. package/out/replicache/src/sync/diff.js +3 -1
  106. package/out/replicache/src/sync/diff.js.map +1 -1
  107. package/out/replicache/src/sync/ids.js.map +1 -1
  108. package/out/replicache/src/sync/patch.js +2 -4
  109. package/out/replicache/src/sync/patch.js.map +1 -1
  110. package/out/replicache/src/sync/pull.js +3 -1
  111. package/out/replicache/src/sync/pull.js.map +1 -1
  112. package/out/replicache/src/sync/push.js.map +1 -1
  113. package/out/replicache/src/sync/request-id.js +1 -1
  114. package/out/replicache/src/sync/request-id.js.map +1 -1
  115. package/out/replicache/src/transactions.js +1 -1
  116. package/out/replicache/src/transactions.js.map +1 -1
  117. package/out/shared/src/arrays.js +4 -1
  118. package/out/shared/src/arrays.js.map +1 -1
  119. package/out/shared/src/asserts.js +1 -1
  120. package/out/shared/src/bigint-json.d.ts +9 -0
  121. package/out/shared/src/bigint-json.d.ts.map +1 -1
  122. package/out/shared/src/bigint-json.js +27 -2
  123. package/out/shared/src/bigint-json.js.map +1 -1
  124. package/out/shared/src/broadcast-channel.js +1 -1
  125. package/out/shared/src/btree-set.js +11 -8
  126. package/out/shared/src/btree-set.js.map +1 -1
  127. package/out/shared/src/cache.js.map +1 -1
  128. package/out/shared/src/config.js +1 -1
  129. package/out/shared/src/custom-key-map.js.map +1 -1
  130. package/out/shared/src/custom-key-set.js.map +1 -1
  131. package/out/shared/src/deep-clone.js +1 -1
  132. package/out/shared/src/document-visible.js.map +1 -1
  133. package/out/shared/src/dotenv.js.map +1 -1
  134. package/out/shared/src/error.js +1 -1
  135. package/out/shared/src/error.js.map +1 -1
  136. package/out/shared/src/hash.js +2 -2
  137. package/out/shared/src/hash.js.map +1 -1
  138. package/out/shared/src/iterables.js.map +1 -1
  139. package/out/shared/src/json-schema.js.map +1 -1
  140. package/out/shared/src/json.js.map +1 -1
  141. package/out/shared/src/logging-test-utils.js +1 -1
  142. package/out/shared/src/logging.js +1 -1
  143. package/out/shared/src/logging.js.map +1 -1
  144. package/out/shared/src/navigator.js +1 -1
  145. package/out/shared/src/options.js +9 -12
  146. package/out/shared/src/options.js.map +1 -1
  147. package/out/shared/src/promise-race.js.map +1 -1
  148. package/out/shared/src/queue.js.map +1 -1
  149. package/out/shared/src/record-proxy.js.map +1 -1
  150. package/out/shared/src/ring-buffer.js.map +1 -1
  151. package/out/shared/src/size-of-value.js +7 -10
  152. package/out/shared/src/size-of-value.js.map +1 -1
  153. package/out/shared/src/sorted-entries.js.map +1 -1
  154. package/out/shared/src/tdigest-schema.js.map +1 -1
  155. package/out/shared/src/tdigest.js.map +1 -1
  156. package/out/shared/src/valita.js +1 -1
  157. package/out/shared/src/valita.js.map +1 -1
  158. package/out/z2s/src/compiler.js +2 -4
  159. package/out/z2s/src/compiler.js.map +1 -1
  160. package/out/z2s/src/sql.js +1 -1
  161. package/out/z2s/src/sql.js.map +1 -1
  162. package/out/zero/package.js +212 -189
  163. package/out/zero/package.js.map +1 -1
  164. package/out/zero/src/adapters/drizzle.js +1 -2
  165. package/out/zero/src/adapters/kysely.js +1 -2
  166. package/out/zero/src/adapters/pg.js +1 -2
  167. package/out/zero/src/adapters/postgresjs.js +1 -2
  168. package/out/zero/src/adapters/prisma.js +1 -2
  169. package/out/zero/src/analyze.js +1 -2
  170. package/out/zero/src/change-protocol/v0.js +1 -2
  171. package/out/zero/src/expo-sqlite.js +1 -2
  172. package/out/zero/src/op-sqlite.js +1 -2
  173. package/out/zero/src/react-native.js.map +1 -1
  174. package/out/zero/src/sqlite.js +1 -2
  175. package/out/zero/src/zero-cache-dev.js +10 -9
  176. package/out/zero/src/zero-cache-dev.js.map +1 -1
  177. package/out/zero/src/zero-out.js +2 -1
  178. package/out/zero/src/zero-out.js.map +1 -1
  179. package/out/zero-cache/src/auth/auth.js +1 -1
  180. package/out/zero-cache/src/auth/auth.js.map +1 -1
  181. package/out/zero-cache/src/auth/jwt.js +4 -1
  182. package/out/zero-cache/src/auth/jwt.js.map +1 -1
  183. package/out/zero-cache/src/auth/load-permissions.js.map +1 -1
  184. package/out/zero-cache/src/auth/read-authorizer.js +3 -2
  185. package/out/zero-cache/src/auth/read-authorizer.js.map +1 -1
  186. package/out/zero-cache/src/auth/write-authorizer.js +5 -16
  187. package/out/zero-cache/src/auth/write-authorizer.js.map +1 -1
  188. package/out/zero-cache/src/config/network.js +1 -1
  189. package/out/zero-cache/src/config/network.js.map +1 -1
  190. package/out/zero-cache/src/config/server-context.js.map +1 -1
  191. package/out/zero-cache/src/config/zero-config.d.ts +9 -0
  192. package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
  193. package/out/zero-cache/src/config/zero-config.js +17 -2
  194. package/out/zero-cache/src/config/zero-config.js.map +1 -1
  195. package/out/zero-cache/src/custom/fetch.js +1 -1
  196. package/out/zero-cache/src/custom/fetch.js.map +1 -1
  197. package/out/zero-cache/src/custom-queries/transform-query.js.map +1 -1
  198. package/out/zero-cache/src/db/create.js.map +1 -1
  199. package/out/zero-cache/src/db/index-predicate.js.map +1 -1
  200. package/out/zero-cache/src/db/lite-tables.d.ts +2 -2
  201. package/out/zero-cache/src/db/lite-tables.d.ts.map +1 -1
  202. package/out/zero-cache/src/db/lite-tables.js.map +1 -1
  203. package/out/zero-cache/src/db/migration-lite.js +1 -1
  204. package/out/zero-cache/src/db/migration-lite.js.map +1 -1
  205. package/out/zero-cache/src/db/migration.js +5 -3
  206. package/out/zero-cache/src/db/migration.js.map +1 -1
  207. package/out/zero-cache/src/db/pg-copy-binary.js +4 -3
  208. package/out/zero-cache/src/db/pg-copy-binary.js.map +1 -1
  209. package/out/zero-cache/src/db/pg-copy.js +0 -1
  210. package/out/zero-cache/src/db/pg-copy.js.map +1 -1
  211. package/out/zero-cache/src/db/pg-to-lite.js +1 -1
  212. package/out/zero-cache/src/db/pg-to-lite.js.map +1 -1
  213. package/out/zero-cache/src/db/run-transaction.js.map +1 -1
  214. package/out/zero-cache/src/db/specs.js +7 -5
  215. package/out/zero-cache/src/db/specs.js.map +1 -1
  216. package/out/zero-cache/src/db/sqlite-corruption.js.map +1 -1
  217. package/out/zero-cache/src/db/statements.d.ts.map +1 -1
  218. package/out/zero-cache/src/db/statements.js +9 -2
  219. package/out/zero-cache/src/db/statements.js.map +1 -1
  220. package/out/zero-cache/src/db/transaction-pool.js +5 -3
  221. package/out/zero-cache/src/db/transaction-pool.js.map +1 -1
  222. package/out/zero-cache/src/db/warmup.js.map +1 -1
  223. package/out/zero-cache/src/observability/events.js.map +1 -1
  224. package/out/zero-cache/src/observability/log-throttle.js.map +1 -1
  225. package/out/zero-cache/src/observability/metrics.js.map +1 -1
  226. package/out/zero-cache/src/scripts/deploy-permissions.js +4 -2
  227. package/out/zero-cache/src/scripts/deploy-permissions.js.map +1 -1
  228. package/out/zero-cache/src/scripts/permissions.js.map +1 -1
  229. package/out/zero-cache/src/server/anonymous-otel-start.js +2 -1
  230. package/out/zero-cache/src/server/anonymous-otel-start.js.map +1 -1
  231. package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
  232. package/out/zero-cache/src/server/change-streamer.js +2 -1
  233. package/out/zero-cache/src/server/change-streamer.js.map +1 -1
  234. package/out/zero-cache/src/server/inspector-delegate.js.map +1 -1
  235. package/out/zero-cache/src/server/logging.js +1 -1
  236. package/out/zero-cache/src/server/logging.js.map +1 -1
  237. package/out/zero-cache/src/server/main.js.map +1 -1
  238. package/out/zero-cache/src/server/otel-log-sink.js.map +1 -1
  239. package/out/zero-cache/src/server/otel-start.js.map +1 -1
  240. package/out/zero-cache/src/server/reaper.js +2 -2
  241. package/out/zero-cache/src/server/reaper.js.map +1 -1
  242. package/out/zero-cache/src/server/replicator.js.map +1 -1
  243. package/out/zero-cache/src/server/runner/run-worker.js +2 -1
  244. package/out/zero-cache/src/server/runner/run-worker.js.map +1 -1
  245. package/out/zero-cache/src/server/runner/runtime.js +2 -1
  246. package/out/zero-cache/src/server/runner/runtime.js.map +1 -1
  247. package/out/zero-cache/src/server/runner/zero-dispatcher.js.map +1 -1
  248. package/out/zero-cache/src/server/shadow-syncer.js +1 -1
  249. package/out/zero-cache/src/server/shadow-syncer.js.map +1 -1
  250. package/out/zero-cache/src/server/slot-keeper.js.map +1 -1
  251. package/out/zero-cache/src/server/syncer-assigner.js.map +1 -1
  252. package/out/zero-cache/src/server/syncer.d.ts.map +1 -1
  253. package/out/zero-cache/src/server/syncer.js +6 -1
  254. package/out/zero-cache/src/server/syncer.js.map +1 -1
  255. package/out/zero-cache/src/server/worker-dispatcher.js +1 -1
  256. package/out/zero-cache/src/server/worker-dispatcher.js.map +1 -1
  257. package/out/zero-cache/src/server/worker-urls.js.map +1 -1
  258. package/out/zero-cache/src/services/analyze.js +1 -1
  259. package/out/zero-cache/src/services/analyze.js.map +1 -1
  260. package/out/zero-cache/src/services/change-source/common/backfill-manager.js +1 -1
  261. package/out/zero-cache/src/services/change-source/common/backfill-manager.js.map +1 -1
  262. package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.js.map +1 -1
  263. package/out/zero-cache/src/services/change-source/common/init-cleanup.js.map +1 -1
  264. package/out/zero-cache/src/services/change-source/common/replica-restore.js.map +1 -1
  265. package/out/zero-cache/src/services/change-source/common/replica-schema.js +3 -3
  266. package/out/zero-cache/src/services/change-source/common/replica-schema.js.map +1 -1
  267. package/out/zero-cache/src/services/change-source/custom/change-source.js +1 -1
  268. package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
  269. package/out/zero-cache/src/services/change-source/pg/backfill-metadata.js.map +1 -1
  270. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js +3 -3
  271. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js.map +1 -1
  272. package/out/zero-cache/src/services/change-source/pg/change-source-init.d.ts.map +1 -1
  273. package/out/zero-cache/src/services/change-source/pg/change-source-init.js +2 -2
  274. package/out/zero-cache/src/services/change-source/pg/change-source-init.js.map +1 -1
  275. package/out/zero-cache/src/services/change-source/pg/change-source.js +4 -6
  276. package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
  277. package/out/zero-cache/src/services/change-source/pg/decommission.js +2 -1
  278. package/out/zero-cache/src/services/change-source/pg/decommission.js.map +1 -1
  279. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts +1 -0
  280. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts.map +1 -1
  281. package/out/zero-cache/src/services/change-source/pg/initial-sync.js +10 -8
  282. package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
  283. package/out/zero-cache/src/services/change-source/pg/logical-replication/binary-reader.js.map +1 -1
  284. package/out/zero-cache/src/services/change-source/pg/logical-replication/pgoutput-parser.js.map +1 -1
  285. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js +3 -2
  286. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js.map +1 -1
  287. package/out/zero-cache/src/services/change-source/pg/lsn.js.map +1 -1
  288. package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.js +2 -2
  289. package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.js.map +1 -1
  290. package/out/zero-cache/src/services/change-source/pg/replication-slot-health.js +1 -1
  291. package/out/zero-cache/src/services/change-source/pg/replication-slot-health.js.map +1 -1
  292. package/out/zero-cache/src/services/change-source/pg/replication-slots.js +3 -3
  293. package/out/zero-cache/src/services/change-source/pg/replication-slots.js.map +1 -1
  294. package/out/zero-cache/src/services/change-source/pg/schema/ddl.d.ts +1 -1
  295. package/out/zero-cache/src/services/change-source/pg/schema/ddl.d.ts.map +1 -1
  296. package/out/zero-cache/src/services/change-source/pg/schema/ddl.js +3 -3
  297. package/out/zero-cache/src/services/change-source/pg/schema/ddl.js.map +1 -1
  298. package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.js +13 -12
  299. package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.js.map +1 -1
  300. package/out/zero-cache/src/services/change-source/pg/schema/init.d.ts +1 -1
  301. package/out/zero-cache/src/services/change-source/pg/schema/init.d.ts.map +1 -1
  302. package/out/zero-cache/src/services/change-source/pg/schema/init.js +8 -8
  303. package/out/zero-cache/src/services/change-source/pg/schema/init.js.map +1 -1
  304. package/out/zero-cache/src/services/change-source/pg/schema/published.d.ts +1 -1
  305. package/out/zero-cache/src/services/change-source/pg/schema/published.d.ts.map +1 -1
  306. package/out/zero-cache/src/services/change-source/pg/schema/published.js +3 -2
  307. package/out/zero-cache/src/services/change-source/pg/schema/published.js.map +1 -1
  308. package/out/zero-cache/src/services/change-source/pg/schema/replica-stage-enum.js.map +1 -1
  309. package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts +2 -2
  310. package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts.map +1 -1
  311. package/out/zero-cache/src/services/change-source/pg/schema/shard.js +13 -10
  312. package/out/zero-cache/src/services/change-source/pg/schema/shard.js.map +1 -1
  313. package/out/zero-cache/src/services/change-source/pg/schema/validation.js +1 -1
  314. package/out/zero-cache/src/services/change-source/pg/schema/validation.js.map +1 -1
  315. package/out/zero-cache/src/services/change-source/protocol/backfill-progress.js +1 -1
  316. package/out/zero-cache/src/services/change-source/protocol/backfill-progress.js.map +1 -1
  317. package/out/zero-cache/src/services/change-source/protocol/current/control.js.map +1 -1
  318. package/out/zero-cache/src/services/change-source/protocol/current/data.js.map +1 -1
  319. package/out/zero-cache/src/services/change-source/protocol/current/downstream.js.map +1 -1
  320. package/out/zero-cache/src/services/change-source/protocol/current/status.js.map +1 -1
  321. package/out/zero-cache/src/services/change-source/protocol/current/upstream.js.map +1 -1
  322. package/out/zero-cache/src/services/change-source/protocol/current.js +1 -1
  323. package/out/zero-cache/src/services/change-source/protocol/mod.js +1 -2
  324. package/out/zero-cache/src/services/change-streamer/backfill-state-tracker.js.map +1 -1
  325. package/out/zero-cache/src/services/change-streamer/backfill-state.js +1 -1
  326. package/out/zero-cache/src/services/change-streamer/backfill-state.js.map +1 -1
  327. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.js.map +1 -1
  328. package/out/zero-cache/src/services/change-streamer/backup-monitor.js.map +1 -1
  329. package/out/zero-cache/src/services/change-streamer/broadcast.js +1 -1
  330. package/out/zero-cache/src/services/change-streamer/broadcast.js.map +1 -1
  331. package/out/zero-cache/src/services/change-streamer/change-log-codec.d.ts +1 -1
  332. package/out/zero-cache/src/services/change-streamer/change-log-codec.d.ts.map +1 -1
  333. package/out/zero-cache/src/services/change-streamer/change-log-codec.js +2 -3
  334. package/out/zero-cache/src/services/change-streamer/change-log-codec.js.map +1 -1
  335. package/out/zero-cache/src/services/change-streamer/change-log-initializer.js +2 -2
  336. package/out/zero-cache/src/services/change-streamer/change-log-initializer.js.map +1 -1
  337. package/out/zero-cache/src/services/change-streamer/change-log-transaction-hash.js.map +1 -1
  338. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +4 -2
  339. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
  340. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +21 -21
  341. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
  342. package/out/zero-cache/src/services/change-streamer/change-streamer.js +1 -1
  343. package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
  344. package/out/zero-cache/src/services/change-streamer/drain-tracker.js.map +1 -1
  345. package/out/zero-cache/src/services/change-streamer/forwarder.js +11 -10
  346. package/out/zero-cache/src/services/change-streamer/forwarder.js.map +1 -1
  347. package/out/zero-cache/src/services/change-streamer/litestream-sync-requester.js +1 -1
  348. package/out/zero-cache/src/services/change-streamer/litestream-sync-requester.js.map +1 -1
  349. package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.js +3 -3
  350. package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.js.map +1 -1
  351. package/out/zero-cache/src/services/change-streamer/replica-poller.js +1 -1
  352. package/out/zero-cache/src/services/change-streamer/replica-poller.js.map +1 -1
  353. package/out/zero-cache/src/services/change-streamer/restore-admission.js +12 -1
  354. package/out/zero-cache/src/services/change-streamer/restore-admission.js.map +1 -1
  355. package/out/zero-cache/src/services/change-streamer/schema/init.js +1 -1
  356. package/out/zero-cache/src/services/change-streamer/schema/init.js.map +1 -1
  357. package/out/zero-cache/src/services/change-streamer/schema/tables.js +1 -1
  358. package/out/zero-cache/src/services/change-streamer/schema/tables.js.map +1 -1
  359. package/out/zero-cache/src/services/change-streamer/snapshot-message.js +1 -1
  360. package/out/zero-cache/src/services/change-streamer/snapshot-message.js.map +1 -1
  361. package/out/zero-cache/src/services/change-streamer/snapshot-reservations.js.map +1 -1
  362. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.js +1 -1
  363. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.js.map +1 -1
  364. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-comparator.js +2 -2
  365. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-comparator.js.map +1 -1
  366. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-purge-scheduler.js.map +1 -1
  367. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-read-router.js.map +1 -1
  368. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.d.ts.map +1 -1
  369. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.js +4 -6
  370. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.js.map +1 -1
  371. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.js +1 -3
  372. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.js.map +1 -1
  373. package/out/zero-cache/src/services/change-streamer/storer.d.ts.map +1 -1
  374. package/out/zero-cache/src/services/change-streamer/storer.js +2 -2
  375. package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
  376. package/out/zero-cache/src/services/change-streamer/subscribe.js +1 -1
  377. package/out/zero-cache/src/services/change-streamer/subscribe.js.map +1 -1
  378. package/out/zero-cache/src/services/change-streamer/subscriber.js +2 -2
  379. package/out/zero-cache/src/services/change-streamer/subscriber.js.map +1 -1
  380. package/out/zero-cache/src/services/change-streamer/upstream-acker.js +0 -1
  381. package/out/zero-cache/src/services/change-streamer/upstream-acker.js.map +1 -1
  382. package/out/zero-cache/src/services/heapz.js +2 -1
  383. package/out/zero-cache/src/services/heapz.js.map +1 -1
  384. package/out/zero-cache/src/services/http-service.js.map +1 -1
  385. package/out/zero-cache/src/services/life-cycle.js +1 -1
  386. package/out/zero-cache/src/services/life-cycle.js.map +1 -1
  387. package/out/zero-cache/src/services/limiter/sliding-window-limiter.js +4 -2
  388. package/out/zero-cache/src/services/limiter/sliding-window-limiter.js.map +1 -1
  389. package/out/zero-cache/src/services/litestream/commands.js +1 -1
  390. package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
  391. package/out/zero-cache/src/services/litestream/litestream-checkpointer.js +2 -2
  392. package/out/zero-cache/src/services/litestream/litestream-checkpointer.js.map +1 -1
  393. package/out/zero-cache/src/services/litestream/litestream-controller.js.map +1 -1
  394. package/out/zero-cache/src/services/litestream/metrics.js.map +1 -1
  395. package/out/zero-cache/src/services/litestream/restore-progress.js.map +1 -1
  396. package/out/zero-cache/src/services/mutagen/mutagen.js +1 -1
  397. package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
  398. package/out/zero-cache/src/services/mutagen/pusher.js +3 -2
  399. package/out/zero-cache/src/services/mutagen/pusher.js.map +1 -1
  400. package/out/zero-cache/src/services/plannerz.js +4 -3
  401. package/out/zero-cache/src/services/plannerz.js.map +1 -1
  402. package/out/zero-cache/src/services/profz.js +5 -3
  403. package/out/zero-cache/src/services/profz.js.map +1 -1
  404. package/out/zero-cache/src/services/replicator/change-log-cookies.js.map +1 -1
  405. package/out/zero-cache/src/services/replicator/change-log-db.js +3 -2
  406. package/out/zero-cache/src/services/replicator/change-log-db.js.map +1 -1
  407. package/out/zero-cache/src/services/replicator/change-log-stream-writer.js +1 -1
  408. package/out/zero-cache/src/services/replicator/change-log-stream-writer.js.map +1 -1
  409. package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
  410. package/out/zero-cache/src/services/replicator/drain-handler.js.map +1 -1
  411. package/out/zero-cache/src/services/replicator/incremental-sync.js +1 -1
  412. package/out/zero-cache/src/services/replicator/incremental-sync.js.map +1 -1
  413. package/out/zero-cache/src/services/replicator/notifier.js.map +1 -1
  414. package/out/zero-cache/src/services/replicator/replication-status.js +4 -3
  415. package/out/zero-cache/src/services/replicator/replication-status.js.map +1 -1
  416. package/out/zero-cache/src/services/replicator/replicator.js.map +1 -1
  417. package/out/zero-cache/src/services/replicator/reporter/recorder.js +1 -1
  418. package/out/zero-cache/src/services/replicator/reporter/recorder.js.map +1 -1
  419. package/out/zero-cache/src/services/replicator/reporter/report-schema.js +1 -1
  420. package/out/zero-cache/src/services/replicator/reporter/report-schema.js.map +1 -1
  421. package/out/zero-cache/src/services/replicator/schema/backfilling.js +1 -1
  422. package/out/zero-cache/src/services/replicator/schema/backfilling.js.map +1 -1
  423. package/out/zero-cache/src/services/replicator/schema/change-log.d.ts +19 -7
  424. package/out/zero-cache/src/services/replicator/schema/change-log.d.ts.map +1 -1
  425. package/out/zero-cache/src/services/replicator/schema/change-log.js +31 -15
  426. package/out/zero-cache/src/services/replicator/schema/change-log.js.map +1 -1
  427. package/out/zero-cache/src/services/replicator/schema/column-metadata.js +1 -1
  428. package/out/zero-cache/src/services/replicator/schema/column-metadata.js.map +1 -1
  429. package/out/zero-cache/src/services/replicator/schema/replication-state.js +10 -7
  430. package/out/zero-cache/src/services/replicator/schema/replication-state.js.map +1 -1
  431. package/out/zero-cache/src/services/replicator/schema/table-metadata.js.map +1 -1
  432. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js +1 -1
  433. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js.map +1 -1
  434. package/out/zero-cache/src/services/replicator/sqlite-change-log-purger.js +1 -1
  435. package/out/zero-cache/src/services/replicator/sqlite-change-log-purger.js.map +1 -1
  436. package/out/zero-cache/src/services/replicator/write-worker-client.js +1 -1
  437. package/out/zero-cache/src/services/replicator/write-worker-client.js.map +1 -1
  438. package/out/zero-cache/src/services/replicator/write-worker.js.map +1 -1
  439. package/out/zero-cache/src/services/run-ast.js.map +1 -1
  440. package/out/zero-cache/src/services/runner.js.map +1 -1
  441. package/out/zero-cache/src/services/running-state.js.map +1 -1
  442. package/out/zero-cache/src/services/shadow-sync/shadow-sync-service.js.map +1 -1
  443. package/out/zero-cache/src/services/statz.js +2 -1
  444. package/out/zero-cache/src/services/statz.js.map +1 -1
  445. package/out/zero-cache/src/services/view-syncer/active-users-gauge.js +2 -2
  446. package/out/zero-cache/src/services/view-syncer/active-users-gauge.js.map +1 -1
  447. package/out/zero-cache/src/services/view-syncer/advancement-metrics.js.map +1 -1
  448. package/out/zero-cache/src/services/view-syncer/batch-pacer.js.map +1 -1
  449. package/out/zero-cache/src/services/view-syncer/client-handler.d.ts +18 -0
  450. package/out/zero-cache/src/services/view-syncer/client-handler.d.ts.map +1 -1
  451. package/out/zero-cache/src/services/view-syncer/client-handler.js +70 -23
  452. package/out/zero-cache/src/services/view-syncer/client-handler.js.map +1 -1
  453. package/out/zero-cache/src/services/view-syncer/client-schema.d.ts +15 -2
  454. package/out/zero-cache/src/services/view-syncer/client-schema.d.ts.map +1 -1
  455. package/out/zero-cache/src/services/view-syncer/client-schema.js +46 -6
  456. package/out/zero-cache/src/services/view-syncer/client-schema.js.map +1 -1
  457. package/out/zero-cache/src/services/view-syncer/connection-context-manager.js.map +1 -1
  458. package/out/zero-cache/src/services/view-syncer/cvr-purger.js +2 -2
  459. package/out/zero-cache/src/services/view-syncer/cvr-purger.js.map +1 -1
  460. package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts.map +1 -1
  461. package/out/zero-cache/src/services/view-syncer/cvr-store.js +10 -5
  462. package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
  463. package/out/zero-cache/src/services/view-syncer/cvr.js +12 -4
  464. package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
  465. package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.js +1 -1
  466. package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.js.map +1 -1
  467. package/out/zero-cache/src/services/view-syncer/drain-coordinator.js.map +1 -1
  468. package/out/zero-cache/src/services/view-syncer/e2e-serving-lag.js.map +1 -1
  469. package/out/zero-cache/src/services/view-syncer/hydration-budget.js.map +1 -1
  470. package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.js +2 -2
  471. package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.js.map +1 -1
  472. package/out/zero-cache/src/services/view-syncer/inspect-handler.js +5 -3
  473. package/out/zero-cache/src/services/view-syncer/inspect-handler.js.map +1 -1
  474. package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts.map +1 -1
  475. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +8 -5
  476. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
  477. package/out/zero-cache/src/services/view-syncer/plan-warnings.js.map +1 -1
  478. package/out/zero-cache/src/services/view-syncer/push-cost.js.map +1 -1
  479. package/out/zero-cache/src/services/view-syncer/push-cutoff.js.map +1 -1
  480. package/out/zero-cache/src/services/view-syncer/query-covering.js.map +1 -1
  481. package/out/zero-cache/src/services/view-syncer/query-shape.js.map +1 -1
  482. package/out/zero-cache/src/services/view-syncer/query-stats.js +1 -1
  483. package/out/zero-cache/src/services/view-syncer/query-stats.js.map +1 -1
  484. package/out/zero-cache/src/services/view-syncer/row-record-cache.js.map +1 -1
  485. package/out/zero-cache/src/services/view-syncer/schema/cvr.js +1 -1
  486. package/out/zero-cache/src/services/view-syncer/schema/types.js +1 -1
  487. package/out/zero-cache/src/services/view-syncer/schema/types.js.map +1 -1
  488. package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.js +7 -1
  489. package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.js.map +1 -1
  490. package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts +12 -4
  491. package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts.map +1 -1
  492. package/out/zero-cache/src/services/view-syncer/snapshotter.js +11 -7
  493. package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
  494. package/out/zero-cache/src/services/view-syncer/ttl-clock.js.map +1 -1
  495. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
  496. package/out/zero-cache/src/services/view-syncer/view-syncer.js +13 -9
  497. package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
  498. package/out/zero-cache/src/types/error-with-level.js.map +1 -1
  499. package/out/zero-cache/src/types/lexi-version.js.map +1 -1
  500. package/out/zero-cache/src/types/lite.js +1 -1
  501. package/out/zero-cache/src/types/lite.js.map +1 -1
  502. package/out/zero-cache/src/types/pg-data-type.js +1 -1
  503. package/out/zero-cache/src/types/pg-data-type.js.map +1 -1
  504. package/out/zero-cache/src/types/pg.js +6 -4
  505. package/out/zero-cache/src/types/pg.js.map +1 -1
  506. package/out/zero-cache/src/types/poke-chunk.js +2 -2
  507. package/out/zero-cache/src/types/poke-chunk.js.map +1 -1
  508. package/out/zero-cache/src/types/processes.js +1 -1
  509. package/out/zero-cache/src/types/processes.js.map +1 -1
  510. package/out/zero-cache/src/types/profiler.js.map +1 -1
  511. package/out/zero-cache/src/types/row-key.js.map +1 -1
  512. package/out/zero-cache/src/types/sql.js.map +1 -1
  513. package/out/zero-cache/src/types/streams.js +10 -9
  514. package/out/zero-cache/src/types/streams.js.map +1 -1
  515. package/out/zero-cache/src/types/strings.js.map +1 -1
  516. package/out/zero-cache/src/types/subscription.js.map +1 -1
  517. package/out/zero-cache/src/types/timeout.js +1 -1
  518. package/out/zero-cache/src/types/websocket-handoff.js +1 -1
  519. package/out/zero-cache/src/types/ws.js +1 -1
  520. package/out/zero-cache/src/workers/connection.js +2 -3
  521. package/out/zero-cache/src/workers/connection.js.map +1 -1
  522. package/out/zero-cache/src/workers/mutator.js.map +1 -1
  523. package/out/zero-cache/src/workers/replicator.js +5 -4
  524. package/out/zero-cache/src/workers/replicator.js.map +1 -1
  525. package/out/zero-cache/src/workers/syncer-ws-message-handler.d.ts.map +1 -1
  526. package/out/zero-cache/src/workers/syncer-ws-message-handler.js +9 -1
  527. package/out/zero-cache/src/workers/syncer-ws-message-handler.js.map +1 -1
  528. package/out/zero-cache/src/workers/syncer.js +2 -2
  529. package/out/zero-cache/src/workers/syncer.js.map +1 -1
  530. package/out/zero-client/src/client/active-clients-manager.js +1 -1
  531. package/out/zero-client/src/client/active-clients-manager.js.map +1 -1
  532. package/out/zero-client/src/client/bindings.js +11 -11
  533. package/out/zero-client/src/client/connection-manager.js.map +1 -1
  534. package/out/zero-client/src/client/connection.js +1 -1
  535. package/out/zero-client/src/client/connection.js.map +1 -1
  536. package/out/zero-client/src/client/context.js.map +1 -1
  537. package/out/zero-client/src/client/crud-impl.js +1 -1
  538. package/out/zero-client/src/client/crud-impl.js.map +1 -1
  539. package/out/zero-client/src/client/crud.js +1 -1
  540. package/out/zero-client/src/client/crud.js.map +1 -1
  541. package/out/zero-client/src/client/custom.js +1 -1
  542. package/out/zero-client/src/client/custom.js.map +1 -1
  543. package/out/zero-client/src/client/delete-clients-manager.js.map +1 -1
  544. package/out/zero-client/src/client/enable-analytics.js +1 -1
  545. package/out/zero-client/src/client/enable-analytics.js.map +1 -1
  546. package/out/zero-client/src/client/error.js.map +1 -1
  547. package/out/zero-client/src/client/inspector/client-group.js.map +1 -1
  548. package/out/zero-client/src/client/inspector/client.js.map +1 -1
  549. package/out/zero-client/src/client/inspector/html-dialog-prompt.js +1 -1
  550. package/out/zero-client/src/client/inspector/html-dialog-prompt.js.map +1 -1
  551. package/out/zero-client/src/client/inspector/inspector.js.map +1 -1
  552. package/out/zero-client/src/client/inspector/lazy-inspector.js.map +1 -1
  553. package/out/zero-client/src/client/inspector/query.js.map +1 -1
  554. package/out/zero-client/src/client/ivm-branch.js +1 -3
  555. package/out/zero-client/src/client/ivm-branch.js.map +1 -1
  556. package/out/zero-client/src/client/keys.js +2 -1
  557. package/out/zero-client/src/client/keys.js.map +1 -1
  558. package/out/zero-client/src/client/log-options.js.map +1 -1
  559. package/out/zero-client/src/client/make-replicache-mutators.js +5 -3
  560. package/out/zero-client/src/client/make-replicache-mutators.js.map +1 -1
  561. package/out/zero-client/src/client/metrics.js +3 -5
  562. package/out/zero-client/src/client/metrics.js.map +1 -1
  563. package/out/zero-client/src/client/mutation-tracker.js.map +1 -1
  564. package/out/zero-client/src/client/mutator-proxy.js.map +1 -1
  565. package/out/zero-client/src/client/options.js.map +1 -1
  566. package/out/zero-client/src/client/query-manager.js +2 -2
  567. package/out/zero-client/src/client/query-manager.js.map +1 -1
  568. package/out/zero-client/src/client/reload-error-handler.js +4 -3
  569. package/out/zero-client/src/client/reload-error-handler.js.map +1 -1
  570. package/out/zero-client/src/client/version.js +1 -1
  571. package/out/zero-client/src/client/zero-poke-handler.js +1 -1
  572. package/out/zero-client/src/client/zero-poke-handler.js.map +1 -1
  573. package/out/zero-client/src/client/zero-rep.js.map +1 -1
  574. package/out/zero-client/src/client/zero.js +19 -11
  575. package/out/zero-client/src/client/zero.js.map +1 -1
  576. package/out/zero-client/src/mod.js +23 -23
  577. package/out/zero-client/src/util/nanoid.js.map +1 -1
  578. package/out/zero-permissions/src/permissions.js +3 -2
  579. package/out/zero-permissions/src/permissions.js.map +1 -1
  580. package/out/zero-pg/src/mod.js +9 -9
  581. package/out/zero-protocol/src/analyze-query-result.js +1 -1
  582. package/out/zero-protocol/src/analyze-query-result.js.map +1 -1
  583. package/out/zero-protocol/src/application-error.js.map +1 -1
  584. package/out/zero-protocol/src/ast.js +19 -9
  585. package/out/zero-protocol/src/ast.js.map +1 -1
  586. package/out/zero-protocol/src/change-desired-queries.js.map +1 -1
  587. package/out/zero-protocol/src/client-schema.js +1 -1
  588. package/out/zero-protocol/src/client-schema.js.map +1 -1
  589. package/out/zero-protocol/src/close-connection.js +1 -1
  590. package/out/zero-protocol/src/close-connection.js.map +1 -1
  591. package/out/zero-protocol/src/connect.js +1 -1
  592. package/out/zero-protocol/src/connect.js.map +1 -1
  593. package/out/zero-protocol/src/custom-queries.js +1 -1
  594. package/out/zero-protocol/src/custom-queries.js.map +1 -1
  595. package/out/zero-protocol/src/data.js +4 -2
  596. package/out/zero-protocol/src/data.js.map +1 -1
  597. package/out/zero-protocol/src/delete-clients.js.map +1 -1
  598. package/out/zero-protocol/src/down.d.ts +27 -27
  599. package/out/zero-protocol/src/down.js.map +1 -1
  600. package/out/zero-protocol/src/error.js.map +1 -1
  601. package/out/zero-protocol/src/inspect-down.js +1 -1
  602. package/out/zero-protocol/src/inspect-down.js.map +1 -1
  603. package/out/zero-protocol/src/inspect-up.js +1 -1
  604. package/out/zero-protocol/src/inspect-up.js.map +1 -1
  605. package/out/zero-protocol/src/mutate-server.js +1 -1
  606. package/out/zero-protocol/src/mutate-server.js.map +1 -1
  607. package/out/zero-protocol/src/mutation-id.js.map +1 -1
  608. package/out/zero-protocol/src/mutation.js +1 -1
  609. package/out/zero-protocol/src/mutation.js.map +1 -1
  610. package/out/zero-protocol/src/mutations-patch.js +1 -1
  611. package/out/zero-protocol/src/mutations-patch.js.map +1 -1
  612. package/out/zero-protocol/src/ping.js +1 -1
  613. package/out/zero-protocol/src/ping.js.map +1 -1
  614. package/out/zero-protocol/src/poke.js +1 -1
  615. package/out/zero-protocol/src/poke.js.map +1 -1
  616. package/out/zero-protocol/src/pong.js +1 -1
  617. package/out/zero-protocol/src/pong.js.map +1 -1
  618. package/out/zero-protocol/src/primary-key.js.map +1 -1
  619. package/out/zero-protocol/src/protocol-version.js +1 -0
  620. package/out/zero-protocol/src/protocol-version.js.map +1 -1
  621. package/out/zero-protocol/src/pull.js +1 -1
  622. package/out/zero-protocol/src/pull.js.map +1 -1
  623. package/out/zero-protocol/src/push.js +1 -1
  624. package/out/zero-protocol/src/push.js.map +1 -1
  625. package/out/zero-protocol/src/queries-patch.js +1 -1
  626. package/out/zero-protocol/src/queries-patch.js.map +1 -1
  627. package/out/zero-protocol/src/query-hash-visitor.js.map +1 -1
  628. package/out/zero-protocol/src/query-hash.js +2 -2
  629. package/out/zero-protocol/src/query-server.js +1 -1
  630. package/out/zero-protocol/src/query-server.js.map +1 -1
  631. package/out/zero-protocol/src/row-patch.js.map +1 -1
  632. package/out/zero-protocol/src/up.js.map +1 -1
  633. package/out/zero-protocol/src/update-auth.js.map +1 -1
  634. package/out/zero-protocol/src/version.js.map +1 -1
  635. package/out/zero-react/src/bindings.js +11 -11
  636. package/out/zero-react/src/mod.js +5 -5
  637. package/out/zero-react/src/use-query.js +1 -1
  638. package/out/zero-react/src/use-query.js.map +1 -1
  639. package/out/zero-react/src/zero-provider.d.ts +1 -1
  640. package/out/zero-react/src/zero-provider.d.ts.map +1 -1
  641. package/out/zero-react/src/zero-provider.js +2 -1
  642. package/out/zero-react/src/zero-provider.js.map +1 -1
  643. package/out/zero-react/src/zero.js +23 -23
  644. package/out/zero-schema/src/builder/schema-builder.js +3 -2
  645. package/out/zero-schema/src/builder/schema-builder.js.map +1 -1
  646. package/out/zero-schema/src/builder/table-builder.js +1 -1
  647. package/out/zero-schema/src/builder/table-builder.js.map +1 -1
  648. package/out/zero-schema/src/compiled-permissions.js.map +1 -1
  649. package/out/zero-schema/src/name-mapper.js +7 -5
  650. package/out/zero-schema/src/name-mapper.js.map +1 -1
  651. package/out/zero-schema/src/schema-config.js +1 -1
  652. package/out/zero-schema/src/schema-config.js.map +1 -1
  653. package/out/zero-server/src/adapters/drizzle.js.map +1 -1
  654. package/out/zero-server/src/adapters/kysely.js.map +1 -1
  655. package/out/zero-server/src/adapters/pg.js.map +1 -1
  656. package/out/zero-server/src/adapters/postgresjs.js.map +1 -1
  657. package/out/zero-server/src/adapters/prisma.js.map +1 -1
  658. package/out/zero-server/src/closable-db-transaction.js.map +1 -1
  659. package/out/zero-server/src/custom.js +1 -1
  660. package/out/zero-server/src/custom.js.map +1 -1
  661. package/out/zero-server/src/mod.js +8 -8
  662. package/out/zero-server/src/process-mutations.js.map +1 -1
  663. package/out/zero-server/src/push-processor.js.map +1 -1
  664. package/out/zero-server/src/queries/process-queries.js +2 -1
  665. package/out/zero-server/src/queries/process-queries.js.map +1 -1
  666. package/out/zero-server/src/schema.js +7 -7
  667. package/out/zero-server/src/schema.js.map +1 -1
  668. package/out/zero-server/src/zql-database.js.map +1 -1
  669. package/out/zero-solid/src/bindings.js +11 -11
  670. package/out/zero-solid/src/mod.js +5 -5
  671. package/out/zero-solid/src/solid-view.js +1 -1
  672. package/out/zero-solid/src/solid-view.js.map +1 -1
  673. package/out/zero-solid/src/use-connection-state.js +2 -1
  674. package/out/zero-solid/src/use-connection-state.js.map +1 -1
  675. package/out/zero-solid/src/use-query.js.map +1 -1
  676. package/out/zero-solid/src/use-zero-online.js +2 -1
  677. package/out/zero-solid/src/use-zero-online.js.map +1 -1
  678. package/out/zero-solid/src/use-zero.js +3 -2
  679. package/out/zero-solid/src/use-zero.js.map +1 -1
  680. package/out/zero-solid/src/zero.js +23 -23
  681. package/out/zero-types/src/name-mapper.js.map +1 -1
  682. package/out/zql/src/builder/builder.js +15 -13
  683. package/out/zql/src/builder/builder.js.map +1 -1
  684. package/out/zql/src/builder/correlated-predicate-pushdown.js.map +1 -1
  685. package/out/zql/src/builder/debug-delegate.js.map +1 -1
  686. package/out/zql/src/builder/like.js +0 -1
  687. package/out/zql/src/builder/like.js.map +1 -1
  688. package/out/zql/src/ivm/array-view.js.map +1 -1
  689. package/out/zql/src/ivm/cap.js +2 -4
  690. package/out/zql/src/ivm/cap.js.map +1 -1
  691. package/out/zql/src/ivm/constraint.js +1 -1
  692. package/out/zql/src/ivm/constraint.js.map +1 -1
  693. package/out/zql/src/ivm/data.js.map +1 -1
  694. package/out/zql/src/ivm/deferred-input.js.map +1 -1
  695. package/out/zql/src/ivm/exists.js +20 -18
  696. package/out/zql/src/ivm/exists.js.map +1 -1
  697. package/out/zql/src/ivm/fan-in.js.map +1 -1
  698. package/out/zql/src/ivm/fan-out.js.map +1 -1
  699. package/out/zql/src/ivm/filter-operators.js +1 -1
  700. package/out/zql/src/ivm/filter-operators.js.map +1 -1
  701. package/out/zql/src/ivm/filter-push.js.map +1 -1
  702. package/out/zql/src/ivm/filter.js.map +1 -1
  703. package/out/zql/src/ivm/flipped-join.js +1 -3
  704. package/out/zql/src/ivm/flipped-join.js.map +1 -1
  705. package/out/zql/src/ivm/join-index-tap.js.map +1 -1
  706. package/out/zql/src/ivm/join-index.js.map +1 -1
  707. package/out/zql/src/ivm/join-utils.js +11 -13
  708. package/out/zql/src/ivm/join-utils.js.map +1 -1
  709. package/out/zql/src/ivm/join.js.map +1 -1
  710. package/out/zql/src/ivm/maybe-split-and-push-edit-change.js.map +1 -1
  711. package/out/zql/src/ivm/memory-source.js +26 -22
  712. package/out/zql/src/ivm/memory-source.js.map +1 -1
  713. package/out/zql/src/ivm/memory-storage.js.map +1 -1
  714. package/out/zql/src/ivm/operator.js +2 -2
  715. package/out/zql/src/ivm/push-accumulated.js +0 -1
  716. package/out/zql/src/ivm/push-accumulated.js.map +1 -1
  717. package/out/zql/src/ivm/skip-yields.js.map +1 -1
  718. package/out/zql/src/ivm/skip.js.map +1 -1
  719. package/out/zql/src/ivm/take-gate.js.map +1 -1
  720. package/out/zql/src/ivm/take.js +3 -5
  721. package/out/zql/src/ivm/take.js.map +1 -1
  722. package/out/zql/src/ivm/union-fan-in.js +1 -1
  723. package/out/zql/src/ivm/union-fan-in.js.map +1 -1
  724. package/out/zql/src/ivm/union-fan-out.js.map +1 -1
  725. package/out/zql/src/ivm/view-apply-change.js +1 -1
  726. package/out/zql/src/ivm/view-apply-change.js.map +1 -1
  727. package/out/zql/src/mutate/crud.js +3 -2
  728. package/out/zql/src/mutate/crud.js.map +1 -1
  729. package/out/zql/src/planner/planner-builder.js +1 -1
  730. package/out/zql/src/planner/planner-builder.js.map +1 -1
  731. package/out/zql/src/planner/planner-connection.js.map +1 -1
  732. package/out/zql/src/planner/planner-debug.js.map +1 -1
  733. package/out/zql/src/planner/planner-fan-in.js.map +1 -1
  734. package/out/zql/src/planner/planner-fan-out.js.map +1 -1
  735. package/out/zql/src/planner/planner-filter.js.map +1 -1
  736. package/out/zql/src/planner/planner-graph.js +1 -5
  737. package/out/zql/src/planner/planner-graph.js.map +1 -1
  738. package/out/zql/src/planner/planner-join.js +1 -1
  739. package/out/zql/src/planner/planner-join.js.map +1 -1
  740. package/out/zql/src/planner/planner-source.js.map +1 -1
  741. package/out/zql/src/planner/planner-terminus.js.map +1 -1
  742. package/out/zql/src/query/create-builder.js +2 -1
  743. package/out/zql/src/query/create-builder.js.map +1 -1
  744. package/out/zql/src/query/expression.js +1 -1
  745. package/out/zql/src/query/expression.js.map +1 -1
  746. package/out/zql/src/query/measure-push-operator.js.map +1 -1
  747. package/out/zql/src/query/named.js +2 -1
  748. package/out/zql/src/query/named.js.map +1 -1
  749. package/out/zql/src/query/query-delegate-base.js +1 -1
  750. package/out/zql/src/query/query-delegate-base.js.map +1 -1
  751. package/out/zql/src/query/query-impl.js +3 -2
  752. package/out/zql/src/query/query-impl.js.map +1 -1
  753. package/out/zql/src/query/query-registry.js +1 -1
  754. package/out/zql/src/query/query-registry.js.map +1 -1
  755. package/out/zql/src/query/query-transitions.js +1 -1
  756. package/out/zql/src/query/query-transitions.js.map +1 -1
  757. package/out/zql/src/query/runnable-query-impl.js +3 -2
  758. package/out/zql/src/query/runnable-query-impl.js.map +1 -1
  759. package/out/zql/src/query/static-query.js.map +1 -1
  760. package/out/zql/src/query/ttl.js +8 -8
  761. package/out/zql/src/query/ttl.js.map +1 -1
  762. package/out/zql/src/query/validate-input.js.map +1 -1
  763. package/out/zqlite/src/database-storage.d.ts +27 -8
  764. package/out/zqlite/src/database-storage.d.ts.map +1 -1
  765. package/out/zqlite/src/database-storage.js +195 -26
  766. package/out/zqlite/src/database-storage.js.map +1 -1
  767. package/out/zqlite/src/db.d.ts +47 -1
  768. package/out/zqlite/src/db.d.ts.map +1 -1
  769. package/out/zqlite/src/db.js +121 -21
  770. package/out/zqlite/src/db.js.map +1 -1
  771. package/out/zqlite/src/explain-queries.js.map +1 -1
  772. package/out/zqlite/src/fetch-sql.d.ts +27 -0
  773. package/out/zqlite/src/fetch-sql.d.ts.map +1 -0
  774. package/out/zqlite/src/fetch-sql.js +112 -0
  775. package/out/zqlite/src/fetch-sql.js.map +1 -0
  776. package/out/zqlite/src/index-usage.js.map +1 -1
  777. package/out/zqlite/src/internal/sql-inline.js.map +1 -1
  778. package/out/zqlite/src/internal/sql.js.map +1 -1
  779. package/out/zqlite/src/internal/statement-cache.d.ts +7 -0
  780. package/out/zqlite/src/internal/statement-cache.d.ts.map +1 -1
  781. package/out/zqlite/src/internal/statement-cache.js +11 -4
  782. package/out/zqlite/src/internal/statement-cache.js.map +1 -1
  783. package/out/zqlite/src/mod.js +5 -5
  784. package/out/zqlite/src/pending-delta.js.map +1 -1
  785. package/out/zqlite/src/query-builder.d.ts +11 -1
  786. package/out/zqlite/src/query-builder.d.ts.map +1 -1
  787. package/out/zqlite/src/query-builder.js +22 -7
  788. package/out/zqlite/src/query-builder.js.map +1 -1
  789. package/out/zqlite/src/query-delegate.js.map +1 -1
  790. package/out/zqlite/src/resolve-scalar-subqueries.js +2 -5
  791. package/out/zqlite/src/resolve-scalar-subqueries.js.map +1 -1
  792. package/out/zqlite/src/sqlite-cost-model.js +3 -2
  793. package/out/zqlite/src/sqlite-cost-model.js.map +1 -1
  794. package/out/zqlite/src/sqlite-stat-fanout.js.map +1 -1
  795. package/out/zqlite/src/table-source.d.ts +20 -2
  796. package/out/zqlite/src/table-source.d.ts.map +1 -1
  797. package/out/zqlite/src/table-source.js +73 -39
  798. package/out/zqlite/src/table-source.js.map +1 -1
  799. package/package.json +17 -15
  800. package/out/otel/src/maybe-time.d.ts +0 -3
  801. package/out/otel/src/maybe-time.d.ts.map +0 -1
  802. package/out/otel/src/maybe-time.js +0 -15
  803. package/out/otel/src/maybe-time.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"take.js","names":["#input","#storage","#limit","#partitionKey","#partitionKeyComparator","#dirtyPartitions","#takeGate","#output","#initialFetch","#rowHiddenFromFetch","#setTakeState","#pushEditChange","#getStateAndConstraint","#pushWithRowHiddenFromFetch"],"sources":["../../../../../zql/src/ivm/take.ts"],"sourcesContent":["import {assert, unreachable} from '../../../shared/src/asserts.ts';\nimport {hasOwn} from '../../../shared/src/has-own.ts';\nimport type {Row, Value} from '../../../zero-protocol/src/data.ts';\nimport type {PrimaryKey} from '../../../zero-protocol/src/primary-key.ts';\nimport {assertOrderingIncludesPK} from '../query/complete-ordering.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport {ChangeType} from './change-type.ts';\nimport {\n makeAddChange,\n makeRemoveChange,\n type Change,\n type EditChange,\n} from './change.ts';\nimport type {Constraint} from './constraint.ts';\nimport {compareValues, type Comparator, type Node} from './data.ts';\nimport {\n throwOutput,\n type FetchRequest,\n type Input,\n type InputBase,\n type Operator,\n type Output,\n type Storage,\n} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport {type Stream} from './stream.ts';\nimport type {TakeBoundProvider, TakeGate} from './take-gate.ts';\n\ntype TakeState = {\n size: number;\n bound: Row | undefined;\n};\n\ninterface TakeStorage {\n get(key: string): TakeState | undefined;\n set(key: string, value: TakeState): void;\n del(key: string): void;\n destroy(): void;\n}\n\nexport type PartitionKey = PrimaryKey;\n\ntype DirtyPartitionState = {\n constraint: Constraint | undefined;\n /**\n * The bound to report to upstream TakeGate via getBound() until Phase 2\n * reconciliation completes.\n *\n * When removals reduce partition size below limit in Phase 1, normal getBound()\n * would return undefined (unbounded), which would cause upstream TakeGate to open\n * and allow unbounded parent pushes through. Preserving gateBound keeps TakeGate\n * capped at the pre-removal boundary until Phase 2 refills the deficit.\n */\n gateBound: Row | undefined;\n};\n\n/**\n * The Take operator is for implementing limit queries. It takes the first n\n * nodes of its input as determined by the input’s comparator. It then keeps\n * a *bound* of the last item it has accepted so that it can evaluate whether\n * new incoming pushes should be accepted or rejected.\n *\n * Take can count rows globally or by unique value of some field.\n *\n * Maintains the invariant that its output size is always <= limit, even\n * mid processing of a push.\n */\nexport class Take implements Operator, TakeBoundProvider {\n readonly #input: Input;\n readonly #storage: TakeStorage;\n readonly #limit: number;\n readonly #partitionKey: PartitionKey | undefined;\n readonly #partitionKeyComparator: Comparator | undefined;\n // Fetch overlay needed for some split push cases.\n #rowHiddenFromFetch: Row | undefined;\n\n #takeGate: TakeGate | undefined;\n /**\n * Partitions that incurred removals in Phase 1 and require deficit refills in\n * Phase 2 (reconcile). Keyed by `takeStateKey` (see `getTakeStateKey`), matching\n * `#storage` keys: `'[\"take\"]'` when unpartitioned, or `'[\"take\", ...partitionValues]'`\n * when partitioned.\n */\n readonly #dirtyPartitions = new Map<string, DirtyPartitionState>();\n\n #output: Output = throwOutput;\n\n setTakeGate(gate: TakeGate): void {\n this.#takeGate = gate;\n }\n\n constructor(\n input: Input,\n storage: Storage,\n limit: number,\n partitionKey?: PartitionKey,\n ) {\n assert(limit >= 0, 'Limit must be non-negative');\n const {sort} = input.getSchema();\n assert(sort !== undefined, 'Take requires sorted input');\n assertOrderingIncludesPK(sort, input.getSchema().primaryKey);\n input.setOutput(this);\n this.#input = input;\n this.#storage = storage as TakeStorage;\n this.#limit = limit;\n this.#partitionKey = partitionKey;\n this.#partitionKeyComparator =\n partitionKey && makePartitionKeyComparator(partitionKey);\n }\n\n setOutput(output: Output): void {\n this.#output = output;\n }\n\n getSchema(): SourceSchema {\n return this.#input.getSchema();\n }\n\n getBound(constraint?: Constraint): Row | undefined {\n if (\n this.#partitionKey &&\n !constraintContainsPartitionKey(constraint, this.#partitionKey)\n ) {\n return undefined;\n }\n const takeStateKey = getTakeStateKey(this.#partitionKey, constraint);\n const dirty = this.#dirtyPartitions.get(takeStateKey);\n if (dirty) {\n // While dirty in Phase 1, return gateBound (the bound prior to removals) so\n // upstream operators (e.g. TakeGate) remain capped rather than unbounding during push.\n return dirty.gateBound;\n }\n const takeState = this.#storage.get(takeStateKey);\n if (!takeState || takeState.size < this.#limit) {\n return undefined;\n }\n return takeState.bound;\n }\n\n *fetch(req: FetchRequest): Stream<Node | 'yield'> {\n assert(\n !this.#partitionKey ||\n (req.constraint !== undefined &&\n constraintContainsPartitionKey(req.constraint, this.#partitionKey)),\n 'Partitioned take does not allow unpartitioned fetches',\n );\n\n const takeStateKey = getTakeStateKey(this.#partitionKey, req.constraint);\n const takeState = this.#storage.get(takeStateKey);\n if (!takeState) {\n if (constraintMatchesPartitionKey(req.constraint, this.#partitionKey)) {\n yield* this.#initialFetch(req);\n }\n return;\n }\n if (takeState.bound === undefined) {\n return;\n }\n let count = 0;\n for (const inputNode of this.#input.fetch(req)) {\n if (inputNode === 'yield') {\n yield inputNode;\n continue;\n }\n if (this.getSchema().compareRows(takeState.bound, inputNode.row) < 0) {\n return;\n }\n if (\n this.#rowHiddenFromFetch &&\n this.getSchema().compareRows(\n this.#rowHiddenFromFetch,\n inputNode.row,\n ) === 0\n ) {\n continue;\n }\n yield inputNode;\n count++;\n if (count >= this.#limit) {\n return;\n }\n }\n }\n\n *#initialFetch(req: FetchRequest): Stream<Node | 'yield'> {\n assert(req.start === undefined, 'Start should be undefined');\n assert(!req.reverse, 'Reverse should be false');\n\n if (this.#limit === 0) {\n return;\n }\n\n assert(\n constraintMatchesPartitionKey(req.constraint, this.#partitionKey),\n 'Constraint should match partition key',\n );\n\n const takeStateKey = getTakeStateKey(this.#partitionKey, req.constraint);\n assert(\n this.#storage.get(takeStateKey) === undefined,\n 'Take state should be undefined',\n );\n\n let size = 0;\n let bound: Row | undefined;\n let downstreamEarlyReturn = true;\n let exceptionThrown = false;\n try {\n for (const inputNode of this.#input.fetch(req)) {\n if (inputNode === 'yield') {\n yield 'yield';\n continue;\n }\n yield inputNode;\n bound = inputNode.row;\n size++;\n if (size === this.#limit) {\n break;\n }\n }\n downstreamEarlyReturn = false;\n } catch (e) {\n exceptionThrown = true;\n throw e;\n } finally {\n if (!exceptionThrown) {\n this.#setTakeState(takeStateKey, size, bound);\n // If it becomes necessary to support downstream early return, this\n // assert should be removed, and replaced with code that consumes\n // the input stream until limit is reached or the input stream is\n // exhausted so that takeState is properly hydrated.\n assert(\n !downstreamEarlyReturn,\n 'Unexpected early return prevented full hydration',\n );\n }\n }\n }\n\n #getStateAndConstraint(row: Row) {\n const takeStateKey = getTakeStateKey(this.#partitionKey, row);\n const takeState = this.#storage.get(takeStateKey);\n let constraint: Constraint | undefined;\n if (takeState) {\n constraint =\n this.#partitionKey &&\n Object.fromEntries(\n this.#partitionKey.map(key => [key, row[key]] as const),\n );\n }\n\n return {takeState, takeStateKey, constraint} as\n | {\n takeState: undefined;\n takeStateKey: string;\n constraint: undefined;\n }\n | {\n takeState: TakeState;\n takeStateKey: string;\n constraint: Constraint | undefined;\n };\n }\n\n *push(change: Change): Stream<'yield'> {\n if (change[ChangeIndex.TYPE] === ChangeType.EDIT) {\n yield* this.#pushEditChange(change);\n return;\n }\n\n const {takeState, takeStateKey, constraint} = this.#getStateAndConstraint(\n change[ChangeIndex.NODE].row,\n );\n if (!takeState) {\n return;\n }\n\n const {compareRows} = this.getSchema();\n\n if (change[ChangeIndex.TYPE] === ChangeType.ADD) {\n if (takeState.size < this.#limit) {\n if (this.#dirtyPartitions.has(takeStateKey)) {\n // While dirty with an unresolved deficit in Phase 1, only admit rows that\n // sort strictly before the current window boundary. We refuse to expand the\n // bound forward during Phase 1 so that Phase 2 can backfill earlier candidate\n // rows from storage in sort order.\n if (\n takeState.bound === undefined ||\n compareRows(change[ChangeIndex.NODE].row, takeState.bound) >= 0\n ) {\n return;\n }\n }\n const nextSize = takeState.size + 1;\n if (nextSize === this.#limit) {\n this.#dirtyPartitions.delete(takeStateKey);\n }\n this.#setTakeState(\n takeStateKey,\n nextSize,\n takeState.bound === undefined ||\n compareRows(takeState.bound, change[ChangeIndex.NODE].row) < 0\n ? change[ChangeIndex.NODE].row\n : takeState.bound,\n );\n yield* this.#output.push(change, this);\n return;\n }\n // size === limit\n if (\n takeState.bound === undefined ||\n compareRows(change[ChangeIndex.NODE].row, takeState.bound) >= 0\n ) {\n return;\n }\n // added row < bound\n let beforeBoundNode: Node | undefined;\n let boundNode: Node | undefined;\n if (this.#limit === 1) {\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n boundNode = node;\n break;\n }\n } else {\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n reverse: true,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n } else if (boundNode === undefined) {\n boundNode = node;\n } else {\n beforeBoundNode = node;\n break;\n }\n }\n }\n assert(\n boundNode !== undefined,\n 'Take: boundNode must be found during fetch',\n );\n const removeChange = makeRemoveChange(boundNode);\n // Remove before add to maintain invariant that\n // output size <= limit.\n this.#setTakeState(\n takeStateKey,\n takeState.size,\n beforeBoundNode === undefined ||\n compareRows(change[ChangeIndex.NODE].row, beforeBoundNode.row) > 0\n ? change[ChangeIndex.NODE].row\n : beforeBoundNode.row,\n );\n yield* this.#pushWithRowHiddenFromFetch(\n change[ChangeIndex.NODE].row,\n removeChange,\n );\n yield* this.#output.push(change, this);\n } else if (change[ChangeIndex.TYPE] === ChangeType.REMOVE) {\n if (takeState.bound === undefined) {\n // change is after bound\n return;\n }\n const compToBound = compareRows(\n change[ChangeIndex.NODE].row,\n takeState.bound,\n );\n if (compToBound > 0 || (compToBound < 0 && this.#limit === 1)) {\n // change is not in window\n return;\n }\n let beforeBoundNode: Node | undefined;\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'after',\n },\n constraint,\n reverse: true,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n beforeBoundNode = node;\n break;\n }\n\n const finalBound =\n takeState.size - 1 === 0\n ? undefined\n : compToBound < 0\n ? takeState.bound\n : beforeBoundNode?.row;\n if (!this.#dirtyPartitions.has(takeStateKey)) {\n // Snapshot the pre-removal bound as the gate bound on the first removal that\n // dirties this partition. Subsequent getBound() calls throughout Phase 1 will return\n // this bound to keep upstream TakeGate bounded until Phase 2 refills deficits.\n this.#dirtyPartitions.set(takeStateKey, {\n constraint,\n gateBound: takeState.bound,\n });\n }\n this.#setTakeState(takeStateKey, takeState.size - 1, finalBound);\n yield* this.#output.push(change, this);\n } else if (change[ChangeIndex.TYPE] === ChangeType.CHILD) {\n // A 'child' change should be pushed to output if its row\n // is <= bound.\n if (\n takeState.bound &&\n compareRows(change[ChangeIndex.NODE].row, takeState.bound) <= 0\n ) {\n yield* this.#output.push(change, this);\n }\n }\n }\n\n *#pushEditChange(change: EditChange): Stream<'yield'> {\n assert(\n !this.#partitionKeyComparator ||\n this.#partitionKeyComparator(\n change[ChangeIndex.OLD_NODE].row,\n change[ChangeIndex.NODE].row,\n ) === 0,\n 'Unexpected change of partition key',\n );\n\n const {takeState, takeStateKey, constraint} = this.#getStateAndConstraint(\n change[ChangeIndex.OLD_NODE].row,\n );\n if (!takeState) {\n return;\n }\n\n assert(takeState.bound, 'Bound should be set');\n const {compareRows} = this.getSchema();\n const oldCmp = compareRows(\n change[ChangeIndex.OLD_NODE].row,\n takeState.bound,\n );\n const newCmp = compareRows(change[ChangeIndex.NODE].row, takeState.bound);\n\n const replaceBoundAndForwardChange = () => {\n this.#setTakeState(\n takeStateKey,\n takeState.size,\n change[ChangeIndex.NODE].row,\n );\n return this.#output.push(change, this);\n };\n\n // The bounds row was changed.\n if (oldCmp === 0) {\n // The new row is the new bound.\n if (newCmp === 0) {\n // no need to update the state since we are keeping the bounds\n yield* this.#output.push(change, this);\n return;\n }\n\n if (newCmp < 0) {\n if (this.#limit === 1) {\n yield* replaceBoundAndForwardChange();\n return;\n }\n\n // New row will be in the result but it might not be the bounds any\n // more. We need to find the row before the bounds to determine the new\n // bounds.\n\n let beforeBoundNode: Node | undefined;\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'after',\n },\n constraint,\n reverse: true,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n beforeBoundNode = node;\n break;\n }\n assert(\n beforeBoundNode !== undefined,\n 'Take: beforeBoundNode must be found during fetch',\n );\n\n this.#setTakeState(takeStateKey, takeState.size, beforeBoundNode.row);\n yield* this.#output.push(change, this);\n return;\n }\n\n assert(newCmp > 0, 'New comparison must be greater than 0');\n // Find the first item at the old bounds. This will be the new bounds.\n let newBoundNode: Node | undefined;\n this.#takeGate?.open();\n try {\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n newBoundNode = node;\n break;\n }\n } finally {\n this.#takeGate?.close();\n }\n assert(\n newBoundNode !== undefined,\n 'Take: newBoundNode must be found during fetch',\n );\n\n // The next row is the new row. We can replace the bounds and keep the\n // edit change.\n if (compareRows(newBoundNode.row, change[ChangeIndex.NODE].row) === 0) {\n yield* replaceBoundAndForwardChange();\n return;\n }\n\n // The new row is now outside the bounds, so we need to remove the old\n // row and add the new bounds row.\n this.#setTakeState(takeStateKey, takeState.size, newBoundNode.row);\n yield* this.#pushWithRowHiddenFromFetch(\n newBoundNode.row,\n makeRemoveChange(change[ChangeIndex.OLD_NODE]),\n );\n yield* this.#output.push(makeAddChange(newBoundNode), this);\n return;\n }\n\n if (oldCmp > 0) {\n assert(newCmp !== 0, 'Invalid state. Row has duplicate primary key');\n\n // Both old and new outside of bounds\n if (newCmp > 0) {\n return;\n }\n\n // old was outside, new is inside. Pushing out the old bounds\n assert(newCmp < 0, 'New comparison must be less than 0');\n\n let oldBoundNode: Node | undefined;\n let newBoundNode: Node | undefined;\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n reverse: true,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n } else if (oldBoundNode === undefined) {\n oldBoundNode = node;\n } else {\n newBoundNode = node;\n break;\n }\n }\n assert(\n oldBoundNode !== undefined,\n 'Take: oldBoundNode must be found during fetch',\n );\n assert(\n newBoundNode !== undefined,\n 'Take: newBoundNode must be found during fetch',\n );\n\n // Remove before add to maintain invariant that\n // output size <= limit.\n this.#setTakeState(takeStateKey, takeState.size, newBoundNode.row);\n yield* this.#pushWithRowHiddenFromFetch(\n change[ChangeIndex.NODE].row,\n makeRemoveChange(oldBoundNode),\n );\n yield* this.#output.push(makeAddChange(change[ChangeIndex.NODE]), this);\n\n return;\n }\n\n if (oldCmp < 0) {\n assert(newCmp !== 0, 'Invalid state. Row has duplicate primary key');\n\n // Both old and new inside of bounds\n if (newCmp < 0) {\n yield* this.#output.push(change, this);\n return;\n }\n\n // old was inside, new is larger than old bound\n\n assert(newCmp > 0, 'New comparison must be greater than 0');\n\n // at this point we need to find the row after the bound and use that or\n // the newRow as the new bound.\n let afterBoundNode: Node | undefined;\n this.#takeGate?.open();\n try {\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'after',\n },\n constraint,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n afterBoundNode = node;\n break;\n }\n } finally {\n this.#takeGate?.close();\n }\n assert(\n afterBoundNode !== undefined,\n 'Take: afterBoundNode must be found during fetch',\n );\n\n // The new row is the new bound. Use an edit change.\n if (compareRows(afterBoundNode.row, change[ChangeIndex.NODE].row) === 0) {\n yield* replaceBoundAndForwardChange();\n return;\n }\n\n yield* this.#output.push(\n makeRemoveChange(change[ChangeIndex.OLD_NODE]),\n this,\n );\n this.#setTakeState(takeStateKey, takeState.size, afterBoundNode.row);\n yield* this.#output.push(makeAddChange(afterBoundNode), this);\n return;\n }\n\n unreachable();\n }\n\n *#pushWithRowHiddenFromFetch(row: Row, change: Change) {\n this.#rowHiddenFromFetch = row;\n try {\n yield* this.#output.push(change, this);\n } finally {\n this.#rowHiddenFromFetch = undefined;\n }\n }\n\n #setTakeState(takeStateKey: string, size: number, bound: Row | undefined) {\n this.#storage.set(takeStateKey, {\n size,\n bound,\n });\n }\n\n destroy(): void {\n this.#input.destroy();\n this.#storage.destroy();\n }\n\n *reconcile(_pusher?: InputBase): Stream<'yield'> {\n if (this.#dirtyPartitions.size > 0) {\n const dirty = [...this.#dirtyPartitions.entries()];\n this.#dirtyPartitions.clear();\n\n for (const [takeStateKey, {constraint}] of dirty) {\n const takeState = this.#storage.get(takeStateKey);\n assert(\n takeState !== undefined,\n 'Take: dirty partition must exist in storage',\n );\n const deficit = this.#limit - takeState.size;\n assert(deficit > 0, 'Take: dirty partition must have a deficit');\n\n const toPush: Node[] = [];\n this.#takeGate?.open();\n try {\n const stream = this.#input.fetch({\n start: takeState.bound\n ? {\n row: takeState.bound,\n basis: 'after',\n }\n : undefined,\n constraint,\n });\n\n for (const node of stream) {\n if (node === 'yield') {\n yield 'yield';\n continue;\n }\n toPush.push(node);\n if (toPush.length === deficit) {\n break;\n }\n }\n } finally {\n this.#takeGate?.close();\n }\n\n let currentSize = takeState.size;\n for (const node of toPush) {\n currentSize++;\n this.#setTakeState(takeStateKey, currentSize, node.row);\n yield* this.#output.push(makeAddChange(node), this);\n }\n }\n }\n\n if (this.#output.reconcile) {\n yield* this.#output.reconcile(this);\n }\n }\n}\n\nfunction getTakeStateKey(\n partitionKey: PartitionKey | undefined,\n rowOrConstraint: Row | Constraint | undefined,\n): string {\n // The order must be consistent. We always use the order as defined by the\n // partition key.\n const partitionValues: Value[] = [];\n\n if (partitionKey && rowOrConstraint) {\n for (const key of partitionKey) {\n partitionValues.push(rowOrConstraint[key]);\n }\n }\n\n return JSON.stringify(['take', ...partitionValues]);\n}\n\nexport function constraintMatchesPartitionKey(\n constraint: Constraint | undefined,\n partitionKey: PartitionKey | undefined,\n): boolean {\n if (constraint === undefined || partitionKey === undefined) {\n return constraint === partitionKey;\n }\n if (partitionKey.length !== Object.keys(constraint).length) {\n return false;\n }\n for (const key of partitionKey) {\n if (!hasOwn(constraint, key)) {\n return false;\n }\n }\n return true;\n}\n\nexport function constraintContainsPartitionKey(\n constraint: Constraint | undefined,\n partitionKey: PartitionKey | undefined,\n): boolean {\n if (constraint === undefined || partitionKey === undefined) {\n return false;\n }\n for (const key of partitionKey) {\n if (!hasOwn(constraint, key)) {\n return false;\n }\n }\n return true;\n}\n\nexport function makePartitionKeyComparator(\n partitionKey: PartitionKey,\n): Comparator {\n return (a, b) => {\n for (const key of partitionKey) {\n const cmp = compareValues(a[key], b[key]);\n if (cmp !== 0) {\n return cmp;\n }\n }\n return 0;\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"take.js","names":[],"sources":["../../../../../zql/src/ivm/take.ts"],"sourcesContent":["import {assert, unreachable} from '../../../shared/src/asserts.ts';\nimport {hasOwn} from '../../../shared/src/has-own.ts';\nimport type {Row, Value} from '../../../zero-protocol/src/data.ts';\nimport type {PrimaryKey} from '../../../zero-protocol/src/primary-key.ts';\nimport {assertOrderingIncludesPK} from '../query/complete-ordering.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport {ChangeType} from './change-type.ts';\nimport {\n makeAddChange,\n makeRemoveChange,\n type Change,\n type EditChange,\n} from './change.ts';\nimport type {Constraint} from './constraint.ts';\nimport {compareValues, type Comparator, type Node} from './data.ts';\nimport {\n throwOutput,\n type FetchRequest,\n type Input,\n type InputBase,\n type Operator,\n type Output,\n type Storage,\n} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport {type Stream} from './stream.ts';\nimport type {TakeBoundProvider, TakeGate} from './take-gate.ts';\n\ntype TakeState = {\n size: number;\n bound: Row | undefined;\n};\n\ninterface TakeStorage {\n get(key: string): TakeState | undefined;\n set(key: string, value: TakeState): void;\n del(key: string): void;\n destroy(): void;\n}\n\nexport type PartitionKey = PrimaryKey;\n\ntype DirtyPartitionState = {\n constraint: Constraint | undefined;\n /**\n * The bound to report to upstream TakeGate via getBound() until Phase 2\n * reconciliation completes.\n *\n * When removals reduce partition size below limit in Phase 1, normal getBound()\n * would return undefined (unbounded), which would cause upstream TakeGate to open\n * and allow unbounded parent pushes through. Preserving gateBound keeps TakeGate\n * capped at the pre-removal boundary until Phase 2 refills the deficit.\n */\n gateBound: Row | undefined;\n};\n\n/**\n * The Take operator is for implementing limit queries. It takes the first n\n * nodes of its input as determined by the input’s comparator. It then keeps\n * a *bound* of the last item it has accepted so that it can evaluate whether\n * new incoming pushes should be accepted or rejected.\n *\n * Take can count rows globally or by unique value of some field.\n *\n * Maintains the invariant that its output size is always <= limit, even\n * mid processing of a push.\n */\nexport class Take implements Operator, TakeBoundProvider {\n readonly #input: Input;\n readonly #storage: TakeStorage;\n readonly #limit: number;\n readonly #partitionKey: PartitionKey | undefined;\n readonly #partitionKeyComparator: Comparator | undefined;\n // Fetch overlay needed for some split push cases.\n #rowHiddenFromFetch: Row | undefined;\n\n #takeGate: TakeGate | undefined;\n /**\n * Partitions that incurred removals in Phase 1 and require deficit refills in\n * Phase 2 (reconcile). Keyed by `takeStateKey` (see `getTakeStateKey`), matching\n * `#storage` keys: `'[\"take\"]'` when unpartitioned, or `'[\"take\", ...partitionValues]'`\n * when partitioned.\n */\n readonly #dirtyPartitions = new Map<string, DirtyPartitionState>();\n\n #output: Output = throwOutput;\n\n setTakeGate(gate: TakeGate): void {\n this.#takeGate = gate;\n }\n\n constructor(\n input: Input,\n storage: Storage,\n limit: number,\n partitionKey?: PartitionKey,\n ) {\n assert(limit >= 0, 'Limit must be non-negative');\n const {sort} = input.getSchema();\n assert(sort !== undefined, 'Take requires sorted input');\n assertOrderingIncludesPK(sort, input.getSchema().primaryKey);\n input.setOutput(this);\n this.#input = input;\n this.#storage = storage as TakeStorage;\n this.#limit = limit;\n this.#partitionKey = partitionKey;\n this.#partitionKeyComparator =\n partitionKey && makePartitionKeyComparator(partitionKey);\n }\n\n setOutput(output: Output): void {\n this.#output = output;\n }\n\n getSchema(): SourceSchema {\n return this.#input.getSchema();\n }\n\n getBound(constraint?: Constraint): Row | undefined {\n if (\n this.#partitionKey &&\n !constraintContainsPartitionKey(constraint, this.#partitionKey)\n ) {\n return undefined;\n }\n const takeStateKey = getTakeStateKey(this.#partitionKey, constraint);\n const dirty = this.#dirtyPartitions.get(takeStateKey);\n if (dirty) {\n // While dirty in Phase 1, return gateBound (the bound prior to removals) so\n // upstream operators (e.g. TakeGate) remain capped rather than unbounding during push.\n return dirty.gateBound;\n }\n const takeState = this.#storage.get(takeStateKey);\n if (!takeState || takeState.size < this.#limit) {\n return undefined;\n }\n return takeState.bound;\n }\n\n *fetch(req: FetchRequest): Stream<Node | 'yield'> {\n assert(\n !this.#partitionKey ||\n (req.constraint !== undefined &&\n constraintContainsPartitionKey(req.constraint, this.#partitionKey)),\n 'Partitioned take does not allow unpartitioned fetches',\n );\n\n const takeStateKey = getTakeStateKey(this.#partitionKey, req.constraint);\n const takeState = this.#storage.get(takeStateKey);\n if (!takeState) {\n if (constraintMatchesPartitionKey(req.constraint, this.#partitionKey)) {\n yield* this.#initialFetch(req, takeStateKey);\n }\n return;\n }\n if (takeState.bound === undefined) {\n return;\n }\n let count = 0;\n for (const inputNode of this.#input.fetch(req)) {\n if (inputNode === 'yield') {\n yield inputNode;\n continue;\n }\n if (this.getSchema().compareRows(takeState.bound, inputNode.row) < 0) {\n return;\n }\n if (\n this.#rowHiddenFromFetch &&\n this.getSchema().compareRows(\n this.#rowHiddenFromFetch,\n inputNode.row,\n ) === 0\n ) {\n continue;\n }\n yield inputNode;\n count++;\n if (count >= this.#limit) {\n return;\n }\n }\n }\n\n *#initialFetch(\n req: FetchRequest,\n takeStateKey: string,\n ): Stream<Node | 'yield'> {\n assert(req.start === undefined, 'Start should be undefined');\n assert(!req.reverse, 'Reverse should be false');\n\n if (this.#limit === 0) {\n return;\n }\n\n assert(\n constraintMatchesPartitionKey(req.constraint, this.#partitionKey),\n 'Constraint should match partition key',\n );\n\n // The caller (fetch) has already checked that there is no state for\n // takeStateKey.\n\n let size = 0;\n let bound: Row | undefined;\n let downstreamEarlyReturn = true;\n let exceptionThrown = false;\n try {\n for (const inputNode of this.#input.fetch(req)) {\n if (inputNode === 'yield') {\n yield 'yield';\n continue;\n }\n yield inputNode;\n bound = inputNode.row;\n size++;\n if (size === this.#limit) {\n break;\n }\n }\n downstreamEarlyReturn = false;\n } catch (e) {\n exceptionThrown = true;\n throw e;\n } finally {\n if (!exceptionThrown) {\n this.#setTakeState(takeStateKey, size, bound);\n // If it becomes necessary to support downstream early return, this\n // assert should be removed, and replaced with code that consumes\n // the input stream until limit is reached or the input stream is\n // exhausted so that takeState is properly hydrated.\n assert(\n !downstreamEarlyReturn,\n 'Unexpected early return prevented full hydration',\n );\n }\n }\n }\n\n #getStateAndConstraint(row: Row) {\n const takeStateKey = getTakeStateKey(this.#partitionKey, row);\n const takeState = this.#storage.get(takeStateKey);\n let constraint: Constraint | undefined;\n if (takeState) {\n constraint =\n this.#partitionKey &&\n Object.fromEntries(\n this.#partitionKey.map(key => [key, row[key]] as const),\n );\n }\n\n return {takeState, takeStateKey, constraint} as\n | {\n takeState: undefined;\n takeStateKey: string;\n constraint: undefined;\n }\n | {\n takeState: TakeState;\n takeStateKey: string;\n constraint: Constraint | undefined;\n };\n }\n\n *push(change: Change): Stream<'yield'> {\n if (change[ChangeIndex.TYPE] === ChangeType.EDIT) {\n yield* this.#pushEditChange(change);\n return;\n }\n\n const {takeState, takeStateKey, constraint} = this.#getStateAndConstraint(\n change[ChangeIndex.NODE].row,\n );\n if (!takeState) {\n return;\n }\n\n const {compareRows} = this.getSchema();\n\n if (change[ChangeIndex.TYPE] === ChangeType.ADD) {\n if (takeState.size < this.#limit) {\n if (this.#dirtyPartitions.has(takeStateKey)) {\n // While dirty with an unresolved deficit in Phase 1, only admit rows that\n // sort strictly before the current window boundary. We refuse to expand the\n // bound forward during Phase 1 so that Phase 2 can backfill earlier candidate\n // rows from storage in sort order.\n if (\n takeState.bound === undefined ||\n compareRows(change[ChangeIndex.NODE].row, takeState.bound) >= 0\n ) {\n return;\n }\n }\n const nextSize = takeState.size + 1;\n if (nextSize === this.#limit) {\n this.#dirtyPartitions.delete(takeStateKey);\n }\n this.#setTakeState(\n takeStateKey,\n nextSize,\n takeState.bound === undefined ||\n compareRows(takeState.bound, change[ChangeIndex.NODE].row) < 0\n ? change[ChangeIndex.NODE].row\n : takeState.bound,\n );\n yield* this.#output.push(change, this);\n return;\n }\n // size === limit\n if (\n takeState.bound === undefined ||\n compareRows(change[ChangeIndex.NODE].row, takeState.bound) >= 0\n ) {\n return;\n }\n // added row < bound\n let beforeBoundNode: Node | undefined;\n let boundNode: Node | undefined;\n if (this.#limit === 1) {\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n boundNode = node;\n break;\n }\n } else {\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n reverse: true,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n } else if (boundNode === undefined) {\n boundNode = node;\n } else {\n beforeBoundNode = node;\n break;\n }\n }\n }\n assert(\n boundNode !== undefined,\n 'Take: boundNode must be found during fetch',\n );\n const removeChange = makeRemoveChange(boundNode);\n // Remove before add to maintain invariant that\n // output size <= limit.\n this.#setTakeState(\n takeStateKey,\n takeState.size,\n beforeBoundNode === undefined ||\n compareRows(change[ChangeIndex.NODE].row, beforeBoundNode.row) > 0\n ? change[ChangeIndex.NODE].row\n : beforeBoundNode.row,\n );\n yield* this.#pushWithRowHiddenFromFetch(\n change[ChangeIndex.NODE].row,\n removeChange,\n );\n yield* this.#output.push(change, this);\n } else if (change[ChangeIndex.TYPE] === ChangeType.REMOVE) {\n if (takeState.bound === undefined) {\n // change is after bound\n return;\n }\n const compToBound = compareRows(\n change[ChangeIndex.NODE].row,\n takeState.bound,\n );\n if (compToBound > 0 || (compToBound < 0 && this.#limit === 1)) {\n // change is not in window\n return;\n }\n let beforeBoundNode: Node | undefined;\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'after',\n },\n constraint,\n reverse: true,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n beforeBoundNode = node;\n break;\n }\n\n const finalBound =\n takeState.size - 1 === 0\n ? undefined\n : compToBound < 0\n ? takeState.bound\n : beforeBoundNode?.row;\n if (!this.#dirtyPartitions.has(takeStateKey)) {\n // Snapshot the pre-removal bound as the gate bound on the first removal that\n // dirties this partition. Subsequent getBound() calls throughout Phase 1 will return\n // this bound to keep upstream TakeGate bounded until Phase 2 refills deficits.\n this.#dirtyPartitions.set(takeStateKey, {\n constraint,\n gateBound: takeState.bound,\n });\n }\n this.#setTakeState(takeStateKey, takeState.size - 1, finalBound);\n yield* this.#output.push(change, this);\n } else if (change[ChangeIndex.TYPE] === ChangeType.CHILD) {\n // A 'child' change should be pushed to output if its row\n // is <= bound.\n if (\n takeState.bound &&\n compareRows(change[ChangeIndex.NODE].row, takeState.bound) <= 0\n ) {\n yield* this.#output.push(change, this);\n }\n }\n }\n\n *#pushEditChange(change: EditChange): Stream<'yield'> {\n assert(\n !this.#partitionKeyComparator ||\n this.#partitionKeyComparator(\n change[ChangeIndex.OLD_NODE].row,\n change[ChangeIndex.NODE].row,\n ) === 0,\n 'Unexpected change of partition key',\n );\n\n const {takeState, takeStateKey, constraint} = this.#getStateAndConstraint(\n change[ChangeIndex.OLD_NODE].row,\n );\n if (!takeState) {\n return;\n }\n\n assert(takeState.bound, 'Bound should be set');\n const {compareRows} = this.getSchema();\n const oldCmp = compareRows(\n change[ChangeIndex.OLD_NODE].row,\n takeState.bound,\n );\n const newCmp = compareRows(change[ChangeIndex.NODE].row, takeState.bound);\n\n const replaceBoundAndForwardChange = () => {\n this.#setTakeState(\n takeStateKey,\n takeState.size,\n change[ChangeIndex.NODE].row,\n );\n return this.#output.push(change, this);\n };\n\n // The bounds row was changed.\n if (oldCmp === 0) {\n // The new row is the new bound.\n if (newCmp === 0) {\n // no need to update the state since we are keeping the bounds\n yield* this.#output.push(change, this);\n return;\n }\n\n if (newCmp < 0) {\n if (this.#limit === 1) {\n yield* replaceBoundAndForwardChange();\n return;\n }\n\n // New row will be in the result but it might not be the bounds any\n // more. We need to find the row before the bounds to determine the new\n // bounds.\n\n let beforeBoundNode: Node | undefined;\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'after',\n },\n constraint,\n reverse: true,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n beforeBoundNode = node;\n break;\n }\n assert(\n beforeBoundNode !== undefined,\n 'Take: beforeBoundNode must be found during fetch',\n );\n\n this.#setTakeState(takeStateKey, takeState.size, beforeBoundNode.row);\n yield* this.#output.push(change, this);\n return;\n }\n\n assert(newCmp > 0, 'New comparison must be greater than 0');\n // Find the first item at the old bounds. This will be the new bounds.\n let newBoundNode: Node | undefined;\n this.#takeGate?.open();\n try {\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n newBoundNode = node;\n break;\n }\n } finally {\n this.#takeGate?.close();\n }\n assert(\n newBoundNode !== undefined,\n 'Take: newBoundNode must be found during fetch',\n );\n\n // The next row is the new row. We can replace the bounds and keep the\n // edit change.\n if (compareRows(newBoundNode.row, change[ChangeIndex.NODE].row) === 0) {\n yield* replaceBoundAndForwardChange();\n return;\n }\n\n // The new row is now outside the bounds, so we need to remove the old\n // row and add the new bounds row.\n this.#setTakeState(takeStateKey, takeState.size, newBoundNode.row);\n yield* this.#pushWithRowHiddenFromFetch(\n newBoundNode.row,\n makeRemoveChange(change[ChangeIndex.OLD_NODE]),\n );\n yield* this.#output.push(makeAddChange(newBoundNode), this);\n return;\n }\n\n if (oldCmp > 0) {\n assert(newCmp !== 0, 'Invalid state. Row has duplicate primary key');\n\n // Both old and new outside of bounds\n if (newCmp > 0) {\n return;\n }\n\n // old was outside, new is inside. Pushing out the old bounds\n assert(newCmp < 0, 'New comparison must be less than 0');\n\n let oldBoundNode: Node | undefined;\n let newBoundNode: Node | undefined;\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'at',\n },\n constraint,\n reverse: true,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n } else if (oldBoundNode === undefined) {\n oldBoundNode = node;\n } else {\n newBoundNode = node;\n break;\n }\n }\n assert(\n oldBoundNode !== undefined,\n 'Take: oldBoundNode must be found during fetch',\n );\n assert(\n newBoundNode !== undefined,\n 'Take: newBoundNode must be found during fetch',\n );\n\n // Remove before add to maintain invariant that\n // output size <= limit.\n this.#setTakeState(takeStateKey, takeState.size, newBoundNode.row);\n yield* this.#pushWithRowHiddenFromFetch(\n change[ChangeIndex.NODE].row,\n makeRemoveChange(oldBoundNode),\n );\n yield* this.#output.push(makeAddChange(change[ChangeIndex.NODE]), this);\n\n return;\n }\n\n if (oldCmp < 0) {\n assert(newCmp !== 0, 'Invalid state. Row has duplicate primary key');\n\n // Both old and new inside of bounds\n if (newCmp < 0) {\n yield* this.#output.push(change, this);\n return;\n }\n\n // old was inside, new is larger than old bound\n\n assert(newCmp > 0, 'New comparison must be greater than 0');\n\n // at this point we need to find the row after the bound and use that or\n // the newRow as the new bound.\n let afterBoundNode: Node | undefined;\n this.#takeGate?.open();\n try {\n for (const node of this.#input.fetch({\n start: {\n row: takeState.bound,\n basis: 'after',\n },\n constraint,\n })) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n afterBoundNode = node;\n break;\n }\n } finally {\n this.#takeGate?.close();\n }\n assert(\n afterBoundNode !== undefined,\n 'Take: afterBoundNode must be found during fetch',\n );\n\n // The new row is the new bound. Use an edit change.\n if (compareRows(afterBoundNode.row, change[ChangeIndex.NODE].row) === 0) {\n yield* replaceBoundAndForwardChange();\n return;\n }\n\n yield* this.#output.push(\n makeRemoveChange(change[ChangeIndex.OLD_NODE]),\n this,\n );\n this.#setTakeState(takeStateKey, takeState.size, afterBoundNode.row);\n yield* this.#output.push(makeAddChange(afterBoundNode), this);\n return;\n }\n\n unreachable();\n }\n\n *#pushWithRowHiddenFromFetch(row: Row, change: Change) {\n this.#rowHiddenFromFetch = row;\n try {\n yield* this.#output.push(change, this);\n } finally {\n this.#rowHiddenFromFetch = undefined;\n }\n }\n\n #setTakeState(takeStateKey: string, size: number, bound: Row | undefined) {\n this.#storage.set(takeStateKey, {\n size,\n bound,\n });\n }\n\n destroy(): void {\n this.#input.destroy();\n this.#storage.destroy();\n }\n\n *reconcile(_pusher?: InputBase): Stream<'yield'> {\n if (this.#dirtyPartitions.size > 0) {\n const dirty = [...this.#dirtyPartitions.entries()];\n this.#dirtyPartitions.clear();\n\n for (const [takeStateKey, {constraint}] of dirty) {\n const takeState = this.#storage.get(takeStateKey);\n assert(\n takeState !== undefined,\n 'Take: dirty partition must exist in storage',\n );\n const deficit = this.#limit - takeState.size;\n assert(deficit > 0, 'Take: dirty partition must have a deficit');\n\n const toPush: Node[] = [];\n this.#takeGate?.open();\n try {\n const stream = this.#input.fetch({\n start: takeState.bound\n ? {\n row: takeState.bound,\n basis: 'after',\n }\n : undefined,\n constraint,\n });\n\n for (const node of stream) {\n if (node === 'yield') {\n yield 'yield';\n continue;\n }\n toPush.push(node);\n if (toPush.length === deficit) {\n break;\n }\n }\n } finally {\n this.#takeGate?.close();\n }\n\n let currentSize = takeState.size;\n for (const node of toPush) {\n currentSize++;\n this.#setTakeState(takeStateKey, currentSize, node.row);\n yield* this.#output.push(makeAddChange(node), this);\n }\n }\n }\n\n if (this.#output.reconcile) {\n yield* this.#output.reconcile(this);\n }\n }\n}\n\nfunction getTakeStateKey(\n partitionKey: PartitionKey | undefined,\n rowOrConstraint: Row | Constraint | undefined,\n): string {\n // The order must be consistent. We always use the order as defined by the\n // partition key.\n const partitionValues: Value[] = [];\n\n if (partitionKey && rowOrConstraint) {\n for (const key of partitionKey) {\n partitionValues.push(rowOrConstraint[key]);\n }\n }\n\n return JSON.stringify(['take', ...partitionValues]);\n}\n\nexport function constraintMatchesPartitionKey(\n constraint: Constraint | undefined,\n partitionKey: PartitionKey | undefined,\n): boolean {\n if (constraint === undefined || partitionKey === undefined) {\n return constraint === partitionKey;\n }\n if (partitionKey.length !== Object.keys(constraint).length) {\n return false;\n }\n for (const key of partitionKey) {\n if (!hasOwn(constraint, key)) {\n return false;\n }\n }\n return true;\n}\n\nexport function constraintContainsPartitionKey(\n constraint: Constraint | undefined,\n partitionKey: PartitionKey | undefined,\n): boolean {\n if (constraint === undefined || partitionKey === undefined) {\n return false;\n }\n for (const key of partitionKey) {\n if (!hasOwn(constraint, key)) {\n return false;\n }\n }\n return true;\n}\n\nexport function makePartitionKeyComparator(\n partitionKey: PartitionKey,\n): Comparator {\n return (a, b) => {\n for (const key of partitionKey) {\n const cmp = compareValues(a[key], b[key]);\n if (cmp !== 0) {\n return cmp;\n }\n }\n return 0;\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAmEA,IAAa,OAAb,MAAyD;CACvD;CACA;CACA;CACA;CACA;CAEA;CAEA;;;;;;;CAOA,mCAA4B,IAAI,IAAiC;CAEjE,UAAkB;CAElB,YAAY,MAAsB;EAChC,KAAK,YAAY;CACnB;CAEA,YACE,OACA,SACA,OACA,cACA;EACA,OAAO,SAAS,GAAG,4BAA4B;EAC/C,MAAM,EAAC,SAAQ,MAAM,UAAU;EAC/B,OAAO,SAAS,KAAA,GAAW,4BAA4B;EACvD,yBAAyB,MAAM,MAAM,UAAU,CAAC,CAAC,UAAU;EAC3D,MAAM,UAAU,IAAI;EACpB,KAAK,SAAS;EACd,KAAK,WAAW;EAChB,KAAK,SAAS;EACd,KAAK,gBAAgB;EACrB,KAAK,0BACH,gBAAgB,2BAA2B,YAAY;CAC3D;CAEA,UAAU,QAAsB;EAC9B,KAAK,UAAU;CACjB;CAEA,YAA0B;EACxB,OAAO,KAAK,OAAO,UAAU;CAC/B;CAEA,SAAS,YAA0C;EACjD,IACE,KAAK,iBACL,CAAC,+BAA+B,YAAY,KAAK,aAAa,GAE9D;EAEF,MAAM,eAAe,gBAAgB,KAAK,eAAe,UAAU;EACnE,MAAM,QAAQ,KAAK,iBAAiB,IAAI,YAAY;EACpD,IAAI,OAGF,OAAO,MAAM;EAEf,MAAM,YAAY,KAAK,SAAS,IAAI,YAAY;EAChD,IAAI,CAAC,aAAa,UAAU,OAAO,KAAK,QACtC;EAEF,OAAO,UAAU;CACnB;CAEA,CAAC,MAAM,KAA2C;EAChD,OACE,CAAC,KAAK,iBACH,IAAI,eAAe,KAAA,KAClB,+BAA+B,IAAI,YAAY,KAAK,aAAa,GACrE,uDACF;EAEA,MAAM,eAAe,gBAAgB,KAAK,eAAe,IAAI,UAAU;EACvE,MAAM,YAAY,KAAK,SAAS,IAAI,YAAY;EAChD,IAAI,CAAC,WAAW;GACd,IAAI,8BAA8B,IAAI,YAAY,KAAK,aAAa,GAClE,OAAO,KAAK,cAAc,KAAK,YAAY;GAE7C;EACF;EACA,IAAI,UAAU,UAAU,KAAA,GACtB;EAEF,IAAI,QAAQ;EACZ,KAAK,MAAM,aAAa,KAAK,OAAO,MAAM,GAAG,GAAG;GAC9C,IAAI,cAAc,SAAS;IACzB,MAAM;IACN;GACF;GACA,IAAI,KAAK,UAAU,CAAC,CAAC,YAAY,UAAU,OAAO,UAAU,GAAG,IAAI,GACjE;GAEF,IACE,KAAK,uBACL,KAAK,UAAU,CAAC,CAAC,YACf,KAAK,qBACL,UAAU,GACZ,MAAM,GAEN;GAEF,MAAM;GACN;GACA,IAAI,SAAS,KAAK,QAChB;EAEJ;CACF;CAEA,CAAC,cACC,KACA,cACwB;EACxB,OAAO,IAAI,UAAU,KAAA,GAAW,2BAA2B;EAC3D,OAAO,CAAC,IAAI,SAAS,yBAAyB;EAE9C,IAAI,KAAK,WAAW,GAClB;EAGF,OACE,8BAA8B,IAAI,YAAY,KAAK,aAAa,GAChE,uCACF;EAKA,IAAI,OAAO;EACX,IAAI;EACJ,IAAI,wBAAwB;EAC5B,IAAI,kBAAkB;EACtB,IAAI;GACF,KAAK,MAAM,aAAa,KAAK,OAAO,MAAM,GAAG,GAAG;IAC9C,IAAI,cAAc,SAAS;KACzB,MAAM;KACN;IACF;IACA,MAAM;IACN,QAAQ,UAAU;IAClB;IACA,IAAI,SAAS,KAAK,QAChB;GAEJ;GACA,wBAAwB;EAC1B,SAAS,GAAG;GACV,kBAAkB;GAClB,MAAM;EACR,UAAU;GACR,IAAI,CAAC,iBAAiB;IACpB,KAAK,cAAc,cAAc,MAAM,KAAK;IAK5C,OACE,CAAC,uBACD,kDACF;GACF;EACF;CACF;CAEA,uBAAuB,KAAU;EAC/B,MAAM,eAAe,gBAAgB,KAAK,eAAe,GAAG;EAC5D,MAAM,YAAY,KAAK,SAAS,IAAI,YAAY;EAChD,IAAI;EACJ,IAAI,WACF,aACE,KAAK,iBACL,OAAO,YACL,KAAK,cAAc,KAAI,QAAO,CAAC,KAAK,IAAI,IAAI,CAAU,CACxD;EAGJ,OAAO;GAAC;GAAW;GAAc;EAAU;CAW7C;CAEA,CAAC,KAAK,QAAiC;EACrC,IAAI,OAAO,OAAsB,GAAiB;GAChD,OAAO,KAAK,gBAAgB,MAAM;GAClC;EACF;EAEA,MAAM,EAAC,WAAW,cAAc,eAAc,KAAK,uBACjD,OAAO,EAAiB,CAAC,GAC3B;EACA,IAAI,CAAC,WACH;EAGF,MAAM,EAAC,gBAAe,KAAK,UAAU;EAErC,IAAI,OAAO,OAAsB,GAAgB;GAC/C,IAAI,UAAU,OAAO,KAAK,QAAQ;IAChC,IAAI,KAAK,iBAAiB,IAAI,YAAY,GAMtC;SAAA,UAAU,UAAU,KAAA,KACpB,YAAY,OAAO,EAAiB,CAAC,KAAK,UAAU,KAAK,KAAK,GAE9D;IAAA;IAGJ,MAAM,WAAW,UAAU,OAAO;IAClC,IAAI,aAAa,KAAK,QACpB,KAAK,iBAAiB,OAAO,YAAY;IAE3C,KAAK,cACH,cACA,UACA,UAAU,UAAU,KAAA,KAClB,YAAY,UAAU,OAAO,OAAO,EAAiB,CAAC,GAAG,IAAI,IAC3D,OAAO,EAAiB,CAAC,MACzB,UAAU,KAChB;IACA,OAAO,KAAK,QAAQ,KAAK,QAAQ,IAAI;IACrC;GACF;GAEA,IACE,UAAU,UAAU,KAAA,KACpB,YAAY,OAAO,EAAiB,CAAC,KAAK,UAAU,KAAK,KAAK,GAE9D;GAGF,IAAI;GACJ,IAAI;GACJ,IAAI,KAAK,WAAW,GAClB,KAAK,MAAM,QAAQ,KAAK,OAAO,MAAM;IACnC,OAAO;KACL,KAAK,UAAU;KACf,OAAO;IACT;IACA;GACF,CAAC,GAAG;IACF,IAAI,SAAS,SAAS;KACpB,MAAM;KACN;IACF;IACA,YAAY;IACZ;GACF;QAEA,KAAK,MAAM,QAAQ,KAAK,OAAO,MAAM;IACnC,OAAO;KACL,KAAK,UAAU;KACf,OAAO;IACT;IACA;IACA,SAAS;GACX,CAAC,GACC,IAAI,SAAS,SAAS;IACpB,MAAM;IACN;GACF,OAAO,IAAI,cAAc,KAAA,GACvB,YAAY;QACP;IACL,kBAAkB;IAClB;GACF;GAGJ,OACE,cAAc,KAAA,GACd,4CACF;GACA,MAAM,eAAe,iBAAiB,SAAS;GAG/C,KAAK,cACH,cACA,UAAU,MACV,oBAAoB,KAAA,KAClB,YAAY,OAAO,EAAiB,CAAC,KAAK,gBAAgB,GAAG,IAAI,IAC/D,OAAO,EAAiB,CAAC,MACzB,gBAAgB,GACtB;GACA,OAAO,KAAK,4BACV,OAAO,EAAiB,CAAC,KACzB,YACF;GACA,OAAO,KAAK,QAAQ,KAAK,QAAQ,IAAI;EACvC,OAAO,IAAI,OAAO,OAAsB,GAAmB;GACzD,IAAI,UAAU,UAAU,KAAA,GAEtB;GAEF,MAAM,cAAc,YAClB,OAAO,EAAiB,CAAC,KACzB,UAAU,KACZ;GACA,IAAI,cAAc,KAAM,cAAc,KAAK,KAAK,WAAW,GAEzD;GAEF,IAAI;GACJ,KAAK,MAAM,QAAQ,KAAK,OAAO,MAAM;IACnC,OAAO;KACL,KAAK,UAAU;KACf,OAAO;IACT;IACA;IACA,SAAS;GACX,CAAC,GAAG;IACF,IAAI,SAAS,SAAS;KACpB,MAAM;KACN;IACF;IACA,kBAAkB;IAClB;GACF;GAEA,MAAM,aACJ,UAAU,OAAO,MAAM,IACnB,KAAA,IACA,cAAc,IACZ,UAAU,QACV,iBAAiB;GACzB,IAAI,CAAC,KAAK,iBAAiB,IAAI,YAAY,GAIzC,KAAK,iBAAiB,IAAI,cAAc;IACtC;IACA,WAAW,UAAU;GACvB,CAAC;GAEH,KAAK,cAAc,cAAc,UAAU,OAAO,GAAG,UAAU;GAC/D,OAAO,KAAK,QAAQ,KAAK,QAAQ,IAAI;EACvC,OAAO,IAAI,OAAO,OAAsB,GAIpC;OAAA,UAAU,SACV,YAAY,OAAO,EAAiB,CAAC,KAAK,UAAU,KAAK,KAAK,GAE9D,OAAO,KAAK,QAAQ,KAAK,QAAQ,IAAI;EAAA;CAG3C;CAEA,CAAC,gBAAgB,QAAqC;EACpD,OACE,CAAC,KAAK,2BACJ,KAAK,wBACH,OAAO,EAAqB,CAAC,KAC7B,OAAO,EAAiB,CAAC,GAC3B,MAAM,GACR,oCACF;EAEA,MAAM,EAAC,WAAW,cAAc,eAAc,KAAK,uBACjD,OAAO,EAAqB,CAAC,GAC/B;EACA,IAAI,CAAC,WACH;EAGF,OAAO,UAAU,OAAO,qBAAqB;EAC7C,MAAM,EAAC,gBAAe,KAAK,UAAU;EACrC,MAAM,SAAS,YACb,OAAO,EAAqB,CAAC,KAC7B,UAAU,KACZ;EACA,MAAM,SAAS,YAAY,OAAO,EAAiB,CAAC,KAAK,UAAU,KAAK;EAExE,MAAM,qCAAqC;GACzC,KAAK,cACH,cACA,UAAU,MACV,OAAO,EAAiB,CAAC,GAC3B;GACA,OAAO,KAAK,QAAQ,KAAK,QAAQ,IAAI;EACvC;EAGA,IAAI,WAAW,GAAG;GAEhB,IAAI,WAAW,GAAG;IAEhB,OAAO,KAAK,QAAQ,KAAK,QAAQ,IAAI;IACrC;GACF;GAEA,IAAI,SAAS,GAAG;IACd,IAAI,KAAK,WAAW,GAAG;KACrB,OAAO,6BAA6B;KACpC;IACF;IAMA,IAAI;IACJ,KAAK,MAAM,QAAQ,KAAK,OAAO,MAAM;KACnC,OAAO;MACL,KAAK,UAAU;MACf,OAAO;KACT;KACA;KACA,SAAS;IACX,CAAC,GAAG;KACF,IAAI,SAAS,SAAS;MACpB,MAAM;MACN;KACF;KACA,kBAAkB;KAClB;IACF;IACA,OACE,oBAAoB,KAAA,GACpB,kDACF;IAEA,KAAK,cAAc,cAAc,UAAU,MAAM,gBAAgB,GAAG;IACpE,OAAO,KAAK,QAAQ,KAAK,QAAQ,IAAI;IACrC;GACF;GAEA,OAAO,SAAS,GAAG,uCAAuC;GAE1D,IAAI;GACJ,KAAK,WAAW,KAAK;GACrB,IAAI;IACF,KAAK,MAAM,QAAQ,KAAK,OAAO,MAAM;KACnC,OAAO;MACL,KAAK,UAAU;MACf,OAAO;KACT;KACA;IACF,CAAC,GAAG;KACF,IAAI,SAAS,SAAS;MACpB,MAAM;MACN;KACF;KACA,eAAe;KACf;IACF;GACF,UAAU;IACR,KAAK,WAAW,MAAM;GACxB;GACA,OACE,iBAAiB,KAAA,GACjB,+CACF;GAIA,IAAI,YAAY,aAAa,KAAK,OAAO,EAAiB,CAAC,GAAG,MAAM,GAAG;IACrE,OAAO,6BAA6B;IACpC;GACF;GAIA,KAAK,cAAc,cAAc,UAAU,MAAM,aAAa,GAAG;GACjE,OAAO,KAAK,4BACV,aAAa,KACb,iBAAiB,OAAO,EAAqB,CAC/C;GACA,OAAO,KAAK,QAAQ,KAAK,cAAc,YAAY,GAAG,IAAI;GAC1D;EACF;EAEA,IAAI,SAAS,GAAG;GACd,OAAO,WAAW,GAAG,8CAA8C;GAGnE,IAAI,SAAS,GACX;GAIF,OAAO,SAAS,GAAG,oCAAoC;GAEvD,IAAI;GACJ,IAAI;GACJ,KAAK,MAAM,QAAQ,KAAK,OAAO,MAAM;IACnC,OAAO;KACL,KAAK,UAAU;KACf,OAAO;IACT;IACA;IACA,SAAS;GACX,CAAC,GACC,IAAI,SAAS,SAAS;IACpB,MAAM;IACN;GACF,OAAO,IAAI,iBAAiB,KAAA,GAC1B,eAAe;QACV;IACL,eAAe;IACf;GACF;GAEF,OACE,iBAAiB,KAAA,GACjB,+CACF;GACA,OACE,iBAAiB,KAAA,GACjB,+CACF;GAIA,KAAK,cAAc,cAAc,UAAU,MAAM,aAAa,GAAG;GACjE,OAAO,KAAK,4BACV,OAAO,EAAiB,CAAC,KACzB,iBAAiB,YAAY,CAC/B;GACA,OAAO,KAAK,QAAQ,KAAK,cAAc,OAAO,EAAiB,GAAG,IAAI;GAEtE;EACF;EAEA,IAAI,SAAS,GAAG;GACd,OAAO,WAAW,GAAG,8CAA8C;GAGnE,IAAI,SAAS,GAAG;IACd,OAAO,KAAK,QAAQ,KAAK,QAAQ,IAAI;IACrC;GACF;GAIA,OAAO,SAAS,GAAG,uCAAuC;GAI1D,IAAI;GACJ,KAAK,WAAW,KAAK;GACrB,IAAI;IACF,KAAK,MAAM,QAAQ,KAAK,OAAO,MAAM;KACnC,OAAO;MACL,KAAK,UAAU;MACf,OAAO;KACT;KACA;IACF,CAAC,GAAG;KACF,IAAI,SAAS,SAAS;MACpB,MAAM;MACN;KACF;KACA,iBAAiB;KACjB;IACF;GACF,UAAU;IACR,KAAK,WAAW,MAAM;GACxB;GACA,OACE,mBAAmB,KAAA,GACnB,iDACF;GAGA,IAAI,YAAY,eAAe,KAAK,OAAO,EAAiB,CAAC,GAAG,MAAM,GAAG;IACvE,OAAO,6BAA6B;IACpC;GACF;GAEA,OAAO,KAAK,QAAQ,KAClB,iBAAiB,OAAO,EAAqB,GAC7C,IACF;GACA,KAAK,cAAc,cAAc,UAAU,MAAM,eAAe,GAAG;GACnE,OAAO,KAAK,QAAQ,KAAK,cAAc,cAAc,GAAG,IAAI;GAC5D;EACF;EAEA,YAAY;CACd;CAEA,CAAC,4BAA4B,KAAU,QAAgB;EACrD,KAAK,sBAAsB;EAC3B,IAAI;GACF,OAAO,KAAK,QAAQ,KAAK,QAAQ,IAAI;EACvC,UAAU;GACR,KAAK,sBAAsB,KAAA;EAC7B;CACF;CAEA,cAAc,cAAsB,MAAc,OAAwB;EACxE,KAAK,SAAS,IAAI,cAAc;GAC9B;GACA;EACF,CAAC;CACH;CAEA,UAAgB;EACd,KAAK,OAAO,QAAQ;EACpB,KAAK,SAAS,QAAQ;CACxB;CAEA,CAAC,UAAU,SAAsC;EAC/C,IAAI,KAAK,iBAAiB,OAAO,GAAG;GAClC,MAAM,QAAQ,CAAC,GAAG,KAAK,iBAAiB,QAAQ,CAAC;GACjD,KAAK,iBAAiB,MAAM;GAE5B,KAAK,MAAM,CAAC,cAAc,EAAC,iBAAgB,OAAO;IAChD,MAAM,YAAY,KAAK,SAAS,IAAI,YAAY;IAChD,OACE,cAAc,KAAA,GACd,6CACF;IACA,MAAM,UAAU,KAAK,SAAS,UAAU;IACxC,OAAO,UAAU,GAAG,2CAA2C;IAE/D,MAAM,SAAiB,CAAC;IACxB,KAAK,WAAW,KAAK;IACrB,IAAI;KACF,MAAM,SAAS,KAAK,OAAO,MAAM;MAC/B,OAAO,UAAU,QACb;OACE,KAAK,UAAU;OACf,OAAO;MACT,IACA,KAAA;MACJ;KACF,CAAC;KAED,KAAK,MAAM,QAAQ,QAAQ;MACzB,IAAI,SAAS,SAAS;OACpB,MAAM;OACN;MACF;MACA,OAAO,KAAK,IAAI;MAChB,IAAI,OAAO,WAAW,SACpB;KAEJ;IACF,UAAU;KACR,KAAK,WAAW,MAAM;IACxB;IAEA,IAAI,cAAc,UAAU;IAC5B,KAAK,MAAM,QAAQ,QAAQ;KACzB;KACA,KAAK,cAAc,cAAc,aAAa,KAAK,GAAG;KACtD,OAAO,KAAK,QAAQ,KAAK,cAAc,IAAI,GAAG,IAAI;IACpD;GACF;EACF;EAEA,IAAI,KAAK,QAAQ,WACf,OAAO,KAAK,QAAQ,UAAU,IAAI;CAEtC;AACF;AAEA,SAAS,gBACP,cACA,iBACQ;CAGR,MAAM,kBAA2B,CAAC;CAElC,IAAI,gBAAgB,iBAClB,KAAK,MAAM,OAAO,cAChB,gBAAgB,KAAK,gBAAgB,IAAI;CAI7C,OAAO,KAAK,UAAU,CAAC,QAAQ,GAAG,eAAe,CAAC;AACpD;AAEA,SAAgB,8BACd,YACA,cACS;CACT,IAAI,eAAe,KAAA,KAAa,iBAAiB,KAAA,GAC/C,OAAO,eAAe;CAExB,IAAI,aAAa,WAAW,OAAO,KAAK,UAAU,CAAC,CAAC,QAClD,OAAO;CAET,KAAK,MAAM,OAAO,cAChB,IAAI,CAAC,OAAO,YAAY,GAAG,GACzB,OAAO;CAGX,OAAO;AACT;AAEA,SAAgB,+BACd,YACA,cACS;CACT,IAAI,eAAe,KAAA,KAAa,iBAAiB,KAAA,GAC/C,OAAO;CAET,KAAK,MAAM,OAAO,cAChB,IAAI,CAAC,OAAO,YAAY,GAAG,GACzB,OAAO;CAGX,OAAO;AACT;AAEA,SAAgB,2BACd,cACY;CACZ,QAAQ,GAAG,MAAM;EACf,KAAK,MAAM,OAAO,cAAc;GAC9B,MAAM,MAAM,cAAc,EAAE,MAAM,EAAE,IAAI;GACxC,IAAI,QAAQ,GACV,OAAO;EAEX;EACA,OAAO;CACT;AACF"}
@@ -222,6 +222,6 @@ function* mergeFetches(fetches, comparator) {
222
222
  }
223
223
  }
224
224
  //#endregion
225
- export { UnionFanIn };
225
+ export { UnionFanIn, mergeFetches };
226
226
 
227
227
  //# sourceMappingURL=union-fan-in.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"union-fan-in.js","names":["#inputs","#schema","#branchRels","#fanOutPushStarted","#pushInternalChange","#accumulatedPushes","#output","#fanOutReconcileStarted"],"sources":["../../../../../zql/src/ivm/union-fan-in.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport {emptyArray} from '../../../shared/src/sentinels.ts';\nimport type {Writable} from '../../../shared/src/writable.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport {ChangeType} from './change-type.ts';\nimport type {Change} from './change.ts';\nimport type {Constraint} from './constraint.ts';\nimport type {Node} from './data.ts';\nimport {\n throwOutput,\n type FetchRequest,\n type Input,\n type InputBase,\n type Operator,\n type Output,\n} from './operator.ts';\nimport {\n makeAddEmptyRelationships,\n mergeEmpty,\n mergeRelationships,\n pushAccumulatedChanges,\n pushWitnessDeltas,\n} from './push-accumulated.ts';\nimport type {SourceSchema} from './schema.ts';\nimport type {Stream} from './stream.ts';\nimport type {UnionFanOut} from './union-fan-out.ts';\n\nexport class UnionFanIn implements Operator {\n readonly #inputs: readonly Input[];\n readonly #schema: SourceSchema;\n #fanOutPushStarted: boolean = false;\n #fanOutReconcileStarted: boolean = false;\n #output: Output = throwOutput;\n #accumulatedPushes: Change[] = [];\n readonly #branchRels: ReadonlySet<string>;\n\n constructor(fanOut: UnionFanOut, inputs: Input[]) {\n this.#inputs = inputs;\n const fanOutSchema = fanOut.getSchema();\n fanOut.setFanIn(this);\n assert(fanOutSchema.sort !== undefined, 'UnionFanIn requires sorted input');\n\n const schema: Writable<SourceSchema> = {\n tableName: fanOutSchema.tableName,\n columns: fanOutSchema.columns,\n primaryKey: fanOutSchema.primaryKey,\n relationships: {\n ...fanOutSchema.relationships,\n },\n isHidden: fanOutSchema.isHidden,\n system: fanOutSchema.system,\n compareRows: fanOutSchema.compareRows,\n sort: fanOutSchema.sort,\n };\n\n // now go through inputs and merge relationships\n const relationshipsFromBranches: Set<string> = new Set();\n for (const input of inputs) {\n const inputSchema = input.getSchema();\n assert(\n schema.tableName === inputSchema.tableName,\n `Table name mismatch in union fan-in: ${schema.tableName} !== ${inputSchema.tableName}`,\n );\n assert(\n schema.primaryKey === inputSchema.primaryKey,\n `Primary key mismatch in union fan-in`,\n );\n assert(\n schema.system === inputSchema.system,\n `System mismatch in union fan-in: ${schema.system} !== ${inputSchema.system}`,\n );\n assert(\n schema.compareRows === inputSchema.compareRows,\n `compareRows mismatch in union fan-in`,\n );\n assert(schema.sort === inputSchema.sort, `Sort mismatch in union fan-in`);\n\n for (const [relName, relSchema] of Object.entries(\n inputSchema.relationships,\n )) {\n if (relName in fanOutSchema.relationships) {\n continue;\n }\n\n // All branches will have unique relationship names except for relationships\n // that come in from `fanOut`.\n assert(\n !relationshipsFromBranches.has(relName),\n `Relationship ${relName} exists in multiple upstream inputs to union fan-in`,\n );\n schema.relationships[relName] = relSchema;\n relationshipsFromBranches.add(relName);\n }\n\n input.setOutput(this);\n }\n\n this.#schema = schema;\n this.#inputs = inputs;\n this.#branchRels = relationshipsFromBranches;\n }\n\n destroy(): void {\n for (const input of this.#inputs) {\n input.destroy();\n }\n }\n\n fetch(req: FetchRequest): Stream<Node | 'yield'> {\n const iterables = this.#inputs.map(input => input.fetch(req));\n const compareRows = this.#schema.compareRows;\n const compare = req.reverse\n ? (l: Node, r: Node) => compareRows(r.row, l.row)\n : (l: Node, r: Node) => compareRows(l.row, r.row);\n const relNames = Object.keys(this.#schema.relationships);\n return mapNodes(mergeFetches(iterables, compare), node => {\n const relationships = {...node.relationships};\n mergeEmpty(relationships, relNames);\n return {row: node.row, relationships};\n });\n }\n\n getSchema(): SourceSchema {\n return this.#schema;\n }\n\n push(change: Change, pusher: InputBase): Stream<'yield'> {\n if (!this.#fanOutPushStarted) {\n return this.#pushInternalChange(change, pusher);\n }\n this.#accumulatedPushes.push(change);\n return emptyArray;\n }\n\n /**\n * An internal change means that a change was received inside the fan-out/fan-in sub-graph.\n *\n * These changes always come from children of a flip-join as no other push generating operators\n * currently exist between union-fan-in and union-fan-out. All other pushes\n * enter into union-fan-out before reaching union-fan-in.\n *\n * - normal joins for `exists` come before `union-fan-out`\n * - joins for `related` come after `union-fan-out`\n * - take comes after `union-fan-out`\n *\n * The algorithm for deciding whether or not to forward a push that came from inside the ufo/ufi sub-graph:\n * 1. If the change is a `child` change we can forward it. This is because all child branches in the ufo/ufi sub-graph are unique.\n * 2. If the change is `add` we can forward it iff no `fetches` for the row return any results.\n * If another branch has it, the add was already emitted in the past.\n * 3. If the change is `remove` we can forward it iff no `fetches` for the row return any results.\n * If no other branches have the change, the remove can be sent as the value is no longer present.\n * If other branches have it, the last branch the processes the remove will send the remove.\n * 4. Edits will always come through as child changes as flip join will flip them into children.\n * An edit that would result in a remove or add will have been split into an add/remove pair rather than being an edit.\n */\n *#pushInternalChange(change: Change, pusher: InputBase): Stream<'yield'> {\n if (change[ChangeIndex.TYPE] === ChangeType.CHILD) {\n yield* this.#output.push(change, this);\n return;\n }\n\n assert(\n change[ChangeIndex.TYPE] === ChangeType.ADD ||\n change[ChangeIndex.TYPE] === ChangeType.REMOVE,\n () =>\n `UnionFanIn: expected add or remove change type, got ${change[ChangeIndex.TYPE]}`,\n );\n\n let hadMatch = false;\n for (const input of this.#inputs) {\n if (input === pusher) {\n hadMatch = true;\n continue;\n }\n\n const constraint: Writable<Constraint> = {};\n for (const key of this.#schema.primaryKey) {\n constraint[key] = change[ChangeIndex.NODE].row[key];\n }\n const fetchResult = input.fetch({\n constraint,\n });\n\n // `fetch` interleaves 'yield' sentinels for cooperative multitasking.\n // They must be forwarded, not just skipped: this probe runs inside a\n // push, and the sentinel is the source offering the scheduler a breath.\n // They must also not be mistaken for rows -- reading one as a row is\n // what broke this before, since an empty branch that happened to yield\n // looked like a branch holding the row, silently dropping the\n // add/remove and desyncing a downstream `Take`'s push and fetch paths.\n let otherBranchHasRow = false;\n for (const node of fetchResult) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n otherBranchHasRow = true;\n break;\n }\n\n if (otherBranchHasRow) {\n // Another branch has the row, so it stays. What changed is that it\n // gained or lost the pusher's witnesses: say so as child changes.\n yield* pushWitnessDeltas(\n change[ChangeIndex.NODE],\n change[ChangeIndex.TYPE],\n name => this.#branchRels.has(name),\n this.#output,\n this,\n );\n return;\n }\n }\n\n assert(hadMatch, 'Pusher was not one of the inputs to union-fan-in!');\n\n // No other branches have the row, so we can push the change.\n yield* this.#output.push(change, this);\n }\n\n fanOutStartedPushing() {\n assert(\n this.#fanOutPushStarted === false,\n 'UnionFanIn: fanOutStartedPushing called while already pushing',\n );\n this.#fanOutPushStarted = true;\n }\n\n fanOutDonePushing(fanOutChangeType: ChangeType): Stream<'yield'> {\n assert(\n this.#fanOutPushStarted,\n 'UnionFanIn: fanOutDonePushing called without fanOutStartedPushing',\n );\n this.#fanOutPushStarted = false;\n if (this.#inputs.length === 0) {\n return emptyArray;\n }\n\n if (this.#accumulatedPushes.length === 0) {\n // It is possible for no forks to pass along the push.\n // E.g., if no filters match in any fork.\n return emptyArray;\n }\n\n return pushAccumulatedChanges(\n this.#accumulatedPushes,\n this.#output,\n this,\n fanOutChangeType,\n mergeRelationships,\n makeAddEmptyRelationships(this.#schema),\n name => this.#branchRels.has(name),\n );\n }\n\n fanOutStartedReconciling() {\n assert(\n this.#fanOutReconcileStarted === false,\n 'UnionFanIn: fanOutStartedReconciling called while already reconciling',\n );\n this.#fanOutReconcileStarted = true;\n }\n\n *fanOutDoneReconciling(): Stream<'yield'> {\n assert(\n this.#fanOutReconcileStarted,\n 'UnionFanIn: fanOutDoneReconciling called without fanOutStartedReconciling',\n );\n this.#fanOutReconcileStarted = false;\n if (this.#inputs.length === 0) {\n return;\n }\n if (this.#output.reconcile) {\n yield* this.#output.reconcile(this);\n }\n }\n\n setOutput(output: Output): void {\n this.#output = output;\n }\n\n *reconcile(_pusher: InputBase): Stream<'yield'> {\n if (this.#fanOutReconcileStarted) {\n return;\n }\n if (this.#output.reconcile) {\n yield* this.#output.reconcile(this);\n }\n }\n}\n\nfunction* mapNodes(\n nodes: Iterable<Node | 'yield'>,\n f: (n: Node) => Node,\n): Stream<Node | 'yield'> {\n for (const n of nodes) {\n yield n === 'yield' ? n : f(n);\n }\n}\n\nexport function* mergeFetches(\n fetches: Iterable<Node | 'yield'>[],\n comparator: (l: Node, r: Node) => number,\n): IterableIterator<Node | 'yield'> {\n const iterators = fetches.map(i => i[Symbol.iterator]());\n let threw = false;\n try {\n const current: (Node | null)[] = [];\n let lastNodeYielded: Node | undefined;\n for (let i = 0; i < iterators.length; i++) {\n const iter = iterators[i];\n let result = iter.next();\n // yield yields when initializing\n while (!result.done && result.value === 'yield') {\n yield result.value;\n result = iter.next();\n }\n current[i] = result.done ? null : (result.value as Node);\n }\n while (current.some(c => c !== null)) {\n const min = current.reduce(\n (acc: [Node, number] | undefined, c, i): [Node, number] | undefined => {\n if (c === null) {\n return acc;\n }\n if (acc === undefined || comparator(c, acc[0]) < 0) {\n return [c, i];\n }\n return acc;\n },\n undefined,\n );\n\n assert(min !== undefined, 'min is undefined');\n const [minNode, minIndex] = min;\n const iter = iterators[minIndex];\n let result = iter.next();\n while (!result.done && result.value === 'yield') {\n yield result.value;\n result = iter.next();\n }\n current[minIndex] = result.done ? null : (result.value as Node);\n if (\n lastNodeYielded !== undefined &&\n comparator(lastNodeYielded, minNode) === 0\n ) {\n continue;\n }\n // The same row from another branch is at the head of that branch's\n // stream. Union its relationships in (first branch wins a conflict)\n // instead of dropping it, so a fetched row carries what a pushed one does.\n let merged = minNode;\n for (let j = 0; j < iterators.length; j++) {\n let head = current[j];\n while (head !== null && comparator(head, minNode) === 0) {\n merged = {\n row: merged.row,\n relationships: {...head.relationships, ...merged.relationships},\n };\n let r = iterators[j].next();\n while (!r.done && r.value === 'yield') {\n yield r.value;\n r = iterators[j].next();\n }\n head = current[j] = r.done ? null : (r.value as Node);\n }\n }\n lastNodeYielded = minNode;\n yield merged;\n }\n } catch (e) {\n threw = true;\n for (const iter of iterators) {\n try {\n iter.throw?.(e);\n } catch (_cleanupError) {\n // error in the iter.throw cleanup,\n // catch so other iterators are cleaned up\n }\n }\n throw e;\n } finally {\n if (!threw) {\n for (const iter of iterators) {\n try {\n iter.return?.();\n } catch (_cleanupError) {\n // error in the iter.return cleanup,\n // catch so other iterators are cleaned up\n }\n }\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"union-fan-in.js","names":[],"sources":["../../../../../zql/src/ivm/union-fan-in.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport {emptyArray} from '../../../shared/src/sentinels.ts';\nimport type {Writable} from '../../../shared/src/writable.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport {ChangeType} from './change-type.ts';\nimport type {Change} from './change.ts';\nimport type {Constraint} from './constraint.ts';\nimport type {Node} from './data.ts';\nimport {\n throwOutput,\n type FetchRequest,\n type Input,\n type InputBase,\n type Operator,\n type Output,\n} from './operator.ts';\nimport {\n makeAddEmptyRelationships,\n mergeEmpty,\n mergeRelationships,\n pushAccumulatedChanges,\n pushWitnessDeltas,\n} from './push-accumulated.ts';\nimport type {SourceSchema} from './schema.ts';\nimport type {Stream} from './stream.ts';\nimport type {UnionFanOut} from './union-fan-out.ts';\n\nexport class UnionFanIn implements Operator {\n readonly #inputs: readonly Input[];\n readonly #schema: SourceSchema;\n #fanOutPushStarted: boolean = false;\n #fanOutReconcileStarted: boolean = false;\n #output: Output = throwOutput;\n #accumulatedPushes: Change[] = [];\n readonly #branchRels: ReadonlySet<string>;\n\n constructor(fanOut: UnionFanOut, inputs: Input[]) {\n this.#inputs = inputs;\n const fanOutSchema = fanOut.getSchema();\n fanOut.setFanIn(this);\n assert(fanOutSchema.sort !== undefined, 'UnionFanIn requires sorted input');\n\n const schema: Writable<SourceSchema> = {\n tableName: fanOutSchema.tableName,\n columns: fanOutSchema.columns,\n primaryKey: fanOutSchema.primaryKey,\n relationships: {\n ...fanOutSchema.relationships,\n },\n isHidden: fanOutSchema.isHidden,\n system: fanOutSchema.system,\n compareRows: fanOutSchema.compareRows,\n sort: fanOutSchema.sort,\n };\n\n // now go through inputs and merge relationships\n const relationshipsFromBranches: Set<string> = new Set();\n for (const input of inputs) {\n const inputSchema = input.getSchema();\n assert(\n schema.tableName === inputSchema.tableName,\n `Table name mismatch in union fan-in: ${schema.tableName} !== ${inputSchema.tableName}`,\n );\n assert(\n schema.primaryKey === inputSchema.primaryKey,\n `Primary key mismatch in union fan-in`,\n );\n assert(\n schema.system === inputSchema.system,\n `System mismatch in union fan-in: ${schema.system} !== ${inputSchema.system}`,\n );\n assert(\n schema.compareRows === inputSchema.compareRows,\n `compareRows mismatch in union fan-in`,\n );\n assert(schema.sort === inputSchema.sort, `Sort mismatch in union fan-in`);\n\n for (const [relName, relSchema] of Object.entries(\n inputSchema.relationships,\n )) {\n if (relName in fanOutSchema.relationships) {\n continue;\n }\n\n // All branches will have unique relationship names except for relationships\n // that come in from `fanOut`.\n assert(\n !relationshipsFromBranches.has(relName),\n `Relationship ${relName} exists in multiple upstream inputs to union fan-in`,\n );\n schema.relationships[relName] = relSchema;\n relationshipsFromBranches.add(relName);\n }\n\n input.setOutput(this);\n }\n\n this.#schema = schema;\n this.#inputs = inputs;\n this.#branchRels = relationshipsFromBranches;\n }\n\n destroy(): void {\n for (const input of this.#inputs) {\n input.destroy();\n }\n }\n\n fetch(req: FetchRequest): Stream<Node | 'yield'> {\n const iterables = this.#inputs.map(input => input.fetch(req));\n const compareRows = this.#schema.compareRows;\n const compare = req.reverse\n ? (l: Node, r: Node) => compareRows(r.row, l.row)\n : (l: Node, r: Node) => compareRows(l.row, r.row);\n const relNames = Object.keys(this.#schema.relationships);\n return mapNodes(mergeFetches(iterables, compare), node => {\n const relationships = {...node.relationships};\n mergeEmpty(relationships, relNames);\n return {row: node.row, relationships};\n });\n }\n\n getSchema(): SourceSchema {\n return this.#schema;\n }\n\n push(change: Change, pusher: InputBase): Stream<'yield'> {\n if (!this.#fanOutPushStarted) {\n return this.#pushInternalChange(change, pusher);\n }\n this.#accumulatedPushes.push(change);\n return emptyArray;\n }\n\n /**\n * An internal change means that a change was received inside the fan-out/fan-in sub-graph.\n *\n * These changes always come from children of a flip-join as no other push generating operators\n * currently exist between union-fan-in and union-fan-out. All other pushes\n * enter into union-fan-out before reaching union-fan-in.\n *\n * - normal joins for `exists` come before `union-fan-out`\n * - joins for `related` come after `union-fan-out`\n * - take comes after `union-fan-out`\n *\n * The algorithm for deciding whether or not to forward a push that came from inside the ufo/ufi sub-graph:\n * 1. If the change is a `child` change we can forward it. This is because all child branches in the ufo/ufi sub-graph are unique.\n * 2. If the change is `add` we can forward it iff no `fetches` for the row return any results.\n * If another branch has it, the add was already emitted in the past.\n * 3. If the change is `remove` we can forward it iff no `fetches` for the row return any results.\n * If no other branches have the change, the remove can be sent as the value is no longer present.\n * If other branches have it, the last branch the processes the remove will send the remove.\n * 4. Edits will always come through as child changes as flip join will flip them into children.\n * An edit that would result in a remove or add will have been split into an add/remove pair rather than being an edit.\n */\n *#pushInternalChange(change: Change, pusher: InputBase): Stream<'yield'> {\n if (change[ChangeIndex.TYPE] === ChangeType.CHILD) {\n yield* this.#output.push(change, this);\n return;\n }\n\n assert(\n change[ChangeIndex.TYPE] === ChangeType.ADD ||\n change[ChangeIndex.TYPE] === ChangeType.REMOVE,\n () =>\n `UnionFanIn: expected add or remove change type, got ${change[ChangeIndex.TYPE]}`,\n );\n\n let hadMatch = false;\n for (const input of this.#inputs) {\n if (input === pusher) {\n hadMatch = true;\n continue;\n }\n\n const constraint: Writable<Constraint> = {};\n for (const key of this.#schema.primaryKey) {\n constraint[key] = change[ChangeIndex.NODE].row[key];\n }\n const fetchResult = input.fetch({\n constraint,\n });\n\n // `fetch` interleaves 'yield' sentinels for cooperative multitasking.\n // They must be forwarded, not just skipped: this probe runs inside a\n // push, and the sentinel is the source offering the scheduler a breath.\n // They must also not be mistaken for rows -- reading one as a row is\n // what broke this before, since an empty branch that happened to yield\n // looked like a branch holding the row, silently dropping the\n // add/remove and desyncing a downstream `Take`'s push and fetch paths.\n let otherBranchHasRow = false;\n for (const node of fetchResult) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n otherBranchHasRow = true;\n break;\n }\n\n if (otherBranchHasRow) {\n // Another branch has the row, so it stays. What changed is that it\n // gained or lost the pusher's witnesses: say so as child changes.\n yield* pushWitnessDeltas(\n change[ChangeIndex.NODE],\n change[ChangeIndex.TYPE],\n name => this.#branchRels.has(name),\n this.#output,\n this,\n );\n return;\n }\n }\n\n assert(hadMatch, 'Pusher was not one of the inputs to union-fan-in!');\n\n // No other branches have the row, so we can push the change.\n yield* this.#output.push(change, this);\n }\n\n fanOutStartedPushing() {\n assert(\n this.#fanOutPushStarted === false,\n 'UnionFanIn: fanOutStartedPushing called while already pushing',\n );\n this.#fanOutPushStarted = true;\n }\n\n fanOutDonePushing(fanOutChangeType: ChangeType): Stream<'yield'> {\n assert(\n this.#fanOutPushStarted,\n 'UnionFanIn: fanOutDonePushing called without fanOutStartedPushing',\n );\n this.#fanOutPushStarted = false;\n if (this.#inputs.length === 0) {\n return emptyArray;\n }\n\n if (this.#accumulatedPushes.length === 0) {\n // It is possible for no forks to pass along the push.\n // E.g., if no filters match in any fork.\n return emptyArray;\n }\n\n return pushAccumulatedChanges(\n this.#accumulatedPushes,\n this.#output,\n this,\n fanOutChangeType,\n mergeRelationships,\n makeAddEmptyRelationships(this.#schema),\n name => this.#branchRels.has(name),\n );\n }\n\n fanOutStartedReconciling() {\n assert(\n this.#fanOutReconcileStarted === false,\n 'UnionFanIn: fanOutStartedReconciling called while already reconciling',\n );\n this.#fanOutReconcileStarted = true;\n }\n\n *fanOutDoneReconciling(): Stream<'yield'> {\n assert(\n this.#fanOutReconcileStarted,\n 'UnionFanIn: fanOutDoneReconciling called without fanOutStartedReconciling',\n );\n this.#fanOutReconcileStarted = false;\n if (this.#inputs.length === 0) {\n return;\n }\n if (this.#output.reconcile) {\n yield* this.#output.reconcile(this);\n }\n }\n\n setOutput(output: Output): void {\n this.#output = output;\n }\n\n *reconcile(_pusher: InputBase): Stream<'yield'> {\n if (this.#fanOutReconcileStarted) {\n return;\n }\n if (this.#output.reconcile) {\n yield* this.#output.reconcile(this);\n }\n }\n}\n\nfunction* mapNodes(\n nodes: Iterable<Node | 'yield'>,\n f: (n: Node) => Node,\n): Stream<Node | 'yield'> {\n for (const n of nodes) {\n yield n === 'yield' ? n : f(n);\n }\n}\n\nexport function* mergeFetches(\n fetches: Iterable<Node | 'yield'>[],\n comparator: (l: Node, r: Node) => number,\n): IterableIterator<Node | 'yield'> {\n const iterators = fetches.map(i => i[Symbol.iterator]());\n let threw = false;\n try {\n const current: (Node | null)[] = [];\n let lastNodeYielded: Node | undefined;\n for (let i = 0; i < iterators.length; i++) {\n const iter = iterators[i];\n let result = iter.next();\n // yield yields when initializing\n while (!result.done && result.value === 'yield') {\n yield result.value;\n result = iter.next();\n }\n current[i] = result.done ? null : (result.value as Node);\n }\n while (current.some(c => c !== null)) {\n const min = current.reduce(\n (acc: [Node, number] | undefined, c, i): [Node, number] | undefined => {\n if (c === null) {\n return acc;\n }\n if (acc === undefined || comparator(c, acc[0]) < 0) {\n return [c, i];\n }\n return acc;\n },\n undefined,\n );\n\n assert(min !== undefined, 'min is undefined');\n const [minNode, minIndex] = min;\n const iter = iterators[minIndex];\n let result = iter.next();\n while (!result.done && result.value === 'yield') {\n yield result.value;\n result = iter.next();\n }\n current[minIndex] = result.done ? null : (result.value as Node);\n if (\n lastNodeYielded !== undefined &&\n comparator(lastNodeYielded, minNode) === 0\n ) {\n continue;\n }\n // The same row from another branch is at the head of that branch's\n // stream. Union its relationships in (first branch wins a conflict)\n // instead of dropping it, so a fetched row carries what a pushed one does.\n let merged = minNode;\n for (let j = 0; j < iterators.length; j++) {\n let head = current[j];\n while (head !== null && comparator(head, minNode) === 0) {\n merged = {\n row: merged.row,\n relationships: {...head.relationships, ...merged.relationships},\n };\n let r = iterators[j].next();\n while (!r.done && r.value === 'yield') {\n yield r.value;\n r = iterators[j].next();\n }\n head = current[j] = r.done ? null : (r.value as Node);\n }\n }\n lastNodeYielded = minNode;\n yield merged;\n }\n } catch (e) {\n threw = true;\n for (const iter of iterators) {\n try {\n iter.throw?.(e);\n } catch (_cleanupError) {\n // error in the iter.throw cleanup,\n // catch so other iterators are cleaned up\n }\n }\n throw e;\n } finally {\n if (!threw) {\n for (const iter of iterators) {\n try {\n iter.return?.();\n } catch (_cleanupError) {\n // error in the iter.return cleanup,\n // catch so other iterators are cleaned up\n }\n }\n }\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"union-fan-out.js","names":["#input","#outputs","#unionFanIn","#destroyCount"],"sources":["../../../../../zql/src/ivm/union-fan-out.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport type {Change} from './change.ts';\nimport type {Node} from './data.ts';\nimport type {\n FetchRequest,\n Input,\n InputBase,\n Operator,\n Output,\n} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport type {Stream} from './stream.ts';\nimport type {UnionFanIn} from './union-fan-in.ts';\n\nexport class UnionFanOut implements Operator {\n #destroyCount: number = 0;\n #unionFanIn?: UnionFanIn;\n readonly #input: Input;\n readonly #outputs: Output[] = [];\n\n constructor(input: Input) {\n this.#input = input;\n input.setOutput(this);\n }\n\n setFanIn(fanIn: UnionFanIn) {\n assert(!this.#unionFanIn, 'FanIn already set for this FanOut');\n this.#unionFanIn = fanIn;\n }\n\n *push(change: Change): Stream<'yield'> {\n must(this.#unionFanIn).fanOutStartedPushing();\n for (const output of this.#outputs) {\n yield* output.push(change, this);\n }\n yield* must(this.#unionFanIn).fanOutDonePushing(change[ChangeIndex.TYPE]);\n }\n\n *reconcile(_pusher: InputBase): Stream<'yield'> {\n must(this.#unionFanIn).fanOutStartedReconciling();\n for (const output of this.#outputs) {\n if (output.reconcile) {\n yield* output.reconcile(this);\n }\n }\n yield* must(this.#unionFanIn).fanOutDoneReconciling();\n }\n\n setOutput(output: Output): void {\n this.#outputs.push(output);\n }\n\n getSchema(): SourceSchema {\n return this.#input.getSchema();\n }\n\n fetch(req: FetchRequest): Stream<Node | 'yield'> {\n return this.#input.fetch(req);\n }\n\n destroy(): void {\n if (this.#destroyCount < this.#outputs.length) {\n ++this.#destroyCount;\n if (this.#destroyCount === this.#outputs.length) {\n this.#input.destroy();\n }\n } else {\n throw new Error('FanOut already destroyed once for each output');\n }\n }\n}\n"],"mappings":";;;AAgBA,IAAa,cAAb,MAA6C;CAC3C,gBAAwB;CACxB;CACA;CACA,WAA8B,CAAC;CAE/B,YAAY,OAAc;EACxB,KAAKA,SAAS;EACd,MAAM,UAAU,IAAI;CACtB;CAEA,SAAS,OAAmB;EAC1B,OAAO,CAAC,KAAKE,aAAa,mCAAmC;EAC7D,KAAKA,cAAc;CACrB;CAEA,CAAC,KAAK,QAAiC;EACrC,KAAK,KAAKA,WAAW,EAAE,qBAAqB;EAC5C,KAAK,MAAM,UAAU,KAAKD,UACxB,OAAO,OAAO,KAAK,QAAQ,IAAI;EAEjC,OAAO,KAAK,KAAKC,WAAW,EAAE,kBAAkB,OAAO,EAAiB;CAC1E;CAEA,CAAC,UAAU,SAAqC;EAC9C,KAAK,KAAKA,WAAW,EAAE,yBAAyB;EAChD,KAAK,MAAM,UAAU,KAAKD,UACxB,IAAI,OAAO,WACT,OAAO,OAAO,UAAU,IAAI;EAGhC,OAAO,KAAK,KAAKC,WAAW,EAAE,sBAAsB;CACtD;CAEA,UAAU,QAAsB;EAC9B,KAAKD,SAAS,KAAK,MAAM;CAC3B;CAEA,YAA0B;EACxB,OAAO,KAAKD,OAAO,UAAU;CAC/B;CAEA,MAAM,KAA2C;EAC/C,OAAO,KAAKA,OAAO,MAAM,GAAG;CAC9B;CAEA,UAAgB;EACd,IAAI,KAAKG,gBAAgB,KAAKF,SAAS,QAAQ;GAC7C,EAAE,KAAKE;GACP,IAAI,KAAKA,kBAAkB,KAAKF,SAAS,QACvC,KAAKD,OAAO,QAAQ;EAExB,OACE,MAAM,IAAI,MAAM,+CAA+C;CAEnE;AACF"}
1
+ {"version":3,"file":"union-fan-out.js","names":[],"sources":["../../../../../zql/src/ivm/union-fan-out.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport type {Change} from './change.ts';\nimport type {Node} from './data.ts';\nimport type {\n FetchRequest,\n Input,\n InputBase,\n Operator,\n Output,\n} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport type {Stream} from './stream.ts';\nimport type {UnionFanIn} from './union-fan-in.ts';\n\nexport class UnionFanOut implements Operator {\n #destroyCount: number = 0;\n #unionFanIn?: UnionFanIn;\n readonly #input: Input;\n readonly #outputs: Output[] = [];\n\n constructor(input: Input) {\n this.#input = input;\n input.setOutput(this);\n }\n\n setFanIn(fanIn: UnionFanIn) {\n assert(!this.#unionFanIn, 'FanIn already set for this FanOut');\n this.#unionFanIn = fanIn;\n }\n\n *push(change: Change): Stream<'yield'> {\n must(this.#unionFanIn).fanOutStartedPushing();\n for (const output of this.#outputs) {\n yield* output.push(change, this);\n }\n yield* must(this.#unionFanIn).fanOutDonePushing(change[ChangeIndex.TYPE]);\n }\n\n *reconcile(_pusher: InputBase): Stream<'yield'> {\n must(this.#unionFanIn).fanOutStartedReconciling();\n for (const output of this.#outputs) {\n if (output.reconcile) {\n yield* output.reconcile(this);\n }\n }\n yield* must(this.#unionFanIn).fanOutDoneReconciling();\n }\n\n setOutput(output: Output): void {\n this.#outputs.push(output);\n }\n\n getSchema(): SourceSchema {\n return this.#input.getSchema();\n }\n\n fetch(req: FetchRequest): Stream<Node | 'yield'> {\n return this.#input.fetch(req);\n }\n\n destroy(): void {\n if (this.#destroyCount < this.#outputs.length) {\n ++this.#destroyCount;\n if (this.#destroyCount === this.#outputs.length) {\n this.#input.destroy();\n }\n } else {\n throw new Error('FanOut already destroyed once for each output');\n }\n }\n}\n"],"mappings":";;;AAgBA,IAAa,cAAb,MAA6C;CAC3C,gBAAwB;CACxB;CACA;CACA,WAA8B,CAAC;CAE/B,YAAY,OAAc;EACxB,KAAK,SAAS;EACd,MAAM,UAAU,IAAI;CACtB;CAEA,SAAS,OAAmB;EAC1B,OAAO,CAAC,KAAK,aAAa,mCAAmC;EAC7D,KAAK,cAAc;CACrB;CAEA,CAAC,KAAK,QAAiC;EACrC,KAAK,KAAK,WAAW,CAAC,CAAC,qBAAqB;EAC5C,KAAK,MAAM,UAAU,KAAK,UACxB,OAAO,OAAO,KAAK,QAAQ,IAAI;EAEjC,OAAO,KAAK,KAAK,WAAW,CAAC,CAAC,kBAAkB,OAAO,EAAiB;CAC1E;CAEA,CAAC,UAAU,SAAqC;EAC9C,KAAK,KAAK,WAAW,CAAC,CAAC,yBAAyB;EAChD,KAAK,MAAM,UAAU,KAAK,UACxB,IAAI,OAAO,WACT,OAAO,OAAO,UAAU,IAAI;EAGhC,OAAO,KAAK,KAAK,WAAW,CAAC,CAAC,sBAAsB;CACtD;CAEA,UAAU,QAAsB;EAC9B,KAAK,SAAS,KAAK,MAAM;CAC3B;CAEA,YAA0B;EACxB,OAAO,KAAK,OAAO,UAAU;CAC/B;CAEA,MAAM,KAA2C;EAC/C,OAAO,KAAK,OAAO,MAAM,GAAG;CAC9B;CAEA,UAAgB;EACd,IAAI,KAAK,gBAAgB,KAAK,SAAS,QAAQ;GAC7C,EAAE,KAAK;GACP,IAAI,KAAK,kBAAkB,KAAK,SAAS,QACvC,KAAK,OAAO,QAAQ;EAExB,OACE,MAAM,IAAI,MAAM,+CAA+C;CAEnE;AACF"}
@@ -370,6 +370,6 @@ function setProperty(mutate, parentEntry, key, value) {
370
370
  }
371
371
  var setRelation = setProperty;
372
372
  //#endregion
373
- export { applyChange, idSymbol, refCountSymbol };
373
+ export { applyChange, applyChangeInternal, idSymbol, refCountSymbol };
374
374
 
375
375
  //# sourceMappingURL=view-apply-change.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"view-apply-change.js","names":[],"sources":["../../../../../zql/src/ivm/view-apply-change.ts"],"sourcesContent":["import {\n assert,\n assertNumber,\n unreachable,\n} from '../../../shared/src/asserts.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport {assignProperty} from '../../../shared/src/objects.ts';\nimport type {Writable} from '../../../shared/src/writable.ts';\nimport type {Row} from '../../../zero-protocol/src/data.ts';\nimport {type Comparator, type Node} from './data.ts';\nimport {skipYields} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport type {Entry, Format} from './view.ts';\n\nexport const refCountSymbol = Symbol('rc');\nexport const idSymbol = Symbol('id');\n\ntype ReadonlyMetaEntry = Entry & {\n readonly [refCountSymbol]: number;\n readonly [idSymbol]?: string | undefined;\n};\n\ntype MutableMetaEntry = Writable<ReadonlyMetaEntry>;\n\n/**\n * MetaEntry is an Entry with internal tracking fields for reference counting\n * and stable identity. All entries in the view tree are MetaEntry instances,\n * created through makeNewMetaEntry().\n */\ntype MetaEntry<M extends Mutate> = M extends true\n ? MutableMetaEntry\n : ReadonlyMetaEntry;\n\ntype MutableArray<M extends Mutate, T> = M extends true ? T[] : readonly T[];\n\ntype MetaEntryList<M extends Mutate> = MutableArray<M, MetaEntry<M>>;\n\ntype MutableMetaEntryList = MutableMetaEntry[];\n\n/**\n * Node with eagerly-expanded relationships (arrays instead of generators).\n * Used when batching changes to capture source state at push time.\n */\nexport type ExpandedNode = {\n row: Row;\n relationships: Record<string, ExpandedNode[]>;\n};\n\n/**\n * A node for view changes. Can be either:\n * - A lazy Node with generator relationships (original behavior)\n * - An ExpandedNode with pre-computed relationship arrays (for batching)\n */\nexport type ViewNode = Node | ExpandedNode;\n\n/**\n * `applyChange` does not consume the `relationships` of `ChildChange#node`,\n * `EditChange#node` and `EditChange#oldNode`. The `ViewChange` type\n * documents and enforces this via the type system.\n */\nexport type ViewChange =\n | AddViewChange\n | RemoveViewChange\n | ChildViewChange\n | EditViewChange;\n\nexport type RowOnlyNode = {row: Row};\n\nexport type AddViewChange = {\n type: 'add';\n node: ViewNode;\n};\n\nexport type RemoveViewChange = {\n type: 'remove';\n node: ViewNode;\n};\n\ntype ChildViewChange = {\n type: 'child';\n node: RowOnlyNode;\n child: {\n relationshipName: string;\n change: ViewChange;\n };\n};\n\ntype EditViewChange = {\n type: 'edit';\n node: RowOnlyNode;\n oldNode: RowOnlyNode;\n};\n\n/**\n * This is a subset of WeakMap but restricted to what we need.\n * @deprecated Not used anymore. This will be removed in the future.\n */\nexport interface RefCountMap {\n get(entry: Entry): number | undefined;\n set(entry: Entry, refCount: number): void;\n delete(entry: Entry): boolean;\n}\n\n/**\n * Get child nodes from a relationship, handling both lazy (Node) and expanded (ExpandedNode).\n */\nfunction getChildNodes(\n node: ViewNode,\n relationship: string,\n): Iterable<ViewNode> {\n const children = node.relationships[relationship];\n if (Array.isArray(children)) {\n // ExpandedNode: already an array\n return children;\n }\n // Node: lazy generator function\n return skipYields(children());\n}\n\ntype Mutate = boolean;\ntype WithIDs = boolean;\n\n/**\n * Transaction-scoped copy-on-write.\n *\n * `applyChange` is immutable: it path-copies a new spine on every call so the\n * previously-emitted snapshot is never mutated. But within a single transaction\n * (a run of `push()` calls before `flush()`) only the final state is ever\n * observed by a listener, so the intermediate spines are pure allocation/GC\n * churn — making a K-change transaction O(K * spineSize).\n *\n * When the caller passes a WeakSet as the `mutate` argument, objects and arrays\n * that were created or cloned during the current transaction are recorded in it.\n * A helper that needs to modify such an object mutates it in place (it is not\n * yet observed) instead of cloning again. The first touch of a node still\n * copies-on-write (preserving the committed snapshot's references); subsequent\n * touches in the same transaction are in-place. This brings a K-change\n * transaction to O(spineSize + K) while keeping cross-transaction reference\n * stability.\n *\n * The set is module-scoped for the duration of a single (synchronous,\n * non-re-entrant) `applyChange` call and saved/restored around it.\n *\n * It is a WeakSet on purpose: it must not keep transiently-cloned arrays/entries\n * alive. A strong Set would extend their lifetime to the end of the transaction,\n * pushing large clones out of the young generation and adding GC pressure that\n * shows up as a regression on single-change transactions over wide lists. A\n * WeakSet gives the same identity test (\"created this transaction?\") while\n * letting dead clones be collected as cheaply as in the plain immutable path.\n */\nlet activeDirty: WeakSet<object> | undefined;\n\n/** True if `o` was created/cloned during the current transaction. */\nfunction owns(o: object): boolean {\n return activeDirty !== undefined && activeDirty.has(o);\n}\n\n/** Record `o` as owned by the current transaction (no-op outside a txn). */\nfunction track<T extends object>(o: T): T {\n activeDirty?.add(o);\n return o;\n}\n\n/**\n * Immutable view update. Returns new Entry on change, same Entry if unchanged.\n * Unchanged entries keep identity, enabling shallow comparison optimizations\n * in UI frameworks (React.memo, Solid's fine-grained reactivity, etc).\n *\n * Propagation: recurse DOWN to find target, copy objects on the way UP.\n * Siblings keep original refs. Only the ancestor path is copied.\n *\n * root {users:[A,B], items:[C,D,E]} --edit C--> root' {users:[A,B], items':[C',D,E]}\n * │ same ref │ │ │\n * └─────────────────┴── unchanged ──────────────┘ new array C' new, D/E same\n *\n * `mutate` selects the update strategy:\n * - `false` (default): fully immutable; every changed node is path-copied.\n * - `true`: mutate the tree in place (only safe when it is not yet observed,\n * e.g. during initial hydration).\n * - a `WeakSet`: transaction-scoped copy-on-write — copy on first touch, then\n * mutate in place for the rest of the transaction. See `activeDirty` above.\n */\nexport function applyChange(\n parentEntry: Entry,\n change: ViewChange,\n schema: SourceSchema,\n relationship: string,\n format: Format,\n withIDs = false,\n mutate: boolean | WeakSet<object> = false,\n): Entry {\n // A WeakSet selects copy-on-write; a boolean selects full mutate / immutable.\n const inPlace = mutate === true;\n const prevDirty = activeDirty;\n activeDirty = typeof mutate === 'object' ? mutate : undefined;\n try {\n return applyChangeInternal(\n parentEntry as MetaEntry<typeof inPlace>,\n change,\n schema,\n relationship,\n format,\n withIDs,\n inPlace,\n );\n } finally {\n activeDirty = prevDirty;\n }\n}\n\nexport function applyChangeInternal<M extends Mutate>(\n parentEntry: MetaEntry<M>,\n change: ViewChange,\n schema: SourceSchema,\n relationship: string,\n format: Format,\n withIDs: WithIDs,\n mutate: M,\n): MetaEntry<M> {\n if (schema.isHidden) {\n switch (change.type) {\n case 'add':\n case 'remove': {\n let currentParent = parentEntry;\n for (const relationship of Object.keys(change.node.relationships)) {\n const childSchema = must(schema.relationships[relationship]);\n for (const node of getChildNodes(change.node, relationship)) {\n currentParent = applyChangeInternal(\n currentParent,\n {type: change.type, node},\n childSchema,\n relationship,\n format,\n withIDs,\n mutate,\n );\n }\n }\n return currentParent;\n }\n case 'edit':\n // If hidden at this level it means that the hidden row was changed. If\n // the row was changed in such a way that it would change the\n // relationships then the edit would have been split into remove and\n // add.\n return parentEntry;\n case 'child': {\n const childSchema = must(\n schema.relationships[change.child.relationshipName],\n );\n return applyChangeInternal(\n parentEntry,\n change.child.change,\n childSchema,\n relationship,\n format,\n withIDs,\n mutate,\n );\n }\n default:\n unreachable(change);\n }\n }\n\n const {singular, relationships: childFormats} = format;\n switch (change.type) {\n // ADD: Insert row (rc=1) or increment refCount if duplicate.\n // RefCount tracks when the same row is reachable via multiple edges\n // within a relationship:\n //\n // issue.labels: [L1, L2] both point to same creator\n // │ │ │\n // ▼ ▼ ▼\n // label.creator ──────────► [User:rc=2] (one row, two refs)\n //\n // add(A) add(A) remove(A) remove(A)\n // ↓ ↓ ↓ ↓\n // [A:rc=1] → [A:rc=2] → [A:rc=1] → (deleted)\n case 'add': {\n if (singular) {\n const oldEntry = getOptionalSingularEntry(parentEntry, relationship);\n if (oldEntry !== undefined) {\n // Duplicate add: increment refCount\n assert(\n schema.compareRows(oldEntry, change.node.row) === 0,\n `Singular relationship '${relationship}' should not have multiple rows. You may need to declare this relationship with the \\`many\\` helper instead of the \\`one\\` helper in your schema.`,\n );\n\n const newEntry = incRefCount(mutate, oldEntry);\n return setRelation(mutate, parentEntry, relationship, newEntry);\n } else {\n // New row: create with rc=1, initialize nested relationships\n const newEntry = makeNewMetaEntry(\n change.node.row,\n schema,\n withIDs,\n 1,\n );\n initializeRelationshipsForNewEntryIfAny(\n newEntry,\n change.node,\n schema,\n childFormats,\n withIDs,\n );\n return setRelation(mutate, parentEntry, relationship, newEntry);\n }\n } else {\n // Plural: binary search for position, insert or increment refCount\n const view = getChildEntryList(parentEntry, relationship);\n const {newEntry, newView} = add(\n change.node.row,\n view,\n schema,\n withIDs,\n mutate,\n );\n\n if (newEntry) {\n initializeRelationshipsForNewEntryIfAny(\n newEntry,\n change.node,\n schema,\n childFormats,\n withIDs,\n );\n }\n return setRelation(mutate, parentEntry, relationship, newView);\n }\n }\n // REMOVE: Decrement refCount, physically remove when rc=0.\n case 'remove': {\n if (singular) {\n const oldEntry = getSingularEntry(parentEntry, relationship);\n const rc = oldEntry[refCountSymbol];\n if (rc === 1) {\n return setRelation(mutate, parentEntry, relationship, undefined);\n }\n const newEntry = decRefCount(mutate, oldEntry);\n return setRelation(mutate, parentEntry, relationship, newEntry);\n } else {\n const view = getChildEntryList(parentEntry, relationship);\n const newView = removeAndUpdateRefCount(\n view,\n change.node.row,\n schema.compareRows,\n mutate,\n );\n return setRelation(mutate, parentEntry, relationship, newView);\n }\n }\n // CHILD: Propagate nested change up to this level (leaf-to-root pattern).\n case 'child': {\n const childSchema = must(\n schema.relationships[change.child.relationshipName],\n );\n const childFormat = format.relationships[change.child.relationshipName];\n if (childFormat === undefined) {\n return parentEntry; // Relationship not in view format\n }\n\n if (singular) {\n const existing = getSingularEntry(parentEntry, relationship);\n const newExisting = applyChangeInternal(\n existing,\n change.child.change,\n childSchema,\n change.child.relationshipName,\n childFormat,\n withIDs,\n mutate,\n );\n // Preserve identity if child didn't change (enables shallow-compare optimizations).\n if (newExisting === existing) {\n return parentEntry;\n }\n return setRelation(mutate, parentEntry, relationship, newExisting);\n } else {\n // Find the target row in the sorted array via binary search.\n const view = getChildEntryList(parentEntry, relationship);\n const pos = binarySearch(view, change.node.row, schema.compareRows);\n assert(pos >= 0, 'node does not exist');\n const existing = view[pos];\n const newExisting = applyChangeInternal(\n existing,\n change.child.change,\n childSchema,\n change.child.relationshipName,\n childFormat,\n withIDs,\n mutate,\n );\n // Preserve identity if descendant didn't change.\n if (newExisting === existing) {\n return parentEntry;\n }\n // applyChangeInternal preserves MetaEntry structure when input is MetaEntry\n assertMetaEntry(newExisting);\n return setRelation(\n mutate,\n parentEntry,\n relationship,\n arrayWith(mutate, view, pos, newExisting),\n );\n }\n }\n // EDIT: Update row fields. If sort key changes, row may move position.\n //\n // Position change with rc>1 (two query paths reach same row):\n //\n // Before: [A, B(rc=2), C, D] After: [A, B(rc=1), C, B'(rc=1), D]\n // │ │ │\n // path1 + path2 path1 ghost path2 moved\n //\n // Why ghost stays: path1 still expects B at old position.\n // B' appears where path2's sort order places it.\n case 'edit': {\n if (singular) {\n const existing = getSingularEntry(parentEntry, relationship);\n const newEntry = applyEdit(existing, change, schema, withIDs, mutate);\n return setRelation(mutate, parentEntry, relationship, newEntry);\n } else {\n const view = getChildEntryList(parentEntry, relationship);\n // Sort key changed: row may need to move\n if (schema.compareRows(change.oldNode.row, change.node.row) !== 0) {\n const oldPos = binarySearch(\n view,\n change.oldNode.row,\n schema.compareRows,\n );\n assert(oldPos >= 0, 'old node does not exist');\n const oldEntry = view[oldPos];\n const rawPos = binarySearch(\n view,\n change.node.row,\n schema.compareRows,\n );\n const found = rawPos >= 0;\n const pos = found ? rawPos : ~rawPos;\n // A special case:\n // when refCount is 1 (so the row is being moved without leaving a\n // placeholder behind), and the new pos is the same as the old, or\n // directly after the old (so after the remove of the old it would be\n // in the same pos): the row does not need to be moved, just edited.\n if (\n oldEntry[refCountSymbol] === 1 &&\n (pos === oldPos || pos - 1 === oldPos)\n ) {\n const newEntry = applyEdit(\n oldEntry,\n change,\n schema,\n withIDs,\n mutate,\n );\n return setRelation(\n mutate,\n parentEntry,\n relationship,\n arrayWith(mutate, view, oldPos, newEntry),\n );\n } else {\n // Move the row. If rc > 1, an edit will be received for each ref.\n // On first edit: move row, apply edit, set rc=1. Leave a copy at\n // old pos with decremented rc. As each edit arrives, old copy's rc\n // decrements and new pos rc increments. When copy rc hits 0, remove.\n const newRefCount = oldEntry[refCountSymbol] - 1;\n let newView: MutableMetaEntryList;\n let adjustedPos = pos;\n\n if (newRefCount === 0) {\n newView = removeAt(mutate, view, oldPos);\n adjustedPos = oldPos < pos ? pos - 1 : pos;\n } else {\n const oldEntryCopy = setRefCount(mutate, oldEntry, newRefCount);\n newView = arrayWith(mutate, view, oldPos, oldEntryCopy);\n }\n\n if (found) {\n // Merge with existing at new pos\n const existingEntry = newView[adjustedPos];\n const editedEntry = applyEdit(\n existingEntry,\n change,\n schema,\n withIDs,\n mutate,\n );\n const updatedEntry = setRefCount(\n mutate,\n editedEntry,\n existingEntry[refCountSymbol] + 1,\n );\n return setRelation(\n mutate,\n parentEntry,\n relationship,\n arrayWith(mutate, newView, adjustedPos, updatedEntry),\n );\n } else {\n // Insert at new pos with rc=1\n const editedEntry = applyEdit(\n oldEntry,\n change,\n schema,\n withIDs,\n mutate,\n );\n const movedEntry = setRefCount(mutate, editedEntry, 1);\n return setRelation(\n mutate,\n parentEntry,\n relationship,\n insertAt(mutate, newView, adjustedPos, movedEntry),\n );\n }\n }\n } else {\n // Sort key unchanged: edit in place\n const pos = binarySearch(\n view,\n change.oldNode.row,\n schema.compareRows,\n );\n assert(pos >= 0, 'node does not exist');\n const newEntry = applyEdit(\n view[pos],\n change,\n schema,\n withIDs,\n mutate,\n );\n return setRelation(\n mutate,\n parentEntry,\n relationship,\n arrayWith(mutate, view, pos, newEntry),\n );\n }\n }\n }\n default:\n unreachable(change);\n }\n}\n\n/**\n * Batch apply multiple changes to an Entry tree.\n * For small batches or complex cases, falls back to sequential applyChange.\n * Future optimization: O(N + K) batch processing for large K.\n */\nexport function applyChanges(\n parentEntry: Entry,\n changes: ViewChange[],\n schema: SourceSchema,\n relationship: string,\n format: Format,\n withIDs: WithIDs = false,\n mutate: Mutate = false,\n): Entry {\n let result = parentEntry;\n for (const change of changes) {\n result = applyChange(\n result,\n change,\n schema,\n relationship,\n format,\n withIDs,\n mutate,\n );\n }\n return result;\n}\n\nfunction applyEdit<M extends Mutate>(\n existing: MetaEntry<M>,\n change: EditViewChange,\n schema: SourceSchema,\n withIDs: WithIDs,\n mutate: Mutate,\n): MetaEntry<M> {\n // In-place edit is safe when fully mutating or when `existing` was already\n // created/cloned in this transaction (copy-on-write). A primary-key change\n // always needs a fresh entry so identity tracks the new key.\n const canMutate = mutate || owns(existing);\n const newEntry: MutableMetaEntry =\n canMutate && schema.compareRows(change.oldNode.row, change.node.row) === 0\n ? Object.assign(existing, change.node.row)\n : track({...existing, ...change.node.row});\n\n if (withIDs) {\n return setProperty(\n mutate,\n newEntry,\n idSymbol,\n makeID(change.node.row, schema),\n );\n }\n return newEntry;\n}\n\n/**\n * Initialize child relationships on a newly-added entry by mutating it in place.\n *\n * New nodes don't exist in the view yet, so we can build in-place (no refs to preserve):\n *\n * New nodes don't exist in the view yet, so we can build in-place (no refs to preserve):\n *\n * Existing node edit (must path-copy): New node (can build in-place):\n *\n * view: [A, B, C] view: [A, B, _]\n * │ │\n * edit C.child add C with children\n * │ │\n * ▼ ▼\n * view': [A, B, C'] ◄─ path-copy view': [A, B, C{children:[...]}]\n * up the tree └─ built in-place\n *\n * Hidden schemas still use applyChange to collapse junction levels.\n */\nfunction initializeRelationshipsForNewEntryIfAny(\n entry: MutableMetaEntry,\n node: ViewNode,\n schema: SourceSchema,\n childFormats: Record<string, Format>,\n withIDs: WithIDs,\n): void {\n let result = entry;\n for (const relationship of Object.keys(node.relationships)) {\n const childSchema = must(schema.relationships[relationship]);\n const childFormat = childFormats[relationship];\n if (childFormat === undefined) {\n continue;\n }\n\n // Hidden/singular: use applyChange to handle properly\n if (childSchema.isHidden || childFormat.singular) {\n const newView = childFormat.singular\n ? undefined\n : track([] as MutableMetaEntryList);\n result[relationship] = newView;\n\n for (const childNode of getChildNodes(node, relationship)) {\n applyChangeInternal(\n result,\n {type: 'add', node: childNode},\n childSchema,\n relationship,\n childFormat,\n withIDs,\n true, // this is a new entry, so we can mutate\n );\n }\n } else {\n // Plural non-hidden: build array in-place for efficiency\n const childArray: MutableMetaEntryList = track([]);\n\n for (const childNode of getChildNodes(node, relationship)) {\n const newEntry = makeNewMetaEntry(\n childNode.row,\n childSchema,\n withIDs,\n 1,\n );\n const rawPos = binarySearch(\n childArray,\n childNode.row,\n childSchema.compareRows,\n );\n\n if (rawPos >= 0) {\n childArray[rawPos][refCountSymbol]++;\n } else {\n childArray.splice(~rawPos, 0, newEntry);\n initializeRelationshipsForNewEntryIfAny(\n newEntry,\n childNode,\n childSchema,\n childFormat.relationships,\n withIDs,\n );\n }\n }\n\n result[relationship] = childArray;\n }\n }\n}\n\nfunction add<M extends Mutate>(\n row: Row,\n view: MetaEntryList<M>,\n schema: SourceSchema,\n withIDs: WithIDs,\n mutate: M,\n): {\n newEntry: MutableMetaEntry | undefined;\n newView: MutableMetaEntryList;\n} {\n const rawPos = binarySearch(view, row, schema.compareRows);\n\n if (rawPos >= 0) {\n const existing = view[rawPos];\n\n const updated = incRefCount(mutate, existing);\n return {\n newEntry: undefined,\n newView: arrayWith(mutate, view, rawPos, updated),\n };\n }\n const newEntry = makeNewMetaEntry(row, schema, withIDs, 1);\n return {newEntry, newView: insertAt(mutate, view, ~rawPos, newEntry)};\n}\n\nfunction insertAt<M extends Mutate, T>(\n mutate: M,\n array: MutableArray<M, T>,\n index: number,\n item: T,\n): T[] {\n if (mutate || owns(array)) {\n (array as T[]).splice(index, 0, item);\n return array as T[];\n }\n return track(array.toSpliced(index, 0, item));\n}\n\nfunction removeAt<M extends Mutate, T>(\n mutate: M,\n array: MutableArray<M, T>,\n index: number,\n): T[] {\n if (mutate || owns(array)) {\n (array as T[]).splice(index, 1);\n return array as T[];\n }\n return track(array.toSpliced(index, 1));\n}\n\nfunction removeAndUpdateRefCount<M extends Mutate>(\n view: MetaEntryList<M>,\n row: Row,\n compareRows: Comparator,\n mutate: M,\n): MutableMetaEntryList {\n const pos = binarySearch(view, row, compareRows);\n assert(pos >= 0, 'node does not exist');\n const oldEntry = view[pos];\n const rc = oldEntry[refCountSymbol];\n if (rc === 1) {\n return removeAt(mutate, view, pos);\n }\n const newEntry = decRefCount(mutate, oldEntry);\n return arrayWith(mutate, view, pos, newEntry);\n}\n\n/**\n * Binary search returning a number.\n * - If found at index `i`: returns `i` (>= 0).\n * - If not found, insertion point is `low`: returns `~low` (< 0).\n * Recover insertion point with `~result`.\n */\nfunction binarySearch(\n view: MetaEntryList<Mutate>,\n target: Row,\n comparator: Comparator,\n): number {\n let high = view.length - 1;\n if (high < 0) {\n return ~0;\n }\n\n // Probe the last entry before searching. Hydration feeds the view rows in\n // the query's sort order, so every insert belongs at the end and the plain\n // search spends log2(n) comparisons to rediscover that -- about eleven per\n // row for a view of a couple of thousand. Row comparison is the single\n // largest cost in hydration on Hermes, so collapsing those eleven to one is\n // worth the one extra comparison this costs when the row does land inside\n // the view, which is a single push rather than a bulk load.\n // MetaEntry has all Row props; comparator only reads string keys\n const last = comparator(view[high] as Row, target);\n if (last < 0) {\n return ~(high + 1);\n }\n if (last === 0) {\n return high;\n }\n\n let low = 0;\n high -= 1;\n while (low <= high) {\n const mid = (low + high) >>> 1;\n const comparison = comparator(view[mid] as Row, target);\n if (comparison < 0) {\n low = mid + 1;\n } else if (comparison > 0) {\n high = mid - 1;\n } else {\n return mid;\n }\n }\n return ~low;\n}\n\n/** Assert value is MetaEntry (has refCountSymbol). */\nfunction assertMetaEntry<M extends Mutate>(\n v: unknown,\n): asserts v is MetaEntry<M> {\n assertNumber((v as Partial<MetaEntry<M>>)[refCountSymbol]);\n}\n\n/** Get singular MetaEntry, throws if missing. */\nfunction getSingularEntry<M extends Mutate>(\n parentEntry: MetaEntry<M>,\n relationship: string,\n): MetaEntry<M> {\n const entry = parentEntry[relationship];\n assert(entry !== undefined, 'node does not exist');\n assertMetaEntry(entry);\n return entry;\n}\n\n/** Get singular MetaEntry or undefined if not set. */\nfunction getOptionalSingularEntry<M extends Mutate>(\n parentEntry: MetaEntry<M>,\n relationship: string,\n): MetaEntry<M> | undefined {\n const entry = parentEntry[relationship];\n if (entry === undefined) {\n return undefined;\n }\n assertMetaEntry(entry);\n return entry;\n}\n\n/** Get child array as MetaEntryList. */\nfunction getChildEntryList<M extends Mutate>(\n parentEntry: MetaEntry<M>,\n relationship: string,\n): MetaEntryList<M> {\n const view = parentEntry[relationship];\n // `relationship` is a string key, so a name colliding with a column would\n // put a row value here; the message must not repeat it back.\n assert(Array.isArray(view), 'expected relationship array');\n return view as MetaEntryList<M>;\n}\n\n/** Create MetaEntry from row with given refCount. */\nfunction makeNewMetaEntry(\n row: Row,\n schema: SourceSchema,\n withIDs: WithIDs,\n rc: number,\n): MutableMetaEntry {\n // This creates a new MetaEntry from a Row. We never mutate Rows.\n if (withIDs) {\n return track({\n ...row,\n [refCountSymbol]: rc,\n [idSymbol]: makeID(row, schema),\n });\n }\n return track({\n ...row,\n [refCountSymbol]: rc,\n });\n}\n\nfunction makeID(row: Row, schema: SourceSchema) {\n // optimization for case of non-compound primary key\n if (schema.primaryKey.length === 1) {\n return JSON.stringify(row[schema.primaryKey[0]]);\n }\n return JSON.stringify(schema.primaryKey.map(k => row[k]));\n}\n\nfunction incRefCount<M extends Mutate>(\n mutate: M,\n entry: MetaEntry<M>,\n): MutableMetaEntry {\n return setRefCount(mutate, entry, entry[refCountSymbol] + 1);\n}\n\nfunction decRefCount<M extends Mutate>(\n mutate: M,\n entry: MetaEntry<M>,\n): MutableMetaEntry {\n return setRefCount(mutate, entry, entry[refCountSymbol] - 1);\n}\n\nfunction setRefCount<M extends Mutate>(\n mutate: M,\n entry: MetaEntry<M>,\n count: number,\n): MutableMetaEntry {\n if (mutate || owns(entry)) {\n (entry as MutableMetaEntry)[refCountSymbol] = count;\n return entry;\n }\n return track({\n ...entry,\n [refCountSymbol]: count,\n });\n}\n\nfunction arrayWith<M extends Mutate, T>(\n mutate: M,\n array: MutableArray<M, T>,\n index: number,\n value: T,\n): T[] {\n if (mutate || owns(array)) {\n (array as T[])[index] = value;\n return array as T[];\n }\n return track(array.with(index, value));\n}\n\nfunction setProperty<\n M extends Mutate,\n K extends string | keyof MetaEntry<M>,\n V,\n>(\n mutate: M,\n parentEntry: MetaEntry<M>,\n key: K,\n value: V,\n): MutableMetaEntry & {[P in K]: V} {\n if (mutate || owns(parentEntry)) {\n assignProperty(parentEntry as {[P in K]: V}, key, value);\n return parentEntry as MutableMetaEntry & {[P in K]: V};\n }\n return track({\n ...parentEntry,\n [key]: value,\n }) as MutableMetaEntry & {[P in K]: V};\n}\n\nconst setRelation: <M extends Mutate>(\n mutate: M,\n parentEntry: MetaEntry<M>,\n relationship: string,\n value: Entry | Entry[] | undefined,\n) => MutableMetaEntry = setProperty;\n"],"mappings":"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"}
1
+ {"version":3,"file":"view-apply-change.js","names":[],"sources":["../../../../../zql/src/ivm/view-apply-change.ts"],"sourcesContent":["import {\n assert,\n assertNumber,\n unreachable,\n} from '../../../shared/src/asserts.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport {assignProperty} from '../../../shared/src/objects.ts';\nimport type {Writable} from '../../../shared/src/writable.ts';\nimport type {Row} from '../../../zero-protocol/src/data.ts';\nimport {type Comparator, type Node} from './data.ts';\nimport {skipYields} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport type {Entry, Format} from './view.ts';\n\nexport const refCountSymbol = Symbol('rc');\nexport const idSymbol = Symbol('id');\n\ntype ReadonlyMetaEntry = Entry & {\n readonly [refCountSymbol]: number;\n readonly [idSymbol]?: string | undefined;\n};\n\ntype MutableMetaEntry = Writable<ReadonlyMetaEntry>;\n\n/**\n * MetaEntry is an Entry with internal tracking fields for reference counting\n * and stable identity. All entries in the view tree are MetaEntry instances,\n * created through makeNewMetaEntry().\n */\ntype MetaEntry<M extends Mutate> = M extends true\n ? MutableMetaEntry\n : ReadonlyMetaEntry;\n\ntype MutableArray<M extends Mutate, T> = M extends true ? T[] : readonly T[];\n\ntype MetaEntryList<M extends Mutate> = MutableArray<M, MetaEntry<M>>;\n\ntype MutableMetaEntryList = MutableMetaEntry[];\n\n/**\n * Node with eagerly-expanded relationships (arrays instead of generators).\n * Used when batching changes to capture source state at push time.\n */\nexport type ExpandedNode = {\n row: Row;\n relationships: Record<string, ExpandedNode[]>;\n};\n\n/**\n * A node for view changes. Can be either:\n * - A lazy Node with generator relationships (original behavior)\n * - An ExpandedNode with pre-computed relationship arrays (for batching)\n */\nexport type ViewNode = Node | ExpandedNode;\n\n/**\n * `applyChange` does not consume the `relationships` of `ChildChange#node`,\n * `EditChange#node` and `EditChange#oldNode`. The `ViewChange` type\n * documents and enforces this via the type system.\n */\nexport type ViewChange =\n | AddViewChange\n | RemoveViewChange\n | ChildViewChange\n | EditViewChange;\n\nexport type RowOnlyNode = {row: Row};\n\nexport type AddViewChange = {\n type: 'add';\n node: ViewNode;\n};\n\nexport type RemoveViewChange = {\n type: 'remove';\n node: ViewNode;\n};\n\ntype ChildViewChange = {\n type: 'child';\n node: RowOnlyNode;\n child: {\n relationshipName: string;\n change: ViewChange;\n };\n};\n\ntype EditViewChange = {\n type: 'edit';\n node: RowOnlyNode;\n oldNode: RowOnlyNode;\n};\n\n/**\n * This is a subset of WeakMap but restricted to what we need.\n * @deprecated Not used anymore. This will be removed in the future.\n */\nexport interface RefCountMap {\n get(entry: Entry): number | undefined;\n set(entry: Entry, refCount: number): void;\n delete(entry: Entry): boolean;\n}\n\n/**\n * Get child nodes from a relationship, handling both lazy (Node) and expanded (ExpandedNode).\n */\nfunction getChildNodes(\n node: ViewNode,\n relationship: string,\n): Iterable<ViewNode> {\n const children = node.relationships[relationship];\n if (Array.isArray(children)) {\n // ExpandedNode: already an array\n return children;\n }\n // Node: lazy generator function\n return skipYields(children());\n}\n\ntype Mutate = boolean;\ntype WithIDs = boolean;\n\n/**\n * Transaction-scoped copy-on-write.\n *\n * `applyChange` is immutable: it path-copies a new spine on every call so the\n * previously-emitted snapshot is never mutated. But within a single transaction\n * (a run of `push()` calls before `flush()`) only the final state is ever\n * observed by a listener, so the intermediate spines are pure allocation/GC\n * churn — making a K-change transaction O(K * spineSize).\n *\n * When the caller passes a WeakSet as the `mutate` argument, objects and arrays\n * that were created or cloned during the current transaction are recorded in it.\n * A helper that needs to modify such an object mutates it in place (it is not\n * yet observed) instead of cloning again. The first touch of a node still\n * copies-on-write (preserving the committed snapshot's references); subsequent\n * touches in the same transaction are in-place. This brings a K-change\n * transaction to O(spineSize + K) while keeping cross-transaction reference\n * stability.\n *\n * The set is module-scoped for the duration of a single (synchronous,\n * non-re-entrant) `applyChange` call and saved/restored around it.\n *\n * It is a WeakSet on purpose: it must not keep transiently-cloned arrays/entries\n * alive. A strong Set would extend their lifetime to the end of the transaction,\n * pushing large clones out of the young generation and adding GC pressure that\n * shows up as a regression on single-change transactions over wide lists. A\n * WeakSet gives the same identity test (\"created this transaction?\") while\n * letting dead clones be collected as cheaply as in the plain immutable path.\n */\nlet activeDirty: WeakSet<object> | undefined;\n\n/** True if `o` was created/cloned during the current transaction. */\nfunction owns(o: object): boolean {\n return activeDirty !== undefined && activeDirty.has(o);\n}\n\n/** Record `o` as owned by the current transaction (no-op outside a txn). */\nfunction track<T extends object>(o: T): T {\n activeDirty?.add(o);\n return o;\n}\n\n/**\n * Immutable view update. Returns new Entry on change, same Entry if unchanged.\n * Unchanged entries keep identity, enabling shallow comparison optimizations\n * in UI frameworks (React.memo, Solid's fine-grained reactivity, etc).\n *\n * Propagation: recurse DOWN to find target, copy objects on the way UP.\n * Siblings keep original refs. Only the ancestor path is copied.\n *\n * root {users:[A,B], items:[C,D,E]} --edit C--> root' {users:[A,B], items':[C',D,E]}\n * │ same ref │ │ │\n * └─────────────────┴── unchanged ──────────────┘ new array C' new, D/E same\n *\n * `mutate` selects the update strategy:\n * - `false` (default): fully immutable; every changed node is path-copied.\n * - `true`: mutate the tree in place (only safe when it is not yet observed,\n * e.g. during initial hydration).\n * - a `WeakSet`: transaction-scoped copy-on-write — copy on first touch, then\n * mutate in place for the rest of the transaction. See `activeDirty` above.\n */\nexport function applyChange(\n parentEntry: Entry,\n change: ViewChange,\n schema: SourceSchema,\n relationship: string,\n format: Format,\n withIDs = false,\n mutate: boolean | WeakSet<object> = false,\n): Entry {\n // A WeakSet selects copy-on-write; a boolean selects full mutate / immutable.\n const inPlace = mutate === true;\n const prevDirty = activeDirty;\n activeDirty = typeof mutate === 'object' ? mutate : undefined;\n try {\n return applyChangeInternal(\n parentEntry as MetaEntry<typeof inPlace>,\n change,\n schema,\n relationship,\n format,\n withIDs,\n inPlace,\n );\n } finally {\n activeDirty = prevDirty;\n }\n}\n\nexport function applyChangeInternal<M extends Mutate>(\n parentEntry: MetaEntry<M>,\n change: ViewChange,\n schema: SourceSchema,\n relationship: string,\n format: Format,\n withIDs: WithIDs,\n mutate: M,\n): MetaEntry<M> {\n if (schema.isHidden) {\n switch (change.type) {\n case 'add':\n case 'remove': {\n let currentParent = parentEntry;\n for (const relationship of Object.keys(change.node.relationships)) {\n const childSchema = must(schema.relationships[relationship]);\n for (const node of getChildNodes(change.node, relationship)) {\n currentParent = applyChangeInternal(\n currentParent,\n {type: change.type, node},\n childSchema,\n relationship,\n format,\n withIDs,\n mutate,\n );\n }\n }\n return currentParent;\n }\n case 'edit':\n // If hidden at this level it means that the hidden row was changed. If\n // the row was changed in such a way that it would change the\n // relationships then the edit would have been split into remove and\n // add.\n return parentEntry;\n case 'child': {\n const childSchema = must(\n schema.relationships[change.child.relationshipName],\n );\n return applyChangeInternal(\n parentEntry,\n change.child.change,\n childSchema,\n relationship,\n format,\n withIDs,\n mutate,\n );\n }\n default:\n unreachable(change);\n }\n }\n\n const {singular, relationships: childFormats} = format;\n switch (change.type) {\n // ADD: Insert row (rc=1) or increment refCount if duplicate.\n // RefCount tracks when the same row is reachable via multiple edges\n // within a relationship:\n //\n // issue.labels: [L1, L2] both point to same creator\n // │ │ │\n // ▼ ▼ ▼\n // label.creator ──────────► [User:rc=2] (one row, two refs)\n //\n // add(A) add(A) remove(A) remove(A)\n // ↓ ↓ ↓ ↓\n // [A:rc=1] → [A:rc=2] → [A:rc=1] → (deleted)\n case 'add': {\n if (singular) {\n const oldEntry = getOptionalSingularEntry(parentEntry, relationship);\n if (oldEntry !== undefined) {\n // Duplicate add: increment refCount\n assert(\n schema.compareRows(oldEntry, change.node.row) === 0,\n `Singular relationship '${relationship}' should not have multiple rows. You may need to declare this relationship with the \\`many\\` helper instead of the \\`one\\` helper in your schema.`,\n );\n\n const newEntry = incRefCount(mutate, oldEntry);\n return setRelation(mutate, parentEntry, relationship, newEntry);\n } else {\n // New row: create with rc=1, initialize nested relationships\n const newEntry = makeNewMetaEntry(\n change.node.row,\n schema,\n withIDs,\n 1,\n );\n initializeRelationshipsForNewEntryIfAny(\n newEntry,\n change.node,\n schema,\n childFormats,\n withIDs,\n );\n return setRelation(mutate, parentEntry, relationship, newEntry);\n }\n } else {\n // Plural: binary search for position, insert or increment refCount\n const view = getChildEntryList(parentEntry, relationship);\n const {newEntry, newView} = add(\n change.node.row,\n view,\n schema,\n withIDs,\n mutate,\n );\n\n if (newEntry) {\n initializeRelationshipsForNewEntryIfAny(\n newEntry,\n change.node,\n schema,\n childFormats,\n withIDs,\n );\n }\n return setRelation(mutate, parentEntry, relationship, newView);\n }\n }\n // REMOVE: Decrement refCount, physically remove when rc=0.\n case 'remove': {\n if (singular) {\n const oldEntry = getSingularEntry(parentEntry, relationship);\n const rc = oldEntry[refCountSymbol];\n if (rc === 1) {\n return setRelation(mutate, parentEntry, relationship, undefined);\n }\n const newEntry = decRefCount(mutate, oldEntry);\n return setRelation(mutate, parentEntry, relationship, newEntry);\n } else {\n const view = getChildEntryList(parentEntry, relationship);\n const newView = removeAndUpdateRefCount(\n view,\n change.node.row,\n schema.compareRows,\n mutate,\n );\n return setRelation(mutate, parentEntry, relationship, newView);\n }\n }\n // CHILD: Propagate nested change up to this level (leaf-to-root pattern).\n case 'child': {\n const childSchema = must(\n schema.relationships[change.child.relationshipName],\n );\n const childFormat = format.relationships[change.child.relationshipName];\n if (childFormat === undefined) {\n return parentEntry; // Relationship not in view format\n }\n\n if (singular) {\n const existing = getSingularEntry(parentEntry, relationship);\n const newExisting = applyChangeInternal(\n existing,\n change.child.change,\n childSchema,\n change.child.relationshipName,\n childFormat,\n withIDs,\n mutate,\n );\n // Preserve identity if child didn't change (enables shallow-compare optimizations).\n if (newExisting === existing) {\n return parentEntry;\n }\n return setRelation(mutate, parentEntry, relationship, newExisting);\n } else {\n // Find the target row in the sorted array via binary search.\n const view = getChildEntryList(parentEntry, relationship);\n const pos = binarySearch(view, change.node.row, schema.compareRows);\n assert(pos >= 0, 'node does not exist');\n const existing = view[pos];\n const newExisting = applyChangeInternal(\n existing,\n change.child.change,\n childSchema,\n change.child.relationshipName,\n childFormat,\n withIDs,\n mutate,\n );\n // Preserve identity if descendant didn't change.\n if (newExisting === existing) {\n return parentEntry;\n }\n // applyChangeInternal preserves MetaEntry structure when input is MetaEntry\n assertMetaEntry(newExisting);\n return setRelation(\n mutate,\n parentEntry,\n relationship,\n arrayWith(mutate, view, pos, newExisting),\n );\n }\n }\n // EDIT: Update row fields. If sort key changes, row may move position.\n //\n // Position change with rc>1 (two query paths reach same row):\n //\n // Before: [A, B(rc=2), C, D] After: [A, B(rc=1), C, B'(rc=1), D]\n // │ │ │\n // path1 + path2 path1 ghost path2 moved\n //\n // Why ghost stays: path1 still expects B at old position.\n // B' appears where path2's sort order places it.\n case 'edit': {\n if (singular) {\n const existing = getSingularEntry(parentEntry, relationship);\n const newEntry = applyEdit(existing, change, schema, withIDs, mutate);\n return setRelation(mutate, parentEntry, relationship, newEntry);\n } else {\n const view = getChildEntryList(parentEntry, relationship);\n // Sort key changed: row may need to move\n if (schema.compareRows(change.oldNode.row, change.node.row) !== 0) {\n const oldPos = binarySearch(\n view,\n change.oldNode.row,\n schema.compareRows,\n );\n assert(oldPos >= 0, 'old node does not exist');\n const oldEntry = view[oldPos];\n const rawPos = binarySearch(\n view,\n change.node.row,\n schema.compareRows,\n );\n const found = rawPos >= 0;\n const pos = found ? rawPos : ~rawPos;\n // A special case:\n // when refCount is 1 (so the row is being moved without leaving a\n // placeholder behind), and the new pos is the same as the old, or\n // directly after the old (so after the remove of the old it would be\n // in the same pos): the row does not need to be moved, just edited.\n if (\n oldEntry[refCountSymbol] === 1 &&\n (pos === oldPos || pos - 1 === oldPos)\n ) {\n const newEntry = applyEdit(\n oldEntry,\n change,\n schema,\n withIDs,\n mutate,\n );\n return setRelation(\n mutate,\n parentEntry,\n relationship,\n arrayWith(mutate, view, oldPos, newEntry),\n );\n } else {\n // Move the row. If rc > 1, an edit will be received for each ref.\n // On first edit: move row, apply edit, set rc=1. Leave a copy at\n // old pos with decremented rc. As each edit arrives, old copy's rc\n // decrements and new pos rc increments. When copy rc hits 0, remove.\n const newRefCount = oldEntry[refCountSymbol] - 1;\n let newView: MutableMetaEntryList;\n let adjustedPos = pos;\n\n if (newRefCount === 0) {\n newView = removeAt(mutate, view, oldPos);\n adjustedPos = oldPos < pos ? pos - 1 : pos;\n } else {\n const oldEntryCopy = setRefCount(mutate, oldEntry, newRefCount);\n newView = arrayWith(mutate, view, oldPos, oldEntryCopy);\n }\n\n if (found) {\n // Merge with existing at new pos\n const existingEntry = newView[adjustedPos];\n const editedEntry = applyEdit(\n existingEntry,\n change,\n schema,\n withIDs,\n mutate,\n );\n const updatedEntry = setRefCount(\n mutate,\n editedEntry,\n existingEntry[refCountSymbol] + 1,\n );\n return setRelation(\n mutate,\n parentEntry,\n relationship,\n arrayWith(mutate, newView, adjustedPos, updatedEntry),\n );\n } else {\n // Insert at new pos with rc=1\n const editedEntry = applyEdit(\n oldEntry,\n change,\n schema,\n withIDs,\n mutate,\n );\n const movedEntry = setRefCount(mutate, editedEntry, 1);\n return setRelation(\n mutate,\n parentEntry,\n relationship,\n insertAt(mutate, newView, adjustedPos, movedEntry),\n );\n }\n }\n } else {\n // Sort key unchanged: edit in place\n const pos = binarySearch(\n view,\n change.oldNode.row,\n schema.compareRows,\n );\n assert(pos >= 0, 'node does not exist');\n const newEntry = applyEdit(\n view[pos],\n change,\n schema,\n withIDs,\n mutate,\n );\n return setRelation(\n mutate,\n parentEntry,\n relationship,\n arrayWith(mutate, view, pos, newEntry),\n );\n }\n }\n }\n default:\n unreachable(change);\n }\n}\n\n/**\n * Batch apply multiple changes to an Entry tree.\n * For small batches or complex cases, falls back to sequential applyChange.\n * Future optimization: O(N + K) batch processing for large K.\n */\nexport function applyChanges(\n parentEntry: Entry,\n changes: ViewChange[],\n schema: SourceSchema,\n relationship: string,\n format: Format,\n withIDs: WithIDs = false,\n mutate: Mutate = false,\n): Entry {\n let result = parentEntry;\n for (const change of changes) {\n result = applyChange(\n result,\n change,\n schema,\n relationship,\n format,\n withIDs,\n mutate,\n );\n }\n return result;\n}\n\nfunction applyEdit<M extends Mutate>(\n existing: MetaEntry<M>,\n change: EditViewChange,\n schema: SourceSchema,\n withIDs: WithIDs,\n mutate: Mutate,\n): MetaEntry<M> {\n // In-place edit is safe when fully mutating or when `existing` was already\n // created/cloned in this transaction (copy-on-write). A primary-key change\n // always needs a fresh entry so identity tracks the new key.\n const canMutate = mutate || owns(existing);\n const newEntry: MutableMetaEntry =\n canMutate && schema.compareRows(change.oldNode.row, change.node.row) === 0\n ? Object.assign(existing, change.node.row)\n : track({...existing, ...change.node.row});\n\n if (withIDs) {\n return setProperty(\n mutate,\n newEntry,\n idSymbol,\n makeID(change.node.row, schema),\n );\n }\n return newEntry;\n}\n\n/**\n * Initialize child relationships on a newly-added entry by mutating it in place.\n *\n * New nodes don't exist in the view yet, so we can build in-place (no refs to preserve):\n *\n * New nodes don't exist in the view yet, so we can build in-place (no refs to preserve):\n *\n * Existing node edit (must path-copy): New node (can build in-place):\n *\n * view: [A, B, C] view: [A, B, _]\n * │ │\n * edit C.child add C with children\n * │ │\n * ▼ ▼\n * view': [A, B, C'] ◄─ path-copy view': [A, B, C{children:[...]}]\n * up the tree └─ built in-place\n *\n * Hidden schemas still use applyChange to collapse junction levels.\n */\nfunction initializeRelationshipsForNewEntryIfAny(\n entry: MutableMetaEntry,\n node: ViewNode,\n schema: SourceSchema,\n childFormats: Record<string, Format>,\n withIDs: WithIDs,\n): void {\n let result = entry;\n for (const relationship of Object.keys(node.relationships)) {\n const childSchema = must(schema.relationships[relationship]);\n const childFormat = childFormats[relationship];\n if (childFormat === undefined) {\n continue;\n }\n\n // Hidden/singular: use applyChange to handle properly\n if (childSchema.isHidden || childFormat.singular) {\n const newView = childFormat.singular\n ? undefined\n : track([] as MutableMetaEntryList);\n result[relationship] = newView;\n\n for (const childNode of getChildNodes(node, relationship)) {\n applyChangeInternal(\n result,\n {type: 'add', node: childNode},\n childSchema,\n relationship,\n childFormat,\n withIDs,\n true, // this is a new entry, so we can mutate\n );\n }\n } else {\n // Plural non-hidden: build array in-place for efficiency\n const childArray: MutableMetaEntryList = track([]);\n\n for (const childNode of getChildNodes(node, relationship)) {\n const newEntry = makeNewMetaEntry(\n childNode.row,\n childSchema,\n withIDs,\n 1,\n );\n const rawPos = binarySearch(\n childArray,\n childNode.row,\n childSchema.compareRows,\n );\n\n if (rawPos >= 0) {\n childArray[rawPos][refCountSymbol]++;\n } else {\n childArray.splice(~rawPos, 0, newEntry);\n initializeRelationshipsForNewEntryIfAny(\n newEntry,\n childNode,\n childSchema,\n childFormat.relationships,\n withIDs,\n );\n }\n }\n\n result[relationship] = childArray;\n }\n }\n}\n\nfunction add<M extends Mutate>(\n row: Row,\n view: MetaEntryList<M>,\n schema: SourceSchema,\n withIDs: WithIDs,\n mutate: M,\n): {\n newEntry: MutableMetaEntry | undefined;\n newView: MutableMetaEntryList;\n} {\n const rawPos = binarySearch(view, row, schema.compareRows);\n\n if (rawPos >= 0) {\n const existing = view[rawPos];\n\n const updated = incRefCount(mutate, existing);\n return {\n newEntry: undefined,\n newView: arrayWith(mutate, view, rawPos, updated),\n };\n }\n const newEntry = makeNewMetaEntry(row, schema, withIDs, 1);\n return {newEntry, newView: insertAt(mutate, view, ~rawPos, newEntry)};\n}\n\nfunction insertAt<M extends Mutate, T>(\n mutate: M,\n array: MutableArray<M, T>,\n index: number,\n item: T,\n): T[] {\n if (mutate || owns(array)) {\n (array as T[]).splice(index, 0, item);\n return array as T[];\n }\n return track(array.toSpliced(index, 0, item));\n}\n\nfunction removeAt<M extends Mutate, T>(\n mutate: M,\n array: MutableArray<M, T>,\n index: number,\n): T[] {\n if (mutate || owns(array)) {\n (array as T[]).splice(index, 1);\n return array as T[];\n }\n return track(array.toSpliced(index, 1));\n}\n\nfunction removeAndUpdateRefCount<M extends Mutate>(\n view: MetaEntryList<M>,\n row: Row,\n compareRows: Comparator,\n mutate: M,\n): MutableMetaEntryList {\n const pos = binarySearch(view, row, compareRows);\n assert(pos >= 0, 'node does not exist');\n const oldEntry = view[pos];\n const rc = oldEntry[refCountSymbol];\n if (rc === 1) {\n return removeAt(mutate, view, pos);\n }\n const newEntry = decRefCount(mutate, oldEntry);\n return arrayWith(mutate, view, pos, newEntry);\n}\n\n/**\n * Binary search returning a number.\n * - If found at index `i`: returns `i` (>= 0).\n * - If not found, insertion point is `low`: returns `~low` (< 0).\n * Recover insertion point with `~result`.\n */\nfunction binarySearch(\n view: MetaEntryList<Mutate>,\n target: Row,\n comparator: Comparator,\n): number {\n let high = view.length - 1;\n if (high < 0) {\n return ~0;\n }\n\n // Probe the last entry before searching. Hydration feeds the view rows in\n // the query's sort order, so every insert belongs at the end and the plain\n // search spends log2(n) comparisons to rediscover that -- about eleven per\n // row for a view of a couple of thousand. Row comparison is the single\n // largest cost in hydration on Hermes, so collapsing those eleven to one is\n // worth the one extra comparison this costs when the row does land inside\n // the view, which is a single push rather than a bulk load.\n // MetaEntry has all Row props; comparator only reads string keys\n const last = comparator(view[high] as Row, target);\n if (last < 0) {\n return ~(high + 1);\n }\n if (last === 0) {\n return high;\n }\n\n let low = 0;\n high -= 1;\n while (low <= high) {\n const mid = (low + high) >>> 1;\n const comparison = comparator(view[mid] as Row, target);\n if (comparison < 0) {\n low = mid + 1;\n } else if (comparison > 0) {\n high = mid - 1;\n } else {\n return mid;\n }\n }\n return ~low;\n}\n\n/** Assert value is MetaEntry (has refCountSymbol). */\nfunction assertMetaEntry<M extends Mutate>(\n v: unknown,\n): asserts v is MetaEntry<M> {\n assertNumber((v as Partial<MetaEntry<M>>)[refCountSymbol]);\n}\n\n/** Get singular MetaEntry, throws if missing. */\nfunction getSingularEntry<M extends Mutate>(\n parentEntry: MetaEntry<M>,\n relationship: string,\n): MetaEntry<M> {\n const entry = parentEntry[relationship];\n assert(entry !== undefined, 'node does not exist');\n assertMetaEntry(entry);\n return entry;\n}\n\n/** Get singular MetaEntry or undefined if not set. */\nfunction getOptionalSingularEntry<M extends Mutate>(\n parentEntry: MetaEntry<M>,\n relationship: string,\n): MetaEntry<M> | undefined {\n const entry = parentEntry[relationship];\n if (entry === undefined) {\n return undefined;\n }\n assertMetaEntry(entry);\n return entry;\n}\n\n/** Get child array as MetaEntryList. */\nfunction getChildEntryList<M extends Mutate>(\n parentEntry: MetaEntry<M>,\n relationship: string,\n): MetaEntryList<M> {\n const view = parentEntry[relationship];\n // `relationship` is a string key, so a name colliding with a column would\n // put a row value here; the message must not repeat it back.\n assert(Array.isArray(view), 'expected relationship array');\n return view as MetaEntryList<M>;\n}\n\n/** Create MetaEntry from row with given refCount. */\nfunction makeNewMetaEntry(\n row: Row,\n schema: SourceSchema,\n withIDs: WithIDs,\n rc: number,\n): MutableMetaEntry {\n // This creates a new MetaEntry from a Row. We never mutate Rows.\n if (withIDs) {\n return track({\n ...row,\n [refCountSymbol]: rc,\n [idSymbol]: makeID(row, schema),\n });\n }\n return track({\n ...row,\n [refCountSymbol]: rc,\n });\n}\n\nfunction makeID(row: Row, schema: SourceSchema) {\n // optimization for case of non-compound primary key\n if (schema.primaryKey.length === 1) {\n return JSON.stringify(row[schema.primaryKey[0]]);\n }\n return JSON.stringify(schema.primaryKey.map(k => row[k]));\n}\n\nfunction incRefCount<M extends Mutate>(\n mutate: M,\n entry: MetaEntry<M>,\n): MutableMetaEntry {\n return setRefCount(mutate, entry, entry[refCountSymbol] + 1);\n}\n\nfunction decRefCount<M extends Mutate>(\n mutate: M,\n entry: MetaEntry<M>,\n): MutableMetaEntry {\n return setRefCount(mutate, entry, entry[refCountSymbol] - 1);\n}\n\nfunction setRefCount<M extends Mutate>(\n mutate: M,\n entry: MetaEntry<M>,\n count: number,\n): MutableMetaEntry {\n if (mutate || owns(entry)) {\n (entry as MutableMetaEntry)[refCountSymbol] = count;\n return entry;\n }\n return track({\n ...entry,\n [refCountSymbol]: count,\n });\n}\n\nfunction arrayWith<M extends Mutate, T>(\n mutate: M,\n array: MutableArray<M, T>,\n index: number,\n value: T,\n): T[] {\n if (mutate || owns(array)) {\n (array as T[])[index] = value;\n return array as T[];\n }\n return track(array.with(index, value));\n}\n\nfunction setProperty<\n M extends Mutate,\n K extends string | keyof MetaEntry<M>,\n V,\n>(\n mutate: M,\n parentEntry: MetaEntry<M>,\n key: K,\n value: V,\n): MutableMetaEntry & {[P in K]: V} {\n if (mutate || owns(parentEntry)) {\n assignProperty(parentEntry as {[P in K]: V}, key, value);\n return parentEntry as MutableMetaEntry & {[P in K]: V};\n }\n return track({\n ...parentEntry,\n [key]: value,\n }) as MutableMetaEntry & {[P in K]: V};\n}\n\nconst setRelation: <M extends Mutate>(\n mutate: M,\n parentEntry: MetaEntry<M>,\n relationship: string,\n value: Entry | Entry[] | undefined,\n) => MutableMetaEntry = setProperty;\n"],"mappings":"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"}
@@ -27,7 +27,8 @@ function makeCRUDMutate(schema, addSchemaCRUD, executor) {
27
27
  return mutate;
28
28
  }
29
29
  function makeTransactionMutate(schema, executor) {
30
- return recordProxy(mapValues(schema.tables, () => void 0), (_value, tableName) => makeTableCRUD(tableName, executor));
30
+ const target = mapValues(schema.tables, () => void 0);
31
+ return recordProxy(target, (_value, tableName) => makeTableCRUD(tableName, executor));
31
32
  }
32
33
  /**
33
34
  * Creates a TableCRUD object that delegates to the executor.
@@ -36,6 +37,6 @@ function makeTableCRUD(tableName, executor) {
36
37
  return Object.fromEntries(CRUD_KINDS.map((kind) => [kind, (value) => executor(tableName, kind, value)]));
37
38
  }
38
39
  //#endregion
39
- export { makeCRUDMutate, makeTransactionMutate };
40
+ export { CRUD_KINDS, makeCRUDMutate, makeTransactionMutate };
40
41
 
41
42
  //# sourceMappingURL=crud.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"crud.js","names":[],"sources":["../../../../../zql/src/mutate/crud.ts"],"sourcesContent":["import type {Expand} from '../../../shared/src/expand.ts';\nimport {assignProperty, mapValues} from '../../../shared/src/objects.ts';\nimport {recordProxy} from '../../../shared/src/record-proxy.ts';\nimport type {SchemaValueToTSType} from '../../../zero-types/src/schema-value.ts';\nimport type {Schema, TableSchema} from '../../../zero-types/src/schema.ts';\nimport type {MutateCRUD} from './custom.ts';\n\nexport type SchemaCRUD<S extends Schema> = {\n [Table in keyof S['tables']]: TableCRUD<S['tables'][Table]>;\n};\n\nexport type TransactionMutate<S extends Schema> = SchemaCRUD<S>;\n\nexport type TableCRUD<S extends TableSchema> = {\n /**\n * Writes a row. If a row with the same primary key already exists this is a\n * no-op; the existing row is left unchanged. A collision on any other unique\n * constraint still throws. Non-primary-key fields that are 'optional' can be\n * omitted or set to `undefined`. Such fields will be assigned the value\n * `null` optimistically and then the default value as defined by the server.\n */\n insert: (value: InsertValue<S>) => Promise<void>;\n\n /**\n * Writes a row unconditionally, overwriting any existing row with the same\n * primary key. Non-primary-key fields that are 'optional' can be omitted or\n * set to `undefined`. Such fields will be assigned the value `null`\n * optimistically and then the default value as defined by the server.\n */\n upsert: (value: UpsertValue<S>) => Promise<void>;\n\n /**\n * Updates a row with the same primary key. If no such row exists, this\n * function does nothing. All non-primary-key fields can be omitted or set to\n * `undefined`. Such fields will be left unchanged from previous value.\n */\n update: (value: UpdateValue<S>) => Promise<void>;\n\n /**\n * Deletes the row with the specified primary key. If no such row exists, this\n * function does nothing.\n */\n delete: (id: DeleteID<S>) => Promise<void>;\n};\n\nexport type CRUDKind = keyof TableCRUD<TableSchema>;\n\nexport const CRUD_KINDS = ['insert', 'upsert', 'update', 'delete'] as const;\n\nexport type DeleteID<S extends TableSchema> = Expand<PrimaryKeyFields<S>>;\n\ntype PrimaryKeyFields<S extends TableSchema> = {\n [\n K in Extract<S['primaryKey'][number], keyof S['columns']>\n ]: SchemaValueToTSType<S['columns'][K]>;\n};\n\nexport type InsertValue<S extends TableSchema> = Expand<\n PrimaryKeyFields<S> & {\n [\n K in keyof S['columns'] as S['columns'][K] extends {optional: true}\n ? K\n : never\n ]?: SchemaValueToTSType<S['columns'][K]> | undefined;\n } & {\n [\n K in keyof S['columns'] as S['columns'][K] extends {optional: true}\n ? never\n : K\n ]: SchemaValueToTSType<S['columns'][K]>;\n }\n>;\n\nexport type UpsertValue<S extends TableSchema> = InsertValue<S>;\n\nexport type UpdateValue<S extends TableSchema> = Expand<\n PrimaryKeyFields<S> & {\n [K in keyof S['columns']]?:\n | SchemaValueToTSType<S['columns'][K]>\n | undefined;\n }\n>;\n\n/**\n * This is the type of the generated mutate.<name>.<verb> function.\n */\nexport type TableMutator<TS extends TableSchema> = {\n /**\n * Writes a row. If a row with the same primary key already exists this is a\n * no-op; the existing row is left unchanged. A collision on any other unique\n * constraint still throws. Non-primary-key fields that are 'optional' can be\n * omitted or set to `undefined`. Such fields will be assigned the value\n * `null` optimistically and then the default value as defined by the server.\n */\n insert: (value: InsertValue<TS>) => Promise<void>;\n\n /**\n * Writes a row unconditionally, overwriting any existing row with the same\n * primary key. Non-primary-key fields that are 'optional' can be omitted or\n * set to `undefined`. Such fields will be assigned the value `null`\n * optimistically and then the default value as defined by the server.\n */\n upsert: (value: UpsertValue<TS>) => Promise<void>;\n\n /**\n * Updates a row with the same primary key. If no such row exists, this\n * function does nothing. All non-primary-key fields can be omitted or set to\n * `undefined`. Such fields will be left unchanged from previous value.\n */\n update: (value: UpdateValue<TS>) => Promise<void>;\n\n /**\n * Deletes the row with the specified primary key. If no such row exists, this\n * function does nothing.\n */\n delete: (id: DeleteID<TS>) => Promise<void>;\n};\n\n/**\n * A function that executes a CRUD operation.\n * Client and server provide different implementations.\n */\nexport type CRUDExecutor = (\n table: string,\n kind: CRUDKind,\n args: unknown,\n) => Promise<void>;\n\n/**\n * Creates a MutateCRUD function from a schema and executor.\n * This is the shared implementation used by both client and server.\n *\n * @param schema - The Zero schema\n * @param executor - A function that executes CRUD operations\n * @returns A MutateCRUD function that can be called with CRUDMutateRequest objects\n */\nexport function makeCRUDMutate<\n TSchema extends Schema,\n TAddSchemaCRUD extends boolean,\n>(\n schema: TSchema,\n addSchemaCRUD: TAddSchemaCRUD,\n executor: CRUDExecutor,\n): MutateCRUD<TSchema, TAddSchemaCRUD> {\n // Create a callable function that accepts CRUDMutateRequest\n const mutate = (request: AnyCRUDMutateRequest) => {\n const {table, kind, args} = request;\n return executor(table, kind, args);\n };\n\n // Only add table properties when enableLegacyMutators is true\n if (addSchemaCRUD) {\n // Add table names as keys so the proxy can discover them\n for (const tableName of Object.keys(schema.tables)) {\n assignProperty(\n mutate as unknown as Record<string, undefined>,\n tableName,\n undefined,\n );\n }\n\n // Wrap in proxy that lazily creates and caches table CRUD objects\n return recordProxy(\n mutate as unknown as Record<string, undefined>,\n (_value, tableName) => makeTableCRUD(tableName, executor),\n ) as unknown as MutateCRUD<TSchema, TAddSchemaCRUD>;\n }\n\n return mutate as MutateCRUD<TSchema, TAddSchemaCRUD>;\n}\n\nexport function makeTransactionMutate<TSchema extends Schema>(\n schema: TSchema,\n executor: CRUDExecutor,\n): TransactionMutate<TSchema> {\n const target = mapValues(schema.tables, () => undefined);\n\n return recordProxy(target, (_value, tableName) =>\n makeTableCRUD(tableName, executor),\n ) as SchemaCRUD<TSchema>;\n}\n\n/**\n * Creates a TableCRUD object that delegates to the executor.\n */\nfunction makeTableCRUD(\n tableName: string,\n executor: CRUDExecutor,\n): TableCRUD<TableSchema> {\n return Object.fromEntries(\n CRUD_KINDS.map(kind => [\n kind,\n (value: unknown) => executor(tableName, kind, value),\n ]),\n ) as TableCRUD<TableSchema>;\n}\n\nexport type CRUDMutator<\n TSchema extends Schema,\n TTable extends keyof TSchema['tables'] & string,\n TKind extends keyof TableMutator<TSchema['tables'][TTable]>,\n TArgs extends Parameters<TableMutator<TSchema['tables'][TTable]>[TKind]>[0],\n> = {\n (args: TArgs): CRUDMutateRequest<TSchema, TTable, TKind, TArgs>;\n\n /**\n * Type-only phantom property to surface mutator types in a covariant position.\n */\n ['~']: Expand<CRUDMutatorTypes<TSchema, TTable, TKind, TArgs>>;\n};\n\nexport type CRUDMutatorTypes<\n TSchema extends Schema,\n TTable extends keyof TSchema['tables'] & string,\n TKind extends keyof TableMutator<TSchema['tables'][TTable]>,\n TArgs extends Parameters<TableMutator<TSchema['tables'][TTable]>[TKind]>[0],\n> = 'CRUDMutator' & CRUDMutateRequest<TSchema, TTable, TKind, TArgs>;\n\nexport type CRUDMutateRequest<\n TSchema extends Schema,\n TTable extends keyof TSchema['tables'],\n TKind extends keyof TableMutator<TSchema['tables'][TTable]>,\n TArgs extends Parameters<TableMutator<TSchema['tables'][TTable]>[TKind]>[0],\n> = {\n readonly schema: TSchema;\n readonly table: TTable;\n readonly kind: TKind;\n readonly args: TArgs;\n};\n\n// oxlint-disable-next-line no-explicit-any\nexport type AnyCRUDMutateRequest = CRUDMutateRequest<any, any, CRUDKind, any>;\n"],"mappings":";;;AA+CA,IAAa,aAAa;CAAC;CAAU;CAAU;CAAU;AAAQ;;;;;;;;;AAyFjE,SAAgB,eAId,QACA,eACA,UACqC;CAErC,MAAM,UAAU,YAAkC;EAChD,MAAM,EAAC,OAAO,MAAM,SAAQ;EAC5B,OAAO,SAAS,OAAO,MAAM,IAAI;CACnC;CAGA,IAAI,eAAe;EAEjB,KAAK,MAAM,aAAa,OAAO,KAAK,OAAO,MAAM,GAC/C,eACE,QACA,WACA,KAAA,CACF;EAIF,OAAO,YACL,SACC,QAAQ,cAAc,cAAc,WAAW,QAAQ,CAC1D;CACF;CAEA,OAAO;AACT;AAEA,SAAgB,sBACd,QACA,UAC4B;CAG5B,OAAO,YAFQ,UAAU,OAAO,cAAc,KAAA,CAE3B,IAAS,QAAQ,cAClC,cAAc,WAAW,QAAQ,CACnC;AACF;;;;AAKA,SAAS,cACP,WACA,UACwB;CACxB,OAAO,OAAO,YACZ,WAAW,KAAI,SAAQ,CACrB,OACC,UAAmB,SAAS,WAAW,MAAM,KAAK,CACrD,CAAC,CACH;AACF"}
1
+ {"version":3,"file":"crud.js","names":[],"sources":["../../../../../zql/src/mutate/crud.ts"],"sourcesContent":["import type {Expand} from '../../../shared/src/expand.ts';\nimport {assignProperty, mapValues} from '../../../shared/src/objects.ts';\nimport {recordProxy} from '../../../shared/src/record-proxy.ts';\nimport type {SchemaValueToTSType} from '../../../zero-types/src/schema-value.ts';\nimport type {Schema, TableSchema} from '../../../zero-types/src/schema.ts';\nimport type {MutateCRUD} from './custom.ts';\n\nexport type SchemaCRUD<S extends Schema> = {\n [Table in keyof S['tables']]: TableCRUD<S['tables'][Table]>;\n};\n\nexport type TransactionMutate<S extends Schema> = SchemaCRUD<S>;\n\nexport type TableCRUD<S extends TableSchema> = {\n /**\n * Writes a row. If a row with the same primary key already exists this is a\n * no-op; the existing row is left unchanged. A collision on any other unique\n * constraint still throws. Non-primary-key fields that are 'optional' can be\n * omitted or set to `undefined`. Such fields will be assigned the value\n * `null` optimistically and then the default value as defined by the server.\n */\n insert: (value: InsertValue<S>) => Promise<void>;\n\n /**\n * Writes a row unconditionally, overwriting any existing row with the same\n * primary key. Non-primary-key fields that are 'optional' can be omitted or\n * set to `undefined`. Such fields will be assigned the value `null`\n * optimistically and then the default value as defined by the server.\n */\n upsert: (value: UpsertValue<S>) => Promise<void>;\n\n /**\n * Updates a row with the same primary key. If no such row exists, this\n * function does nothing. All non-primary-key fields can be omitted or set to\n * `undefined`. Such fields will be left unchanged from previous value.\n */\n update: (value: UpdateValue<S>) => Promise<void>;\n\n /**\n * Deletes the row with the specified primary key. If no such row exists, this\n * function does nothing.\n */\n delete: (id: DeleteID<S>) => Promise<void>;\n};\n\nexport type CRUDKind = keyof TableCRUD<TableSchema>;\n\nexport const CRUD_KINDS = ['insert', 'upsert', 'update', 'delete'] as const;\n\nexport type DeleteID<S extends TableSchema> = Expand<PrimaryKeyFields<S>>;\n\ntype PrimaryKeyFields<S extends TableSchema> = {\n [\n K in Extract<S['primaryKey'][number], keyof S['columns']>\n ]: SchemaValueToTSType<S['columns'][K]>;\n};\n\nexport type InsertValue<S extends TableSchema> = Expand<\n PrimaryKeyFields<S> & {\n [\n K in keyof S['columns'] as S['columns'][K] extends {optional: true}\n ? K\n : never\n ]?: SchemaValueToTSType<S['columns'][K]> | undefined;\n } & {\n [\n K in keyof S['columns'] as S['columns'][K] extends {optional: true}\n ? never\n : K\n ]: SchemaValueToTSType<S['columns'][K]>;\n }\n>;\n\nexport type UpsertValue<S extends TableSchema> = InsertValue<S>;\n\nexport type UpdateValue<S extends TableSchema> = Expand<\n PrimaryKeyFields<S> & {\n [K in keyof S['columns']]?:\n | SchemaValueToTSType<S['columns'][K]>\n | undefined;\n }\n>;\n\n/**\n * This is the type of the generated mutate.<name>.<verb> function.\n */\nexport type TableMutator<TS extends TableSchema> = {\n /**\n * Writes a row. If a row with the same primary key already exists this is a\n * no-op; the existing row is left unchanged. A collision on any other unique\n * constraint still throws. Non-primary-key fields that are 'optional' can be\n * omitted or set to `undefined`. Such fields will be assigned the value\n * `null` optimistically and then the default value as defined by the server.\n */\n insert: (value: InsertValue<TS>) => Promise<void>;\n\n /**\n * Writes a row unconditionally, overwriting any existing row with the same\n * primary key. Non-primary-key fields that are 'optional' can be omitted or\n * set to `undefined`. Such fields will be assigned the value `null`\n * optimistically and then the default value as defined by the server.\n */\n upsert: (value: UpsertValue<TS>) => Promise<void>;\n\n /**\n * Updates a row with the same primary key. If no such row exists, this\n * function does nothing. All non-primary-key fields can be omitted or set to\n * `undefined`. Such fields will be left unchanged from previous value.\n */\n update: (value: UpdateValue<TS>) => Promise<void>;\n\n /**\n * Deletes the row with the specified primary key. If no such row exists, this\n * function does nothing.\n */\n delete: (id: DeleteID<TS>) => Promise<void>;\n};\n\n/**\n * A function that executes a CRUD operation.\n * Client and server provide different implementations.\n */\nexport type CRUDExecutor = (\n table: string,\n kind: CRUDKind,\n args: unknown,\n) => Promise<void>;\n\n/**\n * Creates a MutateCRUD function from a schema and executor.\n * This is the shared implementation used by both client and server.\n *\n * @param schema - The Zero schema\n * @param executor - A function that executes CRUD operations\n * @returns A MutateCRUD function that can be called with CRUDMutateRequest objects\n */\nexport function makeCRUDMutate<\n TSchema extends Schema,\n TAddSchemaCRUD extends boolean,\n>(\n schema: TSchema,\n addSchemaCRUD: TAddSchemaCRUD,\n executor: CRUDExecutor,\n): MutateCRUD<TSchema, TAddSchemaCRUD> {\n // Create a callable function that accepts CRUDMutateRequest\n const mutate = (request: AnyCRUDMutateRequest) => {\n const {table, kind, args} = request;\n return executor(table, kind, args);\n };\n\n // Only add table properties when enableLegacyMutators is true\n if (addSchemaCRUD) {\n // Add table names as keys so the proxy can discover them\n for (const tableName of Object.keys(schema.tables)) {\n assignProperty(\n mutate as unknown as Record<string, undefined>,\n tableName,\n undefined,\n );\n }\n\n // Wrap in proxy that lazily creates and caches table CRUD objects\n return recordProxy(\n mutate as unknown as Record<string, undefined>,\n (_value, tableName) => makeTableCRUD(tableName, executor),\n ) as unknown as MutateCRUD<TSchema, TAddSchemaCRUD>;\n }\n\n return mutate as MutateCRUD<TSchema, TAddSchemaCRUD>;\n}\n\nexport function makeTransactionMutate<TSchema extends Schema>(\n schema: TSchema,\n executor: CRUDExecutor,\n): TransactionMutate<TSchema> {\n const target = mapValues(schema.tables, () => undefined);\n\n return recordProxy(target, (_value, tableName) =>\n makeTableCRUD(tableName, executor),\n ) as SchemaCRUD<TSchema>;\n}\n\n/**\n * Creates a TableCRUD object that delegates to the executor.\n */\nfunction makeTableCRUD(\n tableName: string,\n executor: CRUDExecutor,\n): TableCRUD<TableSchema> {\n return Object.fromEntries(\n CRUD_KINDS.map(kind => [\n kind,\n (value: unknown) => executor(tableName, kind, value),\n ]),\n ) as TableCRUD<TableSchema>;\n}\n\nexport type CRUDMutator<\n TSchema extends Schema,\n TTable extends keyof TSchema['tables'] & string,\n TKind extends keyof TableMutator<TSchema['tables'][TTable]>,\n TArgs extends Parameters<TableMutator<TSchema['tables'][TTable]>[TKind]>[0],\n> = {\n (args: TArgs): CRUDMutateRequest<TSchema, TTable, TKind, TArgs>;\n\n /**\n * Type-only phantom property to surface mutator types in a covariant position.\n */\n ['~']: Expand<CRUDMutatorTypes<TSchema, TTable, TKind, TArgs>>;\n};\n\nexport type CRUDMutatorTypes<\n TSchema extends Schema,\n TTable extends keyof TSchema['tables'] & string,\n TKind extends keyof TableMutator<TSchema['tables'][TTable]>,\n TArgs extends Parameters<TableMutator<TSchema['tables'][TTable]>[TKind]>[0],\n> = 'CRUDMutator' & CRUDMutateRequest<TSchema, TTable, TKind, TArgs>;\n\nexport type CRUDMutateRequest<\n TSchema extends Schema,\n TTable extends keyof TSchema['tables'],\n TKind extends keyof TableMutator<TSchema['tables'][TTable]>,\n TArgs extends Parameters<TableMutator<TSchema['tables'][TTable]>[TKind]>[0],\n> = {\n readonly schema: TSchema;\n readonly table: TTable;\n readonly kind: TKind;\n readonly args: TArgs;\n};\n\n// oxlint-disable-next-line no-explicit-any\nexport type AnyCRUDMutateRequest = CRUDMutateRequest<any, any, CRUDKind, any>;\n"],"mappings":";;;AA+CA,IAAa,aAAa;CAAC;CAAU;CAAU;CAAU;AAAQ;;;;;;;;;AAyFjE,SAAgB,eAId,QACA,eACA,UACqC;CAErC,MAAM,UAAU,YAAkC;EAChD,MAAM,EAAC,OAAO,MAAM,SAAQ;EAC5B,OAAO,SAAS,OAAO,MAAM,IAAI;CACnC;CAGA,IAAI,eAAe;EAEjB,KAAK,MAAM,aAAa,OAAO,KAAK,OAAO,MAAM,GAC/C,eACE,QACA,WACA,KAAA,CACF;EAIF,OAAO,YACL,SACC,QAAQ,cAAc,cAAc,WAAW,QAAQ,CAC1D;CACF;CAEA,OAAO;AACT;AAEA,SAAgB,sBACd,QACA,UAC4B;CAC5B,MAAM,SAAS,UAAU,OAAO,cAAc,KAAA,CAAS;CAEvD,OAAO,YAAY,SAAS,QAAQ,cAClC,cAAc,WAAW,QAAQ,CACnC;AACF;;;;AAKA,SAAS,cACP,WACA,UACwB;CACxB,OAAO,OAAO,YACZ,WAAW,KAAI,SAAQ,CACrB,OACC,UAAmB,SAAS,WAAW,MAAM,KAAK,CACrD,CAAC,CACH;AACF"}
@@ -188,6 +188,6 @@ function applyPlansToAST(ast, plans) {
188
188
  };
189
189
  }
190
190
  //#endregion
191
- export { planQuery };
191
+ export { applyPlansToAST, buildPlanGraph, planQuery };
192
192
 
193
193
  //# sourceMappingURL=planner-builder.js.map