tapimo 0.1.0 → 0.3.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 (520) hide show
  1. package/README.md +57 -16
  2. package/dist/ApiKey.d.ts +4 -1
  3. package/dist/ApiKey.d.ts.map +1 -1
  4. package/dist/ApiKey.js +6 -2
  5. package/dist/ApiKey.js.map +1 -1
  6. package/dist/ApiKeys.d.ts +98 -3
  7. package/dist/ApiKeys.d.ts.map +1 -1
  8. package/dist/ApiKeys.js +150 -1
  9. package/dist/ApiKeys.js.map +1 -1
  10. package/dist/App.d.ts +7917 -2315
  11. package/dist/App.d.ts.map +1 -1
  12. package/dist/App.js +141 -329
  13. package/dist/App.js.map +1 -1
  14. package/dist/Scope.d.ts +20 -4
  15. package/dist/Scope.d.ts.map +1 -1
  16. package/dist/Scope.js +23 -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 +203 -120
  23. package/dist/admin/App.d.ts.map +1 -1
  24. package/dist/admin/apps/api-keys.d.ts +111 -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 +12 -1
  29. package/dist/admin/apps/scopes.d.ts.map +1 -1
  30. package/dist/admin/apps/scopes.js +3 -0
  31. package/dist/admin/apps/scopes.js.map +1 -1
  32. package/dist/admin/apps/verified-tokens.d.ts +116 -116
  33. package/dist/admin/apps/verified-tokens.js +1 -1
  34. package/dist/admin/apps/verified-tokens.js.map +1 -1
  35. package/dist/admin/ui.generated.d.ts +1 -1
  36. package/dist/admin/ui.generated.js +13 -13
  37. package/dist/admin/ui.generated.js.map +1 -1
  38. package/dist/analytics/Analytics.d.ts +151 -0
  39. package/dist/analytics/Analytics.d.ts.map +1 -0
  40. package/dist/analytics/Analytics.js +212 -0
  41. package/dist/analytics/Analytics.js.map +1 -0
  42. package/dist/analytics/tables/requestEvents.d.ts +215 -0
  43. package/dist/analytics/tables/requestEvents.d.ts.map +1 -0
  44. package/dist/analytics/tables/requestEvents.js +264 -0
  45. package/dist/analytics/tables/requestEvents.js.map +1 -0
  46. package/dist/apps/data/App.d.ts +19576 -0
  47. package/dist/apps/data/App.d.ts.map +1 -0
  48. package/dist/apps/data/App.js +272 -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} +4 -4
  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} +8 -8
  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} +108 -108
  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} +39 -39
  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/data/routes/funding.d.ts +765 -0
  83. package/dist/apps/data/routes/funding.d.ts.map +1 -0
  84. package/dist/apps/data/routes/funding.js +383 -0
  85. package/dist/apps/data/routes/funding.js.map +1 -0
  86. package/dist/apps/{indexer.d.ts → data/routes/indexer.d.ts} +4 -4
  87. package/dist/apps/data/routes/indexer.d.ts.map +1 -0
  88. package/dist/apps/{indexer.js → data/routes/indexer.js} +13 -9
  89. package/dist/apps/data/routes/indexer.js.map +1 -0
  90. package/dist/apps/{receipts.d.ts → data/routes/receipts.d.ts} +49 -49
  91. package/dist/apps/data/routes/receipts.d.ts.map +1 -0
  92. package/dist/apps/{receipts.js → data/routes/receipts.js} +14 -20
  93. package/dist/apps/data/routes/receipts.js.map +1 -0
  94. package/dist/apps/{rpc.d.ts → data/routes/rpc.d.ts} +33 -40
  95. package/dist/apps/data/routes/rpc.d.ts.map +1 -0
  96. package/dist/apps/{rpc.js → data/routes/rpc.js} +35 -7
  97. package/dist/apps/data/routes/rpc.js.map +1 -0
  98. package/dist/apps/{tokenlist.d.ts → data/routes/tokenlist.d.ts} +40 -40
  99. package/dist/apps/data/routes/tokenlist.d.ts.map +1 -0
  100. package/dist/apps/{tokenlist.js → data/routes/tokenlist.js} +8 -8
  101. package/dist/apps/data/routes/tokenlist.js.map +1 -0
  102. package/dist/apps/{tokens.d.ts → data/routes/tokens.d.ts} +468 -468
  103. package/dist/apps/data/routes/tokens.d.ts.map +1 -0
  104. package/dist/apps/{tokens.js → data/routes/tokens.js} +19 -19
  105. package/dist/apps/data/routes/tokens.js.map +1 -0
  106. package/dist/apps/{transactions.d.ts → data/routes/transactions.d.ts} +304 -304
  107. package/dist/apps/data/routes/transactions.d.ts.map +1 -0
  108. package/dist/apps/{transactions.js → data/routes/transactions.js} +13 -13
  109. package/dist/apps/data/routes/transactions.js.map +1 -0
  110. package/dist/apps/{transfers.d.ts → data/routes/transfers.d.ts} +62 -62
  111. package/dist/apps/data/routes/transfers.d.ts.map +1 -0
  112. package/dist/apps/{transfers.js → data/routes/transfers.js} +13 -13
  113. package/dist/apps/data/routes/transfers.js.map +1 -0
  114. package/dist/apps/{verified-tokens.d.ts → data/routes/verified-tokens.d.ts} +6 -6
  115. package/dist/apps/data/routes/verified-tokens.d.ts.map +1 -0
  116. package/dist/apps/{verified-tokens.js → data/routes/verified-tokens.js} +9 -9
  117. package/dist/apps/data/routes/verified-tokens.js.map +1 -0
  118. package/dist/apps/{webhooks.d.ts → data/routes/webhooks.d.ts} +17 -17
  119. package/dist/apps/{webhooks.d.ts.map → data/routes/webhooks.d.ts.map} +1 -1
  120. package/dist/apps/{webhooks.js → data/routes/webhooks.js} +38 -34
  121. package/dist/apps/data/routes/webhooks.js.map +1 -0
  122. package/dist/apps/docs/App.d.ts +29 -0
  123. package/dist/apps/docs/App.d.ts.map +1 -0
  124. package/dist/apps/docs/App.js +54 -0
  125. package/dist/apps/docs/App.js.map +1 -0
  126. package/dist/{internal/OpenApi.fonts.d.ts → apps/docs/fonts.d.ts} +1 -1
  127. package/dist/apps/docs/fonts.d.ts.map +1 -0
  128. package/dist/{internal/OpenApi.fonts.js → apps/docs/fonts.js} +1 -1
  129. package/dist/apps/docs/fonts.js.map +1 -0
  130. package/dist/apps/docs/index.d.ts +2 -0
  131. package/dist/apps/docs/index.d.ts.map +1 -0
  132. package/dist/apps/docs/index.js +2 -0
  133. package/dist/apps/docs/index.js.map +1 -0
  134. package/dist/{internal/OpenApi.logo.d.ts → apps/docs/logo.d.ts} +1 -1
  135. package/dist/apps/docs/logo.d.ts.map +1 -0
  136. package/dist/{internal/OpenApi.logo.js → apps/docs/logo.js} +1 -1
  137. package/dist/apps/docs/logo.js.map +1 -0
  138. package/dist/apps/docs/reference.d.ts +21 -0
  139. package/dist/apps/docs/reference.d.ts.map +1 -0
  140. package/dist/apps/docs/reference.js +112 -0
  141. package/dist/apps/docs/reference.js.map +1 -0
  142. package/dist/apps/index.d.ts +5 -0
  143. package/dist/apps/index.d.ts.map +1 -0
  144. package/dist/apps/index.js +5 -0
  145. package/dist/apps/index.js.map +1 -0
  146. package/dist/apps/mcp.d.ts +76 -0
  147. package/dist/apps/mcp.d.ts.map +1 -0
  148. package/dist/apps/mcp.js +188 -0
  149. package/dist/apps/mcp.js.map +1 -0
  150. package/dist/apps/metadata.d.ts +7 -0
  151. package/dist/apps/metadata.d.ts.map +1 -0
  152. package/dist/apps/metadata.js +15 -0
  153. package/dist/apps/metadata.js.map +1 -0
  154. package/dist/apps/platform/App.d.ts +5262 -0
  155. package/dist/apps/platform/App.d.ts.map +1 -0
  156. package/dist/apps/platform/App.js +84 -0
  157. package/dist/apps/platform/App.js.map +1 -0
  158. package/dist/apps/platform/Billing.d.ts +279 -0
  159. package/dist/apps/platform/Billing.d.ts.map +1 -0
  160. package/dist/apps/platform/Billing.js +477 -0
  161. package/dist/apps/platform/Billing.js.map +1 -0
  162. package/dist/apps/platform/index.d.ts +3 -0
  163. package/dist/apps/platform/index.d.ts.map +1 -0
  164. package/dist/apps/platform/index.js +3 -0
  165. package/dist/apps/platform/index.js.map +1 -0
  166. package/dist/apps/platform/routes/api-keys.d.ts +973 -0
  167. package/dist/apps/platform/routes/api-keys.d.ts.map +1 -0
  168. package/dist/apps/platform/routes/api-keys.js +430 -0
  169. package/dist/apps/platform/routes/api-keys.js.map +1 -0
  170. package/dist/apps/platform/routes/billing.d.ts +1520 -0
  171. package/dist/apps/platform/routes/billing.d.ts.map +1 -0
  172. package/dist/apps/platform/routes/billing.js +795 -0
  173. package/dist/apps/platform/routes/billing.js.map +1 -0
  174. package/dist/apps/platform/routes/invitations.d.ts +851 -0
  175. package/dist/apps/platform/routes/invitations.d.ts.map +1 -0
  176. package/dist/apps/platform/routes/invitations.js +471 -0
  177. package/dist/apps/platform/routes/invitations.js.map +1 -0
  178. package/dist/apps/platform/routes/me.d.ts +95 -0
  179. package/dist/apps/platform/routes/me.d.ts.map +1 -0
  180. package/dist/apps/platform/routes/me.js +81 -0
  181. package/dist/apps/platform/routes/me.js.map +1 -0
  182. package/dist/apps/platform/routes/members.d.ts +554 -0
  183. package/dist/apps/platform/routes/members.d.ts.map +1 -0
  184. package/dist/apps/platform/routes/members.js +251 -0
  185. package/dist/apps/platform/routes/members.js.map +1 -0
  186. package/dist/apps/platform/routes/orgs.d.ts +596 -0
  187. package/dist/apps/platform/routes/orgs.d.ts.map +1 -0
  188. package/dist/apps/platform/routes/orgs.js +276 -0
  189. package/dist/apps/platform/routes/orgs.js.map +1 -0
  190. package/dist/apps/platform/routes/projects.d.ts +740 -0
  191. package/dist/apps/platform/routes/projects.d.ts.map +1 -0
  192. package/dist/apps/platform/routes/projects.js +286 -0
  193. package/dist/apps/platform/routes/projects.js.map +1 -0
  194. package/dist/apps/platform/routes/scopes.d.ts +107 -0
  195. package/dist/apps/platform/routes/scopes.d.ts.map +1 -0
  196. package/dist/apps/platform/routes/scopes.js +66 -0
  197. package/dist/apps/platform/routes/scopes.js.map +1 -0
  198. package/dist/apps/platform/routes/usage.d.ts +588 -0
  199. package/dist/apps/platform/routes/usage.d.ts.map +1 -0
  200. package/dist/apps/platform/routes/usage.js +391 -0
  201. package/dist/apps/platform/routes/usage.js.map +1 -0
  202. package/dist/apps/relay/App.d.ts +44 -0
  203. package/dist/apps/relay/App.d.ts.map +1 -0
  204. package/dist/apps/relay/App.js +211 -0
  205. package/dist/apps/relay/App.js.map +1 -0
  206. package/dist/apps/relay/Sponsorships.d.ts +107 -0
  207. package/dist/apps/relay/Sponsorships.d.ts.map +1 -0
  208. package/dist/apps/relay/Sponsorships.js +194 -0
  209. package/dist/apps/relay/Sponsorships.js.map +1 -0
  210. package/dist/apps/relay/index.d.ts +3 -0
  211. package/dist/apps/relay/index.d.ts.map +1 -0
  212. package/dist/apps/relay/index.js +3 -0
  213. package/dist/apps/relay/index.js.map +1 -0
  214. package/dist/cli/Cli.d.ts +9 -2
  215. package/dist/cli/Cli.d.ts.map +1 -1
  216. package/dist/cli/Cli.js +12 -4
  217. package/dist/cli/Cli.js.map +1 -1
  218. package/dist/cli/commands/admin.d.ts +5 -1
  219. package/dist/cli/commands/admin.d.ts.map +1 -1
  220. package/dist/cli/commands/admin.js +82 -3
  221. package/dist/cli/commands/admin.js.map +1 -1
  222. package/dist/db/Db.d.ts +17 -0
  223. package/dist/db/Db.d.ts.map +1 -1
  224. package/dist/db/Db.js +254 -0
  225. package/dist/db/Db.js.map +1 -1
  226. package/dist/db/Schema.d.ts +159 -0
  227. package/dist/db/Schema.d.ts.map +1 -1
  228. package/dist/db/Schema.js +187 -2
  229. package/dist/db/Schema.js.map +1 -1
  230. package/dist/db/tables/billingSettings.d.ts +55 -0
  231. package/dist/db/tables/billingSettings.d.ts.map +1 -0
  232. package/dist/db/tables/billingSettings.js +70 -0
  233. package/dist/db/tables/billingSettings.js.map +1 -0
  234. package/dist/db/tables/invitations.d.ts +84 -0
  235. package/dist/db/tables/invitations.d.ts.map +1 -0
  236. package/dist/db/tables/invitations.js +165 -0
  237. package/dist/db/tables/invitations.js.map +1 -0
  238. package/dist/db/tables/memberships.d.ts +108 -0
  239. package/dist/db/tables/memberships.d.ts.map +1 -0
  240. package/dist/db/tables/memberships.js +153 -0
  241. package/dist/db/tables/memberships.js.map +1 -0
  242. package/dist/db/tables/organizations.d.ts +48 -0
  243. package/dist/db/tables/organizations.d.ts.map +1 -1
  244. package/dist/db/tables/organizations.js +97 -2
  245. package/dist/db/tables/organizations.js.map +1 -1
  246. package/dist/db/tables/projects.d.ts +65 -0
  247. package/dist/db/tables/projects.d.ts.map +1 -0
  248. package/dist/db/tables/projects.js +75 -0
  249. package/dist/db/tables/projects.js.map +1 -0
  250. package/dist/db/tables/requestUsage.d.ts +121 -0
  251. package/dist/db/tables/requestUsage.d.ts.map +1 -0
  252. package/dist/db/tables/requestUsage.js +150 -0
  253. package/dist/db/tables/requestUsage.js.map +1 -0
  254. package/dist/db/tables/sponsoredTransactions.d.ts +239 -0
  255. package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -0
  256. package/dist/db/tables/sponsoredTransactions.js +314 -0
  257. package/dist/db/tables/sponsoredTransactions.js.map +1 -0
  258. package/dist/db/tables/stripeCustomers.d.ts +81 -0
  259. package/dist/db/tables/stripeCustomers.d.ts.map +1 -0
  260. package/dist/db/tables/stripeCustomers.js +120 -0
  261. package/dist/db/tables/stripeCustomers.js.map +1 -0
  262. package/dist/db/tables/users.d.ts +48 -0
  263. package/dist/db/tables/users.d.ts.map +1 -0
  264. package/dist/db/tables/users.js +65 -0
  265. package/dist/db/tables/users.js.map +1 -0
  266. package/dist/handlers/internal/executionError.d.ts.map +1 -1
  267. package/dist/handlers/internal/executionError.js +2 -0
  268. package/dist/handlers/internal/executionError.js.map +1 -1
  269. package/dist/handlers/internal/multisig.d.ts +2 -6
  270. package/dist/handlers/internal/multisig.d.ts.map +1 -1
  271. package/dist/handlers/internal/multisig.js +11 -36
  272. package/dist/handlers/internal/multisig.js.map +1 -1
  273. package/dist/handlers/internal/sponsorship.d.ts +35 -2
  274. package/dist/handlers/internal/sponsorship.d.ts.map +1 -1
  275. package/dist/handlers/internal/sponsorship.js +53 -14
  276. package/dist/handlers/internal/sponsorship.js.map +1 -1
  277. package/dist/handlers/internal/utils.d.ts +10 -3
  278. package/dist/handlers/internal/utils.d.ts.map +1 -1
  279. package/dist/handlers/internal/utils.js +14 -6
  280. package/dist/handlers/internal/utils.js.map +1 -1
  281. package/dist/handlers/relay.d.ts +6 -2
  282. package/dist/handlers/relay.d.ts.map +1 -1
  283. package/dist/handlers/relay.js +16 -2
  284. package/dist/handlers/relay.js.map +1 -1
  285. package/dist/index.d.ts +1 -0
  286. package/dist/index.d.ts.map +1 -1
  287. package/dist/index.js +1 -0
  288. package/dist/index.js.map +1 -1
  289. package/dist/internal/Auth.d.ts +222 -6
  290. package/dist/internal/Auth.d.ts.map +1 -1
  291. package/dist/internal/Auth.js +625 -74
  292. package/dist/internal/Auth.js.map +1 -1
  293. package/dist/internal/Cache.d.ts.map +1 -1
  294. package/dist/internal/Cache.js +4 -1
  295. package/dist/internal/Cache.js.map +1 -1
  296. package/dist/internal/EdgeCache.d.ts +10 -0
  297. package/dist/internal/EdgeCache.d.ts.map +1 -1
  298. package/dist/internal/EdgeCache.js +39 -8
  299. package/dist/internal/EdgeCache.js.map +1 -1
  300. package/dist/internal/Fees.d.ts +27 -0
  301. package/dist/internal/Fees.d.ts.map +1 -0
  302. package/dist/internal/Fees.js +48 -0
  303. package/dist/internal/Fees.js.map +1 -0
  304. package/dist/internal/FundingRoutes.d.ts +183 -0
  305. package/dist/internal/FundingRoutes.d.ts.map +1 -0
  306. package/dist/internal/FundingRoutes.js +490 -0
  307. package/dist/internal/FundingRoutes.js.map +1 -0
  308. package/dist/internal/Id.d.ts +15 -0
  309. package/dist/internal/Id.d.ts.map +1 -0
  310. package/dist/internal/Id.js +22 -0
  311. package/dist/internal/Id.js.map +1 -0
  312. package/dist/internal/Log.d.ts +9 -3
  313. package/dist/internal/Log.d.ts.map +1 -1
  314. package/dist/internal/Log.js +7 -0
  315. package/dist/internal/Log.js.map +1 -1
  316. package/dist/internal/OpenApi.d.ts +10 -20
  317. package/dist/internal/OpenApi.d.ts.map +1 -1
  318. package/dist/internal/OpenApi.js +8 -58
  319. package/dist/internal/OpenApi.js.map +1 -1
  320. package/dist/internal/RateLimit.d.ts +25 -0
  321. package/dist/internal/RateLimit.d.ts.map +1 -1
  322. package/dist/internal/RateLimit.js +51 -0
  323. package/dist/internal/RateLimit.js.map +1 -1
  324. package/dist/internal/Response.d.ts +1 -1
  325. package/dist/internal/Response.d.ts.map +1 -1
  326. package/dist/internal/Schema.d.ts +8 -0
  327. package/dist/internal/Schema.d.ts.map +1 -1
  328. package/dist/internal/Schema.js +7 -0
  329. package/dist/internal/Schema.js.map +1 -1
  330. package/dist/internal/Store.d.ts +7 -1
  331. package/dist/internal/Store.d.ts.map +1 -1
  332. package/dist/internal/Store.js +16 -2
  333. package/dist/internal/Store.js.map +1 -1
  334. package/dist/internal/Viem.d.ts +84 -0
  335. package/dist/internal/Viem.d.ts.map +1 -1
  336. package/dist/internal/Webhooks.d.ts +1 -1
  337. package/dist/internal/Webhooks.d.ts.map +1 -1
  338. package/dist/internal/Webhooks.js +3 -3
  339. package/dist/internal/Webhooks.js.map +1 -1
  340. package/package.json +33 -6
  341. package/src/ApiKey.ts +10 -2
  342. package/src/ApiKeys.test.ts +155 -1
  343. package/src/ApiKeys.ts +203 -1
  344. package/src/App.test.ts +64 -75741
  345. package/src/App.ts +218 -420
  346. package/src/Client.test-d.ts +657 -15
  347. package/src/Scope.ts +26 -4
  348. package/src/Webhooks.ts +2 -2
  349. package/src/admin/apps/api-keys.test.ts +51 -0
  350. package/src/admin/apps/api-keys.ts +84 -0
  351. package/src/admin/apps/scopes.test.ts +11 -1
  352. package/src/admin/apps/scopes.ts +3 -0
  353. package/src/admin/apps/verified-tokens.ts +1 -1
  354. package/src/admin/ui/src/components/Page.tsx +2 -3
  355. package/src/admin/ui/src/routes/__root.tsx +2 -2
  356. package/src/admin/ui/src/routes/api-keys.tsx +2 -2
  357. package/src/admin/ui.generated.ts +13 -13
  358. package/src/analytics/Analytics.test.ts +527 -0
  359. package/src/analytics/Analytics.ts +318 -0
  360. package/src/analytics/tables/requestEvents.ts +523 -0
  361. package/src/apps/composition.test.ts +66 -0
  362. package/src/apps/data/App.test.ts +72 -0
  363. package/src/apps/data/App.ts +330 -0
  364. package/src/apps/data/index.ts +2 -0
  365. package/src/apps/{activities.test.ts → data/routes/activities.test.ts} +3 -3
  366. package/src/apps/{activities.ts → data/routes/activities.ts} +15 -15
  367. package/src/apps/{addresses.ts → data/routes/addresses.ts} +6 -6
  368. package/src/apps/{balances.test.ts → data/routes/balances.test.ts} +2 -2
  369. package/src/apps/{balances.ts → data/routes/balances.ts} +46 -12
  370. package/src/apps/{blocks.test.ts → data/routes/blocks.test.ts} +3 -3
  371. package/src/apps/{blocks.ts → data/routes/blocks.ts} +14 -14
  372. package/src/apps/{coingecko.test.ts → data/routes/coingecko.test.ts} +2 -2
  373. package/src/apps/{coingecko.ts → data/routes/coingecko.ts} +14 -14
  374. package/src/apps/{exchanges.test.ts → data/routes/exchanges.test.ts} +1 -1
  375. package/src/apps/{exchanges.ts → data/routes/exchanges.ts} +20 -20
  376. package/src/apps/{fee-amm.test.ts → data/routes/fee-amm.test.ts} +3 -3
  377. package/src/apps/{fee-amm.ts → data/routes/fee-amm.ts} +18 -18
  378. package/src/apps/data/routes/funding.test.ts +214 -0
  379. package/src/apps/data/routes/funding.ts +584 -0
  380. package/src/apps/{indexer.test.ts → data/routes/indexer.test.ts} +26 -5
  381. package/src/apps/{indexer.ts → data/routes/indexer.ts} +13 -10
  382. package/src/apps/{receipts.test.ts → data/routes/receipts.test.ts} +3 -3
  383. package/src/apps/{receipts.ts → data/routes/receipts.ts} +15 -22
  384. package/src/apps/{rpc.test.ts → data/routes/rpc.test.ts} +122 -7
  385. package/src/apps/{rpc.ts → data/routes/rpc.ts} +38 -8
  386. package/src/apps/{tokenlist.test.ts → data/routes/tokenlist.test.ts} +3 -3
  387. package/src/apps/{tokenlist.ts → data/routes/tokenlist.ts} +9 -9
  388. package/src/apps/{tokens.test.ts → data/routes/tokens.test.ts} +2 -2
  389. package/src/apps/{tokens.ts → data/routes/tokens.ts} +20 -20
  390. package/src/apps/{transactions.test.ts → data/routes/transactions.test.ts} +3 -3
  391. package/src/apps/{transactions.ts → data/routes/transactions.ts} +15 -15
  392. package/src/apps/{transfers.test.ts → data/routes/transfers.test.ts} +2 -2
  393. package/src/apps/{transfers.ts → data/routes/transfers.ts} +15 -15
  394. package/src/apps/{verified-tokens.test.ts → data/routes/verified-tokens.test.ts} +3 -3
  395. package/src/apps/{verified-tokens.ts → data/routes/verified-tokens.ts} +10 -10
  396. package/src/apps/{webhooks.test.ts → data/routes/webhooks.test.ts} +7 -7
  397. package/src/apps/{webhooks.ts → data/routes/webhooks.ts} +50 -43
  398. package/src/apps/docs/App.ts +65 -0
  399. package/src/apps/docs/index.ts +1 -0
  400. package/src/apps/docs/reference.ts +132 -0
  401. package/src/apps/index.ts +4 -0
  402. package/src/apps/mcp.test.ts +206 -0
  403. package/src/apps/mcp.ts +250 -0
  404. package/src/apps/metadata.ts +23 -0
  405. package/src/apps/platform/App.ts +169 -0
  406. package/src/apps/platform/Billing.test.ts +530 -0
  407. package/src/apps/platform/Billing.ts +682 -0
  408. package/src/apps/platform/index.ts +2 -0
  409. package/src/apps/platform/routes/api-keys.test.ts +493 -0
  410. package/src/apps/platform/routes/api-keys.ts +543 -0
  411. package/src/apps/platform/routes/billing.test.ts +1100 -0
  412. package/src/apps/platform/routes/billing.ts +966 -0
  413. package/src/apps/platform/routes/invitations.test.ts +409 -0
  414. package/src/apps/platform/routes/invitations.ts +587 -0
  415. package/src/apps/platform/routes/me.test.ts +351 -0
  416. package/src/apps/platform/routes/me.ts +106 -0
  417. package/src/apps/platform/routes/members.test.ts +195 -0
  418. package/src/apps/platform/routes/members.ts +313 -0
  419. package/src/apps/platform/routes/orgs.test.ts +313 -0
  420. package/src/apps/platform/routes/orgs.ts +365 -0
  421. package/src/apps/platform/routes/projects.test.ts +181 -0
  422. package/src/apps/platform/routes/projects.ts +367 -0
  423. package/src/apps/platform/routes/scopes.test.ts +29 -0
  424. package/src/apps/platform/routes/scopes.ts +85 -0
  425. package/src/apps/platform/routes/usage.test.ts +422 -0
  426. package/src/apps/platform/routes/usage.ts +527 -0
  427. package/src/apps/relay/App.test.ts +933 -0
  428. package/src/apps/relay/App.ts +236 -0
  429. package/src/apps/relay/Sponsorships.test.ts +375 -0
  430. package/src/apps/relay/Sponsorships.ts +306 -0
  431. package/src/apps/relay/index.ts +2 -0
  432. package/src/cli/Cli.test.ts +25 -2
  433. package/src/cli/Cli.ts +18 -3
  434. package/src/cli/commands/admin.test.ts +83 -0
  435. package/src/cli/commands/admin.ts +91 -3
  436. package/src/db/Db.ts +296 -0
  437. package/src/db/Schema.ts +241 -2
  438. package/src/db/tables/billingSettings.test.ts +87 -0
  439. package/src/db/tables/billingSettings.ts +108 -0
  440. package/src/db/tables/invitations.ts +198 -0
  441. package/src/db/tables/memberships.ts +208 -0
  442. package/src/db/tables/organizations.test.ts +38 -2
  443. package/src/db/tables/organizations.ts +117 -2
  444. package/src/db/tables/projects.ts +107 -0
  445. package/src/db/tables/requestUsage.ts +231 -0
  446. package/src/db/tables/sponsoredTransactions.test.ts +325 -0
  447. package/src/db/tables/sponsoredTransactions.ts +464 -0
  448. package/src/db/tables/stripeCustomers.test.ts +109 -0
  449. package/src/db/tables/stripeCustomers.ts +162 -0
  450. package/src/db/tables/users.ts +85 -0
  451. package/src/handlers/internal/executionError.ts +2 -0
  452. package/src/handlers/internal/multisig.test.ts +84 -10
  453. package/src/handlers/internal/multisig.ts +15 -50
  454. package/src/handlers/internal/sponsorship.ts +106 -20
  455. package/src/handlers/internal/utils.ts +19 -7
  456. package/src/handlers/{internal/relay.localnet.test.ts → relay.test.ts} +2 -2
  457. package/src/handlers/relay.ts +23 -5
  458. package/src/index.ts +1 -0
  459. package/src/internal/Auth.test.ts +491 -25
  460. package/src/internal/Auth.ts +839 -84
  461. package/src/internal/Cache.ts +5 -1
  462. package/src/internal/Cursor.test.ts +3 -2
  463. package/src/internal/EdgeCache.test.ts +27 -4
  464. package/src/internal/EdgeCache.ts +47 -9
  465. package/src/internal/Fees.test.ts +32 -0
  466. package/src/internal/Fees.ts +51 -0
  467. package/src/internal/FundingRoutes.ts +583 -0
  468. package/src/internal/Id.test.ts +17 -0
  469. package/src/internal/Id.ts +24 -0
  470. package/src/internal/Log.test.ts +2 -1
  471. package/src/internal/Log.ts +16 -3
  472. package/src/internal/MetricSink.ts +1 -1
  473. package/src/internal/OpenApi.ts +12 -60
  474. package/src/internal/RateLimit.test.ts +84 -0
  475. package/src/internal/RateLimit.ts +70 -0
  476. package/src/internal/Response.ts +1 -1
  477. package/src/internal/Schema.ts +8 -0
  478. package/src/internal/Store.test.ts +52 -0
  479. package/src/internal/Store.ts +24 -5
  480. package/src/internal/Tidx.local.test.ts +1 -1
  481. package/src/internal/VerifiedTokens.test.ts +1 -1
  482. package/src/internal/WebhookDestination.test.ts +1 -1
  483. package/src/internal/Webhooks.test.ts +7 -7
  484. package/src/internal/Webhooks.ts +4 -4
  485. package/dist/apps/activities.js.map +0 -1
  486. package/dist/apps/addresses.js.map +0 -1
  487. package/dist/apps/balances.d.ts.map +0 -1
  488. package/dist/apps/balances.js.map +0 -1
  489. package/dist/apps/blocks.d.ts.map +0 -1
  490. package/dist/apps/blocks.js.map +0 -1
  491. package/dist/apps/coingecko.d.ts.map +0 -1
  492. package/dist/apps/coingecko.js.map +0 -1
  493. package/dist/apps/exchanges.d.ts.map +0 -1
  494. package/dist/apps/exchanges.js.map +0 -1
  495. package/dist/apps/fee-amm.d.ts.map +0 -1
  496. package/dist/apps/fee-amm.js.map +0 -1
  497. package/dist/apps/indexer.d.ts.map +0 -1
  498. package/dist/apps/indexer.js.map +0 -1
  499. package/dist/apps/receipts.d.ts.map +0 -1
  500. package/dist/apps/receipts.js.map +0 -1
  501. package/dist/apps/rpc.d.ts.map +0 -1
  502. package/dist/apps/rpc.js.map +0 -1
  503. package/dist/apps/tokenlist.d.ts.map +0 -1
  504. package/dist/apps/tokenlist.js.map +0 -1
  505. package/dist/apps/tokens.d.ts.map +0 -1
  506. package/dist/apps/tokens.js.map +0 -1
  507. package/dist/apps/transactions.d.ts.map +0 -1
  508. package/dist/apps/transactions.js.map +0 -1
  509. package/dist/apps/transfers.d.ts.map +0 -1
  510. package/dist/apps/transfers.js.map +0 -1
  511. package/dist/apps/verified-tokens.d.ts.map +0 -1
  512. package/dist/apps/verified-tokens.js.map +0 -1
  513. package/dist/apps/webhooks.js.map +0 -1
  514. package/dist/internal/OpenApi.fonts.d.ts.map +0 -1
  515. package/dist/internal/OpenApi.fonts.js.map +0 -1
  516. package/dist/internal/OpenApi.logo.d.ts.map +0 -1
  517. package/dist/internal/OpenApi.logo.js.map +0 -1
  518. package/src/handlers/internal/multisig.localnet.test.ts +0 -146
  519. /package/src/{internal/OpenApi.fonts.ts → apps/docs/fonts.ts} +0 -0
  520. /package/src/{internal/OpenApi.logo.ts → apps/docs/logo.ts} +0 -0
@@ -0,0 +1,313 @@
1
+ import { type Context, Hono } from 'hono'
2
+ import * as z from 'zod/mini'
3
+
4
+ import type * as App from '../../../App.js'
5
+ import * as Auth from '../../../internal/Auth.js'
6
+ import * as Db from '../../../db/Db.js'
7
+ import * as Memberships from '../../../db/tables/memberships.js'
8
+ import * as OpenApi from '../../../internal/OpenApi.js'
9
+ import * as Response from '../../../internal/Response.js'
10
+ import * as Schema from '../../../internal/Schema.js'
11
+ import * as Users from '../../../db/tables/users.js'
12
+
13
+ /** Zod schemas owned by the members resource. */
14
+ export namespace schema {
15
+ /** A membership role. */
16
+ export const Role = z
17
+ .enum(Memberships.roles)
18
+ .check(z.describe('Membership role.'), z.meta({ examples: ['admin'] }))
19
+
20
+ /** One organization member: the membership plus the member's identity. */
21
+ export const Member = Schema.describe(
22
+ z.object({
23
+ address: z
24
+ .optional(z.string())
25
+ .check(
26
+ z.describe("The member's wallet address, when wallet sign-in established it."),
27
+ z.meta({ examples: ['0x20f414b3cbcf1fd8e8f5e1c1c2e3d4a5b6c7d8e9'] }),
28
+ ),
29
+ createdAt: z
30
+ .string()
31
+ .check(
32
+ z.describe('When the member joined (ISO 8601).'),
33
+ z.meta({ examples: ['2026-01-01T00:00:00.000Z'] }),
34
+ ),
35
+ email: z
36
+ .optional(z.string())
37
+ .check(
38
+ z.describe("The member's verified email, when known."),
39
+ z.meta({ examples: ['dev@example.com'] }),
40
+ ),
41
+ role: Role,
42
+ userId: z
43
+ .string()
44
+ .check(
45
+ z.describe('Member user id (`usr_…`).'),
46
+ z.meta({ examples: ['usr_1a2b3c4d5e6f7g8h9j0k1m2n'] }),
47
+ ),
48
+ }),
49
+ "One organization member: the membership plus the member's identity.",
50
+ )
51
+
52
+ /** Path parameters addressing an organization's member collection. */
53
+ export const Params = z
54
+ .object({
55
+ orgId: z
56
+ .string()
57
+ .check(
58
+ z.describe('The organization id (`org_…`).'),
59
+ z.meta({ examples: ['org_1a2b3c4d5e6f7g8h9j0k1m2n'] }),
60
+ ),
61
+ })
62
+ .check(z.describe("Path parameters for an organization's members."))
63
+
64
+ /** Path parameters addressing one member. */
65
+ export const MemberParams = z
66
+ .object({
67
+ orgId: z
68
+ .string()
69
+ .check(
70
+ z.describe('The organization id (`org_…`).'),
71
+ z.meta({ examples: ['org_1a2b3c4d5e6f7g8h9j0k1m2n'] }),
72
+ ),
73
+ userId: z
74
+ .string()
75
+ .check(
76
+ z.describe('The member user id (`usr_…`).'),
77
+ z.meta({ examples: ['usr_1a2b3c4d5e6f7g8h9j0k1m2n'] }),
78
+ ),
79
+ })
80
+ .check(z.describe('Path parameters for one member.'))
81
+
82
+ /** Request body changing a member's role. */
83
+ export const MemberBody = z
84
+ .object({ role: Role })
85
+ .check(z.describe("Fields for changing a member's role."))
86
+
87
+ /** Schemas for the listMembers operation. */
88
+ export namespace listMembers {
89
+ /** Non-paginated list of an organization's members. */
90
+ export const Response = Schema.describe(
91
+ z.object({
92
+ data: z.array(Member).check(z.describe('The members, oldest first.')),
93
+ }),
94
+ "A non-paginated list of an organization's members.",
95
+ )
96
+ }
97
+
98
+ /** Schemas for the removeMember operation. */
99
+ export namespace removeMember {
100
+ /** Confirmation that the member was removed. */
101
+ export const Response = z
102
+ .object({
103
+ userId: z
104
+ .string()
105
+ .check(
106
+ z.describe('User id (`usr_…`) of the removed member.'),
107
+ z.meta({ examples: ['usr_1a2b3c4d5e6f7g8h9j0k1m2n'] }),
108
+ ),
109
+ })
110
+ .check(z.describe('Confirmation that the member was removed.'))
111
+ }
112
+ }
113
+
114
+ /**
115
+ * Mounts the `/orgs/:orgId/members` resource: the organization's team.
116
+ * Members read; owners change roles and remove members (any member may remove
117
+ * themself). The last owner can never be demoted or removed.
118
+ */
119
+ export function members() {
120
+ return new Hono<App.Environment>()
121
+ .get(
122
+ '/v1/orgs/:orgId{org_[A-Za-z0-9_-]+}/members',
123
+ Auth.policy({ session: true }),
124
+ Auth.ensureOrg(),
125
+ OpenApi.validate('param', schema.Params, {
126
+ code: 'param_invalid',
127
+ message: 'Check the path parameters and try again.',
128
+ }),
129
+ OpenApi.describeRoute({
130
+ operationId: 'listMembers',
131
+ responses: OpenApi.responses({
132
+ errors: { 404: 'No accessible organization was found for this id.' },
133
+ success: {
134
+ description: 'The members, oldest first.',
135
+ schema: schema.listMembers.Response,
136
+ },
137
+ }),
138
+ summary: 'List members',
139
+ tags: ['Members'],
140
+ }),
141
+ async (c) => {
142
+ if (Auth.narrowAccess) return Auth.superAdminAccessError(c)
143
+ if (Auth.narrowScope) return Auth.ensureOrgError(c)
144
+ if (OpenApi.narrowValidation)
145
+ return OpenApi.validationError(c, {
146
+ code: 'param_invalid',
147
+ message: 'Check the path parameters and try again.',
148
+ })
149
+
150
+ const db = Db.get(c.get('db'))
151
+ try {
152
+ const records = await Memberships.listByOrgDetailed(db, Auth.org(c).id)
153
+ return c.json(
154
+ Response.validated(schema.listMembers.Response, {
155
+ data: records.map(serializeMember),
156
+ }),
157
+ 200,
158
+ )
159
+ } catch (cause) {
160
+ return Response.upstream(c, cause)
161
+ }
162
+ },
163
+ )
164
+ .patch(
165
+ '/v1/orgs/:orgId{org_[A-Za-z0-9_-]+}/members/:userId{usr_[A-Za-z0-9_-]+}',
166
+ Auth.policy({ session: true }),
167
+ Auth.ensureOrg({ role: 'owner' }),
168
+ OpenApi.validate('param', schema.MemberParams, {
169
+ code: 'param_invalid',
170
+ message: 'Check the path parameters and try again.',
171
+ }),
172
+ OpenApi.validate('json', schema.MemberBody, {
173
+ code: 'body_invalid',
174
+ message: 'Check the request body and try again.',
175
+ }),
176
+ OpenApi.describeRoute({
177
+ description: "Change a member's role. The last owner cannot be demoted.",
178
+ operationId: 'updateMember',
179
+ responses: OpenApi.responses({
180
+ errors: {
181
+ 404: 'No accessible member was found for this id.',
182
+ 409: {
183
+ codes: ['last_owner'],
184
+ description: 'The organization would be left without an owner.',
185
+ },
186
+ },
187
+ success: { description: 'The updated member.', schema: schema.Member },
188
+ }),
189
+ summary: 'Update member',
190
+ tags: ['Members'],
191
+ }),
192
+ async (c) => {
193
+ if (Auth.narrowAccess) return Auth.superAdminAccessError(c)
194
+ if (Auth.narrowScope) return Auth.ensureOrgError(c)
195
+ if (OpenApi.narrowValidation)
196
+ return OpenApi.validationError(c, {
197
+ code: 'param_invalid',
198
+ message: 'Check the path parameters and try again.',
199
+ })
200
+ if (OpenApi.narrowValidation)
201
+ return OpenApi.validationError(c, {
202
+ code: 'body_invalid',
203
+ message: 'Check the request body and try again.',
204
+ })
205
+
206
+ const { userId } = c.req.valid('param')
207
+ const body = c.req.valid('json')
208
+ const db = Db.get(c.get('db'))
209
+ try {
210
+ const result = await Memberships.update(db, Auth.org(c).id, userId, { role: body.role })
211
+ if (result.error === 'last_owner') return lastOwner(c)
212
+ if (result.error) return memberNotFound(c)
213
+ const user = await Users.get(db, userId)
214
+ if (!user) return memberNotFound(c)
215
+ return c.json(
216
+ Response.validated(
217
+ schema.Member,
218
+ serializeMember({ ...result.record, address: user.address, email: user.email }),
219
+ ),
220
+ 200,
221
+ )
222
+ } catch (cause) {
223
+ return Response.upstream(c, cause)
224
+ }
225
+ },
226
+ )
227
+ .delete(
228
+ '/v1/orgs/:orgId{org_[A-Za-z0-9_-]+}/members/:userId{usr_[A-Za-z0-9_-]+}',
229
+ Auth.policy({ session: true }),
230
+ Auth.ensureOrg(),
231
+ OpenApi.validate('param', schema.MemberParams, {
232
+ code: 'param_invalid',
233
+ message: 'Check the path parameters and try again.',
234
+ }),
235
+ OpenApi.describeRoute({
236
+ description: 'Remove a member. Owners remove anyone; any member removes themself. The last owner cannot be removed.', // prettier-ignore
237
+ operationId: 'removeMember',
238
+ responses: OpenApi.responses({
239
+ errors: {
240
+ 404: 'No accessible member was found for this id.',
241
+ 409: {
242
+ codes: ['last_owner'],
243
+ description: 'The organization would be left without an owner.',
244
+ },
245
+ },
246
+ success: {
247
+ description: 'Confirmation that the member was removed.',
248
+ schema: schema.removeMember.Response,
249
+ },
250
+ }),
251
+ summary: 'Remove member',
252
+ tags: ['Members'],
253
+ }),
254
+ async (c) => {
255
+ if (Auth.narrowAccess) return Auth.superAdminAccessError(c)
256
+ if (Auth.narrowScope) return Auth.ensureOrgError(c)
257
+ if (OpenApi.narrowValidation)
258
+ return OpenApi.validationError(c, {
259
+ code: 'param_invalid',
260
+ message: 'Check the path parameters and try again.',
261
+ })
262
+
263
+ const { userId } = c.req.valid('param')
264
+ // Owners remove anyone; everyone else only themself (leaving the org).
265
+ const caller = Auth.membership(c)
266
+ if (caller && caller.role !== 'owner' && caller.userId !== userId)
267
+ return Response.error(c, {
268
+ code: 'forbidden',
269
+ message: 'Requires the owner role',
270
+ status: 403,
271
+ })
272
+
273
+ const db = Db.get(c.get('db'))
274
+ try {
275
+ const result = await Memberships.remove(db, Auth.org(c).id, userId)
276
+ if (result === 'last_owner') return lastOwner(c)
277
+ if (result === 'not_found') return memberNotFound(c)
278
+ return c.json(Response.validated(schema.removeMember.Response, { userId }), 200)
279
+ } catch (cause) {
280
+ return Response.upstream(c, cause)
281
+ }
282
+ },
283
+ )
284
+ }
285
+
286
+ /** Projects a detailed membership row onto the member shape. */
287
+ function serializeMember(
288
+ record: Memberships.Record & { address: string | null; email: string | null },
289
+ ) {
290
+ return {
291
+ ...(record.address === null ? {} : { address: record.address }),
292
+ createdAt: record.createdAt,
293
+ ...(record.email === null ? {} : { email: record.email }),
294
+ role: record.role,
295
+ userId: record.userId,
296
+ }
297
+ }
298
+
299
+ function lastOwner(c: Context<App.Environment>) {
300
+ return Response.error(c, {
301
+ code: 'last_owner',
302
+ message: 'The organization would be left without an owner',
303
+ status: 409,
304
+ })
305
+ }
306
+
307
+ function memberNotFound(c: Context<App.Environment>) {
308
+ return Response.error(c, {
309
+ code: 'member_not_found',
310
+ message: 'Member not found',
311
+ status: 404,
312
+ })
313
+ }
@@ -0,0 +1,313 @@
1
+ import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts'
2
+
3
+ import * as TestApp from '../../../../test/App.js'
4
+ import * as Organizations from '../../../db/tables/organizations.js'
5
+ import * as Projects from '../../../db/tables/projects.js'
6
+ import type * as Log from '../../../internal/Log.js'
7
+ import * as Orgs from './orgs.js'
8
+
9
+ /** Origin pinned for SIWE domain binding; Hono test requests use this host. */
10
+ const origin = 'http://localhost'
11
+
12
+ /** Super admin secret configured on the test app. */
13
+ const secret = 'tempo:sk:b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1b1'
14
+
15
+ /** Wildcard-scoped key: full data access, still not a super admin. */
16
+ const wildcard = {
17
+ id: 'key_wildcard',
18
+ orgId: 'org_test',
19
+ scopes: ['*'],
20
+ token: 'secret_wildcard_key',
21
+ } satisfies TestApp.kvStore.Key
22
+
23
+ /** RequestInit presenting the given token. */
24
+ function as(token: string) {
25
+ return { headers: { 'tempo-api-key': token } } as const
26
+ }
27
+
28
+ /** RequestInit for a JSON mutation carrying the session cookie. */
29
+ function json(method: 'PATCH' | 'POST', cookie: string, body: unknown) {
30
+ return {
31
+ body: JSON.stringify(body),
32
+ headers: { 'content-type': 'application/json', cookie },
33
+ method,
34
+ }
35
+ }
36
+
37
+ function createApp(db = TestApp.database()) {
38
+ return TestApp.create({
39
+ auth: { keys: [TestApp.key, wildcard], superAdmin: { secret } },
40
+ db,
41
+ session: { wallet: { origin } },
42
+ })
43
+ }
44
+
45
+ /** App over a database seeded with two organizations created by the super admin. */
46
+ async function seeded() {
47
+ const db = TestApp.database()
48
+ await Organizations.create(db, { id: 'org_a', name: 'Org A' })
49
+ await Organizations.create(db, { id: 'org_b', name: 'Org B' })
50
+ return createApp(db)
51
+ }
52
+
53
+ /** Signs in a fresh scripted account; returns its session cookie. */
54
+ async function session(app: TestApp.signIn.App) {
55
+ const account = privateKeyToAccount(generatePrivateKey())
56
+ const { cookie } = await TestApp.signIn(app, account)
57
+ return cookie!
58
+ }
59
+
60
+ describe('GET /orgs', () => {
61
+ test('lists all organizations for the super admin', async () => {
62
+ const response = await (await seeded()).request('/v1/orgs', as(secret))
63
+ expect(response.status).toBe(200)
64
+ const body = await TestApp.json(response, Orgs.schema.listOrganizations.Response)
65
+ // Timestamps are nondeterministic; two same-millisecond rows make the
66
+ // newest-first order itself unstable, so sort by id before snapshotting.
67
+ const data = body.data
68
+ .map(({ id, name }) => ({ id, name }))
69
+ .sort((a, b) => a.id.localeCompare(b.id))
70
+ expect(data).toMatchInlineSnapshot(`
71
+ [
72
+ {
73
+ "id": "org_a",
74
+ "name": "Org A",
75
+ },
76
+ {
77
+ "id": "org_b",
78
+ "name": "Org B",
79
+ },
80
+ ]
81
+ `)
82
+ })
83
+
84
+ test('lists only owned organizations for a session', async () => {
85
+ const app = await seeded()
86
+ const cookie = await session(app)
87
+
88
+ const empty = await app.request('/v1/orgs', { headers: { cookie } })
89
+ expect(empty.status).toBe(200)
90
+ expect((await TestApp.json(empty, Orgs.schema.listOrganizations.Response)).data).toEqual([])
91
+
92
+ const created = await app.request('/v1/orgs', json('POST', cookie, { name: 'Acme' }))
93
+ expect(created.status).toBe(200)
94
+
95
+ const listed = await app.request('/v1/orgs', { headers: { cookie } })
96
+ const body = await TestApp.json(listed, Orgs.schema.listOrganizations.Response)
97
+ expect(body.data.map(({ name }) => name)).toEqual(['Acme'])
98
+ })
99
+
100
+ test('rejects missing credentials', async () => {
101
+ const response = await (await seeded()).request('/v1/orgs')
102
+ expect(response.status).toBe(401)
103
+ const { requestId, ...body } = (await response.json()) as { requestId?: string }
104
+ expect(requestId).toBeDefined()
105
+ expect(body).toMatchInlineSnapshot(`
106
+ {
107
+ "error": {
108
+ "code": "api_key_missing",
109
+ "message": "Missing API key",
110
+ },
111
+ }
112
+ `)
113
+ })
114
+
115
+ test('rejects API keys without the wildcard scope', async () => {
116
+ const response = await (await seeded()).request('/v1/orgs', as(TestApp.key.token))
117
+ expect(response.status).toBe(403)
118
+ })
119
+
120
+ test('rejects wildcard API keys', async () => {
121
+ const response = await (await seeded()).request('/v1/orgs', as(wildcard.token))
122
+ expect(response.status).toBe(403)
123
+ const { requestId, ...body } = (await response.json()) as { requestId?: string }
124
+ expect(requestId).toBeDefined()
125
+ expect(body).toMatchInlineSnapshot(`
126
+ {
127
+ "error": {
128
+ "code": "forbidden",
129
+ "message": "API key not permitted for this route",
130
+ },
131
+ }
132
+ `)
133
+ })
134
+
135
+ test('rejects the secret when super admin is unconfigured', async () => {
136
+ const client = TestApp.client({ db: TestApp.database() })
137
+ const response = await client.v1.orgs.$get({}, as(secret))
138
+ expect(response.status).toBe(401)
139
+ })
140
+
141
+ test('logs the super admin actor, never the secret', async () => {
142
+ const entries: Log.Entry[] = []
143
+ const client = TestApp.client({
144
+ auth: { superAdmin: { secret } },
145
+ db: TestApp.database(),
146
+ logger: (entry) => {
147
+ entries.push(entry)
148
+ },
149
+ })
150
+ await client.v1.orgs.$get({}, as(secret))
151
+
152
+ expect(entries[0]?.principal).toMatchInlineSnapshot(`
153
+ {
154
+ "actor": "super_admin",
155
+ "id": "super_admin",
156
+ "type": "super_admin",
157
+ }
158
+ `)
159
+ expect(JSON.stringify(entries)).not.toContain(secret)
160
+ })
161
+ })
162
+
163
+ describe('POST /orgs', () => {
164
+ test('creates a session-owned organization', async () => {
165
+ const app = createApp()
166
+ const cookie = await session(app)
167
+
168
+ const response = await app.request('/v1/orgs', json('POST', cookie, { name: 'Acme' }))
169
+ expect(response.status).toBe(200)
170
+ const body = await TestApp.json(response, Orgs.schema.Organization)
171
+ expect(body.id.startsWith('org_')).toBe(true)
172
+ expect(body.name).toBe('Acme')
173
+ })
174
+
175
+ test('creates a memberless org as the super admin', async () => {
176
+ const app = createApp()
177
+ const response = await app.request('/v1/orgs', {
178
+ body: JSON.stringify({ name: 'Ops' }),
179
+ headers: { 'content-type': 'application/json', 'tempo-api-key': secret },
180
+ method: 'POST',
181
+ })
182
+ expect(response.status).toBe(200)
183
+ const org = await TestApp.json(response, Orgs.schema.Organization)
184
+ expect(org.name).toBe('Ops')
185
+
186
+ // Memberless: invisible to sessions until an owner is seated.
187
+ const cookie = await session(app)
188
+ const foreign = await app.request(`/v1/orgs/${org.id}`, { headers: { cookie } })
189
+ expect(foreign.status).toBe(404)
190
+ const superAdmin = await app.request(`/v1/orgs/${org.id}`, as(secret))
191
+ expect(superAdmin.status).toBe(200)
192
+ })
193
+
194
+ test('rejects invalid bodies', async () => {
195
+ const app = createApp()
196
+ const cookie = await session(app)
197
+ const response = await app.request('/v1/orgs', json('POST', cookie, { name: '' }))
198
+ expect(response.status).toBe(400)
199
+ })
200
+ })
201
+
202
+ describe('GET /orgs/:orgId', () => {
203
+ test('hides foreign organizations behind 404', async () => {
204
+ const app = createApp()
205
+ const owner = await session(app)
206
+ const created = await app.request('/v1/orgs', json('POST', owner, { name: 'Acme' }))
207
+ const org = await TestApp.json(created, Orgs.schema.Organization)
208
+
209
+ const owned = await app.request(`/v1/orgs/${org.id}`, { headers: { cookie: owner } })
210
+ expect(owned.status).toBe(200)
211
+
212
+ const stranger = await session(app)
213
+ const foreign = await app.request(`/v1/orgs/${org.id}`, { headers: { cookie: stranger } })
214
+ expect(foreign.status).toBe(404)
215
+
216
+ const superAdmin = await app.request(`/v1/orgs/${org.id}`, as(secret))
217
+ expect(superAdmin.status).toBe(200)
218
+ })
219
+ })
220
+
221
+ describe('PATCH /orgs/:orgId', () => {
222
+ test('renames an owned organization', async () => {
223
+ const app = createApp()
224
+ const cookie = await session(app)
225
+ const created = await app.request('/v1/orgs', json('POST', cookie, { name: 'Acme' }))
226
+ const org = await TestApp.json(created, Orgs.schema.Organization)
227
+
228
+ const renamed = await app.request(`/v1/orgs/${org.id}`, json('PATCH', cookie, { name: 'Acme, Inc.' })) // prettier-ignore
229
+ expect(renamed.status).toBe(200)
230
+ expect((await TestApp.json(renamed, Orgs.schema.Organization)).name).toBe('Acme, Inc.')
231
+
232
+ const stranger = await session(app)
233
+ const foreign = await app.request(`/v1/orgs/${org.id}`, json('PATCH', stranger, { name: 'Hijack' })) // prettier-ignore
234
+ expect(foreign.status).toBe(404)
235
+ })
236
+ })
237
+
238
+ describe('DELETE /orgs/:orgId', () => {
239
+ test('deletes an owned organization and cascades its projects', async () => {
240
+ const db = TestApp.database()
241
+ const app = createApp(db)
242
+ const cookie = await session(app)
243
+ const created = await app.request('/v1/orgs', json('POST', cookie, { name: 'Acme' }))
244
+ const org = await TestApp.json(created, Orgs.schema.Organization)
245
+ const createdProject = await app.request(
246
+ `/v1/orgs/${org.id}/projects`,
247
+ json('POST', cookie, { name: 'Checkout' }),
248
+ )
249
+ expect(createdProject.status).toBe(200)
250
+ const project = (await createdProject.json()) as { id: string }
251
+
252
+ const deleted = await app.request(`/v1/orgs/${org.id}`, {
253
+ headers: { cookie },
254
+ method: 'DELETE',
255
+ })
256
+ expect(deleted.status).toBe(200)
257
+ expect(await TestApp.json(deleted, Orgs.schema.deleteOrganization.Response)).toEqual({
258
+ id: org.id,
259
+ })
260
+
261
+ const gone = await app.request(`/v1/orgs/${org.id}`, { headers: { cookie } })
262
+ expect(gone.status).toBe(404)
263
+ // Cascade: the org's project rows are deleted, not orphaned.
264
+ expect(await Projects.get(db, project.id)).toBeUndefined()
265
+ })
266
+
267
+ test('hides foreign organizations behind 404', async () => {
268
+ const app = createApp()
269
+ const owner = await session(app)
270
+ const created = await app.request('/v1/orgs', json('POST', owner, { name: 'Acme' }))
271
+ const org = await TestApp.json(created, Orgs.schema.Organization)
272
+
273
+ const stranger = await session(app)
274
+ const foreign = await app.request(`/v1/orgs/${org.id}`, {
275
+ headers: { cookie: stranger },
276
+ method: 'DELETE',
277
+ })
278
+ expect(foreign.status).toBe(404)
279
+
280
+ const still = await app.request(`/v1/orgs/${org.id}`, { headers: { cookie: owner } })
281
+ expect(still.status).toBe(200)
282
+ })
283
+
284
+ test('deletes any organization as the super admin', async () => {
285
+ const app = await seeded()
286
+ const response = await app.request('/v1/orgs/org_a', { ...as(secret), method: 'DELETE' })
287
+ expect(response.status).toBe(200)
288
+
289
+ const gone = await app.request('/v1/orgs/org_a', as(secret))
290
+ expect(gone.status).toBe(404)
291
+ })
292
+ })
293
+
294
+ describe('ownership', () => {
295
+ test('hides organizations without owning users from every session verb', async () => {
296
+ const app = await seeded()
297
+ const cookie = await session(app)
298
+
299
+ const read = await app.request('/v1/orgs/org_a', { headers: { cookie } })
300
+ expect(read.status).toBe(404)
301
+
302
+ const renamed = await app.request('/v1/orgs/org_a', json('PATCH', cookie, { name: 'Steal' }))
303
+ expect(renamed.status).toBe(404)
304
+
305
+ const deleted = await app.request('/v1/orgs/org_a', { headers: { cookie }, method: 'DELETE' })
306
+ expect(deleted.status).toBe(404)
307
+
308
+ // Untouched by the attempts, and still visible to the super admin.
309
+ const superAdmin = await app.request('/v1/orgs/org_a', as(secret))
310
+ expect(superAdmin.status).toBe(200)
311
+ expect((await TestApp.json(superAdmin, Orgs.schema.Organization)).name).toBe('Org A')
312
+ })
313
+ })