tapimo 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (520) hide show
  1. package/README.md +57 -16
  2. package/dist/ApiKey.d.ts +4 -1
  3. package/dist/ApiKey.d.ts.map +1 -1
  4. package/dist/ApiKey.js +6 -2
  5. package/dist/ApiKey.js.map +1 -1
  6. package/dist/ApiKeys.d.ts +98 -3
  7. package/dist/ApiKeys.d.ts.map +1 -1
  8. package/dist/ApiKeys.js +150 -1
  9. package/dist/ApiKeys.js.map +1 -1
  10. package/dist/App.d.ts +7917 -2315
  11. package/dist/App.d.ts.map +1 -1
  12. package/dist/App.js +141 -329
  13. package/dist/App.js.map +1 -1
  14. package/dist/Scope.d.ts +20 -4
  15. package/dist/Scope.d.ts.map +1 -1
  16. package/dist/Scope.js +23 -4
  17. package/dist/Scope.js.map +1 -1
  18. package/dist/Webhooks.d.ts +2 -2
  19. package/dist/Webhooks.d.ts.map +1 -1
  20. package/dist/Webhooks.js +2 -2
  21. package/dist/Webhooks.js.map +1 -1
  22. package/dist/admin/App.d.ts +203 -120
  23. package/dist/admin/App.d.ts.map +1 -1
  24. package/dist/admin/apps/api-keys.d.ts +111 -6
  25. package/dist/admin/apps/api-keys.d.ts.map +1 -1
  26. package/dist/admin/apps/api-keys.js +67 -0
  27. package/dist/admin/apps/api-keys.js.map +1 -1
  28. package/dist/admin/apps/scopes.d.ts +12 -1
  29. package/dist/admin/apps/scopes.d.ts.map +1 -1
  30. package/dist/admin/apps/scopes.js +3 -0
  31. package/dist/admin/apps/scopes.js.map +1 -1
  32. package/dist/admin/apps/verified-tokens.d.ts +116 -116
  33. package/dist/admin/apps/verified-tokens.js +1 -1
  34. package/dist/admin/apps/verified-tokens.js.map +1 -1
  35. package/dist/admin/ui.generated.d.ts +1 -1
  36. package/dist/admin/ui.generated.js +13 -13
  37. package/dist/admin/ui.generated.js.map +1 -1
  38. package/dist/analytics/Analytics.d.ts +151 -0
  39. package/dist/analytics/Analytics.d.ts.map +1 -0
  40. package/dist/analytics/Analytics.js +212 -0
  41. package/dist/analytics/Analytics.js.map +1 -0
  42. package/dist/analytics/tables/requestEvents.d.ts +215 -0
  43. package/dist/analytics/tables/requestEvents.d.ts.map +1 -0
  44. package/dist/analytics/tables/requestEvents.js +264 -0
  45. package/dist/analytics/tables/requestEvents.js.map +1 -0
  46. package/dist/apps/data/App.d.ts +19576 -0
  47. package/dist/apps/data/App.d.ts.map +1 -0
  48. package/dist/apps/data/App.js +272 -0
  49. package/dist/apps/data/App.js.map +1 -0
  50. package/dist/apps/data/index.d.ts +3 -0
  51. package/dist/apps/data/index.d.ts.map +1 -0
  52. package/dist/apps/data/index.js +3 -0
  53. package/dist/apps/data/index.js.map +1 -0
  54. package/dist/apps/{activities.d.ts → data/routes/activities.d.ts} +32 -32
  55. package/dist/apps/{activities.d.ts.map → data/routes/activities.d.ts.map} +1 -1
  56. package/dist/apps/{activities.js → data/routes/activities.js} +13 -13
  57. package/dist/apps/data/routes/activities.js.map +1 -0
  58. package/dist/apps/{addresses.d.ts → data/routes/addresses.d.ts} +22 -22
  59. package/dist/apps/{addresses.d.ts.map → data/routes/addresses.d.ts.map} +1 -1
  60. package/dist/apps/{addresses.js → data/routes/addresses.js} +5 -5
  61. package/dist/apps/data/routes/addresses.js.map +1 -0
  62. package/dist/apps/{balances.d.ts → data/routes/balances.d.ts} +4 -4
  63. package/dist/apps/data/routes/balances.d.ts.map +1 -0
  64. package/dist/apps/{balances.js → data/routes/balances.js} +42 -11
  65. package/dist/apps/data/routes/balances.js.map +1 -0
  66. package/dist/apps/{blocks.d.ts → data/routes/blocks.d.ts} +47 -47
  67. package/dist/apps/data/routes/blocks.d.ts.map +1 -0
  68. package/dist/apps/{blocks.js → data/routes/blocks.js} +13 -13
  69. package/dist/apps/data/routes/blocks.js.map +1 -0
  70. package/dist/apps/{coingecko.d.ts → data/routes/coingecko.d.ts} +8 -8
  71. package/dist/apps/data/routes/coingecko.d.ts.map +1 -0
  72. package/dist/apps/{coingecko.js → data/routes/coingecko.js} +13 -13
  73. package/dist/apps/data/routes/coingecko.js.map +1 -0
  74. package/dist/apps/{exchanges.d.ts → data/routes/exchanges.d.ts} +108 -108
  75. package/dist/apps/data/routes/exchanges.d.ts.map +1 -0
  76. package/dist/apps/{exchanges.js → data/routes/exchanges.js} +19 -19
  77. package/dist/apps/data/routes/exchanges.js.map +1 -0
  78. package/dist/apps/{fee-amm.d.ts → data/routes/fee-amm.d.ts} +39 -39
  79. package/dist/apps/data/routes/fee-amm.d.ts.map +1 -0
  80. package/dist/apps/{fee-amm.js → data/routes/fee-amm.js} +17 -17
  81. package/dist/apps/data/routes/fee-amm.js.map +1 -0
  82. package/dist/apps/data/routes/funding.d.ts +765 -0
  83. package/dist/apps/data/routes/funding.d.ts.map +1 -0
  84. package/dist/apps/data/routes/funding.js +383 -0
  85. package/dist/apps/data/routes/funding.js.map +1 -0
  86. package/dist/apps/{indexer.d.ts → data/routes/indexer.d.ts} +4 -4
  87. package/dist/apps/data/routes/indexer.d.ts.map +1 -0
  88. package/dist/apps/{indexer.js → data/routes/indexer.js} +13 -9
  89. package/dist/apps/data/routes/indexer.js.map +1 -0
  90. package/dist/apps/{receipts.d.ts → data/routes/receipts.d.ts} +49 -49
  91. package/dist/apps/data/routes/receipts.d.ts.map +1 -0
  92. package/dist/apps/{receipts.js → data/routes/receipts.js} +14 -20
  93. package/dist/apps/data/routes/receipts.js.map +1 -0
  94. package/dist/apps/{rpc.d.ts → data/routes/rpc.d.ts} +33 -40
  95. package/dist/apps/data/routes/rpc.d.ts.map +1 -0
  96. package/dist/apps/{rpc.js → data/routes/rpc.js} +35 -7
  97. package/dist/apps/data/routes/rpc.js.map +1 -0
  98. package/dist/apps/{tokenlist.d.ts → data/routes/tokenlist.d.ts} +40 -40
  99. package/dist/apps/data/routes/tokenlist.d.ts.map +1 -0
  100. package/dist/apps/{tokenlist.js → data/routes/tokenlist.js} +8 -8
  101. package/dist/apps/data/routes/tokenlist.js.map +1 -0
  102. package/dist/apps/{tokens.d.ts → data/routes/tokens.d.ts} +468 -468
  103. package/dist/apps/data/routes/tokens.d.ts.map +1 -0
  104. package/dist/apps/{tokens.js → data/routes/tokens.js} +19 -19
  105. package/dist/apps/data/routes/tokens.js.map +1 -0
  106. package/dist/apps/{transactions.d.ts → data/routes/transactions.d.ts} +304 -304
  107. package/dist/apps/data/routes/transactions.d.ts.map +1 -0
  108. package/dist/apps/{transactions.js → data/routes/transactions.js} +13 -13
  109. package/dist/apps/data/routes/transactions.js.map +1 -0
  110. package/dist/apps/{transfers.d.ts → data/routes/transfers.d.ts} +62 -62
  111. package/dist/apps/data/routes/transfers.d.ts.map +1 -0
  112. package/dist/apps/{transfers.js → data/routes/transfers.js} +13 -13
  113. package/dist/apps/data/routes/transfers.js.map +1 -0
  114. package/dist/apps/{verified-tokens.d.ts → data/routes/verified-tokens.d.ts} +6 -6
  115. package/dist/apps/data/routes/verified-tokens.d.ts.map +1 -0
  116. package/dist/apps/{verified-tokens.js → data/routes/verified-tokens.js} +9 -9
  117. package/dist/apps/data/routes/verified-tokens.js.map +1 -0
  118. package/dist/apps/{webhooks.d.ts → data/routes/webhooks.d.ts} +17 -17
  119. package/dist/apps/{webhooks.d.ts.map → data/routes/webhooks.d.ts.map} +1 -1
  120. package/dist/apps/{webhooks.js → data/routes/webhooks.js} +38 -34
  121. package/dist/apps/data/routes/webhooks.js.map +1 -0
  122. package/dist/apps/docs/App.d.ts +29 -0
  123. package/dist/apps/docs/App.d.ts.map +1 -0
  124. package/dist/apps/docs/App.js +54 -0
  125. package/dist/apps/docs/App.js.map +1 -0
  126. package/dist/{internal/OpenApi.fonts.d.ts → apps/docs/fonts.d.ts} +1 -1
  127. package/dist/apps/docs/fonts.d.ts.map +1 -0
  128. package/dist/{internal/OpenApi.fonts.js → apps/docs/fonts.js} +1 -1
  129. package/dist/apps/docs/fonts.js.map +1 -0
  130. package/dist/apps/docs/index.d.ts +2 -0
  131. package/dist/apps/docs/index.d.ts.map +1 -0
  132. package/dist/apps/docs/index.js +2 -0
  133. package/dist/apps/docs/index.js.map +1 -0
  134. package/dist/{internal/OpenApi.logo.d.ts → apps/docs/logo.d.ts} +1 -1
  135. package/dist/apps/docs/logo.d.ts.map +1 -0
  136. package/dist/{internal/OpenApi.logo.js → apps/docs/logo.js} +1 -1
  137. package/dist/apps/docs/logo.js.map +1 -0
  138. package/dist/apps/docs/reference.d.ts +21 -0
  139. package/dist/apps/docs/reference.d.ts.map +1 -0
  140. package/dist/apps/docs/reference.js +112 -0
  141. package/dist/apps/docs/reference.js.map +1 -0
  142. package/dist/apps/index.d.ts +5 -0
  143. package/dist/apps/index.d.ts.map +1 -0
  144. package/dist/apps/index.js +5 -0
  145. package/dist/apps/index.js.map +1 -0
  146. package/dist/apps/mcp.d.ts +76 -0
  147. package/dist/apps/mcp.d.ts.map +1 -0
  148. package/dist/apps/mcp.js +188 -0
  149. package/dist/apps/mcp.js.map +1 -0
  150. package/dist/apps/metadata.d.ts +7 -0
  151. package/dist/apps/metadata.d.ts.map +1 -0
  152. package/dist/apps/metadata.js +15 -0
  153. package/dist/apps/metadata.js.map +1 -0
  154. package/dist/apps/platform/App.d.ts +5262 -0
  155. package/dist/apps/platform/App.d.ts.map +1 -0
  156. package/dist/apps/platform/App.js +84 -0
  157. package/dist/apps/platform/App.js.map +1 -0
  158. package/dist/apps/platform/Billing.d.ts +279 -0
  159. package/dist/apps/platform/Billing.d.ts.map +1 -0
  160. package/dist/apps/platform/Billing.js +477 -0
  161. package/dist/apps/platform/Billing.js.map +1 -0
  162. package/dist/apps/platform/index.d.ts +3 -0
  163. package/dist/apps/platform/index.d.ts.map +1 -0
  164. package/dist/apps/platform/index.js +3 -0
  165. package/dist/apps/platform/index.js.map +1 -0
  166. package/dist/apps/platform/routes/api-keys.d.ts +973 -0
  167. package/dist/apps/platform/routes/api-keys.d.ts.map +1 -0
  168. package/dist/apps/platform/routes/api-keys.js +430 -0
  169. package/dist/apps/platform/routes/api-keys.js.map +1 -0
  170. package/dist/apps/platform/routes/billing.d.ts +1520 -0
  171. package/dist/apps/platform/routes/billing.d.ts.map +1 -0
  172. package/dist/apps/platform/routes/billing.js +795 -0
  173. package/dist/apps/platform/routes/billing.js.map +1 -0
  174. package/dist/apps/platform/routes/invitations.d.ts +851 -0
  175. package/dist/apps/platform/routes/invitations.d.ts.map +1 -0
  176. package/dist/apps/platform/routes/invitations.js +471 -0
  177. package/dist/apps/platform/routes/invitations.js.map +1 -0
  178. package/dist/apps/platform/routes/me.d.ts +95 -0
  179. package/dist/apps/platform/routes/me.d.ts.map +1 -0
  180. package/dist/apps/platform/routes/me.js +81 -0
  181. package/dist/apps/platform/routes/me.js.map +1 -0
  182. package/dist/apps/platform/routes/members.d.ts +554 -0
  183. package/dist/apps/platform/routes/members.d.ts.map +1 -0
  184. package/dist/apps/platform/routes/members.js +251 -0
  185. package/dist/apps/platform/routes/members.js.map +1 -0
  186. package/dist/apps/platform/routes/orgs.d.ts +596 -0
  187. package/dist/apps/platform/routes/orgs.d.ts.map +1 -0
  188. package/dist/apps/platform/routes/orgs.js +276 -0
  189. package/dist/apps/platform/routes/orgs.js.map +1 -0
  190. package/dist/apps/platform/routes/projects.d.ts +740 -0
  191. package/dist/apps/platform/routes/projects.d.ts.map +1 -0
  192. package/dist/apps/platform/routes/projects.js +286 -0
  193. package/dist/apps/platform/routes/projects.js.map +1 -0
  194. package/dist/apps/platform/routes/scopes.d.ts +107 -0
  195. package/dist/apps/platform/routes/scopes.d.ts.map +1 -0
  196. package/dist/apps/platform/routes/scopes.js +66 -0
  197. package/dist/apps/platform/routes/scopes.js.map +1 -0
  198. package/dist/apps/platform/routes/usage.d.ts +588 -0
  199. package/dist/apps/platform/routes/usage.d.ts.map +1 -0
  200. package/dist/apps/platform/routes/usage.js +391 -0
  201. package/dist/apps/platform/routes/usage.js.map +1 -0
  202. package/dist/apps/relay/App.d.ts +44 -0
  203. package/dist/apps/relay/App.d.ts.map +1 -0
  204. package/dist/apps/relay/App.js +211 -0
  205. package/dist/apps/relay/App.js.map +1 -0
  206. package/dist/apps/relay/Sponsorships.d.ts +107 -0
  207. package/dist/apps/relay/Sponsorships.d.ts.map +1 -0
  208. package/dist/apps/relay/Sponsorships.js +194 -0
  209. package/dist/apps/relay/Sponsorships.js.map +1 -0
  210. package/dist/apps/relay/index.d.ts +3 -0
  211. package/dist/apps/relay/index.d.ts.map +1 -0
  212. package/dist/apps/relay/index.js +3 -0
  213. package/dist/apps/relay/index.js.map +1 -0
  214. package/dist/cli/Cli.d.ts +9 -2
  215. package/dist/cli/Cli.d.ts.map +1 -1
  216. package/dist/cli/Cli.js +12 -4
  217. package/dist/cli/Cli.js.map +1 -1
  218. package/dist/cli/commands/admin.d.ts +5 -1
  219. package/dist/cli/commands/admin.d.ts.map +1 -1
  220. package/dist/cli/commands/admin.js +82 -3
  221. package/dist/cli/commands/admin.js.map +1 -1
  222. package/dist/db/Db.d.ts +17 -0
  223. package/dist/db/Db.d.ts.map +1 -1
  224. package/dist/db/Db.js +254 -0
  225. package/dist/db/Db.js.map +1 -1
  226. package/dist/db/Schema.d.ts +159 -0
  227. package/dist/db/Schema.d.ts.map +1 -1
  228. package/dist/db/Schema.js +187 -2
  229. package/dist/db/Schema.js.map +1 -1
  230. package/dist/db/tables/billingSettings.d.ts +55 -0
  231. package/dist/db/tables/billingSettings.d.ts.map +1 -0
  232. package/dist/db/tables/billingSettings.js +70 -0
  233. package/dist/db/tables/billingSettings.js.map +1 -0
  234. package/dist/db/tables/invitations.d.ts +84 -0
  235. package/dist/db/tables/invitations.d.ts.map +1 -0
  236. package/dist/db/tables/invitations.js +165 -0
  237. package/dist/db/tables/invitations.js.map +1 -0
  238. package/dist/db/tables/memberships.d.ts +108 -0
  239. package/dist/db/tables/memberships.d.ts.map +1 -0
  240. package/dist/db/tables/memberships.js +153 -0
  241. package/dist/db/tables/memberships.js.map +1 -0
  242. package/dist/db/tables/organizations.d.ts +48 -0
  243. package/dist/db/tables/organizations.d.ts.map +1 -1
  244. package/dist/db/tables/organizations.js +97 -2
  245. package/dist/db/tables/organizations.js.map +1 -1
  246. package/dist/db/tables/projects.d.ts +65 -0
  247. package/dist/db/tables/projects.d.ts.map +1 -0
  248. package/dist/db/tables/projects.js +75 -0
  249. package/dist/db/tables/projects.js.map +1 -0
  250. package/dist/db/tables/requestUsage.d.ts +121 -0
  251. package/dist/db/tables/requestUsage.d.ts.map +1 -0
  252. package/dist/db/tables/requestUsage.js +150 -0
  253. package/dist/db/tables/requestUsage.js.map +1 -0
  254. package/dist/db/tables/sponsoredTransactions.d.ts +239 -0
  255. package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -0
  256. package/dist/db/tables/sponsoredTransactions.js +314 -0
  257. package/dist/db/tables/sponsoredTransactions.js.map +1 -0
  258. package/dist/db/tables/stripeCustomers.d.ts +81 -0
  259. package/dist/db/tables/stripeCustomers.d.ts.map +1 -0
  260. package/dist/db/tables/stripeCustomers.js +120 -0
  261. package/dist/db/tables/stripeCustomers.js.map +1 -0
  262. package/dist/db/tables/users.d.ts +48 -0
  263. package/dist/db/tables/users.d.ts.map +1 -0
  264. package/dist/db/tables/users.js +65 -0
  265. package/dist/db/tables/users.js.map +1 -0
  266. package/dist/handlers/internal/executionError.d.ts.map +1 -1
  267. package/dist/handlers/internal/executionError.js +2 -0
  268. package/dist/handlers/internal/executionError.js.map +1 -1
  269. package/dist/handlers/internal/multisig.d.ts +2 -6
  270. package/dist/handlers/internal/multisig.d.ts.map +1 -1
  271. package/dist/handlers/internal/multisig.js +11 -36
  272. package/dist/handlers/internal/multisig.js.map +1 -1
  273. package/dist/handlers/internal/sponsorship.d.ts +35 -2
  274. package/dist/handlers/internal/sponsorship.d.ts.map +1 -1
  275. package/dist/handlers/internal/sponsorship.js +53 -14
  276. package/dist/handlers/internal/sponsorship.js.map +1 -1
  277. package/dist/handlers/internal/utils.d.ts +10 -3
  278. package/dist/handlers/internal/utils.d.ts.map +1 -1
  279. package/dist/handlers/internal/utils.js +14 -6
  280. package/dist/handlers/internal/utils.js.map +1 -1
  281. package/dist/handlers/relay.d.ts +6 -2
  282. package/dist/handlers/relay.d.ts.map +1 -1
  283. package/dist/handlers/relay.js +16 -2
  284. package/dist/handlers/relay.js.map +1 -1
  285. package/dist/index.d.ts +1 -0
  286. package/dist/index.d.ts.map +1 -1
  287. package/dist/index.js +1 -0
  288. package/dist/index.js.map +1 -1
  289. package/dist/internal/Auth.d.ts +222 -6
  290. package/dist/internal/Auth.d.ts.map +1 -1
  291. package/dist/internal/Auth.js +625 -74
  292. package/dist/internal/Auth.js.map +1 -1
  293. package/dist/internal/Cache.d.ts.map +1 -1
  294. package/dist/internal/Cache.js +4 -1
  295. package/dist/internal/Cache.js.map +1 -1
  296. package/dist/internal/EdgeCache.d.ts +10 -0
  297. package/dist/internal/EdgeCache.d.ts.map +1 -1
  298. package/dist/internal/EdgeCache.js +39 -8
  299. package/dist/internal/EdgeCache.js.map +1 -1
  300. package/dist/internal/Fees.d.ts +27 -0
  301. package/dist/internal/Fees.d.ts.map +1 -0
  302. package/dist/internal/Fees.js +48 -0
  303. package/dist/internal/Fees.js.map +1 -0
  304. package/dist/internal/FundingRoutes.d.ts +183 -0
  305. package/dist/internal/FundingRoutes.d.ts.map +1 -0
  306. package/dist/internal/FundingRoutes.js +490 -0
  307. package/dist/internal/FundingRoutes.js.map +1 -0
  308. package/dist/internal/Id.d.ts +15 -0
  309. package/dist/internal/Id.d.ts.map +1 -0
  310. package/dist/internal/Id.js +22 -0
  311. package/dist/internal/Id.js.map +1 -0
  312. package/dist/internal/Log.d.ts +9 -3
  313. package/dist/internal/Log.d.ts.map +1 -1
  314. package/dist/internal/Log.js +7 -0
  315. package/dist/internal/Log.js.map +1 -1
  316. package/dist/internal/OpenApi.d.ts +10 -20
  317. package/dist/internal/OpenApi.d.ts.map +1 -1
  318. package/dist/internal/OpenApi.js +8 -58
  319. package/dist/internal/OpenApi.js.map +1 -1
  320. package/dist/internal/RateLimit.d.ts +25 -0
  321. package/dist/internal/RateLimit.d.ts.map +1 -1
  322. package/dist/internal/RateLimit.js +51 -0
  323. package/dist/internal/RateLimit.js.map +1 -1
  324. package/dist/internal/Response.d.ts +1 -1
  325. package/dist/internal/Response.d.ts.map +1 -1
  326. package/dist/internal/Schema.d.ts +8 -0
  327. package/dist/internal/Schema.d.ts.map +1 -1
  328. package/dist/internal/Schema.js +7 -0
  329. package/dist/internal/Schema.js.map +1 -1
  330. package/dist/internal/Store.d.ts +7 -1
  331. package/dist/internal/Store.d.ts.map +1 -1
  332. package/dist/internal/Store.js +16 -2
  333. package/dist/internal/Store.js.map +1 -1
  334. package/dist/internal/Viem.d.ts +84 -0
  335. package/dist/internal/Viem.d.ts.map +1 -1
  336. package/dist/internal/Webhooks.d.ts +1 -1
  337. package/dist/internal/Webhooks.d.ts.map +1 -1
  338. package/dist/internal/Webhooks.js +3 -3
  339. package/dist/internal/Webhooks.js.map +1 -1
  340. package/package.json +33 -6
  341. package/src/ApiKey.ts +10 -2
  342. package/src/ApiKeys.test.ts +155 -1
  343. package/src/ApiKeys.ts +203 -1
  344. package/src/App.test.ts +64 -75741
  345. package/src/App.ts +218 -420
  346. package/src/Client.test-d.ts +657 -15
  347. package/src/Scope.ts +26 -4
  348. package/src/Webhooks.ts +2 -2
  349. package/src/admin/apps/api-keys.test.ts +51 -0
  350. package/src/admin/apps/api-keys.ts +84 -0
  351. package/src/admin/apps/scopes.test.ts +11 -1
  352. package/src/admin/apps/scopes.ts +3 -0
  353. package/src/admin/apps/verified-tokens.ts +1 -1
  354. package/src/admin/ui/src/components/Page.tsx +2 -3
  355. package/src/admin/ui/src/routes/__root.tsx +2 -2
  356. package/src/admin/ui/src/routes/api-keys.tsx +2 -2
  357. package/src/admin/ui.generated.ts +13 -13
  358. package/src/analytics/Analytics.test.ts +527 -0
  359. package/src/analytics/Analytics.ts +318 -0
  360. package/src/analytics/tables/requestEvents.ts +523 -0
  361. package/src/apps/composition.test.ts +66 -0
  362. package/src/apps/data/App.test.ts +72 -0
  363. package/src/apps/data/App.ts +330 -0
  364. package/src/apps/data/index.ts +2 -0
  365. package/src/apps/{activities.test.ts → data/routes/activities.test.ts} +3 -3
  366. package/src/apps/{activities.ts → data/routes/activities.ts} +15 -15
  367. package/src/apps/{addresses.ts → data/routes/addresses.ts} +6 -6
  368. package/src/apps/{balances.test.ts → data/routes/balances.test.ts} +2 -2
  369. package/src/apps/{balances.ts → data/routes/balances.ts} +46 -12
  370. package/src/apps/{blocks.test.ts → data/routes/blocks.test.ts} +3 -3
  371. package/src/apps/{blocks.ts → data/routes/blocks.ts} +14 -14
  372. package/src/apps/{coingecko.test.ts → data/routes/coingecko.test.ts} +2 -2
  373. package/src/apps/{coingecko.ts → data/routes/coingecko.ts} +14 -14
  374. package/src/apps/{exchanges.test.ts → data/routes/exchanges.test.ts} +1 -1
  375. package/src/apps/{exchanges.ts → data/routes/exchanges.ts} +20 -20
  376. package/src/apps/{fee-amm.test.ts → data/routes/fee-amm.test.ts} +3 -3
  377. package/src/apps/{fee-amm.ts → data/routes/fee-amm.ts} +18 -18
  378. package/src/apps/data/routes/funding.test.ts +214 -0
  379. package/src/apps/data/routes/funding.ts +584 -0
  380. package/src/apps/{indexer.test.ts → data/routes/indexer.test.ts} +26 -5
  381. package/src/apps/{indexer.ts → data/routes/indexer.ts} +13 -10
  382. package/src/apps/{receipts.test.ts → data/routes/receipts.test.ts} +3 -3
  383. package/src/apps/{receipts.ts → data/routes/receipts.ts} +15 -22
  384. package/src/apps/{rpc.test.ts → data/routes/rpc.test.ts} +122 -7
  385. package/src/apps/{rpc.ts → data/routes/rpc.ts} +38 -8
  386. package/src/apps/{tokenlist.test.ts → data/routes/tokenlist.test.ts} +3 -3
  387. package/src/apps/{tokenlist.ts → data/routes/tokenlist.ts} +9 -9
  388. package/src/apps/{tokens.test.ts → data/routes/tokens.test.ts} +2 -2
  389. package/src/apps/{tokens.ts → data/routes/tokens.ts} +20 -20
  390. package/src/apps/{transactions.test.ts → data/routes/transactions.test.ts} +3 -3
  391. package/src/apps/{transactions.ts → data/routes/transactions.ts} +15 -15
  392. package/src/apps/{transfers.test.ts → data/routes/transfers.test.ts} +2 -2
  393. package/src/apps/{transfers.ts → data/routes/transfers.ts} +15 -15
  394. package/src/apps/{verified-tokens.test.ts → data/routes/verified-tokens.test.ts} +3 -3
  395. package/src/apps/{verified-tokens.ts → data/routes/verified-tokens.ts} +10 -10
  396. package/src/apps/{webhooks.test.ts → data/routes/webhooks.test.ts} +7 -7
  397. package/src/apps/{webhooks.ts → data/routes/webhooks.ts} +50 -43
  398. package/src/apps/docs/App.ts +65 -0
  399. package/src/apps/docs/index.ts +1 -0
  400. package/src/apps/docs/reference.ts +132 -0
  401. package/src/apps/index.ts +4 -0
  402. package/src/apps/mcp.test.ts +206 -0
  403. package/src/apps/mcp.ts +250 -0
  404. package/src/apps/metadata.ts +23 -0
  405. package/src/apps/platform/App.ts +169 -0
  406. package/src/apps/platform/Billing.test.ts +530 -0
  407. package/src/apps/platform/Billing.ts +682 -0
  408. package/src/apps/platform/index.ts +2 -0
  409. package/src/apps/platform/routes/api-keys.test.ts +493 -0
  410. package/src/apps/platform/routes/api-keys.ts +543 -0
  411. package/src/apps/platform/routes/billing.test.ts +1100 -0
  412. package/src/apps/platform/routes/billing.ts +966 -0
  413. package/src/apps/platform/routes/invitations.test.ts +409 -0
  414. package/src/apps/platform/routes/invitations.ts +587 -0
  415. package/src/apps/platform/routes/me.test.ts +351 -0
  416. package/src/apps/platform/routes/me.ts +106 -0
  417. package/src/apps/platform/routes/members.test.ts +195 -0
  418. package/src/apps/platform/routes/members.ts +313 -0
  419. package/src/apps/platform/routes/orgs.test.ts +313 -0
  420. package/src/apps/platform/routes/orgs.ts +365 -0
  421. package/src/apps/platform/routes/projects.test.ts +181 -0
  422. package/src/apps/platform/routes/projects.ts +367 -0
  423. package/src/apps/platform/routes/scopes.test.ts +29 -0
  424. package/src/apps/platform/routes/scopes.ts +85 -0
  425. package/src/apps/platform/routes/usage.test.ts +422 -0
  426. package/src/apps/platform/routes/usage.ts +527 -0
  427. package/src/apps/relay/App.test.ts +933 -0
  428. package/src/apps/relay/App.ts +236 -0
  429. package/src/apps/relay/Sponsorships.test.ts +375 -0
  430. package/src/apps/relay/Sponsorships.ts +306 -0
  431. package/src/apps/relay/index.ts +2 -0
  432. package/src/cli/Cli.test.ts +25 -2
  433. package/src/cli/Cli.ts +18 -3
  434. package/src/cli/commands/admin.test.ts +83 -0
  435. package/src/cli/commands/admin.ts +91 -3
  436. package/src/db/Db.ts +296 -0
  437. package/src/db/Schema.ts +241 -2
  438. package/src/db/tables/billingSettings.test.ts +87 -0
  439. package/src/db/tables/billingSettings.ts +108 -0
  440. package/src/db/tables/invitations.ts +198 -0
  441. package/src/db/tables/memberships.ts +208 -0
  442. package/src/db/tables/organizations.test.ts +38 -2
  443. package/src/db/tables/organizations.ts +117 -2
  444. package/src/db/tables/projects.ts +107 -0
  445. package/src/db/tables/requestUsage.ts +231 -0
  446. package/src/db/tables/sponsoredTransactions.test.ts +325 -0
  447. package/src/db/tables/sponsoredTransactions.ts +464 -0
  448. package/src/db/tables/stripeCustomers.test.ts +109 -0
  449. package/src/db/tables/stripeCustomers.ts +162 -0
  450. package/src/db/tables/users.ts +85 -0
  451. package/src/handlers/internal/executionError.ts +2 -0
  452. package/src/handlers/internal/multisig.test.ts +84 -10
  453. package/src/handlers/internal/multisig.ts +15 -50
  454. package/src/handlers/internal/sponsorship.ts +106 -20
  455. package/src/handlers/internal/utils.ts +19 -7
  456. package/src/handlers/{internal/relay.localnet.test.ts → relay.test.ts} +2 -2
  457. package/src/handlers/relay.ts +23 -5
  458. package/src/index.ts +1 -0
  459. package/src/internal/Auth.test.ts +491 -25
  460. package/src/internal/Auth.ts +839 -84
  461. package/src/internal/Cache.ts +5 -1
  462. package/src/internal/Cursor.test.ts +3 -2
  463. package/src/internal/EdgeCache.test.ts +27 -4
  464. package/src/internal/EdgeCache.ts +47 -9
  465. package/src/internal/Fees.test.ts +32 -0
  466. package/src/internal/Fees.ts +51 -0
  467. package/src/internal/FundingRoutes.ts +583 -0
  468. package/src/internal/Id.test.ts +17 -0
  469. package/src/internal/Id.ts +24 -0
  470. package/src/internal/Log.test.ts +2 -1
  471. package/src/internal/Log.ts +16 -3
  472. package/src/internal/MetricSink.ts +1 -1
  473. package/src/internal/OpenApi.ts +12 -60
  474. package/src/internal/RateLimit.test.ts +84 -0
  475. package/src/internal/RateLimit.ts +70 -0
  476. package/src/internal/Response.ts +1 -1
  477. package/src/internal/Schema.ts +8 -0
  478. package/src/internal/Store.test.ts +52 -0
  479. package/src/internal/Store.ts +24 -5
  480. package/src/internal/Tidx.local.test.ts +1 -1
  481. package/src/internal/VerifiedTokens.test.ts +1 -1
  482. package/src/internal/WebhookDestination.test.ts +1 -1
  483. package/src/internal/Webhooks.test.ts +7 -7
  484. package/src/internal/Webhooks.ts +4 -4
  485. package/dist/apps/activities.js.map +0 -1
  486. package/dist/apps/addresses.js.map +0 -1
  487. package/dist/apps/balances.d.ts.map +0 -1
  488. package/dist/apps/balances.js.map +0 -1
  489. package/dist/apps/blocks.d.ts.map +0 -1
  490. package/dist/apps/blocks.js.map +0 -1
  491. package/dist/apps/coingecko.d.ts.map +0 -1
  492. package/dist/apps/coingecko.js.map +0 -1
  493. package/dist/apps/exchanges.d.ts.map +0 -1
  494. package/dist/apps/exchanges.js.map +0 -1
  495. package/dist/apps/fee-amm.d.ts.map +0 -1
  496. package/dist/apps/fee-amm.js.map +0 -1
  497. package/dist/apps/indexer.d.ts.map +0 -1
  498. package/dist/apps/indexer.js.map +0 -1
  499. package/dist/apps/receipts.d.ts.map +0 -1
  500. package/dist/apps/receipts.js.map +0 -1
  501. package/dist/apps/rpc.d.ts.map +0 -1
  502. package/dist/apps/rpc.js.map +0 -1
  503. package/dist/apps/tokenlist.d.ts.map +0 -1
  504. package/dist/apps/tokenlist.js.map +0 -1
  505. package/dist/apps/tokens.d.ts.map +0 -1
  506. package/dist/apps/tokens.js.map +0 -1
  507. package/dist/apps/transactions.d.ts.map +0 -1
  508. package/dist/apps/transactions.js.map +0 -1
  509. package/dist/apps/transfers.d.ts.map +0 -1
  510. package/dist/apps/transfers.js.map +0 -1
  511. package/dist/apps/verified-tokens.d.ts.map +0 -1
  512. package/dist/apps/verified-tokens.js.map +0 -1
  513. package/dist/apps/webhooks.js.map +0 -1
  514. package/dist/internal/OpenApi.fonts.d.ts.map +0 -1
  515. package/dist/internal/OpenApi.fonts.js.map +0 -1
  516. package/dist/internal/OpenApi.logo.d.ts.map +0 -1
  517. package/dist/internal/OpenApi.logo.js.map +0 -1
  518. package/src/handlers/internal/multisig.localnet.test.ts +0 -146
  519. /package/src/{internal/OpenApi.fonts.ts → apps/docs/fonts.ts} +0 -0
  520. /package/src/{internal/OpenApi.logo.ts → apps/docs/logo.ts} +0 -0
@@ -0,0 +1,464 @@
1
+ import type { ExpressionBuilder, Selectable } from 'kysely'
2
+ import { sql } from 'kysely'
3
+
4
+ import * as Id from '../../internal/Id.js'
5
+ import type * as Db from '../Db.js'
6
+ import type * as db_Schema from '../Schema.js'
7
+
8
+ /** Columns of the `sponsored_transactions` table, derived from `Schema.SponsoredTransaction`. */
9
+ export type Table = db_Schema.SponsoredTransaction
10
+
11
+ /** A stored sponsored-transaction row. */
12
+ export type Record = Selectable<Table>
13
+
14
+ /**
15
+ * Records one sponsorship commitment as `pending`, keyed by its fee-payer
16
+ * sign payload. Written after fee-payer signing and before any broadcast, so
17
+ * no sponsored transaction goes unrecorded; a later recording site for the
18
+ * same envelope (raw submission after a fill intent) upgrades the row with
19
+ * its transaction hash, reopening failed rows for finalization.
20
+ *
21
+ * @param db - The database.
22
+ * @param input - Attribution and envelope facts captured at signing time.
23
+ * @returns The stored record.
24
+ */
25
+ export async function upsert(db: Db.Db, input: upsert.Input): Promise<Record> {
26
+ const record: Record = {
27
+ apiKeyId: input.apiKeyId,
28
+ billable: input.billable,
29
+ chainId: input.chainId,
30
+ createdAt: new Date().toISOString(),
31
+ currency: input.currency ?? null,
32
+ environment: input.environment,
33
+ feeAmount: null,
34
+ feeMax: input.feeMax ?? null,
35
+ feeToken: input.feeToken ?? null,
36
+ finalizedAt: null,
37
+ id: Id.generate('stx'),
38
+ meterReportedAt: null,
39
+ orgId: input.orgId,
40
+ projectId: input.projectId,
41
+ signPayload: input.signPayload,
42
+ status: 'pending',
43
+ transaction: input.transaction,
44
+ transactionHash: input.transactionHash ?? null,
45
+ }
46
+ return await db.kysely
47
+ .insertInto('sponsored_transactions')
48
+ .values(record)
49
+ .onConflict((oc) =>
50
+ oc.column('signPayload').doUpdateSet((eb) => ({
51
+ // Same sign payload = same envelope = same cap; only fills older nulls.
52
+ currency: eb.fn.coalesce(eb.ref('sponsored_transactions.currency'), eb.ref('excluded.currency')), // prettier-ignore
53
+ feeMax: eb.fn.coalesce(eb.ref('sponsored_transactions.feeMax'), eb.ref('excluded.feeMax')),
54
+ // A raw submission fills the intent's hash; a duplicate intent never
55
+ // clears an already-known hash.
56
+ transactionHash: eb.fn.coalesce(
57
+ eb.ref('excluded.transactionHash'),
58
+ eb.ref('sponsored_transactions.transactionHash'),
59
+ ),
60
+ createdAt: sql`CASE WHEN ${eb.ref('sponsored_transactions.status')} = 'failed' THEN ${eb.ref('excluded.createdAt')} ELSE ${eb.ref('sponsored_transactions.createdAt')} END`,
61
+ finalizedAt: sql`CASE WHEN ${eb.ref('sponsored_transactions.status')} = 'failed' THEN NULL ELSE ${eb.ref('sponsored_transactions.finalizedAt')} END`,
62
+ status: sql`CASE WHEN ${eb.ref('sponsored_transactions.status')} = 'failed' THEN 'pending' ELSE ${eb.ref('sponsored_transactions.status')} END`,
63
+ })),
64
+ )
65
+ .returningAll()
66
+ .executeTakeFirstOrThrow()
67
+ }
68
+
69
+ export declare namespace upsert {
70
+ /** Attribution and envelope facts recorded at sponsorship time. */
71
+ type Input = {
72
+ /** API key id (`key_…`) that requested sponsorship. */
73
+ apiKeyId: string
74
+ /** Whether the sponsorship accrues billable spend; false for sandbox. */
75
+ billable: boolean
76
+ /** Chain the sponsored transaction targets. */
77
+ chainId: number
78
+ /** Lowercase fee currency snapshot (`usd`), when resolved. */
79
+ currency?: string | undefined
80
+ /** Key environment the sponsorship was requested under. */
81
+ environment: Table['environment']
82
+ /** Signed fee cap (`gas × maxFeePerGas`) in base units, when known. */
83
+ feeMax?: string | undefined
84
+ /** Fee token the sponsorship resolved, when known. */
85
+ feeToken?: string | undefined
86
+ /** Organization id (`org_…`) the spend attributes to. */
87
+ orgId: string
88
+ /** Project id (`prj_…`) the spend attributes to. */
89
+ projectId: string
90
+ /** Fee-payer sign payload — a stable identity for the sponsored envelope. */
91
+ signPayload: string
92
+ /** Serialized sponsored transaction, kept for observability beyond log retention. */
93
+ transaction: string
94
+ /** Transaction hash; omit for fill intents, whose senders have not signed yet. */
95
+ transactionHash?: string | undefined
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Reads one sponsorship row by id.
101
+ *
102
+ * @param db - The database.
103
+ * @param id - The sponsored-transaction id (`stx_…`).
104
+ * @returns The record, or `undefined` when absent.
105
+ */
106
+ export function get(db: Db.Db, id: string): Promise<Record | undefined> {
107
+ return db.kysely
108
+ .selectFrom('sponsored_transactions')
109
+ .selectAll()
110
+ .where('id', '=', id)
111
+ .executeTakeFirst()
112
+ }
113
+
114
+ /**
115
+ * Lists pending sponsorships, oldest first, for the finalization job.
116
+ *
117
+ * @param db - The database.
118
+ * @param options - Paging options.
119
+ * @returns The pending records.
120
+ */
121
+ export function listPending(db: Db.Db, options: listPending.Options = {}): Promise<Record[]> {
122
+ let query = db.kysely
123
+ .selectFrom('sponsored_transactions')
124
+ .selectAll()
125
+ .where('status', '=', 'pending')
126
+ // `id` breaks same-millisecond `createdAt` ties for a deterministic scan.
127
+ .orderBy('createdAt', 'asc')
128
+ .orderBy('id', 'asc')
129
+ if (options.limit !== undefined) query = query.limit(options.limit)
130
+ return query.execute()
131
+ }
132
+
133
+ export declare namespace listPending {
134
+ /** Options for {@link listPending}. */
135
+ type Options = {
136
+ /** Maximum rows to return. */
137
+ limit?: number | undefined
138
+ }
139
+ }
140
+
141
+ /**
142
+ * Lists hash-less fill intents, oldest first, for the reconciliation pass.
143
+ * Failed intents are included only when the caller supplies a recovery floor.
144
+ *
145
+ * @param db - The database.
146
+ * @param options - Paging options.
147
+ * @returns The pending intents.
148
+ */
149
+ export function listIntents(db: Db.Db, options: listIntents.Options = {}): Promise<Record[]> {
150
+ let query = db.kysely
151
+ .selectFrom('sponsored_transactions')
152
+ .selectAll()
153
+ .where(intentStatus(options.failedSince))
154
+ .where('transactionHash', 'is', null)
155
+ // `pending` sorts after `failed`; descending keeps active intents first.
156
+ .orderBy('status', 'desc')
157
+ .orderBy('createdAt', 'asc')
158
+ .orderBy('id', 'asc')
159
+ if (options.limit !== undefined) query = query.limit(options.limit)
160
+ return query.execute()
161
+ }
162
+
163
+ export declare namespace listIntents {
164
+ /** Options for {@link listIntents}. */
165
+ type Options = listPending.Options & {
166
+ /** Include failed intents whose terminal timestamp is at or after this ISO timestamp. */
167
+ failedSince?: string | undefined
168
+ }
169
+ }
170
+
171
+ /**
172
+ * Fills a reconciled intent's transaction hash and reopens failed intents.
173
+ * A no-op once another pass already assigned the hash.
174
+ *
175
+ * @param db - The database.
176
+ * @param id - The sponsored-transaction id (`stx_…`).
177
+ * @param transactionHash - The matched on-chain transaction hash.
178
+ */
179
+ export async function assignTransactionHash(
180
+ db: Db.Db,
181
+ id: string,
182
+ transactionHash: string,
183
+ ): Promise<void> {
184
+ await db.kysely
185
+ .updateTable('sponsored_transactions')
186
+ .set({ finalizedAt: null, status: 'pending', transactionHash })
187
+ .where('id', '=', id)
188
+ .where('status', 'in', ['failed', 'pending'])
189
+ .where('transactionHash', 'is', null)
190
+ .execute()
191
+ }
192
+
193
+ /**
194
+ * Transitions a pending sponsorship to `finalized` with the receipt's actual
195
+ * fee. A no-op when the row is no longer pending (idempotent re-runs).
196
+ *
197
+ * @param db - The database.
198
+ * @param id - The sponsored-transaction id (`stx_…`).
199
+ * @param input - The finalized fee facts.
200
+ */
201
+ export async function finalize(db: Db.Db, id: string, input: finalize.Input): Promise<void> {
202
+ await db.kysely
203
+ .updateTable('sponsored_transactions')
204
+ .set({ feeAmount: input.feeAmount, finalizedAt: input.finalizedAt, status: 'finalized' })
205
+ .where('id', '=', id)
206
+ .where('status', '=', 'pending')
207
+ .execute()
208
+ }
209
+
210
+ export declare namespace finalize {
211
+ /** Fee facts resolved from the transaction receipt. */
212
+ type Input = {
213
+ /** Actual fee paid in fee-token base units. */
214
+ feeAmount: string
215
+ /** When the sponsorship finalized (ISO 8601). */
216
+ finalizedAt: string
217
+ }
218
+ }
219
+
220
+ /**
221
+ * Transitions a pending sponsorship to `failed` (never landed within the
222
+ * pending TTL). A no-op when the row is no longer pending.
223
+ *
224
+ * @param db - The database.
225
+ * @param id - The sponsored-transaction id (`stx_…`).
226
+ * @param finalizedAt - When the sponsorship was marked failed (ISO 8601).
227
+ */
228
+ export async function fail(db: Db.Db, id: string, finalizedAt: string): Promise<void> {
229
+ await db.kysely
230
+ .updateTable('sponsored_transactions')
231
+ .set({ finalizedAt, status: 'failed' })
232
+ .where('id', '=', id)
233
+ .where('status', '=', 'pending')
234
+ .execute()
235
+ }
236
+
237
+ /**
238
+ * Records a sponsorship and enforces the period spend limit atomically. A
239
+ * per-org advisory lock serializes concurrent sponsorships so each one's
240
+ * signed cap is counted before the limit check; without it a burst reads
241
+ * pre-burst spend and overshoots. Throws {@link PeriodSpendLimitError} (rolling
242
+ * back the row) when the limit would be exceeded.
243
+ *
244
+ * @param db - The database (primary; never a cached replica).
245
+ * @param input - The sponsorship to record.
246
+ * @param limit - The period spend limit to enforce.
247
+ * @returns The stored record.
248
+ */
249
+ export async function reserve(
250
+ db: Db.Db,
251
+ input: upsert.Input,
252
+ limit: reserve.Limit,
253
+ ): Promise<Record> {
254
+ return db.transaction(async (tx) => {
255
+ // `hashtext` maps the org id into the advisory-lock keyspace; the class
256
+ // namespaces it against other advisory-lock users. Released at tx end.
257
+ await sql`select pg_advisory_xact_lock(${billingLockClass}, hashtext(${input.orgId}))`.execute(tx.kysely) // prettier-ignore
258
+ const record = await upsert(tx, input)
259
+ if (record.billable) {
260
+ const committed = await spend(tx, {
261
+ chainIds: limit.chainIds,
262
+ orgId: input.orgId,
263
+ since: limit.since,
264
+ })
265
+ if (committed > limit.max) throw new PeriodSpendLimitError()
266
+ }
267
+ return record
268
+ })
269
+ }
270
+
271
+ /** Advisory-lock class namespacing per-org sponsorship serialization. */
272
+ const billingLockClass = 1112294220
273
+
274
+ export declare namespace reserve {
275
+ /** The period spend limit enforced by {@link reserve}. */
276
+ type Limit = {
277
+ /** Chains whose spend counts (mainnet chain ids). */
278
+ chainIds: readonly number[]
279
+ /** Limit in fee-token base units. */
280
+ max: bigint
281
+ /** Window start (ISO 8601). */
282
+ since: string
283
+ }
284
+ }
285
+
286
+ /**
287
+ * Billable spend committed since a cutoff, in fee-token base units: finalized
288
+ * rows at their actual fee plus pending rows at their signed fee cap, so
289
+ * in-flight sponsorships consume limit budget until receipts land.
290
+ *
291
+ * @param db - The database.
292
+ * @param options - Aggregation scope.
293
+ * @returns The committed spend in base units.
294
+ */
295
+ export async function spend(db: Db.Db, options: spend.Options): Promise<bigint> {
296
+ const { chainIds, environment = 'production', orgId, since } = options
297
+ const query = db.kysely
298
+ .selectFrom('sponsored_transactions')
299
+ .select([
300
+ sql<string>`coalesce(sum(case when status = 'finalized' then fee_amount::numeric else 0 end), 0)`.as('finalized'), // prettier-ignore
301
+ sql<string>`coalesce(sum(case when status = 'pending' then fee_max::numeric else 0 end), 0)`.as('pending'), // prettier-ignore
302
+ ])
303
+ .where('orgId', '=', orgId)
304
+ .where('environment', '=', environment)
305
+ // Redundant with the CASE arms, but lets the billing index's
306
+ // (org_id, status, created_at) shape serve the range scan.
307
+ .where('status', 'in', ['finalized', 'pending'])
308
+ .where('chainId', 'in', [...chainIds])
309
+ .where('createdAt', '>=', since)
310
+ // Production mirrors the sponsorship gate — only billable rows count. Sandbox
311
+ // rows are non-billable by construction, so the figure is display-only.
312
+ const row = await (environment === 'production' ? query.where('billable', '=', true) : query) //
313
+ .executeTakeFirstOrThrow()
314
+ return BigInt(row.finalized) + BigInt(row.pending)
315
+ }
316
+
317
+ export declare namespace spend {
318
+ /** Options for {@link spend}. */
319
+ type Options = {
320
+ /** Chains whose spend counts (mainnet chain ids). */
321
+ chainIds: readonly number[]
322
+ /** Environment whose spend counts; production mirrors the billing gate, sandbox is display-only. Defaults to `production`. */
323
+ environment?: Table['environment'] | undefined
324
+ /** Organization id (`org_…`) the spend attributes to. */
325
+ orgId: string
326
+ /** Window start (ISO 8601); rows created earlier are out of scope. */
327
+ since: string
328
+ }
329
+ }
330
+
331
+ /**
332
+ * Sponsorship usage bucketed by time for one organization: row counts and
333
+ * committed fees per bucket. Fee semantics mirror {@link spend}: finalized
334
+ * rows at their actual fee, pending rows at their signed cap, failed rows at
335
+ * zero. Buckets with no rows are omitted.
336
+ *
337
+ * @param db - The database.
338
+ * @param options - Aggregation scope.
339
+ * @returns Time-ordered usage buckets.
340
+ */
341
+ export async function usage(db: Db.Db, options: usage.Options): Promise<usage.Bucket[]> {
342
+ // TODO: convert to an OLAP-friendly approach (rollup table or columnar store)
343
+ // when per-org range scans grow slow.
344
+ const { environment, from, interval, orgId, projectId, to } = options
345
+ // `sql.lit` inlines the enum-validated interval so the GROUP BY expression
346
+ // matches the SELECT expression textually; truncation happens in UTC wall
347
+ // time regardless of the session time zone.
348
+ const bucket = sql`date_trunc(${sql.lit(interval)}, (created_at::timestamptz) at time zone 'UTC')`
349
+ let query = db.kysely
350
+ .selectFrom('sponsored_transactions')
351
+ .select([
352
+ sql<string>`to_char(${bucket}, 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"')`.as('timestamp'),
353
+ sql<string>`count(*)`.as('count'),
354
+ sql<string>`coalesce(sum(case when status = 'finalized' then fee_amount::numeric when status = 'pending' then fee_max::numeric else 0 end), 0)`.as('feeTotal'), // prettier-ignore
355
+ ])
356
+ .where('orgId', '=', orgId)
357
+ // Text-range bounds keep the (org_id, created_at) index serving the scan.
358
+ .where('createdAt', '>=', from)
359
+ .where('createdAt', '<', to)
360
+ .groupBy(bucket)
361
+ .orderBy(bucket)
362
+ if (environment !== undefined) query = query.where('environment', '=', environment)
363
+ if (projectId !== undefined) query = query.where('projectId', '=', projectId)
364
+ const rows = await query.execute()
365
+ return rows.map((row) => ({
366
+ count: Number(row.count),
367
+ feeTotal: BigInt(row.feeTotal),
368
+ timestamp: row.timestamp,
369
+ }))
370
+ }
371
+
372
+ export declare namespace usage {
373
+ /** One time bucket of sponsorship usage. */
374
+ type Bucket = {
375
+ /** Sponsored transactions recorded in the bucket, any status. */
376
+ count: number
377
+ /** Committed fees in fee-token base units: finalized fees plus pending caps. */
378
+ feeTotal: bigint
379
+ /** Bucket start (ISO 8601), aligned to UTC calendar boundaries. */
380
+ timestamp: string
381
+ }
382
+
383
+ /** Options for {@link usage}. */
384
+ type Options = {
385
+ /** Key environment to restrict to; omit for all. */
386
+ environment?: Table['environment'] | undefined
387
+ /** Window start (ISO 8601), inclusive. */
388
+ from: string
389
+ /** Bucket width, aligned to UTC calendar boundaries. */
390
+ interval: 'day' | 'hour' | 'month' | 'week'
391
+ /** Organization id (`org_…`) the usage attributes to. */
392
+ orgId: string
393
+ /** Project id (`prj_…`) to restrict to; omit for all. */
394
+ projectId?: string | undefined
395
+ /** Window end (ISO 8601), exclusive. */
396
+ to: string
397
+ }
398
+ }
399
+
400
+ /**
401
+ * Lists finalized billable rows not yet reported to the billing meter, oldest
402
+ * first, for the metering job.
403
+ *
404
+ * @param db - The database.
405
+ * @param options - Reporting scope.
406
+ * @returns The unreported records.
407
+ */
408
+ export function listUnreported(db: Db.Db, options: listUnreported.Options): Promise<Record[]> {
409
+ let query = db.kysely
410
+ .selectFrom('sponsored_transactions')
411
+ .selectAll()
412
+ .where('status', '=', 'finalized')
413
+ .where('billable', '=', true)
414
+ .where('chainId', 'in', [...options.chainIds])
415
+ .where('meterReportedAt', 'is', null)
416
+ // `id` breaks same-millisecond `finalizedAt` ties for a deterministic scan.
417
+ .orderBy('finalizedAt', 'asc')
418
+ .orderBy('id', 'asc')
419
+ if (options.limit !== undefined) query = query.limit(options.limit)
420
+ return query.execute()
421
+ }
422
+
423
+ export declare namespace listUnreported {
424
+ /** Options for {@link listUnreported}. */
425
+ type Options = {
426
+ /** Chains whose spend is metered (mainnet chain ids). */
427
+ chainIds: readonly number[]
428
+ /** Maximum rows to return. */
429
+ limit?: number | undefined
430
+ }
431
+ }
432
+
433
+ /**
434
+ * Marks a row as reported to the billing meter. A no-op once marked — the
435
+ * durable exactly-once guard for the metering job.
436
+ *
437
+ * @param db - The database.
438
+ * @param id - The sponsored-transaction id (`stx_…`).
439
+ * @param at - When the report was acknowledged (ISO 8601).
440
+ */
441
+ export async function markReported(db: Db.Db, id: string, at: string): Promise<void> {
442
+ await db.kysely
443
+ .updateTable('sponsored_transactions')
444
+ .set({ meterReportedAt: at })
445
+ .where('id', '=', id)
446
+ .where('meterReportedAt', 'is', null)
447
+ .execute()
448
+ }
449
+
450
+ /** Intent status predicate; failed rows are recovery candidates only briefly. */
451
+ function intentStatus(failedSince: string | undefined) {
452
+ return (eb: ExpressionBuilder<Db.Database, 'sponsored_transactions'>) => {
453
+ if (failedSince === undefined) return eb('status', '=', 'pending')
454
+ return eb.or([
455
+ eb('status', '=', 'pending'),
456
+ eb.and([eb('status', '=', 'failed'), eb('finalizedAt', '>=', failedSince)]),
457
+ ])
458
+ }
459
+ }
460
+
461
+ /** Thrown by {@link reserve} when recording would exceed the org's period spend limit. */
462
+ export class PeriodSpendLimitError extends Error {
463
+ override name = 'SponsoredTransactions.PeriodSpendLimitError'
464
+ }
@@ -0,0 +1,109 @@
1
+ import { nanoid } from 'nanoid'
2
+
3
+ import { postgresUrl } from '../../../test/containers.js'
4
+ import * as Db from '../Db.js'
5
+ import * as StripeCustomers from './stripeCustomers.js'
6
+
7
+ const create = () => Db.postgres({ connectionString: postgresUrl, schema: `t_${nanoid()}` })
8
+
9
+ /** Redacts nondeterministic row fields (timestamps). */
10
+ function redact(record: StripeCustomers.Record) {
11
+ return { ...record, createdAt: '<timestamp>', updatedAt: '<timestamp>' }
12
+ }
13
+
14
+ describe('create', () => {
15
+ test('behavior: inserts with status none', async () => {
16
+ const db = create()
17
+ await db.migrate()
18
+ const record = await StripeCustomers.create(db, {
19
+ orgId: 'org_1',
20
+ stripeCustomerId: 'cus_a',
21
+ })
22
+ expect(redact(record)).toMatchInlineSnapshot(`
23
+ {
24
+ "createdAt": "<timestamp>",
25
+ "environment": "production",
26
+ "orgId": "org_1",
27
+ "status": "none",
28
+ "stripeCustomerId": "cus_a",
29
+ "updatedAt": "<timestamp>",
30
+ }
31
+ `)
32
+ await db.close()
33
+ })
34
+
35
+ test('behavior: a concurrent duplicate resolves to the winning row', async () => {
36
+ const db = create()
37
+ await db.migrate()
38
+ const winner = await StripeCustomers.create(db, { orgId: 'org_1', stripeCustomerId: 'cus_a' })
39
+ // Loser of the first-checkout race: same org, its own Stripe customer.
40
+ const resolved = await StripeCustomers.create(db, { orgId: 'org_1', stripeCustomerId: 'cus_b' }) // prettier-ignore
41
+ expect(resolved).toStrictEqual(winner)
42
+ expect(await StripeCustomers.getByCustomer(db, 'cus_b')).toBeUndefined()
43
+ await db.close()
44
+ })
45
+ })
46
+
47
+ describe('get', () => {
48
+ test('behavior: reads by org; undefined when absent', async () => {
49
+ const db = create()
50
+ await db.migrate()
51
+ const record = await StripeCustomers.create(db, { orgId: 'org_1', stripeCustomerId: 'cus_a' })
52
+ expect(await StripeCustomers.get(db, 'org_1')).toStrictEqual(record)
53
+ expect(await StripeCustomers.get(db, 'org_missing')).toBeUndefined()
54
+ await db.close()
55
+ })
56
+
57
+ test('behavior: production and sandbox rows coexist per org and read disjointly', async () => {
58
+ const db = create()
59
+ await db.migrate()
60
+ const production = await StripeCustomers.create(db, { orgId: 'org_1', stripeCustomerId: 'cus_live' }) // prettier-ignore
61
+ const sandbox = await StripeCustomers.create(db, { environment: 'sandbox', orgId: 'org_1', stripeCustomerId: 'cus_test' }) // prettier-ignore
62
+ expect(sandbox.environment).toBe('sandbox')
63
+ expect(await StripeCustomers.get(db, 'org_1')).toStrictEqual(production)
64
+ expect(await StripeCustomers.get(db, 'org_1', 'sandbox')).toStrictEqual(sandbox)
65
+ await db.close()
66
+ })
67
+ })
68
+
69
+ describe('getByCustomer', () => {
70
+ test('behavior: reads by Stripe customer id; undefined for unknown customers', async () => {
71
+ const db = create()
72
+ await db.migrate()
73
+ const record = await StripeCustomers.create(db, { orgId: 'org_1', stripeCustomerId: 'cus_a' })
74
+ expect(await StripeCustomers.getByCustomer(db, 'cus_a')).toStrictEqual(record)
75
+ expect(await StripeCustomers.getByCustomer(db, 'cus_unknown')).toBeUndefined()
76
+ await db.close()
77
+ })
78
+ })
79
+
80
+ describe('setStatus', () => {
81
+ test('behavior: persists the status and bumps updatedAt', async () => {
82
+ const db = create()
83
+ await db.migrate()
84
+ const record = await StripeCustomers.create(db, { orgId: 'org_1', stripeCustomerId: 'cus_a' })
85
+ const updated = await StripeCustomers.setStatus(db, 'org_1', 'active')
86
+ expect(updated?.status).toMatchInlineSnapshot(`"active"`)
87
+ expect(updated?.createdAt).toBe(record.createdAt)
88
+ await db.close()
89
+ })
90
+
91
+ test('behavior: undefined when the org has no billing account', async () => {
92
+ const db = create()
93
+ await db.migrate()
94
+ expect(await StripeCustomers.setStatus(db, 'org_missing', 'active')).toBeUndefined()
95
+ await db.close()
96
+ })
97
+ })
98
+
99
+ describe('deleteByOrg', () => {
100
+ test('behavior: deletes the row; false when absent', async () => {
101
+ const db = create()
102
+ await db.migrate()
103
+ await StripeCustomers.create(db, { orgId: 'org_1', stripeCustomerId: 'cus_a' })
104
+ expect(await StripeCustomers.deleteByOrg(db, 'org_1')).toBe(true)
105
+ expect(await StripeCustomers.get(db, 'org_1')).toBeUndefined()
106
+ expect(await StripeCustomers.deleteByOrg(db, 'org_1')).toBe(false)
107
+ await db.close()
108
+ })
109
+ })