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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (551) hide show
  1. package/README.md +3 -28
  2. package/out/_virtual/{_@oxc-project_runtime@0.130.0 → _@oxc-project_runtime@0.122.0}/helpers/usingCtx.js +1 -1
  3. package/out/analyze-query/src/analyze-cli.js +3 -3
  4. package/out/analyze-query/src/analyze-cli.js.map +1 -1
  5. package/out/analyze-query/src/bin-analyze.js +1 -6
  6. package/out/analyze-query/src/bin-analyze.js.map +1 -1
  7. package/out/analyze-query/src/bin-transform.js.map +1 -1
  8. package/out/ast-to-zql/src/ast-to-zql.js.map +1 -1
  9. package/out/ast-to-zql/src/bin.js.map +1 -1
  10. package/out/ast-to-zql/src/format.js.map +1 -1
  11. package/out/datadog/src/datadog-log-sink.js.map +1 -1
  12. package/out/otel/src/enabled.js.map +1 -1
  13. package/out/otel/src/log-options.js.map +1 -1
  14. package/out/otel/src/maybe-time.js.map +1 -1
  15. package/out/otel/src/span.js.map +1 -1
  16. package/out/replicache/src/async-iterable-to-array.js.map +1 -1
  17. package/out/replicache/src/bg-interval.js.map +1 -1
  18. package/out/replicache/src/btree/diff.js.map +1 -1
  19. package/out/replicache/src/btree/node.js.map +1 -1
  20. package/out/replicache/src/btree/read.js.map +1 -1
  21. package/out/replicache/src/btree/splice.js.map +1 -1
  22. package/out/replicache/src/btree/write.js +3 -6
  23. package/out/replicache/src/btree/write.js.map +1 -1
  24. package/out/replicache/src/call-default-fetch.js.map +1 -1
  25. package/out/replicache/src/connection-loop-delegates.js.map +1 -1
  26. package/out/replicache/src/connection-loop.js.map +1 -1
  27. package/out/replicache/src/cookies.js.map +1 -1
  28. package/out/replicache/src/dag/chunk.js.map +1 -1
  29. package/out/replicache/src/dag/gc.js.map +1 -1
  30. package/out/replicache/src/dag/key.js.map +1 -1
  31. package/out/replicache/src/dag/lazy-store.js.map +1 -1
  32. package/out/replicache/src/dag/store-impl.js.map +1 -1
  33. package/out/replicache/src/dag/store.js.map +1 -1
  34. package/out/replicache/src/dag/visitor.js.map +1 -1
  35. package/out/replicache/src/db/commit.js.map +1 -1
  36. package/out/replicache/src/db/index.js.map +1 -1
  37. package/out/replicache/src/db/read.js.map +1 -1
  38. package/out/replicache/src/db/rebase.js.map +1 -1
  39. package/out/replicache/src/db/write.js.map +1 -1
  40. package/out/replicache/src/deleted-clients.js.map +1 -1
  41. package/out/replicache/src/error-responses.js.map +1 -1
  42. package/out/replicache/src/frozen-json.js.map +1 -1
  43. package/out/replicache/src/get-default-puller.js.map +1 -1
  44. package/out/replicache/src/get-default-pusher.js.map +1 -1
  45. package/out/replicache/src/get-kv-store-provider.js.map +1 -1
  46. package/out/replicache/src/hash.js.map +1 -1
  47. package/out/replicache/src/http-request-info.js.map +1 -1
  48. package/out/replicache/src/index-defs.js.map +1 -1
  49. package/out/replicache/src/kv/expo-sqlite/store.js.map +1 -1
  50. package/out/replicache/src/kv/idb-store-with-mem-fallback.js.map +1 -1
  51. package/out/replicache/src/kv/idb-store.js.map +1 -1
  52. package/out/replicache/src/kv/mem-store.js.map +1 -1
  53. package/out/replicache/src/kv/op-sqlite/store.js.map +1 -1
  54. package/out/replicache/src/kv/read-impl.js.map +1 -1
  55. package/out/replicache/src/kv/sqlite-store.d.ts.map +1 -1
  56. package/out/replicache/src/kv/sqlite-store.js +1 -4
  57. package/out/replicache/src/kv/sqlite-store.js.map +1 -1
  58. package/out/replicache/src/kv/throw-if-closed.js.map +1 -1
  59. package/out/replicache/src/kv/write-impl-base.js.map +1 -1
  60. package/out/replicache/src/kv/write-impl.js.map +1 -1
  61. package/out/replicache/src/lazy.js.map +1 -1
  62. package/out/replicache/src/log-options.js.map +1 -1
  63. package/out/replicache/src/make-idb-name.js.map +1 -1
  64. package/out/replicache/src/new-client-channel.js.map +1 -1
  65. package/out/replicache/src/on-persist-channel.js.map +1 -1
  66. package/out/replicache/src/patch-operation.js.map +1 -1
  67. package/out/replicache/src/pending-mutations.js.map +1 -1
  68. package/out/replicache/src/persist/client-gc.js.map +1 -1
  69. package/out/replicache/src/persist/client-group-gc.js.map +1 -1
  70. package/out/replicache/src/persist/client-groups.js +0 -40
  71. package/out/replicache/src/persist/client-groups.js.map +1 -1
  72. package/out/replicache/src/persist/clients.js +0 -28
  73. package/out/replicache/src/persist/clients.js.map +1 -1
  74. package/out/replicache/src/persist/collect-idb-databases.js.map +1 -1
  75. package/out/replicache/src/persist/gather-mem-only-visitor.js.map +1 -1
  76. package/out/replicache/src/persist/gather-not-cached-visitor.js.map +1 -1
  77. package/out/replicache/src/persist/heartbeat.js.map +1 -1
  78. package/out/replicache/src/persist/idb-databases-store-db-name.js.map +1 -1
  79. package/out/replicache/src/persist/idb-databases-store.js.map +1 -1
  80. package/out/replicache/src/persist/make-client-id.js.map +1 -1
  81. package/out/replicache/src/persist/persist.js.map +1 -1
  82. package/out/replicache/src/persist/refresh.js.map +1 -1
  83. package/out/replicache/src/process-scheduler.js.map +1 -1
  84. package/out/replicache/src/pusher.js.map +1 -1
  85. package/out/replicache/src/replicache-impl.js.map +1 -1
  86. package/out/replicache/src/report-error.js.map +1 -1
  87. package/out/replicache/src/request-idle.js.map +1 -1
  88. package/out/replicache/src/scan-iterator.js.map +1 -1
  89. package/out/replicache/src/scan-options.js.map +1 -1
  90. package/out/replicache/src/set-interval-with-signal.js.map +1 -1
  91. package/out/replicache/src/subscriptions.js.map +1 -1
  92. package/out/replicache/src/sync/diff.js.map +1 -1
  93. package/out/replicache/src/sync/ids.js.map +1 -1
  94. package/out/replicache/src/sync/patch.js.map +1 -1
  95. package/out/replicache/src/sync/pull-error.js.map +1 -1
  96. package/out/replicache/src/sync/pull.js.map +1 -1
  97. package/out/replicache/src/sync/push.js.map +1 -1
  98. package/out/replicache/src/sync/request-id.js.map +1 -1
  99. package/out/replicache/src/to-error.js.map +1 -1
  100. package/out/replicache/src/transaction-closed-error.js.map +1 -1
  101. package/out/replicache/src/transactions.js.map +1 -1
  102. package/out/replicache/src/with-transactions.js.map +1 -1
  103. package/out/shared/src/abort-error.js.map +1 -1
  104. package/out/shared/src/arrays.js.map +1 -1
  105. package/out/shared/src/asserts.js.map +1 -1
  106. package/out/shared/src/bigint-json.js.map +1 -1
  107. package/out/shared/src/binary-search.js.map +1 -1
  108. package/out/shared/src/broadcast-channel.js.map +1 -1
  109. package/out/shared/src/browser-env.js.map +1 -1
  110. package/out/shared/src/btree-set.js.map +1 -1
  111. package/out/shared/src/cache.js.map +1 -1
  112. package/out/shared/src/centroid.js.map +1 -1
  113. package/out/shared/src/custom-key-map.js.map +1 -1
  114. package/out/shared/src/custom-key-set.js.map +1 -1
  115. package/out/shared/src/deep-clone.js.map +1 -1
  116. package/out/shared/src/deep-merge.js.map +1 -1
  117. package/out/shared/src/document-visible.js.map +1 -1
  118. package/out/shared/src/dotenv.js.map +1 -1
  119. package/out/shared/src/error.js.map +1 -1
  120. package/out/shared/src/hash.js.map +1 -1
  121. package/out/shared/src/iterables.d.ts +0 -2
  122. package/out/shared/src/iterables.d.ts.map +1 -1
  123. package/out/shared/src/iterables.js +1 -9
  124. package/out/shared/src/iterables.js.map +1 -1
  125. package/out/shared/src/json-schema.js.map +1 -1
  126. package/out/shared/src/json.js.map +1 -1
  127. package/out/shared/src/logging-test-utils.js.map +1 -1
  128. package/out/shared/src/logging.js.map +1 -1
  129. package/out/shared/src/map.js.map +1 -1
  130. package/out/shared/src/must.js.map +1 -1
  131. package/out/shared/src/object-traversal.js.map +1 -1
  132. package/out/shared/src/objects.js.map +1 -1
  133. package/out/shared/src/options.js.map +1 -1
  134. package/out/shared/src/parse-big-int.js.map +1 -1
  135. package/out/shared/src/promise-race.js.map +1 -1
  136. package/out/shared/src/queue.d.ts.map +1 -1
  137. package/out/shared/src/queue.js +21 -15
  138. package/out/shared/src/queue.js.map +1 -1
  139. package/out/shared/src/rand.js.map +1 -1
  140. package/out/shared/src/random-uint64.js.map +1 -1
  141. package/out/shared/src/random-values.js.map +1 -1
  142. package/out/shared/src/record-proxy.js.map +1 -1
  143. package/out/shared/src/resolved-promises.js.map +1 -1
  144. package/out/shared/src/sentinels.js.map +1 -1
  145. package/out/shared/src/set-utils.js.map +1 -1
  146. package/out/shared/src/size-of-value.js.map +1 -1
  147. package/out/shared/src/sleep.js.map +1 -1
  148. package/out/shared/src/sorted-entries.js.map +1 -1
  149. package/out/shared/src/string-compare.js.map +1 -1
  150. package/out/shared/src/subscribable.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.map +1 -1
  154. package/out/z2s/src/compiler.js.map +1 -1
  155. package/out/z2s/src/sql.js.map +1 -1
  156. package/out/zero/package.js +26 -34
  157. package/out/zero/package.js.map +1 -1
  158. package/out/zero/src/build-schema.js.map +1 -1
  159. package/out/zero/src/zero-cache-dev.js.map +1 -1
  160. package/out/zero/src/zero-out.js.map +1 -1
  161. package/out/zero-cache/src/auth/auth.js.map +1 -1
  162. package/out/zero-cache/src/auth/jwt.js.map +1 -1
  163. package/out/zero-cache/src/auth/load-permissions.js.map +1 -1
  164. package/out/zero-cache/src/auth/read-authorizer.js.map +1 -1
  165. package/out/zero-cache/src/auth/write-authorizer.js.map +1 -1
  166. package/out/zero-cache/src/config/network.js.map +1 -1
  167. package/out/zero-cache/src/config/normalize.js.map +1 -1
  168. package/out/zero-cache/src/config/server-context.js.map +1 -1
  169. package/out/zero-cache/src/config/zero-config.js +0 -5
  170. package/out/zero-cache/src/config/zero-config.js.map +1 -1
  171. package/out/zero-cache/src/custom/fetch.js.map +1 -1
  172. package/out/zero-cache/src/custom-queries/transform-query.js.map +1 -1
  173. package/out/zero-cache/src/db/create.js.map +1 -1
  174. package/out/zero-cache/src/db/delete-lite-db.js.map +1 -1
  175. package/out/zero-cache/src/db/lite-tables.js.map +1 -1
  176. package/out/zero-cache/src/db/migration-lite.js +0 -19
  177. package/out/zero-cache/src/db/migration-lite.js.map +1 -1
  178. package/out/zero-cache/src/db/migration.js +0 -19
  179. package/out/zero-cache/src/db/migration.js.map +1 -1
  180. package/out/zero-cache/src/db/pg-copy-binary.js.map +1 -1
  181. package/out/zero-cache/src/db/pg-copy.js.map +1 -1
  182. package/out/zero-cache/src/db/pg-to-lite.js.map +1 -1
  183. package/out/zero-cache/src/db/pg-type-parser.js.map +1 -1
  184. package/out/zero-cache/src/db/run-transaction.js.map +1 -1
  185. package/out/zero-cache/src/db/specs.js.map +1 -1
  186. package/out/zero-cache/src/db/statements.js.map +1 -1
  187. package/out/zero-cache/src/db/transaction-pool.js.map +1 -1
  188. package/out/zero-cache/src/db/warmup.js.map +1 -1
  189. package/out/zero-cache/src/observability/events.js.map +1 -1
  190. package/out/zero-cache/src/observability/metrics.js.map +1 -1
  191. package/out/zero-cache/src/scripts/decommission.js.map +1 -1
  192. package/out/zero-cache/src/scripts/deploy-permissions.js.map +1 -1
  193. package/out/zero-cache/src/scripts/permissions.d.ts.map +1 -1
  194. package/out/zero-cache/src/scripts/permissions.js +2 -1
  195. package/out/zero-cache/src/scripts/permissions.js.map +1 -1
  196. package/out/zero-cache/src/server/anonymous-otel-start.js +7 -8
  197. package/out/zero-cache/src/server/anonymous-otel-start.js.map +1 -1
  198. package/out/zero-cache/src/server/change-streamer.js.map +1 -1
  199. package/out/zero-cache/src/server/inspector-delegate.js.map +1 -1
  200. package/out/zero-cache/src/server/logging.js.map +1 -1
  201. package/out/zero-cache/src/server/main.js.map +1 -1
  202. package/out/zero-cache/src/server/mutator.js.map +1 -1
  203. package/out/zero-cache/src/server/otel-diag-logger.js.map +1 -1
  204. package/out/zero-cache/src/server/otel-log-sink.js.map +1 -1
  205. package/out/zero-cache/src/server/otel-start.js.map +1 -1
  206. package/out/zero-cache/src/server/priority-op.js.map +1 -1
  207. package/out/zero-cache/src/server/reaper.js.map +1 -1
  208. package/out/zero-cache/src/server/replicator.js.map +1 -1
  209. package/out/zero-cache/src/server/runner/main.js.map +1 -1
  210. package/out/zero-cache/src/server/runner/run-worker.js.map +1 -1
  211. package/out/zero-cache/src/server/runner/runtime.js.map +1 -1
  212. package/out/zero-cache/src/server/runner/zero-dispatcher.js.map +1 -1
  213. package/out/zero-cache/src/server/shadow-syncer.js.map +1 -1
  214. package/out/zero-cache/src/server/syncer.js.map +1 -1
  215. package/out/zero-cache/src/server/worker-dispatcher.js.map +1 -1
  216. package/out/zero-cache/src/server/worker-urls.js.map +1 -1
  217. package/out/zero-cache/src/services/analyze.d.ts.map +1 -1
  218. package/out/zero-cache/src/services/analyze.js +2 -5
  219. package/out/zero-cache/src/services/analyze.js.map +1 -1
  220. package/out/zero-cache/src/services/change-source/common/backfill-manager.js.map +1 -1
  221. package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.js.map +1 -1
  222. package/out/zero-cache/src/services/change-source/common/replica-schema.js.map +1 -1
  223. package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
  224. package/out/zero-cache/src/services/change-source/pg/backfill-metadata.js.map +1 -1
  225. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js.map +1 -1
  226. package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
  227. package/out/zero-cache/src/services/change-source/pg/decommission.js.map +1 -1
  228. package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
  229. package/out/zero-cache/src/services/change-source/pg/logical-replication/binary-reader.js.map +1 -1
  230. package/out/zero-cache/src/services/change-source/pg/logical-replication/pgoutput-parser.js.map +1 -1
  231. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js.map +1 -1
  232. package/out/zero-cache/src/services/change-source/pg/lsn.js.map +1 -1
  233. package/out/zero-cache/src/services/change-source/pg/replication-slots.js.map +1 -1
  234. package/out/zero-cache/src/services/change-source/pg/schema/ddl.js.map +1 -1
  235. package/out/zero-cache/src/services/change-source/pg/schema/init.js.map +1 -1
  236. package/out/zero-cache/src/services/change-source/pg/schema/published.js.map +1 -1
  237. package/out/zero-cache/src/services/change-source/pg/schema/shard.js.map +1 -1
  238. package/out/zero-cache/src/services/change-source/pg/schema/validation.js.map +1 -1
  239. package/out/zero-cache/src/services/change-source/protocol/current/control.js.map +1 -1
  240. package/out/zero-cache/src/services/change-source/protocol/current/data.js +0 -2
  241. package/out/zero-cache/src/services/change-source/protocol/current/data.js.map +1 -1
  242. package/out/zero-cache/src/services/change-source/protocol/current/downstream.js.map +1 -1
  243. package/out/zero-cache/src/services/change-source/protocol/current/json.js.map +1 -1
  244. package/out/zero-cache/src/services/change-source/protocol/current/status.js.map +1 -1
  245. package/out/zero-cache/src/services/change-source/protocol/current/upstream.js.map +1 -1
  246. package/out/zero-cache/src/services/change-streamer/backup-monitor.js.map +1 -1
  247. package/out/zero-cache/src/services/change-streamer/broadcast.js.map +1 -1
  248. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
  249. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
  250. package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
  251. package/out/zero-cache/src/services/change-streamer/forwarder.js.map +1 -1
  252. package/out/zero-cache/src/services/change-streamer/replica-monitor.js.map +1 -1
  253. package/out/zero-cache/src/services/change-streamer/schema/init.js +25 -21
  254. package/out/zero-cache/src/services/change-streamer/schema/init.js.map +1 -1
  255. package/out/zero-cache/src/services/change-streamer/schema/tables.js.map +1 -1
  256. package/out/zero-cache/src/services/change-streamer/snapshot.js +0 -15
  257. package/out/zero-cache/src/services/change-streamer/snapshot.js.map +1 -1
  258. package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
  259. package/out/zero-cache/src/services/change-streamer/subscriber.js.map +1 -1
  260. package/out/zero-cache/src/services/heapz.js.map +1 -1
  261. package/out/zero-cache/src/services/http-service.js.map +1 -1
  262. package/out/zero-cache/src/services/life-cycle.js.map +1 -1
  263. package/out/zero-cache/src/services/limiter/sliding-window-limiter.js.map +1 -1
  264. package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
  265. package/out/zero-cache/src/services/mutagen/error.js.map +1 -1
  266. package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
  267. package/out/zero-cache/src/services/mutagen/pusher.js.map +1 -1
  268. package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
  269. package/out/zero-cache/src/services/replicator/incremental-sync.js.map +1 -1
  270. package/out/zero-cache/src/services/replicator/notifier.js.map +1 -1
  271. package/out/zero-cache/src/services/replicator/replication-status.js.map +1 -1
  272. package/out/zero-cache/src/services/replicator/replicator.js.map +1 -1
  273. package/out/zero-cache/src/services/replicator/reporter/recorder.js.map +1 -1
  274. package/out/zero-cache/src/services/replicator/reporter/report-schema.js.map +1 -1
  275. package/out/zero-cache/src/services/replicator/schema/change-log.js.map +1 -1
  276. package/out/zero-cache/src/services/replicator/schema/column-metadata.js.map +1 -1
  277. package/out/zero-cache/src/services/replicator/schema/replication-state.js.map +1 -1
  278. package/out/zero-cache/src/services/replicator/schema/table-metadata.js.map +1 -1
  279. package/out/zero-cache/src/services/replicator/write-worker-client.js.map +1 -1
  280. package/out/zero-cache/src/services/replicator/write-worker.js.map +1 -1
  281. package/out/zero-cache/src/services/run-ast.d.ts.map +1 -1
  282. package/out/zero-cache/src/services/run-ast.js +0 -1
  283. package/out/zero-cache/src/services/run-ast.js.map +1 -1
  284. package/out/zero-cache/src/services/runner.js.map +1 -1
  285. package/out/zero-cache/src/services/running-state.js.map +1 -1
  286. package/out/zero-cache/src/services/shadow-sync/shadow-sync-service.js.map +1 -1
  287. package/out/zero-cache/src/services/statz.js.map +1 -1
  288. package/out/zero-cache/src/services/view-syncer/active-users-gauge.js.map +1 -1
  289. package/out/zero-cache/src/services/view-syncer/client-handler.js.map +1 -1
  290. package/out/zero-cache/src/services/view-syncer/client-schema.js.map +1 -1
  291. package/out/zero-cache/src/services/view-syncer/connection-context-manager.js.map +1 -1
  292. package/out/zero-cache/src/services/view-syncer/cvr-purger.d.ts.map +1 -1
  293. package/out/zero-cache/src/services/view-syncer/cvr-purger.js +1 -2
  294. package/out/zero-cache/src/services/view-syncer/cvr-purger.js.map +1 -1
  295. package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts.map +1 -1
  296. package/out/zero-cache/src/services/view-syncer/cvr-store.js +1 -2
  297. package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
  298. package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
  299. package/out/zero-cache/src/services/view-syncer/drain-coordinator.js.map +1 -1
  300. package/out/zero-cache/src/services/view-syncer/inspect-handler.d.ts +14 -0
  301. package/out/zero-cache/src/services/view-syncer/inspect-handler.d.ts.map +1 -1
  302. package/out/zero-cache/src/services/view-syncer/inspect-handler.js +25 -2
  303. package/out/zero-cache/src/services/view-syncer/inspect-handler.js.map +1 -1
  304. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
  305. package/out/zero-cache/src/services/view-syncer/row-record-cache.js.map +1 -1
  306. package/out/zero-cache/src/services/view-syncer/row-set-signature.js.map +1 -1
  307. package/out/zero-cache/src/services/view-syncer/schema/cvr.js.map +1 -1
  308. package/out/zero-cache/src/services/view-syncer/schema/init.js +113 -97
  309. package/out/zero-cache/src/services/view-syncer/schema/init.js.map +1 -1
  310. package/out/zero-cache/src/services/view-syncer/schema/types.js +1 -103
  311. package/out/zero-cache/src/services/view-syncer/schema/types.js.map +1 -1
  312. package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
  313. package/out/zero-cache/src/services/view-syncer/tracer.js.map +1 -1
  314. package/out/zero-cache/src/services/view-syncer/ttl-clock.js.map +1 -1
  315. package/out/zero-cache/src/services/view-syncer/view-syncer.js +1 -4
  316. package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
  317. package/out/zero-cache/src/types/configuration-error.js.map +1 -1
  318. package/out/zero-cache/src/types/error-with-level.js.map +1 -1
  319. package/out/zero-cache/src/types/http.js.map +1 -1
  320. package/out/zero-cache/src/types/lexi-version.js.map +1 -1
  321. package/out/zero-cache/src/types/lite.js.map +1 -1
  322. package/out/zero-cache/src/types/names.js.map +1 -1
  323. package/out/zero-cache/src/types/pg-data-type.js.map +1 -1
  324. package/out/zero-cache/src/types/pg.js.map +1 -1
  325. package/out/zero-cache/src/types/processes.js.map +1 -1
  326. package/out/zero-cache/src/types/profiler.js.map +1 -1
  327. package/out/zero-cache/src/types/row-key.js.map +1 -1
  328. package/out/zero-cache/src/types/shards.js.map +1 -1
  329. package/out/zero-cache/src/types/sql.js.map +1 -1
  330. package/out/zero-cache/src/types/state-version.js.map +1 -1
  331. package/out/zero-cache/src/types/streams.js.map +1 -1
  332. package/out/zero-cache/src/types/strings.js.map +1 -1
  333. package/out/zero-cache/src/types/subscription.js.map +1 -1
  334. package/out/zero-cache/src/types/timeout.js.map +1 -1
  335. package/out/zero-cache/src/types/url-params.js.map +1 -1
  336. package/out/zero-cache/src/types/websocket-handoff.js.map +1 -1
  337. package/out/zero-cache/src/types/ws.js.map +1 -1
  338. package/out/zero-cache/src/workers/connect-params.js.map +1 -1
  339. package/out/zero-cache/src/workers/connection.js.map +1 -1
  340. package/out/zero-cache/src/workers/mutator.js.map +1 -1
  341. package/out/zero-cache/src/workers/replicator.js.map +1 -1
  342. package/out/zero-cache/src/workers/syncer-ws-message-handler.js.map +1 -1
  343. package/out/zero-cache/src/workers/syncer.js.map +1 -1
  344. package/out/zero-client/src/client/active-clients-manager.js.map +1 -1
  345. package/out/zero-client/src/client/connection-manager.js +1 -2
  346. package/out/zero-client/src/client/connection-manager.js.map +1 -1
  347. package/out/zero-client/src/client/connection.js.map +1 -1
  348. package/out/zero-client/src/client/context.js.map +1 -1
  349. package/out/zero-client/src/client/crud-impl.js.map +1 -1
  350. package/out/zero-client/src/client/crud.js.map +1 -1
  351. package/out/zero-client/src/client/custom.js +1 -2
  352. package/out/zero-client/src/client/custom.js.map +1 -1
  353. package/out/zero-client/src/client/delete-clients-manager.js.map +1 -1
  354. package/out/zero-client/src/client/enable-analytics.js.map +1 -1
  355. package/out/zero-client/src/client/error.js.map +1 -1
  356. package/out/zero-client/src/client/http-string.js.map +1 -1
  357. package/out/zero-client/src/client/inspector/client-group.js.map +1 -1
  358. package/out/zero-client/src/client/inspector/client.js.map +1 -1
  359. package/out/zero-client/src/client/inspector/html-dialog-prompt.js.map +1 -1
  360. package/out/zero-client/src/client/inspector/inspector.js.map +1 -1
  361. package/out/zero-client/src/client/inspector/lazy-inspector.js.map +1 -1
  362. package/out/zero-client/src/client/inspector/query.js.map +1 -1
  363. package/out/zero-client/src/client/ivm-branch.js.map +1 -1
  364. package/out/zero-client/src/client/keys.js.map +1 -1
  365. package/out/zero-client/src/client/log-options.js.map +1 -1
  366. package/out/zero-client/src/client/make-mutate-property.js.map +1 -1
  367. package/out/zero-client/src/client/make-replicache-mutators.js.map +1 -1
  368. package/out/zero-client/src/client/metrics.js.map +1 -1
  369. package/out/zero-client/src/client/mutation-tracker.js.map +1 -1
  370. package/out/zero-client/src/client/mutator-proxy.js.map +1 -1
  371. package/out/zero-client/src/client/options.js.map +1 -1
  372. package/out/zero-client/src/client/query-manager.js.map +1 -1
  373. package/out/zero-client/src/client/reload-error-handler.js.map +1 -1
  374. package/out/zero-client/src/client/server-option.js.map +1 -1
  375. package/out/zero-client/src/client/version.js +1 -1
  376. package/out/zero-client/src/client/zero-poke-handler.js.map +1 -1
  377. package/out/zero-client/src/client/zero-rep.js.map +1 -1
  378. package/out/zero-client/src/client/zero.d.ts.map +1 -1
  379. package/out/zero-client/src/client/zero.js +32 -58
  380. package/out/zero-client/src/client/zero.js.map +1 -1
  381. package/out/zero-client/src/util/nanoid.js.map +1 -1
  382. package/out/zero-client/src/util/socket.d.ts +3 -0
  383. package/out/zero-client/src/util/socket.d.ts.map +1 -0
  384. package/out/zero-client/src/util/socket.js +8 -0
  385. package/out/zero-client/src/util/socket.js.map +1 -0
  386. package/out/zero-protocol/src/analyze-query-result.js +0 -3
  387. package/out/zero-protocol/src/analyze-query-result.js.map +1 -1
  388. package/out/zero-protocol/src/application-error.js.map +1 -1
  389. package/out/zero-protocol/src/ast.js.map +1 -1
  390. package/out/zero-protocol/src/change-desired-queries.js +0 -1
  391. package/out/zero-protocol/src/change-desired-queries.js.map +1 -1
  392. package/out/zero-protocol/src/client-schema.js.map +1 -1
  393. package/out/zero-protocol/src/close-connection.js.map +1 -1
  394. package/out/zero-protocol/src/connect.js +0 -7
  395. package/out/zero-protocol/src/connect.js.map +1 -1
  396. package/out/zero-protocol/src/custom-queries.js.map +1 -1
  397. package/out/zero-protocol/src/data.js.map +1 -1
  398. package/out/zero-protocol/src/delete-clients.js.map +1 -1
  399. package/out/zero-protocol/src/down.js.map +1 -1
  400. package/out/zero-protocol/src/error.js +0 -7
  401. package/out/zero-protocol/src/error.js.map +1 -1
  402. package/out/zero-protocol/src/inspect-down.js.map +1 -1
  403. package/out/zero-protocol/src/inspect-up.js +0 -1
  404. package/out/zero-protocol/src/inspect-up.js.map +1 -1
  405. package/out/zero-protocol/src/mutate-server.js.map +1 -1
  406. package/out/zero-protocol/src/mutation-id.js.map +1 -1
  407. package/out/zero-protocol/src/mutation.js.map +1 -1
  408. package/out/zero-protocol/src/mutations-patch.js.map +1 -1
  409. package/out/zero-protocol/src/ping.js.map +1 -1
  410. package/out/zero-protocol/src/poke.js +0 -4
  411. package/out/zero-protocol/src/poke.js.map +1 -1
  412. package/out/zero-protocol/src/pong.js.map +1 -1
  413. package/out/zero-protocol/src/primary-key.js.map +1 -1
  414. package/out/zero-protocol/src/protocol-version.js.map +1 -1
  415. package/out/zero-protocol/src/pull.js.map +1 -1
  416. package/out/zero-protocol/src/push.js +0 -16
  417. package/out/zero-protocol/src/push.js.map +1 -1
  418. package/out/zero-protocol/src/queries-patch.js.map +1 -1
  419. package/out/zero-protocol/src/query-hash.js.map +1 -1
  420. package/out/zero-protocol/src/query-server.js.map +1 -1
  421. package/out/zero-protocol/src/row-patch.js.map +1 -1
  422. package/out/zero-protocol/src/up.js.map +1 -1
  423. package/out/zero-protocol/src/update-auth.js.map +1 -1
  424. package/out/zero-protocol/src/version.js.map +1 -1
  425. package/out/zero-react/src/use-connection-state.js.map +1 -1
  426. package/out/zero-react/src/use-query.js.map +1 -1
  427. package/out/zero-react/src/use-zero-online.js.map +1 -1
  428. package/out/zero-react/src/zero-provider.js.map +1 -1
  429. package/out/zero-schema/src/builder/relationship-builder.js.map +1 -1
  430. package/out/zero-schema/src/builder/schema-builder.js.map +1 -1
  431. package/out/zero-schema/src/builder/table-builder.js.map +1 -1
  432. package/out/zero-schema/src/compiled-permissions.js.map +1 -1
  433. package/out/zero-schema/src/name-mapper.js.map +1 -1
  434. package/out/zero-schema/src/permissions.js.map +1 -1
  435. package/out/zero-schema/src/schema-config.js.map +1 -1
  436. package/out/zero-server/src/adapters/drizzle.js.map +1 -1
  437. package/out/zero-server/src/adapters/kysely.js.map +1 -1
  438. package/out/zero-server/src/adapters/pg.js.map +1 -1
  439. package/out/zero-server/src/adapters/postgresjs.js.map +1 -1
  440. package/out/zero-server/src/adapters/prisma.js.map +1 -1
  441. package/out/zero-server/src/custom.js +1 -2
  442. package/out/zero-server/src/custom.js.map +1 -1
  443. package/out/zero-server/src/logging.js.map +1 -1
  444. package/out/zero-server/src/pg-query-executor.js.map +1 -1
  445. package/out/zero-server/src/process-mutations.js.map +1 -1
  446. package/out/zero-server/src/push-processor.js.map +1 -1
  447. package/out/zero-server/src/queries/process-queries.js.map +1 -1
  448. package/out/zero-server/src/schema.js.map +1 -1
  449. package/out/zero-server/src/zql-database.js.map +1 -1
  450. package/out/zero-solid/src/solid-view.js.map +1 -1
  451. package/out/zero-solid/src/use-connection-state.js.map +1 -1
  452. package/out/zero-solid/src/use-query.js.map +1 -1
  453. package/out/zero-solid/src/use-zero-online.js.map +1 -1
  454. package/out/zero-solid/src/use-zero.js.map +1 -1
  455. package/out/zero-types/src/format.js.map +1 -1
  456. package/out/zero-types/src/name-mapper.js.map +1 -1
  457. package/out/zql/src/builder/builder.js.map +1 -1
  458. package/out/zql/src/builder/debug-delegate.d.ts +0 -5
  459. package/out/zql/src/builder/debug-delegate.d.ts.map +1 -1
  460. package/out/zql/src/builder/debug-delegate.js +1 -10
  461. package/out/zql/src/builder/debug-delegate.js.map +1 -1
  462. package/out/zql/src/builder/filter.js.map +1 -1
  463. package/out/zql/src/builder/like.js.map +1 -1
  464. package/out/zql/src/error.js.map +1 -1
  465. package/out/zql/src/ivm/array-view.js.map +1 -1
  466. package/out/zql/src/ivm/cap.js.map +1 -1
  467. package/out/zql/src/ivm/change.js.map +1 -1
  468. package/out/zql/src/ivm/constraint.js +1 -1
  469. package/out/zql/src/ivm/constraint.js.map +1 -1
  470. package/out/zql/src/ivm/data.js.map +1 -1
  471. package/out/zql/src/ivm/exists.js.map +1 -1
  472. package/out/zql/src/ivm/fan-in.js.map +1 -1
  473. package/out/zql/src/ivm/fan-out.js.map +1 -1
  474. package/out/zql/src/ivm/filter-operators.js.map +1 -1
  475. package/out/zql/src/ivm/filter-push.js.map +1 -1
  476. package/out/zql/src/ivm/filter.js.map +1 -1
  477. package/out/zql/src/ivm/flipped-join.d.ts +8 -4
  478. package/out/zql/src/ivm/flipped-join.d.ts.map +1 -1
  479. package/out/zql/src/ivm/flipped-join.js +63 -59
  480. package/out/zql/src/ivm/flipped-join.js.map +1 -1
  481. package/out/zql/src/ivm/join-utils.js.map +1 -1
  482. package/out/zql/src/ivm/join.js.map +1 -1
  483. package/out/zql/src/ivm/maybe-split-and-push-edit-change.js.map +1 -1
  484. package/out/zql/src/ivm/memory-source.js.map +1 -1
  485. package/out/zql/src/ivm/memory-storage.js.map +1 -1
  486. package/out/zql/src/ivm/operator.d.ts +1 -1
  487. package/out/zql/src/ivm/operator.js.map +1 -1
  488. package/out/zql/src/ivm/push-accumulated.js.map +1 -1
  489. package/out/zql/src/ivm/schema.d.ts +8 -0
  490. package/out/zql/src/ivm/schema.d.ts.map +1 -1
  491. package/out/zql/src/ivm/skip-yields.js.map +1 -1
  492. package/out/zql/src/ivm/skip.js.map +1 -1
  493. package/out/zql/src/ivm/source.js.map +1 -1
  494. package/out/zql/src/ivm/stream.js.map +1 -1
  495. package/out/zql/src/ivm/take.js.map +1 -1
  496. package/out/zql/src/ivm/union-fan-in.js.map +1 -1
  497. package/out/zql/src/ivm/union-fan-out.js.map +1 -1
  498. package/out/zql/src/ivm/view-apply-change.js.map +1 -1
  499. package/out/zql/src/mutate/crud.js.map +1 -1
  500. package/out/zql/src/mutate/custom.js.map +1 -1
  501. package/out/zql/src/mutate/mutator-registry.js.map +1 -1
  502. package/out/zql/src/mutate/mutator.js.map +1 -1
  503. package/out/zql/src/planner/planner-builder.js.map +1 -1
  504. package/out/zql/src/planner/planner-connection.js.map +1 -1
  505. package/out/zql/src/planner/planner-constraint.js.map +1 -1
  506. package/out/zql/src/planner/planner-debug.js.map +1 -1
  507. package/out/zql/src/planner/planner-fan-in.js.map +1 -1
  508. package/out/zql/src/planner/planner-fan-out.js.map +1 -1
  509. package/out/zql/src/planner/planner-graph.js.map +1 -1
  510. package/out/zql/src/planner/planner-join.d.ts.map +1 -1
  511. package/out/zql/src/planner/planner-join.js +1 -2
  512. package/out/zql/src/planner/planner-join.js.map +1 -1
  513. package/out/zql/src/planner/planner-node.js.map +1 -1
  514. package/out/zql/src/planner/planner-source.js.map +1 -1
  515. package/out/zql/src/planner/planner-terminus.js.map +1 -1
  516. package/out/zql/src/query/complete-ordering.js.map +1 -1
  517. package/out/zql/src/query/create-builder.js.map +1 -1
  518. package/out/zql/src/query/error.js.map +1 -1
  519. package/out/zql/src/query/escape-like.js.map +1 -1
  520. package/out/zql/src/query/expression.js.map +1 -1
  521. package/out/zql/src/query/measure-push-operator.js.map +1 -1
  522. package/out/zql/src/query/metrics-delegate.js.map +1 -1
  523. package/out/zql/src/query/named.js.map +1 -1
  524. package/out/zql/src/query/query-delegate-base.js.map +1 -1
  525. package/out/zql/src/query/query-impl.js +1 -1
  526. package/out/zql/src/query/query-impl.js.map +1 -1
  527. package/out/zql/src/query/query-internals.js.map +1 -1
  528. package/out/zql/src/query/query-registry.js.map +1 -1
  529. package/out/zql/src/query/runnable-query-impl.js.map +1 -1
  530. package/out/zql/src/query/static-query.js.map +1 -1
  531. package/out/zql/src/query/ttl.js.map +1 -1
  532. package/out/zql/src/query/validate-input.js.map +1 -1
  533. package/out/zqlite/src/database-storage.js.map +1 -1
  534. package/out/zqlite/src/db.js.map +1 -1
  535. package/out/zqlite/src/explain-queries.js.map +1 -1
  536. package/out/zqlite/src/internal/sql-inline.js.map +1 -1
  537. package/out/zqlite/src/internal/sql.js.map +1 -1
  538. package/out/zqlite/src/internal/statement-cache.js.map +1 -1
  539. package/out/zqlite/src/query-builder.js.map +1 -1
  540. package/out/zqlite/src/query-delegate.js.map +1 -1
  541. package/out/zqlite/src/resolve-scalar-subqueries.js.map +1 -1
  542. package/out/zqlite/src/sqlite-cost-model.js.map +1 -1
  543. package/out/zqlite/src/sqlite-stat-fanout.js.map +1 -1
  544. package/out/zqlite/src/table-source.d.ts.map +1 -1
  545. package/out/zqlite/src/table-source.js +6 -6
  546. package/out/zqlite/src/table-source.js.map +1 -1
  547. package/package.json +26 -42
  548. package/out/shared/src/ring-buffer.d.ts +0 -32
  549. package/out/shared/src/ring-buffer.d.ts.map +0 -1
  550. package/out/shared/src/ring-buffer.js +0 -109
  551. package/out/shared/src/ring-buffer.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"process-mutations.js","names":["#dbProvider","#req","#params","#lc","#transactImpl","#checkAndIncrementLastMutationID","#getTransactionInput"],"sources":["../../../../zero-server/src/process-mutations.ts"],"sourcesContent":["import type {LogContext, 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 {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 {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\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<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 {@linkcode handleMutateRequest} instead.\n */\nexport const handleMutationRequest = handleMutateRequest;\n\n/**\n * Parsed query params accepted by {@linkcode 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 | {\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} & (\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): 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): 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 logLevel?: LogLevel | 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 logLevel,\n );\n\n const lc = createLogContext(normalized.logLevel).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 };\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 };\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 logLevel: LogLevel | 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 };\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: logLevel ?? 'info',\n };\n}\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 transact = async (\n mutation: CustomMutation,\n cb: TransactFnCallback<D>,\n ): Promise<MutationResponse> => {\n let appError: ApplicationError | undefined = undefined;\n for (;;) {\n try {\n const ret = await this.#transactImpl(mutation, cb, appError);\n if (appError !== undefined) {\n this.#lc.warn?.(\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 `Retry also failed for mutation ${mutation.id} for client ${mutation.clientID}`,\n error,\n );\n throw error;\n }\n\n // First attempt failed → store error and retry without mutator\n const originalError =\n error instanceof DatabaseTransactionError\n ? (error.cause ?? error)\n : error;\n appError = wrapWithApplicationError(originalError);\n this.#lc.warn?.(\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 );\n return ret;\n }\n\n async #transactImpl(\n mutation: CustomMutation,\n cb: TransactFnCallback<D>,\n appError: ApplicationError | 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 `Executing mutator '${mutation.name}' (id=${mutation.id})`,\n );\n await cb(dbTx, mutation.name, mutation.args[0]);\n } else {\n const mutationResult = makeAppErrorResponse(mutation, appError);\n await transactionHooks.writeMutationResult(mutationResult);\n }\n\n return {\n id: {\n clientID: mutation.clientID,\n id: mutation.id,\n },\n result: {},\n };\n },\n this.#getTransactionInput(mutation),\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(mutation: CustomMutation): TransactionProviderInput {\n return {\n upstreamSchema: this.#params.schema,\n clientGroupID: this.#req.clientGroupID,\n clientID: mutation.clientID,\n mutationID: mutation.id,\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 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
+ {"version":3,"file":"process-mutations.js","names":["#dbProvider","#req","#params","#lc","#transactImpl","#checkAndIncrementLastMutationID","#getTransactionInput"],"sources":["../../../../zero-server/src/process-mutations.ts"],"sourcesContent":["import type {LogContext, 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 {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 {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\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<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 {@linkcode handleMutateRequest} instead.\n */\nexport const handleMutationRequest = handleMutateRequest;\n\n/**\n * Parsed query params accepted by {@linkcode 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 | {\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} & (\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): 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): 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 logLevel?: LogLevel | 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 logLevel,\n );\n\n const lc = createLogContext(normalized.logLevel).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 };\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 };\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 logLevel: LogLevel | 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 };\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: logLevel ?? 'info',\n };\n}\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 transact = async (\n mutation: CustomMutation,\n cb: TransactFnCallback<D>,\n ): Promise<MutationResponse> => {\n let appError: ApplicationError | undefined = undefined;\n for (;;) {\n try {\n const ret = await this.#transactImpl(mutation, cb, appError);\n if (appError !== undefined) {\n this.#lc.warn?.(\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 `Retry also failed for mutation ${mutation.id} for client ${mutation.clientID}`,\n error,\n );\n throw error;\n }\n\n // First attempt failed → store error and retry without mutator\n const originalError =\n error instanceof DatabaseTransactionError\n ? (error.cause ?? error)\n : error;\n appError = wrapWithApplicationError(originalError);\n this.#lc.warn?.(\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 );\n return ret;\n }\n\n async #transactImpl(\n mutation: CustomMutation,\n cb: TransactFnCallback<D>,\n appError: ApplicationError | 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 `Executing mutator '${mutation.name}' (id=${mutation.id})`,\n );\n await cb(dbTx, mutation.name, mutation.args[0]);\n } else {\n const mutationResult = makeAppErrorResponse(mutation, appError);\n await transactionHooks.writeMutationResult(mutationResult);\n }\n\n return {\n id: {\n clientID: mutation.clientID,\n id: mutation.id,\n },\n result: {},\n };\n },\n this.#getTransactionInput(mutation),\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(mutation: CustomMutation): TransactionProviderInput {\n return {\n upstreamSchema: this.#params.schema,\n clientGroupID: this.#req.clientGroupID,\n clientID: mutation.clientID,\n mutationID: mutation.id,\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 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","#context","#processMutation","#dispatchMutation"],"sources":["../../../../zero-server/src/push-processor.ts"],"sourcesContent":["import {type LogLevel} 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 #context: C;\n\n constructor(dbProvider: D, context?: C, logLevel: LogLevel = 'info') {\n this.#dbProvider = dbProvider;\n this.#context = context as C;\n this.#logLevel = logLevel;\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 );\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 );\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<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;CAEA,YAAY,YAAe,SAAa,WAAqB,QAAQ;EACnE,KAAKA,cAAc;EACnB,KAAKE,WAAW;EAChB,KAAKD,YAAY;CACnB;CA0BA,QACE,UACA,oBACA,MACyB;EACzB,IAAI,8BAA8B,SAChC,OAAO,oBACL,KAAKD,cACJ,UAAU,aACT,KAAKG,iBAAiB,UAAU,UAAU,QAAQ,GACpD,oBACA,KAAKF,SACP;EAEF,OAAO,oBACL,KAAKD,cACJ,UAAU,aACT,KAAKG,iBAAiB,UAAU,UAAU,QAAQ,GACpD,oBACA,KAAK,MAAM,mDAAmD,GAC9D,KAAKF,SACP;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,MACe;EAEf,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":["#dbProvider","#logLevel","#context","#processMutation","#dispatchMutation"],"sources":["../../../../zero-server/src/push-processor.ts"],"sourcesContent":["import {type LogLevel} 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 #context: C;\n\n constructor(dbProvider: D, context?: C, logLevel: LogLevel = 'info') {\n this.#dbProvider = dbProvider;\n this.#context = context as C;\n this.#logLevel = logLevel;\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 );\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 );\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<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;CAEA,YAAY,YAAe,SAAa,WAAqB,QAAQ;AACnE,QAAA,aAAmB;AACnB,QAAA,UAAgB;AAChB,QAAA,WAAiB;;CA2BnB,QACE,UACA,oBACA,MACyB;AACzB,MAAI,8BAA8B,QAChC,QAAO,oBACL,MAAA,aACC,UAAU,aACT,MAAA,gBAAsB,UAAU,UAAU,SAAS,EACrD,oBACA,MAAA,SACD;AAEH,SAAO,oBACL,MAAA,aACC,UAAU,aACT,MAAA,gBAAsB,UAAU,UAAU,SAAS,EACrD,oBACA,KAAK,MAAM,oDAAoD,EAC/D,MAAA,SACD;;CAGH,iBACE,UACA,UACA,WAC2B;AAC3B,SAAO,UAAU,IAAI,MAAM,SACzB,MAAA,iBAAuB,UAAU,IAAI,MAAM,KAAK,CACjD;;CAGH,kBACE,UACA,MACA,KACA,MACe;EAEf,MAAM,UAAU,eAAe,UAAU,KAAK,YAAY;AAC1D,SAAO,OAAO,YAAY,YAAY,0BAA0B,MAAM;AACtE,MAAI,UAAU,QAAQ,CACpB,QAAO,QAAQ,GAAG;GAChB;GACA,KAAK,MAAA;GACL,IAAI;GACL,CAAC;AAEJ,SAAO,QAAQ,MAAM,KAAK"}
@@ -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 {@linkcode 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 {@linkcode 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 {@linkcode 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 parsed = v.parse(body, transformRequestMessageSchema);\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":[],"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 {@linkcode 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 {@linkcode 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 {@linkcode 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 parsed = v.parse(body, transformRequestMessageSchema);\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 +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 {DBTransaction} 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 dbTransaction: DBTransaction<unknown>,\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 // 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 const query = sql`\n SELECT\n c.table_schema::text AS schema,\n c.table_name::text AS table,\n c.column_name::text AS column,\n c.data_type::text AS \"dataType\",\n c.character_maximum_length AS length,\n c.numeric_precision AS precision,\n c.numeric_scale AS scale,\n t.typtype::text AS typtype,\n t.typname::text AS typename,\n CASE WHEN t.typelem <> 0 THEN et.typtype::text ELSE NULL END AS \"elemTyptype\",\n CASE WHEN t.typelem <> 0 THEN et.typname::text ELSE NULL END AS \"elemTypname\"\n FROM\n information_schema.columns c\n JOIN\n pg_catalog.pg_type t ON c.udt_name = t.typname\n LEFT JOIN\n pg_catalog.pg_type et ON t.typelem = et.oid\n JOIN\n pg_catalog.pg_namespace n ON t.typnamespace = n.oid\n WHERE\n (c.table_schema, c.table_name) IN (${inClause})\n `;\n const {text, values} = formatPg(query);\n const results: Iterable<ServerSchemaRow> = (await dbTransaction.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 }\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 }\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\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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"}
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 {DBTransaction} 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 dbTransaction: DBTransaction<unknown>,\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 // 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 const query = sql`\n SELECT\n c.table_schema::text AS schema,\n c.table_name::text AS table,\n c.column_name::text AS column,\n c.data_type::text AS \"dataType\",\n c.character_maximum_length AS length,\n c.numeric_precision AS precision,\n c.numeric_scale AS scale,\n t.typtype::text AS typtype,\n t.typname::text AS typename,\n CASE WHEN t.typelem <> 0 THEN et.typtype::text ELSE NULL END AS \"elemTyptype\",\n CASE WHEN t.typelem <> 0 THEN et.typname::text ELSE NULL END AS \"elemTypname\"\n FROM\n information_schema.columns c\n JOIN\n pg_catalog.pg_type t ON c.udt_name = t.typname\n LEFT JOIN\n pg_catalog.pg_type et ON t.typelem = et.oid\n JOIN\n pg_catalog.pg_namespace n ON t.typnamespace = n.oid\n WHERE\n (c.table_schema, c.table_name) IN (${inClause})\n `;\n const {text, values} = formatPg(query);\n const results: Iterable<ServerSchemaRow> = (await dbTransaction.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 }\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 }\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\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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"}
@@ -1 +1 @@
1
- {"version":3,"file":"zql-database.js","names":["#crudFactory","#makeServerTransaction"],"sources":["../../../../zero-server/src/zql-database.ts"],"sourcesContent":["import type {MaybePromise} from '../../shared/src/types.ts';\nimport {formatPg, sql} from '../../z2s/src/sql.ts';\nimport type {CleanupResultsArg} from '../../zero-protocol/src/mutation.ts';\nimport type {Schema} from '../../zero-types/src/schema.ts';\nimport type {DBConnection, DBTransaction} from '../../zql/src/mutate/custom.ts';\nimport type {\n HumanReadable,\n Query,\n RunOptions,\n} from '../../zql/src/query/query.ts';\nimport {CRUDMutatorFactory, type TransactionImpl} from './custom.ts';\nimport type {\n Database,\n TransactionProviderHooks,\n TransactionProviderInput,\n} from './process-mutations.ts';\n\n/**\n * Implements a Database for use with PushProcessor that is backed by Postgres.\n *\n * This implementation also implements the same ZQL interfaces for reading and\n * writing data that the Zero client does, so that mutator functions can be\n * shared across client and server.\n */\nexport class ZQLDatabase<\n TSchema extends Schema,\n TWrappedTransaction,\n> implements Database<TransactionImpl<TSchema, TWrappedTransaction>> {\n readonly connection: DBConnection<TWrappedTransaction>;\n readonly #crudFactory: CRUDMutatorFactory<TSchema>;\n\n constructor(connection: DBConnection<TWrappedTransaction>, schema: TSchema) {\n this.connection = connection;\n this.#crudFactory = new CRUDMutatorFactory(schema);\n }\n\n transaction<R>(\n callback: (\n tx: TransactionImpl<TSchema, TWrappedTransaction>,\n transactionHooks: TransactionProviderHooks,\n ) => MaybePromise<R>,\n transactionInput?: TransactionProviderInput,\n ): Promise<R> {\n // Icky hack. This is just here to have user not have to do this.\n // These interfaces need to be factored better.\n const {\n upstreamSchema = '',\n clientGroupID = '',\n clientID = '',\n mutationID = 0,\n } = transactionInput ?? {};\n return this.connection.transaction(async dbTx => {\n const zeroTx = await this.#makeServerTransaction(\n dbTx,\n clientID,\n mutationID,\n );\n\n return callback(zeroTx, {\n async updateClientMutationID() {\n const formatted = formatPg(\n sql`INSERT INTO ${sql.ident(upstreamSchema)}.clients \n as current (\"clientGroupID\", \"clientID\", \"lastMutationID\")\n VALUES (${clientGroupID}, ${clientID}, ${1})\n ON CONFLICT (\"clientGroupID\", \"clientID\")\n DO UPDATE SET \"lastMutationID\" = current.\"lastMutationID\" + 1\n RETURNING \"lastMutationID\"`,\n );\n\n const [{lastMutationID}] = (await dbTx.query(\n formatted.text,\n formatted.values,\n )) as {lastMutationID: bigint}[];\n\n return {lastMutationID};\n },\n\n async writeMutationResult(result) {\n const formatted = formatPg(\n sql`INSERT INTO ${sql.ident(upstreamSchema)}.mutations\n (\"clientGroupID\", \"clientID\", \"mutationID\", \"result\")\n VALUES (${clientGroupID}, ${result.id.clientID}, ${result.id.id}, ${JSON.stringify(\n result.result,\n )}::text::json)`,\n );\n await dbTx.query(formatted.text, formatted.values);\n },\n\n async deleteMutationResults(args: CleanupResultsArg) {\n if ('type' in args && args.type === 'bulk') {\n // Bulk deletion: delete all mutations for multiple clients\n const formatted = formatPg(\n sql`DELETE FROM ${sql.ident(upstreamSchema)}.\"mutations\"\n WHERE \"clientGroupID\" = ${args.clientGroupID}\n AND \"clientID\" = ANY(${args.clientIDs})`,\n );\n await dbTx.query(formatted.text, formatted.values);\n } else {\n // Single client (explicit 'single' or legacy without type): delete up to mutation ID\n const formatted = formatPg(\n sql`DELETE FROM ${sql.ident(upstreamSchema)}.\"mutations\"\n WHERE \"clientGroupID\" = ${args.clientGroupID}\n AND \"clientID\" = ${args.clientID}\n AND \"mutationID\" <= ${args.upToMutationID}`,\n );\n await dbTx.query(formatted.text, formatted.values);\n }\n },\n });\n });\n }\n\n #makeServerTransaction(\n dbTx: DBTransaction<TWrappedTransaction>,\n clientID: string,\n mutationID: number,\n ) {\n return this.#crudFactory.createTransaction(dbTx, clientID, mutationID);\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 return this.transaction(tx => tx.run(query, options));\n }\n}\n"],"mappings":";;;;;;;;;;AAwBA,IAAa,cAAb,MAGqE;CACnE;CACA;CAEA,YAAY,YAA+C,QAAiB;EAC1E,KAAK,aAAa;EAClB,KAAKA,eAAe,IAAI,mBAAmB,MAAM;CACnD;CAEA,YACE,UAIA,kBACY;EAGZ,MAAM,EACJ,iBAAiB,IACjB,gBAAgB,IAChB,WAAW,IACX,aAAa,MACX,oBAAoB,CAAC;EACzB,OAAO,KAAK,WAAW,YAAY,OAAM,SAAQ;GAO/C,OAAO,SAAS,MANK,KAAKC,uBACxB,MACA,UACA,UACF,GAEwB;IACtB,MAAM,yBAAyB;KAC7B,MAAM,YAAY,SAChB,GAAG,eAAe,IAAI,MAAM,cAAc,EAAE;;kCAEtB,cAAc,IAAI,SAAS,IAAI,EAAE;;;+CAIzD;KAEA,MAAM,CAAC,EAAC,oBAAoB,MAAM,KAAK,MACrC,UAAU,MACV,UAAU,MACZ;KAEA,OAAO,EAAC,eAAc;IACxB;IAEA,MAAM,oBAAoB,QAAQ;KAChC,MAAM,YAAY,SAChB,GAAG,eAAe,IAAI,MAAM,cAAc,EAAE;;0BAE9B,cAAc,IAAI,OAAO,GAAG,SAAS,IAAI,OAAO,GAAG,GAAG,IAAI,KAAK,UACvE,OAAO,MACT,EAAE,cACR;KACA,MAAM,KAAK,MAAM,UAAU,MAAM,UAAU,MAAM;IACnD;IAEA,MAAM,sBAAsB,MAAyB;KACnD,IAAI,UAAU,QAAQ,KAAK,SAAS,QAAQ;MAE1C,MAAM,YAAY,SAChB,GAAG,eAAe,IAAI,MAAM,cAAc,EAAE;4CACd,KAAK,cAAc;2CACpB,KAAK,UAAU,EAC9C;MACA,MAAM,KAAK,MAAM,UAAU,MAAM,UAAU,MAAM;KACnD,OAAO;MAEL,MAAM,YAAY,SAChB,GAAG,eAAe,IAAI,MAAM,cAAc,EAAE;4CACd,KAAK,cAAc;uCACxB,KAAK,SAAS;0CACX,KAAK,gBACnC;MACA,MAAM,KAAK,MAAM,UAAU,MAAM,UAAU,MAAM;KACnD;IACF;GACF,CAAC;EACH,CAAC;CACH;CAEA,uBACE,MACA,UACA,YACA;EACA,OAAO,KAAKD,aAAa,kBAAkB,MAAM,UAAU,UAAU;CACvE;CAEA,IACE,OACA,SACiC;EACjC,OAAO,KAAK,aAAY,OAAM,GAAG,IAAI,OAAO,OAAO,CAAC;CACtD;AACF"}
1
+ {"version":3,"file":"zql-database.js","names":["#crudFactory","#makeServerTransaction"],"sources":["../../../../zero-server/src/zql-database.ts"],"sourcesContent":["import type {MaybePromise} from '../../shared/src/types.ts';\nimport {formatPg, sql} from '../../z2s/src/sql.ts';\nimport type {CleanupResultsArg} from '../../zero-protocol/src/mutation.ts';\nimport type {Schema} from '../../zero-types/src/schema.ts';\nimport type {DBConnection, DBTransaction} from '../../zql/src/mutate/custom.ts';\nimport type {\n HumanReadable,\n Query,\n RunOptions,\n} from '../../zql/src/query/query.ts';\nimport {CRUDMutatorFactory, type TransactionImpl} from './custom.ts';\nimport type {\n Database,\n TransactionProviderHooks,\n TransactionProviderInput,\n} from './process-mutations.ts';\n\n/**\n * Implements a Database for use with PushProcessor that is backed by Postgres.\n *\n * This implementation also implements the same ZQL interfaces for reading and\n * writing data that the Zero client does, so that mutator functions can be\n * shared across client and server.\n */\nexport class ZQLDatabase<\n TSchema extends Schema,\n TWrappedTransaction,\n> implements Database<TransactionImpl<TSchema, TWrappedTransaction>> {\n readonly connection: DBConnection<TWrappedTransaction>;\n readonly #crudFactory: CRUDMutatorFactory<TSchema>;\n\n constructor(connection: DBConnection<TWrappedTransaction>, schema: TSchema) {\n this.connection = connection;\n this.#crudFactory = new CRUDMutatorFactory(schema);\n }\n\n transaction<R>(\n callback: (\n tx: TransactionImpl<TSchema, TWrappedTransaction>,\n transactionHooks: TransactionProviderHooks,\n ) => MaybePromise<R>,\n transactionInput?: TransactionProviderInput,\n ): Promise<R> {\n // Icky hack. This is just here to have user not have to do this.\n // These interfaces need to be factored better.\n const {\n upstreamSchema = '',\n clientGroupID = '',\n clientID = '',\n mutationID = 0,\n } = transactionInput ?? {};\n return this.connection.transaction(async dbTx => {\n const zeroTx = await this.#makeServerTransaction(\n dbTx,\n clientID,\n mutationID,\n );\n\n return callback(zeroTx, {\n async updateClientMutationID() {\n const formatted = formatPg(\n sql`INSERT INTO ${sql.ident(upstreamSchema)}.clients \n as current (\"clientGroupID\", \"clientID\", \"lastMutationID\")\n VALUES (${clientGroupID}, ${clientID}, ${1})\n ON CONFLICT (\"clientGroupID\", \"clientID\")\n DO UPDATE SET \"lastMutationID\" = current.\"lastMutationID\" + 1\n RETURNING \"lastMutationID\"`,\n );\n\n const [{lastMutationID}] = (await dbTx.query(\n formatted.text,\n formatted.values,\n )) as {lastMutationID: bigint}[];\n\n return {lastMutationID};\n },\n\n async writeMutationResult(result) {\n const formatted = formatPg(\n sql`INSERT INTO ${sql.ident(upstreamSchema)}.mutations\n (\"clientGroupID\", \"clientID\", \"mutationID\", \"result\")\n VALUES (${clientGroupID}, ${result.id.clientID}, ${result.id.id}, ${JSON.stringify(\n result.result,\n )}::text::json)`,\n );\n await dbTx.query(formatted.text, formatted.values);\n },\n\n async deleteMutationResults(args: CleanupResultsArg) {\n if ('type' in args && args.type === 'bulk') {\n // Bulk deletion: delete all mutations for multiple clients\n const formatted = formatPg(\n sql`DELETE FROM ${sql.ident(upstreamSchema)}.\"mutations\"\n WHERE \"clientGroupID\" = ${args.clientGroupID}\n AND \"clientID\" = ANY(${args.clientIDs})`,\n );\n await dbTx.query(formatted.text, formatted.values);\n } else {\n // Single client (explicit 'single' or legacy without type): delete up to mutation ID\n const formatted = formatPg(\n sql`DELETE FROM ${sql.ident(upstreamSchema)}.\"mutations\"\n WHERE \"clientGroupID\" = ${args.clientGroupID}\n AND \"clientID\" = ${args.clientID}\n AND \"mutationID\" <= ${args.upToMutationID}`,\n );\n await dbTx.query(formatted.text, formatted.values);\n }\n },\n });\n });\n }\n\n #makeServerTransaction(\n dbTx: DBTransaction<TWrappedTransaction>,\n clientID: string,\n mutationID: number,\n ) {\n return this.#crudFactory.createTransaction(dbTx, clientID, mutationID);\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 return this.transaction(tx => tx.run(query, options));\n }\n}\n"],"mappings":";;;;;;;;;;AAwBA,IAAa,cAAb,MAGqE;CACnE;CACA;CAEA,YAAY,YAA+C,QAAiB;AAC1E,OAAK,aAAa;AAClB,QAAA,cAAoB,IAAI,mBAAmB,OAAO;;CAGpD,YACE,UAIA,kBACY;EAGZ,MAAM,EACJ,iBAAiB,IACjB,gBAAgB,IAChB,WAAW,IACX,aAAa,MACX,oBAAoB,EAAE;AAC1B,SAAO,KAAK,WAAW,YAAY,OAAM,SAAQ;AAO/C,UAAO,SANQ,MAAM,MAAA,sBACnB,MACA,UACA,WACD,EAEuB;IACtB,MAAM,yBAAyB;KAC7B,MAAM,YAAY,SAChB,GAAG,eAAe,IAAI,MAAM,eAAe,CAAC;;kCAEtB,cAAc,IAAI,SAAS,IAAI,EAAE;;;gDAIxD;KAED,MAAM,CAAC,EAAC,oBAAoB,MAAM,KAAK,MACrC,UAAU,MACV,UAAU,OACX;AAED,YAAO,EAAC,gBAAe;;IAGzB,MAAM,oBAAoB,QAAQ;KAChC,MAAM,YAAY,SAChB,GAAG,eAAe,IAAI,MAAM,eAAe,CAAC;;0BAE9B,cAAc,IAAI,OAAO,GAAG,SAAS,IAAI,OAAO,GAAG,GAAG,IAAI,KAAK,UACvE,OAAO,OACR,CAAC,eACP;AACD,WAAM,KAAK,MAAM,UAAU,MAAM,UAAU,OAAO;;IAGpD,MAAM,sBAAsB,MAAyB;AACnD,SAAI,UAAU,QAAQ,KAAK,SAAS,QAAQ;MAE1C,MAAM,YAAY,SAChB,GAAG,eAAe,IAAI,MAAM,eAAe,CAAC;4CACd,KAAK,cAAc;2CACpB,KAAK,UAAU,GAC7C;AACD,YAAM,KAAK,MAAM,UAAU,MAAM,UAAU,OAAO;YAC7C;MAEL,MAAM,YAAY,SAChB,GAAG,eAAe,IAAI,MAAM,eAAe,CAAC;4CACd,KAAK,cAAc;uCACxB,KAAK,SAAS;0CACX,KAAK,iBAClC;AACD,YAAM,KAAK,MAAM,UAAU,MAAM,UAAU,OAAO;;;IAGvD,CAAC;IACF;;CAGJ,uBACE,MACA,UACA,YACA;AACA,SAAO,MAAA,YAAkB,kBAAkB,MAAM,UAAU,WAAW;;CAGxE,IACE,OACA,SACiC;AACjC,SAAO,KAAK,aAAY,OAAM,GAAG,IAAI,OAAO,QAAQ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"solid-view.js","names":["#input","#format","#onDestroy","#retry","#updateTTL","#onTransactionCommit","#createEmptyRoot","#applyChangesToRoot","#setState","#makeError","#builderRoot","#applyChanges","#pendingChanges","#applyChangeToRoot"],"sources":["../../../../zero-solid/src/solid-view.ts"],"sourcesContent":["import {produce, reconcile, type SetStoreFunction} from 'solid-js/store';\nimport {emptyArray} from '../../shared/src/sentinels.ts';\nimport {ChangeIndex} from '../../zql/src/ivm/change-index.ts';\nimport {ChangeType} from '../../zql/src/ivm/change-type.ts';\nimport {\n applyChange,\n idSymbol,\n skipYields,\n type ViewChange,\n} from './bindings.ts';\nimport {\n type AnyViewFactory,\n type Change,\n type Entry,\n type ErroredQuery,\n type Format,\n type Input,\n type Node,\n type Output,\n type Query,\n type QueryErrorDetails,\n type QueryResultDetails,\n type Schema,\n type Stream,\n type TTL,\n} from './zero.ts';\n\nexport type State = [Entry, QueryResultDetails];\n\nexport const COMPLETE: QueryResultDetails = Object.freeze({type: 'complete'});\nexport const UNKNOWN: QueryResultDetails = Object.freeze({type: 'unknown'});\n\nexport class SolidView implements Output {\n readonly #input: Input;\n readonly #format: Format;\n readonly #onDestroy: () => void;\n readonly #retry: () => void;\n\n #setState: SetStoreFunction<State>;\n\n // Optimization: if the store is currently empty we build up\n // the view on a plain old JS object stored at #builderRoot, and return\n // that for the new state on transaction commit. This avoids building up\n // large views from scratch via solid produce. The proxy object used by\n // solid produce is slow and in this case we don't care about solid tracking\n // the fine grained changes (everything has changed, it's all new). For a\n // test case with a view with 3000 rows, each row having 2 children, this\n // optimization reduced #applyChanges time from 743ms to 133ms.\n #builderRoot: Entry | undefined;\n #pendingChanges: ViewChange[] = [];\n readonly #updateTTL: (ttl: TTL) => void;\n\n constructor(\n input: Input,\n onTransactionCommit: (cb: () => void) => void,\n format: Format,\n onDestroy: () => void,\n queryComplete: true | ErroredQuery | Promise<true>,\n updateTTL: (ttl: TTL) => void,\n setState: SetStoreFunction<State>,\n retry: () => void,\n ) {\n this.#input = input;\n onTransactionCommit(this.#onTransactionCommit);\n this.#format = format;\n this.#onDestroy = onDestroy;\n this.#updateTTL = updateTTL;\n this.#retry = retry;\n\n input.setOutput(this);\n\n const initialRoot = this.#createEmptyRoot();\n this.#applyChangesToRoot(\n skipYields(input.fetch({})),\n node => ({type: 'add', node}),\n initialRoot,\n );\n\n this.#setState = setState;\n this.#setState(\n reconcile(\n [\n initialRoot,\n queryComplete === true\n ? COMPLETE\n : 'error' in queryComplete\n ? this.#makeError(queryComplete)\n : UNKNOWN,\n ],\n {\n // solidjs's types want a string, but a symbol works\n key: idSymbol as unknown as string,\n },\n ),\n );\n\n if (isEmptyRoot(initialRoot)) {\n this.#builderRoot = this.#createEmptyRoot();\n }\n\n if (queryComplete !== true && !('error' in queryComplete)) {\n void queryComplete\n .then(() => {\n this.#setState(prev => [prev[0], COMPLETE]);\n })\n .catch((error: ErroredQuery) => {\n this.#setState(prev => [prev[0], this.#makeError(error)]);\n });\n }\n }\n\n #makeError(error: ErroredQuery): QueryErrorDetails {\n const message = error.message ?? 'An unknown error occurred';\n return {\n type: 'error',\n retry: this.#retry,\n refetch: this.#retry,\n error: {\n type: error.error,\n message,\n ...(error.details ? {details: error.details} : {}),\n },\n };\n }\n\n destroy(): void {\n this.#onDestroy();\n }\n\n #onTransactionCommit = () => {\n const builderRoot = this.#builderRoot;\n if (builderRoot) {\n if (!isEmptyRoot(builderRoot)) {\n this.#setState(\n 0,\n reconcile(builderRoot, {\n // solidjs's types want a string, but a symbol works\n key: idSymbol as unknown as string,\n }),\n );\n this.#setState(prev => [builderRoot, prev[1]]);\n this.#builderRoot = undefined;\n }\n } else {\n try {\n this.#applyChanges(this.#pendingChanges, c => c);\n } finally {\n this.#pendingChanges = [];\n }\n }\n };\n\n push(change: Change) {\n // Delay updating the solid store state until the transaction commit\n // (because each update of the solid store is quite expensive). If\n // this.#builderRoot is defined apply the changes to it (we are building\n // from an empty root), otherwise queue the changes to be applied\n // using produce at the end of the transaction but read the relationships\n // now as they are only valid to read when the push is received.\n if (this.#builderRoot) {\n this.#applyChangeToRoot(\n materializeRelationships(change),\n this.#builderRoot,\n );\n } else {\n this.#pendingChanges.push(materializeRelationships(change));\n }\n return emptyArray;\n }\n\n #applyChanges<T>(changes: Iterable<T>, mapper: (v: T) => ViewChange): void {\n this.#setState(\n produce((draftState: State) => {\n this.#applyChangesToRoot<T>(changes, mapper, draftState[0]);\n if (isEmptyRoot(draftState[0])) {\n this.#builderRoot = this.#createEmptyRoot();\n }\n }),\n );\n }\n\n #applyChangesToRoot<T>(\n changes: Iterable<T>,\n mapper: (v: T) => ViewChange,\n root: Entry,\n ) {\n for (const change of changes) {\n this.#applyChangeToRoot(mapper(change), root);\n }\n }\n\n #applyChangeToRoot(change: ViewChange, root: Entry) {\n applyChange(\n root,\n change,\n this.#input.getSchema(),\n '',\n this.#format,\n true /* withIDs */,\n );\n }\n\n #createEmptyRoot(): Entry {\n return {\n '': this.#format.singular ? undefined : [],\n };\n }\n\n updateTTL(ttl: TTL): void {\n this.#updateTTL(ttl);\n }\n}\n\nfunction materializeRelationships(change: Change): ViewChange {\n switch (change[ChangeIndex.TYPE]) {\n case ChangeType.ADD:\n return {\n type: 'add',\n node: materializeNodeRelationships(change[ChangeIndex.NODE]),\n };\n case ChangeType.REMOVE:\n return {\n type: 'remove',\n node: materializeNodeRelationships(change[ChangeIndex.NODE]),\n };\n case ChangeType.CHILD:\n return {\n type: 'child',\n node: {row: change[ChangeIndex.NODE].row},\n child: {\n relationshipName: change[ChangeIndex.CHILD_DATA].relationshipName,\n change: materializeRelationships(\n change[ChangeIndex.CHILD_DATA].change,\n ),\n },\n };\n case ChangeType.EDIT:\n return {\n type: 'edit',\n node: {row: change[ChangeIndex.NODE].row},\n oldNode: {row: change[ChangeIndex.OLD_NODE].row},\n };\n }\n}\n\nfunction materializeNodeRelationships(node: Node): Node {\n const relationships: Record<string, () => Stream<Node>> = {};\n for (const relationship in node.relationships) {\n const materialized: Node[] = [];\n for (const n of skipYields(node.relationships[relationship]())) {\n materialized.push(materializeNodeRelationships(n));\n }\n relationships[relationship] = () => materialized;\n }\n return {\n row: node.row,\n relationships,\n };\n}\n\nfunction isEmptyRoot(entry: Entry) {\n const data = entry[''];\n return data === undefined || (Array.isArray(data) && data.length === 0);\n}\n\nexport function createSolidViewFactory(\n setState: SetStoreFunction<State>,\n retry?: () => void,\n) {\n function solidViewFactory<\n TTable extends keyof TSchema['tables'] & string,\n TSchema extends Schema,\n TReturn,\n >(\n _query: Query<TTable, TSchema, TReturn>,\n input: Input,\n format: Format,\n onDestroy: () => void,\n onTransactionCommit: (cb: () => void) => void,\n queryComplete: true | ErroredQuery | Promise<true>,\n updateTTL: (ttl: TTL) => void,\n ) {\n return new SolidView(\n input,\n onTransactionCommit,\n format,\n onDestroy,\n queryComplete,\n updateTTL,\n setState,\n retry || (() => {}),\n );\n }\n\n solidViewFactory satisfies AnyViewFactory;\n\n return solidViewFactory;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"solid-view.js","names":["#input","#format","#onDestroy","#retry","#updateTTL","#onTransactionCommit","#createEmptyRoot","#applyChangesToRoot","#setState","#makeError","#builderRoot","#applyChanges","#pendingChanges","#applyChangeToRoot"],"sources":["../../../../zero-solid/src/solid-view.ts"],"sourcesContent":["import {produce, reconcile, type SetStoreFunction} from 'solid-js/store';\nimport {emptyArray} from '../../shared/src/sentinels.ts';\nimport {ChangeIndex} from '../../zql/src/ivm/change-index.ts';\nimport {ChangeType} from '../../zql/src/ivm/change-type.ts';\nimport {\n applyChange,\n idSymbol,\n skipYields,\n type ViewChange,\n} from './bindings.ts';\nimport {\n type AnyViewFactory,\n type Change,\n type Entry,\n type ErroredQuery,\n type Format,\n type Input,\n type Node,\n type Output,\n type Query,\n type QueryErrorDetails,\n type QueryResultDetails,\n type Schema,\n type Stream,\n type TTL,\n} from './zero.ts';\n\nexport type State = [Entry, QueryResultDetails];\n\nexport const COMPLETE: QueryResultDetails = Object.freeze({type: 'complete'});\nexport const UNKNOWN: QueryResultDetails = Object.freeze({type: 'unknown'});\n\nexport class SolidView implements Output {\n readonly #input: Input;\n readonly #format: Format;\n readonly #onDestroy: () => void;\n readonly #retry: () => void;\n\n #setState: SetStoreFunction<State>;\n\n // Optimization: if the store is currently empty we build up\n // the view on a plain old JS object stored at #builderRoot, and return\n // that for the new state on transaction commit. This avoids building up\n // large views from scratch via solid produce. The proxy object used by\n // solid produce is slow and in this case we don't care about solid tracking\n // the fine grained changes (everything has changed, it's all new). For a\n // test case with a view with 3000 rows, each row having 2 children, this\n // optimization reduced #applyChanges time from 743ms to 133ms.\n #builderRoot: Entry | undefined;\n #pendingChanges: ViewChange[] = [];\n readonly #updateTTL: (ttl: TTL) => void;\n\n constructor(\n input: Input,\n onTransactionCommit: (cb: () => void) => void,\n format: Format,\n onDestroy: () => void,\n queryComplete: true | ErroredQuery | Promise<true>,\n updateTTL: (ttl: TTL) => void,\n setState: SetStoreFunction<State>,\n retry: () => void,\n ) {\n this.#input = input;\n onTransactionCommit(this.#onTransactionCommit);\n this.#format = format;\n this.#onDestroy = onDestroy;\n this.#updateTTL = updateTTL;\n this.#retry = retry;\n\n input.setOutput(this);\n\n const initialRoot = this.#createEmptyRoot();\n this.#applyChangesToRoot(\n skipYields(input.fetch({})),\n node => ({type: 'add', node}),\n initialRoot,\n );\n\n this.#setState = setState;\n this.#setState(\n reconcile(\n [\n initialRoot,\n queryComplete === true\n ? COMPLETE\n : 'error' in queryComplete\n ? this.#makeError(queryComplete)\n : UNKNOWN,\n ],\n {\n // solidjs's types want a string, but a symbol works\n key: idSymbol as unknown as string,\n },\n ),\n );\n\n if (isEmptyRoot(initialRoot)) {\n this.#builderRoot = this.#createEmptyRoot();\n }\n\n if (queryComplete !== true && !('error' in queryComplete)) {\n void queryComplete\n .then(() => {\n this.#setState(prev => [prev[0], COMPLETE]);\n })\n .catch((error: ErroredQuery) => {\n this.#setState(prev => [prev[0], this.#makeError(error)]);\n });\n }\n }\n\n #makeError(error: ErroredQuery): QueryErrorDetails {\n const message = error.message ?? 'An unknown error occurred';\n return {\n type: 'error',\n retry: this.#retry,\n refetch: this.#retry,\n error: {\n type: error.error,\n message,\n ...(error.details ? {details: error.details} : {}),\n },\n };\n }\n\n destroy(): void {\n this.#onDestroy();\n }\n\n #onTransactionCommit = () => {\n const builderRoot = this.#builderRoot;\n if (builderRoot) {\n if (!isEmptyRoot(builderRoot)) {\n this.#setState(\n 0,\n reconcile(builderRoot, {\n // solidjs's types want a string, but a symbol works\n key: idSymbol as unknown as string,\n }),\n );\n this.#setState(prev => [builderRoot, prev[1]]);\n this.#builderRoot = undefined;\n }\n } else {\n try {\n this.#applyChanges(this.#pendingChanges, c => c);\n } finally {\n this.#pendingChanges = [];\n }\n }\n };\n\n push(change: Change) {\n // Delay updating the solid store state until the transaction commit\n // (because each update of the solid store is quite expensive). If\n // this.#builderRoot is defined apply the changes to it (we are building\n // from an empty root), otherwise queue the changes to be applied\n // using produce at the end of the transaction but read the relationships\n // now as they are only valid to read when the push is received.\n if (this.#builderRoot) {\n this.#applyChangeToRoot(\n materializeRelationships(change),\n this.#builderRoot,\n );\n } else {\n this.#pendingChanges.push(materializeRelationships(change));\n }\n return emptyArray;\n }\n\n #applyChanges<T>(changes: Iterable<T>, mapper: (v: T) => ViewChange): void {\n this.#setState(\n produce((draftState: State) => {\n this.#applyChangesToRoot<T>(changes, mapper, draftState[0]);\n if (isEmptyRoot(draftState[0])) {\n this.#builderRoot = this.#createEmptyRoot();\n }\n }),\n );\n }\n\n #applyChangesToRoot<T>(\n changes: Iterable<T>,\n mapper: (v: T) => ViewChange,\n root: Entry,\n ) {\n for (const change of changes) {\n this.#applyChangeToRoot(mapper(change), root);\n }\n }\n\n #applyChangeToRoot(change: ViewChange, root: Entry) {\n applyChange(\n root,\n change,\n this.#input.getSchema(),\n '',\n this.#format,\n true /* withIDs */,\n );\n }\n\n #createEmptyRoot(): Entry {\n return {\n '': this.#format.singular ? undefined : [],\n };\n }\n\n updateTTL(ttl: TTL): void {\n this.#updateTTL(ttl);\n }\n}\n\nfunction materializeRelationships(change: Change): ViewChange {\n switch (change[ChangeIndex.TYPE]) {\n case ChangeType.ADD:\n return {\n type: 'add',\n node: materializeNodeRelationships(change[ChangeIndex.NODE]),\n };\n case ChangeType.REMOVE:\n return {\n type: 'remove',\n node: materializeNodeRelationships(change[ChangeIndex.NODE]),\n };\n case ChangeType.CHILD:\n return {\n type: 'child',\n node: {row: change[ChangeIndex.NODE].row},\n child: {\n relationshipName: change[ChangeIndex.CHILD_DATA].relationshipName,\n change: materializeRelationships(\n change[ChangeIndex.CHILD_DATA].change,\n ),\n },\n };\n case ChangeType.EDIT:\n return {\n type: 'edit',\n node: {row: change[ChangeIndex.NODE].row},\n oldNode: {row: change[ChangeIndex.OLD_NODE].row},\n };\n }\n}\n\nfunction materializeNodeRelationships(node: Node): Node {\n const relationships: Record<string, () => Stream<Node>> = {};\n for (const relationship in node.relationships) {\n const materialized: Node[] = [];\n for (const n of skipYields(node.relationships[relationship]())) {\n materialized.push(materializeNodeRelationships(n));\n }\n relationships[relationship] = () => materialized;\n }\n return {\n row: node.row,\n relationships,\n };\n}\n\nfunction isEmptyRoot(entry: Entry) {\n const data = entry[''];\n return data === undefined || (Array.isArray(data) && data.length === 0);\n}\n\nexport function createSolidViewFactory(\n setState: SetStoreFunction<State>,\n retry?: () => void,\n) {\n function solidViewFactory<\n TTable extends keyof TSchema['tables'] & string,\n TSchema extends Schema,\n TReturn,\n >(\n _query: Query<TTable, TSchema, TReturn>,\n input: Input,\n format: Format,\n onDestroy: () => void,\n onTransactionCommit: (cb: () => void) => void,\n queryComplete: true | ErroredQuery | Promise<true>,\n updateTTL: (ttl: TTL) => void,\n ) {\n return new SolidView(\n input,\n onTransactionCommit,\n format,\n onDestroy,\n queryComplete,\n updateTTL,\n setState,\n retry || (() => {}),\n );\n }\n\n solidViewFactory satisfies AnyViewFactory;\n\n return solidViewFactory;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-connection-state.js","names":[],"sources":["../../../../zero-solid/src/use-connection-state.ts"],"sourcesContent":["import {createEffect, createSignal, onCleanup, type Accessor} from 'solid-js';\nimport {useZero} from './use-zero.ts';\nimport type {ConnectionState} from './zero.ts';\n\n/**\n * Tracks the connection status of the current Zero instance.\n *\n * @returns The connection status of the Zero instance.\n * @see {@link ConnectionState} for more details on the connection state.\n */\nexport function useConnectionState(): Accessor<ConnectionState> {\n const zero = useZero();\n\n const [connectionState, setConnectionState] = createSignal<ConnectionState>(\n zero().connection.state.current,\n );\n\n createEffect(() => {\n const unsubscribe = zero().connection.state.subscribe(setConnectionState);\n\n onCleanup(unsubscribe);\n });\n\n return connectionState;\n}\n"],"mappings":";;;;;;;;;AAUA,SAAgB,qBAAgD;CAC9D,MAAM,OAAO,QAAQ;CAErB,MAAM,CAAC,iBAAiB,sBAAsB,aAC5C,KAAK,EAAE,WAAW,MAAM,OAC1B;CAEA,mBAAmB;EAGjB,UAFoB,KAAK,EAAE,WAAW,MAAM,UAAU,kBAE5C,CAAW;CACvB,CAAC;CAED,OAAO;AACT"}
1
+ {"version":3,"file":"use-connection-state.js","names":[],"sources":["../../../../zero-solid/src/use-connection-state.ts"],"sourcesContent":["import {createEffect, createSignal, onCleanup, type Accessor} from 'solid-js';\nimport {useZero} from './use-zero.ts';\nimport type {ConnectionState} from './zero.ts';\n\n/**\n * Tracks the connection status of the current Zero instance.\n *\n * @returns The connection status of the Zero instance.\n * @see {@link ConnectionState} for more details on the connection state.\n */\nexport function useConnectionState(): Accessor<ConnectionState> {\n const zero = useZero();\n\n const [connectionState, setConnectionState] = createSignal<ConnectionState>(\n zero().connection.state.current,\n );\n\n createEffect(() => {\n const unsubscribe = zero().connection.state.subscribe(setConnectionState);\n\n onCleanup(unsubscribe);\n });\n\n return connectionState;\n}\n"],"mappings":";;;;;;;;;AAUA,SAAgB,qBAAgD;CAC9D,MAAM,OAAO,SAAS;CAEtB,MAAM,CAAC,iBAAiB,sBAAsB,aAC5C,MAAM,CAAC,WAAW,MAAM,QACzB;AAED,oBAAmB;AAGjB,YAFoB,MAAM,CAAC,WAAW,MAAM,UAAU,mBAAmB,CAEnD;GACtB;AAEF,QAAO"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-query.js","names":[],"sources":["../../../../zero-solid/src/use-query.ts"],"sourcesContent":["import {\n createEffect,\n createMemo,\n createSignal,\n on,\n onCleanup,\n untrack,\n type Accessor,\n} from 'solid-js';\nimport {createStore} from 'solid-js/store';\nimport {\n addContextToQuery,\n asQueryInternals,\n DEFAULT_TTL_MS,\n} from './bindings.ts';\nimport {createSolidViewFactory, UNKNOWN, type State} from './solid-view.ts';\nimport {useZero} from './use-zero.ts';\nimport {\n type BaseDefaultContext,\n type BaseDefaultSchema,\n type DefaultContext,\n type DefaultSchema,\n type Falsy,\n type HumanReadable,\n type PullRow,\n type QueryOrQueryRequest,\n type QueryResultDetails,\n type ReadonlyJSONValue,\n type TTL,\n} from './zero.ts';\n\nexport type QueryResult<TReturn> = readonly [\n Accessor<HumanReadable<TReturn>>,\n Accessor<QueryResultDetails & {}>,\n];\n\n/**\n * Result type for \"maybe queries\" - queries that may be falsy.\n * The data value can be undefined when the query is falsy/disabled.\n */\nexport type MaybeQueryResult<TReturn> = readonly [\n Accessor<HumanReadable<TReturn> | undefined>,\n Accessor<QueryResultDetails & {}>,\n];\n\n// Deprecated in 0.22\n/**\n * @deprecated Use {@linkcode UseQueryOptions} instead.\n */\nexport type CreateQueryOptions = {\n ttl?: TTL | undefined;\n};\n\nexport type UseQueryOptions = {\n ttl?: TTL | undefined;\n};\n\n// Deprecated in 0.22\n/**\n * @deprecated Use {@linkcode useQuery} instead.\n */\nexport function createQuery<\n TTable extends keyof TSchema['tables'] & string,\n TInput extends ReadonlyJSONValue | undefined,\n TOutput extends ReadonlyJSONValue | undefined,\n TSchema extends BaseDefaultSchema = DefaultSchema,\n TReturn = PullRow<TTable, TSchema>,\n TContext extends BaseDefaultContext = DefaultContext,\n>(\n querySignal: Accessor<\n QueryOrQueryRequest<TTable, TInput, TOutput, TSchema, TReturn, TContext>\n >,\n options?: CreateQueryOptions | Accessor<CreateQueryOptions>,\n): QueryResult<TReturn> {\n return useQuery(querySignal, options);\n}\n\n// Overload 1: Query - returns QueryResult<TReturn>\nexport function useQuery<\n TTable extends keyof TSchema['tables'] & string,\n TInput extends ReadonlyJSONValue | undefined,\n TOutput extends ReadonlyJSONValue | undefined,\n TSchema extends BaseDefaultSchema = DefaultSchema,\n TReturn = PullRow<TTable, TSchema>,\n TContext extends BaseDefaultContext = DefaultContext,\n>(\n querySignal: Accessor<\n QueryOrQueryRequest<TTable, TInput, TOutput, TSchema, TReturn, TContext>\n >,\n options?: UseQueryOptions | Accessor<UseQueryOptions>,\n): QueryResult<TReturn>;\n\n// Overload 2: Maybe query\nexport function useQuery<\n TTable extends keyof TSchema['tables'] & string,\n TInput extends ReadonlyJSONValue | undefined,\n TOutput extends ReadonlyJSONValue | undefined,\n TSchema extends BaseDefaultSchema = DefaultSchema,\n TReturn = PullRow<TTable, TSchema>,\n TContext extends BaseDefaultContext = DefaultContext,\n>(\n querySignal: Accessor<\n | QueryOrQueryRequest<TTable, TInput, TOutput, TSchema, TReturn, TContext>\n | Falsy\n >,\n options?: UseQueryOptions | Accessor<UseQueryOptions>,\n): MaybeQueryResult<TReturn>;\n\n// Implementation\nexport function useQuery<\n TTable extends keyof TSchema['tables'] & string,\n TInput extends ReadonlyJSONValue | undefined,\n TOutput extends ReadonlyJSONValue | undefined,\n TSchema extends BaseDefaultSchema = DefaultSchema,\n TReturn = PullRow<TTable, TSchema>,\n TContext extends BaseDefaultContext = DefaultContext,\n>(\n querySignal: Accessor<\n | QueryOrQueryRequest<TTable, TInput, TOutput, TSchema, TReturn, TContext>\n | Falsy\n >,\n options?: UseQueryOptions | Accessor<UseQueryOptions>,\n): QueryResult<TReturn> | MaybeQueryResult<TReturn> {\n const [state, setState] = createStore<State>([\n {\n '': undefined,\n },\n UNKNOWN,\n ]);\n const initialRefetchKey = 0;\n const [refetchKey, setRefetchKey] = createSignal(initialRefetchKey);\n\n const refetch = () => {\n setRefetchKey(k => k + 1);\n };\n\n const zero = useZero<TSchema, undefined, TContext>();\n\n // Handle possibly falsy queries\n const q = createMemo(() => {\n const query = querySignal();\n if (!query) return undefined;\n return addContextToQuery(query, zero().context);\n });\n\n const qi = createMemo(() => {\n const query = q();\n if (!query) return undefined;\n return asQueryInternals(query);\n });\n\n const hash = createMemo(() => qi()?.hash());\n const ttl = createMemo(() => normalize(options)?.ttl ?? DEFAULT_TTL_MS);\n\n const initialTTL = ttl();\n\n const view = createMemo(() => {\n // Depend on hash instead of query to avoid recreating the view when the\n // query object changes but the hash is the same.\n const currentHash = hash();\n refetchKey();\n\n // If query is falsy, don't create a view and reset state to undefined\n if (currentHash === undefined) {\n setState([{'': undefined}, UNKNOWN]);\n return undefined;\n }\n\n const untrackedQuery = untrack(q);\n if (!untrackedQuery) {\n setState([{'': undefined}, UNKNOWN]);\n return undefined;\n }\n\n const v = zero().materialize(\n untrackedQuery,\n createSolidViewFactory(setState, refetch),\n {\n ttl: initialTTL,\n },\n );\n\n onCleanup(() => v.destroy());\n\n return v;\n });\n\n // Update TTL on existing view when it changes.\n createEffect(\n on(\n ttl,\n currentTTL => {\n view()?.updateTTL(currentTTL);\n },\n {defer: true},\n ),\n );\n\n return [() => state[0][''] as HumanReadable<TReturn>, () => state[1]];\n}\n\nfunction normalize<T>(options?: T | Accessor<T | undefined>): T | undefined {\n return typeof options === 'function' ? (options as Accessor<T>)() : options;\n}\n"],"mappings":";;;;;;;;;;;;;AA6DA,SAAgB,YAQd,aAGA,SACsB;CACtB,OAAO,SAAS,aAAa,OAAO;AACtC;AAkCA,SAAgB,SAQd,aAIA,SACkD;CAClD,MAAM,CAAC,OAAO,YAAY,YAAmB,CAC3C,EACE,IAAI,KAAA,EACN,GACA,OACF,CAAC;CAED,MAAM,CAAC,YAAY,iBAAiB,aAAa,CAAiB;CAElE,MAAM,gBAAgB;EACpB,eAAc,MAAK,IAAI,CAAC;CAC1B;CAEA,MAAM,OAAO,QAAsC;CAGnD,MAAM,IAAI,iBAAiB;EACzB,MAAM,QAAQ,YAAY;EAC1B,IAAI,CAAC,OAAO,OAAO,KAAA;EACnB,OAAO,kBAAkB,OAAO,KAAK,EAAE,OAAO;CAChD,CAAC;CAED,MAAM,KAAK,iBAAiB;EAC1B,MAAM,QAAQ,EAAE;EAChB,IAAI,CAAC,OAAO,OAAO,KAAA;EACnB,OAAO,iBAAiB,KAAK;CAC/B,CAAC;CAED,MAAM,OAAO,iBAAiB,GAAG,GAAG,KAAK,CAAC;CAC1C,MAAM,MAAM,iBAAiB,UAAU,OAAO,GAAG,OAAA,GAAqB;CAEtE,MAAM,aAAa,IAAI;CAEvB,MAAM,OAAO,iBAAiB;EAG5B,MAAM,cAAc,KAAK;EACzB,WAAW;EAGX,IAAI,gBAAgB,KAAA,GAAW;GAC7B,SAAS,CAAC,EAAC,IAAI,KAAA,EAAS,GAAG,OAAO,CAAC;GACnC;EACF;EAEA,MAAM,iBAAiB,QAAQ,CAAC;EAChC,IAAI,CAAC,gBAAgB;GACnB,SAAS,CAAC,EAAC,IAAI,KAAA,EAAS,GAAG,OAAO,CAAC;GACnC;EACF;EAEA,MAAM,IAAI,KAAK,EAAE,YACf,gBACA,uBAAuB,UAAU,OAAO,GACxC,EACE,KAAK,WACP,CACF;EAEA,gBAAgB,EAAE,QAAQ,CAAC;EAE3B,OAAO;CACT,CAAC;CAGD,aACE,GACE,MACA,eAAc;EACZ,KAAK,GAAG,UAAU,UAAU;CAC9B,GACA,EAAC,OAAO,KAAI,CACd,CACF;CAEA,OAAO,OAAO,MAAM,GAAG,WAAqC,MAAM,EAAE;AACtE;AAEA,SAAS,UAAa,SAAsD;CAC1E,OAAO,OAAO,YAAY,aAAc,QAAwB,IAAI;AACtE"}
1
+ {"version":3,"file":"use-query.js","names":[],"sources":["../../../../zero-solid/src/use-query.ts"],"sourcesContent":["import {\n createEffect,\n createMemo,\n createSignal,\n on,\n onCleanup,\n untrack,\n type Accessor,\n} from 'solid-js';\nimport {createStore} from 'solid-js/store';\nimport {\n addContextToQuery,\n asQueryInternals,\n DEFAULT_TTL_MS,\n} from './bindings.ts';\nimport {createSolidViewFactory, UNKNOWN, type State} from './solid-view.ts';\nimport {useZero} from './use-zero.ts';\nimport {\n type BaseDefaultContext,\n type BaseDefaultSchema,\n type DefaultContext,\n type DefaultSchema,\n type Falsy,\n type HumanReadable,\n type PullRow,\n type QueryOrQueryRequest,\n type QueryResultDetails,\n type ReadonlyJSONValue,\n type TTL,\n} from './zero.ts';\n\nexport type QueryResult<TReturn> = readonly [\n Accessor<HumanReadable<TReturn>>,\n Accessor<QueryResultDetails & {}>,\n];\n\n/**\n * Result type for \"maybe queries\" - queries that may be falsy.\n * The data value can be undefined when the query is falsy/disabled.\n */\nexport type MaybeQueryResult<TReturn> = readonly [\n Accessor<HumanReadable<TReturn> | undefined>,\n Accessor<QueryResultDetails & {}>,\n];\n\n// Deprecated in 0.22\n/**\n * @deprecated Use {@linkcode UseQueryOptions} instead.\n */\nexport type CreateQueryOptions = {\n ttl?: TTL | undefined;\n};\n\nexport type UseQueryOptions = {\n ttl?: TTL | undefined;\n};\n\n// Deprecated in 0.22\n/**\n * @deprecated Use {@linkcode useQuery} instead.\n */\nexport function createQuery<\n TTable extends keyof TSchema['tables'] & string,\n TInput extends ReadonlyJSONValue | undefined,\n TOutput extends ReadonlyJSONValue | undefined,\n TSchema extends BaseDefaultSchema = DefaultSchema,\n TReturn = PullRow<TTable, TSchema>,\n TContext extends BaseDefaultContext = DefaultContext,\n>(\n querySignal: Accessor<\n QueryOrQueryRequest<TTable, TInput, TOutput, TSchema, TReturn, TContext>\n >,\n options?: CreateQueryOptions | Accessor<CreateQueryOptions>,\n): QueryResult<TReturn> {\n return useQuery(querySignal, options);\n}\n\n// Overload 1: Query - returns QueryResult<TReturn>\nexport function useQuery<\n TTable extends keyof TSchema['tables'] & string,\n TInput extends ReadonlyJSONValue | undefined,\n TOutput extends ReadonlyJSONValue | undefined,\n TSchema extends BaseDefaultSchema = DefaultSchema,\n TReturn = PullRow<TTable, TSchema>,\n TContext extends BaseDefaultContext = DefaultContext,\n>(\n querySignal: Accessor<\n QueryOrQueryRequest<TTable, TInput, TOutput, TSchema, TReturn, TContext>\n >,\n options?: UseQueryOptions | Accessor<UseQueryOptions>,\n): QueryResult<TReturn>;\n\n// Overload 2: Maybe query\nexport function useQuery<\n TTable extends keyof TSchema['tables'] & string,\n TInput extends ReadonlyJSONValue | undefined,\n TOutput extends ReadonlyJSONValue | undefined,\n TSchema extends BaseDefaultSchema = DefaultSchema,\n TReturn = PullRow<TTable, TSchema>,\n TContext extends BaseDefaultContext = DefaultContext,\n>(\n querySignal: Accessor<\n | QueryOrQueryRequest<TTable, TInput, TOutput, TSchema, TReturn, TContext>\n | Falsy\n >,\n options?: UseQueryOptions | Accessor<UseQueryOptions>,\n): MaybeQueryResult<TReturn>;\n\n// Implementation\nexport function useQuery<\n TTable extends keyof TSchema['tables'] & string,\n TInput extends ReadonlyJSONValue | undefined,\n TOutput extends ReadonlyJSONValue | undefined,\n TSchema extends BaseDefaultSchema = DefaultSchema,\n TReturn = PullRow<TTable, TSchema>,\n TContext extends BaseDefaultContext = DefaultContext,\n>(\n querySignal: Accessor<\n | QueryOrQueryRequest<TTable, TInput, TOutput, TSchema, TReturn, TContext>\n | Falsy\n >,\n options?: UseQueryOptions | Accessor<UseQueryOptions>,\n): QueryResult<TReturn> | MaybeQueryResult<TReturn> {\n const [state, setState] = createStore<State>([\n {\n '': undefined,\n },\n UNKNOWN,\n ]);\n const initialRefetchKey = 0;\n const [refetchKey, setRefetchKey] = createSignal(initialRefetchKey);\n\n const refetch = () => {\n setRefetchKey(k => k + 1);\n };\n\n const zero = useZero<TSchema, undefined, TContext>();\n\n // Handle possibly falsy queries\n const q = createMemo(() => {\n const query = querySignal();\n if (!query) return undefined;\n return addContextToQuery(query, zero().context);\n });\n\n const qi = createMemo(() => {\n const query = q();\n if (!query) return undefined;\n return asQueryInternals(query);\n });\n\n const hash = createMemo(() => qi()?.hash());\n const ttl = createMemo(() => normalize(options)?.ttl ?? DEFAULT_TTL_MS);\n\n const initialTTL = ttl();\n\n const view = createMemo(() => {\n // Depend on hash instead of query to avoid recreating the view when the\n // query object changes but the hash is the same.\n const currentHash = hash();\n refetchKey();\n\n // If query is falsy, don't create a view and reset state to undefined\n if (currentHash === undefined) {\n setState([{'': undefined}, UNKNOWN]);\n return undefined;\n }\n\n const untrackedQuery = untrack(q);\n if (!untrackedQuery) {\n setState([{'': undefined}, UNKNOWN]);\n return undefined;\n }\n\n const v = zero().materialize(\n untrackedQuery,\n createSolidViewFactory(setState, refetch),\n {\n ttl: initialTTL,\n },\n );\n\n onCleanup(() => v.destroy());\n\n return v;\n });\n\n // Update TTL on existing view when it changes.\n createEffect(\n on(\n ttl,\n currentTTL => {\n view()?.updateTTL(currentTTL);\n },\n {defer: true},\n ),\n );\n\n return [() => state[0][''] as HumanReadable<TReturn>, () => state[1]];\n}\n\nfunction normalize<T>(options?: T | Accessor<T | undefined>): T | undefined {\n return typeof options === 'function' ? (options as Accessor<T>)() : options;\n}\n"],"mappings":";;;;;;;;;;;;;AA6DA,SAAgB,YAQd,aAGA,SACsB;AACtB,QAAO,SAAS,aAAa,QAAQ;;AAmCvC,SAAgB,SAQd,aAIA,SACkD;CAClD,MAAM,CAAC,OAAO,YAAY,YAAmB,CAC3C,EACE,IAAI,KAAA,GACL,EACD,QACD,CAAC;CAEF,MAAM,CAAC,YAAY,iBAAiB,aADV,EACyC;CAEnE,MAAM,gBAAgB;AACpB,iBAAc,MAAK,IAAI,EAAE;;CAG3B,MAAM,OAAO,SAAuC;CAGpD,MAAM,IAAI,iBAAiB;EACzB,MAAM,QAAQ,aAAa;AAC3B,MAAI,CAAC,MAAO,QAAO,KAAA;AACnB,SAAO,kBAAkB,OAAO,MAAM,CAAC,QAAQ;GAC/C;CAEF,MAAM,KAAK,iBAAiB;EAC1B,MAAM,QAAQ,GAAG;AACjB,MAAI,CAAC,MAAO,QAAO,KAAA;AACnB,SAAO,iBAAiB,MAAM;GAC9B;CAEF,MAAM,OAAO,iBAAiB,IAAI,EAAE,MAAM,CAAC;CAC3C,MAAM,MAAM,iBAAiB,UAAU,QAAQ,EAAE,OAAA,IAAsB;CAEvE,MAAM,aAAa,KAAK;CAExB,MAAM,OAAO,iBAAiB;EAG5B,MAAM,cAAc,MAAM;AAC1B,cAAY;AAGZ,MAAI,gBAAgB,KAAA,GAAW;AAC7B,YAAS,CAAC,EAAC,IAAI,KAAA,GAAU,EAAE,QAAQ,CAAC;AACpC;;EAGF,MAAM,iBAAiB,QAAQ,EAAE;AACjC,MAAI,CAAC,gBAAgB;AACnB,YAAS,CAAC,EAAC,IAAI,KAAA,GAAU,EAAE,QAAQ,CAAC;AACpC;;EAGF,MAAM,IAAI,MAAM,CAAC,YACf,gBACA,uBAAuB,UAAU,QAAQ,EACzC,EACE,KAAK,YACN,CACF;AAED,kBAAgB,EAAE,SAAS,CAAC;AAE5B,SAAO;GACP;AAGF,cACE,GACE,MACA,eAAc;AACZ,QAAM,EAAE,UAAU,WAAW;IAE/B,EAAC,OAAO,MAAK,CACd,CACF;AAED,QAAO,OAAO,MAAM,GAAG,WAAqC,MAAM,GAAG;;AAGvE,SAAS,UAAa,SAAsD;AAC1E,QAAO,OAAO,YAAY,aAAc,SAAyB,GAAG"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-zero-online.js","names":[],"sources":["../../../../zero-solid/src/use-zero-online.ts"],"sourcesContent":["import {createEffect, createSignal, onCleanup, type Accessor} from 'solid-js';\nimport {useZero} from './use-zero.ts';\n\n/**\n * Tracks the online status of the current Zero instance.\n *\n * @returns An accessor — call `online()` to get a reactive `boolean`.\n * @deprecated Use {@linkcode useConnectionState} instead, which provides more detailed connection state.\n */\nexport function useZeroOnline(): Accessor<boolean> {\n const zero = useZero();\n\n const [online, setOnline] = createSignal<boolean>(zero().online);\n\n createEffect(() => {\n const unsubscribe = zero().onOnline(setOnline);\n\n onCleanup(unsubscribe);\n });\n\n return online;\n}\n"],"mappings":";;;;;;;;;AASA,SAAgB,gBAAmC;CACjD,MAAM,OAAO,QAAQ;CAErB,MAAM,CAAC,QAAQ,aAAa,aAAsB,KAAK,EAAE,MAAM;CAE/D,mBAAmB;EAGjB,UAFoB,KAAK,EAAE,SAAS,SAE1B,CAAW;CACvB,CAAC;CAED,OAAO;AACT"}
1
+ {"version":3,"file":"use-zero-online.js","names":[],"sources":["../../../../zero-solid/src/use-zero-online.ts"],"sourcesContent":["import {createEffect, createSignal, onCleanup, type Accessor} from 'solid-js';\nimport {useZero} from './use-zero.ts';\n\n/**\n * Tracks the online status of the current Zero instance.\n *\n * @returns An accessor — call `online()` to get a reactive `boolean`.\n * @deprecated Use {@linkcode useConnectionState} instead, which provides more detailed connection state.\n */\nexport function useZeroOnline(): Accessor<boolean> {\n const zero = useZero();\n\n const [online, setOnline] = createSignal<boolean>(zero().online);\n\n createEffect(() => {\n const unsubscribe = zero().onOnline(setOnline);\n\n onCleanup(unsubscribe);\n });\n\n return online;\n}\n"],"mappings":";;;;;;;;;AASA,SAAgB,gBAAmC;CACjD,MAAM,OAAO,SAAS;CAEtB,MAAM,CAAC,QAAQ,aAAa,aAAsB,MAAM,CAAC,OAAO;AAEhE,oBAAmB;AAGjB,YAFoB,MAAM,CAAC,SAAS,UAAU,CAExB;GACtB;AAEF,QAAO"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-zero.js","names":[],"sources":["../../../../zero-solid/src/use-zero.ts"],"sourcesContent":["import {\n batch,\n createContext,\n createEffect,\n createMemo,\n createSignal,\n onCleanup,\n splitProps,\n untrack,\n useContext,\n type Accessor,\n type JSX,\n} from 'solid-js';\nimport {\n Zero,\n type BaseDefaultContext,\n type BaseDefaultSchema,\n type CustomMutatorDefs,\n type DefaultContext,\n type DefaultSchema,\n type ZeroOptions,\n} from './zero.ts';\n\nconst ZeroContext = createContext<\n // oxlint-disable-next-line no-explicit-any\n Accessor<Zero<any, any, any>> | undefined\n>(undefined);\n\n/**\n * @deprecated Use {@linkcode ZeroProvider} instead of managing your own Zero instance.\n */\nexport function createZero<\n S extends BaseDefaultSchema = DefaultSchema,\n MD extends CustomMutatorDefs | undefined = undefined,\n Context extends BaseDefaultContext = DefaultContext,\n>(options: ZeroOptions<S, MD, Context>): Zero<S, MD, Context> {\n const opts = {\n ...options,\n batchViewUpdates: batch,\n };\n return new Zero<S, MD, Context>(opts);\n}\n\nexport function useZero<\n S extends BaseDefaultSchema = DefaultSchema,\n MD extends CustomMutatorDefs | undefined = undefined,\n Context extends BaseDefaultContext = DefaultContext,\n>(): () => Zero<S, MD, Context> {\n const zero = useContext(ZeroContext);\n\n if (zero === undefined) {\n throw new Error('useZero must be used within a ZeroProvider');\n }\n return zero;\n}\n\n/**\n * @deprecated Use {@linkcode useZero} instead, alongside default types defined with:\n *\n * ```ts\n * declare module '@rocicorp/zero' {\n * interface DefaultTypes {\n * schema: typeof schema;\n * context: Context;\n * }\n * }\n */\nexport function createUseZero<\n S extends BaseDefaultSchema = DefaultSchema,\n MD extends CustomMutatorDefs | undefined = undefined,\n Context extends BaseDefaultContext = DefaultContext,\n>() {\n return () => useZero<S, MD, Context>();\n}\n\nexport function ZeroProvider<\n S extends BaseDefaultSchema = DefaultSchema,\n MD extends CustomMutatorDefs | undefined = undefined,\n Context extends BaseDefaultContext = DefaultContext,\n>(\n props: {\n children: JSX.Element;\n /**\n * Called after ZeroProvider constructs a new Zero instance.\n *\n * This runs only when the provider creates Zero from options, and is not\n * called when an existing instance is passed with `zero`.\n */\n init?: (zero: Zero<S, MD, Context>) => void;\n } & (\n | {\n zero: Zero<S, MD, Context>;\n }\n | ZeroOptions<S, MD, Context>\n ),\n) {\n let prevAuth = 'auth' in props ? props.auth : undefined;\n const [rotationGeneration, setRotationGeneration] = createSignal(0);\n\n const auth = createMemo(() => ('auth' in props ? props.auth : undefined));\n const hasAuth = createMemo(() => typeof auth() === 'string');\n\n const zero = createMemo(() => {\n rotationGeneration();\n\n if ('zero' in props) {\n return props.zero;\n }\n\n hasAuth();\n\n const [local, options] = splitProps(props, ['children', 'auth', 'init']);\n\n const authValue = untrack(auth);\n prevAuth = authValue;\n let rotationRequested = false;\n\n const scheduleRotation = () => {\n if (rotationRequested) {\n return;\n }\n rotationRequested = true;\n setRotationGeneration(gen => gen + 1);\n };\n\n const createdZero = new Zero({\n ...options,\n ...(authValue !== undefined ? {auth: authValue} : {}),\n batchViewUpdates: batch,\n onClientStateNotFound: () => {\n if (rotationRequested) {\n return;\n }\n\n if (options.onClientStateNotFound) {\n try {\n options.onClientStateNotFound();\n return;\n } catch {\n // rotate since zero client is now closed\n }\n }\n\n scheduleRotation();\n },\n });\n local.init?.(createdZero);\n onCleanup(() => createdZero.close());\n return createdZero;\n });\n\n createEffect(() => {\n const currentZero = zero();\n if (!currentZero || 'zero' in props) {\n return;\n }\n\n const currentAuth = auth();\n\n if (currentAuth !== prevAuth) {\n const previousAuth = prevAuth;\n prevAuth = currentAuth;\n\n if (typeof previousAuth === 'string' && typeof currentAuth === 'string') {\n void currentZero.connection.connect({auth: currentAuth});\n }\n }\n });\n\n return ZeroContext.Provider({\n value: zero,\n get children() {\n return props.children;\n },\n });\n}\n"],"mappings":";;;;AAuBA,IAAM,cAAc,cAGlB,KAAA,CAAS;;;;AAKX,SAAgB,WAId,SAA4D;CAK5D,OAAO,IAAI,KAAqB;EAH9B,GAAG;EACH,kBAAkB;CAEY,CAAI;AACtC;AAEA,SAAgB,UAIgB;CAC9B,MAAM,OAAO,WAAW,WAAW;CAEnC,IAAI,SAAS,KAAA,GACX,MAAM,IAAI,MAAM,4CAA4C;CAE9D,OAAO;AACT;;;;;;;;;;;;AAaA,SAAgB,gBAIZ;CACF,aAAa,QAAwB;AACvC;AAEA,SAAgB,aAKd,OAeA;CACA,IAAI,WAAW,UAAU,QAAQ,MAAM,OAAO,KAAA;CAC9C,MAAM,CAAC,oBAAoB,yBAAyB,aAAa,CAAC;CAElE,MAAM,OAAO,iBAAkB,UAAU,QAAQ,MAAM,OAAO,KAAA,CAAU;CACxE,MAAM,UAAU,iBAAiB,OAAO,KAAK,MAAM,QAAQ;CAE3D,MAAM,OAAO,iBAAiB;EAC5B,mBAAmB;EAEnB,IAAI,UAAU,OACZ,OAAO,MAAM;EAGf,QAAQ;EAER,MAAM,CAAC,OAAO,WAAW,WAAW,OAAO;GAAC;GAAY;GAAQ;EAAM,CAAC;EAEvE,MAAM,YAAY,QAAQ,IAAI;EAC9B,WAAW;EACX,IAAI,oBAAoB;EAExB,MAAM,yBAAyB;GAC7B,IAAI,mBACF;GAEF,oBAAoB;GACpB,uBAAsB,QAAO,MAAM,CAAC;EACtC;EAEA,MAAM,cAAc,IAAI,KAAK;GAC3B,GAAG;GACH,GAAI,cAAc,KAAA,IAAY,EAAC,MAAM,UAAS,IAAI,CAAC;GACnD,kBAAkB;GAClB,6BAA6B;IAC3B,IAAI,mBACF;IAGF,IAAI,QAAQ,uBACV,IAAI;KACF,QAAQ,sBAAsB;KAC9B;IACF,QAAQ,CAER;IAGF,iBAAiB;GACnB;EACF,CAAC;EACD,MAAM,OAAO,WAAW;EACxB,gBAAgB,YAAY,MAAM,CAAC;EACnC,OAAO;CACT,CAAC;CAED,mBAAmB;EACjB,MAAM,cAAc,KAAK;EACzB,IAAI,CAAC,eAAe,UAAU,OAC5B;EAGF,MAAM,cAAc,KAAK;EAEzB,IAAI,gBAAgB,UAAU;GAC5B,MAAM,eAAe;GACrB,WAAW;GAEX,IAAI,OAAO,iBAAiB,YAAY,OAAO,gBAAgB,UAC7D,YAAiB,WAAW,QAAQ,EAAC,MAAM,YAAW,CAAC;EAE3D;CACF,CAAC;CAED,OAAO,YAAY,SAAS;EAC1B,OAAO;EACP,IAAI,WAAW;GACb,OAAO,MAAM;EACf;CACF,CAAC;AACH"}
1
+ {"version":3,"file":"use-zero.js","names":[],"sources":["../../../../zero-solid/src/use-zero.ts"],"sourcesContent":["import {\n batch,\n createContext,\n createEffect,\n createMemo,\n createSignal,\n onCleanup,\n splitProps,\n untrack,\n useContext,\n type Accessor,\n type JSX,\n} from 'solid-js';\nimport {\n Zero,\n type BaseDefaultContext,\n type BaseDefaultSchema,\n type CustomMutatorDefs,\n type DefaultContext,\n type DefaultSchema,\n type ZeroOptions,\n} from './zero.ts';\n\nconst ZeroContext = createContext<\n // oxlint-disable-next-line no-explicit-any\n Accessor<Zero<any, any, any>> | undefined\n>(undefined);\n\n/**\n * @deprecated Use {@linkcode ZeroProvider} instead of managing your own Zero instance.\n */\nexport function createZero<\n S extends BaseDefaultSchema = DefaultSchema,\n MD extends CustomMutatorDefs | undefined = undefined,\n Context extends BaseDefaultContext = DefaultContext,\n>(options: ZeroOptions<S, MD, Context>): Zero<S, MD, Context> {\n const opts = {\n ...options,\n batchViewUpdates: batch,\n };\n return new Zero<S, MD, Context>(opts);\n}\n\nexport function useZero<\n S extends BaseDefaultSchema = DefaultSchema,\n MD extends CustomMutatorDefs | undefined = undefined,\n Context extends BaseDefaultContext = DefaultContext,\n>(): () => Zero<S, MD, Context> {\n const zero = useContext(ZeroContext);\n\n if (zero === undefined) {\n throw new Error('useZero must be used within a ZeroProvider');\n }\n return zero;\n}\n\n/**\n * @deprecated Use {@linkcode useZero} instead, alongside default types defined with:\n *\n * ```ts\n * declare module '@rocicorp/zero' {\n * interface DefaultTypes {\n * schema: typeof schema;\n * context: Context;\n * }\n * }\n */\nexport function createUseZero<\n S extends BaseDefaultSchema = DefaultSchema,\n MD extends CustomMutatorDefs | undefined = undefined,\n Context extends BaseDefaultContext = DefaultContext,\n>() {\n return () => useZero<S, MD, Context>();\n}\n\nexport function ZeroProvider<\n S extends BaseDefaultSchema = DefaultSchema,\n MD extends CustomMutatorDefs | undefined = undefined,\n Context extends BaseDefaultContext = DefaultContext,\n>(\n props: {\n children: JSX.Element;\n /**\n * Called after ZeroProvider constructs a new Zero instance.\n *\n * This runs only when the provider creates Zero from options, and is not\n * called when an existing instance is passed with `zero`.\n */\n init?: (zero: Zero<S, MD, Context>) => void;\n } & (\n | {\n zero: Zero<S, MD, Context>;\n }\n | ZeroOptions<S, MD, Context>\n ),\n) {\n let prevAuth = 'auth' in props ? props.auth : undefined;\n const [rotationGeneration, setRotationGeneration] = createSignal(0);\n\n const auth = createMemo(() => ('auth' in props ? props.auth : undefined));\n const hasAuth = createMemo(() => typeof auth() === 'string');\n\n const zero = createMemo(() => {\n rotationGeneration();\n\n if ('zero' in props) {\n return props.zero;\n }\n\n hasAuth();\n\n const [local, options] = splitProps(props, ['children', 'auth', 'init']);\n\n const authValue = untrack(auth);\n prevAuth = authValue;\n let rotationRequested = false;\n\n const scheduleRotation = () => {\n if (rotationRequested) {\n return;\n }\n rotationRequested = true;\n setRotationGeneration(gen => gen + 1);\n };\n\n const createdZero = new Zero({\n ...options,\n ...(authValue !== undefined ? {auth: authValue} : {}),\n batchViewUpdates: batch,\n onClientStateNotFound: () => {\n if (rotationRequested) {\n return;\n }\n\n if (options.onClientStateNotFound) {\n try {\n options.onClientStateNotFound();\n return;\n } catch {\n // rotate since zero client is now closed\n }\n }\n\n scheduleRotation();\n },\n });\n local.init?.(createdZero);\n onCleanup(() => createdZero.close());\n return createdZero;\n });\n\n createEffect(() => {\n const currentZero = zero();\n if (!currentZero || 'zero' in props) {\n return;\n }\n\n const currentAuth = auth();\n\n if (currentAuth !== prevAuth) {\n const previousAuth = prevAuth;\n prevAuth = currentAuth;\n\n if (typeof previousAuth === 'string' && typeof currentAuth === 'string') {\n void currentZero.connection.connect({auth: currentAuth});\n }\n }\n });\n\n return ZeroContext.Provider({\n value: zero,\n get children() {\n return props.children;\n },\n });\n}\n"],"mappings":";;;;AAuBA,IAAM,cAAc,cAGlB,KAAA,EAAU;;;;AAKZ,SAAgB,WAId,SAA4D;AAK5D,QAAO,IAAI,KAJE;EACX,GAAG;EACH,kBAAkB;EACnB,CACoC;;AAGvC,SAAgB,UAIgB;CAC9B,MAAM,OAAO,WAAW,YAAY;AAEpC,KAAI,SAAS,KAAA,EACX,OAAM,IAAI,MAAM,6CAA6C;AAE/D,QAAO;;;;;;;;;;;;;AAcT,SAAgB,gBAIZ;AACF,cAAa,SAAyB;;AAGxC,SAAgB,aAKd,OAeA;CACA,IAAI,WAAW,UAAU,QAAQ,MAAM,OAAO,KAAA;CAC9C,MAAM,CAAC,oBAAoB,yBAAyB,aAAa,EAAE;CAEnE,MAAM,OAAO,iBAAkB,UAAU,QAAQ,MAAM,OAAO,KAAA,EAAW;CACzE,MAAM,UAAU,iBAAiB,OAAO,MAAM,KAAK,SAAS;CAE5D,MAAM,OAAO,iBAAiB;AAC5B,sBAAoB;AAEpB,MAAI,UAAU,MACZ,QAAO,MAAM;AAGf,WAAS;EAET,MAAM,CAAC,OAAO,WAAW,WAAW,OAAO;GAAC;GAAY;GAAQ;GAAO,CAAC;EAExE,MAAM,YAAY,QAAQ,KAAK;AAC/B,aAAW;EACX,IAAI,oBAAoB;EAExB,MAAM,yBAAyB;AAC7B,OAAI,kBACF;AAEF,uBAAoB;AACpB,0BAAsB,QAAO,MAAM,EAAE;;EAGvC,MAAM,cAAc,IAAI,KAAK;GAC3B,GAAG;GACH,GAAI,cAAc,KAAA,IAAY,EAAC,MAAM,WAAU,GAAG,EAAE;GACpD,kBAAkB;GAClB,6BAA6B;AAC3B,QAAI,kBACF;AAGF,QAAI,QAAQ,sBACV,KAAI;AACF,aAAQ,uBAAuB;AAC/B;YACM;AAKV,sBAAkB;;GAErB,CAAC;AACF,QAAM,OAAO,YAAY;AACzB,kBAAgB,YAAY,OAAO,CAAC;AACpC,SAAO;GACP;AAEF,oBAAmB;EACjB,MAAM,cAAc,MAAM;AAC1B,MAAI,CAAC,eAAe,UAAU,MAC5B;EAGF,MAAM,cAAc,MAAM;AAE1B,MAAI,gBAAgB,UAAU;GAC5B,MAAM,eAAe;AACrB,cAAW;AAEX,OAAI,OAAO,iBAAiB,YAAY,OAAO,gBAAgB,SACxD,aAAY,WAAW,QAAQ,EAAC,MAAM,aAAY,CAAC;;GAG5D;AAEF,QAAO,YAAY,SAAS;EAC1B,OAAO;EACP,IAAI,WAAW;AACb,UAAO,MAAM;;EAEhB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"format.js","names":[],"sources":["../../../../zero-types/src/format.ts"],"sourcesContent":["/**\n * Format descriptor for query results.\n * Describes whether a result should be singular or a list,\n * and what the format of nested relationships should be.\n */\nexport type Format = {\n singular: boolean;\n relationships: Record<string, Format>;\n};\n\nexport const defaultFormat: Format = {\n singular: false,\n relationships: {},\n} as const;\n"],"mappings":";AAUA,IAAa,gBAAwB;CACnC,UAAU;CACV,eAAe,CAAC;AAClB"}
1
+ {"version":3,"file":"format.js","names":[],"sources":["../../../../zero-types/src/format.ts"],"sourcesContent":["/**\n * Format descriptor for query results.\n * Describes whether a result should be singular or a list,\n * and what the format of nested relationships should be.\n */\nexport type Format = {\n singular: boolean;\n relationships: Record<string, Format>;\n};\n\nexport const defaultFormat: Format = {\n singular: false,\n relationships: {},\n} as const;\n"],"mappings":";AAUA,IAAa,gBAAwB;CACnC,UAAU;CACV,eAAe,EAAE;CAClB"}
@@ -1 +1 @@
1
- {"version":3,"file":"name-mapper.js","names":["#tables","#getTable"],"sources":["../../../../zero-types/src/name-mapper.ts"],"sourcesContent":["import type {JSONValue, ReadonlyJSONValue} from '../../shared/src/json.ts';\n\n// Value type from zero-protocol (JSONValue/ReadonlyJSONValue | undefined)\n// Defined here to avoid circular dependency with zero-protocol\nexport type Value = JSONValue | ReadonlyJSONValue | undefined;\n\nexport type ColumnNames = {[src: string]: string};\n\nexport type DestNames = {\n tableName: string;\n columns: ColumnNames;\n allColumnsSame: boolean;\n};\n\nexport class NameMapper {\n readonly #tables = new Map<string, DestNames>();\n\n constructor(tables: Map<string, DestNames>) {\n this.#tables = tables;\n }\n\n #getTable(src: string, ctx?: JSONValue): DestNames {\n const table = this.#tables.get(src);\n if (!table) {\n throw new Error(\n `unknown table \"${src}\" ${!ctx ? '' : `in ${JSON.stringify(ctx)}`}`,\n );\n }\n return table;\n }\n\n tableName(src: string, context?: JSONValue): string {\n return this.#getTable(src, context).tableName;\n }\n\n tableNameIfKnown(src: string): string | undefined {\n return this.#tables.get(src)?.tableName;\n }\n\n columnName(table: string, src: string, ctx?: JSONValue): string {\n const dst = this.#getTable(table, ctx).columns[src];\n if (!dst) {\n throw new Error(\n `unknown column \"${src}\" of \"${table}\" table ${\n !ctx ? '' : `in ${JSON.stringify(ctx)}`\n }`,\n );\n }\n return dst;\n }\n\n row<V extends Value>(\n table: string,\n row: Readonly<Record<string, V>>,\n ): Readonly<Record<string, V>> {\n const dest = this.#getTable(table);\n const {allColumnsSame, columns} = dest;\n if (allColumnsSame) {\n return row;\n }\n const clientRow: Record<string, V> = {};\n for (const col in row) {\n // Note: columns with unknown names simply pass through.\n clientRow[columns[col] ?? col] = row[col];\n }\n return clientRow;\n }\n\n columns<Columns extends readonly string[] | undefined>(\n table: string,\n cols: Columns,\n ): Columns {\n const dest = this.#getTable(table);\n const {allColumnsSame, columns} = dest;\n\n // Note: Columns not defined in the schema simply pass through.\n return cols === undefined || allColumnsSame\n ? cols\n : (cols.map(col => columns[col] ?? col) as unknown as Columns);\n }\n}\n"],"mappings":";AAcA,IAAa,aAAb,MAAwB;CACtB,0BAAmB,IAAI,IAAuB;CAE9C,YAAY,QAAgC;EAC1C,KAAKA,UAAU;CACjB;CAEA,UAAU,KAAa,KAA4B;EACjD,MAAM,QAAQ,KAAKA,QAAQ,IAAI,GAAG;EAClC,IAAI,CAAC,OACH,MAAM,IAAI,MACR,kBAAkB,IAAI,IAAI,CAAC,MAAM,KAAK,MAAM,KAAK,UAAU,GAAG,KAChE;EAEF,OAAO;CACT;CAEA,UAAU,KAAa,SAA6B;EAClD,OAAO,KAAKC,UAAU,KAAK,OAAO,EAAE;CACtC;CAEA,iBAAiB,KAAiC;EAChD,OAAO,KAAKD,QAAQ,IAAI,GAAG,GAAG;CAChC;CAEA,WAAW,OAAe,KAAa,KAAyB;EAC9D,MAAM,MAAM,KAAKC,UAAU,OAAO,GAAG,EAAE,QAAQ;EAC/C,IAAI,CAAC,KACH,MAAM,IAAI,MACR,mBAAmB,IAAI,QAAQ,MAAM,UACnC,CAAC,MAAM,KAAK,MAAM,KAAK,UAAU,GAAG,KAExC;EAEF,OAAO;CACT;CAEA,IACE,OACA,KAC6B;EAE7B,MAAM,EAAC,gBAAgB,YADV,KAAKA,UAAU,KACM;EAClC,IAAI,gBACF,OAAO;EAET,MAAM,YAA+B,CAAC;EACtC,KAAK,MAAM,OAAO,KAEhB,UAAU,QAAQ,QAAQ,OAAO,IAAI;EAEvC,OAAO;CACT;CAEA,QACE,OACA,MACS;EAET,MAAM,EAAC,gBAAgB,YADV,KAAKA,UAAU,KACM;EAGlC,OAAO,SAAS,KAAA,KAAa,iBACzB,OACC,KAAK,KAAI,QAAO,QAAQ,QAAQ,GAAG;CAC1C;AACF"}
1
+ {"version":3,"file":"name-mapper.js","names":["#tables","#getTable"],"sources":["../../../../zero-types/src/name-mapper.ts"],"sourcesContent":["import type {JSONValue, ReadonlyJSONValue} from '../../shared/src/json.ts';\n\n// Value type from zero-protocol (JSONValue/ReadonlyJSONValue | undefined)\n// Defined here to avoid circular dependency with zero-protocol\nexport type Value = JSONValue | ReadonlyJSONValue | undefined;\n\nexport type ColumnNames = {[src: string]: string};\n\nexport type DestNames = {\n tableName: string;\n columns: ColumnNames;\n allColumnsSame: boolean;\n};\n\nexport class NameMapper {\n readonly #tables = new Map<string, DestNames>();\n\n constructor(tables: Map<string, DestNames>) {\n this.#tables = tables;\n }\n\n #getTable(src: string, ctx?: JSONValue): DestNames {\n const table = this.#tables.get(src);\n if (!table) {\n throw new Error(\n `unknown table \"${src}\" ${!ctx ? '' : `in ${JSON.stringify(ctx)}`}`,\n );\n }\n return table;\n }\n\n tableName(src: string, context?: JSONValue): string {\n return this.#getTable(src, context).tableName;\n }\n\n tableNameIfKnown(src: string): string | undefined {\n return this.#tables.get(src)?.tableName;\n }\n\n columnName(table: string, src: string, ctx?: JSONValue): string {\n const dst = this.#getTable(table, ctx).columns[src];\n if (!dst) {\n throw new Error(\n `unknown column \"${src}\" of \"${table}\" table ${\n !ctx ? '' : `in ${JSON.stringify(ctx)}`\n }`,\n );\n }\n return dst;\n }\n\n row<V extends Value>(\n table: string,\n row: Readonly<Record<string, V>>,\n ): Readonly<Record<string, V>> {\n const dest = this.#getTable(table);\n const {allColumnsSame, columns} = dest;\n if (allColumnsSame) {\n return row;\n }\n const clientRow: Record<string, V> = {};\n for (const col in row) {\n // Note: columns with unknown names simply pass through.\n clientRow[columns[col] ?? col] = row[col];\n }\n return clientRow;\n }\n\n columns<Columns extends readonly string[] | undefined>(\n table: string,\n cols: Columns,\n ): Columns {\n const dest = this.#getTable(table);\n const {allColumnsSame, columns} = dest;\n\n // Note: Columns not defined in the schema simply pass through.\n return cols === undefined || allColumnsSame\n ? cols\n : (cols.map(col => columns[col] ?? col) as unknown as Columns);\n }\n}\n"],"mappings":";AAcA,IAAa,aAAb,MAAwB;CACtB,0BAAmB,IAAI,KAAwB;CAE/C,YAAY,QAAgC;AAC1C,QAAA,SAAe;;CAGjB,UAAU,KAAa,KAA4B;EACjD,MAAM,QAAQ,MAAA,OAAa,IAAI,IAAI;AACnC,MAAI,CAAC,MACH,OAAM,IAAI,MACR,kBAAkB,IAAI,IAAI,CAAC,MAAM,KAAK,MAAM,KAAK,UAAU,IAAI,KAChE;AAEH,SAAO;;CAGT,UAAU,KAAa,SAA6B;AAClD,SAAO,MAAA,SAAe,KAAK,QAAQ,CAAC;;CAGtC,iBAAiB,KAAiC;AAChD,SAAO,MAAA,OAAa,IAAI,IAAI,EAAE;;CAGhC,WAAW,OAAe,KAAa,KAAyB;EAC9D,MAAM,MAAM,MAAA,SAAe,OAAO,IAAI,CAAC,QAAQ;AAC/C,MAAI,CAAC,IACH,OAAM,IAAI,MACR,mBAAmB,IAAI,QAAQ,MAAM,UACnC,CAAC,MAAM,KAAK,MAAM,KAAK,UAAU,IAAI,KAExC;AAEH,SAAO;;CAGT,IACE,OACA,KAC6B;EAE7B,MAAM,EAAC,gBAAgB,YADV,MAAA,SAAe,MAAM;AAElC,MAAI,eACF,QAAO;EAET,MAAM,YAA+B,EAAE;AACvC,OAAK,MAAM,OAAO,IAEhB,WAAU,QAAQ,QAAQ,OAAO,IAAI;AAEvC,SAAO;;CAGT,QACE,OACA,MACS;EAET,MAAM,EAAC,gBAAgB,YADV,MAAA,SAAe,MAAM;AAIlC,SAAO,SAAS,KAAA,KAAa,iBACzB,OACC,KAAK,KAAI,QAAO,QAAQ,QAAQ,IAAI"}