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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (659) hide show
  1. package/README.md +3 -28
  2. package/out/_virtual/{_@oxc-project_runtime@0.130.0 → _@oxc-project_runtime@0.122.0}/helpers/usingCtx.js +1 -1
  3. package/out/_virtual/_rolldown/runtime.js +1 -12
  4. package/out/analyze-query/src/analyze-cli.js.map +1 -1
  5. package/out/analyze-query/src/bin-analyze.js +1 -6
  6. package/out/analyze-query/src/bin-analyze.js.map +1 -1
  7. package/out/analyze-query/src/bin-transform.js.map +1 -1
  8. package/out/ast-to-zql/src/ast-to-zql.js.map +1 -1
  9. package/out/ast-to-zql/src/bin.js.map +1 -1
  10. package/out/ast-to-zql/src/format.js.map +1 -1
  11. package/out/datadog/src/datadog-log-sink.js.map +1 -1
  12. package/out/otel/src/enabled.js.map +1 -1
  13. package/out/otel/src/log-options.js.map +1 -1
  14. package/out/otel/src/maybe-time.js.map +1 -1
  15. package/out/otel/src/span.js.map +1 -1
  16. package/out/replicache/src/async-iterable-to-array.js.map +1 -1
  17. package/out/replicache/src/bg-interval.js.map +1 -1
  18. package/out/replicache/src/btree/diff.js.map +1 -1
  19. package/out/replicache/src/btree/node.js.map +1 -1
  20. package/out/replicache/src/btree/read.js.map +1 -1
  21. package/out/replicache/src/btree/splice.js.map +1 -1
  22. package/out/replicache/src/btree/write.js +3 -6
  23. package/out/replicache/src/btree/write.js.map +1 -1
  24. package/out/replicache/src/call-default-fetch.js.map +1 -1
  25. package/out/replicache/src/connection-loop-delegates.js.map +1 -1
  26. package/out/replicache/src/connection-loop.js.map +1 -1
  27. package/out/replicache/src/cookies.js.map +1 -1
  28. package/out/replicache/src/dag/chunk.js.map +1 -1
  29. package/out/replicache/src/dag/gc.js.map +1 -1
  30. package/out/replicache/src/dag/key.js.map +1 -1
  31. package/out/replicache/src/dag/lazy-store.js.map +1 -1
  32. package/out/replicache/src/dag/store-impl.js.map +1 -1
  33. package/out/replicache/src/dag/store.js.map +1 -1
  34. package/out/replicache/src/dag/visitor.js.map +1 -1
  35. package/out/replicache/src/db/commit.js.map +1 -1
  36. package/out/replicache/src/db/index.js.map +1 -1
  37. package/out/replicache/src/db/read.js.map +1 -1
  38. package/out/replicache/src/db/rebase.js.map +1 -1
  39. package/out/replicache/src/db/write.js.map +1 -1
  40. package/out/replicache/src/deleted-clients.js.map +1 -1
  41. package/out/replicache/src/error-responses.js.map +1 -1
  42. package/out/replicache/src/frozen-json.js.map +1 -1
  43. package/out/replicache/src/get-default-puller.js.map +1 -1
  44. package/out/replicache/src/get-default-pusher.js.map +1 -1
  45. package/out/replicache/src/get-kv-store-provider.js.map +1 -1
  46. package/out/replicache/src/hash.js.map +1 -1
  47. package/out/replicache/src/http-request-info.js.map +1 -1
  48. package/out/replicache/src/index-defs.js.map +1 -1
  49. package/out/replicache/src/kv/expo-sqlite/store.js.map +1 -1
  50. package/out/replicache/src/kv/idb-store-with-mem-fallback.js.map +1 -1
  51. package/out/replicache/src/kv/idb-store.js.map +1 -1
  52. package/out/replicache/src/kv/mem-store.js.map +1 -1
  53. package/out/replicache/src/kv/op-sqlite/store.js.map +1 -1
  54. package/out/replicache/src/kv/read-impl.js.map +1 -1
  55. package/out/replicache/src/kv/sqlite-store.d.ts.map +1 -1
  56. package/out/replicache/src/kv/sqlite-store.js +1 -4
  57. package/out/replicache/src/kv/sqlite-store.js.map +1 -1
  58. package/out/replicache/src/kv/throw-if-closed.js.map +1 -1
  59. package/out/replicache/src/kv/write-impl-base.js.map +1 -1
  60. package/out/replicache/src/kv/write-impl.js.map +1 -1
  61. package/out/replicache/src/lazy.js.map +1 -1
  62. package/out/replicache/src/log-options.js.map +1 -1
  63. package/out/replicache/src/make-idb-name.js.map +1 -1
  64. package/out/replicache/src/new-client-channel.js.map +1 -1
  65. package/out/replicache/src/on-persist-channel.js.map +1 -1
  66. package/out/replicache/src/patch-operation.js.map +1 -1
  67. package/out/replicache/src/pending-mutations.js.map +1 -1
  68. package/out/replicache/src/persist/client-gc.js.map +1 -1
  69. package/out/replicache/src/persist/client-group-gc.js.map +1 -1
  70. package/out/replicache/src/persist/client-groups.js +0 -40
  71. package/out/replicache/src/persist/client-groups.js.map +1 -1
  72. package/out/replicache/src/persist/clients.js +0 -28
  73. package/out/replicache/src/persist/clients.js.map +1 -1
  74. package/out/replicache/src/persist/collect-idb-databases.js.map +1 -1
  75. package/out/replicache/src/persist/gather-mem-only-visitor.js.map +1 -1
  76. package/out/replicache/src/persist/gather-not-cached-visitor.js.map +1 -1
  77. package/out/replicache/src/persist/heartbeat.js.map +1 -1
  78. package/out/replicache/src/persist/idb-databases-store-db-name.js.map +1 -1
  79. package/out/replicache/src/persist/idb-databases-store.js.map +1 -1
  80. package/out/replicache/src/persist/make-client-id.js.map +1 -1
  81. package/out/replicache/src/persist/persist.js.map +1 -1
  82. package/out/replicache/src/persist/refresh.js.map +1 -1
  83. package/out/replicache/src/process-scheduler.js.map +1 -1
  84. package/out/replicache/src/pusher.js.map +1 -1
  85. package/out/replicache/src/replicache-impl.js.map +1 -1
  86. package/out/replicache/src/report-error.js.map +1 -1
  87. package/out/replicache/src/request-idle.js.map +1 -1
  88. package/out/replicache/src/scan-iterator.js.map +1 -1
  89. package/out/replicache/src/scan-options.js.map +1 -1
  90. package/out/replicache/src/set-interval-with-signal.js.map +1 -1
  91. package/out/replicache/src/subscriptions.js.map +1 -1
  92. package/out/replicache/src/sync/diff.js.map +1 -1
  93. package/out/replicache/src/sync/ids.js.map +1 -1
  94. package/out/replicache/src/sync/patch.js.map +1 -1
  95. package/out/replicache/src/sync/pull-error.js.map +1 -1
  96. package/out/replicache/src/sync/pull.js.map +1 -1
  97. package/out/replicache/src/sync/push.js.map +1 -1
  98. package/out/replicache/src/sync/request-id.js.map +1 -1
  99. package/out/replicache/src/to-error.js.map +1 -1
  100. package/out/replicache/src/transaction-closed-error.js.map +1 -1
  101. package/out/replicache/src/transactions.js.map +1 -1
  102. package/out/replicache/src/with-transactions.js.map +1 -1
  103. package/out/shared/src/abort-error.js.map +1 -1
  104. package/out/shared/src/arrays.js.map +1 -1
  105. package/out/shared/src/asserts.js.map +1 -1
  106. package/out/shared/src/bigint-json.js.map +1 -1
  107. package/out/shared/src/binary-search.js.map +1 -1
  108. package/out/shared/src/broadcast-channel.js.map +1 -1
  109. package/out/shared/src/browser-env.js.map +1 -1
  110. package/out/shared/src/btree-set.js.map +1 -1
  111. package/out/shared/src/cache.js.map +1 -1
  112. package/out/shared/src/centroid.js.map +1 -1
  113. package/out/shared/src/custom-key-map.js.map +1 -1
  114. package/out/shared/src/custom-key-set.js.map +1 -1
  115. package/out/shared/src/deep-clone.js.map +1 -1
  116. package/out/shared/src/deep-merge.js.map +1 -1
  117. package/out/shared/src/document-visible.js.map +1 -1
  118. package/out/shared/src/dotenv.js.map +1 -1
  119. package/out/shared/src/error.js.map +1 -1
  120. package/out/shared/src/hash.js.map +1 -1
  121. package/out/shared/src/iterables.js.map +1 -1
  122. package/out/shared/src/json-schema.js.map +1 -1
  123. package/out/shared/src/json.js.map +1 -1
  124. package/out/shared/src/logging-test-utils.js.map +1 -1
  125. package/out/shared/src/logging.js.map +1 -1
  126. package/out/shared/src/map.js.map +1 -1
  127. package/out/shared/src/must.js.map +1 -1
  128. package/out/shared/src/object-traversal.js.map +1 -1
  129. package/out/shared/src/objects.js.map +1 -1
  130. package/out/shared/src/options.js.map +1 -1
  131. package/out/shared/src/parse-big-int.js.map +1 -1
  132. package/out/shared/src/promise-race.js.map +1 -1
  133. package/out/shared/src/queue.d.ts.map +1 -1
  134. package/out/shared/src/queue.js +21 -15
  135. package/out/shared/src/queue.js.map +1 -1
  136. package/out/shared/src/rand.js.map +1 -1
  137. package/out/shared/src/random-uint64.js.map +1 -1
  138. package/out/shared/src/random-values.js.map +1 -1
  139. package/out/shared/src/record-proxy.js.map +1 -1
  140. package/out/shared/src/resolved-promises.js.map +1 -1
  141. package/out/shared/src/sentinels.js.map +1 -1
  142. package/out/shared/src/set-utils.js.map +1 -1
  143. package/out/shared/src/size-of-value.js.map +1 -1
  144. package/out/shared/src/sleep.js.map +1 -1
  145. package/out/shared/src/sorted-entries.js.map +1 -1
  146. package/out/shared/src/string-compare.js.map +1 -1
  147. package/out/shared/src/subscribable.js.map +1 -1
  148. package/out/shared/src/tdigest-schema.js.map +1 -1
  149. package/out/shared/src/tdigest.js.map +1 -1
  150. package/out/shared/src/valita.js.map +1 -1
  151. package/out/z2s/src/compiler.js.map +1 -1
  152. package/out/z2s/src/sql.js.map +1 -1
  153. package/out/zero/package.js +23 -23
  154. package/out/zero/package.js.map +1 -1
  155. package/out/zero/src/build-schema.js.map +1 -1
  156. package/out/zero/src/zero-cache-dev.js.map +1 -1
  157. package/out/zero/src/zero-out.js.map +1 -1
  158. package/out/zero-cache/src/auth/auth.js.map +1 -1
  159. package/out/zero-cache/src/auth/jwt.js.map +1 -1
  160. package/out/zero-cache/src/auth/load-permissions.js.map +1 -1
  161. package/out/zero-cache/src/auth/read-authorizer.js.map +1 -1
  162. package/out/zero-cache/src/auth/write-authorizer.js.map +1 -1
  163. package/out/zero-cache/src/config/network.js.map +1 -1
  164. package/out/zero-cache/src/config/normalize.js.map +1 -1
  165. package/out/zero-cache/src/config/server-context.js.map +1 -1
  166. package/out/zero-cache/src/config/zero-config.js +0 -5
  167. package/out/zero-cache/src/config/zero-config.js.map +1 -1
  168. package/out/zero-cache/src/custom/fetch.js.map +1 -1
  169. package/out/zero-cache/src/custom-queries/transform-query.js.map +1 -1
  170. package/out/zero-cache/src/db/create.js.map +1 -1
  171. package/out/zero-cache/src/db/delete-lite-db.js.map +1 -1
  172. package/out/zero-cache/src/db/lite-tables.js.map +1 -1
  173. package/out/zero-cache/src/db/migration-lite.js +0 -19
  174. package/out/zero-cache/src/db/migration-lite.js.map +1 -1
  175. package/out/zero-cache/src/db/migration.js +0 -19
  176. package/out/zero-cache/src/db/migration.js.map +1 -1
  177. package/out/zero-cache/src/db/pg-copy-binary.js.map +1 -1
  178. package/out/zero-cache/src/db/pg-copy.js.map +1 -1
  179. package/out/zero-cache/src/db/pg-to-lite.js.map +1 -1
  180. package/out/zero-cache/src/db/pg-type-parser.js.map +1 -1
  181. package/out/zero-cache/src/db/run-transaction.js.map +1 -1
  182. package/out/zero-cache/src/db/specs.js.map +1 -1
  183. package/out/zero-cache/src/db/statements.js.map +1 -1
  184. package/out/zero-cache/src/db/transaction-pool.js.map +1 -1
  185. package/out/zero-cache/src/db/warmup.js.map +1 -1
  186. package/out/zero-cache/src/observability/events.js.map +1 -1
  187. package/out/zero-cache/src/observability/metrics.js.map +1 -1
  188. package/out/zero-cache/src/scripts/decommission.js.map +1 -1
  189. package/out/zero-cache/src/scripts/deploy-permissions.js.map +1 -1
  190. package/out/zero-cache/src/scripts/permissions.js.map +1 -1
  191. package/out/zero-cache/src/server/anonymous-otel-start.js +10 -11
  192. package/out/zero-cache/src/server/anonymous-otel-start.js.map +1 -1
  193. package/out/zero-cache/src/server/change-streamer.js.map +1 -1
  194. package/out/zero-cache/src/server/inspector-delegate.js.map +1 -1
  195. package/out/zero-cache/src/server/logging.js.map +1 -1
  196. package/out/zero-cache/src/server/main.js.map +1 -1
  197. package/out/zero-cache/src/server/mutator.js.map +1 -1
  198. package/out/zero-cache/src/server/otel-diag-logger.js.map +1 -1
  199. package/out/zero-cache/src/server/otel-log-sink.js.map +1 -1
  200. package/out/zero-cache/src/server/otel-start.js +1 -1
  201. package/out/zero-cache/src/server/otel-start.js.map +1 -1
  202. package/out/zero-cache/src/server/priority-op.js.map +1 -1
  203. package/out/zero-cache/src/server/reaper.js.map +1 -1
  204. package/out/zero-cache/src/server/replicator.js.map +1 -1
  205. package/out/zero-cache/src/server/runner/main.js.map +1 -1
  206. package/out/zero-cache/src/server/runner/run-worker.js.map +1 -1
  207. package/out/zero-cache/src/server/runner/runtime.js.map +1 -1
  208. package/out/zero-cache/src/server/runner/zero-dispatcher.js.map +1 -1
  209. package/out/zero-cache/src/server/shadow-syncer.js.map +1 -1
  210. package/out/zero-cache/src/server/syncer.js.map +1 -1
  211. package/out/zero-cache/src/server/worker-dispatcher.js.map +1 -1
  212. package/out/zero-cache/src/server/worker-urls.js.map +1 -1
  213. package/out/zero-cache/src/services/analyze.d.ts.map +1 -1
  214. package/out/zero-cache/src/services/analyze.js +2 -5
  215. package/out/zero-cache/src/services/analyze.js.map +1 -1
  216. package/out/zero-cache/src/services/change-source/common/backfill-manager.js.map +1 -1
  217. package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.js.map +1 -1
  218. package/out/zero-cache/src/services/change-source/common/replica-schema.js.map +1 -1
  219. package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
  220. package/out/zero-cache/src/services/change-source/pg/backfill-metadata.js.map +1 -1
  221. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js.map +1 -1
  222. package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
  223. package/out/zero-cache/src/services/change-source/pg/decommission.js.map +1 -1
  224. package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
  225. package/out/zero-cache/src/services/change-source/pg/logical-replication/binary-reader.js.map +1 -1
  226. package/out/zero-cache/src/services/change-source/pg/logical-replication/pgoutput-parser.js.map +1 -1
  227. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js.map +1 -1
  228. package/out/zero-cache/src/services/change-source/pg/lsn.js.map +1 -1
  229. package/out/zero-cache/src/services/change-source/pg/replication-slots.js.map +1 -1
  230. package/out/zero-cache/src/services/change-source/pg/schema/ddl.js.map +1 -1
  231. package/out/zero-cache/src/services/change-source/pg/schema/init.js.map +1 -1
  232. package/out/zero-cache/src/services/change-source/pg/schema/published.js.map +1 -1
  233. package/out/zero-cache/src/services/change-source/pg/schema/shard.js.map +1 -1
  234. package/out/zero-cache/src/services/change-source/pg/schema/validation.js.map +1 -1
  235. package/out/zero-cache/src/services/change-source/protocol/current/control.js.map +1 -1
  236. package/out/zero-cache/src/services/change-source/protocol/current/data.js +0 -2
  237. package/out/zero-cache/src/services/change-source/protocol/current/data.js.map +1 -1
  238. package/out/zero-cache/src/services/change-source/protocol/current/downstream.js.map +1 -1
  239. package/out/zero-cache/src/services/change-source/protocol/current/json.js.map +1 -1
  240. package/out/zero-cache/src/services/change-source/protocol/current/status.js.map +1 -1
  241. package/out/zero-cache/src/services/change-source/protocol/current/upstream.js.map +1 -1
  242. package/out/zero-cache/src/services/change-streamer/backup-monitor.js.map +1 -1
  243. package/out/zero-cache/src/services/change-streamer/broadcast.js.map +1 -1
  244. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
  245. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
  246. package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
  247. package/out/zero-cache/src/services/change-streamer/forwarder.js.map +1 -1
  248. package/out/zero-cache/src/services/change-streamer/replica-monitor.js.map +1 -1
  249. package/out/zero-cache/src/services/change-streamer/schema/init.js +25 -21
  250. package/out/zero-cache/src/services/change-streamer/schema/init.js.map +1 -1
  251. package/out/zero-cache/src/services/change-streamer/schema/tables.js.map +1 -1
  252. package/out/zero-cache/src/services/change-streamer/snapshot.js +0 -15
  253. package/out/zero-cache/src/services/change-streamer/snapshot.js.map +1 -1
  254. package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
  255. package/out/zero-cache/src/services/change-streamer/subscriber.js.map +1 -1
  256. package/out/zero-cache/src/services/heapz.js.map +1 -1
  257. package/out/zero-cache/src/services/http-service.js.map +1 -1
  258. package/out/zero-cache/src/services/life-cycle.js.map +1 -1
  259. package/out/zero-cache/src/services/limiter/sliding-window-limiter.js.map +1 -1
  260. package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
  261. package/out/zero-cache/src/services/mutagen/error.js.map +1 -1
  262. package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
  263. package/out/zero-cache/src/services/mutagen/pusher.js.map +1 -1
  264. package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
  265. package/out/zero-cache/src/services/replicator/incremental-sync.js.map +1 -1
  266. package/out/zero-cache/src/services/replicator/notifier.js.map +1 -1
  267. package/out/zero-cache/src/services/replicator/replication-status.js.map +1 -1
  268. package/out/zero-cache/src/services/replicator/replicator.js.map +1 -1
  269. package/out/zero-cache/src/services/replicator/reporter/recorder.js.map +1 -1
  270. package/out/zero-cache/src/services/replicator/reporter/report-schema.js.map +1 -1
  271. package/out/zero-cache/src/services/replicator/schema/change-log.js.map +1 -1
  272. package/out/zero-cache/src/services/replicator/schema/column-metadata.js.map +1 -1
  273. package/out/zero-cache/src/services/replicator/schema/replication-state.js.map +1 -1
  274. package/out/zero-cache/src/services/replicator/schema/table-metadata.js.map +1 -1
  275. package/out/zero-cache/src/services/replicator/write-worker-client.js.map +1 -1
  276. package/out/zero-cache/src/services/replicator/write-worker.js.map +1 -1
  277. package/out/zero-cache/src/services/run-ast.d.ts.map +1 -1
  278. package/out/zero-cache/src/services/run-ast.js +0 -1
  279. package/out/zero-cache/src/services/run-ast.js.map +1 -1
  280. package/out/zero-cache/src/services/runner.js.map +1 -1
  281. package/out/zero-cache/src/services/running-state.js.map +1 -1
  282. package/out/zero-cache/src/services/shadow-sync/shadow-sync-service.js.map +1 -1
  283. package/out/zero-cache/src/services/statz.js.map +1 -1
  284. package/out/zero-cache/src/services/view-syncer/active-users-gauge.js.map +1 -1
  285. package/out/zero-cache/src/services/view-syncer/client-handler.js.map +1 -1
  286. package/out/zero-cache/src/services/view-syncer/client-schema.js.map +1 -1
  287. package/out/zero-cache/src/services/view-syncer/connection-context-manager.js.map +1 -1
  288. package/out/zero-cache/src/services/view-syncer/cvr-purger.d.ts.map +1 -1
  289. package/out/zero-cache/src/services/view-syncer/cvr-purger.js +1 -2
  290. package/out/zero-cache/src/services/view-syncer/cvr-purger.js.map +1 -1
  291. package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
  292. package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
  293. package/out/zero-cache/src/services/view-syncer/drain-coordinator.js.map +1 -1
  294. package/out/zero-cache/src/services/view-syncer/inspect-handler.d.ts +14 -0
  295. package/out/zero-cache/src/services/view-syncer/inspect-handler.d.ts.map +1 -1
  296. package/out/zero-cache/src/services/view-syncer/inspect-handler.js +25 -2
  297. package/out/zero-cache/src/services/view-syncer/inspect-handler.js.map +1 -1
  298. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
  299. package/out/zero-cache/src/services/view-syncer/row-record-cache.js.map +1 -1
  300. package/out/zero-cache/src/services/view-syncer/row-set-signature.js.map +1 -1
  301. package/out/zero-cache/src/services/view-syncer/schema/cvr.js.map +1 -1
  302. package/out/zero-cache/src/services/view-syncer/schema/init.js +113 -97
  303. package/out/zero-cache/src/services/view-syncer/schema/init.js.map +1 -1
  304. package/out/zero-cache/src/services/view-syncer/schema/types.js +1 -103
  305. package/out/zero-cache/src/services/view-syncer/schema/types.js.map +1 -1
  306. package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
  307. package/out/zero-cache/src/services/view-syncer/tracer.js.map +1 -1
  308. package/out/zero-cache/src/services/view-syncer/ttl-clock.js.map +1 -1
  309. package/out/zero-cache/src/services/view-syncer/view-syncer.js +1 -4
  310. package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
  311. package/out/zero-cache/src/types/configuration-error.js.map +1 -1
  312. package/out/zero-cache/src/types/error-with-level.js.map +1 -1
  313. package/out/zero-cache/src/types/http.js.map +1 -1
  314. package/out/zero-cache/src/types/lexi-version.js.map +1 -1
  315. package/out/zero-cache/src/types/lite.js.map +1 -1
  316. package/out/zero-cache/src/types/names.js.map +1 -1
  317. package/out/zero-cache/src/types/pg-data-type.js.map +1 -1
  318. package/out/zero-cache/src/types/pg.js.map +1 -1
  319. package/out/zero-cache/src/types/processes.js.map +1 -1
  320. package/out/zero-cache/src/types/profiler.js.map +1 -1
  321. package/out/zero-cache/src/types/row-key.js.map +1 -1
  322. package/out/zero-cache/src/types/shards.js.map +1 -1
  323. package/out/zero-cache/src/types/sql.js.map +1 -1
  324. package/out/zero-cache/src/types/state-version.js.map +1 -1
  325. package/out/zero-cache/src/types/streams.js.map +1 -1
  326. package/out/zero-cache/src/types/strings.js.map +1 -1
  327. package/out/zero-cache/src/types/subscription.js.map +1 -1
  328. package/out/zero-cache/src/types/timeout.js.map +1 -1
  329. package/out/zero-cache/src/types/url-params.js.map +1 -1
  330. package/out/zero-cache/src/types/websocket-handoff.js.map +1 -1
  331. package/out/zero-cache/src/types/ws.js.map +1 -1
  332. package/out/zero-cache/src/workers/connect-params.js.map +1 -1
  333. package/out/zero-cache/src/workers/connection.js.map +1 -1
  334. package/out/zero-cache/src/workers/mutator.js.map +1 -1
  335. package/out/zero-cache/src/workers/replicator.js.map +1 -1
  336. package/out/zero-cache/src/workers/syncer-ws-message-handler.js.map +1 -1
  337. package/out/zero-cache/src/workers/syncer.js.map +1 -1
  338. package/out/zero-client/src/client/active-clients-manager.js.map +1 -1
  339. package/out/zero-client/src/client/connection-manager.js +1 -2
  340. package/out/zero-client/src/client/connection-manager.js.map +1 -1
  341. package/out/zero-client/src/client/connection.js.map +1 -1
  342. package/out/zero-client/src/client/context.js.map +1 -1
  343. package/out/zero-client/src/client/crud-impl.js.map +1 -1
  344. package/out/zero-client/src/client/crud.js.map +1 -1
  345. package/out/zero-client/src/client/custom.js +1 -2
  346. package/out/zero-client/src/client/custom.js.map +1 -1
  347. package/out/zero-client/src/client/delete-clients-manager.js.map +1 -1
  348. package/out/zero-client/src/client/enable-analytics.js.map +1 -1
  349. package/out/zero-client/src/client/error.js.map +1 -1
  350. package/out/zero-client/src/client/http-string.js.map +1 -1
  351. package/out/zero-client/src/client/inspector/client-group.js.map +1 -1
  352. package/out/zero-client/src/client/inspector/client.js.map +1 -1
  353. package/out/zero-client/src/client/inspector/html-dialog-prompt.js.map +1 -1
  354. package/out/zero-client/src/client/inspector/inspector.js.map +1 -1
  355. package/out/zero-client/src/client/inspector/lazy-inspector.js.map +1 -1
  356. package/out/zero-client/src/client/inspector/query.js.map +1 -1
  357. package/out/zero-client/src/client/ivm-branch.js.map +1 -1
  358. package/out/zero-client/src/client/keys.js.map +1 -1
  359. package/out/zero-client/src/client/log-options.js.map +1 -1
  360. package/out/zero-client/src/client/make-mutate-property.js.map +1 -1
  361. package/out/zero-client/src/client/make-replicache-mutators.js.map +1 -1
  362. package/out/zero-client/src/client/metrics.js.map +1 -1
  363. package/out/zero-client/src/client/mutation-tracker.js.map +1 -1
  364. package/out/zero-client/src/client/mutator-proxy.js.map +1 -1
  365. package/out/zero-client/src/client/options.js.map +1 -1
  366. package/out/zero-client/src/client/query-manager.js.map +1 -1
  367. package/out/zero-client/src/client/reload-error-handler.js.map +1 -1
  368. package/out/zero-client/src/client/server-option.js.map +1 -1
  369. package/out/zero-client/src/client/version.js +1 -1
  370. package/out/zero-client/src/client/zero-poke-handler.js.map +1 -1
  371. package/out/zero-client/src/client/zero-rep.js.map +1 -1
  372. package/out/zero-client/src/client/zero.d.ts.map +1 -1
  373. package/out/zero-client/src/client/zero.js +32 -58
  374. package/out/zero-client/src/client/zero.js.map +1 -1
  375. package/out/zero-client/src/util/nanoid.js.map +1 -1
  376. package/out/zero-client/src/util/socket.d.ts +3 -0
  377. package/out/zero-client/src/util/socket.d.ts.map +1 -0
  378. package/out/zero-client/src/util/socket.js +8 -0
  379. package/out/zero-client/src/util/socket.js.map +1 -0
  380. package/out/zero-protocol/src/analyze-query-result.js +0 -3
  381. package/out/zero-protocol/src/analyze-query-result.js.map +1 -1
  382. package/out/zero-protocol/src/application-error.js.map +1 -1
  383. package/out/zero-protocol/src/ast.js.map +1 -1
  384. package/out/zero-protocol/src/change-desired-queries.js +0 -1
  385. package/out/zero-protocol/src/change-desired-queries.js.map +1 -1
  386. package/out/zero-protocol/src/client-schema.js.map +1 -1
  387. package/out/zero-protocol/src/close-connection.js.map +1 -1
  388. package/out/zero-protocol/src/connect.js +0 -7
  389. package/out/zero-protocol/src/connect.js.map +1 -1
  390. package/out/zero-protocol/src/custom-queries.js.map +1 -1
  391. package/out/zero-protocol/src/data.js.map +1 -1
  392. package/out/zero-protocol/src/delete-clients.js.map +1 -1
  393. package/out/zero-protocol/src/down.js.map +1 -1
  394. package/out/zero-protocol/src/error.js +0 -7
  395. package/out/zero-protocol/src/error.js.map +1 -1
  396. package/out/zero-protocol/src/inspect-down.js.map +1 -1
  397. package/out/zero-protocol/src/inspect-up.js +0 -1
  398. package/out/zero-protocol/src/inspect-up.js.map +1 -1
  399. package/out/zero-protocol/src/mutate-server.js.map +1 -1
  400. package/out/zero-protocol/src/mutation-id.js.map +1 -1
  401. package/out/zero-protocol/src/mutation.js.map +1 -1
  402. package/out/zero-protocol/src/mutations-patch.js.map +1 -1
  403. package/out/zero-protocol/src/ping.js.map +1 -1
  404. package/out/zero-protocol/src/poke.js +0 -4
  405. package/out/zero-protocol/src/poke.js.map +1 -1
  406. package/out/zero-protocol/src/pong.js.map +1 -1
  407. package/out/zero-protocol/src/primary-key.js.map +1 -1
  408. package/out/zero-protocol/src/protocol-version.js.map +1 -1
  409. package/out/zero-protocol/src/pull.js.map +1 -1
  410. package/out/zero-protocol/src/push.js +0 -16
  411. package/out/zero-protocol/src/push.js.map +1 -1
  412. package/out/zero-protocol/src/queries-patch.js.map +1 -1
  413. package/out/zero-protocol/src/query-hash.js.map +1 -1
  414. package/out/zero-protocol/src/query-server.js.map +1 -1
  415. package/out/zero-protocol/src/row-patch.js.map +1 -1
  416. package/out/zero-protocol/src/up.js.map +1 -1
  417. package/out/zero-protocol/src/update-auth.js.map +1 -1
  418. package/out/zero-protocol/src/version.js.map +1 -1
  419. package/out/zero-react/src/use-connection-state.js +2 -4
  420. package/out/zero-react/src/use-connection-state.js.map +1 -1
  421. package/out/zero-react/src/use-query.js +4 -6
  422. package/out/zero-react/src/use-query.js.map +1 -1
  423. package/out/zero-react/src/use-zero-online.js +2 -4
  424. package/out/zero-react/src/use-zero-online.js.map +1 -1
  425. package/out/zero-react/src/zero-provider.js +12 -15
  426. package/out/zero-react/src/zero-provider.js.map +1 -1
  427. package/out/zero-schema/src/builder/relationship-builder.js.map +1 -1
  428. package/out/zero-schema/src/builder/schema-builder.js.map +1 -1
  429. package/out/zero-schema/src/builder/table-builder.js.map +1 -1
  430. package/out/zero-schema/src/compiled-permissions.js.map +1 -1
  431. package/out/zero-schema/src/name-mapper.js.map +1 -1
  432. package/out/zero-schema/src/permissions.js.map +1 -1
  433. package/out/zero-schema/src/schema-config.js.map +1 -1
  434. package/out/zero-server/src/adapters/drizzle.js.map +1 -1
  435. package/out/zero-server/src/adapters/kysely.js.map +1 -1
  436. package/out/zero-server/src/adapters/pg.js +1 -1
  437. package/out/zero-server/src/adapters/pg.js.map +1 -1
  438. package/out/zero-server/src/adapters/postgresjs.js.map +1 -1
  439. package/out/zero-server/src/adapters/prisma.js.map +1 -1
  440. package/out/zero-server/src/custom.js +1 -2
  441. package/out/zero-server/src/custom.js.map +1 -1
  442. package/out/zero-server/src/logging.js.map +1 -1
  443. package/out/zero-server/src/pg-query-executor.js.map +1 -1
  444. package/out/zero-server/src/process-mutations.js.map +1 -1
  445. package/out/zero-server/src/push-processor.js.map +1 -1
  446. package/out/zero-server/src/queries/process-queries.js.map +1 -1
  447. package/out/zero-server/src/schema.js.map +1 -1
  448. package/out/zero-server/src/zql-database.js.map +1 -1
  449. package/out/zero-solid/src/solid-view.js +1 -1
  450. package/out/zero-solid/src/solid-view.js.map +1 -1
  451. package/out/zero-solid/src/use-connection-state.js +1 -1
  452. package/out/zero-solid/src/use-connection-state.js.map +1 -1
  453. package/out/zero-solid/src/use-query.js +2 -2
  454. package/out/zero-solid/src/use-query.js.map +1 -1
  455. package/out/zero-solid/src/use-zero-online.js +1 -1
  456. package/out/zero-solid/src/use-zero-online.js.map +1 -1
  457. package/out/zero-solid/src/use-zero.js +1 -1
  458. package/out/zero-solid/src/use-zero.js.map +1 -1
  459. package/out/zero-types/src/format.js.map +1 -1
  460. package/out/zero-types/src/name-mapper.js.map +1 -1
  461. package/out/zql/src/builder/builder.js.map +1 -1
  462. package/out/zql/src/builder/debug-delegate.d.ts +0 -5
  463. package/out/zql/src/builder/debug-delegate.d.ts.map +1 -1
  464. package/out/zql/src/builder/debug-delegate.js +1 -10
  465. package/out/zql/src/builder/debug-delegate.js.map +1 -1
  466. package/out/zql/src/builder/filter.js.map +1 -1
  467. package/out/zql/src/builder/like.js.map +1 -1
  468. package/out/zql/src/error.js.map +1 -1
  469. package/out/zql/src/ivm/array-view.js.map +1 -1
  470. package/out/zql/src/ivm/cap.js.map +1 -1
  471. package/out/zql/src/ivm/change.js.map +1 -1
  472. package/out/zql/src/ivm/constraint.js +1 -1
  473. package/out/zql/src/ivm/constraint.js.map +1 -1
  474. package/out/zql/src/ivm/data.js.map +1 -1
  475. package/out/zql/src/ivm/exists.js.map +1 -1
  476. package/out/zql/src/ivm/fan-in.js.map +1 -1
  477. package/out/zql/src/ivm/fan-out.js.map +1 -1
  478. package/out/zql/src/ivm/filter-operators.js.map +1 -1
  479. package/out/zql/src/ivm/filter-push.js.map +1 -1
  480. package/out/zql/src/ivm/filter.js.map +1 -1
  481. package/out/zql/src/ivm/flipped-join.d.ts +8 -4
  482. package/out/zql/src/ivm/flipped-join.d.ts.map +1 -1
  483. package/out/zql/src/ivm/flipped-join.js +63 -59
  484. package/out/zql/src/ivm/flipped-join.js.map +1 -1
  485. package/out/zql/src/ivm/join-utils.js.map +1 -1
  486. package/out/zql/src/ivm/join.js.map +1 -1
  487. package/out/zql/src/ivm/maybe-split-and-push-edit-change.js.map +1 -1
  488. package/out/zql/src/ivm/memory-source.js.map +1 -1
  489. package/out/zql/src/ivm/memory-storage.js.map +1 -1
  490. package/out/zql/src/ivm/operator.d.ts +1 -1
  491. package/out/zql/src/ivm/operator.js.map +1 -1
  492. package/out/zql/src/ivm/push-accumulated.js.map +1 -1
  493. package/out/zql/src/ivm/schema.d.ts +8 -0
  494. package/out/zql/src/ivm/schema.d.ts.map +1 -1
  495. package/out/zql/src/ivm/skip-yields.js.map +1 -1
  496. package/out/zql/src/ivm/skip.js.map +1 -1
  497. package/out/zql/src/ivm/source.js.map +1 -1
  498. package/out/zql/src/ivm/stream.js.map +1 -1
  499. package/out/zql/src/ivm/take.js.map +1 -1
  500. package/out/zql/src/ivm/union-fan-in.js.map +1 -1
  501. package/out/zql/src/ivm/union-fan-out.js.map +1 -1
  502. package/out/zql/src/ivm/view-apply-change.js.map +1 -1
  503. package/out/zql/src/mutate/crud.js.map +1 -1
  504. package/out/zql/src/mutate/custom.js.map +1 -1
  505. package/out/zql/src/mutate/mutator-registry.js.map +1 -1
  506. package/out/zql/src/mutate/mutator.js.map +1 -1
  507. package/out/zql/src/planner/planner-builder.js.map +1 -1
  508. package/out/zql/src/planner/planner-connection.js.map +1 -1
  509. package/out/zql/src/planner/planner-constraint.js.map +1 -1
  510. package/out/zql/src/planner/planner-debug.js.map +1 -1
  511. package/out/zql/src/planner/planner-fan-in.js.map +1 -1
  512. package/out/zql/src/planner/planner-fan-out.js.map +1 -1
  513. package/out/zql/src/planner/planner-graph.js.map +1 -1
  514. package/out/zql/src/planner/planner-join.d.ts.map +1 -1
  515. package/out/zql/src/planner/planner-join.js +1 -2
  516. package/out/zql/src/planner/planner-join.js.map +1 -1
  517. package/out/zql/src/planner/planner-node.js.map +1 -1
  518. package/out/zql/src/planner/planner-source.js.map +1 -1
  519. package/out/zql/src/planner/planner-terminus.js.map +1 -1
  520. package/out/zql/src/query/complete-ordering.js.map +1 -1
  521. package/out/zql/src/query/create-builder.js.map +1 -1
  522. package/out/zql/src/query/error.js.map +1 -1
  523. package/out/zql/src/query/escape-like.js.map +1 -1
  524. package/out/zql/src/query/expression.js.map +1 -1
  525. package/out/zql/src/query/measure-push-operator.js.map +1 -1
  526. package/out/zql/src/query/metrics-delegate.js.map +1 -1
  527. package/out/zql/src/query/named.js.map +1 -1
  528. package/out/zql/src/query/query-delegate-base.js.map +1 -1
  529. package/out/zql/src/query/query-impl.js +1 -1
  530. package/out/zql/src/query/query-impl.js.map +1 -1
  531. package/out/zql/src/query/query-internals.js.map +1 -1
  532. package/out/zql/src/query/query-registry.js.map +1 -1
  533. package/out/zql/src/query/runnable-query-impl.js.map +1 -1
  534. package/out/zql/src/query/static-query.js.map +1 -1
  535. package/out/zql/src/query/ttl.js.map +1 -1
  536. package/out/zql/src/query/validate-input.js.map +1 -1
  537. package/out/zqlite/src/database-storage.js.map +1 -1
  538. package/out/zqlite/src/db.js.map +1 -1
  539. package/out/zqlite/src/explain-queries.js.map +1 -1
  540. package/out/zqlite/src/internal/sql-inline.js.map +1 -1
  541. package/out/zqlite/src/internal/sql.js.map +1 -1
  542. package/out/zqlite/src/internal/statement-cache.js.map +1 -1
  543. package/out/zqlite/src/query-builder.js.map +1 -1
  544. package/out/zqlite/src/query-delegate.js.map +1 -1
  545. package/out/zqlite/src/resolve-scalar-subqueries.js.map +1 -1
  546. package/out/zqlite/src/sqlite-cost-model.js.map +1 -1
  547. package/out/zqlite/src/sqlite-stat-fanout.js.map +1 -1
  548. package/out/zqlite/src/table-source.d.ts.map +1 -1
  549. package/out/zqlite/src/table-source.js +6 -6
  550. package/out/zqlite/src/table-source.js.map +1 -1
  551. package/package.json +23 -23
  552. package/out/_virtual/__vite-optional-peer-dep_pg-native_pg.js +0 -13
  553. package/out/_virtual/__vite-optional-peer-dep_pg-native_pg.js.map +0 -1
  554. package/out/node_modules/.pnpm/@opentelemetry_semantic-conventions@1.41.1/node_modules/@opentelemetry/semantic-conventions/build/esm/stable_attributes.js +0 -12
  555. package/out/node_modules/.pnpm/@opentelemetry_semantic-conventions@1.41.1/node_modules/@opentelemetry/semantic-conventions/build/esm/stable_attributes.js.map +0 -1
  556. package/out/node_modules/.pnpm/pg-cloudflare@1.3.0/node_modules/pg-cloudflare/dist/empty.js +0 -11
  557. package/out/node_modules/.pnpm/pg-cloudflare@1.3.0/node_modules/pg-cloudflare/dist/empty.js.map +0 -1
  558. package/out/node_modules/.pnpm/pg-connection-string@2.12.0/node_modules/pg-connection-string/index.js +0 -130
  559. package/out/node_modules/.pnpm/pg-connection-string@2.12.0/node_modules/pg-connection-string/index.js.map +0 -1
  560. package/out/node_modules/.pnpm/pg-int8@1.0.1/node_modules/pg-int8/index.js +0 -62
  561. package/out/node_modules/.pnpm/pg-int8@1.0.1/node_modules/pg-int8/index.js.map +0 -1
  562. package/out/node_modules/.pnpm/pg-pool@3.13.0_pg@8.20.0/node_modules/pg-pool/index.js +0 -353
  563. package/out/node_modules/.pnpm/pg-pool@3.13.0_pg@8.20.0/node_modules/pg-pool/index.js.map +0 -1
  564. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/buffer-reader.js +0 -60
  565. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/buffer-reader.js.map +0 -1
  566. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/buffer-writer.js +0 -81
  567. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/buffer-writer.js.map +0 -1
  568. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/index.js +0 -35
  569. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/index.js.map +0 -1
  570. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/messages.js +0 -167
  571. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/messages.js.map +0 -1
  572. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/parser.js +0 -288
  573. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/parser.js.map +0 -1
  574. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/serializer.js +0 -177
  575. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/serializer.js.map +0 -1
  576. package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/index.js +0 -46
  577. package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/index.js.map +0 -1
  578. package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/lib/arrayParser.js +0 -16
  579. package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/lib/arrayParser.js.map +0 -1
  580. package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/lib/binaryParsers.js +0 -165
  581. package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/lib/binaryParsers.js.map +0 -1
  582. package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/lib/builtins.js +0 -81
  583. package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/lib/builtins.js.map +0 -1
  584. package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/lib/textParsers.js +0 -167
  585. package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/lib/textParsers.js.map +0 -1
  586. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/esm/index.js +0 -19
  587. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/esm/index.js.map +0 -1
  588. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/client.js +0 -508
  589. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/client.js.map +0 -1
  590. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/connection-parameters.js +0 -104
  591. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/connection-parameters.js.map +0 -1
  592. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/connection.js +0 -160
  593. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/connection.js.map +0 -1
  594. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/cert-signatures.js +0 -97
  595. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/cert-signatures.js.map +0 -1
  596. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/sasl.js +0 -131
  597. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/sasl.js.map +0 -1
  598. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/utils-legacy.js +0 -39
  599. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/utils-legacy.js.map +0 -1
  600. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/utils-webcrypto.js +0 -89
  601. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/utils-webcrypto.js.map +0 -1
  602. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/utils.js +0 -13
  603. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/utils.js.map +0 -1
  604. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/defaults.js +0 -46
  605. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/defaults.js.map +0 -1
  606. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/index.js +0 -71
  607. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/index.js.map +0 -1
  608. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/native/client.js +0 -226
  609. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/native/client.js.map +0 -1
  610. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/native/index.js +0 -11
  611. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/native/index.js.map +0 -1
  612. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/native/query.js +0 -117
  613. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/native/query.js.map +0 -1
  614. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/query.js +0 -151
  615. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/query.js.map +0 -1
  616. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/result.js +0 -76
  617. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/result.js.map +0 -1
  618. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/stream.js +0 -73
  619. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/stream.js.map +0 -1
  620. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/type-overrides.js +0 -35
  621. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/type-overrides.js.map +0 -1
  622. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/utils.js +0 -118
  623. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/utils.js.map +0 -1
  624. package/out/node_modules/.pnpm/pgpass@1.0.5/node_modules/pgpass/lib/helper.js +0 -147
  625. package/out/node_modules/.pnpm/pgpass@1.0.5/node_modules/pgpass/lib/helper.js.map +0 -1
  626. package/out/node_modules/.pnpm/pgpass@1.0.5/node_modules/pgpass/lib/index.js +0 -21
  627. package/out/node_modules/.pnpm/pgpass@1.0.5/node_modules/pgpass/lib/index.js.map +0 -1
  628. package/out/node_modules/.pnpm/postgres-array@2.0.0/node_modules/postgres-array/index.js +0 -84
  629. package/out/node_modules/.pnpm/postgres-array@2.0.0/node_modules/postgres-array/index.js.map +0 -1
  630. package/out/node_modules/.pnpm/postgres-bytea@1.0.1/node_modules/postgres-bytea/index.js +0 -28
  631. package/out/node_modules/.pnpm/postgres-bytea@1.0.1/node_modules/postgres-bytea/index.js.map +0 -1
  632. package/out/node_modules/.pnpm/postgres-date@1.0.7/node_modules/postgres-date/index.js +0 -65
  633. package/out/node_modules/.pnpm/postgres-date@1.0.7/node_modules/postgres-date/index.js.map +0 -1
  634. package/out/node_modules/.pnpm/postgres-interval@1.2.0/node_modules/postgres-interval/index.js +0 -107
  635. package/out/node_modules/.pnpm/postgres-interval@1.2.0/node_modules/postgres-interval/index.js.map +0 -1
  636. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react-jsx-runtime.development.js +0 -696
  637. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react-jsx-runtime.development.js.map +0 -1
  638. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react-jsx-runtime.production.min.js +0 -44
  639. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react-jsx-runtime.production.min.js.map +0 -1
  640. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.development.js +0 -1585
  641. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.development.js.map +0 -1
  642. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.production.min.js +0 -329
  643. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.production.min.js.map +0 -1
  644. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/index.js +0 -13
  645. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/index.js.map +0 -1
  646. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/jsx-runtime.js +0 -13
  647. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/jsx-runtime.js.map +0 -1
  648. package/out/node_modules/.pnpm/solid-js@1.9.13/node_modules/solid-js/dist/server.js +0 -131
  649. package/out/node_modules/.pnpm/solid-js@1.9.13/node_modules/solid-js/dist/server.js.map +0 -1
  650. package/out/node_modules/.pnpm/solid-js@1.9.13/node_modules/solid-js/store/dist/server.js +0 -96
  651. package/out/node_modules/.pnpm/solid-js@1.9.13/node_modules/solid-js/store/dist/server.js.map +0 -1
  652. package/out/node_modules/.pnpm/split2@4.2.0/node_modules/split2/index.js +0 -95
  653. package/out/node_modules/.pnpm/split2@4.2.0/node_modules/split2/index.js.map +0 -1
  654. package/out/node_modules/.pnpm/xtend@4.0.2/node_modules/xtend/mutable.js +0 -18
  655. package/out/node_modules/.pnpm/xtend@4.0.2/node_modules/xtend/mutable.js.map +0 -1
  656. package/out/shared/src/ring-buffer.d.ts +0 -32
  657. package/out/shared/src/ring-buffer.d.ts.map +0 -1
  658. package/out/shared/src/ring-buffer.js +0 -109
  659. package/out/shared/src/ring-buffer.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"migration-lite.js","names":[],"sources":["../../../../../zero-cache/src/db/migration-lite.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport {randInt} from '../../../shared/src/rand.ts';\nimport * as v from '../../../shared/src/valita.ts';\nimport type {Database as Db} from '../../../zqlite/src/db.ts';\nimport {Database} from '../../../zqlite/src/db.ts';\n\ntype Operations = (log: LogContext, tx: Db) => Promise<void> | void;\n\n/**\n * Encapsulates the logic for setting up or upgrading to a new schema. After the\n * Migration code successfully completes, {@link runSchemaMigrations}\n * will update the schema version and commit the transaction.\n */\nexport type Migration = {\n /**\n * Perform database operations that create or alter table structure. This is\n * called at most once during lifetime of the application. If a `migrateData()`\n * operation is defined, that will be performed after `migrateSchema()` succeeds.\n */\n migrateSchema?: Operations;\n\n /**\n * Perform database operations to migrate data to the new schema. This is\n * called after `migrateSchema()` (if defined), and may be called again\n * to re-migrate data after the server was rolled back to an earlier version,\n * and rolled forward again.\n *\n * Consequently, the logic in `migrateData()` must be idempotent.\n */\n migrateData?: Operations;\n\n /**\n * Sets the `minSafeVersion` to the specified value, prohibiting running\n * any earlier code versions.\n */\n minSafeVersion?: number;\n};\n\n/**\n * Mapping of incremental migrations to move from the previous old code\n * version to next one. Versions must be non-zero.\n *\n * The schema resulting from performing incremental migrations should be\n * equivalent to that of the `setupMigration` on a blank database.\n *\n * The highest destinationVersion of this map denotes the current\n * \"code version\", and is also used as the destination version when\n * running the initial setup migration on a blank database.\n */\nexport type IncrementalMigrationMap = {\n [destinationVersion: number]: Migration;\n};\n\n/**\n * Ensures that the schema is compatible with the current code, updating and\n * migrating the schema if necessary.\n */\nexport async function runSchemaMigrations(\n log: LogContext,\n debugName: string,\n dbPath: string,\n setupMigration: Migration,\n incrementalMigrationMap: IncrementalMigrationMap,\n): Promise<void> {\n const start = Date.now();\n log = log.withContext(\n 'initSchema',\n randInt(0, Number.MAX_SAFE_INTEGER).toString(36),\n );\n const db = new Database(log, dbPath);\n\n try {\n const versionMigrations = sorted(incrementalMigrationMap);\n assert(\n versionMigrations.length,\n `Must specify a at least one version migration`,\n );\n assert(\n versionMigrations[0][0] > 0,\n `Versions must be non-zero positive numbers`,\n );\n // oxlint-disable-next-line typescript/no-non-null-assertion\n const codeVersion = versionMigrations.at(-1)![0];\n log.info?.(\n `Checking schema for compatibility with ${debugName} at schema v${codeVersion}`,\n );\n\n let versions = await runTransaction(log, db, tx => {\n const versions = getVersionHistory(tx);\n if (codeVersion < versions.minSafeVersion) {\n throw new Error(\n `Cannot run ${debugName} at schema v${codeVersion} because rollback limit is v${versions.minSafeVersion}`,\n );\n }\n\n if (versions.dataVersion > codeVersion) {\n log.info?.(\n `Data is at v${versions.dataVersion}. Resetting to v${codeVersion}`,\n );\n return updateVersionHistory(log, tx, versions, codeVersion);\n }\n return versions;\n });\n\n if (versions.dataVersion < codeVersion) {\n db.unsafeMode(true); // Enables journal_mode = OFF\n db.pragma('locking_mode = EXCLUSIVE');\n db.pragma('foreign_keys = OFF');\n db.pragma('journal_mode = OFF');\n db.pragma('synchronous = OFF');\n // Unfortunately, AUTO_VACUUM is not compatible with BEGIN CONCURRENT,\n // so it is not an option for the replica file.\n // https://sqlite.org/forum/forumpost/25f183416a\n // db.pragma('auto_vacuum = INCREMENTAL');\n\n const migrations =\n versions.dataVersion === 0\n ? // For the empty database v0, only run the setup migration.\n ([[codeVersion, setupMigration]] as const)\n : versionMigrations;\n\n for (const [dest, migration] of migrations) {\n if (versions.dataVersion < dest) {\n log.info?.(\n `Migrating schema from v${versions.dataVersion} to v${dest}`,\n );\n void log.flush(); // Flush logs before each migration to help debug crash-y migrations.\n\n versions = await runTransaction(log, db, async tx => {\n // Fetch meta from within the transaction to make the migration atomic.\n let versions = getVersionHistory(tx);\n if (versions.dataVersion < dest) {\n versions = await runMigration(log, tx, versions, dest, migration);\n assert(\n versions.dataVersion === dest,\n () =>\n `Migration did not reach target version: expected ${dest}, got ${versions.dataVersion}`,\n );\n }\n return versions;\n });\n }\n }\n\n db.exec('ANALYZE main');\n log.info?.('ANALYZE completed');\n } else {\n // Run optimize whenever opening an sqlite db file as recommended in\n // https://www.sqlite.org/pragma.html#pragma_optimize\n // It is important to run the same initialization steps as is done\n // in the view-syncer (i.e. when preparing database for serving\n // replication) so that any corruption detected in the view-syncer is\n // similarly detected in the change-streamer, facilitating an eventual\n // recovery by resyncing the replica anew.\n db.pragma('optimize = 0x10002');\n\n // TODO: Investigate running `integrity_check` or `quick_check` as well,\n // provided that they are not inordinately expensive on large databases.\n }\n\n db.pragma('synchronous = NORMAL');\n db.unsafeMode(false);\n\n assert(\n versions.dataVersion === codeVersion,\n () =>\n `Final dataVersion (${versions.dataVersion}) does not match codeVersion (${codeVersion})`,\n );\n log.info?.(\n `Running ${debugName} at schema v${codeVersion} (${\n Date.now() - start\n } ms)`,\n );\n } catch (e) {\n log.error?.('Error in ensureSchemaMigrated', e);\n throw e;\n } finally {\n db.close();\n void log.flush(); // Flush the logs but do not block server progress on it.\n }\n}\n\nfunction sorted(\n incrementalMigrationMap: IncrementalMigrationMap,\n): [number, Migration][] {\n const versionMigrations: [number, Migration][] = [];\n for (const [v, m] of Object.entries(incrementalMigrationMap)) {\n versionMigrations.push([Number(v), m]);\n }\n return versionMigrations.sort(([a], [b]) => a - b);\n}\n\n// Exposed for tests.\nexport const versionHistory = v.object({\n /**\n * The `schemaVersion` is highest code version that has ever been run\n * on the database, and is used to delineate the structure of the tables\n * in the database. A schemaVersion only moves forward; rolling back to\n * an earlier (safe) code version does not revert schema changes that\n * have already been applied.\n */\n schemaVersion: v.number(),\n\n /**\n * The data version is the code version of the latest server that ran.\n * Note that this may be less than the schemaVersion in the case that\n * a server is rolled back to an earlier version after a schema change.\n * In such a case, data (but not schema), may need to be re-migrated\n * when rolling forward again.\n */\n dataVersion: v.number(),\n\n /**\n * The minimum code version that is safe to run. This is used when\n * a schema migration is not backwards compatible with an older version\n * of the code.\n */\n minSafeVersion: v.number(),\n});\n\n// Exposed for tests.\nexport type VersionHistory = v.Infer<typeof versionHistory>;\n\n// Exposed for tests\nexport function getVersionHistory(db: Db): VersionHistory {\n // Note: The `lock` column transparently ensures that at most one row exists.\n db.prepare(\n `\n CREATE TABLE IF NOT EXISTS \"_zero.versionHistory\" (\n dataVersion INTEGER NOT NULL,\n schemaVersion INTEGER NOT NULL,\n minSafeVersion INTEGER NOT NULL,\n\n lock INTEGER PRIMARY KEY DEFAULT 1 CHECK (lock=1)\n );\n `,\n ).run();\n const result = db\n .prepare(\n 'SELECT dataVersion, schemaVersion, minSafeVersion FROM \"_zero.versionHistory\"',\n )\n .get() as VersionHistory;\n return result ?? {dataVersion: 0, schemaVersion: 0, minSafeVersion: 0};\n}\n\nfunction updateVersionHistory(\n log: LogContext,\n db: Db,\n prev: VersionHistory,\n newVersion: number,\n minSafeVersion?: number,\n): VersionHistory {\n assert(newVersion > 0, 'newVersion must be positive');\n const meta = {\n ...prev,\n dataVersion: newVersion,\n // The schemaVersion never moves backwards.\n schemaVersion: Math.max(newVersion, prev.schemaVersion),\n minSafeVersion: getMinSafeVersion(log, prev, minSafeVersion),\n } satisfies VersionHistory;\n\n db.prepare(\n `\n INSERT INTO \"_zero.versionHistory\" (dataVersion, schemaVersion, minSafeVersion, lock)\n VALUES (@dataVersion, @schemaVersion, @minSafeVersion, 1)\n ON CONFLICT (lock) DO UPDATE\n SET dataVersion=@dataVersion,\n schemaVersion=@schemaVersion,\n minSafeVersion=@minSafeVersion\n `,\n ).run(meta);\n\n return meta;\n}\n\nasync function runMigration(\n log: LogContext,\n tx: Db,\n versions: VersionHistory,\n destinationVersion: number,\n migration: Migration,\n): Promise<VersionHistory> {\n if (versions.schemaVersion < destinationVersion) {\n await migration.migrateSchema?.(log, tx);\n }\n if (versions.dataVersion < destinationVersion) {\n await migration.migrateData?.(log, tx);\n }\n return updateVersionHistory(\n log,\n tx,\n versions,\n destinationVersion,\n migration.minSafeVersion,\n );\n}\n\n/**\n * Bumps the rollback limit [[toAtLeast]] the specified version.\n * Leaves the rollback limit unchanged if it is equal or greater.\n */\nfunction getMinSafeVersion(\n log: LogContext,\n current: VersionHistory,\n proposedSafeVersion?: number,\n): number {\n if (proposedSafeVersion === undefined) {\n return current.minSafeVersion;\n }\n if (current.minSafeVersion >= proposedSafeVersion) {\n // The rollback limit must never move backwards.\n log.debug?.(\n `rollback limit is already at ${current.minSafeVersion}, ` +\n `don't need to bump to ${proposedSafeVersion}`,\n );\n return current.minSafeVersion;\n }\n log.info?.(\n `bumping rollback limit from ${current.minSafeVersion} to ${proposedSafeVersion}`,\n );\n return proposedSafeVersion;\n}\n\n// Note: We use a custom transaction wrapper (instead of db.begin(...)) in order\n// to support async operations within the transaction.\nasync function runTransaction<T>(\n log: LogContext,\n db: Db,\n tx: (db: Db) => Promise<T> | T,\n): Promise<T> {\n db.prepare('BEGIN EXCLUSIVE').run();\n try {\n const result = await tx(db);\n db.prepare('COMMIT').run();\n return result;\n } catch (e) {\n log.error?.('Aborted transaction due to error', e);\n try {\n db.prepare('ROLLBACK').run();\n } catch (rollbackError) {\n log.error?.('Unable to rollback transaction', rollbackError);\n const combinedError = new Error(\n `Transaction failed and rollback also failed: operation error = ${String(\n e,\n )}; rollback error = ${String(rollbackError)}`,\n );\n combinedError.cause = e;\n throw combinedError;\n }\n throw e;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"migration-lite.js","names":[],"sources":["../../../../../zero-cache/src/db/migration-lite.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport {randInt} from '../../../shared/src/rand.ts';\nimport * as v from '../../../shared/src/valita.ts';\nimport type {Database as Db} from '../../../zqlite/src/db.ts';\nimport {Database} from '../../../zqlite/src/db.ts';\n\ntype Operations = (log: LogContext, tx: Db) => Promise<void> | void;\n\n/**\n * Encapsulates the logic for setting up or upgrading to a new schema. After the\n * Migration code successfully completes, {@link runSchemaMigrations}\n * will update the schema version and commit the transaction.\n */\nexport type Migration = {\n /**\n * Perform database operations that create or alter table structure. This is\n * called at most once during lifetime of the application. If a `migrateData()`\n * operation is defined, that will be performed after `migrateSchema()` succeeds.\n */\n migrateSchema?: Operations;\n\n /**\n * Perform database operations to migrate data to the new schema. This is\n * called after `migrateSchema()` (if defined), and may be called again\n * to re-migrate data after the server was rolled back to an earlier version,\n * and rolled forward again.\n *\n * Consequently, the logic in `migrateData()` must be idempotent.\n */\n migrateData?: Operations;\n\n /**\n * Sets the `minSafeVersion` to the specified value, prohibiting running\n * any earlier code versions.\n */\n minSafeVersion?: number;\n};\n\n/**\n * Mapping of incremental migrations to move from the previous old code\n * version to next one. Versions must be non-zero.\n *\n * The schema resulting from performing incremental migrations should be\n * equivalent to that of the `setupMigration` on a blank database.\n *\n * The highest destinationVersion of this map denotes the current\n * \"code version\", and is also used as the destination version when\n * running the initial setup migration on a blank database.\n */\nexport type IncrementalMigrationMap = {\n [destinationVersion: number]: Migration;\n};\n\n/**\n * Ensures that the schema is compatible with the current code, updating and\n * migrating the schema if necessary.\n */\nexport async function runSchemaMigrations(\n log: LogContext,\n debugName: string,\n dbPath: string,\n setupMigration: Migration,\n incrementalMigrationMap: IncrementalMigrationMap,\n): Promise<void> {\n const start = Date.now();\n log = log.withContext(\n 'initSchema',\n randInt(0, Number.MAX_SAFE_INTEGER).toString(36),\n );\n const db = new Database(log, dbPath);\n\n try {\n const versionMigrations = sorted(incrementalMigrationMap);\n assert(\n versionMigrations.length,\n `Must specify a at least one version migration`,\n );\n assert(\n versionMigrations[0][0] > 0,\n `Versions must be non-zero positive numbers`,\n );\n // oxlint-disable-next-line typescript/no-non-null-assertion\n const codeVersion = versionMigrations.at(-1)![0];\n log.info?.(\n `Checking schema for compatibility with ${debugName} at schema v${codeVersion}`,\n );\n\n let versions = await runTransaction(log, db, tx => {\n const versions = getVersionHistory(tx);\n if (codeVersion < versions.minSafeVersion) {\n throw new Error(\n `Cannot run ${debugName} at schema v${codeVersion} because rollback limit is v${versions.minSafeVersion}`,\n );\n }\n\n if (versions.dataVersion > codeVersion) {\n log.info?.(\n `Data is at v${versions.dataVersion}. Resetting to v${codeVersion}`,\n );\n return updateVersionHistory(log, tx, versions, codeVersion);\n }\n return versions;\n });\n\n if (versions.dataVersion < codeVersion) {\n db.unsafeMode(true); // Enables journal_mode = OFF\n db.pragma('locking_mode = EXCLUSIVE');\n db.pragma('foreign_keys = OFF');\n db.pragma('journal_mode = OFF');\n db.pragma('synchronous = OFF');\n // Unfortunately, AUTO_VACUUM is not compatible with BEGIN CONCURRENT,\n // so it is not an option for the replica file.\n // https://sqlite.org/forum/forumpost/25f183416a\n // db.pragma('auto_vacuum = INCREMENTAL');\n\n const migrations =\n versions.dataVersion === 0\n ? // For the empty database v0, only run the setup migration.\n ([[codeVersion, setupMigration]] as const)\n : versionMigrations;\n\n for (const [dest, migration] of migrations) {\n if (versions.dataVersion < dest) {\n log.info?.(\n `Migrating schema from v${versions.dataVersion} to v${dest}`,\n );\n void log.flush(); // Flush logs before each migration to help debug crash-y migrations.\n\n versions = await runTransaction(log, db, async tx => {\n // Fetch meta from within the transaction to make the migration atomic.\n let versions = getVersionHistory(tx);\n if (versions.dataVersion < dest) {\n versions = await runMigration(log, tx, versions, dest, migration);\n assert(\n versions.dataVersion === dest,\n () =>\n `Migration did not reach target version: expected ${dest}, got ${versions.dataVersion}`,\n );\n }\n return versions;\n });\n }\n }\n\n db.exec('ANALYZE main');\n log.info?.('ANALYZE completed');\n } else {\n // Run optimize whenever opening an sqlite db file as recommended in\n // https://www.sqlite.org/pragma.html#pragma_optimize\n // It is important to run the same initialization steps as is done\n // in the view-syncer (i.e. when preparing database for serving\n // replication) so that any corruption detected in the view-syncer is\n // similarly detected in the change-streamer, facilitating an eventual\n // recovery by resyncing the replica anew.\n db.pragma('optimize = 0x10002');\n\n // TODO: Investigate running `integrity_check` or `quick_check` as well,\n // provided that they are not inordinately expensive on large databases.\n }\n\n db.pragma('synchronous = NORMAL');\n db.unsafeMode(false);\n\n assert(\n versions.dataVersion === codeVersion,\n () =>\n `Final dataVersion (${versions.dataVersion}) does not match codeVersion (${codeVersion})`,\n );\n log.info?.(\n `Running ${debugName} at schema v${codeVersion} (${\n Date.now() - start\n } ms)`,\n );\n } catch (e) {\n log.error?.('Error in ensureSchemaMigrated', e);\n throw e;\n } finally {\n db.close();\n void log.flush(); // Flush the logs but do not block server progress on it.\n }\n}\n\nfunction sorted(\n incrementalMigrationMap: IncrementalMigrationMap,\n): [number, Migration][] {\n const versionMigrations: [number, Migration][] = [];\n for (const [v, m] of Object.entries(incrementalMigrationMap)) {\n versionMigrations.push([Number(v), m]);\n }\n return versionMigrations.sort(([a], [b]) => a - b);\n}\n\n// Exposed for tests.\nexport const versionHistory = v.object({\n /**\n * The `schemaVersion` is highest code version that has ever been run\n * on the database, and is used to delineate the structure of the tables\n * in the database. A schemaVersion only moves forward; rolling back to\n * an earlier (safe) code version does not revert schema changes that\n * have already been applied.\n */\n schemaVersion: v.number(),\n\n /**\n * The data version is the code version of the latest server that ran.\n * Note that this may be less than the schemaVersion in the case that\n * a server is rolled back to an earlier version after a schema change.\n * In such a case, data (but not schema), may need to be re-migrated\n * when rolling forward again.\n */\n dataVersion: v.number(),\n\n /**\n * The minimum code version that is safe to run. This is used when\n * a schema migration is not backwards compatible with an older version\n * of the code.\n */\n minSafeVersion: v.number(),\n});\n\n// Exposed for tests.\nexport type VersionHistory = v.Infer<typeof versionHistory>;\n\n// Exposed for tests\nexport function getVersionHistory(db: Db): VersionHistory {\n // Note: The `lock` column transparently ensures that at most one row exists.\n db.prepare(\n `\n CREATE TABLE IF NOT EXISTS \"_zero.versionHistory\" (\n dataVersion INTEGER NOT NULL,\n schemaVersion INTEGER NOT NULL,\n minSafeVersion INTEGER NOT NULL,\n\n lock INTEGER PRIMARY KEY DEFAULT 1 CHECK (lock=1)\n );\n `,\n ).run();\n const result = db\n .prepare(\n 'SELECT dataVersion, schemaVersion, minSafeVersion FROM \"_zero.versionHistory\"',\n )\n .get() as VersionHistory;\n return result ?? {dataVersion: 0, schemaVersion: 0, minSafeVersion: 0};\n}\n\nfunction updateVersionHistory(\n log: LogContext,\n db: Db,\n prev: VersionHistory,\n newVersion: number,\n minSafeVersion?: number,\n): VersionHistory {\n assert(newVersion > 0, 'newVersion must be positive');\n const meta = {\n ...prev,\n dataVersion: newVersion,\n // The schemaVersion never moves backwards.\n schemaVersion: Math.max(newVersion, prev.schemaVersion),\n minSafeVersion: getMinSafeVersion(log, prev, minSafeVersion),\n } satisfies VersionHistory;\n\n db.prepare(\n `\n INSERT INTO \"_zero.versionHistory\" (dataVersion, schemaVersion, minSafeVersion, lock)\n VALUES (@dataVersion, @schemaVersion, @minSafeVersion, 1)\n ON CONFLICT (lock) DO UPDATE\n SET dataVersion=@dataVersion,\n schemaVersion=@schemaVersion,\n minSafeVersion=@minSafeVersion\n `,\n ).run(meta);\n\n return meta;\n}\n\nasync function runMigration(\n log: LogContext,\n tx: Db,\n versions: VersionHistory,\n destinationVersion: number,\n migration: Migration,\n): Promise<VersionHistory> {\n if (versions.schemaVersion < destinationVersion) {\n await migration.migrateSchema?.(log, tx);\n }\n if (versions.dataVersion < destinationVersion) {\n await migration.migrateData?.(log, tx);\n }\n return updateVersionHistory(\n log,\n tx,\n versions,\n destinationVersion,\n migration.minSafeVersion,\n );\n}\n\n/**\n * Bumps the rollback limit [[toAtLeast]] the specified version.\n * Leaves the rollback limit unchanged if it is equal or greater.\n */\nfunction getMinSafeVersion(\n log: LogContext,\n current: VersionHistory,\n proposedSafeVersion?: number,\n): number {\n if (proposedSafeVersion === undefined) {\n return current.minSafeVersion;\n }\n if (current.minSafeVersion >= proposedSafeVersion) {\n // The rollback limit must never move backwards.\n log.debug?.(\n `rollback limit is already at ${current.minSafeVersion}, ` +\n `don't need to bump to ${proposedSafeVersion}`,\n );\n return current.minSafeVersion;\n }\n log.info?.(\n `bumping rollback limit from ${current.minSafeVersion} to ${proposedSafeVersion}`,\n );\n return proposedSafeVersion;\n}\n\n// Note: We use a custom transaction wrapper (instead of db.begin(...)) in order\n// to support async operations within the transaction.\nasync function runTransaction<T>(\n log: LogContext,\n db: Db,\n tx: (db: Db) => Promise<T> | T,\n): Promise<T> {\n db.prepare('BEGIN EXCLUSIVE').run();\n try {\n const result = await tx(db);\n db.prepare('COMMIT').run();\n return result;\n } catch (e) {\n log.error?.('Aborted transaction due to error', e);\n try {\n db.prepare('ROLLBACK').run();\n } catch (rollbackError) {\n log.error?.('Unable to rollback transaction', rollbackError);\n const combinedError = new Error(\n `Transaction failed and rollback also failed: operation error = ${String(\n e,\n )}; rollback error = ${String(rollbackError)}`,\n );\n combinedError.cause = e;\n throw combinedError;\n }\n throw e;\n }\n}\n"],"mappings":"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"}
@@ -47,27 +47,8 @@ function sorted(incrementalMigrationMap) {
47
47
  return versionMigrations.sort(([a], [b]) => a - b);
48
48
  }
49
49
  var versionHistory = valita_exports.object({
50
- /**
51
- * The `schemaVersion` is highest code version that has ever been run
52
- * on the database, and is used to delineate the structure of the tables
53
- * in the database. A schemaVersion only moves forward; rolling back to
54
- * an earlier (safe) code version does not revert schema changes that
55
- * have already been applied.
56
- */
57
50
  schemaVersion: valita_exports.number(),
58
- /**
59
- * The data version is the code version of the latest server that ran.
60
- * Note that this may be less than the schemaVersion in the case that
61
- * a server is rolled back to an earlier version after a schema change.
62
- * In such a case, data (but not schema), may need to be re-migrated
63
- * when rolling forward again.
64
- */
65
51
  dataVersion: valita_exports.number(),
66
- /**
67
- * The minimum code version that is safe to run. This is used when
68
- * a schema migration is not backwards compatible with an older version
69
- * of the code.
70
- */
71
52
  minSafeVersion: valita_exports.number()
72
53
  });
73
54
  async function createVersionHistoryTable(sql, schemaName) {
@@ -1 +1 @@
1
- {"version":3,"file":"migration.js","names":[],"sources":["../../../../../zero-cache/src/db/migration.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport type postgres from 'postgres';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport * as v from '../../../shared/src/valita.ts';\nimport {type PostgresDB, type PostgresTransaction} from '../types/pg.ts';\nimport {runTx} from './run-transaction.ts';\n\ntype Operations = (log: LogContext, tx: PostgresTransaction) => Promise<void>;\n\n/**\n * Encapsulates the logic for setting up or upgrading to a new schema. After the\n * Migration code successfully completes, {@link runSchemaMigrations}\n * will update the schema version and commit the transaction.\n */\nexport type Migration = {\n /**\n * Perform database operations that create or alter table structure. This is\n * called at most once during lifetime of the application. If a `migrateData()`\n * operation is defined, that will be performed after `migrateSchema()` succeeds.\n */\n migrateSchema?: Operations;\n\n /**\n * Perform database operations to migrate data to the new schema. This is\n * called after `migrateSchema()` (if defined), and may be called again\n * to re-migrate data after the server was rolled back to an earlier version,\n * and rolled forward again.\n *\n * Consequently, the logic in `migrateData()` must be idempotent.\n */\n migrateData?: Operations;\n\n /**\n * Sets the `minSafeVersion` to the specified value, prohibiting running\n * any earlier code versions.\n */\n minSafeVersion?: number;\n};\n\n/**\n * Mapping of incremental migrations to move from the previous old code\n * version to next one. Versions must be non-zero.\n *\n * The schema resulting from performing incremental migrations should be\n * equivalent to that of the `setupMigration` on a blank database.\n *\n * The highest destinationVersion of this map denotes the current\n * \"code version\", and is also used as the destination version when\n * running the initial setup migration on a blank database.\n */\nexport type IncrementalMigrationMap = {\n [destinationVersion: number]: Migration;\n};\n\n/**\n * Ensures that the schema is compatible with the current code, updating and\n * migrating the schema if necessary.\n */\nexport async function runSchemaMigrations(\n log: LogContext,\n debugName: string,\n schemaName: string,\n db: PostgresDB,\n setupMigration: Migration,\n incrementalMigrationMap: IncrementalMigrationMap,\n): Promise<void> {\n log = log.withContext('initSchema', schemaName);\n\n const versionMigrations = sorted(incrementalMigrationMap);\n assert(\n versionMigrations.length,\n `Must specify at least one version migration`,\n );\n assert(\n versionMigrations[0][0] > 0,\n `Versions must be non-zero positive numbers`,\n );\n // oxlint-disable-next-line typescript/no-non-null-assertion\n const codeVersion = versionMigrations.at(-1)![0];\n\n log.info?.(\n `Checking schema for compatibility with ${debugName} at schema v${codeVersion}`,\n );\n\n try {\n await runTx(db, async tx => {\n // Acquire advisory lock to prevent concurrent migrations from racing.\n // This can happen during rolling deployments when multiple pods start\n // up simultaneously. The lock auto-releases when the transaction ends.\n const lockName = `migrate-schema:${schemaName}`;\n await tx`SELECT pg_advisory_xact_lock(hashtext(${lockName}))`;\n\n let versions = await ensureVersionHistory(tx, schemaName);\n\n if (codeVersion < versions.minSafeVersion) {\n throw new Error(\n `Cannot run ${debugName} at schema v${codeVersion} because rollback limit is v${versions.minSafeVersion}`,\n );\n }\n\n if (versions.dataVersion > codeVersion) {\n log.info?.(\n `Data is at v${versions.dataVersion}. Resetting to v${codeVersion}`,\n );\n await updateVersionHistory(log, tx, schemaName, versions, codeVersion);\n return;\n }\n\n if (versions.dataVersion === codeVersion) {\n return;\n }\n\n const migrations =\n versions.dataVersion === 0\n ? // For an empty database (v0), only run the setup migration.\n ([[codeVersion, setupMigration]] as const)\n : versionMigrations;\n\n for (const [dest, migration] of migrations) {\n if (versions.dataVersion < dest) {\n log.info?.(\n `Migrating schema from v${versions.dataVersion} to v${dest}`,\n );\n void log.flush();\n versions = await runMigration(\n log,\n schemaName,\n tx,\n versions,\n dest,\n migration,\n );\n }\n }\n });\n\n log.info?.(`Running ${debugName} at schema v${codeVersion}`);\n } catch (e) {\n log.error?.('Error in ensureSchemaMigrated', e);\n throw e;\n } finally {\n void log.flush();\n }\n}\n\nfunction sorted(\n incrementalMigrationMap: IncrementalMigrationMap,\n): [number, Migration][] {\n const versionMigrations: [number, Migration][] = [];\n for (const [v, m] of Object.entries(incrementalMigrationMap)) {\n versionMigrations.push([Number(v), m]);\n }\n return versionMigrations.sort(([a], [b]) => a - b);\n}\n\n// Exposed for tests.\nexport const versionHistory = v.object({\n /**\n * The `schemaVersion` is highest code version that has ever been run\n * on the database, and is used to delineate the structure of the tables\n * in the database. A schemaVersion only moves forward; rolling back to\n * an earlier (safe) code version does not revert schema changes that\n * have already been applied.\n */\n schemaVersion: v.number(),\n\n /**\n * The data version is the code version of the latest server that ran.\n * Note that this may be less than the schemaVersion in the case that\n * a server is rolled back to an earlier version after a schema change.\n * In such a case, data (but not schema), may need to be re-migrated\n * when rolling forward again.\n */\n dataVersion: v.number(),\n\n /**\n * The minimum code version that is safe to run. This is used when\n * a schema migration is not backwards compatible with an older version\n * of the code.\n */\n minSafeVersion: v.number(),\n});\n\n// Exposed for tests.\nexport type VersionHistory = v.Infer<typeof versionHistory>;\n\n// Exposed for tests.\nexport async function createVersionHistoryTable(\n sql: postgres.Sql,\n schemaName: string,\n) {\n // Note: The `lock` column transparently ensures that at most one row exists.\n await sql`\n CREATE SCHEMA IF NOT EXISTS ${sql(schemaName)};\n CREATE TABLE IF NOT EXISTS ${sql(schemaName)}.\"versionHistory\" (\n \"dataVersion\" int NOT NULL,\n \"schemaVersion\" int NOT NULL,\n \"minSafeVersion\" int NOT NULL,\n\n lock char(1) NOT NULL CONSTRAINT DF_schema_meta_lock DEFAULT 'v',\n CONSTRAINT PK_schema_meta_lock PRIMARY KEY (lock),\n CONSTRAINT CK_schema_meta_lock CHECK (lock='v')\n );`.simple();\n}\n\nasync function ensureVersionHistory(\n sql: postgres.Sql,\n schemaName: string,\n): Promise<VersionHistory> {\n return must(await getVersionHistory(sql, schemaName, true));\n}\n\nexport async function getVersionHistory(\n sql: postgres.Sql,\n schemaName: string,\n create = false,\n): Promise<VersionHistory | null> {\n const exists = await sql`\n SELECT nspname, relname FROM pg_class\n JOIN pg_namespace ON relnamespace = pg_namespace.oid\n WHERE nspname = ${schemaName} AND relname = ${'versionHistory'}`;\n\n if (exists.length === 0) {\n if (create) {\n await createVersionHistoryTable(sql, schemaName);\n } else {\n return null;\n }\n }\n const rows = await sql`\n SELECT \"dataVersion\", \"schemaVersion\", \"minSafeVersion\"\n FROM ${sql(schemaName)}.\"versionHistory\"`;\n\n if (rows.length === 0) {\n return create\n ? {schemaVersion: 0, dataVersion: 0, minSafeVersion: 0}\n : null;\n }\n return v.parse(rows[0], versionHistory);\n}\n\nasync function updateVersionHistory(\n log: LogContext,\n sql: postgres.Sql,\n schemaName: string,\n prev: VersionHistory,\n newVersion: number,\n minSafeVersion?: number,\n): Promise<VersionHistory> {\n assert(newVersion > 0, 'newVersion must be positive');\n const versions = {\n dataVersion: newVersion,\n // The schemaVersion never moves backwards.\n schemaVersion: Math.max(newVersion, prev.schemaVersion),\n minSafeVersion: getMinSafeVersion(log, prev, minSafeVersion),\n } satisfies VersionHistory;\n\n await sql`\n INSERT INTO ${sql(schemaName)}.\"versionHistory\" ${sql(versions)}\n ON CONFLICT (lock) DO UPDATE SET ${sql(versions)}\n `;\n return versions;\n}\n\nasync function runMigration(\n log: LogContext,\n schemaName: string,\n tx: PostgresTransaction,\n versions: VersionHistory,\n destinationVersion: number,\n migration: Migration,\n): Promise<VersionHistory> {\n if (versions.schemaVersion < destinationVersion) {\n await migration.migrateSchema?.(log, tx);\n }\n if (versions.dataVersion < destinationVersion) {\n await migration.migrateData?.(log, tx);\n }\n return updateVersionHistory(\n log,\n tx,\n schemaName,\n versions,\n destinationVersion,\n migration.minSafeVersion,\n );\n}\n\n/**\n * Bumps the rollback limit [[toAtLeast]] the specified version.\n * Leaves the rollback limit unchanged if it is equal or greater.\n */\nfunction getMinSafeVersion(\n log: LogContext,\n current: VersionHistory,\n proposedSafeVersion?: number,\n): number {\n if (proposedSafeVersion === undefined) {\n return current.minSafeVersion;\n }\n if (current.minSafeVersion >= proposedSafeVersion) {\n // The rollback limit must never move backwards.\n log.debug?.(\n `rollback limit is already at ${current.minSafeVersion}, ` +\n `don't need to bump to ${proposedSafeVersion}`,\n );\n return current.minSafeVersion;\n }\n log.info?.(\n `bumping rollback limit from ${current.minSafeVersion} to ${proposedSafeVersion}`,\n );\n return proposedSafeVersion;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"migration.js","names":[],"sources":["../../../../../zero-cache/src/db/migration.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport type postgres from 'postgres';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport * as v from '../../../shared/src/valita.ts';\nimport {type PostgresDB, type PostgresTransaction} from '../types/pg.ts';\nimport {runTx} from './run-transaction.ts';\n\ntype Operations = (log: LogContext, tx: PostgresTransaction) => Promise<void>;\n\n/**\n * Encapsulates the logic for setting up or upgrading to a new schema. After the\n * Migration code successfully completes, {@link runSchemaMigrations}\n * will update the schema version and commit the transaction.\n */\nexport type Migration = {\n /**\n * Perform database operations that create or alter table structure. This is\n * called at most once during lifetime of the application. If a `migrateData()`\n * operation is defined, that will be performed after `migrateSchema()` succeeds.\n */\n migrateSchema?: Operations;\n\n /**\n * Perform database operations to migrate data to the new schema. This is\n * called after `migrateSchema()` (if defined), and may be called again\n * to re-migrate data after the server was rolled back to an earlier version,\n * and rolled forward again.\n *\n * Consequently, the logic in `migrateData()` must be idempotent.\n */\n migrateData?: Operations;\n\n /**\n * Sets the `minSafeVersion` to the specified value, prohibiting running\n * any earlier code versions.\n */\n minSafeVersion?: number;\n};\n\n/**\n * Mapping of incremental migrations to move from the previous old code\n * version to next one. Versions must be non-zero.\n *\n * The schema resulting from performing incremental migrations should be\n * equivalent to that of the `setupMigration` on a blank database.\n *\n * The highest destinationVersion of this map denotes the current\n * \"code version\", and is also used as the destination version when\n * running the initial setup migration on a blank database.\n */\nexport type IncrementalMigrationMap = {\n [destinationVersion: number]: Migration;\n};\n\n/**\n * Ensures that the schema is compatible with the current code, updating and\n * migrating the schema if necessary.\n */\nexport async function runSchemaMigrations(\n log: LogContext,\n debugName: string,\n schemaName: string,\n db: PostgresDB,\n setupMigration: Migration,\n incrementalMigrationMap: IncrementalMigrationMap,\n): Promise<void> {\n log = log.withContext('initSchema', schemaName);\n\n const versionMigrations = sorted(incrementalMigrationMap);\n assert(\n versionMigrations.length,\n `Must specify at least one version migration`,\n );\n assert(\n versionMigrations[0][0] > 0,\n `Versions must be non-zero positive numbers`,\n );\n // oxlint-disable-next-line typescript/no-non-null-assertion\n const codeVersion = versionMigrations.at(-1)![0];\n\n log.info?.(\n `Checking schema for compatibility with ${debugName} at schema v${codeVersion}`,\n );\n\n try {\n await runTx(db, async tx => {\n // Acquire advisory lock to prevent concurrent migrations from racing.\n // This can happen during rolling deployments when multiple pods start\n // up simultaneously. The lock auto-releases when the transaction ends.\n const lockName = `migrate-schema:${schemaName}`;\n await tx`SELECT pg_advisory_xact_lock(hashtext(${lockName}))`;\n\n let versions = await ensureVersionHistory(tx, schemaName);\n\n if (codeVersion < versions.minSafeVersion) {\n throw new Error(\n `Cannot run ${debugName} at schema v${codeVersion} because rollback limit is v${versions.minSafeVersion}`,\n );\n }\n\n if (versions.dataVersion > codeVersion) {\n log.info?.(\n `Data is at v${versions.dataVersion}. Resetting to v${codeVersion}`,\n );\n await updateVersionHistory(log, tx, schemaName, versions, codeVersion);\n return;\n }\n\n if (versions.dataVersion === codeVersion) {\n return;\n }\n\n const migrations =\n versions.dataVersion === 0\n ? // For an empty database (v0), only run the setup migration.\n ([[codeVersion, setupMigration]] as const)\n : versionMigrations;\n\n for (const [dest, migration] of migrations) {\n if (versions.dataVersion < dest) {\n log.info?.(\n `Migrating schema from v${versions.dataVersion} to v${dest}`,\n );\n void log.flush();\n versions = await runMigration(\n log,\n schemaName,\n tx,\n versions,\n dest,\n migration,\n );\n }\n }\n });\n\n log.info?.(`Running ${debugName} at schema v${codeVersion}`);\n } catch (e) {\n log.error?.('Error in ensureSchemaMigrated', e);\n throw e;\n } finally {\n void log.flush();\n }\n}\n\nfunction sorted(\n incrementalMigrationMap: IncrementalMigrationMap,\n): [number, Migration][] {\n const versionMigrations: [number, Migration][] = [];\n for (const [v, m] of Object.entries(incrementalMigrationMap)) {\n versionMigrations.push([Number(v), m]);\n }\n return versionMigrations.sort(([a], [b]) => a - b);\n}\n\n// Exposed for tests.\nexport const versionHistory = v.object({\n /**\n * The `schemaVersion` is highest code version that has ever been run\n * on the database, and is used to delineate the structure of the tables\n * in the database. A schemaVersion only moves forward; rolling back to\n * an earlier (safe) code version does not revert schema changes that\n * have already been applied.\n */\n schemaVersion: v.number(),\n\n /**\n * The data version is the code version of the latest server that ran.\n * Note that this may be less than the schemaVersion in the case that\n * a server is rolled back to an earlier version after a schema change.\n * In such a case, data (but not schema), may need to be re-migrated\n * when rolling forward again.\n */\n dataVersion: v.number(),\n\n /**\n * The minimum code version that is safe to run. This is used when\n * a schema migration is not backwards compatible with an older version\n * of the code.\n */\n minSafeVersion: v.number(),\n});\n\n// Exposed for tests.\nexport type VersionHistory = v.Infer<typeof versionHistory>;\n\n// Exposed for tests.\nexport async function createVersionHistoryTable(\n sql: postgres.Sql,\n schemaName: string,\n) {\n // Note: The `lock` column transparently ensures that at most one row exists.\n await sql`\n CREATE SCHEMA IF NOT EXISTS ${sql(schemaName)};\n CREATE TABLE IF NOT EXISTS ${sql(schemaName)}.\"versionHistory\" (\n \"dataVersion\" int NOT NULL,\n \"schemaVersion\" int NOT NULL,\n \"minSafeVersion\" int NOT NULL,\n\n lock char(1) NOT NULL CONSTRAINT DF_schema_meta_lock DEFAULT 'v',\n CONSTRAINT PK_schema_meta_lock PRIMARY KEY (lock),\n CONSTRAINT CK_schema_meta_lock CHECK (lock='v')\n );`.simple();\n}\n\nasync function ensureVersionHistory(\n sql: postgres.Sql,\n schemaName: string,\n): Promise<VersionHistory> {\n return must(await getVersionHistory(sql, schemaName, true));\n}\n\nexport async function getVersionHistory(\n sql: postgres.Sql,\n schemaName: string,\n create = false,\n): Promise<VersionHistory | null> {\n const exists = await sql`\n SELECT nspname, relname FROM pg_class\n JOIN pg_namespace ON relnamespace = pg_namespace.oid\n WHERE nspname = ${schemaName} AND relname = ${'versionHistory'}`;\n\n if (exists.length === 0) {\n if (create) {\n await createVersionHistoryTable(sql, schemaName);\n } else {\n return null;\n }\n }\n const rows = await sql`\n SELECT \"dataVersion\", \"schemaVersion\", \"minSafeVersion\"\n FROM ${sql(schemaName)}.\"versionHistory\"`;\n\n if (rows.length === 0) {\n return create\n ? {schemaVersion: 0, dataVersion: 0, minSafeVersion: 0}\n : null;\n }\n return v.parse(rows[0], versionHistory);\n}\n\nasync function updateVersionHistory(\n log: LogContext,\n sql: postgres.Sql,\n schemaName: string,\n prev: VersionHistory,\n newVersion: number,\n minSafeVersion?: number,\n): Promise<VersionHistory> {\n assert(newVersion > 0, 'newVersion must be positive');\n const versions = {\n dataVersion: newVersion,\n // The schemaVersion never moves backwards.\n schemaVersion: Math.max(newVersion, prev.schemaVersion),\n minSafeVersion: getMinSafeVersion(log, prev, minSafeVersion),\n } satisfies VersionHistory;\n\n await sql`\n INSERT INTO ${sql(schemaName)}.\"versionHistory\" ${sql(versions)}\n ON CONFLICT (lock) DO UPDATE SET ${sql(versions)}\n `;\n return versions;\n}\n\nasync function runMigration(\n log: LogContext,\n schemaName: string,\n tx: PostgresTransaction,\n versions: VersionHistory,\n destinationVersion: number,\n migration: Migration,\n): Promise<VersionHistory> {\n if (versions.schemaVersion < destinationVersion) {\n await migration.migrateSchema?.(log, tx);\n }\n if (versions.dataVersion < destinationVersion) {\n await migration.migrateData?.(log, tx);\n }\n return updateVersionHistory(\n log,\n tx,\n schemaName,\n versions,\n destinationVersion,\n migration.minSafeVersion,\n );\n}\n\n/**\n * Bumps the rollback limit [[toAtLeast]] the specified version.\n * Leaves the rollback limit unchanged if it is equal or greater.\n */\nfunction getMinSafeVersion(\n log: LogContext,\n current: VersionHistory,\n proposedSafeVersion?: number,\n): number {\n if (proposedSafeVersion === undefined) {\n return current.minSafeVersion;\n }\n if (current.minSafeVersion >= proposedSafeVersion) {\n // The rollback limit must never move backwards.\n log.debug?.(\n `rollback limit is already at ${current.minSafeVersion}, ` +\n `don't need to bump to ${proposedSafeVersion}`,\n );\n return current.minSafeVersion;\n }\n log.info?.(\n `bumping rollback limit from ${current.minSafeVersion} to ${proposedSafeVersion}`,\n );\n return proposedSafeVersion;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"pg-copy-binary.js","names":["#append","#headerParsed","#tryParseHeader","#fieldsRemaining","#remaining","#buffer","#offset","#compact"],"sources":["../../../../../zero-cache/src/db/pg-copy-binary.ts"],"sourcesContent":["import {stringify} from '../../../shared/src/bigint-json.ts';\nimport type {LiteValueType} from '../types/lite.ts';\nimport {\n BOOL,\n BPCHAR,\n BYTEA,\n CHAR,\n DATE,\n FLOAT4,\n FLOAT8,\n INT2,\n INT4,\n INT8,\n JSONB,\n NUMERIC,\n TEXT,\n TIME,\n TIMESTAMP,\n TIMESTAMPTZ,\n TIMETZ,\n UUID,\n VARCHAR,\n} from '../types/pg-types.ts';\nimport {JSON as JSON_OID} from '../types/pg-types.ts';\nimport * as PostgresTypeClass from './postgres-type-class-enum.ts';\nimport type {ColumnSpec} from './specs.ts';\n\n// PostgreSQL COPY binary format signature: \"PGCOPY\\n\\xff\\r\\n\\0\"\nconst PGCOPY_SIGNATURE = Buffer.from([\n 0x50, 0x47, 0x43, 0x4f, 0x50, 0x59, 0x0a, 0xff, 0x0d, 0x0a, 0x00,\n]);\n\nconst HEADER_MIN_SIZE = 11 + 4 + 4; // signature + flags + extension length\n\n// PostgreSQL epoch is 2000-01-01T00:00:00Z.\n// Offset from Unix epoch (1970-01-01) in milliseconds.\nconst PG_EPOCH_UNIX_MILLIS = 946_684_800_000;\n\n// Days from Unix epoch (1970-01-01) to PG epoch (2000-01-01).\nconst PG_EPOCH_UNIX_DAYS = 10_957;\n\nconst MS_PER_DAY = 86_400_000;\n\n// Sentinel values for infinity in PG binary format (as hi/lo int32 pairs).\nconst PG_TIMESTAMP_INF_HI = 0x7fffffff;\nconst PG_TIMESTAMP_INF_LO = 0xffffffff;\nconst PG_TIMESTAMP_NEG_INF_HI = -0x80000000; // readInt32BE of 0x80000000\nconst PG_TIMESTAMP_NEG_INF_LO = 0;\nconst PG_DATE_INFINITY = 0x7fffffff;\nconst PG_DATE_NEG_INFINITY = -0x80000000;\n\n/**\n * Streaming parser for PostgreSQL `COPY ... TO STDOUT WITH (FORMAT binary)`.\n *\n * Analogous to {@link import('./pg-copy.ts').TsvParser} but for binary format.\n * Yields `Buffer | null` per field (null = SQL NULL).\n *\n * The caller tracks column position the same way as with TsvParser.\n */\nexport class BinaryCopyParser {\n #buffer: Buffer = Buffer.alloc(0);\n #offset = 0;\n #headerParsed = false;\n #fieldsRemaining = 0; // fields left in current tuple (0 = need new tuple header)\n\n *parse(chunk: Buffer): Iterable<Buffer | null> {\n this.#append(chunk);\n\n if (!this.#headerParsed) {\n if (!this.#tryParseHeader()) {\n return;\n }\n }\n\n for (;;) {\n // If we're at the start of a tuple, read the field count.\n if (this.#fieldsRemaining === 0) {\n if (this.#remaining() < 2) {\n break;\n }\n const fieldCount = this.#buffer.readInt16BE(this.#offset);\n if (fieldCount === -1) {\n // Trailer marker — end of data.\n break;\n }\n this.#offset += 2;\n this.#fieldsRemaining = fieldCount;\n }\n\n // Parse fields within the current tuple.\n while (this.#fieldsRemaining > 0) {\n if (this.#remaining() < 4) {\n // Not enough data for field length — wait for next chunk.\n this.#compact();\n return;\n }\n const fieldLen = this.#buffer.readInt32BE(this.#offset);\n this.#offset += 4;\n\n if (fieldLen === -1) {\n // NULL field.\n yield null;\n } else {\n if (this.#remaining() < fieldLen) {\n // Not enough data for field value — rewind past the length\n // we just read and wait for more data.\n this.#offset -= 4;\n this.#compact();\n return;\n }\n yield this.#buffer.subarray(this.#offset, this.#offset + fieldLen);\n this.#offset += fieldLen;\n }\n this.#fieldsRemaining--;\n }\n }\n\n this.#compact();\n }\n\n #remaining(): number {\n return this.#buffer.length - this.#offset;\n }\n\n #append(chunk: Buffer): void {\n if (this.#buffer.length === this.#offset) {\n // Fully consumed — replace.\n this.#buffer = chunk;\n this.#offset = 0;\n } else {\n // Concatenate unconsumed remainder with new chunk.\n this.#buffer = Buffer.concat([\n this.#buffer.subarray(this.#offset),\n chunk,\n ]);\n this.#offset = 0;\n }\n }\n\n #compact(): void {\n if (this.#offset > 0) {\n this.#buffer = this.#buffer.subarray(this.#offset);\n this.#offset = 0;\n }\n }\n\n #tryParseHeader(): boolean {\n if (this.#remaining() < HEADER_MIN_SIZE) {\n return false;\n }\n\n // Validate signature.\n for (let i = 0; i < PGCOPY_SIGNATURE.length; i++) {\n if (this.#buffer[this.#offset + i] !== PGCOPY_SIGNATURE[i]) {\n throw new Error('Invalid PGCOPY binary signature');\n }\n }\n this.#offset += 11;\n\n // Flags (int32) — currently only bit 16 (has OID column) is defined.\n // We don't use OID columns, so just skip.\n const flags = this.#buffer.readInt32BE(this.#offset);\n this.#offset += 4;\n if (flags !== 0) {\n throw new Error(`Unsupported PGCOPY flags: ${flags}`);\n }\n\n // Extension area length (int32).\n const extensionLen = this.#buffer.readInt32BE(this.#offset);\n this.#offset += 4;\n\n // Skip extension data if present.\n if (extensionLen > 0) {\n if (this.#remaining() < extensionLen) {\n // Rewind and wait for more data.\n this.#offset -= HEADER_MIN_SIZE;\n return false;\n }\n this.#offset += extensionLen;\n }\n\n this.#headerParsed = true;\n return true;\n }\n}\n\n// ---- Binary Type Decoders ----\n\nexport type BinaryDecoder = (buf: Buffer) => LiteValueType;\n\ntype BinaryColumnSpec = Pick<\n ColumnSpec,\n 'dataType' | 'pgTypeClass' | 'elemPgTypeClass'\n> & {typeOID: number};\n\nconst KNOWN_BINARY_OIDS = new Set([\n BOOL,\n INT2,\n INT4,\n INT8,\n FLOAT4,\n FLOAT8,\n TEXT,\n VARCHAR,\n BPCHAR,\n CHAR,\n UUID,\n BYTEA,\n JSON_OID,\n JSONB,\n TIMESTAMP,\n TIMESTAMPTZ,\n DATE,\n TIME,\n TIMETZ,\n NUMERIC,\n]);\n\n/**\n * Returns true if the column's binary format is known and can be decoded\n * natively. For columns where this returns false, the COPY SELECT should\n * cast the column to `::text` so PG sends the text representation inside\n * the binary frame.\n */\nexport function hasBinaryDecoder(spec: BinaryColumnSpec): boolean {\n if (spec.elemPgTypeClass !== null && spec.elemPgTypeClass !== undefined) {\n return true; // Array types\n }\n if (spec.pgTypeClass === PostgresTypeClass.Enum) {\n return true; // Enums are sent as UTF-8 text in binary format\n }\n return KNOWN_BINARY_OIDS.has(spec.typeOID);\n}\n\n/** Decoder for columns cast to `::text` in the COPY SELECT. */\nexport const textCastDecoder: BinaryDecoder = buf => buf.toString('utf8');\n\n/**\n * Creates a specialized binary decoder for the given column spec.\n * The returned function converts a raw COPY binary field `Buffer`\n * directly to a `LiteValueType`, bypassing text parsing entirely.\n *\n * Only call this for columns where {@link hasBinaryDecoder} returns true.\n * For other columns, cast to `::text` in the SELECT and use\n * {@link textCastDecoder}.\n */\nexport function makeBinaryDecoder(spec: BinaryColumnSpec): BinaryDecoder {\n const {typeOID, pgTypeClass, elemPgTypeClass} = spec;\n\n // Array types: elemPgTypeClass is non-null for arrays.\n if (elemPgTypeClass !== null && elemPgTypeClass !== undefined) {\n return buf => decodeArray(buf);\n }\n\n // Enum types: binary representation is UTF-8 text.\n if (pgTypeClass === PostgresTypeClass.Enum) {\n return buf => buf.toString('utf8');\n }\n\n switch (typeOID) {\n case BOOL:\n return buf => (buf[0] ? 1 : 0);\n case INT2:\n return buf => buf.readInt16BE(0);\n case INT4:\n return buf => buf.readInt32BE(0);\n case INT8:\n return buf => buf.readBigInt64BE(0);\n case FLOAT4:\n return buf => buf.readFloatBE(0);\n case FLOAT8:\n return buf => buf.readDoubleBE(0);\n case TEXT:\n case VARCHAR:\n case BPCHAR:\n case CHAR:\n return buf => buf.toString('utf8');\n case UUID:\n return buf => decodeUUID(buf);\n case BYTEA:\n return buf => Uint8Array.prototype.slice.call(buf) as Uint8Array;\n case JSON_OID:\n return buf => buf.toString('utf8');\n case JSONB:\n // JSONB binary format has a 1-byte version prefix (currently 0x01).\n return buf => buf.toString('utf8', 1);\n case TIMESTAMP:\n case TIMESTAMPTZ:\n return buf => decodeTimestamp(buf);\n case DATE:\n return buf => decodeDate(buf);\n case TIME:\n return buf => decodeTime(buf);\n case TIMETZ:\n return buf => decodeTimeTZ(buf);\n case NUMERIC:\n return buf => decodeNumeric(buf);\n default:\n throw new Error(\n `No binary decoder for type OID ${typeOID}. ` +\n `Use hasBinaryDecoder() to check before calling makeBinaryDecoder().`,\n );\n }\n}\n\n// ---- Individual Decoders (exported for testing) ----\n\n/**\n * UUID: 16 bytes → \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"\n */\nexport function decodeUUID(buf: Buffer): string {\n const hex = buf.toString('hex');\n return (\n hex.substring(0, 8) +\n '-' +\n hex.substring(8, 12) +\n '-' +\n hex.substring(12, 16) +\n '-' +\n hex.substring(16, 20) +\n '-' +\n hex.substring(20, 32)\n );\n}\n\n/**\n * TIMESTAMP / TIMESTAMPTZ: int64 microseconds since PG epoch (2000-01-01 UTC)\n * → floating-point milliseconds since Unix epoch.\n *\n * Matches the output of `timestampToFpMillis()` in `types/pg.ts`.\n *\n * Uses Number arithmetic (avoiding BigInt) for speed. The microsecond value\n * fits safely in a Number for all practical dates (up to ~year 285,000).\n */\nexport function decodeTimestamp(buf: Buffer): number {\n const hi = buf.readInt32BE(0);\n const lo = buf.readUInt32BE(4);\n if (hi === PG_TIMESTAMP_INF_HI && lo === PG_TIMESTAMP_INF_LO) return Infinity;\n if (hi === PG_TIMESTAMP_NEG_INF_HI && lo === PG_TIMESTAMP_NEG_INF_LO) {\n return -Infinity;\n }\n const microseconds = hi * 0x100000000 + lo;\n return microseconds / 1000 + PG_EPOCH_UNIX_MILLIS;\n}\n\n/**\n * DATE: int32 days since PG epoch (2000-01-01) → millis since Unix epoch at\n * UTC midnight. Matches `dateToUTCMidnight()` in `types/pg.ts`.\n */\nexport function decodeDate(buf: Buffer): number {\n const pgDays = buf.readInt32BE(0);\n if (pgDays === PG_DATE_INFINITY) return Infinity;\n if (pgDays === PG_DATE_NEG_INFINITY) return -Infinity;\n return (pgDays + PG_EPOCH_UNIX_DAYS) * MS_PER_DAY;\n}\n\n/**\n * TIME: int64 microseconds since midnight → milliseconds since midnight.\n * Matches `postgresTimeToMilliseconds()` in `types/pg.ts`.\n *\n * Max value is 86,400,000,000 (~8.6e10), well within Number.MAX_SAFE_INTEGER.\n */\nexport function decodeTime(buf: Buffer): number {\n const hi = buf.readInt32BE(0);\n const lo = buf.readUInt32BE(4);\n const micros = hi * 0x100000000 + lo;\n return Math.trunc(micros / 1000);\n}\n\n/**\n * TIMETZ: int64 microseconds since midnight + int32 timezone offset in seconds.\n * PG stores the offset with inverted sign from ISO (POSIX convention):\n * positive = west of UTC, negative = east of UTC.\n * UTC = local_time + pg_offset.\n * → UTC milliseconds since midnight.\n *\n * Max value ~1.3e11 microseconds, well within Number.MAX_SAFE_INTEGER.\n */\nexport function decodeTimeTZ(buf: Buffer): number {\n const hi = buf.readInt32BE(0);\n const lo = buf.readUInt32BE(4);\n const localMicros = hi * 0x100000000 + lo;\n const tzOffsetSeconds = buf.readInt32BE(8);\n const utcMicros = localMicros + tzOffsetSeconds * 1_000_000;\n let ms = Math.trunc(utcMicros / 1000);\n // Normalize to [0, MS_PER_DAY).\n if (ms < 0 || ms >= MS_PER_DAY) {\n ms = ((ms % MS_PER_DAY) + MS_PER_DAY) % MS_PER_DAY;\n }\n return ms;\n}\n\n// NUMERIC binary format constants.\nconst NUMERIC_NEG = 0x4000;\nconst NUMERIC_NAN = 0xc000;\nconst NUMERIC_PINF = 0xd000;\nconst NUMERIC_NINF = 0xf000;\nconst NBASE = 10_000;\n\n/**\n * NUMERIC: variable-length binary format.\n * Header: {ndigits: int16, weight: int16, sign: int16, dscale: int16}\n * Followed by ndigits x int16 base-10000 digits.\n *\n * Converts to a JS `number` (matching the text path's `Number(x)` behavior).\n */\nexport function decodeNumeric(buf: Buffer): number {\n const ndigits = buf.readInt16BE(0);\n const weight = buf.readInt16BE(2);\n const sign = buf.readUInt16BE(4);\n // const dscale = buf.readInt16BE(6); // display scale, not needed for value\n\n if (sign === NUMERIC_NAN) {\n return NaN;\n }\n if (sign === NUMERIC_PINF) {\n return Infinity;\n }\n if (sign === NUMERIC_NINF) {\n return -Infinity;\n }\n if (ndigits === 0) {\n return 0;\n }\n\n // Accumulate base-10000 digits into an integer, then do a single\n // division at the end. Repeated `scale /= NBASE` accumulates\n // floating-point error (e.g. 9900 * 0.0001 = 0.9900000000000001).\n // A single division lets IEEE 754 round to the nearest double,\n // matching the text path's `Number(\"0.99\")` behavior.\n //\n // For numerics with many digits (ndigits > 3), intVal can exceed\n // MAX_SAFE_INTEGER. In that case, fall back to building a string\n // and using Number() to match the text path exactly.\n if (ndigits > 3) {\n return decodeNumericViaString(buf, ndigits, weight, sign);\n }\n\n let intVal = 0;\n for (let i = 0; i < ndigits; i++) {\n intVal = intVal * NBASE + buf.readInt16BE(8 + i * 2);\n }\n\n // weight indicates the power-of-NBASE of the first digit.\n // shift is how many base-10000 positions to divide by.\n const shift = ndigits - weight - 1;\n let result;\n if (shift > 0) {\n result = intVal / NBASE ** shift;\n } else if (shift < 0) {\n result = intVal * NBASE ** -shift;\n } else {\n result = intVal;\n }\n return sign === NUMERIC_NEG ? -result : result;\n}\n\n/**\n * Fallback for numerics with many base-10000 digits where accumulating\n * into an integer would exceed MAX_SAFE_INTEGER. Builds the decimal\n * string and uses Number() to match the text path exactly.\n */\nfunction decodeNumericViaString(\n buf: Buffer,\n ndigits: number,\n weight: number,\n sign: number,\n): number {\n // Number of base-10000 digit groups before the decimal point.\n const intGroups = weight + 1;\n\n let str = '';\n for (let i = 0; i < ndigits; i++) {\n const digit = buf.readInt16BE(8 + i * 2);\n if (i === intGroups) {\n str = str || '0';\n str += '.';\n }\n str += i === 0 ? String(digit) : String(digit).padStart(4, '0');\n }\n\n // Append trailing zero groups if the integer part extends beyond ndigits.\n if (intGroups > ndigits) {\n str += '0'.repeat((intGroups - ndigits) * 4);\n }\n\n return Number((sign === NUMERIC_NEG ? '-' : '') + str);\n}\n\n/**\n * Array: binary format.\n *\n * Header:\n * int32 ndim — number of dimensions (0 for empty array)\n * int32 flags — 0 or 1 (has-nulls)\n * int32 elem_oid — OID of element type\n * Per dimension:\n * int32 dim_size — number of elements in this dimension\n * int32 dim_lb — lower bound (usually 1)\n *\n * Then for each element (in row-major order):\n * int32 length — -1 for NULL, otherwise byte length\n * bytes — element data\n *\n * Result is JSON.stringify'd for storage in SQLite (matching text path behavior).\n */\nexport function decodeArray(buf: Buffer): string {\n let offset = 0;\n\n const ndim = buf.readInt32BE(offset);\n offset += 4;\n // skip flags (has-nulls)\n offset += 4;\n const elemOid = buf.readInt32BE(offset);\n offset += 4;\n\n if (ndim === 0) {\n return '[]';\n }\n\n // Read dimension sizes.\n const dims: number[] = [];\n for (let d = 0; d < ndim; d++) {\n dims.push(buf.readInt32BE(offset));\n offset += 4;\n // skip lower bound\n offset += 4;\n }\n\n const elemDecoder = makeElementDecoder(elemOid);\n\n // Recursively build the nested array structure.\n function readDimension(dim: number): unknown[] {\n const size = dims[dim];\n const arr: unknown[] = [];\n for (let i = 0; i < size; i++) {\n if (dim < ndim - 1) {\n arr.push(readDimension(dim + 1));\n } else {\n // Leaf dimension — read element.\n const elemLen = buf.readInt32BE(offset);\n offset += 4;\n if (elemLen === -1) {\n arr.push(null);\n } else {\n arr.push(elemDecoder(buf.subarray(offset, offset + elemLen)));\n offset += elemLen;\n }\n }\n }\n return arr;\n }\n\n const result = readDimension(0);\n return stringify(result);\n}\n\n/**\n * Creates a decoder for array elements. Array elements use the same\n * binary encoding as scalar columns, but we need to map the element\n * OID to the right decoder. Returns JS values (not LiteValueType)\n * since the result will be JSON.stringify'd.\n */\nfunction makeElementDecoder(elemOid: number): (buf: Buffer) => unknown {\n switch (elemOid) {\n case BOOL:\n return buf => (buf[0] ? true : false);\n case INT2:\n return buf => buf.readInt16BE(0);\n case INT4:\n return buf => buf.readInt32BE(0);\n case INT8:\n return buf => {\n const val = buf.readBigInt64BE(0);\n // Use number if it fits safely, otherwise bigint for JSON.\n return val >= Number.MIN_SAFE_INTEGER && val <= Number.MAX_SAFE_INTEGER\n ? Number(val)\n : val;\n };\n case FLOAT4:\n return buf => buf.readFloatBE(0);\n case FLOAT8:\n return buf => buf.readDoubleBE(0);\n case TEXT:\n case VARCHAR:\n case BPCHAR:\n case CHAR:\n return buf => buf.toString('utf8');\n case UUID:\n return buf => decodeUUID(buf);\n case JSON_OID:\n return buf => JSON.parse(buf.toString('utf8'));\n case JSONB:\n return buf => JSON.parse(buf.toString('utf8', 1));\n case TIMESTAMP:\n case TIMESTAMPTZ:\n return buf => decodeTimestamp(buf);\n case DATE:\n return buf => decodeDate(buf);\n case TIME:\n return buf => decodeTime(buf);\n case TIMETZ:\n return buf => decodeTimeTZ(buf);\n case NUMERIC:\n return buf => decodeNumeric(buf);\n default:\n return buf => buf.toString('utf8');\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"pg-copy-binary.js","names":["#append","#headerParsed","#tryParseHeader","#fieldsRemaining","#remaining","#buffer","#offset","#compact"],"sources":["../../../../../zero-cache/src/db/pg-copy-binary.ts"],"sourcesContent":["import {stringify} from '../../../shared/src/bigint-json.ts';\nimport type {LiteValueType} from '../types/lite.ts';\nimport {\n BOOL,\n BPCHAR,\n BYTEA,\n CHAR,\n DATE,\n FLOAT4,\n FLOAT8,\n INT2,\n INT4,\n INT8,\n JSONB,\n NUMERIC,\n TEXT,\n TIME,\n TIMESTAMP,\n TIMESTAMPTZ,\n TIMETZ,\n UUID,\n VARCHAR,\n} from '../types/pg-types.ts';\nimport {JSON as JSON_OID} from '../types/pg-types.ts';\nimport * as PostgresTypeClass from './postgres-type-class-enum.ts';\nimport type {ColumnSpec} from './specs.ts';\n\n// PostgreSQL COPY binary format signature: \"PGCOPY\\n\\xff\\r\\n\\0\"\nconst PGCOPY_SIGNATURE = Buffer.from([\n 0x50, 0x47, 0x43, 0x4f, 0x50, 0x59, 0x0a, 0xff, 0x0d, 0x0a, 0x00,\n]);\n\nconst HEADER_MIN_SIZE = 11 + 4 + 4; // signature + flags + extension length\n\n// PostgreSQL epoch is 2000-01-01T00:00:00Z.\n// Offset from Unix epoch (1970-01-01) in milliseconds.\nconst PG_EPOCH_UNIX_MILLIS = 946_684_800_000;\n\n// Days from Unix epoch (1970-01-01) to PG epoch (2000-01-01).\nconst PG_EPOCH_UNIX_DAYS = 10_957;\n\nconst MS_PER_DAY = 86_400_000;\n\n// Sentinel values for infinity in PG binary format (as hi/lo int32 pairs).\nconst PG_TIMESTAMP_INF_HI = 0x7fffffff;\nconst PG_TIMESTAMP_INF_LO = 0xffffffff;\nconst PG_TIMESTAMP_NEG_INF_HI = -0x80000000; // readInt32BE of 0x80000000\nconst PG_TIMESTAMP_NEG_INF_LO = 0;\nconst PG_DATE_INFINITY = 0x7fffffff;\nconst PG_DATE_NEG_INFINITY = -0x80000000;\n\n/**\n * Streaming parser for PostgreSQL `COPY ... TO STDOUT WITH (FORMAT binary)`.\n *\n * Analogous to {@link import('./pg-copy.ts').TsvParser} but for binary format.\n * Yields `Buffer | null` per field (null = SQL NULL).\n *\n * The caller tracks column position the same way as with TsvParser.\n */\nexport class BinaryCopyParser {\n #buffer: Buffer = Buffer.alloc(0);\n #offset = 0;\n #headerParsed = false;\n #fieldsRemaining = 0; // fields left in current tuple (0 = need new tuple header)\n\n *parse(chunk: Buffer): Iterable<Buffer | null> {\n this.#append(chunk);\n\n if (!this.#headerParsed) {\n if (!this.#tryParseHeader()) {\n return;\n }\n }\n\n for (;;) {\n // If we're at the start of a tuple, read the field count.\n if (this.#fieldsRemaining === 0) {\n if (this.#remaining() < 2) {\n break;\n }\n const fieldCount = this.#buffer.readInt16BE(this.#offset);\n if (fieldCount === -1) {\n // Trailer marker — end of data.\n break;\n }\n this.#offset += 2;\n this.#fieldsRemaining = fieldCount;\n }\n\n // Parse fields within the current tuple.\n while (this.#fieldsRemaining > 0) {\n if (this.#remaining() < 4) {\n // Not enough data for field length — wait for next chunk.\n this.#compact();\n return;\n }\n const fieldLen = this.#buffer.readInt32BE(this.#offset);\n this.#offset += 4;\n\n if (fieldLen === -1) {\n // NULL field.\n yield null;\n } else {\n if (this.#remaining() < fieldLen) {\n // Not enough data for field value — rewind past the length\n // we just read and wait for more data.\n this.#offset -= 4;\n this.#compact();\n return;\n }\n yield this.#buffer.subarray(this.#offset, this.#offset + fieldLen);\n this.#offset += fieldLen;\n }\n this.#fieldsRemaining--;\n }\n }\n\n this.#compact();\n }\n\n #remaining(): number {\n return this.#buffer.length - this.#offset;\n }\n\n #append(chunk: Buffer): void {\n if (this.#buffer.length === this.#offset) {\n // Fully consumed — replace.\n this.#buffer = chunk;\n this.#offset = 0;\n } else {\n // Concatenate unconsumed remainder with new chunk.\n this.#buffer = Buffer.concat([\n this.#buffer.subarray(this.#offset),\n chunk,\n ]);\n this.#offset = 0;\n }\n }\n\n #compact(): void {\n if (this.#offset > 0) {\n this.#buffer = this.#buffer.subarray(this.#offset);\n this.#offset = 0;\n }\n }\n\n #tryParseHeader(): boolean {\n if (this.#remaining() < HEADER_MIN_SIZE) {\n return false;\n }\n\n // Validate signature.\n for (let i = 0; i < PGCOPY_SIGNATURE.length; i++) {\n if (this.#buffer[this.#offset + i] !== PGCOPY_SIGNATURE[i]) {\n throw new Error('Invalid PGCOPY binary signature');\n }\n }\n this.#offset += 11;\n\n // Flags (int32) — currently only bit 16 (has OID column) is defined.\n // We don't use OID columns, so just skip.\n const flags = this.#buffer.readInt32BE(this.#offset);\n this.#offset += 4;\n if (flags !== 0) {\n throw new Error(`Unsupported PGCOPY flags: ${flags}`);\n }\n\n // Extension area length (int32).\n const extensionLen = this.#buffer.readInt32BE(this.#offset);\n this.#offset += 4;\n\n // Skip extension data if present.\n if (extensionLen > 0) {\n if (this.#remaining() < extensionLen) {\n // Rewind and wait for more data.\n this.#offset -= HEADER_MIN_SIZE;\n return false;\n }\n this.#offset += extensionLen;\n }\n\n this.#headerParsed = true;\n return true;\n }\n}\n\n// ---- Binary Type Decoders ----\n\nexport type BinaryDecoder = (buf: Buffer) => LiteValueType;\n\ntype BinaryColumnSpec = Pick<\n ColumnSpec,\n 'dataType' | 'pgTypeClass' | 'elemPgTypeClass'\n> & {typeOID: number};\n\nconst KNOWN_BINARY_OIDS = new Set([\n BOOL,\n INT2,\n INT4,\n INT8,\n FLOAT4,\n FLOAT8,\n TEXT,\n VARCHAR,\n BPCHAR,\n CHAR,\n UUID,\n BYTEA,\n JSON_OID,\n JSONB,\n TIMESTAMP,\n TIMESTAMPTZ,\n DATE,\n TIME,\n TIMETZ,\n NUMERIC,\n]);\n\n/**\n * Returns true if the column's binary format is known and can be decoded\n * natively. For columns where this returns false, the COPY SELECT should\n * cast the column to `::text` so PG sends the text representation inside\n * the binary frame.\n */\nexport function hasBinaryDecoder(spec: BinaryColumnSpec): boolean {\n if (spec.elemPgTypeClass !== null && spec.elemPgTypeClass !== undefined) {\n return true; // Array types\n }\n if (spec.pgTypeClass === PostgresTypeClass.Enum) {\n return true; // Enums are sent as UTF-8 text in binary format\n }\n return KNOWN_BINARY_OIDS.has(spec.typeOID);\n}\n\n/** Decoder for columns cast to `::text` in the COPY SELECT. */\nexport const textCastDecoder: BinaryDecoder = buf => buf.toString('utf8');\n\n/**\n * Creates a specialized binary decoder for the given column spec.\n * The returned function converts a raw COPY binary field `Buffer`\n * directly to a `LiteValueType`, bypassing text parsing entirely.\n *\n * Only call this for columns where {@link hasBinaryDecoder} returns true.\n * For other columns, cast to `::text` in the SELECT and use\n * {@link textCastDecoder}.\n */\nexport function makeBinaryDecoder(spec: BinaryColumnSpec): BinaryDecoder {\n const {typeOID, pgTypeClass, elemPgTypeClass} = spec;\n\n // Array types: elemPgTypeClass is non-null for arrays.\n if (elemPgTypeClass !== null && elemPgTypeClass !== undefined) {\n return buf => decodeArray(buf);\n }\n\n // Enum types: binary representation is UTF-8 text.\n if (pgTypeClass === PostgresTypeClass.Enum) {\n return buf => buf.toString('utf8');\n }\n\n switch (typeOID) {\n case BOOL:\n return buf => (buf[0] ? 1 : 0);\n case INT2:\n return buf => buf.readInt16BE(0);\n case INT4:\n return buf => buf.readInt32BE(0);\n case INT8:\n return buf => buf.readBigInt64BE(0);\n case FLOAT4:\n return buf => buf.readFloatBE(0);\n case FLOAT8:\n return buf => buf.readDoubleBE(0);\n case TEXT:\n case VARCHAR:\n case BPCHAR:\n case CHAR:\n return buf => buf.toString('utf8');\n case UUID:\n return buf => decodeUUID(buf);\n case BYTEA:\n return buf => Uint8Array.prototype.slice.call(buf) as Uint8Array;\n case JSON_OID:\n return buf => buf.toString('utf8');\n case JSONB:\n // JSONB binary format has a 1-byte version prefix (currently 0x01).\n return buf => buf.toString('utf8', 1);\n case TIMESTAMP:\n case TIMESTAMPTZ:\n return buf => decodeTimestamp(buf);\n case DATE:\n return buf => decodeDate(buf);\n case TIME:\n return buf => decodeTime(buf);\n case TIMETZ:\n return buf => decodeTimeTZ(buf);\n case NUMERIC:\n return buf => decodeNumeric(buf);\n default:\n throw new Error(\n `No binary decoder for type OID ${typeOID}. ` +\n `Use hasBinaryDecoder() to check before calling makeBinaryDecoder().`,\n );\n }\n}\n\n// ---- Individual Decoders (exported for testing) ----\n\n/**\n * UUID: 16 bytes → \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"\n */\nexport function decodeUUID(buf: Buffer): string {\n const hex = buf.toString('hex');\n return (\n hex.substring(0, 8) +\n '-' +\n hex.substring(8, 12) +\n '-' +\n hex.substring(12, 16) +\n '-' +\n hex.substring(16, 20) +\n '-' +\n hex.substring(20, 32)\n );\n}\n\n/**\n * TIMESTAMP / TIMESTAMPTZ: int64 microseconds since PG epoch (2000-01-01 UTC)\n * → floating-point milliseconds since Unix epoch.\n *\n * Matches the output of `timestampToFpMillis()` in `types/pg.ts`.\n *\n * Uses Number arithmetic (avoiding BigInt) for speed. The microsecond value\n * fits safely in a Number for all practical dates (up to ~year 285,000).\n */\nexport function decodeTimestamp(buf: Buffer): number {\n const hi = buf.readInt32BE(0);\n const lo = buf.readUInt32BE(4);\n if (hi === PG_TIMESTAMP_INF_HI && lo === PG_TIMESTAMP_INF_LO) return Infinity;\n if (hi === PG_TIMESTAMP_NEG_INF_HI && lo === PG_TIMESTAMP_NEG_INF_LO) {\n return -Infinity;\n }\n const microseconds = hi * 0x100000000 + lo;\n return microseconds / 1000 + PG_EPOCH_UNIX_MILLIS;\n}\n\n/**\n * DATE: int32 days since PG epoch (2000-01-01) → millis since Unix epoch at\n * UTC midnight. Matches `dateToUTCMidnight()` in `types/pg.ts`.\n */\nexport function decodeDate(buf: Buffer): number {\n const pgDays = buf.readInt32BE(0);\n if (pgDays === PG_DATE_INFINITY) return Infinity;\n if (pgDays === PG_DATE_NEG_INFINITY) return -Infinity;\n return (pgDays + PG_EPOCH_UNIX_DAYS) * MS_PER_DAY;\n}\n\n/**\n * TIME: int64 microseconds since midnight → milliseconds since midnight.\n * Matches `postgresTimeToMilliseconds()` in `types/pg.ts`.\n *\n * Max value is 86,400,000,000 (~8.6e10), well within Number.MAX_SAFE_INTEGER.\n */\nexport function decodeTime(buf: Buffer): number {\n const hi = buf.readInt32BE(0);\n const lo = buf.readUInt32BE(4);\n const micros = hi * 0x100000000 + lo;\n return Math.trunc(micros / 1000);\n}\n\n/**\n * TIMETZ: int64 microseconds since midnight + int32 timezone offset in seconds.\n * PG stores the offset with inverted sign from ISO (POSIX convention):\n * positive = west of UTC, negative = east of UTC.\n * UTC = local_time + pg_offset.\n * → UTC milliseconds since midnight.\n *\n * Max value ~1.3e11 microseconds, well within Number.MAX_SAFE_INTEGER.\n */\nexport function decodeTimeTZ(buf: Buffer): number {\n const hi = buf.readInt32BE(0);\n const lo = buf.readUInt32BE(4);\n const localMicros = hi * 0x100000000 + lo;\n const tzOffsetSeconds = buf.readInt32BE(8);\n const utcMicros = localMicros + tzOffsetSeconds * 1_000_000;\n let ms = Math.trunc(utcMicros / 1000);\n // Normalize to [0, MS_PER_DAY).\n if (ms < 0 || ms >= MS_PER_DAY) {\n ms = ((ms % MS_PER_DAY) + MS_PER_DAY) % MS_PER_DAY;\n }\n return ms;\n}\n\n// NUMERIC binary format constants.\nconst NUMERIC_NEG = 0x4000;\nconst NUMERIC_NAN = 0xc000;\nconst NUMERIC_PINF = 0xd000;\nconst NUMERIC_NINF = 0xf000;\nconst NBASE = 10_000;\n\n/**\n * NUMERIC: variable-length binary format.\n * Header: {ndigits: int16, weight: int16, sign: int16, dscale: int16}\n * Followed by ndigits x int16 base-10000 digits.\n *\n * Converts to a JS `number` (matching the text path's `Number(x)` behavior).\n */\nexport function decodeNumeric(buf: Buffer): number {\n const ndigits = buf.readInt16BE(0);\n const weight = buf.readInt16BE(2);\n const sign = buf.readUInt16BE(4);\n // const dscale = buf.readInt16BE(6); // display scale, not needed for value\n\n if (sign === NUMERIC_NAN) {\n return NaN;\n }\n if (sign === NUMERIC_PINF) {\n return Infinity;\n }\n if (sign === NUMERIC_NINF) {\n return -Infinity;\n }\n if (ndigits === 0) {\n return 0;\n }\n\n // Accumulate base-10000 digits into an integer, then do a single\n // division at the end. Repeated `scale /= NBASE` accumulates\n // floating-point error (e.g. 9900 * 0.0001 = 0.9900000000000001).\n // A single division lets IEEE 754 round to the nearest double,\n // matching the text path's `Number(\"0.99\")` behavior.\n //\n // For numerics with many digits (ndigits > 3), intVal can exceed\n // MAX_SAFE_INTEGER. In that case, fall back to building a string\n // and using Number() to match the text path exactly.\n if (ndigits > 3) {\n return decodeNumericViaString(buf, ndigits, weight, sign);\n }\n\n let intVal = 0;\n for (let i = 0; i < ndigits; i++) {\n intVal = intVal * NBASE + buf.readInt16BE(8 + i * 2);\n }\n\n // weight indicates the power-of-NBASE of the first digit.\n // shift is how many base-10000 positions to divide by.\n const shift = ndigits - weight - 1;\n let result;\n if (shift > 0) {\n result = intVal / NBASE ** shift;\n } else if (shift < 0) {\n result = intVal * NBASE ** -shift;\n } else {\n result = intVal;\n }\n return sign === NUMERIC_NEG ? -result : result;\n}\n\n/**\n * Fallback for numerics with many base-10000 digits where accumulating\n * into an integer would exceed MAX_SAFE_INTEGER. Builds the decimal\n * string and uses Number() to match the text path exactly.\n */\nfunction decodeNumericViaString(\n buf: Buffer,\n ndigits: number,\n weight: number,\n sign: number,\n): number {\n // Number of base-10000 digit groups before the decimal point.\n const intGroups = weight + 1;\n\n let str = '';\n for (let i = 0; i < ndigits; i++) {\n const digit = buf.readInt16BE(8 + i * 2);\n if (i === intGroups) {\n str = str || '0';\n str += '.';\n }\n str += i === 0 ? String(digit) : String(digit).padStart(4, '0');\n }\n\n // Append trailing zero groups if the integer part extends beyond ndigits.\n if (intGroups > ndigits) {\n str += '0'.repeat((intGroups - ndigits) * 4);\n }\n\n return Number((sign === NUMERIC_NEG ? '-' : '') + str);\n}\n\n/**\n * Array: binary format.\n *\n * Header:\n * int32 ndim — number of dimensions (0 for empty array)\n * int32 flags — 0 or 1 (has-nulls)\n * int32 elem_oid — OID of element type\n * Per dimension:\n * int32 dim_size — number of elements in this dimension\n * int32 dim_lb — lower bound (usually 1)\n *\n * Then for each element (in row-major order):\n * int32 length — -1 for NULL, otherwise byte length\n * bytes — element data\n *\n * Result is JSON.stringify'd for storage in SQLite (matching text path behavior).\n */\nexport function decodeArray(buf: Buffer): string {\n let offset = 0;\n\n const ndim = buf.readInt32BE(offset);\n offset += 4;\n // skip flags (has-nulls)\n offset += 4;\n const elemOid = buf.readInt32BE(offset);\n offset += 4;\n\n if (ndim === 0) {\n return '[]';\n }\n\n // Read dimension sizes.\n const dims: number[] = [];\n for (let d = 0; d < ndim; d++) {\n dims.push(buf.readInt32BE(offset));\n offset += 4;\n // skip lower bound\n offset += 4;\n }\n\n const elemDecoder = makeElementDecoder(elemOid);\n\n // Recursively build the nested array structure.\n function readDimension(dim: number): unknown[] {\n const size = dims[dim];\n const arr: unknown[] = [];\n for (let i = 0; i < size; i++) {\n if (dim < ndim - 1) {\n arr.push(readDimension(dim + 1));\n } else {\n // Leaf dimension — read element.\n const elemLen = buf.readInt32BE(offset);\n offset += 4;\n if (elemLen === -1) {\n arr.push(null);\n } else {\n arr.push(elemDecoder(buf.subarray(offset, offset + elemLen)));\n offset += elemLen;\n }\n }\n }\n return arr;\n }\n\n const result = readDimension(0);\n return stringify(result);\n}\n\n/**\n * Creates a decoder for array elements. Array elements use the same\n * binary encoding as scalar columns, but we need to map the element\n * OID to the right decoder. Returns JS values (not LiteValueType)\n * since the result will be JSON.stringify'd.\n */\nfunction makeElementDecoder(elemOid: number): (buf: Buffer) => unknown {\n switch (elemOid) {\n case BOOL:\n return buf => (buf[0] ? true : false);\n case INT2:\n return buf => buf.readInt16BE(0);\n case INT4:\n return buf => buf.readInt32BE(0);\n case INT8:\n return buf => {\n const val = buf.readBigInt64BE(0);\n // Use number if it fits safely, otherwise bigint for JSON.\n return val >= Number.MIN_SAFE_INTEGER && val <= Number.MAX_SAFE_INTEGER\n ? Number(val)\n : val;\n };\n case FLOAT4:\n return buf => buf.readFloatBE(0);\n case FLOAT8:\n return buf => buf.readDoubleBE(0);\n case TEXT:\n case VARCHAR:\n case BPCHAR:\n case CHAR:\n return buf => buf.toString('utf8');\n case UUID:\n return buf => decodeUUID(buf);\n case JSON_OID:\n return buf => JSON.parse(buf.toString('utf8'));\n case JSONB:\n return buf => JSON.parse(buf.toString('utf8', 1));\n case TIMESTAMP:\n case TIMESTAMPTZ:\n return buf => decodeTimestamp(buf);\n case DATE:\n return buf => decodeDate(buf);\n case TIME:\n return buf => decodeTime(buf);\n case TIMETZ:\n return buf => decodeTimeTZ(buf);\n case NUMERIC:\n return buf => decodeNumeric(buf);\n default:\n return buf => buf.toString('utf8');\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"pg-copy.js","names":["#parser","#escaped","#currVal"],"sources":["../../../../../zero-cache/src/db/pg-copy.ts"],"sourcesContent":["import {Transform} from 'node:stream';\n\n/**\n * A stream Transform that parses a Postgres `COPY ... TO` text stream into\n * individual text values. The special {@link NULL_BYTE} string is used to\n * indicate a `null` value (as the `null` value itself indicates the end of\n * the stream and cannot be pushed as an element).\n *\n * Note that the transform assumes that the next step of the pipeline\n * understands the cardinality of values per row and does not push any\n * special value when reaching the end of a row.\n */\nexport class TextTransform extends Transform {\n readonly #parser = new TsvParser();\n\n constructor() {\n super({objectMode: true});\n }\n\n _transform(\n chunk: Buffer,\n _encoding: BufferEncoding,\n callback: (e?: Error) => void,\n ) {\n try {\n for (const value of this.#parser.parse(chunk)) {\n this.push(value === null ? NULL_BYTE : value);\n }\n callback();\n } catch (e) {\n callback(e instanceof Error ? e : new Error(String(e)));\n }\n }\n}\n\n/**\n * Parsing a stream of tab-separated values from a Postgres `COPY` command.\n * The object keeps state and should be reused across chunks of a stream in\n * order to properly recognize that values are split across chunks.\n *\n * Note that `null` values are yielded as `null`. This object does not return\n * the {@link NULL_BYTE} string.\n */\nexport class TsvParser {\n #currVal: string = '';\n #escaped = false;\n\n *parse(chunk: Buffer): Iterable<string | null> {\n let l = 0;\n let r = 0;\n\n for (; r < chunk.length; r++) {\n const ch = chunk[r];\n if (this.#escaped) {\n const escapedChar = ESCAPED_CHARACTERS[ch];\n if (escapedChar === undefined) {\n throw new Error(\n `Unexpected escape character \\\\${String.fromCharCode(ch)}`,\n );\n }\n this.#currVal += escapedChar;\n l = r + 1;\n this.#escaped = false;\n continue;\n }\n switch (ch) {\n case 0x5c: // '\\'\n // flush segment\n l < r && (this.#currVal += chunk.toString('utf8', l, r));\n l = r + 1;\n this.#escaped = true;\n break;\n\n case 0x09: // '\\t'\n case 0x0a: // '\\n'\n // flush segment\n l < r && (this.#currVal += chunk.toString('utf8', l, r));\n l = r + 1;\n\n // Value is done in both cases.\n yield this.#currVal === NULL_BYTE ? null : this.#currVal;\n this.#currVal = '';\n break;\n }\n }\n // flush segment\n l < r && (this.#currVal += chunk.toString('utf8', l, r));\n }\n}\n\n// The lone NULL byte signifies that the column value is `null`.\n// (Postgres does not permit NULL bytes in TEXT values).\n//\n// Note that although NULL bytes can appear in JSON strings,\n// those will always be represented within double-quotes,\n// and thus never as a lone NULL byte.\nexport const NULL_BYTE = '\\u0000';\n\n// escaped characters used in https://www.postgresql.org/docs/current/sql-copy.html\nconst ESCAPED_CHARACTERS: Record<number, string | undefined> = {\n 0x4e: NULL_BYTE, // \\N signifies the NULL character.\n 0x5c: '\\\\',\n 0x62: '\\b',\n 0x66: '\\f',\n 0x6e: '\\n',\n 0x72: '\\r',\n 0x74: '\\t',\n 0x76: '\\v',\n} as const;\n"],"mappings":";;;;;;;;;;AA2CA,IAAa,YAAb,MAAuB;CACrB,WAAmB;CACnB,WAAW;CAEX,CAAC,MAAM,OAAwC;EAC7C,IAAI,IAAI;EACR,IAAI,IAAI;EAER,OAAO,IAAI,MAAM,QAAQ,KAAK;GAC5B,MAAM,KAAK,MAAM;GACjB,IAAI,KAAKC,UAAU;IACjB,MAAM,cAAc,mBAAmB;IACvC,IAAI,gBAAgB,KAAA,GAClB,MAAM,IAAI,MACR,iCAAiC,OAAO,aAAa,EAAE,GACzD;IAEF,KAAKC,YAAY;IACjB,IAAI,IAAI;IACR,KAAKD,WAAW;IAChB;GACF;GACA,QAAQ,IAAR;IACE,KAAK;KAEH,IAAI,MAAM,KAAKC,YAAY,MAAM,SAAS,QAAQ,GAAG,CAAC;KACtD,IAAI,IAAI;KACR,KAAKD,WAAW;KAChB;IAEF,KAAK;IACL,KAAK;KAEH,IAAI,MAAM,KAAKC,YAAY,MAAM,SAAS,QAAQ,GAAG,CAAC;KACtD,IAAI,IAAI;KAGR,MAAM,KAAKA,aAAAA,OAAyB,OAAO,KAAKA;KAChD,KAAKA,WAAW;KAChB;GACJ;EACF;EAEA,IAAI,MAAM,KAAKA,YAAY,MAAM,SAAS,QAAQ,GAAG,CAAC;CACxD;AACF;AAWA,IAAM,qBAAyD;CAC7D,IAAA;CACA,IAAM;CACN,IAAM;CACN,KAAM;CACN,KAAM;CACN,KAAM;CACN,KAAM;CACN,KAAM;AACR"}
1
+ {"version":3,"file":"pg-copy.js","names":["#parser","#escaped","#currVal"],"sources":["../../../../../zero-cache/src/db/pg-copy.ts"],"sourcesContent":["import {Transform} from 'node:stream';\n\n/**\n * A stream Transform that parses a Postgres `COPY ... TO` text stream into\n * individual text values. The special {@link NULL_BYTE} string is used to\n * indicate a `null` value (as the `null` value itself indicates the end of\n * the stream and cannot be pushed as an element).\n *\n * Note that the transform assumes that the next step of the pipeline\n * understands the cardinality of values per row and does not push any\n * special value when reaching the end of a row.\n */\nexport class TextTransform extends Transform {\n readonly #parser = new TsvParser();\n\n constructor() {\n super({objectMode: true});\n }\n\n _transform(\n chunk: Buffer,\n _encoding: BufferEncoding,\n callback: (e?: Error) => void,\n ) {\n try {\n for (const value of this.#parser.parse(chunk)) {\n this.push(value === null ? NULL_BYTE : value);\n }\n callback();\n } catch (e) {\n callback(e instanceof Error ? e : new Error(String(e)));\n }\n }\n}\n\n/**\n * Parsing a stream of tab-separated values from a Postgres `COPY` command.\n * The object keeps state and should be reused across chunks of a stream in\n * order to properly recognize that values are split across chunks.\n *\n * Note that `null` values are yielded as `null`. This object does not return\n * the {@link NULL_BYTE} string.\n */\nexport class TsvParser {\n #currVal: string = '';\n #escaped = false;\n\n *parse(chunk: Buffer): Iterable<string | null> {\n let l = 0;\n let r = 0;\n\n for (; r < chunk.length; r++) {\n const ch = chunk[r];\n if (this.#escaped) {\n const escapedChar = ESCAPED_CHARACTERS[ch];\n if (escapedChar === undefined) {\n throw new Error(\n `Unexpected escape character \\\\${String.fromCharCode(ch)}`,\n );\n }\n this.#currVal += escapedChar;\n l = r + 1;\n this.#escaped = false;\n continue;\n }\n switch (ch) {\n case 0x5c: // '\\'\n // flush segment\n l < r && (this.#currVal += chunk.toString('utf8', l, r));\n l = r + 1;\n this.#escaped = true;\n break;\n\n case 0x09: // '\\t'\n case 0x0a: // '\\n'\n // flush segment\n l < r && (this.#currVal += chunk.toString('utf8', l, r));\n l = r + 1;\n\n // Value is done in both cases.\n yield this.#currVal === NULL_BYTE ? null : this.#currVal;\n this.#currVal = '';\n break;\n }\n }\n // flush segment\n l < r && (this.#currVal += chunk.toString('utf8', l, r));\n }\n}\n\n// The lone NULL byte signifies that the column value is `null`.\n// (Postgres does not permit NULL bytes in TEXT values).\n//\n// Note that although NULL bytes can appear in JSON strings,\n// those will always be represented within double-quotes,\n// and thus never as a lone NULL byte.\nexport const NULL_BYTE = '\\u0000';\n\n// escaped characters used in https://www.postgresql.org/docs/current/sql-copy.html\nconst ESCAPED_CHARACTERS: Record<number, string | undefined> = {\n 0x4e: NULL_BYTE, // \\N signifies the NULL character.\n 0x5c: '\\\\',\n 0x62: '\\b',\n 0x66: '\\f',\n 0x6e: '\\n',\n 0x72: '\\r',\n 0x74: '\\t',\n 0x76: '\\v',\n} as const;\n"],"mappings":";;;;;;;;;;AA2CA,IAAa,YAAb,MAAuB;CACrB,WAAmB;CACnB,WAAW;CAEX,CAAC,MAAM,OAAwC;EAC7C,IAAI,IAAI;EACR,IAAI,IAAI;AAER,SAAO,IAAI,MAAM,QAAQ,KAAK;GAC5B,MAAM,KAAK,MAAM;AACjB,OAAI,MAAA,SAAe;IACjB,MAAM,cAAc,mBAAmB;AACvC,QAAI,gBAAgB,KAAA,EAClB,OAAM,IAAI,MACR,iCAAiC,OAAO,aAAa,GAAG,GACzD;AAEH,UAAA,WAAiB;AACjB,QAAI,IAAI;AACR,UAAA,UAAgB;AAChB;;AAEF,WAAQ,IAAR;IACE,KAAK;AAEH,SAAI,MAAM,MAAA,WAAiB,MAAM,SAAS,QAAQ,GAAG,EAAE;AACvD,SAAI,IAAI;AACR,WAAA,UAAgB;AAChB;IAEF,KAAK;IACL,KAAK;AAEH,SAAI,MAAM,MAAA,WAAiB,MAAM,SAAS,QAAQ,GAAG,EAAE;AACvD,SAAI,IAAI;AAGR,WAAM,MAAA,YAAA,OAA8B,OAAO,MAAA;AAC3C,WAAA,UAAgB;AAChB;;;AAIN,MAAI,MAAM,MAAA,WAAiB,MAAM,SAAS,QAAQ,GAAG,EAAE;;;AAa3D,IAAM,qBAAyD;CAC7D,IAAA;CACA,IAAM;CACN,IAAM;CACN,KAAM;CACN,KAAM;CACN,KAAM;CACN,KAAM;CACN,KAAM;CACP"}
@@ -1 +1 @@
1
- {"version":3,"file":"pg-to-lite.js","names":[],"sources":["../../../../../zero-cache/src/db/pg-to-lite.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {ZERO_VERSION_COLUMN_NAME} from '../services/replicator/schema/constants.ts';\nimport {\n liteTypeString,\n liteTypeToZqlValueType,\n upstreamDataType,\n type LiteTypeString,\n} from '../types/lite.ts';\nimport {liteTableName} from '../types/names.ts';\nimport * as PostgresTypeClass from './postgres-type-class-enum.ts';\nimport {\n type ColumnSpec,\n type IndexSpec,\n type LiteIndexSpec,\n type LiteTableSpec,\n type TableSpec,\n} from './specs.ts';\n\n/**\n * Determines if a PostgreSQL column is an enum type.\n * This checks both the element type class (for arrays of enums) and the main type class.\n */\nexport function isEnumColumn(\n spec: Pick<ColumnSpec, 'pgTypeClass' | 'elemPgTypeClass'>,\n): boolean {\n return (spec.elemPgTypeClass ?? spec.pgTypeClass) === PostgresTypeClass.Enum;\n}\n\n/**\n * Determines if a PostgreSQL column is an array type.\n * In PostgreSQL's system, array columns have a non-null elemPgTypeClass.\n */\nexport function isArrayColumn(\n spec: Pick<ColumnSpec, 'elemPgTypeClass'>,\n): boolean {\n return spec.elemPgTypeClass !== null && spec.elemPgTypeClass !== undefined;\n}\n\nfunction zeroVersionColumnSpec(defaultVersion: string | undefined): ColumnSpec {\n return {\n pos: Number.MAX_SAFE_INTEGER, // i.e. last\n characterMaximumLength: null,\n dataType: 'text',\n notNull: false,\n dflt: !defaultVersion ? null : `'${defaultVersion}'`,\n elemPgTypeClass: null,\n };\n}\n\nexport function warnIfDataTypeSupported(\n lc: LogContext,\n liteTypeString: LiteTypeString,\n table: string,\n column: string,\n) {\n if (liteTypeToZqlValueType(liteTypeString) === undefined) {\n lc.warn?.(\n `\\n\\nWARNING: zero does not yet support the \"${upstreamDataType(\n liteTypeString,\n )}\" data type.\\n` +\n `The \"${table}\".\"${column}\" column will not be synced to clients.\\n\\n`,\n );\n }\n}\n\n// Numeric literals: integers and decimals, optionally negative\nconst NUMERIC_LITERAL_REGEX = /^-?\\d+(\\.\\d+)?$/;\n\n// Boolean literals (PG emits lowercase)\nconst BOOLEAN_LITERAL_REGEX = /^(true|false)$/;\n\n// Quoted string with type cast to a simple scalar type: 'value'::typename\n// For strings and certain incarnations of primitives (e.g. integers greater\n// than 2^31-1, Postgres' nodeToString() represents the values as type-casted\n// 'string' values, e.g. `'2147483648'::bigint`, `'foo'::text`.\n// Only matches simple type names (word characters) - array types like\n// `::text[]` won't match and will trigger backfill.\nconst QUOTED_STRING_WITH_CAST_REGEX = /^('.*')::(\\w+)$/;\n\n// Empty array constructor syntax: ARRAY[]::text[], ARRAY[]::integer[], etc.\n// Maps to '[]' (JSON empty array) in SQLite.\nconst EMPTY_ARRAY_CONSTRUCTOR_REGEX = /^ARRAY\\s*\\[\\s*\\]::\\w+\\[\\]$/i;\n\n// Empty array literal syntax: '{}'::text[], '{}'::integer[], etc.\n// Maps to '[]' (JSON empty array) in SQLite.\nconst EMPTY_ARRAY_LITERAL_REGEX = /^'\\{\\}'::\\w+\\[\\]$/;\n\n// Conservative allowlist approach for SQLite ADD COLUMN defaults.\n// We only allow patterns we know are safe. Everything else triggers\n// backfill from PostgreSQL, which correctly handles complex defaults.\n//\n// Note: We don't validate that the default value matches the column type\n// (e.g., that a numeric literal is used with a numeric column). PostgreSQL\n// already enforces this at schema definition time - you can't define\n// `ALTER TABLE foo ADD bar TEXT DEFAULT 123` in PG. So we trust that any\n// default we receive from the replication stream is type-compatible with\n// whatever we map the type to in SQLite.\n//\n// Example: `true`/`false` literals can only appear as defaults for boolean\n// columns in PG, so we don't need to check the column type before converting\n// to 1/0.\n//\n// See: https://www.sqlite.org/lang_altertable.html#altertabaddcol\n//\n// Exported for testing.\nexport function mapPostgresToLiteDefault(\n table: string,\n column: string,\n defaultExpression: string | null | undefined,\n): string | null {\n if (!defaultExpression) {\n return null;\n }\n\n // Numeric literals pass through unchanged\n if (NUMERIC_LITERAL_REGEX.test(defaultExpression)) {\n return defaultExpression;\n }\n\n // Boolean literals convert to SQLite's 1/0\n if (BOOLEAN_LITERAL_REGEX.test(defaultExpression)) {\n return defaultExpression === 'true' ? '1' : '0';\n }\n\n // Quoted strings with type casts: extract just the quoted part\n const match = QUOTED_STRING_WITH_CAST_REGEX.exec(defaultExpression);\n if (match) {\n return match[1];\n }\n\n // Empty arrays: ARRAY[]::type[] or '{}'::type[] → '[]'\n if (\n EMPTY_ARRAY_CONSTRUCTOR_REGEX.test(defaultExpression) ||\n EMPTY_ARRAY_LITERAL_REGEX.test(defaultExpression)\n ) {\n return \"'[]'\";\n }\n\n // Everything else triggers backfill\n throw new UnsupportedColumnDefaultError(\n `Unsupported default value for ${table}.${column}: ${defaultExpression}`,\n );\n}\n\nexport function mapPostgresToLiteColumn(\n table: string,\n column: {name: string; spec: ColumnSpec},\n ignoreDefault?: 'ignore-default',\n): ColumnSpec {\n const {pos, dataType, notNull, dflt, elemPgTypeClass = null} = column.spec;\n\n // PostgreSQL includes [] in dataType for array types (e.g., 'int4[]',\n // 'int4[][]'). liteTypeString() appends attributes:\n // \"varchar[]|NOT_NULL|TEXT_ARRAY\", \"my_enum[][]|TEXT_ENUM|TEXT_ARRAY\"\n const liteType = liteTypeString(\n dataType,\n notNull,\n isEnumColumn(column.spec),\n isArrayColumn(column.spec),\n );\n\n return {\n pos,\n dataType: liteType,\n characterMaximumLength: null,\n // Note: NOT NULL constraints are always ignored for SQLite (replica) tables.\n // 1. They are enforced by the replication stream.\n // 2. We need nullability for columns with defaults to support\n // write permissions on the \"proposed mutation\" state. Proposed\n // mutations are written to SQLite in a `BEGIN CONCURRENT` transaction in mutagen.\n // Permission policies are run against that state (to get their ruling) then the\n // transaction is rolled back.\n notNull: false,\n // Note: DEFAULT constraints are ignored when creating new tables, but are\n // necessary for adding columns to tables with existing rows.\n dflt:\n ignoreDefault === 'ignore-default'\n ? null\n : mapPostgresToLiteDefault(table, column.name, dflt),\n elemPgTypeClass,\n };\n}\n\nexport function mapPostgresToLite(\n t: TableSpec,\n defaultVersion?: string,\n): LiteTableSpec {\n // PRIMARY KEYS are not written to the replica. Instead, we rely\n // UNIQUE indexes, including those created for upstream PRIMARY KEYs.\n const {schema: _, primaryKey: _dropped, ...liteSpec} = t;\n const name = liteTableName(t);\n return {\n ...liteSpec,\n name,\n columns: {\n ...Object.fromEntries(\n Object.entries(t.columns).map(([col, spec]) => [\n col,\n // `ignore-default` for create table statements because\n // there are no rows to set the default for.\n mapPostgresToLiteColumn(name, {name: col, spec}, 'ignore-default'),\n ]),\n ),\n [ZERO_VERSION_COLUMN_NAME]: zeroVersionColumnSpec(defaultVersion),\n },\n };\n}\n\nexport function mapPostgresToLiteIndex(index: IndexSpec): LiteIndexSpec {\n const {schema, tableName, name, ...liteIndex} = index;\n return {\n tableName: liteTableName({schema, name: tableName}),\n name: liteTableName({schema, name}),\n ...liteIndex,\n };\n}\n\nexport class UnsupportedColumnDefaultError extends Error {\n readonly name = 'UnsupportedColumnDefaultError';\n}\n"],"mappings":";;;;;;;;;AAsBA,SAAgB,aACd,MACS;CACT,QAAQ,KAAK,mBAAmB,KAAK,iBAAiB;AACxD;;;;;AAMA,SAAgB,cACd,MACS;CACT,OAAO,KAAK,oBAAoB,QAAQ,KAAK,oBAAoB,KAAA;AACnE;AAEA,SAAS,sBAAsB,gBAAgD;CAC7E,OAAO;EACL,KAAK,OAAO;EACZ,wBAAwB;EACxB,UAAU;EACV,SAAS;EACT,MAAM,CAAC,iBAAiB,OAAO,IAAI,eAAe;EAClD,iBAAiB;CACnB;AACF;AAEA,SAAgB,wBACd,IACA,gBACA,OACA,QACA;CACA,IAAI,uBAAuB,cAAc,MAAM,KAAA,GAC7C,GAAG,OACD,+CAA+C,iBAC7C,cACF,EAAE,qBACQ,MAAM,KAAK,OAAO,4CAC9B;AAEJ;AAGA,IAAM,wBAAwB;AAG9B,IAAM,wBAAwB;AAQ9B,IAAM,gCAAgC;AAItC,IAAM,gCAAgC;AAItC,IAAM,4BAA4B;AAoBlC,SAAgB,yBACd,OACA,QACA,mBACe;CACf,IAAI,CAAC,mBACH,OAAO;CAIT,IAAI,sBAAsB,KAAK,iBAAiB,GAC9C,OAAO;CAIT,IAAI,sBAAsB,KAAK,iBAAiB,GAC9C,OAAO,sBAAsB,SAAS,MAAM;CAI9C,MAAM,QAAQ,8BAA8B,KAAK,iBAAiB;CAClE,IAAI,OACF,OAAO,MAAM;CAIf,IACE,8BAA8B,KAAK,iBAAiB,KACpD,0BAA0B,KAAK,iBAAiB,GAEhD,OAAO;CAIT,MAAM,IAAI,8BACR,iCAAiC,MAAM,GAAG,OAAO,IAAI,mBACvD;AACF;AAEA,SAAgB,wBACd,OACA,QACA,eACY;CACZ,MAAM,EAAC,KAAK,UAAU,SAAS,MAAM,kBAAkB,SAAQ,OAAO;CAYtE,OAAO;EACL;EACA,UATe,eACf,UACA,SACA,aAAa,OAAO,IAAI,GACxB,cAAc,OAAO,IAAI,CAKf;EACV,wBAAwB;EAQxB,SAAS;EAGT,MACE,kBAAkB,mBACd,OACA,yBAAyB,OAAO,OAAO,MAAM,IAAI;EACvD;CACF;AACF;AAEA,SAAgB,kBACd,GACA,gBACe;CAGf,MAAM,EAAC,QAAQ,GAAG,YAAY,UAAU,GAAG,aAAY;CACvD,MAAM,OAAO,cAAc,CAAC;CAC5B,OAAO;EACL,GAAG;EACH;EACA,SAAS;GACP,GAAG,OAAO,YACR,OAAO,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC,KAAK,UAAU,CAC7C,KAGA,wBAAwB,MAAM;IAAC,MAAM;IAAK;GAAI,GAAG,gBAAgB,CACnE,CAAC,CACH;IACC,2BAA2B,sBAAsB,cAAc;EAClE;CACF;AACF;AAEA,SAAgB,uBAAuB,OAAiC;CACtE,MAAM,EAAC,QAAQ,WAAW,MAAM,GAAG,cAAa;CAChD,OAAO;EACL,WAAW,cAAc;GAAC;GAAQ,MAAM;EAAS,CAAC;EAClD,MAAM,cAAc;GAAC;GAAQ;EAAI,CAAC;EAClC,GAAG;CACL;AACF;AAEA,IAAa,gCAAb,cAAmD,MAAM;CACvD,OAAgB;AAClB"}
1
+ {"version":3,"file":"pg-to-lite.js","names":[],"sources":["../../../../../zero-cache/src/db/pg-to-lite.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {ZERO_VERSION_COLUMN_NAME} from '../services/replicator/schema/constants.ts';\nimport {\n liteTypeString,\n liteTypeToZqlValueType,\n upstreamDataType,\n type LiteTypeString,\n} from '../types/lite.ts';\nimport {liteTableName} from '../types/names.ts';\nimport * as PostgresTypeClass from './postgres-type-class-enum.ts';\nimport {\n type ColumnSpec,\n type IndexSpec,\n type LiteIndexSpec,\n type LiteTableSpec,\n type TableSpec,\n} from './specs.ts';\n\n/**\n * Determines if a PostgreSQL column is an enum type.\n * This checks both the element type class (for arrays of enums) and the main type class.\n */\nexport function isEnumColumn(\n spec: Pick<ColumnSpec, 'pgTypeClass' | 'elemPgTypeClass'>,\n): boolean {\n return (spec.elemPgTypeClass ?? spec.pgTypeClass) === PostgresTypeClass.Enum;\n}\n\n/**\n * Determines if a PostgreSQL column is an array type.\n * In PostgreSQL's system, array columns have a non-null elemPgTypeClass.\n */\nexport function isArrayColumn(\n spec: Pick<ColumnSpec, 'elemPgTypeClass'>,\n): boolean {\n return spec.elemPgTypeClass !== null && spec.elemPgTypeClass !== undefined;\n}\n\nfunction zeroVersionColumnSpec(defaultVersion: string | undefined): ColumnSpec {\n return {\n pos: Number.MAX_SAFE_INTEGER, // i.e. last\n characterMaximumLength: null,\n dataType: 'text',\n notNull: false,\n dflt: !defaultVersion ? null : `'${defaultVersion}'`,\n elemPgTypeClass: null,\n };\n}\n\nexport function warnIfDataTypeSupported(\n lc: LogContext,\n liteTypeString: LiteTypeString,\n table: string,\n column: string,\n) {\n if (liteTypeToZqlValueType(liteTypeString) === undefined) {\n lc.warn?.(\n `\\n\\nWARNING: zero does not yet support the \"${upstreamDataType(\n liteTypeString,\n )}\" data type.\\n` +\n `The \"${table}\".\"${column}\" column will not be synced to clients.\\n\\n`,\n );\n }\n}\n\n// Numeric literals: integers and decimals, optionally negative\nconst NUMERIC_LITERAL_REGEX = /^-?\\d+(\\.\\d+)?$/;\n\n// Boolean literals (PG emits lowercase)\nconst BOOLEAN_LITERAL_REGEX = /^(true|false)$/;\n\n// Quoted string with type cast to a simple scalar type: 'value'::typename\n// For strings and certain incarnations of primitives (e.g. integers greater\n// than 2^31-1, Postgres' nodeToString() represents the values as type-casted\n// 'string' values, e.g. `'2147483648'::bigint`, `'foo'::text`.\n// Only matches simple type names (word characters) - array types like\n// `::text[]` won't match and will trigger backfill.\nconst QUOTED_STRING_WITH_CAST_REGEX = /^('.*')::(\\w+)$/;\n\n// Empty array constructor syntax: ARRAY[]::text[], ARRAY[]::integer[], etc.\n// Maps to '[]' (JSON empty array) in SQLite.\nconst EMPTY_ARRAY_CONSTRUCTOR_REGEX = /^ARRAY\\s*\\[\\s*\\]::\\w+\\[\\]$/i;\n\n// Empty array literal syntax: '{}'::text[], '{}'::integer[], etc.\n// Maps to '[]' (JSON empty array) in SQLite.\nconst EMPTY_ARRAY_LITERAL_REGEX = /^'\\{\\}'::\\w+\\[\\]$/;\n\n// Conservative allowlist approach for SQLite ADD COLUMN defaults.\n// We only allow patterns we know are safe. Everything else triggers\n// backfill from PostgreSQL, which correctly handles complex defaults.\n//\n// Note: We don't validate that the default value matches the column type\n// (e.g., that a numeric literal is used with a numeric column). PostgreSQL\n// already enforces this at schema definition time - you can't define\n// `ALTER TABLE foo ADD bar TEXT DEFAULT 123` in PG. So we trust that any\n// default we receive from the replication stream is type-compatible with\n// whatever we map the type to in SQLite.\n//\n// Example: `true`/`false` literals can only appear as defaults for boolean\n// columns in PG, so we don't need to check the column type before converting\n// to 1/0.\n//\n// See: https://www.sqlite.org/lang_altertable.html#altertabaddcol\n//\n// Exported for testing.\nexport function mapPostgresToLiteDefault(\n table: string,\n column: string,\n defaultExpression: string | null | undefined,\n): string | null {\n if (!defaultExpression) {\n return null;\n }\n\n // Numeric literals pass through unchanged\n if (NUMERIC_LITERAL_REGEX.test(defaultExpression)) {\n return defaultExpression;\n }\n\n // Boolean literals convert to SQLite's 1/0\n if (BOOLEAN_LITERAL_REGEX.test(defaultExpression)) {\n return defaultExpression === 'true' ? '1' : '0';\n }\n\n // Quoted strings with type casts: extract just the quoted part\n const match = QUOTED_STRING_WITH_CAST_REGEX.exec(defaultExpression);\n if (match) {\n return match[1];\n }\n\n // Empty arrays: ARRAY[]::type[] or '{}'::type[] → '[]'\n if (\n EMPTY_ARRAY_CONSTRUCTOR_REGEX.test(defaultExpression) ||\n EMPTY_ARRAY_LITERAL_REGEX.test(defaultExpression)\n ) {\n return \"'[]'\";\n }\n\n // Everything else triggers backfill\n throw new UnsupportedColumnDefaultError(\n `Unsupported default value for ${table}.${column}: ${defaultExpression}`,\n );\n}\n\nexport function mapPostgresToLiteColumn(\n table: string,\n column: {name: string; spec: ColumnSpec},\n ignoreDefault?: 'ignore-default',\n): ColumnSpec {\n const {pos, dataType, notNull, dflt, elemPgTypeClass = null} = column.spec;\n\n // PostgreSQL includes [] in dataType for array types (e.g., 'int4[]',\n // 'int4[][]'). liteTypeString() appends attributes:\n // \"varchar[]|NOT_NULL|TEXT_ARRAY\", \"my_enum[][]|TEXT_ENUM|TEXT_ARRAY\"\n const liteType = liteTypeString(\n dataType,\n notNull,\n isEnumColumn(column.spec),\n isArrayColumn(column.spec),\n );\n\n return {\n pos,\n dataType: liteType,\n characterMaximumLength: null,\n // Note: NOT NULL constraints are always ignored for SQLite (replica) tables.\n // 1. They are enforced by the replication stream.\n // 2. We need nullability for columns with defaults to support\n // write permissions on the \"proposed mutation\" state. Proposed\n // mutations are written to SQLite in a `BEGIN CONCURRENT` transaction in mutagen.\n // Permission policies are run against that state (to get their ruling) then the\n // transaction is rolled back.\n notNull: false,\n // Note: DEFAULT constraints are ignored when creating new tables, but are\n // necessary for adding columns to tables with existing rows.\n dflt:\n ignoreDefault === 'ignore-default'\n ? null\n : mapPostgresToLiteDefault(table, column.name, dflt),\n elemPgTypeClass,\n };\n}\n\nexport function mapPostgresToLite(\n t: TableSpec,\n defaultVersion?: string,\n): LiteTableSpec {\n // PRIMARY KEYS are not written to the replica. Instead, we rely\n // UNIQUE indexes, including those created for upstream PRIMARY KEYs.\n const {schema: _, primaryKey: _dropped, ...liteSpec} = t;\n const name = liteTableName(t);\n return {\n ...liteSpec,\n name,\n columns: {\n ...Object.fromEntries(\n Object.entries(t.columns).map(([col, spec]) => [\n col,\n // `ignore-default` for create table statements because\n // there are no rows to set the default for.\n mapPostgresToLiteColumn(name, {name: col, spec}, 'ignore-default'),\n ]),\n ),\n [ZERO_VERSION_COLUMN_NAME]: zeroVersionColumnSpec(defaultVersion),\n },\n };\n}\n\nexport function mapPostgresToLiteIndex(index: IndexSpec): LiteIndexSpec {\n const {schema, tableName, name, ...liteIndex} = index;\n return {\n tableName: liteTableName({schema, name: tableName}),\n name: liteTableName({schema, name}),\n ...liteIndex,\n };\n}\n\nexport class UnsupportedColumnDefaultError extends Error {\n readonly name = 'UnsupportedColumnDefaultError';\n}\n"],"mappings":";;;;;;;;;AAsBA,SAAgB,aACd,MACS;AACT,SAAQ,KAAK,mBAAmB,KAAK,iBAAiB;;;;;;AAOxD,SAAgB,cACd,MACS;AACT,QAAO,KAAK,oBAAoB,QAAQ,KAAK,oBAAoB,KAAA;;AAGnE,SAAS,sBAAsB,gBAAgD;AAC7E,QAAO;EACL,KAAK,OAAO;EACZ,wBAAwB;EACxB,UAAU;EACV,SAAS;EACT,MAAM,CAAC,iBAAiB,OAAO,IAAI,eAAe;EAClD,iBAAiB;EAClB;;AAGH,SAAgB,wBACd,IACA,gBACA,OACA,QACA;AACA,KAAI,uBAAuB,eAAe,KAAK,KAAA,EAC7C,IAAG,OACD,+CAA+C,iBAC7C,eACD,CAAC,qBACQ,MAAM,KAAK,OAAO,6CAC7B;;AAKL,IAAM,wBAAwB;AAG9B,IAAM,wBAAwB;AAQ9B,IAAM,gCAAgC;AAItC,IAAM,gCAAgC;AAItC,IAAM,4BAA4B;AAoBlC,SAAgB,yBACd,OACA,QACA,mBACe;AACf,KAAI,CAAC,kBACH,QAAO;AAIT,KAAI,sBAAsB,KAAK,kBAAkB,CAC/C,QAAO;AAIT,KAAI,sBAAsB,KAAK,kBAAkB,CAC/C,QAAO,sBAAsB,SAAS,MAAM;CAI9C,MAAM,QAAQ,8BAA8B,KAAK,kBAAkB;AACnE,KAAI,MACF,QAAO,MAAM;AAIf,KACE,8BAA8B,KAAK,kBAAkB,IACrD,0BAA0B,KAAK,kBAAkB,CAEjD,QAAO;AAIT,OAAM,IAAI,8BACR,iCAAiC,MAAM,GAAG,OAAO,IAAI,oBACtD;;AAGH,SAAgB,wBACd,OACA,QACA,eACY;CACZ,MAAM,EAAC,KAAK,UAAU,SAAS,MAAM,kBAAkB,SAAQ,OAAO;AAYtE,QAAO;EACL;EACA,UATe,eACf,UACA,SACA,aAAa,OAAO,KAAK,EACzB,cAAc,OAAO,KAAK,CAC3B;EAKC,wBAAwB;EAQxB,SAAS;EAGT,MACE,kBAAkB,mBACd,OACA,yBAAyB,OAAO,OAAO,MAAM,KAAK;EACxD;EACD;;AAGH,SAAgB,kBACd,GACA,gBACe;CAGf,MAAM,EAAC,QAAQ,GAAG,YAAY,UAAU,GAAG,aAAY;CACvD,MAAM,OAAO,cAAc,EAAE;AAC7B,QAAO;EACL,GAAG;EACH;EACA,SAAS;GACP,GAAG,OAAO,YACR,OAAO,QAAQ,EAAE,QAAQ,CAAC,KAAK,CAAC,KAAK,UAAU,CAC7C,KAGA,wBAAwB,MAAM;IAAC,MAAM;IAAK;IAAK,EAAE,iBAAiB,CACnE,CAAC,CACH;IACA,2BAA2B,sBAAsB,eAAe;GAClE;EACF;;AAGH,SAAgB,uBAAuB,OAAiC;CACtE,MAAM,EAAC,QAAQ,WAAW,MAAM,GAAG,cAAa;AAChD,QAAO;EACL,WAAW,cAAc;GAAC;GAAQ,MAAM;GAAU,CAAC;EACnD,MAAM,cAAc;GAAC;GAAQ;GAAK,CAAC;EACnC,GAAG;EACJ;;AAGH,IAAa,gCAAb,cAAmD,MAAM;CACvD,OAAgB"}
@@ -1 +1 @@
1
- {"version":3,"file":"pg-type-parser.js","names":[],"sources":["../../../../../zero-cache/src/db/pg-type-parser.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport {mapEntries} from '../../../shared/src/objects.ts';\nimport {JSON, JSONB} from '../types/pg-types.ts';\nimport type {PostgresDB} from '../types/pg.ts';\n\n// Arbitrary array type to test if the PostgresDB client has fetched types.\nconst INT4_ARRAY_TYPE = 1007;\n\nexport type TypeParser = (val: string) => unknown;\nexport interface TypeParsers {\n getTypeParser(typeOID: number): TypeParser;\n}\n\nexport type ParseOptions = {\n /**\n * Returns JSON and JSONB values as raw JSON strings (i.e. unparsed).\n * Note that JSON[] and JSONB[] values are returned as arrays of (parsed)\n * JSON values.\n */\n returnJsonAsString?: boolean;\n};\n\nconst JSON_TYPE = String(JSON);\nconst JSONB_TYPE = String(JSONB);\n\n// postgres.js has default type parsers with user-defined overrides\n// configurable per-client (see `postgresTypeConfig` in types/pg.ts).\n//\n// From these, the postgres.js client will automatically derive parsers\n// for array versions of these types, provided that the client was\n// configured with `fetch_types: true` (which is the default).\n//\n// A replication session (with `database: replication`), however, does\n// not support this type fetching, so it is done on a connection from\n// a default client.\nexport async function getTypeParsers(\n db: PostgresDB,\n {returnJsonAsString}: ParseOptions = {},\n): Promise<TypeParsers> {\n if (!db.options.parsers[INT4_ARRAY_TYPE]) {\n assert(db.options.fetch_types, `Supplied db must fetch_types`);\n\n // Execute a query to ensure that fetchArrayTypes() gets executed:\n // https://github.com/porsager/postgres/blob/089214e85c23c90cf142d47fb30bd03f42874984/src/connection.js#L536\n await db`SELECT 1`.simple();\n assert(\n db.options.parsers[INT4_ARRAY_TYPE],\n `array types not fetched ${Object.keys(db.options.parsers)}`,\n );\n }\n const parsers = mapEntries(db.options.parsers, (type, parse) => {\n if ((type === JSON_TYPE || type === JSONB_TYPE) && returnJsonAsString) {\n return [type, (x: string) => x];\n }\n // The postgres.js library tags parsers for array types with an `array: true` field.\n // https://github.com/porsager/postgres/blob/089214e85c23c90cf142d47fb30bd03f42874984/src/connection.js#L760\n const isArrayType = (parse as unknown as {array?: boolean}).array;\n\n // And then skips the first character when parsing the string,\n // e.g. an array parser will parse '{1,2,3}' from '1,2,3}'.\n // https://github.com/porsager/postgres/blob/089214e85c23c90cf142d47fb30bd03f42874984/src/connection.js#L496\n return [\n type,\n isArrayType ? (val: string) => parse(val.substring(1)) : parse,\n ];\n });\n return {\n // A type OID for which a parser is not explicitly defined\n // is parsed as a String.\n // https://github.com/porsager/postgres/blob/b0d8c8f363e006a74472d76f859da60c52a80368/src/connection.js#L494\n //\n // This is also consistent with the `pg` library, in which the absence of a\n // TypeParser defaults to \"noParse\":\n // https://github.com/brianc/node-pg-types/blob/5b26b826466cff4a9092b8c9e31960fe293ef3d9/index.js#L15\n getTypeParser: typeOID => parsers[typeOID] ?? String,\n };\n}\n"],"mappings":";;;;AAMA,IAAM,kBAAkB;AAgBxB,IAAM,YAAY,OAAA,GAAW;AAC7B,IAAM,aAAa,OAAO,KAAK;AAY/B,eAAsB,eACpB,IACA,EAAC,uBAAoC,CAAC,GAChB;CACtB,IAAI,CAAC,GAAG,QAAQ,QAAQ,kBAAkB;EACxC,OAAO,GAAG,QAAQ,aAAa,8BAA8B;EAI7D,MAAM,EAAE,WAAW,OAAO;EAC1B,OACE,GAAG,QAAQ,QAAQ,kBACnB,2BAA2B,OAAO,KAAK,GAAG,QAAQ,OAAO,GAC3D;CACF;CACA,MAAM,UAAU,WAAW,GAAG,QAAQ,UAAU,MAAM,UAAU;EAC9D,KAAK,SAAS,aAAa,SAAS,eAAe,oBACjD,OAAO,CAAC,OAAO,MAAc,CAAC;EAShC,OAAO,CACL,MANmB,MAAuC,SAO3C,QAAgB,MAAM,IAAI,UAAU,CAAC,CAAC,IAAI,KAC3D;CACF,CAAC;CACD,OAAO,EAQL,gBAAe,YAAW,QAAQ,YAAY,OAChD;AACF"}
1
+ {"version":3,"file":"pg-type-parser.js","names":[],"sources":["../../../../../zero-cache/src/db/pg-type-parser.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport {mapEntries} from '../../../shared/src/objects.ts';\nimport {JSON, JSONB} from '../types/pg-types.ts';\nimport type {PostgresDB} from '../types/pg.ts';\n\n// Arbitrary array type to test if the PostgresDB client has fetched types.\nconst INT4_ARRAY_TYPE = 1007;\n\nexport type TypeParser = (val: string) => unknown;\nexport interface TypeParsers {\n getTypeParser(typeOID: number): TypeParser;\n}\n\nexport type ParseOptions = {\n /**\n * Returns JSON and JSONB values as raw JSON strings (i.e. unparsed).\n * Note that JSON[] and JSONB[] values are returned as arrays of (parsed)\n * JSON values.\n */\n returnJsonAsString?: boolean;\n};\n\nconst JSON_TYPE = String(JSON);\nconst JSONB_TYPE = String(JSONB);\n\n// postgres.js has default type parsers with user-defined overrides\n// configurable per-client (see `postgresTypeConfig` in types/pg.ts).\n//\n// From these, the postgres.js client will automatically derive parsers\n// for array versions of these types, provided that the client was\n// configured with `fetch_types: true` (which is the default).\n//\n// A replication session (with `database: replication`), however, does\n// not support this type fetching, so it is done on a connection from\n// a default client.\nexport async function getTypeParsers(\n db: PostgresDB,\n {returnJsonAsString}: ParseOptions = {},\n): Promise<TypeParsers> {\n if (!db.options.parsers[INT4_ARRAY_TYPE]) {\n assert(db.options.fetch_types, `Supplied db must fetch_types`);\n\n // Execute a query to ensure that fetchArrayTypes() gets executed:\n // https://github.com/porsager/postgres/blob/089214e85c23c90cf142d47fb30bd03f42874984/src/connection.js#L536\n await db`SELECT 1`.simple();\n assert(\n db.options.parsers[INT4_ARRAY_TYPE],\n `array types not fetched ${Object.keys(db.options.parsers)}`,\n );\n }\n const parsers = mapEntries(db.options.parsers, (type, parse) => {\n if ((type === JSON_TYPE || type === JSONB_TYPE) && returnJsonAsString) {\n return [type, (x: string) => x];\n }\n // The postgres.js library tags parsers for array types with an `array: true` field.\n // https://github.com/porsager/postgres/blob/089214e85c23c90cf142d47fb30bd03f42874984/src/connection.js#L760\n const isArrayType = (parse as unknown as {array?: boolean}).array;\n\n // And then skips the first character when parsing the string,\n // e.g. an array parser will parse '{1,2,3}' from '1,2,3}'.\n // https://github.com/porsager/postgres/blob/089214e85c23c90cf142d47fb30bd03f42874984/src/connection.js#L496\n return [\n type,\n isArrayType ? (val: string) => parse(val.substring(1)) : parse,\n ];\n });\n return {\n // A type OID for which a parser is not explicitly defined\n // is parsed as a String.\n // https://github.com/porsager/postgres/blob/b0d8c8f363e006a74472d76f859da60c52a80368/src/connection.js#L494\n //\n // This is also consistent with the `pg` library, in which the absence of a\n // TypeParser defaults to \"noParse\":\n // https://github.com/brianc/node-pg-types/blob/5b26b826466cff4a9092b8c9e31960fe293ef3d9/index.js#L15\n getTypeParser: typeOID => parsers[typeOID] ?? String,\n };\n}\n"],"mappings":";;;;AAMA,IAAM,kBAAkB;AAgBxB,IAAM,YAAY,OAAA,IAAY;AAC9B,IAAM,aAAa,OAAO,MAAM;AAYhC,eAAsB,eACpB,IACA,EAAC,uBAAoC,EAAE,EACjB;AACtB,KAAI,CAAC,GAAG,QAAQ,QAAQ,kBAAkB;AACxC,SAAO,GAAG,QAAQ,aAAa,+BAA+B;AAI9D,QAAM,EAAE,WAAW,QAAQ;AAC3B,SACE,GAAG,QAAQ,QAAQ,kBACnB,2BAA2B,OAAO,KAAK,GAAG,QAAQ,QAAQ,GAC3D;;CAEH,MAAM,UAAU,WAAW,GAAG,QAAQ,UAAU,MAAM,UAAU;AAC9D,OAAK,SAAS,aAAa,SAAS,eAAe,mBACjD,QAAO,CAAC,OAAO,MAAc,EAAE;AASjC,SAAO,CACL,MANmB,MAAuC,SAO3C,QAAgB,MAAM,IAAI,UAAU,EAAE,CAAC,GAAG,MAC1D;GACD;AACF,QAAO,EAQL,gBAAe,YAAW,QAAQ,YAAY,QAC/C"}
@@ -1 +1 @@
1
- {"version":3,"file":"run-transaction.js","names":[],"sources":["../../../../../zero-cache/src/db/run-transaction.ts"],"sourcesContent":["import type {Enum} from '../../../shared/src/enum.ts';\nimport {type PostgresDB, type PostgresTransaction} from '../types/pg.ts';\nimport type * as Mode from './mode-enum.ts';\nimport {READ_COMMITTED} from './mode-enum.ts';\n\ntype Mode = Enum<typeof Mode>;\n\n// The default timeout is settable by ZERO_IDLE_IN_TRANSACTION_SESSION_TIMEOUT\n// as an emergency measure and is explicitly not made available as a server\n// option. This value is function of how the zero-cache uses transactions, and\n// should never need to be \"tuned\" or adjusted for different environments.\nconst IDLE_IN_TRANSACTION_SESSION_TIMEOUT_MS = parseInt(\n process.env.ZERO_IDLE_IN_TRANSACTION_SESSION_TIMEOUT ?? '60000',\n);\n\nexport type TransactionOptions = {\n mode?: Mode;\n};\n\n/**\n * Runs a zero-cache transaction on the given postgres DB. For consistency\n * across various postgres providers, certain transaction-level parameters\n * are set to override any defaults set by the provider, including:\n * * `statement_timeout` (disabled)\n * * `idle_in_transaction_session_timeout` (1min)\n */\nexport function runTx<T>(\n db: PostgresDB,\n fn: (tx: PostgresTransaction) => T | Promise<T>,\n opts: TransactionOptions = {},\n) {\n const {\n // Explicitly default to the Postgres default to override any custom\n // `default_transaction_isolation`.\n mode = READ_COMMITTED,\n } = opts;\n return db.begin(mode, sql => {\n // Disable any statement_timeout for the current transaction. By default,\n // Postgres does not impose a statement timeout, but some users and\n // providers set one at the database level (even though it is explicitly\n // discouraged by the Postgres documentation).\n //\n // Zero logic in particular often does not fit into the category of general\n // application logic; for potentially long-running operations like\n // migrations and background cleanup, the statement timeout should be\n // disabled to prevent these operations from timing out.\n void sql`SET LOCAL statement_timeout = 0;`.execute().catch(() => {});\n\n // Set an idle_in_transaction_session_timeout to limit the blast radius of\n // orphaned transactions. The zero-cache does not keep transactions open\n // and idle (though this may change if support for streaming of in-progress\n // transactions is added to our logical replication layer).\n void sql\n .unsafe(\n /*sql*/ `SET LOCAL idle_in_transaction_session_timeout = ${IDLE_IN_TRANSACTION_SESSION_TIMEOUT_MS}`,\n )\n .execute()\n .catch(() => {});\n\n return fn(sql);\n });\n}\n"],"mappings":";;;AAWA,IAAM,yCAAyC,SAC7C,QAAQ,IAAI,4CAA4C,OAC1D;;;;;;;;AAaA,SAAgB,MACd,IACA,IACA,OAA2B,CAAC,GAC5B;CACA,MAAM,EAGJ,OAAO,mBACL;CACJ,OAAO,GAAG,MAAM,OAAM,QAAO;EAU3B,GAAQ,mCAAmC,QAAQ,EAAE,YAAY,CAAC,CAAC;EAMnE,IACG,OACS,mDAAmD,wCAC7D,EACC,QAAQ,EACR,YAAY,CAAC,CAAC;EAEjB,OAAO,GAAG,GAAG;CACf,CAAC;AACH"}
1
+ {"version":3,"file":"run-transaction.js","names":[],"sources":["../../../../../zero-cache/src/db/run-transaction.ts"],"sourcesContent":["import type {Enum} from '../../../shared/src/enum.ts';\nimport {type PostgresDB, type PostgresTransaction} from '../types/pg.ts';\nimport type * as Mode from './mode-enum.ts';\nimport {READ_COMMITTED} from './mode-enum.ts';\n\ntype Mode = Enum<typeof Mode>;\n\n// The default timeout is settable by ZERO_IDLE_IN_TRANSACTION_SESSION_TIMEOUT\n// as an emergency measure and is explicitly not made available as a server\n// option. This value is function of how the zero-cache uses transactions, and\n// should never need to be \"tuned\" or adjusted for different environments.\nconst IDLE_IN_TRANSACTION_SESSION_TIMEOUT_MS = parseInt(\n process.env.ZERO_IDLE_IN_TRANSACTION_SESSION_TIMEOUT ?? '60000',\n);\n\nexport type TransactionOptions = {\n mode?: Mode;\n};\n\n/**\n * Runs a zero-cache transaction on the given postgres DB. For consistency\n * across various postgres providers, certain transaction-level parameters\n * are set to override any defaults set by the provider, including:\n * * `statement_timeout` (disabled)\n * * `idle_in_transaction_session_timeout` (1min)\n */\nexport function runTx<T>(\n db: PostgresDB,\n fn: (tx: PostgresTransaction) => T | Promise<T>,\n opts: TransactionOptions = {},\n) {\n const {\n // Explicitly default to the Postgres default to override any custom\n // `default_transaction_isolation`.\n mode = READ_COMMITTED,\n } = opts;\n return db.begin(mode, sql => {\n // Disable any statement_timeout for the current transaction. By default,\n // Postgres does not impose a statement timeout, but some users and\n // providers set one at the database level (even though it is explicitly\n // discouraged by the Postgres documentation).\n //\n // Zero logic in particular often does not fit into the category of general\n // application logic; for potentially long-running operations like\n // migrations and background cleanup, the statement timeout should be\n // disabled to prevent these operations from timing out.\n void sql`SET LOCAL statement_timeout = 0;`.execute().catch(() => {});\n\n // Set an idle_in_transaction_session_timeout to limit the blast radius of\n // orphaned transactions. The zero-cache does not keep transactions open\n // and idle (though this may change if support for streaming of in-progress\n // transactions is added to our logical replication layer).\n void sql\n .unsafe(\n /*sql*/ `SET LOCAL idle_in_transaction_session_timeout = ${IDLE_IN_TRANSACTION_SESSION_TIMEOUT_MS}`,\n )\n .execute()\n .catch(() => {});\n\n return fn(sql);\n });\n}\n"],"mappings":";;;AAWA,IAAM,yCAAyC,SAC7C,QAAQ,IAAI,4CAA4C,QACzD;;;;;;;;AAaD,SAAgB,MACd,IACA,IACA,OAA2B,EAAE,EAC7B;CACA,MAAM,EAGJ,OAAO,mBACL;AACJ,QAAO,GAAG,MAAM,OAAM,QAAO;AAUtB,KAAG,mCAAmC,SAAS,CAAC,YAAY,GAAG;AAM/D,MACF,OACS,mDAAmD,yCAC5D,CACA,SAAS,CACT,YAAY,GAAG;AAElB,SAAO,GAAG,IAAI;GACd"}
@@ -1 +1 @@
1
- {"version":3,"file":"specs.js","names":[],"sources":["../../../../../zero-cache/src/db/specs.ts"],"sourcesContent":["import type {DeepReadonly} from '../../../shared/src/json.ts';\nimport * as v from '../../../shared/src/valita.ts';\nimport type {PrimaryKey} from '../../../zero-protocol/src/primary-key.ts';\nimport type {SchemaValue} from '../../../zero-schema/src/table-schema.ts';\nimport * as PostgresReplicaIdentity from './postgres-replica-identity-enum.ts';\nimport * as PostgresTypeClass from './postgres-type-class-enum.ts';\n\nexport const pgTypeClassSchema = v.literalUnion(\n PostgresTypeClass.Base,\n PostgresTypeClass.Composite,\n PostgresTypeClass.Domain,\n PostgresTypeClass.Enum,\n PostgresTypeClass.Pseudo,\n PostgresTypeClass.Range,\n PostgresTypeClass.Multirange,\n);\n\nconst pgReplicaIdentitySchema = v.literalUnion(\n PostgresReplicaIdentity.Default,\n PostgresReplicaIdentity.Nothing,\n PostgresReplicaIdentity.Full,\n PostgresReplicaIdentity.Index,\n);\n\nexport const columnSpec = v.object({\n pos: v.number(),\n dataType: v.string(),\n pgTypeClass: pgTypeClassSchema.optional(),\n\n // If the column is an array, this will be the type of the\n // elements in the array. If the column is not an array,\n // this will be null.\n elemPgTypeClass: pgTypeClassSchema.nullable().optional(),\n\n characterMaximumLength: v.number().nullable().optional(),\n notNull: v.boolean().nullable().optional(),\n dflt: v.string().nullable().optional(),\n});\n\nexport type ColumnSpec = Readonly<v.Infer<typeof columnSpec>>;\n\nconst publishedColumnSpec = columnSpec.extend({\n typeOID: v.number(),\n});\n\nexport const liteTableSpec = v.object({\n name: v.string(),\n columns: v.record(columnSpec),\n primaryKey: v.array(v.string()).optional(),\n});\n\nexport const tableSpec = liteTableSpec.extend({\n schema: v.string(),\n});\n\nexport const publishedTableSpec = tableSpec.extend({\n oid: v.number(),\n // Always present for new instances (e.g. from DDL triggers), but\n // may from `initialSchema` object stored in the `replicas` table.\n schemaOID: v.number().optional(),\n columns: v.record(publishedColumnSpec),\n replicaIdentity: pgReplicaIdentitySchema.optional(),\n publications: v.record(v.object({rowFilter: v.string().nullable()})),\n});\n\nexport type MutableLiteTableSpec = v.Infer<typeof liteTableSpec>;\n\nexport type LiteTableSpec = Readonly<MutableLiteTableSpec>;\n\nexport type LiteTableSpecWithKeysAndVersion = Omit<\n LiteTableSpec,\n 'primaryKey'\n> & {\n /**\n * All keys associated with a unique index. Includes indexes with\n * nullable columns.\n */\n uniqueKeys: PrimaryKey[];\n\n /**\n * The key selected to act as the \"primary key\". Primary keys\n * are not explicitly set on the replica, but an appropriate\n * unique index is required.\n */\n primaryKey: PrimaryKey; // note: required\n\n /**\n * All keys associated with a unique index over non-null\n * columns, i.e. suitable as a primary key.\n */\n allPotentialPrimaryKeys: PrimaryKey[];\n\n /**\n * The minimum `_0_version` value for every row in the table. If this is\n * present, `_0_version` value in the row itself should only be used if\n * it is greater (i.e. later) than the `minRowVersion`.\n */\n minRowVersion: string | null;\n};\n\nexport type LiteAndZqlSpec = {\n tableSpec: LiteTableSpecWithKeysAndVersion;\n zqlSpec: Record<string, SchemaValue>;\n};\n\nexport type TableSpec = Readonly<v.Infer<typeof tableSpec>>;\n\nexport type PublishedTableSpec = Readonly<v.Infer<typeof publishedTableSpec>>;\n\nexport const directionSchema = v.literalUnion('ASC', 'DESC');\n\nexport const liteIndexSpec = v.object({\n name: v.string(),\n tableName: v.string(),\n unique: v.boolean(),\n columns: v.record(directionSchema),\n});\n\nexport type MutableLiteIndexSpec = v.Infer<typeof liteIndexSpec>;\n\nexport type LiteIndexSpec = Readonly<MutableLiteIndexSpec>;\n\nexport const indexSpec = liteIndexSpec.extend({\n schema: v.string(),\n});\n\nexport type IndexSpec = DeepReadonly<v.Infer<typeof indexSpec>>;\n\nexport const publishedIndexSpec = indexSpec.extend({\n isReplicaIdentity: v.boolean().optional(),\n isPrimaryKey: v.boolean().optional(),\n isImmediate: v.boolean().optional(),\n});\n\nexport type PublishedIndexSpec = DeepReadonly<\n v.Infer<typeof publishedIndexSpec>\n>;\n"],"mappings":";;AAOA,IAAa,oBAAoB,aAC/B,KACA,KACA,KACA,KACA,KACA,KACA,GACF;AAEA,IAAM,0BAA0B,aAC9B,KACA,KACA,KACA,GACF;AAEA,IAAa,aAAa,eAAE,OAAO;CACjC,KAAK,eAAE,OAAO;CACd,UAAU,eAAE,OAAO;CACnB,aAAa,kBAAkB,SAAS;CAKxC,iBAAiB,kBAAkB,SAAS,EAAE,SAAS;CAEvD,wBAAwB,eAAE,OAAO,EAAE,SAAS,EAAE,SAAS;CACvD,SAAS,eAAE,QAAQ,EAAE,SAAS,EAAE,SAAS;CACzC,MAAM,eAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AACvC,CAAC;AAID,IAAM,sBAAsB,WAAW,OAAO,EAC5C,SAAS,eAAE,OAAO,EACpB,CAAC;AAQD,IAAa,YANgB,eAAE,OAAO;CACpC,MAAM,eAAE,OAAO;CACf,SAAS,eAAE,OAAO,UAAU;CAC5B,YAAY,eAAE,MAAM,eAAE,OAAO,CAAC,EAAE,SAAS;AAC3C,CAEyB,EAAc,OAAO,EAC5C,QAAQ,eAAE,OAAO,EACnB,CAAC;AAED,IAAa,qBAAqB,UAAU,OAAO;CACjD,KAAK,eAAE,OAAO;CAGd,WAAW,eAAE,OAAO,EAAE,SAAS;CAC/B,SAAS,eAAE,OAAO,mBAAmB;CACrC,iBAAiB,wBAAwB,SAAS;CAClD,cAAc,eAAE,OAAO,eAAE,OAAO,EAAC,WAAW,eAAE,OAAO,EAAE,SAAS,EAAC,CAAC,CAAC;AACrE,CAAC;AA8CD,IAAa,kBAAkB,aAAe,OAAO,MAAM;AAa3D,IAAa,YAXgB,eAAE,OAAO;CACpC,MAAM,eAAE,OAAO;CACf,WAAW,eAAE,OAAO;CACpB,QAAQ,eAAE,QAAQ;CAClB,SAAS,eAAE,OAAO,eAAe;AACnC,CAMyB,EAAc,OAAO,EAC5C,QAAQ,eAAE,OAAO,EACnB,CAAC;AAID,IAAa,qBAAqB,UAAU,OAAO;CACjD,mBAAmB,eAAE,QAAQ,EAAE,SAAS;CACxC,cAAc,eAAE,QAAQ,EAAE,SAAS;CACnC,aAAa,eAAE,QAAQ,EAAE,SAAS;AACpC,CAAC"}
1
+ {"version":3,"file":"specs.js","names":[],"sources":["../../../../../zero-cache/src/db/specs.ts"],"sourcesContent":["import type {DeepReadonly} from '../../../shared/src/json.ts';\nimport * as v from '../../../shared/src/valita.ts';\nimport type {PrimaryKey} from '../../../zero-protocol/src/primary-key.ts';\nimport type {SchemaValue} from '../../../zero-schema/src/table-schema.ts';\nimport * as PostgresReplicaIdentity from './postgres-replica-identity-enum.ts';\nimport * as PostgresTypeClass from './postgres-type-class-enum.ts';\n\nexport const pgTypeClassSchema = v.literalUnion(\n PostgresTypeClass.Base,\n PostgresTypeClass.Composite,\n PostgresTypeClass.Domain,\n PostgresTypeClass.Enum,\n PostgresTypeClass.Pseudo,\n PostgresTypeClass.Range,\n PostgresTypeClass.Multirange,\n);\n\nconst pgReplicaIdentitySchema = v.literalUnion(\n PostgresReplicaIdentity.Default,\n PostgresReplicaIdentity.Nothing,\n PostgresReplicaIdentity.Full,\n PostgresReplicaIdentity.Index,\n);\n\nexport const columnSpec = v.object({\n pos: v.number(),\n dataType: v.string(),\n pgTypeClass: pgTypeClassSchema.optional(),\n\n // If the column is an array, this will be the type of the\n // elements in the array. If the column is not an array,\n // this will be null.\n elemPgTypeClass: pgTypeClassSchema.nullable().optional(),\n\n characterMaximumLength: v.number().nullable().optional(),\n notNull: v.boolean().nullable().optional(),\n dflt: v.string().nullable().optional(),\n});\n\nexport type ColumnSpec = Readonly<v.Infer<typeof columnSpec>>;\n\nconst publishedColumnSpec = columnSpec.extend({\n typeOID: v.number(),\n});\n\nexport const liteTableSpec = v.object({\n name: v.string(),\n columns: v.record(columnSpec),\n primaryKey: v.array(v.string()).optional(),\n});\n\nexport const tableSpec = liteTableSpec.extend({\n schema: v.string(),\n});\n\nexport const publishedTableSpec = tableSpec.extend({\n oid: v.number(),\n // Always present for new instances (e.g. from DDL triggers), but\n // may from `initialSchema` object stored in the `replicas` table.\n schemaOID: v.number().optional(),\n columns: v.record(publishedColumnSpec),\n replicaIdentity: pgReplicaIdentitySchema.optional(),\n publications: v.record(v.object({rowFilter: v.string().nullable()})),\n});\n\nexport type MutableLiteTableSpec = v.Infer<typeof liteTableSpec>;\n\nexport type LiteTableSpec = Readonly<MutableLiteTableSpec>;\n\nexport type LiteTableSpecWithKeysAndVersion = Omit<\n LiteTableSpec,\n 'primaryKey'\n> & {\n /**\n * All keys associated with a unique index. Includes indexes with\n * nullable columns.\n */\n uniqueKeys: PrimaryKey[];\n\n /**\n * The key selected to act as the \"primary key\". Primary keys\n * are not explicitly set on the replica, but an appropriate\n * unique index is required.\n */\n primaryKey: PrimaryKey; // note: required\n\n /**\n * All keys associated with a unique index over non-null\n * columns, i.e. suitable as a primary key.\n */\n allPotentialPrimaryKeys: PrimaryKey[];\n\n /**\n * The minimum `_0_version` value for every row in the table. If this is\n * present, `_0_version` value in the row itself should only be used if\n * it is greater (i.e. later) than the `minRowVersion`.\n */\n minRowVersion: string | null;\n};\n\nexport type LiteAndZqlSpec = {\n tableSpec: LiteTableSpecWithKeysAndVersion;\n zqlSpec: Record<string, SchemaValue>;\n};\n\nexport type TableSpec = Readonly<v.Infer<typeof tableSpec>>;\n\nexport type PublishedTableSpec = Readonly<v.Infer<typeof publishedTableSpec>>;\n\nexport const directionSchema = v.literalUnion('ASC', 'DESC');\n\nexport const liteIndexSpec = v.object({\n name: v.string(),\n tableName: v.string(),\n unique: v.boolean(),\n columns: v.record(directionSchema),\n});\n\nexport type MutableLiteIndexSpec = v.Infer<typeof liteIndexSpec>;\n\nexport type LiteIndexSpec = Readonly<MutableLiteIndexSpec>;\n\nexport const indexSpec = liteIndexSpec.extend({\n schema: v.string(),\n});\n\nexport type IndexSpec = DeepReadonly<v.Infer<typeof indexSpec>>;\n\nexport const publishedIndexSpec = indexSpec.extend({\n isReplicaIdentity: v.boolean().optional(),\n isPrimaryKey: v.boolean().optional(),\n isImmediate: v.boolean().optional(),\n});\n\nexport type PublishedIndexSpec = DeepReadonly<\n v.Infer<typeof publishedIndexSpec>\n>;\n"],"mappings":";;AAOA,IAAa,oBAAoB,aAC/B,KACA,KACA,KACA,KACA,KACA,KACA,IACD;AAED,IAAM,0BAA0B,aAC9B,KACA,KACA,KACA,IACD;AAED,IAAa,aAAa,eAAE,OAAO;CACjC,KAAK,eAAE,QAAQ;CACf,UAAU,eAAE,QAAQ;CACpB,aAAa,kBAAkB,UAAU;CAKzC,iBAAiB,kBAAkB,UAAU,CAAC,UAAU;CAExD,wBAAwB,eAAE,QAAQ,CAAC,UAAU,CAAC,UAAU;CACxD,SAAS,eAAE,SAAS,CAAC,UAAU,CAAC,UAAU;CAC1C,MAAM,eAAE,QAAQ,CAAC,UAAU,CAAC,UAAU;CACvC,CAAC;AAIF,IAAM,sBAAsB,WAAW,OAAO,EAC5C,SAAS,eAAE,QAAQ,EACpB,CAAC;AAQF,IAAa,YANgB,eAAE,OAAO;CACpC,MAAM,eAAE,QAAQ;CAChB,SAAS,eAAE,OAAO,WAAW;CAC7B,YAAY,eAAE,MAAM,eAAE,QAAQ,CAAC,CAAC,UAAU;CAC3C,CAAC,CAEqC,OAAO,EAC5C,QAAQ,eAAE,QAAQ,EACnB,CAAC;AAEF,IAAa,qBAAqB,UAAU,OAAO;CACjD,KAAK,eAAE,QAAQ;CAGf,WAAW,eAAE,QAAQ,CAAC,UAAU;CAChC,SAAS,eAAE,OAAO,oBAAoB;CACtC,iBAAiB,wBAAwB,UAAU;CACnD,cAAc,eAAE,OAAO,eAAE,OAAO,EAAC,WAAW,eAAE,QAAQ,CAAC,UAAU,EAAC,CAAC,CAAC;CACrE,CAAC;AA8CF,IAAa,kBAAkB,aAAe,OAAO,OAAO;AAa5D,IAAa,YAXgB,eAAE,OAAO;CACpC,MAAM,eAAE,QAAQ;CAChB,WAAW,eAAE,QAAQ;CACrB,QAAQ,eAAE,SAAS;CACnB,SAAS,eAAE,OAAO,gBAAgB;CACnC,CAAC,CAMqC,OAAO,EAC5C,QAAQ,eAAE,QAAQ,EACnB,CAAC;AAIF,IAAa,qBAAqB,UAAU,OAAO;CACjD,mBAAmB,eAAE,SAAS,CAAC,UAAU;CACzC,cAAc,eAAE,SAAS,CAAC,UAAU;CACpC,aAAa,eAAE,SAAS,CAAC,UAAU;CACpC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"statements.js","names":[],"sources":["../../../../../zero-cache/src/db/statements.ts"],"sourcesContent":["import type {RunResult} from '@rocicorp/zero-sqlite3';\nimport type {Database} from '../../../zqlite/src/db.ts';\nimport {StatementCache} from '../../../zqlite/src/internal/statement-cache.ts';\n\n/**\n * A stateless wrapper around a {@link StatementCache} that facilitates single-line\n * `printf()` style invocations of cached prepared statement operations.\n */\nexport class StatementRunner {\n readonly db: Database;\n readonly statementCache: StatementCache;\n\n constructor(db: Database) {\n this.db = db;\n this.statementCache = new StatementCache(db);\n }\n\n /**\n * Prepares a statement (or retrieves it from the cache) and runs it\n * with the given args.\n */\n run(sql: string, ...args: unknown[]): RunResult {\n return this.statementCache.use(sql, cached =>\n cached.statement.run(...args),\n );\n }\n\n /**\n * Prepares a statement (or retrieves it from the cache) and returns\n * the first result.\n */\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n get(sql: string, ...args: unknown[]): any {\n return this.statementCache.use(sql, cached =>\n cached.statement.get(...args),\n );\n }\n\n /**\n * Prepares a statement (or retrieves it from the cache) and returns\n * all of its results.\n */\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n all(sql: string, ...args: unknown[]): any[] {\n return this.statementCache.use(sql, cached =>\n cached.statement.all(...args),\n );\n }\n\n // Syntactic sugar methods\n begin(): RunResult {\n return this.run('BEGIN');\n }\n\n beginConcurrent(): RunResult {\n return this.run('BEGIN CONCURRENT');\n }\n\n beginImmediate(): RunResult {\n return this.run('BEGIN IMMEDIATE');\n }\n\n commit(): RunResult {\n return this.run('COMMIT');\n }\n\n rollback(): RunResult {\n return this.run('ROLLBACK');\n }\n}\n"],"mappings":";;;;;;AAQA,IAAa,kBAAb,MAA6B;CAC3B;CACA;CAEA,YAAY,IAAc;EACxB,KAAK,KAAK;EACV,KAAK,iBAAiB,IAAI,eAAe,EAAE;CAC7C;;;;;CAMA,IAAI,KAAa,GAAG,MAA4B;EAC9C,OAAO,KAAK,eAAe,IAAI,MAAK,WAClC,OAAO,UAAU,IAAI,GAAG,IAAI,CAC9B;CACF;;;;;CAOA,IAAI,KAAa,GAAG,MAAsB;EACxC,OAAO,KAAK,eAAe,IAAI,MAAK,WAClC,OAAO,UAAU,IAAI,GAAG,IAAI,CAC9B;CACF;;;;;CAOA,IAAI,KAAa,GAAG,MAAwB;EAC1C,OAAO,KAAK,eAAe,IAAI,MAAK,WAClC,OAAO,UAAU,IAAI,GAAG,IAAI,CAC9B;CACF;CAGA,QAAmB;EACjB,OAAO,KAAK,IAAI,OAAO;CACzB;CAEA,kBAA6B;EAC3B,OAAO,KAAK,IAAI,kBAAkB;CACpC;CAEA,iBAA4B;EAC1B,OAAO,KAAK,IAAI,iBAAiB;CACnC;CAEA,SAAoB;EAClB,OAAO,KAAK,IAAI,QAAQ;CAC1B;CAEA,WAAsB;EACpB,OAAO,KAAK,IAAI,UAAU;CAC5B;AACF"}
1
+ {"version":3,"file":"statements.js","names":[],"sources":["../../../../../zero-cache/src/db/statements.ts"],"sourcesContent":["import type {RunResult} from '@rocicorp/zero-sqlite3';\nimport type {Database} from '../../../zqlite/src/db.ts';\nimport {StatementCache} from '../../../zqlite/src/internal/statement-cache.ts';\n\n/**\n * A stateless wrapper around a {@link StatementCache} that facilitates single-line\n * `printf()` style invocations of cached prepared statement operations.\n */\nexport class StatementRunner {\n readonly db: Database;\n readonly statementCache: StatementCache;\n\n constructor(db: Database) {\n this.db = db;\n this.statementCache = new StatementCache(db);\n }\n\n /**\n * Prepares a statement (or retrieves it from the cache) and runs it\n * with the given args.\n */\n run(sql: string, ...args: unknown[]): RunResult {\n return this.statementCache.use(sql, cached =>\n cached.statement.run(...args),\n );\n }\n\n /**\n * Prepares a statement (or retrieves it from the cache) and returns\n * the first result.\n */\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n get(sql: string, ...args: unknown[]): any {\n return this.statementCache.use(sql, cached =>\n cached.statement.get(...args),\n );\n }\n\n /**\n * Prepares a statement (or retrieves it from the cache) and returns\n * all of its results.\n */\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n all(sql: string, ...args: unknown[]): any[] {\n return this.statementCache.use(sql, cached =>\n cached.statement.all(...args),\n );\n }\n\n // Syntactic sugar methods\n begin(): RunResult {\n return this.run('BEGIN');\n }\n\n beginConcurrent(): RunResult {\n return this.run('BEGIN CONCURRENT');\n }\n\n beginImmediate(): RunResult {\n return this.run('BEGIN IMMEDIATE');\n }\n\n commit(): RunResult {\n return this.run('COMMIT');\n }\n\n rollback(): RunResult {\n return this.run('ROLLBACK');\n }\n}\n"],"mappings":";;;;;;AAQA,IAAa,kBAAb,MAA6B;CAC3B;CACA;CAEA,YAAY,IAAc;AACxB,OAAK,KAAK;AACV,OAAK,iBAAiB,IAAI,eAAe,GAAG;;;;;;CAO9C,IAAI,KAAa,GAAG,MAA4B;AAC9C,SAAO,KAAK,eAAe,IAAI,MAAK,WAClC,OAAO,UAAU,IAAI,GAAG,KAAK,CAC9B;;;;;;CAQH,IAAI,KAAa,GAAG,MAAsB;AACxC,SAAO,KAAK,eAAe,IAAI,MAAK,WAClC,OAAO,UAAU,IAAI,GAAG,KAAK,CAC9B;;;;;;CAQH,IAAI,KAAa,GAAG,MAAwB;AAC1C,SAAO,KAAK,eAAe,IAAI,MAAK,WAClC,OAAO,UAAU,IAAI,GAAG,KAAK,CAC9B;;CAIH,QAAmB;AACjB,SAAO,KAAK,IAAI,QAAQ;;CAG1B,kBAA6B;AAC3B,SAAO,KAAK,IAAI,mBAAmB;;CAGrC,iBAA4B;AAC1B,SAAO,KAAK,IAAI,kBAAkB;;CAGpC,SAAoB;AAClB,SAAO,KAAK,IAAI,SAAS;;CAG3B,WAAsB;AACpB,SAAO,KAAK,IAAI,WAAW"}