tapimo 0.0.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (517) hide show
  1. package/README.md +38 -16
  2. package/dist/ApiKey.d.ts +14 -11
  3. package/dist/ApiKey.d.ts.map +1 -1
  4. package/dist/ApiKey.js +11 -12
  5. package/dist/ApiKey.js.map +1 -1
  6. package/dist/ApiKeys.d.ts +207 -0
  7. package/dist/ApiKeys.d.ts.map +1 -0
  8. package/dist/ApiKeys.js +282 -0
  9. package/dist/ApiKeys.js.map +1 -0
  10. package/dist/App.d.ts +6771 -3819
  11. package/dist/App.d.ts.map +1 -1
  12. package/dist/App.js +145 -317
  13. package/dist/App.js.map +1 -1
  14. package/dist/Handler.d.ts +86 -0
  15. package/dist/Handler.d.ts.map +1 -0
  16. package/dist/Handler.js +83 -0
  17. package/dist/Handler.js.map +1 -0
  18. package/dist/Scope.d.ts +14 -4
  19. package/dist/Scope.d.ts.map +1 -1
  20. package/dist/Scope.js +16 -4
  21. package/dist/Scope.js.map +1 -1
  22. package/dist/Webhooks.d.ts +5 -7
  23. package/dist/Webhooks.d.ts.map +1 -1
  24. package/dist/Webhooks.js +11 -4
  25. package/dist/Webhooks.js.map +1 -1
  26. package/dist/admin/Access.d.ts +1 -3
  27. package/dist/admin/Access.d.ts.map +1 -1
  28. package/dist/admin/Access.js +1 -3
  29. package/dist/admin/Access.js.map +1 -1
  30. package/dist/admin/App.d.ts +167 -205
  31. package/dist/admin/App.d.ts.map +1 -1
  32. package/dist/admin/App.js +2 -4
  33. package/dist/admin/App.js.map +1 -1
  34. package/dist/admin/apps/api-keys.d.ts +105 -5
  35. package/dist/admin/apps/api-keys.d.ts.map +1 -1
  36. package/dist/admin/apps/api-keys.js +75 -9
  37. package/dist/admin/apps/api-keys.js.map +1 -1
  38. package/dist/admin/apps/scopes.d.ts +6 -0
  39. package/dist/admin/apps/scopes.d.ts.map +1 -1
  40. package/dist/admin/apps/verified-tokens.d.ts +35 -149
  41. package/dist/admin/apps/verified-tokens.d.ts.map +1 -1
  42. package/dist/admin/apps/verified-tokens.js +18 -39
  43. package/dist/admin/apps/verified-tokens.js.map +1 -1
  44. package/dist/admin/ui.generated.d.ts +1 -1
  45. package/dist/admin/ui.generated.js +1 -1
  46. package/dist/apps/data/App.d.ts +19176 -0
  47. package/dist/apps/data/App.d.ts.map +1 -0
  48. package/dist/apps/data/App.js +236 -0
  49. package/dist/apps/data/App.js.map +1 -0
  50. package/dist/apps/data/index.d.ts +3 -0
  51. package/dist/apps/data/index.d.ts.map +1 -0
  52. package/dist/apps/data/index.js +3 -0
  53. package/dist/apps/data/index.js.map +1 -0
  54. package/dist/apps/{activities.d.ts → data/routes/activities.d.ts} +32 -32
  55. package/dist/apps/{activities.d.ts.map → data/routes/activities.d.ts.map} +1 -1
  56. package/dist/apps/{activities.js → data/routes/activities.js} +13 -13
  57. package/dist/apps/data/routes/activities.js.map +1 -0
  58. package/dist/apps/{addresses.d.ts → data/routes/addresses.d.ts} +22 -22
  59. package/dist/apps/{addresses.d.ts.map → data/routes/addresses.d.ts.map} +1 -1
  60. package/dist/apps/{addresses.js → data/routes/addresses.js} +5 -5
  61. package/dist/apps/data/routes/addresses.js.map +1 -0
  62. package/dist/apps/{balances.d.ts → data/routes/balances.d.ts} +49 -49
  63. package/dist/apps/data/routes/balances.d.ts.map +1 -0
  64. package/dist/apps/{balances.js → data/routes/balances.js} +42 -11
  65. package/dist/apps/data/routes/balances.js.map +1 -0
  66. package/dist/apps/{blocks.d.ts → data/routes/blocks.d.ts} +47 -47
  67. package/dist/apps/data/routes/blocks.d.ts.map +1 -0
  68. package/dist/apps/{blocks.js → data/routes/blocks.js} +13 -13
  69. package/dist/apps/data/routes/blocks.js.map +1 -0
  70. package/dist/apps/{coingecko.d.ts → data/routes/coingecko.d.ts} +183 -183
  71. package/dist/apps/data/routes/coingecko.d.ts.map +1 -0
  72. package/dist/apps/{coingecko.js → data/routes/coingecko.js} +13 -13
  73. package/dist/apps/data/routes/coingecko.js.map +1 -0
  74. package/dist/apps/{exchanges.d.ts → data/routes/exchanges.d.ts} +460 -460
  75. package/dist/apps/data/routes/exchanges.d.ts.map +1 -0
  76. package/dist/apps/{exchanges.js → data/routes/exchanges.js} +19 -19
  77. package/dist/apps/data/routes/exchanges.js.map +1 -0
  78. package/dist/apps/{fee-amm.d.ts → data/routes/fee-amm.d.ts} +94 -94
  79. package/dist/apps/data/routes/fee-amm.d.ts.map +1 -0
  80. package/dist/apps/{fee-amm.js → data/routes/fee-amm.js} +17 -17
  81. package/dist/apps/data/routes/fee-amm.js.map +1 -0
  82. package/dist/apps/{indexer.d.ts → data/routes/indexer.d.ts} +4 -4
  83. package/dist/apps/data/routes/indexer.d.ts.map +1 -0
  84. package/dist/apps/{indexer.js → data/routes/indexer.js} +13 -9
  85. package/dist/apps/data/routes/indexer.js.map +1 -0
  86. package/dist/apps/{receipts.d.ts → data/routes/receipts.d.ts} +6 -6
  87. package/dist/apps/data/routes/receipts.d.ts.map +1 -0
  88. package/dist/apps/{receipts.js → data/routes/receipts.js} +12 -12
  89. package/dist/apps/data/routes/receipts.js.map +1 -0
  90. package/dist/apps/{rpc.d.ts → data/routes/rpc.d.ts} +4 -4
  91. package/dist/apps/data/routes/rpc.d.ts.map +1 -0
  92. package/dist/apps/{rpc.js → data/routes/rpc.js} +6 -6
  93. package/dist/apps/data/routes/rpc.js.map +1 -0
  94. package/dist/apps/{tokenlist.d.ts → data/routes/tokenlist.d.ts} +4 -4
  95. package/dist/apps/data/routes/tokenlist.d.ts.map +1 -0
  96. package/dist/apps/{tokenlist.js → data/routes/tokenlist.js} +8 -8
  97. package/dist/apps/data/routes/tokenlist.js.map +1 -0
  98. package/dist/apps/{tokens.d.ts → data/routes/tokens.d.ts} +11 -11
  99. package/dist/apps/data/routes/tokens.d.ts.map +1 -0
  100. package/dist/apps/{tokens.js → data/routes/tokens.js} +19 -19
  101. package/dist/apps/data/routes/tokens.js.map +1 -0
  102. package/dist/apps/{transactions.d.ts → data/routes/transactions.d.ts} +288 -288
  103. package/dist/apps/data/routes/transactions.d.ts.map +1 -0
  104. package/dist/apps/{transactions.js → data/routes/transactions.js} +13 -13
  105. package/dist/apps/data/routes/transactions.js.map +1 -0
  106. package/dist/apps/{transfers.d.ts → data/routes/transfers.d.ts} +35 -35
  107. package/dist/apps/data/routes/transfers.d.ts.map +1 -0
  108. package/dist/apps/{transfers.js → data/routes/transfers.js} +13 -13
  109. package/dist/apps/data/routes/transfers.js.map +1 -0
  110. package/dist/apps/{verified-tokens.d.ts → data/routes/verified-tokens.d.ts} +42 -42
  111. package/dist/apps/data/routes/verified-tokens.d.ts.map +1 -0
  112. package/dist/apps/{verified-tokens.js → data/routes/verified-tokens.js} +9 -9
  113. package/dist/apps/data/routes/verified-tokens.js.map +1 -0
  114. package/dist/apps/{webhooks.d.ts → data/routes/webhooks.d.ts} +66 -63
  115. package/dist/apps/{webhooks.d.ts.map → data/routes/webhooks.d.ts.map} +1 -1
  116. package/dist/apps/{webhooks.js → data/routes/webhooks.js} +67 -59
  117. package/dist/apps/data/routes/webhooks.js.map +1 -0
  118. package/dist/apps/docs/App.d.ts +27 -0
  119. package/dist/apps/docs/App.d.ts.map +1 -0
  120. package/dist/apps/docs/App.js +122 -0
  121. package/dist/apps/docs/App.js.map +1 -0
  122. package/dist/{internal/OpenApi.fonts.d.ts → apps/docs/fonts.d.ts} +1 -1
  123. package/dist/apps/docs/fonts.d.ts.map +1 -0
  124. package/dist/{internal/OpenApi.fonts.js → apps/docs/fonts.js} +1 -1
  125. package/dist/apps/docs/fonts.js.map +1 -0
  126. package/dist/apps/docs/index.d.ts +2 -0
  127. package/dist/apps/docs/index.d.ts.map +1 -0
  128. package/dist/apps/docs/index.js +2 -0
  129. package/dist/apps/docs/index.js.map +1 -0
  130. package/dist/{internal/OpenApi.logo.d.ts → apps/docs/logo.d.ts} +1 -1
  131. package/dist/apps/docs/logo.d.ts.map +1 -0
  132. package/dist/{internal/OpenApi.logo.js → apps/docs/logo.js} +1 -1
  133. package/dist/apps/docs/logo.js.map +1 -0
  134. package/dist/apps/index.d.ts +4 -0
  135. package/dist/apps/index.d.ts.map +1 -0
  136. package/dist/apps/index.js +4 -0
  137. package/dist/apps/index.js.map +1 -0
  138. package/dist/apps/metadata.d.ts +7 -0
  139. package/dist/apps/metadata.d.ts.map +1 -0
  140. package/dist/apps/metadata.js +15 -0
  141. package/dist/apps/metadata.js.map +1 -0
  142. package/dist/apps/platform/App.d.ts +2985 -0
  143. package/dist/apps/platform/App.d.ts.map +1 -0
  144. package/dist/apps/platform/App.js +74 -0
  145. package/dist/apps/platform/App.js.map +1 -0
  146. package/dist/apps/platform/index.d.ts +2 -0
  147. package/dist/apps/platform/index.d.ts.map +1 -0
  148. package/dist/apps/platform/index.js +2 -0
  149. package/dist/apps/platform/index.js.map +1 -0
  150. package/dist/apps/platform/routes/api-keys.d.ts +681 -0
  151. package/dist/apps/platform/routes/api-keys.d.ts.map +1 -0
  152. package/dist/apps/platform/routes/api-keys.js +328 -0
  153. package/dist/apps/platform/routes/api-keys.js.map +1 -0
  154. package/dist/apps/platform/routes/invitations.d.ts +733 -0
  155. package/dist/apps/platform/routes/invitations.d.ts.map +1 -0
  156. package/dist/apps/platform/routes/invitations.js +413 -0
  157. package/dist/apps/platform/routes/invitations.js.map +1 -0
  158. package/dist/apps/platform/routes/me.d.ts +95 -0
  159. package/dist/apps/platform/routes/me.d.ts.map +1 -0
  160. package/dist/apps/platform/routes/me.js +81 -0
  161. package/dist/apps/platform/routes/me.js.map +1 -0
  162. package/dist/apps/platform/routes/members.d.ts +554 -0
  163. package/dist/apps/platform/routes/members.d.ts.map +1 -0
  164. package/dist/apps/platform/routes/members.js +251 -0
  165. package/dist/apps/platform/routes/members.js.map +1 -0
  166. package/dist/apps/platform/routes/orgs.d.ts +582 -0
  167. package/dist/apps/platform/routes/orgs.d.ts.map +1 -0
  168. package/dist/apps/platform/routes/orgs.js +265 -0
  169. package/dist/apps/platform/routes/orgs.js.map +1 -0
  170. package/dist/apps/platform/routes/projects.d.ts +740 -0
  171. package/dist/apps/platform/routes/projects.d.ts.map +1 -0
  172. package/dist/apps/platform/routes/projects.js +286 -0
  173. package/dist/apps/platform/routes/projects.js.map +1 -0
  174. package/dist/apps/relay/App.d.ts +41 -0
  175. package/dist/apps/relay/App.d.ts.map +1 -0
  176. package/dist/apps/relay/App.js +103 -0
  177. package/dist/apps/relay/App.js.map +1 -0
  178. package/dist/apps/relay/Sponsorships.d.ts +107 -0
  179. package/dist/apps/relay/Sponsorships.d.ts.map +1 -0
  180. package/dist/apps/relay/Sponsorships.js +193 -0
  181. package/dist/apps/relay/Sponsorships.js.map +1 -0
  182. package/dist/apps/relay/index.d.ts +3 -0
  183. package/dist/apps/relay/index.d.ts.map +1 -0
  184. package/dist/apps/relay/index.js +3 -0
  185. package/dist/apps/relay/index.js.map +1 -0
  186. package/dist/{Cli.d.ts → cli/Cli.d.ts} +13 -2
  187. package/dist/cli/Cli.d.ts.map +1 -0
  188. package/dist/cli/Cli.js +19 -0
  189. package/dist/cli/Cli.js.map +1 -0
  190. package/dist/cli/bin.d.ts +3 -0
  191. package/dist/cli/bin.d.ts.map +1 -0
  192. package/dist/cli/bin.js +4 -0
  193. package/dist/cli/bin.js.map +1 -0
  194. package/dist/cli/commands/admin.d.ts +15 -0
  195. package/dist/cli/commands/admin.d.ts.map +1 -0
  196. package/dist/cli/commands/admin.js +60 -0
  197. package/dist/cli/commands/admin.js.map +1 -0
  198. package/dist/cloudflare.d.ts +7 -0
  199. package/dist/cloudflare.d.ts.map +1 -1
  200. package/dist/cloudflare.js +9 -0
  201. package/dist/cloudflare.js.map +1 -1
  202. package/dist/db/Db.d.ts +113 -0
  203. package/dist/db/Db.d.ts.map +1 -0
  204. package/dist/db/Db.js +402 -0
  205. package/dist/db/Db.js.map +1 -0
  206. package/dist/db/Schema.d.ts +168 -0
  207. package/dist/db/Schema.d.ts.map +1 -0
  208. package/dist/db/Schema.js +229 -0
  209. package/dist/db/Schema.js.map +1 -0
  210. package/dist/db/tables/invitations.d.ts +84 -0
  211. package/dist/db/tables/invitations.d.ts.map +1 -0
  212. package/dist/db/tables/invitations.js +165 -0
  213. package/dist/db/tables/invitations.js.map +1 -0
  214. package/dist/db/tables/memberships.d.ts +108 -0
  215. package/dist/db/tables/memberships.d.ts.map +1 -0
  216. package/dist/db/tables/memberships.js +153 -0
  217. package/dist/db/tables/memberships.js.map +1 -0
  218. package/dist/db/tables/organizations.d.ts +123 -0
  219. package/dist/db/tables/organizations.d.ts.map +1 -0
  220. package/dist/db/tables/organizations.js +170 -0
  221. package/dist/db/tables/organizations.js.map +1 -0
  222. package/dist/db/tables/projects.d.ts +65 -0
  223. package/dist/db/tables/projects.d.ts.map +1 -0
  224. package/dist/db/tables/projects.js +75 -0
  225. package/dist/db/tables/projects.js.map +1 -0
  226. package/dist/db/tables/sponsoredTransactions.d.ts +120 -0
  227. package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -0
  228. package/dist/db/tables/sponsoredTransactions.js +161 -0
  229. package/dist/db/tables/sponsoredTransactions.js.map +1 -0
  230. package/dist/db/tables/users.d.ts +48 -0
  231. package/dist/db/tables/users.d.ts.map +1 -0
  232. package/dist/db/tables/users.js +65 -0
  233. package/dist/db/tables/users.js.map +1 -0
  234. package/dist/db/tables/verifiedTokens.d.ts +53 -0
  235. package/dist/db/tables/verifiedTokens.d.ts.map +1 -0
  236. package/dist/db/tables/verifiedTokens.js +74 -0
  237. package/dist/db/tables/verifiedTokens.js.map +1 -0
  238. package/dist/db/tables/webhookDeliveries.d.ts +71 -0
  239. package/dist/db/tables/webhookDeliveries.d.ts.map +1 -0
  240. package/dist/db/tables/webhookDeliveries.js +119 -0
  241. package/dist/db/tables/webhookDeliveries.js.map +1 -0
  242. package/dist/db/tables/webhookSubscriptions.d.ts +158 -0
  243. package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -0
  244. package/dist/db/tables/webhookSubscriptions.js +272 -0
  245. package/dist/db/tables/webhookSubscriptions.js.map +1 -0
  246. package/dist/handlers/internal/executionError.d.ts +25 -0
  247. package/dist/handlers/internal/executionError.d.ts.map +1 -0
  248. package/dist/handlers/internal/executionError.js +186 -0
  249. package/dist/handlers/internal/executionError.js.map +1 -0
  250. package/dist/handlers/internal/multisig.d.ts +233 -0
  251. package/dist/handlers/internal/multisig.d.ts.map +1 -0
  252. package/dist/handlers/internal/multisig.js +542 -0
  253. package/dist/handlers/internal/multisig.js.map +1 -0
  254. package/dist/handlers/internal/requestListener.d.ts +124 -0
  255. package/dist/handlers/internal/requestListener.d.ts.map +1 -0
  256. package/dist/handlers/internal/requestListener.js +173 -0
  257. package/dist/handlers/internal/requestListener.js.map +1 -0
  258. package/dist/handlers/internal/sponsorship.d.ts +113 -0
  259. package/dist/handlers/internal/sponsorship.d.ts.map +1 -0
  260. package/dist/handlers/internal/sponsorship.js +128 -0
  261. package/dist/handlers/internal/sponsorship.js.map +1 -0
  262. package/dist/handlers/internal/types.d.ts +284 -0
  263. package/dist/handlers/internal/types.d.ts.map +1 -0
  264. package/dist/handlers/internal/types.js +2 -0
  265. package/dist/handlers/internal/types.js.map +1 -0
  266. package/dist/handlers/internal/utils.d.ts +34 -0
  267. package/dist/handlers/internal/utils.d.ts.map +1 -0
  268. package/dist/handlers/internal/utils.js +131 -0
  269. package/dist/handlers/internal/utils.js.map +1 -0
  270. package/dist/handlers/relay.d.ts +123 -0
  271. package/dist/handlers/relay.d.ts.map +1 -0
  272. package/dist/handlers/relay.js +1278 -0
  273. package/dist/handlers/relay.js.map +1 -0
  274. package/dist/index.d.ts +4 -2
  275. package/dist/index.d.ts.map +1 -1
  276. package/dist/index.js +4 -2
  277. package/dist/index.js.map +1 -1
  278. package/dist/internal/Auth.d.ts +224 -16
  279. package/dist/internal/Auth.d.ts.map +1 -1
  280. package/dist/internal/Auth.js +572 -33
  281. package/dist/internal/Auth.js.map +1 -1
  282. package/dist/internal/Log.d.ts +7 -3
  283. package/dist/internal/Log.d.ts.map +1 -1
  284. package/dist/internal/Log.js +4 -0
  285. package/dist/internal/Log.js.map +1 -1
  286. package/dist/internal/OpenApi.d.ts +0 -19
  287. package/dist/internal/OpenApi.d.ts.map +1 -1
  288. package/dist/internal/OpenApi.js +0 -57
  289. package/dist/internal/OpenApi.js.map +1 -1
  290. package/dist/internal/RpcSchema.d.ts +10 -0
  291. package/dist/internal/RpcSchema.d.ts.map +1 -0
  292. package/dist/internal/RpcSchema.js +2 -0
  293. package/dist/internal/RpcSchema.js.map +1 -0
  294. package/dist/internal/Store.d.ts +49 -4
  295. package/dist/internal/Store.d.ts.map +1 -1
  296. package/dist/internal/Store.js +81 -5
  297. package/dist/internal/Store.js.map +1 -1
  298. package/dist/internal/VerifiedTokens.d.ts +15 -34
  299. package/dist/internal/VerifiedTokens.d.ts.map +1 -1
  300. package/dist/internal/VerifiedTokens.js +115 -159
  301. package/dist/internal/VerifiedTokens.js.map +1 -1
  302. package/dist/internal/Viem.d.ts +84 -0
  303. package/dist/internal/Viem.d.ts.map +1 -1
  304. package/dist/internal/Webhooks.d.ts +50 -36
  305. package/dist/internal/Webhooks.d.ts.map +1 -1
  306. package/dist/internal/Webhooks.js +92 -205
  307. package/dist/internal/Webhooks.js.map +1 -1
  308. package/package.json +37 -6
  309. package/src/ApiKey.test.ts +3 -3
  310. package/src/ApiKey.ts +11 -12
  311. package/src/ApiKeys.test.ts +278 -0
  312. package/src/ApiKeys.ts +358 -0
  313. package/src/App.test-d.ts +1 -1
  314. package/src/App.test.ts +40508 -19083
  315. package/src/App.ts +239 -416
  316. package/src/Client.test-d.ts +307 -16
  317. package/src/Handler.ts +154 -0
  318. package/src/Scope.ts +17 -4
  319. package/src/Webhooks.test.ts +8 -7
  320. package/src/Webhooks.ts +13 -7
  321. package/src/admin/Access.ts +1 -3
  322. package/src/admin/App.ts +19 -24
  323. package/src/admin/apps/api-keys.test.ts +70 -18
  324. package/src/admin/apps/api-keys.ts +92 -9
  325. package/src/admin/apps/scopes.test.ts +8 -0
  326. package/src/admin/apps/verified-tokens.test.ts +3 -30
  327. package/src/admin/apps/verified-tokens.ts +18 -37
  328. package/src/admin/ui.generated.ts +1 -1
  329. package/src/apps/composition.test.ts +66 -0
  330. package/src/apps/data/App.ts +295 -0
  331. package/src/apps/data/index.ts +2 -0
  332. package/src/apps/{activities.test.ts → data/routes/activities.test.ts} +3 -3
  333. package/src/apps/{activities.ts → data/routes/activities.ts} +15 -15
  334. package/src/apps/{addresses.ts → data/routes/addresses.ts} +6 -6
  335. package/src/apps/{balances.test.ts → data/routes/balances.test.ts} +6 -5
  336. package/src/apps/{balances.ts → data/routes/balances.ts} +46 -12
  337. package/src/apps/{blocks.test.ts → data/routes/blocks.test.ts} +4 -4
  338. package/src/apps/{blocks.ts → data/routes/blocks.ts} +14 -14
  339. package/src/apps/{coingecko.test.ts → data/routes/coingecko.test.ts} +2 -2
  340. package/src/apps/{coingecko.ts → data/routes/coingecko.ts} +14 -14
  341. package/src/apps/{exchanges.test.ts → data/routes/exchanges.test.ts} +2 -2
  342. package/src/apps/{exchanges.ts → data/routes/exchanges.ts} +20 -20
  343. package/src/apps/{fee-amm.test.ts → data/routes/fee-amm.test.ts} +3 -3
  344. package/src/apps/{fee-amm.ts → data/routes/fee-amm.ts} +18 -18
  345. package/src/apps/{indexer.test.ts → data/routes/indexer.test.ts} +27 -6
  346. package/src/apps/{indexer.ts → data/routes/indexer.ts} +13 -10
  347. package/src/apps/{receipts.test.ts → data/routes/receipts.test.ts} +4 -4
  348. package/src/apps/{receipts.ts → data/routes/receipts.ts} +13 -13
  349. package/src/apps/{rpc.test.ts → data/routes/rpc.test.ts} +142 -13
  350. package/src/apps/{rpc.ts → data/routes/rpc.ts} +7 -7
  351. package/src/apps/{tokenlist.test.ts → data/routes/tokenlist.test.ts} +8 -10
  352. package/src/apps/{tokenlist.ts → data/routes/tokenlist.ts} +9 -9
  353. package/src/apps/{tokens.test.ts → data/routes/tokens.test.ts} +6 -5
  354. package/src/apps/{tokens.ts → data/routes/tokens.ts} +20 -20
  355. package/src/apps/{transactions.test.ts → data/routes/transactions.test.ts} +4 -4
  356. package/src/apps/{transactions.ts → data/routes/transactions.ts} +15 -15
  357. package/src/apps/{transfers.test.ts → data/routes/transfers.test.ts} +3 -3
  358. package/src/apps/{transfers.ts → data/routes/transfers.ts} +15 -15
  359. package/src/apps/{verified-tokens.test.ts → data/routes/verified-tokens.test.ts} +27 -15
  360. package/src/apps/{verified-tokens.ts → data/routes/verified-tokens.ts} +10 -10
  361. package/src/apps/{webhooks.test.ts → data/routes/webhooks.test.ts} +137 -150
  362. package/src/apps/{webhooks.ts → data/routes/webhooks.ts} +83 -81
  363. package/src/apps/docs/App.ts +136 -0
  364. package/src/apps/docs/index.ts +1 -0
  365. package/src/apps/index.ts +3 -0
  366. package/src/apps/metadata.ts +23 -0
  367. package/src/apps/platform/App.ts +130 -0
  368. package/src/apps/platform/index.ts +1 -0
  369. package/src/apps/platform/routes/api-keys.test.ts +330 -0
  370. package/src/apps/platform/routes/api-keys.ts +427 -0
  371. package/src/apps/platform/routes/invitations.test.ts +361 -0
  372. package/src/apps/platform/routes/invitations.ts +519 -0
  373. package/src/apps/platform/routes/me.test.ts +121 -0
  374. package/src/apps/platform/routes/me.ts +106 -0
  375. package/src/apps/platform/routes/members.test.ts +195 -0
  376. package/src/apps/platform/routes/members.ts +313 -0
  377. package/src/apps/platform/routes/orgs.test.ts +313 -0
  378. package/src/apps/platform/routes/orgs.ts +343 -0
  379. package/src/apps/platform/routes/projects.test.ts +181 -0
  380. package/src/apps/platform/routes/projects.ts +367 -0
  381. package/src/apps/relay/App.test.ts +484 -0
  382. package/src/apps/relay/App.ts +128 -0
  383. package/src/apps/relay/Sponsorships.test.ts +375 -0
  384. package/src/apps/relay/Sponsorships.ts +305 -0
  385. package/src/apps/relay/index.ts +2 -0
  386. package/src/{Cli.test.ts → cli/Cli.test.ts} +10 -9
  387. package/src/{Cli.ts → cli/Cli.ts} +16 -9
  388. package/src/cli/bin.ts +4 -0
  389. package/src/cli/commands/admin.test.ts +71 -0
  390. package/src/cli/commands/admin.ts +60 -0
  391. package/src/cloudflare.ts +10 -0
  392. package/src/db/Db.test.ts +84 -0
  393. package/src/db/Db.ts +542 -0
  394. package/src/db/Schema.ts +301 -0
  395. package/src/db/tables/invitations.ts +198 -0
  396. package/src/db/tables/memberships.ts +208 -0
  397. package/src/db/tables/organizations.test.ts +160 -0
  398. package/src/db/tables/organizations.ts +232 -0
  399. package/src/db/tables/projects.ts +107 -0
  400. package/src/db/tables/sponsoredTransactions.test.ts +218 -0
  401. package/src/db/tables/sponsoredTransactions.ts +236 -0
  402. package/src/db/tables/users.ts +85 -0
  403. package/src/db/tables/verifiedTokens.ts +111 -0
  404. package/src/db/tables/webhookDeliveries.ts +159 -0
  405. package/src/db/tables/webhookSubscriptions.ts +358 -0
  406. package/src/handlers/internal/executionError.ts +207 -0
  407. package/src/handlers/internal/multisig.test.ts +818 -0
  408. package/src/handlers/internal/multisig.ts +823 -0
  409. package/src/handlers/internal/requestListener.ts +273 -0
  410. package/src/handlers/internal/sponsorship.ts +245 -0
  411. package/src/handlers/internal/types.ts +378 -0
  412. package/src/handlers/internal/utils.ts +153 -0
  413. package/src/handlers/relay.test.ts +2035 -0
  414. package/src/handlers/relay.ts +1623 -0
  415. package/src/index.ts +4 -2
  416. package/src/internal/Auth.test.ts +337 -21
  417. package/src/internal/Auth.ts +783 -44
  418. package/src/internal/Cursor.test.ts +3 -2
  419. package/src/internal/Log.test.ts +1 -1
  420. package/src/internal/Log.ts +11 -3
  421. package/src/internal/MetricSink.ts +1 -1
  422. package/src/internal/OpenApi.ts +0 -59
  423. package/src/internal/RateLimit.test.ts +1 -1
  424. package/src/internal/RpcSchema.ts +10 -0
  425. package/src/internal/Store.test.ts +150 -10
  426. package/src/internal/Store.ts +136 -11
  427. package/src/internal/Tidx.local.test.ts +1 -1
  428. package/src/internal/VerifiedTokens.test.ts +99 -115
  429. package/src/internal/VerifiedTokens.ts +123 -175
  430. package/src/internal/WebhookDestination.test.ts +1 -1
  431. package/src/internal/Webhooks.test.ts +111 -111
  432. package/src/internal/Webhooks.ts +103 -221
  433. package/dist/ApiKeySource.d.ts +0 -166
  434. package/dist/ApiKeySource.d.ts.map +0 -1
  435. package/dist/ApiKeySource.js +0 -234
  436. package/dist/ApiKeySource.js.map +0 -1
  437. package/dist/Cli.d.ts.map +0 -1
  438. package/dist/Cli.js +0 -13
  439. package/dist/Cli.js.map +0 -1
  440. package/dist/apps/activities.js.map +0 -1
  441. package/dist/apps/addresses.js.map +0 -1
  442. package/dist/apps/balances.d.ts.map +0 -1
  443. package/dist/apps/balances.js.map +0 -1
  444. package/dist/apps/blocks.d.ts.map +0 -1
  445. package/dist/apps/blocks.js.map +0 -1
  446. package/dist/apps/coingecko.d.ts.map +0 -1
  447. package/dist/apps/coingecko.js.map +0 -1
  448. package/dist/apps/exchanges.d.ts.map +0 -1
  449. package/dist/apps/exchanges.js.map +0 -1
  450. package/dist/apps/fee-amm.d.ts.map +0 -1
  451. package/dist/apps/fee-amm.js.map +0 -1
  452. package/dist/apps/indexer.d.ts.map +0 -1
  453. package/dist/apps/indexer.js.map +0 -1
  454. package/dist/apps/receipts.d.ts.map +0 -1
  455. package/dist/apps/receipts.js.map +0 -1
  456. package/dist/apps/rpc.d.ts.map +0 -1
  457. package/dist/apps/rpc.js.map +0 -1
  458. package/dist/apps/tokenlist.d.ts.map +0 -1
  459. package/dist/apps/tokenlist.js.map +0 -1
  460. package/dist/apps/tokens.d.ts.map +0 -1
  461. package/dist/apps/tokens.js.map +0 -1
  462. package/dist/apps/transactions.d.ts.map +0 -1
  463. package/dist/apps/transactions.js.map +0 -1
  464. package/dist/apps/transfers.d.ts.map +0 -1
  465. package/dist/apps/transfers.js.map +0 -1
  466. package/dist/apps/verified-tokens.d.ts.map +0 -1
  467. package/dist/apps/verified-tokens.js.map +0 -1
  468. package/dist/apps/webhooks.js.map +0 -1
  469. package/dist/internal/OpenApi.fonts.d.ts.map +0 -1
  470. package/dist/internal/OpenApi.fonts.js.map +0 -1
  471. package/dist/internal/OpenApi.logo.d.ts.map +0 -1
  472. package/dist/internal/OpenApi.logo.js.map +0 -1
  473. package/seeds/31318/icons/0x20c0000000000000000000000000000000000000.svg +0 -1
  474. package/seeds/31318/icons/0x20c0000000000000000000000000000000000001.svg +0 -1
  475. package/seeds/31318/icons/0x20c0000000000000000000000000000000000002.svg +0 -1
  476. package/seeds/31318/icons/0x20c0000000000000000000000000000000000003.svg +0 -1
  477. package/seeds/31318/verified-tokens.json +0 -30
  478. package/seeds/4217/icons/0x20c0000000000000000000000000000000000000.svg +0 -25
  479. package/seeds/4217/icons/0x20c000000000000000000000048c8f36df1c9a4a.svg +0 -1
  480. package/seeds/4217/icons/0x20c0000000000000000000000520792dcccccccc.svg +0 -1
  481. package/seeds/4217/icons/0x20c0000000000000000000000a6da882d075a4c3.svg +0 -4
  482. package/seeds/4217/icons/0x20c000000000000000000000111111111e910f0f.svg +0 -30
  483. package/seeds/4217/icons/0x20c00000000000000000000014f22ca97301eb73.svg +0 -1
  484. package/seeds/4217/icons/0x20c0000000000000000000001621e21f71cf12fb.svg +0 -1
  485. package/seeds/4217/icons/0x20c0000000000000000000002f52d5cc21a3207b.svg +0 -1
  486. package/seeds/4217/icons/0x20c0000000000000000000003158081efd85bfc2.svg +0 -11
  487. package/seeds/4217/icons/0x20c0000000000000000000003554d28269e0f3c2.svg +0 -1
  488. package/seeds/4217/icons/0x20c000000000000000000000383a23bacb546ab9.svg +0 -1
  489. package/seeds/4217/icons/0x20c00000000000000000000042109aef2f8b28e1.png +0 -0
  490. package/seeds/4217/icons/0x20c0000000000000000000005c0bac7cef389a11.svg +0 -1
  491. package/seeds/4217/icons/0x20c0000000000000000000006fd9a167923ba194.png +0 -0
  492. package/seeds/4217/icons/0x20c0000000000000000000007f7ba549dd0251b9.svg +0 -1
  493. package/seeds/4217/icons/0x20c0000000000000000000008191667423f70e67.svg +0 -5
  494. package/seeds/4217/icons/0x20c0000000000000000000008ee4fcff88888888.svg +0 -1
  495. package/seeds/4217/icons/0x20c0000000000000000000009a4a4b17e0dc6651.svg +0 -1
  496. package/seeds/4217/icons/0x20c000000000000000000000ab02d39df30bd17e.svg +0 -1
  497. package/seeds/4217/icons/0x20c000000000000000000000ae247a1130450f09.svg +0 -1
  498. package/seeds/4217/icons/0x20c000000000000000000000aeed2ec36a54d0e5.svg +0 -1
  499. package/seeds/4217/icons/0x20c000000000000000000000b9537d11c60e8b50.svg +0 -1
  500. package/seeds/4217/icons/0x20c000000000000000000000bd95bfb69fbe6ce3.svg +0 -1
  501. package/seeds/4217/icons/0x20c000000000000000000000c412ec89d0c08be5.svg +0 -1
  502. package/seeds/4217/icons/0x20c000000000000000000000f047dd7018e50367.png +0 -0
  503. package/seeds/4217/verified-tokens.json +0 -177
  504. package/seeds/42431/icons/0x20c0000000000000000000000000000000000000.svg +0 -25
  505. package/seeds/42431/icons/0x20c0000000000000000000000000000000000001.svg +0 -1
  506. package/seeds/42431/icons/0x20c0000000000000000000000000000000000002.svg +0 -1
  507. package/seeds/42431/icons/0x20c0000000000000000000000000000000000003.svg +0 -1
  508. package/seeds/42431/icons/0x20c0000000000000000000009e8d7eb59b783726.svg +0 -1
  509. package/seeds/42431/icons/0x20c000000000000000000000d72572838bbee59c.svg +0 -1
  510. package/seeds/42431/verified-tokens.json +0 -44
  511. package/seeds/openrpc/README.md +0 -27
  512. package/seeds/openrpc/execution-apis.json +0 -8989
  513. package/src/ApiKeySource.test-d.ts +0 -11
  514. package/src/ApiKeySource.test.ts +0 -295
  515. package/src/ApiKeySource.ts +0 -303
  516. /package/src/{internal/OpenApi.fonts.ts → apps/docs/fonts.ts} +0 -0
  517. /package/src/{internal/OpenApi.logo.ts → apps/docs/logo.ts} +0 -0
package/src/App.ts CHANGED
@@ -1,16 +1,15 @@
1
- import type { Context, MiddlewareHandler } from 'hono'
1
+ import type { Context } from 'hono'
2
2
  import { Hono } from 'hono'
3
3
  import { compress as hono_compress } from 'hono/compress'
4
4
  import { cors as hono_cors } from 'hono/cors'
5
5
  import { HTTPException } from 'hono/http-exception'
6
6
  import { requestId, type RequestIdVariables } from 'hono/request-id'
7
7
  import { generateSpecs } from 'hono-openapi'
8
- import { Handler } from 'vocs/server'
9
- import * as z from 'zod/mini'
10
8
 
11
9
  import * as ApiKey from './ApiKey.js'
12
10
  import * as Assets from './Assets.js'
13
11
  import * as Auth from './internal/Auth.js'
12
+ import * as Db from './db/Db.js'
14
13
  import * as EdgeCache from './internal/EdgeCache.js'
15
14
  import * as Log from './internal/Log.js'
16
15
  import * as MetricSink from './internal/MetricSink.js'
@@ -19,28 +18,23 @@ import * as OpenApi from './internal/OpenApi.js'
19
18
  import * as Store from './internal/Store.js'
20
19
  import * as Path from './internal/Path.js'
21
20
  import * as Response from './internal/Response.js'
22
- import * as Schema from './internal/Schema.js'
23
- import * as Tidx from './internal/Tidx.js'
21
+ import type * as Tidx from './internal/Tidx.js'
24
22
  import * as Timing from './internal/Timing.js'
25
23
  import * as Viem from './internal/Viem.js'
26
24
 
27
25
  import openrpcSpec from './openrpc.json' with { type: 'json' }
28
26
 
29
- import { transactions as transactionActivities } from './apps/activities.js'
30
- import { addresses } from './apps/addresses.js'
31
- import { blocks, blocksByTimestampCompat } from './apps/blocks.js'
32
- import { coingecko } from './apps/coingecko.js'
33
- import { exchanges } from './apps/exchanges.js'
34
- import { feeAmm } from './apps/fee-amm.js'
35
- import { indexer } from './apps/indexer.js'
36
- import { receipts } from './apps/receipts.js'
37
- import { rpc as rpcApp } from './apps/rpc.js'
38
- import { tokens } from './apps/tokens.js'
39
- import { transactions } from './apps/transactions.js'
40
- import { transfers } from './apps/transfers.js'
41
- import { verifiedTokens } from './apps/verified-tokens.js'
42
- import { tokenlist } from './apps/tokenlist.js'
43
- import { schema as webhookSchema, webhooks } from './apps/webhooks.js'
27
+ import * as Metadata from './apps/metadata.js'
28
+ import * as Data from './apps/data/App.js'
29
+ import * as Platform from './apps/platform/App.js'
30
+
31
+ export import data = Data.data
32
+ export import platform = Platform.platform
33
+
34
+ /** The chain-data route group; see {@link data}. */
35
+ export type DataApp = ReturnType<typeof data>
36
+ /** The tenant-platform route group; see {@link platform}. */
37
+ export type PlatformApp = ReturnType<typeof platform>
44
38
 
45
39
  /** Hono environment variables set by the Tempo API app. */
46
40
  export type Environment = {
@@ -50,6 +44,12 @@ export type Environment = {
50
44
  basePath: string
51
45
  /** Default Tempo chain id for this API app. */
52
46
  chainId: Viem.ChainId
47
+ /** Authoritative Postgres store, or a per-request factory. Resolve at the leaf with `Db.get`. */
48
+ db: Db.Source
49
+ /** Store for staleness-tolerant reads (a caching Hyperdrive config when configured); falls back to `db`. */
50
+ dbCached: Db.Source
51
+ /** KV state store backing API-key records, or undefined when key auth is closed. */
52
+ kv: { store: Store.State } | undefined
53
53
  /** Gets one static API asset. */
54
54
  getAsset: GetAsset
55
55
  /** Gets a Tempo RPC client for a chain id. */
@@ -64,25 +64,28 @@ export type Environment = {
64
64
  supportedChainIds: ReadonlySet<number>
65
65
  /** Configured TIDX client options used by the raw indexer passthrough. */
66
66
  tidx: Tidx.getClient.Tidx | undefined
67
- /** Verified-token feature configuration (state store + refresh window), or undefined when verified tokens are bundle-backed. */
68
- verifiedTokens: { refreshMs?: number | undefined; store: Store.State } | undefined
69
- /** Webhook feature configuration (state store, etc.), or undefined when webhooks are disabled. */
70
- webhook: { maxPerOwner?: number | undefined; store: Store.State } | undefined
67
+ /** Verified-token feature configuration (refresh window), or undefined when the feature is disabled. Token rows live in `db`. */
68
+ verifiedTokens: { refreshMs?: number | undefined } | undefined
69
+ /** Webhook feature configuration, or undefined when webhooks are disabled. Rows live in `db`. */
70
+ webhook: { maxPerOwner?: number | undefined } | undefined
71
71
  } & EdgeCache.Variables &
72
72
  Log.Variables &
73
73
  Timing.Variables
74
74
  }
75
75
 
76
76
  /**
77
- * Tempo API Hono app with shared API dependencies. Inferred from `create`.
78
- * The `path` generic is pinned to `'/'` (its default) so the resulting Hono
79
- * `BasePath` is a concrete literal; without the explicit type argument
80
- * `ReturnType<typeof create>` would resolve `path` to its constraint
81
- * (`string`), and Hono's `MergePath<string, '/foo'>` would surface every
82
- * endpoint key as `` `… ${string}/foo` `` — which can be satisfied but never
83
- * autocompleted.
77
+ * The canonical fully-composed Tempo API app ({@link create} with both route
78
+ * groups). {@link Client} and the reference worker target this type; a subset
79
+ * consumer infers its own narrower type from its own composition chain.
84
80
  */
85
- export type App = ReturnType<typeof create<'/'>>
81
+ export type App = ReturnType<typeof compose>
82
+
83
+ // Type anchor only — never invoked. Expresses the canonical composition so
84
+ // `App` (and thus `Client`) reflect the full route surface; real consumers
85
+ // compose explicitly.
86
+ function compose(options: create.Options) {
87
+ return create(options).route('/', data()).route('/', platform())
88
+ }
86
89
 
87
90
  /** Node.js request listener for `node:http` and compatible interfaces. */
88
91
  export type Listener = (incoming: unknown, outgoing: unknown) => Promise<void>
@@ -90,6 +93,9 @@ export type Listener = (incoming: unknown, outgoing: unknown) => Promise<void>
90
93
  /** HTTP endpoints owned by Tempo API handlers, used for typed auth overrides. */
91
94
  export type Endpoint = Auth.Endpoint<App>
92
95
 
96
+ /** Transactional email sender for platform emails; owned by {@link platform}. */
97
+ export type Email = Platform.Email
98
+
93
99
  /** One static API asset resolved from packaged data. */
94
100
  export type Asset = {
95
101
  /** Loaded asset response. */
@@ -108,8 +114,8 @@ export type GetAsset = (
108
114
  const infoDescription = 'REST API for reading and interacting with Tempo'
109
115
 
110
116
  /** Creates the Tempo API Hono app. */
111
- export function create<const path extends string = '/'>(options: create.Options<path> = {}) {
112
- const { assets, defaultChainId = Viem.defaultChainId, path = '/' as path, rpc } = options
117
+ export function create<const path extends string = '/'>(options: create.Options<path>) {
118
+ const { path = '/' as path } = options
113
119
 
114
120
  // Capture as the literal generic so `Hono.basePath(...)` preserves the
115
121
  // typed prefix. testClient's `Client<T, ...>` inference collapses to a
@@ -123,35 +129,10 @@ export function create<const path extends string = '/'>(options: create.Options<
123
129
  // miss simply re-fetches — so it defaults to in-memory. Point it at a
124
130
  // colo-local store (`Store.cache(caches.default)`) in production; never the
125
131
  // Durable Object, or edge-cache hits re-incur the round-trip they skip.
126
- // Authoritative state (webhooks) lives in its own state store, configured
127
- // separately via `options.webhook.store` (a Durable Object in production).
132
+ // Authoritative state lives elsewhere: webhooks + verified tokens in
133
+ // `options.db`, API-key records in `options.kv`.
128
134
  const store = options.cache?.store ?? Store.memory()
129
135
 
130
- // Memoize one viem client per resolved upstream. Clients are stateless HTTP
131
- // transports, so reusing them avoids per-request construction cost. The cache
132
- // key is the resolved output (`url` + `basicAuth` + `chainId`), not the
133
- // principal, so a resolver `rpc` may branch on any principal field while
134
- // client cardinality stays bounded by the number of distinct upstreams —
135
- // callers that resolve to the same upstream share a client.
136
- const clients = new Map<string, ReturnType<typeof Viem.getClient>>()
137
- const getClient = (chainId = defaultChainId, principal: Auth.Principal | null = null) => {
138
- // Resolve to derive the cache key; `Viem.getClient` re-resolves to build the
139
- // client on a miss. `resolve` is pure and cheap, so the extra call is free.
140
- const { url, basicAuth } = Viem.resolve(rpc, { chainId, principal })
141
- const cacheKey = `${url}|${basicAuth ?? ''}|${chainId}`
142
- const existing = clients.get(cacheKey)
143
- if (existing) return existing
144
- const client = Viem.getClient({ chainId, principal, rpc })
145
- clients.set(cacheKey, client)
146
- return client
147
- }
148
-
149
- // Memoize TIDX clients per `chainId` so the base URL resolver runs once per
150
- // chain instead of on every request. The same factory backs the scheduled
151
- // webhook poller (`Webhooks.createPoller`), keeping client construction in
152
- // one place.
153
- const getTidx = Tidx.createGetClient({ defaultChainId, tidx: options.tidx })
154
-
155
136
  // Build the app in two phases: register middleware imperatively, then chain
156
137
  // route mounts so the merged Hono schema flows through the return type. The
157
138
  // chained type is what makes `testClient(create())` produce typed path
@@ -197,8 +178,8 @@ export function create<const path extends string = '/'>(options: create.Options<
197
178
  base.use(
198
179
  '*',
199
180
  Log.middleware<Environment>({
200
- emit: (entry) => {
201
- log?.(entry)
181
+ emit: async (entry) => {
182
+ await log?.(entry)
202
183
  recordRequest?.(entry)
203
184
  // Return the analytics promise so `Log.middleware` extends the
204
185
  // response with `waitUntil`; otherwise the async sink's `queue.send`
@@ -208,61 +189,25 @@ export function create<const path extends string = '/'>(options: create.Options<
208
189
  },
209
190
  }),
210
191
  )
211
- // The chain ids this deployment serves: the built-in public chains plus the
212
- // configured `defaultChainId` and any explicit `supportedChainIds`. Exposed on
213
- // context so mounted apps with a path `chainId` (e.g. GeckoTerminal) can guard
214
- // it too.
215
- const supportedChainIds = new Set<number>([
216
- ...Object.keys(Viem.url).map(Number),
217
- ...Object.keys(Tidx.url).map(Number),
218
- defaultChainId,
219
- ...(options.supportedChainIds ?? []),
220
- ])
192
+
193
+ // Normalize the `db` option: the plain form is `{ source }` with no cached
194
+ // side; `cached` falls back to `source` so consumers never branch.
195
+ const db = 'source' in options.db ? options.db : { source: options.db }
196
+
197
+ // Built-in auth: the session surface (mounted last) plus the enforcement
198
+ // middleware (installed below), wired together in `Auth.install`.
199
+ const auth = Auth.install({ auth: options.auth, db: db.source, kv: options.kv })
221
200
 
222
201
  base.use('*', Timing.middleware())
223
202
  base.use('*', async (c, next) => {
224
- // Put app-level dependencies on Hono context so mounted handlers only
225
- // receive request-local state from `c`.
226
203
  c.set('basePath', basePath)
227
- c.set('chainId', defaultChainId)
228
- c.set('getAsset', createGetAsset({ assets, basePath, origin: new URL(c.req.url).origin }))
229
- c.set('getClient', (chainId) => getClient(chainId, Auth.getPrincipal(c)))
230
- c.set('getTidx', (chainId) => getTidx(chainId, Auth.getPrincipal(c)))
231
- c.set('rpc', rpc)
204
+ c.set('db', db.source)
205
+ c.set('dbCached', db.cached ?? db.source)
206
+ c.set('kv', options.kv)
232
207
  c.set('store', store)
233
- c.set('supportedChainIds', supportedChainIds)
234
- c.set('tidx', options.tidx)
235
- c.set('verifiedTokens', options.verifiedTokens)
236
- c.set('webhook', options.webhook)
237
208
  await next()
238
209
  })
239
210
 
240
- // Reject a syntactically valid `chainId` this deployment does not serve before
241
- // it reaches an upstream — an unknown chain otherwise hits the configured
242
- // RPC/indexer (a static `tidx.baseUrl` answers for any chain id) and comes
243
- // back as an opaque `upstream_error`.
244
- base.use('*', async (c, next) => {
245
- const raw = c.req.query('chainId') ?? c.req.query('chain_id')
246
- if (raw === undefined) return next()
247
- // Only reject a well-formed-but-unsupported chain here; malformed values
248
- // (e.g. `abc`, `0`) fall through to the route's own query validation.
249
- const parsed = Schema.ChainId.safeParse(raw)
250
- if (!parsed.success || supportedChainIds.has(parsed.data)) return next()
251
- return Response.unsupportedChainId(c, parsed.data, supportedChainIds)
252
- })
253
- // Skip the static asset route entirely when no store is configured; token
254
- // logo URIs simply go unresolved.
255
- if (assets)
256
- base.use('/assets/*', async (c, next) => {
257
- const key = stripAssetPath(c.req.path, basePath).replace(/^\/+/, '')
258
- const asset = key ? await assets.get(key) : undefined
259
- if (!asset) return next()
260
- return c.body(asset.body, 200, {
261
- 'Cache-Control': 'public, max-age=86400',
262
- 'Content-Type': asset.contentType,
263
- })
264
- })
265
-
266
211
  // Safety net: any non-2xx response (auth/validation errors, MPP `402`
267
212
  // challenges, rate-limit `429`s, upstream `502`s, etc.) must never be
268
213
  // cached. This wraps endpoint middleware and handlers registered below.
@@ -278,81 +223,79 @@ export function create<const path extends string = '/'>(options: create.Options<
278
223
  // latency.
279
224
  if (options.cache?.edge !== false) base.use('*', EdgeCache.middleware<Environment>({ store }))
280
225
 
281
- const base_routed = base.get('/health', OpenApi.describeRoute({ hide: true }), (c) =>
226
+ const app = base.get('/health', OpenApi.describeRoute({ hide: true }), (c) =>
282
227
  c.json({ status: 'ok' }, 200),
283
228
  )
284
229
 
285
- for (const middleware of options.middleware ?? []) base_routed.use('*', middleware)
286
-
287
- const app = base_routed
288
- .route('/v1/addresses', addresses())
289
- .route('/v1/blocks', blocks())
290
- .route('/v1/exchange', exchanges())
291
- .route('/v1/fee-amm', feeAmm())
292
- .route('/v1/indexer', indexer())
293
- .route('/v1/tokenlist', tokenlist())
294
- .route('/v1/tokens', tokens())
295
- .route('/v1/transactions', receipts())
296
- .route('/v1/transactions', transactions())
297
- .route('/v1/transactions', transactionActivities())
298
- .route('/v1/transfers', transfers())
299
- .route('/v1/verified-tokens', verifiedTokens())
300
- .route('/v1/webhooks', webhooks({ enabled: options.webhook !== undefined }))
301
- .route('/rpc', rpcApp())
302
- .route('/gecko', coingecko())
303
- // Hidden compatibility route for the prior Tempo API's
304
- // `GET /chains/:chainId/blocks/by-timestamp` (chain id in the path).
305
- .route('/chains', blocksByTimestampCompat())
306
-
307
- const spec = (async () => {
308
- // OAS 3.1 `webhooks`: document the signed envelope Tempo API POSTs to subscriber
309
- // URLs (an outbound payload, not an endpoint of this API). Only when enabled.
310
- const webhooksDoc = (() => {
311
- if (!options.webhook) return {}
312
- // `io: 'output'` documents the delivered (serialized) shape; `unrepresentable: 'any'`
313
- // tolerates the transaction payload's transforms (e.g. lazy fee-token metadata),
314
- // which have no JSON-Schema form, by emitting `{}` for those nodes rather than throwing.
315
- const envelope = z.toJSONSchema(webhookSchema.Envelope, {
316
- io: 'output',
317
- target: 'draft-2020-12',
318
- unrepresentable: 'any',
319
- })
320
- delete envelope.$schema
321
- const header = (name: string, description: string) => ({
322
- description,
323
- in: 'header' as const,
324
- name,
325
- required: true,
326
- schema: { type: 'string' as const },
327
- })
328
- return {
329
- webhooks: {
330
- event: {
331
- post: {
332
- description:
333
- 'When an event you subscribed to happens, Tempo sends this signed message to your webhook URL as an HTTP POST (the same shape is used by `POST /webhooks/:id/ping` so you can test your endpoint). Always verify the `tempo-signature` header before trusting the body, then reply with any 2xx status to confirm you received it. If your endpoint returns a non-2xx status, times out, or redirects, Tempo retries with increasing delays; an endpoint that keeps failing is eventually disabled.',
334
- parameters: [
335
- header(
336
- 'tempo-signature',
337
- 'Signature proving the message really came from Tempo. An HMAC-SHA256 of the body using your webhook secret, formatted `t=<unix>,v1=<hex>`. Verify this before trusting the payload.',
338
- ),
339
- header(
340
- 'tempo-event-id',
341
- 'A unique, stable id for this event (`evt_…`). The same event may be delivered more than once, so use this id to skip duplicates.',
342
- ),
343
- header('tempo-event-type', 'What happened: `token:transfer`, `transaction:included`, `log:emitted`, `block:created`, or `ping`.'), // prettier-ignore
344
- ],
345
- requestBody: {
346
- content: { 'application/json': { schema: envelope } },
347
- required: true,
348
- },
349
- responses: { '2xx': { description: 'Delivery acknowledged.' } },
350
- summary: 'Webhook event delivery',
351
- tags: ['Webhooks'],
352
- },
353
- },
354
- },
355
- }
230
+ // Built-in enforcement: default-closed policies over OpenAPI-registered
231
+ // routes. `auth: false` leaves policies unenforced (tests only).
232
+ if (auth.middleware) app.use('*', auth.middleware)
233
+
234
+ // Collect each mounted group's OpenAPI metadata: Hono's `.route()` drops
235
+ // sub-app metadata, so a group attaches it to its instance (a WeakMap) and we
236
+ // read it back here. Patching the runtime method, not the type, keeps `hc`.
237
+ const metadata: create.Metadata[] = []
238
+ const route = app.route.bind(app)
239
+ ;(app as unknown as { route: typeof route }).route = ((path, sub) => {
240
+ const entry = Metadata.read(sub)
241
+ if (entry) metadata.push(entry)
242
+ return route(path, sub)
243
+ }) as typeof route
244
+
245
+ // Register the JSON error envelopes eagerly (they don't depend on mounted
246
+ // routes) so error handling is live before the first request and a consumer's
247
+ // `withSentry(app)` sees them already in place.
248
+ app.notFound((c) =>
249
+ Response.error(c, { code: 'not_found', message: 'Route not found', status: 404 }),
250
+ )
251
+ app.onError((cause, c) => {
252
+ if (cause instanceof HTTPException) return errorFromException(c, cause)
253
+
254
+ // Redact any leaked API-key token from the logged error/stack.
255
+ console.error(
256
+ ApiKey.redact(cause instanceof Error ? (cause.stack ?? cause.message) : String(cause)),
257
+ )
258
+ return Response.error(c, {
259
+ code: 'internal_error',
260
+ message: 'Internal server error',
261
+ status: 500,
262
+ })
263
+ })
264
+
265
+ // Mount the OpenAPI/OpenRPC documents and docs reference lazily on the first
266
+ // request, so the document sees the full route surface without a `seal()`
267
+ // step. The patched `fetch` runs this before Hono builds its matcher.
268
+ //
269
+ // Spec generation stays lazy: ordinary API traffic only installs the routes,
270
+ // while `/openapi.json` and the Vocs handler start the memoized build.
271
+ let spec: ReturnType<typeof buildSpec> | undefined
272
+ const getSpec = () => (spec ??= buildSpec())
273
+ const buildSpec = async () => {
274
+ // Merge each mounted group's contributed OpenAPI metadata (tags, security
275
+ // schemes, webhooks block). Unused tags/schemes don't render, so a subset
276
+ // app's document carries only what its groups contributed.
277
+ // The built-in session surface's metadata appends after every mounted
278
+ // group's, so consumer-mounted groups lead the document order.
279
+ const metadata_all = (() => {
280
+ const entry = auth.app ? Metadata.read(auth.app) : undefined
281
+ return entry ? [...metadata, entry] : metadata
282
+ })()
283
+ const contributedSchemes = Object.assign({}, ...metadata_all.map((m) => m.securitySchemes ?? {})) // prettier-ignore
284
+ const tags = metadata_all.flatMap((m) => m.tags ?? [])
285
+ // Tag groups contributed under the same name merge into one sidebar group,
286
+ // deduped and filtered to defined tags (a group may list a tag another
287
+ // surface defines, e.g. `Auth` from the built-in session surface).
288
+ const tagGroups = (() => {
289
+ const defined = new Set(tags.map((tag) => tag.name))
290
+ const groups = new Map<string, { name: string; tags: string[] }>()
291
+ for (const { name, tags: members } of metadata_all.flatMap((m) => m['x-tagGroups'] ?? []))
292
+ groups.set(name, { name, tags: [...(groups.get(name)?.tags ?? []), ...members] })
293
+ return [...groups.values()]
294
+ .map((group) => ({
295
+ ...group,
296
+ tags: [...new Set(group.tags)].filter((tag) => defined.has(tag)),
297
+ })) // prettier-ignore
298
+ .filter((group) => group.tags.length > 0)
356
299
  })()
357
300
  const generated = await generateSpecs(app, {
358
301
  documentation: {
@@ -372,6 +315,7 @@ export function create<const path extends string = '/'>(options: create.Options<
372
315
  scheme: 'bearer',
373
316
  type: 'http',
374
317
  },
318
+ ...contributedSchemes,
375
319
  },
376
320
  },
377
321
  info: {
@@ -385,71 +329,24 @@ export function create<const path extends string = '/'>(options: create.Options<
385
329
  { url: basePath || '/', description: 'Relative to the host serving this document.' },
386
330
  ]),
387
331
  ],
388
- tags: [
389
- {
390
- name: 'Activities',
391
- description: 'A readable feed of what an account did onchain.',
392
- },
393
- {
394
- name: 'Balances',
395
- description: 'How much of each token an account holds.',
396
- },
397
- {
398
- name: 'Blocks',
399
- description: 'The ordered batches of transactions making up the chain.',
400
- },
401
- {
402
- name: 'Exchange',
403
- description: "Tempo's built-in stablecoin exchange: pairs, swaps, orders, prices.",
404
- },
405
- {
406
- name: 'Fee AMM',
407
- description: 'Pools that convert stablecoins to pay fees.',
408
- },
409
- {
410
- name: 'Tokens',
411
- description: 'TIP-20 token details, supply, and holders.',
412
- },
413
- {
414
- name: 'Transactions',
415
- description: 'Transactions submitted to Tempo, and their receipts.',
416
- },
417
- {
418
- name: 'Transfers',
419
- description: 'Token movements from executing transactions.',
420
- },
421
- {
422
- name: 'Verified Tokens',
423
- description: 'A curated, trusted list of TIP-20 tokens.',
424
- },
425
- ...(options.webhook
426
- ? [
427
- {
428
- name: 'Webhooks',
429
- description: 'Get signed callbacks when onchain events happen.',
430
- },
431
- ]
432
- : []),
433
- {
434
- name: 'Indexer',
435
- description: "Run read-only SQL queries against Tempo's indexed data.",
436
- },
437
- {
438
- name: 'RPC',
439
- description:
440
- 'Direct access to the chain over Ethereum JSON-RPC. Not part of the stable API contract; best-effort support only. No compatibility, latency, availability, or data-freshness guarantees. Breaking changes may happen with limited notice.',
441
- },
442
- {
443
- name: 'CoinGecko',
444
- description: "Exchange data in CoinGecko's GeckoTerminal format.",
445
- },
446
- ],
332
+ tags,
333
+ ...(tagGroups.length ? { 'x-tagGroups': tagGroups } : {}),
447
334
  },
448
335
  })
449
- // Set per-operation `security` and the `402` payment-challenge response from
450
- // each route's resolved auth policy (the single source of truth), so the
451
- // anonymous option and `402` appear only where the public/MPP lanes are
452
- // enabled not on every operation via the document default.
336
+ // Document the outbound `webhooks` block only when the webhook routes are
337
+ // actually mounted; a disabled feature hides its routes, so it must not
338
+ // advertise deliveries.
339
+ const hasWebhookRoutes = Object.keys(generated.paths).some((path) =>
340
+ /\/webhooks(\/|$)/.test(path),
341
+ )
342
+ const webhookBlocks = hasWebhookRoutes ? metadata_all.flatMap((m) => (m.webhooks ? [m.webhooks()] : [])) : [] // prettier-ignore
343
+ const webhooksDoc = webhookBlocks.length
344
+ ? { webhooks: Object.assign({}, ...webhookBlocks) }
345
+ : {}
346
+ // Set per-operation `security` and the `402` payment-challenge response
347
+ // from each route's resolved auth policy (the single source of truth), so
348
+ // the anonymous option and `402` appear only where the public/MPP lanes
349
+ // are enabled, not on every operation via the document default.
453
350
  applyAccess(generated.paths, Auth.describeAccess(app))
454
351
  // Point the `/rpc` operation at the OpenRPC document via `x-openrpc`.
455
352
  applyOpenrpcLink(generated.paths, basePath)
@@ -459,53 +356,34 @@ export function create<const path extends string = '/'>(options: create.Options<
459
356
  // tag and preserves document path order within a tag, so reordering here
460
357
  // drives the sidebar sub-item order.
461
358
  return { ...generated, ...webhooksDoc, paths: sortPaths(generated.paths) }
462
- })()
359
+ }
360
+ let finalized = false
361
+ const finalize = () => {
362
+ if (finalized) return
363
+ finalized = true
463
364
 
464
- app.get('/openapi.json', async (c) => c.json(await spec))
465
-
466
- // The OpenRPC document is a committed build artifact (no runtime generator);
467
- // see scripts/openrpc/build.ts and `pnpm openrpc:build`.
468
- app.get('/openrpc.json', (c) => c.json(openrpcSpec))
469
-
470
- if (options.docs !== false)
471
- app.route(
472
- '/',
473
- Handler.openApi(
474
- {
475
- // Inline the OpenRPC doc so vocs expands it without a self-subrequest
476
- // (see `inlineOpenrpc`); the public `/openapi.json` keeps the URL ref.
477
- spec: spec.then(inlineOpenrpc),
478
- sidebar: { collapsed: true },
479
- vocs: { logoUrl: OpenApi.logoUrl, title: 'Tempo API Reference', ...options.docs },
480
- },
481
- { fallback: 'next', css: OpenApi.css },
482
- ),
483
- )
365
+ app.get('/openapi.json', async (c) => c.json(await getSpec()))
484
366
 
485
- app.notFound((c) =>
486
- Response.error(c, { code: 'not_found', message: 'Route not found', status: 404 }),
487
- )
367
+ // The OpenRPC document is a committed build artifact (no runtime generator);
368
+ // see scripts/openrpc/build.ts and `pnpm openrpc:build`.
369
+ app.get('/openrpc.json', (c) => c.json(openrpcSpec))
488
370
 
489
- app.onError((cause, c) => {
490
- if (cause instanceof HTTPException) return errorFromException(c, cause)
371
+ // Mount each docs group's reference factory (see `docs()` in `tapimo/docs`)
372
+ // last, so its catch-all sits behind every API route and the documents
373
+ // above — regardless of where the group sat in the composition chain.
374
+ for (const entry of metadata) if (entry.docs) app.route('/', entry.docs({ spec: getSpec }))
375
+ }
491
376
 
492
- // Redact any leaked API-key token from the logged error/stack.
493
- console.error(
494
- ApiKey.redact(cause instanceof Error ? (cause.stack ?? cause.message) : String(cause)),
495
- )
496
- return Response.error(c, {
497
- code: 'internal_error',
498
- message: 'Internal server error',
499
- status: 500,
500
- })
501
- })
377
+ // Patch `fetch` to finalize once before dispatch. `request()` reads
378
+ // `this.fetch` dynamically so it picks up the patch, and the captured
379
+ // `originalFetch` still dispatches as a bare value (used by `App.listener`).
380
+ const originalFetch = app.fetch
381
+ ;(app as unknown as { fetch: typeof originalFetch }).fetch = ((request, ...rest) => {
382
+ finalize()
383
+ return originalFetch(request, ...rest)
384
+ }) as typeof originalFetch
502
385
 
503
- // Return the typed chained Hono directly. Spreading `store`/`listener` onto
504
- // the app would intersect the inferred Hono type and breaks `testClient`'s
505
- // schema inference for downstream consumers. The cache `store` is owned by
506
- // the caller (pass `options.cache.store`), and a Node listener is produced on
507
- // demand via `App.listener(app)`.
508
- return app
386
+ return route('/', auth.app ?? new Hono<Environment>())
509
387
  }
510
388
 
511
389
  /**
@@ -519,40 +397,43 @@ export function listener(app: { fetch: NodeFetch }): Listener {
519
397
  }
520
398
 
521
399
  export declare namespace create {
522
- /** Options for creating the Tempo API Hono app. */
400
+ /**
401
+ * Options for creating the Tempo API Hono app — the shared core:
402
+ * db/cache/kv/auth capabilities and cross-cutting middleware. Chain-data
403
+ * configuration belongs to {@link data}; email to {@link platform}.
404
+ */
523
405
  type Options<path extends string = '/'> = {
524
- /** Static API asset store for the current runtime (e.g. `Assets.cloudflareR2(...)`). Omit to skip static assets token logo URIs go unresolved. */
525
- assets?: Assets | undefined
406
+ /** Authentication: lane defaults, quotas, and credentials for the built-in enforcement middleware, plus the `session` sign-in surface. Omit for default-closed policies with in-memory rate limiting; pass false to leave policies unenforced (tests only). */
407
+ auth?: Auth.install.Options['auth'] | false | undefined
526
408
  /** Caching configuration: the cache store plus the edge response cache toggle. */
527
409
  cache?: Cache | undefined
528
410
  /** Response compression for API handlers. Disabled by default; pass true for defaults or a config object to customize. Leave disabled when fronted by a proxy/CDN that already compresses. */
529
411
  compress?: boolean | Compress | undefined
530
412
  /** CORS for API handlers. Enabled by default (wildcard origin, no credentials); pass a config object to customize, true for defaults, or false to disable (e.g. when CORS is handled by a fronting gateway/CDN). */
531
413
  cors?: boolean | Cors | undefined
532
- /** Default Tempo chain id for requests that omit `chainId`. */
533
- defaultChainId?: Viem.ChainId | undefined
534
- /** Vocs API reference options. Set to false to disable the `/` docs page. */
535
- docs?: Docs | false | undefined
414
+ /** 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`. */
415
+ db: Db.Source | { cached?: Db.Source | undefined; source: Db.Source }
416
+ /** KV state store backing API-key records (e.g. `Store.cloudflareKv(...)`). Omit to leave API-key auth closed every token resolves to `null`. */
417
+ kv?: Kv | undefined
536
418
  /** 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. */
537
419
  logger?: boolean | Log.Level | Logger | undefined
538
420
  /** Metrics backend. Disabled by default; pass a backend (e.g. `Metrics.cloudflare()`) to emit request (HTTP) metrics per response. Pass the same backend to the webhook poller/queue handlers so they emit over it too. */
539
421
  metrics?: Metrics.Metrics | undefined
540
- /** Hono middleware to run before API handlers mount. */
541
- middleware?: readonly MiddlewareHandler[] | undefined
542
422
  /** 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`. */
543
423
  path?: path | undefined
544
424
  /** 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' }]`. */
545
425
  servers?: readonly Server[] | undefined
546
- /** Extra Tempo chain ids this deployment serves, in addition to the built-in public chains and the configured `defaultChainId`. A request for a `chainId` outside this set is rejected with a clear 400 instead of reaching an upstream. */
547
- supportedChainIds?: readonly number[] | undefined
548
- /** Tempo RPC options. */
549
- rpc?: Rpc | undefined
550
- /** TIDX query client options. */
551
- tidx?: Tidx.getClient.Tidx | undefined
552
- /** Verified-token feature configuration. Omit to serve an empty verified list (nothing is verified). Provide a state `store` (Cloudflare KV or a Durable Object in production; never the cache, which a miss would silently drop) to make the curated list API-managed and durable. */
553
- verifiedTokens?: VerifiedTokens | undefined
554
- /** Webhook feature configuration. Omit to disable webhooks: the `/webhooks` routes stay mounted (for a stable schema and typed test client) but return `404` and are hidden from the OpenAPI document. Requires a state `store` (a Durable Object in production; never the cache, which a miss would silently drop). */
555
- webhook?: Webhook | undefined
426
+ }
427
+
428
+ /** KV state-store configuration. */
429
+ type Kv = {
430
+ /**
431
+ * State store backing API-key records. Must be a {@link Store.State}
432
+ * (e.g. `Store.cloudflareKv(...)` or `Store.memory()`); the Web Cache
433
+ * adapter (`Store.cache`) is rejected at the type level since a miss there
434
+ * would silently drop authoritative state.
435
+ */
436
+ store: Store.State
556
437
  }
557
438
 
558
439
  /** Caching configuration. */
@@ -563,41 +444,6 @@ export declare namespace create {
563
444
  store?: Store.Store | undefined
564
445
  }
565
446
 
566
- /** Verified-token feature configuration. */
567
- type VerifiedTokens = {
568
- /**
569
- * Per-isolate soft-refresh window in milliseconds. Within this window
570
- * verified-token lookups are served from the in-memory snapshot with no
571
- * store I/O; after it, the next lookup re-checks the store's `head` version
572
- * (single-flight) and recompiles only when it changed. Defaults to 10_000.
573
- */
574
- refreshMs?: number | undefined
575
- /**
576
- * State store backing the curated verified-token snapshots. Must be a
577
- * {@link Store.State} (e.g. `Store.cloudflareKv(...)`, `Store.durableObject(...)`,
578
- * or `Store.memory()`); the Web Cache adapter (`Store.cache`) is rejected at
579
- * the type level since a miss there would silently drop authoritative state.
580
- */
581
- store: Store.State
582
- }
583
-
584
- /** Webhook feature configuration. */
585
- type Webhook = {
586
- /**
587
- * Maximum active subscriptions a single owner may hold. Creation is rejected
588
- * with `403 limit_exceeded` once reached. Defaults to 100; bounds the
589
- * spam/SSRF surface and unbounded downstream delivery cost per owner.
590
- */
591
- maxPerOwner?: number | undefined
592
- /**
593
- * State store backing webhook subscriptions, cursors, and the delivery
594
- * log. Must be a {@link Store.State} (e.g. `Store.durableObject(...)` or
595
- * `Store.memory()`); the Web Cache adapter (`Store.cache`) is rejected at
596
- * the type level since a miss there would silently drop authoritative state.
597
- */
598
- store: Store.State
599
- }
600
-
601
447
  /** Binary asset store for static API assets (e.g. `Assets.cloudflareR2(...)`). */
602
448
  type Assets = Assets.Assets
603
449
 
@@ -607,8 +453,14 @@ export declare namespace create {
607
453
  /** CORS middleware options. */
608
454
  type Cors = NonNullable<Parameters<typeof hono_cors>[0]>
609
455
 
610
- /** Vocs API reference options (theme, top navigation, logo, socials, …). */
611
- type Docs = NonNullable<Parameters<typeof Handler.openApi>[0]['vocs']>
456
+ /** Reference-UI factory a docs group carries via {@link Metadata} (see `docs()` in `tapimo/docs`): given the app's OpenAPI document builder, returns an app that {@link create} mounts at the root on finalize. */
457
+ type Docs = (context: DocsContext) => Hono<any, any, any>
458
+
459
+ /** Context the app provides to a docs group's reference factory. */
460
+ type DocsContext = {
461
+ /** Memoized OpenAPI document builder — the same document served at `/openapi.json`. */
462
+ spec: () => Promise<{ paths: Record<string, unknown> }>
463
+ }
612
464
 
613
465
  /** Custom request-log sink, receiving one canonical entry per request. */
614
466
  type Logger = Log.Emit
@@ -622,10 +474,39 @@ export declare namespace create {
622
474
  }
623
475
 
624
476
  /**
625
- * Tempo RPC options: a static config, or a per-request resolver function that
626
- * routes by request principal to a separate (e.g. public, no-auth) RPC.
477
+ * What a route group contributes to the composed app beyond its routes:
478
+ * OpenAPI document metadata (tags, security schemes, outbound webhooks block)
479
+ * and an optional docs-UI factory. `create` collects these off each mounted
480
+ * group (via {@link Metadata}); unused entries don't render.
627
481
  */
628
- type Rpc = Viem.getClient.Rpc
482
+ type Metadata = {
483
+ /** Reference-UI factory mounted at the root on finalize (see `docs()` in `tapimo/docs`). */
484
+ docs?: Docs | undefined
485
+ /** Security scheme definitions this group's routes reference (e.g. the session cookie). */
486
+ securitySchemes?: Record<string, unknown> | undefined
487
+ /** Root-level OpenAPI tag definitions for this group's operations. */
488
+ tags?: readonly Tag[] | undefined
489
+ /** 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. */
490
+ webhooks?: (() => Record<string, unknown>) | undefined
491
+ /** Vocs tag-group metadata for grouping root tags in the reference. */
492
+ 'x-tagGroups'?: readonly TagGroup[] | undefined
493
+ }
494
+
495
+ /** One root-level OpenAPI tag definition. */
496
+ type Tag = {
497
+ /** Human-readable summary shown in the reference sidebar. */
498
+ description: string
499
+ /** Tag name referenced by operations. */
500
+ name: string
501
+ }
502
+
503
+ /** One Vocs OpenAPI tag group. */
504
+ type TagGroup = {
505
+ /** Section name shown by tag-group-aware references. */
506
+ name: string
507
+ /** Root tag names in this group. */
508
+ tags: readonly string[]
509
+ }
629
510
  }
630
511
 
631
512
  function getRequestId(c: Context<Environment>) {
@@ -703,10 +584,11 @@ function sortPaths(paths: PathsObject): PathsObject {
703
584
 
704
585
  /**
705
586
  * Sets per-operation `security` and the `402` payment-challenge response from
706
- * each route's resolved access lanes (see {@link Auth.describeAccess}). The
707
- * anonymous (`{}`) security option and the `402` response are emitted only where
708
- * the route's policy enables the public and MPP lanes respectively; required
709
- * API-key scopes are surfaced as the `x-required-scopes` extension.
587
+ * each route's resolved access lanes (see {@link Auth.describeAccess}).
588
+ * Security options compose from the enabled lanes API keys (header +
589
+ * bearer), sessions (cookie + bearer token), anonymous (`{}`) and the `402`
590
+ * response is emitted only where the MPP lane is enabled; required API-key
591
+ * scopes are surfaced as the `x-required-scopes` extension.
710
592
  */
711
593
  function applyAccess(paths: PathsObject, access: Record<string, Auth.OperationAccess>) {
712
594
  for (const item of Object.values(paths)) {
@@ -714,7 +596,12 @@ function applyAccess(paths: PathsObject, access: Record<string, Auth.OperationAc
714
596
  if (!isMutableOperation(op)) continue
715
597
  const lanes = access[op.operationId]
716
598
  if (!lanes) continue
717
- op.security = [{ apiKey: [] }, { bearerAuth: [] }, ...(lanes.public ? [{}] : [])]
599
+ const security = [
600
+ ...(lanes.apiKey ? [{ apiKey: [] }, { bearerAuth: [] }] : []),
601
+ ...(lanes.session ? [{ session: [] }, ...(lanes.apiKey ? [] : [{ bearerAuth: [] }])] : []),
602
+ ...(lanes.public ? [{}] : []),
603
+ ]
604
+ if (security.length > 0) op.security = security
718
605
  if (lanes.scopes.length > 0) op['x-required-scopes'] = [...lanes.scopes]
719
606
  if (lanes.mpp) op.responses = { ...op.responses, 402: OpenApi.paymentChallengeRef }
720
607
  else if (op.responses) delete op.responses['402']
@@ -729,7 +616,7 @@ function applyAccess(paths: PathsObject, access: Record<string, Auth.OperationAc
729
616
  * method/param/return schemas it accepts.
730
617
  *
731
618
  * The public `/openapi.json` keeps a URL reference (small, conventional). The
732
- * docs handler instead receives the document inlined — see `inlineOpenrpc`.
619
+ * docs handler instead inlines the document — see `inlineOpenrpc` in `tapimo/docs`.
733
620
  */
734
621
  function applyOpenrpcLink(paths: PathsObject, basePath: string) {
735
622
  // `basePath` is `/` at the root mount and `/api` etc. otherwise; strip any
@@ -740,31 +627,6 @@ function applyOpenrpcLink(paths: PathsObject, basePath: string) {
740
627
  if (isMutableOperation(op) && isRpcOperation(op)) op['x-openrpc'] = url
741
628
  }
742
629
 
743
- /**
744
- * Returns a shallow copy of `spec` with the JSON-RPC passthrough operation's
745
- * `x-openrpc` replaced by the inline OpenRPC document, for the docs handler.
746
- *
747
- * Vocs expands `x-openrpc` at request time. A URL reference (as kept in the
748
- * public `/openapi.json`) would make the docs worker fetch its own
749
- * `/openrpc.json` — a self-subrequest that is unreliable on Cloudflare Workers
750
- * and silently falls back to a single, unexpanded operation. Inlining the
751
- * committed artifact keeps expansion deterministic across local and deployed
752
- * environments, without bloating the public spec. Only the affected path/op are
753
- * cloned; the rest of the (large) document is shared by reference.
754
- */
755
- function inlineOpenrpc<spec extends { paths: Record<string, unknown> }>(spec: spec): spec {
756
- const paths: Record<string, unknown> = {}
757
- for (const [pathname, item] of Object.entries(spec.paths)) {
758
- let next = item
759
- if (isRecord(item))
760
- for (const [method, op] of Object.entries(item))
761
- if (isMutableOperation(op) && isRpcOperation(op))
762
- next = { ...item, [method]: { ...op, 'x-openrpc': openrpcSpec } }
763
- paths[pathname] = next
764
- }
765
- return { ...spec, paths }
766
- }
767
-
768
630
  type PathsObject = Awaited<ReturnType<typeof generateSpecs>>['paths']
769
631
  type PathItem = PathsObject[string]
770
632
  type Operation = { summary?: string | undefined }
@@ -796,45 +658,6 @@ function isRecord(value: unknown): value is Record<string, unknown> {
796
658
  return !!value && typeof value === 'object' && !Array.isArray(value)
797
659
  }
798
660
 
799
- function createGetAsset(options: createGetAsset.Options): GetAsset {
800
- const { assets, basePath, origin } = options
801
- // Without an asset store, every lookup misses so callers fall back to their
802
- // "no static data" behavior (undefined logo URIs).
803
- if (!assets) return () => undefined
804
- return async (chainId, path) => {
805
- // Public URLs include `/assets/:chainId`, but the store is keyed by
806
- // `:chainId/:path` (e.g. an R2 object key), decoupled from the URL shape.
807
- // `path` may be absolute (`Path.join` returns a leading slash); strip it so
808
- // the key is `:chainId/:path`, not `:chainId//:path`.
809
- const pathname = Path.join(basePath, 'assets', String(chainId), path)
810
- const key = `${chainId}/${path.replace(/^\/+/, '')}`
811
- const asset = await assets.get(key)
812
- if (!asset) return undefined
813
- // Return an absolute public URL (scheme + host) against the request origin
814
- // so logo URLs work for any consumer regardless of the API's domain.
815
- return {
816
- response: new globalThis.Response(asset.body, {
817
- headers: { 'Content-Type': asset.contentType },
818
- }),
819
- uri: new URL(pathname, origin).toString(),
820
- }
821
- }
822
- }
823
-
824
- declare namespace createGetAsset {
825
- type Options = {
826
- assets?: create.Assets | undefined
827
- basePath: string
828
- origin: string
829
- }
830
- }
831
-
832
- function stripAssetPath(path: string, basePath: string) {
833
- for (const prefix of [`${Path.join(basePath, 'assets')}/`, '/assets/'])
834
- if (path.startsWith(prefix)) return `/${path.slice(prefix.length)}`
835
- return path
836
- }
837
-
838
661
  type GetNodeRequestListener = (typeof import('@hono/node-server'))['getRequestListener']
839
662
  type NodeFetch = Parameters<GetNodeRequestListener>[0]
840
663
  type NodeRequestListener = ReturnType<GetNodeRequestListener>