tapimo 0.1.0 → 0.3.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 (520) hide show
  1. package/README.md +57 -16
  2. package/dist/ApiKey.d.ts +4 -1
  3. package/dist/ApiKey.d.ts.map +1 -1
  4. package/dist/ApiKey.js +6 -2
  5. package/dist/ApiKey.js.map +1 -1
  6. package/dist/ApiKeys.d.ts +98 -3
  7. package/dist/ApiKeys.d.ts.map +1 -1
  8. package/dist/ApiKeys.js +150 -1
  9. package/dist/ApiKeys.js.map +1 -1
  10. package/dist/App.d.ts +7917 -2315
  11. package/dist/App.d.ts.map +1 -1
  12. package/dist/App.js +141 -329
  13. package/dist/App.js.map +1 -1
  14. package/dist/Scope.d.ts +20 -4
  15. package/dist/Scope.d.ts.map +1 -1
  16. package/dist/Scope.js +23 -4
  17. package/dist/Scope.js.map +1 -1
  18. package/dist/Webhooks.d.ts +2 -2
  19. package/dist/Webhooks.d.ts.map +1 -1
  20. package/dist/Webhooks.js +2 -2
  21. package/dist/Webhooks.js.map +1 -1
  22. package/dist/admin/App.d.ts +203 -120
  23. package/dist/admin/App.d.ts.map +1 -1
  24. package/dist/admin/apps/api-keys.d.ts +111 -6
  25. package/dist/admin/apps/api-keys.d.ts.map +1 -1
  26. package/dist/admin/apps/api-keys.js +67 -0
  27. package/dist/admin/apps/api-keys.js.map +1 -1
  28. package/dist/admin/apps/scopes.d.ts +12 -1
  29. package/dist/admin/apps/scopes.d.ts.map +1 -1
  30. package/dist/admin/apps/scopes.js +3 -0
  31. package/dist/admin/apps/scopes.js.map +1 -1
  32. package/dist/admin/apps/verified-tokens.d.ts +116 -116
  33. package/dist/admin/apps/verified-tokens.js +1 -1
  34. package/dist/admin/apps/verified-tokens.js.map +1 -1
  35. package/dist/admin/ui.generated.d.ts +1 -1
  36. package/dist/admin/ui.generated.js +13 -13
  37. package/dist/admin/ui.generated.js.map +1 -1
  38. package/dist/analytics/Analytics.d.ts +151 -0
  39. package/dist/analytics/Analytics.d.ts.map +1 -0
  40. package/dist/analytics/Analytics.js +212 -0
  41. package/dist/analytics/Analytics.js.map +1 -0
  42. package/dist/analytics/tables/requestEvents.d.ts +215 -0
  43. package/dist/analytics/tables/requestEvents.d.ts.map +1 -0
  44. package/dist/analytics/tables/requestEvents.js +264 -0
  45. package/dist/analytics/tables/requestEvents.js.map +1 -0
  46. package/dist/apps/data/App.d.ts +19576 -0
  47. package/dist/apps/data/App.d.ts.map +1 -0
  48. package/dist/apps/data/App.js +272 -0
  49. package/dist/apps/data/App.js.map +1 -0
  50. package/dist/apps/data/index.d.ts +3 -0
  51. package/dist/apps/data/index.d.ts.map +1 -0
  52. package/dist/apps/data/index.js +3 -0
  53. package/dist/apps/data/index.js.map +1 -0
  54. package/dist/apps/{activities.d.ts → data/routes/activities.d.ts} +32 -32
  55. package/dist/apps/{activities.d.ts.map → data/routes/activities.d.ts.map} +1 -1
  56. package/dist/apps/{activities.js → data/routes/activities.js} +13 -13
  57. package/dist/apps/data/routes/activities.js.map +1 -0
  58. package/dist/apps/{addresses.d.ts → data/routes/addresses.d.ts} +22 -22
  59. package/dist/apps/{addresses.d.ts.map → data/routes/addresses.d.ts.map} +1 -1
  60. package/dist/apps/{addresses.js → data/routes/addresses.js} +5 -5
  61. package/dist/apps/data/routes/addresses.js.map +1 -0
  62. package/dist/apps/{balances.d.ts → data/routes/balances.d.ts} +4 -4
  63. package/dist/apps/data/routes/balances.d.ts.map +1 -0
  64. package/dist/apps/{balances.js → data/routes/balances.js} +42 -11
  65. package/dist/apps/data/routes/balances.js.map +1 -0
  66. package/dist/apps/{blocks.d.ts → data/routes/blocks.d.ts} +47 -47
  67. package/dist/apps/data/routes/blocks.d.ts.map +1 -0
  68. package/dist/apps/{blocks.js → data/routes/blocks.js} +13 -13
  69. package/dist/apps/data/routes/blocks.js.map +1 -0
  70. package/dist/apps/{coingecko.d.ts → data/routes/coingecko.d.ts} +8 -8
  71. package/dist/apps/data/routes/coingecko.d.ts.map +1 -0
  72. package/dist/apps/{coingecko.js → data/routes/coingecko.js} +13 -13
  73. package/dist/apps/data/routes/coingecko.js.map +1 -0
  74. package/dist/apps/{exchanges.d.ts → data/routes/exchanges.d.ts} +108 -108
  75. package/dist/apps/data/routes/exchanges.d.ts.map +1 -0
  76. package/dist/apps/{exchanges.js → data/routes/exchanges.js} +19 -19
  77. package/dist/apps/data/routes/exchanges.js.map +1 -0
  78. package/dist/apps/{fee-amm.d.ts → data/routes/fee-amm.d.ts} +39 -39
  79. package/dist/apps/data/routes/fee-amm.d.ts.map +1 -0
  80. package/dist/apps/{fee-amm.js → data/routes/fee-amm.js} +17 -17
  81. package/dist/apps/data/routes/fee-amm.js.map +1 -0
  82. package/dist/apps/data/routes/funding.d.ts +765 -0
  83. package/dist/apps/data/routes/funding.d.ts.map +1 -0
  84. package/dist/apps/data/routes/funding.js +383 -0
  85. package/dist/apps/data/routes/funding.js.map +1 -0
  86. package/dist/apps/{indexer.d.ts → data/routes/indexer.d.ts} +4 -4
  87. package/dist/apps/data/routes/indexer.d.ts.map +1 -0
  88. package/dist/apps/{indexer.js → data/routes/indexer.js} +13 -9
  89. package/dist/apps/data/routes/indexer.js.map +1 -0
  90. package/dist/apps/{receipts.d.ts → data/routes/receipts.d.ts} +49 -49
  91. package/dist/apps/data/routes/receipts.d.ts.map +1 -0
  92. package/dist/apps/{receipts.js → data/routes/receipts.js} +14 -20
  93. package/dist/apps/data/routes/receipts.js.map +1 -0
  94. package/dist/apps/{rpc.d.ts → data/routes/rpc.d.ts} +33 -40
  95. package/dist/apps/data/routes/rpc.d.ts.map +1 -0
  96. package/dist/apps/{rpc.js → data/routes/rpc.js} +35 -7
  97. package/dist/apps/data/routes/rpc.js.map +1 -0
  98. package/dist/apps/{tokenlist.d.ts → data/routes/tokenlist.d.ts} +40 -40
  99. package/dist/apps/data/routes/tokenlist.d.ts.map +1 -0
  100. package/dist/apps/{tokenlist.js → data/routes/tokenlist.js} +8 -8
  101. package/dist/apps/data/routes/tokenlist.js.map +1 -0
  102. package/dist/apps/{tokens.d.ts → data/routes/tokens.d.ts} +468 -468
  103. package/dist/apps/data/routes/tokens.d.ts.map +1 -0
  104. package/dist/apps/{tokens.js → data/routes/tokens.js} +19 -19
  105. package/dist/apps/data/routes/tokens.js.map +1 -0
  106. package/dist/apps/{transactions.d.ts → data/routes/transactions.d.ts} +304 -304
  107. package/dist/apps/data/routes/transactions.d.ts.map +1 -0
  108. package/dist/apps/{transactions.js → data/routes/transactions.js} +13 -13
  109. package/dist/apps/data/routes/transactions.js.map +1 -0
  110. package/dist/apps/{transfers.d.ts → data/routes/transfers.d.ts} +62 -62
  111. package/dist/apps/data/routes/transfers.d.ts.map +1 -0
  112. package/dist/apps/{transfers.js → data/routes/transfers.js} +13 -13
  113. package/dist/apps/data/routes/transfers.js.map +1 -0
  114. package/dist/apps/{verified-tokens.d.ts → data/routes/verified-tokens.d.ts} +6 -6
  115. package/dist/apps/data/routes/verified-tokens.d.ts.map +1 -0
  116. package/dist/apps/{verified-tokens.js → data/routes/verified-tokens.js} +9 -9
  117. package/dist/apps/data/routes/verified-tokens.js.map +1 -0
  118. package/dist/apps/{webhooks.d.ts → data/routes/webhooks.d.ts} +17 -17
  119. package/dist/apps/{webhooks.d.ts.map → data/routes/webhooks.d.ts.map} +1 -1
  120. package/dist/apps/{webhooks.js → data/routes/webhooks.js} +38 -34
  121. package/dist/apps/data/routes/webhooks.js.map +1 -0
  122. package/dist/apps/docs/App.d.ts +29 -0
  123. package/dist/apps/docs/App.d.ts.map +1 -0
  124. package/dist/apps/docs/App.js +54 -0
  125. package/dist/apps/docs/App.js.map +1 -0
  126. package/dist/{internal/OpenApi.fonts.d.ts → apps/docs/fonts.d.ts} +1 -1
  127. package/dist/apps/docs/fonts.d.ts.map +1 -0
  128. package/dist/{internal/OpenApi.fonts.js → apps/docs/fonts.js} +1 -1
  129. package/dist/apps/docs/fonts.js.map +1 -0
  130. package/dist/apps/docs/index.d.ts +2 -0
  131. package/dist/apps/docs/index.d.ts.map +1 -0
  132. package/dist/apps/docs/index.js +2 -0
  133. package/dist/apps/docs/index.js.map +1 -0
  134. package/dist/{internal/OpenApi.logo.d.ts → apps/docs/logo.d.ts} +1 -1
  135. package/dist/apps/docs/logo.d.ts.map +1 -0
  136. package/dist/{internal/OpenApi.logo.js → apps/docs/logo.js} +1 -1
  137. package/dist/apps/docs/logo.js.map +1 -0
  138. package/dist/apps/docs/reference.d.ts +21 -0
  139. package/dist/apps/docs/reference.d.ts.map +1 -0
  140. package/dist/apps/docs/reference.js +112 -0
  141. package/dist/apps/docs/reference.js.map +1 -0
  142. package/dist/apps/index.d.ts +5 -0
  143. package/dist/apps/index.d.ts.map +1 -0
  144. package/dist/apps/index.js +5 -0
  145. package/dist/apps/index.js.map +1 -0
  146. package/dist/apps/mcp.d.ts +76 -0
  147. package/dist/apps/mcp.d.ts.map +1 -0
  148. package/dist/apps/mcp.js +188 -0
  149. package/dist/apps/mcp.js.map +1 -0
  150. package/dist/apps/metadata.d.ts +7 -0
  151. package/dist/apps/metadata.d.ts.map +1 -0
  152. package/dist/apps/metadata.js +15 -0
  153. package/dist/apps/metadata.js.map +1 -0
  154. package/dist/apps/platform/App.d.ts +5262 -0
  155. package/dist/apps/platform/App.d.ts.map +1 -0
  156. package/dist/apps/platform/App.js +84 -0
  157. package/dist/apps/platform/App.js.map +1 -0
  158. package/dist/apps/platform/Billing.d.ts +279 -0
  159. package/dist/apps/platform/Billing.d.ts.map +1 -0
  160. package/dist/apps/platform/Billing.js +477 -0
  161. package/dist/apps/platform/Billing.js.map +1 -0
  162. package/dist/apps/platform/index.d.ts +3 -0
  163. package/dist/apps/platform/index.d.ts.map +1 -0
  164. package/dist/apps/platform/index.js +3 -0
  165. package/dist/apps/platform/index.js.map +1 -0
  166. package/dist/apps/platform/routes/api-keys.d.ts +973 -0
  167. package/dist/apps/platform/routes/api-keys.d.ts.map +1 -0
  168. package/dist/apps/platform/routes/api-keys.js +430 -0
  169. package/dist/apps/platform/routes/api-keys.js.map +1 -0
  170. package/dist/apps/platform/routes/billing.d.ts +1520 -0
  171. package/dist/apps/platform/routes/billing.d.ts.map +1 -0
  172. package/dist/apps/platform/routes/billing.js +795 -0
  173. package/dist/apps/platform/routes/billing.js.map +1 -0
  174. package/dist/apps/platform/routes/invitations.d.ts +851 -0
  175. package/dist/apps/platform/routes/invitations.d.ts.map +1 -0
  176. package/dist/apps/platform/routes/invitations.js +471 -0
  177. package/dist/apps/platform/routes/invitations.js.map +1 -0
  178. package/dist/apps/platform/routes/me.d.ts +95 -0
  179. package/dist/apps/platform/routes/me.d.ts.map +1 -0
  180. package/dist/apps/platform/routes/me.js +81 -0
  181. package/dist/apps/platform/routes/me.js.map +1 -0
  182. package/dist/apps/platform/routes/members.d.ts +554 -0
  183. package/dist/apps/platform/routes/members.d.ts.map +1 -0
  184. package/dist/apps/platform/routes/members.js +251 -0
  185. package/dist/apps/platform/routes/members.js.map +1 -0
  186. package/dist/apps/platform/routes/orgs.d.ts +596 -0
  187. package/dist/apps/platform/routes/orgs.d.ts.map +1 -0
  188. package/dist/apps/platform/routes/orgs.js +276 -0
  189. package/dist/apps/platform/routes/orgs.js.map +1 -0
  190. package/dist/apps/platform/routes/projects.d.ts +740 -0
  191. package/dist/apps/platform/routes/projects.d.ts.map +1 -0
  192. package/dist/apps/platform/routes/projects.js +286 -0
  193. package/dist/apps/platform/routes/projects.js.map +1 -0
  194. package/dist/apps/platform/routes/scopes.d.ts +107 -0
  195. package/dist/apps/platform/routes/scopes.d.ts.map +1 -0
  196. package/dist/apps/platform/routes/scopes.js +66 -0
  197. package/dist/apps/platform/routes/scopes.js.map +1 -0
  198. package/dist/apps/platform/routes/usage.d.ts +588 -0
  199. package/dist/apps/platform/routes/usage.d.ts.map +1 -0
  200. package/dist/apps/platform/routes/usage.js +391 -0
  201. package/dist/apps/platform/routes/usage.js.map +1 -0
  202. package/dist/apps/relay/App.d.ts +44 -0
  203. package/dist/apps/relay/App.d.ts.map +1 -0
  204. package/dist/apps/relay/App.js +211 -0
  205. package/dist/apps/relay/App.js.map +1 -0
  206. package/dist/apps/relay/Sponsorships.d.ts +107 -0
  207. package/dist/apps/relay/Sponsorships.d.ts.map +1 -0
  208. package/dist/apps/relay/Sponsorships.js +194 -0
  209. package/dist/apps/relay/Sponsorships.js.map +1 -0
  210. package/dist/apps/relay/index.d.ts +3 -0
  211. package/dist/apps/relay/index.d.ts.map +1 -0
  212. package/dist/apps/relay/index.js +3 -0
  213. package/dist/apps/relay/index.js.map +1 -0
  214. package/dist/cli/Cli.d.ts +9 -2
  215. package/dist/cli/Cli.d.ts.map +1 -1
  216. package/dist/cli/Cli.js +12 -4
  217. package/dist/cli/Cli.js.map +1 -1
  218. package/dist/cli/commands/admin.d.ts +5 -1
  219. package/dist/cli/commands/admin.d.ts.map +1 -1
  220. package/dist/cli/commands/admin.js +82 -3
  221. package/dist/cli/commands/admin.js.map +1 -1
  222. package/dist/db/Db.d.ts +17 -0
  223. package/dist/db/Db.d.ts.map +1 -1
  224. package/dist/db/Db.js +254 -0
  225. package/dist/db/Db.js.map +1 -1
  226. package/dist/db/Schema.d.ts +159 -0
  227. package/dist/db/Schema.d.ts.map +1 -1
  228. package/dist/db/Schema.js +187 -2
  229. package/dist/db/Schema.js.map +1 -1
  230. package/dist/db/tables/billingSettings.d.ts +55 -0
  231. package/dist/db/tables/billingSettings.d.ts.map +1 -0
  232. package/dist/db/tables/billingSettings.js +70 -0
  233. package/dist/db/tables/billingSettings.js.map +1 -0
  234. package/dist/db/tables/invitations.d.ts +84 -0
  235. package/dist/db/tables/invitations.d.ts.map +1 -0
  236. package/dist/db/tables/invitations.js +165 -0
  237. package/dist/db/tables/invitations.js.map +1 -0
  238. package/dist/db/tables/memberships.d.ts +108 -0
  239. package/dist/db/tables/memberships.d.ts.map +1 -0
  240. package/dist/db/tables/memberships.js +153 -0
  241. package/dist/db/tables/memberships.js.map +1 -0
  242. package/dist/db/tables/organizations.d.ts +48 -0
  243. package/dist/db/tables/organizations.d.ts.map +1 -1
  244. package/dist/db/tables/organizations.js +97 -2
  245. package/dist/db/tables/organizations.js.map +1 -1
  246. package/dist/db/tables/projects.d.ts +65 -0
  247. package/dist/db/tables/projects.d.ts.map +1 -0
  248. package/dist/db/tables/projects.js +75 -0
  249. package/dist/db/tables/projects.js.map +1 -0
  250. package/dist/db/tables/requestUsage.d.ts +121 -0
  251. package/dist/db/tables/requestUsage.d.ts.map +1 -0
  252. package/dist/db/tables/requestUsage.js +150 -0
  253. package/dist/db/tables/requestUsage.js.map +1 -0
  254. package/dist/db/tables/sponsoredTransactions.d.ts +239 -0
  255. package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -0
  256. package/dist/db/tables/sponsoredTransactions.js +314 -0
  257. package/dist/db/tables/sponsoredTransactions.js.map +1 -0
  258. package/dist/db/tables/stripeCustomers.d.ts +81 -0
  259. package/dist/db/tables/stripeCustomers.d.ts.map +1 -0
  260. package/dist/db/tables/stripeCustomers.js +120 -0
  261. package/dist/db/tables/stripeCustomers.js.map +1 -0
  262. package/dist/db/tables/users.d.ts +48 -0
  263. package/dist/db/tables/users.d.ts.map +1 -0
  264. package/dist/db/tables/users.js +65 -0
  265. package/dist/db/tables/users.js.map +1 -0
  266. package/dist/handlers/internal/executionError.d.ts.map +1 -1
  267. package/dist/handlers/internal/executionError.js +2 -0
  268. package/dist/handlers/internal/executionError.js.map +1 -1
  269. package/dist/handlers/internal/multisig.d.ts +2 -6
  270. package/dist/handlers/internal/multisig.d.ts.map +1 -1
  271. package/dist/handlers/internal/multisig.js +11 -36
  272. package/dist/handlers/internal/multisig.js.map +1 -1
  273. package/dist/handlers/internal/sponsorship.d.ts +35 -2
  274. package/dist/handlers/internal/sponsorship.d.ts.map +1 -1
  275. package/dist/handlers/internal/sponsorship.js +53 -14
  276. package/dist/handlers/internal/sponsorship.js.map +1 -1
  277. package/dist/handlers/internal/utils.d.ts +10 -3
  278. package/dist/handlers/internal/utils.d.ts.map +1 -1
  279. package/dist/handlers/internal/utils.js +14 -6
  280. package/dist/handlers/internal/utils.js.map +1 -1
  281. package/dist/handlers/relay.d.ts +6 -2
  282. package/dist/handlers/relay.d.ts.map +1 -1
  283. package/dist/handlers/relay.js +16 -2
  284. package/dist/handlers/relay.js.map +1 -1
  285. package/dist/index.d.ts +1 -0
  286. package/dist/index.d.ts.map +1 -1
  287. package/dist/index.js +1 -0
  288. package/dist/index.js.map +1 -1
  289. package/dist/internal/Auth.d.ts +222 -6
  290. package/dist/internal/Auth.d.ts.map +1 -1
  291. package/dist/internal/Auth.js +625 -74
  292. package/dist/internal/Auth.js.map +1 -1
  293. package/dist/internal/Cache.d.ts.map +1 -1
  294. package/dist/internal/Cache.js +4 -1
  295. package/dist/internal/Cache.js.map +1 -1
  296. package/dist/internal/EdgeCache.d.ts +10 -0
  297. package/dist/internal/EdgeCache.d.ts.map +1 -1
  298. package/dist/internal/EdgeCache.js +39 -8
  299. package/dist/internal/EdgeCache.js.map +1 -1
  300. package/dist/internal/Fees.d.ts +27 -0
  301. package/dist/internal/Fees.d.ts.map +1 -0
  302. package/dist/internal/Fees.js +48 -0
  303. package/dist/internal/Fees.js.map +1 -0
  304. package/dist/internal/FundingRoutes.d.ts +183 -0
  305. package/dist/internal/FundingRoutes.d.ts.map +1 -0
  306. package/dist/internal/FundingRoutes.js +490 -0
  307. package/dist/internal/FundingRoutes.js.map +1 -0
  308. package/dist/internal/Id.d.ts +15 -0
  309. package/dist/internal/Id.d.ts.map +1 -0
  310. package/dist/internal/Id.js +22 -0
  311. package/dist/internal/Id.js.map +1 -0
  312. package/dist/internal/Log.d.ts +9 -3
  313. package/dist/internal/Log.d.ts.map +1 -1
  314. package/dist/internal/Log.js +7 -0
  315. package/dist/internal/Log.js.map +1 -1
  316. package/dist/internal/OpenApi.d.ts +10 -20
  317. package/dist/internal/OpenApi.d.ts.map +1 -1
  318. package/dist/internal/OpenApi.js +8 -58
  319. package/dist/internal/OpenApi.js.map +1 -1
  320. package/dist/internal/RateLimit.d.ts +25 -0
  321. package/dist/internal/RateLimit.d.ts.map +1 -1
  322. package/dist/internal/RateLimit.js +51 -0
  323. package/dist/internal/RateLimit.js.map +1 -1
  324. package/dist/internal/Response.d.ts +1 -1
  325. package/dist/internal/Response.d.ts.map +1 -1
  326. package/dist/internal/Schema.d.ts +8 -0
  327. package/dist/internal/Schema.d.ts.map +1 -1
  328. package/dist/internal/Schema.js +7 -0
  329. package/dist/internal/Schema.js.map +1 -1
  330. package/dist/internal/Store.d.ts +7 -1
  331. package/dist/internal/Store.d.ts.map +1 -1
  332. package/dist/internal/Store.js +16 -2
  333. package/dist/internal/Store.js.map +1 -1
  334. package/dist/internal/Viem.d.ts +84 -0
  335. package/dist/internal/Viem.d.ts.map +1 -1
  336. package/dist/internal/Webhooks.d.ts +1 -1
  337. package/dist/internal/Webhooks.d.ts.map +1 -1
  338. package/dist/internal/Webhooks.js +3 -3
  339. package/dist/internal/Webhooks.js.map +1 -1
  340. package/package.json +33 -6
  341. package/src/ApiKey.ts +10 -2
  342. package/src/ApiKeys.test.ts +155 -1
  343. package/src/ApiKeys.ts +203 -1
  344. package/src/App.test.ts +64 -75741
  345. package/src/App.ts +218 -420
  346. package/src/Client.test-d.ts +657 -15
  347. package/src/Scope.ts +26 -4
  348. package/src/Webhooks.ts +2 -2
  349. package/src/admin/apps/api-keys.test.ts +51 -0
  350. package/src/admin/apps/api-keys.ts +84 -0
  351. package/src/admin/apps/scopes.test.ts +11 -1
  352. package/src/admin/apps/scopes.ts +3 -0
  353. package/src/admin/apps/verified-tokens.ts +1 -1
  354. package/src/admin/ui/src/components/Page.tsx +2 -3
  355. package/src/admin/ui/src/routes/__root.tsx +2 -2
  356. package/src/admin/ui/src/routes/api-keys.tsx +2 -2
  357. package/src/admin/ui.generated.ts +13 -13
  358. package/src/analytics/Analytics.test.ts +527 -0
  359. package/src/analytics/Analytics.ts +318 -0
  360. package/src/analytics/tables/requestEvents.ts +523 -0
  361. package/src/apps/composition.test.ts +66 -0
  362. package/src/apps/data/App.test.ts +72 -0
  363. package/src/apps/data/App.ts +330 -0
  364. package/src/apps/data/index.ts +2 -0
  365. package/src/apps/{activities.test.ts → data/routes/activities.test.ts} +3 -3
  366. package/src/apps/{activities.ts → data/routes/activities.ts} +15 -15
  367. package/src/apps/{addresses.ts → data/routes/addresses.ts} +6 -6
  368. package/src/apps/{balances.test.ts → data/routes/balances.test.ts} +2 -2
  369. package/src/apps/{balances.ts → data/routes/balances.ts} +46 -12
  370. package/src/apps/{blocks.test.ts → data/routes/blocks.test.ts} +3 -3
  371. package/src/apps/{blocks.ts → data/routes/blocks.ts} +14 -14
  372. package/src/apps/{coingecko.test.ts → data/routes/coingecko.test.ts} +2 -2
  373. package/src/apps/{coingecko.ts → data/routes/coingecko.ts} +14 -14
  374. package/src/apps/{exchanges.test.ts → data/routes/exchanges.test.ts} +1 -1
  375. package/src/apps/{exchanges.ts → data/routes/exchanges.ts} +20 -20
  376. package/src/apps/{fee-amm.test.ts → data/routes/fee-amm.test.ts} +3 -3
  377. package/src/apps/{fee-amm.ts → data/routes/fee-amm.ts} +18 -18
  378. package/src/apps/data/routes/funding.test.ts +214 -0
  379. package/src/apps/data/routes/funding.ts +584 -0
  380. package/src/apps/{indexer.test.ts → data/routes/indexer.test.ts} +26 -5
  381. package/src/apps/{indexer.ts → data/routes/indexer.ts} +13 -10
  382. package/src/apps/{receipts.test.ts → data/routes/receipts.test.ts} +3 -3
  383. package/src/apps/{receipts.ts → data/routes/receipts.ts} +15 -22
  384. package/src/apps/{rpc.test.ts → data/routes/rpc.test.ts} +122 -7
  385. package/src/apps/{rpc.ts → data/routes/rpc.ts} +38 -8
  386. package/src/apps/{tokenlist.test.ts → data/routes/tokenlist.test.ts} +3 -3
  387. package/src/apps/{tokenlist.ts → data/routes/tokenlist.ts} +9 -9
  388. package/src/apps/{tokens.test.ts → data/routes/tokens.test.ts} +2 -2
  389. package/src/apps/{tokens.ts → data/routes/tokens.ts} +20 -20
  390. package/src/apps/{transactions.test.ts → data/routes/transactions.test.ts} +3 -3
  391. package/src/apps/{transactions.ts → data/routes/transactions.ts} +15 -15
  392. package/src/apps/{transfers.test.ts → data/routes/transfers.test.ts} +2 -2
  393. package/src/apps/{transfers.ts → data/routes/transfers.ts} +15 -15
  394. package/src/apps/{verified-tokens.test.ts → data/routes/verified-tokens.test.ts} +3 -3
  395. package/src/apps/{verified-tokens.ts → data/routes/verified-tokens.ts} +10 -10
  396. package/src/apps/{webhooks.test.ts → data/routes/webhooks.test.ts} +7 -7
  397. package/src/apps/{webhooks.ts → data/routes/webhooks.ts} +50 -43
  398. package/src/apps/docs/App.ts +65 -0
  399. package/src/apps/docs/index.ts +1 -0
  400. package/src/apps/docs/reference.ts +132 -0
  401. package/src/apps/index.ts +4 -0
  402. package/src/apps/mcp.test.ts +206 -0
  403. package/src/apps/mcp.ts +250 -0
  404. package/src/apps/metadata.ts +23 -0
  405. package/src/apps/platform/App.ts +169 -0
  406. package/src/apps/platform/Billing.test.ts +530 -0
  407. package/src/apps/platform/Billing.ts +682 -0
  408. package/src/apps/platform/index.ts +2 -0
  409. package/src/apps/platform/routes/api-keys.test.ts +493 -0
  410. package/src/apps/platform/routes/api-keys.ts +543 -0
  411. package/src/apps/platform/routes/billing.test.ts +1100 -0
  412. package/src/apps/platform/routes/billing.ts +966 -0
  413. package/src/apps/platform/routes/invitations.test.ts +409 -0
  414. package/src/apps/platform/routes/invitations.ts +587 -0
  415. package/src/apps/platform/routes/me.test.ts +351 -0
  416. package/src/apps/platform/routes/me.ts +106 -0
  417. package/src/apps/platform/routes/members.test.ts +195 -0
  418. package/src/apps/platform/routes/members.ts +313 -0
  419. package/src/apps/platform/routes/orgs.test.ts +313 -0
  420. package/src/apps/platform/routes/orgs.ts +365 -0
  421. package/src/apps/platform/routes/projects.test.ts +181 -0
  422. package/src/apps/platform/routes/projects.ts +367 -0
  423. package/src/apps/platform/routes/scopes.test.ts +29 -0
  424. package/src/apps/platform/routes/scopes.ts +85 -0
  425. package/src/apps/platform/routes/usage.test.ts +422 -0
  426. package/src/apps/platform/routes/usage.ts +527 -0
  427. package/src/apps/relay/App.test.ts +933 -0
  428. package/src/apps/relay/App.ts +236 -0
  429. package/src/apps/relay/Sponsorships.test.ts +375 -0
  430. package/src/apps/relay/Sponsorships.ts +306 -0
  431. package/src/apps/relay/index.ts +2 -0
  432. package/src/cli/Cli.test.ts +25 -2
  433. package/src/cli/Cli.ts +18 -3
  434. package/src/cli/commands/admin.test.ts +83 -0
  435. package/src/cli/commands/admin.ts +91 -3
  436. package/src/db/Db.ts +296 -0
  437. package/src/db/Schema.ts +241 -2
  438. package/src/db/tables/billingSettings.test.ts +87 -0
  439. package/src/db/tables/billingSettings.ts +108 -0
  440. package/src/db/tables/invitations.ts +198 -0
  441. package/src/db/tables/memberships.ts +208 -0
  442. package/src/db/tables/organizations.test.ts +38 -2
  443. package/src/db/tables/organizations.ts +117 -2
  444. package/src/db/tables/projects.ts +107 -0
  445. package/src/db/tables/requestUsage.ts +231 -0
  446. package/src/db/tables/sponsoredTransactions.test.ts +325 -0
  447. package/src/db/tables/sponsoredTransactions.ts +464 -0
  448. package/src/db/tables/stripeCustomers.test.ts +109 -0
  449. package/src/db/tables/stripeCustomers.ts +162 -0
  450. package/src/db/tables/users.ts +85 -0
  451. package/src/handlers/internal/executionError.ts +2 -0
  452. package/src/handlers/internal/multisig.test.ts +84 -10
  453. package/src/handlers/internal/multisig.ts +15 -50
  454. package/src/handlers/internal/sponsorship.ts +106 -20
  455. package/src/handlers/internal/utils.ts +19 -7
  456. package/src/handlers/{internal/relay.localnet.test.ts → relay.test.ts} +2 -2
  457. package/src/handlers/relay.ts +23 -5
  458. package/src/index.ts +1 -0
  459. package/src/internal/Auth.test.ts +491 -25
  460. package/src/internal/Auth.ts +839 -84
  461. package/src/internal/Cache.ts +5 -1
  462. package/src/internal/Cursor.test.ts +3 -2
  463. package/src/internal/EdgeCache.test.ts +27 -4
  464. package/src/internal/EdgeCache.ts +47 -9
  465. package/src/internal/Fees.test.ts +32 -0
  466. package/src/internal/Fees.ts +51 -0
  467. package/src/internal/FundingRoutes.ts +583 -0
  468. package/src/internal/Id.test.ts +17 -0
  469. package/src/internal/Id.ts +24 -0
  470. package/src/internal/Log.test.ts +2 -1
  471. package/src/internal/Log.ts +16 -3
  472. package/src/internal/MetricSink.ts +1 -1
  473. package/src/internal/OpenApi.ts +12 -60
  474. package/src/internal/RateLimit.test.ts +84 -0
  475. package/src/internal/RateLimit.ts +70 -0
  476. package/src/internal/Response.ts +1 -1
  477. package/src/internal/Schema.ts +8 -0
  478. package/src/internal/Store.test.ts +52 -0
  479. package/src/internal/Store.ts +24 -5
  480. package/src/internal/Tidx.local.test.ts +1 -1
  481. package/src/internal/VerifiedTokens.test.ts +1 -1
  482. package/src/internal/WebhookDestination.test.ts +1 -1
  483. package/src/internal/Webhooks.test.ts +7 -7
  484. package/src/internal/Webhooks.ts +4 -4
  485. package/dist/apps/activities.js.map +0 -1
  486. package/dist/apps/addresses.js.map +0 -1
  487. package/dist/apps/balances.d.ts.map +0 -1
  488. package/dist/apps/balances.js.map +0 -1
  489. package/dist/apps/blocks.d.ts.map +0 -1
  490. package/dist/apps/blocks.js.map +0 -1
  491. package/dist/apps/coingecko.d.ts.map +0 -1
  492. package/dist/apps/coingecko.js.map +0 -1
  493. package/dist/apps/exchanges.d.ts.map +0 -1
  494. package/dist/apps/exchanges.js.map +0 -1
  495. package/dist/apps/fee-amm.d.ts.map +0 -1
  496. package/dist/apps/fee-amm.js.map +0 -1
  497. package/dist/apps/indexer.d.ts.map +0 -1
  498. package/dist/apps/indexer.js.map +0 -1
  499. package/dist/apps/receipts.d.ts.map +0 -1
  500. package/dist/apps/receipts.js.map +0 -1
  501. package/dist/apps/rpc.d.ts.map +0 -1
  502. package/dist/apps/rpc.js.map +0 -1
  503. package/dist/apps/tokenlist.d.ts.map +0 -1
  504. package/dist/apps/tokenlist.js.map +0 -1
  505. package/dist/apps/tokens.d.ts.map +0 -1
  506. package/dist/apps/tokens.js.map +0 -1
  507. package/dist/apps/transactions.d.ts.map +0 -1
  508. package/dist/apps/transactions.js.map +0 -1
  509. package/dist/apps/transfers.d.ts.map +0 -1
  510. package/dist/apps/transfers.js.map +0 -1
  511. package/dist/apps/verified-tokens.d.ts.map +0 -1
  512. package/dist/apps/verified-tokens.js.map +0 -1
  513. package/dist/apps/webhooks.js.map +0 -1
  514. package/dist/internal/OpenApi.fonts.d.ts.map +0 -1
  515. package/dist/internal/OpenApi.fonts.js.map +0 -1
  516. package/dist/internal/OpenApi.logo.d.ts.map +0 -1
  517. package/dist/internal/OpenApi.logo.js.map +0 -1
  518. package/src/handlers/internal/multisig.localnet.test.ts +0 -146
  519. /package/src/{internal/OpenApi.fonts.ts → apps/docs/fonts.ts} +0 -0
  520. /package/src/{internal/OpenApi.logo.ts → apps/docs/logo.ts} +0 -0
@@ -0,0 +1,306 @@
1
+ import type { Hex } from 'ox'
2
+ import { TxEnvelopeTempo } from 'ox/tempo'
3
+ import {
4
+ type Address,
5
+ type Client,
6
+ type EIP1193RequestFn,
7
+ type TransactionReceipt,
8
+ TransactionReceiptNotFoundError,
9
+ } from 'viem'
10
+ import { getTransactionReceipt } from 'viem/actions'
11
+
12
+ import * as Db from '../../db/Db.js'
13
+ import * as Fees from '../../internal/Fees.js'
14
+ import * as SponsoredTransactions from '../../db/tables/sponsoredTransactions.js'
15
+ import * as Tidx from '../../internal/Tidx.js'
16
+ import * as Viem from '../../internal/Viem.js'
17
+
18
+ /**
19
+ * Runs one finalization pass over pending sponsored transactions: rows with a
20
+ * receipt finalize with the actual fee (`gasUsed × effectiveGasPrice`, in
21
+ * fee-token base units); rows still unmined past the pending TTL fail, as do
22
+ * hash-less fill intents the TTL outlives. Rows whose receipt lookup errors
23
+ * persist past the error TTL fail so they cannot monopolize every batch.
24
+ *
25
+ * @param db - The database holding `sponsored_transactions`.
26
+ * @param options - Finalization options.
27
+ * @returns Counters for the pass.
28
+ */
29
+ export async function finalize(db: Db.Db, options: finalize.Options): Promise<finalize.Result> {
30
+ const {
31
+ batchSize = 100,
32
+ getClient,
33
+ now = () => new Date(),
34
+ pendingTtlMs = 3_600_000,
35
+ receiptErrorTtlMs = 86_400_000,
36
+ } = options
37
+
38
+ const rows = await SponsoredTransactions.listPending(db, { limit: batchSize })
39
+ const result = { failed: 0, finalized: 0, pending: 0 }
40
+
41
+ for (const row of rows) {
42
+ // Fill intents carry no hash until reconciliation matches them on-chain;
43
+ // here only the TTL can settle them.
44
+ if (row.transactionHash === null) {
45
+ if (Date.parse(row.createdAt) + pendingTtlMs <= now().getTime()) {
46
+ await SponsoredTransactions.fail(db, row.id, now().toISOString())
47
+ result.failed++
48
+ } else result.pending++
49
+ continue
50
+ }
51
+
52
+ let receipt: TransactionReceipt | null
53
+ try {
54
+ receipt = await getTransactionReceipt(getClient(row.chainId), {
55
+ hash: row.transactionHash as Hex.Hex,
56
+ })
57
+ } catch (error) {
58
+ // Unmined transactions surface as a typed not-found; let the TTL decide.
59
+ if (error instanceof TransactionReceiptNotFoundError) receipt = null
60
+ else {
61
+ if (Date.parse(row.createdAt) + receiptErrorTtlMs <= now().getTime()) {
62
+ await SponsoredTransactions.fail(db, row.id, now().toISOString())
63
+ result.failed++
64
+ } else result.pending++
65
+ continue
66
+ }
67
+ }
68
+
69
+ if (receipt) {
70
+ // A reverted transaction still finalizes: the fee payer paid its fees.
71
+ const feeAmount = Fees.fromGas(receipt.gasUsed, receipt.effectiveGasPrice)
72
+ await SponsoredTransactions.finalize(db, row.id, {
73
+ feeAmount: feeAmount.toString(),
74
+ finalizedAt: now().toISOString(),
75
+ })
76
+ result.finalized++
77
+ } else if (Date.parse(row.createdAt) + pendingTtlMs <= now().getTime()) {
78
+ await SponsoredTransactions.fail(db, row.id, now().toISOString())
79
+ result.failed++
80
+ } else result.pending++
81
+ }
82
+
83
+ return result
84
+ }
85
+
86
+ export declare namespace finalize {
87
+ /** Options for {@link finalize}. */
88
+ type Options = {
89
+ /** Maximum pending rows processed per pass. @default 100 */
90
+ batchSize?: number | undefined
91
+ /** Client resolver keyed by the row's chain id. */
92
+ getClient: (chainId: number) => Client
93
+ /** Clock; injectable for tests. */
94
+ now?: (() => Date) | undefined
95
+ /** Age after which an unmined sponsorship fails. @default 1 hour */
96
+ pendingTtlMs?: number | undefined
97
+ /** Age after which repeated receipt lookup errors fail a row. @default 24 hours */
98
+ receiptErrorTtlMs?: number | undefined
99
+ }
100
+
101
+ /** Counters returned by one finalization pass. */
102
+ type Result = {
103
+ /** Rows marked failed (unmined past the pending TTL). */
104
+ failed: number
105
+ /** Rows finalized with an actual fee. */
106
+ finalized: number
107
+ /** Rows left pending (unmined, or receipt lookup errored within the error TTL). */
108
+ pending: number
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Runs one reconciliation pass over pending fill intents (rows with no
114
+ * transaction hash): fetches the fee payer's recent on-chain transactions
115
+ * from TIDX, recomputes each candidate's fee-payer sign payload, and fills
116
+ * matching intents' hashes so the receipt pass can finalize them. Query or
117
+ * candidate errors leave intents for the next pass.
118
+ *
119
+ * @param db - The database holding `sponsored_transactions`.
120
+ * @param options - Reconciliation options.
121
+ * @returns Counters for the pass.
122
+ */
123
+ export async function reconcile(db: Db.Db, options: reconcile.Options): Promise<reconcile.Result> {
124
+ const {
125
+ batchSize = 100,
126
+ candidateCap = 500,
127
+ failedIntentTtlMs = 86_400_000,
128
+ feePayer,
129
+ getClient,
130
+ getTidx,
131
+ skewMs = 120_000,
132
+ } = options
133
+
134
+ const intents = await SponsoredTransactions.listIntents(db, {
135
+ failedSince: new Date(Date.now() - failedIntentTtlMs).toISOString(),
136
+ limit: batchSize,
137
+ })
138
+ const result = { matched: 0, scanned: 0 }
139
+ if (intents.length === 0) return result
140
+
141
+ const chains = new Map<number, SponsoredTransactions.Record[]>()
142
+ for (const intent of intents) chains.set(intent.chainId, [...(chains.get(intent.chainId) ?? []), intent]) // prettier-ignore
143
+
144
+ for (const [chainId, rows] of chains) {
145
+ const bySignPayload = new Map(rows.map((row) => [row.signPayload, row]))
146
+ // The intent set bounds the scan window: no cursor state to persist.
147
+ const since = new Date(
148
+ Math.min(...rows.map((row) => Date.parse(row.createdAt))) - skewMs,
149
+ ).toISOString()
150
+
151
+ let candidates: readonly globalThis.Record<string, unknown>[]
152
+ try {
153
+ // Dynamic filter values; the tidx client's query type wants literals.
154
+ const fetched = await getTidx(chainId).fetch({
155
+ chainId,
156
+ query:
157
+ `SELECT hash, "from" FROM txs WHERE fee_payer = '${feePayer.toLowerCase()}' AND block_timestamp >= '${since}' ORDER BY block_timestamp ASC LIMIT ${candidateCap}` as string,
158
+ })
159
+ candidates = fetched.rows
160
+ } catch {
161
+ // Indexer outage must not fail intents; retry on the next pass.
162
+ continue
163
+ }
164
+
165
+ for (const candidate of candidates) {
166
+ if (bySignPayload.size === 0) break
167
+ const hash = candidate['hash'] as Hex.Hex | undefined
168
+ const sender = candidate['from'] as Address | undefined
169
+ if (!hash || !sender) continue
170
+ result.scanned++
171
+ try {
172
+ const serialized = await getRawTransaction(getClient(chainId), hash)
173
+ if (!serialized?.startsWith(TxEnvelopeTempo.serializedType)) continue
174
+ const signPayload = TxEnvelopeTempo.getFeePayerSignPayload(
175
+ TxEnvelopeTempo.deserialize(serialized as TxEnvelopeTempo.Serialized),
176
+ { sender },
177
+ )
178
+ const intent = bySignPayload.get(signPayload)
179
+ if (!intent) continue
180
+ await SponsoredTransactions.assignTransactionHash(db, intent.id, hash)
181
+ bySignPayload.delete(signPayload)
182
+ result.matched++
183
+ } catch {
184
+ // Unresolvable candidate (lookup error, malformed envelope): skip it.
185
+ }
186
+ }
187
+ }
188
+
189
+ return result
190
+ }
191
+
192
+ export declare namespace reconcile {
193
+ /** Options for {@link reconcile}. */
194
+ type Options = {
195
+ /** Maximum intents considered per pass. @default 100 */
196
+ batchSize?: number | undefined
197
+ /** Maximum on-chain candidates fetched per chain per pass. @default 500 */
198
+ candidateCap?: number | undefined
199
+ /** How long failed intents remain reconciliation candidates. @default 24 hours */
200
+ failedIntentTtlMs?: number | undefined
201
+ /** Fee payer address whose on-chain transactions are candidates. */
202
+ feePayer: Address
203
+ /** Client resolver keyed by the intent's chain id. */
204
+ getClient: (chainId: number) => Client
205
+ /** TIDX client resolver keyed by the intent's chain id. */
206
+ getTidx: (chainId: number) => Tidx.Client
207
+ /** Lookback slack before the oldest intent's creation. @default 2 minutes */
208
+ skewMs?: number | undefined
209
+ }
210
+
211
+ /** Counters returned by one reconciliation pass. */
212
+ type Result = {
213
+ /** Intents matched to an on-chain transaction. */
214
+ matched: number
215
+ /** On-chain candidates inspected. */
216
+ scanned: number
217
+ }
218
+ }
219
+
220
+ /**
221
+ * Fetches a mined transaction's canonical envelope bytes.
222
+ * `eth_getRawTransactionByHash` is a node extension absent from viem's typed
223
+ * RPC schema, hence the request-fn narrowing.
224
+ */
225
+ function getRawTransaction(client: Client, hash: Hex.Hex) {
226
+ type Request = EIP1193RequestFn<
227
+ [
228
+ {
229
+ Method: 'eth_getRawTransactionByHash'
230
+ Parameters: readonly [Hex.Hex]
231
+ ReturnType: Hex.Hex | null
232
+ },
233
+ ]
234
+ >
235
+ return (client.request as Request)({ method: 'eth_getRawTransactionByHash', params: [hash] })
236
+ }
237
+
238
+ /**
239
+ * Builds a scheduled sponsorship finalizer bound to a database and RPC config.
240
+ * A host runtime drives {@link createFinalizer.Finalizer.tick} on a timer (a
241
+ * Cloudflare `scheduled` cron handler, a self-host `setInterval`, …); each
242
+ * tick reconciles fill intents (when `feePayer` + `tidx` are configured) and
243
+ * runs one {@link finalize} pass, with memoized per-chain clients.
244
+ */
245
+ export function createFinalizer(options: createFinalizer.Options): createFinalizer.Finalizer {
246
+ const clients = new Map<number, Viem.getClient.ReturnType>()
247
+ const getClient = (chainId: number) => {
248
+ let client = clients.get(chainId)
249
+ if (!client) {
250
+ client = Viem.getClient({ chainId: chainId as Viem.ChainId, rpc: options.rpc })
251
+ clients.set(chainId, client)
252
+ }
253
+ return client
254
+ }
255
+ const getTidx = options.tidx
256
+ ? Tidx.createGetClient({ defaultChainId: options.defaultChainId, tidx: options.tidx })
257
+ : undefined
258
+ const feePayer = options.feePayer
259
+
260
+ return {
261
+ async tick() {
262
+ // Resolve per tick so Workers factories build a fresh Hyperdrive-pooled
263
+ // connection each run.
264
+ const db = Db.get(options.db)
265
+ // Reconcile first so freshly matched intents finalize in the same tick.
266
+ const reconciled =
267
+ feePayer && getTidx
268
+ ? await reconcile(db, {
269
+ batchSize: options.batchSize,
270
+ feePayer,
271
+ getClient,
272
+ getTidx: (chainId) => getTidx(chainId as Viem.ChainId),
273
+ })
274
+ : { matched: 0, scanned: 0 }
275
+ const finalized = await finalize(db, {
276
+ batchSize: options.batchSize,
277
+ getClient,
278
+ now: options.now,
279
+ pendingTtlMs: options.pendingTtlMs,
280
+ })
281
+ return { ...finalized, ...reconciled }
282
+ },
283
+ }
284
+ }
285
+
286
+ export declare namespace createFinalizer {
287
+ /** Options for {@link createFinalizer}. */
288
+ type Options = Pick<finalize.Options, 'batchSize' | 'now' | 'pendingTtlMs'> & {
289
+ /** Database holding sponsorship rows, or a factory resolved per tick (Workers/Hyperdrive). */
290
+ db: Db.Source
291
+ /** Default chain id for the TIDX client resolver. */
292
+ defaultChainId?: Viem.ChainId | undefined
293
+ /** Fee payer address whose on-chain transactions reconcile fill intents; omit to skip reconciliation. */
294
+ feePayer?: Address | undefined
295
+ /** RPC options for the receipt-lookup clients. */
296
+ rpc?: Viem.getClient.Rpc | undefined
297
+ /** TIDX query client options for intent reconciliation; omit to skip reconciliation. */
298
+ tidx?: Tidx.getClient.Tidx | undefined
299
+ }
300
+
301
+ /** A scheduled sponsorship finalizer. */
302
+ type Finalizer = {
303
+ /** Runs one reconcile + finalize pass. */
304
+ tick(): Promise<finalize.Result & reconcile.Result>
305
+ }
306
+ }
@@ -0,0 +1,2 @@
1
+ export { relay } from './App.js'
2
+ export * as Sponsorships from './Sponsorships.js'
@@ -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
- * Creates an incur CLI backed by a hosted Tempo API, plus the operator
7
- * commands (`<cli> admin …`).
14
+ * Creates an incur CLI backed by a hosted Tempo API, plus the super 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,15 +90,59 @@ 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
 
52
109
  /**
53
- * Operator commands that run against a deployment's infrastructure rather
110
+ * Super admin commands that run against a deployment's infrastructure rather
54
111
  * than the hosted API (database migrations, …).
55
112
  */
56
113
  export const admin = incur_Cli
57
114
  .create('admin', {
58
- description: 'Operator commands for a Tempo API deployment.',
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
+ }