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
|
@@ -90,11 +90,12 @@ describe('Cursor', () => {
|
|
|
90
90
|
})
|
|
91
91
|
|
|
92
92
|
test('falls back to an OR expansion for a mixed-direction keyset', () => {
|
|
93
|
+
const holder = `'0x${'ab'.repeat(20)}'`
|
|
93
94
|
expect(
|
|
94
95
|
Cursor.keyset([
|
|
95
96
|
{ literal: '1000', name: 'balance', order: 'desc' },
|
|
96
|
-
{ literal:
|
|
97
|
+
{ literal: holder, name: 'holder', order: 'asc' },
|
|
97
98
|
]),
|
|
98
|
-
).toBe(
|
|
99
|
+
).toBe(`(balance <= 1000 AND ((balance < 1000) OR (balance = 1000 AND holder > ${holder})))`)
|
|
99
100
|
})
|
|
100
101
|
})
|
package/src/internal/Log.test.ts
CHANGED
|
@@ -92,7 +92,7 @@ describe('middleware', () => {
|
|
|
92
92
|
api.use('*', async (c, next) => {
|
|
93
93
|
c.set('principal', {
|
|
94
94
|
id: 'ip:203.0.113.7',
|
|
95
|
-
payment: { payer: '
|
|
95
|
+
payment: { payer: `0x${'aa'.repeat(20)}`, reason: 'public_over_quota', type: 'mpp' },
|
|
96
96
|
type: 'public',
|
|
97
97
|
})
|
|
98
98
|
await next()
|
package/src/internal/Log.ts
CHANGED
|
@@ -42,17 +42,21 @@ export type Entry = {
|
|
|
42
42
|
path: string
|
|
43
43
|
/** MPP payment reason when the request was paid per call. */
|
|
44
44
|
payment?: Auth.Payment['reason'] | undefined
|
|
45
|
-
/** Caller identity. Raw API-key tokens are never logged. */
|
|
45
|
+
/** Caller identity. Raw API-key tokens and the super admin secret are never logged. */
|
|
46
46
|
principal?:
|
|
47
47
|
| {
|
|
48
|
+
/** Attributed super admin identity for super admin callers. */
|
|
49
|
+
actor?: string | undefined
|
|
48
50
|
/** Key environment for API-key callers. */
|
|
49
51
|
environment?: ApiKey.ApiKey['environment'] | undefined
|
|
50
|
-
/** Principal id: the API key id,
|
|
52
|
+
/** Principal id: the API key id, the public quota identity, the user id, or `super_admin`. */
|
|
51
53
|
id: string
|
|
52
54
|
/** Owning organization id for API-key callers. */
|
|
53
55
|
orgId?: string | undefined
|
|
56
|
+
/** Owning project id for project-attributed API-key callers. */
|
|
57
|
+
projectId?: string | undefined
|
|
54
58
|
/** Principal kind. */
|
|
55
|
-
type: 'api_key' | 'public'
|
|
59
|
+
type: 'api_key' | 'public' | 'session' | 'super_admin'
|
|
56
60
|
}
|
|
57
61
|
| undefined
|
|
58
62
|
/** Raw query string (no leading `?`) with API-key tokens redacted, when present. */
|
|
@@ -146,9 +150,13 @@ export function middleware<environment extends Environment = Environment>(
|
|
|
146
150
|
? {}
|
|
147
151
|
: {
|
|
148
152
|
principal: {
|
|
153
|
+
...(principal.type === 'super_admin' ? { actor: principal.actor } : {}),
|
|
149
154
|
...(principal.type === 'api_key' ? { environment: principal.environment } : {}),
|
|
150
155
|
id: principal.id,
|
|
151
156
|
...(principal.type === 'api_key' ? { orgId: principal.orgId } : {}),
|
|
157
|
+
...(principal.type === 'api_key' && principal.projectId !== undefined
|
|
158
|
+
? { projectId: principal.projectId }
|
|
159
|
+
: {}),
|
|
152
160
|
type: principal.type,
|
|
153
161
|
},
|
|
154
162
|
}),
|
|
@@ -60,7 +60,7 @@ type RouteTags = {
|
|
|
60
60
|
cache: 'hit' | 'miss'
|
|
61
61
|
method: string
|
|
62
62
|
principal_environment: 'none' | 'production' | 'sandbox'
|
|
63
|
-
principal_type: 'api_key' | 'public' | 'unknown'
|
|
63
|
+
principal_type: 'api_key' | 'public' | 'session' | 'super_admin' | 'unknown'
|
|
64
64
|
route: string
|
|
65
65
|
}
|
|
66
66
|
|
package/src/internal/OpenApi.ts
CHANGED
|
@@ -2,7 +2,6 @@ import type { Context, Env, MiddlewareHandler, ValidationTargets } from 'hono'
|
|
|
2
2
|
import type { HasRequiredKeys } from 'hono/utils/types'
|
|
3
3
|
import { resolver, type ResponsesWithResolver, validator } from 'hono-openapi'
|
|
4
4
|
import * as z from 'zod/mini'
|
|
5
|
-
import { pilatFontFaces } from './OpenApi.fonts.js'
|
|
6
5
|
import * as Response from './Response.js'
|
|
7
6
|
import * as Schema from './Schema.js'
|
|
8
7
|
|
|
@@ -50,64 +49,6 @@ export {
|
|
|
50
49
|
validator,
|
|
51
50
|
} from 'hono-openapi'
|
|
52
51
|
|
|
53
|
-
// Theme-aware Tempo API wordmark for the docs header (`vocs.logoUrl`), inlined
|
|
54
|
-
// as SVG data URIs. See {@link file://./OpenApi.logo.ts}.
|
|
55
|
-
export { logoUrl } from './OpenApi.logo.js'
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Custom CSS injected into the standalone reference's shell `<head>` (via
|
|
59
|
-
* `Handler.openApi(_, { css })`), after the Vocs design-system styles so it
|
|
60
|
-
* overrides them:
|
|
61
|
-
*
|
|
62
|
-
* - Page/surface backgrounds use Tempo's near-black dark palette and pure-white
|
|
63
|
-
* light palette via `light-dark(<light>, <dark>)`.
|
|
64
|
-
* - The dark border color is darkened (the light theme keeps its default).
|
|
65
|
-
* - Every corner is squared. The universal `!important` rule beats hard-coded
|
|
66
|
-
* `border-radius` values and inline styles (e.g. the Scalar playground), which
|
|
67
|
-
* overriding the radius scale variables alone would miss.
|
|
68
|
-
* - The default sans/body font is Pilat (Vocs reads `--vocs-font-family`); the
|
|
69
|
-
* webfont is embedded via {@link pilatFontFaces}. Body stays at the default
|
|
70
|
-
* weight (400); headings are pinned to 500, which renders Pilat Demi (the
|
|
71
|
-
* family has no 500 cut, so Demi covers the 500-600 range — see
|
|
72
|
-
* {@link pilatFontFaces}).
|
|
73
|
-
*/
|
|
74
|
-
export const css = `${pilatFontFaces}
|
|
75
|
-
|
|
76
|
-
:root {
|
|
77
|
-
--vocs-font-family: "Pilat", Arial, Helvetica, sans-serif;
|
|
78
|
-
--vocs-background-color-primary: light-dark(#fafafa, #0a0a0a);
|
|
79
|
-
--vocs-background-color-surface: light-dark(#fff, #121212);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
:is(h1, h2, h3, h4, h5, h6)[data-v] {
|
|
83
|
-
font-weight: 500;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/* Nudge the header logo a touch larger than the default max-h-7 (1.75rem). */
|
|
87
|
-
[data-v-logo-image] {
|
|
88
|
-
max-height: 2rem;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/* Sidebar leaf items render Regular (the nav's 450 default otherwise resolves to
|
|
92
|
-
* Demi via the 500-600 range). Group labels ([data-v-sidebar-section-header])
|
|
93
|
-
* keep their heavier weight. */
|
|
94
|
-
[data-v-sidebar-item] {
|
|
95
|
-
font-weight: 400;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
[data-vocs-theme='dark'] {
|
|
99
|
-
--vocs-color-gray4: oklch(20% 0 0);
|
|
100
|
-
--vocs-color-gray5: oklch(0.23 0 0);
|
|
101
|
-
--vocs-border-color-primary: oklch(0.25 0 0);
|
|
102
|
-
--vocs-border-color-secondary: oklch(0.25 0 0);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
*,
|
|
106
|
-
*::before,
|
|
107
|
-
*::after {
|
|
108
|
-
border-radius: 0 !important;
|
|
109
|
-
}`
|
|
110
|
-
|
|
111
52
|
/**
|
|
112
53
|
* Standard machine-readable `error.code` values per status, shared API-wide.
|
|
113
54
|
* The reference enumerates `error.code` to these so generated clients narrow it
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { RpcSchema } from 'ox'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Ox-compatible JSON-RPC schema union used to type the relay handler's incoming
|
|
5
|
+
* requests (`RpcRequest.RpcRequest<Ox>`). The relay switches on `request.method`
|
|
6
|
+
* string literals and casts params per method, so a permissive schema whose
|
|
7
|
+
* `params` is an array is sufficient — the full method-by-method schema union
|
|
8
|
+
* from the accounts SDK is not needed here.
|
|
9
|
+
*/
|
|
10
|
+
export type Ox = RpcSchema.Generic<string, readonly unknown[]>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Store } from '
|
|
1
|
+
import { Store } from 'tapimo'
|
|
2
2
|
|
|
3
3
|
describe('from', () => {
|
|
4
4
|
const ops = {
|
|
@@ -68,7 +68,89 @@ describe('increment', () => {
|
|
|
68
68
|
})
|
|
69
69
|
})
|
|
70
70
|
|
|
71
|
+
describe('update', () => {
|
|
72
|
+
test('falls back to a get + put read-modify-write without a native swap', async () => {
|
|
73
|
+
const cells = new Map<string, string>()
|
|
74
|
+
const store = Store.from({
|
|
75
|
+
async delete(key) {
|
|
76
|
+
cells.delete(key)
|
|
77
|
+
},
|
|
78
|
+
async get(key) {
|
|
79
|
+
return cells.get(key) ?? null
|
|
80
|
+
},
|
|
81
|
+
async list() {
|
|
82
|
+
return { keys: [] }
|
|
83
|
+
},
|
|
84
|
+
async put(key, value) {
|
|
85
|
+
cells.set(key, value)
|
|
86
|
+
},
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
expect(await Store.update(store, 'value', (current) => `${current ?? 'start'}+`)).toMatchInlineSnapshot(`"start+"`) // prettier-ignore
|
|
90
|
+
expect(await Store.update(store, 'value', (current) => `${current}+`)).toMatchInlineSnapshot(`"start++"`) // prettier-ignore
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
test('retries the transform when the compare-and-swap loses', async () => {
|
|
94
|
+
const store = Store.memory()
|
|
95
|
+
await store.put('value', 'a')
|
|
96
|
+
|
|
97
|
+
// Interleave a competing write between the read and the swap once.
|
|
98
|
+
let raced = false
|
|
99
|
+
const get = store.get.bind(store)
|
|
100
|
+
store.get = async (key) => {
|
|
101
|
+
const current = await get(key)
|
|
102
|
+
if (!raced) {
|
|
103
|
+
raced = true
|
|
104
|
+
await store.put('value', 'b')
|
|
105
|
+
}
|
|
106
|
+
return current
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const transforms: (null | string)[] = []
|
|
110
|
+
const written = await Store.update(store, 'value', (current) => {
|
|
111
|
+
transforms.push(current)
|
|
112
|
+
return `${current}c`
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
expect(written).toMatchInlineSnapshot(`"bc"`)
|
|
116
|
+
expect(transforms).toMatchInlineSnapshot(`
|
|
117
|
+
[
|
|
118
|
+
"a",
|
|
119
|
+
"b",
|
|
120
|
+
]
|
|
121
|
+
`)
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
test('fails after exhausting compare-and-swap attempts', async () => {
|
|
125
|
+
const store = Store.memory()
|
|
126
|
+
await store.put('value', 'a')
|
|
127
|
+
|
|
128
|
+
// Every read is immediately invalidated by a competing write.
|
|
129
|
+
const get = store.get.bind(store)
|
|
130
|
+
store.get = async (key) => {
|
|
131
|
+
const current = await get(key)
|
|
132
|
+
await store.put('value', `${current}!`)
|
|
133
|
+
return current
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
await expect(
|
|
137
|
+
Store.update(store, 'value', (current) => `${current}c`, { attempts: 2 }),
|
|
138
|
+
).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
139
|
+
`[Store.UpdateContentionError: Store update on "value" lost 2 compare-and-swap attempts.]`,
|
|
140
|
+
)
|
|
141
|
+
})
|
|
142
|
+
})
|
|
143
|
+
|
|
71
144
|
describe('memory', () => {
|
|
145
|
+
test('swaps atomically against the expected value', async () => {
|
|
146
|
+
const store = Store.memory()
|
|
147
|
+
|
|
148
|
+
expect(await store.swap!('value', null, 'first')).toMatchInlineSnapshot(`true`)
|
|
149
|
+
expect(await store.swap!('value', null, 'clobber')).toMatchInlineSnapshot(`false`)
|
|
150
|
+
expect(await store.swap!('value', 'first', 'second')).toMatchInlineSnapshot(`true`)
|
|
151
|
+
expect(await store.get('value')).toMatchInlineSnapshot(`"second"`)
|
|
152
|
+
})
|
|
153
|
+
|
|
72
154
|
test('treats expired entries as absent', async () => {
|
|
73
155
|
const store = Store.memory()
|
|
74
156
|
|
|
@@ -148,6 +230,8 @@ describe('cloudflareKv', () => {
|
|
|
148
230
|
.filter((key) => !options.prefix || key.startsWith(options.prefix))
|
|
149
231
|
.sort()
|
|
150
232
|
.map((name) => ({ name })),
|
|
233
|
+
list_complete: true,
|
|
234
|
+
cursor: '',
|
|
151
235
|
}
|
|
152
236
|
},
|
|
153
237
|
async put(key, value, options) {
|
|
@@ -190,9 +274,69 @@ describe('cloudflareKv', () => {
|
|
|
190
274
|
`[RangeError: Cloudflare KV expirationTtl must be at least 60 seconds]`,
|
|
191
275
|
)
|
|
192
276
|
})
|
|
277
|
+
|
|
278
|
+
test('lists every Cloudflare Workers KV page', async () => {
|
|
279
|
+
const calls: unknown[] = []
|
|
280
|
+
const namespace = {
|
|
281
|
+
async delete() {},
|
|
282
|
+
async get() {
|
|
283
|
+
return null
|
|
284
|
+
},
|
|
285
|
+
async list(options = {}) {
|
|
286
|
+
calls.push(options)
|
|
287
|
+
if (options.cursor === 'page-2')
|
|
288
|
+
return {
|
|
289
|
+
keys: [{ name: 'secret:2' }],
|
|
290
|
+
list_complete: true,
|
|
291
|
+
cursor: '',
|
|
292
|
+
}
|
|
293
|
+
return {
|
|
294
|
+
keys: [{ name: 'secret:1' }],
|
|
295
|
+
list_complete: false,
|
|
296
|
+
cursor: 'page-2',
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
async put() {},
|
|
300
|
+
} satisfies Store.cloudflareKv.Namespace
|
|
301
|
+
const store = Store.cloudflareKv(namespace)
|
|
302
|
+
|
|
303
|
+
expect(await store.list({ prefix: 'secret:' })).toMatchInlineSnapshot(`
|
|
304
|
+
{
|
|
305
|
+
"keys": [
|
|
306
|
+
{
|
|
307
|
+
"name": "secret:1",
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
"name": "secret:2",
|
|
311
|
+
},
|
|
312
|
+
],
|
|
313
|
+
}
|
|
314
|
+
`)
|
|
315
|
+
expect(calls).toMatchInlineSnapshot(`
|
|
316
|
+
[
|
|
317
|
+
{
|
|
318
|
+
"prefix": "secret:",
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
"cursor": "page-2",
|
|
322
|
+
"prefix": "secret:",
|
|
323
|
+
},
|
|
324
|
+
]
|
|
325
|
+
`)
|
|
326
|
+
})
|
|
193
327
|
})
|
|
194
328
|
|
|
195
329
|
describe('durableObject', () => {
|
|
330
|
+
// The stub contract requires native `increment` and `swap` (our DO class
|
|
331
|
+
// implements them); derive them for the in-memory fakes.
|
|
332
|
+
function stubFor(backing: Store.State): Store.durableObject.Stub {
|
|
333
|
+
return {
|
|
334
|
+
...backing,
|
|
335
|
+
increment: (key, options) => Store.increment(backing, key, options),
|
|
336
|
+
swap: (key, expected, next, options) => backing.swap!(key, expected, next, options),
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
196
340
|
test('adapts a Durable Object namespace without caching stubs', async () => {
|
|
197
341
|
const calls: string[] = []
|
|
198
342
|
const backing = Store.memory({
|
|
@@ -201,12 +345,7 @@ describe('durableObject', () => {
|
|
|
201
345
|
const namespace = {
|
|
202
346
|
getByName(name) {
|
|
203
347
|
calls.push(name)
|
|
204
|
-
|
|
205
|
-
// implements it); derive one for the in-memory fake.
|
|
206
|
-
return {
|
|
207
|
-
...backing,
|
|
208
|
-
increment: (key, options) => Store.increment(backing, key, options),
|
|
209
|
-
}
|
|
348
|
+
return stubFor(backing)
|
|
210
349
|
},
|
|
211
350
|
} satisfies Store.durableObject.Namespace
|
|
212
351
|
const store = Store.durableObject(namespace, { name: 'tempo-api' })
|
|
@@ -250,6 +389,7 @@ describe('durableObject', () => {
|
|
|
250
389
|
increment: (key, options) => Store.increment(backing, key, options),
|
|
251
390
|
list: (options) => backing.list(options),
|
|
252
391
|
put: (key, value, options) => backing.put(key, value, options),
|
|
392
|
+
swap: (key, expected, next, options) => backing.swap!(key, expected, next, options),
|
|
253
393
|
} satisfies Store.durableObject.Stub
|
|
254
394
|
return new Proxy(stub, {
|
|
255
395
|
get(target, method: string) {
|
|
@@ -294,7 +434,7 @@ describe('durableObject', () => {
|
|
|
294
434
|
getByName() {
|
|
295
435
|
stubs += 1
|
|
296
436
|
return {
|
|
297
|
-
...backing,
|
|
437
|
+
...stubFor(backing),
|
|
298
438
|
increment: (key, options) => {
|
|
299
439
|
if (failures > 0) {
|
|
300
440
|
failures -= 1
|
|
@@ -317,7 +457,7 @@ describe('durableObject', () => {
|
|
|
317
457
|
const namespace = {
|
|
318
458
|
getByName() {
|
|
319
459
|
return {
|
|
320
|
-
...Store.memory(),
|
|
460
|
+
...stubFor(Store.memory()),
|
|
321
461
|
increment: () => {
|
|
322
462
|
attempts += 1
|
|
323
463
|
throw Object.assign(new Error('Network connection lost.'), { retryable: true })
|
|
@@ -339,7 +479,7 @@ describe('durableObject', () => {
|
|
|
339
479
|
({
|
|
340
480
|
getByName() {
|
|
341
481
|
return {
|
|
342
|
-
...Store.memory(),
|
|
482
|
+
...stubFor(Store.memory()),
|
|
343
483
|
increment: () => {
|
|
344
484
|
attempts += 1
|
|
345
485
|
throw error
|
package/src/internal/Store.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Json } from 'ox'
|
|
2
|
+
|
|
1
3
|
/** Minimal string store contract used by Tempo API internals. */
|
|
2
4
|
export type Store = {
|
|
3
5
|
/** Deletes a key. */
|
|
@@ -26,6 +28,22 @@ export type Store = {
|
|
|
26
28
|
}>
|
|
27
29
|
/** Writes a string value, optionally with a time-to-live. */
|
|
28
30
|
put(key: string, value: string, options?: Store.PutOptions): Promise<void>
|
|
31
|
+
/**
|
|
32
|
+
* Optional atomic compare-and-swap: writes `next` only when the current
|
|
33
|
+
* value equals `expected` (`null` = only-if-absent) and reports whether the
|
|
34
|
+
* write happened. Implement when the backend can execute the compare and
|
|
35
|
+
* write in one place (in-memory, inside a Durable Object); consumers go
|
|
36
|
+
* through the {@link update} helper, which falls back to a get + put when
|
|
37
|
+
* this is absent.
|
|
38
|
+
*/
|
|
39
|
+
swap?:
|
|
40
|
+
| ((
|
|
41
|
+
key: string,
|
|
42
|
+
expected: null | string,
|
|
43
|
+
next: string,
|
|
44
|
+
options?: Store.PutOptions,
|
|
45
|
+
) => Promise<boolean>)
|
|
46
|
+
| undefined
|
|
29
47
|
/**
|
|
30
48
|
* Store kind discriminant. `'state'` marks an authoritative, enumerable
|
|
31
49
|
* store ({@link State}); `'cache'` marks a cache (e.g. the Web Cache,
|
|
@@ -102,6 +120,50 @@ export async function increment(
|
|
|
102
120
|
return count
|
|
103
121
|
}
|
|
104
122
|
|
|
123
|
+
/**
|
|
124
|
+
* Atomically transforms a value on a store: reads the current value, applies
|
|
125
|
+
* `fn`, and writes the result, returning the written value.
|
|
126
|
+
*
|
|
127
|
+
* Uses the store's native {@link Store.swap} compare-and-swap when implemented,
|
|
128
|
+
* re-running `fn` on contention (optimistic concurrency, bounded by
|
|
129
|
+
* `attempts`). Otherwise falls back to a get + put read-modify-write, which
|
|
130
|
+
* can lose concurrent writes — fine for dev/in-process backends, not for
|
|
131
|
+
* contended distributed state.
|
|
132
|
+
*/
|
|
133
|
+
export async function update(
|
|
134
|
+
store: Store,
|
|
135
|
+
key: string,
|
|
136
|
+
fn: (current: null | string) => string,
|
|
137
|
+
options: update.Options = {},
|
|
138
|
+
): Promise<string> {
|
|
139
|
+
const { attempts = 8, ...put } = options
|
|
140
|
+
|
|
141
|
+
if (!store.swap) {
|
|
142
|
+
const next = fn(await store.get(key))
|
|
143
|
+
await store.put(key, next, put)
|
|
144
|
+
return next
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
for (let attempt = 0; attempt < attempts; attempt++) {
|
|
148
|
+
const current = await store.get(key)
|
|
149
|
+
const next = fn(current)
|
|
150
|
+
if (await store.swap(key, current, next, put)) return next
|
|
151
|
+
}
|
|
152
|
+
throw new UpdateContentionError(key, attempts)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export declare namespace update {
|
|
156
|
+
/** Options for {@link update}. */
|
|
157
|
+
type Options = Store.PutOptions & {
|
|
158
|
+
/**
|
|
159
|
+
* Maximum compare-and-swap attempts before failing with
|
|
160
|
+
* {@link UpdateContentionError}.
|
|
161
|
+
* @default 8
|
|
162
|
+
*/
|
|
163
|
+
attempts?: number | undefined
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
105
167
|
/** Creates an in-memory store with TTL support. */
|
|
106
168
|
export function memory(options: memory.Options = {}): State {
|
|
107
169
|
// Track value + optional expiration in one cell so `get` can lazily evict
|
|
@@ -148,6 +210,14 @@ export function memory(options: memory.Options = {}): State {
|
|
|
148
210
|
const expiresAt = options.ttl === undefined ? undefined : Date.now() + options.ttl
|
|
149
211
|
cells.set(key, { expiresAt, value })
|
|
150
212
|
},
|
|
213
|
+
// Native swap: the compare and write are synchronous, so concurrent
|
|
214
|
+
// consumers cannot interleave between them.
|
|
215
|
+
async swap(key, expected, next, options = {}) {
|
|
216
|
+
if ((read(key)?.value ?? null) !== expected) return false
|
|
217
|
+
const expiresAt = options.ttl === undefined ? undefined : Date.now() + options.ttl
|
|
218
|
+
cells.set(key, { expiresAt, value: next })
|
|
219
|
+
return true
|
|
220
|
+
},
|
|
151
221
|
})
|
|
152
222
|
}
|
|
153
223
|
|
|
@@ -211,10 +281,23 @@ export function cloudflareKv(namespace: cloudflareKv.Namespace): State {
|
|
|
211
281
|
return namespace.get(key)
|
|
212
282
|
},
|
|
213
283
|
async list(options = {}) {
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
284
|
+
const keys: { name: string }[] = []
|
|
285
|
+
let cursor: string | undefined
|
|
286
|
+
|
|
287
|
+
for (;;) {
|
|
288
|
+
const result = await namespace.list(
|
|
289
|
+
options.prefix === undefined && cursor === undefined
|
|
290
|
+
? undefined
|
|
291
|
+
: {
|
|
292
|
+
...(options.prefix === undefined ? {} : { prefix: options.prefix }),
|
|
293
|
+
...(cursor === undefined ? {} : { cursor }),
|
|
294
|
+
},
|
|
295
|
+
)
|
|
296
|
+
keys.push(...result.keys.map(({ name }) => ({ name })))
|
|
297
|
+
if (result.list_complete) return { keys }
|
|
298
|
+
if (result.cursor === undefined) throw new Error('Cloudflare KV list is missing a cursor.')
|
|
299
|
+
cursor = result.cursor
|
|
300
|
+
}
|
|
218
301
|
},
|
|
219
302
|
async put(key, value, options = {}) {
|
|
220
303
|
const expirationTtl = cloudflareKvExpirationTtl(options.ttl)
|
|
@@ -238,13 +321,19 @@ export declare namespace cloudflareKv {
|
|
|
238
321
|
/** Lists keys. */
|
|
239
322
|
list(options?: {
|
|
240
323
|
/** Restrict results to keys with this prefix. */
|
|
241
|
-
prefix?: string |
|
|
324
|
+
prefix?: string | undefined
|
|
325
|
+
/** Continue from a previous page. */
|
|
326
|
+
cursor?: string | undefined
|
|
242
327
|
}): Promise<{
|
|
243
328
|
/** Matching keys. */
|
|
244
329
|
keys: readonly {
|
|
245
330
|
/** Key name. */
|
|
246
331
|
name: string
|
|
247
332
|
}[]
|
|
333
|
+
/** Whether every matching key was returned. */
|
|
334
|
+
list_complete: boolean
|
|
335
|
+
/** Cursor for the next page. */
|
|
336
|
+
cursor?: string | undefined
|
|
248
337
|
}>
|
|
249
338
|
/** Writes a text value. */
|
|
250
339
|
put(
|
|
@@ -302,6 +391,19 @@ function durableObjectStorage(storage: durableObject.Storage): State {
|
|
|
302
391
|
|
|
303
392
|
await storage.put(key, durableObjectValue(value, options))
|
|
304
393
|
},
|
|
394
|
+
// Native swap: the object is single-threaded and its input gate holds
|
|
395
|
+
// other events while storage operations are in flight, so the compare and
|
|
396
|
+
// write cannot interleave with another request.
|
|
397
|
+
async swap(key, expected, next, options = {}) {
|
|
398
|
+
const current = (await durableObjectRead(storage, key, await storage.get(key)))?.value ?? null
|
|
399
|
+
if (current !== expected) return false
|
|
400
|
+
if (options.ttl !== undefined && options.ttl <= 0) {
|
|
401
|
+
await storage.delete(key)
|
|
402
|
+
return true
|
|
403
|
+
}
|
|
404
|
+
await storage.put(key, durableObjectValue(next, options))
|
|
405
|
+
return true
|
|
406
|
+
},
|
|
305
407
|
})
|
|
306
408
|
}
|
|
307
409
|
|
|
@@ -341,12 +443,20 @@ export declare namespace durableObject {
|
|
|
341
443
|
/**
|
|
342
444
|
* Minimal Durable Object RPC stub shape used by this adapter — just the store
|
|
343
445
|
* operations (no `type` discriminant, which an RPC stub doesn't carry).
|
|
344
|
-
* `increment`
|
|
345
|
-
* `DurableObject` class, whose RPC surface implements
|
|
446
|
+
* `increment` and `swap` are required: the stub mirrors the
|
|
447
|
+
* `tapimo/cloudflare` `DurableObject` class, whose RPC surface implements
|
|
448
|
+
* them.
|
|
346
449
|
*/
|
|
347
|
-
type Stub = Omit<Store, 'increment' | 'type'> & {
|
|
450
|
+
type Stub = Omit<Store, 'increment' | 'swap' | 'type'> & {
|
|
348
451
|
/** Atomically increments an integer counter inside the object. */
|
|
349
452
|
increment(key: string, options?: Store.PutOptions): Promise<number>
|
|
453
|
+
/** Atomically compares-and-swaps a value inside the object. */
|
|
454
|
+
swap(
|
|
455
|
+
key: string,
|
|
456
|
+
expected: null | string,
|
|
457
|
+
next: string,
|
|
458
|
+
options?: Store.PutOptions,
|
|
459
|
+
): Promise<boolean>
|
|
350
460
|
}
|
|
351
461
|
}
|
|
352
462
|
|
|
@@ -364,7 +474,7 @@ export async function memoize<value>(
|
|
|
364
474
|
const raw = await store.get(key)
|
|
365
475
|
if (raw) {
|
|
366
476
|
try {
|
|
367
|
-
return
|
|
477
|
+
return Json.parse(raw) as value
|
|
368
478
|
} catch {
|
|
369
479
|
// Corrupt entries are treated as misses so the next call refreshes.
|
|
370
480
|
}
|
|
@@ -379,7 +489,7 @@ export async function memoize<value>(
|
|
|
379
489
|
// fetch is still in flight.
|
|
380
490
|
const flight = (async () => {
|
|
381
491
|
const value = await fetch()
|
|
382
|
-
await store.put(key,
|
|
492
|
+
await store.put(key, Json.stringify(value), { ttl })
|
|
383
493
|
return value
|
|
384
494
|
})()
|
|
385
495
|
flights.set(key, flight)
|
|
@@ -450,6 +560,12 @@ function durableObjectStub(
|
|
|
450
560
|
async put(key, value, options) {
|
|
451
561
|
await durableObjectRetry(() => stub(key).put(key, value, options))
|
|
452
562
|
},
|
|
563
|
+
// Native RPC swap: the compare-and-write runs inside the object. A lost
|
|
564
|
+
// response retried after a successful write compares stale and returns
|
|
565
|
+
// false, which optimistic callers already handle as contention.
|
|
566
|
+
async swap(key, expected, next, options) {
|
|
567
|
+
return durableObjectRetry(() => stub(key).swap(key, expected, next, options))
|
|
568
|
+
},
|
|
453
569
|
})
|
|
454
570
|
}
|
|
455
571
|
|
|
@@ -482,7 +598,7 @@ function cloudflareKvExpirationTtl(ttl: number | undefined) {
|
|
|
482
598
|
return seconds
|
|
483
599
|
}
|
|
484
600
|
|
|
485
|
-
const durableObjectValueType = '
|
|
601
|
+
const durableObjectValueType = 'tempo-api:store:v1'
|
|
486
602
|
|
|
487
603
|
type DurableObjectValue = {
|
|
488
604
|
expiresAt?: number | undefined
|
|
@@ -524,3 +640,12 @@ function isDurableObjectValue(value: unknown): value is DurableObjectValue {
|
|
|
524
640
|
typeof value.expiresAt === 'number')
|
|
525
641
|
)
|
|
526
642
|
}
|
|
643
|
+
|
|
644
|
+
/** Thrown when {@link update} exhausts its compare-and-swap attempts. */
|
|
645
|
+
export class UpdateContentionError extends Error {
|
|
646
|
+
override name = 'Store.UpdateContentionError'
|
|
647
|
+
|
|
648
|
+
constructor(key: string, attempts: number) {
|
|
649
|
+
super(`Store update on "${key}" lost ${attempts} compare-and-swap attempts.`)
|
|
650
|
+
}
|
|
651
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as TestApp from '../../test/App.js'
|
|
2
2
|
import * as Runtime from '../../test/runtime.js'
|
|
3
|
-
import * as Indexer from '../apps/indexer.js'
|
|
3
|
+
import * as Indexer from '../apps/data/routes/indexer.js'
|
|
4
4
|
|
|
5
5
|
describe('local TIDX runtime', () => {
|
|
6
6
|
test.runIf(Runtime.get().mode === 'localnet')(
|