@better-auth/core 1.5.6 → 1.6.0-beta.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 (281) hide show
  1. package/dist/api/index.d.mts +42 -13
  2. package/dist/api/index.mjs +0 -4
  3. package/dist/async_hooks/index.d.mts +1 -2
  4. package/dist/async_hooks/index.mjs +0 -2
  5. package/dist/async_hooks/pure.index.d.mts +1 -2
  6. package/dist/async_hooks/pure.index.mjs +0 -2
  7. package/dist/context/endpoint-context.d.mts +2 -3
  8. package/dist/context/endpoint-context.mjs +0 -3
  9. package/dist/context/global.d.mts +1 -2
  10. package/dist/context/global.mjs +1 -3
  11. package/dist/context/index.mjs +1 -2
  12. package/dist/context/request-state.d.mts +1 -2
  13. package/dist/context/request-state.mjs +0 -3
  14. package/dist/context/transaction.d.mts +1 -2
  15. package/dist/context/transaction.mjs +0 -3
  16. package/dist/db/adapter/factory.d.mts +1 -2
  17. package/dist/db/adapter/factory.mjs +3 -7
  18. package/dist/db/adapter/get-default-field-name.d.mts +1 -2
  19. package/dist/db/adapter/get-default-field-name.mjs +0 -3
  20. package/dist/db/adapter/get-default-model-name.d.mts +1 -2
  21. package/dist/db/adapter/get-default-model-name.mjs +0 -3
  22. package/dist/db/adapter/get-field-attributes.d.mts +1 -2
  23. package/dist/db/adapter/get-field-attributes.mjs +0 -3
  24. package/dist/db/adapter/get-field-name.d.mts +1 -2
  25. package/dist/db/adapter/get-field-name.mjs +0 -3
  26. package/dist/db/adapter/get-id-field.d.mts +1 -2
  27. package/dist/db/adapter/get-id-field.mjs +0 -4
  28. package/dist/db/adapter/get-model-name.d.mts +1 -2
  29. package/dist/db/adapter/get-model-name.mjs +0 -3
  30. package/dist/db/adapter/index.d.mts +9 -2
  31. package/dist/db/adapter/index.mjs +0 -3
  32. package/dist/db/adapter/types.d.mts +1 -2
  33. package/dist/db/adapter/utils.d.mts +1 -2
  34. package/dist/db/adapter/utils.mjs +0 -2
  35. package/dist/db/get-tables.d.mts +1 -2
  36. package/dist/db/get-tables.mjs +0 -2
  37. package/dist/db/index.mjs +1 -2
  38. package/dist/db/plugin.d.mts +1 -2
  39. package/dist/db/schema/account.d.mts +1 -2
  40. package/dist/db/schema/account.mjs +0 -3
  41. package/dist/db/schema/rate-limit.d.mts +1 -2
  42. package/dist/db/schema/rate-limit.mjs +0 -3
  43. package/dist/db/schema/session.d.mts +1 -2
  44. package/dist/db/schema/session.mjs +0 -3
  45. package/dist/db/schema/shared.d.mts +1 -2
  46. package/dist/db/schema/shared.mjs +0 -3
  47. package/dist/db/schema/user.d.mts +1 -2
  48. package/dist/db/schema/user.mjs +0 -3
  49. package/dist/db/schema/verification.d.mts +1 -2
  50. package/dist/db/schema/verification.mjs +0 -3
  51. package/dist/db/type.d.mts +1 -2
  52. package/dist/env/color-depth.d.mts +1 -2
  53. package/dist/env/color-depth.mjs +0 -3
  54. package/dist/env/env-impl.d.mts +1 -2
  55. package/dist/env/env-impl.mjs +0 -2
  56. package/dist/env/index.mjs +1 -2
  57. package/dist/env/logger.d.mts +1 -2
  58. package/dist/env/logger.mjs +0 -3
  59. package/dist/error/codes.d.mts +1 -2
  60. package/dist/error/codes.mjs +0 -3
  61. package/dist/error/index.d.mts +1 -2
  62. package/dist/error/index.mjs +0 -3
  63. package/dist/index.mjs +1 -1
  64. package/dist/instrumentation/attributes.d.mts +1 -2
  65. package/dist/instrumentation/attributes.mjs +0 -3
  66. package/dist/instrumentation/index.mjs +1 -2
  67. package/dist/instrumentation/tracer.d.mts +1 -2
  68. package/dist/instrumentation/tracer.mjs +29 -16
  69. package/dist/oauth2/client-credentials-token.d.mts +1 -2
  70. package/dist/oauth2/client-credentials-token.mjs +0 -3
  71. package/dist/oauth2/create-authorization-url.d.mts +1 -2
  72. package/dist/oauth2/create-authorization-url.mjs +0 -3
  73. package/dist/oauth2/index.mjs +1 -2
  74. package/dist/oauth2/oauth-provider.d.mts +1 -2
  75. package/dist/oauth2/refresh-access-token.d.mts +1 -2
  76. package/dist/oauth2/refresh-access-token.mjs +0 -3
  77. package/dist/oauth2/utils.d.mts +1 -2
  78. package/dist/oauth2/utils.mjs +0 -3
  79. package/dist/oauth2/validate-authorization-code.d.mts +1 -2
  80. package/dist/oauth2/validate-authorization-code.mjs +0 -4
  81. package/dist/oauth2/verify.d.mts +1 -2
  82. package/dist/oauth2/verify.mjs +0 -4
  83. package/dist/social-providers/apple.d.mts +1 -2
  84. package/dist/social-providers/apple.mjs +0 -4
  85. package/dist/social-providers/atlassian.d.mts +1 -2
  86. package/dist/social-providers/atlassian.mjs +0 -5
  87. package/dist/social-providers/cognito.d.mts +1 -2
  88. package/dist/social-providers/cognito.mjs +0 -5
  89. package/dist/social-providers/discord.d.mts +1 -2
  90. package/dist/social-providers/discord.mjs +0 -4
  91. package/dist/social-providers/dropbox.d.mts +1 -2
  92. package/dist/social-providers/dropbox.mjs +0 -4
  93. package/dist/social-providers/facebook.d.mts +1 -2
  94. package/dist/social-providers/facebook.mjs +0 -4
  95. package/dist/social-providers/figma.d.mts +1 -2
  96. package/dist/social-providers/figma.mjs +0 -5
  97. package/dist/social-providers/github.d.mts +1 -2
  98. package/dist/social-providers/github.mjs +0 -5
  99. package/dist/social-providers/gitlab.d.mts +1 -2
  100. package/dist/social-providers/gitlab.mjs +0 -4
  101. package/dist/social-providers/google.d.mts +1 -2
  102. package/dist/social-providers/google.mjs +0 -5
  103. package/dist/social-providers/huggingface.d.mts +1 -2
  104. package/dist/social-providers/huggingface.mjs +0 -4
  105. package/dist/social-providers/index.d.mts +1 -2
  106. package/dist/social-providers/index.mjs +0 -3
  107. package/dist/social-providers/kakao.d.mts +1 -2
  108. package/dist/social-providers/kakao.mjs +0 -4
  109. package/dist/social-providers/kick.d.mts +1 -2
  110. package/dist/social-providers/kick.mjs +0 -4
  111. package/dist/social-providers/line.d.mts +1 -2
  112. package/dist/social-providers/line.mjs +0 -4
  113. package/dist/social-providers/linear.d.mts +1 -2
  114. package/dist/social-providers/linear.mjs +0 -4
  115. package/dist/social-providers/linkedin.d.mts +1 -2
  116. package/dist/social-providers/linkedin.mjs +0 -4
  117. package/dist/social-providers/microsoft-entra-id.d.mts +1 -2
  118. package/dist/social-providers/microsoft-entra-id.mjs +0 -5
  119. package/dist/social-providers/naver.d.mts +1 -2
  120. package/dist/social-providers/naver.mjs +0 -4
  121. package/dist/social-providers/notion.d.mts +1 -2
  122. package/dist/social-providers/notion.mjs +0 -4
  123. package/dist/social-providers/paybin.d.mts +1 -2
  124. package/dist/social-providers/paybin.mjs +0 -5
  125. package/dist/social-providers/paypal.d.mts +1 -2
  126. package/dist/social-providers/paypal.mjs +0 -5
  127. package/dist/social-providers/polar.d.mts +1 -2
  128. package/dist/social-providers/polar.mjs +0 -4
  129. package/dist/social-providers/railway.d.mts +1 -2
  130. package/dist/social-providers/railway.mjs +0 -4
  131. package/dist/social-providers/reddit.d.mts +1 -2
  132. package/dist/social-providers/reddit.mjs +0 -4
  133. package/dist/social-providers/roblox.d.mts +1 -2
  134. package/dist/social-providers/roblox.mjs +0 -4
  135. package/dist/social-providers/salesforce.d.mts +1 -2
  136. package/dist/social-providers/salesforce.mjs +0 -5
  137. package/dist/social-providers/slack.d.mts +1 -2
  138. package/dist/social-providers/slack.mjs +0 -4
  139. package/dist/social-providers/spotify.d.mts +1 -2
  140. package/dist/social-providers/spotify.mjs +0 -4
  141. package/dist/social-providers/tiktok.d.mts +1 -2
  142. package/dist/social-providers/tiktok.mjs +0 -4
  143. package/dist/social-providers/twitch.d.mts +1 -2
  144. package/dist/social-providers/twitch.mjs +0 -5
  145. package/dist/social-providers/twitter.d.mts +1 -2
  146. package/dist/social-providers/twitter.mjs +0 -4
  147. package/dist/social-providers/vercel.d.mts +1 -2
  148. package/dist/social-providers/vercel.mjs +0 -4
  149. package/dist/social-providers/vk.d.mts +1 -2
  150. package/dist/social-providers/vk.mjs +0 -4
  151. package/dist/social-providers/wechat.d.mts +1 -2
  152. package/dist/social-providers/wechat.mjs +0 -3
  153. package/dist/social-providers/zoom.d.mts +1 -2
  154. package/dist/social-providers/zoom.mjs +0 -4
  155. package/dist/types/context.d.mts +3 -5
  156. package/dist/types/cookie.d.mts +1 -2
  157. package/dist/types/helper.d.mts +1 -2
  158. package/dist/types/init-options.d.mts +14 -6
  159. package/dist/types/plugin-client.d.mts +2 -2
  160. package/dist/types/plugin.d.mts +6 -3
  161. package/dist/types/secret.d.mts +1 -2
  162. package/dist/utils/db.d.mts +1 -2
  163. package/dist/utils/db.mjs +0 -2
  164. package/dist/utils/deprecate.d.mts +1 -2
  165. package/dist/utils/deprecate.mjs +0 -2
  166. package/dist/utils/error-codes.d.mts +1 -2
  167. package/dist/utils/error-codes.mjs +0 -2
  168. package/dist/utils/fetch-metadata.d.mts +1 -2
  169. package/dist/utils/fetch-metadata.mjs +0 -2
  170. package/dist/utils/id.d.mts +1 -2
  171. package/dist/utils/id.mjs +0 -3
  172. package/dist/utils/ip.d.mts +1 -2
  173. package/dist/utils/ip.mjs +0 -3
  174. package/dist/utils/json.d.mts +1 -2
  175. package/dist/utils/json.mjs +0 -4
  176. package/dist/utils/string.d.mts +1 -2
  177. package/dist/utils/string.mjs +0 -2
  178. package/dist/utils/url.d.mts +1 -2
  179. package/dist/utils/url.mjs +0 -2
  180. package/package.json +11 -9
  181. package/src/api/index.ts +151 -41
  182. package/src/context/endpoint-context.ts +2 -1
  183. package/src/db/adapter/factory.ts +2 -0
  184. package/src/db/adapter/index.ts +8 -0
  185. package/src/instrumentation/tracer.ts +40 -12
  186. package/src/social-providers/index.ts +0 -2
  187. package/src/types/context.ts +2 -3
  188. package/src/types/init-options.ts +13 -4
  189. package/src/types/plugin-client.ts +1 -0
  190. package/src/types/plugin.ts +15 -1
  191. package/dist/api/index.mjs.map +0 -1
  192. package/dist/async_hooks/index.mjs.map +0 -1
  193. package/dist/async_hooks/pure.index.mjs.map +0 -1
  194. package/dist/context/endpoint-context.mjs.map +0 -1
  195. package/dist/context/global.mjs.map +0 -1
  196. package/dist/context/request-state.mjs.map +0 -1
  197. package/dist/context/transaction.mjs.map +0 -1
  198. package/dist/db/adapter/factory.mjs.map +0 -1
  199. package/dist/db/adapter/get-default-field-name.mjs.map +0 -1
  200. package/dist/db/adapter/get-default-model-name.mjs.map +0 -1
  201. package/dist/db/adapter/get-field-attributes.mjs.map +0 -1
  202. package/dist/db/adapter/get-field-name.mjs.map +0 -1
  203. package/dist/db/adapter/get-id-field.mjs.map +0 -1
  204. package/dist/db/adapter/get-model-name.mjs.map +0 -1
  205. package/dist/db/adapter/index.mjs.map +0 -1
  206. package/dist/db/adapter/utils.mjs.map +0 -1
  207. package/dist/db/get-tables.mjs.map +0 -1
  208. package/dist/db/schema/account.mjs.map +0 -1
  209. package/dist/db/schema/rate-limit.mjs.map +0 -1
  210. package/dist/db/schema/session.mjs.map +0 -1
  211. package/dist/db/schema/shared.mjs.map +0 -1
  212. package/dist/db/schema/user.mjs.map +0 -1
  213. package/dist/db/schema/verification.mjs.map +0 -1
  214. package/dist/env/color-depth.mjs.map +0 -1
  215. package/dist/env/env-impl.mjs.map +0 -1
  216. package/dist/env/logger.mjs.map +0 -1
  217. package/dist/error/codes.mjs.map +0 -1
  218. package/dist/error/index.mjs.map +0 -1
  219. package/dist/instrumentation/attributes.mjs.map +0 -1
  220. package/dist/instrumentation/tracer.mjs.map +0 -1
  221. package/dist/oauth2/client-credentials-token.mjs.map +0 -1
  222. package/dist/oauth2/create-authorization-url.mjs.map +0 -1
  223. package/dist/oauth2/refresh-access-token.mjs.map +0 -1
  224. package/dist/oauth2/utils.mjs.map +0 -1
  225. package/dist/oauth2/validate-authorization-code.mjs.map +0 -1
  226. package/dist/oauth2/verify.mjs.map +0 -1
  227. package/dist/social-providers/apple.mjs.map +0 -1
  228. package/dist/social-providers/atlassian.mjs.map +0 -1
  229. package/dist/social-providers/cognito.mjs.map +0 -1
  230. package/dist/social-providers/discord.mjs.map +0 -1
  231. package/dist/social-providers/dropbox.mjs.map +0 -1
  232. package/dist/social-providers/facebook.mjs.map +0 -1
  233. package/dist/social-providers/figma.mjs.map +0 -1
  234. package/dist/social-providers/github.mjs.map +0 -1
  235. package/dist/social-providers/gitlab.mjs.map +0 -1
  236. package/dist/social-providers/google.mjs.map +0 -1
  237. package/dist/social-providers/huggingface.mjs.map +0 -1
  238. package/dist/social-providers/index.mjs.map +0 -1
  239. package/dist/social-providers/kakao.mjs.map +0 -1
  240. package/dist/social-providers/kick.mjs.map +0 -1
  241. package/dist/social-providers/line.mjs.map +0 -1
  242. package/dist/social-providers/linear.mjs.map +0 -1
  243. package/dist/social-providers/linkedin.mjs.map +0 -1
  244. package/dist/social-providers/microsoft-entra-id.mjs.map +0 -1
  245. package/dist/social-providers/naver.mjs.map +0 -1
  246. package/dist/social-providers/notion.mjs.map +0 -1
  247. package/dist/social-providers/paybin.mjs.map +0 -1
  248. package/dist/social-providers/paypal.mjs.map +0 -1
  249. package/dist/social-providers/polar.mjs.map +0 -1
  250. package/dist/social-providers/railway.mjs.map +0 -1
  251. package/dist/social-providers/reddit.mjs.map +0 -1
  252. package/dist/social-providers/roblox.mjs.map +0 -1
  253. package/dist/social-providers/salesforce.mjs.map +0 -1
  254. package/dist/social-providers/slack.mjs.map +0 -1
  255. package/dist/social-providers/spotify.mjs.map +0 -1
  256. package/dist/social-providers/tiktok.mjs.map +0 -1
  257. package/dist/social-providers/twitch.mjs.map +0 -1
  258. package/dist/social-providers/twitter.mjs.map +0 -1
  259. package/dist/social-providers/vercel.mjs.map +0 -1
  260. package/dist/social-providers/vk.mjs.map +0 -1
  261. package/dist/social-providers/wechat.mjs.map +0 -1
  262. package/dist/social-providers/zoom.mjs.map +0 -1
  263. package/dist/utils/db.mjs.map +0 -1
  264. package/dist/utils/deprecate.mjs.map +0 -1
  265. package/dist/utils/error-codes.mjs.map +0 -1
  266. package/dist/utils/fetch-metadata.mjs.map +0 -1
  267. package/dist/utils/id.mjs.map +0 -1
  268. package/dist/utils/ip.mjs.map +0 -1
  269. package/dist/utils/json.mjs.map +0 -1
  270. package/dist/utils/string.mjs.map +0 -1
  271. package/dist/utils/url.mjs.map +0 -1
  272. package/src/context/request-state.test.ts +0 -94
  273. package/src/db/adapter/get-id-field.test.ts +0 -222
  274. package/src/db/test/get-tables.test.ts +0 -116
  275. package/src/env/logger.test.ts +0 -34
  276. package/src/instrumentation/instrumentation.test.ts +0 -139
  277. package/src/oauth2/refresh-access-token.test.ts +0 -90
  278. package/src/oauth2/validate-token.test.ts +0 -229
  279. package/src/utils/deprecate.test.ts +0 -71
  280. package/src/utils/fetch-metadata.test.ts +0 -28
  281. package/src/utils/ip.test.ts +0 -255
@@ -1 +0,0 @@
1
- {"version":3,"file":"wechat.mjs","names":[],"sources":["../../src/social-providers/wechat.ts"],"sourcesContent":["import { betterFetch } from \"@better-fetch/fetch\";\nimport type { OAuth2Tokens, OAuthProvider, ProviderOptions } from \"../oauth2\";\n\n/**\n * WeChat user profile information\n * @see https://developers.weixin.qq.com/doc/oplatform/en/Website_App/WeChat_Login/Wechat_Login.html\n */\nexport interface WeChatProfile extends Record<string, any> {\n\t/**\n\t * User's unique OpenID\n\t */\n\topenid: string;\n\t/**\n\t * User's nickname\n\t */\n\tnickname: string;\n\t/**\n\t * User's avatar image URL\n\t */\n\theadimgurl: string;\n\t/**\n\t * User's privileges\n\t */\n\tprivilege: string[];\n\t/**\n\t * User's UnionID (unique across the developer's various applications)\n\t */\n\tunionid?: string;\n\t/** @note Email is currently unsupported by WeChat */\n\temail?: string;\n}\n\nexport interface WeChatOptions extends ProviderOptions<WeChatProfile> {\n\t/**\n\t * WeChat App ID\n\t */\n\tclientId: string;\n\t/**\n\t * WeChat App Secret\n\t */\n\tclientSecret: string;\n\t/**\n\t * Platform type for WeChat login\n\t * - Currently only supports \"WebsiteApp\" for WeChat Website Application (网站应用)\n\t * @default \"WebsiteApp\"\n\t */\n\tplatformType?: \"WebsiteApp\";\n\n\t/**\n\t * UI language for the WeChat login page\n\t * cn for Simplified Chinese, en for English\n\t * @default \"cn\" if left undefined\n\t */\n\tlang?: \"cn\" | \"en\";\n}\n\nexport const wechat = (options: WeChatOptions) => {\n\treturn {\n\t\tid: \"wechat\",\n\t\tname: \"WeChat\",\n\t\tcreateAuthorizationURL({ state, scopes, redirectURI }) {\n\t\t\tconst _scopes = options.disableDefaultScope ? [] : [\"snsapi_login\"];\n\t\t\toptions.scope && _scopes.push(...options.scope);\n\t\t\tscopes && _scopes.push(...scopes);\n\n\t\t\t// WeChat uses non-standard OAuth2 parameters (appid instead of client_id)\n\t\t\t// and requires a fragment (#wechat_redirect), so we construct the URL manually.\n\t\t\tconst url = new URL(\"https://open.weixin.qq.com/connect/qrconnect\");\n\t\t\turl.searchParams.set(\"scope\", _scopes.join(\",\"));\n\t\t\turl.searchParams.set(\"response_type\", \"code\");\n\t\t\turl.searchParams.set(\"appid\", options.clientId);\n\t\t\turl.searchParams.set(\"redirect_uri\", options.redirectURI || redirectURI);\n\t\t\turl.searchParams.set(\"state\", state);\n\t\t\turl.searchParams.set(\"lang\", options.lang || \"cn\");\n\t\t\turl.hash = \"wechat_redirect\";\n\n\t\t\treturn url;\n\t\t},\n\n\t\t// WeChat uses non-standard token exchange (appid/secret instead of\n\t\t// client_id/client_secret, GET instead of POST), so shared helpers\n\t\t// like validateAuthorizationCode/getOAuth2Tokens cannot be used directly.\n\t\tvalidateAuthorizationCode: async ({ code }) => {\n\t\t\tconst params = new URLSearchParams({\n\t\t\t\tappid: options.clientId,\n\t\t\t\tsecret: options.clientSecret,\n\t\t\t\tcode: code,\n\t\t\t\tgrant_type: \"authorization_code\",\n\t\t\t});\n\n\t\t\tconst { data: tokenData, error } = await betterFetch<{\n\t\t\t\taccess_token: string;\n\t\t\t\texpires_in: number;\n\t\t\t\trefresh_token: string;\n\t\t\t\topenid: string;\n\t\t\t\tscope: string;\n\t\t\t\tunionid?: string;\n\t\t\t\terrcode?: number;\n\t\t\t\terrmsg?: string;\n\t\t\t}>(\n\t\t\t\t\"https://api.weixin.qq.com/sns/oauth2/access_token?\" +\n\t\t\t\t\tparams.toString(),\n\t\t\t\t{\n\t\t\t\t\tmethod: \"GET\",\n\t\t\t\t},\n\t\t\t);\n\n\t\t\tif (error || !tokenData || tokenData.errcode) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Failed to validate authorization code: ${tokenData?.errmsg || error?.message || \"Unknown error\"}`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\ttokenType: \"Bearer\" as const,\n\t\t\t\taccessToken: tokenData.access_token,\n\t\t\t\trefreshToken: tokenData.refresh_token,\n\t\t\t\taccessTokenExpiresAt: new Date(\n\t\t\t\t\tDate.now() + tokenData.expires_in * 1000,\n\t\t\t\t),\n\t\t\t\tscopes: tokenData.scope.split(\",\"),\n\t\t\t\t// WeChat requires openid for the userinfo endpoint, which is\n\t\t\t\t// returned alongside the access token.\n\t\t\t\topenid: tokenData.openid,\n\t\t\t\tunionid: tokenData.unionid,\n\t\t\t};\n\t\t},\n\n\t\trefreshAccessToken: options.refreshAccessToken\n\t\t\t? options.refreshAccessToken\n\t\t\t: async (refreshToken) => {\n\t\t\t\t\tconst params = new URLSearchParams({\n\t\t\t\t\t\tappid: options.clientId,\n\t\t\t\t\t\tgrant_type: \"refresh_token\",\n\t\t\t\t\t\trefresh_token: refreshToken,\n\t\t\t\t\t});\n\n\t\t\t\t\tconst { data: tokenData, error } = await betterFetch<{\n\t\t\t\t\t\taccess_token: string;\n\t\t\t\t\t\texpires_in: number;\n\t\t\t\t\t\trefresh_token: string;\n\t\t\t\t\t\topenid: string;\n\t\t\t\t\t\tscope: string;\n\t\t\t\t\t\terrcode?: number;\n\t\t\t\t\t\terrmsg?: string;\n\t\t\t\t\t}>(\n\t\t\t\t\t\t\"https://api.weixin.qq.com/sns/oauth2/refresh_token?\" +\n\t\t\t\t\t\t\tparams.toString(),\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmethod: \"GET\",\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\n\t\t\t\t\tif (error || !tokenData || tokenData.errcode) {\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t`Failed to refresh access token: ${tokenData?.errmsg || error?.message || \"Unknown error\"}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\treturn {\n\t\t\t\t\t\ttokenType: \"Bearer\" as const,\n\t\t\t\t\t\taccessToken: tokenData.access_token,\n\t\t\t\t\t\trefreshToken: tokenData.refresh_token,\n\t\t\t\t\t\taccessTokenExpiresAt: new Date(\n\t\t\t\t\t\t\tDate.now() + tokenData.expires_in * 1000,\n\t\t\t\t\t\t),\n\t\t\t\t\t\tscopes: tokenData.scope.split(\",\"),\n\t\t\t\t\t};\n\t\t\t\t},\n\n\t\tasync getUserInfo(token) {\n\t\t\tif (options.getUserInfo) {\n\t\t\t\treturn options.getUserInfo(token);\n\t\t\t}\n\n\t\t\tconst openid = (token as OAuth2Tokens & { openid?: string }).openid;\n\n\t\t\tif (!openid) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst params = new URLSearchParams({\n\t\t\t\taccess_token: token.accessToken || \"\",\n\t\t\t\topenid: openid,\n\t\t\t\tlang: \"zh_CN\",\n\t\t\t});\n\n\t\t\tconst { data: profile, error } = await betterFetch<\n\t\t\t\tWeChatProfile & { errcode?: number; errmsg?: string }\n\t\t\t>(\"https://api.weixin.qq.com/sns/userinfo?\" + params.toString(), {\n\t\t\t\tmethod: \"GET\",\n\t\t\t});\n\n\t\t\tif (error || !profile || profile.errcode) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst userMap = await options.mapProfileToUser?.(profile);\n\t\t\treturn {\n\t\t\t\tuser: {\n\t\t\t\t\tid: profile.unionid || profile.openid || openid,\n\t\t\t\t\tname: profile.nickname,\n\t\t\t\t\temail: profile.email || null,\n\t\t\t\t\timage: profile.headimgurl,\n\t\t\t\t\temailVerified: false,\n\t\t\t\t\t...userMap,\n\t\t\t\t},\n\t\t\t\tdata: profile,\n\t\t\t};\n\t\t},\n\t\toptions,\n\t} satisfies OAuthProvider<WeChatProfile, WeChatOptions>;\n};\n"],"mappings":";;;AAwDA,MAAa,UAAU,YAA2B;AACjD,QAAO;EACN,IAAI;EACJ,MAAM;EACN,uBAAuB,EAAE,OAAO,QAAQ,eAAe;GACtD,MAAM,UAAU,QAAQ,sBAAsB,EAAE,GAAG,CAAC,eAAe;AACnE,WAAQ,SAAS,QAAQ,KAAK,GAAG,QAAQ,MAAM;AAC/C,aAAU,QAAQ,KAAK,GAAG,OAAO;GAIjC,MAAM,MAAM,IAAI,IAAI,+CAA+C;AACnE,OAAI,aAAa,IAAI,SAAS,QAAQ,KAAK,IAAI,CAAC;AAChD,OAAI,aAAa,IAAI,iBAAiB,OAAO;AAC7C,OAAI,aAAa,IAAI,SAAS,QAAQ,SAAS;AAC/C,OAAI,aAAa,IAAI,gBAAgB,QAAQ,eAAe,YAAY;AACxE,OAAI,aAAa,IAAI,SAAS,MAAM;AACpC,OAAI,aAAa,IAAI,QAAQ,QAAQ,QAAQ,KAAK;AAClD,OAAI,OAAO;AAEX,UAAO;;EAMR,2BAA2B,OAAO,EAAE,WAAW;GAQ9C,MAAM,EAAE,MAAM,WAAW,UAAU,MAAM,YAUxC,uDAjBc,IAAI,gBAAgB;IAClC,OAAO,QAAQ;IACf,QAAQ,QAAQ;IACV;IACN,YAAY;IACZ,CAAC,CAaO,UAAU,EAClB,EACC,QAAQ,OACR,CACD;AAED,OAAI,SAAS,CAAC,aAAa,UAAU,QACpC,OAAM,IAAI,MACT,0CAA0C,WAAW,UAAU,OAAO,WAAW,kBACjF;AAGF,UAAO;IACN,WAAW;IACX,aAAa,UAAU;IACvB,cAAc,UAAU;IACxB,sBAAsB,IAAI,KACzB,KAAK,KAAK,GAAG,UAAU,aAAa,IACpC;IACD,QAAQ,UAAU,MAAM,MAAM,IAAI;IAGlC,QAAQ,UAAU;IAClB,SAAS,UAAU;IACnB;;EAGF,oBAAoB,QAAQ,qBACzB,QAAQ,qBACR,OAAO,iBAAiB;GAOxB,MAAM,EAAE,MAAM,WAAW,UAAU,MAAM,YASxC,wDAfc,IAAI,gBAAgB;IAClC,OAAO,QAAQ;IACf,YAAY;IACZ,eAAe;IACf,CAAC,CAYO,UAAU,EAClB,EACC,QAAQ,OACR,CACD;AAED,OAAI,SAAS,CAAC,aAAa,UAAU,QACpC,OAAM,IAAI,MACT,mCAAmC,WAAW,UAAU,OAAO,WAAW,kBAC1E;AAGF,UAAO;IACN,WAAW;IACX,aAAa,UAAU;IACvB,cAAc,UAAU;IACxB,sBAAsB,IAAI,KACzB,KAAK,KAAK,GAAG,UAAU,aAAa,IACpC;IACD,QAAQ,UAAU,MAAM,MAAM,IAAI;IAClC;;EAGJ,MAAM,YAAY,OAAO;AACxB,OAAI,QAAQ,YACX,QAAO,QAAQ,YAAY,MAAM;GAGlC,MAAM,SAAU,MAA6C;AAE7D,OAAI,CAAC,OACJ,QAAO;GASR,MAAM,EAAE,MAAM,SAAS,UAAU,MAAM,YAErC,4CARa,IAAI,gBAAgB;IAClC,cAAc,MAAM,eAAe;IAC3B;IACR,MAAM;IACN,CAAC,CAImD,UAAU,EAAE,EAChE,QAAQ,OACR,CAAC;AAEF,OAAI,SAAS,CAAC,WAAW,QAAQ,QAChC,QAAO;GAGR,MAAM,UAAU,MAAM,QAAQ,mBAAmB,QAAQ;AACzD,UAAO;IACN,MAAM;KACL,IAAI,QAAQ,WAAW,QAAQ,UAAU;KACzC,MAAM,QAAQ;KACd,OAAO,QAAQ,SAAS;KACxB,OAAO,QAAQ;KACf,eAAe;KACf,GAAG;KACH;IACD,MAAM;IACN;;EAEF;EACA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"zoom.mjs","names":[],"sources":["../../src/social-providers/zoom.ts"],"sourcesContent":["import { betterFetch } from \"@better-fetch/fetch\";\nimport type { OAuthProvider, ProviderOptions } from \"../oauth2\";\nimport {\n\tgenerateCodeChallenge,\n\trefreshAccessToken,\n\tvalidateAuthorizationCode,\n} from \"../oauth2\";\n\nexport type LoginType =\n\t| 0 /** Facebook OAuth */\n\t| 1 /** Google OAuth */\n\t| 24 /** Apple OAuth */\n\t| 27 /** Microsoft OAuth */\n\t| 97 /** Mobile device */\n\t| 98 /** RingCentral OAuth */\n\t| 99 /** API user */\n\t| 100 /** Zoom Work email */\n\t| 101; /** Single Sign-On (SSO) */\n\nexport type AccountStatus = \"pending\" | \"active\" | \"inactive\";\n\nexport type PronounOption =\n\t| 1 /** Ask the user every time */\n\t| 2 /** Always display */\n\t| 3; /** Do not display */\n\nexport interface PhoneNumber {\n\t/** The country code of the phone number (Example: \"+1\") */\n\tcode: string;\n\n\t/** The country of the phone number (Example: \"US\") */\n\tcountry: string;\n\n\t/** The label for the phone number (Example: \"Mobile\") */\n\tlabel: string;\n\n\t/** The phone number itself (Example: \"800000000\") */\n\tnumber: string;\n\n\t/** Whether the phone number has been verified (Example: true) */\n\tverified: boolean;\n}\n\n/**\n * See the full documentation below:\n * https://developers.zoom.us/docs/api/users/#tag/users/GET/users/{userId}\n */\nexport interface ZoomProfile extends Record<string, any> {\n\t/* cspell:disable-next-line */\n\t/** The user's account ID (Example: \"q6gBJVO5TzexKYTb_I2rpg\") */\n\taccount_id: string;\n\t/** The user's account number (Example: 10009239) */\n\taccount_number: number;\n\t/** The user's cluster (Example: \"us04\") */\n\tcluster: string;\n\t/** The user's CMS ID. Only enabled for Kaltura integration (Example: \"KDcuGIm1QgePTO8WbOqwIQ\") */\n\tcms_user_id: string;\n\t/** The user's cost center (Example: \"cost center\") */\n\tcost_center: string;\n\t/** User create time (Example: \"2018-10-31T04:32:37Z\") */\n\tcreated_at: string;\n\t/** Department (Example: \"Developers\") */\n\tdept: string;\n\t/** User's display name (Example: \"Jill Chill\") */\n\tdisplay_name: string;\n\t/** User's email address (Example: \"jchill@example.com\") */\n\temail: string;\n\t/** User's first name (Example: \"Jill\") */\n\tfirst_name: string;\n\t/* cspell:disable-next-line */\n\t/** IDs of the web groups that the user belongs to (Example: [\"RSMaSp8sTEGK0_oamiA2_w\"]) */\n\tgroup_ids: string[];\n\t/* cspell:disable-next-line */\n\t/** User ID (Example: \"zJKyaiAyTNC-MWjiWC18KQ\") */\n\tid: string;\n\t/* cspell:disable-next-line */\n\t/** IM IDs of the groups that the user belongs to (Example: [\"t-_-d56CSWG-7BF15LLrOw\"]) */\n\tim_group_ids: string[];\n\t/** The user's JID (Example: \"jchill@example.com\") */\n\tjid: string;\n\t/** The user's job title (Example: \"API Developer\") */\n\tjob_title: string;\n\t/** Default language for the Zoom Web Portal (Example: \"en-US\") */\n\tlanguage: string;\n\t/** User last login client version (Example: \"5.9.6.4993(mac)\") */\n\tlast_client_version: string;\n\t/** User last login time (Example: \"2021-05-05T20:40:30Z\") */\n\tlast_login_time: string;\n\t/** User's last name (Example: \"Chill\") */\n\tlast_name: string;\n\t/** The time zone of the user (Example: \"Asia/Shanghai\") */\n\ttimezone: string;\n\t/** User's location (Example: \"Paris\") */\n\tlocation: string;\n\t/** The user's login method (Example: 101) */\n\tlogin_types: LoginType[];\n\t/** User's personal meeting URL (Example: \"example.com\") */\n\tpersonal_meeting_url: string;\n\t/** The URL for user's profile picture (Example: \"example.com\") */\n\tpic_url: string;\n\t/** Personal Meeting ID (PMI) (Example: 3542471135) */\n\tpmi: number;\n\t/** Unique identifier of the user's assigned role (Example: \"0\") */\n\trole_id: string;\n\t/** User's role name (Example: \"Admin\") */\n\trole_name: string;\n\t/** Status of user's account (Example: \"pending\") */\n\tstatus: AccountStatus;\n\t/** Use the personal meeting ID (PMI) for instant meetings (Example: false) */\n\tuse_pmi: boolean;\n\t/** The time and date when the user was created (Example: \"2018-10-31T04:32:37Z\") */\n\tuser_created_at: string;\n\t/** Displays whether user is verified or not (Example: 1) */\n\tverified: number;\n\t/** The user's Zoom Workplace plan option (Example: 64) */\n\tzoom_one_type: number;\n\t/** The user's company (Example: \"Jill\") */\n\tcompany?: string | undefined;\n\t/* cspell:disable-next-line */\n\t/** Custom attributes that have been assigned to the user (Example: [{ \"key\": \"cbf_cywdkexrtqc73f97gd4w6g\", \"name\": \"A1\", \"value\": \"1\" }]) */\n\tcustom_attributes?:\n\t\t| { key: string; name: string; value: string }[]\n\t\t| undefined;\n\t/* cspell:disable-next-line */\n\t/** The employee's unique ID. This field only returns when SAML single sign-on (SSO) is enabled. The `login_type` value is `101` (SSO) (Example: \"HqDyI037Qjili1kNsSIrIg\") */\n\temployee_unique_id?: string | undefined;\n\t/** The manager for the user (Example: \"thill@example.com\") */\n\tmanager?: string | undefined;\n\t/** The phone number's ISO country code (Example: \"+1\") */\n\tphone_numbers?: PhoneNumber[] | undefined;\n\t/** The user's plan type (Example: \"1\") */\n\tplan_united_type?: string | undefined;\n\t/** The user's pronouns (Example: \"3123\") */\n\tpronouns?: string | undefined;\n\t/** The user's display pronouns setting (Example: 1) */\n\tpronouns_option?: PronounOption | undefined;\n\t/** Personal meeting room URL, if the user has one (Example: \"example.com\") */\n\tvanity_url?: string | undefined;\n}\n\nexport interface ZoomOptions extends ProviderOptions<ZoomProfile> {\n\tclientId: string;\n\tpkce?: boolean | undefined;\n}\n\nexport const zoom = (userOptions: ZoomOptions) => {\n\tconst options = {\n\t\tpkce: true,\n\t\t...userOptions,\n\t};\n\n\treturn {\n\t\tid: \"zoom\",\n\t\tname: \"Zoom\",\n\t\tcreateAuthorizationURL: async ({ state, redirectURI, codeVerifier }) => {\n\t\t\tconst params = new URLSearchParams({\n\t\t\t\tresponse_type: \"code\",\n\t\t\t\tredirect_uri: options.redirectURI ? options.redirectURI : redirectURI,\n\t\t\t\tclient_id: options.clientId,\n\t\t\t\tstate,\n\t\t\t});\n\n\t\t\tif (options.pkce) {\n\t\t\t\tconst codeChallenge = await generateCodeChallenge(codeVerifier);\n\t\t\t\tparams.set(\"code_challenge_method\", \"S256\");\n\t\t\t\tparams.set(\"code_challenge\", codeChallenge);\n\t\t\t}\n\n\t\t\tconst url = new URL(\"https://zoom.us/oauth/authorize\");\n\t\t\turl.search = params.toString();\n\n\t\t\treturn url;\n\t\t},\n\t\tvalidateAuthorizationCode: async ({ code, redirectURI, codeVerifier }) => {\n\t\t\treturn validateAuthorizationCode({\n\t\t\t\tcode,\n\t\t\t\tredirectURI: options.redirectURI || redirectURI,\n\t\t\t\tcodeVerifier,\n\t\t\t\toptions,\n\t\t\t\ttokenEndpoint: \"https://zoom.us/oauth/token\",\n\t\t\t\tauthentication: \"post\",\n\t\t\t});\n\t\t},\n\t\trefreshAccessToken: options.refreshAccessToken\n\t\t\t? options.refreshAccessToken\n\t\t\t: async (refreshToken) =>\n\t\t\t\t\trefreshAccessToken({\n\t\t\t\t\t\trefreshToken,\n\t\t\t\t\t\toptions: {\n\t\t\t\t\t\t\tclientId: options.clientId,\n\t\t\t\t\t\t\tclientKey: options.clientKey,\n\t\t\t\t\t\t\tclientSecret: options.clientSecret,\n\t\t\t\t\t\t},\n\t\t\t\t\t\ttokenEndpoint: \"https://zoom.us/oauth/token\",\n\t\t\t\t\t}),\n\t\tasync getUserInfo(token) {\n\t\t\tif (options.getUserInfo) {\n\t\t\t\treturn options.getUserInfo(token);\n\t\t\t}\n\t\t\tconst { data: profile, error } = await betterFetch<ZoomProfile>(\n\t\t\t\t\"https://api.zoom.us/v2/users/me\",\n\t\t\t\t{\n\t\t\t\t\theaders: {\n\t\t\t\t\t\tauthorization: `Bearer ${token.accessToken}`,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t);\n\n\t\t\tif (error) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst userMap = await options.mapProfileToUser?.(profile);\n\n\t\t\treturn {\n\t\t\t\tuser: {\n\t\t\t\t\tid: profile.id,\n\t\t\t\t\tname: profile.display_name,\n\t\t\t\t\timage: profile.pic_url,\n\t\t\t\t\temail: profile.email,\n\t\t\t\t\temailVerified: Boolean(profile.verified),\n\t\t\t\t\t...userMap,\n\t\t\t\t},\n\t\t\t\tdata: {\n\t\t\t\t\t...profile,\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t} satisfies OAuthProvider<ZoomProfile>;\n};\n"],"mappings":";;;;;;;AAiJA,MAAa,QAAQ,gBAA6B;CACjD,MAAM,UAAU;EACf,MAAM;EACN,GAAG;EACH;AAED,QAAO;EACN,IAAI;EACJ,MAAM;EACN,wBAAwB,OAAO,EAAE,OAAO,aAAa,mBAAmB;GACvE,MAAM,SAAS,IAAI,gBAAgB;IAClC,eAAe;IACf,cAAc,QAAQ,cAAc,QAAQ,cAAc;IAC1D,WAAW,QAAQ;IACnB;IACA,CAAC;AAEF,OAAI,QAAQ,MAAM;IACjB,MAAM,gBAAgB,MAAM,sBAAsB,aAAa;AAC/D,WAAO,IAAI,yBAAyB,OAAO;AAC3C,WAAO,IAAI,kBAAkB,cAAc;;GAG5C,MAAM,MAAM,IAAI,IAAI,kCAAkC;AACtD,OAAI,SAAS,OAAO,UAAU;AAE9B,UAAO;;EAER,2BAA2B,OAAO,EAAE,MAAM,aAAa,mBAAmB;AACzE,UAAO,0BAA0B;IAChC;IACA,aAAa,QAAQ,eAAe;IACpC;IACA;IACA,eAAe;IACf,gBAAgB;IAChB,CAAC;;EAEH,oBAAoB,QAAQ,qBACzB,QAAQ,qBACR,OAAO,iBACP,mBAAmB;GAClB;GACA,SAAS;IACR,UAAU,QAAQ;IAClB,WAAW,QAAQ;IACnB,cAAc,QAAQ;IACtB;GACD,eAAe;GACf,CAAC;EACL,MAAM,YAAY,OAAO;AACxB,OAAI,QAAQ,YACX,QAAO,QAAQ,YAAY,MAAM;GAElC,MAAM,EAAE,MAAM,SAAS,UAAU,MAAM,YACtC,mCACA,EACC,SAAS,EACR,eAAe,UAAU,MAAM,eAC/B,EACD,CACD;AAED,OAAI,MACH,QAAO;GAGR,MAAM,UAAU,MAAM,QAAQ,mBAAmB,QAAQ;AAEzD,UAAO;IACN,MAAM;KACL,IAAI,QAAQ;KACZ,MAAM,QAAQ;KACd,OAAO,QAAQ;KACf,OAAO,QAAQ;KACf,eAAe,QAAQ,QAAQ,SAAS;KACxC,GAAG;KACH;IACD,MAAM,EACL,GAAG,SACH;IACD;;EAEF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"db.mjs","names":[],"sources":["../../src/utils/db.ts"],"sourcesContent":["import type { DBFieldAttribute } from \"../db\";\n\n/**\n * Filters output data by removing fields with the `returned: false` attribute.\n * This ensures sensitive fields are not exposed in API responses.\n */\nexport function filterOutputFields<T extends Record<string, unknown> | null>(\n\tdata: T,\n\tadditionalFields: Record<string, DBFieldAttribute> | undefined,\n): T {\n\tif (!data || !additionalFields) {\n\t\treturn data;\n\t}\n\tconst returnFiltered = Object.entries(additionalFields)\n\t\t.filter(([, { returned }]) => returned === false)\n\t\t.map(([key]) => key);\n\treturn Object.entries(structuredClone(data))\n\t\t.filter(([key]) => !returnFiltered.includes(key))\n\t\t.reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {} as T);\n}\n"],"mappings":";;;;;AAMA,SAAgB,mBACf,MACA,kBACI;AACJ,KAAI,CAAC,QAAQ,CAAC,iBACb,QAAO;CAER,MAAM,iBAAiB,OAAO,QAAQ,iBAAiB,CACrD,QAAQ,GAAG,EAAE,gBAAgB,aAAa,MAAM,CAChD,KAAK,CAAC,SAAS,IAAI;AACrB,QAAO,OAAO,QAAQ,gBAAgB,KAAK,CAAC,CAC1C,QAAQ,CAAC,SAAS,CAAC,eAAe,SAAS,IAAI,CAAC,CAChD,QAAQ,KAAK,CAAC,KAAK,YAAY;EAAE,GAAG;GAAM,MAAM;EAAO,GAAG,EAAE,CAAM"}
@@ -1 +0,0 @@
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"}
@@ -1 +0,0 @@
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 +0,0 @@
1
- {"version":3,"file":"fetch-metadata.mjs","names":[],"sources":["../../src/utils/fetch-metadata.ts"],"sourcesContent":["export function isBrowserFetchRequest(headers?: Headers | null): boolean {\n\treturn headers?.get(\"sec-fetch-mode\") === \"cors\";\n}\n"],"mappings":";AAAA,SAAgB,sBAAsB,SAAmC;AACxE,QAAO,SAAS,IAAI,iBAAiB,KAAK"}
@@ -1 +0,0 @@
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"}
@@ -1 +0,0 @@
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":";;;;;;AAyBA,SAAgB,UAAU,IAAqB;AAC9C,QAAO,EAAE,MAAM,CAAC,UAAU,GAAG,CAAC,WAAW,EAAE,MAAM,CAAC,UAAU,GAAG,CAAC;;;;;AAMjE,SAAS,OAAO,IAAqB;AACpC,QAAO,EAAE,MAAM,CAAC,UAAU,GAAG,CAAC;;;;;;AAO/B,SAAS,sBAAsB,MAA6B;CAC3D,MAAM,QAAQ,KAAK,aAAa;AAGhC,KAAI,MAAM,WAAW,UAAU,EAAE;EAChC,MAAM,WAAW,MAAM,UAAU,EAAE;AAEnC,MAAI,EAAE,MAAM,CAAC,UAAU,SAAS,CAAC,QAChC,QAAO;;CAKT,MAAM,QAAQ,KAAK,MAAM,IAAI;AAC7B,KAAI,MAAM,WAAW,KAAK,MAAM,IAAI,aAAa,KAAK,QAAQ;EAC7D,MAAM,WAAW,MAAM;AACvB,MAAI,YAAY,EAAE,MAAM,CAAC,UAAU,SAAS,CAAC,QAC5C,QAAO;;AAMT,KAAI,MAAM,SAAS,UAAU,IAAI,MAAM,SAAS,SAAS,EAAE;EAC1D,MAAM,SAAS,WAAW,KAAK;AAC/B,MACC,OAAO,WAAW,KAClB,OAAO,OAAO,UACd,OAAO,OAAO,UACd,OAAO,OAAO,UACd,OAAO,OAAO,UACd,OAAO,OAAO,UACd,OAAO,OAAO,UACd,OAAO,MACP,OAAO,GAOP,QAAO,GAJO,OAAO,SAAS,OAAO,GAAG,UAAU,GAAG,EAAE,EAAE,GAAG,CAI5C,GAHF,OAAO,SAAS,OAAO,GAAG,UAAU,GAAG,EAAE,EAAE,GAAG,CAGnC,GAFX,OAAO,SAAS,OAAO,GAAG,UAAU,GAAG,EAAE,EAAE,GAAG,CAE1B,GADpB,OAAO,SAAS,OAAO,GAAG,UAAU,GAAG,EAAE,EAAE,GAAG;;AAK9D,QAAO;;;;;;AAOR,SAAS,WAAW,MAAwB;AAE3C,KAAI,KAAK,SAAS,KAAK,EAAE;EACxB,MAAM,QAAQ,KAAK,MAAM,KAAK;EAC9B,MAAM,OAAO,MAAM,KAAK,MAAM,GAAG,MAAM,IAAI,GAAG,EAAE;EAChD,MAAM,QAAQ,MAAM,KAAK,MAAM,GAAG,MAAM,IAAI,GAAG,EAAE;EAIjD,MAAM,gBADc,IACgB,KAAK,SAAS,MAAM;EACxD,MAAM,QAAQ,MAAM,cAAc,CAAC,KAAK,OAAO;EAG/C,MAAM,aAAa,KAAK,KAAK,MAAM,EAAE,SAAS,GAAG,IAAI,CAAC;EACtD,MAAM,cAAc,MAAM,KAAK,MAAM,EAAE,SAAS,GAAG,IAAI,CAAC;AAExD,SAAO;GAAC,GAAG;GAAY,GAAG;GAAO,GAAG;GAAY;;AAIjD,QAAO,KAAK,MAAM,IAAI,CAAC,KAAK,MAAM,EAAE,SAAS,GAAG,IAAI,CAAC;;;;;;AAOtD,SAAS,cACR,MACA,cACS;CACT,MAAM,SAAS,WAAW,KAAK;AAE/B,KAAI,gBAAgB,eAAe,KAAK;EAGvC,IAAI,gBADW;AAoBf,SAjBqB,OAAO,KAAK,UAAU;AAC1C,OAAI,iBAAiB,EACpB,QAAO;AAER,OAAI,iBAAiB,IAAI;AACxB,qBAAiB;AACjB,WAAO;;GAMR,MAAM,SAFQ,OAAO,SAAS,OAAO,GAAG,IAC1B,SAAW,KAAK,gBAAkB;AAEhD,mBAAgB;AAChB,UAAO,OAAO,SAAS,GAAG,CAAC,SAAS,GAAG,IAAI;IAC1C,CAEkB,KAAK,IAAI,CAAC,aAAa;;AAG5C,QAAO,OAAO,KAAK,IAAI,CAAC,aAAa;;;;;;;;;;;;;;;;;;;;;AAsBtC,SAAgB,YACf,IACA,UAA8B,EAAE,EACvB;AAET,KAAI,EAAE,MAAM,CAAC,UAAU,GAAG,CAAC,QAC1B,QAAO,GAAG,aAAa;AAIxB,KAAI,CAAC,OAAO,GAAG,CAEd,QAAO,GAAG,aAAa;CAIxB,MAAM,OAAO,sBAAsB,GAAG;AACtC,KAAI,KACH,QAAO,KAAK,aAAa;AAK1B,QAAO,cAAc,IADA,QAAQ,cAAc,GACL;;;;;;;;;;AAWvC,SAAgB,mBAAmB,IAAY,MAAsB;AAGpE,QAAO,GAAG,GAAG,GAAG"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"json.mjs","names":[],"sources":["../../src/utils/json.ts"],"sourcesContent":["import { logger } from \"../env\";\n\nconst iso8601Regex = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?Z$/;\n\nfunction reviveDate(value: unknown): any {\n\tif (typeof value === \"string\" && iso8601Regex.test(value)) {\n\t\tconst date = new Date(value);\n\t\tif (!isNaN(date.getTime())) {\n\t\t\treturn date;\n\t\t}\n\t}\n\treturn value;\n}\n\n/**\n * Recursively walk a pre-parsed object and convert ISO 8601 date strings\n * to Date instances. This handles the case where a Redis client (or similar)\n * returns already-parsed JSON objects whose date fields are still strings.\n */\nfunction reviveDates(value: unknown): any {\n\tif (value === null || value === undefined) {\n\t\treturn value;\n\t}\n\tif (typeof value === \"string\") {\n\t\treturn reviveDate(value);\n\t}\n\tif (value instanceof Date) {\n\t\treturn value;\n\t}\n\tif (Array.isArray(value)) {\n\t\treturn value.map(reviveDates);\n\t}\n\tif (typeof value === \"object\") {\n\t\tconst result: Record<string, any> = {};\n\t\tfor (const key of Object.keys(value)) {\n\t\t\tresult[key] = reviveDates((value as Record<string, any>)[key]);\n\t\t}\n\t\treturn result;\n\t}\n\treturn value;\n}\n\nexport function safeJSONParse<T>(data: unknown): T | null {\n\ttry {\n\t\tif (typeof data !== \"string\") {\n\t\t\tif (data === null || data === undefined) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn reviveDates(data) as T;\n\t\t}\n\t\treturn JSON.parse(data, (_, value) => reviveDate(value));\n\t} catch (e) {\n\t\tlogger.error(\"Error parsing JSON\", { error: e });\n\t\treturn null;\n\t}\n}\n"],"mappings":";;;;AAEA,MAAM,eAAe;AAErB,SAAS,WAAW,OAAqB;AACxC,KAAI,OAAO,UAAU,YAAY,aAAa,KAAK,MAAM,EAAE;EAC1D,MAAM,OAAO,IAAI,KAAK,MAAM;AAC5B,MAAI,CAAC,MAAM,KAAK,SAAS,CAAC,CACzB,QAAO;;AAGT,QAAO;;;;;;;AAQR,SAAS,YAAY,OAAqB;AACzC,KAAI,UAAU,QAAQ,UAAU,OAC/B,QAAO;AAER,KAAI,OAAO,UAAU,SACpB,QAAO,WAAW,MAAM;AAEzB,KAAI,iBAAiB,KACpB,QAAO;AAER,KAAI,MAAM,QAAQ,MAAM,CACvB,QAAO,MAAM,IAAI,YAAY;AAE9B,KAAI,OAAO,UAAU,UAAU;EAC9B,MAAM,SAA8B,EAAE;AACtC,OAAK,MAAM,OAAO,OAAO,KAAK,MAAM,CACnC,QAAO,OAAO,YAAa,MAA8B,KAAK;AAE/D,SAAO;;AAER,QAAO;;AAGR,SAAgB,cAAiB,MAAyB;AACzD,KAAI;AACH,MAAI,OAAO,SAAS,UAAU;AAC7B,OAAI,SAAS,QAAQ,SAAS,OAC7B,QAAO;AAER,UAAO,YAAY,KAAK;;AAEzB,SAAO,KAAK,MAAM,OAAO,GAAG,UAAU,WAAW,MAAM,CAAC;UAChD,GAAG;AACX,SAAO,MAAM,sBAAsB,EAAE,OAAO,GAAG,CAAC;AAChD,SAAO"}
@@ -1 +0,0 @@
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"}
@@ -1 +0,0 @@
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"}
@@ -1,94 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import type { RequestStateWeakMap } from "./request-state";
3
- import {
4
- defineRequestState,
5
- getCurrentRequestState,
6
- hasRequestState,
7
- runWithRequestState,
8
- } from "./request-state";
9
-
10
- describe("request-state", () => {
11
- describe("runWithRequestState", () => {
12
- it("should execute function within request state context", async () => {
13
- const store: RequestStateWeakMap = new WeakMap();
14
- const result = await runWithRequestState(store, async () => {
15
- const hasStore = await hasRequestState();
16
- expect(hasStore).toBe(true);
17
- return "success";
18
- });
19
- expect(result).toBe("success");
20
- });
21
-
22
- it("should isolate request states between concurrent requests", async () => {
23
- const store1: RequestStateWeakMap = new WeakMap();
24
- const store2: RequestStateWeakMap = new WeakMap();
25
-
26
- const { get, set } = defineRequestState(() => ({}) as { id: string });
27
-
28
- const [result1, result2] = await Promise.all([
29
- runWithRequestState(store1, async () => {
30
- await set({ id: "store1" });
31
- // Simulate some async work
32
- await new Promise((resolve) => setTimeout(resolve, 10));
33
- return await get();
34
- }),
35
- runWithRequestState(store2, async () => {
36
- await set({ id: "store2" });
37
- // Simulate some async work
38
- await new Promise((resolve) => setTimeout(resolve, 5));
39
- return await get();
40
- }),
41
- ]);
42
-
43
- expect(result1).toEqual({ id: "store1" });
44
- expect(result2).toEqual({ id: "store2" });
45
- });
46
-
47
- it("should support nested async operations", async () => {
48
- const store: RequestStateWeakMap = new WeakMap();
49
- const { get, set } = defineRequestState(() => ({ value: 1 }));
50
-
51
- await runWithRequestState(store, async () => {
52
- const nestedResult = await (async () => {
53
- const current = await get();
54
- await set({ value: (current?.value || 0) + 1 });
55
- return await get();
56
- })();
57
-
58
- expect(nestedResult).toEqual({ value: 2 });
59
- expect(await get()).toEqual({ value: 2 });
60
- });
61
- });
62
- });
63
-
64
- describe("hasRequestState", () => {
65
- it("should return false when not in request state context", async () => {
66
- const hasStore = await hasRequestState();
67
- expect(hasStore).toBe(false);
68
- });
69
-
70
- it("should return true when in request state context", async () => {
71
- const store: RequestStateWeakMap = new WeakMap();
72
- await runWithRequestState(store, async () => {
73
- const hasStore = await hasRequestState();
74
- expect(hasStore).toBe(true);
75
- });
76
- });
77
- });
78
-
79
- describe("getCurrentRequestState", () => {
80
- it("should throw error when not in request state context", async () => {
81
- await expect(getCurrentRequestState()).rejects.toThrow(
82
- "No request state found",
83
- );
84
- });
85
-
86
- it("should return the current store when in context", async () => {
87
- const store: RequestStateWeakMap = new WeakMap();
88
- await runWithRequestState(store, async () => {
89
- const currentStore = await getCurrentRequestState();
90
- expect(currentStore).toBe(store);
91
- });
92
- });
93
- });
94
- });
@@ -1,222 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import type { BetterAuthOptions } from "../../types";
3
- import type { BetterAuthDBSchema } from "../type";
4
- import { initGetIdField } from "./get-id-field";
5
-
6
- const minimalSchema: BetterAuthDBSchema = {
7
- user: {
8
- modelName: "user",
9
- fields: {
10
- name: { type: "string" },
11
- email: { type: "string" },
12
- },
13
- },
14
- };
15
-
16
- const uuidRegex =
17
- /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
18
-
19
- function getField(
20
- options: BetterAuthOptions,
21
- initExtra?: Partial<Parameters<typeof initGetIdField>[0]>,
22
- fieldExtra?: { customModelName?: string; forceAllowId?: boolean },
23
- ) {
24
- const idField = initGetIdField({
25
- schema: minimalSchema,
26
- options,
27
- ...initExtra,
28
- });
29
- return idField({
30
- customModelName: fieldExtra?.customModelName ?? "user",
31
- forceAllowId: fieldExtra?.forceAllowId,
32
- });
33
- }
34
-
35
- describe("defaultValue priority", () => {
36
- it("should return undefined when disableIdGeneration is true", () => {
37
- const field = getField(
38
- { database: {} as any },
39
- { disableIdGeneration: true },
40
- );
41
- expect(field.defaultValue).toBeUndefined();
42
- });
43
-
44
- it("should return undefined when generateId is false", () => {
45
- const value = getField({
46
- database: {} as any,
47
- advanced: { database: { generateId: false } },
48
- }).defaultValue?.();
49
- expect(value).toBeUndefined();
50
- });
51
-
52
- it("should return undefined when generateId is 'serial'", () => {
53
- const value = getField({
54
- database: {} as any,
55
- advanced: { database: { generateId: "serial" } },
56
- }).defaultValue?.();
57
- expect(value).toBeUndefined();
58
- });
59
-
60
- it("should use generateId function over 'uuid' and customIdGenerator", () => {
61
- const value = getField(
62
- {
63
- database: {} as any,
64
- advanced: { database: { generateId: () => "fn-id" } },
65
- },
66
- { customIdGenerator: () => "adapter-id" },
67
- ).defaultValue?.();
68
- expect(value).toBe("fn-id");
69
- });
70
-
71
- it("should use 'uuid' over customIdGenerator", () => {
72
- const value = getField(
73
- {
74
- database: {} as any,
75
- advanced: { database: { generateId: "uuid" } },
76
- },
77
- { customIdGenerator: () => "adapter-id", supportsUUIDs: false },
78
- ).defaultValue?.();
79
- expect(value).toMatch(uuidRegex);
80
- });
81
-
82
- it("should use customIdGenerator when generateId is not set", () => {
83
- const value = getField(
84
- { database: {} as any },
85
- { customIdGenerator: () => "adapter-id" },
86
- ).defaultValue?.();
87
- expect(value).toBe("adapter-id");
88
- });
89
-
90
- it("should fall back to default id generation", () => {
91
- const value = getField({ database: {} as any }).defaultValue?.();
92
- expect(typeof value).toBe("string");
93
- expect(value).not.toMatch(uuidRegex);
94
- });
95
- });
96
-
97
- describe("type and required", () => {
98
- it("should have type 'number' when generateId is 'serial'", () => {
99
- const field = getField({
100
- database: {} as any,
101
- advanced: { database: { generateId: "serial" } },
102
- });
103
- expect(field.type).toBe("number");
104
- expect(field.required).toBe(false);
105
- });
106
-
107
- it("should have type 'string' by default", () => {
108
- const field = getField({ database: {} as any });
109
- expect(field.type).toBe("string");
110
- expect(field.required).toBe(true);
111
- });
112
-
113
- it("should not generate id when useUUIDs and supportsUUIDs", () => {
114
- const field = getField(
115
- {
116
- database: {} as any,
117
- advanced: { database: { generateId: "uuid" } },
118
- },
119
- { supportsUUIDs: true },
120
- );
121
- expect(field.required).toBe(false);
122
- expect(field.defaultValue).toBeUndefined();
123
- });
124
- });
125
-
126
- describe("transform.input", () => {
127
- it("should return undefined for falsy value", () => {
128
- const field = getField({ database: {} as any });
129
- expect(field.transform.input(undefined)).toBeUndefined();
130
- expect(field.transform.input(null)).toBeUndefined();
131
- expect(field.transform.input("")).toBeUndefined();
132
- });
133
-
134
- it("should return value as-is by default", () => {
135
- const field = getField({ database: {} as any });
136
- expect(field.transform.input("some-id")).toBe("some-id");
137
- });
138
-
139
- describe("serial", () => {
140
- it("should convert string to number", () => {
141
- const field = getField({
142
- database: {} as any,
143
- advanced: { database: { generateId: "serial" } },
144
- });
145
- expect(field.transform.input("42")).toBe(42);
146
- });
147
-
148
- it("should return undefined for non-numeric string", () => {
149
- const field = getField({
150
- database: {} as any,
151
- advanced: { database: { generateId: "serial" } },
152
- });
153
- expect(field.transform.input("not-a-number")).toBeUndefined();
154
- });
155
- });
156
-
157
- describe("uuid", () => {
158
- it("should return value as-is when shouldGenerateId and not forceAllowId", () => {
159
- const field = getField(
160
- {
161
- database: {} as any,
162
- advanced: { database: { generateId: "uuid" } },
163
- },
164
- { supportsUUIDs: false },
165
- );
166
- const uuid = crypto.randomUUID();
167
- expect(field.transform.input(uuid)).toBe(uuid);
168
- });
169
-
170
- it("should return undefined when supportsUUIDs (DB handles it)", () => {
171
- const field = getField(
172
- {
173
- database: {} as any,
174
- advanced: { database: { generateId: "uuid" } },
175
- },
176
- { supportsUUIDs: true },
177
- );
178
- expect(field.transform.input("some-value")).toBeUndefined();
179
- });
180
-
181
- it("should accept valid UUID when forceAllowId is true", () => {
182
- const uuid = crypto.randomUUID();
183
- const field = getField(
184
- {
185
- database: {} as any,
186
- advanced: { database: { generateId: "uuid" } },
187
- },
188
- { supportsUUIDs: false },
189
- { forceAllowId: true },
190
- );
191
- expect(field.transform.input(uuid)).toBe(uuid);
192
- });
193
-
194
- it("should generate new UUID for non-string value when DB doesn't support UUIDs", () => {
195
- const field = getField(
196
- {
197
- database: {} as any,
198
- advanced: { database: { generateId: "uuid" } },
199
- },
200
- { supportsUUIDs: false },
201
- { forceAllowId: true },
202
- );
203
- const result = field.transform.input(123);
204
- expect(result).toMatch(uuidRegex);
205
- });
206
- });
207
- });
208
-
209
- describe("transform.output", () => {
210
- it("should return undefined for falsy value", () => {
211
- const field = getField({ database: {} as any });
212
- expect(field.transform.output(undefined)).toBeUndefined();
213
- expect(field.transform.output(null)).toBeUndefined();
214
- expect(field.transform.output("")).toBeUndefined();
215
- });
216
-
217
- it("should convert value to string", () => {
218
- const field = getField({ database: {} as any });
219
- expect(field.transform.output(123)).toBe("123");
220
- expect(field.transform.output("abc")).toBe("abc");
221
- });
222
- });
@@ -1,116 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { getAuthTables } from "../get-tables";
3
-
4
- describe("getAuthTables", () => {
5
- it("should use correct field name for refreshTokenExpiresAt", () => {
6
- const tables = getAuthTables({
7
- account: {
8
- fields: {
9
- refreshTokenExpiresAt: "custom_refresh_token_expires_at",
10
- },
11
- },
12
- });
13
-
14
- const accountTable = tables.account;
15
- const refreshTokenExpiresAtField =
16
- accountTable!.fields.refreshTokenExpiresAt!;
17
-
18
- expect(refreshTokenExpiresAtField.fieldName).toBe(
19
- "custom_refresh_token_expires_at",
20
- );
21
- });
22
-
23
- it("should not use accessTokenExpiresAt field name for refreshTokenExpiresAt", () => {
24
- const tables = getAuthTables({
25
- account: {
26
- fields: {
27
- accessTokenExpiresAt: "custom_access_token_expires_at",
28
- refreshTokenExpiresAt: "custom_refresh_token_expires_at",
29
- },
30
- },
31
- });
32
-
33
- const accountTable = tables.account;
34
- const refreshTokenExpiresAtField =
35
- accountTable!.fields.refreshTokenExpiresAt!;
36
- const accessTokenExpiresAtField =
37
- accountTable!.fields.accessTokenExpiresAt!;
38
-
39
- expect(refreshTokenExpiresAtField.fieldName).toBe(
40
- "custom_refresh_token_expires_at",
41
- );
42
- expect(accessTokenExpiresAtField.fieldName).toBe(
43
- "custom_access_token_expires_at",
44
- );
45
- expect(refreshTokenExpiresAtField.fieldName).not.toBe(
46
- accessTokenExpiresAtField.fieldName,
47
- );
48
- });
49
-
50
- it("should use default field names when no custom names provided", () => {
51
- const tables = getAuthTables({});
52
-
53
- const accountTable = tables.account;
54
- const refreshTokenExpiresAtField =
55
- accountTable!.fields.refreshTokenExpiresAt!;
56
- const accessTokenExpiresAtField =
57
- accountTable!.fields.accessTokenExpiresAt!;
58
-
59
- expect(refreshTokenExpiresAtField.fieldName).toBe("refreshTokenExpiresAt");
60
- expect(accessTokenExpiresAtField.fieldName).toBe("accessTokenExpiresAt");
61
- });
62
-
63
- it("should merge additionalFields into verification table metadata", () => {
64
- const tables = getAuthTables({
65
- verification: {
66
- additionalFields: {
67
- newField: {
68
- fieldName: "new_field",
69
- type: "string",
70
- },
71
- },
72
- },
73
- });
74
-
75
- const verificationTable = tables.verification;
76
- const newField = verificationTable!.fields.newField!;
77
-
78
- console.log(newField);
79
- expect(newField).not.toBeUndefined();
80
- expect(newField.fieldName).toBe("new_field");
81
- expect(newField.type).toBe("string");
82
- });
83
-
84
- it("should exclude verification table when secondaryStorage is configured", () => {
85
- const tables = getAuthTables({
86
- secondaryStorage: {
87
- get: async () => null,
88
- set: async () => {},
89
- delete: async () => {},
90
- },
91
- });
92
-
93
- expect(tables.verification).toBeUndefined();
94
- });
95
-
96
- it("should include verification table when storeInDatabase is true", () => {
97
- const tables = getAuthTables({
98
- secondaryStorage: {
99
- get: async () => null,
100
- set: async () => {},
101
- delete: async () => {},
102
- },
103
- verification: {
104
- storeInDatabase: true,
105
- },
106
- });
107
-
108
- expect(tables.verification).toBeDefined();
109
- });
110
-
111
- it("should include verification table when no secondaryStorage", () => {
112
- const tables = getAuthTables({});
113
-
114
- expect(tables.verification).toBeDefined();
115
- });
116
- });
@@ -1,34 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import type { LogLevel } from "./logger";
3
- import { shouldPublishLog } from "./logger";
4
-
5
- describe("shouldPublishLog", () => {
6
- const testCases: {
7
- currentLogLevel: LogLevel;
8
- logLevel: LogLevel;
9
- expected: boolean;
10
- }[] = [
11
- { currentLogLevel: "debug", logLevel: "debug", expected: true },
12
- { currentLogLevel: "debug", logLevel: "info", expected: true },
13
- { currentLogLevel: "debug", logLevel: "warn", expected: true },
14
- { currentLogLevel: "debug", logLevel: "error", expected: true },
15
- { currentLogLevel: "info", logLevel: "debug", expected: false },
16
- { currentLogLevel: "info", logLevel: "info", expected: true },
17
- { currentLogLevel: "info", logLevel: "warn", expected: true },
18
- { currentLogLevel: "info", logLevel: "error", expected: true },
19
- { currentLogLevel: "warn", logLevel: "debug", expected: false },
20
- { currentLogLevel: "warn", logLevel: "info", expected: false },
21
- { currentLogLevel: "warn", logLevel: "warn", expected: true },
22
- { currentLogLevel: "warn", logLevel: "error", expected: true },
23
- { currentLogLevel: "error", logLevel: "debug", expected: false },
24
- { currentLogLevel: "error", logLevel: "info", expected: false },
25
- { currentLogLevel: "error", logLevel: "warn", expected: false },
26
- { currentLogLevel: "error", logLevel: "error", expected: true },
27
- ];
28
-
29
- testCases.forEach(({ currentLogLevel, logLevel, expected }) => {
30
- it(`should return "${expected}" when currentLogLevel is "${currentLogLevel}" and logLevel is "${logLevel}"`, () => {
31
- expect(shouldPublishLog(currentLogLevel, logLevel)).toBe(expected);
32
- });
33
- });
34
- });