@braintwopoint0/playback-commons 0.2.4 → 0.2.6

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.
@@ -0,0 +1,34 @@
1
+ type CorsOptions = {
2
+ /** Comma-separated method list. Default: 'POST, OPTIONS'. */
3
+ methods?: string;
4
+ /** Comma-separated header allowlist. Default: 'Content-Type'. */
5
+ headers?: string;
6
+ /** Preflight cache duration in seconds. Default: 86400 (24h). */
7
+ maxAge?: number;
8
+ };
9
+ /**
10
+ * Build CORS response headers.
11
+ *
12
+ * Always returns `Vary: Origin` so caches don't serve a CORS-allowed response
13
+ * to a different origin. Only emits `Access-Control-*` headers when the request
14
+ * Origin is in the allowlist — exact-match, no wildcards, so a typo'd subdomain
15
+ * or rogue preview URL can't post into a production endpoint.
16
+ *
17
+ * Same-origin requests have no Origin header on POST in some browsers and never
18
+ * preflight, so an absent or unknown origin returning the empty CORS set is the
19
+ * correct behavior.
20
+ */
21
+ declare function corsHeaders(origin: string | null, allowed: ReadonlySet<string>, opts?: CorsOptions): Record<string, string>;
22
+ /**
23
+ * Build a preflight (OPTIONS) Response. Use directly as the route's OPTIONS export:
24
+ *
25
+ * export const OPTIONS = (req: NextRequest) => corsPreflight(req, ALLOWED_ORIGINS)
26
+ *
27
+ * Returns 204 No Content with the CORS headers built from the request's Origin
28
+ * against the allowlist. If the origin isn't allowed, the response still returns
29
+ * 204 but without the `Access-Control-Allow-*` headers — which is what the browser
30
+ * needs to see in order to block the subsequent real request.
31
+ */
32
+ declare function corsPreflight(req: Request, allowed: ReadonlySet<string>, opts?: CorsOptions): Response;
33
+
34
+ export { type CorsOptions, corsHeaders, corsPreflight };
@@ -0,0 +1,26 @@
1
+ // src/api/index.ts
2
+ var DEFAULT_METHODS = "POST, OPTIONS";
3
+ var DEFAULT_HEADERS = "Content-Type";
4
+ var DEFAULT_MAX_AGE = 86400;
5
+ function corsHeaders(origin, allowed, opts = {}) {
6
+ const headers = { Vary: "Origin" };
7
+ if (origin && allowed.has(origin)) {
8
+ headers["Access-Control-Allow-Origin"] = origin;
9
+ headers["Access-Control-Allow-Methods"] = opts.methods ?? DEFAULT_METHODS;
10
+ headers["Access-Control-Allow-Headers"] = opts.headers ?? DEFAULT_HEADERS;
11
+ headers["Access-Control-Max-Age"] = String(opts.maxAge ?? DEFAULT_MAX_AGE);
12
+ }
13
+ return headers;
14
+ }
15
+ function corsPreflight(req, allowed, opts) {
16
+ const origin = req.headers.get("origin");
17
+ return new Response(null, {
18
+ status: 204,
19
+ headers: corsHeaders(origin, allowed, opts)
20
+ });
21
+ }
22
+ export {
23
+ corsHeaders,
24
+ corsPreflight
25
+ };
26
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/api/index.ts"],"sourcesContent":["// CORS primitives for cross-origin API routes between PLAYBACK / PLAYHUB / future\n// PLAYBACK-suite apps.\n//\n// The mechanism (header construction, preflight response) lives here. The policy\n// — i.e. which origins are allowed to call a given endpoint — is intentionally\n// per-route, since \"who can hit my newsletter endpoint\" is a different decision\n// from \"who can hit my admin endpoint\".\n\nexport type CorsOptions = {\n /** Comma-separated method list. Default: 'POST, OPTIONS'. */\n methods?: string\n /** Comma-separated header allowlist. Default: 'Content-Type'. */\n headers?: string\n /** Preflight cache duration in seconds. Default: 86400 (24h). */\n maxAge?: number\n}\n\nconst DEFAULT_METHODS = 'POST, OPTIONS'\nconst DEFAULT_HEADERS = 'Content-Type'\nconst DEFAULT_MAX_AGE = 86400\n\n/**\n * Build CORS response headers.\n *\n * Always returns `Vary: Origin` so caches don't serve a CORS-allowed response\n * to a different origin. Only emits `Access-Control-*` headers when the request\n * Origin is in the allowlist — exact-match, no wildcards, so a typo'd subdomain\n * or rogue preview URL can't post into a production endpoint.\n *\n * Same-origin requests have no Origin header on POST in some browsers and never\n * preflight, so an absent or unknown origin returning the empty CORS set is the\n * correct behavior.\n */\nexport function corsHeaders(\n origin: string | null,\n allowed: ReadonlySet<string>,\n opts: CorsOptions = {}\n): Record<string, string> {\n const headers: Record<string, string> = { Vary: 'Origin' }\n if (origin && allowed.has(origin)) {\n headers['Access-Control-Allow-Origin'] = origin\n headers['Access-Control-Allow-Methods'] = opts.methods ?? DEFAULT_METHODS\n headers['Access-Control-Allow-Headers'] = opts.headers ?? DEFAULT_HEADERS\n headers['Access-Control-Max-Age'] = String(opts.maxAge ?? DEFAULT_MAX_AGE)\n }\n return headers\n}\n\n/**\n * Build a preflight (OPTIONS) Response. Use directly as the route's OPTIONS export:\n *\n * export const OPTIONS = (req: NextRequest) => corsPreflight(req, ALLOWED_ORIGINS)\n *\n * Returns 204 No Content with the CORS headers built from the request's Origin\n * against the allowlist. If the origin isn't allowed, the response still returns\n * 204 but without the `Access-Control-Allow-*` headers — which is what the browser\n * needs to see in order to block the subsequent real request.\n */\nexport function corsPreflight(\n req: Request,\n allowed: ReadonlySet<string>,\n opts?: CorsOptions\n): Response {\n const origin = req.headers.get('origin')\n return new Response(null, {\n status: 204,\n headers: corsHeaders(origin, allowed, opts),\n })\n}\n"],"mappings":";AAiBA,IAAM,kBAAkB;AACxB,IAAM,kBAAkB;AACxB,IAAM,kBAAkB;AAcjB,SAAS,YACd,QACA,SACA,OAAoB,CAAC,GACG;AACxB,QAAM,UAAkC,EAAE,MAAM,SAAS;AACzD,MAAI,UAAU,QAAQ,IAAI,MAAM,GAAG;AACjC,YAAQ,6BAA6B,IAAI;AACzC,YAAQ,8BAA8B,IAAI,KAAK,WAAW;AAC1D,YAAQ,8BAA8B,IAAI,KAAK,WAAW;AAC1D,YAAQ,wBAAwB,IAAI,OAAO,KAAK,UAAU,eAAe;AAAA,EAC3E;AACA,SAAO;AACT;AAYO,SAAS,cACd,KACA,SACA,MACU;AACV,QAAM,SAAS,IAAI,QAAQ,IAAI,QAAQ;AACvC,SAAO,IAAI,SAAS,MAAM;AAAA,IACxB,QAAQ;AAAA,IACR,SAAS,YAAY,QAAQ,SAAS,IAAI;AAAA,EAC5C,CAAC;AACH;","names":[]}
package/dist/ui/index.js CHANGED
@@ -2744,7 +2744,7 @@ function NewsletterForm2({
2744
2744
  const trimmed = email.trim();
2745
2745
  const ok = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(trimmed);
2746
2746
  if (!ok) {
2747
- setState("error");
2747
+ setState("invalid_format");
2748
2748
  return;
2749
2749
  }
2750
2750
  inFlight.current = true;
@@ -2765,11 +2765,13 @@ function NewsletterForm2({
2765
2765
  setEmail("");
2766
2766
  } else if (res.status === 429) {
2767
2767
  setState("rate_limited");
2768
+ } else if (res.status === 400) {
2769
+ setState("invalid_format");
2768
2770
  } else {
2769
- setState("error");
2771
+ setState("server_error");
2770
2772
  }
2771
2773
  } catch {
2772
- setState("error");
2774
+ setState("server_error");
2773
2775
  } finally {
2774
2776
  inFlight.current = false;
2775
2777
  }
@@ -2820,16 +2822,17 @@ function NewsletterForm2({
2820
2822
  value: email,
2821
2823
  onChange: (e) => {
2822
2824
  setEmail(e.target.value);
2823
- if (state === "error" || state === "rate_limited") setState("idle");
2825
+ if (state === "invalid_format" || state === "server_error" || state === "rate_limited")
2826
+ setState("idle");
2824
2827
  },
2825
2828
  placeholder,
2826
- "aria-invalid": state === "error",
2829
+ "aria-invalid": state === "invalid_format",
2827
2830
  "aria-describedby": feedbackId,
2828
2831
  className: cn(
2829
2832
  "w-full sm:flex-1 h-12 sm:h-11 rounded-full bg-[var(--surface-1,#0f1512)] border px-5 text-[16px] sm:text-[14px] text-[var(--timberwolf)] placeholder:text-[rgba(214,213,201,0.44)]",
2830
2833
  "focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--timberwolf)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--night)]",
2831
2834
  "disabled:opacity-60",
2832
- state === "error" ? "border-[rgba(237,106,106,0.5)]" : "border-[rgba(214,213,201,0.08)]"
2835
+ state === "invalid_format" || state === "server_error" ? "border-[rgba(237,106,106,0.5)]" : "border-[rgba(214,213,201,0.08)]"
2833
2836
  )
2834
2837
  }
2835
2838
  ),
@@ -2851,13 +2854,14 @@ function NewsletterForm2({
2851
2854
  className: cn(
2852
2855
  "mt-2 -ml-0.5 text-[13px] min-h-[1.25rem]",
2853
2856
  state === "sent" && "text-[var(--timberwolf)]",
2854
- (state === "error" || state === "rate_limited") && "text-[rgb(237,106,106)]",
2857
+ (state === "invalid_format" || state === "server_error" || state === "rate_limited") && "text-[rgb(237,106,106)]",
2855
2858
  (state === "idle" || state === "sending") && "text-[rgba(214,213,201,0.44)]"
2856
2859
  ),
2857
- role: state === "error" || state === "rate_limited" ? "alert" : void 0,
2860
+ role: state === "invalid_format" || state === "server_error" || state === "rate_limited" ? "alert" : void 0,
2858
2861
  children: [
2859
2862
  state === "sent" && "Thanks \u2014 we\u2019ll be in touch.",
2860
- state === "error" && "Please enter a valid email address.",
2863
+ state === "invalid_format" && "Please enter a valid email address.",
2864
+ state === "server_error" && "Could not subscribe right now. Please try again in a moment.",
2861
2865
  state === "rate_limited" && "Too many attempts. Try again in a minute."
2862
2866
  ]
2863
2867
  }
@@ -2969,45 +2973,6 @@ import {
2969
2973
  Mail
2970
2974
  } from "lucide-react";
2971
2975
 
2972
- // src/auth/context.tsx
2973
- import {
2974
- createContext as createContext2,
2975
- useContext as useContext2,
2976
- useEffect as useEffect6,
2977
- useState as useState14,
2978
- useMemo as useMemo2,
2979
- useCallback as useCallback2,
2980
- useRef as useRef6
2981
- } from "react";
2982
- import { useRouter } from "next/navigation";
2983
-
2984
- // src/supabase/client.ts
2985
- import { createBrowserClient } from "@supabase/ssr";
2986
- function createClient() {
2987
- const cookieDomain = process.env.NEXT_PUBLIC_COOKIE_DOMAIN;
2988
- return createBrowserClient(
2989
- process.env.NEXT_PUBLIC_SUPABASE_URL,
2990
- process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
2991
- {
2992
- ...cookieDomain && {
2993
- cookieOptions: { domain: cookieDomain }
2994
- }
2995
- }
2996
- );
2997
- }
2998
-
2999
- // src/auth/context.tsx
3000
- import { jsx as jsx39 } from "react/jsx-runtime";
3001
- var AuthContext = createContext2(void 0);
3002
- var CACHE_TIMEOUT = 5 * 60 * 1e3;
3003
- function useAuth() {
3004
- const context = useContext2(AuthContext);
3005
- if (context === void 0) {
3006
- throw new Error("useAuth must be used within an AuthProvider");
3007
- }
3008
- return context;
3009
- }
3010
-
3011
2976
  // src/auth/shared.ts
3012
2977
  function validateEmail(email) {
3013
2978
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
@@ -3048,8 +3013,23 @@ function getAuthErrorMessage(error) {
3048
3013
  return errorMappings[message] || message;
3049
3014
  }
3050
3015
 
3016
+ // src/supabase/client.ts
3017
+ import { createBrowserClient } from "@supabase/ssr";
3018
+ function createClient() {
3019
+ const cookieDomain = process.env.NEXT_PUBLIC_COOKIE_DOMAIN;
3020
+ return createBrowserClient(
3021
+ process.env.NEXT_PUBLIC_SUPABASE_URL,
3022
+ process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
3023
+ {
3024
+ ...cookieDomain && {
3025
+ cookieOptions: { domain: cookieDomain }
3026
+ }
3027
+ }
3028
+ );
3029
+ }
3030
+
3051
3031
  // src/ui/sign-in-form.tsx
3052
- import { Fragment as Fragment6, jsx as jsx40, jsxs as jsxs23 } from "react/jsx-runtime";
3032
+ import { Fragment as Fragment6, jsx as jsx39, jsxs as jsxs23 } from "react/jsx-runtime";
3053
3033
  var COOLDOWN_THRESHOLD = 3;
3054
3034
  var COOLDOWN_DURATION_MS = 3e4;
3055
3035
  function SignInForm({
@@ -3068,7 +3048,7 @@ function SignInForm({
3068
3048
  const [loading, setLoading] = React28.useState(false);
3069
3049
  const [consecutiveErrors, setConsecutiveErrors] = React28.useState(0);
3070
3050
  const [cooldownUntil, setCooldownUntil] = React28.useState(null);
3071
- const { signIn } = useAuth();
3051
+ const [supabase] = React28.useState(() => createClient());
3072
3052
  React28.useEffect(() => {
3073
3053
  if (initialError) setError(initialError);
3074
3054
  }, [initialError]);
@@ -3090,7 +3070,10 @@ function SignInForm({
3090
3070
  }
3091
3071
  setLoading(true);
3092
3072
  try {
3093
- const { error: signInError } = await signIn(email, password);
3073
+ const { error: signInError } = await supabase.auth.signInWithPassword({
3074
+ email,
3075
+ password
3076
+ });
3094
3077
  if (signInError) {
3095
3078
  setError(getAuthErrorMessage(signInError));
3096
3079
  const newCount = consecutiveErrors + 1;
@@ -3112,11 +3095,11 @@ function SignInForm({
3112
3095
  };
3113
3096
  return /* @__PURE__ */ jsxs23("div", { className: "bg-card border border-border rounded-xl p-8 space-y-6", children: [
3114
3097
  /* @__PURE__ */ jsxs23("div", { className: "text-center", children: [
3115
- /* @__PURE__ */ jsx40("h1", { className: "text-2xl font-bold text-[var(--timberwolf)] mb-2", children: title }),
3116
- /* @__PURE__ */ jsx40("p", { className: "text-sm text-muted-foreground", children: subtitle })
3098
+ /* @__PURE__ */ jsx39("h1", { className: "text-2xl font-bold text-[var(--timberwolf)] mb-2", children: title }),
3099
+ /* @__PURE__ */ jsx39("p", { className: "text-sm text-muted-foreground", children: subtitle })
3117
3100
  ] }),
3118
3101
  /* @__PURE__ */ jsxs23("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
3119
- error && /* @__PURE__ */ jsx40(
3102
+ error && /* @__PURE__ */ jsx39(
3120
3103
  "div",
3121
3104
  {
3122
3105
  id: "signin-error",
@@ -3124,15 +3107,15 @@ function SignInForm({
3124
3107
  "aria-live": "polite",
3125
3108
  className: "bg-red-900/20 border border-red-700/30 rounded-lg p-3",
3126
3109
  children: /* @__PURE__ */ jsxs23("div", { className: "flex items-center gap-2 text-red-400 text-sm", children: [
3127
- /* @__PURE__ */ jsx40(AlertCircle, { className: "h-4 w-4 flex-shrink-0" }),
3128
- /* @__PURE__ */ jsx40("span", { children: error })
3110
+ /* @__PURE__ */ jsx39(AlertCircle, { className: "h-4 w-4 flex-shrink-0" }),
3111
+ /* @__PURE__ */ jsx39("span", { children: error })
3129
3112
  ] })
3130
3113
  }
3131
3114
  ),
3132
3115
  /* @__PURE__ */ jsxs23("div", { className: "space-y-2", children: [
3133
- /* @__PURE__ */ jsx40(Label, { htmlFor: "email", className: "text-[var(--timberwolf)]", children: "Email" }),
3116
+ /* @__PURE__ */ jsx39(Label, { htmlFor: "email", className: "text-[var(--timberwolf)]", children: "Email" }),
3134
3117
  /* @__PURE__ */ jsxs23("div", { className: "relative", children: [
3135
- /* @__PURE__ */ jsx40(
3118
+ /* @__PURE__ */ jsx39(
3136
3119
  Input,
3137
3120
  {
3138
3121
  id: "email",
@@ -3147,13 +3130,13 @@ function SignInForm({
3147
3130
  "aria-describedby": error ? "signin-error" : void 0
3148
3131
  }
3149
3132
  ),
3150
- /* @__PURE__ */ jsx40(Mail, { className: "h-4 w-4 absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground" })
3133
+ /* @__PURE__ */ jsx39(Mail, { className: "h-4 w-4 absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground" })
3151
3134
  ] })
3152
3135
  ] }),
3153
3136
  /* @__PURE__ */ jsxs23("div", { className: "space-y-2", children: [
3154
- /* @__PURE__ */ jsx40(Label, { htmlFor: "password", className: "text-[var(--timberwolf)]", children: "Password" }),
3137
+ /* @__PURE__ */ jsx39(Label, { htmlFor: "password", className: "text-[var(--timberwolf)]", children: "Password" }),
3155
3138
  /* @__PURE__ */ jsxs23("div", { className: "relative", children: [
3156
- /* @__PURE__ */ jsx40(
3139
+ /* @__PURE__ */ jsx39(
3157
3140
  Input,
3158
3141
  {
3159
3142
  id: "password",
@@ -3168,8 +3151,8 @@ function SignInForm({
3168
3151
  "aria-describedby": error ? "signin-error" : void 0
3169
3152
  }
3170
3153
  ),
3171
- /* @__PURE__ */ jsx40(Lock, { className: "h-4 w-4 absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground" }),
3172
- /* @__PURE__ */ jsx40(
3154
+ /* @__PURE__ */ jsx39(Lock, { className: "h-4 w-4 absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground" }),
3155
+ /* @__PURE__ */ jsx39(
3173
3156
  "button",
3174
3157
  {
3175
3158
  type: "button",
@@ -3178,12 +3161,12 @@ function SignInForm({
3178
3161
  disabled: loading,
3179
3162
  "aria-label": showPassword ? "Hide password" : "Show password",
3180
3163
  "aria-pressed": showPassword,
3181
- children: showPassword ? /* @__PURE__ */ jsx40(EyeOff, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx40(Eye, { className: "h-4 w-4" })
3164
+ children: showPassword ? /* @__PURE__ */ jsx39(EyeOff, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx39(Eye, { className: "h-4 w-4" })
3182
3165
  }
3183
3166
  )
3184
3167
  ] })
3185
3168
  ] }),
3186
- forgotPasswordHref ? /* @__PURE__ */ jsx40("div", { className: "flex justify-end -mt-1", children: /* @__PURE__ */ jsx40(
3169
+ forgotPasswordHref ? /* @__PURE__ */ jsx39("div", { className: "flex justify-end -mt-1", children: /* @__PURE__ */ jsx39(
3187
3170
  Link4,
3188
3171
  {
3189
3172
  href: forgotPasswordHref,
@@ -3191,23 +3174,23 @@ function SignInForm({
3191
3174
  children: "Forgot your password?"
3192
3175
  }
3193
3176
  ) }) : null,
3194
- /* @__PURE__ */ jsx40(
3177
+ /* @__PURE__ */ jsx39(
3195
3178
  Button,
3196
3179
  {
3197
3180
  type: "submit",
3198
3181
  className: "w-full h-11 bg-[var(--timberwolf)] text-[var(--night)] hover:bg-[var(--ash-grey)]",
3199
3182
  disabled: loading,
3200
3183
  children: loading ? /* @__PURE__ */ jsxs23(Fragment6, { children: [
3201
- /* @__PURE__ */ jsx40(Loader2, { className: "h-4 w-4 mr-2 animate-spin" }),
3184
+ /* @__PURE__ */ jsx39(Loader2, { className: "h-4 w-4 mr-2 animate-spin" }),
3202
3185
  "Signing in..."
3203
3186
  ] }) : "Sign in"
3204
3187
  }
3205
3188
  )
3206
3189
  ] }),
3207
- signUpHref ? /* @__PURE__ */ jsx40("div", { className: "text-center text-sm", children: /* @__PURE__ */ jsxs23("p", { className: "text-muted-foreground", children: [
3190
+ signUpHref ? /* @__PURE__ */ jsx39("div", { className: "text-center text-sm", children: /* @__PURE__ */ jsxs23("p", { className: "text-muted-foreground", children: [
3208
3191
  "Don't have an account?",
3209
3192
  " ",
3210
- /* @__PURE__ */ jsx40(
3193
+ /* @__PURE__ */ jsx39(
3211
3194
  Link4,
3212
3195
  {
3213
3196
  href: signUpHref,
@@ -3229,7 +3212,7 @@ import {
3229
3212
  Loader2 as Loader22,
3230
3213
  Mail as Mail2
3231
3214
  } from "lucide-react";
3232
- import { Fragment as Fragment7, jsx as jsx41, jsxs as jsxs24 } from "react/jsx-runtime";
3215
+ import { Fragment as Fragment7, jsx as jsx40, jsxs as jsxs24 } from "react/jsx-runtime";
3233
3216
  var RESEND_COOLDOWN_MS = 3e4;
3234
3217
  var COOLDOWN_STORAGE_KEY = "playback:pwreset-cooldown-until";
3235
3218
  function ForgotPasswordForm({
@@ -3308,18 +3291,18 @@ function ForgotPasswordForm({
3308
3291
  };
3309
3292
  if (emailSent) {
3310
3293
  return /* @__PURE__ */ jsxs24("div", { className: "bg-card border border-border rounded-xl p-8 space-y-6 text-center", children: [
3311
- /* @__PURE__ */ jsx41("div", { className: "flex justify-center", children: /* @__PURE__ */ jsx41("div", { className: "h-12 w-12 rounded-full bg-[var(--timberwolf)]/10 border border-[var(--timberwolf)]/20 flex items-center justify-center", children: /* @__PURE__ */ jsx41(CheckCircle, { className: "h-6 w-6 text-[var(--timberwolf)]" }) }) }),
3294
+ /* @__PURE__ */ jsx40("div", { className: "flex justify-center", children: /* @__PURE__ */ jsx40("div", { className: "h-12 w-12 rounded-full bg-[var(--timberwolf)]/10 border border-[var(--timberwolf)]/20 flex items-center justify-center", children: /* @__PURE__ */ jsx40(CheckCircle, { className: "h-6 w-6 text-[var(--timberwolf)]" }) }) }),
3312
3295
  /* @__PURE__ */ jsxs24("div", { className: "space-y-2", children: [
3313
- /* @__PURE__ */ jsx41("h1", { className: "text-2xl font-bold text-[var(--timberwolf)]", children: "Check your email" }),
3296
+ /* @__PURE__ */ jsx40("h1", { className: "text-2xl font-bold text-[var(--timberwolf)]", children: "Check your email" }),
3314
3297
  /* @__PURE__ */ jsxs24("p", { className: "text-sm text-muted-foreground", children: [
3315
3298
  "We sent a reset link to",
3316
3299
  " ",
3317
- /* @__PURE__ */ jsx41("span", { className: "text-[var(--timberwolf)] font-medium", children: email }),
3300
+ /* @__PURE__ */ jsx40("span", { className: "text-[var(--timberwolf)] font-medium", children: email }),
3318
3301
  ". If it's not in your inbox, check spam."
3319
3302
  ] })
3320
3303
  ] }),
3321
3304
  /* @__PURE__ */ jsxs24("div", { className: "space-y-3 pt-2", children: [
3322
- /* @__PURE__ */ jsx41(
3305
+ /* @__PURE__ */ jsx40(
3323
3306
  Button,
3324
3307
  {
3325
3308
  onClick: () => {
@@ -3333,8 +3316,8 @@ function ForgotPasswordForm({
3333
3316
  children: cooldownSecondsLeft > 0 ? `Send another email in ${cooldownSecondsLeft}s` : "Send another email"
3334
3317
  }
3335
3318
  ),
3336
- /* @__PURE__ */ jsx41(Link5, { href: loginHref, className: "block", children: /* @__PURE__ */ jsxs24(Button, { className: "w-full h-11 bg-[var(--timberwolf)] text-[var(--night)] hover:bg-[var(--ash-grey)]", children: [
3337
- /* @__PURE__ */ jsx41(ArrowLeft, { className: "h-4 w-4 mr-2" }),
3319
+ /* @__PURE__ */ jsx40(Link5, { href: loginHref, className: "block", children: /* @__PURE__ */ jsxs24(Button, { className: "w-full h-11 bg-[var(--timberwolf)] text-[var(--night)] hover:bg-[var(--ash-grey)]", children: [
3320
+ /* @__PURE__ */ jsx40(ArrowLeft, { className: "h-4 w-4 mr-2" }),
3338
3321
  "Back to sign in"
3339
3322
  ] }) })
3340
3323
  ] })
@@ -3342,11 +3325,11 @@ function ForgotPasswordForm({
3342
3325
  }
3343
3326
  return /* @__PURE__ */ jsxs24("div", { className: "bg-card border border-border rounded-xl p-8 space-y-6", children: [
3344
3327
  /* @__PURE__ */ jsxs24("div", { className: "text-center", children: [
3345
- /* @__PURE__ */ jsx41("h1", { className: "text-2xl font-bold text-[var(--timberwolf)] mb-2", children: title }),
3346
- /* @__PURE__ */ jsx41("p", { className: "text-sm text-muted-foreground", children: subtitle })
3328
+ /* @__PURE__ */ jsx40("h1", { className: "text-2xl font-bold text-[var(--timberwolf)] mb-2", children: title }),
3329
+ /* @__PURE__ */ jsx40("p", { className: "text-sm text-muted-foreground", children: subtitle })
3347
3330
  ] }),
3348
3331
  /* @__PURE__ */ jsxs24("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
3349
- error && /* @__PURE__ */ jsx41(
3332
+ error && /* @__PURE__ */ jsx40(
3350
3333
  "div",
3351
3334
  {
3352
3335
  id: "forgot-error",
@@ -3354,15 +3337,15 @@ function ForgotPasswordForm({
3354
3337
  "aria-live": "polite",
3355
3338
  className: "bg-red-900/20 border border-red-700/30 rounded-lg p-3",
3356
3339
  children: /* @__PURE__ */ jsxs24("div", { className: "flex items-center gap-2 text-red-400 text-sm", children: [
3357
- /* @__PURE__ */ jsx41(AlertCircle2, { className: "h-4 w-4 flex-shrink-0" }),
3358
- /* @__PURE__ */ jsx41("span", { children: error })
3340
+ /* @__PURE__ */ jsx40(AlertCircle2, { className: "h-4 w-4 flex-shrink-0" }),
3341
+ /* @__PURE__ */ jsx40("span", { children: error })
3359
3342
  ] })
3360
3343
  }
3361
3344
  ),
3362
3345
  /* @__PURE__ */ jsxs24("div", { className: "space-y-2", children: [
3363
- /* @__PURE__ */ jsx41(Label, { htmlFor: "email", className: "text-[var(--timberwolf)]", children: "Email" }),
3346
+ /* @__PURE__ */ jsx40(Label, { htmlFor: "email", className: "text-[var(--timberwolf)]", children: "Email" }),
3364
3347
  /* @__PURE__ */ jsxs24("div", { className: "relative", children: [
3365
- /* @__PURE__ */ jsx41(
3348
+ /* @__PURE__ */ jsx40(
3366
3349
  Input,
3367
3350
  {
3368
3351
  id: "email",
@@ -3378,29 +3361,29 @@ function ForgotPasswordForm({
3378
3361
  "aria-describedby": error ? "forgot-error" : void 0
3379
3362
  }
3380
3363
  ),
3381
- /* @__PURE__ */ jsx41(Mail2, { className: "h-4 w-4 absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground" })
3364
+ /* @__PURE__ */ jsx40(Mail2, { className: "h-4 w-4 absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground" })
3382
3365
  ] })
3383
3366
  ] }),
3384
- /* @__PURE__ */ jsx41(
3367
+ /* @__PURE__ */ jsx40(
3385
3368
  Button,
3386
3369
  {
3387
3370
  type: "submit",
3388
3371
  className: "w-full h-11 bg-[var(--timberwolf)] text-[var(--night)] hover:bg-[var(--ash-grey)]",
3389
3372
  disabled: loading || cooldownSecondsLeft > 0,
3390
3373
  children: loading ? /* @__PURE__ */ jsxs24(Fragment7, { children: [
3391
- /* @__PURE__ */ jsx41(Loader22, { className: "h-4 w-4 mr-2 animate-spin" }),
3374
+ /* @__PURE__ */ jsx40(Loader22, { className: "h-4 w-4 mr-2 animate-spin" }),
3392
3375
  "Sending email..."
3393
3376
  ] }) : cooldownSecondsLeft > 0 ? `Try again in ${cooldownSecondsLeft}s` : "Send reset link"
3394
3377
  }
3395
3378
  )
3396
3379
  ] }),
3397
- /* @__PURE__ */ jsx41("div", { className: "text-center text-sm", children: /* @__PURE__ */ jsxs24(
3380
+ /* @__PURE__ */ jsx40("div", { className: "text-center text-sm", children: /* @__PURE__ */ jsxs24(
3398
3381
  Link5,
3399
3382
  {
3400
3383
  href: loginHref,
3401
3384
  className: "inline-flex items-center gap-1.5 text-muted-foreground hover:text-[var(--timberwolf)] transition-colors",
3402
3385
  children: [
3403
- /* @__PURE__ */ jsx41(ArrowLeft, { className: "h-3.5 w-3.5" }),
3386
+ /* @__PURE__ */ jsx40(ArrowLeft, { className: "h-3.5 w-3.5" }),
3404
3387
  "Back to sign in"
3405
3388
  ]
3406
3389
  }
@@ -3411,7 +3394,7 @@ function ForgotPasswordForm({
3411
3394
  // src/ui/reset-password-form.tsx
3412
3395
  import * as React30 from "react";
3413
3396
  import Link6 from "next/link";
3414
- import { useRouter as useRouter2 } from "next/navigation";
3397
+ import { useRouter } from "next/navigation";
3415
3398
  import {
3416
3399
  AlertCircle as AlertCircle3,
3417
3400
  CheckCircle as CheckCircle2,
@@ -3420,7 +3403,7 @@ import {
3420
3403
  Loader2 as Loader23,
3421
3404
  Lock as Lock2
3422
3405
  } from "lucide-react";
3423
- import { Fragment as Fragment8, jsx as jsx42, jsxs as jsxs25 } from "react/jsx-runtime";
3406
+ import { Fragment as Fragment8, jsx as jsx41, jsxs as jsxs25 } from "react/jsx-runtime";
3424
3407
  function ResetPasswordForm({
3425
3408
  loginHref = "/auth/login",
3426
3409
  initialError,
@@ -3435,7 +3418,7 @@ function ResetPasswordForm({
3435
3418
  const [error, setError] = React30.useState(initialError ?? "");
3436
3419
  const [loading, setLoading] = React30.useState(false);
3437
3420
  const [passwordReset, setPasswordReset] = React30.useState(false);
3438
- const router = useRouter2();
3421
+ const router = useRouter();
3439
3422
  const [supabase] = React30.useState(() => createClient());
3440
3423
  React30.useEffect(() => {
3441
3424
  if (initialError) setError(initialError);
@@ -3489,21 +3472,21 @@ function ResetPasswordForm({
3489
3472
  };
3490
3473
  if (passwordReset) {
3491
3474
  return /* @__PURE__ */ jsxs25("div", { className: "bg-card border border-border rounded-xl p-8 space-y-6 text-center", children: [
3492
- /* @__PURE__ */ jsx42("div", { className: "flex justify-center", children: /* @__PURE__ */ jsx42("div", { className: "h-12 w-12 rounded-full bg-[var(--timberwolf)]/10 border border-[var(--timberwolf)]/20 flex items-center justify-center", children: /* @__PURE__ */ jsx42(CheckCircle2, { className: "h-6 w-6 text-[var(--timberwolf)]" }) }) }),
3475
+ /* @__PURE__ */ jsx41("div", { className: "flex justify-center", children: /* @__PURE__ */ jsx41("div", { className: "h-12 w-12 rounded-full bg-[var(--timberwolf)]/10 border border-[var(--timberwolf)]/20 flex items-center justify-center", children: /* @__PURE__ */ jsx41(CheckCircle2, { className: "h-6 w-6 text-[var(--timberwolf)]" }) }) }),
3493
3476
  /* @__PURE__ */ jsxs25("div", { className: "space-y-2", children: [
3494
- /* @__PURE__ */ jsx42("h1", { className: "text-2xl font-bold text-[var(--timberwolf)]", children: "Password updated" }),
3495
- /* @__PURE__ */ jsx42("p", { className: "text-sm text-muted-foreground", children: "Redirecting you to sign in\u2026" })
3477
+ /* @__PURE__ */ jsx41("h1", { className: "text-2xl font-bold text-[var(--timberwolf)]", children: "Password updated" }),
3478
+ /* @__PURE__ */ jsx41("p", { className: "text-sm text-muted-foreground", children: "Redirecting you to sign in\u2026" })
3496
3479
  ] }),
3497
- /* @__PURE__ */ jsx42(Link6, { href: loginHref, className: "block pt-2", children: /* @__PURE__ */ jsx42(Button, { className: "w-full h-11 bg-[var(--timberwolf)] text-[var(--night)] hover:bg-[var(--ash-grey)]", children: "Continue to sign in" }) })
3480
+ /* @__PURE__ */ jsx41(Link6, { href: loginHref, className: "block pt-2", children: /* @__PURE__ */ jsx41(Button, { className: "w-full h-11 bg-[var(--timberwolf)] text-[var(--night)] hover:bg-[var(--ash-grey)]", children: "Continue to sign in" }) })
3498
3481
  ] });
3499
3482
  }
3500
3483
  return /* @__PURE__ */ jsxs25("div", { className: "bg-card border border-border rounded-xl p-8 space-y-6", children: [
3501
3484
  /* @__PURE__ */ jsxs25("div", { className: "text-center", children: [
3502
- /* @__PURE__ */ jsx42("h1", { className: "text-2xl font-bold text-[var(--timberwolf)] mb-2", children: title }),
3503
- /* @__PURE__ */ jsx42("p", { className: "text-sm text-muted-foreground", children: subtitle })
3485
+ /* @__PURE__ */ jsx41("h1", { className: "text-2xl font-bold text-[var(--timberwolf)] mb-2", children: title }),
3486
+ /* @__PURE__ */ jsx41("p", { className: "text-sm text-muted-foreground", children: subtitle })
3504
3487
  ] }),
3505
3488
  /* @__PURE__ */ jsxs25("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
3506
- error && /* @__PURE__ */ jsx42(
3489
+ error && /* @__PURE__ */ jsx41(
3507
3490
  "div",
3508
3491
  {
3509
3492
  id: "reset-error",
@@ -3511,15 +3494,15 @@ function ResetPasswordForm({
3511
3494
  "aria-live": "polite",
3512
3495
  className: "bg-red-900/20 border border-red-700/30 rounded-lg p-3",
3513
3496
  children: /* @__PURE__ */ jsxs25("div", { className: "flex items-center gap-2 text-red-400 text-sm", children: [
3514
- /* @__PURE__ */ jsx42(AlertCircle3, { className: "h-4 w-4 flex-shrink-0" }),
3515
- /* @__PURE__ */ jsx42("span", { children: error })
3497
+ /* @__PURE__ */ jsx41(AlertCircle3, { className: "h-4 w-4 flex-shrink-0" }),
3498
+ /* @__PURE__ */ jsx41("span", { children: error })
3516
3499
  ] })
3517
3500
  }
3518
3501
  ),
3519
3502
  /* @__PURE__ */ jsxs25("div", { className: "space-y-2", children: [
3520
- /* @__PURE__ */ jsx42(Label, { htmlFor: "password", className: "text-[var(--timberwolf)]", children: "New password" }),
3503
+ /* @__PURE__ */ jsx41(Label, { htmlFor: "password", className: "text-[var(--timberwolf)]", children: "New password" }),
3521
3504
  /* @__PURE__ */ jsxs25("div", { className: "relative", children: [
3522
- /* @__PURE__ */ jsx42(
3505
+ /* @__PURE__ */ jsx41(
3523
3506
  Input,
3524
3507
  {
3525
3508
  id: "password",
@@ -3535,8 +3518,8 @@ function ResetPasswordForm({
3535
3518
  "aria-describedby": error ? "reset-error" : void 0
3536
3519
  }
3537
3520
  ),
3538
- /* @__PURE__ */ jsx42(Lock2, { className: "h-4 w-4 absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground" }),
3539
- /* @__PURE__ */ jsx42(
3521
+ /* @__PURE__ */ jsx41(Lock2, { className: "h-4 w-4 absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground" }),
3522
+ /* @__PURE__ */ jsx41(
3540
3523
  "button",
3541
3524
  {
3542
3525
  type: "button",
@@ -3545,13 +3528,13 @@ function ResetPasswordForm({
3545
3528
  disabled: loading,
3546
3529
  "aria-label": showPassword ? "Hide password" : "Show password",
3547
3530
  "aria-pressed": showPassword,
3548
- children: showPassword ? /* @__PURE__ */ jsx42(EyeOff2, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx42(Eye2, { className: "h-4 w-4" })
3531
+ children: showPassword ? /* @__PURE__ */ jsx41(EyeOff2, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx41(Eye2, { className: "h-4 w-4" })
3549
3532
  }
3550
3533
  )
3551
3534
  ] })
3552
3535
  ] }),
3553
3536
  /* @__PURE__ */ jsxs25("div", { className: "space-y-2", children: [
3554
- /* @__PURE__ */ jsx42(
3537
+ /* @__PURE__ */ jsx41(
3555
3538
  Label,
3556
3539
  {
3557
3540
  htmlFor: "confirmPassword",
@@ -3560,7 +3543,7 @@ function ResetPasswordForm({
3560
3543
  }
3561
3544
  ),
3562
3545
  /* @__PURE__ */ jsxs25("div", { className: "relative", children: [
3563
- /* @__PURE__ */ jsx42(
3546
+ /* @__PURE__ */ jsx41(
3564
3547
  Input,
3565
3548
  {
3566
3549
  id: "confirmPassword",
@@ -3575,8 +3558,8 @@ function ResetPasswordForm({
3575
3558
  "aria-describedby": error ? "reset-error" : void 0
3576
3559
  }
3577
3560
  ),
3578
- /* @__PURE__ */ jsx42(Lock2, { className: "h-4 w-4 absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground" }),
3579
- /* @__PURE__ */ jsx42(
3561
+ /* @__PURE__ */ jsx41(Lock2, { className: "h-4 w-4 absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground" }),
3562
+ /* @__PURE__ */ jsx41(
3580
3563
  "button",
3581
3564
  {
3582
3565
  type: "button",
@@ -3585,26 +3568,26 @@ function ResetPasswordForm({
3585
3568
  disabled: loading,
3586
3569
  "aria-label": showConfirmPassword ? "Hide password" : "Show password",
3587
3570
  "aria-pressed": showConfirmPassword,
3588
- children: showConfirmPassword ? /* @__PURE__ */ jsx42(EyeOff2, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx42(Eye2, { className: "h-4 w-4" })
3571
+ children: showConfirmPassword ? /* @__PURE__ */ jsx41(EyeOff2, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx41(Eye2, { className: "h-4 w-4" })
3589
3572
  }
3590
3573
  )
3591
3574
  ] })
3592
3575
  ] }),
3593
- /* @__PURE__ */ jsx42("p", { className: "text-xs text-muted-foreground", children: "At least 8 characters with an uppercase letter, a lowercase letter, and a number." }),
3594
- /* @__PURE__ */ jsx42(
3576
+ /* @__PURE__ */ jsx41("p", { className: "text-xs text-muted-foreground", children: "At least 8 characters with an uppercase letter, a lowercase letter, and a number." }),
3577
+ /* @__PURE__ */ jsx41(
3595
3578
  Button,
3596
3579
  {
3597
3580
  type: "submit",
3598
3581
  className: "w-full h-11 bg-[var(--timberwolf)] text-[var(--night)] hover:bg-[var(--ash-grey)]",
3599
3582
  disabled: loading,
3600
3583
  children: loading ? /* @__PURE__ */ jsxs25(Fragment8, { children: [
3601
- /* @__PURE__ */ jsx42(Loader23, { className: "h-4 w-4 mr-2 animate-spin" }),
3584
+ /* @__PURE__ */ jsx41(Loader23, { className: "h-4 w-4 mr-2 animate-spin" }),
3602
3585
  "Updating password..."
3603
3586
  ] }) : "Update password"
3604
3587
  }
3605
3588
  )
3606
3589
  ] }),
3607
- /* @__PURE__ */ jsx42("div", { className: "text-center text-sm", children: /* @__PURE__ */ jsx42(
3590
+ /* @__PURE__ */ jsx41("div", { className: "text-center text-sm", children: /* @__PURE__ */ jsx41(
3608
3591
  Link6,
3609
3592
  {
3610
3593
  href: loginHref,