tapimo 0.14.0 → 0.15.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 +7 -4
- package/dist/ApiKey.js +1 -1
- package/dist/ApiKey.js.map +1 -1
- package/dist/ApiKeys.d.ts +95 -14
- package/dist/ApiKeys.d.ts.map +1 -1
- package/dist/ApiKeys.js +360 -49
- package/dist/ApiKeys.js.map +1 -1
- package/dist/App.d.ts +12457 -10365
- package/dist/App.d.ts.map +1 -1
- package/dist/App.js +300 -22
- package/dist/App.js.map +1 -1
- package/dist/Assets.d.ts.map +1 -1
- package/dist/Assets.js +2 -1
- package/dist/Assets.js.map +1 -1
- package/dist/Handler.d.ts +0 -1
- package/dist/Handler.d.ts.map +1 -1
- package/dist/Handler.js +0 -1
- package/dist/Handler.js.map +1 -1
- package/dist/Scope.d.ts +13 -5
- package/dist/Scope.d.ts.map +1 -1
- package/dist/Scope.js +16 -5
- package/dist/Scope.js.map +1 -1
- package/dist/Webhooks.js +1 -1
- package/dist/Webhooks.js.map +1 -1
- package/dist/admin/App.d.ts +3057 -549
- package/dist/admin/App.d.ts.map +1 -1
- package/dist/admin/App.js +26 -5
- package/dist/admin/App.js.map +1 -1
- package/dist/admin/apps/api-keys.d.ts +159 -7
- package/dist/admin/apps/api-keys.d.ts.map +1 -1
- package/dist/admin/apps/api-keys.js +95 -43
- package/dist/admin/apps/api-keys.js.map +1 -1
- package/dist/admin/apps/earn-vaults.d.ts +1398 -0
- package/dist/admin/apps/earn-vaults.d.ts.map +1 -1
- package/dist/admin/apps/earn-vaults.js +463 -7
- package/dist/admin/apps/earn-vaults.js.map +1 -1
- package/dist/admin/apps/organizations.d.ts +267 -15
- package/dist/admin/apps/organizations.d.ts.map +1 -1
- package/dist/admin/apps/organizations.js +241 -25
- package/dist/admin/apps/organizations.js.map +1 -1
- package/dist/admin/apps/routes-support.d.ts +95 -0
- package/dist/admin/apps/routes-support.d.ts.map +1 -0
- package/dist/admin/apps/routes-support.js +151 -0
- package/dist/admin/apps/routes-support.js.map +1 -0
- package/dist/admin/apps/routes.d.ts +2764 -0
- package/dist/admin/apps/routes.d.ts.map +1 -0
- package/dist/admin/apps/routes.js +728 -0
- package/dist/admin/apps/routes.js.map +1 -0
- package/dist/admin/apps/subsidies.d.ts +501 -0
- package/dist/admin/apps/subsidies.d.ts.map +1 -0
- package/dist/admin/apps/subsidies.js +384 -0
- package/dist/admin/apps/subsidies.js.map +1 -0
- package/dist/admin/apps/verified-tokens.d.ts +88 -88
- package/dist/admin/apps/verified-tokens.d.ts.map +1 -1
- package/dist/admin/apps/verified-tokens.js +3 -3
- package/dist/admin/apps/verified-tokens.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 +129 -69
- package/dist/admin/ui.generated.js.map +1 -1
- package/dist/analytics/Analytics.d.ts +5 -2
- package/dist/analytics/Analytics.d.ts.map +1 -1
- package/dist/analytics/Analytics.js +31 -1
- package/dist/analytics/Analytics.js.map +1 -1
- package/dist/analytics/tables/requestEvents.d.ts +2 -1
- package/dist/analytics/tables/requestEvents.d.ts.map +1 -1
- package/dist/analytics/tables/requestEvents.js +10 -1
- package/dist/analytics/tables/requestEvents.js.map +1 -1
- package/dist/analytics/tables/routesLifecycleIntervals.d.ts +72 -0
- package/dist/analytics/tables/routesLifecycleIntervals.d.ts.map +1 -0
- package/dist/analytics/tables/routesLifecycleIntervals.js +56 -0
- package/dist/analytics/tables/routesLifecycleIntervals.js.map +1 -0
- package/dist/apps/Zones.d.ts +21 -12526
- package/dist/apps/Zones.d.ts.map +1 -1
- package/dist/apps/Zones.js +37 -5
- package/dist/apps/Zones.js.map +1 -1
- package/dist/apps/data/App.d.ts +3971 -2595
- package/dist/apps/data/App.d.ts.map +1 -1
- package/dist/apps/data/App.js +76 -21
- package/dist/apps/data/App.js.map +1 -1
- package/dist/apps/data/routes/activities.d.ts +364 -34
- package/dist/apps/data/routes/activities.d.ts.map +1 -1
- package/dist/apps/data/routes/activities.js +495 -190
- package/dist/apps/data/routes/activities.js.map +1 -1
- package/dist/apps/data/routes/addresses.d.ts +72 -0
- package/dist/apps/data/routes/addresses.d.ts.map +1 -1
- package/dist/apps/data/routes/blocks.d.ts +37 -37
- package/dist/apps/data/routes/coingecko.d.ts.map +1 -1
- package/dist/apps/data/routes/coingecko.js +47 -21
- package/dist/apps/data/routes/coingecko.js.map +1 -1
- package/dist/apps/data/routes/earn.d.ts +1450 -337
- package/dist/apps/data/routes/earn.d.ts.map +1 -1
- package/dist/apps/data/routes/earn.js +1226 -144
- package/dist/apps/data/routes/earn.js.map +1 -1
- package/dist/apps/data/routes/fee-amm.d.ts +5 -0
- package/dist/apps/data/routes/fee-amm.d.ts.map +1 -1
- package/dist/apps/data/routes/fee-amm.js +31 -19
- package/dist/apps/data/routes/fee-amm.js.map +1 -1
- package/dist/apps/data/routes/indexer.d.ts +125 -0
- package/dist/apps/data/routes/indexer.d.ts.map +1 -1
- package/dist/apps/data/routes/indexer.js +83 -27
- package/dist/apps/data/routes/indexer.js.map +1 -1
- package/dist/apps/data/routes/rpc.d.ts +507 -33
- package/dist/apps/data/routes/rpc.d.ts.map +1 -1
- package/dist/apps/data/routes/rpc.js +266 -156
- package/dist/apps/data/routes/rpc.js.map +1 -1
- package/dist/apps/data/routes/tokenlist.d.ts +47 -15
- package/dist/apps/data/routes/tokenlist.d.ts.map +1 -1
- package/dist/apps/data/routes/tokenlist.js +68 -11
- package/dist/apps/data/routes/tokenlist.js.map +1 -1
- package/dist/apps/data/routes/tokens.d.ts +2 -0
- package/dist/apps/data/routes/tokens.d.ts.map +1 -1
- package/dist/apps/data/routes/tokens.js +26 -14
- package/dist/apps/data/routes/tokens.js.map +1 -1
- package/dist/apps/data/routes/transfers.d.ts +28 -12
- package/dist/apps/data/routes/transfers.d.ts.map +1 -1
- package/dist/apps/data/routes/transfers.js +9 -7
- package/dist/apps/data/routes/transfers.js.map +1 -1
- package/dist/apps/data/routes/valuation.d.ts +30 -30
- package/dist/apps/data/routes/verified-tokens.d.ts +9 -9
- package/dist/apps/data/routes/verified-tokens.d.ts.map +1 -1
- package/dist/apps/data/routes/verified-tokens.js +64 -20
- package/dist/apps/data/routes/verified-tokens.js.map +1 -1
- package/dist/apps/data/routes/webhooks.d.ts +613 -176
- package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
- package/dist/apps/data/routes/webhooks.js +291 -195
- package/dist/apps/data/routes/webhooks.js.map +1 -1
- package/dist/apps/data/routes/zones.d.ts.map +1 -1
- package/dist/apps/data/routes/zones.js +20 -12
- package/dist/apps/data/routes/zones.js.map +1 -1
- package/dist/apps/docs/reference.js +2 -2
- package/dist/apps/docs/reference.js.map +1 -1
- package/dist/apps/index.d.ts +1 -1
- package/dist/apps/index.d.ts.map +1 -1
- package/dist/apps/index.js +1 -1
- package/dist/apps/index.js.map +1 -1
- package/dist/apps/management/App.d.ts +3763 -3385
- package/dist/apps/management/App.d.ts.map +1 -1
- package/dist/apps/management/App.js +5 -14
- package/dist/apps/management/App.js.map +1 -1
- package/dist/apps/management/Billing.d.ts +48 -7
- package/dist/apps/management/Billing.d.ts.map +1 -1
- package/dist/apps/management/Billing.js +204 -25
- package/dist/apps/management/Billing.js.map +1 -1
- package/dist/apps/management/routes/api-keys.d.ts +106 -4
- package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
- package/dist/apps/management/routes/api-keys.js +43 -15
- package/dist/apps/management/routes/api-keys.js.map +1 -1
- package/dist/apps/management/routes/billing.d.ts +290 -4
- package/dist/apps/management/routes/billing.d.ts.map +1 -1
- package/dist/apps/management/routes/billing.js +274 -49
- package/dist/apps/management/routes/billing.js.map +1 -1
- package/dist/apps/management/routes/faucet.d.ts +6 -6
- package/dist/apps/management/routes/faucet.js +1 -1
- package/dist/apps/management/routes/faucet.js.map +1 -1
- package/dist/apps/management/routes/invitations.d.ts +77 -2
- package/dist/apps/management/routes/invitations.d.ts.map +1 -1
- package/dist/apps/management/routes/invitations.js +117 -13
- package/dist/apps/management/routes/invitations.js.map +1 -1
- package/dist/apps/management/routes/invite-links.d.ts +4 -4
- package/dist/apps/management/routes/me.d.ts.map +1 -1
- package/dist/apps/management/routes/me.js +6 -2
- package/dist/apps/management/routes/me.js.map +1 -1
- package/dist/apps/management/routes/members.d.ts +30 -30
- package/dist/apps/management/routes/orgs.d.ts +18 -2
- package/dist/apps/management/routes/orgs.d.ts.map +1 -1
- package/dist/apps/management/routes/orgs.js +52 -12
- package/dist/apps/management/routes/orgs.js.map +1 -1
- package/dist/apps/management/routes/projects.d.ts +4 -4
- package/dist/apps/management/routes/projects.d.ts.map +1 -1
- package/dist/apps/management/routes/projects.js +6 -1
- package/dist/apps/management/routes/projects.js.map +1 -1
- package/dist/apps/management/routes/scopes.d.ts.map +1 -1
- package/dist/apps/management/routes/scopes.js +4 -1
- package/dist/apps/management/routes/scopes.js.map +1 -1
- package/dist/apps/management/routes/usage.d.ts +24 -24
- package/dist/apps/management/routes/usage.js +21 -4
- package/dist/apps/management/routes/usage.js.map +1 -1
- package/dist/apps/management/routes/verified-token-requests.d.ts +2 -2
- package/dist/apps/management/routes/webhooks.d.ts +280 -214
- package/dist/apps/management/routes/webhooks.d.ts.map +1 -1
- package/dist/apps/management/routes/webhooks.js +58 -12
- package/dist/apps/management/routes/webhooks.js.map +1 -1
- package/dist/apps/mcp.d.ts +1 -1
- package/dist/apps/mcp.js +7 -7
- package/dist/apps/mcp.js.map +1 -1
- package/dist/apps/mpp/App.d.ts.map +1 -1
- package/dist/apps/mpp/App.js +35 -7
- package/dist/apps/mpp/App.js.map +1 -1
- package/dist/apps/mpp/internal/Adapter.d.ts +2 -0
- package/dist/apps/mpp/internal/Adapter.d.ts.map +1 -1
- package/dist/apps/mpp/internal/Adapter.js +30 -1
- package/dist/apps/mpp/internal/Adapter.js.map +1 -1
- package/dist/apps/mpp/internal/Policy.d.ts +22 -4
- package/dist/apps/mpp/internal/Policy.d.ts.map +1 -1
- package/dist/apps/mpp/internal/Policy.js +24 -6
- package/dist/apps/mpp/internal/Policy.js.map +1 -1
- package/dist/apps/mpp/internal/Sponsorship.d.ts +3 -0
- package/dist/apps/mpp/internal/Sponsorship.d.ts.map +1 -1
- package/dist/apps/mpp/internal/Sponsorship.js +6 -3
- package/dist/apps/mpp/internal/Sponsorship.js.map +1 -1
- package/dist/apps/relay/App.d.ts +2 -0
- package/dist/apps/relay/App.d.ts.map +1 -1
- package/dist/apps/relay/App.js +134 -75
- package/dist/apps/relay/App.js.map +1 -1
- package/dist/apps/relay/Sponsorships.d.ts +5 -3
- package/dist/apps/relay/Sponsorships.d.ts.map +1 -1
- package/dist/apps/relay/Sponsorships.js +209 -28
- package/dist/apps/relay/Sponsorships.js.map +1 -1
- package/dist/apps/{funding → routes}/Access.d.ts +4 -4
- package/dist/apps/routes/Access.d.ts.map +1 -0
- package/dist/apps/{funding → routes}/Access.js +4 -4
- package/dist/apps/routes/Access.js.map +1 -0
- package/dist/apps/{funding → routes}/App.d.ts +418 -124
- package/dist/apps/{funding → routes}/App.d.ts.map +1 -1
- package/dist/apps/{funding → routes}/App.js +16 -16
- package/dist/apps/routes/App.js.map +1 -0
- package/dist/apps/routes/Inventory.d.ts.map +1 -0
- package/dist/apps/{funding → routes}/Inventory.js +2 -2
- package/dist/apps/routes/Inventory.js.map +1 -0
- package/dist/apps/routes/index.d.ts +2 -0
- package/dist/apps/routes/index.d.ts.map +1 -0
- package/dist/apps/routes/index.js +2 -0
- package/dist/apps/routes/index.js.map +1 -0
- package/dist/apps/{funding → routes}/routes/chains.d.ts +7 -7
- package/dist/apps/routes/routes/chains.d.ts.map +1 -0
- package/dist/apps/{funding → routes}/routes/chains.js +25 -25
- package/dist/apps/routes/routes/chains.js.map +1 -0
- package/dist/apps/{funding → routes}/routes/deposit-addresses.d.ts +107 -43
- package/dist/apps/routes/routes/deposit-addresses.d.ts.map +1 -0
- package/dist/apps/{funding → routes}/routes/deposit-addresses.js +497 -222
- package/dist/apps/routes/routes/deposit-addresses.js.map +1 -0
- package/dist/apps/{funding → routes}/routes/providers.d.ts +8 -8
- package/dist/apps/routes/routes/providers.d.ts.map +1 -0
- package/dist/apps/routes/routes/providers.js +57 -0
- package/dist/apps/routes/routes/providers.js.map +1 -0
- package/dist/apps/{funding → routes}/routes/quotes.d.ts +19 -16
- package/dist/apps/routes/routes/quotes.d.ts.map +1 -0
- package/dist/apps/{funding → routes}/routes/quotes.js +55 -38
- package/dist/apps/routes/routes/quotes.js.map +1 -0
- package/dist/apps/{funding → routes}/routes/transfers.d.ts +379 -74
- package/dist/apps/routes/routes/transfers.d.ts.map +1 -0
- package/dist/apps/routes/routes/transfers.js +1140 -0
- package/dist/apps/routes/routes/transfers.js.map +1 -0
- package/dist/cli/Cli.d.ts +30 -0
- package/dist/cli/Cli.d.ts.map +1 -1
- package/dist/cli/commands/admin.d.ts +36 -0
- package/dist/cli/commands/admin.d.ts.map +1 -1
- package/dist/cli/commands/admin.js +273 -0
- package/dist/cli/commands/admin.js.map +1 -1
- package/dist/cloudflare.d.ts +16 -14
- package/dist/cloudflare.d.ts.map +1 -1
- package/dist/cloudflare.js +26 -21
- package/dist/cloudflare.js.map +1 -1
- package/dist/db/Db.d.ts +63 -21
- package/dist/db/Db.d.ts.map +1 -1
- package/dist/db/Db.js +1030 -15
- package/dist/db/Db.js.map +1 -1
- package/dist/db/Schema.d.ts +780 -133
- package/dist/db/Schema.d.ts.map +1 -1
- package/dist/db/Schema.js +575 -162
- package/dist/db/Schema.js.map +1 -1
- package/dist/db/tables/apiKeyAdmissionBootstraps.d.ts +4 -0
- package/dist/db/tables/apiKeyAdmissionBootstraps.d.ts.map +1 -0
- package/dist/db/tables/apiKeyAdmissionBootstraps.js +2 -0
- package/dist/db/tables/apiKeyAdmissionBootstraps.js.map +1 -0
- package/dist/db/tables/apiKeyAdmissions.d.ts +73 -0
- package/dist/db/tables/apiKeyAdmissions.d.ts.map +1 -0
- package/dist/db/tables/apiKeyAdmissions.js +142 -0
- package/dist/db/tables/apiKeyAdmissions.js.map +1 -0
- package/dist/db/tables/apiKeyOwnerAdmissions.d.ts +4 -0
- package/dist/db/tables/apiKeyOwnerAdmissions.d.ts.map +1 -0
- package/dist/db/tables/apiKeyOwnerAdmissions.js +2 -0
- package/dist/db/tables/apiKeyOwnerAdmissions.js.map +1 -0
- package/dist/db/tables/apiKeyOwnerTombstones.d.ts +20 -0
- package/dist/db/tables/apiKeyOwnerTombstones.d.ts.map +1 -0
- package/dist/db/tables/apiKeyOwnerTombstones.js +57 -0
- package/dist/db/tables/apiKeyOwnerTombstones.js.map +1 -0
- package/dist/db/tables/apiKeyRevocations.d.ts +21 -0
- package/dist/db/tables/apiKeyRevocations.d.ts.map +1 -0
- package/dist/db/tables/apiKeyRevocations.js +98 -0
- package/dist/db/tables/apiKeyRevocations.js.map +1 -0
- package/dist/db/tables/authAccounts.d.ts +47 -0
- package/dist/db/tables/authAccounts.d.ts.map +1 -0
- package/dist/db/tables/authAccounts.js +72 -0
- package/dist/db/tables/authAccounts.js.map +1 -0
- package/dist/db/tables/authSessions.d.ts +12 -0
- package/dist/db/tables/authSessions.d.ts.map +1 -0
- package/dist/db/tables/authSessions.js +2 -0
- package/dist/db/tables/authSessions.js.map +1 -0
- package/dist/db/tables/authVerifications.d.ts +12 -0
- package/dist/db/tables/authVerifications.d.ts.map +1 -0
- package/dist/db/tables/authVerifications.js +2 -0
- package/dist/db/tables/authVerifications.js.map +1 -0
- package/dist/db/tables/invitations.d.ts +17 -1
- package/dist/db/tables/invitations.d.ts.map +1 -1
- package/dist/db/tables/invitations.js +123 -43
- package/dist/db/tables/invitations.js.map +1 -1
- package/dist/db/tables/memberships.d.ts +8 -0
- package/dist/db/tables/memberships.d.ts.map +1 -1
- package/dist/db/tables/memberships.js +51 -2
- package/dist/db/tables/memberships.js.map +1 -1
- package/dist/db/tables/organizations.d.ts +32 -10
- package/dist/db/tables/organizations.d.ts.map +1 -1
- package/dist/db/tables/organizations.js +110 -49
- package/dist/db/tables/organizations.js.map +1 -1
- package/dist/db/tables/projects.d.ts +2 -0
- package/dist/db/tables/projects.d.ts.map +1 -1
- package/dist/db/tables/projects.js +12 -3
- package/dist/db/tables/projects.js.map +1 -1
- package/dist/db/tables/requestUsage.d.ts.map +1 -1
- package/dist/db/tables/requestUsage.js.map +1 -1
- package/dist/db/tables/rewardAccounts.d.ts +86 -0
- package/dist/db/tables/rewardAccounts.d.ts.map +1 -0
- package/dist/db/tables/rewardAccounts.js +88 -0
- package/dist/db/tables/rewardAccounts.js.map +1 -0
- package/dist/db/tables/rewardCampaignDistributors.d.ts +36 -0
- package/dist/db/tables/rewardCampaignDistributors.d.ts.map +1 -0
- package/dist/db/tables/rewardCampaignDistributors.js +30 -0
- package/dist/db/tables/rewardCampaignDistributors.js.map +1 -0
- package/dist/db/tables/rewardCampaigns.d.ts +171 -0
- package/dist/db/tables/rewardCampaigns.d.ts.map +1 -0
- package/dist/db/tables/rewardCampaigns.js +392 -0
- package/dist/db/tables/rewardCampaigns.js.map +1 -0
- package/dist/db/tables/rewardCredits.d.ts +53 -0
- package/dist/db/tables/rewardCredits.d.ts.map +1 -0
- package/dist/db/tables/rewardCredits.js +63 -0
- package/dist/db/tables/rewardCredits.js.map +1 -0
- package/dist/db/tables/rewardEligibilityAssociations.d.ts +61 -0
- package/dist/db/tables/rewardEligibilityAssociations.d.ts.map +1 -0
- package/dist/db/tables/rewardEligibilityAssociations.js +103 -0
- package/dist/db/tables/rewardEligibilityAssociations.js.map +1 -0
- package/dist/db/tables/rewardRuns.d.ts +188 -0
- package/dist/db/tables/rewardRuns.d.ts.map +1 -0
- package/dist/db/tables/rewardRuns.js +278 -0
- package/dist/db/tables/rewardRuns.js.map +1 -0
- package/dist/db/tables/rewardTransactionAttempts.d.ts +100 -0
- package/dist/db/tables/rewardTransactionAttempts.d.ts.map +1 -0
- package/dist/db/tables/rewardTransactionAttempts.js +163 -0
- package/dist/db/tables/rewardTransactionAttempts.js.map +1 -0
- package/dist/db/tables/routesCatalog.d.ts +58 -0
- package/dist/db/tables/routesCatalog.d.ts.map +1 -0
- package/dist/db/tables/{fundingCatalog.js → routesCatalog.js} +19 -19
- package/dist/db/tables/routesCatalog.js.map +1 -0
- package/dist/db/tables/routesDepositAddresses.d.ts +476 -0
- package/dist/db/tables/routesDepositAddresses.d.ts.map +1 -0
- package/dist/db/tables/routesDepositAddresses.js +587 -0
- package/dist/db/tables/routesDepositAddresses.js.map +1 -0
- package/dist/db/tables/{fundingDepositRequestObservations.d.ts → routesDepositRequestObservations.d.ts} +6 -6
- package/dist/db/tables/routesDepositRequestObservations.d.ts.map +1 -0
- package/dist/db/tables/{fundingDepositRequestObservations.js → routesDepositRequestObservations.js} +6 -6
- package/dist/db/tables/routesDepositRequestObservations.js.map +1 -0
- package/dist/db/tables/{fundingDeposits.d.ts → routesDeposits.d.ts} +210 -22
- package/dist/db/tables/routesDeposits.d.ts.map +1 -0
- package/dist/db/tables/routesDeposits.js +576 -0
- package/dist/db/tables/routesDeposits.js.map +1 -0
- package/dist/db/tables/routesIdempotency.d.ts +166 -0
- package/dist/db/tables/routesIdempotency.d.ts.map +1 -0
- package/dist/db/tables/routesIdempotency.js +330 -0
- package/dist/db/tables/routesIdempotency.js.map +1 -0
- package/dist/db/tables/routesSubsidies.d.ts +27 -0
- package/dist/db/tables/routesSubsidies.d.ts.map +1 -0
- package/dist/db/tables/routesSubsidies.js +45 -0
- package/dist/db/tables/routesSubsidies.js.map +1 -0
- package/dist/db/tables/{fundingTransferEvents.d.ts → routesTransferEvents.d.ts} +3 -3
- package/dist/db/tables/routesTransferEvents.d.ts.map +1 -0
- package/dist/db/tables/{fundingTransferEvents.js → routesTransferEvents.js} +3 -3
- package/dist/db/tables/routesTransferEvents.js.map +1 -0
- package/dist/db/tables/routesTransferSubsidies.d.ts +19 -0
- package/dist/db/tables/routesTransferSubsidies.d.ts.map +1 -0
- package/dist/db/tables/routesTransferSubsidies.js +17 -0
- package/dist/db/tables/routesTransferSubsidies.js.map +1 -0
- package/dist/db/tables/routesTransferSubsidyNonces.d.ts +51 -0
- package/dist/db/tables/routesTransferSubsidyNonces.d.ts.map +1 -0
- package/dist/db/tables/routesTransferSubsidyNonces.js +82 -0
- package/dist/db/tables/routesTransferSubsidyNonces.js.map +1 -0
- package/dist/db/tables/routesTransferTransactions.d.ts +77 -0
- package/dist/db/tables/routesTransferTransactions.d.ts.map +1 -0
- package/dist/db/tables/{fundingTransferTransactions.js → routesTransferTransactions.js} +31 -13
- package/dist/db/tables/routesTransferTransactions.js.map +1 -0
- package/dist/db/tables/routesTransfers.d.ts +319 -0
- package/dist/db/tables/routesTransfers.d.ts.map +1 -0
- package/dist/db/tables/routesTransfers.js +372 -0
- package/dist/db/tables/routesTransfers.js.map +1 -0
- package/dist/db/tables/sponsoredTransactions.d.ts +75 -20
- package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -1
- package/dist/db/tables/sponsoredTransactions.js +124 -54
- package/dist/db/tables/sponsoredTransactions.js.map +1 -1
- package/dist/db/tables/sponsorshipAttributions.d.ts +69 -0
- package/dist/db/tables/sponsorshipAttributions.d.ts.map +1 -0
- package/dist/db/tables/sponsorshipAttributions.js +77 -0
- package/dist/db/tables/sponsorshipAttributions.js.map +1 -0
- package/dist/db/tables/sponsorshipReconciliationCursors.d.ts +22 -0
- package/dist/db/tables/sponsorshipReconciliationCursors.d.ts.map +1 -0
- package/dist/db/tables/sponsorshipReconciliationCursors.js +45 -0
- package/dist/db/tables/sponsorshipReconciliationCursors.js.map +1 -0
- package/dist/db/tables/stripeCustomers.d.ts +11 -0
- package/dist/db/tables/stripeCustomers.d.ts.map +1 -1
- package/dist/db/tables/stripeCustomers.js +13 -0
- package/dist/db/tables/stripeCustomers.js.map +1 -1
- package/dist/db/tables/users.d.ts +17 -1
- package/dist/db/tables/users.d.ts.map +1 -1
- package/dist/db/tables/users.js +47 -2
- package/dist/db/tables/users.js.map +1 -1
- package/dist/db/tables/webhookQueueEvents.d.ts.map +1 -1
- package/dist/db/tables/webhookQueueEvents.js +5 -1
- package/dist/db/tables/webhookQueueEvents.js.map +1 -1
- package/dist/db/tables/webhookSubscriptions.d.ts +19 -11
- package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -1
- package/dist/db/tables/webhookSubscriptions.js +45 -13
- package/dist/db/tables/webhookSubscriptions.js.map +1 -1
- package/dist/handlers/internal/executionError.d.ts +2 -2
- package/dist/handlers/internal/executionError.d.ts.map +1 -1
- package/dist/handlers/internal/executionError.js +1 -2
- package/dist/handlers/internal/executionError.js.map +1 -1
- package/dist/handlers/internal/multisig.d.ts +4 -232
- package/dist/handlers/internal/multisig.d.ts.map +1 -1
- package/dist/handlers/internal/multisig.js +11 -537
- package/dist/handlers/internal/multisig.js.map +1 -1
- package/dist/handlers/internal/sponsorship.d.ts +19 -8
- package/dist/handlers/internal/sponsorship.d.ts.map +1 -1
- package/dist/handlers/internal/sponsorship.js +13 -9
- package/dist/handlers/internal/sponsorship.js.map +1 -1
- package/dist/handlers/relay.d.ts +9 -12
- package/dist/handlers/relay.d.ts.map +1 -1
- package/dist/handlers/relay.js +81 -85
- package/dist/handlers/relay.js.map +1 -1
- package/dist/index.d.ts +0 -17
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -17
- package/dist/index.js.map +1 -1
- package/dist/internal/AssetResponse.d.ts +12 -0
- package/dist/internal/AssetResponse.d.ts.map +1 -0
- package/dist/internal/AssetResponse.js +21 -0
- package/dist/internal/AssetResponse.js.map +1 -0
- package/dist/internal/Auth.d.ts +48 -12
- package/dist/internal/Auth.d.ts.map +1 -1
- package/dist/internal/Auth.js +603 -175
- package/dist/internal/Auth.js.map +1 -1
- package/dist/internal/BetterAuth.d.ts +1488 -0
- package/dist/internal/BetterAuth.d.ts.map +1 -0
- package/dist/internal/BetterAuth.js +253 -0
- package/dist/internal/BetterAuth.js.map +1 -0
- package/dist/internal/Cache.d.ts +2 -0
- package/dist/internal/Cache.d.ts.map +1 -1
- package/dist/internal/Cache.js +2 -0
- package/dist/internal/Cache.js.map +1 -1
- package/dist/internal/EarnRates.d.ts +26 -3
- package/dist/internal/EarnRates.d.ts.map +1 -1
- package/dist/internal/EarnRates.js +84 -19
- package/dist/internal/EarnRates.js.map +1 -1
- package/dist/internal/EarnSharePrices.d.ts +120 -0
- package/dist/internal/EarnSharePrices.d.ts.map +1 -0
- package/dist/internal/EarnSharePrices.js +179 -0
- package/dist/internal/EarnSharePrices.js.map +1 -0
- package/dist/internal/EarnVaults.d.ts +4 -0
- package/dist/internal/EarnVaults.d.ts.map +1 -1
- package/dist/internal/EarnVaults.js +27 -10
- package/dist/internal/EarnVaults.js.map +1 -1
- package/dist/internal/Email.d.ts +37 -0
- package/dist/internal/Email.d.ts.map +1 -0
- package/dist/internal/Email.js +16 -0
- package/dist/internal/Email.js.map +1 -0
- package/dist/internal/Log.d.ts +32 -28
- package/dist/internal/Log.d.ts.map +1 -1
- package/dist/internal/Log.js +18 -11
- package/dist/internal/Log.js.map +1 -1
- package/dist/internal/MetricSink.d.ts +3 -0
- package/dist/internal/MetricSink.d.ts.map +1 -1
- package/dist/internal/MetricSink.js +67 -8
- package/dist/internal/MetricSink.js.map +1 -1
- package/dist/internal/Mpp.d.ts.map +1 -1
- package/dist/internal/Mpp.js +20 -25
- package/dist/internal/Mpp.js.map +1 -1
- package/dist/internal/OpenApi.d.ts +26 -3
- package/dist/internal/OpenApi.d.ts.map +1 -1
- package/dist/internal/OpenApi.js +25 -10
- package/dist/internal/OpenApi.js.map +1 -1
- package/dist/internal/Provider.d.ts +1 -1
- package/dist/internal/Provider.d.ts.map +1 -1
- package/dist/internal/RateLimit.d.ts.map +1 -1
- package/dist/internal/RateLimit.js +13 -1
- package/dist/internal/RateLimit.js.map +1 -1
- package/dist/internal/Store.d.ts +16 -0
- package/dist/internal/Store.d.ts.map +1 -1
- package/dist/internal/Store.js +59 -8
- package/dist/internal/Store.js.map +1 -1
- package/dist/internal/Tidx.d.ts +9 -2
- package/dist/internal/Tidx.d.ts.map +1 -1
- package/dist/internal/Tidx.js +78 -11
- package/dist/internal/Tidx.js.map +1 -1
- package/dist/internal/Timing.d.ts +26 -0
- package/dist/internal/Timing.d.ts.map +1 -1
- package/dist/internal/Timing.js +63 -5
- package/dist/internal/Timing.js.map +1 -1
- package/dist/internal/VercelOidc.d.ts +94 -0
- package/dist/internal/VercelOidc.d.ts.map +1 -0
- package/dist/internal/VercelOidc.js +128 -0
- package/dist/internal/VercelOidc.js.map +1 -0
- package/dist/internal/VerifiedTokens.d.ts +2 -8
- package/dist/internal/VerifiedTokens.d.ts.map +1 -1
- package/dist/internal/VerifiedTokens.js +24 -24
- package/dist/internal/VerifiedTokens.js.map +1 -1
- package/dist/internal/Viem.d.ts +30 -2067
- package/dist/internal/Viem.d.ts.map +1 -1
- package/dist/internal/Viem.js +167 -17
- package/dist/internal/Viem.js.map +1 -1
- package/dist/internal/Webhooks.d.ts +12 -4
- package/dist/internal/Webhooks.d.ts.map +1 -1
- package/dist/internal/Webhooks.js +9 -3
- package/dist/internal/Webhooks.js.map +1 -1
- package/dist/internal/ZoneRpc.d.ts +7 -0
- package/dist/internal/ZoneRpc.d.ts.map +1 -0
- package/dist/internal/ZoneRpc.js +49 -0
- package/dist/internal/ZoneRpc.js.map +1 -0
- package/dist/internal/index.d.ts +3 -1
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +3 -1
- package/dist/internal/index.js.map +1 -1
- package/dist/internal/rewards/Audit.d.ts +88 -0
- package/dist/internal/rewards/Audit.d.ts.map +1 -0
- package/dist/internal/rewards/Audit.js +134 -0
- package/dist/internal/rewards/Audit.js.map +1 -0
- package/dist/internal/rewards/Campaigns.d.ts +572 -0
- package/dist/internal/rewards/Campaigns.d.ts.map +1 -0
- package/dist/internal/rewards/Campaigns.js +531 -0
- package/dist/internal/rewards/Campaigns.js.map +1 -0
- package/dist/internal/rewards/Credits.d.ts +76 -0
- package/dist/internal/rewards/Credits.d.ts.map +1 -0
- package/dist/internal/rewards/Credits.js +95 -0
- package/dist/internal/rewards/Credits.js.map +1 -0
- package/dist/internal/rewards/Events.d.ts +100 -0
- package/dist/internal/rewards/Events.d.ts.map +1 -0
- package/dist/internal/rewards/Events.js +340 -0
- package/dist/internal/rewards/Events.js.map +1 -0
- package/dist/internal/rewards/Projection.d.ts +237 -0
- package/dist/internal/rewards/Projection.d.ts.map +1 -0
- package/dist/internal/rewards/Projection.js +616 -0
- package/dist/internal/rewards/Projection.js.map +1 -0
- package/dist/internal/rewards/Rates.d.ts +44 -0
- package/dist/internal/rewards/Rates.d.ts.map +1 -0
- package/dist/internal/rewards/Rates.js +60 -0
- package/dist/internal/rewards/Rates.js.map +1 -0
- package/dist/internal/rewards/Read.d.ts +158 -0
- package/dist/internal/rewards/Read.d.ts.map +1 -0
- package/dist/internal/rewards/Read.js +292 -0
- package/dist/internal/rewards/Read.js.map +1 -0
- package/dist/internal/rewards/Runner.d.ts +164 -0
- package/dist/internal/rewards/Runner.d.ts.map +1 -0
- package/dist/internal/rewards/Runner.js +1778 -0
- package/dist/internal/rewards/Runner.js.map +1 -0
- package/dist/internal/rewards/Signer.d.ts +224 -0
- package/dist/internal/rewards/Signer.d.ts.map +1 -0
- package/dist/internal/rewards/Signer.js +135 -0
- package/dist/internal/rewards/Signer.js.map +1 -0
- package/dist/internal/rewards/index.d.ts +15 -0
- package/dist/internal/rewards/index.d.ts.map +1 -0
- package/dist/internal/rewards/index.js +15 -0
- package/dist/internal/rewards/index.js.map +1 -0
- package/dist/internal/{funding → routes}/Action.d.ts +12 -1
- package/dist/internal/routes/Action.d.ts.map +1 -0
- package/dist/internal/{funding → routes}/Action.js +17 -9
- package/dist/internal/routes/Action.js.map +1 -0
- package/dist/internal/{funding → routes}/Catalog.d.ts +20 -20
- package/dist/internal/routes/Catalog.d.ts.map +1 -0
- package/dist/internal/{funding → routes}/Catalog.js +29 -29
- package/dist/internal/routes/Catalog.js.map +1 -0
- package/dist/internal/{funding → routes}/Chain.d.ts +10 -10
- package/dist/internal/routes/Chain.d.ts.map +1 -0
- package/dist/internal/{funding → routes}/Chain.js +3 -3
- package/dist/internal/routes/Chain.js.map +1 -0
- package/dist/internal/{funding → routes}/Deposit.d.ts +45 -23
- package/dist/internal/routes/Deposit.d.ts.map +1 -0
- package/dist/internal/{funding → routes}/Deposit.js +60 -27
- package/dist/internal/routes/Deposit.js.map +1 -0
- package/dist/internal/{funding → routes}/DepositAddress.d.ts +56 -27
- package/dist/internal/routes/DepositAddress.d.ts.map +1 -0
- package/dist/internal/{funding → routes}/DepositAddress.js +95 -33
- package/dist/internal/routes/DepositAddress.js.map +1 -0
- package/dist/internal/{funding → routes}/DepositWebhook.d.ts +4 -4
- package/dist/internal/routes/DepositWebhook.d.ts.map +1 -0
- package/dist/internal/{funding → routes}/DepositWebhook.js +4 -4
- package/dist/internal/routes/DepositWebhook.js.map +1 -0
- package/dist/internal/routes/Economics.d.ts +25 -0
- package/dist/internal/routes/Economics.d.ts.map +1 -0
- package/dist/internal/routes/Economics.js +28 -0
- package/dist/internal/routes/Economics.js.map +1 -0
- package/dist/internal/{funding → routes}/Evidence.d.ts +3 -3
- package/dist/internal/routes/Evidence.d.ts.map +1 -0
- package/dist/internal/{funding → routes}/Evidence.js +7 -7
- package/dist/internal/routes/Evidence.js.map +1 -0
- package/dist/internal/routes/Observability.d.ts +154 -0
- package/dist/internal/routes/Observability.d.ts.map +1 -0
- package/dist/internal/routes/Observability.js +389 -0
- package/dist/internal/routes/Observability.js.map +1 -0
- package/dist/internal/{funding → routes}/Provider.d.ts +311 -105
- package/dist/internal/routes/Provider.d.ts.map +1 -0
- package/dist/internal/{funding → routes}/Provider.js +229 -98
- package/dist/internal/routes/Provider.js.map +1 -0
- package/dist/internal/{funding → routes}/Reconciliation.d.ts +11 -6
- package/dist/internal/routes/Reconciliation.d.ts.map +1 -0
- package/dist/internal/{funding → routes}/Reconciliation.js +408 -158
- package/dist/internal/routes/Reconciliation.js.map +1 -0
- package/dist/internal/routes/Route.d.ts +21 -0
- package/dist/internal/routes/Route.d.ts.map +1 -0
- package/dist/internal/{funding → routes}/Route.js +1 -1
- package/dist/internal/routes/Route.js.map +1 -0
- package/dist/internal/{funding → routes}/SourceObservation.d.ts +6 -6
- package/dist/internal/routes/SourceObservation.d.ts.map +1 -0
- package/dist/internal/{funding → routes}/SourceObservation.js +8 -8
- package/dist/internal/routes/SourceObservation.js.map +1 -0
- package/dist/internal/{funding → routes}/Subsidy.d.ts +48 -3
- package/dist/internal/routes/Subsidy.d.ts.map +1 -0
- package/dist/internal/{funding → routes}/Subsidy.js +43 -3
- package/dist/internal/routes/Subsidy.js.map +1 -0
- package/dist/internal/{funding → routes}/Token.d.ts +4 -4
- package/dist/internal/routes/Token.d.ts.map +1 -0
- package/dist/internal/{funding → routes}/Token.js +3 -3
- package/dist/internal/routes/Token.js.map +1 -0
- package/dist/internal/{funding → routes}/Transfer.d.ts +241 -46
- package/dist/internal/routes/Transfer.d.ts.map +1 -0
- package/dist/internal/routes/Transfer.js +728 -0
- package/dist/internal/routes/Transfer.js.map +1 -0
- package/dist/internal/{funding → routes}/TransferReconciliation.d.ts +13 -10
- package/dist/internal/routes/TransferReconciliation.d.ts.map +1 -0
- package/dist/internal/routes/TransferReconciliation.js +422 -0
- package/dist/internal/routes/TransferReconciliation.js.map +1 -0
- package/dist/internal/routes/TransferSubsidy.d.ts +201 -0
- package/dist/internal/routes/TransferSubsidy.d.ts.map +1 -0
- package/dist/internal/routes/TransferSubsidy.js +256 -0
- package/dist/internal/routes/TransferSubsidy.js.map +1 -0
- package/dist/internal/{funding → routes}/TransferWebhook.d.ts +5 -5
- package/dist/internal/routes/TransferWebhook.d.ts.map +1 -0
- package/dist/internal/{funding → routes}/TransferWebhook.js +3 -3
- package/dist/internal/routes/TransferWebhook.js.map +1 -0
- package/dist/internal/{funding → routes}/index.d.ts +2 -0
- package/dist/internal/routes/index.d.ts.map +1 -0
- package/dist/internal/{funding → routes}/index.js +2 -0
- package/dist/internal/routes/index.js.map +1 -0
- package/dist/internal/{funding → routes}/providers/across.d.ts +4 -4
- package/dist/internal/routes/providers/across.d.ts.map +1 -0
- package/dist/internal/{funding → routes}/providers/across.js +13 -13
- package/dist/internal/routes/providers/across.js.map +1 -0
- package/dist/internal/{funding → routes}/providers/relay.d.ts +21 -12
- package/dist/internal/routes/providers/relay.d.ts.map +1 -0
- package/dist/internal/{funding → routes}/providers/relay.js +194 -110
- package/dist/internal/routes/providers/relay.js.map +1 -0
- package/dist/internal/{funding → routes}/providers/rhino.d.ts +4 -4
- package/dist/internal/routes/providers/rhino.d.ts.map +1 -0
- package/dist/internal/{funding → routes}/providers/rhino.js +10 -10
- package/dist/internal/routes/providers/rhino.js.map +1 -0
- package/dist/internal/{funding → routes}/providers/squid.d.ts +4 -4
- package/dist/internal/routes/providers/squid.d.ts.map +1 -0
- package/dist/internal/{funding → routes}/providers/squid.js +14 -14
- package/dist/internal/routes/providers/squid.js.map +1 -0
- package/dist/internal/{funding → routes}/providers/stargate.d.ts +46 -18
- package/dist/internal/routes/providers/stargate.d.ts.map +1 -0
- package/dist/internal/routes/providers/stargate.js +898 -0
- package/dist/internal/routes/providers/stargate.js.map +1 -0
- package/dist/internal/{funding → routes}/providers/symbiosis.d.ts +4 -4
- package/dist/internal/routes/providers/symbiosis.d.ts.map +1 -0
- package/dist/internal/{funding → routes}/providers/symbiosis.js +30 -20
- package/dist/internal/routes/providers/symbiosis.js.map +1 -0
- package/dist/internal/webhookDestinations/slack.js +12 -12
- package/dist/internal/webhookDestinations/slack.js.map +1 -1
- package/dist/providers/index.d.ts +12 -12
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +12 -12
- package/dist/providers/index.js.map +1 -1
- package/dist/server.d.ts +19 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +19 -0
- package/dist/server.js.map +1 -0
- package/package.json +15 -4
- package/src/ApiKey.ts +1 -1
- package/src/ApiKeys.test.ts +1033 -3
- package/src/ApiKeys.ts +489 -48
- package/src/App.test-d.ts +1 -1
- package/src/App.test.ts +216 -25
- package/src/App.ts +334 -31
- package/src/Assets.test.ts +2 -2
- package/src/Assets.ts +3 -1
- package/src/Auth.test-d.ts +1 -1
- package/src/Client.test-d.ts +539 -110
- package/src/Handler.ts +0 -1
- package/src/OpenApi.contract.test.ts +195 -0
- package/src/Scope.ts +17 -5
- package/src/Webhooks.ts +1 -1
- package/src/admin/App.test.ts +30 -2
- package/src/admin/App.ts +42 -6
- package/src/admin/apps/api-keys.test.ts +267 -5
- package/src/admin/apps/api-keys.ts +100 -50
- package/src/admin/apps/earn-vaults.test.ts +158 -0
- package/src/admin/apps/earn-vaults.ts +679 -6
- package/src/admin/apps/organizations.test.ts +306 -17
- package/src/admin/apps/organizations.ts +299 -24
- package/src/admin/apps/routes-support.test.ts +200 -0
- package/src/admin/apps/routes-support.ts +201 -0
- package/src/admin/apps/routes.test.ts +450 -0
- package/src/admin/apps/routes.ts +896 -0
- package/src/admin/apps/scopes.test.ts +15 -5
- package/src/admin/apps/subsidies.test.ts +306 -0
- package/src/admin/apps/subsidies.ts +457 -0
- package/src/admin/apps/verified-tokens.test.ts +8 -3
- package/src/admin/apps/verified-tokens.ts +3 -3
- package/src/admin/ui/plugin.ts +1 -2
- package/src/admin/ui/src/components/RewardCampaignForm.tsx +1009 -0
- package/src/admin/ui/src/lib/config.ts +11 -2
- package/src/admin/ui/src/lib/earn-vaults.ts +2 -2
- package/src/admin/ui/src/lib/organizations.test.ts +83 -50
- package/src/admin/ui/src/lib/organizations.ts +106 -25
- package/src/admin/ui/src/lib/query-client.ts +1 -1
- package/src/admin/ui/src/lib/reward-campaigns.ts +136 -0
- package/src/admin/ui/src/lib/routes.test.ts +88 -0
- package/src/admin/ui/src/lib/routes.ts +304 -0
- package/src/admin/ui/src/lib/subsidies.ts +75 -0
- package/src/admin/ui/src/routeTree.gen.ts +63 -0
- package/src/admin/ui/src/routes/__root.tsx +12 -1
- package/src/admin/ui/src/routes/api-keys.tsx +196 -84
- package/src/admin/ui/src/routes/earn.vaults_.$vaultId.edit.tsx +105 -70
- package/src/admin/ui/src/routes/organizations_.$orgId.tsx +239 -23
- package/src/admin/ui/src/routes/routes.tsx +1615 -0
- package/src/admin/ui/src/routes/routes_.support.tsx +248 -0
- package/src/admin/ui/src/routes/subsidies.tsx +799 -0
- package/src/admin/ui.generated.ts +144 -69
- package/src/analytics/Analytics.test.ts +108 -18
- package/src/analytics/Analytics.ts +38 -6
- package/src/analytics/tables/requestEvents.test.ts +100 -0
- package/src/analytics/tables/requestEvents.ts +10 -1
- package/src/analytics/tables/routesLifecycleIntervals.ts +140 -0
- package/src/apps/Zones.test.ts +15 -0
- package/src/apps/Zones.ts +52 -5
- package/src/apps/composition.test.ts +68 -60
- package/src/apps/data/App.test.ts +154 -2
- package/src/apps/data/App.ts +88 -28
- package/src/apps/data/routes/activities.test.ts +419 -7
- package/src/apps/data/routes/activities.ts +683 -216
- package/src/apps/data/routes/balances.test.ts +2 -2
- package/src/apps/data/routes/blocks.test.ts +46 -1
- package/src/apps/data/routes/coingecko.test.ts +419 -1
- package/src/apps/data/routes/coingecko.ts +309 -253
- package/src/apps/data/routes/earn.discovery.test.ts +274 -0
- package/src/apps/data/routes/earn.test.ts +1294 -17
- package/src/apps/data/routes/earn.ts +1838 -321
- package/src/apps/data/routes/exchanges.test.ts +12 -15
- package/src/apps/data/routes/fee-amm.test.ts +224 -0
- package/src/apps/data/routes/fee-amm.ts +191 -160
- package/src/apps/data/routes/indexer.test.ts +389 -0
- package/src/apps/data/routes/indexer.ts +165 -69
- package/src/apps/data/routes/receipts.test.ts +1 -1
- package/src/apps/data/routes/rpc.test.ts +352 -8
- package/src/apps/data/routes/rpc.ts +407 -219
- package/src/apps/data/routes/rpc.zone.test.ts +340 -0
- package/src/apps/data/routes/tokenlist.test.ts +285 -0
- package/src/apps/data/routes/tokenlist.ts +173 -88
- package/src/apps/data/routes/tokens.test.ts +93 -3
- package/src/apps/data/routes/tokens.ts +39 -23
- package/src/apps/data/routes/transactions.test.ts +1 -1
- package/src/apps/data/routes/transfers.test.ts +56 -24
- package/src/apps/data/routes/transfers.ts +19 -8
- package/src/apps/data/routes/valuation.test.ts +1 -1
- package/src/apps/data/routes/verified-tokens.test.ts +345 -0
- package/src/apps/data/routes/verified-tokens.ts +99 -29
- package/src/apps/data/routes/webhooks.test.ts +645 -83
- package/src/apps/data/routes/webhooks.ts +1089 -857
- package/src/apps/data/routes/zones.test.ts +148 -71
- package/src/apps/data/routes/zones.ts +71 -55
- package/src/apps/docs/reference.ts +2 -2
- package/src/apps/index.ts +1 -1
- package/src/apps/management/App.ts +9 -51
- package/src/apps/management/Billing.test.ts +375 -14
- package/src/apps/management/Billing.ts +265 -29
- package/src/apps/management/routes/api-keys.test.ts +269 -7
- package/src/apps/management/routes/api-keys.ts +61 -20
- package/src/apps/management/routes/billing.test.ts +477 -15
- package/src/apps/management/routes/billing.ts +377 -50
- package/src/apps/management/routes/faucet.test.ts +1 -1
- package/src/apps/management/routes/faucet.ts +1 -1
- package/src/apps/management/routes/invitations.test.ts +270 -3
- package/src/apps/management/routes/invitations.ts +185 -15
- package/src/apps/management/routes/invoicePreview.test.ts +549 -0
- package/src/apps/management/routes/me.test.ts +246 -5
- package/src/apps/management/routes/me.ts +7 -2
- package/src/apps/management/routes/members.test.ts +108 -0
- package/src/apps/management/routes/orgs.test.ts +191 -3
- package/src/apps/management/routes/orgs.ts +58 -11
- package/src/apps/management/routes/projects.test.ts +33 -3
- package/src/apps/management/routes/projects.ts +6 -1
- package/src/apps/management/routes/scopes.test.ts +30 -2
- package/src/apps/management/routes/scopes.ts +4 -1
- package/src/apps/management/routes/usage.test.ts +32 -0
- package/src/apps/management/routes/usage.ts +22 -4
- package/src/apps/management/routes/webhooks.test.ts +49 -26
- package/src/apps/management/routes/webhooks.ts +78 -22
- package/src/apps/mcp.test.ts +6 -6
- package/src/apps/mcp.ts +7 -7
- package/src/apps/mpp/App.ts +37 -7
- package/src/apps/mpp/Mpp.test.ts +49 -8
- package/src/apps/mpp/Mpp.zone.test.ts +219 -0
- package/src/apps/mpp/internal/Adapter.ts +34 -0
- package/src/apps/mpp/internal/Policy.test.ts +86 -31
- package/src/apps/mpp/internal/Policy.ts +49 -7
- package/src/apps/mpp/internal/Sponsorship.test.ts +207 -0
- package/src/apps/mpp/internal/Sponsorship.ts +9 -3
- package/src/apps/relay/App.test.ts +839 -64
- package/src/apps/relay/App.ts +148 -81
- package/src/apps/relay/App.zone-billing.test.ts +257 -0
- package/src/apps/relay/Sponsorships.test.ts +67 -4
- package/src/apps/relay/Sponsorships.ts +233 -32
- package/src/apps/relay/Sponsorships.zone.test.ts +102 -0
- package/src/apps/{funding → routes}/Access.ts +6 -6
- package/src/apps/routes/App.test.ts +165 -0
- package/src/apps/routes/App.ts +96 -0
- package/src/apps/{funding → routes}/Inventory.ts +2 -2
- package/src/apps/routes/index.ts +1 -0
- package/src/apps/{funding → routes}/routes/chains.test.ts +66 -66
- package/src/apps/{funding → routes}/routes/chains.ts +23 -23
- package/src/apps/{funding → routes}/routes/deposit-addresses.test.ts +1358 -458
- package/src/apps/{funding → routes}/routes/deposit-addresses.ts +566 -246
- package/src/apps/{funding → routes}/routes/providers.test.ts +27 -33
- package/src/apps/{funding → routes}/routes/providers.ts +23 -23
- package/src/apps/{funding → routes}/routes/quotes.test.ts +298 -41
- package/src/apps/{funding → routes}/routes/quotes.ts +57 -40
- package/src/apps/{funding → routes}/routes/transfers.test.ts +1100 -353
- package/src/apps/routes/routes/transfers.ts +1332 -0
- package/src/cli/Cli.test.ts +1 -1
- package/src/cli/commands/admin.test.ts +748 -1
- package/src/cli/commands/admin.ts +278 -0
- package/src/cloudflare.ts +52 -47
- package/src/db/Db.test.ts +245 -2
- package/src/db/Db.ts +1476 -34
- package/src/db/Schema.ts +729 -228
- package/src/db/tables/apiKeyAdmissionBootstraps.ts +4 -0
- package/src/db/tables/apiKeyAdmissions.ts +218 -0
- package/src/db/tables/apiKeyOwnerAdmissions.ts +4 -0
- package/src/db/tables/apiKeyOwnerTombstones.ts +81 -0
- package/src/db/tables/apiKeyRevocations.test.ts +74 -0
- package/src/db/tables/apiKeyRevocations.ts +121 -0
- package/src/db/tables/authAccounts.ts +105 -0
- package/src/db/tables/authSessions.ts +13 -0
- package/src/db/tables/authVerifications.ts +13 -0
- package/src/db/tables/invitations.ts +142 -45
- package/src/db/tables/memberships.ts +79 -3
- package/src/db/tables/organizations.test.ts +690 -13
- package/src/db/tables/organizations.ts +139 -55
- package/src/db/tables/projects.test.ts +16 -0
- package/src/db/tables/projects.ts +13 -3
- package/src/db/tables/requestUsage.ts +4 -1
- package/src/db/tables/rewardAccounts.ts +178 -0
- package/src/db/tables/rewardCampaignDistributors.ts +68 -0
- package/src/db/tables/rewardCampaigns.test.ts +1388 -0
- package/src/db/tables/rewardCampaigns.ts +647 -0
- package/src/db/tables/rewardCredits.ts +110 -0
- package/src/db/tables/rewardEligibilityAssociations.test.ts +271 -0
- package/src/db/tables/rewardEligibilityAssociations.ts +184 -0
- package/src/db/tables/rewardRuns.ts +514 -0
- package/src/db/tables/rewardTransactionAttempts.ts +272 -0
- package/src/db/tables/routesCatalog.test.ts +185 -0
- package/src/db/tables/routesCatalog.ts +163 -0
- package/src/db/tables/routesDepositAddresses.test.ts +1048 -0
- package/src/db/tables/routesDepositAddresses.ts +1008 -0
- package/src/db/tables/{fundingDepositRequestObservations.test.ts → routesDepositRequestObservations.test.ts} +13 -14
- package/src/db/tables/{fundingDepositRequestObservations.ts → routesDepositRequestObservations.ts} +10 -10
- package/src/db/tables/{fundingDeposits.test.ts → routesDeposits.test.ts} +316 -46
- package/src/db/tables/routesDeposits.ts +1062 -0
- package/src/db/tables/routesIdempotency.test.ts +796 -0
- package/src/db/tables/routesIdempotency.ts +496 -0
- package/src/db/tables/routesSubsidies.test.ts +120 -0
- package/src/db/tables/routesSubsidies.ts +71 -0
- package/src/db/tables/{fundingTransferEvents.ts → routesTransferEvents.ts} +4 -4
- package/src/db/tables/routesTransferSubsidies.ts +38 -0
- package/src/db/tables/routesTransferSubsidyNonces.ts +124 -0
- package/src/db/tables/routesTransferTransactions.test.ts +195 -0
- package/src/db/tables/{fundingTransferTransactions.ts → routesTransferTransactions.ts} +63 -18
- package/src/db/tables/routesTransfers.test.ts +338 -0
- package/src/db/tables/routesTransfers.ts +662 -0
- package/src/db/tables/sponsoredTransactions.test.ts +269 -45
- package/src/db/tables/sponsoredTransactions.ts +225 -65
- package/src/db/tables/sponsorshipAttributions.test.ts +160 -0
- package/src/db/tables/sponsorshipAttributions.ts +168 -0
- package/src/db/tables/sponsorshipReconciliationCursors.test.ts +115 -0
- package/src/db/tables/sponsorshipReconciliationCursors.ts +67 -0
- package/src/db/tables/stripeCustomers.test.ts +85 -0
- package/src/db/tables/stripeCustomers.ts +24 -0
- package/src/db/tables/users.test.ts +129 -0
- package/src/db/tables/users.ts +49 -3
- package/src/db/tables/webhookQueueEvents.ts +8 -1
- package/src/db/tables/webhookSubscriptions.ts +95 -20
- package/src/handlers/internal/executionError.ts +3 -4
- package/src/handlers/internal/multisig.ts +13 -816
- package/src/handlers/internal/sponsorship.ts +26 -10
- package/src/handlers/relay.test-d.ts +9 -1
- package/src/handlers/relay.test.ts +823 -3
- package/src/handlers/relay.ts +127 -107
- package/src/index.test-d.ts +7 -0
- package/src/index.ts +0 -17
- package/src/internal/AssetResponse.ts +26 -0
- package/src/internal/Auth.test.ts +1564 -9
- package/src/internal/Auth.ts +833 -239
- package/src/internal/BetterAuth.test.ts +242 -0
- package/src/internal/BetterAuth.ts +349 -0
- package/src/internal/Cache.ts +2 -0
- package/src/internal/EarnRates.test.ts +17 -0
- package/src/internal/EarnRates.ts +124 -31
- package/src/internal/EarnSharePrices.test.ts +182 -0
- package/src/internal/EarnSharePrices.ts +313 -0
- package/src/internal/EarnVaults.test.ts +21 -0
- package/src/internal/EarnVaults.ts +33 -11
- package/src/internal/Email.ts +54 -0
- package/src/internal/Idempotency.test.ts +2 -2
- package/src/internal/Log.test-d.ts +8 -7
- package/src/internal/Log.test.ts +114 -16
- package/src/internal/Log.ts +53 -40
- package/src/internal/MetricSink.test.ts +313 -26
- package/src/internal/MetricSink.ts +73 -8
- package/src/internal/Mpp.test.ts +31 -0
- package/src/internal/Mpp.ts +21 -24
- package/src/internal/OpenApi.ts +45 -12
- package/src/internal/Provider.test.ts +1 -1
- package/src/internal/Provider.ts +1 -1
- package/src/internal/RateLimit.test.ts +21 -3
- package/src/internal/RateLimit.ts +12 -1
- package/src/internal/Store.test-d.ts +1 -1
- package/src/internal/Store.test.ts +151 -8
- package/src/internal/Store.ts +76 -9
- package/src/internal/Tidx.test.ts +59 -8
- package/src/internal/Tidx.ts +86 -14
- package/src/internal/Timing.test.ts +77 -0
- package/src/internal/Timing.ts +83 -6
- package/src/internal/VercelOidc.test.ts +188 -0
- package/src/internal/VercelOidc.ts +193 -0
- package/src/internal/VerifiedTokens.test.ts +60 -6
- package/src/internal/VerifiedTokens.ts +21 -24
- package/src/internal/Viem.batch.test.ts +80 -0
- package/src/internal/Viem.test.ts +201 -2
- package/src/internal/Viem.ts +221 -25
- package/src/internal/Webhooks.test.ts +85 -6
- package/src/internal/Webhooks.ts +22 -4
- package/src/internal/ZoneRpc.ts +50 -0
- package/src/internal/exchange/Provider.test.ts +2 -2
- package/src/internal/index.test.ts +3 -1
- package/src/internal/index.ts +3 -1
- package/src/internal/rewards/Audit.ts +231 -0
- package/src/internal/rewards/Campaigns.test.ts +1234 -0
- package/src/internal/rewards/Campaigns.ts +1205 -0
- package/src/internal/rewards/Credits.ts +165 -0
- package/src/internal/rewards/Events.local.test.ts +113 -0
- package/src/internal/rewards/Events.test.ts +152 -0
- package/src/internal/rewards/Events.ts +468 -0
- package/src/internal/rewards/Projection.test.ts +681 -0
- package/src/internal/rewards/Projection.ts +987 -0
- package/src/internal/rewards/Rates.test.ts +171 -0
- package/src/internal/rewards/Rates.ts +110 -0
- package/src/internal/rewards/Read.test.ts +501 -0
- package/src/internal/rewards/Read.ts +476 -0
- package/src/internal/rewards/Runner.checkpoint.test.ts +80 -0
- package/src/internal/rewards/Runner.states.test.ts +188 -0
- package/src/internal/rewards/Runner.test.ts +90 -0
- package/src/internal/rewards/Runner.ts +2229 -0
- package/src/internal/rewards/Signer.test.ts +79 -0
- package/src/internal/rewards/Signer.ts +180 -0
- package/src/internal/rewards/index.ts +14 -0
- package/src/internal/{funding → routes}/Action.test.ts +63 -14
- package/src/internal/{funding → routes}/Action.ts +35 -8
- package/src/internal/{funding → routes}/Catalog.test.ts +3 -3
- package/src/internal/{funding → routes}/Catalog.ts +39 -39
- package/src/internal/{funding → routes}/Chain.test.ts +1 -1
- package/src/internal/{funding → routes}/Chain.ts +11 -11
- package/src/internal/{funding → routes}/Deposit.test.ts +1 -1
- package/src/internal/{funding → routes}/Deposit.ts +93 -45
- package/src/internal/{funding → routes}/DepositAddress.test.ts +11 -3
- package/src/internal/{funding → routes}/DepositAddress.ts +132 -53
- package/src/internal/{funding → routes}/DepositWebhook.test.ts +61 -18
- package/src/internal/{funding → routes}/DepositWebhook.ts +9 -9
- package/src/internal/routes/Economics.test.ts +42 -0
- package/src/internal/routes/Economics.ts +47 -0
- package/src/internal/{funding → routes}/Evidence.test.ts +6 -6
- package/src/internal/{funding → routes}/Evidence.ts +9 -9
- package/src/internal/routes/Observability.test.ts +636 -0
- package/src/internal/routes/Observability.ts +668 -0
- package/src/internal/{funding → routes}/Provider.test.ts +489 -153
- package/src/internal/{funding → routes}/Provider.ts +489 -186
- package/src/internal/{funding → routes}/Reconciliation.local.test.ts +84 -12
- package/src/internal/{funding → routes}/Reconciliation.test.ts +1213 -279
- package/src/internal/{funding → routes}/Reconciliation.ts +501 -203
- package/src/internal/routes/Reconciliation.unattributed.test.ts +250 -0
- package/src/internal/{funding → routes}/Route.ts +7 -7
- package/src/internal/{funding → routes}/SourceObservation.test.ts +13 -14
- package/src/internal/{funding → routes}/SourceObservation.ts +13 -13
- package/src/internal/{funding → routes}/Subsidy.local.test.ts +7 -7
- package/src/internal/routes/Subsidy.test.ts +169 -0
- package/src/internal/{funding → routes}/Subsidy.ts +86 -6
- package/src/internal/{funding → routes}/Token.test.ts +1 -1
- package/src/internal/{funding → routes}/Token.ts +5 -5
- package/src/internal/routes/Transfer.test.ts +1199 -0
- package/src/internal/routes/Transfer.ts +1206 -0
- package/src/internal/routes/TransferReconciliation.test.ts +731 -0
- package/src/internal/routes/TransferReconciliation.ts +527 -0
- package/src/internal/routes/TransferSubsidy.test.ts +172 -0
- package/src/internal/routes/TransferSubsidy.ts +447 -0
- package/src/internal/{funding → routes}/TransferWebhook.test.ts +13 -13
- package/src/internal/{funding → routes}/TransferWebhook.ts +7 -7
- package/src/internal/{funding → routes}/index.test.ts +5 -3
- package/src/internal/{funding → routes}/index.ts +2 -0
- package/src/internal/{funding → routes}/providers/across.ts +16 -16
- package/src/internal/{funding → routes}/providers/index.test.ts +157 -44
- package/src/internal/{funding → routes}/providers/relay.test.ts +165 -32
- package/src/internal/{funding → routes}/providers/relay.ts +228 -121
- package/src/internal/{funding → routes}/providers/rhino.ts +16 -16
- package/src/internal/{funding → routes}/providers/squid.ts +17 -17
- package/src/internal/routes/providers/stargate.test.ts +728 -0
- package/src/internal/routes/providers/stargate.ts +1276 -0
- package/src/internal/{funding → routes}/providers/symbiosis.ts +64 -24
- package/src/internal/webhookDestinations/slack.ts +10 -10
- package/src/providers/index.ts +13 -13
- package/src/server.ts +18 -0
- package/dist/apps/funding/Access.d.ts.map +0 -1
- package/dist/apps/funding/Access.js.map +0 -1
- package/dist/apps/funding/App.js.map +0 -1
- package/dist/apps/funding/Inventory.d.ts.map +0 -1
- package/dist/apps/funding/Inventory.js.map +0 -1
- package/dist/apps/funding/ProviderAttempts.d.ts +0 -5
- package/dist/apps/funding/ProviderAttempts.d.ts.map +0 -1
- package/dist/apps/funding/ProviderAttempts.js +0 -24
- package/dist/apps/funding/ProviderAttempts.js.map +0 -1
- package/dist/apps/funding/index.d.ts +0 -2
- package/dist/apps/funding/index.d.ts.map +0 -1
- package/dist/apps/funding/index.js +0 -2
- package/dist/apps/funding/index.js.map +0 -1
- package/dist/apps/funding/routes/chains.d.ts.map +0 -1
- package/dist/apps/funding/routes/chains.js.map +0 -1
- package/dist/apps/funding/routes/deposit-addresses.d.ts.map +0 -1
- package/dist/apps/funding/routes/deposit-addresses.js.map +0 -1
- package/dist/apps/funding/routes/providers.d.ts.map +0 -1
- package/dist/apps/funding/routes/providers.js +0 -57
- package/dist/apps/funding/routes/providers.js.map +0 -1
- package/dist/apps/funding/routes/quotes.d.ts.map +0 -1
- package/dist/apps/funding/routes/quotes.js.map +0 -1
- package/dist/apps/funding/routes/transfers.d.ts.map +0 -1
- package/dist/apps/funding/routes/transfers.js +0 -828
- package/dist/apps/funding/routes/transfers.js.map +0 -1
- package/dist/db/tables/fundingCatalog.d.ts +0 -58
- package/dist/db/tables/fundingCatalog.d.ts.map +0 -1
- package/dist/db/tables/fundingCatalog.js.map +0 -1
- package/dist/db/tables/fundingDepositAddresses.d.ts +0 -192
- package/dist/db/tables/fundingDepositAddresses.d.ts.map +0 -1
- package/dist/db/tables/fundingDepositAddresses.js +0 -235
- package/dist/db/tables/fundingDepositAddresses.js.map +0 -1
- package/dist/db/tables/fundingDepositRequestObservations.d.ts.map +0 -1
- package/dist/db/tables/fundingDepositRequestObservations.js.map +0 -1
- package/dist/db/tables/fundingDeposits.d.ts.map +0 -1
- package/dist/db/tables/fundingDeposits.js +0 -295
- package/dist/db/tables/fundingDeposits.js.map +0 -1
- package/dist/db/tables/fundingIdempotency.d.ts +0 -91
- package/dist/db/tables/fundingIdempotency.d.ts.map +0 -1
- package/dist/db/tables/fundingIdempotency.js +0 -103
- package/dist/db/tables/fundingIdempotency.js.map +0 -1
- package/dist/db/tables/fundingTransferEvents.d.ts.map +0 -1
- package/dist/db/tables/fundingTransferEvents.js.map +0 -1
- package/dist/db/tables/fundingTransferTransactions.d.ts +0 -53
- package/dist/db/tables/fundingTransferTransactions.d.ts.map +0 -1
- package/dist/db/tables/fundingTransferTransactions.js.map +0 -1
- package/dist/db/tables/fundingTransfers.d.ts +0 -99
- package/dist/db/tables/fundingTransfers.d.ts.map +0 -1
- package/dist/db/tables/fundingTransfers.js +0 -110
- package/dist/db/tables/fundingTransfers.js.map +0 -1
- package/dist/internal/funding/Action.d.ts.map +0 -1
- package/dist/internal/funding/Action.js.map +0 -1
- package/dist/internal/funding/Catalog.d.ts.map +0 -1
- package/dist/internal/funding/Catalog.js.map +0 -1
- package/dist/internal/funding/Chain.d.ts.map +0 -1
- package/dist/internal/funding/Chain.js.map +0 -1
- package/dist/internal/funding/Deposit.d.ts.map +0 -1
- package/dist/internal/funding/Deposit.js.map +0 -1
- package/dist/internal/funding/DepositAddress.d.ts.map +0 -1
- package/dist/internal/funding/DepositAddress.js.map +0 -1
- package/dist/internal/funding/DepositWebhook.d.ts.map +0 -1
- package/dist/internal/funding/DepositWebhook.js.map +0 -1
- package/dist/internal/funding/Evidence.d.ts.map +0 -1
- package/dist/internal/funding/Evidence.js.map +0 -1
- package/dist/internal/funding/Provider.d.ts.map +0 -1
- package/dist/internal/funding/Provider.js.map +0 -1
- package/dist/internal/funding/Reconciliation.d.ts.map +0 -1
- package/dist/internal/funding/Reconciliation.js.map +0 -1
- package/dist/internal/funding/Route.d.ts +0 -21
- package/dist/internal/funding/Route.d.ts.map +0 -1
- package/dist/internal/funding/Route.js.map +0 -1
- package/dist/internal/funding/SourceObservation.d.ts.map +0 -1
- package/dist/internal/funding/SourceObservation.js.map +0 -1
- package/dist/internal/funding/Subsidy.d.ts.map +0 -1
- package/dist/internal/funding/Subsidy.js.map +0 -1
- package/dist/internal/funding/Token.d.ts.map +0 -1
- package/dist/internal/funding/Token.js.map +0 -1
- package/dist/internal/funding/Transfer.d.ts.map +0 -1
- package/dist/internal/funding/Transfer.js +0 -462
- package/dist/internal/funding/Transfer.js.map +0 -1
- package/dist/internal/funding/TransferReconciliation.d.ts.map +0 -1
- package/dist/internal/funding/TransferReconciliation.js +0 -192
- package/dist/internal/funding/TransferReconciliation.js.map +0 -1
- package/dist/internal/funding/TransferWebhook.d.ts.map +0 -1
- package/dist/internal/funding/TransferWebhook.js.map +0 -1
- package/dist/internal/funding/index.d.ts.map +0 -1
- package/dist/internal/funding/index.js.map +0 -1
- package/dist/internal/funding/providers/across.d.ts.map +0 -1
- package/dist/internal/funding/providers/across.js.map +0 -1
- package/dist/internal/funding/providers/relay.d.ts.map +0 -1
- package/dist/internal/funding/providers/relay.js.map +0 -1
- package/dist/internal/funding/providers/rhino.d.ts.map +0 -1
- package/dist/internal/funding/providers/rhino.js.map +0 -1
- package/dist/internal/funding/providers/squid.d.ts.map +0 -1
- package/dist/internal/funding/providers/squid.js.map +0 -1
- package/dist/internal/funding/providers/stargate.d.ts.map +0 -1
- package/dist/internal/funding/providers/stargate.js +0 -579
- package/dist/internal/funding/providers/stargate.js.map +0 -1
- package/dist/internal/funding/providers/symbiosis.d.ts.map +0 -1
- package/dist/internal/funding/providers/symbiosis.js.map +0 -1
- package/src/apps/funding/App.test.ts +0 -78
- package/src/apps/funding/App.ts +0 -88
- package/src/apps/funding/ProviderAttempts.ts +0 -26
- package/src/apps/funding/index.ts +0 -1
- package/src/apps/funding/routes/transfers.ts +0 -987
- package/src/db/tables/fundingCatalog.test.ts +0 -122
- package/src/db/tables/fundingCatalog.ts +0 -163
- package/src/db/tables/fundingDepositAddresses.test.ts +0 -608
- package/src/db/tables/fundingDepositAddresses.ts +0 -443
- package/src/db/tables/fundingDeposits.ts +0 -572
- package/src/db/tables/fundingIdempotency.test.ts +0 -182
- package/src/db/tables/fundingIdempotency.ts +0 -180
- package/src/db/tables/fundingTransferTransactions.test.ts +0 -112
- package/src/db/tables/fundingTransfers.test.ts +0 -115
- package/src/db/tables/fundingTransfers.ts +0 -189
- package/src/handlers/internal/multisig.test.ts +0 -818
- package/src/internal/funding/Subsidy.test.ts +0 -75
- package/src/internal/funding/Transfer.test.ts +0 -524
- package/src/internal/funding/Transfer.ts +0 -762
- package/src/internal/funding/TransferReconciliation.test.ts +0 -147
- package/src/internal/funding/TransferReconciliation.ts +0 -264
- package/src/internal/funding/providers/stargate.test.ts +0 -328
- package/src/internal/funding/providers/stargate.ts +0 -856
- /package/dist/apps/{funding → routes}/Inventory.d.ts +0 -0
- /package/src/internal/{funding → routes}/Route.test.ts +0 -0
|
@@ -1,11 +1,19 @@
|
|
|
1
|
+
import { serve } from '@hono/node-server'
|
|
2
|
+
import * as Accounts from 'accounts/server'
|
|
1
3
|
import { Hono, type Context } from 'hono'
|
|
2
4
|
import { requestId } from 'hono/request-id'
|
|
3
|
-
import {
|
|
5
|
+
import { exportJWK, generateKeyPair } from 'jose'
|
|
6
|
+
import { Challenge, Receipt, Store as MppStore } from 'mppx'
|
|
4
7
|
import { Mppx, tempo } from 'mppx/hono'
|
|
5
|
-
import
|
|
6
|
-
import { RateLimit, Store } from 'tapimo'
|
|
7
|
-
import {
|
|
8
|
+
import { Session } from 'mppx/tempo'
|
|
9
|
+
import { RateLimit, Store } from 'tapimo/server'
|
|
10
|
+
import { createClient, custom, decodeFunctionData, isAddressEqual, maxUint256 } from 'viem'
|
|
11
|
+
import { getTransaction } from 'viem/actions'
|
|
12
|
+
import { Actions, Transaction } from 'viem/tempo'
|
|
13
|
+
import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts'
|
|
14
|
+
import * as App from '../App.js'
|
|
8
15
|
import * as ApiKey from '../ApiKey.js'
|
|
16
|
+
import * as Management from '../apps/management/App.js'
|
|
9
17
|
import type * as Db from '../db/Db.js'
|
|
10
18
|
import * as Memberships from '../db/tables/memberships.js'
|
|
11
19
|
import * as Organizations from '../db/tables/organizations.js'
|
|
@@ -1009,6 +1017,7 @@ describe('require', () => {
|
|
|
1009
1017
|
})
|
|
1010
1018
|
|
|
1011
1019
|
expect(response.status).toBe(200)
|
|
1020
|
+
expect(consume).toHaveBeenCalledTimes(1)
|
|
1012
1021
|
expect(consume).toHaveBeenCalledWith({
|
|
1013
1022
|
key: 'mpp:api_key:key_test',
|
|
1014
1023
|
limit: { limit: 100, period: 'minute' },
|
|
@@ -1310,8 +1319,8 @@ describe('require', () => {
|
|
|
1310
1319
|
})
|
|
1311
1320
|
})
|
|
1312
1321
|
|
|
1313
|
-
test('paid retry
|
|
1314
|
-
const mpp = createMppServer()
|
|
1322
|
+
test('paid retry settles the accepted voucher on-chain', async () => {
|
|
1323
|
+
const mpp = createMppServer({ settlementSchedule: { units: 1 } })
|
|
1315
1324
|
const app = createApp({
|
|
1316
1325
|
auth: {
|
|
1317
1326
|
apiKey: { resolve: TestApp.resolver({ keys: [{ ...key, token }] }) },
|
|
@@ -1340,9 +1349,150 @@ describe('require', () => {
|
|
|
1340
1349
|
})
|
|
1341
1350
|
// The client deposits the request amount when no deposit is suggested.
|
|
1342
1351
|
expect(state.deposit).toBe(1_000_000n)
|
|
1343
|
-
|
|
1344
|
-
expect(state.settled).toBe(0n)
|
|
1352
|
+
expect(state.settled).toBe(BigInt(receipt.acceptedCumulative))
|
|
1345
1353
|
expect(state.closeRequestedAt).toBe(0)
|
|
1354
|
+
|
|
1355
|
+
const transaction = await getTransaction(Tempo.client, { hash: receipt.txHash! })
|
|
1356
|
+
expect(transaction.nonceKey).toBe(maxUint256)
|
|
1357
|
+
expect(transaction.nonce).toBe(0)
|
|
1358
|
+
})
|
|
1359
|
+
|
|
1360
|
+
test('settles independent session channels concurrently', async () => {
|
|
1361
|
+
const mpp = createMppServer({ settlementSchedule: { units: 1 } })
|
|
1362
|
+
const app = createApp({
|
|
1363
|
+
auth: {
|
|
1364
|
+
apiKey: { resolve: TestApp.resolver({ keys: [{ ...key, token }] }) },
|
|
1365
|
+
mpp,
|
|
1366
|
+
rateLimit: overQuotaExceptMpp(),
|
|
1367
|
+
},
|
|
1368
|
+
policy: {
|
|
1369
|
+
mpp: { session: { amount: '1', description: 'Protected data' } },
|
|
1370
|
+
public: { rateLimit: { limit: 1, period: 'minute' } },
|
|
1371
|
+
},
|
|
1372
|
+
})
|
|
1373
|
+
|
|
1374
|
+
const responses = await Promise.all([
|
|
1375
|
+
createMppClient(app).fetch('http://tempo-api.test/protected'),
|
|
1376
|
+
createMppClient(app).fetch('http://tempo-api.test/protected'),
|
|
1377
|
+
])
|
|
1378
|
+
const receipts = responses.map(sessionReceipt)
|
|
1379
|
+
const transactions = await Promise.all(
|
|
1380
|
+
receipts.map((receipt) => getTransaction(Tempo.client, { hash: receipt.txHash! })),
|
|
1381
|
+
)
|
|
1382
|
+
|
|
1383
|
+
expect(responses.map((response) => response.status)).toEqual([200, 200])
|
|
1384
|
+
expect(receipts[0]!.channelId).not.toBe(receipts[1]!.channelId)
|
|
1385
|
+
expect(transactions.map((transaction) => [transaction.nonceKey, transaction.nonce])).toEqual([
|
|
1386
|
+
[maxUint256, 0],
|
|
1387
|
+
[maxUint256, 0],
|
|
1388
|
+
])
|
|
1389
|
+
})
|
|
1390
|
+
|
|
1391
|
+
test('rolls back the request charge when scheduled settlement fails', async () => {
|
|
1392
|
+
const failures: Session.Server.SessionSettlementFailureContext[] = []
|
|
1393
|
+
const store = MppStore.memory()
|
|
1394
|
+
const mpp = createMppServer({
|
|
1395
|
+
account: Tempo.accounts[3],
|
|
1396
|
+
onSessionSettlementFailure: (context) => {
|
|
1397
|
+
failures.push(context)
|
|
1398
|
+
},
|
|
1399
|
+
recipient: Tempo.accounts[3].address,
|
|
1400
|
+
settlementSchedule: { units: 1 },
|
|
1401
|
+
store,
|
|
1402
|
+
})
|
|
1403
|
+
const app = createApp({
|
|
1404
|
+
auth: {
|
|
1405
|
+
apiKey: { resolve: TestApp.resolver({ keys: [{ ...key, token }] }) },
|
|
1406
|
+
mpp,
|
|
1407
|
+
rateLimit: overQuotaExceptMpp(),
|
|
1408
|
+
},
|
|
1409
|
+
policy: {
|
|
1410
|
+
mpp: { session: { amount: '1', description: 'Protected data' } },
|
|
1411
|
+
public: { rateLimit: { limit: 1, period: 'minute' } },
|
|
1412
|
+
},
|
|
1413
|
+
})
|
|
1414
|
+
|
|
1415
|
+
const response = await createMppClient(app).fetch('http://tempo-api.test/protected')
|
|
1416
|
+
|
|
1417
|
+
expect(response.status).toBe(402)
|
|
1418
|
+
expect(failures.length).toBeGreaterThan(0)
|
|
1419
|
+
for (const failure of failures) {
|
|
1420
|
+
expect(failure).toMatchObject({
|
|
1421
|
+
chainId: Tempo.chain.id,
|
|
1422
|
+
outcome: 'failed',
|
|
1423
|
+
stage: 'settlement',
|
|
1424
|
+
trigger: 'scheduled',
|
|
1425
|
+
})
|
|
1426
|
+
expect(failure.error).toBeInstanceOf(Error)
|
|
1427
|
+
}
|
|
1428
|
+
const channels = Session.Server.ChannelStore.fromStore(store)
|
|
1429
|
+
for (const channelId of new Set(failures.map(({ channelId }) => channelId))) {
|
|
1430
|
+
const channel = await channels.getChannel(channelId)
|
|
1431
|
+
expect({ spent: channel?.spent, units: channel?.units }).toEqual({ spent: 0n, units: 0 })
|
|
1432
|
+
}
|
|
1433
|
+
})
|
|
1434
|
+
|
|
1435
|
+
test('leaves request charges untouched when settlement submission is ambiguous', async () => {
|
|
1436
|
+
const failures: Session.Server.SessionSettlementFailureContext[] = []
|
|
1437
|
+
const store = MppStore.memory()
|
|
1438
|
+
const client = createClient({
|
|
1439
|
+
account: Tempo.accounts[2],
|
|
1440
|
+
chain: Tempo.chain,
|
|
1441
|
+
transport: custom({
|
|
1442
|
+
request(parameters) {
|
|
1443
|
+
if (parameters.method === 'eth_sendRawTransaction') {
|
|
1444
|
+
const transaction = Transaction.deserialize(parameters.params[0] as `0x76${string}`)
|
|
1445
|
+
const isSettlement = transaction.calls.some(
|
|
1446
|
+
(call) =>
|
|
1447
|
+
call.to &&
|
|
1448
|
+
call.data &&
|
|
1449
|
+
isAddressEqual(call.to, Session.Precompile.Protocol.tip20ChannelEscrow) &&
|
|
1450
|
+
decodeFunctionData({ abi: Session.Precompile.escrowAbi, data: call.data })
|
|
1451
|
+
.functionName === 'settle',
|
|
1452
|
+
)
|
|
1453
|
+
if (isSettlement) throw new Error('settlement submission timed out')
|
|
1454
|
+
}
|
|
1455
|
+
return Tempo.client.request(parameters as never)
|
|
1456
|
+
},
|
|
1457
|
+
}),
|
|
1458
|
+
})
|
|
1459
|
+
const mpp = createMppServer({
|
|
1460
|
+
getClient: () => client,
|
|
1461
|
+
onSessionSettlementFailure: (context) => {
|
|
1462
|
+
failures.push(context)
|
|
1463
|
+
},
|
|
1464
|
+
settlementSchedule: { units: 1 },
|
|
1465
|
+
store,
|
|
1466
|
+
})
|
|
1467
|
+
const app = createApp({
|
|
1468
|
+
auth: {
|
|
1469
|
+
apiKey: { resolve: TestApp.resolver({ keys: [{ ...key, token }] }) },
|
|
1470
|
+
mpp,
|
|
1471
|
+
rateLimit: overQuotaExceptMpp(),
|
|
1472
|
+
},
|
|
1473
|
+
policy: {
|
|
1474
|
+
mpp: { session: { amount: '1', description: 'Protected data' } },
|
|
1475
|
+
public: { rateLimit: { limit: 1, period: 'minute' } },
|
|
1476
|
+
},
|
|
1477
|
+
})
|
|
1478
|
+
|
|
1479
|
+
const response = await createMppClient(app).fetch('http://tempo-api.test/protected')
|
|
1480
|
+
|
|
1481
|
+
expect(response.status).toBe(402)
|
|
1482
|
+
expect(failures.length).toBeGreaterThan(0)
|
|
1483
|
+
const channels = Session.Server.ChannelStore.fromStore(store)
|
|
1484
|
+
for (const failure of failures) {
|
|
1485
|
+
expect(failure).toMatchObject({
|
|
1486
|
+
chainId: Tempo.chain.id,
|
|
1487
|
+
outcome: 'ambiguous',
|
|
1488
|
+
stage: 'settlement',
|
|
1489
|
+
trigger: 'scheduled',
|
|
1490
|
+
})
|
|
1491
|
+
await expect(channels.getChannel(failure.channelId)).resolves.toMatchObject({
|
|
1492
|
+
spent: 0n,
|
|
1493
|
+
units: 0,
|
|
1494
|
+
})
|
|
1495
|
+
}
|
|
1346
1496
|
})
|
|
1347
1497
|
|
|
1348
1498
|
test('reuses the session channel across paid requests', async () => {
|
|
@@ -2224,6 +2374,7 @@ function createMppServer(options: createMppServer.Options = {}) {
|
|
|
2224
2374
|
return Mppx.create({
|
|
2225
2375
|
methods: [
|
|
2226
2376
|
tempo.session({
|
|
2377
|
+
account: Tempo.accounts[2],
|
|
2227
2378
|
bootstrap: false,
|
|
2228
2379
|
chainId: Tempo.chain.id,
|
|
2229
2380
|
currency: Tempo.currency,
|
|
@@ -2242,7 +2393,13 @@ function createMppServer(options: createMppServer.Options = {}) {
|
|
|
2242
2393
|
declare namespace createMppServer {
|
|
2243
2394
|
type Options = Pick<
|
|
2244
2395
|
NonNullable<Parameters<typeof tempo.session>[0]>,
|
|
2245
|
-
|
|
2396
|
+
| 'account'
|
|
2397
|
+
| 'chainId'
|
|
2398
|
+
| 'getClient'
|
|
2399
|
+
| 'onSessionSettlementFailure'
|
|
2400
|
+
| 'recipient'
|
|
2401
|
+
| 'settlementSchedule'
|
|
2402
|
+
| 'store'
|
|
2246
2403
|
>
|
|
2247
2404
|
}
|
|
2248
2405
|
|
|
@@ -2366,3 +2523,1401 @@ function normalizePaymentPayer(value: unknown) {
|
|
|
2366
2523
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
2367
2524
|
return !!value && typeof value === 'object' && !Array.isArray(value)
|
|
2368
2525
|
}
|
|
2526
|
+
|
|
2527
|
+
const origin = 'https://api.example.com'
|
|
2528
|
+
const secret = 'test-better-auth-secret-at-least-32-characters'
|
|
2529
|
+
|
|
2530
|
+
describe('Google sign-in', () => {
|
|
2531
|
+
test('resolves a Google session without an email sender', async () => {
|
|
2532
|
+
const db = TestApp.database()
|
|
2533
|
+
const app = TestApp.create({
|
|
2534
|
+
db,
|
|
2535
|
+
session: {
|
|
2536
|
+
google: {
|
|
2537
|
+
clientId: 'google-client-id',
|
|
2538
|
+
clientSecret: 'google-client-secret',
|
|
2539
|
+
},
|
|
2540
|
+
secret,
|
|
2541
|
+
wallet: { origin },
|
|
2542
|
+
},
|
|
2543
|
+
})
|
|
2544
|
+
const now = new Date()
|
|
2545
|
+
const sessionToken = 'google-session-token'
|
|
2546
|
+
|
|
2547
|
+
try {
|
|
2548
|
+
await db.kysely
|
|
2549
|
+
.insertInto('users')
|
|
2550
|
+
.values({
|
|
2551
|
+
address: null,
|
|
2552
|
+
createdAt: now.toISOString(),
|
|
2553
|
+
email: 'developer@example.com',
|
|
2554
|
+
emailVerified: true,
|
|
2555
|
+
id: 'usr_google',
|
|
2556
|
+
image: null,
|
|
2557
|
+
name: 'Tempo Developer',
|
|
2558
|
+
updatedAt: now.toISOString(),
|
|
2559
|
+
})
|
|
2560
|
+
.execute()
|
|
2561
|
+
await db.kysely
|
|
2562
|
+
.insertInto('auth_sessions')
|
|
2563
|
+
.values({
|
|
2564
|
+
createdAt: now,
|
|
2565
|
+
expiresAt: new Date(now.getTime() + 60_000),
|
|
2566
|
+
id: 'ses_google',
|
|
2567
|
+
ipAddress: null,
|
|
2568
|
+
provider: 'google',
|
|
2569
|
+
token: sessionToken,
|
|
2570
|
+
updatedAt: now,
|
|
2571
|
+
userAgent: null,
|
|
2572
|
+
userId: 'usr_google',
|
|
2573
|
+
})
|
|
2574
|
+
.execute()
|
|
2575
|
+
const sessionKey = await crypto.subtle.importKey(
|
|
2576
|
+
'raw',
|
|
2577
|
+
new TextEncoder().encode(secret),
|
|
2578
|
+
{ hash: 'SHA-256', name: 'HMAC' },
|
|
2579
|
+
false,
|
|
2580
|
+
['sign'],
|
|
2581
|
+
)
|
|
2582
|
+
const signature = await crypto.subtle.sign(
|
|
2583
|
+
'HMAC',
|
|
2584
|
+
sessionKey,
|
|
2585
|
+
new TextEncoder().encode(sessionToken),
|
|
2586
|
+
)
|
|
2587
|
+
const cookie = `${sessionToken}.${btoa(String.fromCharCode(...new Uint8Array(signature)))}`
|
|
2588
|
+
|
|
2589
|
+
const response = await app.request(`${origin}/v1/me`, {
|
|
2590
|
+
headers: { cookie: `tempo_auth.session_token=${cookie}` },
|
|
2591
|
+
})
|
|
2592
|
+
|
|
2593
|
+
expect(response.status).toBe(200)
|
|
2594
|
+
expect(await response.json()).toMatchObject({
|
|
2595
|
+
email: 'developer@example.com',
|
|
2596
|
+
id: 'usr_google',
|
|
2597
|
+
})
|
|
2598
|
+
} finally {
|
|
2599
|
+
await db.close()
|
|
2600
|
+
}
|
|
2601
|
+
})
|
|
2602
|
+
|
|
2603
|
+
test('uses a trusted forwarded Console origin for its callback', async () => {
|
|
2604
|
+
const consoleOrigin = 'https://console.example.com'
|
|
2605
|
+
const app = TestApp.create({
|
|
2606
|
+
session: {
|
|
2607
|
+
google: {
|
|
2608
|
+
clientId: 'google-client-id',
|
|
2609
|
+
clientSecret: 'google-client-secret',
|
|
2610
|
+
},
|
|
2611
|
+
secret,
|
|
2612
|
+
trustedOrigins: [consoleOrigin],
|
|
2613
|
+
wallet: { origin },
|
|
2614
|
+
},
|
|
2615
|
+
})
|
|
2616
|
+
const response = await app.request(`${origin}/v1/auth/sign-in/social`, {
|
|
2617
|
+
body: JSON.stringify({ callbackURL: `${consoleOrigin}/`, provider: 'google' }),
|
|
2618
|
+
headers: {
|
|
2619
|
+
'content-type': 'application/json',
|
|
2620
|
+
origin: consoleOrigin,
|
|
2621
|
+
'x-forwarded-host': 'console.example.com',
|
|
2622
|
+
'x-forwarded-proto': 'https',
|
|
2623
|
+
},
|
|
2624
|
+
method: 'POST',
|
|
2625
|
+
})
|
|
2626
|
+
|
|
2627
|
+
expect(response.status).toBe(200)
|
|
2628
|
+
const body = (await response.json()) as { url: string }
|
|
2629
|
+
expect(new URL(body.url).searchParams.get('redirect_uri')).toBe(
|
|
2630
|
+
`${consoleOrigin}/v1/auth/callback/google`,
|
|
2631
|
+
)
|
|
2632
|
+
})
|
|
2633
|
+
})
|
|
2634
|
+
|
|
2635
|
+
describe('email OTP', () => {
|
|
2636
|
+
test.each([
|
|
2637
|
+
{
|
|
2638
|
+
body: '{',
|
|
2639
|
+
name: 'malformed challenge JSON',
|
|
2640
|
+
path: '/v1/auth/siwe/challenge',
|
|
2641
|
+
},
|
|
2642
|
+
{
|
|
2643
|
+
body: JSON.stringify([]),
|
|
2644
|
+
name: 'schema-invalid challenge JSON',
|
|
2645
|
+
path: '/v1/auth/siwe/challenge',
|
|
2646
|
+
},
|
|
2647
|
+
{ body: JSON.stringify({}), name: 'schema-invalid verification JSON', path: '/v1/auth/siwe' },
|
|
2648
|
+
])('validates $name', async ({ body, path }) => {
|
|
2649
|
+
const app = TestApp.create({ session: { secret, wallet: { origin } } })
|
|
2650
|
+
|
|
2651
|
+
const response = await app.request(`${origin}${path}`, {
|
|
2652
|
+
body,
|
|
2653
|
+
headers: { 'content-type': 'application/json' },
|
|
2654
|
+
method: 'POST',
|
|
2655
|
+
})
|
|
2656
|
+
|
|
2657
|
+
expect(response.status).toBe(400)
|
|
2658
|
+
expect(await response.json()).toMatchObject({
|
|
2659
|
+
error: { code: 'body_invalid', message: 'Invalid request body' },
|
|
2660
|
+
})
|
|
2661
|
+
})
|
|
2662
|
+
|
|
2663
|
+
test('leaves unmatched auth requests available to later routes', async () => {
|
|
2664
|
+
const app = TestApp.create({
|
|
2665
|
+
session: { secret, wallet: { origin } },
|
|
2666
|
+
}).route(
|
|
2667
|
+
'/',
|
|
2668
|
+
new Hono().post('/v1/auth/dev-oidc/token', async (c) =>
|
|
2669
|
+
c.json({ idToken: 'test', request: await c.req.json() }),
|
|
2670
|
+
),
|
|
2671
|
+
)
|
|
2672
|
+
|
|
2673
|
+
const response = await app.request(
|
|
2674
|
+
'/v1/auth/dev-oidc/token',
|
|
2675
|
+
json({ subject: 'developer@example.com' }),
|
|
2676
|
+
)
|
|
2677
|
+
|
|
2678
|
+
expect(response.status).toBe(200)
|
|
2679
|
+
expect(await response.json()).toEqual({
|
|
2680
|
+
idToken: 'test',
|
|
2681
|
+
request: { subject: 'developer@example.com' },
|
|
2682
|
+
})
|
|
2683
|
+
})
|
|
2684
|
+
|
|
2685
|
+
test('keeps Better Auth routes out of OpenAPI while using the session scheme', async () => {
|
|
2686
|
+
const app = TestApp.create({
|
|
2687
|
+
session: { secret, wallet: { origin } },
|
|
2688
|
+
})
|
|
2689
|
+
const spec = await (await app.request('/openapi.json')).json()
|
|
2690
|
+
|
|
2691
|
+
expect(spec.paths['/v1/auth/email-otp/send-verification-otp']).toBeUndefined()
|
|
2692
|
+
expect(spec.paths['/v1/auth/sign-in/email-otp']).toBeUndefined()
|
|
2693
|
+
expect(spec.paths['/v1/auth/sign-out']).toBeUndefined()
|
|
2694
|
+
expect(spec.paths['/v1/auth/email-otp']).toBeUndefined()
|
|
2695
|
+
expect(spec.paths['/v1/auth/siwe/logout']).toBeUndefined()
|
|
2696
|
+
expect(spec.components.securitySchemes.emailSession).toBeUndefined()
|
|
2697
|
+
expect(spec.components.securitySchemes.session).toMatchObject({
|
|
2698
|
+
in: 'header',
|
|
2699
|
+
name: 'Cookie',
|
|
2700
|
+
type: 'apiKey',
|
|
2701
|
+
})
|
|
2702
|
+
expect(spec.paths['/v1/auth/logout'].post.responses['204']).toBeDefined()
|
|
2703
|
+
expect(spec.paths['/v1/auth/logout'].post.security).toEqual([])
|
|
2704
|
+
expect(spec.paths['/v1/me'].get.security).toContainEqual({ session: [] })
|
|
2705
|
+
})
|
|
2706
|
+
|
|
2707
|
+
test('sends email OTP without the management group', async () => {
|
|
2708
|
+
const db = TestApp.database()
|
|
2709
|
+
const messages: App.Email.Message[] = []
|
|
2710
|
+
const app = App.create({
|
|
2711
|
+
auth: { session: { secret, wallet: { origin } } },
|
|
2712
|
+
db,
|
|
2713
|
+
email: {
|
|
2714
|
+
from: 'noreply@example.com',
|
|
2715
|
+
send: async (message) => void messages.push(message),
|
|
2716
|
+
},
|
|
2717
|
+
kv: { store: Store.memory() },
|
|
2718
|
+
})
|
|
2719
|
+
|
|
2720
|
+
try {
|
|
2721
|
+
const response = await app.request(
|
|
2722
|
+
`${origin}/v1/auth/email-otp/send-verification-otp`,
|
|
2723
|
+
json({ email: 'developer@example.com', type: 'sign-in' }),
|
|
2724
|
+
)
|
|
2725
|
+
|
|
2726
|
+
expect(response.status).toBe(200)
|
|
2727
|
+
expect(messages).toHaveLength(1)
|
|
2728
|
+
} finally {
|
|
2729
|
+
await db.close()
|
|
2730
|
+
}
|
|
2731
|
+
})
|
|
2732
|
+
|
|
2733
|
+
test('accepts the legacy management email option', async () => {
|
|
2734
|
+
const db = TestApp.database()
|
|
2735
|
+
const messages: App.Email.Message[] = []
|
|
2736
|
+
const app = App.create({
|
|
2737
|
+
auth: { session: { secret, wallet: { origin } } },
|
|
2738
|
+
db,
|
|
2739
|
+
kv: { store: Store.memory() },
|
|
2740
|
+
}).route(
|
|
2741
|
+
'/',
|
|
2742
|
+
Management.management({
|
|
2743
|
+
email: {
|
|
2744
|
+
from: 'noreply@example.com',
|
|
2745
|
+
send: async (message) => void messages.push(message),
|
|
2746
|
+
},
|
|
2747
|
+
}),
|
|
2748
|
+
)
|
|
2749
|
+
|
|
2750
|
+
try {
|
|
2751
|
+
const response = await app.request(
|
|
2752
|
+
`${origin}/v1/auth/email-otp/send-verification-otp`,
|
|
2753
|
+
json({ email: 'developer@example.com', type: 'sign-in' }),
|
|
2754
|
+
)
|
|
2755
|
+
|
|
2756
|
+
expect(response.status).toBe(200)
|
|
2757
|
+
expect(messages).toHaveLength(1)
|
|
2758
|
+
} finally {
|
|
2759
|
+
await db.close()
|
|
2760
|
+
}
|
|
2761
|
+
})
|
|
2762
|
+
|
|
2763
|
+
test('creates a canonical Tempo user and Better Auth session', async () => {
|
|
2764
|
+
const db = TestApp.database()
|
|
2765
|
+
const messages: App.Email.Message[] = []
|
|
2766
|
+
const app = TestApp.create({
|
|
2767
|
+
db,
|
|
2768
|
+
email: {
|
|
2769
|
+
from: 'noreply@example.com',
|
|
2770
|
+
send: async (message) => void messages.push(message),
|
|
2771
|
+
},
|
|
2772
|
+
session: {
|
|
2773
|
+
secret,
|
|
2774
|
+
wallet: { origin },
|
|
2775
|
+
},
|
|
2776
|
+
})
|
|
2777
|
+
|
|
2778
|
+
try {
|
|
2779
|
+
const sent = await app.request(
|
|
2780
|
+
`${origin}/v1/auth/email-otp/send-verification-otp`,
|
|
2781
|
+
json({ email: 'Developer@Example.com', type: 'sign-in' }),
|
|
2782
|
+
)
|
|
2783
|
+
expect(sent.status).toBe(200)
|
|
2784
|
+
expect(await sent.json()).toEqual({ success: true })
|
|
2785
|
+
expect(messages).toHaveLength(1)
|
|
2786
|
+
const otp = messages[0]!.text.match(/\b\d{6}\b/)?.[0]
|
|
2787
|
+
expect(otp).toBeDefined()
|
|
2788
|
+
expect(messages[0]).toMatchObject({
|
|
2789
|
+
from: 'noreply@example.com',
|
|
2790
|
+
subject: `${otp} is your Tempo sign-in code`,
|
|
2791
|
+
to: 'developer@example.com',
|
|
2792
|
+
})
|
|
2793
|
+
|
|
2794
|
+
const verification = await db.kysely
|
|
2795
|
+
.selectFrom('auth_verifications')
|
|
2796
|
+
.select(['value'])
|
|
2797
|
+
.executeTakeFirstOrThrow()
|
|
2798
|
+
expect(verification.value).not.toContain(otp!)
|
|
2799
|
+
|
|
2800
|
+
const verified = await app.request(
|
|
2801
|
+
`${origin}/v1/auth/sign-in/email-otp`,
|
|
2802
|
+
json({ email: 'developer@example.com', otp }),
|
|
2803
|
+
)
|
|
2804
|
+
expect(verified.status).toBe(200)
|
|
2805
|
+
expect(await verified.json()).toMatchObject({ user: { email: 'developer@example.com' } })
|
|
2806
|
+
|
|
2807
|
+
const existing = await Users.getByEmail(db, 'developer@example.com')
|
|
2808
|
+
expect(existing).toBeDefined()
|
|
2809
|
+
|
|
2810
|
+
const setCookie = verified.headers.get('set-cookie')
|
|
2811
|
+
expect(setCookie).toContain('tempo_auth.session_token=')
|
|
2812
|
+
expect(setCookie).toContain('Max-Age=86400')
|
|
2813
|
+
expect(setCookie).toContain('Path=/')
|
|
2814
|
+
expect(setCookie).toContain('HttpOnly')
|
|
2815
|
+
expect(setCookie).toContain('Secure')
|
|
2816
|
+
expect(setCookie).toContain('SameSite=Lax')
|
|
2817
|
+
|
|
2818
|
+
const session = await app.request(`${origin}/v1/auth/get-session`, {
|
|
2819
|
+
headers: { cookie: setCookie!.split(';', 1)[0]! },
|
|
2820
|
+
})
|
|
2821
|
+
expect(await session.json()).toMatchObject({ session: { provider: 'email' } })
|
|
2822
|
+
|
|
2823
|
+
const response = await app.request(`${origin}/v1/me`, {
|
|
2824
|
+
headers: { cookie: setCookie!.split(';', 1)[0]! },
|
|
2825
|
+
})
|
|
2826
|
+
expect(response.status).toBe(200)
|
|
2827
|
+
expect(await response.json()).toMatchObject({
|
|
2828
|
+
email: 'developer@example.com',
|
|
2829
|
+
id: existing!.id,
|
|
2830
|
+
})
|
|
2831
|
+
|
|
2832
|
+
const users = await db.kysely.selectFrom('users').selectAll().execute()
|
|
2833
|
+
expect(users).toHaveLength(1)
|
|
2834
|
+
expect(users[0]).toMatchObject({
|
|
2835
|
+
email: 'developer@example.com',
|
|
2836
|
+
emailVerified: true,
|
|
2837
|
+
})
|
|
2838
|
+
expect(users[0]!.createdAt).toMatch(/Z$/)
|
|
2839
|
+
expect(users[0]!.updatedAt).toMatch(/Z$/)
|
|
2840
|
+
} finally {
|
|
2841
|
+
await db.close()
|
|
2842
|
+
}
|
|
2843
|
+
})
|
|
2844
|
+
|
|
2845
|
+
test('selects the oldest user for a duplicate email', async () => {
|
|
2846
|
+
const db = TestApp.database()
|
|
2847
|
+
const messages: App.Email.Message[] = []
|
|
2848
|
+
const app = TestApp.create({
|
|
2849
|
+
db,
|
|
2850
|
+
email: {
|
|
2851
|
+
from: 'noreply@example.com',
|
|
2852
|
+
send: async (message) => void messages.push(message),
|
|
2853
|
+
},
|
|
2854
|
+
session: { secret, wallet: { origin } },
|
|
2855
|
+
})
|
|
2856
|
+
|
|
2857
|
+
try {
|
|
2858
|
+
await db.kysely
|
|
2859
|
+
.insertInto('users')
|
|
2860
|
+
.values([
|
|
2861
|
+
{
|
|
2862
|
+
address: null,
|
|
2863
|
+
createdAt: '2025-01-01T00:00:00.000Z',
|
|
2864
|
+
email: 'developer@example.com',
|
|
2865
|
+
emailVerified: true,
|
|
2866
|
+
id: 'usr_duplicate_oldest',
|
|
2867
|
+
image: null,
|
|
2868
|
+
name: 'Oldest user',
|
|
2869
|
+
updatedAt: '2025-01-01T00:00:00.000Z',
|
|
2870
|
+
},
|
|
2871
|
+
{
|
|
2872
|
+
address: null,
|
|
2873
|
+
createdAt: '2026-01-01T00:00:00.000Z',
|
|
2874
|
+
email: 'developer@example.com',
|
|
2875
|
+
emailVerified: true,
|
|
2876
|
+
id: 'usr_duplicate_newest',
|
|
2877
|
+
image: null,
|
|
2878
|
+
name: 'Newest user',
|
|
2879
|
+
updatedAt: '2026-01-01T00:00:00.000Z',
|
|
2880
|
+
},
|
|
2881
|
+
])
|
|
2882
|
+
.execute()
|
|
2883
|
+
const cookie = await signInEmail(app, {
|
|
2884
|
+
email: 'developer@example.com',
|
|
2885
|
+
messages,
|
|
2886
|
+
})
|
|
2887
|
+
|
|
2888
|
+
const response = await app.request(`${origin}/v1/me`, {
|
|
2889
|
+
headers: { cookie },
|
|
2890
|
+
})
|
|
2891
|
+
|
|
2892
|
+
expect(response.status).toBe(200)
|
|
2893
|
+
expect(await response.json()).toMatchObject({ id: 'usr_duplicate_oldest' })
|
|
2894
|
+
} finally {
|
|
2895
|
+
await db.close()
|
|
2896
|
+
}
|
|
2897
|
+
})
|
|
2898
|
+
|
|
2899
|
+
test('mounts the Better Auth handler surface', async () => {
|
|
2900
|
+
const app = TestApp.create({
|
|
2901
|
+
session: { secret, wallet: { origin } },
|
|
2902
|
+
})
|
|
2903
|
+
|
|
2904
|
+
const response = await app.request(`${origin}/v1/auth/get-session`)
|
|
2905
|
+
|
|
2906
|
+
expect(response.status).toBe(200)
|
|
2907
|
+
expect(await response.json()).toBeNull()
|
|
2908
|
+
})
|
|
2909
|
+
|
|
2910
|
+
test('mounts Better Auth under the app base path', async () => {
|
|
2911
|
+
const app = TestApp.create({
|
|
2912
|
+
path: '/api',
|
|
2913
|
+
session: { secret, wallet: { origin } },
|
|
2914
|
+
})
|
|
2915
|
+
|
|
2916
|
+
const response = await app.request(`${origin}/api/v1/auth/get-session`)
|
|
2917
|
+
|
|
2918
|
+
expect(response.status).toBe(200)
|
|
2919
|
+
expect(await response.json()).toBeNull()
|
|
2920
|
+
})
|
|
2921
|
+
|
|
2922
|
+
test('handles SIWE routes under an application path', async () => {
|
|
2923
|
+
const app = TestApp.create({
|
|
2924
|
+
path: '/api',
|
|
2925
|
+
session: { secret, wallet: { origin } },
|
|
2926
|
+
})
|
|
2927
|
+
const account = privateKeyToAccount(generatePrivateKey())
|
|
2928
|
+
|
|
2929
|
+
const { verifyResponse } = await TestApp.signIn(
|
|
2930
|
+
{ request: (path, init) => app.request(`/api${path}`, init) },
|
|
2931
|
+
account,
|
|
2932
|
+
)
|
|
2933
|
+
|
|
2934
|
+
expect(verifyResponse.status).toBe(200)
|
|
2935
|
+
})
|
|
2936
|
+
|
|
2937
|
+
test('returns the canonical error envelope when the SIWE store fails', async () => {
|
|
2938
|
+
const entries: Log.Entry[] = []
|
|
2939
|
+
const app = TestApp.create({
|
|
2940
|
+
kv: {
|
|
2941
|
+
store: Store.from({
|
|
2942
|
+
...Store.memory(),
|
|
2943
|
+
async put() {
|
|
2944
|
+
throw new Error('Session store unavailable')
|
|
2945
|
+
},
|
|
2946
|
+
}),
|
|
2947
|
+
},
|
|
2948
|
+
logger: (entry) => void entries.push(entry),
|
|
2949
|
+
session: { secret, wallet: { origin } },
|
|
2950
|
+
})
|
|
2951
|
+
|
|
2952
|
+
const response = await app.request(`${origin}/v1/auth/siwe/challenge`, json({}))
|
|
2953
|
+
|
|
2954
|
+
expect(response.status).toBe(500)
|
|
2955
|
+
expect(await response.json()).toMatchObject({
|
|
2956
|
+
error: { code: 'internal_error', message: 'Internal server error' },
|
|
2957
|
+
})
|
|
2958
|
+
expect(entries.at(-1)).toMatchObject({ errorCode: 'internal_error', level: 'error' })
|
|
2959
|
+
})
|
|
2960
|
+
|
|
2961
|
+
test('does not mount Better Auth without session authentication', async () => {
|
|
2962
|
+
const app = TestApp.create({ auth: false })
|
|
2963
|
+
|
|
2964
|
+
const response = await app.request(`${origin}/v1/auth/get-session`)
|
|
2965
|
+
|
|
2966
|
+
expect(response.status).toBe(404)
|
|
2967
|
+
})
|
|
2968
|
+
|
|
2969
|
+
test('signs out through the Better Auth handler', async () => {
|
|
2970
|
+
const messages: App.Email.Message[] = []
|
|
2971
|
+
const app = TestApp.create({
|
|
2972
|
+
email: {
|
|
2973
|
+
from: 'noreply@example.com',
|
|
2974
|
+
send: async (message) => void messages.push(message),
|
|
2975
|
+
},
|
|
2976
|
+
session: { secret, wallet: { origin } },
|
|
2977
|
+
})
|
|
2978
|
+
const cookie = await signInEmail(app, {
|
|
2979
|
+
email: 'developer@example.com',
|
|
2980
|
+
messages,
|
|
2981
|
+
})
|
|
2982
|
+
|
|
2983
|
+
const response = await app.request(`${origin}/v1/auth/sign-out`, {
|
|
2984
|
+
headers: { cookie },
|
|
2985
|
+
method: 'POST',
|
|
2986
|
+
})
|
|
2987
|
+
|
|
2988
|
+
expect(response.status).toBe(200)
|
|
2989
|
+
expect(await response.json()).toMatchObject({ success: true })
|
|
2990
|
+
expect(response.headers.get('set-cookie')).toContain('tempo_auth.session_token=')
|
|
2991
|
+
expect(
|
|
2992
|
+
await app.request(`${origin}/v1/me`, {
|
|
2993
|
+
headers: { cookie },
|
|
2994
|
+
}),
|
|
2995
|
+
).toMatchObject({ status: 401 })
|
|
2996
|
+
})
|
|
2997
|
+
|
|
2998
|
+
test('rejects unsupported OTP purposes before delivery', async () => {
|
|
2999
|
+
const messages: App.Email.Message[] = []
|
|
3000
|
+
const app = TestApp.create({
|
|
3001
|
+
email: {
|
|
3002
|
+
from: 'noreply@example.com',
|
|
3003
|
+
send: async (message) => void messages.push(message),
|
|
3004
|
+
},
|
|
3005
|
+
session: { secret, wallet: { origin } },
|
|
3006
|
+
})
|
|
3007
|
+
|
|
3008
|
+
const response = await app.request(
|
|
3009
|
+
`${origin}/v1/auth/email-otp/send-verification-otp`,
|
|
3010
|
+
json({ email: 'developer@example.com', type: 'email-verification' }),
|
|
3011
|
+
)
|
|
3012
|
+
|
|
3013
|
+
expect(response.status).toBe(400)
|
|
3014
|
+
expect(await response.json()).toMatchObject({ error: { code: 'body_invalid' } })
|
|
3015
|
+
expect(messages).toHaveLength(0)
|
|
3016
|
+
})
|
|
3017
|
+
|
|
3018
|
+
test('signs out Better Auth through the canonical logout route', async () => {
|
|
3019
|
+
const messages: App.Email.Message[] = []
|
|
3020
|
+
const app = TestApp.create({
|
|
3021
|
+
email: {
|
|
3022
|
+
from: 'noreply@example.com',
|
|
3023
|
+
send: async (message) => void messages.push(message),
|
|
3024
|
+
},
|
|
3025
|
+
session: { secret, wallet: { origin } },
|
|
3026
|
+
})
|
|
3027
|
+
const cookie = await signInEmail(app, {
|
|
3028
|
+
email: 'developer@example.com',
|
|
3029
|
+
messages,
|
|
3030
|
+
})
|
|
3031
|
+
|
|
3032
|
+
const response = await app.request(`${origin}/v1/auth/logout`, {
|
|
3033
|
+
headers: { cookie },
|
|
3034
|
+
method: 'POST',
|
|
3035
|
+
})
|
|
3036
|
+
|
|
3037
|
+
expectSessionsCleared(response)
|
|
3038
|
+
expect(
|
|
3039
|
+
await app.request(`${origin}/v1/me`, {
|
|
3040
|
+
headers: { cookie },
|
|
3041
|
+
}),
|
|
3042
|
+
).toMatchObject({ status: 401 })
|
|
3043
|
+
})
|
|
3044
|
+
|
|
3045
|
+
test('signs out wallet sessions without email configuration', async () => {
|
|
3046
|
+
const app = TestApp.create({ session: { secret, wallet: { origin } } })
|
|
3047
|
+
const account = privateKeyToAccount(generatePrivateKey())
|
|
3048
|
+
const { cookie } = await TestApp.signIn(app, account)
|
|
3049
|
+
|
|
3050
|
+
const response = await app.request(`${origin}/v1/auth/logout`, {
|
|
3051
|
+
headers: { cookie: cookie! },
|
|
3052
|
+
method: 'POST',
|
|
3053
|
+
})
|
|
3054
|
+
|
|
3055
|
+
expectSessionsCleared(response)
|
|
3056
|
+
expect(
|
|
3057
|
+
await app.request(`${origin}/v1/me`, {
|
|
3058
|
+
headers: { cookie: cookie! },
|
|
3059
|
+
}),
|
|
3060
|
+
).toMatchObject({ status: 401 })
|
|
3061
|
+
})
|
|
3062
|
+
|
|
3063
|
+
test('reports wallet session-store logout failures', async () => {
|
|
3064
|
+
const entries: Log.Entry[] = []
|
|
3065
|
+
const source = Store.memory()
|
|
3066
|
+
let failDeletes = false
|
|
3067
|
+
const app = TestApp.create({
|
|
3068
|
+
kv: {
|
|
3069
|
+
store: Store.from({
|
|
3070
|
+
...source,
|
|
3071
|
+
async delete(key) {
|
|
3072
|
+
if (failDeletes) throw new Error('Session store unavailable')
|
|
3073
|
+
await source.delete(key)
|
|
3074
|
+
},
|
|
3075
|
+
}),
|
|
3076
|
+
},
|
|
3077
|
+
logger: (entry) => void entries.push(entry),
|
|
3078
|
+
session: { secret, wallet: { origin } },
|
|
3079
|
+
})
|
|
3080
|
+
const account = privateKeyToAccount(generatePrivateKey())
|
|
3081
|
+
const { cookie } = await TestApp.signIn(app, account)
|
|
3082
|
+
entries.length = 0
|
|
3083
|
+
failDeletes = true
|
|
3084
|
+
|
|
3085
|
+
const response = await app.request(`${origin}/v1/auth/logout`, {
|
|
3086
|
+
headers: { cookie: cookie! },
|
|
3087
|
+
method: 'POST',
|
|
3088
|
+
})
|
|
3089
|
+
|
|
3090
|
+
expect(response.status).toBe(500)
|
|
3091
|
+
expect(await response.json()).toMatchObject({
|
|
3092
|
+
error: { code: 'session_logout_failed', message: 'Session logout failed' },
|
|
3093
|
+
})
|
|
3094
|
+
expect(entries).toHaveLength(1)
|
|
3095
|
+
expect(entries[0]).toMatchObject({ errorCode: 'session_logout_failed', level: 'error' })
|
|
3096
|
+
})
|
|
3097
|
+
|
|
3098
|
+
test('does not resolve email sessions when email is unconfigured', async () => {
|
|
3099
|
+
const db = TestApp.database()
|
|
3100
|
+
const messages: App.Email.Message[] = []
|
|
3101
|
+
const email = TestApp.create({
|
|
3102
|
+
db,
|
|
3103
|
+
email: {
|
|
3104
|
+
from: 'noreply@example.com',
|
|
3105
|
+
send: async (message) => void messages.push(message),
|
|
3106
|
+
},
|
|
3107
|
+
session: { secret, wallet: { origin } },
|
|
3108
|
+
})
|
|
3109
|
+
const cookie = await signInEmail(email, {
|
|
3110
|
+
email: 'developer@example.com',
|
|
3111
|
+
messages,
|
|
3112
|
+
})
|
|
3113
|
+
const wallet = TestApp.create({
|
|
3114
|
+
db,
|
|
3115
|
+
session: { secret, wallet: { origin } },
|
|
3116
|
+
})
|
|
3117
|
+
|
|
3118
|
+
try {
|
|
3119
|
+
const unauthorized = await wallet.request(`${origin}/v1/me`, {
|
|
3120
|
+
headers: { cookie },
|
|
3121
|
+
})
|
|
3122
|
+
expect(unauthorized.status).toBe(401)
|
|
3123
|
+
|
|
3124
|
+
const logout = await wallet.request(`${origin}/v1/auth/logout`, {
|
|
3125
|
+
headers: { cookie },
|
|
3126
|
+
method: 'POST',
|
|
3127
|
+
})
|
|
3128
|
+
expectSessionsCleared(logout)
|
|
3129
|
+
|
|
3130
|
+
const session = await email.request(`${origin}/v1/auth/get-session`, {
|
|
3131
|
+
headers: { cookie },
|
|
3132
|
+
})
|
|
3133
|
+
expect(await session.json()).toBeNull()
|
|
3134
|
+
} finally {
|
|
3135
|
+
await db.close()
|
|
3136
|
+
}
|
|
3137
|
+
})
|
|
3138
|
+
|
|
3139
|
+
test('clears invalid sessions idempotently', async () => {
|
|
3140
|
+
const app = TestApp.create({ session: { secret, wallet: { origin } } })
|
|
3141
|
+
|
|
3142
|
+
const response = await app.request(`${origin}/v1/auth/logout`, {
|
|
3143
|
+
headers: {
|
|
3144
|
+
cookie: 'accounts_auth=expired; tempo_auth.session_token=expired',
|
|
3145
|
+
},
|
|
3146
|
+
method: 'POST',
|
|
3147
|
+
})
|
|
3148
|
+
|
|
3149
|
+
expectSessionsCleared(response)
|
|
3150
|
+
})
|
|
3151
|
+
|
|
3152
|
+
test('reports session database failures', async () => {
|
|
3153
|
+
const db = TestApp.database()
|
|
3154
|
+
const messages: App.Email.Message[] = []
|
|
3155
|
+
const app = TestApp.create({
|
|
3156
|
+
db,
|
|
3157
|
+
email: {
|
|
3158
|
+
from: 'noreply@example.com',
|
|
3159
|
+
send: async (message) => void messages.push(message),
|
|
3160
|
+
},
|
|
3161
|
+
session: { secret, wallet: { origin } },
|
|
3162
|
+
})
|
|
3163
|
+
const cookie = await signInEmail(app, {
|
|
3164
|
+
email: 'developer@example.com',
|
|
3165
|
+
messages,
|
|
3166
|
+
})
|
|
3167
|
+
await db.close()
|
|
3168
|
+
|
|
3169
|
+
const response = await app.request(`${origin}/v1/me`, {
|
|
3170
|
+
headers: { cookie },
|
|
3171
|
+
})
|
|
3172
|
+
|
|
3173
|
+
expect(response.status).toBe(500)
|
|
3174
|
+
expect(await response.json()).toMatchObject({ error: { code: 'internal_error' } })
|
|
3175
|
+
})
|
|
3176
|
+
|
|
3177
|
+
test('rejects consumed and superseded codes without revealing the cause', async () => {
|
|
3178
|
+
const db = TestApp.database()
|
|
3179
|
+
const messages: App.Email.Message[] = []
|
|
3180
|
+
const app = TestApp.create({
|
|
3181
|
+
db,
|
|
3182
|
+
email: {
|
|
3183
|
+
from: 'noreply@example.com',
|
|
3184
|
+
send: async (message) => void messages.push(message),
|
|
3185
|
+
},
|
|
3186
|
+
session: { secret, wallet: { origin } },
|
|
3187
|
+
})
|
|
3188
|
+
|
|
3189
|
+
try {
|
|
3190
|
+
await app.request(
|
|
3191
|
+
`${origin}/v1/auth/email-otp/send-verification-otp`,
|
|
3192
|
+
json({ email: 'developer@example.com', type: 'sign-in' }),
|
|
3193
|
+
)
|
|
3194
|
+
const first = messages[0]!.text.match(/\b\d{6}\b/)![0]!
|
|
3195
|
+
await app.request(
|
|
3196
|
+
`${origin}/v1/auth/email-otp/send-verification-otp`,
|
|
3197
|
+
json({ email: 'developer@example.com', type: 'sign-in' }),
|
|
3198
|
+
)
|
|
3199
|
+
const second = messages[1]!.text.match(/\b\d{6}\b/)![0]!
|
|
3200
|
+
|
|
3201
|
+
const superseded = await app.request(
|
|
3202
|
+
`${origin}/v1/auth/sign-in/email-otp`,
|
|
3203
|
+
json({ email: 'developer@example.com', otp: first }),
|
|
3204
|
+
)
|
|
3205
|
+
expect(superseded.status).toBe(400)
|
|
3206
|
+
expect(await superseded.json()).toMatchObject({ code: 'INVALID_OTP' })
|
|
3207
|
+
|
|
3208
|
+
const verified = await app.request(
|
|
3209
|
+
`${origin}/v1/auth/sign-in/email-otp`,
|
|
3210
|
+
json({ email: 'developer@example.com', otp: second }),
|
|
3211
|
+
)
|
|
3212
|
+
expect(verified.status).toBe(200)
|
|
3213
|
+
|
|
3214
|
+
const consumed = await app.request(
|
|
3215
|
+
`${origin}/v1/auth/sign-in/email-otp`,
|
|
3216
|
+
json({ email: 'developer@example.com', otp: second }),
|
|
3217
|
+
)
|
|
3218
|
+
expect(consumed.status).toBe(400)
|
|
3219
|
+
expect(await consumed.json()).toMatchObject({ code: 'INVALID_OTP' })
|
|
3220
|
+
} finally {
|
|
3221
|
+
await db.close()
|
|
3222
|
+
}
|
|
3223
|
+
})
|
|
3224
|
+
|
|
3225
|
+
test('rejects expired codes through Better Auth', async () => {
|
|
3226
|
+
const db = TestApp.database()
|
|
3227
|
+
const messages: App.Email.Message[] = []
|
|
3228
|
+
const app = TestApp.create({
|
|
3229
|
+
db,
|
|
3230
|
+
email: {
|
|
3231
|
+
from: 'noreply@example.com',
|
|
3232
|
+
send: async (message) => void messages.push(message),
|
|
3233
|
+
},
|
|
3234
|
+
session: { secret, wallet: { origin } },
|
|
3235
|
+
})
|
|
3236
|
+
|
|
3237
|
+
try {
|
|
3238
|
+
await app.request(
|
|
3239
|
+
`${origin}/v1/auth/email-otp/send-verification-otp`,
|
|
3240
|
+
json({ email: 'developer@example.com', type: 'sign-in' }),
|
|
3241
|
+
)
|
|
3242
|
+
const otp = messages[0]!.text.match(/\b\d{6}\b/)![0]!
|
|
3243
|
+
await db.kysely
|
|
3244
|
+
.updateTable('auth_verifications')
|
|
3245
|
+
.set({ expiresAt: new Date(0) })
|
|
3246
|
+
.execute()
|
|
3247
|
+
|
|
3248
|
+
const response = await app.request(
|
|
3249
|
+
`${origin}/v1/auth/sign-in/email-otp`,
|
|
3250
|
+
json({ email: 'developer@example.com', otp }),
|
|
3251
|
+
)
|
|
3252
|
+
|
|
3253
|
+
expect(response.status).toBe(400)
|
|
3254
|
+
expect(await response.json()).toMatchObject({ code: 'OTP_EXPIRED' })
|
|
3255
|
+
} finally {
|
|
3256
|
+
await db.close()
|
|
3257
|
+
}
|
|
3258
|
+
})
|
|
3259
|
+
|
|
3260
|
+
test('uses Better Auth rate limits across request-scoped instances', async () => {
|
|
3261
|
+
const app = TestApp.create({
|
|
3262
|
+
auth: { clientIp: (request) => request.headers.get('x-trusted-ip') ?? undefined },
|
|
3263
|
+
email: { from: 'noreply@example.com', send: async () => {} },
|
|
3264
|
+
rateLimit: { now: () => new Date('2026-01-01T00:00:00.000Z') },
|
|
3265
|
+
session: { secret, wallet: { origin } },
|
|
3266
|
+
})
|
|
3267
|
+
|
|
3268
|
+
for (let index = 0; index < 3; index++) {
|
|
3269
|
+
const response = await app.request(`${origin}/v1/auth/email-otp/send-verification-otp`, {
|
|
3270
|
+
...json({ email: `developer-${index}@example.com`, type: 'sign-in' }),
|
|
3271
|
+
headers: {
|
|
3272
|
+
'cf-connecting-ip': `198.51.100.${index}`,
|
|
3273
|
+
'content-type': 'application/json',
|
|
3274
|
+
'x-trusted-ip': '203.0.113.10',
|
|
3275
|
+
},
|
|
3276
|
+
})
|
|
3277
|
+
expect(response.status).toBe(200)
|
|
3278
|
+
}
|
|
3279
|
+
|
|
3280
|
+
const response = await app.request(`${origin}/v1/auth/email-otp/send-verification-otp`, {
|
|
3281
|
+
...json({ email: 'developer-3@example.com', type: 'sign-in' }),
|
|
3282
|
+
headers: {
|
|
3283
|
+
'cf-connecting-ip': '198.51.100.4',
|
|
3284
|
+
'content-type': 'application/json',
|
|
3285
|
+
'x-trusted-ip': '203.0.113.10',
|
|
3286
|
+
},
|
|
3287
|
+
})
|
|
3288
|
+
expect(response.status).toBe(429)
|
|
3289
|
+
expect(response.headers.get('x-retry-after')).toBe('60')
|
|
3290
|
+
expect(await response.json()).toMatchObject({ message: expect.any(String) })
|
|
3291
|
+
})
|
|
3292
|
+
|
|
3293
|
+
test('limits verification attempts', async () => {
|
|
3294
|
+
const messages: App.Email.Message[] = []
|
|
3295
|
+
const app = TestApp.create({
|
|
3296
|
+
email: {
|
|
3297
|
+
from: 'noreply@example.com',
|
|
3298
|
+
send: async (message) => void messages.push(message),
|
|
3299
|
+
},
|
|
3300
|
+
session: { secret, wallet: { origin } },
|
|
3301
|
+
})
|
|
3302
|
+
await app.request(
|
|
3303
|
+
`${origin}/v1/auth/email-otp/send-verification-otp`,
|
|
3304
|
+
json({ email: 'developer@example.com', type: 'sign-in' }),
|
|
3305
|
+
)
|
|
3306
|
+
const otp = messages[0]!.text.match(/\b\d{6}\b/)![0]!
|
|
3307
|
+
const wrong = `${otp[0] === '0' ? '1' : '0'}${otp.slice(1)}`
|
|
3308
|
+
|
|
3309
|
+
for (let index = 0; index < 3; index++) {
|
|
3310
|
+
const response = await app.request(
|
|
3311
|
+
`${origin}/v1/auth/sign-in/email-otp`,
|
|
3312
|
+
json({ email: 'developer@example.com', otp: wrong }),
|
|
3313
|
+
)
|
|
3314
|
+
expect(response.status).toBe(400)
|
|
3315
|
+
}
|
|
3316
|
+
const response = await app.request(
|
|
3317
|
+
`${origin}/v1/auth/sign-in/email-otp`,
|
|
3318
|
+
json({ email: 'developer@example.com', otp: wrong }),
|
|
3319
|
+
)
|
|
3320
|
+
expect(response.status).toBe(429)
|
|
3321
|
+
expect(await response.json()).toMatchObject({ message: expect.any(String) })
|
|
3322
|
+
})
|
|
3323
|
+
|
|
3324
|
+
test('accepts matching email and wallet sessions', async () => {
|
|
3325
|
+
const db = TestApp.database()
|
|
3326
|
+
const messages: App.Email.Message[] = []
|
|
3327
|
+
const app = TestApp.create({
|
|
3328
|
+
db,
|
|
3329
|
+
email: {
|
|
3330
|
+
from: 'noreply@example.com',
|
|
3331
|
+
send: async (message) => void messages.push(message),
|
|
3332
|
+
},
|
|
3333
|
+
session: { secret, wallet: { origin } },
|
|
3334
|
+
})
|
|
3335
|
+
|
|
3336
|
+
try {
|
|
3337
|
+
const account = privateKeyToAccount(generatePrivateKey())
|
|
3338
|
+
const wallet = await TestApp.signIn(app, account)
|
|
3339
|
+
expect(wallet.verifyResponse.status).toBe(200)
|
|
3340
|
+
const user = await Users.getByAddress(db, account.address)
|
|
3341
|
+
expect(user).toBeDefined()
|
|
3342
|
+
await Users.setEmail(db, user!.id, 'developer@example.com')
|
|
3343
|
+
const email = await signInEmail(app, {
|
|
3344
|
+
email: 'developer@example.com',
|
|
3345
|
+
messages,
|
|
3346
|
+
})
|
|
3347
|
+
|
|
3348
|
+
const response = await app.request(`${origin}/v1/me`, {
|
|
3349
|
+
headers: { cookie: `${email}; ${wallet.cookie}` },
|
|
3350
|
+
})
|
|
3351
|
+
|
|
3352
|
+
expect(response.status).toBe(200)
|
|
3353
|
+
expect(await response.json()).toMatchObject({ email: 'developer@example.com', id: user!.id })
|
|
3354
|
+
|
|
3355
|
+
const logout = await app.request(`${origin}/v1/auth/logout`, {
|
|
3356
|
+
headers: { cookie: `${email}; ${wallet.cookie}` },
|
|
3357
|
+
method: 'POST',
|
|
3358
|
+
})
|
|
3359
|
+
expect(logout.status).toBe(204)
|
|
3360
|
+
expect(
|
|
3361
|
+
await app.request(`${origin}/v1/me`, {
|
|
3362
|
+
headers: { cookie: email },
|
|
3363
|
+
}),
|
|
3364
|
+
).toMatchObject({ status: 401 })
|
|
3365
|
+
expect(
|
|
3366
|
+
await app.request(`${origin}/v1/me`, {
|
|
3367
|
+
headers: { cookie: wallet.cookie! },
|
|
3368
|
+
}),
|
|
3369
|
+
).toMatchObject({ status: 401 })
|
|
3370
|
+
} finally {
|
|
3371
|
+
await db.close()
|
|
3372
|
+
}
|
|
3373
|
+
})
|
|
3374
|
+
|
|
3375
|
+
test('rejects conflicting email and wallet sessions', async () => {
|
|
3376
|
+
const db = TestApp.database()
|
|
3377
|
+
const messages: App.Email.Message[] = []
|
|
3378
|
+
const app = TestApp.create({
|
|
3379
|
+
db,
|
|
3380
|
+
email: {
|
|
3381
|
+
from: 'noreply@example.com',
|
|
3382
|
+
send: async (message) => void messages.push(message),
|
|
3383
|
+
},
|
|
3384
|
+
session: {
|
|
3385
|
+
secret,
|
|
3386
|
+
wallet: { origin },
|
|
3387
|
+
},
|
|
3388
|
+
})
|
|
3389
|
+
|
|
3390
|
+
try {
|
|
3391
|
+
const account = privateKeyToAccount(generatePrivateKey())
|
|
3392
|
+
const wallet = await TestApp.signIn(app, account)
|
|
3393
|
+
expect(wallet.verifyResponse.status).toBe(200)
|
|
3394
|
+
const email = await signInEmail(app, {
|
|
3395
|
+
email: 'developer@example.com',
|
|
3396
|
+
messages,
|
|
3397
|
+
})
|
|
3398
|
+
|
|
3399
|
+
const response = await app.request(`${origin}/v1/me`, {
|
|
3400
|
+
headers: { cookie: `${email}; ${wallet.cookie}` },
|
|
3401
|
+
})
|
|
3402
|
+
|
|
3403
|
+
expect(response.status).toBe(401)
|
|
3404
|
+
|
|
3405
|
+
const logout = await app.request(`${origin}/v1/auth/logout`, {
|
|
3406
|
+
headers: { cookie: `${email}; ${wallet.cookie}` },
|
|
3407
|
+
method: 'POST',
|
|
3408
|
+
})
|
|
3409
|
+
expectSessionsCleared(logout)
|
|
3410
|
+
const emailSession = await app.request(`${origin}/v1/auth/get-session`, {
|
|
3411
|
+
headers: { cookie: email },
|
|
3412
|
+
})
|
|
3413
|
+
expect(emailSession.status).toBe(200)
|
|
3414
|
+
expect(await emailSession.json()).toBeNull()
|
|
3415
|
+
expect(
|
|
3416
|
+
await app.request(`${origin}/v1/me`, {
|
|
3417
|
+
headers: { cookie: email },
|
|
3418
|
+
}),
|
|
3419
|
+
).toMatchObject({ status: 401 })
|
|
3420
|
+
expect(
|
|
3421
|
+
await app.request(`${origin}/v1/me`, {
|
|
3422
|
+
headers: { cookie: wallet.cookie! },
|
|
3423
|
+
}),
|
|
3424
|
+
).toMatchObject({ status: 401 })
|
|
3425
|
+
} finally {
|
|
3426
|
+
await db.close()
|
|
3427
|
+
}
|
|
3428
|
+
})
|
|
3429
|
+
|
|
3430
|
+
test('signs out conflicting wallet bearer and email cookie sessions', async () => {
|
|
3431
|
+
const db = TestApp.database()
|
|
3432
|
+
const messages: App.Email.Message[] = []
|
|
3433
|
+
const app = TestApp.create({
|
|
3434
|
+
db,
|
|
3435
|
+
email: {
|
|
3436
|
+
from: 'noreply@example.com',
|
|
3437
|
+
send: async (message) => void messages.push(message),
|
|
3438
|
+
},
|
|
3439
|
+
session: { secret, wallet: { origin } },
|
|
3440
|
+
})
|
|
3441
|
+
|
|
3442
|
+
try {
|
|
3443
|
+
const account = privateKeyToAccount(generatePrivateKey())
|
|
3444
|
+
const wallet = await TestApp.signIn(app, account, { returnToken: true })
|
|
3445
|
+
expect(wallet.verifyResponse.status).toBe(200)
|
|
3446
|
+
const { token } = (await wallet.verifyResponse.json()) as { token: string }
|
|
3447
|
+
const email = await signInEmail(app, {
|
|
3448
|
+
email: 'developer@example.com',
|
|
3449
|
+
messages,
|
|
3450
|
+
})
|
|
3451
|
+
|
|
3452
|
+
const response = await app.request(`${origin}/v1/me`, {
|
|
3453
|
+
headers: { authorization: `Bearer ${token}`, cookie: email },
|
|
3454
|
+
})
|
|
3455
|
+
expect(response.status).toBe(401)
|
|
3456
|
+
|
|
3457
|
+
const logout = await app.request(`${origin}/v1/auth/logout`, {
|
|
3458
|
+
headers: { authorization: `Bearer ${token}`, cookie: email },
|
|
3459
|
+
method: 'POST',
|
|
3460
|
+
})
|
|
3461
|
+
expectSessionsCleared(logout)
|
|
3462
|
+
expect(
|
|
3463
|
+
await app.request(`${origin}/v1/me`, {
|
|
3464
|
+
headers: { cookie: email },
|
|
3465
|
+
}),
|
|
3466
|
+
).toMatchObject({ status: 401 })
|
|
3467
|
+
expect(
|
|
3468
|
+
await app.request(`${origin}/v1/me`, {
|
|
3469
|
+
headers: { authorization: `Bearer ${token}` },
|
|
3470
|
+
}),
|
|
3471
|
+
).toMatchObject({ status: 401 })
|
|
3472
|
+
} finally {
|
|
3473
|
+
await db.close()
|
|
3474
|
+
}
|
|
3475
|
+
})
|
|
3476
|
+
|
|
3477
|
+
test('signs out wallet cookies when a bearer credential is also present', async () => {
|
|
3478
|
+
const app = TestApp.create({ session: { secret, wallet: { origin } } })
|
|
3479
|
+
const account = privateKeyToAccount(generatePrivateKey())
|
|
3480
|
+
const wallet = await TestApp.signIn(app, account)
|
|
3481
|
+
|
|
3482
|
+
const logout = await app.request(`${origin}/v1/auth/logout`, {
|
|
3483
|
+
headers: {
|
|
3484
|
+
authorization: 'Bearer unrelated-credential',
|
|
3485
|
+
cookie: wallet.cookie!,
|
|
3486
|
+
},
|
|
3487
|
+
method: 'POST',
|
|
3488
|
+
})
|
|
3489
|
+
|
|
3490
|
+
expectSessionsCleared(logout)
|
|
3491
|
+
expect(
|
|
3492
|
+
await app.request(`${origin}/v1/me`, {
|
|
3493
|
+
headers: { cookie: wallet.cookie! },
|
|
3494
|
+
}),
|
|
3495
|
+
).toMatchObject({ status: 401 })
|
|
3496
|
+
})
|
|
3497
|
+
|
|
3498
|
+
test('rate limits unauthenticated logout requests', async () => {
|
|
3499
|
+
const app = TestApp.create({ session: { secret, wallet: { origin } } })
|
|
3500
|
+
|
|
3501
|
+
for (let index = 0; index < 60; index++)
|
|
3502
|
+
expect(
|
|
3503
|
+
await app.request(`${origin}/v1/auth/logout`, {
|
|
3504
|
+
method: 'POST',
|
|
3505
|
+
}),
|
|
3506
|
+
).toMatchObject({ status: 204 })
|
|
3507
|
+
|
|
3508
|
+
const response = await app.request(`${origin}/v1/auth/logout`, {
|
|
3509
|
+
method: 'POST',
|
|
3510
|
+
})
|
|
3511
|
+
|
|
3512
|
+
expect(response.status).toBe(429)
|
|
3513
|
+
expect(await response.json()).toMatchObject({ error: { code: 'rate_limit_exceeded' } })
|
|
3514
|
+
})
|
|
3515
|
+
})
|
|
3516
|
+
|
|
3517
|
+
describe('one-time session tokens', () => {
|
|
3518
|
+
test('redeems the current Better Auth session once', async () => {
|
|
3519
|
+
const db = TestApp.database()
|
|
3520
|
+
const messages: App.Email.Message[] = []
|
|
3521
|
+
const app = TestApp.create({
|
|
3522
|
+
db,
|
|
3523
|
+
email: {
|
|
3524
|
+
from: 'noreply@example.com',
|
|
3525
|
+
send: async (message) => void messages.push(message),
|
|
3526
|
+
},
|
|
3527
|
+
session: { secret, wallet: { origin } },
|
|
3528
|
+
})
|
|
3529
|
+
|
|
3530
|
+
try {
|
|
3531
|
+
const cookie = await signInEmail(app, {
|
|
3532
|
+
email: 'developer@example.com',
|
|
3533
|
+
messages,
|
|
3534
|
+
})
|
|
3535
|
+
const generated = await app.request(`${origin}/v1/auth/one-time-token/generate`, {
|
|
3536
|
+
headers: { cookie },
|
|
3537
|
+
})
|
|
3538
|
+
const { token } = (await generated.json()) as { token: string }
|
|
3539
|
+
const redeem = () => app.request(`${origin}/v1/auth/one-time-token/verify`, json({ token }))
|
|
3540
|
+
|
|
3541
|
+
expect(generated.status).toBe(200)
|
|
3542
|
+
const first = await redeem()
|
|
3543
|
+
expect(first.status).toBe(200)
|
|
3544
|
+
expect(first.headers.get('set-cookie')).toBeNull()
|
|
3545
|
+
expect(await first.json()).toMatchObject({
|
|
3546
|
+
user: { email: 'developer@example.com', emailVerified: true },
|
|
3547
|
+
})
|
|
3548
|
+
expect((await redeem()).status).toBe(400)
|
|
3549
|
+
} finally {
|
|
3550
|
+
await db.close()
|
|
3551
|
+
}
|
|
3552
|
+
})
|
|
3553
|
+
})
|
|
3554
|
+
|
|
3555
|
+
describe('identity sign-in', () => {
|
|
3556
|
+
test('rejects cross-site form bodies', async () => {
|
|
3557
|
+
const app = TestApp.create({
|
|
3558
|
+
session: { identity: { audience: origin }, secret, wallet: { origin } },
|
|
3559
|
+
})
|
|
3560
|
+
|
|
3561
|
+
const response = await app.request(`${origin}/v1/auth/identity`, {
|
|
3562
|
+
body: JSON.stringify({ idToken: 'attacker-token', padding: '' }),
|
|
3563
|
+
headers: {
|
|
3564
|
+
'content-type': 'text/plain',
|
|
3565
|
+
origin: 'https://attacker.example.com',
|
|
3566
|
+
},
|
|
3567
|
+
method: 'POST',
|
|
3568
|
+
})
|
|
3569
|
+
|
|
3570
|
+
expect(response.status).toBe(400)
|
|
3571
|
+
expect(await response.json()).toMatchObject({
|
|
3572
|
+
error: { code: 'body_invalid', message: 'Invalid request body' },
|
|
3573
|
+
})
|
|
3574
|
+
expect(response.headers.get('set-cookie')).toBeNull()
|
|
3575
|
+
})
|
|
3576
|
+
|
|
3577
|
+
test('verifies a Wallet identity and creates an API session', async () => {
|
|
3578
|
+
const db = TestApp.database()
|
|
3579
|
+
const issuer = await createIdentityIssuer({ email: 'developer@example.com' })
|
|
3580
|
+
const account = privateKeyToAccount(generatePrivateKey())
|
|
3581
|
+
const app = TestApp.create({
|
|
3582
|
+
db,
|
|
3583
|
+
session: {
|
|
3584
|
+
identity: { audience: origin, issuer: issuer.url },
|
|
3585
|
+
secret,
|
|
3586
|
+
wallet: { origin },
|
|
3587
|
+
},
|
|
3588
|
+
})
|
|
3589
|
+
|
|
3590
|
+
try {
|
|
3591
|
+
const response = await app.request(
|
|
3592
|
+
`${origin}/v1/auth/identity`,
|
|
3593
|
+
json({ idToken: await issuer.mint({ address: account.address, audience: origin }) }),
|
|
3594
|
+
)
|
|
3595
|
+
const cookie = response.headers.get('set-cookie')?.split(';', 1)[0]
|
|
3596
|
+
|
|
3597
|
+
expect(response.status).toBe(200)
|
|
3598
|
+
expect(cookie).toMatch(/^tempo_identity=/)
|
|
3599
|
+
expect(response.headers.get('set-cookie')).toContain('Max-Age=86400')
|
|
3600
|
+
expect(response.headers.get('set-cookie')).toContain('HttpOnly')
|
|
3601
|
+
expect(response.headers.get('set-cookie')).toContain('SameSite=Lax')
|
|
3602
|
+
expect(response.headers.get('set-cookie')).toContain('Secure')
|
|
3603
|
+
|
|
3604
|
+
const me = await app.request(`${origin}/v1/me`, { headers: { cookie: cookie! } })
|
|
3605
|
+
expect(me.status).toBe(200)
|
|
3606
|
+
expect(await me.json()).toMatchObject({
|
|
3607
|
+
address: account.address.toLowerCase(),
|
|
3608
|
+
email: 'developer@example.com',
|
|
3609
|
+
})
|
|
3610
|
+
|
|
3611
|
+
const logout = await app.request(`${origin}/v1/auth/logout`, {
|
|
3612
|
+
headers: { cookie: cookie! },
|
|
3613
|
+
method: 'POST',
|
|
3614
|
+
})
|
|
3615
|
+
expect(logout.status).toBe(204)
|
|
3616
|
+
expect(logout.headers.getSetCookie()).toEqual(
|
|
3617
|
+
expect.arrayContaining([expect.stringContaining('tempo_identity=')]),
|
|
3618
|
+
)
|
|
3619
|
+
expect(await app.request(`${origin}/v1/me`, { headers: { cookie: cookie! } })).toMatchObject({
|
|
3620
|
+
status: 401,
|
|
3621
|
+
})
|
|
3622
|
+
} finally {
|
|
3623
|
+
await issuer.close()
|
|
3624
|
+
await db.close()
|
|
3625
|
+
}
|
|
3626
|
+
})
|
|
3627
|
+
|
|
3628
|
+
test('clears the identity cookie when session revocation fails', async () => {
|
|
3629
|
+
const db = TestApp.database()
|
|
3630
|
+
const issuer = await createIdentityIssuer({ email: 'developer@example.com' })
|
|
3631
|
+
const source = Store.memory()
|
|
3632
|
+
let failDeletes = false
|
|
3633
|
+
const app = TestApp.create({
|
|
3634
|
+
db,
|
|
3635
|
+
kv: {
|
|
3636
|
+
store: Store.from({
|
|
3637
|
+
...source,
|
|
3638
|
+
async delete(key) {
|
|
3639
|
+
if (failDeletes) throw new Error('Session store unavailable')
|
|
3640
|
+
await source.delete(key)
|
|
3641
|
+
},
|
|
3642
|
+
}),
|
|
3643
|
+
},
|
|
3644
|
+
session: {
|
|
3645
|
+
identity: { audience: origin, issuer: issuer.url },
|
|
3646
|
+
secret,
|
|
3647
|
+
wallet: { origin },
|
|
3648
|
+
},
|
|
3649
|
+
})
|
|
3650
|
+
const account = privateKeyToAccount(generatePrivateKey())
|
|
3651
|
+
|
|
3652
|
+
try {
|
|
3653
|
+
const identity = await app.request(
|
|
3654
|
+
`${origin}/v1/auth/identity`,
|
|
3655
|
+
json({ idToken: await issuer.mint({ address: account.address, audience: origin }) }),
|
|
3656
|
+
)
|
|
3657
|
+
const cookie = identity.headers.get('set-cookie')?.split(';', 1)[0]
|
|
3658
|
+
failDeletes = true
|
|
3659
|
+
const logout = await app.request(`${origin}/v1/auth/logout`, {
|
|
3660
|
+
headers: { cookie: cookie! },
|
|
3661
|
+
method: 'POST',
|
|
3662
|
+
})
|
|
3663
|
+
|
|
3664
|
+
expect(logout.status).toBe(500)
|
|
3665
|
+
expect(logout.headers.getSetCookie()).toEqual(
|
|
3666
|
+
expect.arrayContaining([expect.stringMatching(/^tempo_identity=; Max-Age=0;/)]),
|
|
3667
|
+
)
|
|
3668
|
+
} finally {
|
|
3669
|
+
await issuer.close()
|
|
3670
|
+
await db.close()
|
|
3671
|
+
}
|
|
3672
|
+
})
|
|
3673
|
+
|
|
3674
|
+
test('does not resolve identity sessions when identity is unconfigured', async () => {
|
|
3675
|
+
const db = TestApp.database()
|
|
3676
|
+
const issuer = await createIdentityIssuer({ email: 'developer@example.com' })
|
|
3677
|
+
const store = Store.memory()
|
|
3678
|
+
const configured = TestApp.create({
|
|
3679
|
+
db,
|
|
3680
|
+
kv: { store },
|
|
3681
|
+
session: {
|
|
3682
|
+
identity: { audience: origin, issuer: issuer.url },
|
|
3683
|
+
secret,
|
|
3684
|
+
wallet: { origin },
|
|
3685
|
+
},
|
|
3686
|
+
})
|
|
3687
|
+
const unconfigured = TestApp.create({
|
|
3688
|
+
db,
|
|
3689
|
+
kv: { store },
|
|
3690
|
+
session: { secret, wallet: { origin } },
|
|
3691
|
+
})
|
|
3692
|
+
const account = privateKeyToAccount(generatePrivateKey())
|
|
3693
|
+
|
|
3694
|
+
try {
|
|
3695
|
+
const identity = await configured.request(
|
|
3696
|
+
`${origin}/v1/auth/identity`,
|
|
3697
|
+
json({ idToken: await issuer.mint({ address: account.address, audience: origin }) }),
|
|
3698
|
+
)
|
|
3699
|
+
const cookie = identity.headers.get('set-cookie')?.split(';', 1)[0]
|
|
3700
|
+
const response = await unconfigured.request(`${origin}/v1/me`, {
|
|
3701
|
+
headers: { cookie: cookie! },
|
|
3702
|
+
})
|
|
3703
|
+
|
|
3704
|
+
expect(identity.status).toBe(200)
|
|
3705
|
+
expect(response.status).toBe(401)
|
|
3706
|
+
} finally {
|
|
3707
|
+
await issuer.close()
|
|
3708
|
+
await db.close()
|
|
3709
|
+
}
|
|
3710
|
+
})
|
|
3711
|
+
|
|
3712
|
+
test('reports identity issuer failures', async () => {
|
|
3713
|
+
const db = TestApp.database()
|
|
3714
|
+
const issuer = await createIdentityIssuer({ email: 'developer@example.com' })
|
|
3715
|
+
const account = privateKeyToAccount(generatePrivateKey())
|
|
3716
|
+
const idToken = await issuer.mint({ address: account.address, audience: origin })
|
|
3717
|
+
const entries: Log.Entry[] = []
|
|
3718
|
+
const app = TestApp.create({
|
|
3719
|
+
db,
|
|
3720
|
+
logger: (entry) => void entries.push(entry),
|
|
3721
|
+
session: {
|
|
3722
|
+
identity: { audience: origin, issuer: issuer.url },
|
|
3723
|
+
secret,
|
|
3724
|
+
wallet: { origin },
|
|
3725
|
+
},
|
|
3726
|
+
})
|
|
3727
|
+
await issuer.close()
|
|
3728
|
+
|
|
3729
|
+
try {
|
|
3730
|
+
const response = await app.request(`${origin}/v1/auth/identity`, json({ idToken }))
|
|
3731
|
+
|
|
3732
|
+
expect(response.status).toBe(500)
|
|
3733
|
+
expect(await response.json()).toMatchObject({
|
|
3734
|
+
error: { code: 'internal_error', message: 'Internal server error' },
|
|
3735
|
+
})
|
|
3736
|
+
expect(entries.at(-1)).toMatchObject({ errorCode: 'internal_error', level: 'error' })
|
|
3737
|
+
} finally {
|
|
3738
|
+
await db.close()
|
|
3739
|
+
}
|
|
3740
|
+
})
|
|
3741
|
+
|
|
3742
|
+
test('reconciles the Wallet address into an active email session', async () => {
|
|
3743
|
+
const db = TestApp.database()
|
|
3744
|
+
const issuer = await createIdentityIssuer({ email: 'developer@example.com' })
|
|
3745
|
+
const messages: App.Email.Message[] = []
|
|
3746
|
+
const account = privateKeyToAccount(generatePrivateKey())
|
|
3747
|
+
const app = TestApp.create({
|
|
3748
|
+
db,
|
|
3749
|
+
email: {
|
|
3750
|
+
from: 'noreply@example.com',
|
|
3751
|
+
send: async (message) => void messages.push(message),
|
|
3752
|
+
},
|
|
3753
|
+
session: {
|
|
3754
|
+
identity: { audience: origin, issuer: issuer.url },
|
|
3755
|
+
secret,
|
|
3756
|
+
wallet: { origin },
|
|
3757
|
+
},
|
|
3758
|
+
})
|
|
3759
|
+
|
|
3760
|
+
try {
|
|
3761
|
+
const emailCookie = await signInEmail(app, {
|
|
3762
|
+
email: 'developer@example.com',
|
|
3763
|
+
messages,
|
|
3764
|
+
})
|
|
3765
|
+
const identity = await app.request(
|
|
3766
|
+
`${origin}/v1/auth/identity`,
|
|
3767
|
+
json({ idToken: await issuer.mint({ address: account.address, audience: origin }) }),
|
|
3768
|
+
)
|
|
3769
|
+
const identityCookie = identity.headers.get('set-cookie')?.split(';', 1)[0]
|
|
3770
|
+
const me = await app.request(`${origin}/v1/me`, {
|
|
3771
|
+
headers: { cookie: `${emailCookie}; ${identityCookie}` },
|
|
3772
|
+
})
|
|
3773
|
+
|
|
3774
|
+
expect(identity.status).toBe(200)
|
|
3775
|
+
expect(await me.json()).toMatchObject({
|
|
3776
|
+
address: account.address.toLowerCase(),
|
|
3777
|
+
email: 'developer@example.com',
|
|
3778
|
+
})
|
|
3779
|
+
} finally {
|
|
3780
|
+
await issuer.close()
|
|
3781
|
+
await db.close()
|
|
3782
|
+
}
|
|
3783
|
+
})
|
|
3784
|
+
|
|
3785
|
+
test('rejects an identity issued for another audience', async () => {
|
|
3786
|
+
const issuer = await createIdentityIssuer({ email: 'developer@example.com' })
|
|
3787
|
+
const account = privateKeyToAccount(generatePrivateKey())
|
|
3788
|
+
const app = TestApp.create({
|
|
3789
|
+
session: {
|
|
3790
|
+
identity: { audience: origin, issuer: issuer.url },
|
|
3791
|
+
secret,
|
|
3792
|
+
wallet: { origin },
|
|
3793
|
+
},
|
|
3794
|
+
})
|
|
3795
|
+
|
|
3796
|
+
try {
|
|
3797
|
+
const response = await app.request(
|
|
3798
|
+
`${origin}/v1/auth/identity`,
|
|
3799
|
+
json({
|
|
3800
|
+
idToken: await issuer.mint({
|
|
3801
|
+
address: account.address,
|
|
3802
|
+
audience: 'https://attacker.example.com',
|
|
3803
|
+
}),
|
|
3804
|
+
}),
|
|
3805
|
+
)
|
|
3806
|
+
|
|
3807
|
+
expect(response.status).toBe(401)
|
|
3808
|
+
expect(await response.json()).toMatchObject({
|
|
3809
|
+
error: { code: 'identity_invalid', message: 'Invalid identity token' },
|
|
3810
|
+
})
|
|
3811
|
+
expect(response.headers.get('set-cookie')).toBeNull()
|
|
3812
|
+
} finally {
|
|
3813
|
+
await issuer.close()
|
|
3814
|
+
}
|
|
3815
|
+
})
|
|
3816
|
+
|
|
3817
|
+
test('documents identity sign-in only when configured', async () => {
|
|
3818
|
+
const configured = TestApp.create({
|
|
3819
|
+
session: { identity: { audience: origin }, secret, wallet: { origin } },
|
|
3820
|
+
})
|
|
3821
|
+
const unconfigured = TestApp.create({ session: { secret, wallet: { origin } } })
|
|
3822
|
+
|
|
3823
|
+
const configuredSpec = await (await configured.request('/openapi.json')).json()
|
|
3824
|
+
const unconfiguredSpec = await (await unconfigured.request('/openapi.json')).json()
|
|
3825
|
+
|
|
3826
|
+
expect(configuredSpec.paths['/v1/auth/identity'].post).toBeDefined()
|
|
3827
|
+
expect(unconfiguredSpec.paths['/v1/auth/identity']).toBeUndefined()
|
|
3828
|
+
})
|
|
3829
|
+
})
|
|
3830
|
+
|
|
3831
|
+
function json(body: unknown) {
|
|
3832
|
+
return {
|
|
3833
|
+
body: JSON.stringify(body),
|
|
3834
|
+
headers: { 'content-type': 'application/json' },
|
|
3835
|
+
method: 'POST',
|
|
3836
|
+
}
|
|
3837
|
+
}
|
|
3838
|
+
|
|
3839
|
+
function expectSessionsCleared(response: globalThis.Response) {
|
|
3840
|
+
expect(response.status).toBe(204)
|
|
3841
|
+
expect(response.headers.getSetCookie()).toEqual(
|
|
3842
|
+
expect.arrayContaining([
|
|
3843
|
+
expect.stringContaining('accounts_auth='),
|
|
3844
|
+
expect.stringContaining('tempo_auth.session_token='),
|
|
3845
|
+
]),
|
|
3846
|
+
)
|
|
3847
|
+
}
|
|
3848
|
+
|
|
3849
|
+
/** Local Wallet-style OIDC issuer for identity verification tests. */
|
|
3850
|
+
async function createIdentityIssuer(options: createIdentityIssuer.Options) {
|
|
3851
|
+
const { privateKey, publicKey } = await generateKeyPair('EdDSA', { extractable: true })
|
|
3852
|
+
let provider!: ReturnType<typeof Accounts.Handler.oidcProvider>
|
|
3853
|
+
type Listener = { close: () => Promise<void>; url: string }
|
|
3854
|
+
const { close, url } = await new Promise<Listener>((resolve) => {
|
|
3855
|
+
const server = serve({ fetch: (request) => provider.fetch(request), port: 0 }, (info) =>
|
|
3856
|
+
resolve({
|
|
3857
|
+
close: () => new Promise((done) => server.close(() => done())),
|
|
3858
|
+
url: `http://127.0.0.1:${info.port}`,
|
|
3859
|
+
}),
|
|
3860
|
+
)
|
|
3861
|
+
})
|
|
3862
|
+
provider = Accounts.Handler.oidcProvider({
|
|
3863
|
+
getClaims: () => ({ email: options.email, email_verified: true }),
|
|
3864
|
+
issuer: url,
|
|
3865
|
+
publicKey: JSON.stringify(await exportJWK(publicKey)),
|
|
3866
|
+
signingKey: JSON.stringify(await exportJWK(privateKey)),
|
|
3867
|
+
})
|
|
3868
|
+
return {
|
|
3869
|
+
close,
|
|
3870
|
+
async mint(parameters: createIdentityIssuer.MintParameters) {
|
|
3871
|
+
const response = await provider.fetch(
|
|
3872
|
+
new Request(`${url}/token`, {
|
|
3873
|
+
body: JSON.stringify({
|
|
3874
|
+
audience: parameters.audience,
|
|
3875
|
+
subject: parameters.address,
|
|
3876
|
+
}),
|
|
3877
|
+
headers: { 'content-type': 'application/json' },
|
|
3878
|
+
method: 'POST',
|
|
3879
|
+
}),
|
|
3880
|
+
)
|
|
3881
|
+
const { idToken } = (await response.json()) as { idToken: string }
|
|
3882
|
+
return idToken
|
|
3883
|
+
},
|
|
3884
|
+
url,
|
|
3885
|
+
}
|
|
3886
|
+
}
|
|
3887
|
+
|
|
3888
|
+
declare namespace createIdentityIssuer {
|
|
3889
|
+
type MintParameters = {
|
|
3890
|
+
/** Wallet address used as the token subject. */
|
|
3891
|
+
address: string
|
|
3892
|
+
/** Origin used as the token audience. */
|
|
3893
|
+
audience: string
|
|
3894
|
+
}
|
|
3895
|
+
|
|
3896
|
+
type Options = {
|
|
3897
|
+
/** Verified email included in issued tokens. */
|
|
3898
|
+
email: string
|
|
3899
|
+
}
|
|
3900
|
+
}
|
|
3901
|
+
|
|
3902
|
+
async function signInEmail(app: TestApp.signIn.App, options: signInEmail.Options) {
|
|
3903
|
+
await app.request(
|
|
3904
|
+
`${origin}/v1/auth/email-otp/send-verification-otp`,
|
|
3905
|
+
json({ email: options.email, type: 'sign-in' }),
|
|
3906
|
+
)
|
|
3907
|
+
const otp = options.messages.at(-1)!.text.match(/\b\d{6}\b/)![0]!
|
|
3908
|
+
const response = await app.request(
|
|
3909
|
+
`${origin}/v1/auth/sign-in/email-otp`,
|
|
3910
|
+
json({ email: options.email, otp }),
|
|
3911
|
+
)
|
|
3912
|
+
expect(response.status).toBe(200)
|
|
3913
|
+
return response.headers.get('set-cookie')!.split(';', 1)[0]!
|
|
3914
|
+
}
|
|
3915
|
+
|
|
3916
|
+
declare namespace signInEmail {
|
|
3917
|
+
type Options = {
|
|
3918
|
+
/** Email to authenticate. */
|
|
3919
|
+
email: string
|
|
3920
|
+
/** Captured outbound messages. */
|
|
3921
|
+
messages: readonly App.Email.Message[]
|
|
3922
|
+
}
|
|
3923
|
+
}
|