@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,203 @@
1
+ /**
2
+ * Tests for CreditLedger — including the allowNegative debit parameter (WOP-821).
3
+ */
4
+ import { afterAll, beforeAll, beforeEach, describe, expect, it } from "vitest";
5
+ import { createTestDb, truncateAllTables } from "../test/db.js";
6
+ import { Credit } from "./credit.js";
7
+ import { CreditLedger, InsufficientBalanceError } from "./credit-ledger.js";
8
+ // TOP OF FILE - shared across ALL describes
9
+ let pool;
10
+ let db;
11
+ beforeAll(async () => {
12
+ ({ db, pool } = await createTestDb());
13
+ });
14
+ afterAll(async () => {
15
+ await pool.close();
16
+ });
17
+ describe("CreditLedger core methods", () => {
18
+ let ledger;
19
+ beforeEach(async () => {
20
+ await truncateAllTables(pool);
21
+ ledger = new CreditLedger(db);
22
+ });
23
+ // --- credit() ---
24
+ describe("credit()", () => {
25
+ it("happy path: credits a tenant and returns correct transaction fields", async () => {
26
+ const txn = await ledger.credit("t1", Credit.fromCents(100), "purchase", "Initial deposit", "ref-001", "stripe", "user-abc");
27
+ expect(txn.tenantId).toBe("t1");
28
+ expect(txn.amount.toCents()).toBe(100);
29
+ expect(txn.balanceAfter.toCents()).toBe(100);
30
+ expect(txn.type).toBe("purchase");
31
+ expect(txn.description).toBe("Initial deposit");
32
+ expect(txn.referenceId).toBe("ref-001");
33
+ expect(txn.fundingSource).toBe("stripe");
34
+ expect(txn.attributedUserId).toBe("user-abc");
35
+ expect(txn.id).toEqual(expect.any(String));
36
+ expect(txn.createdAt).toEqual(expect.any(String));
37
+ });
38
+ it("multiple credits accumulate balance correctly", async () => {
39
+ await ledger.credit("t1", Credit.fromCents(100), "purchase");
40
+ await ledger.credit("t1", Credit.fromCents(50), "promo");
41
+ const bal = await ledger.balance("t1");
42
+ expect(bal.toCents()).toBe(150);
43
+ });
44
+ it("rejects zero amount", async () => {
45
+ await expect(ledger.credit("t1", Credit.fromCents(0), "purchase")).rejects.toThrow("amount must be positive for credits");
46
+ });
47
+ it("rejects negative amount", async () => {
48
+ await expect(ledger.credit("t1", Credit.fromRaw(-1), "purchase")).rejects.toThrow("amount must be positive for credits");
49
+ });
50
+ it("optional fields default to null", async () => {
51
+ const txn = await ledger.credit("t1", Credit.fromCents(10), "signup_grant");
52
+ expect(txn.description).toBeNull();
53
+ expect(txn.referenceId).toBeNull();
54
+ expect(txn.fundingSource).toBeNull();
55
+ expect(txn.attributedUserId).toBeNull();
56
+ });
57
+ });
58
+ // --- balance() ---
59
+ describe("balance()", () => {
60
+ it("returns Credit.ZERO for a tenant with no transactions", async () => {
61
+ const bal = await ledger.balance("nonexistent");
62
+ expect(bal.toCents()).toBe(0);
63
+ expect(bal.isZero()).toBe(true);
64
+ });
65
+ it("reflects credits and debits accurately", async () => {
66
+ await ledger.credit("t1", Credit.fromCents(200), "purchase");
67
+ await ledger.debit("t1", Credit.fromCents(50), "bot_runtime");
68
+ const bal = await ledger.balance("t1");
69
+ expect(bal.toCents()).toBe(150);
70
+ });
71
+ });
72
+ // --- history() ---
73
+ describe("history()", () => {
74
+ it("returns transactions in reverse chronological order (newest first)", async () => {
75
+ await ledger.credit("t1", Credit.fromCents(100), "purchase", "first");
76
+ await ledger.credit("t1", Credit.fromCents(200), "promo", "second");
77
+ await ledger.debit("t1", Credit.fromCents(50), "bot_runtime", "third");
78
+ const hist = await ledger.history("t1");
79
+ expect(hist).toHaveLength(3);
80
+ // newest first
81
+ expect(hist[0].description).toBe("third");
82
+ expect(hist[1].description).toBe("second");
83
+ expect(hist[2].description).toBe("first");
84
+ });
85
+ it("all CreditTransaction fields are populated", async () => {
86
+ await ledger.credit("t1", Credit.fromCents(100), "purchase", "desc", "ref-1", "stripe", "user-1");
87
+ const hist = await ledger.history("t1");
88
+ expect(hist).toHaveLength(1);
89
+ const txn = hist[0];
90
+ expect(txn.id).toEqual(expect.any(String));
91
+ expect(txn.tenantId).toBe("t1");
92
+ expect(txn.amount.toCents()).toBe(100);
93
+ expect(txn.balanceAfter.toCents()).toBe(100);
94
+ expect(txn.type).toBe("purchase");
95
+ expect(txn.description).toBe("desc");
96
+ expect(txn.referenceId).toBe("ref-1");
97
+ expect(txn.fundingSource).toBe("stripe");
98
+ expect(txn.attributedUserId).toBe("user-1");
99
+ expect(txn.createdAt).toEqual(expect.any(String));
100
+ });
101
+ it("respects limit and offset for pagination", async () => {
102
+ // Insert 5 transactions
103
+ for (let i = 1; i <= 5; i++) {
104
+ await ledger.credit("t1", Credit.fromCents(10 * i), "purchase", `txn-${i}`);
105
+ }
106
+ const page1 = await ledger.history("t1", { limit: 2, offset: 0 });
107
+ expect(page1).toHaveLength(2);
108
+ expect(page1[0].description).toBe("txn-5"); // newest first
109
+ expect(page1[1].description).toBe("txn-4");
110
+ const page2 = await ledger.history("t1", { limit: 2, offset: 2 });
111
+ expect(page2).toHaveLength(2);
112
+ expect(page2[0].description).toBe("txn-3");
113
+ expect(page2[1].description).toBe("txn-2");
114
+ });
115
+ it("filters by type when provided", async () => {
116
+ await ledger.credit("t1", Credit.fromCents(100), "purchase", "buy");
117
+ await ledger.credit("t1", Credit.fromCents(50), "promo", "free");
118
+ await ledger.debit("t1", Credit.fromCents(10), "bot_runtime", "usage");
119
+ const purchases = await ledger.history("t1", { type: "purchase" });
120
+ expect(purchases).toHaveLength(1);
121
+ expect(purchases[0].description).toBe("buy");
122
+ });
123
+ it("returns empty array for tenant with no transactions", async () => {
124
+ const hist = await ledger.history("nonexistent");
125
+ expect(hist).toEqual([]);
126
+ });
127
+ });
128
+ // --- hasReferenceId() ---
129
+ describe("hasReferenceId()", () => {
130
+ it("returns false for a reference ID that does not exist", async () => {
131
+ expect(await ledger.hasReferenceId("nonexistent-ref")).toBe(false);
132
+ });
133
+ it("returns true for a reference ID used in a credit", async () => {
134
+ await ledger.credit("t1", Credit.fromCents(100), "purchase", "desc", "ref-unique");
135
+ expect(await ledger.hasReferenceId("ref-unique")).toBe(true);
136
+ });
137
+ it("returns true for a reference ID used in a debit", async () => {
138
+ await ledger.credit("t1", Credit.fromCents(100), "purchase");
139
+ await ledger.debit("t1", Credit.fromCents(10), "bot_runtime", "desc", "debit-ref");
140
+ expect(await ledger.hasReferenceId("debit-ref")).toBe(true);
141
+ });
142
+ it("detects reference IDs across different tenants", async () => {
143
+ await ledger.credit("t1", Credit.fromCents(100), "purchase", "desc", "cross-tenant-ref");
144
+ // hasReferenceId is global, not tenant-scoped
145
+ expect(await ledger.hasReferenceId("cross-tenant-ref")).toBe(true);
146
+ });
147
+ });
148
+ // --- tenantsWithBalance() ---
149
+ describe("tenantsWithBalance()", () => {
150
+ it("returns empty array when no tenants exist", async () => {
151
+ const result = await ledger.tenantsWithBalance();
152
+ expect(result).toEqual([]);
153
+ });
154
+ it("returns only tenants with positive balance", async () => {
155
+ // t1: positive balance (100 cents)
156
+ await ledger.credit("t1", Credit.fromCents(100), "purchase");
157
+ // t2: zero balance (credit then debit same amount)
158
+ await ledger.credit("t2", Credit.fromCents(50), "purchase");
159
+ await ledger.debit("t2", Credit.fromCents(50), "bot_runtime");
160
+ // t3: negative balance (via allowNegative)
161
+ await ledger.credit("t3", Credit.fromCents(10), "purchase");
162
+ await ledger.debit("t3", Credit.fromCents(20), "bot_runtime", undefined, undefined, true);
163
+ // t4: positive balance (200 cents)
164
+ await ledger.credit("t4", Credit.fromCents(200), "signup_grant");
165
+ const result = await ledger.tenantsWithBalance();
166
+ const tenantIds = result.map((r) => r.tenantId).sort();
167
+ expect(tenantIds).toEqual(["t1", "t4"]);
168
+ const t1 = result.find((r) => r.tenantId === "t1");
169
+ expect(t1?.balance.toCents()).toBe(100);
170
+ const t4 = result.find((r) => r.tenantId === "t4");
171
+ expect(t4?.balance.toCents()).toBe(200);
172
+ });
173
+ it("excludes tenants with exactly zero balance", async () => {
174
+ await ledger.credit("t1", Credit.fromCents(100), "purchase");
175
+ await ledger.debit("t1", Credit.fromCents(100), "bot_runtime");
176
+ const result = await ledger.tenantsWithBalance();
177
+ expect(result).toEqual([]);
178
+ });
179
+ });
180
+ });
181
+ describe("CreditLedger.debit with allowNegative", () => {
182
+ let ledger;
183
+ beforeEach(async () => {
184
+ await truncateAllTables(pool);
185
+ ledger = new CreditLedger(db);
186
+ });
187
+ it("debit with allowNegative=false (default) throws InsufficientBalanceError when balance insufficient", async () => {
188
+ await ledger.credit("t1", Credit.fromCents(5), "purchase", "setup");
189
+ await expect(ledger.debit("t1", Credit.fromCents(10), "adapter_usage", "test")).rejects.toThrow(InsufficientBalanceError);
190
+ });
191
+ it("debit with allowNegative=true allows negative balance", async () => {
192
+ await ledger.credit("t1", Credit.fromCents(5), "purchase", "setup");
193
+ const txn = await ledger.debit("t1", Credit.fromCents(10), "adapter_usage", "test", undefined, true);
194
+ expect(txn).not.toBeNull();
195
+ expect((await ledger.balance("t1")).toCents()).toBe(-5);
196
+ });
197
+ it("debit with allowNegative=true records correct transaction with negative amount and negative balanceAfter", async () => {
198
+ await ledger.credit("t1", Credit.fromCents(5), "purchase", "setup");
199
+ const txn = await ledger.debit("t1", Credit.fromCents(10), "adapter_usage", "test", undefined, true);
200
+ expect(txn.amount.toCents()).toBe(-10);
201
+ expect(txn.balanceAfter.toCents()).toBe(-5);
202
+ });
203
+ });
@@ -0,0 +1,17 @@
1
+ import type { PlatformDb } from "../db/index.js";
2
+ import { Credit } from "./credit.js";
3
+ export interface ICreditTransactionRepository {
4
+ /** Check if any transaction exists with a referenceId matching a LIKE pattern. */
5
+ existsByReferenceIdLike(pattern: string): Promise<boolean>;
6
+ /** Sum amount for 'purchase' transactions within [startTs, endTs). */
7
+ sumPurchasesForPeriod(startTs: string, endTs: string): Promise<Credit>;
8
+ /** Get distinct tenantIds that had a 'purchase' transaction within [startTs, endTs). */
9
+ getActiveTenantIdsInWindow(startTs: string, endTs: string): Promise<string[]>;
10
+ }
11
+ export declare class DrizzleCreditTransactionRepository implements ICreditTransactionRepository {
12
+ private readonly db;
13
+ constructor(db: PlatformDb);
14
+ existsByReferenceIdLike(pattern: string): Promise<boolean>;
15
+ sumPurchasesForPeriod(startTs: string, endTs: string): Promise<Credit>;
16
+ getActiveTenantIdsInWindow(startTs: string, endTs: string): Promise<string[]>;
17
+ }
@@ -0,0 +1,35 @@
1
+ import { and, eq, gte, lt, sql } from "drizzle-orm";
2
+ import { creditTransactions } from "../db/schema/credits.js";
3
+ import { Credit } from "./credit.js";
4
+ export class DrizzleCreditTransactionRepository {
5
+ db;
6
+ constructor(db) {
7
+ this.db = db;
8
+ }
9
+ async existsByReferenceIdLike(pattern) {
10
+ const row = (await this.db
11
+ .select({ id: creditTransactions.id })
12
+ .from(creditTransactions)
13
+ .where(sql `${creditTransactions.referenceId} LIKE ${pattern}`)
14
+ .limit(1))[0];
15
+ return row != null;
16
+ }
17
+ async sumPurchasesForPeriod(startTs, endTs) {
18
+ const row = (await this.db
19
+ .select({
20
+ total: sql `COALESCE(SUM(${creditTransactions.amount}), 0)`,
21
+ })
22
+ .from(creditTransactions)
23
+ .where(and(eq(creditTransactions.type, "purchase"), gte(creditTransactions.createdAt, startTs), lt(creditTransactions.createdAt, endTs))))[0];
24
+ return Credit.fromRaw(Math.round(row?.total ?? 0));
25
+ }
26
+ async getActiveTenantIdsInWindow(startTs, endTs) {
27
+ const rows = await this.db
28
+ .selectDistinct({
29
+ tenantId: creditTransactions.tenantId,
30
+ })
31
+ .from(creditTransactions)
32
+ .where(and(eq(creditTransactions.type, "purchase"), gte(creditTransactions.createdAt, startTs), lt(creditTransactions.createdAt, endTs)));
33
+ return rows.map((r) => r.tenantId);
34
+ }
35
+ }
@@ -0,0 +1,232 @@
1
+ import crypto from "node:crypto";
2
+ import { afterAll, beforeAll, beforeEach, describe, expect, it } from "vitest";
3
+ import { creditTransactions } from "../db/schema/credits.js";
4
+ import { beginTestTransaction, createTestDb, endTestTransaction, rollbackTestTransaction } from "../test/db.js";
5
+ import { Credit } from "./credit.js";
6
+ import { DrizzleCreditTransactionRepository } from "./credit-transaction-repository.js";
7
+ let pool;
8
+ let db;
9
+ beforeAll(async () => {
10
+ ({ db, pool } = await createTestDb());
11
+ await beginTestTransaction(pool);
12
+ });
13
+ afterAll(async () => {
14
+ await endTestTransaction(pool);
15
+ await pool.close();
16
+ });
17
+ /** Seed a credit_transactions row directly. */
18
+ async function seedTx(opts) {
19
+ await db.insert(creditTransactions).values({
20
+ id: crypto.randomUUID(),
21
+ tenantId: opts.tenantId,
22
+ amount: opts.amount,
23
+ balanceAfter: opts.balanceAfter ?? opts.amount,
24
+ type: opts.type,
25
+ description: "test",
26
+ referenceId: opts.referenceId ?? null,
27
+ createdAt: opts.createdAt ?? new Date().toISOString(),
28
+ });
29
+ }
30
+ describe("DrizzleCreditTransactionRepository", () => {
31
+ let repo;
32
+ beforeEach(async () => {
33
+ await rollbackTestTransaction(pool);
34
+ repo = new DrizzleCreditTransactionRepository(db);
35
+ });
36
+ describe("existsByReferenceIdLike()", () => {
37
+ it("returns false when no transactions exist", async () => {
38
+ const result = await repo.existsByReferenceIdLike("div-%");
39
+ expect(result).toBe(false);
40
+ });
41
+ it("returns true when a matching referenceId exists", async () => {
42
+ await seedTx({
43
+ tenantId: "t1",
44
+ amount: Credit.fromCents(100),
45
+ type: "purchase",
46
+ referenceId: "div-2026-01-01",
47
+ });
48
+ const result = await repo.existsByReferenceIdLike("div-%");
49
+ expect(result).toBe(true);
50
+ });
51
+ it("returns false when no referenceId matches the pattern", async () => {
52
+ await seedTx({
53
+ tenantId: "t1",
54
+ amount: Credit.fromCents(100),
55
+ type: "purchase",
56
+ referenceId: "purchase-abc",
57
+ });
58
+ const result = await repo.existsByReferenceIdLike("div-%");
59
+ expect(result).toBe(false);
60
+ });
61
+ it("matches partial patterns with wildcards", async () => {
62
+ await seedTx({
63
+ tenantId: "t1",
64
+ amount: Credit.fromCents(50),
65
+ type: "community_dividend",
66
+ referenceId: "div-2026-02-15-t1",
67
+ });
68
+ expect(await repo.existsByReferenceIdLike("div-2026-02-%")).toBe(true);
69
+ expect(await repo.existsByReferenceIdLike("div-2026-03-%")).toBe(false);
70
+ });
71
+ });
72
+ describe("sumPurchasesForPeriod()", () => {
73
+ it("returns Credit.ZERO when no transactions exist", async () => {
74
+ const sum = await repo.sumPurchasesForPeriod("2026-01-01T00:00:00Z", "2026-02-01T00:00:00Z");
75
+ expect(sum.toRaw()).toBe(0);
76
+ });
77
+ it("sums only purchase-type transactions", async () => {
78
+ await seedTx({
79
+ tenantId: "t1",
80
+ amount: Credit.fromCents(100),
81
+ type: "purchase",
82
+ createdAt: "2026-01-15T12:00:00Z",
83
+ });
84
+ await seedTx({
85
+ tenantId: "t1",
86
+ amount: Credit.fromCents(200),
87
+ type: "signup_grant",
88
+ createdAt: "2026-01-15T12:00:00Z",
89
+ });
90
+ const sum = await repo.sumPurchasesForPeriod("2026-01-01T00:00:00Z", "2026-02-01T00:00:00Z");
91
+ expect(sum.toRaw()).toBe(Credit.fromCents(100).toRaw());
92
+ });
93
+ it("respects half-open interval [start, end)", async () => {
94
+ // Exactly at start — included
95
+ await seedTx({
96
+ tenantId: "t1",
97
+ amount: Credit.fromCents(10),
98
+ type: "purchase",
99
+ createdAt: "2026-01-01T00:00:00Z",
100
+ });
101
+ // Inside window
102
+ await seedTx({
103
+ tenantId: "t1",
104
+ amount: Credit.fromCents(20),
105
+ type: "purchase",
106
+ createdAt: "2026-01-15T00:00:00Z",
107
+ });
108
+ // Exactly at end — excluded
109
+ await seedTx({
110
+ tenantId: "t1",
111
+ amount: Credit.fromCents(40),
112
+ type: "purchase",
113
+ createdAt: "2026-02-01T00:00:00Z",
114
+ });
115
+ const sum = await repo.sumPurchasesForPeriod("2026-01-01T00:00:00Z", "2026-02-01T00:00:00Z");
116
+ expect(sum.toRaw()).toBe(Credit.fromCents(30).toRaw()); // 10 + 20, not 40
117
+ });
118
+ it("sums across all tenants (not tenant-scoped)", async () => {
119
+ await seedTx({
120
+ tenantId: "t1",
121
+ amount: Credit.fromCents(50),
122
+ type: "purchase",
123
+ createdAt: "2026-01-10T00:00:00Z",
124
+ });
125
+ await seedTx({
126
+ tenantId: "t2",
127
+ amount: Credit.fromCents(75),
128
+ type: "purchase",
129
+ createdAt: "2026-01-10T00:00:00Z",
130
+ });
131
+ const sum = await repo.sumPurchasesForPeriod("2026-01-01T00:00:00Z", "2026-02-01T00:00:00Z");
132
+ expect(sum.toRaw()).toBe(Credit.fromCents(125).toRaw()); // 50 + 75
133
+ });
134
+ });
135
+ describe("getActiveTenantIdsInWindow()", () => {
136
+ it("returns empty array when no transactions exist", async () => {
137
+ const ids = await repo.getActiveTenantIdsInWindow("2026-01-01T00:00:00Z", "2026-02-01T00:00:00Z");
138
+ expect(ids).toEqual([]);
139
+ });
140
+ it("returns distinct tenantIds with purchase transactions in window", async () => {
141
+ await seedTx({
142
+ tenantId: "t1",
143
+ amount: Credit.fromCents(10),
144
+ type: "purchase",
145
+ createdAt: "2026-01-10T00:00:00Z",
146
+ });
147
+ // t1 again — should not duplicate
148
+ await seedTx({
149
+ tenantId: "t1",
150
+ amount: Credit.fromCents(20),
151
+ type: "purchase",
152
+ createdAt: "2026-01-11T00:00:00Z",
153
+ });
154
+ await seedTx({
155
+ tenantId: "t2",
156
+ amount: Credit.fromCents(30),
157
+ type: "purchase",
158
+ createdAt: "2026-01-12T00:00:00Z",
159
+ });
160
+ const ids = await repo.getActiveTenantIdsInWindow("2026-01-01T00:00:00Z", "2026-02-01T00:00:00Z");
161
+ expect(ids.sort()).toEqual(["t1", "t2"]);
162
+ });
163
+ it("excludes non-purchase transaction types", async () => {
164
+ await seedTx({
165
+ tenantId: "t1",
166
+ amount: Credit.fromCents(100),
167
+ type: "signup_grant",
168
+ createdAt: "2026-01-10T00:00:00Z",
169
+ });
170
+ const ids = await repo.getActiveTenantIdsInWindow("2026-01-01T00:00:00Z", "2026-02-01T00:00:00Z");
171
+ expect(ids).toEqual([]);
172
+ });
173
+ it("respects half-open interval [start, end)", async () => {
174
+ // Before window
175
+ await seedTx({
176
+ tenantId: "t-before",
177
+ amount: Credit.fromCents(10),
178
+ type: "purchase",
179
+ createdAt: "2025-12-31T23:59:59Z",
180
+ });
181
+ // At start — included
182
+ await seedTx({
183
+ tenantId: "t-start",
184
+ amount: Credit.fromCents(10),
185
+ type: "purchase",
186
+ createdAt: "2026-01-01T00:00:00Z",
187
+ });
188
+ // At end — excluded
189
+ await seedTx({
190
+ tenantId: "t-end",
191
+ amount: Credit.fromCents(10),
192
+ type: "purchase",
193
+ createdAt: "2026-02-01T00:00:00Z",
194
+ });
195
+ const ids = await repo.getActiveTenantIdsInWindow("2026-01-01T00:00:00Z", "2026-02-01T00:00:00Z");
196
+ expect(ids).toEqual(["t-start"]);
197
+ });
198
+ });
199
+ describe("referenceId uniqueness", () => {
200
+ it("rejects duplicate referenceId (database unique constraint)", async () => {
201
+ await seedTx({
202
+ tenantId: "t1",
203
+ amount: Credit.fromCents(100),
204
+ type: "purchase",
205
+ referenceId: "unique-ref-1",
206
+ });
207
+ await expect(seedTx({
208
+ tenantId: "t1",
209
+ amount: Credit.fromCents(200),
210
+ type: "purchase",
211
+ referenceId: "unique-ref-1",
212
+ })).rejects.toThrow(); // PG unique constraint violation
213
+ });
214
+ it("allows null referenceId on multiple rows", async () => {
215
+ await seedTx({
216
+ tenantId: "t1",
217
+ amount: Credit.fromCents(100),
218
+ type: "purchase",
219
+ referenceId: undefined, // null
220
+ });
221
+ await seedTx({
222
+ tenantId: "t1",
223
+ amount: Credit.fromCents(200),
224
+ type: "purchase",
225
+ referenceId: undefined, // null
226
+ });
227
+ // Both inserted — no constraint violation for nulls
228
+ const result = await repo.existsByReferenceIdLike("%");
229
+ expect(result).toBe(false); // LIKE '%' won't match null referenceIds
230
+ });
231
+ });
232
+ });
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Credit value object with sub-cent precision.
3
+ *
4
+ * SCALE = 1,000,000,000 raw units per dollar (nano-dollars).
5
+ * All arithmetic operates on integer raw units -- no floating point
6
+ * in arithmetic paths. Math.round() is used only at input boundaries
7
+ * (fromDollars, fromCents, multiply).
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * const charge = Credit.fromDollars(0.001); // 1,000,000 raw units
12
+ * const balance = Credit.fromCents(500); // 5,000,000,000 raw units
13
+ * const remaining = balance.subtract(charge);
14
+ * ```
15
+ */
16
+ export declare class Credit {
17
+ private readonly raw;
18
+ static readonly SCALE = 1000000000;
19
+ private constructor();
20
+ /** Zero credit (static readonly instance to avoid allocation per access). */
21
+ static readonly ZERO: Credit;
22
+ /** Create from dollar amount. Rounds to nearest raw unit. */
23
+ static fromDollars(dollars: number): Credit;
24
+ /** Create from cent amount. Rounds to nearest raw unit. */
25
+ static fromCents(cents: number): Credit;
26
+ private static readonly WARN_10K;
27
+ private static readonly WARN_100K;
28
+ private static readonly WARN_1M;
29
+ /** Create from raw integer units. Throws TypeError if not integer. */
30
+ static fromRaw(raw: number): Credit;
31
+ /** Zero credit (factory method alias for Credit.ZERO). */
32
+ static zero(): Credit;
33
+ /** Convert to dollars (floating point, for display only). */
34
+ toDollars(): number;
35
+ /** Convert to cents (floating point, for display only). */
36
+ toCents(): number;
37
+ /**
38
+ * Convert to cents, rounded to nearest integer (Math.round).
39
+ * Use for display values and API responses where exact cent is needed.
40
+ */
41
+ toCentsRounded(): number;
42
+ /**
43
+ * Convert to cents, floored to integer (Math.floor).
44
+ * Use when sending amounts to Stripe/Payram — floor avoids charging
45
+ * more than the displayed amount.
46
+ */
47
+ toCentsFloor(): number;
48
+ /** Display as dollar amount with two decimal places. */
49
+ toDisplayString(): string;
50
+ /** Raw integer units (what gets stored in the database). */
51
+ toRaw(): number;
52
+ /** Add another Credit, returning a new Credit. */
53
+ add(other: Credit): Credit;
54
+ /** Subtract another Credit, returning a new Credit (may be negative). */
55
+ subtract(other: Credit): Credit;
56
+ /** Multiply by a factor, rounding to nearest raw unit. */
57
+ multiply(factor: number): Credit;
58
+ /** True if this credit is negative. */
59
+ isNegative(): boolean;
60
+ /** True if this credit is exactly zero. */
61
+ isZero(): boolean;
62
+ /** True if this credit is greater than other. */
63
+ greaterThan(other: Credit): boolean;
64
+ /** True if this credit is less than other. */
65
+ lessThan(other: Credit): boolean;
66
+ /** True if this credit equals other. */
67
+ equals(other: Credit): boolean;
68
+ /** True if this credit is greater than or equal to other. */
69
+ greaterThanOrEqual(other: Credit): boolean;
70
+ /** True if this credit is less than or equal to other. */
71
+ lessThanOrEqual(other: Credit): boolean;
72
+ toString(): string;
73
+ /** Serialize to raw integer for JSON (API responses, database). */
74
+ toJSON(): number;
75
+ }