tapimo 0.1.0 → 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 (385) hide show
  1. package/README.md +28 -15
  2. package/dist/ApiKey.d.ts +3 -1
  3. package/dist/ApiKey.d.ts.map +1 -1
  4. package/dist/ApiKey.js +1 -0
  5. package/dist/ApiKey.js.map +1 -1
  6. package/dist/ApiKeys.d.ts +71 -3
  7. package/dist/ApiKeys.d.ts.map +1 -1
  8. package/dist/ApiKeys.js +117 -1
  9. package/dist/ApiKeys.js.map +1 -1
  10. package/dist/App.d.ts +7139 -4197
  11. package/dist/App.d.ts.map +1 -1
  12. package/dist/App.js +133 -329
  13. package/dist/App.js.map +1 -1
  14. package/dist/Scope.d.ts +11 -4
  15. package/dist/Scope.d.ts.map +1 -1
  16. package/dist/Scope.js +12 -4
  17. package/dist/Scope.js.map +1 -1
  18. package/dist/Webhooks.d.ts +2 -2
  19. package/dist/Webhooks.d.ts.map +1 -1
  20. package/dist/Webhooks.js +2 -2
  21. package/dist/Webhooks.js.map +1 -1
  22. package/dist/admin/App.d.ts +126 -53
  23. package/dist/admin/App.d.ts.map +1 -1
  24. package/dist/admin/apps/api-keys.d.ts +104 -6
  25. package/dist/admin/apps/api-keys.d.ts.map +1 -1
  26. package/dist/admin/apps/api-keys.js +67 -0
  27. package/dist/admin/apps/api-keys.js.map +1 -1
  28. package/dist/admin/apps/scopes.d.ts +4 -1
  29. package/dist/admin/apps/scopes.d.ts.map +1 -1
  30. package/dist/admin/apps/verified-tokens.d.ts +115 -115
  31. package/dist/admin/apps/verified-tokens.js +1 -1
  32. package/dist/admin/apps/verified-tokens.js.map +1 -1
  33. package/dist/admin/ui.generated.d.ts +1 -1
  34. package/dist/admin/ui.generated.js +1 -1
  35. package/dist/apps/data/App.d.ts +19176 -0
  36. package/dist/apps/data/App.d.ts.map +1 -0
  37. package/dist/apps/data/App.js +236 -0
  38. package/dist/apps/data/App.js.map +1 -0
  39. package/dist/apps/data/index.d.ts +3 -0
  40. package/dist/apps/data/index.d.ts.map +1 -0
  41. package/dist/apps/data/index.js +3 -0
  42. package/dist/apps/data/index.js.map +1 -0
  43. package/dist/apps/{activities.d.ts → data/routes/activities.d.ts} +32 -32
  44. package/dist/apps/{activities.d.ts.map → data/routes/activities.d.ts.map} +1 -1
  45. package/dist/apps/{activities.js → data/routes/activities.js} +13 -13
  46. package/dist/apps/data/routes/activities.js.map +1 -0
  47. package/dist/apps/{addresses.d.ts → data/routes/addresses.d.ts} +22 -22
  48. package/dist/apps/{addresses.d.ts.map → data/routes/addresses.d.ts.map} +1 -1
  49. package/dist/apps/{addresses.js → data/routes/addresses.js} +5 -5
  50. package/dist/apps/data/routes/addresses.js.map +1 -0
  51. package/dist/apps/{balances.d.ts → data/routes/balances.d.ts} +49 -49
  52. package/dist/apps/data/routes/balances.d.ts.map +1 -0
  53. package/dist/apps/{balances.js → data/routes/balances.js} +42 -11
  54. package/dist/apps/data/routes/balances.js.map +1 -0
  55. package/dist/apps/{blocks.d.ts → data/routes/blocks.d.ts} +47 -47
  56. package/dist/apps/data/routes/blocks.d.ts.map +1 -0
  57. package/dist/apps/{blocks.js → data/routes/blocks.js} +13 -13
  58. package/dist/apps/data/routes/blocks.js.map +1 -0
  59. package/dist/apps/{coingecko.d.ts → data/routes/coingecko.d.ts} +183 -183
  60. package/dist/apps/data/routes/coingecko.d.ts.map +1 -0
  61. package/dist/apps/{coingecko.js → data/routes/coingecko.js} +13 -13
  62. package/dist/apps/data/routes/coingecko.js.map +1 -0
  63. package/dist/apps/{exchanges.d.ts → data/routes/exchanges.d.ts} +460 -460
  64. package/dist/apps/data/routes/exchanges.d.ts.map +1 -0
  65. package/dist/apps/{exchanges.js → data/routes/exchanges.js} +19 -19
  66. package/dist/apps/data/routes/exchanges.js.map +1 -0
  67. package/dist/apps/{fee-amm.d.ts → data/routes/fee-amm.d.ts} +94 -94
  68. package/dist/apps/data/routes/fee-amm.d.ts.map +1 -0
  69. package/dist/apps/{fee-amm.js → data/routes/fee-amm.js} +17 -17
  70. package/dist/apps/data/routes/fee-amm.js.map +1 -0
  71. package/dist/apps/{indexer.d.ts → data/routes/indexer.d.ts} +4 -4
  72. package/dist/apps/data/routes/indexer.d.ts.map +1 -0
  73. package/dist/apps/{indexer.js → data/routes/indexer.js} +13 -9
  74. package/dist/apps/data/routes/indexer.js.map +1 -0
  75. package/dist/apps/{receipts.d.ts → data/routes/receipts.d.ts} +49 -49
  76. package/dist/apps/data/routes/receipts.d.ts.map +1 -0
  77. package/dist/apps/{receipts.js → data/routes/receipts.js} +12 -12
  78. package/dist/apps/data/routes/receipts.js.map +1 -0
  79. package/dist/apps/{rpc.d.ts → data/routes/rpc.d.ts} +4 -4
  80. package/dist/apps/data/routes/rpc.d.ts.map +1 -0
  81. package/dist/apps/{rpc.js → data/routes/rpc.js} +6 -6
  82. package/dist/apps/data/routes/rpc.js.map +1 -0
  83. package/dist/apps/{tokenlist.d.ts → data/routes/tokenlist.d.ts} +4 -4
  84. package/dist/apps/data/routes/tokenlist.d.ts.map +1 -0
  85. package/dist/apps/{tokenlist.js → data/routes/tokenlist.js} +8 -8
  86. package/dist/apps/data/routes/tokenlist.js.map +1 -0
  87. package/dist/apps/{tokens.d.ts → data/routes/tokens.d.ts} +468 -468
  88. package/dist/apps/data/routes/tokens.d.ts.map +1 -0
  89. package/dist/apps/{tokens.js → data/routes/tokens.js} +19 -19
  90. package/dist/apps/data/routes/tokens.js.map +1 -0
  91. package/dist/apps/{transactions.d.ts → data/routes/transactions.d.ts} +304 -304
  92. package/dist/apps/data/routes/transactions.d.ts.map +1 -0
  93. package/dist/apps/{transactions.js → data/routes/transactions.js} +13 -13
  94. package/dist/apps/data/routes/transactions.js.map +1 -0
  95. package/dist/apps/{transfers.d.ts → data/routes/transfers.d.ts} +6 -6
  96. package/dist/apps/data/routes/transfers.d.ts.map +1 -0
  97. package/dist/apps/{transfers.js → data/routes/transfers.js} +13 -13
  98. package/dist/apps/data/routes/transfers.js.map +1 -0
  99. package/dist/apps/{verified-tokens.d.ts → data/routes/verified-tokens.d.ts} +42 -42
  100. package/dist/apps/data/routes/verified-tokens.d.ts.map +1 -0
  101. package/dist/apps/{verified-tokens.js → data/routes/verified-tokens.js} +9 -9
  102. package/dist/apps/data/routes/verified-tokens.js.map +1 -0
  103. package/dist/apps/{webhooks.d.ts → data/routes/webhooks.d.ts} +62 -62
  104. package/dist/apps/{webhooks.d.ts.map → data/routes/webhooks.d.ts.map} +1 -1
  105. package/dist/apps/{webhooks.js → data/routes/webhooks.js} +30 -26
  106. package/dist/apps/data/routes/webhooks.js.map +1 -0
  107. package/dist/apps/docs/App.d.ts +27 -0
  108. package/dist/apps/docs/App.d.ts.map +1 -0
  109. package/dist/apps/docs/App.js +122 -0
  110. package/dist/apps/docs/App.js.map +1 -0
  111. package/dist/{internal/OpenApi.fonts.d.ts → apps/docs/fonts.d.ts} +1 -1
  112. package/dist/apps/docs/fonts.d.ts.map +1 -0
  113. package/dist/{internal/OpenApi.fonts.js → apps/docs/fonts.js} +1 -1
  114. package/dist/apps/docs/fonts.js.map +1 -0
  115. package/dist/apps/docs/index.d.ts +2 -0
  116. package/dist/apps/docs/index.d.ts.map +1 -0
  117. package/dist/apps/docs/index.js +2 -0
  118. package/dist/apps/docs/index.js.map +1 -0
  119. package/dist/{internal/OpenApi.logo.d.ts → apps/docs/logo.d.ts} +1 -1
  120. package/dist/apps/docs/logo.d.ts.map +1 -0
  121. package/dist/{internal/OpenApi.logo.js → apps/docs/logo.js} +1 -1
  122. package/dist/apps/docs/logo.js.map +1 -0
  123. package/dist/apps/index.d.ts +4 -0
  124. package/dist/apps/index.d.ts.map +1 -0
  125. package/dist/apps/index.js +4 -0
  126. package/dist/apps/index.js.map +1 -0
  127. package/dist/apps/metadata.d.ts +7 -0
  128. package/dist/apps/metadata.d.ts.map +1 -0
  129. package/dist/apps/metadata.js +15 -0
  130. package/dist/apps/metadata.js.map +1 -0
  131. package/dist/apps/platform/App.d.ts +2985 -0
  132. package/dist/apps/platform/App.d.ts.map +1 -0
  133. package/dist/apps/platform/App.js +74 -0
  134. package/dist/apps/platform/App.js.map +1 -0
  135. package/dist/apps/platform/index.d.ts +2 -0
  136. package/dist/apps/platform/index.d.ts.map +1 -0
  137. package/dist/apps/platform/index.js +2 -0
  138. package/dist/apps/platform/index.js.map +1 -0
  139. package/dist/apps/platform/routes/api-keys.d.ts +681 -0
  140. package/dist/apps/platform/routes/api-keys.d.ts.map +1 -0
  141. package/dist/apps/platform/routes/api-keys.js +328 -0
  142. package/dist/apps/platform/routes/api-keys.js.map +1 -0
  143. package/dist/apps/platform/routes/invitations.d.ts +733 -0
  144. package/dist/apps/platform/routes/invitations.d.ts.map +1 -0
  145. package/dist/apps/platform/routes/invitations.js +413 -0
  146. package/dist/apps/platform/routes/invitations.js.map +1 -0
  147. package/dist/apps/platform/routes/me.d.ts +95 -0
  148. package/dist/apps/platform/routes/me.d.ts.map +1 -0
  149. package/dist/apps/platform/routes/me.js +81 -0
  150. package/dist/apps/platform/routes/me.js.map +1 -0
  151. package/dist/apps/platform/routes/members.d.ts +554 -0
  152. package/dist/apps/platform/routes/members.d.ts.map +1 -0
  153. package/dist/apps/platform/routes/members.js +251 -0
  154. package/dist/apps/platform/routes/members.js.map +1 -0
  155. package/dist/apps/platform/routes/orgs.d.ts +582 -0
  156. package/dist/apps/platform/routes/orgs.d.ts.map +1 -0
  157. package/dist/apps/platform/routes/orgs.js +265 -0
  158. package/dist/apps/platform/routes/orgs.js.map +1 -0
  159. package/dist/apps/platform/routes/projects.d.ts +740 -0
  160. package/dist/apps/platform/routes/projects.d.ts.map +1 -0
  161. package/dist/apps/platform/routes/projects.js +286 -0
  162. package/dist/apps/platform/routes/projects.js.map +1 -0
  163. package/dist/apps/relay/App.d.ts +41 -0
  164. package/dist/apps/relay/App.d.ts.map +1 -0
  165. package/dist/apps/relay/App.js +103 -0
  166. package/dist/apps/relay/App.js.map +1 -0
  167. package/dist/apps/relay/Sponsorships.d.ts +107 -0
  168. package/dist/apps/relay/Sponsorships.d.ts.map +1 -0
  169. package/dist/apps/relay/Sponsorships.js +193 -0
  170. package/dist/apps/relay/Sponsorships.js.map +1 -0
  171. package/dist/apps/relay/index.d.ts +3 -0
  172. package/dist/apps/relay/index.d.ts.map +1 -0
  173. package/dist/apps/relay/index.js +3 -0
  174. package/dist/apps/relay/index.js.map +1 -0
  175. package/dist/cli/Cli.d.ts +1 -1
  176. package/dist/cli/Cli.js +1 -1
  177. package/dist/cli/commands/admin.d.ts +1 -1
  178. package/dist/cli/commands/admin.js +2 -2
  179. package/dist/cli/commands/admin.js.map +1 -1
  180. package/dist/db/Db.d.ts +10 -0
  181. package/dist/db/Db.d.ts.map +1 -1
  182. package/dist/db/Db.js +128 -0
  183. package/dist/db/Db.js.map +1 -1
  184. package/dist/db/Schema.d.ts +80 -0
  185. package/dist/db/Schema.d.ts.map +1 -1
  186. package/dist/db/Schema.js +99 -0
  187. package/dist/db/Schema.js.map +1 -1
  188. package/dist/db/tables/invitations.d.ts +84 -0
  189. package/dist/db/tables/invitations.d.ts.map +1 -0
  190. package/dist/db/tables/invitations.js +165 -0
  191. package/dist/db/tables/invitations.js.map +1 -0
  192. package/dist/db/tables/memberships.d.ts +108 -0
  193. package/dist/db/tables/memberships.d.ts.map +1 -0
  194. package/dist/db/tables/memberships.js +153 -0
  195. package/dist/db/tables/memberships.js.map +1 -0
  196. package/dist/db/tables/organizations.d.ts +44 -0
  197. package/dist/db/tables/organizations.d.ts.map +1 -1
  198. package/dist/db/tables/organizations.js +89 -0
  199. package/dist/db/tables/organizations.js.map +1 -1
  200. package/dist/db/tables/projects.d.ts +65 -0
  201. package/dist/db/tables/projects.d.ts.map +1 -0
  202. package/dist/db/tables/projects.js +75 -0
  203. package/dist/db/tables/projects.js.map +1 -0
  204. package/dist/db/tables/sponsoredTransactions.d.ts +120 -0
  205. package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -0
  206. package/dist/db/tables/sponsoredTransactions.js +161 -0
  207. package/dist/db/tables/sponsoredTransactions.js.map +1 -0
  208. package/dist/db/tables/users.d.ts +48 -0
  209. package/dist/db/tables/users.d.ts.map +1 -0
  210. package/dist/db/tables/users.js +65 -0
  211. package/dist/db/tables/users.js.map +1 -0
  212. package/dist/handlers/internal/executionError.d.ts.map +1 -1
  213. package/dist/handlers/internal/executionError.js +2 -0
  214. package/dist/handlers/internal/executionError.js.map +1 -1
  215. package/dist/handlers/internal/multisig.d.ts +2 -6
  216. package/dist/handlers/internal/multisig.d.ts.map +1 -1
  217. package/dist/handlers/internal/multisig.js +11 -36
  218. package/dist/handlers/internal/multisig.js.map +1 -1
  219. package/dist/handlers/internal/sponsorship.d.ts +32 -2
  220. package/dist/handlers/internal/sponsorship.d.ts.map +1 -1
  221. package/dist/handlers/internal/sponsorship.js +34 -9
  222. package/dist/handlers/internal/sponsorship.js.map +1 -1
  223. package/dist/handlers/relay.d.ts +6 -2
  224. package/dist/handlers/relay.d.ts.map +1 -1
  225. package/dist/handlers/relay.js +16 -2
  226. package/dist/handlers/relay.js.map +1 -1
  227. package/dist/internal/Auth.d.ts +208 -6
  228. package/dist/internal/Auth.d.ts.map +1 -1
  229. package/dist/internal/Auth.js +500 -21
  230. package/dist/internal/Auth.js.map +1 -1
  231. package/dist/internal/Log.d.ts +7 -3
  232. package/dist/internal/Log.d.ts.map +1 -1
  233. package/dist/internal/Log.js +4 -0
  234. package/dist/internal/Log.js.map +1 -1
  235. package/dist/internal/OpenApi.d.ts +0 -19
  236. package/dist/internal/OpenApi.d.ts.map +1 -1
  237. package/dist/internal/OpenApi.js +0 -57
  238. package/dist/internal/OpenApi.js.map +1 -1
  239. package/dist/internal/Store.d.ts +7 -1
  240. package/dist/internal/Store.d.ts.map +1 -1
  241. package/dist/internal/Store.js +16 -2
  242. package/dist/internal/Store.js.map +1 -1
  243. package/dist/internal/Viem.d.ts +84 -0
  244. package/dist/internal/Viem.d.ts.map +1 -1
  245. package/dist/internal/Webhooks.d.ts +1 -1
  246. package/dist/internal/Webhooks.d.ts.map +1 -1
  247. package/package.json +31 -5
  248. package/src/ApiKey.ts +1 -0
  249. package/src/ApiKeys.test.ts +104 -1
  250. package/src/ApiKeys.ts +154 -1
  251. package/src/App.test.ts +11187 -179
  252. package/src/App.ts +200 -420
  253. package/src/Client.test-d.ts +306 -15
  254. package/src/Scope.ts +13 -4
  255. package/src/Webhooks.ts +2 -2
  256. package/src/admin/apps/api-keys.test.ts +51 -0
  257. package/src/admin/apps/api-keys.ts +84 -0
  258. package/src/admin/apps/scopes.test.ts +5 -1
  259. package/src/admin/apps/verified-tokens.ts +1 -1
  260. package/src/admin/ui.generated.ts +1 -1
  261. package/src/apps/composition.test.ts +66 -0
  262. package/src/apps/data/App.ts +295 -0
  263. package/src/apps/data/index.ts +2 -0
  264. package/src/apps/{activities.test.ts → data/routes/activities.test.ts} +3 -3
  265. package/src/apps/{activities.ts → data/routes/activities.ts} +15 -15
  266. package/src/apps/{addresses.ts → data/routes/addresses.ts} +6 -6
  267. package/src/apps/{balances.test.ts → data/routes/balances.test.ts} +2 -2
  268. package/src/apps/{balances.ts → data/routes/balances.ts} +46 -12
  269. package/src/apps/{blocks.test.ts → data/routes/blocks.test.ts} +3 -3
  270. package/src/apps/{blocks.ts → data/routes/blocks.ts} +14 -14
  271. package/src/apps/{coingecko.test.ts → data/routes/coingecko.test.ts} +2 -2
  272. package/src/apps/{coingecko.ts → data/routes/coingecko.ts} +14 -14
  273. package/src/apps/{exchanges.test.ts → data/routes/exchanges.test.ts} +1 -1
  274. package/src/apps/{exchanges.ts → data/routes/exchanges.ts} +20 -20
  275. package/src/apps/{fee-amm.test.ts → data/routes/fee-amm.test.ts} +3 -3
  276. package/src/apps/{fee-amm.ts → data/routes/fee-amm.ts} +18 -18
  277. package/src/apps/{indexer.test.ts → data/routes/indexer.test.ts} +26 -5
  278. package/src/apps/{indexer.ts → data/routes/indexer.ts} +13 -10
  279. package/src/apps/{receipts.test.ts → data/routes/receipts.test.ts} +3 -3
  280. package/src/apps/{receipts.ts → data/routes/receipts.ts} +13 -13
  281. package/src/apps/{rpc.test.ts → data/routes/rpc.test.ts} +44 -5
  282. package/src/apps/{rpc.ts → data/routes/rpc.ts} +7 -7
  283. package/src/apps/{tokenlist.test.ts → data/routes/tokenlist.test.ts} +3 -3
  284. package/src/apps/{tokenlist.ts → data/routes/tokenlist.ts} +9 -9
  285. package/src/apps/{tokens.test.ts → data/routes/tokens.test.ts} +2 -2
  286. package/src/apps/{tokens.ts → data/routes/tokens.ts} +20 -20
  287. package/src/apps/{transactions.test.ts → data/routes/transactions.test.ts} +3 -3
  288. package/src/apps/{transactions.ts → data/routes/transactions.ts} +15 -15
  289. package/src/apps/{transfers.test.ts → data/routes/transfers.test.ts} +2 -2
  290. package/src/apps/{transfers.ts → data/routes/transfers.ts} +15 -15
  291. package/src/apps/{verified-tokens.test.ts → data/routes/verified-tokens.test.ts} +3 -3
  292. package/src/apps/{verified-tokens.ts → data/routes/verified-tokens.ts} +10 -10
  293. package/src/apps/{webhooks.test.ts → data/routes/webhooks.test.ts} +6 -6
  294. package/src/apps/{webhooks.ts → data/routes/webhooks.ts} +39 -35
  295. package/src/apps/docs/App.ts +136 -0
  296. package/src/apps/docs/index.ts +1 -0
  297. package/src/apps/index.ts +3 -0
  298. package/src/apps/metadata.ts +23 -0
  299. package/src/apps/platform/App.ts +130 -0
  300. package/src/apps/platform/index.ts +1 -0
  301. package/src/apps/platform/routes/api-keys.test.ts +330 -0
  302. package/src/apps/platform/routes/api-keys.ts +427 -0
  303. package/src/apps/platform/routes/invitations.test.ts +361 -0
  304. package/src/apps/platform/routes/invitations.ts +519 -0
  305. package/src/apps/platform/routes/me.test.ts +121 -0
  306. package/src/apps/platform/routes/me.ts +106 -0
  307. package/src/apps/platform/routes/members.test.ts +195 -0
  308. package/src/apps/platform/routes/members.ts +313 -0
  309. package/src/apps/platform/routes/orgs.test.ts +313 -0
  310. package/src/apps/platform/routes/orgs.ts +343 -0
  311. package/src/apps/platform/routes/projects.test.ts +181 -0
  312. package/src/apps/platform/routes/projects.ts +367 -0
  313. package/src/apps/relay/App.test.ts +484 -0
  314. package/src/apps/relay/App.ts +128 -0
  315. package/src/apps/relay/Sponsorships.test.ts +375 -0
  316. package/src/apps/relay/Sponsorships.ts +305 -0
  317. package/src/apps/relay/index.ts +2 -0
  318. package/src/cli/Cli.ts +1 -1
  319. package/src/cli/commands/admin.ts +2 -2
  320. package/src/db/Db.ts +159 -0
  321. package/src/db/Schema.ts +137 -0
  322. package/src/db/tables/invitations.ts +198 -0
  323. package/src/db/tables/memberships.ts +208 -0
  324. package/src/db/tables/organizations.test.ts +37 -1
  325. package/src/db/tables/organizations.ts +105 -0
  326. package/src/db/tables/projects.ts +107 -0
  327. package/src/db/tables/sponsoredTransactions.test.ts +218 -0
  328. package/src/db/tables/sponsoredTransactions.ts +236 -0
  329. package/src/db/tables/users.ts +85 -0
  330. package/src/handlers/internal/executionError.ts +2 -0
  331. package/src/handlers/internal/multisig.test.ts +84 -10
  332. package/src/handlers/internal/multisig.ts +15 -50
  333. package/src/handlers/internal/sponsorship.ts +74 -15
  334. package/src/handlers/{internal/relay.localnet.test.ts → relay.test.ts} +2 -2
  335. package/src/handlers/relay.ts +23 -5
  336. package/src/internal/Auth.test.ts +316 -0
  337. package/src/internal/Auth.ts +691 -25
  338. package/src/internal/Cursor.test.ts +3 -2
  339. package/src/internal/Log.test.ts +1 -1
  340. package/src/internal/Log.ts +11 -3
  341. package/src/internal/MetricSink.ts +1 -1
  342. package/src/internal/OpenApi.ts +0 -59
  343. package/src/internal/Store.test.ts +52 -0
  344. package/src/internal/Store.ts +24 -5
  345. package/src/internal/Tidx.local.test.ts +1 -1
  346. package/src/internal/VerifiedTokens.test.ts +1 -1
  347. package/src/internal/WebhookDestination.test.ts +1 -1
  348. package/src/internal/Webhooks.test.ts +4 -4
  349. package/src/internal/Webhooks.ts +1 -1
  350. package/dist/apps/activities.js.map +0 -1
  351. package/dist/apps/addresses.js.map +0 -1
  352. package/dist/apps/balances.d.ts.map +0 -1
  353. package/dist/apps/balances.js.map +0 -1
  354. package/dist/apps/blocks.d.ts.map +0 -1
  355. package/dist/apps/blocks.js.map +0 -1
  356. package/dist/apps/coingecko.d.ts.map +0 -1
  357. package/dist/apps/coingecko.js.map +0 -1
  358. package/dist/apps/exchanges.d.ts.map +0 -1
  359. package/dist/apps/exchanges.js.map +0 -1
  360. package/dist/apps/fee-amm.d.ts.map +0 -1
  361. package/dist/apps/fee-amm.js.map +0 -1
  362. package/dist/apps/indexer.d.ts.map +0 -1
  363. package/dist/apps/indexer.js.map +0 -1
  364. package/dist/apps/receipts.d.ts.map +0 -1
  365. package/dist/apps/receipts.js.map +0 -1
  366. package/dist/apps/rpc.d.ts.map +0 -1
  367. package/dist/apps/rpc.js.map +0 -1
  368. package/dist/apps/tokenlist.d.ts.map +0 -1
  369. package/dist/apps/tokenlist.js.map +0 -1
  370. package/dist/apps/tokens.d.ts.map +0 -1
  371. package/dist/apps/tokens.js.map +0 -1
  372. package/dist/apps/transactions.d.ts.map +0 -1
  373. package/dist/apps/transactions.js.map +0 -1
  374. package/dist/apps/transfers.d.ts.map +0 -1
  375. package/dist/apps/transfers.js.map +0 -1
  376. package/dist/apps/verified-tokens.d.ts.map +0 -1
  377. package/dist/apps/verified-tokens.js.map +0 -1
  378. package/dist/apps/webhooks.js.map +0 -1
  379. package/dist/internal/OpenApi.fonts.d.ts.map +0 -1
  380. package/dist/internal/OpenApi.fonts.js.map +0 -1
  381. package/dist/internal/OpenApi.logo.d.ts.map +0 -1
  382. package/dist/internal/OpenApi.logo.js.map +0 -1
  383. package/src/handlers/internal/multisig.localnet.test.ts +0 -146
  384. /package/src/{internal/OpenApi.fonts.ts → apps/docs/fonts.ts} +0 -0
  385. /package/src/{internal/OpenApi.logo.ts → apps/docs/logo.ts} +0 -0
@@ -1,19 +1,21 @@
1
1
  import { Client } from 'tapimo'
2
2
  import type * as z from 'zod/mini'
3
3
 
4
- import * as Activities from './apps/activities.js'
5
- import * as Balances from './apps/balances.js'
6
- import * as Blocks from './apps/blocks.js'
7
- import * as Exchanges from './apps/exchanges.js'
8
- import * as FeeAmm from './apps/fee-amm.js'
9
- import * as Indexer from './apps/indexer.js'
10
- import * as Receipts from './apps/receipts.js'
11
- import * as Rpc from './apps/rpc.js'
12
- import * as Tokens from './apps/tokens.js'
13
- import * as Transactions from './apps/transactions.js'
14
- import * as Transfers from './apps/transfers.js'
15
- import * as VerifiedTokens from './apps/verified-tokens.js'
16
- import * as Webhooks from './apps/webhooks.js'
4
+ import * as Activities from './apps/data/routes/activities.js'
5
+ import * as Balances from './apps/data/routes/balances.js'
6
+ import * as Blocks from './apps/data/routes/blocks.js'
7
+ import * as Exchanges from './apps/data/routes/exchanges.js'
8
+ import * as FeeAmm from './apps/data/routes/fee-amm.js'
9
+ import * as Indexer from './apps/data/routes/indexer.js'
10
+ import * as Me from './apps/platform/routes/me.js'
11
+ import * as Orgs from './apps/platform/routes/orgs.js'
12
+ import * as Receipts from './apps/data/routes/receipts.js'
13
+ import * as Rpc from './apps/data/routes/rpc.js'
14
+ import * as Tokens from './apps/data/routes/tokens.js'
15
+ import * as Transactions from './apps/data/routes/transactions.js'
16
+ import * as Transfers from './apps/data/routes/transfers.js'
17
+ import * as VerifiedTokens from './apps/data/routes/verified-tokens.js'
18
+ import * as Webhooks from './apps/data/routes/webhooks.js'
17
19
 
18
20
  type Access400<code extends string> = code | 'api_key_malformed'
19
21
  type PaidAccessStatus = PublicAccessStatus | 402
@@ -97,6 +99,295 @@ describe('narrows responses by status', () => {
97
99
  }
98
100
  })
99
101
 
102
+ test('GET /v1/me', async () => {
103
+ const client = Client.create()
104
+ const res = await client.v1.me.$get()
105
+
106
+ expectTypeOf(res.status).toEqualTypeOf<200 | 400 | 401 | 403 | 404 | 502>()
107
+
108
+ switch (res.status) {
109
+ case 200: {
110
+ const json = await res.json()
111
+ expectTypeOf(json.id).toEqualTypeOf<z.output<typeof Me.schema.User>['id']>()
112
+ void json
113
+ return
114
+ }
115
+ case 403: {
116
+ const json = await res.json()
117
+ expectTypeOf(json.error.code).toEqualTypeOf<'forbidden'>()
118
+ void json
119
+ return
120
+ }
121
+ case 404: {
122
+ const json = await res.json()
123
+ expectTypeOf(json.error.code).toEqualTypeOf<'user_not_found'>()
124
+ void json
125
+ return
126
+ }
127
+ }
128
+ })
129
+
130
+ test('POST /v1/orgs', async () => {
131
+ const client = Client.create()
132
+ const res = await client.v1.orgs.$post({ json: { name: 'Acme' } })
133
+
134
+ expectTypeOf(res.status).toEqualTypeOf<200 | 400 | 401 | 403 | 502>()
135
+
136
+ switch (res.status) {
137
+ case 200: {
138
+ const json = await res.json()
139
+ expectTypeOf(json.id).toEqualTypeOf<z.output<typeof Orgs.schema.Organization>['id']>()
140
+ void json
141
+ return
142
+ }
143
+ case 403: {
144
+ const json = await res.json()
145
+ expectTypeOf(json.error.code).toEqualTypeOf<'forbidden'>()
146
+ void json
147
+ return
148
+ }
149
+ }
150
+ })
151
+
152
+ test('GET /v1/orgs/:orgId', async () => {
153
+ const client = Client.create()
154
+ const res = await client.v1.orgs[':orgId{org_[A-Za-z0-9_-]+}'].$get({
155
+ param: { orgId: 'org_test' },
156
+ })
157
+
158
+ expectTypeOf(res.status).toEqualTypeOf<200 | 400 | 401 | 403 | 404>()
159
+
160
+ switch (res.status) {
161
+ case 200: {
162
+ const json = await res.json()
163
+ expectTypeOf(json.name).toEqualTypeOf<string>()
164
+ void json
165
+ return
166
+ }
167
+ case 404: {
168
+ const json = await res.json()
169
+ expectTypeOf(json.error.code).toEqualTypeOf<'organization_not_found'>()
170
+ void json
171
+ return
172
+ }
173
+ }
174
+ })
175
+
176
+ test('DELETE /v1/orgs/:orgId', async () => {
177
+ const client = Client.create()
178
+ const res = await client.v1.orgs[':orgId{org_[A-Za-z0-9_-]+}'].$delete({
179
+ param: { orgId: 'org_test' },
180
+ })
181
+
182
+ expectTypeOf(res.status).toEqualTypeOf<200 | 400 | 401 | 403 | 404 | 502>()
183
+
184
+ switch (res.status) {
185
+ case 200: {
186
+ const json = await res.json()
187
+ expectTypeOf(json.id).toEqualTypeOf<string>()
188
+ void json
189
+ return
190
+ }
191
+ case 404: {
192
+ const json = await res.json()
193
+ expectTypeOf(json.error.code).toEqualTypeOf<'organization_not_found'>()
194
+ void json
195
+ return
196
+ }
197
+ }
198
+ })
199
+
200
+ test('GET /v1/orgs/:orgId/projects/:projectId', async () => {
201
+ const client = Client.create()
202
+ const res = await client.v1.orgs[':orgId{org_[A-Za-z0-9_-]+}'].projects[
203
+ ':projectId{prj_[A-Za-z0-9_-]+}'
204
+ ].$get({ param: { orgId: 'org_test', projectId: 'prj_test' } })
205
+
206
+ expectTypeOf(res.status).toEqualTypeOf<200 | 400 | 401 | 403 | 404>()
207
+
208
+ switch (res.status) {
209
+ case 200: {
210
+ const json = await res.json()
211
+ expectTypeOf(json.environments[0]).toEqualTypeOf<'production' | 'sandbox' | undefined>()
212
+ void json
213
+ return
214
+ }
215
+ case 404: {
216
+ const json = await res.json()
217
+ expectTypeOf(json.error.code).toEqualTypeOf<
218
+ 'organization_not_found' | 'project_not_found'
219
+ >()
220
+ void json
221
+ return
222
+ }
223
+ }
224
+ })
225
+
226
+ test('DELETE /v1/orgs/:orgId/projects/:projectId', async () => {
227
+ const client = Client.create()
228
+ const res = await client.v1.orgs[':orgId{org_[A-Za-z0-9_-]+}'].projects[
229
+ ':projectId{prj_[A-Za-z0-9_-]+}'
230
+ ].$delete({ param: { orgId: 'org_test', projectId: 'prj_test' } })
231
+
232
+ expectTypeOf(res.status).toEqualTypeOf<200 | 400 | 401 | 403 | 404 | 502>()
233
+
234
+ switch (res.status) {
235
+ case 200: {
236
+ const json = await res.json()
237
+ expectTypeOf(json.id).toEqualTypeOf<string>()
238
+ void json
239
+ return
240
+ }
241
+ case 404: {
242
+ const json = await res.json()
243
+ expectTypeOf(json.error.code).toEqualTypeOf<
244
+ 'organization_not_found' | 'project_not_found'
245
+ >()
246
+ void json
247
+ return
248
+ }
249
+ }
250
+ })
251
+
252
+ test('POST /v1/orgs/:orgId/projects/:projectId/api-keys', async () => {
253
+ const client = Client.create()
254
+ const res = await client.v1.orgs[':orgId{org_[A-Za-z0-9_-]+}'].projects[
255
+ ':projectId{prj_[A-Za-z0-9_-]+}'
256
+ ]['api-keys'].$post({
257
+ json: { scopes: ['data:read'] },
258
+ param: { orgId: 'org_test', projectId: 'prj_test' },
259
+ })
260
+
261
+ expectTypeOf(res.status).toEqualTypeOf<200 | 400 | 401 | 403 | 404 | 502>()
262
+
263
+ switch (res.status) {
264
+ case 200: {
265
+ const json = await res.json()
266
+ expectTypeOf(json.token).toEqualTypeOf<string>()
267
+ expectTypeOf(json.id).toEqualTypeOf<string>()
268
+ void json
269
+ return
270
+ }
271
+ }
272
+ })
273
+
274
+ test('DELETE /v1/orgs/:orgId/projects/:projectId/api-keys/:keyId', async () => {
275
+ const client = Client.create()
276
+ const res = await client.v1.orgs[':orgId{org_[A-Za-z0-9_-]+}'].projects[
277
+ ':projectId{prj_[A-Za-z0-9_-]+}'
278
+ ]['api-keys'][':keyId{key_[A-Za-z0-9_-]+}'].$delete({
279
+ param: { keyId: 'key_test', orgId: 'org_test', projectId: 'prj_test' },
280
+ })
281
+
282
+ expectTypeOf(res.status).toEqualTypeOf<200 | 400 | 401 | 403 | 404 | 502>()
283
+
284
+ switch (res.status) {
285
+ case 404: {
286
+ const json = await res.json()
287
+ expectTypeOf(json.error.code).toEqualTypeOf<
288
+ | 'api_key_not_found'
289
+ | 'api_keys_not_enabled'
290
+ | 'organization_not_found'
291
+ | 'project_not_found'
292
+ >()
293
+ void json
294
+ return
295
+ }
296
+ }
297
+ })
298
+
299
+ test('GET /v1/orgs/:orgId/members', async () => {
300
+ const client = Client.create()
301
+ const res = await client.v1.orgs[':orgId{org_[A-Za-z0-9_-]+}'].members.$get({
302
+ param: { orgId: 'org_test' },
303
+ })
304
+
305
+ expectTypeOf(res.status).toEqualTypeOf<200 | 400 | 401 | 403 | 404 | 502>()
306
+
307
+ switch (res.status) {
308
+ case 200: {
309
+ const json = await res.json()
310
+ expectTypeOf(json.data[0]!.role).toEqualTypeOf<'admin' | 'member' | 'owner'>()
311
+ void json
312
+ return
313
+ }
314
+ }
315
+ })
316
+
317
+ test('PATCH /v1/orgs/:orgId/members/:userId', async () => {
318
+ const client = Client.create()
319
+ const res = await client.v1.orgs[':orgId{org_[A-Za-z0-9_-]+}'].members[
320
+ ':userId{usr_[A-Za-z0-9_-]+}'
321
+ ].$patch({ json: { role: 'admin' }, param: { orgId: 'org_test', userId: 'usr_test' } })
322
+
323
+ expectTypeOf(res.status).toEqualTypeOf<200 | 400 | 401 | 403 | 404 | 409 | 502>()
324
+
325
+ switch (res.status) {
326
+ case 409: {
327
+ const json = await res.json()
328
+ expectTypeOf(json.error.code).toEqualTypeOf<'last_owner'>()
329
+ void json
330
+ return
331
+ }
332
+ }
333
+ })
334
+
335
+ test('POST /v1/invitations/:invitationId/accept', async () => {
336
+ const client = Client.create()
337
+ const res = await client.v1.invitations[':invitationId{inv_[A-Za-z0-9_-]+}'].accept.$post({
338
+ param: { invitationId: 'inv_test' },
339
+ })
340
+
341
+ expectTypeOf(res.status).toEqualTypeOf<200 | 400 | 401 | 403 | 404 | 502>()
342
+
343
+ switch (res.status) {
344
+ case 200: {
345
+ const json = await res.json()
346
+ expectTypeOf(json.role).toEqualTypeOf<'admin' | 'member' | 'owner'>()
347
+ void json
348
+ return
349
+ }
350
+ case 404: {
351
+ const json = await res.json()
352
+ expectTypeOf(json.error.code).toEqualTypeOf<'invitation_not_found'>()
353
+ void json
354
+ return
355
+ }
356
+ }
357
+ })
358
+
359
+ test('GET /v1/orgs', async () => {
360
+ const client = Client.create()
361
+ const res = await client.v1.orgs.$get()
362
+
363
+ expectTypeOf(res.status).toEqualTypeOf<200 | 400 | 401 | 403 | 502>()
364
+
365
+ switch (res.status) {
366
+ case 200: {
367
+ const json = await res.json()
368
+ expectTypeOf(json.data[0]!.id).toEqualTypeOf<
369
+ z.output<typeof Orgs.schema.listOrganizations.Response>['data'][number]['id']
370
+ >()
371
+ void json
372
+ return
373
+ }
374
+ case 403: {
375
+ const json = await res.json()
376
+ expectTypeOf(json.error.code).toEqualTypeOf<'forbidden'>()
377
+ expectTypeOf(json.error.message).toEqualTypeOf<string>()
378
+ void json
379
+ return
380
+ }
381
+ case 502: {
382
+ const json = await res.json()
383
+ expectTypeOf(json.error.code).toEqualTypeOf<'upstream_error'>()
384
+ expectTypeOf(json.error.message).toEqualTypeOf<string>()
385
+ void json
386
+ return
387
+ }
388
+ }
389
+ })
390
+
100
391
  test('GET /v1/blocks/:block', async () => {
101
392
  const client = Client.create()
102
393
  const res = await client.v1.blocks[':block'].$get({ param: { block: '1' } })
@@ -1176,7 +1467,7 @@ describe('narrows responses by status', () => {
1176
1467
 
1177
1468
  test('POST /rpc/:chain', async () => {
1178
1469
  const client = Client.create()
1179
- const res = await client.rpc[':chain{mainnet|testnet|[0-9]+}?'].$post({
1470
+ const res = await client.rpc[':chain{(mainnet|testnet|[0-9]+)}?'].$post({
1180
1471
  param: { chain: undefined },
1181
1472
  })
1182
1473
 
@@ -1208,7 +1499,7 @@ describe('narrows responses by status', () => {
1208
1499
 
1209
1500
  test('POST /rpc/:chain testnet shortcut', async () => {
1210
1501
  const client = Client.create()
1211
- const res = await client.rpc[':chain{mainnet|testnet|[0-9]+}?'].$post({
1502
+ const res = await client.rpc[':chain{(mainnet|testnet|[0-9]+)}?'].$post({
1212
1503
  param: { chain: 'testnet' },
1213
1504
  })
1214
1505
 
package/src/Scope.ts CHANGED
@@ -1,9 +1,8 @@
1
1
  /**
2
2
  * The issuable API-key scope vocabulary: the single source of truth for the
3
3
  * `/scopes` catalog, for key minting (`ApiKeys.mint`), and for the scopes
4
- * required by route policies (`Auth.policy`). Exposing it publicly lets operator
5
- * tooling (e.g. the provisioning CLI) validate scopes against the same list the
6
- * server enforces.
4
+ * required by route policies (`Auth.policy`). Exposing it publicly lets super
5
+ * admin tooling validate scopes against the same list the server enforces.
7
6
  */
8
7
 
9
8
  /**
@@ -38,7 +37,11 @@ export const catalog = [
38
37
  },
39
38
  {
40
39
  description: 'Fill and submit transactions through the relay.',
41
- scope: 'rpc-relay',
40
+ scope: 'rpc-relay:read',
41
+ },
42
+ {
43
+ description: 'Sponsor transaction fees through the relay as the managed fee payer.',
44
+ scope: 'rpc-relay:sponsor',
42
45
  },
43
46
  {
44
47
  description: 'Read webhook subscriptions.',
@@ -52,3 +55,9 @@ export const catalog = [
52
55
 
53
56
  /** Issuable API-key scope identifier — the union of every {@link catalog} entry. */
54
57
  export type Id = (typeof catalog)[number]['scope']
58
+
59
+ /**
60
+ * Scopes a session may self-mint through the developer platform. Empty: every
61
+ * scope requires super admin issuance.
62
+ */
63
+ export const selfServe: readonly Id[] = []
package/src/Webhooks.ts CHANGED
@@ -1,4 +1,4 @@
1
- import * as AppWebhooks from './apps/webhooks.js'
1
+ import * as AppWebhooks from './apps/data/routes/webhooks.js'
2
2
  import * as Db from './db/Db.js'
3
3
  import * as MetricSink from './internal/MetricSink.js'
4
4
  import * as Store from './internal/Store.js'
@@ -16,7 +16,7 @@ export * from './internal/Webhooks.js'
16
16
  // type delivery bodies: `Webhooks.schema.Envelope` (a discriminated union on
17
17
  // `type`), plus the per-event `TransferEvent` / `TransactionEvent` / `PingEvent`
18
18
  // payloads. Infer a type with `z.infer<typeof Webhooks.schema.Envelope>`.
19
- export { eventTypes, schema } from './apps/webhooks.js'
19
+ export { eventTypes, schema } from './apps/data/routes/webhooks.js'
20
20
 
21
21
  /**
22
22
  * Builds a scheduled webhook poller bound to a state store and chain config.
@@ -155,3 +155,54 @@ describe('DELETE /api-keys/:id', () => {
155
155
  expect(body.error?.code).toMatchInlineSnapshot(`"not_found"`)
156
156
  })
157
157
  })
158
+
159
+ describe('PATCH /api-keys/:id', () => {
160
+ test('re-attributes a key to an org and project', async () => {
161
+ const { client, store } = TestAdmin.setup()
162
+ const { record, token } = await core_ApiKeys.mint(store, validBody)
163
+
164
+ const response = await client['api-keys'][':id'].$patch({
165
+ json: { orgId: 'org_real', projectId: 'prj_real' },
166
+ param: { id: record.id },
167
+ })
168
+ expect(response.status).toBe(200)
169
+ const body = await TestApp.json(response, core_ApiKeys.schema.Record)
170
+ expect(body.orgId).toBe('org_real')
171
+ expect(body.projectId).toBe('prj_real')
172
+
173
+ // The original token keeps resolving, now carrying the attribution.
174
+ const resolved = await core_ApiKeys.resolve(store, token)
175
+ expect(resolved?.projectId).toBe('prj_real')
176
+ })
177
+
178
+ test('unknown id → 404', async () => {
179
+ const { client } = TestAdmin.setup()
180
+ const response = await client['api-keys'][':id'].$patch({
181
+ json: { projectId: 'prj_x' },
182
+ param: { id: 'key_missing' },
183
+ })
184
+ expect(response.status).toMatchInlineSnapshot(`404`)
185
+ })
186
+ })
187
+
188
+ describe('POST /api-keys/backfill', () => {
189
+ test('rebuilds index entries for legacy records', async () => {
190
+ const { client, store } = TestAdmin.setup()
191
+ const { record } = await core_ApiKeys.mint(store, validBody)
192
+
193
+ // Legacy state: records exist, index entries do not.
194
+ for (const prefix of ['apikey_id:', 'apikey_org:'])
195
+ for (const { name } of (await store.list({ prefix })).keys) await store.delete(name)
196
+ expect(await core_ApiKeys.listByOrg(store, 'org_test')).toEqual([])
197
+
198
+ const response = await client['api-keys'].backfill.$post()
199
+ expect(response.status).toBe(200)
200
+ expect(await TestApp.json(response, ApiKeys.schema.backfillApiKeys.Response)).toEqual({
201
+ indexed: 1,
202
+ scanned: 1,
203
+ })
204
+ expect((await core_ApiKeys.listByOrg(store, 'org_test')).map(({ id }) => id)).toEqual([
205
+ record.id,
206
+ ])
207
+ })
208
+ })
@@ -53,6 +53,37 @@ export namespace schema {
53
53
  )
54
54
  }
55
55
 
56
+ /** Schemas for the attributeApiKey operation. */
57
+ export namespace attributeApiKey {
58
+ /** Request body re-attributing a key; omitted fields keep their value. */
59
+ export const Body = Schema.describe(
60
+ z.object({
61
+ orgId: z.optional(z.string()).check(z.describe('New owning organization id (`org_…`).')),
62
+ projectId: z.optional(z.string()).check(z.describe('New owning project id (`prj_…`).')),
63
+ }),
64
+ 'Attribution to apply to an API key.',
65
+ )
66
+
67
+ /** Path parameters for attributing a key. */
68
+ export const Params = z
69
+ .object({
70
+ id: z.string().check(z.describe('Stable id of the key to attribute.')),
71
+ })
72
+ .check(z.describe('Path parameters for attributing an API key.'))
73
+ }
74
+
75
+ /** Schemas for the backfillApiKeys operation. */
76
+ export namespace backfillApiKeys {
77
+ /** Result counts from the index rebuild. */
78
+ export const Response = Schema.describe(
79
+ z.object({
80
+ indexed: z.number().check(z.describe('Records whose index entries were written.')),
81
+ scanned: z.number().check(z.describe('Stored records scanned.')),
82
+ }),
83
+ 'Result of rebuilding the API-key indexes.',
84
+ )
85
+ }
86
+
56
87
  /** Schemas for the revokeApiKey operation. */
57
88
  export namespace revokeApiKey {
58
89
  /** Path parameters for revoking a key. */
@@ -76,9 +107,30 @@ export namespace schema {
76
107
  * returning the one-time token.
77
108
  * - `GET /` lists key records (metadata only), optionally filtered by `orgId`.
78
109
  * - `DELETE /:id` revokes a key by id (`404` if absent).
110
+ * - `PATCH /:id` re-attributes a key to an org/project (the legacy backfill path).
111
+ * - `POST /backfill` rebuilds the id and org index entries for every record.
79
112
  */
80
113
  export function apiKeys() {
81
114
  return new Hono<App.Environment>()
115
+ .post(
116
+ '/backfill',
117
+ OpenApi.describeRoute({
118
+ description: 'Rebuild the id and org index entries for every stored key record. Records are never modified, so existing tokens keep resolving.', // prettier-ignore
119
+ operationId: 'backfillApiKeys',
120
+ responses: OpenApi.responses({
121
+ success: {
122
+ description: 'Scanned and indexed record counts.',
123
+ schema: schema.backfillApiKeys.Response,
124
+ },
125
+ }),
126
+ summary: 'Backfill key indexes',
127
+ tags: ['API keys'],
128
+ }),
129
+ async (c) => {
130
+ const result = await ApiKeys.backfill(c.get('kv').store)
131
+ return c.json(Response.validated(schema.backfillApiKeys.Response, result))
132
+ },
133
+ )
82
134
  .post(
83
135
  '/',
84
136
  OpenApi.validate('json', schema.createApiKey.Body, {
@@ -153,4 +205,36 @@ export function apiKeys() {
153
205
  return c.json(Response.validated(schema.revokeApiKey.Response, { id }))
154
206
  },
155
207
  )
208
+ .patch(
209
+ '/:id',
210
+ OpenApi.validate('param', schema.attributeApiKey.Params, {
211
+ code: 'param_invalid',
212
+ message: 'Invalid path parameters',
213
+ }),
214
+ OpenApi.validate('json', schema.attributeApiKey.Body, {
215
+ code: 'body_invalid',
216
+ message: 'Invalid request body',
217
+ }),
218
+ OpenApi.describeRoute({
219
+ description: 'Re-attribute a key to an organization and/or project. The token is unchanged.', // prettier-ignore
220
+ operationId: 'attributeApiKey',
221
+ responses: OpenApi.responses({
222
+ errors: { 404: 'API key not found.' },
223
+ success: { description: 'The updated key record.', schema: ApiKeys.schema.Record },
224
+ }),
225
+ summary: 'Attribute API key',
226
+ tags: ['API keys'],
227
+ }),
228
+ async (c) => {
229
+ const { id } = c.req.valid('param')
230
+ const updated = await ApiKeys.attribute(c.get('kv').store, id, c.req.valid('json'))
231
+ if (!updated)
232
+ return Response.error(c, {
233
+ code: 'not_found',
234
+ message: 'API key not found.',
235
+ status: 404,
236
+ })
237
+ return c.json(Response.validated(ApiKeys.schema.Record, updated))
238
+ },
239
+ )
156
240
  }
@@ -24,7 +24,11 @@ describe('GET /scopes', () => {
24
24
  },
25
25
  {
26
26
  "description": "Fill and submit transactions through the relay.",
27
- "scope": "rpc-relay",
27
+ "scope": "rpc-relay:read",
28
+ },
29
+ {
30
+ "description": "Sponsor transaction fees through the relay as the managed fee payer.",
31
+ "scope": "rpc-relay:sponsor",
28
32
  },
29
33
  {
30
34
  "description": "Read webhook subscriptions.",
@@ -6,7 +6,7 @@ import * as Response from '../../internal/Response.js'
6
6
  import * as Schema from '../../internal/Schema.js'
7
7
  import * as Db from '../../db/Db.js'
8
8
  import * as VerifiedTokens from '../../internal/VerifiedTokens.js'
9
- import { schema as readSchema } from '../../apps/verified-tokens.js'
9
+ import { schema as readSchema } from '../../apps/data/routes/verified-tokens.js'
10
10
  import type * as App from '../App.js'
11
11
 
12
12
  /** Required `chainId` query: admin mutations always address a chain explicitly. */
@@ -11,7 +11,7 @@ export type Asset = {
11
11
 
12
12
  /** The prerendered SPA shell HTML (assets normalized to relative URLs). */
13
13
  export const shell =
14
- '<!DOCTYPE html><html lang="en" data-theme="dark" data-regen-radius="none"><head><meta charSet="utf-8"/><meta content="width=device-width, initial-scale=1" name="viewport"/><link rel="stylesheet" href="./assets/styles-Cd7Fp5b_.css" data-precedence="default"/><title>Tempo API Admin</title><link rel="modulepreload" href="./assets/index-CPTihwDe.js"/><link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20269%20269%22%3E%3Crect%20width%3D%22269%22%20height%3D%22269%22%20fill%3D%22%23000%22%2F%3E%3Cpath%20d%3D%22M123.273%20190.794H93.445L121.09%20105.318H85.7334L93.445%2080.2642H191.95L184.238%20105.318H150.773L123.273%20190.794Z%22%20fill%3D%22%23fff%22%2F%3E%3C%2Fsvg%3E"/></head><body><div id="root"></div><script class="$tsr" id="$tsr-stream-barrier">(self.$R=self.$R||{})["tsr"]=[];self.$_TSR={h(){this.hydrated=!0,this.c()},e(){this.streamEnded=!0,this.c()},c(){this.hydrated&&this.streamEnded&&(delete self.$_TSR,delete self.$R.tsr)},p(e){this.initialized?e():this.buffer.push(e)},buffer:[]};$_TSR.router=($R=>$R[0]={manifest:$R[1]={routes:$R[2]={__root__:$R[3]={preloads:$R[4]=["./assets/index-CPTihwDe.js"],scripts:$R[5]=[$R[6]={attrs:$R[7]={type:"module",async:!0,src:"./assets/index-CPTihwDe.js"}}]}}},matches:$R[8]=[$R[9]={i:"__root__\u0000",u:1783028662119,s:"success",ssr:!0}],lastMatchId:"__root__\u0000"})($R["tsr"]);$_TSR.e();document.currentScript.remove()</script><script type="module" async="" src="./assets/index-CPTihwDe.js"></script></body></html>'
14
+ '<!DOCTYPE html><html lang="en" data-theme="dark" data-regen-radius="none"><head><meta charSet="utf-8"/><meta content="width=device-width, initial-scale=1" name="viewport"/><link rel="stylesheet" href="./assets/styles-Cd7Fp5b_.css" data-precedence="default"/><title>Tempo API Admin</title><link rel="modulepreload" href="./assets/index-CPTihwDe.js"/><link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20269%20269%22%3E%3Crect%20width%3D%22269%22%20height%3D%22269%22%20fill%3D%22%23000%22%2F%3E%3Cpath%20d%3D%22M123.273%20190.794H93.445L121.09%20105.318H85.7334L93.445%2080.2642H191.95L184.238%20105.318H150.773L123.273%20190.794Z%22%20fill%3D%22%23fff%22%2F%3E%3C%2Fsvg%3E"/></head><body><div id="root"></div><script class="$tsr" id="$tsr-stream-barrier">(self.$R=self.$R||{})["tsr"]=[];self.$_TSR={h(){this.hydrated=!0,this.c()},e(){this.streamEnded=!0,this.c()},c(){this.hydrated&&this.streamEnded&&(delete self.$_TSR,delete self.$R.tsr)},p(e){this.initialized?e():this.buffer.push(e)},buffer:[]};$_TSR.router=($R=>$R[0]={manifest:$R[1]={routes:$R[2]={__root__:$R[3]={preloads:$R[4]=["./assets/index-CPTihwDe.js"],scripts:$R[5]=[$R[6]={attrs:$R[7]={type:"module",async:!0,src:"./assets/index-CPTihwDe.js"}}]}}},matches:$R[8]=[$R[9]={i:"__root__\u0000",u:1783292569178,s:"success",ssr:!0}],lastMatchId:"__root__\u0000"})($R["tsr"]);$_TSR.e();document.currentScript.remove()</script><script type="module" async="" src="./assets/index-CPTihwDe.js"></script></body></html>'
15
15
 
16
16
  /** Content-hashed UI assets, keyed by their path under the mount (e.g. `assets/index-*.js`). */
17
17
  export const assets: Record<string, Asset> = {
@@ -0,0 +1,66 @@
1
+ import { App, Store } from 'tapimo'
2
+ import { data, platform, relay } from 'tapimo/apps'
3
+ import * as TestApp from '../../test/App.js'
4
+
5
+ // Each test mounts one group and asserts its routes exist while the omitted
6
+ // group's are absent (404 and not in the document). Only `paths` are checked;
7
+ // tags/schemes stay a superset (Decision C).
8
+
9
+ /** Paths (sans method) present in a composed app's OpenAPI document. */
10
+ async function paths(app: { request: (path: string) => Promise<Response> | Response }) {
11
+ const res = await app.request('/openapi.json')
12
+ const spec = (await res.json()) as { paths: Record<string, unknown> }
13
+ return Object.keys(spec.paths)
14
+ }
15
+
16
+ describe('data()', () => {
17
+ test('mounts data routes and omits platform routes', async () => {
18
+ const app = App.create({ db: TestApp.database() }).route('/', data())
19
+
20
+ const keys = await paths(app)
21
+ expect(keys.some((path) => path.startsWith('/v1/tokens'))).toBe(true)
22
+ expect(keys.some((path) => path.startsWith('/v1/blocks'))).toBe(true)
23
+ expect(keys.some((path) => path.startsWith('/v1/orgs'))).toBe(false)
24
+ expect(keys.some((path) => path.startsWith('/v1/me'))).toBe(false)
25
+
26
+ // The omitted platform route resolves to a 404 (never mounted), not a 401.
27
+ const orgs = await app.request('/v1/orgs')
28
+ expect(orgs.status).toBe(404)
29
+ })
30
+ })
31
+
32
+ describe('relay()', () => {
33
+ test('mounts the relay after data() and stays out of the document', async () => {
34
+ const composed = App.create({ db: TestApp.database() }).route('/', data()).route('/', relay())
35
+
36
+ // Mounted but default-closed and hidden: unauthenticated requests are
37
+ // refused, and the path never appears in the OpenAPI document.
38
+ const mounted = await composed.request('/rpc/relay')
39
+ expect(mounted.status).toBe(401)
40
+ expect((await paths(composed)).some((path) => path.startsWith('/rpc/relay'))).toBe(false)
41
+
42
+ // Without the relay group, the data group no longer serves the path.
43
+ const dataOnly = App.create({ db: TestApp.database() }).route('/', data())
44
+ expect((await dataOnly.request('/rpc/relay')).status).toBe(404)
45
+ })
46
+ })
47
+
48
+ describe('platform()', () => {
49
+ test('mounts platform routes and omits data routes', async () => {
50
+ const app = App.create({
51
+ auth: { session: { wallet: { origin: 'https://test.example' } } },
52
+ db: TestApp.database(),
53
+ kv: { store: Store.memory() },
54
+ }).route('/', platform())
55
+
56
+ const keys = await paths(app)
57
+ expect(keys.some((path) => path.startsWith('/v1/orgs'))).toBe(true)
58
+ expect(keys.some((path) => path.startsWith('/v1/me'))).toBe(true)
59
+ expect(keys.some((path) => path.startsWith('/v1/tokens'))).toBe(false)
60
+ expect(keys.some((path) => path.startsWith('/v1/blocks'))).toBe(false)
61
+
62
+ // The omitted data route resolves to a 404 (never mounted).
63
+ const token = await app.request('/v1/tokens/0x20c0000000000000000000000000000000000000')
64
+ expect(token.status).toBe(404)
65
+ })
66
+ })