tapimo 0.5.0 → 0.5.2
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/dist/App.d.ts +4854 -4829
- package/dist/App.d.ts.map +1 -1
- package/dist/App.js +20 -2
- package/dist/App.js.map +1 -1
- package/dist/Assets.js +3 -3
- package/dist/Assets.js.map +1 -1
- package/dist/Scope.d.ts.map +1 -1
- package/dist/Scope.js +1 -2
- package/dist/Scope.js.map +1 -1
- package/dist/Webhooks.d.ts +1 -1
- package/dist/Webhooks.d.ts.map +1 -1
- package/dist/Webhooks.js +5 -10
- package/dist/Webhooks.js.map +1 -1
- package/dist/admin/App.d.ts +6 -34
- package/dist/admin/App.d.ts.map +1 -1
- package/dist/admin/apps/api-keys.d.ts +3 -3
- package/dist/admin/apps/api-keys.d.ts.map +1 -1
- package/dist/admin/apps/api-keys.js +4 -1
- package/dist/admin/apps/api-keys.js.map +1 -1
- package/dist/admin/apps/scopes.d.ts +6 -34
- package/dist/admin/apps/scopes.d.ts.map +1 -1
- package/dist/admin/apps/scopes.js +2 -2
- package/dist/admin/apps/scopes.js.map +1 -1
- package/dist/admin/apps/verified-tokens.d.ts +2 -2
- package/dist/admin/ui.generated.d.ts +1 -1
- package/dist/admin/ui.generated.js +33 -33
- package/dist/admin/ui.generated.js.map +1 -1
- package/dist/apps/data/App.d.ts +1398 -1405
- package/dist/apps/data/App.d.ts.map +1 -1
- package/dist/apps/data/App.js +25 -38
- package/dist/apps/data/App.js.map +1 -1
- package/dist/apps/data/FundingQuoteProviders.d.ts +108 -3
- package/dist/apps/data/FundingQuoteProviders.d.ts.map +1 -1
- package/dist/apps/data/FundingQuoteProviders.js +409 -2
- package/dist/apps/data/FundingQuoteProviders.js.map +1 -1
- package/dist/apps/data/routes/activities.d.ts +245 -245
- package/dist/apps/data/routes/blocks.d.ts +123 -123
- package/dist/apps/data/routes/funding.d.ts +236 -146
- package/dist/apps/data/routes/funding.d.ts.map +1 -1
- package/dist/apps/data/routes/funding.js +23 -12
- package/dist/apps/data/routes/funding.js.map +1 -1
- package/dist/apps/data/routes/indexer.d.ts.map +1 -1
- package/dist/apps/data/routes/indexer.js +9 -2
- package/dist/apps/data/routes/indexer.js.map +1 -1
- package/dist/apps/data/routes/rpc.d.ts +31 -31
- package/dist/apps/data/routes/tokenlist.d.ts +36 -36
- package/dist/apps/data/routes/transactions.d.ts +281 -281
- package/dist/apps/data/routes/transfers.d.ts +66 -73
- package/dist/apps/data/routes/transfers.d.ts.map +1 -1
- package/dist/apps/data/routes/transfers.js +21 -27
- package/dist/apps/data/routes/transfers.js.map +1 -1
- package/dist/apps/data/routes/verified-tokens.d.ts +86 -86
- package/dist/apps/data/routes/webhooks.d.ts +35 -35
- package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
- package/dist/apps/data/routes/webhooks.js +176 -42
- package/dist/apps/data/routes/webhooks.js.map +1 -1
- package/dist/apps/management/App.d.ts +520 -504
- package/dist/apps/management/App.d.ts.map +1 -1
- package/dist/apps/management/Billing.d.ts.map +1 -1
- package/dist/apps/management/Billing.js +5 -4
- package/dist/apps/management/Billing.js.map +1 -1
- package/dist/apps/management/routes/api-keys.d.ts +120 -120
- package/dist/apps/management/routes/billing.d.ts +211 -211
- package/dist/apps/management/routes/billing.d.ts.map +1 -1
- package/dist/apps/management/routes/billing.js +1 -1
- package/dist/apps/management/routes/billing.js.map +1 -1
- package/dist/apps/management/routes/invitations.d.ts +72 -72
- package/dist/apps/management/routes/invite-links.d.ts +159 -159
- package/dist/apps/management/routes/me.d.ts +17 -17
- package/dist/apps/management/routes/members.d.ts +80 -80
- package/dist/apps/management/routes/orgs.d.ts +59 -43
- package/dist/apps/management/routes/orgs.d.ts.map +1 -1
- package/dist/apps/management/routes/orgs.js +18 -1
- package/dist/apps/management/routes/orgs.js.map +1 -1
- package/dist/apps/management/routes/projects.d.ts +109 -109
- package/dist/apps/management/routes/scopes.d.ts +11 -11
- package/dist/apps/management/routes/usage.d.ts +79 -79
- package/dist/apps/mcp.js +2 -2
- package/dist/apps/mcp.js.map +1 -1
- package/dist/apps/relay/App.d.ts +2 -2
- package/dist/apps/relay/App.js +2 -2
- package/dist/apps/relay/Sponsorships.d.ts +3 -0
- package/dist/apps/relay/Sponsorships.d.ts.map +1 -1
- package/dist/apps/relay/Sponsorships.js +15 -1
- package/dist/apps/relay/Sponsorships.js.map +1 -1
- package/dist/db/Db.d.ts.map +1 -1
- package/dist/db/Db.js +4 -0
- package/dist/db/Db.js.map +1 -1
- package/dist/db/Schema.js +1 -1
- package/dist/db/Schema.js.map +1 -1
- package/dist/db/tables/organizations.d.ts +6 -0
- package/dist/db/tables/organizations.d.ts.map +1 -1
- package/dist/db/tables/organizations.js +26 -0
- package/dist/db/tables/organizations.js.map +1 -1
- package/dist/db/tables/requestUsage.d.ts +2 -11
- package/dist/db/tables/requestUsage.d.ts.map +1 -1
- package/dist/db/tables/requestUsage.js +1 -20
- package/dist/db/tables/requestUsage.js.map +1 -1
- package/dist/db/tables/sponsoredTransactions.d.ts +2 -1
- package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -1
- package/dist/db/tables/sponsoredTransactions.js +52 -10
- package/dist/db/tables/sponsoredTransactions.js.map +1 -1
- package/dist/internal/Auth.d.ts +4 -0
- package/dist/internal/Auth.d.ts.map +1 -1
- package/dist/internal/Auth.js +15 -8
- package/dist/internal/Auth.js.map +1 -1
- package/dist/internal/Cache.d.ts.map +1 -1
- package/dist/internal/Cache.js +9 -1
- package/dist/internal/Cache.js.map +1 -1
- package/dist/internal/EdgeCache.d.ts.map +1 -1
- package/dist/internal/EdgeCache.js +12 -3
- package/dist/internal/EdgeCache.js.map +1 -1
- package/dist/internal/FundingQuotes.d.ts +176 -56
- package/dist/internal/FundingQuotes.d.ts.map +1 -1
- package/dist/internal/FundingQuotes.js +100 -5
- package/dist/internal/FundingQuotes.js.map +1 -1
- package/dist/internal/Log.d.ts +18 -12
- package/dist/internal/Log.d.ts.map +1 -1
- package/dist/internal/Log.js +2 -0
- package/dist/internal/Log.js.map +1 -1
- package/dist/internal/RateLimit.d.ts +2 -0
- package/dist/internal/RateLimit.d.ts.map +1 -1
- package/dist/internal/RateLimit.js +4 -4
- package/dist/internal/RateLimit.js.map +1 -1
- package/dist/internal/Tidx.d.ts +9 -3
- package/dist/internal/Tidx.d.ts.map +1 -1
- package/dist/internal/Tidx.js +13 -7
- package/dist/internal/Tidx.js.map +1 -1
- package/dist/internal/Viem.d.ts +14 -1
- package/dist/internal/Viem.d.ts.map +1 -1
- package/dist/internal/Viem.js +19 -1
- package/dist/internal/Viem.js.map +1 -1
- package/package.json +7 -2
- package/src/ApiKeys.test.ts +2 -0
- package/src/App.test.ts +15 -4
- package/src/App.ts +31 -5
- package/src/Assets.ts +3 -3
- package/src/Client.test-d.ts +30 -3
- package/src/Scope.ts +4 -2
- package/src/Webhooks.ts +5 -10
- package/src/admin/apps/api-keys.test.ts +27 -0
- package/src/admin/apps/api-keys.ts +8 -1
- package/src/admin/apps/scopes.test.ts +7 -2
- package/src/admin/apps/scopes.ts +2 -2
- package/src/admin/ui/src/components/Dialog.tsx +8 -2
- package/src/admin/ui/src/lib/scopes.test.ts +19 -0
- package/src/admin/ui/src/lib/scopes.ts +12 -0
- package/src/admin/ui/src/routes/api-keys.tsx +128 -27
- package/src/admin/ui.generated.ts +33 -33
- package/src/apps/composition.test.ts +77 -0
- package/src/apps/data/App.ts +25 -45
- package/src/apps/data/FundingQuoteProviders.test.ts +615 -5
- package/src/apps/data/FundingQuoteProviders.ts +570 -4
- package/src/apps/data/routes/funding.test.ts +181 -7
- package/src/apps/data/routes/funding.ts +27 -12
- package/src/apps/data/routes/indexer.test.ts +82 -20
- package/src/apps/data/routes/indexer.ts +8 -2
- package/src/apps/data/routes/tokens.test.ts +54 -0
- package/src/apps/data/routes/transfers.test.ts +73 -1
- package/src/apps/data/routes/transfers.ts +24 -30
- package/src/apps/data/routes/webhooks.test.ts +203 -0
- package/src/apps/data/routes/webhooks.ts +191 -54
- package/src/apps/management/Billing.test.ts +52 -3
- package/src/apps/management/Billing.ts +5 -4
- package/src/apps/management/routes/billing.ts +6 -1
- package/src/apps/management/routes/orgs.ts +19 -1
- package/src/apps/mcp.test.ts +18 -0
- package/src/apps/mcp.ts +2 -2
- package/src/apps/relay/App.test.ts +6 -5
- package/src/apps/relay/App.ts +2 -2
- package/src/apps/relay/Sponsorships.test.ts +2 -2
- package/src/apps/relay/Sponsorships.ts +24 -1
- package/src/db/Db.test.ts +5 -5
- package/src/db/Db.ts +4 -0
- package/src/db/Schema.ts +1 -1
- package/src/db/tables/billingSettings.test.ts +2 -2
- package/src/db/tables/organizations.test.ts +32 -2
- package/src/db/tables/organizations.ts +28 -0
- package/src/db/tables/requestUsage.ts +3 -23
- package/src/db/tables/sponsoredTransactions.test.ts +60 -2
- package/src/db/tables/sponsoredTransactions.ts +67 -21
- package/src/db/tables/stripeCustomers.test.ts +2 -2
- package/src/internal/Auth.test.ts +63 -10
- package/src/internal/Auth.ts +21 -9
- package/src/internal/Cache.test.ts +19 -0
- package/src/internal/Cache.ts +13 -1
- package/src/internal/EdgeCache.test.ts +40 -0
- package/src/internal/EdgeCache.ts +11 -3
- package/src/internal/FundingQuotes.test.ts +130 -3
- package/src/internal/FundingQuotes.ts +113 -6
- package/src/internal/Log.ts +21 -14
- package/src/internal/RateLimit.test.ts +12 -0
- package/src/internal/RateLimit.ts +4 -4
- package/src/internal/Tidx.test.ts +48 -0
- package/src/internal/Tidx.ts +21 -7
- package/src/internal/Viem.test.ts +46 -0
- package/src/internal/Viem.ts +34 -1
package/dist/internal/Viem.js
CHANGED
|
@@ -31,7 +31,7 @@ export const url = {
|
|
|
31
31
|
* webhook poller). A missing `url` falls back to the built-in public host map
|
|
32
32
|
* ({@link url}); a missing `basicAuth` means no Basic auth is sent.
|
|
33
33
|
*
|
|
34
|
-
* Memoization is keyed on the resolved output (see
|
|
34
|
+
* Memoization is keyed on the resolved output (see {@link createGetClient} and
|
|
35
35
|
* {@link Tidx.createGetClient}), not the context, so a resolver may branch on
|
|
36
36
|
* any principal field without unbounded client cardinality: callers that
|
|
37
37
|
* resolve to the same upstream share a client. Pass the result to
|
|
@@ -69,4 +69,22 @@ export function getClient(options) {
|
|
|
69
69
|
.extend(publicActions)
|
|
70
70
|
.extend(tempoActions());
|
|
71
71
|
}
|
|
72
|
+
/** Builds a memoized Tempo client resolver. */
|
|
73
|
+
export function createGetClient(options = {}) {
|
|
74
|
+
const { defaultChainId: defaultChainId_option = defaultChainId, rpc } = options;
|
|
75
|
+
const clients = new Map();
|
|
76
|
+
return (chainId = defaultChainId_option, principal = null) => {
|
|
77
|
+
const { basicAuth, url } = resolve(rpc, { chainId, principal });
|
|
78
|
+
const cacheKey = JSON.stringify([url, basicAuth, chainId]);
|
|
79
|
+
const existing = clients.get(cacheKey);
|
|
80
|
+
if (existing)
|
|
81
|
+
return existing;
|
|
82
|
+
const client = getClient({
|
|
83
|
+
chainId,
|
|
84
|
+
rpc: { basicAuth, url: () => url },
|
|
85
|
+
});
|
|
86
|
+
clients.set(cacheKey, client);
|
|
87
|
+
return client;
|
|
88
|
+
};
|
|
89
|
+
}
|
|
72
90
|
//# sourceMappingURL=Viem.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Viem.js","sourceRoot":"","sources":["../../src/internal/Viem.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Viem.js","sourceRoot":"","sources":["../../src/internal/Viem.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tapimo",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.5.
|
|
4
|
+
"version": "0.5.2",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"bin": {
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"ox": "0.14.30",
|
|
27
27
|
"pg": "^8.22.0",
|
|
28
28
|
"stripe": "^19.1.0",
|
|
29
|
-
"tidx.ts": "^0.1.
|
|
29
|
+
"tidx.ts": "^0.1.2",
|
|
30
30
|
"viem": "2.54.6",
|
|
31
31
|
"vocs": "^2.3.3",
|
|
32
32
|
"zod": "^4.4.3"
|
|
@@ -50,6 +50,11 @@
|
|
|
50
50
|
"types": "./dist/admin/index.d.ts",
|
|
51
51
|
"default": "./dist/admin/index.js"
|
|
52
52
|
},
|
|
53
|
+
"./client": {
|
|
54
|
+
"src": "./src/Client.ts",
|
|
55
|
+
"types": "./dist/Client.d.ts",
|
|
56
|
+
"default": "./dist/Client.js"
|
|
57
|
+
},
|
|
53
58
|
"./cloudflare": {
|
|
54
59
|
"src": "./src/cloudflare.ts",
|
|
55
60
|
"types": "./dist/cloudflare.d.ts",
|
package/src/ApiKeys.test.ts
CHANGED
package/src/App.test.ts
CHANGED
|
@@ -74,6 +74,18 @@ describe('create', () => {
|
|
|
74
74
|
`)
|
|
75
75
|
})
|
|
76
76
|
|
|
77
|
+
test('ignores client-supplied request ids', async () => {
|
|
78
|
+
const entries: Log.Entry[] = []
|
|
79
|
+
const app = TestApp.create({ logger: (entry) => void entries.push(entry) })
|
|
80
|
+
const response = await app.request('/health', {
|
|
81
|
+
headers: { 'tempo-request-id': 'client-controlled' },
|
|
82
|
+
})
|
|
83
|
+
const requestId = response.headers.get('tempo-request-id')
|
|
84
|
+
|
|
85
|
+
expect(requestId === 'client-controlled').toMatchInlineSnapshot(`false`)
|
|
86
|
+
expect(entries[0]?.requestId === requestId).toMatchInlineSnapshot(`true`)
|
|
87
|
+
})
|
|
88
|
+
|
|
77
89
|
test('documents supported API key transports', async () => {
|
|
78
90
|
const app = TestApp.create()
|
|
79
91
|
const spec = await (await app.request('/openapi.json')).json()
|
|
@@ -222,7 +234,7 @@ describe('create', () => {
|
|
|
222
234
|
const response = await app.request(`/v1/tokens/${TestApp.token}`, {
|
|
223
235
|
headers: {
|
|
224
236
|
authorization: 'Payment malformed',
|
|
225
|
-
'
|
|
237
|
+
'cf-connecting-ip': '203.0.113.10',
|
|
226
238
|
},
|
|
227
239
|
})
|
|
228
240
|
|
|
@@ -248,7 +260,7 @@ describe('create', () => {
|
|
|
248
260
|
const response = await app.request(`/v1/tokens/${TestApp.token}`, {
|
|
249
261
|
headers: {
|
|
250
262
|
authorization: 'Payment malformed',
|
|
251
|
-
'
|
|
263
|
+
'cf-connecting-ip': '203.0.113.10',
|
|
252
264
|
},
|
|
253
265
|
})
|
|
254
266
|
|
|
@@ -473,7 +485,7 @@ describe('create', () => {
|
|
|
473
485
|
// key matches, so a `:chainId//icons` regression would drop every logo.
|
|
474
486
|
const db = TestApp.database()
|
|
475
487
|
await TestApp.verifiedSeed(db, 42431)
|
|
476
|
-
const app = App.create({ db }).route(
|
|
488
|
+
const app = App.create({ db, defaultChainId: 42431 }).route(
|
|
477
489
|
'/',
|
|
478
490
|
App.data({
|
|
479
491
|
assets: {
|
|
@@ -485,7 +497,6 @@ describe('create', () => {
|
|
|
485
497
|
}
|
|
486
498
|
: undefined,
|
|
487
499
|
},
|
|
488
|
-
defaultChainId: 42431,
|
|
489
500
|
}),
|
|
490
501
|
)
|
|
491
502
|
const response = await app.request('/v1/tokens?verified=true')
|
package/src/App.ts
CHANGED
|
@@ -149,6 +149,13 @@ export function create<const path extends string = '/'>(options: create.Options<
|
|
|
149
149
|
// `options.db`, API-key records in `options.kv`.
|
|
150
150
|
const store = options.cache?.store ?? Store.memory()
|
|
151
151
|
const scopeCatalog = Scope.extend(options.scopes)
|
|
152
|
+
const defaultChainId = options.defaultChainId ?? Viem.defaultChainId
|
|
153
|
+
const getClient = Viem.createGetClient({ defaultChainId, rpc: options.rpc })
|
|
154
|
+
const supportedChainIds = new Set<number>([
|
|
155
|
+
...Object.keys(Viem.url).map(Number),
|
|
156
|
+
defaultChainId,
|
|
157
|
+
...(options.supportedChainIds ?? []),
|
|
158
|
+
])
|
|
152
159
|
|
|
153
160
|
// Build the app in two phases: register middleware imperatively, then chain
|
|
154
161
|
// route mounts so the merged Hono schema flows through the return type. The
|
|
@@ -165,7 +172,11 @@ export function create<const path extends string = '/'>(options: create.Options<
|
|
|
165
172
|
'*',
|
|
166
173
|
hono_compress(typeof options.compress === 'object' ? options.compress : undefined),
|
|
167
174
|
)
|
|
168
|
-
base.use('*', requestId(
|
|
175
|
+
base.use('*', requestId())
|
|
176
|
+
base.use('*', async (c, next) => {
|
|
177
|
+
await next()
|
|
178
|
+
c.header('tempo-request-id', c.get('requestId'))
|
|
179
|
+
})
|
|
169
180
|
// One canonical structured log line per request, plus request metrics.
|
|
170
181
|
// Registered after `requestId` (the entry embeds it) and before the timing and
|
|
171
182
|
// edge-cache layers so the entry covers edge-cache hits and includes the
|
|
@@ -233,11 +244,15 @@ export function create<const path extends string = '/'>(options: create.Options<
|
|
|
233
244
|
base.use('*', async (c, next) => {
|
|
234
245
|
c.set('analytics', analytics)
|
|
235
246
|
c.set('basePath', basePath)
|
|
247
|
+
c.set('chainId', defaultChainId)
|
|
236
248
|
c.set('db', db.source)
|
|
237
249
|
c.set('dbCached', db.cached ?? db.source)
|
|
250
|
+
c.set('getClient', (chainId) => getClient(chainId, Auth.getPrincipal(c)))
|
|
238
251
|
c.set('kv', options.kv)
|
|
252
|
+
c.set('rpc', options.rpc)
|
|
239
253
|
c.set('scopeCatalog', scopeCatalog)
|
|
240
254
|
c.set('store', store)
|
|
255
|
+
c.set('supportedChainIds', supportedChainIds)
|
|
241
256
|
await next()
|
|
242
257
|
})
|
|
243
258
|
|
|
@@ -271,7 +286,10 @@ export function create<const path extends string = '/'>(options: create.Options<
|
|
|
271
286
|
const route = app.route.bind(app)
|
|
272
287
|
;(app as unknown as { route: typeof route }).route = ((path, sub) => {
|
|
273
288
|
const entry = Metadata.read(sub)
|
|
274
|
-
if (entry)
|
|
289
|
+
if (entry) {
|
|
290
|
+
metadata.push(entry)
|
|
291
|
+
for (const chainId of entry.supportedChainIds ?? []) supportedChainIds.add(chainId)
|
|
292
|
+
}
|
|
275
293
|
return route(path, sub)
|
|
276
294
|
}) as typeof route
|
|
277
295
|
|
|
@@ -442,9 +460,9 @@ export function listener(app: { fetch: NodeFetch }): Listener {
|
|
|
442
460
|
|
|
443
461
|
export declare namespace create {
|
|
444
462
|
/**
|
|
445
|
-
* Options for creating the Tempo API Hono app
|
|
446
|
-
*
|
|
447
|
-
*
|
|
463
|
+
* Options for creating the Tempo API Hono app: shared chain, RPC, database,
|
|
464
|
+
* cache, authentication, and cross-cutting configuration. Email belongs to
|
|
465
|
+
* {@link management}.
|
|
448
466
|
*/
|
|
449
467
|
type Options<path extends string = '/'> = {
|
|
450
468
|
/** Analytics store for usage reads (e.g. `Analytics.clickhouse(...)`), or a per-request factory. Accepts `{ source }` to mirror `db`. Omit to leave usage analytics unconfigured — usage routes answer 501. Ingest happens outside this app (queue consumer via `Analytics.handleQueue`). */
|
|
@@ -459,6 +477,8 @@ export declare namespace create {
|
|
|
459
477
|
cors?: boolean | Cors | undefined
|
|
460
478
|
/** Authoritative Postgres store backing stateful features (webhooks, verified tokens). Pass a long-lived `Db.postgres(...)` singleton on Node, or a factory (`() => Db.postgres(...)`) on Workers so each request builds a fresh Hyperdrive-pooled connection. Pass `{ source, cached }` to route staleness-tolerant reads (verified tokens) through a second, query-caching Hyperdrive config; `source` stays uncached for read-after-write correctness. Apply migrations out of band via `tapimo admin migrate`. */
|
|
461
479
|
db: Db.Source | { cached?: Db.Source | undefined; source: Db.Source }
|
|
480
|
+
/** Default Tempo chain id for requests that omit `chainId`. */
|
|
481
|
+
defaultChainId?: Viem.ChainId | undefined
|
|
462
482
|
/** KV state store backing API-key records (e.g. `Store.cloudflareKv(...)`). Omit to leave API-key auth closed — every token resolves to `null`. */
|
|
463
483
|
kv?: Kv | undefined
|
|
464
484
|
/** Request logging. Disabled by default. Pass `true` to emit one canonical structured log entry per request through the level-matched console method (indexed by Workers Logs); a level (e.g. `'warn'`) to emit only that level and above (dropping successful-request info lines); or an emit function for a custom sink. */
|
|
@@ -467,10 +487,14 @@ export declare namespace create {
|
|
|
467
487
|
metrics?: Metrics.Metrics | undefined
|
|
468
488
|
/** Base path to mount API handlers under. Provided as a literal via the `path` generic so the resulting Hono schema keeps typed route paths for `testClient`. */
|
|
469
489
|
path?: path | undefined
|
|
490
|
+
/** Tempo RPC options, including per-request routing by request principal. */
|
|
491
|
+
rpc?: Viem.getClient.Rpc | undefined
|
|
470
492
|
/** OpenAPI `servers` for the generated document. Defaults to a single relative server (the host serving the doc), so the reference targets the deployment it is served from. Set the canonical public URL(s) here, e.g. `[{ url: 'https://api.tempo.xyz' }]`. */
|
|
471
493
|
servers?: readonly Server[] | undefined
|
|
472
494
|
/** Host-defined API-key scopes appended to the built-in {@link Scope.catalog}. */
|
|
473
495
|
scopes?: readonly Scope.Entry[] | undefined
|
|
496
|
+
/** Extra chain ids served beyond the built-in chains and `defaultChainId`. */
|
|
497
|
+
supportedChainIds?: readonly number[] | undefined
|
|
474
498
|
}
|
|
475
499
|
|
|
476
500
|
/** KV state-store configuration. */
|
|
@@ -532,6 +556,8 @@ export declare namespace create {
|
|
|
532
556
|
docs?: Docs | undefined
|
|
533
557
|
/** Security scheme definitions this group's routes reference (e.g. the session cookie). */
|
|
534
558
|
securitySchemes?: Record<string, unknown> | undefined
|
|
559
|
+
/** Chain ids served by this route group, merged before auth handles the first request. */
|
|
560
|
+
supportedChainIds?: readonly number[] | undefined
|
|
535
561
|
/** Root-level OpenAPI tag definitions for this group's operations. */
|
|
536
562
|
tags?: readonly Tag[] | undefined
|
|
537
563
|
/** Builds the OAS 3.1 `webhooks` block documenting outbound deliveries, emitted only when the group's webhook routes are mounted. Called during spec generation only, keeping schema conversion off isolate startup. */
|
package/src/Assets.ts
CHANGED
|
@@ -26,9 +26,9 @@ export type Assets = {
|
|
|
26
26
|
export function cloudflareR2(bucket: cloudflareR2.Bucket): Assets {
|
|
27
27
|
return {
|
|
28
28
|
async get(key) {
|
|
29
|
-
//
|
|
30
|
-
const { objects } = await bucket.list({ limit:
|
|
31
|
-
if (objects
|
|
29
|
+
// Legacy source-extension aliases may sort before the extensionless key.
|
|
30
|
+
const { objects } = await bucket.list({ limit: 1_000, prefix: key })
|
|
31
|
+
if (!objects.some((object) => object.key === key)) return undefined
|
|
32
32
|
const object = await bucket.get(key)
|
|
33
33
|
if (!object) return undefined
|
|
34
34
|
return {
|
package/src/Client.test-d.ts
CHANGED
|
@@ -364,7 +364,7 @@ describe('narrows responses by status', () => {
|
|
|
364
364
|
param: { orgId: 'org_test' },
|
|
365
365
|
})
|
|
366
366
|
|
|
367
|
-
expectTypeOf(res.status).toEqualTypeOf<200 | 404 | 502 | PublicAccessStatus>()
|
|
367
|
+
expectTypeOf(res.status).toEqualTypeOf<200 | 404 | 409 | 502 | PublicAccessStatus>()
|
|
368
368
|
|
|
369
369
|
switch (res.status) {
|
|
370
370
|
case 200: {
|
|
@@ -379,6 +379,12 @@ describe('narrows responses by status', () => {
|
|
|
379
379
|
void json
|
|
380
380
|
return
|
|
381
381
|
}
|
|
382
|
+
case 409: {
|
|
383
|
+
const json = await res.json()
|
|
384
|
+
expectTypeOf(json.error.code).toEqualTypeOf<'sponsorships_unreported'>()
|
|
385
|
+
void json
|
|
386
|
+
return
|
|
387
|
+
}
|
|
382
388
|
}
|
|
383
389
|
})
|
|
384
390
|
|
|
@@ -1193,7 +1199,7 @@ describe('narrows responses by status', () => {
|
|
|
1193
1199
|
const client = Client.create()
|
|
1194
1200
|
const res = await client.v1.funding.quotes.$get({
|
|
1195
1201
|
query: {
|
|
1196
|
-
provider: '
|
|
1202
|
+
provider: 'across',
|
|
1197
1203
|
sourceAmount: '1000000',
|
|
1198
1204
|
sourceChain: 'base',
|
|
1199
1205
|
sourceToken: 'USDC',
|
|
@@ -1208,7 +1214,10 @@ describe('narrows responses by status', () => {
|
|
|
1208
1214
|
expectTypeOf(json.data[0]!.sourceAmount).toEqualTypeOf<
|
|
1209
1215
|
z.output<typeof Funding.schema.quoteFunding.Response>['data'][number]['sourceAmount']
|
|
1210
1216
|
>()
|
|
1211
|
-
expectTypeOf(json.data[0]!.provider.id).toEqualTypeOf<
|
|
1217
|
+
expectTypeOf(json.data[0]!.provider.id).toEqualTypeOf<
|
|
1218
|
+
'across' | 'relay' | 'rhino' | 'squid'
|
|
1219
|
+
>()
|
|
1220
|
+
expectTypeOf(json.data[0]!.quote.expiresAt).toEqualTypeOf<string | undefined>()
|
|
1212
1221
|
expectTypeOf(json.data[0]!.quote.outputAmount).toEqualTypeOf<string>()
|
|
1213
1222
|
void json
|
|
1214
1223
|
return
|
|
@@ -1952,6 +1961,11 @@ describe('narrows responses by status', () => {
|
|
|
1952
1961
|
|
|
1953
1962
|
if (res.status === 200) {
|
|
1954
1963
|
const json = await res.json()
|
|
1964
|
+
expectTypeOf(json.destination.type).toEqualTypeOf<'betterstack' | 'slack' | 'url'>()
|
|
1965
|
+
if (json.destination.type === 'betterstack')
|
|
1966
|
+
expectTypeOf(json.destination.token).toEqualTypeOf<string>()
|
|
1967
|
+
if (json.destination.type === 'slack')
|
|
1968
|
+
expectTypeOf(json.destination.url).toEqualTypeOf<string>()
|
|
1955
1969
|
expectTypeOf(json.secret).toEqualTypeOf<
|
|
1956
1970
|
z.output<typeof Webhooks.schema.createWebhook.Response>['secret']
|
|
1957
1971
|
>()
|
|
@@ -1967,6 +1981,11 @@ describe('narrows responses by status', () => {
|
|
|
1967
1981
|
|
|
1968
1982
|
if (res.status === 200) {
|
|
1969
1983
|
const json = await res.json()
|
|
1984
|
+
const destination = json.data[0]!.destination
|
|
1985
|
+
if (destination.type === 'betterstack')
|
|
1986
|
+
expectTypeOf(destination.token).toEqualTypeOf<'[redacted]'>()
|
|
1987
|
+
if (destination.type === 'slack')
|
|
1988
|
+
expectTypeOf(destination.url).toEqualTypeOf<'https://hooks.slack.com/…'>()
|
|
1970
1989
|
expectTypeOf(json.data[0]!.id).toEqualTypeOf<
|
|
1971
1990
|
z.output<typeof Webhooks.schema.listWebhooks.Response>['data'][number]['id']
|
|
1972
1991
|
>()
|
|
@@ -1988,6 +2007,10 @@ describe('narrows responses by status', () => {
|
|
|
1988
2007
|
|
|
1989
2008
|
if (res.status === 200) {
|
|
1990
2009
|
const json = await res.json()
|
|
2010
|
+
if (json.destination.type === 'betterstack')
|
|
2011
|
+
expectTypeOf(json.destination.token).toEqualTypeOf<'[redacted]'>()
|
|
2012
|
+
if (json.destination.type === 'slack')
|
|
2013
|
+
expectTypeOf(json.destination.url).toEqualTypeOf<'https://hooks.slack.com/…'>()
|
|
1991
2014
|
expectTypeOf(json.id).toEqualTypeOf<
|
|
1992
2015
|
z.output<typeof Webhooks.schema.getWebhook.Response>['id']
|
|
1993
2016
|
>()
|
|
@@ -2063,6 +2086,10 @@ describe('narrows responses by status', () => {
|
|
|
2063
2086
|
|
|
2064
2087
|
if (res.status === 200) {
|
|
2065
2088
|
const json = await res.json()
|
|
2089
|
+
if (json.destination.type === 'betterstack')
|
|
2090
|
+
expectTypeOf(json.destination.token).toEqualTypeOf<'[redacted]'>()
|
|
2091
|
+
if (json.destination.type === 'slack')
|
|
2092
|
+
expectTypeOf(json.destination.url).toEqualTypeOf<'https://hooks.slack.com/…'>()
|
|
2066
2093
|
expectTypeOf(json.status).toEqualTypeOf<
|
|
2067
2094
|
z.output<typeof Webhooks.schema.updateWebhook.Response>['status']
|
|
2068
2095
|
>()
|
package/src/Scope.ts
CHANGED
|
@@ -86,8 +86,10 @@ export namespace schema {
|
|
|
86
86
|
/** A concrete per-zone read scope (`zone:421700001:read`). */
|
|
87
87
|
export const Zone = z
|
|
88
88
|
.templateLiteral(['zone:', z.int(), ':read'])
|
|
89
|
-
|
|
90
|
-
|
|
89
|
+
.check(
|
|
90
|
+
z.regex(/^zone:[1-9]\d*:read$/),
|
|
91
|
+
z.describe('Per-zone read scope (`zone:<chainId>:read`).'),
|
|
92
|
+
)
|
|
91
93
|
}
|
|
92
94
|
|
|
93
95
|
/** Per-zone read scope, granting access to one zone chain id. */
|
package/src/Webhooks.ts
CHANGED
|
@@ -28,7 +28,7 @@ export { eventTypes, schema } from './apps/data/routes/webhooks.js'
|
|
|
28
28
|
*
|
|
29
29
|
* The monitoring event types (`log:emitted`, `block:created`,
|
|
30
30
|
* `transaction:included`) scan a confirmed window directly over JSON-RPC, so the
|
|
31
|
-
* poller builds memoized viem clients (via {@link Viem.
|
|
31
|
+
* poller builds memoized viem clients (via {@link Viem.createGetClient}); the legacy
|
|
32
32
|
* `token:transfer` type still reads from TIDX, so it
|
|
33
33
|
* also builds memoized TIDX clients (via {@link Tidx.createGetClient}) when a
|
|
34
34
|
* `tidx` config is supplied. The poller keeps `App.create` a pure Hono app: it
|
|
@@ -42,15 +42,10 @@ export function createPoller(options: createPoller.Options): createPoller.Poller
|
|
|
42
42
|
const getTidx = options.tidx
|
|
43
43
|
? Tidx.createGetClient({ defaultChainId: options.defaultChainId, tidx: options.tidx })
|
|
44
44
|
: undefined
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
client = Viem.getClient({ chainId, rpc: options.rpc })
|
|
50
|
-
clients.set(chainId, client)
|
|
51
|
-
}
|
|
52
|
-
return client
|
|
53
|
-
}
|
|
45
|
+
const getClient = Viem.createGetClient({
|
|
46
|
+
defaultChainId: options.defaultChainId,
|
|
47
|
+
rpc: options.rpc,
|
|
48
|
+
})
|
|
54
49
|
|
|
55
50
|
const metrics = options.metrics ? MetricSink.webhooks(options.metrics) : undefined
|
|
56
51
|
|
|
@@ -68,6 +68,19 @@ describe('POST /api-keys', () => {
|
|
|
68
68
|
expect(resolved?.environment).toBe('sandbox')
|
|
69
69
|
})
|
|
70
70
|
|
|
71
|
+
test('mints a key with a concrete Zone read scope', async () => {
|
|
72
|
+
const { client, store } = TestAdmin.setup()
|
|
73
|
+
const scope = 'zone:421700001:read' as const
|
|
74
|
+
const response = await client['api-keys'].$post({
|
|
75
|
+
json: { ...validBody, scopes: [scope] },
|
|
76
|
+
})
|
|
77
|
+
const body = await TestApp.json(response, ApiKeys.schema.createApiKey.Response)
|
|
78
|
+
|
|
79
|
+
expect(response.status).toBe(200)
|
|
80
|
+
expect(body.scopes).toEqual([scope])
|
|
81
|
+
expect((await core_ApiKeys.resolve(store, body.token))?.scopes).toEqual([scope])
|
|
82
|
+
})
|
|
83
|
+
|
|
71
84
|
test('records the expiry when provided', async () => {
|
|
72
85
|
const { client, store } = TestAdmin.setup()
|
|
73
86
|
const expiresAt = '2100-01-01T00:00:00.000Z'
|
|
@@ -91,6 +104,20 @@ describe('POST /api-keys', () => {
|
|
|
91
104
|
expect(body.error?.code).toMatchInlineSnapshot(`"body_invalid"`)
|
|
92
105
|
})
|
|
93
106
|
|
|
107
|
+
test('rejects non-concrete or non-canonical Zone scopes with 400', async () => {
|
|
108
|
+
for (const scope of ['zone:<chainId>:read', 'zone:0:read', 'zone:000421700001:read']) {
|
|
109
|
+
const { client } = TestAdmin.setup()
|
|
110
|
+
const response = await client['api-keys'].$post({
|
|
111
|
+
// Cast past the typed client to exercise runtime validation.
|
|
112
|
+
json: { ...validBody, scopes: [scope] } as never,
|
|
113
|
+
})
|
|
114
|
+
const body = (await response.json()) as { error?: { code?: string } }
|
|
115
|
+
|
|
116
|
+
expect(response.status).toBe(400)
|
|
117
|
+
expect(body.error?.code).toBe('body_invalid')
|
|
118
|
+
}
|
|
119
|
+
})
|
|
120
|
+
|
|
94
121
|
test('ignores a client-supplied createdBy (audit trail is server-set)', async () => {
|
|
95
122
|
const { client } = TestAdmin.setup()
|
|
96
123
|
const response = await client['api-keys'].$post({
|
|
@@ -19,7 +19,14 @@ export namespace schema {
|
|
|
19
19
|
export const Body = Schema.describe(
|
|
20
20
|
z.extend(z.omit(ApiKeys.schema.MintInput, { createdBy: true }), {
|
|
21
21
|
scopes: z
|
|
22
|
-
.readonly(
|
|
22
|
+
.readonly(
|
|
23
|
+
z.array(
|
|
24
|
+
z.union([
|
|
25
|
+
z.enum([...Scope.catalog.map((entry) => entry.scope), Scope.wildcard]),
|
|
26
|
+
Scope.schema.Zone,
|
|
27
|
+
]),
|
|
28
|
+
),
|
|
29
|
+
)
|
|
23
30
|
.check(z.describe("Granted scopes. '*' grants all scopes.")),
|
|
24
31
|
}),
|
|
25
32
|
'Fields accepted when minting an API key.',
|
|
@@ -54,17 +54,22 @@ describe('GET /scopes', () => {
|
|
|
54
54
|
"scope": "webhooks:write",
|
|
55
55
|
"selfServe": false,
|
|
56
56
|
},
|
|
57
|
+
{
|
|
58
|
+
"description": "Read RPC and indexed data for one zone chain id.",
|
|
59
|
+
"scope": "zone:<chainId>:read",
|
|
60
|
+
"selfServe": false,
|
|
61
|
+
},
|
|
57
62
|
],
|
|
58
63
|
}
|
|
59
64
|
`)
|
|
60
65
|
})
|
|
61
66
|
|
|
62
|
-
test('serves the Scope
|
|
67
|
+
test('serves the extended Scope catalog source of truth verbatim', async () => {
|
|
63
68
|
const { client } = TestAdmin.setup()
|
|
64
69
|
const response = await client.scopes.$get()
|
|
65
70
|
const body = await TestApp.json(response, Scopes.schema.getScopes.Response)
|
|
66
71
|
|
|
67
|
-
expect(body.data).toEqual(Scope.
|
|
72
|
+
expect(body.data).toEqual(Scope.extend())
|
|
68
73
|
})
|
|
69
74
|
|
|
70
75
|
test('is never cached (admin responses are per-principal)', async () => {
|
package/src/admin/apps/scopes.ts
CHANGED
|
@@ -33,7 +33,7 @@ export namespace schema {
|
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
/** Catalog of
|
|
36
|
+
/** Catalog of API-key scopes, including dynamic scope templates. */
|
|
37
37
|
export function scopes() {
|
|
38
38
|
return new Hono<App.Environment>().get(
|
|
39
39
|
'/',
|
|
@@ -45,6 +45,6 @@ export function scopes() {
|
|
|
45
45
|
summary: 'List scopes',
|
|
46
46
|
tags: ['Scopes'],
|
|
47
47
|
}),
|
|
48
|
-
(c) => c.json({ data: Scope.
|
|
48
|
+
(c) => c.json({ data: Scope.extend() }),
|
|
49
49
|
)
|
|
50
50
|
}
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import { Dialog as Base } from '@base-ui/react/dialog'
|
|
2
2
|
import type { ComponentProps, ReactNode } from 'react'
|
|
3
|
+
import { cx } from 'regen-ui'
|
|
3
4
|
|
|
4
5
|
/** Backdrop + centered popup shell. Children render inside; pair with Title/Description. */
|
|
5
|
-
function Popup(props: { children: ReactNode }) {
|
|
6
|
+
function Popup(props: { children: ReactNode; className?: string | undefined }) {
|
|
6
7
|
return (
|
|
7
8
|
<Base.Portal>
|
|
8
9
|
<Base.Backdrop className="fixed inset-0 z-40 bg-black/60 transition-opacity duration-150 data-[ending-style]:opacity-0 data-[starting-style]:opacity-0" />
|
|
9
|
-
<Base.Popup
|
|
10
|
+
<Base.Popup
|
|
11
|
+
className={cx(
|
|
12
|
+
'fixed left-1/2 top-1/2 z-50 w-[calc(100vw-2rem)] max-w-md -translate-x-1/2 -translate-y-1/2 border border-border bg-surface p-6 shadow-lg outline-none transition-opacity duration-150 data-[ending-style]:opacity-0 data-[starting-style]:opacity-0',
|
|
13
|
+
props.className,
|
|
14
|
+
)}
|
|
15
|
+
>
|
|
10
16
|
{props.children}
|
|
11
17
|
</Base.Popup>
|
|
12
18
|
</Base.Portal>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vite-plus/test'
|
|
2
|
+
|
|
3
|
+
import { fromZoneChainId } from './scopes'
|
|
4
|
+
|
|
5
|
+
describe('fromZoneChainId', () => {
|
|
6
|
+
it('builds a canonical Zone read scope', () => {
|
|
7
|
+
expect(fromZoneChainId(' 000421700001 ')).toBe('zone:421700001:read')
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
it('rejects non-positive and malformed chain ids', () => {
|
|
11
|
+
expect(['', '0', '-1', '1.5', 'mainnet'].map(fromZoneChainId)).toEqual([
|
|
12
|
+
undefined,
|
|
13
|
+
undefined,
|
|
14
|
+
undefined,
|
|
15
|
+
undefined,
|
|
16
|
+
undefined,
|
|
17
|
+
])
|
|
18
|
+
})
|
|
19
|
+
})
|
|
@@ -6,6 +6,18 @@ import { client } from './client'
|
|
|
6
6
|
/** A single issuable API-key scope from the catalog endpoint. */
|
|
7
7
|
export type Scope = InferResponseType<(typeof client)['scopes']['$get'], 200>['data'][number]
|
|
8
8
|
|
|
9
|
+
/** Dynamic Zone scope template returned by the scope catalog. */
|
|
10
|
+
export const zoneTemplate = 'zone:<chainId>:read'
|
|
11
|
+
|
|
12
|
+
/** Builds a canonical Zone read scope from a positive integer chain id. */
|
|
13
|
+
export function fromZoneChainId(value: string): `zone:${string}:read` | undefined {
|
|
14
|
+
const chainId = value.trim()
|
|
15
|
+
if (!/^\d+$/.test(chainId)) return undefined
|
|
16
|
+
const normalized = BigInt(chainId)
|
|
17
|
+
if (normalized === 0n) return undefined
|
|
18
|
+
return `zone:${normalized}:read`
|
|
19
|
+
}
|
|
20
|
+
|
|
9
21
|
/** List the issuable API-key scope catalog. */
|
|
10
22
|
export namespace get {
|
|
11
23
|
export const key = ['scopes'] as const
|