@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,238 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { renderNotificationTemplate } from "./notification-templates.js";
3
+ describe("renderNotificationTemplate", () => {
4
+ describe("credits-depleted", () => {
5
+ it("renders valid HTML and text with subject", () => {
6
+ const result = renderNotificationTemplate("credits-depleted", {
7
+ email: "user@example.com",
8
+ creditsUrl: "https://app.wopr.bot/billing/credits",
9
+ });
10
+ expect(result.subject).toBeTruthy();
11
+ expect(result.html).toContain("<!DOCTYPE html>");
12
+ expect(result.text).toBeTruthy();
13
+ });
14
+ });
15
+ describe("grace-period-start", () => {
16
+ it("renders with balance and grace days", () => {
17
+ const result = renderNotificationTemplate("grace-period-start", {
18
+ email: "user@example.com",
19
+ balanceDollars: "$0.50",
20
+ graceDays: 7,
21
+ creditsUrl: "https://app.wopr.bot/billing/credits",
22
+ });
23
+ expect(result.html).toContain("$0.50");
24
+ expect(result.html).toContain("7");
25
+ });
26
+ });
27
+ describe("grace-period-warning", () => {
28
+ it("renders a warning subject", () => {
29
+ const result = renderNotificationTemplate("grace-period-warning", {
30
+ email: "user@example.com",
31
+ creditsUrl: "https://app.wopr.bot/billing/credits",
32
+ });
33
+ expect(result.subject).toMatch(/last chance|suspended|tomorrow/i);
34
+ });
35
+ });
36
+ describe("auto-suspended", () => {
37
+ it("renders with reason", () => {
38
+ const result = renderNotificationTemplate("auto-suspended", {
39
+ email: "user@example.com",
40
+ reason: "Grace period expired",
41
+ creditsUrl: "https://app.wopr.bot/billing/credits",
42
+ });
43
+ expect(result.html).toContain("Grace period expired");
44
+ });
45
+ });
46
+ describe("auto-topup-success", () => {
47
+ it("renders with amount and new balance", () => {
48
+ const result = renderNotificationTemplate("auto-topup-success", {
49
+ email: "user@example.com",
50
+ amountDollars: "$50.00",
51
+ newBalanceDollars: "$55.00",
52
+ creditsUrl: "https://app.wopr.bot/billing/credits",
53
+ });
54
+ expect(result.html).toContain("$50.00");
55
+ expect(result.html).toContain("$55.00");
56
+ });
57
+ });
58
+ describe("auto-topup-failed", () => {
59
+ it("renders a failed top-up notification", () => {
60
+ const result = renderNotificationTemplate("auto-topup-failed", {
61
+ email: "user@example.com",
62
+ creditsUrl: "https://app.wopr.bot/billing/credits",
63
+ });
64
+ expect(result.subject).toMatch(/auto top-up failed|failed/i);
65
+ });
66
+ });
67
+ describe("crypto-payment-confirmed", () => {
68
+ it("renders with amount", () => {
69
+ const result = renderNotificationTemplate("crypto-payment-confirmed", {
70
+ email: "user@example.com",
71
+ amountDollars: "$25.00",
72
+ newBalanceDollars: "$30.00",
73
+ });
74
+ expect(result.html).toContain("$25.00");
75
+ });
76
+ });
77
+ describe("admin-suspended", () => {
78
+ it("renders with reason", () => {
79
+ const result = renderNotificationTemplate("admin-suspended", {
80
+ email: "user@example.com",
81
+ reason: "ToS violation",
82
+ });
83
+ expect(result.html).toContain("ToS violation");
84
+ });
85
+ });
86
+ describe("admin-reactivated", () => {
87
+ it("renders a reactivation email", () => {
88
+ const result = renderNotificationTemplate("admin-reactivated", {
89
+ email: "user@example.com",
90
+ });
91
+ expect(result.subject).toMatch(/reactivated/i);
92
+ });
93
+ });
94
+ describe("credits-granted", () => {
95
+ it("renders with amount and reason", () => {
96
+ const result = renderNotificationTemplate("credits-granted", {
97
+ email: "user@example.com",
98
+ amountDollars: "$5.00",
99
+ reason: "Support credit",
100
+ });
101
+ expect(result.html).toContain("$5.00");
102
+ expect(result.html).toContain("Support credit");
103
+ });
104
+ });
105
+ describe("role-changed", () => {
106
+ it("renders with new role", () => {
107
+ const result = renderNotificationTemplate("role-changed", {
108
+ email: "user@example.com",
109
+ newRole: "tenant_admin",
110
+ });
111
+ expect(result.html).toContain("tenant_admin");
112
+ });
113
+ });
114
+ describe("team-invite", () => {
115
+ it("renders with tenant name and invite URL", () => {
116
+ const result = renderNotificationTemplate("team-invite", {
117
+ email: "user@example.com",
118
+ tenantName: "Acme Corp",
119
+ inviteUrl: "https://app.wopr.bot/invite/abc",
120
+ });
121
+ expect(result.html).toContain("Acme Corp");
122
+ });
123
+ });
124
+ describe("agent-created", () => {
125
+ it("renders with agent name", () => {
126
+ const result = renderNotificationTemplate("agent-created", {
127
+ email: "user@example.com",
128
+ agentName: "HAL 9000",
129
+ });
130
+ expect(result.html).toContain("HAL 9000");
131
+ });
132
+ });
133
+ describe("channel-connected", () => {
134
+ it("renders with channel and agent name", () => {
135
+ const result = renderNotificationTemplate("channel-connected", {
136
+ email: "user@example.com",
137
+ channelName: "Discord",
138
+ agentName: "MyBot",
139
+ });
140
+ expect(result.html).toContain("Discord");
141
+ expect(result.html).toContain("MyBot");
142
+ });
143
+ });
144
+ describe("channel-disconnected", () => {
145
+ it("renders with channel, agent, and reason", () => {
146
+ const result = renderNotificationTemplate("channel-disconnected", {
147
+ email: "user@example.com",
148
+ channelName: "Discord",
149
+ agentName: "MyBot",
150
+ reason: "Token expired",
151
+ });
152
+ expect(result.html).toContain("Discord");
153
+ expect(result.html).toContain("Token expired");
154
+ });
155
+ });
156
+ describe("agent-suspended", () => {
157
+ it("renders with agent name and reason", () => {
158
+ const result = renderNotificationTemplate("agent-suspended", {
159
+ email: "user@example.com",
160
+ agentName: "MyBot",
161
+ reason: "Account suspended",
162
+ });
163
+ expect(result.html).toContain("MyBot");
164
+ });
165
+ });
166
+ describe("custom", () => {
167
+ it("renders with custom subject and body", () => {
168
+ const result = renderNotificationTemplate("custom", {
169
+ email: "user@example.com",
170
+ subject: "Hello from WOPR",
171
+ bodyText: "This is a custom message.",
172
+ });
173
+ expect(result.subject).toBe("Hello from WOPR");
174
+ expect(result.html).toContain("This is a custom message.");
175
+ expect(result.text).toContain("This is a custom message.");
176
+ });
177
+ it("escapes HTML in bodyText to prevent XSS", () => {
178
+ const result = renderNotificationTemplate("custom", {
179
+ email: "user@example.com",
180
+ subject: "Test",
181
+ bodyText: "<script>alert('xss')</script>",
182
+ });
183
+ expect(result.html).not.toContain("<script>");
184
+ expect(result.html).toContain("&lt;script&gt;");
185
+ });
186
+ it("converts newlines to <br> tags in HTML", () => {
187
+ const result = renderNotificationTemplate("custom", {
188
+ email: "user@example.com",
189
+ subject: "Test",
190
+ bodyText: "Line one\nLine two",
191
+ });
192
+ expect(result.html).toContain("<br");
193
+ });
194
+ });
195
+ describe("dividend-weekly-digest", () => {
196
+ it("renders dividend-weekly-digest template", () => {
197
+ const result = renderNotificationTemplate("dividend-weekly-digest", {
198
+ email: "alice@example.com",
199
+ weeklyTotalDollars: "$3.50",
200
+ weeklyTotalCents: 350,
201
+ lifetimeTotalDollars: "$42.00",
202
+ distributionCount: 5,
203
+ poolAvgCents: 2000,
204
+ activeUsersAvg: 10,
205
+ nextDividendDate: "Tuesday, February 25, 2026",
206
+ weekStartDate: "February 17",
207
+ weekEndDate: "February 23",
208
+ unsubscribeUrl: "https://app.wopr.bot/settings/notifications",
209
+ creditsUrl: "https://app.wopr.bot/billing/credits",
210
+ });
211
+ expect(result.subject).toBe("WOPR paid you $3.50 this week");
212
+ expect(result.html).toContain("$3.50");
213
+ expect(result.html).toContain("$42.00");
214
+ expect(result.html).toContain("February 17");
215
+ expect(result.html).toContain("February 23");
216
+ expect(result.html).toContain("Unsubscribe");
217
+ expect(result.text).toContain("$3.50");
218
+ expect(result.text).toContain("Unsubscribe");
219
+ });
220
+ });
221
+ describe("XSS protection", () => {
222
+ it("escapes HTML in user-supplied fields like agentName", () => {
223
+ const result = renderNotificationTemplate("agent-created", {
224
+ email: "user@example.com",
225
+ agentName: '<img src=x onerror="alert(1)">',
226
+ });
227
+ expect(result.html).not.toContain("<img");
228
+ expect(result.html).toContain("&lt;img");
229
+ });
230
+ it("escapes HTML in reason fields", () => {
231
+ const result = renderNotificationTemplate("admin-suspended", {
232
+ email: "user@example.com",
233
+ reason: "<script>evil()</script>",
234
+ });
235
+ expect(result.html).not.toContain("<script>");
236
+ });
237
+ });
238
+ });
@@ -0,0 +1,24 @@
1
+ /**
2
+ * NotificationWorker — processes pending notification queue entries.
3
+ *
4
+ * Called on a timer (e.g. every 30s) from the server startup code.
5
+ * Do NOT put the interval inside this class.
6
+ */
7
+ import type { EmailClient } from "./client.js";
8
+ import type { INotificationPreferencesRepository, INotificationQueueRepository } from "./notification-repository-types.js";
9
+ export interface NotificationWorkerConfig {
10
+ queue: INotificationQueueRepository;
11
+ emailClient: EmailClient;
12
+ preferences: INotificationPreferencesRepository;
13
+ batchSize?: number;
14
+ }
15
+ export declare class NotificationWorker {
16
+ private readonly queue;
17
+ private readonly emailClient;
18
+ private readonly preferences;
19
+ private readonly batchSize;
20
+ constructor(config: NotificationWorkerConfig);
21
+ /** Process one batch of pending notifications. Returns count of processed items. */
22
+ processBatch(): Promise<number>;
23
+ private isEnabledByPreferences;
24
+ }
@@ -0,0 +1,109 @@
1
+ /**
2
+ * NotificationWorker — processes pending notification queue entries.
3
+ *
4
+ * Called on a timer (e.g. every 30s) from the server startup code.
5
+ * Do NOT put the interval inside this class.
6
+ */
7
+ import { logger } from "../config/logger.js";
8
+ import { renderNotificationTemplate } from "./notification-templates.js";
9
+ /** Templates that bypass user preference checks — always sent. */
10
+ const CRITICAL_TEMPLATES = new Set([
11
+ "grace-period-start",
12
+ "grace-period-warning",
13
+ "auto-suspended",
14
+ "admin-suspended",
15
+ "admin-reactivated",
16
+ "password-reset",
17
+ "welcome",
18
+ "account-deletion-requested",
19
+ "account-deletion-cancelled",
20
+ "account-deletion-completed",
21
+ ]);
22
+ /** Map from template name to preference key. */
23
+ const PREF_MAP = {
24
+ "low-balance": "billing_low_balance",
25
+ "credits-depleted": "billing_low_balance",
26
+ "auto-topup-success": "billing_auto_topup",
27
+ "auto-topup-failed": "billing_auto_topup",
28
+ "credit-purchase-receipt": "billing_receipts",
29
+ "crypto-payment-confirmed": "billing_receipts",
30
+ "channel-disconnected": "agent_channel_disconnect",
31
+ "agent-created": "agent_status_changes",
32
+ "channel-connected": "agent_status_changes",
33
+ "agent-suspended": "agent_status_changes",
34
+ "credits-granted": "billing_receipts",
35
+ "dividend-weekly-digest": "billing_receipts",
36
+ "role-changed": "account_role_changes",
37
+ "team-invite": "account_team_invites",
38
+ };
39
+ export class NotificationWorker {
40
+ queue;
41
+ emailClient;
42
+ preferences;
43
+ batchSize;
44
+ constructor(config) {
45
+ this.queue = config.queue;
46
+ this.emailClient = config.emailClient;
47
+ this.preferences = config.preferences;
48
+ this.batchSize = config.batchSize ?? 10;
49
+ }
50
+ /** Process one batch of pending notifications. Returns count of processed items. */
51
+ async processBatch() {
52
+ const pending = await this.queue.fetchPending(this.batchSize);
53
+ let processed = 0;
54
+ for (const notif of pending) {
55
+ try {
56
+ const data = JSON.parse(notif.data);
57
+ const email = data.email;
58
+ if (!email) {
59
+ logger.error("Notification missing email field", {
60
+ notificationId: notif.id,
61
+ template: notif.template,
62
+ });
63
+ this.queue.markFailed(notif.id, notif.attempts + 1);
64
+ processed++;
65
+ continue;
66
+ }
67
+ // Check preferences (skip for critical notifications)
68
+ if (!CRITICAL_TEMPLATES.has(notif.template)) {
69
+ const prefs = this.preferences.get(notif.tenantId);
70
+ if (!this.isEnabledByPreferences(notif.template, prefs)) {
71
+ // User has disabled this notification type — mark sent to clear queue
72
+ this.queue.markSent(notif.id);
73
+ processed++;
74
+ continue;
75
+ }
76
+ }
77
+ // Render the template
78
+ const rendered = renderNotificationTemplate(notif.template, data);
79
+ // Send via email client
80
+ await this.emailClient.send({
81
+ to: email,
82
+ subject: rendered.subject,
83
+ html: rendered.html,
84
+ text: rendered.text,
85
+ userId: notif.tenantId,
86
+ templateName: notif.template,
87
+ });
88
+ this.queue.markSent(notif.id);
89
+ processed++;
90
+ }
91
+ catch (err) {
92
+ logger.error("Notification send failed", {
93
+ notificationId: notif.id,
94
+ template: notif.template,
95
+ error: err instanceof Error ? err.message : String(err),
96
+ });
97
+ this.queue.markFailed(notif.id, notif.attempts + 1);
98
+ processed++;
99
+ }
100
+ }
101
+ return processed;
102
+ }
103
+ isEnabledByPreferences(template, prefs) {
104
+ const prefKey = PREF_MAP[template];
105
+ if (!prefKey)
106
+ return true; // unknown template -> send by default
107
+ return prefs[prefKey] !== false; // default to enabled if key missing
108
+ }
109
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,153 @@
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+ import { NotificationWorker } from "./notification-worker.js";
3
+ vi.mock("../config/logger.js", () => ({
4
+ logger: {
5
+ info: vi.fn(),
6
+ error: vi.fn(),
7
+ warn: vi.fn(),
8
+ debug: vi.fn(),
9
+ },
10
+ }));
11
+ function makeNotif(overrides = {}) {
12
+ return {
13
+ id: "notif-1",
14
+ tenantId: "tenant-1",
15
+ template: "low-balance",
16
+ data: JSON.stringify({ email: "user@example.com", balanceDollars: "$1.00" }),
17
+ status: "pending",
18
+ attempts: 0,
19
+ retryAfter: null,
20
+ sentAt: null,
21
+ createdAt: Date.now(),
22
+ ...overrides,
23
+ };
24
+ }
25
+ function makeQueue(pending = []) {
26
+ return {
27
+ enqueue: vi.fn().mockReturnValue("notif-id"),
28
+ fetchPending: vi.fn().mockReturnValue(pending),
29
+ markSent: vi.fn(),
30
+ markFailed: vi.fn(),
31
+ listForTenant: vi.fn().mockReturnValue({ entries: [], total: 0 }),
32
+ };
33
+ }
34
+ function makePrefs(prefs = {}) {
35
+ const defaultPrefs = {
36
+ billing_low_balance: true,
37
+ billing_receipts: true,
38
+ billing_auto_topup: true,
39
+ agent_channel_disconnect: true,
40
+ agent_status_changes: false,
41
+ account_role_changes: true,
42
+ account_team_invites: true,
43
+ };
44
+ return {
45
+ get: vi.fn().mockReturnValue({ ...defaultPrefs, ...prefs }),
46
+ update: vi.fn(),
47
+ };
48
+ }
49
+ function makeEmailClient() {
50
+ return {
51
+ send: vi.fn().mockResolvedValue({ id: "email-123", success: true }),
52
+ onEmailSent: vi.fn(),
53
+ };
54
+ }
55
+ describe("NotificationWorker", () => {
56
+ let emailClient;
57
+ beforeEach(() => {
58
+ emailClient = makeEmailClient();
59
+ });
60
+ describe("processBatch", () => {
61
+ it("sends emails for pending notifications and marks them sent", async () => {
62
+ const notif = makeNotif();
63
+ const queue = makeQueue([notif]);
64
+ const prefs = makePrefs();
65
+ const worker = new NotificationWorker({ queue, emailClient, preferences: prefs });
66
+ const count = await worker.processBatch();
67
+ expect(emailClient.send).toHaveBeenCalledOnce();
68
+ expect(queue.markSent).toHaveBeenCalledWith("notif-1");
69
+ expect(count).toBe(1);
70
+ });
71
+ it("returns 0 when no pending notifications", async () => {
72
+ const queue = makeQueue([]);
73
+ const prefs = makePrefs();
74
+ const worker = new NotificationWorker({ queue, emailClient, preferences: prefs });
75
+ const count = await worker.processBatch();
76
+ expect(count).toBe(0);
77
+ expect(emailClient.send).not.toHaveBeenCalled();
78
+ });
79
+ it("marks notification as failed (not sent) when email is missing", async () => {
80
+ const notif = makeNotif({ data: JSON.stringify({}) }); // no email
81
+ const queue = makeQueue([notif]);
82
+ const prefs = makePrefs();
83
+ const worker = new NotificationWorker({ queue, emailClient, preferences: prefs });
84
+ await worker.processBatch();
85
+ expect(emailClient.send).not.toHaveBeenCalled();
86
+ expect(queue.markFailed).toHaveBeenCalledWith("notif-1", 1);
87
+ });
88
+ it("marks as sent (skipped) when user preference disables that template", async () => {
89
+ const notif = makeNotif({ template: "agent-created" }); // pref: agent_status_changes
90
+ const queue = makeQueue([notif]);
91
+ const prefs = makePrefs({ agent_status_changes: false }); // disabled
92
+ const worker = new NotificationWorker({ queue, emailClient, preferences: prefs });
93
+ await worker.processBatch();
94
+ expect(emailClient.send).not.toHaveBeenCalled();
95
+ expect(queue.markSent).toHaveBeenCalledWith("notif-1"); // cleared from queue
96
+ });
97
+ it("sends critical templates even when preferences would disable them", async () => {
98
+ // grace-period-start is critical — should bypass preference check
99
+ const notif = makeNotif({
100
+ template: "grace-period-start",
101
+ data: JSON.stringify({
102
+ email: "user@example.com",
103
+ balanceDollars: "$0.00",
104
+ graceDays: 7,
105
+ creditsUrl: "https://app.wopr.bot/billing/credits",
106
+ }),
107
+ });
108
+ const queue = makeQueue([notif]);
109
+ // Even if all prefs disabled, critical templates must send
110
+ const prefs = makePrefs({
111
+ billing_low_balance: false,
112
+ billing_receipts: false,
113
+ billing_auto_topup: false,
114
+ });
115
+ const worker = new NotificationWorker({ queue, emailClient, preferences: prefs });
116
+ await worker.processBatch();
117
+ expect(emailClient.send).toHaveBeenCalledOnce();
118
+ expect(queue.markSent).toHaveBeenCalledWith(notif.id);
119
+ });
120
+ it("marks as failed and increments attempts when send throws", async () => {
121
+ const notif = makeNotif({ attempts: 2 });
122
+ const queue = makeQueue([notif]);
123
+ const prefs = makePrefs();
124
+ vi.spyOn(emailClient, "send").mockRejectedValueOnce(new Error("Network error"));
125
+ const worker = new NotificationWorker({ queue, emailClient, preferences: prefs });
126
+ await worker.processBatch();
127
+ expect(queue.markFailed).toHaveBeenCalledWith("notif-1", 3); // attempts + 1
128
+ expect(queue.markSent).not.toHaveBeenCalled();
129
+ });
130
+ it("processes multiple notifications in one batch", async () => {
131
+ const notif1 = makeNotif({ id: "n1" });
132
+ const notif2 = makeNotif({
133
+ id: "n2",
134
+ template: "welcome",
135
+ data: JSON.stringify({ email: "b@b.com" }),
136
+ });
137
+ const queue = makeQueue([notif1, notif2]);
138
+ const prefs = makePrefs();
139
+ const worker = new NotificationWorker({ queue, emailClient, preferences: prefs });
140
+ const count = await worker.processBatch();
141
+ expect(count).toBe(2);
142
+ expect(emailClient.send).toHaveBeenCalledTimes(2);
143
+ });
144
+ it("respects custom batchSize", async () => {
145
+ const notifs = Array.from({ length: 5 }, (_, i) => makeNotif({ id: `n${i}` }));
146
+ const queue = makeQueue(notifs.slice(0, 3)); // fetchPending called with batchSize
147
+ const prefs = makePrefs();
148
+ const worker = new NotificationWorker({ queue, emailClient, preferences: prefs, batchSize: 3 });
149
+ await worker.processBatch();
150
+ expect(queue.fetchPending).toHaveBeenCalledWith(3);
151
+ });
152
+ });
153
+ });
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Email Verification Middleware — Blocks actions until email is verified.
3
+ *
4
+ * When used after session auth middleware, checks that the authenticated user
5
+ * has verified their email. API token auth (machine-to-machine) is not subject
6
+ * to email verification and is always allowed through.
7
+ */
8
+ import type { Context, Next } from "hono";
9
+ /** Minimal interface for checking email verification status. */
10
+ export interface IEmailVerifier {
11
+ isVerified(userId: string): Promise<boolean>;
12
+ }
13
+ /**
14
+ * Create middleware that blocks session-authenticated users who haven't verified their email.
15
+ *
16
+ * API token auth (authMethod === "api_key") bypasses this check since machine
17
+ * clients don't have email addresses to verify.
18
+ *
19
+ * @param verifier - Email verification store
20
+ */
21
+ export declare function requireEmailVerified(verifier: IEmailVerifier): (c: Context, next: Next) => Promise<void | (Response & import("hono").TypedResponse<{
22
+ error: string;
23
+ message: string;
24
+ code: string;
25
+ }, 403, "json">)>;
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Email Verification Middleware — Blocks actions until email is verified.
3
+ *
4
+ * When used after session auth middleware, checks that the authenticated user
5
+ * has verified their email. API token auth (machine-to-machine) is not subject
6
+ * to email verification and is always allowed through.
7
+ */
8
+ import { logger } from "../config/logger.js";
9
+ /**
10
+ * Create middleware that blocks session-authenticated users who haven't verified their email.
11
+ *
12
+ * API token auth (authMethod === "api_key") bypasses this check since machine
13
+ * clients don't have email addresses to verify.
14
+ *
15
+ * @param verifier - Email verification store
16
+ */
17
+ export function requireEmailVerified(verifier) {
18
+ return async (c, next) => {
19
+ let authMethod;
20
+ let userId;
21
+ try {
22
+ authMethod = c.get("authMethod");
23
+ const user = c.get("user");
24
+ userId = user?.id;
25
+ }
26
+ catch {
27
+ // No auth context set — let downstream auth middleware handle 401
28
+ return next();
29
+ }
30
+ // API token auth bypasses email verification
31
+ if (authMethod === "api_key") {
32
+ return next();
33
+ }
34
+ // Session auth requires verified email
35
+ if (authMethod === "session" && userId) {
36
+ try {
37
+ if (!(await verifier.isVerified(userId))) {
38
+ return c.json({
39
+ error: "Email verification required",
40
+ message: "Please verify your email address before creating bots",
41
+ code: "EMAIL_NOT_VERIFIED",
42
+ }, 403);
43
+ }
44
+ }
45
+ catch (error) {
46
+ // If we can't check verification (DB issue), don't block the user
47
+ logger.warn("Email verification check failed", { error });
48
+ }
49
+ }
50
+ return next();
51
+ };
52
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,62 @@
1
+ import { Hono } from "hono";
2
+ import { beforeEach, describe, expect, it } from "vitest";
3
+ import { requireEmailVerified } from "./require-verified.js";
4
+ describe("requireEmailVerified middleware", () => {
5
+ let verifiedUsers;
6
+ let app;
7
+ beforeEach(() => {
8
+ verifiedUsers = new Set();
9
+ const middleware = requireEmailVerified({
10
+ isVerified: async (userId) => verifiedUsers.has(userId),
11
+ });
12
+ app = new Hono();
13
+ // Simulate session auth middleware setting user context
14
+ app.use("/test/*", async (c, next) => {
15
+ const authMethod = (c.req.header("X-Auth-Method") || "session");
16
+ const userId = c.req.header("X-User-Id") || "user-1";
17
+ c.set("authMethod", authMethod);
18
+ c.set("user", { id: userId, roles: ["user"] });
19
+ return next();
20
+ });
21
+ app.use("/test/*", middleware);
22
+ app.post("/test/create", (c) => c.json({ ok: true }));
23
+ // Route without auth context — use a plain Hono app for this
24
+ const noauthApp = new Hono();
25
+ noauthApp.use("/noauth/*", middleware);
26
+ noauthApp.post("/noauth/create", (c) => c.json({ ok: true }));
27
+ // Mount the noauth app into the main app
28
+ app.route("/", noauthApp);
29
+ });
30
+ it("should block session-authenticated users without verified email", async () => {
31
+ const res = await app.request("/test/create", {
32
+ method: "POST",
33
+ headers: { "X-Auth-Method": "session", "X-User-Id": "user-1" },
34
+ });
35
+ expect(res.status).toBe(403);
36
+ const body = await res.json();
37
+ expect(body.code).toBe("EMAIL_NOT_VERIFIED");
38
+ });
39
+ it("should allow session-authenticated users with verified email", async () => {
40
+ verifiedUsers.add("user-1");
41
+ const res = await app.request("/test/create", {
42
+ method: "POST",
43
+ headers: { "X-Auth-Method": "session", "X-User-Id": "user-1" },
44
+ });
45
+ expect(res.status).toBe(200);
46
+ const body = await res.json();
47
+ expect(body.ok).toBe(true);
48
+ });
49
+ it("should always allow API token auth", async () => {
50
+ const res = await app.request("/test/create", {
51
+ method: "POST",
52
+ headers: { "X-Auth-Method": "api_key", "X-User-Id": "token:write" },
53
+ });
54
+ expect(res.status).toBe(200);
55
+ const body = await res.json();
56
+ expect(body.ok).toBe(true);
57
+ });
58
+ it("should pass through when no auth context is set", async () => {
59
+ const res = await app.request("/noauth/create", { method: "POST" });
60
+ expect(res.status).toBe(200);
61
+ });
62
+ });