tapimo 0.5.3 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/App.d.ts +15830 -7220
- package/dist/App.d.ts.map +1 -1
- package/dist/App.js +9 -1
- package/dist/App.js.map +1 -1
- package/dist/Scope.d.ts +4 -0
- package/dist/Scope.d.ts.map +1 -1
- package/dist/Scope.js +5 -0
- package/dist/Scope.js.map +1 -1
- package/dist/admin/App.d.ts +1011 -412
- package/dist/admin/App.d.ts.map +1 -1
- package/dist/admin/App.js +4 -0
- package/dist/admin/App.js.map +1 -1
- package/dist/admin/apps/api-keys.d.ts +2 -1
- package/dist/admin/apps/api-keys.d.ts.map +1 -1
- package/dist/admin/apps/early-access.d.ts +148 -0
- package/dist/admin/apps/early-access.d.ts.map +1 -0
- package/dist/admin/apps/early-access.js +133 -0
- package/dist/admin/apps/early-access.js.map +1 -0
- package/dist/admin/apps/organizations.d.ts +124 -1
- package/dist/admin/apps/organizations.d.ts.map +1 -1
- package/dist/admin/apps/organizations.js +97 -6
- package/dist/admin/apps/organizations.js.map +1 -1
- package/dist/admin/apps/verified-tokens.d.ts +526 -1
- package/dist/admin/apps/verified-tokens.d.ts.map +1 -1
- package/dist/admin/apps/verified-tokens.js +214 -1
- package/dist/admin/apps/verified-tokens.js.map +1 -1
- package/dist/admin/ui.generated.d.ts +1 -1
- package/dist/admin/ui.generated.d.ts.map +1 -1
- package/dist/admin/ui.generated.js +95 -75
- package/dist/admin/ui.generated.js.map +1 -1
- package/dist/analytics/Analytics.d.ts.map +1 -1
- package/dist/analytics/Analytics.js +20 -0
- package/dist/analytics/Analytics.js.map +1 -1
- package/dist/analytics/tables/requestEvents.d.ts +18 -0
- package/dist/analytics/tables/requestEvents.d.ts.map +1 -1
- package/dist/analytics/tables/requestEvents.js +36 -7
- package/dist/analytics/tables/requestEvents.js.map +1 -1
- package/dist/apps/data/App.d.ts +6260 -4924
- package/dist/apps/data/App.d.ts.map +1 -1
- package/dist/apps/data/App.js +11 -6
- package/dist/apps/data/App.js.map +1 -1
- package/dist/apps/data/FundingQuoteProviders.d.ts +34 -1
- package/dist/apps/data/FundingQuoteProviders.d.ts.map +1 -1
- package/dist/apps/data/FundingQuoteProviders.js +194 -16
- package/dist/apps/data/FundingQuoteProviders.js.map +1 -1
- package/dist/apps/data/FxOracle.d.ts +71 -0
- package/dist/apps/data/FxOracle.d.ts.map +1 -0
- package/dist/apps/data/FxOracle.js +97 -0
- package/dist/apps/data/FxOracle.js.map +1 -0
- package/dist/apps/data/index.d.ts +1 -0
- package/dist/apps/data/index.d.ts.map +1 -1
- package/dist/apps/data/index.js +1 -0
- package/dist/apps/data/index.js.map +1 -1
- package/dist/apps/data/routes/activities.d.ts +3599 -1382
- package/dist/apps/data/routes/activities.d.ts.map +1 -1
- package/dist/apps/data/routes/activities.js +250 -88
- package/dist/apps/data/routes/activities.js.map +1 -1
- package/dist/apps/data/routes/addresses.d.ts +872 -251
- package/dist/apps/data/routes/addresses.d.ts.map +1 -1
- package/dist/apps/data/routes/addresses.js +11 -9
- package/dist/apps/data/routes/addresses.js.map +1 -1
- package/dist/apps/data/routes/balances.d.ts +77 -26
- package/dist/apps/data/routes/balances.d.ts.map +1 -1
- package/dist/apps/data/routes/balances.js +88 -45
- package/dist/apps/data/routes/balances.js.map +1 -1
- package/dist/apps/data/routes/blocks.d.ts +39 -39
- package/dist/apps/data/routes/coingecko.d.ts +54 -54
- package/dist/apps/data/routes/exchanges.d.ts +856 -105
- package/dist/apps/data/routes/exchanges.d.ts.map +1 -1
- package/dist/apps/data/routes/exchanges.js +518 -76
- package/dist/apps/data/routes/exchanges.js.map +1 -1
- package/dist/apps/data/routes/fee-amm.d.ts +81 -67
- package/dist/apps/data/routes/fee-amm.d.ts.map +1 -1
- package/dist/apps/data/routes/fee-amm.js +76 -91
- package/dist/apps/data/routes/fee-amm.js.map +1 -1
- package/dist/apps/data/routes/funding.d.ts +80 -36
- package/dist/apps/data/routes/funding.d.ts.map +1 -1
- package/dist/apps/data/routes/funding.js +65 -31
- package/dist/apps/data/routes/funding.js.map +1 -1
- package/dist/apps/data/routes/receipts.d.ts +392 -315
- package/dist/apps/data/routes/receipts.d.ts.map +1 -1
- package/dist/apps/data/routes/receipts.js +152 -77
- package/dist/apps/data/routes/receipts.js.map +1 -1
- package/dist/apps/data/routes/tokens.d.ts +167 -174
- package/dist/apps/data/routes/tokens.d.ts.map +1 -1
- package/dist/apps/data/routes/tokens.js +15 -13
- package/dist/apps/data/routes/tokens.js.map +1 -1
- package/dist/apps/data/routes/transactions.d.ts +277 -536
- package/dist/apps/data/routes/transactions.d.ts.map +1 -1
- package/dist/apps/data/routes/transactions.js +96 -77
- package/dist/apps/data/routes/transactions.js.map +1 -1
- package/dist/apps/data/routes/transfers.d.ts +157 -81
- package/dist/apps/data/routes/transfers.d.ts.map +1 -1
- package/dist/apps/data/routes/transfers.js +272 -130
- package/dist/apps/data/routes/transfers.js.map +1 -1
- package/dist/apps/data/routes/valuation.d.ts +307 -0
- package/dist/apps/data/routes/valuation.d.ts.map +1 -0
- package/dist/apps/data/routes/valuation.js +292 -0
- package/dist/apps/data/routes/valuation.js.map +1 -0
- package/dist/apps/data/routes/verified-tokens.d.ts +36 -36
- package/dist/apps/data/routes/verified-tokens.d.ts.map +1 -1
- package/dist/apps/data/routes/verified-tokens.js +7 -5
- package/dist/apps/data/routes/verified-tokens.js.map +1 -1
- package/dist/apps/data/routes/webhooks.d.ts +103 -123
- package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
- package/dist/apps/data/routes/webhooks.js +124 -25
- package/dist/apps/data/routes/webhooks.js.map +1 -1
- package/dist/apps/index.d.ts +2 -1
- package/dist/apps/index.d.ts.map +1 -1
- package/dist/apps/index.js +2 -1
- package/dist/apps/index.js.map +1 -1
- package/dist/apps/management/App.d.ts +8554 -1692
- package/dist/apps/management/App.d.ts.map +1 -1
- package/dist/apps/management/App.js +12 -1
- package/dist/apps/management/App.js.map +1 -1
- package/dist/apps/management/routes/api-keys.d.ts +7 -0
- package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
- package/dist/apps/management/routes/api-keys.js +17 -3
- package/dist/apps/management/routes/api-keys.js.map +1 -1
- package/dist/apps/management/routes/billing.d.ts +157 -162
- package/dist/apps/management/routes/billing.d.ts.map +1 -1
- package/dist/apps/management/routes/billing.js +31 -23
- package/dist/apps/management/routes/billing.js.map +1 -1
- package/dist/apps/management/routes/faucet.d.ts +232 -0
- package/dist/apps/management/routes/faucet.d.ts.map +1 -0
- package/dist/apps/management/routes/faucet.js +154 -0
- package/dist/apps/management/routes/faucet.js.map +1 -0
- package/dist/apps/management/routes/invitations.d.ts +60 -60
- package/dist/apps/management/routes/invite-links.d.ts +49 -20
- package/dist/apps/management/routes/invite-links.d.ts.map +1 -1
- package/dist/apps/management/routes/invite-links.js +26 -1
- 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/members.d.ts +6 -6
- package/dist/apps/management/routes/orgs.d.ts +42 -42
- package/dist/apps/management/routes/orgs.d.ts.map +1 -1
- package/dist/apps/management/routes/orgs.js +5 -0
- package/dist/apps/management/routes/orgs.js.map +1 -1
- package/dist/apps/management/routes/projects.d.ts +44 -44
- package/dist/apps/management/routes/usage.d.ts +3 -0
- package/dist/apps/management/routes/usage.d.ts.map +1 -1
- package/dist/apps/management/routes/usage.js +4 -0
- package/dist/apps/management/routes/usage.js.map +1 -1
- package/dist/apps/management/routes/verified-token-requests.d.ts +697 -0
- package/dist/apps/management/routes/verified-token-requests.d.ts.map +1 -0
- package/dist/apps/management/routes/verified-token-requests.js +271 -0
- package/dist/apps/management/routes/verified-token-requests.js.map +1 -0
- package/dist/apps/management/routes/webhooks.d.ts +6977 -0
- package/dist/apps/management/routes/webhooks.d.ts.map +1 -0
- package/dist/apps/management/routes/webhooks.js +573 -0
- package/dist/apps/management/routes/webhooks.js.map +1 -0
- package/dist/apps/mpp/App.d.ts +363 -0
- package/dist/apps/mpp/App.d.ts.map +1 -0
- package/dist/apps/mpp/App.js +72 -0
- package/dist/apps/mpp/App.js.map +1 -0
- package/dist/apps/mpp/Mpp.d.ts +178 -0
- package/dist/apps/mpp/Mpp.d.ts.map +1 -0
- package/dist/apps/mpp/Mpp.js +183 -0
- package/dist/apps/mpp/Mpp.js.map +1 -0
- package/dist/apps/mpp/index.d.ts +3 -0
- package/dist/apps/mpp/index.d.ts.map +1 -0
- package/dist/apps/mpp/index.js +3 -0
- package/dist/apps/mpp/index.js.map +1 -0
- package/dist/apps/relay/App.d.ts.map +1 -1
- package/dist/apps/relay/App.js +56 -9
- package/dist/apps/relay/App.js.map +1 -1
- package/dist/db/Db.d.ts +6 -0
- package/dist/db/Db.d.ts.map +1 -1
- package/dist/db/Db.js +110 -0
- package/dist/db/Db.js.map +1 -1
- package/dist/db/Schema.d.ts +53 -0
- package/dist/db/Schema.d.ts.map +1 -1
- package/dist/db/Schema.js +56 -0
- package/dist/db/Schema.js.map +1 -1
- package/dist/db/tables/earlyAccess.d.ts +50 -0
- package/dist/db/tables/earlyAccess.d.ts.map +1 -0
- package/dist/db/tables/earlyAccess.js +63 -0
- package/dist/db/tables/earlyAccess.js.map +1 -0
- package/dist/db/tables/enabledBillingSources.d.ts +73 -0
- package/dist/db/tables/enabledBillingSources.d.ts.map +1 -0
- package/dist/db/tables/enabledBillingSources.js +69 -0
- package/dist/db/tables/enabledBillingSources.js.map +1 -0
- package/dist/db/tables/invitations.d.ts.map +1 -1
- package/dist/db/tables/invitations.js +3 -0
- package/dist/db/tables/invitations.js.map +1 -1
- package/dist/db/tables/inviteLinks.d.ts +10 -2
- package/dist/db/tables/inviteLinks.d.ts.map +1 -1
- package/dist/db/tables/inviteLinks.js +10 -1
- package/dist/db/tables/inviteLinks.js.map +1 -1
- package/dist/db/tables/organizations.d.ts +5 -2
- package/dist/db/tables/organizations.d.ts.map +1 -1
- package/dist/db/tables/organizations.js +13 -2
- package/dist/db/tables/organizations.js.map +1 -1
- package/dist/db/tables/sponsoredTransactions.d.ts +2 -0
- package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -1
- package/dist/db/tables/sponsoredTransactions.js +2 -0
- package/dist/db/tables/sponsoredTransactions.js.map +1 -1
- package/dist/db/tables/verifiedTokenRequests.d.ts +94 -0
- package/dist/db/tables/verifiedTokenRequests.d.ts.map +1 -0
- package/dist/db/tables/verifiedTokenRequests.js +120 -0
- package/dist/db/tables/verifiedTokenRequests.js.map +1 -0
- package/dist/db/tables/webhookSubscriptions.d.ts +14 -0
- package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -1
- package/dist/db/tables/webhookSubscriptions.js +16 -0
- package/dist/db/tables/webhookSubscriptions.js.map +1 -1
- package/dist/handlers/internal/utils.d.ts +1 -16
- package/dist/handlers/internal/utils.d.ts.map +1 -1
- package/dist/handlers/internal/utils.js +17 -6
- package/dist/handlers/internal/utils.js.map +1 -1
- package/dist/handlers/relay.d.ts.map +1 -1
- package/dist/handlers/relay.js +37 -5
- package/dist/handlers/relay.js.map +1 -1
- package/dist/internal/Auth.d.ts +11 -2
- package/dist/internal/Auth.d.ts.map +1 -1
- package/dist/internal/Auth.js +14 -47
- package/dist/internal/Auth.js.map +1 -1
- package/dist/internal/FundingQuotes.d.ts +147 -47
- package/dist/internal/FundingQuotes.d.ts.map +1 -1
- package/dist/internal/FundingQuotes.js +119 -33
- package/dist/internal/FundingQuotes.js.map +1 -1
- package/dist/internal/Log.d.ts +37 -5
- package/dist/internal/Log.d.ts.map +1 -1
- package/dist/internal/Log.js +52 -3
- package/dist/internal/Log.js.map +1 -1
- package/dist/internal/MetricSink.d.ts.map +1 -1
- package/dist/internal/MetricSink.js +24 -0
- package/dist/internal/MetricSink.js.map +1 -1
- package/dist/internal/OpenApi.d.ts +2 -2
- package/dist/internal/OpenApi.d.ts.map +1 -1
- package/dist/internal/OpenApi.js +3 -0
- package/dist/internal/OpenApi.js.map +1 -1
- package/dist/internal/Response.d.ts +2 -2
- package/dist/internal/Response.d.ts.map +1 -1
- package/dist/internal/Response.js +12 -4
- package/dist/internal/Response.js.map +1 -1
- package/dist/internal/Schema.d.ts +14 -5
- package/dist/internal/Schema.d.ts.map +1 -1
- package/dist/internal/Schema.js +23 -5
- package/dist/internal/Schema.js.map +1 -1
- package/dist/internal/Value.d.ts +18 -0
- package/dist/internal/Value.d.ts.map +1 -1
- package/dist/internal/Value.js +11 -1
- package/dist/internal/Value.js.map +1 -1
- package/dist/internal/VerifiedTokens.d.ts.map +1 -1
- package/dist/internal/VerifiedTokens.js +10 -1
- package/dist/internal/VerifiedTokens.js.map +1 -1
- package/dist/internal/Webhooks.d.ts +24 -3
- package/dist/internal/Webhooks.d.ts.map +1 -1
- package/dist/internal/Webhooks.js +34 -8
- package/dist/internal/Webhooks.js.map +1 -1
- package/package.json +7 -2
- package/src/App.test.ts +9 -4
- package/src/App.ts +23 -3
- package/src/Client.test-d.ts +733 -20
- package/src/Scope.ts +5 -0
- package/src/admin/App.test.ts +4 -0
- package/src/admin/App.ts +4 -0
- package/src/admin/apps/early-access.test.ts +125 -0
- package/src/admin/apps/early-access.ts +176 -0
- package/src/admin/apps/organizations.test.ts +86 -2
- package/src/admin/apps/organizations.ts +155 -31
- package/src/admin/apps/scopes.test.ts +5 -0
- package/src/admin/apps/verified-tokens.test.ts +201 -0
- package/src/admin/apps/verified-tokens.ts +532 -286
- package/src/admin/ui/plugin.ts +2 -1
- package/src/admin/ui/src/lib/early-access.ts +63 -0
- package/src/admin/ui/src/lib/organizations.test.ts +81 -1
- package/src/admin/ui/src/lib/organizations.ts +92 -0
- package/src/admin/ui/src/lib/verified-tokens.ts +79 -0
- package/src/admin/ui/src/routeTree.gen.ts +33 -2
- package/src/admin/ui/src/routes/__root.tsx +2 -1
- package/src/admin/ui/src/routes/api-keys.tsx +1 -1
- package/src/admin/ui/src/routes/early-access.tsx +162 -0
- package/src/admin/ui/src/routes/organizations_.$orgId.tsx +103 -0
- package/src/admin/ui/src/routes/verified-tokens.tsx +230 -19
- package/src/admin/ui.generated.ts +95 -94
- package/src/analytics/Analytics.test.ts +83 -0
- package/src/analytics/Analytics.ts +20 -0
- package/src/analytics/tables/requestEvents.ts +60 -7
- package/src/apps/composition.test.ts +11 -2
- package/src/apps/data/App.test.ts +1 -1
- package/src/apps/data/App.ts +22 -14
- package/src/apps/data/FundingQuoteProviders.test.ts +297 -4
- package/src/apps/data/FundingQuoteProviders.ts +255 -29
- package/src/apps/data/FxOracle.test.ts +118 -0
- package/src/apps/data/FxOracle.ts +153 -0
- package/src/apps/data/index.ts +1 -0
- package/src/apps/data/routes/activities.test.ts +177 -3
- package/src/apps/data/routes/activities.ts +441 -132
- package/src/apps/data/routes/addresses.ts +19 -9
- package/src/apps/data/routes/balances.test.ts +148 -11
- package/src/apps/data/routes/balances.ts +140 -51
- package/src/apps/data/routes/exchanges.test.ts +544 -9
- package/src/apps/data/routes/exchanges.ts +690 -103
- package/src/apps/data/routes/fee-amm.test.ts +47 -23
- package/src/apps/data/routes/fee-amm.ts +98 -119
- package/src/apps/data/routes/funding.test.ts +113 -8
- package/src/apps/data/routes/funding.ts +77 -51
- package/src/apps/data/routes/receipts.test.ts +129 -23
- package/src/apps/data/routes/receipts.ts +205 -100
- package/src/apps/data/routes/tokens.test.ts +43 -0
- package/src/apps/data/routes/tokens.ts +28 -13
- package/src/apps/data/routes/transactions.test.ts +72 -19
- package/src/apps/data/routes/transactions.ts +162 -92
- package/src/apps/data/routes/transfers.test.ts +341 -31
- package/src/apps/data/routes/transfers.ts +358 -158
- package/src/apps/data/routes/valuation.test.ts +437 -0
- package/src/apps/data/routes/valuation.ts +423 -0
- package/src/apps/data/routes/verified-tokens.ts +14 -2
- package/src/apps/data/routes/webhooks.test.ts +56 -8
- package/src/apps/data/routes/webhooks.ts +144 -27
- package/src/apps/index.ts +2 -1
- package/src/apps/management/App.ts +13 -0
- package/src/apps/management/routes/api-keys.test.ts +14 -0
- package/src/apps/management/routes/api-keys.ts +27 -3
- package/src/apps/management/routes/billing.test.ts +152 -20
- package/src/apps/management/routes/billing.ts +34 -18
- package/src/apps/management/routes/faucet.test.ts +87 -0
- package/src/apps/management/routes/faucet.ts +201 -0
- package/src/apps/management/routes/invite-links.test.ts +117 -0
- package/src/apps/management/routes/invite-links.ts +43 -1
- package/src/apps/management/routes/me.test.ts +9 -99
- package/src/apps/management/routes/orgs.test.ts +45 -2
- package/src/apps/management/routes/orgs.ts +6 -0
- package/src/apps/management/routes/scopes.test.ts +1 -0
- package/src/apps/management/routes/usage.test.ts +6 -0
- package/src/apps/management/routes/usage.ts +7 -0
- package/src/apps/management/routes/verified-token-requests.test.ts +377 -0
- package/src/apps/management/routes/verified-token-requests.ts +397 -0
- package/src/apps/management/routes/webhooks.test.ts +426 -0
- package/src/apps/management/routes/webhooks.ts +672 -0
- package/src/apps/mpp/App.ts +86 -0
- package/src/apps/mpp/Mpp.test.ts +193 -0
- package/src/apps/mpp/Mpp.ts +353 -0
- package/src/apps/mpp/index.ts +2 -0
- package/src/apps/mpp/internal/Adapter.ts +11 -0
- package/src/apps/mpp/internal/Error.ts +9 -0
- package/src/apps/mpp/internal/Idempotency.ts +7 -0
- package/src/apps/mpp/internal/Screening.ts +10 -0
- package/src/apps/relay/App.test.ts +88 -6
- package/src/apps/relay/App.ts +61 -9
- package/src/db/Db.test.ts +19 -0
- package/src/db/Db.ts +125 -0
- package/src/db/Schema.ts +68 -0
- package/src/db/tables/earlyAccess.ts +86 -0
- package/src/db/tables/enabledBillingSources.test.ts +303 -0
- package/src/db/tables/enabledBillingSources.ts +118 -0
- package/src/db/tables/invitations.ts +3 -0
- package/src/db/tables/inviteLinks.ts +22 -3
- package/src/db/tables/organizations.test.ts +103 -0
- package/src/db/tables/organizations.ts +18 -2
- package/src/db/tables/sponsoredTransactions.ts +4 -0
- package/src/db/tables/verifiedTokenRequests.test.ts +200 -0
- package/src/db/tables/verifiedTokenRequests.ts +182 -0
- package/src/db/tables/webhookSubscriptions.ts +31 -0
- package/src/handlers/internal/utils.ts +22 -6
- package/src/handlers/relay.errors.test.ts +153 -0
- package/src/handlers/relay.ts +42 -7
- package/src/internal/Auth.test.ts +3 -3
- package/src/internal/Auth.ts +15 -47
- package/src/internal/FundingQuotes.test.ts +161 -6
- package/src/internal/FundingQuotes.ts +141 -39
- package/src/internal/Log.test-d.ts +21 -0
- package/src/internal/Log.test.ts +141 -0
- package/src/internal/Log.ts +93 -6
- package/src/internal/MetricSink.test.ts +215 -0
- package/src/internal/MetricSink.ts +24 -0
- package/src/internal/OpenApi.ts +3 -0
- package/src/internal/Response.test.ts +28 -0
- package/src/internal/Response.ts +12 -4
- package/src/internal/Schema.ts +50 -5
- package/src/internal/Value.test.ts +20 -0
- package/src/internal/Value.ts +24 -1
- package/src/internal/VerifiedTokens.ts +9 -1
- package/src/internal/Webhooks.test.ts +140 -0
- package/src/internal/Webhooks.ts +53 -9
package/src/admin/ui/plugin.ts
CHANGED
|
@@ -23,7 +23,8 @@ const outFile = path.join(here, '..', 'ui.generated.ts')
|
|
|
23
23
|
// Total gzipped budget for the whole UI; the React + TanStack + Regen baseline
|
|
24
24
|
// is heavier than a hand-rolled SPA, so fail loudly before it bloats the
|
|
25
25
|
// package. Raise deliberately (and revisit code-splitting) if it trips.
|
|
26
|
-
|
|
26
|
+
// Raised from 405 KB for the early-access page.
|
|
27
|
+
const maxGzipBytes = 415_000
|
|
27
28
|
|
|
28
29
|
const contentTypes: Record<string, string> = {
|
|
29
30
|
'.css': 'text/css; charset=utf-8',
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import * as ReactQuery from '@tanstack/react-query'
|
|
2
|
+
import type { InferResponseType } from 'hono/client'
|
|
3
|
+
|
|
4
|
+
import { client } from './client'
|
|
5
|
+
|
|
6
|
+
/** A stored allowlist entry (domain or exact email) from the list endpoint. */
|
|
7
|
+
export type Entry = InferResponseType<(typeof client)['early-access']['$get'], 200>['data'][number]
|
|
8
|
+
|
|
9
|
+
/** List early-access allowlist entries. */
|
|
10
|
+
export namespace list {
|
|
11
|
+
export const key = ['early-access'] as const
|
|
12
|
+
export async function fn() {
|
|
13
|
+
const response = await client['early-access'].$get()
|
|
14
|
+
if (!response.ok) throw new Error('Failed to load entries.')
|
|
15
|
+
return response.json()
|
|
16
|
+
}
|
|
17
|
+
export function options() {
|
|
18
|
+
return ReactQuery.queryOptions({ queryFn: fn, queryKey: key, staleTime: 30_000 })
|
|
19
|
+
}
|
|
20
|
+
export function useQuery() {
|
|
21
|
+
return ReactQuery.useQuery(options())
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Add an allowlist entry: a bare domain or a full email. */
|
|
26
|
+
export namespace add {
|
|
27
|
+
export type Input = { entry: string }
|
|
28
|
+
export async function fn(input: Input) {
|
|
29
|
+
const response = await client['early-access'].$post({ json: input })
|
|
30
|
+
if (!response.ok) throw new Error(await errorMessage(response, 'Failed to add entry.'))
|
|
31
|
+
return response.json()
|
|
32
|
+
}
|
|
33
|
+
export function useMutation() {
|
|
34
|
+
const queryClient = ReactQuery.useQueryClient()
|
|
35
|
+
return ReactQuery.useMutation({
|
|
36
|
+
mutationFn: fn,
|
|
37
|
+
onSuccess: () => queryClient.invalidateQueries({ queryKey: list.key }),
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Remove an allowlist entry. */
|
|
43
|
+
export namespace remove {
|
|
44
|
+
export type Input = { entry: string }
|
|
45
|
+
export async function fn({ entry }: Input) {
|
|
46
|
+
const response = await client['early-access'].$delete({ json: { entry } })
|
|
47
|
+
if (!response.ok) throw new Error(await errorMessage(response, 'Failed to remove entry.'))
|
|
48
|
+
return response.json()
|
|
49
|
+
}
|
|
50
|
+
export function useMutation() {
|
|
51
|
+
const queryClient = ReactQuery.useQueryClient()
|
|
52
|
+
return ReactQuery.useMutation({
|
|
53
|
+
mutationFn: fn,
|
|
54
|
+
onSuccess: () => queryClient.invalidateQueries({ queryKey: list.key }),
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Server error-envelope message, with a fallback for non-JSON bodies. */
|
|
60
|
+
async function errorMessage(response: { json(): Promise<unknown> }, fallback: string) {
|
|
61
|
+
const body = (await response.json().catch(() => null)) as { error?: { message?: string } } | null
|
|
62
|
+
return body?.error?.message ?? fallback
|
|
63
|
+
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { QueryClient } from '@tanstack/react-query'
|
|
2
2
|
import { createMemoryHistory, createRouter } from '@tanstack/react-router'
|
|
3
|
+
import { createElement } from 'react'
|
|
4
|
+
import { renderToStaticMarkup } from 'react-dom/server'
|
|
3
5
|
import { describe, expect, it, vi } from 'vite-plus/test'
|
|
4
6
|
|
|
5
7
|
import { routeTree } from '../routeTree.gen'
|
|
6
|
-
import { Route as DetailRoute } from '../routes/organizations_.$orgId'
|
|
8
|
+
import { BillingSourcesCard, Route as DetailRoute } from '../routes/organizations_.$orgId'
|
|
7
9
|
import { Route as ListRoute } from '../routes/organizations'
|
|
8
10
|
import * as Organizations from './organizations'
|
|
9
11
|
|
|
@@ -49,6 +51,84 @@ describe('searchParameters', () => {
|
|
|
49
51
|
})
|
|
50
52
|
})
|
|
51
53
|
|
|
54
|
+
describe('withEnabledSources', () => {
|
|
55
|
+
it('replaces only organization-wide billing availability', () => {
|
|
56
|
+
const detail = {
|
|
57
|
+
billing: {
|
|
58
|
+
enabledSources: [],
|
|
59
|
+
production: { settings: null, source: null },
|
|
60
|
+
sandbox: { settings: null, source: null },
|
|
61
|
+
},
|
|
62
|
+
createdAt: '2026-01-01T00:00:00.000Z',
|
|
63
|
+
createdBy: null,
|
|
64
|
+
id: 'org_example',
|
|
65
|
+
memberCount: 0,
|
|
66
|
+
name: 'Example',
|
|
67
|
+
projectCount: 0,
|
|
68
|
+
updatedAt: '2026-01-01T00:00:00.000Z',
|
|
69
|
+
userId: null,
|
|
70
|
+
} satisfies Organizations.Detail
|
|
71
|
+
|
|
72
|
+
const updated = Organizations.withEnabledSources({
|
|
73
|
+
detail,
|
|
74
|
+
enabledSources: ['stripe', 'tempo'],
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
expect({
|
|
78
|
+
original: detail.billing.enabledSources,
|
|
79
|
+
productionPreserved: updated.billing.production === detail.billing.production,
|
|
80
|
+
updated: updated.billing.enabledSources,
|
|
81
|
+
}).toMatchInlineSnapshot(`
|
|
82
|
+
{
|
|
83
|
+
"original": [],
|
|
84
|
+
"productionPreserved": true,
|
|
85
|
+
"updated": [
|
|
86
|
+
"stripe",
|
|
87
|
+
"tempo",
|
|
88
|
+
],
|
|
89
|
+
}
|
|
90
|
+
`)
|
|
91
|
+
})
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
describe('BillingSourcesCard', () => {
|
|
95
|
+
it('renders source state and the setup-only warning', () => {
|
|
96
|
+
const markup = renderToStaticMarkup(
|
|
97
|
+
createElement(BillingSourcesCard, {
|
|
98
|
+
enabledSources: ['stripe'],
|
|
99
|
+
error: null,
|
|
100
|
+
onChange() {},
|
|
101
|
+
pending: false,
|
|
102
|
+
}),
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
function isChecked(label: string) {
|
|
106
|
+
const marker = `aria-label="${label} billing source"`
|
|
107
|
+
const markerIndex = markup.indexOf(marker)
|
|
108
|
+
if (markerIndex === -1) return undefined
|
|
109
|
+
const tag = markup.slice(
|
|
110
|
+
markup.lastIndexOf('<', markerIndex),
|
|
111
|
+
markup.indexOf('>', markerIndex),
|
|
112
|
+
)
|
|
113
|
+
return tag.includes('aria-checked="true"')
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
expect({
|
|
117
|
+
stripe: isChecked('Stripe'),
|
|
118
|
+
tempo: isChecked('Tempo'),
|
|
119
|
+
warning: markup.includes(
|
|
120
|
+
'Disabling a source only blocks new setup. Existing billing connections continue to work.',
|
|
121
|
+
),
|
|
122
|
+
}).toMatchInlineSnapshot(`
|
|
123
|
+
{
|
|
124
|
+
"stripe": true,
|
|
125
|
+
"tempo": false,
|
|
126
|
+
"warning": true,
|
|
127
|
+
}
|
|
128
|
+
`)
|
|
129
|
+
})
|
|
130
|
+
})
|
|
131
|
+
|
|
52
132
|
describe('organization routes', () => {
|
|
53
133
|
it('matches a direct detail URL without the list route as a parent', () => {
|
|
54
134
|
const router = createRouter({
|
|
@@ -6,6 +6,10 @@ import { client } from './client'
|
|
|
6
6
|
|
|
7
7
|
type ListResponse = InferResponseType<(typeof client)['organizations']['$get'], 200>
|
|
8
8
|
type DetailResponse = InferResponseType<(typeof client)['organizations'][':orgId']['$get'], 200>
|
|
9
|
+
type BillingSourcesResponse = InferResponseType<
|
|
10
|
+
(typeof client)['organizations'][':orgId']['billing-sources'][':source']['$put'],
|
|
11
|
+
200
|
|
12
|
+
>
|
|
9
13
|
type MembersResponse = InferResponseType<
|
|
10
14
|
(typeof client)['organizations'][':orgId']['members']['$get'],
|
|
11
15
|
200
|
|
@@ -37,6 +41,9 @@ export type Organization = ListResponse['data'][number]
|
|
|
37
41
|
/** Detailed organization information, including billing state. */
|
|
38
42
|
export type Detail = DetailResponse
|
|
39
43
|
|
|
44
|
+
/** An organization-wide billing source. */
|
|
45
|
+
export type BillingSource = Detail['billing']['enabledSources'][number]
|
|
46
|
+
|
|
40
47
|
/** An organization member with resolved identity. */
|
|
41
48
|
export type Member = MembersResponse['data'][number]
|
|
42
49
|
|
|
@@ -138,6 +145,91 @@ export namespace get {
|
|
|
138
145
|
}
|
|
139
146
|
}
|
|
140
147
|
|
|
148
|
+
/** Returns organization detail with its enabled billing sources replaced. */
|
|
149
|
+
export function withEnabledSources(options: withEnabledSources.Options): Detail {
|
|
150
|
+
return {
|
|
151
|
+
...options.detail,
|
|
152
|
+
billing: {
|
|
153
|
+
...options.detail.billing,
|
|
154
|
+
enabledSources: options.enabledSources,
|
|
155
|
+
},
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export declare namespace withEnabledSources {
|
|
160
|
+
/** Inputs for replacing enabled billing sources in cached organization detail. */
|
|
161
|
+
type Options = {
|
|
162
|
+
/** Organization detail to update. */
|
|
163
|
+
detail: Detail
|
|
164
|
+
/** Current enabled billing sources. */
|
|
165
|
+
enabledSources: Detail['billing']['enabledSources']
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** Enable an organization-wide billing source. */
|
|
170
|
+
export namespace enableBillingSource {
|
|
171
|
+
/** Billing source to enable for an organization. */
|
|
172
|
+
export type Input = {
|
|
173
|
+
/** Organization id. */
|
|
174
|
+
orgId: string
|
|
175
|
+
/** Billing source to enable. */
|
|
176
|
+
source: BillingSource
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** Enables a billing source and returns the current enabled sources. */
|
|
180
|
+
export async function fn(options: Input) {
|
|
181
|
+
const response = await client.organizations[':orgId']['billing-sources'][':source'].$put({
|
|
182
|
+
param: options,
|
|
183
|
+
})
|
|
184
|
+
if (!response.ok) throw new Error('Failed to enable billing source.')
|
|
185
|
+
return (await response.json()) as BillingSourcesResponse
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** Creates a mutation that enables a billing source. */
|
|
189
|
+
export function useMutation(orgId: string) {
|
|
190
|
+
const queryClient = ReactQuery.useQueryClient()
|
|
191
|
+
return ReactQuery.useMutation({
|
|
192
|
+
mutationFn: (source: BillingSource) => fn({ orgId, source }),
|
|
193
|
+
onSuccess: (response) =>
|
|
194
|
+
queryClient.setQueryData(get.key(orgId), (detail: Detail | undefined) =>
|
|
195
|
+
detail ? withEnabledSources({ detail, enabledSources: response.data }) : detail,
|
|
196
|
+
),
|
|
197
|
+
})
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/** Disable an organization-wide billing source. */
|
|
202
|
+
export namespace disableBillingSource {
|
|
203
|
+
/** Billing source to disable for an organization. */
|
|
204
|
+
export type Input = {
|
|
205
|
+
/** Organization id. */
|
|
206
|
+
orgId: string
|
|
207
|
+
/** Billing source to disable. */
|
|
208
|
+
source: BillingSource
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/** Disables a billing source and returns the current enabled sources. */
|
|
212
|
+
export async function fn(options: Input) {
|
|
213
|
+
const response = await client.organizations[':orgId']['billing-sources'][':source'].$delete({
|
|
214
|
+
param: options,
|
|
215
|
+
})
|
|
216
|
+
if (!response.ok) throw new Error('Failed to disable billing source.')
|
|
217
|
+
return (await response.json()) as BillingSourcesResponse
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/** Creates a mutation that disables a billing source. */
|
|
221
|
+
export function useMutation(orgId: string) {
|
|
222
|
+
const queryClient = ReactQuery.useQueryClient()
|
|
223
|
+
return ReactQuery.useMutation({
|
|
224
|
+
mutationFn: (source: BillingSource) => fn({ orgId, source }),
|
|
225
|
+
onSuccess: (response) =>
|
|
226
|
+
queryClient.setQueryData(get.key(orgId), (detail: Detail | undefined) =>
|
|
227
|
+
detail ? withEnabledSources({ detail, enabledSources: response.data }) : detail,
|
|
228
|
+
),
|
|
229
|
+
})
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
141
233
|
/** List members for one organization. */
|
|
142
234
|
export namespace members {
|
|
143
235
|
export function key(orgId: string) {
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import * as ReactQuery from '@tanstack/react-query'
|
|
2
2
|
import type { InferRequestType, InferResponseType } from 'hono/client'
|
|
3
|
+
import * as z from 'zod/mini'
|
|
3
4
|
|
|
4
5
|
import { client } from './client'
|
|
5
6
|
|
|
7
|
+
/** Route search: the active page tab. */
|
|
8
|
+
export const search = z.object({
|
|
9
|
+
tab: z.catch(z.optional(z.enum(['pending', 'verified'])), undefined),
|
|
10
|
+
})
|
|
11
|
+
|
|
6
12
|
/** The verified-token list endpoint's success body. */
|
|
7
13
|
type ListResponse = InferResponseType<(typeof client)['verified-tokens']['$get'], 200>
|
|
8
14
|
|
|
@@ -67,6 +73,79 @@ export namespace create {
|
|
|
67
73
|
}
|
|
68
74
|
}
|
|
69
75
|
|
|
76
|
+
/** Review queue over organization verification requests. */
|
|
77
|
+
export namespace requests {
|
|
78
|
+
/** A verification request record from the queue endpoint. */
|
|
79
|
+
export type VerifiedTokenRequest = InferResponseType<
|
|
80
|
+
(typeof client)['verified-tokens']['requests']['$get'],
|
|
81
|
+
200
|
|
82
|
+
>['data'][number]
|
|
83
|
+
|
|
84
|
+
export namespace list {
|
|
85
|
+
export function key() {
|
|
86
|
+
return ['verified-token-requests'] as const
|
|
87
|
+
}
|
|
88
|
+
export async function fn() {
|
|
89
|
+
const response = await client['verified-tokens'].requests.$get({ query: {} })
|
|
90
|
+
if (!response.ok) throw new Error('Failed to load requests.')
|
|
91
|
+
return response.json()
|
|
92
|
+
}
|
|
93
|
+
export function options() {
|
|
94
|
+
return ReactQuery.queryOptions({ queryFn: fn, queryKey: key(), staleTime: 30_000 })
|
|
95
|
+
}
|
|
96
|
+
export function useQuery() {
|
|
97
|
+
return ReactQuery.useQuery(options())
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export namespace approve {
|
|
102
|
+
export type Input = { chainId: number; requestId: string }
|
|
103
|
+
export async function fn({ requestId }: Input) {
|
|
104
|
+
const response = await client['verified-tokens'].requests[':requestId'].approve.$post({
|
|
105
|
+
param: { requestId },
|
|
106
|
+
})
|
|
107
|
+
if (!response.ok) throw new Error(await errorMessage(response, 'Failed to approve request.'))
|
|
108
|
+
return response.json()
|
|
109
|
+
}
|
|
110
|
+
export function useMutation() {
|
|
111
|
+
const queryClient = ReactQuery.useQueryClient()
|
|
112
|
+
return ReactQuery.useMutation({
|
|
113
|
+
mutationFn: fn,
|
|
114
|
+
onSuccess: (_data, input) => {
|
|
115
|
+
void queryClient.invalidateQueries({ queryKey: list.key() })
|
|
116
|
+
// Approval publishes the token onto the chain's verified list.
|
|
117
|
+
void queryClient.invalidateQueries({ queryKey: get.key({ chainId: input.chainId }) })
|
|
118
|
+
},
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export namespace deny {
|
|
124
|
+
export type Input = { requestId: string; reviewNote?: string }
|
|
125
|
+
export async function fn({ requestId, reviewNote }: Input) {
|
|
126
|
+
const response = await client['verified-tokens'].requests[':requestId'].deny.$post({
|
|
127
|
+
json: reviewNote ? { reviewNote } : {},
|
|
128
|
+
param: { requestId },
|
|
129
|
+
})
|
|
130
|
+
if (!response.ok) throw new Error(await errorMessage(response, 'Failed to deny request.'))
|
|
131
|
+
return response.json()
|
|
132
|
+
}
|
|
133
|
+
export function useMutation() {
|
|
134
|
+
const queryClient = ReactQuery.useQueryClient()
|
|
135
|
+
return ReactQuery.useMutation({
|
|
136
|
+
mutationFn: fn,
|
|
137
|
+
onSuccess: () => queryClient.invalidateQueries({ queryKey: list.key() }),
|
|
138
|
+
})
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Server error-envelope message, with a fallback for non-JSON bodies. */
|
|
144
|
+
async function errorMessage(response: { json(): Promise<unknown> }, fallback: string) {
|
|
145
|
+
const body = (await response.json().catch(() => null)) as { error?: { message?: string } } | null
|
|
146
|
+
return body?.error?.message ?? fallback
|
|
147
|
+
}
|
|
148
|
+
|
|
70
149
|
/** Remove a verified token from a chain. */
|
|
71
150
|
export namespace remove {
|
|
72
151
|
export type Input = { address: `0x${string}`; chainId: number }
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
import { Route as rootRouteImport } from './routes/__root'
|
|
12
12
|
import { Route as VerifiedTokensRouteImport } from './routes/verified-tokens'
|
|
13
13
|
import { Route as OrganizationsRouteImport } from './routes/organizations'
|
|
14
|
+
import { Route as EarlyAccessRouteImport } from './routes/early-access'
|
|
14
15
|
import { Route as ApiKeysRouteImport } from './routes/api-keys'
|
|
15
16
|
import { Route as IndexRouteImport } from './routes/index'
|
|
16
17
|
import { Route as OrganizationsOrgIdRouteImport } from './routes/organizations_.$orgId'
|
|
@@ -25,6 +26,11 @@ const OrganizationsRoute = OrganizationsRouteImport.update({
|
|
|
25
26
|
path: '/organizations',
|
|
26
27
|
getParentRoute: () => rootRouteImport,
|
|
27
28
|
} as any)
|
|
29
|
+
const EarlyAccessRoute = EarlyAccessRouteImport.update({
|
|
30
|
+
id: '/early-access',
|
|
31
|
+
path: '/early-access',
|
|
32
|
+
getParentRoute: () => rootRouteImport,
|
|
33
|
+
} as any)
|
|
28
34
|
const ApiKeysRoute = ApiKeysRouteImport.update({
|
|
29
35
|
id: '/api-keys',
|
|
30
36
|
path: '/api-keys',
|
|
@@ -44,6 +50,7 @@ const OrganizationsOrgIdRoute = OrganizationsOrgIdRouteImport.update({
|
|
|
44
50
|
export interface FileRoutesByFullPath {
|
|
45
51
|
'/': typeof IndexRoute
|
|
46
52
|
'/api-keys': typeof ApiKeysRoute
|
|
53
|
+
'/early-access': typeof EarlyAccessRoute
|
|
47
54
|
'/organizations': typeof OrganizationsRoute
|
|
48
55
|
'/verified-tokens': typeof VerifiedTokensRoute
|
|
49
56
|
'/organizations/$orgId': typeof OrganizationsOrgIdRoute
|
|
@@ -51,6 +58,7 @@ export interface FileRoutesByFullPath {
|
|
|
51
58
|
export interface FileRoutesByTo {
|
|
52
59
|
'/': typeof IndexRoute
|
|
53
60
|
'/api-keys': typeof ApiKeysRoute
|
|
61
|
+
'/early-access': typeof EarlyAccessRoute
|
|
54
62
|
'/organizations': typeof OrganizationsRoute
|
|
55
63
|
'/verified-tokens': typeof VerifiedTokensRoute
|
|
56
64
|
'/organizations/$orgId': typeof OrganizationsOrgIdRoute
|
|
@@ -59,19 +67,33 @@ export interface FileRoutesById {
|
|
|
59
67
|
__root__: typeof rootRouteImport
|
|
60
68
|
'/': typeof IndexRoute
|
|
61
69
|
'/api-keys': typeof ApiKeysRoute
|
|
70
|
+
'/early-access': typeof EarlyAccessRoute
|
|
62
71
|
'/organizations': typeof OrganizationsRoute
|
|
63
72
|
'/verified-tokens': typeof VerifiedTokensRoute
|
|
64
73
|
'/organizations_/$orgId': typeof OrganizationsOrgIdRoute
|
|
65
74
|
}
|
|
66
75
|
export interface FileRouteTypes {
|
|
67
76
|
fileRoutesByFullPath: FileRoutesByFullPath
|
|
68
|
-
fullPaths:
|
|
77
|
+
fullPaths:
|
|
78
|
+
| '/'
|
|
79
|
+
| '/api-keys'
|
|
80
|
+
| '/early-access'
|
|
81
|
+
| '/organizations'
|
|
82
|
+
| '/verified-tokens'
|
|
83
|
+
| '/organizations/$orgId'
|
|
69
84
|
fileRoutesByTo: FileRoutesByTo
|
|
70
|
-
to:
|
|
85
|
+
to:
|
|
86
|
+
| '/'
|
|
87
|
+
| '/api-keys'
|
|
88
|
+
| '/early-access'
|
|
89
|
+
| '/organizations'
|
|
90
|
+
| '/verified-tokens'
|
|
91
|
+
| '/organizations/$orgId'
|
|
71
92
|
id:
|
|
72
93
|
| '__root__'
|
|
73
94
|
| '/'
|
|
74
95
|
| '/api-keys'
|
|
96
|
+
| '/early-access'
|
|
75
97
|
| '/organizations'
|
|
76
98
|
| '/verified-tokens'
|
|
77
99
|
| '/organizations_/$orgId'
|
|
@@ -80,6 +102,7 @@ export interface FileRouteTypes {
|
|
|
80
102
|
export interface RootRouteChildren {
|
|
81
103
|
IndexRoute: typeof IndexRoute
|
|
82
104
|
ApiKeysRoute: typeof ApiKeysRoute
|
|
105
|
+
EarlyAccessRoute: typeof EarlyAccessRoute
|
|
83
106
|
OrganizationsRoute: typeof OrganizationsRoute
|
|
84
107
|
VerifiedTokensRoute: typeof VerifiedTokensRoute
|
|
85
108
|
OrganizationsOrgIdRoute: typeof OrganizationsOrgIdRoute
|
|
@@ -101,6 +124,13 @@ declare module '@tanstack/react-router' {
|
|
|
101
124
|
preLoaderRoute: typeof OrganizationsRouteImport
|
|
102
125
|
parentRoute: typeof rootRouteImport
|
|
103
126
|
}
|
|
127
|
+
'/early-access': {
|
|
128
|
+
id: '/early-access'
|
|
129
|
+
path: '/early-access'
|
|
130
|
+
fullPath: '/early-access'
|
|
131
|
+
preLoaderRoute: typeof EarlyAccessRouteImport
|
|
132
|
+
parentRoute: typeof rootRouteImport
|
|
133
|
+
}
|
|
104
134
|
'/api-keys': {
|
|
105
135
|
id: '/api-keys'
|
|
106
136
|
path: '/api-keys'
|
|
@@ -128,6 +158,7 @@ declare module '@tanstack/react-router' {
|
|
|
128
158
|
const rootRouteChildren: RootRouteChildren = {
|
|
129
159
|
IndexRoute: IndexRoute,
|
|
130
160
|
ApiKeysRoute: ApiKeysRoute,
|
|
161
|
+
EarlyAccessRoute: EarlyAccessRoute,
|
|
131
162
|
OrganizationsRoute: OrganizationsRoute,
|
|
132
163
|
VerifiedTokensRoute: VerifiedTokensRoute,
|
|
133
164
|
OrganizationsOrgIdRoute: OrganizationsOrgIdRoute,
|
|
@@ -10,7 +10,7 @@ import type { QueryClient } from '@tanstack/react-query'
|
|
|
10
10
|
import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client'
|
|
11
11
|
import { type ComponentType } from 'react'
|
|
12
12
|
import { Button, Identicon } from 'regen-ui'
|
|
13
|
-
import { BadgeCheck, Building2, KeyRound, LogOut } from 'lucide-react'
|
|
13
|
+
import { BadgeCheck, Building2, KeyRound, LogOut, Ticket } from 'lucide-react'
|
|
14
14
|
import { WagmiProvider } from 'wagmi'
|
|
15
15
|
|
|
16
16
|
import appCss from '../styles.css?url'
|
|
@@ -115,6 +115,7 @@ const nav = [
|
|
|
115
115
|
{ exact: false, href: '/organizations', icon: Building2, label: 'Organizations' },
|
|
116
116
|
{ exact: false, href: '/api-keys', icon: KeyRound, label: 'API Keys' },
|
|
117
117
|
{ exact: false, href: '/verified-tokens', icon: BadgeCheck, label: 'Verified Tokens' },
|
|
118
|
+
{ exact: false, href: '/early-access', icon: Ticket, label: 'Early Access' },
|
|
118
119
|
] as const
|
|
119
120
|
|
|
120
121
|
function Sidebar(props: { email: string; logoutUrl?: string }) {
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { createFileRoute } from '@tanstack/react-router'
|
|
2
|
+
import { useState } from 'react'
|
|
3
|
+
import { Button, Fields, Input } from 'regen-ui'
|
|
4
|
+
import { Plus, Trash2 } from 'lucide-react'
|
|
5
|
+
|
|
6
|
+
import { Card, Notice, Page } from '../components/Page'
|
|
7
|
+
import { Dialog } from '../components/Dialog'
|
|
8
|
+
import * as EarlyAccess from '../lib/early-access'
|
|
9
|
+
|
|
10
|
+
export const Route = createFileRoute('/early-access')({
|
|
11
|
+
loader: ({ context }) => context.queryClient.prefetchQuery(EarlyAccess.list.options()),
|
|
12
|
+
component: RouteComponent,
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
function RouteComponent() {
|
|
16
|
+
const [open, setOpen] = useState(false)
|
|
17
|
+
const entries = EarlyAccess.list.useQuery()
|
|
18
|
+
const add = EarlyAccess.add.useMutation()
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<Dialog.Root onOpenChange={setOpen} open={open}>
|
|
22
|
+
<Page
|
|
23
|
+
title="Early Access"
|
|
24
|
+
description="Accounts whose new organizations receive early access features by default."
|
|
25
|
+
action={
|
|
26
|
+
<Dialog.Trigger
|
|
27
|
+
render={
|
|
28
|
+
<Button variant="primary">
|
|
29
|
+
<Plus className="size-4" />
|
|
30
|
+
Add entry
|
|
31
|
+
</Button>
|
|
32
|
+
}
|
|
33
|
+
/>
|
|
34
|
+
}
|
|
35
|
+
>
|
|
36
|
+
<EntryList
|
|
37
|
+
entries={entries.data?.data ?? []}
|
|
38
|
+
error={entries.error}
|
|
39
|
+
loading={entries.isPending}
|
|
40
|
+
/>
|
|
41
|
+
<Dialog.Popup>
|
|
42
|
+
<AddEntryForm
|
|
43
|
+
onSubmit={(input) =>
|
|
44
|
+
add.mutate(input, {
|
|
45
|
+
onError: (error) => alert(error.message),
|
|
46
|
+
onSuccess: () => setOpen(false),
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
pending={add.isPending}
|
|
50
|
+
/>
|
|
51
|
+
</Dialog.Popup>
|
|
52
|
+
</Page>
|
|
53
|
+
</Dialog.Root>
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function AddEntryForm(props: {
|
|
58
|
+
onSubmit: (input: EarlyAccess.add.Input) => void
|
|
59
|
+
pending: boolean
|
|
60
|
+
}) {
|
|
61
|
+
function onSubmit(event: React.FormEvent<HTMLFormElement>) {
|
|
62
|
+
event.preventDefault()
|
|
63
|
+
const entry = new FormData(event.currentTarget).get('entry')
|
|
64
|
+
if (typeof entry !== 'string' || !entry.trim()) return
|
|
65
|
+
props.onSubmit({ entry: entry.trim() })
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<>
|
|
70
|
+
<Dialog.Title>Add Entry</Dialog.Title>
|
|
71
|
+
<Dialog.Description>
|
|
72
|
+
Grant early access features to new organizations created by a whole domain or a single email
|
|
73
|
+
</Dialog.Description>
|
|
74
|
+
<form className="mt-6 flex flex-col gap-8" onSubmit={onSubmit}>
|
|
75
|
+
<Fields className="gap-6" variant="default">
|
|
76
|
+
<Input
|
|
77
|
+
autoComplete="off"
|
|
78
|
+
label="Domain or email"
|
|
79
|
+
name="entry"
|
|
80
|
+
placeholder="example.org or user@example.org"
|
|
81
|
+
required
|
|
82
|
+
spellCheck={false}
|
|
83
|
+
/>
|
|
84
|
+
</Fields>
|
|
85
|
+
<div className="flex justify-end gap-2">
|
|
86
|
+
<Dialog.Close render={<Button type="button">Cancel</Button>} />
|
|
87
|
+
<Button loading={props.pending} type="submit" variant="primary">
|
|
88
|
+
Add entry
|
|
89
|
+
</Button>
|
|
90
|
+
</div>
|
|
91
|
+
</form>
|
|
92
|
+
</>
|
|
93
|
+
)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function EntryList(props: {
|
|
97
|
+
entries: readonly EarlyAccess.Entry[]
|
|
98
|
+
error: Error | null
|
|
99
|
+
loading: boolean
|
|
100
|
+
}) {
|
|
101
|
+
return (
|
|
102
|
+
<Card>
|
|
103
|
+
{props.loading ? (
|
|
104
|
+
<Notice>Loading entries…</Notice>
|
|
105
|
+
) : props.error ? (
|
|
106
|
+
<Notice>{props.error.message}</Notice>
|
|
107
|
+
) : props.entries.length === 0 ? (
|
|
108
|
+
<Notice>No entries yet.</Notice>
|
|
109
|
+
) : (
|
|
110
|
+
<ul className="divide-y divide-border">
|
|
111
|
+
{props.entries.map((entry) => (
|
|
112
|
+
<EntryRow entry={entry} key={entry.entry} />
|
|
113
|
+
))}
|
|
114
|
+
</ul>
|
|
115
|
+
)}
|
|
116
|
+
</Card>
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function EntryRow(props: { entry: EarlyAccess.Entry }) {
|
|
121
|
+
const { entry } = props
|
|
122
|
+
const remove = EarlyAccess.remove.useMutation()
|
|
123
|
+
|
|
124
|
+
function onRemove() {
|
|
125
|
+
if (
|
|
126
|
+
!confirm(
|
|
127
|
+
`Remove ${entry.entry}? Future organizations created by matching users start without early access features. Existing organizations keep their enabled billing sources.`,
|
|
128
|
+
)
|
|
129
|
+
)
|
|
130
|
+
return
|
|
131
|
+
remove.mutate({ entry: entry.entry }, { onError: (error) => alert(error.message) })
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return (
|
|
135
|
+
<li className="flex items-center justify-between gap-4 px-5 py-2.5">
|
|
136
|
+
<div className="flex min-w-0 flex-col gap-1">
|
|
137
|
+
<span className="truncate text-label-14 text-foreground">{entry.entry}</span>
|
|
138
|
+
<span className="text-label-12 text-foreground-secondary">
|
|
139
|
+
Added by {entry.createdBy} · {formatDate(entry.createdAt)}
|
|
140
|
+
</span>
|
|
141
|
+
</div>
|
|
142
|
+
<Button.Icon
|
|
143
|
+
className="text-foreground-secondary hover:text-negative"
|
|
144
|
+
disabled={remove.isPending}
|
|
145
|
+
onClick={onRemove}
|
|
146
|
+
size="lg"
|
|
147
|
+
title="Remove entry"
|
|
148
|
+
variant="plain"
|
|
149
|
+
>
|
|
150
|
+
<Trash2 />
|
|
151
|
+
</Button.Icon>
|
|
152
|
+
</li>
|
|
153
|
+
)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function formatDate(iso: string) {
|
|
157
|
+
return new Date(iso).toLocaleDateString(undefined, {
|
|
158
|
+
day: 'numeric',
|
|
159
|
+
month: 'short',
|
|
160
|
+
year: 'numeric',
|
|
161
|
+
})
|
|
162
|
+
}
|