@rocicorp/zero 1.11.0 → 1.11.1

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 (737) 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.js.map +1 -1
  121. package/out/shared/src/broadcast-channel.js +1 -1
  122. package/out/shared/src/btree-set.js +11 -8
  123. package/out/shared/src/btree-set.js.map +1 -1
  124. package/out/shared/src/cache.js.map +1 -1
  125. package/out/shared/src/config.js +1 -1
  126. package/out/shared/src/custom-key-map.js.map +1 -1
  127. package/out/shared/src/custom-key-set.js.map +1 -1
  128. package/out/shared/src/deep-clone.js +1 -1
  129. package/out/shared/src/document-visible.js.map +1 -1
  130. package/out/shared/src/dotenv.js.map +1 -1
  131. package/out/shared/src/error.js +1 -1
  132. package/out/shared/src/error.js.map +1 -1
  133. package/out/shared/src/hash.js +2 -2
  134. package/out/shared/src/hash.js.map +1 -1
  135. package/out/shared/src/iterables.js.map +1 -1
  136. package/out/shared/src/json-schema.js.map +1 -1
  137. package/out/shared/src/json.js.map +1 -1
  138. package/out/shared/src/logging-test-utils.js +1 -1
  139. package/out/shared/src/logging.js +1 -1
  140. package/out/shared/src/logging.js.map +1 -1
  141. package/out/shared/src/navigator.js +1 -1
  142. package/out/shared/src/options.js +9 -12
  143. package/out/shared/src/options.js.map +1 -1
  144. package/out/shared/src/promise-race.js.map +1 -1
  145. package/out/shared/src/queue.js.map +1 -1
  146. package/out/shared/src/record-proxy.js.map +1 -1
  147. package/out/shared/src/ring-buffer.js.map +1 -1
  148. package/out/shared/src/size-of-value.js +7 -10
  149. package/out/shared/src/size-of-value.js.map +1 -1
  150. package/out/shared/src/sorted-entries.js.map +1 -1
  151. package/out/shared/src/tdigest-schema.js.map +1 -1
  152. package/out/shared/src/tdigest.js.map +1 -1
  153. package/out/shared/src/valita.js +1 -1
  154. package/out/shared/src/valita.js.map +1 -1
  155. package/out/z2s/src/compiler.js +2 -4
  156. package/out/z2s/src/compiler.js.map +1 -1
  157. package/out/z2s/src/sql.js +1 -1
  158. package/out/z2s/src/sql.js.map +1 -1
  159. package/out/zero/package.js +212 -189
  160. package/out/zero/package.js.map +1 -1
  161. package/out/zero/src/adapters/drizzle.js +1 -2
  162. package/out/zero/src/adapters/kysely.js +1 -2
  163. package/out/zero/src/adapters/pg.js +1 -2
  164. package/out/zero/src/adapters/postgresjs.js +1 -2
  165. package/out/zero/src/adapters/prisma.js +1 -2
  166. package/out/zero/src/analyze.js +1 -2
  167. package/out/zero/src/change-protocol/v0.js +1 -2
  168. package/out/zero/src/expo-sqlite.js +1 -2
  169. package/out/zero/src/op-sqlite.js +1 -2
  170. package/out/zero/src/react-native.js.map +1 -1
  171. package/out/zero/src/sqlite.js +1 -2
  172. package/out/zero/src/zero-cache-dev.js +10 -9
  173. package/out/zero/src/zero-cache-dev.js.map +1 -1
  174. package/out/zero/src/zero-out.js +2 -1
  175. package/out/zero/src/zero-out.js.map +1 -1
  176. package/out/zero-cache/src/auth/auth.js +1 -1
  177. package/out/zero-cache/src/auth/auth.js.map +1 -1
  178. package/out/zero-cache/src/auth/jwt.js +4 -1
  179. package/out/zero-cache/src/auth/jwt.js.map +1 -1
  180. package/out/zero-cache/src/auth/load-permissions.js.map +1 -1
  181. package/out/zero-cache/src/auth/read-authorizer.js +3 -2
  182. package/out/zero-cache/src/auth/read-authorizer.js.map +1 -1
  183. package/out/zero-cache/src/auth/write-authorizer.js +5 -16
  184. package/out/zero-cache/src/auth/write-authorizer.js.map +1 -1
  185. package/out/zero-cache/src/config/network.js +1 -1
  186. package/out/zero-cache/src/config/network.js.map +1 -1
  187. package/out/zero-cache/src/config/server-context.js.map +1 -1
  188. package/out/zero-cache/src/config/zero-config.js +2 -2
  189. package/out/zero-cache/src/config/zero-config.js.map +1 -1
  190. package/out/zero-cache/src/custom/fetch.js +1 -1
  191. package/out/zero-cache/src/custom/fetch.js.map +1 -1
  192. package/out/zero-cache/src/custom-queries/transform-query.js.map +1 -1
  193. package/out/zero-cache/src/db/create.js.map +1 -1
  194. package/out/zero-cache/src/db/index-predicate.js.map +1 -1
  195. package/out/zero-cache/src/db/lite-tables.js.map +1 -1
  196. package/out/zero-cache/src/db/migration-lite.js +1 -1
  197. package/out/zero-cache/src/db/migration-lite.js.map +1 -1
  198. package/out/zero-cache/src/db/migration.js +5 -3
  199. package/out/zero-cache/src/db/migration.js.map +1 -1
  200. package/out/zero-cache/src/db/pg-copy-binary.js +4 -3
  201. package/out/zero-cache/src/db/pg-copy-binary.js.map +1 -1
  202. package/out/zero-cache/src/db/pg-copy.js +0 -1
  203. package/out/zero-cache/src/db/pg-copy.js.map +1 -1
  204. package/out/zero-cache/src/db/pg-to-lite.js +1 -1
  205. package/out/zero-cache/src/db/pg-to-lite.js.map +1 -1
  206. package/out/zero-cache/src/db/run-transaction.js.map +1 -1
  207. package/out/zero-cache/src/db/specs.js +7 -5
  208. package/out/zero-cache/src/db/specs.js.map +1 -1
  209. package/out/zero-cache/src/db/sqlite-corruption.js.map +1 -1
  210. package/out/zero-cache/src/db/transaction-pool.js +5 -3
  211. package/out/zero-cache/src/db/transaction-pool.js.map +1 -1
  212. package/out/zero-cache/src/db/warmup.js.map +1 -1
  213. package/out/zero-cache/src/observability/events.js.map +1 -1
  214. package/out/zero-cache/src/observability/log-throttle.js.map +1 -1
  215. package/out/zero-cache/src/observability/metrics.js.map +1 -1
  216. package/out/zero-cache/src/scripts/deploy-permissions.js +4 -2
  217. package/out/zero-cache/src/scripts/deploy-permissions.js.map +1 -1
  218. package/out/zero-cache/src/scripts/permissions.js.map +1 -1
  219. package/out/zero-cache/src/server/anonymous-otel-start.js +2 -1
  220. package/out/zero-cache/src/server/anonymous-otel-start.js.map +1 -1
  221. package/out/zero-cache/src/server/change-streamer.js.map +1 -1
  222. package/out/zero-cache/src/server/inspector-delegate.js.map +1 -1
  223. package/out/zero-cache/src/server/logging.js +1 -1
  224. package/out/zero-cache/src/server/logging.js.map +1 -1
  225. package/out/zero-cache/src/server/main.js.map +1 -1
  226. package/out/zero-cache/src/server/otel-log-sink.js.map +1 -1
  227. package/out/zero-cache/src/server/otel-start.js.map +1 -1
  228. package/out/zero-cache/src/server/reaper.js +2 -2
  229. package/out/zero-cache/src/server/reaper.js.map +1 -1
  230. package/out/zero-cache/src/server/replicator.js.map +1 -1
  231. package/out/zero-cache/src/server/runner/run-worker.js +2 -1
  232. package/out/zero-cache/src/server/runner/run-worker.js.map +1 -1
  233. package/out/zero-cache/src/server/runner/runtime.js +2 -1
  234. package/out/zero-cache/src/server/runner/runtime.js.map +1 -1
  235. package/out/zero-cache/src/server/runner/zero-dispatcher.js.map +1 -1
  236. package/out/zero-cache/src/server/shadow-syncer.js +1 -1
  237. package/out/zero-cache/src/server/shadow-syncer.js.map +1 -1
  238. package/out/zero-cache/src/server/slot-keeper.js.map +1 -1
  239. package/out/zero-cache/src/server/syncer-assigner.js.map +1 -1
  240. package/out/zero-cache/src/server/syncer.js.map +1 -1
  241. package/out/zero-cache/src/server/worker-dispatcher.js +1 -1
  242. package/out/zero-cache/src/server/worker-dispatcher.js.map +1 -1
  243. package/out/zero-cache/src/server/worker-urls.js.map +1 -1
  244. package/out/zero-cache/src/services/analyze.js +1 -1
  245. package/out/zero-cache/src/services/analyze.js.map +1 -1
  246. package/out/zero-cache/src/services/change-source/common/backfill-manager.js +1 -1
  247. package/out/zero-cache/src/services/change-source/common/backfill-manager.js.map +1 -1
  248. package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.js.map +1 -1
  249. package/out/zero-cache/src/services/change-source/common/init-cleanup.js.map +1 -1
  250. package/out/zero-cache/src/services/change-source/common/replica-restore.js.map +1 -1
  251. package/out/zero-cache/src/services/change-source/common/replica-schema.js +3 -3
  252. package/out/zero-cache/src/services/change-source/common/replica-schema.js.map +1 -1
  253. package/out/zero-cache/src/services/change-source/custom/change-source.js +1 -1
  254. package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
  255. package/out/zero-cache/src/services/change-source/pg/backfill-metadata.js.map +1 -1
  256. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js +3 -3
  257. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js.map +1 -1
  258. package/out/zero-cache/src/services/change-source/pg/change-source-init.js +1 -1
  259. package/out/zero-cache/src/services/change-source/pg/change-source-init.js.map +1 -1
  260. package/out/zero-cache/src/services/change-source/pg/change-source.js +4 -6
  261. package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
  262. package/out/zero-cache/src/services/change-source/pg/decommission.js +2 -1
  263. package/out/zero-cache/src/services/change-source/pg/decommission.js.map +1 -1
  264. package/out/zero-cache/src/services/change-source/pg/initial-sync.js +5 -3
  265. package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
  266. package/out/zero-cache/src/services/change-source/pg/logical-replication/binary-reader.js.map +1 -1
  267. package/out/zero-cache/src/services/change-source/pg/logical-replication/pgoutput-parser.js.map +1 -1
  268. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js +3 -2
  269. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js.map +1 -1
  270. package/out/zero-cache/src/services/change-source/pg/lsn.js.map +1 -1
  271. package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.js +2 -2
  272. package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.js.map +1 -1
  273. package/out/zero-cache/src/services/change-source/pg/replication-slot-health.js +1 -1
  274. package/out/zero-cache/src/services/change-source/pg/replication-slot-health.js.map +1 -1
  275. package/out/zero-cache/src/services/change-source/pg/replication-slots.js +3 -3
  276. package/out/zero-cache/src/services/change-source/pg/replication-slots.js.map +1 -1
  277. package/out/zero-cache/src/services/change-source/pg/schema/ddl.js +1 -1
  278. package/out/zero-cache/src/services/change-source/pg/schema/ddl.js.map +1 -1
  279. package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.js +13 -12
  280. package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.js.map +1 -1
  281. package/out/zero-cache/src/services/change-source/pg/schema/init.js.map +1 -1
  282. package/out/zero-cache/src/services/change-source/pg/schema/published.js +1 -1
  283. package/out/zero-cache/src/services/change-source/pg/schema/published.js.map +1 -1
  284. package/out/zero-cache/src/services/change-source/pg/schema/replica-stage-enum.js.map +1 -1
  285. package/out/zero-cache/src/services/change-source/pg/schema/shard.js +9 -6
  286. package/out/zero-cache/src/services/change-source/pg/schema/shard.js.map +1 -1
  287. package/out/zero-cache/src/services/change-source/pg/schema/validation.js +1 -1
  288. package/out/zero-cache/src/services/change-source/pg/schema/validation.js.map +1 -1
  289. package/out/zero-cache/src/services/change-source/protocol/backfill-progress.js +1 -1
  290. package/out/zero-cache/src/services/change-source/protocol/backfill-progress.js.map +1 -1
  291. package/out/zero-cache/src/services/change-source/protocol/current/control.js.map +1 -1
  292. package/out/zero-cache/src/services/change-source/protocol/current/data.js.map +1 -1
  293. package/out/zero-cache/src/services/change-source/protocol/current/downstream.js.map +1 -1
  294. package/out/zero-cache/src/services/change-source/protocol/current/status.js.map +1 -1
  295. package/out/zero-cache/src/services/change-source/protocol/current/upstream.js.map +1 -1
  296. package/out/zero-cache/src/services/change-source/protocol/current.js +1 -1
  297. package/out/zero-cache/src/services/change-source/protocol/mod.js +1 -2
  298. package/out/zero-cache/src/services/change-streamer/backfill-state-tracker.js.map +1 -1
  299. package/out/zero-cache/src/services/change-streamer/backfill-state.js +1 -1
  300. package/out/zero-cache/src/services/change-streamer/backfill-state.js.map +1 -1
  301. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.js.map +1 -1
  302. package/out/zero-cache/src/services/change-streamer/backup-monitor.js.map +1 -1
  303. package/out/zero-cache/src/services/change-streamer/broadcast.js +1 -1
  304. package/out/zero-cache/src/services/change-streamer/broadcast.js.map +1 -1
  305. package/out/zero-cache/src/services/change-streamer/change-log-initializer.js +2 -2
  306. package/out/zero-cache/src/services/change-streamer/change-log-initializer.js.map +1 -1
  307. package/out/zero-cache/src/services/change-streamer/change-log-transaction-hash.js.map +1 -1
  308. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +4 -2
  309. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
  310. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +21 -21
  311. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
  312. package/out/zero-cache/src/services/change-streamer/change-streamer.js +1 -1
  313. package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
  314. package/out/zero-cache/src/services/change-streamer/drain-tracker.js.map +1 -1
  315. package/out/zero-cache/src/services/change-streamer/forwarder.js +11 -10
  316. package/out/zero-cache/src/services/change-streamer/forwarder.js.map +1 -1
  317. package/out/zero-cache/src/services/change-streamer/litestream-sync-requester.js +1 -1
  318. package/out/zero-cache/src/services/change-streamer/litestream-sync-requester.js.map +1 -1
  319. package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.js +3 -3
  320. package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.js.map +1 -1
  321. package/out/zero-cache/src/services/change-streamer/replica-poller.js +1 -1
  322. package/out/zero-cache/src/services/change-streamer/replica-poller.js.map +1 -1
  323. package/out/zero-cache/src/services/change-streamer/restore-admission.js +12 -1
  324. package/out/zero-cache/src/services/change-streamer/restore-admission.js.map +1 -1
  325. package/out/zero-cache/src/services/change-streamer/schema/init.js +1 -1
  326. package/out/zero-cache/src/services/change-streamer/schema/init.js.map +1 -1
  327. package/out/zero-cache/src/services/change-streamer/schema/tables.js +1 -1
  328. package/out/zero-cache/src/services/change-streamer/schema/tables.js.map +1 -1
  329. package/out/zero-cache/src/services/change-streamer/snapshot-message.js +1 -1
  330. package/out/zero-cache/src/services/change-streamer/snapshot-message.js.map +1 -1
  331. package/out/zero-cache/src/services/change-streamer/snapshot-reservations.js.map +1 -1
  332. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.js +1 -1
  333. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.js.map +1 -1
  334. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-comparator.js +1 -1
  335. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-comparator.js.map +1 -1
  336. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-purge-scheduler.js.map +1 -1
  337. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-read-router.js.map +1 -1
  338. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.js +1 -1
  339. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.js.map +1 -1
  340. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.js +1 -3
  341. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.js.map +1 -1
  342. package/out/zero-cache/src/services/change-streamer/storer.js +1 -1
  343. package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
  344. package/out/zero-cache/src/services/change-streamer/subscribe.js +1 -1
  345. package/out/zero-cache/src/services/change-streamer/subscribe.js.map +1 -1
  346. package/out/zero-cache/src/services/change-streamer/subscriber.js +2 -2
  347. package/out/zero-cache/src/services/change-streamer/subscriber.js.map +1 -1
  348. package/out/zero-cache/src/services/change-streamer/upstream-acker.js +0 -1
  349. package/out/zero-cache/src/services/change-streamer/upstream-acker.js.map +1 -1
  350. package/out/zero-cache/src/services/heapz.js +2 -1
  351. package/out/zero-cache/src/services/heapz.js.map +1 -1
  352. package/out/zero-cache/src/services/http-service.js.map +1 -1
  353. package/out/zero-cache/src/services/life-cycle.js +1 -1
  354. package/out/zero-cache/src/services/life-cycle.js.map +1 -1
  355. package/out/zero-cache/src/services/limiter/sliding-window-limiter.js +4 -2
  356. package/out/zero-cache/src/services/limiter/sliding-window-limiter.js.map +1 -1
  357. package/out/zero-cache/src/services/litestream/commands.js +1 -1
  358. package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
  359. package/out/zero-cache/src/services/litestream/litestream-checkpointer.js +2 -2
  360. package/out/zero-cache/src/services/litestream/litestream-checkpointer.js.map +1 -1
  361. package/out/zero-cache/src/services/litestream/litestream-controller.js.map +1 -1
  362. package/out/zero-cache/src/services/litestream/metrics.js.map +1 -1
  363. package/out/zero-cache/src/services/litestream/restore-progress.js.map +1 -1
  364. package/out/zero-cache/src/services/mutagen/mutagen.js +1 -1
  365. package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
  366. package/out/zero-cache/src/services/mutagen/pusher.js +3 -2
  367. package/out/zero-cache/src/services/mutagen/pusher.js.map +1 -1
  368. package/out/zero-cache/src/services/plannerz.js +4 -3
  369. package/out/zero-cache/src/services/plannerz.js.map +1 -1
  370. package/out/zero-cache/src/services/profz.js +5 -3
  371. package/out/zero-cache/src/services/profz.js.map +1 -1
  372. package/out/zero-cache/src/services/replicator/change-log-cookies.js.map +1 -1
  373. package/out/zero-cache/src/services/replicator/change-log-db.js +3 -2
  374. package/out/zero-cache/src/services/replicator/change-log-db.js.map +1 -1
  375. package/out/zero-cache/src/services/replicator/change-log-stream-writer.js +1 -1
  376. package/out/zero-cache/src/services/replicator/change-log-stream-writer.js.map +1 -1
  377. package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
  378. package/out/zero-cache/src/services/replicator/drain-handler.js.map +1 -1
  379. package/out/zero-cache/src/services/replicator/incremental-sync.js +1 -1
  380. package/out/zero-cache/src/services/replicator/incremental-sync.js.map +1 -1
  381. package/out/zero-cache/src/services/replicator/notifier.js.map +1 -1
  382. package/out/zero-cache/src/services/replicator/replication-status.js +4 -3
  383. package/out/zero-cache/src/services/replicator/replication-status.js.map +1 -1
  384. package/out/zero-cache/src/services/replicator/replicator.js.map +1 -1
  385. package/out/zero-cache/src/services/replicator/reporter/recorder.js +1 -1
  386. package/out/zero-cache/src/services/replicator/reporter/recorder.js.map +1 -1
  387. package/out/zero-cache/src/services/replicator/reporter/report-schema.js +1 -1
  388. package/out/zero-cache/src/services/replicator/reporter/report-schema.js.map +1 -1
  389. package/out/zero-cache/src/services/replicator/schema/backfilling.js +1 -1
  390. package/out/zero-cache/src/services/replicator/schema/backfilling.js.map +1 -1
  391. package/out/zero-cache/src/services/replicator/schema/change-log.js.map +1 -1
  392. package/out/zero-cache/src/services/replicator/schema/column-metadata.js +1 -1
  393. package/out/zero-cache/src/services/replicator/schema/column-metadata.js.map +1 -1
  394. package/out/zero-cache/src/services/replicator/schema/replication-state.js +10 -7
  395. package/out/zero-cache/src/services/replicator/schema/replication-state.js.map +1 -1
  396. package/out/zero-cache/src/services/replicator/schema/table-metadata.js.map +1 -1
  397. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js +1 -1
  398. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js.map +1 -1
  399. package/out/zero-cache/src/services/replicator/sqlite-change-log-purger.js +1 -1
  400. package/out/zero-cache/src/services/replicator/sqlite-change-log-purger.js.map +1 -1
  401. package/out/zero-cache/src/services/replicator/write-worker-client.js +1 -1
  402. package/out/zero-cache/src/services/replicator/write-worker-client.js.map +1 -1
  403. package/out/zero-cache/src/services/replicator/write-worker.js.map +1 -1
  404. package/out/zero-cache/src/services/run-ast.js.map +1 -1
  405. package/out/zero-cache/src/services/runner.js.map +1 -1
  406. package/out/zero-cache/src/services/running-state.js.map +1 -1
  407. package/out/zero-cache/src/services/shadow-sync/shadow-sync-service.js.map +1 -1
  408. package/out/zero-cache/src/services/statz.js +2 -1
  409. package/out/zero-cache/src/services/statz.js.map +1 -1
  410. package/out/zero-cache/src/services/view-syncer/active-users-gauge.js +2 -2
  411. package/out/zero-cache/src/services/view-syncer/active-users-gauge.js.map +1 -1
  412. package/out/zero-cache/src/services/view-syncer/advancement-metrics.js.map +1 -1
  413. package/out/zero-cache/src/services/view-syncer/batch-pacer.js.map +1 -1
  414. package/out/zero-cache/src/services/view-syncer/client-handler.js +4 -2
  415. package/out/zero-cache/src/services/view-syncer/client-handler.js.map +1 -1
  416. package/out/zero-cache/src/services/view-syncer/client-schema.js.map +1 -1
  417. package/out/zero-cache/src/services/view-syncer/connection-context-manager.js.map +1 -1
  418. package/out/zero-cache/src/services/view-syncer/cvr-purger.js +2 -2
  419. package/out/zero-cache/src/services/view-syncer/cvr-purger.js.map +1 -1
  420. package/out/zero-cache/src/services/view-syncer/cvr-store.js +9 -5
  421. package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
  422. package/out/zero-cache/src/services/view-syncer/cvr.js +3 -2
  423. package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
  424. package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.js +1 -1
  425. package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.js.map +1 -1
  426. package/out/zero-cache/src/services/view-syncer/drain-coordinator.js.map +1 -1
  427. package/out/zero-cache/src/services/view-syncer/e2e-serving-lag.js.map +1 -1
  428. package/out/zero-cache/src/services/view-syncer/hydration-budget.js.map +1 -1
  429. package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.js +2 -2
  430. package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.js.map +1 -1
  431. package/out/zero-cache/src/services/view-syncer/inspect-handler.js +5 -3
  432. package/out/zero-cache/src/services/view-syncer/inspect-handler.js.map +1 -1
  433. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +6 -4
  434. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
  435. package/out/zero-cache/src/services/view-syncer/plan-warnings.js.map +1 -1
  436. package/out/zero-cache/src/services/view-syncer/push-cost.js.map +1 -1
  437. package/out/zero-cache/src/services/view-syncer/push-cutoff.js.map +1 -1
  438. package/out/zero-cache/src/services/view-syncer/query-covering.js.map +1 -1
  439. package/out/zero-cache/src/services/view-syncer/query-shape.js.map +1 -1
  440. package/out/zero-cache/src/services/view-syncer/query-stats.js +1 -1
  441. package/out/zero-cache/src/services/view-syncer/query-stats.js.map +1 -1
  442. package/out/zero-cache/src/services/view-syncer/row-record-cache.js.map +1 -1
  443. package/out/zero-cache/src/services/view-syncer/schema/cvr.js +1 -1
  444. package/out/zero-cache/src/services/view-syncer/schema/types.js +1 -1
  445. package/out/zero-cache/src/services/view-syncer/schema/types.js.map +1 -1
  446. package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.js +7 -1
  447. package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.js.map +1 -1
  448. package/out/zero-cache/src/services/view-syncer/snapshotter.js +3 -2
  449. package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
  450. package/out/zero-cache/src/services/view-syncer/ttl-clock.js.map +1 -1
  451. package/out/zero-cache/src/services/view-syncer/view-syncer.js +4 -6
  452. package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
  453. package/out/zero-cache/src/types/error-with-level.js.map +1 -1
  454. package/out/zero-cache/src/types/lexi-version.js.map +1 -1
  455. package/out/zero-cache/src/types/lite.js +1 -1
  456. package/out/zero-cache/src/types/lite.js.map +1 -1
  457. package/out/zero-cache/src/types/pg-data-type.js +1 -1
  458. package/out/zero-cache/src/types/pg-data-type.js.map +1 -1
  459. package/out/zero-cache/src/types/pg.js +6 -4
  460. package/out/zero-cache/src/types/pg.js.map +1 -1
  461. package/out/zero-cache/src/types/poke-chunk.js +2 -2
  462. package/out/zero-cache/src/types/poke-chunk.js.map +1 -1
  463. package/out/zero-cache/src/types/processes.js +1 -1
  464. package/out/zero-cache/src/types/processes.js.map +1 -1
  465. package/out/zero-cache/src/types/profiler.js.map +1 -1
  466. package/out/zero-cache/src/types/row-key.js.map +1 -1
  467. package/out/zero-cache/src/types/sql.js.map +1 -1
  468. package/out/zero-cache/src/types/streams.js +10 -9
  469. package/out/zero-cache/src/types/streams.js.map +1 -1
  470. package/out/zero-cache/src/types/strings.js.map +1 -1
  471. package/out/zero-cache/src/types/subscription.js.map +1 -1
  472. package/out/zero-cache/src/types/timeout.js +1 -1
  473. package/out/zero-cache/src/types/websocket-handoff.js +1 -1
  474. package/out/zero-cache/src/types/ws.js +1 -1
  475. package/out/zero-cache/src/workers/connection.js +2 -3
  476. package/out/zero-cache/src/workers/connection.js.map +1 -1
  477. package/out/zero-cache/src/workers/mutator.js.map +1 -1
  478. package/out/zero-cache/src/workers/replicator.js +5 -4
  479. package/out/zero-cache/src/workers/replicator.js.map +1 -1
  480. package/out/zero-cache/src/workers/syncer-ws-message-handler.js.map +1 -1
  481. package/out/zero-cache/src/workers/syncer.js +2 -2
  482. package/out/zero-cache/src/workers/syncer.js.map +1 -1
  483. package/out/zero-client/src/client/active-clients-manager.js +1 -1
  484. package/out/zero-client/src/client/active-clients-manager.js.map +1 -1
  485. package/out/zero-client/src/client/bindings.js +11 -11
  486. package/out/zero-client/src/client/connection-manager.js.map +1 -1
  487. package/out/zero-client/src/client/connection.js +1 -1
  488. package/out/zero-client/src/client/connection.js.map +1 -1
  489. package/out/zero-client/src/client/context.js.map +1 -1
  490. package/out/zero-client/src/client/crud-impl.js +1 -1
  491. package/out/zero-client/src/client/crud-impl.js.map +1 -1
  492. package/out/zero-client/src/client/crud.js +1 -1
  493. package/out/zero-client/src/client/crud.js.map +1 -1
  494. package/out/zero-client/src/client/custom.js +1 -1
  495. package/out/zero-client/src/client/custom.js.map +1 -1
  496. package/out/zero-client/src/client/delete-clients-manager.js.map +1 -1
  497. package/out/zero-client/src/client/enable-analytics.js +1 -1
  498. package/out/zero-client/src/client/enable-analytics.js.map +1 -1
  499. package/out/zero-client/src/client/error.js.map +1 -1
  500. package/out/zero-client/src/client/inspector/client-group.js.map +1 -1
  501. package/out/zero-client/src/client/inspector/client.js.map +1 -1
  502. package/out/zero-client/src/client/inspector/html-dialog-prompt.js +1 -1
  503. package/out/zero-client/src/client/inspector/html-dialog-prompt.js.map +1 -1
  504. package/out/zero-client/src/client/inspector/inspector.js.map +1 -1
  505. package/out/zero-client/src/client/inspector/lazy-inspector.js.map +1 -1
  506. package/out/zero-client/src/client/inspector/query.js.map +1 -1
  507. package/out/zero-client/src/client/ivm-branch.js +1 -3
  508. package/out/zero-client/src/client/ivm-branch.js.map +1 -1
  509. package/out/zero-client/src/client/keys.js +2 -1
  510. package/out/zero-client/src/client/keys.js.map +1 -1
  511. package/out/zero-client/src/client/log-options.js.map +1 -1
  512. package/out/zero-client/src/client/make-replicache-mutators.js +5 -3
  513. package/out/zero-client/src/client/make-replicache-mutators.js.map +1 -1
  514. package/out/zero-client/src/client/metrics.js +3 -5
  515. package/out/zero-client/src/client/metrics.js.map +1 -1
  516. package/out/zero-client/src/client/mutation-tracker.js.map +1 -1
  517. package/out/zero-client/src/client/mutator-proxy.js.map +1 -1
  518. package/out/zero-client/src/client/options.js.map +1 -1
  519. package/out/zero-client/src/client/query-manager.js +2 -2
  520. package/out/zero-client/src/client/query-manager.js.map +1 -1
  521. package/out/zero-client/src/client/reload-error-handler.js +4 -3
  522. package/out/zero-client/src/client/reload-error-handler.js.map +1 -1
  523. package/out/zero-client/src/client/version.js +1 -1
  524. package/out/zero-client/src/client/zero-poke-handler.js +1 -1
  525. package/out/zero-client/src/client/zero-poke-handler.js.map +1 -1
  526. package/out/zero-client/src/client/zero-rep.js.map +1 -1
  527. package/out/zero-client/src/client/zero.js +19 -11
  528. package/out/zero-client/src/client/zero.js.map +1 -1
  529. package/out/zero-client/src/mod.js +23 -23
  530. package/out/zero-client/src/util/nanoid.js.map +1 -1
  531. package/out/zero-permissions/src/permissions.js +3 -2
  532. package/out/zero-permissions/src/permissions.js.map +1 -1
  533. package/out/zero-pg/src/mod.js +9 -9
  534. package/out/zero-protocol/src/analyze-query-result.js +1 -1
  535. package/out/zero-protocol/src/analyze-query-result.js.map +1 -1
  536. package/out/zero-protocol/src/application-error.js.map +1 -1
  537. package/out/zero-protocol/src/ast.js +19 -9
  538. package/out/zero-protocol/src/ast.js.map +1 -1
  539. package/out/zero-protocol/src/change-desired-queries.js.map +1 -1
  540. package/out/zero-protocol/src/client-schema.js +1 -1
  541. package/out/zero-protocol/src/client-schema.js.map +1 -1
  542. package/out/zero-protocol/src/close-connection.js +1 -1
  543. package/out/zero-protocol/src/close-connection.js.map +1 -1
  544. package/out/zero-protocol/src/connect.js +1 -1
  545. package/out/zero-protocol/src/connect.js.map +1 -1
  546. package/out/zero-protocol/src/custom-queries.js +1 -1
  547. package/out/zero-protocol/src/custom-queries.js.map +1 -1
  548. package/out/zero-protocol/src/data.js +4 -2
  549. package/out/zero-protocol/src/data.js.map +1 -1
  550. package/out/zero-protocol/src/delete-clients.js.map +1 -1
  551. package/out/zero-protocol/src/down.js.map +1 -1
  552. package/out/zero-protocol/src/error.js.map +1 -1
  553. package/out/zero-protocol/src/inspect-down.d.ts +81 -81
  554. package/out/zero-protocol/src/inspect-down.js +1 -1
  555. package/out/zero-protocol/src/inspect-down.js.map +1 -1
  556. package/out/zero-protocol/src/inspect-up.js +1 -1
  557. package/out/zero-protocol/src/inspect-up.js.map +1 -1
  558. package/out/zero-protocol/src/mutate-server.js +1 -1
  559. package/out/zero-protocol/src/mutate-server.js.map +1 -1
  560. package/out/zero-protocol/src/mutation-id.js.map +1 -1
  561. package/out/zero-protocol/src/mutation.js +1 -1
  562. package/out/zero-protocol/src/mutation.js.map +1 -1
  563. package/out/zero-protocol/src/mutations-patch.js +1 -1
  564. package/out/zero-protocol/src/mutations-patch.js.map +1 -1
  565. package/out/zero-protocol/src/ping.js +1 -1
  566. package/out/zero-protocol/src/ping.js.map +1 -1
  567. package/out/zero-protocol/src/poke.js +1 -1
  568. package/out/zero-protocol/src/poke.js.map +1 -1
  569. package/out/zero-protocol/src/pong.js +1 -1
  570. package/out/zero-protocol/src/pong.js.map +1 -1
  571. package/out/zero-protocol/src/primary-key.js.map +1 -1
  572. package/out/zero-protocol/src/protocol-version.js +1 -0
  573. package/out/zero-protocol/src/protocol-version.js.map +1 -1
  574. package/out/zero-protocol/src/pull.js +1 -1
  575. package/out/zero-protocol/src/pull.js.map +1 -1
  576. package/out/zero-protocol/src/push.js +1 -1
  577. package/out/zero-protocol/src/push.js.map +1 -1
  578. package/out/zero-protocol/src/queries-patch.js +1 -1
  579. package/out/zero-protocol/src/queries-patch.js.map +1 -1
  580. package/out/zero-protocol/src/query-hash-visitor.js.map +1 -1
  581. package/out/zero-protocol/src/query-hash.js +2 -2
  582. package/out/zero-protocol/src/query-server.js +1 -1
  583. package/out/zero-protocol/src/query-server.js.map +1 -1
  584. package/out/zero-protocol/src/row-patch.js.map +1 -1
  585. package/out/zero-protocol/src/up.js.map +1 -1
  586. package/out/zero-protocol/src/update-auth.js.map +1 -1
  587. package/out/zero-protocol/src/version.js.map +1 -1
  588. package/out/zero-react/src/bindings.js +11 -11
  589. package/out/zero-react/src/mod.js +5 -5
  590. package/out/zero-react/src/use-query.js +1 -1
  591. package/out/zero-react/src/use-query.js.map +1 -1
  592. package/out/zero-react/src/zero-provider.d.ts +1 -1
  593. package/out/zero-react/src/zero-provider.d.ts.map +1 -1
  594. package/out/zero-react/src/zero-provider.js +2 -1
  595. package/out/zero-react/src/zero-provider.js.map +1 -1
  596. package/out/zero-react/src/zero.js +23 -23
  597. package/out/zero-schema/src/builder/schema-builder.js +3 -2
  598. package/out/zero-schema/src/builder/schema-builder.js.map +1 -1
  599. package/out/zero-schema/src/builder/table-builder.js +1 -1
  600. package/out/zero-schema/src/builder/table-builder.js.map +1 -1
  601. package/out/zero-schema/src/compiled-permissions.js.map +1 -1
  602. package/out/zero-schema/src/name-mapper.js +7 -5
  603. package/out/zero-schema/src/name-mapper.js.map +1 -1
  604. package/out/zero-schema/src/schema-config.js +1 -1
  605. package/out/zero-schema/src/schema-config.js.map +1 -1
  606. package/out/zero-server/src/adapters/drizzle.js.map +1 -1
  607. package/out/zero-server/src/adapters/kysely.js.map +1 -1
  608. package/out/zero-server/src/adapters/pg.js.map +1 -1
  609. package/out/zero-server/src/adapters/postgresjs.js.map +1 -1
  610. package/out/zero-server/src/adapters/prisma.js.map +1 -1
  611. package/out/zero-server/src/closable-db-transaction.js.map +1 -1
  612. package/out/zero-server/src/custom.js +1 -1
  613. package/out/zero-server/src/custom.js.map +1 -1
  614. package/out/zero-server/src/mod.js +8 -8
  615. package/out/zero-server/src/process-mutations.js.map +1 -1
  616. package/out/zero-server/src/push-processor.js.map +1 -1
  617. package/out/zero-server/src/queries/process-queries.js +2 -1
  618. package/out/zero-server/src/queries/process-queries.js.map +1 -1
  619. package/out/zero-server/src/schema.js +7 -7
  620. package/out/zero-server/src/schema.js.map +1 -1
  621. package/out/zero-server/src/zql-database.js.map +1 -1
  622. package/out/zero-solid/src/bindings.js +11 -11
  623. package/out/zero-solid/src/mod.js +5 -5
  624. package/out/zero-solid/src/solid-view.js +1 -1
  625. package/out/zero-solid/src/solid-view.js.map +1 -1
  626. package/out/zero-solid/src/use-connection-state.js +2 -1
  627. package/out/zero-solid/src/use-connection-state.js.map +1 -1
  628. package/out/zero-solid/src/use-query.js.map +1 -1
  629. package/out/zero-solid/src/use-zero-online.js +2 -1
  630. package/out/zero-solid/src/use-zero-online.js.map +1 -1
  631. package/out/zero-solid/src/use-zero.js +3 -2
  632. package/out/zero-solid/src/use-zero.js.map +1 -1
  633. package/out/zero-solid/src/zero.js +23 -23
  634. package/out/zero-types/src/name-mapper.js.map +1 -1
  635. package/out/zql/src/builder/builder.js +15 -13
  636. package/out/zql/src/builder/builder.js.map +1 -1
  637. package/out/zql/src/builder/correlated-predicate-pushdown.js.map +1 -1
  638. package/out/zql/src/builder/debug-delegate.js.map +1 -1
  639. package/out/zql/src/builder/like.js +0 -1
  640. package/out/zql/src/builder/like.js.map +1 -1
  641. package/out/zql/src/ivm/array-view.js.map +1 -1
  642. package/out/zql/src/ivm/cap.js.map +1 -1
  643. package/out/zql/src/ivm/constraint.js +1 -1
  644. package/out/zql/src/ivm/constraint.js.map +1 -1
  645. package/out/zql/src/ivm/data.js.map +1 -1
  646. package/out/zql/src/ivm/deferred-input.js.map +1 -1
  647. package/out/zql/src/ivm/exists.js +20 -18
  648. package/out/zql/src/ivm/exists.js.map +1 -1
  649. package/out/zql/src/ivm/fan-in.js.map +1 -1
  650. package/out/zql/src/ivm/fan-out.js.map +1 -1
  651. package/out/zql/src/ivm/filter-operators.js +1 -1
  652. package/out/zql/src/ivm/filter-operators.js.map +1 -1
  653. package/out/zql/src/ivm/filter-push.js.map +1 -1
  654. package/out/zql/src/ivm/filter.js.map +1 -1
  655. package/out/zql/src/ivm/flipped-join.js +1 -3
  656. package/out/zql/src/ivm/flipped-join.js.map +1 -1
  657. package/out/zql/src/ivm/join-index-tap.js.map +1 -1
  658. package/out/zql/src/ivm/join-index.js.map +1 -1
  659. package/out/zql/src/ivm/join-utils.js +11 -13
  660. package/out/zql/src/ivm/join-utils.js.map +1 -1
  661. package/out/zql/src/ivm/join.js.map +1 -1
  662. package/out/zql/src/ivm/maybe-split-and-push-edit-change.js.map +1 -1
  663. package/out/zql/src/ivm/memory-source.js +26 -22
  664. package/out/zql/src/ivm/memory-source.js.map +1 -1
  665. package/out/zql/src/ivm/memory-storage.js.map +1 -1
  666. package/out/zql/src/ivm/operator.js +2 -2
  667. package/out/zql/src/ivm/push-accumulated.js +0 -1
  668. package/out/zql/src/ivm/push-accumulated.js.map +1 -1
  669. package/out/zql/src/ivm/skip-yields.js.map +1 -1
  670. package/out/zql/src/ivm/skip.js.map +1 -1
  671. package/out/zql/src/ivm/take-gate.js.map +1 -1
  672. package/out/zql/src/ivm/take.js +1 -1
  673. package/out/zql/src/ivm/take.js.map +1 -1
  674. package/out/zql/src/ivm/union-fan-in.js +1 -1
  675. package/out/zql/src/ivm/union-fan-in.js.map +1 -1
  676. package/out/zql/src/ivm/union-fan-out.js.map +1 -1
  677. package/out/zql/src/ivm/view-apply-change.js +1 -1
  678. package/out/zql/src/ivm/view-apply-change.js.map +1 -1
  679. package/out/zql/src/mutate/crud.js +3 -2
  680. package/out/zql/src/mutate/crud.js.map +1 -1
  681. package/out/zql/src/planner/planner-builder.js +1 -1
  682. package/out/zql/src/planner/planner-builder.js.map +1 -1
  683. package/out/zql/src/planner/planner-connection.js.map +1 -1
  684. package/out/zql/src/planner/planner-debug.js.map +1 -1
  685. package/out/zql/src/planner/planner-fan-in.js.map +1 -1
  686. package/out/zql/src/planner/planner-fan-out.js.map +1 -1
  687. package/out/zql/src/planner/planner-filter.js.map +1 -1
  688. package/out/zql/src/planner/planner-graph.js +1 -5
  689. package/out/zql/src/planner/planner-graph.js.map +1 -1
  690. package/out/zql/src/planner/planner-join.js +1 -1
  691. package/out/zql/src/planner/planner-join.js.map +1 -1
  692. package/out/zql/src/planner/planner-source.js.map +1 -1
  693. package/out/zql/src/planner/planner-terminus.js.map +1 -1
  694. package/out/zql/src/query/create-builder.js +2 -1
  695. package/out/zql/src/query/create-builder.js.map +1 -1
  696. package/out/zql/src/query/expression.js +1 -1
  697. package/out/zql/src/query/expression.js.map +1 -1
  698. package/out/zql/src/query/measure-push-operator.js.map +1 -1
  699. package/out/zql/src/query/named.js +2 -1
  700. package/out/zql/src/query/named.js.map +1 -1
  701. package/out/zql/src/query/query-delegate-base.js +1 -1
  702. package/out/zql/src/query/query-delegate-base.js.map +1 -1
  703. package/out/zql/src/query/query-impl.js +3 -2
  704. package/out/zql/src/query/query-impl.js.map +1 -1
  705. package/out/zql/src/query/query-registry.js +1 -1
  706. package/out/zql/src/query/query-registry.js.map +1 -1
  707. package/out/zql/src/query/query-transitions.js +1 -1
  708. package/out/zql/src/query/query-transitions.js.map +1 -1
  709. package/out/zql/src/query/runnable-query-impl.js +3 -2
  710. package/out/zql/src/query/runnable-query-impl.js.map +1 -1
  711. package/out/zql/src/query/static-query.js.map +1 -1
  712. package/out/zql/src/query/ttl.js +8 -8
  713. package/out/zql/src/query/ttl.js.map +1 -1
  714. package/out/zql/src/query/validate-input.js.map +1 -1
  715. package/out/zqlite/src/database-storage.js +2 -2
  716. package/out/zqlite/src/database-storage.js.map +1 -1
  717. package/out/zqlite/src/db.js +2 -2
  718. package/out/zqlite/src/db.js.map +1 -1
  719. package/out/zqlite/src/explain-queries.js.map +1 -1
  720. package/out/zqlite/src/index-usage.js.map +1 -1
  721. package/out/zqlite/src/internal/sql-inline.js.map +1 -1
  722. package/out/zqlite/src/internal/sql.js.map +1 -1
  723. package/out/zqlite/src/internal/statement-cache.js +1 -1
  724. package/out/zqlite/src/internal/statement-cache.js.map +1 -1
  725. package/out/zqlite/src/mod.js +5 -5
  726. package/out/zqlite/src/pending-delta.js.map +1 -1
  727. package/out/zqlite/src/query-builder.js +1 -1
  728. package/out/zqlite/src/query-builder.js.map +1 -1
  729. package/out/zqlite/src/query-delegate.js.map +1 -1
  730. package/out/zqlite/src/resolve-scalar-subqueries.js +2 -5
  731. package/out/zqlite/src/resolve-scalar-subqueries.js.map +1 -1
  732. package/out/zqlite/src/sqlite-cost-model.js +3 -2
  733. package/out/zqlite/src/sqlite-cost-model.js.map +1 -1
  734. package/out/zqlite/src/sqlite-stat-fanout.js.map +1 -1
  735. package/out/zqlite/src/table-source.js +1 -1
  736. package/out/zqlite/src/table-source.js.map +1 -1
  737. package/package.json +16 -14
@@ -1 +1 @@
1
- {"version":3,"file":"custom.js","names":["#dbTransaction","#schema","#serverSchema","#addRetryPredicate","#tableCRUDs"],"sources":["../../../../zero-server/src/custom.ts"],"sourcesContent":["import {assert} from '../../shared/src/asserts.ts';\nimport type {ReadonlyJSONValue} from '../../shared/src/json.ts';\nimport {mapValues} from '../../shared/src/objects.ts';\nimport {recordProxy} from '../../shared/src/record-proxy.ts';\nimport {\n formatPgInternalConvert,\n sql,\n sqlConvertColumnArg,\n} from '../../z2s/src/sql.ts';\nimport type {TableSchema} from '../../zero-schema/src/table-schema.ts';\nimport type {Schema} from '../../zero-types/src/schema.ts';\nimport type {\n ServerColumnSchema,\n ServerSchema,\n ServerTableSchema,\n} from '../../zero-types/src/server-schema.ts';\nimport {\n type CRUDExecutor,\n type CRUDKind,\n makeCRUDMutate,\n makeTransactionMutate,\n type SchemaCRUD,\n type TableCRUD,\n type TransactionMutate,\n} from '../../zql/src/mutate/crud.ts';\nimport type {\n DBTransaction,\n MutateCRUD,\n Queryable,\n RetryPredicate,\n ServerTransaction,\n} from '../../zql/src/mutate/custom.ts';\nimport {createRunnableBuilder} from '../../zql/src/query/create-builder.ts';\nimport {QueryDelegateBase} from '../../zql/src/query/query-delegate-base.ts';\nimport {asQueryInternals} from '../../zql/src/query/query-internals.ts';\nimport type {\n HumanReadable,\n Query,\n RunOptions,\n} from '../../zql/src/query/query.ts';\nimport type {ConditionalSchemaQuery} from '../../zql/src/query/schema-query.ts';\nimport {getServerSchema} from './schema.ts';\n\nexport type CustomMutatorDefs<TDBTransaction> = {\n [namespaceOrKey: string]:\n | CustomMutatorImpl<TDBTransaction>\n | CustomMutatorDefs<TDBTransaction>;\n};\n\nexport type CustomMutatorImpl<\n TDBTransaction,\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n TArgs = any,\n Context = unknown,\n> = (\n tx: TDBTransaction,\n args: TArgs,\n ctx: Context,\n) => Promise<ReadonlyJSONValue | void>;\n\n/**\n * QueryDelegate implementation for server-side transactions.\n * Extends QueryDelegateBase to satisfy the QueryDelegate interface,\n * but overrides run() to execute against Postgres and throws on\n * preload()/materialize() which don't make sense server-side.\n */\nclass ServerTransactionQueryDelegate extends QueryDelegateBase {\n readonly #dbTransaction: DBTransaction<unknown>;\n readonly #schema: Schema;\n readonly #serverSchema: ServerSchema;\n\n readonly defaultQueryComplete = true;\n\n constructor(\n dbTransaction: DBTransaction<unknown>,\n schema: Schema,\n serverSchema: ServerSchema,\n ) {\n super();\n this.#dbTransaction = dbTransaction;\n this.#schema = schema;\n this.#serverSchema = serverSchema;\n }\n\n getSource(): never {\n throw new Error('not implemented');\n }\n\n override run<\n TTable extends keyof TSchema['tables'] & string,\n TSchema extends Schema,\n TReturn,\n >(\n query: Query<TTable, TSchema, TReturn>,\n _options?: RunOptions,\n ): Promise<HumanReadable<TReturn>> {\n const queryInternals = asQueryInternals(query);\n return this.#dbTransaction.runQuery<TReturn>(\n queryInternals.ast,\n queryInternals.format,\n this.#schema,\n this.#serverSchema,\n );\n }\n\n override preload(): never {\n throw new Error('preload() is not supported in server transactions');\n }\n\n override materialize(): never {\n throw new Error('materialize() is not supported in server transactions');\n }\n}\n\nexport class TransactionImpl<\n TSchema extends Schema,\n TWrappedTransaction,\n> implements ServerTransaction<TSchema, TWrappedTransaction> {\n readonly location = 'server';\n readonly reason = 'authoritative';\n readonly dbTransaction: DBTransaction<TWrappedTransaction>;\n readonly clientID: string;\n readonly mutationID: number;\n readonly mutate: TransactionMutate<TSchema>;\n /**\n * @deprecated Use {@link createBuilder} with `tx.run(zql.table.where(...))` instead.\n */\n readonly query: ConditionalSchemaQuery<TSchema>;\n\n readonly #schema: TSchema;\n readonly #serverSchema: ServerSchema;\n readonly #addRetryPredicate:\n | ((shouldRetry: RetryPredicate) => void)\n | undefined;\n\n constructor(\n dbTransaction: DBTransaction<TWrappedTransaction>,\n clientID: string,\n mutationID: number,\n mutate: TransactionMutate<TSchema>,\n schema: TSchema,\n serverSchema: ServerSchema,\n addRetryPredicate?: ((shouldRetry: RetryPredicate) => void) | undefined,\n ) {\n this.dbTransaction = dbTransaction;\n this.clientID = clientID;\n this.mutationID = mutationID;\n this.mutate = mutate;\n this.#schema = schema;\n this.#serverSchema = serverSchema;\n this.#addRetryPredicate = addRetryPredicate;\n\n const delegate = new ServerTransactionQueryDelegate(\n dbTransaction,\n schema,\n serverSchema,\n );\n this.query = createRunnableBuilder(delegate, schema);\n }\n\n retryOn(shouldRetry: RetryPredicate): void {\n if (this.#addRetryPredicate === undefined) {\n // Fail loudly: a predicate nothing will consult would silently turn\n // every retryable failure into a recorded error.\n throw new Error(\n 'tx.retryOn() is not supported on this transaction: nothing will re-run its mutator. A custom Database must pass TransactionProviderInput.addRetryPredicate on to createTransaction.',\n );\n }\n this.#addRetryPredicate(shouldRetry);\n }\n\n run<TTable extends keyof TSchema['tables'] & string, TReturn>(\n query: Query<TTable, TSchema, TReturn>,\n _options?: RunOptions,\n ): Promise<HumanReadable<TReturn>> {\n const queryInternals = asQueryInternals(query);\n\n // Execute the query using the database-specific executor\n return this.dbTransaction.runQuery<TReturn>(\n queryInternals.ast,\n queryInternals.format,\n this.#schema,\n this.#serverSchema,\n );\n }\n}\n\nconst dbTxSymbol = Symbol();\n\nconst serverSchemaSymbol = Symbol();\n\ntype WithHiddenTxAndSchema = {\n [dbTxSymbol]: DBTransaction<unknown>;\n [serverSchemaSymbol]: ServerSchema;\n};\n\n/**\n * Factory for creating MutateCRUD instances efficiently.\n *\n * Pre-creates the SQL-generating TableCRUD methods once from the schema,\n * caches the serverSchema after first fetch, and only binds the transaction\n * at transaction time.\n *\n * Use this when you need to create many transactions and want to avoid\n * the overhead of re-creating CRUD methods for each one.\n */\nexport class CRUDMutatorFactory<S extends Schema> {\n readonly #schema: S;\n readonly #tableCRUDs: Record<string, TableCRUD<TableSchema>>;\n // The in-flight or resolved fetch is cached (not just the result) so that\n // concurrent cold callers share a single catalog query.\n #serverSchema: Promise<ServerSchema> | undefined;\n\n constructor(schema: S) {\n this.#schema = schema;\n // Pre-create TableCRUD methods for each table once\n this.#tableCRUDs = {};\n for (const tableSchema of Object.values(schema.tables)) {\n this.#tableCRUDs[tableSchema.name] = makeServerTableCRUD(tableSchema);\n }\n }\n\n /**\n * Gets the cached serverSchema, or fetches and caches it on first call.\n */\n getOrFetchServerSchema(queryable: Queryable): Promise<ServerSchema> {\n if (!this.#serverSchema) {\n this.#serverSchema = getServerSchema(queryable, this.#schema).catch(e => {\n // Don't poison the cache with a transient failure.\n this.#serverSchema = undefined;\n throw e;\n });\n }\n return this.#serverSchema;\n }\n\n /**\n * Creates a CRUDExecutor bound to the given transaction and serverSchema.\n * Uses the pre-created TableCRUD methods from construction time.\n */\n createExecutor(\n dbTransaction: DBTransaction<unknown>,\n serverSchema: ServerSchema,\n ): CRUDExecutor {\n const txHolder: WithHiddenTxAndSchema = {\n [dbTxSymbol]: dbTransaction,\n [serverSchemaSymbol]: serverSchema,\n };\n const boundCRUDs = recordProxy(this.#tableCRUDs, tableCRUD =>\n mapValues(tableCRUD, method => method.bind(txHolder)),\n ) as unknown as SchemaCRUD<S>;\n\n return (table: string, kind: CRUDKind, args: unknown) => {\n const tableCRUD = boundCRUDs[table as keyof S['tables']];\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n return (tableCRUD as any)[kind](args);\n };\n }\n\n /**\n * Creates a full ServerTransaction.\n * Fetches/caches serverSchema automatically.\n */\n async createTransaction<TWrappedTransaction>(\n dbTransaction: DBTransaction<TWrappedTransaction>,\n clientID: string,\n mutationID: number,\n addRetryPredicate?: ((shouldRetry: RetryPredicate) => void) | undefined,\n ): Promise<TransactionImpl<S, TWrappedTransaction>> {\n const serverSchema = await this.getOrFetchServerSchema(dbTransaction);\n const executor = this.createExecutor(dbTransaction, serverSchema);\n const mutate = makeTransactionMutate(this.#schema, executor);\n return new TransactionImpl(\n dbTransaction,\n clientID,\n mutationID,\n mutate,\n this.#schema,\n serverSchema,\n addRetryPredicate,\n );\n }\n}\n\nexport async function makeServerTransaction<\n TSchema extends Schema,\n TWrappedTransaction,\n>(\n dbTransaction: DBTransaction<TWrappedTransaction>,\n clientID: string,\n mutationID: number,\n schema: TSchema,\n) {\n const serverSchema = await getServerSchema(dbTransaction, schema);\n // Use the internal executor and shared function directly,\n // bypassing the validation in makeMutateCRUD/makeSchemaCRUD\n const executor = makeServerCRUDExecutor(schema, dbTransaction, serverSchema);\n const mutate = makeTransactionMutate(schema, executor);\n return new TransactionImpl(\n dbTransaction,\n clientID,\n mutationID,\n mutate,\n schema,\n serverSchema,\n );\n}\n\n/**\n * @deprecated Use transactions instead.\n *\n * Returns a curried function for backwards compatibility.\n */\nexport function makeSchemaCRUD<S extends Schema>(\n schema: S,\n): (\n dbTransaction: DBTransaction<unknown>,\n serverSchema: ServerSchema,\n) => MutateCRUD<S, true> {\n return (dbTransaction: DBTransaction<unknown>, serverSchema: ServerSchema) =>\n makeCRUDMutate(\n schema,\n true,\n makeServerCRUDExecutor(schema, dbTransaction, serverSchema),\n );\n}\n\nfunction removeUndefined<T extends Record<string, unknown>>(value: T): T {\n const valueWithoutUndefined: Record<string, unknown> = {};\n for (const [key, val] of Object.entries(value)) {\n if (val !== undefined) {\n valueWithoutUndefined[key] = val;\n }\n }\n return valueWithoutUndefined as T;\n}\n\n/**\n * Creates a CRUDExecutor for server-side SQL execution.\n *\n * For users with very large schemas it is expensive to re-create\n * all the CRUD mutators for each transaction. Instead, we create\n * the SQL-generating methods once up-front and then bind them to\n * the transaction as requested.\n */\nfunction makeServerCRUDExecutor<S extends Schema>(\n schema: S,\n dbTransaction: DBTransaction<unknown>,\n serverSchema: ServerSchema,\n): CRUDExecutor {\n // Pre-create TableCRUD methods for each table (optimization for large schemas)\n const tableCRUDs: Record<string, TableCRUD<TableSchema>> = {};\n for (const tableSchema of Object.values(schema.tables)) {\n tableCRUDs[tableSchema.name] = makeServerTableCRUD(tableSchema);\n }\n\n // Bind transaction context to the methods\n const txHolder: WithHiddenTxAndSchema = {\n [dbTxSymbol]: dbTransaction,\n [serverSchemaSymbol]: serverSchema,\n };\n const boundCRUDs = recordProxy(tableCRUDs, tableCRUD =>\n mapValues(tableCRUD, method => method.bind(txHolder)),\n ) as unknown as SchemaCRUD<S>;\n\n // Return executor that dispatches to bound methods\n return (table: string, kind: CRUDKind, args: unknown) => {\n const tableCRUD = boundCRUDs[table as keyof S['tables']];\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n return (tableCRUD as any)[kind](args);\n };\n}\n\n/**\n * Creates SQL-generating TableCRUD methods for a table.\n * Methods use `this` context to access transaction and server schema.\n */\nfunction makeServerTableCRUD(schema: TableSchema): TableCRUD<TableSchema> {\n return {\n async insert(this: WithHiddenTxAndSchema, value) {\n value = removeUndefined(value);\n const serverTableSchema = this[serverSchemaSymbol][serverName(schema)];\n\n const targetedColumns = origAndServerNamesFor(Object.keys(value), schema);\n const primaryKeyColumns = origAndServerNamesFor(\n schema.primaryKey,\n schema,\n );\n\n const stmt = formatPgInternalConvert(\n sql`INSERT INTO ${sql.ident(serverName(schema))} (${sql.join(\n targetedColumns.map(([, serverName]) => sql.ident(serverName)),\n ',',\n )}) VALUES (${sql.join(\n Object.entries(value).map(([col, v]) =>\n sqlInsertValue(v, serverTableSchema[serverNameFor(col, schema)]),\n ),\n ', ',\n )}) ON CONFLICT (${sql.join(\n primaryKeyColumns.map(([, serverName]) => sql.ident(serverName)),\n ', ',\n )}) DO NOTHING`,\n );\n const tx = this[dbTxSymbol];\n await tx.query(stmt.text, stmt.values);\n },\n async upsert(this: WithHiddenTxAndSchema, value) {\n value = removeUndefined(value);\n const serverTableSchema = this[serverSchemaSymbol][serverName(schema)];\n const targetedColumns = origAndServerNamesFor(Object.keys(value), schema);\n const primaryKeyColumns = origAndServerNamesFor(\n schema.primaryKey,\n schema,\n );\n const updatedEntries = nonPrimaryKeyEntries(value, schema);\n const conflictAction =\n updatedEntries.length === 0\n ? sql`DO NOTHING`\n : sql`DO UPDATE SET ${sql.join(\n updatedEntries.map(\n ([col, val]) =>\n sql`${sql.ident(\n schema.columns[col].serverName ?? col,\n )} = ${sqlInsertValue(val, serverTableSchema[serverNameFor(col, schema)])}`,\n ),\n ', ',\n )}`;\n const stmt = formatPgInternalConvert(\n sql`INSERT INTO ${sql.ident(serverName(schema))} (${sql.join(\n targetedColumns.map(([, serverName]) => sql.ident(serverName)),\n ',',\n )}) VALUES (${sql.join(\n Object.entries(value).map(([col, val]) =>\n sqlInsertValue(val, serverTableSchema[serverNameFor(col, schema)]),\n ),\n ', ',\n )}) ON CONFLICT (${sql.join(\n primaryKeyColumns.map(([, serverName]) => sql.ident(serverName)),\n ', ',\n )}) ${conflictAction}`,\n );\n const tx = this[dbTxSymbol];\n await tx.query(stmt.text, stmt.values);\n },\n async update(this: WithHiddenTxAndSchema, value) {\n value = removeUndefined(value);\n const serverTableSchema = this[serverSchemaSymbol][serverName(schema)];\n const targetedColumns = origAndServerNamesFor(\n nonPrimaryKeyEntries(value, schema).map(([name]) => name),\n schema,\n );\n if (targetedColumns.length === 0) {\n return;\n }\n const stmt = formatPgInternalConvert(\n sql`UPDATE ${sql.ident(serverName(schema))} SET ${sql.join(\n targetedColumns.map(\n ([origName, serverName]) =>\n sql`${sql.ident(serverName)} = ${sqlInsertValue(value[origName], serverTableSchema[serverName])}`,\n ),\n ', ',\n )} WHERE ${primaryKeyClause(schema, serverTableSchema, value)}`,\n );\n const tx = this[dbTxSymbol];\n await tx.query(stmt.text, stmt.values);\n },\n async delete(this: WithHiddenTxAndSchema, value) {\n value = removeUndefined(value);\n const serverTableSchema = this[serverSchemaSymbol][serverName(schema)];\n const stmt = formatPgInternalConvert(\n sql`DELETE FROM ${sql.ident(\n serverName(schema),\n )} WHERE ${primaryKeyClause(schema, serverTableSchema, value)}`,\n );\n const tx = this[dbTxSymbol];\n await tx.query(stmt.text, stmt.values);\n },\n };\n}\n\nfunction nonPrimaryKeyEntries(\n value: Record<string, unknown>,\n schema: TableSchema,\n) {\n return Object.entries(value).filter(\n ([name]) => !schema.primaryKey.includes(name),\n );\n}\n\nfunction serverName(x: {name: string; serverName?: string | undefined}) {\n return x.serverName ?? x.name;\n}\n\nfunction primaryKeyClause(\n schema: TableSchema,\n serverTableSchema: ServerTableSchema,\n row: Record<string, unknown>,\n) {\n const primaryKey = origAndServerNamesFor(schema.primaryKey, schema);\n return sql`${sql.join(\n primaryKey.map(\n ([origName, serverName]) =>\n sql`${sql.ident(serverName)} = ${sqlValue(row[origName], serverTableSchema[serverName])}`,\n ),\n ' AND ',\n )}`;\n}\n\nfunction origAndServerNamesFor(\n originalNames: readonly string[],\n schema: TableSchema,\n): [origName: string, serverName: string][] {\n return originalNames.map(\n name => [name, serverNameFor(name, schema)] as const,\n );\n}\n\nfunction serverNameFor(originalName: string, schema: TableSchema): string {\n const col = schema.columns[originalName];\n assert(\n col,\n `Column ${originalName} was not found in the Zero schema for the table ${schema.name}`,\n );\n return col.serverName ?? originalName;\n}\n\nfunction sqlValue(value: unknown, serverColumnSchema: ServerColumnSchema) {\n return sqlConvertColumnArg(serverColumnSchema, value, false, true);\n}\n\nfunction sqlInsertValue(\n value: unknown,\n serverColumnSchema: ServerColumnSchema,\n) {\n return sqlConvertColumnArg(serverColumnSchema, value, false, false);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"custom.js","names":[],"sources":["../../../../zero-server/src/custom.ts"],"sourcesContent":["import {assert} from '../../shared/src/asserts.ts';\nimport type {ReadonlyJSONValue} from '../../shared/src/json.ts';\nimport {mapValues} from '../../shared/src/objects.ts';\nimport {recordProxy} from '../../shared/src/record-proxy.ts';\nimport {\n formatPgInternalConvert,\n sql,\n sqlConvertColumnArg,\n} from '../../z2s/src/sql.ts';\nimport type {TableSchema} from '../../zero-schema/src/table-schema.ts';\nimport type {Schema} from '../../zero-types/src/schema.ts';\nimport type {\n ServerColumnSchema,\n ServerSchema,\n ServerTableSchema,\n} from '../../zero-types/src/server-schema.ts';\nimport {\n type CRUDExecutor,\n type CRUDKind,\n makeCRUDMutate,\n makeTransactionMutate,\n type SchemaCRUD,\n type TableCRUD,\n type TransactionMutate,\n} from '../../zql/src/mutate/crud.ts';\nimport type {\n DBTransaction,\n MutateCRUD,\n Queryable,\n RetryPredicate,\n ServerTransaction,\n} from '../../zql/src/mutate/custom.ts';\nimport {createRunnableBuilder} from '../../zql/src/query/create-builder.ts';\nimport {QueryDelegateBase} from '../../zql/src/query/query-delegate-base.ts';\nimport {asQueryInternals} from '../../zql/src/query/query-internals.ts';\nimport type {\n HumanReadable,\n Query,\n RunOptions,\n} from '../../zql/src/query/query.ts';\nimport type {ConditionalSchemaQuery} from '../../zql/src/query/schema-query.ts';\nimport {getServerSchema} from './schema.ts';\n\nexport type CustomMutatorDefs<TDBTransaction> = {\n [namespaceOrKey: string]:\n | CustomMutatorImpl<TDBTransaction>\n | CustomMutatorDefs<TDBTransaction>;\n};\n\nexport type CustomMutatorImpl<\n TDBTransaction,\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n TArgs = any,\n Context = unknown,\n> = (\n tx: TDBTransaction,\n args: TArgs,\n ctx: Context,\n) => Promise<ReadonlyJSONValue | void>;\n\n/**\n * QueryDelegate implementation for server-side transactions.\n * Extends QueryDelegateBase to satisfy the QueryDelegate interface,\n * but overrides run() to execute against Postgres and throws on\n * preload()/materialize() which don't make sense server-side.\n */\nclass ServerTransactionQueryDelegate extends QueryDelegateBase {\n readonly #dbTransaction: DBTransaction<unknown>;\n readonly #schema: Schema;\n readonly #serverSchema: ServerSchema;\n\n readonly defaultQueryComplete = true;\n\n constructor(\n dbTransaction: DBTransaction<unknown>,\n schema: Schema,\n serverSchema: ServerSchema,\n ) {\n super();\n this.#dbTransaction = dbTransaction;\n this.#schema = schema;\n this.#serverSchema = serverSchema;\n }\n\n getSource(): never {\n throw new Error('not implemented');\n }\n\n override run<\n TTable extends keyof TSchema['tables'] & string,\n TSchema extends Schema,\n TReturn,\n >(\n query: Query<TTable, TSchema, TReturn>,\n _options?: RunOptions,\n ): Promise<HumanReadable<TReturn>> {\n const queryInternals = asQueryInternals(query);\n return this.#dbTransaction.runQuery<TReturn>(\n queryInternals.ast,\n queryInternals.format,\n this.#schema,\n this.#serverSchema,\n );\n }\n\n override preload(): never {\n throw new Error('preload() is not supported in server transactions');\n }\n\n override materialize(): never {\n throw new Error('materialize() is not supported in server transactions');\n }\n}\n\nexport class TransactionImpl<\n TSchema extends Schema,\n TWrappedTransaction,\n> implements ServerTransaction<TSchema, TWrappedTransaction> {\n readonly location = 'server';\n readonly reason = 'authoritative';\n readonly dbTransaction: DBTransaction<TWrappedTransaction>;\n readonly clientID: string;\n readonly mutationID: number;\n readonly mutate: TransactionMutate<TSchema>;\n /**\n * @deprecated Use {@link createBuilder} with `tx.run(zql.table.where(...))` instead.\n */\n readonly query: ConditionalSchemaQuery<TSchema>;\n\n readonly #schema: TSchema;\n readonly #serverSchema: ServerSchema;\n readonly #addRetryPredicate:\n | ((shouldRetry: RetryPredicate) => void)\n | undefined;\n\n constructor(\n dbTransaction: DBTransaction<TWrappedTransaction>,\n clientID: string,\n mutationID: number,\n mutate: TransactionMutate<TSchema>,\n schema: TSchema,\n serverSchema: ServerSchema,\n addRetryPredicate?: ((shouldRetry: RetryPredicate) => void) | undefined,\n ) {\n this.dbTransaction = dbTransaction;\n this.clientID = clientID;\n this.mutationID = mutationID;\n this.mutate = mutate;\n this.#schema = schema;\n this.#serverSchema = serverSchema;\n this.#addRetryPredicate = addRetryPredicate;\n\n const delegate = new ServerTransactionQueryDelegate(\n dbTransaction,\n schema,\n serverSchema,\n );\n this.query = createRunnableBuilder(delegate, schema);\n }\n\n retryOn(shouldRetry: RetryPredicate): void {\n if (this.#addRetryPredicate === undefined) {\n // Fail loudly: a predicate nothing will consult would silently turn\n // every retryable failure into a recorded error.\n throw new Error(\n 'tx.retryOn() is not supported on this transaction: nothing will re-run its mutator. A custom Database must pass TransactionProviderInput.addRetryPredicate on to createTransaction.',\n );\n }\n this.#addRetryPredicate(shouldRetry);\n }\n\n run<TTable extends keyof TSchema['tables'] & string, TReturn>(\n query: Query<TTable, TSchema, TReturn>,\n _options?: RunOptions,\n ): Promise<HumanReadable<TReturn>> {\n const queryInternals = asQueryInternals(query);\n\n // Execute the query using the database-specific executor\n return this.dbTransaction.runQuery<TReturn>(\n queryInternals.ast,\n queryInternals.format,\n this.#schema,\n this.#serverSchema,\n );\n }\n}\n\nconst dbTxSymbol = Symbol();\n\nconst serverSchemaSymbol = Symbol();\n\ntype WithHiddenTxAndSchema = {\n [dbTxSymbol]: DBTransaction<unknown>;\n [serverSchemaSymbol]: ServerSchema;\n};\n\n/**\n * Factory for creating MutateCRUD instances efficiently.\n *\n * Pre-creates the SQL-generating TableCRUD methods once from the schema,\n * caches the serverSchema after first fetch, and only binds the transaction\n * at transaction time.\n *\n * Use this when you need to create many transactions and want to avoid\n * the overhead of re-creating CRUD methods for each one.\n */\nexport class CRUDMutatorFactory<S extends Schema> {\n readonly #schema: S;\n readonly #tableCRUDs: Record<string, TableCRUD<TableSchema>>;\n // The in-flight or resolved fetch is cached (not just the result) so that\n // concurrent cold callers share a single catalog query.\n #serverSchema: Promise<ServerSchema> | undefined;\n\n constructor(schema: S) {\n this.#schema = schema;\n // Pre-create TableCRUD methods for each table once\n this.#tableCRUDs = {};\n for (const tableSchema of Object.values(schema.tables)) {\n this.#tableCRUDs[tableSchema.name] = makeServerTableCRUD(tableSchema);\n }\n }\n\n /**\n * Gets the cached serverSchema, or fetches and caches it on first call.\n */\n getOrFetchServerSchema(queryable: Queryable): Promise<ServerSchema> {\n if (!this.#serverSchema) {\n this.#serverSchema = getServerSchema(queryable, this.#schema).catch(e => {\n // Don't poison the cache with a transient failure.\n this.#serverSchema = undefined;\n throw e;\n });\n }\n return this.#serverSchema;\n }\n\n /**\n * Creates a CRUDExecutor bound to the given transaction and serverSchema.\n * Uses the pre-created TableCRUD methods from construction time.\n */\n createExecutor(\n dbTransaction: DBTransaction<unknown>,\n serverSchema: ServerSchema,\n ): CRUDExecutor {\n const txHolder: WithHiddenTxAndSchema = {\n [dbTxSymbol]: dbTransaction,\n [serverSchemaSymbol]: serverSchema,\n };\n const boundCRUDs = recordProxy(this.#tableCRUDs, tableCRUD =>\n mapValues(tableCRUD, method => method.bind(txHolder)),\n ) as unknown as SchemaCRUD<S>;\n\n return (table: string, kind: CRUDKind, args: unknown) => {\n const tableCRUD = boundCRUDs[table as keyof S['tables']];\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n return (tableCRUD as any)[kind](args);\n };\n }\n\n /**\n * Creates a full ServerTransaction.\n * Fetches/caches serverSchema automatically.\n */\n async createTransaction<TWrappedTransaction>(\n dbTransaction: DBTransaction<TWrappedTransaction>,\n clientID: string,\n mutationID: number,\n addRetryPredicate?: ((shouldRetry: RetryPredicate) => void) | undefined,\n ): Promise<TransactionImpl<S, TWrappedTransaction>> {\n const serverSchema = await this.getOrFetchServerSchema(dbTransaction);\n const executor = this.createExecutor(dbTransaction, serverSchema);\n const mutate = makeTransactionMutate(this.#schema, executor);\n return new TransactionImpl(\n dbTransaction,\n clientID,\n mutationID,\n mutate,\n this.#schema,\n serverSchema,\n addRetryPredicate,\n );\n }\n}\n\nexport async function makeServerTransaction<\n TSchema extends Schema,\n TWrappedTransaction,\n>(\n dbTransaction: DBTransaction<TWrappedTransaction>,\n clientID: string,\n mutationID: number,\n schema: TSchema,\n) {\n const serverSchema = await getServerSchema(dbTransaction, schema);\n // Use the internal executor and shared function directly,\n // bypassing the validation in makeMutateCRUD/makeSchemaCRUD\n const executor = makeServerCRUDExecutor(schema, dbTransaction, serverSchema);\n const mutate = makeTransactionMutate(schema, executor);\n return new TransactionImpl(\n dbTransaction,\n clientID,\n mutationID,\n mutate,\n schema,\n serverSchema,\n );\n}\n\n/**\n * @deprecated Use transactions instead.\n *\n * Returns a curried function for backwards compatibility.\n */\nexport function makeSchemaCRUD<S extends Schema>(\n schema: S,\n): (\n dbTransaction: DBTransaction<unknown>,\n serverSchema: ServerSchema,\n) => MutateCRUD<S, true> {\n return (dbTransaction: DBTransaction<unknown>, serverSchema: ServerSchema) =>\n makeCRUDMutate(\n schema,\n true,\n makeServerCRUDExecutor(schema, dbTransaction, serverSchema),\n );\n}\n\nfunction removeUndefined<T extends Record<string, unknown>>(value: T): T {\n const valueWithoutUndefined: Record<string, unknown> = {};\n for (const [key, val] of Object.entries(value)) {\n if (val !== undefined) {\n valueWithoutUndefined[key] = val;\n }\n }\n return valueWithoutUndefined as T;\n}\n\n/**\n * Creates a CRUDExecutor for server-side SQL execution.\n *\n * For users with very large schemas it is expensive to re-create\n * all the CRUD mutators for each transaction. Instead, we create\n * the SQL-generating methods once up-front and then bind them to\n * the transaction as requested.\n */\nfunction makeServerCRUDExecutor<S extends Schema>(\n schema: S,\n dbTransaction: DBTransaction<unknown>,\n serverSchema: ServerSchema,\n): CRUDExecutor {\n // Pre-create TableCRUD methods for each table (optimization for large schemas)\n const tableCRUDs: Record<string, TableCRUD<TableSchema>> = {};\n for (const tableSchema of Object.values(schema.tables)) {\n tableCRUDs[tableSchema.name] = makeServerTableCRUD(tableSchema);\n }\n\n // Bind transaction context to the methods\n const txHolder: WithHiddenTxAndSchema = {\n [dbTxSymbol]: dbTransaction,\n [serverSchemaSymbol]: serverSchema,\n };\n const boundCRUDs = recordProxy(tableCRUDs, tableCRUD =>\n mapValues(tableCRUD, method => method.bind(txHolder)),\n ) as unknown as SchemaCRUD<S>;\n\n // Return executor that dispatches to bound methods\n return (table: string, kind: CRUDKind, args: unknown) => {\n const tableCRUD = boundCRUDs[table as keyof S['tables']];\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n return (tableCRUD as any)[kind](args);\n };\n}\n\n/**\n * Creates SQL-generating TableCRUD methods for a table.\n * Methods use `this` context to access transaction and server schema.\n */\nfunction makeServerTableCRUD(schema: TableSchema): TableCRUD<TableSchema> {\n return {\n async insert(this: WithHiddenTxAndSchema, value) {\n value = removeUndefined(value);\n const serverTableSchema = this[serverSchemaSymbol][serverName(schema)];\n\n const targetedColumns = origAndServerNamesFor(Object.keys(value), schema);\n const primaryKeyColumns = origAndServerNamesFor(\n schema.primaryKey,\n schema,\n );\n\n const stmt = formatPgInternalConvert(\n sql`INSERT INTO ${sql.ident(serverName(schema))} (${sql.join(\n targetedColumns.map(([, serverName]) => sql.ident(serverName)),\n ',',\n )}) VALUES (${sql.join(\n Object.entries(value).map(([col, v]) =>\n sqlInsertValue(v, serverTableSchema[serverNameFor(col, schema)]),\n ),\n ', ',\n )}) ON CONFLICT (${sql.join(\n primaryKeyColumns.map(([, serverName]) => sql.ident(serverName)),\n ', ',\n )}) DO NOTHING`,\n );\n const tx = this[dbTxSymbol];\n await tx.query(stmt.text, stmt.values);\n },\n async upsert(this: WithHiddenTxAndSchema, value) {\n value = removeUndefined(value);\n const serverTableSchema = this[serverSchemaSymbol][serverName(schema)];\n const targetedColumns = origAndServerNamesFor(Object.keys(value), schema);\n const primaryKeyColumns = origAndServerNamesFor(\n schema.primaryKey,\n schema,\n );\n const updatedEntries = nonPrimaryKeyEntries(value, schema);\n const conflictAction =\n updatedEntries.length === 0\n ? sql`DO NOTHING`\n : sql`DO UPDATE SET ${sql.join(\n updatedEntries.map(\n ([col, val]) =>\n sql`${sql.ident(\n schema.columns[col].serverName ?? col,\n )} = ${sqlInsertValue(val, serverTableSchema[serverNameFor(col, schema)])}`,\n ),\n ', ',\n )}`;\n const stmt = formatPgInternalConvert(\n sql`INSERT INTO ${sql.ident(serverName(schema))} (${sql.join(\n targetedColumns.map(([, serverName]) => sql.ident(serverName)),\n ',',\n )}) VALUES (${sql.join(\n Object.entries(value).map(([col, val]) =>\n sqlInsertValue(val, serverTableSchema[serverNameFor(col, schema)]),\n ),\n ', ',\n )}) ON CONFLICT (${sql.join(\n primaryKeyColumns.map(([, serverName]) => sql.ident(serverName)),\n ', ',\n )}) ${conflictAction}`,\n );\n const tx = this[dbTxSymbol];\n await tx.query(stmt.text, stmt.values);\n },\n async update(this: WithHiddenTxAndSchema, value) {\n value = removeUndefined(value);\n const serverTableSchema = this[serverSchemaSymbol][serverName(schema)];\n const targetedColumns = origAndServerNamesFor(\n nonPrimaryKeyEntries(value, schema).map(([name]) => name),\n schema,\n );\n if (targetedColumns.length === 0) {\n return;\n }\n const stmt = formatPgInternalConvert(\n sql`UPDATE ${sql.ident(serverName(schema))} SET ${sql.join(\n targetedColumns.map(\n ([origName, serverName]) =>\n sql`${sql.ident(serverName)} = ${sqlInsertValue(value[origName], serverTableSchema[serverName])}`,\n ),\n ', ',\n )} WHERE ${primaryKeyClause(schema, serverTableSchema, value)}`,\n );\n const tx = this[dbTxSymbol];\n await tx.query(stmt.text, stmt.values);\n },\n async delete(this: WithHiddenTxAndSchema, value) {\n value = removeUndefined(value);\n const serverTableSchema = this[serverSchemaSymbol][serverName(schema)];\n const stmt = formatPgInternalConvert(\n sql`DELETE FROM ${sql.ident(\n serverName(schema),\n )} WHERE ${primaryKeyClause(schema, serverTableSchema, value)}`,\n );\n const tx = this[dbTxSymbol];\n await tx.query(stmt.text, stmt.values);\n },\n };\n}\n\nfunction nonPrimaryKeyEntries(\n value: Record<string, unknown>,\n schema: TableSchema,\n) {\n return Object.entries(value).filter(\n ([name]) => !schema.primaryKey.includes(name),\n );\n}\n\nfunction serverName(x: {name: string; serverName?: string | undefined}) {\n return x.serverName ?? x.name;\n}\n\nfunction primaryKeyClause(\n schema: TableSchema,\n serverTableSchema: ServerTableSchema,\n row: Record<string, unknown>,\n) {\n const primaryKey = origAndServerNamesFor(schema.primaryKey, schema);\n return sql`${sql.join(\n primaryKey.map(\n ([origName, serverName]) =>\n sql`${sql.ident(serverName)} = ${sqlValue(row[origName], serverTableSchema[serverName])}`,\n ),\n ' AND ',\n )}`;\n}\n\nfunction origAndServerNamesFor(\n originalNames: readonly string[],\n schema: TableSchema,\n): [origName: string, serverName: string][] {\n return originalNames.map(\n name => [name, serverNameFor(name, schema)] as const,\n );\n}\n\nfunction serverNameFor(originalName: string, schema: TableSchema): string {\n const col = schema.columns[originalName];\n assert(\n col,\n `Column ${originalName} was not found in the Zero schema for the table ${schema.name}`,\n );\n return col.serverName ?? originalName;\n}\n\nfunction sqlValue(value: unknown, serverColumnSchema: ServerColumnSchema) {\n return sqlConvertColumnArg(serverColumnSchema, value, false, true);\n}\n\nfunction sqlInsertValue(\n value: unknown,\n serverColumnSchema: ServerColumnSchema,\n) {\n return sqlConvertColumnArg(serverColumnSchema, value, false, false);\n}\n"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
- import "../../zero-protocol/src/application-error.js";
2
- import "./custom.js";
3
- import "./pg-query-executor.js";
4
- import "./push-processor.js";
5
- import "./process-mutations.js";
6
- import "./queries/process-queries.js";
7
- import "./zql-database.js";
8
- export {};
1
+ import { ApplicationError, isApplicationError } from "../../zero-protocol/src/application-error.js";
2
+ import { CRUDMutatorFactory, makeSchemaCRUD } from "./custom.js";
3
+ import { executePostgresQuery } from "./pg-query-executor.js";
4
+ import { PushProcessor } from "./push-processor.js";
5
+ import { OutOfOrderMutation, getMutation, handleMutateRequest, handleMutationRequest } from "./process-mutations.js";
6
+ import { handleGetQueriesRequest, handleQueryRequest, handleTransformRequest } from "./queries/process-queries.js";
7
+ import { ZQLDatabase } from "./zql-database.js";
8
+ export { ApplicationError, CRUDMutatorFactory, OutOfOrderMutation, PushProcessor, ZQLDatabase, executePostgresQuery, getMutation, handleGetQueriesRequest, handleMutateRequest, handleMutationRequest, handleQueryRequest, handleTransformRequest, isApplicationError, makeSchemaCRUD };
@@ -1 +1 @@
1
- {"version":3,"file":"process-mutations.js","names":["#dbProvider","#req","#params","#lc","#transactImpl","#shouldRetry","#checkAndIncrementLastMutationID","#getTransactionInput"],"sources":["../../../../zero-server/src/process-mutations.ts"],"sourcesContent":["import {\n consoleLogSink,\n type LogContext,\n type LogLevel,\n type LogSink,\n} from '@rocicorp/logger';\nimport {assert} from '../../shared/src/asserts.ts';\nimport {getErrorDetails, getErrorMessage} from '../../shared/src/error.ts';\nimport type {ReadonlyJSONValue} from '../../shared/src/json.ts';\nimport {promiseVoid} from '../../shared/src/resolved-promises.ts';\nimport type {MaybePromise} from '../../shared/src/types.ts';\nimport * as v from '../../shared/src/valita.ts';\nimport {MutationAlreadyProcessedError} from '../../zero-cache/src/services/mutagen/error.ts';\nimport type {ApplicationError} from '../../zero-protocol/src/application-error.ts';\nimport {\n isApplicationError,\n wrapWithApplicationError,\n} from '../../zero-protocol/src/application-error.ts';\nimport {ErrorKind} from '../../zero-protocol/src/error-kind.ts';\nimport {ErrorOrigin} from '../../zero-protocol/src/error-origin.ts';\nimport {ErrorReason} from '../../zero-protocol/src/error-reason.ts';\nimport type {PushFailedBody} from '../../zero-protocol/src/error.ts';\nimport {\n mutateParamsSchema,\n type MutateResponse,\n} from '../../zero-protocol/src/mutate-server.ts';\nimport type {MutationID} from '../../zero-protocol/src/mutation-id.ts';\nimport {\n CLEANUP_RESULTS_MUTATION_NAME,\n cleanupResultsArgSchema,\n type CleanupResultsArg,\n type CustomMutation,\n type Mutation,\n} from '../../zero-protocol/src/mutation.ts';\nimport {\n pushBodySchema,\n type MutationResponse,\n type PushBody,\n} from '../../zero-protocol/src/push.ts';\nimport type {RetryPredicate} from '../../zql/src/mutate/custom.ts';\nimport type {AnyMutatorRegistry} from '../../zql/src/mutate/mutator-registry.ts';\nimport {isMutator} from '../../zql/src/mutate/mutator.ts';\nimport type {CustomMutatorDefs, CustomMutatorImpl} from './custom.ts';\nimport {createLogContext} from './logging.ts';\nimport {separatorRe} from './push-processor.ts';\n\nexport interface TransactionProviderHooks {\n updateClientMutationID: () => Promise<{lastMutationID: number | bigint}>;\n writeMutationResult: (result: MutationResponse) => Promise<void>;\n deleteMutationResults: (args: CleanupResultsArg) => Promise<void>;\n}\n\nexport interface TransactionProviderInput {\n upstreamSchema: string;\n clientGroupID: string;\n clientID: string;\n mutationID: number;\n /**\n * Receives each predicate the mutator registers with `tx.retryOn()`. A\n * `Database` passes it on to the server transaction it creates.\n */\n addRetryPredicate?: ((shouldRetry: RetryPredicate) => void) | undefined;\n}\n\n/**\n * Defines the abstract interface for a database that PushProcessor can execute\n * transactions against.\n */\nexport interface Database<T> {\n transaction: <R>(\n callback: (\n tx: T,\n transactionHooks: TransactionProviderHooks,\n ) => MaybePromise<R>,\n transactionInput?: TransactionProviderInput,\n ) => Promise<R>;\n}\n\nexport type ExtractTransactionType<D> = D extends Database<infer T> ? T : never;\nexport type Params = v.Infer<typeof mutateParamsSchema>;\n\nexport type TransactFn<D extends Database<ExtractTransactionType<D>>> = (\n cb: TransactFnCallback<D>,\n) => Promise<MutationResponse>;\n\nexport type TransactFnCallback<D extends Database<ExtractTransactionType<D>>> =\n (\n tx: ExtractTransactionType<D>,\n mutatorName: string,\n mutatorArgs: ReadonlyJSONValue | undefined,\n ) => Promise<ReadonlyJSONValue | void>;\n\nexport type Parsed<D extends Database<ExtractTransactionType<D>>> = {\n transact: TransactFn<D>;\n mutations: CustomMutation[];\n};\n\ntype MutationPhase = 'preTransaction' | 'transactionPending' | 'postCommit';\n\nconst applicationErrorWrapper = async <T>(fn: () => Promise<T>): Promise<T> => {\n try {\n return await fn();\n } catch (error) {\n if (\n error instanceof DatabaseTransactionError ||\n error instanceof OutOfOrderMutation ||\n error instanceof MutationAlreadyProcessedError ||\n isApplicationError(error)\n ) {\n throw error;\n }\n\n throw wrapWithApplicationError(error);\n }\n};\n\n/**\n * @deprecated Use {@link handleMutateRequest} instead.\n */\nexport const handleMutationRequest = handleMutateRequest;\n\n/**\n * Parsed query params accepted by {@link handleMutateRequest} when the\n * incoming request URL has already been handled by your framework.\n */\nexport type MutateSearchParams = URLSearchParams | Record<string, string>;\n\n/**\n * Runs once per custom mutation in a `/mutate` request.\n *\n * Call `transact` to execute database work for the mutation. Throwing before\n * `transact` persists an application error; throwing after `transact` resolves\n * only affects server-side logging because the mutation is already committed.\n */\nexport type MutateRequestHandler<\n D extends Database<ExtractTransactionType<D>>,\n> = (\n transact: TransactFn<D>,\n mutation: CustomMutation,\n) => Promise<MutationResponse>;\n\nexport type HandleMutateRequestArgs<\n D extends Database<ExtractTransactionType<D>>,\n> = {\n /** Database used to run transactions and store mutation results. */\n dbProvider: D;\n handler: MutateRequestHandler<D>;\n /**\n * Authenticated user ID. Null or undefined means the user is logged out.\n */\n userID: string | null | undefined;\n /** Optional log level for request parsing and execution. */\n logLevel?: LogLevel | undefined;\n /**\n * Destination for this request's log output. When omitted, logs are written\n * with `console.log`/`info`/`warn`/`error` etc.\n *\n * Provide a custom {@link LogSink} to redirect logs — for example into an\n * application's own structured logging pipeline, so that records emitted while\n * processing a push carry the same fields as the rest of the server's logs.\n */\n logSink?: LogSink | undefined;\n} & (\n | {\n /** Fetch request containing both query params and the JSON body. */\n request: Request;\n }\n | {\n /** Parsed query params from the `/mutate` request URL. */\n query: MutateSearchParams;\n /** Parsed JSON body from the `/mutate` request. */\n body: ReadonlyJSONValue;\n }\n);\n\ntype NormalizedMutateRequestArgs<\n D extends Database<ExtractTransactionType<D>>,\n> = {\n readonly dbProvider: D;\n readonly handler: MutateRequestHandler<D>;\n // Note: semantics of undefined differ from HandleMutateRequestArgs.userID.\n // Here, undefined means the app didn't provide a user ID - we do not know if\n // the user is logged in or not. This is legacy behavior needed to support\n // deprecated signatures of handleMutateRequest which did not receive userID\n // from app.\n readonly userID: string | null | undefined;\n readonly logLevel: LogLevel;\n readonly logSink: LogSink;\n} & (\n | {\n readonly type: 'request';\n readonly request: Request;\n }\n | {\n readonly type: 'body';\n readonly queryParams: Record<string, string>;\n readonly jsonBody: ReadonlyJSONValue;\n }\n);\n\n/**\n * Process a `/mutate` request from a Fetch `Request`.\n */\nexport function handleMutateRequest<\n D extends Database<ExtractTransactionType<D>>,\n>(input: HandleMutateRequestArgs<D>): Promise<MutateResponse>;\n\n/**\n * @deprecated Pass a single object instead:\n * `handleMutateRequest({dbProvider, handler, query, body, userID, logLevel})`.\n */\nexport function handleMutateRequest<\n D extends Database<ExtractTransactionType<D>>,\n>(\n dbProvider: D,\n handler: MutateRequestHandler<D>,\n query: MutateSearchParams,\n body: ReadonlyJSONValue,\n logLevel?: LogLevel,\n logSink?: LogSink,\n): Promise<MutateResponse>;\n\n/**\n * @deprecated Pass a single object instead:\n * `handleMutateRequest({dbProvider, handler, request, userID, logLevel})`.\n */\nexport function handleMutateRequest<\n D extends Database<ExtractTransactionType<D>>,\n>(\n dbProvider: D,\n handler: MutateRequestHandler<D>,\n request: Request,\n logLevel?: LogLevel,\n logSink?: LogSink,\n): Promise<MutateResponse>;\n\nexport async function handleMutateRequest<\n D extends Database<ExtractTransactionType<D>>,\n>(\n inputOrDbProvider: HandleMutateRequestArgs<D> | D,\n maybeHandler?: MutateRequestHandler<D> | undefined,\n requestOrQuery?: Request | MutateSearchParams | undefined,\n bodyOrLogLevel?: ReadonlyJSONValue | LogLevel | undefined,\n logLevelOrLogSink?: LogLevel | LogSink | undefined,\n maybeLogSink?: LogSink | undefined,\n): Promise<MutateResponse> {\n const normalized =\n typeof inputOrDbProvider === 'object' && 'handler' in inputOrDbProvider\n ? normalizeMutateRequestInput(inputOrDbProvider)\n : normalizeLegacyMutateRequestArgs(\n inputOrDbProvider,\n maybeHandler,\n requestOrQuery,\n bodyOrLogLevel,\n logLevelOrLogSink,\n maybeLogSink,\n );\n\n const lc = createLogContext(\n normalized.logLevel,\n normalized.logSink,\n ).withContext('PushProcessor');\n let jsonBody: unknown;\n\n if (normalized.type === 'request') {\n try {\n jsonBody = await normalized.request.json();\n } catch (error) {\n lc.error?.('Failed to parse push body', error);\n const message = `Failed to parse push body: ${getErrorMessage(error)}`;\n const details = getErrorDetails(error);\n return {\n kind: ErrorKind.PushFailed,\n origin: ErrorOrigin.Server,\n reason: ErrorReason.Parse,\n message,\n mutationIDs: [],\n ...(details ? {details} : {}),\n } as const satisfies PushFailedBody;\n }\n } else {\n jsonBody = normalized.jsonBody;\n }\n\n let mutationIDs: MutationID[] = [];\n\n let pushBody: PushBody;\n try {\n pushBody = v.parse(jsonBody, pushBodySchema, 'passthrough');\n mutationIDs = pushBody.mutations.map(m => ({\n id: m.id,\n clientID: m.clientID,\n }));\n } catch (error) {\n lc.error?.('Failed to parse push body', error);\n const message = `Failed to parse push body: ${getErrorMessage(error)}`;\n const details = getErrorDetails(error);\n return {\n kind: ErrorKind.PushFailed,\n origin: ErrorOrigin.Server,\n reason: ErrorReason.Parse,\n message,\n mutationIDs,\n ...(details ? {details} : {}),\n } as const satisfies PushFailedBody;\n }\n\n let parsedQueryParams: Params;\n try {\n parsedQueryParams = v.parse(\n normalized.type === 'request'\n ? Object.fromEntries(new URL(normalized.request.url).searchParams)\n : normalized.queryParams,\n mutateParamsSchema,\n 'passthrough',\n );\n } catch (error) {\n lc.error?.('Failed to parse push query parameters', error);\n const message = `Failed to parse push query parameters: ${getErrorMessage(error)}`;\n const details = getErrorDetails(error);\n return {\n kind: ErrorKind.PushFailed,\n origin: ErrorOrigin.Server,\n reason: ErrorReason.Parse,\n message,\n mutationIDs,\n ...(details ? {details} : {}),\n } as const satisfies PushFailedBody;\n }\n\n if (pushBody.pushVersion !== 1) {\n const response = {\n kind: ErrorKind.PushFailed,\n origin: ErrorOrigin.Server,\n reason: ErrorReason.UnsupportedPushVersion,\n mutationIDs,\n message: `Unsupported push version: ${pushBody.pushVersion}`,\n } as const satisfies PushFailedBody;\n return response;\n }\n\n const responses: MutationResponse[] = [];\n let processedCount = 0;\n\n try {\n const transactor = new Transactor(\n normalized.dbProvider,\n pushBody,\n parsedQueryParams,\n lc,\n );\n\n // Each mutation goes through three phases:\n // 1. Pre-transaction: user logic that runs before `transact` is called. If\n // this throws we still advance LMID and persist the failure result.\n // 2. Transaction: the callback passed to `transact`, which can be retried\n // if it fails with an ApplicationError.\n // 3. Post-commit: any logic that runs after `transact` resolves. Failures\n // here are logged but the mutation remains committed.\n for (const m of pushBody.mutations) {\n // Handle internal mutations (like cleanup) directly without user dispatch\n if (m.type === 'custom' && m.name === CLEANUP_RESULTS_MUTATION_NAME) {\n lc.debug?.(\n `Processing internal mutation '${m.name}' (clientID=${m.clientID})`,\n );\n try {\n await processCleanupResultsMutation(\n normalized.dbProvider,\n m,\n parsedQueryParams,\n lc,\n );\n // No response added - this is fire-and-forget\n processedCount++;\n } catch (error) {\n lc.warn?.(\n `Failed to process cleanup mutation for client ${m.clientID}`,\n error,\n );\n // Don't fail the whole push for cleanup errors\n processedCount++;\n }\n continue;\n }\n\n assert(m.type === 'custom', 'Expected custom mutation');\n lc.debug?.(\n `Processing mutation '${m.name}' (id=${m.id}, clientID=${m.clientID})`,\n );\n\n let mutationPhase: MutationPhase = 'preTransaction';\n\n const transactProxy: TransactFn<D> = async innerCb => {\n mutationPhase = 'transactionPending';\n const result = await transactor.transact(m, innerCb);\n mutationPhase = 'postCommit';\n return result;\n };\n\n try {\n const res = await applicationErrorWrapper(() =>\n normalized.handler(transactProxy, m),\n );\n responses.push(res);\n lc.debug?.(`Mutation '${m.name}' (id=${m.id}) completed successfully`);\n\n processedCount++;\n } catch (error) {\n if (!isApplicationError(error)) {\n throw error;\n }\n\n if (mutationPhase === 'preTransaction') {\n // Pre-transaction\n await transactor.persistPreTransactionFailure(m, error);\n } else if (mutationPhase === 'postCommit') {\n // Post-commit\n lc.error?.(\n `Post-commit mutation handler failed for mutation ${m.id} for client ${m.clientID}`,\n error,\n );\n }\n\n lc.warn?.(\n `Application error processing mutation ${m.id} for client ${m.clientID}`,\n error,\n );\n responses.push(makeAppErrorResponse(m, error));\n\n processedCount++;\n }\n }\n\n return {\n kind: 'MutateResponse',\n mutations: responses,\n ...(typeof normalized.userID !== 'undefined'\n ? {userID: normalized.userID}\n : {}),\n } as const satisfies MutateResponse;\n } catch (error) {\n lc.error?.('Failed to process push request', error);\n // only include mutationIDs for mutations that were not processed\n const unprocessedMutationIDs = mutationIDs.slice(processedCount);\n\n const message = getErrorMessage(error);\n const details = getErrorDetails(error);\n\n return {\n kind: ErrorKind.PushFailed,\n origin: ErrorOrigin.Server,\n reason:\n error instanceof OutOfOrderMutation\n ? ErrorReason.OutOfOrderMutation\n : error instanceof DatabaseTransactionError\n ? ErrorReason.Database\n : ErrorReason.Internal,\n message,\n mutationIDs: unprocessedMutationIDs,\n ...(details ? {details} : {}),\n };\n }\n}\n\nfunction normalizeMutateRequestInput<\n D extends Database<ExtractTransactionType<D>>,\n>(input: HandleMutateRequestArgs<D>): NormalizedMutateRequestArgs<D> {\n if ('request' in input) {\n return {\n type: 'request',\n dbProvider: input.dbProvider,\n handler: input.handler,\n request: input.request,\n userID: input.userID ?? null,\n logLevel: input.logLevel ?? 'info',\n logSink: input.logSink ?? consoleLogSink,\n };\n }\n\n return {\n type: 'body',\n dbProvider: input.dbProvider,\n handler: input.handler,\n jsonBody: input.body,\n userID: input.userID ?? null,\n queryParams:\n input.query instanceof URLSearchParams\n ? Object.fromEntries(input.query)\n : input.query,\n logLevel: input.logLevel ?? 'info',\n logSink: input.logSink ?? consoleLogSink,\n };\n}\n\nfunction normalizeLegacyMutateRequestArgs<\n D extends Database<ExtractTransactionType<D>>,\n>(\n dbProvider: D,\n handler: MutateRequestHandler<D> | undefined,\n requestOrQuery: Request | MutateSearchParams | undefined,\n bodyOrLogLevel: ReadonlyJSONValue | LogLevel | undefined,\n logLevelOrLogSink: LogLevel | LogSink | undefined,\n maybeLogSink: LogSink | undefined,\n): NormalizedMutateRequestArgs<D> {\n assert(typeof handler === 'function', 'Handler function is required');\n assert(\n typeof requestOrQuery !== 'undefined',\n 'Request or query parameters are required',\n );\n\n if (requestOrQuery instanceof Request) {\n return {\n type: 'request',\n dbProvider,\n handler,\n request: requestOrQuery,\n userID: undefined,\n logLevel: (bodyOrLogLevel as LogLevel | undefined) ?? 'info',\n // The `request` overload has no `body` slot, so the sink arrives one\n // position earlier than it does in the `query`/`body` overload.\n logSink: (logLevelOrLogSink as LogSink | undefined) ?? consoleLogSink,\n };\n }\n\n assert(\n typeof bodyOrLogLevel !== 'undefined',\n 'JSON body cannot be undefined',\n );\n\n return {\n type: 'body',\n dbProvider,\n handler,\n jsonBody: bodyOrLogLevel,\n userID: undefined,\n queryParams:\n requestOrQuery instanceof URLSearchParams\n ? Object.fromEntries(requestOrQuery)\n : requestOrQuery,\n logLevel: (logLevelOrLogSink as LogLevel | undefined) ?? 'info',\n logSink: maybeLogSink ?? consoleLogSink,\n };\n}\n\n/**\n * The most times a mutator runs for one mutation within one push when its\n * `tx.retryOn()` predicate keeps answering yes: the first run and four\n * re-runs.\n */\nexport const MAX_MUTATOR_RUNS = 5;\n\nclass Transactor<D extends Database<ExtractTransactionType<D>>> {\n readonly #dbProvider: D;\n readonly #req: PushBody;\n readonly #params: Params;\n readonly #lc: LogContext;\n\n constructor(dbProvider: D, req: PushBody, params: Params, lc: LogContext) {\n this.#dbProvider = dbProvider;\n this.#req = req;\n this.#params = params;\n this.#lc = lc;\n }\n\n /**\n * Asks all of the run's `tx.retryOn()` predicates at once whether to run\n * the mutator again. The first to answer yes decides it and the other\n * answers are ignored; it is no once every predicate has answered no. A\n * predicate that throws or rejects answers no: escaping here would skip\n * recording the mutation's result, and the client would send it again.\n */\n #shouldRetry(\n predicates: readonly RetryPredicate[],\n error: unknown,\n attempt: number,\n ): Promise<boolean> {\n if (predicates.length === 0) {\n return Promise.resolve(false);\n }\n const ask = async (shouldRetry: RetryPredicate): Promise<boolean> => {\n try {\n return (await shouldRetry(error, attempt)) === true;\n } catch (retryError) {\n this.#lc.warn?.(\n 'retryOn predicate threw; treating it as no',\n retryError,\n );\n return false;\n }\n };\n return new Promise(resolve => {\n let pending = predicates.length;\n for (const shouldRetry of predicates) {\n void ask(shouldRetry).then(yes => {\n if (yes) {\n resolve(true);\n } else if (--pending === 0) {\n resolve(false);\n }\n });\n }\n });\n }\n\n transact = async (\n mutation: CustomMutation,\n cb: TransactFnCallback<D>,\n ): Promise<MutationResponse> => {\n let appError: ApplicationError | undefined = undefined;\n // Which run of the mutator this is: 1, then 2 for the first re-run, and so\n // on. When a run fails, it is also the number of the re-run being decided.\n let attempt = 1;\n for (;;) {\n // What this run registers with `tx.retryOn()`. Each run starts empty.\n const predicates: RetryPredicate[] = [];\n try {\n const ret = await this.#transactImpl(\n mutation,\n cb,\n appError,\n shouldRetry => {\n predicates.push(shouldRetry);\n },\n );\n if (appError !== undefined) {\n this.#lc.warn?.(\n // log-leak-ignore -- mutation and client ids, not row data\n `Mutation ${mutation.id} for client ${mutation.clientID} was retried after an error`,\n appError,\n );\n return makeAppErrorResponse(mutation, appError);\n }\n\n return ret;\n } catch (error) {\n if (error instanceof OutOfOrderMutation) {\n this.#lc.error?.(error);\n throw error;\n }\n\n if (error instanceof MutationAlreadyProcessedError) {\n this.#lc.warn?.(error);\n return {\n id: {\n clientID: mutation.clientID,\n id: mutation.id,\n },\n result: {\n error: 'alreadyProcessed',\n details: error.message,\n },\n };\n }\n\n if (appError !== undefined) {\n // Retry also failed → internal error, cannot skip mutation\n this.#lc.error?.(\n // log-leak-ignore -- mutation and client ids, not row data\n `Retry also failed for mutation ${mutation.id} for client ${mutation.clientID}`,\n error,\n );\n throw error;\n }\n\n // What the mutator or the database threw, without the wrapper. This\n // includes errors raised at COMMIT, which the mutator cannot catch.\n const originalError =\n error instanceof DatabaseTransactionError\n ? (error.cause ?? error)\n : error;\n\n // One of the run's `tx.retryOn()` predicates says to run it again → do\n // so, in a fresh transaction.\n //\n // `continue` WITHOUT setting `appError` is the whole mechanism: it is\n // `appError` being set that makes the next `#transactImpl` skip\n // `cb(...)`. `#transactImpl` opens a new transaction each pass, so the\n // re-run gets a new snapshot. `#checkAndIncrementLastMutationID` re-runs\n // safely because the failed run rolled its increment back.\n if (\n attempt < MAX_MUTATOR_RUNS &&\n (await this.#shouldRetry(predicates, originalError, attempt))\n ) {\n this.#lc.warn?.(\n // log-leak-ignore -- mutation and client ids, not row data\n `Mutation ${mutation.id} for client ${mutation.clientID} failed, re-running mutator (retry ${attempt})`,\n originalError,\n );\n attempt++;\n continue;\n }\n\n // Not re-run → store error and retry without mutator\n appError = wrapWithApplicationError(originalError);\n this.#lc.warn?.(\n // log-leak-ignore -- mutation and client ids, not row data\n `Error processing mutation ${mutation.id} for client ${mutation.clientID}, retrying without mutator`,\n appError,\n );\n continue;\n }\n }\n };\n\n async persistPreTransactionFailure(\n mutation: CustomMutation,\n appError: ApplicationError<ReadonlyJSONValue | undefined>,\n ): Promise<MutationResponse> {\n // User-land code threw before calling `transact`. We still need to bump the\n // LMID for this mutation and persist the error so that the client knows it failed.\n const ret = await this.#transactImpl(\n mutation,\n // noop callback since there's no transaction to execute\n () => promiseVoid,\n appError,\n undefined,\n );\n return ret;\n }\n\n async #transactImpl(\n mutation: CustomMutation,\n cb: TransactFnCallback<D>,\n appError: ApplicationError | undefined,\n addRetryPredicate: ((shouldRetry: RetryPredicate) => void) | undefined,\n ): Promise<MutationResponse> {\n let transactionPhase: DatabaseTransactionPhase = 'open';\n\n try {\n const ret = await this.#dbProvider.transaction(\n async (dbTx, transactionHooks) => {\n // update the transaction phase to 'execute' after the transaction is opened\n transactionPhase = 'execute';\n\n await this.#checkAndIncrementLastMutationID(\n transactionHooks,\n mutation.clientID,\n mutation.id,\n );\n\n if (appError === undefined) {\n this.#lc.debug?.(\n // log-leak-ignore -- mutator name and mutation id, not row data\n `Executing mutator '${mutation.name}' (id=${mutation.id})`,\n );\n const data = (await cb(dbTx, mutation.name, mutation.args[0])) as\n | ReadonlyJSONValue\n | undefined;\n const id = {clientID: mutation.clientID, id: mutation.id};\n if (data === undefined) {\n return {id, result: {}};\n }\n // Persisted so the data still reaches the client if the push\n // response is lost. A mutator that returns nothing writes no row.\n const successResult: MutationResponse = {id, result: {data}};\n await transactionHooks.writeMutationResult(successResult);\n return successResult;\n } else {\n const mutationResult = makeAppErrorResponse(mutation, appError);\n await transactionHooks.writeMutationResult(mutationResult);\n return mutationResult;\n }\n },\n this.#getTransactionInput(mutation, addRetryPredicate),\n );\n\n return ret;\n } catch (error) {\n if (\n isApplicationError(error) ||\n error instanceof OutOfOrderMutation ||\n error instanceof MutationAlreadyProcessedError\n ) {\n throw error;\n }\n\n throw new DatabaseTransactionError(transactionPhase, {cause: error});\n }\n }\n\n #getTransactionInput(\n mutation: CustomMutation,\n addRetryPredicate: ((shouldRetry: RetryPredicate) => void) | undefined,\n ): TransactionProviderInput {\n return {\n upstreamSchema: this.#params.schema,\n clientGroupID: this.#req.clientGroupID,\n clientID: mutation.clientID,\n mutationID: mutation.id,\n addRetryPredicate,\n };\n }\n\n async #checkAndIncrementLastMutationID(\n transactionHooks: TransactionProviderHooks,\n clientID: string,\n receivedMutationID: number,\n ) {\n const {lastMutationID} = await transactionHooks.updateClientMutationID();\n\n if (receivedMutationID < lastMutationID) {\n throw new MutationAlreadyProcessedError(\n clientID,\n receivedMutationID,\n lastMutationID,\n );\n } else if (receivedMutationID > lastMutationID) {\n throw new OutOfOrderMutation(\n clientID,\n receivedMutationID,\n lastMutationID,\n );\n }\n }\n}\n\nexport class OutOfOrderMutation extends Error {\n constructor(\n clientID: string,\n receivedMutationID: number,\n lastMutationID: number | bigint,\n ) {\n super(\n `Client ${clientID} sent mutation ID ${receivedMutationID} but expected ${lastMutationID}`,\n );\n }\n}\n\nfunction makeAppErrorResponse(\n m: Mutation,\n error: ApplicationError<ReadonlyJSONValue | undefined>,\n): MutationResponse {\n return {\n id: {\n clientID: m.clientID,\n id: m.id,\n },\n result: {\n error: 'app',\n message: error.message,\n ...(error.details ? {details: error.details} : {}),\n },\n };\n}\n\n/** @deprecated Use getMutator instead */\nexport function getMutation(\n // oxlint-disable-next-line no-explicit-any\n mutators: AnyMutatorRegistry | CustomMutatorDefs<any>,\n name: string,\n // oxlint-disable-next-line no-explicit-any\n): CustomMutatorImpl<any> {\n const path = name.split(separatorRe);\n const mutator = getObjectAtPath(mutators, path);\n assert(typeof mutator === 'function', `could not find mutator ${name}`);\n\n if (isMutator(mutator)) {\n // mutator needs to be called with {tx, args, ctx}\n // CustomMutatorImpl is called with (tx, args, ctx)\n return (tx, args, ctx) => mutator.fn({args, ctx, tx});\n }\n\n // oxlint-disable-next-line no-explicit-any\n return mutator as CustomMutatorImpl<any>;\n}\n\nfunction getObjectAtPath(\n obj: Record<string, unknown>,\n path: string[],\n): unknown {\n let current: unknown = obj;\n for (const part of path) {\n if (typeof current !== 'object' || current === null || !(part in current)) {\n return undefined;\n }\n current = (current as Record<string, unknown>)[part];\n }\n return current;\n}\n\n/**\n * Processes internal cleanup mutation that deletes acknowledged mutation results\n * from the upstream database. This runs without LMID tracking since it's an\n * internal operation.\n */\nasync function processCleanupResultsMutation<\n D extends Database<ExtractTransactionType<D>>,\n>(\n dbProvider: D,\n mutation: CustomMutation,\n queryParams: Params,\n lc: LogContext,\n): Promise<void> {\n const parseResult = v.test(mutation.args[0], cleanupResultsArgSchema);\n if (!parseResult.ok) {\n // log-leak-ignore -- valita reports the failing type, not the value\n lc.warn?.('Cleanup mutation has invalid args', parseResult.error);\n return;\n }\n const args: CleanupResultsArg = parseResult.value;\n\n // Determine clientID for transaction input based on cleanup type\n // Note: legacy format without type field is treated as single\n const clientID =\n 'type' in args && args.type === 'bulk' ? args.clientIDs[0] : args.clientID;\n\n // Run in a transaction, using the hook for DB-specific operation.\n // Note: only upstreamSchema is used by deleteMutationResults; the other\n // fields are required by the interface but ignored for this operation.\n await dbProvider.transaction(\n async (_, hooks) => {\n await hooks.deleteMutationResults(args);\n },\n {\n upstreamSchema: queryParams.schema,\n clientGroupID: args.clientGroupID,\n clientID,\n mutationID: 0,\n },\n );\n}\n\ntype DatabaseTransactionPhase = 'open' | 'execute';\nclass DatabaseTransactionError extends Error {\n constructor(phase: DatabaseTransactionPhase, options?: ErrorOptions) {\n super(\n phase === 'open'\n ? `Failed to open database transaction: ${getErrorMessage(options?.cause)}`\n : `Database transaction failed after opening: ${getErrorMessage(options?.cause)}`,\n options,\n );\n this.name = 'DatabaseTransactionError';\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAmGA,IAAM,0BAA0B,OAAU,OAAqC;CAC7E,IAAI;EACF,OAAO,MAAM,GAAG;CAClB,SAAS,OAAO;EACd,IACE,iBAAiB,4BACjB,iBAAiB,sBACjB,iBAAiB,iCACjB,mBAAmB,KAAK,GAExB,MAAM;EAGR,MAAM,yBAAyB,KAAK;CACtC;AACF;;;;AAKA,IAAa,wBAAwB;AAqHrC,eAAsB,oBAGpB,mBACA,cACA,gBACA,gBACA,mBACA,cACyB;CACzB,MAAM,aACJ,OAAO,sBAAsB,YAAY,aAAa,oBAClD,4BAA4B,iBAAiB,IAC7C,iCACE,mBACA,cACA,gBACA,gBACA,mBACA,YACF;CAEN,MAAM,KAAK,iBACT,WAAW,UACX,WAAW,OACb,EAAE,YAAY,eAAe;CAC7B,IAAI;CAEJ,IAAI,WAAW,SAAS,WACtB,IAAI;EACF,WAAW,MAAM,WAAW,QAAQ,KAAK;CAC3C,SAAS,OAAO;EACd,GAAG,QAAQ,6BAA6B,KAAK;EAC7C,MAAM,UAAU,8BAA8B,gBAAgB,KAAK;EACnE,MAAM,UAAU,gBAAgB,KAAK;EACrC,OAAO;GACL,MAAM;GACN,QAAQ;GACR,QAAQ;GACR;GACA,aAAa,CAAC;GACd,GAAI,UAAU,EAAC,QAAO,IAAI,CAAC;EAC7B;CACF;MAEA,WAAW,WAAW;CAGxB,IAAI,cAA4B,CAAC;CAEjC,IAAI;CACJ,IAAI;EACF,WAAW,MAAQ,UAAU,gBAAgB,aAAa;EAC1D,cAAc,SAAS,UAAU,KAAI,OAAM;GACzC,IAAI,EAAE;GACN,UAAU,EAAE;EACd,EAAE;CACJ,SAAS,OAAO;EACd,GAAG,QAAQ,6BAA6B,KAAK;EAC7C,MAAM,UAAU,8BAA8B,gBAAgB,KAAK;EACnE,MAAM,UAAU,gBAAgB,KAAK;EACrC,OAAO;GACL,MAAM;GACN,QAAQ;GACR,QAAQ;GACR;GACA;GACA,GAAI,UAAU,EAAC,QAAO,IAAI,CAAC;EAC7B;CACF;CAEA,IAAI;CACJ,IAAI;EACF,oBAAoB,MAClB,WAAW,SAAS,YAChB,OAAO,YAAY,IAAI,IAAI,WAAW,QAAQ,GAAG,EAAE,YAAY,IAC/D,WAAW,aACf,oBACA,aACF;CACF,SAAS,OAAO;EACd,GAAG,QAAQ,yCAAyC,KAAK;EACzD,MAAM,UAAU,0CAA0C,gBAAgB,KAAK;EAC/E,MAAM,UAAU,gBAAgB,KAAK;EACrC,OAAO;GACL,MAAM;GACN,QAAQ;GACR,QAAQ;GACR;GACA;GACA,GAAI,UAAU,EAAC,QAAO,IAAI,CAAC;EAC7B;CACF;CAEA,IAAI,SAAS,gBAAgB,GAQ3B,OAAO;EANL,MAAM;EACN,QAAQ;EACR,QAAQ;EACR;EACA,SAAS,6BAA6B,SAAS;CAE1C;CAGT,MAAM,YAAgC,CAAC;CACvC,IAAI,iBAAiB;CAErB,IAAI;EACF,MAAM,aAAa,IAAI,WACrB,WAAW,YACX,UACA,mBACA,EACF;EASA,KAAK,MAAM,KAAK,SAAS,WAAW;GAElC,IAAI,EAAE,SAAS,YAAY,EAAE,SAAA,wBAAwC;IACnE,GAAG,QACD,iCAAiC,EAAE,KAAK,cAAc,EAAE,SAAS,EACnE;IACA,IAAI;KACF,MAAM,8BACJ,WAAW,YACX,GACA,mBACA,EACF;KAEA;IACF,SAAS,OAAO;KACd,GAAG,OACD,iDAAiD,EAAE,YACnD,KACF;KAEA;IACF;IACA;GACF;GAEA,OAAO,EAAE,SAAS,UAAU,0BAA0B;GACtD,GAAG,QACD,wBAAwB,EAAE,KAAK,QAAQ,EAAE,GAAG,aAAa,EAAE,SAAS,EACtE;GAEA,IAAI,gBAA+B;GAEnC,MAAM,gBAA+B,OAAM,YAAW;IACpD,gBAAgB;IAChB,MAAM,SAAS,MAAM,WAAW,SAAS,GAAG,OAAO;IACnD,gBAAgB;IAChB,OAAO;GACT;GAEA,IAAI;IACF,MAAM,MAAM,MAAM,8BAChB,WAAW,QAAQ,eAAe,CAAC,CACrC;IACA,UAAU,KAAK,GAAG;IAClB,GAAG,QAAQ,aAAa,EAAE,KAAK,QAAQ,EAAE,GAAG,yBAAyB;IAErE;GACF,SAAS,OAAO;IACd,IAAI,CAAC,mBAAmB,KAAK,GAC3B,MAAM;IAGR,IAAI,kBAAkB,kBAEpB,MAAM,WAAW,6BAA6B,GAAG,KAAK;SACjD,IAAI,kBAAkB,cAE3B,GAAG,QACD,oDAAoD,EAAE,GAAG,cAAc,EAAE,YACzE,KACF;IAGF,GAAG,OACD,yCAAyC,EAAE,GAAG,cAAc,EAAE,YAC9D,KACF;IACA,UAAU,KAAK,qBAAqB,GAAG,KAAK,CAAC;IAE7C;GACF;EACF;EAEA,OAAO;GACL,MAAM;GACN,WAAW;GACX,GAAI,OAAO,WAAW,WAAW,cAC7B,EAAC,QAAQ,WAAW,OAAM,IAC1B,CAAC;EACP;CACF,SAAS,OAAO;EACd,GAAG,QAAQ,kCAAkC,KAAK;EAElD,MAAM,yBAAyB,YAAY,MAAM,cAAc;EAE/D,MAAM,UAAU,gBAAgB,KAAK;EACrC,MAAM,UAAU,gBAAgB,KAAK;EAErC,OAAO;GACL,MAAM;GACN,QAAQ;GACR,QACE,iBAAiB,qBACb,uBACA,iBAAiB,2BACf,WACA;GACR;GACA,aAAa;GACb,GAAI,UAAU,EAAC,QAAO,IAAI,CAAC;EAC7B;CACF;AACF;AAEA,SAAS,4BAEP,OAAmE;CACnE,IAAI,aAAa,OACf,OAAO;EACL,MAAM;EACN,YAAY,MAAM;EAClB,SAAS,MAAM;EACf,SAAS,MAAM;EACf,QAAQ,MAAM,UAAU;EACxB,UAAU,MAAM,YAAY;EAC5B,SAAS,MAAM,WAAW;CAC5B;CAGF,OAAO;EACL,MAAM;EACN,YAAY,MAAM;EAClB,SAAS,MAAM;EACf,UAAU,MAAM;EAChB,QAAQ,MAAM,UAAU;EACxB,aACE,MAAM,iBAAiB,kBACnB,OAAO,YAAY,MAAM,KAAK,IAC9B,MAAM;EACZ,UAAU,MAAM,YAAY;EAC5B,SAAS,MAAM,WAAW;CAC5B;AACF;AAEA,SAAS,iCAGP,YACA,SACA,gBACA,gBACA,mBACA,cACgC;CAChC,OAAO,OAAO,YAAY,YAAY,8BAA8B;CACpE,OACE,OAAO,mBAAmB,aAC1B,0CACF;CAEA,IAAI,0BAA0B,SAC5B,OAAO;EACL,MAAM;EACN;EACA;EACA,SAAS;EACT,QAAQ,KAAA;EACR,UAAW,kBAA2C;EAGtD,SAAU,qBAA6C;CACzD;CAGF,OACE,OAAO,mBAAmB,aAC1B,+BACF;CAEA,OAAO;EACL,MAAM;EACN;EACA;EACA,UAAU;EACV,QAAQ,KAAA;EACR,aACE,0BAA0B,kBACtB,OAAO,YAAY,cAAc,IACjC;EACN,UAAW,qBAA8C;EACzD,SAAS,gBAAgB;CAC3B;AACF;AASA,IAAM,aAAN,MAAgE;CAC9D;CACA;CACA;CACA;CAEA,YAAY,YAAe,KAAe,QAAgB,IAAgB;EACxE,KAAKA,cAAc;EACnB,KAAKC,OAAO;EACZ,KAAKC,UAAU;EACf,KAAKC,MAAM;CACb;;;;;;;;CASA,aACE,YACA,OACA,SACkB;EAClB,IAAI,WAAW,WAAW,GACxB,OAAO,QAAQ,QAAQ,KAAK;EAE9B,MAAM,MAAM,OAAO,gBAAkD;GACnE,IAAI;IACF,OAAQ,MAAM,YAAY,OAAO,OAAO,MAAO;GACjD,SAAS,YAAY;IACnB,KAAKA,IAAI,OACP,8CACA,UACF;IACA,OAAO;GACT;EACF;EACA,OAAO,IAAI,SAAQ,YAAW;GAC5B,IAAI,UAAU,WAAW;GACzB,KAAK,MAAM,eAAe,YACxB,IAAS,WAAW,EAAE,MAAK,QAAO;IAChC,IAAI,KACF,QAAQ,IAAI;SACP,IAAI,EAAE,YAAY,GACvB,QAAQ,KAAK;GAEjB,CAAC;EAEL,CAAC;CACH;CAEA,WAAW,OACT,UACA,OAC8B;EAC9B,IAAI,WAAyC,KAAA;EAG7C,IAAI,UAAU;EACd,SAAS;GAEP,MAAM,aAA+B,CAAC;GACtC,IAAI;IACF,MAAM,MAAM,MAAM,KAAKC,cACrB,UACA,IACA,WACA,gBAAe;KACb,WAAW,KAAK,WAAW;IAC7B,CACF;IACA,IAAI,aAAa,KAAA,GAAW;KAC1B,KAAKD,IAAI,OAEP,YAAY,SAAS,GAAG,cAAc,SAAS,SAAS,8BACxD,QACF;KACA,OAAO,qBAAqB,UAAU,QAAQ;IAChD;IAEA,OAAO;GACT,SAAS,OAAO;IACd,IAAI,iBAAiB,oBAAoB;KACvC,KAAKA,IAAI,QAAQ,KAAK;KACtB,MAAM;IACR;IAEA,IAAI,iBAAiB,+BAA+B;KAClD,KAAKA,IAAI,OAAO,KAAK;KACrB,OAAO;MACL,IAAI;OACF,UAAU,SAAS;OACnB,IAAI,SAAS;MACf;MACA,QAAQ;OACN,OAAO;OACP,SAAS,MAAM;MACjB;KACF;IACF;IAEA,IAAI,aAAa,KAAA,GAAW;KAE1B,KAAKA,IAAI,QAEP,kCAAkC,SAAS,GAAG,cAAc,SAAS,YACrE,KACF;KACA,MAAM;IACR;IAIA,MAAM,gBACJ,iBAAiB,2BACZ,MAAM,SAAS,QAChB;IAUN,IACE,UAAA,KACC,MAAM,KAAKE,aAAa,YAAY,eAAe,OAAO,GAC3D;KACA,KAAKF,IAAI,OAEP,YAAY,SAAS,GAAG,cAAc,SAAS,SAAS,qCAAqC,QAAQ,IACrG,aACF;KACA;KACA;IACF;IAGA,WAAW,yBAAyB,aAAa;IACjD,KAAKA,IAAI,OAEP,6BAA6B,SAAS,GAAG,cAAc,SAAS,SAAS,6BACzE,QACF;IACA;GACF;EACF;CACF;CAEA,MAAM,6BACJ,UACA,UAC2B;EAU3B,OAAO,MAPW,KAAKC,cACrB,gBAEM,aACN,UACA,KAAA,CACF;CAEF;CAEA,MAAMA,cACJ,UACA,IACA,UACA,mBAC2B;EAC3B,IAAI,mBAA6C;EAEjD,IAAI;GAsCF,OAAO,MArCW,KAAKJ,YAAY,YACjC,OAAO,MAAM,qBAAqB;IAEhC,mBAAmB;IAEnB,MAAM,KAAKM,iCACT,kBACA,SAAS,UACT,SAAS,EACX;IAEA,IAAI,aAAa,KAAA,GAAW;KAC1B,KAAKH,IAAI,QAEP,sBAAsB,SAAS,KAAK,QAAQ,SAAS,GAAG,EAC1D;KACA,MAAM,OAAQ,MAAM,GAAG,MAAM,SAAS,MAAM,SAAS,KAAK,EAAE;KAG5D,MAAM,KAAK;MAAC,UAAU,SAAS;MAAU,IAAI,SAAS;KAAE;KACxD,IAAI,SAAS,KAAA,GACX,OAAO;MAAC;MAAI,QAAQ,CAAC;KAAC;KAIxB,MAAM,gBAAkC;MAAC;MAAI,QAAQ,EAAC,KAAI;KAAC;KAC3D,MAAM,iBAAiB,oBAAoB,aAAa;KACxD,OAAO;IACT,OAAO;KACL,MAAM,iBAAiB,qBAAqB,UAAU,QAAQ;KAC9D,MAAM,iBAAiB,oBAAoB,cAAc;KACzD,OAAO;IACT;GACF,GACA,KAAKI,qBAAqB,UAAU,iBAAiB,CACvD;EAGF,SAAS,OAAO;GACd,IACE,mBAAmB,KAAK,KACxB,iBAAiB,sBACjB,iBAAiB,+BAEjB,MAAM;GAGR,MAAM,IAAI,yBAAyB,kBAAkB,EAAC,OAAO,MAAK,CAAC;EACrE;CACF;CAEA,qBACE,UACA,mBAC0B;EAC1B,OAAO;GACL,gBAAgB,KAAKL,QAAQ;GAC7B,eAAe,KAAKD,KAAK;GACzB,UAAU,SAAS;GACnB,YAAY,SAAS;GACrB;EACF;CACF;CAEA,MAAMK,iCACJ,kBACA,UACA,oBACA;EACA,MAAM,EAAC,mBAAkB,MAAM,iBAAiB,uBAAuB;EAEvE,IAAI,qBAAqB,gBACvB,MAAM,IAAI,8BACR,UACA,oBACA,cACF;OACK,IAAI,qBAAqB,gBAC9B,MAAM,IAAI,mBACR,UACA,oBACA,cACF;CAEJ;AACF;AAEA,IAAa,qBAAb,cAAwC,MAAM;CAC5C,YACE,UACA,oBACA,gBACA;EACA,MACE,UAAU,SAAS,oBAAoB,mBAAmB,gBAAgB,gBAC5E;CACF;AACF;AAEA,SAAS,qBACP,GACA,OACkB;CAClB,OAAO;EACL,IAAI;GACF,UAAU,EAAE;GACZ,IAAI,EAAE;EACR;EACA,QAAQ;GACN,OAAO;GACP,SAAS,MAAM;GACf,GAAI,MAAM,UAAU,EAAC,SAAS,MAAM,QAAO,IAAI,CAAC;EAClD;CACF;AACF;;AAGA,SAAgB,YAEd,UACA,MAEwB;CAExB,MAAM,UAAU,gBAAgB,UADnB,KAAK,MAAM,WACkB,CAAI;CAC9C,OAAO,OAAO,YAAY,YAAY,0BAA0B,MAAM;CAEtE,IAAI,UAAU,OAAO,GAGnB,QAAQ,IAAI,MAAM,QAAQ,QAAQ,GAAG;EAAC;EAAM;EAAK;CAAE,CAAC;CAItD,OAAO;AACT;AAEA,SAAS,gBACP,KACA,MACS;CACT,IAAI,UAAmB;CACvB,KAAK,MAAM,QAAQ,MAAM;EACvB,IAAI,OAAO,YAAY,YAAY,YAAY,QAAQ,EAAE,QAAQ,UAC/D;EAEF,UAAW,QAAoC;CACjD;CACA,OAAO;AACT;;;;;;AAOA,eAAe,8BAGb,YACA,UACA,aACA,IACe;CACf,MAAM,cAAc,KAAO,SAAS,KAAK,IAAI,uBAAuB;CACpE,IAAI,CAAC,YAAY,IAAI;EAEnB,GAAG,OAAO,qCAAqC,YAAY,KAAK;EAChE;CACF;CACA,MAAM,OAA0B,YAAY;CAI5C,MAAM,WACJ,UAAU,QAAQ,KAAK,SAAS,SAAS,KAAK,UAAU,KAAK,KAAK;CAKpE,MAAM,WAAW,YACf,OAAO,GAAG,UAAU;EAClB,MAAM,MAAM,sBAAsB,IAAI;CACxC,GACA;EACE,gBAAgB,YAAY;EAC5B,eAAe,KAAK;EACpB;EACA,YAAY;CACd,CACF;AACF;AAGA,IAAM,2BAAN,cAAuC,MAAM;CAC3C,YAAY,OAAiC,SAAwB;EACnE,MACE,UAAU,SACN,wCAAwC,gBAAgB,SAAS,KAAK,MACtE,8CAA8C,gBAAgB,SAAS,KAAK,KAChF,OACF;EACA,KAAK,OAAO;CACd;AACF"}
1
+ {"version":3,"file":"process-mutations.js","names":["ErrorKind.PushFailed","ErrorOrigin.Server","ErrorReason.Parse","v.parse","ErrorReason.UnsupportedPushVersion","ErrorReason.OutOfOrderMutation","ErrorReason.Database","ErrorReason.Internal","v.test"],"sources":["../../../../zero-server/src/process-mutations.ts"],"sourcesContent":["import {\n consoleLogSink,\n type LogContext,\n type LogLevel,\n type LogSink,\n} from '@rocicorp/logger';\nimport {assert} from '../../shared/src/asserts.ts';\nimport {getErrorDetails, getErrorMessage} from '../../shared/src/error.ts';\nimport type {ReadonlyJSONValue} from '../../shared/src/json.ts';\nimport {promiseVoid} from '../../shared/src/resolved-promises.ts';\nimport type {MaybePromise} from '../../shared/src/types.ts';\nimport * as v from '../../shared/src/valita.ts';\nimport {MutationAlreadyProcessedError} from '../../zero-cache/src/services/mutagen/error.ts';\nimport type {ApplicationError} from '../../zero-protocol/src/application-error.ts';\nimport {\n isApplicationError,\n wrapWithApplicationError,\n} from '../../zero-protocol/src/application-error.ts';\nimport {ErrorKind} from '../../zero-protocol/src/error-kind.ts';\nimport {ErrorOrigin} from '../../zero-protocol/src/error-origin.ts';\nimport {ErrorReason} from '../../zero-protocol/src/error-reason.ts';\nimport type {PushFailedBody} from '../../zero-protocol/src/error.ts';\nimport {\n mutateParamsSchema,\n type MutateResponse,\n} from '../../zero-protocol/src/mutate-server.ts';\nimport type {MutationID} from '../../zero-protocol/src/mutation-id.ts';\nimport {\n CLEANUP_RESULTS_MUTATION_NAME,\n cleanupResultsArgSchema,\n type CleanupResultsArg,\n type CustomMutation,\n type Mutation,\n} from '../../zero-protocol/src/mutation.ts';\nimport {\n pushBodySchema,\n type MutationResponse,\n type PushBody,\n} from '../../zero-protocol/src/push.ts';\nimport type {RetryPredicate} from '../../zql/src/mutate/custom.ts';\nimport type {AnyMutatorRegistry} from '../../zql/src/mutate/mutator-registry.ts';\nimport {isMutator} from '../../zql/src/mutate/mutator.ts';\nimport type {CustomMutatorDefs, CustomMutatorImpl} from './custom.ts';\nimport {createLogContext} from './logging.ts';\nimport {separatorRe} from './push-processor.ts';\n\nexport interface TransactionProviderHooks {\n updateClientMutationID: () => Promise<{lastMutationID: number | bigint}>;\n writeMutationResult: (result: MutationResponse) => Promise<void>;\n deleteMutationResults: (args: CleanupResultsArg) => Promise<void>;\n}\n\nexport interface TransactionProviderInput {\n upstreamSchema: string;\n clientGroupID: string;\n clientID: string;\n mutationID: number;\n /**\n * Receives each predicate the mutator registers with `tx.retryOn()`. A\n * `Database` passes it on to the server transaction it creates.\n */\n addRetryPredicate?: ((shouldRetry: RetryPredicate) => void) | undefined;\n}\n\n/**\n * Defines the abstract interface for a database that PushProcessor can execute\n * transactions against.\n */\nexport interface Database<T> {\n transaction: <R>(\n callback: (\n tx: T,\n transactionHooks: TransactionProviderHooks,\n ) => MaybePromise<R>,\n transactionInput?: TransactionProviderInput,\n ) => Promise<R>;\n}\n\nexport type ExtractTransactionType<D> = D extends Database<infer T> ? T : never;\nexport type Params = v.Infer<typeof mutateParamsSchema>;\n\nexport type TransactFn<D extends Database<ExtractTransactionType<D>>> = (\n cb: TransactFnCallback<D>,\n) => Promise<MutationResponse>;\n\nexport type TransactFnCallback<D extends Database<ExtractTransactionType<D>>> =\n (\n tx: ExtractTransactionType<D>,\n mutatorName: string,\n mutatorArgs: ReadonlyJSONValue | undefined,\n ) => Promise<ReadonlyJSONValue | void>;\n\nexport type Parsed<D extends Database<ExtractTransactionType<D>>> = {\n transact: TransactFn<D>;\n mutations: CustomMutation[];\n};\n\ntype MutationPhase = 'preTransaction' | 'transactionPending' | 'postCommit';\n\nconst applicationErrorWrapper = async <T>(fn: () => Promise<T>): Promise<T> => {\n try {\n return await fn();\n } catch (error) {\n if (\n error instanceof DatabaseTransactionError ||\n error instanceof OutOfOrderMutation ||\n error instanceof MutationAlreadyProcessedError ||\n isApplicationError(error)\n ) {\n throw error;\n }\n\n throw wrapWithApplicationError(error);\n }\n};\n\n/**\n * @deprecated Use {@link handleMutateRequest} instead.\n */\nexport const handleMutationRequest = handleMutateRequest;\n\n/**\n * Parsed query params accepted by {@link handleMutateRequest} when the\n * incoming request URL has already been handled by your framework.\n */\nexport type MutateSearchParams = URLSearchParams | Record<string, string>;\n\n/**\n * Runs once per custom mutation in a `/mutate` request.\n *\n * Call `transact` to execute database work for the mutation. Throwing before\n * `transact` persists an application error; throwing after `transact` resolves\n * only affects server-side logging because the mutation is already committed.\n */\nexport type MutateRequestHandler<\n D extends Database<ExtractTransactionType<D>>,\n> = (\n transact: TransactFn<D>,\n mutation: CustomMutation,\n) => Promise<MutationResponse>;\n\nexport type HandleMutateRequestArgs<\n D extends Database<ExtractTransactionType<D>>,\n> = {\n /** Database used to run transactions and store mutation results. */\n dbProvider: D;\n handler: MutateRequestHandler<D>;\n /**\n * Authenticated user ID. Null or undefined means the user is logged out.\n */\n userID: string | null | undefined;\n /** Optional log level for request parsing and execution. */\n logLevel?: LogLevel | undefined;\n /**\n * Destination for this request's log output. When omitted, logs are written\n * with `console.log`/`info`/`warn`/`error` etc.\n *\n * Provide a custom {@link LogSink} to redirect logs — for example into an\n * application's own structured logging pipeline, so that records emitted while\n * processing a push carry the same fields as the rest of the server's logs.\n */\n logSink?: LogSink | undefined;\n} & (\n | {\n /** Fetch request containing both query params and the JSON body. */\n request: Request;\n }\n | {\n /** Parsed query params from the `/mutate` request URL. */\n query: MutateSearchParams;\n /** Parsed JSON body from the `/mutate` request. */\n body: ReadonlyJSONValue;\n }\n);\n\ntype NormalizedMutateRequestArgs<\n D extends Database<ExtractTransactionType<D>>,\n> = {\n readonly dbProvider: D;\n readonly handler: MutateRequestHandler<D>;\n // Note: semantics of undefined differ from HandleMutateRequestArgs.userID.\n // Here, undefined means the app didn't provide a user ID - we do not know if\n // the user is logged in or not. This is legacy behavior needed to support\n // deprecated signatures of handleMutateRequest which did not receive userID\n // from app.\n readonly userID: string | null | undefined;\n readonly logLevel: LogLevel;\n readonly logSink: LogSink;\n} & (\n | {\n readonly type: 'request';\n readonly request: Request;\n }\n | {\n readonly type: 'body';\n readonly queryParams: Record<string, string>;\n readonly jsonBody: ReadonlyJSONValue;\n }\n);\n\n/**\n * Process a `/mutate` request from a Fetch `Request`.\n */\nexport function handleMutateRequest<\n D extends Database<ExtractTransactionType<D>>,\n>(input: HandleMutateRequestArgs<D>): Promise<MutateResponse>;\n\n/**\n * @deprecated Pass a single object instead:\n * `handleMutateRequest({dbProvider, handler, query, body, userID, logLevel})`.\n */\nexport function handleMutateRequest<\n D extends Database<ExtractTransactionType<D>>,\n>(\n dbProvider: D,\n handler: MutateRequestHandler<D>,\n query: MutateSearchParams,\n body: ReadonlyJSONValue,\n logLevel?: LogLevel,\n logSink?: LogSink,\n): Promise<MutateResponse>;\n\n/**\n * @deprecated Pass a single object instead:\n * `handleMutateRequest({dbProvider, handler, request, userID, logLevel})`.\n */\nexport function handleMutateRequest<\n D extends Database<ExtractTransactionType<D>>,\n>(\n dbProvider: D,\n handler: MutateRequestHandler<D>,\n request: Request,\n logLevel?: LogLevel,\n logSink?: LogSink,\n): Promise<MutateResponse>;\n\nexport async function handleMutateRequest<\n D extends Database<ExtractTransactionType<D>>,\n>(\n inputOrDbProvider: HandleMutateRequestArgs<D> | D,\n maybeHandler?: MutateRequestHandler<D> | undefined,\n requestOrQuery?: Request | MutateSearchParams | undefined,\n bodyOrLogLevel?: ReadonlyJSONValue | LogLevel | undefined,\n logLevelOrLogSink?: LogLevel | LogSink | undefined,\n maybeLogSink?: LogSink | undefined,\n): Promise<MutateResponse> {\n const normalized =\n typeof inputOrDbProvider === 'object' && 'handler' in inputOrDbProvider\n ? normalizeMutateRequestInput(inputOrDbProvider)\n : normalizeLegacyMutateRequestArgs(\n inputOrDbProvider,\n maybeHandler,\n requestOrQuery,\n bodyOrLogLevel,\n logLevelOrLogSink,\n maybeLogSink,\n );\n\n const lc = createLogContext(\n normalized.logLevel,\n normalized.logSink,\n ).withContext('PushProcessor');\n let jsonBody: unknown;\n\n if (normalized.type === 'request') {\n try {\n jsonBody = await normalized.request.json();\n } catch (error) {\n lc.error?.('Failed to parse push body', error);\n const message = `Failed to parse push body: ${getErrorMessage(error)}`;\n const details = getErrorDetails(error);\n return {\n kind: ErrorKind.PushFailed,\n origin: ErrorOrigin.Server,\n reason: ErrorReason.Parse,\n message,\n mutationIDs: [],\n ...(details ? {details} : {}),\n } as const satisfies PushFailedBody;\n }\n } else {\n jsonBody = normalized.jsonBody;\n }\n\n let mutationIDs: MutationID[] = [];\n\n let pushBody: PushBody;\n try {\n pushBody = v.parse(jsonBody, pushBodySchema, 'passthrough');\n mutationIDs = pushBody.mutations.map(m => ({\n id: m.id,\n clientID: m.clientID,\n }));\n } catch (error) {\n lc.error?.('Failed to parse push body', error);\n const message = `Failed to parse push body: ${getErrorMessage(error)}`;\n const details = getErrorDetails(error);\n return {\n kind: ErrorKind.PushFailed,\n origin: ErrorOrigin.Server,\n reason: ErrorReason.Parse,\n message,\n mutationIDs,\n ...(details ? {details} : {}),\n } as const satisfies PushFailedBody;\n }\n\n let parsedQueryParams: Params;\n try {\n parsedQueryParams = v.parse(\n normalized.type === 'request'\n ? Object.fromEntries(new URL(normalized.request.url).searchParams)\n : normalized.queryParams,\n mutateParamsSchema,\n 'passthrough',\n );\n } catch (error) {\n lc.error?.('Failed to parse push query parameters', error);\n const message = `Failed to parse push query parameters: ${getErrorMessage(error)}`;\n const details = getErrorDetails(error);\n return {\n kind: ErrorKind.PushFailed,\n origin: ErrorOrigin.Server,\n reason: ErrorReason.Parse,\n message,\n mutationIDs,\n ...(details ? {details} : {}),\n } as const satisfies PushFailedBody;\n }\n\n if (pushBody.pushVersion !== 1) {\n const response = {\n kind: ErrorKind.PushFailed,\n origin: ErrorOrigin.Server,\n reason: ErrorReason.UnsupportedPushVersion,\n mutationIDs,\n message: `Unsupported push version: ${pushBody.pushVersion}`,\n } as const satisfies PushFailedBody;\n return response;\n }\n\n const responses: MutationResponse[] = [];\n let processedCount = 0;\n\n try {\n const transactor = new Transactor(\n normalized.dbProvider,\n pushBody,\n parsedQueryParams,\n lc,\n );\n\n // Each mutation goes through three phases:\n // 1. Pre-transaction: user logic that runs before `transact` is called. If\n // this throws we still advance LMID and persist the failure result.\n // 2. Transaction: the callback passed to `transact`, which can be retried\n // if it fails with an ApplicationError.\n // 3. Post-commit: any logic that runs after `transact` resolves. Failures\n // here are logged but the mutation remains committed.\n for (const m of pushBody.mutations) {\n // Handle internal mutations (like cleanup) directly without user dispatch\n if (m.type === 'custom' && m.name === CLEANUP_RESULTS_MUTATION_NAME) {\n lc.debug?.(\n `Processing internal mutation '${m.name}' (clientID=${m.clientID})`,\n );\n try {\n await processCleanupResultsMutation(\n normalized.dbProvider,\n m,\n parsedQueryParams,\n lc,\n );\n // No response added - this is fire-and-forget\n processedCount++;\n } catch (error) {\n lc.warn?.(\n `Failed to process cleanup mutation for client ${m.clientID}`,\n error,\n );\n // Don't fail the whole push for cleanup errors\n processedCount++;\n }\n continue;\n }\n\n assert(m.type === 'custom', 'Expected custom mutation');\n lc.debug?.(\n `Processing mutation '${m.name}' (id=${m.id}, clientID=${m.clientID})`,\n );\n\n let mutationPhase: MutationPhase = 'preTransaction';\n\n const transactProxy: TransactFn<D> = async innerCb => {\n mutationPhase = 'transactionPending';\n const result = await transactor.transact(m, innerCb);\n mutationPhase = 'postCommit';\n return result;\n };\n\n try {\n const res = await applicationErrorWrapper(() =>\n normalized.handler(transactProxy, m),\n );\n responses.push(res);\n lc.debug?.(`Mutation '${m.name}' (id=${m.id}) completed successfully`);\n\n processedCount++;\n } catch (error) {\n if (!isApplicationError(error)) {\n throw error;\n }\n\n if (mutationPhase === 'preTransaction') {\n // Pre-transaction\n await transactor.persistPreTransactionFailure(m, error);\n } else if (mutationPhase === 'postCommit') {\n // Post-commit\n lc.error?.(\n `Post-commit mutation handler failed for mutation ${m.id} for client ${m.clientID}`,\n error,\n );\n }\n\n lc.warn?.(\n `Application error processing mutation ${m.id} for client ${m.clientID}`,\n error,\n );\n responses.push(makeAppErrorResponse(m, error));\n\n processedCount++;\n }\n }\n\n return {\n kind: 'MutateResponse',\n mutations: responses,\n ...(typeof normalized.userID !== 'undefined'\n ? {userID: normalized.userID}\n : {}),\n } as const satisfies MutateResponse;\n } catch (error) {\n lc.error?.('Failed to process push request', error);\n // only include mutationIDs for mutations that were not processed\n const unprocessedMutationIDs = mutationIDs.slice(processedCount);\n\n const message = getErrorMessage(error);\n const details = getErrorDetails(error);\n\n return {\n kind: ErrorKind.PushFailed,\n origin: ErrorOrigin.Server,\n reason:\n error instanceof OutOfOrderMutation\n ? ErrorReason.OutOfOrderMutation\n : error instanceof DatabaseTransactionError\n ? ErrorReason.Database\n : ErrorReason.Internal,\n message,\n mutationIDs: unprocessedMutationIDs,\n ...(details ? {details} : {}),\n };\n }\n}\n\nfunction normalizeMutateRequestInput<\n D extends Database<ExtractTransactionType<D>>,\n>(input: HandleMutateRequestArgs<D>): NormalizedMutateRequestArgs<D> {\n if ('request' in input) {\n return {\n type: 'request',\n dbProvider: input.dbProvider,\n handler: input.handler,\n request: input.request,\n userID: input.userID ?? null,\n logLevel: input.logLevel ?? 'info',\n logSink: input.logSink ?? consoleLogSink,\n };\n }\n\n return {\n type: 'body',\n dbProvider: input.dbProvider,\n handler: input.handler,\n jsonBody: input.body,\n userID: input.userID ?? null,\n queryParams:\n input.query instanceof URLSearchParams\n ? Object.fromEntries(input.query)\n : input.query,\n logLevel: input.logLevel ?? 'info',\n logSink: input.logSink ?? consoleLogSink,\n };\n}\n\nfunction normalizeLegacyMutateRequestArgs<\n D extends Database<ExtractTransactionType<D>>,\n>(\n dbProvider: D,\n handler: MutateRequestHandler<D> | undefined,\n requestOrQuery: Request | MutateSearchParams | undefined,\n bodyOrLogLevel: ReadonlyJSONValue | LogLevel | undefined,\n logLevelOrLogSink: LogLevel | LogSink | undefined,\n maybeLogSink: LogSink | undefined,\n): NormalizedMutateRequestArgs<D> {\n assert(typeof handler === 'function', 'Handler function is required');\n assert(\n typeof requestOrQuery !== 'undefined',\n 'Request or query parameters are required',\n );\n\n if (requestOrQuery instanceof Request) {\n return {\n type: 'request',\n dbProvider,\n handler,\n request: requestOrQuery,\n userID: undefined,\n logLevel: (bodyOrLogLevel as LogLevel | undefined) ?? 'info',\n // The `request` overload has no `body` slot, so the sink arrives one\n // position earlier than it does in the `query`/`body` overload.\n logSink: (logLevelOrLogSink as LogSink | undefined) ?? consoleLogSink,\n };\n }\n\n assert(\n typeof bodyOrLogLevel !== 'undefined',\n 'JSON body cannot be undefined',\n );\n\n return {\n type: 'body',\n dbProvider,\n handler,\n jsonBody: bodyOrLogLevel,\n userID: undefined,\n queryParams:\n requestOrQuery instanceof URLSearchParams\n ? Object.fromEntries(requestOrQuery)\n : requestOrQuery,\n logLevel: (logLevelOrLogSink as LogLevel | undefined) ?? 'info',\n logSink: maybeLogSink ?? consoleLogSink,\n };\n}\n\n/**\n * The most times a mutator runs for one mutation within one push when its\n * `tx.retryOn()` predicate keeps answering yes: the first run and four\n * re-runs.\n */\nexport const MAX_MUTATOR_RUNS = 5;\n\nclass Transactor<D extends Database<ExtractTransactionType<D>>> {\n readonly #dbProvider: D;\n readonly #req: PushBody;\n readonly #params: Params;\n readonly #lc: LogContext;\n\n constructor(dbProvider: D, req: PushBody, params: Params, lc: LogContext) {\n this.#dbProvider = dbProvider;\n this.#req = req;\n this.#params = params;\n this.#lc = lc;\n }\n\n /**\n * Asks all of the run's `tx.retryOn()` predicates at once whether to run\n * the mutator again. The first to answer yes decides it and the other\n * answers are ignored; it is no once every predicate has answered no. A\n * predicate that throws or rejects answers no: escaping here would skip\n * recording the mutation's result, and the client would send it again.\n */\n #shouldRetry(\n predicates: readonly RetryPredicate[],\n error: unknown,\n attempt: number,\n ): Promise<boolean> {\n if (predicates.length === 0) {\n return Promise.resolve(false);\n }\n const ask = async (shouldRetry: RetryPredicate): Promise<boolean> => {\n try {\n return (await shouldRetry(error, attempt)) === true;\n } catch (retryError) {\n this.#lc.warn?.(\n 'retryOn predicate threw; treating it as no',\n retryError,\n );\n return false;\n }\n };\n return new Promise(resolve => {\n let pending = predicates.length;\n for (const shouldRetry of predicates) {\n void ask(shouldRetry).then(yes => {\n if (yes) {\n resolve(true);\n } else if (--pending === 0) {\n resolve(false);\n }\n });\n }\n });\n }\n\n transact = async (\n mutation: CustomMutation,\n cb: TransactFnCallback<D>,\n ): Promise<MutationResponse> => {\n let appError: ApplicationError | undefined = undefined;\n // Which run of the mutator this is: 1, then 2 for the first re-run, and so\n // on. When a run fails, it is also the number of the re-run being decided.\n let attempt = 1;\n for (;;) {\n // What this run registers with `tx.retryOn()`. Each run starts empty.\n const predicates: RetryPredicate[] = [];\n try {\n const ret = await this.#transactImpl(\n mutation,\n cb,\n appError,\n shouldRetry => {\n predicates.push(shouldRetry);\n },\n );\n if (appError !== undefined) {\n this.#lc.warn?.(\n // log-leak-ignore -- mutation and client ids, not row data\n `Mutation ${mutation.id} for client ${mutation.clientID} was retried after an error`,\n appError,\n );\n return makeAppErrorResponse(mutation, appError);\n }\n\n return ret;\n } catch (error) {\n if (error instanceof OutOfOrderMutation) {\n this.#lc.error?.(error);\n throw error;\n }\n\n if (error instanceof MutationAlreadyProcessedError) {\n this.#lc.warn?.(error);\n return {\n id: {\n clientID: mutation.clientID,\n id: mutation.id,\n },\n result: {\n error: 'alreadyProcessed',\n details: error.message,\n },\n };\n }\n\n if (appError !== undefined) {\n // Retry also failed → internal error, cannot skip mutation\n this.#lc.error?.(\n // log-leak-ignore -- mutation and client ids, not row data\n `Retry also failed for mutation ${mutation.id} for client ${mutation.clientID}`,\n error,\n );\n throw error;\n }\n\n // What the mutator or the database threw, without the wrapper. This\n // includes errors raised at COMMIT, which the mutator cannot catch.\n const originalError =\n error instanceof DatabaseTransactionError\n ? (error.cause ?? error)\n : error;\n\n // One of the run's `tx.retryOn()` predicates says to run it again → do\n // so, in a fresh transaction.\n //\n // `continue` WITHOUT setting `appError` is the whole mechanism: it is\n // `appError` being set that makes the next `#transactImpl` skip\n // `cb(...)`. `#transactImpl` opens a new transaction each pass, so the\n // re-run gets a new snapshot. `#checkAndIncrementLastMutationID` re-runs\n // safely because the failed run rolled its increment back.\n if (\n attempt < MAX_MUTATOR_RUNS &&\n (await this.#shouldRetry(predicates, originalError, attempt))\n ) {\n this.#lc.warn?.(\n // log-leak-ignore -- mutation and client ids, not row data\n `Mutation ${mutation.id} for client ${mutation.clientID} failed, re-running mutator (retry ${attempt})`,\n originalError,\n );\n attempt++;\n continue;\n }\n\n // Not re-run → store error and retry without mutator\n appError = wrapWithApplicationError(originalError);\n this.#lc.warn?.(\n // log-leak-ignore -- mutation and client ids, not row data\n `Error processing mutation ${mutation.id} for client ${mutation.clientID}, retrying without mutator`,\n appError,\n );\n continue;\n }\n }\n };\n\n async persistPreTransactionFailure(\n mutation: CustomMutation,\n appError: ApplicationError<ReadonlyJSONValue | undefined>,\n ): Promise<MutationResponse> {\n // User-land code threw before calling `transact`. We still need to bump the\n // LMID for this mutation and persist the error so that the client knows it failed.\n const ret = await this.#transactImpl(\n mutation,\n // noop callback since there's no transaction to execute\n () => promiseVoid,\n appError,\n undefined,\n );\n return ret;\n }\n\n async #transactImpl(\n mutation: CustomMutation,\n cb: TransactFnCallback<D>,\n appError: ApplicationError | undefined,\n addRetryPredicate: ((shouldRetry: RetryPredicate) => void) | undefined,\n ): Promise<MutationResponse> {\n let transactionPhase: DatabaseTransactionPhase = 'open';\n\n try {\n const ret = await this.#dbProvider.transaction(\n async (dbTx, transactionHooks) => {\n // update the transaction phase to 'execute' after the transaction is opened\n transactionPhase = 'execute';\n\n await this.#checkAndIncrementLastMutationID(\n transactionHooks,\n mutation.clientID,\n mutation.id,\n );\n\n if (appError === undefined) {\n this.#lc.debug?.(\n // log-leak-ignore -- mutator name and mutation id, not row data\n `Executing mutator '${mutation.name}' (id=${mutation.id})`,\n );\n const data = (await cb(dbTx, mutation.name, mutation.args[0])) as\n | ReadonlyJSONValue\n | undefined;\n const id = {clientID: mutation.clientID, id: mutation.id};\n if (data === undefined) {\n return {id, result: {}};\n }\n // Persisted so the data still reaches the client if the push\n // response is lost. A mutator that returns nothing writes no row.\n const successResult: MutationResponse = {id, result: {data}};\n await transactionHooks.writeMutationResult(successResult);\n return successResult;\n } else {\n const mutationResult = makeAppErrorResponse(mutation, appError);\n await transactionHooks.writeMutationResult(mutationResult);\n return mutationResult;\n }\n },\n this.#getTransactionInput(mutation, addRetryPredicate),\n );\n\n return ret;\n } catch (error) {\n if (\n isApplicationError(error) ||\n error instanceof OutOfOrderMutation ||\n error instanceof MutationAlreadyProcessedError\n ) {\n throw error;\n }\n\n throw new DatabaseTransactionError(transactionPhase, {cause: error});\n }\n }\n\n #getTransactionInput(\n mutation: CustomMutation,\n addRetryPredicate: ((shouldRetry: RetryPredicate) => void) | undefined,\n ): TransactionProviderInput {\n return {\n upstreamSchema: this.#params.schema,\n clientGroupID: this.#req.clientGroupID,\n clientID: mutation.clientID,\n mutationID: mutation.id,\n addRetryPredicate,\n };\n }\n\n async #checkAndIncrementLastMutationID(\n transactionHooks: TransactionProviderHooks,\n clientID: string,\n receivedMutationID: number,\n ) {\n const {lastMutationID} = await transactionHooks.updateClientMutationID();\n\n if (receivedMutationID < lastMutationID) {\n throw new MutationAlreadyProcessedError(\n clientID,\n receivedMutationID,\n lastMutationID,\n );\n } else if (receivedMutationID > lastMutationID) {\n throw new OutOfOrderMutation(\n clientID,\n receivedMutationID,\n lastMutationID,\n );\n }\n }\n}\n\nexport class OutOfOrderMutation extends Error {\n constructor(\n clientID: string,\n receivedMutationID: number,\n lastMutationID: number | bigint,\n ) {\n super(\n `Client ${clientID} sent mutation ID ${receivedMutationID} but expected ${lastMutationID}`,\n );\n }\n}\n\nfunction makeAppErrorResponse(\n m: Mutation,\n error: ApplicationError<ReadonlyJSONValue | undefined>,\n): MutationResponse {\n return {\n id: {\n clientID: m.clientID,\n id: m.id,\n },\n result: {\n error: 'app',\n message: error.message,\n ...(error.details ? {details: error.details} : {}),\n },\n };\n}\n\n/** @deprecated Use getMutator instead */\nexport function getMutation(\n // oxlint-disable-next-line no-explicit-any\n mutators: AnyMutatorRegistry | CustomMutatorDefs<any>,\n name: string,\n // oxlint-disable-next-line no-explicit-any\n): CustomMutatorImpl<any> {\n const path = name.split(separatorRe);\n const mutator = getObjectAtPath(mutators, path);\n assert(typeof mutator === 'function', `could not find mutator ${name}`);\n\n if (isMutator(mutator)) {\n // mutator needs to be called with {tx, args, ctx}\n // CustomMutatorImpl is called with (tx, args, ctx)\n return (tx, args, ctx) => mutator.fn({args, ctx, tx});\n }\n\n // oxlint-disable-next-line no-explicit-any\n return mutator as CustomMutatorImpl<any>;\n}\n\nfunction getObjectAtPath(\n obj: Record<string, unknown>,\n path: string[],\n): unknown {\n let current: unknown = obj;\n for (const part of path) {\n if (typeof current !== 'object' || current === null || !(part in current)) {\n return undefined;\n }\n current = (current as Record<string, unknown>)[part];\n }\n return current;\n}\n\n/**\n * Processes internal cleanup mutation that deletes acknowledged mutation results\n * from the upstream database. This runs without LMID tracking since it's an\n * internal operation.\n */\nasync function processCleanupResultsMutation<\n D extends Database<ExtractTransactionType<D>>,\n>(\n dbProvider: D,\n mutation: CustomMutation,\n queryParams: Params,\n lc: LogContext,\n): Promise<void> {\n const parseResult = v.test(mutation.args[0], cleanupResultsArgSchema);\n if (!parseResult.ok) {\n // log-leak-ignore -- valita reports the failing type, not the value\n lc.warn?.('Cleanup mutation has invalid args', parseResult.error);\n return;\n }\n const args: CleanupResultsArg = parseResult.value;\n\n // Determine clientID for transaction input based on cleanup type\n // Note: legacy format without type field is treated as single\n const clientID =\n 'type' in args && args.type === 'bulk' ? args.clientIDs[0] : args.clientID;\n\n // Run in a transaction, using the hook for DB-specific operation.\n // Note: only upstreamSchema is used by deleteMutationResults; the other\n // fields are required by the interface but ignored for this operation.\n await dbProvider.transaction(\n async (_, hooks) => {\n await hooks.deleteMutationResults(args);\n },\n {\n upstreamSchema: queryParams.schema,\n clientGroupID: args.clientGroupID,\n clientID,\n mutationID: 0,\n },\n );\n}\n\ntype DatabaseTransactionPhase = 'open' | 'execute';\nclass DatabaseTransactionError extends Error {\n constructor(phase: DatabaseTransactionPhase, options?: ErrorOptions) {\n super(\n phase === 'open'\n ? `Failed to open database transaction: ${getErrorMessage(options?.cause)}`\n : `Database transaction failed after opening: ${getErrorMessage(options?.cause)}`,\n options,\n );\n this.name = 'DatabaseTransactionError';\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"push-processor.js","names":["#dbProvider","#logLevel","#logSink","#context","#processMutation","#dispatchMutation"],"sources":["../../../../zero-server/src/push-processor.ts"],"sourcesContent":["import {consoleLogSink, type LogLevel, type LogSink} from '@rocicorp/logger';\nimport {assert} from '../../shared/src/asserts.ts';\nimport type {ReadonlyJSONValue} from '../../shared/src/json.ts';\nimport {must} from '../../shared/src/must.ts';\nimport {getValueAtPath} from '../../shared/src/object-traversal.ts';\nimport type {MutateResponse} from '../../zero-protocol/src/mutate-server.ts';\nimport type {CustomMutation} from '../../zero-protocol/src/mutation.ts';\nimport {type MutationResponse} from '../../zero-protocol/src/push.ts';\nimport {\n type Database,\n type ExtractTransactionType,\n handleMutateRequest,\n type TransactFn,\n} from '../../zero-server/src/process-mutations.ts';\nimport type {Schema} from '../../zero-types/src/schema.ts';\nimport type {Transaction} from '../../zql/src/mutate/custom.ts';\nimport type {AnyMutatorRegistry} from '../../zql/src/mutate/mutator-registry.ts';\nimport {isMutator} from '../../zql/src/mutate/mutator.ts';\nimport type {CustomMutatorDefs} from './custom.ts';\n\nexport const separatorRe = /[.|]/;\n\nexport class PushProcessor<\n _S extends Schema,\n D extends Database<ExtractTransactionType<D>>,\n MD extends AnyMutatorRegistry | CustomMutatorDefs<ExtractTransactionType<D>>,\n C = undefined,\n> {\n readonly #dbProvider: D;\n readonly #logLevel: LogLevel;\n readonly #logSink: LogSink;\n readonly #context: C;\n\n /**\n * @param logSink Destination for this processor's log output. When omitted,\n * logs are written with `console.log`/`info`/`warn`/`error` etc. Provide a\n * custom {@link LogSink} to redirect them into the application's own\n * logging pipeline.\n */\n constructor(\n dbProvider: D,\n context?: C,\n logLevel: LogLevel = 'info',\n logSink: LogSink = consoleLogSink,\n ) {\n this.#dbProvider = dbProvider;\n this.#context = context as C;\n this.#logLevel = logLevel;\n this.#logSink = logSink;\n }\n\n /**\n * Processes a push request from zero-cache.\n * This function will parse the request, check the protocol version, and process each mutation in the request.\n * - If a mutation is out of order: processing will stop and an error will be returned. The zero client will retry the mutation.\n * - If a mutation has already been processed: it will be skipped and the processing will continue.\n * - If a mutation receives an application error: it will be skipped, the error will be returned to the client, and processing will continue.\n *\n * @param mutators the custom mutators for the application\n * @param queryString the query string from the request sent by zero-cache. This will include zero's postgres schema name and appID.\n * @param body the body of the request sent by zero-cache as a JSON object.\n */\n process(\n mutators: MD,\n queryString: URLSearchParams | Record<string, string>,\n body: ReadonlyJSONValue,\n ): Promise<MutateResponse>;\n\n /**\n * This override gets the query string and the body from a Request object.\n *\n * @param mutators the custom mutators for the application\n * @param request A `Request` object.\n */\n process(mutators: MD, request: Request): Promise<MutateResponse>;\n process(\n mutators: MD,\n queryOrQueryString: Request | URLSearchParams | Record<string, string>,\n body?: ReadonlyJSONValue,\n ): Promise<MutateResponse> {\n if (queryOrQueryString instanceof Request) {\n return handleMutateRequest(\n this.#dbProvider,\n (transact, mutation) =>\n this.#processMutation(mutators, transact, mutation),\n queryOrQueryString,\n this.#logLevel,\n this.#logSink,\n );\n }\n return handleMutateRequest(\n this.#dbProvider,\n (transact, mutation) =>\n this.#processMutation(mutators, transact, mutation),\n queryOrQueryString,\n must(body, 'body is required when using query params directly'),\n this.#logLevel,\n this.#logSink,\n );\n }\n\n #processMutation(\n mutators: MD,\n transact: TransactFn<D>,\n _mutation: CustomMutation,\n ): Promise<MutationResponse> {\n return transact((tx, name, args) =>\n this.#dispatchMutation(mutators, tx, name, args),\n );\n }\n\n #dispatchMutation(\n mutators: MD,\n dbTx: ExtractTransactionType<D>,\n key: string,\n args: ReadonlyJSONValue | undefined,\n ): Promise<ReadonlyJSONValue | void> {\n // Legacy mutators used | as a separator, new mutators use .\n const mutator = getValueAtPath(mutators, key, separatorRe);\n assert(typeof mutator === 'function', `could not find mutator ${key}`);\n if (isMutator(mutator)) {\n return mutator.fn({\n args,\n ctx: this.#context,\n tx: dbTx as Transaction<Schema, unknown>,\n });\n }\n return mutator(dbTx, args);\n }\n}\n"],"mappings":";;;;;;;;AAoBA,IAAa,cAAc;AAE3B,IAAa,gBAAb,MAKE;CACA;CACA;CACA;CACA;;;;;;;CAQA,YACE,YACA,SACA,WAAqB,QACrB,UAAmB,gBACnB;EACA,KAAKA,cAAc;EACnB,KAAKG,WAAW;EAChB,KAAKF,YAAY;EACjB,KAAKC,WAAW;CAClB;CA0BA,QACE,UACA,oBACA,MACyB;EACzB,IAAI,8BAA8B,SAChC,OAAO,oBACL,KAAKF,cACJ,UAAU,aACT,KAAKI,iBAAiB,UAAU,UAAU,QAAQ,GACpD,oBACA,KAAKH,WACL,KAAKC,QACP;EAEF,OAAO,oBACL,KAAKF,cACJ,UAAU,aACT,KAAKI,iBAAiB,UAAU,UAAU,QAAQ,GACpD,oBACA,KAAK,MAAM,mDAAmD,GAC9D,KAAKH,WACL,KAAKC,QACP;CACF;CAEA,iBACE,UACA,UACA,WAC2B;EAC3B,OAAO,UAAU,IAAI,MAAM,SACzB,KAAKG,kBAAkB,UAAU,IAAI,MAAM,IAAI,CACjD;CACF;CAEA,kBACE,UACA,MACA,KACA,MACmC;EAEnC,MAAM,UAAU,eAAe,UAAU,KAAK,WAAW;EACzD,OAAO,OAAO,YAAY,YAAY,0BAA0B,KAAK;EACrE,IAAI,UAAU,OAAO,GACnB,OAAO,QAAQ,GAAG;GAChB;GACA,KAAK,KAAKF;GACV,IAAI;EACN,CAAC;EAEH,OAAO,QAAQ,MAAM,IAAI;CAC3B;AACF"}
1
+ {"version":3,"file":"push-processor.js","names":[],"sources":["../../../../zero-server/src/push-processor.ts"],"sourcesContent":["import {consoleLogSink, type LogLevel, type LogSink} from '@rocicorp/logger';\nimport {assert} from '../../shared/src/asserts.ts';\nimport type {ReadonlyJSONValue} from '../../shared/src/json.ts';\nimport {must} from '../../shared/src/must.ts';\nimport {getValueAtPath} from '../../shared/src/object-traversal.ts';\nimport type {MutateResponse} from '../../zero-protocol/src/mutate-server.ts';\nimport type {CustomMutation} from '../../zero-protocol/src/mutation.ts';\nimport {type MutationResponse} from '../../zero-protocol/src/push.ts';\nimport {\n type Database,\n type ExtractTransactionType,\n handleMutateRequest,\n type TransactFn,\n} from '../../zero-server/src/process-mutations.ts';\nimport type {Schema} from '../../zero-types/src/schema.ts';\nimport type {Transaction} from '../../zql/src/mutate/custom.ts';\nimport type {AnyMutatorRegistry} from '../../zql/src/mutate/mutator-registry.ts';\nimport {isMutator} from '../../zql/src/mutate/mutator.ts';\nimport type {CustomMutatorDefs} from './custom.ts';\n\nexport const separatorRe = /[.|]/;\n\nexport class PushProcessor<\n _S extends Schema,\n D extends Database<ExtractTransactionType<D>>,\n MD extends AnyMutatorRegistry | CustomMutatorDefs<ExtractTransactionType<D>>,\n C = undefined,\n> {\n readonly #dbProvider: D;\n readonly #logLevel: LogLevel;\n readonly #logSink: LogSink;\n readonly #context: C;\n\n /**\n * @param logSink Destination for this processor's log output. When omitted,\n * logs are written with `console.log`/`info`/`warn`/`error` etc. Provide a\n * custom {@link LogSink} to redirect them into the application's own\n * logging pipeline.\n */\n constructor(\n dbProvider: D,\n context?: C,\n logLevel: LogLevel = 'info',\n logSink: LogSink = consoleLogSink,\n ) {\n this.#dbProvider = dbProvider;\n this.#context = context as C;\n this.#logLevel = logLevel;\n this.#logSink = logSink;\n }\n\n /**\n * Processes a push request from zero-cache.\n * This function will parse the request, check the protocol version, and process each mutation in the request.\n * - If a mutation is out of order: processing will stop and an error will be returned. The zero client will retry the mutation.\n * - If a mutation has already been processed: it will be skipped and the processing will continue.\n * - If a mutation receives an application error: it will be skipped, the error will be returned to the client, and processing will continue.\n *\n * @param mutators the custom mutators for the application\n * @param queryString the query string from the request sent by zero-cache. This will include zero's postgres schema name and appID.\n * @param body the body of the request sent by zero-cache as a JSON object.\n */\n process(\n mutators: MD,\n queryString: URLSearchParams | Record<string, string>,\n body: ReadonlyJSONValue,\n ): Promise<MutateResponse>;\n\n /**\n * This override gets the query string and the body from a Request object.\n *\n * @param mutators the custom mutators for the application\n * @param request A `Request` object.\n */\n process(mutators: MD, request: Request): Promise<MutateResponse>;\n process(\n mutators: MD,\n queryOrQueryString: Request | URLSearchParams | Record<string, string>,\n body?: ReadonlyJSONValue,\n ): Promise<MutateResponse> {\n if (queryOrQueryString instanceof Request) {\n return handleMutateRequest(\n this.#dbProvider,\n (transact, mutation) =>\n this.#processMutation(mutators, transact, mutation),\n queryOrQueryString,\n this.#logLevel,\n this.#logSink,\n );\n }\n return handleMutateRequest(\n this.#dbProvider,\n (transact, mutation) =>\n this.#processMutation(mutators, transact, mutation),\n queryOrQueryString,\n must(body, 'body is required when using query params directly'),\n this.#logLevel,\n this.#logSink,\n );\n }\n\n #processMutation(\n mutators: MD,\n transact: TransactFn<D>,\n _mutation: CustomMutation,\n ): Promise<MutationResponse> {\n return transact((tx, name, args) =>\n this.#dispatchMutation(mutators, tx, name, args),\n );\n }\n\n #dispatchMutation(\n mutators: MD,\n dbTx: ExtractTransactionType<D>,\n key: string,\n args: ReadonlyJSONValue | undefined,\n ): Promise<ReadonlyJSONValue | void> {\n // Legacy mutators used | as a separator, new mutators use .\n const mutator = getValueAtPath(mutators, key, separatorRe);\n assert(typeof mutator === 'function', `could not find mutator ${key}`);\n if (isMutator(mutator)) {\n return mutator.fn({\n args,\n ctx: this.#context,\n tx: dbTx as Transaction<Schema, unknown>,\n });\n }\n return mutator(dbTx, args);\n }\n}\n"],"mappings":";;;;;;;;AAoBA,IAAa,cAAc;AAE3B,IAAa,gBAAb,MAKE;CACA;CACA;CACA;CACA;;;;;;;CAQA,YACE,YACA,SACA,WAAqB,QACrB,UAAmB,gBACnB;EACA,KAAK,cAAc;EACnB,KAAK,WAAW;EAChB,KAAK,YAAY;EACjB,KAAK,WAAW;CAClB;CA0BA,QACE,UACA,oBACA,MACyB;EACzB,IAAI,8BAA8B,SAChC,OAAO,oBACL,KAAK,cACJ,UAAU,aACT,KAAK,iBAAiB,UAAU,UAAU,QAAQ,GACpD,oBACA,KAAK,WACL,KAAK,QACP;EAEF,OAAO,oBACL,KAAK,cACJ,UAAU,aACT,KAAK,iBAAiB,UAAU,UAAU,QAAQ,GACpD,oBACA,KAAK,MAAM,mDAAmD,GAC9D,KAAK,WACL,KAAK,QACP;CACF;CAEA,iBACE,UACA,UACA,WAC2B;EAC3B,OAAO,UAAU,IAAI,MAAM,SACzB,KAAK,kBAAkB,UAAU,IAAI,MAAM,IAAI,CACjD;CACF;CAEA,kBACE,UACA,MACA,KACA,MACmC;EAEnC,MAAM,UAAU,eAAe,UAAU,KAAK,WAAW;EACzD,OAAO,OAAO,YAAY,YAAY,0BAA0B,KAAK;EACrE,IAAI,UAAU,OAAO,GACnB,OAAO,QAAQ,GAAG;GAChB;GACA,KAAK,KAAK;GACV,IAAI;EACN,CAAC;EAEH,OAAO,QAAQ,MAAM,IAAI;CAC3B;AACF"}
@@ -105,7 +105,8 @@ async function transform(args, apiName) {
105
105
  };
106
106
  }
107
107
  try {
108
- const ast = mapAST(asQueryInternals(finalQuery).ast, nameMapper);
108
+ const q = asQueryInternals(finalQuery);
109
+ const ast = mapAST(q.ast, nameMapper);
109
110
  return {
110
111
  id: req.id,
111
112
  name: req.name,
@@ -1 +1 @@
1
- {"version":3,"file":"process-queries.js","names":[],"sources":["../../../../../zero-server/src/queries/process-queries.ts"],"sourcesContent":["import type {LogLevel} from '@rocicorp/logger';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport {getErrorDetails, getErrorMessage} from '../../../shared/src/error.ts';\nimport type {ReadonlyJSONValue} from '../../../shared/src/json.ts';\nimport type {MaybePromise} from '../../../shared/src/types.ts';\nimport * as v from '../../../shared/src/valita.ts';\nimport {mapAST} from '../../../zero-protocol/src/ast.ts';\nimport {\n transformRequestMessageSchema,\n type TransformRequestMessage,\n type TransformResponseBody,\n} from '../../../zero-protocol/src/custom-queries.ts';\nimport {ErrorKind} from '../../../zero-protocol/src/error-kind.ts';\nimport {ErrorOrigin} from '../../../zero-protocol/src/error-origin.ts';\nimport {ErrorReason} from '../../../zero-protocol/src/error-reason.ts';\nimport type {QueryResponse} from '../../../zero-protocol/src/query-server.ts';\nimport {clientToServer} from '../../../zero-schema/src/name-mapper.ts';\nimport type {Schema} from '../../../zero-types/src/schema.ts';\nimport {QueryParseError} from '../../../zql/src/query/error.ts';\nimport {asQueryInternals} from '../../../zql/src/query/query-internals.ts';\nimport type {AnyQuery} from '../../../zql/src/query/query.ts';\nimport {createLogContext} from '../logging.ts';\n\n/**\n * Invokes the callback `cb` for each query in the request or JSON body.\n * The callback should return a Query or Promise<Query> that is the transformed result.\n *\n * This function will call `cb` in parallel for each query found in the request.\n *\n * If you need to limit concurrency, you can use a library like `p-limit` to wrap the `cb` function.\n * @deprecated Use {@link handleQueryRequest} instead.\n */\nexport function handleGetQueriesRequest<S extends Schema>(\n cb: (\n name: string,\n args: readonly ReadonlyJSONValue[],\n ) => MaybePromise<{query: AnyQuery} | AnyQuery>,\n schema: S,\n requestOrJsonBody: Request | ReadonlyJSONValue,\n logLevel: LogLevel = 'info',\n): Promise<QueryResponse> {\n return transform(\n normalizeLegacyQueryRequestArgs(cb, schema, requestOrJsonBody, logLevel),\n 'getQueries',\n );\n}\n\n/**\n * Invokes the callback `cb` for each query in the request or JSON body.\n * The callback should return a Query or Promise<Query> that is the transformed result.\n *\n * This function will call `cb` in parallel for each query found in the request.\n *\n * If you need to limit concurrency, you can use a library like `p-limit` to wrap the `cb` function.\n * @deprecated Use {@link handleQueryRequest} instead.\n */\nexport function handleTransformRequest<S extends Schema>(\n cb: (\n name: string,\n args: readonly ReadonlyJSONValue[],\n ) => MaybePromise<{query: AnyQuery} | AnyQuery>,\n schema: S,\n requestOrJsonBody: Request | ReadonlyJSONValue,\n logLevel: LogLevel = 'info',\n): Promise<QueryResponse> {\n return transform(\n normalizeLegacyQueryRequestArgs(cb, schema, requestOrJsonBody, logLevel),\n 'transform',\n );\n}\n\n/**\n * Parsed query params accepted by {@link handleQueryRequest} when the\n * incoming request URL has already been handled by your framework.\n */\nexport type QuerySearchParams = URLSearchParams | Record<string, string>;\n\nexport type HandleQueryRequestArgs<S extends Schema> = {\n /** Callback that transforms each requested query into a `Query`. */\n handler: QueryRequestHandler;\n /** Schema used when building the returned ASTs. */\n schema: S;\n /**\n * Authenticated user ID. Null or undefined means the user is logged out.\n */\n userID: string | null | undefined;\n /** Optional log level for request parsing and execution. */\n logLevel?: LogLevel | undefined;\n} & (\n | {\n /** Fetch request containing the `/query` JSON body. */\n request: Request;\n }\n | {\n /** Parsed query params from the `/query` request URL. */\n query: QuerySearchParams;\n /** Parsed JSON body from the `/query` request. */\n body: ReadonlyJSONValue;\n }\n);\n\ntype NormalizedQueryRequestArgs<S extends Schema> = {\n readonly schema: S;\n readonly handler: LegacyQueryRequestHandler;\n // Note: semantics of undefined differ from HandleQueryRequestArgs.userID.\n // Here, undefined means the app didn't provide a user ID - we do not know if\n // the user is logged in or not. This is legacy behavior needed to support\n // deprecated signatures of handleQueryRequest which did not receive userID\n // from app.\n readonly userID: string | null | undefined;\n readonly logLevel: LogLevel;\n} & (\n | {\n readonly type: 'request';\n readonly request: Request;\n }\n | {\n readonly type: 'body';\n readonly jsonBody: ReadonlyJSONValue;\n }\n);\n\n/**\n * Process a `/query` request from a Fetch `Request`.\n */\nexport function handleQueryRequest<S extends Schema>(\n input: HandleQueryRequestArgs<S>,\n): Promise<QueryResponse>;\n\n/**\n * @deprecated Pass a single object instead:\n * `handleQueryRequest({handler, schema, request, userID, logLevel})`.\n */\nexport function handleQueryRequest<S extends Schema>(\n transformQuery: QueryRequestHandler,\n schema: S,\n request: Request,\n logLevel?: LogLevel,\n): Promise<QueryResponse>;\n\n/**\n * @deprecated Pass a single object instead:\n * `handleQueryRequest({handler, schema, body, userID, logLevel})`.\n */\nexport function handleQueryRequest<S extends Schema>(\n transformQuery: QueryRequestHandler,\n schema: S,\n jsonBody: ReadonlyJSONValue,\n logLevel?: LogLevel,\n): Promise<QueryResponse>;\n\nexport function handleQueryRequest<S extends Schema>(\n inputOrTransformQuery: HandleQueryRequestArgs<S> | QueryRequestHandler,\n schema?: S,\n requestOrJsonBody?: Request | ReadonlyJSONValue,\n logLevel?: LogLevel,\n): Promise<QueryResponse> {\n const normalized =\n typeof inputOrTransformQuery === 'object' &&\n 'handler' in inputOrTransformQuery\n ? normalizeQueryRequestInput(inputOrTransformQuery)\n : normalizeLegacyQueryRequestArgs(\n wrapQueryRequestHandler(inputOrTransformQuery),\n schema,\n requestOrJsonBody,\n logLevel,\n );\n\n return transform(normalized, 'query');\n}\n\nfunction normalizeQueryRequestInput<S extends Schema>(\n input: HandleQueryRequestArgs<S>,\n): NormalizedQueryRequestArgs<S> {\n return 'request' in input\n ? normalizeLegacyQueryRequestArgs(\n wrapQueryRequestHandler(input.handler),\n input.schema,\n input.request,\n input.logLevel,\n input.userID ?? null,\n )\n : normalizeLegacyQueryRequestArgs(\n wrapQueryRequestHandler(input.handler),\n input.schema,\n input.body,\n input.logLevel,\n input.userID ?? null,\n );\n}\n\nfunction normalizeLegacyQueryRequestArgs<S extends Schema>(\n handler: LegacyQueryRequestHandler,\n schema: S | undefined,\n requestOrJsonBody: Request | ReadonlyJSONValue | undefined,\n logLevel: LogLevel | undefined,\n userID?: string | null,\n): NormalizedQueryRequestArgs<S> {\n assert(\n typeof schema !== 'undefined',\n 'Schema must be provided when using handleQueryRequest',\n );\n\n if (requestOrJsonBody instanceof Request) {\n return {\n type: 'request',\n handler,\n schema,\n request: requestOrJsonBody,\n userID,\n logLevel: logLevel ?? 'info',\n };\n }\n\n assert(\n typeof requestOrJsonBody !== 'undefined',\n 'JSON body cannot be undefined',\n );\n\n return {\n type: 'body',\n handler,\n schema,\n jsonBody: requestOrJsonBody,\n userID,\n logLevel: logLevel ?? 'info',\n };\n}\n\nasync function transform<S extends Schema>(\n args: NormalizedQueryRequestArgs<S>,\n apiName: 'query' | 'transform' | 'getQueries',\n): Promise<QueryResponse> {\n const lc = createLogContext(args.logLevel).withContext('TransformRequest');\n let parsed: TransformRequestMessage;\n let queryIDs: string[] = [];\n try {\n let body: ReadonlyJSONValue;\n if (args.type === 'request') {\n body = await args.request.json();\n } else {\n body = args.jsonBody;\n }\n\n // Strip, so a newer zero-cache can add fields to the request.\n parsed = v.parse(body, transformRequestMessageSchema, 'strip');\n\n queryIDs = parsed[1].map(r => r.id);\n } catch (error) {\n lc.error?.(`Failed to parse ${apiName} request`, error);\n\n const message = `Failed to parse ${apiName} request: ${getErrorMessage(error)}`;\n const details = getErrorDetails(error);\n\n return {\n kind: ErrorKind.TransformFailed,\n origin: ErrorOrigin.Server,\n reason: ErrorReason.Parse,\n message,\n queryIDs,\n ...(details ? {details} : {}),\n };\n }\n\n try {\n const nameMapper = clientToServer(args.schema.tables);\n\n const responses: TransformResponseBody = await Promise.all(\n parsed[1].map(async req => {\n let finalQuery: AnyQuery;\n try {\n const result = await args.handler(req.name, req.args);\n finalQuery = 'query' in result ? result.query : result;\n } catch (error) {\n const message = getErrorMessage(error);\n const details = getErrorDetails(error);\n\n return {\n error: error instanceof QueryParseError ? 'parse' : 'app',\n id: req.id,\n name: req.name,\n message,\n ...(details ? {details} : {}),\n };\n }\n\n try {\n const q = asQueryInternals(finalQuery);\n const ast = mapAST(q.ast, nameMapper);\n\n return {\n id: req.id,\n name: req.name,\n ast,\n };\n } catch (error) {\n lc.error?.('Failed to map AST', error);\n throw error;\n }\n }),\n );\n\n return {\n kind: 'QueryResponse',\n queries: responses,\n ...(typeof args.userID !== 'undefined' ? {userID: args.userID} : {}),\n } as const satisfies QueryResponse;\n } catch (e) {\n const message = getErrorMessage(e);\n const details = getErrorDetails(e);\n\n return {\n kind: ErrorKind.TransformFailed,\n origin: ErrorOrigin.Server,\n reason: ErrorReason.Internal,\n message,\n queryIDs,\n ...(details ? {details} : {}),\n };\n }\n}\n\n/**\n * A function that transforms a query by name and arguments into a Query object.\n *\n * @param name - The name of the query (can be dot-separated for nested queries)\n * @param args - The arguments to pass to the query (can be undefined)\n * @returns A Query object\n */\nexport type QueryRequestHandler = (\n name: string,\n args: ReadonlyJSONValue | undefined,\n) => AnyQuery;\n\n/** @deprecated Use `QueryRequestHandler` instead. */\nexport type TransformQueryFunction = QueryRequestHandler;\n\nexport type LegacyQueryRequestHandler = (\n name: string,\n args: readonly ReadonlyJSONValue[],\n) => MaybePromise<{query: AnyQuery} | AnyQuery>;\n\nfunction wrapQueryRequestHandler(\n handler: QueryRequestHandler,\n): LegacyQueryRequestHandler {\n return (name, args) => handler(name, args[0]);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"process-queries.js","names":["v.parse","ErrorKind.TransformFailed","ErrorOrigin.Server","ErrorReason.Parse","ErrorReason.Internal"],"sources":["../../../../../zero-server/src/queries/process-queries.ts"],"sourcesContent":["import type {LogLevel} from '@rocicorp/logger';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport {getErrorDetails, getErrorMessage} from '../../../shared/src/error.ts';\nimport type {ReadonlyJSONValue} from '../../../shared/src/json.ts';\nimport type {MaybePromise} from '../../../shared/src/types.ts';\nimport * as v from '../../../shared/src/valita.ts';\nimport {mapAST} from '../../../zero-protocol/src/ast.ts';\nimport {\n transformRequestMessageSchema,\n type TransformRequestMessage,\n type TransformResponseBody,\n} from '../../../zero-protocol/src/custom-queries.ts';\nimport {ErrorKind} from '../../../zero-protocol/src/error-kind.ts';\nimport {ErrorOrigin} from '../../../zero-protocol/src/error-origin.ts';\nimport {ErrorReason} from '../../../zero-protocol/src/error-reason.ts';\nimport type {QueryResponse} from '../../../zero-protocol/src/query-server.ts';\nimport {clientToServer} from '../../../zero-schema/src/name-mapper.ts';\nimport type {Schema} from '../../../zero-types/src/schema.ts';\nimport {QueryParseError} from '../../../zql/src/query/error.ts';\nimport {asQueryInternals} from '../../../zql/src/query/query-internals.ts';\nimport type {AnyQuery} from '../../../zql/src/query/query.ts';\nimport {createLogContext} from '../logging.ts';\n\n/**\n * Invokes the callback `cb` for each query in the request or JSON body.\n * The callback should return a Query or Promise<Query> that is the transformed result.\n *\n * This function will call `cb` in parallel for each query found in the request.\n *\n * If you need to limit concurrency, you can use a library like `p-limit` to wrap the `cb` function.\n * @deprecated Use {@link handleQueryRequest} instead.\n */\nexport function handleGetQueriesRequest<S extends Schema>(\n cb: (\n name: string,\n args: readonly ReadonlyJSONValue[],\n ) => MaybePromise<{query: AnyQuery} | AnyQuery>,\n schema: S,\n requestOrJsonBody: Request | ReadonlyJSONValue,\n logLevel: LogLevel = 'info',\n): Promise<QueryResponse> {\n return transform(\n normalizeLegacyQueryRequestArgs(cb, schema, requestOrJsonBody, logLevel),\n 'getQueries',\n );\n}\n\n/**\n * Invokes the callback `cb` for each query in the request or JSON body.\n * The callback should return a Query or Promise<Query> that is the transformed result.\n *\n * This function will call `cb` in parallel for each query found in the request.\n *\n * If you need to limit concurrency, you can use a library like `p-limit` to wrap the `cb` function.\n * @deprecated Use {@link handleQueryRequest} instead.\n */\nexport function handleTransformRequest<S extends Schema>(\n cb: (\n name: string,\n args: readonly ReadonlyJSONValue[],\n ) => MaybePromise<{query: AnyQuery} | AnyQuery>,\n schema: S,\n requestOrJsonBody: Request | ReadonlyJSONValue,\n logLevel: LogLevel = 'info',\n): Promise<QueryResponse> {\n return transform(\n normalizeLegacyQueryRequestArgs(cb, schema, requestOrJsonBody, logLevel),\n 'transform',\n );\n}\n\n/**\n * Parsed query params accepted by {@link handleQueryRequest} when the\n * incoming request URL has already been handled by your framework.\n */\nexport type QuerySearchParams = URLSearchParams | Record<string, string>;\n\nexport type HandleQueryRequestArgs<S extends Schema> = {\n /** Callback that transforms each requested query into a `Query`. */\n handler: QueryRequestHandler;\n /** Schema used when building the returned ASTs. */\n schema: S;\n /**\n * Authenticated user ID. Null or undefined means the user is logged out.\n */\n userID: string | null | undefined;\n /** Optional log level for request parsing and execution. */\n logLevel?: LogLevel | undefined;\n} & (\n | {\n /** Fetch request containing the `/query` JSON body. */\n request: Request;\n }\n | {\n /** Parsed query params from the `/query` request URL. */\n query: QuerySearchParams;\n /** Parsed JSON body from the `/query` request. */\n body: ReadonlyJSONValue;\n }\n);\n\ntype NormalizedQueryRequestArgs<S extends Schema> = {\n readonly schema: S;\n readonly handler: LegacyQueryRequestHandler;\n // Note: semantics of undefined differ from HandleQueryRequestArgs.userID.\n // Here, undefined means the app didn't provide a user ID - we do not know if\n // the user is logged in or not. This is legacy behavior needed to support\n // deprecated signatures of handleQueryRequest which did not receive userID\n // from app.\n readonly userID: string | null | undefined;\n readonly logLevel: LogLevel;\n} & (\n | {\n readonly type: 'request';\n readonly request: Request;\n }\n | {\n readonly type: 'body';\n readonly jsonBody: ReadonlyJSONValue;\n }\n);\n\n/**\n * Process a `/query` request from a Fetch `Request`.\n */\nexport function handleQueryRequest<S extends Schema>(\n input: HandleQueryRequestArgs<S>,\n): Promise<QueryResponse>;\n\n/**\n * @deprecated Pass a single object instead:\n * `handleQueryRequest({handler, schema, request, userID, logLevel})`.\n */\nexport function handleQueryRequest<S extends Schema>(\n transformQuery: QueryRequestHandler,\n schema: S,\n request: Request,\n logLevel?: LogLevel,\n): Promise<QueryResponse>;\n\n/**\n * @deprecated Pass a single object instead:\n * `handleQueryRequest({handler, schema, body, userID, logLevel})`.\n */\nexport function handleQueryRequest<S extends Schema>(\n transformQuery: QueryRequestHandler,\n schema: S,\n jsonBody: ReadonlyJSONValue,\n logLevel?: LogLevel,\n): Promise<QueryResponse>;\n\nexport function handleQueryRequest<S extends Schema>(\n inputOrTransformQuery: HandleQueryRequestArgs<S> | QueryRequestHandler,\n schema?: S,\n requestOrJsonBody?: Request | ReadonlyJSONValue,\n logLevel?: LogLevel,\n): Promise<QueryResponse> {\n const normalized =\n typeof inputOrTransformQuery === 'object' &&\n 'handler' in inputOrTransformQuery\n ? normalizeQueryRequestInput(inputOrTransformQuery)\n : normalizeLegacyQueryRequestArgs(\n wrapQueryRequestHandler(inputOrTransformQuery),\n schema,\n requestOrJsonBody,\n logLevel,\n );\n\n return transform(normalized, 'query');\n}\n\nfunction normalizeQueryRequestInput<S extends Schema>(\n input: HandleQueryRequestArgs<S>,\n): NormalizedQueryRequestArgs<S> {\n return 'request' in input\n ? normalizeLegacyQueryRequestArgs(\n wrapQueryRequestHandler(input.handler),\n input.schema,\n input.request,\n input.logLevel,\n input.userID ?? null,\n )\n : normalizeLegacyQueryRequestArgs(\n wrapQueryRequestHandler(input.handler),\n input.schema,\n input.body,\n input.logLevel,\n input.userID ?? null,\n );\n}\n\nfunction normalizeLegacyQueryRequestArgs<S extends Schema>(\n handler: LegacyQueryRequestHandler,\n schema: S | undefined,\n requestOrJsonBody: Request | ReadonlyJSONValue | undefined,\n logLevel: LogLevel | undefined,\n userID?: string | null,\n): NormalizedQueryRequestArgs<S> {\n assert(\n typeof schema !== 'undefined',\n 'Schema must be provided when using handleQueryRequest',\n );\n\n if (requestOrJsonBody instanceof Request) {\n return {\n type: 'request',\n handler,\n schema,\n request: requestOrJsonBody,\n userID,\n logLevel: logLevel ?? 'info',\n };\n }\n\n assert(\n typeof requestOrJsonBody !== 'undefined',\n 'JSON body cannot be undefined',\n );\n\n return {\n type: 'body',\n handler,\n schema,\n jsonBody: requestOrJsonBody,\n userID,\n logLevel: logLevel ?? 'info',\n };\n}\n\nasync function transform<S extends Schema>(\n args: NormalizedQueryRequestArgs<S>,\n apiName: 'query' | 'transform' | 'getQueries',\n): Promise<QueryResponse> {\n const lc = createLogContext(args.logLevel).withContext('TransformRequest');\n let parsed: TransformRequestMessage;\n let queryIDs: string[] = [];\n try {\n let body: ReadonlyJSONValue;\n if (args.type === 'request') {\n body = await args.request.json();\n } else {\n body = args.jsonBody;\n }\n\n // Strip, so a newer zero-cache can add fields to the request.\n parsed = v.parse(body, transformRequestMessageSchema, 'strip');\n\n queryIDs = parsed[1].map(r => r.id);\n } catch (error) {\n lc.error?.(`Failed to parse ${apiName} request`, error);\n\n const message = `Failed to parse ${apiName} request: ${getErrorMessage(error)}`;\n const details = getErrorDetails(error);\n\n return {\n kind: ErrorKind.TransformFailed,\n origin: ErrorOrigin.Server,\n reason: ErrorReason.Parse,\n message,\n queryIDs,\n ...(details ? {details} : {}),\n };\n }\n\n try {\n const nameMapper = clientToServer(args.schema.tables);\n\n const responses: TransformResponseBody = await Promise.all(\n parsed[1].map(async req => {\n let finalQuery: AnyQuery;\n try {\n const result = await args.handler(req.name, req.args);\n finalQuery = 'query' in result ? result.query : result;\n } catch (error) {\n const message = getErrorMessage(error);\n const details = getErrorDetails(error);\n\n return {\n error: error instanceof QueryParseError ? 'parse' : 'app',\n id: req.id,\n name: req.name,\n message,\n ...(details ? {details} : {}),\n };\n }\n\n try {\n const q = asQueryInternals(finalQuery);\n const ast = mapAST(q.ast, nameMapper);\n\n return {\n id: req.id,\n name: req.name,\n ast,\n };\n } catch (error) {\n lc.error?.('Failed to map AST', error);\n throw error;\n }\n }),\n );\n\n return {\n kind: 'QueryResponse',\n queries: responses,\n ...(typeof args.userID !== 'undefined' ? {userID: args.userID} : {}),\n } as const satisfies QueryResponse;\n } catch (e) {\n const message = getErrorMessage(e);\n const details = getErrorDetails(e);\n\n return {\n kind: ErrorKind.TransformFailed,\n origin: ErrorOrigin.Server,\n reason: ErrorReason.Internal,\n message,\n queryIDs,\n ...(details ? {details} : {}),\n };\n }\n}\n\n/**\n * A function that transforms a query by name and arguments into a Query object.\n *\n * @param name - The name of the query (can be dot-separated for nested queries)\n * @param args - The arguments to pass to the query (can be undefined)\n * @returns A Query object\n */\nexport type QueryRequestHandler = (\n name: string,\n args: ReadonlyJSONValue | undefined,\n) => AnyQuery;\n\n/** @deprecated Use `QueryRequestHandler` instead. */\nexport type TransformQueryFunction = QueryRequestHandler;\n\nexport type LegacyQueryRequestHandler = (\n name: string,\n args: readonly ReadonlyJSONValue[],\n) => MaybePromise<{query: AnyQuery} | AnyQuery>;\n\nfunction wrapQueryRequestHandler(\n handler: QueryRequestHandler,\n): LegacyQueryRequestHandler {\n return (name, args) => handler(name, args[0]);\n}\n"],"mappings":"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"}
@@ -47,7 +47,8 @@ async function getServerSchema(queryable, schema) {
47
47
  * diffed against the `information_schema` query it replaced.
48
48
  */
49
49
  function serverSchemaQuery(schemaTablePairs) {
50
- return formatPg(sql`
50
+ const inClause = sql.join(schemaTablePairs.map(([schema, table]) => sql`(${schema}::text, ${table}::text)`), ",");
51
+ const query = sql`
51
52
  SELECT
52
53
  n.nspname::text AS schema,
53
54
  cl.relname::text AS table,
@@ -101,8 +102,9 @@ function serverSchemaQuery(schemaTablePairs) {
101
102
  AND NOT pg_catalog.pg_is_other_temp_schema(n.oid)
102
103
  AND (pg_catalog.pg_has_role(cl.relowner, 'USAGE')
103
104
  OR pg_catalog.has_column_privilege(cl.oid, a.attnum, 'SELECT, INSERT, UPDATE, REFERENCES'))
104
- AND (n.nspname, cl.relname) IN (${sql.join(schemaTablePairs.map(([schema, table]) => sql`(${schema}::text, ${table}::text)`), ",")})
105
- `);
105
+ AND (n.nspname, cl.relname) IN (${inClause})
106
+ `;
107
+ return formatPg(query);
106
108
  }
107
109
  function makeSchemaIncompatibleErrorMessage(errors) {
108
110
  if (errors.length === 0) return "No schema incompatibilities found.";
@@ -114,9 +116,7 @@ function makeSchemaIncompatibleErrorMessage(errors) {
114
116
  case "missingColumn":
115
117
  messages.push(`Column "${error.column}" in table "${error.table}" is defined in your zero schema but does not exist in the database.`);
116
118
  break;
117
- case "typeError":
118
- messages.push(`Type mismatch for column "${error.column}" in table "${error.table}": ${error.requiredType === void 0 ? `${error.pgType} is currently unsupported in Zero. Please file a bug at https://bugs.rocicorp.dev/` : `${error.pgType} should be mapped to ${error.requiredType} in Zero not ${error.declaredType}.`}`);
119
- break;
119
+ case "typeError": messages.push(`Type mismatch for column "${error.column}" in table "${error.table}": ${error.requiredType === void 0 ? `${error.pgType} is currently unsupported in Zero. Please file a bug at https://bugs.rocicorp.dev/` : `${error.pgType} should be mapped to ${error.requiredType} in Zero not ${error.declaredType}.`}`);
120
120
  }
121
121
  return [
122
122
  "Schema incompatibility detected between your zero schema definition and the database:",
@@ -164,6 +164,6 @@ function checkSchemasAreCompatible(schema, serverSchema) {
164
164
  return errors;
165
165
  }
166
166
  //#endregion
167
- export { getServerSchema };
167
+ export { checkSchemasAreCompatible, getServerSchema, serverSchemaQuery };
168
168
 
169
169
  //# sourceMappingURL=schema.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"schema.js","names":[],"sources":["../../../../zero-server/src/schema.ts"],"sourcesContent":["import {assert} from '../../shared/src/asserts.ts';\nimport type {Enum} from '../../shared/src/enum.ts';\nimport {must} from '../../shared/src/must.ts';\nimport {formatPg, sql} from '../../z2s/src/sql.ts';\nimport * as PostgresTypeClass from '../../zero-cache/src/db/postgres-type-class-enum.ts';\nimport {dataTypeToZqlValueType} from '../../zero-cache/src/types/pg-data-type.ts';\nimport type {Schema} from '../../zero-types/src/schema.ts';\nimport type {ServerSchema} from '../../zero-types/src/server-schema.ts';\nimport type {Queryable} from '../../zql/src/mutate/custom.ts';\n\ntype PostgresTypeClass = Enum<typeof PostgresTypeClass>;\n\ntype ServerSchemaRow = {\n schema: string;\n table: string;\n column: string;\n dataType: string;\n length: string | null;\n precision: string | null;\n scale: string | null;\n typtype: PostgresTypeClass;\n typename: string;\n elemTyptype: PostgresTypeClass | null;\n elemTypname: string | null;\n};\n\nexport async function getServerSchema<S extends Schema>(\n queryable: Queryable,\n schema: S,\n): Promise<ServerSchema> {\n const schemaTablePairs: [string, string][] = Object.values(schema.tables).map(\n ({name, serverName}) => {\n let schemaTablePair: [string, string] = ['public', serverName ?? name];\n if (serverName) {\n const firstPeriod = serverName.indexOf('.');\n if (firstPeriod > -1) {\n schemaTablePair = [\n serverName.substring(0, firstPeriod),\n serverName.substring(firstPeriod + 1, serverName.length),\n ];\n }\n }\n return schemaTablePair;\n },\n );\n\n if (schemaTablePairs.length === 0) {\n return {}; // No pairs to query for\n }\n\n const {text, values} = serverSchemaQuery(schemaTablePairs);\n const results: Iterable<ServerSchemaRow> = (await queryable.query(\n text,\n values,\n )) as Iterable<ServerSchemaRow>;\n\n const serverSchema: ServerSchema = {};\n\n for (const row of results) {\n const tableName =\n row.schema === 'public' ? row.table : `${row.schema}.${row.table}`;\n let tableSchema = serverSchema[tableName];\n if (!tableSchema) {\n tableSchema = {};\n serverSchema[tableName] = tableSchema;\n }\n const isArray = row.elemTyptype !== null;\n const isEnum = (row.elemTyptype ?? row.typtype) === PostgresTypeClass.Enum;\n let type = row.dataType.toLowerCase();\n if (isArray) {\n type = must(\n row.elemTypname,\n `Array column \"${row.column}\" in table \"${tableName}\" is missing element type name`,\n );\n } else if (isEnum) {\n type = row.typename;\n } else if (\n type === 'user-defined' &&\n dataTypeToZqlValueType(row.typename, false, false) !== undefined\n ) {\n type = row.typename;\n }\n if (\n (type === 'bpchar' ||\n type === 'character' ||\n type === 'character varying' ||\n type === 'varchar') &&\n row.length\n ) {\n type = `${type}(${row.length})`;\n } else if (type === 'character') {\n // Without a length `character` means `character(1)`, so, like Postgres's\n // own format_type, name an unbounded `bpchar` column `bpchar`.\n type = 'bpchar';\n }\n if (\n (type === 'numeric' || type === 'decimal') &&\n row.precision !== null &&\n row.scale !== null\n ) {\n type = `${type}(${row.precision}, ${row.scale})`;\n }\n tableSchema[row.column] = {\n type,\n isEnum,\n isArray,\n };\n }\n\n const errors = checkSchemasAreCompatible(schema, serverSchema);\n assert(errors.length === 0, () => makeSchemaIncompatibleErrorMessage(errors));\n\n return serverSchema;\n}\n\n/**\n * The query behind {@link getServerSchema}, exported so that it can be\n * diffed against the `information_schema` query it replaced.\n */\nexport function serverSchemaQuery(\n schemaTablePairs: readonly (readonly [string, string])[],\n): {text: string; values: unknown[]} {\n // Cast all inputs to text and all outputs to text to avoid\n // any conversions customer's DBTransaction impl has on other types.\n const inClause = sql.join(\n schemaTablePairs.map(\n ([schema, table]) => sql`(${schema}::text, ${table}::text)`,\n ),\n ',',\n );\n // Read pg_catalog directly instead of information_schema.columns. The view\n // derives every column it returns through the information_schema._pg_*\n // functions and a per column privilege check, which is several times slower\n // than the query below. Server processes run this on the first transaction\n // they serve, so serverless deployments pay for it on every cold start.\n //\n // The expressions below reproduce the parts of the view's definition that we\n // select, down to the int4 wire types of length, precision and scale: the\n // _pg_char_max_length / _pg_numeric_precision / _pg_numeric_scale arithmetic,\n // the single level domain unwrap that data_type and udt_name perform (`ut`),\n // and the view's own relkind, attnum and privilege filters. pg_type is joined\n // by OID rather than by udt_name, which additionally avoids duplicate rows\n // when two schemas declare a type with the same name. The types those\n // functions special case are named through regtype, which the parser resolves\n // while planning.\n const query = sql`\n SELECT\n n.nspname::text AS schema,\n cl.relname::text AS table,\n a.attname::text AS column,\n CASE\n WHEN ut.typelem <> 0 AND ut.typlen = -1 THEN 'ARRAY'\n WHEN nut.nspname = 'pg_catalog' THEN pg_catalog.format_type(ut.oid, NULL)\n ELSE 'USER-DEFINED'\n END::text AS \"dataType\",\n CASE\n WHEN tm.typmod = -1 THEN NULL\n WHEN ut.oid IN ('pg_catalog.bpchar'::regtype, 'pg_catalog.varchar'::regtype) THEN tm.typmod - 4\n WHEN ut.oid IN ('pg_catalog.bit'::regtype, 'pg_catalog.varbit'::regtype) THEN tm.typmod\n END AS length,\n CASE ut.oid\n WHEN 'pg_catalog.int2'::regtype THEN 16\n WHEN 'pg_catalog.int4'::regtype THEN 32\n WHEN 'pg_catalog.int8'::regtype THEN 64\n WHEN 'pg_catalog.float4'::regtype THEN 24\n WHEN 'pg_catalog.float8'::regtype THEN 53\n WHEN 'pg_catalog.numeric'::regtype THEN CASE WHEN tm.typmod = -1 THEN NULL ELSE ((tm.typmod - 4) >> 16) & 65535 END\n END AS precision,\n CASE\n WHEN ut.oid IN ('pg_catalog.int2'::regtype, 'pg_catalog.int4'::regtype, 'pg_catalog.int8'::regtype) THEN 0\n WHEN ut.oid = 'pg_catalog.numeric'::regtype AND tm.typmod <> -1 THEN (tm.typmod - 4) & 65535\n END AS scale,\n ut.typtype::text AS typtype,\n ut.typname::text AS typename,\n CASE WHEN ut.typelem <> 0 THEN et.typtype::text ELSE NULL END AS \"elemTyptype\",\n CASE WHEN ut.typelem <> 0 THEN et.typname::text ELSE NULL END AS \"elemTypname\"\n FROM\n pg_catalog.pg_attribute a\n JOIN\n pg_catalog.pg_class cl ON cl.oid = a.attrelid\n JOIN\n pg_catalog.pg_namespace n ON n.oid = cl.relnamespace\n JOIN\n pg_catalog.pg_type t ON t.oid = a.atttypid\n JOIN\n pg_catalog.pg_type ut ON ut.oid = CASE WHEN t.typtype = 'd' THEN t.typbasetype ELSE t.oid END\n JOIN\n pg_catalog.pg_namespace nut ON nut.oid = ut.typnamespace\n LEFT JOIN\n pg_catalog.pg_type et ON et.oid = ut.typelem\n CROSS JOIN LATERAL\n (SELECT CASE WHEN t.typtype = 'd' THEN t.typtypmod ELSE a.atttypmod END AS typmod) tm\n WHERE\n a.attnum > 0\n AND NOT a.attisdropped\n AND cl.relkind IN ('r', 'v', 'f', 'p')\n AND NOT pg_catalog.pg_is_other_temp_schema(n.oid)\n AND (pg_catalog.pg_has_role(cl.relowner, 'USAGE')\n OR pg_catalog.has_column_privilege(cl.oid, a.attnum, 'SELECT, INSERT, UPDATE, REFERENCES'))\n AND (n.nspname, cl.relname) IN (${inClause})\n `;\n return formatPg(query);\n}\n\nfunction makeSchemaIncompatibleErrorMessage(\n errors: SchemaIncompatibilityError[],\n) {\n if (errors.length === 0) {\n return 'No schema incompatibilities found.';\n }\n\n const messages: string[] = [];\n\n for (const error of errors) {\n switch (error.type) {\n case 'missingTable':\n messages.push(\n `Table \"${error.table}\" is defined in your zero schema but does not exist in the database.`,\n );\n break;\n case 'missingColumn':\n messages.push(\n `Column \"${error.column}\" in table \"${error.table}\" is defined in your zero schema but does not exist in the database.`,\n );\n break;\n case 'typeError':\n messages.push(\n `Type mismatch for column \"${error.column}\" in table \"${error.table}\": ${error.requiredType === undefined ? `${error.pgType} is currently unsupported in Zero. Please file a bug at https://bugs.rocicorp.dev/` : `${error.pgType} should be mapped to ${error.requiredType} in Zero not ${error.declaredType}.`}`,\n );\n break;\n }\n }\n\n return [\n 'Schema incompatibility detected between your zero schema definition and the database:',\n '',\n ...messages.map(msg => ` - ${msg}`),\n '',\n 'Please update your schema definition to match the database or migrate your database to match the schema.',\n ].join('\\n');\n}\n\nexport type SchemaIncompatibilityError =\n | {\n type: 'typeError';\n table: string;\n column: string;\n pgType: string;\n declaredType: string;\n requiredType: string | undefined;\n }\n | {\n type: 'missingColumn';\n table: string;\n column: string;\n }\n | {\n type: 'missingTable';\n table: string;\n };\n\nexport function checkSchemasAreCompatible(\n schema: Schema,\n serverSchema: ServerSchema,\n): SchemaIncompatibilityError[] {\n const errors: SchemaIncompatibilityError[] = [];\n // Check that all tables in schema exist in serverSchema\n for (const table of Object.values(schema.tables)) {\n const serverTableName = table.serverName ?? table.name;\n\n if (!serverSchema[serverTableName]) {\n errors.push({\n type: 'missingTable',\n table: serverTableName,\n });\n continue;\n }\n\n // Check that all columns in the table exist in serverSchema\n for (const [columnName, column] of Object.entries(table.columns)) {\n const serverColumnName = column.serverName ?? columnName;\n\n if (!serverSchema[serverTableName][serverColumnName]) {\n errors.push({\n type: 'missingColumn',\n table: serverTableName,\n column: serverColumnName,\n });\n continue;\n }\n\n // Check type compatibility\n const serverColumn = serverSchema[serverTableName][serverColumnName];\n const declaredType = column.type;\n const pgType = serverColumn.type;\n const requiredType = dataTypeToZqlValueType(\n pgType,\n serverColumn.isEnum,\n serverColumn.isArray,\n );\n if (requiredType !== declaredType) {\n errors.push({\n type: 'typeError',\n table: serverTableName,\n column: serverColumnName,\n pgType,\n declaredType,\n requiredType,\n });\n }\n }\n }\n\n return errors;\n}\n"],"mappings":";;;;;AA0BA,eAAsB,gBACpB,WACA,QACuB;CACvB,MAAM,mBAAuC,OAAO,OAAO,OAAO,MAAM,EAAE,KACvE,EAAC,MAAM,iBAAgB;EACtB,IAAI,kBAAoC,CAAC,UAAU,cAAc,IAAI;EACrE,IAAI,YAAY;GACd,MAAM,cAAc,WAAW,QAAQ,GAAG;GAC1C,IAAI,cAAc,IAChB,kBAAkB,CAChB,WAAW,UAAU,GAAG,WAAW,GACnC,WAAW,UAAU,cAAc,GAAG,WAAW,MAAM,CACzD;EAEJ;EACA,OAAO;CACT,CACF;CAEA,IAAI,iBAAiB,WAAW,GAC9B,OAAO,CAAC;CAGV,MAAM,EAAC,MAAM,WAAU,kBAAkB,gBAAgB;CACzD,MAAM,UAAsC,MAAM,UAAU,MAC1D,MACA,MACF;CAEA,MAAM,eAA6B,CAAC;CAEpC,KAAK,MAAM,OAAO,SAAS;EACzB,MAAM,YACJ,IAAI,WAAW,WAAW,IAAI,QAAQ,GAAG,IAAI,OAAO,GAAG,IAAI;EAC7D,IAAI,cAAc,aAAa;EAC/B,IAAI,CAAC,aAAa;GAChB,cAAc,CAAC;GACf,aAAa,aAAa;EAC5B;EACA,MAAM,UAAU,IAAI,gBAAgB;EACpC,MAAM,UAAU,IAAI,eAAe,IAAI,aAAa;EACpD,IAAI,OAAO,IAAI,SAAS,YAAY;EACpC,IAAI,SACF,OAAO,KACL,IAAI,aACJ,iBAAiB,IAAI,OAAO,cAAc,UAAU,+BACtD;OACK,IAAI,QACT,OAAO,IAAI;OACN,IACL,SAAS,kBACT,uBAAuB,IAAI,UAAU,OAAO,KAAK,MAAM,KAAA,GAEvD,OAAO,IAAI;EAEb,KACG,SAAS,YACR,SAAS,eACT,SAAS,uBACT,SAAS,cACX,IAAI,QAEJ,OAAO,GAAG,KAAK,GAAG,IAAI,OAAO;OACxB,IAAI,SAAS,aAGlB,OAAO;EAET,KACG,SAAS,aAAa,SAAS,cAChC,IAAI,cAAc,QAClB,IAAI,UAAU,MAEd,OAAO,GAAG,KAAK,GAAG,IAAI,UAAU,IAAI,IAAI,MAAM;EAEhD,YAAY,IAAI,UAAU;GACxB;GACA;GACA;EACF;CACF;CAEA,MAAM,SAAS,0BAA0B,QAAQ,YAAY;CAC7D,OAAO,OAAO,WAAW,SAAS,mCAAmC,MAAM,CAAC;CAE5E,OAAO;AACT;;;;;AAMA,SAAgB,kBACd,kBACmC;CAgFnC,OAAO,SAAS,GAxDC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4CArBA,IAAI,KACnB,iBAAiB,KACd,CAAC,QAAQ,WAAW,GAAG,IAAI,OAAO,UAAU,MAAM,QACrD,GACA,GAuEwC,EAAS;KAE9B;AACvB;AAEA,SAAS,mCACP,QACA;CACA,IAAI,OAAO,WAAW,GACpB,OAAO;CAGT,MAAM,WAAqB,CAAC;CAE5B,KAAK,MAAM,SAAS,QAClB,QAAQ,MAAM,MAAd;EACE,KAAK;GACH,SAAS,KACP,UAAU,MAAM,MAAM,qEACxB;GACA;EACF,KAAK;GACH,SAAS,KACP,WAAW,MAAM,OAAO,cAAc,MAAM,MAAM,qEACpD;GACA;EACF,KAAK;GACH,SAAS,KACP,6BAA6B,MAAM,OAAO,cAAc,MAAM,MAAM,KAAK,MAAM,iBAAiB,KAAA,IAAY,GAAG,MAAM,OAAO,sFAAsF,GAAG,MAAM,OAAO,uBAAuB,MAAM,aAAa,eAAe,MAAM,aAAa,IAChT;GACA;CACJ;CAGF,OAAO;EACL;EACA;EACA,GAAG,SAAS,KAAI,QAAO,OAAO,KAAK;EACnC;EACA;CACF,EAAE,KAAK,IAAI;AACb;AAqBA,SAAgB,0BACd,QACA,cAC8B;CAC9B,MAAM,SAAuC,CAAC;CAE9C,KAAK,MAAM,SAAS,OAAO,OAAO,OAAO,MAAM,GAAG;EAChD,MAAM,kBAAkB,MAAM,cAAc,MAAM;EAElD,IAAI,CAAC,aAAa,kBAAkB;GAClC,OAAO,KAAK;IACV,MAAM;IACN,OAAO;GACT,CAAC;GACD;EACF;EAGA,KAAK,MAAM,CAAC,YAAY,WAAW,OAAO,QAAQ,MAAM,OAAO,GAAG;GAChE,MAAM,mBAAmB,OAAO,cAAc;GAE9C,IAAI,CAAC,aAAa,iBAAiB,mBAAmB;IACpD,OAAO,KAAK;KACV,MAAM;KACN,OAAO;KACP,QAAQ;IACV,CAAC;IACD;GACF;GAGA,MAAM,eAAe,aAAa,iBAAiB;GACnD,MAAM,eAAe,OAAO;GAC5B,MAAM,SAAS,aAAa;GAC5B,MAAM,eAAe,uBACnB,QACA,aAAa,QACb,aAAa,OACf;GACA,IAAI,iBAAiB,cACnB,OAAO,KAAK;IACV,MAAM;IACN,OAAO;IACP,QAAQ;IACR;IACA;IACA;GACF,CAAC;EAEL;CACF;CAEA,OAAO;AACT"}
1
+ {"version":3,"file":"schema.js","names":[],"sources":["../../../../zero-server/src/schema.ts"],"sourcesContent":["import {assert} from '../../shared/src/asserts.ts';\nimport type {Enum} from '../../shared/src/enum.ts';\nimport {must} from '../../shared/src/must.ts';\nimport {formatPg, sql} from '../../z2s/src/sql.ts';\nimport * as PostgresTypeClass from '../../zero-cache/src/db/postgres-type-class-enum.ts';\nimport {dataTypeToZqlValueType} from '../../zero-cache/src/types/pg-data-type.ts';\nimport type {Schema} from '../../zero-types/src/schema.ts';\nimport type {ServerSchema} from '../../zero-types/src/server-schema.ts';\nimport type {Queryable} from '../../zql/src/mutate/custom.ts';\n\ntype PostgresTypeClass = Enum<typeof PostgresTypeClass>;\n\ntype ServerSchemaRow = {\n schema: string;\n table: string;\n column: string;\n dataType: string;\n length: string | null;\n precision: string | null;\n scale: string | null;\n typtype: PostgresTypeClass;\n typename: string;\n elemTyptype: PostgresTypeClass | null;\n elemTypname: string | null;\n};\n\nexport async function getServerSchema<S extends Schema>(\n queryable: Queryable,\n schema: S,\n): Promise<ServerSchema> {\n const schemaTablePairs: [string, string][] = Object.values(schema.tables).map(\n ({name, serverName}) => {\n let schemaTablePair: [string, string] = ['public', serverName ?? name];\n if (serverName) {\n const firstPeriod = serverName.indexOf('.');\n if (firstPeriod > -1) {\n schemaTablePair = [\n serverName.substring(0, firstPeriod),\n serverName.substring(firstPeriod + 1, serverName.length),\n ];\n }\n }\n return schemaTablePair;\n },\n );\n\n if (schemaTablePairs.length === 0) {\n return {}; // No pairs to query for\n }\n\n const {text, values} = serverSchemaQuery(schemaTablePairs);\n const results: Iterable<ServerSchemaRow> = (await queryable.query(\n text,\n values,\n )) as Iterable<ServerSchemaRow>;\n\n const serverSchema: ServerSchema = {};\n\n for (const row of results) {\n const tableName =\n row.schema === 'public' ? row.table : `${row.schema}.${row.table}`;\n let tableSchema = serverSchema[tableName];\n if (!tableSchema) {\n tableSchema = {};\n serverSchema[tableName] = tableSchema;\n }\n const isArray = row.elemTyptype !== null;\n const isEnum = (row.elemTyptype ?? row.typtype) === PostgresTypeClass.Enum;\n let type = row.dataType.toLowerCase();\n if (isArray) {\n type = must(\n row.elemTypname,\n `Array column \"${row.column}\" in table \"${tableName}\" is missing element type name`,\n );\n } else if (isEnum) {\n type = row.typename;\n } else if (\n type === 'user-defined' &&\n dataTypeToZqlValueType(row.typename, false, false) !== undefined\n ) {\n type = row.typename;\n }\n if (\n (type === 'bpchar' ||\n type === 'character' ||\n type === 'character varying' ||\n type === 'varchar') &&\n row.length\n ) {\n type = `${type}(${row.length})`;\n } else if (type === 'character') {\n // Without a length `character` means `character(1)`, so, like Postgres's\n // own format_type, name an unbounded `bpchar` column `bpchar`.\n type = 'bpchar';\n }\n if (\n (type === 'numeric' || type === 'decimal') &&\n row.precision !== null &&\n row.scale !== null\n ) {\n type = `${type}(${row.precision}, ${row.scale})`;\n }\n tableSchema[row.column] = {\n type,\n isEnum,\n isArray,\n };\n }\n\n const errors = checkSchemasAreCompatible(schema, serverSchema);\n assert(errors.length === 0, () => makeSchemaIncompatibleErrorMessage(errors));\n\n return serverSchema;\n}\n\n/**\n * The query behind {@link getServerSchema}, exported so that it can be\n * diffed against the `information_schema` query it replaced.\n */\nexport function serverSchemaQuery(\n schemaTablePairs: readonly (readonly [string, string])[],\n): {text: string; values: unknown[]} {\n // Cast all inputs to text and all outputs to text to avoid\n // any conversions customer's DBTransaction impl has on other types.\n const inClause = sql.join(\n schemaTablePairs.map(\n ([schema, table]) => sql`(${schema}::text, ${table}::text)`,\n ),\n ',',\n );\n // Read pg_catalog directly instead of information_schema.columns. The view\n // derives every column it returns through the information_schema._pg_*\n // functions and a per column privilege check, which is several times slower\n // than the query below. Server processes run this on the first transaction\n // they serve, so serverless deployments pay for it on every cold start.\n //\n // The expressions below reproduce the parts of the view's definition that we\n // select, down to the int4 wire types of length, precision and scale: the\n // _pg_char_max_length / _pg_numeric_precision / _pg_numeric_scale arithmetic,\n // the single level domain unwrap that data_type and udt_name perform (`ut`),\n // and the view's own relkind, attnum and privilege filters. pg_type is joined\n // by OID rather than by udt_name, which additionally avoids duplicate rows\n // when two schemas declare a type with the same name. The types those\n // functions special case are named through regtype, which the parser resolves\n // while planning.\n const query = sql`\n SELECT\n n.nspname::text AS schema,\n cl.relname::text AS table,\n a.attname::text AS column,\n CASE\n WHEN ut.typelem <> 0 AND ut.typlen = -1 THEN 'ARRAY'\n WHEN nut.nspname = 'pg_catalog' THEN pg_catalog.format_type(ut.oid, NULL)\n ELSE 'USER-DEFINED'\n END::text AS \"dataType\",\n CASE\n WHEN tm.typmod = -1 THEN NULL\n WHEN ut.oid IN ('pg_catalog.bpchar'::regtype, 'pg_catalog.varchar'::regtype) THEN tm.typmod - 4\n WHEN ut.oid IN ('pg_catalog.bit'::regtype, 'pg_catalog.varbit'::regtype) THEN tm.typmod\n END AS length,\n CASE ut.oid\n WHEN 'pg_catalog.int2'::regtype THEN 16\n WHEN 'pg_catalog.int4'::regtype THEN 32\n WHEN 'pg_catalog.int8'::regtype THEN 64\n WHEN 'pg_catalog.float4'::regtype THEN 24\n WHEN 'pg_catalog.float8'::regtype THEN 53\n WHEN 'pg_catalog.numeric'::regtype THEN CASE WHEN tm.typmod = -1 THEN NULL ELSE ((tm.typmod - 4) >> 16) & 65535 END\n END AS precision,\n CASE\n WHEN ut.oid IN ('pg_catalog.int2'::regtype, 'pg_catalog.int4'::regtype, 'pg_catalog.int8'::regtype) THEN 0\n WHEN ut.oid = 'pg_catalog.numeric'::regtype AND tm.typmod <> -1 THEN (tm.typmod - 4) & 65535\n END AS scale,\n ut.typtype::text AS typtype,\n ut.typname::text AS typename,\n CASE WHEN ut.typelem <> 0 THEN et.typtype::text ELSE NULL END AS \"elemTyptype\",\n CASE WHEN ut.typelem <> 0 THEN et.typname::text ELSE NULL END AS \"elemTypname\"\n FROM\n pg_catalog.pg_attribute a\n JOIN\n pg_catalog.pg_class cl ON cl.oid = a.attrelid\n JOIN\n pg_catalog.pg_namespace n ON n.oid = cl.relnamespace\n JOIN\n pg_catalog.pg_type t ON t.oid = a.atttypid\n JOIN\n pg_catalog.pg_type ut ON ut.oid = CASE WHEN t.typtype = 'd' THEN t.typbasetype ELSE t.oid END\n JOIN\n pg_catalog.pg_namespace nut ON nut.oid = ut.typnamespace\n LEFT JOIN\n pg_catalog.pg_type et ON et.oid = ut.typelem\n CROSS JOIN LATERAL\n (SELECT CASE WHEN t.typtype = 'd' THEN t.typtypmod ELSE a.atttypmod END AS typmod) tm\n WHERE\n a.attnum > 0\n AND NOT a.attisdropped\n AND cl.relkind IN ('r', 'v', 'f', 'p')\n AND NOT pg_catalog.pg_is_other_temp_schema(n.oid)\n AND (pg_catalog.pg_has_role(cl.relowner, 'USAGE')\n OR pg_catalog.has_column_privilege(cl.oid, a.attnum, 'SELECT, INSERT, UPDATE, REFERENCES'))\n AND (n.nspname, cl.relname) IN (${inClause})\n `;\n return formatPg(query);\n}\n\nfunction makeSchemaIncompatibleErrorMessage(\n errors: SchemaIncompatibilityError[],\n) {\n if (errors.length === 0) {\n return 'No schema incompatibilities found.';\n }\n\n const messages: string[] = [];\n\n for (const error of errors) {\n switch (error.type) {\n case 'missingTable':\n messages.push(\n `Table \"${error.table}\" is defined in your zero schema but does not exist in the database.`,\n );\n break;\n case 'missingColumn':\n messages.push(\n `Column \"${error.column}\" in table \"${error.table}\" is defined in your zero schema but does not exist in the database.`,\n );\n break;\n case 'typeError':\n messages.push(\n `Type mismatch for column \"${error.column}\" in table \"${error.table}\": ${error.requiredType === undefined ? `${error.pgType} is currently unsupported in Zero. Please file a bug at https://bugs.rocicorp.dev/` : `${error.pgType} should be mapped to ${error.requiredType} in Zero not ${error.declaredType}.`}`,\n );\n break;\n }\n }\n\n return [\n 'Schema incompatibility detected between your zero schema definition and the database:',\n '',\n ...messages.map(msg => ` - ${msg}`),\n '',\n 'Please update your schema definition to match the database or migrate your database to match the schema.',\n ].join('\\n');\n}\n\nexport type SchemaIncompatibilityError =\n | {\n type: 'typeError';\n table: string;\n column: string;\n pgType: string;\n declaredType: string;\n requiredType: string | undefined;\n }\n | {\n type: 'missingColumn';\n table: string;\n column: string;\n }\n | {\n type: 'missingTable';\n table: string;\n };\n\nexport function checkSchemasAreCompatible(\n schema: Schema,\n serverSchema: ServerSchema,\n): SchemaIncompatibilityError[] {\n const errors: SchemaIncompatibilityError[] = [];\n // Check that all tables in schema exist in serverSchema\n for (const table of Object.values(schema.tables)) {\n const serverTableName = table.serverName ?? table.name;\n\n if (!serverSchema[serverTableName]) {\n errors.push({\n type: 'missingTable',\n table: serverTableName,\n });\n continue;\n }\n\n // Check that all columns in the table exist in serverSchema\n for (const [columnName, column] of Object.entries(table.columns)) {\n const serverColumnName = column.serverName ?? columnName;\n\n if (!serverSchema[serverTableName][serverColumnName]) {\n errors.push({\n type: 'missingColumn',\n table: serverTableName,\n column: serverColumnName,\n });\n continue;\n }\n\n // Check type compatibility\n const serverColumn = serverSchema[serverTableName][serverColumnName];\n const declaredType = column.type;\n const pgType = serverColumn.type;\n const requiredType = dataTypeToZqlValueType(\n pgType,\n serverColumn.isEnum,\n serverColumn.isArray,\n );\n if (requiredType !== declaredType) {\n errors.push({\n type: 'typeError',\n table: serverTableName,\n column: serverColumnName,\n pgType,\n declaredType,\n requiredType,\n });\n }\n }\n }\n\n return errors;\n}\n"],"mappings":"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"}