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

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 (728) hide show
  1. package/README.md +28 -3
  2. package/out/_virtual/{_@oxc-project_runtime@0.122.0 → _@oxc-project_runtime@0.130.0}/helpers/usingCtx.js +1 -1
  3. package/out/_virtual/__vite-optional-peer-dep_pg-native_pg.js +13 -0
  4. package/out/_virtual/__vite-optional-peer-dep_pg-native_pg.js.map +1 -0
  5. package/out/_virtual/_rolldown/runtime.js +12 -1
  6. package/out/analyze-query/src/analyze-cli.js.map +1 -1
  7. package/out/analyze-query/src/bin-analyze.js +6 -1
  8. package/out/analyze-query/src/bin-analyze.js.map +1 -1
  9. package/out/analyze-query/src/bin-transform.js.map +1 -1
  10. package/out/ast-to-zql/src/ast-to-zql.js.map +1 -1
  11. package/out/ast-to-zql/src/bin.js.map +1 -1
  12. package/out/ast-to-zql/src/format.js.map +1 -1
  13. package/out/datadog/src/datadog-log-sink.js.map +1 -1
  14. package/out/node_modules/.pnpm/@opentelemetry_semantic-conventions@1.41.1/node_modules/@opentelemetry/semantic-conventions/build/esm/stable_attributes.js +12 -0
  15. package/out/node_modules/.pnpm/@opentelemetry_semantic-conventions@1.41.1/node_modules/@opentelemetry/semantic-conventions/build/esm/stable_attributes.js.map +1 -0
  16. package/out/node_modules/.pnpm/pg-cloudflare@1.3.0/node_modules/pg-cloudflare/dist/empty.js +11 -0
  17. package/out/node_modules/.pnpm/pg-cloudflare@1.3.0/node_modules/pg-cloudflare/dist/empty.js.map +1 -0
  18. package/out/node_modules/.pnpm/pg-connection-string@2.12.0/node_modules/pg-connection-string/index.js +130 -0
  19. package/out/node_modules/.pnpm/pg-connection-string@2.12.0/node_modules/pg-connection-string/index.js.map +1 -0
  20. package/out/node_modules/.pnpm/pg-int8@1.0.1/node_modules/pg-int8/index.js +62 -0
  21. package/out/node_modules/.pnpm/pg-int8@1.0.1/node_modules/pg-int8/index.js.map +1 -0
  22. package/out/node_modules/.pnpm/pg-pool@3.13.0_pg@8.20.0/node_modules/pg-pool/index.js +353 -0
  23. package/out/node_modules/.pnpm/pg-pool@3.13.0_pg@8.20.0/node_modules/pg-pool/index.js.map +1 -0
  24. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/buffer-reader.js +60 -0
  25. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/buffer-reader.js.map +1 -0
  26. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/buffer-writer.js +81 -0
  27. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/buffer-writer.js.map +1 -0
  28. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/index.js +35 -0
  29. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/index.js.map +1 -0
  30. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/messages.js +167 -0
  31. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/messages.js.map +1 -0
  32. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/parser.js +288 -0
  33. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/parser.js.map +1 -0
  34. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/serializer.js +177 -0
  35. package/out/node_modules/.pnpm/pg-protocol@1.13.0/node_modules/pg-protocol/dist/serializer.js.map +1 -0
  36. package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/index.js +46 -0
  37. package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/index.js.map +1 -0
  38. package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/lib/arrayParser.js +16 -0
  39. package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/lib/arrayParser.js.map +1 -0
  40. package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/lib/binaryParsers.js +165 -0
  41. package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/lib/binaryParsers.js.map +1 -0
  42. package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/lib/builtins.js +81 -0
  43. package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/lib/builtins.js.map +1 -0
  44. package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/lib/textParsers.js +167 -0
  45. package/out/node_modules/.pnpm/pg-types@2.2.0/node_modules/pg-types/lib/textParsers.js.map +1 -0
  46. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/esm/index.js +19 -0
  47. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/esm/index.js.map +1 -0
  48. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/client.js +508 -0
  49. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/client.js.map +1 -0
  50. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/connection-parameters.js +104 -0
  51. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/connection-parameters.js.map +1 -0
  52. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/connection.js +160 -0
  53. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/connection.js.map +1 -0
  54. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/cert-signatures.js +97 -0
  55. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/cert-signatures.js.map +1 -0
  56. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/sasl.js +131 -0
  57. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/sasl.js.map +1 -0
  58. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/utils-legacy.js +39 -0
  59. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/utils-legacy.js.map +1 -0
  60. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/utils-webcrypto.js +89 -0
  61. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/utils-webcrypto.js.map +1 -0
  62. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/utils.js +13 -0
  63. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/crypto/utils.js.map +1 -0
  64. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/defaults.js +46 -0
  65. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/defaults.js.map +1 -0
  66. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/index.js +71 -0
  67. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/index.js.map +1 -0
  68. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/native/client.js +226 -0
  69. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/native/client.js.map +1 -0
  70. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/native/index.js +11 -0
  71. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/native/index.js.map +1 -0
  72. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/native/query.js +117 -0
  73. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/native/query.js.map +1 -0
  74. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/query.js +151 -0
  75. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/query.js.map +1 -0
  76. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/result.js +76 -0
  77. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/result.js.map +1 -0
  78. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/stream.js +73 -0
  79. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/stream.js.map +1 -0
  80. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/type-overrides.js +35 -0
  81. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/type-overrides.js.map +1 -0
  82. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/utils.js +118 -0
  83. package/out/node_modules/.pnpm/pg@8.20.0/node_modules/pg/lib/utils.js.map +1 -0
  84. package/out/node_modules/.pnpm/pgpass@1.0.5/node_modules/pgpass/lib/helper.js +147 -0
  85. package/out/node_modules/.pnpm/pgpass@1.0.5/node_modules/pgpass/lib/helper.js.map +1 -0
  86. package/out/node_modules/.pnpm/pgpass@1.0.5/node_modules/pgpass/lib/index.js +21 -0
  87. package/out/node_modules/.pnpm/pgpass@1.0.5/node_modules/pgpass/lib/index.js.map +1 -0
  88. package/out/node_modules/.pnpm/postgres-array@2.0.0/node_modules/postgres-array/index.js +84 -0
  89. package/out/node_modules/.pnpm/postgres-array@2.0.0/node_modules/postgres-array/index.js.map +1 -0
  90. package/out/node_modules/.pnpm/postgres-bytea@1.0.1/node_modules/postgres-bytea/index.js +28 -0
  91. package/out/node_modules/.pnpm/postgres-bytea@1.0.1/node_modules/postgres-bytea/index.js.map +1 -0
  92. package/out/node_modules/.pnpm/postgres-date@1.0.7/node_modules/postgres-date/index.js +65 -0
  93. package/out/node_modules/.pnpm/postgres-date@1.0.7/node_modules/postgres-date/index.js.map +1 -0
  94. package/out/node_modules/.pnpm/postgres-interval@1.2.0/node_modules/postgres-interval/index.js +107 -0
  95. package/out/node_modules/.pnpm/postgres-interval@1.2.0/node_modules/postgres-interval/index.js.map +1 -0
  96. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react-jsx-runtime.development.js +696 -0
  97. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react-jsx-runtime.development.js.map +1 -0
  98. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react-jsx-runtime.production.min.js +44 -0
  99. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react-jsx-runtime.production.min.js.map +1 -0
  100. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.development.js +1585 -0
  101. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.development.js.map +1 -0
  102. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.production.min.js +329 -0
  103. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.production.min.js.map +1 -0
  104. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/index.js +13 -0
  105. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/index.js.map +1 -0
  106. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/jsx-runtime.js +13 -0
  107. package/out/node_modules/.pnpm/react@18.3.1/node_modules/react/jsx-runtime.js.map +1 -0
  108. package/out/node_modules/.pnpm/solid-js@1.9.13/node_modules/solid-js/dist/server.js +131 -0
  109. package/out/node_modules/.pnpm/solid-js@1.9.13/node_modules/solid-js/dist/server.js.map +1 -0
  110. package/out/node_modules/.pnpm/solid-js@1.9.13/node_modules/solid-js/store/dist/server.js +96 -0
  111. package/out/node_modules/.pnpm/solid-js@1.9.13/node_modules/solid-js/store/dist/server.js.map +1 -0
  112. package/out/node_modules/.pnpm/split2@4.2.0/node_modules/split2/index.js +95 -0
  113. package/out/node_modules/.pnpm/split2@4.2.0/node_modules/split2/index.js.map +1 -0
  114. package/out/node_modules/.pnpm/xtend@4.0.2/node_modules/xtend/mutable.js +18 -0
  115. package/out/node_modules/.pnpm/xtend@4.0.2/node_modules/xtend/mutable.js.map +1 -0
  116. package/out/otel/src/enabled.js.map +1 -1
  117. package/out/otel/src/log-options.js.map +1 -1
  118. package/out/otel/src/maybe-time.js.map +1 -1
  119. package/out/otel/src/span.js.map +1 -1
  120. package/out/replicache/src/async-iterable-to-array.js.map +1 -1
  121. package/out/replicache/src/bg-interval.js.map +1 -1
  122. package/out/replicache/src/btree/diff.js.map +1 -1
  123. package/out/replicache/src/btree/node.js.map +1 -1
  124. package/out/replicache/src/btree/read.js.map +1 -1
  125. package/out/replicache/src/btree/splice.js.map +1 -1
  126. package/out/replicache/src/btree/write.js +6 -3
  127. package/out/replicache/src/btree/write.js.map +1 -1
  128. package/out/replicache/src/call-default-fetch.js.map +1 -1
  129. package/out/replicache/src/connection-loop-delegates.js.map +1 -1
  130. package/out/replicache/src/connection-loop.js.map +1 -1
  131. package/out/replicache/src/cookies.js.map +1 -1
  132. package/out/replicache/src/dag/chunk.js.map +1 -1
  133. package/out/replicache/src/dag/gc.js.map +1 -1
  134. package/out/replicache/src/dag/key.js.map +1 -1
  135. package/out/replicache/src/dag/lazy-store.js.map +1 -1
  136. package/out/replicache/src/dag/store-impl.d.ts.map +1 -1
  137. package/out/replicache/src/dag/store-impl.js +8 -3
  138. package/out/replicache/src/dag/store-impl.js.map +1 -1
  139. package/out/replicache/src/dag/store.js.map +1 -1
  140. package/out/replicache/src/dag/visitor.js.map +1 -1
  141. package/out/replicache/src/db/commit.js.map +1 -1
  142. package/out/replicache/src/db/index.js.map +1 -1
  143. package/out/replicache/src/db/read.js.map +1 -1
  144. package/out/replicache/src/db/rebase.js.map +1 -1
  145. package/out/replicache/src/db/write.js.map +1 -1
  146. package/out/replicache/src/deleted-clients.js.map +1 -1
  147. package/out/replicache/src/error-responses.js.map +1 -1
  148. package/out/replicache/src/frozen-json.js.map +1 -1
  149. package/out/replicache/src/get-default-puller.js.map +1 -1
  150. package/out/replicache/src/get-default-pusher.js.map +1 -1
  151. package/out/replicache/src/get-kv-store-provider.js.map +1 -1
  152. package/out/replicache/src/hash.js.map +1 -1
  153. package/out/replicache/src/http-request-info.js.map +1 -1
  154. package/out/replicache/src/index-defs.js.map +1 -1
  155. package/out/replicache/src/kv/expo-sqlite/store.d.ts +1 -1
  156. package/out/replicache/src/kv/expo-sqlite/store.d.ts.map +1 -1
  157. package/out/replicache/src/kv/expo-sqlite/store.js +6 -7
  158. package/out/replicache/src/kv/expo-sqlite/store.js.map +1 -1
  159. package/out/replicache/src/kv/idb-store-with-mem-fallback.js.map +1 -1
  160. package/out/replicache/src/kv/idb-store.js.map +1 -1
  161. package/out/replicache/src/kv/mem-store.js.map +1 -1
  162. package/out/replicache/src/kv/op-sqlite/store.d.ts.map +1 -1
  163. package/out/replicache/src/kv/op-sqlite/store.js +6 -6
  164. package/out/replicache/src/kv/op-sqlite/store.js.map +1 -1
  165. package/out/replicache/src/kv/read-impl.js.map +1 -1
  166. package/out/replicache/src/kv/sqlite-store.d.ts +6 -6
  167. package/out/replicache/src/kv/sqlite-store.d.ts.map +1 -1
  168. package/out/replicache/src/kv/sqlite-store.js +107 -22
  169. package/out/replicache/src/kv/sqlite-store.js.map +1 -1
  170. package/out/replicache/src/kv/throw-if-closed.d.ts +1 -0
  171. package/out/replicache/src/kv/throw-if-closed.d.ts.map +1 -1
  172. package/out/replicache/src/kv/throw-if-closed.js +1 -4
  173. package/out/replicache/src/kv/throw-if-closed.js.map +1 -1
  174. package/out/replicache/src/kv/write-impl-base.js.map +1 -1
  175. package/out/replicache/src/kv/write-impl.js.map +1 -1
  176. package/out/replicache/src/lazy.js.map +1 -1
  177. package/out/replicache/src/log-options.js.map +1 -1
  178. package/out/replicache/src/make-idb-name.js.map +1 -1
  179. package/out/replicache/src/new-client-channel.js.map +1 -1
  180. package/out/replicache/src/on-persist-channel.js.map +1 -1
  181. package/out/replicache/src/patch-operation.js.map +1 -1
  182. package/out/replicache/src/pending-mutations.js.map +1 -1
  183. package/out/replicache/src/persist/client-gc.js.map +1 -1
  184. package/out/replicache/src/persist/client-group-gc.js.map +1 -1
  185. package/out/replicache/src/persist/client-groups.js +40 -0
  186. package/out/replicache/src/persist/client-groups.js.map +1 -1
  187. package/out/replicache/src/persist/clients.js +28 -0
  188. package/out/replicache/src/persist/clients.js.map +1 -1
  189. package/out/replicache/src/persist/collect-idb-databases.js.map +1 -1
  190. package/out/replicache/src/persist/gather-mem-only-visitor.js.map +1 -1
  191. package/out/replicache/src/persist/gather-not-cached-visitor.js.map +1 -1
  192. package/out/replicache/src/persist/heartbeat.js.map +1 -1
  193. package/out/replicache/src/persist/idb-databases-store-db-name.js.map +1 -1
  194. package/out/replicache/src/persist/idb-databases-store.js.map +1 -1
  195. package/out/replicache/src/persist/make-client-id.js.map +1 -1
  196. package/out/replicache/src/persist/persist.d.ts.map +1 -1
  197. package/out/replicache/src/persist/persist.js +4 -2
  198. package/out/replicache/src/persist/persist.js.map +1 -1
  199. package/out/replicache/src/persist/refresh.js.map +1 -1
  200. package/out/replicache/src/process-scheduler.js.map +1 -1
  201. package/out/replicache/src/pusher.js.map +1 -1
  202. package/out/replicache/src/replicache-impl.js.map +1 -1
  203. package/out/replicache/src/report-error.js.map +1 -1
  204. package/out/replicache/src/request-idle.js.map +1 -1
  205. package/out/replicache/src/scan-iterator.js.map +1 -1
  206. package/out/replicache/src/scan-options.js.map +1 -1
  207. package/out/replicache/src/set-interval-with-signal.js.map +1 -1
  208. package/out/replicache/src/subscriptions.js.map +1 -1
  209. package/out/replicache/src/sync/diff.js.map +1 -1
  210. package/out/replicache/src/sync/ids.js.map +1 -1
  211. package/out/replicache/src/sync/patch.js.map +1 -1
  212. package/out/replicache/src/sync/pull-error.js.map +1 -1
  213. package/out/replicache/src/sync/pull.d.ts.map +1 -1
  214. package/out/replicache/src/sync/pull.js +9 -6
  215. package/out/replicache/src/sync/pull.js.map +1 -1
  216. package/out/replicache/src/sync/push.js.map +1 -1
  217. package/out/replicache/src/sync/request-id.js.map +1 -1
  218. package/out/replicache/src/to-error.js.map +1 -1
  219. package/out/replicache/src/transaction-closed-error.js.map +1 -1
  220. package/out/replicache/src/transactions.js.map +1 -1
  221. package/out/replicache/src/with-transactions.js.map +1 -1
  222. package/out/shared/src/abort-error.js.map +1 -1
  223. package/out/shared/src/arrays.js.map +1 -1
  224. package/out/shared/src/asserts.js.map +1 -1
  225. package/out/shared/src/bigint-json.js.map +1 -1
  226. package/out/shared/src/binary-search.js.map +1 -1
  227. package/out/shared/src/broadcast-channel.js.map +1 -1
  228. package/out/shared/src/browser-env.js.map +1 -1
  229. package/out/shared/src/btree-set.js.map +1 -1
  230. package/out/shared/src/cache.js.map +1 -1
  231. package/out/shared/src/centroid.js.map +1 -1
  232. package/out/shared/src/custom-key-map.js.map +1 -1
  233. package/out/shared/src/custom-key-set.js.map +1 -1
  234. package/out/shared/src/deep-clone.js.map +1 -1
  235. package/out/shared/src/deep-merge.js.map +1 -1
  236. package/out/shared/src/document-visible.js.map +1 -1
  237. package/out/shared/src/dotenv.js.map +1 -1
  238. package/out/shared/src/error.js.map +1 -1
  239. package/out/shared/src/hash.js.map +1 -1
  240. package/out/shared/src/iterables.js.map +1 -1
  241. package/out/shared/src/json-schema.js.map +1 -1
  242. package/out/shared/src/json.js.map +1 -1
  243. package/out/shared/src/logging-test-utils.js.map +1 -1
  244. package/out/shared/src/logging.js.map +1 -1
  245. package/out/shared/src/map.d.ts +6 -0
  246. package/out/shared/src/map.d.ts.map +1 -0
  247. package/out/shared/src/map.js +39 -0
  248. package/out/shared/src/map.js.map +1 -0
  249. package/out/shared/src/must.js.map +1 -1
  250. package/out/shared/src/object-traversal.js.map +1 -1
  251. package/out/shared/src/objects.js.map +1 -1
  252. package/out/shared/src/options.js.map +1 -1
  253. package/out/shared/src/parse-big-int.js.map +1 -1
  254. package/out/shared/src/promise-race.js.map +1 -1
  255. package/out/shared/src/queue.d.ts.map +1 -1
  256. package/out/shared/src/queue.js +15 -21
  257. package/out/shared/src/queue.js.map +1 -1
  258. package/out/shared/src/rand.js.map +1 -1
  259. package/out/shared/src/random-uint64.js.map +1 -1
  260. package/out/shared/src/random-values.js.map +1 -1
  261. package/out/shared/src/record-proxy.js.map +1 -1
  262. package/out/shared/src/resolved-promises.js.map +1 -1
  263. package/out/shared/src/ring-buffer.d.ts +32 -0
  264. package/out/shared/src/ring-buffer.d.ts.map +1 -0
  265. package/out/shared/src/ring-buffer.js +109 -0
  266. package/out/shared/src/ring-buffer.js.map +1 -0
  267. package/out/shared/src/sentinels.js.map +1 -1
  268. package/out/shared/src/set-utils.js.map +1 -1
  269. package/out/shared/src/size-of-value.js.map +1 -1
  270. package/out/shared/src/sleep.js.map +1 -1
  271. package/out/shared/src/sorted-entries.js.map +1 -1
  272. package/out/shared/src/string-compare.js.map +1 -1
  273. package/out/shared/src/subscribable.js.map +1 -1
  274. package/out/shared/src/tdigest-schema.js.map +1 -1
  275. package/out/shared/src/tdigest.js.map +1 -1
  276. package/out/shared/src/valita.js.map +1 -1
  277. package/out/z2s/src/compiler.js.map +1 -1
  278. package/out/z2s/src/sql.js.map +1 -1
  279. package/out/zero/package.js +23 -23
  280. package/out/zero/package.js.map +1 -1
  281. package/out/zero/src/build-schema.js.map +1 -1
  282. package/out/zero/src/zero-cache-dev.js.map +1 -1
  283. package/out/zero/src/zero-out.js.map +1 -1
  284. package/out/zero-cache/src/auth/auth.js.map +1 -1
  285. package/out/zero-cache/src/auth/jwt.js.map +1 -1
  286. package/out/zero-cache/src/auth/load-permissions.js.map +1 -1
  287. package/out/zero-cache/src/auth/read-authorizer.js.map +1 -1
  288. package/out/zero-cache/src/auth/write-authorizer.js.map +1 -1
  289. package/out/zero-cache/src/config/network.js.map +1 -1
  290. package/out/zero-cache/src/config/normalize.js.map +1 -1
  291. package/out/zero-cache/src/config/server-context.js.map +1 -1
  292. package/out/zero-cache/src/config/zero-config.js +5 -0
  293. package/out/zero-cache/src/config/zero-config.js.map +1 -1
  294. package/out/zero-cache/src/custom/fetch.js.map +1 -1
  295. package/out/zero-cache/src/custom-queries/transform-query.js.map +1 -1
  296. package/out/zero-cache/src/db/create.js.map +1 -1
  297. package/out/zero-cache/src/db/delete-lite-db.js.map +1 -1
  298. package/out/zero-cache/src/db/lite-tables.js.map +1 -1
  299. package/out/zero-cache/src/db/migration-lite.js +19 -0
  300. package/out/zero-cache/src/db/migration-lite.js.map +1 -1
  301. package/out/zero-cache/src/db/migration.js +19 -0
  302. package/out/zero-cache/src/db/migration.js.map +1 -1
  303. package/out/zero-cache/src/db/pg-copy-binary.js.map +1 -1
  304. package/out/zero-cache/src/db/pg-copy.js.map +1 -1
  305. package/out/zero-cache/src/db/pg-to-lite.js.map +1 -1
  306. package/out/zero-cache/src/db/pg-type-parser.js.map +1 -1
  307. package/out/zero-cache/src/db/run-transaction.js.map +1 -1
  308. package/out/zero-cache/src/db/specs.js.map +1 -1
  309. package/out/zero-cache/src/db/statements.js.map +1 -1
  310. package/out/zero-cache/src/db/transaction-pool.js.map +1 -1
  311. package/out/zero-cache/src/db/warmup.js.map +1 -1
  312. package/out/zero-cache/src/observability/events.js.map +1 -1
  313. package/out/zero-cache/src/observability/metrics.js.map +1 -1
  314. package/out/zero-cache/src/scripts/decommission.js.map +1 -1
  315. package/out/zero-cache/src/scripts/deploy-permissions.js.map +1 -1
  316. package/out/zero-cache/src/scripts/permissions.js.map +1 -1
  317. package/out/zero-cache/src/server/anonymous-otel-start.js +11 -10
  318. package/out/zero-cache/src/server/anonymous-otel-start.js.map +1 -1
  319. package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
  320. package/out/zero-cache/src/server/change-streamer.js +13 -7
  321. package/out/zero-cache/src/server/change-streamer.js.map +1 -1
  322. package/out/zero-cache/src/server/inspector-delegate.js.map +1 -1
  323. package/out/zero-cache/src/server/logging.js.map +1 -1
  324. package/out/zero-cache/src/server/main.d.ts.map +1 -1
  325. package/out/zero-cache/src/server/main.js +4 -2
  326. package/out/zero-cache/src/server/main.js.map +1 -1
  327. package/out/zero-cache/src/server/mutator.js +4 -2
  328. package/out/zero-cache/src/server/mutator.js.map +1 -1
  329. package/out/zero-cache/src/server/otel-diag-logger.js.map +1 -1
  330. package/out/zero-cache/src/server/otel-log-sink.js.map +1 -1
  331. package/out/zero-cache/src/server/otel-start.js +1 -1
  332. package/out/zero-cache/src/server/otel-start.js.map +1 -1
  333. package/out/zero-cache/src/server/priority-op.js.map +1 -1
  334. package/out/zero-cache/src/server/reaper.d.ts.map +1 -1
  335. package/out/zero-cache/src/server/reaper.js +6 -4
  336. package/out/zero-cache/src/server/reaper.js.map +1 -1
  337. package/out/zero-cache/src/server/replicator.d.ts.map +1 -1
  338. package/out/zero-cache/src/server/replicator.js +4 -2
  339. package/out/zero-cache/src/server/replicator.js.map +1 -1
  340. package/out/zero-cache/src/server/runner/main.d.ts.map +1 -1
  341. package/out/zero-cache/src/server/runner/main.js +2 -1
  342. package/out/zero-cache/src/server/runner/main.js.map +1 -1
  343. package/out/zero-cache/src/server/runner/run-worker.js.map +1 -1
  344. package/out/zero-cache/src/server/runner/runtime.js.map +1 -1
  345. package/out/zero-cache/src/server/runner/zero-dispatcher.js.map +1 -1
  346. package/out/zero-cache/src/server/shadow-syncer.js +6 -3
  347. package/out/zero-cache/src/server/shadow-syncer.js.map +1 -1
  348. package/out/zero-cache/src/server/syncer.d.ts.map +1 -1
  349. package/out/zero-cache/src/server/syncer.js +8 -6
  350. package/out/zero-cache/src/server/syncer.js.map +1 -1
  351. package/out/zero-cache/src/server/worker-dispatcher.js.map +1 -1
  352. package/out/zero-cache/src/server/worker-urls.js.map +1 -1
  353. package/out/zero-cache/src/services/analyze.d.ts.map +1 -1
  354. package/out/zero-cache/src/services/analyze.js +5 -2
  355. package/out/zero-cache/src/services/analyze.js.map +1 -1
  356. package/out/zero-cache/src/services/change-source/common/backfill-manager.js.map +1 -1
  357. package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.js.map +1 -1
  358. package/out/zero-cache/src/services/change-source/common/replica-schema.js.map +1 -1
  359. package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
  360. package/out/zero-cache/src/services/change-source/pg/backfill-metadata.js.map +1 -1
  361. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js +2 -2
  362. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js.map +1 -1
  363. package/out/zero-cache/src/services/change-source/pg/change-source.d.ts.map +1 -1
  364. package/out/zero-cache/src/services/change-source/pg/change-source.js +2 -2
  365. package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
  366. package/out/zero-cache/src/services/change-source/pg/decommission.js.map +1 -1
  367. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts +8 -2
  368. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts.map +1 -1
  369. package/out/zero-cache/src/services/change-source/pg/initial-sync.js +13 -12
  370. package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
  371. package/out/zero-cache/src/services/change-source/pg/logical-replication/binary-reader.js.map +1 -1
  372. package/out/zero-cache/src/services/change-source/pg/logical-replication/pgoutput-parser.js.map +1 -1
  373. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js.map +1 -1
  374. package/out/zero-cache/src/services/change-source/pg/lsn.js.map +1 -1
  375. package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts +1 -1
  376. package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts.map +1 -1
  377. package/out/zero-cache/src/services/change-source/pg/replication-slots.js +33 -33
  378. package/out/zero-cache/src/services/change-source/pg/replication-slots.js.map +1 -1
  379. package/out/zero-cache/src/services/change-source/pg/schema/ddl.js.map +1 -1
  380. package/out/zero-cache/src/services/change-source/pg/schema/init.js.map +1 -1
  381. package/out/zero-cache/src/services/change-source/pg/schema/published.js.map +1 -1
  382. package/out/zero-cache/src/services/change-source/pg/schema/shard.js +1 -1
  383. package/out/zero-cache/src/services/change-source/pg/schema/shard.js.map +1 -1
  384. package/out/zero-cache/src/services/change-source/pg/schema/validation.js.map +1 -1
  385. package/out/zero-cache/src/services/change-source/protocol/current/control.js.map +1 -1
  386. package/out/zero-cache/src/services/change-source/protocol/current/data.js +2 -0
  387. package/out/zero-cache/src/services/change-source/protocol/current/data.js.map +1 -1
  388. package/out/zero-cache/src/services/change-source/protocol/current/downstream.js.map +1 -1
  389. package/out/zero-cache/src/services/change-source/protocol/current/json.js.map +1 -1
  390. package/out/zero-cache/src/services/change-source/protocol/current/status.js.map +1 -1
  391. package/out/zero-cache/src/services/change-source/protocol/current/upstream.js.map +1 -1
  392. package/out/zero-cache/src/services/change-streamer/backup-monitor.js.map +1 -1
  393. package/out/zero-cache/src/services/change-streamer/broadcast.js.map +1 -1
  394. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
  395. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
  396. package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
  397. package/out/zero-cache/src/services/change-streamer/forwarder.js.map +1 -1
  398. package/out/zero-cache/src/services/change-streamer/replica-monitor.js.map +1 -1
  399. package/out/zero-cache/src/services/change-streamer/schema/init.js +21 -25
  400. package/out/zero-cache/src/services/change-streamer/schema/init.js.map +1 -1
  401. package/out/zero-cache/src/services/change-streamer/schema/tables.d.ts.map +1 -1
  402. package/out/zero-cache/src/services/change-streamer/schema/tables.js +1 -1
  403. package/out/zero-cache/src/services/change-streamer/schema/tables.js.map +1 -1
  404. package/out/zero-cache/src/services/change-streamer/snapshot.js +15 -0
  405. package/out/zero-cache/src/services/change-streamer/snapshot.js.map +1 -1
  406. package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
  407. package/out/zero-cache/src/services/change-streamer/subscriber.js.map +1 -1
  408. package/out/zero-cache/src/services/heapz.js.map +1 -1
  409. package/out/zero-cache/src/services/http-service.js.map +1 -1
  410. package/out/zero-cache/src/services/life-cycle.d.ts +1 -1
  411. package/out/zero-cache/src/services/life-cycle.d.ts.map +1 -1
  412. package/out/zero-cache/src/services/life-cycle.js +8 -3
  413. package/out/zero-cache/src/services/life-cycle.js.map +1 -1
  414. package/out/zero-cache/src/services/limiter/sliding-window-limiter.js.map +1 -1
  415. package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
  416. package/out/zero-cache/src/services/mutagen/error.js.map +1 -1
  417. package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
  418. package/out/zero-cache/src/services/mutagen/pusher.js.map +1 -1
  419. package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
  420. package/out/zero-cache/src/services/replicator/incremental-sync.js.map +1 -1
  421. package/out/zero-cache/src/services/replicator/notifier.js.map +1 -1
  422. package/out/zero-cache/src/services/replicator/replication-status.js.map +1 -1
  423. package/out/zero-cache/src/services/replicator/replicator.js.map +1 -1
  424. package/out/zero-cache/src/services/replicator/reporter/recorder.js.map +1 -1
  425. package/out/zero-cache/src/services/replicator/reporter/report-schema.js.map +1 -1
  426. package/out/zero-cache/src/services/replicator/schema/change-log.js.map +1 -1
  427. package/out/zero-cache/src/services/replicator/schema/column-metadata.js.map +1 -1
  428. package/out/zero-cache/src/services/replicator/schema/replication-state.js.map +1 -1
  429. package/out/zero-cache/src/services/replicator/schema/table-metadata.js.map +1 -1
  430. package/out/zero-cache/src/services/replicator/write-worker-client.js.map +1 -1
  431. package/out/zero-cache/src/services/replicator/write-worker.js.map +1 -1
  432. package/out/zero-cache/src/services/run-ast.d.ts.map +1 -1
  433. package/out/zero-cache/src/services/run-ast.js +1 -0
  434. package/out/zero-cache/src/services/run-ast.js.map +1 -1
  435. package/out/zero-cache/src/services/runner.js.map +1 -1
  436. package/out/zero-cache/src/services/running-state.d.ts.map +1 -1
  437. package/out/zero-cache/src/services/running-state.js +3 -0
  438. package/out/zero-cache/src/services/running-state.js.map +1 -1
  439. package/out/zero-cache/src/services/shadow-sync/shadow-sync-service.js.map +1 -1
  440. package/out/zero-cache/src/services/statz.js.map +1 -1
  441. package/out/zero-cache/src/services/view-syncer/active-users-gauge.js.map +1 -1
  442. package/out/zero-cache/src/services/view-syncer/client-handler.js +1 -1
  443. package/out/zero-cache/src/services/view-syncer/client-handler.js.map +1 -1
  444. package/out/zero-cache/src/services/view-syncer/client-schema.js.map +1 -1
  445. package/out/zero-cache/src/services/view-syncer/connection-context-manager.js.map +1 -1
  446. package/out/zero-cache/src/services/view-syncer/cvr-purger.d.ts.map +1 -1
  447. package/out/zero-cache/src/services/view-syncer/cvr-purger.js +2 -1
  448. package/out/zero-cache/src/services/view-syncer/cvr-purger.js.map +1 -1
  449. package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
  450. package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
  451. package/out/zero-cache/src/services/view-syncer/drain-coordinator.js.map +1 -1
  452. package/out/zero-cache/src/services/view-syncer/inspect-handler.js +1 -1
  453. package/out/zero-cache/src/services/view-syncer/inspect-handler.js.map +1 -1
  454. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +1 -1
  455. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
  456. package/out/zero-cache/src/services/view-syncer/row-record-cache.js.map +1 -1
  457. package/out/zero-cache/src/services/view-syncer/row-set-signature.js.map +1 -1
  458. package/out/zero-cache/src/services/view-syncer/schema/cvr.js.map +1 -1
  459. package/out/zero-cache/src/services/view-syncer/schema/init.js +97 -113
  460. package/out/zero-cache/src/services/view-syncer/schema/init.js.map +1 -1
  461. package/out/zero-cache/src/services/view-syncer/schema/types.js +103 -1
  462. package/out/zero-cache/src/services/view-syncer/schema/types.js.map +1 -1
  463. package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
  464. package/out/zero-cache/src/services/view-syncer/tracer.js.map +1 -1
  465. package/out/zero-cache/src/services/view-syncer/ttl-clock.js.map +1 -1
  466. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
  467. package/out/zero-cache/src/services/view-syncer/view-syncer.js +15 -5
  468. package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
  469. package/out/zero-cache/src/types/configuration-error.d.ts +4 -0
  470. package/out/zero-cache/src/types/configuration-error.d.ts.map +1 -0
  471. package/out/zero-cache/src/types/configuration-error.js +11 -0
  472. package/out/zero-cache/src/types/configuration-error.js.map +1 -0
  473. package/out/zero-cache/src/types/error-with-level.js.map +1 -1
  474. package/out/zero-cache/src/types/http.js.map +1 -1
  475. package/out/zero-cache/src/types/lexi-version.js.map +1 -1
  476. package/out/zero-cache/src/types/lite.js.map +1 -1
  477. package/out/zero-cache/src/types/names.js.map +1 -1
  478. package/out/zero-cache/src/types/pg-data-type.js.map +1 -1
  479. package/out/zero-cache/src/types/pg.d.ts +2 -0
  480. package/out/zero-cache/src/types/pg.d.ts.map +1 -1
  481. package/out/zero-cache/src/types/pg.js +34 -1
  482. package/out/zero-cache/src/types/pg.js.map +1 -1
  483. package/out/zero-cache/src/types/processes.js.map +1 -1
  484. package/out/zero-cache/src/types/profiler.js.map +1 -1
  485. package/out/zero-cache/src/types/row-key.js.map +1 -1
  486. package/out/zero-cache/src/types/shards.js.map +1 -1
  487. package/out/zero-cache/src/types/sql.js.map +1 -1
  488. package/out/zero-cache/src/types/state-version.js.map +1 -1
  489. package/out/zero-cache/src/types/streams.js.map +1 -1
  490. package/out/zero-cache/src/types/strings.js.map +1 -1
  491. package/out/zero-cache/src/types/subscription.js.map +1 -1
  492. package/out/zero-cache/src/types/timeout.js.map +1 -1
  493. package/out/zero-cache/src/types/url-params.js.map +1 -1
  494. package/out/zero-cache/src/types/websocket-handoff.js.map +1 -1
  495. package/out/zero-cache/src/types/ws.js.map +1 -1
  496. package/out/zero-cache/src/workers/connect-params.js.map +1 -1
  497. package/out/zero-cache/src/workers/connection.js.map +1 -1
  498. package/out/zero-cache/src/workers/mutator.js.map +1 -1
  499. package/out/zero-cache/src/workers/replicator.js.map +1 -1
  500. package/out/zero-cache/src/workers/syncer-ws-message-handler.js.map +1 -1
  501. package/out/zero-cache/src/workers/syncer.js.map +1 -1
  502. package/out/zero-client/src/client/active-clients-manager.js.map +1 -1
  503. package/out/zero-client/src/client/connection-manager.js +2 -1
  504. package/out/zero-client/src/client/connection-manager.js.map +1 -1
  505. package/out/zero-client/src/client/connection.js.map +1 -1
  506. package/out/zero-client/src/client/context.js.map +1 -1
  507. package/out/zero-client/src/client/crud-impl.js.map +1 -1
  508. package/out/zero-client/src/client/crud.js.map +1 -1
  509. package/out/zero-client/src/client/custom.js +2 -1
  510. package/out/zero-client/src/client/custom.js.map +1 -1
  511. package/out/zero-client/src/client/delete-clients-manager.js.map +1 -1
  512. package/out/zero-client/src/client/enable-analytics.js.map +1 -1
  513. package/out/zero-client/src/client/error.js.map +1 -1
  514. package/out/zero-client/src/client/http-string.js.map +1 -1
  515. package/out/zero-client/src/client/inspector/client-group.js.map +1 -1
  516. package/out/zero-client/src/client/inspector/client.js.map +1 -1
  517. package/out/zero-client/src/client/inspector/html-dialog-prompt.js.map +1 -1
  518. package/out/zero-client/src/client/inspector/inspector.js.map +1 -1
  519. package/out/zero-client/src/client/inspector/lazy-inspector.js.map +1 -1
  520. package/out/zero-client/src/client/inspector/query.js.map +1 -1
  521. package/out/zero-client/src/client/ivm-branch.js.map +1 -1
  522. package/out/zero-client/src/client/keys.js.map +1 -1
  523. package/out/zero-client/src/client/log-options.js.map +1 -1
  524. package/out/zero-client/src/client/make-mutate-property.js.map +1 -1
  525. package/out/zero-client/src/client/make-replicache-mutators.js.map +1 -1
  526. package/out/zero-client/src/client/metrics.js.map +1 -1
  527. package/out/zero-client/src/client/mutation-tracker.js.map +1 -1
  528. package/out/zero-client/src/client/mutator-proxy.js.map +1 -1
  529. package/out/zero-client/src/client/options.js.map +1 -1
  530. package/out/zero-client/src/client/query-manager.js.map +1 -1
  531. package/out/zero-client/src/client/reload-error-handler.js.map +1 -1
  532. package/out/zero-client/src/client/server-option.js.map +1 -1
  533. package/out/zero-client/src/client/version.js +1 -1
  534. package/out/zero-client/src/client/zero-poke-handler.js.map +1 -1
  535. package/out/zero-client/src/client/zero-rep.js.map +1 -1
  536. package/out/zero-client/src/client/zero.d.ts.map +1 -1
  537. package/out/zero-client/src/client/zero.js +58 -32
  538. package/out/zero-client/src/client/zero.js.map +1 -1
  539. package/out/zero-client/src/util/nanoid.js.map +1 -1
  540. package/out/zero-protocol/src/analyze-query-result.js +3 -0
  541. package/out/zero-protocol/src/analyze-query-result.js.map +1 -1
  542. package/out/zero-protocol/src/application-error.js.map +1 -1
  543. package/out/zero-protocol/src/ast.js.map +1 -1
  544. package/out/zero-protocol/src/change-desired-queries.js +1 -0
  545. package/out/zero-protocol/src/change-desired-queries.js.map +1 -1
  546. package/out/zero-protocol/src/client-schema.js.map +1 -1
  547. package/out/zero-protocol/src/close-connection.js.map +1 -1
  548. package/out/zero-protocol/src/connect.js +7 -0
  549. package/out/zero-protocol/src/connect.js.map +1 -1
  550. package/out/zero-protocol/src/custom-queries.js.map +1 -1
  551. package/out/zero-protocol/src/data.js.map +1 -1
  552. package/out/zero-protocol/src/delete-clients.js.map +1 -1
  553. package/out/zero-protocol/src/down.js.map +1 -1
  554. package/out/zero-protocol/src/error.js +7 -0
  555. package/out/zero-protocol/src/error.js.map +1 -1
  556. package/out/zero-protocol/src/inspect-down.js.map +1 -1
  557. package/out/zero-protocol/src/inspect-up.js +1 -0
  558. package/out/zero-protocol/src/inspect-up.js.map +1 -1
  559. package/out/zero-protocol/src/mutate-server.js.map +1 -1
  560. package/out/zero-protocol/src/mutation-id.js.map +1 -1
  561. package/out/zero-protocol/src/mutation.js.map +1 -1
  562. package/out/zero-protocol/src/mutations-patch.js.map +1 -1
  563. package/out/zero-protocol/src/ping.js.map +1 -1
  564. package/out/zero-protocol/src/poke.js +4 -0
  565. package/out/zero-protocol/src/poke.js.map +1 -1
  566. package/out/zero-protocol/src/pong.js.map +1 -1
  567. package/out/zero-protocol/src/primary-key.js.map +1 -1
  568. package/out/zero-protocol/src/protocol-version.js.map +1 -1
  569. package/out/zero-protocol/src/pull.js.map +1 -1
  570. package/out/zero-protocol/src/push.js +16 -0
  571. package/out/zero-protocol/src/push.js.map +1 -1
  572. package/out/zero-protocol/src/queries-patch.js.map +1 -1
  573. package/out/zero-protocol/src/query-hash.js.map +1 -1
  574. package/out/zero-protocol/src/query-server.js.map +1 -1
  575. package/out/zero-protocol/src/row-patch.js.map +1 -1
  576. package/out/zero-protocol/src/up.js.map +1 -1
  577. package/out/zero-protocol/src/update-auth.js.map +1 -1
  578. package/out/zero-protocol/src/version.js.map +1 -1
  579. package/out/zero-react/src/use-connection-state.js +4 -2
  580. package/out/zero-react/src/use-connection-state.js.map +1 -1
  581. package/out/zero-react/src/use-query.js +6 -4
  582. package/out/zero-react/src/use-query.js.map +1 -1
  583. package/out/zero-react/src/use-zero-online.js +4 -2
  584. package/out/zero-react/src/use-zero-online.js.map +1 -1
  585. package/out/zero-react/src/zero-provider.js +15 -12
  586. package/out/zero-react/src/zero-provider.js.map +1 -1
  587. package/out/zero-schema/src/builder/relationship-builder.js.map +1 -1
  588. package/out/zero-schema/src/builder/schema-builder.js.map +1 -1
  589. package/out/zero-schema/src/builder/table-builder.js.map +1 -1
  590. package/out/zero-schema/src/compiled-permissions.js.map +1 -1
  591. package/out/zero-schema/src/name-mapper.js.map +1 -1
  592. package/out/zero-schema/src/permissions.js.map +1 -1
  593. package/out/zero-schema/src/schema-config.js.map +1 -1
  594. package/out/zero-server/src/adapters/drizzle.js.map +1 -1
  595. package/out/zero-server/src/adapters/kysely.js.map +1 -1
  596. package/out/zero-server/src/adapters/pg.js +1 -1
  597. package/out/zero-server/src/adapters/pg.js.map +1 -1
  598. package/out/zero-server/src/adapters/postgresjs.js.map +1 -1
  599. package/out/zero-server/src/adapters/prisma.js.map +1 -1
  600. package/out/zero-server/src/custom.js +2 -1
  601. package/out/zero-server/src/custom.js.map +1 -1
  602. package/out/zero-server/src/logging.js.map +1 -1
  603. package/out/zero-server/src/pg-query-executor.js.map +1 -1
  604. package/out/zero-server/src/process-mutations.js.map +1 -1
  605. package/out/zero-server/src/push-processor.js.map +1 -1
  606. package/out/zero-server/src/queries/process-queries.js.map +1 -1
  607. package/out/zero-server/src/schema.js.map +1 -1
  608. package/out/zero-server/src/zql-database.js.map +1 -1
  609. package/out/zero-solid/src/solid-view.js +1 -1
  610. package/out/zero-solid/src/solid-view.js.map +1 -1
  611. package/out/zero-solid/src/use-connection-state.js +1 -1
  612. package/out/zero-solid/src/use-connection-state.js.map +1 -1
  613. package/out/zero-solid/src/use-query.js +2 -2
  614. package/out/zero-solid/src/use-query.js.map +1 -1
  615. package/out/zero-solid/src/use-zero-online.js +1 -1
  616. package/out/zero-solid/src/use-zero-online.js.map +1 -1
  617. package/out/zero-solid/src/use-zero.js +1 -1
  618. package/out/zero-solid/src/use-zero.js.map +1 -1
  619. package/out/zero-types/src/format.js.map +1 -1
  620. package/out/zero-types/src/name-mapper.js.map +1 -1
  621. package/out/zql/src/builder/builder.d.ts.map +1 -1
  622. package/out/zql/src/builder/builder.js +18 -7
  623. package/out/zql/src/builder/builder.js.map +1 -1
  624. package/out/zql/src/builder/debug-delegate.d.ts +5 -0
  625. package/out/zql/src/builder/debug-delegate.d.ts.map +1 -1
  626. package/out/zql/src/builder/debug-delegate.js +10 -1
  627. package/out/zql/src/builder/debug-delegate.js.map +1 -1
  628. package/out/zql/src/builder/filter.js.map +1 -1
  629. package/out/zql/src/builder/like.js.map +1 -1
  630. package/out/zql/src/error.js.map +1 -1
  631. package/out/zql/src/ivm/array-view.js.map +1 -1
  632. package/out/zql/src/ivm/cap.d.ts +32 -0
  633. package/out/zql/src/ivm/cap.d.ts.map +1 -0
  634. package/out/zql/src/ivm/cap.js +205 -0
  635. package/out/zql/src/ivm/cap.js.map +1 -0
  636. package/out/zql/src/ivm/change.js.map +1 -1
  637. package/out/zql/src/ivm/constraint.js +1 -1
  638. package/out/zql/src/ivm/constraint.js.map +1 -1
  639. package/out/zql/src/ivm/data.js.map +1 -1
  640. package/out/zql/src/ivm/exists.js.map +1 -1
  641. package/out/zql/src/ivm/fan-in.js.map +1 -1
  642. package/out/zql/src/ivm/fan-out.js.map +1 -1
  643. package/out/zql/src/ivm/filter-operators.js.map +1 -1
  644. package/out/zql/src/ivm/filter-push.js.map +1 -1
  645. package/out/zql/src/ivm/filter.js.map +1 -1
  646. package/out/zql/src/ivm/flipped-join.d.ts +4 -8
  647. package/out/zql/src/ivm/flipped-join.d.ts.map +1 -1
  648. package/out/zql/src/ivm/flipped-join.js +59 -63
  649. package/out/zql/src/ivm/flipped-join.js.map +1 -1
  650. package/out/zql/src/ivm/join-utils.js.map +1 -1
  651. package/out/zql/src/ivm/join.js.map +1 -1
  652. package/out/zql/src/ivm/maybe-split-and-push-edit-change.js.map +1 -1
  653. package/out/zql/src/ivm/memory-source.js +1 -1
  654. package/out/zql/src/ivm/memory-source.js.map +1 -1
  655. package/out/zql/src/ivm/memory-storage.js.map +1 -1
  656. package/out/zql/src/ivm/operator.d.ts +1 -1
  657. package/out/zql/src/ivm/operator.js.map +1 -1
  658. package/out/zql/src/ivm/push-accumulated.js.map +1 -1
  659. package/out/zql/src/ivm/schema.d.ts +0 -8
  660. package/out/zql/src/ivm/schema.d.ts.map +1 -1
  661. package/out/zql/src/ivm/skip-yields.js.map +1 -1
  662. package/out/zql/src/ivm/skip.js.map +1 -1
  663. package/out/zql/src/ivm/source.js.map +1 -1
  664. package/out/zql/src/ivm/stream.js.map +1 -1
  665. package/out/zql/src/ivm/take.js +2 -2
  666. package/out/zql/src/ivm/take.js.map +1 -1
  667. package/out/zql/src/ivm/union-fan-in.d.ts.map +1 -1
  668. package/out/zql/src/ivm/union-fan-in.js +3 -1
  669. package/out/zql/src/ivm/union-fan-in.js.map +1 -1
  670. package/out/zql/src/ivm/union-fan-out.js.map +1 -1
  671. package/out/zql/src/ivm/view-apply-change.js.map +1 -1
  672. package/out/zql/src/mutate/crud.js.map +1 -1
  673. package/out/zql/src/mutate/custom.js.map +1 -1
  674. package/out/zql/src/mutate/mutator-registry.js.map +1 -1
  675. package/out/zql/src/mutate/mutator.d.ts +12 -3
  676. package/out/zql/src/mutate/mutator.d.ts.map +1 -1
  677. package/out/zql/src/mutate/mutator.js.map +1 -1
  678. package/out/zql/src/planner/planner-builder.js.map +1 -1
  679. package/out/zql/src/planner/planner-connection.js.map +1 -1
  680. package/out/zql/src/planner/planner-constraint.js.map +1 -1
  681. package/out/zql/src/planner/planner-debug.js.map +1 -1
  682. package/out/zql/src/planner/planner-fan-in.js.map +1 -1
  683. package/out/zql/src/planner/planner-fan-out.js.map +1 -1
  684. package/out/zql/src/planner/planner-graph.js.map +1 -1
  685. package/out/zql/src/planner/planner-join.d.ts.map +1 -1
  686. package/out/zql/src/planner/planner-join.js +2 -1
  687. package/out/zql/src/planner/planner-join.js.map +1 -1
  688. package/out/zql/src/planner/planner-node.js.map +1 -1
  689. package/out/zql/src/planner/planner-source.js.map +1 -1
  690. package/out/zql/src/planner/planner-terminus.js.map +1 -1
  691. package/out/zql/src/query/complete-ordering.js.map +1 -1
  692. package/out/zql/src/query/create-builder.js.map +1 -1
  693. package/out/zql/src/query/error.js.map +1 -1
  694. package/out/zql/src/query/escape-like.js.map +1 -1
  695. package/out/zql/src/query/expression.js.map +1 -1
  696. package/out/zql/src/query/measure-push-operator.js.map +1 -1
  697. package/out/zql/src/query/metrics-delegate.js.map +1 -1
  698. package/out/zql/src/query/named.js.map +1 -1
  699. package/out/zql/src/query/query-delegate-base.js.map +1 -1
  700. package/out/zql/src/query/query-impl.js +1 -1
  701. package/out/zql/src/query/query-impl.js.map +1 -1
  702. package/out/zql/src/query/query-internals.js.map +1 -1
  703. package/out/zql/src/query/query-registry.d.ts +10 -3
  704. package/out/zql/src/query/query-registry.d.ts.map +1 -1
  705. package/out/zql/src/query/query-registry.js.map +1 -1
  706. package/out/zql/src/query/runnable-query-impl.js.map +1 -1
  707. package/out/zql/src/query/static-query.js.map +1 -1
  708. package/out/zql/src/query/ttl.js.map +1 -1
  709. package/out/zql/src/query/validate-input.js.map +1 -1
  710. package/out/zqlite/src/database-storage.js.map +1 -1
  711. package/out/zqlite/src/db.js.map +1 -1
  712. package/out/zqlite/src/explain-queries.js.map +1 -1
  713. package/out/zqlite/src/internal/sql-inline.js.map +1 -1
  714. package/out/zqlite/src/internal/sql.js.map +1 -1
  715. package/out/zqlite/src/internal/statement-cache.js.map +1 -1
  716. package/out/zqlite/src/query-builder.js.map +1 -1
  717. package/out/zqlite/src/query-delegate.js.map +1 -1
  718. package/out/zqlite/src/resolve-scalar-subqueries.js.map +1 -1
  719. package/out/zqlite/src/sqlite-cost-model.js.map +1 -1
  720. package/out/zqlite/src/sqlite-stat-fanout.js.map +1 -1
  721. package/out/zqlite/src/table-source.d.ts.map +1 -1
  722. package/out/zqlite/src/table-source.js +7 -7
  723. package/out/zqlite/src/table-source.js.map +1 -1
  724. package/package.json +23 -23
  725. package/out/zero-client/src/util/socket.d.ts +0 -3
  726. package/out/zero-client/src/util/socket.d.ts.map +0 -1
  727. package/out/zero-client/src/util/socket.js +0 -8
  728. package/out/zero-client/src/util/socket.js.map +0 -1
package/README.md CHANGED
@@ -19,10 +19,10 @@ To build and install the package locally, run the following commands:
19
19
  ```bash
20
20
  git clone git@github.com:rocicorp/mono.git
21
21
  cd mono
22
- npm install
23
- npm run build
22
+ pnpm install
23
+ pnpm run build
24
24
  cd packages/zero
25
- npm pack
25
+ pnpm pack
26
26
  ```
27
27
 
28
28
  This creates a tgz (tarball) file in the `packages/zero` directory. You can then install this package in another project by running:
@@ -30,3 +30,28 @@ This creates a tgz (tarball) file in the `packages/zero` directory. You can then
30
30
  ```bash
31
31
  npm install /path/to/rocicorp-zero-<VERSION>.tgz
32
32
  ```
33
+
34
+ ### Releasing
35
+
36
+ Releases are driven by `packages/zero/tool/release.ts` via the `release` GitHub Actions workflow.
37
+ Run `node packages/zero/tool/release.ts --help` for the full CLI reference.
38
+
39
+ To trigger a release via CI, use the [Actions UI](https://github.com/rocicorp/mono/actions/workflows/release.yml) or the `gh` CLI:
40
+
41
+ ```bash
42
+ # Canary from main
43
+ gh workflow run release.yml -f mode=canary -f ref=main
44
+
45
+ # Stable from main (or a tag / commit SHA)
46
+ gh workflow run release.yml -f mode=stable -f ref=main
47
+ gh workflow run release.yml -f mode=stable -f ref=v1.2.3
48
+ gh workflow run release.yml -f mode=stable -f ref=<40-char-sha>
49
+
50
+ # Dry run — build and version-bump without publishing
51
+ gh workflow run release.yml -f mode=canary -f ref=main -f dry_run=true
52
+ ```
53
+
54
+ By default a stable release sets both npm and Docker tags to `latest`. Pass `-f promote_latest=false` to skip that.
55
+
56
+ > **Note**: `-f ref=...` is the build target (branch, tag, or SHA). `--ref` selects which branch
57
+ > the _workflow file itself_ is read from — only needed when running the workflow off a non-default branch.
@@ -1,4 +1,4 @@
1
- //#region \0@oxc-project+runtime@0.122.0/helpers/usingCtx.js
1
+ //#region \0@oxc-project+runtime@0.130.0/helpers/usingCtx.js
2
2
  function _usingCtx() {
3
3
  var r = "function" == typeof SuppressedError ? SuppressedError : function(r, e) {
4
4
  var n = Error();
@@ -0,0 +1,13 @@
1
+ import { __esmMin, __exportAll } from "./_rolldown/runtime.js";
2
+ //#region __vite-optional-peer-dep:pg-native:pg
3
+ var __vite_optional_peer_dep_pg_native_pg_exports = /* @__PURE__ */ __exportAll({ default: () => __vite_optional_peer_dep_pg_native_pg_default });
4
+ var __vite_optional_peer_dep_pg_native_pg_default;
5
+ var init___vite_optional_peer_dep_pg_native_pg = __esmMin((() => {
6
+ __vite_optional_peer_dep_pg_native_pg_default = {};
7
+ throw new Error(`Could not resolve "pg-native" imported by "pg". Is it installed?`);
8
+ }));
9
+ //#endregion
10
+ init___vite_optional_peer_dep_pg_native_pg();
11
+ export { __vite_optional_peer_dep_pg_native_pg_exports, init___vite_optional_peer_dep_pg_native_pg };
12
+
13
+ //# sourceMappingURL=__vite-optional-peer-dep_pg-native_pg.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"__vite-optional-peer-dep_pg-native_pg.js","names":[],"sources":["../../__vite-optional-peer-dep:pg-native:pg"],"sourcesContent":["export default {};\nthrow new Error(`Could not resolve \"pg-native\" imported by \"pg\". Is it installed?`)"],"mappings":";;;;;iDAAe,CAAC;CAChB,MAAM,IAAI,MAAM,kEAAkE"}
@@ -1,8 +1,13 @@
1
+ import { createRequire } from "node:module";
1
2
  //#region \0rolldown/runtime.js
3
+ var __create = Object.create;
2
4
  var __defProp = Object.defineProperty;
3
5
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
5
8
  var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __esmMin = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
10
+ var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
6
11
  var __exportAll = (all, no_symbols) => {
7
12
  let target = {};
8
13
  for (var name in all) __defProp(target, name, {
@@ -23,5 +28,11 @@ var __copyProps = (to, from, except, desc) => {
23
28
  return to;
24
29
  };
25
30
  var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
31
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
32
+ value: mod,
33
+ enumerable: true
34
+ }) : target, mod));
35
+ var __toCommonJS = (mod) => __hasOwnProp.call(mod, "module.exports") ? mod["module.exports"] : __copyProps(__defProp({}, "__esModule", { value: true }), mod);
36
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
26
37
  //#endregion
27
- export { __exportAll, __reExport };
38
+ export { __commonJSMin, __esmMin, __exportAll, __reExport, __require, __toCommonJS, __toESM };
@@ -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 {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 'Has no functional effect on analysis; defaults to \"analyze-cli\".',\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().default(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().default(false),\n desc: ['Include the rows that would be synced to the client.'],\n },\n log: {\n ...logOptions,\n level: logLevel.default('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: ` tsx cli.ts --zero-cache-url=https://zero.example.com \\\\\n --admin-password=\"$ZERO_ADMIN_PASSWORD\" \\\\\n --query='issue.related(\"comments\").limit(10)'\n\n tsx cli.ts --zero-cache-url=http://localhost:4848 \\\\\n --ast='\\\\{\"table\": \"issue\", \"limit\": 5\\\\}'\n\n tsx 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 tsx 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 ?? 'analyze-cli',\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 };\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\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":[],"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 {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 'Has no functional effect on analysis; defaults to \"analyze-cli\".',\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().default(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().default(false),\n desc: ['Include the rows that would be synced to the client.'],\n },\n log: {\n ...logOptions,\n level: logLevel.default('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: ` tsx cli.ts --zero-cache-url=https://zero.example.com \\\\\n --admin-password=\"$ZERO_ADMIN_PASSWORD\" \\\\\n --query='issue.related(\"comments\").limit(10)'\n\n tsx cli.ts --zero-cache-url=http://localhost:4848 \\\\\n --ast='\\\\{\"table\": \"issue\", \"limit\": 5\\\\}'\n\n tsx 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 tsx 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 ?? 'analyze-cli',\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 };\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\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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"}
@@ -238,7 +238,12 @@ for (const [table, queries] of Object.entries(nvisitCounts)) {
238
238
  }
239
239
  colorConsole.log(styleText("bold", "total rows scanned:"), colorRowsConsidered(totalNVisit));
240
240
  colorConsole.log(styleText(["blue", "bold"], "\n\n=== Query Plans: ===\n"));
241
- var plans = explainQueries(debug.getVendedRowCounts() ?? {}, db);
241
+ var fallbackPlans = explainQueries(debug.getVendedRowCounts() ?? {}, db);
242
+ var capturedPlans = debug.getSQLitePlans();
243
+ var plans = {
244
+ ...fallbackPlans,
245
+ ...capturedPlans
246
+ };
242
247
  for (const [query, plan] of Object.entries(plans)) {
243
248
  colorConsole.log(styleText("bold", "query"), query);
244
249
  colorConsole.log(plan.map((row, i) => colorPlanRow(row, i)).join("\n"));
@@ -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 {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().default(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().default(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().default(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 log: {\n ...logOptions,\n level: logLevel.default('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();\nconst tableSpecs = computeZqlSpecs(lc, db, {includeBackfillingColumns: false});\n\nclass AnalyzeQueryDelegate extends QueryDelegateBase {\n readonly debug = debug;\n readonly defaultQueryComplete = true;\n\n getSource(serverTableName: string): Source | undefined {\n let source = sources.get(serverTableName);\n if (source) {\n return source;\n }\n const tableSpec = mustGetTableSpec(tableSpecs, serverTableName);\n const {primaryKey} = tableSpec.tableSpec;\n\n source = new TableSource(\n lc,\n config.log,\n db,\n serverTableName,\n tableSpec.zqlSpec,\n primaryKey,\n );\n\n sources.set(serverTableName, source);\n return source;\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 plans = explainQueries(debug.getVendedRowCounts() ?? {}, db);\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":[],"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 {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().default(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().default(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().default(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 log: {\n ...logOptions,\n level: logLevel.default('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();\nconst tableSpecs = computeZqlSpecs(lc, db, {includeBackfillingColumns: false});\n\nclass AnalyzeQueryDelegate extends QueryDelegateBase {\n readonly debug = debug;\n readonly defaultQueryComplete = true;\n\n getSource(serverTableName: string): Source | undefined {\n let source = sources.get(serverTableName);\n if (source) {\n return source;\n }\n const tableSpec = mustGetTableSpec(tableSpecs, serverTableName);\n const {primaryKey} = tableSpec.tableSpec;\n\n source = new TableSource(\n lc,\n config.log,\n db,\n serverTableName,\n tableSpec.zqlSpec,\n primaryKey,\n );\n\n sources.set(serverTableName, source);\n return source;\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 +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().default('./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,aAdC;CACd,KAAK,EAAC,IAAI,eAAE,QAAQ,EAAC;CACrB,QAAQ;EACN,MAAM,eAAE,QAAQ,CAAC,QAAQ,cAAc;EACvC,MAAM,CAAC,2BAA2B;EACnC;CACD,KAAK;CACL,OAAO;CACP,MAAM;EACJ,MAAM,eAAE,QAAQ,CAAC,UAAU;EAC3B,MAAM,CAAC,0CAA0C;EAClD;CACF,EAEoC,EAAC,eAAe,qBAAoB,CAAC;AAE1E,IAAM,KAAK,IAAI,WAAW,SAAS,EAAE,EAAE,eAAe;AACtD,IAAM,EAAC,gBAAe,MAAM,yBAAyB,OAAO,OAAO;AAEnE,IAAM,QAAQ,SAAS,IAAI,OAAO,IAAI,IAAI,8BAA8B;AAExE,IAAM,OACJ,MAAM,KAAK,uCAAuC,MAAM,eAAe,WAAW,OAAO,CAAC,GAAG,OAAO,CAAC,iCAAiC,KACpI,OAAO,KACR,CAAC;AAEJ,IAAM,WAAW,sBACf,IACA,IACA,KAAK,GAAG,WACR,aACA;CACE,MAAM;CACN,KAAK;CACL,SAAS,EAAE;CACZ,EACD,KAAK,GAAG,SACT,CAAC;AAGF,QAAQ,IAAI,kBAAkB;AAC9B,QAAQ,IAAI,KAAK,UAAU,UAAU,MAAM,EAAE,CAAC;AAC9C,QAAQ,IAAI,kBAAkB;AAC9B,QAAQ,IAAI,MAAM,aAAa,SAAS,QAAQ,SAAS,SAAS,CAAC,CAAC;AAGpE,MAAM,MAAM,KAAK"}
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().default('./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,QAAQ,aAAa;EACtC,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 +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): string {\n let code = '';\n\n // Handle where conditions\n if (ast.where) {\n code += transformCondition(ast.where, '.where', new Set());\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);\n }\n } else {\n code += transformRelated(related);\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(${JSON.stringify(row)}${\n exclusive ? '' : ', { inclusive: true }'\n })`;\n }\n\n return code;\n}\n\ntype Args = Set<string>;\n\ntype Prefix = '.where' | 'cmp';\n\nfunction transformCondition(\n condition: Condition,\n prefix: Prefix,\n args: Args,\n): string {\n switch (condition.type) {\n case 'simple':\n return transformSimpleCondition(condition, prefix);\n case 'and':\n case 'or':\n return transformLogicalCondition(condition, prefix, args);\n case 'correlatedSubquery':\n return transformExistsCondition(condition, prefix, args);\n default:\n unreachable(condition);\n }\n}\n\nfunction transformSimpleCondition(\n condition: SimpleCondition,\n prefix: Prefix,\n): string {\n const {left, op, right} = condition;\n\n const leftCode = transformValuePosition(left);\n const rightCode = transformValuePosition(right);\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): 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);\n }\n\n // Generate multiple where calls for top-level AND conditions\n if (type === 'and') {\n const parts = conditions.map(c => transformCondition(c, prefix, args));\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))\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): 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)}${optionsStr})`;\n }\n prefix satisfies 'cmp';\n args.add('exists');\n return `exists('${relationship}', q => q${astToZQL(nextSubquery)}${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 )}${optionsStr})))`;\n }\n prefix satisfies 'cmp';\n args.add('not');\n args.add('exists');\n return `not(exists('${relationship}', q => q${astToZQL(nextSubquery)}${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(related: CorrelatedSubquery): 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);\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(value: ValuePosition): string {\n switch (value.type) {\n case 'literal':\n return 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":[],"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): string {\n let code = '';\n\n // Handle where conditions\n if (ast.where) {\n code += transformCondition(ast.where, '.where', new Set());\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);\n }\n } else {\n code += transformRelated(related);\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(${JSON.stringify(row)}${\n exclusive ? '' : ', { inclusive: true }'\n })`;\n }\n\n return code;\n}\n\ntype Args = Set<string>;\n\ntype Prefix = '.where' | 'cmp';\n\nfunction transformCondition(\n condition: Condition,\n prefix: Prefix,\n args: Args,\n): string {\n switch (condition.type) {\n case 'simple':\n return transformSimpleCondition(condition, prefix);\n case 'and':\n case 'or':\n return transformLogicalCondition(condition, prefix, args);\n case 'correlatedSubquery':\n return transformExistsCondition(condition, prefix, args);\n default:\n unreachable(condition);\n }\n}\n\nfunction transformSimpleCondition(\n condition: SimpleCondition,\n prefix: Prefix,\n): string {\n const {left, op, right} = condition;\n\n const leftCode = transformValuePosition(left);\n const rightCode = transformValuePosition(right);\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): 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);\n }\n\n // Generate multiple where calls for top-level AND conditions\n if (type === 'and') {\n const parts = conditions.map(c => transformCondition(c, prefix, args));\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))\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): 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)}${optionsStr})`;\n }\n prefix satisfies 'cmp';\n args.add('exists');\n return `exists('${relationship}', q => q${astToZQL(nextSubquery)}${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 )}${optionsStr})))`;\n }\n prefix satisfies 'cmp';\n args.add('not');\n args.add('exists');\n return `not(exists('${relationship}', q => q${astToZQL(nextSubquery)}${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(related: CorrelatedSubquery): 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);\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(value: ValuePosition): string {\n switch (value.type) {\n case 'literal':\n return 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 +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,aATC,EACd,QAAQ;CACN,MAAM,eAAE,QAAQ,CAAC,UAAU;CAC3B,MAAM,CACJ,uEACD;CACF,EACF,CAEmC;AAEpC,IAAI;AACJ,IAAI,OAAO,OACT,WAAU,MAAM,yBAAyB,OAAO,OAAO,EAAE;AAG3D,SAAS,eAAwB;AAC/B,QAAO,CAAC,QAAQ,MAAM;;AAGxB,SAAS,gBAAiC;AACxC,QAAO,IAAI,SAAQ,YAAW;EAC5B,IAAI,OAAO;EACX,MAAM,KAAK,gBAAgB;GACzB,OAAO,QAAQ;GACf,QAAQ,QAAQ;GAChB,UAAU;GACX,CAAC;AAEF,KAAG,GAAG,SAAQ,SAAQ;AACpB,WAAQ,OAAO;IACf;AAEF,KAAG,GAAG,eAAe;AACnB,WAAQ,KAAK;IACb;GACF;;AAGJ,eAAe,OAAsB;AACnC,KAAI;EACF,IAAI;AAEJ,MAAI,cAAc,CAChB,SAAQ,MAAM,eAAe;WACpB,QAAQ,KAAK,SAAS,GAAG;GAClC,MAAM,WAAW,QAAQ,KAAK;AAC9B,WAAQ,MAAM,SAAS,UAAU,QAAQ;SACpC;AACL,WAAQ,MAAM,4BAA4B;AAC1C,WAAQ,MAAM,SAAS;AACvB,WAAQ,MAAM,kCAAkC;AAChD,WAAQ,MAAM,4BAA4B;AAC1C,WAAQ,KAAK,EAAE;;EAGjB,IAAI,MAAM,KAAK,MAAM,MAAM;AAC3B,MAAI,QAAQ;GACV,MAAM,SAAS,eAAe,OAAO,OAAO;AAC5C,SAAM,OAAO,KAAK,OAAO;;EAG3B,MAAM,MAAM,SAAS,IAAI;EACzB,MAAM,OAAO,SAAS,IAAI,QAAQ;AAElC,UAAQ,IAAI,MAAM,aAAa,KAAK,CAAC;UAC9B,OAAO;AACd,UAAQ,MAAM,2BAA2B,MAAM;AAC/C,UAAQ,KAAK,EAAE;;;AAInB,MAAM,MAAM"}
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 +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;AACnE,KAAI;AAKF,UAJe,MAAM,OAAO,aAAa,SAAS;GAChD,MAAM;GACN,YAAY;GACb,CAAC,EACY;UACP,OAAO;AAEd,UAAQ,KAAK,gDAAgD,MAAM;AACnE,SAAO"}
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 +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":["#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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"}
@@ -0,0 +1,12 @@
1
+ //#region ../../node_modules/.pnpm/@opentelemetry+semantic-conventions@1.41.1/node_modules/@opentelemetry/semantic-conventions/build/esm/stable_attributes.js
2
+ /**
3
+ * The version string of the service component. The format is not defined by these conventions.
4
+ *
5
+ * @example 2.0.0
6
+ * @example a01dbef8a
7
+ */
8
+ var ATTR_SERVICE_VERSION = "service.version";
9
+ //#endregion
10
+ export { ATTR_SERVICE_VERSION };
11
+
12
+ //# sourceMappingURL=stable_attributes.js.map