@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":"configuration-error.js","names":[],"sources":["../../../../../zero-cache/src/types/configuration-error.ts"],"sourcesContent":["export class ConfigurationError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = 'ConfigurationError';\n }\n}\n"],"mappings":";AAAA,IAAa,qBAAb,cAAwC,MAAM;CAC5C,YAAY,SAAiB,SAAwB;EACnD,MAAM,SAAS,OAAO;EACtB,KAAK,OAAO;CACd;AACF"}
1
+ {"version":3,"file":"configuration-error.js","names":[],"sources":["../../../../../zero-cache/src/types/configuration-error.ts"],"sourcesContent":["export class ConfigurationError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = 'ConfigurationError';\n }\n}\n"],"mappings":";AAAA,IAAa,qBAAb,cAAwC,MAAM;CAC5C,YAAY,SAAiB,SAAwB;AACnD,QAAM,SAAS,QAAQ;AACvB,OAAK,OAAO"}
@@ -1 +1 @@
1
- {"version":3,"file":"error-with-level.js","names":[],"sources":["../../../../../zero-cache/src/types/error-with-level.ts"],"sourcesContent":["import type {LogLevel} from '@rocicorp/logger';\nimport {getErrorMessage} from '../../../shared/src/error.ts';\nimport {ErrorKind} from '../../../zero-protocol/src/error-kind.ts';\nimport {ErrorOrigin} from '../../../zero-protocol/src/error-origin.ts';\nimport {\n isProtocolError,\n ProtocolError,\n type ErrorBody,\n} from '../../../zero-protocol/src/error.ts';\n\nexport class ProtocolErrorWithLevel extends ProtocolError {\n readonly logLevel: LogLevel;\n\n constructor(\n errorBody: ErrorBody,\n logLevel: LogLevel,\n options?: ErrorOptions,\n ) {\n super(errorBody, options);\n this.logLevel = logLevel;\n }\n}\n\nexport function getLogLevel(error: unknown): LogLevel {\n return error instanceof ProtocolErrorWithLevel\n ? error.logLevel\n : isProtocolError(error)\n ? 'warn'\n : 'error';\n}\n\nexport function wrapWithProtocolError(error: unknown): ProtocolError {\n if (isProtocolError(error)) {\n return error;\n }\n\n return new ProtocolError(\n {\n kind: ErrorKind.Internal,\n message: getErrorMessage(error),\n origin: ErrorOrigin.ZeroCache,\n },\n {cause: error},\n );\n}\n"],"mappings":";;;;;AAUA,IAAa,yBAAb,cAA4C,cAAc;CACxD;CAEA,YACE,WACA,UACA,SACA;EACA,MAAM,WAAW,OAAO;EACxB,KAAK,WAAW;CAClB;AACF;AAEA,SAAgB,YAAY,OAA0B;CACpD,OAAO,iBAAiB,yBACpB,MAAM,WACN,gBAAgB,KAAK,IACnB,SACA;AACR;AAEA,SAAgB,sBAAsB,OAA+B;CACnE,IAAI,gBAAgB,KAAK,GACvB,OAAO;CAGT,OAAO,IAAI,cACT;EACE,MAAM;EACN,SAAS,gBAAgB,KAAK;EAC9B,QAAQ;CACV,GACA,EAAC,OAAO,MAAK,CACf;AACF"}
1
+ {"version":3,"file":"error-with-level.js","names":[],"sources":["../../../../../zero-cache/src/types/error-with-level.ts"],"sourcesContent":["import type {LogLevel} from '@rocicorp/logger';\nimport {getErrorMessage} from '../../../shared/src/error.ts';\nimport {ErrorKind} from '../../../zero-protocol/src/error-kind.ts';\nimport {ErrorOrigin} from '../../../zero-protocol/src/error-origin.ts';\nimport {\n isProtocolError,\n ProtocolError,\n type ErrorBody,\n} from '../../../zero-protocol/src/error.ts';\n\nexport class ProtocolErrorWithLevel extends ProtocolError {\n readonly logLevel: LogLevel;\n\n constructor(\n errorBody: ErrorBody,\n logLevel: LogLevel,\n options?: ErrorOptions,\n ) {\n super(errorBody, options);\n this.logLevel = logLevel;\n }\n}\n\nexport function getLogLevel(error: unknown): LogLevel {\n return error instanceof ProtocolErrorWithLevel\n ? error.logLevel\n : isProtocolError(error)\n ? 'warn'\n : 'error';\n}\n\nexport function wrapWithProtocolError(error: unknown): ProtocolError {\n if (isProtocolError(error)) {\n return error;\n }\n\n return new ProtocolError(\n {\n kind: ErrorKind.Internal,\n message: getErrorMessage(error),\n origin: ErrorOrigin.ZeroCache,\n },\n {cause: error},\n );\n}\n"],"mappings":";;;;;AAUA,IAAa,yBAAb,cAA4C,cAAc;CACxD;CAEA,YACE,WACA,UACA,SACA;AACA,QAAM,WAAW,QAAQ;AACzB,OAAK,WAAW;;;AAIpB,SAAgB,YAAY,OAA0B;AACpD,QAAO,iBAAiB,yBACpB,MAAM,WACN,gBAAgB,MAAM,GACpB,SACA;;AAGR,SAAgB,sBAAsB,OAA+B;AACnE,KAAI,gBAAgB,MAAM,CACxB,QAAO;AAGT,QAAO,IAAI,cACT;EACE,MAAM;EACN,SAAS,gBAAgB,MAAM;EAC/B,QAAQ;EACT,EACD,EAAC,OAAO,OAAM,CACf"}
@@ -1 +1 @@
1
- {"version":3,"file":"http.js","names":[],"sources":["../../../../../zero-cache/src/types/http.ts"],"sourcesContent":["import type {IncomingMessage} from 'http';\n\n/**\n * Contains the subset of {@link IncomingMessage} fields suitable for\n * passing across processes.\n */\nexport type IncomingMessageSubset = Pick<\n IncomingMessage,\n | 'headers'\n | 'headersDistinct'\n | 'httpVersion'\n | 'method'\n | 'rawHeaders'\n | 'rawTrailers'\n | 'trailers'\n | 'trailersDistinct'\n | 'url'\n>;\n\nexport function serializableSubset(\n msg: IncomingMessageSubset,\n): IncomingMessageSubset {\n const {\n headers,\n headersDistinct,\n httpVersion,\n method = 'GET',\n rawHeaders,\n rawTrailers,\n trailers,\n trailersDistinct,\n url,\n } = msg;\n\n return {\n headers,\n headersDistinct,\n httpVersion,\n method,\n rawHeaders,\n rawTrailers,\n trailers,\n trailersDistinct,\n url,\n };\n}\n"],"mappings":";AAmBA,SAAgB,mBACd,KACuB;CACvB,MAAM,EACJ,SACA,iBACA,aACA,SAAS,OACT,YACA,aACA,UACA,kBACA,QACE;CAEJ,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;AACF"}
1
+ {"version":3,"file":"http.js","names":[],"sources":["../../../../../zero-cache/src/types/http.ts"],"sourcesContent":["import type {IncomingMessage} from 'http';\n\n/**\n * Contains the subset of {@link IncomingMessage} fields suitable for\n * passing across processes.\n */\nexport type IncomingMessageSubset = Pick<\n IncomingMessage,\n | 'headers'\n | 'headersDistinct'\n | 'httpVersion'\n | 'method'\n | 'rawHeaders'\n | 'rawTrailers'\n | 'trailers'\n | 'trailersDistinct'\n | 'url'\n>;\n\nexport function serializableSubset(\n msg: IncomingMessageSubset,\n): IncomingMessageSubset {\n const {\n headers,\n headersDistinct,\n httpVersion,\n method = 'GET',\n rawHeaders,\n rawTrailers,\n trailers,\n trailersDistinct,\n url,\n } = msg;\n\n return {\n headers,\n headersDistinct,\n httpVersion,\n method,\n rawHeaders,\n rawTrailers,\n trailers,\n trailersDistinct,\n url,\n };\n}\n"],"mappings":";AAmBA,SAAgB,mBACd,KACuB;CACvB,MAAM,EACJ,SACA,iBACA,aACA,SAAS,OACT,YACA,aACA,UACA,kBACA,QACE;AAEJ,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD"}
@@ -1 +1 @@
1
- {"version":3,"file":"lexi-version.js","names":[],"sources":["../../../../../zero-cache/src/types/lexi-version.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport {parseBigInt} from '../../../shared/src/parse-big-int.ts';\n\n/**\n * A LexiVersion is a lexicographically sortable representation of\n * numbers from 0 to Number.MAX_SAFE_INTEGER (which is the safe range of\n * Version values used in Zero).\n *\n * The Version is first encoded in base36, and then prepended by a single\n * base36 character representing the length (of the base36 version) minus 1.\n * This encoding can encode numbers up to 185 bits, with the maximum encoded\n * number being `\"z\".repeat(37)`, or 36^36-1 (approximately 1.0638735892371651e+56).\n *\n * Examples:\n * * 0 => \"00\"\n * * 10 => \"0a\"\n * * 35 => \"0z\"\n * * 36 => \"110\"\n * * 46655 => \"2zzz\"\n * * 2^64 => \"c3w5e11264sgsg\"\n *\n * Note that when using the `number` type, the library will assert if attempting\n * to encode a Version larger than Number.MAX_SAFE_INTEGER. For large numbers,\n * use the `bigint` type.\n */\nexport type LexiVersion = string;\n\nexport function versionToLexi(v: number | bigint): LexiVersion {\n assert(v >= 0, 'Negative versions are not supported');\n assert(\n typeof v === 'bigint' ||\n (v <= Number.MAX_SAFE_INTEGER && Number.isInteger(v)),\n `Invalid or unsafe version ${v}`,\n );\n const base36Version = BigInt(v).toString(36);\n const length = BigInt(base36Version.length - 1).toString(36);\n assert(\n length.length === 1,\n `Value is too large to be encoded as a LexiVersion: ${v.toString()}`,\n );\n return `${length}${base36Version}`;\n}\n\nexport function versionFromLexi(lexiVersion: LexiVersion): bigint {\n assert(\n lexiVersion.length >= 2,\n () =>\n `LexiVersion must have at least 2 characters, got ${lexiVersion.length}`,\n );\n const length = lexiVersion.substring(0, 1);\n const base36Version = lexiVersion.substring(1);\n assert(\n base36Version.length === parseInt(length, 36) + 1,\n `Invalid LexiVersion: ${lexiVersion}`,\n );\n return parseBigInt(base36Version, 36);\n}\n\nexport type AtLeastOne<T> = [T, ...T[]];\n\nexport function max(...versions: AtLeastOne<LexiVersion>): LexiVersion {\n let winner = versions[0];\n for (let i = 1; i < versions.length; i++) {\n const b = versions[i];\n winner = winner > b ? winner : b;\n }\n return winner;\n}\n\nexport function min(...versions: AtLeastOne<LexiVersion>): LexiVersion {\n let winner = versions[0];\n for (let i = 1; i < versions.length; i++) {\n const b = versions[i];\n winner = winner < b ? winner : b;\n }\n return winner;\n}\n"],"mappings":";;;AA2BA,SAAgB,cAAc,GAAiC;CAC7D,OAAO,KAAK,GAAG,qCAAqC;CACpD,OACE,OAAO,MAAM,YACV,KAAK,OAAO,oBAAoB,OAAO,UAAU,CAAC,GACrD,6BAA6B,GAC/B;CACA,MAAM,gBAAgB,OAAO,CAAC,EAAE,SAAS,EAAE;CAC3C,MAAM,SAAS,OAAO,cAAc,SAAS,CAAC,EAAE,SAAS,EAAE;CAC3D,OACE,OAAO,WAAW,GAClB,sDAAsD,EAAE,SAAS,GACnE;CACA,OAAO,GAAG,SAAS;AACrB;AAEA,SAAgB,gBAAgB,aAAkC;CAChE,OACE,YAAY,UAAU,SAEpB,oDAAoD,YAAY,QACpE;CACA,MAAM,SAAS,YAAY,UAAU,GAAG,CAAC;CACzC,MAAM,gBAAgB,YAAY,UAAU,CAAC;CAC7C,OACE,cAAc,WAAW,SAAS,QAAQ,EAAE,IAAI,GAChD,wBAAwB,aAC1B;CACA,OAAO,YAAY,eAAe,EAAE;AACtC;AAIA,SAAgB,IAAI,GAAG,UAAgD;CACrE,IAAI,SAAS,SAAS;CACtB,KAAK,IAAI,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;EACxC,MAAM,IAAI,SAAS;EACnB,SAAS,SAAS,IAAI,SAAS;CACjC;CACA,OAAO;AACT;AAEA,SAAgB,IAAI,GAAG,UAAgD;CACrE,IAAI,SAAS,SAAS;CACtB,KAAK,IAAI,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;EACxC,MAAM,IAAI,SAAS;EACnB,SAAS,SAAS,IAAI,SAAS;CACjC;CACA,OAAO;AACT"}
1
+ {"version":3,"file":"lexi-version.js","names":[],"sources":["../../../../../zero-cache/src/types/lexi-version.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport {parseBigInt} from '../../../shared/src/parse-big-int.ts';\n\n/**\n * A LexiVersion is a lexicographically sortable representation of\n * numbers from 0 to Number.MAX_SAFE_INTEGER (which is the safe range of\n * Version values used in Zero).\n *\n * The Version is first encoded in base36, and then prepended by a single\n * base36 character representing the length (of the base36 version) minus 1.\n * This encoding can encode numbers up to 185 bits, with the maximum encoded\n * number being `\"z\".repeat(37)`, or 36^36-1 (approximately 1.0638735892371651e+56).\n *\n * Examples:\n * * 0 => \"00\"\n * * 10 => \"0a\"\n * * 35 => \"0z\"\n * * 36 => \"110\"\n * * 46655 => \"2zzz\"\n * * 2^64 => \"c3w5e11264sgsg\"\n *\n * Note that when using the `number` type, the library will assert if attempting\n * to encode a Version larger than Number.MAX_SAFE_INTEGER. For large numbers,\n * use the `bigint` type.\n */\nexport type LexiVersion = string;\n\nexport function versionToLexi(v: number | bigint): LexiVersion {\n assert(v >= 0, 'Negative versions are not supported');\n assert(\n typeof v === 'bigint' ||\n (v <= Number.MAX_SAFE_INTEGER && Number.isInteger(v)),\n `Invalid or unsafe version ${v}`,\n );\n const base36Version = BigInt(v).toString(36);\n const length = BigInt(base36Version.length - 1).toString(36);\n assert(\n length.length === 1,\n `Value is too large to be encoded as a LexiVersion: ${v.toString()}`,\n );\n return `${length}${base36Version}`;\n}\n\nexport function versionFromLexi(lexiVersion: LexiVersion): bigint {\n assert(\n lexiVersion.length >= 2,\n () =>\n `LexiVersion must have at least 2 characters, got ${lexiVersion.length}`,\n );\n const length = lexiVersion.substring(0, 1);\n const base36Version = lexiVersion.substring(1);\n assert(\n base36Version.length === parseInt(length, 36) + 1,\n `Invalid LexiVersion: ${lexiVersion}`,\n );\n return parseBigInt(base36Version, 36);\n}\n\nexport type AtLeastOne<T> = [T, ...T[]];\n\nexport function max(...versions: AtLeastOne<LexiVersion>): LexiVersion {\n let winner = versions[0];\n for (let i = 1; i < versions.length; i++) {\n const b = versions[i];\n winner = winner > b ? winner : b;\n }\n return winner;\n}\n\nexport function min(...versions: AtLeastOne<LexiVersion>): LexiVersion {\n let winner = versions[0];\n for (let i = 1; i < versions.length; i++) {\n const b = versions[i];\n winner = winner < b ? winner : b;\n }\n return winner;\n}\n"],"mappings":";;;AA2BA,SAAgB,cAAc,GAAiC;AAC7D,QAAO,KAAK,GAAG,sCAAsC;AACrD,QACE,OAAO,MAAM,YACV,KAAK,OAAO,oBAAoB,OAAO,UAAU,EAAE,EACtD,6BAA6B,IAC9B;CACD,MAAM,gBAAgB,OAAO,EAAE,CAAC,SAAS,GAAG;CAC5C,MAAM,SAAS,OAAO,cAAc,SAAS,EAAE,CAAC,SAAS,GAAG;AAC5D,QACE,OAAO,WAAW,GAClB,sDAAsD,EAAE,UAAU,GACnE;AACD,QAAO,GAAG,SAAS;;AAGrB,SAAgB,gBAAgB,aAAkC;AAChE,QACE,YAAY,UAAU,SAEpB,oDAAoD,YAAY,SACnE;CACD,MAAM,SAAS,YAAY,UAAU,GAAG,EAAE;CAC1C,MAAM,gBAAgB,YAAY,UAAU,EAAE;AAC9C,QACE,cAAc,WAAW,SAAS,QAAQ,GAAG,GAAG,GAChD,wBAAwB,cACzB;AACD,QAAO,YAAY,eAAe,GAAG;;AAKvC,SAAgB,IAAI,GAAG,UAAgD;CACrE,IAAI,SAAS,SAAS;AACtB,MAAK,IAAI,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;EACxC,MAAM,IAAI,SAAS;AACnB,WAAS,SAAS,IAAI,SAAS;;AAEjC,QAAO;;AAGT,SAAgB,IAAI,GAAG,UAAgD;CACrE,IAAI,SAAS,SAAS;AACtB,MAAK,IAAI,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;EACxC,MAAM,IAAI,SAAS;AACnB,WAAS,SAAS,IAAI,SAAS;;AAEjC,QAAO"}
@@ -1 +1 @@
1
- {"version":3,"file":"lite.js","names":[],"sources":["../../../../../zero-cache/src/types/lite.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport {stringify, type JSONValue} from '../../../shared/src/bigint-json.ts';\nimport type {\n SchemaValue,\n ValueType,\n} from '../../../zero-schema/src/table-schema.ts';\nimport type {ColumnSpec, LiteTableSpec} from '../db/specs.ts';\nimport {dataTypeToZqlValueType as upstreamDataTypeToZqlValueType} from './pg-data-type.ts';\nimport type {PostgresValueType} from './pg.ts';\nimport type {RowValue} from './row-key.ts';\n\n/** Javascript value types supported by better-sqlite3. */\nexport type LiteValueType = number | bigint | string | null | Uint8Array;\n\nexport type LiteRow = Readonly<Record<string, LiteValueType>>;\nexport type LiteRowKey = LiteRow; // just for API readability\n\nfunction columnType(col: string, table: LiteTableSpec) {\n const spec = table.columns[col];\n assert(spec, `Unknown column ${col} in table ${table.name}`);\n return spec.dataType;\n}\n\nexport const JSON_STRINGIFIED = 's';\nexport const JSON_PARSED = 'p';\n\nexport type JSONFormat = typeof JSON_STRINGIFIED | typeof JSON_PARSED;\n\n/**\n * Creates a LiteRow from the supplied RowValue. A copy of the `row`\n * is made only if a value conversion is performed.\n */\nexport function liteRow(\n row: RowValue,\n table: LiteTableSpec,\n jsonFormat: JSONFormat,\n): {row: LiteRow; numCols: number} {\n let copyNeeded = false;\n let numCols = 0;\n\n for (const key in row) {\n numCols++;\n const val = row[key];\n const liteVal = liteValue(val, columnType(key, table), jsonFormat);\n if (val !== liteVal) {\n copyNeeded = true;\n break;\n }\n }\n if (!copyNeeded) {\n return {row: row as unknown as LiteRow, numCols};\n }\n // Slow path for when a conversion is needed.\n numCols = 0;\n const converted: Record<string, LiteValueType> = {};\n for (const key in row) {\n numCols++;\n converted[key] = liteValue(row[key], columnType(key, table), jsonFormat);\n }\n return {row: converted, numCols};\n}\n\n/**\n * Postgres values types that are supported by SQLite are stored as-is.\n * This includes Uint8Arrays for the `bytea` / `BLOB` type.\n * * `boolean` values are converted to `0` or `1` integers.\n * * `PreciseDate` values are converted to epoch microseconds.\n * * JSON and Array values are stored as `JSON.stringify()` strings.\n *\n * Note that this currently does not handle the `bytea[]` type, but that's\n * already a pretty questionable type.\n */\nexport function liteValue(\n val: PostgresValueType,\n pgType: string,\n jsonFormat: JSONFormat,\n): LiteValueType {\n if (val instanceof Uint8Array || val === null) {\n return val;\n }\n const valueType = liteTypeToZqlValueType(pgType);\n if (valueType === 'json') {\n if (jsonFormat === JSON_STRINGIFIED && typeof val === 'string') {\n // JSON and JSONB values are already strings if the JSON was not parsed.\n return val;\n }\n // Non-JSON/JSONB values will always appear as objects / arrays.\n return stringify(val);\n }\n const obj = toLiteValue(val);\n return obj && typeof obj === 'object' ? stringify(obj) : obj;\n}\n\nfunction toLiteValue(val: JSONValue): Exclude<JSONValue, boolean> {\n switch (typeof val) {\n case 'string':\n case 'number':\n case 'bigint':\n return val;\n case 'boolean':\n return val ? 1 : 0;\n }\n if (val === null) {\n return val;\n }\n if (Array.isArray(val)) {\n return val.map(v => toLiteValue(v));\n }\n assert(\n val.constructor?.name === 'Object',\n `Unhandled object type ${val.constructor?.name}`,\n );\n return val; // JSON\n}\n\nexport function mapLiteDataTypeToZqlSchemaValue(\n liteDataType: LiteTypeString,\n): SchemaValue {\n return {type: mapLiteDataTypeToZqlValueType(liteDataType)};\n}\n\nfunction mapLiteDataTypeToZqlValueType(dataType: LiteTypeString): ValueType {\n const type = liteTypeToZqlValueType(dataType);\n if (type === undefined) {\n throw new Error(`Unsupported data type ${dataType}`);\n }\n return type;\n}\n\n// Note: Includes the \"TEXT\" substring for SQLite type affinity\nconst TEXT_ENUM_ATTRIBUTE = '|TEXT_ENUM';\nconst NOT_NULL_ATTRIBUTE = '|NOT_NULL';\nexport const TEXT_ARRAY_ATTRIBUTE = '|TEXT_ARRAY';\n\n/**\n * The `LiteTypeString` utilizes SQLite's loose type system to encode\n * auxiliary information about the upstream column (e.g. type and\n * constraints) that does not necessarily affect how SQLite handles the data,\n * but nonetheless determines how higher level logic handles the data.\n *\n * The format of the type string is the original upstream type, followed\n * by any number of attributes, each of which begins with the `|` character.\n * The current list of attributes are:\n * * `|NOT_NULL` to indicate that the upstream column does not allow nulls\n * * `|TEXT_ENUM` to indicate an enum that should be treated as a string\n * * `|TEXT_ARRAY` to indicate an array\n *\n * Examples:\n * * `int8`\n * * `int8|NOT_NULL`\n * * `timestamp with time zone`\n * * `timestamp with time zone|NOT_NULL`\n * * `nomz|TEXT_ENUM`\n * * `nomz|NOT_NULL|TEXT_ENUM`\n * * `int8[]` - Legacy (read support)\n * * `int8[]|TEXT_ARRAY`\n * * `int8|TEXT_ARRAY[]` - Legacy (read support)\n * * `int8[]|TEXT_ARRAY[]` - Legacy (read support)\n * * `int8|NOT_NULL[]` - Legacy (read support)\n * * `nomz[]|TEXT_ENUM|TEXT_ARRAY`\n * * `nomz|TEXT_ENUM[]` - Legacy (read support)\n * * `nomz|TEXT_ENUM|TEXT_ARRAY[]` - Legacy (read support)\n */\nexport type LiteTypeString = string;\n\n/**\n * Formats a {@link LiteTypeString}.\n */\nexport function liteTypeString(\n upstreamDataType: string,\n notNull: boolean | null | undefined,\n textEnum: boolean,\n textArray: boolean,\n): LiteTypeString {\n let typeString = upstreamDataType;\n assert(!typeString.includes('|'), 'Upstream type should not contain |');\n if (notNull) {\n typeString += NOT_NULL_ATTRIBUTE;\n }\n if (textEnum) {\n typeString += TEXT_ENUM_ATTRIBUTE;\n }\n if (textArray) {\n typeString += TEXT_ARRAY_ATTRIBUTE;\n }\n return typeString;\n}\n\nexport function upstreamDataType(liteTypeString: LiteTypeString) {\n const delim = liteTypeString.indexOf('|');\n return delim > 0 ? liteTypeString.substring(0, delim) : liteTypeString;\n}\n\nexport function nullableUpstream(liteTypeString: LiteTypeString) {\n return !liteTypeString.includes(NOT_NULL_ATTRIBUTE);\n}\n\n/**\n * Returns the value type for the `pgDataType` if it is supported by ZQL.\n * (Note that `pgDataType` values are stored as-is in the SQLite column defs).\n *\n * For types not supported by ZQL, returns `undefined`.\n */\nexport function liteTypeToZqlValueType(\n liteTypeString: LiteTypeString,\n): ValueType | undefined {\n return upstreamDataTypeToZqlValueType(\n upstreamDataType(liteTypeString).toLowerCase(),\n liteTypeString.includes(TEXT_ENUM_ATTRIBUTE),\n isArray(liteTypeString),\n );\n}\n\nexport function isEnum(liteTypeString: LiteTypeString) {\n return liteTypeString.includes(TEXT_ENUM_ATTRIBUTE);\n}\n\nexport function isArray(liteTypeString: LiteTypeString) {\n return (\n liteTypeString.includes(TEXT_ARRAY_ATTRIBUTE) ||\n // Before we added `|TEXT_ARRAY`, we just used `[]` suffix to indicate arrays.\n liteTypeString.includes('[]')\n );\n}\n\nconst invalidDataTypeRe = /^.+\\|.*\\[\\]/;\n\nexport function assertValidLiteColumnSpec(spec: ColumnSpec) {\n const {dataType} = spec;\n assert(\n dataType.includes(TEXT_ARRAY_ATTRIBUTE) === dataType.includes('[]'),\n () =>\n `TEXT_ARRAY_ATTRIBUTE and [] must be consistent in dataType: ${dataType}`,\n );\n assert(\n dataType.includes('[]') === (spec.elemPgTypeClass !== null),\n () =>\n `[] in dataType (${dataType}) must match elemPgTypeClass presence (${spec.elemPgTypeClass})`,\n );\n\n // and no [] after |\n assert(!invalidDataTypeRe.test(dataType), `Invalid dataType ${dataType}`);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"lite.js","names":[],"sources":["../../../../../zero-cache/src/types/lite.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport {stringify, type JSONValue} from '../../../shared/src/bigint-json.ts';\nimport type {\n SchemaValue,\n ValueType,\n} from '../../../zero-schema/src/table-schema.ts';\nimport type {ColumnSpec, LiteTableSpec} from '../db/specs.ts';\nimport {dataTypeToZqlValueType as upstreamDataTypeToZqlValueType} from './pg-data-type.ts';\nimport type {PostgresValueType} from './pg.ts';\nimport type {RowValue} from './row-key.ts';\n\n/** Javascript value types supported by better-sqlite3. */\nexport type LiteValueType = number | bigint | string | null | Uint8Array;\n\nexport type LiteRow = Readonly<Record<string, LiteValueType>>;\nexport type LiteRowKey = LiteRow; // just for API readability\n\nfunction columnType(col: string, table: LiteTableSpec) {\n const spec = table.columns[col];\n assert(spec, `Unknown column ${col} in table ${table.name}`);\n return spec.dataType;\n}\n\nexport const JSON_STRINGIFIED = 's';\nexport const JSON_PARSED = 'p';\n\nexport type JSONFormat = typeof JSON_STRINGIFIED | typeof JSON_PARSED;\n\n/**\n * Creates a LiteRow from the supplied RowValue. A copy of the `row`\n * is made only if a value conversion is performed.\n */\nexport function liteRow(\n row: RowValue,\n table: LiteTableSpec,\n jsonFormat: JSONFormat,\n): {row: LiteRow; numCols: number} {\n let copyNeeded = false;\n let numCols = 0;\n\n for (const key in row) {\n numCols++;\n const val = row[key];\n const liteVal = liteValue(val, columnType(key, table), jsonFormat);\n if (val !== liteVal) {\n copyNeeded = true;\n break;\n }\n }\n if (!copyNeeded) {\n return {row: row as unknown as LiteRow, numCols};\n }\n // Slow path for when a conversion is needed.\n numCols = 0;\n const converted: Record<string, LiteValueType> = {};\n for (const key in row) {\n numCols++;\n converted[key] = liteValue(row[key], columnType(key, table), jsonFormat);\n }\n return {row: converted, numCols};\n}\n\n/**\n * Postgres values types that are supported by SQLite are stored as-is.\n * This includes Uint8Arrays for the `bytea` / `BLOB` type.\n * * `boolean` values are converted to `0` or `1` integers.\n * * `PreciseDate` values are converted to epoch microseconds.\n * * JSON and Array values are stored as `JSON.stringify()` strings.\n *\n * Note that this currently does not handle the `bytea[]` type, but that's\n * already a pretty questionable type.\n */\nexport function liteValue(\n val: PostgresValueType,\n pgType: string,\n jsonFormat: JSONFormat,\n): LiteValueType {\n if (val instanceof Uint8Array || val === null) {\n return val;\n }\n const valueType = liteTypeToZqlValueType(pgType);\n if (valueType === 'json') {\n if (jsonFormat === JSON_STRINGIFIED && typeof val === 'string') {\n // JSON and JSONB values are already strings if the JSON was not parsed.\n return val;\n }\n // Non-JSON/JSONB values will always appear as objects / arrays.\n return stringify(val);\n }\n const obj = toLiteValue(val);\n return obj && typeof obj === 'object' ? stringify(obj) : obj;\n}\n\nfunction toLiteValue(val: JSONValue): Exclude<JSONValue, boolean> {\n switch (typeof val) {\n case 'string':\n case 'number':\n case 'bigint':\n return val;\n case 'boolean':\n return val ? 1 : 0;\n }\n if (val === null) {\n return val;\n }\n if (Array.isArray(val)) {\n return val.map(v => toLiteValue(v));\n }\n assert(\n val.constructor?.name === 'Object',\n `Unhandled object type ${val.constructor?.name}`,\n );\n return val; // JSON\n}\n\nexport function mapLiteDataTypeToZqlSchemaValue(\n liteDataType: LiteTypeString,\n): SchemaValue {\n return {type: mapLiteDataTypeToZqlValueType(liteDataType)};\n}\n\nfunction mapLiteDataTypeToZqlValueType(dataType: LiteTypeString): ValueType {\n const type = liteTypeToZqlValueType(dataType);\n if (type === undefined) {\n throw new Error(`Unsupported data type ${dataType}`);\n }\n return type;\n}\n\n// Note: Includes the \"TEXT\" substring for SQLite type affinity\nconst TEXT_ENUM_ATTRIBUTE = '|TEXT_ENUM';\nconst NOT_NULL_ATTRIBUTE = '|NOT_NULL';\nexport const TEXT_ARRAY_ATTRIBUTE = '|TEXT_ARRAY';\n\n/**\n * The `LiteTypeString` utilizes SQLite's loose type system to encode\n * auxiliary information about the upstream column (e.g. type and\n * constraints) that does not necessarily affect how SQLite handles the data,\n * but nonetheless determines how higher level logic handles the data.\n *\n * The format of the type string is the original upstream type, followed\n * by any number of attributes, each of which begins with the `|` character.\n * The current list of attributes are:\n * * `|NOT_NULL` to indicate that the upstream column does not allow nulls\n * * `|TEXT_ENUM` to indicate an enum that should be treated as a string\n * * `|TEXT_ARRAY` to indicate an array\n *\n * Examples:\n * * `int8`\n * * `int8|NOT_NULL`\n * * `timestamp with time zone`\n * * `timestamp with time zone|NOT_NULL`\n * * `nomz|TEXT_ENUM`\n * * `nomz|NOT_NULL|TEXT_ENUM`\n * * `int8[]` - Legacy (read support)\n * * `int8[]|TEXT_ARRAY`\n * * `int8|TEXT_ARRAY[]` - Legacy (read support)\n * * `int8[]|TEXT_ARRAY[]` - Legacy (read support)\n * * `int8|NOT_NULL[]` - Legacy (read support)\n * * `nomz[]|TEXT_ENUM|TEXT_ARRAY`\n * * `nomz|TEXT_ENUM[]` - Legacy (read support)\n * * `nomz|TEXT_ENUM|TEXT_ARRAY[]` - Legacy (read support)\n */\nexport type LiteTypeString = string;\n\n/**\n * Formats a {@link LiteTypeString}.\n */\nexport function liteTypeString(\n upstreamDataType: string,\n notNull: boolean | null | undefined,\n textEnum: boolean,\n textArray: boolean,\n): LiteTypeString {\n let typeString = upstreamDataType;\n assert(!typeString.includes('|'), 'Upstream type should not contain |');\n if (notNull) {\n typeString += NOT_NULL_ATTRIBUTE;\n }\n if (textEnum) {\n typeString += TEXT_ENUM_ATTRIBUTE;\n }\n if (textArray) {\n typeString += TEXT_ARRAY_ATTRIBUTE;\n }\n return typeString;\n}\n\nexport function upstreamDataType(liteTypeString: LiteTypeString) {\n const delim = liteTypeString.indexOf('|');\n return delim > 0 ? liteTypeString.substring(0, delim) : liteTypeString;\n}\n\nexport function nullableUpstream(liteTypeString: LiteTypeString) {\n return !liteTypeString.includes(NOT_NULL_ATTRIBUTE);\n}\n\n/**\n * Returns the value type for the `pgDataType` if it is supported by ZQL.\n * (Note that `pgDataType` values are stored as-is in the SQLite column defs).\n *\n * For types not supported by ZQL, returns `undefined`.\n */\nexport function liteTypeToZqlValueType(\n liteTypeString: LiteTypeString,\n): ValueType | undefined {\n return upstreamDataTypeToZqlValueType(\n upstreamDataType(liteTypeString).toLowerCase(),\n liteTypeString.includes(TEXT_ENUM_ATTRIBUTE),\n isArray(liteTypeString),\n );\n}\n\nexport function isEnum(liteTypeString: LiteTypeString) {\n return liteTypeString.includes(TEXT_ENUM_ATTRIBUTE);\n}\n\nexport function isArray(liteTypeString: LiteTypeString) {\n return (\n liteTypeString.includes(TEXT_ARRAY_ATTRIBUTE) ||\n // Before we added `|TEXT_ARRAY`, we just used `[]` suffix to indicate arrays.\n liteTypeString.includes('[]')\n );\n}\n\nconst invalidDataTypeRe = /^.+\\|.*\\[\\]/;\n\nexport function assertValidLiteColumnSpec(spec: ColumnSpec) {\n const {dataType} = spec;\n assert(\n dataType.includes(TEXT_ARRAY_ATTRIBUTE) === dataType.includes('[]'),\n () =>\n `TEXT_ARRAY_ATTRIBUTE and [] must be consistent in dataType: ${dataType}`,\n );\n assert(\n dataType.includes('[]') === (spec.elemPgTypeClass !== null),\n () =>\n `[] in dataType (${dataType}) must match elemPgTypeClass presence (${spec.elemPgTypeClass})`,\n );\n\n // and no [] after |\n assert(!invalidDataTypeRe.test(dataType), `Invalid dataType ${dataType}`);\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"names.js","names":[],"sources":["../../../../../zero-cache/src/types/names.ts"],"sourcesContent":["export function liteTableName(t: {schema: string; name: string}) {\n return t.schema === 'public' ? t.name : `${t.schema}.${t.name}`;\n}\n"],"mappings":";AAAA,SAAgB,cAAc,GAAmC;CAC/D,OAAO,EAAE,WAAW,WAAW,EAAE,OAAO,GAAG,EAAE,OAAO,GAAG,EAAE;AAC3D"}
1
+ {"version":3,"file":"names.js","names":[],"sources":["../../../../../zero-cache/src/types/names.ts"],"sourcesContent":["export function liteTableName(t: {schema: string; name: string}) {\n return t.schema === 'public' ? t.name : `${t.schema}.${t.name}`;\n}\n"],"mappings":";AAAA,SAAgB,cAAc,GAAmC;AAC/D,QAAO,EAAE,WAAW,WAAW,EAAE,OAAO,GAAG,EAAE,OAAO,GAAG,EAAE"}
@@ -1 +1 @@
1
- {"version":3,"file":"pg-data-type.js","names":[],"sources":["../../../../../zero-cache/src/types/pg-data-type.ts"],"sourcesContent":["import type {ValueType} from '../../../zero-protocol/src/client-schema.ts';\n\nexport const pgToZqlNumericTypeMap = Object.freeze({\n 'smallint': 'number',\n 'integer': 'number',\n 'int': 'number',\n 'int2': 'number',\n 'int4': 'number',\n 'int8': 'number',\n 'bigint': 'number',\n 'smallserial': 'number',\n 'serial': 'number',\n 'serial2': 'number',\n 'serial4': 'number',\n 'serial8': 'number',\n 'bigserial': 'number',\n 'decimal': 'number',\n 'numeric': 'number',\n 'real': 'number',\n 'double precision': 'number',\n 'float': 'number',\n 'float4': 'number',\n 'float8': 'number',\n});\n\nexport function isPgNumberType(pgType: string): boolean {\n return Object.hasOwn(pgToZqlNumericTypeMap, formatTypeForLookup(pgType));\n}\n\nexport const pgToZqlStringTypeMap = Object.freeze({\n 'bpchar': 'string',\n 'character': 'string',\n 'character varying': 'string',\n 'text': 'string',\n 'uuid': 'string',\n 'varchar': 'string',\n});\n\nexport function isPgStringType(pgType: string): boolean {\n return Object.hasOwn(pgToZqlStringTypeMap, formatTypeForLookup(pgType));\n}\n\nexport const pgToZqlTypeMap = Object.freeze({\n // Numeric types\n ...pgToZqlNumericTypeMap,\n\n // Date/Time types\n 'date': 'number',\n 'time': 'number',\n 'timetz': 'number',\n 'time with time zone': 'number',\n 'time without time zone': 'number',\n 'timestamp': 'number',\n 'timestamptz': 'number',\n 'timestamp with time zone': 'number',\n 'timestamp without time zone': 'number',\n\n // String types\n ...pgToZqlStringTypeMap,\n\n // Boolean types\n 'bool': 'boolean',\n 'boolean': 'boolean',\n\n 'json': 'json',\n 'jsonb': 'json',\n\n // TODO: Add support for these.\n // 'bytea':\n});\n\nexport function dataTypeToZqlValueType(\n pgType: string,\n isEnum: boolean,\n isArray: boolean,\n): ValueType | undefined {\n // We treat pg arrays as JSON values.\n if (isArray) {\n return 'json';\n }\n\n const valueType = (pgToZqlTypeMap as Record<string, ValueType>)[\n formatTypeForLookup(pgType)\n ];\n if (valueType === undefined && isEnum) {\n return 'string';\n }\n return valueType;\n}\n\n// Strips args (i.e. (32) in char(32)) and lowercases.\nfunction formatTypeForLookup(pgType: string): string {\n const startOfArgs = pgType.indexOf('(');\n if (startOfArgs === -1) {\n return pgType.toLocaleLowerCase();\n }\n return pgType.toLocaleLowerCase().substring(0, startOfArgs);\n}\n"],"mappings":";AAEA,IAAa,wBAAwB,OAAO,OAAO;CACjD,YAAY;CACZ,WAAW;CACX,OAAO;CACP,QAAQ;CACR,QAAQ;CACR,QAAQ;CACR,UAAU;CACV,eAAe;CACf,UAAU;CACV,WAAW;CACX,WAAW;CACX,WAAW;CACX,aAAa;CACb,WAAW;CACX,WAAW;CACX,QAAQ;CACR,oBAAoB;CACpB,SAAS;CACT,UAAU;CACV,UAAU;AACZ,CAAC;AAED,SAAgB,eAAe,QAAyB;CACtD,OAAO,OAAO,OAAO,uBAAuB,oBAAoB,MAAM,CAAC;AACzE;AAEA,IAAa,uBAAuB,OAAO,OAAO;CAChD,UAAU;CACV,aAAa;CACb,qBAAqB;CACrB,QAAQ;CACR,QAAQ;CACR,WAAW;AACb,CAAC;AAED,SAAgB,eAAe,QAAyB;CACtD,OAAO,OAAO,OAAO,sBAAsB,oBAAoB,MAAM,CAAC;AACxE;AAEA,IAAa,iBAAiB,OAAO,OAAO;CAE1C,GAAG;CAGH,QAAQ;CACR,QAAQ;CACR,UAAU;CACV,uBAAuB;CACvB,0BAA0B;CAC1B,aAAa;CACb,eAAe;CACf,4BAA4B;CAC5B,+BAA+B;CAG/B,GAAG;CAGH,QAAQ;CACR,WAAW;CAEX,QAAQ;CACR,SAAS;AAIX,CAAC;AAED,SAAgB,uBACd,QACA,QACA,SACuB;CAEvB,IAAI,SACF,OAAO;CAGT,MAAM,YAAa,eACjB,oBAAoB,MAAM;CAE5B,IAAI,cAAc,KAAA,KAAa,QAC7B,OAAO;CAET,OAAO;AACT;AAGA,SAAS,oBAAoB,QAAwB;CACnD,MAAM,cAAc,OAAO,QAAQ,GAAG;CACtC,IAAI,gBAAgB,IAClB,OAAO,OAAO,kBAAkB;CAElC,OAAO,OAAO,kBAAkB,EAAE,UAAU,GAAG,WAAW;AAC5D"}
1
+ {"version":3,"file":"pg-data-type.js","names":[],"sources":["../../../../../zero-cache/src/types/pg-data-type.ts"],"sourcesContent":["import type {ValueType} from '../../../zero-protocol/src/client-schema.ts';\n\nexport const pgToZqlNumericTypeMap = Object.freeze({\n 'smallint': 'number',\n 'integer': 'number',\n 'int': 'number',\n 'int2': 'number',\n 'int4': 'number',\n 'int8': 'number',\n 'bigint': 'number',\n 'smallserial': 'number',\n 'serial': 'number',\n 'serial2': 'number',\n 'serial4': 'number',\n 'serial8': 'number',\n 'bigserial': 'number',\n 'decimal': 'number',\n 'numeric': 'number',\n 'real': 'number',\n 'double precision': 'number',\n 'float': 'number',\n 'float4': 'number',\n 'float8': 'number',\n});\n\nexport function isPgNumberType(pgType: string): boolean {\n return Object.hasOwn(pgToZqlNumericTypeMap, formatTypeForLookup(pgType));\n}\n\nexport const pgToZqlStringTypeMap = Object.freeze({\n 'bpchar': 'string',\n 'character': 'string',\n 'character varying': 'string',\n 'text': 'string',\n 'uuid': 'string',\n 'varchar': 'string',\n});\n\nexport function isPgStringType(pgType: string): boolean {\n return Object.hasOwn(pgToZqlStringTypeMap, formatTypeForLookup(pgType));\n}\n\nexport const pgToZqlTypeMap = Object.freeze({\n // Numeric types\n ...pgToZqlNumericTypeMap,\n\n // Date/Time types\n 'date': 'number',\n 'time': 'number',\n 'timetz': 'number',\n 'time with time zone': 'number',\n 'time without time zone': 'number',\n 'timestamp': 'number',\n 'timestamptz': 'number',\n 'timestamp with time zone': 'number',\n 'timestamp without time zone': 'number',\n\n // String types\n ...pgToZqlStringTypeMap,\n\n // Boolean types\n 'bool': 'boolean',\n 'boolean': 'boolean',\n\n 'json': 'json',\n 'jsonb': 'json',\n\n // TODO: Add support for these.\n // 'bytea':\n});\n\nexport function dataTypeToZqlValueType(\n pgType: string,\n isEnum: boolean,\n isArray: boolean,\n): ValueType | undefined {\n // We treat pg arrays as JSON values.\n if (isArray) {\n return 'json';\n }\n\n const valueType = (pgToZqlTypeMap as Record<string, ValueType>)[\n formatTypeForLookup(pgType)\n ];\n if (valueType === undefined && isEnum) {\n return 'string';\n }\n return valueType;\n}\n\n// Strips args (i.e. (32) in char(32)) and lowercases.\nfunction formatTypeForLookup(pgType: string): string {\n const startOfArgs = pgType.indexOf('(');\n if (startOfArgs === -1) {\n return pgType.toLocaleLowerCase();\n }\n return pgType.toLocaleLowerCase().substring(0, startOfArgs);\n}\n"],"mappings":";AAEA,IAAa,wBAAwB,OAAO,OAAO;CACjD,YAAY;CACZ,WAAW;CACX,OAAO;CACP,QAAQ;CACR,QAAQ;CACR,QAAQ;CACR,UAAU;CACV,eAAe;CACf,UAAU;CACV,WAAW;CACX,WAAW;CACX,WAAW;CACX,aAAa;CACb,WAAW;CACX,WAAW;CACX,QAAQ;CACR,oBAAoB;CACpB,SAAS;CACT,UAAU;CACV,UAAU;CACX,CAAC;AAEF,SAAgB,eAAe,QAAyB;AACtD,QAAO,OAAO,OAAO,uBAAuB,oBAAoB,OAAO,CAAC;;AAG1E,IAAa,uBAAuB,OAAO,OAAO;CAChD,UAAU;CACV,aAAa;CACb,qBAAqB;CACrB,QAAQ;CACR,QAAQ;CACR,WAAW;CACZ,CAAC;AAEF,SAAgB,eAAe,QAAyB;AACtD,QAAO,OAAO,OAAO,sBAAsB,oBAAoB,OAAO,CAAC;;AAGzE,IAAa,iBAAiB,OAAO,OAAO;CAE1C,GAAG;CAGH,QAAQ;CACR,QAAQ;CACR,UAAU;CACV,uBAAuB;CACvB,0BAA0B;CAC1B,aAAa;CACb,eAAe;CACf,4BAA4B;CAC5B,+BAA+B;CAG/B,GAAG;CAGH,QAAQ;CACR,WAAW;CAEX,QAAQ;CACR,SAAS;CAIV,CAAC;AAEF,SAAgB,uBACd,QACA,QACA,SACuB;AAEvB,KAAI,QACF,QAAO;CAGT,MAAM,YAAa,eACjB,oBAAoB,OAAO;AAE7B,KAAI,cAAc,KAAA,KAAa,OAC7B,QAAO;AAET,QAAO;;AAIT,SAAS,oBAAoB,QAAwB;CACnD,MAAM,cAAc,OAAO,QAAQ,IAAI;AACvC,KAAI,gBAAgB,GAClB,QAAO,OAAO,mBAAmB;AAEnC,QAAO,OAAO,mBAAmB,CAAC,UAAU,GAAG,YAAY"}
@@ -1 +1 @@
1
- {"version":3,"file":"pg.js","names":[],"sources":["../../../../../zero-cache/src/types/pg.ts"],"sourcesContent":["import {\n PG_CONFIGURATION_LIMIT_EXCEEDED,\n PG_CONNECTION_DOES_NOT_EXIST,\n PG_CONNECTION_EXCEPTION,\n PG_CONNECTION_FAILURE,\n PG_INSUFFICIENT_PRIVILEGE,\n PG_INVALID_AUTHORIZATION_SPECIFICATION,\n PG_INVALID_CATALOG_NAME,\n PG_INVALID_PASSWORD,\n PG_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION,\n PG_TOO_MANY_CONNECTIONS,\n} from '@drdgvhbh/postgres-error-codes';\nimport {PreciseDate} from '@google-cloud/precise-date';\nimport {OID} from '@postgresql-typed/oids';\nimport type {LogContext} from '@rocicorp/logger';\nimport postgres, {type Notice, type PostgresType} from 'postgres';\nimport {BigIntJSON, type JSONValue} from '../../../shared/src/bigint-json.ts';\nimport {randInt} from '../../../shared/src/rand.ts';\nimport {ConfigurationError} from './configuration-error.ts';\nimport {\n DATE,\n JSON,\n JSONB,\n NUMERIC,\n TIME,\n TIMESTAMP,\n TIMESTAMPTZ,\n TIMETZ,\n} from './pg-types.ts';\n\n// Matches: YEAR-MM-DD HH:MM:SS[.fraction][+-TZ[:MM]][ BC]\nconst pgTimestampRe =\n /^(\\d+)-(\\d{2}-\\d{2}) (\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?)([+-]\\d{1,2}(?::\\d{2})?)?(?: BC)?$/;\n\n// exported for testing.\nexport function timestampToFpMillis(timestamp: string): number {\n if (timestamp === 'infinity') return Infinity;\n if (timestamp === '-infinity') return -Infinity;\n const match = timestamp.match(pgTimestampRe);\n if (!match) {\n throw new Error(`Error parsing ${timestamp}`);\n }\n\n const [, yearStr, monthDay, time, tz] = match;\n const bc = timestamp.endsWith(' BC');\n\n let year = Number(yearStr);\n if (bc) {\n // Postgres: 1 BC = JS year 0, 2 BC = JS year -1, N BC = -(N-1)\n year = -(year - 1);\n }\n\n // Format year as ISO 8601 expanded year if needed.\n // https://tc39.es/ecma262/#sec-expanded-years\n let isoYear: string;\n if (year >= 0 && year <= 9999) {\n isoYear = String(year).padStart(4, '0');\n } else if (year >= 0) {\n isoYear = '+' + String(year).padStart(6, '0');\n } else {\n isoYear = '-' + String(Math.abs(year)).padStart(6, '0');\n }\n\n // Build a UTC ISO string so PreciseDate returns microsecond precision.\n const utcString = `${isoYear}-${monthDay}T${time}Z`;\n\n try {\n const fullTime = new PreciseDate(utcString).getFullTime();\n const millis = Number(fullTime / 1_000_000n);\n const nanos = Number(fullTime % 1_000_000n);\n const ret = millis + nanos * 1e-6; // floating point milliseconds\n\n // Add back in the timezone offset. We passed local time as UTC,\n // so a positive offset means we need to subtract, and vice versa.\n if (tz) {\n const positiveOffset = tz.startsWith('+');\n const [hours, minutes] = tz.split(':');\n const offset =\n Math.abs(Number(hours)) * 60 + (minutes ? Number(minutes) : 0);\n const offsetMillis = offset * 60 * 1_000;\n return positiveOffset ? ret - offsetMillis : ret + offsetMillis;\n }\n return ret;\n } catch (e) {\n throw new Error(`Error parsing ${timestamp}`, {cause: e});\n }\n}\n\nfunction serializeTimestamp(val: unknown): string {\n switch (typeof val) {\n case 'string':\n return val; // Let Postgres parse it\n case 'number': {\n if (Number.isInteger(val)) {\n return new PreciseDate(val).toISOString();\n }\n // Convert floating point to bigint nanoseconds.\n const nanoseconds =\n 1_000_000n * BigInt(Math.trunc(val)) +\n BigInt(Math.trunc((val % 1) * 1e6));\n return new PreciseDate(nanoseconds).toISOString();\n }\n // Note: Don't support bigint inputs until we decide what the semantics are (e.g. micros vs nanos)\n // case 'bigint':\n // return new PreciseDate(val).toISOString();\n default:\n if (val instanceof Date) {\n return val.toISOString();\n }\n }\n throw new Error(`Unsupported type \"${typeof val}\" for timestamp: ${val}`);\n}\n\nconst MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000;\n\nfunction serializeTime(x: unknown, type: 'time' | 'timetz'): string {\n switch (typeof x) {\n case 'string':\n return x; // Let Postgres parse it\n case 'number':\n return millisecondsToPostgresTime(x);\n }\n throw new Error(`Unsupported type \"${typeof x}\" for ${type}: ${x}`);\n}\n\nexport function millisecondsToPostgresTime(milliseconds: number): string {\n if (milliseconds < 0) {\n throw new Error('Milliseconds cannot be negative');\n }\n\n if (milliseconds >= MILLISECONDS_PER_DAY) {\n throw new Error(\n `Milliseconds cannot exceed 24 hours (${MILLISECONDS_PER_DAY}ms)`,\n );\n }\n\n milliseconds = Math.floor(milliseconds); // Ensure it's an integer\n\n const totalSeconds = Math.floor(milliseconds / 1000);\n const hours = Math.floor(totalSeconds / 3600);\n const minutes = Math.floor((totalSeconds % 3600) / 60);\n const seconds = totalSeconds % 60;\n const ms = milliseconds % 1000;\n\n return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}.${ms.toString().padStart(3, '0')}+00`;\n}\n\n// Regular expression to match HH:MM:SS, HH:MM:SS.mmm, or HH:MM:SS+00 / HH:MM:SS.mmm+00\n// Supports optional timezone offset\nconst timeRegex =\n /^(\\d{1,2}):(\\d{2}):(\\d{2})(?:\\.(\\d{1,6}))?(?:([+-])(\\d{1,2})(?::(\\d{2}))?)?$/;\n\nexport function postgresTimeToMilliseconds(timeString: string): number {\n // Validate basic format\n if (!timeString || typeof timeString !== 'string') {\n throw new Error('Invalid time string: must be a non-empty string');\n }\n\n const match = timeString.match(timeRegex);\n\n if (!match) {\n throw new Error(\n `Invalid time format: \"${timeString}\". Expected HH:MM:SS[.mmm][+|-HH[:MM]]`,\n );\n }\n\n // Extract components\n const hours = parseInt(match[1], 10);\n const minutes = parseInt(match[2], 10);\n const seconds = parseInt(match[3], 10);\n // Handle optional milliseconds, pad right with zeros if needed\n let milliseconds = 0;\n if (match[4]) {\n // Pad microseconds to 6 digits\n const msString = match[4].padEnd(6, '0');\n // slice milliseconds out of the microseconds\n // e.g. 123456 -> 123, 1234 -> 123,\n milliseconds = parseInt(msString.slice(0, 3), 10);\n }\n\n // Validate ranges\n if (hours < 0 || hours > 24) {\n throw new Error(\n `Invalid hours: ${hours}. Must be between 0 and 24 (24 means end of day)`,\n );\n }\n\n if (minutes < 0 || minutes >= 60) {\n throw new Error(`Invalid minutes: ${minutes}. Must be between 0 and 59`);\n }\n\n if (seconds < 0 || seconds >= 60) {\n throw new Error(`Invalid seconds: ${seconds}. Must be between 0 and 59`);\n }\n\n if (milliseconds < 0 || milliseconds >= 1000) {\n throw new Error(\n `Invalid milliseconds: ${milliseconds}. Must be between 0 and 999`,\n );\n }\n\n // Special case: PostgreSQL allows 24:00:00 to represent end of day\n if (hours === 24 && (minutes !== 0 || seconds !== 0 || milliseconds !== 0)) {\n throw new Error(\n 'Invalid time: when hours is 24, minutes, seconds, and milliseconds must be 0',\n );\n }\n\n // Calculate total milliseconds\n let totalMs =\n hours * 3600000 + minutes * 60000 + seconds * 1000 + milliseconds;\n\n // Timezone Offset\n if (match[5]) {\n const sign = match[5] === '+' ? 1 : -1;\n const tzHours = parseInt(match[6], 10);\n const tzMinutes = match[7] ? parseInt(match[7], 10) : 0;\n const offsetMs = sign * (tzHours * 3600000 + tzMinutes * 60000);\n totalMs -= offsetMs;\n }\n\n // Normalize to 0-24h only if outside valid range\n if (totalMs > MILLISECONDS_PER_DAY || totalMs < 0) {\n return (\n ((totalMs % MILLISECONDS_PER_DAY) + MILLISECONDS_PER_DAY) %\n MILLISECONDS_PER_DAY\n );\n }\n\n return totalMs;\n}\n\nfunction dateToUTCMidnight(date: string): number {\n if (date === 'infinity') return Infinity;\n if (date === '-infinity') return -Infinity;\n const d = new Date(date);\n return Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate());\n}\n\n/**\n * The (javascript) types of objects that can be returned by our configured\n * Postgres clients. For initial-sync, these comes from the postgres.js client:\n *\n * https://github.com/porsager/postgres/blob/master/src/types.js\n *\n * and for the replication stream these come from the the node-postgres client:\n *\n * https://github.com/brianc/node-pg-types/blob/master/lib/textParsers.js\n */\nexport type PostgresValueType = JSONValue | Uint8Array;\n\nexport type TypeOptions = {\n /**\n * Sends strings directly as JSON values (i.e. without JSON stringification).\n * The application is responsible for ensuring that string inputs for JSON\n * columns are already stringified. Other data types (e.g. objects) will\n * still be stringified by the pg client.\n */\n sendStringAsJson?: boolean;\n};\n\n/**\n * Configures types for the Postgres.js client library (`postgres`).\n *\n * @param jsonAsString Keep JSON / JSONB values as strings instead of parsing.\n */\nexport const postgresTypeConfig = ({sendStringAsJson}: TypeOptions = {}) => ({\n // Type the type IDs as `number` so that Typescript doesn't complain about\n // referencing external types during type inference.\n types: {\n bigint: postgres.BigInt,\n json: {\n to: JSON,\n from: [JSON, JSONB],\n serialize: sendStringAsJson\n ? (x: unknown) => (typeof x === 'string' ? x : BigIntJSON.stringify(x))\n : BigIntJSON.stringify,\n parse: BigIntJSON.parse,\n },\n // Timestamps are converted to PreciseDate objects.\n timestamp: {\n to: TIMESTAMP,\n from: [TIMESTAMP, TIMESTAMPTZ],\n serialize: serializeTimestamp,\n parse: timestampToFpMillis,\n },\n // Times are converted as strings\n time: {\n to: TIME,\n from: [TIME, TIMETZ],\n serialize: (x: unknown) => serializeTime(x, 'time'),\n parse: postgresTimeToMilliseconds,\n },\n\n timetz: {\n to: TIMETZ,\n from: [TIME, TIMETZ],\n serialize: (x: unknown) => serializeTime(x, 'timetz'),\n parse: postgresTimeToMilliseconds,\n },\n\n // The DATE type is stored directly as the PG normalized date string.\n date: {\n to: DATE,\n from: [DATE],\n serialize: (x: string | Date) =>\n (x instanceof Date ? x : new Date(x)).toISOString(),\n parse: dateToUTCMidnight,\n },\n // Returns a `js` number which can lose precision for large numbers.\n // JS number is 53 bits so this should generally not occur.\n // An API will be provided for users to override this type.\n numeric: {\n to: NUMERIC,\n from: [NUMERIC],\n serialize: (x: number) => String(x), // pg expects a string\n parse: (x: string | number) => Number(x),\n },\n },\n});\n\nexport type PostgresDB = postgres.Sql<{\n bigint: bigint;\n json: JSONValue;\n}>;\n\nexport type PostgresTransaction = postgres.TransactionSql<{\n bigint: bigint;\n json: JSONValue;\n}>;\n\nexport function pgClient(\n lc: LogContext,\n connectionURI: string,\n // A descriptive name for the code that is making the connection, so that we\n // can debug things like deadlocks.\n applicationName: string,\n options?: postgres.Options<{\n bigint: PostgresType<bigint>;\n json: PostgresType<JSONValue>;\n }>,\n opts?: TypeOptions,\n): PostgresDB {\n applicationName = `zero-${applicationName}`;\n\n const onnotice = (n: Notice) => {\n // https://www.postgresql.org/docs/current/plpgsql-errors-and-messages.html#PLPGSQL-STATEMENTS-RAISE\n switch (n.severity) {\n case 'NOTICE':\n return; // silenced\n case 'DEBUG':\n lc.debug?.(n);\n return;\n case 'WARNING':\n lc.warn?.(n);\n return;\n case 'EXCEPTION':\n lc.error?.(n);\n return;\n case 'LOG':\n case 'INFO':\n default:\n lc.info?.(n);\n }\n };\n const url = new URL(connectionURI);\n const sslFlag =\n url.searchParams.get('ssl') ?? url.searchParams.get('sslmode') ?? 'prefer';\n\n let ssl: boolean | 'prefer' | {rejectUnauthorized: boolean};\n if (sslFlag === 'disable' || sslFlag === 'false') {\n ssl = false;\n } else if (sslFlag === 'no-verify') {\n ssl = {rejectUnauthorized: false};\n } else {\n ssl = sslFlag as 'prefer';\n }\n\n // Set connections to expire between 5 and 10 minutes to free up state on PG.\n const maxLifetimeSeconds = randInt(5 * 60, 10 * 60);\n const providedConnection = options?.connection ?? {};\n\n return postgres(connectionURI, {\n ...postgresTypeConfig(opts),\n onnotice,\n ['max_lifetime']: maxLifetimeSeconds,\n ssl,\n ...options,\n connection: {\n ...providedConnection,\n ['application_name']: applicationName,\n },\n });\n}\n\nexport async function connectPgClient(\n ...args: Parameters<typeof pgClient>\n): Promise<PostgresDB> {\n const db = pgClient(...args);\n try {\n await db`SELECT 1`.simple().execute();\n return db;\n } catch (e) {\n if (isPostgresConfigError(e)) {\n throw new ConfigurationError(\n 'Unable to connect to Postgres. Check your database configuration.',\n {cause: e},\n );\n }\n throw e;\n }\n}\n\n/**\n * Disables any statement_timeout for the current transaction. By default,\n * Postgres does not impose a statement timeout, but some users and providers\n * set one at the database level (even though it is explicitly discouraged by\n * 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 migrations\n * and background cleanup, the statement timeout should be disabled to prevent\n * these operations from timing out.\n */\nexport function disableStatementTimeout(sql: PostgresTransaction) {\n void sql`SET LOCAL statement_timeout = 0;`.execute().catch(() => {});\n}\n\nexport const typeNameByOID: Record<number, string> = Object.freeze(\n Object.fromEntries(\n Object.entries(OID).map(([name, oid]) => [\n oid,\n name.startsWith('_') ? `${name.substring(1)}[]` : name,\n ]),\n ),\n);\n\nexport function isPostgresError(e: unknown, ...codes: [string, ...string[]]) {\n return e instanceof postgres.PostgresError && codes.includes(e.code);\n}\n\nfunction isPostgresStartupConnectionError(e: unknown) {\n if (!(e instanceof Error) || !('code' in e)) {\n return false;\n }\n const {code} = e;\n // postgres.js throws plain Error instances for connection failures that\n // happen before Postgres can return a SQLSTATE. These are still startup\n // configuration problems for Zero: wrong host/port, unreachable DB, DNS,\n // TLS certificate mismatch, etc.\n return (\n typeof code === 'string' &&\n [\n 'CONNECT_TIMEOUT',\n 'ECONNREFUSED',\n 'ECONNRESET',\n 'EHOSTUNREACH',\n 'ENETUNREACH',\n 'ENOTFOUND',\n 'EAI_AGAIN',\n 'ETIMEDOUT',\n 'ERR_TLS_CERT_ALTNAME_INVALID',\n 'SELF_SIGNED_CERT_IN_CHAIN',\n 'DEPTH_ZERO_SELF_SIGNED_CERT',\n 'UNABLE_TO_VERIFY_LEAF_SIGNATURE',\n ].includes(code)\n );\n}\n\nexport function isPostgresConfigError(e: unknown) {\n return (\n isPostgresStartupConnectionError(e) ||\n isPostgresError(\n e,\n PG_CONNECTION_EXCEPTION,\n PG_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION,\n PG_CONNECTION_DOES_NOT_EXIST,\n PG_CONNECTION_FAILURE,\n PG_INVALID_AUTHORIZATION_SPECIFICATION,\n PG_INVALID_PASSWORD,\n PG_INVALID_CATALOG_NAME,\n PG_INSUFFICIENT_PRIVILEGE,\n PG_TOO_MANY_CONNECTIONS,\n PG_CONFIGURATION_LIMIT_EXCEEDED,\n )\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"pg.js","names":[],"sources":["../../../../../zero-cache/src/types/pg.ts"],"sourcesContent":["import {\n PG_CONFIGURATION_LIMIT_EXCEEDED,\n PG_CONNECTION_DOES_NOT_EXIST,\n PG_CONNECTION_EXCEPTION,\n PG_CONNECTION_FAILURE,\n PG_INSUFFICIENT_PRIVILEGE,\n PG_INVALID_AUTHORIZATION_SPECIFICATION,\n PG_INVALID_CATALOG_NAME,\n PG_INVALID_PASSWORD,\n PG_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION,\n PG_TOO_MANY_CONNECTIONS,\n} from '@drdgvhbh/postgres-error-codes';\nimport {PreciseDate} from '@google-cloud/precise-date';\nimport {OID} from '@postgresql-typed/oids';\nimport type {LogContext} from '@rocicorp/logger';\nimport postgres, {type Notice, type PostgresType} from 'postgres';\nimport {BigIntJSON, type JSONValue} from '../../../shared/src/bigint-json.ts';\nimport {randInt} from '../../../shared/src/rand.ts';\nimport {ConfigurationError} from './configuration-error.ts';\nimport {\n DATE,\n JSON,\n JSONB,\n NUMERIC,\n TIME,\n TIMESTAMP,\n TIMESTAMPTZ,\n TIMETZ,\n} from './pg-types.ts';\n\n// Matches: YEAR-MM-DD HH:MM:SS[.fraction][+-TZ[:MM]][ BC]\nconst pgTimestampRe =\n /^(\\d+)-(\\d{2}-\\d{2}) (\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?)([+-]\\d{1,2}(?::\\d{2})?)?(?: BC)?$/;\n\n// exported for testing.\nexport function timestampToFpMillis(timestamp: string): number {\n if (timestamp === 'infinity') return Infinity;\n if (timestamp === '-infinity') return -Infinity;\n const match = timestamp.match(pgTimestampRe);\n if (!match) {\n throw new Error(`Error parsing ${timestamp}`);\n }\n\n const [, yearStr, monthDay, time, tz] = match;\n const bc = timestamp.endsWith(' BC');\n\n let year = Number(yearStr);\n if (bc) {\n // Postgres: 1 BC = JS year 0, 2 BC = JS year -1, N BC = -(N-1)\n year = -(year - 1);\n }\n\n // Format year as ISO 8601 expanded year if needed.\n // https://tc39.es/ecma262/#sec-expanded-years\n let isoYear: string;\n if (year >= 0 && year <= 9999) {\n isoYear = String(year).padStart(4, '0');\n } else if (year >= 0) {\n isoYear = '+' + String(year).padStart(6, '0');\n } else {\n isoYear = '-' + String(Math.abs(year)).padStart(6, '0');\n }\n\n // Build a UTC ISO string so PreciseDate returns microsecond precision.\n const utcString = `${isoYear}-${monthDay}T${time}Z`;\n\n try {\n const fullTime = new PreciseDate(utcString).getFullTime();\n const millis = Number(fullTime / 1_000_000n);\n const nanos = Number(fullTime % 1_000_000n);\n const ret = millis + nanos * 1e-6; // floating point milliseconds\n\n // Add back in the timezone offset. We passed local time as UTC,\n // so a positive offset means we need to subtract, and vice versa.\n if (tz) {\n const positiveOffset = tz.startsWith('+');\n const [hours, minutes] = tz.split(':');\n const offset =\n Math.abs(Number(hours)) * 60 + (minutes ? Number(minutes) : 0);\n const offsetMillis = offset * 60 * 1_000;\n return positiveOffset ? ret - offsetMillis : ret + offsetMillis;\n }\n return ret;\n } catch (e) {\n throw new Error(`Error parsing ${timestamp}`, {cause: e});\n }\n}\n\nfunction serializeTimestamp(val: unknown): string {\n switch (typeof val) {\n case 'string':\n return val; // Let Postgres parse it\n case 'number': {\n if (Number.isInteger(val)) {\n return new PreciseDate(val).toISOString();\n }\n // Convert floating point to bigint nanoseconds.\n const nanoseconds =\n 1_000_000n * BigInt(Math.trunc(val)) +\n BigInt(Math.trunc((val % 1) * 1e6));\n return new PreciseDate(nanoseconds).toISOString();\n }\n // Note: Don't support bigint inputs until we decide what the semantics are (e.g. micros vs nanos)\n // case 'bigint':\n // return new PreciseDate(val).toISOString();\n default:\n if (val instanceof Date) {\n return val.toISOString();\n }\n }\n throw new Error(`Unsupported type \"${typeof val}\" for timestamp: ${val}`);\n}\n\nconst MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000;\n\nfunction serializeTime(x: unknown, type: 'time' | 'timetz'): string {\n switch (typeof x) {\n case 'string':\n return x; // Let Postgres parse it\n case 'number':\n return millisecondsToPostgresTime(x);\n }\n throw new Error(`Unsupported type \"${typeof x}\" for ${type}: ${x}`);\n}\n\nexport function millisecondsToPostgresTime(milliseconds: number): string {\n if (milliseconds < 0) {\n throw new Error('Milliseconds cannot be negative');\n }\n\n if (milliseconds >= MILLISECONDS_PER_DAY) {\n throw new Error(\n `Milliseconds cannot exceed 24 hours (${MILLISECONDS_PER_DAY}ms)`,\n );\n }\n\n milliseconds = Math.floor(milliseconds); // Ensure it's an integer\n\n const totalSeconds = Math.floor(milliseconds / 1000);\n const hours = Math.floor(totalSeconds / 3600);\n const minutes = Math.floor((totalSeconds % 3600) / 60);\n const seconds = totalSeconds % 60;\n const ms = milliseconds % 1000;\n\n return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}.${ms.toString().padStart(3, '0')}+00`;\n}\n\n// Regular expression to match HH:MM:SS, HH:MM:SS.mmm, or HH:MM:SS+00 / HH:MM:SS.mmm+00\n// Supports optional timezone offset\nconst timeRegex =\n /^(\\d{1,2}):(\\d{2}):(\\d{2})(?:\\.(\\d{1,6}))?(?:([+-])(\\d{1,2})(?::(\\d{2}))?)?$/;\n\nexport function postgresTimeToMilliseconds(timeString: string): number {\n // Validate basic format\n if (!timeString || typeof timeString !== 'string') {\n throw new Error('Invalid time string: must be a non-empty string');\n }\n\n const match = timeString.match(timeRegex);\n\n if (!match) {\n throw new Error(\n `Invalid time format: \"${timeString}\". Expected HH:MM:SS[.mmm][+|-HH[:MM]]`,\n );\n }\n\n // Extract components\n const hours = parseInt(match[1], 10);\n const minutes = parseInt(match[2], 10);\n const seconds = parseInt(match[3], 10);\n // Handle optional milliseconds, pad right with zeros if needed\n let milliseconds = 0;\n if (match[4]) {\n // Pad microseconds to 6 digits\n const msString = match[4].padEnd(6, '0');\n // slice milliseconds out of the microseconds\n // e.g. 123456 -> 123, 1234 -> 123,\n milliseconds = parseInt(msString.slice(0, 3), 10);\n }\n\n // Validate ranges\n if (hours < 0 || hours > 24) {\n throw new Error(\n `Invalid hours: ${hours}. Must be between 0 and 24 (24 means end of day)`,\n );\n }\n\n if (minutes < 0 || minutes >= 60) {\n throw new Error(`Invalid minutes: ${minutes}. Must be between 0 and 59`);\n }\n\n if (seconds < 0 || seconds >= 60) {\n throw new Error(`Invalid seconds: ${seconds}. Must be between 0 and 59`);\n }\n\n if (milliseconds < 0 || milliseconds >= 1000) {\n throw new Error(\n `Invalid milliseconds: ${milliseconds}. Must be between 0 and 999`,\n );\n }\n\n // Special case: PostgreSQL allows 24:00:00 to represent end of day\n if (hours === 24 && (minutes !== 0 || seconds !== 0 || milliseconds !== 0)) {\n throw new Error(\n 'Invalid time: when hours is 24, minutes, seconds, and milliseconds must be 0',\n );\n }\n\n // Calculate total milliseconds\n let totalMs =\n hours * 3600000 + minutes * 60000 + seconds * 1000 + milliseconds;\n\n // Timezone Offset\n if (match[5]) {\n const sign = match[5] === '+' ? 1 : -1;\n const tzHours = parseInt(match[6], 10);\n const tzMinutes = match[7] ? parseInt(match[7], 10) : 0;\n const offsetMs = sign * (tzHours * 3600000 + tzMinutes * 60000);\n totalMs -= offsetMs;\n }\n\n // Normalize to 0-24h only if outside valid range\n if (totalMs > MILLISECONDS_PER_DAY || totalMs < 0) {\n return (\n ((totalMs % MILLISECONDS_PER_DAY) + MILLISECONDS_PER_DAY) %\n MILLISECONDS_PER_DAY\n );\n }\n\n return totalMs;\n}\n\nfunction dateToUTCMidnight(date: string): number {\n if (date === 'infinity') return Infinity;\n if (date === '-infinity') return -Infinity;\n const d = new Date(date);\n return Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate());\n}\n\n/**\n * The (javascript) types of objects that can be returned by our configured\n * Postgres clients. For initial-sync, these comes from the postgres.js client:\n *\n * https://github.com/porsager/postgres/blob/master/src/types.js\n *\n * and for the replication stream these come from the the node-postgres client:\n *\n * https://github.com/brianc/node-pg-types/blob/master/lib/textParsers.js\n */\nexport type PostgresValueType = JSONValue | Uint8Array;\n\nexport type TypeOptions = {\n /**\n * Sends strings directly as JSON values (i.e. without JSON stringification).\n * The application is responsible for ensuring that string inputs for JSON\n * columns are already stringified. Other data types (e.g. objects) will\n * still be stringified by the pg client.\n */\n sendStringAsJson?: boolean;\n};\n\n/**\n * Configures types for the Postgres.js client library (`postgres`).\n *\n * @param jsonAsString Keep JSON / JSONB values as strings instead of parsing.\n */\nexport const postgresTypeConfig = ({sendStringAsJson}: TypeOptions = {}) => ({\n // Type the type IDs as `number` so that Typescript doesn't complain about\n // referencing external types during type inference.\n types: {\n bigint: postgres.BigInt,\n json: {\n to: JSON,\n from: [JSON, JSONB],\n serialize: sendStringAsJson\n ? (x: unknown) => (typeof x === 'string' ? x : BigIntJSON.stringify(x))\n : BigIntJSON.stringify,\n parse: BigIntJSON.parse,\n },\n // Timestamps are converted to PreciseDate objects.\n timestamp: {\n to: TIMESTAMP,\n from: [TIMESTAMP, TIMESTAMPTZ],\n serialize: serializeTimestamp,\n parse: timestampToFpMillis,\n },\n // Times are converted as strings\n time: {\n to: TIME,\n from: [TIME, TIMETZ],\n serialize: (x: unknown) => serializeTime(x, 'time'),\n parse: postgresTimeToMilliseconds,\n },\n\n timetz: {\n to: TIMETZ,\n from: [TIME, TIMETZ],\n serialize: (x: unknown) => serializeTime(x, 'timetz'),\n parse: postgresTimeToMilliseconds,\n },\n\n // The DATE type is stored directly as the PG normalized date string.\n date: {\n to: DATE,\n from: [DATE],\n serialize: (x: string | Date) =>\n (x instanceof Date ? x : new Date(x)).toISOString(),\n parse: dateToUTCMidnight,\n },\n // Returns a `js` number which can lose precision for large numbers.\n // JS number is 53 bits so this should generally not occur.\n // An API will be provided for users to override this type.\n numeric: {\n to: NUMERIC,\n from: [NUMERIC],\n serialize: (x: number) => String(x), // pg expects a string\n parse: (x: string | number) => Number(x),\n },\n },\n});\n\nexport type PostgresDB = postgres.Sql<{\n bigint: bigint;\n json: JSONValue;\n}>;\n\nexport type PostgresTransaction = postgres.TransactionSql<{\n bigint: bigint;\n json: JSONValue;\n}>;\n\nexport function pgClient(\n lc: LogContext,\n connectionURI: string,\n // A descriptive name for the code that is making the connection, so that we\n // can debug things like deadlocks.\n applicationName: string,\n options?: postgres.Options<{\n bigint: PostgresType<bigint>;\n json: PostgresType<JSONValue>;\n }>,\n opts?: TypeOptions,\n): PostgresDB {\n applicationName = `zero-${applicationName}`;\n\n const onnotice = (n: Notice) => {\n // https://www.postgresql.org/docs/current/plpgsql-errors-and-messages.html#PLPGSQL-STATEMENTS-RAISE\n switch (n.severity) {\n case 'NOTICE':\n return; // silenced\n case 'DEBUG':\n lc.debug?.(n);\n return;\n case 'WARNING':\n lc.warn?.(n);\n return;\n case 'EXCEPTION':\n lc.error?.(n);\n return;\n case 'LOG':\n case 'INFO':\n default:\n lc.info?.(n);\n }\n };\n const url = new URL(connectionURI);\n const sslFlag =\n url.searchParams.get('ssl') ?? url.searchParams.get('sslmode') ?? 'prefer';\n\n let ssl: boolean | 'prefer' | {rejectUnauthorized: boolean};\n if (sslFlag === 'disable' || sslFlag === 'false') {\n ssl = false;\n } else if (sslFlag === 'no-verify') {\n ssl = {rejectUnauthorized: false};\n } else {\n ssl = sslFlag as 'prefer';\n }\n\n // Set connections to expire between 5 and 10 minutes to free up state on PG.\n const maxLifetimeSeconds = randInt(5 * 60, 10 * 60);\n const providedConnection = options?.connection ?? {};\n\n return postgres(connectionURI, {\n ...postgresTypeConfig(opts),\n onnotice,\n ['max_lifetime']: maxLifetimeSeconds,\n ssl,\n ...options,\n connection: {\n ...providedConnection,\n ['application_name']: applicationName,\n },\n });\n}\n\nexport async function connectPgClient(\n ...args: Parameters<typeof pgClient>\n): Promise<PostgresDB> {\n const db = pgClient(...args);\n try {\n await db`SELECT 1`.simple().execute();\n return db;\n } catch (e) {\n if (isPostgresConfigError(e)) {\n throw new ConfigurationError(\n 'Unable to connect to Postgres. Check your database configuration.',\n {cause: e},\n );\n }\n throw e;\n }\n}\n\n/**\n * Disables any statement_timeout for the current transaction. By default,\n * Postgres does not impose a statement timeout, but some users and providers\n * set one at the database level (even though it is explicitly discouraged by\n * 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 migrations\n * and background cleanup, the statement timeout should be disabled to prevent\n * these operations from timing out.\n */\nexport function disableStatementTimeout(sql: PostgresTransaction) {\n void sql`SET LOCAL statement_timeout = 0;`.execute().catch(() => {});\n}\n\nexport const typeNameByOID: Record<number, string> = Object.freeze(\n Object.fromEntries(\n Object.entries(OID).map(([name, oid]) => [\n oid,\n name.startsWith('_') ? `${name.substring(1)}[]` : name,\n ]),\n ),\n);\n\nexport function isPostgresError(e: unknown, ...codes: [string, ...string[]]) {\n return e instanceof postgres.PostgresError && codes.includes(e.code);\n}\n\nfunction isPostgresStartupConnectionError(e: unknown) {\n if (!(e instanceof Error) || !('code' in e)) {\n return false;\n }\n const {code} = e;\n // postgres.js throws plain Error instances for connection failures that\n // happen before Postgres can return a SQLSTATE. These are still startup\n // configuration problems for Zero: wrong host/port, unreachable DB, DNS,\n // TLS certificate mismatch, etc.\n return (\n typeof code === 'string' &&\n [\n 'CONNECT_TIMEOUT',\n 'ECONNREFUSED',\n 'ECONNRESET',\n 'EHOSTUNREACH',\n 'ENETUNREACH',\n 'ENOTFOUND',\n 'EAI_AGAIN',\n 'ETIMEDOUT',\n 'ERR_TLS_CERT_ALTNAME_INVALID',\n 'SELF_SIGNED_CERT_IN_CHAIN',\n 'DEPTH_ZERO_SELF_SIGNED_CERT',\n 'UNABLE_TO_VERIFY_LEAF_SIGNATURE',\n ].includes(code)\n );\n}\n\nexport function isPostgresConfigError(e: unknown) {\n return (\n isPostgresStartupConnectionError(e) ||\n isPostgresError(\n e,\n PG_CONNECTION_EXCEPTION,\n PG_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION,\n PG_CONNECTION_DOES_NOT_EXIST,\n PG_CONNECTION_FAILURE,\n PG_INVALID_AUTHORIZATION_SPECIFICATION,\n PG_INVALID_PASSWORD,\n PG_INVALID_CATALOG_NAME,\n PG_INSUFFICIENT_PRIVILEGE,\n PG_TOO_MANY_CONNECTIONS,\n PG_CONFIGURATION_LIMIT_EXCEEDED,\n )\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"processes.js","names":[],"sources":["../../../../../zero-cache/src/types/processes.ts"],"sourcesContent":["import {\n type ChildProcess,\n fork,\n type SendHandle,\n type Serializable,\n} from 'node:child_process';\nimport EventEmitter from 'node:events';\nimport {platform} from 'node:os';\nimport {pid} from 'node:process';\n\n/**\n * Central registry of message type names, which are used to identify\n * the payload in {@link Message} objects sent between processes. The\n * payloads themselves are implementation specific and defined in each\n * component; only the type name is reserved here to avoid collisions.\n *\n * Receiving logic can call {@link getMessage()} with the name of\n * the message of interest to filter messages to those of interest.\n */\nexport const MESSAGE_TYPES = {\n handoff: 'handoff',\n status: 'status',\n subscribe: 'subscribe',\n notify: 'notify',\n ready: 'ready',\n} as const;\n\nexport type Message<Payload> = [keyof typeof MESSAGE_TYPES, Payload];\n\nfunction getMessage<M extends Message<unknown>>(\n type: M[0],\n data: unknown,\n): M[1] | null {\n if (Array.isArray(data) && data.length === 2 && data[0] === type) {\n return data[1] as M[1];\n }\n return null;\n}\n\nfunction onMessageType<M extends Message<unknown>>(\n e: EventEmitter,\n type: M[0],\n handler: (msg: M[1], sendHandle?: SendHandle) => void,\n) {\n return e.on('message', (data, sendHandle) => {\n const msg = getMessage(type, data);\n if (msg) {\n handler(msg, sendHandle);\n }\n });\n}\n\nfunction onceMessageType<M extends Message<unknown>>(\n e: EventEmitter,\n type: M[0],\n handler: (msg: M[1], sendHandle?: SendHandle) => void,\n) {\n const listener = (data: unknown, sendHandle: SendHandle) => {\n const msg = getMessage(type, data);\n if (msg) {\n e.off('message', listener);\n handler(msg, sendHandle);\n }\n };\n return e.on('message', listener);\n}\n\nexport interface Sender {\n send<M extends Message<unknown>>(\n message: M,\n sendHandle?: SendHandle,\n callback?: (error: Error | null) => void,\n ): boolean;\n\n kill(signal?: NodeJS.Signals): void;\n}\n\nexport interface Subprocess extends Sender, EventEmitter {\n pid?: number | undefined;\n}\n\nexport interface Receiver extends EventEmitter {\n /**\n * The receiving side of {@link Sender.send()} that is a wrapper around\n * {@link on}('message', ...) that invokes the `handler` for messages of\n * the specified `type`.\n */\n onMessageType<M extends Message<unknown>>(\n type: M[0],\n handler: (msg: M[1], sendHandle?: SendHandle) => void,\n ): this;\n\n /**\n * The receiving side of {@link Sender.send()} that behaves like\n * {@link once}('message', ...) that invokes the `handler` for the next\n * message of the specified `type` and then unsubscribes.\n */\n onceMessageType<M extends Message<unknown>>(\n type: M[0],\n handler: (msg: M[1], sendHandle?: SendHandle) => void,\n ): this;\n}\n\nexport interface Worker extends Subprocess, Receiver {}\n\n/**\n * Adds the {@link Receiver.onMessageType()} and {@link Receiver.onceMessageType()}\n * methods to convert the given `EventEmitter` to a `Receiver`.\n */\nfunction wrap<P extends EventEmitter>(proc: P): P & Receiver {\n return new Proxy(proc, {\n get(target: P, prop: string | symbol, receiver: unknown) {\n switch (prop) {\n case 'onMessageType':\n return (\n type: keyof typeof MESSAGE_TYPES,\n handler: (msg: unknown, sendHandle?: SendHandle) => void,\n ) => {\n onMessageType(target, type, handler);\n return receiver; // this\n };\n case 'onceMessageType':\n return (\n type: keyof typeof MESSAGE_TYPES,\n handler: (msg: unknown, sendHandle?: SendHandle) => void,\n ) => {\n onceMessageType(target, type, handler);\n return receiver; // this\n };\n }\n return Reflect.get(target, prop, receiver);\n },\n }) as P & Receiver;\n}\n\ntype Proc = Pick<ChildProcess, 'send' | 'kill' | 'pid'> & EventEmitter;\n\n/**\n * The parentWorker for forked processes, or `null` if the process was not forked.\n * (Analogous to the `parentPort: MessagePort | null` of the `\"workers\"` library).\n */\nexport const parentWorker: Worker | null = process.send\n ? wrap(process as Proc)\n : null;\n\nconst SINGLE_PROCESS = 'SINGLE_PROCESS';\nlet singleProcessOverride = false;\nexport function singleProcessMode(): boolean {\n return singleProcessOverride || (process.env[SINGLE_PROCESS] ?? '0') !== '0';\n}\n\nexport function setSingleProcessMode(enabled: boolean = true): void {\n singleProcessOverride = enabled;\n}\n\n/**\n *\n * @param modulePath Path to the module file, relative to zero-cache/src/, or an absolute file:// URL\n */\nexport function childWorker(\n moduleUrl: URL,\n env?: NodeJS.ProcessEnv,\n ...args: string[]\n): Worker {\n args.push(...process.argv.slice(2));\n\n if (singleProcessMode()) {\n const [parent, child] = inProcChannel();\n import(moduleUrl.href)\n .then(async ({default: runWorker}) => {\n try {\n await runWorker(parent, env ?? process.env, ...args);\n child.emit('close', 0);\n return;\n } catch (err) {\n child.emit('error', err);\n child.emit('close', -1);\n }\n })\n .catch(err => child.emit('error', err));\n return child;\n }\n const child = fork(moduleUrl, args, {\n // For production / non-windows, set `detached` to `true` so that SIGINT is\n // not automatically propagated and graceful shutdown happens as intended.\n // For Win32, detached: true causes all subprocesses to open in separate\n // terminals and breaks inter-process kill signals, so set it to false.\n detached: platform() !== 'win32',\n serialization: 'advanced', // use structured clone for IPC\n env,\n });\n return wrap(child);\n}\n\n/**\n * Creates two connected `Worker` instances such that messages sent to one\n * via the {@link Worker.send()} method are received by the other's\n * `on('message', ...)` handler.\n *\n * This is analogous to the two `MessagePort`s of a `MessageChannel`, and\n * is useful for executing code written for inter-process communication\n * in a single process.\n */\nexport function inProcChannel(): [Worker, Worker] {\n const worker1 = new EventEmitter();\n const worker2 = new EventEmitter();\n\n const sendTo =\n (dest: EventEmitter) =>\n (\n message: Serializable,\n sendHandle?: SendHandle,\n callback?: (error: Error | null) => void,\n ) => {\n dest.emit('message', message, sendHandle);\n if (callback) {\n callback(null);\n }\n return true;\n };\n\n const kill =\n (dest: EventEmitter) =>\n (signal: NodeJS.Signals = 'SIGTERM') =>\n dest.emit(signal, signal);\n\n return [\n wrap(\n Object.assign(worker1, {send: sendTo(worker2), kill: kill(worker2), pid}),\n ),\n wrap(\n Object.assign(worker2, {send: sendTo(worker1), kill: kill(worker1), pid}),\n ),\n ];\n}\n"],"mappings":";;;;;AA6BA,SAAS,WACP,MACA,MACa;CACb,IAAI,MAAM,QAAQ,IAAI,KAAK,KAAK,WAAW,KAAK,KAAK,OAAO,MAC1D,OAAO,KAAK;CAEd,OAAO;AACT;AAEA,SAAS,cACP,GACA,MACA,SACA;CACA,OAAO,EAAE,GAAG,YAAY,MAAM,eAAe;EAC3C,MAAM,MAAM,WAAW,MAAM,IAAI;EACjC,IAAI,KACF,QAAQ,KAAK,UAAU;CAE3B,CAAC;AACH;AAEA,SAAS,gBACP,GACA,MACA,SACA;CACA,MAAM,YAAY,MAAe,eAA2B;EAC1D,MAAM,MAAM,WAAW,MAAM,IAAI;EACjC,IAAI,KAAK;GACP,EAAE,IAAI,WAAW,QAAQ;GACzB,QAAQ,KAAK,UAAU;EACzB;CACF;CACA,OAAO,EAAE,GAAG,WAAW,QAAQ;AACjC;;;;;AA4CA,SAAS,KAA6B,MAAuB;CAC3D,OAAO,IAAI,MAAM,MAAM,EACrB,IAAI,QAAW,MAAuB,UAAmB;EACvD,QAAQ,MAAR;GACE,KAAK,iBACH,QACE,MACA,YACG;IACH,cAAc,QAAQ,MAAM,OAAO;IACnC,OAAO;GACT;GACF,KAAK,mBACH,QACE,MACA,YACG;IACH,gBAAgB,QAAQ,MAAM,OAAO;IACrC,OAAO;GACT;EACJ;EACA,OAAO,QAAQ,IAAI,QAAQ,MAAM,QAAQ;CAC3C,EACF,CAAC;AACH;;;;;AAQA,IAAa,eAA8B,QAAQ,OAC/C,KAAK,OAAe,IACpB;AAEJ,IAAM,iBAAiB;AACvB,IAAI,wBAAwB;AAC5B,SAAgB,oBAA6B;CAC3C,OAAO,0BAA0B,QAAQ,IAAI,mBAAmB,SAAS;AAC3E;;;;;AAUA,SAAgB,YACd,WACA,KACA,GAAG,MACK;CACR,KAAK,KAAK,GAAG,QAAQ,KAAK,MAAM,CAAC,CAAC;CAElC,IAAI,kBAAkB,GAAG;EACvB,MAAM,CAAC,QAAQ,SAAS,cAAc;EACtC,OAAO,UAAU,MACd,KAAK,OAAO,EAAC,SAAS,gBAAe;GACpC,IAAI;IACF,MAAM,UAAU,QAAQ,OAAO,QAAQ,KAAK,GAAG,IAAI;IACnD,MAAM,KAAK,SAAS,CAAC;IACrB;GACF,SAAS,KAAK;IACZ,MAAM,KAAK,SAAS,GAAG;IACvB,MAAM,KAAK,SAAS,EAAE;GACxB;EACF,CAAC,EACA,OAAM,QAAO,MAAM,KAAK,SAAS,GAAG,CAAC;EACxC,OAAO;CACT;CAUA,OAAO,KATO,KAAK,WAAW,MAAM;EAKlC,UAAU,SAAS,MAAM;EACzB,eAAe;EACf;CACF,CACY,CAAK;AACnB;;;;;;;;;;AAWA,SAAgB,gBAAkC;CAChD,MAAM,UAAU,IAAI,aAAa;CACjC,MAAM,UAAU,IAAI,aAAa;CAEjC,MAAM,UACH,UAEC,SACA,YACA,aACG;EACH,KAAK,KAAK,WAAW,SAAS,UAAU;EACxC,IAAI,UACF,SAAS,IAAI;EAEf,OAAO;CACT;CAEF,MAAM,QACH,UACA,SAAyB,cACxB,KAAK,KAAK,QAAQ,MAAM;CAE5B,OAAO,CACL,KACE,OAAO,OAAO,SAAS;EAAC,MAAM,OAAO,OAAO;EAAG,MAAM,KAAK,OAAO;EAAG;CAAG,CAAC,CAC1E,GACA,KACE,OAAO,OAAO,SAAS;EAAC,MAAM,OAAO,OAAO;EAAG,MAAM,KAAK,OAAO;EAAG;CAAG,CAAC,CAC1E,CACF;AACF"}
1
+ {"version":3,"file":"processes.js","names":[],"sources":["../../../../../zero-cache/src/types/processes.ts"],"sourcesContent":["import {\n type ChildProcess,\n fork,\n type SendHandle,\n type Serializable,\n} from 'node:child_process';\nimport EventEmitter from 'node:events';\nimport {platform} from 'node:os';\nimport {pid} from 'node:process';\n\n/**\n * Central registry of message type names, which are used to identify\n * the payload in {@link Message} objects sent between processes. The\n * payloads themselves are implementation specific and defined in each\n * component; only the type name is reserved here to avoid collisions.\n *\n * Receiving logic can call {@link getMessage()} with the name of\n * the message of interest to filter messages to those of interest.\n */\nexport const MESSAGE_TYPES = {\n handoff: 'handoff',\n status: 'status',\n subscribe: 'subscribe',\n notify: 'notify',\n ready: 'ready',\n} as const;\n\nexport type Message<Payload> = [keyof typeof MESSAGE_TYPES, Payload];\n\nfunction getMessage<M extends Message<unknown>>(\n type: M[0],\n data: unknown,\n): M[1] | null {\n if (Array.isArray(data) && data.length === 2 && data[0] === type) {\n return data[1] as M[1];\n }\n return null;\n}\n\nfunction onMessageType<M extends Message<unknown>>(\n e: EventEmitter,\n type: M[0],\n handler: (msg: M[1], sendHandle?: SendHandle) => void,\n) {\n return e.on('message', (data, sendHandle) => {\n const msg = getMessage(type, data);\n if (msg) {\n handler(msg, sendHandle);\n }\n });\n}\n\nfunction onceMessageType<M extends Message<unknown>>(\n e: EventEmitter,\n type: M[0],\n handler: (msg: M[1], sendHandle?: SendHandle) => void,\n) {\n const listener = (data: unknown, sendHandle: SendHandle) => {\n const msg = getMessage(type, data);\n if (msg) {\n e.off('message', listener);\n handler(msg, sendHandle);\n }\n };\n return e.on('message', listener);\n}\n\nexport interface Sender {\n send<M extends Message<unknown>>(\n message: M,\n sendHandle?: SendHandle,\n callback?: (error: Error | null) => void,\n ): boolean;\n\n kill(signal?: NodeJS.Signals): void;\n}\n\nexport interface Subprocess extends Sender, EventEmitter {\n pid?: number | undefined;\n}\n\nexport interface Receiver extends EventEmitter {\n /**\n * The receiving side of {@link Sender.send()} that is a wrapper around\n * {@link on}('message', ...) that invokes the `handler` for messages of\n * the specified `type`.\n */\n onMessageType<M extends Message<unknown>>(\n type: M[0],\n handler: (msg: M[1], sendHandle?: SendHandle) => void,\n ): this;\n\n /**\n * The receiving side of {@link Sender.send()} that behaves like\n * {@link once}('message', ...) that invokes the `handler` for the next\n * message of the specified `type` and then unsubscribes.\n */\n onceMessageType<M extends Message<unknown>>(\n type: M[0],\n handler: (msg: M[1], sendHandle?: SendHandle) => void,\n ): this;\n}\n\nexport interface Worker extends Subprocess, Receiver {}\n\n/**\n * Adds the {@link Receiver.onMessageType()} and {@link Receiver.onceMessageType()}\n * methods to convert the given `EventEmitter` to a `Receiver`.\n */\nfunction wrap<P extends EventEmitter>(proc: P): P & Receiver {\n return new Proxy(proc, {\n get(target: P, prop: string | symbol, receiver: unknown) {\n switch (prop) {\n case 'onMessageType':\n return (\n type: keyof typeof MESSAGE_TYPES,\n handler: (msg: unknown, sendHandle?: SendHandle) => void,\n ) => {\n onMessageType(target, type, handler);\n return receiver; // this\n };\n case 'onceMessageType':\n return (\n type: keyof typeof MESSAGE_TYPES,\n handler: (msg: unknown, sendHandle?: SendHandle) => void,\n ) => {\n onceMessageType(target, type, handler);\n return receiver; // this\n };\n }\n return Reflect.get(target, prop, receiver);\n },\n }) as P & Receiver;\n}\n\ntype Proc = Pick<ChildProcess, 'send' | 'kill' | 'pid'> & EventEmitter;\n\n/**\n * The parentWorker for forked processes, or `null` if the process was not forked.\n * (Analogous to the `parentPort: MessagePort | null` of the `\"workers\"` library).\n */\nexport const parentWorker: Worker | null = process.send\n ? wrap(process as Proc)\n : null;\n\nconst SINGLE_PROCESS = 'SINGLE_PROCESS';\nlet singleProcessOverride = false;\nexport function singleProcessMode(): boolean {\n return singleProcessOverride || (process.env[SINGLE_PROCESS] ?? '0') !== '0';\n}\n\nexport function setSingleProcessMode(enabled: boolean = true): void {\n singleProcessOverride = enabled;\n}\n\n/**\n *\n * @param modulePath Path to the module file, relative to zero-cache/src/, or an absolute file:// URL\n */\nexport function childWorker(\n moduleUrl: URL,\n env?: NodeJS.ProcessEnv,\n ...args: string[]\n): Worker {\n args.push(...process.argv.slice(2));\n\n if (singleProcessMode()) {\n const [parent, child] = inProcChannel();\n import(moduleUrl.href)\n .then(async ({default: runWorker}) => {\n try {\n await runWorker(parent, env ?? process.env, ...args);\n child.emit('close', 0);\n return;\n } catch (err) {\n child.emit('error', err);\n child.emit('close', -1);\n }\n })\n .catch(err => child.emit('error', err));\n return child;\n }\n const child = fork(moduleUrl, args, {\n // For production / non-windows, set `detached` to `true` so that SIGINT is\n // not automatically propagated and graceful shutdown happens as intended.\n // For Win32, detached: true causes all subprocesses to open in separate\n // terminals and breaks inter-process kill signals, so set it to false.\n detached: platform() !== 'win32',\n serialization: 'advanced', // use structured clone for IPC\n env,\n });\n return wrap(child);\n}\n\n/**\n * Creates two connected `Worker` instances such that messages sent to one\n * via the {@link Worker.send()} method are received by the other's\n * `on('message', ...)` handler.\n *\n * This is analogous to the two `MessagePort`s of a `MessageChannel`, and\n * is useful for executing code written for inter-process communication\n * in a single process.\n */\nexport function inProcChannel(): [Worker, Worker] {\n const worker1 = new EventEmitter();\n const worker2 = new EventEmitter();\n\n const sendTo =\n (dest: EventEmitter) =>\n (\n message: Serializable,\n sendHandle?: SendHandle,\n callback?: (error: Error | null) => void,\n ) => {\n dest.emit('message', message, sendHandle);\n if (callback) {\n callback(null);\n }\n return true;\n };\n\n const kill =\n (dest: EventEmitter) =>\n (signal: NodeJS.Signals = 'SIGTERM') =>\n dest.emit(signal, signal);\n\n return [\n wrap(\n Object.assign(worker1, {send: sendTo(worker2), kill: kill(worker2), pid}),\n ),\n wrap(\n Object.assign(worker2, {send: sendTo(worker1), kill: kill(worker1), pid}),\n ),\n ];\n}\n"],"mappings":";;;;;AA6BA,SAAS,WACP,MACA,MACa;AACb,KAAI,MAAM,QAAQ,KAAK,IAAI,KAAK,WAAW,KAAK,KAAK,OAAO,KAC1D,QAAO,KAAK;AAEd,QAAO;;AAGT,SAAS,cACP,GACA,MACA,SACA;AACA,QAAO,EAAE,GAAG,YAAY,MAAM,eAAe;EAC3C,MAAM,MAAM,WAAW,MAAM,KAAK;AAClC,MAAI,IACF,SAAQ,KAAK,WAAW;GAE1B;;AAGJ,SAAS,gBACP,GACA,MACA,SACA;CACA,MAAM,YAAY,MAAe,eAA2B;EAC1D,MAAM,MAAM,WAAW,MAAM,KAAK;AAClC,MAAI,KAAK;AACP,KAAE,IAAI,WAAW,SAAS;AAC1B,WAAQ,KAAK,WAAW;;;AAG5B,QAAO,EAAE,GAAG,WAAW,SAAS;;;;;;AA6ClC,SAAS,KAA6B,MAAuB;AAC3D,QAAO,IAAI,MAAM,MAAM,EACrB,IAAI,QAAW,MAAuB,UAAmB;AACvD,UAAQ,MAAR;GACE,KAAK,gBACH,SACE,MACA,YACG;AACH,kBAAc,QAAQ,MAAM,QAAQ;AACpC,WAAO;;GAEX,KAAK,kBACH,SACE,MACA,YACG;AACH,oBAAgB,QAAQ,MAAM,QAAQ;AACtC,WAAO;;;AAGb,SAAO,QAAQ,IAAI,QAAQ,MAAM,SAAS;IAE7C,CAAC;;;;;;AASJ,IAAa,eAA8B,QAAQ,OAC/C,KAAK,QAAgB,GACrB;AAEJ,IAAM,iBAAiB;AACvB,IAAI,wBAAwB;AAC5B,SAAgB,oBAA6B;AAC3C,QAAO,0BAA0B,QAAQ,IAAI,mBAAmB,SAAS;;;;;;AAW3E,SAAgB,YACd,WACA,KACA,GAAG,MACK;AACR,MAAK,KAAK,GAAG,QAAQ,KAAK,MAAM,EAAE,CAAC;AAEnC,KAAI,mBAAmB,EAAE;EACvB,MAAM,CAAC,QAAQ,SAAS,eAAe;AACvC,SAAO,UAAU,MACd,KAAK,OAAO,EAAC,SAAS,gBAAe;AACpC,OAAI;AACF,UAAM,UAAU,QAAQ,OAAO,QAAQ,KAAK,GAAG,KAAK;AACpD,UAAM,KAAK,SAAS,EAAE;AACtB;YACO,KAAK;AACZ,UAAM,KAAK,SAAS,IAAI;AACxB,UAAM,KAAK,SAAS,GAAG;;IAEzB,CACD,OAAM,QAAO,MAAM,KAAK,SAAS,IAAI,CAAC;AACzC,SAAO;;AAWT,QAAO,KATO,KAAK,WAAW,MAAM;EAKlC,UAAU,UAAU,KAAK;EACzB,eAAe;EACf;EACD,CAAC,CACgB;;;;;;;;;;;AAYpB,SAAgB,gBAAkC;CAChD,MAAM,UAAU,IAAI,cAAc;CAClC,MAAM,UAAU,IAAI,cAAc;CAElC,MAAM,UACH,UAEC,SACA,YACA,aACG;AACH,OAAK,KAAK,WAAW,SAAS,WAAW;AACzC,MAAI,SACF,UAAS,KAAK;AAEhB,SAAO;;CAGX,MAAM,QACH,UACA,SAAyB,cACxB,KAAK,KAAK,QAAQ,OAAO;AAE7B,QAAO,CACL,KACE,OAAO,OAAO,SAAS;EAAC,MAAM,OAAO,QAAQ;EAAE,MAAM,KAAK,QAAQ;EAAE;EAAI,CAAC,CAC1E,EACD,KACE,OAAO,OAAO,SAAS;EAAC,MAAM,OAAO,QAAQ;EAAE,MAAM,KAAK,QAAQ;EAAE;EAAI,CAAC,CAC1E,CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"profiler.js","names":["#session"],"sources":["../../../../../zero-cache/src/types/profiler.ts"],"sourcesContent":["import {writeFile} from 'node:fs/promises';\nimport {Session} from 'node:inspector/promises';\nimport {tmpdir} from 'node:os';\nimport {join} from 'node:path';\nimport type {LogContext} from '@rocicorp/logger';\n\n/**\n * Convenience wrapper around a `node:inspector` {@link Session} for\n * optionally taking cpu profiles.\n */\nexport class CpuProfiler {\n static async connect() {\n const session = new Session();\n session.connect();\n await session.post('Profiler.enable');\n return new CpuProfiler(session);\n }\n\n readonly #session;\n\n private constructor(session: Session) {\n this.#session = session;\n }\n\n async start() {\n await this.#session.post('Profiler.start');\n }\n\n async stopAndDispose(lc: LogContext, filename: string) {\n const {profile} = await this.#session.post('Profiler.stop');\n const path = join(tmpdir(), `${filename}.cpuprofile`);\n await writeFile(path, JSON.stringify(profile));\n lc.info?.(`wrote cpu profile to ${path}`);\n this.#session.disconnect();\n }\n}\n"],"mappings":";;;;;;;;;AAUA,IAAa,cAAb,MAAa,YAAY;CACvB,aAAa,UAAU;EACrB,MAAM,UAAU,IAAI,QAAQ;EAC5B,QAAQ,QAAQ;EAChB,MAAM,QAAQ,KAAK,iBAAiB;EACpC,OAAO,IAAI,YAAY,OAAO;CAChC;CAEA;CAEA,YAAoB,SAAkB;EACpC,KAAKA,WAAW;CAClB;CAEA,MAAM,QAAQ;EACZ,MAAM,KAAKA,SAAS,KAAK,gBAAgB;CAC3C;CAEA,MAAM,eAAe,IAAgB,UAAkB;EACrD,MAAM,EAAC,YAAW,MAAM,KAAKA,SAAS,KAAK,eAAe;EAC1D,MAAM,OAAO,KAAK,OAAO,GAAG,GAAG,SAAS,YAAY;EACpD,MAAM,UAAU,MAAM,KAAK,UAAU,OAAO,CAAC;EAC7C,GAAG,OAAO,wBAAwB,MAAM;EACxC,KAAKA,SAAS,WAAW;CAC3B;AACF"}
1
+ {"version":3,"file":"profiler.js","names":["#session"],"sources":["../../../../../zero-cache/src/types/profiler.ts"],"sourcesContent":["import {writeFile} from 'node:fs/promises';\nimport {Session} from 'node:inspector/promises';\nimport {tmpdir} from 'node:os';\nimport {join} from 'node:path';\nimport type {LogContext} from '@rocicorp/logger';\n\n/**\n * Convenience wrapper around a `node:inspector` {@link Session} for\n * optionally taking cpu profiles.\n */\nexport class CpuProfiler {\n static async connect() {\n const session = new Session();\n session.connect();\n await session.post('Profiler.enable');\n return new CpuProfiler(session);\n }\n\n readonly #session;\n\n private constructor(session: Session) {\n this.#session = session;\n }\n\n async start() {\n await this.#session.post('Profiler.start');\n }\n\n async stopAndDispose(lc: LogContext, filename: string) {\n const {profile} = await this.#session.post('Profiler.stop');\n const path = join(tmpdir(), `${filename}.cpuprofile`);\n await writeFile(path, JSON.stringify(profile));\n lc.info?.(`wrote cpu profile to ${path}`);\n this.#session.disconnect();\n }\n}\n"],"mappings":";;;;;;;;;AAUA,IAAa,cAAb,MAAa,YAAY;CACvB,aAAa,UAAU;EACrB,MAAM,UAAU,IAAI,SAAS;AAC7B,UAAQ,SAAS;AACjB,QAAM,QAAQ,KAAK,kBAAkB;AACrC,SAAO,IAAI,YAAY,QAAQ;;CAGjC;CAEA,YAAoB,SAAkB;AACpC,QAAA,UAAgB;;CAGlB,MAAM,QAAQ;AACZ,QAAM,MAAA,QAAc,KAAK,iBAAiB;;CAG5C,MAAM,eAAe,IAAgB,UAAkB;EACrD,MAAM,EAAC,YAAW,MAAM,MAAA,QAAc,KAAK,gBAAgB;EAC3D,MAAM,OAAO,KAAK,QAAQ,EAAE,GAAG,SAAS,aAAa;AACrD,QAAM,UAAU,MAAM,KAAK,UAAU,QAAQ,CAAC;AAC9C,KAAG,OAAO,wBAAwB,OAAO;AACzC,QAAA,QAAc,YAAY"}
@@ -1 +1 @@
1
- {"version":3,"file":"row-key.js","names":[],"sources":["../../../../../zero-cache/src/types/row-key.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport {stringify, type JSONValue} from '../../../shared/src/bigint-json.ts';\nimport {h128} from '../../../shared/src/hash.ts';\n\nexport type ColumnType = {readonly typeOid: number};\nexport type RowKeyType = Readonly<Record<string, ColumnType>>;\nexport type RowKey = Readonly<Record<string, JSONValue>>;\n\nexport type RowID = Readonly<{schema: string; table: string; rowKey: RowKey}>;\n\n// Aliased for documentation purposes when dealing with full rows vs row keys.\n// The actual structure of the objects is the same.\nexport type RowType = RowKeyType;\nexport type RowValue = RowKey;\n\n/**\n * Returns the `RowKey` such that key iteration produces a sorted sequence. If the\n * keys are already sorted, the input is returned as is.\n *\n * Note that the value type is parameterized as `V` so that this method can be used\n * for both (pg) RowKeys and LiteRowKeys.\n */\nexport function normalizedKeyOrder<V>(\n rowKey: Readonly<Record<string, V>>,\n): Readonly<Record<string, V>> {\n let last = '';\n let empty = true;\n for (const col in rowKey) {\n empty = false;\n if (last > col) {\n const entries = Object.entries(rowKey).sort(([a], [b]) =>\n a < b ? -1 : a > b ? 1 : 0,\n );\n assert(entries.length > 0, 'empty row key');\n return Object.fromEntries(entries);\n }\n last = col;\n }\n assert(!empty, 'empty row key');\n // This case iterates over columns and avoids object allocations, which is\n // expected to be the common case (e.g. single column key).\n return rowKey;\n}\n\n/**\n * Returns a normalized string suitable for representing a row key in a form\n * that can be used as a Map key.\n */\nexport function rowKeyString(key: RowKey): string {\n return stringify(tuples(key));\n}\n\nfunction tuples(key: RowKey) {\n return Object.entries(normalizedKeyOrder(key)).flat();\n}\n\nconst rowIDStrings = new WeakMap<RowID, string>();\n\n/**\n * A normalized string representation of a {@link RowID} suitable to use\n * as a Map key. Use {@link rowIDHash} if you need string keys of bounded\n * length.\n */\nexport function rowIDString(id: RowID): string {\n let val = rowIDStrings.get(id);\n if (val) {\n return val;\n }\n val = stringify([id.schema, id.table, ...tuples(id.rowKey)]);\n rowIDStrings.set(id, val);\n return val;\n}\n\nconst rowIDHashes = new WeakMap<RowID, string>();\n\n/**\n * A RowIDHash is a 128-bit column-order-agnostic hash of the schema, table name, and\n * column name / value tuples of a row key. It serves as a compact identifier for\n * a row in the database that:\n *\n * * is guaranteed to fit within the constraints of the CVR store (Durable Object\n * storage keys cannot exceed 2KiB)\n * * can be used to compactly encode (and lookup) the rows of query results for CVR\n * bookkeeping.\n *\n * The hash is encoded in `base36`, with the maximum 128-bit value being 25 characters long.\n */\nexport function rowIDHash(id: RowID): string {\n let hash = rowIDHashes.get(id);\n if (hash) {\n return hash;\n }\n\n const str = rowIDString(id);\n hash = h128(str).toString(36);\n rowIDHashes.set(id, hash);\n return hash;\n}\n"],"mappings":";;;;;;;;;;;AAsBA,SAAgB,mBACd,QAC6B;CAC7B,IAAI,OAAO;CACX,IAAI,QAAQ;CACZ,KAAK,MAAM,OAAO,QAAQ;EACxB,QAAQ;EACR,IAAI,OAAO,KAAK;GACd,MAAM,UAAU,OAAO,QAAQ,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,OACjD,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,CAC3B;GACA,OAAO,QAAQ,SAAS,GAAG,eAAe;GAC1C,OAAO,OAAO,YAAY,OAAO;EACnC;EACA,OAAO;CACT;CACA,OAAO,CAAC,OAAO,eAAe;CAG9B,OAAO;AACT;AAUA,SAAS,OAAO,KAAa;CAC3B,OAAO,OAAO,QAAQ,mBAAmB,GAAG,CAAC,EAAE,KAAK;AACtD;AAEA,IAAM,+BAAe,IAAI,QAAuB;;;;;;AAOhD,SAAgB,YAAY,IAAmB;CAC7C,IAAI,MAAM,aAAa,IAAI,EAAE;CAC7B,IAAI,KACF,OAAO;CAET,MAAM,UAAU;EAAC,GAAG;EAAQ,GAAG;EAAO,GAAG,OAAO,GAAG,MAAM;CAAC,CAAC;CAC3D,aAAa,IAAI,IAAI,GAAG;CACxB,OAAO;AACT"}
1
+ {"version":3,"file":"row-key.js","names":[],"sources":["../../../../../zero-cache/src/types/row-key.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport {stringify, type JSONValue} from '../../../shared/src/bigint-json.ts';\nimport {h128} from '../../../shared/src/hash.ts';\n\nexport type ColumnType = {readonly typeOid: number};\nexport type RowKeyType = Readonly<Record<string, ColumnType>>;\nexport type RowKey = Readonly<Record<string, JSONValue>>;\n\nexport type RowID = Readonly<{schema: string; table: string; rowKey: RowKey}>;\n\n// Aliased for documentation purposes when dealing with full rows vs row keys.\n// The actual structure of the objects is the same.\nexport type RowType = RowKeyType;\nexport type RowValue = RowKey;\n\n/**\n * Returns the `RowKey` such that key iteration produces a sorted sequence. If the\n * keys are already sorted, the input is returned as is.\n *\n * Note that the value type is parameterized as `V` so that this method can be used\n * for both (pg) RowKeys and LiteRowKeys.\n */\nexport function normalizedKeyOrder<V>(\n rowKey: Readonly<Record<string, V>>,\n): Readonly<Record<string, V>> {\n let last = '';\n let empty = true;\n for (const col in rowKey) {\n empty = false;\n if (last > col) {\n const entries = Object.entries(rowKey).sort(([a], [b]) =>\n a < b ? -1 : a > b ? 1 : 0,\n );\n assert(entries.length > 0, 'empty row key');\n return Object.fromEntries(entries);\n }\n last = col;\n }\n assert(!empty, 'empty row key');\n // This case iterates over columns and avoids object allocations, which is\n // expected to be the common case (e.g. single column key).\n return rowKey;\n}\n\n/**\n * Returns a normalized string suitable for representing a row key in a form\n * that can be used as a Map key.\n */\nexport function rowKeyString(key: RowKey): string {\n return stringify(tuples(key));\n}\n\nfunction tuples(key: RowKey) {\n return Object.entries(normalizedKeyOrder(key)).flat();\n}\n\nconst rowIDStrings = new WeakMap<RowID, string>();\n\n/**\n * A normalized string representation of a {@link RowID} suitable to use\n * as a Map key. Use {@link rowIDHash} if you need string keys of bounded\n * length.\n */\nexport function rowIDString(id: RowID): string {\n let val = rowIDStrings.get(id);\n if (val) {\n return val;\n }\n val = stringify([id.schema, id.table, ...tuples(id.rowKey)]);\n rowIDStrings.set(id, val);\n return val;\n}\n\nconst rowIDHashes = new WeakMap<RowID, string>();\n\n/**\n * A RowIDHash is a 128-bit column-order-agnostic hash of the schema, table name, and\n * column name / value tuples of a row key. It serves as a compact identifier for\n * a row in the database that:\n *\n * * is guaranteed to fit within the constraints of the CVR store (Durable Object\n * storage keys cannot exceed 2KiB)\n * * can be used to compactly encode (and lookup) the rows of query results for CVR\n * bookkeeping.\n *\n * The hash is encoded in `base36`, with the maximum 128-bit value being 25 characters long.\n */\nexport function rowIDHash(id: RowID): string {\n let hash = rowIDHashes.get(id);\n if (hash) {\n return hash;\n }\n\n const str = rowIDString(id);\n hash = h128(str).toString(36);\n rowIDHashes.set(id, hash);\n return hash;\n}\n"],"mappings":";;;;;;;;;;;AAsBA,SAAgB,mBACd,QAC6B;CAC7B,IAAI,OAAO;CACX,IAAI,QAAQ;AACZ,MAAK,MAAM,OAAO,QAAQ;AACxB,UAAQ;AACR,MAAI,OAAO,KAAK;GACd,MAAM,UAAU,OAAO,QAAQ,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,OACjD,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,EAC1B;AACD,UAAO,QAAQ,SAAS,GAAG,gBAAgB;AAC3C,UAAO,OAAO,YAAY,QAAQ;;AAEpC,SAAO;;AAET,QAAO,CAAC,OAAO,gBAAgB;AAG/B,QAAO;;AAWT,SAAS,OAAO,KAAa;AAC3B,QAAO,OAAO,QAAQ,mBAAmB,IAAI,CAAC,CAAC,MAAM;;AAGvD,IAAM,+BAAe,IAAI,SAAwB;;;;;;AAOjD,SAAgB,YAAY,IAAmB;CAC7C,IAAI,MAAM,aAAa,IAAI,GAAG;AAC9B,KAAI,IACF,QAAO;AAET,OAAM,UAAU;EAAC,GAAG;EAAQ,GAAG;EAAO,GAAG,OAAO,GAAG,OAAO;EAAC,CAAC;AAC5D,cAAa,IAAI,IAAI,IAAI;AACzB,QAAO"}
@@ -1 +1 @@
1
- {"version":3,"file":"shards.js","names":[],"sources":["../../../../../zero-cache/src/types/shards.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\n\nexport type AppID = {\n readonly appID: string;\n};\n\nexport type ShardID = AppID & {\n readonly shardNum: number;\n};\n\nexport type ShardConfig = ShardID & {\n readonly publications: readonly string[];\n};\n\n// Gets a ShardID from a ZeroConfig.\nexport function getShardID({\n app,\n shard,\n}: {\n app: {id: string};\n shard: {num: number};\n}): ShardID {\n return {\n appID: app.id,\n shardNum: shard.num,\n };\n}\n\n// Gets a ShardConfig from a ZeroConfig.\nexport function getShardConfig({\n app,\n shard,\n}: {\n app: {id: string; publications: string[]};\n shard: {num: number};\n}): ShardConfig {\n return {\n appID: app.id,\n shardNum: shard.num,\n publications: app.publications,\n };\n}\n\n// Constrained by https://www.postgresql.org/docs/current/warm-standby.html#STREAMING-REPLICATION-SLOTS-MANIPULATION\nexport const ALLOWED_APP_ID_CHARACTERS = /^[a-z0-9_]+$/;\n\nexport const INVALID_APP_ID_MESSAGE =\n 'The App ID may only consist of lower-case letters, numbers, and the underscore character';\n\nexport function check(shard: ShardID): {appID: string; shardNum: number} {\n const {appID, shardNum} = shard;\n if (!ALLOWED_APP_ID_CHARACTERS.test(appID)) {\n throw new Error(INVALID_APP_ID_MESSAGE);\n }\n assert(typeof shardNum === 'number', 'shardNum must be a number');\n return {appID, shardNum};\n}\n\nexport function appSchema({appID}: AppID) {\n check({appID, shardNum: 0});\n return appID;\n}\n\nexport function upstreamSchema(shard: ShardID) {\n const {appID, shardNum} = check(shard);\n return `${appID}_${shardNum}`;\n}\n\nexport function cdcSchema(shard: ShardID) {\n const {appID, shardNum} = check(shard);\n return `${appID}_${shardNum}/cdc`;\n}\n\nexport function cvrSchema(shard: ShardID) {\n const {appID, shardNum} = check(shard);\n return `${appID}_${shardNum}/cvr`;\n}\n"],"mappings":";;AAeA,SAAgB,WAAW,EACzB,KACA,SAIU;CACV,OAAO;EACL,OAAO,IAAI;EACX,UAAU,MAAM;CAClB;AACF;AAGA,SAAgB,eAAe,EAC7B,KACA,SAIc;CACd,OAAO;EACL,OAAO,IAAI;EACX,UAAU,MAAM;EAChB,cAAc,IAAI;CACpB;AACF;AAGA,IAAa,4BAA4B;AAEzC,IAAa,yBACX;AAEF,SAAgB,MAAM,OAAmD;CACvE,MAAM,EAAC,OAAO,aAAY;CAC1B,IAAI,CAAC,0BAA0B,KAAK,KAAK,GACvC,MAAM,IAAI,MAAM,sBAAsB;CAExC,OAAO,OAAO,aAAa,UAAU,2BAA2B;CAChE,OAAO;EAAC;EAAO;CAAQ;AACzB;AAEA,SAAgB,UAAU,EAAC,SAAe;CACxC,MAAM;EAAC;EAAO,UAAU;CAAC,CAAC;CAC1B,OAAO;AACT;AAEA,SAAgB,eAAe,OAAgB;CAC7C,MAAM,EAAC,OAAO,aAAY,MAAM,KAAK;CACrC,OAAO,GAAG,MAAM,GAAG;AACrB;AAEA,SAAgB,UAAU,OAAgB;CACxC,MAAM,EAAC,OAAO,aAAY,MAAM,KAAK;CACrC,OAAO,GAAG,MAAM,GAAG,SAAS;AAC9B;AAEA,SAAgB,UAAU,OAAgB;CACxC,MAAM,EAAC,OAAO,aAAY,MAAM,KAAK;CACrC,OAAO,GAAG,MAAM,GAAG,SAAS;AAC9B"}
1
+ {"version":3,"file":"shards.js","names":[],"sources":["../../../../../zero-cache/src/types/shards.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\n\nexport type AppID = {\n readonly appID: string;\n};\n\nexport type ShardID = AppID & {\n readonly shardNum: number;\n};\n\nexport type ShardConfig = ShardID & {\n readonly publications: readonly string[];\n};\n\n// Gets a ShardID from a ZeroConfig.\nexport function getShardID({\n app,\n shard,\n}: {\n app: {id: string};\n shard: {num: number};\n}): ShardID {\n return {\n appID: app.id,\n shardNum: shard.num,\n };\n}\n\n// Gets a ShardConfig from a ZeroConfig.\nexport function getShardConfig({\n app,\n shard,\n}: {\n app: {id: string; publications: string[]};\n shard: {num: number};\n}): ShardConfig {\n return {\n appID: app.id,\n shardNum: shard.num,\n publications: app.publications,\n };\n}\n\n// Constrained by https://www.postgresql.org/docs/current/warm-standby.html#STREAMING-REPLICATION-SLOTS-MANIPULATION\nexport const ALLOWED_APP_ID_CHARACTERS = /^[a-z0-9_]+$/;\n\nexport const INVALID_APP_ID_MESSAGE =\n 'The App ID may only consist of lower-case letters, numbers, and the underscore character';\n\nexport function check(shard: ShardID): {appID: string; shardNum: number} {\n const {appID, shardNum} = shard;\n if (!ALLOWED_APP_ID_CHARACTERS.test(appID)) {\n throw new Error(INVALID_APP_ID_MESSAGE);\n }\n assert(typeof shardNum === 'number', 'shardNum must be a number');\n return {appID, shardNum};\n}\n\nexport function appSchema({appID}: AppID) {\n check({appID, shardNum: 0});\n return appID;\n}\n\nexport function upstreamSchema(shard: ShardID) {\n const {appID, shardNum} = check(shard);\n return `${appID}_${shardNum}`;\n}\n\nexport function cdcSchema(shard: ShardID) {\n const {appID, shardNum} = check(shard);\n return `${appID}_${shardNum}/cdc`;\n}\n\nexport function cvrSchema(shard: ShardID) {\n const {appID, shardNum} = check(shard);\n return `${appID}_${shardNum}/cvr`;\n}\n"],"mappings":";;AAeA,SAAgB,WAAW,EACzB,KACA,SAIU;AACV,QAAO;EACL,OAAO,IAAI;EACX,UAAU,MAAM;EACjB;;AAIH,SAAgB,eAAe,EAC7B,KACA,SAIc;AACd,QAAO;EACL,OAAO,IAAI;EACX,UAAU,MAAM;EAChB,cAAc,IAAI;EACnB;;AAIH,IAAa,4BAA4B;AAEzC,IAAa,yBACX;AAEF,SAAgB,MAAM,OAAmD;CACvE,MAAM,EAAC,OAAO,aAAY;AAC1B,KAAI,CAAC,0BAA0B,KAAK,MAAM,CACxC,OAAM,IAAI,MAAM,uBAAuB;AAEzC,QAAO,OAAO,aAAa,UAAU,4BAA4B;AACjE,QAAO;EAAC;EAAO;EAAS;;AAG1B,SAAgB,UAAU,EAAC,SAAe;AACxC,OAAM;EAAC;EAAO,UAAU;EAAE,CAAC;AAC3B,QAAO;;AAGT,SAAgB,eAAe,OAAgB;CAC7C,MAAM,EAAC,OAAO,aAAY,MAAM,MAAM;AACtC,QAAO,GAAG,MAAM,GAAG;;AAGrB,SAAgB,UAAU,OAAgB;CACxC,MAAM,EAAC,OAAO,aAAY,MAAM,MAAM;AACtC,QAAO,GAAG,MAAM,GAAG,SAAS;;AAG9B,SAAgB,UAAU,OAAgB;CACxC,MAAM,EAAC,OAAO,aAAY,MAAM,MAAM;AACtC,QAAO,GAAG,MAAM,GAAG,SAAS"}
@@ -1 +1 @@
1
- {"version":3,"file":"sql.js","names":[],"sources":["../../../../../zero-cache/src/types/sql.ts"],"sourcesContent":["/**\n * Escapes the identifier with double quotes, as per:\n *\n * https://www.postgresql.org/docs/16/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS\n */\nexport function id(name: string): string {\n return '\"' + name.replace(/\"/g, '\"\"') + '\"'; //.replace(/\\./g, '\".\"') + '\"';\n}\n\n/**\n * Escapes and comma-separates a list of identifiers.\n */\nexport function idList(names: Iterable<string>): string {\n return Array.from(names, name => id(name)).join(',');\n}\n\n/**\n * Escapes a string literal with single quotes, as per:\n * https://www.postgresql.org/docs/16/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS\n */\nexport function lit(value: string): string {\n return \"'\" + value.replace(/'/g, \"''\") + \"'\";\n}\n"],"mappings":";;;;;;AAKA,SAAgB,GAAG,MAAsB;CACvC,OAAO,OAAM,KAAK,QAAQ,MAAM,MAAI,IAAI;AAC1C;;;;AAKA,SAAgB,OAAO,OAAiC;CACtD,OAAO,MAAM,KAAK,QAAO,SAAQ,GAAG,IAAI,CAAC,EAAE,KAAK,GAAG;AACrD;;;;;AAMA,SAAgB,IAAI,OAAuB;CACzC,OAAO,MAAM,MAAM,QAAQ,MAAM,IAAI,IAAI;AAC3C"}
1
+ {"version":3,"file":"sql.js","names":[],"sources":["../../../../../zero-cache/src/types/sql.ts"],"sourcesContent":["/**\n * Escapes the identifier with double quotes, as per:\n *\n * https://www.postgresql.org/docs/16/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS\n */\nexport function id(name: string): string {\n return '\"' + name.replace(/\"/g, '\"\"') + '\"'; //.replace(/\\./g, '\".\"') + '\"';\n}\n\n/**\n * Escapes and comma-separates a list of identifiers.\n */\nexport function idList(names: Iterable<string>): string {\n return Array.from(names, name => id(name)).join(',');\n}\n\n/**\n * Escapes a string literal with single quotes, as per:\n * https://www.postgresql.org/docs/16/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS\n */\nexport function lit(value: string): string {\n return \"'\" + value.replace(/'/g, \"''\") + \"'\";\n}\n"],"mappings":";;;;;;AAKA,SAAgB,GAAG,MAAsB;AACvC,QAAO,OAAM,KAAK,QAAQ,MAAM,OAAK,GAAG;;;;;AAM1C,SAAgB,OAAO,OAAiC;AACtD,QAAO,MAAM,KAAK,QAAO,SAAQ,GAAG,KAAK,CAAC,CAAC,KAAK,IAAI;;;;;;AAOtD,SAAgB,IAAI,OAAuB;AACzC,QAAO,MAAM,MAAM,QAAQ,MAAM,KAAK,GAAG"}
@@ -1 +1 @@
1
- {"version":3,"file":"state-version.js","names":[],"sources":["../../../../../zero-cache/src/types/state-version.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport {versionFromLexi, versionToLexi} from './lexi-version.ts';\n\n/**\n * Identifies the version of the data on the replica, corresponding to the\n * stream of changes produced by the change-source and change-streamer.\n *\n * The `major` version directly tracks the watermark of the replication\n * stream (e.g. the Postgres LSN).\n *\n * The `minor` version is optional and used to auxiliary state changes,\n * such as writes from pending backfills.\n *\n * StateVersions are persisted and passed as lexicographically ordered\n * strings, using the LexiVersion format for major and minor versions,\n * separated by a dot. If there is no minor version, the StateVersion\n * is represented by a single LexiVersion.\n */\nexport type StateVersion = {\n major: bigint;\n minor?: bigint | undefined;\n};\n\ntype StateVersionInput = {\n major: bigint | number;\n minor?: bigint | number | undefined;\n};\n\nexport function stateVersionFromString(ver: string): StateVersion {\n if (!ver.includes('.')) {\n return {major: versionFromLexi(ver)};\n }\n const parts = ver.split('.');\n assert(parts.length === 2, () => `Invalid stateVersion ${ver}`);\n return {\n major: versionFromLexi(parts[0]),\n minor: versionFromLexi(parts[1]),\n };\n}\n\nexport function stateVersionToString(ver: StateVersionInput) {\n return ver.minor === undefined\n ? versionToLexi(ver.major)\n : `${versionToLexi(ver.major)}.${versionToLexi(ver.minor)}`;\n}\n\nexport function majorVersionFromString(ver: string): bigint {\n if (!ver.includes('.')) {\n return versionFromLexi(ver);\n }\n const {major} = stateVersionFromString(ver);\n return major;\n}\n\nexport function majorVersionToString(major: number | bigint) {\n return versionToLexi(major);\n}\n"],"mappings":";;;AA4BA,SAAgB,uBAAuB,KAA2B;CAChE,IAAI,CAAC,IAAI,SAAS,GAAG,GACnB,OAAO,EAAC,OAAO,gBAAgB,GAAG,EAAC;CAErC,MAAM,QAAQ,IAAI,MAAM,GAAG;CAC3B,OAAO,MAAM,WAAW,SAAS,wBAAwB,KAAK;CAC9D,OAAO;EACL,OAAO,gBAAgB,MAAM,EAAE;EAC/B,OAAO,gBAAgB,MAAM,EAAE;CACjC;AACF;AAEA,SAAgB,qBAAqB,KAAwB;CAC3D,OAAO,IAAI,UAAU,KAAA,IACjB,cAAc,IAAI,KAAK,IACvB,GAAG,cAAc,IAAI,KAAK,EAAE,GAAG,cAAc,IAAI,KAAK;AAC5D;AAEA,SAAgB,uBAAuB,KAAqB;CAC1D,IAAI,CAAC,IAAI,SAAS,GAAG,GACnB,OAAO,gBAAgB,GAAG;CAE5B,MAAM,EAAC,UAAS,uBAAuB,GAAG;CAC1C,OAAO;AACT;AAEA,SAAgB,qBAAqB,OAAwB;CAC3D,OAAO,cAAc,KAAK;AAC5B"}
1
+ {"version":3,"file":"state-version.js","names":[],"sources":["../../../../../zero-cache/src/types/state-version.ts"],"sourcesContent":["import {assert} from '../../../shared/src/asserts.ts';\nimport {versionFromLexi, versionToLexi} from './lexi-version.ts';\n\n/**\n * Identifies the version of the data on the replica, corresponding to the\n * stream of changes produced by the change-source and change-streamer.\n *\n * The `major` version directly tracks the watermark of the replication\n * stream (e.g. the Postgres LSN).\n *\n * The `minor` version is optional and used to auxiliary state changes,\n * such as writes from pending backfills.\n *\n * StateVersions are persisted and passed as lexicographically ordered\n * strings, using the LexiVersion format for major and minor versions,\n * separated by a dot. If there is no minor version, the StateVersion\n * is represented by a single LexiVersion.\n */\nexport type StateVersion = {\n major: bigint;\n minor?: bigint | undefined;\n};\n\ntype StateVersionInput = {\n major: bigint | number;\n minor?: bigint | number | undefined;\n};\n\nexport function stateVersionFromString(ver: string): StateVersion {\n if (!ver.includes('.')) {\n return {major: versionFromLexi(ver)};\n }\n const parts = ver.split('.');\n assert(parts.length === 2, () => `Invalid stateVersion ${ver}`);\n return {\n major: versionFromLexi(parts[0]),\n minor: versionFromLexi(parts[1]),\n };\n}\n\nexport function stateVersionToString(ver: StateVersionInput) {\n return ver.minor === undefined\n ? versionToLexi(ver.major)\n : `${versionToLexi(ver.major)}.${versionToLexi(ver.minor)}`;\n}\n\nexport function majorVersionFromString(ver: string): bigint {\n if (!ver.includes('.')) {\n return versionFromLexi(ver);\n }\n const {major} = stateVersionFromString(ver);\n return major;\n}\n\nexport function majorVersionToString(major: number | bigint) {\n return versionToLexi(major);\n}\n"],"mappings":";;;AA4BA,SAAgB,uBAAuB,KAA2B;AAChE,KAAI,CAAC,IAAI,SAAS,IAAI,CACpB,QAAO,EAAC,OAAO,gBAAgB,IAAI,EAAC;CAEtC,MAAM,QAAQ,IAAI,MAAM,IAAI;AAC5B,QAAO,MAAM,WAAW,SAAS,wBAAwB,MAAM;AAC/D,QAAO;EACL,OAAO,gBAAgB,MAAM,GAAG;EAChC,OAAO,gBAAgB,MAAM,GAAG;EACjC;;AAGH,SAAgB,qBAAqB,KAAwB;AAC3D,QAAO,IAAI,UAAU,KAAA,IACjB,cAAc,IAAI,MAAM,GACxB,GAAG,cAAc,IAAI,MAAM,CAAC,GAAG,cAAc,IAAI,MAAM;;AAG7D,SAAgB,uBAAuB,KAAqB;AAC1D,KAAI,CAAC,IAAI,SAAS,IAAI,CACpB,QAAO,gBAAgB,IAAI;CAE7B,MAAM,EAAC,UAAS,uBAAuB,IAAI;AAC3C,QAAO;;AAGT,SAAgB,qBAAqB,OAAwB;AAC3D,QAAO,cAAc,MAAM"}
@@ -1 +1 @@
1
- {"version":3,"file":"streams.js","names":["#lc","#ws","#closeStream","#messageHandler","#connected","#handleOpen","#handleClose","#handleError","#conn"],"sources":["../../../../../zero-cache/src/types/streams.ts"],"sourcesContent":["import {\n pipeline,\n Readable,\n Transform,\n Writable,\n type DuplexOptions,\n} from 'node:stream';\nimport type {LogContext} from '@rocicorp/logger';\nimport {resolver} from '@rocicorp/resolver';\nimport {\n createWebSocketStream,\n type CloseEvent,\n type ErrorEvent,\n type MessageEvent,\n type WebSocket,\n} from 'ws';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport {BigIntJSON, type JSONValue} from '../../../shared/src/bigint-json.ts';\nimport {Queue} from '../../../shared/src/queue.ts';\nimport * as v from '../../../shared/src/valita.ts';\nimport {Subscription, type Options} from './subscription.ts';\nimport {\n closeWithError,\n expectPingsForLiveness,\n sendPingsForLiveness,\n} from './ws.ts';\n\n// Consistent with Postgres keepalives, and shorter than the\n// commonly used default idle timeout of 1 minute.\nconst PING_INTERVAL_MS = 30_000;\n\nexport type Source<T> = AsyncIterable<T> & {\n /**\n * Immediately terminates all current iterations (i.e. {@link AsyncIterator.next next()})\n * will return `{value: undefined, done: true}`), and prevents any subsequent iterations\n * from yielding any values.\n *\n * @param err Terminate the iteration by throwing the `err` instead.\n */\n cancel: (err?: Error) => void;\n\n /**\n * The presence of a `pipeline` iterable allows the usual \"consumed-on-iterate\" semantics\n * to be overridden.\n *\n * This is suitable for transport layers that serialize messages across processes, such\n * as the {@link streamOut()} method; pipelining allows the transport to send messages\n * as they arrive without waiting for the previous message to be acked, streaming\n * them to the receiving process where they are presumably queued and processed without\n * a per-message ack delay. The receiving end of the transport then responds with acks\n * asynchronously as the receiving end processes the messages.\n */\n pipeline?: AsyncIterable<{value: T; consumed: () => void}> | undefined;\n};\n\nexport type Sink<T> = {\n push(message: T): void;\n};\n\n/**\n * Back-pressure-aware transformation of a WebSocket into\n * upstream and downstream {@link Subscription} objects.\n */\n// TODO: Change {@link streamIn} and {@link streamOut} to use this\n// under the covers so that internal communication is also\n// responsive to backpressure.\nexport function stream<In extends JSONValue, Out extends JSONValue>(\n lc: LogContext,\n ws: WebSocket,\n inSchema: v.Type<In>,\n outOptions: Options<Out> = {},\n inOptions: Options<In> = {},\n streamOptions: DuplexOptions = {},\n): {outstream: Sink<Out>; instream: Source<In>} {\n const endpoint = ws.url ?? 'client';\n function close(err?: unknown) {\n if (ws.readyState !== ws.CLOSED && ws.readyState !== ws.CLOSING) {\n if (err) {\n closeWithError(lc, ws, err);\n } else {\n lc.info?.(`closing connection to ${endpoint}`);\n ws.close();\n }\n }\n }\n\n const instream = Subscription.create<In>({\n ...inOptions,\n cleanup: (unconsumed, err) => {\n inOptions.cleanup?.(unconsumed, err);\n close(err);\n },\n });\n const outstream = Subscription.create<Out>({\n ...outOptions,\n cleanup: (unconsumed, err) => {\n outOptions.cleanup?.(unconsumed, err);\n close(err);\n },\n });\n\n const duplex = createWebSocketStream(ws, {\n ...streamOptions,\n decodeStrings: false,\n });\n\n // Outgoing transform.\n function streamOut() {\n // Mainly used for verifying that back-pressure kicks in tests.\n duplex.on('drain', () => lc.debug?.(`drained messages to ${endpoint}`));\n\n pipeline(\n Readable.from(outstream),\n new Transform({\n objectMode: true,\n transform: (msg, _encoding, callback) =>\n callback(null, BigIntJSON.stringify(msg)),\n }),\n duplex,\n err => (err ? outstream.fail(err) : outstream.cancel()),\n );\n }\n\n if (ws.readyState === ws.CONNECTING) {\n ws.on('open', () => {\n lc.info?.(`connected to ${endpoint}`);\n streamOut();\n });\n } else {\n streamOut();\n }\n\n // Incoming transform.\n pipe({\n source: duplex,\n sink: instream,\n parse: chunk => {\n const json = BigIntJSON.parse(chunk.toString());\n return v.parse(json, inSchema, 'passthrough');\n },\n });\n\n sendPingsForLiveness(lc, ws, PING_INTERVAL_MS);\n\n return {outstream, instream};\n}\n\ntype PipeOptions<T> = {\n source: Readable;\n sink: Subscription<T>;\n parse: (buffer: Buffer) => T | null;\n bufferMessages?: number;\n};\n\nexport function pipe<T>({source, sink, parse, bufferMessages}: PipeOptions<T>) {\n bufferMessages ??= 0;\n assert(bufferMessages >= 0, 'bufferMessages must be non-negative');\n const pending: Promise<unknown>[] = [];\n\n pipeline(\n source,\n new Writable({\n decodeStrings: false,\n write: (chunk, _encoding, callback) => {\n let msg: T | null;\n try {\n if ((msg = parse(chunk)) === null) {\n callback();\n return;\n }\n } catch (err) {\n callback(ensureError(err));\n return;\n }\n // Inbound backpressure is exerted by unconsumed messages in the\n // subscription. A buffer can be used to allow messages to queue up in\n // in the Subscription object, which allows the consumer to \"peek\" at\n // whether there are more messages immediately available\n // (via {@link Subscription.queued}.\n const {result} = sink.push(msg);\n pending.push(result);\n void result.then(() => pending.shift());\n\n if (pending.length <= bufferMessages) {\n // immediately allow more messages\n callback();\n } else {\n // wait for the oldest result in the pending queue\n pending[0].then(\n () => callback(),\n err => callback(ensureError(err)),\n );\n }\n },\n destroy: (err, callback) => {\n if (err) {\n sink.fail(ensureError(err));\n }\n // Otherwise, final will handle the cancel.\n callback();\n },\n final: callback => {\n sink.cancel();\n callback();\n },\n }),\n err => (err ? sink.fail(err) : sink.cancel()),\n );\n}\n\nfunction ensureError(err: unknown) {\n return err instanceof Error ? err : new Error(String(err));\n}\n\nconst ackSchema = v.object({ack: v.number()});\n\ntype Ack = v.Infer<typeof ackSchema>;\n\ntype Streamed<T> = {\n /** Application-level message. */\n msg: T;\n\n /** ID used for the Ack message. */\n id: number;\n};\n\nexport function streamOut<T extends JSONValue>(\n lc: LogContext,\n source: Source<T>,\n sink: WebSocket,\n): Promise<void> {\n return streamOutInternal(lc, source, sink, BigIntJSON.stringify);\n}\n\n/**\n * Streams out a `Source` for which messages are already stringified JSON.\n */\nexport function streamOutStringified(\n lc: LogContext,\n source: Source<string>,\n sink: WebSocket,\n): Promise<void> {\n return streamOutInternal(lc, source, sink, json => json);\n}\n\nasync function streamOutInternal<T extends JSONValue>(\n lc: LogContext,\n source: Source<T>,\n sink: WebSocket,\n stringify: (payload: T) => string,\n): Promise<void> {\n sendPingsForLiveness(lc, sink, PING_INTERVAL_MS);\n\n const closer = WebSocketCloser.forSource(lc, sink, source);\n\n const acks = new Queue<Ack>();\n sink.addEventListener('message', ({data}) => {\n try {\n if (typeof data !== 'string') {\n throw new Error('Expected string message');\n }\n acks.enqueue(v.parse(JSON.parse(data), ackSchema));\n } catch (e) {\n lc.error?.(`error parsing ack`, e);\n closer.close(e);\n }\n });\n\n try {\n let nextID = 0;\n const {pipeline} = source;\n if (pipeline) {\n lc.debug?.(`started pipelined outbound stream`);\n for await (const {value: msg, consumed} of pipeline) {\n const id = ++nextID;\n const data = `{\"id\":${id},\"msg\":${stringify(msg)}}`;\n // Enable for debugging. Otherwise too verbose.\n // lc.debug?.(`pipelining`, data);\n sink.send(data);\n\n void (async () => {\n const {ack} = await acks.dequeue();\n // lc.debug?.(`received ack`, ack);\n if (ack !== id) {\n throw new Error(`Unexpected ack for ${id}: ${ack}`);\n }\n consumed();\n })();\n }\n } else {\n lc.debug?.(`started synchronous outbound stream`);\n for await (const msg of source) {\n const id = ++nextID;\n const data = `{\"id\":${id},\"msg\":${stringify(msg)}}`;\n // Enable for debugging. Otherwise too verbose.\n // lc.debug?.(`sending`, data);\n sink.send(data);\n\n const {ack} = await acks.dequeue();\n if (ack !== id) {\n throw new Error(`Unexpected ack for ${id}: ${ack}`);\n }\n }\n }\n closer.close();\n } catch (e) {\n closer.close(e);\n }\n}\n\nexport async function streamIn<T extends JSONValue>(\n lc: LogContext,\n source: WebSocket,\n schema: v.Type<T>,\n): Promise<Source<T>> {\n expectPingsForLiveness(lc, source, PING_INTERVAL_MS);\n\n const streamedSchema = v.object({\n msg: schema,\n id: v.number(),\n });\n\n const sink: Subscription<T, Streamed<T>> = new Subscription<T, Streamed<T>>(\n {\n consumed: ({id}) => source.send(JSON.stringify({ack: id} satisfies Ack)),\n cleanup: () => closer.close(),\n },\n ({msg}) => msg,\n );\n\n const closer = WebSocketCloser.forSink(lc, source, sink, handleMessage);\n\n function handleMessage(event: MessageEvent) {\n const data = event.data.toString();\n if (!sink.active) {\n lc.warn?.('dropping ws message received after close', data);\n return;\n }\n try {\n const value = BigIntJSON.parse(data);\n const msg = v.parse(value, streamedSchema, 'passthrough');\n // Enable for debugging. Otherwise too verbose.\n // lc.debug?.(`received`, data);\n sink.push(msg);\n } catch (e) {\n closer.close(e);\n }\n }\n\n await closer.connected;\n return sink;\n}\n\nclass WebSocketCloser {\n readonly #lc: LogContext;\n readonly #ws: WebSocket;\n readonly #closeStream: () => void;\n readonly #messageHandler: ((e: MessageEvent) => void | undefined) | null;\n readonly #connected = resolver();\n\n get connected(): Promise<void> {\n return this.#connected.promise;\n }\n\n static forSource<T>(lc: LogContext, ws: WebSocket, stream: Source<T>) {\n // If the websocket is closed, call cancel() to notify the Source of\n // any unconsumed messages.\n return new WebSocketCloser(lc, ws, () => stream.cancel());\n }\n\n static forSink<T>(\n lc: LogContext,\n ws: WebSocket,\n stream: Subscription<T, Streamed<T>>,\n messageHandler: (e: MessageEvent) => void | undefined,\n ) {\n // If the websocket is closed, call end() to allow the downstream Sink\n // to process any pending messages before closing the stream.\n return new WebSocketCloser(lc, ws, () => stream.end(), messageHandler);\n }\n\n private constructor(\n lc: LogContext,\n ws: WebSocket,\n closeStream: () => void,\n messageHandler?: (e: MessageEvent) => void | undefined,\n ) {\n this.#lc = lc;\n this.#ws = ws;\n this.#closeStream = closeStream;\n this.#messageHandler = messageHandler ?? null;\n\n ws.addEventListener('open', this.#handleOpen);\n ws.addEventListener('close', this.#handleClose);\n ws.addEventListener('error', this.#handleError);\n if (this.#messageHandler) {\n ws.addEventListener('message', this.#messageHandler);\n }\n\n switch (ws.readyState) {\n case ws.CONNECTING:\n break; // expected for new connections. resolve or reject in handlers.\n case ws.OPEN:\n this.#connected.resolve();\n break;\n default:\n this.#connected.reject(\n new Error(`websocket already in state ${ws.readyState}`),\n );\n break;\n }\n }\n\n get #conn(): string {\n return 'connection' + (this.#ws.url ? ` to ${this.#ws.url}` : '');\n }\n\n #handleOpen = () => {\n this.#lc.info?.(`${this.#conn} established`);\n this.#connected.resolve();\n };\n\n #handleClose = (e: CloseEvent) => {\n const {code, reason, wasClean} = e;\n this.#lc.info?.(`${this.#conn} closed`, {\n code,\n reason,\n wasClean,\n });\n this.close();\n this.#connected.reject(`${this.#conn} closed with code ${code}`);\n };\n\n #handleError = ({message, error}: ErrorEvent) => {\n if (this.#ws.readyState === this.#ws.OPEN) {\n this.#lc.error?.(`error in ${this.#conn}`, message, error);\n }\n this.#connected.reject(error);\n };\n\n close(err?: unknown) {\n if (err) {\n this.#lc.error?.(`closing stream with error`, err);\n }\n this.#closeStream();\n if (!this.closed()) {\n this.#ws.close();\n }\n }\n\n closed() {\n return (\n this.#ws.readyState === this.#ws.CLOSED ||\n this.#ws.readyState === this.#ws.CLOSING\n );\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"streams.js","names":["#lc","#ws","#closeStream","#messageHandler","#connected","#handleOpen","#handleClose","#handleError","#conn"],"sources":["../../../../../zero-cache/src/types/streams.ts"],"sourcesContent":["import {\n pipeline,\n Readable,\n Transform,\n Writable,\n type DuplexOptions,\n} from 'node:stream';\nimport type {LogContext} from '@rocicorp/logger';\nimport {resolver} from '@rocicorp/resolver';\nimport {\n createWebSocketStream,\n type CloseEvent,\n type ErrorEvent,\n type MessageEvent,\n type WebSocket,\n} from 'ws';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport {BigIntJSON, type JSONValue} from '../../../shared/src/bigint-json.ts';\nimport {Queue} from '../../../shared/src/queue.ts';\nimport * as v from '../../../shared/src/valita.ts';\nimport {Subscription, type Options} from './subscription.ts';\nimport {\n closeWithError,\n expectPingsForLiveness,\n sendPingsForLiveness,\n} from './ws.ts';\n\n// Consistent with Postgres keepalives, and shorter than the\n// commonly used default idle timeout of 1 minute.\nconst PING_INTERVAL_MS = 30_000;\n\nexport type Source<T> = AsyncIterable<T> & {\n /**\n * Immediately terminates all current iterations (i.e. {@link AsyncIterator.next next()})\n * will return `{value: undefined, done: true}`), and prevents any subsequent iterations\n * from yielding any values.\n *\n * @param err Terminate the iteration by throwing the `err` instead.\n */\n cancel: (err?: Error) => void;\n\n /**\n * The presence of a `pipeline` iterable allows the usual \"consumed-on-iterate\" semantics\n * to be overridden.\n *\n * This is suitable for transport layers that serialize messages across processes, such\n * as the {@link streamOut()} method; pipelining allows the transport to send messages\n * as they arrive without waiting for the previous message to be acked, streaming\n * them to the receiving process where they are presumably queued and processed without\n * a per-message ack delay. The receiving end of the transport then responds with acks\n * asynchronously as the receiving end processes the messages.\n */\n pipeline?: AsyncIterable<{value: T; consumed: () => void}> | undefined;\n};\n\nexport type Sink<T> = {\n push(message: T): void;\n};\n\n/**\n * Back-pressure-aware transformation of a WebSocket into\n * upstream and downstream {@link Subscription} objects.\n */\n// TODO: Change {@link streamIn} and {@link streamOut} to use this\n// under the covers so that internal communication is also\n// responsive to backpressure.\nexport function stream<In extends JSONValue, Out extends JSONValue>(\n lc: LogContext,\n ws: WebSocket,\n inSchema: v.Type<In>,\n outOptions: Options<Out> = {},\n inOptions: Options<In> = {},\n streamOptions: DuplexOptions = {},\n): {outstream: Sink<Out>; instream: Source<In>} {\n const endpoint = ws.url ?? 'client';\n function close(err?: unknown) {\n if (ws.readyState !== ws.CLOSED && ws.readyState !== ws.CLOSING) {\n if (err) {\n closeWithError(lc, ws, err);\n } else {\n lc.info?.(`closing connection to ${endpoint}`);\n ws.close();\n }\n }\n }\n\n const instream = Subscription.create<In>({\n ...inOptions,\n cleanup: (unconsumed, err) => {\n inOptions.cleanup?.(unconsumed, err);\n close(err);\n },\n });\n const outstream = Subscription.create<Out>({\n ...outOptions,\n cleanup: (unconsumed, err) => {\n outOptions.cleanup?.(unconsumed, err);\n close(err);\n },\n });\n\n const duplex = createWebSocketStream(ws, {\n ...streamOptions,\n decodeStrings: false,\n });\n\n // Outgoing transform.\n function streamOut() {\n // Mainly used for verifying that back-pressure kicks in tests.\n duplex.on('drain', () => lc.debug?.(`drained messages to ${endpoint}`));\n\n pipeline(\n Readable.from(outstream),\n new Transform({\n objectMode: true,\n transform: (msg, _encoding, callback) =>\n callback(null, BigIntJSON.stringify(msg)),\n }),\n duplex,\n err => (err ? outstream.fail(err) : outstream.cancel()),\n );\n }\n\n if (ws.readyState === ws.CONNECTING) {\n ws.on('open', () => {\n lc.info?.(`connected to ${endpoint}`);\n streamOut();\n });\n } else {\n streamOut();\n }\n\n // Incoming transform.\n pipe({\n source: duplex,\n sink: instream,\n parse: chunk => {\n const json = BigIntJSON.parse(chunk.toString());\n return v.parse(json, inSchema, 'passthrough');\n },\n });\n\n sendPingsForLiveness(lc, ws, PING_INTERVAL_MS);\n\n return {outstream, instream};\n}\n\ntype PipeOptions<T> = {\n source: Readable;\n sink: Subscription<T>;\n parse: (buffer: Buffer) => T | null;\n bufferMessages?: number;\n};\n\nexport function pipe<T>({source, sink, parse, bufferMessages}: PipeOptions<T>) {\n bufferMessages ??= 0;\n assert(bufferMessages >= 0, 'bufferMessages must be non-negative');\n const pending: Promise<unknown>[] = [];\n\n pipeline(\n source,\n new Writable({\n decodeStrings: false,\n write: (chunk, _encoding, callback) => {\n let msg: T | null;\n try {\n if ((msg = parse(chunk)) === null) {\n callback();\n return;\n }\n } catch (err) {\n callback(ensureError(err));\n return;\n }\n // Inbound backpressure is exerted by unconsumed messages in the\n // subscription. A buffer can be used to allow messages to queue up in\n // in the Subscription object, which allows the consumer to \"peek\" at\n // whether there are more messages immediately available\n // (via {@link Subscription.queued}.\n const {result} = sink.push(msg);\n pending.push(result);\n void result.then(() => pending.shift());\n\n if (pending.length <= bufferMessages) {\n // immediately allow more messages\n callback();\n } else {\n // wait for the oldest result in the pending queue\n pending[0].then(\n () => callback(),\n err => callback(ensureError(err)),\n );\n }\n },\n destroy: (err, callback) => {\n if (err) {\n sink.fail(ensureError(err));\n }\n // Otherwise, final will handle the cancel.\n callback();\n },\n final: callback => {\n sink.cancel();\n callback();\n },\n }),\n err => (err ? sink.fail(err) : sink.cancel()),\n );\n}\n\nfunction ensureError(err: unknown) {\n return err instanceof Error ? err : new Error(String(err));\n}\n\nconst ackSchema = v.object({ack: v.number()});\n\ntype Ack = v.Infer<typeof ackSchema>;\n\ntype Streamed<T> = {\n /** Application-level message. */\n msg: T;\n\n /** ID used for the Ack message. */\n id: number;\n};\n\nexport function streamOut<T extends JSONValue>(\n lc: LogContext,\n source: Source<T>,\n sink: WebSocket,\n): Promise<void> {\n return streamOutInternal(lc, source, sink, BigIntJSON.stringify);\n}\n\n/**\n * Streams out a `Source` for which messages are already stringified JSON.\n */\nexport function streamOutStringified(\n lc: LogContext,\n source: Source<string>,\n sink: WebSocket,\n): Promise<void> {\n return streamOutInternal(lc, source, sink, json => json);\n}\n\nasync function streamOutInternal<T extends JSONValue>(\n lc: LogContext,\n source: Source<T>,\n sink: WebSocket,\n stringify: (payload: T) => string,\n): Promise<void> {\n sendPingsForLiveness(lc, sink, PING_INTERVAL_MS);\n\n const closer = WebSocketCloser.forSource(lc, sink, source);\n\n const acks = new Queue<Ack>();\n sink.addEventListener('message', ({data}) => {\n try {\n if (typeof data !== 'string') {\n throw new Error('Expected string message');\n }\n acks.enqueue(v.parse(JSON.parse(data), ackSchema));\n } catch (e) {\n lc.error?.(`error parsing ack`, e);\n closer.close(e);\n }\n });\n\n try {\n let nextID = 0;\n const {pipeline} = source;\n if (pipeline) {\n lc.debug?.(`started pipelined outbound stream`);\n for await (const {value: msg, consumed} of pipeline) {\n const id = ++nextID;\n const data = `{\"id\":${id},\"msg\":${stringify(msg)}}`;\n // Enable for debugging. Otherwise too verbose.\n // lc.debug?.(`pipelining`, data);\n sink.send(data);\n\n void (async () => {\n const {ack} = await acks.dequeue();\n // lc.debug?.(`received ack`, ack);\n if (ack !== id) {\n throw new Error(`Unexpected ack for ${id}: ${ack}`);\n }\n consumed();\n })();\n }\n } else {\n lc.debug?.(`started synchronous outbound stream`);\n for await (const msg of source) {\n const id = ++nextID;\n const data = `{\"id\":${id},\"msg\":${stringify(msg)}}`;\n // Enable for debugging. Otherwise too verbose.\n // lc.debug?.(`sending`, data);\n sink.send(data);\n\n const {ack} = await acks.dequeue();\n if (ack !== id) {\n throw new Error(`Unexpected ack for ${id}: ${ack}`);\n }\n }\n }\n closer.close();\n } catch (e) {\n closer.close(e);\n }\n}\n\nexport async function streamIn<T extends JSONValue>(\n lc: LogContext,\n source: WebSocket,\n schema: v.Type<T>,\n): Promise<Source<T>> {\n expectPingsForLiveness(lc, source, PING_INTERVAL_MS);\n\n const streamedSchema = v.object({\n msg: schema,\n id: v.number(),\n });\n\n const sink: Subscription<T, Streamed<T>> = new Subscription<T, Streamed<T>>(\n {\n consumed: ({id}) => source.send(JSON.stringify({ack: id} satisfies Ack)),\n cleanup: () => closer.close(),\n },\n ({msg}) => msg,\n );\n\n const closer = WebSocketCloser.forSink(lc, source, sink, handleMessage);\n\n function handleMessage(event: MessageEvent) {\n const data = event.data.toString();\n if (!sink.active) {\n lc.warn?.('dropping ws message received after close', data);\n return;\n }\n try {\n const value = BigIntJSON.parse(data);\n const msg = v.parse(value, streamedSchema, 'passthrough');\n // Enable for debugging. Otherwise too verbose.\n // lc.debug?.(`received`, data);\n sink.push(msg);\n } catch (e) {\n closer.close(e);\n }\n }\n\n await closer.connected;\n return sink;\n}\n\nclass WebSocketCloser {\n readonly #lc: LogContext;\n readonly #ws: WebSocket;\n readonly #closeStream: () => void;\n readonly #messageHandler: ((e: MessageEvent) => void | undefined) | null;\n readonly #connected = resolver();\n\n get connected(): Promise<void> {\n return this.#connected.promise;\n }\n\n static forSource<T>(lc: LogContext, ws: WebSocket, stream: Source<T>) {\n // If the websocket is closed, call cancel() to notify the Source of\n // any unconsumed messages.\n return new WebSocketCloser(lc, ws, () => stream.cancel());\n }\n\n static forSink<T>(\n lc: LogContext,\n ws: WebSocket,\n stream: Subscription<T, Streamed<T>>,\n messageHandler: (e: MessageEvent) => void | undefined,\n ) {\n // If the websocket is closed, call end() to allow the downstream Sink\n // to process any pending messages before closing the stream.\n return new WebSocketCloser(lc, ws, () => stream.end(), messageHandler);\n }\n\n private constructor(\n lc: LogContext,\n ws: WebSocket,\n closeStream: () => void,\n messageHandler?: (e: MessageEvent) => void | undefined,\n ) {\n this.#lc = lc;\n this.#ws = ws;\n this.#closeStream = closeStream;\n this.#messageHandler = messageHandler ?? null;\n\n ws.addEventListener('open', this.#handleOpen);\n ws.addEventListener('close', this.#handleClose);\n ws.addEventListener('error', this.#handleError);\n if (this.#messageHandler) {\n ws.addEventListener('message', this.#messageHandler);\n }\n\n switch (ws.readyState) {\n case ws.CONNECTING:\n break; // expected for new connections. resolve or reject in handlers.\n case ws.OPEN:\n this.#connected.resolve();\n break;\n default:\n this.#connected.reject(\n new Error(`websocket already in state ${ws.readyState}`),\n );\n break;\n }\n }\n\n get #conn(): string {\n return 'connection' + (this.#ws.url ? ` to ${this.#ws.url}` : '');\n }\n\n #handleOpen = () => {\n this.#lc.info?.(`${this.#conn} established`);\n this.#connected.resolve();\n };\n\n #handleClose = (e: CloseEvent) => {\n const {code, reason, wasClean} = e;\n this.#lc.info?.(`${this.#conn} closed`, {\n code,\n reason,\n wasClean,\n });\n this.close();\n this.#connected.reject(`${this.#conn} closed with code ${code}`);\n };\n\n #handleError = ({message, error}: ErrorEvent) => {\n if (this.#ws.readyState === this.#ws.OPEN) {\n this.#lc.error?.(`error in ${this.#conn}`, message, error);\n }\n this.#connected.reject(error);\n };\n\n close(err?: unknown) {\n if (err) {\n this.#lc.error?.(`closing stream with error`, err);\n }\n this.#closeStream();\n if (!this.closed()) {\n this.#ws.close();\n }\n }\n\n closed() {\n return (\n this.#ws.readyState === this.#ws.CLOSED ||\n this.#ws.readyState === this.#ws.CLOSING\n );\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"strings.js","names":[],"sources":["../../../../../zero-cache/src/types/strings.ts"],"sourcesContent":["export function elide(val: string, maxBytes: number) {\n const encoder = new TextEncoder();\n if (encoder.encode(val).length <= maxBytes) {\n return val;\n }\n val = val.substring(0, maxBytes - 3);\n while (encoder.encode(val + '...').length > maxBytes) {\n val = val.substring(0, val.length - 1);\n }\n return val + '...';\n}\n"],"mappings":";AAAA,SAAgB,MAAM,KAAa,UAAkB;CACnD,MAAM,UAAU,IAAI,YAAY;CAChC,IAAI,QAAQ,OAAO,GAAG,EAAE,UAAU,UAChC,OAAO;CAET,MAAM,IAAI,UAAU,GAAG,WAAW,CAAC;CACnC,OAAO,QAAQ,OAAO,MAAM,KAAK,EAAE,SAAS,UAC1C,MAAM,IAAI,UAAU,GAAG,IAAI,SAAS,CAAC;CAEvC,OAAO,MAAM;AACf"}
1
+ {"version":3,"file":"strings.js","names":[],"sources":["../../../../../zero-cache/src/types/strings.ts"],"sourcesContent":["export function elide(val: string, maxBytes: number) {\n const encoder = new TextEncoder();\n if (encoder.encode(val).length <= maxBytes) {\n return val;\n }\n val = val.substring(0, maxBytes - 3);\n while (encoder.encode(val + '...').length > maxBytes) {\n val = val.substring(0, val.length - 1);\n }\n return val + '...';\n}\n"],"mappings":";AAAA,SAAgB,MAAM,KAAa,UAAkB;CACnD,MAAM,UAAU,IAAI,aAAa;AACjC,KAAI,QAAQ,OAAO,IAAI,CAAC,UAAU,SAChC,QAAO;AAET,OAAM,IAAI,UAAU,GAAG,WAAW,EAAE;AACpC,QAAO,QAAQ,OAAO,MAAM,MAAM,CAAC,SAAS,SAC1C,OAAM,IAAI,UAAU,GAAG,IAAI,SAAS,EAAE;AAExC,QAAO,MAAM"}
@@ -1 +1 @@
1
- {"version":3,"file":"subscription.js","names":["#consumers","#messages","#consuming","#pipelineEnabled","#coalesce","#consumed","#cleanup","#publish","#sentinel","#terminate","#pipeline"],"sources":["../../../../../zero-cache/src/types/subscription.ts"],"sourcesContent":["import {resolver, type Resolver} from '@rocicorp/resolver';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport type {Sink, Source} from './streams.ts';\n\n/**\n * A Subscription abstracts a continuous, logically infinite stream of messages intended\n * for serial processing. Unlike the more general Node `Stream` API, a Subscription has\n * a limited API with specific semantics:\n *\n * * **Serial processing**: Messages must be consumed via the {@link AsyncIterable}\n * interface, e.g.\n * ```ts\n * const subscription = server.subscribe(parameters);\n *\n * for await (const message of subscription) {\n * await process(message); // fully process the message before consuming the next\n * }\n * ```\n *\n * Moreover, the consumer is expected to completely process each message before\n * requesting the next. This is important for cleanup semantics (explained later).\n *\n * * **cancel()**, not close(): The underlying data in a subscription is logically infinite\n * and only terminated when the consumer is no longer interested in receiving the messages\n * (or requires a Subscription with a different configuration). As such, there is no API\n * for gracefully closing the subscription after pending messages are consumed; rather,\n * cancellation is immediate, and upon cancellation, pending messages are dropped. A\n * Subscription can also be terminated with exceptional (i.e. `Error`) circumstances,\n * for which the behavior is equivalent.\n *\n * * **Coalescing** (optional): A producer can configure pending messages in the Subscription\n * to be merged together with a {@link Options.coalesce coalesce} function. This is useful\n * for semantics in which the consumer is not necessarily interested in every incremental\n * change, but rather the cumulative change since the last processed message. A\n * Subscription with coalescing is guaranteed to have at most one outstanding message,\n * regardless of how quickly messages are produced and consumed. This effectively constrains\n * the amount of outstanding work in the system.\n *\n * ### Resource Tracking and Cleanup\n *\n * Because message consumption is constrained to the async iteration API, standard\n * control flow mechanisms allow the producer to perform bookkeeping without any\n * explicit cleanup actions on the part of the consumer. This includes:\n *\n * * **Per-message cleanup**: Each request for the {@link AsyncIterator.next next}\n * message, or the termination of the iteration, signals that the consumer has\n * finished processing the previous message. The producer of a Subscription can\n * supply a {@link Options.consumed consumed} callback to receive these processed\n * messages, allowing it to clean up attached resources (e.g. TransactionPools, etc.).\n *\n * * **Per-subscription cleanup**: The producer of a Subscription can supply a\n * {@link Options.cleanup cleanup} callback that is invoked when the Subscription\n * is terminated, either explicitly via {@link Subscription.cancel cancel()} /\n * {@link Subscription.fail fail()}, or implicitly when an iteration is exited via a\n * `break`, `return`, or `throw` statement. All unconsumed messages are passed to the\n * call back to facilitate bookkeeping.\n *\n * @param T The external message type, published to the AsyncIterable\n * @param M The internal message type used in the producer-side interfaces\n * (e.g. {@link push}, {@link Options.consumed}, {@link Options.coalesce},\n * and {@link Options.cleanup}). This is often the same as the external type\n * T, but may be diverged to facilitate internal bookkeeping.\n */\nexport class Subscription<T, M = T> implements Source<T>, Sink<M> {\n /**\n * Convenience factory method for creating a {@link Subscription} with internal message type\n * `M` as a subtype of `T`, defaulting to the same type. The default `publish` method publishes\n * the message of type `M` directly to the AsyncIterable.\n */\n static create<T, M extends T = T>(\n options: Options<M> = {},\n publish: (m: M) => T = m => m,\n ) {\n return new Subscription(options, publish);\n }\n\n // Consumers waiting to consume messages (i.e. an async iteration awaiting the next message).\n readonly #consumers: Resolver<Entry<M> | null>[] = [];\n // Messages waiting to be dequeued.\n readonly #messages: (Entry<M> | 'terminus')[] = [];\n // Messages dequeued but not yet consumed.\n readonly #consuming = new Set<Entry<M>>();\n readonly #pipelineEnabled: boolean;\n // Sentinel value signaling that the subscription is \"done\" and no more\n // messages can be added.\n #sentinel: 'canceled' | Error | undefined = undefined;\n\n #coalesce: ((curr: Entry<M>, prev: Entry<M>) => M) | undefined;\n #consumed: (prev: Entry<M>) => void;\n #cleanup: (unconsumed: Entry<M>[], err?: Error) => void;\n #publish: (internal: M) => T;\n\n /**\n * @param publish function for converting the internally pushed / coalesced message\n * of type `M` to the external type `T` exposed via async iteration.\n */\n constructor(options: Options<M> = {}, publish: (m: M) => T) {\n const {\n coalesce,\n consumed = () => {},\n cleanup = () => {},\n pipeline = coalesce === undefined,\n } = options;\n\n this.#coalesce = !coalesce\n ? undefined\n : (curr, prev) => {\n try {\n return coalesce(curr.value, prev.value);\n } finally {\n prev.resolve('coalesced');\n }\n };\n\n this.#consumed = entry => {\n consumed(entry.value);\n this.#consuming.delete(entry);\n entry.resolve('consumed');\n };\n\n this.#cleanup = (entries, err) => {\n cleanup(\n entries.map(e => e.value),\n err,\n );\n entries.forEach(e => e.resolve('unconsumed'));\n };\n\n this.#publish = publish;\n\n this.#pipelineEnabled = pipeline;\n }\n\n /**\n * Pushes the next message to be consumed, and returns a `result` that resolves to the\n * eventual {@link Result} of the `value`.\n *\n * If there is an existing unconsumed message and the Subscription has a\n * {@link Options#coalesce coalesce} function, the specified `value` will be coalesced\n * with the pending message. In this case, the result of the pending message\n * is resolved to `coalesced`, regardless of the `coalesce` function implementation.\n *\n * If the subscription is in a terminal state, the message is dropped and the\n * result resolves to `unconsumed`.\n */\n push(value: M): PendingResult {\n const {promise: result, resolve} = resolver<Result>();\n const entry = {value, resolve};\n\n if (this.#sentinel) {\n entry.resolve('unconsumed');\n return {result};\n }\n const consumer = this.#consumers.shift();\n if (consumer) {\n consumer.resolve(entry);\n } else if (\n this.#coalesce &&\n this.#messages.length &&\n this.#messages.at(-1) !== 'terminus'\n ) {\n // oxlint-disable-next-line typescript/no-non-null-assertion\n const prev = this.#messages.at(-1)!;\n assert(prev !== 'terminus', 'prev should not be terminus after check');\n this.#messages[this.#messages.length - 1] = {\n value: this.#coalesce(entry, prev),\n resolve,\n };\n } else {\n this.#messages.push(entry);\n }\n return {result};\n }\n\n /** False if the subscription has been canceled or has failed. */\n get active(): boolean {\n return this.#sentinel === undefined;\n }\n\n /** The number of messages waiting to be dequeued. */\n get queued(): number {\n return this.#messages.length;\n }\n\n /** The number of messages dequeued but not yet \"consumed\" */\n get consuming(): number {\n return this.#consuming.size;\n }\n\n /**\n * Cancels the subscription after any queued messages are consumed. This is\n * meant for the producer-side code.\n *\n * Any messages pushed after calling `end()` will be unconsumed as if\n * `cancel()` were called (once the first set of pending messages is\n * consumed). In particular, if a coalesce function is defined, the new\n * messages will not be coalesced with the messages enqueued before `end()`\n * was called. However, to effect the intent of memory efficiency, multiple\n * messages pushed after calling `end()` will be coalesced together.\n *\n */\n end() {\n if (this.#sentinel) {\n // already terminated\n } else if (this.#messages.length === 0) {\n this.cancel();\n } else {\n this.#messages.push('terminus');\n }\n }\n\n /**\n * Cancels the subscription immediately, cleans up, and terminates any iteration.\n * This is intended for the consumer to call when it is no longer interested\n * in the subscription.\n *\n * @param err If an `err` is specified, an iteration over the Subscription /\n * Sink will throw the `err` (equivalent to the producer calling\n * {@link fail()}). If undefined, the iteration will exit gracefully.\n */\n cancel(err?: Error) {\n this.#terminate(err ?? 'canceled');\n }\n\n /** Fails the subscription, cleans up, and throws from any iteration. */\n fail(err: Error) {\n this.#terminate(err);\n }\n\n #terminate(sentinel: 'canceled' | Error) {\n if (!this.#sentinel) {\n this.#sentinel = sentinel;\n this.#cleanup(\n [...this.#consuming, ...this.#messages.filter(m => m !== 'terminus')],\n sentinel instanceof Error ? sentinel : undefined,\n );\n this.#messages.splice(0);\n\n for (\n let consumer = this.#consumers.shift();\n consumer;\n consumer = this.#consumers.shift()\n ) {\n sentinel === 'canceled'\n ? consumer.resolve(null)\n : consumer.reject(sentinel);\n }\n }\n }\n\n get pipeline(): AsyncIterable<{value: T; consumed: () => void}> | undefined {\n return this.#pipelineEnabled\n ? {[Symbol.asyncIterator]: () => this.#pipeline()}\n : undefined;\n }\n\n #pipeline(): AsyncIterator<{value: T; consumed: () => void}> {\n return {\n next: async () => {\n const entry = this.#messages.shift();\n if (entry === 'terminus') {\n this.cancel();\n return {value: undefined, done: true};\n }\n if (entry !== undefined) {\n this.#consuming.add(entry);\n return {\n value: {\n value: this.#publish(entry.value),\n consumed: () => this.#consumed(entry),\n },\n };\n }\n if (this.#sentinel === 'canceled') {\n return {value: undefined, done: true};\n }\n if (this.#sentinel) {\n return Promise.reject(this.#sentinel);\n }\n const consumer = resolver<Entry<M> | null>();\n this.#consumers.push(consumer);\n\n // Wait for push() (or termination) to resolve the consumer.\n const result = await consumer.promise;\n if (result !== null) {\n this.#consuming.add(result);\n return {\n value: {\n value: this.#publish(result.value),\n consumed: () => this.#consumed(result),\n },\n };\n }\n return {value: undefined, done: true};\n },\n\n return: value => {\n this.cancel();\n return Promise.resolve({value, done: true});\n },\n };\n }\n\n [Symbol.asyncIterator](): AsyncIterator<T> {\n const delegate = this.#pipeline();\n\n let prevConsumed = () => {};\n return {\n next: async () => {\n prevConsumed();\n\n const entry = await delegate.next();\n if (entry.done) {\n return entry;\n }\n\n const {value, consumed} = entry.value;\n prevConsumed = consumed;\n return {value};\n },\n\n return: value => {\n prevConsumed();\n\n this.cancel();\n return Promise.resolve({value, done: true});\n },\n };\n }\n}\n\nexport type Options<M> = {\n /**\n * Coalesces messages waiting to be consumed. This is useful for \"watermark\" type\n * subscriptions in which the consumer is only interested in the cumulative state\n * change since the last processed message. When a `coalesce` function is specified,\n * there is guaranteed to be at most one message waiting to be consumed.\n *\n * Note that the `curr` argument comes before `prev`. This facilitates a common\n * scenario in which coalescing just means using the newest value; in such a case,\n * `coalesce` can simply be the identity function (e.g. `msg => msg`).\n */\n coalesce?: (curr: M, prev: M) => M;\n\n /**\n * Called on the previous message in an iteration (1) when the next message is requested,\n * or (2) when the iteration is terminated. This allows the producer to perform\n * per-message cleanup.\n *\n * Note that when a {@link Options.coalesce coalesce} function is defined,\n * `consumed` is _not_ called on the `prev` message; it is the responsibility of\n * producers requiring both coalescing and consumption notification to perform any\n * necessary cleanup of `prev` messages when coalescing.\n */\n consumed?: (prev: M) => void;\n\n /**\n * `cleanup` is called exactly once when the subscription is terminated via a failure or\n * cancelation (whichever happens first), which includes implicit cancelation when\n * the consumer exits an iteration via a `break`, `return`, or `throw` statement.\n *\n * Note that the `err` argument will only reflect an explicit cancelation via a call\n * to {@link Subscription.fail()}. On the other hand, if the iteration is canceled via\n * a `throw` statement, the thrown reason is not reflected in the `err` parameter, as that\n * information is not made available to the AsyncIterator implementation.\n */\n cleanup?: (unconsumed: M[], err?: Error) => void;\n\n /**\n * Enable or disable pipelining when streaming messages over a websocket.\n *\n * If unspecified, pipelining is enabled if there is no {@link Options.coalesce coalesce}\n * method, as pipelining is counter to the semantics of coalescing. However, the\n * application can explicitly enable pipelining even if there is a coalesce method\n * by specifying `true` for this option. This assumes that coalescing is either\n * not important for websocket semantics, or that the receiving end of the websocket\n * transport performs the desired coalescing.\n */\n pipeline?: boolean;\n};\n\n/** Post-queueing results of messages. */\nexport type Result = 'consumed' | 'coalesced' | 'unconsumed';\n\n/**\n * {@link Subscription.subscribe()} wraps the `Promise<Result>` in a `PendingResult`\n * object to avoid forcing all callers to handle the Promise, as most logic does not\n * need to.\n */\nexport type PendingResult = {result: Promise<Result>};\n\ntype Entry<M> = {\n readonly value: M;\n readonly resolve: (r: Result) => void;\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"subscription.js","names":["#consumers","#messages","#consuming","#pipelineEnabled","#coalesce","#consumed","#cleanup","#publish","#sentinel","#terminate","#pipeline"],"sources":["../../../../../zero-cache/src/types/subscription.ts"],"sourcesContent":["import {resolver, type Resolver} from '@rocicorp/resolver';\nimport {assert} from '../../../shared/src/asserts.ts';\nimport type {Sink, Source} from './streams.ts';\n\n/**\n * A Subscription abstracts a continuous, logically infinite stream of messages intended\n * for serial processing. Unlike the more general Node `Stream` API, a Subscription has\n * a limited API with specific semantics:\n *\n * * **Serial processing**: Messages must be consumed via the {@link AsyncIterable}\n * interface, e.g.\n * ```ts\n * const subscription = server.subscribe(parameters);\n *\n * for await (const message of subscription) {\n * await process(message); // fully process the message before consuming the next\n * }\n * ```\n *\n * Moreover, the consumer is expected to completely process each message before\n * requesting the next. This is important for cleanup semantics (explained later).\n *\n * * **cancel()**, not close(): The underlying data in a subscription is logically infinite\n * and only terminated when the consumer is no longer interested in receiving the messages\n * (or requires a Subscription with a different configuration). As such, there is no API\n * for gracefully closing the subscription after pending messages are consumed; rather,\n * cancellation is immediate, and upon cancellation, pending messages are dropped. A\n * Subscription can also be terminated with exceptional (i.e. `Error`) circumstances,\n * for which the behavior is equivalent.\n *\n * * **Coalescing** (optional): A producer can configure pending messages in the Subscription\n * to be merged together with a {@link Options.coalesce coalesce} function. This is useful\n * for semantics in which the consumer is not necessarily interested in every incremental\n * change, but rather the cumulative change since the last processed message. A\n * Subscription with coalescing is guaranteed to have at most one outstanding message,\n * regardless of how quickly messages are produced and consumed. This effectively constrains\n * the amount of outstanding work in the system.\n *\n * ### Resource Tracking and Cleanup\n *\n * Because message consumption is constrained to the async iteration API, standard\n * control flow mechanisms allow the producer to perform bookkeeping without any\n * explicit cleanup actions on the part of the consumer. This includes:\n *\n * * **Per-message cleanup**: Each request for the {@link AsyncIterator.next next}\n * message, or the termination of the iteration, signals that the consumer has\n * finished processing the previous message. The producer of a Subscription can\n * supply a {@link Options.consumed consumed} callback to receive these processed\n * messages, allowing it to clean up attached resources (e.g. TransactionPools, etc.).\n *\n * * **Per-subscription cleanup**: The producer of a Subscription can supply a\n * {@link Options.cleanup cleanup} callback that is invoked when the Subscription\n * is terminated, either explicitly via {@link Subscription.cancel cancel()} /\n * {@link Subscription.fail fail()}, or implicitly when an iteration is exited via a\n * `break`, `return`, or `throw` statement. All unconsumed messages are passed to the\n * call back to facilitate bookkeeping.\n *\n * @param T The external message type, published to the AsyncIterable\n * @param M The internal message type used in the producer-side interfaces\n * (e.g. {@link push}, {@link Options.consumed}, {@link Options.coalesce},\n * and {@link Options.cleanup}). This is often the same as the external type\n * T, but may be diverged to facilitate internal bookkeeping.\n */\nexport class Subscription<T, M = T> implements Source<T>, Sink<M> {\n /**\n * Convenience factory method for creating a {@link Subscription} with internal message type\n * `M` as a subtype of `T`, defaulting to the same type. The default `publish` method publishes\n * the message of type `M` directly to the AsyncIterable.\n */\n static create<T, M extends T = T>(\n options: Options<M> = {},\n publish: (m: M) => T = m => m,\n ) {\n return new Subscription(options, publish);\n }\n\n // Consumers waiting to consume messages (i.e. an async iteration awaiting the next message).\n readonly #consumers: Resolver<Entry<M> | null>[] = [];\n // Messages waiting to be dequeued.\n readonly #messages: (Entry<M> | 'terminus')[] = [];\n // Messages dequeued but not yet consumed.\n readonly #consuming = new Set<Entry<M>>();\n readonly #pipelineEnabled: boolean;\n // Sentinel value signaling that the subscription is \"done\" and no more\n // messages can be added.\n #sentinel: 'canceled' | Error | undefined = undefined;\n\n #coalesce: ((curr: Entry<M>, prev: Entry<M>) => M) | undefined;\n #consumed: (prev: Entry<M>) => void;\n #cleanup: (unconsumed: Entry<M>[], err?: Error) => void;\n #publish: (internal: M) => T;\n\n /**\n * @param publish function for converting the internally pushed / coalesced message\n * of type `M` to the external type `T` exposed via async iteration.\n */\n constructor(options: Options<M> = {}, publish: (m: M) => T) {\n const {\n coalesce,\n consumed = () => {},\n cleanup = () => {},\n pipeline = coalesce === undefined,\n } = options;\n\n this.#coalesce = !coalesce\n ? undefined\n : (curr, prev) => {\n try {\n return coalesce(curr.value, prev.value);\n } finally {\n prev.resolve('coalesced');\n }\n };\n\n this.#consumed = entry => {\n consumed(entry.value);\n this.#consuming.delete(entry);\n entry.resolve('consumed');\n };\n\n this.#cleanup = (entries, err) => {\n cleanup(\n entries.map(e => e.value),\n err,\n );\n entries.forEach(e => e.resolve('unconsumed'));\n };\n\n this.#publish = publish;\n\n this.#pipelineEnabled = pipeline;\n }\n\n /**\n * Pushes the next message to be consumed, and returns a `result` that resolves to the\n * eventual {@link Result} of the `value`.\n *\n * If there is an existing unconsumed message and the Subscription has a\n * {@link Options#coalesce coalesce} function, the specified `value` will be coalesced\n * with the pending message. In this case, the result of the pending message\n * is resolved to `coalesced`, regardless of the `coalesce` function implementation.\n *\n * If the subscription is in a terminal state, the message is dropped and the\n * result resolves to `unconsumed`.\n */\n push(value: M): PendingResult {\n const {promise: result, resolve} = resolver<Result>();\n const entry = {value, resolve};\n\n if (this.#sentinel) {\n entry.resolve('unconsumed');\n return {result};\n }\n const consumer = this.#consumers.shift();\n if (consumer) {\n consumer.resolve(entry);\n } else if (\n this.#coalesce &&\n this.#messages.length &&\n this.#messages.at(-1) !== 'terminus'\n ) {\n // oxlint-disable-next-line typescript/no-non-null-assertion\n const prev = this.#messages.at(-1)!;\n assert(prev !== 'terminus', 'prev should not be terminus after check');\n this.#messages[this.#messages.length - 1] = {\n value: this.#coalesce(entry, prev),\n resolve,\n };\n } else {\n this.#messages.push(entry);\n }\n return {result};\n }\n\n /** False if the subscription has been canceled or has failed. */\n get active(): boolean {\n return this.#sentinel === undefined;\n }\n\n /** The number of messages waiting to be dequeued. */\n get queued(): number {\n return this.#messages.length;\n }\n\n /** The number of messages dequeued but not yet \"consumed\" */\n get consuming(): number {\n return this.#consuming.size;\n }\n\n /**\n * Cancels the subscription after any queued messages are consumed. This is\n * meant for the producer-side code.\n *\n * Any messages pushed after calling `end()` will be unconsumed as if\n * `cancel()` were called (once the first set of pending messages is\n * consumed). In particular, if a coalesce function is defined, the new\n * messages will not be coalesced with the messages enqueued before `end()`\n * was called. However, to effect the intent of memory efficiency, multiple\n * messages pushed after calling `end()` will be coalesced together.\n *\n */\n end() {\n if (this.#sentinel) {\n // already terminated\n } else if (this.#messages.length === 0) {\n this.cancel();\n } else {\n this.#messages.push('terminus');\n }\n }\n\n /**\n * Cancels the subscription immediately, cleans up, and terminates any iteration.\n * This is intended for the consumer to call when it is no longer interested\n * in the subscription.\n *\n * @param err If an `err` is specified, an iteration over the Subscription /\n * Sink will throw the `err` (equivalent to the producer calling\n * {@link fail()}). If undefined, the iteration will exit gracefully.\n */\n cancel(err?: Error) {\n this.#terminate(err ?? 'canceled');\n }\n\n /** Fails the subscription, cleans up, and throws from any iteration. */\n fail(err: Error) {\n this.#terminate(err);\n }\n\n #terminate(sentinel: 'canceled' | Error) {\n if (!this.#sentinel) {\n this.#sentinel = sentinel;\n this.#cleanup(\n [...this.#consuming, ...this.#messages.filter(m => m !== 'terminus')],\n sentinel instanceof Error ? sentinel : undefined,\n );\n this.#messages.splice(0);\n\n for (\n let consumer = this.#consumers.shift();\n consumer;\n consumer = this.#consumers.shift()\n ) {\n sentinel === 'canceled'\n ? consumer.resolve(null)\n : consumer.reject(sentinel);\n }\n }\n }\n\n get pipeline(): AsyncIterable<{value: T; consumed: () => void}> | undefined {\n return this.#pipelineEnabled\n ? {[Symbol.asyncIterator]: () => this.#pipeline()}\n : undefined;\n }\n\n #pipeline(): AsyncIterator<{value: T; consumed: () => void}> {\n return {\n next: async () => {\n const entry = this.#messages.shift();\n if (entry === 'terminus') {\n this.cancel();\n return {value: undefined, done: true};\n }\n if (entry !== undefined) {\n this.#consuming.add(entry);\n return {\n value: {\n value: this.#publish(entry.value),\n consumed: () => this.#consumed(entry),\n },\n };\n }\n if (this.#sentinel === 'canceled') {\n return {value: undefined, done: true};\n }\n if (this.#sentinel) {\n return Promise.reject(this.#sentinel);\n }\n const consumer = resolver<Entry<M> | null>();\n this.#consumers.push(consumer);\n\n // Wait for push() (or termination) to resolve the consumer.\n const result = await consumer.promise;\n if (result !== null) {\n this.#consuming.add(result);\n return {\n value: {\n value: this.#publish(result.value),\n consumed: () => this.#consumed(result),\n },\n };\n }\n return {value: undefined, done: true};\n },\n\n return: value => {\n this.cancel();\n return Promise.resolve({value, done: true});\n },\n };\n }\n\n [Symbol.asyncIterator](): AsyncIterator<T> {\n const delegate = this.#pipeline();\n\n let prevConsumed = () => {};\n return {\n next: async () => {\n prevConsumed();\n\n const entry = await delegate.next();\n if (entry.done) {\n return entry;\n }\n\n const {value, consumed} = entry.value;\n prevConsumed = consumed;\n return {value};\n },\n\n return: value => {\n prevConsumed();\n\n this.cancel();\n return Promise.resolve({value, done: true});\n },\n };\n }\n}\n\nexport type Options<M> = {\n /**\n * Coalesces messages waiting to be consumed. This is useful for \"watermark\" type\n * subscriptions in which the consumer is only interested in the cumulative state\n * change since the last processed message. When a `coalesce` function is specified,\n * there is guaranteed to be at most one message waiting to be consumed.\n *\n * Note that the `curr` argument comes before `prev`. This facilitates a common\n * scenario in which coalescing just means using the newest value; in such a case,\n * `coalesce` can simply be the identity function (e.g. `msg => msg`).\n */\n coalesce?: (curr: M, prev: M) => M;\n\n /**\n * Called on the previous message in an iteration (1) when the next message is requested,\n * or (2) when the iteration is terminated. This allows the producer to perform\n * per-message cleanup.\n *\n * Note that when a {@link Options.coalesce coalesce} function is defined,\n * `consumed` is _not_ called on the `prev` message; it is the responsibility of\n * producers requiring both coalescing and consumption notification to perform any\n * necessary cleanup of `prev` messages when coalescing.\n */\n consumed?: (prev: M) => void;\n\n /**\n * `cleanup` is called exactly once when the subscription is terminated via a failure or\n * cancelation (whichever happens first), which includes implicit cancelation when\n * the consumer exits an iteration via a `break`, `return`, or `throw` statement.\n *\n * Note that the `err` argument will only reflect an explicit cancelation via a call\n * to {@link Subscription.fail()}. On the other hand, if the iteration is canceled via\n * a `throw` statement, the thrown reason is not reflected in the `err` parameter, as that\n * information is not made available to the AsyncIterator implementation.\n */\n cleanup?: (unconsumed: M[], err?: Error) => void;\n\n /**\n * Enable or disable pipelining when streaming messages over a websocket.\n *\n * If unspecified, pipelining is enabled if there is no {@link Options.coalesce coalesce}\n * method, as pipelining is counter to the semantics of coalescing. However, the\n * application can explicitly enable pipelining even if there is a coalesce method\n * by specifying `true` for this option. This assumes that coalescing is either\n * not important for websocket semantics, or that the receiving end of the websocket\n * transport performs the desired coalescing.\n */\n pipeline?: boolean;\n};\n\n/** Post-queueing results of messages. */\nexport type Result = 'consumed' | 'coalesced' | 'unconsumed';\n\n/**\n * {@link Subscription.subscribe()} wraps the `Promise<Result>` in a `PendingResult`\n * object to avoid forcing all callers to handle the Promise, as most logic does not\n * need to.\n */\nexport type PendingResult = {result: Promise<Result>};\n\ntype Entry<M> = {\n readonly value: M;\n readonly resolve: (r: Result) => void;\n};\n"],"mappings":"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"}