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,526 @@
|
|
|
1
|
+
/** Minimal string store contract used by Tempo API internals. */
|
|
2
|
+
export type Store = {
|
|
3
|
+
/** Deletes a key. */
|
|
4
|
+
delete(key: string): Promise<void>
|
|
5
|
+
/** Gets a string value. Returns `null` for missing or expired keys. */
|
|
6
|
+
get(key: string): Promise<null | string>
|
|
7
|
+
/**
|
|
8
|
+
* Optional native counter: increments an integer value and returns the new
|
|
9
|
+
* count. A missing or expired key restarts at `1`; `ttl` applies like
|
|
10
|
+
* {@link Store.put}. Implement when the backend can execute the
|
|
11
|
+
* read-modify-write in one place (in-memory, inside a Durable Object);
|
|
12
|
+
* consumers go through the {@link increment} helper, which falls back to a
|
|
13
|
+
* get + put when this is absent.
|
|
14
|
+
*/
|
|
15
|
+
increment?: ((key: string, options?: Store.PutOptions) => Promise<number>) | undefined
|
|
16
|
+
/** Lists keys. */
|
|
17
|
+
list(options?: {
|
|
18
|
+
/** Restrict results to keys with this prefix. */
|
|
19
|
+
prefix?: string | undefined
|
|
20
|
+
}): Promise<{
|
|
21
|
+
/** Matching keys. */
|
|
22
|
+
keys: readonly {
|
|
23
|
+
/** Key name. */
|
|
24
|
+
name: string
|
|
25
|
+
}[]
|
|
26
|
+
}>
|
|
27
|
+
/** Writes a string value, optionally with a time-to-live. */
|
|
28
|
+
put(key: string, value: string, options?: Store.PutOptions): Promise<void>
|
|
29
|
+
/**
|
|
30
|
+
* Store kind discriminant. `'state'` marks an authoritative, enumerable
|
|
31
|
+
* store ({@link State}); `'cache'` marks a cache (e.g. the Web Cache,
|
|
32
|
+
* which cannot enumerate keys and silently drops entries).
|
|
33
|
+
*/
|
|
34
|
+
type: Store.Type
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export namespace Store {
|
|
38
|
+
/** Store kind: `'state'` (authoritative, enumerable) or `'cache'` (a cache). */
|
|
39
|
+
export type Type = 'state' | 'cache'
|
|
40
|
+
|
|
41
|
+
/** Options for writing a value to a store. */
|
|
42
|
+
export type PutOptions = {
|
|
43
|
+
/**
|
|
44
|
+
* Time-to-live in milliseconds. After this duration the key is treated as
|
|
45
|
+
* absent. Production backends should map this to native expiration
|
|
46
|
+
* (Redis `PEXPIRE`, Cloudflare `expirationTtl`, etc.).
|
|
47
|
+
*/
|
|
48
|
+
ttl?: number | undefined
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* An authoritative, enumerable {@link Store} for stateful features (webhook
|
|
54
|
+
* subscriptions, rate-limit counters, …). Discriminated by `type: 'state'` so
|
|
55
|
+
* stateful features can require it at the type level and reject cache adapters
|
|
56
|
+
* like the Web Cache ({@link cache}, `type: 'cache'`), which cannot enumerate
|
|
57
|
+
* keys and silently drops entries on eviction. `memory`, `cloudflareKv`, and
|
|
58
|
+
* `durableObject` produce it.
|
|
59
|
+
*/
|
|
60
|
+
export type State = Store & { type: 'state' }
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Wraps store operations into a {@link Store}, defaulting to an authoritative
|
|
64
|
+
* {@link State} store. Omit `type` for the common authoritative case; pass
|
|
65
|
+
* `type: 'cache'` to opt into a cache-kind store (non-enumerable, lossy — e.g.
|
|
66
|
+
* the Web Cache).
|
|
67
|
+
*/
|
|
68
|
+
export function from<const store extends from.Input>(store: store): from.Output<store> {
|
|
69
|
+
// Default the discriminant so only cache-kind stores have to opt in.
|
|
70
|
+
return { ...store, type: store.type ?? 'state' } as never
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export declare namespace from {
|
|
74
|
+
/** Store operations with an optional kind discriminant (defaults to `'state'`). */
|
|
75
|
+
type Input = Omit<Store, 'type'> & {
|
|
76
|
+
/** Store kind; defaults to `'state'`. Pass `'cache'` for a cache-kind store. */
|
|
77
|
+
type?: Store.Type | undefined
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Resolved store kind: {@link State} unless `type: 'cache'` is provided. */
|
|
81
|
+
type Output<store extends Input> = store extends { type: 'cache' } ? Store : State
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Increments an integer counter on a store and returns the new value. A
|
|
86
|
+
* missing or expired key restarts at `1`; `ttl` applies like {@link Store.put}.
|
|
87
|
+
*
|
|
88
|
+
* Uses the store's native {@link Store.increment} when implemented (atomic
|
|
89
|
+
* where the backend executes it in one place — in-memory, inside a Durable
|
|
90
|
+
* Object). Otherwise falls back to a get + put read-modify-write, which
|
|
91
|
+
* costs two round trips on remote stores and can race under concurrency —
|
|
92
|
+
* fine for dev/in-process backends, not for enforced distributed counters.
|
|
93
|
+
*/
|
|
94
|
+
export async function increment(
|
|
95
|
+
store: Store,
|
|
96
|
+
key: string,
|
|
97
|
+
options: Store.PutOptions = {},
|
|
98
|
+
): Promise<number> {
|
|
99
|
+
if (store.increment) return store.increment(key, options)
|
|
100
|
+
const count = Number((await store.get(key)) ?? 0) + 1
|
|
101
|
+
await store.put(key, String(count), options)
|
|
102
|
+
return count
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Creates an in-memory store with TTL support. */
|
|
106
|
+
export function memory(options: memory.Options = {}): State {
|
|
107
|
+
// Track value + optional expiration in one cell so `get` can lazily evict
|
|
108
|
+
// expired entries without a background sweeper.
|
|
109
|
+
type Cell = { expiresAt: number | undefined; value: string }
|
|
110
|
+
const cells = new Map<string, Cell>(
|
|
111
|
+
(options.entries ?? []).map(([key, value]) => [key, { expiresAt: undefined, value }]),
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
function read(key: string) {
|
|
115
|
+
const cell = cells.get(key)
|
|
116
|
+
if (!cell) return null
|
|
117
|
+
if (cell.expiresAt !== undefined && cell.expiresAt <= Date.now()) {
|
|
118
|
+
cells.delete(key)
|
|
119
|
+
return null
|
|
120
|
+
}
|
|
121
|
+
return cell
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return from({
|
|
125
|
+
async delete(key) {
|
|
126
|
+
cells.delete(key)
|
|
127
|
+
},
|
|
128
|
+
async get(key) {
|
|
129
|
+
return read(key)?.value ?? null
|
|
130
|
+
},
|
|
131
|
+
// Native increment: the read-modify-write is synchronous, so concurrent
|
|
132
|
+
// consumers cannot interleave between the read and the write.
|
|
133
|
+
async increment(key, options = {}) {
|
|
134
|
+
const count = Number(read(key)?.value ?? 0) + 1
|
|
135
|
+
const expiresAt = options.ttl === undefined ? undefined : Date.now() + options.ttl
|
|
136
|
+
cells.set(key, { expiresAt, value: String(count) })
|
|
137
|
+
return count
|
|
138
|
+
},
|
|
139
|
+
async list(options = {}) {
|
|
140
|
+
const keys = Array.from(cells.keys())
|
|
141
|
+
.filter((key) => (!options.prefix || key.startsWith(options.prefix)) && read(key))
|
|
142
|
+
.sort()
|
|
143
|
+
.map((name) => ({ name }))
|
|
144
|
+
|
|
145
|
+
return { keys }
|
|
146
|
+
},
|
|
147
|
+
async put(key, value, options = {}) {
|
|
148
|
+
const expiresAt = options.ttl === undefined ? undefined : Date.now() + options.ttl
|
|
149
|
+
cells.set(key, { expiresAt, value })
|
|
150
|
+
},
|
|
151
|
+
})
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export declare namespace memory {
|
|
155
|
+
/** Options for creating an in-memory store. */
|
|
156
|
+
type Options = {
|
|
157
|
+
/** Initial entries. */
|
|
158
|
+
entries?: readonly (readonly [string, string])[] | undefined
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Creates a store backed by a Web Cache API instance (e.g. Cloudflare's
|
|
164
|
+
* colo-local `caches.default`). Values are stored as cache entries keyed by a
|
|
165
|
+
* synthetic URL, with `ttl` mapped to `Cache-Control: max-age`. The Web Cache
|
|
166
|
+
* cannot enumerate keys, so `list` returns no keys — use this for caching
|
|
167
|
+
* (get/put/delete) only, e.g. the origin read cache and edge response cache.
|
|
168
|
+
*/
|
|
169
|
+
export function cache(webCache: cache.Cache): Store {
|
|
170
|
+
const request = (key: string) => new Request(`https://tempo-api.cache/${encodeURIComponent(key)}`)
|
|
171
|
+
return from({
|
|
172
|
+
async delete(key) {
|
|
173
|
+
await webCache.delete(request(key))
|
|
174
|
+
},
|
|
175
|
+
async get(key) {
|
|
176
|
+
const hit = await webCache.match(request(key))
|
|
177
|
+
return hit ? hit.text() : null
|
|
178
|
+
},
|
|
179
|
+
async list() {
|
|
180
|
+
return { keys: [] }
|
|
181
|
+
},
|
|
182
|
+
async put(key, value, options = {}) {
|
|
183
|
+
// The Web Cache only persists responses with a positive freshness lifetime.
|
|
184
|
+
const seconds = Math.max(1, Math.ceil((options.ttl ?? 0) / 1_000))
|
|
185
|
+
const headers = new Headers({ 'Cache-Control': `max-age=${seconds}` })
|
|
186
|
+
await webCache.put(request(key), new Response(value, { headers }))
|
|
187
|
+
},
|
|
188
|
+
type: 'cache',
|
|
189
|
+
})
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export declare namespace cache {
|
|
193
|
+
/** Minimal Web Cache API surface used by this adapter. */
|
|
194
|
+
type Cache = {
|
|
195
|
+
/** Deletes a cache entry. */
|
|
196
|
+
delete(request: Request | string): Promise<boolean>
|
|
197
|
+
/** Looks up a cache entry. */
|
|
198
|
+
match(request: Request | string): Promise<Response | undefined>
|
|
199
|
+
/** Stores a cache entry. */
|
|
200
|
+
put(request: Request | string, response: Response): Promise<void>
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/** Creates a store backed by a Cloudflare Workers KV namespace. */
|
|
205
|
+
export function cloudflareKv(namespace: cloudflareKv.Namespace): State {
|
|
206
|
+
return from({
|
|
207
|
+
async delete(key) {
|
|
208
|
+
await namespace.delete(key)
|
|
209
|
+
},
|
|
210
|
+
async get(key) {
|
|
211
|
+
return namespace.get(key)
|
|
212
|
+
},
|
|
213
|
+
async list(options = {}) {
|
|
214
|
+
const result = await namespace.list(
|
|
215
|
+
options.prefix === undefined ? undefined : { prefix: options.prefix },
|
|
216
|
+
)
|
|
217
|
+
return { keys: result.keys.map(({ name }) => ({ name })) }
|
|
218
|
+
},
|
|
219
|
+
async put(key, value, options = {}) {
|
|
220
|
+
const expirationTtl = cloudflareKvExpirationTtl(options.ttl)
|
|
221
|
+
if (expirationTtl === 0) {
|
|
222
|
+
await namespace.delete(key)
|
|
223
|
+
return
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
await namespace.put(key, value, expirationTtl === undefined ? undefined : { expirationTtl })
|
|
227
|
+
},
|
|
228
|
+
})
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export declare namespace cloudflareKv {
|
|
232
|
+
/** Minimal Cloudflare Workers KV namespace shape used by this adapter. */
|
|
233
|
+
type Namespace = {
|
|
234
|
+
/** Deletes a key. */
|
|
235
|
+
delete(key: string): Promise<void>
|
|
236
|
+
/** Gets a text value. */
|
|
237
|
+
get(key: string): Promise<null | string>
|
|
238
|
+
/** Lists keys. */
|
|
239
|
+
list(options?: {
|
|
240
|
+
/** Restrict results to keys with this prefix. */
|
|
241
|
+
prefix?: string | null | undefined
|
|
242
|
+
}): Promise<{
|
|
243
|
+
/** Matching keys. */
|
|
244
|
+
keys: readonly {
|
|
245
|
+
/** Key name. */
|
|
246
|
+
name: string
|
|
247
|
+
}[]
|
|
248
|
+
}>
|
|
249
|
+
/** Writes a text value. */
|
|
250
|
+
put(
|
|
251
|
+
key: string,
|
|
252
|
+
value: string,
|
|
253
|
+
options?: {
|
|
254
|
+
/** Native Cloudflare KV TTL in seconds. */
|
|
255
|
+
expirationTtl?: number | undefined
|
|
256
|
+
},
|
|
257
|
+
): Promise<void>
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export function durableObject(storage: durableObject.Storage): State
|
|
262
|
+
export function durableObject(
|
|
263
|
+
namespace: durableObject.Namespace,
|
|
264
|
+
options: durableObject.Options,
|
|
265
|
+
): State
|
|
266
|
+
/** Creates a store backed by Cloudflare Durable Object storage or namespace RPC. */
|
|
267
|
+
export function durableObject(
|
|
268
|
+
target: durableObject.Namespace | durableObject.Storage,
|
|
269
|
+
options?: durableObject.Options,
|
|
270
|
+
): State {
|
|
271
|
+
if (durableObjectNamespace(target)) {
|
|
272
|
+
const name = options?.name
|
|
273
|
+
if (name === undefined) throw new TypeError('Durable Object name is required')
|
|
274
|
+
return durableObjectStub(target, name)
|
|
275
|
+
}
|
|
276
|
+
return durableObjectStorage(target)
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function durableObjectStorage(storage: durableObject.Storage): State {
|
|
280
|
+
return from({
|
|
281
|
+
async delete(key) {
|
|
282
|
+
await storage.delete(key)
|
|
283
|
+
},
|
|
284
|
+
async get(key) {
|
|
285
|
+
return (await durableObjectRead(storage, key, await storage.get(key)))?.value ?? null
|
|
286
|
+
},
|
|
287
|
+
async list(options = {}) {
|
|
288
|
+
const values = await storage.list<unknown>(options)
|
|
289
|
+
const keys: { name: string }[] = []
|
|
290
|
+
|
|
291
|
+
for (const [key, value] of values) {
|
|
292
|
+
if (await durableObjectRead(storage, key, value)) keys.push({ name: key })
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
return { keys }
|
|
296
|
+
},
|
|
297
|
+
async put(key, value, options = {}) {
|
|
298
|
+
if (options.ttl !== undefined && options.ttl <= 0) {
|
|
299
|
+
await storage.delete(key)
|
|
300
|
+
return
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
await storage.put(key, durableObjectValue(value, options))
|
|
304
|
+
},
|
|
305
|
+
})
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export declare namespace durableObject {
|
|
309
|
+
/** Minimal Durable Object namespace shape used by this adapter. */
|
|
310
|
+
type Namespace = {
|
|
311
|
+
/** Gets the Durable Object stub for a stable object name. */
|
|
312
|
+
getByName(name: string): Stub
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/** Options for creating a store from a Durable Object namespace. */
|
|
316
|
+
type Options = {
|
|
317
|
+
/**
|
|
318
|
+
* Durable Object name. A static string pins one object holding every key
|
|
319
|
+
* (enumerable via `list`). A resolver function shards keys across objects
|
|
320
|
+
* (`name(key)` per operation) so each key's object is created near the
|
|
321
|
+
* traffic that uses it — sharded stores cannot `list`.
|
|
322
|
+
*/
|
|
323
|
+
name: string | ((key: string) => string)
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/** Minimal Cloudflare Durable Object storage shape used by this adapter. */
|
|
327
|
+
type Storage = {
|
|
328
|
+
/** Deletes a key. */
|
|
329
|
+
delete(key: string): Promise<unknown>
|
|
330
|
+
/** Gets a stored value. */
|
|
331
|
+
get<value = unknown>(key: string): Promise<undefined | value>
|
|
332
|
+
/** Lists stored values. */
|
|
333
|
+
list<value = unknown>(options?: {
|
|
334
|
+
/** Restrict results to keys with this prefix. */
|
|
335
|
+
prefix?: string | undefined
|
|
336
|
+
}): Promise<Map<string, value>>
|
|
337
|
+
/** Writes a stored value. */
|
|
338
|
+
put<value>(key: string, value: value): Promise<void>
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Minimal Durable Object RPC stub shape used by this adapter — just the store
|
|
343
|
+
* operations (no `type` discriminant, which an RPC stub doesn't carry).
|
|
344
|
+
* `increment` is required: the stub mirrors the `tapimo/cloudflare`
|
|
345
|
+
* `DurableObject` class, whose RPC surface implements it.
|
|
346
|
+
*/
|
|
347
|
+
type Stub = Omit<Store, 'increment' | 'type'> & {
|
|
348
|
+
/** Atomically increments an integer counter inside the object. */
|
|
349
|
+
increment(key: string, options?: Store.PutOptions): Promise<number>
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Wraps an async producer with store-backed caching. Returns the cached value if
|
|
355
|
+
* present and valid; otherwise runs `fetch`, stores the JSON-encoded result
|
|
356
|
+
* under `key` with `ttl` milliseconds, and returns it.
|
|
357
|
+
*/
|
|
358
|
+
export async function memoize<value>(
|
|
359
|
+
fetch: () => Promise<value>,
|
|
360
|
+
options: memoize.Options,
|
|
361
|
+
): Promise<value> {
|
|
362
|
+
const { key, store, ttl } = options
|
|
363
|
+
|
|
364
|
+
const raw = await store.get(key)
|
|
365
|
+
if (raw) {
|
|
366
|
+
try {
|
|
367
|
+
return JSON.parse(raw) as value
|
|
368
|
+
} catch {
|
|
369
|
+
// Corrupt entries are treated as misses so the next call refreshes.
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const flights = flightsFor(store)
|
|
374
|
+
const existing = flights.get(key)
|
|
375
|
+
if (existing) return existing as Promise<value>
|
|
376
|
+
|
|
377
|
+
// Coalesce concurrent misses for this process. The store remains the cache
|
|
378
|
+
// boundary; this only prevents a local cold-key stampede while the first
|
|
379
|
+
// fetch is still in flight.
|
|
380
|
+
const flight = (async () => {
|
|
381
|
+
const value = await fetch()
|
|
382
|
+
await store.put(key, JSON.stringify(value), { ttl })
|
|
383
|
+
return value
|
|
384
|
+
})()
|
|
385
|
+
flights.set(key, flight)
|
|
386
|
+
|
|
387
|
+
try {
|
|
388
|
+
return await flight
|
|
389
|
+
} finally {
|
|
390
|
+
flights.delete(key)
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
export declare namespace memoize {
|
|
395
|
+
/** Options for {@link memoize}. */
|
|
396
|
+
type Options = {
|
|
397
|
+
/** Cache key. Callers are responsible for namespacing/versioning. */
|
|
398
|
+
key: string
|
|
399
|
+
/** Store used to persist cache entries. */
|
|
400
|
+
store: Store
|
|
401
|
+
/** Time-to-live in milliseconds. */
|
|
402
|
+
ttl: number
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
const flights = new WeakMap<Store, Map<string, Promise<unknown>>>()
|
|
407
|
+
|
|
408
|
+
function flightsFor(store: Store) {
|
|
409
|
+
let inFlight = flights.get(store)
|
|
410
|
+
if (!inFlight) {
|
|
411
|
+
inFlight = new Map()
|
|
412
|
+
flights.set(store, inFlight)
|
|
413
|
+
}
|
|
414
|
+
return inFlight
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function durableObjectNamespace(
|
|
418
|
+
target: durableObject.Namespace | durableObject.Storage,
|
|
419
|
+
): target is durableObject.Namespace {
|
|
420
|
+
return typeof target === 'object' && target !== null && 'getByName' in target
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
function durableObjectStub(
|
|
424
|
+
namespace: durableObject.Namespace,
|
|
425
|
+
name: string | ((key: string) => string),
|
|
426
|
+
): State {
|
|
427
|
+
// Durable Object stubs are request-scoped I/O objects in Workers, so
|
|
428
|
+
// resolve the stub inside each store operation instead of caching it.
|
|
429
|
+
const stub = (key: string) => namespace.getByName(typeof name === 'function' ? name(key) : name)
|
|
430
|
+
|
|
431
|
+
return from({
|
|
432
|
+
async delete(key) {
|
|
433
|
+
await durableObjectRetry(() => stub(key).delete(key))
|
|
434
|
+
},
|
|
435
|
+
async get(key) {
|
|
436
|
+
return durableObjectRetry(() => stub(key).get(key))
|
|
437
|
+
},
|
|
438
|
+
// Native RPC increment: the read-modify-write runs inside the object
|
|
439
|
+
// (one round trip, atomic) instead of the get + put fallback (two).
|
|
440
|
+
async increment(key, options) {
|
|
441
|
+
return durableObjectRetry(() => stub(key).increment(key, options))
|
|
442
|
+
},
|
|
443
|
+
async list(options) {
|
|
444
|
+
// A sharded store scatters keys across objects, so no single object can
|
|
445
|
+
// enumerate them.
|
|
446
|
+
if (typeof name === 'function')
|
|
447
|
+
throw new TypeError('cannot list a sharded Durable Object store')
|
|
448
|
+
return durableObjectRetry(() => stub('').list(options))
|
|
449
|
+
},
|
|
450
|
+
async put(key, value, options) {
|
|
451
|
+
await durableObjectRetry(() => stub(key).put(key, value, options))
|
|
452
|
+
},
|
|
453
|
+
})
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Runs a Durable Object stub call, retrying once when workerd marks the error
|
|
458
|
+
* retryable (transient transport failures, e.g. "Network connection lost.")
|
|
459
|
+
* and not overloaded. Callers pass a thunk so the retry resolves a fresh
|
|
460
|
+
* stub: a stub that threw is disconnected.
|
|
461
|
+
*/
|
|
462
|
+
async function durableObjectRetry<value>(call: () => Promise<value>): Promise<value> {
|
|
463
|
+
try {
|
|
464
|
+
return await call()
|
|
465
|
+
} catch (error) {
|
|
466
|
+
const { overloaded, retryable } = (error ?? {}) as {
|
|
467
|
+
overloaded?: boolean | undefined
|
|
468
|
+
retryable?: boolean | undefined
|
|
469
|
+
}
|
|
470
|
+
if (retryable !== true || overloaded === true) throw error
|
|
471
|
+
return call()
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
function cloudflareKvExpirationTtl(ttl: number | undefined) {
|
|
476
|
+
if (ttl === undefined) return undefined
|
|
477
|
+
if (ttl <= 0) return 0
|
|
478
|
+
|
|
479
|
+
// Workers KV expects TTLs in seconds and rejects values under 60 seconds.
|
|
480
|
+
const seconds = Math.ceil(ttl / 1_000)
|
|
481
|
+
if (seconds < 60) throw new RangeError('Cloudflare KV expirationTtl must be at least 60 seconds')
|
|
482
|
+
return seconds
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
const durableObjectValueType = 'cadent:store:v1'
|
|
486
|
+
|
|
487
|
+
type DurableObjectValue = {
|
|
488
|
+
expiresAt?: number | undefined
|
|
489
|
+
type: typeof durableObjectValueType
|
|
490
|
+
value: string
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
function durableObjectValue(value: string, options: Store.PutOptions): DurableObjectValue | string {
|
|
494
|
+
if (options.ttl === undefined) return value
|
|
495
|
+
|
|
496
|
+
// Durable Object storage does not expose per-key TTL in the current Worker
|
|
497
|
+
// API, so this adapter stores expirations in-band and evicts lazily on read.
|
|
498
|
+
return { expiresAt: Date.now() + options.ttl, type: durableObjectValueType, value }
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
async function durableObjectRead(
|
|
502
|
+
storage: durableObject.Storage,
|
|
503
|
+
key: string,
|
|
504
|
+
value: unknown,
|
|
505
|
+
): Promise<DurableObjectValue | { value: string } | null> {
|
|
506
|
+
if (typeof value === 'string') return { value }
|
|
507
|
+
if (!isDurableObjectValue(value)) return null
|
|
508
|
+
if (value.expiresAt !== undefined && value.expiresAt <= Date.now()) {
|
|
509
|
+
await storage.delete(key)
|
|
510
|
+
return null
|
|
511
|
+
}
|
|
512
|
+
return value
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
function isDurableObjectValue(value: unknown): value is DurableObjectValue {
|
|
516
|
+
if (!value || typeof value !== 'object') return false
|
|
517
|
+
return (
|
|
518
|
+
'type' in value &&
|
|
519
|
+
value.type === durableObjectValueType &&
|
|
520
|
+
'value' in value &&
|
|
521
|
+
typeof value.value === 'string' &&
|
|
522
|
+
(!('expiresAt' in value) ||
|
|
523
|
+
value.expiresAt === undefined ||
|
|
524
|
+
typeof value.expiresAt === 'number')
|
|
525
|
+
)
|
|
526
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as TestApp from '../../test/App.js'
|
|
2
|
+
import * as Runtime from '../../test/runtime.js'
|
|
3
|
+
import * as Indexer from '../apps/indexer.js'
|
|
4
|
+
|
|
5
|
+
describe('local TIDX runtime', () => {
|
|
6
|
+
test.runIf(Runtime.get().mode === 'localnet')(
|
|
7
|
+
'proxies queries to a disposable TIDX',
|
|
8
|
+
async () => {
|
|
9
|
+
const runtime = Runtime.get()
|
|
10
|
+
const client = TestApp.client()
|
|
11
|
+
const response = await client.v1.indexer.query.$get(
|
|
12
|
+
{ query: { chainId: String(runtime.chainId), sql: 'select 1 as value' } },
|
|
13
|
+
TestApp.auth,
|
|
14
|
+
)
|
|
15
|
+
const body = await TestApp.json(response, Indexer.schema.Response)
|
|
16
|
+
|
|
17
|
+
expect(response.status).toMatchInlineSnapshot(`200`)
|
|
18
|
+
expect(body.ok).toMatchInlineSnapshot(`true`)
|
|
19
|
+
expect(body.columns).toMatchInlineSnapshot(`
|
|
20
|
+
[
|
|
21
|
+
"value",
|
|
22
|
+
]
|
|
23
|
+
`)
|
|
24
|
+
expect(body.rows).toMatchInlineSnapshot(`
|
|
25
|
+
[
|
|
26
|
+
[
|
|
27
|
+
1,
|
|
28
|
+
],
|
|
29
|
+
]
|
|
30
|
+
`)
|
|
31
|
+
},
|
|
32
|
+
)
|
|
33
|
+
})
|