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.
Files changed (288) hide show
  1. package/dist/App.d.ts +8641 -8263
  2. package/dist/App.d.ts.map +1 -1
  3. package/dist/App.js +9 -1
  4. package/dist/App.js.map +1 -1
  5. package/dist/Scope.d.ts +4 -0
  6. package/dist/Scope.d.ts.map +1 -1
  7. package/dist/Scope.js +5 -0
  8. package/dist/Scope.js.map +1 -1
  9. package/dist/admin/App.d.ts +112 -11
  10. package/dist/admin/App.d.ts.map +1 -1
  11. package/dist/admin/apps/api-keys.d.ts +2 -1
  12. package/dist/admin/apps/api-keys.d.ts.map +1 -1
  13. package/dist/admin/apps/early-access.d.ts +2 -2
  14. package/dist/admin/apps/early-access.d.ts.map +1 -1
  15. package/dist/admin/apps/early-access.js +5 -5
  16. package/dist/admin/apps/early-access.js.map +1 -1
  17. package/dist/admin/apps/organizations.d.ts +124 -1
  18. package/dist/admin/apps/organizations.d.ts.map +1 -1
  19. package/dist/admin/apps/organizations.js +97 -6
  20. package/dist/admin/apps/organizations.js.map +1 -1
  21. package/dist/admin/ui.generated.d.ts +1 -1
  22. package/dist/admin/ui.generated.d.ts.map +1 -1
  23. package/dist/admin/ui.generated.js +47 -39
  24. package/dist/admin/ui.generated.js.map +1 -1
  25. package/dist/analytics/Analytics.d.ts.map +1 -1
  26. package/dist/analytics/Analytics.js +20 -0
  27. package/dist/analytics/Analytics.js.map +1 -1
  28. package/dist/analytics/tables/requestEvents.d.ts +18 -0
  29. package/dist/analytics/tables/requestEvents.d.ts.map +1 -1
  30. package/dist/analytics/tables/requestEvents.js +36 -7
  31. package/dist/analytics/tables/requestEvents.js.map +1 -1
  32. package/dist/apps/data/App.d.ts +8050 -8019
  33. package/dist/apps/data/App.d.ts.map +1 -1
  34. package/dist/apps/data/FundingQuoteProviders.js +2 -15
  35. package/dist/apps/data/FundingQuoteProviders.js.map +1 -1
  36. package/dist/apps/data/FxOracle.d.ts +1 -1
  37. package/dist/apps/data/FxOracle.d.ts.map +1 -1
  38. package/dist/apps/data/FxOracle.js +1 -1
  39. package/dist/apps/data/FxOracle.js.map +1 -1
  40. package/dist/apps/data/routes/activities.d.ts +27 -27
  41. package/dist/apps/data/routes/activities.d.ts.map +1 -1
  42. package/dist/apps/data/routes/activities.js +21 -1
  43. package/dist/apps/data/routes/activities.js.map +1 -1
  44. package/dist/apps/data/routes/balances.d.ts +58 -58
  45. package/dist/apps/data/routes/blocks.d.ts +39 -39
  46. package/dist/apps/data/routes/coingecko.d.ts +176 -176
  47. package/dist/apps/data/routes/exchanges.d.ts +669 -669
  48. package/dist/apps/data/routes/fee-amm.d.ts +157 -143
  49. package/dist/apps/data/routes/fee-amm.d.ts.map +1 -1
  50. package/dist/apps/data/routes/fee-amm.js +76 -91
  51. package/dist/apps/data/routes/fee-amm.js.map +1 -1
  52. package/dist/apps/data/routes/funding.d.ts +72 -34
  53. package/dist/apps/data/routes/funding.d.ts.map +1 -1
  54. package/dist/apps/data/routes/funding.js +34 -26
  55. package/dist/apps/data/routes/funding.js.map +1 -1
  56. package/dist/apps/data/routes/indexer.d.ts +30 -30
  57. package/dist/apps/data/routes/receipts.d.ts +8 -8
  58. package/dist/apps/data/routes/rpc.d.ts +31 -31
  59. package/dist/apps/data/routes/tokenlist.d.ts.map +1 -1
  60. package/dist/apps/data/routes/tokenlist.js +12 -3
  61. package/dist/apps/data/routes/tokenlist.js.map +1 -1
  62. package/dist/apps/data/routes/tokens.d.ts +4 -4
  63. package/dist/apps/data/routes/tokens.d.ts.map +1 -1
  64. package/dist/apps/data/routes/tokens.js +5 -1
  65. package/dist/apps/data/routes/tokens.js.map +1 -1
  66. package/dist/apps/data/routes/transactions.d.ts +6 -6
  67. package/dist/apps/data/routes/transfers.d.ts +101 -90
  68. package/dist/apps/data/routes/transfers.d.ts.map +1 -1
  69. package/dist/apps/data/routes/transfers.js +226 -141
  70. package/dist/apps/data/routes/transfers.js.map +1 -1
  71. package/dist/apps/data/routes/valuation.d.ts +2 -2
  72. package/dist/apps/data/routes/valuation.js +3 -3
  73. package/dist/apps/data/routes/valuation.js.map +1 -1
  74. package/dist/apps/data/routes/verified-tokens.d.ts +36 -36
  75. package/dist/apps/data/routes/verified-tokens.d.ts.map +1 -1
  76. package/dist/apps/data/routes/verified-tokens.js +34 -9
  77. package/dist/apps/data/routes/verified-tokens.js.map +1 -1
  78. package/dist/apps/data/routes/webhooks.d.ts +2 -2
  79. package/dist/apps/data/routes/webhooks.js +14 -6
  80. package/dist/apps/data/routes/webhooks.js.map +1 -1
  81. package/dist/apps/index.d.ts +1 -0
  82. package/dist/apps/index.d.ts.map +1 -1
  83. package/dist/apps/index.js +1 -0
  84. package/dist/apps/index.js.map +1 -1
  85. package/dist/apps/management/App.d.ts +460 -523
  86. package/dist/apps/management/App.d.ts.map +1 -1
  87. package/dist/apps/management/routes/api-keys.d.ts +7 -23
  88. package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
  89. package/dist/apps/management/routes/api-keys.js +20 -8
  90. package/dist/apps/management/routes/api-keys.js.map +1 -1
  91. package/dist/apps/management/routes/billing.d.ts +106 -151
  92. package/dist/apps/management/routes/billing.d.ts.map +1 -1
  93. package/dist/apps/management/routes/billing.js +28 -28
  94. package/dist/apps/management/routes/billing.js.map +1 -1
  95. package/dist/apps/management/routes/faucet.d.ts +48 -34
  96. package/dist/apps/management/routes/faucet.d.ts.map +1 -1
  97. package/dist/apps/management/routes/faucet.js +33 -18
  98. package/dist/apps/management/routes/faucet.js.map +1 -1
  99. package/dist/apps/management/routes/invitations.d.ts +131 -131
  100. package/dist/apps/management/routes/invitations.d.ts.map +1 -1
  101. package/dist/apps/management/routes/invitations.js +0 -3
  102. package/dist/apps/management/routes/invitations.js.map +1 -1
  103. package/dist/apps/management/routes/me.d.ts +6 -8
  104. package/dist/apps/management/routes/me.d.ts.map +1 -1
  105. package/dist/apps/management/routes/me.js +0 -4
  106. package/dist/apps/management/routes/me.js.map +1 -1
  107. package/dist/apps/management/routes/members.d.ts +73 -73
  108. package/dist/apps/management/routes/orgs.d.ts +94 -94
  109. package/dist/apps/management/routes/orgs.d.ts.map +1 -1
  110. package/dist/apps/management/routes/orgs.js +5 -0
  111. package/dist/apps/management/routes/orgs.js.map +1 -1
  112. package/dist/apps/management/routes/usage.d.ts +72 -72
  113. package/dist/apps/management/routes/verified-token-requests.d.ts +94 -94
  114. package/dist/apps/management/routes/webhooks.d.ts +1 -1
  115. package/dist/apps/mpp/App.d.ts +363 -0
  116. package/dist/apps/mpp/App.d.ts.map +1 -0
  117. package/dist/apps/mpp/App.js +72 -0
  118. package/dist/apps/mpp/App.js.map +1 -0
  119. package/dist/apps/mpp/Mpp.d.ts +178 -0
  120. package/dist/apps/mpp/Mpp.d.ts.map +1 -0
  121. package/dist/apps/mpp/Mpp.js +183 -0
  122. package/dist/apps/mpp/Mpp.js.map +1 -0
  123. package/dist/apps/mpp/index.d.ts +3 -0
  124. package/dist/apps/mpp/index.d.ts.map +1 -0
  125. package/dist/apps/mpp/index.js +3 -0
  126. package/dist/apps/mpp/index.js.map +1 -0
  127. package/dist/apps/relay/App.d.ts.map +1 -1
  128. package/dist/apps/relay/App.js +57 -10
  129. package/dist/apps/relay/App.js.map +1 -1
  130. package/dist/apps/relay/Sponsorships.js +1 -1
  131. package/dist/apps/relay/Sponsorships.js.map +1 -1
  132. package/dist/db/Db.d.ts +2 -0
  133. package/dist/db/Db.d.ts.map +1 -1
  134. package/dist/db/Db.js +27 -0
  135. package/dist/db/Db.js.map +1 -1
  136. package/dist/db/Schema.d.ts +12 -0
  137. package/dist/db/Schema.d.ts.map +1 -1
  138. package/dist/db/Schema.js +11 -0
  139. package/dist/db/Schema.js.map +1 -1
  140. package/dist/db/tables/enabledBillingSources.d.ts +73 -0
  141. package/dist/db/tables/enabledBillingSources.d.ts.map +1 -0
  142. package/dist/db/tables/enabledBillingSources.js +69 -0
  143. package/dist/db/tables/enabledBillingSources.js.map +1 -0
  144. package/dist/db/tables/invitations.d.ts +1 -12
  145. package/dist/db/tables/invitations.d.ts.map +1 -1
  146. package/dist/db/tables/invitations.js +3 -23
  147. package/dist/db/tables/invitations.js.map +1 -1
  148. package/dist/db/tables/organizations.d.ts +5 -2
  149. package/dist/db/tables/organizations.d.ts.map +1 -1
  150. package/dist/db/tables/organizations.js +32 -2
  151. package/dist/db/tables/organizations.js.map +1 -1
  152. package/dist/db/tables/sponsoredTransactions.d.ts +2 -0
  153. package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -1
  154. package/dist/db/tables/sponsoredTransactions.js +13 -3
  155. package/dist/db/tables/sponsoredTransactions.js.map +1 -1
  156. package/dist/handlers/internal/utils.d.ts +1 -16
  157. package/dist/handlers/internal/utils.d.ts.map +1 -1
  158. package/dist/handlers/internal/utils.js +17 -6
  159. package/dist/handlers/internal/utils.js.map +1 -1
  160. package/dist/handlers/relay.d.ts.map +1 -1
  161. package/dist/handlers/relay.js +37 -5
  162. package/dist/handlers/relay.js.map +1 -1
  163. package/dist/internal/Auth.d.ts +0 -32
  164. package/dist/internal/Auth.d.ts.map +1 -1
  165. package/dist/internal/Auth.js +3 -51
  166. package/dist/internal/Auth.js.map +1 -1
  167. package/dist/internal/EdgeCache.d.ts +1 -1
  168. package/dist/internal/EdgeCache.d.ts.map +1 -1
  169. package/dist/internal/EdgeCache.js +3 -2
  170. package/dist/internal/EdgeCache.js.map +1 -1
  171. package/dist/internal/FundingQuotes.d.ts +78 -32
  172. package/dist/internal/FundingQuotes.d.ts.map +1 -1
  173. package/dist/internal/FundingQuotes.js +45 -8
  174. package/dist/internal/FundingQuotes.js.map +1 -1
  175. package/dist/internal/Log.d.ts +18 -4
  176. package/dist/internal/Log.d.ts.map +1 -1
  177. package/dist/internal/Log.js +50 -3
  178. package/dist/internal/Log.js.map +1 -1
  179. package/dist/internal/MetricSink.d.ts.map +1 -1
  180. package/dist/internal/MetricSink.js +15 -0
  181. package/dist/internal/MetricSink.js.map +1 -1
  182. package/dist/internal/Response.d.ts +2 -2
  183. package/dist/internal/Response.d.ts.map +1 -1
  184. package/dist/internal/Response.js +12 -4
  185. package/dist/internal/Response.js.map +1 -1
  186. package/dist/internal/Schema.d.ts +2 -2
  187. package/dist/internal/Schema.d.ts.map +1 -1
  188. package/dist/internal/Schema.js +4 -2
  189. package/dist/internal/Schema.js.map +1 -1
  190. package/dist/internal/Viem.d.ts +1 -1
  191. package/package.json +7 -2
  192. package/src/App.test.ts +9 -4
  193. package/src/App.ts +14 -2
  194. package/src/Client.test-d.ts +109 -28
  195. package/src/Scope.ts +5 -0
  196. package/src/admin/App.test.ts +4 -0
  197. package/src/admin/apps/early-access.ts +6 -5
  198. package/src/admin/apps/organizations.test.ts +86 -2
  199. package/src/admin/apps/organizations.ts +155 -31
  200. package/src/admin/apps/scopes.test.ts +5 -0
  201. package/src/admin/ui/src/lib/organizations.test.ts +81 -1
  202. package/src/admin/ui/src/lib/organizations.ts +92 -0
  203. package/src/admin/ui/src/routes/api-keys.tsx +1 -1
  204. package/src/admin/ui/src/routes/early-access.tsx +8 -3
  205. package/src/admin/ui/src/routes/organizations_.$orgId.tsx +103 -0
  206. package/src/admin/ui.generated.ts +49 -39
  207. package/src/analytics/Analytics.test.ts +83 -0
  208. package/src/analytics/Analytics.ts +20 -0
  209. package/src/analytics/tables/requestEvents.ts +60 -7
  210. package/src/apps/composition.test.ts +11 -2
  211. package/src/apps/data/App.test.ts +14 -2
  212. package/src/apps/data/FundingQuoteProviders.test.ts +0 -5
  213. package/src/apps/data/FundingQuoteProviders.ts +2 -29
  214. package/src/apps/data/FxOracle.test.ts +2 -2
  215. package/src/apps/data/FxOracle.ts +2 -2
  216. package/src/apps/data/routes/activities.test.ts +6 -2
  217. package/src/apps/data/routes/activities.ts +39 -1
  218. package/src/apps/data/routes/balances.test.ts +6 -2
  219. package/src/apps/data/routes/exchanges.test.ts +7 -3
  220. package/src/apps/data/routes/fee-amm.test.ts +47 -23
  221. package/src/apps/data/routes/fee-amm.ts +98 -119
  222. package/src/apps/data/routes/funding.test.ts +29 -8
  223. package/src/apps/data/routes/funding.ts +42 -48
  224. package/src/apps/data/routes/receipts.test.ts +6 -2
  225. package/src/apps/data/routes/tokenlist.test.ts +15 -0
  226. package/src/apps/data/routes/tokenlist.ts +18 -5
  227. package/src/apps/data/routes/tokens.ts +5 -1
  228. package/src/apps/data/routes/transfers.test.ts +203 -35
  229. package/src/apps/data/routes/transfers.ts +294 -172
  230. package/src/apps/data/routes/valuation.test.ts +18 -3
  231. package/src/apps/data/routes/valuation.ts +4 -4
  232. package/src/apps/data/routes/verified-tokens.test.ts +51 -0
  233. package/src/apps/data/routes/verified-tokens.ts +52 -15
  234. package/src/apps/data/routes/webhooks.test.ts +56 -0
  235. package/src/apps/data/routes/webhooks.ts +17 -8
  236. package/src/apps/index.ts +1 -0
  237. package/src/apps/management/routes/api-keys.test.ts +2 -75
  238. package/src/apps/management/routes/api-keys.ts +30 -7
  239. package/src/apps/management/routes/billing.test.ts +106 -85
  240. package/src/apps/management/routes/billing.ts +32 -21
  241. package/src/apps/management/routes/faucet.test.ts +11 -3
  242. package/src/apps/management/routes/faucet.ts +46 -30
  243. package/src/apps/management/routes/invitations.test.ts +0 -172
  244. package/src/apps/management/routes/invitations.ts +0 -3
  245. package/src/apps/management/routes/me.test.ts +3 -145
  246. package/src/apps/management/routes/me.ts +0 -6
  247. package/src/apps/management/routes/orgs.test.ts +45 -2
  248. package/src/apps/management/routes/orgs.ts +6 -0
  249. package/src/apps/management/routes/scopes.test.ts +1 -0
  250. package/src/apps/mpp/App.ts +86 -0
  251. package/src/apps/mpp/Mpp.test.ts +193 -0
  252. package/src/apps/mpp/Mpp.ts +353 -0
  253. package/src/apps/mpp/index.ts +2 -0
  254. package/src/apps/mpp/internal/Adapter.ts +11 -0
  255. package/src/apps/mpp/internal/Error.ts +9 -0
  256. package/src/apps/mpp/internal/Idempotency.ts +7 -0
  257. package/src/apps/mpp/internal/Screening.ts +10 -0
  258. package/src/apps/relay/App.test.ts +90 -8
  259. package/src/apps/relay/App.ts +62 -10
  260. package/src/apps/relay/Sponsorships.test.ts +2 -0
  261. package/src/apps/relay/Sponsorships.ts +1 -1
  262. package/src/db/Db.test.ts +2 -2
  263. package/src/db/Db.ts +30 -0
  264. package/src/db/Schema.ts +15 -0
  265. package/src/db/tables/enabledBillingSources.test.ts +303 -0
  266. package/src/db/tables/enabledBillingSources.ts +118 -0
  267. package/src/db/tables/invitations.ts +4 -26
  268. package/src/db/tables/organizations.test.ts +191 -0
  269. package/src/db/tables/organizations.ts +44 -3
  270. package/src/db/tables/sponsoredTransactions.test.ts +41 -2
  271. package/src/db/tables/sponsoredTransactions.ts +15 -3
  272. package/src/handlers/internal/utils.ts +22 -6
  273. package/src/handlers/relay.errors.test.ts +153 -0
  274. package/src/handlers/relay.ts +42 -7
  275. package/src/internal/Auth.test.ts +3 -3
  276. package/src/internal/Auth.ts +3 -63
  277. package/src/internal/EdgeCache.test.ts +18 -5
  278. package/src/internal/EdgeCache.ts +4 -2
  279. package/src/internal/FundingQuotes.test.ts +18 -5
  280. package/src/internal/FundingQuotes.ts +46 -11
  281. package/src/internal/Log.test.ts +91 -0
  282. package/src/internal/Log.ts +69 -5
  283. package/src/internal/MetricSink.test.ts +55 -0
  284. package/src/internal/MetricSink.ts +14 -0
  285. package/src/internal/Response.test.ts +28 -0
  286. package/src/internal/Response.ts +12 -4
  287. package/src/internal/Schema.test.ts +13 -0
  288. package/src/internal/Schema.ts +14 -7
@@ -16,8 +16,6 @@ import * as ApiKey from '../ApiKey.js'
16
16
  import * as ApiKeys from '../ApiKeys.js'
17
17
  import * as Db from '../db/Db.js'
18
18
  import type * as Store from './Store.js'
19
- import * as EarlyAccess from '../db/tables/earlyAccess.js'
20
- import * as Invitations from '../db/tables/invitations.js'
21
19
  import * as Memberships from '../db/tables/memberships.js'
22
20
  import * as Metadata from '../apps/metadata.js'
23
21
  import * as OpenApi from './OpenApi.js'
@@ -32,6 +30,7 @@ import * as Viem from './Viem.js'
32
30
 
33
31
  const policySymbol = Symbol('tempo-api.auth.policy')
34
32
  const mppManagementSymbol = Symbol('tempo-api.auth.mpp-management')
33
+ const defaultApiKeyRateLimit = { perMinute: 10_000 } satisfies RateLimit.Limit
35
34
  const defaultMppRateLimit = { perMinute: 100 } satisfies RateLimit.Limit
36
35
 
37
36
  /** Auth context consumed by Tempo API handlers. */
@@ -159,8 +158,6 @@ export type PolicyOverride = {
159
158
  | undefined
160
159
  /** Session lane: `true` allows signed-in wallet sessions, `false` disables. */
161
160
  session?: boolean | undefined
162
- /** Temporary early-access gate: `true` rejects sessions without early access ({@link tmp_earlyAccess}). TODO: remove after GA. */
163
- tmp_earlyAccess?: boolean | undefined
164
161
  }
165
162
 
166
163
  /** Authenticated or anonymous caller. */
@@ -229,8 +226,6 @@ export type Variables = {
229
226
  principal?: Principal | undefined
230
227
  /** Project resolved by {@link ensureProject} for the current request. */
231
228
  project?: Projects.Record | undefined
232
- /** Temporary early-access gate toggle; unset or false grants everyone early access. TODO: remove after GA. */
233
- tmp_earlyAccess?: boolean | undefined
234
229
  }
235
230
 
236
231
  /** Reads the authenticated principal from a Hono context. */
@@ -385,42 +380,6 @@ export function ensureProjectError(c: hono_Context) {
385
380
  })
386
381
  }
387
382
 
388
- /** Whether the caller can grant early access, independent of the temporary route gate. TODO: remove after GA. */
389
- export async function tmp_earlyAccessGrant(c: hono_Context): Promise<boolean> {
390
- const principal = getPrincipal(c)
391
- // Trust chain: `management:write` keys are only mintable by early-access owners.
392
- if (principal?.type === 'api_key' || principal?.type === 'super_admin') return true
393
- if (principal?.type !== 'session' || !principal.email) return false
394
- const email = principal.email.trim().toLowerCase()
395
- const db = Db.get(contextDb(c))
396
- if (await EarlyAccess.matches(db, email)) return true
397
- return Invitations.hasEarlyAccessGrant(db, email)
398
- }
399
-
400
- /** Whether the caller has effective early access, including a dormant temporary gate. TODO: remove after GA. */
401
- export async function tmp_earlyAccess(c: hono_Context): Promise<boolean> {
402
- const enabled = (c.get as (key: string) => unknown)('tmp_earlyAccess') as boolean | undefined
403
- if (!enabled) return true
404
- return tmp_earlyAccessGrant(c)
405
- }
406
-
407
- /**
408
- * Always false at runtime, but typed as `boolean` so handlers can include a
409
- * never-reached early-access-error branch in Hono's inferred response union
410
- * (the `tmp_earlyAccess` policy flag produces this `403` before the handler
411
- * runs). TODO: remove after GA.
412
- */
413
- export const tmp_narrowEarlyAccess = false as boolean
414
-
415
- /** Typed early-access error response for Hono client inference; also the live gate rejection. TODO: remove after GA. */
416
- export function tmp_ensureEarlyAccessError(c: hono_Context) {
417
- return Response.error(c, {
418
- code: 'early_access_required',
419
- message: 'Requires early access. Ask a teammate with access to invite you by email, or contact Tempo.', // prettier-ignore
420
- status: 403,
421
- })
422
- }
423
-
424
383
  /** Reads the caller's membership resolved by the organization or project guard; `undefined` for API keys and `super_admin`. */
425
384
  export function membership<environment extends Environment>(
426
385
  c: hono_Context<environment>,
@@ -629,9 +588,6 @@ export function middleware<
629
588
  if (management && (!auth.mpp || !endpointPolicy.mpp)) return next()
630
589
 
631
590
  c.set('auth', auth)
632
- // Consumed by `tmp_earlyAccess`; absent (or false) leaves the gate dormant.
633
- // TODO: remove after GA.
634
- c.set('tmp_earlyAccess', options.tmp_earlyAccess)
635
591
  return require<environment>(endpointPolicy)(c, next)
636
592
  })
637
593
  }
@@ -687,12 +643,6 @@ export declare namespace middleware {
687
643
  * checks — before the key lookup runs. Omit to disable the entry entirely.
688
644
  */
689
645
  superAdmin?: { secret: string } | undefined
690
- /**
691
- * Temporary invite-only gate for routes flagged via {@link policy}.
692
- * Allowlist entries live in the `early_access` table (admin dash); unset
693
- * grants everyone early access. TODO: remove after GA.
694
- */
695
- tmp_earlyAccess?: boolean | undefined
696
646
  }
697
647
 
698
648
  /** Resolve-cache tuning for API-key resolution. */
@@ -1196,7 +1146,6 @@ function resolvePolicy(
1196
1146
  let mpp = base.mpp
1197
1147
  let public_ = base.public
1198
1148
  let session = base.session
1199
- let tmp_earlyAccess = base.tmp_earlyAccess
1200
1149
 
1201
1150
  for (const override of overrides) {
1202
1151
  if (override === false) return undefined
@@ -1205,7 +1154,6 @@ function resolvePolicy(
1205
1154
  mpp = resolveMppLane(mpp, override.mpp, defaults.mpp)
1206
1155
  public_ = resolvePublicLane(public_, override.public, defaults.public)
1207
1156
  session = resolveSessionLane(session, override.session, defaults.session)
1208
- tmp_earlyAccess = override.tmp_earlyAccess ?? tmp_earlyAccess
1209
1157
  }
1210
1158
 
1211
1159
  // The OpenAPI summary/description rides along as the payment-challenge
@@ -1221,7 +1169,6 @@ function resolvePolicy(
1221
1169
  ...(mpp ? { mpp } : {}),
1222
1170
  ...(public_ ? { public: public_ } : {}),
1223
1171
  ...(session ? { session } : {}),
1224
- ...(tmp_earlyAccess ? { tmp_earlyAccess } : {}),
1225
1172
  }
1226
1173
  }
1227
1174
 
@@ -1444,12 +1391,6 @@ export function require<environment extends Environment = Environment>(
1444
1391
  )
1445
1392
  return Response.unsupportedChainId(c, chainId, supportedChainIds)
1446
1393
  const proceed = async () => {
1447
- // Runs after principal resolution, so api_key and super_admin pass by
1448
- // type; only gated sessions are rejected. TODO: remove after GA.
1449
- if (policy.tmp_earlyAccess && !(await tmp_earlyAccess(c))) {
1450
- c.res = tmp_ensureEarlyAccessError(c)
1451
- return
1452
- }
1453
1394
  await next()
1454
1395
  // Query credential responses must not enter shared caches.
1455
1396
  const cacheControl = c.res.headers.get('Cache-Control')?.toLowerCase()
@@ -1685,8 +1626,6 @@ export declare namespace require {
1685
1626
  | undefined
1686
1627
  /** Session access policy: `true` allows signed-in wallet sessions. */
1687
1628
  session?: boolean | undefined
1688
- /** Temporary early-access gate for this route; see {@link tmp_earlyAccess}. TODO: remove after GA. */
1689
- tmp_earlyAccess?: boolean | undefined
1690
1629
  }
1691
1630
  }
1692
1631
 
@@ -1782,7 +1721,8 @@ function resolveApiKeyLimit(options: resolveApiKeyLimit.Options): RateLimit.Limi
1782
1721
  policy?.rateLimit ?? // 2. route override (protective cap)
1783
1722
  apiKey.rateLimits?.[Scope.wildcard] ?? // 3. per-key default
1784
1723
  auth.apiKey?.rateLimits?.[scope] ?? // 4. config per-scope
1785
- auth.apiKey?.rateLimits?.[Scope.wildcard] ?? { perMinute: 100 } // 5. config default // 6. hardcoded fallback
1724
+ auth.apiKey?.rateLimits?.[Scope.wildcard] ?? // 5. config default
1725
+ defaultApiKeyRateLimit // 6. hardcoded fallback
1786
1726
  )
1787
1727
  }
1788
1728
 
@@ -219,7 +219,7 @@ describe('middleware', () => {
219
219
 
220
220
  test('rechecks route eligibility before every cache lookup', async () => {
221
221
  let eligible = true
222
- const { app, downstreamCalls, store } = createApp({ eligibility: () => eligible })
222
+ const { app, downstreamCalls, store } = createApp({ eligibility: [() => eligible] })
223
223
 
224
224
  await app.request('/cached')
225
225
  eligible = false
@@ -229,6 +229,19 @@ describe('middleware', () => {
229
229
  expect(store.gets).toBe(1)
230
230
  expect(store.size).toBe(1)
231
231
  })
232
+
233
+ test('requires every route eligibility predicate to permit caching', async () => {
234
+ const { app, downstreamCalls, store } = createApp({
235
+ eligibility: [() => true, () => false],
236
+ })
237
+
238
+ await app.request('/cached')
239
+ await app.request('/cached')
240
+
241
+ expect(downstreamCalls()).toBe(2)
242
+ expect(store.gets).toBe(0)
243
+ expect(store.size).toBe(0)
244
+ })
232
245
  })
233
246
 
234
247
  type Environment = { Variables: EdgeCache.Variables }
@@ -263,7 +276,7 @@ function memoryStore() {
263
276
 
264
277
  function createApp(
265
278
  options: {
266
- eligibility?: ((c: Context<Environment>) => boolean) | undefined
279
+ eligibility?: readonly ((c: Context<Environment>) => boolean)[] | undefined
267
280
  store?: Store.Store | undefined
268
281
  } = {},
269
282
  ) {
@@ -282,9 +295,9 @@ function createApp(
282
295
  const cacheable_marked = EdgeCache.markCacheable(
283
296
  createMiddleware<Environment>(async (_c, next) => next()),
284
297
  )
285
- const cacheable = options.eligibility
286
- ? EdgeCache.setEligibility(cacheable_marked, options.eligibility)
287
- : cacheable_marked
298
+ for (const predicate of options.eligibility ?? [])
299
+ EdgeCache.setEligibility(cacheable_marked, predicate)
300
+ const cacheable = cacheable_marked
288
301
  app.get('/cached', cacheable, (c) => {
289
302
  c.set('edgeCache', { maxAge: 30, staleWhileRevalidate: 300 })
290
303
  c.header('Age', '123')
@@ -154,12 +154,14 @@ export function markCacheable<const handler extends (...args: never[]) => unknow
154
154
  return Object.assign(handler, { [cacheableSymbol]: true })
155
155
  }
156
156
 
157
- /** Adds a request eligibility check to a cacheable route handler. */
157
+ /** Adds a request eligibility check; every check must permit caching. */
158
158
  export function setEligibility<const handler extends (...args: never[]) => unknown>(
159
159
  handler: handler,
160
160
  predicate: (c: Context) => boolean,
161
161
  ): handler {
162
- if (cacheableSymbol in handler) eligibility.set(handler, predicate)
162
+ const existing = eligibility.get(handler)
163
+ if (cacheableSymbol in handler)
164
+ eligibility.set(handler, existing ? (c) => existing(c) && predicate(c) : predicate)
163
165
  return handler
164
166
  }
165
167
 
@@ -40,16 +40,29 @@ describe('listQuotes', () => {
40
40
  expect(seen?.candidate.id).toBe('base-usdc-tempo-usdce-relay')
41
41
  expect(quotes).toHaveLength(1)
42
42
  expect(quotes[0]).toMatchObject({
43
+ destinationAmount: {
44
+ baseUnits: '990000',
45
+ currency: 'USD',
46
+ decimals: 6,
47
+ formatted: '0.99',
48
+ },
49
+ destinationAmountMin: {
50
+ baseUnits: '980000',
51
+ currency: 'USD',
52
+ decimals: 6,
53
+ formatted: '0.98',
54
+ },
43
55
  id: 'base-usdc-tempo-usdce-relay',
44
56
  quality: {
45
57
  liquiditySource: 'providerQuote',
46
58
  tier: 'liquid',
47
59
  },
48
- quote: {
49
- inputAmount: '1000000',
50
- minimumOutputAmount: '980000',
51
- outputAmount: '990000',
52
- outputAmountFormatted: '0.99',
60
+ quote: { sampledAt: '2026-01-01T00:00:00Z' },
61
+ sourceAmount: {
62
+ baseUnits: '1000000',
63
+ currency: 'USD',
64
+ decimals: 6,
65
+ formatted: '1',
53
66
  },
54
67
  })
55
68
  expect(attempts.map(stableAttempt)).toEqual([{ outcome: 'available', provider: 'relay' }])
@@ -1,3 +1,5 @@
1
+ import * as Value from './Value.js'
2
+
1
3
  /** Tempo mainnet as a CAIP-2 chain reference. */
2
4
  const destinationChain = {
3
5
  addressFormat: 'evm',
@@ -27,6 +29,7 @@ const sourceChains = [
27
29
  {
28
30
  address: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
29
31
  coingeckoId: 'usd-coin',
32
+ currency: 'USD',
30
33
  decimals: 6,
31
34
  name: 'USD Coin',
32
35
  standard: 'ERC-20',
@@ -35,6 +38,7 @@ const sourceChains = [
35
38
  {
36
39
  address: '0xdac17f958d2ee523a2206206994597c13d831ec7',
37
40
  coingeckoId: 'tether',
41
+ currency: 'USD',
38
42
  decimals: 6,
39
43
  name: 'Tether USD',
40
44
  standard: 'ERC-20',
@@ -43,6 +47,7 @@ const sourceChains = [
43
47
  {
44
48
  address: '0x6c3ea9036406852006290770bedfcaba0e23a0e8',
45
49
  coingeckoId: 'paypal-usd',
50
+ currency: 'USD',
46
51
  decimals: 6,
47
52
  name: 'PayPal USD',
48
53
  standard: 'ERC-20',
@@ -51,6 +56,7 @@ const sourceChains = [
51
56
  {
52
57
  address: '0x1abaea1f7c830bd89acc67ec4af516284b1bc33c',
53
58
  coingeckoId: 'euro-coin',
59
+ currency: 'EUR',
54
60
  decimals: 6,
55
61
  name: 'Euro Coin',
56
62
  standard: 'ERC-20',
@@ -59,6 +65,7 @@ const sourceChains = [
59
65
  {
60
66
  address: '0x6b175474e89094c44da98b954eedeac495271d0f',
61
67
  coingeckoId: 'dai',
68
+ currency: 'USD',
62
69
  decimals: 18,
63
70
  name: 'Dai Stablecoin',
64
71
  standard: 'ERC-20',
@@ -83,6 +90,7 @@ const sourceChains = [
83
90
  {
84
91
  address: '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913',
85
92
  coingeckoId: 'usd-coin',
93
+ currency: 'USD',
86
94
  decimals: 6,
87
95
  name: 'USD Coin',
88
96
  standard: 'ERC-20',
@@ -91,6 +99,7 @@ const sourceChains = [
91
99
  {
92
100
  address: '0xfde4c96c8593536e31f229ea8f37b2ada2699bb2',
93
101
  coingeckoId: 'tether',
102
+ currency: 'USD',
94
103
  decimals: 6,
95
104
  name: 'Tether USD',
96
105
  standard: 'ERC-20',
@@ -99,6 +108,7 @@ const sourceChains = [
99
108
  {
100
109
  address: '0x60a3e35cc302bfa44cb288bc5a4f316fdb1adb42',
101
110
  coingeckoId: 'euro-coin',
111
+ currency: 'EUR',
102
112
  decimals: 6,
103
113
  name: 'Euro Coin',
104
114
  standard: 'ERC-20',
@@ -122,6 +132,7 @@ const sourceChains = [
122
132
  {
123
133
  address: '0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d',
124
134
  coingeckoId: 'usd-coin',
135
+ currency: 'USD',
125
136
  decimals: 18,
126
137
  name: 'Binance-Peg USD Coin',
127
138
  standard: 'BEP-20',
@@ -130,6 +141,7 @@ const sourceChains = [
130
141
  {
131
142
  address: '0x55d398326f99059ff775485246999027b3197955',
132
143
  coingeckoId: 'tether',
144
+ currency: 'USD',
133
145
  decimals: 18,
134
146
  name: 'Tether USD',
135
147
  standard: 'BEP-20',
@@ -151,6 +163,7 @@ const sourceChains = [
151
163
  {
152
164
  address: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
153
165
  coingeckoId: 'usd-coin',
166
+ currency: 'USD',
154
167
  decimals: 6,
155
168
  name: 'USD Coin',
156
169
  standard: 'SPL',
@@ -173,6 +186,7 @@ const sourceChains = [
173
186
  {
174
187
  address: 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t',
175
188
  coingeckoId: 'tether',
189
+ currency: 'USD',
176
190
  decimals: 6,
177
191
  name: 'Tether USD',
178
192
  standard: 'TRC-20',
@@ -186,6 +200,7 @@ const sourceChains = [
186
200
  const destinationTokens = [
187
201
  {
188
202
  address: '0x20c0000000000000000000000000000000000000',
203
+ currency: 'USD',
189
204
  decimals: 6,
190
205
  name: 'pathUSD',
191
206
  slug: 'pathusd',
@@ -194,6 +209,7 @@ const destinationTokens = [
194
209
  },
195
210
  {
196
211
  address: '0x20c000000000000000000000b9537d11c60e8b50',
212
+ currency: 'USD',
197
213
  decimals: 6,
198
214
  name: 'Bridged USDC (Stargate)',
199
215
  slug: 'usdce',
@@ -202,6 +218,7 @@ const destinationTokens = [
202
218
  },
203
219
  {
204
220
  address: '0x20c0000000000000000000001621e21f71cf12fb',
221
+ currency: 'EUR',
205
222
  decimals: 6,
206
223
  name: 'Bridged EURC',
207
224
  slug: 'eurce',
@@ -210,6 +227,7 @@ const destinationTokens = [
210
227
  },
211
228
  {
212
229
  address: '0x20c00000000000000000000014f22ca97301eb73',
230
+ currency: 'USD',
213
231
  decimals: 6,
214
232
  name: 'USDT0',
215
233
  slug: 'usdt0',
@@ -218,6 +236,7 @@ const destinationTokens = [
218
236
  },
219
237
  {
220
238
  address: '0x20c0000000000000000000003554d28269e0f3c2',
239
+ currency: 'USD',
221
240
  decimals: 6,
222
241
  name: 'frxUSD',
223
242
  slug: 'frxusd',
@@ -346,8 +365,6 @@ export type QuoteProbeResult = {
346
365
  minimumOutputAmount?: string | undefined
347
366
  /** Expected destination amount in base units. */
348
367
  outputAmount?: string | undefined
349
- /** Expected destination amount rendered with destination token decimals. */
350
- outputAmountFormatted?: string | undefined
351
368
  /** Timestamp for the live quote probe. */
352
369
  sampledAt: string
353
370
  /** Provider-specific quote status for the requested amount. */
@@ -659,6 +676,7 @@ function formatChain(chain: SourceChainInput) {
659
676
  function formatDestinationToken(token: DestinationTokenInput) {
660
677
  return {
661
678
  address: token.address,
679
+ currency: token.currency,
662
680
  decimals: token.decimals,
663
681
  metadata: {},
664
682
  name: token.name,
@@ -706,6 +724,7 @@ function formatQuoteCandidate(options: {
706
724
  function formatSourceToken(chain: SourceChainInput, token: TokenInput) {
707
725
  return {
708
726
  address: token.address,
727
+ currency: token.currency,
709
728
  decimals: token.decimals,
710
729
  metadata: { coingeckoId: token.coingeckoId },
711
730
  name: token.name,
@@ -741,7 +760,24 @@ type SuccessfulQuoteProbe = QuoteProbeResult & {
741
760
 
742
761
  function formatFundingQuote(candidate: QuoteCandidate, quote: SuccessfulQuoteProbe) {
743
762
  return {
744
- ...candidate,
763
+ destinationAmount: Value.tokenAmount({
764
+ baseUnits: quote.outputAmount,
765
+ currency: candidate.destinationToken.currency,
766
+ decimals: candidate.destinationToken.decimals,
767
+ }),
768
+ ...(quote.minimumOutputAmount !== undefined
769
+ ? {
770
+ destinationAmountMin: Value.tokenAmount({
771
+ baseUnits: quote.minimumOutputAmount,
772
+ currency: candidate.destinationToken.currency,
773
+ decimals: candidate.destinationToken.decimals,
774
+ }),
775
+ }
776
+ : {}),
777
+ destinationChain: candidate.destinationChain,
778
+ destinationToken: candidate.destinationToken,
779
+ id: candidate.id,
780
+ provider: candidate.provider,
745
781
  quality: {
746
782
  ...(quote.quality?.estimatedSeconds !== undefined
747
783
  ? { estimatedSeconds: quote.quality.estimatedSeconds }
@@ -754,16 +790,15 @@ function formatFundingQuote(candidate: QuoteCandidate, quote: SuccessfulQuotePro
754
790
  },
755
791
  quote: {
756
792
  ...(quote.expiresAt !== undefined ? { expiresAt: quote.expiresAt } : {}),
757
- inputAmount: candidate.sourceAmount.amount,
758
- ...(quote.minimumOutputAmount !== undefined
759
- ? { minimumOutputAmount: quote.minimumOutputAmount }
760
- : {}),
761
- outputAmount: quote.outputAmount,
762
- outputAmountFormatted:
763
- quote.outputAmountFormatted ??
764
- decimalFromBaseUnits(quote.outputAmount, candidate.destinationToken.decimals),
765
793
  sampledAt: quote.sampledAt,
766
794
  },
795
+ sourceAmount: Value.tokenAmount({
796
+ baseUnits: candidate.sourceAmount.amount,
797
+ currency: candidate.sourceToken.currency,
798
+ decimals: candidate.sourceToken.decimals,
799
+ }),
800
+ sourceChain: candidate.sourceChain,
801
+ sourceToken: candidate.sourceToken,
767
802
  } as const
768
803
  }
769
804
 
@@ -138,6 +138,97 @@ describe('middleware', () => {
138
138
  `)
139
139
  })
140
140
 
141
+ test('records a JSON-RPC error returned with HTTP 200', async () => {
142
+ const entries: Log.Entry[] = []
143
+ const api = app({ emit: (entry) => void entries.push(entry) })
144
+ api.post('/rpc/sponsor', (c) => {
145
+ const body = {
146
+ error: {
147
+ code: -32602,
148
+ data: { code: 'billing_required', retryable: false },
149
+ message: 'Sponsorship requires an active billing source.',
150
+ },
151
+ id: 1,
152
+ jsonrpc: '2.0',
153
+ }
154
+ c.set('rpcResponse', Log.rpcErrors(body))
155
+ return c.json(body)
156
+ })
157
+
158
+ const response = await api.request('/rpc/sponsor', { method: 'POST' })
159
+
160
+ expect(response.status).toBe(200)
161
+ expect(stable(entries[0]!)).toMatchInlineSnapshot(`
162
+ {
163
+ "level": "warn",
164
+ "method": "POST",
165
+ "path": "/rpc/sponsor",
166
+ "route": "/rpc/sponsor",
167
+ "rpc": {
168
+ "code": -32602,
169
+ "dataCode": "billing_required",
170
+ "errors": 1,
171
+ },
172
+ "status": 200,
173
+ }
174
+ `)
175
+ })
176
+
177
+ test('logs JSON-RPC server errors at error level', async () => {
178
+ const entries: Log.Entry[] = []
179
+ const api = app({ emit: (entry) => void entries.push(entry) })
180
+ api.post('/rpc/sponsor', (c) => {
181
+ const body = {
182
+ error: {
183
+ code: -32603,
184
+ data: { code: 'internal_error' },
185
+ message: 'Internal error',
186
+ },
187
+ id: 1,
188
+ jsonrpc: '2.0',
189
+ }
190
+ c.set('rpcResponse', Log.rpcErrors(body))
191
+ return c.json(body)
192
+ })
193
+
194
+ await api.request('/rpc/sponsor', { method: 'POST' })
195
+
196
+ expect(entries[0]?.level).toBe('error')
197
+ })
198
+
199
+ test('counts batch JSON-RPC errors without retaining mixed codes', async () => {
200
+ const entries: Log.Entry[] = []
201
+ const api = app({ emit: (entry) => void entries.push(entry) })
202
+ api.post('/rpc/relay', (c) => {
203
+ const body = [
204
+ { id: 1, jsonrpc: '2.0', result: '0x1' },
205
+ { error: { code: -32602, message: 'Invalid params.' }, id: 2, jsonrpc: '2.0' },
206
+ {
207
+ error: { code: -32603, data: { code: 'Internal Error' }, message: 'Internal error.' },
208
+ id: 3,
209
+ jsonrpc: '2.0',
210
+ },
211
+ ]
212
+ c.set('rpcResponse', Log.rpcErrors(body))
213
+ return c.json(body)
214
+ })
215
+
216
+ await api.request('/rpc/relay', { method: 'POST' })
217
+
218
+ expect(stable(entries[0]!)).toMatchInlineSnapshot(`
219
+ {
220
+ "level": "warn",
221
+ "method": "POST",
222
+ "path": "/rpc/relay",
223
+ "route": "/rpc/relay",
224
+ "rpc": {
225
+ "errors": 2,
226
+ },
227
+ "status": 200,
228
+ }
229
+ `)
230
+ })
231
+
141
232
  test('includes redacted provider failure details', async () => {
142
233
  const entries: Log.Entry[] = []
143
234
  const api = app({ emit: (entry) => void entries.push(entry) })