@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,213 @@
1
+ import { createHash } from "node:crypto";
2
+ import { Hono } from "hono";
3
+ import { describe, expect, it, vi } from "vitest";
4
+ import { dualAuth, sessionAuth } from "./middleware.js";
5
+ function sha256(token) {
6
+ return createHash("sha256").update(token).digest("hex");
7
+ }
8
+ function mockApiKeyRepo(entries) {
9
+ const hashMap = new Map();
10
+ for (const [token, user] of entries) {
11
+ hashMap.set(sha256(token), user);
12
+ }
13
+ return {
14
+ async findByHash(keyHash) {
15
+ return hashMap.get(keyHash) ?? null;
16
+ },
17
+ };
18
+ }
19
+ function mockAuth(sessionResult = null) {
20
+ return {
21
+ api: {
22
+ getSession: vi.fn().mockResolvedValue(sessionResult),
23
+ },
24
+ };
25
+ }
26
+ describe("sessionAuth middleware", () => {
27
+ it("sets user and authMethod for valid session", async () => {
28
+ const auth = mockAuth({ user: { id: "user-1", role: "admin" } });
29
+ const app = new Hono();
30
+ app.use("/*", sessionAuth(auth));
31
+ app.get("/test", (c) => {
32
+ const user = c.get("user");
33
+ const method = c.get("authMethod");
34
+ return c.json({ userId: user.id, roles: user.roles, method });
35
+ });
36
+ const res = await app.request("/test");
37
+ expect(res.status).toBe(200);
38
+ const body = await res.json();
39
+ expect(body.userId).toBe("user-1");
40
+ expect(body.roles).toEqual(["admin", "user"]);
41
+ expect(body.method).toBe("session");
42
+ });
43
+ it("sets user roles as ['user'] for non-admin", async () => {
44
+ const auth = mockAuth({ user: { id: "user-2" } });
45
+ const app = new Hono();
46
+ app.use("/*", sessionAuth(auth));
47
+ app.get("/test", (c) => {
48
+ const user = c.get("user");
49
+ return c.json({ roles: user.roles });
50
+ });
51
+ const res = await app.request("/test");
52
+ expect(res.status).toBe(200);
53
+ const body = await res.json();
54
+ expect(body.roles).toEqual(["user"]);
55
+ });
56
+ it("returns 401 when no session found", async () => {
57
+ const auth = mockAuth(null);
58
+ const app = new Hono();
59
+ app.use("/*", sessionAuth(auth));
60
+ app.get("/test", (c) => c.json({ ok: true }));
61
+ const res = await app.request("/test");
62
+ expect(res.status).toBe(401);
63
+ const body = await res.json();
64
+ expect(body.error).toBe("Authentication required");
65
+ });
66
+ it("returns 401 when session has no user", async () => {
67
+ const auth = {
68
+ api: {
69
+ getSession: vi.fn().mockResolvedValue({ user: null }),
70
+ },
71
+ };
72
+ const app = new Hono();
73
+ app.use("/*", sessionAuth(auth));
74
+ app.get("/test", (c) => c.json({ ok: true }));
75
+ const res = await app.request("/test");
76
+ expect(res.status).toBe(401);
77
+ });
78
+ it("returns 503 when getSession throws a DB error", async () => {
79
+ const auth = {
80
+ api: {
81
+ getSession: vi.fn().mockRejectedValue(new Error("DB error")),
82
+ },
83
+ };
84
+ const app = new Hono();
85
+ app.use("/*", sessionAuth(auth));
86
+ app.get("/test", (c) => c.json({ ok: true }));
87
+ const res = await app.request("/test");
88
+ expect(res.status).toBe(503);
89
+ const body = await res.json();
90
+ expect(body.error).toBe("Service unavailable");
91
+ });
92
+ });
93
+ describe("dualAuth middleware", () => {
94
+ const apiKeyRepo = mockApiKeyRepo(new Map([
95
+ ["api-key-admin", { id: "admin-1", roles: ["admin"] }],
96
+ ["api-key-reader", { id: "reader-1", roles: ["user"] }],
97
+ ]));
98
+ it("authenticates with session cookie first", async () => {
99
+ const auth = mockAuth({ user: { id: "session-user", role: "admin" } });
100
+ const app = new Hono();
101
+ app.use("/*", dualAuth(auth, apiKeyRepo));
102
+ app.get("/test", (c) => {
103
+ const user = c.get("user");
104
+ const method = c.get("authMethod");
105
+ return c.json({ userId: user.id, method });
106
+ });
107
+ const res = await app.request("/test");
108
+ expect(res.status).toBe(200);
109
+ const body = await res.json();
110
+ expect(body.userId).toBe("session-user");
111
+ expect(body.method).toBe("session");
112
+ });
113
+ it("falls back to bearer token when session fails", async () => {
114
+ const auth = mockAuth(null);
115
+ const app = new Hono();
116
+ app.use("/*", dualAuth(auth, apiKeyRepo));
117
+ app.get("/test", (c) => {
118
+ const user = c.get("user");
119
+ const method = c.get("authMethod");
120
+ return c.json({ userId: user.id, method });
121
+ });
122
+ const res = await app.request("/test", {
123
+ headers: { Authorization: "Bearer api-key-admin" },
124
+ });
125
+ expect(res.status).toBe(200);
126
+ const body = await res.json();
127
+ expect(body.userId).toBe("admin-1");
128
+ expect(body.method).toBe("api_key");
129
+ });
130
+ it("returns 401 when neither session nor token is valid", async () => {
131
+ const auth = mockAuth(null);
132
+ const app = new Hono();
133
+ app.use("/*", dualAuth(auth, apiKeyRepo));
134
+ app.get("/test", (c) => c.json({ ok: true }));
135
+ const res = await app.request("/test");
136
+ expect(res.status).toBe(401);
137
+ const body = await res.json();
138
+ expect(body.error).toBe("Authentication required");
139
+ });
140
+ it("returns 401 when bearer token is unknown", async () => {
141
+ const auth = mockAuth(null);
142
+ const app = new Hono();
143
+ app.use("/*", dualAuth(auth, apiKeyRepo));
144
+ app.get("/test", (c) => c.json({ ok: true }));
145
+ const res = await app.request("/test", {
146
+ headers: { Authorization: "Bearer bad-token" },
147
+ });
148
+ expect(res.status).toBe(401);
149
+ });
150
+ it("returns 401 when Authorization header is not Bearer scheme", async () => {
151
+ const auth = mockAuth(null);
152
+ const app = new Hono();
153
+ app.use("/*", dualAuth(auth, apiKeyRepo));
154
+ app.get("/test", (c) => c.json({ ok: true }));
155
+ const res = await app.request("/test", {
156
+ headers: { Authorization: "Basic dXNlcjpwYXNz" },
157
+ });
158
+ expect(res.status).toBe(401);
159
+ });
160
+ it("works without apiKeyRepo (session-only mode)", async () => {
161
+ const auth = mockAuth(null);
162
+ const app = new Hono();
163
+ app.use("/*", dualAuth(auth));
164
+ app.get("/test", (c) => c.json({ ok: true }));
165
+ const res = await app.request("/test");
166
+ expect(res.status).toBe(401);
167
+ });
168
+ it("returns 503 when session DB lookup throws", async () => {
169
+ const auth = {
170
+ api: {
171
+ getSession: vi.fn().mockRejectedValue(new Error("Session DB error")),
172
+ },
173
+ };
174
+ const app = new Hono();
175
+ app.use("/*", dualAuth(auth, apiKeyRepo));
176
+ app.get("/test", (c) => c.json({ ok: true }));
177
+ const res = await app.request("/test", {
178
+ headers: { Authorization: "Bearer api-key-admin" },
179
+ });
180
+ expect(res.status).toBe(503);
181
+ const body = await res.json();
182
+ expect(body.error).toBe("Service unavailable");
183
+ });
184
+ it("returns 503 when API key DB lookup throws", async () => {
185
+ const auth = mockAuth(null);
186
+ const failingRepo = {
187
+ findByHash: vi.fn().mockRejectedValue(new Error("DB connection lost")),
188
+ };
189
+ const app = new Hono();
190
+ app.use("/*", dualAuth(auth, failingRepo));
191
+ app.get("/test", (c) => c.json({ ok: true }));
192
+ const res = await app.request("/test", {
193
+ headers: { Authorization: "Bearer some-token" },
194
+ });
195
+ expect(res.status).toBe(503);
196
+ const body = await res.json();
197
+ expect(body.error).toBe("Service unavailable");
198
+ });
199
+ it("copies API user roles to prevent mutation", async () => {
200
+ const auth = mockAuth(null);
201
+ const app = new Hono();
202
+ app.use("/*", dualAuth(auth, apiKeyRepo));
203
+ app.get("/test", (c) => {
204
+ const user = c.get("user");
205
+ return c.json({ roles: user.roles });
206
+ });
207
+ const res = await app.request("/test", {
208
+ headers: { Authorization: "Bearer api-key-reader" },
209
+ });
210
+ const body = await res.json();
211
+ expect(body.roles).toEqual(["user"]);
212
+ });
213
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,306 @@
1
+ import { Hono } from "hono";
2
+ import { describe, expect, it } from "vitest";
3
+ import { buildTokenMap, parseTokenScope, scopedBearerAuth, scopeSatisfies } from "./index.js";
4
+ // ---------------------------------------------------------------------------
5
+ // parseTokenScope
6
+ // ---------------------------------------------------------------------------
7
+ describe("parseTokenScope", () => {
8
+ it("parses read scope from wopr_read_<random>", () => {
9
+ expect(parseTokenScope("wopr_read_abc123")).toBe("read");
10
+ });
11
+ it("parses write scope from wopr_write_<random>", () => {
12
+ expect(parseTokenScope("wopr_write_xyz789")).toBe("write");
13
+ });
14
+ it("parses admin scope from wopr_admin_<random>", () => {
15
+ expect(parseTokenScope("wopr_admin_secret42")).toBe("admin");
16
+ });
17
+ it("handles tokens with underscores in the random part", () => {
18
+ expect(parseTokenScope("wopr_read_abc_def_ghi")).toBe("read");
19
+ });
20
+ it("returns null for tokens without wopr_ prefix", () => {
21
+ expect(parseTokenScope("some-plain-token")).toBeNull();
22
+ expect(parseTokenScope("fleet-token-123")).toBeNull();
23
+ });
24
+ it("returns null for empty string", () => {
25
+ expect(parseTokenScope("")).toBeNull();
26
+ });
27
+ it("returns null for wopr_ with no scope", () => {
28
+ expect(parseTokenScope("wopr_")).toBeNull();
29
+ });
30
+ it("returns null for wopr_ with invalid scope", () => {
31
+ expect(parseTokenScope("wopr_superadmin_abc")).toBeNull();
32
+ expect(parseTokenScope("wopr_delete_abc")).toBeNull();
33
+ expect(parseTokenScope("wopr_root_abc")).toBeNull();
34
+ });
35
+ it("returns null for wopr_ with scope but no random part", () => {
36
+ expect(parseTokenScope("wopr_read_")).toBeNull();
37
+ expect(parseTokenScope("wopr_read")).toBeNull();
38
+ });
39
+ it("returns null for partial prefix", () => {
40
+ expect(parseTokenScope("wop_read_abc")).toBeNull();
41
+ expect(parseTokenScope("WOPR_read_abc")).toBeNull();
42
+ });
43
+ });
44
+ // ---------------------------------------------------------------------------
45
+ // scopeSatisfies
46
+ // ---------------------------------------------------------------------------
47
+ describe("scopeSatisfies", () => {
48
+ it("read satisfies read", () => {
49
+ expect(scopeSatisfies("read", "read")).toBe(true);
50
+ });
51
+ it("write satisfies read", () => {
52
+ expect(scopeSatisfies("write", "read")).toBe(true);
53
+ });
54
+ it("write satisfies write", () => {
55
+ expect(scopeSatisfies("write", "write")).toBe(true);
56
+ });
57
+ it("admin satisfies read", () => {
58
+ expect(scopeSatisfies("admin", "read")).toBe(true);
59
+ });
60
+ it("admin satisfies write", () => {
61
+ expect(scopeSatisfies("admin", "write")).toBe(true);
62
+ });
63
+ it("admin satisfies admin", () => {
64
+ expect(scopeSatisfies("admin", "admin")).toBe(true);
65
+ });
66
+ it("read does NOT satisfy write", () => {
67
+ expect(scopeSatisfies("read", "write")).toBe(false);
68
+ });
69
+ it("read does NOT satisfy admin", () => {
70
+ expect(scopeSatisfies("read", "admin")).toBe(false);
71
+ });
72
+ it("write does NOT satisfy admin", () => {
73
+ expect(scopeSatisfies("write", "admin")).toBe(false);
74
+ });
75
+ });
76
+ // ---------------------------------------------------------------------------
77
+ // buildTokenMap
78
+ // ---------------------------------------------------------------------------
79
+ describe("buildTokenMap", () => {
80
+ it("maps FLEET_API_TOKEN as admin (backwards compat)", () => {
81
+ const map = buildTokenMap({ FLEET_API_TOKEN: "legacy-token" });
82
+ expect(map.get("legacy-token")).toBe("admin");
83
+ });
84
+ it("maps scoped env vars to correct scopes", () => {
85
+ const map = buildTokenMap({
86
+ FLEET_API_TOKEN_READ: "wopr_read_r1",
87
+ FLEET_API_TOKEN_WRITE: "wopr_write_w1",
88
+ FLEET_API_TOKEN_ADMIN: "wopr_admin_a1",
89
+ });
90
+ expect(map.get("wopr_read_r1")).toBe("read");
91
+ expect(map.get("wopr_write_w1")).toBe("write");
92
+ expect(map.get("wopr_admin_a1")).toBe("admin");
93
+ });
94
+ it("scoped vars take priority over legacy token", () => {
95
+ const token = "shared-token";
96
+ const map = buildTokenMap({
97
+ FLEET_API_TOKEN: token,
98
+ FLEET_API_TOKEN_READ: token,
99
+ });
100
+ // The scoped var should have set it to read first
101
+ expect(map.get(token)).toBe("read");
102
+ });
103
+ it("returns empty map when no env vars set", () => {
104
+ const map = buildTokenMap({});
105
+ expect(map.size).toBe(0);
106
+ });
107
+ it("ignores empty string env vars", () => {
108
+ const map = buildTokenMap({
109
+ FLEET_API_TOKEN: "",
110
+ FLEET_API_TOKEN_READ: "",
111
+ FLEET_API_TOKEN_WRITE: "",
112
+ FLEET_API_TOKEN_ADMIN: "",
113
+ });
114
+ expect(map.size).toBe(0);
115
+ });
116
+ it("ignores whitespace-only env vars", () => {
117
+ const map = buildTokenMap({
118
+ FLEET_API_TOKEN: " ",
119
+ FLEET_API_TOKEN_READ: " ",
120
+ });
121
+ expect(map.size).toBe(0);
122
+ });
123
+ it("infers scope from wopr_ format for legacy token", () => {
124
+ const map = buildTokenMap({ FLEET_API_TOKEN: "wopr_read_abc123" });
125
+ expect(map.get("wopr_read_abc123")).toBe("read");
126
+ });
127
+ it("supports all scoped vars alongside legacy", () => {
128
+ const map = buildTokenMap({
129
+ FLEET_API_TOKEN: "legacy-admin",
130
+ FLEET_API_TOKEN_READ: "read-token",
131
+ FLEET_API_TOKEN_WRITE: "write-token",
132
+ FLEET_API_TOKEN_ADMIN: "admin-token",
133
+ });
134
+ expect(map.size).toBe(4);
135
+ expect(map.get("legacy-admin")).toBe("admin");
136
+ expect(map.get("read-token")).toBe("read");
137
+ expect(map.get("write-token")).toBe("write");
138
+ expect(map.get("admin-token")).toBe("admin");
139
+ });
140
+ });
141
+ // ---------------------------------------------------------------------------
142
+ // scopedBearerAuth middleware
143
+ // ---------------------------------------------------------------------------
144
+ describe("scopedBearerAuth middleware", () => {
145
+ const tokenMap = new Map([
146
+ ["wopr_read_r1", "read"],
147
+ ["wopr_write_w1", "write"],
148
+ ["wopr_admin_a1", "admin"],
149
+ ["legacy-admin", "admin"],
150
+ ]);
151
+ function createApp(requiredScope) {
152
+ const app = new Hono();
153
+ app.use("/*", scopedBearerAuth(tokenMap, requiredScope));
154
+ app.get("/test", (c) => c.json({ ok: true }));
155
+ app.post("/test", (c) => c.json({ ok: true }));
156
+ return app;
157
+ }
158
+ describe("authentication", () => {
159
+ it("rejects request with no Authorization header", async () => {
160
+ const app = createApp("read");
161
+ const res = await app.request("/test");
162
+ expect(res.status).toBe(401);
163
+ const body = await res.json();
164
+ expect(body.error).toBe("Authentication required");
165
+ });
166
+ it("rejects request with empty Authorization header", async () => {
167
+ const app = createApp("read");
168
+ const res = await app.request("/test", {
169
+ headers: { Authorization: "" },
170
+ });
171
+ expect(res.status).toBe(401);
172
+ });
173
+ it("rejects request with non-Bearer scheme", async () => {
174
+ const app = createApp("read");
175
+ const res = await app.request("/test", {
176
+ headers: { Authorization: "Basic abc123" },
177
+ });
178
+ expect(res.status).toBe(401);
179
+ });
180
+ it("rejects request with unknown token", async () => {
181
+ const app = createApp("read");
182
+ const res = await app.request("/test", {
183
+ headers: { Authorization: "Bearer unknown-token" },
184
+ });
185
+ expect(res.status).toBe(401);
186
+ const body = await res.json();
187
+ expect(body.error).toBe("Invalid or expired token");
188
+ });
189
+ });
190
+ describe("read scope routes", () => {
191
+ const app = createApp("read");
192
+ it("allows read token on read routes", async () => {
193
+ const res = await app.request("/test", {
194
+ headers: { Authorization: "Bearer wopr_read_r1" },
195
+ });
196
+ expect(res.status).toBe(200);
197
+ });
198
+ it("allows write token on read routes", async () => {
199
+ const res = await app.request("/test", {
200
+ headers: { Authorization: "Bearer wopr_write_w1" },
201
+ });
202
+ expect(res.status).toBe(200);
203
+ });
204
+ it("allows admin token on read routes", async () => {
205
+ const res = await app.request("/test", {
206
+ headers: { Authorization: "Bearer wopr_admin_a1" },
207
+ });
208
+ expect(res.status).toBe(200);
209
+ });
210
+ it("allows legacy admin token on read routes", async () => {
211
+ const res = await app.request("/test", {
212
+ headers: { Authorization: "Bearer legacy-admin" },
213
+ });
214
+ expect(res.status).toBe(200);
215
+ });
216
+ });
217
+ describe("write scope routes", () => {
218
+ const app = createApp("write");
219
+ it("rejects read token on write routes with 403", async () => {
220
+ const res = await app.request("/test", {
221
+ method: "POST",
222
+ headers: { Authorization: "Bearer wopr_read_r1" },
223
+ });
224
+ expect(res.status).toBe(403);
225
+ const body = await res.json();
226
+ expect(body.error).toBe("Insufficient scope");
227
+ expect(body.required).toBe("write");
228
+ expect(body.provided).toBe("read");
229
+ });
230
+ it("allows write token on write routes", async () => {
231
+ const res = await app.request("/test", {
232
+ method: "POST",
233
+ headers: { Authorization: "Bearer wopr_write_w1" },
234
+ });
235
+ expect(res.status).toBe(200);
236
+ });
237
+ it("allows admin token on write routes", async () => {
238
+ const res = await app.request("/test", {
239
+ method: "POST",
240
+ headers: { Authorization: "Bearer wopr_admin_a1" },
241
+ });
242
+ expect(res.status).toBe(200);
243
+ });
244
+ });
245
+ describe("admin scope routes", () => {
246
+ const app = createApp("admin");
247
+ it("rejects read token on admin routes with 403", async () => {
248
+ const res = await app.request("/test", {
249
+ headers: { Authorization: "Bearer wopr_read_r1" },
250
+ });
251
+ expect(res.status).toBe(403);
252
+ const body = await res.json();
253
+ expect(body.error).toBe("Insufficient scope");
254
+ expect(body.required).toBe("admin");
255
+ expect(body.provided).toBe("read");
256
+ });
257
+ it("rejects write token on admin routes with 403", async () => {
258
+ const res = await app.request("/test", {
259
+ headers: { Authorization: "Bearer wopr_write_w1" },
260
+ });
261
+ expect(res.status).toBe(403);
262
+ const body = await res.json();
263
+ expect(body.error).toBe("Insufficient scope");
264
+ expect(body.required).toBe("admin");
265
+ expect(body.provided).toBe("write");
266
+ });
267
+ it("allows admin token on admin routes", async () => {
268
+ const res = await app.request("/test", {
269
+ headers: { Authorization: "Bearer wopr_admin_a1" },
270
+ });
271
+ expect(res.status).toBe(200);
272
+ });
273
+ it("allows legacy admin token on admin routes", async () => {
274
+ const res = await app.request("/test", {
275
+ headers: { Authorization: "Bearer legacy-admin" },
276
+ });
277
+ expect(res.status).toBe(200);
278
+ });
279
+ });
280
+ describe("backwards compatibility", () => {
281
+ it("existing FLEET_API_TOKEN works as admin on all scopes", async () => {
282
+ const legacyMap = buildTokenMap({ FLEET_API_TOKEN: "my-old-token" });
283
+ for (const scope of ["read", "write", "admin"]) {
284
+ const app = new Hono();
285
+ app.use("/*", scopedBearerAuth(legacyMap, scope));
286
+ app.get("/test", (c) => c.json({ ok: true }));
287
+ const res = await app.request("/test", {
288
+ headers: { Authorization: "Bearer my-old-token" },
289
+ });
290
+ expect(res.status).toBe(200);
291
+ }
292
+ });
293
+ });
294
+ describe("empty token map", () => {
295
+ it("rejects all tokens when map is empty", async () => {
296
+ const emptyMap = new Map();
297
+ const app = new Hono();
298
+ app.use("/*", scopedBearerAuth(emptyMap, "read"));
299
+ app.get("/test", (c) => c.json({ ok: true }));
300
+ const res = await app.request("/test", {
301
+ headers: { Authorization: "Bearer any-token" },
302
+ });
303
+ expect(res.status).toBe(401);
304
+ });
305
+ });
306
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,62 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { validateTenantAccess } from "./index.js";
3
+ function mockOrgMemberRepo(overrides = {}) {
4
+ return {
5
+ listMembers: vi.fn().mockResolvedValue([]),
6
+ addMember: vi.fn().mockResolvedValue(undefined),
7
+ updateMemberRole: vi.fn().mockResolvedValue(undefined),
8
+ removeMember: vi.fn().mockResolvedValue(undefined),
9
+ findMember: vi.fn().mockResolvedValue(null),
10
+ countAdminsAndOwners: vi.fn().mockResolvedValue(0),
11
+ listInvites: vi.fn().mockResolvedValue([]),
12
+ createInvite: vi.fn().mockResolvedValue(undefined),
13
+ findInviteById: vi.fn().mockResolvedValue(null),
14
+ findInviteByToken: vi.fn().mockResolvedValue(null),
15
+ deleteInvite: vi.fn().mockResolvedValue(undefined),
16
+ deleteAllMembers: vi.fn().mockResolvedValue(undefined),
17
+ deleteAllInvites: vi.fn().mockResolvedValue(undefined),
18
+ ...overrides,
19
+ };
20
+ }
21
+ describe("validateTenantAccess", () => {
22
+ it("allows access when requestedTenantId matches userId (personal tenant)", async () => {
23
+ const repo = mockOrgMemberRepo();
24
+ const result = await validateTenantAccess("user-1", "user-1", repo);
25
+ expect(result).toBe(true);
26
+ expect(repo.findMember).not.toHaveBeenCalled();
27
+ });
28
+ it("allows access when user is a member of the requested org", async () => {
29
+ const repo = mockOrgMemberRepo({
30
+ findMember: vi.fn().mockResolvedValue({
31
+ id: "m1",
32
+ orgId: "org-1",
33
+ userId: "user-1",
34
+ role: "member",
35
+ joinedAt: 0,
36
+ }),
37
+ });
38
+ const result = await validateTenantAccess("user-1", "org-1", repo);
39
+ expect(result).toBe(true);
40
+ expect(repo.findMember).toHaveBeenCalledWith("org-1", "user-1");
41
+ });
42
+ it("denies access when user is NOT a member of the requested org", async () => {
43
+ const repo = mockOrgMemberRepo({
44
+ findMember: vi.fn().mockResolvedValue(null),
45
+ });
46
+ const result = await validateTenantAccess("user-1", "org-evil", repo);
47
+ expect(result).toBe(false);
48
+ expect(repo.findMember).toHaveBeenCalledWith("org-evil", "user-1");
49
+ });
50
+ it("allows access when requestedTenantId is undefined (falls back to personal)", async () => {
51
+ const repo = mockOrgMemberRepo();
52
+ const result = await validateTenantAccess("user-1", undefined, repo);
53
+ expect(result).toBe(true);
54
+ expect(repo.findMember).not.toHaveBeenCalled();
55
+ });
56
+ it("allows access when requestedTenantId is empty string (falls back to personal)", async () => {
57
+ const repo = mockOrgMemberRepo();
58
+ const result = await validateTenantAccess("user-1", "", repo);
59
+ expect(result).toBe(true);
60
+ expect(repo.findMember).not.toHaveBeenCalled();
61
+ });
62
+ });
@@ -0,0 +1,9 @@
1
+ import type { RoleStore } from "../admin/role-store.js";
2
+ export interface IUserCreator {
3
+ createUser(userId: string): Promise<void>;
4
+ }
5
+ /**
6
+ * Create a user creator that auto-promotes the first signup to platform_admin
7
+ * when no admins exist. After promotion, all subsequent signups are no-ops.
8
+ */
9
+ export declare function createUserCreator(roleStore: RoleStore): Promise<IUserCreator>;
@@ -0,0 +1,47 @@
1
+ import { logger } from "../config/logger.js";
2
+ class UserCreator {
3
+ async createUser(_userId) {
4
+ // No-op for normal signups
5
+ }
6
+ }
7
+ class AdminUserCreator {
8
+ roleStore;
9
+ holder;
10
+ constructor(roleStore, holder) {
11
+ this.roleStore = roleStore;
12
+ this.holder = holder;
13
+ }
14
+ async createUser(userId) {
15
+ // Swap to no-op BEFORE await to prevent concurrent double-promoting.
16
+ // Save the current creator so we can restore it on failure.
17
+ const previous = this.holder.creator;
18
+ this.holder.creator = new UserCreator();
19
+ try {
20
+ await this.roleStore.setRole(userId, "*", "platform_admin", "bootstrap");
21
+ logger.info(`Bootstrap: first user ${userId} auto-promoted to platform_admin`);
22
+ }
23
+ catch (err) {
24
+ // Restore holder so the next signup can retry bootstrap
25
+ this.holder.creator = previous;
26
+ throw err;
27
+ }
28
+ }
29
+ }
30
+ class UserCreatorHolder {
31
+ creator;
32
+ constructor(creator) {
33
+ this.creator = creator;
34
+ }
35
+ }
36
+ /**
37
+ * Create a user creator that auto-promotes the first signup to platform_admin
38
+ * when no admins exist. After promotion, all subsequent signups are no-ops.
39
+ */
40
+ export async function createUserCreator(roleStore) {
41
+ const holder = new UserCreatorHolder(new UserCreator());
42
+ const count = await roleStore.countPlatformAdmins();
43
+ if (count === 0) {
44
+ holder.creator = new AdminUserCreator(roleStore, holder);
45
+ }
46
+ return { createUser: (id) => holder.creator.createUser(id) };
47
+ }
@@ -0,0 +1 @@
1
+ export {};