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
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import { type Context, Hono } from 'hono'
|
|
2
|
+
import * as z from 'zod/mini'
|
|
3
|
+
|
|
4
|
+
import * as Db from '../../db/Db.js'
|
|
5
|
+
import * as Organizations from '../../db/tables/organizations.js'
|
|
6
|
+
import * as ZoneDelegationRequests from '../../db/tables/zoneDelegationRequests.js'
|
|
7
|
+
import * as ZoneDelegations from '../../db/tables/zoneDelegations.js'
|
|
8
|
+
import * as Cursor from '../../internal/Cursor.js'
|
|
9
|
+
import * as OpenApi from '../../internal/OpenApi.js'
|
|
10
|
+
import * as Response from '../../internal/Response.js'
|
|
11
|
+
import * as ZoneDelegationApproval from '../../internal/ZoneDelegationApproval.js'
|
|
12
|
+
import type * as App from '../App.js'
|
|
13
|
+
import * as Delegations from './zone-delegations.js'
|
|
14
|
+
|
|
15
|
+
/** Request schemas for the Zone delegation approval workflow. */
|
|
16
|
+
namespace schema {
|
|
17
|
+
export const Query = z.object({
|
|
18
|
+
cursor: z
|
|
19
|
+
.optional(
|
|
20
|
+
z.string().check(
|
|
21
|
+
z.maxLength(512),
|
|
22
|
+
z.refine((value) => Boolean(Cursor.decode(value, ['timestamp', 'id']))),
|
|
23
|
+
),
|
|
24
|
+
)
|
|
25
|
+
.check(
|
|
26
|
+
z.describe('Next-page cursor from a prior response.'),
|
|
27
|
+
z.meta({ examples: [Cursor.encode(['2026-01-01T00:00:00.000Z', 'zdr_example'])] }),
|
|
28
|
+
),
|
|
29
|
+
})
|
|
30
|
+
export const Params = z.object({
|
|
31
|
+
id: z
|
|
32
|
+
.string()
|
|
33
|
+
.check(z.regex(/^zdr_[A-Za-z0-9_-]+$/), z.describe('Zone delegation request identifier.')),
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Mounts authenticated request, review, approval, application, and cancellation operations. */
|
|
38
|
+
export function zoneDelegationRequests() {
|
|
39
|
+
return new Hono<App.Environment>()
|
|
40
|
+
.use('*', async (c, next) => {
|
|
41
|
+
const config = c.get('delegationApproval')
|
|
42
|
+
if (!config?.groupId || !config.lookup)
|
|
43
|
+
return Response.error(c, {
|
|
44
|
+
code: 'zone_delegation_approval_unconfigured',
|
|
45
|
+
message: 'The internal identity approver group is not configured.',
|
|
46
|
+
status: 503,
|
|
47
|
+
})
|
|
48
|
+
try {
|
|
49
|
+
const member = await config.identify(c.req.raw, c)
|
|
50
|
+
if (
|
|
51
|
+
!ZoneDelegationApproval.active(member) ||
|
|
52
|
+
member.email.toLowerCase() !== c.get('identity')?.email.toLowerCase()
|
|
53
|
+
)
|
|
54
|
+
return Response.error(c, {
|
|
55
|
+
code: 'unauthorized',
|
|
56
|
+
message: 'A verified internal identity session is required.',
|
|
57
|
+
status: 401,
|
|
58
|
+
})
|
|
59
|
+
c.set('approver', member)
|
|
60
|
+
await next()
|
|
61
|
+
if (c.error) throw c.error
|
|
62
|
+
return
|
|
63
|
+
} catch (cause) {
|
|
64
|
+
if (cause instanceof ZoneDelegationApproval.RequiredError) {
|
|
65
|
+
c.res = Response.error(c, {
|
|
66
|
+
code: 'zone_delegation_approval_required',
|
|
67
|
+
message: cause.message,
|
|
68
|
+
status: 403,
|
|
69
|
+
})
|
|
70
|
+
return c.res
|
|
71
|
+
}
|
|
72
|
+
c.get('metrics')?.count('zone_delegation_approval_failure_count', 1)
|
|
73
|
+
// Downstream errors may already have finalized Hono's response.
|
|
74
|
+
c.res = Response.upstream(c, cause)
|
|
75
|
+
return c.res
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
.post(
|
|
79
|
+
'/',
|
|
80
|
+
OpenApi.validate('json', Delegations.schema.Body, {
|
|
81
|
+
code: 'body_invalid',
|
|
82
|
+
// Two member lists of 1,000 addresses.
|
|
83
|
+
maxBytes: 256 * 1_024,
|
|
84
|
+
message: 'Invalid delegation request.',
|
|
85
|
+
}),
|
|
86
|
+
OpenApi.describeRoute({
|
|
87
|
+
description: 'File a new delegation for two-approver review. Widening an active delegation is revoking it and filing a new one.', // prettier-ignore
|
|
88
|
+
operationId: 'createZoneDelegationRequest',
|
|
89
|
+
responses: {
|
|
90
|
+
201: { description: 'The pending Zone delegation request.' },
|
|
91
|
+
404: { description: 'Organization not found.' },
|
|
92
|
+
},
|
|
93
|
+
summary: 'Request Zone delegation',
|
|
94
|
+
tags: ['Zone Delegation Requests'],
|
|
95
|
+
}),
|
|
96
|
+
async (c) => {
|
|
97
|
+
const config = c.get('delegationApproval')!
|
|
98
|
+
const input = c.req.valid('json')
|
|
99
|
+
const db = Db.get(c.get('db'))
|
|
100
|
+
if (!(await Organizations.get(db, input.orgId))) return notFound(c)
|
|
101
|
+
const request = await ZoneDelegationRequests.create(db, {
|
|
102
|
+
groupId: config.groupId,
|
|
103
|
+
input,
|
|
104
|
+
requester: c.get('approver'),
|
|
105
|
+
})
|
|
106
|
+
return c.json(request, 201)
|
|
107
|
+
},
|
|
108
|
+
)
|
|
109
|
+
.get(
|
|
110
|
+
'/',
|
|
111
|
+
OpenApi.validate('query', schema.Query, {
|
|
112
|
+
code: 'query_invalid',
|
|
113
|
+
message: 'Invalid request cursor.',
|
|
114
|
+
}),
|
|
115
|
+
OpenApi.describeRoute({
|
|
116
|
+
operationId: 'listZoneDelegationRequests',
|
|
117
|
+
responses: { 200: { description: 'A page of Zone delegation requests, newest first.' } },
|
|
118
|
+
summary: 'List delegation requests',
|
|
119
|
+
tags: ['Zone Delegation Requests'],
|
|
120
|
+
}),
|
|
121
|
+
async (c) =>
|
|
122
|
+
c.json(await ZoneDelegationRequests.list(Db.get(c.get('db')), c.req.valid('query')), 200),
|
|
123
|
+
)
|
|
124
|
+
.get(
|
|
125
|
+
'/:id',
|
|
126
|
+
OpenApi.validate('param', schema.Params, {
|
|
127
|
+
code: 'param_invalid',
|
|
128
|
+
message: 'Invalid request id.',
|
|
129
|
+
}),
|
|
130
|
+
OpenApi.describeRoute({
|
|
131
|
+
operationId: 'getZoneDelegationRequest',
|
|
132
|
+
responses: { 200: { description: 'The Zone delegation request with its approvals.' } },
|
|
133
|
+
summary: 'Get delegation request',
|
|
134
|
+
tags: ['Zone Delegation Requests'],
|
|
135
|
+
}),
|
|
136
|
+
async (c) => {
|
|
137
|
+
const request = await ZoneDelegationRequests.get(Db.get(c.get('db')), c.req.valid('param').id) // prettier-ignore
|
|
138
|
+
if (!request)
|
|
139
|
+
return Response.error(c, {
|
|
140
|
+
code: 'not_found',
|
|
141
|
+
message: 'Request not found.',
|
|
142
|
+
status: 404,
|
|
143
|
+
})
|
|
144
|
+
return c.json(request, 200)
|
|
145
|
+
},
|
|
146
|
+
)
|
|
147
|
+
.post(
|
|
148
|
+
'/:id/approve',
|
|
149
|
+
OpenApi.validate('param', schema.Params, {
|
|
150
|
+
code: 'param_invalid',
|
|
151
|
+
message: 'Invalid request id.',
|
|
152
|
+
}),
|
|
153
|
+
OpenApi.describeRoute({
|
|
154
|
+
operationId: 'approveZoneDelegationRequest',
|
|
155
|
+
responses: { 200: { description: 'The request with the recorded approval.' } },
|
|
156
|
+
summary: 'Approve delegation request',
|
|
157
|
+
tags: ['Zone Delegation Requests'],
|
|
158
|
+
}),
|
|
159
|
+
async (c) => {
|
|
160
|
+
const request = await ZoneDelegationRequests.approve(Db.get(c.get('db')), {
|
|
161
|
+
groupId: c.get('delegationApproval')!.groupId,
|
|
162
|
+
id: c.req.valid('param').id,
|
|
163
|
+
lookup: (ids) => c.get('delegationApproval')!.lookup(c.req.raw, ids),
|
|
164
|
+
member: c.get('approver'),
|
|
165
|
+
})
|
|
166
|
+
return c.json(request, 200)
|
|
167
|
+
},
|
|
168
|
+
)
|
|
169
|
+
.post(
|
|
170
|
+
'/:id/apply',
|
|
171
|
+
OpenApi.validate('param', schema.Params, {
|
|
172
|
+
code: 'param_invalid',
|
|
173
|
+
message: 'Invalid request id.',
|
|
174
|
+
}),
|
|
175
|
+
OpenApi.describeRoute({
|
|
176
|
+
operationId: 'applyZoneDelegationRequest',
|
|
177
|
+
responses: OpenApi.responses({
|
|
178
|
+
errors: { 409: 'An active delegation already covers this Zone.' },
|
|
179
|
+
success: { description: 'The created delegation.', schema: Delegations.schema.Response },
|
|
180
|
+
}),
|
|
181
|
+
summary: 'Apply delegation request',
|
|
182
|
+
tags: ['Zone Delegation Requests'],
|
|
183
|
+
}),
|
|
184
|
+
async (c) => {
|
|
185
|
+
const db = Db.get(c.get('db'))
|
|
186
|
+
const request = await ZoneDelegationRequests.claim(db, {
|
|
187
|
+
groupId: c.get('delegationApproval')!.groupId,
|
|
188
|
+
id: c.req.valid('param').id,
|
|
189
|
+
lookup: (ids) => c.get('delegationApproval')!.lookup(c.req.raw, ids),
|
|
190
|
+
member: c.get('approver'),
|
|
191
|
+
})
|
|
192
|
+
try {
|
|
193
|
+
const data = await ZoneDelegations.create(db, request.input)
|
|
194
|
+
await ZoneDelegationRequests.finish(db, request.id, { delegationId: data.id, status: 'applied' }) // prettier-ignore
|
|
195
|
+
return c.json(Response.validated(Delegations.schema.Response, { data }), 200)
|
|
196
|
+
} catch (cause) {
|
|
197
|
+
// A consumed claim is never replayed: the requester files a new request.
|
|
198
|
+
await ZoneDelegationRequests.finish(db, request.id, { status: 'failed' })
|
|
199
|
+
if (cause instanceof ZoneDelegations.ConflictError)
|
|
200
|
+
return Response.error(c, {
|
|
201
|
+
code: 'zone_delegation_exists',
|
|
202
|
+
message: 'An active delegation already covers this organization, environment, and Zone.', // prettier-ignore
|
|
203
|
+
status: 409,
|
|
204
|
+
})
|
|
205
|
+
throw cause
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
)
|
|
209
|
+
.delete(
|
|
210
|
+
'/:id',
|
|
211
|
+
OpenApi.validate('param', schema.Params, {
|
|
212
|
+
code: 'param_invalid',
|
|
213
|
+
message: 'Invalid request id.',
|
|
214
|
+
}),
|
|
215
|
+
OpenApi.describeRoute({
|
|
216
|
+
operationId: 'cancelZoneDelegationRequest',
|
|
217
|
+
responses: { 200: { description: 'The cancelled Zone delegation request.' } },
|
|
218
|
+
summary: 'Cancel delegation request',
|
|
219
|
+
tags: ['Zone Delegation Requests'],
|
|
220
|
+
}),
|
|
221
|
+
async (c) => {
|
|
222
|
+
const request = await ZoneDelegationRequests.cancel(Db.get(c.get('db')), c.req.valid('param').id) // prettier-ignore
|
|
223
|
+
if (!request)
|
|
224
|
+
return Response.error(c, {
|
|
225
|
+
code: 'not_found',
|
|
226
|
+
message: 'Pending request not found.',
|
|
227
|
+
status: 404,
|
|
228
|
+
})
|
|
229
|
+
return c.json(request, 200)
|
|
230
|
+
},
|
|
231
|
+
)
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function notFound(c: Context<App.Environment>) {
|
|
235
|
+
return Response.error(c, {
|
|
236
|
+
code: 'not_found',
|
|
237
|
+
message: 'Organization not found.',
|
|
238
|
+
status: 404,
|
|
239
|
+
})
|
|
240
|
+
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import * as TestAdmin from '../../../test/Admin.js'
|
|
2
2
|
import * as TestApp from '../../../test/App.js'
|
|
3
3
|
import * as ApiKey from '../../ApiKey.js'
|
|
4
|
+
import * as ApiKeys from '../../ApiKeys.js'
|
|
4
5
|
import * as AuditLogs from '../../db/tables/adminAuditLogs.js'
|
|
5
6
|
import * as Organizations from '../../db/tables/organizations.js'
|
|
6
7
|
import * as Projects from '../../db/tables/projects.js'
|
|
8
|
+
import * as ZoneDelegationsTable from '../../db/tables/zoneDelegations.js'
|
|
7
9
|
import * as ZoneDelegations from './zone-delegations.js'
|
|
8
10
|
|
|
9
11
|
const chainId = 421_700_001
|
|
@@ -16,31 +18,29 @@ describe('zoneDelegations', () => {
|
|
|
16
18
|
expect((await client['zone-delegations'].$get({ query: {} })).status).toBe(401)
|
|
17
19
|
})
|
|
18
20
|
|
|
19
|
-
test('
|
|
21
|
+
test('lists, narrows, and revokes a delegation with audit entries; widening needs approval', async () => {
|
|
20
22
|
const { client, db } = TestAdmin.setup()
|
|
21
23
|
const organization = await Organizations.create(db, { name: 'Delegated' })
|
|
22
|
-
const project = await Projects.create(db, { name: 'Checkout', orgId: organization.id })
|
|
23
24
|
const body = {
|
|
24
25
|
accounts: [`0x${'AB'.repeat(20)}` as const],
|
|
25
26
|
accountsAll: false,
|
|
26
27
|
chainId,
|
|
27
28
|
environment: 'production' as const,
|
|
28
29
|
orgId: organization.id,
|
|
29
|
-
projects: [project.id],
|
|
30
30
|
tokensAll: true,
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
// Creation only lands through an approved request.
|
|
33
34
|
const created = await client['zone-delegations'].$post({ json: body })
|
|
34
|
-
expect(created.status).toBe(
|
|
35
|
-
|
|
35
|
+
expect(created.status).toBe(403)
|
|
36
|
+
expect(await created.json()).toMatchObject({ error: { code: 'zone_delegation_approval_required' } }) // prettier-ignore
|
|
37
|
+
const data = await ZoneDelegationsTable.create(db, body)
|
|
38
|
+
await ZoneDelegationsTable.create(db, { ...body, chainId: chainId + 1 })
|
|
36
39
|
expect(stable(data)).toMatchInlineSnapshot(`
|
|
37
40
|
{
|
|
38
41
|
"accountsAll": false,
|
|
39
42
|
"chainId": 421700001,
|
|
40
43
|
"environment": "production",
|
|
41
|
-
"projects": [
|
|
42
|
-
"prj",
|
|
43
|
-
],
|
|
44
44
|
"tokensAll": true,
|
|
45
45
|
}
|
|
46
46
|
`)
|
|
@@ -51,10 +51,6 @@ describe('zoneDelegations', () => {
|
|
|
51
51
|
const tokens = await client['zone-delegations'][':id'].tokens.$get({ param: { id: data.id }, query: {} }) // prettier-ignore
|
|
52
52
|
expect((await TestApp.json(tokens, ZoneDelegations.schema.MembersResponse)).all).toBe(true)
|
|
53
53
|
|
|
54
|
-
// A second active delegation for the same Zone conflicts; another Zone is free.
|
|
55
|
-
expect((await client['zone-delegations'].$post({ json: body })).status).toBe(409)
|
|
56
|
-
expect((await client['zone-delegations'].$post({ json: { ...body, chainId: chainId + 1 } })).status).toBe(200) // prettier-ignore
|
|
57
|
-
|
|
58
54
|
const listed = await client['zone-delegations'].$get({
|
|
59
55
|
query: { environment: 'production', orgId: organization.id },
|
|
60
56
|
})
|
|
@@ -63,12 +59,14 @@ describe('zoneDelegations', () => {
|
|
|
63
59
|
const filtered = await client['zone-delegations'].$get({ query: { chainId: String(chainId) } })
|
|
64
60
|
expect((await TestApp.json(filtered, ZoneDelegations.schema.ListResponse)).data).toHaveLength(1)
|
|
65
61
|
|
|
62
|
+
// Widening edits are refused (revoke and request anew); narrowing ones apply.
|
|
63
|
+
expect((await client['zone-delegations'][':id'].$patch({ json: { accountsAll: true }, param: { id: data.id } })).status).toBe(403) // prettier-ignore
|
|
66
64
|
const patched = await client['zone-delegations'][':id'].$patch({
|
|
67
|
-
json: {
|
|
65
|
+
json: { tokensAll: false },
|
|
68
66
|
param: { id: data.id },
|
|
69
67
|
})
|
|
70
68
|
expect(patched.status).toBe(200)
|
|
71
|
-
expect((await TestApp.json(patched, ZoneDelegations.schema.Response)).data).toMatchObject({
|
|
69
|
+
expect((await TestApp.json(patched, ZoneDelegations.schema.Response)).data).toMatchObject({ id: data.id, tokensAll: false }) // prettier-ignore
|
|
72
70
|
expect((await client['zone-delegations'][':id'].$patch({ json: {}, param: { id: data.id } })).status).toBe(400) // prettier-ignore
|
|
73
71
|
|
|
74
72
|
const revoked = await client['zone-delegations'][':id'].$delete({ param: { id: data.id } })
|
|
@@ -79,11 +77,6 @@ describe('zoneDelegations', () => {
|
|
|
79
77
|
const fetched = await client['zone-delegations'][':id'].$get({ param: { id: data.id } })
|
|
80
78
|
expect((await TestApp.json(fetched, ZoneDelegations.schema.Response)).data.revokedAt).toBeDefined() // prettier-ignore
|
|
81
79
|
|
|
82
|
-
// The freed slot accepts a new delegation under a new id.
|
|
83
|
-
const next = await client['zone-delegations'].$post({ json: { ...body, accounts: undefined, accountsAll: true } }) // prettier-ignore
|
|
84
|
-
expect(next.status).toBe(200)
|
|
85
|
-
expect((await TestApp.json(next, ZoneDelegations.schema.Response)).data.id).not.toBe(data.id)
|
|
86
|
-
|
|
87
80
|
// Both the exact and the wildcard protected path match the collection, so dedupe by request.
|
|
88
81
|
const audit = await AuditLogs.list(db, { path: '/zone-delegations' })
|
|
89
82
|
const seen = new Set<string>()
|
|
@@ -93,44 +86,41 @@ describe('zoneDelegations', () => {
|
|
|
93
86
|
.map(({ method, status }) => `${method} ${status}`),
|
|
94
87
|
).toMatchInlineSnapshot(`
|
|
95
88
|
[
|
|
96
|
-
"POST 200",
|
|
97
89
|
"GET 200",
|
|
98
90
|
"PATCH 409",
|
|
99
91
|
"DELETE 404",
|
|
100
92
|
"DELETE 200",
|
|
101
93
|
"PATCH 400",
|
|
102
94
|
"PATCH 200",
|
|
95
|
+
"PATCH 403",
|
|
103
96
|
"GET 200",
|
|
104
97
|
"GET 200",
|
|
105
|
-
"POST 200",
|
|
106
|
-
"POST 409",
|
|
107
98
|
"GET 200",
|
|
108
99
|
"GET 200",
|
|
109
|
-
"POST
|
|
100
|
+
"POST 403",
|
|
110
101
|
]
|
|
111
102
|
`)
|
|
112
103
|
})
|
|
113
104
|
|
|
114
|
-
test('adds, lists, and removes
|
|
105
|
+
test('adds, lists, and removes members under an all flag; listed members only grow through approval', async () => {
|
|
115
106
|
const { client, db } = TestAdmin.setup()
|
|
116
107
|
const organization = await Organizations.create(db, { name: 'Members' })
|
|
117
|
-
const
|
|
118
|
-
json: { accountsAll: false, chainId, environment: 'production', orgId: organization.id, projects: 'all', tokensAll: false }, // prettier-ignore
|
|
119
|
-
})
|
|
120
|
-
const { data } = await TestApp.json(created, ZoneDelegations.schema.Response)
|
|
108
|
+
const data = await ZoneDelegationsTable.create(db, { accountsAll: true, chainId, environment: 'production', orgId: organization.id, tokensAll: false, tokens: [other] }) // prettier-ignore
|
|
121
109
|
const param = { id: data.id }
|
|
110
|
+
// Members under `all` are inert, so adding them widens nothing.
|
|
122
111
|
const added = await client['zone-delegations'][':id'].accounts.$post({ json: { addresses: [`0x${'AB'.repeat(20)}`, other] }, param }) // prettier-ignore
|
|
123
112
|
expect(added.status).toBe(200)
|
|
124
113
|
const addedBody = await TestApp.json(added, ZoneDelegations.schema.MembersResponse)
|
|
125
|
-
expect([addedBody.all, addedBody.data.map(({ address }) => address).sort()]).toEqual([
|
|
114
|
+
expect([addedBody.all, addedBody.data.map(({ address }) => address).sort()]).toEqual([true, [account, other]]) // prettier-ignore
|
|
126
115
|
const removed = await client['zone-delegations'][':id'].accounts.$delete({ json: { addresses: [other] }, param }) // prettier-ignore
|
|
127
116
|
expect(await TestApp.json(removed, ZoneDelegations.schema.RemovedResponse)).toEqual({ data: [other] }) // prettier-ignore
|
|
128
117
|
const accounts = await client['zone-delegations'][':id'].accounts.$get({ param, query: {} })
|
|
129
118
|
expect((await TestApp.json(accounts, ZoneDelegations.schema.MembersResponse)).data.map(({ address }) => address)).toEqual([account]) // prettier-ignore
|
|
130
119
|
|
|
131
|
-
|
|
132
|
-
expect(tokens.status).toBe(
|
|
133
|
-
expect(
|
|
120
|
+
// A listed token set grows only through a new approved delegation; removal stays open.
|
|
121
|
+
expect((await client['zone-delegations'][':id'].tokens.$post({ json: { addresses: [account] }, param })).status).toBe(403) // prettier-ignore
|
|
122
|
+
expect(await TestApp.json(await client['zone-delegations'][':id'].tokens.$delete({ json: { addresses: [other] }, param }), ZoneDelegations.schema.RemovedResponse)).toEqual({ data: [other] }) // prettier-ignore
|
|
123
|
+
expect((await TestApp.json(await client['zone-delegations'][':id'].tokens.$get({ param, query: {} }), ZoneDelegations.schema.MembersResponse)).data).toEqual([]) // prettier-ignore
|
|
134
124
|
|
|
135
125
|
expect((await client['zone-delegations'][':id'].accounts.$post({ json: { addresses: [] }, param })).status).toBe(400) // prettier-ignore
|
|
136
126
|
expect((await client['zone-delegations'][':id'].accounts.$post({ json: { addresses: ['0xnope' as never] }, param })).status).toBe(400) // prettier-ignore
|
|
@@ -175,11 +165,7 @@ describe('zoneDelegations', () => {
|
|
|
175
165
|
const plan = await TestApp.json(planned, ZoneDelegations.schema.BackfillPlan)
|
|
176
166
|
expect({
|
|
177
167
|
...plan,
|
|
178
|
-
delegations: plan.delegations.map((d) => ({
|
|
179
|
-
...d,
|
|
180
|
-
orgId: 'org',
|
|
181
|
-
projects: d.projects === 'all' ? d.projects : d.projects.map((id) => id.split('_')[0]),
|
|
182
|
-
})),
|
|
168
|
+
delegations: plan.delegations.map((d) => ({ ...d, orgId: 'org' })),
|
|
183
169
|
wildcard: plan.wildcard.map((k) => ({ ...k, orgId: 'org' })),
|
|
184
170
|
}).toMatchInlineSnapshot(`
|
|
185
171
|
{
|
|
@@ -189,7 +175,6 @@ describe('zoneDelegations', () => {
|
|
|
189
175
|
"chainId": 421700004,
|
|
190
176
|
"environment": "production",
|
|
191
177
|
"orgId": "org",
|
|
192
|
-
"projects": "all",
|
|
193
178
|
"tokensAll": true,
|
|
194
179
|
},
|
|
195
180
|
{
|
|
@@ -197,7 +182,6 @@ describe('zoneDelegations', () => {
|
|
|
197
182
|
"chainId": 421700001,
|
|
198
183
|
"environment": "production",
|
|
199
184
|
"orgId": "org",
|
|
200
|
-
"projects": "all",
|
|
201
185
|
"tokensAll": true,
|
|
202
186
|
},
|
|
203
187
|
],
|
|
@@ -233,38 +217,214 @@ describe('zoneDelegations', () => {
|
|
|
233
217
|
expect(rows.map(({ chainId, orgId }) => ({ chainId, owner: orgId.split('_')[0] })).sort((a, b) => a.chainId - b.chainId)).toEqual([{ chainId, owner: 'org' }, { chainId: chainId + 3, owner: 'key' }]) // prettier-ignore
|
|
234
218
|
})
|
|
235
219
|
|
|
236
|
-
test('
|
|
220
|
+
test('plans and applies the per-Zone read scope rewrite', async () => {
|
|
221
|
+
// Three hosted Zones; anything else a scope names is unconfigured.
|
|
222
|
+
const { client, db, store } = TestAdmin.setup({
|
|
223
|
+
zones: [0, 1, 2].map((offset) => TestApp.zone({ chainId: chainId + offset, rpcUrl: 'https://zone.rpc.test' })), // prettier-ignore
|
|
224
|
+
})
|
|
225
|
+
const organization = await Organizations.create(db, { name: 'Rewritten' })
|
|
226
|
+
const whole = { accountsAll: true, environment: 'production', orgId: organization.id, tokensAll: true } as const // prettier-ignore
|
|
227
|
+
await ZoneDelegationsTable.create(db, { ...whole, chainId })
|
|
228
|
+
const record = (id: string, token: string, extra: Record<string, unknown>) =>
|
|
229
|
+
store.put(
|
|
230
|
+
ApiKey.keyFor(token),
|
|
231
|
+
JSON.stringify({ createdAt: '2026-01-01T00:00:00.000Z', environment: 'production', id, orgId: organization.id, tokenLast4: token.slice(-4), ...extra }), // prettier-ignore
|
|
232
|
+
)
|
|
233
|
+
await record('key_reader', 'secret_reader', { scopes: ['data:read', `zone:${chainId}:read`, `zone:${chainId + 1}:read`] }) // prettier-ignore
|
|
234
|
+
// Hand-added tags never earn a delegation; sibling project keys share one Zone slot.
|
|
235
|
+
const project = await Projects.create(db, { name: 'Checkout', orgId: organization.id })
|
|
236
|
+
await record('key_tagged', 'secret_tagged', { projectId: project.id, scopes: [`zone:${chainId + 1}:read`], zoneTags: [chainId + 2] }) // prettier-ignore
|
|
237
|
+
await record('key_wild', 'secret_wild', { name: 'Integration', scopes: ['*'] })
|
|
238
|
+
await record('key_orphan', 'secret_orphan', { orgId: 'key_orphan', scopes: [`zone:${chainId}:read`] }) // prettier-ignore
|
|
239
|
+
await record('key_plain', 'secret_plain', { scopes: ['data:read'] })
|
|
240
|
+
await record('key_mixed', 'secret_mixed', { scopes: [`zone:${chainId}:read`, `zone:${chainId + 1}:write`] }) // prettier-ignore
|
|
241
|
+
// Already in both formats: its missing delegation is still ensured, the record is left alone.
|
|
242
|
+
await record('key_done', 'secret_done', { scopes: [`zone:${chainId + 2}:read`, 'zone:read'], zoneTags: [chainId + 2] }) // prettier-ignore
|
|
243
|
+
// A scope naming a Zone nothing serves earns no tag, and its delegation goes with it.
|
|
244
|
+
await record('key_stale', 'secret_stale', { scopes: [`zone:${chainId}:read`, `zone:${chainId + 9}:read`] }) // prettier-ignore
|
|
245
|
+
const stale = await ZoneDelegationsTable.create(db, { ...whole, chainId: chainId + 9 })
|
|
246
|
+
await record('key_tagless', 'secret_tagless', { scopes: ['data:read', 'zone:read'] })
|
|
247
|
+
|
|
248
|
+
const planned = await client['zone-delegations']['rewrite-keys'].$get()
|
|
249
|
+
expect(planned.status).toBe(200)
|
|
250
|
+
const plan = await TestApp.json(planned, ZoneDelegations.schema.RewritePlan)
|
|
251
|
+
const anonymize = (key: { orgId: string; projectId?: string | undefined }) => ({ ...key, orgId: key.orgId.split('_')[0], ...(key.projectId ? { projectId: 'prj' } : {}) }) // prettier-ignore
|
|
252
|
+
expect({
|
|
253
|
+
...plan,
|
|
254
|
+
changes: plan.changes.map(anonymize),
|
|
255
|
+
mixed: plan.mixed.map(anonymize),
|
|
256
|
+
orgless: plan.orgless.map(anonymize),
|
|
257
|
+
tagless: plan.tagless.map(anonymize),
|
|
258
|
+
wildcard: plan.wildcard.map(anonymize),
|
|
259
|
+
}).toMatchInlineSnapshot(`
|
|
260
|
+
{
|
|
261
|
+
"changes": [
|
|
262
|
+
{
|
|
263
|
+
"complete": true,
|
|
264
|
+
"environment": "production",
|
|
265
|
+
"id": "key_done",
|
|
266
|
+
"orgId": "org",
|
|
267
|
+
"revoked": [],
|
|
268
|
+
"scopes": [
|
|
269
|
+
"zone:421700003:read",
|
|
270
|
+
"zone:read",
|
|
271
|
+
],
|
|
272
|
+
"undelegated": [
|
|
273
|
+
421700003,
|
|
274
|
+
],
|
|
275
|
+
"zoneTags": [
|
|
276
|
+
421700003,
|
|
277
|
+
],
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
"complete": false,
|
|
281
|
+
"environment": "production",
|
|
282
|
+
"id": "key_reader",
|
|
283
|
+
"orgId": "org",
|
|
284
|
+
"revoked": [],
|
|
285
|
+
"scopes": [
|
|
286
|
+
"data:read",
|
|
287
|
+
"zone:421700001:read",
|
|
288
|
+
"zone:421700002:read",
|
|
289
|
+
"zone:read",
|
|
290
|
+
],
|
|
291
|
+
"undelegated": [
|
|
292
|
+
421700002,
|
|
293
|
+
],
|
|
294
|
+
"zoneTags": [
|
|
295
|
+
421700001,
|
|
296
|
+
421700002,
|
|
297
|
+
],
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
"complete": false,
|
|
301
|
+
"environment": "production",
|
|
302
|
+
"id": "key_tagged",
|
|
303
|
+
"orgId": "org",
|
|
304
|
+
"projectId": "prj",
|
|
305
|
+
"revoked": [],
|
|
306
|
+
"scopes": [
|
|
307
|
+
"zone:421700002:read",
|
|
308
|
+
"zone:read",
|
|
309
|
+
],
|
|
310
|
+
"undelegated": [
|
|
311
|
+
421700002,
|
|
312
|
+
],
|
|
313
|
+
"zoneTags": [
|
|
314
|
+
421700002,
|
|
315
|
+
421700003,
|
|
316
|
+
],
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
"complete": false,
|
|
320
|
+
"environment": "production",
|
|
321
|
+
"id": "key_stale",
|
|
322
|
+
"orgId": "org",
|
|
323
|
+
"revoked": [],
|
|
324
|
+
"scopes": [
|
|
325
|
+
"zone:421700001:read",
|
|
326
|
+
"zone:421700010:read",
|
|
327
|
+
"zone:read",
|
|
328
|
+
],
|
|
329
|
+
"undelegated": [],
|
|
330
|
+
"zoneTags": [
|
|
331
|
+
421700001,
|
|
332
|
+
],
|
|
333
|
+
},
|
|
334
|
+
],
|
|
335
|
+
"expiring": [],
|
|
336
|
+
"invalid": [],
|
|
337
|
+
"keys": 9,
|
|
338
|
+
"mixed": [
|
|
339
|
+
{
|
|
340
|
+
"environment": "production",
|
|
341
|
+
"id": "key_mixed",
|
|
342
|
+
"orgId": "org",
|
|
343
|
+
},
|
|
344
|
+
],
|
|
345
|
+
"narrow": [],
|
|
346
|
+
"orgless": [
|
|
347
|
+
{
|
|
348
|
+
"environment": "production",
|
|
349
|
+
"id": "key_orphan",
|
|
350
|
+
"orgId": "key",
|
|
351
|
+
},
|
|
352
|
+
],
|
|
353
|
+
"revoked": [],
|
|
354
|
+
"tagless": [
|
|
355
|
+
{
|
|
356
|
+
"environment": "production",
|
|
357
|
+
"id": "key_tagless",
|
|
358
|
+
"orgId": "org",
|
|
359
|
+
},
|
|
360
|
+
],
|
|
361
|
+
"unconfigured": [
|
|
362
|
+
421700010,
|
|
363
|
+
],
|
|
364
|
+
"unreadableRecords": 0,
|
|
365
|
+
"wildcard": [
|
|
366
|
+
{
|
|
367
|
+
"environment": "production",
|
|
368
|
+
"id": "key_wild",
|
|
369
|
+
"name": "Integration",
|
|
370
|
+
"orgId": "org",
|
|
371
|
+
},
|
|
372
|
+
],
|
|
373
|
+
}
|
|
374
|
+
`)
|
|
375
|
+
expect((await ApiKeys.resolve(store, 'secret_reader'))?.scopes).toEqual(['data:read', `zone:${chainId}:read`, `zone:${chainId + 1}:read`]) // prettier-ignore
|
|
376
|
+
|
|
377
|
+
// A delegation listing members would narrow a whole-Zone reader, so apply refuses before writing.
|
|
378
|
+
const listed = await ZoneDelegationsTable.create(db, { ...whole, accountsAll: false, chainId: chainId + 1 }) // prettier-ignore
|
|
379
|
+
const refused = await client['zone-delegations']['rewrite-keys'].$post()
|
|
380
|
+
expect(refused.status).toBe(400)
|
|
381
|
+
expect(await refused.json()).toMatchObject({ error: { code: 'zone_delegations_narrow' } })
|
|
382
|
+
expect((await ApiKeys.resolve(store, 'secret_reader'))?.zoneTags).toBeUndefined()
|
|
383
|
+
expect((await TestApp.json(await client['zone-delegations']['rewrite-keys'].$get(), ZoneDelegations.schema.RewritePlan)).narrow.map(({ id }) => id)).toEqual([listed.id]) // prettier-ignore
|
|
384
|
+
// Revoking it is an admin decision the rewrite must not undo: keys reading that Zone are reported and left alone.
|
|
385
|
+
await ZoneDelegationsTable.revoke(db, listed.id)
|
|
386
|
+
|
|
387
|
+
const applied = await client['zone-delegations']['rewrite-keys'].$post()
|
|
388
|
+
expect(applied.status).toBe(200)
|
|
389
|
+
const result = await TestApp.json(applied, ZoneDelegations.schema.RewriteApplied)
|
|
390
|
+
// The complete key's missing slot is ensured without writing its record; the two keys reading the revoked slot are reported and left unwritten.
|
|
391
|
+
expect([result.createdDelegations, result.revokedDelegations, result.rewritten]).toEqual([1, 1, 1]) // prettier-ignore
|
|
392
|
+
expect(result.changes.map(({ id, revoked, undelegated }) => [id, undelegated, revoked] as const).sort(([a], [b]) => a.localeCompare(b))).toEqual([['key_done', [], []], ['key_reader', [chainId + 1], [chainId + 1]], ['key_stale', [], []], ['key_tagged', [chainId + 1], [chainId + 1]]]) // prettier-ignore
|
|
393
|
+
expect(result.revoked.map(({ id }) => id)).toEqual([listed.id])
|
|
394
|
+
expect(await ApiKeys.resolve(store, 'secret_done')).toMatchObject({ scopes: [`zone:${chainId + 2}:read`, 'zone:read'], zoneTags: [chainId + 2] }) // prettier-ignore
|
|
395
|
+
expect((await ZoneDelegationsTable.get(db, stale.id))?.revokedAt).toBeDefined()
|
|
396
|
+
expect(await ApiKeys.resolve(store, 'secret_stale')).toMatchObject({ scopes: [`zone:${chainId}:read`, `zone:${chainId + 9}:read`, 'zone:read'], zoneTags: [chainId] }) // prettier-ignore
|
|
397
|
+
expect((await ApiKeys.resolve(store, 'secret_tagless'))?.scopes).toEqual(['data:read', 'zone:read']) // prettier-ignore
|
|
398
|
+
expect(await ApiKeys.resolve(store, 'secret_tagged')).toMatchObject({ scopes: [`zone:${chainId + 1}:read`], zoneTags: [chainId + 2] }) // prettier-ignore
|
|
399
|
+
expect(result.orgless.map(({ id }) => id)).toEqual(['key_orphan'])
|
|
400
|
+
expect((await ApiKeys.resolve(store, 'secret_orphan'))?.scopes).toEqual([`zone:${chainId}:read`]) // prettier-ignore
|
|
401
|
+
expect((await ApiKeys.resolve(store, 'secret_mixed'))?.zoneTags).toBeUndefined()
|
|
402
|
+
expect((await ApiKeys.resolve(store, 'secret_reader'))?.zoneTags).toBeUndefined()
|
|
403
|
+
const again = await TestApp.json(await client['zone-delegations']['rewrite-keys'].$post(), ZoneDelegations.schema.RewriteApplied) // prettier-ignore
|
|
404
|
+
// The rerun keeps showing the two blocked keys until an admin delegates the Zone again.
|
|
405
|
+
expect([again.rewritten, again.createdDelegations, again.revokedDelegations, again.changes.filter(({ complete }) => !complete).map(({ id }) => id).sort(), again.revoked.length]).toEqual([0, 0, 0, ['key_reader', 'key_tagged'], 1]) // prettier-ignore
|
|
406
|
+
})
|
|
407
|
+
|
|
408
|
+
test('refuses unknown organizations and malformed addresses', async () => {
|
|
237
409
|
const { client, db } = TestAdmin.setup()
|
|
238
410
|
const organization = await Organizations.create(db, { name: 'Owner' })
|
|
239
|
-
const otherOrganization = await Organizations.create(db, { name: 'Other' })
|
|
240
|
-
const foreign = await Projects.create(db, { name: 'Foreign', orgId: otherOrganization.id })
|
|
241
411
|
const body = {
|
|
242
412
|
accountsAll: true,
|
|
243
413
|
chainId,
|
|
244
414
|
environment: 'sandbox',
|
|
245
415
|
orgId: organization.id,
|
|
246
|
-
projects: 'all',
|
|
247
416
|
tokensAll: true,
|
|
248
417
|
} as const
|
|
249
418
|
expect((await client['zone-delegations'].$post({ json: { ...body, orgId: 'org_missing' } })).status).toBe(404) // prettier-ignore
|
|
250
|
-
expect((await client['zone-delegations'].$post({ json: { ...body, projects: [foreign.id] } })).status).toBe(404) // prettier-ignore
|
|
251
419
|
expect((await client['zone-delegations'].$post({ json: { ...body, accounts: ['0xnope'] as never } })).status).toBe(400) // prettier-ignore
|
|
252
420
|
expect((await client['zone-delegations'][':id'].$get({ param: { id: 'zd_missing' } })).status).toBe(404) // prettier-ignore
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
expect((await client['zone-delegations'][':id'].$patch({ json: { projects: [foreign.id] }, param: { id: data.id } })).status).toBe(404) // prettier-ignore
|
|
421
|
+
// The retired `projects` field is no longer part of the contract.
|
|
422
|
+
expect((await client['zone-delegations'].$post({ json: { ...body, projects: 'all' } as never })).status).toBe(400) // prettier-ignore
|
|
256
423
|
})
|
|
257
424
|
})
|
|
258
425
|
|
|
259
|
-
/** Drops generated ids and timestamps
|
|
260
|
-
function stable(record: {
|
|
261
|
-
createdAt:
|
|
262
|
-
|
|
263
|
-
updatedAt: string
|
|
264
|
-
orgId: string
|
|
265
|
-
projects: 'all' | readonly string[]
|
|
266
|
-
}) {
|
|
267
|
-
// prettier-ignore
|
|
268
|
-
const { createdAt: _c, id: _i, orgId: _o, projects, updatedAt: _u, ...rest } = record
|
|
269
|
-
return { ...rest, projects: projects === 'all' ? projects : projects.map((id) => id.split('_')[0]) } // prettier-ignore
|
|
426
|
+
/** Drops generated ids and timestamps. */
|
|
427
|
+
function stable(record: { createdAt: string; id: string; updatedAt: string; orgId: string }) {
|
|
428
|
+
const { createdAt: _c, id: _i, orgId: _o, updatedAt: _u, ...rest } = record
|
|
429
|
+
return rest
|
|
270
430
|
}
|