tapimo 0.15.1 → 0.18.1
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 +893 -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 +70 -70
- 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 +529 -76
- 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 +157 -157
- 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 +205 -201
- 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 +357 -357
- 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/blocks.d.ts +125 -125
- 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 +109 -127
- package/dist/apps/data/routes/fee-amm.d.ts.map +1 -1
- package/dist/apps/data/routes/fee-amm.js +83 -54
- package/dist/apps/data/routes/fee-amm.js.map +1 -1
- package/dist/apps/data/routes/indexer.d.ts +43 -43
- package/dist/apps/data/routes/receipts.d.ts +235 -235
- package/dist/apps/data/routes/rpc.d.ts +30 -30
- 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 +77 -77
- package/dist/apps/data/routes/valuation.d.ts +32 -32
- package/dist/apps/data/routes/verified-tokens.d.ts +86 -86
- package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
- package/dist/apps/data/routes/webhooks.js +1 -1
- package/dist/apps/data/routes/webhooks.js.map +1 -1
- package/dist/apps/data/routes/zones.d.ts +31 -31
- package/dist/apps/management/App.d.ts +2909 -2064
- 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 +257 -245
- 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 +138 -138
- package/dist/apps/management/routes/invite-links.d.ts +172 -172
- package/dist/apps/management/routes/members.d.ts +16 -16
- package/dist/apps/management/routes/orgs.d.ts +104 -104
- 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 +125 -125
- package/dist/apps/management/routes/usage.d.ts +8 -8
- package/dist/apps/management/routes/verified-token-requests.d.ts +86 -86
- package/dist/apps/management/routes/webhooks.d.ts +485 -485
- package/dist/apps/mpp/App.d.ts +44 -44
- 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/chains.d.ts +25 -25
- 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 +171 -81
- 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 +532 -480
- 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 +413 -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/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 +57 -6
- package/dist/internal/Auth.d.ts.map +1 -1
- package/dist/internal/Auth.js +182 -78
- 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 +3 -0
- package/dist/internal/routes/providers/relay.d.ts.map +1 -1
- package/dist/internal/routes/providers/relay.js +352 -41
- 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/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/routeTree.gen.ts +21 -0
- 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 +1 -0
- 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 +1 -0
- package/src/admin/ui.generated.ts +258 -202
- 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/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 +134 -2
- package/src/apps/data/routes/fee-amm.ts +122 -60
- package/src/apps/data/routes/receipts.test.ts +21 -18
- package/src/apps/data/routes/rpc.test.ts +2 -2
- 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.test.ts +12 -3
- package/src/apps/data/routes/webhooks.ts +2 -1
- 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/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 +498 -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 +66 -5
- 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/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 +291 -8
- package/src/internal/Auth.ts +227 -96
- 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 +1263 -41
- package/src/internal/routes/providers/relay.ts +423 -38
- 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()) {
|