@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,124 @@
1
+ import { appendFileSync, existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
2
+ import { dirname } from "node:path";
3
+ /**
4
+ * Write-Ahead Log for meter events.
5
+ *
6
+ * Provides durable, fail-closed event persistence. Events are written
7
+ * to disk BEFORE being buffered, ensuring no event is lost even if
8
+ * the process crashes before flush completes.
9
+ */
10
+ export class MeterWAL {
11
+ walPath;
12
+ lock = Promise.resolve();
13
+ async withLock(fn) {
14
+ const prev = this.lock;
15
+ let resolve;
16
+ this.lock = new Promise((r) => {
17
+ resolve = r;
18
+ });
19
+ try {
20
+ await prev;
21
+ return await fn();
22
+ }
23
+ finally {
24
+ resolve();
25
+ }
26
+ }
27
+ constructor(walPath) {
28
+ this.walPath = walPath;
29
+ this.ensureDir();
30
+ }
31
+ ensureDir() {
32
+ const dir = dirname(this.walPath);
33
+ if (!existsSync(dir)) {
34
+ mkdirSync(dir, { recursive: true });
35
+ }
36
+ }
37
+ /**
38
+ * Append an event to the WAL. appendFileSync is atomic on POSIX (O_APPEND),
39
+ * so no mutex is needed here. Returns the event with a generated ID.
40
+ */
41
+ append(event) {
42
+ const eventWithId = {
43
+ ...event,
44
+ id: event.id ?? crypto.randomUUID(),
45
+ };
46
+ const line = `${JSON.stringify(eventWithId)}\n`;
47
+ appendFileSync(this.walPath, line, { encoding: "utf8", flag: "a" });
48
+ return eventWithId;
49
+ }
50
+ /**
51
+ * Read all events from the WAL. Returns events in the order they were written.
52
+ * Skips malformed lines (defensive against incomplete writes).
53
+ */
54
+ readAll() {
55
+ if (!existsSync(this.walPath)) {
56
+ return [];
57
+ }
58
+ const content = readFileSync(this.walPath, "utf8");
59
+ if (!content.trim()) {
60
+ return [];
61
+ }
62
+ const events = [];
63
+ for (const line of content.trim().split("\n")) {
64
+ try {
65
+ events.push(JSON.parse(line));
66
+ }
67
+ catch {
68
+ // Skip malformed lines (e.g., from incomplete writes).
69
+ }
70
+ }
71
+ return events;
72
+ }
73
+ /**
74
+ * Remove specific event IDs from the WAL. This is done by rewriting
75
+ * the entire file without the specified events. Mutex-guarded.
76
+ */
77
+ async remove(eventIds) {
78
+ return this.withLock(() => {
79
+ if (!existsSync(this.walPath) || eventIds.size === 0) {
80
+ return;
81
+ }
82
+ const events = this.readAll();
83
+ const filtered = events.filter((e) => !eventIds.has(e.id));
84
+ if (filtered.length === 0) {
85
+ // All events removed — delete the WAL file.
86
+ this._clear();
87
+ }
88
+ else {
89
+ // Rewrite the WAL with remaining events.
90
+ const content = `${filtered.map((e) => JSON.stringify(e)).join("\n")}\n`;
91
+ writeFileSync(this.walPath, content, { encoding: "utf8" });
92
+ }
93
+ });
94
+ }
95
+ _clear() {
96
+ if (existsSync(this.walPath)) {
97
+ unlinkSync(this.walPath);
98
+ }
99
+ }
100
+ /**
101
+ * Clear the entire WAL (typically after successful flush). Mutex-guarded.
102
+ */
103
+ async clear() {
104
+ return this.withLock(() => {
105
+ this._clear();
106
+ });
107
+ }
108
+ /**
109
+ * Check if the WAL is empty.
110
+ */
111
+ isEmpty() {
112
+ if (!existsSync(this.walPath)) {
113
+ return true;
114
+ }
115
+ const content = readFileSync(this.walPath, "utf8");
116
+ return !content.trim();
117
+ }
118
+ /**
119
+ * Get the number of events in the WAL.
120
+ */
121
+ count() {
122
+ return this.readAll().length;
123
+ }
124
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,175 @@
1
+ import { existsSync, mkdirSync, rmSync } from "node:fs";
2
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
3
+ import { Credit } from "../credits/credit.js";
4
+ import { MeterDLQ } from "./dlq.js";
5
+ import { MeterWAL } from "./wal.js";
6
+ const TEST_DIR = "/tmp/wopr-wal-test";
7
+ const TEST_WAL_PATH = `${TEST_DIR}/test-wal.jsonl`;
8
+ const TEST_DLQ_PATH = `${TEST_DIR}/test-dlq.jsonl`;
9
+ function makeEvent(overrides = {}) {
10
+ return {
11
+ tenant: "tenant-1",
12
+ cost: Credit.fromDollars(0.001),
13
+ charge: Credit.fromDollars(0.002),
14
+ capability: "embeddings",
15
+ provider: "openai",
16
+ timestamp: Date.now(),
17
+ ...overrides,
18
+ };
19
+ }
20
+ describe("MeterWAL", () => {
21
+ let wal;
22
+ beforeEach(() => {
23
+ if (existsSync(TEST_DIR)) {
24
+ rmSync(TEST_DIR, { recursive: true });
25
+ }
26
+ mkdirSync(TEST_DIR, { recursive: true });
27
+ wal = new MeterWAL(TEST_WAL_PATH);
28
+ });
29
+ afterEach(() => {
30
+ if (existsSync(TEST_DIR)) {
31
+ rmSync(TEST_DIR, { recursive: true });
32
+ }
33
+ });
34
+ it("appends events to the WAL", async () => {
35
+ const event = makeEvent();
36
+ const result = await wal.append(event);
37
+ expect(result.id).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i);
38
+ expect(result.tenant).toBe("tenant-1");
39
+ expect(wal.count()).toBe(1);
40
+ });
41
+ it("preserves event ID if provided", async () => {
42
+ const event = { ...makeEvent(), id: "custom-id-123" };
43
+ const result = await wal.append(event);
44
+ expect(result.id).toBe("custom-id-123");
45
+ });
46
+ it("generates UUID if ID not provided", async () => {
47
+ const event = makeEvent();
48
+ const result = await wal.append(event);
49
+ expect(result.id).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i);
50
+ });
51
+ it("readAll returns events in order", async () => {
52
+ await wal.append(makeEvent({ tenant: "t-1" }));
53
+ await wal.append(makeEvent({ tenant: "t-2" }));
54
+ await wal.append(makeEvent({ tenant: "t-3" }));
55
+ const events = wal.readAll();
56
+ expect(events).toHaveLength(3);
57
+ expect(events[0].tenant).toBe("t-1");
58
+ expect(events[1].tenant).toBe("t-2");
59
+ expect(events[2].tenant).toBe("t-3");
60
+ });
61
+ it("readAll returns empty array for empty WAL", () => {
62
+ const events = wal.readAll();
63
+ expect(events).toEqual([]);
64
+ });
65
+ it("remove deletes specific events", async () => {
66
+ const e1 = await wal.append(makeEvent({ tenant: "t-1" }));
67
+ const e2 = await wal.append(makeEvent({ tenant: "t-2" }));
68
+ const e3 = await wal.append(makeEvent({ tenant: "t-3" }));
69
+ await wal.remove(new Set([e2.id]));
70
+ const events = wal.readAll();
71
+ expect(events).toHaveLength(2);
72
+ expect(events[0].id).toBe(e1.id);
73
+ expect(events[1].id).toBe(e3.id);
74
+ });
75
+ it("remove with empty set does nothing", async () => {
76
+ await wal.append(makeEvent());
77
+ await wal.remove(new Set());
78
+ expect(wal.count()).toBe(1);
79
+ });
80
+ it("remove all events clears the WAL file", async () => {
81
+ const e1 = await wal.append(makeEvent());
82
+ const e2 = await wal.append(makeEvent());
83
+ await wal.remove(new Set([e1.id, e2.id]));
84
+ expect(wal.isEmpty()).toBe(true);
85
+ expect(existsSync(TEST_WAL_PATH)).toBe(false);
86
+ });
87
+ it("clear removes the WAL file", async () => {
88
+ await wal.append(makeEvent());
89
+ expect(existsSync(TEST_WAL_PATH)).toBe(true);
90
+ await wal.clear();
91
+ expect(existsSync(TEST_WAL_PATH)).toBe(false);
92
+ expect(wal.isEmpty()).toBe(true);
93
+ });
94
+ it("isEmpty returns true for non-existent WAL", () => {
95
+ expect(wal.isEmpty()).toBe(true);
96
+ });
97
+ it("isEmpty returns false after append", async () => {
98
+ await wal.append(makeEvent());
99
+ expect(wal.isEmpty()).toBe(false);
100
+ });
101
+ it("count returns 0 for empty WAL", () => {
102
+ expect(wal.count()).toBe(0);
103
+ });
104
+ it("handles multiple appends without data loss", async () => {
105
+ for (let i = 0; i < 100; i++) {
106
+ await wal.append(makeEvent({ tenant: `t-${i}` }));
107
+ }
108
+ expect(wal.count()).toBe(100);
109
+ });
110
+ it("concurrent append during remove does not lose events", async () => {
111
+ // Seed 3 events
112
+ const e1 = await wal.append(makeEvent({ tenant: "t-1" }));
113
+ const e2 = await wal.append(makeEvent({ tenant: "t-2" }));
114
+ await wal.append(makeEvent({ tenant: "t-3" }));
115
+ // Remove e1 and e2, while concurrently appending e4
116
+ const removePromise = wal.remove(new Set([e1.id, e2.id]));
117
+ const appendPromise = wal.append(makeEvent({ tenant: "t-4" }));
118
+ await Promise.all([removePromise, appendPromise]);
119
+ const events = wal.readAll();
120
+ const tenants = events.map((e) => e.tenant);
121
+ // e3 must survive the remove, e4 must not be lost
122
+ expect(tenants).toContain("t-3");
123
+ expect(tenants).toContain("t-4");
124
+ expect(tenants).not.toContain("t-1");
125
+ expect(tenants).not.toContain("t-2");
126
+ expect(events).toHaveLength(2);
127
+ });
128
+ });
129
+ describe("MeterDLQ", () => {
130
+ let dlq;
131
+ beforeEach(() => {
132
+ if (existsSync(TEST_DIR)) {
133
+ rmSync(TEST_DIR, { recursive: true });
134
+ }
135
+ mkdirSync(TEST_DIR, { recursive: true });
136
+ dlq = new MeterDLQ(TEST_DLQ_PATH);
137
+ });
138
+ afterEach(() => {
139
+ if (existsSync(TEST_DIR)) {
140
+ rmSync(TEST_DIR, { recursive: true });
141
+ }
142
+ });
143
+ it("appends failed events with metadata", () => {
144
+ const event = { ...makeEvent(), id: "failed-event-1" };
145
+ dlq.append(event, "Database connection lost", 3);
146
+ const entries = dlq.readAll();
147
+ expect(entries).toHaveLength(1);
148
+ expect(entries[0].id).toBe("failed-event-1");
149
+ expect(entries[0].tenant).toBe("tenant-1");
150
+ expect(entries[0].dlq_error).toBe("Database connection lost");
151
+ expect(entries[0].dlq_retries).toBe(3);
152
+ expect(entries[0].dlq_timestamp).toBeGreaterThan(0);
153
+ });
154
+ it("readAll returns empty array for empty DLQ", () => {
155
+ const entries = dlq.readAll();
156
+ expect(entries).toEqual([]);
157
+ });
158
+ it("count returns 0 for empty DLQ", () => {
159
+ expect(dlq.count()).toBe(0);
160
+ });
161
+ it("count returns correct number of entries", () => {
162
+ dlq.append({ ...makeEvent(), id: "e1" }, "error1", 3);
163
+ dlq.append({ ...makeEvent(), id: "e2" }, "error2", 3);
164
+ expect(dlq.count()).toBe(2);
165
+ });
166
+ it("preserves all DLQ entries across multiple appends", () => {
167
+ for (let i = 0; i < 10; i++) {
168
+ dlq.append({ ...makeEvent(), id: `e-${i}` }, `error-${i}`, i + 1);
169
+ }
170
+ const entries = dlq.readAll();
171
+ expect(entries).toHaveLength(10);
172
+ expect(entries[0].dlq_retries).toBe(1);
173
+ expect(entries[9].dlq_retries).toBe(10);
174
+ });
175
+ });
@@ -0,0 +1,24 @@
1
+ import type { MiddlewareHandler } from "hono";
2
+ /**
3
+ * Validate that a request's Origin or Referer matches one of the allowed origins.
4
+ * Returns true if the request is safe, false if it should be blocked.
5
+ */
6
+ export declare function validateCsrfOrigin(headers: Headers, allowedOrigins: string[]): boolean;
7
+ export interface CsrfOptions {
8
+ /** Allowed origins (e.g. ["https://app.example.com"]). */
9
+ allowedOrigins: string[];
10
+ /**
11
+ * Paths exempt from CSRF validation.
12
+ * Use trailing "*" for prefix matching (e.g. "/api/auth/*").
13
+ * Exact strings match exactly (e.g. "/api/billing/webhook").
14
+ */
15
+ exemptPaths?: string[];
16
+ }
17
+ /**
18
+ * Hono middleware that validates Origin/Referer on state-changing requests.
19
+ * Skips:
20
+ * - GET/HEAD/OPTIONS requests (safe methods)
21
+ * - Requests with Bearer token (not vulnerable to CSRF)
22
+ * - Exempt paths (configurable)
23
+ */
24
+ export declare function csrfProtection(options: CsrfOptions): MiddlewareHandler;
@@ -0,0 +1,80 @@
1
+ import { extractBearerToken } from "../auth/index.js";
2
+ const MUTATION_METHODS = new Set(["POST", "PUT", "PATCH", "DELETE"]);
3
+ /**
4
+ * Check whether a path matches any of the exempt patterns.
5
+ * Patterns ending with "*" match as prefixes; exact strings match exactly.
6
+ */
7
+ function isExempt(path, exemptPaths) {
8
+ for (const pattern of exemptPaths) {
9
+ if (pattern.endsWith("*")) {
10
+ const prefix = pattern.slice(0, -1);
11
+ if (path.startsWith(prefix))
12
+ return true;
13
+ }
14
+ else {
15
+ if (path === pattern)
16
+ return true;
17
+ }
18
+ }
19
+ return false;
20
+ }
21
+ /**
22
+ * Validate that a request's Origin or Referer matches one of the allowed origins.
23
+ * Returns true if the request is safe, false if it should be blocked.
24
+ */
25
+ export function validateCsrfOrigin(headers, allowedOrigins) {
26
+ const origin = headers.get("origin");
27
+ // Check Origin header first (most reliable)
28
+ if (origin) {
29
+ return allowedOrigins.includes(origin);
30
+ }
31
+ // Fall back to Referer header
32
+ const referer = headers.get("referer");
33
+ if (referer) {
34
+ try {
35
+ const refererOrigin = new URL(referer).origin;
36
+ return allowedOrigins.includes(refererOrigin);
37
+ }
38
+ catch {
39
+ return false;
40
+ }
41
+ }
42
+ // No Origin or Referer on a mutation request — block it
43
+ return false;
44
+ }
45
+ /**
46
+ * Hono middleware that validates Origin/Referer on state-changing requests.
47
+ * Skips:
48
+ * - GET/HEAD/OPTIONS requests (safe methods)
49
+ * - Requests with Bearer token (not vulnerable to CSRF)
50
+ * - Exempt paths (configurable)
51
+ */
52
+ export function csrfProtection(options) {
53
+ const exempt = options.exemptPaths ?? [];
54
+ return async (c, next) => {
55
+ // Safe methods — no CSRF risk
56
+ if (!MUTATION_METHODS.has(c.req.method)) {
57
+ return next();
58
+ }
59
+ // Exempt paths
60
+ if (isExempt(c.req.path, exempt)) {
61
+ return next();
62
+ }
63
+ // Bearer-token requests are not vulnerable to CSRF (browser doesn't auto-send)
64
+ const authHeader = c.req.header("Authorization");
65
+ if (extractBearerToken(authHeader)) {
66
+ return next();
67
+ }
68
+ // Unauthenticated requests (no session user, no bearer token) cannot be CSRF attacks —
69
+ // there is no credential to hijack. Let auth middleware return 401 naturally.
70
+ const user = c.get("user");
71
+ if (!user) {
72
+ return next();
73
+ }
74
+ // Validate Origin/Referer
75
+ if (!validateCsrfOrigin(c.req.raw.headers, options.allowedOrigins)) {
76
+ return c.json({ error: "CSRF validation failed" }, 403);
77
+ }
78
+ return next();
79
+ };
80
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,152 @@
1
+ import { Hono } from "hono";
2
+ import { describe, expect, it } from "vitest";
3
+ import { csrfProtection, validateCsrfOrigin } from "./csrf.js";
4
+ // ---------------------------------------------------------------------------
5
+ // validateCsrfOrigin unit tests
6
+ // ---------------------------------------------------------------------------
7
+ describe("validateCsrfOrigin", () => {
8
+ function makeHeaders(headers) {
9
+ return new Headers(headers);
10
+ }
11
+ it("returns true when Origin matches allowed origin", () => {
12
+ const result = validateCsrfOrigin(makeHeaders({ origin: "https://app.example.com" }), ["https://app.example.com"]);
13
+ expect(result).toBe(true);
14
+ });
15
+ it("returns true when Origin matches one of multiple allowed origins", () => {
16
+ const result = validateCsrfOrigin(makeHeaders({ origin: "https://staging.example.com" }), [
17
+ "https://app.example.com",
18
+ "https://staging.example.com",
19
+ ]);
20
+ expect(result).toBe(true);
21
+ });
22
+ it("returns false when Origin does not match any allowed origin", () => {
23
+ const result = validateCsrfOrigin(makeHeaders({ origin: "https://evil.com" }), ["https://app.example.com"]);
24
+ expect(result).toBe(false);
25
+ });
26
+ it("falls back to Referer when Origin is absent", () => {
27
+ const result = validateCsrfOrigin(makeHeaders({ referer: "https://app.example.com/dashboard" }), [
28
+ "https://app.example.com",
29
+ ]);
30
+ expect(result).toBe(true);
31
+ });
32
+ it("returns false when neither Origin nor Referer is present", () => {
33
+ const result = validateCsrfOrigin(makeHeaders({}), ["https://app.example.com"]);
34
+ expect(result).toBe(false);
35
+ });
36
+ it("returns false for malformed Referer URL", () => {
37
+ const result = validateCsrfOrigin(makeHeaders({ referer: "not-a-url" }), ["https://app.example.com"]);
38
+ expect(result).toBe(false);
39
+ });
40
+ });
41
+ // ---------------------------------------------------------------------------
42
+ // csrfProtection middleware integration tests
43
+ // ---------------------------------------------------------------------------
44
+ describe("csrfProtection middleware", () => {
45
+ const exemptPaths = [
46
+ "/api/auth/*",
47
+ "/api/billing/webhook",
48
+ "/api/billing/crypto/*",
49
+ "/internal/*",
50
+ "/health*",
51
+ "/auth/*",
52
+ ];
53
+ function createApp(allowedOrigins) {
54
+ const app = new Hono();
55
+ // Simulate session user being set so CSRF validation logic actually runs
56
+ app.use("/*", async (c, next) => {
57
+ c.set("user", { id: "test-user", roles: ["user"] });
58
+ return next();
59
+ });
60
+ app.use("/*", csrfProtection({ allowedOrigins, exemptPaths }));
61
+ app.post("/api/fleet/bots", (c) => c.json({ ok: true }));
62
+ app.put("/api/billing/checkout", (c) => c.json({ ok: true }));
63
+ app.delete("/fleet/bots/123", (c) => c.json({ ok: true }));
64
+ app.get("/api/fleet/bots", (c) => c.json({ ok: true }));
65
+ app.post("/api/auth/sign-in/email", (c) => c.json({ ok: true }));
66
+ app.post("/api/billing/webhook", (c) => c.json({ ok: true }));
67
+ app.post("/internal/nodes/register", (c) => c.json({ ok: true }));
68
+ app.post("/health", (c) => c.json({ ok: true }));
69
+ app.post("/trpc/fleet.create", (c) => c.json({ ok: true }));
70
+ return app;
71
+ }
72
+ const origins = ["https://app.example.com"];
73
+ it("blocks POST without Origin header", async () => {
74
+ const app = createApp(origins);
75
+ const res = await app.request("/api/fleet/bots", { method: "POST" });
76
+ expect(res.status).toBe(403);
77
+ const body = await res.json();
78
+ expect(body.error).toBe("CSRF validation failed");
79
+ });
80
+ it("blocks POST with wrong Origin", async () => {
81
+ const app = createApp(origins);
82
+ const res = await app.request("/api/fleet/bots", {
83
+ method: "POST",
84
+ headers: { origin: "https://evil.com" },
85
+ });
86
+ expect(res.status).toBe(403);
87
+ });
88
+ it("allows POST with correct Origin", async () => {
89
+ const app = createApp(origins);
90
+ const res = await app.request("/api/fleet/bots", {
91
+ method: "POST",
92
+ headers: { origin: "https://app.example.com" },
93
+ });
94
+ expect(res.status).toBe(200);
95
+ });
96
+ it("allows GET requests without Origin", async () => {
97
+ const app = createApp(origins);
98
+ const res = await app.request("/api/fleet/bots", { method: "GET" });
99
+ expect(res.status).toBe(200);
100
+ });
101
+ it("blocks PUT without Origin", async () => {
102
+ const app = createApp(origins);
103
+ const res = await app.request("/api/billing/checkout", { method: "PUT" });
104
+ expect(res.status).toBe(403);
105
+ });
106
+ it("blocks DELETE without Origin", async () => {
107
+ const app = createApp(origins);
108
+ const res = await app.request("/fleet/bots/123", { method: "DELETE" });
109
+ expect(res.status).toBe(403);
110
+ });
111
+ it("exempts /api/auth/* routes", async () => {
112
+ const app = createApp(origins);
113
+ const res = await app.request("/api/auth/sign-in/email", { method: "POST" });
114
+ expect(res.status).toBe(200);
115
+ });
116
+ it("exempts /api/billing/webhook", async () => {
117
+ const app = createApp(origins);
118
+ const res = await app.request("/api/billing/webhook", { method: "POST" });
119
+ expect(res.status).toBe(200);
120
+ });
121
+ it("exempts /internal/* routes", async () => {
122
+ const app = createApp(origins);
123
+ const res = await app.request("/internal/nodes/register", { method: "POST" });
124
+ expect(res.status).toBe(200);
125
+ });
126
+ it("exempts /health routes", async () => {
127
+ const app = createApp(origins);
128
+ const res = await app.request("/health", { method: "POST" });
129
+ expect(res.status).toBe(200);
130
+ });
131
+ it("skips CSRF check when Authorization Bearer header is present", async () => {
132
+ const app = createApp(origins);
133
+ const res = await app.request("/api/fleet/bots", {
134
+ method: "POST",
135
+ headers: { authorization: "Bearer some-token" },
136
+ });
137
+ expect(res.status).toBe(200);
138
+ });
139
+ it("protects /trpc/* mutation routes", async () => {
140
+ const app = createApp(origins);
141
+ const res = await app.request("/trpc/fleet.create", { method: "POST" });
142
+ expect(res.status).toBe(403);
143
+ });
144
+ it("allows /trpc/* with correct Origin", async () => {
145
+ const app = createApp(origins);
146
+ const res = await app.request("/trpc/fleet.create", {
147
+ method: "POST",
148
+ headers: { origin: "https://app.example.com" },
149
+ });
150
+ expect(res.status).toBe(200);
151
+ });
152
+ });
@@ -0,0 +1,9 @@
1
+ import type { PlatformDb } from "../db/index.js";
2
+ import type { IRateLimitRepository, RateLimitEntry } from "./rate-limit-repository.js";
3
+ export declare class DrizzleRateLimitRepository implements IRateLimitRepository {
4
+ private readonly db;
5
+ constructor(db: PlatformDb);
6
+ increment(key: string, scope: string, windowMs: number): Promise<RateLimitEntry>;
7
+ get(key: string, scope: string): Promise<RateLimitEntry | null>;
8
+ purgeStale(windowMs: number): Promise<number>;
9
+ }
@@ -0,0 +1,52 @@
1
+ import { and, eq, lt, sql } from "drizzle-orm";
2
+ import { rateLimitEntries } from "../db/schema/index.js";
3
+ export class DrizzleRateLimitRepository {
4
+ db;
5
+ constructor(db) {
6
+ this.db = db;
7
+ }
8
+ async increment(key, scope, windowMs) {
9
+ const now = Date.now();
10
+ // Check if existing entry is within the current window
11
+ const rows = await this.db
12
+ .select()
13
+ .from(rateLimitEntries)
14
+ .where(and(eq(rateLimitEntries.key, key), eq(rateLimitEntries.scope, scope)));
15
+ const existing = rows[0];
16
+ if (existing && now - existing.windowStart < windowMs) {
17
+ // Within window: increment
18
+ await this.db
19
+ .update(rateLimitEntries)
20
+ .set({ count: sql `${rateLimitEntries.count} + 1` })
21
+ .where(and(eq(rateLimitEntries.key, key), eq(rateLimitEntries.scope, scope)));
22
+ return { key, scope, count: existing.count + 1, windowStart: existing.windowStart };
23
+ }
24
+ // New window (or first request): upsert with count = 1
25
+ await this.db
26
+ .insert(rateLimitEntries)
27
+ .values({ key, scope, count: 1, windowStart: now })
28
+ .onConflictDoUpdate({
29
+ target: [rateLimitEntries.key, rateLimitEntries.scope],
30
+ set: { count: 1, windowStart: now },
31
+ });
32
+ return { key, scope, count: 1, windowStart: now };
33
+ }
34
+ async get(key, scope) {
35
+ const rows = await this.db
36
+ .select()
37
+ .from(rateLimitEntries)
38
+ .where(and(eq(rateLimitEntries.key, key), eq(rateLimitEntries.scope, scope)));
39
+ const row = rows[0];
40
+ if (!row)
41
+ return null;
42
+ return { key: row.key, scope: row.scope, count: row.count, windowStart: row.windowStart };
43
+ }
44
+ async purgeStale(windowMs) {
45
+ const cutoff = Date.now() - windowMs;
46
+ const result = await this.db
47
+ .delete(rateLimitEntries)
48
+ .where(lt(rateLimitEntries.windowStart, cutoff))
49
+ .returning({ key: rateLimitEntries.key });
50
+ return result.length;
51
+ }
52
+ }