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,493 @@
1
+ import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts'
2
+
3
+ import * as ApiKeys from '../../../ApiKeys.js'
4
+ import * as TestApp from '../../../../test/App.js'
5
+ import * as Keys from './api-keys.js'
6
+ import * as Orgs from './orgs.js'
7
+ import * as Projects from './projects.js'
8
+
9
+ /** Origin pinned for SIWE domain binding; Hono test requests use this host. */
10
+ const origin = 'http://localhost'
11
+
12
+ /** Super admin secret configured on the test app. */
13
+ const secret = 'tempo:sk:c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2'
14
+
15
+ /** RequestInit for a JSON mutation carrying the session cookie. */
16
+ function json(method: 'PATCH' | 'POST', cookie: string, body: unknown) {
17
+ return {
18
+ body: JSON.stringify(body),
19
+ headers: { 'content-type': 'application/json', cookie },
20
+ method,
21
+ }
22
+ }
23
+
24
+ /** RequestInit for a JSON mutation authenticated as the super admin. */
25
+ function superAdminJson(method: 'PATCH' | 'POST', body: unknown) {
26
+ return {
27
+ body: JSON.stringify(body),
28
+ headers: { 'content-type': 'application/json', 'tempo-api-key': secret },
29
+ method,
30
+ }
31
+ }
32
+
33
+ // Resolve-cache disabled so revocation assertions observe the store directly.
34
+ type State = ReturnType<typeof TestApp.kvStore>
35
+
36
+ function createApp(options: { kv?: { store: State } } = {}) {
37
+ return TestApp.create({
38
+ auth: { cache: false, superAdmin: { secret } },
39
+ ...(options.kv ? { kv: options.kv } : {}),
40
+ session: { wallet: { origin } },
41
+ })
42
+ }
43
+
44
+ function failRecordDeletes(store: State) {
45
+ const delete_ = store.delete.bind(store)
46
+ store.delete = async (key) => {
47
+ if (key.startsWith('apikey:')) throw new Error('KV delete failed.')
48
+ await delete_(key)
49
+ }
50
+ }
51
+
52
+ /** Signs in a fresh scripted account; returns its session cookie. */
53
+ async function session(app: TestApp.signIn.App) {
54
+ const account = privateKeyToAccount(generatePrivateKey())
55
+ const { cookie } = await TestApp.signIn(app, account)
56
+ return cookie!
57
+ }
58
+
59
+ /** Creates an org and a project owned by the session. */
60
+ async function fixture(app: ReturnType<typeof createApp>, cookie: string) {
61
+ const org = await TestApp.json(
62
+ await app.request('/v1/orgs', json('POST', cookie, { name: 'Acme' })),
63
+ Orgs.schema.Organization,
64
+ )
65
+ const project = await TestApp.json(
66
+ await app.request(`/v1/orgs/${org.id}/projects`, json('POST', cookie, { name: 'Checkout' })),
67
+ Projects.schema.Project,
68
+ )
69
+ return { org, project }
70
+ }
71
+
72
+ describe('keys', () => {
73
+ test('mints, lists, authenticates, and revokes a project key', async () => {
74
+ const app = createApp()
75
+ const cookie = await session(app)
76
+ const { org, project } = await fixture(app, cookie)
77
+ const base = `/v1/orgs/${org.id}/projects/${project.id}/api-keys`
78
+
79
+ // Scoped keys mint through the super admin: no scope is self-issuable.
80
+ const minted = await app.request(
81
+ base,
82
+ superAdminJson('POST', { environment: 'sandbox', name: 'CI', scopes: ['data:read'] }),
83
+ )
84
+ expect(minted.status).toBe(200)
85
+ const created = await TestApp.json(minted, Keys.schema.createApiKey.Response)
86
+ expect(created.id).toMatch(/^key_/)
87
+ expect(created.token).toMatch(/^tempo_sandbox:sk:[a-f0-9]{48}$/)
88
+ expect(created.orgId).toBe(org.id)
89
+ expect(created.projectId).toBe(project.id)
90
+ expect(created.createdBy).toBe('super_admin')
91
+
92
+ // The minted key authenticates data API requests. `no-store` forces an
93
+ // origin pass — an edge-cache hit would skip auth and mask the assertion.
94
+ const data = await app.request('/v1/tokens', {
95
+ headers: { 'cache-control': 'no-store', 'tempo-api-key': created.token },
96
+ })
97
+ expect(data.status).toBe(200)
98
+
99
+ // Listing exposes metadata only — never the token. The key is sandbox, so
100
+ // the list is scoped to that environment (production is the default).
101
+ const listed = await app.request(`${base}?environment=sandbox`, { headers: { cookie } })
102
+ expect(listed.status).toBe(200)
103
+ const list = await TestApp.json(listed, Keys.schema.listApiKeys.Response)
104
+ expect(list.data.map(({ id }) => id)).toEqual([created.id])
105
+ expect(JSON.stringify(list)).not.toContain(created.token)
106
+ expect(list.data[0]!.tokenLast4).toBe(created.token.slice(-4))
107
+
108
+ const revoked = await app.request(`${base}/${created.id}`, {
109
+ headers: { cookie },
110
+ method: 'DELETE',
111
+ })
112
+ expect(revoked.status).toBe(200)
113
+ expect(await TestApp.json(revoked, Keys.schema.revokeApiKey.Response)).toEqual({
114
+ id: created.id,
115
+ })
116
+
117
+ // With the resolve cache disabled, revocation is immediate.
118
+ const after = await app.request('/v1/tokens', {
119
+ headers: { 'cache-control': 'no-store', 'tempo-api-key': created.token },
120
+ })
121
+ expect(after.status).toBe(401)
122
+ const empty = await TestApp.json(
123
+ await app.request(base, { headers: { cookie } }),
124
+ Keys.schema.listApiKeys.Response,
125
+ )
126
+ expect(empty).toEqual({ data: [] })
127
+ })
128
+
129
+ test('rejects the wildcard scope at mint', async () => {
130
+ const app = createApp()
131
+ const cookie = await session(app)
132
+ const { org, project } = await fixture(app, cookie)
133
+
134
+ const response = await app.request(
135
+ `/v1/orgs/${org.id}/projects/${project.id}/api-keys`,
136
+ json('POST', cookie, { scopes: ['*'] }),
137
+ )
138
+ expect(response.status).toBe(400)
139
+ })
140
+
141
+ test('mints zero-scope keys for sessions', async () => {
142
+ const app = createApp()
143
+ const cookie = await session(app)
144
+ const { org, project } = await fixture(app, cookie)
145
+
146
+ const minted = await app.request(
147
+ `/v1/orgs/${org.id}/projects/${project.id}/api-keys`,
148
+ json('POST', cookie, { name: 'CI' }),
149
+ )
150
+ expect(minted.status).toBe(200)
151
+ const created = await TestApp.json(minted, Keys.schema.createApiKey.Response)
152
+ expect(created.scopes).toEqual([])
153
+ expect(created.createdBy).toMatch(/^usr_/)
154
+
155
+ // The key authenticates but grants nothing: scoped routes stay forbidden.
156
+ const data = await app.request('/v1/tokens', {
157
+ headers: { 'cache-control': 'no-store', 'tempo-api-key': created.token },
158
+ })
159
+ expect(data.status).toBe(403)
160
+ const { requestId, ...body } = (await data.json()) as { requestId?: string }
161
+ expect(requestId).toBeDefined()
162
+ expect(body).toMatchInlineSnapshot(`
163
+ {
164
+ "error": {
165
+ "code": "api_key_forbidden",
166
+ "message": "API key missing required scope",
167
+ },
168
+ }
169
+ `)
170
+ })
171
+
172
+ test('lets sessions mint catalog scopes on production keys', async () => {
173
+ const app = createApp()
174
+ const cookie = await session(app)
175
+ const { org, project } = await fixture(app, cookie)
176
+
177
+ const created = await TestApp.json(
178
+ await app.request(
179
+ `/v1/orgs/${org.id}/projects/${project.id}/api-keys`,
180
+ json('POST', cookie, { scopes: ['data:read'] }),
181
+ ),
182
+ Keys.schema.createApiKey.Response,
183
+ )
184
+ expect(created.environment).toBe('production')
185
+ expect(created.scopes).toEqual(['data:read'])
186
+ // Session-minted, not granted through the super admin.
187
+ expect(created.createdBy).not.toBe('super_admin')
188
+ })
189
+
190
+ test('rejects session mints of non-self-serve scopes (webhooks)', async () => {
191
+ const app = createApp()
192
+ const cookie = await session(app)
193
+ const { org, project } = await fixture(app, cookie)
194
+
195
+ const response = await app.request(
196
+ `/v1/orgs/${org.id}/projects/${project.id}/api-keys`,
197
+ json('POST', cookie, { scopes: ['webhooks:write'] }),
198
+ )
199
+ expect(response.status).toBe(403)
200
+ const body = (await response.json()) as { error: { code: string } }
201
+ expect(body.error.code).toBe('scope_not_issuable')
202
+ })
203
+
204
+ test('lets sessions mint catalog scopes on sandbox keys', async () => {
205
+ const app = createApp()
206
+ const cookie = await session(app)
207
+ const { org, project } = await fixture(app, cookie)
208
+
209
+ const created = await TestApp.json(
210
+ await app.request(
211
+ `/v1/orgs/${org.id}/projects/${project.id}/api-keys`,
212
+ json('POST', cookie, { environment: 'sandbox', scopes: ['data:read'] }),
213
+ ),
214
+ Keys.schema.createApiKey.Response,
215
+ )
216
+ expect(created.environment).toBe('sandbox')
217
+ expect(created.scopes).toEqual(['data:read'])
218
+ // Session-minted, not granted through the super admin.
219
+ expect(created.createdBy).not.toBe('super_admin')
220
+ })
221
+
222
+ test('binds keys to their project on every verb', async () => {
223
+ const app = createApp()
224
+ const owner = await session(app)
225
+ const { org, project } = await fixture(app, owner)
226
+ const other = await TestApp.json(
227
+ await app.request(`/v1/orgs/${org.id}/projects`, json('POST', owner, { name: 'Payments' })),
228
+ Projects.schema.Project,
229
+ )
230
+ const minted = await TestApp.json(
231
+ await app.request(
232
+ `/v1/orgs/${org.id}/projects/${project.id}/api-keys`,
233
+ superAdminJson('POST', { scopes: ['data:read'] }),
234
+ ),
235
+ Keys.schema.createApiKey.Response,
236
+ )
237
+
238
+ // Same org, sibling project: the key id must not resolve through it.
239
+ const crossList = await app.request(`/v1/orgs/${org.id}/projects/${other.id}/api-keys`, {
240
+ headers: { cookie: owner },
241
+ })
242
+ expect((await TestApp.json(crossList, Keys.schema.listApiKeys.Response)).data).toEqual([])
243
+ const crossRevoke = await app.request(
244
+ `/v1/orgs/${org.id}/projects/${other.id}/api-keys/${minted.id}`,
245
+ { headers: { cookie: owner }, method: 'DELETE' },
246
+ )
247
+ expect(crossRevoke.status).toBe(404)
248
+
249
+ // Foreign session: the whole surface 404s at the org-ownership link.
250
+ const stranger = await session(app)
251
+ const foreignMint = await app.request(
252
+ `/v1/orgs/${org.id}/projects/${project.id}/api-keys`,
253
+ json('POST', stranger, { scopes: ['data:read'] }),
254
+ )
255
+ expect(foreignMint.status).toBe(404)
256
+ const foreignRevoke = await app.request(
257
+ `/v1/orgs/${org.id}/projects/${project.id}/api-keys/${minted.id}`,
258
+ { headers: { cookie: stranger }, method: 'DELETE' },
259
+ )
260
+ expect(foreignRevoke.status).toBe(404)
261
+
262
+ // The key survives every attempt, still listed for the owner.
263
+ const listed = await app.request(`/v1/orgs/${org.id}/projects/${project.id}/api-keys`, {
264
+ headers: { cookie: owner },
265
+ })
266
+ const list = await TestApp.json(listed, Keys.schema.listApiKeys.Response)
267
+ expect(list.data.map(({ id }) => id)).toEqual([minted.id])
268
+ })
269
+
270
+ test('lists keys org-wide across projects', async () => {
271
+ const store = TestApp.kvStore({ keys: [] })
272
+ const app = createApp({ kv: { store } })
273
+ const owner = await session(app)
274
+ const { org, project } = await fixture(app, owner)
275
+ const other = await TestApp.json(
276
+ await app.request(`/v1/orgs/${org.id}/projects`, json('POST', owner, { name: 'Payments' })),
277
+ Projects.schema.Project,
278
+ )
279
+
280
+ const first = await TestApp.json(
281
+ await app.request(
282
+ `/v1/orgs/${org.id}/projects/${project.id}/api-keys`,
283
+ superAdminJson('POST', { name: 'Checkout CI', scopes: ['data:read'] }),
284
+ ),
285
+ Keys.schema.createApiKey.Response,
286
+ )
287
+ const second = await TestApp.json(
288
+ await app.request(
289
+ `/v1/orgs/${org.id}/projects/${other.id}/api-keys`,
290
+ superAdminJson('POST', { name: 'Payments CI', scopes: ['data:read'] }),
291
+ ),
292
+ Keys.schema.createApiKey.Response,
293
+ )
294
+ // Org-attributed key without a project (admin-surface mints land like this).
295
+ const orgWide = await ApiKeys.mint(store, { orgId: org.id, scopes: ['data:read'] })
296
+
297
+ const listed = await app.request(`/v1/orgs/${org.id}/api-keys`, {
298
+ headers: { cookie: owner },
299
+ })
300
+ const list = await TestApp.json(listed, Keys.schema.listOrgApiKeys.Response)
301
+ expect(list.data.map(({ id }) => id).sort()).toEqual(
302
+ [first.id, second.id, orgWide.record.id].sort(),
303
+ )
304
+ expect(list.data.find(({ id }) => id === first.id)?.projectId).toBe(project.id)
305
+ expect(list.data.find(({ id }) => id === second.id)?.projectId).toBe(other.id)
306
+ expect(list.data.find(({ id }) => id === orgWide.record.id)?.projectId).toBeUndefined()
307
+
308
+ // Non-members see the org as absent.
309
+ const stranger = await session(app)
310
+ const foreign = await app.request(`/v1/orgs/${org.id}/api-keys`, {
311
+ headers: { cookie: stranger },
312
+ })
313
+ expect(foreign.status).toBe(404)
314
+ })
315
+
316
+ test('filters keys by environment (defaulting to production)', async () => {
317
+ const app = createApp()
318
+ const cookie = await session(app)
319
+ const { org, project } = await fixture(app, cookie)
320
+ const projectBase = `/v1/orgs/${org.id}/projects/${project.id}/api-keys`
321
+ const orgBase = `/v1/orgs/${org.id}/api-keys`
322
+
323
+ const production = await TestApp.json(
324
+ await app.request(
325
+ projectBase,
326
+ superAdminJson('POST', { name: 'Prod', scopes: ['data:read'] }),
327
+ ),
328
+ Keys.schema.createApiKey.Response,
329
+ )
330
+ const sandbox = await TestApp.json(
331
+ await app.request(
332
+ projectBase,
333
+ superAdminJson('POST', { environment: 'sandbox', name: 'Sandbox', scopes: ['data:read'] }),
334
+ ),
335
+ Keys.schema.createApiKey.Response,
336
+ )
337
+
338
+ const ids = async (path: string) =>
339
+ (
340
+ await TestApp.json(
341
+ await app.request(path, { headers: { cookie } }),
342
+ Keys.schema.listApiKeys.Response,
343
+ )
344
+ ).data
345
+ .map(({ id }) => id)
346
+ .sort()
347
+
348
+ // Each filter returns only its own environment.
349
+ expect(await ids(`${projectBase}?environment=production`)).toEqual([production.id])
350
+ expect(await ids(`${projectBase}?environment=sandbox`)).toEqual([sandbox.id])
351
+ // Absent param defaults to production.
352
+ expect(await ids(projectBase)).toEqual([production.id])
353
+
354
+ // Same behavior on the org-wide list route.
355
+ const orgIds = async (path: string) =>
356
+ (
357
+ await TestApp.json(
358
+ await app.request(path, { headers: { cookie } }),
359
+ Keys.schema.listOrgApiKeys.Response,
360
+ )
361
+ ).data
362
+ .map(({ id }) => id)
363
+ .sort()
364
+ expect(await orgIds(`${orgBase}?environment=sandbox`)).toEqual([sandbox.id])
365
+ expect(await orgIds(orgBase)).toEqual([production.id])
366
+
367
+ // An unknown environment value is rejected.
368
+ const invalid = await app.request(`${projectBase}?environment=staging`, { headers: { cookie } })
369
+ expect(invalid.status).toBe(400)
370
+ })
371
+
372
+ test('revokes keys when their project or organization is deleted', async () => {
373
+ const app = createApp()
374
+ const cookie = await session(app)
375
+ const { org, project } = await fixture(app, cookie)
376
+ const minted = await TestApp.json(
377
+ await app.request(
378
+ `/v1/orgs/${org.id}/projects/${project.id}/api-keys`,
379
+ superAdminJson('POST', { scopes: ['data:read'] }),
380
+ ),
381
+ Keys.schema.createApiKey.Response,
382
+ )
383
+
384
+ const deletedProject = await app.request(`/v1/orgs/${org.id}/projects/${project.id}`, {
385
+ headers: { cookie },
386
+ method: 'DELETE',
387
+ })
388
+ expect(deletedProject.status).toBe(200)
389
+ const afterProject = await app.request('/v1/tokens', {
390
+ headers: { 'cache-control': 'no-store', 'tempo-api-key': minted.token },
391
+ })
392
+ expect(afterProject.status).toBe(401)
393
+
394
+ // Org deletion revokes keys of every remaining project.
395
+ const other = await TestApp.json(
396
+ await app.request(`/v1/orgs/${org.id}/projects`, json('POST', cookie, { name: 'Payments' })),
397
+ Projects.schema.Project,
398
+ )
399
+ const survivor = await TestApp.json(
400
+ await app.request(
401
+ `/v1/orgs/${org.id}/projects/${other.id}/api-keys`,
402
+ superAdminJson('POST', { scopes: ['data:read'] }),
403
+ ),
404
+ Keys.schema.createApiKey.Response,
405
+ )
406
+ const deletedOrg = await app.request(`/v1/orgs/${org.id}`, {
407
+ headers: { cookie },
408
+ method: 'DELETE',
409
+ })
410
+ expect(deletedOrg.status).toBe(200)
411
+ const afterOrg = await app.request('/v1/tokens', {
412
+ headers: { 'cache-control': 'no-store', 'tempo-api-key': survivor.token },
413
+ })
414
+ expect(afterOrg.status).toBe(401)
415
+ })
416
+
417
+ test('keeps the project when key revocation fails during project deletion', async () => {
418
+ const store = TestApp.kvStore({ keys: [] })
419
+ const app = createApp({ kv: { store } })
420
+ const cookie = await session(app)
421
+ const { org, project } = await fixture(app, cookie)
422
+ await TestApp.json(
423
+ await app.request(
424
+ `/v1/orgs/${org.id}/projects/${project.id}/api-keys`,
425
+ superAdminJson('POST', { scopes: ['data:read'] }),
426
+ ),
427
+ Keys.schema.createApiKey.Response,
428
+ )
429
+ failRecordDeletes(store)
430
+
431
+ const deletedProject = await app.request(`/v1/orgs/${org.id}/projects/${project.id}`, {
432
+ headers: { cookie },
433
+ method: 'DELETE',
434
+ })
435
+ expect(deletedProject.status).toBe(502)
436
+
437
+ const projectStill = await app.request(`/v1/orgs/${org.id}/projects/${project.id}`, {
438
+ headers: { cookie },
439
+ })
440
+ expect(projectStill.status).toBe(200)
441
+ })
442
+
443
+ test('keeps the organization when key revocation fails during organization deletion', async () => {
444
+ const store = TestApp.kvStore({ keys: [] })
445
+ const app = createApp({ kv: { store } })
446
+ const cookie = await session(app)
447
+ const { org, project } = await fixture(app, cookie)
448
+ await TestApp.json(
449
+ await app.request(
450
+ `/v1/orgs/${org.id}/projects/${project.id}/api-keys`,
451
+ superAdminJson('POST', { scopes: ['data:read'] }),
452
+ ),
453
+ Keys.schema.createApiKey.Response,
454
+ )
455
+ failRecordDeletes(store)
456
+
457
+ const deletedOrg = await app.request(`/v1/orgs/${org.id}`, {
458
+ headers: { cookie },
459
+ method: 'DELETE',
460
+ })
461
+ expect(deletedOrg.status).toBe(502)
462
+
463
+ const orgStill = await app.request(`/v1/orgs/${org.id}`, { headers: { cookie } })
464
+ expect(orgStill.status).toBe(200)
465
+ })
466
+
467
+ test('mints as the super admin with super admin attribution', async () => {
468
+ const app = createApp()
469
+ const cookie = await session(app)
470
+ const { org, project } = await fixture(app, cookie)
471
+
472
+ const minted = await app.request(`/v1/orgs/${org.id}/projects/${project.id}/api-keys`, {
473
+ body: JSON.stringify({ scopes: ['data:read'] }),
474
+ headers: { 'content-type': 'application/json', 'tempo-api-key': secret },
475
+ method: 'POST',
476
+ })
477
+ expect(minted.status).toBe(200)
478
+ const created = await TestApp.json(minted, Keys.schema.createApiKey.Response)
479
+ expect(created.createdBy).toBe('super_admin')
480
+ expect(created.environment).toBe('production')
481
+ })
482
+
483
+ test('rejects API keys on the keys surface', async () => {
484
+ const app = createApp()
485
+ const cookie = await session(app)
486
+ const { org, project } = await fixture(app, cookie)
487
+
488
+ const response = await app.request(`/v1/orgs/${org.id}/projects/${project.id}/api-keys`, {
489
+ headers: { 'tempo-api-key': TestApp.key.token },
490
+ })
491
+ expect(response.status).toBe(403)
492
+ })
493
+ })