tapimo 0.6.0 → 0.7.1
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 +8641 -8263
- package/dist/App.d.ts.map +1 -1
- package/dist/App.js +9 -1
- package/dist/App.js.map +1 -1
- package/dist/Scope.d.ts +4 -0
- package/dist/Scope.d.ts.map +1 -1
- package/dist/Scope.js +5 -0
- package/dist/Scope.js.map +1 -1
- package/dist/admin/App.d.ts +112 -11
- package/dist/admin/App.d.ts.map +1 -1
- package/dist/admin/apps/api-keys.d.ts +2 -1
- package/dist/admin/apps/api-keys.d.ts.map +1 -1
- package/dist/admin/apps/early-access.d.ts +2 -2
- package/dist/admin/apps/early-access.d.ts.map +1 -1
- package/dist/admin/apps/early-access.js +5 -5
- package/dist/admin/apps/early-access.js.map +1 -1
- package/dist/admin/apps/organizations.d.ts +124 -1
- package/dist/admin/apps/organizations.d.ts.map +1 -1
- package/dist/admin/apps/organizations.js +97 -6
- package/dist/admin/apps/organizations.js.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 +47 -39
- package/dist/admin/ui.generated.js.map +1 -1
- package/dist/analytics/Analytics.d.ts.map +1 -1
- package/dist/analytics/Analytics.js +20 -0
- package/dist/analytics/Analytics.js.map +1 -1
- package/dist/analytics/tables/requestEvents.d.ts +18 -0
- package/dist/analytics/tables/requestEvents.d.ts.map +1 -1
- package/dist/analytics/tables/requestEvents.js +36 -7
- package/dist/analytics/tables/requestEvents.js.map +1 -1
- package/dist/apps/data/App.d.ts +8050 -8019
- package/dist/apps/data/App.d.ts.map +1 -1
- package/dist/apps/data/FundingQuoteProviders.js +2 -15
- package/dist/apps/data/FundingQuoteProviders.js.map +1 -1
- package/dist/apps/data/FxOracle.d.ts +1 -1
- package/dist/apps/data/FxOracle.d.ts.map +1 -1
- package/dist/apps/data/FxOracle.js +1 -1
- package/dist/apps/data/FxOracle.js.map +1 -1
- package/dist/apps/data/routes/activities.d.ts +27 -27
- package/dist/apps/data/routes/activities.d.ts.map +1 -1
- package/dist/apps/data/routes/activities.js +21 -1
- package/dist/apps/data/routes/activities.js.map +1 -1
- package/dist/apps/data/routes/balances.d.ts +58 -58
- 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 +669 -669
- package/dist/apps/data/routes/fee-amm.d.ts +157 -143
- package/dist/apps/data/routes/fee-amm.d.ts.map +1 -1
- package/dist/apps/data/routes/fee-amm.js +76 -91
- package/dist/apps/data/routes/fee-amm.js.map +1 -1
- package/dist/apps/data/routes/funding.d.ts +72 -34
- package/dist/apps/data/routes/funding.d.ts.map +1 -1
- package/dist/apps/data/routes/funding.js +34 -26
- package/dist/apps/data/routes/funding.js.map +1 -1
- package/dist/apps/data/routes/indexer.d.ts +30 -30
- package/dist/apps/data/routes/receipts.d.ts +8 -8
- package/dist/apps/data/routes/rpc.d.ts +31 -31
- package/dist/apps/data/routes/tokenlist.d.ts.map +1 -1
- package/dist/apps/data/routes/tokenlist.js +12 -3
- package/dist/apps/data/routes/tokenlist.js.map +1 -1
- package/dist/apps/data/routes/tokens.d.ts +4 -4
- package/dist/apps/data/routes/tokens.d.ts.map +1 -1
- package/dist/apps/data/routes/tokens.js +5 -1
- package/dist/apps/data/routes/tokens.js.map +1 -1
- package/dist/apps/data/routes/transactions.d.ts +6 -6
- package/dist/apps/data/routes/transfers.d.ts +101 -90
- package/dist/apps/data/routes/transfers.d.ts.map +1 -1
- package/dist/apps/data/routes/transfers.js +226 -141
- package/dist/apps/data/routes/transfers.js.map +1 -1
- package/dist/apps/data/routes/valuation.d.ts +2 -2
- package/dist/apps/data/routes/valuation.js +3 -3
- package/dist/apps/data/routes/valuation.js.map +1 -1
- package/dist/apps/data/routes/verified-tokens.d.ts +36 -36
- package/dist/apps/data/routes/verified-tokens.d.ts.map +1 -1
- package/dist/apps/data/routes/verified-tokens.js +34 -9
- package/dist/apps/data/routes/verified-tokens.js.map +1 -1
- package/dist/apps/data/routes/webhooks.d.ts +2 -2
- package/dist/apps/data/routes/webhooks.js +14 -6
- package/dist/apps/data/routes/webhooks.js.map +1 -1
- package/dist/apps/index.d.ts +1 -0
- package/dist/apps/index.d.ts.map +1 -1
- package/dist/apps/index.js +1 -0
- package/dist/apps/index.js.map +1 -1
- package/dist/apps/management/App.d.ts +460 -523
- package/dist/apps/management/App.d.ts.map +1 -1
- package/dist/apps/management/routes/api-keys.d.ts +7 -23
- package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
- package/dist/apps/management/routes/api-keys.js +20 -8
- package/dist/apps/management/routes/api-keys.js.map +1 -1
- package/dist/apps/management/routes/billing.d.ts +106 -151
- package/dist/apps/management/routes/billing.d.ts.map +1 -1
- package/dist/apps/management/routes/billing.js +28 -28
- package/dist/apps/management/routes/billing.js.map +1 -1
- package/dist/apps/management/routes/faucet.d.ts +48 -34
- package/dist/apps/management/routes/faucet.d.ts.map +1 -1
- package/dist/apps/management/routes/faucet.js +33 -18
- package/dist/apps/management/routes/faucet.js.map +1 -1
- package/dist/apps/management/routes/invitations.d.ts +131 -131
- package/dist/apps/management/routes/invitations.d.ts.map +1 -1
- package/dist/apps/management/routes/invitations.js +0 -3
- package/dist/apps/management/routes/invitations.js.map +1 -1
- package/dist/apps/management/routes/me.d.ts +6 -8
- package/dist/apps/management/routes/me.d.ts.map +1 -1
- package/dist/apps/management/routes/me.js +0 -4
- package/dist/apps/management/routes/me.js.map +1 -1
- package/dist/apps/management/routes/members.d.ts +73 -73
- package/dist/apps/management/routes/orgs.d.ts +94 -94
- package/dist/apps/management/routes/orgs.d.ts.map +1 -1
- package/dist/apps/management/routes/orgs.js +5 -0
- package/dist/apps/management/routes/orgs.js.map +1 -1
- package/dist/apps/management/routes/usage.d.ts +72 -72
- package/dist/apps/management/routes/verified-token-requests.d.ts +94 -94
- package/dist/apps/management/routes/webhooks.d.ts +1 -1
- package/dist/apps/mpp/App.d.ts +363 -0
- package/dist/apps/mpp/App.d.ts.map +1 -0
- package/dist/apps/mpp/App.js +72 -0
- package/dist/apps/mpp/App.js.map +1 -0
- package/dist/apps/mpp/Mpp.d.ts +178 -0
- package/dist/apps/mpp/Mpp.d.ts.map +1 -0
- package/dist/apps/mpp/Mpp.js +183 -0
- package/dist/apps/mpp/Mpp.js.map +1 -0
- package/dist/apps/mpp/index.d.ts +3 -0
- package/dist/apps/mpp/index.d.ts.map +1 -0
- package/dist/apps/mpp/index.js +3 -0
- package/dist/apps/mpp/index.js.map +1 -0
- package/dist/apps/relay/App.d.ts.map +1 -1
- package/dist/apps/relay/App.js +57 -10
- package/dist/apps/relay/App.js.map +1 -1
- package/dist/apps/relay/Sponsorships.js +1 -1
- package/dist/apps/relay/Sponsorships.js.map +1 -1
- package/dist/db/Db.d.ts +2 -0
- package/dist/db/Db.d.ts.map +1 -1
- package/dist/db/Db.js +27 -0
- package/dist/db/Db.js.map +1 -1
- package/dist/db/Schema.d.ts +12 -0
- package/dist/db/Schema.d.ts.map +1 -1
- package/dist/db/Schema.js +11 -0
- package/dist/db/Schema.js.map +1 -1
- package/dist/db/tables/enabledBillingSources.d.ts +73 -0
- package/dist/db/tables/enabledBillingSources.d.ts.map +1 -0
- package/dist/db/tables/enabledBillingSources.js +69 -0
- package/dist/db/tables/enabledBillingSources.js.map +1 -0
- package/dist/db/tables/invitations.d.ts +1 -12
- package/dist/db/tables/invitations.d.ts.map +1 -1
- package/dist/db/tables/invitations.js +3 -23
- package/dist/db/tables/invitations.js.map +1 -1
- package/dist/db/tables/organizations.d.ts +5 -2
- package/dist/db/tables/organizations.d.ts.map +1 -1
- package/dist/db/tables/organizations.js +32 -2
- package/dist/db/tables/organizations.js.map +1 -1
- package/dist/db/tables/sponsoredTransactions.d.ts +2 -0
- package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -1
- package/dist/db/tables/sponsoredTransactions.js +13 -3
- package/dist/db/tables/sponsoredTransactions.js.map +1 -1
- package/dist/handlers/internal/utils.d.ts +1 -16
- package/dist/handlers/internal/utils.d.ts.map +1 -1
- package/dist/handlers/internal/utils.js +17 -6
- package/dist/handlers/internal/utils.js.map +1 -1
- package/dist/handlers/relay.d.ts.map +1 -1
- package/dist/handlers/relay.js +37 -5
- package/dist/handlers/relay.js.map +1 -1
- package/dist/internal/Auth.d.ts +0 -32
- package/dist/internal/Auth.d.ts.map +1 -1
- package/dist/internal/Auth.js +3 -51
- package/dist/internal/Auth.js.map +1 -1
- package/dist/internal/EdgeCache.d.ts +1 -1
- package/dist/internal/EdgeCache.d.ts.map +1 -1
- package/dist/internal/EdgeCache.js +3 -2
- package/dist/internal/EdgeCache.js.map +1 -1
- package/dist/internal/FundingQuotes.d.ts +78 -32
- package/dist/internal/FundingQuotes.d.ts.map +1 -1
- package/dist/internal/FundingQuotes.js +45 -8
- package/dist/internal/FundingQuotes.js.map +1 -1
- package/dist/internal/Log.d.ts +18 -4
- package/dist/internal/Log.d.ts.map +1 -1
- package/dist/internal/Log.js +50 -3
- package/dist/internal/Log.js.map +1 -1
- package/dist/internal/MetricSink.d.ts.map +1 -1
- package/dist/internal/MetricSink.js +15 -0
- package/dist/internal/MetricSink.js.map +1 -1
- package/dist/internal/Response.d.ts +2 -2
- package/dist/internal/Response.d.ts.map +1 -1
- package/dist/internal/Response.js +12 -4
- package/dist/internal/Response.js.map +1 -1
- package/dist/internal/Schema.d.ts +2 -2
- package/dist/internal/Schema.d.ts.map +1 -1
- package/dist/internal/Schema.js +4 -2
- package/dist/internal/Schema.js.map +1 -1
- package/dist/internal/Viem.d.ts +1 -1
- package/package.json +7 -2
- package/src/App.test.ts +9 -4
- package/src/App.ts +14 -2
- package/src/Client.test-d.ts +109 -28
- package/src/Scope.ts +5 -0
- package/src/admin/App.test.ts +4 -0
- package/src/admin/apps/early-access.ts +6 -5
- package/src/admin/apps/organizations.test.ts +86 -2
- package/src/admin/apps/organizations.ts +155 -31
- package/src/admin/apps/scopes.test.ts +5 -0
- package/src/admin/ui/src/lib/organizations.test.ts +81 -1
- package/src/admin/ui/src/lib/organizations.ts +92 -0
- package/src/admin/ui/src/routes/api-keys.tsx +1 -1
- package/src/admin/ui/src/routes/early-access.tsx +8 -3
- package/src/admin/ui/src/routes/organizations_.$orgId.tsx +103 -0
- package/src/admin/ui.generated.ts +49 -39
- package/src/analytics/Analytics.test.ts +83 -0
- package/src/analytics/Analytics.ts +20 -0
- package/src/analytics/tables/requestEvents.ts +60 -7
- package/src/apps/composition.test.ts +11 -2
- package/src/apps/data/App.test.ts +14 -2
- package/src/apps/data/FundingQuoteProviders.test.ts +0 -5
- package/src/apps/data/FundingQuoteProviders.ts +2 -29
- package/src/apps/data/FxOracle.test.ts +2 -2
- package/src/apps/data/FxOracle.ts +2 -2
- package/src/apps/data/routes/activities.test.ts +6 -2
- package/src/apps/data/routes/activities.ts +39 -1
- package/src/apps/data/routes/balances.test.ts +6 -2
- package/src/apps/data/routes/exchanges.test.ts +7 -3
- package/src/apps/data/routes/fee-amm.test.ts +47 -23
- package/src/apps/data/routes/fee-amm.ts +98 -119
- package/src/apps/data/routes/funding.test.ts +29 -8
- package/src/apps/data/routes/funding.ts +42 -48
- package/src/apps/data/routes/receipts.test.ts +6 -2
- package/src/apps/data/routes/tokenlist.test.ts +15 -0
- package/src/apps/data/routes/tokenlist.ts +18 -5
- package/src/apps/data/routes/tokens.ts +5 -1
- package/src/apps/data/routes/transfers.test.ts +203 -35
- package/src/apps/data/routes/transfers.ts +294 -172
- package/src/apps/data/routes/valuation.test.ts +18 -3
- package/src/apps/data/routes/valuation.ts +4 -4
- package/src/apps/data/routes/verified-tokens.test.ts +51 -0
- package/src/apps/data/routes/verified-tokens.ts +52 -15
- package/src/apps/data/routes/webhooks.test.ts +56 -0
- package/src/apps/data/routes/webhooks.ts +17 -8
- package/src/apps/index.ts +1 -0
- package/src/apps/management/routes/api-keys.test.ts +2 -75
- package/src/apps/management/routes/api-keys.ts +30 -7
- package/src/apps/management/routes/billing.test.ts +106 -85
- package/src/apps/management/routes/billing.ts +32 -21
- package/src/apps/management/routes/faucet.test.ts +11 -3
- package/src/apps/management/routes/faucet.ts +46 -30
- package/src/apps/management/routes/invitations.test.ts +0 -172
- package/src/apps/management/routes/invitations.ts +0 -3
- package/src/apps/management/routes/me.test.ts +3 -145
- package/src/apps/management/routes/me.ts +0 -6
- package/src/apps/management/routes/orgs.test.ts +45 -2
- package/src/apps/management/routes/orgs.ts +6 -0
- package/src/apps/management/routes/scopes.test.ts +1 -0
- package/src/apps/mpp/App.ts +86 -0
- package/src/apps/mpp/Mpp.test.ts +193 -0
- package/src/apps/mpp/Mpp.ts +353 -0
- package/src/apps/mpp/index.ts +2 -0
- package/src/apps/mpp/internal/Adapter.ts +11 -0
- package/src/apps/mpp/internal/Error.ts +9 -0
- package/src/apps/mpp/internal/Idempotency.ts +7 -0
- package/src/apps/mpp/internal/Screening.ts +10 -0
- package/src/apps/relay/App.test.ts +90 -8
- package/src/apps/relay/App.ts +62 -10
- package/src/apps/relay/Sponsorships.test.ts +2 -0
- package/src/apps/relay/Sponsorships.ts +1 -1
- package/src/db/Db.test.ts +2 -2
- package/src/db/Db.ts +30 -0
- package/src/db/Schema.ts +15 -0
- package/src/db/tables/enabledBillingSources.test.ts +303 -0
- package/src/db/tables/enabledBillingSources.ts +118 -0
- package/src/db/tables/invitations.ts +4 -26
- package/src/db/tables/organizations.test.ts +191 -0
- package/src/db/tables/organizations.ts +44 -3
- package/src/db/tables/sponsoredTransactions.test.ts +41 -2
- package/src/db/tables/sponsoredTransactions.ts +15 -3
- package/src/handlers/internal/utils.ts +22 -6
- package/src/handlers/relay.errors.test.ts +153 -0
- package/src/handlers/relay.ts +42 -7
- package/src/internal/Auth.test.ts +3 -3
- package/src/internal/Auth.ts +3 -63
- package/src/internal/EdgeCache.test.ts +18 -5
- package/src/internal/EdgeCache.ts +4 -2
- package/src/internal/FundingQuotes.test.ts +18 -5
- package/src/internal/FundingQuotes.ts +46 -11
- package/src/internal/Log.test.ts +91 -0
- package/src/internal/Log.ts +69 -5
- package/src/internal/MetricSink.test.ts +55 -0
- package/src/internal/MetricSink.ts +14 -0
- package/src/internal/Response.test.ts +28 -0
- package/src/internal/Response.ts +12 -4
- package/src/internal/Schema.test.ts +13 -0
- package/src/internal/Schema.ts +14 -7
package/src/internal/Log.ts
CHANGED
|
@@ -17,6 +17,8 @@ export type Variables = {
|
|
|
17
17
|
providerFailure?: Entry['provider'] | undefined
|
|
18
18
|
/** Redacted provider failures when multiple upstream adapters fail. */
|
|
19
19
|
providerFailures?: Entry['providerFailures'] | undefined
|
|
20
|
+
/** Bounded JSON-RPC failure metadata recorded by a composed RPC handler. */
|
|
21
|
+
rpcResponse?: Entry['rpc'] | undefined
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
/** Hono environment shape used by the request logger. */
|
|
@@ -71,7 +73,7 @@ export type Entry = {
|
|
|
71
73
|
errorCode?: string | undefined
|
|
72
74
|
/** Bounded funding provider attempts made while serving the request. */
|
|
73
75
|
fundingProviderAttempts?: readonly FundingProviderAttempt[] | undefined
|
|
74
|
-
/** Log level
|
|
76
|
+
/** Log level: 5xx `error`; 4xx or JSON-RPC failure `warn`; otherwise `info`. */
|
|
75
77
|
level: Level
|
|
76
78
|
/** Request method. */
|
|
77
79
|
method: string
|
|
@@ -121,7 +123,16 @@ export type Entry = {
|
|
|
121
123
|
requestId: string
|
|
122
124
|
/** Matched handler path (bounded cardinality), e.g. `/v1/tokens/:address`; `/*` when a middleware (e.g. an edge-cache hit) short-circuited routing. */
|
|
123
125
|
route: string
|
|
124
|
-
/**
|
|
126
|
+
/** JSON-RPC failures returned in an otherwise successful HTTP response. */
|
|
127
|
+
rpc?: {
|
|
128
|
+
/** Numeric JSON-RPC error code when every failed response shares one code. */
|
|
129
|
+
code?: number | undefined
|
|
130
|
+
/** Stable application error code from `error.data.code`, when shared by every failed response. */
|
|
131
|
+
dataCode?: string | undefined
|
|
132
|
+
/** Number of failed responses in the single or batch JSON-RPC response. */
|
|
133
|
+
errors: number
|
|
134
|
+
}
|
|
135
|
+
/** HTTP response status code. JSON-RPC failures retain their protocol-native `200`. */
|
|
125
136
|
status: number
|
|
126
137
|
/** Upstream operation durations in milliseconds, from the request's Server-Timing metrics (e.g. RPC and TIDX calls). */
|
|
127
138
|
timings?: Record<string, number> | undefined
|
|
@@ -138,8 +149,9 @@ export type Emit = (entry: Entry) => Promise<void> | void
|
|
|
138
149
|
*
|
|
139
150
|
* The entry derives everything from state other layers already produce —
|
|
140
151
|
* `requestId`, the auth principal, `RateLimit-*` headers, Server-Timing
|
|
141
|
-
* metrics, the `errorCode` recorded by `Response.error`,
|
|
142
|
-
* hit marker
|
|
152
|
+
* metrics, the `errorCode` recorded by `Response.error`, JSON-RPC response
|
|
153
|
+
* errors, and the edge-cache hit marker, so handlers never log per-request
|
|
154
|
+
* lines themselves.
|
|
143
155
|
*/
|
|
144
156
|
export function middleware<environment extends Environment = Environment>(
|
|
145
157
|
options: middleware.Options = {},
|
|
@@ -164,6 +176,7 @@ export function middleware<environment extends Environment = Environment>(
|
|
|
164
176
|
| undefined
|
|
165
177
|
const provider = c.get('providerFailure' as never) as Entry['provider']
|
|
166
178
|
const providerFailures = c.get('providerFailures' as never) as Entry['providerFailures']
|
|
179
|
+
const rpc = c.get('rpcResponse' as never) as Entry['rpc']
|
|
167
180
|
|
|
168
181
|
// The `request` total metric duplicates `duration` (measured here, one
|
|
169
182
|
// layer further out), so only operation timings are kept.
|
|
@@ -193,7 +206,12 @@ export function middleware<environment extends Environment = Environment>(
|
|
|
193
206
|
duration: round(duration),
|
|
194
207
|
...(errorCode === undefined ? {} : { errorCode }),
|
|
195
208
|
...(fundingProviderAttempts === undefined ? {} : { fundingProviderAttempts }),
|
|
196
|
-
level:
|
|
209
|
+
level:
|
|
210
|
+
status >= 500 || rpcServerError(rpc?.code)
|
|
211
|
+
? 'error'
|
|
212
|
+
: status >= 400 || rpc
|
|
213
|
+
? 'warn'
|
|
214
|
+
: 'info',
|
|
197
215
|
method: c.req.method,
|
|
198
216
|
path: url.pathname,
|
|
199
217
|
...(principal?.payment === undefined ? {} : { payment: principal.payment.reason }),
|
|
@@ -220,6 +238,7 @@ export function middleware<environment extends Environment = Environment>(
|
|
|
220
238
|
...(rateLimit === undefined ? {} : { rateLimit }),
|
|
221
239
|
requestId: c.get('requestId' as never) as string,
|
|
222
240
|
route: c.req.routePath,
|
|
241
|
+
...(rpc === undefined ? {} : { rpc }),
|
|
223
242
|
status,
|
|
224
243
|
...(timings === undefined ? {} : { timings }),
|
|
225
244
|
})
|
|
@@ -238,6 +257,51 @@ export function middleware<environment extends Environment = Environment>(
|
|
|
238
257
|
}
|
|
239
258
|
}
|
|
240
259
|
|
|
260
|
+
const rpcDataCode = /^[a-z][a-z0-9_]{0,63}$/
|
|
261
|
+
|
|
262
|
+
/** Extracts bounded error metadata from an already-parsed JSON-RPC response. */
|
|
263
|
+
export function rpcErrors(body: unknown): Entry['rpc'] {
|
|
264
|
+
const values = Array.isArray(body) ? body : [body]
|
|
265
|
+
const errors = values.flatMap((value) => {
|
|
266
|
+
if (!value || typeof value !== 'object') return []
|
|
267
|
+
const record = value as Record<string, unknown>
|
|
268
|
+
if (record['jsonrpc'] !== '2.0' || !record['error'] || typeof record['error'] !== 'object')
|
|
269
|
+
return []
|
|
270
|
+
const error = record['error'] as Record<string, unknown>
|
|
271
|
+
if (
|
|
272
|
+
!Number.isInteger(error['code']) ||
|
|
273
|
+
Number(error['code']) < -2_147_483_648 ||
|
|
274
|
+
Number(error['code']) > 2_147_483_647
|
|
275
|
+
)
|
|
276
|
+
return []
|
|
277
|
+
const data = error['data']
|
|
278
|
+
const dataCode_value =
|
|
279
|
+
data && typeof data === 'object' ? (data as Record<string, unknown>)['code'] : undefined
|
|
280
|
+
const dataCode = typeof dataCode_value === 'string' ? dataCode_value : undefined
|
|
281
|
+
return [
|
|
282
|
+
{
|
|
283
|
+
code: error['code'] as number,
|
|
284
|
+
...(dataCode && rpcDataCode.test(dataCode) ? { dataCode } : {}),
|
|
285
|
+
},
|
|
286
|
+
]
|
|
287
|
+
})
|
|
288
|
+
if (errors.length === 0) return undefined
|
|
289
|
+
|
|
290
|
+
const codes = new Set(errors.map((error) => error.code))
|
|
291
|
+
const dataCodes = new Set(errors.map((error) => error.dataCode).filter(Boolean))
|
|
292
|
+
return {
|
|
293
|
+
...(codes.size === 1 ? { code: errors[0]!.code } : {}),
|
|
294
|
+
...(dataCodes.size === 1 && errors.every((error) => error.dataCode !== undefined)
|
|
295
|
+
? { dataCode: errors[0]!.dataCode }
|
|
296
|
+
: {}),
|
|
297
|
+
errors: Math.min(errors.length, 65_535),
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function rpcServerError(code: number | undefined) {
|
|
302
|
+
return code === -32603 || (code !== undefined && code >= -32099 && code <= -32000)
|
|
303
|
+
}
|
|
304
|
+
|
|
241
305
|
export declare namespace middleware {
|
|
242
306
|
/** Options for the request logger middleware. */
|
|
243
307
|
type Options = {
|
|
@@ -10,6 +10,61 @@ type MetricEvent = {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
describe('requests', () => {
|
|
13
|
+
test('records JSON-RPC errors with bounded code tags', () => {
|
|
14
|
+
const events: MetricEvent[] = []
|
|
15
|
+
const metrics = Metrics.from({
|
|
16
|
+
count(name, value, tags) {
|
|
17
|
+
events.push({ name, tags, type: 'count', value })
|
|
18
|
+
},
|
|
19
|
+
flush() {},
|
|
20
|
+
gauge() {},
|
|
21
|
+
histogram() {},
|
|
22
|
+
})
|
|
23
|
+
const record = MetricSink.requests(metrics)
|
|
24
|
+
|
|
25
|
+
record({
|
|
26
|
+
duration: 10,
|
|
27
|
+
level: 'warn',
|
|
28
|
+
method: 'POST',
|
|
29
|
+
path: '/rpc/relay',
|
|
30
|
+
requestId: 'request_private',
|
|
31
|
+
route: '/rpc/relay',
|
|
32
|
+
rpc: { code: -32602, dataCode: 'billing_required', errors: 2 },
|
|
33
|
+
status: 200,
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
expect(events).toMatchInlineSnapshot(`
|
|
37
|
+
[
|
|
38
|
+
{
|
|
39
|
+
"name": "http_response_count",
|
|
40
|
+
"tags": {
|
|
41
|
+
"cache": "miss",
|
|
42
|
+
"method": "POST",
|
|
43
|
+
"principal_environment": "none",
|
|
44
|
+
"principal_type": "unknown",
|
|
45
|
+
"route": "/rpc/relay",
|
|
46
|
+
"status": 200,
|
|
47
|
+
},
|
|
48
|
+
"type": "count",
|
|
49
|
+
"value": 1,
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"name": "rpc_response_error_count",
|
|
53
|
+
"tags": {
|
|
54
|
+
"cache": "miss",
|
|
55
|
+
"method": "POST",
|
|
56
|
+
"principal_environment": "none",
|
|
57
|
+
"principal_type": "unknown",
|
|
58
|
+
"route": "/rpc/relay",
|
|
59
|
+
"rpc_error_code": "-32602",
|
|
60
|
+
},
|
|
61
|
+
"type": "count",
|
|
62
|
+
"value": 2,
|
|
63
|
+
},
|
|
64
|
+
]
|
|
65
|
+
`)
|
|
66
|
+
})
|
|
67
|
+
|
|
13
68
|
test('records bounded funding provider attempts without caller data', () => {
|
|
14
69
|
const events: MetricEvent[] = []
|
|
15
70
|
let flushes = 0
|
|
@@ -85,6 +85,12 @@ export function requests(backend: Metrics): RequestSink {
|
|
|
85
85
|
})
|
|
86
86
|
backend.histogram('http_response_duration_ms', entry.duration, tags)
|
|
87
87
|
|
|
88
|
+
if (entry.rpc)
|
|
89
|
+
backend.count('rpc_response_error_count', entry.rpc.errors, {
|
|
90
|
+
...tags,
|
|
91
|
+
rpc_error_code: rpcErrorCode(entry.rpc.code),
|
|
92
|
+
})
|
|
93
|
+
|
|
88
94
|
if (entry.status === 429 || entry.errorCode === 'rate_limit_exceeded')
|
|
89
95
|
backend.count('http_rate_limit_count', 1, {
|
|
90
96
|
...tags,
|
|
@@ -109,6 +115,14 @@ export function requests(backend: Metrics): RequestSink {
|
|
|
109
115
|
}
|
|
110
116
|
}
|
|
111
117
|
|
|
118
|
+
/** Maps arbitrary upstream JSON-RPC codes into bounded metric tags. */
|
|
119
|
+
function rpcErrorCode(code: number | undefined) {
|
|
120
|
+
if (code === undefined) return 'mixed'
|
|
121
|
+
if ([-32700, -32600, -32601, -32602, -32603].includes(code)) return String(code)
|
|
122
|
+
if (code >= -32099 && code <= -32000) return 'server'
|
|
123
|
+
return 'other'
|
|
124
|
+
}
|
|
125
|
+
|
|
112
126
|
function routeTags(entry: Log.Entry): RouteTags {
|
|
113
127
|
return {
|
|
114
128
|
cache: entry.cache ?? 'miss',
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Hono } from 'hono'
|
|
2
|
+
|
|
3
|
+
import * as Response from './Response.js'
|
|
4
|
+
|
|
5
|
+
describe('upstream', () => {
|
|
6
|
+
test('returns a stable message and preserves the cause on context', async () => {
|
|
7
|
+
const app = new Hono()
|
|
8
|
+
const cause = new Error('provider URL contains secret credentials')
|
|
9
|
+
let captured: Error | undefined
|
|
10
|
+
|
|
11
|
+
app.use(async (c, next) => {
|
|
12
|
+
await next()
|
|
13
|
+
captured = c.error
|
|
14
|
+
})
|
|
15
|
+
app.get('/', (c) => Response.upstream(c, cause))
|
|
16
|
+
|
|
17
|
+
const response = await app.request('/')
|
|
18
|
+
expect(await response.json()).toMatchInlineSnapshot(`
|
|
19
|
+
{
|
|
20
|
+
"error": {
|
|
21
|
+
"code": "upstream_error",
|
|
22
|
+
"message": "Upstream service could not complete the request",
|
|
23
|
+
},
|
|
24
|
+
}
|
|
25
|
+
`)
|
|
26
|
+
expect(captured).toBe(cause)
|
|
27
|
+
})
|
|
28
|
+
})
|
package/src/internal/Response.ts
CHANGED
|
@@ -39,12 +39,20 @@ export function unsupportedChainId(c: Context, chainId: number, supported: Itera
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
/**
|
|
42
|
-
* Maps an unexpected upstream failure to a 502 JSON error envelope.
|
|
43
|
-
*
|
|
42
|
+
* Maps an unexpected upstream failure to a stable 502 JSON error envelope.
|
|
43
|
+
* Preserves `Error` causes for reporting and rethrows non-`Error` causes.
|
|
44
44
|
*/
|
|
45
45
|
export function upstream(c: Context, cause: unknown) {
|
|
46
|
-
if (cause instanceof Error)
|
|
47
|
-
|
|
46
|
+
if (cause instanceof Error) {
|
|
47
|
+
// Preserve the original error for error-reporting middleware without
|
|
48
|
+
// exposing provider URLs, payloads, or credentials in the response.
|
|
49
|
+
c.error = cause
|
|
50
|
+
return error(c, {
|
|
51
|
+
code: 'upstream_error',
|
|
52
|
+
message: 'Upstream service could not complete the request',
|
|
53
|
+
status: 502,
|
|
54
|
+
})
|
|
55
|
+
}
|
|
48
56
|
throw cause
|
|
49
57
|
}
|
|
50
58
|
|
|
@@ -32,3 +32,16 @@ describe('ChainIdQuery', () => {
|
|
|
32
32
|
expect(Schema.ChainIdQuery.parse('42431')).toMatchInlineSnapshot(`42431`)
|
|
33
33
|
})
|
|
34
34
|
})
|
|
35
|
+
|
|
36
|
+
describe('timestampBound', () => {
|
|
37
|
+
test('normalizes RFC 3339 offsets to UTC', () => {
|
|
38
|
+
const bound = Schema.timestampBound('swaps', 'from')
|
|
39
|
+
|
|
40
|
+
expect(bound.parse('2026-06-04T12:00:00+05:30')).toMatchInlineSnapshot(
|
|
41
|
+
`"2026-06-04T06:30:00.000Z"`,
|
|
42
|
+
)
|
|
43
|
+
expect(bound.parse('2026-06-04T12:00:00-05:00')).toMatchInlineSnapshot(
|
|
44
|
+
`"2026-06-04T17:00:00.000Z"`,
|
|
45
|
+
)
|
|
46
|
+
})
|
|
47
|
+
})
|
package/src/internal/Schema.ts
CHANGED
|
@@ -495,16 +495,23 @@ export function blockNumberBound(noun: string, bound: 'from' | 'to') {
|
|
|
495
495
|
|
|
496
496
|
/**
|
|
497
497
|
* Optional inclusive ISO-8601 timestamp bound query parameter (`timestamp.from`
|
|
498
|
-
* / `timestamp.to`). `noun` is the
|
|
498
|
+
* / `timestamp.to`), normalized to UTC. `noun` is the listed resource.
|
|
499
499
|
*/
|
|
500
500
|
export function timestampBound(noun: string, bound: 'from' | 'to') {
|
|
501
501
|
const direction = bound === 'from' ? 'at or after' : 'at or before'
|
|
502
|
-
return z
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
502
|
+
return z
|
|
503
|
+
.optional(
|
|
504
|
+
z.pipe(
|
|
505
|
+
z.iso.datetime({ offset: true }),
|
|
506
|
+
z.transform((value) => new Date(value).toISOString()),
|
|
507
|
+
),
|
|
508
|
+
)
|
|
509
|
+
.check(
|
|
510
|
+
z.describe(`Only include ${noun} ${direction} this ISO 8601 timestamp.`),
|
|
511
|
+
z.meta({
|
|
512
|
+
examples: [bound === 'from' ? '2024-01-01T00:00:00Z' : '2024-12-31T23:59:59Z'],
|
|
513
|
+
}),
|
|
514
|
+
)
|
|
508
515
|
}
|
|
509
516
|
|
|
510
517
|
/**
|