@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,634 @@
1
+ import type { PGlite } from "@electric-sql/pglite";
2
+ import { TRPCError } from "@trpc/server";
3
+ import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
4
+ import type { PlatformDb } from "../db/index.js";
5
+ import { DrizzleOrgMemberRepository } from "./org-member-repository.js";
6
+ import { createTestDb, truncateAllTables } from "../test/db.js";
7
+ import { DrizzleOrgRepository } from "./drizzle-org-repository.js";
8
+ import { OrgService } from "./org-service.js";
9
+
10
+ async function setup(db: PlatformDb) {
11
+ const orgRepo = new DrizzleOrgRepository(db);
12
+ const memberRepo = new DrizzleOrgMemberRepository(db);
13
+ const service = new OrgService(orgRepo, memberRepo, db);
14
+ return { service, orgRepo, memberRepo };
15
+ }
16
+
17
+ describe("OrgService", () => {
18
+ let db: PlatformDb;
19
+ let pool: PGlite;
20
+ let service: OrgService;
21
+ const userId = "user-1";
22
+
23
+ beforeAll(async () => {
24
+ ({ db, pool } = await createTestDb());
25
+ });
26
+
27
+ afterAll(async () => {
28
+ await pool.close().catch(() => {});
29
+ });
30
+
31
+ beforeEach(async () => {
32
+ await truncateAllTables(pool);
33
+ ({ service } = await setup(db));
34
+ });
35
+
36
+ describe("getOrCreatePersonalOrg", () => {
37
+ it("creates personal org and owner member on first call", async () => {
38
+ const org = await service.getOrCreatePersonalOrg(userId, "Alice");
39
+ expect(org.type).toBe("personal");
40
+ expect(org.ownerId).toBe(userId);
41
+ expect(org.members).toHaveLength(1);
42
+ expect(org.members[0].role).toBe("owner");
43
+ });
44
+
45
+ it("is idempotent — second call returns the same org", async () => {
46
+ await service.getOrCreatePersonalOrg(userId, "Alice");
47
+ const org = await service.getOrCreatePersonalOrg(userId, "Alice");
48
+ expect(org.members).toHaveLength(1);
49
+ });
50
+ });
51
+
52
+ describe("updateOrg", () => {
53
+ it("persists name change to DB", async () => {
54
+ // Create an org, then update it
55
+ const { orgRepo, memberRepo } = await setup(db);
56
+ const svc = new OrgService(orgRepo, memberRepo, db);
57
+ const owner = "owner-1";
58
+ const org = await orgRepo.createOrg(owner, "Original", "original-slug");
59
+ await memberRepo.addMember({
60
+ id: "m1",
61
+ orgId: org.id,
62
+ userId: owner,
63
+ role: "owner",
64
+ joinedAt: Date.now(),
65
+ });
66
+
67
+ const updated = await svc.updateOrg(org.id, owner, { name: "Updated Name" });
68
+ expect(updated.name).toBe("Updated Name");
69
+ expect(updated.slug).toBe("original-slug"); // unchanged
70
+
71
+ // Verify persisted
72
+ const fetched = await orgRepo.getById(org.id);
73
+ expect(fetched?.name).toBe("Updated Name");
74
+ });
75
+
76
+ it("persists slug change to DB", async () => {
77
+ const { orgRepo, memberRepo } = await setup(db);
78
+ const svc = new OrgService(orgRepo, memberRepo, db);
79
+ const owner = "owner-2";
80
+ const org = await orgRepo.createOrg(owner, "My Org", "my-org");
81
+ await memberRepo.addMember({
82
+ id: "m2",
83
+ orgId: org.id,
84
+ userId: owner,
85
+ role: "owner",
86
+ joinedAt: Date.now(),
87
+ });
88
+
89
+ const updated = await svc.updateOrg(org.id, owner, { slug: "new-slug-ab" });
90
+ expect(updated.slug).toBe("new-slug-ab");
91
+
92
+ const fetched = await orgRepo.getById(org.id);
93
+ expect(fetched?.slug).toBe("new-slug-ab");
94
+ });
95
+
96
+ it("throws FORBIDDEN when non-admin tries to update", async () => {
97
+ const { orgRepo, memberRepo } = await setup(db);
98
+ const svc = new OrgService(orgRepo, memberRepo, db);
99
+ const owner = "owner-3";
100
+ const org = await orgRepo.createOrg(owner, "Org", "org-slug");
101
+ await memberRepo.addMember({
102
+ id: "m3",
103
+ orgId: org.id,
104
+ userId: owner,
105
+ role: "owner",
106
+ joinedAt: Date.now(),
107
+ });
108
+
109
+ await expect(svc.updateOrg(org.id, "not-a-member", { name: "hack" })).rejects.toThrow();
110
+ });
111
+
112
+ it("throws CONFLICT when slug is already taken", async () => {
113
+ const { orgRepo, memberRepo } = await setup(db);
114
+ const svc = new OrgService(orgRepo, memberRepo, db);
115
+ const owner = "owner-4";
116
+ await orgRepo.createOrg("other", "Other Org", "taken-slug");
117
+ const org = await orgRepo.createOrg(owner, "My Org", "mine-slug");
118
+ await memberRepo.addMember({
119
+ id: "m4",
120
+ orgId: org.id,
121
+ userId: owner,
122
+ role: "owner",
123
+ joinedAt: Date.now(),
124
+ });
125
+
126
+ await expect(svc.updateOrg(org.id, owner, { slug: "taken-slug" })).rejects.toThrow();
127
+ });
128
+ });
129
+
130
+ describe("transferOwnership", () => {
131
+ it("updates member roles AND persists new ownerId to tenants table", async () => {
132
+ const { orgRepo, memberRepo } = await setup(db);
133
+ const svc = new OrgService(orgRepo, memberRepo, db);
134
+ const owner = "owner-5";
135
+ const newOwner = "new-owner-5";
136
+ const org = await orgRepo.createOrg(owner, "Transfer Org", "transfer-org");
137
+ await memberRepo.addMember({
138
+ id: "m5",
139
+ orgId: org.id,
140
+ userId: owner,
141
+ role: "owner",
142
+ joinedAt: Date.now(),
143
+ });
144
+ await memberRepo.addMember({
145
+ id: "m6",
146
+ orgId: org.id,
147
+ userId: newOwner,
148
+ role: "member",
149
+ joinedAt: Date.now(),
150
+ });
151
+
152
+ await svc.transferOwnership(org.id, owner, newOwner);
153
+
154
+ const updatedOrg = await orgRepo.getById(org.id);
155
+ expect(updatedOrg?.ownerId).toBe(newOwner);
156
+
157
+ const newOwnerMember = await memberRepo.findMember(org.id, newOwner);
158
+ expect(newOwnerMember?.role).toBe("owner");
159
+
160
+ const oldOwnerMember = await memberRepo.findMember(org.id, owner);
161
+ expect(oldOwnerMember?.role).toBe("admin");
162
+ });
163
+
164
+ it("throws FORBIDDEN if non-owner tries to transfer", async () => {
165
+ const { orgRepo, memberRepo } = await setup(db);
166
+ const svc = new OrgService(orgRepo, memberRepo, db);
167
+ const owner = "owner-6";
168
+ const other = "other-6";
169
+ const org = await orgRepo.createOrg(owner, "Org6", "org6");
170
+ await memberRepo.addMember({
171
+ id: "m7",
172
+ orgId: org.id,
173
+ userId: owner,
174
+ role: "owner",
175
+ joinedAt: Date.now(),
176
+ });
177
+ await memberRepo.addMember({
178
+ id: "m8",
179
+ orgId: org.id,
180
+ userId: other,
181
+ role: "admin",
182
+ joinedAt: Date.now(),
183
+ });
184
+
185
+ await expect(svc.transferOwnership(org.id, other, owner)).rejects.toThrow();
186
+ });
187
+
188
+ it("throws NOT_FOUND if target member does not exist", async () => {
189
+ const { orgRepo, memberRepo } = await setup(db);
190
+ const svc = new OrgService(orgRepo, memberRepo, db);
191
+ const owner = "owner-7";
192
+ const org = await orgRepo.createOrg(owner, "Org7", "org7");
193
+ await memberRepo.addMember({
194
+ id: "m9",
195
+ orgId: org.id,
196
+ userId: owner,
197
+ role: "owner",
198
+ joinedAt: Date.now(),
199
+ });
200
+
201
+ await expect(svc.transferOwnership(org.id, owner, "nonexistent")).rejects.toThrow();
202
+ });
203
+ });
204
+
205
+ describe("createOrg", () => {
206
+ it("happy path — creates org and owner membership", async () => {
207
+ const owner = "owner-co1";
208
+ const result = await service.createOrg(owner, "My Org", "my-org-co1");
209
+ expect(result.id).toEqual(expect.any(String));
210
+ expect(result.name).toBe("My Org");
211
+ expect(result.slug).toBe("my-org-co1");
212
+
213
+ // Verify member row was created
214
+ const { memberRepo } = await setup(db);
215
+ const member = await memberRepo.findMember(result.id, owner);
216
+ expect(member?.role).toBe("owner");
217
+ });
218
+
219
+ it("throws CONFLICT (TRPCError) when slug is already taken", async () => {
220
+ await service.createOrg("user-a", "Org A", "taken-slug-co");
221
+ const err = await service.createOrg("user-b", "Org B", "taken-slug-co").catch((e) => e);
222
+ expect(err).toBeInstanceOf(TRPCError);
223
+ expect((err as TRPCError).code).toBe("CONFLICT");
224
+ });
225
+
226
+ it("throws CONFLICT (TRPCError) when DB unique constraint fires (race condition path)", async () => {
227
+ // Simulate the race: getBySlug returns null (both concurrent requests passed the check),
228
+ // but the DB insert fails with a unique constraint violation.
229
+ const { orgRepo, memberRepo } = await setup(db);
230
+ await orgRepo.createOrg("user-race", "Race Org", "race-slug-co");
231
+
232
+ // Bypass the service-level slug check to reach the DB insert directly
233
+ vi.spyOn(orgRepo, "getBySlug").mockResolvedValueOnce(null);
234
+ const racingSvc = new OrgService(orgRepo, memberRepo, db);
235
+
236
+ // This reaches the DB insert directly with the duplicate slug
237
+ const err = await racingSvc.createOrg("user-race2", "Race Org 2", "race-slug-co").catch((e) => e);
238
+ expect(err).toBeInstanceOf(TRPCError);
239
+ expect((err as TRPCError).code).toBe("CONFLICT");
240
+ });
241
+
242
+ it("throws BAD_REQUEST for invalid slug format", async () => {
243
+ const err = await service.createOrg("user-c", "Org C", "INVALID_SLUG").catch((e) => e);
244
+ expect(err).toBeInstanceOf(TRPCError);
245
+ expect((err as TRPCError).code).toBe("BAD_REQUEST");
246
+ });
247
+
248
+ it("logs error when compensating delete also fails", async () => {
249
+ const { logger: loggerModule } = await import("../config/logger.js");
250
+ const errorSpy = vi.spyOn(loggerModule, "error");
251
+
252
+ const { orgRepo, memberRepo } = await setup(db);
253
+ const failingMemberRepo = {
254
+ ...memberRepo,
255
+ addMember: async () => {
256
+ throw new Error("addMember boom");
257
+ },
258
+ } as unknown as typeof memberRepo;
259
+
260
+ const mockDb = {
261
+ delete: () => ({
262
+ where: () => Promise.reject(new Error("delete boom")),
263
+ }),
264
+ } as unknown as typeof db;
265
+
266
+ const svc = new OrgService(orgRepo, failingMemberRepo, mockDb);
267
+
268
+ await expect(svc.createOrg("user-e", "Org E", "double-fail")).rejects.toThrow("addMember boom");
269
+
270
+ expect(errorSpy).toHaveBeenCalledWith(
271
+ expect.stringContaining("Compensating org delete failed"),
272
+ expect.objectContaining({ err: expect.any(Error) }),
273
+ );
274
+
275
+ errorSpy.mockRestore();
276
+ });
277
+
278
+ it("partial failure — throws when addMember fails, org creation rolled back", async () => {
279
+ const { orgRepo, memberRepo } = await setup(db);
280
+ const failingMemberRepo = {
281
+ ...memberRepo,
282
+ addMember: async () => {
283
+ throw new Error("addMember boom");
284
+ },
285
+ } as unknown as typeof memberRepo;
286
+ const svc = new OrgService(orgRepo, failingMemberRepo, db);
287
+
288
+ await expect(svc.createOrg("user-d", "Org D", "partial-fail-co")).rejects.toThrow("addMember boom");
289
+ expect(await orgRepo.getBySlug("partial-fail-co")).toBeNull();
290
+ });
291
+ });
292
+
293
+ describe("validateSlug", () => {
294
+ it("accepts a valid slug", async () => {
295
+ expect(() => service.validateSlug("valid-slug-12")).not.toThrow();
296
+ });
297
+
298
+ it("rejects a slug with invalid characters", async () => {
299
+ expect(() => service.validateSlug("UPPERCASE")).toThrow();
300
+ });
301
+
302
+ it("rejects a slug that is too short", async () => {
303
+ expect(() => service.validateSlug("ab")).toThrow();
304
+ });
305
+ });
306
+
307
+ describe("getOrg", () => {
308
+ it("returns org with members and invites", async () => {
309
+ const { orgRepo, memberRepo } = await setup(db);
310
+ const svc = new OrgService(orgRepo, memberRepo, db);
311
+ const owner = "owner-g1";
312
+ const org = await orgRepo.createOrg(owner, "Get Org", "get-org");
313
+ await memberRepo.addMember({
314
+ id: "mg1",
315
+ orgId: org.id,
316
+ userId: owner,
317
+ role: "owner",
318
+ joinedAt: Date.now(),
319
+ });
320
+
321
+ const result = await svc.getOrg(org.id);
322
+ expect(result.id).toBe(org.id);
323
+ expect(result.members).toHaveLength(1);
324
+ expect(result.invites).toHaveLength(0);
325
+ });
326
+
327
+ it("throws NOT_FOUND for unknown org", async () => {
328
+ await expect(service.getOrg("nonexistent")).rejects.toThrow();
329
+ });
330
+ });
331
+
332
+ describe("deleteOrg", () => {
333
+ it("deletes org when called by owner", async () => {
334
+ const { orgRepo, memberRepo } = await setup(db);
335
+ const svc = new OrgService(orgRepo, memberRepo, db);
336
+ const owner = "owner-d1";
337
+ const org = await orgRepo.createOrg(owner, "Del Org", "del-org");
338
+ await memberRepo.addMember({
339
+ id: "md1",
340
+ orgId: org.id,
341
+ userId: owner,
342
+ role: "owner",
343
+ joinedAt: Date.now(),
344
+ });
345
+
346
+ await svc.deleteOrg(org.id, owner);
347
+
348
+ // Org should be gone
349
+ const found = await orgRepo.getById(org.id);
350
+ expect(found).toBeNull();
351
+ // Members should be gone
352
+ const members = await memberRepo.listMembers(org.id);
353
+ expect(members).toHaveLength(0);
354
+ });
355
+
356
+ it("deletes org with members and invites", async () => {
357
+ const { orgRepo, memberRepo } = await setup(db);
358
+ const svc = new OrgService(orgRepo, memberRepo, db);
359
+ const owner = "owner-d3";
360
+ const member = "member-d3";
361
+ const org = await orgRepo.createOrg(owner, "Del Org 3", "del-org-3");
362
+ await memberRepo.addMember({
363
+ id: "md4",
364
+ orgId: org.id,
365
+ userId: owner,
366
+ role: "owner",
367
+ joinedAt: Date.now(),
368
+ });
369
+ await memberRepo.addMember({
370
+ id: "md5",
371
+ orgId: org.id,
372
+ userId: member,
373
+ role: "member",
374
+ joinedAt: Date.now(),
375
+ });
376
+ await memberRepo.createInvite({
377
+ id: "inv-d3",
378
+ orgId: org.id,
379
+ email: "invited@example.com",
380
+ role: "member",
381
+ invitedBy: owner,
382
+ token: "tok-d3",
383
+ expiresAt: Date.now() + 86400000,
384
+ createdAt: Date.now(),
385
+ });
386
+
387
+ await svc.deleteOrg(org.id, owner);
388
+
389
+ expect(await orgRepo.getById(org.id)).toBeNull();
390
+ expect(await memberRepo.listMembers(org.id)).toHaveLength(0);
391
+ });
392
+
393
+ it("throws FORBIDDEN when non-owner tries to delete", async () => {
394
+ const { orgRepo, memberRepo } = await setup(db);
395
+ const svc = new OrgService(orgRepo, memberRepo, db);
396
+ const owner = "owner-d1b";
397
+ const other = "other-d1b";
398
+ const org = await orgRepo.createOrg(owner, "Del Org B", "del-org-b");
399
+ await memberRepo.addMember({
400
+ id: "md1b",
401
+ orgId: org.id,
402
+ userId: owner,
403
+ role: "owner",
404
+ joinedAt: Date.now(),
405
+ });
406
+ await memberRepo.addMember({
407
+ id: "md2b",
408
+ orgId: org.id,
409
+ userId: other,
410
+ role: "admin",
411
+ joinedAt: Date.now(),
412
+ });
413
+
414
+ await expect(svc.deleteOrg(org.id, other)).rejects.toThrow();
415
+ });
416
+
417
+ it("throws NOT_FOUND for nonexistent org", async () => {
418
+ await expect(service.deleteOrg("nonexistent", userId)).rejects.toThrow();
419
+ });
420
+ });
421
+
422
+ describe("inviteMember", () => {
423
+ it("creates an invite and returns it", async () => {
424
+ const { orgRepo, memberRepo } = await setup(db);
425
+ const svc = new OrgService(orgRepo, memberRepo, db);
426
+ const owner = "owner-i1";
427
+ const org = await orgRepo.createOrg(owner, "Invite Org", "invite-org");
428
+ await memberRepo.addMember({
429
+ id: "mi1",
430
+ orgId: org.id,
431
+ userId: owner,
432
+ role: "owner",
433
+ joinedAt: Date.now(),
434
+ });
435
+
436
+ const invite = await svc.inviteMember(org.id, owner, "new@example.com", "member");
437
+ expect(invite.email).toBe("new@example.com");
438
+ expect(invite.role).toBe("member");
439
+ });
440
+
441
+ it("throws FORBIDDEN when non-admin invites", async () => {
442
+ const { orgRepo, memberRepo } = await setup(db);
443
+ const svc = new OrgService(orgRepo, memberRepo, db);
444
+ const owner = "owner-i2";
445
+ const org = await orgRepo.createOrg(owner, "Org I2", "org-i2");
446
+ await memberRepo.addMember({
447
+ id: "mi2",
448
+ orgId: org.id,
449
+ userId: owner,
450
+ role: "owner",
451
+ joinedAt: Date.now(),
452
+ });
453
+
454
+ await expect(svc.inviteMember(org.id, "stranger", "x@x.com", "member")).rejects.toThrow();
455
+ });
456
+ });
457
+
458
+ describe("revokeInvite", () => {
459
+ it("revokes an existing invite", async () => {
460
+ const { orgRepo, memberRepo } = await setup(db);
461
+ const svc = new OrgService(orgRepo, memberRepo, db);
462
+ const owner = "owner-r1";
463
+ const org = await orgRepo.createOrg(owner, "Revoke Org", "revoke-org");
464
+ await memberRepo.addMember({
465
+ id: "mr1",
466
+ orgId: org.id,
467
+ userId: owner,
468
+ role: "owner",
469
+ joinedAt: Date.now(),
470
+ });
471
+ const invite = await svc.inviteMember(org.id, owner, "rev@example.com", "member");
472
+
473
+ await expect(svc.revokeInvite(org.id, owner, invite.id)).resolves.not.toThrow();
474
+ });
475
+
476
+ it("throws NOT_FOUND for unknown invite", async () => {
477
+ const { orgRepo, memberRepo } = await setup(db);
478
+ const svc = new OrgService(orgRepo, memberRepo, db);
479
+ const owner = "owner-r2";
480
+ const org = await orgRepo.createOrg(owner, "Revoke Org 2", "revoke-org-2");
481
+ await memberRepo.addMember({
482
+ id: "mr2",
483
+ orgId: org.id,
484
+ userId: owner,
485
+ role: "owner",
486
+ joinedAt: Date.now(),
487
+ });
488
+
489
+ await expect(svc.revokeInvite(org.id, owner, "nonexistent-invite")).rejects.toThrow();
490
+ });
491
+ });
492
+
493
+ describe("changeRole", () => {
494
+ it("changes a member's role", async () => {
495
+ const { orgRepo, memberRepo } = await setup(db);
496
+ const svc = new OrgService(orgRepo, memberRepo, db);
497
+ const owner = "owner-c1";
498
+ const member = "member-c1";
499
+ const org = await orgRepo.createOrg(owner, "Change Org", "change-org");
500
+ await memberRepo.addMember({
501
+ id: "mc1",
502
+ orgId: org.id,
503
+ userId: owner,
504
+ role: "owner",
505
+ joinedAt: Date.now(),
506
+ });
507
+ await memberRepo.addMember({
508
+ id: "mc2",
509
+ orgId: org.id,
510
+ userId: member,
511
+ role: "member",
512
+ joinedAt: Date.now(),
513
+ });
514
+
515
+ await expect(svc.changeRole(org.id, owner, member, "admin")).resolves.not.toThrow();
516
+ expect((await memberRepo.findMember(org.id, member))?.role).toBe("admin");
517
+ });
518
+
519
+ it("throws BAD_REQUEST when trying to change owner's role", async () => {
520
+ const { orgRepo, memberRepo } = await setup(db);
521
+ const svc = new OrgService(orgRepo, memberRepo, db);
522
+ const owner = "owner-c2";
523
+ const org = await orgRepo.createOrg(owner, "Change Org 2", "change-org-2");
524
+ await memberRepo.addMember({
525
+ id: "mc3",
526
+ orgId: org.id,
527
+ userId: owner,
528
+ role: "owner",
529
+ joinedAt: Date.now(),
530
+ });
531
+
532
+ await expect(svc.changeRole(org.id, owner, owner, "admin")).rejects.toThrow();
533
+ });
534
+
535
+ it("throws NOT_FOUND when target member does not exist", async () => {
536
+ const { orgRepo, memberRepo } = await setup(db);
537
+ const svc = new OrgService(orgRepo, memberRepo, db);
538
+ const owner = "owner-c3";
539
+ const org = await orgRepo.createOrg(owner, "Change Org 3", "change-org-3");
540
+ await memberRepo.addMember({
541
+ id: "mc4",
542
+ orgId: org.id,
543
+ userId: owner,
544
+ role: "owner",
545
+ joinedAt: Date.now(),
546
+ });
547
+
548
+ await expect(svc.changeRole(org.id, owner, "nobody", "admin")).rejects.toThrow();
549
+ });
550
+ });
551
+
552
+ describe("removeMember", () => {
553
+ it("removes a regular member", async () => {
554
+ const { orgRepo, memberRepo } = await setup(db);
555
+ const svc = new OrgService(orgRepo, memberRepo, db);
556
+ const owner = "owner-rm1";
557
+ const member = "member-rm1";
558
+ const org = await orgRepo.createOrg(owner, "Remove Org", "remove-org");
559
+ await memberRepo.addMember({
560
+ id: "mrm1",
561
+ orgId: org.id,
562
+ userId: owner,
563
+ role: "owner",
564
+ joinedAt: Date.now(),
565
+ });
566
+ await memberRepo.addMember({
567
+ id: "mrm2",
568
+ orgId: org.id,
569
+ userId: member,
570
+ role: "member",
571
+ joinedAt: Date.now(),
572
+ });
573
+
574
+ await expect(svc.removeMember(org.id, owner, member)).resolves.not.toThrow();
575
+ });
576
+
577
+ it("throws BAD_REQUEST when trying to remove the owner", async () => {
578
+ const { orgRepo, memberRepo } = await setup(db);
579
+ const svc = new OrgService(orgRepo, memberRepo, db);
580
+ const owner = "owner-rm2";
581
+ const org = await orgRepo.createOrg(owner, "Remove Org 2", "remove-org-2");
582
+ await memberRepo.addMember({
583
+ id: "mrm3",
584
+ orgId: org.id,
585
+ userId: owner,
586
+ role: "owner",
587
+ joinedAt: Date.now(),
588
+ });
589
+
590
+ await expect(svc.removeMember(org.id, owner, owner)).rejects.toThrow();
591
+ });
592
+
593
+ it("throws NOT_FOUND when target member does not exist", async () => {
594
+ const { orgRepo, memberRepo } = await setup(db);
595
+ const svc = new OrgService(orgRepo, memberRepo, db);
596
+ const owner = "owner-rm3";
597
+ const org = await orgRepo.createOrg(owner, "Remove Org 3", "remove-org-3");
598
+ await memberRepo.addMember({
599
+ id: "mrm4",
600
+ orgId: org.id,
601
+ userId: owner,
602
+ role: "owner",
603
+ joinedAt: Date.now(),
604
+ });
605
+
606
+ await expect(svc.removeMember(org.id, owner, "nobody")).rejects.toThrow();
607
+ });
608
+
609
+ it("allows removing an admin when the owner is still present (count > 1)", async () => {
610
+ const { orgRepo, memberRepo } = await setup(db);
611
+ const svc = new OrgService(orgRepo, memberRepo, db);
612
+ const owner = "owner-rm4";
613
+ const admin = "admin-rm4";
614
+ const org = await orgRepo.createOrg(owner, "Remove Org 4", "remove-org-4");
615
+ await memberRepo.addMember({
616
+ id: "mrm5",
617
+ orgId: org.id,
618
+ userId: owner,
619
+ role: "owner",
620
+ joinedAt: Date.now(),
621
+ });
622
+ await memberRepo.addMember({
623
+ id: "mrm6",
624
+ orgId: org.id,
625
+ userId: admin,
626
+ role: "admin",
627
+ joinedAt: Date.now(),
628
+ });
629
+
630
+ // Owner counts as admin/owner too (count = 2), so removing the admin succeeds
631
+ await expect(svc.removeMember(org.id, owner, admin)).resolves.not.toThrow();
632
+ });
633
+ });
634
+ });