tapimo 0.15.0 → 0.18.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 +137 -0
- package/dist/ApiKey.d.ts +1 -0
- package/dist/ApiKey.d.ts.map +1 -1
- package/dist/ApiKey.js +4 -2
- package/dist/ApiKey.js.map +1 -1
- package/dist/ApiKeys.d.ts +37 -11
- package/dist/ApiKeys.d.ts.map +1 -1
- package/dist/ApiKeys.js +163 -10
- package/dist/ApiKeys.js.map +1 -1
- package/dist/App.d.ts +13085 -11974
- package/dist/App.d.ts.map +1 -1
- package/dist/App.js +46 -15
- package/dist/App.js.map +1 -1
- package/dist/Scope.d.ts +1 -1
- package/dist/Scope.d.ts.map +1 -1
- package/dist/Scope.js +1 -1
- package/dist/Scope.js.map +1 -1
- package/dist/admin/App.d.ts +896 -191
- package/dist/admin/App.d.ts.map +1 -1
- package/dist/admin/App.js +5 -1
- package/dist/admin/App.js.map +1 -1
- package/dist/admin/apps/api-keys.d.ts +33 -33
- package/dist/admin/apps/api-keys.d.ts.map +1 -1
- package/dist/admin/apps/api-keys.js +8 -3
- package/dist/admin/apps/api-keys.js.map +1 -1
- package/dist/admin/apps/earn-vaults.d.ts +66 -27
- package/dist/admin/apps/earn-vaults.d.ts.map +1 -1
- package/dist/admin/apps/earn-vaults.js +36 -7
- package/dist/admin/apps/earn-vaults.js.map +1 -1
- package/dist/admin/apps/organizations.d.ts +25 -25
- package/dist/admin/apps/organizations.d.ts.map +1 -1
- package/dist/admin/apps/organizations.js +5 -4
- package/dist/admin/apps/organizations.js.map +1 -1
- package/dist/admin/apps/route-policies.d.ts +632 -0
- package/dist/admin/apps/route-policies.d.ts.map +1 -0
- package/dist/admin/apps/route-policies.js +257 -0
- package/dist/admin/apps/route-policies.js.map +1 -0
- package/dist/admin/apps/routes-support.js +39 -3
- package/dist/admin/apps/routes-support.js.map +1 -1
- package/dist/admin/apps/routes.d.ts +454 -1
- package/dist/admin/apps/routes.d.ts.map +1 -1
- package/dist/admin/apps/routes.js +82 -1
- package/dist/admin/apps/routes.js.map +1 -1
- package/dist/admin/apps/verified-tokens.d.ts +10 -4
- package/dist/admin/apps/verified-tokens.d.ts.map +1 -1
- package/dist/admin/apps/verified-tokens.js +24 -4
- package/dist/admin/apps/verified-tokens.js.map +1 -1
- package/dist/admin/index.d.ts +0 -1
- package/dist/admin/index.d.ts.map +1 -1
- package/dist/admin/index.js +0 -1
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/ui.generated.d.ts +1 -1
- package/dist/admin/ui.generated.d.ts.map +1 -1
- package/dist/admin/ui.generated.js +99 -103
- package/dist/admin/ui.generated.js.map +1 -1
- package/dist/analytics/Analytics.d.ts.map +1 -1
- package/dist/analytics/Analytics.js +65 -1
- package/dist/analytics/Analytics.js.map +1 -1
- package/dist/analytics/tables/requestEvents.d.ts +1 -1
- package/dist/analytics/tables/requestEvents.d.ts.map +1 -1
- package/dist/analytics/tables/requestEvents.js +58 -59
- package/dist/analytics/tables/requestEvents.js.map +1 -1
- package/dist/apps/data/App.d.ts +2709 -2670
- package/dist/apps/data/App.d.ts.map +1 -1
- package/dist/apps/data/routes/activities.d.ts +5 -5
- package/dist/apps/data/routes/activities.d.ts.map +1 -1
- package/dist/apps/data/routes/activities.js +50 -0
- package/dist/apps/data/routes/activities.js.map +1 -1
- package/dist/apps/data/routes/addresses.d.ts +248 -248
- package/dist/apps/data/routes/balances.d.ts +22 -22
- package/dist/apps/data/routes/balances.d.ts.map +1 -1
- package/dist/apps/data/routes/balances.js +5 -7
- package/dist/apps/data/routes/balances.js.map +1 -1
- package/dist/apps/data/routes/coingecko.d.ts +54 -54
- package/dist/apps/data/routes/earn.d.ts +103 -12
- package/dist/apps/data/routes/earn.d.ts.map +1 -1
- package/dist/apps/data/routes/earn.js +456 -220
- package/dist/apps/data/routes/earn.js.map +1 -1
- package/dist/apps/data/routes/exchanges.d.ts.map +1 -1
- package/dist/apps/data/routes/exchanges.js +267 -97
- package/dist/apps/data/routes/exchanges.js.map +1 -1
- package/dist/apps/data/routes/fee-amm.d.ts +1 -19
- package/dist/apps/data/routes/fee-amm.d.ts.map +1 -1
- package/dist/apps/data/routes/fee-amm.js +93 -56
- package/dist/apps/data/routes/fee-amm.js.map +1 -1
- package/dist/apps/data/routes/indexer.d.ts +10 -10
- package/dist/apps/data/routes/rpc.d.ts +30 -30
- package/dist/apps/data/routes/rpc.js +45 -11
- package/dist/apps/data/routes/rpc.js.map +1 -1
- package/dist/apps/data/routes/tokens.d.ts.map +1 -1
- package/dist/apps/data/routes/tokens.js +11 -6
- package/dist/apps/data/routes/tokens.js.map +1 -1
- package/dist/apps/data/routes/transactions.d.ts +2 -0
- package/dist/apps/data/routes/transactions.d.ts.map +1 -1
- package/dist/apps/data/routes/transactions.js +30 -22
- package/dist/apps/data/routes/transactions.js.map +1 -1
- package/dist/apps/data/routes/transfers.d.ts +12 -12
- package/dist/apps/data/routes/valuation.d.ts +30 -30
- package/dist/apps/data/routes/verified-tokens.d.ts +5 -5
- package/dist/apps/data/routes/webhooks.d.ts +1 -1
- package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
- package/dist/apps/data/routes/webhooks.js +18 -11
- package/dist/apps/data/routes/webhooks.js.map +1 -1
- package/dist/apps/data/routes/zones.d.ts +3 -3
- package/dist/apps/management/App.d.ts +3043 -2198
- package/dist/apps/management/App.d.ts.map +1 -1
- package/dist/apps/management/App.js +2 -0
- package/dist/apps/management/App.js.map +1 -1
- package/dist/apps/management/Billing.d.ts +2 -4
- package/dist/apps/management/Billing.d.ts.map +1 -1
- package/dist/apps/management/Billing.js +65 -26
- package/dist/apps/management/Billing.js.map +1 -1
- package/dist/apps/management/routes/api-keys.d.ts +16 -4
- package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
- package/dist/apps/management/routes/api-keys.js +70 -11
- package/dist/apps/management/routes/api-keys.js.map +1 -1
- package/dist/apps/management/routes/billing.d.ts +84 -84
- package/dist/apps/management/routes/cli.d.ts +912 -0
- package/dist/apps/management/routes/cli.d.ts.map +1 -0
- package/dist/apps/management/routes/cli.js +416 -0
- package/dist/apps/management/routes/cli.js.map +1 -0
- package/dist/apps/management/routes/faucet.d.ts +8 -8
- package/dist/apps/management/routes/invitations.d.ts +24 -24
- package/dist/apps/management/routes/invite-links.d.ts +66 -66
- package/dist/apps/management/routes/members.d.ts +16 -16
- package/dist/apps/management/routes/orgs.d.ts +20 -20
- package/dist/apps/management/routes/orgs.d.ts.map +1 -1
- package/dist/apps/management/routes/orgs.js +8 -2
- package/dist/apps/management/routes/orgs.js.map +1 -1
- package/dist/apps/management/routes/projects.d.ts +76 -76
- package/dist/apps/management/routes/usage.d.ts +8 -8
- package/dist/apps/management/routes/verified-token-requests.d.ts +42 -42
- package/dist/apps/management/routes/verified-token-requests.d.ts.map +1 -1
- package/dist/apps/management/routes/verified-token-requests.js +7 -9
- package/dist/apps/management/routes/verified-token-requests.js.map +1 -1
- package/dist/apps/management/routes/webhooks.d.ts +119 -119
- package/dist/apps/mcp.d.ts.map +1 -1
- package/dist/apps/mcp.js +16 -3
- package/dist/apps/mcp.js.map +1 -1
- package/dist/apps/mpp/internal/Policy.d.ts.map +1 -1
- package/dist/apps/mpp/internal/Policy.js +4 -2
- package/dist/apps/mpp/internal/Policy.js.map +1 -1
- package/dist/apps/mpp/internal/Sponsorship.d.ts.map +1 -1
- package/dist/apps/mpp/internal/Sponsorship.js +6 -4
- package/dist/apps/mpp/internal/Sponsorship.js.map +1 -1
- package/dist/apps/relay/App.d.ts +8 -0
- package/dist/apps/relay/App.d.ts.map +1 -1
- package/dist/apps/relay/App.js +16 -4
- package/dist/apps/relay/App.js.map +1 -1
- package/dist/apps/relay/Sponsorships.d.ts +4 -6
- package/dist/apps/relay/Sponsorships.d.ts.map +1 -1
- package/dist/apps/relay/Sponsorships.js +71 -60
- package/dist/apps/relay/Sponsorships.js.map +1 -1
- package/dist/apps/routes/Access.d.ts +53 -0
- package/dist/apps/routes/Access.d.ts.map +1 -1
- package/dist/apps/routes/Access.js +121 -0
- package/dist/apps/routes/Access.js.map +1 -1
- package/dist/apps/routes/App.d.ts +397 -158
- package/dist/apps/routes/App.d.ts.map +1 -1
- package/dist/apps/routes/App.js.map +1 -1
- package/dist/apps/routes/routes/deposit-addresses.d.ts +169 -61
- package/dist/apps/routes/routes/deposit-addresses.d.ts.map +1 -1
- package/dist/apps/routes/routes/deposit-addresses.js +217 -32
- package/dist/apps/routes/routes/deposit-addresses.js.map +1 -1
- package/dist/apps/routes/routes/quotes.d.ts +90 -0
- package/dist/apps/routes/routes/quotes.d.ts.map +1 -1
- package/dist/apps/routes/routes/quotes.js +101 -27
- package/dist/apps/routes/routes/quotes.js.map +1 -1
- package/dist/apps/routes/routes/transfers.d.ts +187 -135
- package/dist/apps/routes/routes/transfers.d.ts.map +1 -1
- package/dist/apps/routes/routes/transfers.js +182 -37
- package/dist/apps/routes/routes/transfers.js.map +1 -1
- package/dist/cli/ApiKeys.d.ts +120 -0
- package/dist/cli/ApiKeys.d.ts.map +1 -0
- package/dist/cli/ApiKeys.js +138 -0
- package/dist/cli/ApiKeys.js.map +1 -0
- package/dist/cli/Cli.d.ts +92 -34
- package/dist/cli/Cli.d.ts.map +1 -1
- package/dist/cli/Cli.js +188 -11
- package/dist/cli/Cli.js.map +1 -1
- package/dist/cli/Credentials.d.ts +95 -0
- package/dist/cli/Credentials.d.ts.map +1 -0
- package/dist/cli/Credentials.js +110 -0
- package/dist/cli/Credentials.js.map +1 -0
- package/dist/cli/Spec.d.ts +59 -0
- package/dist/cli/Spec.d.ts.map +1 -0
- package/dist/cli/Spec.js +77 -0
- package/dist/cli/Spec.js.map +1 -0
- package/dist/cli/bin.js +5 -1
- package/dist/cli/bin.js.map +1 -1
- package/dist/cli/commands/admin.d.ts +5 -5
- package/dist/cli/commands/admin.d.ts.map +1 -1
- package/dist/cli/commands/auth.d.ts +73 -0
- package/dist/cli/commands/auth.d.ts.map +1 -0
- package/dist/cli/commands/auth.js +204 -0
- package/dist/cli/commands/auth.js.map +1 -0
- package/dist/cli/commands/keys.d.ts +17 -0
- package/dist/cli/commands/keys.d.ts.map +1 -0
- package/dist/cli/commands/keys.js +109 -0
- package/dist/cli/commands/keys.js.map +1 -0
- package/dist/cloudflare.d.ts +27 -2
- package/dist/cloudflare.d.ts.map +1 -1
- package/dist/cloudflare.js +144 -17
- package/dist/cloudflare.js.map +1 -1
- package/dist/db/Db.d.ts +23 -0
- package/dist/db/Db.d.ts.map +1 -1
- package/dist/db/Db.js +418 -1
- package/dist/db/Db.js.map +1 -1
- package/dist/db/Schema.d.ts +44 -1
- package/dist/db/Schema.d.ts.map +1 -1
- package/dist/db/Schema.js +56 -3
- package/dist/db/Schema.js.map +1 -1
- package/dist/db/tables/apiKeyAdmissions.d.ts +28 -2
- package/dist/db/tables/apiKeyAdmissions.d.ts.map +1 -1
- package/dist/db/tables/apiKeyAdmissions.js +65 -9
- package/dist/db/tables/apiKeyAdmissions.js.map +1 -1
- package/dist/db/tables/apiKeyOwnerTombstones.d.ts +2 -0
- package/dist/db/tables/apiKeyOwnerTombstones.d.ts.map +1 -1
- package/dist/db/tables/apiKeyOwnerTombstones.js +2 -2
- package/dist/db/tables/apiKeyOwnerTombstones.js.map +1 -1
- package/dist/db/tables/authAccounts.d.ts.map +1 -1
- package/dist/db/tables/authAccounts.js +1 -0
- package/dist/db/tables/authAccounts.js.map +1 -1
- package/dist/db/tables/earnVaults.d.ts +5 -1
- package/dist/db/tables/earnVaults.d.ts.map +1 -1
- package/dist/db/tables/earnVaults.js +12 -1
- package/dist/db/tables/earnVaults.js.map +1 -1
- package/dist/db/tables/organizations.d.ts +4 -2
- package/dist/db/tables/organizations.d.ts.map +1 -1
- package/dist/db/tables/organizations.js +8 -34
- package/dist/db/tables/organizations.js.map +1 -1
- package/dist/db/tables/requestUsage.d.ts +5 -1
- package/dist/db/tables/requestUsage.d.ts.map +1 -1
- package/dist/db/tables/requestUsage.js +10 -2
- package/dist/db/tables/requestUsage.js.map +1 -1
- package/dist/db/tables/rewardEligibilityAssociations.d.ts +13 -0
- package/dist/db/tables/rewardEligibilityAssociations.d.ts.map +1 -1
- package/dist/db/tables/rewardEligibilityAssociations.js +11 -0
- package/dist/db/tables/rewardEligibilityAssociations.js.map +1 -1
- package/dist/db/tables/rewardRuns.d.ts.map +1 -1
- package/dist/db/tables/rewardRuns.js +4 -0
- package/dist/db/tables/rewardRuns.js.map +1 -1
- package/dist/db/tables/rewardTransactionAttempts.d.ts.map +1 -1
- package/dist/db/tables/rewardTransactionAttempts.js +2 -0
- package/dist/db/tables/rewardTransactionAttempts.js.map +1 -1
- package/dist/db/tables/routesDepositAddresses.d.ts +29 -0
- package/dist/db/tables/routesDepositAddresses.d.ts.map +1 -1
- package/dist/db/tables/routesDepositAddresses.js +84 -31
- package/dist/db/tables/routesDepositAddresses.js.map +1 -1
- package/dist/db/tables/routesDeposits.d.ts.map +1 -1
- package/dist/db/tables/routesDeposits.js +31 -21
- package/dist/db/tables/routesDeposits.js.map +1 -1
- package/dist/db/tables/routesIdempotency.d.ts.map +1 -1
- package/dist/db/tables/routesIdempotency.js +2 -0
- package/dist/db/tables/routesIdempotency.js.map +1 -1
- package/dist/db/tables/routesPolicies.d.ts +58 -0
- package/dist/db/tables/routesPolicies.d.ts.map +1 -0
- package/dist/db/tables/routesPolicies.js +198 -0
- package/dist/db/tables/routesPolicies.js.map +1 -0
- package/dist/db/tables/routesTransferSubsidies.d.ts +22 -0
- package/dist/db/tables/routesTransferSubsidies.d.ts.map +1 -1
- package/dist/db/tables/routesTransferSubsidies.js +41 -0
- package/dist/db/tables/routesTransferSubsidies.js.map +1 -1
- package/dist/db/tables/routesTransferSubsidyNonces.d.ts.map +1 -1
- package/dist/db/tables/routesTransferSubsidyNonces.js +12 -4
- package/dist/db/tables/routesTransferSubsidyNonces.js.map +1 -1
- package/dist/db/tables/routesTransfers.d.ts +26 -1
- package/dist/db/tables/routesTransfers.d.ts.map +1 -1
- package/dist/db/tables/routesTransfers.js +36 -6
- package/dist/db/tables/routesTransfers.js.map +1 -1
- package/dist/db/tables/sponsoredTransactions.d.ts +45 -13
- package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -1
- package/dist/db/tables/sponsoredTransactions.js +144 -33
- package/dist/db/tables/sponsoredTransactions.js.map +1 -1
- package/dist/db/tables/users.d.ts +2 -0
- package/dist/db/tables/users.d.ts.map +1 -1
- package/dist/db/tables/users.js +11 -0
- package/dist/db/tables/users.js.map +1 -1
- package/dist/db/tables/verifiedTokenRequests.d.ts +21 -4
- package/dist/db/tables/verifiedTokenRequests.d.ts.map +1 -1
- package/dist/db/tables/verifiedTokenRequests.js +65 -44
- package/dist/db/tables/verifiedTokenRequests.js.map +1 -1
- package/dist/handlers/internal/multisig.d.ts +1 -1
- package/dist/handlers/internal/multisig.d.ts.map +1 -1
- package/dist/handlers/internal/multisig.js +3 -2
- package/dist/handlers/internal/multisig.js.map +1 -1
- package/dist/handlers/relay.d.ts +2 -0
- package/dist/handlers/relay.d.ts.map +1 -1
- package/dist/handlers/relay.js +34 -15
- package/dist/handlers/relay.js.map +1 -1
- package/dist/internal/Auth.d.ts +73 -6
- package/dist/internal/Auth.d.ts.map +1 -1
- package/dist/internal/Auth.js +212 -85
- package/dist/internal/Auth.js.map +1 -1
- package/dist/internal/BetterAuth.d.ts +22 -0
- package/dist/internal/BetterAuth.d.ts.map +1 -1
- package/dist/internal/BetterAuth.js +17 -14
- package/dist/internal/BetterAuth.js.map +1 -1
- package/dist/internal/Cache.d.ts +5 -0
- package/dist/internal/Cache.d.ts.map +1 -1
- package/dist/internal/Cache.js +3 -1
- package/dist/internal/Cache.js.map +1 -1
- package/dist/internal/CliAuth.d.ts +243 -0
- package/dist/internal/CliAuth.d.ts.map +1 -0
- package/dist/internal/CliAuth.js +249 -0
- package/dist/internal/CliAuth.js.map +1 -0
- package/dist/internal/EarnSharePrices.d.ts +1 -1
- package/dist/internal/EarnSharePrices.d.ts.map +1 -1
- package/dist/internal/EarnSharePrices.js +7 -3
- package/dist/internal/EarnSharePrices.js.map +1 -1
- package/dist/internal/EarnVaults.d.ts +47 -0
- package/dist/internal/EarnVaults.d.ts.map +1 -1
- package/dist/internal/EarnVaults.js +188 -4
- package/dist/internal/EarnVaults.js.map +1 -1
- package/dist/internal/EarnWithdrawals.d.ts +73 -0
- package/dist/internal/EarnWithdrawals.d.ts.map +1 -0
- package/dist/internal/EarnWithdrawals.js +189 -0
- package/dist/internal/EarnWithdrawals.js.map +1 -0
- package/dist/internal/ExecutionContext.d.ts +21 -0
- package/dist/internal/ExecutionContext.d.ts.map +1 -0
- package/dist/internal/ExecutionContext.js +16 -0
- package/dist/internal/ExecutionContext.js.map +1 -0
- package/dist/internal/Log.d.ts +7 -3
- package/dist/internal/Log.d.ts.map +1 -1
- package/dist/internal/Log.js +163 -130
- package/dist/internal/Log.js.map +1 -1
- package/dist/internal/MetricSink.d.ts.map +1 -1
- package/dist/internal/MetricSink.js +26 -1
- package/dist/internal/MetricSink.js.map +1 -1
- package/dist/internal/RateLimit.d.ts +67 -0
- package/dist/internal/RateLimit.d.ts.map +1 -1
- package/dist/internal/RateLimit.js +174 -27
- package/dist/internal/RateLimit.js.map +1 -1
- package/dist/internal/Store.d.ts +6 -0
- package/dist/internal/Store.d.ts.map +1 -1
- package/dist/internal/Store.js +4 -5
- package/dist/internal/Store.js.map +1 -1
- package/dist/internal/Timing.d.ts +6 -0
- package/dist/internal/Timing.d.ts.map +1 -1
- package/dist/internal/Timing.js +21 -1
- package/dist/internal/Timing.js.map +1 -1
- package/dist/internal/Tron.d.ts +124 -0
- package/dist/internal/Tron.d.ts.map +1 -0
- package/dist/internal/Tron.js +202 -0
- package/dist/internal/Tron.js.map +1 -0
- package/dist/internal/Viem.d.ts +4 -0
- package/dist/internal/Viem.d.ts.map +1 -1
- package/dist/internal/Viem.js +25 -6
- package/dist/internal/Viem.js.map +1 -1
- package/dist/internal/rewards/Read.d.ts +2 -2
- package/dist/internal/rewards/Read.d.ts.map +1 -1
- package/dist/internal/rewards/Read.js +11 -2
- package/dist/internal/rewards/Read.js.map +1 -1
- package/dist/internal/routes/Action.d.ts +27 -0
- package/dist/internal/routes/Action.d.ts.map +1 -1
- package/dist/internal/routes/Action.js +49 -1
- package/dist/internal/routes/Action.js.map +1 -1
- package/dist/internal/routes/Chain.d.ts +2 -0
- package/dist/internal/routes/Chain.d.ts.map +1 -1
- package/dist/internal/routes/Chain.js +4 -0
- package/dist/internal/routes/Chain.js.map +1 -1
- package/dist/internal/routes/DepositAddress.d.ts.map +1 -1
- package/dist/internal/routes/DepositAddress.js +5 -0
- package/dist/internal/routes/DepositAddress.js.map +1 -1
- package/dist/internal/routes/Evidence.d.ts.map +1 -1
- package/dist/internal/routes/Evidence.js +6 -2
- package/dist/internal/routes/Evidence.js.map +1 -1
- package/dist/internal/routes/EvmTransferSettlement.d.ts +17 -0
- package/dist/internal/routes/EvmTransferSettlement.d.ts.map +1 -0
- package/dist/internal/routes/EvmTransferSettlement.js +196 -0
- package/dist/internal/routes/EvmTransferSettlement.js.map +1 -0
- package/dist/internal/routes/Policy.d.ts +161 -0
- package/dist/internal/routes/Policy.d.ts.map +1 -0
- package/dist/internal/routes/Policy.js +249 -0
- package/dist/internal/routes/Policy.js.map +1 -0
- package/dist/internal/routes/Provider.d.ts +66 -3
- package/dist/internal/routes/Provider.d.ts.map +1 -1
- package/dist/internal/routes/Provider.js +106 -27
- package/dist/internal/routes/Provider.js.map +1 -1
- package/dist/internal/routes/Quota.d.ts +57 -0
- package/dist/internal/routes/Quota.d.ts.map +1 -0
- package/dist/internal/routes/Quota.js +144 -0
- package/dist/internal/routes/Quota.js.map +1 -0
- package/dist/internal/routes/Reconciliation.d.ts.map +1 -1
- package/dist/internal/routes/Reconciliation.js +163 -47
- package/dist/internal/routes/Reconciliation.js.map +1 -1
- package/dist/internal/routes/SolanaTransferSettlement.d.ts +12 -0
- package/dist/internal/routes/SolanaTransferSettlement.d.ts.map +1 -0
- package/dist/internal/routes/SolanaTransferSettlement.js +220 -0
- package/dist/internal/routes/SolanaTransferSettlement.js.map +1 -0
- package/dist/internal/routes/SolanaTransferSubsidy.d.ts +172 -0
- package/dist/internal/routes/SolanaTransferSubsidy.d.ts.map +1 -0
- package/dist/internal/routes/SolanaTransferSubsidy.js +564 -0
- package/dist/internal/routes/SolanaTransferSubsidy.js.map +1 -0
- package/dist/internal/routes/Subsidy.d.ts +2 -2
- package/dist/internal/routes/Subsidy.d.ts.map +1 -1
- package/dist/internal/routes/Subsidy.js +2 -2
- package/dist/internal/routes/Subsidy.js.map +1 -1
- package/dist/internal/routes/Transfer.d.ts +229 -1
- package/dist/internal/routes/Transfer.d.ts.map +1 -1
- package/dist/internal/routes/Transfer.js +644 -7
- package/dist/internal/routes/Transfer.js.map +1 -1
- package/dist/internal/routes/TransferReconciliation.d.ts +6 -0
- package/dist/internal/routes/TransferReconciliation.d.ts.map +1 -1
- package/dist/internal/routes/TransferReconciliation.js +83 -159
- package/dist/internal/routes/TransferReconciliation.js.map +1 -1
- package/dist/internal/routes/TransferSettlement.d.ts +45 -0
- package/dist/internal/routes/TransferSettlement.d.ts.map +1 -0
- package/dist/internal/routes/TransferSettlement.js +2 -0
- package/dist/internal/routes/TransferSettlement.js.map +1 -0
- package/dist/internal/routes/TransferSubsidy.d.ts +23 -8
- package/dist/internal/routes/TransferSubsidy.d.ts.map +1 -1
- package/dist/internal/routes/TransferSubsidy.js +64 -5
- package/dist/internal/routes/TransferSubsidy.js.map +1 -1
- package/dist/internal/routes/TronTransferSettlement.d.ts +12 -0
- package/dist/internal/routes/TronTransferSettlement.d.ts.map +1 -0
- package/dist/internal/routes/TronTransferSettlement.js +201 -0
- package/dist/internal/routes/TronTransferSettlement.js.map +1 -0
- package/dist/internal/routes/TronTransferSubsidy.d.ts +174 -0
- package/dist/internal/routes/TronTransferSubsidy.d.ts.map +1 -0
- package/dist/internal/routes/TronTransferSubsidy.js +439 -0
- package/dist/internal/routes/TronTransferSubsidy.js.map +1 -0
- package/dist/internal/routes/index.d.ts +3 -0
- package/dist/internal/routes/index.d.ts.map +1 -1
- package/dist/internal/routes/index.js +3 -0
- package/dist/internal/routes/index.js.map +1 -1
- package/dist/internal/routes/providers/across.d.ts +3 -0
- package/dist/internal/routes/providers/across.d.ts.map +1 -1
- package/dist/internal/routes/providers/across.js +11 -0
- package/dist/internal/routes/providers/across.js.map +1 -1
- package/dist/internal/routes/providers/legacyMesh.d.ts +29 -0
- package/dist/internal/routes/providers/legacyMesh.d.ts.map +1 -0
- package/dist/internal/routes/providers/legacyMesh.js +474 -0
- package/dist/internal/routes/providers/legacyMesh.js.map +1 -0
- package/dist/internal/routes/providers/legacyMeshDelivery.d.ts +59 -0
- package/dist/internal/routes/providers/legacyMeshDelivery.d.ts.map +1 -0
- package/dist/internal/routes/providers/legacyMeshDelivery.js +229 -0
- package/dist/internal/routes/providers/legacyMeshDelivery.js.map +1 -0
- package/dist/internal/routes/providers/legacyMeshOutbound.d.ts +19 -0
- package/dist/internal/routes/providers/legacyMeshOutbound.d.ts.map +1 -0
- package/dist/internal/routes/providers/legacyMeshOutbound.js +457 -0
- package/dist/internal/routes/providers/legacyMeshOutbound.js.map +1 -0
- package/dist/internal/routes/providers/legacyMeshOutboundDelivery.d.ts +123 -0
- package/dist/internal/routes/providers/legacyMeshOutboundDelivery.d.ts.map +1 -0
- package/dist/internal/routes/providers/legacyMeshOutboundDelivery.js +333 -0
- package/dist/internal/routes/providers/legacyMeshOutboundDelivery.js.map +1 -0
- package/dist/internal/routes/providers/propAmm.d.ts +70 -0
- package/dist/internal/routes/providers/propAmm.d.ts.map +1 -0
- package/dist/internal/routes/providers/propAmm.js +524 -0
- package/dist/internal/routes/providers/propAmm.js.map +1 -0
- package/dist/internal/routes/providers/relay.d.ts +4 -1
- package/dist/internal/routes/providers/relay.d.ts.map +1 -1
- package/dist/internal/routes/providers/relay.js +353 -42
- package/dist/internal/routes/providers/relay.js.map +1 -1
- package/dist/internal/routes/providers/rhino.d.ts +3 -0
- package/dist/internal/routes/providers/rhino.d.ts.map +1 -1
- package/dist/internal/routes/providers/rhino.js +11 -0
- package/dist/internal/routes/providers/rhino.js.map +1 -1
- package/dist/internal/routes/providers/squid.d.ts +3 -0
- package/dist/internal/routes/providers/squid.d.ts.map +1 -1
- package/dist/internal/routes/providers/squid.js +19 -0
- package/dist/internal/routes/providers/squid.js.map +1 -1
- package/dist/internal/routes/providers/stargate.d.ts.map +1 -1
- package/dist/internal/routes/providers/stargate.js +22 -9
- package/dist/internal/routes/providers/stargate.js.map +1 -1
- package/dist/internal/routes/providers/symbiosis.d.ts.map +1 -1
- package/dist/internal/routes/providers/symbiosis.js +13 -17
- package/dist/internal/routes/providers/symbiosis.js.map +1 -1
- package/dist/providers/index.d.ts +14 -0
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +6 -0
- package/dist/providers/index.js.map +1 -1
- package/package.json +16 -6
- package/src/ApiKey.ts +7 -3
- package/src/ApiKeys.test.ts +652 -8
- package/src/ApiKeys.ts +191 -11
- package/src/App.test.ts +405 -18
- package/src/App.ts +51 -15
- package/src/Client.test-d.ts +157 -10
- package/src/Client.test.ts +4 -4
- package/src/Scope.ts +1 -2
- package/src/admin/App.ts +11 -5
- package/src/admin/apps/api-keys.test.ts +98 -2
- package/src/admin/apps/api-keys.ts +21 -12
- package/src/admin/apps/earn-vaults.test.ts +76 -7
- package/src/admin/apps/earn-vaults.ts +47 -8
- package/src/admin/apps/organizations.test.ts +147 -31
- package/src/admin/apps/organizations.ts +5 -4
- package/src/admin/apps/route-policies.test.ts +220 -0
- package/src/admin/apps/route-policies.ts +322 -0
- package/src/admin/apps/routes-support.test.ts +107 -2
- package/src/admin/apps/routes-support.ts +43 -3
- package/src/admin/apps/routes.test.ts +150 -0
- package/src/admin/apps/routes.ts +97 -1
- package/src/admin/apps/scopes.test.ts +1 -1
- package/src/admin/apps/verified-tokens.test.ts +32 -0
- package/src/admin/apps/verified-tokens.ts +30 -4
- package/src/admin/index.ts +0 -1
- package/src/admin/ui/plugin.ts +2 -1
- package/src/admin/ui/src/components/Dialog.tsx +1 -1
- package/src/admin/ui/src/components/EarnVaultForm.tsx +45 -0
- package/src/admin/ui/src/components/RoutePicker.tsx +79 -0
- package/src/admin/ui/src/lib/earn-vaults.ts +1 -1
- package/src/admin/ui/src/lib/route-policies.test.ts +452 -0
- package/src/admin/ui/src/lib/route-policies.ts +232 -0
- package/src/admin/ui/src/lib/routes.ts +34 -0
- package/src/admin/ui/src/lib/verified-tokens.ts +24 -9
- package/src/admin/ui/src/routeTree.gen.ts +21 -0
- package/src/admin/ui/src/routes/__root.tsx +1 -1
- package/src/admin/ui/src/routes/api-keys.tsx +21 -2
- package/src/admin/ui/src/routes/early-access.tsx +1 -0
- package/src/admin/ui/src/routes/earn.vaults.tsx +1 -0
- package/src/admin/ui/src/routes/earn.vaults_.$vaultId.edit.tsx +22 -0
- package/src/admin/ui/src/routes/earn.vaults_.new.tsx +10 -0
- package/src/admin/ui/src/routes/organizations.tsx +35 -4
- package/src/admin/ui/src/routes/organizations_.$orgId.tsx +18 -3
- package/src/admin/ui/src/routes/route-policies.tsx +678 -0
- package/src/admin/ui/src/routes/routes.tsx +85 -2
- package/src/admin/ui/src/routes/routes_.support.tsx +6 -22
- package/src/admin/ui/src/routes/subsidies.tsx +1 -0
- package/src/admin/ui/src/routes/verified-tokens.tsx +65 -20
- package/src/admin/ui.generated.ts +99 -104
- package/src/analytics/Analytics.test.ts +4 -50
- package/src/analytics/Analytics.ts +65 -1
- package/src/analytics/tables/requestEvents.test.ts +402 -1
- package/src/analytics/tables/requestEvents.ts +62 -93
- package/src/apps/data/App.test.ts +26 -0
- package/src/apps/data/routes/activities.test.ts +315 -14
- package/src/apps/data/routes/activities.ts +60 -0
- package/src/apps/data/routes/balances.ts +5 -7
- package/src/apps/data/routes/earn.bench.ts +18 -0
- package/src/apps/data/routes/earn.local.test.ts +507 -0
- package/src/apps/data/routes/earn.test.ts +405 -127
- package/src/apps/data/routes/earn.ts +660 -270
- package/src/apps/data/routes/exchanges.bench.ts +3 -1
- package/src/apps/data/routes/exchanges.test.ts +166 -2
- package/src/apps/data/routes/exchanges.ts +308 -101
- package/src/apps/data/routes/fee-amm.test.ts +153 -2
- package/src/apps/data/routes/fee-amm.ts +132 -62
- package/src/apps/data/routes/receipts.test.ts +21 -18
- package/src/apps/data/routes/rpc.test.ts +34 -2
- package/src/apps/data/routes/rpc.ts +47 -10
- package/src/apps/data/routes/tokens.provenance.test.ts +157 -0
- package/src/apps/data/routes/tokens.test.ts +4 -4
- package/src/apps/data/routes/tokens.ts +11 -6
- package/src/apps/data/routes/transactions.test.ts +29 -0
- package/src/apps/data/routes/transactions.ts +33 -22
- package/src/apps/data/routes/webhooks.scan.test.ts +29 -0
- package/src/apps/data/routes/webhooks.test.ts +18 -3
- package/src/apps/data/routes/webhooks.ts +27 -13
- package/src/apps/management/App.ts +4 -0
- package/src/apps/management/Billing.test.ts +253 -27
- package/src/apps/management/Billing.ts +83 -29
- package/src/apps/management/routes/api-keys.test.ts +1 -1
- package/src/apps/management/routes/api-keys.ts +85 -11
- package/src/apps/management/routes/billing.test.ts +68 -4
- package/src/apps/management/routes/cli.test.ts +370 -0
- package/src/apps/management/routes/cli.ts +533 -0
- package/src/apps/management/routes/me.test.ts +14 -4
- package/src/apps/management/routes/orgs.test.ts +108 -4
- package/src/apps/management/routes/orgs.ts +7 -2
- package/src/apps/management/routes/usage.test.ts +2 -2
- package/src/apps/management/routes/verified-token-requests.test.ts +16 -2
- package/src/apps/management/routes/verified-token-requests.ts +7 -10
- package/src/apps/mcp.test.ts +108 -25
- package/src/apps/mcp.ts +15 -3
- package/src/apps/mpp/internal/Policy.test.ts +51 -4
- package/src/apps/mpp/internal/Policy.ts +6 -2
- package/src/apps/mpp/internal/Sponsorship.test.ts +29 -0
- package/src/apps/mpp/internal/Sponsorship.ts +6 -4
- package/src/apps/relay/App.test.ts +85 -1
- package/src/apps/relay/App.ts +28 -4
- package/src/apps/relay/Sponsorships.test.ts +97 -1
- package/src/apps/relay/Sponsorships.ts +77 -66
- package/src/apps/routes/Access.test.ts +319 -0
- package/src/apps/routes/Access.ts +147 -0
- package/src/apps/routes/App.ts +10 -0
- package/src/apps/routes/routes/chains.test.ts +3 -2
- package/src/apps/routes/routes/deposit-addresses.test.ts +978 -34
- package/src/apps/routes/routes/deposit-addresses.ts +243 -32
- package/src/apps/routes/routes/propAmm.local.test.ts +951 -0
- package/src/apps/routes/routes/quotes.test.ts +81 -8
- package/src/apps/routes/routes/quotes.ts +109 -27
- package/src/apps/routes/routes/transfers.test.ts +252 -15
- package/src/apps/routes/routes/transfers.ts +212 -33
- package/src/cli/ApiKeys.test.ts +265 -0
- package/src/cli/ApiKeys.ts +189 -0
- package/src/cli/Cli.test.ts +188 -0
- package/src/cli/Cli.ts +280 -12
- package/src/cli/Credentials.test.ts +122 -0
- package/src/cli/Credentials.ts +144 -0
- package/src/cli/Spec.test.ts +133 -0
- package/src/cli/Spec.ts +132 -0
- package/src/cli/bin.test.ts +49 -0
- package/src/cli/bin.ts +5 -1
- package/src/cli/commands/auth.test.ts +138 -0
- package/src/cli/commands/auth.ts +254 -0
- package/src/cli/commands/keys.test.ts +68 -0
- package/src/cli/commands/keys.ts +125 -0
- package/src/cloudflare.ts +184 -36
- package/src/db/Db.test.ts +877 -0
- package/src/db/Db.ts +507 -1
- package/src/db/Schema.ts +75 -4
- package/src/db/tables/apiKeyAdmissions.test.ts +57 -0
- package/src/db/tables/apiKeyAdmissions.ts +101 -11
- package/src/db/tables/apiKeyOwnerTombstones.ts +2 -2
- package/src/db/tables/authAccounts.test.ts +30 -0
- package/src/db/tables/authAccounts.ts +1 -0
- package/src/db/tables/earnVaults.test.ts +34 -0
- package/src/db/tables/earnVaults.ts +17 -2
- package/src/db/tables/organizations.test.ts +148 -84
- package/src/db/tables/organizations.ts +11 -43
- package/src/db/tables/requestUsage.ts +13 -2
- package/src/db/tables/rewardCampaigns.test.ts +163 -0
- package/src/db/tables/rewardEligibilityAssociations.test.ts +14 -0
- package/src/db/tables/rewardEligibilityAssociations.ts +24 -0
- package/src/db/tables/rewardRuns.ts +4 -0
- package/src/db/tables/rewardTransactionAttempts.ts +2 -0
- package/src/db/tables/routesDepositAddresses.test.ts +263 -0
- package/src/db/tables/routesDepositAddresses.ts +118 -34
- package/src/db/tables/routesDeposits.test.ts +133 -0
- package/src/db/tables/routesDeposits.ts +47 -26
- package/src/db/tables/routesIdempotency.test.ts +80 -0
- package/src/db/tables/routesIdempotency.ts +2 -0
- package/src/db/tables/routesPolicies.test.ts +143 -0
- package/src/db/tables/routesPolicies.ts +251 -0
- package/src/db/tables/routesTransferSubsidies.ts +64 -0
- package/src/db/tables/routesTransferSubsidyNonces.ts +16 -4
- package/src/db/tables/routesTransfers.test.ts +183 -2
- package/src/db/tables/routesTransfers.ts +65 -6
- package/src/db/tables/sponsoredTransactions.test.ts +351 -76
- package/src/db/tables/sponsoredTransactions.ts +182 -38
- package/src/db/tables/users.test.ts +51 -0
- package/src/db/tables/users.ts +12 -0
- package/src/db/tables/verifiedTokenRequests.test.ts +39 -20
- package/src/db/tables/verifiedTokenRequests.ts +80 -44
- package/src/handlers/internal/multisig.store.test.ts +120 -0
- package/src/handlers/internal/multisig.test.ts +389 -0
- package/src/handlers/internal/multisig.ts +13 -10
- package/src/handlers/relay.test.ts +259 -83
- package/src/handlers/relay.ts +36 -17
- package/src/internal/Auth.test.ts +383 -8
- package/src/internal/Auth.ts +272 -99
- package/src/internal/BetterAuth.test.ts +58 -22
- package/src/internal/BetterAuth.ts +23 -17
- package/src/internal/Cache.ts +3 -1
- package/src/internal/CliAuth.test.ts +279 -0
- package/src/internal/CliAuth.ts +319 -0
- package/src/internal/EarnSharePrices.test.ts +25 -0
- package/src/internal/EarnSharePrices.ts +9 -2
- package/src/internal/EarnVaults.test.ts +67 -0
- package/src/internal/EarnVaults.ts +299 -4
- package/src/internal/EarnWithdrawals.test.ts +315 -0
- package/src/internal/EarnWithdrawals.ts +266 -0
- package/src/internal/ExecutionContext.ts +31 -0
- package/src/internal/Log.test.ts +210 -0
- package/src/internal/Log.ts +180 -142
- package/src/internal/MetricSink.test.ts +190 -15
- package/src/internal/MetricSink.ts +27 -1
- package/src/internal/RateLimit.test.ts +144 -8
- package/src/internal/RateLimit.ts +254 -26
- package/src/internal/Store.ts +4 -5
- package/src/internal/Timing.test.ts +32 -0
- package/src/internal/Timing.ts +20 -1
- package/src/internal/Tron.test-d.ts +22 -0
- package/src/internal/Tron.test.ts +94 -0
- package/src/internal/Tron.ts +289 -0
- package/src/internal/Viem.rpc.test.ts +76 -0
- package/src/internal/Viem.ts +29 -6
- package/src/internal/rewards/Read.test.ts +109 -0
- package/src/internal/rewards/Read.ts +13 -4
- package/src/internal/routes/Action.test.ts +70 -0
- package/src/internal/routes/Action.ts +76 -1
- package/src/internal/routes/Chain.test.ts +18 -0
- package/src/internal/routes/Chain.ts +5 -0
- package/src/internal/routes/DepositAddress.ts +5 -0
- package/src/internal/routes/Evidence.test.ts +64 -0
- package/src/internal/routes/Evidence.ts +11 -3
- package/src/internal/routes/EvmTransferSettlement.ts +213 -0
- package/src/internal/routes/Policy.test.ts +316 -0
- package/src/internal/routes/Policy.ts +399 -0
- package/src/internal/routes/Provider.test.ts +103 -2
- package/src/internal/routes/Provider.ts +149 -27
- package/src/internal/routes/Quota.test.ts +149 -0
- package/src/internal/routes/Quota.ts +226 -0
- package/src/internal/routes/Reconciliation.local.test.ts +285 -0
- package/src/internal/routes/Reconciliation.test.ts +371 -13
- package/src/internal/routes/Reconciliation.ts +193 -42
- package/src/internal/routes/SolanaTransferSettlement.ts +232 -0
- package/src/internal/routes/SolanaTransferSubsidy.test.ts +471 -0
- package/src/internal/routes/SolanaTransferSubsidy.ts +760 -0
- package/src/internal/routes/SourceObservation.test.ts +221 -0
- package/src/internal/routes/Subsidy.local.test.ts +46 -0
- package/src/internal/routes/Subsidy.ts +4 -2
- package/src/internal/routes/Transfer.solana.test.ts +389 -0
- package/src/internal/routes/Transfer.test.ts +130 -1
- package/src/internal/routes/Transfer.tron.test.ts +172 -0
- package/src/internal/routes/Transfer.ts +866 -8
- package/src/internal/routes/TransferReconciliation.test.ts +26 -4
- package/src/internal/routes/TransferReconciliation.ts +91 -166
- package/src/internal/routes/TransferSettlement.ts +48 -0
- package/src/internal/routes/TransferSubsidy.local.test.ts +143 -0
- package/src/internal/routes/TransferSubsidy.test.ts +34 -4
- package/src/internal/routes/TransferSubsidy.ts +87 -12
- package/src/internal/routes/TronTransferSettlement.ts +212 -0
- package/src/internal/routes/TronTransferSubsidy.test.ts +109 -0
- package/src/internal/routes/TronTransferSubsidy.ts +619 -0
- package/src/internal/routes/index.test.ts +3 -0
- package/src/internal/routes/index.ts +3 -0
- package/src/internal/routes/providers/across.ts +13 -0
- package/src/internal/routes/providers/index.test.ts +141 -0
- package/src/internal/routes/providers/legacyMesh.test.ts +484 -0
- package/src/internal/routes/providers/legacyMesh.ts +633 -0
- package/src/internal/routes/providers/legacyMeshDelivery.test.ts +306 -0
- package/src/internal/routes/providers/legacyMeshDelivery.ts +320 -0
- package/src/internal/routes/providers/legacyMeshOutbound.test.ts +401 -0
- package/src/internal/routes/providers/legacyMeshOutbound.ts +566 -0
- package/src/internal/routes/providers/legacyMeshOutboundDelivery.ts +473 -0
- package/src/internal/routes/providers/propAmm.ts +640 -0
- package/src/internal/routes/providers/relay.test.ts +1299 -18
- package/src/internal/routes/providers/relay.ts +424 -39
- package/src/internal/routes/providers/rhino.ts +14 -1
- package/src/internal/routes/providers/squid.ts +21 -0
- package/src/internal/routes/providers/stargate.test.ts +105 -1
- package/src/internal/routes/providers/stargate.ts +22 -9
- package/src/internal/routes/providers/symbiosis.ts +13 -47
- package/src/providers/index.test.ts +2 -0
- package/src/providers/index.ts +18 -0
- package/dist/admin/Access.d.ts +0 -39
- package/dist/admin/Access.d.ts.map +0 -1
- package/dist/admin/Access.js +0 -73
- package/dist/admin/Access.js.map +0 -1
- package/src/admin/Access.test.ts +0 -130
- package/src/admin/Access.ts +0 -85
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import * as TestApp from '../../test/App.js'
|
|
2
|
+
import * as TestCli from '../../test/cli.js'
|
|
3
|
+
import * as core_ApiKeys from '../ApiKeys.js'
|
|
4
|
+
import * as Client from '../Client.js'
|
|
5
|
+
import * as Memberships from '../db/tables/memberships.js'
|
|
6
|
+
import * as CliAuth from '../internal/CliAuth.js'
|
|
7
|
+
import * as Store from '../internal/Store.js'
|
|
8
|
+
import * as ApiKeys from './ApiKeys.js'
|
|
9
|
+
|
|
10
|
+
describe('resolve', () => {
|
|
11
|
+
test('infers a project key, caches it privately, and expires it with the session', async () => {
|
|
12
|
+
const context = await TestCli.create()
|
|
13
|
+
const { cookie } = await TestCli.login(context)
|
|
14
|
+
const owner = await organization(context.app, cookie)
|
|
15
|
+
const request = auth(context)
|
|
16
|
+
const [credential, concurrent, separate] = await Promise.all([
|
|
17
|
+
ApiKeys.resolve(request, { scopes: ['routes:read'] }),
|
|
18
|
+
ApiKeys.resolve(auth(context), { scopes: ['routes:read'] }),
|
|
19
|
+
ApiKeys.resolve(request, { scopes: ['routes:read'] }),
|
|
20
|
+
])
|
|
21
|
+
expect(typeof credential).toBe('string')
|
|
22
|
+
expect(concurrent).toBe(credential)
|
|
23
|
+
expect(separate).toBe(credential)
|
|
24
|
+
expect(await ApiKeys.resolve(auth(context), { scopes: ['routes:read'] })).toBe(credential)
|
|
25
|
+
const session = (await context.credentials.get(TestCli.origin))!
|
|
26
|
+
expect(session.apiKeys).toHaveLength(1)
|
|
27
|
+
expect(session.apiKeys![0]).toMatchObject({
|
|
28
|
+
...owner,
|
|
29
|
+
environment: 'production',
|
|
30
|
+
expiresAt: session.expiresAt,
|
|
31
|
+
scopes: ['routes:read'],
|
|
32
|
+
})
|
|
33
|
+
const listed = await context.app.request(
|
|
34
|
+
`/v1/orgs/${owner.orgId}/projects/${owner.projectId}/api-keys?include=`,
|
|
35
|
+
{ headers: { cookie } },
|
|
36
|
+
)
|
|
37
|
+
const { data } = await listed.json()
|
|
38
|
+
expect(data[0].expiresAt).toBe(session.expiresAt)
|
|
39
|
+
const { code, lines } = await TestCli.serve(context.cli, [
|
|
40
|
+
'v1',
|
|
41
|
+
'routes',
|
|
42
|
+
'transfers',
|
|
43
|
+
'get',
|
|
44
|
+
'--format',
|
|
45
|
+
'json',
|
|
46
|
+
])
|
|
47
|
+
expect(code).toBe(0)
|
|
48
|
+
expect(JSON.parse(lines.join('\n'))).toMatchObject({ data: [] })
|
|
49
|
+
await context.credentials.set(TestCli.origin, {
|
|
50
|
+
...session,
|
|
51
|
+
apiKeys: session.apiKeys!.map((key) => ({ ...key, expiresAt: new Date(0).toISOString() })),
|
|
52
|
+
})
|
|
53
|
+
expect(await ApiKeys.resolve(auth(context), { scopes: ['routes:read'] })).not.toBe(credential)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
test('requires fresh consent for legacy local and server sessions', async () => {
|
|
57
|
+
const store = Store.memory()
|
|
58
|
+
const context = await TestCli.create({ app: TestApp.create({ kv: { store } }) })
|
|
59
|
+
const { cookie } = await TestCli.login(context)
|
|
60
|
+
const owner = await organization(context.app, cookie)
|
|
61
|
+
const current = (await context.credentials.get(TestCli.origin))!
|
|
62
|
+
const { scopes: _scopes, ...legacy } = current
|
|
63
|
+
await context.credentials.set(TestCli.origin, legacy)
|
|
64
|
+
const denied = await ApiKeys.resolve(auth(context), { scopes: ['data:read'] })
|
|
65
|
+
expect(denied instanceof Response && (await denied.json())).toMatchObject({
|
|
66
|
+
error: { code: 'scope_not_approved' },
|
|
67
|
+
})
|
|
68
|
+
const { token } = await CliAuth.mint(store, { client: {}, userId: current.userId })
|
|
69
|
+
expect(
|
|
70
|
+
(
|
|
71
|
+
await context.app.request('/v1/me', {
|
|
72
|
+
headers: { authorization: `Bearer ${token}` },
|
|
73
|
+
})
|
|
74
|
+
).status,
|
|
75
|
+
).toBe(200)
|
|
76
|
+
const response = await context.app.request(
|
|
77
|
+
`/v1/orgs/${owner.orgId}/projects/${owner.projectId}/api-keys`,
|
|
78
|
+
{
|
|
79
|
+
body: JSON.stringify({ scopes: ['data:read'] }),
|
|
80
|
+
headers: { authorization: `Bearer ${token}`, 'content-type': 'application/json' },
|
|
81
|
+
method: 'POST',
|
|
82
|
+
},
|
|
83
|
+
)
|
|
84
|
+
expect(response.status).toBe(403)
|
|
85
|
+
expect(await response.json()).toMatchObject({ error: { code: 'scope_not_issuable' } })
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
test('requires owner membership to derive or rotate Routes write keys', async () => {
|
|
89
|
+
const db = TestApp.database()
|
|
90
|
+
const store = Store.memory()
|
|
91
|
+
const context = await TestCli.create({ app: TestApp.create({ db, kv: { store } }) })
|
|
92
|
+
const ownerCookie = await TestCli.consoleSession(context.app)
|
|
93
|
+
const owner = await organization(context.app, ownerCookie)
|
|
94
|
+
await TestCli.login(context)
|
|
95
|
+
const session = (await context.credentials.get(TestCli.origin))!
|
|
96
|
+
await Memberships.create(db, { orgId: owner.orgId, role: 'admin', userId: session.userId })
|
|
97
|
+
const { record } = await core_ApiKeys.mintBounded(db, store, {
|
|
98
|
+
...owner,
|
|
99
|
+
environment: 'production',
|
|
100
|
+
scopes: ['routes:read', 'routes:write'],
|
|
101
|
+
})
|
|
102
|
+
const denied = await ApiKeys.resolve(auth(context), {
|
|
103
|
+
...owner,
|
|
104
|
+
scopes: ['routes:write'],
|
|
105
|
+
sourceKeyId: record.id,
|
|
106
|
+
})
|
|
107
|
+
expect(denied instanceof Response && denied.status).toBe(403)
|
|
108
|
+
expect(denied instanceof Response && (await denied.json())).toMatchObject({
|
|
109
|
+
error: { code: 'scope_not_issuable' },
|
|
110
|
+
})
|
|
111
|
+
const rotated = await context.app.request(
|
|
112
|
+
`/v1/orgs/${owner.orgId}/projects/${owner.projectId}/api-keys/${record.id}/rotate`,
|
|
113
|
+
{ headers: { authorization: `Bearer ${session.token}` }, method: 'POST' },
|
|
114
|
+
)
|
|
115
|
+
expect(rotated.status).toBe(403)
|
|
116
|
+
expect(await rotated.json()).toMatchObject({ error: { code: 'scope_not_issuable' } })
|
|
117
|
+
expect((await context.credentials.get(TestCli.origin))?.apiKeys).toBeUndefined()
|
|
118
|
+
expect(await core_ApiKeys.get(store, record.id)).not.toBeNull()
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
test('rejects operations outside approved scopes without minting a key', async () => {
|
|
122
|
+
const context = await TestCli.create()
|
|
123
|
+
const { cookie } = await TestCli.login(context, { scopes: [] })
|
|
124
|
+
const result = await ApiKeys.resolve(auth(context), { scopes: ['routes:write'] })
|
|
125
|
+
expect(result instanceof Response && (await result.json())).toMatchObject({
|
|
126
|
+
error: { code: 'scope_not_approved' },
|
|
127
|
+
})
|
|
128
|
+
expect((await context.credentials.get(TestCli.origin))?.apiKeys).toBeUndefined()
|
|
129
|
+
const owner = await organization(context.app, cookie)
|
|
130
|
+
const session = (await context.credentials.get(TestCli.origin))!
|
|
131
|
+
const denied = await context.app.request(
|
|
132
|
+
`/v1/orgs/${owner.orgId}/projects/${owner.projectId}/api-keys`,
|
|
133
|
+
{
|
|
134
|
+
body: JSON.stringify({ scopes: ['data:read'] }),
|
|
135
|
+
headers: { authorization: `Bearer ${session.token}`, 'content-type': 'application/json' },
|
|
136
|
+
method: 'POST',
|
|
137
|
+
},
|
|
138
|
+
)
|
|
139
|
+
expect(denied.status).toBe(403)
|
|
140
|
+
expect(await denied.json()).toMatchObject({ error: { code: 'scope_not_issuable' } })
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
test('requires selection when organization ownership is ambiguous', async () => {
|
|
144
|
+
const context = await TestCli.create()
|
|
145
|
+
const { cookie } = await TestCli.login(context)
|
|
146
|
+
const owner = await organization(context.app, cookie)
|
|
147
|
+
await organization(context.app, cookie)
|
|
148
|
+
const result = await ApiKeys.resolve(auth(context), { scopes: ['routes:read'] })
|
|
149
|
+
expect(result instanceof Response && (await result.json())).toMatchObject({
|
|
150
|
+
error: { code: 'organization_required' },
|
|
151
|
+
})
|
|
152
|
+
expect((await context.credentials.get(TestCli.origin))?.apiKeys).toBeUndefined()
|
|
153
|
+
await context.app.request(`/v1/orgs/${owner.orgId}/projects`, {
|
|
154
|
+
body: JSON.stringify({ name: 'Another project' }),
|
|
155
|
+
headers: { 'content-type': 'application/json', cookie },
|
|
156
|
+
method: 'POST',
|
|
157
|
+
})
|
|
158
|
+
const ambiguous = await ApiKeys.resolve(auth(context), {
|
|
159
|
+
orgId: owner.orgId,
|
|
160
|
+
scopes: ['routes:read'],
|
|
161
|
+
})
|
|
162
|
+
expect(ambiguous instanceof Response && (await ambiguous.json())).toMatchObject({
|
|
163
|
+
error: { code: 'project_required' },
|
|
164
|
+
})
|
|
165
|
+
expect(
|
|
166
|
+
typeof (await ApiKeys.resolve(auth(context), { ...owner, scopes: ['routes:read'] })),
|
|
167
|
+
).toBe('string')
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
test.each([{ scopes: ['routes:read', 'routes:write'] }, { scopes: ['*'] }])(
|
|
171
|
+
'derives Routes writes from approved scopes $scopes without rotating the source',
|
|
172
|
+
async ({ scopes }) => {
|
|
173
|
+
const db = TestApp.database()
|
|
174
|
+
const store = Store.memory()
|
|
175
|
+
const context = await TestCli.create({ app: TestApp.create({ db, kv: { store } }) })
|
|
176
|
+
const { cookie } = await TestCli.login(context)
|
|
177
|
+
const owner = await organization(context.app, cookie)
|
|
178
|
+
const denied = await ApiKeys.resolve(auth(context), { scopes: ['routes:write'] })
|
|
179
|
+
expect(denied instanceof Response && (await denied.json())).toMatchObject({
|
|
180
|
+
error: { code: 'api_key_required' },
|
|
181
|
+
})
|
|
182
|
+
const { record } = await core_ApiKeys.mintBounded(db, store, {
|
|
183
|
+
...owner,
|
|
184
|
+
allowedIps: ['203.0.113.7'],
|
|
185
|
+
environment: 'production',
|
|
186
|
+
expiresAt: new Date(Date.now() + 60_000).toISOString(),
|
|
187
|
+
scopes,
|
|
188
|
+
})
|
|
189
|
+
expect(typeof (await ApiKeys.resolve(auth(context), { scopes: ['routes:write'] }))).toBe(
|
|
190
|
+
'string',
|
|
191
|
+
)
|
|
192
|
+
const cached = (await context.credentials.get(TestCli.origin))!.apiKeys![0]!
|
|
193
|
+
const derived = await core_ApiKeys.get(store, cached.id)
|
|
194
|
+
expect(derived?.expiresAt).toBe(record.expiresAt)
|
|
195
|
+
expect(derived?.scopes).toEqual(['routes:write', 'routes:read'])
|
|
196
|
+
expect(derived?.allowedIps).toEqual(record.allowedIps)
|
|
197
|
+
expect(await core_ApiKeys.get(store, record.id)).not.toBeNull()
|
|
198
|
+
const first = await ApiKeys.resolve(auth(context), { scopes: ['routes:write'] })
|
|
199
|
+
expect(first).toBe(cached.token)
|
|
200
|
+
const second = await core_ApiKeys.mintBounded(db, store, {
|
|
201
|
+
...owner,
|
|
202
|
+
environment: 'production',
|
|
203
|
+
scopes: ['routes:read', 'routes:write'],
|
|
204
|
+
})
|
|
205
|
+
const ambiguous = await ApiKeys.resolve(auth(context), { scopes: ['routes:write'] })
|
|
206
|
+
expect(ambiguous instanceof Response && (await ambiguous.json())).toMatchObject({
|
|
207
|
+
error: { code: 'api_key_required' },
|
|
208
|
+
})
|
|
209
|
+
expect(
|
|
210
|
+
await ApiKeys.resolve(auth(context), {
|
|
211
|
+
scopes: ['routes:write'],
|
|
212
|
+
sourceKeyId: record.id,
|
|
213
|
+
}),
|
|
214
|
+
).toBe(cached.token)
|
|
215
|
+
expect((await context.credentials.get(TestCli.origin))?.apiKeys).toHaveLength(1)
|
|
216
|
+
expect(
|
|
217
|
+
await ApiKeys.resolve(auth(context), {
|
|
218
|
+
scopes: ['routes:write'],
|
|
219
|
+
sourceKeyId: second.record.id,
|
|
220
|
+
}),
|
|
221
|
+
).not.toBe(cached.token)
|
|
222
|
+
const keys = (await context.credentials.get(TestCli.origin))!.apiKeys!
|
|
223
|
+
expect(keys).toHaveLength(2)
|
|
224
|
+
expect(keys[1]?.sourceKeyId).toBe(second.record.id)
|
|
225
|
+
expect(
|
|
226
|
+
await ApiKeys.revoke(auth(context), (await context.credentials.get(TestCli.origin))!),
|
|
227
|
+
).toBe(true)
|
|
228
|
+
expect(await core_ApiKeys.get(store, cached.id)).toBeNull()
|
|
229
|
+
expect(await core_ApiKeys.get(store, keys[1]!.id)).toBeNull()
|
|
230
|
+
expect(await core_ApiKeys.get(store, record.id)).not.toBeNull()
|
|
231
|
+
},
|
|
232
|
+
)
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
function auth(context: Awaited<ReturnType<typeof TestCli.create>>) {
|
|
236
|
+
return {
|
|
237
|
+
api: Client.create({
|
|
238
|
+
fetch: async (input: Request | string | URL, init?: RequestInit) =>
|
|
239
|
+
context.app.fetch(new Request(input, init)),
|
|
240
|
+
url: TestCli.origin,
|
|
241
|
+
}),
|
|
242
|
+
apiUrl: new URL(TestCli.origin),
|
|
243
|
+
credentials: context.credentials,
|
|
244
|
+
fetch: async (request: Request) => context.app.fetch(request),
|
|
245
|
+
name: 'tapimo',
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
async function organization(app: ReturnType<typeof TestApp.create>, cookie: string) {
|
|
250
|
+
const headers = { 'content-type': 'application/json', cookie }
|
|
251
|
+
const created = await app.request('/v1/orgs', {
|
|
252
|
+
body: JSON.stringify({ name: 'CLI project' }),
|
|
253
|
+
headers,
|
|
254
|
+
method: 'POST',
|
|
255
|
+
})
|
|
256
|
+
const org = await created.json()
|
|
257
|
+
const project = await (
|
|
258
|
+
await app.request(`/v1/orgs/${org.id}/projects`, {
|
|
259
|
+
body: JSON.stringify({ name: 'CLI' }),
|
|
260
|
+
headers,
|
|
261
|
+
method: 'POST',
|
|
262
|
+
})
|
|
263
|
+
).json()
|
|
264
|
+
return { orgId: org.id as string, projectId: project.id as string }
|
|
265
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import * as Scope from '../Scope.js'
|
|
2
|
+
import type * as Auth from './commands/auth.js'
|
|
3
|
+
import type * as Credentials from './Credentials.js'
|
|
4
|
+
|
|
5
|
+
const resolutions = new WeakMap<Auth.Context, Promise<unknown>>()
|
|
6
|
+
|
|
7
|
+
/** Resolves a private, session-bound API key without replacing explicit credentials. */
|
|
8
|
+
export function resolve(context: Auth.Context, options: resolve.Options) {
|
|
9
|
+
const previous = resolutions.get(context) ?? Promise.resolve()
|
|
10
|
+
const pending = previous
|
|
11
|
+
.catch(() => undefined)
|
|
12
|
+
.then(() =>
|
|
13
|
+
context.credentials.withLock
|
|
14
|
+
? context.credentials.withLock((credentials) =>
|
|
15
|
+
resolveKey({ ...context, credentials }, options),
|
|
16
|
+
)
|
|
17
|
+
: resolveKey(context, options),
|
|
18
|
+
)
|
|
19
|
+
resolutions.set(context, pending)
|
|
20
|
+
return pending.finally(() => {
|
|
21
|
+
if (resolutions.get(context) === pending) resolutions.delete(context)
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async function resolveKey(context: Auth.Context, options: resolve.Options) {
|
|
26
|
+
const { api, apiUrl, credentials } = context
|
|
27
|
+
const session = await credentials.get(apiUrl)
|
|
28
|
+
if (!session) return undefined
|
|
29
|
+
const approved = session.scopes
|
|
30
|
+
if (!approved || !options.scopes.every((scope) => approved.includes(scope)))
|
|
31
|
+
return failure(
|
|
32
|
+
'scope_not_approved',
|
|
33
|
+
'Run login again and enable the required scopes under “Modify auth scopes”.',
|
|
34
|
+
)
|
|
35
|
+
const request = { headers: { authorization: `Bearer ${session.token}` } }
|
|
36
|
+
const me = await api.v1.me.$get(undefined, request)
|
|
37
|
+
if (me.status !== 200) return me
|
|
38
|
+
const orgs = await api.v1.orgs.$get(undefined, request)
|
|
39
|
+
if (orgs.status !== 200) return orgs
|
|
40
|
+
const organizations = (await orgs.json()).data
|
|
41
|
+
const orgId = options.orgId ?? (organizations.length === 1 ? organizations[0]!.id : undefined)
|
|
42
|
+
if (!orgId)
|
|
43
|
+
return failure(
|
|
44
|
+
'organization_required',
|
|
45
|
+
`Set TEMPO_API_ORG to one of: ${describe(organizations)}.`,
|
|
46
|
+
)
|
|
47
|
+
const org = api.v1.orgs[':orgId{org_[A-Za-z0-9_-]+}']
|
|
48
|
+
const projects = await org.projects.$get({ param: { orgId } }, request)
|
|
49
|
+
if (projects.status !== 200) return projects
|
|
50
|
+
const choices = (await projects.json()).data
|
|
51
|
+
const projectId = options.projectId ?? (choices.length === 1 ? choices[0]!.id : undefined)
|
|
52
|
+
if (!projectId)
|
|
53
|
+
return failure('project_required', `Set TEMPO_API_PROJECT to one of: ${describe(choices)}.`)
|
|
54
|
+
if (!choices.some(({ id }) => id === projectId))
|
|
55
|
+
return failure(
|
|
56
|
+
'project_not_found',
|
|
57
|
+
'The selected project does not belong to this organization.',
|
|
58
|
+
)
|
|
59
|
+
const environment = options.environment ?? 'production'
|
|
60
|
+
const catalog = await api.v1.scopes.$get({}, request)
|
|
61
|
+
if (catalog.status !== 200) return catalog
|
|
62
|
+
const entries = (await catalog.json()).data
|
|
63
|
+
const selfServe = entries.filter(({ selfServe }) => selfServe).map(({ scope }) => scope)
|
|
64
|
+
const scopes = [...new Set(options.scopes)]
|
|
65
|
+
if (
|
|
66
|
+
scopes.includes('routes:write') &&
|
|
67
|
+
!scopes.includes('routes:read') &&
|
|
68
|
+
approved.includes('routes:read')
|
|
69
|
+
)
|
|
70
|
+
scopes.push('routes:read')
|
|
71
|
+
const project = org.projects[':projectId{prj_[A-Za-z0-9_-]+}']
|
|
72
|
+
const restricted = scopes.filter((scope) => !selfServe.includes(scope))
|
|
73
|
+
const sourceKeyId = await (async () => {
|
|
74
|
+
if (restricted.length === 0 && !options.sourceKeyId) return undefined
|
|
75
|
+
const listed = await project['api-keys'].$get(
|
|
76
|
+
{ param: { orgId, projectId }, query: { environment, include: '' } },
|
|
77
|
+
request,
|
|
78
|
+
)
|
|
79
|
+
if (listed.status !== 200) return listed
|
|
80
|
+
// Locally inferred credentials are destinations, not additional provisioned source choices.
|
|
81
|
+
const inferredIds = new Set(session.apiKeys?.map(({ id }) => id))
|
|
82
|
+
const sources = (await listed.json()).data.filter(
|
|
83
|
+
(key) =>
|
|
84
|
+
!inferredIds.has(key.id) &&
|
|
85
|
+
restricted.every(
|
|
86
|
+
(scope) => key.scopes.includes(scope) || key.scopes.includes(Scope.wildcard),
|
|
87
|
+
),
|
|
88
|
+
)
|
|
89
|
+
const source = options.sourceKeyId
|
|
90
|
+
? sources.find(({ id }) => id === options.sourceKeyId)
|
|
91
|
+
: sources.length === 1
|
|
92
|
+
? sources[0]
|
|
93
|
+
: undefined
|
|
94
|
+
if (!source)
|
|
95
|
+
return failure(
|
|
96
|
+
'api_key_required',
|
|
97
|
+
sources.length === 0
|
|
98
|
+
? 'This project needs a Tempo-approved key with the required scopes. Set TEMPO_API_KEY to an approved key, or ask Tempo to provision this project.'
|
|
99
|
+
: `Set TEMPO_API_SOURCE_KEY to one of: ${sources.map(({ id }) => id).join(', ')}.`,
|
|
100
|
+
)
|
|
101
|
+
return source.id
|
|
102
|
+
})()
|
|
103
|
+
if (sourceKeyId && typeof sourceKeyId !== 'string') return sourceKeyId
|
|
104
|
+
// Resolve the live source grant before reusing a key so caching never hides new ambiguity.
|
|
105
|
+
const cached = session.apiKeys?.find(
|
|
106
|
+
(key) =>
|
|
107
|
+
key.orgId === orgId &&
|
|
108
|
+
key.projectId === projectId &&
|
|
109
|
+
key.environment === environment &&
|
|
110
|
+
(!key.expiresAt || Date.parse(key.expiresAt) > Date.now()) &&
|
|
111
|
+
(!sourceKeyId || key.sourceKeyId === sourceKeyId) &&
|
|
112
|
+
options.scopes.every((scope) => key.scopes.includes(scope)),
|
|
113
|
+
)
|
|
114
|
+
if (cached) return cached.token
|
|
115
|
+
const minted = await project['api-keys'].$post(
|
|
116
|
+
{
|
|
117
|
+
json: { environment, name: 'Tempo CLI', scopes, ...(sourceKeyId ? { sourceKeyId } : {}) },
|
|
118
|
+
param: { orgId, projectId },
|
|
119
|
+
},
|
|
120
|
+
request,
|
|
121
|
+
)
|
|
122
|
+
if (minted.status !== 200) return minted
|
|
123
|
+
const key = await minted.json()
|
|
124
|
+
await credentials.set(apiUrl, {
|
|
125
|
+
...session,
|
|
126
|
+
apiKeys: [
|
|
127
|
+
...(session.apiKeys ?? []),
|
|
128
|
+
{
|
|
129
|
+
environment,
|
|
130
|
+
...(key.expiresAt ? { expiresAt: key.expiresAt } : {}),
|
|
131
|
+
id: key.id,
|
|
132
|
+
orgId,
|
|
133
|
+
projectId,
|
|
134
|
+
scopes: key.scopes,
|
|
135
|
+
...(sourceKeyId ? { sourceKeyId } : {}),
|
|
136
|
+
token: key.token,
|
|
137
|
+
},
|
|
138
|
+
],
|
|
139
|
+
})
|
|
140
|
+
return key.token
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export declare namespace resolve {
|
|
144
|
+
/** Context and permissions required by the generated operation. */
|
|
145
|
+
type Options = {
|
|
146
|
+
/** API-key environment. Defaults to production. */
|
|
147
|
+
environment?: 'production' | 'sandbox' | undefined
|
|
148
|
+
/** Owning organization; inferred only when unambiguous. */
|
|
149
|
+
orgId?: string | undefined
|
|
150
|
+
/** Attributed project; inferred only when unambiguous. */
|
|
151
|
+
projectId?: string | undefined
|
|
152
|
+
/** Scopes required by the operation. */
|
|
153
|
+
scopes: readonly string[]
|
|
154
|
+
/** Approved source key when more than one can delegate the required scopes. */
|
|
155
|
+
sourceKeyId?: string | undefined
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** Revokes inferred keys before forgetting or replacing a CLI session. */
|
|
160
|
+
export async function revoke(context: Auth.Context, session: Credentials.Session) {
|
|
161
|
+
const keys = session.apiKeys ?? []
|
|
162
|
+
const request = { headers: { authorization: `Bearer ${session.token}` } }
|
|
163
|
+
const results = await Promise.all(
|
|
164
|
+
keys.map(async (key) => {
|
|
165
|
+
const response = await context.api.v1.orgs[':orgId{org_[A-Za-z0-9_-]+}'].projects[
|
|
166
|
+
':projectId{prj_[A-Za-z0-9_-]+}'
|
|
167
|
+
]['api-keys'][':keyId{key_[A-Za-z0-9_-]+}']
|
|
168
|
+
.$delete(
|
|
169
|
+
{
|
|
170
|
+
param: { keyId: key.id, orgId: key.orgId, projectId: key.projectId },
|
|
171
|
+
},
|
|
172
|
+
request,
|
|
173
|
+
)
|
|
174
|
+
.catch(() => undefined)
|
|
175
|
+
return response?.status === 200 || response?.status === 404
|
|
176
|
+
}),
|
|
177
|
+
)
|
|
178
|
+
return results.every(Boolean)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function failure(code: string, message: string) {
|
|
182
|
+
return Response.json({ error: { code, message } }, { status: 403 })
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function describe(choices: readonly { id: string; name: string }[]) {
|
|
186
|
+
return choices.length
|
|
187
|
+
? choices.map(({ id, name }) => `${id} (${name})`).join(', ')
|
|
188
|
+
: 'none; create one in the Tempo console first'
|
|
189
|
+
}
|
package/src/cli/Cli.test.ts
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
|
+
import * as fs from 'node:fs/promises'
|
|
2
|
+
import * as http from 'node:http'
|
|
3
|
+
import type * as net from 'node:net'
|
|
4
|
+
import * as os from 'node:os'
|
|
5
|
+
import * as path from 'node:path'
|
|
1
6
|
import { Cli as incur_Cli, z } from 'incur'
|
|
2
7
|
import { Cli } from 'tapimo/server'
|
|
3
8
|
|
|
4
9
|
import * as TestApp from '../../test/App.js'
|
|
10
|
+
import * as TestCli from '../../test/cli.js'
|
|
5
11
|
import * as Runtime from '../../test/runtime.js'
|
|
6
12
|
|
|
7
13
|
const apiUrl = 'https://api.tempo-api.test'
|
|
@@ -83,6 +89,188 @@ describe('from', () => {
|
|
|
83
89
|
fetch.mockRestore()
|
|
84
90
|
}
|
|
85
91
|
})
|
|
92
|
+
|
|
93
|
+
test('generates commands for the v1 API only', async () => {
|
|
94
|
+
const context = await TestCli.create()
|
|
95
|
+
const spec = (await (await context.app.request('/openapi.json')).json()) as {
|
|
96
|
+
paths: Record<string, unknown>
|
|
97
|
+
}
|
|
98
|
+
const groups = new Set(Object.keys(spec.paths).map((path) => path.split('/')[1]))
|
|
99
|
+
expect(groups).toEqual(new Set(['gecko', 'rpc', 'v1']))
|
|
100
|
+
|
|
101
|
+
const { lines } = await TestCli.serve(context.cli, ['--help'])
|
|
102
|
+
const commands = lines
|
|
103
|
+
.slice(lines.indexOf('Commands:') + 1, lines.indexOf('Integrations:'))
|
|
104
|
+
.map((line) => line.trim())
|
|
105
|
+
expect(commands).toContain('v1 Call any Tempo API endpoint.')
|
|
106
|
+
for (const name of ['gecko', 'rpc'])
|
|
107
|
+
expect(commands.some((line) => line.startsWith(`${name} `))).toBe(false)
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
test('sends the CLI session to session operations and the API key elsewhere', async () => {
|
|
111
|
+
const context = await TestCli.create({ apiKey: TestApp.key.token })
|
|
112
|
+
await TestCli.login(context)
|
|
113
|
+
const { token } = (await context.credentials.get(TestCli.origin))!
|
|
114
|
+
const sent = (pathname: string) =>
|
|
115
|
+
context.requests.findLast((request) => new URL(request.url).pathname === pathname)!.headers
|
|
116
|
+
|
|
117
|
+
expect((await TestCli.serve(context.cli, ['v1', 'orgs', 'get', '--format', 'json'])).code).toBe(
|
|
118
|
+
0,
|
|
119
|
+
)
|
|
120
|
+
expect(sent('/v1/orgs').get('authorization')).toBe(`Bearer ${token}`)
|
|
121
|
+
expect(sent('/v1/orgs').has('tempo-api-key')).toBe(false)
|
|
122
|
+
|
|
123
|
+
await TestCli.serve(context.cli, ['v1', 'tokens', 'get', '--format', 'json'])
|
|
124
|
+
expect(sent('/v1/tokens').get('tempo-api-key')).toBe(TestApp.key.token)
|
|
125
|
+
expect(sent('/v1/tokens').has('authorization')).toBe(false)
|
|
126
|
+
|
|
127
|
+
// Explicit credentials always win over stored ones.
|
|
128
|
+
await TestCli.serve(context.cli, ['v1', 'orgs', 'get', '--authorization', 'Bearer explicit'])
|
|
129
|
+
expect(sent('/v1/orgs').get('authorization')).toBe('Bearer explicit')
|
|
130
|
+
|
|
131
|
+
// Including the `key` query credential, which the API reads after `Authorization`.
|
|
132
|
+
await TestCli.serve(context.cli, ['/v1/orgs', '--key', TestApp.key.token])
|
|
133
|
+
const request = context.requests.at(-1)!
|
|
134
|
+
expect(new URL(request.url).searchParams.get('key')).toBe(TestApp.key.token)
|
|
135
|
+
expect(request.headers.has('authorization')).toBe(false)
|
|
136
|
+
expect(request.headers.has('tempo-api-key')).toBe(false)
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
test('preserves an explicit compatibility API key without inferring credentials', async () => {
|
|
140
|
+
const context = await TestCli.create({
|
|
141
|
+
request: { headers: { 'x-api-key': TestApp.key.token } },
|
|
142
|
+
})
|
|
143
|
+
await TestCli.login(context)
|
|
144
|
+
await TestCli.serve(context.cli, ['v1', 'routes', 'transfers', 'get', '--format', 'json'])
|
|
145
|
+
const request = context.requests.findLast(
|
|
146
|
+
(request) => new URL(request.url).pathname === '/v1/routes/transfers',
|
|
147
|
+
)!
|
|
148
|
+
expect(request.headers.get('x-api-key')).toBe(TestApp.key.token)
|
|
149
|
+
expect(request.headers.has('tempo-api-key')).toBe(false)
|
|
150
|
+
expect(request.headers.has('authorization')).toBe(false)
|
|
151
|
+
expect((await context.credentials.get(TestCli.origin))?.apiKeys).toBeUndefined()
|
|
152
|
+
expect(
|
|
153
|
+
context.requests.some(
|
|
154
|
+
(request) => request.method === 'POST' && request.url.endsWith('/api-keys'),
|
|
155
|
+
),
|
|
156
|
+
).toBe(false)
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
test('forwards gateway request defaults to login, inference, and revocation', async () => {
|
|
160
|
+
const context = await TestCli.create({
|
|
161
|
+
request: {
|
|
162
|
+
headers: {
|
|
163
|
+
'cf-access-client-id': 'gateway-client',
|
|
164
|
+
'cf-access-client-secret': 'gateway-secret',
|
|
165
|
+
},
|
|
166
|
+
redirect: 'manual',
|
|
167
|
+
},
|
|
168
|
+
})
|
|
169
|
+
const { cookie } = await TestCli.login(context)
|
|
170
|
+
const created = await context.app.request('/v1/orgs', {
|
|
171
|
+
body: JSON.stringify({ name: 'Gateway project' }),
|
|
172
|
+
headers: { 'content-type': 'application/json', cookie },
|
|
173
|
+
method: 'POST',
|
|
174
|
+
})
|
|
175
|
+
const org = await created.json()
|
|
176
|
+
await context.app.request(`/v1/orgs/${org.id}/projects`, {
|
|
177
|
+
body: JSON.stringify({ name: 'Gateway' }),
|
|
178
|
+
headers: { 'content-type': 'application/json', cookie },
|
|
179
|
+
method: 'POST',
|
|
180
|
+
})
|
|
181
|
+
const result = await TestCli.serve(context.cli, [
|
|
182
|
+
'v1',
|
|
183
|
+
'routes',
|
|
184
|
+
'transfers',
|
|
185
|
+
'get',
|
|
186
|
+
'--format',
|
|
187
|
+
'json',
|
|
188
|
+
])
|
|
189
|
+
expect(result.code).toBe(0)
|
|
190
|
+
expect((await context.credentials.get(TestCli.origin))?.apiKeys).toHaveLength(1)
|
|
191
|
+
expect((await TestCli.serve(context.cli, ['logout', '--format', 'json'])).code).toBe(0)
|
|
192
|
+
expect(context.requests.some((request) => new URL(request.url).pathname === '/v1/scopes')).toBe(
|
|
193
|
+
true,
|
|
194
|
+
)
|
|
195
|
+
expect(
|
|
196
|
+
context.requests.some(
|
|
197
|
+
(request) => request.method === 'POST' && request.url.endsWith('/api-keys'),
|
|
198
|
+
),
|
|
199
|
+
).toBe(true)
|
|
200
|
+
expect(context.requests.some((request) => request.method === 'DELETE')).toBe(true)
|
|
201
|
+
for (const request of context.requests) {
|
|
202
|
+
expect(request.headers.get('cf-access-client-id')).toBe('gateway-client')
|
|
203
|
+
expect(request.headers.get('cf-access-client-secret')).toBe('gateway-secret')
|
|
204
|
+
expect(request.redirect).toBe('manual')
|
|
205
|
+
}
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
test('sends the API key only to operations that accept one', async () => {
|
|
209
|
+
const context = await TestCli.create({ apiKey: TestApp.key.token })
|
|
210
|
+
const sent = (pathname: string) =>
|
|
211
|
+
context.requests.findLast((request) => new URL(request.url).pathname === pathname)
|
|
212
|
+
|
|
213
|
+
// Sign-in-only operations stop before sending, so an API key cannot earn a misleading 403.
|
|
214
|
+
const invitations = await TestCli.serve(context.cli, [
|
|
215
|
+
'v1',
|
|
216
|
+
'invitations',
|
|
217
|
+
'get',
|
|
218
|
+
'--format',
|
|
219
|
+
'json',
|
|
220
|
+
])
|
|
221
|
+
expect(invitations.code).toBe(1)
|
|
222
|
+
expect(JSON.parse(invitations.lines.join('\n'))).toMatchObject({
|
|
223
|
+
code: 'not_signed_in',
|
|
224
|
+
message: 'Not signed in, or the session expired. Run `tapimo login`.',
|
|
225
|
+
})
|
|
226
|
+
expect(sent('/v1/invitations')).toBeUndefined()
|
|
227
|
+
|
|
228
|
+
// Public operations go out without credentials.
|
|
229
|
+
await TestCli.serve(context.cli, [
|
|
230
|
+
'v1',
|
|
231
|
+
'auth',
|
|
232
|
+
'siwe',
|
|
233
|
+
'challenge',
|
|
234
|
+
'post',
|
|
235
|
+
'--format',
|
|
236
|
+
'json',
|
|
237
|
+
])
|
|
238
|
+
expect(sent('/v1/auth/siwe/challenge')?.headers.has('tempo-api-key')).toBe(false)
|
|
239
|
+
})
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
describe('fromEnv', () => {
|
|
243
|
+
test('keeps session commands usable when the API document cannot load', async () => {
|
|
244
|
+
const home = await fs.mkdtemp(path.join(os.tmpdir(), 'tapimo-home-'))
|
|
245
|
+
// Port 9 (discard) refuses connections, so the document fetch fails fast.
|
|
246
|
+
const cli = await Cli.fromEnv(
|
|
247
|
+
{ TEMPO_API_URL: 'http://127.0.0.1:9', TEMPO_HOME: home },
|
|
248
|
+
{ docs: false },
|
|
249
|
+
)
|
|
250
|
+
const { code, lines } = await TestCli.serve(cli, ['whoami', '--format', 'json'])
|
|
251
|
+
expect(code).toBe(1)
|
|
252
|
+
expect(JSON.parse(lines.join('\n')).code).toBe('not_signed_in')
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
test('skips fetching the API document when one is supplied', async () => {
|
|
256
|
+
const home = await fs.mkdtemp(path.join(os.tmpdir(), 'tapimo-home-'))
|
|
257
|
+
let requests = 0
|
|
258
|
+
const server = http.createServer((_, response) => {
|
|
259
|
+
requests += 1
|
|
260
|
+
response.end('{}')
|
|
261
|
+
})
|
|
262
|
+
await new Promise<void>((resolve) => server.listen(0, '127.0.0.1', resolve))
|
|
263
|
+
const { port } = server.address() as net.AddressInfo
|
|
264
|
+
try {
|
|
265
|
+
await Cli.fromEnv(
|
|
266
|
+
{ TEMPO_API_URL: `http://127.0.0.1:${port}`, TEMPO_HOME: home },
|
|
267
|
+
{ docs: false, openapi: { paths: {} } },
|
|
268
|
+
)
|
|
269
|
+
expect(requests).toBe(0)
|
|
270
|
+
} finally {
|
|
271
|
+
server.close()
|
|
272
|
+
}
|
|
273
|
+
})
|
|
86
274
|
})
|
|
87
275
|
|
|
88
276
|
function mockHostedApi(app = TestApp.create()) {
|