@rocicorp/zero 1.11.0-head-86ca5eff-20261014 → 1.11.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 (750) hide show
  1. package/out/_virtual/{_@oxc-project_runtime@0.130.0/helpers → _@oxc-project_runtime@0.154.0/helpers/esm}/usingCtx.js +2 -2
  2. package/out/analyze-query/src/analyze-cli.js +2 -1
  3. package/out/analyze-query/src/analyze-cli.js.map +1 -1
  4. package/out/analyze-query/src/bin-analyze.js +5 -3
  5. package/out/analyze-query/src/bin-analyze.js.map +1 -1
  6. package/out/analyze-query/src/bin-transform.js +3 -2
  7. package/out/analyze-query/src/bin-transform.js.map +1 -1
  8. package/out/ast-to-zql/src/ast-to-zql.js.map +1 -1
  9. package/out/ast-to-zql/src/bin.js +3 -2
  10. package/out/ast-to-zql/src/bin.js.map +1 -1
  11. package/out/ast-to-zql/src/format.js.map +1 -1
  12. package/out/datadog/src/datadog-log-sink.js +5 -4
  13. package/out/datadog/src/datadog-log-sink.js.map +1 -1
  14. package/out/otel/src/log-options.js.map +1 -1
  15. package/out/replicache/src/btree/node.js +13 -11
  16. package/out/replicache/src/btree/node.js.map +1 -1
  17. package/out/replicache/src/btree/read.js +4 -2
  18. package/out/replicache/src/btree/read.js.map +1 -1
  19. package/out/replicache/src/btree/splice.js.map +1 -1
  20. package/out/replicache/src/btree/write.js +9 -5
  21. package/out/replicache/src/btree/write.js.map +1 -1
  22. package/out/replicache/src/config.js +1 -2
  23. package/out/replicache/src/connection-loop.js.map +1 -1
  24. package/out/replicache/src/dag/chunk.js +1 -1
  25. package/out/replicache/src/dag/gc.js.map +1 -1
  26. package/out/replicache/src/dag/lazy-store.js +8 -7
  27. package/out/replicache/src/dag/lazy-store.js.map +1 -1
  28. package/out/replicache/src/dag/store-impl.js +1 -1
  29. package/out/replicache/src/dag/store-impl.js.map +1 -1
  30. package/out/replicache/src/dag/visitor.js.map +1 -1
  31. package/out/replicache/src/db/commit.js +5 -3
  32. package/out/replicache/src/db/commit.js.map +1 -1
  33. package/out/replicache/src/db/read.js +1 -1
  34. package/out/replicache/src/db/read.js.map +1 -1
  35. package/out/replicache/src/db/rebase.js.map +1 -1
  36. package/out/replicache/src/db/write.js +5 -2
  37. package/out/replicache/src/db/write.js.map +1 -1
  38. package/out/replicache/src/deleted-clients.js +3 -2
  39. package/out/replicache/src/deleted-clients.js.map +1 -1
  40. package/out/replicache/src/expo-sqlite.js +1 -2
  41. package/out/replicache/src/frozen-json.js +1 -1
  42. package/out/replicache/src/frozen-json.js.map +1 -1
  43. package/out/replicache/src/get-default-puller.js +1 -1
  44. package/out/replicache/src/hash.js +1 -1
  45. package/out/replicache/src/hash.js.map +1 -1
  46. package/out/replicache/src/impl.js +1 -2
  47. package/out/replicache/src/index-defs.js +5 -3
  48. package/out/replicache/src/index-defs.js.map +1 -1
  49. package/out/replicache/src/kv/expo-sqlite/store.js +1 -1
  50. package/out/replicache/src/kv/expo-sqlite/store.js.map +1 -1
  51. package/out/replicache/src/kv/idb-store.js +1 -1
  52. package/out/replicache/src/kv/idb-store.js.map +1 -1
  53. package/out/replicache/src/kv/mem-fallback-store.js +1 -1
  54. package/out/replicache/src/kv/mem-fallback-store.js.map +1 -1
  55. package/out/replicache/src/kv/mem-store.js.map +1 -1
  56. package/out/replicache/src/kv/op-sqlite/store.js.map +1 -1
  57. package/out/replicache/src/kv/op-sqlite/types.js.map +1 -1
  58. package/out/replicache/src/kv/read-impl.js.map +1 -1
  59. package/out/replicache/src/kv/sqlite-store.js +1 -1
  60. package/out/replicache/src/kv/sqlite-store.js.map +1 -1
  61. package/out/replicache/src/kv/write-impl-base.js +4 -1
  62. package/out/replicache/src/kv/write-impl-base.js.map +1 -1
  63. package/out/replicache/src/kv/write-impl.js.map +1 -1
  64. package/out/replicache/src/log-options.js +2 -1
  65. package/out/replicache/src/log-options.js.map +1 -1
  66. package/out/replicache/src/make-idb-name.js +1 -1
  67. package/out/replicache/src/new-client-channel.js +9 -7
  68. package/out/replicache/src/new-client-channel.js.map +1 -1
  69. package/out/replicache/src/on-persist-channel.js +1 -1
  70. package/out/replicache/src/on-persist-channel.js.map +1 -1
  71. package/out/replicache/src/op-sqlite.js +1 -2
  72. package/out/replicache/src/pending-mutations.js +2 -1
  73. package/out/replicache/src/pending-mutations.js.map +1 -1
  74. package/out/replicache/src/persist/client-gc.js +2 -2
  75. package/out/replicache/src/persist/client-gc.js.map +1 -1
  76. package/out/replicache/src/persist/client-group-gc.js +2 -2
  77. package/out/replicache/src/persist/client-group-gc.js.map +1 -1
  78. package/out/replicache/src/persist/client-groups.js +1 -1
  79. package/out/replicache/src/persist/client-groups.js.map +1 -1
  80. package/out/replicache/src/persist/clients.js +1 -1
  81. package/out/replicache/src/persist/clients.js.map +1 -1
  82. package/out/replicache/src/persist/collect-idb-databases.js +5 -4
  83. package/out/replicache/src/persist/collect-idb-databases.js.map +1 -1
  84. package/out/replicache/src/persist/gather-mem-only-visitor.js.map +1 -1
  85. package/out/replicache/src/persist/gather-not-cached-visitor.js.map +1 -1
  86. package/out/replicache/src/persist/heartbeat.js +2 -2
  87. package/out/replicache/src/persist/heartbeat.js.map +1 -1
  88. package/out/replicache/src/persist/idb-databases-store.js +1 -1
  89. package/out/replicache/src/persist/idb-databases-store.js.map +1 -1
  90. package/out/replicache/src/persist/make-client-id.js +1 -1
  91. package/out/replicache/src/persist/make-client-id.js.map +1 -1
  92. package/out/replicache/src/persist/persist.js +3 -2
  93. package/out/replicache/src/persist/persist.js.map +1 -1
  94. package/out/replicache/src/persist/refresh.js +8 -5
  95. package/out/replicache/src/persist/refresh.js.map +1 -1
  96. package/out/replicache/src/process-scheduler.js.map +1 -1
  97. package/out/replicache/src/replicache-impl.js +3 -3
  98. package/out/replicache/src/replicache-impl.js.map +1 -1
  99. package/out/replicache/src/scan-iterator.js.map +1 -1
  100. package/out/replicache/src/scan-options.js +7 -6
  101. package/out/replicache/src/scan-options.js.map +1 -1
  102. package/out/replicache/src/sqlite.js +1 -2
  103. package/out/replicache/src/subscriptions.js +1 -1
  104. package/out/replicache/src/subscriptions.js.map +1 -1
  105. package/out/replicache/src/sync/diff.js +3 -1
  106. package/out/replicache/src/sync/diff.js.map +1 -1
  107. package/out/replicache/src/sync/ids.js.map +1 -1
  108. package/out/replicache/src/sync/patch.js +2 -4
  109. package/out/replicache/src/sync/patch.js.map +1 -1
  110. package/out/replicache/src/sync/pull.js +3 -1
  111. package/out/replicache/src/sync/pull.js.map +1 -1
  112. package/out/replicache/src/sync/push.js.map +1 -1
  113. package/out/replicache/src/sync/request-id.js +1 -1
  114. package/out/replicache/src/sync/request-id.js.map +1 -1
  115. package/out/replicache/src/transactions.js +1 -1
  116. package/out/replicache/src/transactions.js.map +1 -1
  117. package/out/shared/src/arrays.js +4 -1
  118. package/out/shared/src/arrays.js.map +1 -1
  119. package/out/shared/src/asserts.js +1 -1
  120. package/out/shared/src/bigint-json.js.map +1 -1
  121. package/out/shared/src/broadcast-channel.js +1 -1
  122. package/out/shared/src/btree-set.js +11 -8
  123. package/out/shared/src/btree-set.js.map +1 -1
  124. package/out/shared/src/cache.js.map +1 -1
  125. package/out/shared/src/config.js +1 -1
  126. package/out/shared/src/custom-key-map.js.map +1 -1
  127. package/out/shared/src/custom-key-set.js.map +1 -1
  128. package/out/shared/src/deep-clone.js +1 -1
  129. package/out/shared/src/document-visible.js.map +1 -1
  130. package/out/shared/src/dotenv.js.map +1 -1
  131. package/out/shared/src/error.js +1 -1
  132. package/out/shared/src/error.js.map +1 -1
  133. package/out/shared/src/hash.js +2 -2
  134. package/out/shared/src/hash.js.map +1 -1
  135. package/out/shared/src/iterables.js.map +1 -1
  136. package/out/shared/src/json-schema.js.map +1 -1
  137. package/out/shared/src/json.js.map +1 -1
  138. package/out/shared/src/logging-test-utils.js +1 -1
  139. package/out/shared/src/logging.js +1 -1
  140. package/out/shared/src/logging.js.map +1 -1
  141. package/out/shared/src/navigator.js +1 -1
  142. package/out/shared/src/options.js +9 -12
  143. package/out/shared/src/options.js.map +1 -1
  144. package/out/shared/src/promise-race.js.map +1 -1
  145. package/out/shared/src/queue.js.map +1 -1
  146. package/out/shared/src/record-proxy.js.map +1 -1
  147. package/out/shared/src/ring-buffer.js.map +1 -1
  148. package/out/shared/src/size-of-value.js +7 -10
  149. package/out/shared/src/size-of-value.js.map +1 -1
  150. package/out/shared/src/sorted-entries.js.map +1 -1
  151. package/out/shared/src/tdigest-schema.js.map +1 -1
  152. package/out/shared/src/tdigest.js.map +1 -1
  153. package/out/shared/src/valita.js +1 -1
  154. package/out/shared/src/valita.js.map +1 -1
  155. package/out/z2s/src/compiler.js +2 -4
  156. package/out/z2s/src/compiler.js.map +1 -1
  157. package/out/z2s/src/sql.js +1 -1
  158. package/out/z2s/src/sql.js.map +1 -1
  159. package/out/zero/package.js +212 -189
  160. package/out/zero/package.js.map +1 -1
  161. package/out/zero/src/adapters/drizzle.js +1 -2
  162. package/out/zero/src/adapters/kysely.js +1 -2
  163. package/out/zero/src/adapters/pg.js +1 -2
  164. package/out/zero/src/adapters/postgresjs.js +1 -2
  165. package/out/zero/src/adapters/prisma.js +1 -2
  166. package/out/zero/src/analyze.js +1 -2
  167. package/out/zero/src/change-protocol/v0.js +1 -2
  168. package/out/zero/src/expo-sqlite.js +1 -2
  169. package/out/zero/src/op-sqlite.js +1 -2
  170. package/out/zero/src/react-native.js.map +1 -1
  171. package/out/zero/src/sqlite.js +1 -2
  172. package/out/zero/src/zero-cache-dev.js +10 -9
  173. package/out/zero/src/zero-cache-dev.js.map +1 -1
  174. package/out/zero/src/zero-out.js +2 -1
  175. package/out/zero/src/zero-out.js.map +1 -1
  176. package/out/zero-cache/src/auth/auth.js +1 -1
  177. package/out/zero-cache/src/auth/auth.js.map +1 -1
  178. package/out/zero-cache/src/auth/jwt.js +4 -1
  179. package/out/zero-cache/src/auth/jwt.js.map +1 -1
  180. package/out/zero-cache/src/auth/load-permissions.js.map +1 -1
  181. package/out/zero-cache/src/auth/read-authorizer.js +3 -2
  182. package/out/zero-cache/src/auth/read-authorizer.js.map +1 -1
  183. package/out/zero-cache/src/auth/write-authorizer.js +5 -16
  184. package/out/zero-cache/src/auth/write-authorizer.js.map +1 -1
  185. package/out/zero-cache/src/config/network.js +1 -1
  186. package/out/zero-cache/src/config/network.js.map +1 -1
  187. package/out/zero-cache/src/config/server-context.js.map +1 -1
  188. package/out/zero-cache/src/config/zero-config.js +2 -2
  189. package/out/zero-cache/src/config/zero-config.js.map +1 -1
  190. package/out/zero-cache/src/custom/fetch.js +1 -1
  191. package/out/zero-cache/src/custom/fetch.js.map +1 -1
  192. package/out/zero-cache/src/custom-queries/transform-query.js.map +1 -1
  193. package/out/zero-cache/src/db/create.js.map +1 -1
  194. package/out/zero-cache/src/db/index-predicate.js.map +1 -1
  195. package/out/zero-cache/src/db/lite-tables.js.map +1 -1
  196. package/out/zero-cache/src/db/migration-lite.js +1 -1
  197. package/out/zero-cache/src/db/migration-lite.js.map +1 -1
  198. package/out/zero-cache/src/db/migration.js +5 -3
  199. package/out/zero-cache/src/db/migration.js.map +1 -1
  200. package/out/zero-cache/src/db/pg-copy-binary.js +4 -3
  201. package/out/zero-cache/src/db/pg-copy-binary.js.map +1 -1
  202. package/out/zero-cache/src/db/pg-copy.js +0 -1
  203. package/out/zero-cache/src/db/pg-copy.js.map +1 -1
  204. package/out/zero-cache/src/db/pg-to-lite.js +1 -1
  205. package/out/zero-cache/src/db/pg-to-lite.js.map +1 -1
  206. package/out/zero-cache/src/db/run-transaction.js.map +1 -1
  207. package/out/zero-cache/src/db/specs.js +7 -5
  208. package/out/zero-cache/src/db/specs.js.map +1 -1
  209. package/out/zero-cache/src/db/sqlite-corruption.js.map +1 -1
  210. package/out/zero-cache/src/db/transaction-pool.js +5 -3
  211. package/out/zero-cache/src/db/transaction-pool.js.map +1 -1
  212. package/out/zero-cache/src/db/warmup.js.map +1 -1
  213. package/out/zero-cache/src/observability/events.js.map +1 -1
  214. package/out/zero-cache/src/observability/log-throttle.js.map +1 -1
  215. package/out/zero-cache/src/observability/metrics.js.map +1 -1
  216. package/out/zero-cache/src/scripts/deploy-permissions.js +4 -2
  217. package/out/zero-cache/src/scripts/deploy-permissions.js.map +1 -1
  218. package/out/zero-cache/src/scripts/permissions.js.map +1 -1
  219. package/out/zero-cache/src/server/anonymous-otel-start.js +2 -1
  220. package/out/zero-cache/src/server/anonymous-otel-start.js.map +1 -1
  221. package/out/zero-cache/src/server/change-streamer.js.map +1 -1
  222. package/out/zero-cache/src/server/inspector-delegate.js.map +1 -1
  223. package/out/zero-cache/src/server/logging.js +1 -1
  224. package/out/zero-cache/src/server/logging.js.map +1 -1
  225. package/out/zero-cache/src/server/main.js.map +1 -1
  226. package/out/zero-cache/src/server/otel-log-sink.js.map +1 -1
  227. package/out/zero-cache/src/server/otel-start.js.map +1 -1
  228. package/out/zero-cache/src/server/reaper.js +2 -2
  229. package/out/zero-cache/src/server/reaper.js.map +1 -1
  230. package/out/zero-cache/src/server/replicator.js.map +1 -1
  231. package/out/zero-cache/src/server/runner/run-worker.js +2 -1
  232. package/out/zero-cache/src/server/runner/run-worker.js.map +1 -1
  233. package/out/zero-cache/src/server/runner/runtime.js +2 -1
  234. package/out/zero-cache/src/server/runner/runtime.js.map +1 -1
  235. package/out/zero-cache/src/server/runner/zero-dispatcher.js.map +1 -1
  236. package/out/zero-cache/src/server/shadow-syncer.js +1 -1
  237. package/out/zero-cache/src/server/shadow-syncer.js.map +1 -1
  238. package/out/zero-cache/src/server/slot-keeper.js.map +1 -1
  239. package/out/zero-cache/src/server/syncer-assigner.js.map +1 -1
  240. package/out/zero-cache/src/server/syncer.js.map +1 -1
  241. package/out/zero-cache/src/server/worker-dispatcher.js +1 -1
  242. package/out/zero-cache/src/server/worker-dispatcher.js.map +1 -1
  243. package/out/zero-cache/src/server/worker-urls.js.map +1 -1
  244. package/out/zero-cache/src/services/analyze.js +1 -1
  245. package/out/zero-cache/src/services/analyze.js.map +1 -1
  246. package/out/zero-cache/src/services/change-source/common/backfill-manager.js +1 -1
  247. package/out/zero-cache/src/services/change-source/common/backfill-manager.js.map +1 -1
  248. package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.js.map +1 -1
  249. package/out/zero-cache/src/services/change-source/common/init-cleanup.js.map +1 -1
  250. package/out/zero-cache/src/services/change-source/common/replica-restore.js.map +1 -1
  251. package/out/zero-cache/src/services/change-source/common/replica-schema.js +3 -3
  252. package/out/zero-cache/src/services/change-source/common/replica-schema.js.map +1 -1
  253. package/out/zero-cache/src/services/change-source/custom/change-source.js +1 -1
  254. package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
  255. package/out/zero-cache/src/services/change-source/pg/backfill-metadata.js.map +1 -1
  256. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js +3 -3
  257. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js.map +1 -1
  258. package/out/zero-cache/src/services/change-source/pg/change-source-init.js +1 -1
  259. package/out/zero-cache/src/services/change-source/pg/change-source-init.js.map +1 -1
  260. package/out/zero-cache/src/services/change-source/pg/change-source.js +4 -6
  261. package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
  262. package/out/zero-cache/src/services/change-source/pg/decommission.js +2 -1
  263. package/out/zero-cache/src/services/change-source/pg/decommission.js.map +1 -1
  264. package/out/zero-cache/src/services/change-source/pg/initial-sync.js +5 -3
  265. package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
  266. package/out/zero-cache/src/services/change-source/pg/logical-replication/binary-reader.js.map +1 -1
  267. package/out/zero-cache/src/services/change-source/pg/logical-replication/pgoutput-parser.js.map +1 -1
  268. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js +3 -2
  269. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js.map +1 -1
  270. package/out/zero-cache/src/services/change-source/pg/lsn.js.map +1 -1
  271. package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.js +2 -2
  272. package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.js.map +1 -1
  273. package/out/zero-cache/src/services/change-source/pg/replication-slot-health.js +1 -1
  274. package/out/zero-cache/src/services/change-source/pg/replication-slot-health.js.map +1 -1
  275. package/out/zero-cache/src/services/change-source/pg/replication-slots.js +3 -3
  276. package/out/zero-cache/src/services/change-source/pg/replication-slots.js.map +1 -1
  277. package/out/zero-cache/src/services/change-source/pg/schema/ddl.js +1 -1
  278. package/out/zero-cache/src/services/change-source/pg/schema/ddl.js.map +1 -1
  279. package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.js +13 -12
  280. package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.js.map +1 -1
  281. package/out/zero-cache/src/services/change-source/pg/schema/init.js.map +1 -1
  282. package/out/zero-cache/src/services/change-source/pg/schema/published.js +1 -1
  283. package/out/zero-cache/src/services/change-source/pg/schema/published.js.map +1 -1
  284. package/out/zero-cache/src/services/change-source/pg/schema/replica-stage-enum.js.map +1 -1
  285. package/out/zero-cache/src/services/change-source/pg/schema/shard.js +9 -6
  286. package/out/zero-cache/src/services/change-source/pg/schema/shard.js.map +1 -1
  287. package/out/zero-cache/src/services/change-source/pg/schema/validation.js +1 -1
  288. package/out/zero-cache/src/services/change-source/pg/schema/validation.js.map +1 -1
  289. package/out/zero-cache/src/services/change-source/protocol/backfill-progress.js +1 -1
  290. package/out/zero-cache/src/services/change-source/protocol/backfill-progress.js.map +1 -1
  291. package/out/zero-cache/src/services/change-source/protocol/current/control.js.map +1 -1
  292. package/out/zero-cache/src/services/change-source/protocol/current/data.js.map +1 -1
  293. package/out/zero-cache/src/services/change-source/protocol/current/downstream.js.map +1 -1
  294. package/out/zero-cache/src/services/change-source/protocol/current/status.js.map +1 -1
  295. package/out/zero-cache/src/services/change-source/protocol/current/upstream.js.map +1 -1
  296. package/out/zero-cache/src/services/change-source/protocol/current.js +1 -1
  297. package/out/zero-cache/src/services/change-source/protocol/mod.js +1 -2
  298. package/out/zero-cache/src/services/change-streamer/backfill-state-tracker.js.map +1 -1
  299. package/out/zero-cache/src/services/change-streamer/backfill-state.js +1 -1
  300. package/out/zero-cache/src/services/change-streamer/backfill-state.js.map +1 -1
  301. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.js.map +1 -1
  302. package/out/zero-cache/src/services/change-streamer/backup-monitor.js.map +1 -1
  303. package/out/zero-cache/src/services/change-streamer/broadcast.js +1 -1
  304. package/out/zero-cache/src/services/change-streamer/broadcast.js.map +1 -1
  305. package/out/zero-cache/src/services/change-streamer/change-log-initializer.js +2 -2
  306. package/out/zero-cache/src/services/change-streamer/change-log-initializer.js.map +1 -1
  307. package/out/zero-cache/src/services/change-streamer/change-log-transaction-hash.js.map +1 -1
  308. package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts +3 -1
  309. package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts.map +1 -1
  310. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +8 -3
  311. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
  312. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +23 -23
  313. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
  314. package/out/zero-cache/src/services/change-streamer/change-streamer.js +1 -1
  315. package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
  316. package/out/zero-cache/src/services/change-streamer/drain-tracker.d.ts +4 -4
  317. package/out/zero-cache/src/services/change-streamer/drain-tracker.d.ts.map +1 -1
  318. package/out/zero-cache/src/services/change-streamer/drain-tracker.js +4 -8
  319. package/out/zero-cache/src/services/change-streamer/drain-tracker.js.map +1 -1
  320. package/out/zero-cache/src/services/change-streamer/forwarder.js +11 -10
  321. package/out/zero-cache/src/services/change-streamer/forwarder.js.map +1 -1
  322. package/out/zero-cache/src/services/change-streamer/litestream-sync-requester.js +1 -1
  323. package/out/zero-cache/src/services/change-streamer/litestream-sync-requester.js.map +1 -1
  324. package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.js +3 -3
  325. package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.js.map +1 -1
  326. package/out/zero-cache/src/services/change-streamer/replica-poller.js +1 -1
  327. package/out/zero-cache/src/services/change-streamer/replica-poller.js.map +1 -1
  328. package/out/zero-cache/src/services/change-streamer/restore-admission.js +12 -1
  329. package/out/zero-cache/src/services/change-streamer/restore-admission.js.map +1 -1
  330. package/out/zero-cache/src/services/change-streamer/schema/init.js +1 -1
  331. package/out/zero-cache/src/services/change-streamer/schema/init.js.map +1 -1
  332. package/out/zero-cache/src/services/change-streamer/schema/tables.js +1 -1
  333. package/out/zero-cache/src/services/change-streamer/schema/tables.js.map +1 -1
  334. package/out/zero-cache/src/services/change-streamer/snapshot-message.js +1 -1
  335. package/out/zero-cache/src/services/change-streamer/snapshot-message.js.map +1 -1
  336. package/out/zero-cache/src/services/change-streamer/snapshot-reservations.js.map +1 -1
  337. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.js +1 -1
  338. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.js.map +1 -1
  339. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-comparator.js +1 -1
  340. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-comparator.js.map +1 -1
  341. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-purge-scheduler.js.map +1 -1
  342. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-read-router.js.map +1 -1
  343. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.js +1 -1
  344. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.js.map +1 -1
  345. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.js +1 -3
  346. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.js.map +1 -1
  347. package/out/zero-cache/src/services/change-streamer/storer.js +1 -1
  348. package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
  349. package/out/zero-cache/src/services/change-streamer/subscribe.js +1 -1
  350. package/out/zero-cache/src/services/change-streamer/subscribe.js.map +1 -1
  351. package/out/zero-cache/src/services/change-streamer/subscriber.js +2 -2
  352. package/out/zero-cache/src/services/change-streamer/subscriber.js.map +1 -1
  353. package/out/zero-cache/src/services/change-streamer/upstream-acker.js +0 -1
  354. package/out/zero-cache/src/services/change-streamer/upstream-acker.js.map +1 -1
  355. package/out/zero-cache/src/services/heapz.js +2 -1
  356. package/out/zero-cache/src/services/heapz.js.map +1 -1
  357. package/out/zero-cache/src/services/http-service.d.ts +6 -0
  358. package/out/zero-cache/src/services/http-service.d.ts.map +1 -1
  359. package/out/zero-cache/src/services/http-service.js +11 -0
  360. package/out/zero-cache/src/services/http-service.js.map +1 -1
  361. package/out/zero-cache/src/services/life-cycle.d.ts +3 -2
  362. package/out/zero-cache/src/services/life-cycle.d.ts.map +1 -1
  363. package/out/zero-cache/src/services/life-cycle.js +18 -5
  364. package/out/zero-cache/src/services/life-cycle.js.map +1 -1
  365. package/out/zero-cache/src/services/limiter/sliding-window-limiter.js +4 -2
  366. package/out/zero-cache/src/services/limiter/sliding-window-limiter.js.map +1 -1
  367. package/out/zero-cache/src/services/litestream/commands.js +1 -1
  368. package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
  369. package/out/zero-cache/src/services/litestream/litestream-checkpointer.js +2 -2
  370. package/out/zero-cache/src/services/litestream/litestream-checkpointer.js.map +1 -1
  371. package/out/zero-cache/src/services/litestream/litestream-controller.js.map +1 -1
  372. package/out/zero-cache/src/services/litestream/metrics.js.map +1 -1
  373. package/out/zero-cache/src/services/litestream/restore-progress.js.map +1 -1
  374. package/out/zero-cache/src/services/mutagen/mutagen.js +1 -1
  375. package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
  376. package/out/zero-cache/src/services/mutagen/pusher.js +3 -2
  377. package/out/zero-cache/src/services/mutagen/pusher.js.map +1 -1
  378. package/out/zero-cache/src/services/plannerz.js +4 -3
  379. package/out/zero-cache/src/services/plannerz.js.map +1 -1
  380. package/out/zero-cache/src/services/profz.js +5 -3
  381. package/out/zero-cache/src/services/profz.js.map +1 -1
  382. package/out/zero-cache/src/services/replicator/change-log-cookies.js.map +1 -1
  383. package/out/zero-cache/src/services/replicator/change-log-db.js +3 -2
  384. package/out/zero-cache/src/services/replicator/change-log-db.js.map +1 -1
  385. package/out/zero-cache/src/services/replicator/change-log-stream-writer.js +1 -1
  386. package/out/zero-cache/src/services/replicator/change-log-stream-writer.js.map +1 -1
  387. package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
  388. package/out/zero-cache/src/services/replicator/drain-handler.d.ts +8 -0
  389. package/out/zero-cache/src/services/replicator/drain-handler.d.ts.map +1 -1
  390. package/out/zero-cache/src/services/replicator/drain-handler.js +38 -4
  391. package/out/zero-cache/src/services/replicator/drain-handler.js.map +1 -1
  392. package/out/zero-cache/src/services/replicator/incremental-sync.js +1 -1
  393. package/out/zero-cache/src/services/replicator/incremental-sync.js.map +1 -1
  394. package/out/zero-cache/src/services/replicator/notifier.js.map +1 -1
  395. package/out/zero-cache/src/services/replicator/replication-status.js +4 -3
  396. package/out/zero-cache/src/services/replicator/replication-status.js.map +1 -1
  397. package/out/zero-cache/src/services/replicator/replicator.js.map +1 -1
  398. package/out/zero-cache/src/services/replicator/reporter/recorder.js +1 -1
  399. package/out/zero-cache/src/services/replicator/reporter/recorder.js.map +1 -1
  400. package/out/zero-cache/src/services/replicator/reporter/report-schema.js +1 -1
  401. package/out/zero-cache/src/services/replicator/reporter/report-schema.js.map +1 -1
  402. package/out/zero-cache/src/services/replicator/schema/backfilling.js +1 -1
  403. package/out/zero-cache/src/services/replicator/schema/backfilling.js.map +1 -1
  404. package/out/zero-cache/src/services/replicator/schema/change-log.js.map +1 -1
  405. package/out/zero-cache/src/services/replicator/schema/column-metadata.js +1 -1
  406. package/out/zero-cache/src/services/replicator/schema/column-metadata.js.map +1 -1
  407. package/out/zero-cache/src/services/replicator/schema/replication-state.js +10 -7
  408. package/out/zero-cache/src/services/replicator/schema/replication-state.js.map +1 -1
  409. package/out/zero-cache/src/services/replicator/schema/table-metadata.js.map +1 -1
  410. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js +1 -1
  411. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js.map +1 -1
  412. package/out/zero-cache/src/services/replicator/sqlite-change-log-purger.js +1 -1
  413. package/out/zero-cache/src/services/replicator/sqlite-change-log-purger.js.map +1 -1
  414. package/out/zero-cache/src/services/replicator/write-worker-client.js +1 -1
  415. package/out/zero-cache/src/services/replicator/write-worker-client.js.map +1 -1
  416. package/out/zero-cache/src/services/replicator/write-worker.js.map +1 -1
  417. package/out/zero-cache/src/services/run-ast.js.map +1 -1
  418. package/out/zero-cache/src/services/runner.js.map +1 -1
  419. package/out/zero-cache/src/services/running-state.js.map +1 -1
  420. package/out/zero-cache/src/services/shadow-sync/shadow-sync-service.js.map +1 -1
  421. package/out/zero-cache/src/services/statz.js +2 -1
  422. package/out/zero-cache/src/services/statz.js.map +1 -1
  423. package/out/zero-cache/src/services/view-syncer/active-users-gauge.js +2 -2
  424. package/out/zero-cache/src/services/view-syncer/active-users-gauge.js.map +1 -1
  425. package/out/zero-cache/src/services/view-syncer/advancement-metrics.js.map +1 -1
  426. package/out/zero-cache/src/services/view-syncer/batch-pacer.js.map +1 -1
  427. package/out/zero-cache/src/services/view-syncer/client-handler.js +4 -2
  428. package/out/zero-cache/src/services/view-syncer/client-handler.js.map +1 -1
  429. package/out/zero-cache/src/services/view-syncer/client-schema.js.map +1 -1
  430. package/out/zero-cache/src/services/view-syncer/connection-context-manager.js.map +1 -1
  431. package/out/zero-cache/src/services/view-syncer/cvr-purger.js +2 -2
  432. package/out/zero-cache/src/services/view-syncer/cvr-purger.js.map +1 -1
  433. package/out/zero-cache/src/services/view-syncer/cvr-store.js +9 -5
  434. package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
  435. package/out/zero-cache/src/services/view-syncer/cvr.js +3 -2
  436. package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
  437. package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.js +1 -1
  438. package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.js.map +1 -1
  439. package/out/zero-cache/src/services/view-syncer/drain-coordinator.js.map +1 -1
  440. package/out/zero-cache/src/services/view-syncer/e2e-serving-lag.js.map +1 -1
  441. package/out/zero-cache/src/services/view-syncer/hydration-budget.js.map +1 -1
  442. package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.js +2 -2
  443. package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.js.map +1 -1
  444. package/out/zero-cache/src/services/view-syncer/inspect-handler.js +5 -3
  445. package/out/zero-cache/src/services/view-syncer/inspect-handler.js.map +1 -1
  446. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +6 -4
  447. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
  448. package/out/zero-cache/src/services/view-syncer/plan-warnings.js.map +1 -1
  449. package/out/zero-cache/src/services/view-syncer/push-cost.js.map +1 -1
  450. package/out/zero-cache/src/services/view-syncer/push-cutoff.js.map +1 -1
  451. package/out/zero-cache/src/services/view-syncer/query-covering.js.map +1 -1
  452. package/out/zero-cache/src/services/view-syncer/query-shape.js.map +1 -1
  453. package/out/zero-cache/src/services/view-syncer/query-stats.js +1 -1
  454. package/out/zero-cache/src/services/view-syncer/query-stats.js.map +1 -1
  455. package/out/zero-cache/src/services/view-syncer/row-record-cache.js.map +1 -1
  456. package/out/zero-cache/src/services/view-syncer/schema/cvr.js +1 -1
  457. package/out/zero-cache/src/services/view-syncer/schema/types.js +1 -1
  458. package/out/zero-cache/src/services/view-syncer/schema/types.js.map +1 -1
  459. package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.js +7 -1
  460. package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.js.map +1 -1
  461. package/out/zero-cache/src/services/view-syncer/snapshotter.js +3 -2
  462. package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
  463. package/out/zero-cache/src/services/view-syncer/ttl-clock.js.map +1 -1
  464. package/out/zero-cache/src/services/view-syncer/view-syncer.js +4 -6
  465. package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
  466. package/out/zero-cache/src/types/error-with-level.js.map +1 -1
  467. package/out/zero-cache/src/types/lexi-version.js.map +1 -1
  468. package/out/zero-cache/src/types/lite.js +1 -1
  469. package/out/zero-cache/src/types/lite.js.map +1 -1
  470. package/out/zero-cache/src/types/pg-data-type.js +1 -1
  471. package/out/zero-cache/src/types/pg-data-type.js.map +1 -1
  472. package/out/zero-cache/src/types/pg.js +6 -4
  473. package/out/zero-cache/src/types/pg.js.map +1 -1
  474. package/out/zero-cache/src/types/poke-chunk.js +2 -2
  475. package/out/zero-cache/src/types/poke-chunk.js.map +1 -1
  476. package/out/zero-cache/src/types/processes.js +1 -1
  477. package/out/zero-cache/src/types/processes.js.map +1 -1
  478. package/out/zero-cache/src/types/profiler.js.map +1 -1
  479. package/out/zero-cache/src/types/row-key.js.map +1 -1
  480. package/out/zero-cache/src/types/sql.js.map +1 -1
  481. package/out/zero-cache/src/types/streams.js +10 -9
  482. package/out/zero-cache/src/types/streams.js.map +1 -1
  483. package/out/zero-cache/src/types/strings.js.map +1 -1
  484. package/out/zero-cache/src/types/subscription.js.map +1 -1
  485. package/out/zero-cache/src/types/timeout.js +1 -1
  486. package/out/zero-cache/src/types/websocket-handoff.js +1 -1
  487. package/out/zero-cache/src/types/ws.js +1 -1
  488. package/out/zero-cache/src/workers/connection.js +2 -3
  489. package/out/zero-cache/src/workers/connection.js.map +1 -1
  490. package/out/zero-cache/src/workers/mutator.js.map +1 -1
  491. package/out/zero-cache/src/workers/replicator.js +5 -4
  492. package/out/zero-cache/src/workers/replicator.js.map +1 -1
  493. package/out/zero-cache/src/workers/syncer-ws-message-handler.js.map +1 -1
  494. package/out/zero-cache/src/workers/syncer.js +2 -2
  495. package/out/zero-cache/src/workers/syncer.js.map +1 -1
  496. package/out/zero-client/src/client/active-clients-manager.js +1 -1
  497. package/out/zero-client/src/client/active-clients-manager.js.map +1 -1
  498. package/out/zero-client/src/client/bindings.js +11 -11
  499. package/out/zero-client/src/client/connection-manager.js.map +1 -1
  500. package/out/zero-client/src/client/connection.js +1 -1
  501. package/out/zero-client/src/client/connection.js.map +1 -1
  502. package/out/zero-client/src/client/context.js.map +1 -1
  503. package/out/zero-client/src/client/crud-impl.js +1 -1
  504. package/out/zero-client/src/client/crud-impl.js.map +1 -1
  505. package/out/zero-client/src/client/crud.js +1 -1
  506. package/out/zero-client/src/client/crud.js.map +1 -1
  507. package/out/zero-client/src/client/custom.js +1 -1
  508. package/out/zero-client/src/client/custom.js.map +1 -1
  509. package/out/zero-client/src/client/delete-clients-manager.js.map +1 -1
  510. package/out/zero-client/src/client/enable-analytics.js +1 -1
  511. package/out/zero-client/src/client/enable-analytics.js.map +1 -1
  512. package/out/zero-client/src/client/error.js.map +1 -1
  513. package/out/zero-client/src/client/inspector/client-group.js.map +1 -1
  514. package/out/zero-client/src/client/inspector/client.js.map +1 -1
  515. package/out/zero-client/src/client/inspector/html-dialog-prompt.js +1 -1
  516. package/out/zero-client/src/client/inspector/html-dialog-prompt.js.map +1 -1
  517. package/out/zero-client/src/client/inspector/inspector.js.map +1 -1
  518. package/out/zero-client/src/client/inspector/lazy-inspector.js.map +1 -1
  519. package/out/zero-client/src/client/inspector/query.js.map +1 -1
  520. package/out/zero-client/src/client/ivm-branch.js +1 -3
  521. package/out/zero-client/src/client/ivm-branch.js.map +1 -1
  522. package/out/zero-client/src/client/keys.js +2 -1
  523. package/out/zero-client/src/client/keys.js.map +1 -1
  524. package/out/zero-client/src/client/log-options.js.map +1 -1
  525. package/out/zero-client/src/client/make-replicache-mutators.js +5 -3
  526. package/out/zero-client/src/client/make-replicache-mutators.js.map +1 -1
  527. package/out/zero-client/src/client/metrics.js +3 -5
  528. package/out/zero-client/src/client/metrics.js.map +1 -1
  529. package/out/zero-client/src/client/mutation-tracker.js.map +1 -1
  530. package/out/zero-client/src/client/mutator-proxy.js.map +1 -1
  531. package/out/zero-client/src/client/options.js.map +1 -1
  532. package/out/zero-client/src/client/query-manager.js +2 -2
  533. package/out/zero-client/src/client/query-manager.js.map +1 -1
  534. package/out/zero-client/src/client/reload-error-handler.js +4 -3
  535. package/out/zero-client/src/client/reload-error-handler.js.map +1 -1
  536. package/out/zero-client/src/client/version.js +1 -1
  537. package/out/zero-client/src/client/zero-poke-handler.js +1 -1
  538. package/out/zero-client/src/client/zero-poke-handler.js.map +1 -1
  539. package/out/zero-client/src/client/zero-rep.js.map +1 -1
  540. package/out/zero-client/src/client/zero.js +19 -11
  541. package/out/zero-client/src/client/zero.js.map +1 -1
  542. package/out/zero-client/src/mod.js +23 -23
  543. package/out/zero-client/src/util/nanoid.js.map +1 -1
  544. package/out/zero-permissions/src/permissions.js +3 -2
  545. package/out/zero-permissions/src/permissions.js.map +1 -1
  546. package/out/zero-pg/src/mod.js +9 -9
  547. package/out/zero-protocol/src/analyze-query-result.js +1 -1
  548. package/out/zero-protocol/src/analyze-query-result.js.map +1 -1
  549. package/out/zero-protocol/src/application-error.js.map +1 -1
  550. package/out/zero-protocol/src/ast.js +19 -9
  551. package/out/zero-protocol/src/ast.js.map +1 -1
  552. package/out/zero-protocol/src/change-desired-queries.js.map +1 -1
  553. package/out/zero-protocol/src/client-schema.js +1 -1
  554. package/out/zero-protocol/src/client-schema.js.map +1 -1
  555. package/out/zero-protocol/src/close-connection.js +1 -1
  556. package/out/zero-protocol/src/close-connection.js.map +1 -1
  557. package/out/zero-protocol/src/connect.js +1 -1
  558. package/out/zero-protocol/src/connect.js.map +1 -1
  559. package/out/zero-protocol/src/custom-queries.js +1 -1
  560. package/out/zero-protocol/src/custom-queries.js.map +1 -1
  561. package/out/zero-protocol/src/data.js +4 -2
  562. package/out/zero-protocol/src/data.js.map +1 -1
  563. package/out/zero-protocol/src/delete-clients.js.map +1 -1
  564. package/out/zero-protocol/src/down.js.map +1 -1
  565. package/out/zero-protocol/src/error.js.map +1 -1
  566. package/out/zero-protocol/src/inspect-down.d.ts +81 -81
  567. package/out/zero-protocol/src/inspect-down.js +1 -1
  568. package/out/zero-protocol/src/inspect-down.js.map +1 -1
  569. package/out/zero-protocol/src/inspect-up.js +1 -1
  570. package/out/zero-protocol/src/inspect-up.js.map +1 -1
  571. package/out/zero-protocol/src/mutate-server.js +1 -1
  572. package/out/zero-protocol/src/mutate-server.js.map +1 -1
  573. package/out/zero-protocol/src/mutation-id.js.map +1 -1
  574. package/out/zero-protocol/src/mutation.js +1 -1
  575. package/out/zero-protocol/src/mutation.js.map +1 -1
  576. package/out/zero-protocol/src/mutations-patch.js +1 -1
  577. package/out/zero-protocol/src/mutations-patch.js.map +1 -1
  578. package/out/zero-protocol/src/ping.js +1 -1
  579. package/out/zero-protocol/src/ping.js.map +1 -1
  580. package/out/zero-protocol/src/poke.js +1 -1
  581. package/out/zero-protocol/src/poke.js.map +1 -1
  582. package/out/zero-protocol/src/pong.js +1 -1
  583. package/out/zero-protocol/src/pong.js.map +1 -1
  584. package/out/zero-protocol/src/primary-key.js.map +1 -1
  585. package/out/zero-protocol/src/protocol-version.js +1 -0
  586. package/out/zero-protocol/src/protocol-version.js.map +1 -1
  587. package/out/zero-protocol/src/pull.js +1 -1
  588. package/out/zero-protocol/src/pull.js.map +1 -1
  589. package/out/zero-protocol/src/push.js +1 -1
  590. package/out/zero-protocol/src/push.js.map +1 -1
  591. package/out/zero-protocol/src/queries-patch.js +1 -1
  592. package/out/zero-protocol/src/queries-patch.js.map +1 -1
  593. package/out/zero-protocol/src/query-hash-visitor.js.map +1 -1
  594. package/out/zero-protocol/src/query-hash.js +2 -2
  595. package/out/zero-protocol/src/query-server.js +1 -1
  596. package/out/zero-protocol/src/query-server.js.map +1 -1
  597. package/out/zero-protocol/src/row-patch.js.map +1 -1
  598. package/out/zero-protocol/src/up.js.map +1 -1
  599. package/out/zero-protocol/src/update-auth.js.map +1 -1
  600. package/out/zero-protocol/src/version.js.map +1 -1
  601. package/out/zero-react/src/bindings.js +11 -11
  602. package/out/zero-react/src/mod.js +5 -5
  603. package/out/zero-react/src/use-query.js +1 -1
  604. package/out/zero-react/src/use-query.js.map +1 -1
  605. package/out/zero-react/src/zero-provider.d.ts +1 -1
  606. package/out/zero-react/src/zero-provider.d.ts.map +1 -1
  607. package/out/zero-react/src/zero-provider.js +2 -1
  608. package/out/zero-react/src/zero-provider.js.map +1 -1
  609. package/out/zero-react/src/zero.js +23 -23
  610. package/out/zero-schema/src/builder/schema-builder.js +3 -2
  611. package/out/zero-schema/src/builder/schema-builder.js.map +1 -1
  612. package/out/zero-schema/src/builder/table-builder.js +1 -1
  613. package/out/zero-schema/src/builder/table-builder.js.map +1 -1
  614. package/out/zero-schema/src/compiled-permissions.js.map +1 -1
  615. package/out/zero-schema/src/name-mapper.js +7 -5
  616. package/out/zero-schema/src/name-mapper.js.map +1 -1
  617. package/out/zero-schema/src/schema-config.js +1 -1
  618. package/out/zero-schema/src/schema-config.js.map +1 -1
  619. package/out/zero-server/src/adapters/drizzle.js.map +1 -1
  620. package/out/zero-server/src/adapters/kysely.js.map +1 -1
  621. package/out/zero-server/src/adapters/pg.js.map +1 -1
  622. package/out/zero-server/src/adapters/postgresjs.js.map +1 -1
  623. package/out/zero-server/src/adapters/prisma.js.map +1 -1
  624. package/out/zero-server/src/closable-db-transaction.js.map +1 -1
  625. package/out/zero-server/src/custom.js +1 -1
  626. package/out/zero-server/src/custom.js.map +1 -1
  627. package/out/zero-server/src/mod.js +8 -8
  628. package/out/zero-server/src/process-mutations.js.map +1 -1
  629. package/out/zero-server/src/push-processor.js.map +1 -1
  630. package/out/zero-server/src/queries/process-queries.js +2 -1
  631. package/out/zero-server/src/queries/process-queries.js.map +1 -1
  632. package/out/zero-server/src/schema.js +7 -7
  633. package/out/zero-server/src/schema.js.map +1 -1
  634. package/out/zero-server/src/zql-database.js.map +1 -1
  635. package/out/zero-solid/src/bindings.js +11 -11
  636. package/out/zero-solid/src/mod.js +5 -5
  637. package/out/zero-solid/src/solid-view.js +1 -1
  638. package/out/zero-solid/src/solid-view.js.map +1 -1
  639. package/out/zero-solid/src/use-connection-state.js +2 -1
  640. package/out/zero-solid/src/use-connection-state.js.map +1 -1
  641. package/out/zero-solid/src/use-query.js.map +1 -1
  642. package/out/zero-solid/src/use-zero-online.js +2 -1
  643. package/out/zero-solid/src/use-zero-online.js.map +1 -1
  644. package/out/zero-solid/src/use-zero.js +3 -2
  645. package/out/zero-solid/src/use-zero.js.map +1 -1
  646. package/out/zero-solid/src/zero.js +23 -23
  647. package/out/zero-types/src/name-mapper.js.map +1 -1
  648. package/out/zql/src/builder/builder.js +15 -13
  649. package/out/zql/src/builder/builder.js.map +1 -1
  650. package/out/zql/src/builder/correlated-predicate-pushdown.js.map +1 -1
  651. package/out/zql/src/builder/debug-delegate.js.map +1 -1
  652. package/out/zql/src/builder/like.js +0 -1
  653. package/out/zql/src/builder/like.js.map +1 -1
  654. package/out/zql/src/ivm/array-view.js.map +1 -1
  655. package/out/zql/src/ivm/cap.js.map +1 -1
  656. package/out/zql/src/ivm/constraint.js +1 -1
  657. package/out/zql/src/ivm/constraint.js.map +1 -1
  658. package/out/zql/src/ivm/data.js.map +1 -1
  659. package/out/zql/src/ivm/deferred-input.js.map +1 -1
  660. package/out/zql/src/ivm/exists.js +20 -18
  661. package/out/zql/src/ivm/exists.js.map +1 -1
  662. package/out/zql/src/ivm/fan-in.js.map +1 -1
  663. package/out/zql/src/ivm/fan-out.js.map +1 -1
  664. package/out/zql/src/ivm/filter-operators.js +1 -1
  665. package/out/zql/src/ivm/filter-operators.js.map +1 -1
  666. package/out/zql/src/ivm/filter-push.js.map +1 -1
  667. package/out/zql/src/ivm/filter.js.map +1 -1
  668. package/out/zql/src/ivm/flipped-join.js +1 -3
  669. package/out/zql/src/ivm/flipped-join.js.map +1 -1
  670. package/out/zql/src/ivm/join-index-tap.js.map +1 -1
  671. package/out/zql/src/ivm/join-index.js.map +1 -1
  672. package/out/zql/src/ivm/join-utils.js +11 -13
  673. package/out/zql/src/ivm/join-utils.js.map +1 -1
  674. package/out/zql/src/ivm/join.js.map +1 -1
  675. package/out/zql/src/ivm/maybe-split-and-push-edit-change.js.map +1 -1
  676. package/out/zql/src/ivm/memory-source.js +26 -22
  677. package/out/zql/src/ivm/memory-source.js.map +1 -1
  678. package/out/zql/src/ivm/memory-storage.js.map +1 -1
  679. package/out/zql/src/ivm/operator.js +2 -2
  680. package/out/zql/src/ivm/push-accumulated.js +0 -1
  681. package/out/zql/src/ivm/push-accumulated.js.map +1 -1
  682. package/out/zql/src/ivm/skip-yields.js.map +1 -1
  683. package/out/zql/src/ivm/skip.js.map +1 -1
  684. package/out/zql/src/ivm/take-gate.js.map +1 -1
  685. package/out/zql/src/ivm/take.js +1 -1
  686. package/out/zql/src/ivm/take.js.map +1 -1
  687. package/out/zql/src/ivm/union-fan-in.js +1 -1
  688. package/out/zql/src/ivm/union-fan-in.js.map +1 -1
  689. package/out/zql/src/ivm/union-fan-out.js.map +1 -1
  690. package/out/zql/src/ivm/view-apply-change.js +1 -1
  691. package/out/zql/src/ivm/view-apply-change.js.map +1 -1
  692. package/out/zql/src/mutate/crud.js +3 -2
  693. package/out/zql/src/mutate/crud.js.map +1 -1
  694. package/out/zql/src/planner/planner-builder.js +1 -1
  695. package/out/zql/src/planner/planner-builder.js.map +1 -1
  696. package/out/zql/src/planner/planner-connection.js.map +1 -1
  697. package/out/zql/src/planner/planner-debug.js.map +1 -1
  698. package/out/zql/src/planner/planner-fan-in.js.map +1 -1
  699. package/out/zql/src/planner/planner-fan-out.js.map +1 -1
  700. package/out/zql/src/planner/planner-filter.js.map +1 -1
  701. package/out/zql/src/planner/planner-graph.js +1 -5
  702. package/out/zql/src/planner/planner-graph.js.map +1 -1
  703. package/out/zql/src/planner/planner-join.js +1 -1
  704. package/out/zql/src/planner/planner-join.js.map +1 -1
  705. package/out/zql/src/planner/planner-source.js.map +1 -1
  706. package/out/zql/src/planner/planner-terminus.js.map +1 -1
  707. package/out/zql/src/query/create-builder.js +2 -1
  708. package/out/zql/src/query/create-builder.js.map +1 -1
  709. package/out/zql/src/query/expression.js +1 -1
  710. package/out/zql/src/query/expression.js.map +1 -1
  711. package/out/zql/src/query/measure-push-operator.js.map +1 -1
  712. package/out/zql/src/query/named.js +2 -1
  713. package/out/zql/src/query/named.js.map +1 -1
  714. package/out/zql/src/query/query-delegate-base.js +1 -1
  715. package/out/zql/src/query/query-delegate-base.js.map +1 -1
  716. package/out/zql/src/query/query-impl.js +3 -2
  717. package/out/zql/src/query/query-impl.js.map +1 -1
  718. package/out/zql/src/query/query-registry.js +1 -1
  719. package/out/zql/src/query/query-registry.js.map +1 -1
  720. package/out/zql/src/query/query-transitions.js +1 -1
  721. package/out/zql/src/query/query-transitions.js.map +1 -1
  722. package/out/zql/src/query/runnable-query-impl.js +3 -2
  723. package/out/zql/src/query/runnable-query-impl.js.map +1 -1
  724. package/out/zql/src/query/static-query.js.map +1 -1
  725. package/out/zql/src/query/ttl.js +8 -8
  726. package/out/zql/src/query/ttl.js.map +1 -1
  727. package/out/zql/src/query/validate-input.js.map +1 -1
  728. package/out/zqlite/src/database-storage.js +2 -2
  729. package/out/zqlite/src/database-storage.js.map +1 -1
  730. package/out/zqlite/src/db.js +2 -2
  731. package/out/zqlite/src/db.js.map +1 -1
  732. package/out/zqlite/src/explain-queries.js.map +1 -1
  733. package/out/zqlite/src/index-usage.js.map +1 -1
  734. package/out/zqlite/src/internal/sql-inline.js.map +1 -1
  735. package/out/zqlite/src/internal/sql.js.map +1 -1
  736. package/out/zqlite/src/internal/statement-cache.js +1 -1
  737. package/out/zqlite/src/internal/statement-cache.js.map +1 -1
  738. package/out/zqlite/src/mod.js +5 -5
  739. package/out/zqlite/src/pending-delta.js.map +1 -1
  740. package/out/zqlite/src/query-builder.js +1 -1
  741. package/out/zqlite/src/query-builder.js.map +1 -1
  742. package/out/zqlite/src/query-delegate.js.map +1 -1
  743. package/out/zqlite/src/resolve-scalar-subqueries.js +2 -5
  744. package/out/zqlite/src/resolve-scalar-subqueries.js.map +1 -1
  745. package/out/zqlite/src/sqlite-cost-model.js +3 -2
  746. package/out/zqlite/src/sqlite-cost-model.js.map +1 -1
  747. package/out/zqlite/src/sqlite-stat-fanout.js.map +1 -1
  748. package/out/zqlite/src/table-source.js +1 -1
  749. package/out/zqlite/src/table-source.js.map +1 -1
  750. package/package.json +16 -14
@@ -1,4 +1,4 @@
1
- //#region \0@oxc-project+runtime@0.130.0/helpers/usingCtx.js
1
+ //#region \0@oxc-project+runtime@0.154.0/helpers/esm/usingCtx.js
2
2
  function _usingCtx() {
3
3
  var r = "function" == typeof SuppressedError ? SuppressedError : function(r, e) {
4
4
  var n = Error();
@@ -54,4 +54,4 @@ function _usingCtx() {
54
54
  };
55
55
  }
56
56
  //#endregion
57
- export { _usingCtx };
57
+ export { _usingCtx as default };
@@ -101,8 +101,9 @@ var stderrLogSink = { log(level, context, ...args) {
101
101
  * Exits the process with code 1 on error.
102
102
  */
103
103
  async function runAnalyzeCLI(opts) {
104
+ const argv = (opts.argv ?? process.argv.slice(2)).map((s) => s.replaceAll("\n", " "));
104
105
  const config = parseOptions(options, {
105
- argv: (opts.argv ?? process.argv.slice(2)).map((s) => s.replaceAll("\n", " ")),
106
+ argv,
106
107
  envNamePrefix: ZERO_ENV_VAR_PREFIX,
107
108
  description: [{
108
109
  header: "analyze-query (remote)",
@@ -1 +1 @@
1
- {"version":3,"file":"analyze-cli.js","names":[],"sources":["../../../../analyze-query/src/analyze-cli.ts"],"sourcesContent":["import '../../shared/src/dotenv.ts';\n\nimport {Console} from 'node:console';\nimport {styleText} from 'node:util';\nimport type {LogSink} from '@rocicorp/logger';\nimport {WebSocket as NodeWebSocket} from 'ws';\nimport {logLevel, logOptions} from '../../otel/src/log-options.ts';\nimport {colorConsole} from '../../shared/src/logging.ts';\nimport {parseOptions} from '../../shared/src/options.ts';\nimport * as v from '../../shared/src/valita.ts';\nimport {ZERO_ENV_VAR_PREFIX} from '../../zero-cache/src/config/zero-config.ts';\nimport {Zero} from '../../zero-client/src/client/zero.ts';\nimport type {AnalyzeQueryResult} from '../../zero-protocol/src/analyze-query-result.ts';\nimport type {AST} from '../../zero-protocol/src/ast.ts';\nimport type {Schema} from '../../zero-types/src/schema.ts';\nimport {formatPlannerEvents} from '../../zql/src/planner/planner-debug.ts';\nimport {createBuilder} from '../../zql/src/query/create-builder.ts';\nimport type {AnyQuery} from '../../zql/src/query/query.ts';\nimport type {SchemaQuery} from '../../zql/src/query/schema-query.ts';\n\nexport type AnalyzeCLIOptions = {\n schema: Schema;\n /** Defaults to `process.argv.slice(2)`. */\n argv?: readonly string[] | undefined;\n};\n\nconst options = {\n zeroCacheURL: {\n type: v.string().optional(),\n desc: [\n 'URL of the remote zero-cache to analyze against.',\n 'Accepts http(s):// or ws(s):// (ws(s) is the transport actually used).',\n ],\n },\n adminPassword: {\n type: v.string().optional(),\n desc: [\n 'Admin password for zero-cache.',\n 'Required when the server is configured with one; ignored in dev mode.',\n ],\n },\n authToken: {\n type: v.string().optional(),\n desc: [\n 'Raw JWT forwarded to zero-cache.',\n 'Used server-side to fill permission variables for the query.',\n ],\n },\n cookie: {\n type: v.string().optional(),\n desc: [\n 'Cookie header value sent on the WebSocket upgrade request,',\n 'e.g. `session=abc; foo=bar`. Use this when zero-cache is behind',\n 'a proxy that resolves auth via cookies. Merged with --headers-json',\n '(--cookie wins on conflict).',\n ],\n },\n headersJson: {\n type: v.string().optional(),\n desc: [\n 'JSON object of arbitrary headers to send on the WebSocket upgrade',\n 'request, e.g. `{\"x-api-key\":\"...\"}`. Escape hatch for exotic auth',\n 'schemes; prefer --auth-token or --cookie when possible.',\n ],\n },\n userId: {\n type: v.string().optional(),\n desc: [\n 'Optional userID to report to zero-cache.',\n 'Omit for anonymous analysis.',\n ],\n },\n ast: {\n type: v.string().optional(),\n desc: [\n 'JSON-encoded AST. Exactly one of --ast / --query / --query-name is required.',\n 'The AST is sent to the server verbatim — provide it in server (post-mapping) form.',\n ],\n },\n query: {\n type: v.string().optional(),\n desc: [\n 'ZQL query in chain form, e.g. `issue.related(\"comments\").limit(10)`.',\n 'Evaluated against the schema you pass to runAnalyzeCLI.',\n ],\n },\n queryName: {\n type: v.string().optional(),\n desc: [\n 'Name of a server-registered custom (named) query.',\n 'The server resolves the name + args via its registered query handler.',\n ],\n },\n queryArgs: {\n type: v.string().optional(),\n desc: [\n 'JSON-encoded array of arguments for --query-name. Defaults to `[]`.',\n ],\n },\n outputVendedRows: {\n type: v.boolean().optional(() => false),\n desc: [\n 'Include the rows read from the replica to execute the query.',\n 'Each row appears once per read.',\n ],\n },\n outputSyncedRows: {\n type: v.boolean().optional(() => false),\n desc: ['Include the rows that would be synced to the client.'],\n },\n joinPlans: {\n type: v.boolean().optional(() => false),\n desc: ['Include join planner diagnostics.'],\n },\n log: {\n ...logOptions,\n level: logLevel.optional(() => 'error'),\n },\n};\n\ntype QueryPlan =\n | {kind: 'ast'; ast: AST}\n | {kind: 'zql'; text: string}\n | {kind: 'named'; name: string; args: ReadonlyArray<unknown>};\n\n// Route all Zero client log output to stderr so stdout contains only the\n// analyze result. Shell redirection (`2>/dev/null`) can then cleanly silence\n// logs without affecting output.\nconst stderrConsole = new Console({\n stdout: process.stderr,\n stderr: process.stderr,\n});\nconst stderrLogSink: LogSink = {\n log(level, context, ...args) {\n const ctx = context\n ? Object.entries(context).map(([k, v]) =>\n v === undefined ? k : `${k}=${v}`,\n )\n : [];\n stderrConsole[level](...ctx, ...args);\n },\n};\n\n/**\n * Entry point for a user's `cli.ts`. Parses argv, connects to a remote\n * zero-cache by standing up an in-process Zero client (in-memory storage,\n * no subscriptions), calls the inspector's `analyze-query` RPC, and\n * renders the result. Intended to be called as:\n *\n * ```ts\n * import {schema} from './schema.ts';\n * import {runAnalyzeCLI} from '@rocicorp/zero/analyze';\n * await runAnalyzeCLI({schema});\n * ```\n *\n * Exits the process with code 1 on error.\n */\nexport async function runAnalyzeCLI(opts: AnalyzeCLIOptions): Promise<void> {\n const argv = (opts.argv ?? process.argv.slice(2)).map(s =>\n s.replaceAll('\\n', ' '),\n );\n\n const config = parseOptions(options, {\n argv,\n envNamePrefix: ZERO_ENV_VAR_PREFIX,\n description: [\n {\n header: 'analyze-query (remote)',\n content: `Analyze a ZQL query against a remote zero-cache.\n\n Connects to zero-cache's inspector protocol and reports the server-observed\n row scans, SQLite query plans, and timings.`,\n },\n {\n header: 'Examples',\n content: ` node cli.ts --zero-cache-url=https://zero.example.com \\\\\n --admin-password=\"$ZERO_ADMIN_PASSWORD\" \\\\\n --query='issue.related(\"comments\").limit(10)'\n\n node cli.ts --zero-cache-url=http://localhost:4848 \\\\\n --ast='\\\\{\"table\": \"issue\", \"limit\": 5\\\\}'\n\n node cli.ts --zero-cache-url=http://localhost:4848 \\\\\n --query-name=issueList --query-args='[]'`,\n },\n ],\n });\n\n if (!config.zeroCacheURL) {\n colorConsole.error('--zero-cache-url is required. See --help for usage.');\n process.exit(1);\n }\n\n const plan = buildQueryPlan(config);\n\n const handshakeHeaders = resolveHandshakeHeaders(config);\n if (Object.keys(handshakeHeaders).length > 0) {\n installWebSocketHeaderShim(handshakeHeaders);\n }\n\n // zero-client and replicache reference a build-time `TESTING` global that\n // bundlers replace with a boolean literal; under node there's no replacement,\n // so provide a runtime default.\n (globalThis as {TESTING?: boolean}).TESTING ??= false;\n\n const z = new Zero({\n schema: opts.schema,\n server: config.zeroCacheURL,\n auth: config.authToken,\n userID: config.userId,\n kvStore: 'mem',\n logLevel: config.log.level,\n logSink: stderrLogSink,\n });\n\n let result: AnalyzeQueryResult;\n try {\n const authOk = await z.inspector.authenticate(config.adminPassword ?? '');\n if (!authOk) {\n throw new Error(\n 'admin password rejected (or --admin-password is required)',\n );\n }\n\n const rpcOptions = {\n vendedRows: config.outputVendedRows,\n syncedRows: config.outputSyncedRows,\n joinPlans: config.joinPlans,\n };\n\n if (plan.kind === 'ast') {\n result = await z.inspector.analyzeServerAST(plan.ast, rpcOptions);\n } else if (plan.kind === 'named') {\n result = await z.inspector.analyzeNamedQuery(\n plan.name,\n plan.args as ReadonlyArray<never>,\n rpcOptions,\n );\n } else {\n const built = buildZqlQuery(plan.text, createBuilder(opts.schema));\n result = await z.inspector.analyzeQuery(built, rpcOptions);\n }\n } catch (e) {\n colorConsole.error(e instanceof Error ? e.message : String(e));\n await z.close().catch(() => {});\n process.exit(1);\n }\n\n renderResult(result, {\n outputSyncedRows: config.outputSyncedRows,\n outputVendedRows: config.outputVendedRows,\n });\n\n await z.close();\n}\n\nfunction buildQueryPlan(config: {\n ast?: string | undefined;\n query?: string | undefined;\n queryName?: string | undefined;\n queryArgs?: string | undefined;\n}): QueryPlan {\n const selectors = [\n config.ast !== undefined && 'ast',\n config.query !== undefined && 'query',\n config.queryName !== undefined && 'queryName',\n ].filter(Boolean) as string[];\n\n if (selectors.length === 0) {\n colorConsole.error(\n 'Exactly one of --ast / --query / --query-name is required.',\n );\n process.exit(1);\n }\n if (selectors.length > 1) {\n colorConsole.error(\n `Only one of --ast / --query / --query-name may be provided; got: ${selectors.join(', ')}`,\n );\n process.exit(1);\n }\n\n if (config.ast !== undefined) {\n return {kind: 'ast', ast: JSON.parse(config.ast) as AST};\n }\n if (config.query !== undefined) {\n return {kind: 'zql', text: config.query};\n }\n const args = config.queryArgs\n ? (JSON.parse(config.queryArgs) as ReadonlyArray<unknown>)\n : [];\n return {kind: 'named', name: config.queryName as string, args};\n}\n\nfunction buildZqlQuery(\n queryString: string,\n builder: SchemaQuery<Schema>,\n): AnyQuery {\n const f = new Function('builder', `return builder.${queryString};`);\n return f(builder) as AnyQuery;\n}\n\nfunction resolveHandshakeHeaders(config: {\n cookie?: string | undefined;\n headersJson?: string | undefined;\n}): Record<string, string> {\n const headers: Record<string, string> = {};\n if (config.headersJson !== undefined) {\n let parsed: unknown;\n try {\n parsed = JSON.parse(config.headersJson);\n } catch (e) {\n colorConsole.error(\n `--headers-json is not valid JSON: ${e instanceof Error ? e.message : String(e)}`,\n );\n process.exit(1);\n }\n if (\n parsed === null ||\n typeof parsed !== 'object' ||\n Array.isArray(parsed)\n ) {\n colorConsole.error('--headers-json must be a JSON object.');\n process.exit(1);\n }\n for (const [k, val] of Object.entries(parsed)) {\n if (typeof val !== 'string') {\n colorConsole.error(\n `--headers-json values must be strings; got ${typeof val} for \"${k}\".`,\n );\n process.exit(1);\n }\n headers[k] = val;\n }\n }\n if (config.cookie !== undefined) {\n headers.cookie = config.cookie;\n }\n return headers;\n}\n\nfunction installWebSocketHeaderShim(headers: Record<string, string>): void {\n class HeaderInjectingWebSocket extends NodeWebSocket {\n constructor(url: string | URL, protocols?: string | string[]) {\n super(url, protocols, {headers});\n }\n }\n (globalThis as {WebSocket?: unknown}).WebSocket = HeaderInjectingWebSocket;\n}\n\nfunction renderResult(\n result: AnalyzeQueryResult,\n opts: {outputSyncedRows: boolean; outputVendedRows: boolean},\n) {\n if (opts.outputSyncedRows) {\n colorConsole.log(styleText(['blue', 'bold'], '=== Synced Rows: ===\\n'));\n for (const [table, rows] of Object.entries(result.syncedRows ?? {})) {\n colorConsole.log(styleText('bold', table + ':'), rows);\n }\n }\n\n colorConsole.log(styleText(['blue', 'bold'], '=== Query Stats: ===\\n'));\n colorConsole.log(\n styleText('bold', 'total synced rows:'),\n result.syncedRowCount,\n );\n\n const readRowCountsByQuery = result.readRowCountsByQuery ?? {};\n let totalRowsRead = 0;\n for (const table of Object.keys(readRowCountsByQuery).sort()) {\n const counts = readRowCountsByQuery[table];\n for (const n of Object.values(counts)) {\n totalRowsRead += n;\n }\n colorConsole.log(styleText('bold', `${table} vended:`), counts);\n }\n colorConsole.log(\n styleText('bold', 'Rows Read (into JS):'),\n colorRowsConsidered(totalRowsRead),\n );\n const duration = result.elapsed ?? result.end - result.start;\n colorConsole.log(styleText('bold', 'time:'), colorTime(duration), 'ms');\n\n if (opts.outputVendedRows) {\n colorConsole.log(\n styleText(['blue', 'bold'], '=== JS Row Scan Values: ===\\n'),\n );\n for (const [table, rows] of Object.entries(result.readRows ?? {})) {\n colorConsole.log(styleText('bold', `${table}:`), rows);\n }\n }\n\n colorConsole.log(\n styleText(['blue', 'bold'], '\\n=== Rows Scanned (by SQLite): ===\\n'),\n );\n const dbScansByQuery = result.dbScansByQuery ?? {};\n let totalNVisit = 0;\n for (const [table, queries] of Object.entries(dbScansByQuery)) {\n colorConsole.log(styleText('bold', `${table}:`), queries);\n for (const count of Object.values(queries)) {\n totalNVisit += count;\n }\n }\n colorConsole.log(\n styleText('bold', 'total rows scanned:'),\n colorRowsConsidered(totalNVisit),\n );\n\n colorConsole.log(styleText(['blue', 'bold'], '\\n\\n=== Query Plans: ===\\n'));\n const plans = result.sqlitePlans ?? {};\n for (const [query, plan] of Object.entries(plans)) {\n colorConsole.log(styleText('bold', 'query'), query);\n colorConsole.log(plan.map((row, i) => colorPlanRow(row, i)).join('\\n'));\n colorConsole.log('\\n');\n }\n\n if (result.warnings.length > 0) {\n colorConsole.log(styleText(['yellow', 'bold'], '=== Warnings: ===\\n'));\n for (const w of result.warnings) {\n colorConsole.log(styleText('yellow', w));\n }\n }\n\n if (result.joinPlans && result.joinPlans.length > 0) {\n colorConsole.log(styleText(['blue', 'bold'], '\\n=== Join Plans: ===\\n'));\n colorConsole.log(formatPlannerEvents(result.joinPlans));\n }\n}\n\nfunction colorTime(duration: number) {\n if (duration < 100) {\n return styleText('green', duration.toFixed(2) + 'ms');\n } else if (duration < 1000) {\n return styleText('yellow', duration.toFixed(2) + 'ms');\n }\n return styleText('red', duration.toFixed(2) + 'ms');\n}\n\nfunction colorRowsConsidered(n: number) {\n if (n < 1000) {\n return styleText('green', n.toString());\n } else if (n < 10000) {\n return styleText('yellow', n.toString());\n }\n return styleText('red', n.toString());\n}\n\nfunction colorPlanRow(row: string, i: number) {\n if (row.includes('SCAN')) {\n if (i === 0) {\n return styleText('yellow', row);\n }\n return styleText('red', row);\n }\n return styleText('green', row);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"analyze-cli.js","names":["v","NodeWebSocket"],"sources":["../../../../analyze-query/src/analyze-cli.ts"],"sourcesContent":["import '../../shared/src/dotenv.ts';\n\nimport {Console} from 'node:console';\nimport {styleText} from 'node:util';\nimport type {LogSink} from '@rocicorp/logger';\nimport {WebSocket as NodeWebSocket} from 'ws';\nimport {logLevel, logOptions} from '../../otel/src/log-options.ts';\nimport {colorConsole} from '../../shared/src/logging.ts';\nimport {parseOptions} from '../../shared/src/options.ts';\nimport * as v from '../../shared/src/valita.ts';\nimport {ZERO_ENV_VAR_PREFIX} from '../../zero-cache/src/config/zero-config.ts';\nimport {Zero} from '../../zero-client/src/client/zero.ts';\nimport type {AnalyzeQueryResult} from '../../zero-protocol/src/analyze-query-result.ts';\nimport type {AST} from '../../zero-protocol/src/ast.ts';\nimport type {Schema} from '../../zero-types/src/schema.ts';\nimport {formatPlannerEvents} from '../../zql/src/planner/planner-debug.ts';\nimport {createBuilder} from '../../zql/src/query/create-builder.ts';\nimport type {AnyQuery} from '../../zql/src/query/query.ts';\nimport type {SchemaQuery} from '../../zql/src/query/schema-query.ts';\n\nexport type AnalyzeCLIOptions = {\n schema: Schema;\n /** Defaults to `process.argv.slice(2)`. */\n argv?: readonly string[] | undefined;\n};\n\nconst options = {\n zeroCacheURL: {\n type: v.string().optional(),\n desc: [\n 'URL of the remote zero-cache to analyze against.',\n 'Accepts http(s):// or ws(s):// (ws(s) is the transport actually used).',\n ],\n },\n adminPassword: {\n type: v.string().optional(),\n desc: [\n 'Admin password for zero-cache.',\n 'Required when the server is configured with one; ignored in dev mode.',\n ],\n },\n authToken: {\n type: v.string().optional(),\n desc: [\n 'Raw JWT forwarded to zero-cache.',\n 'Used server-side to fill permission variables for the query.',\n ],\n },\n cookie: {\n type: v.string().optional(),\n desc: [\n 'Cookie header value sent on the WebSocket upgrade request,',\n 'e.g. `session=abc; foo=bar`. Use this when zero-cache is behind',\n 'a proxy that resolves auth via cookies. Merged with --headers-json',\n '(--cookie wins on conflict).',\n ],\n },\n headersJson: {\n type: v.string().optional(),\n desc: [\n 'JSON object of arbitrary headers to send on the WebSocket upgrade',\n 'request, e.g. `{\"x-api-key\":\"...\"}`. Escape hatch for exotic auth',\n 'schemes; prefer --auth-token or --cookie when possible.',\n ],\n },\n userId: {\n type: v.string().optional(),\n desc: [\n 'Optional userID to report to zero-cache.',\n 'Omit for anonymous analysis.',\n ],\n },\n ast: {\n type: v.string().optional(),\n desc: [\n 'JSON-encoded AST. Exactly one of --ast / --query / --query-name is required.',\n 'The AST is sent to the server verbatim — provide it in server (post-mapping) form.',\n ],\n },\n query: {\n type: v.string().optional(),\n desc: [\n 'ZQL query in chain form, e.g. `issue.related(\"comments\").limit(10)`.',\n 'Evaluated against the schema you pass to runAnalyzeCLI.',\n ],\n },\n queryName: {\n type: v.string().optional(),\n desc: [\n 'Name of a server-registered custom (named) query.',\n 'The server resolves the name + args via its registered query handler.',\n ],\n },\n queryArgs: {\n type: v.string().optional(),\n desc: [\n 'JSON-encoded array of arguments for --query-name. Defaults to `[]`.',\n ],\n },\n outputVendedRows: {\n type: v.boolean().optional(() => false),\n desc: [\n 'Include the rows read from the replica to execute the query.',\n 'Each row appears once per read.',\n ],\n },\n outputSyncedRows: {\n type: v.boolean().optional(() => false),\n desc: ['Include the rows that would be synced to the client.'],\n },\n joinPlans: {\n type: v.boolean().optional(() => false),\n desc: ['Include join planner diagnostics.'],\n },\n log: {\n ...logOptions,\n level: logLevel.optional(() => 'error'),\n },\n};\n\ntype QueryPlan =\n | {kind: 'ast'; ast: AST}\n | {kind: 'zql'; text: string}\n | {kind: 'named'; name: string; args: ReadonlyArray<unknown>};\n\n// Route all Zero client log output to stderr so stdout contains only the\n// analyze result. Shell redirection (`2>/dev/null`) can then cleanly silence\n// logs without affecting output.\nconst stderrConsole = new Console({\n stdout: process.stderr,\n stderr: process.stderr,\n});\nconst stderrLogSink: LogSink = {\n log(level, context, ...args) {\n const ctx = context\n ? Object.entries(context).map(([k, v]) =>\n v === undefined ? k : `${k}=${v}`,\n )\n : [];\n stderrConsole[level](...ctx, ...args);\n },\n};\n\n/**\n * Entry point for a user's `cli.ts`. Parses argv, connects to a remote\n * zero-cache by standing up an in-process Zero client (in-memory storage,\n * no subscriptions), calls the inspector's `analyze-query` RPC, and\n * renders the result. Intended to be called as:\n *\n * ```ts\n * import {schema} from './schema.ts';\n * import {runAnalyzeCLI} from '@rocicorp/zero/analyze';\n * await runAnalyzeCLI({schema});\n * ```\n *\n * Exits the process with code 1 on error.\n */\nexport async function runAnalyzeCLI(opts: AnalyzeCLIOptions): Promise<void> {\n const argv = (opts.argv ?? process.argv.slice(2)).map(s =>\n s.replaceAll('\\n', ' '),\n );\n\n const config = parseOptions(options, {\n argv,\n envNamePrefix: ZERO_ENV_VAR_PREFIX,\n description: [\n {\n header: 'analyze-query (remote)',\n content: `Analyze a ZQL query against a remote zero-cache.\n\n Connects to zero-cache's inspector protocol and reports the server-observed\n row scans, SQLite query plans, and timings.`,\n },\n {\n header: 'Examples',\n content: ` node cli.ts --zero-cache-url=https://zero.example.com \\\\\n --admin-password=\"$ZERO_ADMIN_PASSWORD\" \\\\\n --query='issue.related(\"comments\").limit(10)'\n\n node cli.ts --zero-cache-url=http://localhost:4848 \\\\\n --ast='\\\\{\"table\": \"issue\", \"limit\": 5\\\\}'\n\n node cli.ts --zero-cache-url=http://localhost:4848 \\\\\n --query-name=issueList --query-args='[]'`,\n },\n ],\n });\n\n if (!config.zeroCacheURL) {\n colorConsole.error('--zero-cache-url is required. See --help for usage.');\n process.exit(1);\n }\n\n const plan = buildQueryPlan(config);\n\n const handshakeHeaders = resolveHandshakeHeaders(config);\n if (Object.keys(handshakeHeaders).length > 0) {\n installWebSocketHeaderShim(handshakeHeaders);\n }\n\n // zero-client and replicache reference a build-time `TESTING` global that\n // bundlers replace with a boolean literal; under node there's no replacement,\n // so provide a runtime default.\n (globalThis as {TESTING?: boolean}).TESTING ??= false;\n\n const z = new Zero({\n schema: opts.schema,\n server: config.zeroCacheURL,\n auth: config.authToken,\n userID: config.userId,\n kvStore: 'mem',\n logLevel: config.log.level,\n logSink: stderrLogSink,\n });\n\n let result: AnalyzeQueryResult;\n try {\n const authOk = await z.inspector.authenticate(config.adminPassword ?? '');\n if (!authOk) {\n throw new Error(\n 'admin password rejected (or --admin-password is required)',\n );\n }\n\n const rpcOptions = {\n vendedRows: config.outputVendedRows,\n syncedRows: config.outputSyncedRows,\n joinPlans: config.joinPlans,\n };\n\n if (plan.kind === 'ast') {\n result = await z.inspector.analyzeServerAST(plan.ast, rpcOptions);\n } else if (plan.kind === 'named') {\n result = await z.inspector.analyzeNamedQuery(\n plan.name,\n plan.args as ReadonlyArray<never>,\n rpcOptions,\n );\n } else {\n const built = buildZqlQuery(plan.text, createBuilder(opts.schema));\n result = await z.inspector.analyzeQuery(built, rpcOptions);\n }\n } catch (e) {\n colorConsole.error(e instanceof Error ? e.message : String(e));\n await z.close().catch(() => {});\n process.exit(1);\n }\n\n renderResult(result, {\n outputSyncedRows: config.outputSyncedRows,\n outputVendedRows: config.outputVendedRows,\n });\n\n await z.close();\n}\n\nfunction buildQueryPlan(config: {\n ast?: string | undefined;\n query?: string | undefined;\n queryName?: string | undefined;\n queryArgs?: string | undefined;\n}): QueryPlan {\n const selectors = [\n config.ast !== undefined && 'ast',\n config.query !== undefined && 'query',\n config.queryName !== undefined && 'queryName',\n ].filter(Boolean) as string[];\n\n if (selectors.length === 0) {\n colorConsole.error(\n 'Exactly one of --ast / --query / --query-name is required.',\n );\n process.exit(1);\n }\n if (selectors.length > 1) {\n colorConsole.error(\n `Only one of --ast / --query / --query-name may be provided; got: ${selectors.join(', ')}`,\n );\n process.exit(1);\n }\n\n if (config.ast !== undefined) {\n return {kind: 'ast', ast: JSON.parse(config.ast) as AST};\n }\n if (config.query !== undefined) {\n return {kind: 'zql', text: config.query};\n }\n const args = config.queryArgs\n ? (JSON.parse(config.queryArgs) as ReadonlyArray<unknown>)\n : [];\n return {kind: 'named', name: config.queryName as string, args};\n}\n\nfunction buildZqlQuery(\n queryString: string,\n builder: SchemaQuery<Schema>,\n): AnyQuery {\n const f = new Function('builder', `return builder.${queryString};`);\n return f(builder) as AnyQuery;\n}\n\nfunction resolveHandshakeHeaders(config: {\n cookie?: string | undefined;\n headersJson?: string | undefined;\n}): Record<string, string> {\n const headers: Record<string, string> = {};\n if (config.headersJson !== undefined) {\n let parsed: unknown;\n try {\n parsed = JSON.parse(config.headersJson);\n } catch (e) {\n colorConsole.error(\n `--headers-json is not valid JSON: ${e instanceof Error ? e.message : String(e)}`,\n );\n process.exit(1);\n }\n if (\n parsed === null ||\n typeof parsed !== 'object' ||\n Array.isArray(parsed)\n ) {\n colorConsole.error('--headers-json must be a JSON object.');\n process.exit(1);\n }\n for (const [k, val] of Object.entries(parsed)) {\n if (typeof val !== 'string') {\n colorConsole.error(\n `--headers-json values must be strings; got ${typeof val} for \"${k}\".`,\n );\n process.exit(1);\n }\n headers[k] = val;\n }\n }\n if (config.cookie !== undefined) {\n headers.cookie = config.cookie;\n }\n return headers;\n}\n\nfunction installWebSocketHeaderShim(headers: Record<string, string>): void {\n class HeaderInjectingWebSocket extends NodeWebSocket {\n constructor(url: string | URL, protocols?: string | string[]) {\n super(url, protocols, {headers});\n }\n }\n (globalThis as {WebSocket?: unknown}).WebSocket = HeaderInjectingWebSocket;\n}\n\nfunction renderResult(\n result: AnalyzeQueryResult,\n opts: {outputSyncedRows: boolean; outputVendedRows: boolean},\n) {\n if (opts.outputSyncedRows) {\n colorConsole.log(styleText(['blue', 'bold'], '=== Synced Rows: ===\\n'));\n for (const [table, rows] of Object.entries(result.syncedRows ?? {})) {\n colorConsole.log(styleText('bold', table + ':'), rows);\n }\n }\n\n colorConsole.log(styleText(['blue', 'bold'], '=== Query Stats: ===\\n'));\n colorConsole.log(\n styleText('bold', 'total synced rows:'),\n result.syncedRowCount,\n );\n\n const readRowCountsByQuery = result.readRowCountsByQuery ?? {};\n let totalRowsRead = 0;\n for (const table of Object.keys(readRowCountsByQuery).sort()) {\n const counts = readRowCountsByQuery[table];\n for (const n of Object.values(counts)) {\n totalRowsRead += n;\n }\n colorConsole.log(styleText('bold', `${table} vended:`), counts);\n }\n colorConsole.log(\n styleText('bold', 'Rows Read (into JS):'),\n colorRowsConsidered(totalRowsRead),\n );\n const duration = result.elapsed ?? result.end - result.start;\n colorConsole.log(styleText('bold', 'time:'), colorTime(duration), 'ms');\n\n if (opts.outputVendedRows) {\n colorConsole.log(\n styleText(['blue', 'bold'], '=== JS Row Scan Values: ===\\n'),\n );\n for (const [table, rows] of Object.entries(result.readRows ?? {})) {\n colorConsole.log(styleText('bold', `${table}:`), rows);\n }\n }\n\n colorConsole.log(\n styleText(['blue', 'bold'], '\\n=== Rows Scanned (by SQLite): ===\\n'),\n );\n const dbScansByQuery = result.dbScansByQuery ?? {};\n let totalNVisit = 0;\n for (const [table, queries] of Object.entries(dbScansByQuery)) {\n colorConsole.log(styleText('bold', `${table}:`), queries);\n for (const count of Object.values(queries)) {\n totalNVisit += count;\n }\n }\n colorConsole.log(\n styleText('bold', 'total rows scanned:'),\n colorRowsConsidered(totalNVisit),\n );\n\n colorConsole.log(styleText(['blue', 'bold'], '\\n\\n=== Query Plans: ===\\n'));\n const plans = result.sqlitePlans ?? {};\n for (const [query, plan] of Object.entries(plans)) {\n colorConsole.log(styleText('bold', 'query'), query);\n colorConsole.log(plan.map((row, i) => colorPlanRow(row, i)).join('\\n'));\n colorConsole.log('\\n');\n }\n\n if (result.warnings.length > 0) {\n colorConsole.log(styleText(['yellow', 'bold'], '=== Warnings: ===\\n'));\n for (const w of result.warnings) {\n colorConsole.log(styleText('yellow', w));\n }\n }\n\n if (result.joinPlans && result.joinPlans.length > 0) {\n colorConsole.log(styleText(['blue', 'bold'], '\\n=== Join Plans: ===\\n'));\n colorConsole.log(formatPlannerEvents(result.joinPlans));\n }\n}\n\nfunction colorTime(duration: number) {\n if (duration < 100) {\n return styleText('green', duration.toFixed(2) + 'ms');\n } else if (duration < 1000) {\n return styleText('yellow', duration.toFixed(2) + 'ms');\n }\n return styleText('red', duration.toFixed(2) + 'ms');\n}\n\nfunction colorRowsConsidered(n: number) {\n if (n < 1000) {\n return styleText('green', n.toString());\n } else if (n < 10000) {\n return styleText('yellow', n.toString());\n }\n return styleText('red', n.toString());\n}\n\nfunction colorPlanRow(row: string, i: number) {\n if (row.includes('SCAN')) {\n if (i === 0) {\n return styleText('yellow', row);\n }\n return styleText('red', row);\n }\n return styleText('green', row);\n}\n"],"mappings":"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"}
@@ -27,7 +27,7 @@ import { explainQueries } from "../../zqlite/src/explain-queries.js";
27
27
  import { styleText } from "node:util";
28
28
  import fs from "node:fs";
29
29
  //#region ../analyze-query/src/bin-analyze.ts
30
- var cfg = parseOptions({
30
+ var options = {
31
31
  schema: deployPermissionsOptions.schema,
32
32
  replicaFile: {
33
33
  ...zeroOptions.replica.file,
@@ -84,7 +84,8 @@ var cfg = parseOptions({
84
84
  ...logOptions,
85
85
  level: logLevel.optional(() => "error")
86
86
  }
87
- }, {
87
+ };
88
+ var cfg = parseOptions(options, {
88
89
  argv: process.argv.slice(2).map((s) => s.replaceAll("\n", " ")),
89
90
  envNamePrefix: ZERO_ENV_VAR_PREFIX,
90
91
  description: [{
@@ -181,7 +182,8 @@ else {
181
182
  }
182
183
  function runQuery(queryString) {
183
184
  const z = { query: Object.fromEntries(Object.entries(schema.tables).map(([name]) => [name, newQuery(schema, name)])) };
184
- const ast = asQueryInternals(new Function("z", `return z.query.${queryString};`)(z)).ast;
185
+ const q = new Function("z", `return z.query.${queryString};`)(z);
186
+ const ast = asQueryInternals(q).ast;
185
187
  return runAst(lc, clientSchema, ast, false, {
186
188
  applyPermissions: config.applyPermissions,
187
189
  auth: config.authData ? {
@@ -1 +1 @@
1
- {"version":3,"file":"bin-analyze.js","names":[],"sources":["../../../../analyze-query/src/bin-analyze.ts"],"sourcesContent":["import '../../shared/src/dotenv.ts';\n\nimport fs from 'node:fs';\nimport {styleText} from 'node:util';\nimport {astToZQL} from '../../ast-to-zql/src/ast-to-zql.ts';\nimport {formatOutput} from '../../ast-to-zql/src/format.ts';\nimport {logLevel, logOptions} from '../../otel/src/log-options.ts';\nimport {colorConsole, createLogContext} from '../../shared/src/logging.ts';\nimport {getOrInsertComputed} from '../../shared/src/map.ts';\nimport {must} from '../../shared/src/must.ts';\nimport {parseOptions} from '../../shared/src/options.ts';\nimport * as v from '../../shared/src/valita.ts';\nimport {\n appOptions,\n shardOptions,\n ZERO_ENV_VAR_PREFIX,\n zeroOptions,\n} from '../../zero-cache/src/config/zero-config.ts';\nimport {\n computeZqlSpecs,\n mustGetTableSpec,\n} from '../../zero-cache/src/db/lite-tables.ts';\nimport {\n deployPermissionsOptions,\n loadSchemaAndPermissions,\n} from '../../zero-cache/src/scripts/permissions.ts';\nimport {runAst} from '../../zero-cache/src/services/run-ast.ts';\nimport {pgClient} from '../../zero-cache/src/types/pg.ts';\nimport {getShardID, upstreamSchema} from '../../zero-cache/src/types/shards.ts';\nimport type {AnalyzeQueryResult} from '../../zero-protocol/src/analyze-query-result.ts';\nimport {type AST} from '../../zero-protocol/src/ast.ts';\nimport {clientSchemaFrom} from '../../zero-schema/src/builder/schema-builder.ts';\nimport {clientToServer} from '../../zero-schema/src/name-mapper.ts';\nimport type {Schema} from '../../zero-types/src/schema.ts';\nimport {\n Debug,\n runtimeDebugFlags,\n} from '../../zql/src/builder/debug-delegate.ts';\nimport type {Source} from '../../zql/src/ivm/source.ts';\nimport {QueryDelegateBase} from '../../zql/src/query/query-delegate-base.ts';\nimport {newQuery} from '../../zql/src/query/query-impl.ts';\nimport {asQueryInternals} from '../../zql/src/query/query-internals.ts';\nimport {type PullRow, type Query} from '../../zql/src/query/query.ts';\nimport {Database} from '../../zqlite/src/db.ts';\nimport {explainQueries} from '../../zqlite/src/explain-queries.ts';\nimport {TableSource} from '../../zqlite/src/table-source.ts';\n\nconst options = {\n schema: deployPermissionsOptions.schema,\n replicaFile: {\n ...zeroOptions.replica.file,\n desc: ['File path to the SQLite replica to test queries against.'],\n },\n ast: {\n type: v.string().optional(),\n desc: [\n 'AST for the query to be analyzed. Only one of ast/query/hash should be provided.',\n ],\n },\n query: {\n type: v.string().optional(),\n desc: [\n 'Query to be analyzed in the form of: table.where(...).related(...).etc. ',\n 'Only one of ast/query/hash should be provided.',\n ],\n },\n hash: {\n type: v.string().optional(),\n desc: [\n 'Hash of the query to be analyzed. This is used to look up the query in the database. ',\n 'Only one of ast/query/hash should be provided.',\n 'You should run this script from the directory containing your .env file to reduce the amount of',\n 'configuration required. The .env file should contain the connection URL to the CVR database.',\n ],\n },\n applyPermissions: {\n type: v.boolean().optional(() => false),\n desc: [\n 'Whether to apply permissions (from your schema file) to the provided query.',\n ],\n },\n authData: {\n type: v.string().optional(),\n desc: [\n 'JSON encoded payload of the auth data.',\n 'This will be used to fill permission variables if the \"applyPermissions\" option is set',\n ],\n },\n outputVendedRows: {\n type: v.boolean().optional(() => false),\n desc: [\n 'Whether to output the rows which were read from the replica in order to execute the analyzed query. ',\n 'If the same row is read more than once it will be logged once for each time it was read.',\n ],\n },\n outputSyncedRows: {\n type: v.boolean().optional(() => false),\n desc: [\n 'Whether to output the rows which would be synced to the client for the analyzed query.',\n ],\n },\n cvr: {\n db: {\n type: v.string().optional(),\n desc: [\n 'Connection URL to the CVR database. If using --hash, either this or --upstream-db',\n 'must be specified.',\n ],\n },\n },\n upstream: {\n db: {\n desc: [\n `Connection URL to the \"upstream\" authoritative postgres database. If using --hash, `,\n 'either this or --cvr-db must be specified.',\n ],\n type: v.string().optional(),\n },\n type: zeroOptions.upstream.type,\n },\n app: appOptions,\n shard: shardOptions,\n // Read from the same env var as zero-cache, so the analysis matches what\n // the server runs.\n enableCorrelatedPredicatePushdown:\n zeroOptions.enableCorrelatedPredicatePushdown,\n log: {\n ...logOptions,\n level: logLevel.optional(() => 'error'),\n },\n};\n\nconst cfg = parseOptions(options, {\n argv: process.argv.slice(2).map(s => s.replaceAll('\\n', ' ')),\n envNamePrefix: ZERO_ENV_VAR_PREFIX,\n description: [\n {\n header: 'analyze-query',\n content: `Analyze a ZQL query and show information about how it runs against a SQLite replica.\n\n analyze-query uses the same environment variables and flags as zero-cache-dev. If run from your development environment, it will pick up your ZERO_REPLICA_FILE, ZERO_SCHEMA_PATH, and other env vars automatically.\n\n If run in another environment (e.g., production) you will have to specify these flags. In particular, you must have a copy of the appropriate Zero schema file to give to the --schema-path flag.`,\n },\n {\n header: 'Examples',\n content: `# In development\n npx analyze-query --query='issue.related(\"comments\").limit(10)'\n npx analyze-query --ast='\\\\{\"table\": \"artist\",\"limit\": 10\\\\}'\n npx analyze-query --hash=1234567890\n\n # In production\n # First copy schema.ts to your production environment, then run:\n npx analyze-query \\\\\n --schema-path='./schema.ts' \\\\\n --replica-file='/path/to/replica.db' \\\\\n --query='issue.related(\"comments\").limit(10)'\n\n npx analyze-query \\\\\n --schema-path='./schema.ts' \\\\\n --replica-file='/path/to/replica.db' \\\\\n --ast='\\\\{\"table\": \"artist\",\"limit\": 10\\\\}'\n\n # cvr-db is required when using the hash option.\n # It is typically the same as your upstream db.\n npx analyze-query \\\\\n --schema-path='./schema.ts' \\\\\n --replica-file='/path/to/replica.db' \\\\\n --cvr-db='postgres://user:pass@host:port/db' \\\\\n --hash=1234567890\n `,\n },\n ],\n});\nconst config = {\n ...cfg,\n cvr: {\n ...cfg.cvr,\n db: cfg.cvr.db ?? cfg.upstream.db,\n },\n};\n\nruntimeDebugFlags.trackRowCountsVended = true;\nruntimeDebugFlags.trackRowsVended = config.outputVendedRows;\n\nconst lc = createLogContext({\n log: config.log,\n});\n\ncolorConsole.warn(\n 'DEPRECATED: `analyze-query` is deprecated. Please migrate to a project-specific analyze command built with `runAnalyzeCLI` from `@rocicorp/zero/analyze`.',\n);\n\nif (!fs.existsSync(config.replicaFile)) {\n colorConsole.error(`Replica file ${config.replicaFile} does not exist`);\n process.exit(1);\n}\nconst db = new Database(lc, config.replicaFile);\n\nconst {schema, permissions} = await loadSchemaAndPermissions(\n config.schema.path,\n);\nconst clientSchema = clientSchemaFrom(schema).clientSchema;\n\nconst sources = new Map<string, TableSource>();\nconst clientToServerMapper = clientToServer(schema.tables);\nconst debug = new Debug(config.outputVendedRows);\nconst tableSpecs = computeZqlSpecs(lc, db, {includeBackfillingColumns: false});\n\nclass AnalyzeQueryDelegate extends QueryDelegateBase {\n readonly debug = debug;\n readonly defaultQueryComplete = true;\n readonly disableCorrelatedPredicatePushdown =\n !config.enableCorrelatedPredicatePushdown;\n\n getSource(serverTableName: string): Source | undefined {\n return getOrInsertComputed(sources, serverTableName, serverTableName => {\n const tableSpec = mustGetTableSpec(tableSpecs, serverTableName);\n const {primaryKey} = tableSpec.tableSpec;\n return new TableSource(\n lc,\n config.log,\n db,\n serverTableName,\n tableSpec.zqlSpec,\n primaryKey,\n );\n });\n }\n}\n\nconst host = new AnalyzeQueryDelegate();\n\nlet result: AnalyzeQueryResult;\n\nif (config.ast) {\n result = await runAst(\n lc,\n clientSchema,\n JSON.parse(config.ast),\n true,\n {\n applyPermissions: config.applyPermissions,\n auth: config.authData\n ? {type: 'jwt' as const, raw: '', decoded: JSON.parse(config.authData)}\n : undefined,\n clientToServerMapper,\n permissions,\n syncedRows: config.outputSyncedRows,\n db,\n tableSpecs,\n host,\n },\n async () => {},\n );\n} else if (config.query) {\n result = await runQuery(config.query);\n} else if (config.hash) {\n result = await runHash(config.hash);\n} else {\n colorConsole.error('No query or AST or hash provided');\n process.exit(1);\n}\n\nfunction runQuery(queryString: string): Promise<AnalyzeQueryResult> {\n const z = {\n query: Object.fromEntries(\n Object.entries(schema.tables).map(([name]) => [\n name,\n newQuery(schema, name),\n ]),\n ),\n };\n\n const f = new Function('z', `return z.query.${queryString};`);\n const q: Query<string, Schema, PullRow<string, Schema>> = f(z);\n\n const ast = asQueryInternals(q).ast;\n return runAst(\n lc,\n clientSchema,\n ast,\n false,\n {\n applyPermissions: config.applyPermissions,\n auth: config.authData\n ? {type: 'jwt' as const, raw: '', decoded: JSON.parse(config.authData)}\n : undefined,\n clientToServerMapper,\n permissions,\n syncedRows: config.outputSyncedRows,\n db,\n tableSpecs,\n host,\n },\n async () => {},\n );\n}\n\nasync function runHash(hash: string) {\n const cvrDB = pgClient(\n lc,\n must(config.cvr.db, 'CVR DB must be provided when using the hash option'),\n 'analyze-query-hash-cvr',\n );\n\n const rows = await cvrDB`select \"clientAST\", \"internal\" from ${cvrDB(\n upstreamSchema(getShardID(config)) + '/cvr',\n )}.\"queries\" where \"queryHash\" = ${must(hash)} limit 1;`;\n await cvrDB.end();\n\n colorConsole.log('ZQL from Hash:');\n const ast = rows[0].clientAST as AST;\n colorConsole.log(await formatOutput(ast.table + astToZQL(ast)));\n\n return runAst(\n lc,\n clientSchema,\n ast,\n true,\n {\n applyPermissions: config.applyPermissions,\n auth: config.authData\n ? {type: 'jwt' as const, raw: '', decoded: JSON.parse(config.authData)}\n : undefined,\n clientToServerMapper,\n permissions,\n syncedRows: config.outputSyncedRows,\n db,\n tableSpecs,\n host,\n },\n async () => {},\n );\n}\n\nif (config.outputSyncedRows) {\n colorConsole.log(styleText(['blue', 'bold'], '=== Synced Rows: ===\\n'));\n for (const [table, rows] of Object.entries(result.syncedRows ?? {})) {\n colorConsole.log(styleText('bold', table + ':'), rows);\n }\n}\n\ncolorConsole.log(styleText(['blue', 'bold'], '=== Query Stats: ===\\n'));\ncolorConsole.log(\n styleText('bold', 'total synced rows:'),\n result.syncedRowCount,\n);\nshowStats();\nif (config.outputVendedRows) {\n colorConsole.log(\n styleText(['blue', 'bold'], '=== JS Row Scan Values: ===\\n'),\n );\n for (const source of sources.values()) {\n colorConsole.log(\n styleText('bold', `${source.tableSchema.name}:`),\n debug.getVendedRows()?.[source.tableSchema.name] ?? {},\n );\n }\n}\n\ncolorConsole.log(\n styleText(['blue', 'bold'], '\\n=== Rows Scanned (by SQLite): ===\\n'),\n);\nconst nvisitCounts = debug.getNVisitCounts();\nlet totalNVisit = 0;\nfor (const [table, queries] of Object.entries(nvisitCounts)) {\n colorConsole.log(styleText('bold', `${table}:`), queries);\n for (const count of Object.values(queries)) {\n totalNVisit += count;\n }\n}\ncolorConsole.log(\n styleText('bold', 'total rows scanned:'),\n colorRowsConsidered(totalNVisit),\n);\n\ncolorConsole.log(styleText(['blue', 'bold'], '\\n\\n=== Query Plans: ===\\n'));\nconst fallbackPlans = explainQueries(debug.getVendedRowCounts() ?? {}, db);\nconst capturedPlans = debug.getSQLitePlans();\nconst plans: Record<string, string[]> = {...fallbackPlans, ...capturedPlans};\nfor (const [query, plan] of Object.entries(plans)) {\n colorConsole.log(styleText('bold', 'query'), query);\n colorConsole.log(plan.map((row, i) => colorPlanRow(row, i)).join('\\n'));\n colorConsole.log('\\n');\n}\n\nfunction showStats() {\n let totalRowsConsidered = 0;\n for (const source of sources.values()) {\n const values = Object.values(\n debug.getVendedRowCounts()?.[source.tableSchema.name] ?? {},\n );\n for (const v of values) {\n totalRowsConsidered += v;\n }\n colorConsole.log(\n styleText('bold', source.tableSchema.name + ' vended:'),\n debug.getVendedRowCounts()?.[source.tableSchema.name] ?? {},\n );\n }\n\n colorConsole.log(\n styleText('bold', 'Rows Read (into JS):'),\n colorRowsConsidered(totalRowsConsidered),\n );\n colorConsole.log(\n styleText('bold', 'time:'),\n colorTime(result.end - result.start),\n 'ms',\n );\n}\n\nfunction colorTime(duration: number) {\n if (duration < 100) {\n return styleText('green', duration.toFixed(2) + 'ms');\n } else if (duration < 1000) {\n return styleText('yellow', duration.toFixed(2) + 'ms');\n }\n return styleText('red', duration.toFixed(2) + 'ms');\n}\n\nfunction colorRowsConsidered(n: number) {\n if (n < 1000) {\n return styleText('green', n.toString());\n } else if (n < 10000) {\n return styleText('yellow', n.toString());\n }\n return styleText('red', n.toString());\n}\n\nfunction colorPlanRow(row: string, i: number) {\n if (row.includes('SCAN')) {\n if (i === 0) {\n return styleText('yellow', row);\n }\n return styleText('red', row);\n }\n return styleText('green', row);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"bin-analyze.js","names":["v"],"sources":["../../../../analyze-query/src/bin-analyze.ts"],"sourcesContent":["import '../../shared/src/dotenv.ts';\n\nimport fs from 'node:fs';\nimport {styleText} from 'node:util';\nimport {astToZQL} from '../../ast-to-zql/src/ast-to-zql.ts';\nimport {formatOutput} from '../../ast-to-zql/src/format.ts';\nimport {logLevel, logOptions} from '../../otel/src/log-options.ts';\nimport {colorConsole, createLogContext} from '../../shared/src/logging.ts';\nimport {getOrInsertComputed} from '../../shared/src/map.ts';\nimport {must} from '../../shared/src/must.ts';\nimport {parseOptions} from '../../shared/src/options.ts';\nimport * as v from '../../shared/src/valita.ts';\nimport {\n appOptions,\n shardOptions,\n ZERO_ENV_VAR_PREFIX,\n zeroOptions,\n} from '../../zero-cache/src/config/zero-config.ts';\nimport {\n computeZqlSpecs,\n mustGetTableSpec,\n} from '../../zero-cache/src/db/lite-tables.ts';\nimport {\n deployPermissionsOptions,\n loadSchemaAndPermissions,\n} from '../../zero-cache/src/scripts/permissions.ts';\nimport {runAst} from '../../zero-cache/src/services/run-ast.ts';\nimport {pgClient} from '../../zero-cache/src/types/pg.ts';\nimport {getShardID, upstreamSchema} from '../../zero-cache/src/types/shards.ts';\nimport type {AnalyzeQueryResult} from '../../zero-protocol/src/analyze-query-result.ts';\nimport {type AST} from '../../zero-protocol/src/ast.ts';\nimport {clientSchemaFrom} from '../../zero-schema/src/builder/schema-builder.ts';\nimport {clientToServer} from '../../zero-schema/src/name-mapper.ts';\nimport type {Schema} from '../../zero-types/src/schema.ts';\nimport {\n Debug,\n runtimeDebugFlags,\n} from '../../zql/src/builder/debug-delegate.ts';\nimport type {Source} from '../../zql/src/ivm/source.ts';\nimport {QueryDelegateBase} from '../../zql/src/query/query-delegate-base.ts';\nimport {newQuery} from '../../zql/src/query/query-impl.ts';\nimport {asQueryInternals} from '../../zql/src/query/query-internals.ts';\nimport {type PullRow, type Query} from '../../zql/src/query/query.ts';\nimport {Database} from '../../zqlite/src/db.ts';\nimport {explainQueries} from '../../zqlite/src/explain-queries.ts';\nimport {TableSource} from '../../zqlite/src/table-source.ts';\n\nconst options = {\n schema: deployPermissionsOptions.schema,\n replicaFile: {\n ...zeroOptions.replica.file,\n desc: ['File path to the SQLite replica to test queries against.'],\n },\n ast: {\n type: v.string().optional(),\n desc: [\n 'AST for the query to be analyzed. Only one of ast/query/hash should be provided.',\n ],\n },\n query: {\n type: v.string().optional(),\n desc: [\n 'Query to be analyzed in the form of: table.where(...).related(...).etc. ',\n 'Only one of ast/query/hash should be provided.',\n ],\n },\n hash: {\n type: v.string().optional(),\n desc: [\n 'Hash of the query to be analyzed. This is used to look up the query in the database. ',\n 'Only one of ast/query/hash should be provided.',\n 'You should run this script from the directory containing your .env file to reduce the amount of',\n 'configuration required. The .env file should contain the connection URL to the CVR database.',\n ],\n },\n applyPermissions: {\n type: v.boolean().optional(() => false),\n desc: [\n 'Whether to apply permissions (from your schema file) to the provided query.',\n ],\n },\n authData: {\n type: v.string().optional(),\n desc: [\n 'JSON encoded payload of the auth data.',\n 'This will be used to fill permission variables if the \"applyPermissions\" option is set',\n ],\n },\n outputVendedRows: {\n type: v.boolean().optional(() => false),\n desc: [\n 'Whether to output the rows which were read from the replica in order to execute the analyzed query. ',\n 'If the same row is read more than once it will be logged once for each time it was read.',\n ],\n },\n outputSyncedRows: {\n type: v.boolean().optional(() => false),\n desc: [\n 'Whether to output the rows which would be synced to the client for the analyzed query.',\n ],\n },\n cvr: {\n db: {\n type: v.string().optional(),\n desc: [\n 'Connection URL to the CVR database. If using --hash, either this or --upstream-db',\n 'must be specified.',\n ],\n },\n },\n upstream: {\n db: {\n desc: [\n `Connection URL to the \"upstream\" authoritative postgres database. If using --hash, `,\n 'either this or --cvr-db must be specified.',\n ],\n type: v.string().optional(),\n },\n type: zeroOptions.upstream.type,\n },\n app: appOptions,\n shard: shardOptions,\n // Read from the same env var as zero-cache, so the analysis matches what\n // the server runs.\n enableCorrelatedPredicatePushdown:\n zeroOptions.enableCorrelatedPredicatePushdown,\n log: {\n ...logOptions,\n level: logLevel.optional(() => 'error'),\n },\n};\n\nconst cfg = parseOptions(options, {\n argv: process.argv.slice(2).map(s => s.replaceAll('\\n', ' ')),\n envNamePrefix: ZERO_ENV_VAR_PREFIX,\n description: [\n {\n header: 'analyze-query',\n content: `Analyze a ZQL query and show information about how it runs against a SQLite replica.\n\n analyze-query uses the same environment variables and flags as zero-cache-dev. If run from your development environment, it will pick up your ZERO_REPLICA_FILE, ZERO_SCHEMA_PATH, and other env vars automatically.\n\n If run in another environment (e.g., production) you will have to specify these flags. In particular, you must have a copy of the appropriate Zero schema file to give to the --schema-path flag.`,\n },\n {\n header: 'Examples',\n content: `# In development\n npx analyze-query --query='issue.related(\"comments\").limit(10)'\n npx analyze-query --ast='\\\\{\"table\": \"artist\",\"limit\": 10\\\\}'\n npx analyze-query --hash=1234567890\n\n # In production\n # First copy schema.ts to your production environment, then run:\n npx analyze-query \\\\\n --schema-path='./schema.ts' \\\\\n --replica-file='/path/to/replica.db' \\\\\n --query='issue.related(\"comments\").limit(10)'\n\n npx analyze-query \\\\\n --schema-path='./schema.ts' \\\\\n --replica-file='/path/to/replica.db' \\\\\n --ast='\\\\{\"table\": \"artist\",\"limit\": 10\\\\}'\n\n # cvr-db is required when using the hash option.\n # It is typically the same as your upstream db.\n npx analyze-query \\\\\n --schema-path='./schema.ts' \\\\\n --replica-file='/path/to/replica.db' \\\\\n --cvr-db='postgres://user:pass@host:port/db' \\\\\n --hash=1234567890\n `,\n },\n ],\n});\nconst config = {\n ...cfg,\n cvr: {\n ...cfg.cvr,\n db: cfg.cvr.db ?? cfg.upstream.db,\n },\n};\n\nruntimeDebugFlags.trackRowCountsVended = true;\nruntimeDebugFlags.trackRowsVended = config.outputVendedRows;\n\nconst lc = createLogContext({\n log: config.log,\n});\n\ncolorConsole.warn(\n 'DEPRECATED: `analyze-query` is deprecated. Please migrate to a project-specific analyze command built with `runAnalyzeCLI` from `@rocicorp/zero/analyze`.',\n);\n\nif (!fs.existsSync(config.replicaFile)) {\n colorConsole.error(`Replica file ${config.replicaFile} does not exist`);\n process.exit(1);\n}\nconst db = new Database(lc, config.replicaFile);\n\nconst {schema, permissions} = await loadSchemaAndPermissions(\n config.schema.path,\n);\nconst clientSchema = clientSchemaFrom(schema).clientSchema;\n\nconst sources = new Map<string, TableSource>();\nconst clientToServerMapper = clientToServer(schema.tables);\nconst debug = new Debug(config.outputVendedRows);\nconst tableSpecs = computeZqlSpecs(lc, db, {includeBackfillingColumns: false});\n\nclass AnalyzeQueryDelegate extends QueryDelegateBase {\n readonly debug = debug;\n readonly defaultQueryComplete = true;\n readonly disableCorrelatedPredicatePushdown =\n !config.enableCorrelatedPredicatePushdown;\n\n getSource(serverTableName: string): Source | undefined {\n return getOrInsertComputed(sources, serverTableName, serverTableName => {\n const tableSpec = mustGetTableSpec(tableSpecs, serverTableName);\n const {primaryKey} = tableSpec.tableSpec;\n return new TableSource(\n lc,\n config.log,\n db,\n serverTableName,\n tableSpec.zqlSpec,\n primaryKey,\n );\n });\n }\n}\n\nconst host = new AnalyzeQueryDelegate();\n\nlet result: AnalyzeQueryResult;\n\nif (config.ast) {\n result = await runAst(\n lc,\n clientSchema,\n JSON.parse(config.ast),\n true,\n {\n applyPermissions: config.applyPermissions,\n auth: config.authData\n ? {type: 'jwt' as const, raw: '', decoded: JSON.parse(config.authData)}\n : undefined,\n clientToServerMapper,\n permissions,\n syncedRows: config.outputSyncedRows,\n db,\n tableSpecs,\n host,\n },\n async () => {},\n );\n} else if (config.query) {\n result = await runQuery(config.query);\n} else if (config.hash) {\n result = await runHash(config.hash);\n} else {\n colorConsole.error('No query or AST or hash provided');\n process.exit(1);\n}\n\nfunction runQuery(queryString: string): Promise<AnalyzeQueryResult> {\n const z = {\n query: Object.fromEntries(\n Object.entries(schema.tables).map(([name]) => [\n name,\n newQuery(schema, name),\n ]),\n ),\n };\n\n const f = new Function('z', `return z.query.${queryString};`);\n const q: Query<string, Schema, PullRow<string, Schema>> = f(z);\n\n const ast = asQueryInternals(q).ast;\n return runAst(\n lc,\n clientSchema,\n ast,\n false,\n {\n applyPermissions: config.applyPermissions,\n auth: config.authData\n ? {type: 'jwt' as const, raw: '', decoded: JSON.parse(config.authData)}\n : undefined,\n clientToServerMapper,\n permissions,\n syncedRows: config.outputSyncedRows,\n db,\n tableSpecs,\n host,\n },\n async () => {},\n );\n}\n\nasync function runHash(hash: string) {\n const cvrDB = pgClient(\n lc,\n must(config.cvr.db, 'CVR DB must be provided when using the hash option'),\n 'analyze-query-hash-cvr',\n );\n\n const rows = await cvrDB`select \"clientAST\", \"internal\" from ${cvrDB(\n upstreamSchema(getShardID(config)) + '/cvr',\n )}.\"queries\" where \"queryHash\" = ${must(hash)} limit 1;`;\n await cvrDB.end();\n\n colorConsole.log('ZQL from Hash:');\n const ast = rows[0].clientAST as AST;\n colorConsole.log(await formatOutput(ast.table + astToZQL(ast)));\n\n return runAst(\n lc,\n clientSchema,\n ast,\n true,\n {\n applyPermissions: config.applyPermissions,\n auth: config.authData\n ? {type: 'jwt' as const, raw: '', decoded: JSON.parse(config.authData)}\n : undefined,\n clientToServerMapper,\n permissions,\n syncedRows: config.outputSyncedRows,\n db,\n tableSpecs,\n host,\n },\n async () => {},\n );\n}\n\nif (config.outputSyncedRows) {\n colorConsole.log(styleText(['blue', 'bold'], '=== Synced Rows: ===\\n'));\n for (const [table, rows] of Object.entries(result.syncedRows ?? {})) {\n colorConsole.log(styleText('bold', table + ':'), rows);\n }\n}\n\ncolorConsole.log(styleText(['blue', 'bold'], '=== Query Stats: ===\\n'));\ncolorConsole.log(\n styleText('bold', 'total synced rows:'),\n result.syncedRowCount,\n);\nshowStats();\nif (config.outputVendedRows) {\n colorConsole.log(\n styleText(['blue', 'bold'], '=== JS Row Scan Values: ===\\n'),\n );\n for (const source of sources.values()) {\n colorConsole.log(\n styleText('bold', `${source.tableSchema.name}:`),\n debug.getVendedRows()?.[source.tableSchema.name] ?? {},\n );\n }\n}\n\ncolorConsole.log(\n styleText(['blue', 'bold'], '\\n=== Rows Scanned (by SQLite): ===\\n'),\n);\nconst nvisitCounts = debug.getNVisitCounts();\nlet totalNVisit = 0;\nfor (const [table, queries] of Object.entries(nvisitCounts)) {\n colorConsole.log(styleText('bold', `${table}:`), queries);\n for (const count of Object.values(queries)) {\n totalNVisit += count;\n }\n}\ncolorConsole.log(\n styleText('bold', 'total rows scanned:'),\n colorRowsConsidered(totalNVisit),\n);\n\ncolorConsole.log(styleText(['blue', 'bold'], '\\n\\n=== Query Plans: ===\\n'));\nconst fallbackPlans = explainQueries(debug.getVendedRowCounts() ?? {}, db);\nconst capturedPlans = debug.getSQLitePlans();\nconst plans: Record<string, string[]> = {...fallbackPlans, ...capturedPlans};\nfor (const [query, plan] of Object.entries(plans)) {\n colorConsole.log(styleText('bold', 'query'), query);\n colorConsole.log(plan.map((row, i) => colorPlanRow(row, i)).join('\\n'));\n colorConsole.log('\\n');\n}\n\nfunction showStats() {\n let totalRowsConsidered = 0;\n for (const source of sources.values()) {\n const values = Object.values(\n debug.getVendedRowCounts()?.[source.tableSchema.name] ?? {},\n );\n for (const v of values) {\n totalRowsConsidered += v;\n }\n colorConsole.log(\n styleText('bold', source.tableSchema.name + ' vended:'),\n debug.getVendedRowCounts()?.[source.tableSchema.name] ?? {},\n );\n }\n\n colorConsole.log(\n styleText('bold', 'Rows Read (into JS):'),\n colorRowsConsidered(totalRowsConsidered),\n );\n colorConsole.log(\n styleText('bold', 'time:'),\n colorTime(result.end - result.start),\n 'ms',\n );\n}\n\nfunction colorTime(duration: number) {\n if (duration < 100) {\n return styleText('green', duration.toFixed(2) + 'ms');\n } else if (duration < 1000) {\n return styleText('yellow', duration.toFixed(2) + 'ms');\n }\n return styleText('red', duration.toFixed(2) + 'ms');\n}\n\nfunction colorRowsConsidered(n: number) {\n if (n < 1000) {\n return styleText('green', n.toString());\n } else if (n < 10000) {\n return styleText('yellow', n.toString());\n }\n return styleText('red', n.toString());\n}\n\nfunction colorPlanRow(row: string, i: number) {\n if (row.includes('SCAN')) {\n if (i === 0) {\n return styleText('yellow', row);\n }\n return styleText('red', row);\n }\n return styleText('green', row);\n}\n"],"mappings":"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"}
@@ -11,7 +11,7 @@ import { transformAndHashQuery } from "../../zero-cache/src/auth/read-authorizer
11
11
  import { pgClient } from "../../zero-cache/src/types/pg.js";
12
12
  import { LogContext, consoleLogSink } from "@rocicorp/logger";
13
13
  //#region ../analyze-query/src/bin-transform.ts
14
- var config = parseOptions({
14
+ var options = {
15
15
  cvr: { db: valita_exports.string() },
16
16
  schema: {
17
17
  type: valita_exports.string().optional(() => "./schema.ts"),
@@ -23,7 +23,8 @@ var config = parseOptions({
23
23
  type: valita_exports.string().optional(),
24
24
  desc: ["Hash of the query to fetch the AST for."]
25
25
  }
26
- }, { envNamePrefix: ZERO_ENV_VAR_PREFIX });
26
+ };
27
+ var config = parseOptions(options, { envNamePrefix: ZERO_ENV_VAR_PREFIX });
27
28
  var lc = new LogContext("debug", {}, consoleLogSink);
28
29
  var { permissions } = await loadSchemaAndPermissions(config.schema);
29
30
  var cvrDB = pgClient(lc, config.cvr.db, "analyze-query-transform-cvr");
@@ -1 +1 @@
1
- {"version":3,"file":"bin-transform.js","names":[],"sources":["../../../../analyze-query/src/bin-transform.ts"],"sourcesContent":["import '../../shared/src/dotenv.ts';\n\nimport {consoleLogSink, LogContext} from '@rocicorp/logger';\nimport {astToZQL} from '../../ast-to-zql/src/ast-to-zql.ts';\nimport {formatOutput} from '../../ast-to-zql/src/format.ts';\nimport {must} from '../../shared/src/must.ts';\nimport {parseOptions} from '../../shared/src/options.ts';\nimport * as v from '../../shared/src/valita.ts';\nimport {transformAndHashQuery} from '../../zero-cache/src/auth/read-authorizer.ts';\nimport {\n appOptions,\n shardOptions,\n ZERO_ENV_VAR_PREFIX,\n} from '../../zero-cache/src/config/zero-config.ts';\nimport {loadSchemaAndPermissions} from '../../zero-cache/src/scripts/permissions.ts';\nimport {pgClient} from '../../zero-cache/src/types/pg.ts';\nimport {getShardID, upstreamSchema} from '../../zero-cache/src/types/shards.ts';\n\nconst options = {\n cvr: {db: v.string()},\n schema: {\n type: v.string().optional(() => './schema.ts'),\n desc: ['Path to the schema file.'],\n },\n app: appOptions,\n shard: shardOptions,\n hash: {\n type: v.string().optional(),\n desc: ['Hash of the query to fetch the AST for.'],\n },\n};\n\nconst config = parseOptions(options, {envNamePrefix: ZERO_ENV_VAR_PREFIX});\n\nconst lc = new LogContext('debug', {}, consoleLogSink);\nconst {permissions} = await loadSchemaAndPermissions(config.schema);\n\nconst cvrDB = pgClient(lc, config.cvr.db, 'analyze-query-transform-cvr');\n\nconst rows =\n await cvrDB`select \"clientAST\", \"internal\" from ${cvrDB(upstreamSchema(getShardID(config)) + '/cvr')}.\"queries\" where \"queryHash\" = ${must(\n config.hash,\n )} limit 1;`;\n\nconst queryAst = transformAndHashQuery(\n lc,\n '',\n rows[0].clientAST,\n permissions,\n {\n type: 'jwt',\n raw: '',\n decoded: {},\n },\n rows[0].internal,\n).transformedAst;\n\n// oxlint-disable no-console\nconsole.log('\\n=== AST ===\\n');\nconsole.log(JSON.stringify(queryAst, null, 2));\nconsole.log('\\n=== ZQL ===\\n');\nconsole.log(await formatOutput(queryAst.table + astToZQL(queryAst)));\n// oxlint-enable no-console\n\nawait cvrDB.end();\n"],"mappings":";;;;;;;;;;;;;AAgCA,IAAM,SAAS,aAAa;CAb1B,KAAK,EAAC,IAAI,eAAE,OAAO,EAAC;CACpB,QAAQ;EACN,MAAM,eAAE,OAAO,EAAE,eAAe,aAAa;EAC7C,MAAM,CAAC,0BAA0B;CACnC;CACA,KAAK;CACL,OAAO;CACP,MAAM;EACJ,MAAM,eAAE,OAAO,EAAE,SAAS;EAC1B,MAAM,CAAC,yCAAyC;CAClD;AAG0B,GAAS,EAAC,eAAe,oBAAmB,CAAC;AAEzE,IAAM,KAAK,IAAI,WAAW,SAAS,CAAC,GAAG,cAAc;AACrD,IAAM,EAAC,gBAAe,MAAM,yBAAyB,OAAO,MAAM;AAElE,IAAM,QAAQ,SAAS,IAAI,OAAO,IAAI,IAAI,6BAA6B;AAEvE,IAAM,OACJ,MAAM,KAAK,uCAAuC,MAAM,eAAe,WAAW,MAAM,CAAC,IAAI,MAAM,EAAE,iCAAiC,KACpI,OAAO,IACT,EAAE;AAEJ,IAAM,WAAW,sBACf,IACA,IACA,KAAK,GAAG,WACR,aACA;CACE,MAAM;CACN,KAAK;CACL,SAAS,CAAC;AACZ,GACA,KAAK,GAAG,QACV,EAAE;AAGF,QAAQ,IAAI,iBAAiB;AAC7B,QAAQ,IAAI,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AAC7C,QAAQ,IAAI,iBAAiB;AAC7B,QAAQ,IAAI,MAAM,aAAa,SAAS,QAAQ,SAAS,QAAQ,CAAC,CAAC;AAGnE,MAAM,MAAM,IAAI"}
1
+ {"version":3,"file":"bin-transform.js","names":["v"],"sources":["../../../../analyze-query/src/bin-transform.ts"],"sourcesContent":["import '../../shared/src/dotenv.ts';\n\nimport {consoleLogSink, LogContext} from '@rocicorp/logger';\nimport {astToZQL} from '../../ast-to-zql/src/ast-to-zql.ts';\nimport {formatOutput} from '../../ast-to-zql/src/format.ts';\nimport {must} from '../../shared/src/must.ts';\nimport {parseOptions} from '../../shared/src/options.ts';\nimport * as v from '../../shared/src/valita.ts';\nimport {transformAndHashQuery} from '../../zero-cache/src/auth/read-authorizer.ts';\nimport {\n appOptions,\n shardOptions,\n ZERO_ENV_VAR_PREFIX,\n} from '../../zero-cache/src/config/zero-config.ts';\nimport {loadSchemaAndPermissions} from '../../zero-cache/src/scripts/permissions.ts';\nimport {pgClient} from '../../zero-cache/src/types/pg.ts';\nimport {getShardID, upstreamSchema} from '../../zero-cache/src/types/shards.ts';\n\nconst options = {\n cvr: {db: v.string()},\n schema: {\n type: v.string().optional(() => './schema.ts'),\n desc: ['Path to the schema file.'],\n },\n app: appOptions,\n shard: shardOptions,\n hash: {\n type: v.string().optional(),\n desc: ['Hash of the query to fetch the AST for.'],\n },\n};\n\nconst config = parseOptions(options, {envNamePrefix: ZERO_ENV_VAR_PREFIX});\n\nconst lc = new LogContext('debug', {}, consoleLogSink);\nconst {permissions} = await loadSchemaAndPermissions(config.schema);\n\nconst cvrDB = pgClient(lc, config.cvr.db, 'analyze-query-transform-cvr');\n\nconst rows =\n await cvrDB`select \"clientAST\", \"internal\" from ${cvrDB(upstreamSchema(getShardID(config)) + '/cvr')}.\"queries\" where \"queryHash\" = ${must(\n config.hash,\n )} limit 1;`;\n\nconst queryAst = transformAndHashQuery(\n lc,\n '',\n rows[0].clientAST,\n permissions,\n {\n type: 'jwt',\n raw: '',\n decoded: {},\n },\n rows[0].internal,\n).transformedAst;\n\n// oxlint-disable no-console\nconsole.log('\\n=== AST ===\\n');\nconsole.log(JSON.stringify(queryAst, null, 2));\nconsole.log('\\n=== ZQL ===\\n');\nconsole.log(await formatOutput(queryAst.table + astToZQL(queryAst)));\n// oxlint-enable no-console\n\nawait cvrDB.end();\n"],"mappings":";;;;;;;;;;;;;AAkBA,IAAM,UAAU;CACd,KAAK,EAAC,IAAIA,eAAE,OAAO,EAAC;CACpB,QAAQ;EACN,MAAMA,eAAE,OAAO,CAAC,CAAC,eAAe,aAAa;EAC7C,MAAM,CAAC,0BAA0B;CACnC;CACA,KAAK;CACL,OAAO;CACP,MAAM;EACJ,MAAMA,eAAE,OAAO,CAAC,CAAC,SAAS;EAC1B,MAAM,CAAC,yCAAyC;CAClD;AACF;AAEA,IAAM,SAAS,aAAa,SAAS,EAAC,eAAe,oBAAmB,CAAC;AAEzE,IAAM,KAAK,IAAI,WAAW,SAAS,CAAC,GAAG,cAAc;AACrD,IAAM,EAAC,gBAAe,MAAM,yBAAyB,OAAO,MAAM;AAElE,IAAM,QAAQ,SAAS,IAAI,OAAO,IAAI,IAAI,6BAA6B;AAEvE,IAAM,OACJ,MAAM,KAAK,uCAAuC,MAAM,eAAe,WAAW,MAAM,CAAC,IAAI,MAAM,EAAE,iCAAiC,KACpI,OAAO,IACT,EAAE;AAEJ,IAAM,WAAW,sBACf,IACA,IACA,KAAK,EAAE,CAAC,WACR,aACA;CACE,MAAM;CACN,KAAK;CACL,SAAS,CAAC;AACZ,GACA,KAAK,EAAE,CAAC,QACV,CAAC,CAAC;AAGF,QAAQ,IAAI,iBAAiB;AAC7B,QAAQ,IAAI,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AAC7C,QAAQ,IAAI,iBAAiB;AAC7B,QAAQ,IAAI,MAAM,aAAa,SAAS,QAAQ,SAAS,QAAQ,CAAC,CAAC;AAGnE,MAAM,MAAM,IAAI"}
@@ -1 +1 @@
1
- {"version":3,"file":"ast-to-zql.js","names":[],"sources":["../../../../ast-to-zql/src/ast-to-zql.ts"],"sourcesContent":["import {unreachable} from '../../shared/src/asserts.ts';\nimport {toSorted} from '../../shared/src/iterables.ts';\nimport {must} from '../../shared/src/must.ts';\nimport type {\n AST,\n Condition,\n Conjunction,\n CorrelatedSubquery,\n CorrelatedSubqueryCondition,\n Disjunction,\n LiteralReference,\n Ordering,\n Parameter,\n SimpleCondition,\n ValuePosition,\n} from '../../zero-protocol/src/ast.ts';\nimport {SUBQ_PREFIX} from '../../zero-protocol/src/ast.ts';\n\n/**\n * Converts an AST to the equivalent query builder code.\n * This is useful for debugging and understanding queries.\n *\n * @example\n * ```\n * const ast = query.issue.where('id', '=', 123)[astSymbol];\n * console.log(astToZQL(ast)); // outputs: .where('id', '=', 123)\n * ```\n */\nexport function astToZQL(ast: AST, options: AstToZQLOptions = {}): string {\n let code = '';\n\n // Handle where conditions\n if (ast.where) {\n code += transformCondition(ast.where, '.where', new Set(), options);\n }\n\n // Handle related subqueries\n if (ast.related && ast.related.length > 0) {\n for (const related of ast.related) {\n if (related.hidden) {\n const nestedRelated = related.subquery.related?.[0];\n if (nestedRelated) {\n code += transformRelated(nestedRelated, options);\n }\n } else {\n code += transformRelated(related, options);\n }\n }\n }\n\n // Handle orderBy\n if (ast.orderBy && ast.orderBy.length > 0) {\n code += transformOrder(ast.orderBy);\n }\n\n // Handle limit\n if (ast.limit !== undefined) {\n code += `.limit(${ast.limit})`;\n }\n\n // Handle start\n if (ast.start) {\n const {row, exclusive} = ast.start;\n code += `.start(${options.redactLiterals ? '?' : JSON.stringify(row)}${\n exclusive ? '' : ', { inclusive: true }'\n })`;\n }\n\n return code;\n}\n\nexport type AstToZQLOptions = {\n /**\n * Renders every literal value, and the `start` row, as `?` so that the\n * output can be logged without leaking row data or query arguments.\n */\n readonly redactLiterals?: boolean | undefined;\n};\n\ntype Args = Set<string>;\n\ntype Prefix = '.where' | 'cmp';\n\nfunction transformCondition(\n condition: Condition,\n prefix: Prefix,\n args: Args,\n options: AstToZQLOptions,\n): string {\n switch (condition.type) {\n case 'simple':\n return transformSimpleCondition(condition, prefix, options);\n case 'and':\n case 'or':\n return transformLogicalCondition(condition, prefix, args, options);\n case 'correlatedSubquery':\n return transformExistsCondition(condition, prefix, args, options);\n default:\n unreachable(condition);\n }\n}\n\nfunction transformSimpleCondition(\n condition: SimpleCondition,\n prefix: Prefix,\n options: AstToZQLOptions,\n): string {\n const {left, op, right} = condition;\n\n const leftCode = transformValuePosition(left, options);\n const rightCode = transformValuePosition(right, options);\n\n // Handle the shorthand form for equals\n if (op === '=') {\n return `${prefix}(${leftCode}, ${rightCode})`;\n }\n\n return `${prefix}(${leftCode}, '${op}', ${rightCode})`;\n}\n\nfunction transformLogicalCondition(\n condition: Conjunction | Disjunction,\n prefix: Prefix,\n args: Args,\n options: AstToZQLOptions,\n): string {\n const {type, conditions} = condition;\n\n // For single condition, no need for logical operator\n if (conditions.length === 1) {\n return transformCondition(conditions[0], prefix, args, options);\n }\n\n // Generate multiple where calls for top-level AND conditions\n if (type === 'and') {\n const parts = conditions.map(c =>\n transformCondition(c, prefix, args, options),\n );\n // Simply concatenate the where conditions\n if (prefix === '.where') {\n return parts.join('');\n }\n args.add('and');\n return 'and(' + parts.join(', ') + ')';\n }\n\n args = new Set<string>();\n\n // Handle nested conditions with a callback for OR conditions and nested ANDs/ORs\n const conditionsCode = conditions\n .map(c => transformCondition(c, 'cmp', args, options))\n .join(', ');\n\n args.add('cmp');\n args.add(type);\n const argsCode = toSorted(args).join(', ');\n\n return `.where(({${argsCode}}) => ${type}(${conditionsCode}))`;\n}\n\nfunction transformExistsCondition(\n condition: CorrelatedSubqueryCondition,\n prefix: '.where' | 'cmp',\n args: Set<string>,\n options: AstToZQLOptions,\n): string {\n const {related, op} = condition;\n const relationship = extractRelationshipName(related);\n\n const nextSubquery = getNextExistsSubquery(related);\n\n // Check if subquery has additional properties\n const hasSubQueryProps =\n nextSubquery.where ||\n (nextSubquery.related && nextSubquery.related.length > 0) ||\n nextSubquery.orderBy ||\n nextSubquery.limit;\n\n // Build options string for flip and scalar\n const optionParts: string[] = [];\n if (condition.flip !== undefined) {\n optionParts.push(`flip: ${condition.flip}`);\n }\n if (condition.scalar !== undefined) {\n optionParts.push(`scalar: ${condition.scalar}`);\n }\n const optionsStr =\n optionParts.length > 0 ? `, {${optionParts.join(', ')}}` : '';\n\n if (op === 'EXISTS') {\n if (!hasSubQueryProps) {\n if (prefix === '.where') {\n return `.whereExists('${relationship}'${optionsStr})`;\n }\n args.add('exists');\n return `exists('${relationship}'${optionsStr})`;\n }\n\n if (prefix === '.where') {\n return `.whereExists('${relationship}', q => q${astToZQL(nextSubquery, options)}${optionsStr})`;\n }\n prefix satisfies 'cmp';\n args.add('exists');\n return `exists('${relationship}', q => q${astToZQL(nextSubquery, options)}${optionsStr})`;\n }\n\n op satisfies 'NOT EXISTS';\n\n if (hasSubQueryProps) {\n if (prefix === '.where') {\n return `.where(({exists, not}) => not(exists('${relationship}', q => q${astToZQL(\n nextSubquery,\n options,\n )}${optionsStr})))`;\n }\n prefix satisfies 'cmp';\n args.add('not');\n args.add('exists');\n return `not(exists('${relationship}', q => q${astToZQL(nextSubquery, options)}${optionsStr}))`;\n }\n\n if (prefix === '.where') {\n return `.where(({exists, not}) => not(exists('${relationship}'${optionsStr})))`;\n }\n args.add('not');\n args.add('exists');\n\n return `not(exists('${relationship}'${optionsStr})))`;\n}\n\n// If the `exists` is applied against a junction edge, both hops will have the same alias and both hops will be exists conditions.\nfunction getNextExistsSubquery(related: CorrelatedSubquery): AST {\n if (\n related.subquery.where?.type === 'correlatedSubquery' &&\n related.subquery.where.related.subquery.alias?.includes(\n SUBQ_PREFIX + 'zhidden_',\n )\n ) {\n return getNextExistsSubquery(related.subquery.where.related);\n }\n\n return related.subquery;\n}\n\nfunction extractRelationshipName(related: CorrelatedSubquery): string {\n const alias = must(related.subquery.alias);\n return alias.startsWith(SUBQ_PREFIX)\n ? alias.substring(SUBQ_PREFIX.length)\n : alias;\n}\n\nfunction transformRelated(\n related: CorrelatedSubquery,\n options: AstToZQLOptions,\n): string {\n const {alias} = related.subquery;\n if (!alias) return '';\n\n const relationship = alias;\n let code = `.related('${relationship}'`;\n\n // If the subquery has additional filters or configurations\n if (\n related.subquery.where ||\n (related.subquery.related && related.subquery.related.length > 0) ||\n related.subquery.orderBy ||\n related.subquery.limit\n ) {\n code += ', q => q' + astToZQL(related.subquery, options);\n }\n\n code += ')';\n return code;\n}\n\nfunction transformOrder(orderBy: Ordering): string {\n let code = '';\n for (const [field, direction] of orderBy) {\n code += `.orderBy('${field}', '${direction}')`;\n }\n return code;\n}\n\nfunction transformValuePosition(\n value: ValuePosition,\n options: AstToZQLOptions,\n): string {\n switch (value.type) {\n case 'literal':\n return options.redactLiterals ? '?' : transformLiteral(value);\n case 'column':\n return `'${value.name}'`;\n case 'static':\n return transformParameter(value);\n default:\n unreachable(value);\n }\n}\n\nfunction transformLiteral(literal: LiteralReference): string {\n if (literal.value === null) {\n return 'null';\n }\n if (Array.isArray(literal.value)) {\n return JSON.stringify(literal.value);\n }\n if (typeof literal.value === 'string') {\n return `'${literal.value.replace(/'/g, \"\\\\'\")}'`;\n }\n return String(literal.value);\n}\n\nfunction transformParameter(param: Parameter): string {\n const fieldStr = Array.isArray(param.field)\n ? `[${param.field.map(f => `'${f}'`).join(', ')}]`\n : `'${param.field}'`;\n\n return `authParam(${fieldStr})`;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ast-to-zql.js","names":["relationship"],"sources":["../../../../ast-to-zql/src/ast-to-zql.ts"],"sourcesContent":["import {unreachable} from '../../shared/src/asserts.ts';\nimport {toSorted} from '../../shared/src/iterables.ts';\nimport {must} from '../../shared/src/must.ts';\nimport type {\n AST,\n Condition,\n Conjunction,\n CorrelatedSubquery,\n CorrelatedSubqueryCondition,\n Disjunction,\n LiteralReference,\n Ordering,\n Parameter,\n SimpleCondition,\n ValuePosition,\n} from '../../zero-protocol/src/ast.ts';\nimport {SUBQ_PREFIX} from '../../zero-protocol/src/ast.ts';\n\n/**\n * Converts an AST to the equivalent query builder code.\n * This is useful for debugging and understanding queries.\n *\n * @example\n * ```\n * const ast = query.issue.where('id', '=', 123)[astSymbol];\n * console.log(astToZQL(ast)); // outputs: .where('id', '=', 123)\n * ```\n */\nexport function astToZQL(ast: AST, options: AstToZQLOptions = {}): string {\n let code = '';\n\n // Handle where conditions\n if (ast.where) {\n code += transformCondition(ast.where, '.where', new Set(), options);\n }\n\n // Handle related subqueries\n if (ast.related && ast.related.length > 0) {\n for (const related of ast.related) {\n if (related.hidden) {\n const nestedRelated = related.subquery.related?.[0];\n if (nestedRelated) {\n code += transformRelated(nestedRelated, options);\n }\n } else {\n code += transformRelated(related, options);\n }\n }\n }\n\n // Handle orderBy\n if (ast.orderBy && ast.orderBy.length > 0) {\n code += transformOrder(ast.orderBy);\n }\n\n // Handle limit\n if (ast.limit !== undefined) {\n code += `.limit(${ast.limit})`;\n }\n\n // Handle start\n if (ast.start) {\n const {row, exclusive} = ast.start;\n code += `.start(${options.redactLiterals ? '?' : JSON.stringify(row)}${\n exclusive ? '' : ', { inclusive: true }'\n })`;\n }\n\n return code;\n}\n\nexport type AstToZQLOptions = {\n /**\n * Renders every literal value, and the `start` row, as `?` so that the\n * output can be logged without leaking row data or query arguments.\n */\n readonly redactLiterals?: boolean | undefined;\n};\n\ntype Args = Set<string>;\n\ntype Prefix = '.where' | 'cmp';\n\nfunction transformCondition(\n condition: Condition,\n prefix: Prefix,\n args: Args,\n options: AstToZQLOptions,\n): string {\n switch (condition.type) {\n case 'simple':\n return transformSimpleCondition(condition, prefix, options);\n case 'and':\n case 'or':\n return transformLogicalCondition(condition, prefix, args, options);\n case 'correlatedSubquery':\n return transformExistsCondition(condition, prefix, args, options);\n default:\n unreachable(condition);\n }\n}\n\nfunction transformSimpleCondition(\n condition: SimpleCondition,\n prefix: Prefix,\n options: AstToZQLOptions,\n): string {\n const {left, op, right} = condition;\n\n const leftCode = transformValuePosition(left, options);\n const rightCode = transformValuePosition(right, options);\n\n // Handle the shorthand form for equals\n if (op === '=') {\n return `${prefix}(${leftCode}, ${rightCode})`;\n }\n\n return `${prefix}(${leftCode}, '${op}', ${rightCode})`;\n}\n\nfunction transformLogicalCondition(\n condition: Conjunction | Disjunction,\n prefix: Prefix,\n args: Args,\n options: AstToZQLOptions,\n): string {\n const {type, conditions} = condition;\n\n // For single condition, no need for logical operator\n if (conditions.length === 1) {\n return transformCondition(conditions[0], prefix, args, options);\n }\n\n // Generate multiple where calls for top-level AND conditions\n if (type === 'and') {\n const parts = conditions.map(c =>\n transformCondition(c, prefix, args, options),\n );\n // Simply concatenate the where conditions\n if (prefix === '.where') {\n return parts.join('');\n }\n args.add('and');\n return 'and(' + parts.join(', ') + ')';\n }\n\n args = new Set<string>();\n\n // Handle nested conditions with a callback for OR conditions and nested ANDs/ORs\n const conditionsCode = conditions\n .map(c => transformCondition(c, 'cmp', args, options))\n .join(', ');\n\n args.add('cmp');\n args.add(type);\n const argsCode = toSorted(args).join(', ');\n\n return `.where(({${argsCode}}) => ${type}(${conditionsCode}))`;\n}\n\nfunction transformExistsCondition(\n condition: CorrelatedSubqueryCondition,\n prefix: '.where' | 'cmp',\n args: Set<string>,\n options: AstToZQLOptions,\n): string {\n const {related, op} = condition;\n const relationship = extractRelationshipName(related);\n\n const nextSubquery = getNextExistsSubquery(related);\n\n // Check if subquery has additional properties\n const hasSubQueryProps =\n nextSubquery.where ||\n (nextSubquery.related && nextSubquery.related.length > 0) ||\n nextSubquery.orderBy ||\n nextSubquery.limit;\n\n // Build options string for flip and scalar\n const optionParts: string[] = [];\n if (condition.flip !== undefined) {\n optionParts.push(`flip: ${condition.flip}`);\n }\n if (condition.scalar !== undefined) {\n optionParts.push(`scalar: ${condition.scalar}`);\n }\n const optionsStr =\n optionParts.length > 0 ? `, {${optionParts.join(', ')}}` : '';\n\n if (op === 'EXISTS') {\n if (!hasSubQueryProps) {\n if (prefix === '.where') {\n return `.whereExists('${relationship}'${optionsStr})`;\n }\n args.add('exists');\n return `exists('${relationship}'${optionsStr})`;\n }\n\n if (prefix === '.where') {\n return `.whereExists('${relationship}', q => q${astToZQL(nextSubquery, options)}${optionsStr})`;\n }\n prefix satisfies 'cmp';\n args.add('exists');\n return `exists('${relationship}', q => q${astToZQL(nextSubquery, options)}${optionsStr})`;\n }\n\n op satisfies 'NOT EXISTS';\n\n if (hasSubQueryProps) {\n if (prefix === '.where') {\n return `.where(({exists, not}) => not(exists('${relationship}', q => q${astToZQL(\n nextSubquery,\n options,\n )}${optionsStr})))`;\n }\n prefix satisfies 'cmp';\n args.add('not');\n args.add('exists');\n return `not(exists('${relationship}', q => q${astToZQL(nextSubquery, options)}${optionsStr}))`;\n }\n\n if (prefix === '.where') {\n return `.where(({exists, not}) => not(exists('${relationship}'${optionsStr})))`;\n }\n args.add('not');\n args.add('exists');\n\n return `not(exists('${relationship}'${optionsStr})))`;\n}\n\n// If the `exists` is applied against a junction edge, both hops will have the same alias and both hops will be exists conditions.\nfunction getNextExistsSubquery(related: CorrelatedSubquery): AST {\n if (\n related.subquery.where?.type === 'correlatedSubquery' &&\n related.subquery.where.related.subquery.alias?.includes(\n SUBQ_PREFIX + 'zhidden_',\n )\n ) {\n return getNextExistsSubquery(related.subquery.where.related);\n }\n\n return related.subquery;\n}\n\nfunction extractRelationshipName(related: CorrelatedSubquery): string {\n const alias = must(related.subquery.alias);\n return alias.startsWith(SUBQ_PREFIX)\n ? alias.substring(SUBQ_PREFIX.length)\n : alias;\n}\n\nfunction transformRelated(\n related: CorrelatedSubquery,\n options: AstToZQLOptions,\n): string {\n const {alias} = related.subquery;\n if (!alias) return '';\n\n const relationship = alias;\n let code = `.related('${relationship}'`;\n\n // If the subquery has additional filters or configurations\n if (\n related.subquery.where ||\n (related.subquery.related && related.subquery.related.length > 0) ||\n related.subquery.orderBy ||\n related.subquery.limit\n ) {\n code += ', q => q' + astToZQL(related.subquery, options);\n }\n\n code += ')';\n return code;\n}\n\nfunction transformOrder(orderBy: Ordering): string {\n let code = '';\n for (const [field, direction] of orderBy) {\n code += `.orderBy('${field}', '${direction}')`;\n }\n return code;\n}\n\nfunction transformValuePosition(\n value: ValuePosition,\n options: AstToZQLOptions,\n): string {\n switch (value.type) {\n case 'literal':\n return options.redactLiterals ? '?' : transformLiteral(value);\n case 'column':\n return `'${value.name}'`;\n case 'static':\n return transformParameter(value);\n default:\n unreachable(value);\n }\n}\n\nfunction transformLiteral(literal: LiteralReference): string {\n if (literal.value === null) {\n return 'null';\n }\n if (Array.isArray(literal.value)) {\n return JSON.stringify(literal.value);\n }\n if (typeof literal.value === 'string') {\n return `'${literal.value.replace(/'/g, \"\\\\'\")}'`;\n }\n return String(literal.value);\n}\n\nfunction transformParameter(param: Parameter): string {\n const fieldStr = Array.isArray(param.field)\n ? `[${param.field.map(f => `'${f}'`).join(', ')}]`\n : `'${param.field}'`;\n\n return `authParam(${fieldStr})`;\n}\n"],"mappings":"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"}
@@ -9,10 +9,11 @@ import process from "node:process";
9
9
  import { readFile } from "node:fs/promises";
10
10
  import { createInterface } from "node:readline";
11
11
  //#region ../ast-to-zql/src/bin.ts
12
- var config = parseOptions({ schema: {
12
+ var options = { schema: {
13
13
  type: valita_exports.string().optional(),
14
14
  desc: ["Path to the schema file. Use this to re-map the AST to client names."]
15
- } });
15
+ } };
16
+ var config = parseOptions(options);
16
17
  var schema;
17
18
  if (config.schema) schema = (await loadSchemaAndPermissions(config.schema)).schema;
18
19
  function isStdinPiped() {
@@ -1 +1 @@
1
- {"version":3,"file":"bin.js","names":[],"sources":["../../../../ast-to-zql/src/bin.ts"],"sourcesContent":["/* oxlint-disable no-console */\nimport {readFile} from 'node:fs/promises';\nimport process from 'node:process';\nimport {createInterface} from 'node:readline';\nimport {parseOptions} from '../../shared/src/options.ts';\nimport * as v from '../../shared/src/valita.ts';\nimport {loadSchemaAndPermissions} from '../../zero-cache/src/scripts/permissions.ts';\nimport {mapAST} from '../../zero-protocol/src/ast.ts';\nimport {serverToClient} from '../../zero-schema/src/name-mapper.ts';\nimport type {Schema} from '../../zero-types/src/schema.ts';\nimport {astToZQL} from './ast-to-zql.ts';\nimport {formatOutput} from './format.ts';\n\nconst options = {\n schema: {\n type: v.string().optional(),\n desc: [\n 'Path to the schema file. Use this to re-map the AST to client names.',\n ],\n },\n};\n\nconst config = parseOptions(options);\n\nlet schema: Schema | undefined;\nif (config.schema) {\n schema = (await loadSchemaAndPermissions(config.schema)).schema;\n}\n\nfunction isStdinPiped(): boolean {\n return !process.stdin.isTTY;\n}\n\nfunction readFromStdin(): Promise<string> {\n return new Promise(resolve => {\n let data = '';\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n terminal: false,\n });\n\n rl.on('line', line => {\n data += line + '\\n';\n });\n\n rl.on('close', () => {\n resolve(data);\n });\n });\n}\n\nasync function main(): Promise<void> {\n try {\n let input: string;\n\n if (isStdinPiped()) {\n input = await readFromStdin();\n } else if (process.argv.length > 2) {\n const filePath = process.argv[2];\n input = await readFile(filePath, 'utf-8');\n } else {\n console.error('Error: No input provided.');\n console.error('Usage:');\n console.error(` cat ast.json | npx ast-to-zql`);\n console.error(` npx ast-to-zql ast.json`);\n process.exit(1);\n }\n\n let ast = JSON.parse(input);\n if (schema) {\n const mapper = serverToClient(schema.tables);\n ast = mapAST(ast, mapper);\n }\n\n const zql = astToZQL(ast);\n const code = `query.${ast.table}${zql}`;\n\n console.log(await formatOutput(code));\n } catch (error) {\n console.error('Error processing input:', error);\n process.exit(1);\n }\n}\n\nawait main();\n"],"mappings":";;;;;;;;;;;AAsBA,IAAM,SAAS,aAAa,EAR1B,QAAQ;CACN,MAAM,eAAE,OAAO,EAAE,SAAS;CAC1B,MAAM,CACJ,sEACF;AACF,EAG0B,CAAO;AAEnC,IAAI;AACJ,IAAI,OAAO,QACT,UAAU,MAAM,yBAAyB,OAAO,MAAM,GAAG;AAG3D,SAAS,eAAwB;CAC/B,OAAO,CAAC,QAAQ,MAAM;AACxB;AAEA,SAAS,gBAAiC;CACxC,OAAO,IAAI,SAAQ,YAAW;EAC5B,IAAI,OAAO;EACX,MAAM,KAAK,gBAAgB;GACzB,OAAO,QAAQ;GACf,QAAQ,QAAQ;GAChB,UAAU;EACZ,CAAC;EAED,GAAG,GAAG,SAAQ,SAAQ;GACpB,QAAQ,OAAO;EACjB,CAAC;EAED,GAAG,GAAG,eAAe;GACnB,QAAQ,IAAI;EACd,CAAC;CACH,CAAC;AACH;AAEA,eAAe,OAAsB;CACnC,IAAI;EACF,IAAI;EAEJ,IAAI,aAAa,GACf,QAAQ,MAAM,cAAc;OACvB,IAAI,QAAQ,KAAK,SAAS,GAAG;GAClC,MAAM,WAAW,QAAQ,KAAK;GAC9B,QAAQ,MAAM,SAAS,UAAU,OAAO;EAC1C,OAAO;GACL,QAAQ,MAAM,2BAA2B;GACzC,QAAQ,MAAM,QAAQ;GACtB,QAAQ,MAAM,iCAAiC;GAC/C,QAAQ,MAAM,2BAA2B;GACzC,QAAQ,KAAK,CAAC;EAChB;EAEA,IAAI,MAAM,KAAK,MAAM,KAAK;EAC1B,IAAI,QAAQ;GACV,MAAM,SAAS,eAAe,OAAO,MAAM;GAC3C,MAAM,OAAO,KAAK,MAAM;EAC1B;EAEA,MAAM,MAAM,SAAS,GAAG;EACxB,MAAM,OAAO,SAAS,IAAI,QAAQ;EAElC,QAAQ,IAAI,MAAM,aAAa,IAAI,CAAC;CACtC,SAAS,OAAO;EACd,QAAQ,MAAM,2BAA2B,KAAK;EAC9C,QAAQ,KAAK,CAAC;CAChB;AACF;AAEA,MAAM,KAAK"}
1
+ {"version":3,"file":"bin.js","names":["v"],"sources":["../../../../ast-to-zql/src/bin.ts"],"sourcesContent":["/* oxlint-disable no-console */\nimport {readFile} from 'node:fs/promises';\nimport process from 'node:process';\nimport {createInterface} from 'node:readline';\nimport {parseOptions} from '../../shared/src/options.ts';\nimport * as v from '../../shared/src/valita.ts';\nimport {loadSchemaAndPermissions} from '../../zero-cache/src/scripts/permissions.ts';\nimport {mapAST} from '../../zero-protocol/src/ast.ts';\nimport {serverToClient} from '../../zero-schema/src/name-mapper.ts';\nimport type {Schema} from '../../zero-types/src/schema.ts';\nimport {astToZQL} from './ast-to-zql.ts';\nimport {formatOutput} from './format.ts';\n\nconst options = {\n schema: {\n type: v.string().optional(),\n desc: [\n 'Path to the schema file. Use this to re-map the AST to client names.',\n ],\n },\n};\n\nconst config = parseOptions(options);\n\nlet schema: Schema | undefined;\nif (config.schema) {\n schema = (await loadSchemaAndPermissions(config.schema)).schema;\n}\n\nfunction isStdinPiped(): boolean {\n return !process.stdin.isTTY;\n}\n\nfunction readFromStdin(): Promise<string> {\n return new Promise(resolve => {\n let data = '';\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n terminal: false,\n });\n\n rl.on('line', line => {\n data += line + '\\n';\n });\n\n rl.on('close', () => {\n resolve(data);\n });\n });\n}\n\nasync function main(): Promise<void> {\n try {\n let input: string;\n\n if (isStdinPiped()) {\n input = await readFromStdin();\n } else if (process.argv.length > 2) {\n const filePath = process.argv[2];\n input = await readFile(filePath, 'utf-8');\n } else {\n console.error('Error: No input provided.');\n console.error('Usage:');\n console.error(` cat ast.json | npx ast-to-zql`);\n console.error(` npx ast-to-zql ast.json`);\n process.exit(1);\n }\n\n let ast = JSON.parse(input);\n if (schema) {\n const mapper = serverToClient(schema.tables);\n ast = mapAST(ast, mapper);\n }\n\n const zql = astToZQL(ast);\n const code = `query.${ast.table}${zql}`;\n\n console.log(await formatOutput(code));\n } catch (error) {\n console.error('Error processing input:', error);\n process.exit(1);\n }\n}\n\nawait main();\n"],"mappings":";;;;;;;;;;;AAaA,IAAM,UAAU,EACd,QAAQ;CACN,MAAMA,eAAE,OAAO,CAAC,CAAC,SAAS;CAC1B,MAAM,CACJ,sEACF;AACF,EACF;AAEA,IAAM,SAAS,aAAa,OAAO;AAEnC,IAAI;AACJ,IAAI,OAAO,QACT,UAAU,MAAM,yBAAyB,OAAO,MAAM,EAAA,CAAG;AAG3D,SAAS,eAAwB;CAC/B,OAAO,CAAC,QAAQ,MAAM;AACxB;AAEA,SAAS,gBAAiC;CACxC,OAAO,IAAI,SAAQ,YAAW;EAC5B,IAAI,OAAO;EACX,MAAM,KAAK,gBAAgB;GACzB,OAAO,QAAQ;GACf,QAAQ,QAAQ;GAChB,UAAU;EACZ,CAAC;EAED,GAAG,GAAG,SAAQ,SAAQ;GACpB,QAAQ,OAAO;EACjB,CAAC;EAED,GAAG,GAAG,eAAe;GACnB,QAAQ,IAAI;EACd,CAAC;CACH,CAAC;AACH;AAEA,eAAe,OAAsB;CACnC,IAAI;EACF,IAAI;EAEJ,IAAI,aAAa,GACf,QAAQ,MAAM,cAAc;OACvB,IAAI,QAAQ,KAAK,SAAS,GAAG;GAClC,MAAM,WAAW,QAAQ,KAAK;GAC9B,QAAQ,MAAM,SAAS,UAAU,OAAO;EAC1C,OAAO;GACL,QAAQ,MAAM,2BAA2B;GACzC,QAAQ,MAAM,QAAQ;GACtB,QAAQ,MAAM,iCAAiC;GAC/C,QAAQ,MAAM,2BAA2B;GACzC,QAAQ,KAAK,CAAC;EAChB;EAEA,IAAI,MAAM,KAAK,MAAM,KAAK;EAC1B,IAAI,QAAQ;GACV,MAAM,SAAS,eAAe,OAAO,MAAM;GAC3C,MAAM,OAAO,KAAK,MAAM;EAC1B;EAEA,MAAM,MAAM,SAAS,GAAG;EACxB,MAAM,OAAO,SAAS,IAAI,QAAQ;EAElC,QAAQ,IAAI,MAAM,aAAa,IAAI,CAAC;CACtC,SAAS,OAAO;EACd,QAAQ,MAAM,2BAA2B,KAAK;EAC9C,QAAQ,KAAK,CAAC;CAChB;AACF;AAEA,MAAM,KAAK"}
@@ -1 +1 @@
1
- {"version":3,"file":"format.js","names":[],"sources":["../../../../ast-to-zql/src/format.ts"],"sourcesContent":["import {format} from 'oxfmt';\n\nexport async function formatOutput(content: string): Promise<string> {\n try {\n const result = await format('output.ts', content, {\n semi: false,\n printWidth: 80,\n });\n return result.code;\n } catch (error) {\n // oxlint-disable-next-line no-console\n console.warn('Warning: Unable to format output with oxfmt:', error);\n return content;\n }\n}\n"],"mappings":";;AAEA,eAAsB,aAAa,SAAkC;CACnE,IAAI;EAKF,QAAO,MAJc,OAAO,aAAa,SAAS;GAChD,MAAM;GACN,YAAY;EACd,CAAC,GACa;CAChB,SAAS,OAAO;EAEd,QAAQ,KAAK,gDAAgD,KAAK;EAClE,OAAO;CACT;AACF"}
1
+ {"version":3,"file":"format.js","names":[],"sources":["../../../../ast-to-zql/src/format.ts"],"sourcesContent":["import {format} from 'oxfmt';\n\nexport async function formatOutput(content: string): Promise<string> {\n try {\n const result = await format('output.ts', content, {\n semi: false,\n printWidth: 80,\n });\n return result.code;\n } catch (error) {\n // oxlint-disable-next-line no-console\n console.warn('Warning: Unable to format output with oxfmt:', error);\n return content;\n }\n}\n"],"mappings":";;AAEA,eAAsB,aAAa,SAAkC;CACnE,IAAI;EAKF,QAAO,MAJc,OAAO,aAAa,SAAS;GAChD,MAAM;GACN,YAAY;EACd,CAAC,EAAA,CACa;CAChB,SAAS,OAAO;EAEd,QAAQ,KAAK,gDAAgD,KAAK;EAClE,OAAO;CACT;AACF"}
@@ -1,9 +1,10 @@
1
1
  import { Lock } from "@rocicorp/lock";
2
2
  //#region ../datadog/src/datadog-log-sink.ts
3
3
  var DD_BASE_URL = new URL("https://http-intake.logs.datadoghq.com/api/v2/logs");
4
- var MAX_ENTRY_BYTES = 5 * 1024 * 1024;
4
+ var MAX_LOG_ENTRIES_PER_FLUSH = 1e3;
5
+ var MAX_ENTRY_BYTES = 5242880;
5
6
  var MAX_MESSAGE_RETRIES = 2;
6
- MAX_ENTRY_BYTES / 4;
7
+ var MAX_ENTRY_CHARS = MAX_ENTRY_BYTES / 4;
7
8
  var DatadogLogSink = class {
8
9
  #messages = [];
9
10
  #apiKey;
@@ -88,7 +89,7 @@ var DatadogLogSink = class {
88
89
  if (m.flushRetryCount > MAX_MESSAGE_RETRIES) numWithTooManyRetries++;
89
90
  }
90
91
  if (numWithTooManyRetries > 0) {
91
- console.error(`Dropping ${numWithTooManyRetries} datadog log messages which failed to send ${MAX_MESSAGE_RETRIES + 1} times.`);
92
+ console.error(`Dropping ${numWithTooManyRetries} datadog log messages which failed to send 3 times.`);
92
93
  this.#messages.splice(0, numWithTooManyRetries);
93
94
  }
94
95
  }
@@ -153,6 +154,6 @@ function makeMessage(message, context, logLevel) {
153
154
  return msg;
154
155
  }
155
156
  //#endregion
156
- export { DatadogLogSink };
157
+ export { DatadogLogSink, MAX_ENTRY_CHARS, MAX_LOG_ENTRIES_PER_FLUSH };
157
158
 
158
159
  //# sourceMappingURL=datadog-log-sink.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"datadog-log-sink.js","names":["#apiKey","#source","#service","#host","#version","#interval","#baseURL","#messages","#startTimer","#timerID","#flushLock"],"sources":["../../../../datadog/src/datadog-log-sink.ts"],"sourcesContent":["import {Lock} from '@rocicorp/lock';\nimport type {Context, LogLevel, LogSink} from '@rocicorp/logger';\n\nexport interface DatadogLogSinkOptions {\n apiKey?: string | undefined;\n source?: string | undefined;\n service?: string | undefined;\n host?: string | undefined;\n version?: string | undefined;\n interval?: number | undefined;\n baseURL?: URL | undefined;\n}\n\nconst DD_BASE_URL = new URL(\n 'https://http-intake.logs.datadoghq.com/api/v2/logs',\n);\n\n// https://docs.datadoghq.com/api/latest/logs/\nexport const MAX_LOG_ENTRIES_PER_FLUSH = 1000;\nexport const FORCE_FLUSH_THRESHOLD = 250;\nconst MAX_ENTRY_BYTES = 5 * 1024 * 1024;\nconst MAX_MESSAGE_RETRIES = 2;\n\n// Conservative limit that assumes all chars are encoded as 4 UTF-8 bytes.\n// This makes the actual limit somewhere closer to 1.25 MB, which is still\n// a reasonable amount of log data to send per request.\nexport const MAX_ENTRY_CHARS = MAX_ENTRY_BYTES / 4;\n\nexport class DatadogLogSink implements LogSink {\n #messages: Message[] = [];\n readonly #apiKey: string | undefined;\n readonly #source: string | undefined;\n readonly #service: string | undefined;\n readonly #host: string | undefined;\n readonly #version: string | undefined;\n readonly #interval: number;\n readonly #baseURL: string;\n #timerID: ReturnType<typeof setTimeout> | 0 = 0;\n #flushLock = new Lock();\n\n constructor(options: DatadogLogSinkOptions) {\n const {\n apiKey,\n source,\n service,\n host,\n version,\n interval = 5_000,\n baseURL: baseUrl = DD_BASE_URL,\n } = options;\n\n this.#apiKey = apiKey;\n this.#source = source;\n this.#service = service;\n this.#host = host;\n this.#version = version;\n this.#interval = interval;\n this.#baseURL = baseUrl.toString();\n }\n\n log(level: LogLevel, context: Context | undefined, ...args: unknown[]): void {\n this.#messages.push(makeMessage(args, context, level));\n if (level === 'error' || this.#messages.length === FORCE_FLUSH_THRESHOLD) {\n // Do not await. Later calls to flush will await as needed.\n void this.flush();\n } else {\n this.#startTimer();\n }\n }\n #startTimer() {\n if (this.#timerID) {\n return;\n }\n\n this.#timerID = setTimeout(() => {\n this.#timerID = 0;\n\n void this.flush();\n }, this.#interval);\n }\n\n flush(): Promise<void> {\n return this.#flushLock.withLock(async () => {\n const {length} = this.#messages;\n if (length === 0) {\n return;\n }\n do {\n const flushTime = Date.now();\n const stringified = [];\n let totalBytes = 0;\n\n for (const m of this.#messages) {\n // As a small perf optimization, we directly mutate\n // the message rather than making a shallow copy.\n // The LOG_SINK_FLUSH_DELAY_ATTRIBUTE will be clobbered by\n // the next flush if this flush fails (which is the desired behavior).\n m.flushDelayMs = flushTime - m.date;\n\n let str = JSON.stringify(m);\n if (str.length > MAX_ENTRY_CHARS) {\n // A single message above the total payload limit will otherwise halt\n // log flushing progress. Drop and replace with a message indicating so.\n m.message = `[Dropped message of length ${str.length}]`;\n str = JSON.stringify(m);\n }\n // Calculate the totalBytes with the newline characters between messages.\n if (str.length + totalBytes + stringified.length > MAX_ENTRY_CHARS) {\n break;\n }\n totalBytes += str.length;\n stringified.push(str);\n\n if (stringified.length === MAX_LOG_ENTRIES_PER_FLUSH) {\n break;\n }\n }\n\n const body = stringified.join('\\n');\n const url = new URL(this.#baseURL);\n if (this.#apiKey !== undefined) {\n url.searchParams.set('dd-api-key', this.#apiKey);\n }\n\n if (this.#source) {\n // Both need to be set for server to treat us as the browser SDK for\n // value 'browser'.\n url.searchParams.set('ddsource', this.#source);\n url.searchParams.set('dd-evp-origin', this.#source);\n }\n\n if (this.#service) {\n url.searchParams.set('service', this.#service);\n }\n\n if (this.#host) {\n url.searchParams.set('host', this.#host);\n }\n\n if (this.#version) {\n url.searchParams.set('ddtags', `version:${this.#version}`);\n }\n\n let ok = false;\n try {\n const response = await fetch(url.toString(), {\n method: 'POST',\n body,\n keepalive: true,\n } as RequestInit);\n\n ok = response.ok;\n if (!ok) {\n // Log to console so that we might catch this in `wrangler tail`.\n // oxlint-disable-next-line no-console\n console.error(\n 'response',\n response.status,\n response.statusText,\n await response.text,\n );\n }\n } catch (e) {\n // Log to console so that we might catch this in `wrangler tail`.\n // oxlint-disable-next-line no-console\n console.error('Log flush to datadog failed', e);\n }\n\n if (ok) {\n // Remove messages that were successfully flushed.\n this.#messages.splice(0, stringified.length);\n } else {\n let numWithTooManyRetries = 0;\n for (let i = 0; i < stringified.length; i++) {\n const m = this.#messages[i];\n m.flushRetryCount = (m.flushRetryCount ?? 0) + 1;\n if (m.flushRetryCount > MAX_MESSAGE_RETRIES) {\n numWithTooManyRetries++;\n }\n }\n if (numWithTooManyRetries > 0) {\n // oxlint-disable-next-line no-console\n console.error(\n `Dropping ${numWithTooManyRetries} datadog log messages which failed to send ${\n MAX_MESSAGE_RETRIES + 1\n } times.`,\n );\n // Remove messages that have failed too many times.\n this.#messages.splice(0, numWithTooManyRetries);\n }\n }\n } while (this.#messages.length >= FORCE_FLUSH_THRESHOLD);\n // If any messages left at this point schedule another flush.\n if (this.#messages.length) {\n this.#startTimer();\n }\n });\n }\n}\n\ntype Message = Context & {\n status: LogLevel;\n date: number;\n message: unknown;\n error?: {origin: 'logger'};\n flushDelayMs?: number;\n flushRetryCount?: number;\n};\n\nfunction flattenMessage(message: unknown): unknown {\n if (Array.isArray(message) && message.length === 1) {\n return flattenMessage(message[0]);\n }\n return message;\n}\n\nfunction convertError(e: Error): {\n name: string;\n message: string;\n stack: string | undefined;\n} {\n return {\n name: e.name,\n message: e.message,\n stack: e.stack,\n };\n}\n\nfunction convertErrors(message: unknown): unknown {\n if (message instanceof Error) {\n return convertError(message);\n }\n if (message instanceof Array) {\n const convertedMessage: unknown[] = [];\n for (const item of message) {\n if (item instanceof Error) {\n convertedMessage.push(convertError(item));\n } else {\n convertedMessage.push(item);\n }\n }\n return convertedMessage;\n }\n return message;\n}\n\nconst LOG_SINK_FLUSH_RETRY_COUNT = 'flushRetryCount';\nconst LOG_SINK_FLUSH_DELAY_ATTRIBUTE = 'flushDelayMs';\n// This code assumes that no context keys will start with\n// @DATADOG_RESERVED_ (a fairly safe assumption).\nconst RESERVED_KEY_PREFIX = '@DATADOG_RESERVED_';\n// See https://docs.datadoghq.com/logs/log_configuration/attributes_naming_convention/#reserved-attributes\n// Note 'msg' and 'date' are not documented.\n// We should avoid using these as context keys. We escape them here\n// because otherwise the impact on the data dog log UI is very confusing\n// (e.g. using 'msg' as a context key results, in the context value\n// replacing the log message.)\nconst RESERVED_KEYS: ReadonlyArray<string> = [\n 'host',\n 'source',\n 'status',\n 'service',\n 'version',\n 'trace_id',\n 'message',\n 'msg', // alias for message\n 'date',\n // The following are attributes reserved by the DataDogLogSink\n // itself (as opposed to DataDog), to report on its own behavior.\n LOG_SINK_FLUSH_DELAY_ATTRIBUTE,\n LOG_SINK_FLUSH_RETRY_COUNT,\n];\n\nfunction makeMessage(\n message: unknown,\n context: Context | undefined,\n logLevel: LogLevel,\n): Message {\n let safeContext = undefined;\n if (context !== undefined) {\n for (const reservedKey of RESERVED_KEYS) {\n if (Object.hasOwn(context, reservedKey)) {\n if (safeContext === undefined) {\n safeContext = {...context};\n }\n safeContext[RESERVED_KEY_PREFIX + reservedKey] =\n safeContext[reservedKey];\n delete safeContext[reservedKey];\n }\n }\n }\n const msg: Message = {\n ...(safeContext ?? context),\n date: Date.now(),\n message: convertErrors(flattenMessage(message)),\n status: logLevel,\n };\n if (logLevel === 'error') {\n msg.error = {origin: 'logger'};\n }\n return msg;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"datadog-log-sink.js","names":[],"sources":["../../../../datadog/src/datadog-log-sink.ts"],"sourcesContent":["import {Lock} from '@rocicorp/lock';\nimport type {Context, LogLevel, LogSink} from '@rocicorp/logger';\n\nexport interface DatadogLogSinkOptions {\n apiKey?: string | undefined;\n source?: string | undefined;\n service?: string | undefined;\n host?: string | undefined;\n version?: string | undefined;\n interval?: number | undefined;\n baseURL?: URL | undefined;\n}\n\nconst DD_BASE_URL = new URL(\n 'https://http-intake.logs.datadoghq.com/api/v2/logs',\n);\n\n// https://docs.datadoghq.com/api/latest/logs/\nexport const MAX_LOG_ENTRIES_PER_FLUSH = 1000;\nexport const FORCE_FLUSH_THRESHOLD = 250;\nconst MAX_ENTRY_BYTES = 5 * 1024 * 1024;\nconst MAX_MESSAGE_RETRIES = 2;\n\n// Conservative limit that assumes all chars are encoded as 4 UTF-8 bytes.\n// This makes the actual limit somewhere closer to 1.25 MB, which is still\n// a reasonable amount of log data to send per request.\nexport const MAX_ENTRY_CHARS = MAX_ENTRY_BYTES / 4;\n\nexport class DatadogLogSink implements LogSink {\n #messages: Message[] = [];\n readonly #apiKey: string | undefined;\n readonly #source: string | undefined;\n readonly #service: string | undefined;\n readonly #host: string | undefined;\n readonly #version: string | undefined;\n readonly #interval: number;\n readonly #baseURL: string;\n #timerID: ReturnType<typeof setTimeout> | 0 = 0;\n #flushLock = new Lock();\n\n constructor(options: DatadogLogSinkOptions) {\n const {\n apiKey,\n source,\n service,\n host,\n version,\n interval = 5_000,\n baseURL: baseUrl = DD_BASE_URL,\n } = options;\n\n this.#apiKey = apiKey;\n this.#source = source;\n this.#service = service;\n this.#host = host;\n this.#version = version;\n this.#interval = interval;\n this.#baseURL = baseUrl.toString();\n }\n\n log(level: LogLevel, context: Context | undefined, ...args: unknown[]): void {\n this.#messages.push(makeMessage(args, context, level));\n if (level === 'error' || this.#messages.length === FORCE_FLUSH_THRESHOLD) {\n // Do not await. Later calls to flush will await as needed.\n void this.flush();\n } else {\n this.#startTimer();\n }\n }\n #startTimer() {\n if (this.#timerID) {\n return;\n }\n\n this.#timerID = setTimeout(() => {\n this.#timerID = 0;\n\n void this.flush();\n }, this.#interval);\n }\n\n flush(): Promise<void> {\n return this.#flushLock.withLock(async () => {\n const {length} = this.#messages;\n if (length === 0) {\n return;\n }\n do {\n const flushTime = Date.now();\n const stringified = [];\n let totalBytes = 0;\n\n for (const m of this.#messages) {\n // As a small perf optimization, we directly mutate\n // the message rather than making a shallow copy.\n // The LOG_SINK_FLUSH_DELAY_ATTRIBUTE will be clobbered by\n // the next flush if this flush fails (which is the desired behavior).\n m.flushDelayMs = flushTime - m.date;\n\n let str = JSON.stringify(m);\n if (str.length > MAX_ENTRY_CHARS) {\n // A single message above the total payload limit will otherwise halt\n // log flushing progress. Drop and replace with a message indicating so.\n m.message = `[Dropped message of length ${str.length}]`;\n str = JSON.stringify(m);\n }\n // Calculate the totalBytes with the newline characters between messages.\n if (str.length + totalBytes + stringified.length > MAX_ENTRY_CHARS) {\n break;\n }\n totalBytes += str.length;\n stringified.push(str);\n\n if (stringified.length === MAX_LOG_ENTRIES_PER_FLUSH) {\n break;\n }\n }\n\n const body = stringified.join('\\n');\n const url = new URL(this.#baseURL);\n if (this.#apiKey !== undefined) {\n url.searchParams.set('dd-api-key', this.#apiKey);\n }\n\n if (this.#source) {\n // Both need to be set for server to treat us as the browser SDK for\n // value 'browser'.\n url.searchParams.set('ddsource', this.#source);\n url.searchParams.set('dd-evp-origin', this.#source);\n }\n\n if (this.#service) {\n url.searchParams.set('service', this.#service);\n }\n\n if (this.#host) {\n url.searchParams.set('host', this.#host);\n }\n\n if (this.#version) {\n url.searchParams.set('ddtags', `version:${this.#version}`);\n }\n\n let ok = false;\n try {\n const response = await fetch(url.toString(), {\n method: 'POST',\n body,\n keepalive: true,\n } as RequestInit);\n\n ok = response.ok;\n if (!ok) {\n // Log to console so that we might catch this in `wrangler tail`.\n // oxlint-disable-next-line no-console\n console.error(\n 'response',\n response.status,\n response.statusText,\n await response.text,\n );\n }\n } catch (e) {\n // Log to console so that we might catch this in `wrangler tail`.\n // oxlint-disable-next-line no-console\n console.error('Log flush to datadog failed', e);\n }\n\n if (ok) {\n // Remove messages that were successfully flushed.\n this.#messages.splice(0, stringified.length);\n } else {\n let numWithTooManyRetries = 0;\n for (let i = 0; i < stringified.length; i++) {\n const m = this.#messages[i];\n m.flushRetryCount = (m.flushRetryCount ?? 0) + 1;\n if (m.flushRetryCount > MAX_MESSAGE_RETRIES) {\n numWithTooManyRetries++;\n }\n }\n if (numWithTooManyRetries > 0) {\n // oxlint-disable-next-line no-console\n console.error(\n `Dropping ${numWithTooManyRetries} datadog log messages which failed to send ${\n MAX_MESSAGE_RETRIES + 1\n } times.`,\n );\n // Remove messages that have failed too many times.\n this.#messages.splice(0, numWithTooManyRetries);\n }\n }\n } while (this.#messages.length >= FORCE_FLUSH_THRESHOLD);\n // If any messages left at this point schedule another flush.\n if (this.#messages.length) {\n this.#startTimer();\n }\n });\n }\n}\n\ntype Message = Context & {\n status: LogLevel;\n date: number;\n message: unknown;\n error?: {origin: 'logger'};\n flushDelayMs?: number;\n flushRetryCount?: number;\n};\n\nfunction flattenMessage(message: unknown): unknown {\n if (Array.isArray(message) && message.length === 1) {\n return flattenMessage(message[0]);\n }\n return message;\n}\n\nfunction convertError(e: Error): {\n name: string;\n message: string;\n stack: string | undefined;\n} {\n return {\n name: e.name,\n message: e.message,\n stack: e.stack,\n };\n}\n\nfunction convertErrors(message: unknown): unknown {\n if (message instanceof Error) {\n return convertError(message);\n }\n if (message instanceof Array) {\n const convertedMessage: unknown[] = [];\n for (const item of message) {\n if (item instanceof Error) {\n convertedMessage.push(convertError(item));\n } else {\n convertedMessage.push(item);\n }\n }\n return convertedMessage;\n }\n return message;\n}\n\nconst LOG_SINK_FLUSH_RETRY_COUNT = 'flushRetryCount';\nconst LOG_SINK_FLUSH_DELAY_ATTRIBUTE = 'flushDelayMs';\n// This code assumes that no context keys will start with\n// @DATADOG_RESERVED_ (a fairly safe assumption).\nconst RESERVED_KEY_PREFIX = '@DATADOG_RESERVED_';\n// See https://docs.datadoghq.com/logs/log_configuration/attributes_naming_convention/#reserved-attributes\n// Note 'msg' and 'date' are not documented.\n// We should avoid using these as context keys. We escape them here\n// because otherwise the impact on the data dog log UI is very confusing\n// (e.g. using 'msg' as a context key results, in the context value\n// replacing the log message.)\nconst RESERVED_KEYS: ReadonlyArray<string> = [\n 'host',\n 'source',\n 'status',\n 'service',\n 'version',\n 'trace_id',\n 'message',\n 'msg', // alias for message\n 'date',\n // The following are attributes reserved by the DataDogLogSink\n // itself (as opposed to DataDog), to report on its own behavior.\n LOG_SINK_FLUSH_DELAY_ATTRIBUTE,\n LOG_SINK_FLUSH_RETRY_COUNT,\n];\n\nfunction makeMessage(\n message: unknown,\n context: Context | undefined,\n logLevel: LogLevel,\n): Message {\n let safeContext = undefined;\n if (context !== undefined) {\n for (const reservedKey of RESERVED_KEYS) {\n if (Object.hasOwn(context, reservedKey)) {\n if (safeContext === undefined) {\n safeContext = {...context};\n }\n safeContext[RESERVED_KEY_PREFIX + reservedKey] =\n safeContext[reservedKey];\n delete safeContext[reservedKey];\n }\n }\n }\n const msg: Message = {\n ...(safeContext ?? context),\n date: Date.now(),\n message: convertErrors(flattenMessage(message)),\n status: logLevel,\n };\n if (logLevel === 'error') {\n msg.error = {origin: 'logger'};\n }\n return msg;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"log-options.js","names":[],"sources":["../../../../otel/src/log-options.ts"],"sourcesContent":["import {type Config} from '../../shared/src/options-types.ts';\nimport * as v from '../../shared/src/valita.ts';\n\nexport const logLevel = v.literalUnion('debug', 'info', 'warn', 'error');\n\nexport const logOptions = {\n level: logLevel.optional(() => 'info'),\n\n format: {\n type: v.literalUnion('text', 'json').optional(() => 'text'),\n desc: [\n `Use {bold text} for developer-friendly console logging`,\n `and {bold json} for consumption by structured-logging services`,\n ],\n },\n\n slowRowThreshold: {\n type: v.number().optional(() => 2),\n desc: [\n `The number of ms a row must take to fetch from table-source before it is considered slow.`,\n ],\n },\n\n slowHydrateThreshold: {\n type: v.number().optional(() => 100),\n desc: [\n `The number of milliseconds a query hydration must take to print a slow warning.`,\n ``,\n `The warning logs the query with its literal values redacted, and is logged`,\n `at most once every 5 minutes per query shape (the query with its values`,\n `redacted), with a count of the slow hydrations suppressed in between.`,\n ],\n },\n\n slowAdvanceThreshold: {\n type: v.number().optional(() => 100),\n desc: [\n `The number of milliseconds a query must take to process one advancement`,\n `(the changes of a replicated transaction, or a batch of them) to print a`,\n `slow warning.`,\n ``,\n `Like {bold slowHydrateThreshold}, the warning logs the query with its values`,\n `redacted, at most once every 5 minutes per query shape. When an advancement`,\n `times out and resets the pipelines, the queries that took the most time are`,\n `logged regardless of this threshold.`,\n ],\n },\n\n planWarningRowThreshold: {\n type: v.number().optional(() => 10_000),\n desc: [\n `Log a warning when the query planner estimates that one read of a table`,\n `scans or sorts at least this many rows: a read that scans the whole`,\n `table because no index covers the columns it looks rows up by, or that`,\n `sorts every matching row because no index covers the ordering.`,\n ``,\n `The warning is logged at most once an hour per query shape (the query`,\n `with its values redacted). Set to 0 to disable. Requires the query planner.`,\n ],\n },\n\n planWarningCostThreshold: {\n type: v.number().optional(() => 1_000_000),\n desc: [\n `Log a warning when the best plan the query planner finds for a query is`,\n `still estimated to process at least this many rows. Throttled like`,\n `{bold planWarningRowThreshold}. Set to 0 to disable.`,\n ],\n },\n\n queryStatsIntervalSeconds: {\n type: v.number().optional(() => 60),\n desc: [\n `How often each view-syncer worker logs the work done per query shape (the`,\n `query with its values redacted): the number of hydrations and`,\n `advancements, the time they took, and the rows they read and output.`,\n ``,\n `Unlike the slow query warnings, every query is counted, so the logs can be`,\n `summed across workers and over time to rank queries by the time they take.`,\n `Logged at the info level as {bold query-stats} events, for the 100 shapes`,\n `that took the most time in the interval, plus a {bold query-stats-summary}`,\n `event with the totals and how often each index of the replica was read.`,\n `Set to 0 to disable.`,\n ],\n },\n\n ivmSampling: {\n type: v.number().optional(() => 5000),\n desc: [\n `How often to collect IVM metrics. 1 out of N requests will be sampled where N is this value.`,\n ],\n },\n};\n\nexport type LogConfig = Config<typeof logOptions>;\n"],"mappings":";;AAGA,IAAa,WAAW,aAAe,SAAS,QAAQ,QAAQ,OAAO;AAEvE,IAAa,aAAa;CACxB,OAAO,SAAS,eAAe,MAAM;CAErC,QAAQ;EACN,MAAM,aAAe,QAAQ,MAAM,EAAE,eAAe,MAAM;EAC1D,MAAM,CACJ,0DACA,gEACF;CACF;CAEA,kBAAkB;EAChB,MAAM,eAAE,OAAO,EAAE,eAAe,CAAC;EACjC,MAAM,CACJ,2FACF;CACF;CAEA,sBAAsB;EACpB,MAAM,eAAE,OAAO,EAAE,eAAe,GAAG;EACnC,MAAM;GACJ;GACA;GACA;GACA;GACA;EACF;CACF;CAEA,sBAAsB;EACpB,MAAM,eAAE,OAAO,EAAE,eAAe,GAAG;EACnC,MAAM;GACJ;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CAEA,yBAAyB;EACvB,MAAM,eAAE,OAAO,EAAE,eAAe,GAAM;EACtC,MAAM;GACJ;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CAEA,0BAA0B;EACxB,MAAM,eAAE,OAAO,EAAE,eAAe,GAAS;EACzC,MAAM;GACJ;GACA;GACA;EACF;CACF;CAEA,2BAA2B;EACzB,MAAM,eAAE,OAAO,EAAE,eAAe,EAAE;EAClC,MAAM;GACJ;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CAEA,aAAa;EACX,MAAM,eAAE,OAAO,EAAE,eAAe,GAAI;EACpC,MAAM,CACJ,8FACF;CACF;AACF"}
1
+ {"version":3,"file":"log-options.js","names":["v.literalUnion","v"],"sources":["../../../../otel/src/log-options.ts"],"sourcesContent":["import {type Config} from '../../shared/src/options-types.ts';\nimport * as v from '../../shared/src/valita.ts';\n\nexport const logLevel = v.literalUnion('debug', 'info', 'warn', 'error');\n\nexport const logOptions = {\n level: logLevel.optional(() => 'info'),\n\n format: {\n type: v.literalUnion('text', 'json').optional(() => 'text'),\n desc: [\n `Use {bold text} for developer-friendly console logging`,\n `and {bold json} for consumption by structured-logging services`,\n ],\n },\n\n slowRowThreshold: {\n type: v.number().optional(() => 2),\n desc: [\n `The number of ms a row must take to fetch from table-source before it is considered slow.`,\n ],\n },\n\n slowHydrateThreshold: {\n type: v.number().optional(() => 100),\n desc: [\n `The number of milliseconds a query hydration must take to print a slow warning.`,\n ``,\n `The warning logs the query with its literal values redacted, and is logged`,\n `at most once every 5 minutes per query shape (the query with its values`,\n `redacted), with a count of the slow hydrations suppressed in between.`,\n ],\n },\n\n slowAdvanceThreshold: {\n type: v.number().optional(() => 100),\n desc: [\n `The number of milliseconds a query must take to process one advancement`,\n `(the changes of a replicated transaction, or a batch of them) to print a`,\n `slow warning.`,\n ``,\n `Like {bold slowHydrateThreshold}, the warning logs the query with its values`,\n `redacted, at most once every 5 minutes per query shape. When an advancement`,\n `times out and resets the pipelines, the queries that took the most time are`,\n `logged regardless of this threshold.`,\n ],\n },\n\n planWarningRowThreshold: {\n type: v.number().optional(() => 10_000),\n desc: [\n `Log a warning when the query planner estimates that one read of a table`,\n `scans or sorts at least this many rows: a read that scans the whole`,\n `table because no index covers the columns it looks rows up by, or that`,\n `sorts every matching row because no index covers the ordering.`,\n ``,\n `The warning is logged at most once an hour per query shape (the query`,\n `with its values redacted). Set to 0 to disable. Requires the query planner.`,\n ],\n },\n\n planWarningCostThreshold: {\n type: v.number().optional(() => 1_000_000),\n desc: [\n `Log a warning when the best plan the query planner finds for a query is`,\n `still estimated to process at least this many rows. Throttled like`,\n `{bold planWarningRowThreshold}. Set to 0 to disable.`,\n ],\n },\n\n queryStatsIntervalSeconds: {\n type: v.number().optional(() => 60),\n desc: [\n `How often each view-syncer worker logs the work done per query shape (the`,\n `query with its values redacted): the number of hydrations and`,\n `advancements, the time they took, and the rows they read and output.`,\n ``,\n `Unlike the slow query warnings, every query is counted, so the logs can be`,\n `summed across workers and over time to rank queries by the time they take.`,\n `Logged at the info level as {bold query-stats} events, for the 100 shapes`,\n `that took the most time in the interval, plus a {bold query-stats-summary}`,\n `event with the totals and how often each index of the replica was read.`,\n `Set to 0 to disable.`,\n ],\n },\n\n ivmSampling: {\n type: v.number().optional(() => 5000),\n desc: [\n `How often to collect IVM metrics. 1 out of N requests will be sampled where N is this value.`,\n ],\n },\n};\n\nexport type LogConfig = Config<typeof logOptions>;\n"],"mappings":";;AAGA,IAAa,WAAWA,aAAe,SAAS,QAAQ,QAAQ,OAAO;AAEvE,IAAa,aAAa;CACxB,OAAO,SAAS,eAAe,MAAM;CAErC,QAAQ;EACN,MAAMA,aAAe,QAAQ,MAAM,CAAC,CAAC,eAAe,MAAM;EAC1D,MAAM,CACJ,0DACA,gEACF;CACF;CAEA,kBAAkB;EAChB,MAAMC,eAAE,OAAO,CAAC,CAAC,eAAe,CAAC;EACjC,MAAM,CACJ,2FACF;CACF;CAEA,sBAAsB;EACpB,MAAMA,eAAE,OAAO,CAAC,CAAC,eAAe,GAAG;EACnC,MAAM;GACJ;GACA;GACA;GACA;GACA;EACF;CACF;CAEA,sBAAsB;EACpB,MAAMA,eAAE,OAAO,CAAC,CAAC,eAAe,GAAG;EACnC,MAAM;GACJ;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CAEA,yBAAyB;EACvB,MAAMA,eAAE,OAAO,CAAC,CAAC,eAAe,GAAM;EACtC,MAAM;GACJ;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CAEA,0BAA0B;EACxB,MAAMA,eAAE,OAAO,CAAC,CAAC,eAAe,GAAS;EACzC,MAAM;GACJ;GACA;GACA;EACF;CACF;CAEA,2BAA2B;EACzB,MAAMA,eAAE,OAAO,CAAC,CAAC,eAAe,EAAE;EAClC,MAAM;GACJ;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;CACF;CAEA,aAAa;EACX,MAAMA,eAAE,OAAO,CAAC,CAAC,eAAe,GAAI;EACpC,MAAM,CACJ,8FACF;CACF;AACF"}
@@ -236,7 +236,7 @@ var InternalNodeImpl = class InternalNodeImpl extends NodeImpl {
236
236
  lastGroup.nodes.set(index, node);
237
237
  lastGroup.maxIndex = index;
238
238
  } else groups.push({
239
- nodes: new Map([[index, node]]),
239
+ nodes: /* @__PURE__ */ new Map([[index, node]]),
240
240
  minIndex: index,
241
241
  maxIndex: index
242
242
  });
@@ -282,7 +282,8 @@ var InternalNodeImpl = class InternalNodeImpl extends NodeImpl {
282
282
  let removeCount;
283
283
  if (i > 0) {
284
284
  const hash = thisEntries[i - 1][1];
285
- values = joinIterables((await tree.getNode(hash)).entries, childNode.entries);
285
+ const previousSibling = await tree.getNode(hash);
286
+ values = joinIterables(previousSibling.entries, childNode.entries);
286
287
  startIndex = i - 1;
287
288
  removeCount = 2;
288
289
  } else if (i < thisEntries.length - 1) {
@@ -405,14 +406,15 @@ function partition(values, getSizeOfEntry, min, max, create) {
405
406
  accum.push(value);
406
407
  }
407
408
  }
408
- if (sum > 0) if (lastPartition !== null && sum + lastSize <= max) {
409
- lastPartition.push(...accum);
410
- commitLast();
411
- } else {
412
- commitLast();
413
- results.push(create(accum));
414
- }
415
- else commitLast();
409
+ if (sum > 0) {
410
+ if (lastPartition !== null && sum + lastSize <= max) {
411
+ lastPartition.push(...accum);
412
+ commitLast();
413
+ } else {
414
+ commitLast();
415
+ results.push(create(accum));
416
+ }
417
+ } else commitLast();
416
418
  return results;
417
419
  }
418
420
  var emptyDataNode = makeNodeChunkData(0, [], 7);
@@ -427,6 +429,6 @@ function createNewInternalEntryForNode(node, getSizeOfEntry) {
427
429
  ];
428
430
  }
429
431
  //#endregion
430
- export { DataNodeImpl, InternalNodeImpl, binarySearch, binarySearchFound, createNewInternalEntryForNode, emptyDataNode, emptyDataNodeImpl, findLeaf, isDataNodeImpl, newNodeImpl, parseBTreeNode, partition, toChunkData };
432
+ export { DataNodeImpl, InternalNodeImpl, binarySearch, binarySearchFound, createNewInternalEntryForNode, emptyDataNode, emptyDataNodeImpl, findLeaf, isDataNodeImpl, makeNodeChunkData, newNodeImpl, parseBTreeNode, partition, toChunkData };
431
433
 
432
434
  //# sourceMappingURL=node.js.map