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,278 @@
1
+ import * as ApiKeys from './ApiKeys.js'
2
+ import * as Store from './internal/Store.js'
3
+
4
+ describe('resolve', () => {
5
+ test('resolves an unknown token to null', async () => {
6
+ expect(await ApiKeys.resolve(Store.memory(), 'tempo:sk:missing')).toBeNull()
7
+ })
8
+
9
+ test('resolves before expiry, rejects after', async () => {
10
+ vi.useFakeTimers({ now: new Date('2026-01-01T00:00:00.000Z') })
11
+ try {
12
+ const state = Store.memory()
13
+ const { token } = await ApiKeys.mint(state, {
14
+ expiresAt: '2026-01-01T00:01:00.000Z',
15
+ orgId: 'org_a',
16
+ scopes: ['data:read'],
17
+ })
18
+
19
+ expect((await ApiKeys.resolve(state, token))?.orgId).toBe('org_a')
20
+
21
+ // Advance the clock past the expiry.
22
+ vi.setSystemTime(new Date('2026-01-01T00:02:00.000Z'))
23
+ expect(await ApiKeys.resolve(state, token)).toBeNull()
24
+ } finally {
25
+ vi.useRealTimers()
26
+ }
27
+ })
28
+
29
+ test('treats a corrupt record as absent', async () => {
30
+ const state = Store.memory()
31
+ const { token } = await ApiKeys.mint(state, { orgId: 'org_a', scopes: ['data:read'] })
32
+ // Overwrite the stored record (keyed by token hash) with invalid JSON.
33
+ const { keys } = await state.list({ prefix: 'apikey:' })
34
+ await state.put(keys[0]!.name, 'not json')
35
+ expect(await ApiKeys.resolve(state, token)).toBeNull()
36
+ })
37
+
38
+ test('rejects a record with an unknown scope', async () => {
39
+ const state = Store.memory()
40
+ const { token } = await ApiKeys.mint(state, { orgId: 'org_a', scopes: ['data:read'] })
41
+ const { keys } = await state.list({ prefix: 'apikey:' })
42
+ await state.put(
43
+ keys[0]!.name,
44
+ JSON.stringify({
45
+ createdAt: '2026-01-01T00:00:00.000Z',
46
+ id: 'key_x',
47
+ orgId: 'org_a',
48
+ scopes: ['data:read', 'totally:made-up'],
49
+ tokenLast4: 'xxxx',
50
+ }),
51
+ )
52
+ expect(await ApiKeys.resolve(state, token)).toBeNull()
53
+ })
54
+ })
55
+
56
+ // Mock the system clock so timestamps are deterministic without injecting a
57
+ // clock into the module.
58
+ describe('mint', () => {
59
+ beforeEach(() => vi.useFakeTimers({ now: new Date('2026-01-01T00:00:00.000Z') }))
60
+ afterEach(() => vi.useRealTimers())
61
+
62
+ test('mints a key and resolves it', async () => {
63
+ const state = Store.memory()
64
+
65
+ const { record, token } = await ApiKeys.mint(state, {
66
+ createdBy: 'admin@tempo.xyz',
67
+ name: 'CI key',
68
+ orgId: 'org_acme',
69
+ scopes: ['data:read', 'webhooks:write'],
70
+ })
71
+
72
+ expect(token).toMatch(/^tempo:sk:[a-f0-9]{48}$/)
73
+ expect(record.tokenLast4).toBe(token.slice(-4))
74
+ expect({ ...record, id: '<id>', tokenLast4: '<last4>' }).toMatchInlineSnapshot(`
75
+ {
76
+ "createdAt": "2026-01-01T00:00:00.000Z",
77
+ "createdBy": "admin@tempo.xyz",
78
+ "environment": "production",
79
+ "id": "<id>",
80
+ "name": "CI key",
81
+ "orgId": "org_acme",
82
+ "scopes": [
83
+ "data:read",
84
+ "webhooks:write",
85
+ ],
86
+ "tokenLast4": "<last4>",
87
+ }
88
+ `)
89
+
90
+ const resolved = await ApiKeys.resolve(state, token)
91
+ expect(resolved?.orgId).toBe('org_acme')
92
+ expect(resolved?.scopes).toEqual(['data:read', 'webhooks:write'])
93
+ })
94
+
95
+ test('rejects an unknown scope at the write boundary', async () => {
96
+ await expect(
97
+ ApiKeys.mint(Store.memory(), { orgId: 'org_a', scopes: ['totally:made-up' as never] }),
98
+ ).rejects.toThrowError(/invalid API key input/)
99
+ })
100
+ })
101
+
102
+ describe('list', () => {
103
+ beforeEach(() => vi.useFakeTimers({ now: new Date('2026-01-01T00:00:00.000Z') }))
104
+ afterEach(() => vi.useRealTimers())
105
+
106
+ test('lists keys newest first, filtered by org', async () => {
107
+ const state = Store.memory()
108
+
109
+ // Advance the clock between mints so their createdAt timestamps differ and
110
+ // "newest first" is well-defined.
111
+ await ApiKeys.mint(state, { name: 'a', orgId: 'org_a', scopes: ['data:read'] })
112
+ vi.setSystemTime(new Date('2026-01-01T00:00:01.000Z'))
113
+ await ApiKeys.mint(state, { name: 'b', orgId: 'org_b', scopes: ['data:read'] })
114
+
115
+ const all = await ApiKeys.list(state)
116
+ expect(all.map((record) => record.name)).toMatchInlineSnapshot(`
117
+ [
118
+ "b",
119
+ "a",
120
+ ]
121
+ `)
122
+
123
+ const scoped = await ApiKeys.list(state, { orgId: 'org_b' })
124
+ expect(scoped.map((record) => record.orgId)).toMatchInlineSnapshot(`
125
+ [
126
+ "org_b",
127
+ ]
128
+ `)
129
+ })
130
+
131
+ test('excludes a key past its expiry', async () => {
132
+ const state = Store.memory()
133
+
134
+ await ApiKeys.mint(state, {
135
+ expiresAt: '2026-01-01T00:00:30.000Z',
136
+ name: 'short-lived',
137
+ orgId: 'org_a',
138
+ scopes: ['data:read'],
139
+ })
140
+
141
+ expect((await ApiKeys.list(state)).map((record) => record.name)).toMatchInlineSnapshot(`
142
+ [
143
+ "short-lived",
144
+ ]
145
+ `)
146
+
147
+ // Advance the clock past the expiry.
148
+ vi.setSystemTime(new Date('2026-01-01T00:01:00.000Z'))
149
+ expect(await ApiKeys.list(state)).toMatchInlineSnapshot(`[]`)
150
+ })
151
+ })
152
+
153
+ describe('revoke', () => {
154
+ test('revokes a key by id', async () => {
155
+ const state = Store.memory()
156
+
157
+ const { record, token } = await ApiKeys.mint(state, { orgId: 'org_a', scopes: ['data:read'] })
158
+
159
+ expect(await ApiKeys.revoke(state, record.id)).toBe(true)
160
+ expect(await ApiKeys.resolve(state, token)).toBeNull()
161
+ expect(await ApiKeys.revoke(state, record.id)).toBe(false)
162
+ })
163
+
164
+ test('revoke is O(1): deletes the record without scanning the prefix', async () => {
165
+ const state = Store.memory()
166
+
167
+ const { record } = await ApiKeys.mint(state, { orgId: 'org_a', scopes: ['data:read'] })
168
+
169
+ // The id index entry maps id -> record key; revoke uses it directly.
170
+ expect(await ApiKeys.revoke(state, record.id)).toBe(true)
171
+ // The record and both index entries are gone.
172
+ const { keys } = await state.list({ prefix: 'apikey' })
173
+ expect(keys).toMatchInlineSnapshot(`[]`)
174
+ })
175
+ })
176
+
177
+ describe('get', () => {
178
+ test('reads a record by id, null when absent', async () => {
179
+ const state = Store.memory()
180
+ const { record } = await ApiKeys.mint(state, {
181
+ orgId: 'org_a',
182
+ projectId: 'prj_a',
183
+ scopes: ['data:read'],
184
+ })
185
+
186
+ expect((await ApiKeys.get(state, record.id))?.projectId).toBe('prj_a')
187
+ expect(await ApiKeys.get(state, 'key_missing')).toBeNull()
188
+ })
189
+ })
190
+
191
+ describe('listByOrg', () => {
192
+ beforeEach(() => vi.useFakeTimers({ now: new Date('2026-01-01T00:00:00.000Z') }))
193
+ afterEach(() => vi.useRealTimers())
194
+
195
+ test('lists through the org index, newest first, filtered by project', async () => {
196
+ const state = Store.memory()
197
+
198
+ await ApiKeys.mint(state, { name: 'a', orgId: 'org_a', projectId: 'prj_a', scopes: ['data:read'] }) // prettier-ignore
199
+ vi.setSystemTime(new Date('2026-01-01T00:00:01.000Z'))
200
+ await ApiKeys.mint(state, { name: 'b', orgId: 'org_a', projectId: 'prj_b', scopes: ['data:read'] }) // prettier-ignore
201
+ await ApiKeys.mint(state, { name: 'c', orgId: 'org_b', scopes: ['data:read'] })
202
+
203
+ expect((await ApiKeys.listByOrg(state, 'org_a')).map((record) => record.name))
204
+ .toMatchInlineSnapshot(`
205
+ [
206
+ "b",
207
+ "a",
208
+ ]
209
+ `)
210
+ expect(
211
+ (await ApiKeys.listByOrg(state, 'org_a', { projectId: 'prj_b' })).map(
212
+ (record) => record.name,
213
+ ),
214
+ ).toMatchInlineSnapshot(`
215
+ [
216
+ "b",
217
+ ]
218
+ `)
219
+ })
220
+
221
+ test('skips and lazily cleans dangling index entries', async () => {
222
+ const state = Store.memory()
223
+ await ApiKeys.mint(state, { orgId: 'org_a', scopes: ['data:read'] })
224
+
225
+ // Simulate a record evicted out-of-band (e.g. native TTL), leaving the
226
+ // index entry behind.
227
+ const recordKey = (await state.list({ prefix: 'apikey:' })).keys[0]!.name
228
+ await state.delete(recordKey)
229
+
230
+ expect(await ApiKeys.listByOrg(state, 'org_a')).toEqual([])
231
+ expect((await state.list({ prefix: 'apikey_org:' })).keys).toMatchInlineSnapshot(`[]`)
232
+ })
233
+ })
234
+
235
+ describe('attribute', () => {
236
+ test('re-attributes org and project, moving the org index', async () => {
237
+ const state = Store.memory()
238
+ const { record, token } = await ApiKeys.mint(state, { orgId: 'org_a', scopes: ['data:read'] })
239
+
240
+ const updated = await ApiKeys.attribute(state, record.id, {
241
+ orgId: 'org_b',
242
+ projectId: 'prj_b',
243
+ })
244
+ expect(updated?.orgId).toBe('org_b')
245
+ expect(updated?.projectId).toBe('prj_b')
246
+
247
+ // The original token keeps resolving, now carrying the attribution.
248
+ const resolved = await ApiKeys.resolve(state, token)
249
+ expect(resolved?.orgId).toBe('org_b')
250
+ expect(resolved?.projectId).toBe('prj_b')
251
+
252
+ expect(await ApiKeys.listByOrg(state, 'org_a')).toEqual([])
253
+ expect((await ApiKeys.listByOrg(state, 'org_b')).map(({ id }) => id)).toEqual([record.id])
254
+
255
+ expect(await ApiKeys.attribute(state, 'key_missing', { projectId: 'prj_x' })).toBeNull()
256
+ })
257
+ })
258
+
259
+ describe('backfill', () => {
260
+ test('rebuilds index entries for legacy records', async () => {
261
+ const state = Store.memory()
262
+ const { record, token } = await ApiKeys.mint(state, { orgId: 'org_a', scopes: ['data:read'] })
263
+
264
+ // Legacy state: records exist, index entries do not.
265
+ for (const { name } of (await state.list({ prefix: 'apikey_id:' })).keys)
266
+ await state.delete(name)
267
+ for (const { name } of (await state.list({ prefix: 'apikey_org:' })).keys)
268
+ await state.delete(name)
269
+ expect(await ApiKeys.listByOrg(state, 'org_a')).toEqual([])
270
+
271
+ expect(await ApiKeys.backfill(state)).toEqual({ indexed: 1, scanned: 1 })
272
+
273
+ expect((await ApiKeys.listByOrg(state, 'org_a')).map(({ id }) => id)).toEqual([record.id])
274
+ expect(await ApiKeys.resolve(state, token)).not.toBeNull()
275
+ // The id index is restored, so revoke-by-id works again.
276
+ expect(await ApiKeys.revoke(state, record.id)).toBe(true)
277
+ })
278
+ })
package/src/ApiKeys.ts ADDED
@@ -0,0 +1,358 @@
1
+ /**
2
+ * API-key persistence over the app's KV state store (`App.create({ kv })`).
3
+ *
4
+ * Records are keyed by token hash (`ApiKey.keyFor`) — the plaintext token is
5
+ * never stored — and validated against the scope catalog on read; unknown,
6
+ * corrupt, expired, or invalid records resolve to `null`. An `id → recordKey`
7
+ * index makes revoke-by-id O(1). The persisted {@link Record}/{@link
8
+ * MintInput} shapes live here (derived from `ApiKey.schema` so they cannot
9
+ * drift from the resolved shape), while the shared credential primitives
10
+ * (token format/generation, storage-key derivation, redaction) live in
11
+ * `./ApiKey.js`.
12
+ */
13
+
14
+ import * as z from 'zod/mini'
15
+
16
+ import * as ApiKey from './ApiKey.js'
17
+ import type * as Store from './internal/Store.js'
18
+
19
+ /** Zod schemas owned by the API-keys module. */
20
+ export namespace schema {
21
+ /**
22
+ * Fields accepted when minting a key, and the single source of truth for the
23
+ * {@link MintInput} type and `mint` validation. Derived from the resolved
24
+ * `ApiKey.schema` (minus the generated `id`) so the scope vocabulary stays in
25
+ * lockstep with the resolved shape.
26
+ */
27
+ export const MintInput = z.extend(z.omit(ApiKey.schema.ApiKey, { id: true }), {
28
+ createdBy: z
29
+ .optional(z.string())
30
+ .check(z.describe('Identity creating the key (e.g. admin email), recorded for audit.')),
31
+ expiresAt: z
32
+ .optional(z.string())
33
+ .check(z.describe('ISO 8601 expiry timestamp. Omit for a non-expiring key.')),
34
+ // TODO: make required once orgs exist.
35
+ orgId: z.optional(z.string()).check(z.describe('Owning organization id.')),
36
+ })
37
+
38
+ /**
39
+ * A persisted API-key record: the resolved `ApiKey.schema` plus
40
+ * persistence/display fields. Extending the resolved schema keeps the resolved
41
+ * and persisted shapes in lockstep.
42
+ */
43
+ export const Record = z.extend(ApiKey.schema.ApiKey, {
44
+ createdAt: z.string().check(z.describe('ISO 8601 creation timestamp.')),
45
+ createdBy: z.optional(z.string()).check(z.describe('Identity that created the key.')),
46
+ expiresAt: z.optional(z.string()).check(z.describe('ISO 8601 expiry timestamp.')),
47
+ tokenLast4: z.string().check(z.describe('Last 4 chars of the plaintext token.')),
48
+ })
49
+ }
50
+
51
+ /**
52
+ * A durable API-key record as persisted. A superset of the resolved
53
+ * `ApiKey.ApiKey` with provisioning/display metadata. The plaintext token is
54
+ * never stored — records are keyed by its hash (`ApiKey.keyFor`) and carry only
55
+ * `tokenLast4` for display.
56
+ */
57
+ export type Record = z.output<typeof schema.Record>
58
+
59
+ /**
60
+ * Fields accepted when minting a key via {@link mint}. Uses the schema's
61
+ * *input* type so defaulted fields (e.g. `environment`) are optional for
62
+ * callers — the default is applied during `mint`'s validation.
63
+ */
64
+ export type MintInput = z.input<typeof schema.MintInput>
65
+
66
+ /** Result of {@link mint}: the persisted record and its one-time token. */
67
+ export type MintResult = {
68
+ /** The persisted key record (metadata only — never the token). */
69
+ record: Record
70
+ /** The plaintext token, shown once and unrecoverable afterward. */
71
+ token: string
72
+ }
73
+
74
+ /**
75
+ * Mints a key: validates the input (e.g. rejects an unknown scope at the write
76
+ * boundary), persists its hashed record, and returns the record plus the
77
+ * one-time plaintext token (shown once, unrecoverable afterward).
78
+ *
79
+ * @param state - The KV state store holding key records.
80
+ * @param input - The key to mint.
81
+ * @returns The persisted record and its one-time token.
82
+ */
83
+ export async function mint(state: Store.State, input: MintInput): Promise<MintResult> {
84
+ const valid = schema.MintInput.safeParse(input)
85
+ if (!valid.success) throw new Error(`invalid API key input: ${z.prettifyError(valid.error)}`)
86
+ const data = valid.data
87
+
88
+ const time = new Date()
89
+ const token = ApiKey.generateToken(data.environment)
90
+ const recordKey = ApiKey.keyFor(token)
91
+ const id = ApiKey.generateId()
92
+ const record: Record = {
93
+ createdAt: time.toISOString(),
94
+ environment: data.environment,
95
+ id,
96
+ // TODO: drop fallback once a real org concept exists.
97
+ orgId: data.orgId ?? id,
98
+ scopes: data.scopes,
99
+ tokenLast4: token.slice(-4),
100
+ ...(data.createdBy === undefined ? {} : { createdBy: data.createdBy }),
101
+ ...(data.expiresAt === undefined ? {} : { expiresAt: data.expiresAt }),
102
+ ...(data.name === undefined ? {} : { name: data.name }),
103
+ ...(data.projectId === undefined ? {} : { projectId: data.projectId }),
104
+ ...(data.rateLimits === undefined ? {} : { rateLimits: data.rateLimits }),
105
+ }
106
+
107
+ // Bound the stored record by its expiry so the backend evicts it natively.
108
+ const ttl =
109
+ data.expiresAt === undefined
110
+ ? undefined
111
+ : Math.max(1, Date.parse(data.expiresAt) - time.getTime())
112
+ await state.put(recordKey, JSON.stringify(record), { ttl })
113
+ // Index `id → recordKey` so revoke-by-id is O(1) (records are keyed by
114
+ // token hash, unrecoverable from the id alone). Shares the record's TTL so
115
+ // both entries expire together.
116
+ await state.put(idKeyFor(record.id), recordKey, { ttl })
117
+ // Index `org → recordKey` (one entry per key, so concurrent mints never
118
+ // race a shared value) backing org-scoped listing without a full scan.
119
+ await state.put(orgKeyFor(record.orgId, record.id), recordKey, { ttl })
120
+ return { record, token }
121
+ }
122
+
123
+ /**
124
+ * Resolves a plaintext API key token to its metadata, or `null`. Expiry is
125
+ * enforced on read — not just via the store's native TTL — so it holds on
126
+ * backends that evict lazily and regardless of TTL granularity.
127
+ *
128
+ * @param state - The KV state store holding key records.
129
+ * @param token - The presented plaintext token.
130
+ * @returns The resolved key metadata, or `null`.
131
+ */
132
+ export async function resolve(state: Store.State, token: string): Promise<ApiKey.ApiKey | null> {
133
+ return read(state, ApiKey.keyFor(token), Date.now())
134
+ }
135
+
136
+ /**
137
+ * Reads a key record by id (metadata only — never the token), or `null`.
138
+ *
139
+ * @param state - The KV state store holding key records.
140
+ * @param id - The key id (`key_…`).
141
+ * @returns The record, or `null`.
142
+ */
143
+ export async function get(state: Store.State, id: string): Promise<Record | null> {
144
+ const recordKey = await state.get(idKeyFor(id))
145
+ if (!recordKey) return null
146
+ return read(state, recordKey, Date.now())
147
+ }
148
+
149
+ /**
150
+ * Lists key records (metadata only — never tokens), newest first. Expired or
151
+ * catalog-invalid records are skipped.
152
+ *
153
+ * @param state - The KV state store holding key records.
154
+ * @param options - Optional owner filter.
155
+ * @returns The records.
156
+ */
157
+ export async function list(
158
+ state: Store.State,
159
+ options: list.Options = {},
160
+ ): Promise<readonly Record[]> {
161
+ const { keys } = await state.list({ prefix: ApiKey.recordPrefix })
162
+ const records: Record[] = []
163
+ for (const { name } of keys) {
164
+ const record = await read(state, name, Date.now())
165
+ if (record && (!options.orgId || record.orgId === options.orgId)) records.push(record)
166
+ }
167
+ // Newest first; ids are random, so sort by creation time.
168
+ return records.sort((a, b) => b.createdAt.localeCompare(a.createdAt))
169
+ }
170
+
171
+ export declare namespace list {
172
+ /** Options for {@link list}. */
173
+ type Options = {
174
+ /** Restrict results to keys owned by this organization. */
175
+ orgId?: string | undefined
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Lists an organization's key records through the org index (no full scan),
181
+ * newest first. Dangling index entries (revoked or expired records) are
182
+ * skipped and lazily deleted.
183
+ *
184
+ * @param state - The KV state store holding key records.
185
+ * @param orgId - The owning organization id (`org_…`).
186
+ * @param options - Optional project filter.
187
+ * @returns The records.
188
+ */
189
+ export async function listByOrg(
190
+ state: Store.State,
191
+ orgId: string,
192
+ options: listByOrg.Options = {},
193
+ ): Promise<readonly Record[]> {
194
+ const { keys } = await state.list({ prefix: `${orgPrefix}${orgId}:` })
195
+ const records: Record[] = []
196
+ for (const { name } of keys) {
197
+ const recordKey = await state.get(name)
198
+ const record = recordKey ? await read(state, recordKey, Date.now()) : null
199
+ if (!record) {
200
+ await state.delete(name)
201
+ continue
202
+ }
203
+ if (!options.projectId || record.projectId === options.projectId) records.push(record)
204
+ }
205
+ return records.sort((a, b) => b.createdAt.localeCompare(a.createdAt))
206
+ }
207
+
208
+ export declare namespace listByOrg {
209
+ /** Options for {@link listByOrg}. */
210
+ type Options = {
211
+ /** Restrict results to keys attributed to this project. */
212
+ projectId?: string | undefined
213
+ }
214
+ }
215
+
216
+ /**
217
+ * Revokes a key by id, deleting its record and index entries.
218
+ *
219
+ * @param state - The KV state store holding key records.
220
+ * @param id - The key id (`key_…`).
221
+ * @returns Whether a matching record was deleted.
222
+ */
223
+ export async function revoke(state: Store.State, id: string): Promise<boolean> {
224
+ const recordKey = await state.get(idKeyFor(id))
225
+ if (!recordKey) return false
226
+ // Parse loosely for the org index entry: even a record the catalog no
227
+ // longer validates must clean up its index on revoke.
228
+ const raw = await state.get(recordKey)
229
+ const orgId = (() => {
230
+ try {
231
+ return raw ? (JSON.parse(raw) as { orgId?: string }).orgId : undefined
232
+ } catch {
233
+ return undefined
234
+ }
235
+ })()
236
+ await state.delete(recordKey)
237
+ await state.delete(idKeyFor(id))
238
+ if (orgId) await state.delete(orgKeyFor(orgId, id))
239
+ return true
240
+ }
241
+
242
+ /**
243
+ * Re-attributes a key to an organization and/or project, rewriting its record
244
+ * in place (same token hash) and moving the org index entry when the org
245
+ * changes. The backfill path for legacy keys.
246
+ *
247
+ * @param state - The KV state store holding key records.
248
+ * @param id - The key id (`key_…`).
249
+ * @param input - The attribution to apply.
250
+ * @returns The updated record, or `null` when the key is absent.
251
+ */
252
+ export async function attribute(
253
+ state: Store.State,
254
+ id: string,
255
+ input: attribute.Input,
256
+ ): Promise<Record | null> {
257
+ const recordKey = await state.get(idKeyFor(id))
258
+ if (!recordKey) return null
259
+ const record = await read(state, recordKey, Date.now())
260
+ if (!record) return null
261
+
262
+ const updated: Record = {
263
+ ...record,
264
+ ...(input.orgId === undefined ? {} : { orgId: input.orgId }),
265
+ ...(input.projectId === undefined ? {} : { projectId: input.projectId }),
266
+ }
267
+ const ttl =
268
+ updated.expiresAt === undefined
269
+ ? undefined
270
+ : Math.max(1, Date.parse(updated.expiresAt) - Date.now())
271
+ await state.put(recordKey, JSON.stringify(updated), { ttl })
272
+ if (updated.orgId !== record.orgId) await state.delete(orgKeyFor(record.orgId, id))
273
+ await state.put(orgKeyFor(updated.orgId, id), recordKey, { ttl })
274
+ return updated
275
+ }
276
+
277
+ export declare namespace attribute {
278
+ /** Attribution applied by {@link attribute}; omitted fields keep their current value. */
279
+ type Input = {
280
+ /** New owning organization id (`org_…`). */
281
+ orgId?: string | undefined
282
+ /** New owning project id (`prj_…`). */
283
+ projectId?: string | undefined
284
+ }
285
+ }
286
+
287
+ /**
288
+ * Rebuilds the `id` and `org` index entries for every stored record — the
289
+ * run-once backfill making legacy keys visible to org-scoped listing. Records
290
+ * themselves are never modified, so existing tokens keep resolving.
291
+ *
292
+ * @param state - The KV state store holding key records.
293
+ * @returns Scanned and indexed record counts.
294
+ */
295
+ export async function backfill(state: Store.State): Promise<backfill.Result> {
296
+ const { keys } = await state.list({ prefix: ApiKey.recordPrefix })
297
+ let indexed = 0
298
+ for (const { name } of keys) {
299
+ const record = await read(state, name, Date.now())
300
+ if (!record) continue
301
+ const ttl =
302
+ record.expiresAt === undefined
303
+ ? undefined
304
+ : Math.max(1, Date.parse(record.expiresAt) - Date.now())
305
+ await state.put(idKeyFor(record.id), name, { ttl })
306
+ await state.put(orgKeyFor(record.orgId, record.id), name, { ttl })
307
+ indexed += 1
308
+ }
309
+ return { indexed, scanned: keys.length }
310
+ }
311
+
312
+ export declare namespace backfill {
313
+ /** Result of {@link backfill}. */
314
+ type Result = {
315
+ /** Records whose index entries were written. */
316
+ indexed: number
317
+ /** Stored records scanned (including invalid or expired ones, skipped). */
318
+ scanned: number
319
+ }
320
+ }
321
+
322
+ /**
323
+ * Reads and validates a stored record by storage key. Invalid, corrupt, or
324
+ * expired records (`expiresAt <= now`) resolve to `null`.
325
+ */
326
+ async function read(state: Store.State, key: string, now: number): Promise<Record | null> {
327
+ const raw = await state.get(key)
328
+ if (!raw) return null
329
+ const value = (() => {
330
+ try {
331
+ return JSON.parse(raw) as unknown
332
+ } catch {
333
+ return undefined
334
+ }
335
+ })()
336
+ if (value === undefined) return null
337
+ const parsed = schema.Record.safeParse(value)
338
+ if (!parsed.success) return null
339
+ const record = parsed.data
340
+ if (record.expiresAt !== undefined && Date.parse(record.expiresAt) <= now) return null
341
+ return record
342
+ }
343
+
344
+ /** Storage-key prefix for the `id → recordKey` index. */
345
+ const idPrefix = 'apikey_id:'
346
+
347
+ /** Storage key under which a key's `id → recordKey` index entry is persisted. */
348
+ function idKeyFor(id: string): string {
349
+ return `${idPrefix}${id}`
350
+ }
351
+
352
+ /** Storage-key prefix for the per-key `org → recordKey` listing index. */
353
+ const orgPrefix = 'apikey_org:'
354
+
355
+ /** Storage key under which a key's org index entry is persisted. */
356
+ function orgKeyFor(orgId: string, id: string): string {
357
+ return `${orgPrefix}${orgId}:${id}`
358
+ }
package/src/App.test-d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { App, Auth } from 'cadent'
1
+ import type { App, Auth } from 'tapimo'
2
2
 
3
3
  describe('App.Endpoint', () => {
4
4
  // Smoke-test that `App.Endpoint` narrows to the inferred route surface so