tapimo 0.5.3 → 0.7.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 (376) hide show
  1. package/dist/App.d.ts +15830 -7220
  2. package/dist/App.d.ts.map +1 -1
  3. package/dist/App.js +9 -1
  4. package/dist/App.js.map +1 -1
  5. package/dist/Scope.d.ts +4 -0
  6. package/dist/Scope.d.ts.map +1 -1
  7. package/dist/Scope.js +5 -0
  8. package/dist/Scope.js.map +1 -1
  9. package/dist/admin/App.d.ts +1011 -412
  10. package/dist/admin/App.d.ts.map +1 -1
  11. package/dist/admin/App.js +4 -0
  12. package/dist/admin/App.js.map +1 -1
  13. package/dist/admin/apps/api-keys.d.ts +2 -1
  14. package/dist/admin/apps/api-keys.d.ts.map +1 -1
  15. package/dist/admin/apps/early-access.d.ts +148 -0
  16. package/dist/admin/apps/early-access.d.ts.map +1 -0
  17. package/dist/admin/apps/early-access.js +133 -0
  18. package/dist/admin/apps/early-access.js.map +1 -0
  19. package/dist/admin/apps/organizations.d.ts +124 -1
  20. package/dist/admin/apps/organizations.d.ts.map +1 -1
  21. package/dist/admin/apps/organizations.js +97 -6
  22. package/dist/admin/apps/organizations.js.map +1 -1
  23. package/dist/admin/apps/verified-tokens.d.ts +526 -1
  24. package/dist/admin/apps/verified-tokens.d.ts.map +1 -1
  25. package/dist/admin/apps/verified-tokens.js +214 -1
  26. package/dist/admin/apps/verified-tokens.js.map +1 -1
  27. package/dist/admin/ui.generated.d.ts +1 -1
  28. package/dist/admin/ui.generated.d.ts.map +1 -1
  29. package/dist/admin/ui.generated.js +95 -75
  30. package/dist/admin/ui.generated.js.map +1 -1
  31. package/dist/analytics/Analytics.d.ts.map +1 -1
  32. package/dist/analytics/Analytics.js +20 -0
  33. package/dist/analytics/Analytics.js.map +1 -1
  34. package/dist/analytics/tables/requestEvents.d.ts +18 -0
  35. package/dist/analytics/tables/requestEvents.d.ts.map +1 -1
  36. package/dist/analytics/tables/requestEvents.js +36 -7
  37. package/dist/analytics/tables/requestEvents.js.map +1 -1
  38. package/dist/apps/data/App.d.ts +6260 -4924
  39. package/dist/apps/data/App.d.ts.map +1 -1
  40. package/dist/apps/data/App.js +11 -6
  41. package/dist/apps/data/App.js.map +1 -1
  42. package/dist/apps/data/FundingQuoteProviders.d.ts +34 -1
  43. package/dist/apps/data/FundingQuoteProviders.d.ts.map +1 -1
  44. package/dist/apps/data/FundingQuoteProviders.js +194 -16
  45. package/dist/apps/data/FundingQuoteProviders.js.map +1 -1
  46. package/dist/apps/data/FxOracle.d.ts +71 -0
  47. package/dist/apps/data/FxOracle.d.ts.map +1 -0
  48. package/dist/apps/data/FxOracle.js +97 -0
  49. package/dist/apps/data/FxOracle.js.map +1 -0
  50. package/dist/apps/data/index.d.ts +1 -0
  51. package/dist/apps/data/index.d.ts.map +1 -1
  52. package/dist/apps/data/index.js +1 -0
  53. package/dist/apps/data/index.js.map +1 -1
  54. package/dist/apps/data/routes/activities.d.ts +3599 -1382
  55. package/dist/apps/data/routes/activities.d.ts.map +1 -1
  56. package/dist/apps/data/routes/activities.js +250 -88
  57. package/dist/apps/data/routes/activities.js.map +1 -1
  58. package/dist/apps/data/routes/addresses.d.ts +872 -251
  59. package/dist/apps/data/routes/addresses.d.ts.map +1 -1
  60. package/dist/apps/data/routes/addresses.js +11 -9
  61. package/dist/apps/data/routes/addresses.js.map +1 -1
  62. package/dist/apps/data/routes/balances.d.ts +77 -26
  63. package/dist/apps/data/routes/balances.d.ts.map +1 -1
  64. package/dist/apps/data/routes/balances.js +88 -45
  65. package/dist/apps/data/routes/balances.js.map +1 -1
  66. package/dist/apps/data/routes/blocks.d.ts +39 -39
  67. package/dist/apps/data/routes/coingecko.d.ts +54 -54
  68. package/dist/apps/data/routes/exchanges.d.ts +856 -105
  69. package/dist/apps/data/routes/exchanges.d.ts.map +1 -1
  70. package/dist/apps/data/routes/exchanges.js +518 -76
  71. package/dist/apps/data/routes/exchanges.js.map +1 -1
  72. package/dist/apps/data/routes/fee-amm.d.ts +81 -67
  73. package/dist/apps/data/routes/fee-amm.d.ts.map +1 -1
  74. package/dist/apps/data/routes/fee-amm.js +76 -91
  75. package/dist/apps/data/routes/fee-amm.js.map +1 -1
  76. package/dist/apps/data/routes/funding.d.ts +80 -36
  77. package/dist/apps/data/routes/funding.d.ts.map +1 -1
  78. package/dist/apps/data/routes/funding.js +65 -31
  79. package/dist/apps/data/routes/funding.js.map +1 -1
  80. package/dist/apps/data/routes/receipts.d.ts +392 -315
  81. package/dist/apps/data/routes/receipts.d.ts.map +1 -1
  82. package/dist/apps/data/routes/receipts.js +152 -77
  83. package/dist/apps/data/routes/receipts.js.map +1 -1
  84. package/dist/apps/data/routes/tokens.d.ts +167 -174
  85. package/dist/apps/data/routes/tokens.d.ts.map +1 -1
  86. package/dist/apps/data/routes/tokens.js +15 -13
  87. package/dist/apps/data/routes/tokens.js.map +1 -1
  88. package/dist/apps/data/routes/transactions.d.ts +277 -536
  89. package/dist/apps/data/routes/transactions.d.ts.map +1 -1
  90. package/dist/apps/data/routes/transactions.js +96 -77
  91. package/dist/apps/data/routes/transactions.js.map +1 -1
  92. package/dist/apps/data/routes/transfers.d.ts +157 -81
  93. package/dist/apps/data/routes/transfers.d.ts.map +1 -1
  94. package/dist/apps/data/routes/transfers.js +272 -130
  95. package/dist/apps/data/routes/transfers.js.map +1 -1
  96. package/dist/apps/data/routes/valuation.d.ts +307 -0
  97. package/dist/apps/data/routes/valuation.d.ts.map +1 -0
  98. package/dist/apps/data/routes/valuation.js +292 -0
  99. package/dist/apps/data/routes/valuation.js.map +1 -0
  100. package/dist/apps/data/routes/verified-tokens.d.ts +36 -36
  101. package/dist/apps/data/routes/verified-tokens.d.ts.map +1 -1
  102. package/dist/apps/data/routes/verified-tokens.js +7 -5
  103. package/dist/apps/data/routes/verified-tokens.js.map +1 -1
  104. package/dist/apps/data/routes/webhooks.d.ts +103 -123
  105. package/dist/apps/data/routes/webhooks.d.ts.map +1 -1
  106. package/dist/apps/data/routes/webhooks.js +124 -25
  107. package/dist/apps/data/routes/webhooks.js.map +1 -1
  108. package/dist/apps/index.d.ts +2 -1
  109. package/dist/apps/index.d.ts.map +1 -1
  110. package/dist/apps/index.js +2 -1
  111. package/dist/apps/index.js.map +1 -1
  112. package/dist/apps/management/App.d.ts +8554 -1692
  113. package/dist/apps/management/App.d.ts.map +1 -1
  114. package/dist/apps/management/App.js +12 -1
  115. package/dist/apps/management/App.js.map +1 -1
  116. package/dist/apps/management/routes/api-keys.d.ts +7 -0
  117. package/dist/apps/management/routes/api-keys.d.ts.map +1 -1
  118. package/dist/apps/management/routes/api-keys.js +17 -3
  119. package/dist/apps/management/routes/api-keys.js.map +1 -1
  120. package/dist/apps/management/routes/billing.d.ts +157 -162
  121. package/dist/apps/management/routes/billing.d.ts.map +1 -1
  122. package/dist/apps/management/routes/billing.js +31 -23
  123. package/dist/apps/management/routes/billing.js.map +1 -1
  124. package/dist/apps/management/routes/faucet.d.ts +232 -0
  125. package/dist/apps/management/routes/faucet.d.ts.map +1 -0
  126. package/dist/apps/management/routes/faucet.js +154 -0
  127. package/dist/apps/management/routes/faucet.js.map +1 -0
  128. package/dist/apps/management/routes/invitations.d.ts +60 -60
  129. package/dist/apps/management/routes/invite-links.d.ts +49 -20
  130. package/dist/apps/management/routes/invite-links.d.ts.map +1 -1
  131. package/dist/apps/management/routes/invite-links.js +26 -1
  132. package/dist/apps/management/routes/invite-links.js.map +1 -1
  133. package/dist/apps/management/routes/me.d.ts +6 -6
  134. package/dist/apps/management/routes/members.d.ts +6 -6
  135. package/dist/apps/management/routes/orgs.d.ts +42 -42
  136. package/dist/apps/management/routes/orgs.d.ts.map +1 -1
  137. package/dist/apps/management/routes/orgs.js +5 -0
  138. package/dist/apps/management/routes/orgs.js.map +1 -1
  139. package/dist/apps/management/routes/projects.d.ts +44 -44
  140. package/dist/apps/management/routes/usage.d.ts +3 -0
  141. package/dist/apps/management/routes/usage.d.ts.map +1 -1
  142. package/dist/apps/management/routes/usage.js +4 -0
  143. package/dist/apps/management/routes/usage.js.map +1 -1
  144. package/dist/apps/management/routes/verified-token-requests.d.ts +697 -0
  145. package/dist/apps/management/routes/verified-token-requests.d.ts.map +1 -0
  146. package/dist/apps/management/routes/verified-token-requests.js +271 -0
  147. package/dist/apps/management/routes/verified-token-requests.js.map +1 -0
  148. package/dist/apps/management/routes/webhooks.d.ts +6977 -0
  149. package/dist/apps/management/routes/webhooks.d.ts.map +1 -0
  150. package/dist/apps/management/routes/webhooks.js +573 -0
  151. package/dist/apps/management/routes/webhooks.js.map +1 -0
  152. package/dist/apps/mpp/App.d.ts +363 -0
  153. package/dist/apps/mpp/App.d.ts.map +1 -0
  154. package/dist/apps/mpp/App.js +72 -0
  155. package/dist/apps/mpp/App.js.map +1 -0
  156. package/dist/apps/mpp/Mpp.d.ts +178 -0
  157. package/dist/apps/mpp/Mpp.d.ts.map +1 -0
  158. package/dist/apps/mpp/Mpp.js +183 -0
  159. package/dist/apps/mpp/Mpp.js.map +1 -0
  160. package/dist/apps/mpp/index.d.ts +3 -0
  161. package/dist/apps/mpp/index.d.ts.map +1 -0
  162. package/dist/apps/mpp/index.js +3 -0
  163. package/dist/apps/mpp/index.js.map +1 -0
  164. package/dist/apps/relay/App.d.ts.map +1 -1
  165. package/dist/apps/relay/App.js +56 -9
  166. package/dist/apps/relay/App.js.map +1 -1
  167. package/dist/db/Db.d.ts +6 -0
  168. package/dist/db/Db.d.ts.map +1 -1
  169. package/dist/db/Db.js +110 -0
  170. package/dist/db/Db.js.map +1 -1
  171. package/dist/db/Schema.d.ts +53 -0
  172. package/dist/db/Schema.d.ts.map +1 -1
  173. package/dist/db/Schema.js +56 -0
  174. package/dist/db/Schema.js.map +1 -1
  175. package/dist/db/tables/earlyAccess.d.ts +50 -0
  176. package/dist/db/tables/earlyAccess.d.ts.map +1 -0
  177. package/dist/db/tables/earlyAccess.js +63 -0
  178. package/dist/db/tables/earlyAccess.js.map +1 -0
  179. package/dist/db/tables/enabledBillingSources.d.ts +73 -0
  180. package/dist/db/tables/enabledBillingSources.d.ts.map +1 -0
  181. package/dist/db/tables/enabledBillingSources.js +69 -0
  182. package/dist/db/tables/enabledBillingSources.js.map +1 -0
  183. package/dist/db/tables/invitations.d.ts.map +1 -1
  184. package/dist/db/tables/invitations.js +3 -0
  185. package/dist/db/tables/invitations.js.map +1 -1
  186. package/dist/db/tables/inviteLinks.d.ts +10 -2
  187. package/dist/db/tables/inviteLinks.d.ts.map +1 -1
  188. package/dist/db/tables/inviteLinks.js +10 -1
  189. package/dist/db/tables/inviteLinks.js.map +1 -1
  190. package/dist/db/tables/organizations.d.ts +5 -2
  191. package/dist/db/tables/organizations.d.ts.map +1 -1
  192. package/dist/db/tables/organizations.js +13 -2
  193. package/dist/db/tables/organizations.js.map +1 -1
  194. package/dist/db/tables/sponsoredTransactions.d.ts +2 -0
  195. package/dist/db/tables/sponsoredTransactions.d.ts.map +1 -1
  196. package/dist/db/tables/sponsoredTransactions.js +2 -0
  197. package/dist/db/tables/sponsoredTransactions.js.map +1 -1
  198. package/dist/db/tables/verifiedTokenRequests.d.ts +94 -0
  199. package/dist/db/tables/verifiedTokenRequests.d.ts.map +1 -0
  200. package/dist/db/tables/verifiedTokenRequests.js +120 -0
  201. package/dist/db/tables/verifiedTokenRequests.js.map +1 -0
  202. package/dist/db/tables/webhookSubscriptions.d.ts +14 -0
  203. package/dist/db/tables/webhookSubscriptions.d.ts.map +1 -1
  204. package/dist/db/tables/webhookSubscriptions.js +16 -0
  205. package/dist/db/tables/webhookSubscriptions.js.map +1 -1
  206. package/dist/handlers/internal/utils.d.ts +1 -16
  207. package/dist/handlers/internal/utils.d.ts.map +1 -1
  208. package/dist/handlers/internal/utils.js +17 -6
  209. package/dist/handlers/internal/utils.js.map +1 -1
  210. package/dist/handlers/relay.d.ts.map +1 -1
  211. package/dist/handlers/relay.js +37 -5
  212. package/dist/handlers/relay.js.map +1 -1
  213. package/dist/internal/Auth.d.ts +11 -2
  214. package/dist/internal/Auth.d.ts.map +1 -1
  215. package/dist/internal/Auth.js +14 -47
  216. package/dist/internal/Auth.js.map +1 -1
  217. package/dist/internal/FundingQuotes.d.ts +147 -47
  218. package/dist/internal/FundingQuotes.d.ts.map +1 -1
  219. package/dist/internal/FundingQuotes.js +119 -33
  220. package/dist/internal/FundingQuotes.js.map +1 -1
  221. package/dist/internal/Log.d.ts +37 -5
  222. package/dist/internal/Log.d.ts.map +1 -1
  223. package/dist/internal/Log.js +52 -3
  224. package/dist/internal/Log.js.map +1 -1
  225. package/dist/internal/MetricSink.d.ts.map +1 -1
  226. package/dist/internal/MetricSink.js +24 -0
  227. package/dist/internal/MetricSink.js.map +1 -1
  228. package/dist/internal/OpenApi.d.ts +2 -2
  229. package/dist/internal/OpenApi.d.ts.map +1 -1
  230. package/dist/internal/OpenApi.js +3 -0
  231. package/dist/internal/OpenApi.js.map +1 -1
  232. package/dist/internal/Response.d.ts +2 -2
  233. package/dist/internal/Response.d.ts.map +1 -1
  234. package/dist/internal/Response.js +12 -4
  235. package/dist/internal/Response.js.map +1 -1
  236. package/dist/internal/Schema.d.ts +14 -5
  237. package/dist/internal/Schema.d.ts.map +1 -1
  238. package/dist/internal/Schema.js +23 -5
  239. package/dist/internal/Schema.js.map +1 -1
  240. package/dist/internal/Value.d.ts +18 -0
  241. package/dist/internal/Value.d.ts.map +1 -1
  242. package/dist/internal/Value.js +11 -1
  243. package/dist/internal/Value.js.map +1 -1
  244. package/dist/internal/VerifiedTokens.d.ts.map +1 -1
  245. package/dist/internal/VerifiedTokens.js +10 -1
  246. package/dist/internal/VerifiedTokens.js.map +1 -1
  247. package/dist/internal/Webhooks.d.ts +24 -3
  248. package/dist/internal/Webhooks.d.ts.map +1 -1
  249. package/dist/internal/Webhooks.js +34 -8
  250. package/dist/internal/Webhooks.js.map +1 -1
  251. package/package.json +7 -2
  252. package/src/App.test.ts +9 -4
  253. package/src/App.ts +23 -3
  254. package/src/Client.test-d.ts +733 -20
  255. package/src/Scope.ts +5 -0
  256. package/src/admin/App.test.ts +4 -0
  257. package/src/admin/App.ts +4 -0
  258. package/src/admin/apps/early-access.test.ts +125 -0
  259. package/src/admin/apps/early-access.ts +176 -0
  260. package/src/admin/apps/organizations.test.ts +86 -2
  261. package/src/admin/apps/organizations.ts +155 -31
  262. package/src/admin/apps/scopes.test.ts +5 -0
  263. package/src/admin/apps/verified-tokens.test.ts +201 -0
  264. package/src/admin/apps/verified-tokens.ts +532 -286
  265. package/src/admin/ui/plugin.ts +2 -1
  266. package/src/admin/ui/src/lib/early-access.ts +63 -0
  267. package/src/admin/ui/src/lib/organizations.test.ts +81 -1
  268. package/src/admin/ui/src/lib/organizations.ts +92 -0
  269. package/src/admin/ui/src/lib/verified-tokens.ts +79 -0
  270. package/src/admin/ui/src/routeTree.gen.ts +33 -2
  271. package/src/admin/ui/src/routes/__root.tsx +2 -1
  272. package/src/admin/ui/src/routes/api-keys.tsx +1 -1
  273. package/src/admin/ui/src/routes/early-access.tsx +162 -0
  274. package/src/admin/ui/src/routes/organizations_.$orgId.tsx +103 -0
  275. package/src/admin/ui/src/routes/verified-tokens.tsx +230 -19
  276. package/src/admin/ui.generated.ts +95 -94
  277. package/src/analytics/Analytics.test.ts +83 -0
  278. package/src/analytics/Analytics.ts +20 -0
  279. package/src/analytics/tables/requestEvents.ts +60 -7
  280. package/src/apps/composition.test.ts +11 -2
  281. package/src/apps/data/App.test.ts +1 -1
  282. package/src/apps/data/App.ts +22 -14
  283. package/src/apps/data/FundingQuoteProviders.test.ts +297 -4
  284. package/src/apps/data/FundingQuoteProviders.ts +255 -29
  285. package/src/apps/data/FxOracle.test.ts +118 -0
  286. package/src/apps/data/FxOracle.ts +153 -0
  287. package/src/apps/data/index.ts +1 -0
  288. package/src/apps/data/routes/activities.test.ts +177 -3
  289. package/src/apps/data/routes/activities.ts +441 -132
  290. package/src/apps/data/routes/addresses.ts +19 -9
  291. package/src/apps/data/routes/balances.test.ts +148 -11
  292. package/src/apps/data/routes/balances.ts +140 -51
  293. package/src/apps/data/routes/exchanges.test.ts +544 -9
  294. package/src/apps/data/routes/exchanges.ts +690 -103
  295. package/src/apps/data/routes/fee-amm.test.ts +47 -23
  296. package/src/apps/data/routes/fee-amm.ts +98 -119
  297. package/src/apps/data/routes/funding.test.ts +113 -8
  298. package/src/apps/data/routes/funding.ts +77 -51
  299. package/src/apps/data/routes/receipts.test.ts +129 -23
  300. package/src/apps/data/routes/receipts.ts +205 -100
  301. package/src/apps/data/routes/tokens.test.ts +43 -0
  302. package/src/apps/data/routes/tokens.ts +28 -13
  303. package/src/apps/data/routes/transactions.test.ts +72 -19
  304. package/src/apps/data/routes/transactions.ts +162 -92
  305. package/src/apps/data/routes/transfers.test.ts +341 -31
  306. package/src/apps/data/routes/transfers.ts +358 -158
  307. package/src/apps/data/routes/valuation.test.ts +437 -0
  308. package/src/apps/data/routes/valuation.ts +423 -0
  309. package/src/apps/data/routes/verified-tokens.ts +14 -2
  310. package/src/apps/data/routes/webhooks.test.ts +56 -8
  311. package/src/apps/data/routes/webhooks.ts +144 -27
  312. package/src/apps/index.ts +2 -1
  313. package/src/apps/management/App.ts +13 -0
  314. package/src/apps/management/routes/api-keys.test.ts +14 -0
  315. package/src/apps/management/routes/api-keys.ts +27 -3
  316. package/src/apps/management/routes/billing.test.ts +152 -20
  317. package/src/apps/management/routes/billing.ts +34 -18
  318. package/src/apps/management/routes/faucet.test.ts +87 -0
  319. package/src/apps/management/routes/faucet.ts +201 -0
  320. package/src/apps/management/routes/invite-links.test.ts +117 -0
  321. package/src/apps/management/routes/invite-links.ts +43 -1
  322. package/src/apps/management/routes/me.test.ts +9 -99
  323. package/src/apps/management/routes/orgs.test.ts +45 -2
  324. package/src/apps/management/routes/orgs.ts +6 -0
  325. package/src/apps/management/routes/scopes.test.ts +1 -0
  326. package/src/apps/management/routes/usage.test.ts +6 -0
  327. package/src/apps/management/routes/usage.ts +7 -0
  328. package/src/apps/management/routes/verified-token-requests.test.ts +377 -0
  329. package/src/apps/management/routes/verified-token-requests.ts +397 -0
  330. package/src/apps/management/routes/webhooks.test.ts +426 -0
  331. package/src/apps/management/routes/webhooks.ts +672 -0
  332. package/src/apps/mpp/App.ts +86 -0
  333. package/src/apps/mpp/Mpp.test.ts +193 -0
  334. package/src/apps/mpp/Mpp.ts +353 -0
  335. package/src/apps/mpp/index.ts +2 -0
  336. package/src/apps/mpp/internal/Adapter.ts +11 -0
  337. package/src/apps/mpp/internal/Error.ts +9 -0
  338. package/src/apps/mpp/internal/Idempotency.ts +7 -0
  339. package/src/apps/mpp/internal/Screening.ts +10 -0
  340. package/src/apps/relay/App.test.ts +88 -6
  341. package/src/apps/relay/App.ts +61 -9
  342. package/src/db/Db.test.ts +19 -0
  343. package/src/db/Db.ts +125 -0
  344. package/src/db/Schema.ts +68 -0
  345. package/src/db/tables/earlyAccess.ts +86 -0
  346. package/src/db/tables/enabledBillingSources.test.ts +303 -0
  347. package/src/db/tables/enabledBillingSources.ts +118 -0
  348. package/src/db/tables/invitations.ts +3 -0
  349. package/src/db/tables/inviteLinks.ts +22 -3
  350. package/src/db/tables/organizations.test.ts +103 -0
  351. package/src/db/tables/organizations.ts +18 -2
  352. package/src/db/tables/sponsoredTransactions.ts +4 -0
  353. package/src/db/tables/verifiedTokenRequests.test.ts +200 -0
  354. package/src/db/tables/verifiedTokenRequests.ts +182 -0
  355. package/src/db/tables/webhookSubscriptions.ts +31 -0
  356. package/src/handlers/internal/utils.ts +22 -6
  357. package/src/handlers/relay.errors.test.ts +153 -0
  358. package/src/handlers/relay.ts +42 -7
  359. package/src/internal/Auth.test.ts +3 -3
  360. package/src/internal/Auth.ts +15 -47
  361. package/src/internal/FundingQuotes.test.ts +161 -6
  362. package/src/internal/FundingQuotes.ts +141 -39
  363. package/src/internal/Log.test-d.ts +21 -0
  364. package/src/internal/Log.test.ts +141 -0
  365. package/src/internal/Log.ts +93 -6
  366. package/src/internal/MetricSink.test.ts +215 -0
  367. package/src/internal/MetricSink.ts +24 -0
  368. package/src/internal/OpenApi.ts +3 -0
  369. package/src/internal/Response.test.ts +28 -0
  370. package/src/internal/Response.ts +12 -4
  371. package/src/internal/Schema.ts +50 -5
  372. package/src/internal/Value.test.ts +20 -0
  373. package/src/internal/Value.ts +24 -1
  374. package/src/internal/VerifiedTokens.ts +9 -1
  375. package/src/internal/Webhooks.test.ts +140 -0
  376. package/src/internal/Webhooks.ts +53 -9
@@ -0,0 +1,303 @@
1
+ import { nanoid } from 'nanoid'
2
+
3
+ import * as Runtime from '../../../test/runtime.js'
4
+ import * as Db from '../Db.js'
5
+ import * as EarlyAccess from './earlyAccess.js'
6
+ import * as EnabledBillingSources from './enabledBillingSources.js'
7
+ import * as Invitations from './invitations.js'
8
+ import * as Organizations from './organizations.js'
9
+ import * as StripeCustomers from './stripeCustomers.js'
10
+ import * as Users from './users.js'
11
+
12
+ const create = () => Db.postgres({ connectionString: Runtime.postgresUrl, schema: `t_${nanoid()}` })
13
+
14
+ async function createOwned(db: Db.Db, options: createOwned.Options) {
15
+ const user = await Users.upsertByAddress(db, { address: options.address })
16
+ if (options.email) await Users.setEmail(db, user.id, options.email)
17
+ const organization = await Organizations.createOwned(db, { name: options.name, userId: user.id })
18
+ return { organization, user }
19
+ }
20
+
21
+ declare namespace createOwned {
22
+ /** Inputs for an owned-organization fixture. */
23
+ type Options = {
24
+ /** User address. */
25
+ address: string
26
+ /** Optional user email. */
27
+ email?: string | undefined
28
+ /** Organization name. */
29
+ name: string
30
+ }
31
+ }
32
+
33
+ /** Redacts nondeterministic row fields. */
34
+ function redact(record: EnabledBillingSources.Record) {
35
+ return { ...record, createdAt: '<timestamp>' }
36
+ }
37
+
38
+ describe('add', () => {
39
+ test('behavior: enables each source once per organization', async () => {
40
+ const db = create()
41
+ await db.migrate()
42
+ const stripe = await EnabledBillingSources.add(db, {
43
+ createdBy: 'admin@tempo.xyz',
44
+ orgId: 'org_1',
45
+ source: 'stripe',
46
+ })
47
+ expect(stripe && redact(stripe)).toMatchInlineSnapshot(`
48
+ {
49
+ "createdAt": "<timestamp>",
50
+ "createdBy": "admin@tempo.xyz",
51
+ "orgId": "org_1",
52
+ "source": "stripe",
53
+ }
54
+ `)
55
+ expect(
56
+ await EnabledBillingSources.add(db, {
57
+ createdBy: 'other@tempo.xyz',
58
+ orgId: 'org_1',
59
+ source: 'stripe',
60
+ }),
61
+ ).toBeUndefined()
62
+ expect(
63
+ await EnabledBillingSources.add(db, {
64
+ createdBy: 'admin@tempo.xyz',
65
+ orgId: 'org_1',
66
+ source: 'tempo',
67
+ }),
68
+ ).toBeDefined()
69
+ await db.close()
70
+ })
71
+ })
72
+
73
+ describe('listByOrg', () => {
74
+ test('behavior: lists only the organization in canonical source order', async () => {
75
+ const db = create()
76
+ await db.migrate()
77
+ await EnabledBillingSources.add(db, {
78
+ createdBy: 'admin@tempo.xyz',
79
+ orgId: 'org_1',
80
+ source: 'tempo',
81
+ })
82
+ await EnabledBillingSources.add(db, {
83
+ createdBy: 'admin@tempo.xyz',
84
+ orgId: 'org_2',
85
+ source: 'stripe',
86
+ })
87
+ await EnabledBillingSources.add(db, {
88
+ createdBy: 'admin@tempo.xyz',
89
+ orgId: 'org_1',
90
+ source: 'stripe',
91
+ })
92
+ expect((await EnabledBillingSources.listByOrg(db, 'org_1')).map(redact)).toMatchInlineSnapshot(`
93
+ [
94
+ {
95
+ "createdAt": "<timestamp>",
96
+ "createdBy": "admin@tempo.xyz",
97
+ "orgId": "org_1",
98
+ "source": "stripe",
99
+ },
100
+ {
101
+ "createdAt": "<timestamp>",
102
+ "createdBy": "admin@tempo.xyz",
103
+ "orgId": "org_1",
104
+ "source": "tempo",
105
+ },
106
+ ]
107
+ `)
108
+ await db.close()
109
+ })
110
+
111
+ test('behavior: new organizations start with no enabled sources', async () => {
112
+ const db = create()
113
+ await db.migrate()
114
+ await Organizations.create(db, { id: 'org_new', name: 'New' })
115
+ expect(await EnabledBillingSources.listByOrg(db, 'org_new')).toEqual([])
116
+ await db.close()
117
+ })
118
+ })
119
+
120
+ describe('remove', () => {
121
+ test('behavior: removes one source and reports absence', async () => {
122
+ const db = create()
123
+ await db.migrate()
124
+ await EnabledBillingSources.add(db, {
125
+ createdBy: 'admin@tempo.xyz',
126
+ orgId: 'org_1',
127
+ source: 'stripe',
128
+ })
129
+ await EnabledBillingSources.add(db, {
130
+ createdBy: 'admin@tempo.xyz',
131
+ orgId: 'org_1',
132
+ source: 'tempo',
133
+ })
134
+ expect(
135
+ redact(
136
+ (await EnabledBillingSources.remove(db, {
137
+ orgId: 'org_1',
138
+ source: 'stripe',
139
+ }))!,
140
+ ),
141
+ ).toMatchInlineSnapshot(`
142
+ {
143
+ "createdAt": "<timestamp>",
144
+ "createdBy": "admin@tempo.xyz",
145
+ "orgId": "org_1",
146
+ "source": "stripe",
147
+ }
148
+ `)
149
+ expect(
150
+ await EnabledBillingSources.remove(db, { orgId: 'org_1', source: 'stripe' }),
151
+ ).toBeUndefined()
152
+ expect(await EnabledBillingSources.isEnabled(db, { orgId: 'org_1', source: 'tempo' })).toBe(
153
+ true,
154
+ )
155
+ await db.close()
156
+ })
157
+ })
158
+
159
+ describe('isEnabled', () => {
160
+ test('behavior: checks one organization and source', async () => {
161
+ const db = create()
162
+ await db.migrate()
163
+ await EnabledBillingSources.add(db, {
164
+ createdBy: 'admin@tempo.xyz',
165
+ orgId: 'org_1',
166
+ source: 'stripe',
167
+ })
168
+ expect(await EnabledBillingSources.isEnabled(db, { orgId: 'org_1', source: 'stripe' })).toBe(
169
+ true,
170
+ )
171
+ expect(await EnabledBillingSources.isEnabled(db, { orgId: 'org_1', source: 'tempo' })).toBe(
172
+ false,
173
+ )
174
+ expect(await EnabledBillingSources.isEnabled(db, { orgId: 'org_2', source: 'stripe' })).toBe(
175
+ false,
176
+ )
177
+ await db.close()
178
+ })
179
+ })
180
+
181
+ describe('migration', () => {
182
+ test('behavior: catch-up backfills one Stripe source per distinct customer organization', async () => {
183
+ const db = create()
184
+ await db.migrate()
185
+ await StripeCustomers.create(db, { orgId: 'org_1', stripeCustomerId: 'cus_live_1' })
186
+ await StripeCustomers.create(db, {
187
+ environment: 'sandbox',
188
+ orgId: 'org_1',
189
+ stripeCustomerId: 'cus_test_1',
190
+ })
191
+ await StripeCustomers.create(db, { orgId: 'org_2', stripeCustomerId: 'cus_live_2' })
192
+ await db.kysely
193
+ .deleteFrom('migrations')
194
+ .where('name', '=', '0072_enabled_billing_sources_stripe_catch_up')
195
+ .execute()
196
+ await db.migrate()
197
+ await db.kysely
198
+ .deleteFrom('migrations')
199
+ .where('name', '=', '0072_enabled_billing_sources_stripe_catch_up')
200
+ .execute()
201
+ await db.migrate()
202
+
203
+ expect(
204
+ (await EnabledBillingSources.listByOrg(db, 'org_1')).map((record) => ({
205
+ createdBy: record.createdBy,
206
+ source: record.source,
207
+ })),
208
+ ).toMatchInlineSnapshot(`
209
+ [
210
+ {
211
+ "createdBy": "migration",
212
+ "source": "stripe",
213
+ },
214
+ ]
215
+ `)
216
+ expect((await EnabledBillingSources.listByOrg(db, 'org_2')).map((record) => record.source))
217
+ .toMatchInlineSnapshot(`
218
+ [
219
+ "stripe",
220
+ ]
221
+ `)
222
+ await db.close()
223
+ })
224
+
225
+ test('behavior: backfills Stripe only for organizations owned by allowlisted users', async () => {
226
+ const db = create()
227
+ await db.migrate()
228
+ await EarlyAccess.add(db, { createdBy: 'admin@tempo.xyz', entry: 'exact@example.net' })
229
+ await EarlyAccess.add(db, { createdBy: 'admin@tempo.xyz', entry: 'example.org' })
230
+ const exact = await createOwned(db, {
231
+ address: `0x${'11'.repeat(20)}`,
232
+ email: ' Exact@Example.NET ',
233
+ name: 'Exact',
234
+ })
235
+ const domain = await createOwned(db, {
236
+ address: `0x${'22'.repeat(20)}`,
237
+ email: 'Owner@Example.ORG',
238
+ name: 'Domain',
239
+ })
240
+ const invited = await createOwned(db, {
241
+ address: `0x${'33'.repeat(20)}`,
242
+ email: 'Invited@Example.NET',
243
+ name: 'Invited',
244
+ })
245
+ const legacyInvitation = await Invitations.create(db, {
246
+ email: 'invited@example.net',
247
+ expiresAt: '2020-01-01T00:00:00.000Z',
248
+ invitedBy: exact.user.id,
249
+ orgId: exact.organization.id,
250
+ role: 'member',
251
+ })
252
+ await db.kysely
253
+ .updateTable('invitations')
254
+ .set({ grantsEarlyAccess: true })
255
+ .where('id', '=', legacyInvitation.id)
256
+ .execute()
257
+ const plain = await createOwned(db, {
258
+ address: `0x${'44'.repeat(20)}`,
259
+ email: 'plain@example.net',
260
+ name: 'Plain',
261
+ })
262
+ const emailLess = await createOwned(db, {
263
+ address: `0x${'55'.repeat(20)}`,
264
+ name: 'Email-less',
265
+ })
266
+
267
+ for (let attempt = 0; attempt < 2; attempt++) {
268
+ await db.kysely
269
+ .deleteFrom('migrations')
270
+ .where('name', '=', '0073_enabled_billing_sources_early_access_backfill')
271
+ .execute()
272
+ await db.migrate()
273
+ }
274
+
275
+ const result = await Promise.all(
276
+ [exact, domain, invited, plain, emailLess].map(async ({ organization }) =>
277
+ (await EnabledBillingSources.listByOrg(db, organization.id)).map(
278
+ ({ createdBy, source }) => ({ createdBy, source }),
279
+ ),
280
+ ),
281
+ )
282
+ expect(result).toMatchInlineSnapshot(`
283
+ [
284
+ [
285
+ {
286
+ "createdBy": "migration",
287
+ "source": "stripe",
288
+ },
289
+ ],
290
+ [
291
+ {
292
+ "createdBy": "migration",
293
+ "source": "stripe",
294
+ },
295
+ ],
296
+ [],
297
+ [],
298
+ [],
299
+ ]
300
+ `)
301
+ await db.close()
302
+ })
303
+ })
@@ -0,0 +1,118 @@
1
+ import type { Selectable } from 'kysely'
2
+
3
+ import type * as Db from '../Db.js'
4
+ import type * as db_Schema from '../Schema.js'
5
+
6
+ /** Columns of the `enabled_billing_sources` table, derived from `Schema.EnabledBillingSource`. */
7
+ export type Table = db_Schema.EnabledBillingSource
8
+
9
+ /** A stored enabled billing-source row. */
10
+ export type Record = Selectable<Table>
11
+
12
+ /** An enabled billing-source type. */
13
+ export type Source = Record['source']
14
+
15
+ /** Billing-source types in canonical display order. */
16
+ export const sources = ['stripe', 'tempo'] as const satisfies readonly Source[]
17
+
18
+ /**
19
+ * Enables a billing-source type for an organization.
20
+ *
21
+ * @param db - The database.
22
+ * @param input - The source to enable.
23
+ * @returns The stored row, or `undefined` when already enabled.
24
+ */
25
+ export function add(db: Db.Db, input: add.Input): Promise<Record | undefined> {
26
+ return db.kysely
27
+ .insertInto('enabled_billing_sources')
28
+ .values({
29
+ createdAt: new Date().toISOString(),
30
+ createdBy: input.createdBy,
31
+ orgId: input.orgId,
32
+ source: input.source,
33
+ })
34
+ .onConflict((oc) => oc.columns(['orgId', 'source']).doNothing())
35
+ .returningAll()
36
+ .executeTakeFirst()
37
+ }
38
+
39
+ export declare namespace add {
40
+ /** Fields accepted when enabling a billing source. */
41
+ type Input = {
42
+ /** Identity enabling the source. */
43
+ createdBy: string
44
+ /** Organization id (`org_…`) the source is enabled for. */
45
+ orgId: string
46
+ /** Billing-source type to enable. */
47
+ source: Source
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Lists an organization's enabled billing sources in canonical order.
53
+ *
54
+ * @param db - The database.
55
+ * @param orgId - The organization id (`org_…`).
56
+ * @returns The enabled sources.
57
+ */
58
+ export function listByOrg(db: Db.Db, orgId: string): Promise<Record[]> {
59
+ return db.kysely
60
+ .selectFrom('enabled_billing_sources')
61
+ .selectAll()
62
+ .where('orgId', '=', orgId)
63
+ .orderBy('source', 'asc')
64
+ .execute()
65
+ }
66
+
67
+ /**
68
+ * Removes one enabled billing source from an organization.
69
+ *
70
+ * @param db - The database.
71
+ * @param input - The source to remove.
72
+ * @returns The removed row, or `undefined` when absent.
73
+ */
74
+ export function remove(db: Db.Db, input: remove.Input): Promise<Record | undefined> {
75
+ return db.kysely
76
+ .deleteFrom('enabled_billing_sources')
77
+ .where('orgId', '=', input.orgId)
78
+ .where('source', '=', input.source)
79
+ .returningAll()
80
+ .executeTakeFirst()
81
+ }
82
+
83
+ export declare namespace remove {
84
+ /** Fields identifying one enabled billing source. */
85
+ type Input = {
86
+ /** Organization id (`org_…`) the source is enabled for. */
87
+ orgId: string
88
+ /** Billing-source type to remove. */
89
+ source: Source
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Checks whether a billing-source type is enabled for an organization.
95
+ *
96
+ * @param db - The database.
97
+ * @param input - The source to check.
98
+ * @returns Whether the source is enabled.
99
+ */
100
+ export async function isEnabled(db: Db.Db, input: isEnabled.Input): Promise<boolean> {
101
+ const record = await db.kysely
102
+ .selectFrom('enabled_billing_sources')
103
+ .select('source')
104
+ .where('orgId', '=', input.orgId)
105
+ .where('source', '=', input.source)
106
+ .executeTakeFirst()
107
+ return record !== undefined
108
+ }
109
+
110
+ export declare namespace isEnabled {
111
+ /** Fields identifying one enabled billing source. */
112
+ type Input = {
113
+ /** Organization id (`org_…`) the source is enabled for. */
114
+ orgId: string
115
+ /** Billing-source type to check. */
116
+ source: Source
117
+ }
118
+ }
@@ -31,6 +31,8 @@ export function create(db: Db.Db, input: create.Input): Promise<Record> {
31
31
  createdAt: now,
32
32
  email: input.email.toLowerCase(),
33
33
  expiresAt: input.expiresAt,
34
+ // Keep the legacy column inert until it can be removed safely.
35
+ grantsEarlyAccess: false,
34
36
  id: Id.generate('inv'),
35
37
  invitedBy: input.invitedBy,
36
38
  orgId: input.orgId,
@@ -45,6 +47,7 @@ export function create(db: Db.Db, input: create.Input): Promise<Record> {
45
47
  .doUpdateSet({
46
48
  createdAt: now,
47
49
  expiresAt: input.expiresAt,
50
+ grantsEarlyAccess: false,
48
51
  invitedBy: input.invitedBy,
49
52
  role: input.role,
50
53
  }),
@@ -1,11 +1,18 @@
1
- import type { Selectable } from 'kysely'
1
+ import type { JSONColumnType, Selectable } from 'kysely'
2
2
 
3
3
  import * as Id from '../../internal/Id.js'
4
4
  import type * as Db from '../Db.js'
5
5
  import type * as db_Schema from '../Schema.js'
6
6
 
7
7
  /** Columns of the `invite_links` table. */
8
- export type Table = db_Schema.InviteLink
8
+ export type Table = Omit<db_Schema.InviteLink, 'allowedEmailDomains'> & {
9
+ /** Lowercase email domains allowed to redeem, or null for unrestricted. */
10
+ allowedEmailDomains: JSONColumnType<
11
+ db_Schema.InviteLink['allowedEmailDomains'],
12
+ string | null,
13
+ string | null
14
+ >
15
+ }
9
16
  /** Columns of the `invite_link_redemptions` table. */
10
17
  export type RedemptionTable = db_Schema.InviteLinkRedemption
11
18
  /** A stored invite link. */
@@ -28,6 +35,7 @@ export function status(record: Record, now = new Date().toISOString()): Status {
28
35
  export function create(
29
36
  db: Db.Db,
30
37
  input: {
38
+ allowedEmailDomains: readonly string[] | null
31
39
  createdBy: string
32
40
  expiresAt: string | null
33
41
  maxUses: number | null
@@ -40,6 +48,8 @@ export function create(
40
48
  return db.kysely
41
49
  .insertInto('invite_links')
42
50
  .values({
51
+ allowedEmailDomains:
52
+ input.allowedEmailDomains === null ? null : JSON.stringify(input.allowedEmailDomains),
43
53
  createdAt: now,
44
54
  createdBy: input.createdBy,
45
55
  deletedAt: null,
@@ -155,6 +165,10 @@ export function accept(
155
165
  .executeTakeFirst()
156
166
  if (existing) return { consumed: false, link }
157
167
  if (status(link, now) !== 'active') return undefined
168
+ const normalizedEmail = email.trim().toLowerCase()
169
+ const domain = normalizedEmail.slice(normalizedEmail.lastIndexOf('@') + 1)
170
+ if (link.allowedEmailDomains !== null && !link.allowedEmailDomains.includes(domain))
171
+ throw new EmailDomainForbiddenError()
158
172
  const inserted = await trx
159
173
  .insertInto('memberships')
160
174
  .values({ createdAt: now, orgId: link.orgId, role: 'member', updatedAt: now, userId })
@@ -171,7 +185,7 @@ export function accept(
171
185
  .insertInto('invite_link_redemptions')
172
186
  .values({
173
187
  createdAt: now,
174
- email: email.toLowerCase(),
188
+ email: normalizedEmail,
175
189
  id: Id.generate('ilr'),
176
190
  inviteLinkId: link.id,
177
191
  inviteLinkName: link.name,
@@ -185,3 +199,8 @@ export function accept(
185
199
  }
186
200
  })
187
201
  }
202
+
203
+ /** The verified email domain is not allowed by the invite link. */
204
+ export class EmailDomainForbiddenError extends Error {
205
+ override name = 'InviteLinks.EmailDomainForbiddenError'
206
+ }
@@ -3,6 +3,8 @@ import { nanoid } from 'nanoid'
3
3
  import * as Runtime from '../../../test/runtime.js'
4
4
  import * as Webhooks from '../../internal/Webhooks.js'
5
5
  import * as Db from '../Db.js'
6
+ import * as EnabledBillingSources from './enabledBillingSources.js'
7
+ import * as Memberships from './memberships.js'
6
8
  import * as Organizations from './organizations.js'
7
9
  import * as SponsoredTransactions from './sponsoredTransactions.js'
8
10
  import * as StripeCustomers from './stripeCustomers.js'
@@ -60,6 +62,66 @@ describe('create', () => {
60
62
  })
61
63
  })
62
64
 
65
+ describe('createOwned', () => {
66
+ test('behavior: creates the organization, owner, and enabled sources atomically', async () => {
67
+ const db = create()
68
+ await db.migrate()
69
+ const record = await Organizations.createOwned(db, {
70
+ enabledBillingSources: ['stripe'],
71
+ name: 'Acme, Inc.',
72
+ userId: 'usr_owner',
73
+ })
74
+
75
+ expect(
76
+ (await Memberships.listByOrg(db, record.id)).map(({ role, userId }) => ({ role, userId })),
77
+ ).toMatchInlineSnapshot(`
78
+ [
79
+ {
80
+ "role": "owner",
81
+ "userId": "usr_owner",
82
+ },
83
+ ]
84
+ `)
85
+ expect(
86
+ (await EnabledBillingSources.listByOrg(db, record.id)).map(({ createdBy, source }) => ({
87
+ createdBy,
88
+ source,
89
+ })),
90
+ ).toMatchInlineSnapshot(`
91
+ [
92
+ {
93
+ "createdBy": "usr_owner",
94
+ "source": "stripe",
95
+ },
96
+ ]
97
+ `)
98
+ await db.close()
99
+ })
100
+
101
+ test('behavior: rolls back all rows when initial source insertion fails', async () => {
102
+ const db = create()
103
+ await db.migrate()
104
+
105
+ await expect(
106
+ Organizations.createOwned(db, {
107
+ enabledBillingSources: ['stripe', 'stripe'],
108
+ name: 'Acme, Inc.',
109
+ userId: 'usr_owner',
110
+ }),
111
+ ).rejects.toThrow()
112
+ expect(await Organizations.listByUser(db, 'usr_owner')).toEqual([])
113
+ expect(await db.kysely.selectFrom('enabled_billing_sources').selectAll().execute()).toEqual([])
114
+ expect(
115
+ await db.kysely
116
+ .selectFrom('memberships')
117
+ .selectAll()
118
+ .where('userId', '=', 'usr_owner')
119
+ .execute(),
120
+ ).toEqual([])
121
+ await db.close()
122
+ })
123
+ })
124
+
63
125
  describe('get', () => {
64
126
  test('behavior: returns undefined when absent', async () => {
65
127
  const db = create()
@@ -137,6 +199,11 @@ describe('deleteOrganization', () => {
137
199
  orgId: 'org_1',
138
200
  stripeCustomerId: 'cus_org_1',
139
201
  })
202
+ await EnabledBillingSources.add(db, {
203
+ createdBy: 'admin@tempo.xyz',
204
+ orgId: 'org_1',
205
+ source: 'stripe',
206
+ })
140
207
  await SponsoredTransactions.upsert(db, {
141
208
  apiKeyId: 'key_1',
142
209
  billable: true,
@@ -153,10 +220,46 @@ describe('deleteOrganization', () => {
153
220
  Organizations.UnreportedSponsorshipsError,
154
221
  )
155
222
  expect(await Organizations.get(db, 'org_1')).toBeDefined()
223
+ expect(await EnabledBillingSources.isEnabled(db, { orgId: 'org_1', source: 'stripe' })).toBe(
224
+ true,
225
+ )
156
226
  expect(await StripeCustomers.get(db, 'org_1')).toBeDefined()
157
227
  await db.close()
158
228
  })
159
229
 
230
+ test('behavior: deletes enabled billing sources', async () => {
231
+ const db = create()
232
+ await db.migrate()
233
+ await Organizations.create(db, { id: 'org_1', name: 'One' })
234
+ await Organizations.create(db, { id: 'org_2', name: 'Two' })
235
+ await EnabledBillingSources.add(db, {
236
+ createdBy: 'admin@tempo.xyz',
237
+ orgId: 'org_1',
238
+ source: 'stripe',
239
+ })
240
+ await EnabledBillingSources.add(db, {
241
+ createdBy: 'admin@tempo.xyz',
242
+ orgId: 'org_1',
243
+ source: 'tempo',
244
+ })
245
+ await EnabledBillingSources.add(db, {
246
+ createdBy: 'admin@tempo.xyz',
247
+ orgId: 'org_2',
248
+ source: 'stripe',
249
+ })
250
+
251
+ expect(await Organizations.deleteOrganization(db, 'org_1')).toBe(true)
252
+
253
+ expect(await EnabledBillingSources.listByOrg(db, 'org_1')).toEqual([])
254
+ expect((await EnabledBillingSources.listByOrg(db, 'org_2')).map((record) => record.source))
255
+ .toMatchInlineSnapshot(`
256
+ [
257
+ "stripe",
258
+ ]
259
+ `)
260
+ await db.close()
261
+ })
262
+
160
263
  test('behavior: deletes api-key-owned webhook subscriptions', async () => {
161
264
  const db = create()
162
265
  await db.migrate()
@@ -3,6 +3,7 @@ import { sql, type Selectable } from 'kysely'
3
3
  import * as Id from '../../internal/Id.js'
4
4
  import type * as Db from '../Db.js'
5
5
  import type * as db_Schema from '../Schema.js'
6
+ import type * as EnabledBillingSources from './enabledBillingSources.js'
6
7
  import type * as Memberships from './memberships.js'
7
8
 
8
9
  /** Columns of the `organizations` table, derived from `Schema.Organization`. */
@@ -50,8 +51,8 @@ export declare namespace create {
50
51
  }
51
52
 
52
53
  /**
53
- * Inserts an organization owned by `userId` the org row plus its `owner`
54
- * membership, atomically.
54
+ * Inserts an organization, its owner membership, and initial billing sources
55
+ * atomically.
55
56
  *
56
57
  * @param db - The database.
57
58
  * @param input - The organization to insert.
@@ -82,6 +83,18 @@ export function createOwned(db: Db.Db, input: createOwned.Input): Promise<Record
82
83
  userId: input.userId,
83
84
  })
84
85
  .execute()
86
+ if (input.enabledBillingSources?.length)
87
+ await trx
88
+ .insertInto('enabled_billing_sources')
89
+ .values(
90
+ input.enabledBillingSources.map((source) => ({
91
+ createdAt: now,
92
+ createdBy: input.userId,
93
+ orgId: record.id,
94
+ source,
95
+ })),
96
+ )
97
+ .execute()
85
98
  return record
86
99
  })
87
100
  }
@@ -89,6 +102,8 @@ export function createOwned(db: Db.Db, input: createOwned.Input): Promise<Record
89
102
  export declare namespace createOwned {
90
103
  /** Fields accepted when inserting a user-owned organization. */
91
104
  type Input = {
105
+ /** Billing sources enabled when the organization is created. */
106
+ enabledBillingSources?: readonly EnabledBillingSources.Source[] | undefined
92
107
  /** Human-readable organization name. */
93
108
  name: string
94
109
  /** Owning user id (`usr_…`), granted the `owner` membership. */
@@ -121,6 +136,7 @@ export function deleteOrganization(db: Db.Db, id: string): Promise<boolean> {
121
136
  .where('ownerId', '=', id)
122
137
  .execute()
123
138
  await trx.deleteFrom('billing_settings').where('orgId', '=', id).execute()
139
+ await trx.deleteFrom('enabled_billing_sources').where('orgId', '=', id).execute()
124
140
  await trx.deleteFrom('invitations').where('orgId', '=', id).execute()
125
141
  await trx.deleteFrom('invite_link_redemptions').where('orgId', '=', id).execute()
126
142
  await trx.deleteFrom('invite_links').where('orgId', '=', id).execute()