@shipfox/api-auth 2.0.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 (319) hide show
  1. package/.turbo/turbo-build.log +2 -0
  2. package/.turbo/turbo-type$colon$emit.log +1 -0
  3. package/.turbo/turbo-type.log +1 -0
  4. package/CHANGELOG.md +116 -0
  5. package/LICENSE +21 -0
  6. package/README.md +336 -0
  7. package/dist/config.d.ts +22 -0
  8. package/dist/config.d.ts.map +1 -0
  9. package/dist/config.js +95 -0
  10. package/dist/config.js.map +1 -0
  11. package/dist/core/auth.d.ts +108 -0
  12. package/dist/core/auth.d.ts.map +1 -0
  13. package/dist/core/auth.js +476 -0
  14. package/dist/core/auth.js.map +1 -0
  15. package/dist/core/entities/email-verification.d.ts +9 -0
  16. package/dist/core/entities/email-verification.d.ts.map +1 -0
  17. package/dist/core/entities/email-verification.js +3 -0
  18. package/dist/core/entities/email-verification.js.map +1 -0
  19. package/dist/core/entities/password-reset.d.ts +9 -0
  20. package/dist/core/entities/password-reset.d.ts.map +1 -0
  21. package/dist/core/entities/password-reset.js +3 -0
  22. package/dist/core/entities/password-reset.js.map +1 -0
  23. package/dist/core/entities/refresh-token.d.ts +12 -0
  24. package/dist/core/entities/refresh-token.d.ts.map +1 -0
  25. package/dist/core/entities/refresh-token.js +3 -0
  26. package/dist/core/entities/refresh-token.js.map +1 -0
  27. package/dist/core/entities/user.d.ts +12 -0
  28. package/dist/core/entities/user.d.ts.map +1 -0
  29. package/dist/core/entities/user.js +3 -0
  30. package/dist/core/entities/user.js.map +1 -0
  31. package/dist/core/errors.d.ts +54 -0
  32. package/dist/core/errors.d.ts.map +1 -0
  33. package/dist/core/errors.js +106 -0
  34. package/dist/core/errors.js.map +1 -0
  35. package/dist/core/job-lease-token.d.ts +12 -0
  36. package/dist/core/job-lease-token.d.ts.map +1 -0
  37. package/dist/core/job-lease-token.js +58 -0
  38. package/dist/core/job-lease-token.js.map +1 -0
  39. package/dist/core/jwt.d.ts +37 -0
  40. package/dist/core/jwt.d.ts.map +1 -0
  41. package/dist/core/jwt.js +46 -0
  42. package/dist/core/jwt.js.map +1 -0
  43. package/dist/core/password.d.ts +10 -0
  44. package/dist/core/password.d.ts.map +1 -0
  45. package/dist/core/password.js +13 -0
  46. package/dist/core/password.js.map +1 -0
  47. package/dist/core/rate-limit.d.ts +34 -0
  48. package/dist/core/rate-limit.d.ts.map +1 -0
  49. package/dist/core/rate-limit.js +76 -0
  50. package/dist/core/rate-limit.js.map +1 -0
  51. package/dist/core/runner-session-token.d.ts +6 -0
  52. package/dist/core/runner-session-token.d.ts.map +1 -0
  53. package/dist/core/runner-session-token.js +37 -0
  54. package/dist/core/runner-session-token.js.map +1 -0
  55. package/dist/db/db.d.ts +1723 -0
  56. package/dist/db/db.d.ts.map +1 -0
  57. package/dist/db/db.js +28 -0
  58. package/dist/db/db.js.map +1 -0
  59. package/dist/db/email-verifications.d.ts +39 -0
  60. package/dist/db/email-verifications.d.ts.map +1 -0
  61. package/dist/db/email-verifications.js +95 -0
  62. package/dist/db/email-verifications.js.map +1 -0
  63. package/dist/db/migrations.d.ts +2 -0
  64. package/dist/db/migrations.d.ts.map +1 -0
  65. package/dist/db/migrations.js +5 -0
  66. package/dist/db/migrations.js.map +1 -0
  67. package/dist/db/password-resets.d.ts +23 -0
  68. package/dist/db/password-resets.d.ts.map +1 -0
  69. package/dist/db/password-resets.js +37 -0
  70. package/dist/db/password-resets.js.map +1 -0
  71. package/dist/db/rate-limits.d.ts +18 -0
  72. package/dist/db/rate-limits.d.ts.map +1 -0
  73. package/dist/db/rate-limits.js +44 -0
  74. package/dist/db/rate-limits.js.map +1 -0
  75. package/dist/db/refresh-tokens.d.ts +44 -0
  76. package/dist/db/refresh-tokens.d.ts.map +1 -0
  77. package/dist/db/refresh-tokens.js +71 -0
  78. package/dist/db/refresh-tokens.js.map +1 -0
  79. package/dist/db/schema/common.d.ts +2 -0
  80. package/dist/db/schema/common.d.ts.map +1 -0
  81. package/dist/db/schema/common.js +4 -0
  82. package/dist/db/schema/common.js.map +1 -0
  83. package/dist/db/schema/email-verifications.d.ts +114 -0
  84. package/dist/db/schema/email-verifications.d.ts.map +1 -0
  85. package/dist/db/schema/email-verifications.js +35 -0
  86. package/dist/db/schema/email-verifications.js.map +1 -0
  87. package/dist/db/schema/outbox.d.ts +195 -0
  88. package/dist/db/schema/outbox.d.ts.map +1 -0
  89. package/dist/db/schema/outbox.js +5 -0
  90. package/dist/db/schema/outbox.js.map +1 -0
  91. package/dist/db/schema/password-resets.d.ts +114 -0
  92. package/dist/db/schema/password-resets.d.ts.map +1 -0
  93. package/dist/db/schema/password-resets.js +35 -0
  94. package/dist/db/schema/password-resets.js.map +1 -0
  95. package/dist/db/schema/rate-limits.d.ts +145 -0
  96. package/dist/db/schema/rate-limits.d.ts.map +1 -0
  97. package/dist/db/schema/rate-limits.js +25 -0
  98. package/dist/db/schema/rate-limits.js.map +1 -0
  99. package/dist/db/schema/refresh-tokens.d.ts +165 -0
  100. package/dist/db/schema/refresh-tokens.d.ts.map +1 -0
  101. package/dist/db/schema/refresh-tokens.js +47 -0
  102. package/dist/db/schema/refresh-tokens.js.map +1 -0
  103. package/dist/db/schema/users.d.ts +149 -0
  104. package/dist/db/schema/users.d.ts.map +1 -0
  105. package/dist/db/schema/users.js +40 -0
  106. package/dist/db/schema/users.js.map +1 -0
  107. package/dist/db/users.d.ts +28 -0
  108. package/dist/db/users.d.ts.map +1 -0
  109. package/dist/db/users.js +83 -0
  110. package/dist/db/users.js.map +1 -0
  111. package/dist/index.d.ts +14 -0
  112. package/dist/index.d.ts.map +1 -0
  113. package/dist/index.js +52 -0
  114. package/dist/index.js.map +1 -0
  115. package/dist/metrics/index.d.ts +2 -0
  116. package/dist/metrics/index.d.ts.map +1 -0
  117. package/dist/metrics/index.js +3 -0
  118. package/dist/metrics/index.js.map +1 -0
  119. package/dist/metrics/instance.d.ts +16 -0
  120. package/dist/metrics/instance.d.ts.map +1 -0
  121. package/dist/metrics/instance.js +52 -0
  122. package/dist/metrics/instance.js.map +1 -0
  123. package/dist/presentation/auth/bearer-token-auth.d.ts +15 -0
  124. package/dist/presentation/auth/bearer-token-auth.d.ts.map +1 -0
  125. package/dist/presentation/auth/bearer-token-auth.js +31 -0
  126. package/dist/presentation/auth/bearer-token-auth.js.map +1 -0
  127. package/dist/presentation/auth/jwt-auth.d.ts +10 -0
  128. package/dist/presentation/auth/jwt-auth.d.ts.map +1 -0
  129. package/dist/presentation/auth/jwt-auth.js +31 -0
  130. package/dist/presentation/auth/jwt-auth.js.map +1 -0
  131. package/dist/presentation/auth/lease-token-auth.d.ts +15 -0
  132. package/dist/presentation/auth/lease-token-auth.d.ts.map +1 -0
  133. package/dist/presentation/auth/lease-token-auth.js +27 -0
  134. package/dist/presentation/auth/lease-token-auth.js.map +1 -0
  135. package/dist/presentation/auth/refresh-cookie.d.ts +7 -0
  136. package/dist/presentation/auth/refresh-cookie.d.ts.map +1 -0
  137. package/dist/presentation/auth/refresh-cookie.js +29 -0
  138. package/dist/presentation/auth/refresh-cookie.js.map +1 -0
  139. package/dist/presentation/auth/runner-session-auth.d.ts +3 -0
  140. package/dist/presentation/auth/runner-session-auth.d.ts.map +1 -0
  141. package/dist/presentation/auth/runner-session-auth.js +16 -0
  142. package/dist/presentation/auth/runner-session-auth.js.map +1 -0
  143. package/dist/presentation/dto/user.d.ts +8 -0
  144. package/dist/presentation/dto/user.d.ts.map +1 -0
  145. package/dist/presentation/dto/user.js +19 -0
  146. package/dist/presentation/dto/user.js.map +1 -0
  147. package/dist/presentation/e2eRoutes/create-session.d.ts +2 -0
  148. package/dist/presentation/e2eRoutes/create-session.d.ts.map +1 -0
  149. package/dist/presentation/e2eRoutes/create-session.js +49 -0
  150. package/dist/presentation/e2eRoutes/create-session.js.map +1 -0
  151. package/dist/presentation/e2eRoutes/create-user.d.ts +2 -0
  152. package/dist/presentation/e2eRoutes/create-user.d.ts.map +1 -0
  153. package/dist/presentation/e2eRoutes/create-user.js +42 -0
  154. package/dist/presentation/e2eRoutes/create-user.js.map +1 -0
  155. package/dist/presentation/e2eRoutes/index.d.ts +3 -0
  156. package/dist/presentation/e2eRoutes/index.d.ts.map +1 -0
  157. package/dist/presentation/e2eRoutes/index.js +15 -0
  158. package/dist/presentation/e2eRoutes/index.js.map +1 -0
  159. package/dist/presentation/routes/email-verification/verify-email-confirm.d.ts +2 -0
  160. package/dist/presentation/routes/email-verification/verify-email-confirm.d.ts.map +1 -0
  161. package/dist/presentation/routes/email-verification/verify-email-confirm.js +35 -0
  162. package/dist/presentation/routes/email-verification/verify-email-confirm.js.map +1 -0
  163. package/dist/presentation/routes/email-verification/verify-email-resend.d.ts +2 -0
  164. package/dist/presentation/routes/email-verification/verify-email-resend.d.ts.map +1 -0
  165. package/dist/presentation/routes/email-verification/verify-email-resend.js +27 -0
  166. package/dist/presentation/routes/email-verification/verify-email-resend.js.map +1 -0
  167. package/dist/presentation/routes/index.d.ts +3 -0
  168. package/dist/presentation/routes/index.d.ts.map +1 -0
  169. package/dist/presentation/routes/index.js +31 -0
  170. package/dist/presentation/routes/index.js.map +1 -0
  171. package/dist/presentation/routes/password/change-password.d.ts +2 -0
  172. package/dist/presentation/routes/password/change-password.d.ts.map +1 -0
  173. package/dist/presentation/routes/password/change-password.js +50 -0
  174. package/dist/presentation/routes/password/change-password.js.map +1 -0
  175. package/dist/presentation/routes/password/password-reset-confirm.d.ts +2 -0
  176. package/dist/presentation/routes/password/password-reset-confirm.d.ts.map +1 -0
  177. package/dist/presentation/routes/password/password-reset-confirm.js +36 -0
  178. package/dist/presentation/routes/password/password-reset-confirm.js.map +1 -0
  179. package/dist/presentation/routes/password/password-reset-request.d.ts +2 -0
  180. package/dist/presentation/routes/password/password-reset-request.d.ts.map +1 -0
  181. package/dist/presentation/routes/password/password-reset-request.js +26 -0
  182. package/dist/presentation/routes/password/password-reset-request.js.map +1 -0
  183. package/dist/presentation/routes/rate-limit.d.ts +10 -0
  184. package/dist/presentation/routes/rate-limit.d.ts.map +1 -0
  185. package/dist/presentation/routes/rate-limit.js +101 -0
  186. package/dist/presentation/routes/rate-limit.js.map +1 -0
  187. package/dist/presentation/routes/registration/signup.d.ts +2 -0
  188. package/dist/presentation/routes/registration/signup.d.ts.map +1 -0
  189. package/dist/presentation/routes/registration/signup.js +82 -0
  190. package/dist/presentation/routes/registration/signup.js.map +1 -0
  191. package/dist/presentation/routes/session/login.d.ts +2 -0
  192. package/dist/presentation/routes/session/login.d.ts.map +1 -0
  193. package/dist/presentation/routes/session/login.js +48 -0
  194. package/dist/presentation/routes/session/login.js.map +1 -0
  195. package/dist/presentation/routes/session/logout.d.ts +2 -0
  196. package/dist/presentation/routes/session/logout.d.ts.map +1 -0
  197. package/dist/presentation/routes/session/logout.js +25 -0
  198. package/dist/presentation/routes/session/logout.js.map +1 -0
  199. package/dist/presentation/routes/session/me.d.ts +2 -0
  200. package/dist/presentation/routes/session/me.d.ts.map +1 -0
  201. package/dist/presentation/routes/session/me.js +42 -0
  202. package/dist/presentation/routes/session/me.js.map +1 -0
  203. package/dist/presentation/routes/session/refresh.d.ts +2 -0
  204. package/dist/presentation/routes/session/refresh.d.ts.map +1 -0
  205. package/dist/presentation/routes/session/refresh.js +49 -0
  206. package/dist/presentation/routes/session/refresh.js.map +1 -0
  207. package/dist/presentation/subscribers/index.d.ts +3 -0
  208. package/dist/presentation/subscribers/index.d.ts.map +1 -0
  209. package/dist/presentation/subscribers/index.js +4 -0
  210. package/dist/presentation/subscribers/index.js.map +1 -0
  211. package/dist/presentation/subscribers/on-email-verification-send-requested.d.ts +3 -0
  212. package/dist/presentation/subscribers/on-email-verification-send-requested.d.ts.map +1 -0
  213. package/dist/presentation/subscribers/on-email-verification-send-requested.js +13 -0
  214. package/dist/presentation/subscribers/on-email-verification-send-requested.js.map +1 -0
  215. package/dist/presentation/subscribers/on-password-reset-send-requested.d.ts +3 -0
  216. package/dist/presentation/subscribers/on-password-reset-send-requested.d.ts.map +1 -0
  217. package/dist/presentation/subscribers/on-password-reset-send-requested.js +14 -0
  218. package/dist/presentation/subscribers/on-password-reset-send-requested.js.map +1 -0
  219. package/dist/tsconfig.test.tsbuildinfo +1 -0
  220. package/drizzle/0000_initial.sql +81 -0
  221. package/drizzle/meta/0000_snapshot.json +643 -0
  222. package/drizzle/meta/_journal.json +13 -0
  223. package/drizzle.config.ts +7 -0
  224. package/package.json +88 -0
  225. package/src/config.ts +88 -0
  226. package/src/core/auth.test.ts +675 -0
  227. package/src/core/auth.ts +560 -0
  228. package/src/core/entities/email-verification.ts +8 -0
  229. package/src/core/entities/password-reset.ts +8 -0
  230. package/src/core/entities/refresh-token.ts +11 -0
  231. package/src/core/entities/user.ts +12 -0
  232. package/src/core/errors.ts +123 -0
  233. package/src/core/job-lease-token.test.ts +201 -0
  234. package/src/core/job-lease-token.ts +83 -0
  235. package/src/core/jwt.test.ts +180 -0
  236. package/src/core/jwt.ts +66 -0
  237. package/src/core/password.test.ts +28 -0
  238. package/src/core/password.ts +21 -0
  239. package/src/core/rate-limit.test.ts +462 -0
  240. package/src/core/rate-limit.ts +130 -0
  241. package/src/core/runner-session-token.test.ts +126 -0
  242. package/src/core/runner-session-token.ts +49 -0
  243. package/src/db/db.ts +28 -0
  244. package/src/db/email-verifications.test.ts +61 -0
  245. package/src/db/email-verifications.ts +160 -0
  246. package/src/db/migrations.ts +4 -0
  247. package/src/db/password-resets.test.ts +61 -0
  248. package/src/db/password-resets.ts +69 -0
  249. package/src/db/rate-limits.ts +69 -0
  250. package/src/db/refresh-tokens.test.ts +164 -0
  251. package/src/db/refresh-tokens.ts +149 -0
  252. package/src/db/schema/common.ts +3 -0
  253. package/src/db/schema/email-verifications.ts +37 -0
  254. package/src/db/schema/outbox.ts +4 -0
  255. package/src/db/schema/password-resets.ts +37 -0
  256. package/src/db/schema/rate-limits.ts +27 -0
  257. package/src/db/schema/refresh-tokens.ts +43 -0
  258. package/src/db/schema/users.ts +37 -0
  259. package/src/db/users.test.ts +75 -0
  260. package/src/db/users.ts +121 -0
  261. package/src/index.test.ts +39 -0
  262. package/src/index.ts +69 -0
  263. package/src/metrics/index.ts +13 -0
  264. package/src/metrics/instance.test.ts +69 -0
  265. package/src/metrics/instance.ts +77 -0
  266. package/src/presentation/auth/bearer-token-auth.test.ts +99 -0
  267. package/src/presentation/auth/bearer-token-auth.ts +51 -0
  268. package/src/presentation/auth/jwt-auth.test.ts +136 -0
  269. package/src/presentation/auth/jwt-auth.ts +39 -0
  270. package/src/presentation/auth/lease-token-auth.test.ts +56 -0
  271. package/src/presentation/auth/lease-token-auth.ts +25 -0
  272. package/src/presentation/auth/refresh-cookie.test.ts +39 -0
  273. package/src/presentation/auth/refresh-cookie.ts +34 -0
  274. package/src/presentation/auth/runner-session-auth.test.ts +54 -0
  275. package/src/presentation/auth/runner-session-auth.ts +13 -0
  276. package/src/presentation/dto/user.ts +21 -0
  277. package/src/presentation/e2eRoutes/create-session.ts +39 -0
  278. package/src/presentation/e2eRoutes/create-user.ts +38 -0
  279. package/src/presentation/e2eRoutes/index.test.ts +102 -0
  280. package/src/presentation/e2eRoutes/index.ts +10 -0
  281. package/src/presentation/routes/email-verification/verify-email-confirm.test.ts +66 -0
  282. package/src/presentation/routes/email-verification/verify-email-confirm.ts +37 -0
  283. package/src/presentation/routes/email-verification/verify-email-resend.test.ts +100 -0
  284. package/src/presentation/routes/email-verification/verify-email-resend.ts +26 -0
  285. package/src/presentation/routes/index.ts +29 -0
  286. package/src/presentation/routes/password/change-password.test.ts +106 -0
  287. package/src/presentation/routes/password/change-password.ts +45 -0
  288. package/src/presentation/routes/password/password-reset-confirm.test.ts +63 -0
  289. package/src/presentation/routes/password/password-reset-confirm.ts +35 -0
  290. package/src/presentation/routes/password/password-reset-request.test.ts +56 -0
  291. package/src/presentation/routes/password/password-reset-request.ts +25 -0
  292. package/src/presentation/routes/rate-limit.test.ts +341 -0
  293. package/src/presentation/routes/rate-limit.ts +119 -0
  294. package/src/presentation/routes/registration/signup.test.ts +284 -0
  295. package/src/presentation/routes/registration/signup.ts +81 -0
  296. package/src/presentation/routes/session/login.test.ts +77 -0
  297. package/src/presentation/routes/session/login.ts +50 -0
  298. package/src/presentation/routes/session/logout.test.ts +55 -0
  299. package/src/presentation/routes/session/logout.ts +23 -0
  300. package/src/presentation/routes/session/me.test.ts +56 -0
  301. package/src/presentation/routes/session/me.ts +35 -0
  302. package/src/presentation/routes/session/refresh.test.ts +89 -0
  303. package/src/presentation/routes/session/refresh.ts +54 -0
  304. package/src/presentation/subscribers/email-send-requested.test.ts +77 -0
  305. package/src/presentation/subscribers/index.ts +2 -0
  306. package/src/presentation/subscribers/on-email-verification-send-requested.ts +10 -0
  307. package/src/presentation/subscribers/on-password-reset-send-requested.ts +13 -0
  308. package/test/env.ts +10 -0
  309. package/test/factories/index.ts +1 -0
  310. package/test/factories/user.ts +36 -0
  311. package/test/globalSetup.ts +18 -0
  312. package/test/index.ts +1 -0
  313. package/test/routes.ts +275 -0
  314. package/test/setup.ts +17 -0
  315. package/tsconfig.build.json +9 -0
  316. package/tsconfig.build.tsbuildinfo +1 -0
  317. package/tsconfig.json +4 -0
  318. package/tsconfig.test.json +8 -0
  319. package/vitest.config.ts +11 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/db/db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,cAAc,EAAC,MAAM,uBAAuB,CAAC;AASnE,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAOlB,CAAC;AAIF,wBAAgB,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAGjB;AAED,wBAAgB,OAAO,IAAI,IAAI,CAE9B"}
package/dist/db/db.js ADDED
@@ -0,0 +1,28 @@
1
+ import { drizzle } from '@shipfox/node-drizzle';
2
+ import { pgClient } from '@shipfox/node-postgres';
3
+ import { emailVerifications } from './schema/email-verifications.js';
4
+ import { authOutbox } from './schema/outbox.js';
5
+ import { passwordResets } from './schema/password-resets.js';
6
+ import { authRateLimits } from './schema/rate-limits.js';
7
+ import { refreshTokens } from './schema/refresh-tokens.js';
8
+ import { users } from './schema/users.js';
9
+ export const schema = {
10
+ users,
11
+ passwordResets,
12
+ refreshTokens,
13
+ emailVerifications,
14
+ authOutbox,
15
+ authRateLimits
16
+ };
17
+ let _db;
18
+ export function db() {
19
+ if (!_db) _db = drizzle(pgClient(), {
20
+ schema
21
+ });
22
+ return _db;
23
+ }
24
+ export function closeDb() {
25
+ _db = undefined;
26
+ }
27
+
28
+ //# sourceMappingURL=db.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/db/db.ts"],"sourcesContent":["import {drizzle, type NodePgDatabase} from '@shipfox/node-drizzle';\nimport {pgClient} from '@shipfox/node-postgres';\nimport {emailVerifications} from './schema/email-verifications.js';\nimport {authOutbox} from './schema/outbox.js';\nimport {passwordResets} from './schema/password-resets.js';\nimport {authRateLimits} from './schema/rate-limits.js';\nimport {refreshTokens} from './schema/refresh-tokens.js';\nimport {users} from './schema/users.js';\n\nexport const schema = {\n users,\n passwordResets,\n refreshTokens,\n emailVerifications,\n authOutbox,\n authRateLimits,\n};\n\nlet _db: NodePgDatabase<typeof schema> | undefined;\n\nexport function db() {\n if (!_db) _db = drizzle(pgClient(), {schema});\n return _db;\n}\n\nexport function closeDb(): void {\n _db = undefined;\n}\n"],"names":["drizzle","pgClient","emailVerifications","authOutbox","passwordResets","authRateLimits","refreshTokens","users","schema","_db","db","closeDb","undefined"],"mappings":"AAAA,SAAQA,OAAO,QAA4B,wBAAwB;AACnE,SAAQC,QAAQ,QAAO,yBAAyB;AAChD,SAAQC,kBAAkB,QAAO,kCAAkC;AACnE,SAAQC,UAAU,QAAO,qBAAqB;AAC9C,SAAQC,cAAc,QAAO,8BAA8B;AAC3D,SAAQC,cAAc,QAAO,0BAA0B;AACvD,SAAQC,aAAa,QAAO,6BAA6B;AACzD,SAAQC,KAAK,QAAO,oBAAoB;AAExC,OAAO,MAAMC,SAAS;IACpBD;IACAH;IACAE;IACAJ;IACAC;IACAE;AACF,EAAE;AAEF,IAAII;AAEJ,OAAO,SAASC;IACd,IAAI,CAACD,KAAKA,MAAMT,QAAQC,YAAY;QAACO;IAAM;IAC3C,OAAOC;AACT;AAEA,OAAO,SAASE;IACdF,MAAMG;AACR"}
@@ -0,0 +1,39 @@
1
+ import type { EmailVerification } from '#core/entities/email-verification.js';
2
+ import type { User } from '#core/entities/user.js';
3
+ interface CreateEmailVerificationBaseParams {
4
+ userId: string;
5
+ hashedToken: string;
6
+ expiresAt: Date;
7
+ }
8
+ export type CreateEmailVerificationParams = CreateEmailVerificationBaseParams & ({
9
+ sendEmail: {
10
+ email: string;
11
+ verifyLink: string;
12
+ };
13
+ skipEmail?: never;
14
+ } | {
15
+ sendEmail?: never;
16
+ skipEmail: true;
17
+ });
18
+ export declare function createEmailVerification(params: CreateEmailVerificationParams): Promise<EmailVerification>;
19
+ export interface CreateResendEmailVerificationParams {
20
+ email: string;
21
+ hashedToken: string;
22
+ expiresAt: Date;
23
+ cooldownSeconds: number;
24
+ sendEmail: {
25
+ verifyLink: string;
26
+ };
27
+ now?: Date | undefined;
28
+ }
29
+ export interface CreateResendEmailVerificationResult {
30
+ user?: User | undefined;
31
+ verification?: EmailVerification | undefined;
32
+ nextResendAvailableAt: Date;
33
+ }
34
+ export declare function createResendEmailVerification(params: CreateResendEmailVerificationParams): Promise<CreateResendEmailVerificationResult>;
35
+ export declare function consumeEmailVerification(params: {
36
+ hashedToken: string;
37
+ }): Promise<EmailVerification | undefined>;
38
+ export {};
39
+ //# sourceMappingURL=email-verifications.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"email-verifications.d.ts","sourceRoot":"","sources":["../../src/db/email-verifications.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,sCAAsC,CAAC;AAC5E,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,wBAAwB,CAAC;AAMjD,UAAU,iCAAiC;IACzC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,MAAM,6BAA6B,GAAG,iCAAiC,GAC3E,CACI;IAAC,SAAS,EAAE;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAC,CAAC;IAAC,SAAS,CAAC,EAAE,KAAK,CAAA;CAAC,GACnE;IAAC,SAAS,CAAC,EAAE,KAAK,CAAC;IAAC,SAAS,EAAE,IAAI,CAAA;CAAC,CACvC,CAAC;AAEJ,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,6BAA6B,GACpC,OAAO,CAAC,iBAAiB,CAAC,CA0B5B;AAED,MAAM,WAAW,mCAAmC;IAClD,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,IAAI,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE;QAAC,UAAU,EAAE,MAAM,CAAA;KAAC,CAAC;IAChC,GAAG,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;CACxB;AAED,MAAM,WAAW,mCAAmC;IAClD,IAAI,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;IACxB,YAAY,CAAC,EAAE,iBAAiB,GAAG,SAAS,CAAC;IAC7C,qBAAqB,EAAE,IAAI,CAAC;CAC7B;AAMD,wBAAsB,6BAA6B,CACjD,MAAM,EAAE,mCAAmC,GAC1C,OAAO,CAAC,mCAAmC,CAAC,CAkE9C;AAED,wBAAsB,wBAAwB,CAAC,MAAM,EAAE;IACrD,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC,CAgBzC"}
@@ -0,0 +1,95 @@
1
+ import { AUTH_EMAIL_VERIFICATION_SEND_REQUESTED } from '@shipfox/api-auth-dto';
2
+ import { writeOutboxEvent } from '@shipfox/node-outbox';
3
+ import { and, desc, eq, gt, isNull, sql } from 'drizzle-orm';
4
+ import { db } from './db.js';
5
+ import { emailVerifications, toEmailVerification } from './schema/email-verifications.js';
6
+ import { authOutbox } from './schema/outbox.js';
7
+ import { toUser, users } from './schema/users.js';
8
+ export async function createEmailVerification(params) {
9
+ return await db().transaction(async (tx)=>{
10
+ await tx.update(emailVerifications).set({
11
+ usedAt: sql`now()`
12
+ }).where(and(eq(emailVerifications.userId, params.userId), isNull(emailVerifications.usedAt)));
13
+ const rows = await tx.insert(emailVerifications).values({
14
+ userId: params.userId,
15
+ hashedToken: params.hashedToken,
16
+ expiresAt: params.expiresAt
17
+ }).returning();
18
+ const row = rows[0];
19
+ if (!row) throw new Error('Insert returned no rows');
20
+ if (params.sendEmail) {
21
+ await writeOutboxEvent(tx, authOutbox, {
22
+ type: AUTH_EMAIL_VERIFICATION_SEND_REQUESTED,
23
+ payload: params.sendEmail
24
+ });
25
+ }
26
+ return toEmailVerification(row);
27
+ });
28
+ }
29
+ function secondsAfter(date, seconds) {
30
+ return new Date(date.getTime() + seconds * 1000);
31
+ }
32
+ export async function createResendEmailVerification(params) {
33
+ const now = params.now ?? new Date();
34
+ const genericNextResendAvailableAt = secondsAfter(now, params.cooldownSeconds);
35
+ return await db().transaction(async (tx)=>{
36
+ const userRows = await tx.select().from(users).where(eq(users.email, params.email)).limit(1).for('update');
37
+ const userRow = userRows[0];
38
+ if (!userRow) {
39
+ return {
40
+ nextResendAvailableAt: genericNextResendAvailableAt
41
+ };
42
+ }
43
+ const user = toUser(userRow);
44
+ if (user.status !== 'active' || user.emailVerifiedAt !== null) {
45
+ return {
46
+ nextResendAvailableAt: genericNextResendAvailableAt
47
+ };
48
+ }
49
+ const latestRows = await tx.select().from(emailVerifications).where(and(eq(emailVerifications.userId, user.id), isNull(emailVerifications.usedAt))).orderBy(desc(emailVerifications.createdAt)).limit(1);
50
+ const latest = latestRows[0] ? toEmailVerification(latestRows[0]) : undefined;
51
+ if (latest && secondsAfter(latest.createdAt, params.cooldownSeconds) > now) {
52
+ // Return the same public retry estimate as no-op cases so the endpoint does
53
+ // not expose existing account state or prior verification timing.
54
+ return {
55
+ user,
56
+ nextResendAvailableAt: genericNextResendAvailableAt
57
+ };
58
+ }
59
+ await tx.update(emailVerifications).set({
60
+ usedAt: sql`now()`
61
+ }).where(and(eq(emailVerifications.userId, user.id), isNull(emailVerifications.usedAt)));
62
+ const rows = await tx.insert(emailVerifications).values({
63
+ userId: user.id,
64
+ hashedToken: params.hashedToken,
65
+ expiresAt: params.expiresAt
66
+ }).returning();
67
+ const row = rows[0];
68
+ if (!row) throw new Error('Insert returned no rows');
69
+ const verification = toEmailVerification(row);
70
+ if (params.sendEmail) {
71
+ await writeOutboxEvent(tx, authOutbox, {
72
+ type: AUTH_EMAIL_VERIFICATION_SEND_REQUESTED,
73
+ payload: {
74
+ email: user.email,
75
+ verifyLink: params.sendEmail.verifyLink
76
+ }
77
+ });
78
+ }
79
+ return {
80
+ user,
81
+ verification,
82
+ nextResendAvailableAt: secondsAfter(verification.createdAt, params.cooldownSeconds)
83
+ };
84
+ });
85
+ }
86
+ export async function consumeEmailVerification(params) {
87
+ const rows = await db().update(emailVerifications).set({
88
+ usedAt: sql`now()`
89
+ }).where(and(eq(emailVerifications.hashedToken, params.hashedToken), isNull(emailVerifications.usedAt), gt(emailVerifications.expiresAt, sql`now()`))).returning();
90
+ const row = rows[0];
91
+ if (!row) return undefined;
92
+ return toEmailVerification(row);
93
+ }
94
+
95
+ //# sourceMappingURL=email-verifications.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/db/email-verifications.ts"],"sourcesContent":["import {AUTH_EMAIL_VERIFICATION_SEND_REQUESTED, type AuthEventMap} from '@shipfox/api-auth-dto';\nimport {writeOutboxEvent} from '@shipfox/node-outbox';\nimport {and, desc, eq, gt, isNull, sql} from 'drizzle-orm';\nimport type {EmailVerification} from '#core/entities/email-verification.js';\nimport type {User} from '#core/entities/user.js';\nimport {db} from './db.js';\nimport {emailVerifications, toEmailVerification} from './schema/email-verifications.js';\nimport {authOutbox} from './schema/outbox.js';\nimport {toUser, users} from './schema/users.js';\n\ninterface CreateEmailVerificationBaseParams {\n userId: string;\n hashedToken: string;\n expiresAt: Date;\n}\n\nexport type CreateEmailVerificationParams = CreateEmailVerificationBaseParams &\n (\n | {sendEmail: {email: string; verifyLink: string}; skipEmail?: never}\n | {sendEmail?: never; skipEmail: true}\n );\n\nexport async function createEmailVerification(\n params: CreateEmailVerificationParams,\n): Promise<EmailVerification> {\n return await db().transaction(async (tx) => {\n await tx\n .update(emailVerifications)\n .set({usedAt: sql`now()`})\n .where(and(eq(emailVerifications.userId, params.userId), isNull(emailVerifications.usedAt)));\n\n const rows = await tx\n .insert(emailVerifications)\n .values({\n userId: params.userId,\n hashedToken: params.hashedToken,\n expiresAt: params.expiresAt,\n })\n .returning();\n\n const row = rows[0];\n if (!row) throw new Error('Insert returned no rows');\n if (params.sendEmail) {\n await writeOutboxEvent<AuthEventMap>(tx, authOutbox, {\n type: AUTH_EMAIL_VERIFICATION_SEND_REQUESTED,\n payload: params.sendEmail,\n });\n }\n return toEmailVerification(row);\n });\n}\n\nexport interface CreateResendEmailVerificationParams {\n email: string;\n hashedToken: string;\n expiresAt: Date;\n cooldownSeconds: number;\n sendEmail: {verifyLink: string};\n now?: Date | undefined;\n}\n\nexport interface CreateResendEmailVerificationResult {\n user?: User | undefined;\n verification?: EmailVerification | undefined;\n nextResendAvailableAt: Date;\n}\n\nfunction secondsAfter(date: Date, seconds: number): Date {\n return new Date(date.getTime() + seconds * 1000);\n}\n\nexport async function createResendEmailVerification(\n params: CreateResendEmailVerificationParams,\n): Promise<CreateResendEmailVerificationResult> {\n const now = params.now ?? new Date();\n const genericNextResendAvailableAt = secondsAfter(now, params.cooldownSeconds);\n\n return await db().transaction(async (tx) => {\n const userRows = await tx\n .select()\n .from(users)\n .where(eq(users.email, params.email))\n .limit(1)\n .for('update');\n const userRow = userRows[0];\n if (!userRow) {\n return {nextResendAvailableAt: genericNextResendAvailableAt};\n }\n\n const user = toUser(userRow);\n if (user.status !== 'active' || user.emailVerifiedAt !== null) {\n return {nextResendAvailableAt: genericNextResendAvailableAt};\n }\n\n const latestRows = await tx\n .select()\n .from(emailVerifications)\n .where(and(eq(emailVerifications.userId, user.id), isNull(emailVerifications.usedAt)))\n .orderBy(desc(emailVerifications.createdAt))\n .limit(1);\n const latest = latestRows[0] ? toEmailVerification(latestRows[0]) : undefined;\n if (latest && secondsAfter(latest.createdAt, params.cooldownSeconds) > now) {\n // Return the same public retry estimate as no-op cases so the endpoint does\n // not expose existing account state or prior verification timing.\n return {user, nextResendAvailableAt: genericNextResendAvailableAt};\n }\n\n await tx\n .update(emailVerifications)\n .set({usedAt: sql`now()`})\n .where(and(eq(emailVerifications.userId, user.id), isNull(emailVerifications.usedAt)));\n\n const rows = await tx\n .insert(emailVerifications)\n .values({\n userId: user.id,\n hashedToken: params.hashedToken,\n expiresAt: params.expiresAt,\n })\n .returning();\n\n const row = rows[0];\n if (!row) throw new Error('Insert returned no rows');\n const verification = toEmailVerification(row);\n if (params.sendEmail) {\n await writeOutboxEvent<AuthEventMap>(tx, authOutbox, {\n type: AUTH_EMAIL_VERIFICATION_SEND_REQUESTED,\n payload: {\n email: user.email,\n verifyLink: params.sendEmail.verifyLink,\n },\n });\n }\n return {\n user,\n verification,\n nextResendAvailableAt: secondsAfter(verification.createdAt, params.cooldownSeconds),\n };\n });\n}\n\nexport async function consumeEmailVerification(params: {\n hashedToken: string;\n}): Promise<EmailVerification | undefined> {\n const rows = await db()\n .update(emailVerifications)\n .set({usedAt: sql`now()`})\n .where(\n and(\n eq(emailVerifications.hashedToken, params.hashedToken),\n isNull(emailVerifications.usedAt),\n gt(emailVerifications.expiresAt, sql`now()`),\n ),\n )\n .returning();\n\n const row = rows[0];\n if (!row) return undefined;\n return toEmailVerification(row);\n}\n"],"names":["AUTH_EMAIL_VERIFICATION_SEND_REQUESTED","writeOutboxEvent","and","desc","eq","gt","isNull","sql","db","emailVerifications","toEmailVerification","authOutbox","toUser","users","createEmailVerification","params","transaction","tx","update","set","usedAt","where","userId","rows","insert","values","hashedToken","expiresAt","returning","row","Error","sendEmail","type","payload","secondsAfter","date","seconds","Date","getTime","createResendEmailVerification","now","genericNextResendAvailableAt","cooldownSeconds","userRows","select","from","email","limit","for","userRow","nextResendAvailableAt","user","status","emailVerifiedAt","latestRows","id","orderBy","createdAt","latest","undefined","verification","verifyLink","consumeEmailVerification"],"mappings":"AAAA,SAAQA,sCAAsC,QAA0B,wBAAwB;AAChG,SAAQC,gBAAgB,QAAO,uBAAuB;AACtD,SAAQC,GAAG,EAAEC,IAAI,EAAEC,EAAE,EAAEC,EAAE,EAAEC,MAAM,EAAEC,GAAG,QAAO,cAAc;AAG3D,SAAQC,EAAE,QAAO,UAAU;AAC3B,SAAQC,kBAAkB,EAAEC,mBAAmB,QAAO,kCAAkC;AACxF,SAAQC,UAAU,QAAO,qBAAqB;AAC9C,SAAQC,MAAM,EAAEC,KAAK,QAAO,oBAAoB;AAchD,OAAO,eAAeC,wBACpBC,MAAqC;IAErC,OAAO,MAAMP,KAAKQ,WAAW,CAAC,OAAOC;QACnC,MAAMA,GACHC,MAAM,CAACT,oBACPU,GAAG,CAAC;YAACC,QAAQb,GAAG,CAAC,KAAK,CAAC;QAAA,GACvBc,KAAK,CAACnB,IAAIE,GAAGK,mBAAmBa,MAAM,EAAEP,OAAOO,MAAM,GAAGhB,OAAOG,mBAAmBW,MAAM;QAE3F,MAAMG,OAAO,MAAMN,GAChBO,MAAM,CAACf,oBACPgB,MAAM,CAAC;YACNH,QAAQP,OAAOO,MAAM;YACrBI,aAAaX,OAAOW,WAAW;YAC/BC,WAAWZ,OAAOY,SAAS;QAC7B,GACCC,SAAS;QAEZ,MAAMC,MAAMN,IAAI,CAAC,EAAE;QACnB,IAAI,CAACM,KAAK,MAAM,IAAIC,MAAM;QAC1B,IAAIf,OAAOgB,SAAS,EAAE;YACpB,MAAM9B,iBAA+BgB,IAAIN,YAAY;gBACnDqB,MAAMhC;gBACNiC,SAASlB,OAAOgB,SAAS;YAC3B;QACF;QACA,OAAOrB,oBAAoBmB;IAC7B;AACF;AAiBA,SAASK,aAAaC,IAAU,EAAEC,OAAe;IAC/C,OAAO,IAAIC,KAAKF,KAAKG,OAAO,KAAKF,UAAU;AAC7C;AAEA,OAAO,eAAeG,8BACpBxB,MAA2C;IAE3C,MAAMyB,MAAMzB,OAAOyB,GAAG,IAAI,IAAIH;IAC9B,MAAMI,+BAA+BP,aAAaM,KAAKzB,OAAO2B,eAAe;IAE7E,OAAO,MAAMlC,KAAKQ,WAAW,CAAC,OAAOC;QACnC,MAAM0B,WAAW,MAAM1B,GACpB2B,MAAM,GACNC,IAAI,CAAChC,OACLQ,KAAK,CAACjB,GAAGS,MAAMiC,KAAK,EAAE/B,OAAO+B,KAAK,GAClCC,KAAK,CAAC,GACNC,GAAG,CAAC;QACP,MAAMC,UAAUN,QAAQ,CAAC,EAAE;QAC3B,IAAI,CAACM,SAAS;YACZ,OAAO;gBAACC,uBAAuBT;YAA4B;QAC7D;QAEA,MAAMU,OAAOvC,OAAOqC;QACpB,IAAIE,KAAKC,MAAM,KAAK,YAAYD,KAAKE,eAAe,KAAK,MAAM;YAC7D,OAAO;gBAACH,uBAAuBT;YAA4B;QAC7D;QAEA,MAAMa,aAAa,MAAMrC,GACtB2B,MAAM,GACNC,IAAI,CAACpC,oBACLY,KAAK,CAACnB,IAAIE,GAAGK,mBAAmBa,MAAM,EAAE6B,KAAKI,EAAE,GAAGjD,OAAOG,mBAAmBW,MAAM,IAClFoC,OAAO,CAACrD,KAAKM,mBAAmBgD,SAAS,GACzCV,KAAK,CAAC;QACT,MAAMW,SAASJ,UAAU,CAAC,EAAE,GAAG5C,oBAAoB4C,UAAU,CAAC,EAAE,IAAIK;QACpE,IAAID,UAAUxB,aAAawB,OAAOD,SAAS,EAAE1C,OAAO2B,eAAe,IAAIF,KAAK;YAC1E,4EAA4E;YAC5E,kEAAkE;YAClE,OAAO;gBAACW;gBAAMD,uBAAuBT;YAA4B;QACnE;QAEA,MAAMxB,GACHC,MAAM,CAACT,oBACPU,GAAG,CAAC;YAACC,QAAQb,GAAG,CAAC,KAAK,CAAC;QAAA,GACvBc,KAAK,CAACnB,IAAIE,GAAGK,mBAAmBa,MAAM,EAAE6B,KAAKI,EAAE,GAAGjD,OAAOG,mBAAmBW,MAAM;QAErF,MAAMG,OAAO,MAAMN,GAChBO,MAAM,CAACf,oBACPgB,MAAM,CAAC;YACNH,QAAQ6B,KAAKI,EAAE;YACf7B,aAAaX,OAAOW,WAAW;YAC/BC,WAAWZ,OAAOY,SAAS;QAC7B,GACCC,SAAS;QAEZ,MAAMC,MAAMN,IAAI,CAAC,EAAE;QACnB,IAAI,CAACM,KAAK,MAAM,IAAIC,MAAM;QAC1B,MAAM8B,eAAelD,oBAAoBmB;QACzC,IAAId,OAAOgB,SAAS,EAAE;YACpB,MAAM9B,iBAA+BgB,IAAIN,YAAY;gBACnDqB,MAAMhC;gBACNiC,SAAS;oBACPa,OAAOK,KAAKL,KAAK;oBACjBe,YAAY9C,OAAOgB,SAAS,CAAC8B,UAAU;gBACzC;YACF;QACF;QACA,OAAO;YACLV;YACAS;YACAV,uBAAuBhB,aAAa0B,aAAaH,SAAS,EAAE1C,OAAO2B,eAAe;QACpF;IACF;AACF;AAEA,OAAO,eAAeoB,yBAAyB/C,MAE9C;IACC,MAAMQ,OAAO,MAAMf,KAChBU,MAAM,CAACT,oBACPU,GAAG,CAAC;QAACC,QAAQb,GAAG,CAAC,KAAK,CAAC;IAAA,GACvBc,KAAK,CACJnB,IACEE,GAAGK,mBAAmBiB,WAAW,EAAEX,OAAOW,WAAW,GACrDpB,OAAOG,mBAAmBW,MAAM,GAChCf,GAAGI,mBAAmBkB,SAAS,EAAEpB,GAAG,CAAC,KAAK,CAAC,IAG9CqB,SAAS;IAEZ,MAAMC,MAAMN,IAAI,CAAC,EAAE;IACnB,IAAI,CAACM,KAAK,OAAO8B;IACjB,OAAOjD,oBAAoBmB;AAC7B"}
@@ -0,0 +1,2 @@
1
+ export declare const migrationsPath: string;
2
+ //# sourceMappingURL=migrations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrations.d.ts","sourceRoot":"","sources":["../../src/db/migrations.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,cAAc,QAAoE,CAAC"}
@@ -0,0 +1,5 @@
1
+ import { dirname, resolve } from 'node:path';
2
+ import { fileURLToPath } from 'node:url';
3
+ export const migrationsPath = resolve(dirname(fileURLToPath(import.meta.url)), '../../drizzle');
4
+
5
+ //# sourceMappingURL=migrations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/db/migrations.ts"],"sourcesContent":["import {dirname, resolve} from 'node:path';\nimport {fileURLToPath} from 'node:url';\n\nexport const migrationsPath = resolve(dirname(fileURLToPath(import.meta.url)), '../../drizzle');\n"],"names":["dirname","resolve","fileURLToPath","migrationsPath","url"],"mappings":"AAAA,SAAQA,OAAO,EAAEC,OAAO,QAAO,YAAY;AAC3C,SAAQC,aAAa,QAAO,WAAW;AAEvC,OAAO,MAAMC,iBAAiBF,QAAQD,QAAQE,cAAc,YAAYE,GAAG,IAAI,iBAAiB"}
@@ -0,0 +1,23 @@
1
+ import type { PasswordReset } from '#core/entities/password-reset.js';
2
+ interface CreatePasswordResetBaseParams {
3
+ userId: string;
4
+ hashedToken: string;
5
+ expiresAt: Date;
6
+ }
7
+ export type CreatePasswordResetParams = CreatePasswordResetBaseParams & ({
8
+ sendEmail: {
9
+ email: string;
10
+ resetLink: string;
11
+ expiresInHours: number;
12
+ };
13
+ skipEmail?: never;
14
+ } | {
15
+ sendEmail?: never;
16
+ skipEmail: true;
17
+ });
18
+ export declare function createPasswordReset(params: CreatePasswordResetParams): Promise<PasswordReset>;
19
+ export declare function consumePasswordReset(params: {
20
+ hashedToken: string;
21
+ }): Promise<PasswordReset | undefined>;
22
+ export {};
23
+ //# sourceMappingURL=password-resets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"password-resets.d.ts","sourceRoot":"","sources":["../../src/db/password-resets.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,kCAAkC,CAAC;AAKpE,UAAU,6BAA6B;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,MAAM,yBAAyB,GAAG,6BAA6B,GACnE,CACI;IAAC,SAAS,EAAE;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAC,CAAC;IAAC,SAAS,CAAC,EAAE,KAAK,CAAA;CAAC,GAC1F;IAAC,SAAS,CAAC,EAAE,KAAK,CAAC;IAAC,SAAS,EAAE,IAAI,CAAA;CAAC,CACvC,CAAC;AAEJ,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,yBAAyB,GAChC,OAAO,CAAC,aAAa,CAAC,CA0BxB;AAED,wBAAsB,oBAAoB,CAAC,MAAM,EAAE;IACjD,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CAgBrC"}
@@ -0,0 +1,37 @@
1
+ import { AUTH_PASSWORD_RESET_SEND_REQUESTED } from '@shipfox/api-auth-dto';
2
+ import { writeOutboxEvent } from '@shipfox/node-outbox';
3
+ import { and, eq, gt, isNull, sql } from 'drizzle-orm';
4
+ import { db } from './db.js';
5
+ import { authOutbox } from './schema/outbox.js';
6
+ import { passwordResets, toPasswordReset } from './schema/password-resets.js';
7
+ export async function createPasswordReset(params) {
8
+ return await db().transaction(async (tx)=>{
9
+ await tx.update(passwordResets).set({
10
+ usedAt: sql`now()`
11
+ }).where(and(eq(passwordResets.userId, params.userId), isNull(passwordResets.usedAt)));
12
+ const rows = await tx.insert(passwordResets).values({
13
+ userId: params.userId,
14
+ hashedToken: params.hashedToken,
15
+ expiresAt: params.expiresAt
16
+ }).returning();
17
+ const row = rows[0];
18
+ if (!row) throw new Error('Insert returned no rows');
19
+ if (params.sendEmail) {
20
+ await writeOutboxEvent(tx, authOutbox, {
21
+ type: AUTH_PASSWORD_RESET_SEND_REQUESTED,
22
+ payload: params.sendEmail
23
+ });
24
+ }
25
+ return toPasswordReset(row);
26
+ });
27
+ }
28
+ export async function consumePasswordReset(params) {
29
+ const rows = await db().update(passwordResets).set({
30
+ usedAt: sql`now()`
31
+ }).where(and(eq(passwordResets.hashedToken, params.hashedToken), isNull(passwordResets.usedAt), gt(passwordResets.expiresAt, sql`now()`))).returning();
32
+ const row = rows[0];
33
+ if (!row) return undefined;
34
+ return toPasswordReset(row);
35
+ }
36
+
37
+ //# sourceMappingURL=password-resets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/db/password-resets.ts"],"sourcesContent":["import {AUTH_PASSWORD_RESET_SEND_REQUESTED, type AuthEventMap} from '@shipfox/api-auth-dto';\nimport {writeOutboxEvent} from '@shipfox/node-outbox';\nimport {and, eq, gt, isNull, sql} from 'drizzle-orm';\nimport type {PasswordReset} from '#core/entities/password-reset.js';\nimport {db} from './db.js';\nimport {authOutbox} from './schema/outbox.js';\nimport {passwordResets, toPasswordReset} from './schema/password-resets.js';\n\ninterface CreatePasswordResetBaseParams {\n userId: string;\n hashedToken: string;\n expiresAt: Date;\n}\n\nexport type CreatePasswordResetParams = CreatePasswordResetBaseParams &\n (\n | {sendEmail: {email: string; resetLink: string; expiresInHours: number}; skipEmail?: never}\n | {sendEmail?: never; skipEmail: true}\n );\n\nexport async function createPasswordReset(\n params: CreatePasswordResetParams,\n): Promise<PasswordReset> {\n return await db().transaction(async (tx) => {\n await tx\n .update(passwordResets)\n .set({usedAt: sql`now()`})\n .where(and(eq(passwordResets.userId, params.userId), isNull(passwordResets.usedAt)));\n\n const rows = await tx\n .insert(passwordResets)\n .values({\n userId: params.userId,\n hashedToken: params.hashedToken,\n expiresAt: params.expiresAt,\n })\n .returning();\n\n const row = rows[0];\n if (!row) throw new Error('Insert returned no rows');\n if (params.sendEmail) {\n await writeOutboxEvent<AuthEventMap>(tx, authOutbox, {\n type: AUTH_PASSWORD_RESET_SEND_REQUESTED,\n payload: params.sendEmail,\n });\n }\n return toPasswordReset(row);\n });\n}\n\nexport async function consumePasswordReset(params: {\n hashedToken: string;\n}): Promise<PasswordReset | undefined> {\n const rows = await db()\n .update(passwordResets)\n .set({usedAt: sql`now()`})\n .where(\n and(\n eq(passwordResets.hashedToken, params.hashedToken),\n isNull(passwordResets.usedAt),\n gt(passwordResets.expiresAt, sql`now()`),\n ),\n )\n .returning();\n\n const row = rows[0];\n if (!row) return undefined;\n return toPasswordReset(row);\n}\n"],"names":["AUTH_PASSWORD_RESET_SEND_REQUESTED","writeOutboxEvent","and","eq","gt","isNull","sql","db","authOutbox","passwordResets","toPasswordReset","createPasswordReset","params","transaction","tx","update","set","usedAt","where","userId","rows","insert","values","hashedToken","expiresAt","returning","row","Error","sendEmail","type","payload","consumePasswordReset","undefined"],"mappings":"AAAA,SAAQA,kCAAkC,QAA0B,wBAAwB;AAC5F,SAAQC,gBAAgB,QAAO,uBAAuB;AACtD,SAAQC,GAAG,EAAEC,EAAE,EAAEC,EAAE,EAAEC,MAAM,EAAEC,GAAG,QAAO,cAAc;AAErD,SAAQC,EAAE,QAAO,UAAU;AAC3B,SAAQC,UAAU,QAAO,qBAAqB;AAC9C,SAAQC,cAAc,EAAEC,eAAe,QAAO,8BAA8B;AAc5E,OAAO,eAAeC,oBACpBC,MAAiC;IAEjC,OAAO,MAAML,KAAKM,WAAW,CAAC,OAAOC;QACnC,MAAMA,GACHC,MAAM,CAACN,gBACPO,GAAG,CAAC;YAACC,QAAQX,GAAG,CAAC,KAAK,CAAC;QAAA,GACvBY,KAAK,CAAChB,IAAIC,GAAGM,eAAeU,MAAM,EAAEP,OAAOO,MAAM,GAAGd,OAAOI,eAAeQ,MAAM;QAEnF,MAAMG,OAAO,MAAMN,GAChBO,MAAM,CAACZ,gBACPa,MAAM,CAAC;YACNH,QAAQP,OAAOO,MAAM;YACrBI,aAAaX,OAAOW,WAAW;YAC/BC,WAAWZ,OAAOY,SAAS;QAC7B,GACCC,SAAS;QAEZ,MAAMC,MAAMN,IAAI,CAAC,EAAE;QACnB,IAAI,CAACM,KAAK,MAAM,IAAIC,MAAM;QAC1B,IAAIf,OAAOgB,SAAS,EAAE;YACpB,MAAM3B,iBAA+Ba,IAAIN,YAAY;gBACnDqB,MAAM7B;gBACN8B,SAASlB,OAAOgB,SAAS;YAC3B;QACF;QACA,OAAOlB,gBAAgBgB;IACzB;AACF;AAEA,OAAO,eAAeK,qBAAqBnB,MAE1C;IACC,MAAMQ,OAAO,MAAMb,KAChBQ,MAAM,CAACN,gBACPO,GAAG,CAAC;QAACC,QAAQX,GAAG,CAAC,KAAK,CAAC;IAAA,GACvBY,KAAK,CACJhB,IACEC,GAAGM,eAAec,WAAW,EAAEX,OAAOW,WAAW,GACjDlB,OAAOI,eAAeQ,MAAM,GAC5Bb,GAAGK,eAAee,SAAS,EAAElB,GAAG,CAAC,KAAK,CAAC,IAG1CmB,SAAS;IAEZ,MAAMC,MAAMN,IAAI,CAAC,EAAE;IACnB,IAAI,CAACM,KAAK,OAAOM;IACjB,OAAOtB,gBAAgBgB;AACzB"}
@@ -0,0 +1,18 @@
1
+ export interface ConsumeAuthRateLimitParams {
2
+ action: string;
3
+ scope: string;
4
+ identifierHmac: string;
5
+ windowStart: Date;
6
+ expiresAt: Date;
7
+ timeoutMs: number;
8
+ }
9
+ export interface ConsumeAuthRateLimitResult {
10
+ count: number;
11
+ expiresAt: Date;
12
+ }
13
+ export declare function consumeAuthRateLimit(params: ConsumeAuthRateLimitParams): Promise<ConsumeAuthRateLimitResult>;
14
+ export declare function pruneExpiredAuthRateLimits(params?: {
15
+ now?: Date | undefined;
16
+ minIntervalMs?: number | undefined;
17
+ }): Promise<number | undefined>;
18
+ //# sourceMappingURL=rate-limits.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limits.d.ts","sourceRoot":"","sources":["../../src/db/rate-limits.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,IAAI,CAAC;IAClB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,0BAA0B;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,0BAA0B,GACjC,OAAO,CAAC,0BAA0B,CAAC,CAgCrC;AAID,wBAAsB,0BAA0B,CAC9C,MAAM,GAAE;IAAC,GAAG,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAM,GACxE,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAU7B"}
@@ -0,0 +1,44 @@
1
+ import { lt, sql } from 'drizzle-orm';
2
+ import { db } from './db.js';
3
+ import { authRateLimits } from './schema/rate-limits.js';
4
+ export async function consumeAuthRateLimit(params) {
5
+ return await db().transaction(async (tx)=>{
6
+ await tx.execute(sql`select set_config('statement_timeout', ${`${params.timeoutMs}ms`}, true)`);
7
+ const rows = await tx.insert(authRateLimits).values({
8
+ action: params.action,
9
+ scope: params.scope,
10
+ identifierHmac: params.identifierHmac,
11
+ windowStart: params.windowStart,
12
+ count: 1,
13
+ expiresAt: params.expiresAt
14
+ }).onConflictDoUpdate({
15
+ target: [
16
+ authRateLimits.action,
17
+ authRateLimits.scope,
18
+ authRateLimits.identifierHmac,
19
+ authRateLimits.windowStart
20
+ ],
21
+ set: {
22
+ count: sql`${authRateLimits.count} + 1`,
23
+ updatedAt: sql`now()`
24
+ }
25
+ }).returning({
26
+ count: authRateLimits.count,
27
+ expiresAt: authRateLimits.expiresAt
28
+ });
29
+ const row = rows[0];
30
+ if (!row) throw new Error('Rate limit upsert returned no rows');
31
+ return row;
32
+ });
33
+ }
34
+ let nextPruneAt = 0;
35
+ export async function pruneExpiredAuthRateLimits(params = {}) {
36
+ const now = params.now ?? new Date();
37
+ const minIntervalMs = params.minIntervalMs ?? 60_000;
38
+ if (minIntervalMs > 0 && now.getTime() < nextPruneAt) return undefined;
39
+ nextPruneAt = now.getTime() + minIntervalMs;
40
+ const result = await db().delete(authRateLimits).where(lt(authRateLimits.expiresAt, now));
41
+ return result.rowCount ?? 0;
42
+ }
43
+
44
+ //# sourceMappingURL=rate-limits.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/db/rate-limits.ts"],"sourcesContent":["import {lt, sql} from 'drizzle-orm';\nimport {db} from './db.js';\nimport {authRateLimits} from './schema/rate-limits.js';\n\nexport interface ConsumeAuthRateLimitParams {\n action: string;\n scope: string;\n identifierHmac: string;\n windowStart: Date;\n expiresAt: Date;\n timeoutMs: number;\n}\n\nexport interface ConsumeAuthRateLimitResult {\n count: number;\n expiresAt: Date;\n}\n\nexport async function consumeAuthRateLimit(\n params: ConsumeAuthRateLimitParams,\n): Promise<ConsumeAuthRateLimitResult> {\n return await db().transaction(async (tx) => {\n await tx.execute(sql`select set_config('statement_timeout', ${`${params.timeoutMs}ms`}, true)`);\n\n const rows = await tx\n .insert(authRateLimits)\n .values({\n action: params.action,\n scope: params.scope,\n identifierHmac: params.identifierHmac,\n windowStart: params.windowStart,\n count: 1,\n expiresAt: params.expiresAt,\n })\n .onConflictDoUpdate({\n target: [\n authRateLimits.action,\n authRateLimits.scope,\n authRateLimits.identifierHmac,\n authRateLimits.windowStart,\n ],\n set: {\n count: sql`${authRateLimits.count} + 1`,\n updatedAt: sql`now()`,\n },\n })\n .returning({count: authRateLimits.count, expiresAt: authRateLimits.expiresAt});\n\n const row = rows[0];\n if (!row) throw new Error('Rate limit upsert returned no rows');\n return row;\n });\n}\n\nlet nextPruneAt = 0;\n\nexport async function pruneExpiredAuthRateLimits(\n params: {now?: Date | undefined; minIntervalMs?: number | undefined} = {},\n): Promise<number | undefined> {\n const now = params.now ?? new Date();\n const minIntervalMs = params.minIntervalMs ?? 60_000;\n if (minIntervalMs > 0 && now.getTime() < nextPruneAt) return undefined;\n\n nextPruneAt = now.getTime() + minIntervalMs;\n\n const result = await db().delete(authRateLimits).where(lt(authRateLimits.expiresAt, now));\n\n return result.rowCount ?? 0;\n}\n"],"names":["lt","sql","db","authRateLimits","consumeAuthRateLimit","params","transaction","tx","execute","timeoutMs","rows","insert","values","action","scope","identifierHmac","windowStart","count","expiresAt","onConflictDoUpdate","target","set","updatedAt","returning","row","Error","nextPruneAt","pruneExpiredAuthRateLimits","now","Date","minIntervalMs","getTime","undefined","result","delete","where","rowCount"],"mappings":"AAAA,SAAQA,EAAE,EAAEC,GAAG,QAAO,cAAc;AACpC,SAAQC,EAAE,QAAO,UAAU;AAC3B,SAAQC,cAAc,QAAO,0BAA0B;AAgBvD,OAAO,eAAeC,qBACpBC,MAAkC;IAElC,OAAO,MAAMH,KAAKI,WAAW,CAAC,OAAOC;QACnC,MAAMA,GAAGC,OAAO,CAACP,GAAG,CAAC,uCAAuC,EAAE,GAAGI,OAAOI,SAAS,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC;QAE9F,MAAMC,OAAO,MAAMH,GAChBI,MAAM,CAACR,gBACPS,MAAM,CAAC;YACNC,QAAQR,OAAOQ,MAAM;YACrBC,OAAOT,OAAOS,KAAK;YACnBC,gBAAgBV,OAAOU,cAAc;YACrCC,aAAaX,OAAOW,WAAW;YAC/BC,OAAO;YACPC,WAAWb,OAAOa,SAAS;QAC7B,GACCC,kBAAkB,CAAC;YAClBC,QAAQ;gBACNjB,eAAeU,MAAM;gBACrBV,eAAeW,KAAK;gBACpBX,eAAeY,cAAc;gBAC7BZ,eAAea,WAAW;aAC3B;YACDK,KAAK;gBACHJ,OAAOhB,GAAG,CAAC,EAAEE,eAAec,KAAK,CAAC,IAAI,CAAC;gBACvCK,WAAWrB,GAAG,CAAC,KAAK,CAAC;YACvB;QACF,GACCsB,SAAS,CAAC;YAACN,OAAOd,eAAec,KAAK;YAAEC,WAAWf,eAAee,SAAS;QAAA;QAE9E,MAAMM,MAAMd,IAAI,CAAC,EAAE;QACnB,IAAI,CAACc,KAAK,MAAM,IAAIC,MAAM;QAC1B,OAAOD;IACT;AACF;AAEA,IAAIE,cAAc;AAElB,OAAO,eAAeC,2BACpBtB,SAAuE,CAAC,CAAC;IAEzE,MAAMuB,MAAMvB,OAAOuB,GAAG,IAAI,IAAIC;IAC9B,MAAMC,gBAAgBzB,OAAOyB,aAAa,IAAI;IAC9C,IAAIA,gBAAgB,KAAKF,IAAIG,OAAO,KAAKL,aAAa,OAAOM;IAE7DN,cAAcE,IAAIG,OAAO,KAAKD;IAE9B,MAAMG,SAAS,MAAM/B,KAAKgC,MAAM,CAAC/B,gBAAgBgC,KAAK,CAACnC,GAAGG,eAAee,SAAS,EAAEU;IAEpF,OAAOK,OAAOG,QAAQ,IAAI;AAC5B"}
@@ -0,0 +1,44 @@
1
+ import type { RefreshToken } from '#core/entities/refresh-token.js';
2
+ export interface CreateRefreshTokenParams {
3
+ userId: string;
4
+ hashedToken: string;
5
+ expiresAt: Date;
6
+ }
7
+ export declare function createRefreshToken(params: CreateRefreshTokenParams): Promise<RefreshToken>;
8
+ /**
9
+ * Looks up the live session token by hash — one that can still authenticate as
10
+ * the current session. Returns `undefined` for revoked, expired, or already
11
+ * rotated tokens; rotated rows survive in the table only for the grace window
12
+ * and are not "active".
13
+ */
14
+ export declare function findActiveRefreshTokenByHash(params: {
15
+ hashedToken: string;
16
+ }): Promise<RefreshToken | undefined>;
17
+ /**
18
+ * Looks up a non-revoked, non-expired token by hash, including ones already
19
+ * rotated. Rotation reads {@link RefreshToken.rotatedAt} to decide whether to
20
+ * rotate, honour the grace window, or reject reuse, so — unlike
21
+ * {@link findActiveRefreshTokenByHash} — it must still see rotated rows.
22
+ */
23
+ export declare function findRefreshTokenByHash(params: {
24
+ hashedToken: string;
25
+ }): Promise<RefreshToken | undefined>;
26
+ /**
27
+ * Atomically claims rotation and inserts the successor token. The predecessor is
28
+ * marked rather than deleted so later reuse of the retired token can be detected.
29
+ * If the successor insert fails, the predecessor rotation rolls back.
30
+ */
31
+ export declare function rotateRefreshToken(params: {
32
+ id: string;
33
+ currentHashedToken: string;
34
+ nextHashedToken: string;
35
+ expiresAt: Date;
36
+ }): Promise<RefreshToken | undefined>;
37
+ export declare function revokeRefreshTokenByHash(params: {
38
+ hashedToken: string;
39
+ }): Promise<void>;
40
+ export declare function revokeRefreshTokensForUser(params: {
41
+ userId: string;
42
+ exceptRefreshTokenId?: string | undefined;
43
+ }): Promise<void>;
44
+ //# sourceMappingURL=refresh-tokens.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refresh-tokens.d.ts","sourceRoot":"","sources":["../../src/db/refresh-tokens.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,iCAAiC,CAAC;AAIlE,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,wBAAwB,GAAG,OAAO,CAAC,YAAY,CAAC,CAahG;AAED;;;;;GAKG;AACH,wBAAsB,4BAA4B,CAAC,MAAM,EAAE;IACzD,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAiBpC;AAED;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAAC,MAAM,EAAE;IACnD,WAAW,EAAE,MAAM,CAAC;CACrB,GAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAgBpC;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,MAAM,EAAE;IAC/C,EAAE,EAAE,MAAM,CAAC;IACX,kBAAkB,EAAE,MAAM,CAAC;IAC3B,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,IAAI,CAAC;CACjB,GAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAoCpC;AAED,wBAAsB,wBAAwB,CAAC,MAAM,EAAE;IAAC,WAAW,EAAE,MAAM,CAAA;CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAK3F;AAED,wBAAsB,0BAA0B,CAAC,MAAM,EAAE;IACvD,MAAM,EAAE,MAAM,CAAC;IACf,oBAAoB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3C,GAAG,OAAO,CAAC,IAAI,CAAC,CAWhB"}
@@ -0,0 +1,71 @@
1
+ import { and, eq, gt, isNull, ne, sql } from 'drizzle-orm';
2
+ import { db } from './db.js';
3
+ import { refreshTokens, toRefreshToken } from './schema/refresh-tokens.js';
4
+ export async function createRefreshToken(params) {
5
+ const rows = await db().insert(refreshTokens).values({
6
+ userId: params.userId,
7
+ hashedToken: params.hashedToken,
8
+ expiresAt: params.expiresAt
9
+ }).returning();
10
+ const row = rows[0];
11
+ if (!row) throw new Error('Insert returned no rows');
12
+ return toRefreshToken(row);
13
+ }
14
+ /**
15
+ * Looks up the live session token by hash — one that can still authenticate as
16
+ * the current session. Returns `undefined` for revoked, expired, or already
17
+ * rotated tokens; rotated rows survive in the table only for the grace window
18
+ * and are not "active".
19
+ */ export async function findActiveRefreshTokenByHash(params) {
20
+ const rows = await db().select().from(refreshTokens).where(and(eq(refreshTokens.hashedToken, params.hashedToken), isNull(refreshTokens.revokedAt), isNull(refreshTokens.rotatedAt), gt(refreshTokens.expiresAt, sql`now()`))).limit(1);
21
+ const row = rows[0];
22
+ if (!row) return undefined;
23
+ return toRefreshToken(row);
24
+ }
25
+ /**
26
+ * Looks up a non-revoked, non-expired token by hash, including ones already
27
+ * rotated. Rotation reads {@link RefreshToken.rotatedAt} to decide whether to
28
+ * rotate, honour the grace window, or reject reuse, so — unlike
29
+ * {@link findActiveRefreshTokenByHash} — it must still see rotated rows.
30
+ */ export async function findRefreshTokenByHash(params) {
31
+ const rows = await db().select().from(refreshTokens).where(and(eq(refreshTokens.hashedToken, params.hashedToken), isNull(refreshTokens.revokedAt), gt(refreshTokens.expiresAt, sql`now()`))).limit(1);
32
+ const row = rows[0];
33
+ if (!row) return undefined;
34
+ return toRefreshToken(row);
35
+ }
36
+ /**
37
+ * Atomically claims rotation and inserts the successor token. The predecessor is
38
+ * marked rather than deleted so later reuse of the retired token can be detected.
39
+ * If the successor insert fails, the predecessor rotation rolls back.
40
+ */ export async function rotateRefreshToken(params) {
41
+ return await db().transaction(async (tx)=>{
42
+ const rotatedRows = await tx.update(refreshTokens).set({
43
+ rotatedAt: sql`now()`,
44
+ lastUsedAt: sql`now()`,
45
+ updatedAt: sql`now()`
46
+ }).where(and(eq(refreshTokens.id, params.id), eq(refreshTokens.hashedToken, params.currentHashedToken), isNull(refreshTokens.revokedAt), isNull(refreshTokens.rotatedAt), gt(refreshTokens.expiresAt, sql`now()`))).returning();
47
+ const rotated = rotatedRows[0];
48
+ if (!rotated) return undefined;
49
+ const successorRows = await tx.insert(refreshTokens).values({
50
+ userId: rotated.userId,
51
+ hashedToken: params.nextHashedToken,
52
+ expiresAt: params.expiresAt
53
+ }).returning();
54
+ if (!successorRows[0]) throw new Error('Insert returned no rows');
55
+ return toRefreshToken(rotated);
56
+ });
57
+ }
58
+ export async function revokeRefreshTokenByHash(params) {
59
+ await db().update(refreshTokens).set({
60
+ revokedAt: sql`now()`,
61
+ updatedAt: sql`now()`
62
+ }).where(and(eq(refreshTokens.hashedToken, params.hashedToken), isNull(refreshTokens.revokedAt)));
63
+ }
64
+ export async function revokeRefreshTokensForUser(params) {
65
+ await db().update(refreshTokens).set({
66
+ revokedAt: sql`now()`,
67
+ updatedAt: sql`now()`
68
+ }).where(and(eq(refreshTokens.userId, params.userId), isNull(refreshTokens.revokedAt), params.exceptRefreshTokenId ? ne(refreshTokens.id, params.exceptRefreshTokenId) : undefined));
69
+ }
70
+
71
+ //# sourceMappingURL=refresh-tokens.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/db/refresh-tokens.ts"],"sourcesContent":["import {and, eq, gt, isNull, ne, sql} from 'drizzle-orm';\nimport type {RefreshToken} from '#core/entities/refresh-token.js';\nimport {db} from './db.js';\nimport {refreshTokens, toRefreshToken} from './schema/refresh-tokens.js';\n\nexport interface CreateRefreshTokenParams {\n userId: string;\n hashedToken: string;\n expiresAt: Date;\n}\n\nexport async function createRefreshToken(params: CreateRefreshTokenParams): Promise<RefreshToken> {\n const rows = await db()\n .insert(refreshTokens)\n .values({\n userId: params.userId,\n hashedToken: params.hashedToken,\n expiresAt: params.expiresAt,\n })\n .returning();\n\n const row = rows[0];\n if (!row) throw new Error('Insert returned no rows');\n return toRefreshToken(row);\n}\n\n/**\n * Looks up the live session token by hash — one that can still authenticate as\n * the current session. Returns `undefined` for revoked, expired, or already\n * rotated tokens; rotated rows survive in the table only for the grace window\n * and are not \"active\".\n */\nexport async function findActiveRefreshTokenByHash(params: {\n hashedToken: string;\n}): Promise<RefreshToken | undefined> {\n const rows = await db()\n .select()\n .from(refreshTokens)\n .where(\n and(\n eq(refreshTokens.hashedToken, params.hashedToken),\n isNull(refreshTokens.revokedAt),\n isNull(refreshTokens.rotatedAt),\n gt(refreshTokens.expiresAt, sql`now()`),\n ),\n )\n .limit(1);\n\n const row = rows[0];\n if (!row) return undefined;\n return toRefreshToken(row);\n}\n\n/**\n * Looks up a non-revoked, non-expired token by hash, including ones already\n * rotated. Rotation reads {@link RefreshToken.rotatedAt} to decide whether to\n * rotate, honour the grace window, or reject reuse, so — unlike\n * {@link findActiveRefreshTokenByHash} — it must still see rotated rows.\n */\nexport async function findRefreshTokenByHash(params: {\n hashedToken: string;\n}): Promise<RefreshToken | undefined> {\n const rows = await db()\n .select()\n .from(refreshTokens)\n .where(\n and(\n eq(refreshTokens.hashedToken, params.hashedToken),\n isNull(refreshTokens.revokedAt),\n gt(refreshTokens.expiresAt, sql`now()`),\n ),\n )\n .limit(1);\n\n const row = rows[0];\n if (!row) return undefined;\n return toRefreshToken(row);\n}\n\n/**\n * Atomically claims rotation and inserts the successor token. The predecessor is\n * marked rather than deleted so later reuse of the retired token can be detected.\n * If the successor insert fails, the predecessor rotation rolls back.\n */\nexport async function rotateRefreshToken(params: {\n id: string;\n currentHashedToken: string;\n nextHashedToken: string;\n expiresAt: Date;\n}): Promise<RefreshToken | undefined> {\n return await db().transaction(async (tx) => {\n const rotatedRows = await tx\n .update(refreshTokens)\n .set({\n rotatedAt: sql`now()`,\n lastUsedAt: sql`now()`,\n updatedAt: sql`now()`,\n })\n .where(\n and(\n eq(refreshTokens.id, params.id),\n eq(refreshTokens.hashedToken, params.currentHashedToken),\n isNull(refreshTokens.revokedAt),\n isNull(refreshTokens.rotatedAt),\n gt(refreshTokens.expiresAt, sql`now()`),\n ),\n )\n .returning();\n\n const rotated = rotatedRows[0];\n if (!rotated) return undefined;\n\n const successorRows = await tx\n .insert(refreshTokens)\n .values({\n userId: rotated.userId,\n hashedToken: params.nextHashedToken,\n expiresAt: params.expiresAt,\n })\n .returning();\n\n if (!successorRows[0]) throw new Error('Insert returned no rows');\n\n return toRefreshToken(rotated);\n });\n}\n\nexport async function revokeRefreshTokenByHash(params: {hashedToken: string}): Promise<void> {\n await db()\n .update(refreshTokens)\n .set({revokedAt: sql`now()`, updatedAt: sql`now()`})\n .where(and(eq(refreshTokens.hashedToken, params.hashedToken), isNull(refreshTokens.revokedAt)));\n}\n\nexport async function revokeRefreshTokensForUser(params: {\n userId: string;\n exceptRefreshTokenId?: string | undefined;\n}): Promise<void> {\n await db()\n .update(refreshTokens)\n .set({revokedAt: sql`now()`, updatedAt: sql`now()`})\n .where(\n and(\n eq(refreshTokens.userId, params.userId),\n isNull(refreshTokens.revokedAt),\n params.exceptRefreshTokenId ? ne(refreshTokens.id, params.exceptRefreshTokenId) : undefined,\n ),\n );\n}\n"],"names":["and","eq","gt","isNull","ne","sql","db","refreshTokens","toRefreshToken","createRefreshToken","params","rows","insert","values","userId","hashedToken","expiresAt","returning","row","Error","findActiveRefreshTokenByHash","select","from","where","revokedAt","rotatedAt","limit","undefined","findRefreshTokenByHash","rotateRefreshToken","transaction","tx","rotatedRows","update","set","lastUsedAt","updatedAt","id","currentHashedToken","rotated","successorRows","nextHashedToken","revokeRefreshTokenByHash","revokeRefreshTokensForUser","exceptRefreshTokenId"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare const pgTable: import("drizzle-orm/pg-core").PgTableFn<undefined>;
2
+ //# sourceMappingURL=common.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../../src/db/schema/common.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,OAAO,oDAA2C,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { pgTableCreator } from 'drizzle-orm/pg-core';
2
+ export const pgTable = pgTableCreator((name)=>`auth_${name}`);
3
+
4
+ //# sourceMappingURL=common.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/db/schema/common.ts"],"sourcesContent":["import {pgTableCreator} from 'drizzle-orm/pg-core';\n\nexport const pgTable = pgTableCreator((name) => `auth_${name}`);\n"],"names":["pgTableCreator","pgTable","name"],"mappings":"AAAA,SAAQA,cAAc,QAAO,sBAAsB;AAEnD,OAAO,MAAMC,UAAUD,eAAe,CAACE,OAAS,CAAC,KAAK,EAAEA,MAAM,EAAE"}