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
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import type * as ApiKeySource from './ApiKeySource.js'
|
|
2
|
-
|
|
3
|
-
describe('MintInput.scopes', () => {
|
|
4
|
-
// The scope union flows from the central `Scope.catalog`, so minting
|
|
5
|
-
// with an unissuable scope is a compile error.
|
|
6
|
-
test('accepts catalog scopes and the wildcard, rejects unknown ones', () => {
|
|
7
|
-
expectTypeOf<['data:read', 'webhooks:write']>().toExtend<ApiKeySource.MintInput['scopes']>()
|
|
8
|
-
expectTypeOf<['*']>().toExtend<ApiKeySource.MintInput['scopes']>()
|
|
9
|
-
expectTypeOf<['totally:made-up']>().not.toExtend<ApiKeySource.MintInput['scopes']>()
|
|
10
|
-
})
|
|
11
|
-
})
|
package/src/ApiKeySource.test.ts
DELETED
|
@@ -1,295 +0,0 @@
|
|
|
1
|
-
import * as ApiKeySource from './ApiKeySource.js'
|
|
2
|
-
import * as Store from './internal/Store.js'
|
|
3
|
-
|
|
4
|
-
describe('store', () => {
|
|
5
|
-
test('resolves an unknown token to null', async () => {
|
|
6
|
-
const source = ApiKeySource.store(Store.memory())
|
|
7
|
-
expect(await source.resolve('tempo:sk:missing')).toBeNull()
|
|
8
|
-
})
|
|
9
|
-
|
|
10
|
-
// Mock the system clock so timestamps are deterministic without injecting a
|
|
11
|
-
// clock into the source.
|
|
12
|
-
describe('provisioning', () => {
|
|
13
|
-
beforeEach(() => vi.useFakeTimers({ now: new Date('2026-01-01T00:00:00.000Z') }))
|
|
14
|
-
afterEach(() => vi.useRealTimers())
|
|
15
|
-
|
|
16
|
-
test('mints a key and resolves it', async () => {
|
|
17
|
-
const source = ApiKeySource.store(Store.memory())
|
|
18
|
-
|
|
19
|
-
const { record, token } = await source.mint({
|
|
20
|
-
createdBy: 'admin@tempo.xyz',
|
|
21
|
-
name: 'CI key',
|
|
22
|
-
orgId: 'org_acme',
|
|
23
|
-
scopes: ['data:read', 'webhooks:write'],
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
expect(token).toMatch(/^tempo:sk:[a-f0-9]{48}$/)
|
|
27
|
-
expect(record.tokenLast4).toBe(token.slice(-4))
|
|
28
|
-
expect({ ...record, id: '<id>', tokenLast4: '<last4>' }).toMatchInlineSnapshot(`
|
|
29
|
-
{
|
|
30
|
-
"createdAt": "2026-01-01T00:00:00.000Z",
|
|
31
|
-
"createdBy": "admin@tempo.xyz",
|
|
32
|
-
"environment": "production",
|
|
33
|
-
"id": "<id>",
|
|
34
|
-
"name": "CI key",
|
|
35
|
-
"orgId": "org_acme",
|
|
36
|
-
"scopes": [
|
|
37
|
-
"data:read",
|
|
38
|
-
"webhooks:write",
|
|
39
|
-
],
|
|
40
|
-
"tokenLast4": "<last4>",
|
|
41
|
-
}
|
|
42
|
-
`)
|
|
43
|
-
|
|
44
|
-
const resolved = await source.resolve(token)
|
|
45
|
-
expect(resolved?.orgId).toBe('org_acme')
|
|
46
|
-
expect(resolved?.scopes).toEqual(['data:read', 'webhooks:write'])
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
test('lists keys newest first, filtered by org', async () => {
|
|
50
|
-
const source = ApiKeySource.store(Store.memory())
|
|
51
|
-
|
|
52
|
-
// Advance the clock between mints so their createdAt timestamps differ and
|
|
53
|
-
// "newest first" is well-defined.
|
|
54
|
-
await source.mint({ name: 'a', orgId: 'org_a', scopes: ['data:read'] })
|
|
55
|
-
vi.setSystemTime(new Date('2026-01-01T00:00:01.000Z'))
|
|
56
|
-
await source.mint({ name: 'b', orgId: 'org_b', scopes: ['data:read'] })
|
|
57
|
-
|
|
58
|
-
const all = await source.list()
|
|
59
|
-
expect(all.map((record) => record.name)).toMatchInlineSnapshot(`
|
|
60
|
-
[
|
|
61
|
-
"b",
|
|
62
|
-
"a",
|
|
63
|
-
]
|
|
64
|
-
`)
|
|
65
|
-
|
|
66
|
-
const scoped = await source.list({ orgId: 'org_b' })
|
|
67
|
-
expect(scoped.map((record) => record.orgId)).toMatchInlineSnapshot(`
|
|
68
|
-
[
|
|
69
|
-
"org_b",
|
|
70
|
-
]
|
|
71
|
-
`)
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
test('revokes a key by id', async () => {
|
|
75
|
-
const source = ApiKeySource.store(Store.memory())
|
|
76
|
-
|
|
77
|
-
const { record, token } = await source.mint({ orgId: 'org_a', scopes: ['data:read'] })
|
|
78
|
-
|
|
79
|
-
expect(await source.revoke(record.id)).toBe(true)
|
|
80
|
-
expect(await source.resolve(token)).toBeNull()
|
|
81
|
-
expect(await source.revoke(record.id)).toBe(false)
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
test('revoke is O(1): deletes the record without scanning the prefix', async () => {
|
|
85
|
-
const store = Store.memory()
|
|
86
|
-
const source = ApiKeySource.store(store)
|
|
87
|
-
|
|
88
|
-
const { record } = await source.mint({ orgId: 'org_a', scopes: ['data:read'] })
|
|
89
|
-
|
|
90
|
-
// The id index entry maps id -> record key; revoke uses it directly.
|
|
91
|
-
expect(await source.revoke(record.id)).toBe(true)
|
|
92
|
-
// Both the record and its id index entry are gone.
|
|
93
|
-
const { keys } = await store.list({ prefix: 'apikey' })
|
|
94
|
-
expect(keys).toMatchInlineSnapshot(`[]`)
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
test('excludes a key past its expiry from list', async () => {
|
|
98
|
-
const source = ApiKeySource.store(Store.memory())
|
|
99
|
-
|
|
100
|
-
await source.mint({
|
|
101
|
-
expiresAt: '2026-01-01T00:00:30.000Z',
|
|
102
|
-
name: 'short-lived',
|
|
103
|
-
orgId: 'org_a',
|
|
104
|
-
scopes: ['data:read'],
|
|
105
|
-
})
|
|
106
|
-
|
|
107
|
-
expect((await source.list()).map((record) => record.name)).toMatchInlineSnapshot(`
|
|
108
|
-
[
|
|
109
|
-
"short-lived",
|
|
110
|
-
]
|
|
111
|
-
`)
|
|
112
|
-
|
|
113
|
-
// Advance the clock past the expiry.
|
|
114
|
-
vi.setSystemTime(new Date('2026-01-01T00:01:00.000Z'))
|
|
115
|
-
expect(await source.list()).toMatchInlineSnapshot(`[]`)
|
|
116
|
-
})
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
test('resolves before expiry, rejects after', async () => {
|
|
120
|
-
vi.useFakeTimers({ now: new Date('2026-01-01T00:00:00.000Z') })
|
|
121
|
-
try {
|
|
122
|
-
const source = ApiKeySource.store(Store.memory())
|
|
123
|
-
const { token } = await source.mint({
|
|
124
|
-
expiresAt: '2026-01-01T00:01:00.000Z',
|
|
125
|
-
orgId: 'org_a',
|
|
126
|
-
scopes: ['data:read'],
|
|
127
|
-
})
|
|
128
|
-
|
|
129
|
-
expect((await source.resolve(token))?.orgId).toBe('org_a')
|
|
130
|
-
|
|
131
|
-
// Advance the clock past the expiry.
|
|
132
|
-
vi.setSystemTime(new Date('2026-01-01T00:02:00.000Z'))
|
|
133
|
-
expect(await source.resolve(token)).toBeNull()
|
|
134
|
-
} finally {
|
|
135
|
-
vi.useRealTimers()
|
|
136
|
-
}
|
|
137
|
-
})
|
|
138
|
-
|
|
139
|
-
test('treats a corrupt record as absent', async () => {
|
|
140
|
-
const store = Store.memory()
|
|
141
|
-
const source = ApiKeySource.store(store)
|
|
142
|
-
const { token } = await source.mint({
|
|
143
|
-
orgId: 'org_a',
|
|
144
|
-
scopes: ['data:read'],
|
|
145
|
-
})
|
|
146
|
-
// Overwrite the stored record (keyed by token hash) with invalid JSON.
|
|
147
|
-
const { keys } = await store.list({ prefix: 'apikey:' })
|
|
148
|
-
await store.put(keys[0]!.name, 'not json')
|
|
149
|
-
expect(await source.resolve(token)).toBeNull()
|
|
150
|
-
})
|
|
151
|
-
|
|
152
|
-
test('rejects a record with an unknown scope', async () => {
|
|
153
|
-
const store = Store.memory()
|
|
154
|
-
const source = ApiKeySource.store(store)
|
|
155
|
-
const { token } = await source.mint({
|
|
156
|
-
orgId: 'org_a',
|
|
157
|
-
scopes: ['data:read'],
|
|
158
|
-
})
|
|
159
|
-
const { keys } = await store.list({ prefix: 'apikey:' })
|
|
160
|
-
await store.put(
|
|
161
|
-
keys[0]!.name,
|
|
162
|
-
JSON.stringify({
|
|
163
|
-
createdAt: '2026-01-01T00:00:00.000Z',
|
|
164
|
-
id: 'key_x',
|
|
165
|
-
orgId: 'org_a',
|
|
166
|
-
scopes: ['data:read', 'totally:made-up'],
|
|
167
|
-
tokenLast4: 'xxxx',
|
|
168
|
-
}),
|
|
169
|
-
)
|
|
170
|
-
expect(await source.resolve(token)).toBeNull()
|
|
171
|
-
})
|
|
172
|
-
})
|
|
173
|
-
|
|
174
|
-
describe('cached', () => {
|
|
175
|
-
/** Wraps a source so resolve calls to the backend can be counted. */
|
|
176
|
-
function counting(source: ApiKeySource.Source) {
|
|
177
|
-
let resolves = 0
|
|
178
|
-
return {
|
|
179
|
-
resolves: () => resolves,
|
|
180
|
-
source: ApiKeySource.from({
|
|
181
|
-
...source,
|
|
182
|
-
async resolve(token) {
|
|
183
|
-
resolves++
|
|
184
|
-
return source.resolve(token)
|
|
185
|
-
},
|
|
186
|
-
}),
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
test('serves repeat resolves from cache until the TTL expires', async () => {
|
|
191
|
-
const backing = ApiKeySource.store(Store.memory())
|
|
192
|
-
const { token } = await backing.mint({ orgId: 'org_acme', scopes: ['data:read'] })
|
|
193
|
-
const { resolves, source } = counting(backing)
|
|
194
|
-
let time = 0
|
|
195
|
-
const cached = ApiKeySource.cached(source, { now: () => time })
|
|
196
|
-
|
|
197
|
-
const first = await cached.resolve(token)
|
|
198
|
-
time = 59_999
|
|
199
|
-
const second = await cached.resolve(token)
|
|
200
|
-
|
|
201
|
-
expect(resolves()).toMatchInlineSnapshot(`1`)
|
|
202
|
-
expect(second).toBe(first)
|
|
203
|
-
expect({
|
|
204
|
-
...first,
|
|
205
|
-
createdAt: '<createdAt>',
|
|
206
|
-
id: '<id>',
|
|
207
|
-
tokenLast4: '<last4>',
|
|
208
|
-
}).toMatchInlineSnapshot(`
|
|
209
|
-
{
|
|
210
|
-
"createdAt": "<createdAt>",
|
|
211
|
-
"environment": "production",
|
|
212
|
-
"id": "<id>",
|
|
213
|
-
"orgId": "org_acme",
|
|
214
|
-
"scopes": [
|
|
215
|
-
"data:read",
|
|
216
|
-
],
|
|
217
|
-
"tokenLast4": "<last4>",
|
|
218
|
-
}
|
|
219
|
-
`)
|
|
220
|
-
|
|
221
|
-
time = 60_000
|
|
222
|
-
await cached.resolve(token)
|
|
223
|
-
expect(resolves()).toMatchInlineSnapshot(`2`)
|
|
224
|
-
})
|
|
225
|
-
|
|
226
|
-
test('negative-caches unknown tokens with the shorter TTL', async () => {
|
|
227
|
-
const { resolves, source } = counting(ApiKeySource.store(Store.memory()))
|
|
228
|
-
let time = 0
|
|
229
|
-
const cached = ApiKeySource.cached(source, { now: () => time })
|
|
230
|
-
|
|
231
|
-
expect(await cached.resolve('tempo:sk:missing')).toBeNull()
|
|
232
|
-
time = 9_999
|
|
233
|
-
expect(await cached.resolve('tempo:sk:missing')).toBeNull()
|
|
234
|
-
expect(resolves()).toMatchInlineSnapshot(`1`)
|
|
235
|
-
|
|
236
|
-
time = 10_000
|
|
237
|
-
expect(await cached.resolve('tempo:sk:missing')).toBeNull()
|
|
238
|
-
expect(resolves()).toMatchInlineSnapshot(`2`)
|
|
239
|
-
})
|
|
240
|
-
|
|
241
|
-
test('single-flights concurrent misses for one token', async () => {
|
|
242
|
-
let calls = 0
|
|
243
|
-
const pending = deferred<null>()
|
|
244
|
-
const cached = ApiKeySource.cached(
|
|
245
|
-
ApiKeySource.from({
|
|
246
|
-
...ApiKeySource.noop,
|
|
247
|
-
async resolve() {
|
|
248
|
-
calls++
|
|
249
|
-
return pending.promise
|
|
250
|
-
},
|
|
251
|
-
}),
|
|
252
|
-
)
|
|
253
|
-
|
|
254
|
-
const requests = [cached.resolve('tempo:sk:a'), cached.resolve('tempo:sk:a')]
|
|
255
|
-
pending.resolve(null)
|
|
256
|
-
|
|
257
|
-
expect(await Promise.all(requests)).toMatchInlineSnapshot(`
|
|
258
|
-
[
|
|
259
|
-
null,
|
|
260
|
-
null,
|
|
261
|
-
]
|
|
262
|
-
`)
|
|
263
|
-
expect(calls).toMatchInlineSnapshot(`1`)
|
|
264
|
-
})
|
|
265
|
-
|
|
266
|
-
test('does not cache resolve failures', async () => {
|
|
267
|
-
let calls = 0
|
|
268
|
-
const cached = ApiKeySource.cached(
|
|
269
|
-
ApiKeySource.from({
|
|
270
|
-
...ApiKeySource.noop,
|
|
271
|
-
async resolve() {
|
|
272
|
-
calls++
|
|
273
|
-
if (calls === 1) throw new Error('kv unavailable')
|
|
274
|
-
return null
|
|
275
|
-
},
|
|
276
|
-
}),
|
|
277
|
-
)
|
|
278
|
-
|
|
279
|
-
await expect(cached.resolve('tempo:sk:a')).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
280
|
-
`[Error: kv unavailable]`,
|
|
281
|
-
)
|
|
282
|
-
expect(await cached.resolve('tempo:sk:a')).toBeNull()
|
|
283
|
-
expect(calls).toMatchInlineSnapshot(`2`)
|
|
284
|
-
})
|
|
285
|
-
})
|
|
286
|
-
|
|
287
|
-
function deferred<value>() {
|
|
288
|
-
let reject!: (cause?: unknown) => void
|
|
289
|
-
let resolve!: (value: PromiseLike<value> | value) => void
|
|
290
|
-
const promise = new Promise<value>((resolve_, reject_) => {
|
|
291
|
-
reject = reject_
|
|
292
|
-
resolve = resolve_
|
|
293
|
-
})
|
|
294
|
-
return { promise, reject, resolve }
|
|
295
|
-
}
|
package/src/ApiKeySource.ts
DELETED
|
@@ -1,303 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pluggable API-key sources for Tempo API.
|
|
3
|
-
*
|
|
4
|
-
* Every backend implements one shared contract ({@link Source}) covering both
|
|
5
|
-
* the read path ({@link Source.resolve}, wired into `Auth.middleware`'s
|
|
6
|
-
* `apiKey.source`) and the provisioning path ({@link Source.mint}/{@link
|
|
7
|
-
* Source.list}/{@link Source.revoke}), so backends are interchangeable wherever a
|
|
8
|
-
* source is accepted. {@link store} is the built-in store-backed source (e.g. a
|
|
9
|
-
* Cloudflare KV namespace); {@link from} type-checks a bring-your-own backend
|
|
10
|
-
* (D1, Postgres, …) against the contract. The persisted {@link Record}/{@link
|
|
11
|
-
* MintInput} shapes live here (derived from `ApiKey.schema` so they cannot drift
|
|
12
|
-
* from the resolved shape), while the shared credential primitives (token
|
|
13
|
-
* format/generation, storage-key derivation, redaction) live in `./ApiKey.js`.
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import * as z from 'zod/mini'
|
|
17
|
-
|
|
18
|
-
import * as ApiKey from './ApiKey.js'
|
|
19
|
-
import type * as Store from './internal/Store.js'
|
|
20
|
-
|
|
21
|
-
/** Zod schemas owned by the API-key source module. */
|
|
22
|
-
export namespace schema {
|
|
23
|
-
/**
|
|
24
|
-
* Fields accepted when minting a key, and the single source of truth for the
|
|
25
|
-
* {@link MintInput} type and `mint` validation. Derived from the resolved
|
|
26
|
-
* `ApiKey.schema` (minus the generated `id`) so the scope vocabulary stays in
|
|
27
|
-
* lockstep with the resolved shape.
|
|
28
|
-
*/
|
|
29
|
-
export const MintInput = z.extend(z.omit(ApiKey.schema.ApiKey, { id: true }), {
|
|
30
|
-
createdBy: z
|
|
31
|
-
.optional(z.string())
|
|
32
|
-
.check(z.describe('Identity creating the key (e.g. admin email), recorded for audit.')),
|
|
33
|
-
expiresAt: z
|
|
34
|
-
.optional(z.string())
|
|
35
|
-
.check(z.describe('ISO 8601 expiry timestamp. Omit for a non-expiring key.')),
|
|
36
|
-
// TODO: make required once orgs exist.
|
|
37
|
-
orgId: z.optional(z.string()).check(z.describe('Owning organization id.')),
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* A persisted API-key record: the resolved `ApiKey.schema` plus
|
|
42
|
-
* persistence/display fields. Extending the resolved schema keeps the resolved
|
|
43
|
-
* and persisted shapes in lockstep.
|
|
44
|
-
*/
|
|
45
|
-
export const Record = z.extend(ApiKey.schema.ApiKey, {
|
|
46
|
-
createdAt: z.string().check(z.describe('ISO 8601 creation timestamp.')),
|
|
47
|
-
createdBy: z.optional(z.string()).check(z.describe('Identity that created the key.')),
|
|
48
|
-
expiresAt: z.optional(z.string()).check(z.describe('ISO 8601 expiry timestamp.')),
|
|
49
|
-
tokenLast4: z.string().check(z.describe('Last 4 chars of the plaintext token.')),
|
|
50
|
-
})
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* A durable API-key record as persisted by a source. A superset of the resolved
|
|
55
|
-
* `ApiKey.ApiKey` with provisioning/display metadata. The plaintext token is
|
|
56
|
-
* never stored — records are keyed by its hash (`ApiKey.keyFor`) and carry only
|
|
57
|
-
* `tokenLast4` for display.
|
|
58
|
-
*/
|
|
59
|
-
export type Record = z.output<typeof schema.Record>
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Fields accepted when minting a key via {@link Source.mint}. Uses the schema's
|
|
63
|
-
* *input* type so defaulted fields (e.g. `environment`) are optional for callers
|
|
64
|
-
* — the default is applied during `mint`'s validation.
|
|
65
|
-
*/
|
|
66
|
-
export type MintInput = z.input<typeof schema.MintInput>
|
|
67
|
-
|
|
68
|
-
/** Result of {@link Source.mint}: the persisted record and its one-time token. */
|
|
69
|
-
export type MintResult = {
|
|
70
|
-
/** The persisted key record (metadata only — never the token). */
|
|
71
|
-
record: Record
|
|
72
|
-
/** The plaintext token, shown once and unrecoverable afterward. */
|
|
73
|
-
token: string
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/** Options for {@link Source.list}. */
|
|
77
|
-
export type ListOptions = {
|
|
78
|
-
/** Restrict results to keys owned by this organization. */
|
|
79
|
-
orgId?: string | undefined
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* The contract every pluggable API-key source implements — both the read path
|
|
84
|
-
* ({@link Source.resolve}, used by `Auth.middleware`'s `apiKey.source`) and the
|
|
85
|
-
* provisioning path ({@link Source.mint}/{@link Source.list}/{@link
|
|
86
|
-
* Source.revoke}). {@link store} is the built-in store-backed source; future
|
|
87
|
-
* database-backed sources (D1, Postgres, …) implement this same contract, so any
|
|
88
|
-
* backend stays interchangeable wherever a source is accepted.
|
|
89
|
-
*/
|
|
90
|
-
export type Source = {
|
|
91
|
-
/** Lists key records (metadata only — never tokens), newest first. */
|
|
92
|
-
list(options?: ListOptions): Promise<readonly Record[]>
|
|
93
|
-
/**
|
|
94
|
-
* Mints a key: persists its hashed record and returns the record plus the
|
|
95
|
-
* one-time plaintext token (shown once, unrecoverable afterward).
|
|
96
|
-
*/
|
|
97
|
-
mint(input: MintInput): Promise<MintResult>
|
|
98
|
-
/** Resolves a plaintext API key token to its metadata, or `null`. */
|
|
99
|
-
resolve(token: string): Promise<ApiKey.ApiKey | null>
|
|
100
|
-
/** Revokes a key by id. Returns whether a matching record was deleted. */
|
|
101
|
-
revoke(id: string): Promise<boolean>
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Creates a {@link Source} from a value, type-checking it against the contract.
|
|
106
|
-
* The escape hatch for bringing your own backend (D1, Postgres, …): author the
|
|
107
|
-
* implementation inline and have it validated against {@link Source}.
|
|
108
|
-
*/
|
|
109
|
-
export function from<const source extends Source>(source: source): source {
|
|
110
|
-
return source
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* The default {@link Source} when none is configured: it resolves every token to
|
|
115
|
-
* `null` and lists nothing, so API-key access stays closed until a real source
|
|
116
|
-
* (e.g. {@link store}) is wired in. Its provisioning methods are inert —
|
|
117
|
-
* {@link Source.list} returns `[]`, {@link Source.revoke} returns `false`, and
|
|
118
|
-
* {@link Source.mint} throws, since there is nowhere to persist a key.
|
|
119
|
-
*/
|
|
120
|
-
export const noop: Source = {
|
|
121
|
-
async list() {
|
|
122
|
-
return []
|
|
123
|
-
},
|
|
124
|
-
async mint() {
|
|
125
|
-
throw new Error('cannot mint: no API-key source is configured')
|
|
126
|
-
},
|
|
127
|
-
async resolve() {
|
|
128
|
-
return null
|
|
129
|
-
},
|
|
130
|
-
async revoke() {
|
|
131
|
-
return false
|
|
132
|
-
},
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Creates a store-backed {@link Source} over a {@link Store.State}. Records are
|
|
137
|
-
* persisted keyed by token hash (`ApiKey.keyFor`) and validated against the
|
|
138
|
-
* scope catalog on read; unknown, corrupt, expired, or invalid records resolve
|
|
139
|
-
* to `null`. An `id → recordKey` index makes revoke-by-id O(1).
|
|
140
|
-
*/
|
|
141
|
-
export function store(state: Store.State): Source {
|
|
142
|
-
return {
|
|
143
|
-
async list(options = {}) {
|
|
144
|
-
const { keys } = await state.list({ prefix: ApiKey.recordPrefix })
|
|
145
|
-
const records: Record[] = []
|
|
146
|
-
for (const { name } of keys) {
|
|
147
|
-
const record = await read(state, name, Date.now())
|
|
148
|
-
if (record && (!options.orgId || record.orgId === options.orgId)) records.push(record)
|
|
149
|
-
}
|
|
150
|
-
// Newest first; ids are random, so sort by creation time.
|
|
151
|
-
return records.sort((a, b) => b.createdAt.localeCompare(a.createdAt))
|
|
152
|
-
},
|
|
153
|
-
|
|
154
|
-
async mint(input) {
|
|
155
|
-
// Validate input (e.g. reject an unknown scope) at the write boundary so we
|
|
156
|
-
// never persist a record the reader would later refuse to resolve.
|
|
157
|
-
const valid = schema.MintInput.safeParse(input)
|
|
158
|
-
if (!valid.success) throw new Error(`invalid API key input: ${z.prettifyError(valid.error)}`)
|
|
159
|
-
const data = valid.data
|
|
160
|
-
|
|
161
|
-
const time = new Date()
|
|
162
|
-
const token = ApiKey.generateToken(data.environment)
|
|
163
|
-
const recordKey = ApiKey.keyFor(token)
|
|
164
|
-
const id = ApiKey.generateId()
|
|
165
|
-
const record: Record = {
|
|
166
|
-
createdAt: time.toISOString(),
|
|
167
|
-
environment: data.environment,
|
|
168
|
-
id,
|
|
169
|
-
// TODO: drop fallback once a real org concept exists.
|
|
170
|
-
orgId: data.orgId ?? id,
|
|
171
|
-
scopes: data.scopes,
|
|
172
|
-
tokenLast4: token.slice(-4),
|
|
173
|
-
...(data.createdBy === undefined ? {} : { createdBy: data.createdBy }),
|
|
174
|
-
...(data.expiresAt === undefined ? {} : { expiresAt: data.expiresAt }),
|
|
175
|
-
...(data.name === undefined ? {} : { name: data.name }),
|
|
176
|
-
...(data.rateLimits === undefined ? {} : { rateLimits: data.rateLimits }),
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// Bound the stored record by its expiry so the backend evicts it natively.
|
|
180
|
-
const ttl =
|
|
181
|
-
data.expiresAt === undefined
|
|
182
|
-
? undefined
|
|
183
|
-
: Math.max(1, Date.parse(data.expiresAt) - time.getTime())
|
|
184
|
-
await state.put(recordKey, JSON.stringify(record), { ttl })
|
|
185
|
-
// Index `id → recordKey` so revoke-by-id is O(1) (records are keyed by
|
|
186
|
-
// token hash, unrecoverable from the id alone). Shares the record's TTL so
|
|
187
|
-
// both entries expire together.
|
|
188
|
-
await state.put(idKeyFor(record.id), recordKey, { ttl })
|
|
189
|
-
return { record, token }
|
|
190
|
-
},
|
|
191
|
-
|
|
192
|
-
async resolve(token) {
|
|
193
|
-
return read(state, ApiKey.keyFor(token), Date.now())
|
|
194
|
-
},
|
|
195
|
-
|
|
196
|
-
async revoke(id) {
|
|
197
|
-
const recordKey = await state.get(idKeyFor(id))
|
|
198
|
-
if (!recordKey) return false
|
|
199
|
-
await state.delete(recordKey)
|
|
200
|
-
await state.delete(idKeyFor(id))
|
|
201
|
-
return true
|
|
202
|
-
},
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Wraps a source with a short-TTL in-memory cache on the resolve path, so hot
|
|
208
|
-
* keys skip the backend read (e.g. a KV `get`) on every request. Positive
|
|
209
|
-
* results cache for `ttl` (default 60s), negative for `negativeTtl` (default
|
|
210
|
-
* 10s) so a freshly minted key is usable quickly; concurrent misses for one
|
|
211
|
-
* token are single-flighted. Entries are keyed by the token's storage-key
|
|
212
|
-
* derivation (`ApiKey.keyFor`, a sha256) — never the plaintext token.
|
|
213
|
-
*
|
|
214
|
-
* The cache is per-isolate, so a revoked key may stay resolvable elsewhere
|
|
215
|
-
* for up to `ttl` — the accepted trade-off for removing a backend read from
|
|
216
|
-
* the auth path of every request. Provisioning methods pass through uncached.
|
|
217
|
-
*/
|
|
218
|
-
export function cached(source: Source, options: cached.Options = {}): Source {
|
|
219
|
-
const { negativeTtl = 10_000, now = Date.now, ttl = 60_000 } = options
|
|
220
|
-
type Cell = { expiresAt: number; value: ApiKey.ApiKey | null }
|
|
221
|
-
const cells = new Map<string, Cell>()
|
|
222
|
-
const flights = new Map<string, Promise<ApiKey.ApiKey | null>>()
|
|
223
|
-
|
|
224
|
-
return {
|
|
225
|
-
...source,
|
|
226
|
-
async resolve(token) {
|
|
227
|
-
// Versioned so a future record-shape change can't serve stale shapes.
|
|
228
|
-
const key = `v1:${ApiKey.keyFor(token)}`
|
|
229
|
-
const cell = cells.get(key)
|
|
230
|
-
if (cell && cell.expiresAt > now()) return cell.value
|
|
231
|
-
if (cell) cells.delete(key)
|
|
232
|
-
|
|
233
|
-
const existing = flights.get(key)
|
|
234
|
-
if (existing) return existing
|
|
235
|
-
|
|
236
|
-
const flight = (async () => {
|
|
237
|
-
const value = await source.resolve(token)
|
|
238
|
-
// Bound the cache: random invalid tokens each add a (negative) entry,
|
|
239
|
-
// so sweep expired cells at the cap and then drop the oldest.
|
|
240
|
-
if (cells.size >= cachedMaxEntries) {
|
|
241
|
-
const time = now()
|
|
242
|
-
for (const [name, entry] of cells) if (entry.expiresAt <= time) cells.delete(name)
|
|
243
|
-
while (cells.size >= cachedMaxEntries) cells.delete(cells.keys().next().value as string)
|
|
244
|
-
}
|
|
245
|
-
cells.set(key, { expiresAt: now() + (value ? ttl : negativeTtl), value })
|
|
246
|
-
return value
|
|
247
|
-
})()
|
|
248
|
-
flights.set(key, flight)
|
|
249
|
-
try {
|
|
250
|
-
return await flight
|
|
251
|
-
} finally {
|
|
252
|
-
flights.delete(key)
|
|
253
|
-
}
|
|
254
|
-
},
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
export declare namespace cached {
|
|
259
|
-
/** Options for wrapping a source with a short-TTL resolve cache. */
|
|
260
|
-
type Options = {
|
|
261
|
-
/** Time-to-live for negative (`null`) results in milliseconds. */
|
|
262
|
-
negativeTtl?: number | undefined
|
|
263
|
-
/** Clock used for expiry. */
|
|
264
|
-
now?: (() => number) | undefined
|
|
265
|
-
/** Time-to-live for positive results in milliseconds. */
|
|
266
|
-
ttl?: number | undefined
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/** Maximum resolve-cache entries held per isolate before eviction. */
|
|
271
|
-
const cachedMaxEntries = 10_000
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* Reads and validates a stored record by storage key. Invalid, corrupt, or
|
|
275
|
-
* expired records (`expiresAt <= now`) resolve to `null`. Expiry is enforced
|
|
276
|
-
* here — not just via the store's native TTL — so it holds on backends that
|
|
277
|
-
* evict lazily and regardless of TTL granularity.
|
|
278
|
-
*/
|
|
279
|
-
async function read(state: Store.State, key: string, now: number): Promise<Record | null> {
|
|
280
|
-
const raw = await state.get(key)
|
|
281
|
-
if (!raw) return null
|
|
282
|
-
const value = (() => {
|
|
283
|
-
try {
|
|
284
|
-
return JSON.parse(raw) as unknown
|
|
285
|
-
} catch {
|
|
286
|
-
return undefined
|
|
287
|
-
}
|
|
288
|
-
})()
|
|
289
|
-
if (value === undefined) return null
|
|
290
|
-
const parsed = schema.Record.safeParse(value)
|
|
291
|
-
if (!parsed.success) return null
|
|
292
|
-
const record = parsed.data
|
|
293
|
-
if (record.expiresAt !== undefined && Date.parse(record.expiresAt) <= now) return null
|
|
294
|
-
return record
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
/** Storage-key prefix for the store source's `id → recordKey` index. */
|
|
298
|
-
const idPrefix = 'apikey_id:'
|
|
299
|
-
|
|
300
|
-
/** Storage key under which the store source's `id → recordKey` index entry is persisted. */
|
|
301
|
-
function idKeyFor(id: string): string {
|
|
302
|
-
return `${idPrefix}${id}`
|
|
303
|
-
}
|
|
File without changes
|
|
File without changes
|