tapimo 0.0.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (517) hide show
  1. package/README.md +38 -16
  2. package/dist/ApiKey.d.ts +14 -11
  3. package/dist/ApiKey.d.ts.map +1 -1
  4. package/dist/ApiKey.js +11 -12
  5. package/dist/ApiKey.js.map +1 -1
  6. package/dist/ApiKeys.d.ts +207 -0
  7. package/dist/ApiKeys.d.ts.map +1 -0
  8. package/dist/ApiKeys.js +282 -0
  9. package/dist/ApiKeys.js.map +1 -0
  10. package/dist/App.d.ts +6771 -3819
  11. package/dist/App.d.ts.map +1 -1
  12. package/dist/App.js +145 -317
  13. package/dist/App.js.map +1 -1
  14. package/dist/Handler.d.ts +86 -0
  15. package/dist/Handler.d.ts.map +1 -0
  16. package/dist/Handler.js +83 -0
  17. package/dist/Handler.js.map +1 -0
  18. package/dist/Scope.d.ts +14 -4
  19. package/dist/Scope.d.ts.map +1 -1
  20. package/dist/Scope.js +16 -4
  21. package/dist/Scope.js.map +1 -1
  22. package/dist/Webhooks.d.ts +5 -7
  23. package/dist/Webhooks.d.ts.map +1 -1
  24. package/dist/Webhooks.js +11 -4
  25. package/dist/Webhooks.js.map +1 -1
  26. package/dist/admin/Access.d.ts +1 -3
  27. package/dist/admin/Access.d.ts.map +1 -1
  28. package/dist/admin/Access.js +1 -3
  29. package/dist/admin/Access.js.map +1 -1
  30. package/dist/admin/App.d.ts +167 -205
  31. package/dist/admin/App.d.ts.map +1 -1
  32. package/dist/admin/App.js +2 -4
  33. package/dist/admin/App.js.map +1 -1
  34. package/dist/admin/apps/api-keys.d.ts +105 -5
  35. package/dist/admin/apps/api-keys.d.ts.map +1 -1
  36. package/dist/admin/apps/api-keys.js +75 -9
  37. package/dist/admin/apps/api-keys.js.map +1 -1
  38. package/dist/admin/apps/scopes.d.ts +6 -0
  39. package/dist/admin/apps/scopes.d.ts.map +1 -1
  40. package/dist/admin/apps/verified-tokens.d.ts +35 -149
  41. package/dist/admin/apps/verified-tokens.d.ts.map +1 -1
  42. package/dist/admin/apps/verified-tokens.js +18 -39
  43. package/dist/admin/apps/verified-tokens.js.map +1 -1
  44. package/dist/admin/ui.generated.d.ts +1 -1
  45. package/dist/admin/ui.generated.js +1 -1
  46. package/dist/apps/data/App.d.ts +19176 -0
  47. package/dist/apps/data/App.d.ts.map +1 -0
  48. package/dist/apps/data/App.js +236 -0
  49. package/dist/apps/data/App.js.map +1 -0
  50. package/dist/apps/data/index.d.ts +3 -0
  51. package/dist/apps/data/index.d.ts.map +1 -0
  52. package/dist/apps/data/index.js +3 -0
  53. package/dist/apps/data/index.js.map +1 -0
  54. package/dist/apps/{activities.d.ts → data/routes/activities.d.ts} +32 -32
  55. package/dist/apps/{activities.d.ts.map → data/routes/activities.d.ts.map} +1 -1
  56. package/dist/apps/{activities.js → data/routes/activities.js} +13 -13
  57. package/dist/apps/data/routes/activities.js.map +1 -0
  58. package/dist/apps/{addresses.d.ts → data/routes/addresses.d.ts} +22 -22
  59. package/dist/apps/{addresses.d.ts.map → data/routes/addresses.d.ts.map} +1 -1
  60. package/dist/apps/{addresses.js → data/routes/addresses.js} +5 -5
  61. package/dist/apps/data/routes/addresses.js.map +1 -0
  62. package/dist/apps/{balances.d.ts → data/routes/balances.d.ts} +49 -49
  63. package/dist/apps/data/routes/balances.d.ts.map +1 -0
  64. package/dist/apps/{balances.js → data/routes/balances.js} +42 -11
  65. package/dist/apps/data/routes/balances.js.map +1 -0
  66. package/dist/apps/{blocks.d.ts → data/routes/blocks.d.ts} +47 -47
  67. package/dist/apps/data/routes/blocks.d.ts.map +1 -0
  68. package/dist/apps/{blocks.js → data/routes/blocks.js} +13 -13
  69. package/dist/apps/data/routes/blocks.js.map +1 -0
  70. package/dist/apps/{coingecko.d.ts → data/routes/coingecko.d.ts} +183 -183
  71. package/dist/apps/data/routes/coingecko.d.ts.map +1 -0
  72. package/dist/apps/{coingecko.js → data/routes/coingecko.js} +13 -13
  73. package/dist/apps/data/routes/coingecko.js.map +1 -0
  74. package/dist/apps/{exchanges.d.ts → data/routes/exchanges.d.ts} +460 -460
  75. package/dist/apps/data/routes/exchanges.d.ts.map +1 -0
  76. package/dist/apps/{exchanges.js → data/routes/exchanges.js} +19 -19
  77. package/dist/apps/data/routes/exchanges.js.map +1 -0
  78. package/dist/apps/{fee-amm.d.ts → data/routes/fee-amm.d.ts} +94 -94
  79. package/dist/apps/data/routes/fee-amm.d.ts.map +1 -0
  80. package/dist/apps/{fee-amm.js → data/routes/fee-amm.js} +17 -17
  81. package/dist/apps/data/routes/fee-amm.js.map +1 -0
  82. package/dist/apps/{indexer.d.ts → data/routes/indexer.d.ts} +4 -4
  83. package/dist/apps/data/routes/indexer.d.ts.map +1 -0
  84. package/dist/apps/{indexer.js → data/routes/indexer.js} +13 -9
  85. package/dist/apps/data/routes/indexer.js.map +1 -0
  86. package/dist/apps/{receipts.d.ts → data/routes/receipts.d.ts} +6 -6
  87. package/dist/apps/data/routes/receipts.d.ts.map +1 -0
  88. package/dist/apps/{receipts.js → data/routes/receipts.js} +12 -12
  89. package/dist/apps/data/routes/receipts.js.map +1 -0
  90. package/dist/apps/{rpc.d.ts → data/routes/rpc.d.ts} +4 -4
  91. package/dist/apps/data/routes/rpc.d.ts.map +1 -0
  92. package/dist/apps/{rpc.js → data/routes/rpc.js} +6 -6
  93. package/dist/apps/data/routes/rpc.js.map +1 -0
  94. package/dist/apps/{tokenlist.d.ts → data/routes/tokenlist.d.ts} +4 -4
  95. package/dist/apps/data/routes/tokenlist.d.ts.map +1 -0
  96. package/dist/apps/{tokenlist.js → data/routes/tokenlist.js} +8 -8
  97. package/dist/apps/data/routes/tokenlist.js.map +1 -0
  98. package/dist/apps/{tokens.d.ts → data/routes/tokens.d.ts} +11 -11
  99. package/dist/apps/data/routes/tokens.d.ts.map +1 -0
  100. package/dist/apps/{tokens.js → data/routes/tokens.js} +19 -19
  101. package/dist/apps/data/routes/tokens.js.map +1 -0
  102. package/dist/apps/{transactions.d.ts → data/routes/transactions.d.ts} +288 -288
  103. package/dist/apps/data/routes/transactions.d.ts.map +1 -0
  104. package/dist/apps/{transactions.js → data/routes/transactions.js} +13 -13
  105. package/dist/apps/data/routes/transactions.js.map +1 -0
  106. package/dist/apps/{transfers.d.ts → data/routes/transfers.d.ts} +35 -35
  107. package/dist/apps/data/routes/transfers.d.ts.map +1 -0
  108. package/dist/apps/{transfers.js → data/routes/transfers.js} +13 -13
  109. package/dist/apps/data/routes/transfers.js.map +1 -0
  110. package/dist/apps/{verified-tokens.d.ts → data/routes/verified-tokens.d.ts} +42 -42
  111. package/dist/apps/data/routes/verified-tokens.d.ts.map +1 -0
  112. package/dist/apps/{verified-tokens.js → data/routes/verified-tokens.js} +9 -9
  113. package/dist/apps/data/routes/verified-tokens.js.map +1 -0
  114. package/dist/apps/{webhooks.d.ts → data/routes/webhooks.d.ts} +66 -63
  115. package/dist/apps/{webhooks.d.ts.map → data/routes/webhooks.d.ts.map} +1 -1
  116. package/dist/apps/{webhooks.js → data/routes/webhooks.js} +67 -59
  117. package/dist/apps/data/routes/webhooks.js.map +1 -0
  118. package/dist/apps/docs/App.d.ts +27 -0
  119. package/dist/apps/docs/App.d.ts.map +1 -0
  120. package/dist/apps/docs/App.js +122 -0
  121. package/dist/apps/docs/App.js.map +1 -0
  122. package/dist/{internal/OpenApi.fonts.d.ts → apps/docs/fonts.d.ts} +1 -1
  123. package/dist/apps/docs/fonts.d.ts.map +1 -0
  124. package/dist/{internal/OpenApi.fonts.js → apps/docs/fonts.js} +1 -1
  125. package/dist/apps/docs/fonts.js.map +1 -0
  126. package/dist/apps/docs/index.d.ts +2 -0
  127. package/dist/apps/docs/index.d.ts.map +1 -0
  128. package/dist/apps/docs/index.js +2 -0
  129. package/dist/apps/docs/index.js.map +1 -0
  130. package/dist/{internal/OpenApi.logo.d.ts → apps/docs/logo.d.ts} +1 -1
  131. package/dist/apps/docs/logo.d.ts.map +1 -0
  132. package/dist/{internal/OpenApi.logo.js → apps/docs/logo.js} +1 -1
  133. package/dist/apps/docs/logo.js.map +1 -0
  134. package/dist/apps/index.d.ts +4 -0
  135. package/dist/apps/index.d.ts.map +1 -0
  136. package/dist/apps/index.js +4 -0
  137. package/dist/apps/index.js.map +1 -0
  138. package/dist/apps/metadata.d.ts +7 -0
  139. package/dist/apps/metadata.d.ts.map +1 -0
  140. package/dist/apps/metadata.js +15 -0
  141. package/dist/apps/metadata.js.map +1 -0
  142. package/dist/apps/platform/App.d.ts +2985 -0
  143. package/dist/apps/platform/App.d.ts.map +1 -0
  144. package/dist/apps/platform/App.js +74 -0
  145. package/dist/apps/platform/App.js.map +1 -0
  146. package/dist/apps/platform/index.d.ts +2 -0
  147. package/dist/apps/platform/index.d.ts.map +1 -0
  148. package/dist/apps/platform/index.js +2 -0
  149. package/dist/apps/platform/index.js.map +1 -0
  150. package/dist/apps/platform/routes/api-keys.d.ts +681 -0
  151. package/dist/apps/platform/routes/api-keys.d.ts.map +1 -0
  152. package/dist/apps/platform/routes/api-keys.js +328 -0
  153. package/dist/apps/platform/routes/api-keys.js.map +1 -0
  154. package/dist/apps/platform/routes/invitations.d.ts +733 -0
  155. package/dist/apps/platform/routes/invitations.d.ts.map +1 -0
  156. package/dist/apps/platform/routes/invitations.js +413 -0
  157. package/dist/apps/platform/routes/invitations.js.map +1 -0
  158. package/dist/apps/platform/routes/me.d.ts +95 -0
  159. package/dist/apps/platform/routes/me.d.ts.map +1 -0
  160. package/dist/apps/platform/routes/me.js +81 -0
  161. package/dist/apps/platform/routes/me.js.map +1 -0
  162. package/dist/apps/platform/routes/members.d.ts +554 -0
  163. package/dist/apps/platform/routes/members.d.ts.map +1 -0
  164. package/dist/apps/platform/routes/members.js +251 -0
  165. package/dist/apps/platform/routes/members.js.map +1 -0
  166. package/dist/apps/platform/routes/orgs.d.ts +582 -0
  167. package/dist/apps/platform/routes/orgs.d.ts.map +1 -0
  168. package/dist/apps/platform/routes/orgs.js +265 -0
  169. package/dist/apps/platform/routes/orgs.js.map +1 -0
  170. package/dist/apps/platform/routes/projects.d.ts +740 -0
  171. package/dist/apps/platform/routes/projects.d.ts.map +1 -0
  172. package/dist/apps/platform/routes/projects.js +286 -0
  173. package/dist/apps/platform/routes/projects.js.map +1 -0
  174. package/dist/apps/relay/App.d.ts +41 -0
  175. package/dist/apps/relay/App.d.ts.map +1 -0
  176. package/dist/apps/relay/App.js +103 -0
  177. package/dist/apps/relay/App.js.map +1 -0
  178. package/dist/apps/relay/Sponsorships.d.ts +107 -0
  179. package/dist/apps/relay/Sponsorships.d.ts.map +1 -0
  180. package/dist/apps/relay/Sponsorships.js +193 -0
  181. package/dist/apps/relay/Sponsorships.js.map +1 -0
  182. package/dist/apps/relay/index.d.ts +3 -0
  183. package/dist/apps/relay/index.d.ts.map +1 -0
  184. package/dist/apps/relay/index.js +3 -0
  185. package/dist/apps/relay/index.js.map +1 -0
  186. package/dist/{Cli.d.ts → cli/Cli.d.ts} +13 -2
  187. package/dist/cli/Cli.d.ts.map +1 -0
  188. package/dist/cli/Cli.js +19 -0
  189. package/dist/cli/Cli.js.map +1 -0
  190. package/dist/cli/bin.d.ts +3 -0
  191. package/dist/cli/bin.d.ts.map +1 -0
  192. package/dist/cli/bin.js +4 -0
  193. package/dist/cli/bin.js.map +1 -0
  194. package/dist/cli/commands/admin.d.ts +15 -0
  195. package/dist/cli/commands/admin.d.ts.map +1 -0
  196. package/dist/cli/commands/admin.js +60 -0
  197. package/dist/cli/commands/admin.js.map +1 -0
  198. package/dist/cloudflare.d.ts +7 -0
  199. package/dist/cloudflare.d.ts.map +1 -1
  200. package/dist/cloudflare.js +9 -0
  201. package/dist/cloudflare.js.map +1 -1
  202. package/dist/db/Db.d.ts +113 -0
  203. package/dist/db/Db.d.ts.map +1 -0
  204. package/dist/db/Db.js +402 -0
  205. package/dist/db/Db.js.map +1 -0
  206. package/dist/db/Schema.d.ts +168 -0
  207. package/dist/db/Schema.d.ts.map +1 -0
  208. package/dist/db/Schema.js +229 -0
  209. package/dist/db/Schema.js.map +1 -0
  210. package/dist/db/tables/invitations.d.ts +84 -0
  211. package/dist/db/tables/invitations.d.ts.map +1 -0
  212. package/dist/db/tables/invitations.js +165 -0
  213. package/dist/db/tables/invitations.js.map +1 -0
  214. package/dist/db/tables/memberships.d.ts +108 -0
  215. package/dist/db/tables/memberships.d.ts.map +1 -0
  216. package/dist/db/tables/memberships.js +153 -0
  217. package/dist/db/tables/memberships.js.map +1 -0
  218. package/dist/db/tables/organizations.d.ts +123 -0
  219. package/dist/db/tables/organizations.d.ts.map +1 -0
  220. package/dist/db/tables/organizations.js +170 -0
  221. package/dist/db/tables/organizations.js.map +1 -0
  222. package/dist/db/tables/projects.d.ts +65 -0
  223. package/dist/db/tables/projects.d.ts.map +1 -0
  224. package/dist/db/tables/projects.js +75 -0
  225. package/dist/db/tables/projects.js.map +1 -0
  226. package/dist/db/tables/sponsoredTransactions.d.ts +120 -0
  227. package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -0
  228. package/dist/db/tables/sponsoredTransactions.js +161 -0
  229. package/dist/db/tables/sponsoredTransactions.js.map +1 -0
  230. package/dist/db/tables/users.d.ts +48 -0
  231. package/dist/db/tables/users.d.ts.map +1 -0
  232. package/dist/db/tables/users.js +65 -0
  233. package/dist/db/tables/users.js.map +1 -0
  234. package/dist/db/tables/verifiedTokens.d.ts +53 -0
  235. package/dist/db/tables/verifiedTokens.d.ts.map +1 -0
  236. package/dist/db/tables/verifiedTokens.js +74 -0
  237. package/dist/db/tables/verifiedTokens.js.map +1 -0
  238. package/dist/db/tables/webhookDeliveries.d.ts +71 -0
  239. package/dist/db/tables/webhookDeliveries.d.ts.map +1 -0
  240. package/dist/db/tables/webhookDeliveries.js +119 -0
  241. package/dist/db/tables/webhookDeliveries.js.map +1 -0
  242. package/dist/db/tables/webhookSubscriptions.d.ts +158 -0
  243. package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -0
  244. package/dist/db/tables/webhookSubscriptions.js +272 -0
  245. package/dist/db/tables/webhookSubscriptions.js.map +1 -0
  246. package/dist/handlers/internal/executionError.d.ts +25 -0
  247. package/dist/handlers/internal/executionError.d.ts.map +1 -0
  248. package/dist/handlers/internal/executionError.js +186 -0
  249. package/dist/handlers/internal/executionError.js.map +1 -0
  250. package/dist/handlers/internal/multisig.d.ts +233 -0
  251. package/dist/handlers/internal/multisig.d.ts.map +1 -0
  252. package/dist/handlers/internal/multisig.js +542 -0
  253. package/dist/handlers/internal/multisig.js.map +1 -0
  254. package/dist/handlers/internal/requestListener.d.ts +124 -0
  255. package/dist/handlers/internal/requestListener.d.ts.map +1 -0
  256. package/dist/handlers/internal/requestListener.js +173 -0
  257. package/dist/handlers/internal/requestListener.js.map +1 -0
  258. package/dist/handlers/internal/sponsorship.d.ts +113 -0
  259. package/dist/handlers/internal/sponsorship.d.ts.map +1 -0
  260. package/dist/handlers/internal/sponsorship.js +128 -0
  261. package/dist/handlers/internal/sponsorship.js.map +1 -0
  262. package/dist/handlers/internal/types.d.ts +284 -0
  263. package/dist/handlers/internal/types.d.ts.map +1 -0
  264. package/dist/handlers/internal/types.js +2 -0
  265. package/dist/handlers/internal/types.js.map +1 -0
  266. package/dist/handlers/internal/utils.d.ts +34 -0
  267. package/dist/handlers/internal/utils.d.ts.map +1 -0
  268. package/dist/handlers/internal/utils.js +131 -0
  269. package/dist/handlers/internal/utils.js.map +1 -0
  270. package/dist/handlers/relay.d.ts +123 -0
  271. package/dist/handlers/relay.d.ts.map +1 -0
  272. package/dist/handlers/relay.js +1278 -0
  273. package/dist/handlers/relay.js.map +1 -0
  274. package/dist/index.d.ts +4 -2
  275. package/dist/index.d.ts.map +1 -1
  276. package/dist/index.js +4 -2
  277. package/dist/index.js.map +1 -1
  278. package/dist/internal/Auth.d.ts +224 -16
  279. package/dist/internal/Auth.d.ts.map +1 -1
  280. package/dist/internal/Auth.js +572 -33
  281. package/dist/internal/Auth.js.map +1 -1
  282. package/dist/internal/Log.d.ts +7 -3
  283. package/dist/internal/Log.d.ts.map +1 -1
  284. package/dist/internal/Log.js +4 -0
  285. package/dist/internal/Log.js.map +1 -1
  286. package/dist/internal/OpenApi.d.ts +0 -19
  287. package/dist/internal/OpenApi.d.ts.map +1 -1
  288. package/dist/internal/OpenApi.js +0 -57
  289. package/dist/internal/OpenApi.js.map +1 -1
  290. package/dist/internal/RpcSchema.d.ts +10 -0
  291. package/dist/internal/RpcSchema.d.ts.map +1 -0
  292. package/dist/internal/RpcSchema.js +2 -0
  293. package/dist/internal/RpcSchema.js.map +1 -0
  294. package/dist/internal/Store.d.ts +49 -4
  295. package/dist/internal/Store.d.ts.map +1 -1
  296. package/dist/internal/Store.js +81 -5
  297. package/dist/internal/Store.js.map +1 -1
  298. package/dist/internal/VerifiedTokens.d.ts +15 -34
  299. package/dist/internal/VerifiedTokens.d.ts.map +1 -1
  300. package/dist/internal/VerifiedTokens.js +115 -159
  301. package/dist/internal/VerifiedTokens.js.map +1 -1
  302. package/dist/internal/Viem.d.ts +84 -0
  303. package/dist/internal/Viem.d.ts.map +1 -1
  304. package/dist/internal/Webhooks.d.ts +50 -36
  305. package/dist/internal/Webhooks.d.ts.map +1 -1
  306. package/dist/internal/Webhooks.js +92 -205
  307. package/dist/internal/Webhooks.js.map +1 -1
  308. package/package.json +37 -6
  309. package/src/ApiKey.test.ts +3 -3
  310. package/src/ApiKey.ts +11 -12
  311. package/src/ApiKeys.test.ts +278 -0
  312. package/src/ApiKeys.ts +358 -0
  313. package/src/App.test-d.ts +1 -1
  314. package/src/App.test.ts +40508 -19083
  315. package/src/App.ts +239 -416
  316. package/src/Client.test-d.ts +307 -16
  317. package/src/Handler.ts +154 -0
  318. package/src/Scope.ts +17 -4
  319. package/src/Webhooks.test.ts +8 -7
  320. package/src/Webhooks.ts +13 -7
  321. package/src/admin/Access.ts +1 -3
  322. package/src/admin/App.ts +19 -24
  323. package/src/admin/apps/api-keys.test.ts +70 -18
  324. package/src/admin/apps/api-keys.ts +92 -9
  325. package/src/admin/apps/scopes.test.ts +8 -0
  326. package/src/admin/apps/verified-tokens.test.ts +3 -30
  327. package/src/admin/apps/verified-tokens.ts +18 -37
  328. package/src/admin/ui.generated.ts +1 -1
  329. package/src/apps/composition.test.ts +66 -0
  330. package/src/apps/data/App.ts +295 -0
  331. package/src/apps/data/index.ts +2 -0
  332. package/src/apps/{activities.test.ts → data/routes/activities.test.ts} +3 -3
  333. package/src/apps/{activities.ts → data/routes/activities.ts} +15 -15
  334. package/src/apps/{addresses.ts → data/routes/addresses.ts} +6 -6
  335. package/src/apps/{balances.test.ts → data/routes/balances.test.ts} +6 -5
  336. package/src/apps/{balances.ts → data/routes/balances.ts} +46 -12
  337. package/src/apps/{blocks.test.ts → data/routes/blocks.test.ts} +4 -4
  338. package/src/apps/{blocks.ts → data/routes/blocks.ts} +14 -14
  339. package/src/apps/{coingecko.test.ts → data/routes/coingecko.test.ts} +2 -2
  340. package/src/apps/{coingecko.ts → data/routes/coingecko.ts} +14 -14
  341. package/src/apps/{exchanges.test.ts → data/routes/exchanges.test.ts} +2 -2
  342. package/src/apps/{exchanges.ts → data/routes/exchanges.ts} +20 -20
  343. package/src/apps/{fee-amm.test.ts → data/routes/fee-amm.test.ts} +3 -3
  344. package/src/apps/{fee-amm.ts → data/routes/fee-amm.ts} +18 -18
  345. package/src/apps/{indexer.test.ts → data/routes/indexer.test.ts} +27 -6
  346. package/src/apps/{indexer.ts → data/routes/indexer.ts} +13 -10
  347. package/src/apps/{receipts.test.ts → data/routes/receipts.test.ts} +4 -4
  348. package/src/apps/{receipts.ts → data/routes/receipts.ts} +13 -13
  349. package/src/apps/{rpc.test.ts → data/routes/rpc.test.ts} +142 -13
  350. package/src/apps/{rpc.ts → data/routes/rpc.ts} +7 -7
  351. package/src/apps/{tokenlist.test.ts → data/routes/tokenlist.test.ts} +8 -10
  352. package/src/apps/{tokenlist.ts → data/routes/tokenlist.ts} +9 -9
  353. package/src/apps/{tokens.test.ts → data/routes/tokens.test.ts} +6 -5
  354. package/src/apps/{tokens.ts → data/routes/tokens.ts} +20 -20
  355. package/src/apps/{transactions.test.ts → data/routes/transactions.test.ts} +4 -4
  356. package/src/apps/{transactions.ts → data/routes/transactions.ts} +15 -15
  357. package/src/apps/{transfers.test.ts → data/routes/transfers.test.ts} +3 -3
  358. package/src/apps/{transfers.ts → data/routes/transfers.ts} +15 -15
  359. package/src/apps/{verified-tokens.test.ts → data/routes/verified-tokens.test.ts} +27 -15
  360. package/src/apps/{verified-tokens.ts → data/routes/verified-tokens.ts} +10 -10
  361. package/src/apps/{webhooks.test.ts → data/routes/webhooks.test.ts} +137 -150
  362. package/src/apps/{webhooks.ts → data/routes/webhooks.ts} +83 -81
  363. package/src/apps/docs/App.ts +136 -0
  364. package/src/apps/docs/index.ts +1 -0
  365. package/src/apps/index.ts +3 -0
  366. package/src/apps/metadata.ts +23 -0
  367. package/src/apps/platform/App.ts +130 -0
  368. package/src/apps/platform/index.ts +1 -0
  369. package/src/apps/platform/routes/api-keys.test.ts +330 -0
  370. package/src/apps/platform/routes/api-keys.ts +427 -0
  371. package/src/apps/platform/routes/invitations.test.ts +361 -0
  372. package/src/apps/platform/routes/invitations.ts +519 -0
  373. package/src/apps/platform/routes/me.test.ts +121 -0
  374. package/src/apps/platform/routes/me.ts +106 -0
  375. package/src/apps/platform/routes/members.test.ts +195 -0
  376. package/src/apps/platform/routes/members.ts +313 -0
  377. package/src/apps/platform/routes/orgs.test.ts +313 -0
  378. package/src/apps/platform/routes/orgs.ts +343 -0
  379. package/src/apps/platform/routes/projects.test.ts +181 -0
  380. package/src/apps/platform/routes/projects.ts +367 -0
  381. package/src/apps/relay/App.test.ts +484 -0
  382. package/src/apps/relay/App.ts +128 -0
  383. package/src/apps/relay/Sponsorships.test.ts +375 -0
  384. package/src/apps/relay/Sponsorships.ts +305 -0
  385. package/src/apps/relay/index.ts +2 -0
  386. package/src/{Cli.test.ts → cli/Cli.test.ts} +10 -9
  387. package/src/{Cli.ts → cli/Cli.ts} +16 -9
  388. package/src/cli/bin.ts +4 -0
  389. package/src/cli/commands/admin.test.ts +71 -0
  390. package/src/cli/commands/admin.ts +60 -0
  391. package/src/cloudflare.ts +10 -0
  392. package/src/db/Db.test.ts +84 -0
  393. package/src/db/Db.ts +542 -0
  394. package/src/db/Schema.ts +301 -0
  395. package/src/db/tables/invitations.ts +198 -0
  396. package/src/db/tables/memberships.ts +208 -0
  397. package/src/db/tables/organizations.test.ts +160 -0
  398. package/src/db/tables/organizations.ts +232 -0
  399. package/src/db/tables/projects.ts +107 -0
  400. package/src/db/tables/sponsoredTransactions.test.ts +218 -0
  401. package/src/db/tables/sponsoredTransactions.ts +236 -0
  402. package/src/db/tables/users.ts +85 -0
  403. package/src/db/tables/verifiedTokens.ts +111 -0
  404. package/src/db/tables/webhookDeliveries.ts +159 -0
  405. package/src/db/tables/webhookSubscriptions.ts +358 -0
  406. package/src/handlers/internal/executionError.ts +207 -0
  407. package/src/handlers/internal/multisig.test.ts +818 -0
  408. package/src/handlers/internal/multisig.ts +823 -0
  409. package/src/handlers/internal/requestListener.ts +273 -0
  410. package/src/handlers/internal/sponsorship.ts +245 -0
  411. package/src/handlers/internal/types.ts +378 -0
  412. package/src/handlers/internal/utils.ts +153 -0
  413. package/src/handlers/relay.test.ts +2035 -0
  414. package/src/handlers/relay.ts +1623 -0
  415. package/src/index.ts +4 -2
  416. package/src/internal/Auth.test.ts +337 -21
  417. package/src/internal/Auth.ts +783 -44
  418. package/src/internal/Cursor.test.ts +3 -2
  419. package/src/internal/Log.test.ts +1 -1
  420. package/src/internal/Log.ts +11 -3
  421. package/src/internal/MetricSink.ts +1 -1
  422. package/src/internal/OpenApi.ts +0 -59
  423. package/src/internal/RateLimit.test.ts +1 -1
  424. package/src/internal/RpcSchema.ts +10 -0
  425. package/src/internal/Store.test.ts +150 -10
  426. package/src/internal/Store.ts +136 -11
  427. package/src/internal/Tidx.local.test.ts +1 -1
  428. package/src/internal/VerifiedTokens.test.ts +99 -115
  429. package/src/internal/VerifiedTokens.ts +123 -175
  430. package/src/internal/WebhookDestination.test.ts +1 -1
  431. package/src/internal/Webhooks.test.ts +111 -111
  432. package/src/internal/Webhooks.ts +103 -221
  433. package/dist/ApiKeySource.d.ts +0 -166
  434. package/dist/ApiKeySource.d.ts.map +0 -1
  435. package/dist/ApiKeySource.js +0 -234
  436. package/dist/ApiKeySource.js.map +0 -1
  437. package/dist/Cli.d.ts.map +0 -1
  438. package/dist/Cli.js +0 -13
  439. package/dist/Cli.js.map +0 -1
  440. package/dist/apps/activities.js.map +0 -1
  441. package/dist/apps/addresses.js.map +0 -1
  442. package/dist/apps/balances.d.ts.map +0 -1
  443. package/dist/apps/balances.js.map +0 -1
  444. package/dist/apps/blocks.d.ts.map +0 -1
  445. package/dist/apps/blocks.js.map +0 -1
  446. package/dist/apps/coingecko.d.ts.map +0 -1
  447. package/dist/apps/coingecko.js.map +0 -1
  448. package/dist/apps/exchanges.d.ts.map +0 -1
  449. package/dist/apps/exchanges.js.map +0 -1
  450. package/dist/apps/fee-amm.d.ts.map +0 -1
  451. package/dist/apps/fee-amm.js.map +0 -1
  452. package/dist/apps/indexer.d.ts.map +0 -1
  453. package/dist/apps/indexer.js.map +0 -1
  454. package/dist/apps/receipts.d.ts.map +0 -1
  455. package/dist/apps/receipts.js.map +0 -1
  456. package/dist/apps/rpc.d.ts.map +0 -1
  457. package/dist/apps/rpc.js.map +0 -1
  458. package/dist/apps/tokenlist.d.ts.map +0 -1
  459. package/dist/apps/tokenlist.js.map +0 -1
  460. package/dist/apps/tokens.d.ts.map +0 -1
  461. package/dist/apps/tokens.js.map +0 -1
  462. package/dist/apps/transactions.d.ts.map +0 -1
  463. package/dist/apps/transactions.js.map +0 -1
  464. package/dist/apps/transfers.d.ts.map +0 -1
  465. package/dist/apps/transfers.js.map +0 -1
  466. package/dist/apps/verified-tokens.d.ts.map +0 -1
  467. package/dist/apps/verified-tokens.js.map +0 -1
  468. package/dist/apps/webhooks.js.map +0 -1
  469. package/dist/internal/OpenApi.fonts.d.ts.map +0 -1
  470. package/dist/internal/OpenApi.fonts.js.map +0 -1
  471. package/dist/internal/OpenApi.logo.d.ts.map +0 -1
  472. package/dist/internal/OpenApi.logo.js.map +0 -1
  473. package/seeds/31318/icons/0x20c0000000000000000000000000000000000000.svg +0 -1
  474. package/seeds/31318/icons/0x20c0000000000000000000000000000000000001.svg +0 -1
  475. package/seeds/31318/icons/0x20c0000000000000000000000000000000000002.svg +0 -1
  476. package/seeds/31318/icons/0x20c0000000000000000000000000000000000003.svg +0 -1
  477. package/seeds/31318/verified-tokens.json +0 -30
  478. package/seeds/4217/icons/0x20c0000000000000000000000000000000000000.svg +0 -25
  479. package/seeds/4217/icons/0x20c000000000000000000000048c8f36df1c9a4a.svg +0 -1
  480. package/seeds/4217/icons/0x20c0000000000000000000000520792dcccccccc.svg +0 -1
  481. package/seeds/4217/icons/0x20c0000000000000000000000a6da882d075a4c3.svg +0 -4
  482. package/seeds/4217/icons/0x20c000000000000000000000111111111e910f0f.svg +0 -30
  483. package/seeds/4217/icons/0x20c00000000000000000000014f22ca97301eb73.svg +0 -1
  484. package/seeds/4217/icons/0x20c0000000000000000000001621e21f71cf12fb.svg +0 -1
  485. package/seeds/4217/icons/0x20c0000000000000000000002f52d5cc21a3207b.svg +0 -1
  486. package/seeds/4217/icons/0x20c0000000000000000000003158081efd85bfc2.svg +0 -11
  487. package/seeds/4217/icons/0x20c0000000000000000000003554d28269e0f3c2.svg +0 -1
  488. package/seeds/4217/icons/0x20c000000000000000000000383a23bacb546ab9.svg +0 -1
  489. package/seeds/4217/icons/0x20c00000000000000000000042109aef2f8b28e1.png +0 -0
  490. package/seeds/4217/icons/0x20c0000000000000000000005c0bac7cef389a11.svg +0 -1
  491. package/seeds/4217/icons/0x20c0000000000000000000006fd9a167923ba194.png +0 -0
  492. package/seeds/4217/icons/0x20c0000000000000000000007f7ba549dd0251b9.svg +0 -1
  493. package/seeds/4217/icons/0x20c0000000000000000000008191667423f70e67.svg +0 -5
  494. package/seeds/4217/icons/0x20c0000000000000000000008ee4fcff88888888.svg +0 -1
  495. package/seeds/4217/icons/0x20c0000000000000000000009a4a4b17e0dc6651.svg +0 -1
  496. package/seeds/4217/icons/0x20c000000000000000000000ab02d39df30bd17e.svg +0 -1
  497. package/seeds/4217/icons/0x20c000000000000000000000ae247a1130450f09.svg +0 -1
  498. package/seeds/4217/icons/0x20c000000000000000000000aeed2ec36a54d0e5.svg +0 -1
  499. package/seeds/4217/icons/0x20c000000000000000000000b9537d11c60e8b50.svg +0 -1
  500. package/seeds/4217/icons/0x20c000000000000000000000bd95bfb69fbe6ce3.svg +0 -1
  501. package/seeds/4217/icons/0x20c000000000000000000000c412ec89d0c08be5.svg +0 -1
  502. package/seeds/4217/icons/0x20c000000000000000000000f047dd7018e50367.png +0 -0
  503. package/seeds/4217/verified-tokens.json +0 -177
  504. package/seeds/42431/icons/0x20c0000000000000000000000000000000000000.svg +0 -25
  505. package/seeds/42431/icons/0x20c0000000000000000000000000000000000001.svg +0 -1
  506. package/seeds/42431/icons/0x20c0000000000000000000000000000000000002.svg +0 -1
  507. package/seeds/42431/icons/0x20c0000000000000000000000000000000000003.svg +0 -1
  508. package/seeds/42431/icons/0x20c0000000000000000000009e8d7eb59b783726.svg +0 -1
  509. package/seeds/42431/icons/0x20c000000000000000000000d72572838bbee59c.svg +0 -1
  510. package/seeds/42431/verified-tokens.json +0 -44
  511. package/seeds/openrpc/README.md +0 -27
  512. package/seeds/openrpc/execution-apis.json +0 -8989
  513. package/src/ApiKeySource.test-d.ts +0 -11
  514. package/src/ApiKeySource.test.ts +0 -295
  515. package/src/ApiKeySource.ts +0 -303
  516. /package/src/{internal/OpenApi.fonts.ts → apps/docs/fonts.ts} +0 -0
  517. /package/src/{internal/OpenApi.logo.ts → apps/docs/logo.ts} +0 -0
@@ -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
+ })
@@ -0,0 +1,343 @@
1
+ import { type Context, Hono } from 'hono'
2
+ import * as z from 'zod/mini'
3
+
4
+ import * as ApiKeys from '../../../ApiKeys.js'
5
+ import type * as App from '../../../App.js'
6
+ import * as Auth from '../../../internal/Auth.js'
7
+ import * as Db from '../../../db/Db.js'
8
+ import * as OpenApi from '../../../internal/OpenApi.js'
9
+ import * as Organizations from '../../../db/tables/organizations.js'
10
+ import * as Response from '../../../internal/Response.js'
11
+ import * as Schema from '../../../internal/Schema.js'
12
+
13
+ /** Zod schemas owned by the organizations resource. */
14
+ export namespace schema {
15
+ const name = z
16
+ .string()
17
+ .check(
18
+ z.minLength(1),
19
+ z.maxLength(100),
20
+ z.describe('Human-readable name.'),
21
+ z.meta({ examples: ['Acme, Inc.'] }),
22
+ )
23
+
24
+ /** One organization: the top-level tenant that owns API keys. */
25
+ export const Organization = Schema.describe(
26
+ z.object({
27
+ createdAt: z
28
+ .string()
29
+ .check(
30
+ z.describe('When the organization was created (ISO 8601).'),
31
+ z.meta({ examples: ['2026-01-01T00:00:00.000Z'] }),
32
+ ),
33
+ id: z
34
+ .string()
35
+ .check(
36
+ z.describe('Opaque organization id (`org_…`).'),
37
+ z.meta({ examples: ['org_1a2b3c4d5e6f7g8h9j0k1m2n'] }),
38
+ ),
39
+ name: z
40
+ .string()
41
+ .check(
42
+ z.describe('Human-readable organization name.'),
43
+ z.meta({ examples: ['Acme, Inc.'] }),
44
+ ),
45
+ updatedAt: z
46
+ .string()
47
+ .check(
48
+ z.describe('When the organization was last updated (ISO 8601).'),
49
+ z.meta({ examples: ['2026-01-01T00:00:00.000Z'] }),
50
+ ),
51
+ }),
52
+ 'An organization: the top-level tenant that owns API keys.',
53
+ )
54
+
55
+ /** Path parameters addressing one organization. */
56
+ export const Params = z
57
+ .object({
58
+ orgId: z
59
+ .string()
60
+ .check(
61
+ z.describe('The organization id (`org_…`).'),
62
+ z.meta({ examples: ['org_1a2b3c4d5e6f7g8h9j0k1m2n'] }),
63
+ ),
64
+ })
65
+ .check(z.describe('Path parameters for one organization.'))
66
+
67
+ /** Request body creating or renaming an organization. */
68
+ export const OrganizationBody = z
69
+ .object({ name })
70
+ .check(z.describe('Fields for creating or renaming an organization.'))
71
+
72
+ /** Schemas for the deleteOrganization operation. */
73
+ export namespace deleteOrganization {
74
+ /** Confirmation that the organization was deleted. */
75
+ export const Response = z
76
+ .object({
77
+ id: z
78
+ .string()
79
+ .check(
80
+ z.describe('ID of the organization that was deleted.'),
81
+ z.meta({ examples: ['org_1a2b3c4d5e6f7g8h9j0k1m2n'] }),
82
+ ),
83
+ })
84
+ .check(z.describe('Confirmation that the organization was deleted.'))
85
+ }
86
+
87
+ /** Schemas for the listOrganizations operation. */
88
+ export namespace listOrganizations {
89
+ /** Non-paginated list of organizations. */
90
+ export const Response = Schema.describe(
91
+ z.object({
92
+ data: z.array(Organization).check(z.describe('The organizations, newest first.')),
93
+ }),
94
+ 'A non-paginated list of organizations.',
95
+ )
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Mounts the `/orgs` resource: session-owned tenancy (each organization is
101
+ * owned by the user that created it). The super admin bypasses ownership;
102
+ * inaccessible or absent ids render `404` so foreign org ids are
103
+ * indistinguishable from missing ones. Projects nest under `/orgs/:orgId` in
104
+ * the projects app.
105
+ */
106
+ export function orgs() {
107
+ return new Hono<App.Environment>()
108
+ .get(
109
+ '/v1/orgs',
110
+ Auth.policy({ session: true }),
111
+ OpenApi.describeRoute({
112
+ operationId: 'listOrganizations',
113
+ responses: OpenApi.responses({
114
+ success: {
115
+ description: 'The organizations, newest first.',
116
+ schema: schema.listOrganizations.Response,
117
+ },
118
+ }),
119
+ summary: 'List organizations',
120
+ tags: ['Organizations'],
121
+ }),
122
+ async (c) => {
123
+ if (Auth.narrowAccess) return Auth.superAdminAccessError(c)
124
+
125
+ const principal = Auth.getPrincipal(c)
126
+ if (principal?.type !== 'session' && principal?.type !== 'super_admin')
127
+ return sessionRequired(c)
128
+
129
+ const db = Db.get(c.get('db'))
130
+ try {
131
+ const records =
132
+ principal.type === 'super_admin'
133
+ ? await Organizations.list(db)
134
+ : await Organizations.listByMember(db, principal.id)
135
+ return c.json(
136
+ Response.validated(schema.listOrganizations.Response, {
137
+ data: records.map(serializeOrganization),
138
+ }),
139
+ 200,
140
+ )
141
+ } catch (cause) {
142
+ return Response.upstream(c, cause)
143
+ }
144
+ },
145
+ )
146
+ .post(
147
+ '/v1/orgs',
148
+ Auth.policy({ session: true }),
149
+ OpenApi.validate('json', schema.OrganizationBody, {
150
+ code: 'body_invalid',
151
+ message: 'Check the request body and try again.',
152
+ }),
153
+ OpenApi.describeRoute({
154
+ operationId: 'createOrganization',
155
+ responses: OpenApi.responses({
156
+ success: {
157
+ description: 'The created organization.',
158
+ schema: schema.Organization,
159
+ },
160
+ }),
161
+ summary: 'Create organization',
162
+ tags: ['Organizations'],
163
+ }),
164
+ async (c) => {
165
+ if (Auth.narrowAccess) return Auth.superAdminAccessError(c)
166
+ if (OpenApi.narrowValidation)
167
+ return OpenApi.validationError(c, {
168
+ code: 'body_invalid',
169
+ message: 'Check the request body and try again.',
170
+ })
171
+
172
+ const principal = Auth.getPrincipal(c)
173
+ if (principal?.type !== 'session' && principal?.type !== 'super_admin')
174
+ return sessionRequired(c)
175
+
176
+ const body = c.req.valid('json')
177
+ const db = Db.get(c.get('db'))
178
+ try {
179
+ // Sessions own what they create; the super admin has no user row.
180
+ // Organizations provisioned by the super admin start memberless.
181
+ const record =
182
+ principal.type === 'super_admin'
183
+ ? await Organizations.create(db, { createdBy: 'super_admin', name: body.name })
184
+ : await Organizations.createOwned(db, { name: body.name, userId: principal.id })
185
+ return c.json(Response.validated(schema.Organization, serializeOrganization(record)), 200)
186
+ } catch (cause) {
187
+ return Response.upstream(c, cause)
188
+ }
189
+ },
190
+ )
191
+ .get(
192
+ '/v1/orgs/:orgId{org_[A-Za-z0-9_-]+}',
193
+ Auth.policy({ session: true }),
194
+ Auth.ensureOrg(),
195
+ OpenApi.validate('param', schema.Params, {
196
+ code: 'param_invalid',
197
+ message: 'Check the path parameters and try again.',
198
+ }),
199
+ OpenApi.describeRoute({
200
+ operationId: 'getOrganization',
201
+ responses: OpenApi.responses({
202
+ errors: { 404: 'No accessible organization was found for this id.' },
203
+ success: { description: 'One organization.', schema: schema.Organization },
204
+ }),
205
+ summary: 'Get organization',
206
+ tags: ['Organizations'],
207
+ }),
208
+ async (c) => {
209
+ if (Auth.narrowAccess) return Auth.superAdminAccessError(c)
210
+ if (Auth.narrowScope) return Auth.ensureOrgError(c)
211
+ if (OpenApi.narrowValidation)
212
+ return OpenApi.validationError(c, {
213
+ code: 'param_invalid',
214
+ message: 'Check the path parameters and try again.',
215
+ })
216
+
217
+ return c.json(
218
+ Response.validated(schema.Organization, serializeOrganization(Auth.org(c))),
219
+ 200,
220
+ )
221
+ },
222
+ )
223
+ .patch(
224
+ '/v1/orgs/:orgId{org_[A-Za-z0-9_-]+}',
225
+ Auth.policy({ session: true }),
226
+ Auth.ensureOrg({ role: 'owner' }),
227
+ OpenApi.validate('param', schema.Params, {
228
+ code: 'param_invalid',
229
+ message: 'Check the path parameters and try again.',
230
+ }),
231
+ OpenApi.validate('json', schema.OrganizationBody, {
232
+ code: 'body_invalid',
233
+ message: 'Check the request body and try again.',
234
+ }),
235
+ OpenApi.describeRoute({
236
+ operationId: 'updateOrganization',
237
+ responses: OpenApi.responses({
238
+ errors: { 404: 'No accessible organization was found for this id.' },
239
+ success: { description: 'The updated organization.', schema: schema.Organization },
240
+ }),
241
+ summary: 'Update organization',
242
+ tags: ['Organizations'],
243
+ }),
244
+ async (c) => {
245
+ if (Auth.narrowAccess) return Auth.superAdminAccessError(c)
246
+ if (OpenApi.narrowValidation)
247
+ return OpenApi.validationError(c, {
248
+ code: 'param_invalid',
249
+ message: 'Check the path parameters and try again.',
250
+ })
251
+ if (OpenApi.narrowValidation)
252
+ return OpenApi.validationError(c, {
253
+ code: 'body_invalid',
254
+ message: 'Check the request body and try again.',
255
+ })
256
+
257
+ const body = c.req.valid('json')
258
+ const db = Db.get(c.get('db'))
259
+ try {
260
+ const updated = await Organizations.update(db, Auth.org(c).id, { name: body.name })
261
+ if (!updated) return organizationNotFound(c)
262
+ return c.json(
263
+ Response.validated(schema.Organization, serializeOrganization(updated)),
264
+ 200,
265
+ )
266
+ } catch (cause) {
267
+ return Response.upstream(c, cause)
268
+ }
269
+ },
270
+ )
271
+ .delete(
272
+ '/v1/orgs/:orgId{org_[A-Za-z0-9_-]+}',
273
+ Auth.policy({ session: true }),
274
+ Auth.ensureOrg({ role: 'owner' }),
275
+ OpenApi.validate('param', schema.Params, {
276
+ code: 'param_invalid',
277
+ message: 'Check the path parameters and try again.',
278
+ }),
279
+ OpenApi.describeRoute({
280
+ description: 'Delete an organization and all of its projects.',
281
+ operationId: 'deleteOrganization',
282
+ responses: OpenApi.responses({
283
+ errors: { 404: 'No accessible organization was found for this id.' },
284
+ success: {
285
+ description: 'Confirmation that the organization was deleted.',
286
+ schema: schema.deleteOrganization.Response,
287
+ },
288
+ }),
289
+ summary: 'Delete organization',
290
+ tags: ['Organizations'],
291
+ }),
292
+ async (c) => {
293
+ if (Auth.narrowAccess) return Auth.superAdminAccessError(c)
294
+ if (Auth.narrowScope) return Auth.ensureOrgError(c)
295
+ if (OpenApi.narrowValidation)
296
+ return OpenApi.validationError(c, {
297
+ code: 'param_invalid',
298
+ message: 'Check the path parameters and try again.',
299
+ })
300
+
301
+ const db = Db.get(c.get('db'))
302
+ try {
303
+ const kv = c.get('kv')
304
+ if (kv)
305
+ for (const record of await ApiKeys.listByOrg(kv.store, Auth.org(c).id))
306
+ await ApiKeys.revoke(kv.store, record.id)
307
+ const deleted = await Organizations.deleteOrganization(db, Auth.org(c).id)
308
+ if (!deleted) return organizationNotFound(c)
309
+ return c.json(
310
+ Response.validated(schema.deleteOrganization.Response, { id: Auth.org(c).id }),
311
+ 200,
312
+ )
313
+ } catch (cause) {
314
+ return Response.upstream(c, cause)
315
+ }
316
+ },
317
+ )
318
+ }
319
+
320
+ function serializeOrganization(record: Organizations.Record) {
321
+ return {
322
+ createdAt: record.createdAt,
323
+ id: record.id,
324
+ name: record.name,
325
+ updatedAt: record.updatedAt,
326
+ }
327
+ }
328
+
329
+ function organizationNotFound(c: Context<App.Environment>) {
330
+ return Response.error(c, {
331
+ code: 'organization_not_found',
332
+ message: 'Organization not found',
333
+ status: 404,
334
+ })
335
+ }
336
+
337
+ function sessionRequired(c: Context<App.Environment>) {
338
+ return Response.error(c, {
339
+ code: 'forbidden',
340
+ message: 'Session required',
341
+ status: 403,
342
+ })
343
+ }