tapimo 0.4.0 → 0.4.3
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 +1 -1
- package/dist/ApiKey.d.ts +1 -9
- package/dist/ApiKey.d.ts.map +1 -1
- package/dist/ApiKey.js +2 -3
- package/dist/ApiKey.js.map +1 -1
- package/dist/ApiKeys.d.ts +53 -27
- package/dist/ApiKeys.d.ts.map +1 -1
- package/dist/ApiKeys.js +32 -17
- package/dist/ApiKeys.js.map +1 -1
- package/dist/App.d.ts +2451 -1533
- package/dist/App.d.ts.map +1 -1
- package/dist/App.js +23 -9
- package/dist/App.js.map +1 -1
- package/dist/Assets.d.ts +10 -0
- package/dist/Assets.d.ts.map +1 -1
- package/dist/Assets.js +4 -0
- package/dist/Assets.js.map +1 -1
- package/dist/Auth.d.ts +2 -1
- package/dist/Auth.d.ts.map +1 -1
- package/dist/Auth.js +2 -1
- package/dist/Auth.js.map +1 -1
- package/dist/Scope.d.ts +40 -15
- package/dist/Scope.d.ts.map +1 -1
- package/dist/Scope.js +44 -13
- package/dist/Scope.js.map +1 -1
- package/dist/admin/App.d.ts +560 -9
- package/dist/admin/App.d.ts.map +1 -1
- package/dist/admin/App.js +38 -6
- package/dist/admin/App.js.map +1 -1
- package/dist/admin/apps/api-keys.d.ts +10 -24
- package/dist/admin/apps/api-keys.d.ts.map +1 -1
- package/dist/admin/apps/api-keys.js +10 -3
- package/dist/admin/apps/api-keys.js.map +1 -1
- package/dist/admin/apps/organizations.d.ts +833 -0
- package/dist/admin/apps/organizations.d.ts.map +1 -0
- package/dist/admin/apps/organizations.js +733 -0
- package/dist/admin/apps/organizations.js.map +1 -0
- package/dist/admin/apps/scopes.d.ts +8 -0
- package/dist/admin/apps/scopes.d.ts.map +1 -1
- package/dist/admin/ui.generated.d.ts +1 -1
- package/dist/admin/ui.generated.d.ts.map +1 -1
- package/dist/admin/ui.generated.js +53 -13
- package/dist/admin/ui.generated.js.map +1 -1
- package/dist/analytics/Analytics.d.ts +10 -0
- package/dist/analytics/Analytics.d.ts.map +1 -1
- package/dist/analytics/Analytics.js +8 -0
- package/dist/analytics/Analytics.js.map +1 -1
- package/dist/analytics/tables/requestEvents.d.ts +9 -0
- package/dist/analytics/tables/requestEvents.d.ts.map +1 -1
- package/dist/analytics/tables/requestEvents.js +14 -0
- package/dist/analytics/tables/requestEvents.js.map +1 -1
- package/dist/apps/data/App.d.ts +1354 -984
- package/dist/apps/data/App.d.ts.map +1 -1
- package/dist/apps/data/App.js +20 -3
- package/dist/apps/data/App.js.map +1 -1
- package/dist/apps/data/FundingRouteProviders.d.ts +94 -5
- package/dist/apps/data/FundingRouteProviders.d.ts.map +1 -1
- package/dist/apps/data/FundingRouteProviders.js +196 -5
- package/dist/apps/data/FundingRouteProviders.js.map +1 -1
- package/dist/apps/data/routes/activities.d.ts +38 -38
- package/dist/apps/data/routes/addresses.d.ts +24 -24
- package/dist/apps/data/routes/balances.d.ts +45 -45
- package/dist/apps/data/routes/blocks.d.ts +39 -39
- package/dist/apps/data/routes/coingecko.d.ts +176 -176
- package/dist/apps/data/routes/exchanges.d.ts +453 -453
- package/dist/apps/data/routes/fee-amm.d.ts +101 -101
- package/dist/apps/data/routes/funding.d.ts +730 -138
- package/dist/apps/data/routes/funding.d.ts.map +1 -1
- package/dist/apps/data/routes/funding.js +234 -0
- package/dist/apps/data/routes/funding.js.map +1 -1
- package/dist/apps/data/routes/indexer.d.ts +5 -5
- package/dist/apps/data/routes/rpc.d.ts +31 -31
- package/dist/apps/data/routes/verified-tokens.d.ts +81 -81
- package/dist/apps/management/App.d.ts +1156 -615
- package/dist/apps/management/App.d.ts.map +1 -1
- package/dist/apps/management/App.js +2 -2
- package/dist/apps/management/Billing.d.ts +3 -0
- package/dist/apps/management/Billing.d.ts.map +1 -1
- package/dist/apps/management/Billing.js +7 -2
- package/dist/apps/management/Billing.js.map +1 -1
- package/dist/apps/management/routes/api-keys.d.ts +106 -90
- package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
- package/dist/apps/management/routes/api-keys.js +41 -30
- package/dist/apps/management/routes/api-keys.js.map +1 -1
- package/dist/apps/management/routes/billing.d.ts +278 -146
- package/dist/apps/management/routes/billing.d.ts.map +1 -1
- package/dist/apps/management/routes/billing.js +14 -14
- package/dist/apps/management/routes/billing.js.map +1 -1
- package/dist/apps/management/routes/invitations.d.ts +93 -20
- package/dist/apps/management/routes/invitations.d.ts.map +1 -1
- package/dist/apps/management/routes/invitations.js +12 -15
- package/dist/apps/management/routes/invitations.js.map +1 -1
- package/dist/apps/management/routes/me.d.ts +13 -13
- package/dist/apps/management/routes/members.d.ts +89 -18
- package/dist/apps/management/routes/members.d.ts.map +1 -1
- package/dist/apps/management/routes/members.js +10 -10
- package/dist/apps/management/routes/members.js.map +1 -1
- package/dist/apps/management/routes/orgs.d.ts +130 -70
- package/dist/apps/management/routes/orgs.d.ts.map +1 -1
- package/dist/apps/management/routes/orgs.js +19 -19
- package/dist/apps/management/routes/orgs.js.map +1 -1
- package/dist/apps/management/routes/projects.d.ts +137 -48
- package/dist/apps/management/routes/projects.d.ts.map +1 -1
- package/dist/apps/management/routes/projects.js +12 -13
- package/dist/apps/management/routes/projects.js.map +1 -1
- package/dist/apps/management/routes/scopes.d.ts +14 -28
- package/dist/apps/management/routes/scopes.d.ts.map +1 -1
- package/dist/apps/management/routes/scopes.js +6 -7
- package/dist/apps/management/routes/scopes.js.map +1 -1
- package/dist/apps/management/routes/usage.d.ts +63 -19
- package/dist/apps/management/routes/usage.d.ts.map +1 -1
- package/dist/apps/management/routes/usage.js +9 -10
- package/dist/apps/management/routes/usage.js.map +1 -1
- package/dist/apps/mcp.d.ts.map +1 -1
- package/dist/apps/mcp.js +35 -3
- package/dist/apps/mcp.js.map +1 -1
- package/dist/apps/relay/App.d.ts +26 -4
- package/dist/apps/relay/App.d.ts.map +1 -1
- package/dist/apps/relay/App.js +17 -14
- package/dist/apps/relay/App.js.map +1 -1
- package/dist/db/Db.d.ts +5 -3
- package/dist/db/Db.d.ts.map +1 -1
- package/dist/db/Db.js +52 -0
- package/dist/db/Db.js.map +1 -1
- package/dist/db/Schema.d.ts +13 -0
- package/dist/db/Schema.d.ts.map +1 -1
- package/dist/db/Schema.js +16 -1
- package/dist/db/Schema.js.map +1 -1
- package/dist/db/tables/adminAuditLogs.d.ts +27 -0
- package/dist/db/tables/adminAuditLogs.d.ts.map +1 -0
- package/dist/db/tables/adminAuditLogs.js +19 -0
- package/dist/db/tables/adminAuditLogs.js.map +1 -0
- package/dist/db/tables/invitations.d.ts +1 -1
- package/dist/db/tables/invitations.d.ts.map +1 -1
- package/dist/db/tables/memberships.d.ts +32 -4
- package/dist/db/tables/memberships.d.ts.map +1 -1
- package/dist/db/tables/memberships.js +31 -0
- package/dist/db/tables/memberships.js.map +1 -1
- package/dist/db/tables/organizations.d.ts +43 -1
- package/dist/db/tables/organizations.d.ts.map +1 -1
- package/dist/db/tables/organizations.js +103 -0
- package/dist/db/tables/organizations.js.map +1 -1
- package/dist/db/tables/projects.d.ts +25 -0
- package/dist/db/tables/projects.d.ts.map +1 -1
- package/dist/db/tables/projects.js +22 -0
- package/dist/db/tables/projects.js.map +1 -1
- package/dist/db/tables/webhookDeliveries.d.ts +26 -0
- package/dist/db/tables/webhookDeliveries.d.ts.map +1 -1
- package/dist/db/tables/webhookDeliveries.js +37 -0
- package/dist/db/tables/webhookDeliveries.js.map +1 -1
- package/dist/handlers/relay.d.ts +13 -10
- package/dist/handlers/relay.d.ts.map +1 -1
- package/dist/handlers/relay.js +46 -16
- package/dist/handlers/relay.js.map +1 -1
- package/dist/internal/Auth.d.ts +13 -18
- package/dist/internal/Auth.d.ts.map +1 -1
- package/dist/internal/Auth.js +78 -58
- package/dist/internal/Auth.js.map +1 -1
- package/dist/internal/Cursor.d.ts +1 -1
- package/dist/internal/Cursor.d.ts.map +1 -1
- package/dist/internal/Cursor.js +4 -0
- package/dist/internal/Cursor.js.map +1 -1
- package/dist/internal/EdgeCache.d.ts.map +1 -1
- package/dist/internal/EdgeCache.js +2 -0
- package/dist/internal/EdgeCache.js.map +1 -1
- package/dist/internal/FundingRoutes.d.ts +170 -0
- package/dist/internal/FundingRoutes.d.ts.map +1 -1
- package/dist/internal/FundingRoutes.js +68 -6
- package/dist/internal/FundingRoutes.js.map +1 -1
- package/dist/internal/Log.d.ts +16 -1
- package/dist/internal/Log.d.ts.map +1 -1
- package/dist/internal/Log.js +5 -3
- package/dist/internal/Log.js.map +1 -1
- package/package.json +2 -2
- package/src/ApiKey.ts +2 -3
- package/src/ApiKeys.ts +95 -17
- package/src/App.test.ts +44 -2
- package/src/App.ts +29 -9
- package/src/Assets.ts +8 -0
- package/src/Auth.test-d.ts +36 -0
- package/src/Auth.ts +20 -1
- package/src/Client.test-d.ts +64 -17
- package/src/Scope.test.ts +34 -0
- package/src/Scope.ts +53 -17
- package/src/admin/App.test.ts +33 -3
- package/src/admin/App.ts +49 -14
- package/src/admin/apps/api-keys.ts +10 -3
- package/src/admin/apps/organizations.test.ts +455 -0
- package/src/admin/apps/organizations.ts +953 -0
- package/src/admin/apps/scopes.test.ts +10 -0
- package/src/admin/ui/plugin.ts +1 -1
- package/src/admin/ui/src/components/MobileProfile.tsx +2 -0
- package/src/admin/ui/src/components/ThemeToggle.tsx +20 -0
- package/src/admin/ui/src/lib/api-keys.ts +36 -19
- package/src/admin/ui/src/lib/config.ts +2 -3
- package/src/admin/ui/src/lib/organizations.test.ts +89 -0
- package/src/admin/ui/src/lib/organizations.ts +262 -0
- package/src/admin/ui/src/lib/query-client.ts +3 -1
- package/src/admin/ui/src/lib/theme.ts +57 -0
- package/src/admin/ui/src/routeTree.gen.ts +47 -4
- package/src/admin/ui/src/routes/__root.tsx +18 -3
- package/src/admin/ui/src/routes/api-keys.tsx +52 -7
- package/src/admin/ui/src/routes/index.tsx +1 -1
- package/src/admin/ui/src/routes/organizations.tsx +158 -0
- package/src/admin/ui/src/routes/organizations_.$orgId.tsx +710 -0
- package/src/admin/ui/src/start.ts +6 -0
- package/src/admin/ui.generated.ts +63 -13
- package/src/analytics/Analytics.ts +25 -0
- package/src/analytics/tables/requestEvents.ts +33 -0
- package/src/apps/composition.test.ts +82 -2
- package/src/apps/data/App.ts +24 -3
- package/src/apps/data/FundingRouteProviders.test.ts +204 -0
- package/src/apps/data/FundingRouteProviders.ts +294 -9
- package/src/apps/data/routes/funding.test.ts +90 -23
- package/src/apps/data/routes/funding.ts +315 -0
- package/src/apps/data/routes/rpc.test.ts +33 -0
- package/src/apps/management/App.ts +2 -2
- package/src/apps/management/Billing.ts +10 -2
- package/src/apps/management/routes/api-keys.test.ts +137 -1
- package/src/apps/management/routes/api-keys.ts +63 -38
- package/src/apps/management/routes/billing.ts +14 -14
- package/src/apps/management/routes/invitations.test.ts +102 -1
- package/src/apps/management/routes/invitations.ts +13 -15
- package/src/apps/management/routes/members.ts +10 -10
- package/src/apps/management/routes/orgs.test.ts +78 -16
- package/src/apps/management/routes/orgs.ts +21 -20
- package/src/apps/management/routes/projects.ts +12 -13
- package/src/apps/management/routes/scopes.test.ts +16 -0
- package/src/apps/management/routes/scopes.ts +6 -7
- package/src/apps/management/routes/usage.ts +9 -14
- package/src/apps/mcp.test.ts +25 -1
- package/src/apps/mcp.ts +43 -3
- package/src/apps/relay/App.test.ts +23 -18
- package/src/apps/relay/App.ts +35 -14
- package/src/db/Db.ts +59 -3
- package/src/db/Schema.ts +20 -1
- package/src/db/tables/adminAuditLogs.ts +47 -0
- package/src/db/tables/invitations.ts +1 -1
- package/src/db/tables/memberships.ts +59 -3
- package/src/db/tables/organizations.ts +148 -1
- package/src/db/tables/projects.ts +47 -0
- package/src/db/tables/webhookDeliveries.ts +83 -0
- package/src/handlers/relay.test-d.ts +8 -0
- package/src/handlers/relay.test.ts +2 -2
- package/src/handlers/relay.ts +62 -22
- package/src/internal/Auth.test.ts +174 -7
- package/src/internal/Auth.ts +90 -62
- package/src/internal/Cursor.test.ts +10 -0
- package/src/internal/Cursor.ts +5 -1
- package/src/internal/EdgeCache.test.ts +11 -0
- package/src/internal/EdgeCache.ts +2 -0
- package/src/internal/FundingRoutes.test.ts +26 -0
- package/src/internal/FundingRoutes.ts +187 -18
- package/src/internal/Log.test.ts +41 -3
- package/src/internal/Log.ts +23 -4
|
@@ -8,6 +8,7 @@ import * as FundingRoutes from '../../../internal/FundingRoutes.js'
|
|
|
8
8
|
import * as OpenApi from '../../../internal/OpenApi.js'
|
|
9
9
|
import * as Response from '../../../internal/Response.js'
|
|
10
10
|
import * as Schema from '../../../internal/Schema.js'
|
|
11
|
+
import * as FundingRouteProviders from '../FundingRouteProviders.js'
|
|
11
12
|
|
|
12
13
|
/** Zod schemas owned by the funding routes resource. */
|
|
13
14
|
export namespace schema {
|
|
@@ -268,6 +269,111 @@ export namespace schema {
|
|
|
268
269
|
'Provider-derived quote, limit, and freshness data for a route candidate.',
|
|
269
270
|
)
|
|
270
271
|
|
|
272
|
+
/** Token reference attached to a normalized quote fee. */
|
|
273
|
+
export const QuoteFeeToken = Schema.describe(
|
|
274
|
+
z.object({
|
|
275
|
+
address: z
|
|
276
|
+
.string()
|
|
277
|
+
.check(
|
|
278
|
+
z.describe('Contract address of the fee token.'),
|
|
279
|
+
z.meta({ examples: ['0x833589fcd6edb6e08f4c7c32d4f71b54bda02913'] }),
|
|
280
|
+
),
|
|
281
|
+
chainId: z
|
|
282
|
+
.string()
|
|
283
|
+
.check(z.describe('CAIP-2 chain id of the fee token.'), z.meta({ examples: ['eip155:8453'] })), // prettier-ignore
|
|
284
|
+
decimals: z
|
|
285
|
+
.number()
|
|
286
|
+
.check(
|
|
287
|
+
z.int(),
|
|
288
|
+
z.nonnegative(),
|
|
289
|
+
z.describe('Number of decimal places used by the fee token.'),
|
|
290
|
+
z.meta({ examples: [6] }),
|
|
291
|
+
),
|
|
292
|
+
symbol: z
|
|
293
|
+
.string()
|
|
294
|
+
.check(z.describe('Ticker symbol of the fee token.'), z.meta({ examples: ['USDC'] })),
|
|
295
|
+
}),
|
|
296
|
+
'Token in which a normalized quote fee is denominated.',
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
/** Normalized fee summary for an explicit funding quote. */
|
|
300
|
+
export const QuoteFee = Schema.describe(
|
|
301
|
+
z.object({
|
|
302
|
+
amount: Schema.DecimalString.check(
|
|
303
|
+
z.describe("Fee amount in the fee token's smallest unit."),
|
|
304
|
+
z.meta({ examples: ['1200'] }),
|
|
305
|
+
),
|
|
306
|
+
amountUsd: z
|
|
307
|
+
.nullable(z.string().check(z.regex(/^\d+(\.\d+)?$/)))
|
|
308
|
+
.check(
|
|
309
|
+
z.describe('Provider-estimated USD value of the fee.'),
|
|
310
|
+
z.meta({ examples: ['0.0012'] }),
|
|
311
|
+
),
|
|
312
|
+
bps: z
|
|
313
|
+
.nullable(z.number().check(z.int(), z.nonnegative()))
|
|
314
|
+
.check(z.describe('Fee as basis points of the quoted input.'), z.meta({ examples: [12] })),
|
|
315
|
+
token: QuoteFeeToken,
|
|
316
|
+
}),
|
|
317
|
+
'Provider-neutral fee amount and denomination.',
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
/** Explicit depositor-specific quote for one curated funding route. */
|
|
321
|
+
export const FundingQuote = Schema.describe(
|
|
322
|
+
z.object({
|
|
323
|
+
destinationChain: Chain.check(z.describe('Destination chain for the quoted route.')),
|
|
324
|
+
destinationToken: Token.check(z.describe('Token received on the destination chain.')),
|
|
325
|
+
estimatedSeconds: z
|
|
326
|
+
.nullable(z.number().check(z.int(), z.nonnegative()))
|
|
327
|
+
.check(z.describe('Estimated settlement time in seconds.'), z.meta({ examples: [2] })),
|
|
328
|
+
expiresAt: z.iso
|
|
329
|
+
.datetime()
|
|
330
|
+
.check(
|
|
331
|
+
z.describe('Timestamp after which the quote must not be used.'),
|
|
332
|
+
z.meta({ examples: ['2026-01-01T00:01:00Z'] }),
|
|
333
|
+
),
|
|
334
|
+
fees: z
|
|
335
|
+
.object({
|
|
336
|
+
total: QuoteFee.check(z.describe('Current estimated total fee.')),
|
|
337
|
+
totalMax: QuoteFee.check(z.describe('Maximum total fee allowed by the quote.')),
|
|
338
|
+
})
|
|
339
|
+
.check(z.describe('Current and maximum provider fee summaries.')),
|
|
340
|
+
inputAmount: Schema.DecimalString.check(
|
|
341
|
+
z.describe('Exact source amount quoted in base units.'),
|
|
342
|
+
z.meta({ examples: ['1000000'] }),
|
|
343
|
+
),
|
|
344
|
+
minimumOutputAmount: Schema.DecimalString.check(
|
|
345
|
+
z.describe('Slippage-adjusted minimum destination amount in base units.'),
|
|
346
|
+
z.meta({ examples: ['988000'] }),
|
|
347
|
+
),
|
|
348
|
+
outputAmount: Schema.DecimalString.check(
|
|
349
|
+
z.describe('Expected destination amount in base units.'),
|
|
350
|
+
z.meta({ examples: ['990000'] }),
|
|
351
|
+
),
|
|
352
|
+
outputAmountFormatted: z
|
|
353
|
+
.string()
|
|
354
|
+
.check(
|
|
355
|
+
z.describe('Expected destination amount rendered with token decimals.'),
|
|
356
|
+
z.meta({ examples: ['0.99'] }),
|
|
357
|
+
),
|
|
358
|
+
provider: ProviderRef.check(z.describe('Provider that produced the quote.')),
|
|
359
|
+
routeId: z
|
|
360
|
+
.string()
|
|
361
|
+
.check(
|
|
362
|
+
z.describe('Stable id of the curated route that was quoted.'),
|
|
363
|
+
z.meta({ examples: ['base-usdc-tempo-usdce-across'] }),
|
|
364
|
+
),
|
|
365
|
+
sampledAt: z.iso
|
|
366
|
+
.datetime()
|
|
367
|
+
.check(
|
|
368
|
+
z.describe('Timestamp when Tempo sampled the provider quote.'),
|
|
369
|
+
z.meta({ examples: ['2026-01-01T00:00:00Z'] }),
|
|
370
|
+
),
|
|
371
|
+
sourceChain: Chain.check(z.describe('Source chain for the quoted route.')),
|
|
372
|
+
sourceToken: Token.check(z.describe('Token spent on the source chain.')),
|
|
373
|
+
}),
|
|
374
|
+
'Normalized quote information without provider execution payloads.',
|
|
375
|
+
)
|
|
376
|
+
|
|
271
377
|
/** Static or live funding route candidate. */
|
|
272
378
|
export const RouteCandidate = Schema.describe(
|
|
273
379
|
z.object({
|
|
@@ -422,6 +528,63 @@ export namespace schema {
|
|
|
422
528
|
)
|
|
423
529
|
}
|
|
424
530
|
|
|
531
|
+
/** Schemas for the quoteFundingRoute operation. */
|
|
532
|
+
export namespace quoteFundingRoute {
|
|
533
|
+
/** JSON body for an explicit funding route quote. */
|
|
534
|
+
export const Body = z
|
|
535
|
+
.strictObject({
|
|
536
|
+
depositor: Schema.Address.check(
|
|
537
|
+
z.describe('Address that would submit the funding transaction.'),
|
|
538
|
+
),
|
|
539
|
+
destinationToken: z
|
|
540
|
+
.optional(z.string())
|
|
541
|
+
.check(
|
|
542
|
+
z.describe('Tempo destination token symbol, address, or token key.'),
|
|
543
|
+
z.meta({ examples: ['USDC.e'] }),
|
|
544
|
+
),
|
|
545
|
+
provider: z
|
|
546
|
+
.literal('across')
|
|
547
|
+
.check(
|
|
548
|
+
z.describe('Provider id selected from funding route discovery.'),
|
|
549
|
+
z.meta({ examples: ['across'] }),
|
|
550
|
+
),
|
|
551
|
+
recipient: z
|
|
552
|
+
.optional(Schema.Address)
|
|
553
|
+
.check(z.describe('Optional destination recipient; defaults to the depositor.')),
|
|
554
|
+
sourceAmount: z
|
|
555
|
+
.string()
|
|
556
|
+
.check(
|
|
557
|
+
z.minLength(1),
|
|
558
|
+
z.describe('Exact source amount to quote.'),
|
|
559
|
+
z.meta({ examples: ['1000000'] }),
|
|
560
|
+
),
|
|
561
|
+
sourceAmountUnits: z
|
|
562
|
+
._default(z.enum(['baseUnits', 'decimal']), 'baseUnits')
|
|
563
|
+
.check(
|
|
564
|
+
z.describe('Whether `sourceAmount` uses base units or decimal notation.'),
|
|
565
|
+
z.meta({ examples: ['baseUnits'] }),
|
|
566
|
+
),
|
|
567
|
+
sourceChain: z
|
|
568
|
+
.string()
|
|
569
|
+
.check(
|
|
570
|
+
z.minLength(1),
|
|
571
|
+
z.describe('Source chain alias or CAIP-2 id.'),
|
|
572
|
+
z.meta({ examples: ['base'] }),
|
|
573
|
+
),
|
|
574
|
+
sourceToken: z
|
|
575
|
+
.string()
|
|
576
|
+
.check(
|
|
577
|
+
z.minLength(1),
|
|
578
|
+
z.describe('Source token symbol, address, mint, or token key.'),
|
|
579
|
+
z.meta({ examples: ['USDC'] }),
|
|
580
|
+
),
|
|
581
|
+
})
|
|
582
|
+
.check(z.describe('Parameters for a depositor-specific exact-input funding quote.'))
|
|
583
|
+
|
|
584
|
+
/** Normalized funding quote without provider execution payloads. */
|
|
585
|
+
export const Response = FundingQuote
|
|
586
|
+
}
|
|
587
|
+
|
|
425
588
|
/** Schemas for the listFundingChains operation. */
|
|
426
589
|
export namespace listFundingChains {
|
|
427
590
|
/** Non-paginated source-chain inventory. */
|
|
@@ -518,6 +681,56 @@ const providersResponseExample = {
|
|
|
518
681
|
data: FundingRoutes.listProviders(),
|
|
519
682
|
} satisfies z.output<typeof schema.listProviders.Response>
|
|
520
683
|
|
|
684
|
+
const [fundingQuoteRouteExample] = FundingRoutes.listStaticRoutes({
|
|
685
|
+
destinationToken: 'USDC.e',
|
|
686
|
+
limit: 1,
|
|
687
|
+
provider: 'across',
|
|
688
|
+
sourceAmount: '1000000',
|
|
689
|
+
sourceAmountUnits: 'baseUnits',
|
|
690
|
+
sourceChain: 'base',
|
|
691
|
+
sourceToken: 'USDC',
|
|
692
|
+
})
|
|
693
|
+
|
|
694
|
+
const fundingQuoteResponseExample = {
|
|
695
|
+
destinationChain: fundingQuoteRouteExample!.destinationChain,
|
|
696
|
+
destinationToken: fundingQuoteRouteExample!.destinationToken,
|
|
697
|
+
estimatedSeconds: 2,
|
|
698
|
+
expiresAt: '2026-01-01T00:01:00Z',
|
|
699
|
+
fees: {
|
|
700
|
+
total: {
|
|
701
|
+
amount: '1200',
|
|
702
|
+
amountUsd: '0.0012',
|
|
703
|
+
bps: 12,
|
|
704
|
+
token: {
|
|
705
|
+
address: fundingQuoteRouteExample!.sourceToken.address,
|
|
706
|
+
chainId: fundingQuoteRouteExample!.sourceChain.id,
|
|
707
|
+
decimals: fundingQuoteRouteExample!.sourceToken.decimals,
|
|
708
|
+
symbol: fundingQuoteRouteExample!.sourceToken.symbol,
|
|
709
|
+
},
|
|
710
|
+
},
|
|
711
|
+
totalMax: {
|
|
712
|
+
amount: '1800',
|
|
713
|
+
amountUsd: '0.0018',
|
|
714
|
+
bps: 18,
|
|
715
|
+
token: {
|
|
716
|
+
address: fundingQuoteRouteExample!.sourceToken.address,
|
|
717
|
+
chainId: fundingQuoteRouteExample!.sourceChain.id,
|
|
718
|
+
decimals: fundingQuoteRouteExample!.sourceToken.decimals,
|
|
719
|
+
symbol: fundingQuoteRouteExample!.sourceToken.symbol,
|
|
720
|
+
},
|
|
721
|
+
},
|
|
722
|
+
},
|
|
723
|
+
inputAmount: '1000000',
|
|
724
|
+
minimumOutputAmount: '988000',
|
|
725
|
+
outputAmount: '990000',
|
|
726
|
+
outputAmountFormatted: '0.99',
|
|
727
|
+
provider: fundingQuoteRouteExample!.provider,
|
|
728
|
+
routeId: fundingQuoteRouteExample!.id,
|
|
729
|
+
sampledAt: '2026-01-01T00:00:00Z',
|
|
730
|
+
sourceChain: fundingQuoteRouteExample!.sourceChain,
|
|
731
|
+
sourceToken: fundingQuoteRouteExample!.sourceToken,
|
|
732
|
+
} satisfies z.output<typeof schema.quoteFundingRoute.Response>
|
|
733
|
+
|
|
521
734
|
/** Mounts funding route discovery and generic provider inventory endpoints. */
|
|
522
735
|
export function funding(options: funding.Options = {}) {
|
|
523
736
|
return new Hono<App.Environment>()
|
|
@@ -585,6 +798,103 @@ export function funding(options: funding.Options = {}) {
|
|
|
585
798
|
}
|
|
586
799
|
},
|
|
587
800
|
)
|
|
801
|
+
.post(
|
|
802
|
+
'/v1/funding/routes/quote',
|
|
803
|
+
Auth.policy({ apiKey: { scopes: ['data:read'] }, mpp: true, public: true }),
|
|
804
|
+
OpenApi.validate('json', schema.quoteFundingRoute.Body, {
|
|
805
|
+
code: 'body_invalid',
|
|
806
|
+
message: 'Invalid quote parameters',
|
|
807
|
+
}),
|
|
808
|
+
OpenApi.describeRoute({
|
|
809
|
+
description:
|
|
810
|
+
'Gets a depositor-specific exact-input quote for one curated funding route. The response excludes provider transaction calldata, approvals, and wallet balance details.',
|
|
811
|
+
operationId: 'quoteFundingRoute',
|
|
812
|
+
responses: OpenApi.responses({
|
|
813
|
+
errors: {
|
|
814
|
+
400: { codes: ['body_invalid'] },
|
|
815
|
+
404: { codes: ['route_not_found', 'quote_unavailable'] },
|
|
816
|
+
501: { codes: ['provider_unconfigured'] },
|
|
817
|
+
502: { codes: ['provider_error', 'provider_rate_limited', 'provider_timeout'] },
|
|
818
|
+
},
|
|
819
|
+
success: {
|
|
820
|
+
description: 'Normalized route quote without provider execution payloads.',
|
|
821
|
+
example: fundingQuoteResponseExample,
|
|
822
|
+
schema: schema.quoteFundingRoute.Response,
|
|
823
|
+
},
|
|
824
|
+
}),
|
|
825
|
+
summary: 'Quote funding route',
|
|
826
|
+
tags: ['Funding'],
|
|
827
|
+
}),
|
|
828
|
+
async (c) => {
|
|
829
|
+
if (Auth.narrowAccess) return Auth.paidAccessError(c)
|
|
830
|
+
if (OpenApi.narrowValidation)
|
|
831
|
+
return OpenApi.validationError(c, {
|
|
832
|
+
code: 'body_invalid',
|
|
833
|
+
message: 'Invalid quote parameters',
|
|
834
|
+
})
|
|
835
|
+
|
|
836
|
+
const body = c.req.valid('json')
|
|
837
|
+
try {
|
|
838
|
+
const quote = await FundingRoutes.quoteRoute({
|
|
839
|
+
depositor: body.depositor,
|
|
840
|
+
destinationToken: body.destinationToken,
|
|
841
|
+
provider: body.provider,
|
|
842
|
+
quote: options.acrossQuote,
|
|
843
|
+
quoteTimeoutMs: options.quoteTimeoutMs,
|
|
844
|
+
recipient: body.recipient,
|
|
845
|
+
sourceAmount: body.sourceAmount,
|
|
846
|
+
sourceAmountUnits: body.sourceAmountUnits,
|
|
847
|
+
sourceChain: body.sourceChain,
|
|
848
|
+
sourceToken: body.sourceToken,
|
|
849
|
+
})
|
|
850
|
+
c.header('Cache-Control', 'no-store')
|
|
851
|
+
return c.json(Response.validated(schema.quoteFundingRoute.Response, quote), 200)
|
|
852
|
+
} catch (cause) {
|
|
853
|
+
const message = requestErrorMessage(cause)
|
|
854
|
+
if (message) return Response.error(c, { code: 'body_invalid', message, status: 400 })
|
|
855
|
+
if (cause instanceof FundingRoutes.FundingRouteNotFoundError)
|
|
856
|
+
return Response.error(c, {
|
|
857
|
+
code: 'route_not_found',
|
|
858
|
+
message: 'No curated funding route matches the request.',
|
|
859
|
+
status: 404,
|
|
860
|
+
})
|
|
861
|
+
if (cause instanceof FundingRoutes.ProviderQuoteUnavailableError)
|
|
862
|
+
return Response.error(c, {
|
|
863
|
+
code: 'quote_unavailable',
|
|
864
|
+
message: 'The provider cannot quote this route.',
|
|
865
|
+
status: 404,
|
|
866
|
+
})
|
|
867
|
+
if (cause instanceof FundingRoutes.ProviderNotConfiguredError)
|
|
868
|
+
return Response.error(c, {
|
|
869
|
+
code: 'provider_unconfigured',
|
|
870
|
+
message: 'The provider quote integration is not configured.',
|
|
871
|
+
status: 501,
|
|
872
|
+
})
|
|
873
|
+
if (cause instanceof FundingRoutes.ProviderRateLimitError)
|
|
874
|
+
return Response.error(c, {
|
|
875
|
+
code: 'provider_rate_limited',
|
|
876
|
+
message: 'The provider temporarily rate-limited this quote.',
|
|
877
|
+
status: 502,
|
|
878
|
+
})
|
|
879
|
+
if (cause instanceof FundingRoutes.ProviderAdapterTimeoutError)
|
|
880
|
+
return Response.error(c, {
|
|
881
|
+
code: 'provider_timeout',
|
|
882
|
+
message: 'The provider quote timed out.',
|
|
883
|
+
status: 502,
|
|
884
|
+
})
|
|
885
|
+
if (cause instanceof Error) {
|
|
886
|
+
const providerFailure = FundingRouteProviders.acrossQuoteFailure({ cause })
|
|
887
|
+
if (providerFailure) c.set('providerFailure', providerFailure)
|
|
888
|
+
return Response.error(c, {
|
|
889
|
+
code: 'provider_error',
|
|
890
|
+
message: 'The provider could not complete the quote.',
|
|
891
|
+
status: 502,
|
|
892
|
+
})
|
|
893
|
+
}
|
|
894
|
+
throw cause
|
|
895
|
+
}
|
|
896
|
+
},
|
|
897
|
+
)
|
|
588
898
|
.get(
|
|
589
899
|
'/v1/funding/chains',
|
|
590
900
|
Auth.policy({ apiKey: { scopes: ['data:read'] }, mpp: true, public: true }),
|
|
@@ -658,6 +968,7 @@ export function funding(options: funding.Options = {}) {
|
|
|
658
968
|
data: FundingRoutes.listProviders({
|
|
659
969
|
adapters: options.adapters,
|
|
660
970
|
capability: query.capability,
|
|
971
|
+
configuredProviders: options.acrossQuote ? ['across'] : [],
|
|
661
972
|
}),
|
|
662
973
|
}),
|
|
663
974
|
200,
|
|
@@ -669,10 +980,14 @@ export function funding(options: funding.Options = {}) {
|
|
|
669
980
|
export declare namespace funding {
|
|
670
981
|
/** Options for funding route discovery. */
|
|
671
982
|
type Options = {
|
|
983
|
+
/** Across explicit quote capability. */
|
|
984
|
+
acrossQuote?: FundingRoutes.ProviderQuote | undefined
|
|
672
985
|
/** Provider adapters used to enrich static inventory with live data. */
|
|
673
986
|
adapters?: FundingRoutes.ProviderAdapters | undefined
|
|
674
987
|
/** Per-provider quote timeout in milliseconds. */
|
|
675
988
|
adapterTimeoutMs?: number | undefined
|
|
989
|
+
/** Timeout for depositor-specific provider quotes. */
|
|
990
|
+
quoteTimeoutMs?: number | undefined
|
|
676
991
|
}
|
|
677
992
|
}
|
|
678
993
|
|
|
@@ -338,6 +338,39 @@ describe('POST /rpc', () => {
|
|
|
338
338
|
fetch.restore()
|
|
339
339
|
}
|
|
340
340
|
})
|
|
341
|
+
|
|
342
|
+
test('uses the RPC path for sandbox chain checks', async () => {
|
|
343
|
+
const app = TestApp.create({
|
|
344
|
+
auth: {
|
|
345
|
+
keys: [
|
|
346
|
+
{
|
|
347
|
+
environment: 'sandbox',
|
|
348
|
+
id: 'key_sandbox',
|
|
349
|
+
orgId: 'org_test',
|
|
350
|
+
scopes: ['data:read'],
|
|
351
|
+
token: 'secret_sandbox_key',
|
|
352
|
+
},
|
|
353
|
+
],
|
|
354
|
+
},
|
|
355
|
+
defaultChainId: 4217,
|
|
356
|
+
})
|
|
357
|
+
const request = (path: string) =>
|
|
358
|
+
app.fetch(
|
|
359
|
+
new Request(`http://tempo-api.test${path}`, {
|
|
360
|
+
body: JSON.stringify({ id: 1, jsonrpc: '2.0', method: 'eth_chainId' }),
|
|
361
|
+
headers: { 'tempo-api-key': 'secret_sandbox_key' },
|
|
362
|
+
method: 'POST',
|
|
363
|
+
}),
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
const rejected = await request('/rpc/4217?chainId=42431')
|
|
367
|
+
const rejectedDefault = await request('/rpc?chainId=42431')
|
|
368
|
+
const rejectedTrailing = await request('/rpc/?chainId=42431')
|
|
369
|
+
|
|
370
|
+
expect(rejected.status).toMatchInlineSnapshot(`403`)
|
|
371
|
+
expect(rejectedDefault.status).toMatchInlineSnapshot(`403`)
|
|
372
|
+
expect(rejectedTrailing.status).toMatchInlineSnapshot(`404`)
|
|
373
|
+
})
|
|
341
374
|
})
|
|
342
375
|
|
|
343
376
|
describe('POST /rpc/relay', () => {
|
|
@@ -87,8 +87,8 @@ export type Environment = {
|
|
|
87
87
|
* invitations, and API-key management. Every route reads its dependencies from
|
|
88
88
|
* request context set by {@link App.create}.
|
|
89
89
|
*
|
|
90
|
-
* The session-auth surface
|
|
91
|
-
*
|
|
90
|
+
* The session-auth surface is owned by {@link App.create}; management routes
|
|
91
|
+
* reference it alongside scoped API-key access.
|
|
92
92
|
*
|
|
93
93
|
* ```ts
|
|
94
94
|
* App.create(options).route('/', management())
|
|
@@ -7,6 +7,7 @@ import type * as BillingSettings from '../../db/tables/billingSettings.js'
|
|
|
7
7
|
import * as Db from '../../db/Db.js'
|
|
8
8
|
import * as Fees from '../../internal/Fees.js'
|
|
9
9
|
import * as RequestUsage from '../../db/tables/requestUsage.js'
|
|
10
|
+
import type * as Scope from '../../Scope.js'
|
|
10
11
|
import * as SponsoredTransactions from '../../db/tables/sponsoredTransactions.js'
|
|
11
12
|
import * as StripeCustomers from '../../db/tables/stripeCustomers.js'
|
|
12
13
|
import * as Viem from '../../internal/Viem.js'
|
|
@@ -641,7 +642,7 @@ export async function listActive(
|
|
|
641
642
|
export function createKeyBillingSyncer(
|
|
642
643
|
options: createKeyBillingSyncer.Options,
|
|
643
644
|
): createKeyBillingSyncer.Syncer {
|
|
644
|
-
const { db, environments = ['sandbox'], kv } = options
|
|
645
|
+
const { db, environments = ['sandbox'], kv, scopeCatalog } = options
|
|
645
646
|
return {
|
|
646
647
|
async tick() {
|
|
647
648
|
// Resolve per tick so Workers factories build a fresh Hyperdrive-pooled
|
|
@@ -655,7 +656,12 @@ export function createKeyBillingSyncer(
|
|
|
655
656
|
checked += 1
|
|
656
657
|
// `setBillingActive` writes only drifted records, so a converged
|
|
657
658
|
// fleet costs list reads and no writes.
|
|
658
|
-
updated += await ApiKeys.setBillingActive(kv, {
|
|
659
|
+
updated += await ApiKeys.setBillingActive(kv, {
|
|
660
|
+
active,
|
|
661
|
+
environment,
|
|
662
|
+
orgId,
|
|
663
|
+
scopeCatalog,
|
|
664
|
+
})
|
|
659
665
|
}
|
|
660
666
|
}
|
|
661
667
|
return { checked, updated }
|
|
@@ -672,6 +678,8 @@ export declare namespace createKeyBillingSyncer {
|
|
|
672
678
|
environments?: readonly StripeCustomers.Record['environment'][] | undefined
|
|
673
679
|
/** KV state store holding API-key records. */
|
|
674
680
|
kv: Store.State
|
|
681
|
+
/** Scope catalog accepted while reconciling API-key records. */
|
|
682
|
+
scopeCatalog?: Scope.Catalog | undefined
|
|
675
683
|
}
|
|
676
684
|
|
|
677
685
|
/** A scheduled snapshot-reconcile job. */
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts'
|
|
2
2
|
|
|
3
3
|
import * as ApiKeys from '../../../ApiKeys.js'
|
|
4
|
+
import * as Auth from '../../../internal/Auth.js'
|
|
5
|
+
import * as OpenApi from '../../../internal/OpenApi.js'
|
|
6
|
+
import * as Scope from '../../../Scope.js'
|
|
4
7
|
import * as TestApp from '../../../../test/App.js'
|
|
8
|
+
import * as Memberships from '../../../db/tables/memberships.js'
|
|
5
9
|
import * as Keys from './api-keys.js'
|
|
10
|
+
import * as Me from './me.js'
|
|
6
11
|
import * as Orgs from './orgs.js'
|
|
7
12
|
import * as Projects from './projects.js'
|
|
8
13
|
|
|
@@ -33,10 +38,18 @@ function superAdminJson(method: 'PATCH' | 'POST', body: unknown) {
|
|
|
33
38
|
// Resolve-cache disabled so revocation assertions observe the store directly.
|
|
34
39
|
type State = ReturnType<typeof TestApp.kvStore>
|
|
35
40
|
|
|
36
|
-
function createApp(
|
|
41
|
+
function createApp(
|
|
42
|
+
options: {
|
|
43
|
+
db?: ReturnType<typeof TestApp.database>
|
|
44
|
+
kv?: { store: State }
|
|
45
|
+
scopes?: readonly Scope.Entry[]
|
|
46
|
+
} = {},
|
|
47
|
+
) {
|
|
37
48
|
return TestApp.create({
|
|
38
49
|
auth: { cache: false, superAdmin: { secret } },
|
|
50
|
+
db: options.db ?? TestApp.database(),
|
|
39
51
|
...(options.kv ? { kv: options.kv } : {}),
|
|
52
|
+
...(options.scopes ? { scopes: options.scopes } : {}),
|
|
40
53
|
session: { wallet: { origin } },
|
|
41
54
|
})
|
|
42
55
|
}
|
|
@@ -70,6 +83,77 @@ async function fixture(app: ReturnType<typeof createApp>, cookie: string) {
|
|
|
70
83
|
}
|
|
71
84
|
|
|
72
85
|
describe('keys', () => {
|
|
86
|
+
test('requires an owner to issue management write keys', async () => {
|
|
87
|
+
const db = TestApp.database()
|
|
88
|
+
const app = createApp({ db })
|
|
89
|
+
const owner = await session(app)
|
|
90
|
+
const { org, project } = await fixture(app, owner)
|
|
91
|
+
const admin = await session(app)
|
|
92
|
+
const user = await TestApp.json(
|
|
93
|
+
await app.request('/v1/me', { headers: { cookie: admin } }),
|
|
94
|
+
Me.schema.User,
|
|
95
|
+
)
|
|
96
|
+
await Memberships.create(db, {
|
|
97
|
+
orgId: org.id,
|
|
98
|
+
role: 'admin',
|
|
99
|
+
userId: user.id,
|
|
100
|
+
})
|
|
101
|
+
const base = `/v1/orgs/${org.id}/projects/${project.id}/api-keys`
|
|
102
|
+
|
|
103
|
+
const read = await app.request(base, json('POST', admin, { scopes: ['management:read'] }))
|
|
104
|
+
expect(read.status).toBe(200)
|
|
105
|
+
|
|
106
|
+
const denied = await app.request(base, json('POST', admin, { scopes: ['management:write'] }))
|
|
107
|
+
expect(denied.status).toBe(403)
|
|
108
|
+
expect(await denied.json()).toMatchObject({ error: { code: 'scope_not_issuable' } })
|
|
109
|
+
|
|
110
|
+
const write = await app.request(base, json('POST', owner, { scopes: ['management:write'] }))
|
|
111
|
+
expect(write.status).toBe(200)
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
test('lets management keys manage org credentials without delegating issuance', async () => {
|
|
115
|
+
const app = createApp()
|
|
116
|
+
const cookie = await session(app)
|
|
117
|
+
const { org, project } = await fixture(app, cookie)
|
|
118
|
+
const other = await TestApp.json(
|
|
119
|
+
await app.request(`/v1/orgs/${org.id}/projects`, json('POST', cookie, { name: 'Payments' })),
|
|
120
|
+
Projects.schema.Project,
|
|
121
|
+
)
|
|
122
|
+
const base = `/v1/orgs/${org.id}/projects/${project.id}/api-keys`
|
|
123
|
+
const siblingBase = `/v1/orgs/${org.id}/projects/${other.id}/api-keys`
|
|
124
|
+
const manager = await TestApp.json(
|
|
125
|
+
await app.request(
|
|
126
|
+
base,
|
|
127
|
+
superAdminJson('POST', {
|
|
128
|
+
scopes: ['management:read', 'management:write'],
|
|
129
|
+
}),
|
|
130
|
+
),
|
|
131
|
+
Keys.schema.createApiKey.Response,
|
|
132
|
+
)
|
|
133
|
+
const target = await TestApp.json(
|
|
134
|
+
await app.request(siblingBase, superAdminJson('POST', { scopes: ['data:read'] })),
|
|
135
|
+
Keys.schema.createApiKey.Response,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
const listed = await app.request(siblingBase, {
|
|
139
|
+
headers: { 'tempo-api-key': manager.token },
|
|
140
|
+
})
|
|
141
|
+
expect(listed.status).toBe(200)
|
|
142
|
+
|
|
143
|
+
const delegated = await app.request(base, {
|
|
144
|
+
body: JSON.stringify({ scopes: ['management:read'] }),
|
|
145
|
+
headers: { 'content-type': 'application/json', 'tempo-api-key': manager.token },
|
|
146
|
+
method: 'POST',
|
|
147
|
+
})
|
|
148
|
+
expect(delegated.status).toBe(403)
|
|
149
|
+
|
|
150
|
+
const revoked = await app.request(`${siblingBase}/${target.id}`, {
|
|
151
|
+
headers: { 'tempo-api-key': manager.token },
|
|
152
|
+
method: 'DELETE',
|
|
153
|
+
})
|
|
154
|
+
expect(revoked.status).toBe(200)
|
|
155
|
+
})
|
|
156
|
+
|
|
73
157
|
test('mints, lists, authenticates, and revokes a project key', async () => {
|
|
74
158
|
const app = createApp()
|
|
75
159
|
const cookie = await session(app)
|
|
@@ -187,6 +271,58 @@ describe('keys', () => {
|
|
|
187
271
|
expect(created.createdBy).not.toBe('super_admin')
|
|
188
272
|
})
|
|
189
273
|
|
|
274
|
+
test('mints and authenticates host scopes', async () => {
|
|
275
|
+
const app = createApp({
|
|
276
|
+
scopes: [
|
|
277
|
+
{
|
|
278
|
+
description: 'Read project accounts.',
|
|
279
|
+
scope: 'accounts:read',
|
|
280
|
+
selfServe: true,
|
|
281
|
+
},
|
|
282
|
+
],
|
|
283
|
+
}).get(
|
|
284
|
+
'/custom/accounts',
|
|
285
|
+
Auth.policy({ apiKey: { scopes: ['accounts:read'] } }),
|
|
286
|
+
OpenApi.describeRoute({
|
|
287
|
+
operationId: 'getCustomAccounts',
|
|
288
|
+
responses: { 200: { description: 'Custom accounts response.' } },
|
|
289
|
+
summary: 'Get custom accounts',
|
|
290
|
+
}),
|
|
291
|
+
(c) => c.json({ ok: true }, 200),
|
|
292
|
+
)
|
|
293
|
+
const cookie = await session(app)
|
|
294
|
+
const { org, project } = await fixture(app, cookie)
|
|
295
|
+
|
|
296
|
+
const created = await TestApp.json(
|
|
297
|
+
await app.request(
|
|
298
|
+
`/v1/orgs/${org.id}/projects/${project.id}/api-keys`,
|
|
299
|
+
json('POST', cookie, { scopes: ['accounts:read'] }),
|
|
300
|
+
),
|
|
301
|
+
Keys.schema.createApiKey.Response,
|
|
302
|
+
)
|
|
303
|
+
const response = await app.request('/custom/accounts', {
|
|
304
|
+
headers: { 'tempo-api-key': created.token },
|
|
305
|
+
})
|
|
306
|
+
|
|
307
|
+
expect(created.scopes).toEqual(['accounts:read'])
|
|
308
|
+
expect(response.status).toBe(200)
|
|
309
|
+
expect(await response.json()).toEqual({ ok: true })
|
|
310
|
+
})
|
|
311
|
+
|
|
312
|
+
test('rejects unregistered host scopes', async () => {
|
|
313
|
+
const app = createApp()
|
|
314
|
+
const cookie = await session(app)
|
|
315
|
+
const { org, project } = await fixture(app, cookie)
|
|
316
|
+
|
|
317
|
+
const response = await app.request(
|
|
318
|
+
`/v1/orgs/${org.id}/projects/${project.id}/api-keys`,
|
|
319
|
+
superAdminJson('POST', { scopes: ['accounts:read'] }),
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
expect(response.status).toBe(400)
|
|
323
|
+
expect(await response.json()).toMatchObject({ error: { code: 'body_invalid' } })
|
|
324
|
+
})
|
|
325
|
+
|
|
190
326
|
test('rejects session mints of non-self-serve scopes (webhooks)', async () => {
|
|
191
327
|
const app = createApp()
|
|
192
328
|
const cookie = await session(app)
|