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,153 @@
|
|
|
1
|
+
import { RateLimit, Store } from 'cadent'
|
|
2
|
+
import * as TestApp from '../../test/App.js'
|
|
3
|
+
import * as Mppx from '../../test/Mppx.js'
|
|
4
|
+
import * as Runtime from '../../test/runtime.js'
|
|
5
|
+
import * as Tempo from '../../test/Tempo.js'
|
|
6
|
+
import * as Tidx from '../internal/Tidx.js'
|
|
7
|
+
import * as Indexer from './indexer.js'
|
|
8
|
+
|
|
9
|
+
const chainId = String(Runtime.get().chainId)
|
|
10
|
+
|
|
11
|
+
describe('GET /indexer/query', () => {
|
|
12
|
+
test('proxies a query to the upstream indexer', async () => {
|
|
13
|
+
const client = TestApp.client()
|
|
14
|
+
const response = await client.v1.indexer.query.$get(
|
|
15
|
+
// testClient route inputs are query strings, not numbers.
|
|
16
|
+
{ query: { sql: 'select hash from txs limit 1', chainId } },
|
|
17
|
+
TestApp.auth,
|
|
18
|
+
)
|
|
19
|
+
const body = await TestApp.json(response, Indexer.schema.Response)
|
|
20
|
+
|
|
21
|
+
expect(response.status).toMatchInlineSnapshot(`200`)
|
|
22
|
+
expect(response.headers.get('content-type')).toMatchInlineSnapshot(`"application/json"`)
|
|
23
|
+
// A successful response proves the proxy injected TIDX basic auth upstream.
|
|
24
|
+
expect(body.ok).toMatchInlineSnapshot(`true`)
|
|
25
|
+
expect(body.columns).toMatchInlineSnapshot(`
|
|
26
|
+
[
|
|
27
|
+
"hash",
|
|
28
|
+
]
|
|
29
|
+
`)
|
|
30
|
+
expect(body.row_count).toMatchInlineSnapshot(`1`)
|
|
31
|
+
expect(Array.isArray(body.rows)).toMatchInlineSnapshot(`true`)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
test('escaped string literals round-trip through the real indexer', async () => {
|
|
35
|
+
// Pins the upstream's accepted escape form: TIDX's ANSI SQL parser only
|
|
36
|
+
// accepts quote doubling (`''`) inside string literals and rejects
|
|
37
|
+
// backslash escaping with a parse error, so this fails if `Tidx.escape`
|
|
38
|
+
// ever regresses to a form the indexer cannot parse.
|
|
39
|
+
const client = TestApp.client()
|
|
40
|
+
const value = "a'b\\c"
|
|
41
|
+
const response = await client.v1.indexer.query.$get(
|
|
42
|
+
{
|
|
43
|
+
query: {
|
|
44
|
+
sql: `select '${Tidx.escape(value)}' as x`,
|
|
45
|
+
chainId,
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
TestApp.auth,
|
|
49
|
+
)
|
|
50
|
+
const body = await TestApp.json(response, Indexer.schema.Response)
|
|
51
|
+
|
|
52
|
+
expect(response.status).toMatchInlineSnapshot(`200`)
|
|
53
|
+
expect(body.ok).toMatchInlineSnapshot(`true`)
|
|
54
|
+
expect(body.rows[0]?.[0]).toBe(value)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
test('bounds and caches buffered queries', async () => {
|
|
58
|
+
// Stub the upstream so the injected execution bounds are observable on the
|
|
59
|
+
// outgoing request; the auth path is in-memory and never touches fetch.
|
|
60
|
+
const fetch = vi.spyOn(globalThis, 'fetch').mockResolvedValue(
|
|
61
|
+
new Response(JSON.stringify({ columns: [], ok: true, row_count: 0, rows: [] }), {
|
|
62
|
+
headers: { 'content-type': 'application/json' },
|
|
63
|
+
}),
|
|
64
|
+
)
|
|
65
|
+
try {
|
|
66
|
+
const client = TestApp.client()
|
|
67
|
+
const response = await client.v1.indexer.query.$get(
|
|
68
|
+
{ query: { sql: 'select 1', chainId } },
|
|
69
|
+
TestApp.auth,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
expect(response.status).toMatchInlineSnapshot(`200`)
|
|
73
|
+
// Buffered (non-`live`) queries are response-cached on the `feed` tier.
|
|
74
|
+
expect(response.headers.get('cache-control')).toMatchInlineSnapshot(
|
|
75
|
+
`"private, max-age=10, stale-while-revalidate=30"`,
|
|
76
|
+
)
|
|
77
|
+
// Tempo API enforces the documented execution bounds upstream even when the
|
|
78
|
+
// caller omits them.
|
|
79
|
+
const upstream = new URL((fetch.mock.calls[0]![0] as Request).url)
|
|
80
|
+
expect(upstream.searchParams.get('timeout_ms')).toMatchInlineSnapshot(`"5000"`)
|
|
81
|
+
expect(upstream.searchParams.get('limit')).toMatchInlineSnapshot(`"10000"`)
|
|
82
|
+
} finally {
|
|
83
|
+
fetch.mockRestore()
|
|
84
|
+
}
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
test('requires authentication', async () => {
|
|
88
|
+
const client = TestApp.client()
|
|
89
|
+
const response = await client.v1.indexer.query.$get(
|
|
90
|
+
{ query: { sql: 'select 1' } },
|
|
91
|
+
{ headers: { 'tempo-api-key': 'wrong' } },
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
expect(response.status).toMatchInlineSnapshot(`401`)
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
test('proxies without auth middleware', async () => {
|
|
98
|
+
const client = TestApp.client({ auth: false })
|
|
99
|
+
const response = await client.v1.indexer.query.$get({
|
|
100
|
+
query: { sql: 'select hash from txs limit 1', chainId },
|
|
101
|
+
})
|
|
102
|
+
const body = await TestApp.json(response, Indexer.schema.Response)
|
|
103
|
+
|
|
104
|
+
expect(response.status).toMatchInlineSnapshot(`200`)
|
|
105
|
+
expect(body.ok).toMatchInlineSnapshot(`true`)
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
test('completes the MPP payment lifecycle when public quota is exhausted', async () => {
|
|
109
|
+
const rateLimitStore = Store.memory()
|
|
110
|
+
const app = TestApp.create({
|
|
111
|
+
rateLimit: { store: rateLimitStore },
|
|
112
|
+
mpp: {
|
|
113
|
+
charge: {
|
|
114
|
+
chainId: Tempo.chain.id,
|
|
115
|
+
currency: Tempo.currency,
|
|
116
|
+
decimals: 6,
|
|
117
|
+
getClient: () => Tempo.client,
|
|
118
|
+
recipient: Tempo.accounts[2].address,
|
|
119
|
+
},
|
|
120
|
+
secretKey: 'secret_test_key_0123456789abcdef',
|
|
121
|
+
},
|
|
122
|
+
})
|
|
123
|
+
await exhaustQuota(RateLimit.memory({ store: rateLimitStore }), 'public:anonymous', {
|
|
124
|
+
perMinute: 60,
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
// An unpaid over-quota request is challenged for payment. MPP challenges
|
|
128
|
+
// and receipts ride on real HTTP headers, so we drive the lifecycle
|
|
129
|
+
// through `app.fetch` rather than the typed route client.
|
|
130
|
+
const challenge = await app.fetch(
|
|
131
|
+
new Request('http://tempo-api.test/v1/indexer/query?sql=select%201'),
|
|
132
|
+
)
|
|
133
|
+
expect(challenge.status).toMatchInlineSnapshot(`402`)
|
|
134
|
+
expect(challenge.headers.get('www-authenticate')?.startsWith('Payment ')).toMatchInlineSnapshot(
|
|
135
|
+
`true`,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
// Paying the challenge unlocks a real proxied query and returns a receipt.
|
|
139
|
+
const sql = encodeURIComponent('select hash from txs limit 1')
|
|
140
|
+
const paid = await Mppx.createClient(app).fetch(
|
|
141
|
+
`http://tempo-api.test/v1/indexer/query?sql=${sql}&chainId=${chainId}`,
|
|
142
|
+
)
|
|
143
|
+
const body = await TestApp.json(paid, Indexer.schema.Response)
|
|
144
|
+
|
|
145
|
+
expect(paid.status).toMatchInlineSnapshot(`200`)
|
|
146
|
+
expect(paid.headers.has('payment-receipt')).toMatchInlineSnapshot(`true`)
|
|
147
|
+
expect(body.ok).toMatchInlineSnapshot(`true`)
|
|
148
|
+
})
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
async function exhaustQuota(rateLimit: RateLimit.Store, key: string, limit: RateLimit.Limit) {
|
|
152
|
+
for (let index = 0; index < limit.perMinute; index++) await rateLimit.consume({ key, limit })
|
|
153
|
+
}
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
import { Hono, type Context, type TypedResponse } from 'hono'
|
|
2
|
+
import * as z from 'zod/mini'
|
|
3
|
+
|
|
4
|
+
import type * as App from '../App.js'
|
|
5
|
+
import * as Auth from '../internal/Auth.js'
|
|
6
|
+
import * as Cache from '../internal/Cache.js'
|
|
7
|
+
import * as OpenApi from '../internal/OpenApi.js'
|
|
8
|
+
import * as Path from '../internal/Path.js'
|
|
9
|
+
import * as Response from '../internal/Response.js'
|
|
10
|
+
import * as Schema from '../internal/Schema.js'
|
|
11
|
+
import * as Tidx from '../internal/Tidx.js'
|
|
12
|
+
|
|
13
|
+
// Request headers that must not be forwarded upstream: hop-by-hop headers, the
|
|
14
|
+
// inbound host, and any caller auth so the consumer's Tempo API credentials never
|
|
15
|
+
// reach the indexer (the proxy injects its own TIDX basic auth instead).
|
|
16
|
+
const strippedHeaders = new Set([
|
|
17
|
+
'authorization',
|
|
18
|
+
'connection',
|
|
19
|
+
'cookie',
|
|
20
|
+
'host',
|
|
21
|
+
'keep-alive',
|
|
22
|
+
'proxy-authorization',
|
|
23
|
+
'te',
|
|
24
|
+
'trailer',
|
|
25
|
+
'transfer-encoding',
|
|
26
|
+
'upgrade',
|
|
27
|
+
'tempo-api-key',
|
|
28
|
+
])
|
|
29
|
+
|
|
30
|
+
/** Never reached; carries typed passthrough response variants into Hono client inference. */
|
|
31
|
+
const narrowPassthrough = false as boolean
|
|
32
|
+
|
|
33
|
+
/** Zod schemas owned by the indexer passthrough. */
|
|
34
|
+
export namespace schema {
|
|
35
|
+
/** Structured result returned by the indexer `query` endpoint. */
|
|
36
|
+
export const Response = z
|
|
37
|
+
.object({
|
|
38
|
+
ok: z
|
|
39
|
+
.boolean()
|
|
40
|
+
.check(
|
|
41
|
+
z.describe('Whether the SQL query completed successfully.'),
|
|
42
|
+
z.meta({ examples: [true] }),
|
|
43
|
+
),
|
|
44
|
+
columns: z
|
|
45
|
+
.array(z.string())
|
|
46
|
+
.check(z.describe('Column names returned by the query, in row-value order.')),
|
|
47
|
+
rows: z
|
|
48
|
+
.array(z.array(z.unknown()))
|
|
49
|
+
.check(
|
|
50
|
+
z.describe(
|
|
51
|
+
'Query results. Each row is an array of values in the same order as `columns`.',
|
|
52
|
+
),
|
|
53
|
+
),
|
|
54
|
+
row_count: z
|
|
55
|
+
.number()
|
|
56
|
+
.check(
|
|
57
|
+
z.int(),
|
|
58
|
+
z.nonnegative(),
|
|
59
|
+
z.describe('Number of rows returned in this response.'),
|
|
60
|
+
z.meta({ examples: [1] }),
|
|
61
|
+
),
|
|
62
|
+
engine: z
|
|
63
|
+
.optional(z.enum(['postgres', 'clickhouse']))
|
|
64
|
+
.check(
|
|
65
|
+
z.describe('Indexer engine that ran the query.'),
|
|
66
|
+
z.meta({ examples: ['postgres'] }),
|
|
67
|
+
),
|
|
68
|
+
query_time_ms: z
|
|
69
|
+
.optional(z.number())
|
|
70
|
+
.check(
|
|
71
|
+
z.describe('Server-side query execution time in milliseconds.'),
|
|
72
|
+
z.meta({ examples: [12] }),
|
|
73
|
+
),
|
|
74
|
+
})
|
|
75
|
+
.check(z.describe('Structured result from a Tempo indexer SQL query.'))
|
|
76
|
+
|
|
77
|
+
/** Error envelope returned by the upstream indexer. */
|
|
78
|
+
export const Error = z
|
|
79
|
+
.object({
|
|
80
|
+
ok: z.literal(false).check(z.describe('Always `false` when the indexer returns an error.')),
|
|
81
|
+
error: z.string().check(z.describe('Human-readable error message from the indexer.')),
|
|
82
|
+
})
|
|
83
|
+
.check(z.describe('Error response returned by the upstream Tempo indexer.'))
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Creates the raw indexer (TIDX) passthrough handler. */
|
|
87
|
+
export function indexer() {
|
|
88
|
+
const queryCache = Cache.response<App.Environment>({
|
|
89
|
+
cacheControl: Cache.policies.feed,
|
|
90
|
+
name: 'tempo-api:indexer:v1',
|
|
91
|
+
key: (c) => {
|
|
92
|
+
const url = new URL(c.req.url)
|
|
93
|
+
if (!url.searchParams.has('chainId') && !url.searchParams.has('chain_id'))
|
|
94
|
+
url.searchParams.set('chainId', String(c.get('chainId')))
|
|
95
|
+
url.searchParams.sort()
|
|
96
|
+
return url.toString()
|
|
97
|
+
},
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
return new Hono<App.Environment>().get(
|
|
101
|
+
'/query',
|
|
102
|
+
Auth.policy({ apiKey: { scopes: ['indexer:query'] }, mpp: true, public: true }),
|
|
103
|
+
OpenApi.describeRoute({
|
|
104
|
+
description:
|
|
105
|
+
'Runs a read-only SQL-style query against Tempo’s indexed chain data, with optional live streaming as new blocks arrive.',
|
|
106
|
+
operationId: 'indexerQuery',
|
|
107
|
+
parameters: [
|
|
108
|
+
{
|
|
109
|
+
description:
|
|
110
|
+
'Read-only SQL query to run against Tempo’s indexed chain data. Use `SELECT` statements only.',
|
|
111
|
+
example: 'SELECT num, hash, timestamp FROM blocks ORDER BY num DESC LIMIT 10',
|
|
112
|
+
in: 'query',
|
|
113
|
+
name: 'sql',
|
|
114
|
+
required: true,
|
|
115
|
+
schema: { type: 'string' },
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
description:
|
|
119
|
+
'Which Tempo network to query. Use the alias `mainnet` or `testnet`, or a numeric chain id (mainnet is `4217`).',
|
|
120
|
+
example: 4217,
|
|
121
|
+
in: 'query',
|
|
122
|
+
name: 'chainId',
|
|
123
|
+
required: true,
|
|
124
|
+
schema: { minimum: 1, type: 'integer' },
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
description:
|
|
128
|
+
'ABI event signature to expose as a named SQL CTE, so you can query decoded event fields directly. Repeat this parameter to add multiple signatures.',
|
|
129
|
+
example: 'Transfer(address indexed from, address indexed to, uint256 value)',
|
|
130
|
+
explode: true,
|
|
131
|
+
in: 'query',
|
|
132
|
+
name: 'signature',
|
|
133
|
+
required: false,
|
|
134
|
+
schema: { items: { type: 'string' }, type: 'array' },
|
|
135
|
+
style: 'form',
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
description:
|
|
139
|
+
'Choose the indexer engine yourself. By default the indexer routes automatically; `clickhouse` cannot be used with `live=true`.',
|
|
140
|
+
example: 'postgres',
|
|
141
|
+
in: 'query',
|
|
142
|
+
name: 'engine',
|
|
143
|
+
required: false,
|
|
144
|
+
schema: { enum: ['postgres', 'clickhouse'], type: 'string' },
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
description:
|
|
148
|
+
'Stream results as Server-Sent Events and re-run the query on every new block. This cannot be combined with `engine=clickhouse`.',
|
|
149
|
+
example: false,
|
|
150
|
+
in: 'query',
|
|
151
|
+
name: 'live',
|
|
152
|
+
required: false,
|
|
153
|
+
schema: { default: false, type: 'boolean' },
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
description:
|
|
157
|
+
'Maximum number of rows to return. The server clamps this to the range 1 through 10,000.',
|
|
158
|
+
example: 10,
|
|
159
|
+
in: 'query',
|
|
160
|
+
name: 'limit',
|
|
161
|
+
required: false,
|
|
162
|
+
schema: { default: 10000, maximum: 10000, minimum: 1, type: 'integer' },
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
description:
|
|
166
|
+
'Per-query timeout in milliseconds. The server clamps this to the range 100 through 30,000.',
|
|
167
|
+
example: 5000,
|
|
168
|
+
in: 'query',
|
|
169
|
+
name: 'timeout_ms',
|
|
170
|
+
required: false,
|
|
171
|
+
schema: { default: 5000, maximum: 30000, minimum: 100, type: 'integer' },
|
|
172
|
+
},
|
|
173
|
+
],
|
|
174
|
+
responses: {
|
|
175
|
+
200: {
|
|
176
|
+
content: {
|
|
177
|
+
'application/json': {
|
|
178
|
+
schema: OpenApi.resolver(schema.Response),
|
|
179
|
+
example: {
|
|
180
|
+
ok: true,
|
|
181
|
+
columns: ['num', 'hash', 'timestamp'],
|
|
182
|
+
rows: [[1234567, '0x515801d7f9a5ac705e793e85904c9c69b3f1694b465cc8fb6ba3f0298dc82665', 1718668800]], // prettier-ignore
|
|
183
|
+
row_count: 1,
|
|
184
|
+
engine: 'postgres',
|
|
185
|
+
query_time_ms: 12,
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
// `live=true` streams Server-Sent Events: `result` events carry this
|
|
189
|
+
// same shape, alongside `error` and `lagged` events.
|
|
190
|
+
'text/event-stream': { schema: OpenApi.resolver(schema.Response) },
|
|
191
|
+
},
|
|
192
|
+
description:
|
|
193
|
+
'Structured result from a Tempo indexer SQL query. When `live=true` the response is a `text/event-stream` (SSE) of `result` events with this same shape, interleaved with `error` and `lagged` events, re-run on every new block.',
|
|
194
|
+
headers: OpenApi.successHeaders,
|
|
195
|
+
},
|
|
196
|
+
// 400 and 422 are the upstream indexer's own error shape (`{ ok: false, error }`),
|
|
197
|
+
// not the Tempo API JSON error envelope; the auth/rate-limit/proxy errors below are.
|
|
198
|
+
400: {
|
|
199
|
+
content: { 'application/json': { schema: OpenApi.resolver(schema.Error) } },
|
|
200
|
+
description:
|
|
201
|
+
'The query parameters are invalid; this uses the upstream indexer’s error shape.',
|
|
202
|
+
},
|
|
203
|
+
401: OpenApi.standardError(401, 'Your API key is missing or invalid.'),
|
|
204
|
+
422: {
|
|
205
|
+
content: { 'application/json': { schema: OpenApi.resolver(schema.Error) } },
|
|
206
|
+
description:
|
|
207
|
+
'The SQL failed validation or execution; this uses the upstream indexer’s error shape.',
|
|
208
|
+
},
|
|
209
|
+
429: OpenApi.standardError(429, 'You have exceeded the rate limit.'),
|
|
210
|
+
500: OpenApi.standardError(500, 'The Tempo API encountered an internal server error.'),
|
|
211
|
+
502: OpenApi.standardError(
|
|
212
|
+
502,
|
|
213
|
+
'The upstream Tempo indexer could not complete the request.',
|
|
214
|
+
),
|
|
215
|
+
},
|
|
216
|
+
summary: 'Query indexed chain data',
|
|
217
|
+
tags: ['Indexer'],
|
|
218
|
+
}),
|
|
219
|
+
(c, next) => (isLive(c) ? next() : queryCache(c, next)),
|
|
220
|
+
(c) => {
|
|
221
|
+
if (Auth.narrowAccess) return Auth.paidAccessError(c)
|
|
222
|
+
if (narrowPassthrough)
|
|
223
|
+
return c.json(
|
|
224
|
+
Response.validated(schema.Response, { columns: [], ok: true, row_count: 0, rows: [] }),
|
|
225
|
+
200,
|
|
226
|
+
)
|
|
227
|
+
if (narrowPassthrough)
|
|
228
|
+
return c.json(
|
|
229
|
+
Response.validated(schema.Error, { error: 'Invalid query parameters', ok: false }),
|
|
230
|
+
400,
|
|
231
|
+
)
|
|
232
|
+
if (narrowPassthrough)
|
|
233
|
+
return c.json(
|
|
234
|
+
Response.validated(schema.Error, {
|
|
235
|
+
error: 'SQL validation or query execution error',
|
|
236
|
+
ok: false,
|
|
237
|
+
}),
|
|
238
|
+
422,
|
|
239
|
+
)
|
|
240
|
+
return passthrough(c, 'v1')
|
|
241
|
+
},
|
|
242
|
+
)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
async function passthrough(
|
|
246
|
+
c: Context<App.Environment>,
|
|
247
|
+
version: string,
|
|
248
|
+
): Promise<globalThis.Response & TypedResponse<never, never, never>> {
|
|
249
|
+
const tidx = c.get('tidx')
|
|
250
|
+
|
|
251
|
+
// Resolve the upstream base URL per chain so mainnet and testnet
|
|
252
|
+
// requests reach the correct indexer.
|
|
253
|
+
const chainIdParam = c.req.query('chainId')
|
|
254
|
+
const parsed = chainIdParam ? Schema.ChainId.safeParse(chainIdParam) : undefined
|
|
255
|
+
const chainId = parsed?.success ? parsed.data : c.get('chainId')
|
|
256
|
+
// Resolve the upstream for this caller. A resolver `tidx` can route by
|
|
257
|
+
// principal (e.g. anonymous `type: 'public'` callers to a public, no-auth
|
|
258
|
+
// indexer); a static config ignores the principal.
|
|
259
|
+
const { baseUrl, basicAuth } = Tidx.resolve(tidx, { chainId, principal: Auth.getPrincipal(c) })
|
|
260
|
+
// The chain id is syntactically valid but this deployment has no indexer
|
|
261
|
+
// upstream configured for it (no `tidx` config and no built-in default).
|
|
262
|
+
if (!baseUrl)
|
|
263
|
+
return Response.error(c, {
|
|
264
|
+
code: 'chain_id_unsupported',
|
|
265
|
+
message: 'Unsupported chain id',
|
|
266
|
+
status: 400,
|
|
267
|
+
}) as never
|
|
268
|
+
|
|
269
|
+
// Strip the `<basePath>/<version>/indexer` mount prefix to recover the
|
|
270
|
+
// upstream path.
|
|
271
|
+
const prefix = Path.join(c.get('basePath'), version, 'indexer')
|
|
272
|
+
const requestUrl = new URL(c.req.url)
|
|
273
|
+
const rest = requestUrl.pathname.slice(prefix.length) || '/'
|
|
274
|
+
|
|
275
|
+
const upstream = new URL(baseUrl)
|
|
276
|
+
upstream.pathname = Path.join(upstream.pathname, rest)
|
|
277
|
+
upstream.search = requestUrl.search
|
|
278
|
+
|
|
279
|
+
// Enforce the documented execution bounds server-side: an arbitrary SELECT
|
|
280
|
+
// from an anonymous caller is always bounded even if the upstream's own
|
|
281
|
+
// defaulting changes. Caller-supplied values pass through (the upstream
|
|
282
|
+
// clamps them to its documented ranges).
|
|
283
|
+
if (!upstream.searchParams.has('timeout_ms')) upstream.searchParams.set('timeout_ms', '5000')
|
|
284
|
+
if (!upstream.searchParams.has('limit')) upstream.searchParams.set('limit', '10000')
|
|
285
|
+
|
|
286
|
+
const headers = new Headers()
|
|
287
|
+
for (const [name, value] of c.req.raw.headers)
|
|
288
|
+
if (!strippedHeaders.has(name.toLowerCase())) headers.set(name, value)
|
|
289
|
+
if (basicAuth) headers.set('authorization', `Basic ${btoa(basicAuth)}`)
|
|
290
|
+
|
|
291
|
+
const method = c.req.method
|
|
292
|
+
|
|
293
|
+
// `duplex` is required when streaming a request body, but is not yet in
|
|
294
|
+
// the DOM `RequestInit` type.
|
|
295
|
+
const init: RequestInit & { duplex?: 'half' } = { headers, method, redirect: 'manual' }
|
|
296
|
+
const body = method === 'GET' || method === 'HEAD' ? null : c.req.raw.body
|
|
297
|
+
if (body) {
|
|
298
|
+
init.body = body
|
|
299
|
+
init.duplex = 'half'
|
|
300
|
+
}
|
|
301
|
+
// Bound buffered queries so a hung upstream connection cannot pin the
|
|
302
|
+
// request forever: the upstream's own per-query ceiling is 30 s, so 35 s
|
|
303
|
+
// only fires when the connection itself stalls. The SSE `live` stream is
|
|
304
|
+
// long-lived by design and gets no timeout.
|
|
305
|
+
if (!isLive(c)) init.signal = AbortSignal.timeout(35_000)
|
|
306
|
+
|
|
307
|
+
try {
|
|
308
|
+
const response = await fetch(new Request(upstream, init))
|
|
309
|
+
|
|
310
|
+
// Stream the upstream response back verbatim so both JSON queries and
|
|
311
|
+
// SSE `live` streams pass through untouched.
|
|
312
|
+
return c.newResponse(response.body, response) as globalThis.Response &
|
|
313
|
+
TypedResponse<never, never, never>
|
|
314
|
+
} catch (cause) {
|
|
315
|
+
// Timeouts and network failures become the standard 502 envelope instead
|
|
316
|
+
// of an unhandled 500.
|
|
317
|
+
return Response.upstream(c, cause) as never
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Whether the request asked for the SSE `live` stream. Anything other than an
|
|
323
|
+
* explicit falsy value counts as live, so an ambiguous value is never buffered
|
|
324
|
+
* into the response cache or cut off by the passthrough timeout.
|
|
325
|
+
*/
|
|
326
|
+
function isLive(c: Context<App.Environment>): boolean {
|
|
327
|
+
const live = c.req.query('live')
|
|
328
|
+
if (live === undefined) return false
|
|
329
|
+
return !['', '0', 'false'].includes(live.toLowerCase())
|
|
330
|
+
}
|