@wopr-network/platform-core 0.1.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 (694) hide show
  1. package/biome.json +61 -0
  2. package/dist/admin/admin-audit-log-repository.d.ts +33 -0
  3. package/dist/admin/admin-audit-log-repository.js +102 -0
  4. package/dist/admin/audit-log.d.ts +49 -0
  5. package/dist/admin/audit-log.js +63 -0
  6. package/dist/admin/index.d.ts +6 -0
  7. package/dist/admin/index.js +3 -0
  8. package/dist/admin/role-store.d.ts +37 -0
  9. package/dist/admin/role-store.js +106 -0
  10. package/dist/auth/api-key-repository.d.ts +11 -0
  11. package/dist/auth/api-key-repository.js +33 -0
  12. package/dist/auth/api-key-repository.test.d.ts +1 -0
  13. package/dist/auth/api-key-repository.test.js +46 -0
  14. package/dist/auth/auth.test.d.ts +1 -0
  15. package/dist/auth/auth.test.js +140 -0
  16. package/dist/auth/better-auth.d.ts +42 -0
  17. package/dist/auth/better-auth.js +196 -0
  18. package/dist/auth/index.d.ts +186 -0
  19. package/dist/auth/index.js +422 -0
  20. package/dist/auth/login-history-repository.d.ts +14 -0
  21. package/dist/auth/login-history-repository.js +15 -0
  22. package/dist/auth/login-history-repository.test.d.ts +1 -0
  23. package/dist/auth/login-history-repository.test.js +47 -0
  24. package/dist/auth/middleware.d.ts +55 -0
  25. package/dist/auth/middleware.js +101 -0
  26. package/dist/auth/middleware.test.d.ts +1 -0
  27. package/dist/auth/middleware.test.js +213 -0
  28. package/dist/auth/scoped-tokens.test.d.ts +1 -0
  29. package/dist/auth/scoped-tokens.test.js +306 -0
  30. package/dist/auth/tenant-access.test.d.ts +1 -0
  31. package/dist/auth/tenant-access.test.js +62 -0
  32. package/dist/auth/user-creator.d.ts +9 -0
  33. package/dist/auth/user-creator.js +47 -0
  34. package/dist/auth/user-creator.test.d.ts +1 -0
  35. package/dist/auth/user-creator.test.js +78 -0
  36. package/dist/auth/user-role-repository.d.ts +31 -0
  37. package/dist/auth/user-role-repository.js +53 -0
  38. package/dist/auth/user-role-repository.test.d.ts +1 -0
  39. package/dist/auth/user-role-repository.test.js +122 -0
  40. package/dist/billing/drizzle-webhook-seen-repository.d.ts +10 -0
  41. package/dist/billing/drizzle-webhook-seen-repository.js +28 -0
  42. package/dist/billing/index.d.ts +7 -0
  43. package/dist/billing/index.js +7 -0
  44. package/dist/billing/payment-processor.d.ts +127 -0
  45. package/dist/billing/payment-processor.js +8 -0
  46. package/dist/billing/payment-processor.test.d.ts +1 -0
  47. package/dist/billing/payment-processor.test.js +71 -0
  48. package/dist/billing/payram/cents-credits-boundary.test.d.ts +1 -0
  49. package/dist/billing/payram/cents-credits-boundary.test.js +75 -0
  50. package/dist/billing/payram/charge-store.d.ts +41 -0
  51. package/dist/billing/payram/charge-store.js +72 -0
  52. package/dist/billing/payram/charge-store.test.d.ts +1 -0
  53. package/dist/billing/payram/charge-store.test.js +64 -0
  54. package/dist/billing/payram/checkout.d.ts +15 -0
  55. package/dist/billing/payram/checkout.js +24 -0
  56. package/dist/billing/payram/checkout.test.d.ts +1 -0
  57. package/dist/billing/payram/checkout.test.js +74 -0
  58. package/dist/billing/payram/client.d.ts +7 -0
  59. package/dist/billing/payram/client.js +15 -0
  60. package/dist/billing/payram/client.test.d.ts +1 -0
  61. package/dist/billing/payram/client.test.js +52 -0
  62. package/dist/billing/payram/index.d.ts +8 -0
  63. package/dist/billing/payram/index.js +4 -0
  64. package/dist/billing/payram/types.d.ts +40 -0
  65. package/dist/billing/payram/types.js +1 -0
  66. package/dist/billing/payram/webhook.d.ts +19 -0
  67. package/dist/billing/payram/webhook.js +67 -0
  68. package/dist/billing/payram/webhook.test.d.ts +7 -0
  69. package/dist/billing/payram/webhook.test.js +248 -0
  70. package/dist/billing/stripe/cents-credits-boundary.test.d.ts +1 -0
  71. package/dist/billing/stripe/cents-credits-boundary.test.js +62 -0
  72. package/dist/billing/stripe/checkout.d.ts +20 -0
  73. package/dist/billing/stripe/checkout.js +63 -0
  74. package/dist/billing/stripe/checkout.test.d.ts +1 -0
  75. package/dist/billing/stripe/checkout.test.js +148 -0
  76. package/dist/billing/stripe/client.d.ts +14 -0
  77. package/dist/billing/stripe/client.js +33 -0
  78. package/dist/billing/stripe/client.test.d.ts +1 -0
  79. package/dist/billing/stripe/client.test.js +58 -0
  80. package/dist/billing/stripe/credit-prices.d.ts +63 -0
  81. package/dist/billing/stripe/credit-prices.js +81 -0
  82. package/dist/billing/stripe/credit-prices.test.d.ts +1 -0
  83. package/dist/billing/stripe/credit-prices.test.js +87 -0
  84. package/dist/billing/stripe/index.d.ts +14 -0
  85. package/dist/billing/stripe/index.js +8 -0
  86. package/dist/billing/stripe/payment-methods-detach-all.test.d.ts +1 -0
  87. package/dist/billing/stripe/payment-methods-detach-all.test.js +40 -0
  88. package/dist/billing/stripe/payment-methods.d.ts +25 -0
  89. package/dist/billing/stripe/payment-methods.js +53 -0
  90. package/dist/billing/stripe/payment-methods.test.d.ts +1 -0
  91. package/dist/billing/stripe/payment-methods.test.js +122 -0
  92. package/dist/billing/stripe/portal.d.ts +10 -0
  93. package/dist/billing/stripe/portal.js +16 -0
  94. package/dist/billing/stripe/portal.test.d.ts +1 -0
  95. package/dist/billing/stripe/portal.test.js +48 -0
  96. package/dist/billing/stripe/setup-intent.d.ts +16 -0
  97. package/dist/billing/stripe/setup-intent.js +22 -0
  98. package/dist/billing/stripe/setup-intent.test.d.ts +1 -0
  99. package/dist/billing/stripe/setup-intent.test.js +58 -0
  100. package/dist/billing/stripe/stripe-payment-processor.d.ts +49 -0
  101. package/dist/billing/stripe/stripe-payment-processor.js +166 -0
  102. package/dist/billing/stripe/stripe-payment-processor.test.d.ts +1 -0
  103. package/dist/billing/stripe/stripe-payment-processor.test.js +413 -0
  104. package/dist/billing/stripe/tenant-store.d.ts +56 -0
  105. package/dist/billing/stripe/tenant-store.js +119 -0
  106. package/dist/billing/stripe/tenant-store.test.d.ts +1 -0
  107. package/dist/billing/stripe/tenant-store.test.js +97 -0
  108. package/dist/billing/stripe/types.d.ts +49 -0
  109. package/dist/billing/stripe/types.js +1 -0
  110. package/dist/billing/webhook-seen-repository.d.ts +14 -0
  111. package/dist/billing/webhook-seen-repository.js +13 -0
  112. package/dist/config/billing-env.test.d.ts +1 -0
  113. package/dist/config/billing-env.test.js +48 -0
  114. package/dist/config/index.d.ts +46 -0
  115. package/dist/config/index.js +38 -0
  116. package/dist/config/logger.d.ts +2 -0
  117. package/dist/config/logger.js +11 -0
  118. package/dist/config/provider-endpoints.d.ts +6 -0
  119. package/dist/config/provider-endpoints.js +12 -0
  120. package/dist/credits/auto-topup-charge.d.ts +27 -0
  121. package/dist/credits/auto-topup-charge.js +139 -0
  122. package/dist/credits/auto-topup-charge.test.d.ts +1 -0
  123. package/dist/credits/auto-topup-charge.test.js +242 -0
  124. package/dist/credits/auto-topup-event-log-repository.d.ts +16 -0
  125. package/dist/credits/auto-topup-event-log-repository.js +18 -0
  126. package/dist/credits/auto-topup-event-log-repository.test.d.ts +1 -0
  127. package/dist/credits/auto-topup-event-log-repository.test.js +83 -0
  128. package/dist/credits/auto-topup-schedule.d.ts +27 -0
  129. package/dist/credits/auto-topup-schedule.js +66 -0
  130. package/dist/credits/auto-topup-schedule.test.d.ts +1 -0
  131. package/dist/credits/auto-topup-schedule.test.js +145 -0
  132. package/dist/credits/auto-topup-settings-repository.d.ts +54 -0
  133. package/dist/credits/auto-topup-settings-repository.js +184 -0
  134. package/dist/credits/auto-topup-settings-repository.test.d.ts +1 -0
  135. package/dist/credits/auto-topup-settings-repository.test.js +104 -0
  136. package/dist/credits/auto-topup-usage.d.ts +22 -0
  137. package/dist/credits/auto-topup-usage.js +56 -0
  138. package/dist/credits/auto-topup-usage.test.d.ts +1 -0
  139. package/dist/credits/auto-topup-usage.test.js +181 -0
  140. package/dist/credits/credit-expiry-cron.d.ts +19 -0
  141. package/dist/credits/credit-expiry-cron.js +50 -0
  142. package/dist/credits/credit-expiry-cron.test.d.ts +1 -0
  143. package/dist/credits/credit-expiry-cron.test.js +67 -0
  144. package/dist/credits/credit-ledger-extra.test.d.ts +1 -0
  145. package/dist/credits/credit-ledger-extra.test.js +40 -0
  146. package/dist/credits/credit-ledger.bench.d.ts +1 -0
  147. package/dist/credits/credit-ledger.bench.js +33 -0
  148. package/dist/credits/credit-ledger.d.ts +130 -0
  149. package/dist/credits/credit-ledger.js +293 -0
  150. package/dist/credits/credit-ledger.test.d.ts +4 -0
  151. package/dist/credits/credit-ledger.test.js +203 -0
  152. package/dist/credits/credit-transaction-repository.d.ts +17 -0
  153. package/dist/credits/credit-transaction-repository.js +35 -0
  154. package/dist/credits/credit-transaction-repository.test.d.ts +1 -0
  155. package/dist/credits/credit-transaction-repository.test.js +232 -0
  156. package/dist/credits/credit.d.ts +75 -0
  157. package/dist/credits/credit.js +139 -0
  158. package/dist/credits/credit.test.d.ts +1 -0
  159. package/dist/credits/credit.test.js +196 -0
  160. package/dist/credits/dividend-cron.d.ts +29 -0
  161. package/dist/credits/dividend-cron.js +88 -0
  162. package/dist/credits/dividend-cron.test.d.ts +1 -0
  163. package/dist/credits/dividend-cron.test.js +128 -0
  164. package/dist/credits/dividend-repository.d.ts +29 -0
  165. package/dist/credits/dividend-repository.js +126 -0
  166. package/dist/credits/dividend-repository.test.d.ts +1 -0
  167. package/dist/credits/dividend-repository.test.js +176 -0
  168. package/dist/credits/index.d.ts +9 -0
  169. package/dist/credits/index.js +5 -0
  170. package/dist/credits/repository-types.d.ts +29 -0
  171. package/dist/credits/repository-types.js +1 -0
  172. package/dist/credits/signup-grant.d.ts +12 -0
  173. package/dist/credits/signup-grant.js +35 -0
  174. package/dist/credits/signup-grant.test.d.ts +1 -0
  175. package/dist/credits/signup-grant.test.js +51 -0
  176. package/dist/credits/tenant-customer-repository.d.ts +30 -0
  177. package/dist/credits/tenant-customer-repository.js +5 -0
  178. package/dist/db/auth-user-repository.d.ts +46 -0
  179. package/dist/db/auth-user-repository.js +90 -0
  180. package/dist/db/credit-column.d.ts +27 -0
  181. package/dist/db/credit-column.js +13 -0
  182. package/dist/db/index.d.ts +14 -0
  183. package/dist/db/index.js +8 -0
  184. package/dist/db/schema/account-deletion-requests.d.ts +203 -0
  185. package/dist/db/schema/account-deletion-requests.js +36 -0
  186. package/dist/db/schema/account-export-requests.d.ts +148 -0
  187. package/dist/db/schema/account-export-requests.js +19 -0
  188. package/dist/db/schema/admin-audit.d.ts +194 -0
  189. package/dist/db/schema/admin-audit.js +21 -0
  190. package/dist/db/schema/admin-users.d.ts +177 -0
  191. package/dist/db/schema/admin-users.js +23 -0
  192. package/dist/db/schema/affiliate-fraud.d.ts +160 -0
  193. package/dist/db/schema/affiliate-fraud.js +18 -0
  194. package/dist/db/schema/affiliate.d.ts +277 -0
  195. package/dist/db/schema/affiliate.js +32 -0
  196. package/dist/db/schema/coupon-codes.d.ts +143 -0
  197. package/dist/db/schema/coupon-codes.js +17 -0
  198. package/dist/db/schema/credit-auto-topup-settings.d.ts +232 -0
  199. package/dist/db/schema/credit-auto-topup-settings.js +27 -0
  200. package/dist/db/schema/credit-auto-topup.d.ts +130 -0
  201. package/dist/db/schema/credit-auto-topup.js +21 -0
  202. package/dist/db/schema/credits.d.ts +283 -0
  203. package/dist/db/schema/credits.js +38 -0
  204. package/dist/db/schema/dividend-distributions.d.ts +130 -0
  205. package/dist/db/schema/dividend-distributions.js +19 -0
  206. package/dist/db/schema/email-notifications.d.ts +99 -0
  207. package/dist/db/schema/email-notifications.js +21 -0
  208. package/dist/db/schema/index.d.ts +33 -0
  209. package/dist/db/schema/index.js +33 -0
  210. package/dist/db/schema/meter-events.d.ts +599 -0
  211. package/dist/db/schema/meter-events.js +55 -0
  212. package/dist/db/schema/notification-preferences.d.ts +165 -0
  213. package/dist/db/schema/notification-preferences.js +18 -0
  214. package/dist/db/schema/notification-queue.d.ts +236 -0
  215. package/dist/db/schema/notification-queue.js +40 -0
  216. package/dist/db/schema/org-memberships.d.ts +63 -0
  217. package/dist/db/schema/org-memberships.js +15 -0
  218. package/dist/db/schema/organization-members.d.ts +235 -0
  219. package/dist/db/schema/organization-members.js +27 -0
  220. package/dist/db/schema/payram.d.ts +164 -0
  221. package/dist/db/schema/payram.js +21 -0
  222. package/dist/db/schema/platform-api-keys.d.ts +143 -0
  223. package/dist/db/schema/platform-api-keys.js +20 -0
  224. package/dist/db/schema/promotion-redemptions.d.ts +143 -0
  225. package/dist/db/schema/promotion-redemptions.js +18 -0
  226. package/dist/db/schema/promotions.d.ts +445 -0
  227. package/dist/db/schema/promotions.js +48 -0
  228. package/dist/db/schema/provider-credentials.d.ts +201 -0
  229. package/dist/db/schema/provider-credentials.js +36 -0
  230. package/dist/db/schema/rate-limit-entries.d.ts +75 -0
  231. package/dist/db/schema/rate-limit-entries.js +7 -0
  232. package/dist/db/schema/secret-audit-log.d.ts +109 -0
  233. package/dist/db/schema/secret-audit-log.js +15 -0
  234. package/dist/db/schema/session-usage.d.ts +194 -0
  235. package/dist/db/schema/session-usage.js +19 -0
  236. package/dist/db/schema/spending-limits.d.ts +92 -0
  237. package/dist/db/schema/spending-limits.js +8 -0
  238. package/dist/db/schema/tenant-addons.d.ts +58 -0
  239. package/dist/db/schema/tenant-addons.js +9 -0
  240. package/dist/db/schema/tenant-api-keys.d.ts +131 -0
  241. package/dist/db/schema/tenant-api-keys.js +21 -0
  242. package/dist/db/schema/tenant-capability-settings.d.ts +79 -0
  243. package/dist/db/schema/tenant-capability-settings.js +12 -0
  244. package/dist/db/schema/tenant-customers.d.ts +303 -0
  245. package/dist/db/schema/tenant-customers.js +25 -0
  246. package/dist/db/schema/tenants.d.ts +126 -0
  247. package/dist/db/schema/tenants.js +18 -0
  248. package/dist/db/schema/user-roles.d.ts +98 -0
  249. package/dist/db/schema/user-roles.js +18 -0
  250. package/dist/db/schema/webhook-seen-events.d.ts +58 -0
  251. package/dist/db/schema/webhook-seen-events.js +9 -0
  252. package/dist/email/billing-emails.d.ts +51 -0
  253. package/dist/email/billing-emails.js +163 -0
  254. package/dist/email/billing-emails.test.d.ts +1 -0
  255. package/dist/email/billing-emails.test.js +162 -0
  256. package/dist/email/client.d.ts +51 -0
  257. package/dist/email/client.js +102 -0
  258. package/dist/email/client.test.d.ts +1 -0
  259. package/dist/email/client.test.js +120 -0
  260. package/dist/email/drizzle-billing-email-repository.d.ts +21 -0
  261. package/dist/email/drizzle-billing-email-repository.js +36 -0
  262. package/dist/email/drizzle-billing-email-repository.test.d.ts +1 -0
  263. package/dist/email/drizzle-billing-email-repository.test.js +42 -0
  264. package/dist/email/index.d.ts +33 -0
  265. package/dist/email/index.js +22 -0
  266. package/dist/email/notification-preferences-store.d.ts +12 -0
  267. package/dist/email/notification-preferences-store.js +82 -0
  268. package/dist/email/notification-preferences-store.test.d.ts +1 -0
  269. package/dist/email/notification-preferences-store.test.js +86 -0
  270. package/dist/email/notification-queue-store.d.ts +25 -0
  271. package/dist/email/notification-queue-store.js +97 -0
  272. package/dist/email/notification-queue-store.test.d.ts +1 -0
  273. package/dist/email/notification-queue-store.test.js +177 -0
  274. package/dist/email/notification-repository-types.d.ts +70 -0
  275. package/dist/email/notification-repository-types.js +6 -0
  276. package/dist/email/notification-service.d.ts +41 -0
  277. package/dist/email/notification-service.js +196 -0
  278. package/dist/email/notification-service.test.d.ts +1 -0
  279. package/dist/email/notification-service.test.js +160 -0
  280. package/dist/email/notification-templates.d.ts +18 -0
  281. package/dist/email/notification-templates.js +574 -0
  282. package/dist/email/notification-templates.test.d.ts +1 -0
  283. package/dist/email/notification-templates.test.js +238 -0
  284. package/dist/email/notification-worker.d.ts +24 -0
  285. package/dist/email/notification-worker.js +109 -0
  286. package/dist/email/notification-worker.test.d.ts +1 -0
  287. package/dist/email/notification-worker.test.js +153 -0
  288. package/dist/email/require-verified.d.ts +25 -0
  289. package/dist/email/require-verified.js +52 -0
  290. package/dist/email/require-verified.test.d.ts +1 -0
  291. package/dist/email/require-verified.test.js +62 -0
  292. package/dist/email/resend-adapter.d.ts +47 -0
  293. package/dist/email/resend-adapter.js +137 -0
  294. package/dist/email/resend-adapter.test.d.ts +1 -0
  295. package/dist/email/resend-adapter.test.js +190 -0
  296. package/dist/email/templates.d.ts +22 -0
  297. package/dist/email/templates.js +359 -0
  298. package/dist/email/templates.test.d.ts +1 -0
  299. package/dist/email/templates.test.js +170 -0
  300. package/dist/email/verification.d.ts +42 -0
  301. package/dist/email/verification.js +83 -0
  302. package/dist/email/verification.test.d.ts +1 -0
  303. package/dist/email/verification.test.js +141 -0
  304. package/dist/index.d.ts +13 -0
  305. package/dist/index.js +23 -0
  306. package/dist/metering/aggregator.d.ts +54 -0
  307. package/dist/metering/aggregator.js +123 -0
  308. package/dist/metering/aggregator.test.d.ts +1 -0
  309. package/dist/metering/aggregator.test.js +179 -0
  310. package/dist/metering/dlq.d.ts +31 -0
  311. package/dist/metering/dlq.js +82 -0
  312. package/dist/metering/dlq.test.d.ts +1 -0
  313. package/dist/metering/dlq.test.js +117 -0
  314. package/dist/metering/drizzle-usage-summary-repository.d.ts +67 -0
  315. package/dist/metering/drizzle-usage-summary-repository.js +98 -0
  316. package/dist/metering/emitter.d.ts +66 -0
  317. package/dist/metering/emitter.js +185 -0
  318. package/dist/metering/emitter.test.d.ts +1 -0
  319. package/dist/metering/emitter.test.js +171 -0
  320. package/dist/metering/index.d.ts +11 -0
  321. package/dist/metering/index.js +5 -0
  322. package/dist/metering/load-test.bench.d.ts +1 -0
  323. package/dist/metering/load-test.bench.js +103 -0
  324. package/dist/metering/meter-event-repository.d.ts +33 -0
  325. package/dist/metering/meter-event-repository.js +58 -0
  326. package/dist/metering/meter-repositories.test.d.ts +1 -0
  327. package/dist/metering/meter-repositories.test.js +419 -0
  328. package/dist/metering/metering.test.d.ts +1 -0
  329. package/dist/metering/metering.test.js +1046 -0
  330. package/dist/metering/reconciliation-cron.d.ts +37 -0
  331. package/dist/metering/reconciliation-cron.js +85 -0
  332. package/dist/metering/reconciliation-cron.test.d.ts +1 -0
  333. package/dist/metering/reconciliation-cron.test.js +162 -0
  334. package/dist/metering/reconciliation-repository.d.ts +27 -0
  335. package/dist/metering/reconciliation-repository.js +43 -0
  336. package/dist/metering/reconciliation-repository.test.d.ts +1 -0
  337. package/dist/metering/reconciliation-repository.test.js +160 -0
  338. package/dist/metering/types.d.ts +88 -0
  339. package/dist/metering/types.js +1 -0
  340. package/dist/metering/wal.d.ts +49 -0
  341. package/dist/metering/wal.js +124 -0
  342. package/dist/metering/wal.test.d.ts +1 -0
  343. package/dist/metering/wal.test.js +175 -0
  344. package/dist/middleware/csrf.d.ts +24 -0
  345. package/dist/middleware/csrf.js +80 -0
  346. package/dist/middleware/csrf.test.d.ts +1 -0
  347. package/dist/middleware/csrf.test.js +152 -0
  348. package/dist/middleware/drizzle-rate-limit-repository.d.ts +9 -0
  349. package/dist/middleware/drizzle-rate-limit-repository.js +52 -0
  350. package/dist/middleware/drizzle-rate-limit-repository.test.d.ts +1 -0
  351. package/dist/middleware/drizzle-rate-limit-repository.test.js +74 -0
  352. package/dist/middleware/get-client-ip.d.ts +22 -0
  353. package/dist/middleware/get-client-ip.js +51 -0
  354. package/dist/middleware/get-client-ip.test.d.ts +1 -0
  355. package/dist/middleware/get-client-ip.test.js +40 -0
  356. package/dist/middleware/index.d.ts +5 -0
  357. package/dist/middleware/index.js +4 -0
  358. package/dist/middleware/rate-limit-repository.d.ts +19 -0
  359. package/dist/middleware/rate-limit-repository.js +1 -0
  360. package/dist/middleware/rate-limit.d.ts +57 -0
  361. package/dist/middleware/rate-limit.js +109 -0
  362. package/dist/middleware/rate-limit.test.d.ts +1 -0
  363. package/dist/middleware/rate-limit.test.js +247 -0
  364. package/dist/security/credential-vault/audit-repository.d.ts +27 -0
  365. package/dist/security/credential-vault/audit-repository.js +42 -0
  366. package/dist/security/credential-vault/audit-repository.test.d.ts +1 -0
  367. package/dist/security/credential-vault/audit-repository.test.js +78 -0
  368. package/dist/security/credential-vault/credential-repository.d.ts +94 -0
  369. package/dist/security/credential-vault/credential-repository.js +145 -0
  370. package/dist/security/credential-vault/credential-repository.test.d.ts +1 -0
  371. package/dist/security/credential-vault/credential-repository.test.js +206 -0
  372. package/dist/security/credential-vault/index.d.ts +12 -0
  373. package/dist/security/credential-vault/index.js +6 -0
  374. package/dist/security/credential-vault/key-rotation.d.ts +18 -0
  375. package/dist/security/credential-vault/key-rotation.js +52 -0
  376. package/dist/security/credential-vault/key-rotation.test.d.ts +1 -0
  377. package/dist/security/credential-vault/key-rotation.test.js +95 -0
  378. package/dist/security/credential-vault/migrate-plaintext.d.ts +15 -0
  379. package/dist/security/credential-vault/migrate-plaintext.js +80 -0
  380. package/dist/security/credential-vault/migrate-plaintext.test.d.ts +1 -0
  381. package/dist/security/credential-vault/migrate-plaintext.test.js +111 -0
  382. package/dist/security/credential-vault/migration-check.d.ts +15 -0
  383. package/dist/security/credential-vault/migration-check.js +71 -0
  384. package/dist/security/credential-vault/migration-check.test.d.ts +1 -0
  385. package/dist/security/credential-vault/migration-check.test.js +457 -0
  386. package/dist/security/credential-vault/store.d.ts +106 -0
  387. package/dist/security/credential-vault/store.js +181 -0
  388. package/dist/security/credential-vault/store.test.d.ts +1 -0
  389. package/dist/security/credential-vault/store.test.js +482 -0
  390. package/dist/security/encryption.d.ts +22 -0
  391. package/dist/security/encryption.js +53 -0
  392. package/dist/security/encryption.test.d.ts +1 -0
  393. package/dist/security/encryption.test.js +95 -0
  394. package/dist/security/host-validation.d.ts +11 -0
  395. package/dist/security/host-validation.js +108 -0
  396. package/dist/security/host-validation.test.d.ts +1 -0
  397. package/dist/security/host-validation.test.js +106 -0
  398. package/dist/security/index.d.ts +11 -0
  399. package/dist/security/index.js +11 -0
  400. package/dist/security/key-audit.d.ts +16 -0
  401. package/dist/security/key-audit.js +35 -0
  402. package/dist/security/key-audit.test.d.ts +1 -0
  403. package/dist/security/key-audit.test.js +50 -0
  404. package/dist/security/key-injection.d.ts +28 -0
  405. package/dist/security/key-injection.js +57 -0
  406. package/dist/security/key-injection.test.d.ts +1 -0
  407. package/dist/security/key-injection.test.js +97 -0
  408. package/dist/security/key-validation.d.ts +16 -0
  409. package/dist/security/key-validation.js +78 -0
  410. package/dist/security/key-validation.test.d.ts +1 -0
  411. package/dist/security/key-validation.test.js +87 -0
  412. package/dist/security/redirect-allowlist.d.ts +6 -0
  413. package/dist/security/redirect-allowlist.js +36 -0
  414. package/dist/security/redirect-allowlist.test.d.ts +1 -0
  415. package/dist/security/redirect-allowlist.test.js +55 -0
  416. package/dist/security/tenant-keys/capability-settings-store.d.ts +22 -0
  417. package/dist/security/tenant-keys/capability-settings-store.js +33 -0
  418. package/dist/security/tenant-keys/capability-settings-store.test.d.ts +1 -0
  419. package/dist/security/tenant-keys/capability-settings-store.test.js +77 -0
  420. package/dist/security/tenant-keys/index.d.ts +10 -0
  421. package/dist/security/tenant-keys/index.js +5 -0
  422. package/dist/security/tenant-keys/key-resolution-repository.d.ts +15 -0
  423. package/dist/security/tenant-keys/key-resolution-repository.js +18 -0
  424. package/dist/security/tenant-keys/key-resolution-repository.test.d.ts +1 -0
  425. package/dist/security/tenant-keys/key-resolution-repository.test.js +72 -0
  426. package/dist/security/tenant-keys/key-resolution.d.ts +39 -0
  427. package/dist/security/tenant-keys/key-resolution.js +59 -0
  428. package/dist/security/tenant-keys/key-resolution.test.d.ts +1 -0
  429. package/dist/security/tenant-keys/key-resolution.test.js +97 -0
  430. package/dist/security/tenant-keys/org-key-resolution.d.ts +30 -0
  431. package/dist/security/tenant-keys/org-key-resolution.js +50 -0
  432. package/dist/security/tenant-keys/org-key-resolution.test.d.ts +1 -0
  433. package/dist/security/tenant-keys/org-key-resolution.test.js +103 -0
  434. package/dist/security/tenant-keys/tenant-key-repository.d.ts +36 -0
  435. package/dist/security/tenant-keys/tenant-key-repository.js +96 -0
  436. package/dist/security/tenant-keys/tenant-key-repository.test.d.ts +1 -0
  437. package/dist/security/tenant-keys/tenant-key-repository.test.js +114 -0
  438. package/dist/security/types.d.ts +35 -0
  439. package/dist/security/types.js +15 -0
  440. package/dist/tenancy/drizzle-org-repository.d.ts +40 -0
  441. package/dist/tenancy/drizzle-org-repository.js +126 -0
  442. package/dist/tenancy/index.d.ts +6 -0
  443. package/dist/tenancy/index.js +3 -0
  444. package/dist/tenancy/org-member-repository.d.ts +57 -0
  445. package/dist/tenancy/org-member-repository.js +99 -0
  446. package/dist/tenancy/org-repository.test.d.ts +1 -0
  447. package/dist/tenancy/org-repository.test.js +143 -0
  448. package/dist/tenancy/org-service.d.ts +70 -0
  449. package/dist/tenancy/org-service.js +223 -0
  450. package/dist/tenancy/org-service.test.d.ts +1 -0
  451. package/dist/tenancy/org-service.test.js +550 -0
  452. package/dist/test/db.d.ts +33 -0
  453. package/dist/test/db.js +65 -0
  454. package/dist/trpc/index.d.ts +1 -0
  455. package/dist/trpc/index.js +1 -0
  456. package/dist/trpc/init.d.ts +49 -0
  457. package/dist/trpc/init.js +108 -0
  458. package/dist/trpc/init.test.d.ts +1 -0
  459. package/dist/trpc/init.test.js +154 -0
  460. package/drizzle/migrations/0000_slippery_mandrill.sql +559 -0
  461. package/drizzle/migrations/meta/0000_snapshot.json +4374 -0
  462. package/drizzle/migrations/meta/_journal.json +13 -0
  463. package/drizzle.config.ts +41 -0
  464. package/package.json +64 -0
  465. package/src/admin/admin-audit-log-repository.ts +135 -0
  466. package/src/admin/audit-log.ts +111 -0
  467. package/src/admin/index.ts +6 -0
  468. package/src/admin/role-store.ts +134 -0
  469. package/src/auth/api-key-repository.test.ts +63 -0
  470. package/src/auth/api-key-repository.ts +46 -0
  471. package/src/auth/auth.test.ts +166 -0
  472. package/src/auth/better-auth.ts +216 -0
  473. package/src/auth/index.ts +520 -0
  474. package/src/auth/login-history-repository.test.ts +54 -0
  475. package/src/auth/login-history-repository.ts +28 -0
  476. package/src/auth/middleware.test.ts +264 -0
  477. package/src/auth/middleware.ts +117 -0
  478. package/src/auth/scoped-tokens.test.ts +362 -0
  479. package/src/auth/tenant-access.test.ts +69 -0
  480. package/src/auth/user-creator.test.ts +98 -0
  481. package/src/auth/user-creator.ts +54 -0
  482. package/src/auth/user-role-repository.test.ts +149 -0
  483. package/src/auth/user-role-repository.ts +67 -0
  484. package/src/billing/drizzle-webhook-seen-repository.ts +34 -0
  485. package/src/billing/index.ts +22 -0
  486. package/src/billing/payment-processor.test.ts +93 -0
  487. package/src/billing/payment-processor.ts +150 -0
  488. package/src/billing/payram/cents-credits-boundary.test.ts +84 -0
  489. package/src/billing/payram/charge-store.test.ts +84 -0
  490. package/src/billing/payram/charge-store.ts +109 -0
  491. package/src/billing/payram/checkout.test.ts +99 -0
  492. package/src/billing/payram/checkout.ts +40 -0
  493. package/src/billing/payram/client.test.ts +62 -0
  494. package/src/billing/payram/client.ts +21 -0
  495. package/src/billing/payram/index.ts +14 -0
  496. package/src/billing/payram/types.ts +44 -0
  497. package/src/billing/payram/webhook.test.ts +318 -0
  498. package/src/billing/payram/webhook.ts +97 -0
  499. package/src/billing/stripe/cents-credits-boundary.test.ts +70 -0
  500. package/src/billing/stripe/checkout.test.ts +186 -0
  501. package/src/billing/stripe/checkout.ts +82 -0
  502. package/src/billing/stripe/client.test.ts +64 -0
  503. package/src/billing/stripe/client.ts +39 -0
  504. package/src/billing/stripe/credit-prices.test.ts +114 -0
  505. package/src/billing/stripe/credit-prices.ts +113 -0
  506. package/src/billing/stripe/index.ts +14 -0
  507. package/src/billing/stripe/payment-methods-detach-all.test.ts +53 -0
  508. package/src/billing/stripe/payment-methods.test.ts +157 -0
  509. package/src/billing/stripe/payment-methods.ts +76 -0
  510. package/src/billing/stripe/portal.test.ts +63 -0
  511. package/src/billing/stripe/portal.ts +25 -0
  512. package/src/billing/stripe/setup-intent.test.ts +78 -0
  513. package/src/billing/stripe/setup-intent.ts +34 -0
  514. package/src/billing/stripe/stripe-payment-processor.test.ts +517 -0
  515. package/src/billing/stripe/stripe-payment-processor.ts +255 -0
  516. package/src/billing/stripe/tenant-store.test.ts +124 -0
  517. package/src/billing/stripe/tenant-store.ts +151 -0
  518. package/src/billing/stripe/types.ts +53 -0
  519. package/src/billing/webhook-seen-repository.ts +24 -0
  520. package/src/config/billing-env.test.ts +54 -0
  521. package/src/config/index.ts +44 -0
  522. package/src/config/logger.ts +12 -0
  523. package/src/config/provider-endpoints.ts +14 -0
  524. package/src/credits/auto-topup-charge.test.ts +292 -0
  525. package/src/credits/auto-topup-charge.ts +171 -0
  526. package/src/credits/auto-topup-event-log-repository.test.ts +99 -0
  527. package/src/credits/auto-topup-event-log-repository.ts +30 -0
  528. package/src/credits/auto-topup-schedule.test.ts +179 -0
  529. package/src/credits/auto-topup-schedule.ts +93 -0
  530. package/src/credits/auto-topup-settings-repository.test.ts +123 -0
  531. package/src/credits/auto-topup-settings-repository.ts +245 -0
  532. package/src/credits/auto-topup-usage.test.ts +220 -0
  533. package/src/credits/auto-topup-usage.ts +68 -0
  534. package/src/credits/credit-expiry-cron.test.ts +125 -0
  535. package/src/credits/credit-expiry-cron.ts +76 -0
  536. package/src/credits/credit-ledger-extra.test.ts +57 -0
  537. package/src/credits/credit-ledger.bench.ts +56 -0
  538. package/src/credits/credit-ledger.test.ts +276 -0
  539. package/src/credits/credit-ledger.ts +450 -0
  540. package/src/credits/credit-transaction-repository.test.ts +274 -0
  541. package/src/credits/credit-transaction-repository.ts +62 -0
  542. package/src/credits/credit.test.ts +234 -0
  543. package/src/credits/credit.ts +160 -0
  544. package/src/credits/dividend-cron.test.ts +158 -0
  545. package/src/credits/dividend-cron.ts +127 -0
  546. package/src/credits/dividend-repository.test.ts +223 -0
  547. package/src/credits/dividend-repository.ts +182 -0
  548. package/src/credits/index.ts +25 -0
  549. package/src/credits/repository-types.ts +33 -0
  550. package/src/credits/signup-grant.test.ts +63 -0
  551. package/src/credits/signup-grant.ts +44 -0
  552. package/src/credits/tenant-customer-repository.ts +28 -0
  553. package/src/db/auth-user-repository.ts +124 -0
  554. package/src/db/credit-column.ts +17 -0
  555. package/src/db/index.ts +21 -0
  556. package/src/db/schema/account-deletion-requests.ts +41 -0
  557. package/src/db/schema/account-export-requests.ts +24 -0
  558. package/src/db/schema/admin-audit.ts +26 -0
  559. package/src/db/schema/admin-users.ts +31 -0
  560. package/src/db/schema/affiliate-fraud.ts +23 -0
  561. package/src/db/schema/affiliate.ts +38 -0
  562. package/src/db/schema/coupon-codes.ts +22 -0
  563. package/src/db/schema/credit-auto-topup-settings.ts +32 -0
  564. package/src/db/schema/credit-auto-topup.ts +26 -0
  565. package/src/db/schema/credits.ts +44 -0
  566. package/src/db/schema/dividend-distributions.ts +24 -0
  567. package/src/db/schema/email-notifications.ts +26 -0
  568. package/src/db/schema/index.ts +33 -0
  569. package/src/db/schema/meter-events.ts +70 -0
  570. package/src/db/schema/notification-preferences.ts +19 -0
  571. package/src/db/schema/notification-queue.ts +45 -0
  572. package/src/db/schema/org-memberships.ts +20 -0
  573. package/src/db/schema/organization-members.ts +37 -0
  574. package/src/db/schema/payram.ts +26 -0
  575. package/src/db/schema/platform-api-keys.ts +25 -0
  576. package/src/db/schema/promotion-redemptions.ts +23 -0
  577. package/src/db/schema/promotions.ts +57 -0
  578. package/src/db/schema/provider-credentials.ts +41 -0
  579. package/src/db/schema/rate-limit-entries.ts +12 -0
  580. package/src/db/schema/secret-audit-log.ts +20 -0
  581. package/src/db/schema/session-usage.ts +24 -0
  582. package/src/db/schema/spending-limits.ts +9 -0
  583. package/src/db/schema/tenant-addons.ts +14 -0
  584. package/src/db/schema/tenant-api-keys.ts +26 -0
  585. package/src/db/schema/tenant-capability-settings.ts +17 -0
  586. package/src/db/schema/tenant-customers.ts +35 -0
  587. package/src/db/schema/tenants.ts +23 -0
  588. package/src/db/schema/user-roles.ts +23 -0
  589. package/src/db/schema/webhook-seen-events.ts +14 -0
  590. package/src/email/billing-emails.test.ts +198 -0
  591. package/src/email/billing-emails.ts +211 -0
  592. package/src/email/client.test.ts +149 -0
  593. package/src/email/client.ts +137 -0
  594. package/src/email/drizzle-billing-email-repository.test.ts +52 -0
  595. package/src/email/drizzle-billing-email-repository.ts +59 -0
  596. package/src/email/index.ts +57 -0
  597. package/src/email/notification-preferences-store.test.ts +102 -0
  598. package/src/email/notification-preferences-store.ts +90 -0
  599. package/src/email/notification-queue-store.test.ts +215 -0
  600. package/src/email/notification-queue-store.ts +127 -0
  601. package/src/email/notification-repository-types.ts +101 -0
  602. package/src/email/notification-service.test.ts +178 -0
  603. package/src/email/notification-service.ts +265 -0
  604. package/src/email/notification-templates.test.ts +261 -0
  605. package/src/email/notification-templates.ts +727 -0
  606. package/src/email/notification-worker.test.ts +189 -0
  607. package/src/email/notification-worker.ts +133 -0
  608. package/src/email/require-verified.ts +65 -0
  609. package/src/email/resend-adapter.test.ts +253 -0
  610. package/src/email/resend-adapter.ts +157 -0
  611. package/src/email/templates.test.ts +217 -0
  612. package/src/email/templates.ts +469 -0
  613. package/src/email/verification.test.ts +185 -0
  614. package/src/email/verification.ts +110 -0
  615. package/src/index.ts +51 -0
  616. package/src/metering/aggregator.test.ts +239 -0
  617. package/src/metering/aggregator.ts +160 -0
  618. package/src/metering/dlq.test.ts +134 -0
  619. package/src/metering/dlq.ts +102 -0
  620. package/src/metering/drizzle-usage-summary-repository.ts +167 -0
  621. package/src/metering/emitter.test.ts +202 -0
  622. package/src/metering/emitter.ts +227 -0
  623. package/src/metering/index.ts +21 -0
  624. package/src/metering/load-test.bench.ts +130 -0
  625. package/src/metering/meter-event-repository.ts +87 -0
  626. package/src/metering/meter-repositories.test.ts +491 -0
  627. package/src/metering/metering.test.ts +1317 -0
  628. package/src/metering/reconciliation-cron.test.ts +202 -0
  629. package/src/metering/reconciliation-cron.ts +134 -0
  630. package/src/metering/reconciliation-repository.test.ts +196 -0
  631. package/src/metering/reconciliation-repository.ts +83 -0
  632. package/src/metering/types.ts +93 -0
  633. package/src/metering/wal.test.ts +222 -0
  634. package/src/metering/wal.ts +139 -0
  635. package/src/middleware/csrf.test.ts +178 -0
  636. package/src/middleware/csrf.ts +101 -0
  637. package/src/middleware/drizzle-rate-limit-repository.test.ts +97 -0
  638. package/src/middleware/drizzle-rate-limit-repository.ts +57 -0
  639. package/src/middleware/get-client-ip.test.ts +49 -0
  640. package/src/middleware/get-client-ip.ts +62 -0
  641. package/src/middleware/index.ts +12 -0
  642. package/src/middleware/rate-limit-repository.ts +22 -0
  643. package/src/middleware/rate-limit.test.ts +338 -0
  644. package/src/middleware/rate-limit.ts +169 -0
  645. package/src/security/credential-vault/audit-repository.test.ts +91 -0
  646. package/src/security/credential-vault/audit-repository.ts +64 -0
  647. package/src/security/credential-vault/credential-repository.test.ts +264 -0
  648. package/src/security/credential-vault/credential-repository.ts +233 -0
  649. package/src/security/credential-vault/index.ts +26 -0
  650. package/src/security/credential-vault/key-rotation.test.ts +139 -0
  651. package/src/security/credential-vault/key-rotation.ts +70 -0
  652. package/src/security/credential-vault/migrate-plaintext.test.ts +138 -0
  653. package/src/security/credential-vault/migrate-plaintext.ts +101 -0
  654. package/src/security/credential-vault/migration-check.test.ts +533 -0
  655. package/src/security/credential-vault/migration-check.ts +88 -0
  656. package/src/security/credential-vault/store.test.ts +569 -0
  657. package/src/security/credential-vault/store.ts +284 -0
  658. package/src/security/encryption.test.ts +114 -0
  659. package/src/security/encryption.ts +65 -0
  660. package/src/security/host-validation.test.ts +136 -0
  661. package/src/security/host-validation.ts +116 -0
  662. package/src/security/index.ts +59 -0
  663. package/src/security/key-audit.test.ts +57 -0
  664. package/src/security/key-audit.ts +45 -0
  665. package/src/security/key-injection.test.ts +131 -0
  666. package/src/security/key-injection.ts +71 -0
  667. package/src/security/key-validation.test.ts +111 -0
  668. package/src/security/key-validation.ts +84 -0
  669. package/src/security/redirect-allowlist.test.ts +70 -0
  670. package/src/security/redirect-allowlist.ts +35 -0
  671. package/src/security/tenant-keys/capability-settings-store.test.ts +98 -0
  672. package/src/security/tenant-keys/capability-settings-store.ts +53 -0
  673. package/src/security/tenant-keys/index.ts +10 -0
  674. package/src/security/tenant-keys/key-resolution-repository.test.ts +95 -0
  675. package/src/security/tenant-keys/key-resolution-repository.ts +31 -0
  676. package/src/security/tenant-keys/key-resolution.test.ts +173 -0
  677. package/src/security/tenant-keys/key-resolution.ts +87 -0
  678. package/src/security/tenant-keys/org-key-resolution.test.ts +217 -0
  679. package/src/security/tenant-keys/org-key-resolution.ts +76 -0
  680. package/src/security/tenant-keys/tenant-key-repository.test.ts +143 -0
  681. package/src/security/tenant-keys/tenant-key-repository.ts +130 -0
  682. package/src/security/types.ts +43 -0
  683. package/src/tenancy/drizzle-org-repository.ts +169 -0
  684. package/src/tenancy/index.ts +6 -0
  685. package/src/tenancy/org-member-repository.ts +159 -0
  686. package/src/tenancy/org-repository.test.ts +172 -0
  687. package/src/tenancy/org-service.test.ts +634 -0
  688. package/src/tenancy/org-service.ts +290 -0
  689. package/src/test/db.ts +97 -0
  690. package/src/trpc/index.ts +11 -0
  691. package/src/trpc/init.test.ts +196 -0
  692. package/src/trpc/init.ts +138 -0
  693. package/tsconfig.json +20 -0
  694. package/vitest.config.ts +8 -0
@@ -0,0 +1,126 @@
1
+ import { and, desc, eq, gte, lt, sql } from "drizzle-orm";
2
+ import { adminUsers } from "../db/schema/admin-users.js";
3
+ import { creditTransactions } from "../db/schema/credits.js";
4
+ import { dividendDistributions } from "../db/schema/dividend-distributions.js";
5
+ import { Credit } from "./credit.js";
6
+ export class DrizzleDividendRepository {
7
+ db;
8
+ constructor(db) {
9
+ this.db = db;
10
+ }
11
+ async getStats(tenantId) {
12
+ // 1. Pool = sum of purchase amounts from yesterday UTC
13
+ const poolRow = (await this.db
14
+ // raw SQL: Drizzle cannot express COALESCE(SUM(...), 0) aggregate
15
+ .select({ total: sql `COALESCE(SUM(${creditTransactions.amount}), 0)` })
16
+ .from(creditTransactions)
17
+ .where(and(eq(creditTransactions.type, "purchase"),
18
+ // raw SQL: Drizzle cannot express date_trunc with interval arithmetic
19
+ sql `${creditTransactions.createdAt}::timestamp >= date_trunc('day', timezone('UTC', now())) - INTERVAL '1 day'`, sql `${creditTransactions.createdAt}::timestamp < date_trunc('day', timezone('UTC', now()))`)))[0];
20
+ const poolCents = poolRow?.total ?? 0;
21
+ const pool = Credit.fromCents(poolCents);
22
+ // 2. Active users = distinct tenants with a purchase in the last 7 days
23
+ const activeRow = (await this.db
24
+ // raw SQL: Drizzle cannot express COUNT(DISTINCT col)
25
+ .select({ count: sql `COUNT(DISTINCT ${creditTransactions.tenantId})` })
26
+ .from(creditTransactions)
27
+ .where(and(eq(creditTransactions.type, "purchase"),
28
+ // raw SQL: Drizzle cannot express timestamp comparison with interval arithmetic
29
+ sql `${creditTransactions.createdAt}::timestamp >= timezone('UTC', now()) - INTERVAL '7 days'`)))[0];
30
+ const activeUsers = activeRow?.count ?? 0;
31
+ // 3. Per-user projection (avoid division by zero)
32
+ const perUser = activeUsers > 0 ? Credit.fromRaw(Math.floor(pool.toRaw() / activeUsers)) : Credit.ZERO;
33
+ // 4. Next distribution = midnight UTC tonight
34
+ const now = new Date();
35
+ const nextMidnight = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() + 1, 0, 0, 0));
36
+ const nextDistributionAt = nextMidnight.toISOString();
37
+ // 5. User eligibility — last purchase within 7 days
38
+ const userPurchaseRow = (await this.db
39
+ .select({ createdAt: creditTransactions.createdAt })
40
+ .from(creditTransactions)
41
+ .where(and(eq(creditTransactions.tenantId, tenantId), eq(creditTransactions.type, "purchase")))
42
+ .orderBy(desc(creditTransactions.createdAt))
43
+ .limit(1))[0];
44
+ let userEligible = false;
45
+ let userLastPurchaseAt = null;
46
+ let userWindowExpiresAt = null;
47
+ if (userPurchaseRow) {
48
+ const rawTs = userPurchaseRow.createdAt;
49
+ // Parse the timestamp directly. PGlite may return ISO strings with or without
50
+ // timezone suffix. JavaScript's Date constructor handles ISO 8601 strings natively.
51
+ const lastPurchase = new Date(rawTs);
52
+ userLastPurchaseAt = lastPurchase.toISOString();
53
+ const windowExpiry = new Date(lastPurchase.getTime() + 7 * 24 * 60 * 60 * 1000);
54
+ userWindowExpiresAt = windowExpiry.toISOString();
55
+ userEligible = windowExpiry.getTime() > Date.now();
56
+ }
57
+ return {
58
+ pool,
59
+ activeUsers,
60
+ perUser,
61
+ nextDistributionAt,
62
+ userEligible,
63
+ userLastPurchaseAt,
64
+ userWindowExpiresAt,
65
+ };
66
+ }
67
+ async getHistory(tenantId, limit, offset) {
68
+ const safeLimit = Math.min(Math.max(1, limit), 250);
69
+ const safeOffset = Math.max(0, offset);
70
+ const rows = await this.db
71
+ .select({
72
+ date: dividendDistributions.date,
73
+ amountCents: dividendDistributions.amountCents,
74
+ poolCents: dividendDistributions.poolCents,
75
+ activeUsers: dividendDistributions.activeUsers,
76
+ })
77
+ .from(dividendDistributions)
78
+ .where(eq(dividendDistributions.tenantId, tenantId))
79
+ .orderBy(desc(dividendDistributions.date))
80
+ .limit(safeLimit)
81
+ .offset(safeOffset);
82
+ return rows.map((row) => ({
83
+ date: row.date,
84
+ amount: Credit.fromCents(row.amountCents),
85
+ pool: Credit.fromCents(row.poolCents),
86
+ activeUsers: row.activeUsers,
87
+ }));
88
+ }
89
+ async getLifetimeTotal(tenantId) {
90
+ const row = (await this.db
91
+ // raw SQL: Drizzle cannot express COALESCE(SUM(...), 0) aggregate
92
+ .select({ total: sql `COALESCE(SUM(${dividendDistributions.amountCents}), 0)` })
93
+ .from(dividendDistributions)
94
+ .where(eq(dividendDistributions.tenantId, tenantId)))[0];
95
+ return Credit.fromCents(row?.total ?? 0);
96
+ }
97
+ async getDigestTenantAggregates(windowStart, windowEnd) {
98
+ const rows = await this.db
99
+ .select({
100
+ tenantId: dividendDistributions.tenantId,
101
+ // raw SQL: Drizzle cannot express SUM/COUNT(DISTINCT)/AVG with CAST aggregates
102
+ totalCents: sql `SUM(${dividendDistributions.amountCents})`,
103
+ distributionCount: sql `COUNT(DISTINCT ${dividendDistributions.date})`,
104
+ avgPoolCents: sql `CAST(AVG(${dividendDistributions.poolCents}) AS INTEGER)`,
105
+ avgActiveUsers: sql `CAST(AVG(${dividendDistributions.activeUsers}) AS INTEGER)`,
106
+ })
107
+ .from(dividendDistributions)
108
+ .where(and(gte(dividendDistributions.date, windowStart), lt(dividendDistributions.date, windowEnd)))
109
+ .groupBy(dividendDistributions.tenantId);
110
+ return rows.map((row) => ({
111
+ tenantId: row.tenantId,
112
+ total: Credit.fromCents(row.totalCents),
113
+ distributionCount: row.distributionCount,
114
+ avgPool: Credit.fromCents(row.avgPoolCents),
115
+ avgActiveUsers: row.avgActiveUsers,
116
+ }));
117
+ }
118
+ async getTenantEmail(tenantId) {
119
+ const row = (await this.db
120
+ .select({ email: adminUsers.email })
121
+ .from(adminUsers)
122
+ .where(eq(adminUsers.tenantId, tenantId))
123
+ .limit(1))[0];
124
+ return row?.email;
125
+ }
126
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,176 @@
1
+ import crypto from "node:crypto";
2
+ import { afterAll, beforeAll, beforeEach, describe, expect, it } from "vitest";
3
+ import { adminUsers } from "../db/schema/admin-users.js";
4
+ import { dividendDistributions } from "../db/schema/dividend-distributions.js";
5
+ import { createTestDb, truncateAllTables } from "../test/db.js";
6
+ import { Credit } from "./credit.js";
7
+ import { CreditLedger } from "./credit-ledger.js";
8
+ import { DrizzleDividendRepository } from "./dividend-repository.js";
9
+ let pool;
10
+ let db;
11
+ beforeAll(async () => {
12
+ ({ db, pool } = await createTestDb());
13
+ });
14
+ afterAll(async () => {
15
+ await pool.close();
16
+ });
17
+ /** Insert a dividend_distributions row directly (bypassing repo — it has no write method). */
18
+ async function seedDividend(opts) {
19
+ await db.insert(dividendDistributions).values({
20
+ id: crypto.randomUUID(),
21
+ tenantId: opts.tenantId,
22
+ date: opts.date,
23
+ amountCents: opts.amountCents,
24
+ poolCents: opts.poolCents,
25
+ activeUsers: opts.activeUsers,
26
+ });
27
+ }
28
+ /** Insert an admin_users row for getTenantEmail tests. */
29
+ async function seedAdminUser(tenantId, email) {
30
+ await db.insert(adminUsers).values({
31
+ id: crypto.randomUUID(),
32
+ email,
33
+ tenantId,
34
+ status: "active",
35
+ role: "tenant_admin",
36
+ createdAt: Date.now(),
37
+ });
38
+ }
39
+ describe("DrizzleDividendRepository", () => {
40
+ let repo;
41
+ beforeEach(async () => {
42
+ await truncateAllTables(pool);
43
+ repo = new DrizzleDividendRepository(db);
44
+ });
45
+ // --- getHistory() ---
46
+ describe("getHistory()", () => {
47
+ it("returns empty array when tenant has no distributions", async () => {
48
+ const history = await repo.getHistory("nonexistent", 50, 0);
49
+ expect(history).toEqual([]);
50
+ });
51
+ it("returns distributions in reverse chronological order", async () => {
52
+ await seedDividend({ tenantId: "t1", date: "2026-01-01", amountCents: 10, poolCents: 1000, activeUsers: 5 });
53
+ await seedDividend({ tenantId: "t1", date: "2026-01-02", amountCents: 20, poolCents: 2000, activeUsers: 10 });
54
+ await seedDividend({ tenantId: "t1", date: "2026-01-03", amountCents: 30, poolCents: 3000, activeUsers: 15 });
55
+ const history = await repo.getHistory("t1", 50, 0);
56
+ expect(history).toHaveLength(3);
57
+ expect(history[0].date).toBe("2026-01-03");
58
+ expect(history[1].date).toBe("2026-01-02");
59
+ expect(history[2].date).toBe("2026-01-01");
60
+ });
61
+ it("converts amountCents and poolCents to Credit objects", async () => {
62
+ await seedDividend({ tenantId: "t1", date: "2026-01-15", amountCents: 42, poolCents: 5000, activeUsers: 8 });
63
+ const history = await repo.getHistory("t1", 50, 0);
64
+ expect(history).toHaveLength(1);
65
+ expect(history[0].amount.toCents()).toBe(42);
66
+ expect(history[0].pool.toCents()).toBe(5000);
67
+ expect(history[0].activeUsers).toBe(8);
68
+ });
69
+ it("respects limit and offset", async () => {
70
+ for (let i = 1; i <= 5; i++) {
71
+ await seedDividend({
72
+ tenantId: "t1",
73
+ date: `2026-01-0${i}`,
74
+ amountCents: i * 10,
75
+ poolCents: 1000,
76
+ activeUsers: 5,
77
+ });
78
+ }
79
+ const page1 = await repo.getHistory("t1", 2, 0);
80
+ expect(page1).toHaveLength(2);
81
+ expect(page1[0].date).toBe("2026-01-05");
82
+ expect(page1[1].date).toBe("2026-01-04");
83
+ const page2 = await repo.getHistory("t1", 2, 2);
84
+ expect(page2).toHaveLength(2);
85
+ expect(page2[0].date).toBe("2026-01-03");
86
+ });
87
+ it("is tenant-isolated", async () => {
88
+ await seedDividend({ tenantId: "t1", date: "2026-01-01", amountCents: 10, poolCents: 1000, activeUsers: 5 });
89
+ await seedDividend({ tenantId: "t2", date: "2026-01-01", amountCents: 99, poolCents: 9000, activeUsers: 50 });
90
+ const t1History = await repo.getHistory("t1", 50, 0);
91
+ expect(t1History).toHaveLength(1);
92
+ expect(t1History[0].amount.toCents()).toBe(10);
93
+ });
94
+ });
95
+ // --- getLifetimeTotal() ---
96
+ describe("getLifetimeTotal()", () => {
97
+ it("returns Credit.ZERO when tenant has no distributions", async () => {
98
+ const total = await repo.getLifetimeTotal("nonexistent");
99
+ expect(total.toCents()).toBe(0);
100
+ });
101
+ it("sums all distributions for a tenant", async () => {
102
+ await seedDividend({ tenantId: "t1", date: "2026-01-01", amountCents: 10, poolCents: 1000, activeUsers: 5 });
103
+ await seedDividend({ tenantId: "t1", date: "2026-01-02", amountCents: 25, poolCents: 2000, activeUsers: 10 });
104
+ const total = await repo.getLifetimeTotal("t1");
105
+ expect(total.toCents()).toBe(35);
106
+ });
107
+ it("is tenant-isolated", async () => {
108
+ await seedDividend({ tenantId: "t1", date: "2026-01-01", amountCents: 10, poolCents: 1000, activeUsers: 5 });
109
+ await seedDividend({ tenantId: "t2", date: "2026-01-01", amountCents: 999, poolCents: 9000, activeUsers: 50 });
110
+ const total = await repo.getLifetimeTotal("t1");
111
+ expect(total.toCents()).toBe(10);
112
+ });
113
+ });
114
+ // --- getDigestTenantAggregates() ---
115
+ describe("getDigestTenantAggregates()", () => {
116
+ it("returns empty array when no distributions in window", async () => {
117
+ const result = await repo.getDigestTenantAggregates("2026-02-01", "2026-02-28");
118
+ expect(result).toEqual([]);
119
+ });
120
+ it("aggregates distributions per tenant within [windowStart, windowEnd)", async () => {
121
+ // In window
122
+ await seedDividend({ tenantId: "t1", date: "2026-02-01", amountCents: 10, poolCents: 1000, activeUsers: 5 });
123
+ await seedDividend({ tenantId: "t1", date: "2026-02-02", amountCents: 20, poolCents: 2000, activeUsers: 10 });
124
+ // Out of window
125
+ await seedDividend({ tenantId: "t1", date: "2026-03-01", amountCents: 999, poolCents: 9000, activeUsers: 50 });
126
+ const result = await repo.getDigestTenantAggregates("2026-02-01", "2026-03-01");
127
+ expect(result).toHaveLength(1);
128
+ expect(result[0].tenantId).toBe("t1");
129
+ expect(result[0].total.toCents()).toBe(30); // 10 + 20
130
+ expect(result[0].distributionCount).toBe(2);
131
+ });
132
+ it("returns multiple tenants", async () => {
133
+ await seedDividend({ tenantId: "t1", date: "2026-02-01", amountCents: 10, poolCents: 1000, activeUsers: 5 });
134
+ await seedDividend({ tenantId: "t2", date: "2026-02-01", amountCents: 50, poolCents: 5000, activeUsers: 20 });
135
+ const result = await repo.getDigestTenantAggregates("2026-02-01", "2026-03-01");
136
+ expect(result).toHaveLength(2);
137
+ const t1 = result.find((r) => r.tenantId === "t1");
138
+ const t2 = result.find((r) => r.tenantId === "t2");
139
+ expect(t1?.total.toCents()).toBe(10);
140
+ expect(t2?.total.toCents()).toBe(50);
141
+ });
142
+ });
143
+ // --- getStats() ---
144
+ describe("getStats()", () => {
145
+ it("returns zero pool and zero activeUsers when no purchase transactions exist", async () => {
146
+ const stats = await repo.getStats("t1");
147
+ expect(stats.pool.toCents()).toBe(0);
148
+ expect(stats.activeUsers).toBe(0);
149
+ expect(stats.perUser.toCents()).toBe(0);
150
+ expect(stats.userEligible).toBe(false);
151
+ expect(stats.userLastPurchaseAt).toBeNull();
152
+ expect(stats.userWindowExpiresAt).toBeNull();
153
+ expect(stats.nextDistributionAt).toEqual(expect.any(String));
154
+ });
155
+ it("marks user as eligible when they have a recent purchase", async () => {
156
+ const ledger = new CreditLedger(db);
157
+ await ledger.credit("t1", Credit.fromCents(100), "purchase", "recent buy");
158
+ const stats = await repo.getStats("t1");
159
+ expect(stats.userEligible).toBe(true);
160
+ expect(stats.userLastPurchaseAt).toEqual(expect.any(String));
161
+ expect(stats.userWindowExpiresAt).toEqual(expect.any(String));
162
+ });
163
+ });
164
+ // --- getTenantEmail() ---
165
+ describe("getTenantEmail()", () => {
166
+ it("returns undefined when tenant has no admin user", async () => {
167
+ const email = await repo.getTenantEmail("nonexistent");
168
+ expect(email).toBeUndefined();
169
+ });
170
+ it("returns the email for the tenant's admin user", async () => {
171
+ await seedAdminUser("t1", "alice@example.com");
172
+ const email = await repo.getTenantEmail("t1");
173
+ expect(email).toBe("alice@example.com");
174
+ });
175
+ });
176
+ });
@@ -0,0 +1,9 @@
1
+ export type { AutoTopupSettings, IAutoTopupSettingsRepository, } from "./auto-topup-settings-repository.js";
2
+ export { ALLOWED_SCHEDULE_INTERVALS, ALLOWED_THRESHOLDS, ALLOWED_TOPUP_AMOUNTS, computeNextScheduleAt, DrizzleAutoTopupSettingsRepository, } from "./auto-topup-settings-repository.js";
3
+ export type { CreditExpiryCronConfig, CreditExpiryCronResult } from "./credit-expiry-cron.js";
4
+ export { runCreditExpiryCron } from "./credit-expiry-cron.js";
5
+ export type { CreditTransaction, CreditType, DebitType, HistoryOptions, ICreditLedger, TransactionType, } from "./credit-ledger.js";
6
+ export { CreditLedger, DrizzleCreditLedger, InsufficientBalanceError } from "./credit-ledger.js";
7
+ export { grantSignupCredits, SIGNUP_GRANT } from "./signup-grant.js";
8
+ export { Credit } from "./credit.js";
9
+ export type { ITenantCustomerRepository, TenantCustomerRow } from "./tenant-customer-repository.js";
@@ -0,0 +1,5 @@
1
+ export { ALLOWED_SCHEDULE_INTERVALS, ALLOWED_THRESHOLDS, ALLOWED_TOPUP_AMOUNTS, computeNextScheduleAt, DrizzleAutoTopupSettingsRepository, } from "./auto-topup-settings-repository.js";
2
+ export { runCreditExpiryCron } from "./credit-expiry-cron.js";
3
+ export { CreditLedger, DrizzleCreditLedger, InsufficientBalanceError } from "./credit-ledger.js";
4
+ export { grantSignupCredits, SIGNUP_GRANT } from "./signup-grant.js";
5
+ export { Credit } from "./credit.js";
@@ -0,0 +1,29 @@
1
+ import type { Credit } from "./credit.js";
2
+ /** Domain type for a provisioned phone number tracked for monthly billing. */
3
+ export interface ProvisionedPhoneNumber {
4
+ sid: string;
5
+ tenantId: string;
6
+ phoneNumber: string;
7
+ provisionedAt: string;
8
+ lastBilledAt: string | null;
9
+ }
10
+ export interface DividendStats {
11
+ pool: Credit;
12
+ activeUsers: number;
13
+ perUser: Credit;
14
+ nextDistributionAt: string;
15
+ userEligible: boolean;
16
+ userLastPurchaseAt: string | null;
17
+ userWindowExpiresAt: string | null;
18
+ }
19
+ export interface DividendHistoryEntry {
20
+ date: string;
21
+ amount: Credit;
22
+ pool: Credit;
23
+ activeUsers: number;
24
+ }
25
+ export interface WebhookSeenEvent {
26
+ eventId: string;
27
+ source: string;
28
+ seenAt: number;
29
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,12 @@
1
+ import { Credit } from "./credit.js";
2
+ import type { ICreditLedger } from "./credit-ledger.js";
3
+ /** Signup grant amount: $5.00 */
4
+ export declare const SIGNUP_GRANT: Credit;
5
+ /**
6
+ * Grant the signup credit bonus to a newly verified tenant.
7
+ *
8
+ * Idempotent: uses `signup:<tenantId>` as referenceId to prevent double-grants.
9
+ *
10
+ * @returns true if the grant was applied, false if already granted.
11
+ */
12
+ export declare function grantSignupCredits(ledger: ICreditLedger, tenantId: string): Promise<boolean>;
@@ -0,0 +1,35 @@
1
+ import { Credit } from "./credit.js";
2
+ /** Signup grant amount: $5.00 */
3
+ export const SIGNUP_GRANT = Credit.fromDollars(5);
4
+ /**
5
+ * Grant the signup credit bonus to a newly verified tenant.
6
+ *
7
+ * Idempotent: uses `signup:<tenantId>` as referenceId to prevent double-grants.
8
+ *
9
+ * @returns true if the grant was applied, false if already granted.
10
+ */
11
+ export async function grantSignupCredits(ledger, tenantId) {
12
+ const refId = `signup:${tenantId}`;
13
+ // Idempotency check
14
+ if (await ledger.hasReferenceId(refId)) {
15
+ return false;
16
+ }
17
+ try {
18
+ await ledger.credit(tenantId, SIGNUP_GRANT, "signup_grant", "Welcome bonus — $5.00 credit on email verification", refId);
19
+ }
20
+ catch (err) {
21
+ // Concurrent verify-email request won the race and already inserted the same referenceId.
22
+ // Treat unique constraint violation as a no-op (idempotent).
23
+ if (isUniqueConstraintViolation(err))
24
+ return false;
25
+ throw err;
26
+ }
27
+ return true;
28
+ }
29
+ function isUniqueConstraintViolation(err) {
30
+ if (!(err instanceof Error))
31
+ return false;
32
+ if (err.code === "23505")
33
+ return true;
34
+ return err.message.includes("UNIQUE") || err.message.includes("duplicate key");
35
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,51 @@
1
+ import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
2
+ import { createTestDb, truncateAllTables } from "../test/db.js";
3
+ import { CreditLedger } from "./credit-ledger.js";
4
+ import { grantSignupCredits, SIGNUP_GRANT } from "./signup-grant.js";
5
+ describe("grantSignupCredits", () => {
6
+ let pool;
7
+ let db;
8
+ let ledger;
9
+ beforeAll(async () => {
10
+ ({ db, pool } = await createTestDb());
11
+ });
12
+ afterAll(async () => {
13
+ await pool.close();
14
+ });
15
+ beforeEach(async () => {
16
+ await truncateAllTables(pool);
17
+ ledger = new CreditLedger(db);
18
+ });
19
+ it("grants credits to a new tenant and returns true", async () => {
20
+ const result = await grantSignupCredits(ledger, "tenant-1");
21
+ expect(result).toBe(true);
22
+ expect((await ledger.balance("tenant-1")).toCents()).toBe(SIGNUP_GRANT.toCents());
23
+ });
24
+ it("returns false for duplicate grant (idempotency)", async () => {
25
+ await grantSignupCredits(ledger, "tenant-1");
26
+ const result = await grantSignupCredits(ledger, "tenant-1");
27
+ expect(result).toBe(false);
28
+ expect((await ledger.balance("tenant-1")).toCents()).toBe(SIGNUP_GRANT.toCents());
29
+ });
30
+ it("grants independently to different tenants", async () => {
31
+ await grantSignupCredits(ledger, "tenant-1");
32
+ await grantSignupCredits(ledger, "tenant-2");
33
+ expect((await ledger.balance("tenant-1")).toCents()).toBe(SIGNUP_GRANT.toCents());
34
+ expect((await ledger.balance("tenant-2")).toCents()).toBe(SIGNUP_GRANT.toCents());
35
+ });
36
+ it("SIGNUP_GRANT.toCents() equals 500", () => {
37
+ expect(SIGNUP_GRANT.toCents()).toBe(500);
38
+ });
39
+ it("returns false when credit() throws a unique constraint violation (TOCTOU race)", async () => {
40
+ // Simulate two concurrent requests: both pass hasReferenceId check,
41
+ // then the second credit() call loses the race and gets a unique constraint error.
42
+ const uniqueErr = Object.assign(new Error("duplicate key value violates unique constraint"), {
43
+ code: "23505",
44
+ });
45
+ const racingLedger = new CreditLedger(db);
46
+ vi.spyOn(racingLedger, "hasReferenceId").mockResolvedValue(false);
47
+ vi.spyOn(racingLedger, "credit").mockRejectedValue(uniqueErr);
48
+ const result = await grantSignupCredits(racingLedger, "tenant-race");
49
+ expect(result).toBe(false);
50
+ });
51
+ });
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Stub interface for tenant-customer repository.
3
+ * The Drizzle implementation lives in the billing module (extracted in Task 12).
4
+ */
5
+ export interface TenantCustomerRow {
6
+ tenant: string;
7
+ processor_customer_id: string;
8
+ processor: string;
9
+ tier: string;
10
+ billing_hold: number;
11
+ inference_mode: string;
12
+ created_at: number;
13
+ updated_at: number;
14
+ }
15
+ export interface ITenantCustomerRepository {
16
+ getByTenant(tenant: string): Promise<TenantCustomerRow | null>;
17
+ getByProcessorCustomerId(processorCustomerId: string): Promise<TenantCustomerRow | null>;
18
+ upsert(row: {
19
+ tenant: string;
20
+ processorCustomerId: string;
21
+ tier?: string;
22
+ }): Promise<void>;
23
+ setTier(tenant: string, tier: string): Promise<void>;
24
+ setBillingHold(tenant: string, hold: boolean): Promise<void>;
25
+ hasBillingHold(tenant: string): Promise<boolean>;
26
+ getInferenceMode(tenant: string): Promise<string>;
27
+ setInferenceMode(tenant: string, mode: string): Promise<void>;
28
+ list(): Promise<TenantCustomerRow[]>;
29
+ buildCustomerIdMap(): Promise<Record<string, string>>;
30
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Stub interface for tenant-customer repository.
3
+ * The Drizzle implementation lives in the billing module (extracted in Task 12).
4
+ */
5
+ export {};
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Auth user repository — read/write the better-auth PostgreSQL user and account tables.
3
+ *
4
+ * Uses raw pg queries because better-auth manages its own schema
5
+ * independently of Drizzle.
6
+ */
7
+ import type { Pool } from "pg";
8
+ /**
9
+ * Ensure the twoFactorEnabled column exists on the better-auth user table.
10
+ * Idempotent — safe to call on every startup alongside runAuthMigrations().
11
+ */
12
+ export declare function initTwoFactorSchema(pool: Pool): Promise<void>;
13
+ export interface AuthUser {
14
+ id: string;
15
+ name: string;
16
+ email: string;
17
+ image: string | null;
18
+ twoFactorEnabled: boolean;
19
+ }
20
+ export interface LinkedAccount {
21
+ id: string;
22
+ providerId: string;
23
+ accountId: string;
24
+ }
25
+ export interface IAuthUserRepository {
26
+ getUser(userId: string): Promise<AuthUser | null>;
27
+ updateUser(userId: string, data: {
28
+ name?: string;
29
+ image?: string | null;
30
+ }): Promise<AuthUser>;
31
+ changePassword(userId: string, currentPassword: string, newPassword: string): Promise<boolean>;
32
+ listAccounts(userId: string): Promise<LinkedAccount[]>;
33
+ unlinkAccount(userId: string, providerId: string): Promise<boolean>;
34
+ }
35
+ export declare class BetterAuthUserRepository implements IAuthUserRepository {
36
+ private readonly pool;
37
+ constructor(pool: Pool);
38
+ getUser(userId: string): Promise<AuthUser | null>;
39
+ updateUser(userId: string, data: {
40
+ name?: string;
41
+ image?: string | null;
42
+ }): Promise<AuthUser>;
43
+ changePassword(userId: string, currentPassword: string, newPassword: string): Promise<boolean>;
44
+ listAccounts(userId: string): Promise<LinkedAccount[]>;
45
+ unlinkAccount(userId: string, providerId: string): Promise<boolean>;
46
+ }
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Auth user repository — read/write the better-auth PostgreSQL user and account tables.
3
+ *
4
+ * Uses raw pg queries because better-auth manages its own schema
5
+ * independently of Drizzle.
6
+ */
7
+ import { hashPassword, verifyPassword } from "better-auth/crypto";
8
+ /**
9
+ * Ensure the twoFactorEnabled column exists on the better-auth user table.
10
+ * Idempotent — safe to call on every startup alongside runAuthMigrations().
11
+ */
12
+ export async function initTwoFactorSchema(pool) {
13
+ // raw SQL: better-auth manages its own schema outside Drizzle
14
+ await pool.query(`ALTER TABLE "user" ADD COLUMN IF NOT EXISTS "twoFactorEnabled" BOOLEAN NOT NULL DEFAULT false`);
15
+ }
16
+ export class BetterAuthUserRepository {
17
+ pool;
18
+ constructor(pool) {
19
+ this.pool = pool;
20
+ }
21
+ async getUser(userId) {
22
+ // raw SQL: better-auth manages its own schema outside Drizzle
23
+ const { rows } = await this.pool.query(`SELECT id, name, email, image, "twoFactorEnabled" FROM "user" WHERE id = $1`, [userId]);
24
+ const row = rows[0];
25
+ if (row)
26
+ row.twoFactorEnabled = row.twoFactorEnabled ?? false;
27
+ return row ?? null;
28
+ }
29
+ async updateUser(userId, data) {
30
+ const fields = [];
31
+ const values = [];
32
+ let paramIndex = 1;
33
+ if (data.name !== undefined) {
34
+ fields.push(`name = $${paramIndex++}`);
35
+ values.push(data.name);
36
+ }
37
+ if (data.image !== undefined) {
38
+ fields.push(`image = $${paramIndex++}`);
39
+ values.push(data.image);
40
+ }
41
+ if (fields.length > 0) {
42
+ values.push(userId);
43
+ // raw SQL: better-auth manages its own schema outside Drizzle
44
+ await this.pool.query(`UPDATE "user" SET ${fields.join(", ")} WHERE id = $${paramIndex}`, values);
45
+ }
46
+ // raw SQL: better-auth manages its own schema outside Drizzle
47
+ const { rows } = await this.pool.query(`SELECT id, name, email, image, "twoFactorEnabled" FROM "user" WHERE id = $1`, [userId]);
48
+ if (rows.length === 0)
49
+ throw new Error(`User not found: ${userId}`);
50
+ const result = rows[0];
51
+ result.twoFactorEnabled = result.twoFactorEnabled ?? false;
52
+ return result;
53
+ }
54
+ async changePassword(userId, currentPassword, newPassword) {
55
+ // raw SQL: better-auth manages its own schema outside Drizzle
56
+ const { rows } = await this.pool.query(`SELECT password FROM account WHERE user_id = $1 AND provider_id = 'credential'`, [userId]);
57
+ const row = rows[0];
58
+ if (!row?.password)
59
+ return false;
60
+ const valid = await verifyPassword({ hash: row.password, password: currentPassword });
61
+ if (!valid)
62
+ return false;
63
+ const newHash = await hashPassword(newPassword);
64
+ // raw SQL: better-auth manages its own schema outside Drizzle
65
+ await this.pool.query(`UPDATE account SET password = $1 WHERE user_id = $2 AND provider_id = 'credential'`, [
66
+ newHash,
67
+ userId,
68
+ ]);
69
+ return true;
70
+ }
71
+ async listAccounts(userId) {
72
+ // raw SQL: better-auth manages its own schema outside Drizzle
73
+ const { rows } = await this.pool.query(`SELECT id, provider_id, account_id FROM account WHERE user_id = $1`, [
74
+ userId,
75
+ ]);
76
+ return rows.map((r) => ({
77
+ id: r.id,
78
+ providerId: r.provider_id,
79
+ accountId: r.account_id,
80
+ }));
81
+ }
82
+ async unlinkAccount(userId, providerId) {
83
+ // raw SQL: better-auth manages its own schema outside Drizzle
84
+ const { rowCount } = await this.pool.query(`DELETE FROM account WHERE user_id = $1 AND provider_id = $2`, [
85
+ userId,
86
+ providerId,
87
+ ]);
88
+ return (rowCount ?? 0) > 0;
89
+ }
90
+ }
@@ -0,0 +1,27 @@
1
+ import { Credit } from "../credits/credit.js";
2
+ export declare const creditColumn: {
3
+ (): import("drizzle-orm/pg-core").PgCustomColumnBuilder<{
4
+ name: "";
5
+ dataType: "custom";
6
+ columnType: "PgCustomColumn";
7
+ data: Credit;
8
+ driverParam: string;
9
+ enumValues: undefined;
10
+ }>;
11
+ <TConfig extends Record<string, any>>(fieldConfig?: TConfig | undefined): import("drizzle-orm/pg-core").PgCustomColumnBuilder<{
12
+ name: "";
13
+ dataType: "custom";
14
+ columnType: "PgCustomColumn";
15
+ data: Credit;
16
+ driverParam: string;
17
+ enumValues: undefined;
18
+ }>;
19
+ <TName extends string>(dbName: TName, fieldConfig?: unknown): import("drizzle-orm/pg-core").PgCustomColumnBuilder<{
20
+ name: TName;
21
+ dataType: "custom";
22
+ columnType: "PgCustomColumn";
23
+ data: Credit;
24
+ driverParam: string;
25
+ enumValues: undefined;
26
+ }>;
27
+ };