tapimo 0.19.0 → 0.20.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 +13 -5
- package/dist/ApiKey.d.ts +2 -2
- package/dist/ApiKey.d.ts.map +1 -1
- package/dist/ApiKey.js +2 -2
- package/dist/ApiKey.js.map +1 -1
- package/dist/ApiKeys.d.ts +85 -11
- package/dist/ApiKeys.d.ts.map +1 -1
- package/dist/ApiKeys.js +218 -56
- package/dist/ApiKeys.js.map +1 -1
- package/dist/App.d.ts +12543 -13912
- package/dist/App.d.ts.map +1 -1
- package/dist/App.js +4 -5
- package/dist/App.js.map +1 -1
- package/dist/Scope.d.ts +12 -20
- package/dist/Scope.d.ts.map +1 -1
- package/dist/Scope.js +20 -36
- package/dist/Scope.js.map +1 -1
- package/dist/admin/App.d.ts +871 -828
- package/dist/admin/App.d.ts.map +1 -1
- package/dist/admin/App.js +14 -26
- package/dist/admin/App.js.map +1 -1
- package/dist/admin/apps/api-keys.d.ts +93 -186
- package/dist/admin/apps/api-keys.d.ts.map +1 -1
- package/dist/admin/apps/api-keys.js +3 -5
- package/dist/admin/apps/api-keys.js.map +1 -1
- package/dist/admin/apps/audit-logs.d.ts +18 -18
- package/dist/admin/apps/audit-logs.js +1 -1
- package/dist/admin/apps/audit-logs.js.map +1 -1
- package/dist/admin/apps/earn-vaults.d.ts +27 -27
- package/dist/admin/apps/earn-vaults.d.ts.map +1 -1
- package/dist/admin/apps/earn-vaults.js +11 -5
- package/dist/admin/apps/earn-vaults.js.map +1 -1
- package/dist/admin/apps/organizations.d.ts +17 -17
- package/dist/admin/apps/prop-amms.d.ts +4 -4
- package/dist/admin/apps/routes-support.js +1 -38
- package/dist/admin/apps/routes-support.js.map +1 -1
- package/dist/admin/apps/routes.d.ts +153 -153
- package/dist/admin/apps/sentinel.d.ts +4 -4
- package/dist/admin/apps/verified-tokens.d.ts +24 -24
- package/dist/admin/apps/zone-delegation-requests.d.ts +332 -0
- package/dist/admin/apps/zone-delegation-requests.d.ts.map +1 -0
- package/dist/admin/apps/zone-delegation-requests.js +200 -0
- package/dist/admin/apps/zone-delegation-requests.js.map +1 -0
- package/dist/admin/apps/zone-delegations.d.ts +512 -52
- package/dist/admin/apps/zone-delegations.d.ts.map +1 -1
- package/dist/admin/apps/zone-delegations.js +134 -44
- package/dist/admin/apps/zone-delegations.js.map +1 -1
- package/dist/admin/index.d.ts +1 -1
- package/dist/admin/index.d.ts.map +1 -1
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/ui.generated.d.ts +1 -1
- package/dist/admin/ui.generated.d.ts.map +1 -1
- package/dist/admin/ui.generated.js +341 -397
- package/dist/admin/ui.generated.js.map +1 -1
- package/dist/analytics/tables/requestEvents.d.ts.map +1 -1
- package/dist/analytics/tables/requestEvents.js +4 -2
- package/dist/analytics/tables/requestEvents.js.map +1 -1
- package/dist/apps/data/App.d.ts +11932 -13214
- package/dist/apps/data/App.d.ts.map +1 -1
- package/dist/apps/data/App.js +32 -13
- package/dist/apps/data/App.js.map +1 -1
- package/dist/apps/data/ZoneSelection.d.ts +1 -1
- package/dist/apps/data/ZoneSelection.d.ts.map +1 -1
- package/dist/apps/data/ZoneSelection.js +2 -2
- package/dist/apps/data/ZoneSelection.js.map +1 -1
- package/dist/apps/data/routes/activities.d.ts +60 -27
- package/dist/apps/data/routes/activities.d.ts.map +1 -1
- package/dist/apps/data/routes/activities.js +91 -16
- package/dist/apps/data/routes/activities.js.map +1 -1
- package/dist/apps/data/routes/addresses.d.ts +893 -875
- package/dist/apps/data/routes/addresses.d.ts.map +1 -1
- package/dist/apps/data/routes/balances.d.ts +36 -36
- package/dist/apps/data/routes/balances.d.ts.map +1 -1
- package/dist/apps/data/routes/balances.js +3 -1
- package/dist/apps/data/routes/balances.js.map +1 -1
- package/dist/apps/data/routes/blocks.d.ts +40 -40
- package/dist/apps/data/routes/blocks.d.ts.map +1 -1
- package/dist/apps/data/routes/blocks.js +3 -7
- package/dist/apps/data/routes/blocks.js.map +1 -1
- package/dist/apps/data/routes/coingecko.d.ts +236 -236
- package/dist/apps/data/routes/earn-transactions.d.ts +14 -14
- package/dist/apps/data/routes/earn.d.ts +261 -1445
- package/dist/apps/data/routes/earn.d.ts.map +1 -1
- package/dist/apps/data/routes/earn.js +64 -490
- package/dist/apps/data/routes/earn.js.map +1 -1
- package/dist/apps/data/routes/exchanges.d.ts +144 -144
- package/dist/apps/data/routes/exchanges.d.ts.map +1 -1
- package/dist/apps/data/routes/exchanges.js +5 -15
- package/dist/apps/data/routes/exchanges.js.map +1 -1
- package/dist/apps/data/routes/fee-amm.d.ts.map +1 -1
- package/dist/apps/data/routes/fee-amm.js +3 -9
- package/dist/apps/data/routes/fee-amm.js.map +1 -1
- package/dist/apps/data/routes/indexer.d.ts +13 -13
- package/dist/apps/data/routes/receipts.d.ts +53 -53
- 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.d.ts +52 -0
- package/dist/apps/data/routes/rpc.d.ts.map +1 -1
- package/dist/apps/data/routes/rpc.js +19 -18
- package/dist/apps/data/routes/rpc.js.map +1 -1
- package/dist/apps/data/routes/tokenlist.d.ts +14 -14
- package/dist/apps/data/routes/tokenlist.d.ts.map +1 -1
- package/dist/apps/data/routes/tokenlist.js +1 -3
- package/dist/apps/data/routes/tokenlist.js.map +1 -1
- package/dist/apps/data/routes/tokens.d.ts.map +1 -1
- package/dist/apps/data/routes/tokens.js +7 -5
- package/dist/apps/data/routes/tokens.js.map +1 -1
- package/dist/apps/data/routes/transactions.d.ts +281 -281
- package/dist/apps/data/routes/transactions.js +3 -3
- package/dist/apps/data/routes/transactions.js.map +1 -1
- package/dist/apps/data/routes/transfers.d.ts +12 -12
- package/dist/apps/data/routes/transfers.d.ts.map +1 -1
- package/dist/apps/data/routes/transfers.js +1 -3
- 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/valuation.d.ts.map +1 -1
- package/dist/apps/data/routes/valuation.js +1 -3
- package/dist/apps/data/routes/valuation.js.map +1 -1
- package/dist/apps/data/routes/verified-tokens.d.ts +86 -86
- package/dist/apps/data/routes/webhooks.d.ts +102 -102
- package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
- package/dist/apps/data/routes/webhooks.js +9 -23
- package/dist/apps/data/routes/webhooks.js.map +1 -1
- package/dist/apps/data/routes/zone-deposits.d.ts +16 -16
- package/dist/apps/data/routes/zone-withdrawals.d.ts +31 -31
- package/dist/apps/data/routes/zones.d.ts +28 -28
- package/dist/apps/management/App.d.ts +18 -102
- package/dist/apps/management/App.d.ts.map +1 -1
- package/dist/apps/management/App.js +1 -1
- package/dist/apps/management/App.js.map +1 -1
- package/dist/apps/management/routes/api-keys.d.ts +253 -339
- package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
- package/dist/apps/management/routes/api-keys.js +25 -11
- package/dist/apps/management/routes/api-keys.js.map +1 -1
- package/dist/apps/management/routes/billing.d.ts +92 -92
- package/dist/apps/management/routes/billing.d.ts.map +1 -1
- package/dist/apps/management/routes/billing.js +5 -13
- package/dist/apps/management/routes/billing.js.map +1 -1
- package/dist/apps/management/routes/cli.d.ts.map +1 -1
- package/dist/apps/management/routes/cli.js +3 -9
- package/dist/apps/management/routes/cli.js.map +1 -1
- package/dist/apps/management/routes/faucet.d.ts +16 -16
- package/dist/apps/management/routes/fee-payer-usage.d.ts +38 -38
- package/dist/apps/management/routes/fee-payer-usage.d.ts.map +1 -1
- package/dist/apps/management/routes/fee-payer-usage.js +2 -4
- package/dist/apps/management/routes/fee-payer-usage.js.map +1 -1
- package/dist/apps/management/routes/invitations.d.ts +52 -52
- package/dist/apps/management/routes/invitations.d.ts.map +1 -1
- package/dist/apps/management/routes/invitations.js +4 -12
- package/dist/apps/management/routes/invitations.js.map +1 -1
- package/dist/apps/management/routes/invite-links.d.ts.map +1 -1
- package/dist/apps/management/routes/invite-links.js +6 -14
- package/dist/apps/management/routes/invite-links.js.map +1 -1
- package/dist/apps/management/routes/me.d.ts +6 -6
- package/dist/apps/management/routes/me.d.ts.map +1 -1
- package/dist/apps/management/routes/me.js +3 -7
- package/dist/apps/management/routes/me.js.map +1 -1
- package/dist/apps/management/routes/members.d.ts +84 -84
- package/dist/apps/management/routes/members.d.ts.map +1 -1
- package/dist/apps/management/routes/members.js +2 -4
- package/dist/apps/management/routes/members.js.map +1 -1
- package/dist/apps/management/routes/orgs.d.ts +44 -44
- package/dist/apps/management/routes/orgs.d.ts.map +1 -1
- package/dist/apps/management/routes/orgs.js +2 -6
- package/dist/apps/management/routes/orgs.js.map +1 -1
- package/dist/apps/management/routes/projects.d.ts.map +1 -1
- package/dist/apps/management/routes/projects.js +2 -6
- package/dist/apps/management/routes/projects.js.map +1 -1
- package/dist/apps/management/routes/route-usage.d.ts +34 -34
- package/dist/apps/management/routes/route-usage.d.ts.map +1 -1
- package/dist/apps/management/routes/route-usage.js +2 -4
- package/dist/apps/management/routes/route-usage.js.map +1 -1
- package/dist/apps/management/routes/routes-access-requests.d.ts +120 -91
- package/dist/apps/management/routes/routes-access-requests.d.ts.map +1 -1
- package/dist/apps/management/routes/routes-access-requests.js +49 -9
- package/dist/apps/management/routes/routes-access-requests.js.map +1 -1
- package/dist/apps/management/routes/usage.d.ts.map +1 -1
- package/dist/apps/management/routes/usage.js +8 -16
- package/dist/apps/management/routes/usage.js.map +1 -1
- package/dist/apps/management/routes/verified-token-requests.d.ts +39 -39
- package/dist/apps/management/routes/verified-token-requests.d.ts.map +1 -1
- package/dist/apps/management/routes/verified-token-requests.js +3 -7
- package/dist/apps/management/routes/verified-token-requests.js.map +1 -1
- package/dist/apps/management/routes/webhooks.d.ts +2089 -2089
- package/dist/apps/management/routes/zone-delegations.d.ts +14 -17
- package/dist/apps/management/routes/zone-delegations.d.ts.map +1 -1
- package/dist/apps/management/routes/zone-delegations.js +1 -6
- package/dist/apps/management/routes/zone-delegations.js.map +1 -1
- package/dist/apps/mpp/App.d.ts +44 -44
- package/dist/apps/mpp/App.js +3 -3
- package/dist/apps/mpp/App.js.map +1 -1
- package/dist/apps/mpp/internal/Adapter.js +1 -1
- package/dist/apps/mpp/internal/Adapter.js.map +1 -1
- package/dist/apps/mpp/internal/Error.d.ts.map +1 -1
- package/dist/apps/mpp/internal/Error.js +1 -0
- package/dist/apps/mpp/internal/Error.js.map +1 -1
- package/dist/apps/mpp/internal/Policy.d.ts +5 -1
- package/dist/apps/mpp/internal/Policy.d.ts.map +1 -1
- package/dist/apps/mpp/internal/Policy.js +35 -13
- package/dist/apps/mpp/internal/Policy.js.map +1 -1
- package/dist/apps/relay/App.d.ts +2 -2
- package/dist/apps/relay/App.d.ts.map +1 -1
- package/dist/apps/relay/App.js +25 -16
- package/dist/apps/relay/App.js.map +1 -1
- package/dist/apps/routes/App.d.ts +311 -311
- package/dist/apps/routes/routes/deposit-addresses.d.ts +1099 -1099
- package/dist/apps/routes/routes/list.d.ts +74 -74
- package/dist/apps/routes/routes/transfers.d.ts +94 -94
- package/dist/cli/ApiKeys.d.ts +19 -26
- package/dist/cli/ApiKeys.d.ts.map +1 -1
- package/dist/db/Db.d.ts +3 -3
- package/dist/db/Db.d.ts.map +1 -1
- package/dist/db/Db.js +27 -0
- package/dist/db/Db.js.map +1 -1
- package/dist/db/Schema.d.ts +28 -62
- package/dist/db/Schema.d.ts.map +1 -1
- package/dist/db/Schema.js +18 -18
- package/dist/db/Schema.js.map +1 -1
- package/dist/db/tables/rewardRuns.d.ts +21 -0
- package/dist/db/tables/rewardRuns.d.ts.map +1 -1
- package/dist/db/tables/rewardRuns.js +19 -0
- package/dist/db/tables/rewardRuns.js.map +1 -1
- package/dist/db/tables/routesAccessRequests.d.ts +13 -0
- package/dist/db/tables/routesAccessRequests.d.ts.map +1 -1
- package/dist/db/tables/routesAccessRequests.js +12 -0
- package/dist/db/tables/routesAccessRequests.js.map +1 -1
- package/dist/db/tables/zoneDelegationRequests.d.ts +165 -0
- package/dist/db/tables/zoneDelegationRequests.d.ts.map +1 -0
- package/dist/db/tables/{zoneKeyRequests.js → zoneDelegationRequests.js} +43 -42
- package/dist/db/tables/zoneDelegationRequests.js.map +1 -0
- package/dist/db/tables/zoneDelegations.d.ts +11 -5
- package/dist/db/tables/zoneDelegations.d.ts.map +1 -1
- package/dist/db/tables/zoneDelegations.js +21 -3
- package/dist/db/tables/zoneDelegations.js.map +1 -1
- package/dist/handlers/internal/accounts.d.ts +7 -0
- package/dist/handlers/internal/accounts.d.ts.map +1 -0
- package/dist/handlers/internal/{multisig.js → accounts.js} +10 -10
- package/dist/handlers/internal/{multisig.js.map → accounts.js.map} +1 -1
- package/dist/handlers/internal/sponsorship.js +17 -17
- package/dist/handlers/internal/sponsorship.js.map +1 -1
- package/dist/handlers/relay.d.ts +6 -6
- package/dist/handlers/relay.d.ts.map +1 -1
- package/dist/handlers/relay.js +15 -15
- package/dist/handlers/relay.js.map +1 -1
- package/dist/internal/Auth.d.ts.map +1 -1
- package/dist/internal/Auth.js +4 -8
- package/dist/internal/Auth.js.map +1 -1
- package/dist/internal/EarnRates.d.ts.map +1 -1
- package/dist/internal/EarnRates.js +1 -3
- package/dist/internal/EarnRates.js.map +1 -1
- package/dist/internal/EarnVaults.js +2 -2
- package/dist/internal/Log.d.ts +2 -4
- package/dist/internal/Log.d.ts.map +1 -1
- package/dist/internal/Log.js +0 -3
- package/dist/internal/Log.js.map +1 -1
- package/dist/internal/Response.d.ts +1 -1
- package/dist/internal/Response.d.ts.map +1 -1
- package/dist/internal/Response.js +0 -3
- package/dist/internal/Response.js.map +1 -1
- package/dist/internal/Schema.d.ts +11 -0
- package/dist/internal/Schema.d.ts.map +1 -1
- package/dist/internal/Schema.js +24 -1
- package/dist/internal/Schema.js.map +1 -1
- package/dist/internal/ZoneAccess.d.ts +70 -35
- package/dist/internal/ZoneAccess.d.ts.map +1 -1
- package/dist/internal/ZoneAccess.js +312 -131
- package/dist/internal/ZoneAccess.js.map +1 -1
- package/dist/internal/ZoneDelegation.d.ts +7 -19
- package/dist/internal/ZoneDelegation.d.ts.map +1 -1
- package/dist/internal/ZoneDelegation.js +8 -29
- package/dist/internal/ZoneDelegation.js.map +1 -1
- package/dist/internal/{ZoneKeyApproval.d.ts → ZoneDelegationApproval.d.ts} +3 -5
- package/dist/internal/ZoneDelegationApproval.d.ts.map +1 -0
- package/dist/internal/ZoneDelegationApproval.js +16 -0
- package/dist/internal/ZoneDelegationApproval.js.map +1 -0
- package/dist/internal/ZoneKeyRewrite.d.ts +98 -0
- package/dist/internal/ZoneKeyRewrite.d.ts.map +1 -0
- package/dist/internal/ZoneKeyRewrite.js +151 -0
- package/dist/internal/ZoneKeyRewrite.js.map +1 -0
- package/dist/internal/rewards/Campaigns.d.ts +2 -0
- package/dist/internal/rewards/Campaigns.d.ts.map +1 -1
- package/dist/internal/rewards/Campaigns.js.map +1 -1
- package/dist/internal/rewards/Distributions.d.ts.map +1 -1
- package/dist/internal/rewards/Distributions.js +2 -6
- package/dist/internal/rewards/Distributions.js.map +1 -1
- package/dist/internal/rewards/Monitor.d.ts +4 -1
- package/dist/internal/rewards/Monitor.d.ts.map +1 -1
- package/dist/internal/rewards/Monitor.js +19 -2
- package/dist/internal/rewards/Monitor.js.map +1 -1
- package/dist/internal/rewards/Runner.d.ts.map +1 -1
- package/dist/internal/rewards/Runner.js +9 -1
- package/dist/internal/rewards/Runner.js.map +1 -1
- package/dist/internal/routes/Action.d.ts.map +1 -1
- package/dist/internal/routes/Action.js +2 -3
- package/dist/internal/routes/Action.js.map +1 -1
- package/dist/internal/routes/Deposit.d.ts.map +1 -1
- package/dist/internal/routes/Deposit.js +2 -6
- package/dist/internal/routes/Deposit.js.map +1 -1
- package/dist/internal/routes/DepositAddress.d.ts.map +1 -1
- package/dist/internal/routes/DepositAddress.js +4 -10
- package/dist/internal/routes/DepositAddress.js.map +1 -1
- package/dist/internal/routes/Policy.d.ts.map +1 -1
- package/dist/internal/routes/Policy.js +2 -1
- package/dist/internal/routes/Policy.js.map +1 -1
- package/dist/internal/routes/Provider.js +8 -8
- package/dist/internal/routes/Provider.js.map +1 -1
- package/dist/internal/routes/Reconciliation.d.ts.map +1 -1
- package/dist/internal/routes/Reconciliation.js +3 -2
- package/dist/internal/routes/Reconciliation.js.map +1 -1
- package/dist/internal/routes/Transfer.d.ts.map +1 -1
- package/dist/internal/routes/Transfer.js +4 -12
- package/dist/internal/routes/Transfer.js.map +1 -1
- package/dist/internal/routes/UniversalDepositAddress.d.ts.map +1 -1
- package/dist/internal/routes/UniversalDepositAddress.js +3 -6
- package/dist/internal/routes/UniversalDepositAddress.js.map +1 -1
- package/dist/internal/routes/providers/SquidOrder.d.ts.map +1 -1
- package/dist/internal/routes/providers/SquidOrder.js +3 -2
- package/dist/internal/routes/providers/SquidOrder.js.map +1 -1
- package/dist/internal/routes/providers/relay.d.ts.map +1 -1
- package/dist/internal/routes/providers/relay.js +4 -3
- package/dist/internal/routes/providers/relay.js.map +1 -1
- package/package.json +20 -5
- package/src/ApiKey.ts +2 -2
- package/src/ApiKeys.test.ts +284 -51
- package/src/ApiKeys.ts +297 -64
- package/src/App.ts +4 -5
- package/src/Client.test-d.ts +33 -126
- package/src/Scope.test.ts +26 -45
- package/src/Scope.ts +24 -37
- package/src/admin/App.test.ts +51 -4
- package/src/admin/App.ts +25 -33
- package/src/admin/Mcp.test.ts +2 -1
- package/src/admin/apps/api-keys.test.ts +90 -20
- package/src/admin/apps/api-keys.ts +5 -8
- package/src/admin/apps/audit-logs.ts +1 -1
- package/src/admin/apps/earn-vaults.test.ts +19 -19
- package/src/admin/apps/earn-vaults.ts +11 -5
- package/src/admin/apps/routes-support.test.ts +254 -3
- package/src/admin/apps/routes-support.ts +1 -44
- package/src/admin/apps/scopes.test.ts +3 -3
- package/src/admin/apps/zone-delegation-requests.test.ts +246 -0
- package/src/admin/apps/zone-delegation-requests.ts +240 -0
- package/src/admin/apps/zone-delegations.test.ts +218 -58
- package/src/admin/apps/zone-delegations.ts +146 -46
- package/src/admin/index.ts +1 -1
- package/src/admin/ui/src/components/ApiKeyForm.tsx +4 -3
- package/src/admin/ui/src/lib/config.ts +3 -3
- package/src/admin/ui/src/lib/query-client.ts +1 -1
- package/src/admin/ui/src/lib/review-queues.ts +10 -10
- package/src/admin/ui/src/lib/routes-setup.test.ts +1 -2
- package/src/admin/ui/src/lib/routes-setup.ts +3 -5
- package/src/admin/ui/src/lib/scopes.test.ts +3 -6
- package/src/admin/ui/src/lib/scopes.ts +2 -9
- package/src/admin/ui/src/lib/{zone-key-requests.ts → zone-delegation-requests.ts} +27 -31
- package/src/admin/ui/src/routeTree.gen.ts +17 -17
- package/src/admin/ui/src/routes/__root.tsx +1 -1
- package/src/admin/ui/src/routes/api-keys.tsx +2 -15
- package/src/admin/ui/src/routes/audit-logs.tsx +2 -2
- package/src/admin/ui/src/routes/index.tsx +11 -11
- package/src/admin/ui/src/routes/organizations.tsx +3 -1
- package/src/admin/ui/src/routes/organizations_.$orgId.tsx +3 -1
- package/src/admin/ui/src/routes/organizations_.$orgId_.setup.tsx +1 -3
- package/src/admin/ui/src/routes/{zone-key-requests.tsx → zone-delegation-requests.tsx} +228 -172
- package/src/admin/ui/vite.config.ts +14 -1
- package/src/admin/ui.generated.ts +412 -583
- package/src/analytics/tables/requestEvents.test.ts +13 -3
- package/src/analytics/tables/requestEvents.ts +4 -3
- package/src/apps/data/App.test.ts +72 -35
- package/src/apps/data/App.ts +29 -13
- package/src/apps/data/ZoneSelection.test.ts +53 -70
- package/src/apps/data/ZoneSelection.ts +2 -2
- package/src/apps/data/routes/activities.test.ts +140 -6
- package/src/apps/data/routes/activities.ts +124 -18
- package/src/apps/data/routes/balances.ts +5 -1
- package/src/apps/data/routes/blocks.ts +9 -13
- package/src/apps/data/routes/earn-transactions.local.test.ts +6 -1
- package/src/apps/data/routes/earn-transactions.test.ts +7 -3
- package/src/apps/data/routes/earn.bench.ts +0 -14
- package/src/apps/data/routes/earn.discovery.test.ts +4 -39
- package/src/apps/data/routes/earn.local.test.ts +27 -34
- package/src/apps/data/routes/earn.test.ts +172 -490
- package/src/apps/data/routes/earn.ts +103 -652
- package/src/apps/data/routes/exchanges.ts +20 -30
- package/src/apps/data/routes/fee-amm.ts +12 -18
- package/src/apps/data/routes/indexer.test.ts +14 -12
- package/src/apps/data/routes/receipts.ts +1 -1
- package/src/apps/data/routes/rpc.test.ts +23 -18
- package/src/apps/data/routes/rpc.ts +21 -21
- package/src/apps/data/routes/rpc.zone.test.ts +60 -6
- package/src/apps/data/routes/tokenlist.ts +4 -6
- package/src/apps/data/routes/tokens.test.ts +20 -0
- package/src/apps/data/routes/tokens.ts +10 -5
- package/src/apps/data/routes/transactions.ts +3 -3
- package/src/apps/data/routes/transfers.ts +4 -6
- package/src/apps/data/routes/valuation.test.ts +2 -4
- package/src/apps/data/routes/valuation.ts +4 -6
- package/src/apps/data/routes/webhooks.test.ts +1 -5
- package/src/apps/data/routes/webhooks.ts +30 -44
- package/src/apps/data/routes/zones.test.ts +35 -19
- package/src/apps/management/App.ts +3 -1
- package/src/apps/management/routes/api-keys.test.ts +112 -22
- package/src/apps/management/routes/api-keys.ts +30 -15
- package/src/apps/management/routes/billing.ts +17 -25
- package/src/apps/management/routes/cli.ts +12 -18
- package/src/apps/management/routes/fee-payer-usage.ts +5 -4
- package/src/apps/management/routes/invitations.ts +16 -21
- package/src/apps/management/routes/invite-links.ts +18 -26
- package/src/apps/management/routes/me.ts +9 -13
- package/src/apps/management/routes/members.ts +5 -7
- package/src/apps/management/routes/orgs.ts +8 -12
- package/src/apps/management/routes/projects.ts +8 -12
- package/src/apps/management/routes/route-usage.ts +5 -4
- package/src/apps/management/routes/routes-access-requests.test.ts +64 -2
- package/src/apps/management/routes/routes-access-requests.ts +99 -15
- package/src/apps/management/routes/scopes.test.ts +3 -3
- package/src/apps/management/routes/usage.ts +20 -28
- package/src/apps/management/routes/verified-token-requests.ts +9 -13
- package/src/apps/management/routes/zone-delegations.test.ts +3 -5
- package/src/apps/management/routes/zone-delegations.ts +4 -9
- package/src/apps/mpp/App.ts +3 -3
- package/src/apps/mpp/Mpp.zone.test.ts +49 -18
- package/src/apps/mpp/internal/Adapter.ts +1 -1
- package/src/apps/mpp/internal/Error.test.ts +3 -0
- package/src/apps/mpp/internal/Error.ts +1 -0
- package/src/apps/mpp/internal/Policy.test.ts +9 -8
- package/src/apps/mpp/internal/Policy.ts +37 -14
- package/src/apps/relay/App.sandbox.test.ts +15 -14
- package/src/apps/relay/App.test.ts +120 -61
- package/src/apps/relay/App.ts +28 -18
- package/src/apps/relay/App.zone-billing.test.ts +9 -4
- package/src/apps/routes/App.test.ts +21 -22
- package/src/db/Db.ts +34 -3
- package/src/db/Schema.ts +26 -26
- package/src/db/tables/rewardRuns.ts +39 -0
- package/src/db/tables/routesAccessRequests.test.ts +65 -0
- package/src/db/tables/routesAccessRequests.ts +25 -0
- package/src/db/tables/{zoneKeyRequests.test.ts → zoneDelegationRequests.test.ts} +36 -77
- package/src/db/tables/{zoneKeyRequests.ts → zoneDelegationRequests.ts} +69 -52
- package/src/db/tables/zoneDelegations.test.ts +5 -6
- package/src/db/tables/zoneDelegations.ts +25 -8
- package/src/handlers/internal/{multisig.store.test.ts → accounts.store.test.ts} +16 -16
- package/src/handlers/internal/{multisig.test.ts → accounts.test.ts} +85 -82
- package/src/handlers/internal/{multisig.ts → accounts.ts} +9 -9
- package/src/handlers/internal/sponsorship.ts +20 -20
- package/src/handlers/relay.test.ts +48 -46
- package/src/handlers/relay.ts +26 -26
- package/src/internal/Auth.test.ts +6 -2
- package/src/internal/Auth.ts +4 -10
- package/src/internal/EarnRates.ts +4 -6
- package/src/internal/EarnVaults.ts +2 -2
- package/src/internal/Log.test-d.ts +1 -0
- package/src/internal/Log.test.ts +4 -6
- package/src/internal/Log.ts +2 -6
- package/src/internal/Response.ts +0 -3
- package/src/internal/Schema.test.ts +42 -0
- package/src/internal/Schema.ts +36 -1
- package/src/internal/ZoneAccess.test.ts +515 -312
- package/src/internal/ZoneAccess.ts +410 -158
- package/src/internal/ZoneDelegation.test.ts +1 -41
- package/src/internal/ZoneDelegation.ts +10 -48
- package/src/internal/ZoneDelegationApproval.test.ts +22 -0
- package/src/internal/{ZoneKeyApproval.ts → ZoneDelegationApproval.ts} +4 -11
- package/src/internal/ZoneKeyRewrite.ts +231 -0
- package/src/internal/rewards/Campaigns.ts +2 -0
- package/src/internal/rewards/Distributions.ts +8 -12
- package/src/internal/rewards/Monitor.test.ts +64 -0
- package/src/internal/rewards/Monitor.ts +27 -4
- package/src/internal/rewards/Runner.ts +9 -1
- package/src/internal/routes/Action.ts +5 -6
- package/src/internal/routes/Deposit.ts +8 -12
- package/src/internal/routes/DepositAddress.ts +13 -19
- package/src/internal/routes/Policy.ts +2 -1
- package/src/internal/routes/Provider.ts +8 -8
- package/src/internal/routes/Reconciliation.ts +3 -2
- package/src/internal/routes/Transfer.ts +16 -24
- package/src/internal/routes/UniversalDepositAddress.ts +9 -12
- package/src/internal/routes/providers/SquidOrder.ts +3 -2
- package/src/internal/routes/providers/relay.ts +4 -3
- package/dist/admin/apps/zone-key-requests.d.ts +0 -406
- package/dist/admin/apps/zone-key-requests.d.ts.map +0 -1
- package/dist/admin/apps/zone-key-requests.js +0 -183
- package/dist/admin/apps/zone-key-requests.js.map +0 -1
- package/dist/db/tables/zoneKeyRequests.d.ts +0 -289
- package/dist/db/tables/zoneKeyRequests.d.ts.map +0 -1
- package/dist/db/tables/zoneKeyRequests.js.map +0 -1
- package/dist/handlers/internal/multisig.d.ts +0 -7
- package/dist/handlers/internal/multisig.d.ts.map +0 -1
- package/dist/internal/ZoneKeyApproval.d.ts.map +0 -1
- package/dist/internal/ZoneKeyApproval.js +0 -21
- package/dist/internal/ZoneKeyApproval.js.map +0 -1
- package/src/admin/apps/zone-key-requests.test.ts +0 -272
- package/src/admin/apps/zone-key-requests.ts +0 -231
- package/src/internal/ZoneKeyApproval.test.ts +0 -46
package/src/ApiKeys.ts
CHANGED
|
@@ -22,10 +22,10 @@ import * as ApiKeyRevocations from './db/tables/apiKeyRevocations.js'
|
|
|
22
22
|
import * as Organizations from './db/tables/organizations.js'
|
|
23
23
|
import * as RoutesPolicies from './db/tables/routesPolicies.js'
|
|
24
24
|
import * as RoutesSubsidies from './db/tables/routesSubsidies.js'
|
|
25
|
+
import * as ZoneDelegations from './db/tables/zoneDelegations.js'
|
|
25
26
|
import * as Store from './internal/Store.js'
|
|
26
27
|
import * as Scope from './Scope.js'
|
|
27
|
-
import * as
|
|
28
|
-
import * as ZoneKeyRequests from './db/tables/zoneKeyRequests.js'
|
|
28
|
+
import * as Schema from './internal/Schema.js'
|
|
29
29
|
|
|
30
30
|
const billingSnapshotSchema = z.object({
|
|
31
31
|
active: z.boolean().check(z.describe('Whether billing is active.')),
|
|
@@ -44,7 +44,7 @@ export const minimumExpiryLeadMs = 2 * 60_000
|
|
|
44
44
|
/** Zod schemas owned by the API-keys module. */
|
|
45
45
|
export namespace schema {
|
|
46
46
|
/** Key expiry requested through an API, far enough ahead that the stored record outlives the request. */
|
|
47
|
-
export const ExpiresAt =
|
|
47
|
+
export const ExpiresAt = Schema.dateTime({ offset: true }).check(
|
|
48
48
|
z.refine((value) => Date.parse(value) >= Date.now() + minimumExpiryLeadMs, {
|
|
49
49
|
error: 'Expiry must be at least two minutes in the future.',
|
|
50
50
|
}),
|
|
@@ -62,7 +62,7 @@ export namespace schema {
|
|
|
62
62
|
.optional(z.string())
|
|
63
63
|
.check(z.describe('Identity creating the key (e.g. admin email), recorded for audit.')),
|
|
64
64
|
expiresAt: z
|
|
65
|
-
.optional(
|
|
65
|
+
.optional(Schema.dateTime({ offset: true }))
|
|
66
66
|
.check(z.describe('ISO 8601 expiry timestamp. Omit for a non-expiring key.')),
|
|
67
67
|
// TODO: make required once orgs exist.
|
|
68
68
|
orgId: z.optional(z.string()).check(z.describe('Owning organization id.')),
|
|
@@ -175,14 +175,12 @@ export async function mintBounded(
|
|
|
175
175
|
input: MintInput,
|
|
176
176
|
options: mintBounded.Options = {},
|
|
177
177
|
): Promise<MintResult> {
|
|
178
|
-
if (ZoneKeyApproval.required(input.scopes)) throw new ZoneKeyApproval.RequiredError()
|
|
179
178
|
return mintAdmitted(db, state, input, options)
|
|
180
179
|
}
|
|
181
180
|
|
|
182
181
|
/**
|
|
183
182
|
* Mints a replacement for an existing key on behalf of its owner. The locked source
|
|
184
|
-
* must still hold every requested scope, so rotation never widens access
|
|
185
|
-
* Zone read approval; admin rotation keeps using {@link mintBounded} and its gate.
|
|
183
|
+
* must still hold every requested scope, so rotation never widens access.
|
|
186
184
|
*/
|
|
187
185
|
export async function rotateBounded(
|
|
188
186
|
db: Db.Db,
|
|
@@ -190,7 +188,7 @@ export async function rotateBounded(
|
|
|
190
188
|
input: MintInput,
|
|
191
189
|
options: rotateBounded.Options,
|
|
192
190
|
): Promise<MintResult> {
|
|
193
|
-
return mintAdmitted(db, state, input, { ...options, sourceScopes: input.scopes })
|
|
191
|
+
return mintAdmitted(db, state, input, { ...options, sourceScopes: withoutRetired(input.scopes) })
|
|
194
192
|
}
|
|
195
193
|
|
|
196
194
|
export declare namespace rotateBounded {
|
|
@@ -198,45 +196,23 @@ export declare namespace rotateBounded {
|
|
|
198
196
|
type Options = mint.Options & Required<Pick<mint.Options, 'policySourceApiKeyId'>>
|
|
199
197
|
}
|
|
200
198
|
|
|
201
|
-
/** Issues the exact approved settings once using the requester session and unexpired approvals. */
|
|
202
|
-
export async function mintApproved(
|
|
203
|
-
db: Db.Db,
|
|
204
|
-
state: Store.State,
|
|
205
|
-
options: mintApproved.Options,
|
|
206
|
-
): Promise<MintResult> {
|
|
207
|
-
const request = await ZoneKeyRequests.claim(db, options)
|
|
208
|
-
try {
|
|
209
|
-
const result = await mintAdmitted(db, state, request.input, {}, request.keyId)
|
|
210
|
-
await ZoneKeyRequests.finish(db, request.id, 'issued')
|
|
211
|
-
return result
|
|
212
|
-
} catch (cause) {
|
|
213
|
-
// An ambiguous write never permits another issuance; fence the reserved id before cleanup.
|
|
214
|
-
await db.transaction(async (tx) => {
|
|
215
|
-
await ApiKeyAdmissions.lock(tx, request.keyId)
|
|
216
|
-
await ApiKeyRevocations.markPending(tx, request.keyId, request.input.orgId)
|
|
217
|
-
})
|
|
218
|
-
await ZoneKeyRequests.finish(db, request.id, 'failed')
|
|
219
|
-
await revokeBounded(db, state, request.keyId).catch(() => undefined)
|
|
220
|
-
throw cause
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
export declare namespace mintApproved {
|
|
225
|
-
/** Authenticated issuance context. */
|
|
226
|
-
type Options = ZoneKeyRequests.claim.Options
|
|
227
|
-
}
|
|
228
|
-
|
|
229
199
|
async function mintAdmitted(
|
|
230
200
|
db: Db.Db,
|
|
231
201
|
state: Store.State,
|
|
232
|
-
|
|
202
|
+
input_raw: MintInput,
|
|
233
203
|
options: mintBounded.Options = {},
|
|
234
204
|
id = ApiKey.generateId(),
|
|
235
205
|
): Promise<MintResult> {
|
|
236
206
|
if (options.sourceScopes && !options.policySourceApiKeyId)
|
|
237
207
|
throw new RoutesPolicies.InvalidScopeError()
|
|
208
|
+
// A copy of an existing key (rotation) drops the retired per-Zone scopes the source still holds.
|
|
209
|
+
const input = options.policySourceApiKeyId
|
|
210
|
+
? { ...input_raw, scopes: withoutRetired(input_raw.scopes) }
|
|
211
|
+
: input_raw
|
|
238
212
|
if (input.orgId === undefined) {
|
|
239
213
|
if (options.policySourceApiKeyId) throw new RoutesPolicies.InvalidScopeError()
|
|
214
|
+
// No organization means no delegation, so nothing could bound the key's broadcasts.
|
|
215
|
+
if (boundByDelegations(input.scopes)) throw new UndelegatedZoneTagsError()
|
|
240
216
|
return mint(state, input, options)
|
|
241
217
|
}
|
|
242
218
|
const orgId = input.orgId
|
|
@@ -269,14 +245,26 @@ async function mintAdmitted(
|
|
|
269
245
|
...rest
|
|
270
246
|
} = validated
|
|
271
247
|
const expiry = earliestExpiry(expiresAt, source.expiresAt)
|
|
248
|
+
// A broadcasting copy keeps only the tags still delegated, so a revoked delegation narrows the
|
|
249
|
+
// replacement instead of blocking the rotation; the gate refuses undelegated Zones anyway.
|
|
250
|
+
const zoneTags = boundByDelegations(rest.scopes)
|
|
251
|
+
? await delegatedTags(tx, {
|
|
252
|
+
environment: rest.environment,
|
|
253
|
+
orgId,
|
|
254
|
+
zoneTags: source.zoneTags,
|
|
255
|
+
})
|
|
256
|
+
: source.zoneTags
|
|
272
257
|
return {
|
|
273
258
|
...rest,
|
|
274
259
|
allowedIps: source.allowedIps,
|
|
275
260
|
...(expiry === undefined ? {} : { expiresAt: expiry }),
|
|
276
261
|
...(source.rateLimits === undefined ? {} : { rateLimits: source.rateLimits }),
|
|
277
|
-
...(
|
|
262
|
+
...(zoneTags === undefined ? {} : { zoneTags }),
|
|
278
263
|
}
|
|
279
264
|
})()
|
|
265
|
+
// Requested tags are what bound a broadcasting key, so they are checked as written; a copy's were narrowed above.
|
|
266
|
+
if (!options.policySourceApiKeyId && boundByDelegations(data.scopes))
|
|
267
|
+
await ensureDelegatedTags(tx, { environment: data.environment, orgId, zoneTags: data.zoneTags }) // prettier-ignore
|
|
280
268
|
// Policy copies share deletion's subsidy-before-owner lock order.
|
|
281
269
|
if (options.policySourceApiKeyId) await RoutesSubsidies.lockOrganization(tx, orgId)
|
|
282
270
|
const owner = { orgId, projectId: data.projectId }
|
|
@@ -712,6 +700,14 @@ export async function update(
|
|
|
712
700
|
if (!recordKey) return null
|
|
713
701
|
const record = await read(state, recordKey, Date.now(), options.scopeCatalog ?? Scope.catalog)
|
|
714
702
|
if (!record) return null
|
|
703
|
+
// Scopes the record already holds republish as they are; only new grants must be issuable.
|
|
704
|
+
if (
|
|
705
|
+
input.scopes &&
|
|
706
|
+
!issuableScopes(input.scopes.filter((scope) => !record.scopes.includes(scope)), options.scopeCatalog ?? Scope.catalog) // prettier-ignore
|
|
707
|
+
)
|
|
708
|
+
throw new InvalidInputError(
|
|
709
|
+
'per-Zone scopes are retired; grant zone:read or zone:write with zoneTags',
|
|
710
|
+
)
|
|
715
711
|
const owner = {
|
|
716
712
|
orgId: input.orgId ?? record.orgId,
|
|
717
713
|
projectId: input.projectId ?? record.projectId,
|
|
@@ -792,15 +788,21 @@ export async function updateBounded(
|
|
|
792
788
|
)
|
|
793
789
|
return { type: 'missing' } as const
|
|
794
790
|
const pendingScopes = ledger.admitted?.pendingScopes
|
|
795
|
-
//
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
ZoneKeyApproval.required(record.scopes) || ZoneKeyApproval.required(pendingScopes ?? [])
|
|
791
|
+
// Scopes still awaiting publication count, so a tag edit in that window is bounded too.
|
|
792
|
+
const scopes = input.scopes ?? pendingScopes ?? record.scopes
|
|
793
|
+
// A key that broadcasts through `zone:write` keeps every tag inside its organization's delegations.
|
|
799
794
|
if (
|
|
800
|
-
|
|
801
|
-
(
|
|
795
|
+
boundByDelegations(scopes) &&
|
|
796
|
+
(attributing || input.scopes !== undefined || input.zoneTags !== undefined)
|
|
802
797
|
)
|
|
803
|
-
|
|
798
|
+
await ensureDelegatedTags(tx, {
|
|
799
|
+
environment: record.environment,
|
|
800
|
+
orgId: input.orgId ?? record.orgId,
|
|
801
|
+
zoneTags:
|
|
802
|
+
input.zoneTags === undefined
|
|
803
|
+
? record.zoneTags
|
|
804
|
+
: (Input.parseNullable(ApiKey.schema.ZoneTags, input.zoneTags) ?? undefined),
|
|
805
|
+
})
|
|
804
806
|
const expiresAt = normalizeExpiresAt(
|
|
805
807
|
input.expiresAt === undefined ? record.expiresAt : (input.expiresAt ?? undefined),
|
|
806
808
|
)
|
|
@@ -848,10 +850,11 @@ export async function updateBounded(
|
|
|
848
850
|
if (reassigning) {
|
|
849
851
|
// Fail closed across the KV/SQL boundary. The key remains scope-less if
|
|
850
852
|
// clearing, reassignment, or the eventual SQL commit fails.
|
|
853
|
+
// Retired read scopes are inert once the rewrite ran, so a move to a new organization drops them.
|
|
851
854
|
await ApiKeyAdmissions.setPendingScopes(
|
|
852
855
|
tx,
|
|
853
856
|
id,
|
|
854
|
-
input.scopes ?? pendingScopes ?? record.scopes,
|
|
857
|
+
withoutRetired(input.scopes ?? pendingScopes ?? record.scopes),
|
|
855
858
|
)
|
|
856
859
|
const disabled = await update(state, id, { scopes: [] }, options)
|
|
857
860
|
if (!disabled) return { type: 'missing' } as const
|
|
@@ -902,22 +905,6 @@ export declare namespace updateBounded {
|
|
|
902
905
|
}
|
|
903
906
|
}
|
|
904
907
|
|
|
905
|
-
/** Whether an edit only gives a key minted before organizations existed its organization and changes nothing else. */
|
|
906
|
-
function adopts(record: Record, input: update.Input) {
|
|
907
|
-
if (record.orgId !== record.id) return false
|
|
908
|
-
const { orgId, projectId: _projectId, ...rest } = input
|
|
909
|
-
if (Object.values(rest).some((value) => value !== undefined)) return false
|
|
910
|
-
return orgId !== undefined
|
|
911
|
-
}
|
|
912
|
-
|
|
913
|
-
/** Whether an edit only moves a key's expiry earlier and changes nothing else. */
|
|
914
|
-
function shortens(record: Record, input: update.Input) {
|
|
915
|
-
const { expiresAt, ...rest } = input
|
|
916
|
-
if (Object.values(rest).some((value) => value !== undefined)) return false
|
|
917
|
-
if (expiresAt == null) return false
|
|
918
|
-
return record.expiresAt === undefined || Date.parse(expiresAt) <= Date.parse(record.expiresAt)
|
|
919
|
-
}
|
|
920
|
-
|
|
921
908
|
/** Returns the earliest of the given ISO timestamps, ignoring absent ones. */
|
|
922
909
|
export function earliestExpiry(...values: readonly (string | undefined)[]) {
|
|
923
910
|
return values
|
|
@@ -1115,6 +1102,176 @@ export declare namespace backfill {
|
|
|
1115
1102
|
}
|
|
1116
1103
|
}
|
|
1117
1104
|
|
|
1105
|
+
/**
|
|
1106
|
+
* Plans the additive rewrite of stored per-Zone read scopes: `zone:read` and
|
|
1107
|
+
* the chain ids as tags are added beside them, merged with existing tags, so
|
|
1108
|
+
* the running Worker and the next both admit the record. Wildcard keys are
|
|
1109
|
+
* listed and left alone; nothing is written. Admission today ignores tags and
|
|
1110
|
+
* already treats a per-Zone read scope as `zone:read`, so the rewrite changes no decision.
|
|
1111
|
+
*/
|
|
1112
|
+
export async function planZoneReadRewrite(
|
|
1113
|
+
state: Store.State,
|
|
1114
|
+
options: planZoneReadRewrite.Options = {},
|
|
1115
|
+
): Promise<planZoneReadRewrite.Result> {
|
|
1116
|
+
const scopeCatalog = options.scopeCatalog ?? Scope.catalog
|
|
1117
|
+
const { keys } = await state.list({ prefix: ApiKey.recordPrefix })
|
|
1118
|
+
const now = Date.now()
|
|
1119
|
+
const changes: planZoneReadRewrite.Change[] = []
|
|
1120
|
+
const expiring: Record[] = []
|
|
1121
|
+
const invalid: Record[] = []
|
|
1122
|
+
const mixed: Record[] = []
|
|
1123
|
+
const tagless: Record[] = []
|
|
1124
|
+
const wildcard: Record[] = []
|
|
1125
|
+
let unreadableRecords = 0
|
|
1126
|
+
let live = 0
|
|
1127
|
+
for (const { name } of keys) {
|
|
1128
|
+
const record = await read(state, name, now, scopeCatalog)
|
|
1129
|
+
if (!record) {
|
|
1130
|
+
unreadableRecords += 1
|
|
1131
|
+
continue
|
|
1132
|
+
}
|
|
1133
|
+
// Revocation and owner-deletion fences live under their own keys; a fenced record is never rewritten.
|
|
1134
|
+
if (await isFenced(state, record)) continue
|
|
1135
|
+
live += 1
|
|
1136
|
+
if (record.scopes.includes(Scope.wildcard)) {
|
|
1137
|
+
wildcard.push(record)
|
|
1138
|
+
continue
|
|
1139
|
+
}
|
|
1140
|
+
// One record the schema refuses (say, past the tag limit) is reported, never a reason to abort the plan.
|
|
1141
|
+
const change = (() => {
|
|
1142
|
+
try {
|
|
1143
|
+
return rewriteZoneRead(record, options.zones)
|
|
1144
|
+
} catch {
|
|
1145
|
+
return 'invalid' as const
|
|
1146
|
+
}
|
|
1147
|
+
})()
|
|
1148
|
+
if (change === 'invalid') invalid.push(record)
|
|
1149
|
+
else if (change === 'mixed') mixed.push(record)
|
|
1150
|
+
// KV refuses TTLs under a minute, and such a record expires on its own before anyone uses it.
|
|
1151
|
+
else if (change && !change.complete && expiresSoon(record, now)) expiring.push(record)
|
|
1152
|
+
else if (change) changes.push({ ...change, record, recordKey: name })
|
|
1153
|
+
else if (['zone:read', 'zone:write'].some((scope) => record.scopes.includes(scope)) && !record.zoneTags?.length) tagless.push(record) // prettier-ignore
|
|
1154
|
+
}
|
|
1155
|
+
return { changes, expiring, invalid, keys: live, mixed, tagless, unreadableRecords, wildcard }
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
/**
|
|
1159
|
+
* The record with `zone:read` and tags added beside its per-Zone read scopes,
|
|
1160
|
+
* so Workers of either release admit it; undefined when it holds no such scope.
|
|
1161
|
+
* A record already carrying both is `complete`: still planned, so its
|
|
1162
|
+
* delegations are checked, but never written. Zones outside `zones` earn no tag
|
|
1163
|
+
* and lose any they had, since nothing serves them. `mixed` when its write Zones
|
|
1164
|
+
* differ from its read Zones: one tag list cannot express both, so the key is
|
|
1165
|
+
* re-minted by hand instead.
|
|
1166
|
+
*/
|
|
1167
|
+
function rewriteZoneRead(record: Record, zones: ReadonlySet<number> | undefined) {
|
|
1168
|
+
const named = [...new Set(record.scopes.flatMap((scope) => Scope.zoneReadChainId(scope) ?? []))].sort((a, b) => a - b) // prettier-ignore
|
|
1169
|
+
const written = [...new Set(record.scopes.flatMap((scope) => Scope.zoneWriteChainId(scope) ?? []))].sort((a, b) => a - b) // prettier-ignore
|
|
1170
|
+
if (named.length === 0 && written.length === 0) return undefined
|
|
1171
|
+
// Zones nothing serves earn no tag, so they are dropped from tags and scopes alike before the
|
|
1172
|
+
// read and write sets are compared. Tags serve both verbs: what the key reads (tags plus read
|
|
1173
|
+
// Zones) must equal what it writes (write Zones, plus its tags when it already holds the generic
|
|
1174
|
+
// write scope), or it is `mixed`; otherwise a read Zone or a leftover tag would become a broadcast grant.
|
|
1175
|
+
const served = (chainIds: readonly number[]) => chainIds.filter((chainId) => zones?.has(chainId) ?? true) // prettier-ignore
|
|
1176
|
+
const kept = served(record.zoneTags ?? [])
|
|
1177
|
+
const chainIds = served(named)
|
|
1178
|
+
const writeChainIds = served(written)
|
|
1179
|
+
const read = new Set([...kept, ...chainIds])
|
|
1180
|
+
const writes = new Set(record.scopes.includes('zone:write') ? [...kept, ...writeChainIds] : writeChainIds) // prettier-ignore
|
|
1181
|
+
const bounded = read.size > 0 || record.scopes.includes('zone:read')
|
|
1182
|
+
if (writes.size && bounded && (writes.size !== read.size || [...writes].some((chainId) => !read.has(chainId)))) return 'mixed' as const // prettier-ignore
|
|
1183
|
+
const all = [...new Set([...named, ...written])]
|
|
1184
|
+
const unconfigured = all.filter((chainId) => !(zones?.has(chainId) ?? true)).sort((a, b) => a - b) // prettier-ignore
|
|
1185
|
+
const zoneTags = [...new Set([...kept, ...chainIds, ...writeChainIds])].sort((a, b) => a - b)
|
|
1186
|
+
// A generic scope is added only when a served Zone for that verb survives into the tags.
|
|
1187
|
+
const generic = [
|
|
1188
|
+
...(chainIds.length && !record.scopes.includes('zone:read') ? ['zone:read'] : []),
|
|
1189
|
+
...(writeChainIds.length && !record.scopes.includes('zone:write') ? ['zone:write'] : []),
|
|
1190
|
+
]
|
|
1191
|
+
const complete =
|
|
1192
|
+
generic.length === 0 &&
|
|
1193
|
+
zoneTags.length === (record.zoneTags?.length ?? 0) &&
|
|
1194
|
+
zoneTags.every((chainId, index) => record.zoneTags?.[index] === chainId)
|
|
1195
|
+
const rewritten = complete ? record : schema.Record.parse({ ...record, scopes: [...record.scopes, ...generic], zoneTags }) // prettier-ignore
|
|
1196
|
+
return { chainIds, complete, rewritten, unconfigured, writeChainIds }
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
export declare namespace planZoneReadRewrite {
|
|
1200
|
+
/** Options for {@link planZoneReadRewrite}. */
|
|
1201
|
+
type Options = {
|
|
1202
|
+
/** Scope catalog accepted while reading keys. */
|
|
1203
|
+
scopeCatalog?: Scope.Catalog | undefined
|
|
1204
|
+
/** Zone chain ids the deployment serves; per-Zone scopes naming any other Zone earn no tag. */
|
|
1205
|
+
zones?: ReadonlySet<number> | undefined
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
/** One stored record and the record replacing it. */
|
|
1209
|
+
type Change = {
|
|
1210
|
+
/** Served Zones the per-Zone read scopes name, alias-resolved; the only Zones the rewrite may delegate. */
|
|
1211
|
+
chainIds: readonly number[]
|
|
1212
|
+
/** Whether the record already holds `zone:read` and every tag; only its delegations are checked, nothing is written. */
|
|
1213
|
+
complete: boolean
|
|
1214
|
+
/** The record as stored. */
|
|
1215
|
+
record: Record
|
|
1216
|
+
/** Storage key of the record. */
|
|
1217
|
+
recordKey: string
|
|
1218
|
+
/** The record with the generic Zone scopes and merged tags added beside its per-Zone scopes. */
|
|
1219
|
+
rewritten: Record
|
|
1220
|
+
/** Zones the per-Zone scopes name that the deployment does not serve; they earn no tag. */
|
|
1221
|
+
unconfigured: readonly number[]
|
|
1222
|
+
/** Served Zones the per-Zone write scopes name, alias-resolved; they need a delegation to bound the tag, never member coverage. */
|
|
1223
|
+
writeChainIds: readonly number[]
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
/** Result of {@link planZoneReadRewrite}. */
|
|
1227
|
+
type Result = {
|
|
1228
|
+
/** Records holding per-Zone read scopes, with their replacements. */
|
|
1229
|
+
changes: readonly Change[]
|
|
1230
|
+
/** Records expiring within a minute; KV refuses their TTL and they lapse on their own, so they are left unchanged. */
|
|
1231
|
+
expiring: readonly Record[]
|
|
1232
|
+
/** Records whose rewritten form the schema refuses; left unchanged for a manual fix. */
|
|
1233
|
+
invalid: readonly Record[]
|
|
1234
|
+
/** Live, unfenced records read. */
|
|
1235
|
+
keys: number
|
|
1236
|
+
/** Records whose per-Zone write Zones differ from their read Zones; one tag list cannot express both, so they are re-minted by hand. */
|
|
1237
|
+
mixed: readonly Record[]
|
|
1238
|
+
/** Records holding `zone:read` with no tags and no per-Zone scope; they read no Zone and are left alone. */
|
|
1239
|
+
tagless: readonly Record[]
|
|
1240
|
+
/** Records the store could not read (expired, or scopes outside the catalog). */
|
|
1241
|
+
unreadableRecords: number
|
|
1242
|
+
/** Live records holding `*`, listed for a decision and left unchanged. */
|
|
1243
|
+
wildcard: readonly Record[]
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
/**
|
|
1248
|
+
* Rewrites the records a {@link planZoneReadRewrite} plan names, keeping each
|
|
1249
|
+
* record's TTL. Each record is re-read and transformed at write time so an edit
|
|
1250
|
+
* made since the plan survives; a record already in both formats is skipped.
|
|
1251
|
+
*/
|
|
1252
|
+
export async function applyZoneReadRewrite(
|
|
1253
|
+
state: Store.State,
|
|
1254
|
+
changes: readonly planZoneReadRewrite.Change[],
|
|
1255
|
+
options: planZoneReadRewrite.Options = {},
|
|
1256
|
+
): Promise<number> {
|
|
1257
|
+
let written = 0
|
|
1258
|
+
for (const { recordKey } of changes) {
|
|
1259
|
+
const now = Date.now()
|
|
1260
|
+
const current = await read(state, recordKey, now, options.scopeCatalog ?? Scope.catalog)
|
|
1261
|
+
const change = current && rewriteZoneRead(current, options.zones)
|
|
1262
|
+
if (!change || change === 'mixed' || change.complete || expiresSoon(current, now)) continue
|
|
1263
|
+
await state.put(recordKey, JSON.stringify(change.rewritten), { ttl: ttlFor(change.rewritten.expiresAt, now) }) // prettier-ignore
|
|
1264
|
+
written += 1
|
|
1265
|
+
}
|
|
1266
|
+
return written
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
/** Whether a record expires within the minute Cloudflare KV needs for a TTL. */
|
|
1270
|
+
function expiresSoon(record: Record, now: number): boolean {
|
|
1271
|
+
const ttl = ttlFor(record.expiresAt, now)
|
|
1272
|
+
return ttl !== undefined && ttl < 60_000
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1118
1275
|
/**
|
|
1119
1276
|
* Reads and validates a stored record by storage key. Invalid, corrupt, or
|
|
1120
1277
|
* expired records (`expiresAt <= now`) resolve to `null`.
|
|
@@ -1228,8 +1385,12 @@ function validateMintInput(
|
|
|
1228
1385
|
): z.output<typeof schema.MintInput> {
|
|
1229
1386
|
const valid = schema.MintInput.safeParse(input)
|
|
1230
1387
|
if (!valid.success) throw new InvalidInputError(z.prettifyError(valid.error))
|
|
1231
|
-
|
|
1232
|
-
|
|
1388
|
+
const scopeCatalog = options.scopeCatalog ?? Scope.catalog
|
|
1389
|
+
if (!validScopes(valid.data.scopes, scopeCatalog)) throw new InvalidInputError('unknown scope')
|
|
1390
|
+
if (!issuableScopes(valid.data.scopes, scopeCatalog))
|
|
1391
|
+
throw new InvalidInputError(
|
|
1392
|
+
'per-Zone scopes are retired; grant zone:read or zone:write with zoneTags',
|
|
1393
|
+
)
|
|
1233
1394
|
return {
|
|
1234
1395
|
...valid.data,
|
|
1235
1396
|
...(valid.data.expiresAt === undefined
|
|
@@ -1289,6 +1450,66 @@ function validScopes(scopes: readonly string[], scopeCatalog: Scope.Catalog): bo
|
|
|
1289
1450
|
return scopes.every((scope) => scope === Scope.wildcard || Scope.includes(scopeCatalog, scope))
|
|
1290
1451
|
}
|
|
1291
1452
|
|
|
1453
|
+
function issuableScopes(scopes: readonly string[], scopeCatalog: Scope.Catalog): boolean {
|
|
1454
|
+
return scopes.every((scope) => scope === Scope.wildcard || Scope.issuable(scopeCatalog, scope))
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1457
|
+
/** Drops retired per-Zone scopes a copied or republished key still holds; the rewrite has tagged it, so they grant nothing new. */
|
|
1458
|
+
function withoutRetired(scopes: readonly Scope.Id[]): readonly Scope.Id[] {
|
|
1459
|
+
return scopes.filter((scope) => Scope.parseZone(scope) === undefined)
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1462
|
+
/** Whether scopes broadcast through the generic Zone write scope, whose tags delegations bound. */
|
|
1463
|
+
function boundByDelegations(scopes: readonly string[]): boolean {
|
|
1464
|
+
return scopes.includes('zone:write')
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
/**
|
|
1468
|
+
* Refuses tags naming Zones the organization is not delegated in the key's
|
|
1469
|
+
* environment. Broadcasts never check accounts or tokens, so the delegation's
|
|
1470
|
+
* existence is what bounds a `zone:write` key; self-serve read keys are held
|
|
1471
|
+
* to the same bound by the management routes.
|
|
1472
|
+
*/
|
|
1473
|
+
export async function ensureDelegatedTags(db: Db.Db, options: ensureDelegatedTags.Options) {
|
|
1474
|
+
const { environment = 'production', orgId, zoneTags } = options
|
|
1475
|
+
if (!zoneTags?.length) throw new UndelegatedZoneTagsError()
|
|
1476
|
+
const delegated = await delegatedZones(db, { environment, orgId })
|
|
1477
|
+
const undelegated = zoneTags.filter((chainId) => !delegated.has(chainId))
|
|
1478
|
+
if (undelegated.length) throw new UndelegatedZoneTagsError(undelegated)
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
/** The tags among `zoneTags` whose Zone the organization is still delegated; undefined when none remain. */
|
|
1482
|
+
async function delegatedTags(
|
|
1483
|
+
db: Db.Db,
|
|
1484
|
+
options: ensureDelegatedTags.Options,
|
|
1485
|
+
): Promise<readonly number[] | undefined> {
|
|
1486
|
+
const { environment = 'production', orgId, zoneTags } = options
|
|
1487
|
+
if (!zoneTags?.length) return undefined
|
|
1488
|
+
const delegated = await delegatedZones(db, { environment, orgId })
|
|
1489
|
+
const kept = zoneTags.filter((chainId) => delegated.has(chainId))
|
|
1490
|
+
return kept.length ? kept : undefined
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
/** Chain ids of the organization's active delegations in one environment. */
|
|
1494
|
+
async function delegatedZones(
|
|
1495
|
+
db: Db.Db,
|
|
1496
|
+
owner: ZoneDelegations.Owner,
|
|
1497
|
+
): Promise<ReadonlySet<number>> {
|
|
1498
|
+
return new Set((await ZoneDelegations.listActive(db, owner)).map(({ chainId }) => chainId))
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
export declare namespace ensureDelegatedTags {
|
|
1502
|
+
/** Key owner and tags to check. */
|
|
1503
|
+
type Options = {
|
|
1504
|
+
/** Key environment; delegations are per environment. */
|
|
1505
|
+
environment?: ApiKey.ApiKey['environment'] | undefined
|
|
1506
|
+
/** Owning organization id. */
|
|
1507
|
+
orgId: string
|
|
1508
|
+
/** Tags the key would carry. */
|
|
1509
|
+
zoneTags?: readonly number[] | undefined
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1292
1513
|
/** Storage-key prefix for the `id → recordKey` index. */
|
|
1293
1514
|
const idPrefix = 'apikey_id:'
|
|
1294
1515
|
|
|
@@ -1429,6 +1650,18 @@ export class InvalidInputError extends Error {
|
|
|
1429
1650
|
}
|
|
1430
1651
|
}
|
|
1431
1652
|
|
|
1653
|
+
/** Thrown when a Zone key would carry tags outside its organization's active delegations. */
|
|
1654
|
+
export class UndelegatedZoneTagsError extends InvalidInputError {
|
|
1655
|
+
override name = 'ApiKeys.UndelegatedZoneTagsError'
|
|
1656
|
+
constructor(chainIds: readonly number[] = []) {
|
|
1657
|
+
super(
|
|
1658
|
+
chainIds.length
|
|
1659
|
+
? `\`zoneTags\` name Zones the organization is not delegated: ${chainIds.join(', ')}`
|
|
1660
|
+
: 'Zone scopes require `zoneTags` naming Zones the organization is delegated',
|
|
1661
|
+
)
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
|
|
1432
1665
|
/** Thrown when an API key owner is being deleted or is already deleted. */
|
|
1433
1666
|
export class OwnerDeletedError extends Error {
|
|
1434
1667
|
override name = 'ApiKeys.OwnerDeletedError'
|
package/src/App.ts
CHANGED
|
@@ -362,14 +362,13 @@ export function create<const path extends string = '/'>(options: create.Options<
|
|
|
362
362
|
}),
|
|
363
363
|
}),
|
|
364
364
|
)
|
|
365
|
-
//
|
|
366
|
-
//
|
|
367
|
-
// way; it never changes the response.
|
|
365
|
+
// Every Zone decision is metered once the handler returns, after later
|
|
366
|
+
// refusals (quota, sandbox, method allowlist) applied to the recorded grant.
|
|
368
367
|
base.use('*', async (c, next) => {
|
|
369
368
|
try {
|
|
370
369
|
await next()
|
|
371
370
|
} finally {
|
|
372
|
-
|
|
371
|
+
ZoneAccess.settle(c)
|
|
373
372
|
}
|
|
374
373
|
})
|
|
375
374
|
|
|
@@ -495,7 +494,7 @@ export function create<const path extends string = '/'>(options: create.Options<
|
|
|
495
494
|
// Reads and writes split by HTTP method; a write scope never reads Zone data.
|
|
496
495
|
// Recorded before workload identity runs so a refusal keeps its Zone attribution.
|
|
497
496
|
const method = c.req.method.toUpperCase()
|
|
498
|
-
const access = ZoneAccess.authorize(c, {
|
|
497
|
+
const access = await ZoneAccess.authorize(c, {
|
|
499
498
|
chainId,
|
|
500
499
|
verb: method === 'GET' || method === 'HEAD' ? 'read' : 'write',
|
|
501
500
|
})
|