@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
@@ -2,7 +2,6 @@ import { assert, unreachable } from "../../../../shared/src/asserts.js";
2
2
  import { must } from "../../../../shared/src/must.js";
3
3
  import { InvalidConnectionRequest, InvalidConnectionRequestBaseCookie, Rehome } from "../../../../zero-protocol/src/error-kind-enum.js";
4
4
  import { ZeroCache } from "../../../../zero-protocol/src/error-origin-enum.js";
5
- import "../../../../zero-protocol/src/error-reason-enum.js";
6
5
  import { ProtocolError, isProtocolError } from "../../../../zero-protocol/src/error.js";
7
6
  import { MAX_TTL_MS, clampTTL } from "../../../../zql/src/query/ttl.js";
8
7
  import { stringify } from "../../../../shared/src/bigint-json.js";
@@ -19,10 +18,11 @@ import { Subscription } from "../../types/subscription.js";
19
18
  import { CustomKeyMap } from "../../../../shared/src/custom-key-map.js";
20
19
  import { ttlClockAsNumber, ttlClockFromNumber } from "./ttl-clock.js";
21
20
  import { EMPTY_CVR_VERSION, cmpVersions, versionFromString, versionString, versionToCookie } from "./schema/types.js";
22
- import { ProtocolErrorWithLevel, getLogLevel } from "../../types/error-with-level.js";
21
+ import { ProtocolErrorWithLevel, getLogLevel, wrapWithProtocolError } from "../../types/error-with-level.js";
22
+ import { isAuthErrorBody } from "../../auth/auth.js";
23
23
  import { ClientHandler, startPoke } from "./client-handler.js";
24
- import { CVRStore, ClientNotFoundError } from "./cvr-store.js";
25
24
  import { tracer } from "./tracer.js";
25
+ import { CVRStore, ClientNotFoundError } from "./cvr-store.js";
26
26
  import { CVRConfigDrivenUpdater, CVRQueryDrivenUpdater, nextEvictionTime } from "./cvr.js";
27
27
  import { handleInspect } from "./inspect-handler.js";
28
28
  import { resolver } from "@rocicorp/resolver";
@@ -41,6 +41,7 @@ function randomID() {
41
41
  var TTL_CLOCK_INTERVAL = 6e4;
42
42
  var ViewSyncerService = class {
43
43
  id;
44
+ contextManager;
44
45
  #shard;
45
46
  #lc;
46
47
  #pipelines;
@@ -48,10 +49,6 @@ var ViewSyncerService = class {
48
49
  #drainCoordinator;
49
50
  #keepaliveMs;
50
51
  #slowHydrateThreshold;
51
- #queryConfig;
52
- #authSession;
53
- userQueryURL;
54
- userQueryHeaders;
55
52
  #lastConnectTime = Date.now();
56
53
  /**
57
54
  * The TTL clock is used to determine the time at which queries are considered
@@ -82,10 +79,8 @@ var ViewSyncerService = class {
82
79
  #initialized = resolver();
83
80
  #cvr;
84
81
  #pipelinesSynced = false;
85
- #httpCookie;
86
- #origin;
87
- #lastAuthRevision = 0;
88
82
  #expiredQueriesTimer = 0;
83
+ #authMaintenanceTimer = 0;
89
84
  #setTimeout;
90
85
  #customQueryTransformer;
91
86
  #queryReplacements = /* @__PURE__ */ new Map();
@@ -106,15 +101,19 @@ var ViewSyncerService = class {
106
101
  });
107
102
  #queryTransformationHashChanges = getOrCreateCounter("sync", "query.transformation-hash-changes", "Number of times query transformation hash changed");
108
103
  #queryTransformationNoOps = getOrCreateCounter("sync", "query.transformation-no-ops", "Number of times query transformation resulted in no-op (hash unchanged)");
104
+ #lockWaitTime = getOrCreateHistogram("sync", "lock-wait-time", {
105
+ description: "Time spent waiting to acquire the ViewSyncer lock.",
106
+ unit: "s"
107
+ });
108
+ #pipelineResets = getOrCreateCounter("sync", "pipeline-resets", "Number of pipeline resets");
109
109
  #inspectorDelegate;
110
110
  #config;
111
111
  #runPriorityOp;
112
- constructor(config, lc, shard, taskID, clientGroupID, cvrDb, pipelineDriver, versionChanges, drainCoordinator, slowHydrateThreshold, inspectorDelegate, customQueryTransformer, runPriorityOp, authSession, keepaliveMs = DEFAULT_KEEPALIVE_MS, setTimeoutFn = setTimeout.bind(globalThis)) {
113
- const queryConfig = config.query?.url ? config.query : config.getQueries;
112
+ constructor(config, lc, shard, taskID, clientGroupID, cvrDb, pipelineDriver, versionChanges, drainCoordinator, slowHydrateThreshold, inspectorDelegate, contextManager, customQueryTransformer, runPriorityOp, keepaliveMs = DEFAULT_KEEPALIVE_MS, setTimeoutFn = setTimeout.bind(globalThis)) {
114
113
  this.#config = config;
115
114
  this.id = clientGroupID;
115
+ this.contextManager = contextManager;
116
116
  this.#shard = shard;
117
- this.#queryConfig = queryConfig;
118
117
  this.#lc = lc;
119
118
  this.#pipelines = pipelineDriver;
120
119
  this.#stateChanges = versionChanges;
@@ -123,36 +122,17 @@ var ViewSyncerService = class {
123
122
  this.#slowHydrateThreshold = slowHydrateThreshold;
124
123
  this.#inspectorDelegate = inspectorDelegate;
125
124
  this.#customQueryTransformer = customQueryTransformer;
126
- this.#authSession = authSession;
127
125
  this.#cvrStore = new CVRStore(lc, cvrDb, shard, taskID, clientGroupID, () => this.#stateChanges.cancel());
128
126
  this.#setTimeout = setTimeoutFn;
129
127
  this.#runPriorityOp = runPriorityOp;
130
128
  this.keepalive();
131
129
  }
132
- get auth() {
133
- return this.#authSession.auth;
134
- }
135
- clearAuth() {
136
- this.#authSession.clear();
137
- this.#lastAuthRevision = 0;
138
- }
139
- initAuthSession(userID, wireAuth) {
140
- return this.#authSession.update(userID, wireAuth);
141
- }
142
- #getHeaderOptions(forwardCookie) {
143
- return {
144
- apiKey: this.#queryConfig.apiKey,
145
- customHeaders: this.userQueryHeaders,
146
- allowedClientHeaders: this.#queryConfig.allowedClientHeaders,
147
- token: this.#authSession.auth?.raw,
148
- cookie: forwardCookie ? this.#httpCookie : void 0,
149
- origin: this.#origin
150
- };
151
- }
152
130
  #runInLockWithCVR(fn) {
153
131
  const rid = randomID();
154
132
  this.#lc.debug?.("about to acquire lock for cvr ", rid);
133
+ const lockWaitStart = performance.now();
155
134
  return this.#lock.withLock(async () => {
135
+ this.#lockWaitTime.record((performance.now() - lockWaitStart) / 1e3);
156
136
  this.#lc.debug?.("acquired lock in #runInLockWithCVR ", rid);
157
137
  const lc = this.#lc.withContext("lock", rid);
158
138
  if (!this.#stateChanges.active) {
@@ -186,6 +166,8 @@ var ViewSyncerService = class {
186
166
  } catch (e) {
187
167
  this.#cvr = void 0;
188
168
  throw e;
169
+ } finally {
170
+ this.#scheduleAuthMaintenance(lc);
189
171
  }
190
172
  });
191
173
  }
@@ -216,6 +198,7 @@ var ViewSyncerService = class {
216
198
  const result = await this.#advancePipelines(lc, cvr);
217
199
  if (result === "success") return;
218
200
  lc.info?.(`resetting pipelines: ${result.message}`);
201
+ this.#pipelineResets.add(1, { reason: result.reason });
219
202
  this.#pipelines.reset(clientSchema);
220
203
  this.#pipelinesSynced = false;
221
204
  }
@@ -227,7 +210,7 @@ var ViewSyncerService = class {
227
210
  }
228
211
  lc.info?.(`init pipelines@${version} (cvr@${cvrVer})`);
229
212
  await this.#hydrateUnchangedQueries(lc, cvr);
230
- await this.#syncQueryPipelineSet(lc, cvr, "missing");
213
+ await this.#syncQueryPipelineSet(lc, cvr, "missing", void 0);
231
214
  this.#pipelinesSynced = true;
232
215
  });
233
216
  }
@@ -246,13 +229,19 @@ var ViewSyncerService = class {
246
229
  if (hasExpiredQueries(cvr)) {
247
230
  lc = lc.withContext("method", "#removeExpiredQueries");
248
231
  lc.debug?.("Queries have expired");
249
- if (this.#pipelinesSynced) await this.#syncQueryPipelineSet(lc, cvr, "missing");
232
+ if (this.#pipelinesSynced) await this.#syncQueryPipelineSet(lc, cvr, "missing", void 0);
250
233
  }
251
234
  this.#scheduleExpireEviction(lc, cvr);
252
235
  };
253
236
  #totalHydrationTimeMs() {
254
237
  return this.#pipelines.totalHydrationTimeMs();
255
238
  }
239
+ get queryCount() {
240
+ return this.#pipelines.initialized() ? this.#pipelines.queries().size : 0;
241
+ }
242
+ get rowCount() {
243
+ return this.#cvrStore.rowCount;
244
+ }
256
245
  #keepAliveUntil = 0;
257
246
  /**
258
247
  * Guarantees that the ViewSyncer will remain running for at least
@@ -285,6 +274,10 @@ var ViewSyncerService = class {
285
274
  return this.#clients.size === 0;
286
275
  }
287
276
  #deleteClientDueToDisconnect(clientID, client) {
277
+ this.contextManager.closeConnection({
278
+ clientID,
279
+ wsID: client.wsID
280
+ });
288
281
  if (this.#clients.get(clientID) === client) {
289
282
  this.#clients.delete(clientID);
290
283
  if (this.#clients.size === 0) {
@@ -299,73 +292,110 @@ var ViewSyncerService = class {
299
292
  clearTimeout(this.#expiredQueriesTimer);
300
293
  this.#expiredQueriesTimer = 0;
301
294
  }
302
- initConnection(ctx, initConnectionMessage) {
295
+ #stopAuthMaintenanceTimer() {
296
+ if (this.#authMaintenanceTimer !== 0) this.#lc.debug?.("Stopping auth maintenance timer");
297
+ clearTimeout(this.#authMaintenanceTimer);
298
+ this.#authMaintenanceTimer = 0;
299
+ }
300
+ /**
301
+ * Schedules the auth maintenance wakeup from coordinator-derived
302
+ * deadlines. The timer plumbing is intentionally separate from the actual
303
+ * revalidation/retransform work so future policy changes only need to update
304
+ * the maintenance workers, not the wakeup logic.
305
+ *
306
+ * This is intentionally cheap & idempotent, so it can be called frequently
307
+ * when upstream state might have changed.
308
+ */
309
+ #scheduleAuthMaintenance(lc) {
310
+ this.#stopAuthMaintenanceTimer();
311
+ const plan = this.contextManager.planMaintenance();
312
+ if (plan.earliestDeadlineAt === void 0) {
313
+ lc.debug?.("No auth maintenance wakeup scheduled");
314
+ return;
315
+ }
316
+ const delay = Math.max(0, plan.earliestDeadlineAt - Date.now());
317
+ lc.debug?.(`Scheduling auth maintenance timer at ${new Date(plan.earliestDeadlineAt).toISOString()}`, {
318
+ delay,
319
+ earliestDeadlineAt: plan.earliestDeadlineAt
320
+ });
321
+ this.#authMaintenanceTimer = this.#setTimeout(() => {
322
+ this.#authMaintenanceTimer = 0;
323
+ this.#runInLockWithCVR((lc, cvr) => this.#runAuthMaintenance(lc, cvr)).catch((e) => this.#stateChanges.fail(e));
324
+ }, delay);
325
+ }
326
+ async #runAuthMaintenance(lc, _cvr) {
327
+ const plan = this.contextManager.planMaintenance();
328
+ if (plan.dueRevalidations.length === 0 && !plan.dueRetransform) {
329
+ lc.debug?.("Auth maintenance woke up with no due work");
330
+ return;
331
+ }
332
+ lc.debug?.("Auth maintenance woke up with pending work", {
333
+ dueRevalidations: plan.dueRevalidations.length,
334
+ dueRetransform: plan.dueRetransform
335
+ });
336
+ for (const connection of plan.dueRevalidations) try {
337
+ await this.#validateConnection(connection);
338
+ } catch (e) {
339
+ if (isProtocolError(e) && isTransformFailedError(e)) {
340
+ lc.warn?.("Scheduled auth revalidation failed; deferring auth maintenance", {
341
+ clientID: connection.clientID,
342
+ wsID: connection.wsID,
343
+ message: e.message
344
+ });
345
+ this.contextManager.deferMaintenance("revalidate");
346
+ return;
347
+ }
348
+ throw e;
349
+ }
350
+ if (this.contextManager.planMaintenance().dueRetransform) await this.#runBackgroundRetransform(lc);
351
+ }
352
+ initConnection(selector, initConnectionMessage) {
303
353
  this.#lc.debug?.("viewSyncer.initConnection");
304
354
  return startSpan(tracer, "vs.initConnection", () => {
305
- const { clientID, profileID, wsID, baseCookie, httpCookie, origin, protocolVersion } = ctx;
306
- this.#httpCookie = httpCookie;
307
- this.#origin = origin;
308
- const [, { userQueryURL, userQueryHeaders }] = initConnectionMessage;
309
- if (this.userQueryURL === void 0) {
310
- this.userQueryURL = userQueryURL;
311
- this.userQueryHeaders = userQueryHeaders;
312
- } else if (this.userQueryURL !== userQueryURL) this.#lc.warn?.("Client provided different query parameters than client group", {
313
- clientID,
314
- clientURL: userQueryURL,
315
- clientGroupURL: this.userQueryURL
316
- });
317
- const lc = this.#lc.withContext("clientID", clientID).withContext("wsID", wsID);
355
+ const ctx = this.contextManager.mustGetConnectionContext(selector);
356
+ const lc = this.#lc.withContext("clientID", ctx.clientID).withContext("wsID", ctx.wsID);
318
357
  const downstream = Subscription.create({ cleanup: (_, err) => {
319
358
  err ? lc[getLogLevel(err)]?.(`client closed with error`, err) : lc.info?.("client closed");
320
- this.#deleteClientDueToDisconnect(clientID, newClient);
321
- this.#activeClients.add(-1, { [PROTOCOL_VERSION_ATTR]: protocolVersion });
359
+ this.#deleteClientDueToDisconnect(ctx.clientID, newClient);
360
+ this.#activeClients.add(-1, { [PROTOCOL_VERSION_ATTR]: ctx.protocolVersion });
322
361
  } });
323
- this.#activeClients.add(1, { [PROTOCOL_VERSION_ATTR]: protocolVersion });
362
+ this.#activeClients.add(1, { [PROTOCOL_VERSION_ATTR]: ctx.protocolVersion });
324
363
  if (this.#clients.size === 0) this.#ttlClockBase = Date.now();
325
- const newClient = new ClientHandler(lc, this.id, clientID, wsID, this.#shard, baseCookie, downstream);
326
- this.#clients.get(clientID)?.close(`replaced by wsID: ${wsID}`);
327
- this.#clients.set(clientID, newClient);
328
- this.#runInLockForClient(ctx, initConnectionMessage, async (lc, clientID, msg, cvr) => {
364
+ const newClient = new ClientHandler(lc, this.id, ctx.clientID, ctx.wsID, this.#shard, ctx.baseCookie, downstream);
365
+ this.#clients.get(ctx.clientID)?.close(`replaced by wsID: ${ctx.wsID}`);
366
+ this.#clients.set(ctx.clientID, newClient);
367
+ startAsyncSpan(tracer, "vs.initConnection.async", () => this.#runInLockForClient(ctx, initConnectionMessage, async (lc, clientID, msg, cvr) => {
329
368
  if (cvr.clientSchema === null && !msg.clientSchema) throw new ProtocolErrorWithLevel({
330
369
  kind: InvalidConnectionRequest,
331
370
  message: "The initConnection message for a new client group must include client schema.",
332
371
  origin: ZeroCache
333
372
  }, "warn");
334
- await this.#handleConfigUpdate(lc, clientID, msg, cvr, "all", profileID ?? `cg${this.id}`);
373
+ if (!await this.#validateConnection(ctx)) return;
374
+ await this.#handleConfigUpdate(lc, clientID, msg, cvr, "all", ctx.profileID ?? `cg${this.id}`, ctx);
335
375
  this.#initialized.resolve("initialized");
336
- }, newClient).catch((e) => newClient.fail(e));
376
+ }, newClient)).catch((e) => newClient.fail(e));
337
377
  return downstream;
338
378
  });
339
379
  }
340
- async changeDesiredQueries(ctx, msg) {
341
- await this.#runInLockForClient(ctx, msg, async (lc, clientID, msg, cvr) => {
342
- let customQueryTransformMode = "missing";
343
- const currentAuthRevision = this.#authSession.revision;
344
- if (this.#lastAuthRevision < currentAuthRevision) {
345
- customQueryTransformMode = "all";
346
- lc.debug?.("Auth revision changed, setting customQueryTransformMode to all");
347
- }
348
- const result = await this.#handleConfigUpdate(lc, clientID, msg, cvr, customQueryTransformMode);
349
- if (customQueryTransformMode === "all") this.#lastAuthRevision = currentAuthRevision;
350
- return result;
351
- });
380
+ async changeDesiredQueries(selector, msg) {
381
+ await this.#runInLockForClient(selector, msg, (lc, clientID, msg, cvr) => this.#handleConfigUpdate(lc, clientID, msg, cvr, "missing", void 0, this.contextManager.mustGetConnectionContext(selector)));
352
382
  }
353
- async updateAuth(ctx, msg) {
354
- await this.#runInLockForClient(ctx, msg, async (lc, clientID, _, cvr) => {
355
- const authResult = await this.#authSession.update(ctx.userID, msg[1].auth);
356
- if (!authResult.ok) throw new ProtocolErrorWithLevel(authResult.error, "warn");
357
- const currentAuthRevision = this.#authSession.revision;
358
- if (this.#lastAuthRevision >= currentAuthRevision) {
359
- lc.debug?.("Auth revision unchanged, skipping query re-transformation");
383
+ async updateAuth(selector, msg, authRevisionChanged) {
384
+ await this.#runInLockForClient(selector, msg, async (lc, clientID, _, cvr) => {
385
+ if (!authRevisionChanged) {
386
+ lc.debug?.("Auth unchanged, skipping query re-transformation");
360
387
  return;
361
- } else lc.debug?.("Auth revision changed, re-transforming queries");
362
- const result = await this.#handleConfigUpdate(lc, clientID, {}, cvr, "all");
363
- this.#lastAuthRevision = currentAuthRevision;
364
- return result;
388
+ }
389
+ lc.debug?.("Auth changed, re-validating and re-transforming queries");
390
+ const connection = this.contextManager.mustGetConnectionContext(selector);
391
+ if (!this.#pipelinesSynced) {
392
+ if (!await this.#validateConnection(connection)) return;
393
+ }
394
+ return await this.#handleConfigUpdate(lc, clientID, {}, cvr, "all", void 0, connection);
365
395
  });
366
396
  }
367
- async deleteClients(ctx, msg) {
368
- return await this.#runInLockForClient(ctx, [msg[0], { deleted: msg[1] }], (lc, clientID, msg, cvr) => this.#handleConfigUpdate(lc, clientID, msg, cvr, "missing")) ?? [];
397
+ async deleteClients(selector, msg) {
398
+ return await this.#runInLockForClient(selector, [msg[0], { deleted: msg[1] }], (lc, clientID, msg, cvr) => this.#handleConfigUpdate(lc, clientID, msg, cvr, "missing", void 0, this.contextManager.mustGetConnectionContext(selector))) ?? [];
369
399
  }
370
400
  #getTTLClock(now) {
371
401
  const delta = now - this.#ttlClockBase;
@@ -407,7 +437,7 @@ var ViewSyncerService = class {
407
437
  lc.warn?.("failed to update TTL clock", rid, `after ${Date.now() - start} ms`, e);
408
438
  });
409
439
  }
410
- async #updateCVRConfig(lc, cvr, clientID, customQueryTransformMode, fn) {
440
+ async #updateCVRConfig(lc, cvr, clientID, customQueryTransformMode, ctx, fn) {
411
441
  const updater = new CVRConfigDrivenUpdater(this.#cvrStore, cvr, this.#shard);
412
442
  updater.ensureClient(clientID);
413
443
  const patches = fn(updater);
@@ -420,21 +450,24 @@ var ViewSyncerService = class {
420
450
  await pokers.end(newCVR.version);
421
451
  });
422
452
  }
423
- if (this.#pipelinesSynced) await this.#syncQueryPipelineSet(lc, this.#cvr, customQueryTransformMode);
453
+ if (this.#pipelinesSynced) await this.#syncQueryPipelineSet(lc, this.#cvr, customQueryTransformMode, ctx);
424
454
  return this.#cvr;
425
455
  }
426
456
  /**
427
457
  * Runs the given `fn` to process the `msg` from within the `#lock`,
428
458
  * optionally adding the `newClient` if supplied.
429
459
  */
430
- #runInLockForClient(ctx, msg, fn, newClient) {
460
+ #runInLockForClient(selector, msg, fn, newClient) {
431
461
  this.#lc.debug?.("viewSyncer.#runInLockForClient");
432
- const { clientID, wsID } = ctx;
462
+ const { clientID, wsID } = selector;
433
463
  const [cmd, body] = msg;
434
464
  if (newClient || !this.#clients.has(clientID)) this.#lastConnectTime = Date.now();
435
- return startAsyncSpan(tracer, `vs.#runInLockForClient(${cmd})`, async () => {
465
+ return startAsyncSpan(tracer, `vs.#runInLockForClient(${cmd})`, async (span) => {
466
+ span.setAttribute("clientGroupID", this.id);
467
+ span.setAttribute("clientID", clientID);
436
468
  let client;
437
469
  let result;
470
+ let ctx;
438
471
  try {
439
472
  await this.#runInLockWithCVR(async (lc, cvr) => {
440
473
  lc = lc.withContext("clientID", clientID).withContext("wsID", wsID).withContext("cmd", cmd);
@@ -444,6 +477,7 @@ var ViewSyncerService = class {
444
477
  lc.debug?.("mismatched wsID", client?.wsID, wsID);
445
478
  return;
446
479
  }
480
+ ctx = this.contextManager.getConnectionContext(selector);
447
481
  if (newClient) {
448
482
  assert(newClient === client, "newClient must match existing client");
449
483
  checkClientAndCVRVersions(client.version(), cvr.version);
@@ -452,12 +486,8 @@ var ViewSyncerService = class {
452
486
  result = await fn(lc, clientID, body, cvr);
453
487
  });
454
488
  } catch (e) {
455
- const lc = this.#lc.withContext("clientID", clientID).withContext("wsID", wsID).withContext("cmd", cmd);
456
- lc[getLogLevel(e)]?.(`closing connection with error`, e);
457
- if (isTransformAuthFailure(e)) {
458
- lc.debug?.("Auth failure detected in transform response");
459
- this.clearAuth();
460
- }
489
+ this.#lc.withContext("clientID", clientID).withContext("wsID", wsID).withContext("cmd", cmd)[getLogLevel(e)]?.(`closing connection with error`, e);
490
+ if (ctx) this.contextManager.failConnection(selector, ctx.revision);
461
491
  if (client) client.fail(e);
462
492
  else throw e;
463
493
  }
@@ -468,10 +498,10 @@ var ViewSyncerService = class {
468
498
  const clients = [...this.#clients.values()];
469
499
  return atVersion ? clients.filter((c) => cmpVersions(c.version() ?? EMPTY_CVR_VERSION, atVersion) === 0) : clients;
470
500
  }
471
- #handleConfigUpdate = (lc, clientID, { clientSchema, deleted, desiredQueriesPatch, activeClients }, cvr, customQueryTransformMode, profileID) => startAsyncSpan(tracer, "vs.#handleConfigUpdate", async () => {
501
+ #handleConfigUpdate = (lc, clientID, { clientSchema, deleted, desiredQueriesPatch, activeClients }, cvr, customQueryTransformMode, profileID, ctx) => startAsyncSpan(tracer, "vs.#handleConfigUpdate", async () => {
472
502
  const deletedClientIDs = [];
473
503
  const deletedClientGroupIDs = [];
474
- cvr = await this.#updateCVRConfig(lc, cvr, clientID, customQueryTransformMode, (updater) => {
504
+ cvr = await this.#updateCVRConfig(lc, cvr, clientID, customQueryTransformMode, ctx, (updater) => {
475
505
  const { ttlClock } = cvr;
476
506
  const patches = [];
477
507
  if (clientSchema) updater.setClientSchema(lc, clientSchema);
@@ -568,16 +598,17 @@ var ViewSyncerService = class {
568
598
  let customHashMismatchCount = 0;
569
599
  let otherHashMismatchCount = 0;
570
600
  if (customQueries.size > 0 && !this.#customQueryTransformer) lc.warn?.("Custom/named queries were requested but no `ZERO_QUERY_URL` is configured for Zero Cache.");
601
+ const backgroundContext = this.contextManager.mustGetBackgroundConnectionContext();
571
602
  const customQueryTransformer = this.#customQueryTransformer;
572
603
  if (customQueryTransformer && customQueries.size > 0) {
573
- const transformedCustomQueries = await this.#runPriorityOp(lc, "#hydrateUnchangedQueries transforming custom queries", () => customQueryTransformer.transform(this.#getHeaderOptions(this.#queryConfig.forwardCookies), customQueries.values(), this.userQueryURL));
574
- if (Array.isArray(transformedCustomQueries)) for (const q of transformedCustomQueries) if ("error" in q) customErrorCount++;
604
+ const transformedCustomQueries = await this.#runPriorityOp(lc, "#hydrateUnchangedQueries transforming custom queries", () => customQueryTransformer.transform(backgroundContext, customQueries.values()));
605
+ if (!transformedCustomQueries.cached) this.contextManager.validateConnection(backgroundContext, backgroundContext.revision);
606
+ if (Array.isArray(transformedCustomQueries.result)) for (const q of transformedCustomQueries.result) if ("error" in q) customErrorCount++;
575
607
  else if (q.transformationHash !== customQueries.get(q.id)?.transformationHash) customHashMismatchCount++;
576
608
  else transformedQueries.push(q);
577
609
  }
578
610
  for (const q of otherQueries) {
579
- const auth = this.#authSession.auth;
580
- const transformed = transformAndHashQuery(lc, q.id, q.ast, must(this.#pipelines.currentPermissions()).permissions ?? { tables: {} }, auth?.type === "jwt" ? auth : void 0, q.type === "internal");
611
+ const transformed = transformAndHashQuery(lc, q.id, q.ast, must(this.#pipelines.currentPermissions()).permissions ?? { tables: {} }, backgroundContext.auth?.type === "jwt" ? backgroundContext.auth : void 0, q.type === "internal");
581
612
  if (transformed.transformationHash === q.transformationHash) transformedQueries.push(transformed);
582
613
  else otherHashMismatchCount++;
583
614
  }
@@ -650,8 +681,9 @@ var ViewSyncerService = class {
650
681
  *
651
682
  * This must be called from within the #lock.
652
683
  */
653
- #syncQueryPipelineSet(lc, cvr, customQueryTransformMode) {
654
- return startAsyncSpan(tracer, "vs.#syncQueryPipelineSet", async () => {
684
+ #syncQueryPipelineSet(lc, cvr, customQueryTransformMode, ctx) {
685
+ return startAsyncSpan(tracer, "vs.#syncQueryPipelineSet", async (span) => {
686
+ span.setAttribute("clientGroupID", this.id);
655
687
  assert(this.#pipelines.initialized(), "pipelines must be initialized (syncQueryPipelineSet)");
656
688
  if (this.#ttlClock === void 0) this.#ttlClock = cvr.ttlClock;
657
689
  const now = Date.now();
@@ -660,6 +692,7 @@ var ViewSyncerService = class {
660
692
  const customQueries = /* @__PURE__ */ new Map();
661
693
  const otherQueries = [];
662
694
  const transformedQueries = [];
695
+ const resolvedContext = ctx ?? this.contextManager.mustGetBackgroundConnectionContext();
663
696
  for (const [id, query] of cvrQueryEntires) if (query.type === "custom") {
664
697
  assert(id === query.id, "custom query id mismatch");
665
698
  customQueries.set(id, query);
@@ -669,8 +702,7 @@ var ViewSyncerService = class {
669
702
  });
670
703
  for (const { id, query: origQuery } of otherQueries) {
671
704
  assert(id === origQuery.id, "query id mismatch");
672
- const auth = this.#authSession.auth;
673
- const transformed = transformAndHashQuery(lc, origQuery.id, origQuery.ast, must(this.#pipelines.currentPermissions()).permissions ?? { tables: {} }, auth?.type === "jwt" ? auth : void 0, origQuery.type === "internal");
705
+ const transformed = transformAndHashQuery(lc, origQuery.id, origQuery.ast, must(this.#pipelines.currentPermissions()).permissions ?? { tables: {} }, resolvedContext.auth?.type === "jwt" ? resolvedContext.auth : void 0, origQuery.type === "internal");
674
706
  transformedQueries.push({
675
707
  id,
676
708
  origQuery,
@@ -685,9 +717,12 @@ var ViewSyncerService = class {
685
717
  const transformStart = performance.now();
686
718
  let transformedCustomQueries;
687
719
  try {
688
- transformedCustomQueries = await this.#runPriorityOp(lc, "#syncQueryPipelineSet transforming custom queries", () => customQueryTransformer.transform(this.#getHeaderOptions(true), customQueriesToTransform, this.userQueryURL));
689
- if (!Array.isArray(transformedCustomQueries) && transformedCustomQueries.kind === "TransformFailed") throw new ProtocolErrorWithLevel(transformedCustomQueries, "warn");
690
- else this.#queryTransformations.add(1, { result: "success" });
720
+ transformedCustomQueries = await this.#runPriorityOp(lc, "#syncQueryPipelineSet transforming custom queries", () => customQueryTransformer.transform(resolvedContext, customQueriesToTransform));
721
+ if ("kind" in transformedCustomQueries.result) throw new ProtocolErrorWithLevel(transformedCustomQueries.result, "warn");
722
+ else {
723
+ if (!transformedCustomQueries.cached) this.contextManager.validateConnection(resolvedContext, resolvedContext.revision);
724
+ this.#queryTransformations.add(1, { result: "success" });
725
+ }
691
726
  } catch (e) {
692
727
  this.#queryTransformations.add(1, { result: "error" });
693
728
  throw e;
@@ -696,7 +731,7 @@ var ViewSyncerService = class {
696
731
  this.#queryTransformationTime.record(transformDuration);
697
732
  }
698
733
  const successfullyTransformedCustomQueries = /* @__PURE__ */ new Map();
699
- erroredQueryIDs = this.#processTransformedCustomQueries(lc, transformedCustomQueries, (q) => {
734
+ erroredQueryIDs = this.#processTransformedCustomQueries(lc, transformedCustomQueries.result, (q) => {
700
735
  const origQuery = customQueries.get(q.id);
701
736
  if (origQuery) {
702
737
  successfullyTransformedCustomQueries.set(q.id, q);
@@ -720,7 +755,7 @@ var ViewSyncerService = class {
720
755
  }
721
756
  }
722
757
  }
723
- const removeQueriesQueryIds = new Set(Object.values(cvr.queries).filter((q) => expired(ttlClock, q)).map((q) => q.id).concat(erroredQueryIDs || []));
758
+ const removeQueriesQueryIds = new Set([...Object.values(cvr.queries).filter((q) => expired(ttlClock, q)).map((q) => q.id), ...erroredQueryIDs || []]);
724
759
  const addQueries = transformedQueries.map(({ id, transformed }) => ({
725
760
  id,
726
761
  ast: transformed.transformedAst,
@@ -731,7 +766,7 @@ var ViewSyncerService = class {
731
766
  const orig = cvr.queries[q.id];
732
767
  lc.debug?.("ViewSyncer adding query", q.ast, "transformed from", orig.type === "custom" ? orig.name : orig.ast);
733
768
  }
734
- if (addQueries.length > 0 || removeQueriesQueryIds.size > 0) await this.#addAndRemoveQueries(lc, cvr, addQueries, [...removeQueriesQueryIds].map((id) => ({ id })));
769
+ if (addQueries.length > 0 || removeQueriesQueryIds.size > 0) await this.#addAndRemoveQueries(lc, cvr, addQueries, Array.from(removeQueriesQueryIds, (id) => ({ id })));
735
770
  else await this.#catchupClients(lc, cvr);
736
771
  });
737
772
  }
@@ -804,11 +839,13 @@ var ViewSyncerService = class {
804
839
  hydrationTime.record(totalProcessTime / 1e3);
805
840
  }
806
841
  await this.#processChanges(lc, timer, generateRowChanges(this.#slowHydrateThreshold), updater, pokers);
807
- for (const patch of await updater.deleteUnreferencedRows(lc)) await pokers.addPatch(patch);
842
+ await startAsyncSpan(tracer, "vs.#syncQueryPipelineSet.deleteUnreferencedRows", async () => {
843
+ for (const patch of await updater.deleteUnreferencedRows(lc)) await pokers.addPatch(patch);
844
+ });
808
845
  this.#cvr = await this.#flushUpdater(lc, updater);
809
846
  const finalVersion = this.#cvr.version;
810
847
  await this.#catchupClients(lc, cvr, finalVersion, addQueries.map((q) => q.id), pokers);
811
- await pokers.end(finalVersion);
848
+ await startAsyncSpan(tracer, "vs.#syncQueryPipelineSet.pokeEnd", () => pokers.end(finalVersion));
812
849
  const wallTime = performance.now() - start;
813
850
  lc.info?.(`finished processing queries (process: ${totalProcessTime} ms, wall: ${wallTime} ms)`);
814
851
  });
@@ -889,7 +926,10 @@ var ViewSyncerService = class {
889
926
  total += rows.size;
890
927
  lc.debug?.(`processing ${rows.size} (of ${total}) rows (${wallElapsed} ms)`);
891
928
  const patches = await updater.received(lc, rows);
892
- for (const patch of patches) await pokers.addPatch(patch);
929
+ await startAsyncSpan(tracer, "processBatch.flushToClient", async (span) => {
930
+ span.setAttribute("patches", patches.length);
931
+ for (const patch of patches) await pokers.addPatch(patch);
932
+ });
893
933
  rows.clear();
894
934
  });
895
935
  await startAsyncSpan(tracer, "loopingChanges", async (span) => {
@@ -944,7 +984,8 @@ var ViewSyncerService = class {
944
984
  * Returns false if the advancement failed due to a schema change.
945
985
  */
946
986
  #advancePipelines(lc, cvr) {
947
- return startAsyncSpan(tracer, "vs.#advancePipelines", async () => {
987
+ return startAsyncSpan(tracer, "vs.#advancePipelines", async (span) => {
988
+ span.setAttribute("clientGroupID", this.id);
948
989
  assert(this.#pipelines.initialized(), "pipelines must be initialized (advancePipelines");
949
990
  const start = performance.now();
950
991
  const timer = new TimeSliceTimer(lc);
@@ -964,7 +1005,7 @@ var ViewSyncerService = class {
964
1005
  }
965
1006
  this.#cvr = await this.#flushUpdater(lc, updater);
966
1007
  const finalVersion = this.#cvr.version;
967
- await pokers.end(finalVersion);
1008
+ await startAsyncSpan(tracer, "vs.#advancePipelines.pokeEnd", () => pokers.end(finalVersion));
968
1009
  const wallTime = performance.now() - start;
969
1010
  const totalProcessTime = timer.totalElapsed();
970
1011
  lc.info?.(`finished processing advancement of ${numChanges} changes ((process: ${totalProcessTime} ms, wall: ${wallTime} ms))`);
@@ -972,14 +1013,86 @@ var ViewSyncerService = class {
972
1013
  return "success";
973
1014
  });
974
1015
  }
975
- async inspect(context, msg) {
976
- await this.#runInLockForClient(context, msg, this.#handleInspect);
1016
+ async inspect(selector, msg) {
1017
+ await this.#runInLockForClient(selector, msg, this.#handleInspect);
977
1018
  }
978
1019
  #handleInspect = async (lc, clientID, body, cvr) => {
979
1020
  const client = must(this.#clients.get(clientID));
980
- const auth = this.#authSession.auth;
981
- return handleInspect(lc, body, cvr, client, this.#inspectorDelegate, this.id, this.#cvrStore, this.#config, this.#getHeaderOptions(this.#queryConfig.forwardCookies ?? false), this.userQueryURL, auth?.type === "jwt" ? auth : void 0);
1021
+ const ctx = this.contextManager.mustGetConnectionContext({
1022
+ clientID,
1023
+ wsID: client.wsID
1024
+ });
1025
+ return handleInspect(lc, body, cvr, client, this.#inspectorDelegate, this.id, this.#cvrStore, this.#config, ctx);
982
1026
  };
1027
+ async #runBackgroundRetransform(lc) {
1028
+ const attemptRetransform = async (connection) => {
1029
+ await this.#syncQueryPipelineSet(lc, must(this.#cvr, "cvr missing during auth maintenance retransform"), "all", connection);
1030
+ this.contextManager.markBackgroundRetransformSuccess({
1031
+ clientID: connection.clientID,
1032
+ wsID: connection.wsID
1033
+ }, connection.revision);
1034
+ };
1035
+ let backgroundConnection = this.contextManager.getBackgroundConnectionContext();
1036
+ if (!backgroundConnection) {
1037
+ lc.debug?.("Skipping background retransform with no selected connection");
1038
+ return;
1039
+ }
1040
+ for (;;) {
1041
+ try {
1042
+ await attemptRetransform(backgroundConnection);
1043
+ return;
1044
+ } catch (e) {
1045
+ if (isProtocolError(e)) {
1046
+ if (isAuthErrorBody(e.errorBody)) {
1047
+ lc.warn?.("Background retransform auth failed; failing connection and searching for replacement", {
1048
+ clientID: backgroundConnection.clientID,
1049
+ message: e.message
1050
+ });
1051
+ this.#failMaintenanceConnection(backgroundConnection, e);
1052
+ } else if (isTransformFailedError(e)) {
1053
+ lc.warn?.("Background retransform failed; deferring auth maintenance", {
1054
+ clientID: backgroundConnection.clientID,
1055
+ message: e.message
1056
+ });
1057
+ this.contextManager.deferMaintenance("retransform");
1058
+ return;
1059
+ }
1060
+ } else throw e;
1061
+ }
1062
+ const replacement = this.contextManager.getBackgroundConnectionContext();
1063
+ if (!replacement) {
1064
+ lc.debug?.("No replacement connection available for background retransform");
1065
+ return;
1066
+ }
1067
+ lc.debug?.("Retrying background retransform with replacement connection", {
1068
+ clientID: replacement.clientID,
1069
+ wsID: replacement.wsID
1070
+ });
1071
+ backgroundConnection = replacement;
1072
+ }
1073
+ }
1074
+ async #validateConnection(ctx) {
1075
+ try {
1076
+ if (this.#customQueryTransformer) {
1077
+ const validation = await this.#customQueryTransformer.validate(ctx);
1078
+ if (validation !== void 0) throw new ProtocolErrorWithLevel(validation, "warn");
1079
+ }
1080
+ this.contextManager.validateConnection(ctx, ctx.revision);
1081
+ return true;
1082
+ } catch (e) {
1083
+ if (isProtocolError(e) && isAuthErrorBody(e.errorBody)) {
1084
+ this.#failMaintenanceConnection(ctx, e);
1085
+ return false;
1086
+ }
1087
+ throw e;
1088
+ }
1089
+ }
1090
+ #failMaintenanceConnection(ctx, error) {
1091
+ if (!this.contextManager.failConnection(ctx, ctx.revision)) return;
1092
+ const wrapped = wrapWithProtocolError(error);
1093
+ const client = this.#clients.get(ctx.clientID);
1094
+ if (client?.wsID === ctx.wsID) client.fail(wrapped);
1095
+ }
983
1096
  stop() {
984
1097
  this.#lc.info?.("stopping view syncer");
985
1098
  this.#initialized.reject("shut down before initialization completed");
@@ -987,9 +1100,9 @@ var ViewSyncerService = class {
987
1100
  return this.#stopped.promise;
988
1101
  }
989
1102
  async #cleanup(err) {
990
- this.clearAuth();
991
1103
  this.#stopTTLClockInterval();
992
1104
  this.#stopExpireTimer();
1105
+ this.#stopAuthMaintenanceTimer();
993
1106
  for (const client of this.#clients.values()) if (err) client.fail(err);
994
1107
  else client.close(`closed clientGroupID=${this.id}`);
995
1108
  await this.#lock.withLock(() => {});
@@ -1025,9 +1138,8 @@ function checkClientAndCVRVersions(client, cvr) {
1025
1138
  origin: ZeroCache
1026
1139
  });
1027
1140
  }
1028
- function isTransformAuthFailure(error) {
1029
- if (!isProtocolError(error)) return false;
1030
- return error.errorBody.kind === "TransformFailed" && error.errorBody.reason === "http" && (error.errorBody.status === 401 || error.errorBody.status === 403);
1141
+ function isTransformFailedError(error) {
1142
+ return error.errorBody.kind === "TransformFailed" && !isAuthErrorBody(error.errorBody);
1031
1143
  }
1032
1144
  /**
1033
1145
  * A query must be expired for all clients in order to be considered