@better-auth/core 1.5.5 → 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.
- package/dist/api/index.d.mts +41 -14
- package/dist/api/index.mjs +1 -2
- package/dist/api/index.mjs.map +1 -1
- package/dist/async_hooks/index.mjs +1 -1
- package/dist/async_hooks/pure.index.mjs +1 -1
- package/dist/async_hooks/pure.index.mjs.map +1 -1
- package/dist/context/endpoint-context.d.mts +1 -2
- package/dist/context/endpoint-context.mjs +1 -2
- package/dist/context/endpoint-context.mjs.map +1 -1
- package/dist/context/global.mjs +2 -2
- package/dist/context/global.mjs.map +1 -1
- package/dist/context/index.mjs +1 -2
- package/dist/context/request-state.mjs +1 -2
- package/dist/context/request-state.mjs.map +1 -1
- package/dist/context/transaction.mjs +1 -2
- package/dist/context/transaction.mjs.map +1 -1
- package/dist/db/adapter/factory.d.mts +0 -2
- package/dist/db/adapter/factory.mjs +54 -22
- package/dist/db/adapter/factory.mjs.map +1 -1
- package/dist/db/adapter/get-default-field-name.mjs +1 -2
- package/dist/db/adapter/get-default-field-name.mjs.map +1 -1
- package/dist/db/adapter/get-default-model-name.mjs +1 -2
- package/dist/db/adapter/get-default-model-name.mjs.map +1 -1
- package/dist/db/adapter/get-field-attributes.d.mts +0 -3
- package/dist/db/adapter/get-field-attributes.mjs +1 -2
- package/dist/db/adapter/get-field-attributes.mjs.map +1 -1
- package/dist/db/adapter/get-field-name.mjs +1 -2
- package/dist/db/adapter/get-field-name.mjs.map +1 -1
- package/dist/db/adapter/get-id-field.d.mts +0 -3
- package/dist/db/adapter/get-id-field.mjs +3 -4
- package/dist/db/adapter/get-id-field.mjs.map +1 -1
- package/dist/db/adapter/get-model-name.mjs +1 -2
- package/dist/db/adapter/get-model-name.mjs.map +1 -1
- package/dist/db/adapter/index.d.mts +0 -2
- package/dist/db/adapter/index.mjs +1 -2
- package/dist/db/adapter/index.mjs.map +1 -1
- package/dist/db/adapter/types.d.mts +0 -2
- package/dist/db/adapter/utils.mjs +1 -1
- package/dist/db/adapter/utils.mjs.map +1 -1
- package/dist/db/get-tables.d.mts +0 -2
- package/dist/db/get-tables.mjs +1 -1
- package/dist/db/index.mjs +1 -2
- package/dist/db/schema/account.d.mts +0 -1
- package/dist/db/schema/account.mjs +1 -2
- package/dist/db/schema/account.mjs.map +1 -1
- package/dist/db/schema/rate-limit.d.mts +0 -1
- package/dist/db/schema/rate-limit.mjs +1 -2
- package/dist/db/schema/rate-limit.mjs.map +1 -1
- package/dist/db/schema/session.d.mts +0 -1
- package/dist/db/schema/session.mjs +1 -2
- package/dist/db/schema/session.mjs.map +1 -1
- package/dist/db/schema/shared.mjs +1 -2
- package/dist/db/schema/shared.mjs.map +1 -1
- package/dist/db/schema/user.d.mts +0 -1
- package/dist/db/schema/user.mjs +1 -2
- package/dist/db/schema/user.mjs.map +1 -1
- package/dist/db/schema/verification.d.mts +0 -1
- package/dist/db/schema/verification.mjs +1 -2
- package/dist/db/schema/verification.mjs.map +1 -1
- package/dist/db/type.d.mts +0 -1
- package/dist/env/color-depth.mjs +1 -2
- package/dist/env/color-depth.mjs.map +1 -1
- package/dist/env/env-impl.mjs +1 -1
- package/dist/env/env-impl.mjs.map +1 -1
- package/dist/env/index.mjs +1 -2
- package/dist/env/logger.mjs +1 -2
- package/dist/env/logger.mjs.map +1 -1
- package/dist/error/codes.mjs +1 -2
- package/dist/error/codes.mjs.map +1 -1
- package/dist/error/index.mjs +1 -2
- package/dist/error/index.mjs.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/instrumentation/attributes.d.mts +12 -0
- package/dist/instrumentation/attributes.mjs +12 -0
- package/dist/instrumentation/attributes.mjs.map +1 -0
- package/dist/instrumentation/index.d.mts +3 -0
- package/dist/instrumentation/index.mjs +3 -0
- package/dist/instrumentation/tracer.d.mts +14 -0
- package/dist/instrumentation/tracer.mjs +36 -0
- package/dist/instrumentation/tracer.mjs.map +1 -0
- package/dist/oauth2/client-credentials-token.d.mts +0 -1
- package/dist/oauth2/client-credentials-token.mjs +1 -2
- package/dist/oauth2/client-credentials-token.mjs.map +1 -1
- package/dist/oauth2/create-authorization-url.d.mts +0 -3
- package/dist/oauth2/create-authorization-url.mjs +1 -2
- package/dist/oauth2/create-authorization-url.mjs.map +1 -1
- package/dist/oauth2/index.mjs +1 -2
- package/dist/oauth2/oauth-provider.d.mts +0 -2
- package/dist/oauth2/refresh-access-token.d.mts +0 -1
- package/dist/oauth2/refresh-access-token.mjs +1 -2
- package/dist/oauth2/refresh-access-token.mjs.map +1 -1
- package/dist/oauth2/utils.mjs +1 -2
- package/dist/oauth2/utils.mjs.map +1 -1
- package/dist/oauth2/validate-authorization-code.d.mts +0 -2
- package/dist/oauth2/validate-authorization-code.mjs +1 -2
- package/dist/oauth2/validate-authorization-code.mjs.map +1 -1
- package/dist/oauth2/verify.mjs +1 -2
- package/dist/oauth2/verify.mjs.map +1 -1
- package/dist/social-providers/apple.d.mts +0 -2
- package/dist/social-providers/apple.mjs +2 -3
- package/dist/social-providers/apple.mjs.map +1 -1
- package/dist/social-providers/atlassian.d.mts +0 -2
- package/dist/social-providers/atlassian.mjs +4 -4
- package/dist/social-providers/atlassian.mjs.map +1 -1
- package/dist/social-providers/cognito.d.mts +0 -2
- package/dist/social-providers/cognito.mjs +1 -2
- package/dist/social-providers/cognito.mjs.map +1 -1
- package/dist/social-providers/discord.d.mts +0 -2
- package/dist/social-providers/discord.mjs +4 -4
- package/dist/social-providers/discord.mjs.map +1 -1
- package/dist/social-providers/dropbox.d.mts +0 -2
- package/dist/social-providers/dropbox.mjs +1 -2
- package/dist/social-providers/dropbox.mjs.map +1 -1
- package/dist/social-providers/facebook.d.mts +0 -2
- package/dist/social-providers/facebook.mjs +1 -2
- package/dist/social-providers/facebook.mjs.map +1 -1
- package/dist/social-providers/figma.d.mts +0 -2
- package/dist/social-providers/figma.mjs +4 -4
- package/dist/social-providers/figma.mjs.map +1 -1
- package/dist/social-providers/github.d.mts +0 -2
- package/dist/social-providers/github.mjs +2 -3
- package/dist/social-providers/github.mjs.map +1 -1
- package/dist/social-providers/gitlab.d.mts +0 -2
- package/dist/social-providers/gitlab.mjs +1 -2
- package/dist/social-providers/gitlab.mjs.map +1 -1
- package/dist/social-providers/google.d.mts +0 -2
- package/dist/social-providers/google.mjs +1 -2
- package/dist/social-providers/google.mjs.map +1 -1
- package/dist/social-providers/huggingface.d.mts +0 -2
- package/dist/social-providers/huggingface.mjs +4 -4
- package/dist/social-providers/huggingface.mjs.map +1 -1
- package/dist/social-providers/index.d.mts +61 -3
- package/dist/social-providers/index.mjs +5 -4
- package/dist/social-providers/index.mjs.map +1 -1
- package/dist/social-providers/kakao.d.mts +0 -2
- package/dist/social-providers/kakao.mjs +4 -4
- package/dist/social-providers/kakao.mjs.map +1 -1
- package/dist/social-providers/kick.d.mts +0 -2
- package/dist/social-providers/kick.mjs +1 -2
- package/dist/social-providers/kick.mjs.map +1 -1
- package/dist/social-providers/line.d.mts +0 -2
- package/dist/social-providers/line.mjs +1 -2
- package/dist/social-providers/line.mjs.map +1 -1
- package/dist/social-providers/linear.d.mts +0 -2
- package/dist/social-providers/linear.mjs +1 -2
- package/dist/social-providers/linear.mjs.map +1 -1
- package/dist/social-providers/linkedin.d.mts +0 -2
- package/dist/social-providers/linkedin.mjs +1 -2
- package/dist/social-providers/linkedin.mjs.map +1 -1
- package/dist/social-providers/microsoft-entra-id.d.mts +0 -2
- package/dist/social-providers/microsoft-entra-id.mjs +1 -2
- package/dist/social-providers/microsoft-entra-id.mjs.map +1 -1
- package/dist/social-providers/naver.d.mts +0 -2
- package/dist/social-providers/naver.mjs +4 -4
- package/dist/social-providers/naver.mjs.map +1 -1
- package/dist/social-providers/notion.d.mts +0 -2
- package/dist/social-providers/notion.mjs +1 -2
- package/dist/social-providers/notion.mjs.map +1 -1
- package/dist/social-providers/paybin.d.mts +0 -2
- package/dist/social-providers/paybin.mjs +1 -2
- package/dist/social-providers/paybin.mjs.map +1 -1
- package/dist/social-providers/paypal.d.mts +0 -2
- package/dist/social-providers/paypal.mjs +1 -2
- package/dist/social-providers/paypal.mjs.map +1 -1
- package/dist/social-providers/polar.d.mts +0 -2
- package/dist/social-providers/polar.mjs +4 -4
- package/dist/social-providers/polar.mjs.map +1 -1
- package/dist/social-providers/railway.d.mts +0 -2
- package/dist/social-providers/railway.mjs +1 -2
- package/dist/social-providers/railway.mjs.map +1 -1
- package/dist/social-providers/reddit.d.mts +0 -2
- package/dist/social-providers/reddit.mjs +1 -2
- package/dist/social-providers/reddit.mjs.map +1 -1
- package/dist/social-providers/roblox.d.mts +0 -2
- package/dist/social-providers/roblox.mjs +4 -4
- package/dist/social-providers/roblox.mjs.map +1 -1
- package/dist/social-providers/salesforce.d.mts +0 -2
- package/dist/social-providers/salesforce.mjs +1 -2
- package/dist/social-providers/salesforce.mjs.map +1 -1
- package/dist/social-providers/slack.d.mts +0 -2
- package/dist/social-providers/slack.mjs +4 -4
- package/dist/social-providers/slack.mjs.map +1 -1
- package/dist/social-providers/spotify.d.mts +0 -2
- package/dist/social-providers/spotify.mjs +4 -4
- package/dist/social-providers/spotify.mjs.map +1 -1
- package/dist/social-providers/tiktok.d.mts +0 -2
- package/dist/social-providers/tiktok.mjs +4 -4
- package/dist/social-providers/tiktok.mjs.map +1 -1
- package/dist/social-providers/twitch.d.mts +0 -2
- package/dist/social-providers/twitch.mjs +4 -4
- package/dist/social-providers/twitch.mjs.map +1 -1
- package/dist/social-providers/twitter.d.mts +0 -2
- package/dist/social-providers/twitter.mjs +4 -4
- package/dist/social-providers/twitter.mjs.map +1 -1
- package/dist/social-providers/vercel.d.mts +0 -2
- package/dist/social-providers/vercel.mjs +1 -2
- package/dist/social-providers/vercel.mjs.map +1 -1
- package/dist/social-providers/vk.d.mts +0 -2
- package/dist/social-providers/vk.mjs +4 -4
- package/dist/social-providers/vk.mjs.map +1 -1
- package/dist/social-providers/wechat.d.mts +114 -0
- package/dist/social-providers/wechat.mjs +83 -0
- package/dist/social-providers/wechat.mjs.map +1 -0
- package/dist/social-providers/zoom.d.mts +0 -2
- package/dist/social-providers/zoom.mjs +1 -2
- package/dist/social-providers/zoom.mjs.map +1 -1
- package/dist/types/context.d.mts +1 -5
- package/dist/types/init-options.d.mts +0 -1
- package/dist/types/plugin.d.mts +4 -2
- package/dist/utils/db.d.mts +0 -2
- package/dist/utils/db.mjs +1 -1
- package/dist/utils/deprecate.mjs +1 -1
- package/dist/utils/error-codes.mjs +1 -1
- package/dist/utils/fetch-metadata.mjs +1 -1
- package/dist/utils/id.mjs +1 -2
- package/dist/utils/id.mjs.map +1 -1
- package/dist/utils/ip.mjs +1 -2
- package/dist/utils/ip.mjs.map +1 -1
- package/dist/utils/json.mjs +1 -2
- package/dist/utils/json.mjs.map +1 -1
- package/dist/utils/string.mjs +1 -1
- package/dist/utils/url.mjs +1 -1
- package/package.json +18 -5
- package/src/api/index.ts +151 -41
- package/src/context/endpoint-context.ts +2 -1
- package/src/db/adapter/factory.ts +119 -47
- package/src/db/adapter/get-id-field.test.ts +222 -0
- package/src/db/adapter/get-id-field.ts +15 -4
- package/src/instrumentation/attributes.ts +22 -0
- package/src/instrumentation/index.ts +2 -0
- package/src/instrumentation/instrumentation.test.ts +139 -0
- package/src/instrumentation/tracer.ts +62 -0
- package/src/social-providers/apple.ts +1 -1
- package/src/social-providers/atlassian.ts +3 -2
- package/src/social-providers/discord.ts +3 -2
- package/src/social-providers/figma.ts +3 -2
- package/src/social-providers/github.ts +1 -1
- package/src/social-providers/huggingface.ts +3 -2
- package/src/social-providers/index.ts +3 -0
- package/src/social-providers/kakao.ts +3 -2
- package/src/social-providers/naver.ts +3 -2
- package/src/social-providers/polar.ts +3 -2
- package/src/social-providers/roblox.ts +3 -2
- package/src/social-providers/slack.ts +3 -2
- package/src/social-providers/spotify.ts +3 -2
- package/src/social-providers/tiktok.ts +3 -2
- package/src/social-providers/twitch.ts +3 -2
- package/src/social-providers/twitter.ts +3 -2
- package/src/social-providers/vk.ts +3 -2
- package/src/social-providers/wechat.ts +213 -0
- package/src/types/context.ts +1 -3
- package/src/types/plugin.ts +14 -1
|
@@ -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":"
|
|
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"}
|
package/dist/types/context.d.mts
CHANGED
|
@@ -8,11 +8,9 @@ import { Awaitable, LiteralString } from "./helper.mjs";
|
|
|
8
8
|
import { BetterAuthPlugin } from "./plugin.mjs";
|
|
9
9
|
import { BetterAuthOptions, BetterAuthRateLimitOptions } from "./init-options.mjs";
|
|
10
10
|
import { Account } from "../db/schema/account.mjs";
|
|
11
|
-
import "../db/index.mjs";
|
|
12
11
|
import { BetterAuthCookie, BetterAuthCookies } from "./cookie.mjs";
|
|
13
12
|
import { SecretConfig } from "./secret.mjs";
|
|
14
13
|
import { OAuthProvider } from "../oauth2/oauth-provider.mjs";
|
|
15
|
-
import "../oauth2/index.mjs";
|
|
16
14
|
import { CookieOptions, EndpointContext } from "better-call";
|
|
17
15
|
|
|
18
16
|
//#region src/types/context.d.ts
|
|
@@ -51,9 +49,7 @@ type InferPluginOptions<O extends BetterAuthOptions, ID extends BetterAuthPlugin
|
|
|
51
49
|
*/
|
|
52
50
|
interface BetterAuthPluginRegistry<AuthOptions, Options> {}
|
|
53
51
|
type BetterAuthPluginRegistryIdentifier = keyof BetterAuthPluginRegistry<unknown, unknown>;
|
|
54
|
-
type GenericEndpointContext<Options extends BetterAuthOptions = BetterAuthOptions> = EndpointContext<string, any
|
|
55
|
-
context: AuthContext<Options>;
|
|
56
|
-
};
|
|
52
|
+
type GenericEndpointContext<Options extends BetterAuthOptions = BetterAuthOptions> = EndpointContext<string, any, any, any, any, any, any, AuthContext<Options>>;
|
|
57
53
|
interface InternalAdapter<_Options extends BetterAuthOptions = BetterAuthOptions> {
|
|
58
54
|
createOAuthUser(user: Omit<User, "id" | "createdAt" | "updatedAt">, account: Omit<Account, "userId" | "id" | "createdAt" | "updatedAt"> & Partial<Account>): Promise<{
|
|
59
55
|
user: User;
|
|
@@ -9,7 +9,6 @@ import { SocialProviderList, SocialProviders } from "../social-providers/index.m
|
|
|
9
9
|
import { Awaitable, LiteralString, LiteralUnion } from "./helper.mjs";
|
|
10
10
|
import { BetterAuthPlugin } from "./plugin.mjs";
|
|
11
11
|
import { Account, BaseAccount } from "../db/schema/account.mjs";
|
|
12
|
-
import "../db/index.mjs";
|
|
13
12
|
import { AuthContext, GenericEndpointContext } from "./context.mjs";
|
|
14
13
|
import { AuthMiddleware } from "../api/index.mjs";
|
|
15
14
|
import { CookieOptions } from "better-call";
|
package/dist/types/plugin.d.mts
CHANGED
|
@@ -2,7 +2,6 @@ import { BetterAuthPluginDBSchema } from "../db/plugin.mjs";
|
|
|
2
2
|
import { Awaitable, LiteralString } from "./helper.mjs";
|
|
3
3
|
import { RawError } from "../utils/error-codes.mjs";
|
|
4
4
|
import { BetterAuthOptions } from "./init-options.mjs";
|
|
5
|
-
import "../db/index.mjs";
|
|
6
5
|
import { AuthContext } from "./context.mjs";
|
|
7
6
|
import { AuthMiddleware } from "../api/index.mjs";
|
|
8
7
|
import { Endpoint, EndpointContext, InputContext, Middleware } from "better-call";
|
|
@@ -10,7 +9,10 @@ import { Migration } from "kysely";
|
|
|
10
9
|
|
|
11
10
|
//#region src/types/plugin.d.ts
|
|
12
11
|
type DeepPartial<T> = T extends Function ? T : T extends object ? { [K in keyof T]?: DeepPartial<T[K]> } : T;
|
|
13
|
-
type HookEndpointContext = Partial<EndpointContext<string, any
|
|
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">> & {
|
|
14
16
|
path?: string;
|
|
15
17
|
context: AuthContext & {
|
|
16
18
|
returned?: unknown | undefined;
|
package/dist/utils/db.d.mts
CHANGED
package/dist/utils/db.mjs
CHANGED
package/dist/utils/deprecate.mjs
CHANGED
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
|
package/dist/utils/id.mjs.map
CHANGED
|
@@ -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":"
|
|
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
|
package/dist/utils/ip.mjs.map
CHANGED
|
@@ -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":"
|
|
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"}
|
package/dist/utils/json.mjs
CHANGED
|
@@ -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
|
package/dist/utils/json.mjs.map
CHANGED
|
@@ -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":"
|
|
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"}
|
package/dist/utils/string.mjs
CHANGED
package/dist/utils/url.mjs
CHANGED
|
@@ -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
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@better-auth/core",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.7-beta.1",
|
|
4
4
|
"description": "The most comprehensive authentication framework for TypeScript.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -87,6 +87,11 @@
|
|
|
87
87
|
"dev-source": "./src/oauth2/index.ts",
|
|
88
88
|
"types": "./dist/oauth2/index.d.mts",
|
|
89
89
|
"default": "./dist/oauth2/index.mjs"
|
|
90
|
+
},
|
|
91
|
+
"./instrumentation": {
|
|
92
|
+
"dev-source": "./src/instrumentation/index.ts",
|
|
93
|
+
"types": "./dist/instrumentation/index.d.mts",
|
|
94
|
+
"default": "./dist/instrumentation/index.mjs"
|
|
90
95
|
}
|
|
91
96
|
},
|
|
92
97
|
"typesVersions": {
|
|
@@ -126,27 +131,35 @@
|
|
|
126
131
|
],
|
|
127
132
|
"oauth2": [
|
|
128
133
|
"dist/oauth2/index.d.mts"
|
|
134
|
+
],
|
|
135
|
+
"instrumentation": [
|
|
136
|
+
"dist/instrumentation/index.d.mts"
|
|
129
137
|
]
|
|
130
138
|
}
|
|
131
139
|
},
|
|
132
140
|
"dependencies": {
|
|
141
|
+
"@opentelemetry/semantic-conventions": "^1.39.0",
|
|
133
142
|
"@standard-schema/spec": "^1.1.0",
|
|
134
143
|
"zod": "^4.3.6"
|
|
135
144
|
},
|
|
136
145
|
"devDependencies": {
|
|
137
146
|
"@better-auth/utils": "0.3.1",
|
|
138
147
|
"@better-fetch/fetch": "1.1.21",
|
|
139
|
-
"
|
|
148
|
+
"@opentelemetry/api": "^1.9.0",
|
|
149
|
+
"@opentelemetry/sdk-trace-base": "^1.30.0",
|
|
150
|
+
"@opentelemetry/sdk-trace-node": "^1.30.0",
|
|
151
|
+
"better-call": "2.0.2",
|
|
140
152
|
"@cloudflare/workers-types": "^4.20250121.0",
|
|
141
153
|
"jose": "^6.1.3",
|
|
142
|
-
"kysely": "^0.28.
|
|
154
|
+
"kysely": "^0.28.14",
|
|
143
155
|
"nanostores": "^1.1.1",
|
|
144
|
-
"tsdown": "0.21.
|
|
156
|
+
"tsdown": "0.21.1"
|
|
145
157
|
},
|
|
146
158
|
"peerDependencies": {
|
|
147
159
|
"@better-auth/utils": "0.3.1",
|
|
148
160
|
"@better-fetch/fetch": "1.1.21",
|
|
149
|
-
"
|
|
161
|
+
"@opentelemetry/api": "^1.9.0",
|
|
162
|
+
"better-call": "2.0.2",
|
|
150
163
|
"@cloudflare/workers-types": ">=4",
|
|
151
164
|
"jose": "^6.1.0",
|
|
152
165
|
"kysely": "^0.28.5",
|
package/src/api/index.ts
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import type {
|
|
2
|
+
Endpoint,
|
|
2
3
|
EndpointContext,
|
|
3
|
-
|
|
4
|
-
|
|
4
|
+
EndpointMetadata,
|
|
5
|
+
EndpointRuntimeOptions,
|
|
6
|
+
HTTPMethod,
|
|
7
|
+
Middleware,
|
|
8
|
+
ResolveBodyInput,
|
|
9
|
+
ResolveErrorInput,
|
|
10
|
+
ResolveMetaInput,
|
|
11
|
+
ResolveQueryInput,
|
|
12
|
+
StandardSchemaV1,
|
|
5
13
|
} from "better-call";
|
|
6
14
|
import { createEndpoint, createMiddleware } from "better-call";
|
|
7
15
|
import { runWithEndpointContext } from "../context";
|
|
@@ -33,47 +41,145 @@ export const createAuthMiddleware = createMiddleware.create({
|
|
|
33
41
|
|
|
34
42
|
const use = [optionsMiddleware];
|
|
35
43
|
|
|
36
|
-
type
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
44
|
+
type BodyOption<M, B extends object | undefined = undefined> = M extends
|
|
45
|
+
| "GET"
|
|
46
|
+
| "HEAD"
|
|
47
|
+
| ("GET" | "HEAD")[]
|
|
48
|
+
? { body?: never }
|
|
49
|
+
: { body?: B };
|
|
50
|
+
|
|
51
|
+
type AuthEndpointOptions<
|
|
52
|
+
Method extends HTTPMethod | HTTPMethod[] | "*",
|
|
53
|
+
BodySchema extends object | undefined,
|
|
54
|
+
QuerySchema extends object | undefined,
|
|
55
|
+
Use extends Middleware[],
|
|
56
|
+
ReqHeaders extends boolean,
|
|
57
|
+
ReqRequest extends boolean,
|
|
58
|
+
Meta extends EndpointMetadata | undefined,
|
|
59
|
+
ErrorSchema extends StandardSchemaV1 | undefined = undefined,
|
|
60
|
+
> = { method: Method } & BodyOption<Method, BodySchema> & {
|
|
61
|
+
query?: QuerySchema;
|
|
62
|
+
use?: [...Use];
|
|
63
|
+
requireHeaders?: ReqHeaders;
|
|
64
|
+
requireRequest?: ReqRequest;
|
|
65
|
+
error?: ErrorSchema;
|
|
66
|
+
cloneRequest?: boolean;
|
|
67
|
+
disableBody?: boolean;
|
|
68
|
+
metadata?: Meta;
|
|
69
|
+
[key: string]: any;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Normalize readonly tuples produced by `const` type parameters
|
|
74
|
+
* into mutable arrays so downstream `M extends Array<any>` checks work.
|
|
75
|
+
*/
|
|
76
|
+
type NormalizeMethod<M> = M extends readonly (infer E)[] ? E[] : M;
|
|
41
77
|
|
|
78
|
+
// Path + options + handler overload
|
|
42
79
|
export function createAuthEndpoint<
|
|
43
80
|
Path extends string,
|
|
44
|
-
|
|
45
|
-
|
|
81
|
+
const Method extends HTTPMethod | HTTPMethod[] | "*",
|
|
82
|
+
BodySchema extends object | undefined = undefined,
|
|
83
|
+
QuerySchema extends object | undefined = undefined,
|
|
84
|
+
Use extends Middleware[] = [],
|
|
85
|
+
ReqHeaders extends boolean = false,
|
|
86
|
+
ReqRequest extends boolean = false,
|
|
87
|
+
R = unknown,
|
|
88
|
+
Meta extends EndpointMetadata | undefined = undefined,
|
|
89
|
+
ErrorSchema extends StandardSchemaV1 | undefined = undefined,
|
|
46
90
|
>(
|
|
47
91
|
path: Path,
|
|
48
|
-
options:
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
92
|
+
options: AuthEndpointOptions<
|
|
93
|
+
Method,
|
|
94
|
+
BodySchema,
|
|
95
|
+
QuerySchema,
|
|
96
|
+
Use,
|
|
97
|
+
ReqHeaders,
|
|
98
|
+
ReqRequest,
|
|
99
|
+
Meta,
|
|
100
|
+
ErrorSchema
|
|
101
|
+
>,
|
|
102
|
+
handler: (
|
|
103
|
+
ctx: EndpointContext<
|
|
104
|
+
Path,
|
|
105
|
+
Method,
|
|
106
|
+
BodySchema,
|
|
107
|
+
QuerySchema,
|
|
108
|
+
Use,
|
|
109
|
+
ReqHeaders,
|
|
110
|
+
ReqRequest,
|
|
111
|
+
AuthContext,
|
|
112
|
+
Meta
|
|
113
|
+
>,
|
|
114
|
+
) => Promise<R>,
|
|
115
|
+
): Endpoint<
|
|
116
|
+
Path,
|
|
117
|
+
NormalizeMethod<Method>,
|
|
118
|
+
ResolveBodyInput<BodySchema, Meta>,
|
|
119
|
+
ResolveQueryInput<QuerySchema, Meta>,
|
|
120
|
+
Use,
|
|
55
121
|
R,
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
): StrictEndpoint<Path, Options, R>;
|
|
122
|
+
ResolveMetaInput<Meta>,
|
|
123
|
+
ResolveErrorInput<ErrorSchema, Meta>
|
|
124
|
+
>;
|
|
60
125
|
|
|
126
|
+
// Options-only (virtual/path-less) overload
|
|
61
127
|
export function createAuthEndpoint<
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
128
|
+
const Method extends HTTPMethod | HTTPMethod[] | "*",
|
|
129
|
+
BodySchema extends object | undefined = undefined,
|
|
130
|
+
QuerySchema extends object | undefined = undefined,
|
|
131
|
+
Use extends Middleware[] = [],
|
|
132
|
+
ReqHeaders extends boolean = false,
|
|
133
|
+
ReqRequest extends boolean = false,
|
|
134
|
+
R = unknown,
|
|
135
|
+
Meta extends EndpointMetadata | undefined = undefined,
|
|
136
|
+
ErrorSchema extends StandardSchemaV1 | undefined = undefined,
|
|
65
137
|
>(
|
|
66
|
-
|
|
67
|
-
|
|
138
|
+
options: AuthEndpointOptions<
|
|
139
|
+
Method,
|
|
140
|
+
BodySchema,
|
|
141
|
+
QuerySchema,
|
|
142
|
+
Use,
|
|
143
|
+
ReqHeaders,
|
|
144
|
+
ReqRequest,
|
|
145
|
+
Meta,
|
|
146
|
+
ErrorSchema
|
|
147
|
+
>,
|
|
148
|
+
handler: (
|
|
149
|
+
ctx: EndpointContext<
|
|
150
|
+
string,
|
|
151
|
+
Method,
|
|
152
|
+
BodySchema,
|
|
153
|
+
QuerySchema,
|
|
154
|
+
Use,
|
|
155
|
+
ReqHeaders,
|
|
156
|
+
ReqRequest,
|
|
157
|
+
AuthContext,
|
|
158
|
+
Meta
|
|
159
|
+
>,
|
|
160
|
+
) => Promise<R>,
|
|
161
|
+
): Endpoint<
|
|
162
|
+
string,
|
|
163
|
+
NormalizeMethod<Method>,
|
|
164
|
+
ResolveBodyInput<BodySchema, Meta>,
|
|
165
|
+
ResolveQueryInput<QuerySchema, Meta>,
|
|
166
|
+
Use,
|
|
167
|
+
R,
|
|
168
|
+
ResolveMetaInput<Meta>,
|
|
169
|
+
ResolveErrorInput<ErrorSchema, Meta>
|
|
170
|
+
>;
|
|
171
|
+
|
|
172
|
+
// Implementation
|
|
173
|
+
export function createAuthEndpoint(
|
|
174
|
+
pathOrOptions: any,
|
|
175
|
+
handlerOrOptions: any,
|
|
68
176
|
handlerOrNever?: any,
|
|
69
177
|
) {
|
|
70
|
-
const path:
|
|
178
|
+
const path: string | undefined =
|
|
71
179
|
typeof pathOrOptions === "string" ? pathOrOptions : undefined;
|
|
72
|
-
const options:
|
|
73
|
-
typeof handlerOrOptions === "object"
|
|
74
|
-
|
|
75
|
-
: (pathOrOptions as Opts);
|
|
76
|
-
const handler: EndpointHandler<Path, Opts, R> =
|
|
180
|
+
const options: EndpointRuntimeOptions =
|
|
181
|
+
typeof handlerOrOptions === "object" ? handlerOrOptions : pathOrOptions;
|
|
182
|
+
const handler =
|
|
77
183
|
typeof handlerOrOptions === "function" ? handlerOrOptions : handlerOrNever;
|
|
78
184
|
|
|
79
185
|
if (path) {
|
|
@@ -82,9 +188,9 @@ export function createAuthEndpoint<
|
|
|
82
188
|
{
|
|
83
189
|
...options,
|
|
84
190
|
use: [...(options?.use || []), ...use],
|
|
85
|
-
},
|
|
191
|
+
} as any,
|
|
86
192
|
// todo: prettify the code, we want to call `runWithEndpointContext` to top level
|
|
87
|
-
async (ctx) => runWithEndpointContext(ctx
|
|
193
|
+
async (ctx: any) => runWithEndpointContext(ctx, () => handler(ctx)),
|
|
88
194
|
);
|
|
89
195
|
}
|
|
90
196
|
|
|
@@ -92,15 +198,19 @@ export function createAuthEndpoint<
|
|
|
92
198
|
{
|
|
93
199
|
...options,
|
|
94
200
|
use: [...(options?.use || []), ...use],
|
|
95
|
-
},
|
|
201
|
+
} as any,
|
|
96
202
|
// todo: prettify the code, we want to call `runWithEndpointContext` to top level
|
|
97
|
-
async (ctx) => runWithEndpointContext(ctx
|
|
203
|
+
async (ctx: any) => runWithEndpointContext(ctx, () => handler(ctx)),
|
|
98
204
|
);
|
|
99
205
|
}
|
|
100
206
|
|
|
101
|
-
export type AuthEndpoint<
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
207
|
+
export type AuthEndpoint = ReturnType<typeof createAuthEndpoint>;
|
|
208
|
+
/**
|
|
209
|
+
* The handler type for plugin hooks.
|
|
210
|
+
*
|
|
211
|
+
* Accepts both `Middleware` instances (from `createAuthMiddleware`)
|
|
212
|
+
* and plain async functions for better-call v1/v2 compatibility.
|
|
213
|
+
*/
|
|
214
|
+
export type AuthMiddleware = (
|
|
215
|
+
inputContext: Record<string, any>,
|
|
216
|
+
) => Promise<unknown>;
|
|
@@ -5,7 +5,8 @@ import type { AuthContext } from "../types";
|
|
|
5
5
|
import { __getBetterAuthGlobal } from "./global";
|
|
6
6
|
|
|
7
7
|
export type AuthEndpointContext = Partial<
|
|
8
|
-
InputContext<string, any
|
|
8
|
+
InputContext<string, any, any, any, any, any> &
|
|
9
|
+
EndpointContext<string, any, any, any, any, any, any, AuthContext>
|
|
9
10
|
> & {
|
|
10
11
|
context: AuthContext;
|
|
11
12
|
};
|