@better-auth/core 1.5.0-beta.8 → 1.5.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 (313) hide show
  1. package/README.md +17 -0
  2. package/dist/api/index.d.mts +144 -41
  3. package/dist/api/index.mjs +2 -1
  4. package/dist/api/index.mjs.map +1 -0
  5. package/dist/async_hooks/index.d.mts +2 -1
  6. package/dist/async_hooks/index.mjs +2 -1
  7. package/dist/async_hooks/index.mjs.map +1 -0
  8. package/dist/async_hooks/pure.index.d.mts +2 -1
  9. package/dist/async_hooks/pure.index.mjs +2 -1
  10. package/dist/async_hooks/pure.index.mjs.map +1 -0
  11. package/dist/context/endpoint-context.d.mts +2 -1
  12. package/dist/context/endpoint-context.mjs +4 -3
  13. package/dist/context/endpoint-context.mjs.map +1 -0
  14. package/dist/context/global.d.mts +2 -2
  15. package/dist/context/global.mjs +3 -2
  16. package/dist/context/global.mjs.map +1 -0
  17. package/dist/context/index.d.mts +2 -2
  18. package/dist/context/index.mjs +2 -2
  19. package/dist/context/request-state.d.mts +2 -1
  20. package/dist/context/request-state.mjs +4 -3
  21. package/dist/context/request-state.mjs.map +1 -0
  22. package/dist/context/transaction.d.mts +12 -3
  23. package/dist/context/transaction.mjs +55 -11
  24. package/dist/context/transaction.mjs.map +1 -0
  25. package/dist/db/adapter/factory.d.mts +6 -13
  26. package/dist/db/adapter/factory.mjs +44 -57
  27. package/dist/db/adapter/factory.mjs.map +1 -0
  28. package/dist/db/adapter/get-default-field-name.d.mts +2 -1
  29. package/dist/db/adapter/get-default-field-name.mjs +3 -2
  30. package/dist/db/adapter/get-default-field-name.mjs.map +1 -0
  31. package/dist/db/adapter/get-default-model-name.d.mts +2 -1
  32. package/dist/db/adapter/get-default-model-name.mjs +5 -4
  33. package/dist/db/adapter/get-default-model-name.mjs.map +1 -0
  34. package/dist/db/adapter/get-field-attributes.d.mts +3 -2
  35. package/dist/db/adapter/get-field-attributes.mjs +2 -1
  36. package/dist/db/adapter/get-field-attributes.mjs.map +1 -0
  37. package/dist/db/adapter/get-field-name.d.mts +2 -1
  38. package/dist/db/adapter/get-field-name.mjs +2 -1
  39. package/dist/db/adapter/get-field-name.mjs.map +1 -0
  40. package/dist/db/adapter/get-id-field.d.mts +3 -2
  41. package/dist/db/adapter/get-id-field.mjs +3 -2
  42. package/dist/db/adapter/get-id-field.mjs.map +1 -0
  43. package/dist/db/adapter/get-model-name.d.mts +2 -1
  44. package/dist/db/adapter/get-model-name.mjs +2 -1
  45. package/dist/db/adapter/get-model-name.mjs.map +1 -0
  46. package/dist/db/adapter/index.d.mts +10 -4
  47. package/dist/db/adapter/index.mjs +19 -2
  48. package/dist/db/adapter/index.mjs.map +1 -0
  49. package/dist/db/adapter/types.d.mts +3 -34
  50. package/dist/db/adapter/utils.d.mts +2 -1
  51. package/dist/db/adapter/utils.mjs +2 -1
  52. package/dist/db/adapter/utils.mjs.map +1 -0
  53. package/dist/db/get-tables.d.mts +2 -1
  54. package/dist/db/get-tables.mjs +46 -39
  55. package/dist/db/get-tables.mjs.map +1 -0
  56. package/dist/db/index.d.mts +7 -7
  57. package/dist/db/plugin.d.mts +2 -1
  58. package/dist/db/schema/account.d.mts +8 -4
  59. package/dist/db/schema/account.mjs +2 -1
  60. package/dist/db/schema/account.mjs.map +1 -0
  61. package/dist/db/schema/rate-limit.d.mts +8 -2
  62. package/dist/db/schema/rate-limit.mjs +2 -1
  63. package/dist/db/schema/rate-limit.mjs.map +1 -0
  64. package/dist/db/schema/session.d.mts +8 -4
  65. package/dist/db/schema/session.mjs +2 -1
  66. package/dist/db/schema/session.mjs.map +1 -0
  67. package/dist/db/schema/shared.d.mts +2 -1
  68. package/dist/db/schema/shared.mjs +2 -1
  69. package/dist/db/schema/shared.mjs.map +1 -0
  70. package/dist/db/schema/user.d.mts +8 -4
  71. package/dist/db/schema/user.mjs +2 -1
  72. package/dist/db/schema/user.mjs.map +1 -0
  73. package/dist/db/schema/verification.d.mts +8 -4
  74. package/dist/db/schema/verification.mjs +2 -1
  75. package/dist/db/schema/verification.mjs.map +1 -0
  76. package/dist/db/type.d.mts +28 -2
  77. package/dist/env/color-depth.d.mts +2 -1
  78. package/dist/env/color-depth.mjs +2 -1
  79. package/dist/env/color-depth.mjs.map +1 -0
  80. package/dist/env/env-impl.d.mts +3 -2
  81. package/dist/env/env-impl.mjs +9 -8
  82. package/dist/env/env-impl.mjs.map +1 -0
  83. package/dist/env/logger.d.mts +2 -1
  84. package/dist/env/logger.mjs +3 -2
  85. package/dist/env/logger.mjs.map +1 -0
  86. package/dist/error/codes.d.mts +64 -181
  87. package/dist/error/codes.mjs +6 -2
  88. package/dist/error/codes.mjs.map +1 -0
  89. package/dist/error/index.d.mts +2 -1
  90. package/dist/error/index.mjs +2 -1
  91. package/dist/error/index.mjs.map +1 -0
  92. package/dist/index.d.mts +5 -4
  93. package/dist/oauth2/client-credentials-token.d.mts +25 -3
  94. package/dist/oauth2/client-credentials-token.mjs +15 -2
  95. package/dist/oauth2/client-credentials-token.mjs.map +1 -0
  96. package/dist/oauth2/create-authorization-url.d.mts +5 -2
  97. package/dist/oauth2/create-authorization-url.mjs +3 -1
  98. package/dist/oauth2/create-authorization-url.mjs.map +1 -0
  99. package/dist/oauth2/index.d.mts +4 -4
  100. package/dist/oauth2/index.mjs +4 -4
  101. package/dist/oauth2/oauth-provider.d.mts +3 -2
  102. package/dist/oauth2/refresh-access-token.d.mts +24 -4
  103. package/dist/oauth2/refresh-access-token.mjs +20 -2
  104. package/dist/oauth2/refresh-access-token.mjs.map +1 -0
  105. package/dist/oauth2/utils.d.mts +2 -1
  106. package/dist/oauth2/utils.mjs +2 -1
  107. package/dist/oauth2/utils.mjs.map +1 -0
  108. package/dist/oauth2/validate-authorization-code.d.mts +37 -4
  109. package/dist/oauth2/validate-authorization-code.mjs +25 -13
  110. package/dist/oauth2/validate-authorization-code.mjs.map +1 -0
  111. package/dist/oauth2/verify.d.mts +7 -13
  112. package/dist/oauth2/verify.mjs +2 -1
  113. package/dist/oauth2/verify.mjs.map +1 -0
  114. package/dist/social-providers/apple.d.mts +2 -1
  115. package/dist/social-providers/apple.mjs +22 -21
  116. package/dist/social-providers/apple.mjs.map +1 -0
  117. package/dist/social-providers/atlassian.d.mts +2 -1
  118. package/dist/social-providers/atlassian.mjs +2 -1
  119. package/dist/social-providers/atlassian.mjs.map +1 -0
  120. package/dist/social-providers/cognito.d.mts +2 -1
  121. package/dist/social-providers/cognito.mjs +4 -3
  122. package/dist/social-providers/cognito.mjs.map +1 -0
  123. package/dist/social-providers/discord.d.mts +2 -1
  124. package/dist/social-providers/discord.mjs +2 -1
  125. package/dist/social-providers/discord.mjs.map +1 -0
  126. package/dist/social-providers/dropbox.d.mts +2 -1
  127. package/dist/social-providers/dropbox.mjs +3 -2
  128. package/dist/social-providers/dropbox.mjs.map +1 -0
  129. package/dist/social-providers/facebook.d.mts +2 -1
  130. package/dist/social-providers/facebook.mjs +13 -12
  131. package/dist/social-providers/facebook.mjs.map +1 -0
  132. package/dist/social-providers/figma.d.mts +2 -1
  133. package/dist/social-providers/figma.mjs +2 -1
  134. package/dist/social-providers/figma.mjs.map +1 -0
  135. package/dist/social-providers/github.d.mts +3 -2
  136. package/dist/social-providers/github.mjs +23 -6
  137. package/dist/social-providers/github.mjs.map +1 -0
  138. package/dist/social-providers/gitlab.d.mts +2 -1
  139. package/dist/social-providers/gitlab.mjs +3 -2
  140. package/dist/social-providers/gitlab.mjs.map +1 -0
  141. package/dist/social-providers/google.d.mts +2 -1
  142. package/dist/social-providers/google.mjs +18 -13
  143. package/dist/social-providers/google.mjs.map +1 -0
  144. package/dist/social-providers/huggingface.d.mts +2 -1
  145. package/dist/social-providers/huggingface.mjs +3 -2
  146. package/dist/social-providers/huggingface.mjs.map +1 -0
  147. package/dist/social-providers/index.d.mts +61 -8
  148. package/dist/social-providers/index.mjs +5 -2
  149. package/dist/social-providers/index.mjs.map +1 -0
  150. package/dist/social-providers/kakao.d.mts +3 -2
  151. package/dist/social-providers/kakao.mjs +3 -2
  152. package/dist/social-providers/kakao.mjs.map +1 -0
  153. package/dist/social-providers/kick.d.mts +2 -1
  154. package/dist/social-providers/kick.mjs +2 -1
  155. package/dist/social-providers/kick.mjs.map +1 -0
  156. package/dist/social-providers/line.d.mts +2 -1
  157. package/dist/social-providers/line.mjs +3 -2
  158. package/dist/social-providers/line.mjs.map +1 -0
  159. package/dist/social-providers/linear.d.mts +2 -1
  160. package/dist/social-providers/linear.mjs +2 -1
  161. package/dist/social-providers/linear.mjs.map +1 -0
  162. package/dist/social-providers/linkedin.d.mts +2 -1
  163. package/dist/social-providers/linkedin.mjs +2 -1
  164. package/dist/social-providers/linkedin.mjs.map +1 -0
  165. package/dist/social-providers/microsoft-entra-id.d.mts +4 -1
  166. package/dist/social-providers/microsoft-entra-id.mjs +36 -2
  167. package/dist/social-providers/microsoft-entra-id.mjs.map +1 -0
  168. package/dist/social-providers/naver.d.mts +11 -20
  169. package/dist/social-providers/naver.mjs +3 -2
  170. package/dist/social-providers/naver.mjs.map +1 -0
  171. package/dist/social-providers/notion.d.mts +2 -1
  172. package/dist/social-providers/notion.mjs +3 -2
  173. package/dist/social-providers/notion.mjs.map +1 -0
  174. package/dist/social-providers/paybin.d.mts +2 -1
  175. package/dist/social-providers/paybin.mjs +3 -2
  176. package/dist/social-providers/paybin.mjs.map +1 -0
  177. package/dist/social-providers/paypal.d.mts +2 -1
  178. package/dist/social-providers/paypal.mjs +2 -1
  179. package/dist/social-providers/paypal.mjs.map +1 -0
  180. package/dist/social-providers/polar.d.mts +2 -1
  181. package/dist/social-providers/polar.mjs +3 -2
  182. package/dist/social-providers/polar.mjs.map +1 -0
  183. package/dist/social-providers/railway.d.mts +68 -0
  184. package/dist/social-providers/railway.mjs +78 -0
  185. package/dist/social-providers/railway.mjs.map +1 -0
  186. package/dist/social-providers/reddit.d.mts +2 -1
  187. package/dist/social-providers/reddit.mjs +2 -1
  188. package/dist/social-providers/reddit.mjs.map +1 -0
  189. package/dist/social-providers/roblox.d.mts +2 -1
  190. package/dist/social-providers/roblox.mjs +2 -1
  191. package/dist/social-providers/roblox.mjs.map +1 -0
  192. package/dist/social-providers/salesforce.d.mts +2 -1
  193. package/dist/social-providers/salesforce.mjs +2 -1
  194. package/dist/social-providers/salesforce.mjs.map +1 -0
  195. package/dist/social-providers/slack.d.mts +2 -1
  196. package/dist/social-providers/slack.mjs +2 -1
  197. package/dist/social-providers/slack.mjs.map +1 -0
  198. package/dist/social-providers/spotify.d.mts +2 -1
  199. package/dist/social-providers/spotify.mjs +2 -1
  200. package/dist/social-providers/spotify.mjs.map +1 -0
  201. package/dist/social-providers/tiktok.d.mts +3 -3
  202. package/dist/social-providers/tiktok.mjs +3 -2
  203. package/dist/social-providers/tiktok.mjs.map +1 -0
  204. package/dist/social-providers/twitch.d.mts +2 -1
  205. package/dist/social-providers/twitch.mjs +2 -1
  206. package/dist/social-providers/twitch.mjs.map +1 -0
  207. package/dist/social-providers/twitter.d.mts +14 -25
  208. package/dist/social-providers/twitter.mjs +2 -1
  209. package/dist/social-providers/twitter.mjs.map +1 -0
  210. package/dist/social-providers/vercel.d.mts +2 -1
  211. package/dist/social-providers/vercel.mjs +3 -2
  212. package/dist/social-providers/vercel.mjs.map +1 -0
  213. package/dist/social-providers/vk.d.mts +2 -1
  214. package/dist/social-providers/vk.mjs +2 -1
  215. package/dist/social-providers/vk.mjs.map +1 -0
  216. package/dist/social-providers/zoom.d.mts +3 -10
  217. package/dist/social-providers/zoom.mjs +2 -1
  218. package/dist/social-providers/zoom.mjs.map +1 -0
  219. package/dist/types/context.d.mts +54 -21
  220. package/dist/types/cookie.d.mts +2 -1
  221. package/dist/types/helper.d.mts +4 -1
  222. package/dist/types/index.d.mts +4 -3
  223. package/dist/types/init-options.d.mts +235 -144
  224. package/dist/types/plugin-client.d.mts +4 -1
  225. package/dist/types/plugin.d.mts +12 -11
  226. package/dist/types/secret.d.mts +12 -0
  227. package/dist/utils/db.d.mts +12 -0
  228. package/dist/utils/db.mjs +17 -0
  229. package/dist/utils/db.mjs.map +1 -0
  230. package/dist/utils/deprecate.d.mts +2 -2
  231. package/dist/utils/deprecate.mjs +2 -1
  232. package/dist/utils/deprecate.mjs.map +1 -0
  233. package/dist/utils/error-codes.d.mts +8 -6
  234. package/dist/utils/error-codes.mjs +3 -2
  235. package/dist/utils/error-codes.mjs.map +1 -0
  236. package/dist/utils/id.d.mts +2 -1
  237. package/dist/utils/id.mjs +2 -1
  238. package/dist/utils/id.mjs.map +1 -0
  239. package/dist/utils/ip.d.mts +55 -0
  240. package/dist/utils/ip.mjs +119 -0
  241. package/dist/utils/ip.mjs.map +1 -0
  242. package/dist/utils/json.d.mts +2 -1
  243. package/dist/utils/json.mjs +2 -1
  244. package/dist/utils/json.mjs.map +1 -0
  245. package/dist/utils/string.d.mts +2 -1
  246. package/dist/utils/string.mjs +2 -1
  247. package/dist/utils/string.mjs.map +1 -0
  248. package/dist/utils/url.d.mts +2 -1
  249. package/dist/utils/url.mjs +2 -1
  250. package/dist/utils/url.mjs.map +1 -0
  251. package/package.json +35 -13
  252. package/src/context/index.ts +1 -0
  253. package/src/context/transaction.ts +72 -9
  254. package/src/db/adapter/factory.ts +41 -73
  255. package/src/db/adapter/get-id-field.ts +1 -3
  256. package/src/db/adapter/index.ts +20 -15
  257. package/src/db/adapter/types.ts +2 -41
  258. package/src/db/get-tables.ts +48 -37
  259. package/src/db/index.ts +30 -5
  260. package/src/db/schema/account.ts +16 -3
  261. package/src/db/schema/rate-limit.ts +16 -1
  262. package/src/db/schema/session.ts +15 -3
  263. package/src/db/schema/user.ts +15 -3
  264. package/src/db/schema/verification.ts +16 -3
  265. package/src/db/test/get-tables.test.ts +33 -0
  266. package/src/db/type.ts +154 -1
  267. package/src/env/env-impl.ts +2 -2
  268. package/src/env/logger.ts +1 -1
  269. package/src/error/codes.ts +17 -0
  270. package/src/oauth2/client-credentials-token.ts +26 -2
  271. package/src/oauth2/create-authorization-url.ts +3 -1
  272. package/src/oauth2/index.ts +3 -0
  273. package/src/oauth2/oauth-provider.ts +1 -1
  274. package/src/oauth2/refresh-access-token.test.ts +90 -0
  275. package/src/oauth2/refresh-access-token.ts +37 -4
  276. package/src/oauth2/validate-authorization-code.ts +55 -29
  277. package/src/oauth2/validate-token.test.ts +229 -0
  278. package/src/social-providers/apple.ts +29 -29
  279. package/src/social-providers/cognito.ts +6 -5
  280. package/src/social-providers/dropbox.ts +1 -1
  281. package/src/social-providers/facebook.ts +3 -3
  282. package/src/social-providers/github.ts +26 -4
  283. package/src/social-providers/gitlab.ts +1 -1
  284. package/src/social-providers/google.ts +18 -14
  285. package/src/social-providers/huggingface.ts +1 -1
  286. package/src/social-providers/index.ts +9 -5
  287. package/src/social-providers/kakao.ts +1 -1
  288. package/src/social-providers/line.ts +1 -1
  289. package/src/social-providers/microsoft-entra-id.ts +84 -1
  290. package/src/social-providers/naver.ts +1 -1
  291. package/src/social-providers/notion.ts +1 -1
  292. package/src/social-providers/paybin.ts +1 -5
  293. package/src/social-providers/polar.ts +1 -1
  294. package/src/social-providers/railway.ts +100 -0
  295. package/src/social-providers/tiktok.ts +2 -1
  296. package/src/social-providers/vercel.ts +1 -1
  297. package/src/social-providers/zoom.ts +0 -8
  298. package/src/types/context.ts +79 -15
  299. package/src/types/helper.ts +9 -0
  300. package/src/types/index.ts +14 -2
  301. package/src/types/init-options.ts +298 -171
  302. package/src/types/plugin-client.ts +1 -0
  303. package/src/types/plugin.ts +11 -6
  304. package/src/types/secret.ts +8 -0
  305. package/src/utils/db.ts +20 -0
  306. package/src/utils/deprecate.test.ts +0 -1
  307. package/src/utils/error-codes.ts +12 -9
  308. package/src/utils/ip.test.ts +255 -0
  309. package/src/utils/ip.ts +211 -0
  310. package/.turbo/turbo-build.log +0 -180
  311. package/tsconfig.json +0 -7
  312. package/tsdown.config.ts +0 -32
  313. package/vitest.config.ts +0 -3
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deprecate.mjs","names":[],"sources":["../../src/utils/deprecate.ts"],"sourcesContent":["import type { InternalLogger } from \"../env\";\n\n/**\n * Wraps a function to log a deprecation warning at once.\n */\nexport function deprecate<T extends (...args: any[]) => any>(\n\tfn: T,\n\tmessage: string,\n\tlogger?: InternalLogger,\n): T {\n\tlet warned = false;\n\n\treturn function (this: any, ...args: Parameters<T>): ReturnType<T> {\n\t\tif (!warned) {\n\t\t\tconst warn = logger?.warn ?? console.warn;\n\t\t\twarn(`[Deprecation] ${message}`);\n\t\t\twarned = true;\n\t\t}\n\t\treturn fn.apply(this, args);\n\t} as T;\n}\n"],"mappings":";;;;AAKA,SAAgB,UACf,IACA,SACA,QACI;CACJ,IAAI,SAAS;AAEb,QAAO,SAAqB,GAAG,MAAoC;AAClE,MAAI,CAAC,QAAQ;AAEZ,IADa,QAAQ,QAAQ,QAAQ,MAChC,iBAAiB,UAAU;AAChC,YAAS;;AAEV,SAAO,GAAG,MAAM,MAAM,KAAK"}
@@ -2,11 +2,13 @@
2
2
  type UpperLetter = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z";
3
3
  type SpecialCharacter = "_";
4
4
  type IsValidUpperSnakeCase<S extends string> = S extends `${infer F}${infer R}` ? F extends UpperLetter | SpecialCharacter ? IsValidUpperSnakeCase<R> : false : true;
5
- type InvalidKeyError<K$1 extends string> = `Invalid error code key: "${K$1}" - must only contain uppercase letters (A-Z) and underscores (_)`;
5
+ type InvalidKeyError<K extends string> = `Invalid error code key: "${K}" - must only contain uppercase letters (A-Z) and underscores (_)`;
6
6
  type ValidateErrorCodes<T> = { [K in keyof T]: K extends string ? IsValidUpperSnakeCase<K> extends false ? InvalidKeyError<K> : T[K] : T[K] };
7
- declare function defineErrorCodes<const T extends Record<string, string>>(codes: ValidateErrorCodes<T>): { [K in keyof T]: {
8
- code: K;
9
- message: T[K];
10
- } };
7
+ type RawError<K extends string = string> = {
8
+ readonly code: K;
9
+ message: string;
10
+ };
11
+ declare function defineErrorCodes<const T extends Record<string, string>, R extends { [K in keyof T & string]: RawError<K> }>(codes: ValidateErrorCodes<T>): R;
11
12
  //#endregion
12
- export { defineErrorCodes };
13
+ export { RawError, defineErrorCodes };
14
+ //# sourceMappingURL=error-codes.d.mts.map
@@ -3,9 +3,10 @@ function defineErrorCodes(codes) {
3
3
  return Object.fromEntries(Object.entries(codes).map(([key, value]) => [key, {
4
4
  code: key,
5
5
  message: value,
6
- toString: () => value
6
+ toString: () => key
7
7
  }]));
8
8
  }
9
9
 
10
10
  //#endregion
11
- export { defineErrorCodes };
11
+ export { defineErrorCodes };
12
+ //# sourceMappingURL=error-codes.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-codes.mjs","names":[],"sources":["../../src/utils/error-codes.ts"],"sourcesContent":["type UpperLetter =\n\t| \"A\"\n\t| \"B\"\n\t| \"C\"\n\t| \"D\"\n\t| \"E\"\n\t| \"F\"\n\t| \"G\"\n\t| \"H\"\n\t| \"I\"\n\t| \"J\"\n\t| \"K\"\n\t| \"L\"\n\t| \"M\"\n\t| \"N\"\n\t| \"O\"\n\t| \"P\"\n\t| \"Q\"\n\t| \"R\"\n\t| \"S\"\n\t| \"T\"\n\t| \"U\"\n\t| \"V\"\n\t| \"W\"\n\t| \"X\"\n\t| \"Y\"\n\t| \"Z\";\ntype SpecialCharacter = \"_\";\n\ntype IsValidUpperSnakeCase<S extends string> = S extends `${infer F}${infer R}`\n\t? F extends UpperLetter | SpecialCharacter\n\t\t? IsValidUpperSnakeCase<R>\n\t\t: false\n\t: true;\n\ntype InvalidKeyError<K extends string> =\n\t`Invalid error code key: \"${K}\" - must only contain uppercase letters (A-Z) and underscores (_)`;\n\ntype ValidateErrorCodes<T> = {\n\t[K in keyof T]: K extends string\n\t\t? IsValidUpperSnakeCase<K> extends false\n\t\t\t? InvalidKeyError<K>\n\t\t\t: T[K]\n\t\t: T[K];\n};\n\nexport type RawError<K extends string = string> = {\n\treadonly code: K;\n\tmessage: string;\n};\n\nexport function defineErrorCodes<\n\tconst T extends Record<string, string>,\n\tR extends {\n\t\t[K in keyof T & string]: RawError<K>;\n\t},\n>(codes: ValidateErrorCodes<T>): R {\n\treturn Object.fromEntries(\n\t\tObject.entries(codes).map(([key, value]) => [\n\t\t\tkey,\n\t\t\t{\n\t\t\t\tcode: key,\n\t\t\t\tmessage: value,\n\t\t\t\ttoString: () => key,\n\t\t\t},\n\t\t]),\n\t) as any;\n}\n"],"mappings":";AAmDA,SAAgB,iBAKd,OAAiC;AAClC,QAAO,OAAO,YACb,OAAO,QAAQ,MAAM,CAAC,KAAK,CAAC,KAAK,WAAW,CAC3C,KACA;EACC,MAAM;EACN,SAAS;EACT,gBAAgB;EAChB,CACD,CAAC,CACF"}
@@ -1,4 +1,5 @@
1
1
  //#region src/utils/id.d.ts
2
2
  declare const generateId: (size?: number) => string;
3
3
  //#endregion
4
- export { generateId };
4
+ export { generateId };
5
+ //# sourceMappingURL=id.d.mts.map
package/dist/utils/id.mjs CHANGED
@@ -6,4 +6,5 @@ const generateId = (size) => {
6
6
  };
7
7
 
8
8
  //#endregion
9
- export { generateId };
9
+ export { generateId };
10
+ //# sourceMappingURL=id.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"id.mjs","names":[],"sources":["../../src/utils/id.ts"],"sourcesContent":["import { createRandomStringGenerator } from \"@better-auth/utils/random\";\n\nexport const generateId = (size?: number) => {\n\treturn createRandomStringGenerator(\"a-z\", \"A-Z\", \"0-9\")(size || 32);\n};\n"],"mappings":";;;AAEA,MAAa,cAAc,SAAkB;AAC5C,QAAO,4BAA4B,OAAO,OAAO,MAAM,CAAC,QAAQ,GAAG"}
@@ -0,0 +1,55 @@
1
+ //#region src/utils/ip.d.ts
2
+ /**
3
+ * Normalizes an IP address for consistent rate limiting.
4
+ *
5
+ * Features:
6
+ * - Normalizes IPv6 to canonical lowercase form
7
+ * - Converts IPv4-mapped IPv6 to IPv4
8
+ * - Supports IPv6 subnet extraction
9
+ * - Handles all edge cases (::1, ::, etc.)
10
+ */
11
+ interface NormalizeIPOptions {
12
+ /**
13
+ * For IPv6 addresses, extract the subnet prefix instead of full address.
14
+ * Common values: 32, 48, 64, 128 (default: 128 = full address)
15
+ *
16
+ * @default 128
17
+ */
18
+ ipv6Subnet?: 128 | 64 | 48 | 32;
19
+ }
20
+ /**
21
+ * Checks if an IP is valid IPv4 or IPv6
22
+ */
23
+ declare function isValidIP(ip: string): boolean;
24
+ /**
25
+ * Normalizes an IP address (IPv4 or IPv6) for consistent rate limiting.
26
+ *
27
+ * @param ip - The IP address to normalize
28
+ * @param options - Normalization options
29
+ * @returns Normalized IP address
30
+ *
31
+ * @example
32
+ * normalizeIP("2001:DB8::1")
33
+ * // -> "2001:0db8:0000:0000:0000:0000:0000:0000"
34
+ *
35
+ * @example
36
+ * normalizeIP("::ffff:192.0.2.1")
37
+ * // -> "192.0.2.1" (converted to IPv4)
38
+ *
39
+ * @example
40
+ * normalizeIP("2001:db8::1", { ipv6Subnet: 64 })
41
+ * // -> "2001:0db8:0000:0000:0000:0000:0000:0000" (subnet /64)
42
+ */
43
+ declare function normalizeIP(ip: string, options?: NormalizeIPOptions): string;
44
+ /**
45
+ * Creates a rate limit key from IP and path
46
+ * Uses a separator to prevent collision attacks
47
+ *
48
+ * @param ip - The IP address (should be normalized)
49
+ * @param path - The request path
50
+ * @returns Rate limit key
51
+ */
52
+ declare function createRateLimitKey(ip: string, path: string): string;
53
+ //#endregion
54
+ export { createRateLimitKey, isValidIP, normalizeIP };
55
+ //# sourceMappingURL=ip.d.mts.map
@@ -0,0 +1,119 @@
1
+ import * as z from "zod";
2
+
3
+ //#region src/utils/ip.ts
4
+ /**
5
+ * Checks if an IP is valid IPv4 or IPv6
6
+ */
7
+ function isValidIP(ip) {
8
+ return z.ipv4().safeParse(ip).success || z.ipv6().safeParse(ip).success;
9
+ }
10
+ /**
11
+ * Checks if an IP is IPv6
12
+ */
13
+ function isIPv6(ip) {
14
+ return z.ipv6().safeParse(ip).success;
15
+ }
16
+ /**
17
+ * Converts IPv4-mapped IPv6 address to IPv4
18
+ * e.g., "::ffff:192.0.2.1" -> "192.0.2.1"
19
+ */
20
+ function extractIPv4FromMapped(ipv6) {
21
+ const lower = ipv6.toLowerCase();
22
+ if (lower.startsWith("::ffff:")) {
23
+ const ipv4Part = lower.substring(7);
24
+ if (z.ipv4().safeParse(ipv4Part).success) return ipv4Part;
25
+ }
26
+ const parts = ipv6.split(":");
27
+ if (parts.length === 7 && parts[5]?.toLowerCase() === "ffff") {
28
+ const ipv4Part = parts[6];
29
+ if (ipv4Part && z.ipv4().safeParse(ipv4Part).success) return ipv4Part;
30
+ }
31
+ if (lower.includes("::ffff:") || lower.includes(":ffff:")) {
32
+ const groups = expandIPv6(ipv6);
33
+ if (groups.length === 8 && groups[0] === "0000" && groups[1] === "0000" && groups[2] === "0000" && groups[3] === "0000" && groups[4] === "0000" && groups[5] === "ffff" && groups[6] && groups[7]) return `${Number.parseInt(groups[6].substring(0, 2), 16)}.${Number.parseInt(groups[6].substring(2, 4), 16)}.${Number.parseInt(groups[7].substring(0, 2), 16)}.${Number.parseInt(groups[7].substring(2, 4), 16)}`;
34
+ }
35
+ return null;
36
+ }
37
+ /**
38
+ * Expands a compressed IPv6 address to full form
39
+ * e.g., "2001:db8::1" -> ["2001", "0db8", "0000", "0000", "0000", "0000", "0000", "0001"]
40
+ */
41
+ function expandIPv6(ipv6) {
42
+ if (ipv6.includes("::")) {
43
+ const sides = ipv6.split("::");
44
+ const left = sides[0] ? sides[0].split(":") : [];
45
+ const right = sides[1] ? sides[1].split(":") : [];
46
+ const missingGroups = 8 - left.length - right.length;
47
+ const zeros = Array(missingGroups).fill("0000");
48
+ const paddedLeft = left.map((g) => g.padStart(4, "0"));
49
+ const paddedRight = right.map((g) => g.padStart(4, "0"));
50
+ return [
51
+ ...paddedLeft,
52
+ ...zeros,
53
+ ...paddedRight
54
+ ];
55
+ }
56
+ return ipv6.split(":").map((g) => g.padStart(4, "0"));
57
+ }
58
+ /**
59
+ * Normalizes an IPv6 address to canonical form
60
+ * e.g., "2001:DB8::1" -> "2001:0db8:0000:0000:0000:0000:0000:0001"
61
+ */
62
+ function normalizeIPv6(ipv6, subnetPrefix) {
63
+ const groups = expandIPv6(ipv6);
64
+ if (subnetPrefix && subnetPrefix < 128) {
65
+ let bitsRemaining = subnetPrefix;
66
+ return groups.map((group) => {
67
+ if (bitsRemaining <= 0) return "0000";
68
+ if (bitsRemaining >= 16) {
69
+ bitsRemaining -= 16;
70
+ return group;
71
+ }
72
+ const masked = Number.parseInt(group, 16) & (65535 << 16 - bitsRemaining & 65535);
73
+ bitsRemaining = 0;
74
+ return masked.toString(16).padStart(4, "0");
75
+ }).join(":").toLowerCase();
76
+ }
77
+ return groups.join(":").toLowerCase();
78
+ }
79
+ /**
80
+ * Normalizes an IP address (IPv4 or IPv6) for consistent rate limiting.
81
+ *
82
+ * @param ip - The IP address to normalize
83
+ * @param options - Normalization options
84
+ * @returns Normalized IP address
85
+ *
86
+ * @example
87
+ * normalizeIP("2001:DB8::1")
88
+ * // -> "2001:0db8:0000:0000:0000:0000:0000:0000"
89
+ *
90
+ * @example
91
+ * normalizeIP("::ffff:192.0.2.1")
92
+ * // -> "192.0.2.1" (converted to IPv4)
93
+ *
94
+ * @example
95
+ * normalizeIP("2001:db8::1", { ipv6Subnet: 64 })
96
+ * // -> "2001:0db8:0000:0000:0000:0000:0000:0000" (subnet /64)
97
+ */
98
+ function normalizeIP(ip, options = {}) {
99
+ if (z.ipv4().safeParse(ip).success) return ip.toLowerCase();
100
+ if (!isIPv6(ip)) return ip.toLowerCase();
101
+ const ipv4 = extractIPv4FromMapped(ip);
102
+ if (ipv4) return ipv4.toLowerCase();
103
+ return normalizeIPv6(ip, options.ipv6Subnet || 64);
104
+ }
105
+ /**
106
+ * Creates a rate limit key from IP and path
107
+ * Uses a separator to prevent collision attacks
108
+ *
109
+ * @param ip - The IP address (should be normalized)
110
+ * @param path - The request path
111
+ * @returns Rate limit key
112
+ */
113
+ function createRateLimitKey(ip, path) {
114
+ return `${ip}|${path}`;
115
+ }
116
+
117
+ //#endregion
118
+ export { createRateLimitKey, isValidIP, normalizeIP };
119
+ //# sourceMappingURL=ip.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ip.mjs","names":[],"sources":["../../src/utils/ip.ts"],"sourcesContent":["import * as z from \"zod\";\n\n/**\n * Normalizes an IP address for consistent rate limiting.\n *\n * Features:\n * - Normalizes IPv6 to canonical lowercase form\n * - Converts IPv4-mapped IPv6 to IPv4\n * - Supports IPv6 subnet extraction\n * - Handles all edge cases (::1, ::, etc.)\n */\n\ninterface NormalizeIPOptions {\n\t/**\n\t * For IPv6 addresses, extract the subnet prefix instead of full address.\n\t * Common values: 32, 48, 64, 128 (default: 128 = full address)\n\t *\n\t * @default 128\n\t */\n\tipv6Subnet?: 128 | 64 | 48 | 32;\n}\n\n/**\n * Checks if an IP is valid IPv4 or IPv6\n */\nexport function isValidIP(ip: string): boolean {\n\treturn z.ipv4().safeParse(ip).success || z.ipv6().safeParse(ip).success;\n}\n\n/**\n * Checks if an IP is IPv6\n */\nfunction isIPv6(ip: string): boolean {\n\treturn z.ipv6().safeParse(ip).success;\n}\n\n/**\n * Converts IPv4-mapped IPv6 address to IPv4\n * e.g., \"::ffff:192.0.2.1\" -> \"192.0.2.1\"\n */\nfunction extractIPv4FromMapped(ipv6: string): string | null {\n\tconst lower = ipv6.toLowerCase();\n\n\t// Handle ::ffff:192.0.2.1 format\n\tif (lower.startsWith(\"::ffff:\")) {\n\t\tconst ipv4Part = lower.substring(7);\n\t\t// Check if it's a valid IPv4\n\t\tif (z.ipv4().safeParse(ipv4Part).success) {\n\t\t\treturn ipv4Part;\n\t\t}\n\t}\n\n\t// Handle full form: 0:0:0:0:0:ffff:192.0.2.1\n\tconst parts = ipv6.split(\":\");\n\tif (parts.length === 7 && parts[5]?.toLowerCase() === \"ffff\") {\n\t\tconst ipv4Part = parts[6];\n\t\tif (ipv4Part && z.ipv4().safeParse(ipv4Part).success) {\n\t\t\treturn ipv4Part;\n\t\t}\n\t}\n\n\t// Handle hex-encoded IPv4 in mapped address\n\t// e.g., ::ffff:c000:0201 -> 192.0.2.1\n\tif (lower.includes(\"::ffff:\") || lower.includes(\":ffff:\")) {\n\t\tconst groups = expandIPv6(ipv6);\n\t\tif (\n\t\t\tgroups.length === 8 &&\n\t\t\tgroups[0] === \"0000\" &&\n\t\t\tgroups[1] === \"0000\" &&\n\t\t\tgroups[2] === \"0000\" &&\n\t\t\tgroups[3] === \"0000\" &&\n\t\t\tgroups[4] === \"0000\" &&\n\t\t\tgroups[5] === \"ffff\" &&\n\t\t\tgroups[6] &&\n\t\t\tgroups[7]\n\t\t) {\n\t\t\t// Convert last two groups to IPv4\n\t\t\tconst byte1 = Number.parseInt(groups[6].substring(0, 2), 16);\n\t\t\tconst byte2 = Number.parseInt(groups[6].substring(2, 4), 16);\n\t\t\tconst byte3 = Number.parseInt(groups[7].substring(0, 2), 16);\n\t\t\tconst byte4 = Number.parseInt(groups[7].substring(2, 4), 16);\n\t\t\treturn `${byte1}.${byte2}.${byte3}.${byte4}`;\n\t\t}\n\t}\n\n\treturn null;\n}\n\n/**\n * Expands a compressed IPv6 address to full form\n * e.g., \"2001:db8::1\" -> [\"2001\", \"0db8\", \"0000\", \"0000\", \"0000\", \"0000\", \"0000\", \"0001\"]\n */\nfunction expandIPv6(ipv6: string): string[] {\n\t// Handle :: notation (zero compression)\n\tif (ipv6.includes(\"::\")) {\n\t\tconst sides = ipv6.split(\"::\");\n\t\tconst left = sides[0] ? sides[0].split(\":\") : [];\n\t\tconst right = sides[1] ? sides[1].split(\":\") : [];\n\n\t\t// Calculate missing groups\n\t\tconst totalGroups = 8;\n\t\tconst missingGroups = totalGroups - left.length - right.length;\n\t\tconst zeros = Array(missingGroups).fill(\"0000\");\n\n\t\t// Pad existing groups to 4 digits\n\t\tconst paddedLeft = left.map((g) => g.padStart(4, \"0\"));\n\t\tconst paddedRight = right.map((g) => g.padStart(4, \"0\"));\n\n\t\treturn [...paddedLeft, ...zeros, ...paddedRight];\n\t}\n\n\t// No compression, just pad each group\n\treturn ipv6.split(\":\").map((g) => g.padStart(4, \"0\"));\n}\n\n/**\n * Normalizes an IPv6 address to canonical form\n * e.g., \"2001:DB8::1\" -> \"2001:0db8:0000:0000:0000:0000:0000:0001\"\n */\nfunction normalizeIPv6(\n\tipv6: string,\n\tsubnetPrefix?: 128 | 32 | 48 | 64,\n): string {\n\tconst groups = expandIPv6(ipv6);\n\n\tif (subnetPrefix && subnetPrefix < 128) {\n\t\t// Apply subnet mask\n\t\tconst prefix = subnetPrefix;\n\t\tlet bitsRemaining: number = prefix;\n\n\t\tconst maskedGroups = groups.map((group) => {\n\t\t\tif (bitsRemaining <= 0) {\n\t\t\t\treturn \"0000\";\n\t\t\t}\n\t\t\tif (bitsRemaining >= 16) {\n\t\t\t\tbitsRemaining -= 16;\n\t\t\t\treturn group;\n\t\t\t}\n\n\t\t\t// Partial mask for this group\n\t\t\tconst value = Number.parseInt(group, 16);\n\t\t\tconst mask = (0xffff << (16 - bitsRemaining)) & 0xffff;\n\t\t\tconst masked = value & mask;\n\t\t\tbitsRemaining = 0;\n\t\t\treturn masked.toString(16).padStart(4, \"0\");\n\t\t});\n\n\t\treturn maskedGroups.join(\":\").toLowerCase();\n\t}\n\n\treturn groups.join(\":\").toLowerCase();\n}\n\n/**\n * Normalizes an IP address (IPv4 or IPv6) for consistent rate limiting.\n *\n * @param ip - The IP address to normalize\n * @param options - Normalization options\n * @returns Normalized IP address\n *\n * @example\n * normalizeIP(\"2001:DB8::1\")\n * // -> \"2001:0db8:0000:0000:0000:0000:0000:0000\"\n *\n * @example\n * normalizeIP(\"::ffff:192.0.2.1\")\n * // -> \"192.0.2.1\" (converted to IPv4)\n *\n * @example\n * normalizeIP(\"2001:db8::1\", { ipv6Subnet: 64 })\n * // -> \"2001:0db8:0000:0000:0000:0000:0000:0000\" (subnet /64)\n */\nexport function normalizeIP(\n\tip: string,\n\toptions: NormalizeIPOptions = {},\n): string {\n\t// IPv4 addresses are already normalized\n\tif (z.ipv4().safeParse(ip).success) {\n\t\treturn ip.toLowerCase();\n\t}\n\n\t// Check if it's IPv6\n\tif (!isIPv6(ip)) {\n\t\t// Return as-is if not valid (shouldn't happen due to prior validation)\n\t\treturn ip.toLowerCase();\n\t}\n\n\t// Check for IPv4-mapped IPv6\n\tconst ipv4 = extractIPv4FromMapped(ip);\n\tif (ipv4) {\n\t\treturn ipv4.toLowerCase();\n\t}\n\n\t// Normalize IPv6\n\tconst subnetPrefix = options.ipv6Subnet || 64;\n\treturn normalizeIPv6(ip, subnetPrefix);\n}\n\n/**\n * Creates a rate limit key from IP and path\n * Uses a separator to prevent collision attacks\n *\n * @param ip - The IP address (should be normalized)\n * @param path - The request path\n * @returns Rate limit key\n */\nexport function createRateLimitKey(ip: string, path: string): string {\n\t// Use | as separator to prevent collision attacks\n\t// e.g., \"192.0.2.1\" + \"/sign-in\" vs \"192.0.2\" + \".1/sign-in\"\n\treturn `${ip}|${path}`;\n}\n"],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  //#region src/utils/json.d.ts
2
2
  declare function safeJSONParse<T>(data: unknown): T | null;
3
3
  //#endregion
4
- export { safeJSONParse };
4
+ export { safeJSONParse };
5
+ //# sourceMappingURL=json.d.mts.map
@@ -22,4 +22,5 @@ function safeJSONParse(data) {
22
22
  }
23
23
 
24
24
  //#endregion
25
- export { safeJSONParse };
25
+ export { safeJSONParse };
26
+ //# sourceMappingURL=json.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json.mjs","names":[],"sources":["../../src/utils/json.ts"],"sourcesContent":["import { logger } from \"../env\";\n\nexport function safeJSONParse<T>(data: unknown): T | null {\n\tfunction reviver(_: string, value: any): any {\n\t\tif (typeof value === \"string\") {\n\t\t\tconst iso8601Regex = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?Z$/;\n\t\t\tif (iso8601Regex.test(value)) {\n\t\t\t\tconst date = new Date(value);\n\t\t\t\tif (!isNaN(date.getTime())) {\n\t\t\t\t\treturn date;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn value;\n\t}\n\ttry {\n\t\tif (typeof data !== \"string\") {\n\t\t\treturn data as T;\n\t\t}\n\t\treturn JSON.parse(data, reviver);\n\t} catch (e) {\n\t\tlogger.error(\"Error parsing JSON\", { error: e });\n\t\treturn null;\n\t}\n}\n"],"mappings":";;;;AAEA,SAAgB,cAAiB,MAAyB;CACzD,SAAS,QAAQ,GAAW,OAAiB;AAC5C,MAAI,OAAO,UAAU,UAEpB;OADqB,mDACJ,KAAK,MAAM,EAAE;IAC7B,MAAM,OAAO,IAAI,KAAK,MAAM;AAC5B,QAAI,CAAC,MAAM,KAAK,SAAS,CAAC,CACzB,QAAO;;;AAIV,SAAO;;AAER,KAAI;AACH,MAAI,OAAO,SAAS,SACnB,QAAO;AAER,SAAO,KAAK,MAAM,MAAM,QAAQ;UACxB,GAAG;AACX,SAAO,MAAM,sBAAsB,EAAE,OAAO,GAAG,CAAC;AAChD,SAAO"}
@@ -1,4 +1,5 @@
1
1
  //#region src/utils/string.d.ts
2
2
  declare function capitalizeFirstLetter(str: string): string;
3
3
  //#endregion
4
- export { capitalizeFirstLetter };
4
+ export { capitalizeFirstLetter };
5
+ //# sourceMappingURL=string.d.mts.map
@@ -4,4 +4,5 @@ function capitalizeFirstLetter(str) {
4
4
  }
5
5
 
6
6
  //#endregion
7
- export { capitalizeFirstLetter };
7
+ export { capitalizeFirstLetter };
8
+ //# sourceMappingURL=string.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"string.mjs","names":[],"sources":["../../src/utils/string.ts"],"sourcesContent":["export function capitalizeFirstLetter(str: string) {\n\treturn str.charAt(0).toUpperCase() + str.slice(1);\n}\n"],"mappings":";AAAA,SAAgB,sBAAsB,KAAa;AAClD,QAAO,IAAI,OAAO,EAAE,CAAC,aAAa,GAAG,IAAI,MAAM,EAAE"}
@@ -17,4 +17,5 @@
17
17
  */
18
18
  declare function normalizePathname(requestUrl: string, basePath: string): string;
19
19
  //#endregion
20
- export { normalizePathname };
20
+ export { normalizePathname };
21
+ //# sourceMappingURL=url.d.mts.map
@@ -29,4 +29,5 @@ function normalizePathname(requestUrl, basePath) {
29
29
  }
30
30
 
31
31
  //#endregion
32
- export { normalizePathname };
32
+ export { normalizePathname };
33
+ //# sourceMappingURL=url.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url.mjs","names":[],"sources":["../../src/utils/url.ts"],"sourcesContent":["/**\n * Normalizes a request pathname by removing the basePath prefix and trailing slashes.\n * This is useful for matching paths against configured path lists.\n *\n * @param requestUrl - The full request URL\n * @param basePath - The base path of the auth API (e.g., \"/api/auth\")\n * @returns The normalized path without basePath prefix or trailing slashes,\n * or \"/\" if URL parsing fails\n *\n * @example\n * normalizePathname(\"http://localhost:3000/api/auth/sso/saml2/callback/provider1\", \"/api/auth\")\n * // Returns: \"/sso/saml2/callback/provider1\"\n *\n * normalizePathname(\"http://localhost:3000/sso/saml2/callback/provider1/\", \"/\")\n * // Returns: \"/sso/saml2/callback/provider1\"\n */\nexport function normalizePathname(\n\trequestUrl: string,\n\tbasePath: string,\n): string {\n\tlet pathname: string;\n\ttry {\n\t\tpathname = new URL(requestUrl).pathname.replace(/\\/+$/, \"\") || \"/\";\n\t} catch {\n\t\treturn \"/\";\n\t}\n\n\tif (basePath === \"/\" || basePath === \"\") {\n\t\treturn pathname;\n\t}\n\n\t// Check for exact match or proper path boundary (basePath followed by \"/\" or end)\n\t// This prevents \"/api/auth\" from matching \"/api/authevil/...\"\n\tif (pathname === basePath) {\n\t\treturn \"/\";\n\t}\n\n\tif (pathname.startsWith(basePath + \"/\")) {\n\t\treturn pathname.slice(basePath.length).replace(/\\/+$/, \"\") || \"/\";\n\t}\n\n\treturn pathname;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAgBA,SAAgB,kBACf,YACA,UACS;CACT,IAAI;AACJ,KAAI;AACH,aAAW,IAAI,IAAI,WAAW,CAAC,SAAS,QAAQ,QAAQ,GAAG,IAAI;SACxD;AACP,SAAO;;AAGR,KAAI,aAAa,OAAO,aAAa,GACpC,QAAO;AAKR,KAAI,aAAa,SAChB,QAAO;AAGR,KAAI,SAAS,WAAW,WAAW,IAAI,CACtC,QAAO,SAAS,MAAM,SAAS,OAAO,CAAC,QAAQ,QAAQ,GAAG,IAAI;AAG/D,QAAO"}
package/package.json CHANGED
@@ -1,13 +1,28 @@
1
1
  {
2
2
  "name": "@better-auth/core",
3
- "version": "1.5.0-beta.8",
3
+ "version": "1.5.0",
4
4
  "description": "The most comprehensive authentication framework for TypeScript.",
5
5
  "type": "module",
6
+ "license": "MIT",
7
+ "homepage": "https://www.better-auth.com",
6
8
  "repository": {
7
9
  "type": "git",
8
10
  "url": "git+https://github.com/better-auth/better-auth.git",
9
11
  "directory": "packages/core"
10
12
  },
13
+ "keywords": [
14
+ "auth",
15
+ "core",
16
+ "typescript",
17
+ "better-auth"
18
+ ],
19
+ "publishConfig": {
20
+ "access": "public"
21
+ },
22
+ "files": [
23
+ "dist",
24
+ "src"
25
+ ],
11
26
  "main": "./dist/index.mjs",
12
27
  "module": "./dist/index.mjs",
13
28
  "types": "./dist/index.d.mts",
@@ -114,27 +129,34 @@
114
129
  ]
115
130
  }
116
131
  },
132
+ "dependencies": {
133
+ "@standard-schema/spec": "^1.1.0",
134
+ "zod": "^4.3.6"
135
+ },
117
136
  "devDependencies": {
118
- "@better-auth/utils": "0.3.0",
137
+ "@better-auth/utils": "0.3.1",
119
138
  "@better-fetch/fetch": "1.1.21",
120
- "better-call": "1.1.8",
121
- "jose": "^6.1.0",
122
- "kysely": "^0.28.5",
123
- "nanostores": "^1.0.1",
124
- "tsdown": "^0.19.0"
125
- },
126
- "dependencies": {
127
- "@standard-schema/spec": "^1.0.0",
128
- "zod": "^4.1.12"
139
+ "better-call": "1.3.2",
140
+ "@cloudflare/workers-types": "^4.20250121.0",
141
+ "jose": "^6.1.3",
142
+ "kysely": "^0.28.11",
143
+ "nanostores": "^1.1.1",
144
+ "tsdown": "^0.20.3"
129
145
  },
130
146
  "peerDependencies": {
131
- "@better-auth/utils": "0.3.0",
147
+ "@better-auth/utils": "0.3.1",
132
148
  "@better-fetch/fetch": "1.1.21",
133
- "better-call": "1.1.8",
149
+ "better-call": "1.3.2",
150
+ "@cloudflare/workers-types": ">=4",
134
151
  "jose": "^6.1.0",
135
152
  "kysely": "^0.28.5",
136
153
  "nanostores": "^1.0.1"
137
154
  },
155
+ "peerDependenciesMeta": {
156
+ "@cloudflare/workers-types": {
157
+ "optional": true
158
+ }
159
+ },
138
160
  "scripts": {
139
161
  "build": "tsdown",
140
162
  "dev": "tsdown --watch",
@@ -17,6 +17,7 @@ export {
17
17
  export {
18
18
  getCurrentAdapter,
19
19
  getCurrentDBAdapterAsyncLocalStorage,
20
+ queueAfterTransactionHook,
20
21
  runWithAdapter,
21
22
  runWithTransaction,
22
23
  } from "./transaction";
@@ -3,6 +3,11 @@ import { getAsyncLocalStorage } from "@better-auth/core/async_hooks";
3
3
  import type { DBAdapter, DBTransactionAdapter } from "../db/adapter";
4
4
  import { __getBetterAuthGlobal } from "./global";
5
5
 
6
+ type HookContext = {
7
+ adapter: DBTransactionAdapter;
8
+ pendingHooks: Array<() => Promise<void>>;
9
+ };
10
+
6
11
  const ensureAsyncStorage = async () => {
7
12
  const betterAuthGlobal = __getBetterAuthGlobal();
8
13
  if (!betterAuthGlobal.context.adapterAsyncStorage) {
@@ -10,7 +15,7 @@ const ensureAsyncStorage = async () => {
10
15
  betterAuthGlobal.context.adapterAsyncStorage = new AsyncLocalStorage();
11
16
  }
12
17
  return betterAuthGlobal.context
13
- .adapterAsyncStorage as AsyncLocalStorage<DBTransactionAdapter>;
18
+ .adapterAsyncStorage as AsyncLocalStorage<HookContext>;
14
19
  };
15
20
 
16
21
  /**
@@ -27,7 +32,8 @@ export const getCurrentAdapter = async (
27
32
  ): Promise<DBTransactionAdapter> => {
28
33
  return ensureAsyncStorage()
29
34
  .then((als) => {
30
- return als.getStore() || fallback;
35
+ const store = als.getStore();
36
+ return store?.adapter || fallback;
31
37
  })
32
38
  .catch(() => {
33
39
  return fallback;
@@ -38,11 +44,28 @@ export const runWithAdapter = async <R>(
38
44
  adapter: DBAdapter,
39
45
  fn: () => R,
40
46
  ): Promise<R> => {
41
- let called = true;
47
+ let called = false;
42
48
  return ensureAsyncStorage()
43
- .then((als) => {
49
+ .then(async (als) => {
44
50
  called = true;
45
- return als.run(adapter, fn);
51
+ const pendingHooks: Array<() => Promise<void>> = [];
52
+ let result: Awaited<R>;
53
+ let error: unknown;
54
+ let hasError = false;
55
+ try {
56
+ result = await als.run({ adapter, pendingHooks }, fn);
57
+ } catch (err) {
58
+ error = err;
59
+ hasError = true;
60
+ }
61
+ // Execute pending hooks after the function completes (even if it threw)
62
+ for (const hook of pendingHooks) {
63
+ await hook();
64
+ }
65
+ if (hasError) {
66
+ throw error;
67
+ }
68
+ return result!;
46
69
  })
47
70
  .catch((err) => {
48
71
  if (!called) {
@@ -58,11 +81,27 @@ export const runWithTransaction = async <R>(
58
81
  ): Promise<R> => {
59
82
  let called = true;
60
83
  return ensureAsyncStorage()
61
- .then((als) => {
84
+ .then(async (als) => {
62
85
  called = true;
63
- return adapter.transaction(async (trx) => {
64
- return als.run(trx, fn);
65
- });
86
+ const pendingHooks: Array<() => Promise<void>> = [];
87
+ let result: Awaited<R>;
88
+ let error: unknown;
89
+ let hasError = false;
90
+ try {
91
+ result = await adapter.transaction(async (trx) => {
92
+ return als.run({ adapter: trx, pendingHooks }, fn);
93
+ });
94
+ } catch (e) {
95
+ hasError = true;
96
+ error = e;
97
+ }
98
+ for (const hook of pendingHooks) {
99
+ await hook();
100
+ }
101
+ if (hasError) {
102
+ throw error;
103
+ }
104
+ return result!;
66
105
  })
67
106
  .catch((err) => {
68
107
  if (!called) {
@@ -71,3 +110,27 @@ export const runWithTransaction = async <R>(
71
110
  throw err;
72
111
  });
73
112
  };
113
+
114
+ /**
115
+ * Queue a hook to be executed after the current transaction commits.
116
+ * If not in a transaction, the hook will execute immediately.
117
+ */
118
+ export const queueAfterTransactionHook = async (
119
+ hook: () => Promise<void>,
120
+ ): Promise<void> => {
121
+ return ensureAsyncStorage()
122
+ .then((als) => {
123
+ const store = als.getStore();
124
+ if (store) {
125
+ // We're in a transaction context, queue the hook
126
+ store.pendingHooks.push(hook);
127
+ } else {
128
+ // Not in a transaction, execute immediately
129
+ return hook();
130
+ }
131
+ })
132
+ .catch(() => {
133
+ // No async storage available, execute immediately
134
+ return hook();
135
+ });
136
+ };