@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,533 @@
1
+ import type { PGlite } from "@electric-sql/pglite";
2
+ import { eq } from "drizzle-orm";
3
+ import { afterAll, beforeAll, beforeEach, describe, expect, it } from "vitest";
4
+ import type { PlatformDb } from "../../db/index.js";
5
+ import { providerCredentials, tenantApiKeys } from "../../db/schema/index.js";
6
+ import { createTestDb, truncateAllTables } from "../../test/db.js";
7
+ import { decrypt, encrypt, generateInstanceKey } from "../encryption.js";
8
+ import type { EncryptedPayload } from "../types.js";
9
+ import { DrizzleCredentialRepository, DrizzleMigrationTenantKeyAccess } from "./credential-repository.js";
10
+ import { reEncryptAllCredentials } from "./key-rotation.js";
11
+ import { migratePlaintextCredentials } from "./migrate-plaintext.js";
12
+ import { auditCredentialEncryption } from "./migration-check.js";
13
+ import { CredentialVaultStore, getVaultEncryptionKey } from "./store.js";
14
+
15
+ // TOP OF FILE - shared across ALL describes
16
+ let pool: PGlite;
17
+ let db: PlatformDb;
18
+
19
+ beforeAll(async () => {
20
+ ({ db, pool } = await createTestDb());
21
+ });
22
+
23
+ afterAll(async () => {
24
+ await pool.close();
25
+ });
26
+
27
+ describe("auditCredentialEncryption", () => {
28
+ beforeEach(async () => {
29
+ await truncateAllTables(pool);
30
+ });
31
+
32
+ it("returns empty array when all credentials are properly encrypted", async () => {
33
+ const key = generateInstanceKey();
34
+ const encrypted = JSON.stringify(encrypt("sk-ant-test123", key));
35
+ await db.insert(providerCredentials).values({
36
+ id: "cred-1",
37
+ provider: "anthropic",
38
+ keyName: "Test",
39
+ encryptedValue: encrypted,
40
+ authType: "header",
41
+ createdBy: "admin",
42
+ });
43
+
44
+ const findings = await auditCredentialEncryption(db);
45
+ expect(findings).toEqual([]);
46
+ });
47
+
48
+ it("detects plaintext API keys", async () => {
49
+ await db.insert(providerCredentials).values({
50
+ id: "cred-1",
51
+ provider: "anthropic",
52
+ keyName: "Test",
53
+ encryptedValue: "sk-ant-api12345678901234567890",
54
+ authType: "header",
55
+ createdBy: "admin",
56
+ });
57
+
58
+ const findings = await auditCredentialEncryption(db);
59
+ expect(findings).toHaveLength(1);
60
+ expect(findings[0].table).toBe("provider_credentials");
61
+ expect(findings[0].rowId).toBe("cred-1");
62
+ });
63
+
64
+ it("detects malformed encrypted payloads (missing fields)", async () => {
65
+ await db.insert(providerCredentials).values({
66
+ id: "cred-1",
67
+ provider: "anthropic",
68
+ keyName: "Test",
69
+ encryptedValue: JSON.stringify({ iv: "aa" }), // missing authTag and ciphertext
70
+ authType: "header",
71
+ createdBy: "admin",
72
+ });
73
+
74
+ const findings = await auditCredentialEncryption(db);
75
+ expect(findings).toHaveLength(1);
76
+ });
77
+
78
+ it("returns empty array when table has no rows", async () => {
79
+ const findings = await auditCredentialEncryption(db);
80
+ expect(findings).toEqual([]);
81
+ });
82
+
83
+ it("handles missing tenant_api_keys gracefully (no plaintext tenant keys)", async () => {
84
+ // tenant_api_keys table exists but is empty — should not throw
85
+ const findings = await auditCredentialEncryption(db);
86
+ expect(findings).toEqual([]);
87
+ });
88
+
89
+ it("detects plaintext in tenant_api_keys when table exists", async () => {
90
+ await db.insert(tenantApiKeys).values({
91
+ id: "tk-1",
92
+ tenantId: "tenant-a",
93
+ provider: "anthropic",
94
+ label: "Test",
95
+ encryptedKey: "sk-ant-api12345678901234567890",
96
+ createdAt: Date.now(),
97
+ updatedAt: Date.now(),
98
+ });
99
+
100
+ const findings = await auditCredentialEncryption(db);
101
+ expect(findings).toHaveLength(1);
102
+ expect(findings[0].table).toBe("tenant_api_keys");
103
+ expect(findings[0].rowId).toBe("tk-1");
104
+ });
105
+ });
106
+
107
+ describe("migratePlaintextCredentials", () => {
108
+ let vaultKey: Buffer;
109
+ let credRepo: DrizzleCredentialRepository;
110
+ let tenantAccess: DrizzleMigrationTenantKeyAccess;
111
+
112
+ beforeEach(async () => {
113
+ await truncateAllTables(pool);
114
+ vaultKey = generateInstanceKey();
115
+ credRepo = new DrizzleCredentialRepository(db);
116
+ tenantAccess = new DrizzleMigrationTenantKeyAccess(db);
117
+ });
118
+
119
+ it("skips already-encrypted rows", async () => {
120
+ const encrypted = JSON.stringify(encrypt("sk-ant-test", vaultKey));
121
+ await db.insert(providerCredentials).values({
122
+ id: "cred-1",
123
+ provider: "anthropic",
124
+ keyName: "Test",
125
+ encryptedValue: encrypted,
126
+ authType: "header",
127
+ createdBy: "admin",
128
+ });
129
+
130
+ const results = await migratePlaintextCredentials(credRepo, vaultKey, () => vaultKey, tenantAccess);
131
+ expect(results[0].migratedCount).toBe(0);
132
+ expect(results[0].errors).toHaveLength(0);
133
+ });
134
+
135
+ it("encrypts plaintext rows", async () => {
136
+ await db.insert(providerCredentials).values({
137
+ id: "cred-1",
138
+ provider: "anthropic",
139
+ keyName: "Test",
140
+ encryptedValue: "sk-ant-plaintext-key-1234567890",
141
+ authType: "header",
142
+ createdBy: "admin",
143
+ });
144
+
145
+ const results = await migratePlaintextCredentials(credRepo, vaultKey, () => vaultKey, tenantAccess);
146
+ expect(results[0].migratedCount).toBe(1);
147
+
148
+ // Verify the value is now encrypted JSON
149
+ const rows = await db
150
+ .select({ encryptedValue: providerCredentials.encryptedValue })
151
+ .from(providerCredentials)
152
+ .where(eq(providerCredentials.id, "cred-1"));
153
+ const parsed = JSON.parse(rows[0].encryptedValue);
154
+ expect(parsed).toHaveProperty("iv");
155
+ expect(parsed).toHaveProperty("authTag");
156
+ expect(parsed).toHaveProperty("ciphertext");
157
+ });
158
+
159
+ it("handles empty provider_credentials gracefully", async () => {
160
+ // Should not throw when table is empty
161
+ const results = await migratePlaintextCredentials(credRepo, vaultKey, () => vaultKey, tenantAccess);
162
+ expect(results[0].table).toBe("provider_credentials");
163
+ });
164
+
165
+ it("migrates tenant_api_keys when table exists", async () => {
166
+ await db.insert(tenantApiKeys).values({
167
+ id: "tk-1",
168
+ tenantId: "tenant-a",
169
+ provider: "anthropic",
170
+ label: "Test",
171
+ encryptedKey: "sk-ant-plaintext-key-1234567890",
172
+ createdAt: Date.now(),
173
+ updatedAt: Date.now(),
174
+ });
175
+
176
+ const results = await migratePlaintextCredentials(credRepo, vaultKey, () => vaultKey, tenantAccess);
177
+ const tenantResult = results.find((r) => r.table === "tenant_api_keys");
178
+ expect(tenantResult).not.toBeNull();
179
+ expect(tenantResult?.migratedCount).toBe(1);
180
+ });
181
+
182
+ it("returns table name in results", async () => {
183
+ const results = await migratePlaintextCredentials(credRepo, vaultKey, () => vaultKey, tenantAccess);
184
+ expect(results[0].table).toBe("provider_credentials");
185
+ });
186
+
187
+ it("migrated row is subsequently detected as already-encrypted", async () => {
188
+ await db.insert(providerCredentials).values({
189
+ id: "cred-1",
190
+ provider: "anthropic",
191
+ keyName: "Test",
192
+ encryptedValue: "sk-ant-plaintext-key-1234567890",
193
+ authType: "header",
194
+ createdBy: "admin",
195
+ });
196
+
197
+ // First migration: should encrypt
198
+ const results1 = await migratePlaintextCredentials(credRepo, vaultKey, () => vaultKey, tenantAccess);
199
+ expect(results1[0].migratedCount).toBe(1);
200
+
201
+ // Second migration: row is now encrypted, should be skipped
202
+ const results2 = await migratePlaintextCredentials(credRepo, vaultKey, () => vaultKey, tenantAccess);
203
+ expect(results2[0].migratedCount).toBe(0);
204
+ });
205
+
206
+ it("migrating already-migrated credential is a no-op (value unchanged)", async () => {
207
+ await db.insert(providerCredentials).values({
208
+ id: "cred-1",
209
+ provider: "anthropic",
210
+ keyName: "Test",
211
+ encryptedValue: "sk-ant-plaintext-key-1234567890",
212
+ authType: "header",
213
+ createdBy: "admin",
214
+ });
215
+
216
+ // Migrate once
217
+ await migratePlaintextCredentials(credRepo, vaultKey, () => vaultKey, tenantAccess);
218
+
219
+ // Capture the encrypted value
220
+ const rowsBefore = await db
221
+ .select({ encryptedValue: providerCredentials.encryptedValue })
222
+ .from(providerCredentials)
223
+ .where(eq(providerCredentials.id, "cred-1"));
224
+ const valueBefore = rowsBefore[0].encryptedValue;
225
+
226
+ // Migrate again — should be no-op
227
+ const results = await migratePlaintextCredentials(credRepo, vaultKey, () => vaultKey, tenantAccess);
228
+ expect(results[0].migratedCount).toBe(0);
229
+
230
+ // Value should be identical (not re-encrypted)
231
+ const rowsAfter = await db
232
+ .select({ encryptedValue: providerCredentials.encryptedValue })
233
+ .from(providerCredentials)
234
+ .where(eq(providerCredentials.id, "cred-1"));
235
+ expect(rowsAfter[0].encryptedValue).toBe(valueBefore);
236
+ });
237
+
238
+ it("batch migrates 100 plaintext credentials with no plaintext remaining", async () => {
239
+ // Insert 100 plaintext credentials
240
+ for (let i = 0; i < 100; i++) {
241
+ await db.insert(providerCredentials).values({
242
+ id: `cred-${i}`,
243
+ provider: "anthropic",
244
+ keyName: `Key-${i}`,
245
+ encryptedValue: `sk-ant-plaintext-batch-key-${String(i).padStart(4, "0")}`,
246
+ authType: "header",
247
+ createdBy: "admin",
248
+ });
249
+ }
250
+
251
+ const results = await migratePlaintextCredentials(credRepo, vaultKey, () => vaultKey, tenantAccess);
252
+ expect(results[0].migratedCount).toBe(100);
253
+ expect(results[0].errors).toHaveLength(0);
254
+
255
+ // Verify no plaintext remains
256
+ const allRows = await db
257
+ .select({ id: providerCredentials.id, encryptedValue: providerCredentials.encryptedValue })
258
+ .from(providerCredentials);
259
+ expect(allRows).toHaveLength(100);
260
+
261
+ for (const row of allRows) {
262
+ const parsed = JSON.parse(row.encryptedValue);
263
+ expect(parsed).toHaveProperty("iv");
264
+ expect(parsed).toHaveProperty("authTag");
265
+ expect(parsed).toHaveProperty("ciphertext");
266
+ // Ensure the raw value does not contain any plaintext key pattern
267
+ expect(row.encryptedValue).not.toMatch(/sk-ant-plaintext/);
268
+ }
269
+ });
270
+ });
271
+
272
+ describe("credential vault migration path", () => {
273
+ let vaultKey: Buffer;
274
+ let credRepo: DrizzleCredentialRepository;
275
+ let tenantAccess: DrizzleMigrationTenantKeyAccess;
276
+
277
+ beforeEach(async () => {
278
+ await truncateAllTables(pool);
279
+ vaultKey = generateInstanceKey();
280
+ credRepo = new DrizzleCredentialRepository(db);
281
+ tenantAccess = new DrizzleMigrationTenantKeyAccess(db);
282
+ });
283
+
284
+ it("pre-migration plaintext credential is readable after migratePlaintextCredentials", async () => {
285
+ // Insert a plaintext credential (simulating legacy state)
286
+ await db.insert(providerCredentials).values({
287
+ id: "cred-legacy",
288
+ provider: "anthropic",
289
+ keyName: "Legacy Key",
290
+ encryptedValue: "sk-ant-legacy-plaintext-key-999",
291
+ authType: "header",
292
+ authHeader: "x-api-key",
293
+ createdBy: "admin",
294
+ });
295
+
296
+ // Pre-migration: raw value is plaintext
297
+ const rowsBefore = await db
298
+ .select({ encryptedValue: providerCredentials.encryptedValue })
299
+ .from(providerCredentials)
300
+ .where(eq(providerCredentials.id, "cred-legacy"));
301
+ expect(rowsBefore[0].encryptedValue).toBe("sk-ant-legacy-plaintext-key-999");
302
+
303
+ // Migrate
304
+ const results = await migratePlaintextCredentials(credRepo, vaultKey, () => vaultKey, tenantAccess);
305
+ expect(results[0].migratedCount).toBe(1);
306
+ expect(results[0].errors).toHaveLength(0);
307
+
308
+ // Post-migration: decrypt through CredentialVaultStore
309
+ const repo = new DrizzleCredentialRepository(db);
310
+ const store = new CredentialVaultStore(repo, vaultKey);
311
+ const decrypted = await store.decrypt("cred-legacy");
312
+ expect(decrypted).not.toBeNull();
313
+ const cred = decrypted as NonNullable<typeof decrypted>;
314
+ expect(cred.plaintextKey).toBe("sk-ant-legacy-plaintext-key-999");
315
+ expect(cred.provider).toBe("anthropic");
316
+ expect(cred.keyName).toBe("Legacy Key");
317
+ expect(cred.authType).toBe("header");
318
+ expect(cred.authHeader).toBe("x-api-key");
319
+ });
320
+
321
+ it("post-migration credential metadata is fully preserved", async () => {
322
+ await db.insert(providerCredentials).values({
323
+ id: "cred-meta",
324
+ provider: "openai",
325
+ keyName: "Metadata Test",
326
+ encryptedValue: "sk-openai-meta-test-key-12345",
327
+ authType: "bearer",
328
+ authHeader: null,
329
+ createdBy: "admin-user",
330
+ });
331
+
332
+ await migratePlaintextCredentials(credRepo, vaultKey, () => vaultKey, tenantAccess);
333
+
334
+ const repo = new DrizzleCredentialRepository(db);
335
+ const store = new CredentialVaultStore(repo, vaultKey);
336
+
337
+ // Verify summary metadata is intact
338
+ const summary = await store.getById("cred-meta");
339
+ expect(summary).not.toBeNull();
340
+ const s = summary as NonNullable<typeof summary>;
341
+ expect(s.provider).toBe("openai");
342
+ expect(s.keyName).toBe("Metadata Test");
343
+ expect(s.authType).toBe("bearer");
344
+ expect(s.authHeader).toBeNull();
345
+ expect(s.createdBy).toBe("admin-user");
346
+ expect(s.isActive).toBe(true);
347
+ });
348
+
349
+ it("migration idempotency: running twice does not corrupt and encrypted value is unchanged", async () => {
350
+ await db.insert(providerCredentials).values({
351
+ id: "cred-idem",
352
+ provider: "anthropic",
353
+ keyName: "Idempotent",
354
+ encryptedValue: "sk-ant-idempotent-test-key-000",
355
+ authType: "header",
356
+ createdBy: "admin",
357
+ });
358
+
359
+ // First migration
360
+ const r1 = await migratePlaintextCredentials(credRepo, vaultKey, () => vaultKey, tenantAccess);
361
+ expect(r1[0].migratedCount).toBe(1);
362
+
363
+ // Capture encrypted value
364
+ const rowsAfterFirst = await db
365
+ .select({ encryptedValue: providerCredentials.encryptedValue })
366
+ .from(providerCredentials)
367
+ .where(eq(providerCredentials.id, "cred-idem"));
368
+ const encryptedAfterFirst = rowsAfterFirst[0].encryptedValue;
369
+
370
+ // Second migration (should be no-op)
371
+ const r2 = await migratePlaintextCredentials(credRepo, vaultKey, () => vaultKey, tenantAccess);
372
+ expect(r2[0].migratedCount).toBe(0);
373
+
374
+ // Encrypted value must be byte-identical
375
+ const rowsAfterSecond = await db
376
+ .select({ encryptedValue: providerCredentials.encryptedValue })
377
+ .from(providerCredentials)
378
+ .where(eq(providerCredentials.id, "cred-idem"));
379
+ expect(rowsAfterSecond[0].encryptedValue).toBe(encryptedAfterFirst);
380
+
381
+ // Still decryptable through the store
382
+ const repo = new DrizzleCredentialRepository(db);
383
+ const store = new CredentialVaultStore(repo, vaultKey);
384
+ const decrypted = await store.decrypt("cred-idem");
385
+ expect((decrypted as NonNullable<typeof decrypted>).plaintextKey).toBe("sk-ant-idempotent-test-key-000");
386
+ });
387
+
388
+ it("partial migration failure: valid rows are migrated, invalid rows produce errors, valid rows remain readable", async () => {
389
+ // Row 1: valid plaintext
390
+ await db.insert(providerCredentials).values({
391
+ id: "cred-valid",
392
+ provider: "anthropic",
393
+ keyName: "Valid",
394
+ encryptedValue: "sk-ant-valid-plaintext-key-111",
395
+ authType: "header",
396
+ createdBy: "admin",
397
+ });
398
+
399
+ // Row 2: already encrypted (should be skipped, not an error)
400
+ const alreadyEncrypted = JSON.stringify(encrypt("sk-ant-already-encrypted", vaultKey));
401
+ await db.insert(providerCredentials).values({
402
+ id: "cred-encrypted",
403
+ provider: "openai",
404
+ keyName: "Already Encrypted",
405
+ encryptedValue: alreadyEncrypted,
406
+ authType: "bearer",
407
+ createdBy: "admin",
408
+ });
409
+
410
+ const results = await migratePlaintextCredentials(credRepo, vaultKey, () => vaultKey, tenantAccess);
411
+ expect(results[0].migratedCount).toBe(1); // only the plaintext row
412
+ expect(results[0].errors).toHaveLength(0);
413
+
414
+ // Both rows are now readable through the store
415
+ const repo = new DrizzleCredentialRepository(db);
416
+ const store = new CredentialVaultStore(repo, vaultKey);
417
+
418
+ const d1 = await store.decrypt("cred-valid");
419
+ expect((d1 as NonNullable<typeof d1>).plaintextKey).toBe("sk-ant-valid-plaintext-key-111");
420
+
421
+ const d2 = await store.decrypt("cred-encrypted");
422
+ expect((d2 as NonNullable<typeof d2>).plaintextKey).toBe("sk-ant-already-encrypted");
423
+ });
424
+
425
+ it("key rotation after plaintext migration: full chain works", async () => {
426
+ const oldSecret = "old-platform-secret-rotation-test";
427
+ const newSecret = "new-platform-secret-rotation-test";
428
+ const oldKey = getVaultEncryptionKey(oldSecret);
429
+
430
+ // Start with plaintext
431
+ await db.insert(providerCredentials).values({
432
+ id: "cred-chain",
433
+ provider: "anthropic",
434
+ keyName: "Chain Test",
435
+ encryptedValue: "sk-ant-chain-test-key-xyz",
436
+ authType: "header",
437
+ authHeader: "x-api-key",
438
+ createdBy: "admin",
439
+ });
440
+
441
+ // Step 1: Migrate plaintext to encrypted with old key
442
+ const oldCredRepo = new DrizzleCredentialRepository(db);
443
+ const migrateResults = await migratePlaintextCredentials(oldCredRepo, oldKey, () => oldKey);
444
+ expect(migrateResults[0].migratedCount).toBe(1);
445
+
446
+ // Verify readable with old key
447
+ const repo1 = new DrizzleCredentialRepository(db);
448
+ const store1 = new CredentialVaultStore(repo1, oldKey);
449
+ const d1 = await store1.decrypt("cred-chain");
450
+ expect((d1 as NonNullable<typeof d1>).plaintextKey).toBe("sk-ant-chain-test-key-xyz");
451
+
452
+ // Step 2: Rotate keys from old secret to new secret
453
+ const rotCredAccess = new DrizzleCredentialRepository(db);
454
+ const rotTenantKeyAccess = new DrizzleMigrationTenantKeyAccess(db);
455
+ const rotResult = await reEncryptAllCredentials(rotCredAccess, rotTenantKeyAccess, oldSecret, newSecret);
456
+ expect(rotResult.providerCredentials.migrated).toBe(1);
457
+ expect(rotResult.providerCredentials.errors).toHaveLength(0);
458
+
459
+ // Verify readable with new key
460
+ const newKey = getVaultEncryptionKey(newSecret);
461
+ const repo2 = new DrizzleCredentialRepository(db);
462
+ const store2 = new CredentialVaultStore(repo2, newKey);
463
+ const d2 = await store2.decrypt("cred-chain");
464
+ const d2cred = d2 as NonNullable<typeof d2>;
465
+ expect(d2cred.plaintextKey).toBe("sk-ant-chain-test-key-xyz");
466
+ expect(d2cred.provider).toBe("anthropic");
467
+ expect(d2cred.authHeader).toBe("x-api-key");
468
+
469
+ // Old key can no longer decrypt
470
+ const rowRaw = await db
471
+ .select({ encryptedValue: providerCredentials.encryptedValue })
472
+ .from(providerCredentials)
473
+ .where(eq(providerCredentials.id, "cred-chain"));
474
+ const payload: EncryptedPayload = JSON.parse(rowRaw[0].encryptedValue);
475
+ expect(() => decrypt(payload, oldKey)).toThrow();
476
+ });
477
+
478
+ it("tenant_api_keys migration: plaintext tenant key is readable post-migration", async () => {
479
+ await db.insert(tenantApiKeys).values({
480
+ id: "tk-migrate",
481
+ tenantId: "tenant-test",
482
+ provider: "anthropic",
483
+ label: "Tenant Migration Test",
484
+ encryptedKey: "sk-ant-tenant-plaintext-key-888",
485
+ createdAt: Date.now(),
486
+ updatedAt: Date.now(),
487
+ });
488
+
489
+ const tenantKeyDeriver = (_tenantId: string) => vaultKey;
490
+ const localTenantAccess = new DrizzleMigrationTenantKeyAccess(db);
491
+ const results = await migratePlaintextCredentials(credRepo, vaultKey, tenantKeyDeriver, localTenantAccess);
492
+ const tenantResult = results.find((r) => r.table === "tenant_api_keys");
493
+ expect(tenantResult).not.toBeNull();
494
+ const tr = tenantResult as NonNullable<typeof tenantResult>;
495
+ expect(tr.migratedCount).toBe(1);
496
+ expect(tr.errors).toHaveLength(0);
497
+
498
+ // Verify the encrypted value is valid
499
+ const rows = await db
500
+ .select({ encryptedKey: tenantApiKeys.encryptedKey })
501
+ .from(tenantApiKeys)
502
+ .where(eq(tenantApiKeys.id, "tk-migrate"));
503
+ const payload: EncryptedPayload = JSON.parse(rows[0].encryptedKey);
504
+ const decrypted = decrypt(payload, vaultKey);
505
+ expect(decrypted).toBe("sk-ant-tenant-plaintext-key-888");
506
+ });
507
+
508
+ it("audit after migration: no plaintext findings remain", async () => {
509
+ // Insert 3 plaintext credentials
510
+ for (let i = 0; i < 3; i++) {
511
+ await db.insert(providerCredentials).values({
512
+ id: `cred-audit-${i}`,
513
+ provider: "anthropic",
514
+ keyName: `Audit Key ${i}`,
515
+ encryptedValue: `sk-ant-audit-plaintext-key-${i}`,
516
+ authType: "header",
517
+ createdBy: "admin",
518
+ });
519
+ }
520
+
521
+ // Pre-migration audit should find 3 plaintext entries
522
+ const findingsBefore = await auditCredentialEncryption(db);
523
+ expect(findingsBefore).toHaveLength(3);
524
+
525
+ // Migrate
526
+ const results = await migratePlaintextCredentials(credRepo, vaultKey, () => vaultKey, tenantAccess);
527
+ expect(results[0].migratedCount).toBe(3);
528
+
529
+ // Post-migration audit should find 0 plaintext entries
530
+ const findingsAfter = await auditCredentialEncryption(db);
531
+ expect(findingsAfter).toHaveLength(0);
532
+ });
533
+ });
@@ -0,0 +1,88 @@
1
+ import type { PlatformDb } from "../../db/index.js";
2
+ import { providerCredentials, tenantApiKeys } from "../../db/schema/index.js";
3
+ import { scanForKeyLeaks } from "../key-audit.js";
4
+
5
+ /**
6
+ * Scan all credential columns in the database for plaintext API key patterns.
7
+ * Returns an array of findings. Empty array = all clear.
8
+ *
9
+ * This is a safety net, not a migration — the platform was designed encrypted-first.
10
+ * Run as part of deployment validation or as a periodic security check.
11
+ */
12
+ export interface PlaintextFinding {
13
+ table: string;
14
+ column: string;
15
+ rowId: string;
16
+ provider: string;
17
+ }
18
+
19
+ export async function auditCredentialEncryption(db: PlatformDb): Promise<PlaintextFinding[]> {
20
+ const findings: PlaintextFinding[] = [];
21
+
22
+ // Check provider_credentials.encrypted_value
23
+ const providerRows = await db
24
+ .select({ id: providerCredentials.id, encryptedValue: providerCredentials.encryptedValue })
25
+ .from(providerCredentials);
26
+
27
+ for (const row of providerRows) {
28
+ // A properly encrypted value should be a JSON object with iv/authTag/ciphertext
29
+ try {
30
+ const parsed = JSON.parse(row.encryptedValue);
31
+ if (!parsed.iv || !parsed.authTag || !parsed.ciphertext) {
32
+ findings.push({
33
+ table: "provider_credentials",
34
+ column: "encrypted_value",
35
+ rowId: row.id,
36
+ provider: "unknown",
37
+ });
38
+ }
39
+ } catch {
40
+ // Not valid JSON = likely plaintext
41
+ const leaks = scanForKeyLeaks(row.encryptedValue);
42
+ if (leaks.length > 0 || row.encryptedValue.trim().length > 0) {
43
+ findings.push({
44
+ table: "provider_credentials",
45
+ column: "encrypted_value",
46
+ rowId: row.id,
47
+ provider: leaks[0]?.provider ?? "unknown",
48
+ });
49
+ }
50
+ }
51
+ }
52
+
53
+ // Check tenant_api_keys.encrypted_key (if table exists)
54
+ try {
55
+ const tenantRows = await db
56
+ .select({ id: tenantApiKeys.id, encryptedKey: tenantApiKeys.encryptedKey })
57
+ .from(tenantApiKeys);
58
+
59
+ for (const row of tenantRows) {
60
+ try {
61
+ const parsed = JSON.parse(row.encryptedKey);
62
+ if (!parsed.iv || !parsed.authTag || !parsed.ciphertext) {
63
+ findings.push({
64
+ table: "tenant_api_keys",
65
+ column: "encrypted_key",
66
+ rowId: row.id,
67
+ provider: "unknown",
68
+ });
69
+ }
70
+ } catch {
71
+ const leaks = scanForKeyLeaks(row.encryptedKey);
72
+ if (leaks.length > 0 || row.encryptedKey.trim().length > 0) {
73
+ findings.push({
74
+ table: "tenant_api_keys",
75
+ column: "encrypted_key",
76
+ rowId: row.id,
77
+ provider: leaks[0]?.provider ?? "unknown",
78
+ });
79
+ }
80
+ }
81
+ }
82
+ } catch (err) {
83
+ if (!(err instanceof Error && err.message.includes("no such table"))) throw err;
84
+ // Table doesn't exist yet — that's fine
85
+ }
86
+
87
+ return findings;
88
+ }