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,104 @@
1
+ /* eslint-disable */
2
+
3
+ // @ts-nocheck
4
+
5
+ // noinspection JSUnusedGlobalSymbols
6
+
7
+ // This file was automatically generated by TanStack Router.
8
+ // You should NOT make any changes in this file as it will be overwritten.
9
+ // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
10
+
11
+ import { Route as rootRouteImport } from './routes/__root'
12
+ import { Route as VerifiedTokensRouteImport } from './routes/verified-tokens'
13
+ import { Route as ApiKeysRouteImport } from './routes/api-keys'
14
+ import { Route as IndexRouteImport } from './routes/index'
15
+
16
+ const VerifiedTokensRoute = VerifiedTokensRouteImport.update({
17
+ id: '/verified-tokens',
18
+ path: '/verified-tokens',
19
+ getParentRoute: () => rootRouteImport,
20
+ } as any)
21
+ const ApiKeysRoute = ApiKeysRouteImport.update({
22
+ id: '/api-keys',
23
+ path: '/api-keys',
24
+ getParentRoute: () => rootRouteImport,
25
+ } as any)
26
+ const IndexRoute = IndexRouteImport.update({
27
+ id: '/',
28
+ path: '/',
29
+ getParentRoute: () => rootRouteImport,
30
+ } as any)
31
+
32
+ export interface FileRoutesByFullPath {
33
+ '/': typeof IndexRoute
34
+ '/api-keys': typeof ApiKeysRoute
35
+ '/verified-tokens': typeof VerifiedTokensRoute
36
+ }
37
+ export interface FileRoutesByTo {
38
+ '/': typeof IndexRoute
39
+ '/api-keys': typeof ApiKeysRoute
40
+ '/verified-tokens': typeof VerifiedTokensRoute
41
+ }
42
+ export interface FileRoutesById {
43
+ __root__: typeof rootRouteImport
44
+ '/': typeof IndexRoute
45
+ '/api-keys': typeof ApiKeysRoute
46
+ '/verified-tokens': typeof VerifiedTokensRoute
47
+ }
48
+ export interface FileRouteTypes {
49
+ fileRoutesByFullPath: FileRoutesByFullPath
50
+ fullPaths: '/' | '/api-keys' | '/verified-tokens'
51
+ fileRoutesByTo: FileRoutesByTo
52
+ to: '/' | '/api-keys' | '/verified-tokens'
53
+ id: '__root__' | '/' | '/api-keys' | '/verified-tokens'
54
+ fileRoutesById: FileRoutesById
55
+ }
56
+ export interface RootRouteChildren {
57
+ IndexRoute: typeof IndexRoute
58
+ ApiKeysRoute: typeof ApiKeysRoute
59
+ VerifiedTokensRoute: typeof VerifiedTokensRoute
60
+ }
61
+
62
+ declare module '@tanstack/react-router' {
63
+ interface FileRoutesByPath {
64
+ '/verified-tokens': {
65
+ id: '/verified-tokens'
66
+ path: '/verified-tokens'
67
+ fullPath: '/verified-tokens'
68
+ preLoaderRoute: typeof VerifiedTokensRouteImport
69
+ parentRoute: typeof rootRouteImport
70
+ }
71
+ '/api-keys': {
72
+ id: '/api-keys'
73
+ path: '/api-keys'
74
+ fullPath: '/api-keys'
75
+ preLoaderRoute: typeof ApiKeysRouteImport
76
+ parentRoute: typeof rootRouteImport
77
+ }
78
+ '/': {
79
+ id: '/'
80
+ path: '/'
81
+ fullPath: '/'
82
+ preLoaderRoute: typeof IndexRouteImport
83
+ parentRoute: typeof rootRouteImport
84
+ }
85
+ }
86
+ }
87
+
88
+ const rootRouteChildren: RootRouteChildren = {
89
+ IndexRoute: IndexRoute,
90
+ ApiKeysRoute: ApiKeysRoute,
91
+ VerifiedTokensRoute: VerifiedTokensRoute,
92
+ }
93
+ export const routeTree = rootRouteImport
94
+ ._addFileChildren(rootRouteChildren)
95
+ ._addFileTypes<FileRouteTypes>()
96
+
97
+ import type { getRouter } from './router.tsx'
98
+ import type { createStart } from '@tanstack/react-start'
99
+ declare module '@tanstack/react-start' {
100
+ interface Register {
101
+ ssr: true
102
+ router: Awaited<ReturnType<typeof getRouter>>
103
+ }
104
+ }
@@ -0,0 +1,25 @@
1
+ import { createRouter } from '@tanstack/react-router'
2
+
3
+ import { queryClient } from './lib/query-client'
4
+ import { routeTree } from './routeTree.gen'
5
+
6
+ // The shell carries a `<base href="<mount>/">`, so derive the router `basepath`
7
+ // from it to keep routing/links under the mount. The Vite dev shell has none,
8
+ // so fall back to the root (`document.baseURI` would be the current URL there).
9
+ function getBasepath() {
10
+ if (typeof document === 'undefined') return '/'
11
+ const base = document.querySelector('base')
12
+ if (!base) return '/'
13
+ return new URL(base.href).pathname.replace(/\/$/, '') || '/'
14
+ }
15
+
16
+ export function getRouter() {
17
+ return createRouter({
18
+ basepath: getBasepath(),
19
+ context: { queryClient },
20
+ defaultPreload: 'intent',
21
+ defaultPreloadStaleTime: 0,
22
+ routeTree,
23
+ scrollRestoration: true,
24
+ })
25
+ }
@@ -0,0 +1,219 @@
1
+ import {
2
+ createRootRouteWithContext,
3
+ HeadContent,
4
+ Link,
5
+ Outlet,
6
+ Scripts,
7
+ useMatchRoute,
8
+ } from '@tanstack/react-router'
9
+ import type { QueryClient } from '@tanstack/react-query'
10
+ import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client'
11
+ import { type ComponentType } from 'react'
12
+ import { Button, Identicon } from 'regen-ui'
13
+ import { BadgeCheck, KeyRound, LogOut } from 'lucide-react'
14
+ import { WagmiProvider } from 'wagmi'
15
+
16
+ import appCss from '../styles.css?url'
17
+ import { TempoLogo, TempoMark } from '../components/TempoLogo'
18
+ import { identiconAddress } from '../components/MobileProfile'
19
+ import * as Config from '../lib/config'
20
+ import { persistOptions, queryClient } from '../lib/query-client'
21
+ import { config as wagmiConfig } from '../lib/wagmi'
22
+
23
+ export type RouterContext = { queryClient: QueryClient }
24
+
25
+ const favicon = `data:image/svg+xml,${encodeURIComponent(
26
+ '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 269 269"><rect width="269" height="269" fill="#000"/><path d="M123.273 190.794H93.445L121.09 105.318H85.7334L93.445 80.2642H191.95L184.238 105.318H150.773L123.273 190.794Z" fill="#fff"/></svg>',
27
+ )}`
28
+
29
+ export const Route = createRootRouteWithContext<RouterContext>()({
30
+ head: () => ({
31
+ links: [
32
+ { rel: 'stylesheet', href: appCss },
33
+ { rel: 'icon', type: 'image/svg+xml', href: favicon },
34
+ ],
35
+ meta: [
36
+ { charSet: 'utf-8' },
37
+ { content: 'width=device-width, initial-scale=1', name: 'viewport' },
38
+ { title: 'Tempo API Admin' },
39
+ ],
40
+ }),
41
+ shellComponent: RootDocument,
42
+ component: AdminShell,
43
+ pendingComponent: () => <Loading />,
44
+ })
45
+
46
+ function RootDocument({ children }: { children: React.ReactNode }) {
47
+ return (
48
+ <html lang="en" data-theme="dark" data-regen-radius="none">
49
+ <head>
50
+ <HeadContent />
51
+ </head>
52
+ <body>
53
+ <div id="root">{children}</div>
54
+ <Scripts />
55
+ </body>
56
+ </html>
57
+ )
58
+ }
59
+
60
+ function Loading() {
61
+ return (
62
+ <div className="flex min-h-screen items-center justify-center text-foreground-secondary">
63
+ Loading…
64
+ </div>
65
+ )
66
+ }
67
+
68
+ function AdminShell() {
69
+ return (
70
+ <WagmiProvider config={wagmiConfig}>
71
+ <PersistQueryClientProvider client={queryClient} persistOptions={persistOptions}>
72
+ <Admin />
73
+ </PersistQueryClientProvider>
74
+ </WagmiProvider>
75
+ )
76
+ }
77
+
78
+ function Admin() {
79
+ const { data: config, error, isPending } = Config.use()
80
+
81
+ if (isPending) return null
82
+ if (error)
83
+ return (
84
+ <div className="flex min-h-screen items-center justify-center text-foreground">
85
+ {error.message}
86
+ </div>
87
+ )
88
+ if (!config.auth.identity) return <SignInRequired loginUrl={config.auth.loginUrl} />
89
+
90
+ const { email } = config.auth.identity
91
+ const { logoutUrl } = config.auth
92
+ return (
93
+ <div className="relative flex min-h-dvh min-w-[360px] bg-background max-md:h-dvh max-md:flex-col max-md:overflow-hidden">
94
+ <div className="flex-1 bg-background max-md:hidden" />
95
+ <Sidebar email={email} logoutUrl={logoutUrl} />
96
+ <main className="flex w-full max-w-[1024px] flex-col px-4 pt-5 pb-10 max-md:min-h-0 max-md:flex-1 max-md:overflow-y-auto sm:px-8">
97
+ <Outlet />
98
+ </main>
99
+ <div className="flex-1 max-md:hidden" />
100
+ <MobileNav />
101
+ </div>
102
+ )
103
+ }
104
+
105
+ const nav = [
106
+ { exact: false, href: '/api-keys', icon: KeyRound, label: 'API Keys' },
107
+ { exact: false, href: '/verified-tokens', icon: BadgeCheck, label: 'Verified Tokens' },
108
+ ] as const
109
+
110
+ function Sidebar(props: { email: string; logoutUrl?: string }) {
111
+ return (
112
+ <aside className="sticky top-0 flex h-dvh w-[260px] shrink-0 flex-col gap-6 border-r border-border bg-background px-2.5 pb-5 max-lg:w-[68px] max-lg:items-center max-lg:px-1.5 max-md:hidden">
113
+ <Link
114
+ aria-label="Tempo API Admin"
115
+ className="-mb-1 inline-flex rounded-button px-3 pt-5 pb-3"
116
+ to="/"
117
+ >
118
+ <TempoLogo className="mt-2 h-4 w-auto text-foreground max-lg:hidden" />
119
+ <TempoMark className="mt-2 h-4 w-auto text-foreground lg:hidden" />
120
+ </Link>
121
+ <nav aria-label="Main" className="flex flex-1 flex-col gap-1">
122
+ {nav.map((item) => (
123
+ <NavItem
124
+ exact={item.exact}
125
+ href={item.href}
126
+ icon={item.icon}
127
+ key={item.href}
128
+ label={item.label}
129
+ />
130
+ ))}
131
+ </nav>
132
+ <ProfileCard email={props.email} logoutUrl={props.logoutUrl} />
133
+ </aside>
134
+ )
135
+ }
136
+
137
+ function NavItem(props: {
138
+ exact: boolean
139
+ href: string
140
+ icon: ComponentType<{ className?: string }>
141
+ label: string
142
+ }) {
143
+ const { exact, href, icon: Icon, label } = props
144
+ const matchRoute = useMatchRoute()
145
+ const active = !!matchRoute({ to: href, fuzzy: !exact })
146
+
147
+ return (
148
+ <Link
149
+ aria-current={active ? 'page' : undefined}
150
+ aria-label={label}
151
+ className="flex items-center gap-3 rounded-button px-3 py-3 text-label-16 text-foreground-secondary hover:bg-surface-hover data-active:font-semibold data-active:text-foreground max-lg:justify-center max-lg:gap-0 max-lg:p-2.5"
152
+ data-active={active || undefined}
153
+ to={href}
154
+ >
155
+ <Icon className="size-5" />
156
+ <span className="max-lg:hidden">{label}</span>
157
+ </Link>
158
+ )
159
+ }
160
+
161
+ function ProfileCard(props: { email: string; logoutUrl?: string }) {
162
+ return (
163
+ <div className="flex items-center gap-3 p-2.5 max-lg:justify-center max-lg:p-2">
164
+ <Identicon address={identiconAddress(props.email)} className="size-9 shrink-0" size={36} />
165
+ <p className="flex-1 truncate text-label-14 text-foreground max-lg:hidden">{props.email}</p>
166
+ {props.logoutUrl ? (
167
+ <a
168
+ aria-label="Sign out"
169
+ className="shrink-0 text-foreground-secondary hover:text-foreground max-lg:hidden"
170
+ href={props.logoutUrl}
171
+ >
172
+ <LogOut className="size-[18px]" />
173
+ </a>
174
+ ) : null}
175
+ </div>
176
+ )
177
+ }
178
+
179
+ /** Bottom navigation shown below the md breakpoint, in place of the sidebar. */
180
+ function MobileNav() {
181
+ const matchRoute = useMatchRoute()
182
+ return (
183
+ <nav
184
+ aria-label="Main"
185
+ className="z-10 flex w-full shrink-0 items-center justify-around border-t border-border bg-background pb-[env(safe-area-inset-bottom)] md:hidden"
186
+ >
187
+ {nav.map((item) => {
188
+ const active = !!matchRoute({ to: item.href, fuzzy: !item.exact })
189
+ const Icon = item.icon
190
+ return (
191
+ <Link
192
+ aria-current={active ? 'page' : undefined}
193
+ aria-label={item.label}
194
+ className="flex flex-1 flex-col items-center gap-1 py-3 text-foreground-secondary data-active:text-foreground"
195
+ data-active={active || undefined}
196
+ key={item.href}
197
+ to={item.href}
198
+ >
199
+ <Icon className="size-5" />
200
+ <span className="text-label-12">{item.label}</span>
201
+ </Link>
202
+ )
203
+ })}
204
+ </nav>
205
+ )
206
+ }
207
+
208
+ function SignInRequired({ loginUrl }: { loginUrl?: string }) {
209
+ return (
210
+ <div className="flex min-h-screen flex-col items-center justify-center gap-3 bg-background text-foreground">
211
+ <p className="font-medium">Sign in required</p>
212
+ {loginUrl ? (
213
+ <Button render={<a href={loginUrl} />}>Sign in</Button>
214
+ ) : (
215
+ <p className="text-sm text-foreground-secondary">Your session has expired.</p>
216
+ )}
217
+ </div>
218
+ )
219
+ }
@@ -0,0 +1,329 @@
1
+ import { createFileRoute } from '@tanstack/react-router'
2
+ import { type ReactNode, useState } from 'react'
3
+ import { Button, Fields, Input, Rows, Select, useCopy } from 'regen-ui'
4
+ import { Check, ChevronRight, Copy as CopyIcon, Plus, Trash2, X } from 'lucide-react'
5
+
6
+ import { Card, Notice, Page } from '../components/Page'
7
+ import { Checkbox } from '../components/Checkbox'
8
+ import { Dialog } from '../components/Dialog'
9
+ import * as ApiKeys from '../lib/api-keys'
10
+ import * as Scopes from '../lib/scopes'
11
+
12
+ export const Route = createFileRoute('/api-keys')({
13
+ loader: ({ context }) =>
14
+ Promise.all([
15
+ context.queryClient.prefetchQuery(ApiKeys.get.options()),
16
+ context.queryClient.prefetchQuery(Scopes.get.options()),
17
+ ]),
18
+ component: RouteComponent,
19
+ })
20
+
21
+ function RouteComponent() {
22
+ const [open, setOpen] = useState(false)
23
+ const [environment, setEnvironment] = useState<'production' | 'sandbox'>('production')
24
+ const keys = ApiKeys.get.useQuery()
25
+ const provision = ApiKeys.provision.useMutation()
26
+
27
+ function onSubmit(form: FormData) {
28
+ const field = (key: string) => {
29
+ const value = form.get(key)
30
+ return typeof value === 'string' ? value.trim() : ''
31
+ }
32
+ const name = field('name')
33
+ const rateLimit = field('rateLimit')
34
+ const perMinute = rateLimit ? Number(rateLimit) : undefined
35
+ const environment = field('environment') === 'sandbox' ? 'sandbox' : 'production'
36
+ const scopes = form.getAll('scopes').filter((v): v is string => typeof v === 'string')
37
+ if (scopes.length === 0) {
38
+ alert('Select at least one scope.')
39
+ return
40
+ }
41
+ provision.mutate(
42
+ {
43
+ environment,
44
+ scopes: scopes as ApiKeys.provision.Input['scopes'],
45
+ ...(name ? { name } : {}),
46
+ ...(perMinute === undefined ? {} : { rateLimits: { '*': { perMinute } } }),
47
+ },
48
+ { onError: (error) => alert(error.message), onSuccess: () => setOpen(false) },
49
+ )
50
+ }
51
+
52
+ return (
53
+ <Dialog.Root onOpenChange={setOpen} open={open}>
54
+ <Page
55
+ title="API Keys"
56
+ description="Manage keys that authenticate requests to the API"
57
+ action={
58
+ <>
59
+ <Select
60
+ aria-label="Filter keys by environment"
61
+ items={[
62
+ { label: 'Production', value: 'production' },
63
+ { label: 'Sandbox', value: 'sandbox' },
64
+ ]}
65
+ onChange={(value) => setEnvironment(value as 'production' | 'sandbox')}
66
+ value={environment}
67
+ />
68
+ <Dialog.Trigger
69
+ render={
70
+ <Button variant="primary">
71
+ <Plus className="size-4" />
72
+ Provision a key
73
+ </Button>
74
+ }
75
+ />
76
+ </>
77
+ }
78
+ >
79
+ {provision.data ? (
80
+ <TokenBanner onDismiss={() => provision.reset()} token={provision.data.token} />
81
+ ) : null}
82
+ <KeyList
83
+ environment={environment}
84
+ error={keys.error}
85
+ keys={(keys.data?.data ?? []).filter(
86
+ (key) => (key.environment ?? 'production') === environment,
87
+ )}
88
+ loading={keys.isPending}
89
+ />
90
+ <Dialog.Popup>
91
+ <ProvisionForm onSubmit={onSubmit} pending={provision.isPending} />
92
+ </Dialog.Popup>
93
+ </Page>
94
+ </Dialog.Root>
95
+ )
96
+ }
97
+
98
+ function TokenBanner(props: { onDismiss: () => void; token: string }) {
99
+ const { copy, notifying } = useCopy()
100
+ return (
101
+ <Card className="flex flex-col gap-3 p-4">
102
+ <div className="flex items-start justify-between gap-3">
103
+ <div className="flex flex-col gap-1">
104
+ <span className="text-label-14 text-foreground">Key provisioned</span>
105
+ <span className="text-label-12 text-foreground-secondary">
106
+ Copy this token now, it won't be shown again.
107
+ </span>
108
+ </div>
109
+ <Button.Icon
110
+ className="-mt-2 -mr-2"
111
+ onClick={props.onDismiss}
112
+ size="lg"
113
+ title="Dismiss"
114
+ variant="plain"
115
+ >
116
+ <X />
117
+ </Button.Icon>
118
+ </div>
119
+
120
+ <button
121
+ aria-label={`Copy ${props.token}`}
122
+ className="group flex w-full cursor-pointer items-center gap-3 border border-border bg-secondary px-3 py-2.5 text-left transition-colors hover:bg-secondary-hover"
123
+ onClick={() => copy(props.token)}
124
+ type="button"
125
+ >
126
+ <code className="min-w-0 flex-1 truncate text-copy-14 text-foreground">{props.token}</code>
127
+ <span className="text-foreground-secondary group-hover:text-foreground">
128
+ {notifying ? <Check className="size-4" /> : <CopyIcon className="size-4" />}
129
+ </span>
130
+ </button>
131
+ </Card>
132
+ )
133
+ }
134
+
135
+ function ProvisionForm(props: { onSubmit: (form: FormData) => void; pending: boolean }) {
136
+ const scopes = Scopes.get.useQuery()
137
+ return (
138
+ <>
139
+ <Dialog.Title>Provision a Key</Dialog.Title>
140
+ <Dialog.Description>Create a key that authenticates requests to the API</Dialog.Description>
141
+ <form
142
+ className="mt-6 flex flex-col gap-8"
143
+ onSubmit={(event) => {
144
+ event.preventDefault()
145
+ props.onSubmit(new FormData(event.currentTarget))
146
+ }}
147
+ >
148
+ <Fields className="gap-6" variant="default">
149
+ <Input label="Name" name="name" placeholder="e.g. production-api" />
150
+ <div className="flex flex-col gap-2">
151
+ <span className="label-13 text-foreground-secondary">Environment</span>
152
+ <select
153
+ className="flex h-[40px] w-full items-center gap-[10px] rounded-body border-[2px] border-transparent bg-secondary px-[12px] copy-14 text-foreground outline-none hover:bg-secondary-hover active:bg-secondary-active focus-visible:border-focus focus-visible:bg-secondary-hover"
154
+ defaultValue="production"
155
+ name="environment"
156
+ >
157
+ <option value="production">Production</option>
158
+ <option value="sandbox">Sandbox</option>
159
+ </select>
160
+ </div>
161
+ <Input
162
+ defaultValue="100"
163
+ label="Rate limit"
164
+ min={0}
165
+ name="rateLimit"
166
+ placeholder="Requests per minute"
167
+ suffix="req/min"
168
+ type="number"
169
+ />
170
+ <div className="flex flex-col gap-2">
171
+ <span className="label-13 text-foreground-secondary">Scopes</span>
172
+ {scopes.isPending ? (
173
+ <span className="text-label-13 text-foreground-secondary">Loading scopes…</span>
174
+ ) : scopes.error ? (
175
+ <span className="text-label-13 text-negative">{scopes.error.message}</span>
176
+ ) : (
177
+ <Rows className="border border-border" variant="surface">
178
+ {(scopes.data?.data ?? []).map((entry) => (
179
+ <Rows.Row
180
+ key={entry.scope}
181
+ className="min-h-[56px] py-3"
182
+ render={<label className="cursor-pointer" />}
183
+ style={{ height: 'auto' }}
184
+ >
185
+ <Checkbox
186
+ defaultChecked={entry.scope === 'data:read'}
187
+ name="scopes"
188
+ value={entry.scope}
189
+ />
190
+ <span className="flex min-w-0 flex-col gap-0.5">
191
+ <span className="text-label-13 text-foreground">{entry.scope}</span>
192
+ <span className="text-label-12 text-foreground-secondary">
193
+ {entry.description}
194
+ </span>
195
+ </span>
196
+ </Rows.Row>
197
+ ))}
198
+ </Rows>
199
+ )}
200
+ </div>
201
+ </Fields>
202
+ <div className="flex justify-end gap-2">
203
+ <Dialog.Close render={<Button type="button">Cancel</Button>} />
204
+ <Button loading={props.pending} type="submit" variant="primary">
205
+ Provision key
206
+ </Button>
207
+ </div>
208
+ </form>
209
+ </>
210
+ )
211
+ }
212
+
213
+ function KeyList(props: {
214
+ environment: 'production' | 'sandbox'
215
+ error: Error | null
216
+ keys: readonly ApiKeys.ApiKey[]
217
+ loading: boolean
218
+ }) {
219
+ return (
220
+ <Card>
221
+ {props.loading ? (
222
+ <Notice>Loading keys…</Notice>
223
+ ) : props.error ? (
224
+ <Notice>{props.error.message}</Notice>
225
+ ) : props.keys.length === 0 ? (
226
+ <Notice>No {props.environment} keys yet.</Notice>
227
+ ) : (
228
+ <Rows variant="surface">
229
+ {props.keys.map((key) => (
230
+ <KeyRow key={key.id} record={key} />
231
+ ))}
232
+ </Rows>
233
+ )}
234
+ </Card>
235
+ )
236
+ }
237
+
238
+ function KeyRow(props: { record: ApiKeys.ApiKey }) {
239
+ const { record } = props
240
+ const [expanded, setExpanded] = useState(false)
241
+ const revoke = ApiKeys.revoke.useMutation()
242
+ const perMinute = record.rateLimits?.['*']?.perMinute
243
+
244
+ function onRevoke() {
245
+ if (!confirm(`Revoke key ${record.name ?? record.id}? This can't be undone.`)) return
246
+ revoke.mutate(record.id, { onError: (error) => alert(error.message) })
247
+ }
248
+
249
+ return (
250
+ <>
251
+ <Rows.Row
252
+ className="min-h-[44px] py-2"
253
+ onClick={() => setExpanded((value) => !value)}
254
+ style={{ height: 'auto' }}
255
+ >
256
+ <span className="flex min-w-0 flex-1 items-baseline gap-2">
257
+ <span className="truncate text-label-14 text-foreground">
258
+ {record.name ?? 'Unnamed key'}
259
+ </span>
260
+ {record.environment === 'sandbox' ? (
261
+ <span className="shrink-0 rounded-none bg-warning-subtle px-2 py-0.5 text-label-12 font-normal text-warning">
262
+ sandbox
263
+ </span>
264
+ ) : null}
265
+ </span>
266
+ <span className="shrink-0 text-label-12 text-foreground-secondary">
267
+ {formatDate(record.createdAt)}
268
+ </span>
269
+ <ChevronRight
270
+ aria-hidden
271
+ className="size-4 shrink-0 text-foreground-secondary opacity-70 transition-transform data-[open]:rotate-90"
272
+ data-open={expanded ? '' : undefined}
273
+ />
274
+ </Rows.Row>
275
+ {expanded ? (
276
+ <Rows.Panel>
277
+ <dl className="flex flex-wrap gap-x-10 gap-y-4 px-5 py-4">
278
+ <Detail label="Key ID">{record.id}</Detail>
279
+ <Detail label="Environment">{record.environment ?? 'production'}</Detail>
280
+ <Detail label="Rate limit">
281
+ {perMinute === undefined ? 'Inherited' : `${perMinute} req/min`}
282
+ </Detail>
283
+ <Detail label="Token">···{record.tokenLast4}</Detail>
284
+ <Detail label="Provisioned by">{record.createdBy ?? '—'}</Detail>
285
+ {record.expiresAt ? (
286
+ <Detail label="Expires">{formatDate(record.expiresAt)}</Detail>
287
+ ) : null}
288
+ <Detail className="w-full" label="Scopes">
289
+ {record.scopes.length === 0 ? (
290
+ '—'
291
+ ) : (
292
+ <span className="flex flex-wrap gap-x-3 gap-y-1">
293
+ {record.scopes.map((scope) => (
294
+ <span key={scope} className="text-label-13 text-foreground">
295
+ {scope}
296
+ </span>
297
+ ))}
298
+ </span>
299
+ )}
300
+ </Detail>
301
+ </dl>
302
+ <div className="flex justify-end border-t border-border px-5 py-3">
303
+ <Button loading={revoke.isPending} onClick={onRevoke} size="small" variant="negative">
304
+ <Trash2 className="size-4" />
305
+ Revoke key
306
+ </Button>
307
+ </div>
308
+ </Rows.Panel>
309
+ ) : null}
310
+ </>
311
+ )
312
+ }
313
+
314
+ function Detail(props: { children: ReactNode; className?: string; label: ReactNode }) {
315
+ return (
316
+ <div className={`flex min-w-0 flex-col gap-1 ${props.className ?? ''}`}>
317
+ <dt className="text-label-12 text-foreground-secondary">{props.label}</dt>
318
+ <dd className="min-w-0 text-label-13 text-foreground">{props.children}</dd>
319
+ </div>
320
+ )
321
+ }
322
+
323
+ function formatDate(iso: string) {
324
+ return new Date(iso).toLocaleDateString(undefined, {
325
+ day: 'numeric',
326
+ month: 'short',
327
+ year: 'numeric',
328
+ })
329
+ }
@@ -0,0 +1,7 @@
1
+ import { createFileRoute, redirect } from '@tanstack/react-router'
2
+
3
+ export const Route = createFileRoute('/')({
4
+ beforeLoad: () => {
5
+ throw redirect({ to: '/api-keys' })
6
+ },
7
+ })