tapimo 0.0.0 → 0.0.2

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 (422) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +119 -0
  3. package/dist/ApiKey.d.ts +65 -0
  4. package/dist/ApiKey.d.ts.map +1 -0
  5. package/dist/ApiKey.js +105 -0
  6. package/dist/ApiKey.js.map +1 -0
  7. package/dist/ApiKeySource.d.ts +166 -0
  8. package/dist/ApiKeySource.d.ts.map +1 -0
  9. package/dist/ApiKeySource.js +234 -0
  10. package/dist/ApiKeySource.js.map +1 -0
  11. package/dist/App.d.ts +19317 -0
  12. package/dist/App.d.ts.map +1 -0
  13. package/dist/App.js +612 -0
  14. package/dist/App.js.map +1 -0
  15. package/dist/Assets.d.ts +44 -0
  16. package/dist/Assets.d.ts.map +1 -0
  17. package/dist/Assets.js +23 -0
  18. package/dist/Assets.js.map +1 -0
  19. package/dist/Auth.d.ts +2 -0
  20. package/dist/Auth.d.ts.map +1 -0
  21. package/dist/Auth.js +2 -0
  22. package/dist/Auth.js.map +1 -0
  23. package/dist/Cli.d.ts +21 -0
  24. package/dist/Cli.d.ts.map +1 -0
  25. package/dist/Cli.js +13 -0
  26. package/dist/Cli.js.map +1 -0
  27. package/dist/Client.d.ts +20 -0
  28. package/dist/Client.d.ts.map +1 -0
  29. package/dist/Client.js +24 -0
  30. package/dist/Client.js.map +1 -0
  31. package/dist/Metrics.d.ts +70 -0
  32. package/dist/Metrics.d.ts.map +1 -0
  33. package/dist/Metrics.js +91 -0
  34. package/dist/Metrics.js.map +1 -0
  35. package/dist/Scope.d.ts +35 -0
  36. package/dist/Scope.d.ts.map +1 -0
  37. package/dist/Scope.js +38 -0
  38. package/dist/Scope.js.map +1 -0
  39. package/dist/Webhooks.d.ts +79 -0
  40. package/dist/Webhooks.d.ts.map +1 -0
  41. package/dist/Webhooks.js +107 -0
  42. package/dist/Webhooks.js.map +1 -0
  43. package/dist/admin/Access.d.ts +41 -0
  44. package/dist/admin/Access.d.ts.map +1 -0
  45. package/dist/admin/Access.js +75 -0
  46. package/dist/admin/Access.js.map +1 -0
  47. package/dist/admin/App.d.ts +1221 -0
  48. package/dist/admin/App.d.ts.map +1 -0
  49. package/dist/admin/App.js +113 -0
  50. package/dist/admin/App.js.map +1 -0
  51. package/dist/admin/apps/api-keys.d.ts +209 -0
  52. package/dist/admin/apps/api-keys.d.ts.map +1 -0
  53. package/dist/admin/apps/api-keys.js +129 -0
  54. package/dist/admin/apps/api-keys.js.map +1 -0
  55. package/dist/admin/apps/scopes.d.ts +46 -0
  56. package/dist/admin/apps/scopes.d.ts.map +1 -0
  57. package/dist/admin/apps/scopes.js +41 -0
  58. package/dist/admin/apps/scopes.js.map +1 -0
  59. package/dist/admin/apps/verified-tokens.d.ts +1206 -0
  60. package/dist/admin/apps/verified-tokens.d.ts.map +1 -0
  61. package/dist/admin/apps/verified-tokens.js +485 -0
  62. package/dist/admin/apps/verified-tokens.js.map +1 -0
  63. package/dist/admin/index.d.ts +3 -0
  64. package/dist/admin/index.d.ts.map +1 -0
  65. package/dist/admin/index.js +3 -0
  66. package/dist/admin/index.js.map +1 -0
  67. package/dist/admin/ui.generated.d.ts +11 -0
  68. package/dist/admin/ui.generated.d.ts.map +1 -0
  69. package/dist/admin/ui.generated.js +43 -0
  70. package/dist/admin/ui.generated.js.map +1 -0
  71. package/dist/apps/activities.d.ts +18725 -0
  72. package/dist/apps/activities.d.ts.map +1 -0
  73. package/dist/apps/activities.js +3138 -0
  74. package/dist/apps/activities.js.map +1 -0
  75. package/dist/apps/addresses.d.ts +3662 -0
  76. package/dist/apps/addresses.d.ts.map +1 -0
  77. package/dist/apps/addresses.js +22 -0
  78. package/dist/apps/addresses.js.map +1 -0
  79. package/dist/apps/balances.d.ts +342 -0
  80. package/dist/apps/balances.d.ts.map +1 -0
  81. package/dist/apps/balances.js +393 -0
  82. package/dist/apps/balances.js.map +1 -0
  83. package/dist/apps/blocks.d.ts +931 -0
  84. package/dist/apps/blocks.d.ts.map +1 -0
  85. package/dist/apps/blocks.js +689 -0
  86. package/dist/apps/blocks.js.map +1 -0
  87. package/dist/apps/coingecko.d.ts +1053 -0
  88. package/dist/apps/coingecko.d.ts.map +1 -0
  89. package/dist/apps/coingecko.js +1015 -0
  90. package/dist/apps/coingecko.js.map +1 -0
  91. package/dist/apps/exchanges.d.ts +3000 -0
  92. package/dist/apps/exchanges.d.ts.map +1 -0
  93. package/dist/apps/exchanges.js +3570 -0
  94. package/dist/apps/exchanges.js.map +1 -0
  95. package/dist/apps/fee-amm.d.ts +640 -0
  96. package/dist/apps/fee-amm.d.ts.map +1 -0
  97. package/dist/apps/fee-amm.js +815 -0
  98. package/dist/apps/fee-amm.js.map +1 -0
  99. package/dist/apps/indexer.d.ts +116 -0
  100. package/dist/apps/indexer.d.ts.map +1 -0
  101. package/dist/apps/indexer.js +270 -0
  102. package/dist/apps/indexer.js.map +1 -0
  103. package/dist/apps/receipts.d.ts +1438 -0
  104. package/dist/apps/receipts.d.ts.map +1 -0
  105. package/dist/apps/receipts.js +874 -0
  106. package/dist/apps/receipts.js.map +1 -0
  107. package/dist/apps/rpc.d.ts +173 -0
  108. package/dist/apps/rpc.d.ts.map +1 -0
  109. package/dist/apps/rpc.js +191 -0
  110. package/dist/apps/rpc.js.map +1 -0
  111. package/dist/apps/tokenlist.d.ts +192 -0
  112. package/dist/apps/tokenlist.d.ts.map +1 -0
  113. package/dist/apps/tokenlist.js +153 -0
  114. package/dist/apps/tokenlist.js.map +1 -0
  115. package/dist/apps/tokens.d.ts +2246 -0
  116. package/dist/apps/tokens.d.ts.map +1 -0
  117. package/dist/apps/tokens.js +1789 -0
  118. package/dist/apps/tokens.js.map +1 -0
  119. package/dist/apps/transactions.d.ts +2327 -0
  120. package/dist/apps/transactions.d.ts.map +1 -0
  121. package/dist/apps/transactions.js +975 -0
  122. package/dist/apps/transactions.js.map +1 -0
  123. package/dist/apps/transfers.d.ts +589 -0
  124. package/dist/apps/transfers.d.ts.map +1 -0
  125. package/dist/apps/transfers.js +769 -0
  126. package/dist/apps/transfers.js.map +1 -0
  127. package/dist/apps/verified-tokens.d.ts +544 -0
  128. package/dist/apps/verified-tokens.d.ts.map +1 -0
  129. package/dist/apps/verified-tokens.js +234 -0
  130. package/dist/apps/verified-tokens.js.map +1 -0
  131. package/dist/apps/webhooks.d.ts +7684 -0
  132. package/dist/apps/webhooks.d.ts.map +1 -0
  133. package/dist/apps/webhooks.js +2112 -0
  134. package/dist/apps/webhooks.js.map +1 -0
  135. package/dist/cloudflare.d.ts +32 -0
  136. package/dist/cloudflare.d.ts.map +1 -0
  137. package/dist/cloudflare.js +45 -0
  138. package/dist/cloudflare.js.map +1 -0
  139. package/dist/index.d.ts +16 -0
  140. package/dist/index.d.ts.map +1 -0
  141. package/dist/index.js +16 -0
  142. package/dist/index.js.map +1 -0
  143. package/dist/internal/Auth.d.ts +298 -0
  144. package/dist/internal/Auth.d.ts.map +1 -0
  145. package/dist/internal/Auth.js +618 -0
  146. package/dist/internal/Auth.js.map +1 -0
  147. package/dist/internal/Cache.d.ts +74 -0
  148. package/dist/internal/Cache.d.ts.map +1 -0
  149. package/dist/internal/Cache.js +215 -0
  150. package/dist/internal/Cache.js.map +1 -0
  151. package/dist/internal/Cursor.d.ts +79 -0
  152. package/dist/internal/Cursor.d.ts.map +1 -0
  153. package/dist/internal/Cursor.js +116 -0
  154. package/dist/internal/Cursor.js.map +1 -0
  155. package/dist/internal/EdgeCache.d.ts +59 -0
  156. package/dist/internal/EdgeCache.d.ts.map +1 -0
  157. package/dist/internal/EdgeCache.js +174 -0
  158. package/dist/internal/EdgeCache.js.map +1 -0
  159. package/dist/internal/Log.d.ts +104 -0
  160. package/dist/internal/Log.d.ts.map +1 -0
  161. package/dist/internal/Log.js +118 -0
  162. package/dist/internal/Log.js.map +1 -0
  163. package/dist/internal/MetricSink.d.ts +65 -0
  164. package/dist/internal/MetricSink.d.ts.map +1 -0
  165. package/dist/internal/MetricSink.js +146 -0
  166. package/dist/internal/MetricSink.js.map +1 -0
  167. package/dist/internal/Mpp.d.ts +71 -0
  168. package/dist/internal/Mpp.d.ts.map +1 -0
  169. package/dist/internal/Mpp.js +105 -0
  170. package/dist/internal/Mpp.js.map +1 -0
  171. package/dist/internal/OpenApi.d.ts +313 -0
  172. package/dist/internal/OpenApi.d.ts.map +1 -0
  173. package/dist/internal/OpenApi.fonts.d.ts +2 -0
  174. package/dist/internal/OpenApi.fonts.d.ts.map +1 -0
  175. package/dist/internal/OpenApi.fonts.js +39 -0
  176. package/dist/internal/OpenApi.fonts.js.map +1 -0
  177. package/dist/internal/OpenApi.js +335 -0
  178. package/dist/internal/OpenApi.js.map +1 -0
  179. package/dist/internal/OpenApi.logo.d.ts +6 -0
  180. package/dist/internal/OpenApi.logo.d.ts.map +1 -0
  181. package/dist/internal/OpenApi.logo.js +19 -0
  182. package/dist/internal/OpenApi.logo.js.map +1 -0
  183. package/dist/internal/Path.d.ts +5 -0
  184. package/dist/internal/Path.d.ts.map +1 -0
  185. package/dist/internal/Path.js +16 -0
  186. package/dist/internal/Path.js.map +1 -0
  187. package/dist/internal/RateLimit.d.ts +60 -0
  188. package/dist/internal/RateLimit.d.ts.map +1 -0
  189. package/dist/internal/RateLimit.js +48 -0
  190. package/dist/internal/RateLimit.js.map +1 -0
  191. package/dist/internal/Response.d.ts +132 -0
  192. package/dist/internal/Response.d.ts.map +1 -0
  193. package/dist/internal/Response.js +72 -0
  194. package/dist/internal/Response.js.map +1 -0
  195. package/dist/internal/Schema.d.ts +257 -0
  196. package/dist/internal/Schema.d.ts.map +1 -0
  197. package/dist/internal/Schema.js +360 -0
  198. package/dist/internal/Schema.js.map +1 -0
  199. package/dist/internal/Store.d.ts +204 -0
  200. package/dist/internal/Store.d.ts.map +1 -0
  201. package/dist/internal/Store.js +291 -0
  202. package/dist/internal/Store.js.map +1 -0
  203. package/dist/internal/Tidx.d.ts +120 -0
  204. package/dist/internal/Tidx.d.ts.map +1 -0
  205. package/dist/internal/Tidx.js +156 -0
  206. package/dist/internal/Tidx.js.map +1 -0
  207. package/dist/internal/Timing.d.ts +22 -0
  208. package/dist/internal/Timing.d.ts.map +1 -0
  209. package/dist/internal/Timing.js +32 -0
  210. package/dist/internal/Timing.js.map +1 -0
  211. package/dist/internal/Ttl.d.ts +25 -0
  212. package/dist/internal/Ttl.d.ts.map +1 -0
  213. package/dist/internal/Ttl.js +49 -0
  214. package/dist/internal/Ttl.js.map +1 -0
  215. package/dist/internal/Value.d.ts +47 -0
  216. package/dist/internal/Value.d.ts.map +1 -0
  217. package/dist/internal/Value.js +122 -0
  218. package/dist/internal/Value.js.map +1 -0
  219. package/dist/internal/VerifiedTokens.d.ts +228 -0
  220. package/dist/internal/VerifiedTokens.d.ts.map +1 -0
  221. package/dist/internal/VerifiedTokens.js +450 -0
  222. package/dist/internal/VerifiedTokens.js.map +1 -0
  223. package/dist/internal/Viem.d.ts +2082 -0
  224. package/dist/internal/Viem.d.ts.map +1 -0
  225. package/dist/internal/Viem.js +63 -0
  226. package/dist/internal/Viem.js.map +1 -0
  227. package/dist/internal/WebhookDestination.d.ts +198 -0
  228. package/dist/internal/WebhookDestination.d.ts.map +1 -0
  229. package/dist/internal/WebhookDestination.js +170 -0
  230. package/dist/internal/WebhookDestination.js.map +1 -0
  231. package/dist/internal/Webhooks.d.ts +433 -0
  232. package/dist/internal/Webhooks.d.ts.map +1 -0
  233. package/dist/internal/Webhooks.js +489 -0
  234. package/dist/internal/Webhooks.js.map +1 -0
  235. package/dist/internal/webhookDestinations/betterStack.d.ts +26 -0
  236. package/dist/internal/webhookDestinations/betterStack.d.ts.map +1 -0
  237. package/dist/internal/webhookDestinations/betterStack.js +76 -0
  238. package/dist/internal/webhookDestinations/betterStack.js.map +1 -0
  239. package/dist/internal/webhookDestinations/slack.d.ts +59 -0
  240. package/dist/internal/webhookDestinations/slack.d.ts.map +1 -0
  241. package/dist/internal/webhookDestinations/slack.js +422 -0
  242. package/dist/internal/webhookDestinations/slack.js.map +1 -0
  243. package/dist/internal/webhookDestinations/url.d.ts +17 -0
  244. package/dist/internal/webhookDestinations/url.d.ts.map +1 -0
  245. package/dist/internal/webhookDestinations/url.js +130 -0
  246. package/dist/internal/webhookDestinations/url.js.map +1 -0
  247. package/dist/openrpc.json +5302 -0
  248. package/package.json +46 -4
  249. package/seeds/31318/icons/0x20c0000000000000000000000000000000000000.svg +1 -0
  250. package/seeds/31318/icons/0x20c0000000000000000000000000000000000001.svg +1 -0
  251. package/seeds/31318/icons/0x20c0000000000000000000000000000000000002.svg +1 -0
  252. package/seeds/31318/icons/0x20c0000000000000000000000000000000000003.svg +1 -0
  253. package/seeds/31318/verified-tokens.json +30 -0
  254. package/seeds/4217/icons/0x20c0000000000000000000000000000000000000.svg +25 -0
  255. package/seeds/4217/icons/0x20c000000000000000000000048c8f36df1c9a4a.svg +1 -0
  256. package/seeds/4217/icons/0x20c0000000000000000000000520792dcccccccc.svg +1 -0
  257. package/seeds/4217/icons/0x20c0000000000000000000000a6da882d075a4c3.svg +4 -0
  258. package/seeds/4217/icons/0x20c000000000000000000000111111111e910f0f.svg +30 -0
  259. package/seeds/4217/icons/0x20c00000000000000000000014f22ca97301eb73.svg +1 -0
  260. package/seeds/4217/icons/0x20c0000000000000000000001621e21f71cf12fb.svg +1 -0
  261. package/seeds/4217/icons/0x20c0000000000000000000002f52d5cc21a3207b.svg +1 -0
  262. package/seeds/4217/icons/0x20c0000000000000000000003158081efd85bfc2.svg +11 -0
  263. package/seeds/4217/icons/0x20c0000000000000000000003554d28269e0f3c2.svg +1 -0
  264. package/seeds/4217/icons/0x20c000000000000000000000383a23bacb546ab9.svg +1 -0
  265. package/seeds/4217/icons/0x20c00000000000000000000042109aef2f8b28e1.png +0 -0
  266. package/seeds/4217/icons/0x20c0000000000000000000005c0bac7cef389a11.svg +1 -0
  267. package/seeds/4217/icons/0x20c0000000000000000000006fd9a167923ba194.png +0 -0
  268. package/seeds/4217/icons/0x20c0000000000000000000007f7ba549dd0251b9.svg +1 -0
  269. package/seeds/4217/icons/0x20c0000000000000000000008191667423f70e67.svg +5 -0
  270. package/seeds/4217/icons/0x20c0000000000000000000008ee4fcff88888888.svg +1 -0
  271. package/seeds/4217/icons/0x20c0000000000000000000009a4a4b17e0dc6651.svg +1 -0
  272. package/seeds/4217/icons/0x20c000000000000000000000ab02d39df30bd17e.svg +1 -0
  273. package/seeds/4217/icons/0x20c000000000000000000000ae247a1130450f09.svg +1 -0
  274. package/seeds/4217/icons/0x20c000000000000000000000aeed2ec36a54d0e5.svg +1 -0
  275. package/seeds/4217/icons/0x20c000000000000000000000b9537d11c60e8b50.svg +1 -0
  276. package/seeds/4217/icons/0x20c000000000000000000000bd95bfb69fbe6ce3.svg +1 -0
  277. package/seeds/4217/icons/0x20c000000000000000000000c412ec89d0c08be5.svg +1 -0
  278. package/seeds/4217/icons/0x20c000000000000000000000f047dd7018e50367.png +0 -0
  279. package/seeds/4217/verified-tokens.json +177 -0
  280. package/seeds/42431/icons/0x20c0000000000000000000000000000000000000.svg +25 -0
  281. package/seeds/42431/icons/0x20c0000000000000000000000000000000000001.svg +1 -0
  282. package/seeds/42431/icons/0x20c0000000000000000000000000000000000002.svg +1 -0
  283. package/seeds/42431/icons/0x20c0000000000000000000000000000000000003.svg +1 -0
  284. package/seeds/42431/icons/0x20c0000000000000000000009e8d7eb59b783726.svg +1 -0
  285. package/seeds/42431/icons/0x20c000000000000000000000d72572838bbee59c.svg +1 -0
  286. package/seeds/42431/verified-tokens.json +44 -0
  287. package/seeds/openrpc/README.md +27 -0
  288. package/seeds/openrpc/execution-apis.json +8989 -0
  289. package/src/ApiKey.test.ts +91 -0
  290. package/src/ApiKey.ts +122 -0
  291. package/src/ApiKeySource.test-d.ts +11 -0
  292. package/src/ApiKeySource.test.ts +295 -0
  293. package/src/ApiKeySource.ts +303 -0
  294. package/src/App.test-d.ts +38 -0
  295. package/src/App.test.ts +65927 -0
  296. package/src/App.ts +855 -0
  297. package/src/Assets.ts +58 -0
  298. package/src/Auth.ts +1 -0
  299. package/src/Cli.test.ts +81 -0
  300. package/src/Cli.ts +38 -0
  301. package/src/Client.test-d.ts +1438 -0
  302. package/src/Client.test.ts +134 -0
  303. package/src/Client.ts +45 -0
  304. package/src/Metrics.ts +154 -0
  305. package/src/Scope.ts +50 -0
  306. package/src/Webhooks.test.ts +49 -0
  307. package/src/Webhooks.ts +172 -0
  308. package/src/admin/Access.test.ts +130 -0
  309. package/src/admin/Access.ts +87 -0
  310. package/src/admin/App.test.ts +225 -0
  311. package/src/admin/App.ts +216 -0
  312. package/src/admin/apps/api-keys.test.ts +156 -0
  313. package/src/admin/apps/api-keys.ts +157 -0
  314. package/src/admin/apps/scopes.test.ts +52 -0
  315. package/src/admin/apps/scopes.ts +47 -0
  316. package/src/admin/apps/verified-tokens.test.ts +415 -0
  317. package/src/admin/apps/verified-tokens.ts +566 -0
  318. package/src/admin/index.ts +2 -0
  319. package/src/admin/ui/plugin.ts +135 -0
  320. package/src/admin/ui/src/components/Checkbox.tsx +22 -0
  321. package/src/admin/ui/src/components/Dialog.tsx +32 -0
  322. package/src/admin/ui/src/components/MobileProfile.tsx +31 -0
  323. package/src/admin/ui/src/components/Page.tsx +53 -0
  324. package/src/admin/ui/src/components/TempoLogo.tsx +37 -0
  325. package/src/admin/ui/src/fonts/Pilat-Demi.woff2 +0 -0
  326. package/src/admin/ui/src/fonts/Pilat-Regular.woff2 +0 -0
  327. package/src/admin/ui/src/lib/api-keys.ts +63 -0
  328. package/src/admin/ui/src/lib/client.ts +15 -0
  329. package/src/admin/ui/src/lib/config.ts +57 -0
  330. package/src/admin/ui/src/lib/query-client.ts +26 -0
  331. package/src/admin/ui/src/lib/scopes.ts +25 -0
  332. package/src/admin/ui/src/lib/verified-tokens.ts +89 -0
  333. package/src/admin/ui/src/lib/wagmi.ts +18 -0
  334. package/src/admin/ui/src/routeTree.gen.ts +104 -0
  335. package/src/admin/ui/src/router.tsx +25 -0
  336. package/src/admin/ui/src/routes/__root.tsx +219 -0
  337. package/src/admin/ui/src/routes/api-keys.tsx +329 -0
  338. package/src/admin/ui/src/routes/index.tsx +7 -0
  339. package/src/admin/ui/src/routes/verified-tokens.tsx +331 -0
  340. package/src/admin/ui/src/styles.css +20 -0
  341. package/src/admin/ui/tsconfig.json +16 -0
  342. package/src/admin/ui/vite.config.ts +29 -0
  343. package/src/admin/ui.generated.ts +60 -0
  344. package/src/apps/activities.test.ts +1020 -0
  345. package/src/apps/activities.ts +3934 -0
  346. package/src/apps/addresses.ts +24 -0
  347. package/src/apps/balances.test.ts +274 -0
  348. package/src/apps/balances.ts +533 -0
  349. package/src/apps/blocks.test.ts +385 -0
  350. package/src/apps/blocks.ts +896 -0
  351. package/src/apps/coingecko.test.ts +218 -0
  352. package/src/apps/coingecko.ts +1383 -0
  353. package/src/apps/exchanges.test.ts +958 -0
  354. package/src/apps/exchanges.ts +4773 -0
  355. package/src/apps/fee-amm.test.ts +321 -0
  356. package/src/apps/fee-amm.ts +1121 -0
  357. package/src/apps/indexer.test.ts +153 -0
  358. package/src/apps/indexer.ts +330 -0
  359. package/src/apps/receipts.test.ts +285 -0
  360. package/src/apps/receipts.ts +1193 -0
  361. package/src/apps/rpc.test.ts +302 -0
  362. package/src/apps/rpc.ts +222 -0
  363. package/src/apps/tokenlist.test.ts +80 -0
  364. package/src/apps/tokenlist.ts +207 -0
  365. package/src/apps/tokens.test.ts +1062 -0
  366. package/src/apps/tokens.ts +2292 -0
  367. package/src/apps/transactions.test.ts +408 -0
  368. package/src/apps/transactions.ts +1363 -0
  369. package/src/apps/transfers.test.ts +371 -0
  370. package/src/apps/transfers.ts +1116 -0
  371. package/src/apps/verified-tokens.test.ts +114 -0
  372. package/src/apps/verified-tokens.ts +295 -0
  373. package/src/apps/webhooks.test.ts +1937 -0
  374. package/src/apps/webhooks.ts +2926 -0
  375. package/src/cloudflare.ts +55 -0
  376. package/src/index.ts +15 -0
  377. package/src/internal/Auth.test.ts +980 -0
  378. package/src/internal/Auth.ts +1012 -0
  379. package/src/internal/Cache.test.ts +240 -0
  380. package/src/internal/Cache.ts +262 -0
  381. package/src/internal/Cursor.test.ts +100 -0
  382. package/src/internal/Cursor.ts +149 -0
  383. package/src/internal/EdgeCache.test.ts +234 -0
  384. package/src/internal/EdgeCache.ts +224 -0
  385. package/src/internal/Errors.ts +14 -0
  386. package/src/internal/Log.test.ts +244 -0
  387. package/src/internal/Log.ts +216 -0
  388. package/src/internal/MetricSink.ts +220 -0
  389. package/src/internal/Mpp.test.ts +64 -0
  390. package/src/internal/Mpp.ts +141 -0
  391. package/src/internal/OpenApi.fonts.ts +38 -0
  392. package/src/internal/OpenApi.logo.ts +18 -0
  393. package/src/internal/OpenApi.ts +488 -0
  394. package/src/internal/Path.test.ts +14 -0
  395. package/src/internal/Path.ts +15 -0
  396. package/src/internal/RateLimit.test.ts +93 -0
  397. package/src/internal/RateLimit.ts +93 -0
  398. package/src/internal/Response.ts +131 -0
  399. package/src/internal/Schema.test.ts +34 -0
  400. package/src/internal/Schema.ts +523 -0
  401. package/src/internal/Store.test.ts +545 -0
  402. package/src/internal/Store.ts +526 -0
  403. package/src/internal/Tidx.local.test.ts +33 -0
  404. package/src/internal/Tidx.test.ts +294 -0
  405. package/src/internal/Tidx.ts +238 -0
  406. package/src/internal/Timing.test.ts +22 -0
  407. package/src/internal/Timing.ts +62 -0
  408. package/src/internal/Ttl.test.ts +18 -0
  409. package/src/internal/Ttl.ts +59 -0
  410. package/src/internal/Value.ts +114 -0
  411. package/src/internal/VerifiedTokens.test.ts +333 -0
  412. package/src/internal/VerifiedTokens.ts +645 -0
  413. package/src/internal/Viem.test.ts +115 -0
  414. package/src/internal/Viem.ts +165 -0
  415. package/src/internal/WebhookDestination.test.ts +648 -0
  416. package/src/internal/WebhookDestination.ts +331 -0
  417. package/src/internal/Webhooks.test.ts +668 -0
  418. package/src/internal/Webhooks.ts +940 -0
  419. package/src/internal/webhookDestinations/betterStack.ts +91 -0
  420. package/src/internal/webhookDestinations/slack.ts +496 -0
  421. package/src/internal/webhookDestinations/url.ts +128 -0
  422. package/src/openrpc.json +5302 -0
@@ -0,0 +1,1012 @@
1
+ import type { Context as hono_Context, Hono, MiddlewareHandler } from 'hono'
2
+ import type { HonoBase } from 'hono/hono-base'
3
+ import { createMiddleware } from 'hono/factory'
4
+ import { matchedRoutes } from 'hono/route'
5
+ import type { MergePath, RouterRoute, Schema as core_Schema } from 'hono/types'
6
+ import { findTargetHandler } from 'hono/utils/handler'
7
+ import { Credential, type Method } from 'mppx'
8
+ import { Mppx, tempo } from 'mppx/hono'
9
+
10
+ import * as ApiKeySource from '../ApiKeySource.js'
11
+ import * as ApiKey from '../ApiKey.js'
12
+ import * as OpenApi from './OpenApi.js'
13
+ import * as RateLimit from './RateLimit.js'
14
+ import * as Response from './Response.js'
15
+ import * as Schema from './Schema.js'
16
+ import * as Scope from '../Scope.js'
17
+ import * as Viem from './Viem.js'
18
+
19
+ const policySymbol = Symbol('tempo-api.auth.policy')
20
+
21
+ /** Auth context consumed by Tempo API handlers. */
22
+ export type Context = {
23
+ /** API key authentication configuration. */
24
+ apiKey?:
25
+ | {
26
+ /**
27
+ * API-key quotas keyed by quota scope. The reserved `'*'` entry is the
28
+ * config default; any other key sets that scope's quota. Mirrors the
29
+ * per-key {@link ApiKey.ApiKey.rateLimits} shape.
30
+ */
31
+ rateLimits?: Record<string, RateLimit.Limit> | undefined
32
+ /** Backing API-key source. */
33
+ source: ApiKeySource.Source
34
+ }
35
+ | undefined
36
+ /** MPP payment configuration. */
37
+ mpp?: Mpp | undefined
38
+ /** Rate-limit store used by API handlers. */
39
+ rateLimit?: RateLimit.Store | undefined
40
+ }
41
+
42
+ /** Hono environment variables set by auth middleware. */
43
+ export type Environment = {
44
+ Variables: Variables
45
+ }
46
+
47
+ /**
48
+ * HTTP endpoint key inferred from a Hono app schema. The schema is matched
49
+ * against `HonoBase` (which carries it as its 2nd generic) rather than `Hono`
50
+ * (which re-declares only 3 generics and loses the inferred schema when used
51
+ * as a value type from `ReturnType<typeof create>`).
52
+ */
53
+ export type Endpoint<app extends Hono<any, any>, mount extends string = '/'> =
54
+ app extends HonoBase<any, infer schema extends core_Schema, any>
55
+ ? endpoint.FromSchema<schema, mount>
56
+ : never
57
+
58
+ /** Metadata for an accepted payment. */
59
+ export type Payment = {
60
+ /** Verified MPP payer identifier. */
61
+ payer?: string | undefined
62
+ /** Why payment was required for the request. */
63
+ reason: 'api_key_over_quota' | 'public_over_quota'
64
+ /** Payment protocol kind. */
65
+ type: 'mpp'
66
+ }
67
+
68
+ /** MPP charge options used by route access policies. */
69
+ export type Charge = Method.RequestDefaults<ReturnType<typeof tempo.charge>> & {
70
+ /** Optional human-readable payment description. */
71
+ description?: string | undefined
72
+ /** Optional challenge expiration timestamp or date. */
73
+ expires?: Date | string | undefined
74
+ /** Optional server-defined correlation data. */
75
+ meta?: Record<string, string> | undefined
76
+ /** Optional route/resource scope bound to the challenge. */
77
+ scope?: string | undefined
78
+ }
79
+
80
+ /** MPP payment handler consumed by auth policies. */
81
+ type Mpp = {
82
+ /** Creates a Hono middleware for a Tempo charge. */
83
+ charge(options: Charge): MiddlewareHandler
84
+ }
85
+
86
+ /**
87
+ * Route-level auth policy override. Each lane is independent: `true` enables it
88
+ * with the middleware defaults, `false` disables it, an object enables it with
89
+ * the given overrides, and omitting it inherits the resolved policy. Lanes are
90
+ * default-closed except `apiKey` (see {@link middleware}), so a route that
91
+ * declares nothing — or declares only `apiKey` — is API-key-only.
92
+ */
93
+ export type PolicyOverride = {
94
+ /** API-key lane: pass quota/scopes overrides, or a boolean to enable/disable. */
95
+ apiKey?:
96
+ | {
97
+ /** API-key quota override for this route (a protective per-route cap). */
98
+ rateLimit?: RateLimit.Limit | undefined
99
+ /** Required API-key scopes override for this route. */
100
+ scopes?: readonly Scope.Id[] | undefined
101
+ }
102
+ | boolean
103
+ | undefined
104
+ /** MPP paid lane: object to override the charge, or boolean to enable/disable. */
105
+ mpp?:
106
+ | {
107
+ /** Tempo charge override for this route. */
108
+ charge?: Charge | undefined
109
+ }
110
+ | boolean
111
+ | undefined
112
+ /** Public lane: object to override the quota, or boolean to enable/disable. */
113
+ public?:
114
+ | {
115
+ /** Anonymous public quota override for this route. */
116
+ rateLimit?: RateLimit.Limit | undefined
117
+ }
118
+ | boolean
119
+ | undefined
120
+ }
121
+
122
+ /** Authenticated or anonymous caller. */
123
+ export type Principal =
124
+ | {
125
+ /** Resolved API key. */
126
+ apiKey: ApiKey.ApiKey
127
+ /** Key environment (`production` accesses any chain; `sandbox` is non-mainnet only). */
128
+ environment: ApiKey.ApiKey['environment']
129
+ /** Principal id. */
130
+ id: string
131
+ /** Owning organization id. */
132
+ orgId: string
133
+ /** Payment metadata for paid overflow requests. */
134
+ payment?: Payment | undefined
135
+ /** Principal kind. */
136
+ type: 'api_key'
137
+ }
138
+ | {
139
+ /** Public quota identity. */
140
+ id: string
141
+ /** Payment metadata for paid overflow requests. */
142
+ payment?: Payment | undefined
143
+ /** Principal kind. */
144
+ type: 'public'
145
+ }
146
+
147
+ /** Hono variables set by auth middleware. */
148
+ export type Variables = {
149
+ /** Auth context consumed by protected handlers. */
150
+ auth: Context
151
+ /** Authenticated or anonymous caller. */
152
+ principal?: Principal | undefined
153
+ }
154
+
155
+ /** Reads the authenticated principal from a Hono context. */
156
+ export function getPrincipal<environment extends Environment>(c: hono_Context<environment>) {
157
+ return c.get('principal') ?? null
158
+ }
159
+
160
+ /**
161
+ * Always false at runtime, but typed as `boolean` so handlers can include a
162
+ * never-reached access-error branch in Hono's inferred response union.
163
+ */
164
+ export const narrowAccess = false as boolean
165
+
166
+ /** Typed auth/payment error response for Hono client inference. Never reached at runtime. */
167
+ export function accessError(c: hono_Context) {
168
+ const status = Number(c.req.header('tempo-narrow-access-status'))
169
+ if (status === 400)
170
+ return Response.error(c, {
171
+ code: 'api_key_malformed',
172
+ message: 'Malformed API key',
173
+ status: 400,
174
+ })
175
+ if (status === 401) {
176
+ const code =
177
+ c.req.header('tempo-narrow-access-code') === 'api_key_missing'
178
+ ? 'api_key_missing'
179
+ : 'api_key_invalid'
180
+ return Response.error(c, {
181
+ code,
182
+ message: 'Missing or invalid API key',
183
+ status: 401,
184
+ })
185
+ }
186
+ if (status === 403)
187
+ return Response.error(c, {
188
+ code: 'api_key_forbidden',
189
+ message: 'API key missing required scope',
190
+ status: 403,
191
+ })
192
+ const code =
193
+ c.req.header('tempo-narrow-access-code') === 'rate_limit_exceeded'
194
+ ? 'rate_limit_exceeded'
195
+ : 'payment_required'
196
+ return Response.error(c, {
197
+ code,
198
+ message: 'Payment required or rate limit exceeded',
199
+ status: 429,
200
+ })
201
+ }
202
+
203
+ /** Typed auth/payment error response for Hono client inference. Never reached at runtime. */
204
+ export function paidAccessError(c: hono_Context) {
205
+ const status = Number(c.req.header('tempo-narrow-access-status'))
206
+ if (status !== 402) return accessError(c)
207
+ // MPP owns the 402 challenge body and headers; it is intentionally not the
208
+ // API's JSON error envelope.
209
+ return c.body(null, 402)
210
+ }
211
+
212
+ /** Creates auth middleware for protected OpenAPI routes. */
213
+ export function middleware<
214
+ endpoint extends string = string,
215
+ environment extends Environment = Environment,
216
+ >(options: middleware.Options<endpoint>): MiddlewareHandler<environment> {
217
+ const { charge = {}, ...mpp } = options.mpp === false ? {} : (options.mpp ?? {})
218
+ const defaults = laneDefaults(options)
219
+ const base = basePolicy(options, defaults)
220
+ // Resolution is cached per isolate by default (positive 60s / negative 10s,
221
+ // keyed by token sha256 — see `ApiKeySource.cached`) so hot keys skip the
222
+ // backend read on the auth path of every request. Pass `apiKey.cache: false`
223
+ // when instant revocation matters more than the per-request read.
224
+ const source = (() => {
225
+ if (options.apiKey === false || !options.apiKey?.source) return ApiKeySource.noop
226
+ if (options.apiKey.cache === false) return options.apiKey.source
227
+ return ApiKeySource.cached(options.apiKey.source, options.apiKey.cache)
228
+ })()
229
+ const auth: Context = {
230
+ ...(options.apiKey === false
231
+ ? {}
232
+ : {
233
+ apiKey: {
234
+ ...(options.apiKey?.rateLimits ? { rateLimits: options.apiKey.rateLimits } : {}),
235
+ source,
236
+ },
237
+ }),
238
+ ...(options.mpp === false
239
+ ? {}
240
+ : {
241
+ mpp: Mppx.create({
242
+ ...mpp,
243
+ methods: [tempo.charge({ amount: '0.001', ...charge })],
244
+ secretKey: mpp.secretKey ?? crypto.randomUUID(),
245
+ }),
246
+ }),
247
+ rateLimit: RateLimit.memory(options.rateLimit),
248
+ }
249
+
250
+ return createMiddleware<environment>((c, next) => {
251
+ const route = openApiRoute(c)
252
+ if (!route) return next()
253
+
254
+ const { key, description } = route
255
+ const overrides = options.overrides as
256
+ | Record<string, PolicyOverride | false | undefined>
257
+ | undefined
258
+ const endpointPolicy = resolvePolicy(
259
+ base,
260
+ defaults,
261
+ [...policies(c), overrides?.[key]],
262
+ description,
263
+ )
264
+ if (!endpointPolicy) return next()
265
+
266
+ c.set('auth', auth)
267
+ return require<environment>(endpointPolicy)(c, next)
268
+ })
269
+ }
270
+
271
+ export declare namespace middleware {
272
+ /** Per-route policy overrides keyed by HTTP endpoint. */
273
+ type Overrides<endpoint extends string = string> = Partial<
274
+ Record<NoInfer<endpoint>, PolicyOverride | false | undefined>
275
+ >
276
+
277
+ /** Options for creating auth middleware. */
278
+ type Options<endpoint extends string = string> = {
279
+ /** Default API-key access policy. Pass false to disable API-key access. */
280
+ apiKey?:
281
+ | {
282
+ /**
283
+ * Resolve-cache tuning for the source (per-isolate, positive 60s /
284
+ * negative 10s by default), or `false` to resolve uncached on every
285
+ * request — e.g. when instant revocation matters more than the
286
+ * per-request backend read.
287
+ */
288
+ cache?: ApiKeySource.cached.Options | false | undefined
289
+ /** API-key quotas keyed by quota scope (reserved `'*'` is the config default). */
290
+ rateLimits?: Record<string, RateLimit.Limit> | undefined
291
+ /** Backing API-key source. Defaults to an empty source (rejects all). */
292
+ source?: ApiKeySource.Source | undefined
293
+ /** Required API-key scopes. */
294
+ scopes?: readonly Scope.Id[] | undefined
295
+ }
296
+ | false
297
+ | undefined
298
+ /** MPP payment options. Pass false to disable paid overflow. */
299
+ mpp?: MppOptions | false | undefined
300
+ /** Per-route policy overrides keyed by HTTP endpoint, e.g. `GET /tokens/:token`. */
301
+ overrides?: Overrides<endpoint> | undefined
302
+ /** Default public access policy. Pass false to disable anonymous access. */
303
+ public?:
304
+ | {
305
+ /** Anonymous public quota for protected routes. */
306
+ rateLimit?: RateLimit.Limit | undefined
307
+ }
308
+ | false
309
+ | undefined
310
+ /** Rate-limit store options. */
311
+ rateLimit?: RateLimit.memory.Options | undefined
312
+ }
313
+
314
+ /** MPP payment options used by auth middleware. */
315
+ type MppOptions = Omit<NonNullable<Parameters<typeof Mppx.create>[0]>, 'methods'> & {
316
+ /** Global Tempo charge options passed to the MPP method instantiator. */
317
+ charge?: NonNullable<Parameters<typeof tempo.charge>[0]> | undefined
318
+ }
319
+ }
320
+
321
+ /** Creates route middleware that contributes an auth policy override. */
322
+ export function policy(options: PolicyOverride): MiddlewareHandler {
323
+ const middleware = createMiddleware((_, next) => next()) as MiddlewareHandler & PolicyHandler
324
+ middleware[policySymbol] = options
325
+ return middleware
326
+ }
327
+
328
+ export declare namespace endpoint {
329
+ /**
330
+ * HTTP endpoint key inferred from a Hono app schema. Distributes over the
331
+ * schema union so the `BlankSchema | MergeSchemaPath<...>` shape Hono
332
+ * produces from `.basePath(...).route(...)` chains contributes its typed
333
+ * routes (a non-distributing mapped type would collapse `BlankSchema` to
334
+ * `never` and infect the whole union).
335
+ */
336
+ type FromSchema<schema extends core_Schema, mount extends string = '/'> = schema extends infer s
337
+ ? {
338
+ [path in keyof s & string]: {
339
+ [method in keyof s[path] &
340
+ `$${string}`]: `${Method<method>} ${Path<MergePath<mount, path>>}`
341
+ }[keyof s[path] & `$${string}`]
342
+ }[keyof s & string]
343
+ : never
344
+
345
+ /** HTTP method string extracted from Hono's schema method key. */
346
+ type Method<method extends string> = method extends `$${infer value}` ? Uppercase<value> : never
347
+
348
+ /** Public endpoint path with Hono route regex constraints removed. */
349
+ type Path<path extends string> = path extends `${infer head}/${infer tail}`
350
+ ? `${Segment<head>}/${Path<tail>}`
351
+ : Segment<path>
352
+
353
+ /** Public endpoint path segment with any regex constraint removed. */
354
+ type Segment<segment extends string> = segment extends `:${infer parameter}{${string}`
355
+ ? `:${parameter}`
356
+ : segment
357
+ }
358
+
359
+ /** Per-lane default config a route inherits when it enables a lane. */
360
+ type LaneDefaults = {
361
+ apiKey: NonNullable<require.Policy['apiKey']>
362
+ mpp: NonNullable<require.Policy['mpp']>
363
+ public: NonNullable<require.Policy['public']>
364
+ }
365
+
366
+ type OpenApiHandler = {
367
+ [OpenApi.uniqueSymbol]?: OpenApi.HandlerUniqueProperty | undefined
368
+ }
369
+
370
+ type OpenApiRoute = {
371
+ key: string
372
+ description: string | undefined
373
+ }
374
+
375
+ type PolicyHandler = {
376
+ [policySymbol]?: PolicyOverride | undefined
377
+ }
378
+
379
+ function laneDefaults(options: middleware.Options): LaneDefaults {
380
+ const apiKey = options.apiKey === false ? undefined : options.apiKey
381
+ const public_ = options.public === false ? undefined : options.public
382
+ return {
383
+ apiKey: {
384
+ // `rateLimit` is intentionally omitted: `policy.apiKey.rateLimit` means an
385
+ // explicit per-route cap (precedence tier 2), not the config default. The
386
+ // config default (tier 5) lives on `auth.apiKey.rateLimits['*']` and is
387
+ // applied by `resolveApiKeyLimit` at consume time.
388
+ //
389
+ // Default-closed to a full-access key: a route that declares no scopes
390
+ // requires the `'*'` wildcard, so a narrowly-scoped key only reaches
391
+ // routes that explicitly grant its scope. Routes opt into specific
392
+ // catalog scopes via `Auth.policy`.
393
+ scopes: apiKey?.scopes ?? [Scope.wildcard],
394
+ },
395
+ mpp: { charge: {} },
396
+ public: { rateLimit: public_?.rateLimit ?? { perMinute: 60 } },
397
+ }
398
+ }
399
+
400
+ /**
401
+ * Base policy before route overrides. Default-closed except the API-key lane:
402
+ * a route that declares nothing is API-key-only, and must opt into the public
403
+ * and paid lanes explicitly. This makes a partial override fail safe — omitting
404
+ * a lane leaves it closed rather than silently inheriting open access.
405
+ */
406
+ function basePolicy(options: middleware.Options, defaults: LaneDefaults): require.Policy {
407
+ return options.apiKey === false ? {} : { apiKey: defaults.apiKey }
408
+ }
409
+
410
+ function resolvePolicy(
411
+ base: require.Policy,
412
+ defaults: LaneDefaults,
413
+ overrides: readonly (PolicyOverride | false | undefined)[],
414
+ description: string | undefined,
415
+ ): require.Policy | undefined {
416
+ let apiKey = base.apiKey
417
+ let mpp = base.mpp
418
+ let public_ = base.public
419
+
420
+ for (const override of overrides) {
421
+ if (override === false) return undefined
422
+ if (!override) continue
423
+ apiKey = resolveApiKeyLane(apiKey, override.apiKey, defaults.apiKey)
424
+ mpp = resolveMppLane(mpp, override.mpp, defaults.mpp)
425
+ public_ = resolvePublicLane(public_, override.public, defaults.public)
426
+ }
427
+
428
+ // The OpenAPI summary/description rides along as the payment-challenge
429
+ // description. It is metadata, not a policy override, so it only applies when
430
+ // the paid lane is actually enabled and has no explicit description already.
431
+ if (mpp && description !== undefined && mpp.charge.description === undefined)
432
+ mpp = { charge: { ...mpp.charge, description } }
433
+
434
+ if (!apiKey && !mpp && !public_) return undefined
435
+ return {
436
+ ...(apiKey ? { apiKey } : {}),
437
+ ...(mpp ? { mpp } : {}),
438
+ ...(public_ ? { public: public_ } : {}),
439
+ }
440
+ }
441
+
442
+ function resolveApiKeyLane(
443
+ current: require.Policy['apiKey'],
444
+ override: PolicyOverride['apiKey'],
445
+ default_: LaneDefaults['apiKey'],
446
+ ): require.Policy['apiKey'] {
447
+ if (override === undefined) return current
448
+ if (override === false) return undefined
449
+ if (override === true) return current ?? default_
450
+ const base = current ?? default_
451
+ return {
452
+ rateLimit: override.rateLimit ?? base.rateLimit,
453
+ scopes: override.scopes ?? base.scopes,
454
+ }
455
+ }
456
+
457
+ function resolveMppLane(
458
+ current: require.Policy['mpp'],
459
+ override: PolicyOverride['mpp'],
460
+ default_: LaneDefaults['mpp'],
461
+ ): require.Policy['mpp'] {
462
+ if (override === undefined) return current
463
+ if (override === false) return undefined
464
+ if (override === true) return current ?? default_
465
+ const base = current ?? default_
466
+ return { charge: { ...base.charge, ...override.charge } }
467
+ }
468
+
469
+ function resolvePublicLane(
470
+ current: require.Policy['public'],
471
+ override: PolicyOverride['public'],
472
+ default_: LaneDefaults['public'],
473
+ ): require.Policy['public'] {
474
+ if (override === undefined) return current
475
+ if (override === false) return undefined
476
+ if (override === true) return current ?? default_
477
+ const base = current ?? default_
478
+ return { rateLimit: override.rateLimit ?? base.rateLimit }
479
+ }
480
+
481
+ function openApiRoute(c: hono_Context): OpenApiRoute | undefined {
482
+ const route = matchedRoutes(c)
483
+ .slice(c.req.routeIndex + 1)
484
+ .find((route) => !!openApiSpec(route))
485
+ if (!route) return undefined
486
+
487
+ const spec = openApiSpec(route)
488
+ if (!spec) return undefined
489
+
490
+ const description = typeof spec.summary === 'string' ? spec.summary : spec.description
491
+ return {
492
+ key: endpointKey(c, route),
493
+ description: typeof description === 'string' ? description : undefined,
494
+ }
495
+ }
496
+
497
+ function policies(c: hono_Context) {
498
+ return matchedRoutes(c)
499
+ .slice(c.req.routeIndex + 1)
500
+ .map(policyFromRoute)
501
+ .filter((policy): policy is PolicyOverride => policy !== undefined)
502
+ }
503
+
504
+ function openApiSpec(route: RouterRoute) {
505
+ const handler = findTargetHandler(route.handler) as OpenApiHandler
506
+ const metadata = handler[OpenApi.uniqueSymbol]
507
+ if (!metadata || !('spec' in metadata)) return undefined
508
+ return metadata.spec
509
+ }
510
+
511
+ function policyFromRoute(route: RouterRoute) {
512
+ const handler = findTargetHandler(route.handler) as PolicyHandler
513
+ return handler[policySymbol]
514
+ }
515
+
516
+ /** Resolved access lanes for one documented operation. */
517
+ export type OperationAccess = {
518
+ /** Whether MPP payment is accepted (enables the `402` payment-challenge response). */
519
+ mpp: boolean
520
+ /** Whether anonymous public-quota access is allowed (keeps the anonymous security option). */
521
+ public: boolean
522
+ /** Required API-key scopes (`[]` accepts any valid key); empty when the API-key lane is disabled. */
523
+ scopes: readonly Scope.Id[]
524
+ }
525
+
526
+ /**
527
+ * Maps each documented operation id to its resolved access lanes by reading the
528
+ * `policy` overrides co-located on the app's routes. This is the single source
529
+ * of truth for per-operation `security` and the `402` response in the generated
530
+ * OpenAPI document (see `App.create`): the API-key lane is default-on, and the
531
+ * public/MPP lanes apply only where a route opts in via {@link policy}.
532
+ */
533
+ export function describeAccess(app: Hono<any, any>): Record<string, OperationAccess> {
534
+ const groups = new Map<string, { operationId?: string; overrides: PolicyOverride[] }>()
535
+ for (const route of app.routes) {
536
+ const key = `${route.method} ${route.path}`
537
+ const group = groups.get(key) ?? { overrides: [] }
538
+ const spec = openApiSpec(route) as { operationId?: unknown } | undefined
539
+ if (typeof spec?.operationId === 'string') group.operationId = spec.operationId
540
+ const override = policyFromRoute(route)
541
+ if (override) group.overrides.push(override)
542
+ groups.set(key, group)
543
+ }
544
+
545
+ const access: Record<string, OperationAccess> = {}
546
+ for (const { operationId, overrides } of groups.values()) {
547
+ if (!operationId) continue
548
+ let apiKey = true
549
+ let scopes: readonly Scope.Id[] = []
550
+ let public_ = false
551
+ let mpp = false
552
+ for (const override of overrides) {
553
+ if (override.apiKey !== undefined) apiKey = override.apiKey !== false
554
+ if (override.apiKey && override.apiKey !== true && override.apiKey.scopes)
555
+ scopes = override.apiKey.scopes
556
+ if (override.public !== undefined) public_ = override.public !== false
557
+ if (override.mpp !== undefined) mpp = override.mpp !== false
558
+ }
559
+ access[operationId] = { mpp, public: public_, scopes: apiKey ? scopes : [] }
560
+ }
561
+ return access
562
+ }
563
+
564
+ function endpointKey(c: hono_Context, route: RouterRoute) {
565
+ return `${route.method.toUpperCase()} ${stripBasePath(normalizeRoutePath(route.path), basePath(c))}`
566
+ }
567
+
568
+ function normalizeRoutePath(path: string) {
569
+ // Hono stores regex-constrained params as `:name{...}`. Auth overrides use
570
+ // the stable public route shape, e.g. `GET /tokens/:symbol`.
571
+ return path
572
+ .split('/')
573
+ .map((segment) => {
574
+ if (!segment.startsWith(':')) return segment
575
+ const pattern = segment.indexOf('{')
576
+ return pattern === -1 ? segment : segment.slice(0, pattern)
577
+ })
578
+ .join('/')
579
+ }
580
+
581
+ function stripBasePath(path: string, basePath: string | undefined) {
582
+ if (!basePath || basePath === '/') return path
583
+ if (path === basePath) return '/'
584
+ if (path.startsWith(`${basePath}/`)) return path.slice(basePath.length)
585
+ return path
586
+ }
587
+
588
+ function basePath(c: hono_Context) {
589
+ const variables = c.var as Record<string, unknown>
590
+ return typeof variables['basePath'] === 'string' ? variables['basePath'] : undefined
591
+ }
592
+
593
+ /** Requires route access through API key, public quota, or MPP payment. */
594
+ export function require<environment extends Environment = Environment>(
595
+ policy: require.Policy,
596
+ ): MiddlewareHandler<environment> {
597
+ return createMiddleware<environment>(async (c, next) => {
598
+ const auth = c.get('auth')
599
+ const credential = getApiKeyCredential(c)
600
+ // The deprecated `key` query param has now been consumed for auth. Strip it
601
+ // from the request URL so it never reaches a route's query validator —
602
+ // endpoint query schemas are `z.strictObject`, so an unexpected `key`
603
+ // param would otherwise fail validation with `query_invalid`, breaking the
604
+ // legacy `?key=...` lane on every strict-query endpoint.
605
+ stripLegacyAuthQuery(c)
606
+ if (credential === false)
607
+ return jsonError(c, {
608
+ code: 'api_key_malformed',
609
+ message: 'Malformed API key',
610
+ status: 400,
611
+ })
612
+
613
+ const hasPayment = hasPaymentCredential(c) && isPaymentEnabled(auth, policy)
614
+ if (credential) {
615
+ const apiKey = await auth.apiKey?.source.resolve(credential.token)
616
+ if (!apiKey)
617
+ return jsonError(c, {
618
+ code: 'api_key_invalid',
619
+ message: 'Invalid API key',
620
+ status: 401,
621
+ })
622
+
623
+ // Sandbox keys are restricted to non-mainnet chains. An explicit
624
+ // `chainId` of mainnet makes the key unusable for this request, so it is
625
+ // ignored and the request falls through to the public policy below (as if
626
+ // no key were presented). A request that omits `chainId` is steered to
627
+ // testnet so a sandbox key still "just works" without passing `?chainId`.
628
+ const explicitChain = explicitChainId(c)
629
+ const sandboxOnMainnet =
630
+ apiKey.environment === 'sandbox' &&
631
+ explicitChain !== undefined &&
632
+ Viem.isMainnet(explicitChain)
633
+
634
+ if (!sandboxOnMainnet) {
635
+ if (
636
+ apiKey.environment === 'sandbox' &&
637
+ explicitChain === undefined &&
638
+ Viem.isMainnet((c.get('chainId' as never) as number | undefined) ?? Viem.defaultChainId)
639
+ )
640
+ // `chainId` lives on the app's `Variables`, not the narrower auth
641
+ // contract, so it is read/written through an untyped cast.
642
+ c.set('chainId' as never, Viem.chainId.testnet as never)
643
+
644
+ const principal = principalFromApiKey(apiKey)
645
+ c.set('principal', principal)
646
+ const missing = apiKey.scopes.includes(Scope.wildcard)
647
+ ? []
648
+ : (policy.apiKey?.scopes.filter((scope) => !apiKey.scopes.includes(scope)) ?? [])
649
+ if (missing.length > 0)
650
+ return jsonError(c, {
651
+ code: 'api_key_forbidden',
652
+ message: 'API key missing required scope',
653
+ status: 403,
654
+ })
655
+
656
+ if (hasPayment)
657
+ return requirePayment(c, next, {
658
+ auth,
659
+ payment: { reason: 'api_key_over_quota', type: 'mpp' },
660
+ policy,
661
+ principal,
662
+ })
663
+
664
+ if (policy.apiKey) {
665
+ // A route consumes exactly one quota bucket: its single required scope,
666
+ // else `'*'` (zero scopes). Routes today require at most one scope; a
667
+ // future multi-scope route buckets against its first scope.
668
+ const scope = policy.apiKey.scopes[0] ?? Scope.wildcard
669
+ const limit = resolveApiKeyLimit({ apiKey, auth, policy: policy.apiKey, scope })
670
+ const result = await consumeRateLimit(auth, {
671
+ key: `api_key:${apiKey.id}:scope:${scope}`,
672
+ limit,
673
+ })
674
+ if (result) {
675
+ setRateLimitHeaders(c, result)
676
+ // Surface the bucket so callers can correlate a 429 with the exhausted
677
+ // quota. Set before the over-quota branch so it rides the 429 (an MPP
678
+ // 402 challenge is owned by mppx and drops these headers — by design).
679
+ c.header('RateLimit-Scope', scope)
680
+ }
681
+ if (result && !result.allowed)
682
+ return requirePaymentOrRateLimit(c, next, {
683
+ auth,
684
+ payment: { reason: 'api_key_over_quota', type: 'mpp' },
685
+ policy,
686
+ principal,
687
+ rateLimit: result,
688
+ })
689
+ }
690
+
691
+ return next()
692
+ }
693
+ }
694
+
695
+ const publicPrincipal = principalFromPublic(c)
696
+ if (hasPayment) {
697
+ c.set('principal', publicPrincipal)
698
+ return requirePayment(c, next, {
699
+ auth,
700
+ payment: { reason: 'public_over_quota', type: 'mpp' },
701
+ policy,
702
+ principal: publicPrincipal,
703
+ })
704
+ }
705
+
706
+ const limit = policy.public?.rateLimit
707
+ if (limit) {
708
+ const result = await consumeRateLimit(auth, {
709
+ key: `public:${publicPrincipal.id}`,
710
+ limit,
711
+ })
712
+ if (result) setRateLimitHeaders(c, result)
713
+ if (!result || result.allowed) {
714
+ c.set('principal', publicPrincipal)
715
+ return next()
716
+ }
717
+
718
+ c.set('principal', publicPrincipal)
719
+ return requirePaymentOrRateLimit(c, next, {
720
+ auth,
721
+ payment: { reason: 'public_over_quota', type: 'mpp' },
722
+ policy,
723
+ principal: publicPrincipal,
724
+ rateLimit: result,
725
+ })
726
+ }
727
+
728
+ if (isPaymentEnabled(auth, policy)) {
729
+ c.set('principal', publicPrincipal)
730
+ return requirePayment(c, next, {
731
+ auth,
732
+ payment: { reason: 'public_over_quota', type: 'mpp' },
733
+ policy,
734
+ principal: publicPrincipal,
735
+ })
736
+ }
737
+
738
+ return jsonError(c, {
739
+ code: 'api_key_missing',
740
+ message: 'Missing API key',
741
+ status: 401,
742
+ })
743
+ })
744
+ }
745
+
746
+ export declare namespace require {
747
+ /** Route access policy. */
748
+ type Policy = {
749
+ /** API-key access policy. */
750
+ apiKey?:
751
+ | {
752
+ /** API-key quota override for this route (a protective per-route cap). */
753
+ rateLimit?: RateLimit.Limit | undefined
754
+ /**
755
+ * Required API-key scopes. Defaults to `['*']`, so a route that does
756
+ * not declare scopes requires a full-access key; narrow it by listing
757
+ * catalog scopes via {@link policy}.
758
+ */
759
+ scopes: readonly (Scope.Id | typeof Scope.wildcard)[]
760
+ }
761
+ | undefined
762
+ /** MPP payment access policy. */
763
+ mpp?:
764
+ | {
765
+ /** Tempo charge policy. */
766
+ charge: Charge
767
+ }
768
+ | undefined
769
+ /** Public access policy. */
770
+ public?:
771
+ | {
772
+ /** Anonymous public quota for this route. */
773
+ rateLimit: RateLimit.Limit
774
+ }
775
+ | undefined
776
+ }
777
+ }
778
+
779
+ type ApiKeyCredential = {
780
+ token: string
781
+ }
782
+
783
+ type PaymentAccess = {
784
+ auth: Context
785
+ payment: Omit<Payment, 'payer'>
786
+ policy: require.Policy
787
+ principal: Principal
788
+ }
789
+
790
+ type RateLimitAccess = PaymentAccess & {
791
+ rateLimit: RateLimit.Result
792
+ }
793
+
794
+ function consumeRateLimit(
795
+ auth: Context,
796
+ options: RateLimit.Store.ConsumeOptions,
797
+ ): Promise<RateLimit.Result | null> {
798
+ if (!auth.rateLimit) return Promise.resolve(null)
799
+ // Fail open: a rate-limit store error (e.g. a lost Durable Object
800
+ // connection) degrades to unenforced quota instead of failing the request.
801
+ return auth.rateLimit.consume(options).catch((error) => {
802
+ console.error('[auth] rate-limit consume failed; failing open', error)
803
+ return null
804
+ })
805
+ }
806
+
807
+ /**
808
+ * Resolves the rate limit for an API-key request against a quota bucket.
809
+ * Most-specific wins: a per-key per-scope grant is the only thing that pierces a
810
+ * route's protective cap; the per-key `'*'` default does not (so a premium key
811
+ * can't silently blow past an expensive endpoint's ceiling). Per-key values live
812
+ * in the optional {@link ApiKey.ApiKey.rateLimits} map and stay `undefined` until a
813
+ * record source populates them, so until then only tiers 2/4/5/6 apply.
814
+ */
815
+ function resolveApiKeyLimit(options: resolveApiKeyLimit.Options): RateLimit.Limit {
816
+ const { apiKey, auth, policy, scope } = options
817
+ return (
818
+ apiKey.rateLimits?.[scope] ?? // 1. per-key, per-scope
819
+ policy?.rateLimit ?? // 2. route override (protective cap)
820
+ apiKey.rateLimits?.[Scope.wildcard] ?? // 3. per-key default
821
+ auth.apiKey?.rateLimits?.[scope] ?? // 4. config per-scope
822
+ auth.apiKey?.rateLimits?.[Scope.wildcard] ?? { perMinute: 100 } // 5. config default // 6. hardcoded fallback
823
+ )
824
+ }
825
+
826
+ declare namespace resolveApiKeyLimit {
827
+ type Options = {
828
+ /** Resolved API key (carries per-key overrides). */
829
+ apiKey: ApiKey.ApiKey
830
+ /** Auth context (carries config defaults). */
831
+ auth: Context
832
+ /** Route API-key policy (carries the per-route cap). */
833
+ policy: require.Policy['apiKey']
834
+ /** Quota bucket the request consumes. */
835
+ scope: string
836
+ }
837
+ }
838
+
839
+ function getApiKeyCredential(c: hono_Context): ApiKeyCredential | false | null {
840
+ const header = c.req.header('tempo-api-key')
841
+ if (header) return { token: header }
842
+
843
+ // `Authorization: Bearer` compatibility fallback. Must not consume or reject
844
+ // `Authorization: Payment` (MPP), which is a distinct credential — a request
845
+ // carrying only a payment scheme falls through to the deprecated lanes below
846
+ // (which find nothing) and resolves to no API-key credential.
847
+ const authorization = c.req.header('authorization')
848
+ if (authorization && !Credential.extractPaymentScheme(authorization)) {
849
+ const match = /^Bearer\s+(.+)$/i.exec(authorization)
850
+ if (!match?.[1]) return false
851
+ return { token: match[1] }
852
+ }
853
+
854
+ // Deprecated fallbacks carried over from the v1 API so existing integrations
855
+ // keep authenticating: the `x-api-key` header and the `key` query parameter.
856
+ const legacyHeader = c.req.header('x-api-key')
857
+ if (legacyHeader) return { token: legacyHeader }
858
+
859
+ const legacyQuery = c.req.query('key')
860
+ if (legacyQuery) return { token: legacyQuery }
861
+
862
+ return null
863
+ }
864
+
865
+ /**
866
+ * Remove the deprecated `key` auth query param from the request URL once it has
867
+ * been read for authentication. Route query schemas are strict
868
+ * (`z.strictObject`), so leaving `key` in the query string would make the
869
+ * downstream validator reject the request as `query_invalid`. Stripping it
870
+ * keeps the legacy `?key=...` auth lane working on strict-query endpoints
871
+ * (e.g. `GET /v1/tokens`).
872
+ */
873
+ function stripLegacyAuthQuery(c: hono_Context) {
874
+ const url = new URL(c.req.url)
875
+ if (!url.searchParams.has('key')) return
876
+ url.searchParams.delete('key')
877
+ c.req.raw = new Request(url, c.req.raw)
878
+ }
879
+
880
+ /**
881
+ * Reads an explicit `chainId`/`chain_id` query parameter, normalized to a
882
+ * numeric chain id. Returns `undefined` when absent or malformed (a malformed
883
+ * value falls through to the route's own validation). Mirrors the resolution in
884
+ * `App.create`'s unsupported-chain guard.
885
+ */
886
+ function explicitChainId(c: hono_Context): number | undefined {
887
+ const raw = c.req.query('chainId') ?? c.req.query('chain_id')
888
+ if (raw === undefined) return undefined
889
+ const parsed = Schema.ChainId.safeParse(raw)
890
+ return parsed.success ? parsed.data : undefined
891
+ }
892
+
893
+ function getRequestId(c: hono_Context) {
894
+ const variables = c.var as Record<string, unknown>
895
+ return typeof variables['requestId'] === 'string' ? variables['requestId'] : undefined
896
+ }
897
+
898
+ function hasPaymentCredential(c: hono_Context) {
899
+ const authorization = c.req.header('authorization')
900
+ if (!authorization) return false
901
+ return !!Credential.extractPaymentScheme(authorization)
902
+ }
903
+
904
+ function isPaymentEnabled(auth: Context, policy: require.Policy) {
905
+ return !!auth.mpp && !!policy.mpp
906
+ }
907
+
908
+ function jsonError(c: hono_Context, options: jsonError.Options) {
909
+ const requestId = getRequestId(c)
910
+ // Auth error envelopes embed per-request state and must not be cached.
911
+ c.header('Cache-Control', 'no-store')
912
+ c.set('errorCode', options.code)
913
+ return c.json(
914
+ {
915
+ error: {
916
+ code: options.code,
917
+ message: options.message,
918
+ },
919
+ ...(requestId === undefined ? {} : { requestId }),
920
+ },
921
+ options.status,
922
+ )
923
+ }
924
+
925
+ declare namespace jsonError {
926
+ type Options = {
927
+ code: string
928
+ message: string
929
+ status: 400 | 401 | 403 | 429
930
+ }
931
+ }
932
+
933
+ function payer(c: hono_Context) {
934
+ try {
935
+ return Credential.fromRequest(c.req.raw).source
936
+ } catch {
937
+ return undefined
938
+ }
939
+ }
940
+
941
+ function principalFromApiKey(apiKey: ApiKey.ApiKey): Principal {
942
+ return {
943
+ apiKey,
944
+ environment: apiKey.environment,
945
+ id: apiKey.id,
946
+ orgId: apiKey.orgId,
947
+ type: 'api_key',
948
+ }
949
+ }
950
+
951
+ function principalFromPublic(c: hono_Context): Principal {
952
+ const forwarded = c.req.header('x-forwarded-for')?.split(',')[0]?.trim()
953
+ return {
954
+ id:
955
+ c.req.header('cf-connecting-ip') ??
956
+ c.req.header('x-real-ip') ??
957
+ forwarded ??
958
+ c.req.header('x-test-client-ip') ??
959
+ 'anonymous',
960
+ type: 'public',
961
+ }
962
+ }
963
+
964
+ async function requirePayment<environment extends Environment>(
965
+ c: hono_Context<environment>,
966
+ next: () => Promise<void>,
967
+ options: PaymentAccess,
968
+ ) {
969
+ const middleware = options.auth.mpp?.charge(options.policy.mpp?.charge ?? {})
970
+ if (!middleware)
971
+ return jsonError(c, {
972
+ code: 'payment_required',
973
+ message: 'Payment required',
974
+ status: 429,
975
+ })
976
+
977
+ return middleware(c, async () => {
978
+ const payer_ = payer(c)
979
+ c.set('principal', {
980
+ ...options.principal,
981
+ payment: {
982
+ ...options.payment,
983
+ ...(payer_ === undefined ? {} : { payer: payer_ }),
984
+ },
985
+ })
986
+ await next()
987
+ })
988
+ }
989
+
990
+ function requirePaymentOrRateLimit<environment extends Environment>(
991
+ c: hono_Context<environment>,
992
+ next: () => Promise<void>,
993
+ options: RateLimitAccess,
994
+ ) {
995
+ if (isPaymentEnabled(options.auth, options.policy)) return requirePayment(c, next, options)
996
+
997
+ c.header(
998
+ 'Retry-After',
999
+ String(Math.max(options.rateLimit.reset - Math.ceil(Date.now() / 1_000), 1)),
1000
+ )
1001
+ return jsonError(c, {
1002
+ code: 'rate_limit_exceeded',
1003
+ message: 'Rate limit exceeded',
1004
+ status: 429,
1005
+ })
1006
+ }
1007
+
1008
+ function setRateLimitHeaders(c: hono_Context, result: RateLimit.Result) {
1009
+ c.header('RateLimit-Limit', String(result.limit))
1010
+ c.header('RateLimit-Remaining', String(result.remaining))
1011
+ c.header('RateLimit-Reset', String(result.reset))
1012
+ }