tapimo 0.1.0 → 0.3.0
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/README.md +57 -16
- package/dist/ApiKey.d.ts +4 -1
- package/dist/ApiKey.d.ts.map +1 -1
- package/dist/ApiKey.js +6 -2
- package/dist/ApiKey.js.map +1 -1
- package/dist/ApiKeys.d.ts +98 -3
- package/dist/ApiKeys.d.ts.map +1 -1
- package/dist/ApiKeys.js +150 -1
- package/dist/ApiKeys.js.map +1 -1
- package/dist/App.d.ts +7917 -2315
- package/dist/App.d.ts.map +1 -1
- package/dist/App.js +141 -329
- package/dist/App.js.map +1 -1
- package/dist/Scope.d.ts +20 -4
- package/dist/Scope.d.ts.map +1 -1
- package/dist/Scope.js +23 -4
- package/dist/Scope.js.map +1 -1
- package/dist/Webhooks.d.ts +2 -2
- package/dist/Webhooks.d.ts.map +1 -1
- package/dist/Webhooks.js +2 -2
- package/dist/Webhooks.js.map +1 -1
- package/dist/admin/App.d.ts +203 -120
- package/dist/admin/App.d.ts.map +1 -1
- package/dist/admin/apps/api-keys.d.ts +111 -6
- package/dist/admin/apps/api-keys.d.ts.map +1 -1
- package/dist/admin/apps/api-keys.js +67 -0
- package/dist/admin/apps/api-keys.js.map +1 -1
- package/dist/admin/apps/scopes.d.ts +12 -1
- package/dist/admin/apps/scopes.d.ts.map +1 -1
- package/dist/admin/apps/scopes.js +3 -0
- package/dist/admin/apps/scopes.js.map +1 -1
- package/dist/admin/apps/verified-tokens.d.ts +116 -116
- package/dist/admin/apps/verified-tokens.js +1 -1
- package/dist/admin/apps/verified-tokens.js.map +1 -1
- package/dist/admin/ui.generated.d.ts +1 -1
- package/dist/admin/ui.generated.js +13 -13
- package/dist/admin/ui.generated.js.map +1 -1
- package/dist/analytics/Analytics.d.ts +151 -0
- package/dist/analytics/Analytics.d.ts.map +1 -0
- package/dist/analytics/Analytics.js +212 -0
- package/dist/analytics/Analytics.js.map +1 -0
- package/dist/analytics/tables/requestEvents.d.ts +215 -0
- package/dist/analytics/tables/requestEvents.d.ts.map +1 -0
- package/dist/analytics/tables/requestEvents.js +264 -0
- package/dist/analytics/tables/requestEvents.js.map +1 -0
- package/dist/apps/data/App.d.ts +19576 -0
- package/dist/apps/data/App.d.ts.map +1 -0
- package/dist/apps/data/App.js +272 -0
- package/dist/apps/data/App.js.map +1 -0
- package/dist/apps/data/index.d.ts +3 -0
- package/dist/apps/data/index.d.ts.map +1 -0
- package/dist/apps/data/index.js +3 -0
- package/dist/apps/data/index.js.map +1 -0
- package/dist/apps/{activities.d.ts → data/routes/activities.d.ts} +32 -32
- package/dist/apps/{activities.d.ts.map → data/routes/activities.d.ts.map} +1 -1
- package/dist/apps/{activities.js → data/routes/activities.js} +13 -13
- package/dist/apps/data/routes/activities.js.map +1 -0
- package/dist/apps/{addresses.d.ts → data/routes/addresses.d.ts} +22 -22
- package/dist/apps/{addresses.d.ts.map → data/routes/addresses.d.ts.map} +1 -1
- package/dist/apps/{addresses.js → data/routes/addresses.js} +5 -5
- package/dist/apps/data/routes/addresses.js.map +1 -0
- package/dist/apps/{balances.d.ts → data/routes/balances.d.ts} +4 -4
- package/dist/apps/data/routes/balances.d.ts.map +1 -0
- package/dist/apps/{balances.js → data/routes/balances.js} +42 -11
- package/dist/apps/data/routes/balances.js.map +1 -0
- package/dist/apps/{blocks.d.ts → data/routes/blocks.d.ts} +47 -47
- package/dist/apps/data/routes/blocks.d.ts.map +1 -0
- package/dist/apps/{blocks.js → data/routes/blocks.js} +13 -13
- package/dist/apps/data/routes/blocks.js.map +1 -0
- package/dist/apps/{coingecko.d.ts → data/routes/coingecko.d.ts} +8 -8
- package/dist/apps/data/routes/coingecko.d.ts.map +1 -0
- package/dist/apps/{coingecko.js → data/routes/coingecko.js} +13 -13
- package/dist/apps/data/routes/coingecko.js.map +1 -0
- package/dist/apps/{exchanges.d.ts → data/routes/exchanges.d.ts} +108 -108
- package/dist/apps/data/routes/exchanges.d.ts.map +1 -0
- package/dist/apps/{exchanges.js → data/routes/exchanges.js} +19 -19
- package/dist/apps/data/routes/exchanges.js.map +1 -0
- package/dist/apps/{fee-amm.d.ts → data/routes/fee-amm.d.ts} +39 -39
- package/dist/apps/data/routes/fee-amm.d.ts.map +1 -0
- package/dist/apps/{fee-amm.js → data/routes/fee-amm.js} +17 -17
- package/dist/apps/data/routes/fee-amm.js.map +1 -0
- package/dist/apps/data/routes/funding.d.ts +765 -0
- package/dist/apps/data/routes/funding.d.ts.map +1 -0
- package/dist/apps/data/routes/funding.js +383 -0
- package/dist/apps/data/routes/funding.js.map +1 -0
- package/dist/apps/{indexer.d.ts → data/routes/indexer.d.ts} +4 -4
- package/dist/apps/data/routes/indexer.d.ts.map +1 -0
- package/dist/apps/{indexer.js → data/routes/indexer.js} +13 -9
- package/dist/apps/data/routes/indexer.js.map +1 -0
- package/dist/apps/{receipts.d.ts → data/routes/receipts.d.ts} +49 -49
- package/dist/apps/data/routes/receipts.d.ts.map +1 -0
- package/dist/apps/{receipts.js → data/routes/receipts.js} +14 -20
- package/dist/apps/data/routes/receipts.js.map +1 -0
- package/dist/apps/{rpc.d.ts → data/routes/rpc.d.ts} +33 -40
- package/dist/apps/data/routes/rpc.d.ts.map +1 -0
- package/dist/apps/{rpc.js → data/routes/rpc.js} +35 -7
- package/dist/apps/data/routes/rpc.js.map +1 -0
- package/dist/apps/{tokenlist.d.ts → data/routes/tokenlist.d.ts} +40 -40
- package/dist/apps/data/routes/tokenlist.d.ts.map +1 -0
- package/dist/apps/{tokenlist.js → data/routes/tokenlist.js} +8 -8
- package/dist/apps/data/routes/tokenlist.js.map +1 -0
- package/dist/apps/{tokens.d.ts → data/routes/tokens.d.ts} +468 -468
- package/dist/apps/data/routes/tokens.d.ts.map +1 -0
- package/dist/apps/{tokens.js → data/routes/tokens.js} +19 -19
- package/dist/apps/data/routes/tokens.js.map +1 -0
- package/dist/apps/{transactions.d.ts → data/routes/transactions.d.ts} +304 -304
- package/dist/apps/data/routes/transactions.d.ts.map +1 -0
- package/dist/apps/{transactions.js → data/routes/transactions.js} +13 -13
- package/dist/apps/data/routes/transactions.js.map +1 -0
- package/dist/apps/{transfers.d.ts → data/routes/transfers.d.ts} +62 -62
- package/dist/apps/data/routes/transfers.d.ts.map +1 -0
- package/dist/apps/{transfers.js → data/routes/transfers.js} +13 -13
- package/dist/apps/data/routes/transfers.js.map +1 -0
- package/dist/apps/{verified-tokens.d.ts → data/routes/verified-tokens.d.ts} +6 -6
- package/dist/apps/data/routes/verified-tokens.d.ts.map +1 -0
- package/dist/apps/{verified-tokens.js → data/routes/verified-tokens.js} +9 -9
- package/dist/apps/data/routes/verified-tokens.js.map +1 -0
- package/dist/apps/{webhooks.d.ts → data/routes/webhooks.d.ts} +17 -17
- package/dist/apps/{webhooks.d.ts.map → data/routes/webhooks.d.ts.map} +1 -1
- package/dist/apps/{webhooks.js → data/routes/webhooks.js} +38 -34
- package/dist/apps/data/routes/webhooks.js.map +1 -0
- package/dist/apps/docs/App.d.ts +29 -0
- package/dist/apps/docs/App.d.ts.map +1 -0
- package/dist/apps/docs/App.js +54 -0
- package/dist/apps/docs/App.js.map +1 -0
- package/dist/{internal/OpenApi.fonts.d.ts → apps/docs/fonts.d.ts} +1 -1
- package/dist/apps/docs/fonts.d.ts.map +1 -0
- package/dist/{internal/OpenApi.fonts.js → apps/docs/fonts.js} +1 -1
- package/dist/apps/docs/fonts.js.map +1 -0
- package/dist/apps/docs/index.d.ts +2 -0
- package/dist/apps/docs/index.d.ts.map +1 -0
- package/dist/apps/docs/index.js +2 -0
- package/dist/apps/docs/index.js.map +1 -0
- package/dist/{internal/OpenApi.logo.d.ts → apps/docs/logo.d.ts} +1 -1
- package/dist/apps/docs/logo.d.ts.map +1 -0
- package/dist/{internal/OpenApi.logo.js → apps/docs/logo.js} +1 -1
- package/dist/apps/docs/logo.js.map +1 -0
- package/dist/apps/docs/reference.d.ts +21 -0
- package/dist/apps/docs/reference.d.ts.map +1 -0
- package/dist/apps/docs/reference.js +112 -0
- package/dist/apps/docs/reference.js.map +1 -0
- package/dist/apps/index.d.ts +5 -0
- package/dist/apps/index.d.ts.map +1 -0
- package/dist/apps/index.js +5 -0
- package/dist/apps/index.js.map +1 -0
- package/dist/apps/mcp.d.ts +76 -0
- package/dist/apps/mcp.d.ts.map +1 -0
- package/dist/apps/mcp.js +188 -0
- package/dist/apps/mcp.js.map +1 -0
- package/dist/apps/metadata.d.ts +7 -0
- package/dist/apps/metadata.d.ts.map +1 -0
- package/dist/apps/metadata.js +15 -0
- package/dist/apps/metadata.js.map +1 -0
- package/dist/apps/platform/App.d.ts +5262 -0
- package/dist/apps/platform/App.d.ts.map +1 -0
- package/dist/apps/platform/App.js +84 -0
- package/dist/apps/platform/App.js.map +1 -0
- package/dist/apps/platform/Billing.d.ts +279 -0
- package/dist/apps/platform/Billing.d.ts.map +1 -0
- package/dist/apps/platform/Billing.js +477 -0
- package/dist/apps/platform/Billing.js.map +1 -0
- package/dist/apps/platform/index.d.ts +3 -0
- package/dist/apps/platform/index.d.ts.map +1 -0
- package/dist/apps/platform/index.js +3 -0
- package/dist/apps/platform/index.js.map +1 -0
- package/dist/apps/platform/routes/api-keys.d.ts +973 -0
- package/dist/apps/platform/routes/api-keys.d.ts.map +1 -0
- package/dist/apps/platform/routes/api-keys.js +430 -0
- package/dist/apps/platform/routes/api-keys.js.map +1 -0
- package/dist/apps/platform/routes/billing.d.ts +1520 -0
- package/dist/apps/platform/routes/billing.d.ts.map +1 -0
- package/dist/apps/platform/routes/billing.js +795 -0
- package/dist/apps/platform/routes/billing.js.map +1 -0
- package/dist/apps/platform/routes/invitations.d.ts +851 -0
- package/dist/apps/platform/routes/invitations.d.ts.map +1 -0
- package/dist/apps/platform/routes/invitations.js +471 -0
- package/dist/apps/platform/routes/invitations.js.map +1 -0
- package/dist/apps/platform/routes/me.d.ts +95 -0
- package/dist/apps/platform/routes/me.d.ts.map +1 -0
- package/dist/apps/platform/routes/me.js +81 -0
- package/dist/apps/platform/routes/me.js.map +1 -0
- package/dist/apps/platform/routes/members.d.ts +554 -0
- package/dist/apps/platform/routes/members.d.ts.map +1 -0
- package/dist/apps/platform/routes/members.js +251 -0
- package/dist/apps/platform/routes/members.js.map +1 -0
- package/dist/apps/platform/routes/orgs.d.ts +596 -0
- package/dist/apps/platform/routes/orgs.d.ts.map +1 -0
- package/dist/apps/platform/routes/orgs.js +276 -0
- package/dist/apps/platform/routes/orgs.js.map +1 -0
- package/dist/apps/platform/routes/projects.d.ts +740 -0
- package/dist/apps/platform/routes/projects.d.ts.map +1 -0
- package/dist/apps/platform/routes/projects.js +286 -0
- package/dist/apps/platform/routes/projects.js.map +1 -0
- package/dist/apps/platform/routes/scopes.d.ts +107 -0
- package/dist/apps/platform/routes/scopes.d.ts.map +1 -0
- package/dist/apps/platform/routes/scopes.js +66 -0
- package/dist/apps/platform/routes/scopes.js.map +1 -0
- package/dist/apps/platform/routes/usage.d.ts +588 -0
- package/dist/apps/platform/routes/usage.d.ts.map +1 -0
- package/dist/apps/platform/routes/usage.js +391 -0
- package/dist/apps/platform/routes/usage.js.map +1 -0
- package/dist/apps/relay/App.d.ts +44 -0
- package/dist/apps/relay/App.d.ts.map +1 -0
- package/dist/apps/relay/App.js +211 -0
- package/dist/apps/relay/App.js.map +1 -0
- package/dist/apps/relay/Sponsorships.d.ts +107 -0
- package/dist/apps/relay/Sponsorships.d.ts.map +1 -0
- package/dist/apps/relay/Sponsorships.js +194 -0
- package/dist/apps/relay/Sponsorships.js.map +1 -0
- package/dist/apps/relay/index.d.ts +3 -0
- package/dist/apps/relay/index.d.ts.map +1 -0
- package/dist/apps/relay/index.js +3 -0
- package/dist/apps/relay/index.js.map +1 -0
- package/dist/cli/Cli.d.ts +9 -2
- package/dist/cli/Cli.d.ts.map +1 -1
- package/dist/cli/Cli.js +12 -4
- package/dist/cli/Cli.js.map +1 -1
- package/dist/cli/commands/admin.d.ts +5 -1
- package/dist/cli/commands/admin.d.ts.map +1 -1
- package/dist/cli/commands/admin.js +82 -3
- package/dist/cli/commands/admin.js.map +1 -1
- package/dist/db/Db.d.ts +17 -0
- package/dist/db/Db.d.ts.map +1 -1
- package/dist/db/Db.js +254 -0
- package/dist/db/Db.js.map +1 -1
- package/dist/db/Schema.d.ts +159 -0
- package/dist/db/Schema.d.ts.map +1 -1
- package/dist/db/Schema.js +187 -2
- package/dist/db/Schema.js.map +1 -1
- package/dist/db/tables/billingSettings.d.ts +55 -0
- package/dist/db/tables/billingSettings.d.ts.map +1 -0
- package/dist/db/tables/billingSettings.js +70 -0
- package/dist/db/tables/billingSettings.js.map +1 -0
- package/dist/db/tables/invitations.d.ts +84 -0
- package/dist/db/tables/invitations.d.ts.map +1 -0
- package/dist/db/tables/invitations.js +165 -0
- package/dist/db/tables/invitations.js.map +1 -0
- package/dist/db/tables/memberships.d.ts +108 -0
- package/dist/db/tables/memberships.d.ts.map +1 -0
- package/dist/db/tables/memberships.js +153 -0
- package/dist/db/tables/memberships.js.map +1 -0
- package/dist/db/tables/organizations.d.ts +48 -0
- package/dist/db/tables/organizations.d.ts.map +1 -1
- package/dist/db/tables/organizations.js +97 -2
- package/dist/db/tables/organizations.js.map +1 -1
- package/dist/db/tables/projects.d.ts +65 -0
- package/dist/db/tables/projects.d.ts.map +1 -0
- package/dist/db/tables/projects.js +75 -0
- package/dist/db/tables/projects.js.map +1 -0
- package/dist/db/tables/requestUsage.d.ts +121 -0
- package/dist/db/tables/requestUsage.d.ts.map +1 -0
- package/dist/db/tables/requestUsage.js +150 -0
- package/dist/db/tables/requestUsage.js.map +1 -0
- package/dist/db/tables/sponsoredTransactions.d.ts +239 -0
- package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -0
- package/dist/db/tables/sponsoredTransactions.js +314 -0
- package/dist/db/tables/sponsoredTransactions.js.map +1 -0
- package/dist/db/tables/stripeCustomers.d.ts +81 -0
- package/dist/db/tables/stripeCustomers.d.ts.map +1 -0
- package/dist/db/tables/stripeCustomers.js +120 -0
- package/dist/db/tables/stripeCustomers.js.map +1 -0
- package/dist/db/tables/users.d.ts +48 -0
- package/dist/db/tables/users.d.ts.map +1 -0
- package/dist/db/tables/users.js +65 -0
- package/dist/db/tables/users.js.map +1 -0
- package/dist/handlers/internal/executionError.d.ts.map +1 -1
- package/dist/handlers/internal/executionError.js +2 -0
- package/dist/handlers/internal/executionError.js.map +1 -1
- package/dist/handlers/internal/multisig.d.ts +2 -6
- package/dist/handlers/internal/multisig.d.ts.map +1 -1
- package/dist/handlers/internal/multisig.js +11 -36
- package/dist/handlers/internal/multisig.js.map +1 -1
- package/dist/handlers/internal/sponsorship.d.ts +35 -2
- package/dist/handlers/internal/sponsorship.d.ts.map +1 -1
- package/dist/handlers/internal/sponsorship.js +53 -14
- package/dist/handlers/internal/sponsorship.js.map +1 -1
- package/dist/handlers/internal/utils.d.ts +10 -3
- package/dist/handlers/internal/utils.d.ts.map +1 -1
- package/dist/handlers/internal/utils.js +14 -6
- package/dist/handlers/internal/utils.js.map +1 -1
- package/dist/handlers/relay.d.ts +6 -2
- package/dist/handlers/relay.d.ts.map +1 -1
- package/dist/handlers/relay.js +16 -2
- package/dist/handlers/relay.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/internal/Auth.d.ts +222 -6
- package/dist/internal/Auth.d.ts.map +1 -1
- package/dist/internal/Auth.js +625 -74
- package/dist/internal/Auth.js.map +1 -1
- package/dist/internal/Cache.d.ts.map +1 -1
- package/dist/internal/Cache.js +4 -1
- package/dist/internal/Cache.js.map +1 -1
- package/dist/internal/EdgeCache.d.ts +10 -0
- package/dist/internal/EdgeCache.d.ts.map +1 -1
- package/dist/internal/EdgeCache.js +39 -8
- package/dist/internal/EdgeCache.js.map +1 -1
- package/dist/internal/Fees.d.ts +27 -0
- package/dist/internal/Fees.d.ts.map +1 -0
- package/dist/internal/Fees.js +48 -0
- package/dist/internal/Fees.js.map +1 -0
- package/dist/internal/FundingRoutes.d.ts +183 -0
- package/dist/internal/FundingRoutes.d.ts.map +1 -0
- package/dist/internal/FundingRoutes.js +490 -0
- package/dist/internal/FundingRoutes.js.map +1 -0
- package/dist/internal/Id.d.ts +15 -0
- package/dist/internal/Id.d.ts.map +1 -0
- package/dist/internal/Id.js +22 -0
- package/dist/internal/Id.js.map +1 -0
- package/dist/internal/Log.d.ts +9 -3
- package/dist/internal/Log.d.ts.map +1 -1
- package/dist/internal/Log.js +7 -0
- package/dist/internal/Log.js.map +1 -1
- package/dist/internal/OpenApi.d.ts +10 -20
- package/dist/internal/OpenApi.d.ts.map +1 -1
- package/dist/internal/OpenApi.js +8 -58
- package/dist/internal/OpenApi.js.map +1 -1
- package/dist/internal/RateLimit.d.ts +25 -0
- package/dist/internal/RateLimit.d.ts.map +1 -1
- package/dist/internal/RateLimit.js +51 -0
- package/dist/internal/RateLimit.js.map +1 -1
- package/dist/internal/Response.d.ts +1 -1
- package/dist/internal/Response.d.ts.map +1 -1
- package/dist/internal/Schema.d.ts +8 -0
- package/dist/internal/Schema.d.ts.map +1 -1
- package/dist/internal/Schema.js +7 -0
- package/dist/internal/Schema.js.map +1 -1
- package/dist/internal/Store.d.ts +7 -1
- package/dist/internal/Store.d.ts.map +1 -1
- package/dist/internal/Store.js +16 -2
- package/dist/internal/Store.js.map +1 -1
- package/dist/internal/Viem.d.ts +84 -0
- package/dist/internal/Viem.d.ts.map +1 -1
- package/dist/internal/Webhooks.d.ts +1 -1
- package/dist/internal/Webhooks.d.ts.map +1 -1
- package/dist/internal/Webhooks.js +3 -3
- package/dist/internal/Webhooks.js.map +1 -1
- package/package.json +33 -6
- package/src/ApiKey.ts +10 -2
- package/src/ApiKeys.test.ts +155 -1
- package/src/ApiKeys.ts +203 -1
- package/src/App.test.ts +64 -75741
- package/src/App.ts +218 -420
- package/src/Client.test-d.ts +657 -15
- package/src/Scope.ts +26 -4
- package/src/Webhooks.ts +2 -2
- package/src/admin/apps/api-keys.test.ts +51 -0
- package/src/admin/apps/api-keys.ts +84 -0
- package/src/admin/apps/scopes.test.ts +11 -1
- package/src/admin/apps/scopes.ts +3 -0
- package/src/admin/apps/verified-tokens.ts +1 -1
- package/src/admin/ui/src/components/Page.tsx +2 -3
- package/src/admin/ui/src/routes/__root.tsx +2 -2
- package/src/admin/ui/src/routes/api-keys.tsx +2 -2
- package/src/admin/ui.generated.ts +13 -13
- package/src/analytics/Analytics.test.ts +527 -0
- package/src/analytics/Analytics.ts +318 -0
- package/src/analytics/tables/requestEvents.ts +523 -0
- package/src/apps/composition.test.ts +66 -0
- package/src/apps/data/App.test.ts +72 -0
- package/src/apps/data/App.ts +330 -0
- package/src/apps/data/index.ts +2 -0
- package/src/apps/{activities.test.ts → data/routes/activities.test.ts} +3 -3
- package/src/apps/{activities.ts → data/routes/activities.ts} +15 -15
- package/src/apps/{addresses.ts → data/routes/addresses.ts} +6 -6
- package/src/apps/{balances.test.ts → data/routes/balances.test.ts} +2 -2
- package/src/apps/{balances.ts → data/routes/balances.ts} +46 -12
- package/src/apps/{blocks.test.ts → data/routes/blocks.test.ts} +3 -3
- package/src/apps/{blocks.ts → data/routes/blocks.ts} +14 -14
- package/src/apps/{coingecko.test.ts → data/routes/coingecko.test.ts} +2 -2
- package/src/apps/{coingecko.ts → data/routes/coingecko.ts} +14 -14
- package/src/apps/{exchanges.test.ts → data/routes/exchanges.test.ts} +1 -1
- package/src/apps/{exchanges.ts → data/routes/exchanges.ts} +20 -20
- package/src/apps/{fee-amm.test.ts → data/routes/fee-amm.test.ts} +3 -3
- package/src/apps/{fee-amm.ts → data/routes/fee-amm.ts} +18 -18
- package/src/apps/data/routes/funding.test.ts +214 -0
- package/src/apps/data/routes/funding.ts +584 -0
- package/src/apps/{indexer.test.ts → data/routes/indexer.test.ts} +26 -5
- package/src/apps/{indexer.ts → data/routes/indexer.ts} +13 -10
- package/src/apps/{receipts.test.ts → data/routes/receipts.test.ts} +3 -3
- package/src/apps/{receipts.ts → data/routes/receipts.ts} +15 -22
- package/src/apps/{rpc.test.ts → data/routes/rpc.test.ts} +122 -7
- package/src/apps/{rpc.ts → data/routes/rpc.ts} +38 -8
- package/src/apps/{tokenlist.test.ts → data/routes/tokenlist.test.ts} +3 -3
- package/src/apps/{tokenlist.ts → data/routes/tokenlist.ts} +9 -9
- package/src/apps/{tokens.test.ts → data/routes/tokens.test.ts} +2 -2
- package/src/apps/{tokens.ts → data/routes/tokens.ts} +20 -20
- package/src/apps/{transactions.test.ts → data/routes/transactions.test.ts} +3 -3
- package/src/apps/{transactions.ts → data/routes/transactions.ts} +15 -15
- package/src/apps/{transfers.test.ts → data/routes/transfers.test.ts} +2 -2
- package/src/apps/{transfers.ts → data/routes/transfers.ts} +15 -15
- package/src/apps/{verified-tokens.test.ts → data/routes/verified-tokens.test.ts} +3 -3
- package/src/apps/{verified-tokens.ts → data/routes/verified-tokens.ts} +10 -10
- package/src/apps/{webhooks.test.ts → data/routes/webhooks.test.ts} +7 -7
- package/src/apps/{webhooks.ts → data/routes/webhooks.ts} +50 -43
- package/src/apps/docs/App.ts +65 -0
- package/src/apps/docs/index.ts +1 -0
- package/src/apps/docs/reference.ts +132 -0
- package/src/apps/index.ts +4 -0
- package/src/apps/mcp.test.ts +206 -0
- package/src/apps/mcp.ts +250 -0
- package/src/apps/metadata.ts +23 -0
- package/src/apps/platform/App.ts +169 -0
- package/src/apps/platform/Billing.test.ts +530 -0
- package/src/apps/platform/Billing.ts +682 -0
- package/src/apps/platform/index.ts +2 -0
- package/src/apps/platform/routes/api-keys.test.ts +493 -0
- package/src/apps/platform/routes/api-keys.ts +543 -0
- package/src/apps/platform/routes/billing.test.ts +1100 -0
- package/src/apps/platform/routes/billing.ts +966 -0
- package/src/apps/platform/routes/invitations.test.ts +409 -0
- package/src/apps/platform/routes/invitations.ts +587 -0
- package/src/apps/platform/routes/me.test.ts +351 -0
- package/src/apps/platform/routes/me.ts +106 -0
- package/src/apps/platform/routes/members.test.ts +195 -0
- package/src/apps/platform/routes/members.ts +313 -0
- package/src/apps/platform/routes/orgs.test.ts +313 -0
- package/src/apps/platform/routes/orgs.ts +365 -0
- package/src/apps/platform/routes/projects.test.ts +181 -0
- package/src/apps/platform/routes/projects.ts +367 -0
- package/src/apps/platform/routes/scopes.test.ts +29 -0
- package/src/apps/platform/routes/scopes.ts +85 -0
- package/src/apps/platform/routes/usage.test.ts +422 -0
- package/src/apps/platform/routes/usage.ts +527 -0
- package/src/apps/relay/App.test.ts +933 -0
- package/src/apps/relay/App.ts +236 -0
- package/src/apps/relay/Sponsorships.test.ts +375 -0
- package/src/apps/relay/Sponsorships.ts +306 -0
- package/src/apps/relay/index.ts +2 -0
- package/src/cli/Cli.test.ts +25 -2
- package/src/cli/Cli.ts +18 -3
- package/src/cli/commands/admin.test.ts +83 -0
- package/src/cli/commands/admin.ts +91 -3
- package/src/db/Db.ts +296 -0
- package/src/db/Schema.ts +241 -2
- package/src/db/tables/billingSettings.test.ts +87 -0
- package/src/db/tables/billingSettings.ts +108 -0
- package/src/db/tables/invitations.ts +198 -0
- package/src/db/tables/memberships.ts +208 -0
- package/src/db/tables/organizations.test.ts +38 -2
- package/src/db/tables/organizations.ts +117 -2
- package/src/db/tables/projects.ts +107 -0
- package/src/db/tables/requestUsage.ts +231 -0
- package/src/db/tables/sponsoredTransactions.test.ts +325 -0
- package/src/db/tables/sponsoredTransactions.ts +464 -0
- package/src/db/tables/stripeCustomers.test.ts +109 -0
- package/src/db/tables/stripeCustomers.ts +162 -0
- package/src/db/tables/users.ts +85 -0
- package/src/handlers/internal/executionError.ts +2 -0
- package/src/handlers/internal/multisig.test.ts +84 -10
- package/src/handlers/internal/multisig.ts +15 -50
- package/src/handlers/internal/sponsorship.ts +106 -20
- package/src/handlers/internal/utils.ts +19 -7
- package/src/handlers/{internal/relay.localnet.test.ts → relay.test.ts} +2 -2
- package/src/handlers/relay.ts +23 -5
- package/src/index.ts +1 -0
- package/src/internal/Auth.test.ts +491 -25
- package/src/internal/Auth.ts +839 -84
- package/src/internal/Cache.ts +5 -1
- package/src/internal/Cursor.test.ts +3 -2
- package/src/internal/EdgeCache.test.ts +27 -4
- package/src/internal/EdgeCache.ts +47 -9
- package/src/internal/Fees.test.ts +32 -0
- package/src/internal/Fees.ts +51 -0
- package/src/internal/FundingRoutes.ts +583 -0
- package/src/internal/Id.test.ts +17 -0
- package/src/internal/Id.ts +24 -0
- package/src/internal/Log.test.ts +2 -1
- package/src/internal/Log.ts +16 -3
- package/src/internal/MetricSink.ts +1 -1
- package/src/internal/OpenApi.ts +12 -60
- package/src/internal/RateLimit.test.ts +84 -0
- package/src/internal/RateLimit.ts +70 -0
- package/src/internal/Response.ts +1 -1
- package/src/internal/Schema.ts +8 -0
- package/src/internal/Store.test.ts +52 -0
- package/src/internal/Store.ts +24 -5
- package/src/internal/Tidx.local.test.ts +1 -1
- package/src/internal/VerifiedTokens.test.ts +1 -1
- package/src/internal/WebhookDestination.test.ts +1 -1
- package/src/internal/Webhooks.test.ts +7 -7
- package/src/internal/Webhooks.ts +4 -4
- package/dist/apps/activities.js.map +0 -1
- package/dist/apps/addresses.js.map +0 -1
- package/dist/apps/balances.d.ts.map +0 -1
- package/dist/apps/balances.js.map +0 -1
- package/dist/apps/blocks.d.ts.map +0 -1
- package/dist/apps/blocks.js.map +0 -1
- package/dist/apps/coingecko.d.ts.map +0 -1
- package/dist/apps/coingecko.js.map +0 -1
- package/dist/apps/exchanges.d.ts.map +0 -1
- package/dist/apps/exchanges.js.map +0 -1
- package/dist/apps/fee-amm.d.ts.map +0 -1
- package/dist/apps/fee-amm.js.map +0 -1
- package/dist/apps/indexer.d.ts.map +0 -1
- package/dist/apps/indexer.js.map +0 -1
- package/dist/apps/receipts.d.ts.map +0 -1
- package/dist/apps/receipts.js.map +0 -1
- package/dist/apps/rpc.d.ts.map +0 -1
- package/dist/apps/rpc.js.map +0 -1
- package/dist/apps/tokenlist.d.ts.map +0 -1
- package/dist/apps/tokenlist.js.map +0 -1
- package/dist/apps/tokens.d.ts.map +0 -1
- package/dist/apps/tokens.js.map +0 -1
- package/dist/apps/transactions.d.ts.map +0 -1
- package/dist/apps/transactions.js.map +0 -1
- package/dist/apps/transfers.d.ts.map +0 -1
- package/dist/apps/transfers.js.map +0 -1
- package/dist/apps/verified-tokens.d.ts.map +0 -1
- package/dist/apps/verified-tokens.js.map +0 -1
- package/dist/apps/webhooks.js.map +0 -1
- package/dist/internal/OpenApi.fonts.d.ts.map +0 -1
- package/dist/internal/OpenApi.fonts.js.map +0 -1
- package/dist/internal/OpenApi.logo.d.ts.map +0 -1
- package/dist/internal/OpenApi.logo.js.map +0 -1
- package/src/handlers/internal/multisig.localnet.test.ts +0 -146
- /package/src/{internal/OpenApi.fonts.ts → apps/docs/fonts.ts} +0 -0
- /package/src/{internal/OpenApi.logo.ts → apps/docs/logo.ts} +0 -0
|
@@ -175,6 +175,7 @@ describe('require', () => {
|
|
|
175
175
|
expect(entries[0]?.errorCode).toMatchInlineSnapshot(`"api_key_forbidden"`)
|
|
176
176
|
expect(entries[0]?.principal).toMatchInlineSnapshot(`
|
|
177
177
|
{
|
|
178
|
+
"billingActive": false,
|
|
178
179
|
"environment": "production",
|
|
179
180
|
"id": "key_test",
|
|
180
181
|
"orgId": "org_test",
|
|
@@ -267,10 +268,247 @@ describe('require', () => {
|
|
|
267
268
|
})
|
|
268
269
|
})
|
|
269
270
|
|
|
271
|
+
describe('session', () => {
|
|
272
|
+
const sessionPrincipal = {
|
|
273
|
+
id: 'usr_test',
|
|
274
|
+
identity: { provider: 'wallet', subject: '0x0000000000000000000000000000000000000001' },
|
|
275
|
+
type: 'session',
|
|
276
|
+
} as const satisfies Auth.SessionPrincipal
|
|
277
|
+
|
|
278
|
+
/** Harness with the session capability installed on the auth context. */
|
|
279
|
+
function createSessionApp(
|
|
280
|
+
options: {
|
|
281
|
+
policy?: Auth.require.Policy | undefined
|
|
282
|
+
resolve?: Auth.Session['resolve'] | undefined
|
|
283
|
+
} = {},
|
|
284
|
+
) {
|
|
285
|
+
const auth = {
|
|
286
|
+
apiKey: { resolve: TestApp.resolver({ keys: [{ ...key, token }] }) },
|
|
287
|
+
rateLimit: RateLimit.memory(),
|
|
288
|
+
...(options.resolve ? { session: { resolve: options.resolve } } : {}),
|
|
289
|
+
}
|
|
290
|
+
const app = new Hono<Auth.Environment>()
|
|
291
|
+
app.use('*', async (c, next) => {
|
|
292
|
+
c.set('auth', auth)
|
|
293
|
+
await next()
|
|
294
|
+
})
|
|
295
|
+
app.use('/protected', Auth.require(options.policy ?? { session: true }))
|
|
296
|
+
app.get('/protected', (c) => c.json(serializePrincipal(Auth.getPrincipal(c))))
|
|
297
|
+
return app
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
test('resolves cookie sessions for session-lane routes', async () => {
|
|
301
|
+
const app = createSessionApp({ resolve: async () => sessionPrincipal })
|
|
302
|
+
const response = await app.request('/protected')
|
|
303
|
+
const body = await response.json()
|
|
304
|
+
|
|
305
|
+
expect(response.status).toMatchInlineSnapshot(`200`)
|
|
306
|
+
expect(body).toMatchInlineSnapshot(`
|
|
307
|
+
{
|
|
308
|
+
"id": "usr_test",
|
|
309
|
+
"type": "session",
|
|
310
|
+
}
|
|
311
|
+
`)
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
test('falls through to the session lane on invalid bearer tokens', async () => {
|
|
315
|
+
const app = createSessionApp({ resolve: async () => sessionPrincipal })
|
|
316
|
+
const response = await app.request('/protected', {
|
|
317
|
+
headers: { authorization: 'Bearer not-an-api-key' },
|
|
318
|
+
})
|
|
319
|
+
|
|
320
|
+
expect(response.status).toMatchInlineSnapshot(`200`)
|
|
321
|
+
})
|
|
322
|
+
|
|
323
|
+
test('rejects anonymous requests without a session', async () => {
|
|
324
|
+
const app = createSessionApp({ resolve: async () => null })
|
|
325
|
+
const response = await app.request('/protected')
|
|
326
|
+
|
|
327
|
+
expect(response.status).toMatchInlineSnapshot(`401`)
|
|
328
|
+
})
|
|
329
|
+
|
|
330
|
+
test('stays closed without the app session capability', async () => {
|
|
331
|
+
const app = createSessionApp()
|
|
332
|
+
const response = await app.request('/protected')
|
|
333
|
+
|
|
334
|
+
expect(response.status).toMatchInlineSnapshot(`401`)
|
|
335
|
+
})
|
|
336
|
+
|
|
337
|
+
test('valid API keys win over sessions', async () => {
|
|
338
|
+
const app = createSessionApp({
|
|
339
|
+
policy: { apiKey: { scopes: ['data:read'] }, session: true },
|
|
340
|
+
resolve: async () => sessionPrincipal,
|
|
341
|
+
})
|
|
342
|
+
const response = await app.request('/protected', {
|
|
343
|
+
headers: { 'tempo-api-key': token },
|
|
344
|
+
})
|
|
345
|
+
const body = await response.json()
|
|
346
|
+
|
|
347
|
+
expect(response.status).toMatchInlineSnapshot(`200`)
|
|
348
|
+
expect(body).toMatchInlineSnapshot(`
|
|
349
|
+
{
|
|
350
|
+
"id": "key_test",
|
|
351
|
+
"orgId": "org_test",
|
|
352
|
+
"type": "api_key",
|
|
353
|
+
}
|
|
354
|
+
`)
|
|
355
|
+
})
|
|
356
|
+
|
|
357
|
+
test('is not consulted on lane-less routes', async () => {
|
|
358
|
+
const app = createSessionApp({
|
|
359
|
+
policy: {},
|
|
360
|
+
resolve: async () => sessionPrincipal,
|
|
361
|
+
})
|
|
362
|
+
const response = await app.request('/protected')
|
|
363
|
+
|
|
364
|
+
expect(response.status).toMatchInlineSnapshot(`401`)
|
|
365
|
+
})
|
|
366
|
+
})
|
|
367
|
+
|
|
368
|
+
describe('superAdmin', () => {
|
|
369
|
+
const secret = 'tempo:sk:a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0'
|
|
370
|
+
|
|
371
|
+
/** Auth context with the super admin secret configured alongside the test key. */
|
|
372
|
+
function withSuperAdmin(): Auth.Context {
|
|
373
|
+
return {
|
|
374
|
+
apiKey: { resolve: TestApp.resolver({ keys: [{ ...key, token }] }) },
|
|
375
|
+
rateLimit: RateLimit.memory(),
|
|
376
|
+
superAdmin: { tokenHash: ApiKey.hash(secret) },
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
test('resolves the super admin secret before key lookup', async () => {
|
|
381
|
+
const app = createApp({ auth: withSuperAdmin() })
|
|
382
|
+
const response = await app.request('/protected', {
|
|
383
|
+
headers: { 'tempo-api-key': secret },
|
|
384
|
+
})
|
|
385
|
+
const body = await response.json()
|
|
386
|
+
|
|
387
|
+
expect(response.status).toMatchInlineSnapshot(`200`)
|
|
388
|
+
expect(body).toMatchInlineSnapshot(`
|
|
389
|
+
{
|
|
390
|
+
"actor": "super_admin",
|
|
391
|
+
"id": "super_admin",
|
|
392
|
+
"type": "super_admin",
|
|
393
|
+
}
|
|
394
|
+
`)
|
|
395
|
+
})
|
|
396
|
+
|
|
397
|
+
test('accepts the super admin secret as a bearer token', async () => {
|
|
398
|
+
const app = createApp({ auth: withSuperAdmin() })
|
|
399
|
+
const response = await app.request('/protected', {
|
|
400
|
+
headers: { authorization: `Bearer ${secret}` },
|
|
401
|
+
})
|
|
402
|
+
|
|
403
|
+
expect(response.status).toMatchInlineSnapshot(`200`)
|
|
404
|
+
})
|
|
405
|
+
|
|
406
|
+
test('bypasses scope checks', async () => {
|
|
407
|
+
const app = createApp({
|
|
408
|
+
auth: withSuperAdmin(),
|
|
409
|
+
policy: { apiKey: { scopes: [Scope.wildcard] } },
|
|
410
|
+
})
|
|
411
|
+
const response = await app.request('/protected', {
|
|
412
|
+
headers: { 'tempo-api-key': secret },
|
|
413
|
+
})
|
|
414
|
+
|
|
415
|
+
expect(response.status).toMatchInlineSnapshot(`200`)
|
|
416
|
+
})
|
|
417
|
+
|
|
418
|
+
test('non-matching tokens fall through to key lookup', async () => {
|
|
419
|
+
const app = createApp({ auth: withSuperAdmin() })
|
|
420
|
+
const response = await app.request('/protected', {
|
|
421
|
+
headers: { 'tempo-api-key': token },
|
|
422
|
+
})
|
|
423
|
+
const body = await response.json()
|
|
424
|
+
|
|
425
|
+
expect(response.status).toMatchInlineSnapshot(`200`)
|
|
426
|
+
expect(body).toMatchInlineSnapshot(`
|
|
427
|
+
{
|
|
428
|
+
"id": "key_test",
|
|
429
|
+
"orgId": "org_test",
|
|
430
|
+
"type": "api_key",
|
|
431
|
+
}
|
|
432
|
+
`)
|
|
433
|
+
})
|
|
434
|
+
|
|
435
|
+
test('is disabled when unconfigured', async () => {
|
|
436
|
+
const app = createApp()
|
|
437
|
+
const response = await app.request('/protected', {
|
|
438
|
+
headers: { 'tempo-api-key': secret },
|
|
439
|
+
})
|
|
440
|
+
const body = await response.json()
|
|
441
|
+
|
|
442
|
+
expect(response.status).toMatchInlineSnapshot(`401`)
|
|
443
|
+
expect(body).toMatchInlineSnapshot(`
|
|
444
|
+
{
|
|
445
|
+
"error": {
|
|
446
|
+
"code": "api_key_invalid",
|
|
447
|
+
"message": "Invalid API key",
|
|
448
|
+
},
|
|
449
|
+
}
|
|
450
|
+
`)
|
|
451
|
+
})
|
|
452
|
+
|
|
453
|
+
test('is the only principal for lane-less routes', async () => {
|
|
454
|
+
const app = createApp({ auth: withSuperAdmin(), policy: {} })
|
|
455
|
+
|
|
456
|
+
const superAdmin = await app.request('/protected', {
|
|
457
|
+
headers: { 'tempo-api-key': secret },
|
|
458
|
+
})
|
|
459
|
+
expect(superAdmin.status).toMatchInlineSnapshot(`200`)
|
|
460
|
+
|
|
461
|
+
const keyed = await app.request('/protected', {
|
|
462
|
+
headers: { 'tempo-api-key': token },
|
|
463
|
+
})
|
|
464
|
+
const body = await keyed.json()
|
|
465
|
+
expect(keyed.status).toMatchInlineSnapshot(`403`)
|
|
466
|
+
expect(body).toMatchInlineSnapshot(`
|
|
467
|
+
{
|
|
468
|
+
"error": {
|
|
469
|
+
"code": "forbidden",
|
|
470
|
+
"message": "API key not permitted for this route",
|
|
471
|
+
},
|
|
472
|
+
}
|
|
473
|
+
`)
|
|
474
|
+
|
|
475
|
+
const anonymous = await app.request('/protected')
|
|
476
|
+
expect(anonymous.status).toMatchInlineSnapshot(`401`)
|
|
477
|
+
})
|
|
478
|
+
|
|
479
|
+
test('logs the principal actor, never the secret', async () => {
|
|
480
|
+
const entries: Log.Entry[] = []
|
|
481
|
+
const app = createApp({
|
|
482
|
+
auth: withSuperAdmin(),
|
|
483
|
+
logger: (entry) => {
|
|
484
|
+
entries.push(entry)
|
|
485
|
+
},
|
|
486
|
+
})
|
|
487
|
+
await app.request('/protected', { headers: { 'tempo-api-key': secret } })
|
|
488
|
+
|
|
489
|
+
expect(entries[0]?.principal).toMatchInlineSnapshot(`
|
|
490
|
+
{
|
|
491
|
+
"actor": "super_admin",
|
|
492
|
+
"id": "super_admin",
|
|
493
|
+
"type": "super_admin",
|
|
494
|
+
}
|
|
495
|
+
`)
|
|
496
|
+
expect(JSON.stringify(entries)).not.toContain(secret)
|
|
497
|
+
})
|
|
498
|
+
})
|
|
499
|
+
|
|
270
500
|
describe('environment', () => {
|
|
271
501
|
const sandboxToken = 'secret_sandbox'
|
|
272
502
|
|
|
273
|
-
function createChainApp(
|
|
503
|
+
function createChainApp(
|
|
504
|
+
options: {
|
|
505
|
+
public?: boolean
|
|
506
|
+
publicRateLimit?: RateLimit.Limit
|
|
507
|
+
rateLimit?: RateLimit.Store
|
|
508
|
+
sandboxBillingActive?: boolean
|
|
509
|
+
mpp?: Auth.Context['mpp']
|
|
510
|
+
} = {},
|
|
511
|
+
) {
|
|
274
512
|
const auth: Auth.Context = {
|
|
275
513
|
apiKey: {
|
|
276
514
|
resolve: TestApp.resolver({
|
|
@@ -282,27 +520,33 @@ describe('require', () => {
|
|
|
282
520
|
orgId: 'org_test',
|
|
283
521
|
scopes: [Scope.wildcard],
|
|
284
522
|
token: sandboxToken,
|
|
523
|
+
...(options.sandboxBillingActive === undefined
|
|
524
|
+
? {}
|
|
525
|
+
: { billingActive: options.sandboxBillingActive }),
|
|
285
526
|
},
|
|
286
527
|
],
|
|
287
528
|
}),
|
|
288
529
|
},
|
|
289
|
-
rateLimit: RateLimit.memory(),
|
|
530
|
+
rateLimit: options.rateLimit ?? RateLimit.memory(),
|
|
531
|
+
...(options.publicRateLimit ? { publicRateLimit: options.publicRateLimit } : {}),
|
|
532
|
+
...(options.mpp ? { mpp: options.mpp } : {}),
|
|
290
533
|
}
|
|
291
534
|
const policy: Auth.require.Policy = {
|
|
292
535
|
apiKey: { scopes: [Scope.wildcard] },
|
|
293
536
|
...(options.public === false ? {} : { public: { rateLimit: { perMinute: 60 } } }),
|
|
537
|
+
...(options.mpp ? { mpp: { charge: { amount: '1' } } } : {}),
|
|
294
538
|
}
|
|
295
539
|
const app = new Hono<Auth.Environment>()
|
|
296
540
|
app.use('*', async (c, next) => {
|
|
297
541
|
c.set('auth', auth)
|
|
298
|
-
// Mirror the app default so
|
|
542
|
+
// Mirror the app default (mainnet) so the effective chain is observable.
|
|
299
543
|
c.set('chainId' as never, Viem.defaultChainId as never)
|
|
300
544
|
await next()
|
|
301
545
|
})
|
|
302
546
|
app.use('/protected', Auth.require(policy))
|
|
303
547
|
app.get('/protected', (c) => {
|
|
304
548
|
// Mirror how real handlers resolve the effective chain: explicit query
|
|
305
|
-
// wins, else the context default
|
|
549
|
+
// wins, else the context default. Auth no longer steers this value.
|
|
306
550
|
const raw = c.req.query('chainId')
|
|
307
551
|
const chainId = raw ? Number(raw) : ((c.get('chainId' as never) as number | null) ?? null)
|
|
308
552
|
return c.json({ chainId, principal: Auth.getPrincipal(c)?.type ?? null })
|
|
@@ -310,45 +554,39 @@ describe('require', () => {
|
|
|
310
554
|
return app
|
|
311
555
|
}
|
|
312
556
|
|
|
313
|
-
test('
|
|
557
|
+
test('rejects a sandbox key on explicit mainnet', async () => {
|
|
314
558
|
const app = createChainApp()
|
|
315
559
|
const response = await app.request('/protected?chainId=4217', {
|
|
316
560
|
headers: { 'tempo-api-key': sandboxToken },
|
|
317
561
|
})
|
|
318
|
-
expect(response.status).toMatchInlineSnapshot(`
|
|
319
|
-
// Treated as anonymous (public) rather than rejected; chain stays mainnet.
|
|
562
|
+
expect(response.status).toMatchInlineSnapshot(`403`)
|
|
320
563
|
expect(await response.json()).toMatchInlineSnapshot(`
|
|
321
564
|
{
|
|
322
|
-
"
|
|
323
|
-
|
|
565
|
+
"error": {
|
|
566
|
+
"code": "api_key_forbidden",
|
|
567
|
+
"message": "Sandbox API keys only support testnet. Pass a testnet \`chainId\`.",
|
|
568
|
+
},
|
|
324
569
|
}
|
|
325
570
|
`)
|
|
326
571
|
})
|
|
327
572
|
|
|
328
|
-
test('
|
|
573
|
+
test('rejects a sandbox key on the mainnet alias', async () => {
|
|
329
574
|
const app = createChainApp()
|
|
330
575
|
const response = await app.request('/protected?chainId=mainnet', {
|
|
331
576
|
headers: { 'tempo-api-key': sandboxToken },
|
|
332
577
|
})
|
|
333
|
-
expect(response.status).toMatchInlineSnapshot(`
|
|
334
|
-
expect((await response.json()).
|
|
578
|
+
expect(response.status).toMatchInlineSnapshot(`403`)
|
|
579
|
+
expect((await response.json()).error.code).toMatchInlineSnapshot(`"api_key_forbidden"`)
|
|
335
580
|
})
|
|
336
581
|
|
|
337
|
-
test('a sandbox key on mainnet with no public lane
|
|
582
|
+
test('rejects a sandbox key on explicit mainnet even with no public lane', async () => {
|
|
338
583
|
const app = createChainApp({ public: false })
|
|
339
584
|
const response = await app.request('/protected?chainId=4217', {
|
|
340
585
|
headers: { 'tempo-api-key': sandboxToken },
|
|
341
586
|
})
|
|
342
|
-
//
|
|
343
|
-
expect(response.status).toMatchInlineSnapshot(`
|
|
344
|
-
expect(await response.json()).toMatchInlineSnapshot(`
|
|
345
|
-
{
|
|
346
|
-
"error": {
|
|
347
|
-
"code": "api_key_missing",
|
|
348
|
-
"message": "Missing API key",
|
|
349
|
-
},
|
|
350
|
-
}
|
|
351
|
-
`)
|
|
587
|
+
// A resolved-but-forbidden key is a 403, not anonymous fall-through.
|
|
588
|
+
expect(response.status).toMatchInlineSnapshot(`403`)
|
|
589
|
+
expect((await response.json()).error.code).toMatchInlineSnapshot(`"api_key_forbidden"`)
|
|
352
590
|
})
|
|
353
591
|
|
|
354
592
|
test('uses a sandbox key on an explicit non-mainnet chain', async () => {
|
|
@@ -365,15 +603,17 @@ describe('require', () => {
|
|
|
365
603
|
`)
|
|
366
604
|
})
|
|
367
605
|
|
|
368
|
-
test('
|
|
606
|
+
test('authenticates a sandbox key with no query chain without steering', async () => {
|
|
369
607
|
const app = createChainApp()
|
|
370
608
|
const response = await app.request('/protected', {
|
|
371
609
|
headers: { 'tempo-api-key': sandboxToken },
|
|
372
610
|
})
|
|
611
|
+
// Auth no longer mutates `chainId`; the mainnet default is left for the
|
|
612
|
+
// data group's own sandbox chain guard to reject.
|
|
373
613
|
expect(response.status).toMatchInlineSnapshot(`200`)
|
|
374
614
|
expect(await response.json()).toMatchInlineSnapshot(`
|
|
375
615
|
{
|
|
376
|
-
"chainId":
|
|
616
|
+
"chainId": 4217,
|
|
377
617
|
"principal": "api_key",
|
|
378
618
|
}
|
|
379
619
|
`)
|
|
@@ -392,6 +632,85 @@ describe('require', () => {
|
|
|
392
632
|
}
|
|
393
633
|
`)
|
|
394
634
|
})
|
|
635
|
+
|
|
636
|
+
test('throttles a sandbox key to the public quota when billing is inactive', async () => {
|
|
637
|
+
const app = createChainApp({
|
|
638
|
+
public: true, // policy.public.rateLimit is { perMinute: 60 }
|
|
639
|
+
sandboxBillingActive: false,
|
|
640
|
+
})
|
|
641
|
+
const response = await app.request('/protected?chainId=42431', {
|
|
642
|
+
headers: { 'tempo-api-key': sandboxToken },
|
|
643
|
+
})
|
|
644
|
+
expect(response.status).toBe(200)
|
|
645
|
+
// The public ceiling (60), not the API-key default (100).
|
|
646
|
+
expect(response.headers.get('RateLimit-Limit')).toMatchInlineSnapshot(`"60"`)
|
|
647
|
+
})
|
|
648
|
+
|
|
649
|
+
test('grants a sandbox key the full API-key quota when billing is active', async () => {
|
|
650
|
+
const app = createChainApp({
|
|
651
|
+
public: true,
|
|
652
|
+
sandboxBillingActive: true,
|
|
653
|
+
})
|
|
654
|
+
const response = await app.request('/protected?chainId=42431', {
|
|
655
|
+
headers: { 'tempo-api-key': sandboxToken },
|
|
656
|
+
})
|
|
657
|
+
expect(response.status).toBe(200)
|
|
658
|
+
// The API-key default (100), not the public ceiling (60).
|
|
659
|
+
expect(response.headers.get('RateLimit-Limit')).toMatchInlineSnapshot(`"100"`)
|
|
660
|
+
})
|
|
661
|
+
|
|
662
|
+
test('throttles a sandbox key to the public quota when billing is unknown', async () => {
|
|
663
|
+
// A sandbox key minted before billing was ever active carries no
|
|
664
|
+
// `billingActive` flag; it falls back to the public quota.
|
|
665
|
+
const app = createChainApp({ public: true })
|
|
666
|
+
const response = await app.request('/protected?chainId=42431', {
|
|
667
|
+
headers: { 'tempo-api-key': sandboxToken },
|
|
668
|
+
})
|
|
669
|
+
expect(response.status).toBe(200)
|
|
670
|
+
expect(response.headers.get('RateLimit-Limit')).toMatchInlineSnapshot(`"60"`)
|
|
671
|
+
})
|
|
672
|
+
|
|
673
|
+
test('throttles to the configured public default on API-key-only routes', async () => {
|
|
674
|
+
const app = createChainApp({
|
|
675
|
+
public: false, // no per-route public lane
|
|
676
|
+
publicRateLimit: { perMinute: 7 },
|
|
677
|
+
sandboxBillingActive: false,
|
|
678
|
+
})
|
|
679
|
+
const response = await app.request('/protected?chainId=42431', {
|
|
680
|
+
headers: { 'tempo-api-key': sandboxToken },
|
|
681
|
+
})
|
|
682
|
+
expect(response.status).toBe(200)
|
|
683
|
+
expect(response.headers.get('RateLimit-Limit')).toMatchInlineSnapshot(`"7"`)
|
|
684
|
+
})
|
|
685
|
+
|
|
686
|
+
test('returns 429 (never a payment challenge) for a throttled sandbox key over quota', async () => {
|
|
687
|
+
const app = createChainApp({
|
|
688
|
+
mpp: createMppServer(),
|
|
689
|
+
rateLimit: overQuota(),
|
|
690
|
+
sandboxBillingActive: false,
|
|
691
|
+
})
|
|
692
|
+
const response = await app.request('/protected?chainId=42431', {
|
|
693
|
+
headers: { 'tempo-api-key': sandboxToken },
|
|
694
|
+
})
|
|
695
|
+
// A throttled sandbox key must activate billing, not pay per request, so
|
|
696
|
+
// the over-quota response is a plain 429 rather than a 402 MPP challenge.
|
|
697
|
+
expect(response.status).toMatchInlineSnapshot(`429`)
|
|
698
|
+
expect((await response.json()).error.code).toMatchInlineSnapshot(`"rate_limit_exceeded"`)
|
|
699
|
+
})
|
|
700
|
+
|
|
701
|
+
test('ignores a payment credential for a throttled sandbox key under quota', async () => {
|
|
702
|
+
const app = createChainApp({
|
|
703
|
+
mpp: createMppServer(),
|
|
704
|
+
sandboxBillingActive: false,
|
|
705
|
+
})
|
|
706
|
+
const response = await app.request('/protected?chainId=42431', {
|
|
707
|
+
headers: { 'tempo-api-key': sandboxToken, authorization: 'Payment paid' },
|
|
708
|
+
})
|
|
709
|
+
// The payment lane can't buy past the throttle; the request runs on the
|
|
710
|
+
// public quota instead of being charged.
|
|
711
|
+
expect(response.status).toBe(200)
|
|
712
|
+
expect(response.headers.get('RateLimit-Limit')).toMatchInlineSnapshot(`"60"`)
|
|
713
|
+
})
|
|
395
714
|
})
|
|
396
715
|
|
|
397
716
|
describe('mpp', () => {
|
|
@@ -613,6 +932,144 @@ describe('require', () => {
|
|
|
613
932
|
error.mockRestore()
|
|
614
933
|
}
|
|
615
934
|
})
|
|
935
|
+
|
|
936
|
+
test('fails open (at warn) when the rate-limit store stalls past the timeout', async () => {
|
|
937
|
+
const error = vi.spyOn(console, 'error').mockImplementation(() => {})
|
|
938
|
+
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})
|
|
939
|
+
vi.useFakeTimers()
|
|
940
|
+
try {
|
|
941
|
+
const app = createApp({
|
|
942
|
+
auth: {
|
|
943
|
+
apiKey: { resolve: TestApp.resolver({ keys: [{ ...key, token }] }) },
|
|
944
|
+
// A hot Durable Object under a storage-latency blip never returns;
|
|
945
|
+
// the consume must not block the request indefinitely.
|
|
946
|
+
rateLimit: { consume: () => new Promise<never>(() => {}) },
|
|
947
|
+
},
|
|
948
|
+
policy: { public: { rateLimit: { perMinute: 1 } } },
|
|
949
|
+
})
|
|
950
|
+
const pending = app.request('/protected', {
|
|
951
|
+
headers: { 'x-forwarded-for': '203.0.113.10' },
|
|
952
|
+
})
|
|
953
|
+
// Advance past the 300ms consume timeout so it fails open.
|
|
954
|
+
await vi.advanceTimersByTimeAsync(300)
|
|
955
|
+
const response = await pending
|
|
956
|
+
const body = await response.json()
|
|
957
|
+
|
|
958
|
+
expect(response.status).toMatchInlineSnapshot(`200`)
|
|
959
|
+
expect(response.headers.has('ratelimit-limit')).toMatchInlineSnapshot(`false`)
|
|
960
|
+
expect(body).toMatchInlineSnapshot(`
|
|
961
|
+
{
|
|
962
|
+
"id": "203.0.113.10",
|
|
963
|
+
"type": "public",
|
|
964
|
+
}
|
|
965
|
+
`)
|
|
966
|
+
// A timeout is an expected degradation: warn, never error.
|
|
967
|
+
expect(warn.mock.calls.length).toMatchInlineSnapshot(`1`)
|
|
968
|
+
expect(error.mock.calls.length).toMatchInlineSnapshot(`0`)
|
|
969
|
+
} finally {
|
|
970
|
+
vi.useRealTimers()
|
|
971
|
+
warn.mockRestore()
|
|
972
|
+
error.mockRestore()
|
|
973
|
+
}
|
|
974
|
+
})
|
|
975
|
+
|
|
976
|
+
test('fails open (at warn) when the rate-limit Durable Object is overloaded', async () => {
|
|
977
|
+
const error = vi.spyOn(console, 'error').mockImplementation(() => {})
|
|
978
|
+
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})
|
|
979
|
+
try {
|
|
980
|
+
const app = createApp({
|
|
981
|
+
auth: {
|
|
982
|
+
apiKey: { resolve: TestApp.resolver({ keys: [{ ...key, token }] }) },
|
|
983
|
+
rateLimit: {
|
|
984
|
+
async consume() {
|
|
985
|
+
throw Object.assign(
|
|
986
|
+
new Error('Durable Object is overloaded. Requests queued for too long.'),
|
|
987
|
+
{ overloaded: true },
|
|
988
|
+
)
|
|
989
|
+
},
|
|
990
|
+
},
|
|
991
|
+
},
|
|
992
|
+
policy: { public: { rateLimit: { perMinute: 1 } } },
|
|
993
|
+
})
|
|
994
|
+
const response = await app.request('/protected', {
|
|
995
|
+
headers: { 'x-forwarded-for': '203.0.113.10' },
|
|
996
|
+
})
|
|
997
|
+
const body = await response.json()
|
|
998
|
+
|
|
999
|
+
expect(response.status).toMatchInlineSnapshot(`200`)
|
|
1000
|
+
expect(response.headers.has('ratelimit-limit')).toMatchInlineSnapshot(`false`)
|
|
1001
|
+
expect(body).toMatchInlineSnapshot(`
|
|
1002
|
+
{
|
|
1003
|
+
"id": "203.0.113.10",
|
|
1004
|
+
"type": "public",
|
|
1005
|
+
}
|
|
1006
|
+
`)
|
|
1007
|
+
expect(warn.mock.calls.length).toMatchInlineSnapshot(`1`)
|
|
1008
|
+
expect(error.mock.calls.length).toMatchInlineSnapshot(`0`)
|
|
1009
|
+
} finally {
|
|
1010
|
+
warn.mockRestore()
|
|
1011
|
+
error.mockRestore()
|
|
1012
|
+
}
|
|
1013
|
+
})
|
|
1014
|
+
|
|
1015
|
+
test('fails open (at warn) when the rate-limit Durable Object errors retryably', async () => {
|
|
1016
|
+
const error = vi.spyOn(console, 'error').mockImplementation(() => {})
|
|
1017
|
+
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})
|
|
1018
|
+
try {
|
|
1019
|
+
const app = createApp({
|
|
1020
|
+
auth: {
|
|
1021
|
+
apiKey: { resolve: TestApp.resolver({ keys: [{ ...key, token }] }) },
|
|
1022
|
+
rateLimit: {
|
|
1023
|
+
async consume() {
|
|
1024
|
+
// A transient transport fault whose single retry also failed.
|
|
1025
|
+
throw Object.assign(new Error('Network connection lost.'), { retryable: true })
|
|
1026
|
+
},
|
|
1027
|
+
},
|
|
1028
|
+
},
|
|
1029
|
+
policy: { public: { rateLimit: { perMinute: 1 } } },
|
|
1030
|
+
})
|
|
1031
|
+
const response = await app.request('/protected', {
|
|
1032
|
+
headers: { 'x-forwarded-for': '203.0.113.10' },
|
|
1033
|
+
})
|
|
1034
|
+
|
|
1035
|
+
expect(response.status).toMatchInlineSnapshot(`200`)
|
|
1036
|
+
expect(response.headers.has('ratelimit-limit')).toMatchInlineSnapshot(`false`)
|
|
1037
|
+
expect(warn.mock.calls.length).toMatchInlineSnapshot(`1`)
|
|
1038
|
+
expect(error.mock.calls.length).toMatchInlineSnapshot(`0`)
|
|
1039
|
+
} finally {
|
|
1040
|
+
warn.mockRestore()
|
|
1041
|
+
error.mockRestore()
|
|
1042
|
+
}
|
|
1043
|
+
})
|
|
1044
|
+
|
|
1045
|
+
test('fails open (at warn) on a redacted Durable Object internal error', async () => {
|
|
1046
|
+
const error = vi.spyOn(console, 'error').mockImplementation(() => {})
|
|
1047
|
+
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {})
|
|
1048
|
+
try {
|
|
1049
|
+
const app = createApp({
|
|
1050
|
+
auth: {
|
|
1051
|
+
apiKey: { resolve: TestApp.resolver({ keys: [{ ...key, token }] }) },
|
|
1052
|
+
rateLimit: {
|
|
1053
|
+
async consume() {
|
|
1054
|
+
throw new Error(`internal error; reference = ${'ab12'.repeat(6)}`)
|
|
1055
|
+
},
|
|
1056
|
+
},
|
|
1057
|
+
},
|
|
1058
|
+
policy: { public: { rateLimit: { perMinute: 1 } } },
|
|
1059
|
+
})
|
|
1060
|
+
const response = await app.request('/protected', {
|
|
1061
|
+
headers: { 'x-forwarded-for': '203.0.113.10' },
|
|
1062
|
+
})
|
|
1063
|
+
|
|
1064
|
+
expect(response.status).toMatchInlineSnapshot(`200`)
|
|
1065
|
+
expect(response.headers.has('ratelimit-limit')).toMatchInlineSnapshot(`false`)
|
|
1066
|
+
expect(warn.mock.calls.length).toMatchInlineSnapshot(`1`)
|
|
1067
|
+
expect(error.mock.calls.length).toMatchInlineSnapshot(`0`)
|
|
1068
|
+
} finally {
|
|
1069
|
+
warn.mockRestore()
|
|
1070
|
+
error.mockRestore()
|
|
1071
|
+
}
|
|
1072
|
+
})
|
|
616
1073
|
})
|
|
617
1074
|
|
|
618
1075
|
describe('rateLimit per-scope', () => {
|
|
@@ -805,6 +1262,7 @@ describe('require', () => {
|
|
|
805
1262
|
expect(response.headers.get('ratelimit-limit')).toMatchInlineSnapshot(`"1"`)
|
|
806
1263
|
expect(entries[0]?.principal).toMatchInlineSnapshot(`
|
|
807
1264
|
{
|
|
1265
|
+
"billingActive": false,
|
|
808
1266
|
"environment": "production",
|
|
809
1267
|
"id": "key_test",
|
|
810
1268
|
"orgId": "org_test",
|
|
@@ -948,6 +1406,14 @@ function serializePrincipal(principal: Auth.Principal | null) {
|
|
|
948
1406
|
type: principal.type,
|
|
949
1407
|
}
|
|
950
1408
|
|
|
1409
|
+
if (principal.type === 'super_admin')
|
|
1410
|
+
return {
|
|
1411
|
+
actor: principal.actor,
|
|
1412
|
+
id: principal.id,
|
|
1413
|
+
...payment,
|
|
1414
|
+
type: principal.type,
|
|
1415
|
+
}
|
|
1416
|
+
|
|
951
1417
|
return {
|
|
952
1418
|
id: principal.id,
|
|
953
1419
|
...payment,
|