tapimo 0.0.2 → 0.2.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 +38 -16
- package/dist/ApiKey.d.ts +14 -11
- package/dist/ApiKey.d.ts.map +1 -1
- package/dist/ApiKey.js +11 -12
- package/dist/ApiKey.js.map +1 -1
- package/dist/ApiKeys.d.ts +207 -0
- package/dist/ApiKeys.d.ts.map +1 -0
- package/dist/ApiKeys.js +282 -0
- package/dist/ApiKeys.js.map +1 -0
- package/dist/App.d.ts +6771 -3819
- package/dist/App.d.ts.map +1 -1
- package/dist/App.js +145 -317
- package/dist/App.js.map +1 -1
- package/dist/Handler.d.ts +86 -0
- package/dist/Handler.d.ts.map +1 -0
- package/dist/Handler.js +83 -0
- package/dist/Handler.js.map +1 -0
- package/dist/Scope.d.ts +14 -4
- package/dist/Scope.d.ts.map +1 -1
- package/dist/Scope.js +16 -4
- package/dist/Scope.js.map +1 -1
- package/dist/Webhooks.d.ts +5 -7
- package/dist/Webhooks.d.ts.map +1 -1
- package/dist/Webhooks.js +11 -4
- package/dist/Webhooks.js.map +1 -1
- package/dist/admin/Access.d.ts +1 -3
- package/dist/admin/Access.d.ts.map +1 -1
- package/dist/admin/Access.js +1 -3
- package/dist/admin/Access.js.map +1 -1
- package/dist/admin/App.d.ts +167 -205
- package/dist/admin/App.d.ts.map +1 -1
- package/dist/admin/App.js +2 -4
- package/dist/admin/App.js.map +1 -1
- package/dist/admin/apps/api-keys.d.ts +105 -5
- package/dist/admin/apps/api-keys.d.ts.map +1 -1
- package/dist/admin/apps/api-keys.js +75 -9
- package/dist/admin/apps/api-keys.js.map +1 -1
- package/dist/admin/apps/scopes.d.ts +6 -0
- package/dist/admin/apps/scopes.d.ts.map +1 -1
- package/dist/admin/apps/verified-tokens.d.ts +35 -149
- package/dist/admin/apps/verified-tokens.d.ts.map +1 -1
- package/dist/admin/apps/verified-tokens.js +18 -39
- package/dist/admin/apps/verified-tokens.js.map +1 -1
- package/dist/admin/ui.generated.d.ts +1 -1
- package/dist/admin/ui.generated.js +1 -1
- package/dist/apps/data/App.d.ts +19176 -0
- package/dist/apps/data/App.d.ts.map +1 -0
- package/dist/apps/data/App.js +236 -0
- package/dist/apps/data/App.js.map +1 -0
- package/dist/apps/data/index.d.ts +3 -0
- package/dist/apps/data/index.d.ts.map +1 -0
- package/dist/apps/data/index.js +3 -0
- package/dist/apps/data/index.js.map +1 -0
- package/dist/apps/{activities.d.ts → data/routes/activities.d.ts} +32 -32
- package/dist/apps/{activities.d.ts.map → data/routes/activities.d.ts.map} +1 -1
- package/dist/apps/{activities.js → data/routes/activities.js} +13 -13
- package/dist/apps/data/routes/activities.js.map +1 -0
- package/dist/apps/{addresses.d.ts → data/routes/addresses.d.ts} +22 -22
- package/dist/apps/{addresses.d.ts.map → data/routes/addresses.d.ts.map} +1 -1
- package/dist/apps/{addresses.js → data/routes/addresses.js} +5 -5
- package/dist/apps/data/routes/addresses.js.map +1 -0
- package/dist/apps/{balances.d.ts → data/routes/balances.d.ts} +49 -49
- package/dist/apps/data/routes/balances.d.ts.map +1 -0
- package/dist/apps/{balances.js → data/routes/balances.js} +42 -11
- package/dist/apps/data/routes/balances.js.map +1 -0
- package/dist/apps/{blocks.d.ts → data/routes/blocks.d.ts} +47 -47
- package/dist/apps/data/routes/blocks.d.ts.map +1 -0
- package/dist/apps/{blocks.js → data/routes/blocks.js} +13 -13
- package/dist/apps/data/routes/blocks.js.map +1 -0
- package/dist/apps/{coingecko.d.ts → data/routes/coingecko.d.ts} +183 -183
- package/dist/apps/data/routes/coingecko.d.ts.map +1 -0
- package/dist/apps/{coingecko.js → data/routes/coingecko.js} +13 -13
- package/dist/apps/data/routes/coingecko.js.map +1 -0
- package/dist/apps/{exchanges.d.ts → data/routes/exchanges.d.ts} +460 -460
- package/dist/apps/data/routes/exchanges.d.ts.map +1 -0
- package/dist/apps/{exchanges.js → data/routes/exchanges.js} +19 -19
- package/dist/apps/data/routes/exchanges.js.map +1 -0
- package/dist/apps/{fee-amm.d.ts → data/routes/fee-amm.d.ts} +94 -94
- package/dist/apps/data/routes/fee-amm.d.ts.map +1 -0
- package/dist/apps/{fee-amm.js → data/routes/fee-amm.js} +17 -17
- package/dist/apps/data/routes/fee-amm.js.map +1 -0
- package/dist/apps/{indexer.d.ts → data/routes/indexer.d.ts} +4 -4
- package/dist/apps/data/routes/indexer.d.ts.map +1 -0
- package/dist/apps/{indexer.js → data/routes/indexer.js} +13 -9
- package/dist/apps/data/routes/indexer.js.map +1 -0
- package/dist/apps/{receipts.d.ts → data/routes/receipts.d.ts} +6 -6
- package/dist/apps/data/routes/receipts.d.ts.map +1 -0
- package/dist/apps/{receipts.js → data/routes/receipts.js} +12 -12
- package/dist/apps/data/routes/receipts.js.map +1 -0
- package/dist/apps/{rpc.d.ts → data/routes/rpc.d.ts} +4 -4
- package/dist/apps/data/routes/rpc.d.ts.map +1 -0
- package/dist/apps/{rpc.js → data/routes/rpc.js} +6 -6
- package/dist/apps/data/routes/rpc.js.map +1 -0
- package/dist/apps/{tokenlist.d.ts → data/routes/tokenlist.d.ts} +4 -4
- package/dist/apps/data/routes/tokenlist.d.ts.map +1 -0
- package/dist/apps/{tokenlist.js → data/routes/tokenlist.js} +8 -8
- package/dist/apps/data/routes/tokenlist.js.map +1 -0
- package/dist/apps/{tokens.d.ts → data/routes/tokens.d.ts} +11 -11
- package/dist/apps/data/routes/tokens.d.ts.map +1 -0
- package/dist/apps/{tokens.js → data/routes/tokens.js} +19 -19
- package/dist/apps/data/routes/tokens.js.map +1 -0
- package/dist/apps/{transactions.d.ts → data/routes/transactions.d.ts} +288 -288
- package/dist/apps/data/routes/transactions.d.ts.map +1 -0
- package/dist/apps/{transactions.js → data/routes/transactions.js} +13 -13
- package/dist/apps/data/routes/transactions.js.map +1 -0
- package/dist/apps/{transfers.d.ts → data/routes/transfers.d.ts} +35 -35
- package/dist/apps/data/routes/transfers.d.ts.map +1 -0
- package/dist/apps/{transfers.js → data/routes/transfers.js} +13 -13
- package/dist/apps/data/routes/transfers.js.map +1 -0
- package/dist/apps/{verified-tokens.d.ts → data/routes/verified-tokens.d.ts} +42 -42
- package/dist/apps/data/routes/verified-tokens.d.ts.map +1 -0
- package/dist/apps/{verified-tokens.js → data/routes/verified-tokens.js} +9 -9
- package/dist/apps/data/routes/verified-tokens.js.map +1 -0
- package/dist/apps/{webhooks.d.ts → data/routes/webhooks.d.ts} +66 -63
- package/dist/apps/{webhooks.d.ts.map → data/routes/webhooks.d.ts.map} +1 -1
- package/dist/apps/{webhooks.js → data/routes/webhooks.js} +67 -59
- package/dist/apps/data/routes/webhooks.js.map +1 -0
- package/dist/apps/docs/App.d.ts +27 -0
- package/dist/apps/docs/App.d.ts.map +1 -0
- package/dist/apps/docs/App.js +122 -0
- package/dist/apps/docs/App.js.map +1 -0
- package/dist/{internal/OpenApi.fonts.d.ts → apps/docs/fonts.d.ts} +1 -1
- package/dist/apps/docs/fonts.d.ts.map +1 -0
- package/dist/{internal/OpenApi.fonts.js → apps/docs/fonts.js} +1 -1
- package/dist/apps/docs/fonts.js.map +1 -0
- package/dist/apps/docs/index.d.ts +2 -0
- package/dist/apps/docs/index.d.ts.map +1 -0
- package/dist/apps/docs/index.js +2 -0
- package/dist/apps/docs/index.js.map +1 -0
- package/dist/{internal/OpenApi.logo.d.ts → apps/docs/logo.d.ts} +1 -1
- package/dist/apps/docs/logo.d.ts.map +1 -0
- package/dist/{internal/OpenApi.logo.js → apps/docs/logo.js} +1 -1
- package/dist/apps/docs/logo.js.map +1 -0
- package/dist/apps/index.d.ts +4 -0
- package/dist/apps/index.d.ts.map +1 -0
- package/dist/apps/index.js +4 -0
- package/dist/apps/index.js.map +1 -0
- package/dist/apps/metadata.d.ts +7 -0
- package/dist/apps/metadata.d.ts.map +1 -0
- package/dist/apps/metadata.js +15 -0
- package/dist/apps/metadata.js.map +1 -0
- package/dist/apps/platform/App.d.ts +2985 -0
- package/dist/apps/platform/App.d.ts.map +1 -0
- package/dist/apps/platform/App.js +74 -0
- package/dist/apps/platform/App.js.map +1 -0
- package/dist/apps/platform/index.d.ts +2 -0
- package/dist/apps/platform/index.d.ts.map +1 -0
- package/dist/apps/platform/index.js +2 -0
- package/dist/apps/platform/index.js.map +1 -0
- package/dist/apps/platform/routes/api-keys.d.ts +681 -0
- package/dist/apps/platform/routes/api-keys.d.ts.map +1 -0
- package/dist/apps/platform/routes/api-keys.js +328 -0
- package/dist/apps/platform/routes/api-keys.js.map +1 -0
- package/dist/apps/platform/routes/invitations.d.ts +733 -0
- package/dist/apps/platform/routes/invitations.d.ts.map +1 -0
- package/dist/apps/platform/routes/invitations.js +413 -0
- package/dist/apps/platform/routes/invitations.js.map +1 -0
- package/dist/apps/platform/routes/me.d.ts +95 -0
- package/dist/apps/platform/routes/me.d.ts.map +1 -0
- package/dist/apps/platform/routes/me.js +81 -0
- package/dist/apps/platform/routes/me.js.map +1 -0
- package/dist/apps/platform/routes/members.d.ts +554 -0
- package/dist/apps/platform/routes/members.d.ts.map +1 -0
- package/dist/apps/platform/routes/members.js +251 -0
- package/dist/apps/platform/routes/members.js.map +1 -0
- package/dist/apps/platform/routes/orgs.d.ts +582 -0
- package/dist/apps/platform/routes/orgs.d.ts.map +1 -0
- package/dist/apps/platform/routes/orgs.js +265 -0
- package/dist/apps/platform/routes/orgs.js.map +1 -0
- package/dist/apps/platform/routes/projects.d.ts +740 -0
- package/dist/apps/platform/routes/projects.d.ts.map +1 -0
- package/dist/apps/platform/routes/projects.js +286 -0
- package/dist/apps/platform/routes/projects.js.map +1 -0
- package/dist/apps/relay/App.d.ts +41 -0
- package/dist/apps/relay/App.d.ts.map +1 -0
- package/dist/apps/relay/App.js +103 -0
- package/dist/apps/relay/App.js.map +1 -0
- package/dist/apps/relay/Sponsorships.d.ts +107 -0
- package/dist/apps/relay/Sponsorships.d.ts.map +1 -0
- package/dist/apps/relay/Sponsorships.js +193 -0
- package/dist/apps/relay/Sponsorships.js.map +1 -0
- package/dist/apps/relay/index.d.ts +3 -0
- package/dist/apps/relay/index.d.ts.map +1 -0
- package/dist/apps/relay/index.js +3 -0
- package/dist/apps/relay/index.js.map +1 -0
- package/dist/{Cli.d.ts → cli/Cli.d.ts} +13 -2
- package/dist/cli/Cli.d.ts.map +1 -0
- package/dist/cli/Cli.js +19 -0
- package/dist/cli/Cli.js.map +1 -0
- package/dist/cli/bin.d.ts +3 -0
- package/dist/cli/bin.d.ts.map +1 -0
- package/dist/cli/bin.js +4 -0
- package/dist/cli/bin.js.map +1 -0
- package/dist/cli/commands/admin.d.ts +15 -0
- package/dist/cli/commands/admin.d.ts.map +1 -0
- package/dist/cli/commands/admin.js +60 -0
- package/dist/cli/commands/admin.js.map +1 -0
- package/dist/cloudflare.d.ts +7 -0
- package/dist/cloudflare.d.ts.map +1 -1
- package/dist/cloudflare.js +9 -0
- package/dist/cloudflare.js.map +1 -1
- package/dist/db/Db.d.ts +113 -0
- package/dist/db/Db.d.ts.map +1 -0
- package/dist/db/Db.js +402 -0
- package/dist/db/Db.js.map +1 -0
- package/dist/db/Schema.d.ts +168 -0
- package/dist/db/Schema.d.ts.map +1 -0
- package/dist/db/Schema.js +229 -0
- package/dist/db/Schema.js.map +1 -0
- package/dist/db/tables/invitations.d.ts +84 -0
- package/dist/db/tables/invitations.d.ts.map +1 -0
- package/dist/db/tables/invitations.js +165 -0
- package/dist/db/tables/invitations.js.map +1 -0
- package/dist/db/tables/memberships.d.ts +108 -0
- package/dist/db/tables/memberships.d.ts.map +1 -0
- package/dist/db/tables/memberships.js +153 -0
- package/dist/db/tables/memberships.js.map +1 -0
- package/dist/db/tables/organizations.d.ts +123 -0
- package/dist/db/tables/organizations.d.ts.map +1 -0
- package/dist/db/tables/organizations.js +170 -0
- package/dist/db/tables/organizations.js.map +1 -0
- package/dist/db/tables/projects.d.ts +65 -0
- package/dist/db/tables/projects.d.ts.map +1 -0
- package/dist/db/tables/projects.js +75 -0
- package/dist/db/tables/projects.js.map +1 -0
- package/dist/db/tables/sponsoredTransactions.d.ts +120 -0
- package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -0
- package/dist/db/tables/sponsoredTransactions.js +161 -0
- package/dist/db/tables/sponsoredTransactions.js.map +1 -0
- package/dist/db/tables/users.d.ts +48 -0
- package/dist/db/tables/users.d.ts.map +1 -0
- package/dist/db/tables/users.js +65 -0
- package/dist/db/tables/users.js.map +1 -0
- package/dist/db/tables/verifiedTokens.d.ts +53 -0
- package/dist/db/tables/verifiedTokens.d.ts.map +1 -0
- package/dist/db/tables/verifiedTokens.js +74 -0
- package/dist/db/tables/verifiedTokens.js.map +1 -0
- package/dist/db/tables/webhookDeliveries.d.ts +71 -0
- package/dist/db/tables/webhookDeliveries.d.ts.map +1 -0
- package/dist/db/tables/webhookDeliveries.js +119 -0
- package/dist/db/tables/webhookDeliveries.js.map +1 -0
- package/dist/db/tables/webhookSubscriptions.d.ts +158 -0
- package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -0
- package/dist/db/tables/webhookSubscriptions.js +272 -0
- package/dist/db/tables/webhookSubscriptions.js.map +1 -0
- package/dist/handlers/internal/executionError.d.ts +25 -0
- package/dist/handlers/internal/executionError.d.ts.map +1 -0
- package/dist/handlers/internal/executionError.js +186 -0
- package/dist/handlers/internal/executionError.js.map +1 -0
- package/dist/handlers/internal/multisig.d.ts +233 -0
- package/dist/handlers/internal/multisig.d.ts.map +1 -0
- package/dist/handlers/internal/multisig.js +542 -0
- package/dist/handlers/internal/multisig.js.map +1 -0
- package/dist/handlers/internal/requestListener.d.ts +124 -0
- package/dist/handlers/internal/requestListener.d.ts.map +1 -0
- package/dist/handlers/internal/requestListener.js +173 -0
- package/dist/handlers/internal/requestListener.js.map +1 -0
- package/dist/handlers/internal/sponsorship.d.ts +113 -0
- package/dist/handlers/internal/sponsorship.d.ts.map +1 -0
- package/dist/handlers/internal/sponsorship.js +128 -0
- package/dist/handlers/internal/sponsorship.js.map +1 -0
- package/dist/handlers/internal/types.d.ts +284 -0
- package/dist/handlers/internal/types.d.ts.map +1 -0
- package/dist/handlers/internal/types.js +2 -0
- package/dist/handlers/internal/types.js.map +1 -0
- package/dist/handlers/internal/utils.d.ts +34 -0
- package/dist/handlers/internal/utils.d.ts.map +1 -0
- package/dist/handlers/internal/utils.js +131 -0
- package/dist/handlers/internal/utils.js.map +1 -0
- package/dist/handlers/relay.d.ts +123 -0
- package/dist/handlers/relay.d.ts.map +1 -0
- package/dist/handlers/relay.js +1278 -0
- package/dist/handlers/relay.js.map +1 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/internal/Auth.d.ts +224 -16
- package/dist/internal/Auth.d.ts.map +1 -1
- package/dist/internal/Auth.js +572 -33
- package/dist/internal/Auth.js.map +1 -1
- package/dist/internal/Log.d.ts +7 -3
- package/dist/internal/Log.d.ts.map +1 -1
- package/dist/internal/Log.js +4 -0
- package/dist/internal/Log.js.map +1 -1
- package/dist/internal/OpenApi.d.ts +0 -19
- package/dist/internal/OpenApi.d.ts.map +1 -1
- package/dist/internal/OpenApi.js +0 -57
- package/dist/internal/OpenApi.js.map +1 -1
- package/dist/internal/RpcSchema.d.ts +10 -0
- package/dist/internal/RpcSchema.d.ts.map +1 -0
- package/dist/internal/RpcSchema.js +2 -0
- package/dist/internal/RpcSchema.js.map +1 -0
- package/dist/internal/Store.d.ts +49 -4
- package/dist/internal/Store.d.ts.map +1 -1
- package/dist/internal/Store.js +81 -5
- package/dist/internal/Store.js.map +1 -1
- package/dist/internal/VerifiedTokens.d.ts +15 -34
- package/dist/internal/VerifiedTokens.d.ts.map +1 -1
- package/dist/internal/VerifiedTokens.js +115 -159
- package/dist/internal/VerifiedTokens.js.map +1 -1
- package/dist/internal/Viem.d.ts +84 -0
- package/dist/internal/Viem.d.ts.map +1 -1
- package/dist/internal/Webhooks.d.ts +50 -36
- package/dist/internal/Webhooks.d.ts.map +1 -1
- package/dist/internal/Webhooks.js +92 -205
- package/dist/internal/Webhooks.js.map +1 -1
- package/package.json +37 -6
- package/src/ApiKey.test.ts +3 -3
- package/src/ApiKey.ts +11 -12
- package/src/ApiKeys.test.ts +278 -0
- package/src/ApiKeys.ts +358 -0
- package/src/App.test-d.ts +1 -1
- package/src/App.test.ts +40508 -19083
- package/src/App.ts +239 -416
- package/src/Client.test-d.ts +307 -16
- package/src/Handler.ts +154 -0
- package/src/Scope.ts +17 -4
- package/src/Webhooks.test.ts +8 -7
- package/src/Webhooks.ts +13 -7
- package/src/admin/Access.ts +1 -3
- package/src/admin/App.ts +19 -24
- package/src/admin/apps/api-keys.test.ts +70 -18
- package/src/admin/apps/api-keys.ts +92 -9
- package/src/admin/apps/scopes.test.ts +8 -0
- package/src/admin/apps/verified-tokens.test.ts +3 -30
- package/src/admin/apps/verified-tokens.ts +18 -37
- package/src/admin/ui.generated.ts +1 -1
- package/src/apps/composition.test.ts +66 -0
- package/src/apps/data/App.ts +295 -0
- package/src/apps/data/index.ts +2 -0
- package/src/apps/{activities.test.ts → data/routes/activities.test.ts} +3 -3
- package/src/apps/{activities.ts → data/routes/activities.ts} +15 -15
- package/src/apps/{addresses.ts → data/routes/addresses.ts} +6 -6
- package/src/apps/{balances.test.ts → data/routes/balances.test.ts} +6 -5
- package/src/apps/{balances.ts → data/routes/balances.ts} +46 -12
- package/src/apps/{blocks.test.ts → data/routes/blocks.test.ts} +4 -4
- package/src/apps/{blocks.ts → data/routes/blocks.ts} +14 -14
- package/src/apps/{coingecko.test.ts → data/routes/coingecko.test.ts} +2 -2
- package/src/apps/{coingecko.ts → data/routes/coingecko.ts} +14 -14
- package/src/apps/{exchanges.test.ts → data/routes/exchanges.test.ts} +2 -2
- package/src/apps/{exchanges.ts → data/routes/exchanges.ts} +20 -20
- package/src/apps/{fee-amm.test.ts → data/routes/fee-amm.test.ts} +3 -3
- package/src/apps/{fee-amm.ts → data/routes/fee-amm.ts} +18 -18
- package/src/apps/{indexer.test.ts → data/routes/indexer.test.ts} +27 -6
- package/src/apps/{indexer.ts → data/routes/indexer.ts} +13 -10
- package/src/apps/{receipts.test.ts → data/routes/receipts.test.ts} +4 -4
- package/src/apps/{receipts.ts → data/routes/receipts.ts} +13 -13
- package/src/apps/{rpc.test.ts → data/routes/rpc.test.ts} +142 -13
- package/src/apps/{rpc.ts → data/routes/rpc.ts} +7 -7
- package/src/apps/{tokenlist.test.ts → data/routes/tokenlist.test.ts} +8 -10
- package/src/apps/{tokenlist.ts → data/routes/tokenlist.ts} +9 -9
- package/src/apps/{tokens.test.ts → data/routes/tokens.test.ts} +6 -5
- package/src/apps/{tokens.ts → data/routes/tokens.ts} +20 -20
- package/src/apps/{transactions.test.ts → data/routes/transactions.test.ts} +4 -4
- package/src/apps/{transactions.ts → data/routes/transactions.ts} +15 -15
- package/src/apps/{transfers.test.ts → data/routes/transfers.test.ts} +3 -3
- package/src/apps/{transfers.ts → data/routes/transfers.ts} +15 -15
- package/src/apps/{verified-tokens.test.ts → data/routes/verified-tokens.test.ts} +27 -15
- package/src/apps/{verified-tokens.ts → data/routes/verified-tokens.ts} +10 -10
- package/src/apps/{webhooks.test.ts → data/routes/webhooks.test.ts} +137 -150
- package/src/apps/{webhooks.ts → data/routes/webhooks.ts} +83 -81
- package/src/apps/docs/App.ts +136 -0
- package/src/apps/docs/index.ts +1 -0
- package/src/apps/index.ts +3 -0
- package/src/apps/metadata.ts +23 -0
- package/src/apps/platform/App.ts +130 -0
- package/src/apps/platform/index.ts +1 -0
- package/src/apps/platform/routes/api-keys.test.ts +330 -0
- package/src/apps/platform/routes/api-keys.ts +427 -0
- package/src/apps/platform/routes/invitations.test.ts +361 -0
- package/src/apps/platform/routes/invitations.ts +519 -0
- package/src/apps/platform/routes/me.test.ts +121 -0
- package/src/apps/platform/routes/me.ts +106 -0
- package/src/apps/platform/routes/members.test.ts +195 -0
- package/src/apps/platform/routes/members.ts +313 -0
- package/src/apps/platform/routes/orgs.test.ts +313 -0
- package/src/apps/platform/routes/orgs.ts +343 -0
- package/src/apps/platform/routes/projects.test.ts +181 -0
- package/src/apps/platform/routes/projects.ts +367 -0
- package/src/apps/relay/App.test.ts +484 -0
- package/src/apps/relay/App.ts +128 -0
- package/src/apps/relay/Sponsorships.test.ts +375 -0
- package/src/apps/relay/Sponsorships.ts +305 -0
- package/src/apps/relay/index.ts +2 -0
- package/src/{Cli.test.ts → cli/Cli.test.ts} +10 -9
- package/src/{Cli.ts → cli/Cli.ts} +16 -9
- package/src/cli/bin.ts +4 -0
- package/src/cli/commands/admin.test.ts +71 -0
- package/src/cli/commands/admin.ts +60 -0
- package/src/cloudflare.ts +10 -0
- package/src/db/Db.test.ts +84 -0
- package/src/db/Db.ts +542 -0
- package/src/db/Schema.ts +301 -0
- package/src/db/tables/invitations.ts +198 -0
- package/src/db/tables/memberships.ts +208 -0
- package/src/db/tables/organizations.test.ts +160 -0
- package/src/db/tables/organizations.ts +232 -0
- package/src/db/tables/projects.ts +107 -0
- package/src/db/tables/sponsoredTransactions.test.ts +218 -0
- package/src/db/tables/sponsoredTransactions.ts +236 -0
- package/src/db/tables/users.ts +85 -0
- package/src/db/tables/verifiedTokens.ts +111 -0
- package/src/db/tables/webhookDeliveries.ts +159 -0
- package/src/db/tables/webhookSubscriptions.ts +358 -0
- package/src/handlers/internal/executionError.ts +207 -0
- package/src/handlers/internal/multisig.test.ts +818 -0
- package/src/handlers/internal/multisig.ts +823 -0
- package/src/handlers/internal/requestListener.ts +273 -0
- package/src/handlers/internal/sponsorship.ts +245 -0
- package/src/handlers/internal/types.ts +378 -0
- package/src/handlers/internal/utils.ts +153 -0
- package/src/handlers/relay.test.ts +2035 -0
- package/src/handlers/relay.ts +1623 -0
- package/src/index.ts +4 -2
- package/src/internal/Auth.test.ts +337 -21
- package/src/internal/Auth.ts +783 -44
- package/src/internal/Cursor.test.ts +3 -2
- package/src/internal/Log.test.ts +1 -1
- package/src/internal/Log.ts +11 -3
- package/src/internal/MetricSink.ts +1 -1
- package/src/internal/OpenApi.ts +0 -59
- package/src/internal/RateLimit.test.ts +1 -1
- package/src/internal/RpcSchema.ts +10 -0
- package/src/internal/Store.test.ts +150 -10
- package/src/internal/Store.ts +136 -11
- package/src/internal/Tidx.local.test.ts +1 -1
- package/src/internal/VerifiedTokens.test.ts +99 -115
- package/src/internal/VerifiedTokens.ts +123 -175
- package/src/internal/WebhookDestination.test.ts +1 -1
- package/src/internal/Webhooks.test.ts +111 -111
- package/src/internal/Webhooks.ts +103 -221
- package/dist/ApiKeySource.d.ts +0 -166
- package/dist/ApiKeySource.d.ts.map +0 -1
- package/dist/ApiKeySource.js +0 -234
- package/dist/ApiKeySource.js.map +0 -1
- package/dist/Cli.d.ts.map +0 -1
- package/dist/Cli.js +0 -13
- package/dist/Cli.js.map +0 -1
- package/dist/apps/activities.js.map +0 -1
- package/dist/apps/addresses.js.map +0 -1
- package/dist/apps/balances.d.ts.map +0 -1
- package/dist/apps/balances.js.map +0 -1
- package/dist/apps/blocks.d.ts.map +0 -1
- package/dist/apps/blocks.js.map +0 -1
- package/dist/apps/coingecko.d.ts.map +0 -1
- package/dist/apps/coingecko.js.map +0 -1
- package/dist/apps/exchanges.d.ts.map +0 -1
- package/dist/apps/exchanges.js.map +0 -1
- package/dist/apps/fee-amm.d.ts.map +0 -1
- package/dist/apps/fee-amm.js.map +0 -1
- package/dist/apps/indexer.d.ts.map +0 -1
- package/dist/apps/indexer.js.map +0 -1
- package/dist/apps/receipts.d.ts.map +0 -1
- package/dist/apps/receipts.js.map +0 -1
- package/dist/apps/rpc.d.ts.map +0 -1
- package/dist/apps/rpc.js.map +0 -1
- package/dist/apps/tokenlist.d.ts.map +0 -1
- package/dist/apps/tokenlist.js.map +0 -1
- package/dist/apps/tokens.d.ts.map +0 -1
- package/dist/apps/tokens.js.map +0 -1
- package/dist/apps/transactions.d.ts.map +0 -1
- package/dist/apps/transactions.js.map +0 -1
- package/dist/apps/transfers.d.ts.map +0 -1
- package/dist/apps/transfers.js.map +0 -1
- package/dist/apps/verified-tokens.d.ts.map +0 -1
- package/dist/apps/verified-tokens.js.map +0 -1
- package/dist/apps/webhooks.js.map +0 -1
- package/dist/internal/OpenApi.fonts.d.ts.map +0 -1
- package/dist/internal/OpenApi.fonts.js.map +0 -1
- package/dist/internal/OpenApi.logo.d.ts.map +0 -1
- package/dist/internal/OpenApi.logo.js.map +0 -1
- package/seeds/31318/icons/0x20c0000000000000000000000000000000000000.svg +0 -1
- package/seeds/31318/icons/0x20c0000000000000000000000000000000000001.svg +0 -1
- package/seeds/31318/icons/0x20c0000000000000000000000000000000000002.svg +0 -1
- package/seeds/31318/icons/0x20c0000000000000000000000000000000000003.svg +0 -1
- package/seeds/31318/verified-tokens.json +0 -30
- package/seeds/4217/icons/0x20c0000000000000000000000000000000000000.svg +0 -25
- package/seeds/4217/icons/0x20c000000000000000000000048c8f36df1c9a4a.svg +0 -1
- package/seeds/4217/icons/0x20c0000000000000000000000520792dcccccccc.svg +0 -1
- package/seeds/4217/icons/0x20c0000000000000000000000a6da882d075a4c3.svg +0 -4
- package/seeds/4217/icons/0x20c000000000000000000000111111111e910f0f.svg +0 -30
- package/seeds/4217/icons/0x20c00000000000000000000014f22ca97301eb73.svg +0 -1
- package/seeds/4217/icons/0x20c0000000000000000000001621e21f71cf12fb.svg +0 -1
- package/seeds/4217/icons/0x20c0000000000000000000002f52d5cc21a3207b.svg +0 -1
- package/seeds/4217/icons/0x20c0000000000000000000003158081efd85bfc2.svg +0 -11
- package/seeds/4217/icons/0x20c0000000000000000000003554d28269e0f3c2.svg +0 -1
- package/seeds/4217/icons/0x20c000000000000000000000383a23bacb546ab9.svg +0 -1
- package/seeds/4217/icons/0x20c00000000000000000000042109aef2f8b28e1.png +0 -0
- package/seeds/4217/icons/0x20c0000000000000000000005c0bac7cef389a11.svg +0 -1
- package/seeds/4217/icons/0x20c0000000000000000000006fd9a167923ba194.png +0 -0
- package/seeds/4217/icons/0x20c0000000000000000000007f7ba549dd0251b9.svg +0 -1
- package/seeds/4217/icons/0x20c0000000000000000000008191667423f70e67.svg +0 -5
- package/seeds/4217/icons/0x20c0000000000000000000008ee4fcff88888888.svg +0 -1
- package/seeds/4217/icons/0x20c0000000000000000000009a4a4b17e0dc6651.svg +0 -1
- package/seeds/4217/icons/0x20c000000000000000000000ab02d39df30bd17e.svg +0 -1
- package/seeds/4217/icons/0x20c000000000000000000000ae247a1130450f09.svg +0 -1
- package/seeds/4217/icons/0x20c000000000000000000000aeed2ec36a54d0e5.svg +0 -1
- package/seeds/4217/icons/0x20c000000000000000000000b9537d11c60e8b50.svg +0 -1
- package/seeds/4217/icons/0x20c000000000000000000000bd95bfb69fbe6ce3.svg +0 -1
- package/seeds/4217/icons/0x20c000000000000000000000c412ec89d0c08be5.svg +0 -1
- package/seeds/4217/icons/0x20c000000000000000000000f047dd7018e50367.png +0 -0
- package/seeds/4217/verified-tokens.json +0 -177
- package/seeds/42431/icons/0x20c0000000000000000000000000000000000000.svg +0 -25
- package/seeds/42431/icons/0x20c0000000000000000000000000000000000001.svg +0 -1
- package/seeds/42431/icons/0x20c0000000000000000000000000000000000002.svg +0 -1
- package/seeds/42431/icons/0x20c0000000000000000000000000000000000003.svg +0 -1
- package/seeds/42431/icons/0x20c0000000000000000000009e8d7eb59b783726.svg +0 -1
- package/seeds/42431/icons/0x20c000000000000000000000d72572838bbee59c.svg +0 -1
- package/seeds/42431/verified-tokens.json +0 -44
- package/seeds/openrpc/README.md +0 -27
- package/seeds/openrpc/execution-apis.json +0 -8989
- package/src/ApiKeySource.test-d.ts +0 -11
- package/src/ApiKeySource.test.ts +0 -295
- package/src/ApiKeySource.ts +0 -303
- /package/src/{internal/OpenApi.fonts.ts → apps/docs/fonts.ts} +0 -0
- /package/src/{internal/OpenApi.logo.ts → apps/docs/logo.ts} +0 -0
|
@@ -0,0 +1,519 @@
|
|
|
1
|
+
import { type Context, Hono } from 'hono'
|
|
2
|
+
import * as z from 'zod/mini'
|
|
3
|
+
|
|
4
|
+
import * as Auth from '../../../internal/Auth.js'
|
|
5
|
+
import * as Db from '../../../db/Db.js'
|
|
6
|
+
import * as Invitations from '../../../db/tables/invitations.js'
|
|
7
|
+
import * as Memberships from '../../../db/tables/memberships.js'
|
|
8
|
+
import * as OpenApi from '../../../internal/OpenApi.js'
|
|
9
|
+
import * as Response from '../../../internal/Response.js'
|
|
10
|
+
import * as Schema from '../../../internal/Schema.js'
|
|
11
|
+
|
|
12
|
+
import type { Email, Environment } from '../App.js'
|
|
13
|
+
|
|
14
|
+
/** How long an invitation stays acceptable. */
|
|
15
|
+
const ttlDays = 7
|
|
16
|
+
|
|
17
|
+
/** Zod schemas owned by the invitations resource. */
|
|
18
|
+
export namespace schema {
|
|
19
|
+
/** A membership role. */
|
|
20
|
+
const role = z
|
|
21
|
+
.enum(Memberships.roles)
|
|
22
|
+
.check(z.describe('Role granted when the invitation is accepted.'), z.meta({ examples: ['member'] })) // prettier-ignore
|
|
23
|
+
|
|
24
|
+
/** One pending invitation. */
|
|
25
|
+
export const Invitation = Schema.describe(
|
|
26
|
+
z.object({
|
|
27
|
+
createdAt: z
|
|
28
|
+
.string()
|
|
29
|
+
.check(
|
|
30
|
+
z.describe('When the invitation was created (ISO 8601).'),
|
|
31
|
+
z.meta({ examples: ['2026-01-01T00:00:00.000Z'] }),
|
|
32
|
+
),
|
|
33
|
+
email: z
|
|
34
|
+
.string()
|
|
35
|
+
.check(z.describe('Invitee email (lowercase).'), z.meta({ examples: ['dev@example.com'] })),
|
|
36
|
+
expiresAt: z
|
|
37
|
+
.string()
|
|
38
|
+
.check(
|
|
39
|
+
z.describe('When the invitation expires (ISO 8601).'),
|
|
40
|
+
z.meta({ examples: ['2026-01-08T00:00:00.000Z'] }),
|
|
41
|
+
),
|
|
42
|
+
id: z
|
|
43
|
+
.string()
|
|
44
|
+
.check(
|
|
45
|
+
z.describe('Opaque invitation id (`inv_…`).'),
|
|
46
|
+
z.meta({ examples: ['inv_1a2b3c4d5e6f7g8h9j0k1m2n'] }),
|
|
47
|
+
),
|
|
48
|
+
invitedBy: z
|
|
49
|
+
.string()
|
|
50
|
+
.check(
|
|
51
|
+
z.describe('User id (`usr_…`) that created the invitation.'),
|
|
52
|
+
z.meta({ examples: ['usr_1a2b3c4d5e6f7g8h9j0k1m2n'] }),
|
|
53
|
+
),
|
|
54
|
+
orgId: z
|
|
55
|
+
.string()
|
|
56
|
+
.check(
|
|
57
|
+
z.describe('Organization id (`org_…`) the invitation joins.'),
|
|
58
|
+
z.meta({ examples: ['org_1a2b3c4d5e6f7g8h9j0k1m2n'] }),
|
|
59
|
+
),
|
|
60
|
+
role,
|
|
61
|
+
}),
|
|
62
|
+
'One pending invitation.',
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
/** Path parameters addressing an organization's invitation collection. */
|
|
66
|
+
export const Params = z
|
|
67
|
+
.object({
|
|
68
|
+
orgId: z
|
|
69
|
+
.string()
|
|
70
|
+
.check(
|
|
71
|
+
z.describe('The organization id (`org_…`).'),
|
|
72
|
+
z.meta({ examples: ['org_1a2b3c4d5e6f7g8h9j0k1m2n'] }),
|
|
73
|
+
),
|
|
74
|
+
})
|
|
75
|
+
.check(z.describe("Path parameters for an organization's invitations."))
|
|
76
|
+
|
|
77
|
+
/** Path parameters addressing one org-scoped invitation. */
|
|
78
|
+
export const InvitationParams = z
|
|
79
|
+
.object({
|
|
80
|
+
invitationId: z
|
|
81
|
+
.string()
|
|
82
|
+
.check(
|
|
83
|
+
z.describe('The invitation id (`inv_…`).'),
|
|
84
|
+
z.meta({ examples: ['inv_1a2b3c4d5e6f7g8h9j0k1m2n'] }),
|
|
85
|
+
),
|
|
86
|
+
orgId: z
|
|
87
|
+
.string()
|
|
88
|
+
.check(
|
|
89
|
+
z.describe('The organization id (`org_…`).'),
|
|
90
|
+
z.meta({ examples: ['org_1a2b3c4d5e6f7g8h9j0k1m2n'] }),
|
|
91
|
+
),
|
|
92
|
+
})
|
|
93
|
+
.check(z.describe('Path parameters for one invitation.'))
|
|
94
|
+
|
|
95
|
+
/** Path parameters addressing one of the caller's invitations. */
|
|
96
|
+
export const AcceptParams = z
|
|
97
|
+
.object({
|
|
98
|
+
invitationId: z
|
|
99
|
+
.string()
|
|
100
|
+
.check(
|
|
101
|
+
z.describe('The invitation id (`inv_…`).'),
|
|
102
|
+
z.meta({ examples: ['inv_1a2b3c4d5e6f7g8h9j0k1m2n'] }),
|
|
103
|
+
),
|
|
104
|
+
})
|
|
105
|
+
.check(z.describe("Path parameters for one of the caller's invitations."))
|
|
106
|
+
|
|
107
|
+
/** Request body creating an invitation. */
|
|
108
|
+
export const InvitationBody = z
|
|
109
|
+
.object({
|
|
110
|
+
email: z
|
|
111
|
+
.email()
|
|
112
|
+
.check(z.describe('Invitee email.'), z.meta({ examples: ['dev@example.com'] })),
|
|
113
|
+
role,
|
|
114
|
+
})
|
|
115
|
+
.check(z.describe('Fields for creating an invitation.'))
|
|
116
|
+
|
|
117
|
+
/** Schemas for the listInvitations / listMyInvitations operations. */
|
|
118
|
+
export namespace listInvitations {
|
|
119
|
+
/** Non-paginated list of pending invitations. */
|
|
120
|
+
export const Response = Schema.describe(
|
|
121
|
+
z.object({
|
|
122
|
+
data: z.array(Invitation).check(z.describe('Pending invitations, newest first.')),
|
|
123
|
+
}),
|
|
124
|
+
'A non-paginated list of pending invitations.',
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Schemas for the listMyInvitations operation. */
|
|
129
|
+
export namespace listMyInvitations {
|
|
130
|
+
/** A pending invitation addressed to the caller, with the inviting org's name. */
|
|
131
|
+
export const Item = z.extend(Invitation, {
|
|
132
|
+
orgName: z
|
|
133
|
+
.string()
|
|
134
|
+
.check(
|
|
135
|
+
z.describe('Human-readable name of the inviting organization.'),
|
|
136
|
+
z.meta({ examples: ['Acme, Inc.'] }),
|
|
137
|
+
),
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
/** Non-paginated list of the caller's pending invitations. */
|
|
141
|
+
export const Response = Schema.describe(
|
|
142
|
+
z.object({
|
|
143
|
+
data: z.array(Item).check(z.describe('Pending invitations, newest first.')),
|
|
144
|
+
}),
|
|
145
|
+
"A non-paginated list of the caller's pending invitations.",
|
|
146
|
+
)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** Schemas for the acceptInvitation operation. */
|
|
150
|
+
export namespace acceptInvitation {
|
|
151
|
+
/** The membership granted by accepting. */
|
|
152
|
+
export const Response = Schema.describe(
|
|
153
|
+
z.object({
|
|
154
|
+
orgId: z
|
|
155
|
+
.string()
|
|
156
|
+
.check(
|
|
157
|
+
z.describe('Organization id (`org_…`) joined.'),
|
|
158
|
+
z.meta({ examples: ['org_1a2b3c4d5e6f7g8h9j0k1m2n'] }),
|
|
159
|
+
),
|
|
160
|
+
role: z
|
|
161
|
+
.enum(Memberships.roles)
|
|
162
|
+
.check(z.describe('Granted role.'), z.meta({ examples: ['member'] })),
|
|
163
|
+
userId: z
|
|
164
|
+
.string()
|
|
165
|
+
.check(
|
|
166
|
+
z.describe('Member user id (`usr_…`).'),
|
|
167
|
+
z.meta({ examples: ['usr_1a2b3c4d5e6f7g8h9j0k1m2n'] }),
|
|
168
|
+
),
|
|
169
|
+
}),
|
|
170
|
+
'The membership granted by accepting.',
|
|
171
|
+
)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** Schemas for the revokeInvitation operation. */
|
|
175
|
+
export namespace revokeInvitation {
|
|
176
|
+
/** Confirmation that the invitation was revoked. */
|
|
177
|
+
export const Response = z
|
|
178
|
+
.object({
|
|
179
|
+
id: z
|
|
180
|
+
.string()
|
|
181
|
+
.check(
|
|
182
|
+
z.describe('ID of the invitation that was revoked.'),
|
|
183
|
+
z.meta({ examples: ['inv_1a2b3c4d5e6f7g8h9j0k1m2n'] }),
|
|
184
|
+
),
|
|
185
|
+
})
|
|
186
|
+
.check(z.describe('Confirmation that the invitation was revoked.'))
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Mounts the invitations surface: org-scoped management
|
|
192
|
+
* (`/orgs/:orgId/invitations`, admins and owners) plus the invitee's side
|
|
193
|
+
* (`/invitations`, matched against the session's verified email). Invitation
|
|
194
|
+
* emails dispatch through the sender the platform group publishes on context
|
|
195
|
+
* (`c.get('email')`), best-effort.
|
|
196
|
+
*/
|
|
197
|
+
export function invitations() {
|
|
198
|
+
return new Hono<Environment>()
|
|
199
|
+
.post(
|
|
200
|
+
'/v1/orgs/:orgId{org_[A-Za-z0-9_-]+}/invitations',
|
|
201
|
+
Auth.policy({ session: true }),
|
|
202
|
+
Auth.ensureOrg({ role: 'admin' }),
|
|
203
|
+
OpenApi.validate('param', schema.Params, {
|
|
204
|
+
code: 'param_invalid',
|
|
205
|
+
message: 'Check the path parameters and try again.',
|
|
206
|
+
}),
|
|
207
|
+
OpenApi.validate('json', schema.InvitationBody, {
|
|
208
|
+
code: 'body_invalid',
|
|
209
|
+
message: 'Check the request body and try again.',
|
|
210
|
+
}),
|
|
211
|
+
OpenApi.describeRoute({
|
|
212
|
+
description: 'Invite an email to the organization with a role. The invitee accepts after signing in with that verified email.', // prettier-ignore
|
|
213
|
+
operationId: 'createInvitation',
|
|
214
|
+
responses: OpenApi.responses({
|
|
215
|
+
errors: { 404: 'No accessible organization was found for this id.' },
|
|
216
|
+
success: { description: 'The created invitation.', schema: schema.Invitation },
|
|
217
|
+
}),
|
|
218
|
+
summary: 'Create invitation',
|
|
219
|
+
tags: ['Invitations'],
|
|
220
|
+
}),
|
|
221
|
+
async (c) => {
|
|
222
|
+
if (Auth.narrowAccess) return Auth.superAdminAccessError(c)
|
|
223
|
+
if (Auth.narrowScope) return Auth.ensureOrgError(c)
|
|
224
|
+
if (OpenApi.narrowValidation)
|
|
225
|
+
return OpenApi.validationError(c, {
|
|
226
|
+
code: 'param_invalid',
|
|
227
|
+
message: 'Check the path parameters and try again.',
|
|
228
|
+
})
|
|
229
|
+
if (OpenApi.narrowValidation)
|
|
230
|
+
return OpenApi.validationError(c, {
|
|
231
|
+
code: 'body_invalid',
|
|
232
|
+
message: 'Check the request body and try again.',
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
const body = c.req.valid('json')
|
|
236
|
+
// Owners invite anyone; everyone else grants only roles strictly below
|
|
237
|
+
// their own (an admin invites members, never peers). The super admin
|
|
238
|
+
// has no membership and may grant any role.
|
|
239
|
+
const caller = Auth.membership(c)
|
|
240
|
+
if (
|
|
241
|
+
caller &&
|
|
242
|
+
caller.role !== 'owner' &&
|
|
243
|
+
Memberships.rank[body.role] >= Memberships.rank[caller.role]
|
|
244
|
+
)
|
|
245
|
+
return Response.error(c, {
|
|
246
|
+
code: 'forbidden',
|
|
247
|
+
message: 'Cannot invite at or above your own role',
|
|
248
|
+
status: 403,
|
|
249
|
+
})
|
|
250
|
+
const invitedBy = caller?.userId ?? 'super_admin'
|
|
251
|
+
|
|
252
|
+
const db = Db.get(c.get('db'))
|
|
253
|
+
try {
|
|
254
|
+
const record = await Invitations.create(db, {
|
|
255
|
+
email: body.email,
|
|
256
|
+
expiresAt: new Date(Date.now() + ttlDays * 86_400_000).toISOString(),
|
|
257
|
+
invitedBy,
|
|
258
|
+
orgId: Auth.org(c).id,
|
|
259
|
+
role: body.role,
|
|
260
|
+
})
|
|
261
|
+
dispatchEmail(c, { email: c.get('email'), org: Auth.org(c).name, record })
|
|
262
|
+
return c.json(Response.validated(schema.Invitation, serializeInvitation(record)), 200)
|
|
263
|
+
} catch (cause) {
|
|
264
|
+
return Response.upstream(c, cause)
|
|
265
|
+
}
|
|
266
|
+
},
|
|
267
|
+
)
|
|
268
|
+
.get(
|
|
269
|
+
'/v1/orgs/:orgId{org_[A-Za-z0-9_-]+}/invitations',
|
|
270
|
+
Auth.policy({ session: true }),
|
|
271
|
+
Auth.ensureOrg({ role: 'admin' }),
|
|
272
|
+
OpenApi.validate('param', schema.Params, {
|
|
273
|
+
code: 'param_invalid',
|
|
274
|
+
message: 'Check the path parameters and try again.',
|
|
275
|
+
}),
|
|
276
|
+
OpenApi.describeRoute({
|
|
277
|
+
operationId: 'listInvitations',
|
|
278
|
+
responses: OpenApi.responses({
|
|
279
|
+
errors: { 404: 'No accessible organization was found for this id.' },
|
|
280
|
+
success: {
|
|
281
|
+
description: 'Pending invitations, newest first.',
|
|
282
|
+
schema: schema.listInvitations.Response,
|
|
283
|
+
},
|
|
284
|
+
}),
|
|
285
|
+
summary: 'List invitations',
|
|
286
|
+
tags: ['Invitations'],
|
|
287
|
+
}),
|
|
288
|
+
async (c) => {
|
|
289
|
+
if (Auth.narrowAccess) return Auth.superAdminAccessError(c)
|
|
290
|
+
if (Auth.narrowScope) return Auth.ensureOrgError(c)
|
|
291
|
+
if (OpenApi.narrowValidation)
|
|
292
|
+
return OpenApi.validationError(c, {
|
|
293
|
+
code: 'param_invalid',
|
|
294
|
+
message: 'Check the path parameters and try again.',
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
const db = Db.get(c.get('db'))
|
|
298
|
+
try {
|
|
299
|
+
const records = await Invitations.listByOrg(db, Auth.org(c).id)
|
|
300
|
+
return c.json(
|
|
301
|
+
Response.validated(schema.listInvitations.Response, {
|
|
302
|
+
data: records.map(serializeInvitation),
|
|
303
|
+
}),
|
|
304
|
+
200,
|
|
305
|
+
)
|
|
306
|
+
} catch (cause) {
|
|
307
|
+
return Response.upstream(c, cause)
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
)
|
|
311
|
+
.delete(
|
|
312
|
+
'/v1/orgs/:orgId{org_[A-Za-z0-9_-]+}/invitations/:invitationId{inv_[A-Za-z0-9_-]+}',
|
|
313
|
+
Auth.policy({ session: true }),
|
|
314
|
+
Auth.ensureOrg({ role: 'admin' }),
|
|
315
|
+
OpenApi.validate('param', schema.InvitationParams, {
|
|
316
|
+
code: 'param_invalid',
|
|
317
|
+
message: 'Check the path parameters and try again.',
|
|
318
|
+
}),
|
|
319
|
+
OpenApi.describeRoute({
|
|
320
|
+
operationId: 'revokeInvitation',
|
|
321
|
+
responses: OpenApi.responses({
|
|
322
|
+
errors: { 404: 'No accessible pending invitation was found for this id.' },
|
|
323
|
+
success: {
|
|
324
|
+
description: 'Confirmation that the invitation was revoked.',
|
|
325
|
+
schema: schema.revokeInvitation.Response,
|
|
326
|
+
},
|
|
327
|
+
}),
|
|
328
|
+
summary: 'Revoke invitation',
|
|
329
|
+
tags: ['Invitations'],
|
|
330
|
+
}),
|
|
331
|
+
async (c) => {
|
|
332
|
+
if (Auth.narrowAccess) return Auth.superAdminAccessError(c)
|
|
333
|
+
if (Auth.narrowScope) return Auth.ensureOrgError(c)
|
|
334
|
+
if (OpenApi.narrowValidation)
|
|
335
|
+
return OpenApi.validationError(c, {
|
|
336
|
+
code: 'param_invalid',
|
|
337
|
+
message: 'Check the path parameters and try again.',
|
|
338
|
+
})
|
|
339
|
+
|
|
340
|
+
const { invitationId } = c.req.valid('param')
|
|
341
|
+
const db = Db.get(c.get('db'))
|
|
342
|
+
try {
|
|
343
|
+
// Bind the invitation to the scoped org before revoking.
|
|
344
|
+
const record = await Invitations.get(db, invitationId)
|
|
345
|
+
if (!record || record.orgId !== Auth.org(c).id) return invitationNotFound(c)
|
|
346
|
+
const revoked = await Invitations.revoke(db, invitationId)
|
|
347
|
+
if (!revoked) return invitationNotFound(c)
|
|
348
|
+
return c.json(
|
|
349
|
+
Response.validated(schema.revokeInvitation.Response, { id: invitationId }),
|
|
350
|
+
200,
|
|
351
|
+
)
|
|
352
|
+
} catch (cause) {
|
|
353
|
+
return Response.upstream(c, cause)
|
|
354
|
+
}
|
|
355
|
+
},
|
|
356
|
+
)
|
|
357
|
+
.get(
|
|
358
|
+
'/v1/invitations',
|
|
359
|
+
Auth.policy({ session: true }),
|
|
360
|
+
OpenApi.describeRoute({
|
|
361
|
+
description: "Pending invitations addressed to the session's verified email.",
|
|
362
|
+
operationId: 'listMyInvitations',
|
|
363
|
+
responses: OpenApi.responses({
|
|
364
|
+
success: {
|
|
365
|
+
description: 'Pending invitations, newest first.',
|
|
366
|
+
schema: schema.listMyInvitations.Response,
|
|
367
|
+
},
|
|
368
|
+
}),
|
|
369
|
+
summary: 'List my invitations',
|
|
370
|
+
tags: ['Invitations'],
|
|
371
|
+
}),
|
|
372
|
+
async (c) => {
|
|
373
|
+
if (Auth.narrowAccess) return Auth.superAdminAccessError(c)
|
|
374
|
+
|
|
375
|
+
const principal = Auth.getPrincipal(c)
|
|
376
|
+
if (principal?.type !== 'session') return sessionRequired(c)
|
|
377
|
+
// Without a verified email there is nothing to match against.
|
|
378
|
+
if (!principal.email)
|
|
379
|
+
return c.json(Response.validated(schema.listMyInvitations.Response, { data: [] }), 200)
|
|
380
|
+
|
|
381
|
+
const db = Db.get(c.get('db'))
|
|
382
|
+
try {
|
|
383
|
+
const records = await Invitations.listByEmail(db, principal.email)
|
|
384
|
+
return c.json(
|
|
385
|
+
Response.validated(schema.listMyInvitations.Response, {
|
|
386
|
+
data: records.map((record) => ({
|
|
387
|
+
...serializeInvitation(record),
|
|
388
|
+
orgName: record.orgName,
|
|
389
|
+
})),
|
|
390
|
+
}),
|
|
391
|
+
200,
|
|
392
|
+
)
|
|
393
|
+
} catch (cause) {
|
|
394
|
+
return Response.upstream(c, cause)
|
|
395
|
+
}
|
|
396
|
+
},
|
|
397
|
+
)
|
|
398
|
+
.post(
|
|
399
|
+
'/v1/invitations/:invitationId{inv_[A-Za-z0-9_-]+}/accept',
|
|
400
|
+
Auth.policy({ session: true }),
|
|
401
|
+
OpenApi.validate('param', schema.AcceptParams, {
|
|
402
|
+
code: 'param_invalid',
|
|
403
|
+
message: 'Check the path parameters and try again.',
|
|
404
|
+
}),
|
|
405
|
+
OpenApi.describeRoute({
|
|
406
|
+
description: "Accept an invitation addressed to the session's verified email, joining the organization with the invited role.", // prettier-ignore
|
|
407
|
+
operationId: 'acceptInvitation',
|
|
408
|
+
responses: OpenApi.responses({
|
|
409
|
+
errors: { 404: 'No accessible pending invitation was found for this id.' },
|
|
410
|
+
success: {
|
|
411
|
+
description: 'The membership granted by accepting.',
|
|
412
|
+
schema: schema.acceptInvitation.Response,
|
|
413
|
+
},
|
|
414
|
+
}),
|
|
415
|
+
summary: 'Accept invitation',
|
|
416
|
+
tags: ['Invitations'],
|
|
417
|
+
}),
|
|
418
|
+
async (c) => {
|
|
419
|
+
if (Auth.narrowAccess) return Auth.superAdminAccessError(c)
|
|
420
|
+
if (OpenApi.narrowValidation)
|
|
421
|
+
return OpenApi.validationError(c, {
|
|
422
|
+
code: 'param_invalid',
|
|
423
|
+
message: 'Check the path parameters and try again.',
|
|
424
|
+
})
|
|
425
|
+
|
|
426
|
+
const principal = Auth.getPrincipal(c)
|
|
427
|
+
if (principal?.type !== 'session') return sessionRequired(c)
|
|
428
|
+
|
|
429
|
+
const { invitationId } = c.req.valid('param')
|
|
430
|
+
const db = Db.get(c.get('db'))
|
|
431
|
+
try {
|
|
432
|
+
// An invitation is visible only to the verified holder of its email;
|
|
433
|
+
// everyone else sees 404.
|
|
434
|
+
const record = await Invitations.get(db, invitationId)
|
|
435
|
+
if (!record || record.email !== principal.email?.toLowerCase())
|
|
436
|
+
return invitationNotFound(c)
|
|
437
|
+
const accepted = await Invitations.accept(db, invitationId, principal.id)
|
|
438
|
+
if (!accepted) return invitationNotFound(c)
|
|
439
|
+
const membership = await Memberships.get(db, accepted.orgId, principal.id)
|
|
440
|
+
return c.json(
|
|
441
|
+
Response.validated(schema.acceptInvitation.Response, {
|
|
442
|
+
orgId: accepted.orgId,
|
|
443
|
+
role: membership?.role ?? accepted.role,
|
|
444
|
+
userId: principal.id,
|
|
445
|
+
}),
|
|
446
|
+
200,
|
|
447
|
+
)
|
|
448
|
+
} catch (cause) {
|
|
449
|
+
return Response.upstream(c, cause)
|
|
450
|
+
}
|
|
451
|
+
},
|
|
452
|
+
)
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/** Dispatches the invitation email through the app's sender, best-effort. */
|
|
456
|
+
function dispatchEmail(
|
|
457
|
+
c: Context<Environment>,
|
|
458
|
+
options: { email: Email | undefined; org: string; record: Invitations.Record },
|
|
459
|
+
) {
|
|
460
|
+
const { email: sender, org, record } = options
|
|
461
|
+
if (!sender) return
|
|
462
|
+
const days = Math.max(1, Math.round((Date.parse(record.expiresAt) - Date.now()) / 86_400_000))
|
|
463
|
+
const expiry = days === 1 ? '1 day' : `${days} days`
|
|
464
|
+
const link = new URL(sender.consoleUrl ?? 'https://console.tempo.xyz')
|
|
465
|
+
link.searchParams.set('email', record.email)
|
|
466
|
+
const url = link.href
|
|
467
|
+
const dispatch = Promise.resolve(
|
|
468
|
+
sender.send({
|
|
469
|
+
from: sender.from,
|
|
470
|
+
html: `<p>You've been invited to join <strong>${escapeHtml(org)}</strong> on the Tempo Developer Platform</p><p>Sign in at <a href="${url}">${url}</a> to accept. This invitation expires in ${expiry}.</p>`,
|
|
471
|
+
subject: `You've been invited to ${org} on the Tempo Developer Platform`,
|
|
472
|
+
text: `You've been invited to join ${org} on the Tempo Developer Platform\n\nSign in at ${url} to accept. This invitation expires in ${expiry}.`,
|
|
473
|
+
to: record.email,
|
|
474
|
+
}),
|
|
475
|
+
).catch((error) => console.error('invitation email dispatch failed', error))
|
|
476
|
+
try {
|
|
477
|
+
c.executionCtx.waitUntil(dispatch)
|
|
478
|
+
} catch {
|
|
479
|
+
// Non-Workers runtime: the floating promise settles on its own.
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/** Escapes org-controlled text for the HTML body. */
|
|
484
|
+
function escapeHtml(text: string) {
|
|
485
|
+
return text
|
|
486
|
+
.replaceAll('&', '&')
|
|
487
|
+
.replaceAll('<', '<')
|
|
488
|
+
.replaceAll('>', '>')
|
|
489
|
+
.replaceAll('"', '"')
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/** Projects an invitation row onto the pending-invitation shape. */
|
|
493
|
+
function serializeInvitation(record: Invitations.Record) {
|
|
494
|
+
return {
|
|
495
|
+
createdAt: record.createdAt,
|
|
496
|
+
email: record.email,
|
|
497
|
+
expiresAt: record.expiresAt,
|
|
498
|
+
id: record.id,
|
|
499
|
+
invitedBy: record.invitedBy,
|
|
500
|
+
orgId: record.orgId,
|
|
501
|
+
role: record.role,
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
function invitationNotFound(c: Context<Environment>) {
|
|
506
|
+
return Response.error(c, {
|
|
507
|
+
code: 'invitation_not_found',
|
|
508
|
+
message: 'Invitation not found',
|
|
509
|
+
status: 404,
|
|
510
|
+
})
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
function sessionRequired(c: Context<Environment>) {
|
|
514
|
+
return Response.error(c, {
|
|
515
|
+
code: 'forbidden',
|
|
516
|
+
message: 'Session required',
|
|
517
|
+
status: 403,
|
|
518
|
+
})
|
|
519
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts'
|
|
2
|
+
|
|
3
|
+
import * as TestApp from '../../../../test/App.js'
|
|
4
|
+
import * as Me from './me.js'
|
|
5
|
+
|
|
6
|
+
/** Origin pinned for SIWE domain binding; Hono test requests use this host. */
|
|
7
|
+
const origin = 'http://localhost'
|
|
8
|
+
|
|
9
|
+
const superAdminSecret = 'tempo:sk:c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2c2'
|
|
10
|
+
|
|
11
|
+
function createApp() {
|
|
12
|
+
return TestApp.create({
|
|
13
|
+
auth: { keys: [TestApp.key], superAdmin: { secret: superAdminSecret } },
|
|
14
|
+
session: { wallet: { origin } },
|
|
15
|
+
})
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
describe('GET /me', () => {
|
|
19
|
+
test('returns the signed-in user', async () => {
|
|
20
|
+
const app = createApp()
|
|
21
|
+
const account = privateKeyToAccount(generatePrivateKey())
|
|
22
|
+
const { cookie, verifyResponse } = await TestApp.signIn(app, account)
|
|
23
|
+
|
|
24
|
+
expect(verifyResponse.status).toBe(200)
|
|
25
|
+
expect(cookie).toBeDefined()
|
|
26
|
+
|
|
27
|
+
const response = await app.request('/v1/me', { headers: { cookie: cookie! } })
|
|
28
|
+
expect(response.status).toBe(200)
|
|
29
|
+
const body = await TestApp.json(response, Me.schema.User)
|
|
30
|
+
expect(body.address).toBe(account.address.toLowerCase())
|
|
31
|
+
expect(body.id.startsWith('usr_')).toBe(true)
|
|
32
|
+
expect(body.email).toBeUndefined()
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test('sign-in is idempotent per address', async () => {
|
|
36
|
+
const app = createApp()
|
|
37
|
+
const account = privateKeyToAccount(generatePrivateKey())
|
|
38
|
+
|
|
39
|
+
const first = await TestApp.signIn(app, account)
|
|
40
|
+
const firstMe = await app.request('/v1/me', { headers: { cookie: first.cookie! } })
|
|
41
|
+
const firstBody = await TestApp.json(firstMe, Me.schema.User)
|
|
42
|
+
|
|
43
|
+
const second = await TestApp.signIn(app, account)
|
|
44
|
+
const secondMe = await app.request('/v1/me', { headers: { cookie: second.cookie! } })
|
|
45
|
+
const secondBody = await TestApp.json(secondMe, Me.schema.User)
|
|
46
|
+
|
|
47
|
+
expect(secondBody.id).toBe(firstBody.id)
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
test('supports bearer-token sessions', async () => {
|
|
51
|
+
const app = createApp()
|
|
52
|
+
const account = privateKeyToAccount(generatePrivateKey())
|
|
53
|
+
const { verifyResponse } = await TestApp.signIn(app, account, { returnToken: true })
|
|
54
|
+
const { token } = (await verifyResponse.json()) as { token: string }
|
|
55
|
+
|
|
56
|
+
expect(typeof token).toBe('string')
|
|
57
|
+
// The bearer session token takes the API-key header path first; the failed
|
|
58
|
+
// key lookup must fall through to the session lane.
|
|
59
|
+
const response = await app.request('/v1/me', {
|
|
60
|
+
headers: { authorization: `Bearer ${token}` },
|
|
61
|
+
})
|
|
62
|
+
expect(response.status).toBe(200)
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
test('logout revokes the session', async () => {
|
|
66
|
+
const app = createApp()
|
|
67
|
+
const account = privateKeyToAccount(generatePrivateKey())
|
|
68
|
+
const { cookie } = await TestApp.signIn(app, account)
|
|
69
|
+
|
|
70
|
+
const logout = await app.request('/v1/auth/siwe/logout', {
|
|
71
|
+
headers: { cookie: cookie! },
|
|
72
|
+
method: 'POST',
|
|
73
|
+
})
|
|
74
|
+
expect(logout.status).toBe(204)
|
|
75
|
+
|
|
76
|
+
const response = await app.request('/v1/me', { headers: { cookie: cookie! } })
|
|
77
|
+
expect(response.status).toBe(401)
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
test('rejects anonymous requests', async () => {
|
|
81
|
+
const app = createApp()
|
|
82
|
+
const response = await app.request('/v1/me')
|
|
83
|
+
expect(response.status).toBe(401)
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
test('rejects API keys', async () => {
|
|
87
|
+
const app = createApp()
|
|
88
|
+
const response = await app.request('/v1/me', {
|
|
89
|
+
headers: { 'tempo-api-key': TestApp.key.token },
|
|
90
|
+
})
|
|
91
|
+
expect(response.status).toBe(403)
|
|
92
|
+
const { requestId, ...body } = (await response.json()) as { requestId?: string }
|
|
93
|
+
expect(requestId).toBeDefined()
|
|
94
|
+
expect(body).toMatchInlineSnapshot(`
|
|
95
|
+
{
|
|
96
|
+
"error": {
|
|
97
|
+
"code": "forbidden",
|
|
98
|
+
"message": "API key not permitted for this route",
|
|
99
|
+
},
|
|
100
|
+
}
|
|
101
|
+
`)
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
test('rejects the super admin (no user row)', async () => {
|
|
105
|
+
const app = createApp()
|
|
106
|
+
const response = await app.request('/v1/me', {
|
|
107
|
+
headers: { 'tempo-api-key': superAdminSecret },
|
|
108
|
+
})
|
|
109
|
+
expect(response.status).toBe(403)
|
|
110
|
+
const { requestId, ...body } = (await response.json()) as { requestId?: string }
|
|
111
|
+
expect(requestId).toBeDefined()
|
|
112
|
+
expect(body).toMatchInlineSnapshot(`
|
|
113
|
+
{
|
|
114
|
+
"error": {
|
|
115
|
+
"code": "forbidden",
|
|
116
|
+
"message": "Session required",
|
|
117
|
+
},
|
|
118
|
+
}
|
|
119
|
+
`)
|
|
120
|
+
})
|
|
121
|
+
})
|