@better-auth/core 1.5.6 → 1.5.7-beta.1

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 (169) hide show
  1. package/dist/api/index.d.mts +41 -11
  2. package/dist/api/index.mjs +1 -2
  3. package/dist/api/index.mjs.map +1 -1
  4. package/dist/async_hooks/index.mjs +1 -1
  5. package/dist/async_hooks/pure.index.mjs +1 -1
  6. package/dist/async_hooks/pure.index.mjs.map +1 -1
  7. package/dist/context/endpoint-context.d.mts +1 -1
  8. package/dist/context/endpoint-context.mjs +1 -2
  9. package/dist/context/endpoint-context.mjs.map +1 -1
  10. package/dist/context/global.mjs +2 -2
  11. package/dist/context/global.mjs.map +1 -1
  12. package/dist/context/index.mjs +1 -2
  13. package/dist/context/request-state.mjs +1 -2
  14. package/dist/context/request-state.mjs.map +1 -1
  15. package/dist/context/transaction.mjs +1 -2
  16. package/dist/context/transaction.mjs.map +1 -1
  17. package/dist/db/adapter/factory.mjs +1 -2
  18. package/dist/db/adapter/factory.mjs.map +1 -1
  19. package/dist/db/adapter/get-default-field-name.mjs +1 -2
  20. package/dist/db/adapter/get-default-field-name.mjs.map +1 -1
  21. package/dist/db/adapter/get-default-model-name.mjs +1 -2
  22. package/dist/db/adapter/get-default-model-name.mjs.map +1 -1
  23. package/dist/db/adapter/get-field-attributes.mjs +1 -2
  24. package/dist/db/adapter/get-field-attributes.mjs.map +1 -1
  25. package/dist/db/adapter/get-field-name.mjs +1 -2
  26. package/dist/db/adapter/get-field-name.mjs.map +1 -1
  27. package/dist/db/adapter/get-id-field.mjs +1 -2
  28. package/dist/db/adapter/get-id-field.mjs.map +1 -1
  29. package/dist/db/adapter/get-model-name.mjs +1 -2
  30. package/dist/db/adapter/get-model-name.mjs.map +1 -1
  31. package/dist/db/adapter/index.mjs +1 -2
  32. package/dist/db/adapter/index.mjs.map +1 -1
  33. package/dist/db/adapter/utils.mjs +1 -1
  34. package/dist/db/adapter/utils.mjs.map +1 -1
  35. package/dist/db/get-tables.mjs +1 -1
  36. package/dist/db/index.mjs +1 -2
  37. package/dist/db/schema/account.mjs +1 -2
  38. package/dist/db/schema/account.mjs.map +1 -1
  39. package/dist/db/schema/rate-limit.mjs +1 -2
  40. package/dist/db/schema/rate-limit.mjs.map +1 -1
  41. package/dist/db/schema/session.mjs +1 -2
  42. package/dist/db/schema/session.mjs.map +1 -1
  43. package/dist/db/schema/shared.mjs +1 -2
  44. package/dist/db/schema/shared.mjs.map +1 -1
  45. package/dist/db/schema/user.mjs +1 -2
  46. package/dist/db/schema/user.mjs.map +1 -1
  47. package/dist/db/schema/verification.mjs +1 -2
  48. package/dist/db/schema/verification.mjs.map +1 -1
  49. package/dist/env/color-depth.mjs +1 -2
  50. package/dist/env/color-depth.mjs.map +1 -1
  51. package/dist/env/env-impl.mjs +1 -1
  52. package/dist/env/env-impl.mjs.map +1 -1
  53. package/dist/env/index.mjs +1 -2
  54. package/dist/env/logger.mjs +1 -2
  55. package/dist/env/logger.mjs.map +1 -1
  56. package/dist/error/codes.mjs +1 -2
  57. package/dist/error/codes.mjs.map +1 -1
  58. package/dist/error/index.mjs +1 -2
  59. package/dist/error/index.mjs.map +1 -1
  60. package/dist/index.mjs +1 -1
  61. package/dist/instrumentation/attributes.mjs +1 -2
  62. package/dist/instrumentation/attributes.mjs.map +1 -1
  63. package/dist/instrumentation/index.mjs +1 -2
  64. package/dist/instrumentation/tracer.mjs +2 -3
  65. package/dist/instrumentation/tracer.mjs.map +1 -1
  66. package/dist/oauth2/client-credentials-token.mjs +1 -2
  67. package/dist/oauth2/client-credentials-token.mjs.map +1 -1
  68. package/dist/oauth2/create-authorization-url.mjs +1 -2
  69. package/dist/oauth2/create-authorization-url.mjs.map +1 -1
  70. package/dist/oauth2/index.mjs +1 -2
  71. package/dist/oauth2/refresh-access-token.mjs +1 -2
  72. package/dist/oauth2/refresh-access-token.mjs.map +1 -1
  73. package/dist/oauth2/utils.mjs +1 -2
  74. package/dist/oauth2/utils.mjs.map +1 -1
  75. package/dist/oauth2/validate-authorization-code.mjs +1 -2
  76. package/dist/oauth2/validate-authorization-code.mjs.map +1 -1
  77. package/dist/oauth2/verify.mjs +1 -2
  78. package/dist/oauth2/verify.mjs.map +1 -1
  79. package/dist/social-providers/apple.mjs +1 -2
  80. package/dist/social-providers/apple.mjs.map +1 -1
  81. package/dist/social-providers/atlassian.mjs +1 -2
  82. package/dist/social-providers/atlassian.mjs.map +1 -1
  83. package/dist/social-providers/cognito.mjs +1 -2
  84. package/dist/social-providers/cognito.mjs.map +1 -1
  85. package/dist/social-providers/discord.mjs +1 -2
  86. package/dist/social-providers/discord.mjs.map +1 -1
  87. package/dist/social-providers/dropbox.mjs +1 -2
  88. package/dist/social-providers/dropbox.mjs.map +1 -1
  89. package/dist/social-providers/facebook.mjs +1 -2
  90. package/dist/social-providers/facebook.mjs.map +1 -1
  91. package/dist/social-providers/figma.mjs +1 -2
  92. package/dist/social-providers/figma.mjs.map +1 -1
  93. package/dist/social-providers/github.mjs +1 -2
  94. package/dist/social-providers/github.mjs.map +1 -1
  95. package/dist/social-providers/gitlab.mjs +1 -2
  96. package/dist/social-providers/gitlab.mjs.map +1 -1
  97. package/dist/social-providers/google.mjs +1 -2
  98. package/dist/social-providers/google.mjs.map +1 -1
  99. package/dist/social-providers/huggingface.mjs +1 -2
  100. package/dist/social-providers/huggingface.mjs.map +1 -1
  101. package/dist/social-providers/index.mjs +1 -2
  102. package/dist/social-providers/index.mjs.map +1 -1
  103. package/dist/social-providers/kakao.mjs +1 -2
  104. package/dist/social-providers/kakao.mjs.map +1 -1
  105. package/dist/social-providers/kick.mjs +1 -2
  106. package/dist/social-providers/kick.mjs.map +1 -1
  107. package/dist/social-providers/line.mjs +1 -2
  108. package/dist/social-providers/line.mjs.map +1 -1
  109. package/dist/social-providers/linear.mjs +1 -2
  110. package/dist/social-providers/linear.mjs.map +1 -1
  111. package/dist/social-providers/linkedin.mjs +1 -2
  112. package/dist/social-providers/linkedin.mjs.map +1 -1
  113. package/dist/social-providers/microsoft-entra-id.mjs +1 -2
  114. package/dist/social-providers/microsoft-entra-id.mjs.map +1 -1
  115. package/dist/social-providers/naver.mjs +1 -2
  116. package/dist/social-providers/naver.mjs.map +1 -1
  117. package/dist/social-providers/notion.mjs +1 -2
  118. package/dist/social-providers/notion.mjs.map +1 -1
  119. package/dist/social-providers/paybin.mjs +1 -2
  120. package/dist/social-providers/paybin.mjs.map +1 -1
  121. package/dist/social-providers/paypal.mjs +1 -2
  122. package/dist/social-providers/paypal.mjs.map +1 -1
  123. package/dist/social-providers/polar.mjs +1 -2
  124. package/dist/social-providers/polar.mjs.map +1 -1
  125. package/dist/social-providers/railway.mjs +1 -2
  126. package/dist/social-providers/railway.mjs.map +1 -1
  127. package/dist/social-providers/reddit.mjs +1 -2
  128. package/dist/social-providers/reddit.mjs.map +1 -1
  129. package/dist/social-providers/roblox.mjs +1 -2
  130. package/dist/social-providers/roblox.mjs.map +1 -1
  131. package/dist/social-providers/salesforce.mjs +1 -2
  132. package/dist/social-providers/salesforce.mjs.map +1 -1
  133. package/dist/social-providers/slack.mjs +1 -2
  134. package/dist/social-providers/slack.mjs.map +1 -1
  135. package/dist/social-providers/spotify.mjs +1 -2
  136. package/dist/social-providers/spotify.mjs.map +1 -1
  137. package/dist/social-providers/tiktok.mjs +1 -2
  138. package/dist/social-providers/tiktok.mjs.map +1 -1
  139. package/dist/social-providers/twitch.mjs +1 -2
  140. package/dist/social-providers/twitch.mjs.map +1 -1
  141. package/dist/social-providers/twitter.mjs +1 -2
  142. package/dist/social-providers/twitter.mjs.map +1 -1
  143. package/dist/social-providers/vercel.mjs +1 -2
  144. package/dist/social-providers/vercel.mjs.map +1 -1
  145. package/dist/social-providers/vk.mjs +1 -2
  146. package/dist/social-providers/vk.mjs.map +1 -1
  147. package/dist/social-providers/wechat.mjs +1 -2
  148. package/dist/social-providers/wechat.mjs.map +1 -1
  149. package/dist/social-providers/zoom.mjs +1 -2
  150. package/dist/social-providers/zoom.mjs.map +1 -1
  151. package/dist/types/context.d.mts +1 -3
  152. package/dist/types/plugin.d.mts +4 -1
  153. package/dist/utils/db.mjs +1 -1
  154. package/dist/utils/deprecate.mjs +1 -1
  155. package/dist/utils/error-codes.mjs +1 -1
  156. package/dist/utils/fetch-metadata.mjs +1 -1
  157. package/dist/utils/id.mjs +1 -2
  158. package/dist/utils/id.mjs.map +1 -1
  159. package/dist/utils/ip.mjs +1 -2
  160. package/dist/utils/ip.mjs.map +1 -1
  161. package/dist/utils/json.mjs +1 -2
  162. package/dist/utils/json.mjs.map +1 -1
  163. package/dist/utils/string.mjs +1 -1
  164. package/dist/utils/url.mjs +1 -1
  165. package/package.json +5 -5
  166. package/src/api/index.ts +151 -41
  167. package/src/context/endpoint-context.ts +2 -1
  168. package/src/types/context.ts +1 -3
  169. package/src/types/plugin.ts +14 -1
@@ -2,7 +2,6 @@ import { refreshAccessToken } from "../oauth2/refresh-access-token.mjs";
2
2
  import { validateAuthorizationCode } from "../oauth2/validate-authorization-code.mjs";
3
3
  import "../oauth2/index.mjs";
4
4
  import { betterFetch } from "@better-fetch/fetch";
5
-
6
5
  //#region src/social-providers/tiktok.ts
7
6
  const tiktok = (options) => {
8
7
  const tokenEndpoint = "https://open.tiktokapis.com/v2/oauth/token/";
@@ -58,7 +57,7 @@ const tiktok = (options) => {
58
57
  options
59
58
  };
60
59
  };
61
-
62
60
  //#endregion
63
61
  export { tiktok };
62
+
64
63
  //# sourceMappingURL=tiktok.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"tiktok.mjs","names":[],"sources":["../../src/social-providers/tiktok.ts"],"sourcesContent":["import { betterFetch } from \"@better-fetch/fetch\";\nimport type { OAuthProvider, ProviderOptions } from \"../oauth2\";\nimport { refreshAccessToken, validateAuthorizationCode } from \"../oauth2\";\n\n/**\n * [More info](https://developers.tiktok.com/doc/tiktok-api-v2-get-user-info/)\n */\nexport interface TiktokProfile extends Record<string, any> {\n\tdata: {\n\t\tuser: {\n\t\t\t/**\n\t\t\t * The unique identification of the user in the current application.Open id\n\t\t\t * for the client.\n\t\t\t *\n\t\t\t * To return this field, add `fields=open_id` in the user profile request's query parameter.\n\t\t\t */\n\t\t\topen_id: string;\n\t\t\t/**\n\t\t\t * The unique identification of the user across different apps for the same developer.\n\t\t\t * For example, if a partner has X number of clients,\n\t\t\t * it will get X number of open_id for the same TikTok user,\n\t\t\t * but one persistent union_id for the particular user.\n\t\t\t *\n\t\t\t * To return this field, add `fields=union_id` in the user profile request's query parameter.\n\t\t\t */\n\t\t\tunion_id?: string | undefined;\n\t\t\t/**\n\t\t\t * User's profile image.\n\t\t\t *\n\t\t\t * To return this field, add `fields=avatar_url` in the user profile request's query parameter.\n\t\t\t */\n\t\t\tavatar_url?: string | undefined;\n\t\t\t/**\n\t\t\t * User`s profile image in 100x100 size.\n\t\t\t *\n\t\t\t * To return this field, add `fields=avatar_url_100` in the user profile request's query parameter.\n\t\t\t */\n\t\t\tavatar_url_100?: string | undefined;\n\t\t\t/**\n\t\t\t * User's profile image with higher resolution\n\t\t\t *\n\t\t\t * To return this field, add `fields=avatar_url_100` in the user profile request's query parameter.\n\t\t\t */\n\t\t\tavatar_large_url: string;\n\t\t\t/**\n\t\t\t * User's profile name\n\t\t\t *\n\t\t\t * To return this field, add `fields=display_name` in the user profile request's query parameter.\n\t\t\t */\n\t\t\tdisplay_name: string;\n\t\t\t/**\n\t\t\t * User's username.\n\t\t\t *\n\t\t\t * To return this field, add `fields=username` in the user profile request's query parameter.\n\t\t\t */\n\t\t\tusername: string;\n\t\t\t/** @note Email is currently unsupported by TikTok */\n\t\t\temail?: string | undefined;\n\t\t\t/**\n\t\t\t * User's bio description if there is a valid one.\n\t\t\t *\n\t\t\t * To return this field, add `fields=bio_description` in the user profile request's query parameter.\n\t\t\t */\n\t\t\tbio_description?: string | undefined;\n\t\t\t/**\n\t\t\t * The link to user's TikTok profile page.\n\t\t\t *\n\t\t\t * To return this field, add `fields=profile_deep_link` in the user profile request's query parameter.\n\t\t\t */\n\t\t\tprofile_deep_link?: string | undefined;\n\t\t\t/**\n\t\t\t * Whether TikTok has provided a verified badge to the account after confirming\n\t\t\t * that it belongs to the user it represents.\n\t\t\t *\n\t\t\t * To return this field, add `fields=is_verified` in the user profile request's query parameter.\n\t\t\t */\n\t\t\tis_verified?: boolean | undefined;\n\t\t\t/**\n\t\t\t * User's followers count.\n\t\t\t *\n\t\t\t * To return this field, add `fields=follower_count` in the user profile request's query parameter.\n\t\t\t */\n\t\t\tfollower_count?: number | undefined;\n\t\t\t/**\n\t\t\t * The number of accounts that the user is following.\n\t\t\t *\n\t\t\t * To return this field, add `fields=following_count` in the user profile request's query parameter.\n\t\t\t */\n\t\t\tfollowing_count?: number | undefined;\n\t\t\t/**\n\t\t\t * The total number of likes received by the user across all of their videos.\n\t\t\t *\n\t\t\t * To return this field, add `fields=likes_count` in the user profile request's query parameter.\n\t\t\t */\n\t\t\tlikes_count?: number | undefined;\n\t\t\t/**\n\t\t\t * The total number of publicly posted videos by the user.\n\t\t\t *\n\t\t\t * To return this field, add `fields=video_count` in the user profile request's query parameter.\n\t\t\t */\n\t\t\tvideo_count?: number | undefined;\n\t\t};\n\t};\n\terror?:\n\t\t| {\n\t\t\t\t/**\n\t\t\t\t * The error category in string.\n\t\t\t\t */\n\t\t\t\tcode?: string;\n\t\t\t\t/**\n\t\t\t\t * The error message in string.\n\t\t\t\t */\n\t\t\t\tmessage?: string;\n\t\t\t\t/**\n\t\t\t\t * The error message in string.\n\t\t\t\t */\n\t\t\t\tlog_id?: string;\n\t\t }\n\t\t| undefined;\n}\n\nexport interface TiktokOptions extends ProviderOptions {\n\t// Client ID is not used in TikTok, we delete it from the options\n\tclientId?: never | undefined;\n\tclientSecret: string;\n\tclientKey: string;\n}\n\nexport const tiktok = (options: TiktokOptions) => {\n\tconst tokenEndpoint = \"https://open.tiktokapis.com/v2/oauth/token/\";\n\treturn {\n\t\tid: \"tiktok\",\n\t\tname: \"TikTok\",\n\t\tcreateAuthorizationURL({ state, scopes, redirectURI }) {\n\t\t\tconst _scopes = options.disableDefaultScope ? [] : [\"user.info.profile\"];\n\t\t\tif (options.scope) _scopes.push(...options.scope);\n\t\t\tif (scopes) _scopes.push(...scopes);\n\t\t\treturn new URL(\n\t\t\t\t`https://www.tiktok.com/v2/auth/authorize?scope=${_scopes.join(\n\t\t\t\t\t\",\",\n\t\t\t\t)}&response_type=code&client_key=${options.clientKey}&redirect_uri=${encodeURIComponent(\n\t\t\t\t\toptions.redirectURI || redirectURI,\n\t\t\t\t)}&state=${state}`,\n\t\t\t);\n\t\t},\n\n\t\tvalidateAuthorizationCode: async ({ code, redirectURI }) => {\n\t\t\treturn validateAuthorizationCode({\n\t\t\t\tcode,\n\t\t\t\tredirectURI: options.redirectURI || redirectURI,\n\t\t\t\toptions: {\n\t\t\t\t\tclientKey: options.clientKey,\n\t\t\t\t\tclientSecret: options.clientSecret,\n\t\t\t\t},\n\t\t\t\ttokenEndpoint,\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\treturn refreshAccessToken({\n\t\t\t\t\t\trefreshToken,\n\t\t\t\t\t\toptions: {\n\t\t\t\t\t\t\tclientSecret: options.clientSecret,\n\t\t\t\t\t\t},\n\t\t\t\t\t\ttokenEndpoint,\n\t\t\t\t\t\tauthentication: \"post\",\n\t\t\t\t\t\textraParams: {\n\t\t\t\t\t\t\tclient_key: options.clientKey,\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\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\n\t\t\tconst fields = [\n\t\t\t\t\"open_id\",\n\t\t\t\t\"avatar_large_url\",\n\t\t\t\t\"display_name\",\n\t\t\t\t\"username\",\n\t\t\t];\n\t\t\tconst { data: profile, error } = await betterFetch<TiktokProfile>(\n\t\t\t\t`https://open.tiktokapis.com/v2/user/info/?fields=${fields.join(\",\")}`,\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\treturn {\n\t\t\t\tuser: {\n\t\t\t\t\temail: profile.data.user.email || profile.data.user.username,\n\t\t\t\t\tid: profile.data.user.open_id,\n\t\t\t\t\tname:\n\t\t\t\t\t\tprofile.data.user.display_name || profile.data.user.username || \"\",\n\t\t\t\t\timage: profile.data.user.avatar_large_url,\n\t\t\t\t\temailVerified: false,\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<TiktokProfile, TiktokOptions>;\n};\n"],"mappings":";;;;;;AAgIA,MAAa,UAAU,YAA2B;CACjD,MAAM,gBAAgB;AACtB,QAAO;EACN,IAAI;EACJ,MAAM;EACN,uBAAuB,EAAE,OAAO,QAAQ,eAAe;GACtD,MAAM,UAAU,QAAQ,sBAAsB,EAAE,GAAG,CAAC,oBAAoB;AACxE,OAAI,QAAQ,MAAO,SAAQ,KAAK,GAAG,QAAQ,MAAM;AACjD,OAAI,OAAQ,SAAQ,KAAK,GAAG,OAAO;AACnC,UAAO,IAAI,IACV,kDAAkD,QAAQ,KACzD,IACA,CAAC,iCAAiC,QAAQ,UAAU,gBAAgB,mBACpE,QAAQ,eAAe,YACvB,CAAC,SAAS,QACX;;EAGF,2BAA2B,OAAO,EAAE,MAAM,kBAAkB;AAC3D,UAAO,0BAA0B;IAChC;IACA,aAAa,QAAQ,eAAe;IACpC,SAAS;KACR,WAAW,QAAQ;KACnB,cAAc,QAAQ;KACtB;IACD;IACA,CAAC;;EAEH,oBAAoB,QAAQ,qBACzB,QAAQ,qBACR,OAAO,iBAAiB;AACxB,UAAO,mBAAmB;IACzB;IACA,SAAS,EACR,cAAc,QAAQ,cACtB;IACD;IACA,gBAAgB;IAChB,aAAa,EACZ,YAAY,QAAQ,WACpB;IACD,CAAC;;EAEL,MAAM,YAAY,OAAO;AACxB,OAAI,QAAQ,YACX,QAAO,QAAQ,YAAY,MAAM;GASlC,MAAM,EAAE,MAAM,SAAS,UAAU,MAAM,YACtC,oDAPc;IACd;IACA;IACA;IACA;IACA,CAE2D,KAAK,IAAI,IACpE,EACC,SAAS,EACR,eAAe,UAAU,MAAM,eAC/B,EACD,CACD;AAED,OAAI,MACH,QAAO;AAGR,UAAO;IACN,MAAM;KACL,OAAO,QAAQ,KAAK,KAAK,SAAS,QAAQ,KAAK,KAAK;KACpD,IAAI,QAAQ,KAAK,KAAK;KACtB,MACC,QAAQ,KAAK,KAAK,gBAAgB,QAAQ,KAAK,KAAK,YAAY;KACjE,OAAO,QAAQ,KAAK,KAAK;KACzB,eAAe;KACf;IACD,MAAM;IACN;;EAEF;EACA"}
1
+ {"version":3,"file":"tiktok.mjs","names":[],"sources":["../../src/social-providers/tiktok.ts"],"sourcesContent":["import { betterFetch } from \"@better-fetch/fetch\";\nimport type { OAuthProvider, ProviderOptions } from \"../oauth2\";\nimport { refreshAccessToken, validateAuthorizationCode } from \"../oauth2\";\n\n/**\n * [More info](https://developers.tiktok.com/doc/tiktok-api-v2-get-user-info/)\n */\nexport interface TiktokProfile extends Record<string, any> {\n\tdata: {\n\t\tuser: {\n\t\t\t/**\n\t\t\t * The unique identification of the user in the current application.Open id\n\t\t\t * for the client.\n\t\t\t *\n\t\t\t * To return this field, add `fields=open_id` in the user profile request's query parameter.\n\t\t\t */\n\t\t\topen_id: string;\n\t\t\t/**\n\t\t\t * The unique identification of the user across different apps for the same developer.\n\t\t\t * For example, if a partner has X number of clients,\n\t\t\t * it will get X number of open_id for the same TikTok user,\n\t\t\t * but one persistent union_id for the particular user.\n\t\t\t *\n\t\t\t * To return this field, add `fields=union_id` in the user profile request's query parameter.\n\t\t\t */\n\t\t\tunion_id?: string | undefined;\n\t\t\t/**\n\t\t\t * User's profile image.\n\t\t\t *\n\t\t\t * To return this field, add `fields=avatar_url` in the user profile request's query parameter.\n\t\t\t */\n\t\t\tavatar_url?: string | undefined;\n\t\t\t/**\n\t\t\t * User`s profile image in 100x100 size.\n\t\t\t *\n\t\t\t * To return this field, add `fields=avatar_url_100` in the user profile request's query parameter.\n\t\t\t */\n\t\t\tavatar_url_100?: string | undefined;\n\t\t\t/**\n\t\t\t * User's profile image with higher resolution\n\t\t\t *\n\t\t\t * To return this field, add `fields=avatar_url_100` in the user profile request's query parameter.\n\t\t\t */\n\t\t\tavatar_large_url: string;\n\t\t\t/**\n\t\t\t * User's profile name\n\t\t\t *\n\t\t\t * To return this field, add `fields=display_name` in the user profile request's query parameter.\n\t\t\t */\n\t\t\tdisplay_name: string;\n\t\t\t/**\n\t\t\t * User's username.\n\t\t\t *\n\t\t\t * To return this field, add `fields=username` in the user profile request's query parameter.\n\t\t\t */\n\t\t\tusername: string;\n\t\t\t/** @note Email is currently unsupported by TikTok */\n\t\t\temail?: string | undefined;\n\t\t\t/**\n\t\t\t * User's bio description if there is a valid one.\n\t\t\t *\n\t\t\t * To return this field, add `fields=bio_description` in the user profile request's query parameter.\n\t\t\t */\n\t\t\tbio_description?: string | undefined;\n\t\t\t/**\n\t\t\t * The link to user's TikTok profile page.\n\t\t\t *\n\t\t\t * To return this field, add `fields=profile_deep_link` in the user profile request's query parameter.\n\t\t\t */\n\t\t\tprofile_deep_link?: string | undefined;\n\t\t\t/**\n\t\t\t * Whether TikTok has provided a verified badge to the account after confirming\n\t\t\t * that it belongs to the user it represents.\n\t\t\t *\n\t\t\t * To return this field, add `fields=is_verified` in the user profile request's query parameter.\n\t\t\t */\n\t\t\tis_verified?: boolean | undefined;\n\t\t\t/**\n\t\t\t * User's followers count.\n\t\t\t *\n\t\t\t * To return this field, add `fields=follower_count` in the user profile request's query parameter.\n\t\t\t */\n\t\t\tfollower_count?: number | undefined;\n\t\t\t/**\n\t\t\t * The number of accounts that the user is following.\n\t\t\t *\n\t\t\t * To return this field, add `fields=following_count` in the user profile request's query parameter.\n\t\t\t */\n\t\t\tfollowing_count?: number | undefined;\n\t\t\t/**\n\t\t\t * The total number of likes received by the user across all of their videos.\n\t\t\t *\n\t\t\t * To return this field, add `fields=likes_count` in the user profile request's query parameter.\n\t\t\t */\n\t\t\tlikes_count?: number | undefined;\n\t\t\t/**\n\t\t\t * The total number of publicly posted videos by the user.\n\t\t\t *\n\t\t\t * To return this field, add `fields=video_count` in the user profile request's query parameter.\n\t\t\t */\n\t\t\tvideo_count?: number | undefined;\n\t\t};\n\t};\n\terror?:\n\t\t| {\n\t\t\t\t/**\n\t\t\t\t * The error category in string.\n\t\t\t\t */\n\t\t\t\tcode?: string;\n\t\t\t\t/**\n\t\t\t\t * The error message in string.\n\t\t\t\t */\n\t\t\t\tmessage?: string;\n\t\t\t\t/**\n\t\t\t\t * The error message in string.\n\t\t\t\t */\n\t\t\t\tlog_id?: string;\n\t\t }\n\t\t| undefined;\n}\n\nexport interface TiktokOptions extends ProviderOptions {\n\t// Client ID is not used in TikTok, we delete it from the options\n\tclientId?: never | undefined;\n\tclientSecret: string;\n\tclientKey: string;\n}\n\nexport const tiktok = (options: TiktokOptions) => {\n\tconst tokenEndpoint = \"https://open.tiktokapis.com/v2/oauth/token/\";\n\treturn {\n\t\tid: \"tiktok\",\n\t\tname: \"TikTok\",\n\t\tcreateAuthorizationURL({ state, scopes, redirectURI }) {\n\t\t\tconst _scopes = options.disableDefaultScope ? [] : [\"user.info.profile\"];\n\t\t\tif (options.scope) _scopes.push(...options.scope);\n\t\t\tif (scopes) _scopes.push(...scopes);\n\t\t\treturn new URL(\n\t\t\t\t`https://www.tiktok.com/v2/auth/authorize?scope=${_scopes.join(\n\t\t\t\t\t\",\",\n\t\t\t\t)}&response_type=code&client_key=${options.clientKey}&redirect_uri=${encodeURIComponent(\n\t\t\t\t\toptions.redirectURI || redirectURI,\n\t\t\t\t)}&state=${state}`,\n\t\t\t);\n\t\t},\n\n\t\tvalidateAuthorizationCode: async ({ code, redirectURI }) => {\n\t\t\treturn validateAuthorizationCode({\n\t\t\t\tcode,\n\t\t\t\tredirectURI: options.redirectURI || redirectURI,\n\t\t\t\toptions: {\n\t\t\t\t\tclientKey: options.clientKey,\n\t\t\t\t\tclientSecret: options.clientSecret,\n\t\t\t\t},\n\t\t\t\ttokenEndpoint,\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\treturn refreshAccessToken({\n\t\t\t\t\t\trefreshToken,\n\t\t\t\t\t\toptions: {\n\t\t\t\t\t\t\tclientSecret: options.clientSecret,\n\t\t\t\t\t\t},\n\t\t\t\t\t\ttokenEndpoint,\n\t\t\t\t\t\tauthentication: \"post\",\n\t\t\t\t\t\textraParams: {\n\t\t\t\t\t\t\tclient_key: options.clientKey,\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\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\n\t\t\tconst fields = [\n\t\t\t\t\"open_id\",\n\t\t\t\t\"avatar_large_url\",\n\t\t\t\t\"display_name\",\n\t\t\t\t\"username\",\n\t\t\t];\n\t\t\tconst { data: profile, error } = await betterFetch<TiktokProfile>(\n\t\t\t\t`https://open.tiktokapis.com/v2/user/info/?fields=${fields.join(\",\")}`,\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\treturn {\n\t\t\t\tuser: {\n\t\t\t\t\temail: profile.data.user.email || profile.data.user.username,\n\t\t\t\t\tid: profile.data.user.open_id,\n\t\t\t\t\tname:\n\t\t\t\t\t\tprofile.data.user.display_name || profile.data.user.username || \"\",\n\t\t\t\t\timage: profile.data.user.avatar_large_url,\n\t\t\t\t\temailVerified: false,\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<TiktokProfile, TiktokOptions>;\n};\n"],"mappings":";;;;;AAgIA,MAAa,UAAU,YAA2B;CACjD,MAAM,gBAAgB;AACtB,QAAO;EACN,IAAI;EACJ,MAAM;EACN,uBAAuB,EAAE,OAAO,QAAQ,eAAe;GACtD,MAAM,UAAU,QAAQ,sBAAsB,EAAE,GAAG,CAAC,oBAAoB;AACxE,OAAI,QAAQ,MAAO,SAAQ,KAAK,GAAG,QAAQ,MAAM;AACjD,OAAI,OAAQ,SAAQ,KAAK,GAAG,OAAO;AACnC,UAAO,IAAI,IACV,kDAAkD,QAAQ,KACzD,IACA,CAAC,iCAAiC,QAAQ,UAAU,gBAAgB,mBACpE,QAAQ,eAAe,YACvB,CAAC,SAAS,QACX;;EAGF,2BAA2B,OAAO,EAAE,MAAM,kBAAkB;AAC3D,UAAO,0BAA0B;IAChC;IACA,aAAa,QAAQ,eAAe;IACpC,SAAS;KACR,WAAW,QAAQ;KACnB,cAAc,QAAQ;KACtB;IACD;IACA,CAAC;;EAEH,oBAAoB,QAAQ,qBACzB,QAAQ,qBACR,OAAO,iBAAiB;AACxB,UAAO,mBAAmB;IACzB;IACA,SAAS,EACR,cAAc,QAAQ,cACtB;IACD;IACA,gBAAgB;IAChB,aAAa,EACZ,YAAY,QAAQ,WACpB;IACD,CAAC;;EAEL,MAAM,YAAY,OAAO;AACxB,OAAI,QAAQ,YACX,QAAO,QAAQ,YAAY,MAAM;GASlC,MAAM,EAAE,MAAM,SAAS,UAAU,MAAM,YACtC,oDAPc;IACd;IACA;IACA;IACA;IACA,CAE2D,KAAK,IAAI,IACpE,EACC,SAAS,EACR,eAAe,UAAU,MAAM,eAC/B,EACD,CACD;AAED,OAAI,MACH,QAAO;AAGR,UAAO;IACN,MAAM;KACL,OAAO,QAAQ,KAAK,KAAK,SAAS,QAAQ,KAAK,KAAK;KACpD,IAAI,QAAQ,KAAK,KAAK;KACtB,MACC,QAAQ,KAAK,KAAK,gBAAgB,QAAQ,KAAK,KAAK,YAAY;KACjE,OAAO,QAAQ,KAAK,KAAK;KACzB,eAAe;KACf;IACD,MAAM;IACN;;EAEF;EACA"}
@@ -5,7 +5,6 @@ import { refreshAccessToken } from "../oauth2/refresh-access-token.mjs";
5
5
  import { validateAuthorizationCode } from "../oauth2/validate-authorization-code.mjs";
6
6
  import "../oauth2/index.mjs";
7
7
  import { decodeJwt } from "jose";
8
-
9
8
  //#region src/social-providers/twitch.ts
10
9
  const twitch = (options) => {
11
10
  const tokenEndpoint = "https://id.twitch.tv/oauth2/token";
@@ -74,7 +73,7 @@ const twitch = (options) => {
74
73
  options
75
74
  };
76
75
  };
77
-
78
76
  //#endregion
79
77
  export { twitch };
78
+
80
79
  //# sourceMappingURL=twitch.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"twitch.mjs","names":[],"sources":["../../src/social-providers/twitch.ts"],"sourcesContent":["import { decodeJwt } from \"jose\";\nimport { logger } from \"../env\";\nimport type { OAuthProvider, ProviderOptions } from \"../oauth2\";\nimport {\n\tcreateAuthorizationURL,\n\trefreshAccessToken,\n\tvalidateAuthorizationCode,\n} from \"../oauth2\";\n\n/**\n * @see https://dev.twitch.tv/docs/authentication/getting-tokens-oidc/#requesting-claims\n */\nexport interface TwitchProfile {\n\t/**\n\t * The sub of the user\n\t */\n\tsub: string;\n\t/**\n\t * The preferred username of the user\n\t */\n\tpreferred_username: string;\n\t/**\n\t * The email of the user\n\t */\n\temail: string;\n\t/**\n\t * Indicate if this user has a verified email.\n\t */\n\temail_verified: boolean;\n\t/**\n\t * The picture of the user\n\t */\n\tpicture: string;\n}\n\nexport interface TwitchOptions extends ProviderOptions<TwitchProfile> {\n\tclientId: string;\n\tclaims?: string[] | undefined;\n}\nexport const twitch = (options: TwitchOptions) => {\n\tconst tokenEndpoint = \"https://id.twitch.tv/oauth2/token\";\n\treturn {\n\t\tid: \"twitch\",\n\t\tname: \"Twitch\",\n\t\tcreateAuthorizationURL({ state, scopes, redirectURI }) {\n\t\t\tconst _scopes = options.disableDefaultScope\n\t\t\t\t? []\n\t\t\t\t: [\"user:read:email\", \"openid\"];\n\t\t\tif (options.scope) _scopes.push(...options.scope);\n\t\t\tif (scopes) _scopes.push(...scopes);\n\t\t\treturn createAuthorizationURL({\n\t\t\t\tid: \"twitch\",\n\t\t\t\tredirectURI,\n\t\t\t\toptions,\n\t\t\t\tauthorizationEndpoint: \"https://id.twitch.tv/oauth2/authorize\",\n\t\t\t\tscopes: _scopes,\n\t\t\t\tstate,\n\t\t\t\tclaims: options.claims || [\n\t\t\t\t\t\"email\",\n\t\t\t\t\t\"email_verified\",\n\t\t\t\t\t\"preferred_username\",\n\t\t\t\t\t\"picture\",\n\t\t\t\t],\n\t\t\t});\n\t\t},\n\t\tvalidateAuthorizationCode: async ({ code, redirectURI }) => {\n\t\t\treturn validateAuthorizationCode({\n\t\t\t\tcode,\n\t\t\t\tredirectURI,\n\t\t\t\toptions,\n\t\t\t\ttokenEndpoint,\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\treturn refreshAccessToken({\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,\n\t\t\t\t\t});\n\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 idToken = token.idToken;\n\t\t\tif (!idToken) {\n\t\t\t\tlogger.error(\"No idToken found in token\");\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tconst profile = decodeJwt(idToken) as TwitchProfile;\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.sub,\n\t\t\t\t\tname: profile.preferred_username,\n\t\t\t\t\temail: profile.email,\n\t\t\t\t\timage: profile.picture,\n\t\t\t\t\temailVerified: profile.email_verified,\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<TwitchProfile>;\n};\n"],"mappings":";;;;;;;;;AAuCA,MAAa,UAAU,YAA2B;CACjD,MAAM,gBAAgB;AACtB,QAAO;EACN,IAAI;EACJ,MAAM;EACN,uBAAuB,EAAE,OAAO,QAAQ,eAAe;GACtD,MAAM,UAAU,QAAQ,sBACrB,EAAE,GACF,CAAC,mBAAmB,SAAS;AAChC,OAAI,QAAQ,MAAO,SAAQ,KAAK,GAAG,QAAQ,MAAM;AACjD,OAAI,OAAQ,SAAQ,KAAK,GAAG,OAAO;AACnC,UAAO,uBAAuB;IAC7B,IAAI;IACJ;IACA;IACA,uBAAuB;IACvB,QAAQ;IACR;IACA,QAAQ,QAAQ,UAAU;KACzB;KACA;KACA;KACA;KACA;IACD,CAAC;;EAEH,2BAA2B,OAAO,EAAE,MAAM,kBAAkB;AAC3D,UAAO,0BAA0B;IAChC;IACA;IACA;IACA;IACA,CAAC;;EAEH,oBAAoB,QAAQ,qBACzB,QAAQ,qBACR,OAAO,iBAAiB;AACxB,UAAO,mBAAmB;IACzB;IACA,SAAS;KACR,UAAU,QAAQ;KAClB,WAAW,QAAQ;KACnB,cAAc,QAAQ;KACtB;IACD;IACA,CAAC;;EAEL,MAAM,YAAY,OAAO;AACxB,OAAI,QAAQ,YACX,QAAO,QAAQ,YAAY,MAAM;GAElC,MAAM,UAAU,MAAM;AACtB,OAAI,CAAC,SAAS;AACb,WAAO,MAAM,4BAA4B;AACzC,WAAO;;GAER,MAAM,UAAU,UAAU,QAAQ;GAClC,MAAM,UAAU,MAAM,QAAQ,mBAAmB,QAAQ;AACzD,UAAO;IACN,MAAM;KACL,IAAI,QAAQ;KACZ,MAAM,QAAQ;KACd,OAAO,QAAQ;KACf,OAAO,QAAQ;KACf,eAAe,QAAQ;KACvB,GAAG;KACH;IACD,MAAM;IACN;;EAEF;EACA"}
1
+ {"version":3,"file":"twitch.mjs","names":[],"sources":["../../src/social-providers/twitch.ts"],"sourcesContent":["import { decodeJwt } from \"jose\";\nimport { logger } from \"../env\";\nimport type { OAuthProvider, ProviderOptions } from \"../oauth2\";\nimport {\n\tcreateAuthorizationURL,\n\trefreshAccessToken,\n\tvalidateAuthorizationCode,\n} from \"../oauth2\";\n\n/**\n * @see https://dev.twitch.tv/docs/authentication/getting-tokens-oidc/#requesting-claims\n */\nexport interface TwitchProfile {\n\t/**\n\t * The sub of the user\n\t */\n\tsub: string;\n\t/**\n\t * The preferred username of the user\n\t */\n\tpreferred_username: string;\n\t/**\n\t * The email of the user\n\t */\n\temail: string;\n\t/**\n\t * Indicate if this user has a verified email.\n\t */\n\temail_verified: boolean;\n\t/**\n\t * The picture of the user\n\t */\n\tpicture: string;\n}\n\nexport interface TwitchOptions extends ProviderOptions<TwitchProfile> {\n\tclientId: string;\n\tclaims?: string[] | undefined;\n}\nexport const twitch = (options: TwitchOptions) => {\n\tconst tokenEndpoint = \"https://id.twitch.tv/oauth2/token\";\n\treturn {\n\t\tid: \"twitch\",\n\t\tname: \"Twitch\",\n\t\tcreateAuthorizationURL({ state, scopes, redirectURI }) {\n\t\t\tconst _scopes = options.disableDefaultScope\n\t\t\t\t? []\n\t\t\t\t: [\"user:read:email\", \"openid\"];\n\t\t\tif (options.scope) _scopes.push(...options.scope);\n\t\t\tif (scopes) _scopes.push(...scopes);\n\t\t\treturn createAuthorizationURL({\n\t\t\t\tid: \"twitch\",\n\t\t\t\tredirectURI,\n\t\t\t\toptions,\n\t\t\t\tauthorizationEndpoint: \"https://id.twitch.tv/oauth2/authorize\",\n\t\t\t\tscopes: _scopes,\n\t\t\t\tstate,\n\t\t\t\tclaims: options.claims || [\n\t\t\t\t\t\"email\",\n\t\t\t\t\t\"email_verified\",\n\t\t\t\t\t\"preferred_username\",\n\t\t\t\t\t\"picture\",\n\t\t\t\t],\n\t\t\t});\n\t\t},\n\t\tvalidateAuthorizationCode: async ({ code, redirectURI }) => {\n\t\t\treturn validateAuthorizationCode({\n\t\t\t\tcode,\n\t\t\t\tredirectURI,\n\t\t\t\toptions,\n\t\t\t\ttokenEndpoint,\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\treturn refreshAccessToken({\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,\n\t\t\t\t\t});\n\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 idToken = token.idToken;\n\t\t\tif (!idToken) {\n\t\t\t\tlogger.error(\"No idToken found in token\");\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tconst profile = decodeJwt(idToken) as TwitchProfile;\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.sub,\n\t\t\t\t\tname: profile.preferred_username,\n\t\t\t\t\temail: profile.email,\n\t\t\t\t\timage: profile.picture,\n\t\t\t\t\temailVerified: profile.email_verified,\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<TwitchProfile>;\n};\n"],"mappings":";;;;;;;;AAuCA,MAAa,UAAU,YAA2B;CACjD,MAAM,gBAAgB;AACtB,QAAO;EACN,IAAI;EACJ,MAAM;EACN,uBAAuB,EAAE,OAAO,QAAQ,eAAe;GACtD,MAAM,UAAU,QAAQ,sBACrB,EAAE,GACF,CAAC,mBAAmB,SAAS;AAChC,OAAI,QAAQ,MAAO,SAAQ,KAAK,GAAG,QAAQ,MAAM;AACjD,OAAI,OAAQ,SAAQ,KAAK,GAAG,OAAO;AACnC,UAAO,uBAAuB;IAC7B,IAAI;IACJ;IACA;IACA,uBAAuB;IACvB,QAAQ;IACR;IACA,QAAQ,QAAQ,UAAU;KACzB;KACA;KACA;KACA;KACA;IACD,CAAC;;EAEH,2BAA2B,OAAO,EAAE,MAAM,kBAAkB;AAC3D,UAAO,0BAA0B;IAChC;IACA;IACA;IACA;IACA,CAAC;;EAEH,oBAAoB,QAAQ,qBACzB,QAAQ,qBACR,OAAO,iBAAiB;AACxB,UAAO,mBAAmB;IACzB;IACA,SAAS;KACR,UAAU,QAAQ;KAClB,WAAW,QAAQ;KACnB,cAAc,QAAQ;KACtB;IACD;IACA,CAAC;;EAEL,MAAM,YAAY,OAAO;AACxB,OAAI,QAAQ,YACX,QAAO,QAAQ,YAAY,MAAM;GAElC,MAAM,UAAU,MAAM;AACtB,OAAI,CAAC,SAAS;AACb,WAAO,MAAM,4BAA4B;AACzC,WAAO;;GAER,MAAM,UAAU,UAAU,QAAQ;GAClC,MAAM,UAAU,MAAM,QAAQ,mBAAmB,QAAQ;AACzD,UAAO;IACN,MAAM;KACL,IAAI,QAAQ;KACZ,MAAM,QAAQ;KACd,OAAO,QAAQ;KACf,OAAO,QAAQ;KACf,eAAe,QAAQ;KACvB,GAAG;KACH;IACD,MAAM;IACN;;EAEF;EACA"}
@@ -3,7 +3,6 @@ import { refreshAccessToken } from "../oauth2/refresh-access-token.mjs";
3
3
  import { validateAuthorizationCode } from "../oauth2/validate-authorization-code.mjs";
4
4
  import "../oauth2/index.mjs";
5
5
  import { betterFetch } from "@better-fetch/fetch";
6
-
7
6
  //#region src/social-providers/twitter.ts
8
7
  const twitter = (options) => {
9
8
  const tokenEndpoint = "https://api.x.com/2/oauth2/token";
@@ -83,7 +82,7 @@ const twitter = (options) => {
83
82
  options
84
83
  };
85
84
  };
86
-
87
85
  //#endregion
88
86
  export { twitter };
87
+
89
88
  //# sourceMappingURL=twitter.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"twitter.mjs","names":[],"sources":["../../src/social-providers/twitter.ts"],"sourcesContent":["import { betterFetch } from \"@better-fetch/fetch\";\nimport type { OAuthProvider, ProviderOptions } from \"../oauth2\";\nimport {\n\tcreateAuthorizationURL,\n\trefreshAccessToken,\n\tvalidateAuthorizationCode,\n} from \"../oauth2\";\n\nexport interface TwitterProfile {\n\tdata: {\n\t\t/**\n\t\t * Unique identifier of this user. This is returned as a string in order to avoid complications with languages and tools\n\t\t * that cannot handle large integers.\n\t\t */\n\t\tid: string;\n\t\t/** The friendly name of this user, as shown on their profile. */\n\t\tname: string;\n\t\t/** The email address of this user. */\n\t\temail?: string | undefined;\n\t\t/** The Twitter handle (screen name) of this user. */\n\t\tusername: string;\n\t\t/**\n\t\t * The location specified in the user's profile, if the user provided one.\n\t\t * As this is a freeform value, it may not indicate a valid location, but it may be fuzzily evaluated when performing searches with location queries.\n\t\t *\n\t\t * To return this field, add `user.fields=location` in the authorization request's query parameter.\n\t\t */\n\t\tlocation?: string | undefined;\n\t\t/**\n\t\t * This object and its children fields contain details about text that has a special meaning in the user's description.\n\t\t *\n\t\t *To return this field, add `user.fields=entities` in the authorization request's query parameter.\n\t\t */\n\t\tentities?:\n\t\t\t| {\n\t\t\t\t\t/** Contains details about the user's profile website. */\n\t\t\t\t\turl: {\n\t\t\t\t\t\t/** Contains details about the user's profile website. */\n\t\t\t\t\t\turls: Array<{\n\t\t\t\t\t\t\t/** The start position (zero-based) of the recognized user's profile website. All start indices are inclusive. */\n\t\t\t\t\t\t\tstart: number;\n\t\t\t\t\t\t\t/** The end position (zero-based) of the recognized user's profile website. This end index is exclusive. */\n\t\t\t\t\t\t\tend: number;\n\t\t\t\t\t\t\t/** The URL in the format entered by the user. */\n\t\t\t\t\t\t\turl: string;\n\t\t\t\t\t\t\t/** The fully resolved URL. */\n\t\t\t\t\t\t\texpanded_url: string;\n\t\t\t\t\t\t\t/** The URL as displayed in the user's profile. */\n\t\t\t\t\t\t\tdisplay_url: string;\n\t\t\t\t\t\t}>;\n\t\t\t\t\t};\n\t\t\t\t\t/** Contains details about URLs, Hashtags, Cashtags, or mentions located within a user's description. */\n\t\t\t\t\tdescription: {\n\t\t\t\t\t\thashtags: Array<{\n\t\t\t\t\t\t\tstart: number;\n\t\t\t\t\t\t\tend: number;\n\t\t\t\t\t\t\ttag: string;\n\t\t\t\t\t\t}>;\n\t\t\t\t\t};\n\t\t\t }\n\t\t\t| undefined;\n\t\t/**\n\t\t * Indicate if this user is a verified Twitter user.\n\t\t *\n\t\t * To return this field, add `user.fields=verified` in the authorization request's query parameter.\n\t\t */\n\t\tverified?: boolean | undefined;\n\t\t/**\n\t\t * The text of this user's profile description (also known as bio), if the user provided one.\n\t\t *\n\t\t * To return this field, add `user.fields=description` in the authorization request's query parameter.\n\t\t */\n\t\tdescription?: string | undefined;\n\t\t/**\n\t\t * The URL specified in the user's profile, if present.\n\t\t *\n\t\t * To return this field, add `user.fields=url` in the authorization request's query parameter.\n\t\t */\n\t\turl?: string | undefined;\n\t\t/** The URL to the profile image for this user, as shown on the user's profile. */\n\t\tprofile_image_url?: string | undefined;\n\t\tprotected?: boolean | undefined;\n\t\t/**\n\t\t * Unique identifier of this user's pinned Tweet.\n\t\t *\n\t\t * You can obtain the expanded object in `includes.tweets` by adding `expansions=pinned_tweet_id` in the authorization request's query parameter.\n\t\t */\n\t\tpinned_tweet_id?: string | undefined;\n\t\tcreated_at?: string | undefined;\n\t};\n\tincludes?:\n\t\t| {\n\t\t\t\ttweets?: Array<{\n\t\t\t\t\tid: string;\n\t\t\t\t\ttext: string;\n\t\t\t\t}>;\n\t\t }\n\t\t| undefined;\n\t[claims: string]: unknown;\n}\n\nexport interface TwitterOption extends ProviderOptions<TwitterProfile> {\n\tclientId: string;\n}\n\nexport const twitter = (options: TwitterOption) => {\n\tconst tokenEndpoint = \"https://api.x.com/2/oauth2/token\";\n\treturn {\n\t\tid: \"twitter\",\n\t\tname: \"Twitter\",\n\t\tcreateAuthorizationURL(data) {\n\t\t\tconst _scopes = options.disableDefaultScope\n\t\t\t\t? []\n\t\t\t\t: [\"users.read\", \"tweet.read\", \"offline.access\", \"users.email\"];\n\t\t\tif (options.scope) _scopes.push(...options.scope);\n\t\t\tif (data.scopes) _scopes.push(...data.scopes);\n\t\t\treturn createAuthorizationURL({\n\t\t\t\tid: \"twitter\",\n\t\t\t\toptions,\n\t\t\t\tauthorizationEndpoint: \"https://x.com/i/oauth2/authorize\",\n\t\t\t\tscopes: _scopes,\n\t\t\t\tstate: data.state,\n\t\t\t\tcodeVerifier: data.codeVerifier,\n\t\t\t\tredirectURI: data.redirectURI,\n\t\t\t});\n\t\t},\n\t\tvalidateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {\n\t\t\treturn validateAuthorizationCode({\n\t\t\t\tcode,\n\t\t\t\tcodeVerifier,\n\t\t\t\tauthentication: \"basic\",\n\t\t\t\tredirectURI,\n\t\t\t\toptions,\n\t\t\t\ttokenEndpoint,\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\treturn refreshAccessToken({\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\tauthentication: \"basic\",\n\t\t\t\t\t\ttokenEndpoint,\n\t\t\t\t\t});\n\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: profileError } =\n\t\t\t\tawait betterFetch<TwitterProfile>(\n\t\t\t\t\t\"https://api.x.com/2/users/me?user.fields=profile_image_url\",\n\t\t\t\t\t{\n\t\t\t\t\t\tmethod: \"GET\",\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\tAuthorization: `Bearer ${token.accessToken}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t);\n\n\t\t\tif (profileError) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst { data: emailData, error: emailError } = await betterFetch<{\n\t\t\t\tdata: { confirmed_email: string };\n\t\t\t}>(\"https://api.x.com/2/users/me?user.fields=confirmed_email\", {\n\t\t\t\tmethod: \"GET\",\n\t\t\t\theaders: {\n\t\t\t\t\tAuthorization: `Bearer ${token.accessToken}`,\n\t\t\t\t},\n\t\t\t});\n\t\t\tlet emailVerified = false;\n\t\t\tif (!emailError && emailData?.data?.confirmed_email) {\n\t\t\t\tprofile.data.email = emailData.data.confirmed_email;\n\t\t\t\temailVerified = true;\n\t\t\t}\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.data.id,\n\t\t\t\t\tname: profile.data.name,\n\t\t\t\t\temail: profile.data.email || profile.data.username || null,\n\t\t\t\t\timage: profile.data.profile_image_url,\n\t\t\t\t\temailVerified: emailVerified,\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<TwitterProfile>;\n};\n"],"mappings":";;;;;;;AAyGA,MAAa,WAAW,YAA2B;CAClD,MAAM,gBAAgB;AACtB,QAAO;EACN,IAAI;EACJ,MAAM;EACN,uBAAuB,MAAM;GAC5B,MAAM,UAAU,QAAQ,sBACrB,EAAE,GACF;IAAC;IAAc;IAAc;IAAkB;IAAc;AAChE,OAAI,QAAQ,MAAO,SAAQ,KAAK,GAAG,QAAQ,MAAM;AACjD,OAAI,KAAK,OAAQ,SAAQ,KAAK,GAAG,KAAK,OAAO;AAC7C,UAAO,uBAAuB;IAC7B,IAAI;IACJ;IACA,uBAAuB;IACvB,QAAQ;IACR,OAAO,KAAK;IACZ,cAAc,KAAK;IACnB,aAAa,KAAK;IAClB,CAAC;;EAEH,2BAA2B,OAAO,EAAE,MAAM,cAAc,kBAAkB;AACzE,UAAO,0BAA0B;IAChC;IACA;IACA,gBAAgB;IAChB;IACA;IACA;IACA,CAAC;;EAGH,oBAAoB,QAAQ,qBACzB,QAAQ,qBACR,OAAO,iBAAiB;AACxB,UAAO,mBAAmB;IACzB;IACA,SAAS;KACR,UAAU,QAAQ;KAClB,WAAW,QAAQ;KACnB,cAAc,QAAQ;KACtB;IACD,gBAAgB;IAChB;IACA,CAAC;;EAEL,MAAM,YAAY,OAAO;AACxB,OAAI,QAAQ,YACX,QAAO,QAAQ,YAAY,MAAM;GAElC,MAAM,EAAE,MAAM,SAAS,OAAO,iBAC7B,MAAM,YACL,8DACA;IACC,QAAQ;IACR,SAAS,EACR,eAAe,UAAU,MAAM,eAC/B;IACD,CACD;AAEF,OAAI,aACH,QAAO;GAGR,MAAM,EAAE,MAAM,WAAW,OAAO,eAAe,MAAM,YAElD,4DAA4D;IAC9D,QAAQ;IACR,SAAS,EACR,eAAe,UAAU,MAAM,eAC/B;IACD,CAAC;GACF,IAAI,gBAAgB;AACpB,OAAI,CAAC,cAAc,WAAW,MAAM,iBAAiB;AACpD,YAAQ,KAAK,QAAQ,UAAU,KAAK;AACpC,oBAAgB;;GAEjB,MAAM,UAAU,MAAM,QAAQ,mBAAmB,QAAQ;AACzD,UAAO;IACN,MAAM;KACL,IAAI,QAAQ,KAAK;KACjB,MAAM,QAAQ,KAAK;KACnB,OAAO,QAAQ,KAAK,SAAS,QAAQ,KAAK,YAAY;KACtD,OAAO,QAAQ,KAAK;KACL;KACf,GAAG;KACH;IACD,MAAM;IACN;;EAEF;EACA"}
1
+ {"version":3,"file":"twitter.mjs","names":[],"sources":["../../src/social-providers/twitter.ts"],"sourcesContent":["import { betterFetch } from \"@better-fetch/fetch\";\nimport type { OAuthProvider, ProviderOptions } from \"../oauth2\";\nimport {\n\tcreateAuthorizationURL,\n\trefreshAccessToken,\n\tvalidateAuthorizationCode,\n} from \"../oauth2\";\n\nexport interface TwitterProfile {\n\tdata: {\n\t\t/**\n\t\t * Unique identifier of this user. This is returned as a string in order to avoid complications with languages and tools\n\t\t * that cannot handle large integers.\n\t\t */\n\t\tid: string;\n\t\t/** The friendly name of this user, as shown on their profile. */\n\t\tname: string;\n\t\t/** The email address of this user. */\n\t\temail?: string | undefined;\n\t\t/** The Twitter handle (screen name) of this user. */\n\t\tusername: string;\n\t\t/**\n\t\t * The location specified in the user's profile, if the user provided one.\n\t\t * As this is a freeform value, it may not indicate a valid location, but it may be fuzzily evaluated when performing searches with location queries.\n\t\t *\n\t\t * To return this field, add `user.fields=location` in the authorization request's query parameter.\n\t\t */\n\t\tlocation?: string | undefined;\n\t\t/**\n\t\t * This object and its children fields contain details about text that has a special meaning in the user's description.\n\t\t *\n\t\t *To return this field, add `user.fields=entities` in the authorization request's query parameter.\n\t\t */\n\t\tentities?:\n\t\t\t| {\n\t\t\t\t\t/** Contains details about the user's profile website. */\n\t\t\t\t\turl: {\n\t\t\t\t\t\t/** Contains details about the user's profile website. */\n\t\t\t\t\t\turls: Array<{\n\t\t\t\t\t\t\t/** The start position (zero-based) of the recognized user's profile website. All start indices are inclusive. */\n\t\t\t\t\t\t\tstart: number;\n\t\t\t\t\t\t\t/** The end position (zero-based) of the recognized user's profile website. This end index is exclusive. */\n\t\t\t\t\t\t\tend: number;\n\t\t\t\t\t\t\t/** The URL in the format entered by the user. */\n\t\t\t\t\t\t\turl: string;\n\t\t\t\t\t\t\t/** The fully resolved URL. */\n\t\t\t\t\t\t\texpanded_url: string;\n\t\t\t\t\t\t\t/** The URL as displayed in the user's profile. */\n\t\t\t\t\t\t\tdisplay_url: string;\n\t\t\t\t\t\t}>;\n\t\t\t\t\t};\n\t\t\t\t\t/** Contains details about URLs, Hashtags, Cashtags, or mentions located within a user's description. */\n\t\t\t\t\tdescription: {\n\t\t\t\t\t\thashtags: Array<{\n\t\t\t\t\t\t\tstart: number;\n\t\t\t\t\t\t\tend: number;\n\t\t\t\t\t\t\ttag: string;\n\t\t\t\t\t\t}>;\n\t\t\t\t\t};\n\t\t\t }\n\t\t\t| undefined;\n\t\t/**\n\t\t * Indicate if this user is a verified Twitter user.\n\t\t *\n\t\t * To return this field, add `user.fields=verified` in the authorization request's query parameter.\n\t\t */\n\t\tverified?: boolean | undefined;\n\t\t/**\n\t\t * The text of this user's profile description (also known as bio), if the user provided one.\n\t\t *\n\t\t * To return this field, add `user.fields=description` in the authorization request's query parameter.\n\t\t */\n\t\tdescription?: string | undefined;\n\t\t/**\n\t\t * The URL specified in the user's profile, if present.\n\t\t *\n\t\t * To return this field, add `user.fields=url` in the authorization request's query parameter.\n\t\t */\n\t\turl?: string | undefined;\n\t\t/** The URL to the profile image for this user, as shown on the user's profile. */\n\t\tprofile_image_url?: string | undefined;\n\t\tprotected?: boolean | undefined;\n\t\t/**\n\t\t * Unique identifier of this user's pinned Tweet.\n\t\t *\n\t\t * You can obtain the expanded object in `includes.tweets` by adding `expansions=pinned_tweet_id` in the authorization request's query parameter.\n\t\t */\n\t\tpinned_tweet_id?: string | undefined;\n\t\tcreated_at?: string | undefined;\n\t};\n\tincludes?:\n\t\t| {\n\t\t\t\ttweets?: Array<{\n\t\t\t\t\tid: string;\n\t\t\t\t\ttext: string;\n\t\t\t\t}>;\n\t\t }\n\t\t| undefined;\n\t[claims: string]: unknown;\n}\n\nexport interface TwitterOption extends ProviderOptions<TwitterProfile> {\n\tclientId: string;\n}\n\nexport const twitter = (options: TwitterOption) => {\n\tconst tokenEndpoint = \"https://api.x.com/2/oauth2/token\";\n\treturn {\n\t\tid: \"twitter\",\n\t\tname: \"Twitter\",\n\t\tcreateAuthorizationURL(data) {\n\t\t\tconst _scopes = options.disableDefaultScope\n\t\t\t\t? []\n\t\t\t\t: [\"users.read\", \"tweet.read\", \"offline.access\", \"users.email\"];\n\t\t\tif (options.scope) _scopes.push(...options.scope);\n\t\t\tif (data.scopes) _scopes.push(...data.scopes);\n\t\t\treturn createAuthorizationURL({\n\t\t\t\tid: \"twitter\",\n\t\t\t\toptions,\n\t\t\t\tauthorizationEndpoint: \"https://x.com/i/oauth2/authorize\",\n\t\t\t\tscopes: _scopes,\n\t\t\t\tstate: data.state,\n\t\t\t\tcodeVerifier: data.codeVerifier,\n\t\t\t\tredirectURI: data.redirectURI,\n\t\t\t});\n\t\t},\n\t\tvalidateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {\n\t\t\treturn validateAuthorizationCode({\n\t\t\t\tcode,\n\t\t\t\tcodeVerifier,\n\t\t\t\tauthentication: \"basic\",\n\t\t\t\tredirectURI,\n\t\t\t\toptions,\n\t\t\t\ttokenEndpoint,\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\treturn refreshAccessToken({\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\tauthentication: \"basic\",\n\t\t\t\t\t\ttokenEndpoint,\n\t\t\t\t\t});\n\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: profileError } =\n\t\t\t\tawait betterFetch<TwitterProfile>(\n\t\t\t\t\t\"https://api.x.com/2/users/me?user.fields=profile_image_url\",\n\t\t\t\t\t{\n\t\t\t\t\t\tmethod: \"GET\",\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\tAuthorization: `Bearer ${token.accessToken}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t);\n\n\t\t\tif (profileError) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst { data: emailData, error: emailError } = await betterFetch<{\n\t\t\t\tdata: { confirmed_email: string };\n\t\t\t}>(\"https://api.x.com/2/users/me?user.fields=confirmed_email\", {\n\t\t\t\tmethod: \"GET\",\n\t\t\t\theaders: {\n\t\t\t\t\tAuthorization: `Bearer ${token.accessToken}`,\n\t\t\t\t},\n\t\t\t});\n\t\t\tlet emailVerified = false;\n\t\t\tif (!emailError && emailData?.data?.confirmed_email) {\n\t\t\t\tprofile.data.email = emailData.data.confirmed_email;\n\t\t\t\temailVerified = true;\n\t\t\t}\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.data.id,\n\t\t\t\t\tname: profile.data.name,\n\t\t\t\t\temail: profile.data.email || profile.data.username || null,\n\t\t\t\t\timage: profile.data.profile_image_url,\n\t\t\t\t\temailVerified: emailVerified,\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<TwitterProfile>;\n};\n"],"mappings":";;;;;;AAyGA,MAAa,WAAW,YAA2B;CAClD,MAAM,gBAAgB;AACtB,QAAO;EACN,IAAI;EACJ,MAAM;EACN,uBAAuB,MAAM;GAC5B,MAAM,UAAU,QAAQ,sBACrB,EAAE,GACF;IAAC;IAAc;IAAc;IAAkB;IAAc;AAChE,OAAI,QAAQ,MAAO,SAAQ,KAAK,GAAG,QAAQ,MAAM;AACjD,OAAI,KAAK,OAAQ,SAAQ,KAAK,GAAG,KAAK,OAAO;AAC7C,UAAO,uBAAuB;IAC7B,IAAI;IACJ;IACA,uBAAuB;IACvB,QAAQ;IACR,OAAO,KAAK;IACZ,cAAc,KAAK;IACnB,aAAa,KAAK;IAClB,CAAC;;EAEH,2BAA2B,OAAO,EAAE,MAAM,cAAc,kBAAkB;AACzE,UAAO,0BAA0B;IAChC;IACA;IACA,gBAAgB;IAChB;IACA;IACA;IACA,CAAC;;EAGH,oBAAoB,QAAQ,qBACzB,QAAQ,qBACR,OAAO,iBAAiB;AACxB,UAAO,mBAAmB;IACzB;IACA,SAAS;KACR,UAAU,QAAQ;KAClB,WAAW,QAAQ;KACnB,cAAc,QAAQ;KACtB;IACD,gBAAgB;IAChB;IACA,CAAC;;EAEL,MAAM,YAAY,OAAO;AACxB,OAAI,QAAQ,YACX,QAAO,QAAQ,YAAY,MAAM;GAElC,MAAM,EAAE,MAAM,SAAS,OAAO,iBAC7B,MAAM,YACL,8DACA;IACC,QAAQ;IACR,SAAS,EACR,eAAe,UAAU,MAAM,eAC/B;IACD,CACD;AAEF,OAAI,aACH,QAAO;GAGR,MAAM,EAAE,MAAM,WAAW,OAAO,eAAe,MAAM,YAElD,4DAA4D;IAC9D,QAAQ;IACR,SAAS,EACR,eAAe,UAAU,MAAM,eAC/B;IACD,CAAC;GACF,IAAI,gBAAgB;AACpB,OAAI,CAAC,cAAc,WAAW,MAAM,iBAAiB;AACpD,YAAQ,KAAK,QAAQ,UAAU,KAAK;AACpC,oBAAgB;;GAEjB,MAAM,UAAU,MAAM,QAAQ,mBAAmB,QAAQ;AACzD,UAAO;IACN,MAAM;KACL,IAAI,QAAQ,KAAK;KACjB,MAAM,QAAQ,KAAK;KACnB,OAAO,QAAQ,KAAK,SAAS,QAAQ,KAAK,YAAY;KACtD,OAAO,QAAQ,KAAK;KACL;KACf,GAAG;KACH;IACD,MAAM;IACN;;EAEF;EACA"}
@@ -3,7 +3,6 @@ import { createAuthorizationURL } from "../oauth2/create-authorization-url.mjs";
3
3
  import { validateAuthorizationCode } from "../oauth2/validate-authorization-code.mjs";
4
4
  import "../oauth2/index.mjs";
5
5
  import { betterFetch } from "@better-fetch/fetch";
6
-
7
6
  //#region src/social-providers/vercel.ts
8
7
  const vercel = (options) => {
9
8
  return {
@@ -56,7 +55,7 @@ const vercel = (options) => {
56
55
  options
57
56
  };
58
57
  };
59
-
60
58
  //#endregion
61
59
  export { vercel };
60
+
62
61
  //# sourceMappingURL=vercel.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"vercel.mjs","names":[],"sources":["../../src/social-providers/vercel.ts"],"sourcesContent":["import { betterFetch } from \"@better-fetch/fetch\";\nimport { BetterAuthError } from \"../error\";\nimport type { OAuthProvider, ProviderOptions } from \"../oauth2\";\nimport { createAuthorizationURL, validateAuthorizationCode } from \"../oauth2\";\n\nexport interface VercelProfile {\n\tsub: string;\n\tname?: string;\n\tpreferred_username?: string;\n\temail?: string;\n\temail_verified?: boolean;\n\tpicture?: string;\n}\n\nexport interface VercelOptions extends ProviderOptions<VercelProfile> {\n\tclientId: string;\n}\n\nexport const vercel = (options: VercelOptions) => {\n\treturn {\n\t\tid: \"vercel\",\n\t\tname: \"Vercel\",\n\t\tcreateAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {\n\t\t\tif (!codeVerifier) {\n\t\t\t\tthrow new BetterAuthError(\"codeVerifier is required for Vercel\");\n\t\t\t}\n\n\t\t\tlet _scopes: string[] | undefined = undefined;\n\t\t\tif (options.scope !== undefined || scopes !== undefined) {\n\t\t\t\t_scopes = [];\n\t\t\t\tif (options.scope) _scopes.push(...options.scope);\n\t\t\t\tif (scopes) _scopes.push(...scopes);\n\t\t\t}\n\n\t\t\treturn createAuthorizationURL({\n\t\t\t\tid: \"vercel\",\n\t\t\t\toptions,\n\t\t\t\tauthorizationEndpoint: \"https://vercel.com/oauth/authorize\",\n\t\t\t\tscopes: _scopes,\n\t\t\t\tstate,\n\t\t\t\tcodeVerifier,\n\t\t\t\tredirectURI,\n\t\t\t});\n\t\t},\n\t\tvalidateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {\n\t\t\treturn validateAuthorizationCode({\n\t\t\t\tcode,\n\t\t\t\tcodeVerifier,\n\t\t\t\tredirectURI,\n\t\t\t\toptions,\n\t\t\t\ttokenEndpoint: \"https://api.vercel.com/login/oauth/token\",\n\t\t\t});\n\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\n\t\t\tconst { data: profile, error } = await betterFetch<VercelProfile>(\n\t\t\t\t\"https://api.vercel.com/login/oauth/userinfo\",\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 || !profile) {\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.sub,\n\t\t\t\t\tname: profile.name ?? profile.preferred_username ?? \"\",\n\t\t\t\t\temail: profile.email,\n\t\t\t\t\timage: profile.picture,\n\t\t\t\t\temailVerified: profile.email_verified ?? 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<VercelProfile>;\n};\n"],"mappings":";;;;;;;AAkBA,MAAa,UAAU,YAA2B;AACjD,QAAO;EACN,IAAI;EACJ,MAAM;EACN,uBAAuB,EAAE,OAAO,QAAQ,cAAc,eAAe;AACpE,OAAI,CAAC,aACJ,OAAM,IAAI,gBAAgB,sCAAsC;GAGjE,IAAI,UAAgC;AACpC,OAAI,QAAQ,UAAU,UAAa,WAAW,QAAW;AACxD,cAAU,EAAE;AACZ,QAAI,QAAQ,MAAO,SAAQ,KAAK,GAAG,QAAQ,MAAM;AACjD,QAAI,OAAQ,SAAQ,KAAK,GAAG,OAAO;;AAGpC,UAAO,uBAAuB;IAC7B,IAAI;IACJ;IACA,uBAAuB;IACvB,QAAQ;IACR;IACA;IACA;IACA,CAAC;;EAEH,2BAA2B,OAAO,EAAE,MAAM,cAAc,kBAAkB;AACzE,UAAO,0BAA0B;IAChC;IACA;IACA;IACA;IACA,eAAe;IACf,CAAC;;EAEH,MAAM,YAAY,OAAO;AACxB,OAAI,QAAQ,YACX,QAAO,QAAQ,YAAY,MAAM;GAGlC,MAAM,EAAE,MAAM,SAAS,UAAU,MAAM,YACtC,+CACA,EACC,SAAS,EACR,eAAe,UAAU,MAAM,eAC/B,EACD,CACD;AAED,OAAI,SAAS,CAAC,QACb,QAAO;GAGR,MAAM,UAAU,MAAM,QAAQ,mBAAmB,QAAQ;AACzD,UAAO;IACN,MAAM;KACL,IAAI,QAAQ;KACZ,MAAM,QAAQ,QAAQ,QAAQ,sBAAsB;KACpD,OAAO,QAAQ;KACf,OAAO,QAAQ;KACf,eAAe,QAAQ,kBAAkB;KACzC,GAAG;KACH;IACD,MAAM;IACN;;EAEF;EACA"}
1
+ {"version":3,"file":"vercel.mjs","names":[],"sources":["../../src/social-providers/vercel.ts"],"sourcesContent":["import { betterFetch } from \"@better-fetch/fetch\";\nimport { BetterAuthError } from \"../error\";\nimport type { OAuthProvider, ProviderOptions } from \"../oauth2\";\nimport { createAuthorizationURL, validateAuthorizationCode } from \"../oauth2\";\n\nexport interface VercelProfile {\n\tsub: string;\n\tname?: string;\n\tpreferred_username?: string;\n\temail?: string;\n\temail_verified?: boolean;\n\tpicture?: string;\n}\n\nexport interface VercelOptions extends ProviderOptions<VercelProfile> {\n\tclientId: string;\n}\n\nexport const vercel = (options: VercelOptions) => {\n\treturn {\n\t\tid: \"vercel\",\n\t\tname: \"Vercel\",\n\t\tcreateAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {\n\t\t\tif (!codeVerifier) {\n\t\t\t\tthrow new BetterAuthError(\"codeVerifier is required for Vercel\");\n\t\t\t}\n\n\t\t\tlet _scopes: string[] | undefined = undefined;\n\t\t\tif (options.scope !== undefined || scopes !== undefined) {\n\t\t\t\t_scopes = [];\n\t\t\t\tif (options.scope) _scopes.push(...options.scope);\n\t\t\t\tif (scopes) _scopes.push(...scopes);\n\t\t\t}\n\n\t\t\treturn createAuthorizationURL({\n\t\t\t\tid: \"vercel\",\n\t\t\t\toptions,\n\t\t\t\tauthorizationEndpoint: \"https://vercel.com/oauth/authorize\",\n\t\t\t\tscopes: _scopes,\n\t\t\t\tstate,\n\t\t\t\tcodeVerifier,\n\t\t\t\tredirectURI,\n\t\t\t});\n\t\t},\n\t\tvalidateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {\n\t\t\treturn validateAuthorizationCode({\n\t\t\t\tcode,\n\t\t\t\tcodeVerifier,\n\t\t\t\tredirectURI,\n\t\t\t\toptions,\n\t\t\t\ttokenEndpoint: \"https://api.vercel.com/login/oauth/token\",\n\t\t\t});\n\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\n\t\t\tconst { data: profile, error } = await betterFetch<VercelProfile>(\n\t\t\t\t\"https://api.vercel.com/login/oauth/userinfo\",\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 || !profile) {\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.sub,\n\t\t\t\t\tname: profile.name ?? profile.preferred_username ?? \"\",\n\t\t\t\t\temail: profile.email,\n\t\t\t\t\timage: profile.picture,\n\t\t\t\t\temailVerified: profile.email_verified ?? 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<VercelProfile>;\n};\n"],"mappings":";;;;;;AAkBA,MAAa,UAAU,YAA2B;AACjD,QAAO;EACN,IAAI;EACJ,MAAM;EACN,uBAAuB,EAAE,OAAO,QAAQ,cAAc,eAAe;AACpE,OAAI,CAAC,aACJ,OAAM,IAAI,gBAAgB,sCAAsC;GAGjE,IAAI,UAAgC,KAAA;AACpC,OAAI,QAAQ,UAAU,KAAA,KAAa,WAAW,KAAA,GAAW;AACxD,cAAU,EAAE;AACZ,QAAI,QAAQ,MAAO,SAAQ,KAAK,GAAG,QAAQ,MAAM;AACjD,QAAI,OAAQ,SAAQ,KAAK,GAAG,OAAO;;AAGpC,UAAO,uBAAuB;IAC7B,IAAI;IACJ;IACA,uBAAuB;IACvB,QAAQ;IACR;IACA;IACA;IACA,CAAC;;EAEH,2BAA2B,OAAO,EAAE,MAAM,cAAc,kBAAkB;AACzE,UAAO,0BAA0B;IAChC;IACA;IACA;IACA;IACA,eAAe;IACf,CAAC;;EAEH,MAAM,YAAY,OAAO;AACxB,OAAI,QAAQ,YACX,QAAO,QAAQ,YAAY,MAAM;GAGlC,MAAM,EAAE,MAAM,SAAS,UAAU,MAAM,YACtC,+CACA,EACC,SAAS,EACR,eAAe,UAAU,MAAM,eAC/B,EACD,CACD;AAED,OAAI,SAAS,CAAC,QACb,QAAO;GAGR,MAAM,UAAU,MAAM,QAAQ,mBAAmB,QAAQ;AACzD,UAAO;IACN,MAAM;KACL,IAAI,QAAQ;KACZ,MAAM,QAAQ,QAAQ,QAAQ,sBAAsB;KACpD,OAAO,QAAQ;KACf,OAAO,QAAQ;KACf,eAAe,QAAQ,kBAAkB;KACzC,GAAG;KACH;IACD,MAAM;IACN;;EAEF;EACA"}
@@ -3,7 +3,6 @@ import { refreshAccessToken } from "../oauth2/refresh-access-token.mjs";
3
3
  import { validateAuthorizationCode } from "../oauth2/validate-authorization-code.mjs";
4
4
  import "../oauth2/index.mjs";
5
5
  import { betterFetch } from "@better-fetch/fetch";
6
-
7
6
  //#region src/social-providers/vk.ts
8
7
  const vk = (options) => {
9
8
  const tokenEndpoint = "https://id.vk.com/oauth2/auth";
@@ -79,7 +78,7 @@ const vk = (options) => {
79
78
  options
80
79
  };
81
80
  };
82
-
83
81
  //#endregion
84
82
  export { vk };
83
+
85
84
  //# sourceMappingURL=vk.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"vk.mjs","names":[],"sources":["../../src/social-providers/vk.ts"],"sourcesContent":["import { betterFetch } from \"@better-fetch/fetch\";\nimport type { OAuthProvider, ProviderOptions } from \"../oauth2\";\nimport {\n\tcreateAuthorizationURL,\n\trefreshAccessToken,\n\tvalidateAuthorizationCode,\n} from \"../oauth2\";\n\nexport interface VkProfile {\n\tuser: {\n\t\tuser_id: string;\n\t\tfirst_name: string;\n\t\tlast_name: string;\n\t\temail?: string | undefined;\n\t\tphone?: number | undefined;\n\t\tavatar?: string | undefined;\n\t\tsex?: number | undefined;\n\t\tverified?: boolean | undefined;\n\t\tbirthday: string;\n\t};\n}\n\nexport interface VkOption extends ProviderOptions {\n\tclientId: string;\n\tscheme?: (\"light\" | \"dark\") | undefined;\n}\n\nexport const vk = (options: VkOption) => {\n\tconst tokenEndpoint = \"https://id.vk.com/oauth2/auth\";\n\treturn {\n\t\tid: \"vk\",\n\t\tname: \"VK\",\n\t\tasync createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {\n\t\t\tconst _scopes = options.disableDefaultScope ? [] : [\"email\", \"phone\"];\n\t\t\tif (options.scope) _scopes.push(...options.scope);\n\t\t\tif (scopes) _scopes.push(...scopes);\n\t\t\tconst authorizationEndpoint = \"https://id.vk.com/authorize\";\n\n\t\t\treturn createAuthorizationURL({\n\t\t\t\tid: \"vk\",\n\t\t\t\toptions,\n\t\t\t\tauthorizationEndpoint,\n\t\t\t\tscopes: _scopes,\n\t\t\t\tstate,\n\t\t\t\tredirectURI,\n\t\t\t\tcodeVerifier,\n\t\t\t});\n\t\t},\n\t\tvalidateAuthorizationCode: async ({\n\t\t\tcode,\n\t\t\tcodeVerifier,\n\t\t\tredirectURI,\n\t\t\tdeviceId,\n\t\t}) => {\n\t\t\treturn validateAuthorizationCode({\n\t\t\t\tcode,\n\t\t\t\tcodeVerifier,\n\t\t\t\tredirectURI: options.redirectURI || redirectURI,\n\t\t\t\toptions,\n\t\t\t\tdeviceId,\n\t\t\t\ttokenEndpoint,\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\treturn refreshAccessToken({\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,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\tasync getUserInfo(data) {\n\t\t\tif (options.getUserInfo) {\n\t\t\t\treturn options.getUserInfo(data);\n\t\t\t}\n\t\t\tif (!data.accessToken) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tconst formBody = new URLSearchParams({\n\t\t\t\taccess_token: data.accessToken,\n\t\t\t\tclient_id: options.clientId,\n\t\t\t}).toString();\n\t\t\tconst { data: profile, error } = await betterFetch<VkProfile>(\n\t\t\t\t\"https://id.vk.com/oauth2/user_info\",\n\t\t\t\t{\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\t\t\t},\n\t\t\t\t\tbody: formBody,\n\t\t\t\t},\n\t\t\t);\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\t\t\tif (!profile.user.email && !userMap?.email) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tuser: {\n\t\t\t\t\tid: profile.user.user_id,\n\t\t\t\t\tfirst_name: profile.user.first_name,\n\t\t\t\t\tlast_name: profile.user.last_name,\n\t\t\t\t\temail: profile.user.email,\n\t\t\t\t\timage: profile.user.avatar,\n\t\t\t\t\temailVerified: false,\n\t\t\t\t\tbirthday: profile.user.birthday,\n\t\t\t\t\tsex: profile.user.sex,\n\t\t\t\t\tname: `${profile.user.first_name} ${profile.user.last_name}`,\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<VkProfile>;\n};\n"],"mappings":";;;;;;;AA2BA,MAAa,MAAM,YAAsB;CACxC,MAAM,gBAAgB;AACtB,QAAO;EACN,IAAI;EACJ,MAAM;EACN,MAAM,uBAAuB,EAAE,OAAO,QAAQ,cAAc,eAAe;GAC1E,MAAM,UAAU,QAAQ,sBAAsB,EAAE,GAAG,CAAC,SAAS,QAAQ;AACrE,OAAI,QAAQ,MAAO,SAAQ,KAAK,GAAG,QAAQ,MAAM;AACjD,OAAI,OAAQ,SAAQ,KAAK,GAAG,OAAO;AAGnC,UAAO,uBAAuB;IAC7B,IAAI;IACJ;IACA,uBAL6B;IAM7B,QAAQ;IACR;IACA;IACA;IACA,CAAC;;EAEH,2BAA2B,OAAO,EACjC,MACA,cACA,aACA,eACK;AACL,UAAO,0BAA0B;IAChC;IACA;IACA,aAAa,QAAQ,eAAe;IACpC;IACA;IACA;IACA,CAAC;;EAEH,oBAAoB,QAAQ,qBACzB,QAAQ,qBACR,OAAO,iBAAiB;AACxB,UAAO,mBAAmB;IACzB;IACA,SAAS;KACR,UAAU,QAAQ;KAClB,WAAW,QAAQ;KACnB,cAAc,QAAQ;KACtB;IACD;IACA,CAAC;;EAEL,MAAM,YAAY,MAAM;AACvB,OAAI,QAAQ,YACX,QAAO,QAAQ,YAAY,KAAK;AAEjC,OAAI,CAAC,KAAK,YACT,QAAO;GAER,MAAM,WAAW,IAAI,gBAAgB;IACpC,cAAc,KAAK;IACnB,WAAW,QAAQ;IACnB,CAAC,CAAC,UAAU;GACb,MAAM,EAAE,MAAM,SAAS,UAAU,MAAM,YACtC,sCACA;IACC,QAAQ;IACR,SAAS,EACR,gBAAgB,qCAChB;IACD,MAAM;IACN,CACD;AACD,OAAI,MACH,QAAO;GAGR,MAAM,UAAU,MAAM,QAAQ,mBAAmB,QAAQ;AACzD,OAAI,CAAC,QAAQ,KAAK,SAAS,CAAC,SAAS,MACpC,QAAO;AAGR,UAAO;IACN,MAAM;KACL,IAAI,QAAQ,KAAK;KACjB,YAAY,QAAQ,KAAK;KACzB,WAAW,QAAQ,KAAK;KACxB,OAAO,QAAQ,KAAK;KACpB,OAAO,QAAQ,KAAK;KACpB,eAAe;KACf,UAAU,QAAQ,KAAK;KACvB,KAAK,QAAQ,KAAK;KAClB,MAAM,GAAG,QAAQ,KAAK,WAAW,GAAG,QAAQ,KAAK;KACjD,GAAG;KACH;IACD,MAAM;IACN;;EAEF;EACA"}
1
+ {"version":3,"file":"vk.mjs","names":[],"sources":["../../src/social-providers/vk.ts"],"sourcesContent":["import { betterFetch } from \"@better-fetch/fetch\";\nimport type { OAuthProvider, ProviderOptions } from \"../oauth2\";\nimport {\n\tcreateAuthorizationURL,\n\trefreshAccessToken,\n\tvalidateAuthorizationCode,\n} from \"../oauth2\";\n\nexport interface VkProfile {\n\tuser: {\n\t\tuser_id: string;\n\t\tfirst_name: string;\n\t\tlast_name: string;\n\t\temail?: string | undefined;\n\t\tphone?: number | undefined;\n\t\tavatar?: string | undefined;\n\t\tsex?: number | undefined;\n\t\tverified?: boolean | undefined;\n\t\tbirthday: string;\n\t};\n}\n\nexport interface VkOption extends ProviderOptions {\n\tclientId: string;\n\tscheme?: (\"light\" | \"dark\") | undefined;\n}\n\nexport const vk = (options: VkOption) => {\n\tconst tokenEndpoint = \"https://id.vk.com/oauth2/auth\";\n\treturn {\n\t\tid: \"vk\",\n\t\tname: \"VK\",\n\t\tasync createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {\n\t\t\tconst _scopes = options.disableDefaultScope ? [] : [\"email\", \"phone\"];\n\t\t\tif (options.scope) _scopes.push(...options.scope);\n\t\t\tif (scopes) _scopes.push(...scopes);\n\t\t\tconst authorizationEndpoint = \"https://id.vk.com/authorize\";\n\n\t\t\treturn createAuthorizationURL({\n\t\t\t\tid: \"vk\",\n\t\t\t\toptions,\n\t\t\t\tauthorizationEndpoint,\n\t\t\t\tscopes: _scopes,\n\t\t\t\tstate,\n\t\t\t\tredirectURI,\n\t\t\t\tcodeVerifier,\n\t\t\t});\n\t\t},\n\t\tvalidateAuthorizationCode: async ({\n\t\t\tcode,\n\t\t\tcodeVerifier,\n\t\t\tredirectURI,\n\t\t\tdeviceId,\n\t\t}) => {\n\t\t\treturn validateAuthorizationCode({\n\t\t\t\tcode,\n\t\t\t\tcodeVerifier,\n\t\t\t\tredirectURI: options.redirectURI || redirectURI,\n\t\t\t\toptions,\n\t\t\t\tdeviceId,\n\t\t\t\ttokenEndpoint,\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\treturn refreshAccessToken({\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,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\tasync getUserInfo(data) {\n\t\t\tif (options.getUserInfo) {\n\t\t\t\treturn options.getUserInfo(data);\n\t\t\t}\n\t\t\tif (!data.accessToken) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tconst formBody = new URLSearchParams({\n\t\t\t\taccess_token: data.accessToken,\n\t\t\t\tclient_id: options.clientId,\n\t\t\t}).toString();\n\t\t\tconst { data: profile, error } = await betterFetch<VkProfile>(\n\t\t\t\t\"https://id.vk.com/oauth2/user_info\",\n\t\t\t\t{\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\t\t\t},\n\t\t\t\t\tbody: formBody,\n\t\t\t\t},\n\t\t\t);\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\t\t\tif (!profile.user.email && !userMap?.email) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tuser: {\n\t\t\t\t\tid: profile.user.user_id,\n\t\t\t\t\tfirst_name: profile.user.first_name,\n\t\t\t\t\tlast_name: profile.user.last_name,\n\t\t\t\t\temail: profile.user.email,\n\t\t\t\t\timage: profile.user.avatar,\n\t\t\t\t\temailVerified: false,\n\t\t\t\t\tbirthday: profile.user.birthday,\n\t\t\t\t\tsex: profile.user.sex,\n\t\t\t\t\tname: `${profile.user.first_name} ${profile.user.last_name}`,\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<VkProfile>;\n};\n"],"mappings":";;;;;;AA2BA,MAAa,MAAM,YAAsB;CACxC,MAAM,gBAAgB;AACtB,QAAO;EACN,IAAI;EACJ,MAAM;EACN,MAAM,uBAAuB,EAAE,OAAO,QAAQ,cAAc,eAAe;GAC1E,MAAM,UAAU,QAAQ,sBAAsB,EAAE,GAAG,CAAC,SAAS,QAAQ;AACrE,OAAI,QAAQ,MAAO,SAAQ,KAAK,GAAG,QAAQ,MAAM;AACjD,OAAI,OAAQ,SAAQ,KAAK,GAAG,OAAO;AAGnC,UAAO,uBAAuB;IAC7B,IAAI;IACJ;IACA,uBAL6B;IAM7B,QAAQ;IACR;IACA;IACA;IACA,CAAC;;EAEH,2BAA2B,OAAO,EACjC,MACA,cACA,aACA,eACK;AACL,UAAO,0BAA0B;IAChC;IACA;IACA,aAAa,QAAQ,eAAe;IACpC;IACA;IACA;IACA,CAAC;;EAEH,oBAAoB,QAAQ,qBACzB,QAAQ,qBACR,OAAO,iBAAiB;AACxB,UAAO,mBAAmB;IACzB;IACA,SAAS;KACR,UAAU,QAAQ;KAClB,WAAW,QAAQ;KACnB,cAAc,QAAQ;KACtB;IACD;IACA,CAAC;;EAEL,MAAM,YAAY,MAAM;AACvB,OAAI,QAAQ,YACX,QAAO,QAAQ,YAAY,KAAK;AAEjC,OAAI,CAAC,KAAK,YACT,QAAO;GAER,MAAM,WAAW,IAAI,gBAAgB;IACpC,cAAc,KAAK;IACnB,WAAW,QAAQ;IACnB,CAAC,CAAC,UAAU;GACb,MAAM,EAAE,MAAM,SAAS,UAAU,MAAM,YACtC,sCACA;IACC,QAAQ;IACR,SAAS,EACR,gBAAgB,qCAChB;IACD,MAAM;IACN,CACD;AACD,OAAI,MACH,QAAO;GAGR,MAAM,UAAU,MAAM,QAAQ,mBAAmB,QAAQ;AACzD,OAAI,CAAC,QAAQ,KAAK,SAAS,CAAC,SAAS,MACpC,QAAO;AAGR,UAAO;IACN,MAAM;KACL,IAAI,QAAQ,KAAK;KACjB,YAAY,QAAQ,KAAK;KACzB,WAAW,QAAQ,KAAK;KACxB,OAAO,QAAQ,KAAK;KACpB,OAAO,QAAQ,KAAK;KACpB,eAAe;KACf,UAAU,QAAQ,KAAK;KACvB,KAAK,QAAQ,KAAK;KAClB,MAAM,GAAG,QAAQ,KAAK,WAAW,GAAG,QAAQ,KAAK;KACjD,GAAG;KACH;IACD,MAAM;IACN;;EAEF;EACA"}
@@ -1,5 +1,4 @@
1
1
  import { betterFetch } from "@better-fetch/fetch";
2
-
3
2
  //#region src/social-providers/wechat.ts
4
3
  const wechat = (options) => {
5
4
  return {
@@ -78,7 +77,7 @@ const wechat = (options) => {
78
77
  options
79
78
  };
80
79
  };
81
-
82
80
  //#endregion
83
81
  export { wechat };
82
+
84
83
  //# sourceMappingURL=wechat.mjs.map
@@ -1 +1 @@
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
+ {"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"}
@@ -3,7 +3,6 @@ import { refreshAccessToken } from "../oauth2/refresh-access-token.mjs";
3
3
  import { validateAuthorizationCode } from "../oauth2/validate-authorization-code.mjs";
4
4
  import "../oauth2/index.mjs";
5
5
  import { betterFetch } from "@better-fetch/fetch";
6
-
7
6
  //#region src/social-providers/zoom.ts
8
7
  const zoom = (userOptions) => {
9
8
  const options = {
@@ -67,7 +66,7 @@ const zoom = (userOptions) => {
67
66
  }
68
67
  };
69
68
  };
70
-
71
69
  //#endregion
72
70
  export { zoom };
71
+
73
72
  //# sourceMappingURL=zoom.mjs.map
@@ -1 +1 @@
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
+ {"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"}
@@ -49,9 +49,7 @@ type InferPluginOptions<O extends BetterAuthOptions, ID extends BetterAuthPlugin
49
49
  */
50
50
  interface BetterAuthPluginRegistry<AuthOptions, Options> {}
51
51
  type BetterAuthPluginRegistryIdentifier = keyof BetterAuthPluginRegistry<unknown, unknown>;
52
- type GenericEndpointContext<Options extends BetterAuthOptions = BetterAuthOptions> = EndpointContext<string, any> & {
53
- context: AuthContext<Options>;
54
- };
52
+ type GenericEndpointContext<Options extends BetterAuthOptions = BetterAuthOptions> = EndpointContext<string, any, any, any, any, any, any, AuthContext<Options>>;
55
53
  interface InternalAdapter<_Options extends BetterAuthOptions = BetterAuthOptions> {
56
54
  createOAuthUser(user: Omit<User, "id" | "createdAt" | "updatedAt">, account: Omit<Account, "userId" | "id" | "createdAt" | "updatedAt"> & Partial<Account>): Promise<{
57
55
  user: User;
@@ -9,7 +9,10 @@ import { Migration } from "kysely";
9
9
 
10
10
  //#region src/types/plugin.d.ts
11
11
  type DeepPartial<T> = T extends Function ? T : T extends object ? { [K in keyof T]?: DeepPartial<T[K]> } : T;
12
- type HookEndpointContext = Partial<EndpointContext<string, any> & Omit<InputContext<string, any>, "method">> & {
12
+ type HookEndpointContext = Partial<EndpointContext<string, any, any, any, any, any, any, AuthContext & {
13
+ returned?: unknown | undefined;
14
+ responseHeaders?: Headers | undefined;
15
+ }> & Omit<InputContext<string, any, any, any, any, any>, "method">> & {
13
16
  path?: string;
14
17
  context: AuthContext & {
15
18
  returned?: unknown | undefined;
package/dist/utils/db.mjs CHANGED
@@ -11,7 +11,7 @@ function filterOutputFields(data, additionalFields) {
11
11
  [key]: value
12
12
  }), {});
13
13
  }
14
-
15
14
  //#endregion
16
15
  export { filterOutputFields };
16
+
17
17
  //# sourceMappingURL=db.mjs.map
@@ -12,7 +12,7 @@ function deprecate(fn, message, logger) {
12
12
  return fn.apply(this, args);
13
13
  };
14
14
  }
15
-
16
15
  //#endregion
17
16
  export { deprecate };
17
+
18
18
  //# sourceMappingURL=deprecate.mjs.map
@@ -6,7 +6,7 @@ function defineErrorCodes(codes) {
6
6
  toString: () => key
7
7
  }]));
8
8
  }
9
-
10
9
  //#endregion
11
10
  export { defineErrorCodes };
11
+
12
12
  //# sourceMappingURL=error-codes.mjs.map
@@ -2,7 +2,7 @@
2
2
  function isBrowserFetchRequest(headers) {
3
3
  return headers?.get("sec-fetch-mode") === "cors";
4
4
  }
5
-
6
5
  //#endregion
7
6
  export { isBrowserFetchRequest };
7
+
8
8
  //# sourceMappingURL=fetch-metadata.mjs.map
package/dist/utils/id.mjs CHANGED
@@ -1,10 +1,9 @@
1
1
  import { createRandomStringGenerator } from "@better-auth/utils/random";
2
-
3
2
  //#region src/utils/id.ts
4
3
  const generateId = (size) => {
5
4
  return createRandomStringGenerator("a-z", "A-Z", "0-9")(size || 32);
6
5
  };
7
-
8
6
  //#endregion
9
7
  export { generateId };
8
+
10
9
  //# sourceMappingURL=id.mjs.map
@@ -1 +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"}
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"}
package/dist/utils/ip.mjs CHANGED
@@ -1,5 +1,4 @@
1
1
  import * as z from "zod";
2
-
3
2
  //#region src/utils/ip.ts
4
3
  /**
5
4
  * Checks if an IP is valid IPv4 or IPv6
@@ -113,7 +112,7 @@ function normalizeIP(ip, options = {}) {
113
112
  function createRateLimitKey(ip, path) {
114
113
  return `${ip}|${path}`;
115
114
  }
116
-
117
115
  //#endregion
118
116
  export { createRateLimitKey, isValidIP, normalizeIP };
117
+
119
118
  //# sourceMappingURL=ip.mjs.map
@@ -1 +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":";;;;;;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
+ {"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,6 +1,5 @@
1
1
  import { logger } from "../env/logger.mjs";
2
2
  import "../env/index.mjs";
3
-
4
3
  //#region src/utils/json.ts
5
4
  const iso8601Regex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/;
6
5
  function reviveDate(value) {
@@ -39,7 +38,7 @@ function safeJSONParse(data) {
39
38
  return null;
40
39
  }
41
40
  }
42
-
43
41
  //#endregion
44
42
  export { safeJSONParse };
43
+
45
44
  //# sourceMappingURL=json.mjs.map
@@ -1 +1 @@
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
+ {"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,KAAA,EAC/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,KAAA,EAC7B,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"}
@@ -2,7 +2,7 @@
2
2
  function capitalizeFirstLetter(str) {
3
3
  return str.charAt(0).toUpperCase() + str.slice(1);
4
4
  }
5
-
6
5
  //#endregion
7
6
  export { capitalizeFirstLetter };
7
+
8
8
  //# sourceMappingURL=string.mjs.map
@@ -27,7 +27,7 @@ function normalizePathname(requestUrl, basePath) {
27
27
  if (pathname.startsWith(basePath + "/")) return pathname.slice(basePath.length).replace(/\/+$/, "") || "/";
28
28
  return pathname;
29
29
  }
30
-
31
30
  //#endregion
32
31
  export { normalizePathname };
32
+
33
33
  //# sourceMappingURL=url.mjs.map