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
@@ -175,6 +175,7 @@ describe('require', () => {
175
175
  expect(entries[0]?.errorCode).toMatchInlineSnapshot(`"api_key_forbidden"`)
176
176
  expect(entries[0]?.principal).toMatchInlineSnapshot(`
177
177
  {
178
+ "billingActive": false,
178
179
  "environment": "production",
179
180
  "id": "key_test",
180
181
  "orgId": "org_test",
@@ -267,10 +268,247 @@ describe('require', () => {
267
268
  })
268
269
  })
269
270
 
271
+ describe('session', () => {
272
+ const sessionPrincipal = {
273
+ id: 'usr_test',
274
+ identity: { provider: 'wallet', subject: '0x0000000000000000000000000000000000000001' },
275
+ type: 'session',
276
+ } as const satisfies Auth.SessionPrincipal
277
+
278
+ /** Harness with the session capability installed on the auth context. */
279
+ function createSessionApp(
280
+ options: {
281
+ policy?: Auth.require.Policy | undefined
282
+ resolve?: Auth.Session['resolve'] | undefined
283
+ } = {},
284
+ ) {
285
+ const auth = {
286
+ apiKey: { resolve: TestApp.resolver({ keys: [{ ...key, token }] }) },
287
+ rateLimit: RateLimit.memory(),
288
+ ...(options.resolve ? { session: { resolve: options.resolve } } : {}),
289
+ }
290
+ const app = new Hono<Auth.Environment>()
291
+ app.use('*', async (c, next) => {
292
+ c.set('auth', auth)
293
+ await next()
294
+ })
295
+ app.use('/protected', Auth.require(options.policy ?? { session: true }))
296
+ app.get('/protected', (c) => c.json(serializePrincipal(Auth.getPrincipal(c))))
297
+ return app
298
+ }
299
+
300
+ test('resolves cookie sessions for session-lane routes', async () => {
301
+ const app = createSessionApp({ resolve: async () => sessionPrincipal })
302
+ const response = await app.request('/protected')
303
+ const body = await response.json()
304
+
305
+ expect(response.status).toMatchInlineSnapshot(`200`)
306
+ expect(body).toMatchInlineSnapshot(`
307
+ {
308
+ "id": "usr_test",
309
+ "type": "session",
310
+ }
311
+ `)
312
+ })
313
+
314
+ test('falls through to the session lane on invalid bearer tokens', async () => {
315
+ const app = createSessionApp({ resolve: async () => sessionPrincipal })
316
+ const response = await app.request('/protected', {
317
+ headers: { authorization: 'Bearer not-an-api-key' },
318
+ })
319
+
320
+ expect(response.status).toMatchInlineSnapshot(`200`)
321
+ })
322
+
323
+ test('rejects anonymous requests without a session', async () => {
324
+ const app = createSessionApp({ resolve: async () => null })
325
+ const response = await app.request('/protected')
326
+
327
+ expect(response.status).toMatchInlineSnapshot(`401`)
328
+ })
329
+
330
+ test('stays closed without the app session capability', async () => {
331
+ const app = createSessionApp()
332
+ const response = await app.request('/protected')
333
+
334
+ expect(response.status).toMatchInlineSnapshot(`401`)
335
+ })
336
+
337
+ test('valid API keys win over sessions', async () => {
338
+ const app = createSessionApp({
339
+ policy: { apiKey: { scopes: ['data:read'] }, session: true },
340
+ resolve: async () => sessionPrincipal,
341
+ })
342
+ const response = await app.request('/protected', {
343
+ headers: { 'tempo-api-key': token },
344
+ })
345
+ const body = await response.json()
346
+
347
+ expect(response.status).toMatchInlineSnapshot(`200`)
348
+ expect(body).toMatchInlineSnapshot(`
349
+ {
350
+ "id": "key_test",
351
+ "orgId": "org_test",
352
+ "type": "api_key",
353
+ }
354
+ `)
355
+ })
356
+
357
+ test('is not consulted on lane-less routes', async () => {
358
+ const app = createSessionApp({
359
+ policy: {},
360
+ resolve: async () => sessionPrincipal,
361
+ })
362
+ const response = await app.request('/protected')
363
+
364
+ expect(response.status).toMatchInlineSnapshot(`401`)
365
+ })
366
+ })
367
+
368
+ describe('superAdmin', () => {
369
+ const secret = 'tempo:sk:a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0'
370
+
371
+ /** Auth context with the super admin secret configured alongside the test key. */
372
+ function withSuperAdmin(): Auth.Context {
373
+ return {
374
+ apiKey: { resolve: TestApp.resolver({ keys: [{ ...key, token }] }) },
375
+ rateLimit: RateLimit.memory(),
376
+ superAdmin: { tokenHash: ApiKey.hash(secret) },
377
+ }
378
+ }
379
+
380
+ test('resolves the super admin secret before key lookup', async () => {
381
+ const app = createApp({ auth: withSuperAdmin() })
382
+ const response = await app.request('/protected', {
383
+ headers: { 'tempo-api-key': secret },
384
+ })
385
+ const body = await response.json()
386
+
387
+ expect(response.status).toMatchInlineSnapshot(`200`)
388
+ expect(body).toMatchInlineSnapshot(`
389
+ {
390
+ "actor": "super_admin",
391
+ "id": "super_admin",
392
+ "type": "super_admin",
393
+ }
394
+ `)
395
+ })
396
+
397
+ test('accepts the super admin secret as a bearer token', async () => {
398
+ const app = createApp({ auth: withSuperAdmin() })
399
+ const response = await app.request('/protected', {
400
+ headers: { authorization: `Bearer ${secret}` },
401
+ })
402
+
403
+ expect(response.status).toMatchInlineSnapshot(`200`)
404
+ })
405
+
406
+ test('bypasses scope checks', async () => {
407
+ const app = createApp({
408
+ auth: withSuperAdmin(),
409
+ policy: { apiKey: { scopes: [Scope.wildcard] } },
410
+ })
411
+ const response = await app.request('/protected', {
412
+ headers: { 'tempo-api-key': secret },
413
+ })
414
+
415
+ expect(response.status).toMatchInlineSnapshot(`200`)
416
+ })
417
+
418
+ test('non-matching tokens fall through to key lookup', async () => {
419
+ const app = createApp({ auth: withSuperAdmin() })
420
+ const response = await app.request('/protected', {
421
+ headers: { 'tempo-api-key': token },
422
+ })
423
+ const body = await response.json()
424
+
425
+ expect(response.status).toMatchInlineSnapshot(`200`)
426
+ expect(body).toMatchInlineSnapshot(`
427
+ {
428
+ "id": "key_test",
429
+ "orgId": "org_test",
430
+ "type": "api_key",
431
+ }
432
+ `)
433
+ })
434
+
435
+ test('is disabled when unconfigured', async () => {
436
+ const app = createApp()
437
+ const response = await app.request('/protected', {
438
+ headers: { 'tempo-api-key': secret },
439
+ })
440
+ const body = await response.json()
441
+
442
+ expect(response.status).toMatchInlineSnapshot(`401`)
443
+ expect(body).toMatchInlineSnapshot(`
444
+ {
445
+ "error": {
446
+ "code": "api_key_invalid",
447
+ "message": "Invalid API key",
448
+ },
449
+ }
450
+ `)
451
+ })
452
+
453
+ test('is the only principal for lane-less routes', async () => {
454
+ const app = createApp({ auth: withSuperAdmin(), policy: {} })
455
+
456
+ const superAdmin = await app.request('/protected', {
457
+ headers: { 'tempo-api-key': secret },
458
+ })
459
+ expect(superAdmin.status).toMatchInlineSnapshot(`200`)
460
+
461
+ const keyed = await app.request('/protected', {
462
+ headers: { 'tempo-api-key': token },
463
+ })
464
+ const body = await keyed.json()
465
+ expect(keyed.status).toMatchInlineSnapshot(`403`)
466
+ expect(body).toMatchInlineSnapshot(`
467
+ {
468
+ "error": {
469
+ "code": "forbidden",
470
+ "message": "API key not permitted for this route",
471
+ },
472
+ }
473
+ `)
474
+
475
+ const anonymous = await app.request('/protected')
476
+ expect(anonymous.status).toMatchInlineSnapshot(`401`)
477
+ })
478
+
479
+ test('logs the principal actor, never the secret', async () => {
480
+ const entries: Log.Entry[] = []
481
+ const app = createApp({
482
+ auth: withSuperAdmin(),
483
+ logger: (entry) => {
484
+ entries.push(entry)
485
+ },
486
+ })
487
+ await app.request('/protected', { headers: { 'tempo-api-key': secret } })
488
+
489
+ expect(entries[0]?.principal).toMatchInlineSnapshot(`
490
+ {
491
+ "actor": "super_admin",
492
+ "id": "super_admin",
493
+ "type": "super_admin",
494
+ }
495
+ `)
496
+ expect(JSON.stringify(entries)).not.toContain(secret)
497
+ })
498
+ })
499
+
270
500
  describe('environment', () => {
271
501
  const sandboxToken = 'secret_sandbox'
272
502
 
273
- function createChainApp(options: { public?: boolean } = {}) {
503
+ function createChainApp(
504
+ options: {
505
+ public?: boolean
506
+ publicRateLimit?: RateLimit.Limit
507
+ rateLimit?: RateLimit.Store
508
+ sandboxBillingActive?: boolean
509
+ mpp?: Auth.Context['mpp']
510
+ } = {},
511
+ ) {
274
512
  const auth: Auth.Context = {
275
513
  apiKey: {
276
514
  resolve: TestApp.resolver({
@@ -282,27 +520,33 @@ describe('require', () => {
282
520
  orgId: 'org_test',
283
521
  scopes: [Scope.wildcard],
284
522
  token: sandboxToken,
523
+ ...(options.sandboxBillingActive === undefined
524
+ ? {}
525
+ : { billingActive: options.sandboxBillingActive }),
285
526
  },
286
527
  ],
287
528
  }),
288
529
  },
289
- rateLimit: RateLimit.memory(),
530
+ rateLimit: options.rateLimit ?? RateLimit.memory(),
531
+ ...(options.publicRateLimit ? { publicRateLimit: options.publicRateLimit } : {}),
532
+ ...(options.mpp ? { mpp: options.mpp } : {}),
290
533
  }
291
534
  const policy: Auth.require.Policy = {
292
535
  apiKey: { scopes: [Scope.wildcard] },
293
536
  ...(options.public === false ? {} : { public: { rateLimit: { perMinute: 60 } } }),
537
+ ...(options.mpp ? { mpp: { charge: { amount: '1' } } } : {}),
294
538
  }
295
539
  const app = new Hono<Auth.Environment>()
296
540
  app.use('*', async (c, next) => {
297
541
  c.set('auth', auth)
298
- // Mirror the app default so sandbox steering toward testnet is observable.
542
+ // Mirror the app default (mainnet) so the effective chain is observable.
299
543
  c.set('chainId' as never, Viem.defaultChainId as never)
300
544
  await next()
301
545
  })
302
546
  app.use('/protected', Auth.require(policy))
303
547
  app.get('/protected', (c) => {
304
548
  // Mirror how real handlers resolve the effective chain: explicit query
305
- // wins, else the context default (which auth may steer to testnet).
549
+ // wins, else the context default. Auth no longer steers this value.
306
550
  const raw = c.req.query('chainId')
307
551
  const chainId = raw ? Number(raw) : ((c.get('chainId' as never) as number | null) ?? null)
308
552
  return c.json({ chainId, principal: Auth.getPrincipal(c)?.type ?? null })
@@ -310,45 +554,39 @@ describe('require', () => {
310
554
  return app
311
555
  }
312
556
 
313
- test('falls through to the public policy for a sandbox key on explicit mainnet', async () => {
557
+ test('rejects a sandbox key on explicit mainnet', async () => {
314
558
  const app = createChainApp()
315
559
  const response = await app.request('/protected?chainId=4217', {
316
560
  headers: { 'tempo-api-key': sandboxToken },
317
561
  })
318
- expect(response.status).toMatchInlineSnapshot(`200`)
319
- // Treated as anonymous (public) rather than rejected; chain stays mainnet.
562
+ expect(response.status).toMatchInlineSnapshot(`403`)
320
563
  expect(await response.json()).toMatchInlineSnapshot(`
321
564
  {
322
- "chainId": 4217,
323
- "principal": "public",
565
+ "error": {
566
+ "code": "api_key_forbidden",
567
+ "message": "Sandbox API keys only support testnet. Pass a testnet \`chainId\`.",
568
+ },
324
569
  }
325
570
  `)
326
571
  })
327
572
 
328
- test('falls through to the public policy for a sandbox key on the mainnet alias', async () => {
573
+ test('rejects a sandbox key on the mainnet alias', async () => {
329
574
  const app = createChainApp()
330
575
  const response = await app.request('/protected?chainId=mainnet', {
331
576
  headers: { 'tempo-api-key': sandboxToken },
332
577
  })
333
- expect(response.status).toMatchInlineSnapshot(`200`)
334
- expect((await response.json()).principal).toMatchInlineSnapshot(`"public"`)
578
+ expect(response.status).toMatchInlineSnapshot(`403`)
579
+ expect((await response.json()).error.code).toMatchInlineSnapshot(`"api_key_forbidden"`)
335
580
  })
336
581
 
337
- test('a sandbox key on mainnet with no public lane is rejected as missing', async () => {
582
+ test('rejects a sandbox key on explicit mainnet even with no public lane', async () => {
338
583
  const app = createChainApp({ public: false })
339
584
  const response = await app.request('/protected?chainId=4217', {
340
585
  headers: { 'tempo-api-key': sandboxToken },
341
586
  })
342
- // With no public lane, the public policy closes to "missing API key".
343
- expect(response.status).toMatchInlineSnapshot(`401`)
344
- expect(await response.json()).toMatchInlineSnapshot(`
345
- {
346
- "error": {
347
- "code": "api_key_missing",
348
- "message": "Missing API key",
349
- },
350
- }
351
- `)
587
+ // A resolved-but-forbidden key is a 403, not anonymous fall-through.
588
+ expect(response.status).toMatchInlineSnapshot(`403`)
589
+ expect((await response.json()).error.code).toMatchInlineSnapshot(`"api_key_forbidden"`)
352
590
  })
353
591
 
354
592
  test('uses a sandbox key on an explicit non-mainnet chain', async () => {
@@ -365,15 +603,17 @@ describe('require', () => {
365
603
  `)
366
604
  })
367
605
 
368
- test('defaults a sandbox key with no chain to testnet', async () => {
606
+ test('authenticates a sandbox key with no query chain without steering', async () => {
369
607
  const app = createChainApp()
370
608
  const response = await app.request('/protected', {
371
609
  headers: { 'tempo-api-key': sandboxToken },
372
610
  })
611
+ // Auth no longer mutates `chainId`; the mainnet default is left for the
612
+ // data group's own sandbox chain guard to reject.
373
613
  expect(response.status).toMatchInlineSnapshot(`200`)
374
614
  expect(await response.json()).toMatchInlineSnapshot(`
375
615
  {
376
- "chainId": 42431,
616
+ "chainId": 4217,
377
617
  "principal": "api_key",
378
618
  }
379
619
  `)
@@ -392,6 +632,85 @@ describe('require', () => {
392
632
  }
393
633
  `)
394
634
  })
635
+
636
+ test('throttles a sandbox key to the public quota when billing is inactive', async () => {
637
+ const app = createChainApp({
638
+ public: true, // policy.public.rateLimit is { perMinute: 60 }
639
+ sandboxBillingActive: false,
640
+ })
641
+ const response = await app.request('/protected?chainId=42431', {
642
+ headers: { 'tempo-api-key': sandboxToken },
643
+ })
644
+ expect(response.status).toBe(200)
645
+ // The public ceiling (60), not the API-key default (100).
646
+ expect(response.headers.get('RateLimit-Limit')).toMatchInlineSnapshot(`"60"`)
647
+ })
648
+
649
+ test('grants a sandbox key the full API-key quota when billing is active', async () => {
650
+ const app = createChainApp({
651
+ public: true,
652
+ sandboxBillingActive: true,
653
+ })
654
+ const response = await app.request('/protected?chainId=42431', {
655
+ headers: { 'tempo-api-key': sandboxToken },
656
+ })
657
+ expect(response.status).toBe(200)
658
+ // The API-key default (100), not the public ceiling (60).
659
+ expect(response.headers.get('RateLimit-Limit')).toMatchInlineSnapshot(`"100"`)
660
+ })
661
+
662
+ test('throttles a sandbox key to the public quota when billing is unknown', async () => {
663
+ // A sandbox key minted before billing was ever active carries no
664
+ // `billingActive` flag; it falls back to the public quota.
665
+ const app = createChainApp({ public: true })
666
+ const response = await app.request('/protected?chainId=42431', {
667
+ headers: { 'tempo-api-key': sandboxToken },
668
+ })
669
+ expect(response.status).toBe(200)
670
+ expect(response.headers.get('RateLimit-Limit')).toMatchInlineSnapshot(`"60"`)
671
+ })
672
+
673
+ test('throttles to the configured public default on API-key-only routes', async () => {
674
+ const app = createChainApp({
675
+ public: false, // no per-route public lane
676
+ publicRateLimit: { perMinute: 7 },
677
+ sandboxBillingActive: false,
678
+ })
679
+ const response = await app.request('/protected?chainId=42431', {
680
+ headers: { 'tempo-api-key': sandboxToken },
681
+ })
682
+ expect(response.status).toBe(200)
683
+ expect(response.headers.get('RateLimit-Limit')).toMatchInlineSnapshot(`"7"`)
684
+ })
685
+
686
+ test('returns 429 (never a payment challenge) for a throttled sandbox key over quota', async () => {
687
+ const app = createChainApp({
688
+ mpp: createMppServer(),
689
+ rateLimit: overQuota(),
690
+ sandboxBillingActive: false,
691
+ })
692
+ const response = await app.request('/protected?chainId=42431', {
693
+ headers: { 'tempo-api-key': sandboxToken },
694
+ })
695
+ // A throttled sandbox key must activate billing, not pay per request, so
696
+ // the over-quota response is a plain 429 rather than a 402 MPP challenge.
697
+ expect(response.status).toMatchInlineSnapshot(`429`)
698
+ expect((await response.json()).error.code).toMatchInlineSnapshot(`"rate_limit_exceeded"`)
699
+ })
700
+
701
+ test('ignores a payment credential for a throttled sandbox key under quota', async () => {
702
+ const app = createChainApp({
703
+ mpp: createMppServer(),
704
+ sandboxBillingActive: false,
705
+ })
706
+ const response = await app.request('/protected?chainId=42431', {
707
+ headers: { 'tempo-api-key': sandboxToken, authorization: 'Payment paid' },
708
+ })
709
+ // The payment lane can't buy past the throttle; the request runs on the
710
+ // public quota instead of being charged.
711
+ expect(response.status).toBe(200)
712
+ expect(response.headers.get('RateLimit-Limit')).toMatchInlineSnapshot(`"60"`)
713
+ })
395
714
  })
396
715
 
397
716
  describe('mpp', () => {
@@ -613,6 +932,144 @@ describe('require', () => {
613
932
  error.mockRestore()
614
933
  }
615
934
  })
935
+
936
+ test('fails open (at warn) when the rate-limit store stalls past the timeout', async () => {
937
+ const error = vi.spyOn(console, 'error').mockImplementation(() => {})
938
+ const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})
939
+ vi.useFakeTimers()
940
+ try {
941
+ const app = createApp({
942
+ auth: {
943
+ apiKey: { resolve: TestApp.resolver({ keys: [{ ...key, token }] }) },
944
+ // A hot Durable Object under a storage-latency blip never returns;
945
+ // the consume must not block the request indefinitely.
946
+ rateLimit: { consume: () => new Promise<never>(() => {}) },
947
+ },
948
+ policy: { public: { rateLimit: { perMinute: 1 } } },
949
+ })
950
+ const pending = app.request('/protected', {
951
+ headers: { 'x-forwarded-for': '203.0.113.10' },
952
+ })
953
+ // Advance past the 300ms consume timeout so it fails open.
954
+ await vi.advanceTimersByTimeAsync(300)
955
+ const response = await pending
956
+ const body = await response.json()
957
+
958
+ expect(response.status).toMatchInlineSnapshot(`200`)
959
+ expect(response.headers.has('ratelimit-limit')).toMatchInlineSnapshot(`false`)
960
+ expect(body).toMatchInlineSnapshot(`
961
+ {
962
+ "id": "203.0.113.10",
963
+ "type": "public",
964
+ }
965
+ `)
966
+ // A timeout is an expected degradation: warn, never error.
967
+ expect(warn.mock.calls.length).toMatchInlineSnapshot(`1`)
968
+ expect(error.mock.calls.length).toMatchInlineSnapshot(`0`)
969
+ } finally {
970
+ vi.useRealTimers()
971
+ warn.mockRestore()
972
+ error.mockRestore()
973
+ }
974
+ })
975
+
976
+ test('fails open (at warn) when the rate-limit Durable Object is overloaded', async () => {
977
+ const error = vi.spyOn(console, 'error').mockImplementation(() => {})
978
+ const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})
979
+ try {
980
+ const app = createApp({
981
+ auth: {
982
+ apiKey: { resolve: TestApp.resolver({ keys: [{ ...key, token }] }) },
983
+ rateLimit: {
984
+ async consume() {
985
+ throw Object.assign(
986
+ new Error('Durable Object is overloaded. Requests queued for too long.'),
987
+ { overloaded: true },
988
+ )
989
+ },
990
+ },
991
+ },
992
+ policy: { public: { rateLimit: { perMinute: 1 } } },
993
+ })
994
+ const response = await app.request('/protected', {
995
+ headers: { 'x-forwarded-for': '203.0.113.10' },
996
+ })
997
+ const body = await response.json()
998
+
999
+ expect(response.status).toMatchInlineSnapshot(`200`)
1000
+ expect(response.headers.has('ratelimit-limit')).toMatchInlineSnapshot(`false`)
1001
+ expect(body).toMatchInlineSnapshot(`
1002
+ {
1003
+ "id": "203.0.113.10",
1004
+ "type": "public",
1005
+ }
1006
+ `)
1007
+ expect(warn.mock.calls.length).toMatchInlineSnapshot(`1`)
1008
+ expect(error.mock.calls.length).toMatchInlineSnapshot(`0`)
1009
+ } finally {
1010
+ warn.mockRestore()
1011
+ error.mockRestore()
1012
+ }
1013
+ })
1014
+
1015
+ test('fails open (at warn) when the rate-limit Durable Object errors retryably', async () => {
1016
+ const error = vi.spyOn(console, 'error').mockImplementation(() => {})
1017
+ const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})
1018
+ try {
1019
+ const app = createApp({
1020
+ auth: {
1021
+ apiKey: { resolve: TestApp.resolver({ keys: [{ ...key, token }] }) },
1022
+ rateLimit: {
1023
+ async consume() {
1024
+ // A transient transport fault whose single retry also failed.
1025
+ throw Object.assign(new Error('Network connection lost.'), { retryable: true })
1026
+ },
1027
+ },
1028
+ },
1029
+ policy: { public: { rateLimit: { perMinute: 1 } } },
1030
+ })
1031
+ const response = await app.request('/protected', {
1032
+ headers: { 'x-forwarded-for': '203.0.113.10' },
1033
+ })
1034
+
1035
+ expect(response.status).toMatchInlineSnapshot(`200`)
1036
+ expect(response.headers.has('ratelimit-limit')).toMatchInlineSnapshot(`false`)
1037
+ expect(warn.mock.calls.length).toMatchInlineSnapshot(`1`)
1038
+ expect(error.mock.calls.length).toMatchInlineSnapshot(`0`)
1039
+ } finally {
1040
+ warn.mockRestore()
1041
+ error.mockRestore()
1042
+ }
1043
+ })
1044
+
1045
+ test('fails open (at warn) on a redacted Durable Object internal error', async () => {
1046
+ const error = vi.spyOn(console, 'error').mockImplementation(() => {})
1047
+ const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})
1048
+ try {
1049
+ const app = createApp({
1050
+ auth: {
1051
+ apiKey: { resolve: TestApp.resolver({ keys: [{ ...key, token }] }) },
1052
+ rateLimit: {
1053
+ async consume() {
1054
+ throw new Error(`internal error; reference = ${'ab12'.repeat(6)}`)
1055
+ },
1056
+ },
1057
+ },
1058
+ policy: { public: { rateLimit: { perMinute: 1 } } },
1059
+ })
1060
+ const response = await app.request('/protected', {
1061
+ headers: { 'x-forwarded-for': '203.0.113.10' },
1062
+ })
1063
+
1064
+ expect(response.status).toMatchInlineSnapshot(`200`)
1065
+ expect(response.headers.has('ratelimit-limit')).toMatchInlineSnapshot(`false`)
1066
+ expect(warn.mock.calls.length).toMatchInlineSnapshot(`1`)
1067
+ expect(error.mock.calls.length).toMatchInlineSnapshot(`0`)
1068
+ } finally {
1069
+ warn.mockRestore()
1070
+ error.mockRestore()
1071
+ }
1072
+ })
616
1073
  })
617
1074
 
618
1075
  describe('rateLimit per-scope', () => {
@@ -805,6 +1262,7 @@ describe('require', () => {
805
1262
  expect(response.headers.get('ratelimit-limit')).toMatchInlineSnapshot(`"1"`)
806
1263
  expect(entries[0]?.principal).toMatchInlineSnapshot(`
807
1264
  {
1265
+ "billingActive": false,
808
1266
  "environment": "production",
809
1267
  "id": "key_test",
810
1268
  "orgId": "org_test",
@@ -948,6 +1406,14 @@ function serializePrincipal(principal: Auth.Principal | null) {
948
1406
  type: principal.type,
949
1407
  }
950
1408
 
1409
+ if (principal.type === 'super_admin')
1410
+ return {
1411
+ actor: principal.actor,
1412
+ id: principal.id,
1413
+ ...payment,
1414
+ type: principal.type,
1415
+ }
1416
+
951
1417
  return {
952
1418
  id: principal.id,
953
1419
  ...payment,