create-croissant 0.1.0 → 0.1.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/index.js +3 -3
- package/package.json +16 -8
- package/template/.prettierignore +10 -0
- package/template/.prettierrc +11 -0
- package/template/README.md +88 -0
- package/template/apps/web/.env.example +4 -0
- package/template/apps/web/components.json +23 -0
- package/template/apps/web/drizzle.config.ts +11 -0
- package/template/apps/web/eslint.config.js +10 -0
- package/template/apps/web/node_modules/@better-auth/core/LICENSE.md +20 -0
- package/template/apps/web/node_modules/@better-auth/core/README.md +17 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/api/index.d.mts +278 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/api/index.mjs +30 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/async_hooks/index.d.mts +6 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/async_hooks/index.mjs +20 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/async_hooks/pure.index.d.mts +6 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/async_hooks/pure.index.mjs +33 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/context/endpoint-context.d.mts +18 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/context/endpoint-context.mjs +29 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/context/global.d.mts +6 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/context/global.mjs +36 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/context/index.d.mts +5 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/context/index.mjs +5 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/context/request-state.d.mts +27 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/context/request-state.mjs +47 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/context/transaction.d.mts +24 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/context/transaction.mjs +93 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/db/adapter/factory.d.mts +17 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/db/adapter/factory.mjs +754 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/db/adapter/get-default-field-name.d.mts +18 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/db/adapter/get-default-field-name.mjs +36 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/db/adapter/get-default-model-name.d.mts +12 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/db/adapter/get-default-model-name.mjs +30 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/db/adapter/get-field-attributes.d.mts +26 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/db/adapter/get-field-attributes.mjs +37 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/db/adapter/get-field-name.d.mts +18 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/db/adapter/get-field-name.mjs +31 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/db/adapter/get-id-field.d.mts +36 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/db/adapter/get-id-field.mjs +64 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/db/adapter/get-model-name.d.mts +12 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/db/adapter/get-model-name.mjs +21 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/db/adapter/index.d.mts +525 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/db/adapter/index.mjs +24 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/db/adapter/types.d.mts +105 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/db/adapter/utils.d.mts +7 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/db/adapter/utils.mjs +37 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/db/get-tables.d.mts +6 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/db/get-tables.mjs +265 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/db/index.d.mts +10 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/db/index.mjs +8 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/db/plugin.d.mts +12 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/db/schema/account.d.mts +28 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/db/schema/account.mjs +17 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/db/schema/rate-limit.d.mts +18 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/db/schema/rate-limit.mjs +9 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/db/schema/session.d.mts +23 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/db/schema/session.mjs +12 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/db/schema/shared.d.mts +10 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/db/schema/shared.mjs +9 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/db/schema/user.d.mts +22 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/db/schema/user.mjs +11 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/db/schema/verification.d.mts +21 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/db/schema/verification.mjs +10 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/db/type.d.mts +167 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/env/color-depth.d.mts +4 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/env/color-depth.mjs +86 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/env/env-impl.d.mts +32 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/env/env-impl.mjs +81 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/env/index.d.mts +4 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/env/index.mjs +4 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/env/logger.d.mts +48 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/env/logger.mjs +79 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/error/codes.d.mts +68 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/error/codes.mjs +54 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/error/index.d.mts +19 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/error/index.mjs +27 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/index.d.mts +9 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/index.mjs +1 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/instrumentation/api.mjs +42 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/instrumentation/attributes.d.mts +11 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/instrumentation/attributes.mjs +10 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/instrumentation/index.d.mts +3 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/instrumentation/index.mjs +3 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/instrumentation/tracer.d.mts +13 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/instrumentation/tracer.mjs +53 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/oauth2/client-credentials-token.d.mts +56 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/oauth2/client-credentials-token.mjs +64 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/oauth2/create-authorization-url.d.mts +44 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/oauth2/create-authorization-url.mjs +41 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/oauth2/index.d.mts +8 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/oauth2/index.mjs +7 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/oauth2/oauth-provider.d.mts +192 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/oauth2/refresh-access-token.d.mts +54 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/oauth2/refresh-access-token.mjs +73 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/oauth2/utils.d.mts +7 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/oauth2/utils.mjs +25 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/oauth2/validate-authorization-code.d.mts +85 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/oauth2/validate-authorization-code.mjs +79 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/oauth2/verify.d.mts +42 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/oauth2/verify.mjs +92 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/apple.d.mts +117 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/apple.mjs +101 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/atlassian.d.mts +70 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/atlassian.mjs +80 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/cognito.d.mts +85 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/cognito.mjs +161 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/discord.d.mts +124 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/discord.mjs +62 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/dropbox.d.mts +69 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/dropbox.mjs +72 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/facebook.d.mts +79 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/facebook.mjs +117 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/figma.d.mts +61 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/figma.mjs +83 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/github.d.mts +102 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/github.mjs +92 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/gitlab.d.mts +123 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/gitlab.mjs +79 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/google.d.mts +97 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/google.mjs +108 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/huggingface.d.mts +83 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/huggingface.mjs +73 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/index.d.mts +1834 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/index.mjs +78 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/kakao.d.mts +161 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/kakao.mjs +70 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/kick.d.mts +73 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/kick.mjs +68 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/line.d.mts +105 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/line.mjs +110 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/linear.d.mts +68 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/linear.mjs +85 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/linkedin.d.mts +67 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/linkedin.mjs +73 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/microsoft-entra-id.d.mts +174 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/microsoft-entra-id.mjs +135 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/naver.d.mts +92 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/naver.mjs +65 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/notion.d.mts +64 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/notion.mjs +72 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/paybin.d.mts +71 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/paybin.mjs +81 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/paypal.d.mts +129 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/paypal.mjs +140 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/polar.d.mts +74 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/polar.mjs +71 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/railway.d.mts +65 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/railway.mjs +74 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/reddit.d.mts +62 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/reddit.mjs +80 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/roblox.d.mts +70 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/roblox.mjs +57 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/salesforce.d.mts +79 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/salesforce.mjs +87 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/slack.d.mts +83 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/slack.mjs +66 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/spotify.d.mts +63 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/spotify.mjs +69 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/tiktok.d.mts +168 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/tiktok.mjs +60 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/twitch.d.mts +79 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/twitch.mjs +75 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/twitter.d.mts +126 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/twitter.mjs +85 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/vercel.d.mts +62 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/vercel.mjs +58 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/vk.d.mts +70 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/vk.mjs +81 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/wechat.d.mts +113 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/wechat.mjs +81 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/zoom.d.mts +163 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/social-providers/zoom.mjs +69 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/types/context.d.mts +277 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/types/cookie.d.mts +15 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/types/helper.d.mts +10 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/types/index.d.mts +9 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/types/init-options.d.mts +1358 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/types/plugin-client.d.mts +113 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/types/plugin.d.mts +124 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/types/secret.d.mts +11 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/utils/async.d.mts +22 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/utils/async.mjs +32 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/utils/db.d.mts +9 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/utils/db.mjs +15 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/utils/deprecate.d.mts +9 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/utils/deprecate.mjs +16 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/utils/error-codes.d.mts +13 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/utils/error-codes.mjs +10 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/utils/fetch-metadata.d.mts +4 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/utils/fetch-metadata.mjs +6 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/utils/host.d.mts +147 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/utils/host.mjs +291 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/utils/id.d.mts +4 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/utils/id.mjs +7 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/utils/ip.d.mts +54 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/utils/ip.mjs +116 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/utils/json.d.mts +4 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/utils/json.mjs +41 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/utils/string.d.mts +4 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/utils/string.mjs +6 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/utils/url.d.mts +20 -0
- package/template/apps/web/node_modules/@better-auth/core/dist/utils/url.mjs +31 -0
- package/template/apps/web/node_modules/@better-auth/core/package.json +187 -0
- package/template/apps/web/node_modules/@better-auth/core/src/api/index.ts +106 -0
- package/template/apps/web/node_modules/@better-auth/core/src/async_hooks/index.ts +40 -0
- package/template/apps/web/node_modules/@better-auth/core/src/async_hooks/pure.index.ts +46 -0
- package/template/apps/web/node_modules/@better-auth/core/src/context/endpoint-context.ts +50 -0
- package/template/apps/web/node_modules/@better-auth/core/src/context/global.ts +57 -0
- package/template/apps/web/node_modules/@better-auth/core/src/context/index.ts +23 -0
- package/template/apps/web/node_modules/@better-auth/core/src/context/request-state.ts +91 -0
- package/template/apps/web/node_modules/@better-auth/core/src/context/transaction.ts +136 -0
- package/template/apps/web/node_modules/@better-auth/core/src/db/adapter/factory.ts +1440 -0
- package/template/apps/web/node_modules/@better-auth/core/src/db/adapter/get-default-field-name.ts +59 -0
- package/template/apps/web/node_modules/@better-auth/core/src/db/adapter/get-default-model-name.ts +51 -0
- package/template/apps/web/node_modules/@better-auth/core/src/db/adapter/get-field-attributes.ts +62 -0
- package/template/apps/web/node_modules/@better-auth/core/src/db/adapter/get-field-name.ts +43 -0
- package/template/apps/web/node_modules/@better-auth/core/src/db/adapter/get-id-field.ts +150 -0
- package/template/apps/web/node_modules/@better-auth/core/src/db/adapter/get-model-name.ts +36 -0
- package/template/apps/web/node_modules/@better-auth/core/src/db/adapter/index.ts +567 -0
- package/template/apps/web/node_modules/@better-auth/core/src/db/adapter/types.ts +132 -0
- package/template/apps/web/node_modules/@better-auth/core/src/db/adapter/utils.ts +61 -0
- package/template/apps/web/node_modules/@better-auth/core/src/db/get-tables.ts +296 -0
- package/template/apps/web/node_modules/@better-auth/core/src/db/index.ts +43 -0
- package/template/apps/web/node_modules/@better-auth/core/src/db/plugin.ts +11 -0
- package/template/apps/web/node_modules/@better-auth/core/src/db/schema/account.ts +47 -0
- package/template/apps/web/node_modules/@better-auth/core/src/db/schema/rate-limit.ts +36 -0
- package/template/apps/web/node_modules/@better-auth/core/src/db/schema/session.ts +29 -0
- package/template/apps/web/node_modules/@better-auth/core/src/db/schema/shared.ts +7 -0
- package/template/apps/web/node_modules/@better-auth/core/src/db/schema/user.ts +28 -0
- package/template/apps/web/node_modules/@better-auth/core/src/db/schema/verification.ts +28 -0
- package/template/apps/web/node_modules/@better-auth/core/src/db/type.ts +333 -0
- package/template/apps/web/node_modules/@better-auth/core/src/env/color-depth.ts +172 -0
- package/template/apps/web/node_modules/@better-auth/core/src/env/env-impl.ts +124 -0
- package/template/apps/web/node_modules/@better-auth/core/src/env/index.ts +23 -0
- package/template/apps/web/node_modules/@better-auth/core/src/env/logger.ts +145 -0
- package/template/apps/web/node_modules/@better-auth/core/src/error/codes.ts +71 -0
- package/template/apps/web/node_modules/@better-auth/core/src/error/index.ts +35 -0
- package/template/apps/web/node_modules/@better-auth/core/src/index.ts +1 -0
- package/template/apps/web/node_modules/@better-auth/core/src/instrumentation/api.ts +64 -0
- package/template/apps/web/node_modules/@better-auth/core/src/instrumentation/attributes.ts +22 -0
- package/template/apps/web/node_modules/@better-auth/core/src/instrumentation/index.ts +2 -0
- package/template/apps/web/node_modules/@better-auth/core/src/instrumentation/tracer.ts +95 -0
- package/template/apps/web/node_modules/@better-auth/core/src/oauth2/client-credentials-token.ts +126 -0
- package/template/apps/web/node_modules/@better-auth/core/src/oauth2/create-authorization-url.ts +89 -0
- package/template/apps/web/node_modules/@better-auth/core/src/oauth2/index.ts +29 -0
- package/template/apps/web/node_modules/@better-auth/core/src/oauth2/oauth-provider.ts +222 -0
- package/template/apps/web/node_modules/@better-auth/core/src/oauth2/refresh-access-token.ts +157 -0
- package/template/apps/web/node_modules/@better-auth/core/src/oauth2/utils.ts +38 -0
- package/template/apps/web/node_modules/@better-auth/core/src/oauth2/validate-authorization-code.ts +180 -0
- package/template/apps/web/node_modules/@better-auth/core/src/oauth2/verify.ts +221 -0
- package/template/apps/web/node_modules/@better-auth/core/src/social-providers/apple.ts +223 -0
- package/template/apps/web/node_modules/@better-auth/core/src/social-providers/atlassian.ts +133 -0
- package/template/apps/web/node_modules/@better-auth/core/src/social-providers/cognito.ts +280 -0
- package/template/apps/web/node_modules/@better-auth/core/src/social-providers/discord.ts +170 -0
- package/template/apps/web/node_modules/@better-auth/core/src/social-providers/dropbox.ts +112 -0
- package/template/apps/web/node_modules/@better-auth/core/src/social-providers/facebook.ts +206 -0
- package/template/apps/web/node_modules/@better-auth/core/src/social-providers/figma.ts +118 -0
- package/template/apps/web/node_modules/@better-auth/core/src/social-providers/github.ts +184 -0
- package/template/apps/web/node_modules/@better-auth/core/src/social-providers/gitlab.ts +155 -0
- package/template/apps/web/node_modules/@better-auth/core/src/social-providers/google.ts +203 -0
- package/template/apps/web/node_modules/@better-auth/core/src/social-providers/huggingface.ts +119 -0
- package/template/apps/web/node_modules/@better-auth/core/src/social-providers/index.ts +132 -0
- package/template/apps/web/node_modules/@better-auth/core/src/social-providers/kakao.ts +179 -0
- package/template/apps/web/node_modules/@better-auth/core/src/social-providers/kick.ts +109 -0
- package/template/apps/web/node_modules/@better-auth/core/src/social-providers/line.ts +169 -0
- package/template/apps/web/node_modules/@better-auth/core/src/social-providers/linear.ts +121 -0
- package/template/apps/web/node_modules/@better-auth/core/src/social-providers/linkedin.ts +110 -0
- package/template/apps/web/node_modules/@better-auth/core/src/social-providers/microsoft-entra-id.ts +342 -0
- package/template/apps/web/node_modules/@better-auth/core/src/social-providers/naver.ts +113 -0
- package/template/apps/web/node_modules/@better-auth/core/src/social-providers/notion.ts +108 -0
- package/template/apps/web/node_modules/@better-auth/core/src/social-providers/paybin.ts +118 -0
- package/template/apps/web/node_modules/@better-auth/core/src/social-providers/paypal.ts +263 -0
- package/template/apps/web/node_modules/@better-auth/core/src/social-providers/polar.ts +111 -0
- package/template/apps/web/node_modules/@better-auth/core/src/social-providers/railway.ts +100 -0
- package/template/apps/web/node_modules/@better-auth/core/src/social-providers/reddit.ts +122 -0
- package/template/apps/web/node_modules/@better-auth/core/src/social-providers/roblox.ts +112 -0
- package/template/apps/web/node_modules/@better-auth/core/src/social-providers/salesforce.ts +159 -0
- package/template/apps/web/node_modules/@better-auth/core/src/social-providers/slack.ts +112 -0
- package/template/apps/web/node_modules/@better-auth/core/src/social-providers/spotify.ts +94 -0
- package/template/apps/web/node_modules/@better-auth/core/src/social-providers/tiktok.ts +211 -0
- package/template/apps/web/node_modules/@better-auth/core/src/social-providers/twitch.ts +112 -0
- package/template/apps/web/node_modules/@better-auth/core/src/social-providers/twitter.ts +199 -0
- package/template/apps/web/node_modules/@better-auth/core/src/social-providers/vercel.ts +87 -0
- package/template/apps/web/node_modules/@better-auth/core/src/social-providers/vk.ts +125 -0
- package/template/apps/web/node_modules/@better-auth/core/src/social-providers/wechat.ts +213 -0
- package/template/apps/web/node_modules/@better-auth/core/src/social-providers/zoom.ts +230 -0
- package/template/apps/web/node_modules/@better-auth/core/src/types/context.ts +415 -0
- package/template/apps/web/node_modules/@better-auth/core/src/types/cookie.ts +10 -0
- package/template/apps/web/node_modules/@better-auth/core/src/types/helper.ts +27 -0
- package/template/apps/web/node_modules/@better-auth/core/src/types/index.ts +40 -0
- package/template/apps/web/node_modules/@better-auth/core/src/types/init-options.ts +1610 -0
- package/template/apps/web/node_modules/@better-auth/core/src/types/plugin-client.ts +129 -0
- package/template/apps/web/node_modules/@better-auth/core/src/types/plugin.ts +163 -0
- package/template/apps/web/node_modules/@better-auth/core/src/types/secret.ts +8 -0
- package/template/apps/web/node_modules/@better-auth/core/src/utils/async.ts +53 -0
- package/template/apps/web/node_modules/@better-auth/core/src/utils/db.ts +20 -0
- package/template/apps/web/node_modules/@better-auth/core/src/utils/deprecate.ts +21 -0
- package/template/apps/web/node_modules/@better-auth/core/src/utils/error-codes.ts +68 -0
- package/template/apps/web/node_modules/@better-auth/core/src/utils/fetch-metadata.ts +3 -0
- package/template/apps/web/node_modules/@better-auth/core/src/utils/host.ts +401 -0
- package/template/apps/web/node_modules/@better-auth/core/src/utils/id.ts +5 -0
- package/template/apps/web/node_modules/@better-auth/core/src/utils/ip.ts +211 -0
- package/template/apps/web/node_modules/@better-auth/core/src/utils/json.ts +56 -0
- package/template/apps/web/node_modules/@better-auth/core/src/utils/string.ts +3 -0
- package/template/apps/web/node_modules/@better-auth/core/src/utils/url.ts +43 -0
- package/template/apps/web/node_modules/@better-auth/drizzle-adapter/LICENSE.md +20 -0
- package/template/apps/web/node_modules/@better-auth/drizzle-adapter/README.md +17 -0
- package/template/apps/web/node_modules/@better-auth/drizzle-adapter/dist/index.d.mts +47 -0
- package/template/apps/web/node_modules/@better-auth/drizzle-adapter/dist/index.mjs +458 -0
- package/template/apps/web/node_modules/@better-auth/drizzle-adapter/package.json +62 -0
- package/template/apps/web/node_modules/@better-auth/utils/README.md +384 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/base32.cjs +104 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/base32.d.cts +44 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/base32.d.mts +44 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/base32.d.ts +44 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/base32.mjs +101 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/base64.cjs +80 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/base64.d.cts +16 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/base64.d.mts +16 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/base64.d.ts +16 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/base64.mjs +77 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/binary.cjs +16 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/binary.d.cts +8 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/binary.d.mts +8 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/binary.d.ts +8 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/binary.mjs +14 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/ecdsa.cjs +90 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/ecdsa.d.cts +19 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/ecdsa.d.mts +19 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/ecdsa.d.ts +19 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/ecdsa.mjs +88 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/hash.cjs +31 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/hash.d.cts +7 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/hash.d.mts +7 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/hash.d.ts +7 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/hash.mjs +29 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/hex.cjs +40 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/hex.d.cts +8 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/hex.d.mts +8 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/hex.d.ts +8 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/hex.mjs +38 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/hmac.cjs +58 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/hmac.d.cts +9 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/hmac.d.mts +9 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/hmac.d.ts +9 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/hmac.mjs +56 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/index.cjs +10 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/index.d.cts +3 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/index.d.mts +3 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/index.d.ts +3 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/index.mjs +8 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/otp.cjs +90 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/otp.d.cts +13 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/otp.d.mts +13 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/otp.d.ts +13 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/otp.mjs +88 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/password.cjs +36 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/password.d.cts +4 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/password.d.mts +4 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/password.d.ts +4 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/password.mjs +33 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/password.node.cjs +47 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/password.node.d.cts +4 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/password.node.d.mts +4 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/password.node.d.ts +4 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/password.node.mjs +44 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/random.cjs +55 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/random.d.cts +4 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/random.d.mts +4 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/random.d.ts +4 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/random.mjs +53 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/rsa.cjs +76 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/rsa.d.cts +16 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/rsa.d.mts +16 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/rsa.d.ts +16 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/rsa.mjs +74 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/shared/utils.ecd028f7.d.cts +22 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/shared/utils.ecd028f7.d.mts +22 -0
- package/template/apps/web/node_modules/@better-auth/utils/dist/shared/utils.ecd028f7.d.ts +22 -0
- package/template/apps/web/node_modules/@better-auth/utils/package.json +95 -0
- package/template/apps/web/node_modules/better-call/LICENSE +21 -0
- package/template/apps/web/node_modules/better-call/dist/_virtual/_rolldown/runtime.cjs +29 -0
- package/template/apps/web/node_modules/better-call/dist/adapters/node/request.cjs +181 -0
- package/template/apps/web/node_modules/better-call/dist/adapters/node/request.cjs.map +1 -0
- package/template/apps/web/node_modules/better-call/dist/adapters/node/request.d.cts +16 -0
- package/template/apps/web/node_modules/better-call/dist/adapters/node/request.d.mts +16 -0
- package/template/apps/web/node_modules/better-call/dist/adapters/node/request.mjs +178 -0
- package/template/apps/web/node_modules/better-call/dist/adapters/node/request.mjs.map +1 -0
- package/template/apps/web/node_modules/better-call/dist/client.cjs +23 -0
- package/template/apps/web/node_modules/better-call/dist/client.cjs.map +1 -0
- package/template/apps/web/node_modules/better-call/dist/client.d.cts +53 -0
- package/template/apps/web/node_modules/better-call/dist/client.d.mts +53 -0
- package/template/apps/web/node_modules/better-call/dist/client.mjs +14 -0
- package/template/apps/web/node_modules/better-call/dist/client.mjs.map +1 -0
- package/template/apps/web/node_modules/better-call/dist/context.cjs +103 -0
- package/template/apps/web/node_modules/better-call/dist/context.cjs.map +1 -0
- package/template/apps/web/node_modules/better-call/dist/context.d.cts +341 -0
- package/template/apps/web/node_modules/better-call/dist/context.d.mts +341 -0
- package/template/apps/web/node_modules/better-call/dist/context.mjs +103 -0
- package/template/apps/web/node_modules/better-call/dist/context.mjs.map +1 -0
- package/template/apps/web/node_modules/better-call/dist/cookies.cjs +87 -0
- package/template/apps/web/node_modules/better-call/dist/cookies.cjs.map +1 -0
- package/template/apps/web/node_modules/better-call/dist/cookies.d.cts +103 -0
- package/template/apps/web/node_modules/better-call/dist/cookies.d.mts +103 -0
- package/template/apps/web/node_modules/better-call/dist/cookies.mjs +84 -0
- package/template/apps/web/node_modules/better-call/dist/cookies.mjs.map +1 -0
- package/template/apps/web/node_modules/better-call/dist/crypto.cjs +39 -0
- package/template/apps/web/node_modules/better-call/dist/crypto.cjs.map +1 -0
- package/template/apps/web/node_modules/better-call/dist/crypto.mjs +36 -0
- package/template/apps/web/node_modules/better-call/dist/crypto.mjs.map +1 -0
- package/template/apps/web/node_modules/better-call/dist/endpoint.cjs +70 -0
- package/template/apps/web/node_modules/better-call/dist/endpoint.cjs.map +1 -0
- package/template/apps/web/node_modules/better-call/dist/endpoint.d.cts +475 -0
- package/template/apps/web/node_modules/better-call/dist/endpoint.d.mts +475 -0
- package/template/apps/web/node_modules/better-call/dist/endpoint.mjs +70 -0
- package/template/apps/web/node_modules/better-call/dist/endpoint.mjs.map +1 -0
- package/template/apps/web/node_modules/better-call/dist/error.cjs +141 -0
- package/template/apps/web/node_modules/better-call/dist/error.cjs.map +1 -0
- package/template/apps/web/node_modules/better-call/dist/error.d.cts +103 -0
- package/template/apps/web/node_modules/better-call/dist/error.d.mts +103 -0
- package/template/apps/web/node_modules/better-call/dist/error.mjs +133 -0
- package/template/apps/web/node_modules/better-call/dist/error.mjs.map +1 -0
- package/template/apps/web/node_modules/better-call/dist/helper.d.cts +12 -0
- package/template/apps/web/node_modules/better-call/dist/helper.d.mts +12 -0
- package/template/apps/web/node_modules/better-call/dist/index.cjs +28 -0
- package/template/apps/web/node_modules/better-call/dist/index.d.cts +11 -0
- package/template/apps/web/node_modules/better-call/dist/index.d.mts +11 -0
- package/template/apps/web/node_modules/better-call/dist/index.mjs +10 -0
- package/template/apps/web/node_modules/better-call/dist/middleware.cjs +52 -0
- package/template/apps/web/node_modules/better-call/dist/middleware.cjs.map +1 -0
- package/template/apps/web/node_modules/better-call/dist/middleware.d.cts +123 -0
- package/template/apps/web/node_modules/better-call/dist/middleware.d.mts +123 -0
- package/template/apps/web/node_modules/better-call/dist/middleware.mjs +52 -0
- package/template/apps/web/node_modules/better-call/dist/middleware.mjs.map +1 -0
- package/template/apps/web/node_modules/better-call/dist/node.cjs +18 -0
- package/template/apps/web/node_modules/better-call/dist/node.cjs.map +1 -0
- package/template/apps/web/node_modules/better-call/dist/node.d.cts +9 -0
- package/template/apps/web/node_modules/better-call/dist/node.d.mts +9 -0
- package/template/apps/web/node_modules/better-call/dist/node.mjs +15 -0
- package/template/apps/web/node_modules/better-call/dist/node.mjs.map +1 -0
- package/template/apps/web/node_modules/better-call/dist/openapi.cjs +191 -0
- package/template/apps/web/node_modules/better-call/dist/openapi.cjs.map +1 -0
- package/template/apps/web/node_modules/better-call/dist/openapi.d.cts +113 -0
- package/template/apps/web/node_modules/better-call/dist/openapi.d.mts +113 -0
- package/template/apps/web/node_modules/better-call/dist/openapi.mjs +189 -0
- package/template/apps/web/node_modules/better-call/dist/openapi.mjs.map +1 -0
- package/template/apps/web/node_modules/better-call/dist/router.cjs +118 -0
- package/template/apps/web/node_modules/better-call/dist/router.cjs.map +1 -0
- package/template/apps/web/node_modules/better-call/dist/router.d.cts +103 -0
- package/template/apps/web/node_modules/better-call/dist/router.d.mts +103 -0
- package/template/apps/web/node_modules/better-call/dist/router.mjs +117 -0
- package/template/apps/web/node_modules/better-call/dist/router.mjs.map +1 -0
- package/template/apps/web/node_modules/better-call/dist/standard-schema.d.cts +59 -0
- package/template/apps/web/node_modules/better-call/dist/standard-schema.d.mts +59 -0
- package/template/apps/web/node_modules/better-call/dist/to-response.cjs +153 -0
- package/template/apps/web/node_modules/better-call/dist/to-response.cjs.map +1 -0
- package/template/apps/web/node_modules/better-call/dist/to-response.d.cts +12 -0
- package/template/apps/web/node_modules/better-call/dist/to-response.d.mts +12 -0
- package/template/apps/web/node_modules/better-call/dist/to-response.mjs +153 -0
- package/template/apps/web/node_modules/better-call/dist/to-response.mjs.map +1 -0
- package/template/apps/web/node_modules/better-call/dist/utils.cjs +86 -0
- package/template/apps/web/node_modules/better-call/dist/utils.cjs.map +1 -0
- package/template/apps/web/node_modules/better-call/dist/utils.mjs +82 -0
- package/template/apps/web/node_modules/better-call/dist/utils.mjs.map +1 -0
- package/template/apps/web/node_modules/better-call/dist/validator.cjs +58 -0
- package/template/apps/web/node_modules/better-call/dist/validator.cjs.map +1 -0
- package/template/apps/web/node_modules/better-call/dist/validator.mjs +57 -0
- package/template/apps/web/node_modules/better-call/dist/validator.mjs.map +1 -0
- package/template/apps/web/node_modules/better-call/package.json +96 -0
- package/template/apps/web/node_modules/set-cookie-parser/LICENSE +21 -0
- package/template/apps/web/node_modules/set-cookie-parser/README.md +169 -0
- package/template/apps/web/node_modules/set-cookie-parser/dist/.eslintrc.cjs +16 -0
- package/template/apps/web/node_modules/set-cookie-parser/dist/set-cookie.cjs +260 -0
- package/template/apps/web/node_modules/set-cookie-parser/lib/set-cookie.d.ts +119 -0
- package/template/apps/web/node_modules/set-cookie-parser/lib/set-cookie.js +265 -0
- package/template/apps/web/node_modules/set-cookie-parser/package.json +61 -0
- package/template/apps/web/package.json +44 -0
- package/template/apps/web/public/favicon.ico +0 -0
- package/template/apps/web/public/manifest.json +25 -0
- package/template/apps/web/public/robots.txt +3 -0
- package/template/apps/web/src/components/app-sidebar.tsx +183 -0
- package/template/apps/web/src/components/login-form.tsx +111 -0
- package/template/apps/web/src/components/search-form.tsx +27 -0
- package/template/apps/web/src/components/signup-form.tsx +137 -0
- package/template/apps/web/src/components/version-switcher.tsx +64 -0
- package/template/apps/web/src/hooks/use-mobile.ts +19 -0
- package/template/apps/web/src/lib/auth-client.ts +3 -0
- package/template/apps/web/src/lib/auth-utils.ts +15 -0
- package/template/apps/web/src/lib/orpc.ts +32 -0
- package/template/apps/web/src/routeTree.gen.ts +171 -0
- package/template/apps/web/src/router.tsx +20 -0
- package/template/apps/web/src/routes/__root.tsx +41 -0
- package/template/apps/web/src/routes/api/auth/$.ts +15 -0
- package/template/apps/web/src/routes/api/rpc.$.ts +34 -0
- package/template/apps/web/src/routes/dashboard.tsx +59 -0
- package/template/apps/web/src/routes/index.tsx +57 -0
- package/template/apps/web/src/routes/login.tsx +16 -0
- package/template/apps/web/src/routes/signup.tsx +16 -0
- package/template/apps/web/tsconfig.json +29 -0
- package/template/apps/web/vite.config.ts +19 -0
- package/template/docker-compose.yml +16 -0
- package/template/package.json +35 -0
- package/template/packages/auth/eslint.config.ts +3 -0
- package/template/packages/auth/package.json +24 -0
- package/template/packages/auth/src/lib/auth.ts +13 -0
- package/template/packages/auth/tsconfig.json +17 -0
- package/template/packages/auth/tsconfig.lint.json +14 -0
- package/template/packages/db/eslint.config.ts +3 -0
- package/template/packages/db/package.json +27 -0
- package/template/packages/db/src/auth-schema.ts +93 -0
- package/template/packages/db/src/index.ts +7 -0
- package/template/packages/db/src/planets-schema.ts +13 -0
- package/template/packages/db/src/schema.ts +2 -0
- package/template/packages/db/tsconfig.json +17 -0
- package/template/packages/db/tsconfig.lint.json +14 -0
- package/template/packages/orpc/eslint.config.ts +3 -0
- package/template/packages/orpc/package.json +23 -0
- package/template/packages/orpc/src/lib/router.ts +52 -0
- package/template/packages/orpc/tsconfig.json +17 -0
- package/template/packages/orpc/tsconfig.lint.json +14 -0
- package/template/packages/ui/components.json +23 -0
- package/template/packages/ui/eslint.config.ts +3 -0
- package/template/packages/ui/package.json +48 -0
- package/template/packages/ui/src/components/.gitkeep +0 -0
- package/template/packages/ui/src/components/accordion.tsx +72 -0
- package/template/packages/ui/src/components/alert-dialog.tsx +187 -0
- package/template/packages/ui/src/components/alert.tsx +77 -0
- package/template/packages/ui/src/components/aspect-ratio.tsx +22 -0
- package/template/packages/ui/src/components/avatar.tsx +107 -0
- package/template/packages/ui/src/components/badge.tsx +53 -0
- package/template/packages/ui/src/components/breadcrumb.tsx +125 -0
- package/template/packages/ui/src/components/button-group.tsx +88 -0
- package/template/packages/ui/src/components/button.tsx +59 -0
- package/template/packages/ui/src/components/calendar.tsx +229 -0
- package/template/packages/ui/src/components/card.tsx +103 -0
- package/template/packages/ui/src/components/carousel.tsx +240 -0
- package/template/packages/ui/src/components/chart.tsx +371 -0
- package/template/packages/ui/src/components/checkbox.tsx +29 -0
- package/template/packages/ui/src/components/collapsible.tsx +19 -0
- package/template/packages/ui/src/components/combobox.tsx +295 -0
- package/template/packages/ui/src/components/command.tsx +196 -0
- package/template/packages/ui/src/components/context-menu.tsx +271 -0
- package/template/packages/ui/src/components/dialog.tsx +158 -0
- package/template/packages/ui/src/components/direction.tsx +4 -0
- package/template/packages/ui/src/components/drawer.tsx +134 -0
- package/template/packages/ui/src/components/dropdown-menu.tsx +266 -0
- package/template/packages/ui/src/components/empty.tsx +105 -0
- package/template/packages/ui/src/components/field.tsx +237 -0
- package/template/packages/ui/src/components/hover-card.tsx +51 -0
- package/template/packages/ui/src/components/input-group.tsx +159 -0
- package/template/packages/ui/src/components/input-otp.tsx +85 -0
- package/template/packages/ui/src/components/input.tsx +20 -0
- package/template/packages/ui/src/components/item.tsx +202 -0
- package/template/packages/ui/src/components/kbd.tsx +26 -0
- package/template/packages/ui/src/components/label.tsx +20 -0
- package/template/packages/ui/src/components/menubar.tsx +280 -0
- package/template/packages/ui/src/components/native-select.tsx +61 -0
- package/template/packages/ui/src/components/navigation-menu.tsx +168 -0
- package/template/packages/ui/src/components/pagination.tsx +130 -0
- package/template/packages/ui/src/components/popover.tsx +88 -0
- package/template/packages/ui/src/components/progress.tsx +83 -0
- package/template/packages/ui/src/components/radio-group.tsx +36 -0
- package/template/packages/ui/src/components/resizable.tsx +50 -0
- package/template/packages/ui/src/components/scroll-area.tsx +53 -0
- package/template/packages/ui/src/components/select.tsx +201 -0
- package/template/packages/ui/src/components/separator.tsx +23 -0
- package/template/packages/ui/src/components/sheet.tsx +138 -0
- package/template/packages/ui/src/components/sidebar.tsx +726 -0
- package/template/packages/ui/src/components/skeleton.tsx +13 -0
- package/template/packages/ui/src/components/slider.tsx +52 -0
- package/template/packages/ui/src/components/sonner.tsx +50 -0
- package/template/packages/ui/src/components/spinner.tsx +10 -0
- package/template/packages/ui/src/components/switch.tsx +30 -0
- package/template/packages/ui/src/components/table.tsx +116 -0
- package/template/packages/ui/src/components/tabs.tsx +81 -0
- package/template/packages/ui/src/components/textarea.tsx +18 -0
- package/template/packages/ui/src/components/toggle-group.tsx +87 -0
- package/template/packages/ui/src/components/toggle.tsx +46 -0
- package/template/packages/ui/src/components/tooltip.tsx +64 -0
- package/template/packages/ui/src/hooks/.gitkeep +0 -0
- package/template/packages/ui/src/hooks/use-mobile.ts +19 -0
- package/template/packages/ui/src/lib/.gitkeep +0 -0
- package/template/packages/ui/src/lib/utils.ts +7 -0
- package/template/packages/ui/src/styles/globals.css +130 -0
- package/template/packages/ui/tsconfig.json +17 -0
- package/template/packages/ui/tsconfig.lint.json +14 -0
- package/template/tsconfig.json +9 -0
- package/template/turbo.json +24 -0
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
import { isValidIP, normalizeIP } from "./ip";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Host classification per RFC 6890 (Special-Purpose IP Address Registries),
|
|
5
|
+
* RFC 6761 (Special-Use Domain Names), and RFC 8252 §7.3 (loopback redirect URIs).
|
|
6
|
+
*
|
|
7
|
+
* This module is the single source of truth for "is this host public? private?
|
|
8
|
+
* loopback? link-local?" in the codebase. Consumers MUST prefer these predicates
|
|
9
|
+
* over bespoke regexes or substring matches; divergent checks are how bypass
|
|
10
|
+
* vulnerabilities get introduced (e.g. Oligo's "0.0.0.0 Day" 2024).
|
|
11
|
+
*
|
|
12
|
+
* Four user-facing primitives:
|
|
13
|
+
*
|
|
14
|
+
* - `classifyHost(host)` — the workhorse. Returns a {@link HostClassification}
|
|
15
|
+
* with `kind`, `literal`, and `canonical` fields.
|
|
16
|
+
* - `isLoopbackIP(host)` — strict: IPv4 `127.0.0.0/8` or IPv6 `::1` only.
|
|
17
|
+
* Use this for RFC 8252 §7.3 loopback redirect URI matching where IP
|
|
18
|
+
* literals are REQUIRED.
|
|
19
|
+
* - `isLoopbackHost(host)` — permissive: also accepts `localhost` and RFC 6761
|
|
20
|
+
* `.localhost` subdomains. Use this for developer ergonomics (CORS, cookie
|
|
21
|
+
* secure bypass, dev-mode HTTP allow-list).
|
|
22
|
+
* - `isPublicRoutableHost(host)` — SSRF gate. Returns false for every
|
|
23
|
+
* non-`public` kind. Use this before server-side fetches to user-controlled
|
|
24
|
+
* URLs.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The semantic kind of a host, derived from RFC 6890 special-purpose registries
|
|
29
|
+
* plus a few domain-name categories (localhost, cloud metadata FQDNs).
|
|
30
|
+
*/
|
|
31
|
+
export type HostKind =
|
|
32
|
+
/** IPv4 `127.0.0.0/8` or IPv6 `::1`. */
|
|
33
|
+
| "loopback"
|
|
34
|
+
/** DNS name `localhost` or RFC 6761 `.localhost` TLD. */
|
|
35
|
+
| "localhost"
|
|
36
|
+
/** IPv4 `0.0.0.0` or IPv6 `::` — "this host on this network", not loopback. */
|
|
37
|
+
| "unspecified"
|
|
38
|
+
/** RFC 1918 `10/8`, `172.16/12`, `192.168/16`, or IPv6 ULA `fc00::/7`. */
|
|
39
|
+
| "private"
|
|
40
|
+
/** IPv4 `169.254/16` or IPv6 `fe80::/10`. Includes AWS IMDS `169.254.169.254`. */
|
|
41
|
+
| "linkLocal"
|
|
42
|
+
/** RFC 6598 carrier-grade NAT `100.64.0.0/10`. */
|
|
43
|
+
| "sharedAddressSpace"
|
|
44
|
+
/** RFC 5737 `192.0.2/24`, `198.51.100/24`, `203.0.113/24`, or RFC 3849 `2001:db8::/32`. */
|
|
45
|
+
| "documentation"
|
|
46
|
+
/** RFC 2544 `198.18.0.0/15`. */
|
|
47
|
+
| "benchmarking"
|
|
48
|
+
/** IPv4 `224.0.0.0/4` or IPv6 `ff00::/8`. */
|
|
49
|
+
| "multicast"
|
|
50
|
+
/** IPv4 limited broadcast `255.255.255.255`. */
|
|
51
|
+
| "broadcast"
|
|
52
|
+
/** Other RFC 6890 special-purpose ranges (0/8, 192.0.0/24, 240/4, 2001::/32, etc.). */
|
|
53
|
+
| "reserved"
|
|
54
|
+
/** Cloud metadata service FQDN (e.g. `metadata.google.internal`). */
|
|
55
|
+
| "cloudMetadata"
|
|
56
|
+
/** Any host not matching a special-purpose range above. */
|
|
57
|
+
| "public";
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The syntactic form of the input host: an IPv4 literal, an IPv6 literal, or
|
|
61
|
+
* a domain name. IPv4-mapped IPv6 (`::ffff:192.0.2.1`) is reported as `ipv4`
|
|
62
|
+
* because it's unmapped during canonicalization.
|
|
63
|
+
*/
|
|
64
|
+
export type HostLiteral = "ipv4" | "ipv6" | "fqdn";
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Result of {@link classifyHost}. All fields are readonly.
|
|
68
|
+
*
|
|
69
|
+
* @property kind - Semantic classification per RFC 6890 + RFC 6761.
|
|
70
|
+
* @property literal - Syntactic form of the input (IPv4, IPv6, or FQDN).
|
|
71
|
+
* @property canonical - Lowercase, port-stripped, bracket-stripped, zone-id-stripped
|
|
72
|
+
* form suitable for equality comparison. IPv6 is expanded to full form.
|
|
73
|
+
* IPv4-mapped IPv6 is collapsed to the underlying IPv4.
|
|
74
|
+
*/
|
|
75
|
+
export interface HostClassification {
|
|
76
|
+
readonly kind: HostKind;
|
|
77
|
+
readonly literal: HostLiteral;
|
|
78
|
+
readonly canonical: string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Cloud provider instance metadata service FQDNs. These resolve to link-local
|
|
83
|
+
* IPs (usually `169.254.169.254`) inside their respective clouds and are
|
|
84
|
+
* prime SSRF targets.
|
|
85
|
+
*
|
|
86
|
+
* The IPs themselves are already caught by the `linkLocal` kind; this set
|
|
87
|
+
* only exists for the FQDN form that a naive server-side fetch might resolve
|
|
88
|
+
* via its own resolver.
|
|
89
|
+
*/
|
|
90
|
+
const CLOUD_METADATA_HOSTS: ReadonlySet<string> = new Set([
|
|
91
|
+
"metadata.google.internal",
|
|
92
|
+
"metadata.goog",
|
|
93
|
+
"metadata",
|
|
94
|
+
"instance-data",
|
|
95
|
+
"instance-data.ec2.internal",
|
|
96
|
+
]);
|
|
97
|
+
|
|
98
|
+
/** Strip `[...]` if the entire input is bracketed (IPv6 literal form). */
|
|
99
|
+
function stripBrackets(host: string): string {
|
|
100
|
+
if (host.length >= 2 && host.startsWith("[") && host.endsWith("]")) {
|
|
101
|
+
return host.slice(1, -1);
|
|
102
|
+
}
|
|
103
|
+
return host;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Strip trailing `:port` from host-with-port strings.
|
|
108
|
+
*
|
|
109
|
+
* - Bracketed IPv6 with port: `[::1]:8080` → `[::1]`
|
|
110
|
+
* - IPv4/FQDN with port: `127.0.0.1:3000` / `example.com:443` → base form
|
|
111
|
+
* - Bare IPv6: `::1` / `fe80::1` → unchanged (multiple colons means no port)
|
|
112
|
+
*/
|
|
113
|
+
function stripPort(host: string): string {
|
|
114
|
+
if (host.startsWith("[")) {
|
|
115
|
+
const end = host.indexOf("]");
|
|
116
|
+
if (end === -1) return host;
|
|
117
|
+
return host.slice(0, end + 1);
|
|
118
|
+
}
|
|
119
|
+
const firstColon = host.indexOf(":");
|
|
120
|
+
if (firstColon === -1) return host;
|
|
121
|
+
if (host.indexOf(":", firstColon + 1) !== -1) return host;
|
|
122
|
+
return host.slice(0, firstColon);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** Strip IPv6 zone identifier: `fe80::1%eth0` → `fe80::1`. */
|
|
126
|
+
function stripZoneId(host: string): string {
|
|
127
|
+
const zone = host.indexOf("%");
|
|
128
|
+
if (zone === -1) return host;
|
|
129
|
+
return host.slice(0, zone);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Strip trailing dots (RFC 1034 absolute DNS form): `localhost.` → `localhost`.
|
|
134
|
+
* Without this, `metadata.google.internal.` would fall through to `public` and
|
|
135
|
+
* bypass the cloud-metadata / `.localhost` checks, since WHATWG URL parsing
|
|
136
|
+
* preserves the trailing dot in `url.hostname`.
|
|
137
|
+
*/
|
|
138
|
+
function stripTrailingDot(host: string): string {
|
|
139
|
+
return host.replace(/\.+$/, "");
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** Fast dotted-decimal shape check. Does NOT validate octet bounds. */
|
|
143
|
+
function looksLikeIPv4(host: string): boolean {
|
|
144
|
+
return /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(host);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Pack a validated dotted-decimal IPv4 into a 32-bit unsigned integer. */
|
|
148
|
+
function ipv4ToUint32(ip: string): number {
|
|
149
|
+
const parts = ip.split(".");
|
|
150
|
+
return (
|
|
151
|
+
((Number(parts[0]) << 24) |
|
|
152
|
+
(Number(parts[1]) << 16) |
|
|
153
|
+
(Number(parts[2]) << 8) |
|
|
154
|
+
Number(parts[3])) >>>
|
|
155
|
+
0
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** Check whether a 32-bit value matches `prefix/length` (both unsigned). */
|
|
160
|
+
function inIPv4Range(value: number, prefix: number, length: number): boolean {
|
|
161
|
+
if (length === 0) return true;
|
|
162
|
+
const mask = length === 32 ? 0xffffffff : (~0 << (32 - length)) >>> 0;
|
|
163
|
+
return (value & mask) === (prefix & mask);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function classifyIPv4(ip: string): HostKind {
|
|
167
|
+
if (ip === "0.0.0.0") return "unspecified";
|
|
168
|
+
if (ip === "255.255.255.255") return "broadcast";
|
|
169
|
+
|
|
170
|
+
const n = ipv4ToUint32(ip);
|
|
171
|
+
|
|
172
|
+
if (inIPv4Range(n, ipv4ToUint32("127.0.0.0"), 8)) return "loopback";
|
|
173
|
+
if (inIPv4Range(n, ipv4ToUint32("10.0.0.0"), 8)) return "private";
|
|
174
|
+
if (inIPv4Range(n, ipv4ToUint32("172.16.0.0"), 12)) return "private";
|
|
175
|
+
if (inIPv4Range(n, ipv4ToUint32("192.168.0.0"), 16)) return "private";
|
|
176
|
+
if (inIPv4Range(n, ipv4ToUint32("169.254.0.0"), 16)) return "linkLocal";
|
|
177
|
+
if (inIPv4Range(n, ipv4ToUint32("100.64.0.0"), 10))
|
|
178
|
+
return "sharedAddressSpace";
|
|
179
|
+
if (inIPv4Range(n, ipv4ToUint32("192.0.2.0"), 24)) return "documentation";
|
|
180
|
+
if (inIPv4Range(n, ipv4ToUint32("198.51.100.0"), 24)) return "documentation";
|
|
181
|
+
if (inIPv4Range(n, ipv4ToUint32("203.0.113.0"), 24)) return "documentation";
|
|
182
|
+
if (inIPv4Range(n, ipv4ToUint32("198.18.0.0"), 15)) return "benchmarking";
|
|
183
|
+
if (inIPv4Range(n, ipv4ToUint32("224.0.0.0"), 4)) return "multicast";
|
|
184
|
+
if (inIPv4Range(n, ipv4ToUint32("0.0.0.0"), 8)) return "reserved";
|
|
185
|
+
if (inIPv4Range(n, ipv4ToUint32("192.0.0.0"), 24)) return "reserved";
|
|
186
|
+
if (inIPv4Range(n, ipv4ToUint32("240.0.0.0"), 4)) return "reserved";
|
|
187
|
+
|
|
188
|
+
return "public";
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Extract an IPv4 address embedded in an expanded IPv6 literal.
|
|
193
|
+
*
|
|
194
|
+
* Used to recurse into tunnel/translation forms (6to4, NAT64, Teredo) so a
|
|
195
|
+
* private destination cannot be smuggled behind a syntactically-public IPv6
|
|
196
|
+
* literal. `startGroup` is the index of the first of two 16-bit groups in the
|
|
197
|
+
* expanded form (`0000:0000:...`). With `xor: true`, the 32-bit value is XORed
|
|
198
|
+
* with `0xffffffff` before decoding (Teredo obfuscates the client IPv4 this
|
|
199
|
+
* way).
|
|
200
|
+
*/
|
|
201
|
+
function extractEmbeddedIPv4(
|
|
202
|
+
expanded: string,
|
|
203
|
+
startGroup: number,
|
|
204
|
+
options: { xor?: boolean } = {},
|
|
205
|
+
): string | null {
|
|
206
|
+
const offset = startGroup * 5;
|
|
207
|
+
const g1 = Number.parseInt(expanded.slice(offset, offset + 4), 16);
|
|
208
|
+
const g2 = Number.parseInt(expanded.slice(offset + 5, offset + 9), 16);
|
|
209
|
+
if (!Number.isFinite(g1) || !Number.isFinite(g2)) return null;
|
|
210
|
+
let combined = ((g1 << 16) | g2) >>> 0;
|
|
211
|
+
if (options.xor) combined = (combined ^ 0xffffffff) >>> 0;
|
|
212
|
+
return `${(combined >>> 24) & 0xff}.${(combined >>> 16) & 0xff}.${(combined >>> 8) & 0xff}.${combined & 0xff}`;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Classify an expanded, full-form, lowercase IPv6 address (no IPv4-mapped
|
|
217
|
+
* input — those are unmapped to IPv4 before reaching here).
|
|
218
|
+
*
|
|
219
|
+
* 6to4 (`2002::/16`), NAT64 (`64:ff9b::/96`) and Teredo (`2001:0000::/32`)
|
|
220
|
+
* embed an IPv4 that can route to private/loopback space. If the embedded
|
|
221
|
+
* IPv4 classifies as non-`public`, return `reserved` — blocks SSRF without
|
|
222
|
+
* advertising the address as a loopback literal for RFC 8252 §7.3 matching.
|
|
223
|
+
*/
|
|
224
|
+
function classifyIPv6(expanded: string): HostKind {
|
|
225
|
+
if (expanded === "0000:0000:0000:0000:0000:0000:0000:0000")
|
|
226
|
+
return "unspecified";
|
|
227
|
+
if (expanded === "0000:0000:0000:0000:0000:0000:0000:0001") return "loopback";
|
|
228
|
+
|
|
229
|
+
const firstByte = Number.parseInt(expanded.slice(0, 2), 16);
|
|
230
|
+
const secondByte = Number.parseInt(expanded.slice(2, 4), 16);
|
|
231
|
+
|
|
232
|
+
if (firstByte === 0xff) return "multicast";
|
|
233
|
+
if (firstByte === 0xfe && (secondByte & 0xc0) === 0x80) return "linkLocal";
|
|
234
|
+
if ((firstByte & 0xfe) === 0xfc) return "private";
|
|
235
|
+
|
|
236
|
+
if (expanded.startsWith("2001:0db8:")) return "documentation";
|
|
237
|
+
|
|
238
|
+
if (expanded.startsWith("2002:")) {
|
|
239
|
+
const embedded = extractEmbeddedIPv4(expanded, 1);
|
|
240
|
+
if (embedded && classifyIPv4(embedded) !== "public") return "reserved";
|
|
241
|
+
return "public";
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (expanded.startsWith("0064:ff9b:0000:0000:0000:0000:")) {
|
|
245
|
+
const embedded = extractEmbeddedIPv4(expanded, 6);
|
|
246
|
+
if (embedded && classifyIPv4(embedded) !== "public") return "reserved";
|
|
247
|
+
return "reserved";
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (expanded.startsWith("2001:0000:")) {
|
|
251
|
+
const embedded = extractEmbeddedIPv4(expanded, 6, { xor: true });
|
|
252
|
+
if (embedded && classifyIPv4(embedded) !== "public") return "reserved";
|
|
253
|
+
return "reserved";
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (expanded.startsWith("0100:0000:0000:0000:")) return "reserved";
|
|
257
|
+
|
|
258
|
+
return "public";
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Classify a host string according to RFC 6890 / RFC 6761.
|
|
263
|
+
*
|
|
264
|
+
* Accepts inputs in any of these shapes and normalizes before classifying:
|
|
265
|
+
*
|
|
266
|
+
* - Bare IPv4: `127.0.0.1`
|
|
267
|
+
* - Bare IPv6: `::1`, `fe80::1%eth0`
|
|
268
|
+
* - Bracketed IPv6: `[::1]`
|
|
269
|
+
* - Host with port: `localhost:3000`, `127.0.0.1:443`, `[::1]:8080`
|
|
270
|
+
* - FQDN: `example.com`, `tenant.localhost`
|
|
271
|
+
* - IPv4-mapped IPv6: `::ffff:192.0.2.1` (reported as `literal: "ipv4"`)
|
|
272
|
+
*
|
|
273
|
+
* Invalid or non-resolvable FQDNs are returned as `{ kind: "public", literal: "fqdn" }`
|
|
274
|
+
* — this function never throws. Callers that need structural validation must
|
|
275
|
+
* combine this with a URL/hostname validator upstream.
|
|
276
|
+
*
|
|
277
|
+
* @example
|
|
278
|
+
* classifyHost("127.0.0.1")
|
|
279
|
+
* // { kind: "loopback", literal: "ipv4", canonical: "127.0.0.1" }
|
|
280
|
+
*
|
|
281
|
+
* @example
|
|
282
|
+
* classifyHost("[::1]:8080")
|
|
283
|
+
* // { kind: "loopback", literal: "ipv6", canonical: "0000:0000:...:0001" }
|
|
284
|
+
*
|
|
285
|
+
* @example
|
|
286
|
+
* classifyHost("::ffff:192.0.2.1")
|
|
287
|
+
* // { kind: "documentation", literal: "ipv4", canonical: "192.0.2.1" }
|
|
288
|
+
*
|
|
289
|
+
* @example
|
|
290
|
+
* classifyHost("tenant-a.localhost")
|
|
291
|
+
* // { kind: "localhost", literal: "fqdn", canonical: "tenant-a.localhost" }
|
|
292
|
+
*/
|
|
293
|
+
export function classifyHost(host: string): HostClassification {
|
|
294
|
+
const stripped = stripTrailingDot(
|
|
295
|
+
stripZoneId(stripBrackets(stripPort(host.trim()))),
|
|
296
|
+
);
|
|
297
|
+
const lowered = stripped.toLowerCase();
|
|
298
|
+
|
|
299
|
+
if (lowered === "") {
|
|
300
|
+
return { kind: "reserved", literal: "fqdn", canonical: "" };
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (!isValidIP(lowered)) {
|
|
304
|
+
if (lowered === "localhost" || lowered.endsWith(".localhost")) {
|
|
305
|
+
return { kind: "localhost", literal: "fqdn", canonical: lowered };
|
|
306
|
+
}
|
|
307
|
+
if (CLOUD_METADATA_HOSTS.has(lowered)) {
|
|
308
|
+
return { kind: "cloudMetadata", literal: "fqdn", canonical: lowered };
|
|
309
|
+
}
|
|
310
|
+
return { kind: "public", literal: "fqdn", canonical: lowered };
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (looksLikeIPv4(lowered)) {
|
|
314
|
+
return { kind: classifyIPv4(lowered), literal: "ipv4", canonical: lowered };
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const canonical = normalizeIP(lowered, { ipv6Subnet: 128 });
|
|
318
|
+
|
|
319
|
+
if (looksLikeIPv4(canonical)) {
|
|
320
|
+
return {
|
|
321
|
+
kind: classifyIPv4(canonical),
|
|
322
|
+
literal: "ipv4",
|
|
323
|
+
canonical,
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return { kind: classifyIPv6(canonical), literal: "ipv6", canonical };
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Strict loopback-IP-literal check per RFC 8252 §7.3.
|
|
332
|
+
*
|
|
333
|
+
* Returns true ONLY for IPv4 `127.0.0.0/8` or IPv6 `::1`. The DNS name
|
|
334
|
+
* `localhost` returns false — RFC 8252 §8.3 explicitly recommends against
|
|
335
|
+
* relying on name resolution for loopback redirect URIs.
|
|
336
|
+
*
|
|
337
|
+
* Use this for OAuth redirect URI matching.
|
|
338
|
+
*
|
|
339
|
+
* @example
|
|
340
|
+
* isLoopbackIP("127.0.0.1") // true
|
|
341
|
+
* isLoopbackIP("::1") // true
|
|
342
|
+
* isLoopbackIP("[::1]:8080") // true
|
|
343
|
+
* isLoopbackIP("localhost") // false (use isLoopbackHost for DNS names)
|
|
344
|
+
* isLoopbackIP("0.0.0.0") // false (unspecified, not loopback)
|
|
345
|
+
*/
|
|
346
|
+
export function isLoopbackIP(host: string): boolean {
|
|
347
|
+
return classifyHost(host).kind === "loopback";
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Permissive loopback check for developer-ergonomics code paths.
|
|
352
|
+
*
|
|
353
|
+
* Returns true for IPv4 `127.0.0.0/8`, IPv6 `::1`, the literal name `localhost`,
|
|
354
|
+
* and any RFC 6761 `.localhost` subdomain (`tenant.localhost`, `app.localhost`).
|
|
355
|
+
*
|
|
356
|
+
* Use this for things like: allowing HTTP for dev servers, skipping Secure
|
|
357
|
+
* cookie requirements, browser-trust heuristics. Do NOT use this for OAuth
|
|
358
|
+
* redirect URI matching — use {@link isLoopbackIP} there.
|
|
359
|
+
*
|
|
360
|
+
* @example
|
|
361
|
+
* isLoopbackHost("localhost") // true
|
|
362
|
+
* isLoopbackHost("tenant.localhost") // true (RFC 6761)
|
|
363
|
+
* isLoopbackHost("127.0.0.1") // true
|
|
364
|
+
* isLoopbackHost("0.0.0.0") // false (unspecified, NOT loopback)
|
|
365
|
+
*/
|
|
366
|
+
export function isLoopbackHost(host: string): boolean {
|
|
367
|
+
const kind = classifyHost(host).kind;
|
|
368
|
+
return kind === "loopback" || kind === "localhost";
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* First-line SSRF gate: returns true ONLY for hosts that classify as `public`.
|
|
373
|
+
*
|
|
374
|
+
* Every RFC 6890 special-purpose range (loopback, private, link-local,
|
|
375
|
+
* unspecified, documentation, multicast, broadcast, reserved, shared address
|
|
376
|
+
* space, benchmarking) and cloud-metadata FQDN returns false.
|
|
377
|
+
*
|
|
378
|
+
* Use this BEFORE issuing a server-side fetch to a user-supplied URL, e.g.
|
|
379
|
+
* OAuth introspection endpoints, webhook targets, or metadata-document
|
|
380
|
+
* fetches (CIMD).
|
|
381
|
+
*
|
|
382
|
+
* Limitations (this is a syntactic check, not a complete SSRF mitigation):
|
|
383
|
+
* - No DNS resolution: a public-looking FQDN that resolves to a private IP
|
|
384
|
+
* passes this check. Re-verify the resolved address before connecting, or
|
|
385
|
+
* pin the socket to the resolved IP.
|
|
386
|
+
* - No DNS-rebinding defense: attackers can return a public IP on the first
|
|
387
|
+
* lookup and a private IP on the second. Resolve once and reuse the IP.
|
|
388
|
+
* - No redirect following: HTTP 3xx responses can redirect to private hosts.
|
|
389
|
+
* Re-run this check on every redirect target, or disable auto-follow.
|
|
390
|
+
*
|
|
391
|
+
* @example
|
|
392
|
+
* isPublicRoutableHost("example.com") // true
|
|
393
|
+
* isPublicRoutableHost("127.0.0.1") // false (loopback)
|
|
394
|
+
* isPublicRoutableHost("169.254.169.254") // false (linkLocal / AWS IMDS)
|
|
395
|
+
* isPublicRoutableHost("metadata.google.internal") // false (cloudMetadata)
|
|
396
|
+
* isPublicRoutableHost("10.0.0.1") // false (private)
|
|
397
|
+
* isPublicRoutableHost("::ffff:127.0.0.1") // false (mapped loopback)
|
|
398
|
+
*/
|
|
399
|
+
export function isPublicRoutableHost(host: string): boolean {
|
|
400
|
+
return classifyHost(host).kind === "public";
|
|
401
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Normalizes an IP address for consistent rate limiting.
|
|
5
|
+
*
|
|
6
|
+
* Features:
|
|
7
|
+
* - Normalizes IPv6 to canonical lowercase form
|
|
8
|
+
* - Converts IPv4-mapped IPv6 to IPv4
|
|
9
|
+
* - Supports IPv6 subnet extraction
|
|
10
|
+
* - Handles all edge cases (::1, ::, etc.)
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
interface NormalizeIPOptions {
|
|
14
|
+
/**
|
|
15
|
+
* For IPv6 addresses, extract the subnet prefix instead of full address.
|
|
16
|
+
* Common values: 32, 48, 64, 128 (default: 128 = full address)
|
|
17
|
+
*
|
|
18
|
+
* @default 128
|
|
19
|
+
*/
|
|
20
|
+
ipv6Subnet?: 128 | 64 | 48 | 32;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Checks if an IP is valid IPv4 or IPv6
|
|
25
|
+
*/
|
|
26
|
+
export function isValidIP(ip: string): boolean {
|
|
27
|
+
return z.ipv4().safeParse(ip).success || z.ipv6().safeParse(ip).success;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Checks if an IP is IPv6
|
|
32
|
+
*/
|
|
33
|
+
function isIPv6(ip: string): boolean {
|
|
34
|
+
return z.ipv6().safeParse(ip).success;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Converts IPv4-mapped IPv6 address to IPv4
|
|
39
|
+
* e.g., "::ffff:192.0.2.1" -> "192.0.2.1"
|
|
40
|
+
*/
|
|
41
|
+
function extractIPv4FromMapped(ipv6: string): string | null {
|
|
42
|
+
const lower = ipv6.toLowerCase();
|
|
43
|
+
|
|
44
|
+
// Handle ::ffff:192.0.2.1 format
|
|
45
|
+
if (lower.startsWith("::ffff:")) {
|
|
46
|
+
const ipv4Part = lower.substring(7);
|
|
47
|
+
// Check if it's a valid IPv4
|
|
48
|
+
if (z.ipv4().safeParse(ipv4Part).success) {
|
|
49
|
+
return ipv4Part;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Handle full form: 0:0:0:0:0:ffff:192.0.2.1
|
|
54
|
+
const parts = ipv6.split(":");
|
|
55
|
+
if (parts.length === 7 && parts[5]?.toLowerCase() === "ffff") {
|
|
56
|
+
const ipv4Part = parts[6];
|
|
57
|
+
if (ipv4Part && z.ipv4().safeParse(ipv4Part).success) {
|
|
58
|
+
return ipv4Part;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Handle hex-encoded IPv4 in mapped address
|
|
63
|
+
// e.g., ::ffff:c000:0201 -> 192.0.2.1
|
|
64
|
+
if (lower.includes("::ffff:") || lower.includes(":ffff:")) {
|
|
65
|
+
const groups = expandIPv6(ipv6);
|
|
66
|
+
if (
|
|
67
|
+
groups.length === 8 &&
|
|
68
|
+
groups[0] === "0000" &&
|
|
69
|
+
groups[1] === "0000" &&
|
|
70
|
+
groups[2] === "0000" &&
|
|
71
|
+
groups[3] === "0000" &&
|
|
72
|
+
groups[4] === "0000" &&
|
|
73
|
+
groups[5] === "ffff" &&
|
|
74
|
+
groups[6] &&
|
|
75
|
+
groups[7]
|
|
76
|
+
) {
|
|
77
|
+
// Convert last two groups to IPv4
|
|
78
|
+
const byte1 = Number.parseInt(groups[6].substring(0, 2), 16);
|
|
79
|
+
const byte2 = Number.parseInt(groups[6].substring(2, 4), 16);
|
|
80
|
+
const byte3 = Number.parseInt(groups[7].substring(0, 2), 16);
|
|
81
|
+
const byte4 = Number.parseInt(groups[7].substring(2, 4), 16);
|
|
82
|
+
return `${byte1}.${byte2}.${byte3}.${byte4}`;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Expands a compressed IPv6 address to full form
|
|
91
|
+
* e.g., "2001:db8::1" -> ["2001", "0db8", "0000", "0000", "0000", "0000", "0000", "0001"]
|
|
92
|
+
*/
|
|
93
|
+
function expandIPv6(ipv6: string): string[] {
|
|
94
|
+
// Handle :: notation (zero compression)
|
|
95
|
+
if (ipv6.includes("::")) {
|
|
96
|
+
const sides = ipv6.split("::");
|
|
97
|
+
const left = sides[0] ? sides[0].split(":") : [];
|
|
98
|
+
const right = sides[1] ? sides[1].split(":") : [];
|
|
99
|
+
|
|
100
|
+
// Calculate missing groups
|
|
101
|
+
const totalGroups = 8;
|
|
102
|
+
const missingGroups = totalGroups - left.length - right.length;
|
|
103
|
+
const zeros = Array(missingGroups).fill("0000");
|
|
104
|
+
|
|
105
|
+
// Pad existing groups to 4 digits
|
|
106
|
+
const paddedLeft = left.map((g) => g.padStart(4, "0"));
|
|
107
|
+
const paddedRight = right.map((g) => g.padStart(4, "0"));
|
|
108
|
+
|
|
109
|
+
return [...paddedLeft, ...zeros, ...paddedRight];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// No compression, just pad each group
|
|
113
|
+
return ipv6.split(":").map((g) => g.padStart(4, "0"));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Normalizes an IPv6 address to canonical form
|
|
118
|
+
* e.g., "2001:DB8::1" -> "2001:0db8:0000:0000:0000:0000:0000:0001"
|
|
119
|
+
*/
|
|
120
|
+
function normalizeIPv6(
|
|
121
|
+
ipv6: string,
|
|
122
|
+
subnetPrefix?: 128 | 32 | 48 | 64,
|
|
123
|
+
): string {
|
|
124
|
+
const groups = expandIPv6(ipv6);
|
|
125
|
+
|
|
126
|
+
if (subnetPrefix && subnetPrefix < 128) {
|
|
127
|
+
// Apply subnet mask
|
|
128
|
+
const prefix = subnetPrefix;
|
|
129
|
+
let bitsRemaining: number = prefix;
|
|
130
|
+
|
|
131
|
+
const maskedGroups = groups.map((group) => {
|
|
132
|
+
if (bitsRemaining <= 0) {
|
|
133
|
+
return "0000";
|
|
134
|
+
}
|
|
135
|
+
if (bitsRemaining >= 16) {
|
|
136
|
+
bitsRemaining -= 16;
|
|
137
|
+
return group;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Partial mask for this group
|
|
141
|
+
const value = Number.parseInt(group, 16);
|
|
142
|
+
const mask = (0xffff << (16 - bitsRemaining)) & 0xffff;
|
|
143
|
+
const masked = value & mask;
|
|
144
|
+
bitsRemaining = 0;
|
|
145
|
+
return masked.toString(16).padStart(4, "0");
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
return maskedGroups.join(":").toLowerCase();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return groups.join(":").toLowerCase();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Normalizes an IP address (IPv4 or IPv6) for consistent rate limiting.
|
|
156
|
+
*
|
|
157
|
+
* @param ip - The IP address to normalize
|
|
158
|
+
* @param options - Normalization options
|
|
159
|
+
* @returns Normalized IP address
|
|
160
|
+
*
|
|
161
|
+
* @example
|
|
162
|
+
* normalizeIP("2001:DB8::1")
|
|
163
|
+
* // -> "2001:0db8:0000:0000:0000:0000:0000:0000"
|
|
164
|
+
*
|
|
165
|
+
* @example
|
|
166
|
+
* normalizeIP("::ffff:192.0.2.1")
|
|
167
|
+
* // -> "192.0.2.1" (converted to IPv4)
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* normalizeIP("2001:db8::1", { ipv6Subnet: 64 })
|
|
171
|
+
* // -> "2001:0db8:0000:0000:0000:0000:0000:0000" (subnet /64)
|
|
172
|
+
*/
|
|
173
|
+
export function normalizeIP(
|
|
174
|
+
ip: string,
|
|
175
|
+
options: NormalizeIPOptions = {},
|
|
176
|
+
): string {
|
|
177
|
+
// IPv4 addresses are already normalized
|
|
178
|
+
if (z.ipv4().safeParse(ip).success) {
|
|
179
|
+
return ip.toLowerCase();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Check if it's IPv6
|
|
183
|
+
if (!isIPv6(ip)) {
|
|
184
|
+
// Return as-is if not valid (shouldn't happen due to prior validation)
|
|
185
|
+
return ip.toLowerCase();
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Check for IPv4-mapped IPv6
|
|
189
|
+
const ipv4 = extractIPv4FromMapped(ip);
|
|
190
|
+
if (ipv4) {
|
|
191
|
+
return ipv4.toLowerCase();
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Normalize IPv6
|
|
195
|
+
const subnetPrefix = options.ipv6Subnet || 64;
|
|
196
|
+
return normalizeIPv6(ip, subnetPrefix);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Creates a rate limit key from IP and path
|
|
201
|
+
* Uses a separator to prevent collision attacks
|
|
202
|
+
*
|
|
203
|
+
* @param ip - The IP address (should be normalized)
|
|
204
|
+
* @param path - The request path
|
|
205
|
+
* @returns Rate limit key
|
|
206
|
+
*/
|
|
207
|
+
export function createRateLimitKey(ip: string, path: string): string {
|
|
208
|
+
// Use | as separator to prevent collision attacks
|
|
209
|
+
// e.g., "192.0.2.1" + "/sign-in" vs "192.0.2" + ".1/sign-in"
|
|
210
|
+
return `${ip}|${path}`;
|
|
211
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { logger } from "../env";
|
|
2
|
+
|
|
3
|
+
const iso8601Regex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/;
|
|
4
|
+
|
|
5
|
+
function reviveDate(value: unknown): any {
|
|
6
|
+
if (typeof value === "string" && iso8601Regex.test(value)) {
|
|
7
|
+
const date = new Date(value);
|
|
8
|
+
if (!isNaN(date.getTime())) {
|
|
9
|
+
return date;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Recursively walk a pre-parsed object and convert ISO 8601 date strings
|
|
17
|
+
* to Date instances. This handles the case where a Redis client (or similar)
|
|
18
|
+
* returns already-parsed JSON objects whose date fields are still strings.
|
|
19
|
+
*/
|
|
20
|
+
function reviveDates(value: unknown): any {
|
|
21
|
+
if (value === null || value === undefined) {
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
if (typeof value === "string") {
|
|
25
|
+
return reviveDate(value);
|
|
26
|
+
}
|
|
27
|
+
if (value instanceof Date) {
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
30
|
+
if (Array.isArray(value)) {
|
|
31
|
+
return value.map(reviveDates);
|
|
32
|
+
}
|
|
33
|
+
if (typeof value === "object") {
|
|
34
|
+
const result: Record<string, any> = {};
|
|
35
|
+
for (const key of Object.keys(value)) {
|
|
36
|
+
result[key] = reviveDates((value as Record<string, any>)[key]);
|
|
37
|
+
}
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
return value;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function safeJSONParse<T>(data: unknown): T | null {
|
|
44
|
+
try {
|
|
45
|
+
if (typeof data !== "string") {
|
|
46
|
+
if (data === null || data === undefined) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
return reviveDates(data) as T;
|
|
50
|
+
}
|
|
51
|
+
return JSON.parse(data, (_, value) => reviveDate(value));
|
|
52
|
+
} catch (e) {
|
|
53
|
+
logger.error("Error parsing JSON", { error: e });
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|