@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,67 @@
1
+ import { afterAll, beforeAll, beforeEach, describe, expect, it } from "vitest";
2
+ import { createTestDb, truncateAllTables } from "../test/db.js";
3
+ import { Credit } from "./credit.js";
4
+ import { runCreditExpiryCron } from "./credit-expiry-cron.js";
5
+ import { DrizzleCreditLedger } from "./credit-ledger.js";
6
+ describe("runCreditExpiryCron", () => {
7
+ let pool;
8
+ let ledger;
9
+ beforeAll(async () => {
10
+ const { db, pool: p } = await createTestDb();
11
+ pool = p;
12
+ ledger = new DrizzleCreditLedger(db);
13
+ });
14
+ afterAll(async () => {
15
+ await pool.close();
16
+ });
17
+ beforeEach(async () => {
18
+ await truncateAllTables(pool);
19
+ });
20
+ // All tests pass an explicit `now` parameter — hardcoded dates are time-independent
21
+ // because runCreditExpiryCron never reads the system clock.
22
+ it("returns empty result when no expired credits exist", async () => {
23
+ const result = await runCreditExpiryCron({ ledger, now: "2026-01-15T00:00:00Z" });
24
+ expect(result.processed).toBe(0);
25
+ expect(result.expired).toEqual([]);
26
+ expect(result.errors).toEqual([]);
27
+ });
28
+ it("debits expired promotional credit grant", async () => {
29
+ await ledger.credit("tenant-1", Credit.fromCents(500), "promo", "New user bonus", "promo:tenant-1", undefined, undefined, "2026-01-10T00:00:00Z");
30
+ const result = await runCreditExpiryCron({ ledger, now: "2026-01-15T00:00:00Z" });
31
+ expect(result.processed).toBe(1);
32
+ expect(result.expired).toContain("tenant-1");
33
+ const balance = await ledger.balance("tenant-1");
34
+ expect(balance.toCents()).toBe(0);
35
+ });
36
+ it("does not debit non-expired credits", async () => {
37
+ await ledger.credit("tenant-1", Credit.fromCents(500), "promo", "Future bonus", "promo:tenant-1-future", undefined, undefined, "2026-02-01T00:00:00Z");
38
+ const result = await runCreditExpiryCron({ ledger, now: "2026-01-15T00:00:00Z" });
39
+ expect(result.processed).toBe(0);
40
+ const balance = await ledger.balance("tenant-1");
41
+ expect(balance.toCents()).toBe(500);
42
+ });
43
+ it("does not debit credits without expires_at", async () => {
44
+ await ledger.credit("tenant-1", Credit.fromCents(500), "purchase", "Top-up");
45
+ const result = await runCreditExpiryCron({ ledger, now: "2026-01-15T00:00:00Z" });
46
+ expect(result.processed).toBe(0);
47
+ const balance = await ledger.balance("tenant-1");
48
+ expect(balance.toCents()).toBe(500);
49
+ });
50
+ it("only debits up to available balance when partially consumed", async () => {
51
+ await ledger.credit("tenant-1", Credit.fromCents(500), "promo", "Promo", "promo:partial", undefined, undefined, "2026-01-10T00:00:00Z");
52
+ await ledger.debit("tenant-1", Credit.fromCents(300), "bot_runtime", "Runtime");
53
+ const result = await runCreditExpiryCron({ ledger, now: "2026-01-15T00:00:00Z" });
54
+ expect(result.processed).toBe(1);
55
+ const balance = await ledger.balance("tenant-1");
56
+ expect(balance.toCents()).toBe(0);
57
+ });
58
+ it("is idempotent -- does not double-debit on second run", async () => {
59
+ await ledger.credit("tenant-1", Credit.fromCents(500), "promo", "Promo", "promo:idemp", undefined, undefined, "2026-01-10T00:00:00Z");
60
+ await runCreditExpiryCron({ ledger, now: "2026-01-15T00:00:00Z" });
61
+ const balanceAfterFirst = await ledger.balance("tenant-1");
62
+ const result2 = await runCreditExpiryCron({ ledger, now: "2026-01-15T00:00:00Z" });
63
+ expect(result2.processed).toBe(0);
64
+ const balanceAfterSecond = await ledger.balance("tenant-1");
65
+ expect(balanceAfterSecond.toCents()).toBe(balanceAfterFirst.toCents());
66
+ });
67
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,40 @@
1
+ import { afterAll, beforeAll, beforeEach, describe, expect, it } from "vitest";
2
+ import { beginTestTransaction, createTestDb, endTestTransaction, rollbackTestTransaction } from "../test/db.js";
3
+ import { Credit } from "./credit.js";
4
+ import { CreditLedger, InsufficientBalanceError } from "./credit-ledger.js";
5
+ let pool;
6
+ let db;
7
+ beforeAll(async () => {
8
+ ({ db, pool } = await createTestDb());
9
+ await beginTestTransaction(pool);
10
+ });
11
+ afterAll(async () => {
12
+ await endTestTransaction(pool);
13
+ await pool.close();
14
+ });
15
+ describe("CreditLedger concurrent debit safety", () => {
16
+ let ledger;
17
+ beforeEach(async () => {
18
+ await rollbackTestTransaction(pool);
19
+ ledger = new CreditLedger(db);
20
+ });
21
+ it("concurrent debits do not overdraw — at least one should fail with InsufficientBalanceError", async () => {
22
+ // Fund with exactly 100 cents
23
+ await ledger.credit("t1", Credit.fromCents(100), "purchase");
24
+ // Fire two 100-cent debits concurrently — only one can succeed
25
+ const results = await Promise.allSettled([
26
+ ledger.debit("t1", Credit.fromCents(100), "bot_runtime", "debit-1"),
27
+ ledger.debit("t1", Credit.fromCents(100), "bot_runtime", "debit-2"),
28
+ ]);
29
+ const fulfilled = results.filter((r) => r.status === "fulfilled");
30
+ const rejected = results.filter((r) => r.status === "rejected");
31
+ // Exactly one succeeds, one fails (PGlite serializes transactions so this is deterministic)
32
+ expect(fulfilled).toHaveLength(1);
33
+ expect(rejected).toHaveLength(1);
34
+ const err = rejected[0].reason;
35
+ expect(err).toBeInstanceOf(InsufficientBalanceError);
36
+ // Final balance must be exactly 0, not negative
37
+ const bal = await ledger.balance("t1");
38
+ expect(bal.toCents()).toBe(0);
39
+ });
40
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,33 @@
1
+ import { afterAll, beforeAll, beforeEach, bench, describe } from "vitest";
2
+ import { createTestDb, truncateAllTables } from "../test/db.js";
3
+ import { Credit } from "./credit.js";
4
+ import { CreditLedger } from "./credit-ledger.js";
5
+ let db;
6
+ let pool;
7
+ beforeAll(async () => {
8
+ ({ db, pool } = await createTestDb());
9
+ });
10
+ afterAll(async () => {
11
+ await pool.close();
12
+ });
13
+ describe("CreditLedger throughput", () => {
14
+ let ledger;
15
+ beforeEach(async () => {
16
+ await truncateAllTables(pool);
17
+ ledger = new CreditLedger(db);
18
+ });
19
+ let creditIdx = 0;
20
+ let debitIdx = 0;
21
+ bench("credit operation", async () => {
22
+ const tenant = `tenant-${creditIdx++ % 100}`;
23
+ await ledger.credit(tenant, Credit.fromCents(100), "purchase", "bench");
24
+ }, { iterations: 1_000 });
25
+ bench("debit operation", async () => {
26
+ const tenant = `tenant-${debitIdx++ % 100}`;
27
+ await ledger.debit(tenant, Credit.fromCents(1), "adapter_usage", "bench");
28
+ }, { iterations: 1_000 });
29
+ bench("balance query", async () => {
30
+ const tenant = `tenant-${debitIdx++ % 100}`;
31
+ await ledger.balance(tenant);
32
+ }, { iterations: 5_000 });
33
+ });
@@ -0,0 +1,130 @@
1
+ /**
2
+ * NAMING CONVENTION — cents vs credits (WOP-1058)
3
+ *
4
+ * - `_cents` suffix = value denominated in USD cents.
5
+ * Used for: Stripe amounts, PayRam amounts, ledger balances, ledger transactions.
6
+ * The platform credit unit IS the USD cent (1 credit = 1 cent = $0.01).
7
+ *
8
+ * - `_credits` suffix = platform credit count (used in DB columns and raw storage).
9
+ * Semantically identical to cents but signals "this is a platform concept stored
10
+ * as a Credit.toRaw() nanodollar value in the database."
11
+ *
12
+ * - NEVER rename a Stripe/PayRam-facing `_cents` field to `_credits`.
13
+ * Stripe's `amount` parameter and `amount_total` response are always USD cents.
14
+ * PayRam's `amountUsdCents` is always USD cents.
15
+ *
16
+ * - When adding new fields: if it touches a payment processor API, use `_cents`.
17
+ * If it's a DB column storing Credit.toRaw() values, use `_credits`.
18
+ *
19
+ * The Credit class bridges the two:
20
+ * Credit.fromCents(cents) — converts USD cents → Credit (nanodollar raw units)
21
+ * credit.toCents() — converts Credit → USD cents (for display / API responses)
22
+ */
23
+ import type { PlatformDb } from "../db/index.js";
24
+ import { Credit } from "./credit.js";
25
+ /** Transaction types that add credits */
26
+ export type CreditType = "signup_grant" | "admin_grant" | "purchase" | "bounty" | "referral" | "promo" | "community_dividend" | "affiliate_bonus" | "affiliate_match" | "correction";
27
+ /** Transaction types that remove credits */
28
+ export type DebitType = "bot_runtime" | "adapter_usage" | "addon" | "refund" | "correction" | "resource_upgrade" | "storage_upgrade" | "onboarding_llm" | "credit_expiry";
29
+ export type TransactionType = CreditType | DebitType;
30
+ export interface CreditTransaction {
31
+ id: string;
32
+ tenantId: string;
33
+ amount: Credit;
34
+ balanceAfter: Credit;
35
+ type: string;
36
+ description: string | null;
37
+ referenceId: string | null;
38
+ fundingSource: string | null;
39
+ attributedUserId: string | null;
40
+ createdAt: string;
41
+ expiresAt: string | null;
42
+ }
43
+ export interface HistoryOptions {
44
+ limit?: number;
45
+ offset?: number;
46
+ type?: string;
47
+ }
48
+ export interface MemberUsageSummary {
49
+ userId: string;
50
+ totalDebit: Credit;
51
+ transactionCount: number;
52
+ }
53
+ /** Insufficient balance error — thrown when a debit would make balance negative. */
54
+ export declare class InsufficientBalanceError extends Error {
55
+ currentBalance: Credit;
56
+ requestedAmount: Credit;
57
+ constructor(currentBalance: Credit, requestedAmount: Credit);
58
+ }
59
+ export interface ICreditLedger {
60
+ credit(tenantId: string, amount: Credit, type: CreditType, description?: string, referenceId?: string, fundingSource?: string, attributedUserId?: string, expiresAt?: string): Promise<CreditTransaction>;
61
+ expiredCredits(now: string): Promise<Array<{
62
+ id: string;
63
+ tenantId: string;
64
+ amount: Credit;
65
+ }>>;
66
+ debit(tenantId: string, amount: Credit, type: DebitType, description?: string, referenceId?: string, allowNegative?: boolean, attributedUserId?: string): Promise<CreditTransaction>;
67
+ balance(tenantId: string): Promise<Credit>;
68
+ hasReferenceId(referenceId: string): Promise<boolean>;
69
+ history(tenantId: string, opts?: HistoryOptions): Promise<CreditTransaction[]>;
70
+ tenantsWithBalance(): Promise<Array<{
71
+ tenantId: string;
72
+ balance: Credit;
73
+ }>>;
74
+ memberUsage(tenantId: string): Promise<MemberUsageSummary[]>;
75
+ lifetimeSpend(tenantId: string): Promise<Credit>;
76
+ lifetimeSpendBatch(tenantIds: string[]): Promise<Map<string, Credit>>;
77
+ }
78
+ /**
79
+ * Credit ledger — the single source of truth for credit balances.
80
+ *
81
+ * All mutations go through Drizzle transactions to ensure the
82
+ * creditBalances row is always consistent with the sum of creditTransactions.
83
+ * Zero raw SQL in application code.
84
+ */
85
+ export declare class DrizzleCreditLedger implements ICreditLedger {
86
+ private readonly db;
87
+ constructor(db: PlatformDb);
88
+ /**
89
+ * Read balance with FOR UPDATE row lock. Ensures the row exists first
90
+ * (upsert with ON CONFLICT DO NOTHING), then locks it for the duration
91
+ * of the enclosing transaction.
92
+ */
93
+ private lockedBalance;
94
+ /**
95
+ * Add credits to a tenant's balance.
96
+ * @returns The created transaction record.
97
+ */
98
+ credit(tenantId: string, amount: Credit, type: CreditType, description?: string, referenceId?: string, fundingSource?: string, attributedUserId?: string, expiresAt?: string): Promise<CreditTransaction>;
99
+ /**
100
+ * Deduct credits from a tenant's balance.
101
+ * Throws InsufficientBalanceError if balance would go negative (unless allowNegative is true).
102
+ * @param allowNegative - If true, allow balance to go negative (for grace buffer debits). Default: false.
103
+ * @returns The created transaction record.
104
+ */
105
+ debit(tenantId: string, amount: Credit, type: DebitType, description?: string, referenceId?: string, allowNegative?: boolean, attributedUserId?: string): Promise<CreditTransaction>;
106
+ /** Get current balance for a tenant. Returns Credit.ZERO if tenant has no balance row. */
107
+ balance(tenantId: string): Promise<Credit>;
108
+ /** Check if a reference ID has already been used (for idempotency). */
109
+ hasReferenceId(referenceId: string): Promise<boolean>;
110
+ /** Get transaction history for a tenant with optional filtering and pagination. */
111
+ history(tenantId: string, opts?: HistoryOptions): Promise<CreditTransaction[]>;
112
+ /** Aggregate debit totals per attributed user for a tenant. */
113
+ memberUsage(tenantId: string): Promise<MemberUsageSummary[]>;
114
+ /** Sum of all debit transactions (absolute value) for a tenant. */
115
+ lifetimeSpend(tenantId: string): Promise<Credit>;
116
+ /** Batch version of lifetimeSpend — single query for multiple tenants. */
117
+ lifetimeSpendBatch(tenantIds: string[]): Promise<Map<string, Credit>>;
118
+ /** Return expired credit grant transactions not yet processed by the expiry cron. */
119
+ expiredCredits(now: string): Promise<Array<{
120
+ id: string;
121
+ tenantId: string;
122
+ amount: Credit;
123
+ }>>;
124
+ /** List all tenants with positive balance (for cron deduction). */
125
+ tenantsWithBalance(): Promise<Array<{
126
+ tenantId: string;
127
+ balance: Credit;
128
+ }>>;
129
+ }
130
+ export { DrizzleCreditLedger as CreditLedger };
@@ -0,0 +1,293 @@
1
+ /**
2
+ * NAMING CONVENTION — cents vs credits (WOP-1058)
3
+ *
4
+ * - `_cents` suffix = value denominated in USD cents.
5
+ * Used for: Stripe amounts, PayRam amounts, ledger balances, ledger transactions.
6
+ * The platform credit unit IS the USD cent (1 credit = 1 cent = $0.01).
7
+ *
8
+ * - `_credits` suffix = platform credit count (used in DB columns and raw storage).
9
+ * Semantically identical to cents but signals "this is a platform concept stored
10
+ * as a Credit.toRaw() nanodollar value in the database."
11
+ *
12
+ * - NEVER rename a Stripe/PayRam-facing `_cents` field to `_credits`.
13
+ * Stripe's `amount` parameter and `amount_total` response are always USD cents.
14
+ * PayRam's `amountUsdCents` is always USD cents.
15
+ *
16
+ * - When adding new fields: if it touches a payment processor API, use `_cents`.
17
+ * If it's a DB column storing Credit.toRaw() values, use `_credits`.
18
+ *
19
+ * The Credit class bridges the two:
20
+ * Credit.fromCents(cents) — converts USD cents → Credit (nanodollar raw units)
21
+ * credit.toCents() — converts Credit → USD cents (for display / API responses)
22
+ */
23
+ import crypto from "node:crypto";
24
+ import { and, desc, eq, inArray, isNotNull, sql } from "drizzle-orm";
25
+ import { creditBalances, creditTransactions } from "../db/schema/credits.js";
26
+ import { Credit } from "./credit.js";
27
+ /** Insufficient balance error — thrown when a debit would make balance negative. */
28
+ export class InsufficientBalanceError extends Error {
29
+ currentBalance;
30
+ requestedAmount;
31
+ constructor(currentBalance, requestedAmount) {
32
+ super(`Insufficient balance: current ${currentBalance.toDisplayString()}, requested debit ${requestedAmount.toDisplayString()}`);
33
+ this.name = "InsufficientBalanceError";
34
+ this.currentBalance = currentBalance;
35
+ this.requestedAmount = requestedAmount;
36
+ }
37
+ }
38
+ /**
39
+ * Credit ledger — the single source of truth for credit balances.
40
+ *
41
+ * All mutations go through Drizzle transactions to ensure the
42
+ * creditBalances row is always consistent with the sum of creditTransactions.
43
+ * Zero raw SQL in application code.
44
+ */
45
+ export class DrizzleCreditLedger {
46
+ db;
47
+ constructor(db) {
48
+ this.db = db;
49
+ }
50
+ /**
51
+ * Read balance with FOR UPDATE row lock. Ensures the row exists first
52
+ * (upsert with ON CONFLICT DO NOTHING), then locks it for the duration
53
+ * of the enclosing transaction.
54
+ */
55
+ async lockedBalance(tx, tenantId) {
56
+ // raw SQL: Drizzle cannot express INSERT ... ON CONFLICT DO NOTHING for atomic upsert
57
+ await tx.execute(sql `INSERT INTO credit_balances (tenant_id, balance_credits, last_updated)
58
+ VALUES (${tenantId}, 0, now())
59
+ ON CONFLICT (tenant_id) DO NOTHING`);
60
+ // raw SQL: Drizzle cannot express SELECT FOR UPDATE for row-level locking
61
+ const rows = (await tx.execute(sql `SELECT balance_credits FROM credit_balances
62
+ WHERE tenant_id = ${tenantId} FOR UPDATE`));
63
+ const raw = rows.rows[0]?.balance_credits;
64
+ return raw != null ? Credit.fromRaw(Number(raw)) : Credit.ZERO;
65
+ }
66
+ /**
67
+ * Add credits to a tenant's balance.
68
+ * @returns The created transaction record.
69
+ */
70
+ async credit(tenantId, amount, type, description, referenceId, fundingSource, attributedUserId, expiresAt) {
71
+ if (amount.isZero() || amount.isNegative()) {
72
+ throw new Error("amount must be positive for credits");
73
+ }
74
+ return this.db.transaction(async (tx) => {
75
+ const currentBalance = await this.lockedBalance(tx, tenantId);
76
+ const newBalance = currentBalance.add(amount);
77
+ // Row guaranteed to exist after lockedBalance()
78
+ await tx
79
+ .update(creditBalances)
80
+ .set({
81
+ balance: newBalance,
82
+ lastUpdated: sql `(now())`,
83
+ })
84
+ .where(eq(creditBalances.tenantId, tenantId));
85
+ // Insert transaction record
86
+ const id = crypto.randomUUID();
87
+ const txn = {
88
+ id,
89
+ tenantId,
90
+ amount,
91
+ balanceAfter: newBalance,
92
+ type,
93
+ description: description ?? null,
94
+ referenceId: referenceId ?? null,
95
+ fundingSource: fundingSource ?? null,
96
+ attributedUserId: attributedUserId ?? null,
97
+ expiresAt: expiresAt ?? null,
98
+ };
99
+ await tx.insert(creditTransactions).values(txn);
100
+ return {
101
+ id,
102
+ tenantId: txn.tenantId,
103
+ amount,
104
+ balanceAfter: newBalance,
105
+ type: txn.type,
106
+ description: txn.description ?? null,
107
+ referenceId: txn.referenceId ?? null,
108
+ fundingSource: txn.fundingSource ?? null,
109
+ attributedUserId: txn.attributedUserId ?? null,
110
+ createdAt: new Date().toISOString(),
111
+ expiresAt: expiresAt ?? null,
112
+ };
113
+ });
114
+ }
115
+ /**
116
+ * Deduct credits from a tenant's balance.
117
+ * Throws InsufficientBalanceError if balance would go negative (unless allowNegative is true).
118
+ * @param allowNegative - If true, allow balance to go negative (for grace buffer debits). Default: false.
119
+ * @returns The created transaction record.
120
+ */
121
+ async debit(tenantId, amount, type, description, referenceId, allowNegative, attributedUserId) {
122
+ if (amount.isZero() || amount.isNegative()) {
123
+ throw new Error("amount must be positive for debits");
124
+ }
125
+ return this.db.transaction(async (tx) => {
126
+ const currentBalance = await this.lockedBalance(tx, tenantId);
127
+ if (!allowNegative && currentBalance.lessThan(amount)) {
128
+ throw new InsufficientBalanceError(currentBalance, amount);
129
+ }
130
+ const newBalance = currentBalance.subtract(amount);
131
+ // Row guaranteed to exist after lockedBalance()
132
+ await tx
133
+ .update(creditBalances)
134
+ .set({
135
+ balance: newBalance,
136
+ lastUpdated: sql `(now())`,
137
+ })
138
+ .where(eq(creditBalances.tenantId, tenantId));
139
+ const id = crypto.randomUUID();
140
+ const negativeAmount = Credit.fromRaw(-amount.toRaw()); // negative for debits
141
+ const txn = {
142
+ id,
143
+ tenantId,
144
+ amount: negativeAmount,
145
+ balanceAfter: newBalance,
146
+ type,
147
+ description: description ?? null,
148
+ referenceId: referenceId ?? null,
149
+ fundingSource: null,
150
+ attributedUserId: attributedUserId ?? null,
151
+ };
152
+ await tx.insert(creditTransactions).values(txn);
153
+ return {
154
+ id,
155
+ tenantId: txn.tenantId,
156
+ amount: negativeAmount,
157
+ balanceAfter: newBalance,
158
+ type: txn.type,
159
+ description: txn.description ?? null,
160
+ referenceId: txn.referenceId ?? null,
161
+ fundingSource: null,
162
+ attributedUserId: txn.attributedUserId ?? null,
163
+ createdAt: new Date().toISOString(),
164
+ expiresAt: null,
165
+ };
166
+ });
167
+ }
168
+ /** Get current balance for a tenant. Returns Credit.ZERO if tenant has no balance row. */
169
+ async balance(tenantId) {
170
+ const rows = await this.db
171
+ .select({ balance: creditBalances.balance })
172
+ .from(creditBalances)
173
+ .where(eq(creditBalances.tenantId, tenantId));
174
+ return rows[0]?.balance ?? Credit.ZERO;
175
+ }
176
+ /** Check if a reference ID has already been used (for idempotency). */
177
+ async hasReferenceId(referenceId) {
178
+ const rows = await this.db
179
+ .select({ id: creditTransactions.id })
180
+ .from(creditTransactions)
181
+ .where(eq(creditTransactions.referenceId, referenceId))
182
+ .limit(1);
183
+ return rows.length > 0;
184
+ }
185
+ /** Get transaction history for a tenant with optional filtering and pagination. */
186
+ async history(tenantId, opts = {}) {
187
+ const limit = Math.min(Math.max(1, opts.limit ?? 50), 250);
188
+ const offset = Math.max(0, opts.offset ?? 0);
189
+ const conditions = [eq(creditTransactions.tenantId, tenantId)];
190
+ if (opts.type) {
191
+ conditions.push(eq(creditTransactions.type, opts.type));
192
+ }
193
+ return this.db
194
+ .select()
195
+ .from(creditTransactions)
196
+ .where(and(...conditions))
197
+ .orderBy(desc(creditTransactions.createdAt))
198
+ .limit(limit)
199
+ .offset(offset);
200
+ }
201
+ /** Aggregate debit totals per attributed user for a tenant. */
202
+ async memberUsage(tenantId) {
203
+ const rows = await this.db
204
+ .select({
205
+ userId: creditTransactions.attributedUserId,
206
+ totalDebitRaw: sql `COALESCE(SUM(ABS(${creditTransactions.amount})), 0)`,
207
+ transactionCount: sql `COUNT(*)`,
208
+ })
209
+ .from(creditTransactions)
210
+ .where(and(eq(creditTransactions.tenantId, tenantId), isNotNull(creditTransactions.attributedUserId), sql `${creditTransactions.amount} < 0`))
211
+ .groupBy(creditTransactions.attributedUserId);
212
+ return rows
213
+ .filter((r) => r.userId != null)
214
+ .map((r) => ({
215
+ userId: r.userId,
216
+ totalDebit: Credit.fromRaw(Number(r.totalDebitRaw)),
217
+ transactionCount: r.transactionCount,
218
+ }));
219
+ }
220
+ /** Sum of all debit transactions (absolute value) for a tenant. */
221
+ async lifetimeSpend(tenantId) {
222
+ const rows = await this.db
223
+ .select({
224
+ // raw SQL: Drizzle cannot express COALESCE(SUM(ABS(...))) with typed operators
225
+ totalRaw: sql `COALESCE(SUM(ABS(${creditTransactions.amount})), 0)`,
226
+ })
227
+ .from(creditTransactions)
228
+ .where(and(eq(creditTransactions.tenantId, tenantId), sql `${creditTransactions.amount} < 0`));
229
+ const raw = BigInt(String(rows[0]?.totalRaw ?? 0));
230
+ if (raw > BigInt(Number.MAX_SAFE_INTEGER)) {
231
+ throw new Error(`lifetimeSpend overflow: ${raw}`);
232
+ }
233
+ return Credit.fromRaw(Number(raw));
234
+ }
235
+ /** Batch version of lifetimeSpend — single query for multiple tenants. */
236
+ async lifetimeSpendBatch(tenantIds) {
237
+ if (tenantIds.length === 0)
238
+ return new Map();
239
+ const rows = await this.db
240
+ .select({
241
+ tenantId: creditTransactions.tenantId,
242
+ // raw SQL: Drizzle cannot express COALESCE(SUM(ABS(...))) with typed operators
243
+ totalRaw: sql `COALESCE(SUM(ABS(${creditTransactions.amount})), 0)`,
244
+ })
245
+ .from(creditTransactions)
246
+ .where(and(inArray(creditTransactions.tenantId, tenantIds), sql `${creditTransactions.amount} < 0`))
247
+ .groupBy(creditTransactions.tenantId);
248
+ const result = new Map();
249
+ for (const row of rows) {
250
+ const raw = BigInt(String(row.totalRaw));
251
+ if (raw > BigInt(Number.MAX_SAFE_INTEGER)) {
252
+ throw new Error(`lifetimeSpend overflow: ${raw}`);
253
+ }
254
+ result.set(row.tenantId, Credit.fromRaw(Number(raw)));
255
+ }
256
+ // Fill zeros for tenants with no debit transactions
257
+ for (const id of tenantIds) {
258
+ if (!result.has(id))
259
+ result.set(id, Credit.ZERO);
260
+ }
261
+ return result;
262
+ }
263
+ /** Return expired credit grant transactions not yet processed by the expiry cron. */
264
+ async expiredCredits(now) {
265
+ const rows = await this.db
266
+ .select({
267
+ id: creditTransactions.id,
268
+ tenantId: creditTransactions.tenantId,
269
+ amount: creditTransactions.amount,
270
+ })
271
+ .from(creditTransactions)
272
+ .where(and(isNotNull(creditTransactions.expiresAt), sql `${creditTransactions.expiresAt} <= ${now}`, sql `${creditTransactions.amount} > 0`));
273
+ const result = [];
274
+ for (const row of rows) {
275
+ if (await this.hasReferenceId(`expiry:${row.id}`))
276
+ continue;
277
+ result.push(row);
278
+ }
279
+ return result;
280
+ }
281
+ /** List all tenants with positive balance (for cron deduction). */
282
+ async tenantsWithBalance() {
283
+ return this.db
284
+ .select({
285
+ tenantId: creditBalances.tenantId,
286
+ balance: creditBalances.balance,
287
+ })
288
+ .from(creditBalances)
289
+ .where(sql `${creditBalances.balance} > 0`);
290
+ }
291
+ }
292
+ // Backward-compat alias — callers using 'new CreditLedger(db)' continue to work.
293
+ export { DrizzleCreditLedger as CreditLedger };
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Tests for CreditLedger — including the allowNegative debit parameter (WOP-821).
3
+ */
4
+ export {};