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.
- package/LICENSE +21 -0
- package/README.md +119 -0
- package/dist/ApiKey.d.ts +65 -0
- package/dist/ApiKey.d.ts.map +1 -0
- package/dist/ApiKey.js +105 -0
- package/dist/ApiKey.js.map +1 -0
- package/dist/ApiKeySource.d.ts +166 -0
- package/dist/ApiKeySource.d.ts.map +1 -0
- package/dist/ApiKeySource.js +234 -0
- package/dist/ApiKeySource.js.map +1 -0
- package/dist/App.d.ts +19317 -0
- package/dist/App.d.ts.map +1 -0
- package/dist/App.js +612 -0
- package/dist/App.js.map +1 -0
- package/dist/Assets.d.ts +44 -0
- package/dist/Assets.d.ts.map +1 -0
- package/dist/Assets.js +23 -0
- package/dist/Assets.js.map +1 -0
- package/dist/Auth.d.ts +2 -0
- package/dist/Auth.d.ts.map +1 -0
- package/dist/Auth.js +2 -0
- package/dist/Auth.js.map +1 -0
- package/dist/Cli.d.ts +21 -0
- package/dist/Cli.d.ts.map +1 -0
- package/dist/Cli.js +13 -0
- package/dist/Cli.js.map +1 -0
- package/dist/Client.d.ts +20 -0
- package/dist/Client.d.ts.map +1 -0
- package/dist/Client.js +24 -0
- package/dist/Client.js.map +1 -0
- package/dist/Metrics.d.ts +70 -0
- package/dist/Metrics.d.ts.map +1 -0
- package/dist/Metrics.js +91 -0
- package/dist/Metrics.js.map +1 -0
- package/dist/Scope.d.ts +35 -0
- package/dist/Scope.d.ts.map +1 -0
- package/dist/Scope.js +38 -0
- package/dist/Scope.js.map +1 -0
- package/dist/Webhooks.d.ts +79 -0
- package/dist/Webhooks.d.ts.map +1 -0
- package/dist/Webhooks.js +107 -0
- package/dist/Webhooks.js.map +1 -0
- package/dist/admin/Access.d.ts +41 -0
- package/dist/admin/Access.d.ts.map +1 -0
- package/dist/admin/Access.js +75 -0
- package/dist/admin/Access.js.map +1 -0
- package/dist/admin/App.d.ts +1221 -0
- package/dist/admin/App.d.ts.map +1 -0
- package/dist/admin/App.js +113 -0
- package/dist/admin/App.js.map +1 -0
- package/dist/admin/apps/api-keys.d.ts +209 -0
- package/dist/admin/apps/api-keys.d.ts.map +1 -0
- package/dist/admin/apps/api-keys.js +129 -0
- package/dist/admin/apps/api-keys.js.map +1 -0
- package/dist/admin/apps/scopes.d.ts +46 -0
- package/dist/admin/apps/scopes.d.ts.map +1 -0
- package/dist/admin/apps/scopes.js +41 -0
- package/dist/admin/apps/scopes.js.map +1 -0
- package/dist/admin/apps/verified-tokens.d.ts +1206 -0
- package/dist/admin/apps/verified-tokens.d.ts.map +1 -0
- package/dist/admin/apps/verified-tokens.js +485 -0
- package/dist/admin/apps/verified-tokens.js.map +1 -0
- package/dist/admin/index.d.ts +3 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +3 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/ui.generated.d.ts +11 -0
- package/dist/admin/ui.generated.d.ts.map +1 -0
- package/dist/admin/ui.generated.js +43 -0
- package/dist/admin/ui.generated.js.map +1 -0
- package/dist/apps/activities.d.ts +18725 -0
- package/dist/apps/activities.d.ts.map +1 -0
- package/dist/apps/activities.js +3138 -0
- package/dist/apps/activities.js.map +1 -0
- package/dist/apps/addresses.d.ts +3662 -0
- package/dist/apps/addresses.d.ts.map +1 -0
- package/dist/apps/addresses.js +22 -0
- package/dist/apps/addresses.js.map +1 -0
- package/dist/apps/balances.d.ts +342 -0
- package/dist/apps/balances.d.ts.map +1 -0
- package/dist/apps/balances.js +393 -0
- package/dist/apps/balances.js.map +1 -0
- package/dist/apps/blocks.d.ts +931 -0
- package/dist/apps/blocks.d.ts.map +1 -0
- package/dist/apps/blocks.js +689 -0
- package/dist/apps/blocks.js.map +1 -0
- package/dist/apps/coingecko.d.ts +1053 -0
- package/dist/apps/coingecko.d.ts.map +1 -0
- package/dist/apps/coingecko.js +1015 -0
- package/dist/apps/coingecko.js.map +1 -0
- package/dist/apps/exchanges.d.ts +3000 -0
- package/dist/apps/exchanges.d.ts.map +1 -0
- package/dist/apps/exchanges.js +3570 -0
- package/dist/apps/exchanges.js.map +1 -0
- package/dist/apps/fee-amm.d.ts +640 -0
- package/dist/apps/fee-amm.d.ts.map +1 -0
- package/dist/apps/fee-amm.js +815 -0
- package/dist/apps/fee-amm.js.map +1 -0
- package/dist/apps/indexer.d.ts +116 -0
- package/dist/apps/indexer.d.ts.map +1 -0
- package/dist/apps/indexer.js +270 -0
- package/dist/apps/indexer.js.map +1 -0
- package/dist/apps/receipts.d.ts +1438 -0
- package/dist/apps/receipts.d.ts.map +1 -0
- package/dist/apps/receipts.js +874 -0
- package/dist/apps/receipts.js.map +1 -0
- package/dist/apps/rpc.d.ts +173 -0
- package/dist/apps/rpc.d.ts.map +1 -0
- package/dist/apps/rpc.js +191 -0
- package/dist/apps/rpc.js.map +1 -0
- package/dist/apps/tokenlist.d.ts +192 -0
- package/dist/apps/tokenlist.d.ts.map +1 -0
- package/dist/apps/tokenlist.js +153 -0
- package/dist/apps/tokenlist.js.map +1 -0
- package/dist/apps/tokens.d.ts +2246 -0
- package/dist/apps/tokens.d.ts.map +1 -0
- package/dist/apps/tokens.js +1789 -0
- package/dist/apps/tokens.js.map +1 -0
- package/dist/apps/transactions.d.ts +2327 -0
- package/dist/apps/transactions.d.ts.map +1 -0
- package/dist/apps/transactions.js +975 -0
- package/dist/apps/transactions.js.map +1 -0
- package/dist/apps/transfers.d.ts +589 -0
- package/dist/apps/transfers.d.ts.map +1 -0
- package/dist/apps/transfers.js +769 -0
- package/dist/apps/transfers.js.map +1 -0
- package/dist/apps/verified-tokens.d.ts +544 -0
- package/dist/apps/verified-tokens.d.ts.map +1 -0
- package/dist/apps/verified-tokens.js +234 -0
- package/dist/apps/verified-tokens.js.map +1 -0
- package/dist/apps/webhooks.d.ts +7684 -0
- package/dist/apps/webhooks.d.ts.map +1 -0
- package/dist/apps/webhooks.js +2112 -0
- package/dist/apps/webhooks.js.map +1 -0
- package/dist/cloudflare.d.ts +32 -0
- package/dist/cloudflare.d.ts.map +1 -0
- package/dist/cloudflare.js +45 -0
- package/dist/cloudflare.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/Auth.d.ts +298 -0
- package/dist/internal/Auth.d.ts.map +1 -0
- package/dist/internal/Auth.js +618 -0
- package/dist/internal/Auth.js.map +1 -0
- package/dist/internal/Cache.d.ts +74 -0
- package/dist/internal/Cache.d.ts.map +1 -0
- package/dist/internal/Cache.js +215 -0
- package/dist/internal/Cache.js.map +1 -0
- package/dist/internal/Cursor.d.ts +79 -0
- package/dist/internal/Cursor.d.ts.map +1 -0
- package/dist/internal/Cursor.js +116 -0
- package/dist/internal/Cursor.js.map +1 -0
- package/dist/internal/EdgeCache.d.ts +59 -0
- package/dist/internal/EdgeCache.d.ts.map +1 -0
- package/dist/internal/EdgeCache.js +174 -0
- package/dist/internal/EdgeCache.js.map +1 -0
- package/dist/internal/Log.d.ts +104 -0
- package/dist/internal/Log.d.ts.map +1 -0
- package/dist/internal/Log.js +118 -0
- package/dist/internal/Log.js.map +1 -0
- package/dist/internal/MetricSink.d.ts +65 -0
- package/dist/internal/MetricSink.d.ts.map +1 -0
- package/dist/internal/MetricSink.js +146 -0
- package/dist/internal/MetricSink.js.map +1 -0
- package/dist/internal/Mpp.d.ts +71 -0
- package/dist/internal/Mpp.d.ts.map +1 -0
- package/dist/internal/Mpp.js +105 -0
- package/dist/internal/Mpp.js.map +1 -0
- package/dist/internal/OpenApi.d.ts +313 -0
- package/dist/internal/OpenApi.d.ts.map +1 -0
- package/dist/internal/OpenApi.fonts.d.ts +2 -0
- package/dist/internal/OpenApi.fonts.d.ts.map +1 -0
- package/dist/internal/OpenApi.fonts.js +39 -0
- package/dist/internal/OpenApi.fonts.js.map +1 -0
- package/dist/internal/OpenApi.js +335 -0
- package/dist/internal/OpenApi.js.map +1 -0
- package/dist/internal/OpenApi.logo.d.ts +6 -0
- package/dist/internal/OpenApi.logo.d.ts.map +1 -0
- package/dist/internal/OpenApi.logo.js +19 -0
- package/dist/internal/OpenApi.logo.js.map +1 -0
- package/dist/internal/Path.d.ts +5 -0
- package/dist/internal/Path.d.ts.map +1 -0
- package/dist/internal/Path.js +16 -0
- package/dist/internal/Path.js.map +1 -0
- package/dist/internal/RateLimit.d.ts +60 -0
- package/dist/internal/RateLimit.d.ts.map +1 -0
- package/dist/internal/RateLimit.js +48 -0
- package/dist/internal/RateLimit.js.map +1 -0
- package/dist/internal/Response.d.ts +132 -0
- package/dist/internal/Response.d.ts.map +1 -0
- package/dist/internal/Response.js +72 -0
- package/dist/internal/Response.js.map +1 -0
- package/dist/internal/Schema.d.ts +257 -0
- package/dist/internal/Schema.d.ts.map +1 -0
- package/dist/internal/Schema.js +360 -0
- package/dist/internal/Schema.js.map +1 -0
- package/dist/internal/Store.d.ts +204 -0
- package/dist/internal/Store.d.ts.map +1 -0
- package/dist/internal/Store.js +291 -0
- package/dist/internal/Store.js.map +1 -0
- package/dist/internal/Tidx.d.ts +120 -0
- package/dist/internal/Tidx.d.ts.map +1 -0
- package/dist/internal/Tidx.js +156 -0
- package/dist/internal/Tidx.js.map +1 -0
- package/dist/internal/Timing.d.ts +22 -0
- package/dist/internal/Timing.d.ts.map +1 -0
- package/dist/internal/Timing.js +32 -0
- package/dist/internal/Timing.js.map +1 -0
- package/dist/internal/Ttl.d.ts +25 -0
- package/dist/internal/Ttl.d.ts.map +1 -0
- package/dist/internal/Ttl.js +49 -0
- package/dist/internal/Ttl.js.map +1 -0
- package/dist/internal/Value.d.ts +47 -0
- package/dist/internal/Value.d.ts.map +1 -0
- package/dist/internal/Value.js +122 -0
- package/dist/internal/Value.js.map +1 -0
- package/dist/internal/VerifiedTokens.d.ts +228 -0
- package/dist/internal/VerifiedTokens.d.ts.map +1 -0
- package/dist/internal/VerifiedTokens.js +450 -0
- package/dist/internal/VerifiedTokens.js.map +1 -0
- package/dist/internal/Viem.d.ts +2082 -0
- package/dist/internal/Viem.d.ts.map +1 -0
- package/dist/internal/Viem.js +63 -0
- package/dist/internal/Viem.js.map +1 -0
- package/dist/internal/WebhookDestination.d.ts +198 -0
- package/dist/internal/WebhookDestination.d.ts.map +1 -0
- package/dist/internal/WebhookDestination.js +170 -0
- package/dist/internal/WebhookDestination.js.map +1 -0
- package/dist/internal/Webhooks.d.ts +433 -0
- package/dist/internal/Webhooks.d.ts.map +1 -0
- package/dist/internal/Webhooks.js +489 -0
- package/dist/internal/Webhooks.js.map +1 -0
- package/dist/internal/webhookDestinations/betterStack.d.ts +26 -0
- package/dist/internal/webhookDestinations/betterStack.d.ts.map +1 -0
- package/dist/internal/webhookDestinations/betterStack.js +76 -0
- package/dist/internal/webhookDestinations/betterStack.js.map +1 -0
- package/dist/internal/webhookDestinations/slack.d.ts +59 -0
- package/dist/internal/webhookDestinations/slack.d.ts.map +1 -0
- package/dist/internal/webhookDestinations/slack.js +422 -0
- package/dist/internal/webhookDestinations/slack.js.map +1 -0
- package/dist/internal/webhookDestinations/url.d.ts +17 -0
- package/dist/internal/webhookDestinations/url.d.ts.map +1 -0
- package/dist/internal/webhookDestinations/url.js +130 -0
- package/dist/internal/webhookDestinations/url.js.map +1 -0
- package/dist/openrpc.json +5302 -0
- package/package.json +46 -4
- package/seeds/31318/icons/0x20c0000000000000000000000000000000000000.svg +1 -0
- package/seeds/31318/icons/0x20c0000000000000000000000000000000000001.svg +1 -0
- package/seeds/31318/icons/0x20c0000000000000000000000000000000000002.svg +1 -0
- package/seeds/31318/icons/0x20c0000000000000000000000000000000000003.svg +1 -0
- package/seeds/31318/verified-tokens.json +30 -0
- package/seeds/4217/icons/0x20c0000000000000000000000000000000000000.svg +25 -0
- package/seeds/4217/icons/0x20c000000000000000000000048c8f36df1c9a4a.svg +1 -0
- package/seeds/4217/icons/0x20c0000000000000000000000520792dcccccccc.svg +1 -0
- package/seeds/4217/icons/0x20c0000000000000000000000a6da882d075a4c3.svg +4 -0
- package/seeds/4217/icons/0x20c000000000000000000000111111111e910f0f.svg +30 -0
- package/seeds/4217/icons/0x20c00000000000000000000014f22ca97301eb73.svg +1 -0
- package/seeds/4217/icons/0x20c0000000000000000000001621e21f71cf12fb.svg +1 -0
- package/seeds/4217/icons/0x20c0000000000000000000002f52d5cc21a3207b.svg +1 -0
- package/seeds/4217/icons/0x20c0000000000000000000003158081efd85bfc2.svg +11 -0
- package/seeds/4217/icons/0x20c0000000000000000000003554d28269e0f3c2.svg +1 -0
- package/seeds/4217/icons/0x20c000000000000000000000383a23bacb546ab9.svg +1 -0
- package/seeds/4217/icons/0x20c00000000000000000000042109aef2f8b28e1.png +0 -0
- package/seeds/4217/icons/0x20c0000000000000000000005c0bac7cef389a11.svg +1 -0
- package/seeds/4217/icons/0x20c0000000000000000000006fd9a167923ba194.png +0 -0
- package/seeds/4217/icons/0x20c0000000000000000000007f7ba549dd0251b9.svg +1 -0
- package/seeds/4217/icons/0x20c0000000000000000000008191667423f70e67.svg +5 -0
- package/seeds/4217/icons/0x20c0000000000000000000008ee4fcff88888888.svg +1 -0
- package/seeds/4217/icons/0x20c0000000000000000000009a4a4b17e0dc6651.svg +1 -0
- package/seeds/4217/icons/0x20c000000000000000000000ab02d39df30bd17e.svg +1 -0
- package/seeds/4217/icons/0x20c000000000000000000000ae247a1130450f09.svg +1 -0
- package/seeds/4217/icons/0x20c000000000000000000000aeed2ec36a54d0e5.svg +1 -0
- package/seeds/4217/icons/0x20c000000000000000000000b9537d11c60e8b50.svg +1 -0
- package/seeds/4217/icons/0x20c000000000000000000000bd95bfb69fbe6ce3.svg +1 -0
- package/seeds/4217/icons/0x20c000000000000000000000c412ec89d0c08be5.svg +1 -0
- package/seeds/4217/icons/0x20c000000000000000000000f047dd7018e50367.png +0 -0
- package/seeds/4217/verified-tokens.json +177 -0
- package/seeds/42431/icons/0x20c0000000000000000000000000000000000000.svg +25 -0
- package/seeds/42431/icons/0x20c0000000000000000000000000000000000001.svg +1 -0
- package/seeds/42431/icons/0x20c0000000000000000000000000000000000002.svg +1 -0
- package/seeds/42431/icons/0x20c0000000000000000000000000000000000003.svg +1 -0
- package/seeds/42431/icons/0x20c0000000000000000000009e8d7eb59b783726.svg +1 -0
- package/seeds/42431/icons/0x20c000000000000000000000d72572838bbee59c.svg +1 -0
- package/seeds/42431/verified-tokens.json +44 -0
- package/seeds/openrpc/README.md +27 -0
- package/seeds/openrpc/execution-apis.json +8989 -0
- package/src/ApiKey.test.ts +91 -0
- package/src/ApiKey.ts +122 -0
- package/src/ApiKeySource.test-d.ts +11 -0
- package/src/ApiKeySource.test.ts +295 -0
- package/src/ApiKeySource.ts +303 -0
- package/src/App.test-d.ts +38 -0
- package/src/App.test.ts +65927 -0
- package/src/App.ts +855 -0
- package/src/Assets.ts +58 -0
- package/src/Auth.ts +1 -0
- package/src/Cli.test.ts +81 -0
- package/src/Cli.ts +38 -0
- package/src/Client.test-d.ts +1438 -0
- package/src/Client.test.ts +134 -0
- package/src/Client.ts +45 -0
- package/src/Metrics.ts +154 -0
- package/src/Scope.ts +50 -0
- package/src/Webhooks.test.ts +49 -0
- package/src/Webhooks.ts +172 -0
- package/src/admin/Access.test.ts +130 -0
- package/src/admin/Access.ts +87 -0
- package/src/admin/App.test.ts +225 -0
- package/src/admin/App.ts +216 -0
- package/src/admin/apps/api-keys.test.ts +156 -0
- package/src/admin/apps/api-keys.ts +157 -0
- package/src/admin/apps/scopes.test.ts +52 -0
- package/src/admin/apps/scopes.ts +47 -0
- package/src/admin/apps/verified-tokens.test.ts +415 -0
- package/src/admin/apps/verified-tokens.ts +566 -0
- package/src/admin/index.ts +2 -0
- package/src/admin/ui/plugin.ts +135 -0
- package/src/admin/ui/src/components/Checkbox.tsx +22 -0
- package/src/admin/ui/src/components/Dialog.tsx +32 -0
- package/src/admin/ui/src/components/MobileProfile.tsx +31 -0
- package/src/admin/ui/src/components/Page.tsx +53 -0
- package/src/admin/ui/src/components/TempoLogo.tsx +37 -0
- package/src/admin/ui/src/fonts/Pilat-Demi.woff2 +0 -0
- package/src/admin/ui/src/fonts/Pilat-Regular.woff2 +0 -0
- package/src/admin/ui/src/lib/api-keys.ts +63 -0
- package/src/admin/ui/src/lib/client.ts +15 -0
- package/src/admin/ui/src/lib/config.ts +57 -0
- package/src/admin/ui/src/lib/query-client.ts +26 -0
- package/src/admin/ui/src/lib/scopes.ts +25 -0
- package/src/admin/ui/src/lib/verified-tokens.ts +89 -0
- package/src/admin/ui/src/lib/wagmi.ts +18 -0
- package/src/admin/ui/src/routeTree.gen.ts +104 -0
- package/src/admin/ui/src/router.tsx +25 -0
- package/src/admin/ui/src/routes/__root.tsx +219 -0
- package/src/admin/ui/src/routes/api-keys.tsx +329 -0
- package/src/admin/ui/src/routes/index.tsx +7 -0
- package/src/admin/ui/src/routes/verified-tokens.tsx +331 -0
- package/src/admin/ui/src/styles.css +20 -0
- package/src/admin/ui/tsconfig.json +16 -0
- package/src/admin/ui/vite.config.ts +29 -0
- package/src/admin/ui.generated.ts +60 -0
- package/src/apps/activities.test.ts +1020 -0
- package/src/apps/activities.ts +3934 -0
- package/src/apps/addresses.ts +24 -0
- package/src/apps/balances.test.ts +274 -0
- package/src/apps/balances.ts +533 -0
- package/src/apps/blocks.test.ts +385 -0
- package/src/apps/blocks.ts +896 -0
- package/src/apps/coingecko.test.ts +218 -0
- package/src/apps/coingecko.ts +1383 -0
- package/src/apps/exchanges.test.ts +958 -0
- package/src/apps/exchanges.ts +4773 -0
- package/src/apps/fee-amm.test.ts +321 -0
- package/src/apps/fee-amm.ts +1121 -0
- package/src/apps/indexer.test.ts +153 -0
- package/src/apps/indexer.ts +330 -0
- package/src/apps/receipts.test.ts +285 -0
- package/src/apps/receipts.ts +1193 -0
- package/src/apps/rpc.test.ts +302 -0
- package/src/apps/rpc.ts +222 -0
- package/src/apps/tokenlist.test.ts +80 -0
- package/src/apps/tokenlist.ts +207 -0
- package/src/apps/tokens.test.ts +1062 -0
- package/src/apps/tokens.ts +2292 -0
- package/src/apps/transactions.test.ts +408 -0
- package/src/apps/transactions.ts +1363 -0
- package/src/apps/transfers.test.ts +371 -0
- package/src/apps/transfers.ts +1116 -0
- package/src/apps/verified-tokens.test.ts +114 -0
- package/src/apps/verified-tokens.ts +295 -0
- package/src/apps/webhooks.test.ts +1937 -0
- package/src/apps/webhooks.ts +2926 -0
- package/src/cloudflare.ts +55 -0
- package/src/index.ts +15 -0
- package/src/internal/Auth.test.ts +980 -0
- package/src/internal/Auth.ts +1012 -0
- package/src/internal/Cache.test.ts +240 -0
- package/src/internal/Cache.ts +262 -0
- package/src/internal/Cursor.test.ts +100 -0
- package/src/internal/Cursor.ts +149 -0
- package/src/internal/EdgeCache.test.ts +234 -0
- package/src/internal/EdgeCache.ts +224 -0
- package/src/internal/Errors.ts +14 -0
- package/src/internal/Log.test.ts +244 -0
- package/src/internal/Log.ts +216 -0
- package/src/internal/MetricSink.ts +220 -0
- package/src/internal/Mpp.test.ts +64 -0
- package/src/internal/Mpp.ts +141 -0
- package/src/internal/OpenApi.fonts.ts +38 -0
- package/src/internal/OpenApi.logo.ts +18 -0
- package/src/internal/OpenApi.ts +488 -0
- package/src/internal/Path.test.ts +14 -0
- package/src/internal/Path.ts +15 -0
- package/src/internal/RateLimit.test.ts +93 -0
- package/src/internal/RateLimit.ts +93 -0
- package/src/internal/Response.ts +131 -0
- package/src/internal/Schema.test.ts +34 -0
- package/src/internal/Schema.ts +523 -0
- package/src/internal/Store.test.ts +545 -0
- package/src/internal/Store.ts +526 -0
- package/src/internal/Tidx.local.test.ts +33 -0
- package/src/internal/Tidx.test.ts +294 -0
- package/src/internal/Tidx.ts +238 -0
- package/src/internal/Timing.test.ts +22 -0
- package/src/internal/Timing.ts +62 -0
- package/src/internal/Ttl.test.ts +18 -0
- package/src/internal/Ttl.ts +59 -0
- package/src/internal/Value.ts +114 -0
- package/src/internal/VerifiedTokens.test.ts +333 -0
- package/src/internal/VerifiedTokens.ts +645 -0
- package/src/internal/Viem.test.ts +115 -0
- package/src/internal/Viem.ts +165 -0
- package/src/internal/WebhookDestination.test.ts +648 -0
- package/src/internal/WebhookDestination.ts +331 -0
- package/src/internal/Webhooks.test.ts +668 -0
- package/src/internal/Webhooks.ts +940 -0
- package/src/internal/webhookDestinations/betterStack.ts +91 -0
- package/src/internal/webhookDestinations/slack.ts +496 -0
- package/src/internal/webhookDestinations/url.ts +128 -0
- 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
|
+
}
|