tapimo 0.10.2 → 0.12.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/dist/App.d.ts +4896 -2166
- package/dist/App.d.ts.map +1 -1
- package/dist/App.js +2 -2
- package/dist/App.js.map +1 -1
- package/dist/Assets.d.ts +15 -0
- package/dist/Assets.d.ts.map +1 -1
- package/dist/Assets.js +7 -0
- package/dist/Assets.js.map +1 -1
- package/dist/Webhooks.d.ts +0 -75
- package/dist/Webhooks.d.ts.map +1 -1
- package/dist/Webhooks.js +1 -143
- package/dist/Webhooks.js.map +1 -1
- package/dist/admin/App.d.ts +127 -78
- package/dist/admin/App.d.ts.map +1 -1
- package/dist/admin/apps/earn-vaults.d.ts +115 -1
- package/dist/admin/apps/earn-vaults.d.ts.map +1 -1
- package/dist/admin/apps/earn-vaults.js +59 -6
- package/dist/admin/apps/earn-vaults.js.map +1 -1
- package/dist/admin/apps/organizations.d.ts +3 -3
- package/dist/admin/apps/organizations.d.ts.map +1 -1
- package/dist/admin/apps/organizations.js +4 -2
- package/dist/admin/apps/organizations.js.map +1 -1
- package/dist/admin/apps/verified-tokens.d.ts +83 -83
- 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 +151 -151
- package/dist/admin/ui.generated.js.map +1 -1
- package/dist/apps/data/App.d.ts +5924 -4310
- package/dist/apps/data/App.d.ts.map +1 -1
- package/dist/apps/data/App.js +61 -9
- package/dist/apps/data/App.js.map +1 -1
- package/dist/apps/data/ZoneSelection.d.ts +62 -0
- package/dist/apps/data/ZoneSelection.d.ts.map +1 -0
- package/dist/apps/data/ZoneSelection.js +44 -0
- package/dist/apps/data/ZoneSelection.js.map +1 -0
- package/dist/apps/data/routes/activities.d.ts +2368 -5
- package/dist/apps/data/routes/activities.d.ts.map +1 -1
- package/dist/apps/data/routes/activities.js +423 -114
- package/dist/apps/data/routes/activities.js.map +1 -1
- package/dist/apps/data/routes/addresses.d.ts +131 -0
- package/dist/apps/data/routes/addresses.d.ts.map +1 -1
- package/dist/apps/data/routes/balances.d.ts +25 -25
- package/dist/apps/data/routes/balances.js +1 -1
- package/dist/apps/data/routes/balances.js.map +1 -1
- package/dist/apps/data/routes/blocks.d.ts +1 -1
- package/dist/apps/data/routes/blocks.js +1 -1
- package/dist/apps/data/routes/blocks.js.map +1 -1
- package/dist/apps/data/routes/earn.d.ts +135 -10
- package/dist/apps/data/routes/earn.d.ts.map +1 -1
- package/dist/apps/data/routes/earn.js +157 -25
- package/dist/apps/data/routes/earn.js.map +1 -1
- package/dist/apps/data/routes/exchanges.d.ts +829 -123
- package/dist/apps/data/routes/exchanges.d.ts.map +1 -1
- package/dist/apps/data/routes/exchanges.js +16 -13
- package/dist/apps/data/routes/exchanges.js.map +1 -1
- package/dist/apps/data/routes/fee-amm.d.ts +26 -26
- package/dist/apps/data/routes/fee-amm.d.ts.map +1 -1
- package/dist/apps/data/routes/fee-amm.js +9 -11
- package/dist/apps/data/routes/fee-amm.js.map +1 -1
- package/dist/apps/data/routes/receipts.d.ts +54 -54
- package/dist/apps/data/routes/receipts.js +1 -1
- package/dist/apps/data/routes/receipts.js.map +1 -1
- package/dist/apps/data/routes/rpc.js +24 -10
- package/dist/apps/data/routes/rpc.js.map +1 -1
- package/dist/apps/data/routes/tokenlist.d.ts +12 -12
- package/dist/apps/data/routes/tokenlist.d.ts.map +1 -1
- package/dist/apps/data/routes/tokenlist.js +16 -15
- package/dist/apps/data/routes/tokenlist.js.map +1 -1
- package/dist/apps/data/routes/tokens.d.ts +17 -3
- package/dist/apps/data/routes/tokens.d.ts.map +1 -1
- package/dist/apps/data/routes/tokens.js +34 -15
- package/dist/apps/data/routes/tokens.js.map +1 -1
- package/dist/apps/data/routes/transactions.d.ts +1 -1
- package/dist/apps/data/routes/transactions.js +1 -1
- package/dist/apps/data/routes/transactions.js.map +1 -1
- package/dist/apps/data/routes/transfers.d.ts +13 -13
- package/dist/apps/data/routes/transfers.d.ts.map +1 -1
- package/dist/apps/data/routes/transfers.js +7 -9
- package/dist/apps/data/routes/transfers.js.map +1 -1
- package/dist/apps/data/routes/valuation.d.ts +21 -21
- package/dist/apps/data/routes/verified-tokens.d.ts +5 -5
- package/dist/apps/data/routes/webhooks.d.ts +1011 -151
- package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
- package/dist/apps/data/routes/webhooks.js +126 -739
- package/dist/apps/data/routes/webhooks.js.map +1 -1
- package/dist/apps/data/routes/zones.d.ts +15 -15
- package/dist/apps/funding/App.d.ts +27 -27
- package/dist/apps/funding/routes/chains.d.ts.map +1 -1
- package/dist/apps/funding/routes/chains.js +5 -4
- package/dist/apps/funding/routes/chains.js.map +1 -1
- package/dist/apps/funding/routes/deposit-addresses.d.ts +18 -18
- package/dist/apps/funding/routes/deposit-addresses.d.ts.map +1 -1
- package/dist/apps/funding/routes/deposit-addresses.js +8 -8
- package/dist/apps/funding/routes/deposit-addresses.js.map +1 -1
- package/dist/apps/funding/routes/providers.d.ts.map +1 -1
- package/dist/apps/funding/routes/providers.js +3 -2
- package/dist/apps/funding/routes/providers.js.map +1 -1
- package/dist/apps/funding/routes/quotes.d.ts.map +1 -1
- package/dist/apps/funding/routes/quotes.js +2 -2
- package/dist/apps/funding/routes/quotes.js.map +1 -1
- package/dist/apps/funding/routes/transfers.d.ts +33 -33
- package/dist/apps/management/App.d.ts +1433 -322
- package/dist/apps/management/App.d.ts.map +1 -1
- package/dist/apps/management/Billing.d.ts.map +1 -1
- package/dist/apps/management/Billing.js +19 -6
- package/dist/apps/management/Billing.js.map +1 -1
- package/dist/apps/management/routes/api-keys.d.ts +154 -94
- package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
- package/dist/apps/management/routes/api-keys.js +76 -30
- package/dist/apps/management/routes/api-keys.js.map +1 -1
- package/dist/apps/management/routes/billing.d.ts +103 -30
- package/dist/apps/management/routes/billing.d.ts.map +1 -1
- package/dist/apps/management/routes/billing.js +91 -32
- package/dist/apps/management/routes/billing.js.map +1 -1
- package/dist/apps/management/routes/faucet.d.ts +12 -12
- package/dist/apps/management/routes/faucet.d.ts.map +1 -1
- package/dist/apps/management/routes/faucet.js +23 -12
- package/dist/apps/management/routes/faucet.js.map +1 -1
- package/dist/apps/management/routes/invitations.d.ts +46 -13
- package/dist/apps/management/routes/invitations.d.ts.map +1 -1
- package/dist/apps/management/routes/invitations.js +79 -29
- package/dist/apps/management/routes/invitations.js.map +1 -1
- package/dist/apps/management/routes/invite-links.d.ts +128 -83
- package/dist/apps/management/routes/invite-links.d.ts.map +1 -1
- package/dist/apps/management/routes/invite-links.js +100 -41
- package/dist/apps/management/routes/invite-links.js.map +1 -1
- package/dist/apps/management/routes/me.d.ts +5 -5
- package/dist/apps/management/routes/me.d.ts.map +1 -1
- package/dist/apps/management/routes/me.js +14 -10
- package/dist/apps/management/routes/me.js.map +1 -1
- package/dist/apps/management/routes/members.d.ts +68 -47
- package/dist/apps/management/routes/members.d.ts.map +1 -1
- package/dist/apps/management/routes/members.js +37 -18
- package/dist/apps/management/routes/members.js.map +1 -1
- package/dist/apps/management/routes/orgs.d.ts +48 -8
- package/dist/apps/management/routes/orgs.d.ts.map +1 -1
- package/dist/apps/management/routes/orgs.js +48 -19
- package/dist/apps/management/routes/orgs.js.map +1 -1
- package/dist/apps/management/routes/projects.d.ts +130 -68
- package/dist/apps/management/routes/projects.d.ts.map +1 -1
- package/dist/apps/management/routes/projects.js +72 -21
- 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 +8 -5
- package/dist/apps/management/routes/scopes.js.map +1 -1
- package/dist/apps/management/routes/usage.d.ts +38 -38
- package/dist/apps/management/routes/usage.d.ts.map +1 -1
- package/dist/apps/management/routes/usage.js +41 -31
- package/dist/apps/management/routes/usage.js.map +1 -1
- package/dist/apps/management/routes/verified-token-requests.d.ts +34 -6
- package/dist/apps/management/routes/verified-token-requests.d.ts.map +1 -1
- package/dist/apps/management/routes/verified-token-requests.js +36 -17
- package/dist/apps/management/routes/verified-token-requests.js.map +1 -1
- package/dist/apps/management/routes/webhooks.d.ts +1186 -197
- package/dist/apps/management/routes/webhooks.d.ts.map +1 -1
- package/dist/apps/management/routes/webhooks.js +106 -38
- package/dist/apps/management/routes/webhooks.js.map +1 -1
- package/dist/apps/mpp/internal/Adapter.d.ts +1 -1
- package/dist/apps/mpp/internal/Adapter.d.ts.map +1 -1
- package/dist/apps/mpp/internal/Adapter.js +12 -0
- package/dist/apps/mpp/internal/Adapter.js.map +1 -1
- package/dist/db/Db.d.ts +2 -0
- package/dist/db/Db.d.ts.map +1 -1
- package/dist/db/Db.js +61 -0
- package/dist/db/Db.js.map +1 -1
- package/dist/db/Schema.d.ts +30 -2
- package/dist/db/Schema.d.ts.map +1 -1
- package/dist/db/Schema.js +28 -3
- package/dist/db/Schema.js.map +1 -1
- package/dist/db/tables/earnVaults.d.ts +5 -1
- package/dist/db/tables/earnVaults.d.ts.map +1 -1
- package/dist/db/tables/earnVaults.js +5 -0
- package/dist/db/tables/earnVaults.js.map +1 -1
- package/dist/db/tables/webhookDeliveries.d.ts.map +1 -1
- package/dist/db/tables/webhookDeliveries.js +10 -0
- package/dist/db/tables/webhookDeliveries.js.map +1 -1
- package/dist/db/tables/webhookQueueCompletions.d.ts +15 -0
- package/dist/db/tables/webhookQueueCompletions.d.ts.map +1 -0
- package/dist/db/tables/webhookQueueCompletions.js +32 -0
- package/dist/db/tables/webhookQueueCompletions.js.map +1 -0
- package/dist/db/tables/webhookQueueEvents.d.ts +16 -20
- package/dist/db/tables/webhookQueueEvents.d.ts.map +1 -1
- package/dist/db/tables/webhookQueueEvents.js +68 -43
- package/dist/db/tables/webhookQueueEvents.js.map +1 -1
- package/dist/db/tables/webhookSubscriptions.d.ts +47 -72
- package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -1
- package/dist/db/tables/webhookSubscriptions.js +92 -105
- package/dist/db/tables/webhookSubscriptions.js.map +1 -1
- package/dist/internal/Auth.d.ts +8 -1
- package/dist/internal/Auth.d.ts.map +1 -1
- package/dist/internal/Auth.js +13 -3
- package/dist/internal/Auth.js.map +1 -1
- package/dist/internal/Cache.d.ts.map +1 -1
- package/dist/internal/Cache.js +11 -7
- package/dist/internal/Cache.js.map +1 -1
- package/dist/internal/EarnVaults.d.ts +41 -2
- package/dist/internal/EarnVaults.d.ts.map +1 -1
- package/dist/internal/EarnVaults.js +167 -0
- package/dist/internal/EarnVaults.js.map +1 -1
- package/dist/internal/MetricSink.d.ts +35 -125
- package/dist/internal/MetricSink.d.ts.map +1 -1
- package/dist/internal/MetricSink.js +50 -121
- package/dist/internal/MetricSink.js.map +1 -1
- package/dist/internal/OpenApi.d.ts +26 -22
- package/dist/internal/OpenApi.d.ts.map +1 -1
- package/dist/internal/OpenApi.js +128 -39
- package/dist/internal/OpenApi.js.map +1 -1
- package/dist/internal/Response.d.ts +13 -8
- package/dist/internal/Response.d.ts.map +1 -1
- package/dist/internal/Response.js +16 -5
- package/dist/internal/Response.js.map +1 -1
- package/dist/internal/Schema.d.ts +8 -8
- package/dist/internal/Schema.d.ts.map +1 -1
- package/dist/internal/Schema.js +10 -9
- package/dist/internal/Schema.js.map +1 -1
- package/dist/internal/Store.d.ts +15 -6
- package/dist/internal/Store.d.ts.map +1 -1
- package/dist/internal/Store.js +45 -15
- package/dist/internal/Store.js.map +1 -1
- package/dist/internal/Tidx.d.ts +2 -0
- package/dist/internal/Tidx.d.ts.map +1 -1
- package/dist/internal/Tidx.js +16 -4
- package/dist/internal/Tidx.js.map +1 -1
- package/dist/internal/WebhookTransfer.d.ts +1 -55
- package/dist/internal/WebhookTransfer.d.ts.map +1 -1
- package/dist/internal/WebhookTransfer.js +1 -239
- package/dist/internal/WebhookTransfer.js.map +1 -1
- package/dist/internal/Webhooks.d.ts +112 -56
- package/dist/internal/Webhooks.d.ts.map +1 -1
- package/dist/internal/Webhooks.js +124 -93
- package/dist/internal/Webhooks.js.map +1 -1
- package/dist/internal/funding/Deposit.d.ts +2 -2
- package/dist/internal/funding/Deposit.d.ts.map +1 -1
- package/dist/internal/funding/Deposit.js +9 -8
- package/dist/internal/funding/Deposit.js.map +1 -1
- package/dist/internal/funding/DepositAddress.d.ts +4 -4
- package/dist/internal/funding/DepositAddress.d.ts.map +1 -1
- package/dist/internal/funding/DepositAddress.js +9 -8
- package/dist/internal/funding/DepositAddress.js.map +1 -1
- package/dist/internal/funding/DepositWebhook.d.ts +25 -0
- package/dist/internal/funding/DepositWebhook.d.ts.map +1 -0
- package/dist/internal/funding/DepositWebhook.js +74 -0
- package/dist/internal/funding/DepositWebhook.js.map +1 -0
- package/dist/internal/funding/Provider.d.ts.map +1 -1
- package/dist/internal/funding/Provider.js +13 -12
- package/dist/internal/funding/Provider.js.map +1 -1
- package/dist/internal/funding/Reconciliation.d.ts +6 -0
- package/dist/internal/funding/Reconciliation.d.ts.map +1 -1
- package/dist/internal/funding/Reconciliation.js +42 -10
- package/dist/internal/funding/Reconciliation.js.map +1 -1
- package/dist/internal/funding/index.d.ts +1 -0
- package/dist/internal/funding/index.d.ts.map +1 -1
- package/dist/internal/funding/index.js +1 -0
- package/dist/internal/funding/index.js.map +1 -1
- package/dist/internal/funding/providers/stargate.d.ts +19 -0
- package/dist/internal/funding/providers/stargate.d.ts.map +1 -0
- package/dist/internal/funding/providers/stargate.js +143 -0
- package/dist/internal/funding/providers/stargate.js.map +1 -0
- package/dist/internal/webhookDestinations/slack.d.ts.map +1 -1
- package/dist/internal/webhookDestinations/slack.js +16 -0
- package/dist/internal/webhookDestinations/slack.js.map +1 -1
- package/dist/providers/index.d.ts +7 -0
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +3 -0
- package/dist/providers/index.js.map +1 -1
- package/package.json +2 -2
- package/src/App.test.ts +48 -0
- package/src/App.ts +7 -2
- package/src/Assets.test.ts +18 -0
- package/src/Assets.ts +22 -0
- package/src/Client.test-d.ts +287 -17
- package/src/Webhooks.ts +1 -220
- package/src/admin/apps/earn-vaults.test.ts +143 -0
- package/src/admin/apps/earn-vaults.ts +83 -6
- package/src/admin/apps/organizations.ts +6 -2
- package/src/admin/ui/src/routes/api-keys.tsx +36 -0
- package/src/admin/ui.generated.ts +151 -189
- package/src/apps/data/App.test.ts +31 -0
- package/src/apps/data/App.ts +64 -10
- package/src/apps/data/ZoneSelection.test.ts +196 -0
- package/src/apps/data/ZoneSelection.ts +108 -0
- package/src/apps/data/routes/activities.test.ts +435 -12
- package/src/apps/data/routes/activities.ts +562 -125
- package/src/apps/data/routes/balances.ts +1 -1
- package/src/apps/data/routes/blocks.ts +1 -1
- package/src/apps/data/routes/earn.test.ts +183 -23
- package/src/apps/data/routes/earn.ts +225 -29
- package/src/apps/data/routes/earn.zone-access.test.ts +46 -0
- package/src/apps/data/routes/exchanges.test.ts +31 -1
- package/src/apps/data/routes/exchanges.ts +19 -15
- package/src/apps/data/routes/fee-amm.ts +18 -11
- package/src/apps/data/routes/receipts.ts +1 -1
- package/src/apps/data/routes/rpc.test.ts +75 -0
- package/src/apps/data/routes/rpc.ts +23 -9
- package/src/apps/data/routes/tokenlist.ts +21 -18
- package/src/apps/data/routes/tokens.test.ts +29 -0
- package/src/apps/data/routes/tokens.ts +38 -15
- package/src/apps/data/routes/transactions.ts +1 -1
- package/src/apps/data/routes/transfers.test.ts +3 -3
- package/src/apps/data/routes/transfers.ts +12 -9
- package/src/apps/data/routes/webhooks.test.ts +179 -1497
- package/src/apps/data/routes/webhooks.ts +156 -976
- package/src/apps/data/routes/zones.test.ts +19 -13
- package/src/apps/funding/routes/chains.test.ts +211 -7
- package/src/apps/funding/routes/chains.ts +19 -12
- package/src/apps/funding/routes/deposit-addresses.test.ts +394 -0
- package/src/apps/funding/routes/deposit-addresses.ts +84 -72
- package/src/apps/funding/routes/providers.ts +9 -5
- package/src/apps/funding/routes/quotes.test.ts +46 -0
- package/src/apps/funding/routes/quotes.ts +8 -5
- package/src/apps/funding/routes/transfers.test.ts +35 -32
- package/src/apps/management/Billing.test.ts +26 -0
- package/src/apps/management/Billing.ts +24 -7
- package/src/apps/management/routes/api-keys.test.ts +178 -0
- package/src/apps/management/routes/api-keys.ts +203 -144
- package/src/apps/management/routes/billing.test.ts +209 -0
- package/src/apps/management/routes/billing.ts +254 -177
- package/src/apps/management/routes/faucet.test.ts +39 -0
- package/src/apps/management/routes/faucet.ts +75 -41
- package/src/apps/management/routes/invitations.test.ts +216 -0
- package/src/apps/management/routes/invitations.ts +186 -109
- package/src/apps/management/routes/invite-links.test.ts +263 -1
- package/src/apps/management/routes/invite-links.ts +295 -214
- package/src/apps/management/routes/me.test.ts +49 -0
- package/src/apps/management/routes/me.ts +42 -32
- package/src/apps/management/routes/members.test.ts +124 -1
- package/src/apps/management/routes/members.ts +81 -51
- package/src/apps/management/routes/orgs.test.ts +176 -0
- package/src/apps/management/routes/orgs.ts +101 -59
- package/src/apps/management/routes/projects.test.ts +183 -0
- package/src/apps/management/routes/projects.ts +124 -60
- package/src/apps/management/routes/scopes.test.ts +44 -0
- package/src/apps/management/routes/scopes.ts +37 -28
- package/src/apps/management/routes/usage.test.ts +92 -0
- package/src/apps/management/routes/usage.ts +186 -151
- package/src/apps/management/routes/verified-token-requests.test.ts +114 -0
- package/src/apps/management/routes/verified-token-requests.ts +166 -133
- package/src/apps/management/routes/webhooks.test.ts +313 -1
- package/src/apps/management/routes/webhooks.ts +220 -71
- package/src/apps/mpp/internal/Adapter.ts +13 -1
- package/src/db/Db.test.ts +42 -5
- package/src/db/Db.ts +85 -0
- package/src/db/Schema.ts +41 -3
- package/src/db/tables/earnVaults.test.ts +22 -0
- package/src/db/tables/earnVaults.ts +11 -1
- package/src/db/tables/organizations.test.ts +5 -2
- package/src/db/tables/webhookDeliveries.ts +13 -0
- package/src/db/tables/webhookQueueCompletions.ts +55 -0
- package/src/db/tables/webhookQueueEvents.ts +97 -57
- package/src/db/tables/webhookSubscriptions.ts +134 -144
- package/src/internal/Auth.ts +23 -5
- package/src/internal/Cache.ts +14 -7
- package/src/internal/EarnVaults.capabilities.test.ts +33 -0
- package/src/internal/EarnVaults.test.ts +79 -0
- package/src/internal/EarnVaults.ts +277 -0
- package/src/internal/MetricSink.test.ts +41 -373
- package/src/internal/MetricSink.ts +92 -273
- package/src/internal/OpenApi.ts +165 -49
- package/src/internal/Response.test.ts +95 -0
- package/src/internal/Response.ts +40 -2
- package/src/internal/Schema.test.ts +34 -0
- package/src/internal/Schema.ts +20 -9
- package/src/internal/Store.test.ts +186 -0
- package/src/internal/Store.ts +57 -17
- package/src/internal/Tidx.test.ts +19 -0
- package/src/internal/Tidx.ts +16 -4
- package/src/internal/WebhookTransfer.ts +1 -351
- package/src/internal/Webhooks.test.ts +222 -145
- package/src/internal/Webhooks.ts +234 -135
- package/src/internal/funding/Deposit.ts +71 -64
- package/src/internal/funding/DepositAddress.ts +50 -43
- package/src/internal/funding/DepositWebhook.test.ts +131 -0
- package/src/internal/funding/DepositWebhook.ts +101 -0
- package/src/internal/funding/Provider.ts +164 -142
- package/src/internal/funding/Reconciliation.test.ts +14 -0
- package/src/internal/funding/Reconciliation.ts +161 -86
- package/src/internal/funding/index.test.ts +1 -0
- package/src/internal/funding/index.ts +1 -0
- package/src/internal/funding/providers/index.test.ts +185 -0
- package/src/internal/funding/providers/stargate.ts +184 -0
- package/src/internal/webhookDestinations/slack.ts +15 -0
- package/src/providers/index.test.ts +1 -0
- package/src/providers/index.ts +9 -0
- package/src/Webhooks.bench.ts +0 -714
- package/src/Webhooks.memory.bench.ts +0 -250
- package/src/Webhooks.test.ts +0 -146
package/src/internal/OpenApi.ts
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import type { Context, Env, MiddlewareHandler, ValidationTargets } from 'hono'
|
|
2
2
|
import type { HasRequiredKeys } from 'hono/utils/types'
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
describeRoute as core_describeRoute,
|
|
5
|
+
type DescribeRouteOptions,
|
|
6
|
+
resolver,
|
|
7
|
+
type ResponsesWithResolver,
|
|
8
|
+
uniqueSymbol,
|
|
9
|
+
validator,
|
|
10
|
+
} from 'hono-openapi'
|
|
4
11
|
import * as z from 'zod/mini'
|
|
5
12
|
import * as Response from './Response.js'
|
|
6
13
|
import * as Schema from './Schema.js'
|
|
@@ -41,7 +48,6 @@ const schemaOptions = { options: { override: recoverExamples, unrepresentable: '
|
|
|
41
48
|
// re-exports because `uniqueSymbol` is a `unique symbol` whose identity is
|
|
42
49
|
// lost when copied through a `const` declaration.
|
|
43
50
|
export {
|
|
44
|
-
describeRoute,
|
|
45
51
|
type HandlerUniqueProperty,
|
|
46
52
|
resolver,
|
|
47
53
|
type ResponsesWithResolver,
|
|
@@ -49,12 +55,53 @@ export {
|
|
|
49
55
|
validator,
|
|
50
56
|
} from 'hono-openapi'
|
|
51
57
|
|
|
58
|
+
function cloneResponseContainers(spec: DescribeRouteOptions): DescribeRouteOptions {
|
|
59
|
+
if (!spec.responses) return spec
|
|
60
|
+
return {
|
|
61
|
+
...spec,
|
|
62
|
+
responses: Object.fromEntries(
|
|
63
|
+
Object.entries(spec.responses).map(([status, response]) => [
|
|
64
|
+
status,
|
|
65
|
+
!response || !('content' in response)
|
|
66
|
+
? response
|
|
67
|
+
: {
|
|
68
|
+
...response,
|
|
69
|
+
content: Object.fromEntries(
|
|
70
|
+
Object.entries(response.content ?? {}).map(([mediaType, media]) => [
|
|
71
|
+
mediaType,
|
|
72
|
+
{ ...media },
|
|
73
|
+
]),
|
|
74
|
+
),
|
|
75
|
+
},
|
|
76
|
+
]),
|
|
77
|
+
),
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Describes a route without allowing spec generation to mutate reusable response definitions. */
|
|
82
|
+
export function describeRoute(spec: DescribeRouteOptions): MiddlewareHandler {
|
|
83
|
+
const handler = core_describeRoute(spec)
|
|
84
|
+
Object.defineProperty(handler, uniqueSymbol, {
|
|
85
|
+
get: () => ({ spec: cloneResponseContainers(spec) }),
|
|
86
|
+
})
|
|
87
|
+
return handler
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Emits a named OpenAPI component and uses its `$ref` wherever the schema appears. */
|
|
91
|
+
export function component<const schema extends z.ZodMiniType>(
|
|
92
|
+
schema: schema,
|
|
93
|
+
name: string,
|
|
94
|
+
): schema {
|
|
95
|
+
const cloned = schema.clone()
|
|
96
|
+
z.globalRegistry.add(cloned, { ...z.globalRegistry.get(schema), ref: name })
|
|
97
|
+
return cloned as schema
|
|
98
|
+
}
|
|
99
|
+
|
|
52
100
|
/**
|
|
53
101
|
* Standard machine-readable `error.code` values per status, shared API-wide.
|
|
54
102
|
* The reference enumerates `error.code` to these so generated clients narrow it
|
|
55
|
-
* to a literal union instead of an open `string`.
|
|
56
|
-
*
|
|
57
|
-
* via the `codes` override in {@link responses.Options}.
|
|
103
|
+
* to a literal union instead of an open `string`. The values here are shared
|
|
104
|
+
* defaults; operation-specific codes are supplied through {@link responses.Options}.
|
|
58
105
|
*/
|
|
59
106
|
export const errorCodes = {
|
|
60
107
|
400: [
|
|
@@ -80,7 +127,7 @@ export const errorCodes = {
|
|
|
80
127
|
'api_key_malformed',
|
|
81
128
|
],
|
|
82
129
|
401: ['api_key_missing', 'api_key_invalid', 'unauthorized'],
|
|
83
|
-
403: ['api_key_forbidden', 'api_key_ip_forbidden', '
|
|
130
|
+
403: ['api_key_forbidden', 'api_key_ip_forbidden', 'forbidden'],
|
|
84
131
|
404: [
|
|
85
132
|
'not_found',
|
|
86
133
|
'token_not_found',
|
|
@@ -164,11 +211,78 @@ export const rateLimitHeaders = {
|
|
|
164
211
|
/** Representative `requestId` used in error-body examples. */
|
|
165
212
|
const exampleRequestId = '0a1b2c3d-4e5f-6071-8293-a4b5c6d7e8f9'
|
|
166
213
|
|
|
167
|
-
|
|
168
|
-
|
|
214
|
+
const standardErrorNames = {
|
|
215
|
+
400: 'InvalidRequestError',
|
|
216
|
+
401: 'AuthenticationError',
|
|
217
|
+
403: 'ForbiddenError',
|
|
218
|
+
404: 'ResourceNotFoundError',
|
|
219
|
+
429: 'RateLimitError',
|
|
220
|
+
500: 'InternalError',
|
|
221
|
+
501: 'NotImplementedError',
|
|
222
|
+
502: 'UpstreamError',
|
|
223
|
+
} as const satisfies Record<keyof typeof errorCodes, string>
|
|
224
|
+
|
|
225
|
+
const reservedErrorNames = new Set<string>(Object.values(standardErrorNames))
|
|
226
|
+
const errorSchemas = new Map<string, z.ZodMiniType>()
|
|
227
|
+
|
|
228
|
+
function errorName(status: number, codes: readonly string[]) {
|
|
229
|
+
const standard = errorCodes[status as keyof typeof errorCodes]
|
|
230
|
+
if (
|
|
231
|
+
standard &&
|
|
232
|
+
standard.length === codes.length &&
|
|
233
|
+
[...standard].sort().every((code, index) => code === codes[index])
|
|
234
|
+
)
|
|
235
|
+
return standardErrorNames[status as keyof typeof standardErrorNames]
|
|
236
|
+
const name = `${codes
|
|
237
|
+
.map((code) =>
|
|
238
|
+
code
|
|
239
|
+
.split('_')
|
|
240
|
+
.map((part) => `${part[0]?.toUpperCase()}${part.slice(1)}`)
|
|
241
|
+
.join(''),
|
|
242
|
+
)
|
|
243
|
+
.join('Or')}Error`
|
|
244
|
+
if (reservedErrorNames.has(name)) return `${name.slice(0, -'Error'.length)}${status}Error`
|
|
245
|
+
return name
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function errorSchema(status: number, codes: readonly [string, ...string[]] | undefined) {
|
|
249
|
+
const defaults = errorCodes[status as keyof typeof errorCodes]
|
|
250
|
+
if (!codes && !defaults) {
|
|
251
|
+
const cached = errorSchemas.get('*')
|
|
252
|
+
if (cached) return cached
|
|
253
|
+
const schema = component(Schema.errorResponse(), 'ErrorResponse')
|
|
254
|
+
errorSchemas.set('*', schema)
|
|
255
|
+
return schema
|
|
256
|
+
}
|
|
257
|
+
const normalized = [...(codes ?? defaults ?? [])].sort() as [string, ...string[]]
|
|
258
|
+
const key = normalized.join('\0')
|
|
259
|
+
const cached = errorSchemas.get(key)
|
|
260
|
+
if (cached) return cached
|
|
261
|
+
const schema = component(Schema.errorResponse(normalized), errorName(status, normalized))
|
|
262
|
+
errorSchemas.set(key, schema)
|
|
263
|
+
return schema
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function mergeErrorCodes(
|
|
267
|
+
status: number,
|
|
268
|
+
codes: readonly string[] | undefined,
|
|
269
|
+
): readonly [string, ...string[]] | undefined {
|
|
270
|
+
if (!codes) return errorCodes[status as keyof typeof errorCodes]
|
|
271
|
+
if (status !== 400 && status !== 403)
|
|
272
|
+
return codes.length ? (codes as [string, ...string[]]) : undefined
|
|
273
|
+
const baseline = status === 400 ? (['api_key_malformed'] as const) : errorCodes[403]
|
|
274
|
+
return [...new Set([...baseline, ...codes])] as [string, ...string[]]
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/** Builds the `application/json` content for a standard error response: the named enumerated-code envelope plus an example body. */
|
|
278
|
+
function errorContent(
|
|
279
|
+
status: number,
|
|
280
|
+
codes: readonly [string, ...string[]] | undefined,
|
|
281
|
+
message: string,
|
|
282
|
+
) {
|
|
169
283
|
return {
|
|
170
284
|
'application/json': {
|
|
171
|
-
schema: resolver(
|
|
285
|
+
schema: resolver(errorSchema(status, codes), schemaOptions),
|
|
172
286
|
example: { error: { code: codes?.[0] ?? 'error', message }, requestId: exampleRequestId },
|
|
173
287
|
},
|
|
174
288
|
}
|
|
@@ -186,32 +300,30 @@ export const paymentChallenge = {
|
|
|
186
300
|
headers: { 'WWW-Authenticate': headerRef('WwwAuthenticate') },
|
|
187
301
|
} as const
|
|
188
302
|
|
|
189
|
-
|
|
190
|
-
const rateLimitedResponse = {
|
|
191
|
-
content: errorContent(errorCodes[429], 'Rate limit exceeded.'),
|
|
192
|
-
description:
|
|
193
|
-
'Rate limit exceeded. On endpoints that accept MPP, over-quota unpaid requests return `402 Payment Required` instead.',
|
|
194
|
-
headers: { ...rateLimitHeaders },
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/** Shared `500` internal-error response, registered as `components.responses.InternalError`. */
|
|
198
|
-
const serverErrorResponse = {
|
|
199
|
-
content: errorContent(errorCodes[500], 'Internal server error.'),
|
|
200
|
-
description: 'Internal server error.',
|
|
201
|
-
}
|
|
303
|
+
type ResponseComponentName = 'InternalError' | 'PaymentRequired' | 'RateLimited'
|
|
202
304
|
|
|
203
305
|
/**
|
|
204
306
|
* Reusable responses registered under `components.responses` (see `App.create`)
|
|
205
307
|
* and `$ref`-erenced from operations, so the uniform `402`/`429`/`500` responses
|
|
206
308
|
* are defined once instead of inlined on every operation.
|
|
207
309
|
*/
|
|
208
|
-
export
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
310
|
+
export function responseComponents(): ResponsesWithResolver {
|
|
311
|
+
return {
|
|
312
|
+
InternalError: {
|
|
313
|
+
content: errorContent(500, errorCodes[500], 'Internal server error.'),
|
|
314
|
+
description: 'Internal server error.',
|
|
315
|
+
},
|
|
316
|
+
PaymentRequired: paymentChallenge,
|
|
317
|
+
RateLimited: {
|
|
318
|
+
content: errorContent(429, errorCodes[429], 'Rate limit exceeded.'),
|
|
319
|
+
description:
|
|
320
|
+
'Rate limit exceeded. On endpoints that accept MPP, over-quota unpaid requests return `402 Payment Required` instead.',
|
|
321
|
+
headers: { ...rateLimitHeaders },
|
|
322
|
+
},
|
|
323
|
+
}
|
|
212
324
|
}
|
|
213
325
|
|
|
214
|
-
const responseRef = (name:
|
|
326
|
+
const responseRef = (name: ResponseComponentName) => ({
|
|
215
327
|
$ref: `#/components/responses/${name}`,
|
|
216
328
|
})
|
|
217
329
|
|
|
@@ -232,22 +344,22 @@ export function standardError(
|
|
|
232
344
|
): ResponsesWithResolver[string] {
|
|
233
345
|
if (status === 429) return responseRef('RateLimited')
|
|
234
346
|
if (status === 500) return responseRef('InternalError')
|
|
235
|
-
return { content: errorContent(
|
|
347
|
+
return { content: errorContent(status, mergeErrorCodes(status, codes), description), description }
|
|
236
348
|
}
|
|
237
349
|
|
|
238
350
|
/**
|
|
239
351
|
* Builds an OpenAPI `responses` map with the API's standard error responses
|
|
240
352
|
* pre-filled, plus the success response (with rate-limit headers):
|
|
241
353
|
*
|
|
242
|
-
* - `400` invalid request, `401` missing/invalid key, `
|
|
243
|
-
* `500` internal error, and `502` upstream failure on every operation;
|
|
244
|
-
* - `
|
|
354
|
+
* - `400` invalid request, `401` missing/invalid key, `403` forbidden, `429`
|
|
355
|
+
* rate limited, `500` internal error, and `502` upstream failure on every operation;
|
|
356
|
+
* - `404`/`409`/`412`/`413`/`415` only when an `errors` entry is set.
|
|
245
357
|
*
|
|
246
|
-
* `error.code` is enumerated per status from {@link errorCodes}
|
|
247
|
-
*
|
|
248
|
-
* `429
|
|
249
|
-
*
|
|
250
|
-
*
|
|
358
|
+
* `error.code` is enumerated per status from {@link errorCodes}. Operation-level
|
|
359
|
+
* 400/403 codes merge with shared auth codes; other overrides replace defaults.
|
|
360
|
+
* The default `429` and `500` `$ref` shared {@link responseComponents}; an exact
|
|
361
|
+
* operation-level `429` override is emitted inline with the standard headers.
|
|
362
|
+
* `402` is injected per operation from the route's MPP policy in `App.ts`.
|
|
251
363
|
*/
|
|
252
364
|
export function responses<schema extends z.ZodMiniType>(
|
|
253
365
|
options: responses.Options<schema>,
|
|
@@ -257,10 +369,11 @@ export function responses<schema extends z.ZodMiniType>(
|
|
|
257
369
|
const override = errors?.[status as 400]
|
|
258
370
|
const description =
|
|
259
371
|
typeof override === 'string' ? override : (override?.description ?? fallback)
|
|
260
|
-
const codes =
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
372
|
+
const codes = mergeErrorCodes(status, typeof override === 'object' ? override.codes : undefined)
|
|
373
|
+
return {
|
|
374
|
+
content: errorContent(status, codes, description),
|
|
375
|
+
description,
|
|
376
|
+
}
|
|
264
377
|
}
|
|
265
378
|
return {
|
|
266
379
|
200: {
|
|
@@ -279,13 +392,15 @@ export function responses<schema extends z.ZodMiniType>(
|
|
|
279
392
|
},
|
|
280
393
|
400: error(400, 'Invalid request.'),
|
|
281
394
|
401: error(401, 'Missing or invalid API key.'),
|
|
282
|
-
|
|
395
|
+
403: error(403, 'Forbidden.'),
|
|
283
396
|
...(errors?.[404] && { 404: error(404, 'Not found.') }),
|
|
284
397
|
...(errors?.[409] && { 409: error(409, 'Conflict.') }),
|
|
285
398
|
...(errors?.[412] && { 412: error(412, 'Precondition failed.') }),
|
|
286
399
|
...(errors?.[413] && { 413: error(413, 'Payload too large.') }),
|
|
287
400
|
...(errors?.[415] && { 415: error(415, 'Unsupported media type.') }),
|
|
288
|
-
429:
|
|
401
|
+
429: errors?.[429]
|
|
402
|
+
? { ...error(429, 'Rate limit exceeded.'), headers: { ...rateLimitHeaders } }
|
|
403
|
+
: responseRef('RateLimited'),
|
|
289
404
|
500: responseRef('InternalError'),
|
|
290
405
|
...(errors?.[501] && { 501: error(501, 'Not implemented.') }),
|
|
291
406
|
502: error(502, 'Upstream data failure.'),
|
|
@@ -295,23 +410,22 @@ export function responses<schema extends z.ZodMiniType>(
|
|
|
295
410
|
export declare namespace responses {
|
|
296
411
|
/**
|
|
297
412
|
* Override for a standard error response: a description string, or an object
|
|
298
|
-
* adding
|
|
413
|
+
* adding operation codes. Codes replace defaults except shared 400/403 auth codes.
|
|
299
414
|
*/
|
|
300
415
|
type ErrorOverride =
|
|
301
416
|
| string
|
|
302
417
|
| {
|
|
303
418
|
/** Human-readable description of the error response. */
|
|
304
419
|
description?: string | undefined
|
|
305
|
-
/**
|
|
306
|
-
codes?: readonly
|
|
420
|
+
/** Operation-specific `error.code` values for this status. */
|
|
421
|
+
codes?: readonly string[] | undefined
|
|
307
422
|
}
|
|
308
423
|
|
|
309
424
|
/** Options for building a standard OpenAPI responses map. */
|
|
310
425
|
type Options<schema> = {
|
|
311
426
|
/**
|
|
312
|
-
* Optional overrides for standard error responses
|
|
313
|
-
* `
|
|
314
|
-
* includes that status in the map.
|
|
427
|
+
* Optional overrides for standard error responses. Setting
|
|
428
|
+
* `404`/`409`/`412`/`413`/`415` includes that status in the map.
|
|
315
429
|
*/
|
|
316
430
|
errors?:
|
|
317
431
|
| {
|
|
@@ -319,7 +433,7 @@ export declare namespace responses {
|
|
|
319
433
|
400?: ErrorOverride | undefined
|
|
320
434
|
/** Override for the 401 response. */
|
|
321
435
|
401?: ErrorOverride | undefined
|
|
322
|
-
/**
|
|
436
|
+
/** Add operation-specific 403 codes or override its description. */
|
|
323
437
|
403?: ErrorOverride | undefined
|
|
324
438
|
/** Override for the 404 response. Setting this includes 404 in the map. */
|
|
325
439
|
404?: ErrorOverride | undefined
|
|
@@ -331,6 +445,8 @@ export declare namespace responses {
|
|
|
331
445
|
413?: ErrorOverride | undefined
|
|
332
446
|
/** Override for the 415 response. Setting this includes 415 in the map. */
|
|
333
447
|
415?: ErrorOverride | undefined
|
|
448
|
+
/** Override the shared 429 response with operation-specific rate-limit codes. */
|
|
449
|
+
429?: ErrorOverride | undefined
|
|
334
450
|
/** Override for the 501 response. Setting this includes 501 in the map. */
|
|
335
451
|
501?: ErrorOverride | undefined
|
|
336
452
|
/** Override for the 502 response. */
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Hono } from 'hono'
|
|
2
|
+
import { Tidx } from 'tidx.ts'
|
|
2
3
|
|
|
3
4
|
import * as Response from './Response.js'
|
|
4
5
|
|
|
@@ -25,4 +26,98 @@ describe('upstream', () => {
|
|
|
25
26
|
`)
|
|
26
27
|
expect(captured).toBe(cause)
|
|
27
28
|
})
|
|
29
|
+
|
|
30
|
+
test('returns a query error when a positional page times out', async () => {
|
|
31
|
+
const app = new Hono()
|
|
32
|
+
const cause = new Tidx.FetchRequestError(
|
|
33
|
+
'Request timeout',
|
|
34
|
+
new globalThis.Response(null, { status: 408 }),
|
|
35
|
+
)
|
|
36
|
+
let captured: Error | undefined
|
|
37
|
+
|
|
38
|
+
app.use(async (c, next) => {
|
|
39
|
+
await next()
|
|
40
|
+
captured = c.error
|
|
41
|
+
})
|
|
42
|
+
app.get('/', (c) => Response.upstream(c, cause, { page: 2 }))
|
|
43
|
+
|
|
44
|
+
const response = await app.request('/')
|
|
45
|
+
expect(response.status).toMatchInlineSnapshot(`400`)
|
|
46
|
+
expect(await response.json()).toMatchInlineSnapshot(`
|
|
47
|
+
{
|
|
48
|
+
"error": {
|
|
49
|
+
"code": "query_invalid",
|
|
50
|
+
"message": "The requested page timed out. Use cursor pagination for deep traversal.",
|
|
51
|
+
},
|
|
52
|
+
}
|
|
53
|
+
`)
|
|
54
|
+
expect(captured).toBe(cause)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
test('returns a query error when a positional page is rejected', async () => {
|
|
58
|
+
const app = new Hono()
|
|
59
|
+
const cause = new Tidx.FetchRequestError(
|
|
60
|
+
'Query rejected',
|
|
61
|
+
new globalThis.Response(null, { status: 422 }),
|
|
62
|
+
)
|
|
63
|
+
let captured: Error | undefined
|
|
64
|
+
|
|
65
|
+
app.use(async (c, next) => {
|
|
66
|
+
await next()
|
|
67
|
+
captured = c.error
|
|
68
|
+
})
|
|
69
|
+
app.get('/', (c) => Response.upstream(c, cause, { page: 584 }))
|
|
70
|
+
|
|
71
|
+
const response = await app.request('/')
|
|
72
|
+
expect(response.status).toMatchInlineSnapshot(`400`)
|
|
73
|
+
expect(await response.json()).toMatchInlineSnapshot(`
|
|
74
|
+
{
|
|
75
|
+
"error": {
|
|
76
|
+
"code": "query_invalid",
|
|
77
|
+
"message": "The requested page was rejected. Use cursor pagination for deep traversal.",
|
|
78
|
+
},
|
|
79
|
+
}
|
|
80
|
+
`)
|
|
81
|
+
expect(captured).toBe(cause)
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
test('keeps timeouts without a deep positional page as upstream errors', async () => {
|
|
85
|
+
const app = new Hono()
|
|
86
|
+
const cause = new Tidx.FetchRequestError(
|
|
87
|
+
'Request timeout',
|
|
88
|
+
new globalThis.Response(null, { status: 408 }),
|
|
89
|
+
)
|
|
90
|
+
app.get('/', (c) => Response.upstream(c, cause, { page: undefined }))
|
|
91
|
+
|
|
92
|
+
const response = await app.request('/')
|
|
93
|
+
expect(response.status).toMatchInlineSnapshot(`502`)
|
|
94
|
+
expect(await response.json()).toMatchInlineSnapshot(`
|
|
95
|
+
{
|
|
96
|
+
"error": {
|
|
97
|
+
"code": "upstream_error",
|
|
98
|
+
"message": "Upstream service could not complete the request",
|
|
99
|
+
},
|
|
100
|
+
}
|
|
101
|
+
`)
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
test('keeps query rejections without a deep positional page as upstream errors', async () => {
|
|
105
|
+
const app = new Hono()
|
|
106
|
+
const cause = new Tidx.FetchRequestError(
|
|
107
|
+
'Query rejected',
|
|
108
|
+
new globalThis.Response(null, { status: 422 }),
|
|
109
|
+
)
|
|
110
|
+
app.get('/', (c) => Response.upstream(c, cause, { page: undefined }))
|
|
111
|
+
|
|
112
|
+
const response = await app.request('/')
|
|
113
|
+
expect(response.status).toMatchInlineSnapshot(`502`)
|
|
114
|
+
expect(await response.json()).toMatchInlineSnapshot(`
|
|
115
|
+
{
|
|
116
|
+
"error": {
|
|
117
|
+
"code": "upstream_error",
|
|
118
|
+
"message": "Upstream service could not complete the request",
|
|
119
|
+
},
|
|
120
|
+
}
|
|
121
|
+
`)
|
|
122
|
+
})
|
|
28
123
|
})
|
package/src/internal/Response.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { Context } from 'hono'
|
|
2
2
|
import type * as z from 'zod/mini'
|
|
3
3
|
|
|
4
|
+
import * as Tidx from './Tidx.js'
|
|
5
|
+
|
|
4
6
|
/** Creates a JSON error response. */
|
|
5
7
|
export function error<const status extends error.Status, const code extends string>(
|
|
6
8
|
c: Context,
|
|
@@ -39,14 +41,34 @@ export function unsupportedChainId(c: Context, chainId: number, supported: Itera
|
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
/**
|
|
42
|
-
* Maps
|
|
44
|
+
* Maps upstream failures to 502, except TIDX query failures after the first positional page return 400.
|
|
43
45
|
* Preserves `Error` causes for reporting and rethrows non-`Error` causes.
|
|
44
46
|
*/
|
|
45
|
-
export function upstream(c: Context, cause: unknown)
|
|
47
|
+
export function upstream(c: Context, cause: unknown): upstream.Response
|
|
48
|
+
export function upstream(
|
|
49
|
+
c: Context,
|
|
50
|
+
cause: unknown,
|
|
51
|
+
options: upstream.Options,
|
|
52
|
+
): upstream.PageResponse
|
|
53
|
+
export function upstream(c: Context, cause: unknown, options?: upstream.Options) {
|
|
46
54
|
if (cause instanceof Error) {
|
|
47
55
|
// Preserve the original error for error-reporting middleware without
|
|
48
56
|
// exposing provider URLs, payloads, or credentials in the response.
|
|
49
57
|
c.error = cause
|
|
58
|
+
if (options?.page !== undefined && options.page > 1) {
|
|
59
|
+
if (Tidx.isQueryTimeout(cause))
|
|
60
|
+
return error(c, {
|
|
61
|
+
code: 'query_invalid',
|
|
62
|
+
message: 'The requested page timed out. Use cursor pagination for deep traversal.',
|
|
63
|
+
status: 400,
|
|
64
|
+
})
|
|
65
|
+
if (Tidx.isQueryRejection(cause))
|
|
66
|
+
return error(c, {
|
|
67
|
+
code: 'query_invalid',
|
|
68
|
+
message: 'The requested page was rejected. Use cursor pagination for deep traversal.',
|
|
69
|
+
status: 400,
|
|
70
|
+
})
|
|
71
|
+
}
|
|
50
72
|
return error(c, {
|
|
51
73
|
code: 'upstream_error',
|
|
52
74
|
message: 'Upstream service could not complete the request',
|
|
@@ -56,6 +78,22 @@ export function upstream(c: Context, cause: unknown) {
|
|
|
56
78
|
throw cause
|
|
57
79
|
}
|
|
58
80
|
|
|
81
|
+
export declare namespace upstream {
|
|
82
|
+
/** Pagination context for classifying a deep-page query failure. */
|
|
83
|
+
type Options = {
|
|
84
|
+
/** Validated positional page number, when requested. */
|
|
85
|
+
page: number | undefined
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Upstream response for routes that support positional pagination. */
|
|
89
|
+
type PageResponse =
|
|
90
|
+
| ReturnType<typeof error<400, 'query_invalid'>>
|
|
91
|
+
| ReturnType<typeof error<502, 'upstream_error'>>
|
|
92
|
+
|
|
93
|
+
/** Default upstream error response. */
|
|
94
|
+
type Response = ReturnType<typeof error<502, 'upstream_error'>>
|
|
95
|
+
}
|
|
96
|
+
|
|
59
97
|
/**
|
|
60
98
|
* Validates `value` against `schema`. Returns the parsed value on success or
|
|
61
99
|
* throws a generic upstream error so the public response body never leaks Zod
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import * as z from 'zod/mini'
|
|
2
|
+
|
|
1
3
|
import * as Schema from './Schema.js'
|
|
2
4
|
|
|
3
5
|
describe('ChainId', () => {
|
|
@@ -33,6 +35,38 @@ describe('ChainIdQuery', () => {
|
|
|
33
35
|
})
|
|
34
36
|
})
|
|
35
37
|
|
|
38
|
+
describe('Limit', () => {
|
|
39
|
+
test('defaults to 10 and accepts page sizes through 50', () => {
|
|
40
|
+
expect(Schema.Limit.parse(undefined)).toMatchInlineSnapshot(`10`)
|
|
41
|
+
expect(Schema.Limit.parse('5')).toMatchInlineSnapshot(`5`)
|
|
42
|
+
expect(Schema.Limit.parse('50')).toMatchInlineSnapshot(`50`)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
test('caps larger page sizes at 50', () => {
|
|
46
|
+
expect(Schema.Limit.parse('51')).toMatchInlineSnapshot(`50`)
|
|
47
|
+
expect(Schema.Limit.parse('200')).toMatchInlineSnapshot(`50`)
|
|
48
|
+
expect(Schema.Limit.parse('10000')).toMatchInlineSnapshot(`50`)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
test('rejects page sizes below the minimum', () => {
|
|
52
|
+
expect(Schema.Limit.safeParse('4').success).toMatchInlineSnapshot(`false`)
|
|
53
|
+
})
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
describe('pageChecks', () => {
|
|
57
|
+
const Query = z
|
|
58
|
+
.object({ cursor: Schema.Cursor, limit: Schema.Limit, page: Schema.Page })
|
|
59
|
+
.check(...Schema.pageChecks())
|
|
60
|
+
|
|
61
|
+
test('accepts positional pages through the 500-row window', () => {
|
|
62
|
+
expect(Query.safeParse({ limit: '10', page: '50' }).success).toMatchInlineSnapshot(`true`)
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
test('rejects deeper positional pages', () => {
|
|
66
|
+
expect(Query.safeParse({ limit: '10', page: '51' }).success).toMatchInlineSnapshot(`false`)
|
|
67
|
+
})
|
|
68
|
+
})
|
|
69
|
+
|
|
36
70
|
describe('timestampBound', () => {
|
|
37
71
|
test('normalizes RFC 3339 offsets to UTC', () => {
|
|
38
72
|
const bound = Schema.timestampBound('swaps', 'from')
|
package/src/internal/Schema.ts
CHANGED
|
@@ -191,6 +191,8 @@ export const ErrorDetail = z
|
|
|
191
191
|
})
|
|
192
192
|
.check(z.describe('A single problem with the request.'))
|
|
193
193
|
|
|
194
|
+
z.globalRegistry.add(ErrorDetail, { ...z.globalRegistry.get(ErrorDetail), ref: 'ErrorDetail' })
|
|
195
|
+
|
|
194
196
|
/**
|
|
195
197
|
* Builds the global error envelope, optionally enumerating `error.code` to a
|
|
196
198
|
* fixed set of machine-readable codes. Pass the codes a given status can return
|
|
@@ -337,11 +339,22 @@ export const TokenAmount = describe(
|
|
|
337
339
|
*/
|
|
338
340
|
export const minLimit = 5
|
|
339
341
|
|
|
342
|
+
/** Maximum number of rows returned per page. Larger requests are clamped to this value. */
|
|
343
|
+
export const maxLimit = 50
|
|
344
|
+
|
|
340
345
|
/** Page-size query parameter shared across list endpoints. */
|
|
341
346
|
export const Limit = z
|
|
342
|
-
._default(
|
|
347
|
+
._default(
|
|
348
|
+
z.pipe(
|
|
349
|
+
z.coerce.number().check(z.int(), z.gte(minLimit)),
|
|
350
|
+
z.transform((value) => Math.min(value, maxLimit)),
|
|
351
|
+
),
|
|
352
|
+
10,
|
|
353
|
+
)
|
|
343
354
|
.check(
|
|
344
|
-
z.describe(
|
|
355
|
+
z.describe(
|
|
356
|
+
'How many items to return per page (5\u201350, default 10). Use `nextCursor` to fetch more.',
|
|
357
|
+
),
|
|
345
358
|
z.meta({ examples: [10] }),
|
|
346
359
|
)
|
|
347
360
|
|
|
@@ -373,12 +386,10 @@ export const Cursor = z
|
|
|
373
386
|
|
|
374
387
|
/**
|
|
375
388
|
* Maximum page window (`page × limit` rows) accepted by positional pagination.
|
|
376
|
-
*
|
|
377
|
-
*
|
|
378
|
-
* cap): shallow page-numbered access is served positionally, deep traversal
|
|
379
|
-
* must use cursors.
|
|
389
|
+
* Stays below the indexer's own 10k row ceiling so large offsets do not reach
|
|
390
|
+
* the indexer. Deeper traversal must use cursors.
|
|
380
391
|
*/
|
|
381
|
-
export const maxPageWindow =
|
|
392
|
+
export const maxPageWindow = 500
|
|
382
393
|
|
|
383
394
|
/**
|
|
384
395
|
* Page-number query parameter (1-indexed) shared by list endpoints that
|
|
@@ -432,8 +443,8 @@ export const NextCursor = z
|
|
|
432
443
|
* count is computed by a capped subquery (`… LIMIT countCap`) so it can't blow
|
|
433
444
|
* the indexer's execution budget on the 10⁹-plus row tables; when the bound is
|
|
434
445
|
* hit the count is a lower bound, signalled by `Meta.totalCountCapped`. Mirrors the
|
|
435
|
-
* indexer's own 10k row ceiling
|
|
436
|
-
*
|
|
446
|
+
* indexer's own 10k row ceiling and the capped-count conventions of comparable
|
|
447
|
+
* APIs (Stripe Search's 10k `total_count`,
|
|
437
448
|
* Elasticsearch `track_total_hits`, Shopify's `Count.precision`).
|
|
438
449
|
*/
|
|
439
450
|
export const countCap = 10_000
|