tapimo 0.2.0 → 0.4.0

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 (362) hide show
  1. package/README.md +36 -8
  2. package/dist/ApiKey.d.ts +1 -0
  3. package/dist/ApiKey.d.ts.map +1 -1
  4. package/dist/ApiKey.js +5 -2
  5. package/dist/ApiKey.js.map +1 -1
  6. package/dist/ApiKeys.d.ts +28 -1
  7. package/dist/ApiKeys.d.ts.map +1 -1
  8. package/dist/ApiKeys.js +34 -1
  9. package/dist/ApiKeys.js.map +1 -1
  10. package/dist/App.d.ts +7532 -4850
  11. package/dist/App.d.ts.map +1 -1
  12. package/dist/App.js +11 -3
  13. package/dist/App.js.map +1 -1
  14. package/dist/Scope.d.ts +11 -2
  15. package/dist/Scope.d.ts.map +1 -1
  16. package/dist/Scope.js +14 -3
  17. package/dist/Scope.js.map +1 -1
  18. package/dist/admin/App.d.ts +63 -53
  19. package/dist/admin/App.d.ts.map +1 -1
  20. package/dist/admin/apps/api-keys.d.ts +7 -0
  21. package/dist/admin/apps/api-keys.d.ts.map +1 -1
  22. package/dist/admin/apps/scopes.d.ts +8 -0
  23. package/dist/admin/apps/scopes.d.ts.map +1 -1
  24. package/dist/admin/apps/scopes.js +3 -0
  25. package/dist/admin/apps/scopes.js.map +1 -1
  26. package/dist/admin/apps/verified-tokens.d.ts +57 -57
  27. package/dist/admin/ui.generated.d.ts +1 -1
  28. package/dist/admin/ui.generated.js +13 -13
  29. package/dist/admin/ui.generated.js.map +1 -1
  30. package/dist/analytics/Analytics.d.ts +151 -0
  31. package/dist/analytics/Analytics.d.ts.map +1 -0
  32. package/dist/analytics/Analytics.js +212 -0
  33. package/dist/analytics/Analytics.js.map +1 -0
  34. package/dist/analytics/tables/requestEvents.d.ts +215 -0
  35. package/dist/analytics/tables/requestEvents.d.ts.map +1 -0
  36. package/dist/analytics/tables/requestEvents.js +264 -0
  37. package/dist/analytics/tables/requestEvents.js.map +1 -0
  38. package/dist/apps/data/App.d.ts +1049 -624
  39. package/dist/apps/data/App.d.ts.map +1 -1
  40. package/dist/apps/data/App.js +36 -0
  41. package/dist/apps/data/App.js.map +1 -1
  42. package/dist/apps/data/FundingRouteProviders.d.ts +54 -0
  43. package/dist/apps/data/FundingRouteProviders.d.ts.map +1 -0
  44. package/dist/apps/data/FundingRouteProviders.js +355 -0
  45. package/dist/apps/data/FundingRouteProviders.js.map +1 -0
  46. package/dist/apps/data/index.d.ts +1 -0
  47. package/dist/apps/data/index.d.ts.map +1 -1
  48. package/dist/apps/data/index.js +1 -0
  49. package/dist/apps/data/index.js.map +1 -1
  50. package/dist/apps/data/routes/balances.d.ts +45 -45
  51. package/dist/apps/data/routes/coingecko.d.ts +176 -176
  52. package/dist/apps/data/routes/exchanges.d.ts +455 -455
  53. package/dist/apps/data/routes/fee-amm.d.ts +101 -101
  54. package/dist/apps/data/routes/funding.d.ts +843 -0
  55. package/dist/apps/data/routes/funding.d.ts.map +1 -0
  56. package/dist/apps/data/routes/funding.js +443 -0
  57. package/dist/apps/data/routes/funding.js.map +1 -0
  58. package/dist/apps/data/routes/indexer.d.ts +5 -5
  59. package/dist/apps/data/routes/receipts.d.ts.map +1 -1
  60. package/dist/apps/data/routes/receipts.js +2 -8
  61. package/dist/apps/data/routes/receipts.js.map +1 -1
  62. package/dist/apps/data/routes/rpc.d.ts +29 -36
  63. package/dist/apps/data/routes/rpc.d.ts.map +1 -1
  64. package/dist/apps/data/routes/rpc.js +29 -1
  65. package/dist/apps/data/routes/rpc.js.map +1 -1
  66. package/dist/apps/data/routes/tokenlist.d.ts +36 -36
  67. package/dist/apps/data/routes/transfers.d.ts +56 -56
  68. package/dist/apps/data/routes/verified-tokens.d.ts +36 -36
  69. package/dist/apps/data/routes/webhooks.d.ts +45 -45
  70. package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
  71. package/dist/apps/data/routes/webhooks.js +8 -8
  72. package/dist/apps/data/routes/webhooks.js.map +1 -1
  73. package/dist/apps/docs/App.d.ts +5 -3
  74. package/dist/apps/docs/App.d.ts.map +1 -1
  75. package/dist/apps/docs/App.js +28 -96
  76. package/dist/apps/docs/App.js.map +1 -1
  77. package/dist/apps/docs/reference.d.ts +21 -0
  78. package/dist/apps/docs/reference.d.ts.map +1 -0
  79. package/dist/apps/docs/reference.js +112 -0
  80. package/dist/apps/docs/reference.js.map +1 -0
  81. package/dist/apps/index.d.ts +3 -2
  82. package/dist/apps/index.d.ts.map +1 -1
  83. package/dist/apps/index.js +3 -2
  84. package/dist/apps/index.js.map +1 -1
  85. package/dist/apps/{platform → management}/App.d.ts +3015 -738
  86. package/dist/apps/management/App.d.ts.map +1 -0
  87. package/dist/apps/{platform → management}/App.js +20 -10
  88. package/dist/apps/management/App.js.map +1 -0
  89. package/dist/apps/management/Billing.d.ts +279 -0
  90. package/dist/apps/management/Billing.d.ts.map +1 -0
  91. package/dist/apps/management/Billing.js +477 -0
  92. package/dist/apps/management/Billing.js.map +1 -0
  93. package/dist/apps/management/index.d.ts +3 -0
  94. package/dist/apps/management/index.d.ts.map +1 -0
  95. package/dist/apps/management/index.js +3 -0
  96. package/dist/apps/management/index.js.map +1 -0
  97. package/dist/apps/{platform → management}/routes/api-keys.d.ts +341 -49
  98. package/dist/apps/management/routes/api-keys.d.ts.map +1 -0
  99. package/dist/apps/{platform → management}/routes/api-keys.js +116 -14
  100. package/dist/apps/management/routes/api-keys.js.map +1 -0
  101. package/dist/apps/management/routes/billing.d.ts +1520 -0
  102. package/dist/apps/management/routes/billing.d.ts.map +1 -0
  103. package/dist/apps/management/routes/billing.js +795 -0
  104. package/dist/apps/management/routes/billing.js.map +1 -0
  105. package/dist/apps/{platform → management}/routes/invitations.d.ts +202 -84
  106. package/dist/apps/management/routes/invitations.d.ts.map +1 -0
  107. package/dist/apps/{platform → management}/routes/invitations.js +59 -1
  108. package/dist/apps/management/routes/invitations.js.map +1 -0
  109. package/dist/apps/{platform → management}/routes/me.d.ts +17 -17
  110. package/dist/apps/management/routes/me.d.ts.map +1 -0
  111. package/dist/apps/management/routes/me.js.map +1 -0
  112. package/dist/apps/management/routes/members.d.ts.map +1 -0
  113. package/dist/apps/management/routes/members.js.map +1 -0
  114. package/dist/apps/{platform → management}/routes/orgs.d.ts +50 -36
  115. package/dist/apps/management/routes/orgs.d.ts.map +1 -0
  116. package/dist/apps/{platform → management}/routes/orgs.js +14 -3
  117. package/dist/apps/management/routes/orgs.js.map +1 -0
  118. package/dist/apps/{platform → management}/routes/projects.d.ts +97 -97
  119. package/dist/apps/{platform → management}/routes/projects.d.ts.map +1 -1
  120. package/dist/apps/management/routes/projects.js.map +1 -0
  121. package/dist/apps/management/routes/scopes.d.ts +107 -0
  122. package/dist/apps/management/routes/scopes.d.ts.map +1 -0
  123. package/dist/apps/management/routes/scopes.js +66 -0
  124. package/dist/apps/management/routes/scopes.js.map +1 -0
  125. package/dist/apps/management/routes/usage.d.ts +588 -0
  126. package/dist/apps/management/routes/usage.d.ts.map +1 -0
  127. package/dist/apps/management/routes/usage.js +391 -0
  128. package/dist/apps/management/routes/usage.js.map +1 -0
  129. package/dist/apps/mcp.d.ts +76 -0
  130. package/dist/apps/mcp.d.ts.map +1 -0
  131. package/dist/apps/mcp.js +188 -0
  132. package/dist/apps/mcp.js.map +1 -0
  133. package/dist/apps/relay/App.d.ts +4 -1
  134. package/dist/apps/relay/App.d.ts.map +1 -1
  135. package/dist/apps/relay/App.js +118 -10
  136. package/dist/apps/relay/App.js.map +1 -1
  137. package/dist/apps/relay/Sponsorships.d.ts.map +1 -1
  138. package/dist/apps/relay/Sponsorships.js +2 -1
  139. package/dist/apps/relay/Sponsorships.js.map +1 -1
  140. package/dist/cli/Cli.d.ts +8 -1
  141. package/dist/cli/Cli.d.ts.map +1 -1
  142. package/dist/cli/Cli.js +11 -3
  143. package/dist/cli/Cli.js.map +1 -1
  144. package/dist/cli/commands/admin.d.ts +4 -0
  145. package/dist/cli/commands/admin.d.ts.map +1 -1
  146. package/dist/cli/commands/admin.js +80 -1
  147. package/dist/cli/commands/admin.js.map +1 -1
  148. package/dist/db/Db.d.ts +7 -0
  149. package/dist/db/Db.d.ts.map +1 -1
  150. package/dist/db/Db.js +126 -0
  151. package/dist/db/Db.js.map +1 -1
  152. package/dist/db/Schema.d.ts +79 -0
  153. package/dist/db/Schema.d.ts.map +1 -1
  154. package/dist/db/Schema.js +88 -2
  155. package/dist/db/Schema.js.map +1 -1
  156. package/dist/db/tables/billingSettings.d.ts +55 -0
  157. package/dist/db/tables/billingSettings.d.ts.map +1 -0
  158. package/dist/db/tables/billingSettings.js +70 -0
  159. package/dist/db/tables/billingSettings.js.map +1 -0
  160. package/dist/db/tables/invitations.js +2 -2
  161. package/dist/db/tables/invitations.js.map +1 -1
  162. package/dist/db/tables/organizations.d.ts +6 -2
  163. package/dist/db/tables/organizations.d.ts.map +1 -1
  164. package/dist/db/tables/organizations.js +10 -4
  165. package/dist/db/tables/organizations.js.map +1 -1
  166. package/dist/db/tables/projects.js +2 -2
  167. package/dist/db/tables/projects.js.map +1 -1
  168. package/dist/db/tables/requestUsage.d.ts +121 -0
  169. package/dist/db/tables/requestUsage.d.ts.map +1 -0
  170. package/dist/db/tables/requestUsage.js +150 -0
  171. package/dist/db/tables/requestUsage.js.map +1 -0
  172. package/dist/db/tables/sponsoredTransactions.d.ts +119 -0
  173. package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -1
  174. package/dist/db/tables/sponsoredTransactions.js +155 -2
  175. package/dist/db/tables/sponsoredTransactions.js.map +1 -1
  176. package/dist/db/tables/stripeCustomers.d.ts +81 -0
  177. package/dist/db/tables/stripeCustomers.d.ts.map +1 -0
  178. package/dist/db/tables/stripeCustomers.js +120 -0
  179. package/dist/db/tables/stripeCustomers.js.map +1 -0
  180. package/dist/db/tables/users.js +2 -2
  181. package/dist/db/tables/users.js.map +1 -1
  182. package/dist/handlers/internal/sponsorship.d.ts +5 -2
  183. package/dist/handlers/internal/sponsorship.d.ts.map +1 -1
  184. package/dist/handlers/internal/sponsorship.js +19 -5
  185. package/dist/handlers/internal/sponsorship.js.map +1 -1
  186. package/dist/handlers/internal/utils.d.ts +10 -3
  187. package/dist/handlers/internal/utils.d.ts.map +1 -1
  188. package/dist/handlers/internal/utils.js +14 -6
  189. package/dist/handlers/internal/utils.js.map +1 -1
  190. package/dist/index.d.ts +1 -0
  191. package/dist/index.d.ts.map +1 -1
  192. package/dist/index.js +1 -0
  193. package/dist/index.js.map +1 -1
  194. package/dist/internal/Auth.d.ts +18 -4
  195. package/dist/internal/Auth.d.ts.map +1 -1
  196. package/dist/internal/Auth.js +149 -77
  197. package/dist/internal/Auth.js.map +1 -1
  198. package/dist/internal/Cache.d.ts.map +1 -1
  199. package/dist/internal/Cache.js +4 -1
  200. package/dist/internal/Cache.js.map +1 -1
  201. package/dist/internal/EdgeCache.d.ts +10 -0
  202. package/dist/internal/EdgeCache.d.ts.map +1 -1
  203. package/dist/internal/EdgeCache.js +39 -8
  204. package/dist/internal/EdgeCache.js.map +1 -1
  205. package/dist/internal/Fees.d.ts +27 -0
  206. package/dist/internal/Fees.d.ts.map +1 -0
  207. package/dist/internal/Fees.js +48 -0
  208. package/dist/internal/Fees.js.map +1 -0
  209. package/dist/internal/FundingRoutes.d.ts +915 -0
  210. package/dist/internal/FundingRoutes.d.ts.map +1 -0
  211. package/dist/internal/FundingRoutes.js +625 -0
  212. package/dist/internal/FundingRoutes.js.map +1 -0
  213. package/dist/internal/Id.d.ts +15 -0
  214. package/dist/internal/Id.d.ts.map +1 -0
  215. package/dist/internal/Id.js +22 -0
  216. package/dist/internal/Id.js.map +1 -0
  217. package/dist/internal/Log.d.ts +2 -0
  218. package/dist/internal/Log.d.ts.map +1 -1
  219. package/dist/internal/Log.js +3 -0
  220. package/dist/internal/Log.js.map +1 -1
  221. package/dist/internal/OpenApi.d.ts +10 -1
  222. package/dist/internal/OpenApi.d.ts.map +1 -1
  223. package/dist/internal/OpenApi.js +8 -1
  224. package/dist/internal/OpenApi.js.map +1 -1
  225. package/dist/internal/RateLimit.d.ts +25 -0
  226. package/dist/internal/RateLimit.d.ts.map +1 -1
  227. package/dist/internal/RateLimit.js +51 -0
  228. package/dist/internal/RateLimit.js.map +1 -1
  229. package/dist/internal/Response.d.ts +1 -1
  230. package/dist/internal/Response.d.ts.map +1 -1
  231. package/dist/internal/Schema.d.ts +8 -0
  232. package/dist/internal/Schema.d.ts.map +1 -1
  233. package/dist/internal/Schema.js +7 -0
  234. package/dist/internal/Schema.js.map +1 -1
  235. package/dist/internal/Webhooks.d.ts.map +1 -1
  236. package/dist/internal/Webhooks.js +3 -3
  237. package/dist/internal/Webhooks.js.map +1 -1
  238. package/package.json +8 -7
  239. package/src/ApiKey.ts +9 -2
  240. package/src/ApiKeys.test.ts +51 -0
  241. package/src/ApiKeys.ts +50 -1
  242. package/src/App.test.ts +33 -86718
  243. package/src/App.ts +26 -8
  244. package/src/Client.test-d.ts +353 -2
  245. package/src/Scope.ts +16 -3
  246. package/src/admin/apps/scopes.test.ts +6 -0
  247. package/src/admin/apps/scopes.ts +3 -0
  248. package/src/admin/ui/src/components/Page.tsx +2 -3
  249. package/src/admin/ui/src/routes/__root.tsx +2 -2
  250. package/src/admin/ui/src/routes/api-keys.tsx +2 -2
  251. package/src/admin/ui.generated.ts +13 -13
  252. package/src/analytics/Analytics.test.ts +527 -0
  253. package/src/analytics/Analytics.ts +318 -0
  254. package/src/analytics/tables/requestEvents.ts +523 -0
  255. package/src/apps/composition.test.ts +26 -13
  256. package/src/apps/data/App.test.ts +72 -0
  257. package/src/apps/data/App.ts +37 -0
  258. package/src/apps/data/FundingRouteProviders.test.ts +281 -0
  259. package/src/apps/data/FundingRouteProviders.ts +459 -0
  260. package/src/apps/data/index.ts +1 -0
  261. package/src/apps/data/routes/funding.test.ts +314 -0
  262. package/src/apps/data/routes/funding.ts +698 -0
  263. package/src/apps/data/routes/receipts.ts +2 -9
  264. package/src/apps/data/routes/rpc.test.ts +78 -2
  265. package/src/apps/data/routes/rpc.ts +31 -1
  266. package/src/apps/data/routes/webhooks.test.ts +1 -1
  267. package/src/apps/data/routes/webhooks.ts +11 -8
  268. package/src/apps/docs/App.ts +30 -101
  269. package/src/apps/docs/reference.ts +132 -0
  270. package/src/apps/index.ts +3 -2
  271. package/src/apps/{platform → management}/App.ts +56 -17
  272. package/src/apps/management/Billing.test.ts +530 -0
  273. package/src/apps/management/Billing.ts +682 -0
  274. package/src/apps/management/index.ts +2 -0
  275. package/src/apps/{platform → management}/routes/api-keys.test.ts +172 -9
  276. package/src/apps/{platform → management}/routes/api-keys.ts +137 -21
  277. package/src/apps/management/routes/billing.test.ts +1100 -0
  278. package/src/apps/management/routes/billing.ts +966 -0
  279. package/src/apps/{platform → management}/routes/invitations.test.ts +55 -7
  280. package/src/apps/{platform → management}/routes/invitations.ts +69 -1
  281. package/src/apps/management/routes/me.test.ts +351 -0
  282. package/src/apps/{platform → management}/routes/orgs.ts +26 -4
  283. package/src/apps/management/routes/scopes.test.ts +29 -0
  284. package/src/apps/management/routes/scopes.ts +85 -0
  285. package/src/apps/management/routes/usage.test.ts +422 -0
  286. package/src/apps/management/routes/usage.ts +527 -0
  287. package/src/apps/mcp.test.ts +206 -0
  288. package/src/apps/mcp.ts +250 -0
  289. package/src/apps/relay/App.test.ts +457 -8
  290. package/src/apps/relay/App.ts +120 -12
  291. package/src/apps/relay/Sponsorships.test.ts +1 -1
  292. package/src/apps/relay/Sponsorships.ts +2 -1
  293. package/src/cli/Cli.test.ts +25 -2
  294. package/src/cli/Cli.ts +17 -2
  295. package/src/cli/commands/admin.test.ts +83 -0
  296. package/src/cli/commands/admin.ts +89 -1
  297. package/src/db/Db.ts +137 -0
  298. package/src/db/Schema.ts +104 -2
  299. package/src/db/tables/billingSettings.test.ts +87 -0
  300. package/src/db/tables/billingSettings.ts +108 -0
  301. package/src/db/tables/invitations.ts +2 -2
  302. package/src/db/tables/organizations.test.ts +1 -1
  303. package/src/db/tables/organizations.ts +15 -5
  304. package/src/db/tables/projects.ts +2 -2
  305. package/src/db/tables/requestUsage.ts +231 -0
  306. package/src/db/tables/sponsoredTransactions.test.ts +108 -1
  307. package/src/db/tables/sponsoredTransactions.ts +230 -2
  308. package/src/db/tables/stripeCustomers.test.ts +109 -0
  309. package/src/db/tables/stripeCustomers.ts +162 -0
  310. package/src/db/tables/users.ts +2 -2
  311. package/src/handlers/internal/sponsorship.ts +34 -7
  312. package/src/handlers/internal/utils.ts +19 -7
  313. package/src/index.ts +1 -0
  314. package/src/internal/Auth.test.ts +175 -25
  315. package/src/internal/Auth.ts +176 -87
  316. package/src/internal/Cache.ts +5 -1
  317. package/src/internal/EdgeCache.test.ts +27 -4
  318. package/src/internal/EdgeCache.ts +47 -9
  319. package/src/internal/Fees.test.ts +32 -0
  320. package/src/internal/Fees.ts +51 -0
  321. package/src/internal/FundingRoutes.test.ts +213 -0
  322. package/src/internal/FundingRoutes.ts +898 -0
  323. package/src/internal/Id.test.ts +17 -0
  324. package/src/internal/Id.ts +24 -0
  325. package/src/internal/Log.test.ts +1 -0
  326. package/src/internal/Log.ts +5 -0
  327. package/src/internal/OpenApi.ts +12 -1
  328. package/src/internal/RateLimit.test.ts +84 -0
  329. package/src/internal/RateLimit.ts +70 -0
  330. package/src/internal/Response.ts +1 -1
  331. package/src/internal/Schema.ts +8 -0
  332. package/src/internal/Webhooks.test.ts +3 -3
  333. package/src/internal/Webhooks.ts +3 -3
  334. package/dist/apps/platform/App.d.ts.map +0 -1
  335. package/dist/apps/platform/App.js.map +0 -1
  336. package/dist/apps/platform/index.d.ts +0 -2
  337. package/dist/apps/platform/index.d.ts.map +0 -1
  338. package/dist/apps/platform/index.js +0 -2
  339. package/dist/apps/platform/index.js.map +0 -1
  340. package/dist/apps/platform/routes/api-keys.d.ts.map +0 -1
  341. package/dist/apps/platform/routes/api-keys.js.map +0 -1
  342. package/dist/apps/platform/routes/invitations.d.ts.map +0 -1
  343. package/dist/apps/platform/routes/invitations.js.map +0 -1
  344. package/dist/apps/platform/routes/me.d.ts.map +0 -1
  345. package/dist/apps/platform/routes/me.js.map +0 -1
  346. package/dist/apps/platform/routes/members.d.ts.map +0 -1
  347. package/dist/apps/platform/routes/members.js.map +0 -1
  348. package/dist/apps/platform/routes/orgs.d.ts.map +0 -1
  349. package/dist/apps/platform/routes/orgs.js.map +0 -1
  350. package/dist/apps/platform/routes/projects.js.map +0 -1
  351. package/src/apps/platform/index.ts +0 -1
  352. package/src/apps/platform/routes/me.test.ts +0 -121
  353. /package/dist/apps/{platform → management}/routes/me.js +0 -0
  354. /package/dist/apps/{platform → management}/routes/members.d.ts +0 -0
  355. /package/dist/apps/{platform → management}/routes/members.js +0 -0
  356. /package/dist/apps/{platform → management}/routes/projects.js +0 -0
  357. /package/src/apps/{platform → management}/routes/me.ts +0 -0
  358. /package/src/apps/{platform → management}/routes/members.test.ts +0 -0
  359. /package/src/apps/{platform → management}/routes/members.ts +0 -0
  360. /package/src/apps/{platform → management}/routes/orgs.test.ts +0 -0
  361. /package/src/apps/{platform → management}/routes/projects.test.ts +0 -0
  362. /package/src/apps/{platform → management}/routes/projects.ts +0 -0
@@ -1,10 +1,14 @@
1
1
  import { Hono } from 'hono'
2
2
  import { createMiddleware } from 'hono/factory'
3
- import { Addresses } from 'viem/tempo'
3
+ import { RpcResponse } from 'ox'
4
+ import { Addresses, TokenId, Transaction } from 'viem/tempo'
4
5
 
5
6
  import type * as App from '../../App.js'
7
+ import * as BillingSettings from '../../db/tables/billingSettings.js'
6
8
  import * as Db from '../../db/Db.js'
9
+ import * as Fees from '../../internal/Fees.js'
7
10
  import * as SponsoredTransactions from '../../db/tables/sponsoredTransactions.js'
11
+ import * as Billing from '../management/Billing.js'
8
12
  import * as Handler from '../../Handler.js'
9
13
  import * as Auth from '../../internal/Auth.js'
10
14
  import * as OpenApi from '../../internal/OpenApi.js'
@@ -43,10 +47,13 @@ export declare namespace relay {
43
47
  /** Options for the relay route group. */
44
48
  type Options = {
45
49
  feePayer?:
46
- | Pick<
50
+ | (Pick<
47
51
  NonNullable<Handler.relay.Options['feePayer']>,
48
52
  'account' | 'feeToken' | 'name' | 'url'
49
- >
53
+ > & {
54
+ /** Per-transaction fee cap (decimal USD string); orgs tighten it, never loosen. @default '1.00' */
55
+ txFeeLimit?: string | undefined
56
+ })
50
57
  | undefined
51
58
  }
52
59
  }
@@ -56,6 +63,10 @@ export declare namespace relay {
56
63
  // than closed over group locals; relay traffic is low-QPS and the viem
57
64
  // clients are memoized upstream, so per-request handler construction is cheap.
58
65
  function middleware(options: relay.Options = {}) {
66
+ // Platform-wide per-transaction cap; org settings tighten it, never loosen.
67
+ const txFeeLimit = options.feePayer
68
+ ? Billing.toBaseUnits(options.feePayer.txFeeLimit ?? '1.00')
69
+ : 0n
59
70
  return createMiddleware<App.Environment>(async (c) => {
60
71
  const verifiedTokens = c.get('verifiedTokens')
61
72
 
@@ -76,20 +87,97 @@ function middleware(options: relay.Options = {}) {
76
87
  principal?.type === 'api_key' &&
77
88
  (principal.apiKey.scopes.includes('rpc-relay:sponsor') ||
78
89
  principal.apiKey.scopes.includes(Scope.wildcard))
90
+ // Lifted so validation and the sponsored-token default read one value.
91
+ const feeToken_sponsor = options.feePayer.feeToken ?? Addresses.pathUsd
79
92
  return {
80
93
  ...options.feePayer,
81
- feeToken: options.feePayer.feeToken ?? Addresses.pathUsd,
82
- validate: ({ chainId }) => {
94
+ feeToken: feeToken_sponsor,
95
+ validate: async (transaction) => {
83
96
  // Allowlist the chains this deployment serves rather than denylisting
84
97
  // only mainnet: the relay resolves chainId from the request body, so
85
- // the query-string chain guard never sees it. Mainnet stays closed
86
- // until billing ships; nothing meters real fee-payer spend yet.
87
- const id = Number(chainId)
88
- return sponsorable && c.get('supportedChainIds').has(id) && !Viem.isMainnet(id)
98
+ // the query-string chain guard never sees it.
99
+ const id = Number(transaction.chainId)
100
+ if (!attribution || !sponsorable || !c.get('supportedChainIds').has(id)) return false
101
+ if (!Viem.isMainnet(id)) return true
102
+ // Mainnet is production-only: a sandbox key resolves chainId from the
103
+ // request body, so the auth-layer sandbox/mainnet guard never sees
104
+ // it. Refuse here, or sandbox spend would sponsor mainnet unbilled.
105
+ if (attribution.environment !== 'production') return false
106
+
107
+ // Enforcement reads hit the primary, never the cached replica: a
108
+ // stale `past_due`, a lowered limit, or another isolate's pending
109
+ // spend must not keep the gate open.
110
+ const db = Db.get(c.get('db'))
111
+ // Mainnet sponsorship requires an active billing source; deployments
112
+ // without billing configured have no rows, so mainnet stays closed.
113
+ // `past_due` gets its own reason: the caller must settle an invoice,
114
+ // not attach a card.
115
+ const billing = await Billing.status(db, attribution.orgId)
116
+ if (billing === 'past_due') return 'billing_past_due'
117
+ if (billing !== 'active') return 'billing_required'
118
+
119
+ const feeToken = (() => {
120
+ try {
121
+ return TokenId.toAddress(transaction.feeToken ?? feeToken_sponsor)
122
+ } catch {
123
+ return undefined
124
+ }
125
+ })()
126
+ const verified = feeToken
127
+ ? (await VerifiedTokens.read(Db.get(c.get('dbCached')), id))?.byAddress.get(feeToken.toLowerCase()) // prettier-ignore
128
+ : undefined
129
+ if (
130
+ !verified ||
131
+ verified.currency.toLowerCase() !== 'usd' ||
132
+ verified.decimals !== Fees.tokenDecimals
133
+ )
134
+ // prettier-ignore
135
+ return 'fee_token_unsupported'
136
+
137
+ // Fail closed: a transaction whose signed cap cannot be computed
138
+ // cannot be bounded, so it never sponsors on mainnet.
139
+ const feeMax = Fees.maxOf(transaction)
140
+ if (feeMax === undefined) return 'tx_fee_limit_exceeded'
141
+ const settings = await BillingSettings.get(db, attribution.orgId)
142
+ const cap = (() => {
143
+ if (settings?.txFeeLimit == null) return txFeeLimit
144
+ const org = Billing.toBaseUnits(settings.txFeeLimit)
145
+ return org < txFeeLimit ? org : txFeeLimit
146
+ })()
147
+ if (feeMax > cap) return 'tx_fee_limit_exceeded'
148
+ // The period spend limit is enforced atomically at recording time
149
+ // (`onSponsored` → `SponsoredTransactions.reserve`); a check here
150
+ // would be a check-then-act race a burst could slip through.
151
+ return true
89
152
  },
90
153
  onSponsored: async (event) => {
91
154
  if (!attribution) return
92
- await SponsoredTransactions.upsert(Db.get(c.get('db')), {
155
+ const mainnet = Viem.isMainnet(event.chainId)
156
+ // Defense in depth: `validate` already refuses non-production
157
+ // mainnet, so reaching here with that pair is a wiring bug.
158
+ if (mainnet && attribution.environment !== 'production')
159
+ throw new RpcResponse.InvalidParamsError({ message: 'Sponsorship rejected.' })
160
+
161
+ // The cap recomputes from canonical envelope bytes, mirroring the
162
+ // reconciliation convention; pending rows charge it to spend limits.
163
+ const feeMax = Fees.maxOf(Transaction.deserialize(event.transaction as `0x76${string}`))
164
+ const db = Db.get(c.get('db'))
165
+
166
+ const feeToken = (() => {
167
+ try {
168
+ return event.feeToken !== undefined ? TokenId.toAddress(event.feeToken) : undefined
169
+ } catch {
170
+ return undefined
171
+ }
172
+ })()
173
+
174
+ const currency = feeToken
175
+ ? (await VerifiedTokens.read(Db.get(c.get('dbCached')), event.chainId))?.byAddress
176
+ .get(feeToken.toLowerCase())
177
+ ?.currency.toLowerCase()
178
+ : undefined
179
+
180
+ const input = {
93
181
  apiKeyId: attribution.apiKeyId,
94
182
  billable: attribution.environment === 'production',
95
183
  chainId: event.chainId,
@@ -98,9 +186,29 @@ function middleware(options: relay.Options = {}) {
98
186
  projectId: attribution.projectId,
99
187
  signPayload: event.signPayload,
100
188
  transaction: event.transaction,
101
- ...(event.feeToken ? { feeToken: event.feeToken } : {}),
189
+ ...(currency ? { currency } : {}),
190
+ ...(feeMax !== undefined ? { feeMax: feeMax.toString() } : {}),
191
+ ...(feeToken ? { feeToken } : {}),
102
192
  ...(event.transactionHash ? { transactionHash: event.transactionHash } : {}),
103
- })
193
+ }
194
+ // Re-read settings on the authoritative path: the limit may have
195
+ // changed since `validate`, and only mainnet spend is limited.
196
+ const settings = mainnet ? await BillingSettings.get(db, attribution.orgId) : undefined
197
+ if (settings?.spendLimit == null) return void (await SponsoredTransactions.upsert(db, input)) // prettier-ignore
198
+ try {
199
+ await SponsoredTransactions.reserve(db, input, {
200
+ chainIds: [Viem.chainId.mainnet],
201
+ max: Billing.toBaseUnits(settings.spendLimit),
202
+ since: Billing.periodStart(settings.period),
203
+ })
204
+ } catch (error) {
205
+ if (error instanceof SponsoredTransactions.PeriodSpendLimitError)
206
+ throw new RpcResponse.InvalidParamsError({
207
+ data: { code: 'spend_limit_exceeded' },
208
+ message: 'Spend limit exceeded.',
209
+ })
210
+ throw error
211
+ }
104
212
  },
105
213
  } satisfies Handler.relay.Options['feePayer']
106
214
  })()
@@ -49,7 +49,7 @@ describe('finalize', () => {
49
49
  const finalized = await SponsoredTransactions.get(db, record.id)
50
50
  expect(finalized?.status).toMatchInlineSnapshot(`"finalized"`)
51
51
  // 21000 gas × 3 = 63000 base units.
52
- expect(finalized?.feeAmount).toMatchInlineSnapshot(`"63000"`)
52
+ expect(finalized?.feeAmount).toMatchInlineSnapshot(`"1"`)
53
53
  expect(finalized?.finalizedAt).not.toBeNull()
54
54
  await db.close()
55
55
  })
@@ -10,6 +10,7 @@ import {
10
10
  import { getTransactionReceipt } from 'viem/actions'
11
11
 
12
12
  import * as Db from '../../db/Db.js'
13
+ import * as Fees from '../../internal/Fees.js'
13
14
  import * as SponsoredTransactions from '../../db/tables/sponsoredTransactions.js'
14
15
  import * as Tidx from '../../internal/Tidx.js'
15
16
  import * as Viem from '../../internal/Viem.js'
@@ -67,7 +68,7 @@ export async function finalize(db: Db.Db, options: finalize.Options): Promise<fi
67
68
 
68
69
  if (receipt) {
69
70
  // A reverted transaction still finalizes: the fee payer paid its fees.
70
- const feeAmount = receipt.gasUsed * receipt.effectiveGasPrice
71
+ const feeAmount = Fees.fromGas(receipt.gasUsed, receipt.effectiveGasPrice)
71
72
  await SponsoredTransactions.finalize(db, row.id, {
72
73
  feeAmount: feeAmount.toString(),
73
74
  finalizedAt: now().toISOString(),
@@ -1,3 +1,4 @@
1
+ import { Cli as incur_Cli, z } from 'incur'
1
2
  import { Cli } from 'tapimo'
2
3
 
3
4
  import * as TestApp from '../../test/App.js'
@@ -10,7 +11,7 @@ describe('from', () => {
10
11
  const fetch = mockHostedApi()
11
12
 
12
13
  try {
13
- const cli = Cli.from(apiUrl, { version: '1.2.3' })
14
+ const cli = Cli.from(apiUrl, { docs: false, version: '1.2.3' })
14
15
  await serve(cli, ['--help'])
15
16
 
16
17
  expect(cli.name).toMatchInlineSnapshot(`"tapimo"`)
@@ -28,7 +29,7 @@ describe('from', () => {
28
29
  const fetch = mockHostedApi(TestApp.create({ db }))
29
30
 
30
31
  try {
31
- const cli = Cli.from(apiUrl)
32
+ const cli = Cli.from(apiUrl, { docs: false })
32
33
  // `/v1/tokens?verified=true` now serves the curated verified list through
33
34
  // the standard listing endpoint, so the CLI surfaces the filter as a
34
35
  // `--verified` flag on the `v1 tokens get` command instead of routing
@@ -52,6 +53,28 @@ describe('from', () => {
52
53
  fetch.mockRestore()
53
54
  }
54
55
  })
56
+
57
+ test('mounts the docs MCP source as a command group', async () => {
58
+ const fetch = mockHostedApi()
59
+
60
+ // Stub remote docs MCP server: an in-process incur CLI serving `/mcp`.
61
+ const remote = incur_Cli.create('docs-remote', { version: '1.0.0' }).command('search', {
62
+ description: 'Search docs',
63
+ options: z.object({ query: z.string().describe('Search query') }),
64
+ run: (context) => ({ hits: [context.options.query] }),
65
+ })
66
+
67
+ try {
68
+ const cli = Cli.from(apiUrl, {
69
+ docs: { fetch: (request) => remote.fetch(request), url: 'http://docs.local/mcp' },
70
+ })
71
+ const output = await serve(cli, ['docs', 'search', '--query', 'fees', '--format', 'json'])
72
+
73
+ expect(JSON.parse(output)).toEqual({ hits: ['fees'] })
74
+ } finally {
75
+ fetch.mockRestore()
76
+ }
77
+ })
55
78
  })
56
79
 
57
80
  function mockHostedApi(app = TestApp.create()) {
package/src/cli/Cli.ts CHANGED
@@ -1,29 +1,42 @@
1
1
  import { Cli as incur_Cli, Fetch, type Openapi } from 'incur'
2
2
 
3
+ import type { mcp } from '../apps/mcp.js'
3
4
  import * as Admin from './commands/admin.js'
4
5
 
6
+ /** Tool patterns hidden from MCP clients: super admin, partner-shaped gecko mirrors, browser session flows. */
7
+ const excludedTools = ['admin_*', 'gecko_*', 'v1_auth_*']
8
+
9
+ /** Usage guidance surfaced to MCP clients on initialize (`<cli> --mcp`). */
10
+ const instructions =
11
+ 'Tools mirror the hosted Tempo API. Authenticated tools accept an `authorization` input (`Bearer <api key>`). List endpoints paginate via `cursor`/`nextCursor`. `docs_*` tools search the Tempo documentation.'
12
+
5
13
  /**
6
14
  * Creates an incur CLI backed by a hosted Tempo API, plus the super admin
7
- * commands (`<cli> admin …`).
15
+ * commands (`<cli> admin …`) and the remote Tempo docs tools (`<cli> docs …`).
8
16
  */
9
17
  export function from(url: string | URL, options: from.Options = {}) {
10
18
  const {
11
19
  description = 'Tempo API CLI',
20
+ docs = 'https://mcp.tempo.xyz/mcp',
12
21
  name = 'tapimo',
13
22
  openapi = 'openapi.json',
14
23
  request,
15
24
  version = '0.0.0',
16
25
  } = options
17
26
 
18
- return incur_Cli
27
+ const cli = incur_Cli
19
28
  .create(name, {
20
29
  description,
21
30
  fetch: Fetch.fromRequest(url, request),
31
+ mcp: { instructions, tools: { exclude: excludedTools } },
22
32
  openapi,
23
33
  openapiConfig: options.openapiConfig ?? { mode: 'namespace' },
24
34
  version,
25
35
  })
26
36
  .command(Admin.admin)
37
+ if (docs !== false)
38
+ cli.command('docs', { description: 'Search the Tempo documentation.', mcp: docs })
39
+ return cli
27
40
  }
28
41
 
29
42
  export declare namespace from {
@@ -31,6 +44,8 @@ export declare namespace from {
31
44
  type Options = {
32
45
  /** CLI description. */
33
46
  description?: string | undefined
47
+ /** Remote docs MCP source mounted as the `docs` command group, or `false` to omit it. @default 'https://mcp.tempo.xyz/mcp' */
48
+ docs?: mcp.Source | false | undefined
34
49
  /** CLI name. */
35
50
  name?: string | undefined
36
51
  /** OpenAPI document source. Relative paths resolve from the API URL. */
@@ -1,6 +1,9 @@
1
1
  import { nanoid } from 'nanoid'
2
2
 
3
3
  import * as TestCli from '../../../test/cli.js'
4
+ import * as Analytics from '../../analytics/Analytics.js'
5
+
6
+ afterEach(() => vi.unstubAllGlobals())
4
7
 
5
8
  describe('admin', () => {
6
9
  describe('migrate', () => {
@@ -67,5 +70,85 @@ describe('admin', () => {
67
70
  expect(code).toBe(1)
68
71
  expect(envelope.error.code).toBe('migrate_failed')
69
72
  })
73
+
74
+ test('behavior: applies ClickHouse analytics migrations when configured', async () => {
75
+ const fetch = vi
76
+ .fn<typeof globalThis.fetch>()
77
+ .mockResolvedValue(new Response('', { status: 200 }))
78
+ vi.stubGlobal('fetch', fetch)
79
+
80
+ const { code, envelope } = await TestCli.run(
81
+ ['admin', 'migrate', '--db-schema', `t_${nanoid()}`],
82
+ {
83
+ CLICKHOUSE_DATABASE: 'tempo_api',
84
+ CLICKHOUSE_MIGRATE_PASSWORD: 'ddl-secret',
85
+ CLICKHOUSE_MIGRATE_USER: 'ddl',
86
+ CLICKHOUSE_URL: 'https://clickhouse.test',
87
+ },
88
+ )
89
+ expect(code).toBe(0)
90
+ expect(envelope).toEqual({ data: { analytics: true, migrated: true }, ok: true })
91
+ const url = new URL(String(fetch.mock.calls[0]![0]))
92
+ expect(url.searchParams.get('database')).toBe('tempo_api')
93
+ const bodies = fetch.mock.calls.map((call) => String(call[1]?.body))
94
+ for (const migration of Analytics.migrations) expect(bodies).toContain(migration.sql)
95
+ })
96
+
97
+ test('behavior: skips analytics migrations when ClickHouse env is unset or empty', async () => {
98
+ const fetch = vi.fn<typeof globalThis.fetch>()
99
+ vi.stubGlobal('fetch', fetch)
100
+
101
+ const { code, envelope } = await TestCli.run(
102
+ ['admin', 'migrate', '--db-schema', `t_${nanoid()}`],
103
+ {
104
+ CLICKHOUSE_DATABASE: '',
105
+ CLICKHOUSE_MIGRATE_PASSWORD: '',
106
+ CLICKHOUSE_MIGRATE_USER: '',
107
+ CLICKHOUSE_URL: '',
108
+ },
109
+ )
110
+ expect(code).toBe(0)
111
+ expect(envelope).toEqual({ data: { migrated: true }, ok: true })
112
+ expect(fetch).not.toHaveBeenCalled()
113
+ })
114
+
115
+ test('error: partial ClickHouse config exits 1', async () => {
116
+ const { code, envelope } = await TestCli.run(['admin', 'migrate'], {
117
+ CLICKHOUSE_MIGRATE_PASSWORD: 'ddl-secret',
118
+ })
119
+ expect(code).toBe(1)
120
+ expect(envelope).toEqual({
121
+ error: {
122
+ code: 'missing_clickhouse_config',
123
+ message:
124
+ 'ClickHouse analytics migration is partially configured; set CLICKHOUSE_DATABASE, CLICKHOUSE_MIGRATE_USER, CLICKHOUSE_URL.',
125
+ },
126
+ ok: false,
127
+ })
128
+ })
129
+
130
+ test('error: a ClickHouse migration failure exits 1', async () => {
131
+ vi.stubGlobal(
132
+ 'fetch',
133
+ vi.fn<typeof globalThis.fetch>().mockResolvedValue(new Response('denied', { status: 403 })),
134
+ )
135
+
136
+ const { code, envelope } = await TestCli.run([
137
+ 'admin',
138
+ 'migrate',
139
+ '--clickhouse-database',
140
+ 'tempo_api',
141
+ '--clickhouse-migrate-password',
142
+ 'ddl-secret',
143
+ '--clickhouse-migrate-user',
144
+ 'ddl',
145
+ '--clickhouse-url',
146
+ 'https://clickhouse.test',
147
+ '--db-schema',
148
+ `t_${nanoid()}`,
149
+ ])
150
+ expect(code).toBe(1)
151
+ expect(envelope.error.code).toBe('migrate_failed')
152
+ })
70
153
  })
71
154
  })
@@ -1,21 +1,53 @@
1
1
  import { Cli as incur_Cli, z } from 'incur'
2
2
 
3
+ import * as Analytics from '../../analytics/Analytics.js'
3
4
  import * as Db from '../../db/Db.js'
4
5
 
5
6
  /**
6
7
  * Applies pending Postgres migrations, connecting directly via
7
8
  * `--database-url` or `DATABASE_URL` (never through Hyperdrive — the migrate
8
9
  * advisory lock needs session semantics); `--db-schema` scopes the session.
10
+ * When ClickHouse migrate credentials are configured, also applies analytics
11
+ * schema migrations ({@link Analytics.migrations}) over the HTTP interface.
9
12
  */
10
13
  const migrate = incur_Cli.create('migrate', {
11
14
  description: 'Apply pending database migrations.',
12
15
  env: z.object({
16
+ CLICKHOUSE_DATABASE: z
17
+ .string()
18
+ .optional()
19
+ .describe('ClickHouse database for analytics migrations.'),
20
+ CLICKHOUSE_MIGRATE_PASSWORD: z
21
+ .string()
22
+ .optional()
23
+ .describe('Password for the DDL-capable ClickHouse user.'),
24
+ CLICKHOUSE_MIGRATE_USER: z
25
+ .string()
26
+ .optional()
27
+ .describe('DDL-capable ClickHouse user for analytics migrations.'),
28
+ CLICKHOUSE_URL: z.string().optional().describe('ClickHouse HTTPS endpoint.'),
13
29
  DATABASE_URL: z
14
30
  .string()
15
31
  .optional()
16
32
  .describe('Postgres connection string. Used when --database-url is omitted.'),
17
33
  }),
18
34
  options: z.object({
35
+ clickhouseDatabase: z
36
+ .string()
37
+ .optional()
38
+ .describe('ClickHouse database. Overrides CLICKHOUSE_DATABASE.'),
39
+ clickhouseMigratePassword: z
40
+ .string()
41
+ .optional()
42
+ .describe('ClickHouse DDL password. Overrides CLICKHOUSE_MIGRATE_PASSWORD.'),
43
+ clickhouseMigrateUser: z
44
+ .string()
45
+ .optional()
46
+ .describe('ClickHouse DDL user. Overrides CLICKHOUSE_MIGRATE_USER.'),
47
+ clickhouseUrl: z
48
+ .string()
49
+ .optional()
50
+ .describe('ClickHouse HTTPS endpoint. Overrides CLICKHOUSE_URL.'),
19
51
  databaseUrl: z
20
52
  .string()
21
53
  .optional()
@@ -29,13 +61,25 @@ const migrate = incur_Cli.create('migrate', {
29
61
  code: 'missing_database_url',
30
62
  message: 'Set --database-url or DATABASE_URL to the Postgres connection string.',
31
63
  })
64
+
65
+ const analytics = clickhouseConfig({
66
+ database: c.options.clickhouseDatabase ?? c.env.CLICKHOUSE_DATABASE,
67
+ password: c.options.clickhouseMigratePassword ?? c.env.CLICKHOUSE_MIGRATE_PASSWORD,
68
+ url: c.options.clickhouseUrl ?? c.env.CLICKHOUSE_URL,
69
+ user: c.options.clickhouseMigrateUser ?? c.env.CLICKHOUSE_MIGRATE_USER,
70
+ })
71
+ if (analytics?.missing)
72
+ return c.error({
73
+ code: 'missing_clickhouse_config',
74
+ message: `ClickHouse analytics migration is partially configured; set ${analytics.missing.join(', ')}.`,
75
+ })
76
+
32
77
  const db = Db.postgres({
33
78
  connectionString,
34
79
  ...(c.options.dbSchema ? { schema: c.options.dbSchema } : {}),
35
80
  })
36
81
  try {
37
82
  await db.migrate()
38
- return { migrated: true }
39
83
  } catch (error) {
40
84
  /* v8 ignore next */
41
85
  const message = error instanceof Error ? error.message : 'Migration failed.'
@@ -46,6 +90,19 @@ const migrate = incur_Cli.create('migrate', {
46
90
  } finally {
47
91
  await db.close()
48
92
  }
93
+ if (!analytics) return { migrated: true }
94
+
95
+ try {
96
+ await Analytics.clickhouse(analytics.options).migrate()
97
+ } catch (error) {
98
+ /* v8 ignore next */
99
+ const message = error instanceof Error ? error.message : 'Migration failed.'
100
+ return c.error({
101
+ code: 'migrate_failed',
102
+ message,
103
+ })
104
+ }
105
+ return { analytics: true, migrated: true }
49
106
  },
50
107
  })
51
108
 
@@ -58,3 +115,34 @@ export const admin = incur_Cli
58
115
  description: 'Super admin commands for a Tempo API deployment.',
59
116
  })
60
117
  .command(migrate)
118
+
119
+ /**
120
+ * Resolves ClickHouse migrate config. Credentials gate the migration (absent
121
+ * → skip); empty values count as unset since CI renders missing secrets as
122
+ * empty strings. Partial config returns the missing variable names instead.
123
+ */
124
+ function clickhouseConfig(input: {
125
+ database?: string | undefined
126
+ password?: string | undefined
127
+ url?: string | undefined
128
+ user?: string | undefined
129
+ }):
130
+ | { missing: readonly string[]; options?: undefined }
131
+ | { missing?: undefined; options: Analytics.clickhouse.Options }
132
+ | undefined {
133
+ const database = input.database || undefined
134
+ const password = input.password || undefined
135
+ const url = input.url || undefined
136
+ const user = input.user || undefined
137
+ if (password === undefined && user === undefined) return undefined
138
+ if (database === undefined || password === undefined || url === undefined || user === undefined)
139
+ return {
140
+ missing: [
141
+ ...(database === undefined ? ['CLICKHOUSE_DATABASE'] : []),
142
+ ...(password === undefined ? ['CLICKHOUSE_MIGRATE_PASSWORD'] : []),
143
+ ...(user === undefined ? ['CLICKHOUSE_MIGRATE_USER'] : []),
144
+ ...(url === undefined ? ['CLICKHOUSE_URL'] : []),
145
+ ],
146
+ }
147
+ return { options: { database, password, url, user } }
148
+ }