@rocicorp/zero 1.2.0 → 1.3.0-canary.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (303) hide show
  1. package/out/analyze-query/src/bin-analyze.js +25 -25
  2. package/out/analyze-query/src/bin-analyze.js.map +1 -1
  3. package/out/ast-to-zql/src/ast-to-zql.d.ts.map +1 -1
  4. package/out/ast-to-zql/src/ast-to-zql.js +2 -1
  5. package/out/ast-to-zql/src/ast-to-zql.js.map +1 -1
  6. package/out/replicache/src/btree/node.d.ts.map +1 -1
  7. package/out/replicache/src/btree/node.js +2 -2
  8. package/out/replicache/src/btree/node.js.map +1 -1
  9. package/out/replicache/src/connection-loop.js +3 -3
  10. package/out/replicache/src/connection-loop.js.map +1 -1
  11. package/out/replicache/src/deleted-clients.d.ts +0 -4
  12. package/out/replicache/src/deleted-clients.d.ts.map +1 -1
  13. package/out/replicache/src/deleted-clients.js +1 -1
  14. package/out/replicache/src/deleted-clients.js.map +1 -1
  15. package/out/replicache/src/hash.d.ts.map +1 -1
  16. package/out/replicache/src/hash.js.map +1 -1
  17. package/out/replicache/src/process-scheduler.d.ts.map +1 -1
  18. package/out/replicache/src/process-scheduler.js.map +1 -1
  19. package/out/replicache/src/request-idle.js +1 -1
  20. package/out/replicache/src/request-idle.js.map +1 -1
  21. package/out/replicache/src/sync/patch.d.ts +1 -1
  22. package/out/replicache/src/sync/patch.d.ts.map +1 -1
  23. package/out/replicache/src/sync/patch.js +1 -1
  24. package/out/replicache/src/sync/patch.js.map +1 -1
  25. package/out/shared/src/arrays.d.ts.map +1 -1
  26. package/out/shared/src/arrays.js +1 -2
  27. package/out/shared/src/arrays.js.map +1 -1
  28. package/out/shared/src/bigint-json.js +1 -1
  29. package/out/shared/src/bigint-json.js.map +1 -1
  30. package/out/shared/src/btree-set.js +1 -1
  31. package/out/shared/src/btree-set.js.map +1 -1
  32. package/out/shared/src/iterables.d.ts +7 -0
  33. package/out/shared/src/iterables.d.ts.map +1 -1
  34. package/out/shared/src/iterables.js +10 -1
  35. package/out/shared/src/iterables.js.map +1 -1
  36. package/out/shared/src/logging.d.ts.map +1 -1
  37. package/out/shared/src/logging.js +10 -9
  38. package/out/shared/src/logging.js.map +1 -1
  39. package/out/shared/src/options.js +1 -1
  40. package/out/shared/src/options.js.map +1 -1
  41. package/out/shared/src/sorted-entries.d.ts +2 -0
  42. package/out/shared/src/sorted-entries.d.ts.map +1 -0
  43. package/out/shared/src/sorted-entries.js +9 -0
  44. package/out/shared/src/sorted-entries.js.map +1 -0
  45. package/out/shared/src/tdigest-schema.d.ts.map +1 -1
  46. package/out/shared/src/tdigest-schema.js.map +1 -1
  47. package/out/shared/src/tdigest.d.ts.map +1 -1
  48. package/out/shared/src/tdigest.js +7 -7
  49. package/out/shared/src/tdigest.js.map +1 -1
  50. package/out/shared/src/valita.d.ts.map +1 -1
  51. package/out/shared/src/valita.js +1 -1
  52. package/out/shared/src/valita.js.map +1 -1
  53. package/out/z2s/src/sql.d.ts +2 -2
  54. package/out/z2s/src/sql.d.ts.map +1 -1
  55. package/out/z2s/src/sql.js +3 -3
  56. package/out/z2s/src/sql.js.map +1 -1
  57. package/out/zero/package.js +6 -7
  58. package/out/zero/package.js.map +1 -1
  59. package/out/zero/src/pg.js +1 -1
  60. package/out/zero/src/server.js +1 -1
  61. package/out/zero-cache/src/auth/auth.d.ts +8 -26
  62. package/out/zero-cache/src/auth/auth.d.ts.map +1 -1
  63. package/out/zero-cache/src/auth/auth.js +57 -82
  64. package/out/zero-cache/src/auth/auth.js.map +1 -1
  65. package/out/zero-cache/src/auth/jwt.d.ts +3 -3
  66. package/out/zero-cache/src/auth/jwt.d.ts.map +1 -1
  67. package/out/zero-cache/src/auth/jwt.js.map +1 -1
  68. package/out/zero-cache/src/auth/load-permissions.js +1 -1
  69. package/out/zero-cache/src/auth/load-permissions.js.map +1 -1
  70. package/out/zero-cache/src/config/zero-config.d.ts +38 -2
  71. package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
  72. package/out/zero-cache/src/config/zero-config.js +56 -1
  73. package/out/zero-cache/src/config/zero-config.js.map +1 -1
  74. package/out/zero-cache/src/custom/fetch.d.ts +2 -9
  75. package/out/zero-cache/src/custom/fetch.d.ts.map +1 -1
  76. package/out/zero-cache/src/custom/fetch.js +10 -4
  77. package/out/zero-cache/src/custom/fetch.js.map +1 -1
  78. package/out/zero-cache/src/custom-queries/transform-query.d.ts +20 -9
  79. package/out/zero-cache/src/custom-queries/transform-query.d.ts.map +1 -1
  80. package/out/zero-cache/src/custom-queries/transform-query.js +74 -37
  81. package/out/zero-cache/src/custom-queries/transform-query.js.map +1 -1
  82. package/out/zero-cache/src/db/migration-lite.d.ts.map +1 -1
  83. package/out/zero-cache/src/db/migration-lite.js +1 -1
  84. package/out/zero-cache/src/db/migration-lite.js.map +1 -1
  85. package/out/zero-cache/src/db/migration.d.ts.map +1 -1
  86. package/out/zero-cache/src/db/migration.js +1 -1
  87. package/out/zero-cache/src/db/migration.js.map +1 -1
  88. package/out/zero-cache/src/db/pg-copy-binary.d.ts +101 -0
  89. package/out/zero-cache/src/db/pg-copy-binary.d.ts.map +1 -0
  90. package/out/zero-cache/src/db/pg-copy-binary.js +381 -0
  91. package/out/zero-cache/src/db/pg-copy-binary.js.map +1 -0
  92. package/out/zero-cache/src/db/transaction-pool.d.ts.map +1 -1
  93. package/out/zero-cache/src/db/transaction-pool.js +3 -0
  94. package/out/zero-cache/src/db/transaction-pool.js.map +1 -1
  95. package/out/zero-cache/src/db/warmup.d.ts.map +1 -1
  96. package/out/zero-cache/src/db/warmup.js +3 -1
  97. package/out/zero-cache/src/db/warmup.js.map +1 -1
  98. package/out/zero-cache/src/server/anonymous-otel-start.d.ts.map +1 -1
  99. package/out/zero-cache/src/server/anonymous-otel-start.js +2 -1
  100. package/out/zero-cache/src/server/anonymous-otel-start.js.map +1 -1
  101. package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
  102. package/out/zero-cache/src/server/change-streamer.js +5 -2
  103. package/out/zero-cache/src/server/change-streamer.js.map +1 -1
  104. package/out/zero-cache/src/server/inspector-delegate.d.ts +2 -2
  105. package/out/zero-cache/src/server/inspector-delegate.d.ts.map +1 -1
  106. package/out/zero-cache/src/server/inspector-delegate.js +4 -4
  107. package/out/zero-cache/src/server/inspector-delegate.js.map +1 -1
  108. package/out/zero-cache/src/server/main.js +1 -1
  109. package/out/zero-cache/src/server/main.js.map +1 -1
  110. package/out/zero-cache/src/server/reaper.d.ts.map +1 -1
  111. package/out/zero-cache/src/server/reaper.js +4 -1
  112. package/out/zero-cache/src/server/reaper.js.map +1 -1
  113. package/out/zero-cache/src/server/runner/run-worker.js +1 -1
  114. package/out/zero-cache/src/server/syncer.d.ts.map +1 -1
  115. package/out/zero-cache/src/server/syncer.js +41 -20
  116. package/out/zero-cache/src/server/syncer.js.map +1 -1
  117. package/out/zero-cache/src/server/worker-urls.d.ts.map +1 -1
  118. package/out/zero-cache/src/server/worker-urls.js +2 -1
  119. package/out/zero-cache/src/server/worker-urls.js.map +1 -1
  120. package/out/zero-cache/src/services/change-source/change-source.d.ts +4 -0
  121. package/out/zero-cache/src/services/change-source/change-source.d.ts.map +1 -1
  122. package/out/zero-cache/src/services/change-source/common/backfill-manager.d.ts.map +1 -1
  123. package/out/zero-cache/src/services/change-source/common/backfill-manager.js +3 -2
  124. package/out/zero-cache/src/services/change-source/common/backfill-manager.js.map +1 -1
  125. package/out/zero-cache/src/services/change-source/custom/change-source.d.ts.map +1 -1
  126. package/out/zero-cache/src/services/change-source/custom/change-source.js +5 -2
  127. package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
  128. package/out/zero-cache/src/services/change-source/pg/change-source.d.ts.map +1 -1
  129. package/out/zero-cache/src/services/change-source/pg/change-source.js +13 -4
  130. package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
  131. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts +3 -1
  132. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts.map +1 -1
  133. package/out/zero-cache/src/services/change-source/pg/initial-sync.js +91 -9
  134. package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
  135. package/out/zero-cache/src/services/change-source/pg/schema/shard.js +2 -2
  136. package/out/zero-cache/src/services/change-source/pg/schema/shard.js.map +1 -1
  137. package/out/zero-cache/src/services/change-streamer/broadcast.js +1 -1
  138. package/out/zero-cache/src/services/change-streamer/broadcast.js.map +1 -1
  139. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +3 -0
  140. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
  141. package/out/zero-cache/src/services/life-cycle.d.ts +5 -4
  142. package/out/zero-cache/src/services/life-cycle.d.ts.map +1 -1
  143. package/out/zero-cache/src/services/life-cycle.js +11 -11
  144. package/out/zero-cache/src/services/life-cycle.js.map +1 -1
  145. package/out/zero-cache/src/services/litestream/commands.d.ts.map +1 -1
  146. package/out/zero-cache/src/services/litestream/commands.js +5 -5
  147. package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
  148. package/out/zero-cache/src/services/mutagen/pusher.d.ts +20 -20
  149. package/out/zero-cache/src/services/mutagen/pusher.d.ts.map +1 -1
  150. package/out/zero-cache/src/services/mutagen/pusher.js +91 -104
  151. package/out/zero-cache/src/services/mutagen/pusher.js.map +1 -1
  152. package/out/zero-cache/src/services/replicator/change-processor.js +1 -1
  153. package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
  154. package/out/zero-cache/src/services/replicator/replication-status.js.map +1 -1
  155. package/out/zero-cache/src/services/view-syncer/client-schema.d.ts.map +1 -1
  156. package/out/zero-cache/src/services/view-syncer/client-schema.js +4 -3
  157. package/out/zero-cache/src/services/view-syncer/client-schema.js.map +1 -1
  158. package/out/zero-cache/src/services/view-syncer/connection-context-manager.d.ts +168 -0
  159. package/out/zero-cache/src/services/view-syncer/connection-context-manager.d.ts.map +1 -0
  160. package/out/zero-cache/src/services/view-syncer/connection-context-manager.js +385 -0
  161. package/out/zero-cache/src/services/view-syncer/connection-context-manager.js.map +1 -0
  162. package/out/zero-cache/src/services/view-syncer/cvr-store.js +2 -2
  163. package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
  164. package/out/zero-cache/src/services/view-syncer/cvr.d.ts.map +1 -1
  165. package/out/zero-cache/src/services/view-syncer/cvr.js +5 -4
  166. package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
  167. package/out/zero-cache/src/services/view-syncer/inspect-handler.d.ts +2 -3
  168. package/out/zero-cache/src/services/view-syncer/inspect-handler.d.ts.map +1 -1
  169. package/out/zero-cache/src/services/view-syncer/inspect-handler.js +3 -3
  170. package/out/zero-cache/src/services/view-syncer/inspect-handler.js.map +1 -1
  171. package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts.map +1 -1
  172. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +5 -3
  173. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
  174. package/out/zero-cache/src/services/view-syncer/row-record-cache.d.ts.map +1 -1
  175. package/out/zero-cache/src/services/view-syncer/row-record-cache.js +13 -7
  176. package/out/zero-cache/src/services/view-syncer/row-record-cache.js.map +1 -1
  177. package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts +3 -1
  178. package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts.map +1 -1
  179. package/out/zero-cache/src/services/view-syncer/snapshotter.js +6 -9
  180. package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
  181. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts +24 -26
  182. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
  183. package/out/zero-cache/src/services/view-syncer/view-syncer.js +236 -124
  184. package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
  185. package/out/zero-cache/src/types/lite.d.ts.map +1 -1
  186. package/out/zero-cache/src/types/lite.js +3 -2
  187. package/out/zero-cache/src/types/lite.js.map +1 -1
  188. package/out/zero-cache/src/types/pg-types.js +4 -1
  189. package/out/zero-cache/src/types/pg-types.js.map +1 -1
  190. package/out/zero-cache/src/types/pg-versions.d.ts +3 -0
  191. package/out/zero-cache/src/types/pg-versions.d.ts.map +1 -0
  192. package/out/zero-cache/src/types/pg-versions.js +7 -0
  193. package/out/zero-cache/src/types/pg-versions.js.map +1 -0
  194. package/out/zero-cache/src/types/pg.d.ts.map +1 -1
  195. package/out/zero-cache/src/types/pg.js +6 -1
  196. package/out/zero-cache/src/types/pg.js.map +1 -1
  197. package/out/zero-cache/src/types/subscription.d.ts.map +1 -1
  198. package/out/zero-cache/src/types/subscription.js +2 -2
  199. package/out/zero-cache/src/types/subscription.js.map +1 -1
  200. package/out/zero-cache/src/workers/connect-params.d.ts +1 -1
  201. package/out/zero-cache/src/workers/connect-params.d.ts.map +1 -1
  202. package/out/zero-cache/src/workers/connect-params.js +1 -1
  203. package/out/zero-cache/src/workers/connect-params.js.map +1 -1
  204. package/out/zero-cache/src/workers/connection.js +2 -2
  205. package/out/zero-cache/src/workers/syncer-ws-message-handler.d.ts +2 -1
  206. package/out/zero-cache/src/workers/syncer-ws-message-handler.d.ts.map +1 -1
  207. package/out/zero-cache/src/workers/syncer-ws-message-handler.js +64 -38
  208. package/out/zero-cache/src/workers/syncer-ws-message-handler.js.map +1 -1
  209. package/out/zero-cache/src/workers/syncer.d.ts +2 -1
  210. package/out/zero-cache/src/workers/syncer.d.ts.map +1 -1
  211. package/out/zero-cache/src/workers/syncer.js +70 -31
  212. package/out/zero-cache/src/workers/syncer.js.map +1 -1
  213. package/out/zero-client/src/client/connection.d.ts +4 -4
  214. package/out/zero-client/src/client/connection.d.ts.map +1 -1
  215. package/out/zero-client/src/client/connection.js.map +1 -1
  216. package/out/zero-client/src/client/http-string.d.ts.map +1 -1
  217. package/out/zero-client/src/client/http-string.js.map +1 -1
  218. package/out/zero-client/src/client/metrics.d.ts.map +1 -1
  219. package/out/zero-client/src/client/metrics.js +2 -1
  220. package/out/zero-client/src/client/metrics.js.map +1 -1
  221. package/out/zero-client/src/client/options.d.ts +30 -5
  222. package/out/zero-client/src/client/options.d.ts.map +1 -1
  223. package/out/zero-client/src/client/options.js.map +1 -1
  224. package/out/zero-client/src/client/server-option.js +1 -1
  225. package/out/zero-client/src/client/server-option.js.map +1 -1
  226. package/out/zero-client/src/client/version.js +1 -1
  227. package/out/zero-client/src/client/zero-poke-handler.d.ts.map +1 -1
  228. package/out/zero-client/src/client/zero-poke-handler.js +1 -1
  229. package/out/zero-client/src/client/zero-poke-handler.js.map +1 -1
  230. package/out/zero-client/src/client/zero.d.ts +4 -3
  231. package/out/zero-client/src/client/zero.d.ts.map +1 -1
  232. package/out/zero-client/src/client/zero.js +33 -11
  233. package/out/zero-client/src/client/zero.js.map +1 -1
  234. package/out/zero-pg/src/mod.js +1 -1
  235. package/out/zero-protocol/src/ast.d.ts.map +1 -1
  236. package/out/zero-protocol/src/ast.js.map +1 -1
  237. package/out/zero-protocol/src/change-desired-queries.d.ts +4 -0
  238. package/out/zero-protocol/src/change-desired-queries.d.ts.map +1 -1
  239. package/out/zero-protocol/src/change-desired-queries.js +4 -1
  240. package/out/zero-protocol/src/change-desired-queries.js.map +1 -1
  241. package/out/zero-protocol/src/connect.d.ts +4 -0
  242. package/out/zero-protocol/src/connect.d.ts.map +1 -1
  243. package/out/zero-protocol/src/connect.js +2 -1
  244. package/out/zero-protocol/src/connect.js.map +1 -1
  245. package/out/zero-protocol/src/primary-key.d.ts.map +1 -1
  246. package/out/zero-protocol/src/primary-key.js.map +1 -1
  247. package/out/zero-protocol/src/protocol-version.d.ts +1 -1
  248. package/out/zero-protocol/src/protocol-version.d.ts.map +1 -1
  249. package/out/zero-protocol/src/protocol-version.js.map +1 -1
  250. package/out/zero-protocol/src/push.d.ts +4 -0
  251. package/out/zero-protocol/src/push.d.ts.map +1 -1
  252. package/out/zero-protocol/src/push.js +2 -1
  253. package/out/zero-protocol/src/push.js.map +1 -1
  254. package/out/zero-protocol/src/up.d.ts +3 -0
  255. package/out/zero-protocol/src/up.d.ts.map +1 -1
  256. package/out/zero-react/src/zero-provider.d.ts.map +1 -1
  257. package/out/zero-react/src/zero-provider.js +11 -5
  258. package/out/zero-react/src/zero-provider.js.map +1 -1
  259. package/out/zero-schema/src/name-mapper.js +1 -1
  260. package/out/zero-schema/src/name-mapper.js.map +1 -1
  261. package/out/zero-server/src/mod.js +1 -1
  262. package/out/zero-server/src/process-mutations.d.ts.map +1 -1
  263. package/out/zero-server/src/process-mutations.js +2 -1
  264. package/out/zero-server/src/process-mutations.js.map +1 -1
  265. package/out/zero-server/src/push-processor.d.ts +1 -0
  266. package/out/zero-server/src/push-processor.d.ts.map +1 -1
  267. package/out/zero-server/src/push-processor.js +3 -2
  268. package/out/zero-server/src/push-processor.js.map +1 -1
  269. package/out/zero-solid/src/use-zero.d.ts.map +1 -1
  270. package/out/zero-solid/src/use-zero.js +8 -9
  271. package/out/zero-solid/src/use-zero.js.map +1 -1
  272. package/out/zql/src/builder/like.js +2 -1
  273. package/out/zql/src/builder/like.js.map +1 -1
  274. package/out/zql/src/ivm/data.d.ts.map +1 -1
  275. package/out/zql/src/ivm/data.js +6 -15
  276. package/out/zql/src/ivm/data.js.map +1 -1
  277. package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
  278. package/out/zql/src/ivm/memory-source.js +14 -8
  279. package/out/zql/src/ivm/memory-source.js.map +1 -1
  280. package/out/zql/src/query/complete-ordering.js +1 -1
  281. package/out/zql/src/query/complete-ordering.js.map +1 -1
  282. package/out/zql/src/query/query-impl.d.ts.map +1 -1
  283. package/out/zql/src/query/query-impl.js +2 -2
  284. package/out/zql/src/query/query-impl.js.map +1 -1
  285. package/out/zql/src/query/query-registry.d.ts.map +1 -1
  286. package/out/zql/src/query/query-registry.js +2 -1
  287. package/out/zql/src/query/query-registry.js.map +1 -1
  288. package/out/zql/src/query/ttl.js +1 -1
  289. package/out/zql/src/query/ttl.js.map +1 -1
  290. package/out/zqlite/src/internal/sql.d.ts +2 -2
  291. package/out/zqlite/src/internal/sql.d.ts.map +1 -1
  292. package/out/zqlite/src/internal/sql.js +1 -2
  293. package/out/zqlite/src/internal/sql.js.map +1 -1
  294. package/out/zqlite/src/sqlite-cost-model.d.ts +1 -1
  295. package/out/zqlite/src/sqlite-cost-model.d.ts.map +1 -1
  296. package/out/zqlite/src/sqlite-cost-model.js +1 -1
  297. package/out/zqlite/src/sqlite-cost-model.js.map +1 -1
  298. package/out/zqlite/src/sqlite-stat-fanout.js +1 -1
  299. package/out/zqlite/src/sqlite-stat-fanout.js.map +1 -1
  300. package/out/zqlite/src/table-source.d.ts.map +1 -1
  301. package/out/zqlite/src/table-source.js +8 -12
  302. package/out/zqlite/src/table-source.js.map +1 -1
  303. package/package.json +6 -7
@@ -0,0 +1,385 @@
1
+ import { InvalidConnectionRequest, Unauthorized } from "../../../../zero-protocol/src/error-kind-enum.js";
2
+ import { ZeroCache } from "../../../../zero-protocol/src/error-origin-enum.js";
3
+ import { ProtocolErrorWithLevel } from "../../types/error-with-level.js";
4
+ import { compileUrlPattern } from "../../custom/fetch.js";
5
+ import { authEquals, resolveAuth } from "../../auth/auth.js";
6
+ //#region ../zero-cache/src/services/view-syncer/connection-context-manager.ts
7
+ /**
8
+ * State machine for the auth state of a single `ViewSyncerService`.
9
+ *
10
+ * Connections are registered as `provisional`, optionally backfilled with
11
+ * `initConnection` metadata, and then promoted to `validated` once their
12
+ * stored `userID` is confirmed as valid. The manager also tracks which
13
+ * validated connection currently serves as the group's background connection.
14
+ *
15
+ * This is intentionally side-effect free.
16
+ */
17
+ var ConnectionContextManagerImpl = class {
18
+ #lc;
19
+ #connections = /* @__PURE__ */ new Map();
20
+ #group = {
21
+ userID: void 0,
22
+ backgroundConnection: void 0,
23
+ retransformAt: void 0,
24
+ maintenanceNotBeforeAt: void 0,
25
+ validated: false
26
+ };
27
+ #validateLegacyJWT;
28
+ #now;
29
+ #revalidateIntervalMs;
30
+ #retransformIntervalMs;
31
+ #queryConfig;
32
+ #pushConfig;
33
+ #nextInsertionOrder = 0;
34
+ constructor(lc, revalidateIntervalSeconds, retransformIntervalSeconds, queryConfig, pushConfig, validateLegacyJWT, now) {
35
+ this.#lc = lc;
36
+ this.#now = now ?? Date.now;
37
+ this.#revalidateIntervalMs = revalidateIntervalSeconds === void 0 ? void 0 : revalidateIntervalSeconds * 1e3;
38
+ this.#retransformIntervalMs = retransformIntervalSeconds === void 0 ? void 0 : retransformIntervalSeconds * 1e3;
39
+ this.#queryConfig = queryConfig;
40
+ this.#pushConfig = pushConfig;
41
+ this.#validateLegacyJWT = validateLegacyJWT;
42
+ }
43
+ /**
44
+ * Creates or replaces the live record for a websocket connection.
45
+ *
46
+ * Re-registering the same `clientID` drops the old socket record and starts
47
+ * the replacement back in `provisional` state.
48
+ */
49
+ registerConnection(selector, connectParams, auth) {
50
+ this.#removeConnection(selector);
51
+ const sharedHeaders = {
52
+ customHeaders: void 0,
53
+ token: auth?.raw,
54
+ origin: connectParams.origin,
55
+ userID: connectParams.userID
56
+ };
57
+ const connection = {
58
+ state: "provisional",
59
+ clientID: connectParams.clientID,
60
+ wsID: connectParams.wsID,
61
+ revision: 0,
62
+ userID: connectParams.userID,
63
+ auth,
64
+ profileID: connectParams.profileID,
65
+ baseCookie: connectParams.baseCookie,
66
+ protocolVersion: connectParams.protocolVersion,
67
+ revalidateAt: void 0,
68
+ queryContext: {
69
+ url: this.#queryConfig?.url?.[0],
70
+ allowedUrlPatterns: this.#queryConfig?.url?.map(compileUrlPattern),
71
+ headerOptions: {
72
+ ...sharedHeaders,
73
+ apiKey: this.#queryConfig?.apiKey,
74
+ allowedClientHeaders: this.#queryConfig?.allowedClientHeaders,
75
+ cookie: this.#queryConfig?.forwardCookies ? connectParams.httpCookie : void 0
76
+ }
77
+ },
78
+ pushContext: {
79
+ url: this.#pushConfig?.url?.[0],
80
+ allowedUrlPatterns: this.#pushConfig?.url?.map(compileUrlPattern),
81
+ headerOptions: {
82
+ ...sharedHeaders,
83
+ apiKey: this.#pushConfig?.apiKey,
84
+ allowedClientHeaders: this.#pushConfig?.allowedClientHeaders,
85
+ cookie: this.#pushConfig?.forwardCookies ? connectParams.httpCookie : void 0
86
+ }
87
+ },
88
+ insertionOrder: ++this.#nextInsertionOrder
89
+ };
90
+ this.#connections.set(connection.clientID, connection);
91
+ this.#refreshBackgroundConnectionContext();
92
+ this.#updateBackgroundRetransformDeadline(false);
93
+ return snapshotConnection(connection);
94
+ }
95
+ /**
96
+ * Backfills `initConnection` data for sockets that were registered before the
97
+ * client could send its full init payload.
98
+ *
99
+ * This updates metadata only; it does not validate the connection.
100
+ */
101
+ initConnection(selector, body) {
102
+ const connection = this.#mustGetConnectionContext(selector);
103
+ if (body.userQueryURL) connection.queryContext.url = body.userQueryURL;
104
+ if (body.userQueryHeaders) connection.queryContext.headerOptions.customHeaders = body.userQueryHeaders;
105
+ if (body.userPushURL) connection.pushContext.url = body.userPushURL;
106
+ if (body.userPushHeaders) connection.pushContext.headerOptions.customHeaders = body.userPushHeaders;
107
+ connection.revision++;
108
+ this.#demoteConnection(connection);
109
+ return snapshotConnection(connection);
110
+ }
111
+ /**
112
+ * A material auth change demotes the connection back to provisional until it
113
+ * is validated again.
114
+ */
115
+ async updateAuth(selector, body) {
116
+ const connection = this.#mustGetConnectionContext(selector);
117
+ const nextAuth = await resolveAuth(this.#lc, connection.auth, connection.userID, body.auth, this.#validateLegacyJWT);
118
+ const authChanged = !authEquals(connection.auth, nextAuth);
119
+ connection.auth = nextAuth;
120
+ if (authChanged) {
121
+ connection.revision++;
122
+ this.#demoteConnection(connection);
123
+ }
124
+ return snapshotConnection(connection);
125
+ }
126
+ /**
127
+ * Validates one connection against the group's pinned `userID`.
128
+ *
129
+ * The first successful validation binds the group `userID`. Later
130
+ * validations must match it. Validation also refreshes the connection's
131
+ * revalidation deadline and may pick the connection as the group
132
+ * background connection if none is currently available. If the websocket is
133
+ * gone by the time async validation finishes, this becomes a no-op.
134
+ */
135
+ validateConnection(selector, revision) {
136
+ const connection = this.#getConnectionContext(selector);
137
+ if (!connection) return;
138
+ if (connection.revision !== revision) {
139
+ this.#lc.debug?.("Skipping validateConnection for stale revision", {
140
+ clientID: selector.clientID,
141
+ attemptedRevision: revision,
142
+ currentRevision: connection.revision
143
+ });
144
+ return;
145
+ }
146
+ if (this.#group.validated && this.#group.userID !== connection.userID) throw new ProtocolErrorWithLevel({
147
+ kind: Unauthorized,
148
+ message: "Client groups are pinned to a single userID. Connection userID does not match existing client group userID.",
149
+ origin: ZeroCache
150
+ }, "warn");
151
+ if (!this.#group.validated) {
152
+ this.#group.validated = true;
153
+ this.#group.userID = connection.userID;
154
+ }
155
+ connection.state = "validated";
156
+ connection.revalidateAt = this.#nextRevalidateAt();
157
+ this.#refreshBackgroundConnectionContext(connection);
158
+ this.#updateBackgroundRetransformDeadline(false);
159
+ return {
160
+ connection: snapshotConnection(connection),
161
+ group: this.getGroupState()
162
+ };
163
+ }
164
+ /** Removes one connection due to failed auth and updates all derived background/deadline state. */
165
+ failConnection(selector, revision) {
166
+ return this.#removeConnection(selector, revision);
167
+ }
168
+ /** Removes one disconnected connection and updates all derived background/deadline state. */
169
+ closeConnection(selector) {
170
+ return this.#removeConnection(selector);
171
+ }
172
+ /**
173
+ * Records a successful background retransform. This starts a fresh interval
174
+ * from the manager clock when shared retransform is schedulable, or
175
+ * clears the deadline if it is not.
176
+ */
177
+ markBackgroundRetransformSuccess(selector, revision) {
178
+ const backgroundConnection = this.#getBackgroundConnectionContext();
179
+ if (!backgroundConnection) return;
180
+ if (selector !== void 0 && (backgroundConnection.clientID !== selector.clientID || backgroundConnection.wsID !== selector.wsID || backgroundConnection.revision !== revision)) return;
181
+ this.#updateBackgroundRetransformDeadline(true);
182
+ }
183
+ deferMaintenance(kind) {
184
+ const intervalMs = kind === "revalidate" ? this.#revalidateIntervalMs : this.#retransformIntervalMs;
185
+ if (intervalMs === void 0) return;
186
+ this.#group.maintenanceNotBeforeAt = Math.max(this.#group.maintenanceNotBeforeAt ?? 0, this.#now() + intervalMs);
187
+ }
188
+ /** Returns the current live record for a client slot, if any. */
189
+ getConnectionContext(selector) {
190
+ return snapshotConnection(this.#getConnectionContext(selector));
191
+ }
192
+ /** Returns the live record for one websocket or throws if it is unavailable. */
193
+ mustGetConnectionContext(selector) {
194
+ return snapshotConnection(this.#mustGetConnectionContext(selector));
195
+ }
196
+ /** Returns the current background connection, if one exists. */
197
+ getBackgroundConnectionContext() {
198
+ return snapshotConnection(this.#getBackgroundConnectionContext());
199
+ }
200
+ mustGetBackgroundConnectionContext() {
201
+ const backgroundConnection = this.#getBackgroundConnectionContext();
202
+ if (!backgroundConnection) throw new ProtocolErrorWithLevel({
203
+ kind: InvalidConnectionRequest,
204
+ message: "No validated connection is available for shared query work.",
205
+ origin: ZeroCache
206
+ }, "warn");
207
+ return backgroundConnection;
208
+ }
209
+ /** Returns the shared group auth state. */
210
+ getGroupState() {
211
+ return snapshotGroup(this.#group);
212
+ }
213
+ /**
214
+ * Reports which maintenance work is currently due.
215
+ *
216
+ * The result is a pure snapshot: callers decide which actions to run and
217
+ * when to wake up next. `earliestDeadlineAt` is the earliest outstanding
218
+ * maintenance deadline, including overdue work, unless a transient failure
219
+ * has deferred all scheduled maintenance until `maintenanceNotBeforeAt`.
220
+ */
221
+ planMaintenance() {
222
+ const dueRevalidations = [];
223
+ const now = this.#now();
224
+ let earliestDeadlineAt = this.#group.retransformAt;
225
+ for (const connection of this.#connections.values()) {
226
+ if (connection.state !== "validated" || connection.revalidateAt === void 0) continue;
227
+ if (connection.revalidateAt <= now) dueRevalidations.push(snapshotConnection(connection));
228
+ earliestDeadlineAt = minDefined(earliestDeadlineAt, connection.revalidateAt);
229
+ }
230
+ const dueRetransform = this.#group.retransformAt !== void 0 && this.#group.retransformAt <= now;
231
+ const maintenanceNotBeforeAt = this.#group.maintenanceNotBeforeAt;
232
+ if (maintenanceNotBeforeAt !== void 0 && maintenanceNotBeforeAt > now && earliestDeadlineAt !== void 0) return {
233
+ dueRevalidations: [],
234
+ dueRetransform: false,
235
+ earliestDeadlineAt: Math.max(earliestDeadlineAt, maintenanceNotBeforeAt)
236
+ };
237
+ return {
238
+ dueRevalidations: dueRevalidations.sort(compareByInsertionOrder),
239
+ dueRetransform,
240
+ earliestDeadlineAt
241
+ };
242
+ }
243
+ #removeConnection(selector, revision) {
244
+ const connection = this.#getConnectionContext(selector);
245
+ if (!connection) return;
246
+ if (revision !== void 0 && connection.revision !== revision) {
247
+ this.#lc.debug?.("Ignoring failConnection for stale revision", {
248
+ clientID: selector.clientID,
249
+ wsID: selector.wsID,
250
+ attemptedRevision: revision,
251
+ currentRevision: connection.revision
252
+ });
253
+ return;
254
+ }
255
+ const snapshot = snapshotConnection(connection);
256
+ this.#connections.delete(connection.clientID);
257
+ this.#refreshBackgroundConnectionContext();
258
+ this.#updateBackgroundRetransformDeadline(false);
259
+ return snapshot;
260
+ }
261
+ #demoteConnection(connection) {
262
+ connection.state = "provisional";
263
+ connection.revalidateAt = void 0;
264
+ this.#refreshBackgroundConnectionContext();
265
+ this.#updateBackgroundRetransformDeadline(false);
266
+ }
267
+ /**
268
+ * Keeps the background connection sticky while it remains validated.
269
+ *
270
+ * When a newly validated `preferred` connection is provided, it is promoted
271
+ * only if there is no current validated background connection. Otherwise the
272
+ * existing background connection stays in place until it disappears or is
273
+ * demoted, at which point the newest validated connection is selected.
274
+ */
275
+ #refreshBackgroundConnectionContext(preferred) {
276
+ if (preferred?.state === "validated") {
277
+ const currentBackgroundConnection = this.#getBackgroundConnectionContext();
278
+ if (currentBackgroundConnection?.clientID === preferred.clientID && currentBackgroundConnection.wsID === preferred.wsID) return;
279
+ if (currentBackgroundConnection !== void 0) return;
280
+ this.#group.backgroundConnection = {
281
+ clientID: preferred.clientID,
282
+ wsID: preferred.wsID
283
+ };
284
+ this.#lc.debug?.("Selected background connection for shared auth work", {
285
+ clientID: preferred.clientID,
286
+ wsID: preferred.wsID,
287
+ revision: preferred.revision,
288
+ reason: "preferred-validated"
289
+ });
290
+ return;
291
+ }
292
+ if (this.#getBackgroundConnectionContext()?.state === "validated") return;
293
+ const nextBackgroundConnection = [...this.#connections.values()].filter((connection) => connection.state === "validated").sort(comparePreferredValidatedConnection).at(0);
294
+ this.#group.backgroundConnection = nextBackgroundConnection ? {
295
+ clientID: nextBackgroundConnection.clientID,
296
+ wsID: nextBackgroundConnection.wsID
297
+ } : void 0;
298
+ if (nextBackgroundConnection) this.#lc.debug?.("Selected background connection for shared auth work", {
299
+ clientID: nextBackgroundConnection.clientID,
300
+ wsID: nextBackgroundConnection.wsID,
301
+ revision: nextBackgroundConnection.revision,
302
+ reason: "fallback-validated"
303
+ });
304
+ }
305
+ #getBackgroundConnectionContext() {
306
+ const backgroundConnection = this.#group.backgroundConnection;
307
+ if (!backgroundConnection) return;
308
+ return this.#getConnectionContext(backgroundConnection);
309
+ }
310
+ #getConnectionContext(selector) {
311
+ const connection = this.#connections.get(selector.clientID);
312
+ if (!connection) return;
313
+ if (connection.wsID !== selector.wsID) return;
314
+ return connection;
315
+ }
316
+ #mustGetConnectionContext(selector) {
317
+ const connection = this.#getConnectionContext(selector);
318
+ if (!connection) throw new ProtocolErrorWithLevel({
319
+ kind: InvalidConnectionRequest,
320
+ message: "Connection auth state was not available for this websocket.",
321
+ origin: ZeroCache
322
+ }, "warn");
323
+ return connection;
324
+ }
325
+ /**
326
+ * Keeps the group background retransform deadline coherent with current
327
+ * schedulability.
328
+ *
329
+ * When `reset` is false, this seeds a deadline only when shared retransform
330
+ * is now possible and no deadline exists yet, preserving any existing
331
+ * cadence. When `reset` is true, it starts a fresh interval from `#now()` if
332
+ * retransform is schedulable, or clears the deadline if it is not.
333
+ */
334
+ #updateBackgroundRetransformDeadline(reset) {
335
+ if (!this.#getBackgroundConnectionContext() || this.#retransformIntervalMs === void 0) {
336
+ this.#group.retransformAt = void 0;
337
+ return;
338
+ }
339
+ if (reset || this.#group.retransformAt === void 0) this.#group.retransformAt = this.#now() + this.#retransformIntervalMs;
340
+ }
341
+ #nextRevalidateAt() {
342
+ return this.#revalidateIntervalMs === void 0 ? void 0 : this.#now() + this.#revalidateIntervalMs;
343
+ }
344
+ };
345
+ function snapshotConnection(connection) {
346
+ if (!connection) return;
347
+ return {
348
+ ...connection,
349
+ queryContext: {
350
+ ...connection.queryContext,
351
+ headerOptions: {
352
+ ...connection.queryContext.headerOptions,
353
+ customHeaders: connection.queryContext.headerOptions.customHeaders ? { ...connection.queryContext.headerOptions.customHeaders } : void 0
354
+ }
355
+ },
356
+ pushContext: {
357
+ ...connection.pushContext,
358
+ headerOptions: {
359
+ ...connection.pushContext.headerOptions,
360
+ customHeaders: connection.pushContext.headerOptions.customHeaders ? { ...connection.pushContext.headerOptions.customHeaders } : void 0
361
+ }
362
+ }
363
+ };
364
+ }
365
+ function snapshotGroup(group) {
366
+ return {
367
+ ...group,
368
+ backgroundConnection: group.backgroundConnection ? { ...group.backgroundConnection } : void 0
369
+ };
370
+ }
371
+ function compareByInsertionOrder(a, b) {
372
+ return a.insertionOrder - b.insertionOrder || a.wsID.localeCompare(b.wsID);
373
+ }
374
+ function comparePreferredValidatedConnection(a, b) {
375
+ return b.insertionOrder - a.insertionOrder || b.wsID.localeCompare(a.wsID);
376
+ }
377
+ function minDefined(a, b) {
378
+ if (a === void 0) return b;
379
+ if (b === void 0) return a;
380
+ return Math.min(a, b);
381
+ }
382
+ //#endregion
383
+ export { ConnectionContextManagerImpl };
384
+
385
+ //# sourceMappingURL=connection-context-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection-context-manager.js","names":["#lc","#connections","#group","#validateLegacyJWT","#now","#revalidateIntervalMs","#retransformIntervalMs","#queryConfig","#pushConfig","#removeConnection","#nextInsertionOrder","#refreshBackgroundConnectionContext","#updateBackgroundRetransformDeadline","#mustGetConnectionContext","#demoteConnection","#getConnectionContext","#nextRevalidateAt","#getBackgroundConnectionContext"],"sources":["../../../../../../zero-cache/src/services/view-syncer/connection-context-manager.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport type {InitConnectionBody} from '../../../../zero-protocol/src/connect.ts';\nimport {ErrorKind} from '../../../../zero-protocol/src/error-kind.ts';\nimport {ErrorOrigin} from '../../../../zero-protocol/src/error-origin.ts';\nimport {\n authEquals,\n resolveAuth,\n type Auth,\n type ValidateLegacyJWT,\n} from '../../auth/auth.ts';\nimport {compileUrlPattern} from '../../custom/fetch.ts';\nimport {ProtocolErrorWithLevel} from '../../types/error-with-level.ts';\nimport type {ConnectParams} from '../../workers/connect-params.ts';\nimport type {UpdateAuthBody} from '../../../../zero-protocol/src/update-auth.ts';\nimport type {ZeroConfig} from '../../config/zero-config.ts';\n\nexport type ConnectionState = 'provisional' | 'validated';\n\n/**\n * Identifies one live websocket for a client slot.\n */\nexport type ConnectionSelector = {\n readonly clientID: string;\n readonly wsID: string;\n};\n\ntype FetchConfig = ZeroConfig['query'];\n\nexport type HeaderOptions = {\n apiKey?: string | undefined;\n customHeaders?: Record<string, string> | undefined;\n allowedClientHeaders?: readonly string[] | undefined;\n cookie?: string | undefined;\n origin?: string | undefined;\n};\n\nexport type ConnectionFetchContext = {\n url: string | undefined;\n allowedUrlPatterns: URLPattern[] | undefined;\n headerOptions: HeaderOptions;\n};\n\n/**\n * A snapshot of one live connection tracked by the manager.\n *\n * `revalidateAt` is only populated while the connection is `validated`.\n */\nexport type ConnectionContext = {\n state: ConnectionState;\n\n readonly clientID: string;\n readonly wsID: string;\n readonly userID: string | undefined;\n\n auth: Auth | undefined;\n\n readonly profileID: string | null;\n readonly baseCookie: string | null;\n readonly protocolVersion: number;\n\n revision: number;\n\n revalidateAt: number | undefined;\n\n readonly insertionOrder: number;\n\n readonly queryContext: ConnectionFetchContext;\n readonly pushContext: ConnectionFetchContext;\n};\n\n/**\n * Group-scoped auth state shared across the live connections.\n *\n * The background connection is the validated connection currently used for\n * shared background work. Retransform happens on a group level, and uses\n * the background connection's credential to refetch the latest queries.\n *\n * Since auth can be pinned to logged-out users, `userID` can be undefined\n * even after validation.\n */\nexport type GroupAuthState = {\n userID: string | undefined;\n validated: boolean;\n\n backgroundConnection: ConnectionSelector | undefined;\n retransformAt: number | undefined;\n // Defer all maintenance in case a transient failure occurs.\n maintenanceNotBeforeAt: number | undefined;\n};\n\nexport type ConnectionContextManager = {\n registerConnection(\n selector: ConnectionSelector,\n connectParams: ConnectParams,\n auth?: Auth,\n ): Readonly<ConnectionContext>;\n\n initConnection(\n selector: ConnectionSelector,\n body: InitConnectionBody,\n ): Readonly<ConnectionContext>;\n\n updateAuth(\n selector: ConnectionSelector,\n body: UpdateAuthBody,\n ): Promise<Readonly<ConnectionContext>>;\n\n validateConnection(\n selector: ConnectionSelector,\n revision: number,\n ):\n | Readonly<{\n connection: ConnectionContext;\n group: GroupAuthState;\n }>\n | undefined;\n\n failConnection(\n selector: ConnectionSelector,\n revision: number,\n ): Readonly<ConnectionContext> | undefined;\n closeConnection(\n selector: ConnectionSelector,\n ): Readonly<ConnectionContext> | undefined;\n\n markBackgroundRetransformSuccess(\n selector: ConnectionSelector,\n revision: number,\n ): void;\n\n deferMaintenance(kind: 'revalidate' | 'retransform'): void;\n\n getConnectionContext(\n selector: ConnectionSelector,\n ): Readonly<ConnectionContext> | undefined;\n mustGetConnectionContext(\n selector: ConnectionSelector,\n ): Readonly<ConnectionContext>;\n\n getBackgroundConnectionContext(): Readonly<ConnectionContext> | undefined;\n mustGetBackgroundConnectionContext(): Readonly<ConnectionContext>;\n\n getGroupState(): Readonly<GroupAuthState>;\n\n planMaintenance(): {\n dueRevalidations: Readonly<ConnectionContext>[];\n dueRetransform: boolean;\n earliestDeadlineAt: number | undefined;\n };\n};\n\n/**\n * State machine for the auth state of a single `ViewSyncerService`.\n *\n * Connections are registered as `provisional`, optionally backfilled with\n * `initConnection` metadata, and then promoted to `validated` once their\n * stored `userID` is confirmed as valid. The manager also tracks which\n * validated connection currently serves as the group's background connection.\n *\n * This is intentionally side-effect free.\n */\nexport class ConnectionContextManagerImpl implements ConnectionContextManager {\n readonly #lc: LogContext;\n\n // The live connection records, keyed by clientID\n readonly #connections = new Map<string, ConnectionContext>();\n readonly #group: GroupAuthState = {\n userID: undefined,\n backgroundConnection: undefined,\n retransformAt: undefined,\n maintenanceNotBeforeAt: undefined,\n validated: false,\n };\n\n readonly #validateLegacyJWT: ValidateLegacyJWT | undefined;\n\n readonly #now: () => number;\n readonly #revalidateIntervalMs: number | undefined;\n readonly #retransformIntervalMs: number | undefined;\n readonly #queryConfig: FetchConfig | undefined;\n readonly #pushConfig: FetchConfig | undefined;\n #nextInsertionOrder = 0;\n\n constructor(\n lc: LogContext,\n revalidateIntervalSeconds?: number,\n retransformIntervalSeconds?: number,\n queryConfig?: FetchConfig,\n pushConfig?: FetchConfig,\n validateLegacyJWT?: ValidateLegacyJWT,\n now?: () => number,\n ) {\n this.#lc = lc;\n this.#now = now ?? Date.now;\n this.#revalidateIntervalMs =\n revalidateIntervalSeconds === undefined\n ? undefined\n : revalidateIntervalSeconds * 1000;\n this.#retransformIntervalMs =\n retransformIntervalSeconds === undefined\n ? undefined\n : retransformIntervalSeconds * 1000;\n this.#queryConfig = queryConfig;\n this.#pushConfig = pushConfig;\n this.#validateLegacyJWT = validateLegacyJWT;\n }\n\n /**\n * Creates or replaces the live record for a websocket connection.\n *\n * Re-registering the same `clientID` drops the old socket record and starts\n * the replacement back in `provisional` state.\n */\n registerConnection(\n selector: ConnectionSelector,\n connectParams: ConnectParams,\n auth?: Auth,\n ): Readonly<ConnectionContext> {\n this.#removeConnection(selector);\n\n const sharedHeaders = {\n customHeaders: undefined,\n token: auth?.raw,\n origin: connectParams.origin,\n userID: connectParams.userID,\n };\n\n const connection: ConnectionContext = {\n state: 'provisional',\n\n clientID: connectParams.clientID,\n wsID: connectParams.wsID,\n revision: 0,\n userID: connectParams.userID,\n auth,\n\n profileID: connectParams.profileID,\n baseCookie: connectParams.baseCookie,\n protocolVersion: connectParams.protocolVersion,\n\n revalidateAt: undefined,\n\n queryContext: {\n url: this.#queryConfig?.url?.[0],\n allowedUrlPatterns: this.#queryConfig?.url?.map(compileUrlPattern),\n headerOptions: {\n ...sharedHeaders,\n apiKey: this.#queryConfig?.apiKey,\n allowedClientHeaders: this.#queryConfig?.allowedClientHeaders,\n cookie: this.#queryConfig?.forwardCookies\n ? connectParams.httpCookie\n : undefined,\n },\n },\n pushContext: {\n url: this.#pushConfig?.url?.[0],\n allowedUrlPatterns: this.#pushConfig?.url?.map(compileUrlPattern),\n headerOptions: {\n ...sharedHeaders,\n apiKey: this.#pushConfig?.apiKey,\n allowedClientHeaders: this.#pushConfig?.allowedClientHeaders,\n cookie: this.#pushConfig?.forwardCookies\n ? connectParams.httpCookie\n : undefined,\n },\n },\n\n insertionOrder: ++this.#nextInsertionOrder,\n };\n this.#connections.set(connection.clientID, connection);\n this.#refreshBackgroundConnectionContext();\n this.#updateBackgroundRetransformDeadline(false);\n return snapshotConnection(connection);\n }\n\n /**\n * Backfills `initConnection` data for sockets that were registered before the\n * client could send its full init payload.\n *\n * This updates metadata only; it does not validate the connection.\n */\n initConnection(\n selector: ConnectionSelector,\n body: InitConnectionBody,\n ): Readonly<ConnectionContext> {\n const connection = this.#mustGetConnectionContext(selector);\n\n if (body.userQueryURL) {\n connection.queryContext.url = body.userQueryURL;\n }\n if (body.userQueryHeaders) {\n connection.queryContext.headerOptions.customHeaders =\n body.userQueryHeaders;\n }\n if (body.userPushURL) {\n connection.pushContext.url = body.userPushURL;\n }\n if (body.userPushHeaders) {\n connection.pushContext.headerOptions.customHeaders = body.userPushHeaders;\n }\n\n connection.revision++;\n this.#demoteConnection(connection);\n\n return snapshotConnection(connection);\n }\n\n /**\n * A material auth change demotes the connection back to provisional until it\n * is validated again.\n */\n async updateAuth(\n selector: ConnectionSelector,\n body: UpdateAuthBody,\n ): Promise<Readonly<ConnectionContext>> {\n const connection = this.#mustGetConnectionContext(selector);\n\n const nextAuth = await resolveAuth(\n this.#lc,\n connection.auth,\n connection.userID,\n body.auth,\n this.#validateLegacyJWT,\n );\n\n const authChanged = !authEquals(connection.auth, nextAuth);\n connection.auth = nextAuth;\n if (authChanged) {\n connection.revision++;\n this.#demoteConnection(connection);\n }\n\n return snapshotConnection(connection);\n }\n\n /**\n * Validates one connection against the group's pinned `userID`.\n *\n * The first successful validation binds the group `userID`. Later\n * validations must match it. Validation also refreshes the connection's\n * revalidation deadline and may pick the connection as the group\n * background connection if none is currently available. If the websocket is\n * gone by the time async validation finishes, this becomes a no-op.\n */\n validateConnection(\n selector: ConnectionSelector,\n revision: number,\n ):\n | Readonly<{\n connection: ConnectionContext;\n group: GroupAuthState;\n }>\n | undefined {\n const connection = this.#getConnectionContext(selector);\n if (!connection) {\n return undefined;\n }\n\n if (connection.revision !== revision) {\n this.#lc.debug?.('Skipping validateConnection for stale revision', {\n clientID: selector.clientID,\n attemptedRevision: revision,\n currentRevision: connection.revision,\n });\n return undefined;\n }\n\n if (this.#group.validated && this.#group.userID !== connection.userID) {\n throw new ProtocolErrorWithLevel(\n {\n kind: ErrorKind.Unauthorized,\n message:\n 'Client groups are pinned to a single userID. Connection userID does not match existing client group userID.',\n origin: ErrorOrigin.ZeroCache,\n },\n 'warn',\n );\n }\n\n if (!this.#group.validated) {\n this.#group.validated = true;\n this.#group.userID = connection.userID;\n }\n\n connection.state = 'validated';\n connection.revalidateAt = this.#nextRevalidateAt();\n this.#refreshBackgroundConnectionContext(connection);\n this.#updateBackgroundRetransformDeadline(false);\n\n return {\n connection: snapshotConnection(connection),\n group: this.getGroupState(),\n };\n }\n\n /** Removes one connection due to failed auth and updates all derived background/deadline state. */\n failConnection(\n selector: ConnectionSelector,\n revision: number,\n ): ConnectionContext | undefined {\n return this.#removeConnection(selector, revision);\n }\n\n /** Removes one disconnected connection and updates all derived background/deadline state. */\n closeConnection(selector: ConnectionSelector): ConnectionContext | undefined {\n return this.#removeConnection(selector);\n }\n\n /**\n * Records a successful background retransform. This starts a fresh interval\n * from the manager clock when shared retransform is schedulable, or\n * clears the deadline if it is not.\n */\n markBackgroundRetransformSuccess(\n selector: ConnectionSelector,\n revision: number,\n ): void {\n const backgroundConnection = this.#getBackgroundConnectionContext();\n if (!backgroundConnection) {\n return;\n }\n if (\n selector !== undefined &&\n (backgroundConnection.clientID !== selector.clientID ||\n backgroundConnection.wsID !== selector.wsID ||\n backgroundConnection.revision !== revision)\n ) {\n return;\n }\n this.#updateBackgroundRetransformDeadline(true);\n }\n\n deferMaintenance(kind: 'revalidate' | 'retransform'): void {\n const intervalMs =\n kind === 'revalidate'\n ? this.#revalidateIntervalMs\n : this.#retransformIntervalMs;\n if (intervalMs === undefined) {\n return;\n }\n this.#group.maintenanceNotBeforeAt = Math.max(\n this.#group.maintenanceNotBeforeAt ?? 0,\n this.#now() + intervalMs,\n );\n }\n\n /** Returns the current live record for a client slot, if any. */\n getConnectionContext(\n selector: ConnectionSelector,\n ): Readonly<ConnectionContext> | undefined {\n return snapshotConnection(this.#getConnectionContext(selector));\n }\n\n /** Returns the live record for one websocket or throws if it is unavailable. */\n mustGetConnectionContext(\n selector: ConnectionSelector,\n ): Readonly<ConnectionContext> {\n return snapshotConnection(this.#mustGetConnectionContext(selector));\n }\n\n /** Returns the current background connection, if one exists. */\n getBackgroundConnectionContext(): Readonly<ConnectionContext> | undefined {\n return snapshotConnection(this.#getBackgroundConnectionContext());\n }\n\n mustGetBackgroundConnectionContext(): Readonly<ConnectionContext> {\n const backgroundConnection = this.#getBackgroundConnectionContext();\n if (!backgroundConnection) {\n throw new ProtocolErrorWithLevel(\n {\n kind: ErrorKind.InvalidConnectionRequest,\n message:\n 'No validated connection is available for shared query work.',\n origin: ErrorOrigin.ZeroCache,\n },\n 'warn',\n );\n }\n return backgroundConnection;\n }\n\n /** Returns the shared group auth state. */\n getGroupState(): Readonly<GroupAuthState> {\n return snapshotGroup(this.#group);\n }\n\n /**\n * Reports which maintenance work is currently due.\n *\n * The result is a pure snapshot: callers decide which actions to run and\n * when to wake up next. `earliestDeadlineAt` is the earliest outstanding\n * maintenance deadline, including overdue work, unless a transient failure\n * has deferred all scheduled maintenance until `maintenanceNotBeforeAt`.\n */\n planMaintenance(): {\n dueRevalidations: Readonly<ConnectionContext>[];\n dueRetransform: boolean;\n earliestDeadlineAt: number | undefined;\n } {\n const dueRevalidations: Readonly<ConnectionContext>[] = [];\n const now = this.#now();\n let earliestDeadlineAt = this.#group.retransformAt;\n\n for (const connection of this.#connections.values()) {\n if (\n connection.state !== 'validated' ||\n connection.revalidateAt === undefined\n ) {\n continue;\n }\n if (connection.revalidateAt <= now) {\n dueRevalidations.push(snapshotConnection(connection));\n }\n earliestDeadlineAt = minDefined(\n earliestDeadlineAt,\n connection.revalidateAt,\n );\n }\n\n const dueRetransform =\n this.#group.retransformAt !== undefined &&\n this.#group.retransformAt <= now;\n const maintenanceNotBeforeAt = this.#group.maintenanceNotBeforeAt;\n\n if (\n maintenanceNotBeforeAt !== undefined &&\n maintenanceNotBeforeAt > now &&\n earliestDeadlineAt !== undefined\n ) {\n return {\n dueRevalidations: [],\n dueRetransform: false,\n earliestDeadlineAt: Math.max(\n earliestDeadlineAt,\n maintenanceNotBeforeAt,\n ),\n };\n }\n\n return {\n dueRevalidations: dueRevalidations.sort(compareByInsertionOrder),\n dueRetransform,\n earliestDeadlineAt,\n };\n }\n\n #removeConnection(\n selector: ConnectionSelector,\n revision?: number,\n ): Readonly<ConnectionContext> | undefined {\n const connection = this.#getConnectionContext(selector);\n\n if (!connection) {\n return undefined;\n }\n\n // If the revision has changed, we should not remove the connection\n if (revision !== undefined && connection.revision !== revision) {\n this.#lc.debug?.('Ignoring failConnection for stale revision', {\n clientID: selector.clientID,\n wsID: selector.wsID,\n attemptedRevision: revision,\n currentRevision: connection.revision,\n });\n return undefined;\n }\n\n const snapshot = snapshotConnection(connection);\n\n this.#connections.delete(connection.clientID);\n this.#refreshBackgroundConnectionContext();\n this.#updateBackgroundRetransformDeadline(false);\n\n return snapshot;\n }\n\n #demoteConnection(connection: ConnectionContext): void {\n connection.state = 'provisional';\n connection.revalidateAt = undefined;\n this.#refreshBackgroundConnectionContext();\n this.#updateBackgroundRetransformDeadline(false);\n }\n\n /**\n * Keeps the background connection sticky while it remains validated.\n *\n * When a newly validated `preferred` connection is provided, it is promoted\n * only if there is no current validated background connection. Otherwise the\n * existing background connection stays in place until it disappears or is\n * demoted, at which point the newest validated connection is selected.\n */\n #refreshBackgroundConnectionContext(preferred?: ConnectionContext): void {\n if (preferred?.state === 'validated') {\n const currentBackgroundConnection =\n this.#getBackgroundConnectionContext();\n if (\n currentBackgroundConnection?.clientID === preferred.clientID &&\n currentBackgroundConnection.wsID === preferred.wsID\n ) {\n return;\n }\n if (currentBackgroundConnection !== undefined) {\n return;\n }\n this.#group.backgroundConnection = {\n clientID: preferred.clientID,\n wsID: preferred.wsID,\n };\n this.#lc.debug?.('Selected background connection for shared auth work', {\n clientID: preferred.clientID,\n wsID: preferred.wsID,\n revision: preferred.revision,\n reason: 'preferred-validated',\n });\n return;\n }\n\n const currentBackgroundConnection = this.#getBackgroundConnectionContext();\n if (currentBackgroundConnection?.state === 'validated') {\n return;\n }\n\n const nextBackgroundConnection = [...this.#connections.values()]\n .filter(connection => connection.state === 'validated')\n .sort(comparePreferredValidatedConnection)\n .at(0);\n this.#group.backgroundConnection = nextBackgroundConnection\n ? {\n clientID: nextBackgroundConnection.clientID,\n wsID: nextBackgroundConnection.wsID,\n }\n : undefined;\n if (nextBackgroundConnection) {\n this.#lc.debug?.('Selected background connection for shared auth work', {\n clientID: nextBackgroundConnection.clientID,\n wsID: nextBackgroundConnection.wsID,\n revision: nextBackgroundConnection.revision,\n reason: 'fallback-validated',\n });\n }\n }\n\n #getBackgroundConnectionContext(): ConnectionContext | undefined {\n const backgroundConnection = this.#group.backgroundConnection;\n if (!backgroundConnection) {\n return undefined;\n }\n return this.#getConnectionContext(backgroundConnection);\n }\n\n #getConnectionContext(\n selector: ConnectionSelector,\n ): ConnectionContext | undefined {\n const connection = this.#connections.get(selector.clientID);\n if (!connection) {\n return undefined;\n }\n if (connection.wsID !== selector.wsID) {\n return undefined;\n }\n return connection;\n }\n\n #mustGetConnectionContext(selector: ConnectionSelector): ConnectionContext {\n const connection = this.#getConnectionContext(selector);\n\n if (!connection) {\n throw new ProtocolErrorWithLevel(\n {\n kind: ErrorKind.InvalidConnectionRequest,\n message:\n 'Connection auth state was not available for this websocket.',\n origin: ErrorOrigin.ZeroCache,\n },\n 'warn',\n );\n }\n\n return connection;\n }\n\n /**\n * Keeps the group background retransform deadline coherent with current\n * schedulability.\n *\n * When `reset` is false, this seeds a deadline only when shared retransform\n * is now possible and no deadline exists yet, preserving any existing\n * cadence. When `reset` is true, it starts a fresh interval from `#now()` if\n * retransform is schedulable, or clears the deadline if it is not.\n */\n #updateBackgroundRetransformDeadline(reset: boolean) {\n const backgroundConnection = this.#getBackgroundConnectionContext();\n if (!backgroundConnection || this.#retransformIntervalMs === undefined) {\n this.#group.retransformAt = undefined;\n return;\n }\n\n if (reset || this.#group.retransformAt === undefined) {\n this.#group.retransformAt = this.#now() + this.#retransformIntervalMs;\n }\n }\n\n #nextRevalidateAt() {\n return this.#revalidateIntervalMs === undefined\n ? undefined\n : this.#now() + this.#revalidateIntervalMs;\n }\n}\n\nfunction snapshotConnection<T extends ConnectionContext | undefined>(\n connection: T,\n): T extends undefined ? T | undefined : Readonly<T> {\n if (!connection) {\n return undefined as T extends undefined ? T | undefined : Readonly<T>;\n }\n return {\n ...connection,\n queryContext: {\n ...connection.queryContext,\n headerOptions: {\n ...connection.queryContext.headerOptions,\n customHeaders: connection.queryContext.headerOptions.customHeaders\n ? {...connection.queryContext.headerOptions.customHeaders}\n : undefined,\n },\n },\n pushContext: {\n ...connection.pushContext,\n headerOptions: {\n ...connection.pushContext.headerOptions,\n customHeaders: connection.pushContext.headerOptions.customHeaders\n ? {...connection.pushContext.headerOptions.customHeaders}\n : undefined,\n },\n },\n } as T extends undefined ? T | undefined : Readonly<T>;\n}\n\nfunction snapshotGroup(group: GroupAuthState): Readonly<GroupAuthState> {\n return {\n ...group,\n backgroundConnection: group.backgroundConnection\n ? {...group.backgroundConnection}\n : undefined,\n };\n}\n\nfunction compareByInsertionOrder(\n a: Pick<ConnectionContext, 'insertionOrder' | 'wsID'>,\n b: Pick<ConnectionContext, 'insertionOrder' | 'wsID'>,\n) {\n return a.insertionOrder - b.insertionOrder || a.wsID.localeCompare(b.wsID);\n}\n\nfunction comparePreferredValidatedConnection(\n a: Pick<ConnectionContext, 'insertionOrder' | 'wsID'>,\n b: Pick<ConnectionContext, 'insertionOrder' | 'wsID'>,\n) {\n return b.insertionOrder - a.insertionOrder || b.wsID.localeCompare(a.wsID);\n}\n\nfunction minDefined(a: number | undefined, b: number | undefined) {\n if (a === undefined) {\n return b;\n }\n if (b === undefined) {\n return a;\n }\n return Math.min(a, b);\n}\n"],"mappings":"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"}
@@ -656,7 +656,7 @@ var CVRStore = class {
656
656
  let queryFlushes = [];
657
657
  if (hasQueryUpdates) {
658
658
  queryFlushes = this.#flushQueries(tx, lc);
659
- const partialOnlyCount = Array.from(this.#pendingQueryPartialUpdates.keys()).filter((key) => !this.#pendingQueryUpdates.has(key)).length;
659
+ const partialOnlyCount = [...this.#pendingQueryPartialUpdates.keys()].filter((key) => !this.#pendingQueryUpdates.has(key)).length;
660
660
  stats.queries = this.#pendingQueryUpdates.size + partialOnlyCount;
661
661
  stats.statements += (this.#pendingQueryUpdates.size > 0 ? 1 : 0) + (partialOnlyCount > 0 ? 1 : 0);
662
662
  if (desireFlush) {
@@ -679,7 +679,7 @@ var CVRStore = class {
679
679
  return Promise.all(pipelined);
680
680
  }, { mode: READ_COMMITTED });
681
681
  lc.debug?.(`flush tx completed after ${Date.now() - start} ms`);
682
- const baseQueries = (this.#pendingInstanceWrite ? 1 : 0) + this.#writes.size + (this.#pendingQueryUpdates.size > 0 ? 1 : 0) + (Array.from(this.#pendingQueryPartialUpdates.keys()).filter((key) => !this.#pendingQueryUpdates.has(key)).length > 0 ? 1 : 0) + (this.#pendingDesireUpdates.size > 0 ? 1 : 0);
682
+ const baseQueries = (this.#pendingInstanceWrite ? 1 : 0) + this.#writes.size + (this.#pendingQueryUpdates.size > 0 ? 1 : 0) + ([...this.#pendingQueryPartialUpdates.keys()].filter((key) => !this.#pendingQueryUpdates.has(key)).length > 0 ? 1 : 0) + (this.#pendingDesireUpdates.size > 0 ? 1 : 0);
683
683
  const rowsFlushed = results.length - baseQueries > 0;
684
684
  if (!rowsFlushed) stats.rowsDeferred = this.#pendingRowRecordUpdates.size;
685
685
  else stats.rows += this.#pendingRowRecordUpdates.size;