tapimo 0.2.0 → 0.4.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 +36 -8
- package/dist/ApiKey.d.ts +1 -0
- package/dist/ApiKey.d.ts.map +1 -1
- package/dist/ApiKey.js +5 -2
- package/dist/ApiKey.js.map +1 -1
- package/dist/ApiKeys.d.ts +28 -1
- package/dist/ApiKeys.d.ts.map +1 -1
- package/dist/ApiKeys.js +34 -1
- package/dist/ApiKeys.js.map +1 -1
- package/dist/App.d.ts +7532 -4850
- package/dist/App.d.ts.map +1 -1
- package/dist/App.js +11 -3
- package/dist/App.js.map +1 -1
- package/dist/Scope.d.ts +11 -2
- package/dist/Scope.d.ts.map +1 -1
- package/dist/Scope.js +14 -3
- package/dist/Scope.js.map +1 -1
- package/dist/admin/App.d.ts +63 -53
- package/dist/admin/App.d.ts.map +1 -1
- package/dist/admin/apps/api-keys.d.ts +7 -0
- package/dist/admin/apps/api-keys.d.ts.map +1 -1
- package/dist/admin/apps/scopes.d.ts +8 -0
- package/dist/admin/apps/scopes.d.ts.map +1 -1
- package/dist/admin/apps/scopes.js +3 -0
- package/dist/admin/apps/scopes.js.map +1 -1
- package/dist/admin/apps/verified-tokens.d.ts +57 -57
- package/dist/admin/ui.generated.d.ts +1 -1
- package/dist/admin/ui.generated.js +13 -13
- package/dist/admin/ui.generated.js.map +1 -1
- package/dist/analytics/Analytics.d.ts +151 -0
- package/dist/analytics/Analytics.d.ts.map +1 -0
- package/dist/analytics/Analytics.js +212 -0
- package/dist/analytics/Analytics.js.map +1 -0
- package/dist/analytics/tables/requestEvents.d.ts +215 -0
- package/dist/analytics/tables/requestEvents.d.ts.map +1 -0
- package/dist/analytics/tables/requestEvents.js +264 -0
- package/dist/analytics/tables/requestEvents.js.map +1 -0
- package/dist/apps/data/App.d.ts +1049 -624
- package/dist/apps/data/App.d.ts.map +1 -1
- package/dist/apps/data/App.js +36 -0
- package/dist/apps/data/App.js.map +1 -1
- package/dist/apps/data/FundingRouteProviders.d.ts +54 -0
- package/dist/apps/data/FundingRouteProviders.d.ts.map +1 -0
- package/dist/apps/data/FundingRouteProviders.js +355 -0
- package/dist/apps/data/FundingRouteProviders.js.map +1 -0
- package/dist/apps/data/index.d.ts +1 -0
- package/dist/apps/data/index.d.ts.map +1 -1
- package/dist/apps/data/index.js +1 -0
- package/dist/apps/data/index.js.map +1 -1
- package/dist/apps/data/routes/balances.d.ts +45 -45
- package/dist/apps/data/routes/coingecko.d.ts +176 -176
- package/dist/apps/data/routes/exchanges.d.ts +455 -455
- package/dist/apps/data/routes/fee-amm.d.ts +101 -101
- package/dist/apps/data/routes/funding.d.ts +843 -0
- package/dist/apps/data/routes/funding.d.ts.map +1 -0
- package/dist/apps/data/routes/funding.js +443 -0
- package/dist/apps/data/routes/funding.js.map +1 -0
- package/dist/apps/data/routes/indexer.d.ts +5 -5
- package/dist/apps/data/routes/receipts.d.ts.map +1 -1
- package/dist/apps/data/routes/receipts.js +2 -8
- package/dist/apps/data/routes/receipts.js.map +1 -1
- package/dist/apps/data/routes/rpc.d.ts +29 -36
- package/dist/apps/data/routes/rpc.d.ts.map +1 -1
- package/dist/apps/data/routes/rpc.js +29 -1
- package/dist/apps/data/routes/rpc.js.map +1 -1
- package/dist/apps/data/routes/tokenlist.d.ts +36 -36
- package/dist/apps/data/routes/transfers.d.ts +56 -56
- package/dist/apps/data/routes/verified-tokens.d.ts +36 -36
- package/dist/apps/data/routes/webhooks.d.ts +45 -45
- package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
- package/dist/apps/data/routes/webhooks.js +8 -8
- package/dist/apps/data/routes/webhooks.js.map +1 -1
- package/dist/apps/docs/App.d.ts +5 -3
- package/dist/apps/docs/App.d.ts.map +1 -1
- package/dist/apps/docs/App.js +28 -96
- package/dist/apps/docs/App.js.map +1 -1
- package/dist/apps/docs/reference.d.ts +21 -0
- package/dist/apps/docs/reference.d.ts.map +1 -0
- package/dist/apps/docs/reference.js +112 -0
- package/dist/apps/docs/reference.js.map +1 -0
- package/dist/apps/index.d.ts +3 -2
- package/dist/apps/index.d.ts.map +1 -1
- package/dist/apps/index.js +3 -2
- package/dist/apps/index.js.map +1 -1
- package/dist/apps/{platform → management}/App.d.ts +3015 -738
- package/dist/apps/management/App.d.ts.map +1 -0
- package/dist/apps/{platform → management}/App.js +20 -10
- package/dist/apps/management/App.js.map +1 -0
- package/dist/apps/management/Billing.d.ts +279 -0
- package/dist/apps/management/Billing.d.ts.map +1 -0
- package/dist/apps/management/Billing.js +477 -0
- package/dist/apps/management/Billing.js.map +1 -0
- package/dist/apps/management/index.d.ts +3 -0
- package/dist/apps/management/index.d.ts.map +1 -0
- package/dist/apps/management/index.js +3 -0
- package/dist/apps/management/index.js.map +1 -0
- package/dist/apps/{platform → management}/routes/api-keys.d.ts +341 -49
- package/dist/apps/management/routes/api-keys.d.ts.map +1 -0
- package/dist/apps/{platform → management}/routes/api-keys.js +116 -14
- package/dist/apps/management/routes/api-keys.js.map +1 -0
- package/dist/apps/management/routes/billing.d.ts +1520 -0
- package/dist/apps/management/routes/billing.d.ts.map +1 -0
- package/dist/apps/management/routes/billing.js +795 -0
- package/dist/apps/management/routes/billing.js.map +1 -0
- package/dist/apps/{platform → management}/routes/invitations.d.ts +202 -84
- package/dist/apps/management/routes/invitations.d.ts.map +1 -0
- package/dist/apps/{platform → management}/routes/invitations.js +59 -1
- package/dist/apps/management/routes/invitations.js.map +1 -0
- package/dist/apps/{platform → management}/routes/me.d.ts +17 -17
- package/dist/apps/management/routes/me.d.ts.map +1 -0
- package/dist/apps/management/routes/me.js.map +1 -0
- package/dist/apps/management/routes/members.d.ts.map +1 -0
- package/dist/apps/management/routes/members.js.map +1 -0
- package/dist/apps/{platform → management}/routes/orgs.d.ts +50 -36
- package/dist/apps/management/routes/orgs.d.ts.map +1 -0
- package/dist/apps/{platform → management}/routes/orgs.js +14 -3
- package/dist/apps/management/routes/orgs.js.map +1 -0
- package/dist/apps/{platform → management}/routes/projects.d.ts +97 -97
- package/dist/apps/{platform → management}/routes/projects.d.ts.map +1 -1
- package/dist/apps/management/routes/projects.js.map +1 -0
- package/dist/apps/management/routes/scopes.d.ts +107 -0
- package/dist/apps/management/routes/scopes.d.ts.map +1 -0
- package/dist/apps/management/routes/scopes.js +66 -0
- package/dist/apps/management/routes/scopes.js.map +1 -0
- package/dist/apps/management/routes/usage.d.ts +588 -0
- package/dist/apps/management/routes/usage.d.ts.map +1 -0
- package/dist/apps/management/routes/usage.js +391 -0
- package/dist/apps/management/routes/usage.js.map +1 -0
- package/dist/apps/mcp.d.ts +76 -0
- package/dist/apps/mcp.d.ts.map +1 -0
- package/dist/apps/mcp.js +188 -0
- package/dist/apps/mcp.js.map +1 -0
- package/dist/apps/relay/App.d.ts +4 -1
- package/dist/apps/relay/App.d.ts.map +1 -1
- package/dist/apps/relay/App.js +118 -10
- package/dist/apps/relay/App.js.map +1 -1
- package/dist/apps/relay/Sponsorships.d.ts.map +1 -1
- package/dist/apps/relay/Sponsorships.js +2 -1
- package/dist/apps/relay/Sponsorships.js.map +1 -1
- package/dist/cli/Cli.d.ts +8 -1
- package/dist/cli/Cli.d.ts.map +1 -1
- package/dist/cli/Cli.js +11 -3
- package/dist/cli/Cli.js.map +1 -1
- package/dist/cli/commands/admin.d.ts +4 -0
- package/dist/cli/commands/admin.d.ts.map +1 -1
- package/dist/cli/commands/admin.js +80 -1
- package/dist/cli/commands/admin.js.map +1 -1
- package/dist/db/Db.d.ts +7 -0
- package/dist/db/Db.d.ts.map +1 -1
- package/dist/db/Db.js +126 -0
- package/dist/db/Db.js.map +1 -1
- package/dist/db/Schema.d.ts +79 -0
- package/dist/db/Schema.d.ts.map +1 -1
- package/dist/db/Schema.js +88 -2
- package/dist/db/Schema.js.map +1 -1
- package/dist/db/tables/billingSettings.d.ts +55 -0
- package/dist/db/tables/billingSettings.d.ts.map +1 -0
- package/dist/db/tables/billingSettings.js +70 -0
- package/dist/db/tables/billingSettings.js.map +1 -0
- package/dist/db/tables/invitations.js +2 -2
- package/dist/db/tables/invitations.js.map +1 -1
- package/dist/db/tables/organizations.d.ts +6 -2
- package/dist/db/tables/organizations.d.ts.map +1 -1
- package/dist/db/tables/organizations.js +10 -4
- package/dist/db/tables/organizations.js.map +1 -1
- package/dist/db/tables/projects.js +2 -2
- package/dist/db/tables/projects.js.map +1 -1
- package/dist/db/tables/requestUsage.d.ts +121 -0
- package/dist/db/tables/requestUsage.d.ts.map +1 -0
- package/dist/db/tables/requestUsage.js +150 -0
- package/dist/db/tables/requestUsage.js.map +1 -0
- package/dist/db/tables/sponsoredTransactions.d.ts +119 -0
- package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -1
- package/dist/db/tables/sponsoredTransactions.js +155 -2
- package/dist/db/tables/sponsoredTransactions.js.map +1 -1
- package/dist/db/tables/stripeCustomers.d.ts +81 -0
- package/dist/db/tables/stripeCustomers.d.ts.map +1 -0
- package/dist/db/tables/stripeCustomers.js +120 -0
- package/dist/db/tables/stripeCustomers.js.map +1 -0
- package/dist/db/tables/users.js +2 -2
- package/dist/db/tables/users.js.map +1 -1
- package/dist/handlers/internal/sponsorship.d.ts +5 -2
- package/dist/handlers/internal/sponsorship.d.ts.map +1 -1
- package/dist/handlers/internal/sponsorship.js +19 -5
- package/dist/handlers/internal/sponsorship.js.map +1 -1
- package/dist/handlers/internal/utils.d.ts +10 -3
- package/dist/handlers/internal/utils.d.ts.map +1 -1
- package/dist/handlers/internal/utils.js +14 -6
- package/dist/handlers/internal/utils.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/internal/Auth.d.ts +18 -4
- package/dist/internal/Auth.d.ts.map +1 -1
- package/dist/internal/Auth.js +149 -77
- package/dist/internal/Auth.js.map +1 -1
- package/dist/internal/Cache.d.ts.map +1 -1
- package/dist/internal/Cache.js +4 -1
- package/dist/internal/Cache.js.map +1 -1
- package/dist/internal/EdgeCache.d.ts +10 -0
- package/dist/internal/EdgeCache.d.ts.map +1 -1
- package/dist/internal/EdgeCache.js +39 -8
- package/dist/internal/EdgeCache.js.map +1 -1
- package/dist/internal/Fees.d.ts +27 -0
- package/dist/internal/Fees.d.ts.map +1 -0
- package/dist/internal/Fees.js +48 -0
- package/dist/internal/Fees.js.map +1 -0
- package/dist/internal/FundingRoutes.d.ts +915 -0
- package/dist/internal/FundingRoutes.d.ts.map +1 -0
- package/dist/internal/FundingRoutes.js +625 -0
- package/dist/internal/FundingRoutes.js.map +1 -0
- package/dist/internal/Id.d.ts +15 -0
- package/dist/internal/Id.d.ts.map +1 -0
- package/dist/internal/Id.js +22 -0
- package/dist/internal/Id.js.map +1 -0
- package/dist/internal/Log.d.ts +2 -0
- package/dist/internal/Log.d.ts.map +1 -1
- package/dist/internal/Log.js +3 -0
- package/dist/internal/Log.js.map +1 -1
- package/dist/internal/OpenApi.d.ts +10 -1
- package/dist/internal/OpenApi.d.ts.map +1 -1
- package/dist/internal/OpenApi.js +8 -1
- package/dist/internal/OpenApi.js.map +1 -1
- package/dist/internal/RateLimit.d.ts +25 -0
- package/dist/internal/RateLimit.d.ts.map +1 -1
- package/dist/internal/RateLimit.js +51 -0
- package/dist/internal/RateLimit.js.map +1 -1
- package/dist/internal/Response.d.ts +1 -1
- package/dist/internal/Response.d.ts.map +1 -1
- package/dist/internal/Schema.d.ts +8 -0
- package/dist/internal/Schema.d.ts.map +1 -1
- package/dist/internal/Schema.js +7 -0
- package/dist/internal/Schema.js.map +1 -1
- package/dist/internal/Webhooks.d.ts.map +1 -1
- package/dist/internal/Webhooks.js +3 -3
- package/dist/internal/Webhooks.js.map +1 -1
- package/package.json +8 -7
- package/src/ApiKey.ts +9 -2
- package/src/ApiKeys.test.ts +51 -0
- package/src/ApiKeys.ts +50 -1
- package/src/App.test.ts +33 -86718
- package/src/App.ts +26 -8
- package/src/Client.test-d.ts +353 -2
- package/src/Scope.ts +16 -3
- package/src/admin/apps/scopes.test.ts +6 -0
- package/src/admin/apps/scopes.ts +3 -0
- package/src/admin/ui/src/components/Page.tsx +2 -3
- package/src/admin/ui/src/routes/__root.tsx +2 -2
- package/src/admin/ui/src/routes/api-keys.tsx +2 -2
- package/src/admin/ui.generated.ts +13 -13
- package/src/analytics/Analytics.test.ts +527 -0
- package/src/analytics/Analytics.ts +318 -0
- package/src/analytics/tables/requestEvents.ts +523 -0
- package/src/apps/composition.test.ts +26 -13
- package/src/apps/data/App.test.ts +72 -0
- package/src/apps/data/App.ts +37 -0
- package/src/apps/data/FundingRouteProviders.test.ts +281 -0
- package/src/apps/data/FundingRouteProviders.ts +459 -0
- package/src/apps/data/index.ts +1 -0
- package/src/apps/data/routes/funding.test.ts +314 -0
- package/src/apps/data/routes/funding.ts +698 -0
- package/src/apps/data/routes/receipts.ts +2 -9
- package/src/apps/data/routes/rpc.test.ts +78 -2
- package/src/apps/data/routes/rpc.ts +31 -1
- package/src/apps/data/routes/webhooks.test.ts +1 -1
- package/src/apps/data/routes/webhooks.ts +11 -8
- package/src/apps/docs/App.ts +30 -101
- package/src/apps/docs/reference.ts +132 -0
- package/src/apps/index.ts +3 -2
- package/src/apps/{platform → management}/App.ts +56 -17
- package/src/apps/management/Billing.test.ts +530 -0
- package/src/apps/management/Billing.ts +682 -0
- package/src/apps/management/index.ts +2 -0
- package/src/apps/{platform → management}/routes/api-keys.test.ts +172 -9
- package/src/apps/{platform → management}/routes/api-keys.ts +137 -21
- package/src/apps/management/routes/billing.test.ts +1100 -0
- package/src/apps/management/routes/billing.ts +966 -0
- package/src/apps/{platform → management}/routes/invitations.test.ts +55 -7
- package/src/apps/{platform → management}/routes/invitations.ts +69 -1
- package/src/apps/management/routes/me.test.ts +351 -0
- package/src/apps/{platform → management}/routes/orgs.ts +26 -4
- package/src/apps/management/routes/scopes.test.ts +29 -0
- package/src/apps/management/routes/scopes.ts +85 -0
- package/src/apps/management/routes/usage.test.ts +422 -0
- package/src/apps/management/routes/usage.ts +527 -0
- package/src/apps/mcp.test.ts +206 -0
- package/src/apps/mcp.ts +250 -0
- package/src/apps/relay/App.test.ts +457 -8
- package/src/apps/relay/App.ts +120 -12
- package/src/apps/relay/Sponsorships.test.ts +1 -1
- package/src/apps/relay/Sponsorships.ts +2 -1
- package/src/cli/Cli.test.ts +25 -2
- package/src/cli/Cli.ts +17 -2
- package/src/cli/commands/admin.test.ts +83 -0
- package/src/cli/commands/admin.ts +89 -1
- package/src/db/Db.ts +137 -0
- package/src/db/Schema.ts +104 -2
- package/src/db/tables/billingSettings.test.ts +87 -0
- package/src/db/tables/billingSettings.ts +108 -0
- package/src/db/tables/invitations.ts +2 -2
- package/src/db/tables/organizations.test.ts +1 -1
- package/src/db/tables/organizations.ts +15 -5
- package/src/db/tables/projects.ts +2 -2
- package/src/db/tables/requestUsage.ts +231 -0
- package/src/db/tables/sponsoredTransactions.test.ts +108 -1
- package/src/db/tables/sponsoredTransactions.ts +230 -2
- package/src/db/tables/stripeCustomers.test.ts +109 -0
- package/src/db/tables/stripeCustomers.ts +162 -0
- package/src/db/tables/users.ts +2 -2
- package/src/handlers/internal/sponsorship.ts +34 -7
- package/src/handlers/internal/utils.ts +19 -7
- package/src/index.ts +1 -0
- package/src/internal/Auth.test.ts +175 -25
- package/src/internal/Auth.ts +176 -87
- package/src/internal/Cache.ts +5 -1
- package/src/internal/EdgeCache.test.ts +27 -4
- package/src/internal/EdgeCache.ts +47 -9
- package/src/internal/Fees.test.ts +32 -0
- package/src/internal/Fees.ts +51 -0
- package/src/internal/FundingRoutes.test.ts +213 -0
- package/src/internal/FundingRoutes.ts +898 -0
- package/src/internal/Id.test.ts +17 -0
- package/src/internal/Id.ts +24 -0
- package/src/internal/Log.test.ts +1 -0
- package/src/internal/Log.ts +5 -0
- package/src/internal/OpenApi.ts +12 -1
- package/src/internal/RateLimit.test.ts +84 -0
- package/src/internal/RateLimit.ts +70 -0
- package/src/internal/Response.ts +1 -1
- package/src/internal/Schema.ts +8 -0
- package/src/internal/Webhooks.test.ts +3 -3
- package/src/internal/Webhooks.ts +3 -3
- package/dist/apps/platform/App.d.ts.map +0 -1
- package/dist/apps/platform/App.js.map +0 -1
- package/dist/apps/platform/index.d.ts +0 -2
- package/dist/apps/platform/index.d.ts.map +0 -1
- package/dist/apps/platform/index.js +0 -2
- package/dist/apps/platform/index.js.map +0 -1
- package/dist/apps/platform/routes/api-keys.d.ts.map +0 -1
- package/dist/apps/platform/routes/api-keys.js.map +0 -1
- package/dist/apps/platform/routes/invitations.d.ts.map +0 -1
- package/dist/apps/platform/routes/invitations.js.map +0 -1
- package/dist/apps/platform/routes/me.d.ts.map +0 -1
- package/dist/apps/platform/routes/me.js.map +0 -1
- package/dist/apps/platform/routes/members.d.ts.map +0 -1
- package/dist/apps/platform/routes/members.js.map +0 -1
- package/dist/apps/platform/routes/orgs.d.ts.map +0 -1
- package/dist/apps/platform/routes/orgs.js.map +0 -1
- package/dist/apps/platform/routes/projects.js.map +0 -1
- package/src/apps/platform/index.ts +0 -1
- package/src/apps/platform/routes/me.test.ts +0 -121
- /package/dist/apps/{platform → management}/routes/me.js +0 -0
- /package/dist/apps/{platform → management}/routes/members.d.ts +0 -0
- /package/dist/apps/{platform → management}/routes/members.js +0 -0
- /package/dist/apps/{platform → management}/routes/projects.js +0 -0
- /package/src/apps/{platform → management}/routes/me.ts +0 -0
- /package/src/apps/{platform → management}/routes/members.test.ts +0 -0
- /package/src/apps/{platform → management}/routes/members.ts +0 -0
- /package/src/apps/{platform → management}/routes/orgs.test.ts +0 -0
- /package/src/apps/{platform → management}/routes/projects.test.ts +0 -0
- /package/src/apps/{platform → management}/routes/projects.ts +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts'
|
|
2
2
|
|
|
3
|
+
import * as ApiKeys from '../../../ApiKeys.js'
|
|
3
4
|
import * as TestApp from '../../../../test/App.js'
|
|
4
5
|
import * as Keys from './api-keys.js'
|
|
5
6
|
import * as Orgs from './orgs.js'
|
|
@@ -95,8 +96,9 @@ describe('keys', () => {
|
|
|
95
96
|
})
|
|
96
97
|
expect(data.status).toBe(200)
|
|
97
98
|
|
|
98
|
-
// Listing exposes metadata only — never the token.
|
|
99
|
-
|
|
99
|
+
// Listing exposes metadata only — never the token. The key is sandbox, so
|
|
100
|
+
// the list is scoped to that environment (production is the default).
|
|
101
|
+
const listed = await app.request(`${base}?environment=sandbox`, { headers: { cookie } })
|
|
100
102
|
expect(listed.status).toBe(200)
|
|
101
103
|
const list = await TestApp.json(listed, Keys.schema.listApiKeys.Response)
|
|
102
104
|
expect(list.data.map(({ id }) => id)).toEqual([created.id])
|
|
@@ -136,28 +138,87 @@ describe('keys', () => {
|
|
|
136
138
|
expect(response.status).toBe(400)
|
|
137
139
|
})
|
|
138
140
|
|
|
139
|
-
test('
|
|
141
|
+
test('mints zero-scope keys for sessions', async () => {
|
|
140
142
|
const app = createApp()
|
|
141
143
|
const cookie = await session(app)
|
|
142
144
|
const { org, project } = await fixture(app, cookie)
|
|
143
145
|
|
|
144
|
-
const
|
|
146
|
+
const minted = await app.request(
|
|
145
147
|
`/v1/orgs/${org.id}/projects/${project.id}/api-keys`,
|
|
146
|
-
json('POST', cookie, {
|
|
148
|
+
json('POST', cookie, { name: 'CI' }),
|
|
147
149
|
)
|
|
148
|
-
expect(
|
|
149
|
-
const
|
|
150
|
+
expect(minted.status).toBe(200)
|
|
151
|
+
const created = await TestApp.json(minted, Keys.schema.createApiKey.Response)
|
|
152
|
+
expect(created.scopes).toEqual([])
|
|
153
|
+
expect(created.createdBy).toMatch(/^usr_/)
|
|
154
|
+
|
|
155
|
+
// The key authenticates but grants nothing: scoped routes stay forbidden.
|
|
156
|
+
const data = await app.request('/v1/tokens', {
|
|
157
|
+
headers: { 'cache-control': 'no-store', 'tempo-api-key': created.token },
|
|
158
|
+
})
|
|
159
|
+
expect(data.status).toBe(403)
|
|
160
|
+
const { requestId, ...body } = (await data.json()) as { requestId?: string }
|
|
150
161
|
expect(requestId).toBeDefined()
|
|
151
162
|
expect(body).toMatchInlineSnapshot(`
|
|
152
163
|
{
|
|
153
164
|
"error": {
|
|
154
|
-
"code": "
|
|
155
|
-
"message": "
|
|
165
|
+
"code": "api_key_forbidden",
|
|
166
|
+
"message": "API key missing required scope",
|
|
156
167
|
},
|
|
157
168
|
}
|
|
158
169
|
`)
|
|
159
170
|
})
|
|
160
171
|
|
|
172
|
+
test('lets sessions mint catalog scopes on production keys', async () => {
|
|
173
|
+
const app = createApp()
|
|
174
|
+
const cookie = await session(app)
|
|
175
|
+
const { org, project } = await fixture(app, cookie)
|
|
176
|
+
|
|
177
|
+
const created = await TestApp.json(
|
|
178
|
+
await app.request(
|
|
179
|
+
`/v1/orgs/${org.id}/projects/${project.id}/api-keys`,
|
|
180
|
+
json('POST', cookie, { scopes: ['data:read'] }),
|
|
181
|
+
),
|
|
182
|
+
Keys.schema.createApiKey.Response,
|
|
183
|
+
)
|
|
184
|
+
expect(created.environment).toBe('production')
|
|
185
|
+
expect(created.scopes).toEqual(['data:read'])
|
|
186
|
+
// Session-minted, not granted through the super admin.
|
|
187
|
+
expect(created.createdBy).not.toBe('super_admin')
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
test('rejects session mints of non-self-serve scopes (webhooks)', async () => {
|
|
191
|
+
const app = createApp()
|
|
192
|
+
const cookie = await session(app)
|
|
193
|
+
const { org, project } = await fixture(app, cookie)
|
|
194
|
+
|
|
195
|
+
const response = await app.request(
|
|
196
|
+
`/v1/orgs/${org.id}/projects/${project.id}/api-keys`,
|
|
197
|
+
json('POST', cookie, { scopes: ['webhooks:write'] }),
|
|
198
|
+
)
|
|
199
|
+
expect(response.status).toBe(403)
|
|
200
|
+
const body = (await response.json()) as { error: { code: string } }
|
|
201
|
+
expect(body.error.code).toBe('scope_not_issuable')
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
test('lets sessions mint catalog scopes on sandbox keys', async () => {
|
|
205
|
+
const app = createApp()
|
|
206
|
+
const cookie = await session(app)
|
|
207
|
+
const { org, project } = await fixture(app, cookie)
|
|
208
|
+
|
|
209
|
+
const created = await TestApp.json(
|
|
210
|
+
await app.request(
|
|
211
|
+
`/v1/orgs/${org.id}/projects/${project.id}/api-keys`,
|
|
212
|
+
json('POST', cookie, { environment: 'sandbox', scopes: ['data:read'] }),
|
|
213
|
+
),
|
|
214
|
+
Keys.schema.createApiKey.Response,
|
|
215
|
+
)
|
|
216
|
+
expect(created.environment).toBe('sandbox')
|
|
217
|
+
expect(created.scopes).toEqual(['data:read'])
|
|
218
|
+
// Session-minted, not granted through the super admin.
|
|
219
|
+
expect(created.createdBy).not.toBe('super_admin')
|
|
220
|
+
})
|
|
221
|
+
|
|
161
222
|
test('binds keys to their project on every verb', async () => {
|
|
162
223
|
const app = createApp()
|
|
163
224
|
const owner = await session(app)
|
|
@@ -206,6 +267,108 @@ describe('keys', () => {
|
|
|
206
267
|
expect(list.data.map(({ id }) => id)).toEqual([minted.id])
|
|
207
268
|
})
|
|
208
269
|
|
|
270
|
+
test('lists keys org-wide across projects', async () => {
|
|
271
|
+
const store = TestApp.kvStore({ keys: [] })
|
|
272
|
+
const app = createApp({ kv: { store } })
|
|
273
|
+
const owner = await session(app)
|
|
274
|
+
const { org, project } = await fixture(app, owner)
|
|
275
|
+
const other = await TestApp.json(
|
|
276
|
+
await app.request(`/v1/orgs/${org.id}/projects`, json('POST', owner, { name: 'Payments' })),
|
|
277
|
+
Projects.schema.Project,
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
const first = await TestApp.json(
|
|
281
|
+
await app.request(
|
|
282
|
+
`/v1/orgs/${org.id}/projects/${project.id}/api-keys`,
|
|
283
|
+
superAdminJson('POST', { name: 'Checkout CI', scopes: ['data:read'] }),
|
|
284
|
+
),
|
|
285
|
+
Keys.schema.createApiKey.Response,
|
|
286
|
+
)
|
|
287
|
+
const second = await TestApp.json(
|
|
288
|
+
await app.request(
|
|
289
|
+
`/v1/orgs/${org.id}/projects/${other.id}/api-keys`,
|
|
290
|
+
superAdminJson('POST', { name: 'Payments CI', scopes: ['data:read'] }),
|
|
291
|
+
),
|
|
292
|
+
Keys.schema.createApiKey.Response,
|
|
293
|
+
)
|
|
294
|
+
// Org-attributed key without a project (admin-surface mints land like this).
|
|
295
|
+
const orgWide = await ApiKeys.mint(store, { orgId: org.id, scopes: ['data:read'] })
|
|
296
|
+
|
|
297
|
+
const listed = await app.request(`/v1/orgs/${org.id}/api-keys`, {
|
|
298
|
+
headers: { cookie: owner },
|
|
299
|
+
})
|
|
300
|
+
const list = await TestApp.json(listed, Keys.schema.listOrgApiKeys.Response)
|
|
301
|
+
expect(list.data.map(({ id }) => id).sort()).toEqual(
|
|
302
|
+
[first.id, second.id, orgWide.record.id].sort(),
|
|
303
|
+
)
|
|
304
|
+
expect(list.data.find(({ id }) => id === first.id)?.projectId).toBe(project.id)
|
|
305
|
+
expect(list.data.find(({ id }) => id === second.id)?.projectId).toBe(other.id)
|
|
306
|
+
expect(list.data.find(({ id }) => id === orgWide.record.id)?.projectId).toBeUndefined()
|
|
307
|
+
|
|
308
|
+
// Non-members see the org as absent.
|
|
309
|
+
const stranger = await session(app)
|
|
310
|
+
const foreign = await app.request(`/v1/orgs/${org.id}/api-keys`, {
|
|
311
|
+
headers: { cookie: stranger },
|
|
312
|
+
})
|
|
313
|
+
expect(foreign.status).toBe(404)
|
|
314
|
+
})
|
|
315
|
+
|
|
316
|
+
test('filters keys by environment (defaulting to production)', async () => {
|
|
317
|
+
const app = createApp()
|
|
318
|
+
const cookie = await session(app)
|
|
319
|
+
const { org, project } = await fixture(app, cookie)
|
|
320
|
+
const projectBase = `/v1/orgs/${org.id}/projects/${project.id}/api-keys`
|
|
321
|
+
const orgBase = `/v1/orgs/${org.id}/api-keys`
|
|
322
|
+
|
|
323
|
+
const production = await TestApp.json(
|
|
324
|
+
await app.request(
|
|
325
|
+
projectBase,
|
|
326
|
+
superAdminJson('POST', { name: 'Prod', scopes: ['data:read'] }),
|
|
327
|
+
),
|
|
328
|
+
Keys.schema.createApiKey.Response,
|
|
329
|
+
)
|
|
330
|
+
const sandbox = await TestApp.json(
|
|
331
|
+
await app.request(
|
|
332
|
+
projectBase,
|
|
333
|
+
superAdminJson('POST', { environment: 'sandbox', name: 'Sandbox', scopes: ['data:read'] }),
|
|
334
|
+
),
|
|
335
|
+
Keys.schema.createApiKey.Response,
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
const ids = async (path: string) =>
|
|
339
|
+
(
|
|
340
|
+
await TestApp.json(
|
|
341
|
+
await app.request(path, { headers: { cookie } }),
|
|
342
|
+
Keys.schema.listApiKeys.Response,
|
|
343
|
+
)
|
|
344
|
+
).data
|
|
345
|
+
.map(({ id }) => id)
|
|
346
|
+
.sort()
|
|
347
|
+
|
|
348
|
+
// Each filter returns only its own environment.
|
|
349
|
+
expect(await ids(`${projectBase}?environment=production`)).toEqual([production.id])
|
|
350
|
+
expect(await ids(`${projectBase}?environment=sandbox`)).toEqual([sandbox.id])
|
|
351
|
+
// Absent param defaults to production.
|
|
352
|
+
expect(await ids(projectBase)).toEqual([production.id])
|
|
353
|
+
|
|
354
|
+
// Same behavior on the org-wide list route.
|
|
355
|
+
const orgIds = async (path: string) =>
|
|
356
|
+
(
|
|
357
|
+
await TestApp.json(
|
|
358
|
+
await app.request(path, { headers: { cookie } }),
|
|
359
|
+
Keys.schema.listOrgApiKeys.Response,
|
|
360
|
+
)
|
|
361
|
+
).data
|
|
362
|
+
.map(({ id }) => id)
|
|
363
|
+
.sort()
|
|
364
|
+
expect(await orgIds(`${orgBase}?environment=sandbox`)).toEqual([sandbox.id])
|
|
365
|
+
expect(await orgIds(orgBase)).toEqual([production.id])
|
|
366
|
+
|
|
367
|
+
// An unknown environment value is rejected.
|
|
368
|
+
const invalid = await app.request(`${projectBase}?environment=staging`, { headers: { cookie } })
|
|
369
|
+
expect(invalid.status).toBe(400)
|
|
370
|
+
})
|
|
371
|
+
|
|
209
372
|
test('revokes keys when their project or organization is deleted', async () => {
|
|
210
373
|
const app = createApp()
|
|
211
374
|
const cookie = await session(app)
|
|
@@ -3,7 +3,9 @@ import * as z from 'zod/mini'
|
|
|
3
3
|
|
|
4
4
|
import * as ApiKeys from '../../../ApiKeys.js'
|
|
5
5
|
import type * as App from '../../../App.js'
|
|
6
|
+
import * as Db from '../../../db/Db.js'
|
|
6
7
|
import * as Auth from '../../../internal/Auth.js'
|
|
8
|
+
import * as Billing from '../Billing.js'
|
|
7
9
|
import * as OpenApi from '../../../internal/OpenApi.js'
|
|
8
10
|
import * as Response from '../../../internal/Response.js'
|
|
9
11
|
import * as Schema from '../../../internal/Schema.js'
|
|
@@ -50,12 +52,10 @@ export namespace schema {
|
|
|
50
52
|
z.describe('Owning organization id (`org_…`).'),
|
|
51
53
|
z.meta({ examples: ['org_1a2b3c4d5e6f7g8h9j0k1m2n'] }),
|
|
52
54
|
),
|
|
53
|
-
projectId: z
|
|
54
|
-
.
|
|
55
|
-
.
|
|
56
|
-
|
|
57
|
-
z.meta({ examples: ['prj_1a2b3c4d5e6f7g8h9j0k1m2n'] }),
|
|
58
|
-
),
|
|
55
|
+
projectId: z.optional(z.string()).check(
|
|
56
|
+
z.describe('Owning project id (`prj_…`).'), // prettier-ignore
|
|
57
|
+
z.meta({ examples: ['prj_1a2b3c4d5e6f7g8h9j0k1m2n'] }),
|
|
58
|
+
),
|
|
59
59
|
scopes: z
|
|
60
60
|
.array(z.enum([...Scope.catalog.map((entry) => entry.scope), Scope.wildcard]))
|
|
61
61
|
.check(
|
|
@@ -92,6 +92,18 @@ export namespace schema {
|
|
|
92
92
|
})
|
|
93
93
|
.check(z.describe("Path parameters for a project's API keys."))
|
|
94
94
|
|
|
95
|
+
/** Path parameters addressing an organization's key collection. */
|
|
96
|
+
export const OrgParams = z
|
|
97
|
+
.object({
|
|
98
|
+
orgId: z
|
|
99
|
+
.string()
|
|
100
|
+
.check(
|
|
101
|
+
z.describe('The organization id (`org_…`).'),
|
|
102
|
+
z.meta({ examples: ['org_1a2b3c4d5e6f7g8h9j0k1m2n'] }),
|
|
103
|
+
),
|
|
104
|
+
})
|
|
105
|
+
.check(z.describe("Path parameters for an organization's API keys."))
|
|
106
|
+
|
|
95
107
|
/** Path parameters addressing one API key. */
|
|
96
108
|
export const KeyParams = z
|
|
97
109
|
.object({
|
|
@@ -126,12 +138,8 @@ export namespace schema {
|
|
|
126
138
|
.optional(z.string().check(z.minLength(1), z.maxLength(100)))
|
|
127
139
|
.check(z.describe('Human-readable key name.'), z.meta({ examples: ['CI'] })),
|
|
128
140
|
scopes: z
|
|
129
|
-
.array(z.enum(Scope.catalog.map((entry) => entry.scope)))
|
|
130
|
-
.check(
|
|
131
|
-
z.minLength(1),
|
|
132
|
-
z.describe('Scopes to grant.'),
|
|
133
|
-
z.meta({ examples: [['data:read']] }),
|
|
134
|
-
),
|
|
141
|
+
._default(z.array(z.enum(Scope.catalog.map((entry) => entry.scope))), [])
|
|
142
|
+
.check(z.describe('Scopes to grant.'), z.meta({ examples: [['data:read']] })),
|
|
135
143
|
})
|
|
136
144
|
.check(z.describe('Fields for minting an API key.'))
|
|
137
145
|
|
|
@@ -153,6 +161,11 @@ export namespace schema {
|
|
|
153
161
|
|
|
154
162
|
/** Schemas for the listApiKeys operation. */
|
|
155
163
|
export namespace listApiKeys {
|
|
164
|
+
/** Query parameters filtering a project's keys. */
|
|
165
|
+
export const Query = z
|
|
166
|
+
.strictObject({ environment: Schema.Environment })
|
|
167
|
+
.check(z.describe("Query parameters for a project's API keys."))
|
|
168
|
+
|
|
156
169
|
/** Non-paginated list of a project's keys. */
|
|
157
170
|
export const Response = Schema.describe(
|
|
158
171
|
z.object({
|
|
@@ -162,6 +175,24 @@ export namespace schema {
|
|
|
162
175
|
)
|
|
163
176
|
}
|
|
164
177
|
|
|
178
|
+
/** Schemas for the listOrgApiKeys operation. */
|
|
179
|
+
export namespace listOrgApiKeys {
|
|
180
|
+
/** Query parameters filtering the organization's keys. */
|
|
181
|
+
export const Query = z
|
|
182
|
+
.strictObject({ environment: Schema.Environment })
|
|
183
|
+
.check(z.describe("Query parameters for an organization's API keys."))
|
|
184
|
+
|
|
185
|
+
/** Non-paginated list of every key in the organization. */
|
|
186
|
+
export const Response = Schema.describe(
|
|
187
|
+
z.object({
|
|
188
|
+
data: z
|
|
189
|
+
.array(Key)
|
|
190
|
+
.check(z.describe("The organization's keys across all projects, newest first.")),
|
|
191
|
+
}),
|
|
192
|
+
"A non-paginated list of an organization's API keys (metadata only).",
|
|
193
|
+
)
|
|
194
|
+
}
|
|
195
|
+
|
|
165
196
|
/** Schemas for the revokeApiKey operation. */
|
|
166
197
|
export namespace revokeApiKey {
|
|
167
198
|
/** Confirmation that the key was revoked. */
|
|
@@ -179,9 +210,11 @@ export namespace schema {
|
|
|
179
210
|
}
|
|
180
211
|
|
|
181
212
|
/**
|
|
182
|
-
* Mounts the `/orgs/:orgId/projects/:projectId/api-keys` resource
|
|
183
|
-
* project-scoped
|
|
184
|
-
*
|
|
213
|
+
* Mounts the `/orgs/:orgId/projects/:projectId/api-keys` resource
|
|
214
|
+
* (project-scoped mint/list/revoke) plus the org-wide read
|
|
215
|
+
* `/orgs/:orgId/api-keys`, over the KV state store. Access follows the
|
|
216
|
+
* org/project scope (owning session or super admin); the token appears once,
|
|
217
|
+
* at mint.
|
|
185
218
|
*/
|
|
186
219
|
export function apiKeys(options: apiKeys.Options = {}) {
|
|
187
220
|
const hidden = !options.enabled
|
|
@@ -241,7 +274,9 @@ export function apiKeys(options: apiKeys.Options = {}) {
|
|
|
241
274
|
const body = c.req.valid('json')
|
|
242
275
|
|
|
243
276
|
// Sessions mint only self-issuable scopes; the super admin grants the
|
|
244
|
-
// full catalog.
|
|
277
|
+
// full catalog. Self-serve covers the whole catalog now that billing
|
|
278
|
+
// ships, so this is a safety net: a future non-self-serve scope would
|
|
279
|
+
// be refused here without further changes.
|
|
245
280
|
if (createdBy !== 'super_admin') {
|
|
246
281
|
const denied = body.scopes.filter((scope) => !Scope.selfServe.includes(scope))
|
|
247
282
|
if (denied.length > 0)
|
|
@@ -253,11 +288,20 @@ export function apiKeys(options: apiKeys.Options = {}) {
|
|
|
253
288
|
}
|
|
254
289
|
|
|
255
290
|
try {
|
|
291
|
+
const orgId = Auth.org(c).id
|
|
292
|
+
// Snapshot billing onto the record so the auth path throttles a
|
|
293
|
+
// sandbox key to the public quota without a per-request billing read.
|
|
294
|
+
// Only sandbox is gated; production keys carry no snapshot.
|
|
295
|
+
const billingActive =
|
|
296
|
+
body.environment === 'sandbox'
|
|
297
|
+
? await Billing.active(Db.get(c.get('db')), orgId, 'sandbox')
|
|
298
|
+
: undefined
|
|
256
299
|
const { record, token } = await ApiKeys.mint(kv.store, {
|
|
257
300
|
createdBy,
|
|
258
301
|
environment: body.environment,
|
|
302
|
+
...(billingActive === undefined ? {} : { billingActive }),
|
|
259
303
|
...(body.name === undefined ? {} : { name: body.name }),
|
|
260
|
-
orgId
|
|
304
|
+
orgId,
|
|
261
305
|
projectId: Auth.project(c).id,
|
|
262
306
|
scopes: body.scopes,
|
|
263
307
|
})
|
|
@@ -279,11 +323,18 @@ export function apiKeys(options: apiKeys.Options = {}) {
|
|
|
279
323
|
code: 'param_invalid',
|
|
280
324
|
message: 'Check the path parameters and try again.',
|
|
281
325
|
}),
|
|
326
|
+
OpenApi.validate('query', schema.listApiKeys.Query, {
|
|
327
|
+
code: 'query_invalid',
|
|
328
|
+
message: 'Check the query parameters and try again.',
|
|
329
|
+
}),
|
|
282
330
|
OpenApi.describeRoute({
|
|
283
331
|
hide: hidden,
|
|
284
332
|
operationId: 'listApiKeys',
|
|
285
333
|
responses: OpenApi.responses({
|
|
286
|
-
errors: {
|
|
334
|
+
errors: {
|
|
335
|
+
400: { codes: ['query_invalid'], description: 'Invalid query parameters.' },
|
|
336
|
+
404: 'No accessible project was found for this id.',
|
|
337
|
+
},
|
|
287
338
|
success: {
|
|
288
339
|
description: "The project's keys, newest first (metadata only).",
|
|
289
340
|
schema: schema.listApiKeys.Response,
|
|
@@ -300,11 +351,17 @@ export function apiKeys(options: apiKeys.Options = {}) {
|
|
|
300
351
|
code: 'param_invalid',
|
|
301
352
|
message: 'Check the path parameters and try again.',
|
|
302
353
|
})
|
|
354
|
+
if (OpenApi.narrowValidation)
|
|
355
|
+
return OpenApi.validationError(c, {
|
|
356
|
+
code: 'query_invalid',
|
|
357
|
+
message: 'Check the query parameters and try again.',
|
|
358
|
+
})
|
|
303
359
|
|
|
304
360
|
const kv = c.get('kv')
|
|
305
361
|
if (!kv) return notEnabled(c)
|
|
306
362
|
try {
|
|
307
363
|
const records = await ApiKeys.listByOrg(kv.store, Auth.org(c).id, {
|
|
364
|
+
environment: c.req.valid('query').environment,
|
|
308
365
|
projectId: Auth.project(c).id,
|
|
309
366
|
})
|
|
310
367
|
return c.json(
|
|
@@ -318,6 +375,66 @@ export function apiKeys(options: apiKeys.Options = {}) {
|
|
|
318
375
|
}
|
|
319
376
|
},
|
|
320
377
|
)
|
|
378
|
+
.get(
|
|
379
|
+
'/v1/orgs/:orgId{org_[A-Za-z0-9_-]+}/api-keys',
|
|
380
|
+
Auth.policy({ session: true }),
|
|
381
|
+
Auth.ensureOrg(),
|
|
382
|
+
OpenApi.validate('param', schema.OrgParams, {
|
|
383
|
+
code: 'param_invalid',
|
|
384
|
+
message: 'Check the path parameters and try again.',
|
|
385
|
+
}),
|
|
386
|
+
OpenApi.validate('query', schema.listOrgApiKeys.Query, {
|
|
387
|
+
code: 'query_invalid',
|
|
388
|
+
message: 'Check the query parameters and try again.',
|
|
389
|
+
}),
|
|
390
|
+
OpenApi.describeRoute({
|
|
391
|
+
description: 'List every API key in the organization, across all of its projects.',
|
|
392
|
+
hide: hidden,
|
|
393
|
+
operationId: 'listOrgApiKeys',
|
|
394
|
+
responses: OpenApi.responses({
|
|
395
|
+
errors: {
|
|
396
|
+
400: { codes: ['query_invalid'], description: 'Invalid query parameters.' },
|
|
397
|
+
404: 'No accessible organization was found for this id.',
|
|
398
|
+
},
|
|
399
|
+
success: {
|
|
400
|
+
description: "The organization's keys across all projects, newest first (metadata only).", // prettier-ignore
|
|
401
|
+
schema: schema.listOrgApiKeys.Response,
|
|
402
|
+
},
|
|
403
|
+
}),
|
|
404
|
+
summary: 'List organization API keys',
|
|
405
|
+
tags: ['API Keys'],
|
|
406
|
+
}),
|
|
407
|
+
async (c) => {
|
|
408
|
+
if (Auth.narrowAccess) return Auth.superAdminAccessError(c)
|
|
409
|
+
if (Auth.narrowScope) return Auth.ensureOrgError(c)
|
|
410
|
+
if (OpenApi.narrowValidation)
|
|
411
|
+
return OpenApi.validationError(c, {
|
|
412
|
+
code: 'param_invalid',
|
|
413
|
+
message: 'Check the path parameters and try again.',
|
|
414
|
+
})
|
|
415
|
+
if (OpenApi.narrowValidation)
|
|
416
|
+
return OpenApi.validationError(c, {
|
|
417
|
+
code: 'query_invalid',
|
|
418
|
+
message: 'Check the query parameters and try again.',
|
|
419
|
+
})
|
|
420
|
+
|
|
421
|
+
const kv = c.get('kv')
|
|
422
|
+
if (!kv) return notEnabled(c)
|
|
423
|
+
try {
|
|
424
|
+
const records = await ApiKeys.listByOrg(kv.store, Auth.org(c).id, {
|
|
425
|
+
environment: c.req.valid('query').environment,
|
|
426
|
+
})
|
|
427
|
+
return c.json(
|
|
428
|
+
Response.validated(schema.listOrgApiKeys.Response, {
|
|
429
|
+
data: records.map(serializeKey),
|
|
430
|
+
}),
|
|
431
|
+
200,
|
|
432
|
+
)
|
|
433
|
+
} catch (cause) {
|
|
434
|
+
return Response.upstream(c, cause)
|
|
435
|
+
}
|
|
436
|
+
},
|
|
437
|
+
)
|
|
321
438
|
.delete(
|
|
322
439
|
'/v1/orgs/:orgId{org_[A-Za-z0-9_-]+}/projects/:projectId{prj_[A-Za-z0-9_-]+}/api-keys/:keyId{key_[A-Za-z0-9_-]+}',
|
|
323
440
|
Auth.policy({ session: true }),
|
|
@@ -377,7 +494,7 @@ export declare namespace apiKeys {
|
|
|
377
494
|
}
|
|
378
495
|
}
|
|
379
496
|
|
|
380
|
-
/** Projects a persisted record onto the
|
|
497
|
+
/** Projects a persisted record onto the management key shape (no rate limits, no token). */
|
|
381
498
|
function serializeKey(record: ApiKeys.Record) {
|
|
382
499
|
return {
|
|
383
500
|
createdAt: record.createdAt,
|
|
@@ -387,8 +504,7 @@ function serializeKey(record: ApiKeys.Record) {
|
|
|
387
504
|
id: record.id,
|
|
388
505
|
...(record.name === undefined ? {} : { name: record.name }),
|
|
389
506
|
orgId: record.orgId,
|
|
390
|
-
|
|
391
|
-
projectId: record.projectId ?? '',
|
|
507
|
+
...(record.projectId === undefined ? {} : { projectId: record.projectId }),
|
|
392
508
|
scopes: [...record.scopes],
|
|
393
509
|
tokenLast4: record.tokenLast4,
|
|
394
510
|
}
|