@vaultix.ai/react 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -21,10 +21,15 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  OrganizationSwitcher: () => OrganizationSwitcher,
24
+ RedirectToSignIn: () => RedirectToSignIn,
25
+ RedirectToSignUp: () => RedirectToSignUp,
24
26
  SignIn: () => SignIn,
25
27
  SignUp: () => SignUp,
28
+ SignedIn: () => SignedIn,
29
+ SignedOut: () => SignedOut,
26
30
  UserButton: () => UserButton,
27
31
  VaultixProvider: () => VaultixProvider,
32
+ useAuth: () => useAuth,
28
33
  useOrganization: () => useOrganization,
29
34
  useSession: () => useSession,
30
35
  useUser: () => useUser,
@@ -169,6 +174,7 @@ function VaultixProvider({
169
174
  }
170
175
 
171
176
  // src/hooks/index.ts
177
+ var import_react2 = require("react");
172
178
  function useVaultix() {
173
179
  return useVaultixContext();
174
180
  }
@@ -184,10 +190,26 @@ function useOrganization() {
184
190
  const { organization, isLoaded } = useVaultixContext();
185
191
  return { organization, isLoaded };
186
192
  }
193
+ function useAuth() {
194
+ const { user, session, isLoaded, isSignedIn, signOut } = useVaultixContext();
195
+ const getToken = (0, import_react2.useCallback)(async () => {
196
+ if (!isSignedIn) return null;
197
+ return null;
198
+ }, [isSignedIn]);
199
+ return {
200
+ isLoaded,
201
+ isSignedIn,
202
+ userId: user?.id ?? null,
203
+ sessionId: session?.id ?? null,
204
+ orgId: session?.orgId ?? null,
205
+ signOut,
206
+ getToken
207
+ };
208
+ }
187
209
 
188
210
  // src/components/SignIn.tsx
189
211
  var import_clsx = require("clsx");
190
- var import_react2 = require("react");
212
+ var import_react3 = require("react");
191
213
  var import_jsx_runtime2 = require("react/jsx-runtime");
192
214
  function toBase64url(buf) {
193
215
  return btoa(String.fromCharCode(...new Uint8Array(buf))).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
@@ -200,6 +222,18 @@ function resolveApiOrigin2(prop) {
200
222
  }
201
223
  return "";
202
224
  }
225
+ function resolveAfterSignInUrl(redirectUrlProp) {
226
+ if (redirectUrlProp) return redirectUrlProp;
227
+ if (typeof document !== "undefined") {
228
+ const el = document.querySelector("[data-vaultix-after-sign-in]");
229
+ const attr = el?.getAttribute("data-vaultix-after-sign-in");
230
+ if (attr) return attr;
231
+ const params = new URLSearchParams(window.location.search);
232
+ const redirectUrl = params.get("redirect_url");
233
+ if (redirectUrl) return redirectUrl;
234
+ }
235
+ return "/";
236
+ }
203
237
  function SignIn({
204
238
  redirectUrl,
205
239
  onSuccess,
@@ -208,17 +242,32 @@ function SignIn({
208
242
  className
209
243
  }) {
210
244
  const apiOrigin = resolveApiOrigin2(apiOriginProp);
211
- const [step, setStep] = (0, import_react2.useState)("email");
212
- const [email, setEmail] = (0, import_react2.useState)("");
213
- const [password, setPassword] = (0, import_react2.useState)("");
214
- const [totp, setTotp] = (0, import_react2.useState)("");
215
- const [error, setError] = (0, import_react2.useState)(null);
216
- const [loading, setLoading] = (0, import_react2.useState)(false);
217
- const challengeIdRef = (0, import_react2.useRef)(null);
245
+ const [step, setStep] = (0, import_react3.useState)("email");
246
+ const [email, setEmail] = (0, import_react3.useState)("");
247
+ const [password, setPassword] = (0, import_react3.useState)("");
248
+ const [totp, setTotp] = (0, import_react3.useState)("");
249
+ const [forgotCode, setForgotCode] = (0, import_react3.useState)("");
250
+ const [newPassword, setNewPassword] = (0, import_react3.useState)("");
251
+ const [error, setError] = (0, import_react3.useState)(null);
252
+ const [loading, setLoading] = (0, import_react3.useState)(false);
253
+ const [info, setInfo] = (0, import_react3.useState)(null);
254
+ const challengeIdRef = (0, import_react3.useRef)(null);
218
255
  function setErr(msg) {
219
256
  setError(msg);
220
257
  onError?.(msg);
221
258
  }
259
+ function handleSuccess(handshakeToken) {
260
+ onSuccess?.(handshakeToken);
261
+ const target = resolveAfterSignInUrl(redirectUrl);
262
+ const url = new URL(target, window.location.origin);
263
+ url.searchParams.set("__vaultix_handshake", handshakeToken);
264
+ window.location.href = url.toString();
265
+ }
266
+ function handleGoogleSignIn() {
267
+ const target = resolveAfterSignInUrl(redirectUrl);
268
+ const params = new URLSearchParams({ redirect_url: target });
269
+ window.location.href = `${apiOrigin}/v1/auth/oauth/google?${params}`;
270
+ }
222
271
  async function handleEmailSubmit(e) {
223
272
  e.preventDefault();
224
273
  setError(null);
@@ -263,7 +312,7 @@ function SignIn({
263
312
  setStep("totp");
264
313
  return;
265
314
  }
266
- handleSuccess(data.session_id);
315
+ handleSuccess(data.handshake_token);
267
316
  } catch {
268
317
  setErr("Network error. Please try again.");
269
318
  } finally {
@@ -319,7 +368,7 @@ function SignIn({
319
368
  setErr(verifyData.error ?? "Passkey verification failed.");
320
369
  return;
321
370
  }
322
- handleSuccess(verifyData.session_id);
371
+ handleSuccess(verifyData.handshake_token);
323
372
  } catch (err) {
324
373
  if (err instanceof Error && err.name === "NotAllowedError") {
325
374
  setErr("Passkey was cancelled or timed out.");
@@ -339,28 +388,86 @@ function SignIn({
339
388
  method: "POST",
340
389
  credentials: "include",
341
390
  headers: { "Content-Type": "application/json" },
342
- body: JSON.stringify({
343
- challenge_id: challengeIdRef.current,
344
- code: totp
345
- })
391
+ body: JSON.stringify({ challenge_id: challengeIdRef.current, code: totp })
346
392
  });
347
393
  const data = await res.json();
348
394
  if (!res.ok) {
349
395
  setErr(data.error ?? "Invalid code.");
350
396
  return;
351
397
  }
352
- handleSuccess(data.session_id);
398
+ handleSuccess(data.handshake_token);
399
+ } catch {
400
+ setErr("Network error. Please try again.");
401
+ } finally {
402
+ setLoading(false);
403
+ }
404
+ }
405
+ async function handleForgotSubmit(e) {
406
+ e.preventDefault();
407
+ setError(null);
408
+ setLoading(true);
409
+ try {
410
+ await fetch(`${apiOrigin}/v1/auth/forgot-password`, {
411
+ method: "POST",
412
+ credentials: "include",
413
+ headers: { "Content-Type": "application/json" },
414
+ body: JSON.stringify({ email })
415
+ });
416
+ setInfo("Check your email for a reset code.");
417
+ setStep("forgot-verify");
353
418
  } catch {
354
419
  setErr("Network error. Please try again.");
355
420
  } finally {
356
421
  setLoading(false);
357
422
  }
358
423
  }
359
- function handleSuccess(sessionId) {
360
- onSuccess?.(sessionId);
361
- const target = redirectUrl ?? document.querySelector("[data-vaultix-after-sign-in]")?.getAttribute("data-vaultix-after-sign-in") ?? "/";
362
- if (target) window.location.href = target;
424
+ async function handleForgotVerifySubmit(e) {
425
+ e.preventDefault();
426
+ setError(null);
427
+ setInfo(null);
428
+ setStep("forgot-reset");
363
429
  }
430
+ async function handleResetPasswordSubmit(e) {
431
+ e.preventDefault();
432
+ setError(null);
433
+ setLoading(true);
434
+ try {
435
+ const res = await fetch(`${apiOrigin}/v1/auth/reset-password`, {
436
+ method: "POST",
437
+ credentials: "include",
438
+ headers: { "Content-Type": "application/json" },
439
+ body: JSON.stringify({ email, code: forgotCode, new_password: newPassword })
440
+ });
441
+ const data = await res.json();
442
+ if (!res.ok) {
443
+ setErr(data.error ?? "Could not reset password.");
444
+ return;
445
+ }
446
+ handleSuccess(data.handshake_token);
447
+ } catch {
448
+ setErr("Network error. Please try again.");
449
+ } finally {
450
+ setLoading(false);
451
+ }
452
+ }
453
+ const title = {
454
+ email: "Sign in",
455
+ password: "Enter password",
456
+ passkey: "Passkey sign-in",
457
+ totp: "Two-factor code",
458
+ forgot: "Forgot password",
459
+ "forgot-verify": "Enter reset code",
460
+ "forgot-reset": "New password"
461
+ };
462
+ const subtitle = {
463
+ email: "Welcome back",
464
+ password: email,
465
+ passkey: email,
466
+ totp: "Enter the 6-digit code from your authenticator app",
467
+ forgot: "We'll send a reset code to your email",
468
+ "forgot-verify": `Code sent to ${email}`,
469
+ "forgot-reset": "Choose a new password"
470
+ };
364
471
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
365
472
  "div",
366
473
  {
@@ -373,34 +480,29 @@ function SignIn({
373
480
  children: [
374
481
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "px-8 pt-8 pb-6 text-center", children: [
375
482
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "inline-flex items-center justify-center w-10 h-10 rounded-xl bg-gradient-to-br from-purple-600 to-blue-600 shadow-lg shadow-purple-500/30 mb-4", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(LockIcon, {}) }),
376
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("h1", { className: "text-xl font-semibold text-white/90", children: [
377
- step === "email" && "Sign in",
378
- step === "password" && "Enter password",
379
- step === "passkey" && "Passkey sign-in",
380
- step === "totp" && "Two-factor code"
381
- ] }),
382
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "text-sm text-[#475569] mt-1", children: [
383
- step === "email" && "Welcome back",
384
- step === "password" && email,
385
- step === "passkey" && email,
386
- step === "totp" && "Enter the 6-digit code from your authenticator app"
387
- ] })
483
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h1", { className: "text-xl font-semibold text-white/90", children: title[step] }),
484
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-sm text-[#475569] mt-1", children: subtitle[step] })
388
485
  ] }),
389
486
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "px-8 pb-8 space-y-4", children: [
390
487
  error && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "rounded-lg bg-red-500/10 border border-red-500/30 px-3 py-2", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xs text-red-400", children: error }) }),
391
- step === "email" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("form", { onSubmit: handleEmailSubmit, className: "space-y-3", children: [
392
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
393
- Input,
394
- {
395
- type: "email",
396
- placeholder: "you@company.com",
397
- value: email,
398
- onChange: setEmail,
399
- autoFocus: true,
400
- required: true
401
- }
402
- ),
403
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(PrimaryButton, { loading, children: "Continue" })
488
+ info && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "rounded-lg bg-blue-500/10 border border-blue-500/30 px-3 py-2", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xs text-blue-400", children: info }) }),
489
+ step === "email" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "space-y-3", children: [
490
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(GoogleButton, { onClick: handleGoogleSignIn }),
491
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Divider, {}),
492
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("form", { onSubmit: handleEmailSubmit, className: "space-y-3", children: [
493
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
494
+ Input,
495
+ {
496
+ type: "email",
497
+ placeholder: "you@company.com",
498
+ value: email,
499
+ onChange: setEmail,
500
+ autoFocus: true,
501
+ required: true
502
+ }
503
+ ),
504
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(PrimaryButton, { loading, children: "Continue" })
505
+ ] })
404
506
  ] }),
405
507
  step === "password" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("form", { onSubmit: handlePasswordSubmit, className: "space-y-3", children: [
406
508
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
@@ -415,6 +517,10 @@ function SignIn({
415
517
  }
416
518
  ),
417
519
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(PrimaryButton, { loading, children: "Sign in" }),
520
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(GhostButton, { onClick: () => {
521
+ setStep("forgot");
522
+ setError(null);
523
+ }, children: "Forgot password?" }),
418
524
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(GhostButton, { onClick: () => setStep("email"), children: "\u2190 Back" })
419
525
  ] }),
420
526
  step === "passkey" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "space-y-3", children: [
@@ -443,6 +549,58 @@ function SignIn({
443
549
  ),
444
550
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(PrimaryButton, { loading, children: "Verify" }),
445
551
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(GhostButton, { onClick: () => setStep("password"), children: "\u2190 Back" })
552
+ ] }),
553
+ step === "forgot" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("form", { onSubmit: handleForgotSubmit, className: "space-y-3", children: [
554
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
555
+ Input,
556
+ {
557
+ type: "email",
558
+ placeholder: "you@company.com",
559
+ value: email,
560
+ onChange: setEmail,
561
+ autoFocus: true,
562
+ required: true
563
+ }
564
+ ),
565
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(PrimaryButton, { loading, children: "Send reset code" }),
566
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(GhostButton, { onClick: () => {
567
+ setStep("password");
568
+ setError(null);
569
+ }, children: "\u2190 Back" })
570
+ ] }),
571
+ step === "forgot-verify" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("form", { onSubmit: handleForgotVerifySubmit, className: "space-y-3", children: [
572
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
573
+ Input,
574
+ {
575
+ type: "text",
576
+ inputMode: "numeric",
577
+ pattern: "[0-9]{6}",
578
+ maxLength: 6,
579
+ placeholder: "000000",
580
+ value: forgotCode,
581
+ onChange: setForgotCode,
582
+ autoFocus: true,
583
+ required: true,
584
+ className: "text-center tracking-[0.4em] text-lg"
585
+ }
586
+ ),
587
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(PrimaryButton, { loading, children: "Continue" })
588
+ ] }),
589
+ step === "forgot-reset" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("form", { onSubmit: handleResetPasswordSubmit, className: "space-y-3", children: [
590
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
591
+ Input,
592
+ {
593
+ type: "password",
594
+ placeholder: "New password",
595
+ value: newPassword,
596
+ onChange: setNewPassword,
597
+ autoFocus: true,
598
+ required: true,
599
+ minLength: 8
600
+ }
601
+ ),
602
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(PasswordStrength, { password: newPassword }),
603
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(PrimaryButton, { loading, children: "Reset password" })
446
604
  ] })
447
605
  ] })
448
606
  ]
@@ -495,28 +653,63 @@ function GhostButton({ children, onClick }) {
495
653
  }
496
654
  );
497
655
  }
498
- function Spinner() {
656
+ function GoogleButton({ onClick }) {
499
657
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
500
- "svg",
658
+ "button",
501
659
  {
502
- className: "animate-spin h-4 w-4 text-white/80",
503
- xmlns: "http://www.w3.org/2000/svg",
504
- fill: "none",
505
- viewBox: "0 0 24 24",
660
+ type: "button",
661
+ onClick,
662
+ className: (0, import_clsx.clsx)(
663
+ "w-full flex items-center justify-center gap-3 px-4 py-2.5 rounded-xl",
664
+ "text-sm font-medium text-white/80",
665
+ "bg-white/5 border border-white/10",
666
+ "hover:bg-white/10 hover:border-white/20",
667
+ "transition-all duration-150"
668
+ ),
506
669
  children: [
507
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }),
508
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
509
- "path",
510
- {
511
- className: "opacity-75",
512
- fill: "currentColor",
513
- d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
514
- }
515
- )
670
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(GoogleIcon, {}),
671
+ "Continue with Google"
516
672
  ]
517
673
  }
518
674
  );
519
675
  }
676
+ function Divider() {
677
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-3", children: [
678
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex-1 h-px bg-white/8" }),
679
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-[10px] text-[#475569] uppercase tracking-wider", children: "or" }),
680
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex-1 h-px bg-white/8" })
681
+ ] });
682
+ }
683
+ function PasswordStrength({ password }) {
684
+ const score = (() => {
685
+ if (password.length === 0) return 0;
686
+ let s = 0;
687
+ if (password.length >= 8) s++;
688
+ if (/[A-Z]/.test(password)) s++;
689
+ if (/[0-9]/.test(password)) s++;
690
+ if (/[^A-Za-z0-9]/.test(password)) s++;
691
+ return s;
692
+ })();
693
+ if (password.length === 0) return null;
694
+ const labels = ["", "Weak", "Fair", "Good", "Strong"];
695
+ const colors = ["", "bg-red-500", "bg-amber-400", "bg-blue-400", "bg-emerald-400"];
696
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "space-y-1", children: [
697
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex gap-1", children: [1, 2, 3, 4].map((i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
698
+ "div",
699
+ {
700
+ className: (0, import_clsx.clsx)("flex-1 h-0.5 rounded-full transition-all duration-300", i <= score ? colors[score] : "bg-white/10")
701
+ },
702
+ i
703
+ )) }),
704
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-[10px] text-[#475569]", children: labels[score] })
705
+ ] });
706
+ }
707
+ function Spinner() {
708
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { className: "animate-spin h-4 w-4 text-white/80", xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", children: [
709
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }),
710
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" })
711
+ ] });
712
+ }
520
713
  function LockIcon() {
521
714
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "white", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
522
715
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "3", y: "11", width: "18", height: "11", rx: "2", ry: "2" }),
@@ -533,10 +726,18 @@ function FingerprintIcon() {
533
726
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M12 12v.01" })
534
727
  ] });
535
728
  }
729
+ function GoogleIcon() {
730
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", children: [
731
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z", fill: "#4285F4" }),
732
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z", fill: "#34A853" }),
733
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z", fill: "#FBBC05" }),
734
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z", fill: "#EA4335" })
735
+ ] });
736
+ }
536
737
 
537
738
  // src/components/SignUp.tsx
538
739
  var import_clsx2 = require("clsx");
539
- var import_react3 = require("react");
740
+ var import_react4 = require("react");
540
741
  var import_jsx_runtime3 = require("react/jsx-runtime");
541
742
  function resolveApiOrigin3(prop) {
542
743
  if (prop) return prop;
@@ -546,6 +747,18 @@ function resolveApiOrigin3(prop) {
546
747
  }
547
748
  return "";
548
749
  }
750
+ function resolveAfterSignUpUrl(redirectUrlProp) {
751
+ if (redirectUrlProp) return redirectUrlProp;
752
+ if (typeof document !== "undefined") {
753
+ const el = document.querySelector("[data-vaultix-after-sign-up]");
754
+ const attr = el?.getAttribute("data-vaultix-after-sign-up");
755
+ if (attr) return attr;
756
+ const params = new URLSearchParams(window.location.search);
757
+ const redirectUrl = params.get("redirect_url");
758
+ if (redirectUrl) return redirectUrl;
759
+ }
760
+ return "/";
761
+ }
549
762
  function SignUp({
550
763
  redirectUrl,
551
764
  onSuccess,
@@ -554,17 +767,29 @@ function SignUp({
554
767
  className
555
768
  }) {
556
769
  const apiOrigin = resolveApiOrigin3(apiOriginProp);
557
- const [step, setStep] = (0, import_react3.useState)("email");
558
- const [email, setEmail] = (0, import_react3.useState)("");
559
- const [password, setPassword] = (0, import_react3.useState)("");
560
- const [verificationCode, setVerificationCode] = (0, import_react3.useState)("");
561
- const [error, setError] = (0, import_react3.useState)(null);
562
- const [loading, setLoading] = (0, import_react3.useState)(false);
563
- const [registrationId, setRegistrationId] = (0, import_react3.useState)(null);
770
+ const [step, setStep] = (0, import_react4.useState)("email");
771
+ const [email, setEmail] = (0, import_react4.useState)("");
772
+ const [password, setPassword] = (0, import_react4.useState)("");
773
+ const [verificationCode, setVerificationCode] = (0, import_react4.useState)("");
774
+ const [error, setError] = (0, import_react4.useState)(null);
775
+ const [loading, setLoading] = (0, import_react4.useState)(false);
776
+ const [registrationId, setRegistrationId] = (0, import_react4.useState)(null);
564
777
  function setErr(msg) {
565
778
  setError(msg);
566
779
  onError?.(msg);
567
780
  }
781
+ function handleSuccess(handshakeToken) {
782
+ onSuccess?.(handshakeToken);
783
+ const target = resolveAfterSignUpUrl(redirectUrl);
784
+ const url = new URL(target, window.location.origin);
785
+ url.searchParams.set("__vaultix_handshake", handshakeToken);
786
+ window.location.href = url.toString();
787
+ }
788
+ function handleGoogleSignUp() {
789
+ const target = resolveAfterSignUpUrl(redirectUrl);
790
+ const params = new URLSearchParams({ redirect_url: target });
791
+ window.location.href = `${apiOrigin}/v1/auth/oauth/google?${params}`;
792
+ }
568
793
  async function handleEmailPassword(e) {
569
794
  e.preventDefault();
570
795
  setError(null);
@@ -608,9 +833,7 @@ function SignUp({
608
833
  setErr(data.error ?? "Invalid code.");
609
834
  return;
610
835
  }
611
- onSuccess?.(data.session_id);
612
- const target = redirectUrl ?? document.querySelector("[data-vaultix-after-sign-up]")?.getAttribute("data-vaultix-after-sign-up") ?? "/";
613
- if (target) window.location.href = target;
836
+ handleSuccess(data.handshake_token);
614
837
  } catch {
615
838
  setErr("Network error. Please try again.");
616
839
  } finally {
@@ -645,31 +868,35 @@ function SignUp({
645
868
  ] }),
646
869
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "px-8 pb-8 space-y-4", children: [
647
870
  error && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "rounded-lg bg-red-500/10 border border-red-500/30 px-3 py-2", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "text-xs text-red-400", children: error }) }),
648
- (step === "email" || step === "password") && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("form", { onSubmit: handleEmailPassword, className: "space-y-3", children: [
649
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
650
- SignUpInput,
651
- {
652
- type: "email",
653
- placeholder: "you@company.com",
654
- value: email,
655
- onChange: setEmail,
656
- autoFocus: true,
657
- required: true
658
- }
659
- ),
660
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
661
- SignUpInput,
662
- {
663
- type: "password",
664
- placeholder: "Create a password",
665
- value: password,
666
- onChange: setPassword,
667
- required: true,
668
- minLength: 8
669
- }
670
- ),
671
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PasswordStrength, { password }),
672
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SignUpPrimaryButton, { loading, children: "Create account" })
871
+ step === "email" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "space-y-3", children: [
872
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(GoogleButton2, { onClick: handleGoogleSignUp }),
873
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Divider2, {}),
874
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("form", { onSubmit: handleEmailPassword, className: "space-y-3", children: [
875
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
876
+ SignUpInput,
877
+ {
878
+ type: "email",
879
+ placeholder: "you@company.com",
880
+ value: email,
881
+ onChange: setEmail,
882
+ autoFocus: true,
883
+ required: true
884
+ }
885
+ ),
886
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
887
+ SignUpInput,
888
+ {
889
+ type: "password",
890
+ placeholder: "Create a password",
891
+ value: password,
892
+ onChange: setPassword,
893
+ required: true,
894
+ minLength: 8
895
+ }
896
+ ),
897
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PasswordStrength2, { password }),
898
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SignUpPrimaryButton, { loading, children: "Create account" })
899
+ ] })
673
900
  ] }),
674
901
  step === "verify" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("form", { onSubmit: handleVerification, className: "space-y-3", children: [
675
902
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
@@ -739,7 +966,34 @@ function SignUpPrimaryButton({
739
966
  }
740
967
  );
741
968
  }
742
- function PasswordStrength({ password }) {
969
+ function GoogleButton2({ onClick }) {
970
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
971
+ "button",
972
+ {
973
+ type: "button",
974
+ onClick,
975
+ className: (0, import_clsx2.clsx)(
976
+ "w-full flex items-center justify-center gap-3 px-4 py-2.5 rounded-xl",
977
+ "text-sm font-medium text-white/80",
978
+ "bg-white/5 border border-white/10",
979
+ "hover:bg-white/10 hover:border-white/20",
980
+ "transition-all duration-150"
981
+ ),
982
+ children: [
983
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(GoogleIcon2, {}),
984
+ "Continue with Google"
985
+ ]
986
+ }
987
+ );
988
+ }
989
+ function Divider2() {
990
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center gap-3", children: [
991
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "flex-1 h-px bg-white/8" }),
992
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "text-[10px] text-[#475569] uppercase tracking-wider", children: "or" }),
993
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "flex-1 h-px bg-white/8" })
994
+ ] });
995
+ }
996
+ function PasswordStrength2({ password }) {
743
997
  const score = (() => {
744
998
  if (password.length === 0) return 0;
745
999
  let s = 0;
@@ -751,13 +1005,7 @@ function PasswordStrength({ password }) {
751
1005
  })();
752
1006
  if (password.length === 0) return null;
753
1007
  const labels = ["", "Weak", "Fair", "Good", "Strong"];
754
- const colors = [
755
- "",
756
- "bg-red-500",
757
- "bg-amber-400",
758
- "bg-blue-400",
759
- "bg-emerald-400"
760
- ];
1008
+ const colors = ["", "bg-red-500", "bg-amber-400", "bg-blue-400", "bg-emerald-400"];
761
1009
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "space-y-1", children: [
762
1010
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "flex gap-1", children: [1, 2, 3, 4].map((i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
763
1011
  "div",
@@ -773,45 +1021,71 @@ function PasswordStrength({ password }) {
773
1021
  ] });
774
1022
  }
775
1023
  function Spinner2() {
776
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
777
- "svg",
778
- {
779
- className: "animate-spin h-4 w-4 text-white/80",
780
- xmlns: "http://www.w3.org/2000/svg",
781
- fill: "none",
782
- viewBox: "0 0 24 24",
783
- children: [
784
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }),
785
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
786
- "path",
787
- {
788
- className: "opacity-75",
789
- fill: "currentColor",
790
- d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
791
- }
792
- )
793
- ]
794
- }
795
- );
1024
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { className: "animate-spin h-4 w-4 text-white/80", xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", children: [
1025
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }),
1026
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" })
1027
+ ] });
796
1028
  }
797
1029
  function SparkleIcon() {
798
1030
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "white", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" }) });
799
1031
  }
1032
+ function GoogleIcon2() {
1033
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", children: [
1034
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z", fill: "#4285F4" }),
1035
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z", fill: "#34A853" }),
1036
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z", fill: "#FBBC05" }),
1037
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z", fill: "#EA4335" })
1038
+ ] });
1039
+ }
1040
+
1041
+ // src/components/SignedIn.tsx
1042
+ var import_jsx_runtime4 = require("react/jsx-runtime");
1043
+ function SignedIn({ children }) {
1044
+ const { isLoaded, isSignedIn } = useVaultixContext();
1045
+ if (!isLoaded || !isSignedIn) return null;
1046
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children });
1047
+ }
1048
+ function SignedOut({ children }) {
1049
+ const { isLoaded, isSignedIn } = useVaultixContext();
1050
+ if (!isLoaded || isSignedIn) return null;
1051
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children });
1052
+ }
1053
+
1054
+ // src/components/RedirectComponents.tsx
1055
+ var import_react5 = require("react");
1056
+ function RedirectToSignIn({ redirectUrl = "/sign-in" }) {
1057
+ const { isLoaded, isSignedIn } = useVaultixContext();
1058
+ (0, import_react5.useEffect)(() => {
1059
+ if (isLoaded && !isSignedIn) {
1060
+ window.location.href = redirectUrl;
1061
+ }
1062
+ }, [isLoaded, isSignedIn, redirectUrl]);
1063
+ return null;
1064
+ }
1065
+ function RedirectToSignUp({ redirectUrl = "/sign-up" }) {
1066
+ const { isLoaded, isSignedIn } = useVaultixContext();
1067
+ (0, import_react5.useEffect)(() => {
1068
+ if (isLoaded && !isSignedIn) {
1069
+ window.location.href = redirectUrl;
1070
+ }
1071
+ }, [isLoaded, isSignedIn, redirectUrl]);
1072
+ return null;
1073
+ }
800
1074
 
801
1075
  // src/components/UserButton.tsx
802
1076
  var import_clsx3 = require("clsx");
803
- var import_react4 = require("react");
804
- var import_jsx_runtime4 = require("react/jsx-runtime");
1077
+ var import_react6 = require("react");
1078
+ var import_jsx_runtime5 = require("react/jsx-runtime");
805
1079
  function UserButton({
806
1080
  showName = false,
807
1081
  afterSignOutUrl,
808
1082
  className
809
1083
  }) {
810
1084
  const { user, session, isLoaded, isSignedIn, signOut } = useVaultixContext();
811
- const [open, setOpen] = (0, import_react4.useState)(false);
812
- const [signingOut, setSigningOut] = (0, import_react4.useState)(false);
813
- const containerRef = (0, import_react4.useRef)(null);
814
- (0, import_react4.useEffect)(() => {
1085
+ const [open, setOpen] = (0, import_react6.useState)(false);
1086
+ const [signingOut, setSigningOut] = (0, import_react6.useState)(false);
1087
+ const containerRef = (0, import_react6.useRef)(null);
1088
+ (0, import_react6.useEffect)(() => {
815
1089
  function onPointerDown(e) {
816
1090
  if (containerRef.current && !containerRef.current.contains(e.target)) {
817
1091
  setOpen(false);
@@ -820,7 +1094,7 @@ function UserButton({
820
1094
  document.addEventListener("pointerdown", onPointerDown);
821
1095
  return () => document.removeEventListener("pointerdown", onPointerDown);
822
1096
  }, []);
823
- if (!isLoaded) return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(AvatarSkeleton, {});
1097
+ if (!isLoaded) return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(AvatarSkeleton, {});
824
1098
  if (!isSignedIn || !user) return null;
825
1099
  const initials = [user.firstName, user.lastName].filter(Boolean).map((s) => s.charAt(0)).join("").toUpperCase() || user.email.charAt(0).toUpperCase();
826
1100
  async function handleSignOut() {
@@ -828,19 +1102,19 @@ function UserButton({
828
1102
  await signOut();
829
1103
  if (afterSignOutUrl) window.location.href = afterSignOutUrl;
830
1104
  }
831
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { ref: containerRef, className: (0, import_clsx3.clsx)("relative", className), children: [
832
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1105
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { ref: containerRef, className: (0, import_clsx3.clsx)("relative", className), children: [
1106
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
833
1107
  "button",
834
1108
  {
835
1109
  onClick: () => setOpen((o) => !o),
836
1110
  className: "flex items-center gap-2 rounded-xl p-1 hover:bg-white/8 transition-colors",
837
1111
  children: [
838
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Avatar, { initials, imageUrl: user.imageUrl }),
839
- showName && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-sm font-medium text-white/90 pr-1", children: user.firstName ?? user.email })
1112
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Avatar, { initials, imageUrl: user.imageUrl }),
1113
+ showName && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-sm font-medium text-white/90 pr-1", children: user.firstName ?? user.email })
840
1114
  ]
841
1115
  }
842
1116
  ),
843
- open && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1117
+ open && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
844
1118
  "div",
845
1119
  {
846
1120
  className: (0, import_clsx3.clsx)(
@@ -849,40 +1123,40 @@ function UserButton({
849
1123
  ),
850
1124
  style: { background: "rgba(22,27,45,0.96)" },
851
1125
  children: [
852
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center gap-3 px-4 py-3 border-b border-white/8", children: [
853
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Avatar, { initials, imageUrl: user.imageUrl, size: "lg" }),
854
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex-1 min-w-0", children: [
855
- (user.firstName || user.lastName) && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "text-sm font-semibold text-white/90 truncate", children: [user.firstName, user.lastName].filter(Boolean).join(" ") }),
856
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "text-xs text-[#475569] truncate", children: user.email })
1126
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-3 px-4 py-3 border-b border-white/8", children: [
1127
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Avatar, { initials, imageUrl: user.imageUrl, size: "lg" }),
1128
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex-1 min-w-0", children: [
1129
+ (user.firstName || user.lastName) && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "text-sm font-semibold text-white/90 truncate", children: [user.firstName, user.lastName].filter(Boolean).join(" ") }),
1130
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "text-xs text-[#475569] truncate", children: user.email })
857
1131
  ] })
858
1132
  ] }),
859
- session && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "px-4 py-2 border-b border-white/8", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex items-center justify-between", children: [
860
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-[10px] uppercase tracking-widest text-[#475569]", children: "Risk level" }),
861
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(RiskBadge, { level: session.riskLevel })
1133
+ session && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "px-4 py-2 border-b border-white/8", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center justify-between", children: [
1134
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-[10px] uppercase tracking-widest text-[#475569]", children: "Risk level" }),
1135
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(RiskBadge, { level: session.riskLevel })
862
1136
  ] }) }),
863
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "p-2", children: [
864
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1137
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "p-2", children: [
1138
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
865
1139
  DropdownItem,
866
1140
  {
867
1141
  label: "Manage account",
868
- icon: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(UserIcon, {}),
1142
+ icon: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(UserIcon, {}),
869
1143
  onClick: () => setOpen(false)
870
1144
  }
871
1145
  ),
872
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1146
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
873
1147
  DropdownItem,
874
1148
  {
875
1149
  label: "Security settings",
876
- icon: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ShieldIcon, {}),
1150
+ icon: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ShieldIcon, {}),
877
1151
  onClick: () => setOpen(false)
878
1152
  }
879
1153
  ),
880
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "h-px bg-white/8 my-1" }),
881
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1154
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "h-px bg-white/8 my-1" }),
1155
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
882
1156
  DropdownItem,
883
1157
  {
884
1158
  label: signingOut ? "Signing out\u2026" : "Sign out",
885
- icon: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SignOutIcon, {}),
1159
+ icon: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SignOutIcon, {}),
886
1160
  onClick: handleSignOut,
887
1161
  destructive: true
888
1162
  }
@@ -898,7 +1172,7 @@ function Avatar({ initials, imageUrl, size = "sm" }) {
898
1172
  if (imageUrl) {
899
1173
  return (
900
1174
  // eslint-disable-next-line @next/next/no-img-element
901
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1175
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
902
1176
  "img",
903
1177
  {
904
1178
  src: imageUrl,
@@ -908,7 +1182,7 @@ function Avatar({ initials, imageUrl, size = "sm" }) {
908
1182
  )
909
1183
  );
910
1184
  }
911
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1185
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
912
1186
  "div",
913
1187
  {
914
1188
  className: (0, import_clsx3.clsx)(
@@ -921,7 +1195,7 @@ function Avatar({ initials, imageUrl, size = "sm" }) {
921
1195
  );
922
1196
  }
923
1197
  function AvatarSkeleton() {
924
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "w-8 h-8 rounded-full bg-white/5 animate-pulse" });
1198
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "w-8 h-8 rounded-full bg-white/5 animate-pulse" });
925
1199
  }
926
1200
  function RiskBadge({ level }) {
927
1201
  const styles = {
@@ -930,7 +1204,7 @@ function RiskBadge({ level }) {
930
1204
  high: "bg-orange-500/15 text-orange-400 border-orange-500/30",
931
1205
  critical: "bg-red-500/15 text-red-400 border-red-500/30"
932
1206
  };
933
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1207
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
934
1208
  "span",
935
1209
  {
936
1210
  className: (0, import_clsx3.clsx)(
@@ -942,7 +1216,7 @@ function RiskBadge({ level }) {
942
1216
  );
943
1217
  }
944
1218
  function DropdownItem({ label, icon, onClick, destructive }) {
945
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1219
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
946
1220
  "button",
947
1221
  {
948
1222
  onClick,
@@ -951,33 +1225,33 @@ function DropdownItem({ label, icon, onClick, destructive }) {
951
1225
  destructive ? "text-red-400 hover:bg-red-500/10" : "text-white/70 hover:text-white hover:bg-white/8"
952
1226
  ),
953
1227
  children: [
954
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "opacity-70", children: icon }),
1228
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "opacity-70", children: icon }),
955
1229
  label
956
1230
  ]
957
1231
  }
958
1232
  );
959
1233
  }
960
1234
  function UserIcon() {
961
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
962
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" }),
963
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "12", cy: "7", r: "4" })
1235
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1236
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" }),
1237
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("circle", { cx: "12", cy: "7", r: "4" })
964
1238
  ] });
965
1239
  }
966
1240
  function ShieldIcon() {
967
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" }) });
1241
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" }) });
968
1242
  }
969
1243
  function SignOutIcon() {
970
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
971
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" }),
972
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("polyline", { points: "16 17 21 12 16 7" }),
973
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: "21", y1: "12", x2: "9", y2: "12" })
1244
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1245
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" }),
1246
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polyline", { points: "16 17 21 12 16 7" }),
1247
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: "21", y1: "12", x2: "9", y2: "12" })
974
1248
  ] });
975
1249
  }
976
1250
 
977
1251
  // src/components/OrganizationSwitcher.tsx
978
1252
  var import_clsx4 = require("clsx");
979
- var import_react5 = require("react");
980
- var import_jsx_runtime5 = require("react/jsx-runtime");
1253
+ var import_react7 = require("react");
1254
+ var import_jsx_runtime6 = require("react/jsx-runtime");
981
1255
  function resolveApiOrigin4() {
982
1256
  if (typeof document !== "undefined") {
983
1257
  const el = document.querySelector("[data-vaultix-api]");
@@ -990,11 +1264,11 @@ function OrganizationSwitcher({
990
1264
  className
991
1265
  }) {
992
1266
  const { organization, isLoaded, isSignedIn } = useVaultixContext();
993
- const [open, setOpen] = (0, import_react5.useState)(false);
994
- const [orgs, setOrgs] = (0, import_react5.useState)([]);
995
- const [switching, setSwitching] = (0, import_react5.useState)(null);
996
- const containerRef = (0, import_react5.useRef)(null);
997
- (0, import_react5.useEffect)(() => {
1267
+ const [open, setOpen] = (0, import_react7.useState)(false);
1268
+ const [orgs, setOrgs] = (0, import_react7.useState)([]);
1269
+ const [switching, setSwitching] = (0, import_react7.useState)(null);
1270
+ const containerRef = (0, import_react7.useRef)(null);
1271
+ (0, import_react7.useEffect)(() => {
998
1272
  function onPointerDown(e) {
999
1273
  if (containerRef.current && !containerRef.current.contains(e.target)) {
1000
1274
  setOpen(false);
@@ -1003,7 +1277,7 @@ function OrganizationSwitcher({
1003
1277
  document.addEventListener("pointerdown", onPointerDown);
1004
1278
  return () => document.removeEventListener("pointerdown", onPointerDown);
1005
1279
  }, []);
1006
- (0, import_react5.useEffect)(() => {
1280
+ (0, import_react7.useEffect)(() => {
1007
1281
  if (!open) return;
1008
1282
  const api = resolveApiOrigin4();
1009
1283
  fetch(`${api}/v1/me/organizations`, { credentials: "include" }).then((r) => r.json()).then(
@@ -1037,8 +1311,8 @@ function OrganizationSwitcher({
1037
1311
  }
1038
1312
  const displayName = organization?.name ?? "Personal";
1039
1313
  const initials = displayName.split(/\s+/).slice(0, 2).map((w) => w[0]).join("").toUpperCase();
1040
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { ref: containerRef, className: (0, import_clsx4.clsx)("relative", className), children: [
1041
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1314
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { ref: containerRef, className: (0, import_clsx4.clsx)("relative", className), children: [
1315
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1042
1316
  "button",
1043
1317
  {
1044
1318
  onClick: () => setOpen((o) => !o),
@@ -1048,16 +1322,16 @@ function OrganizationSwitcher({
1048
1322
  "transition-all duration-150"
1049
1323
  ),
1050
1324
  children: [
1051
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(OrgAvatar, { initials }),
1052
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "text-left min-w-0", children: [
1053
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "text-sm font-semibold text-white/90 truncate max-w-[120px]", children: displayName }),
1054
- organization && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "text-[10px] text-[#475569] capitalize", children: organization.role })
1325
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(OrgAvatar, { initials }),
1326
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "text-left min-w-0", children: [
1327
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-sm font-semibold text-white/90 truncate max-w-[120px]", children: displayName }),
1328
+ organization && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-[10px] text-[#475569] capitalize", children: organization.role })
1055
1329
  ] }),
1056
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ChevronIcon, {})
1330
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ChevronIcon, {})
1057
1331
  ]
1058
1332
  }
1059
1333
  ),
1060
- open && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1334
+ open && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1061
1335
  "div",
1062
1336
  {
1063
1337
  className: (0, import_clsx4.clsx)(
@@ -1066,13 +1340,13 @@ function OrganizationSwitcher({
1066
1340
  ),
1067
1341
  style: { background: "rgba(22,27,45,0.96)" },
1068
1342
  children: [
1069
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "px-4 py-3 border-b border-white/8", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "text-[10px] uppercase tracking-widest text-[#475569]", children: "Switch organization" }) }),
1070
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "p-2 max-h-56 overflow-y-auto", children: [
1071
- orgs.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "text-xs text-[#475569] text-center py-4", children: "Loading\u2026" }),
1343
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "px-4 py-3 border-b border-white/8", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-[10px] uppercase tracking-widest text-[#475569]", children: "Switch organization" }) }),
1344
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "p-2 max-h-56 overflow-y-auto", children: [
1345
+ orgs.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-xs text-[#475569] text-center py-4", children: "Loading\u2026" }),
1072
1346
  orgs.map((org) => {
1073
1347
  const isActive = org.id === organization?.id;
1074
1348
  const orgInitials = org.name.split(/\s+/).slice(0, 2).map((w) => w[0]).join("").toUpperCase();
1075
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1349
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1076
1350
  "button",
1077
1351
  {
1078
1352
  onClick: () => !isActive && switchOrg(org.id),
@@ -1082,9 +1356,9 @@ function OrganizationSwitcher({
1082
1356
  isActive ? "bg-purple-500/10 cursor-default" : "hover:bg-white/8 cursor-pointer"
1083
1357
  ),
1084
1358
  children: [
1085
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(OrgAvatar, { initials: orgInitials, active: isActive }),
1086
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex-1 min-w-0", children: [
1087
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1359
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(OrgAvatar, { initials: orgInitials, active: isActive }),
1360
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex-1 min-w-0", children: [
1361
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1088
1362
  "p",
1089
1363
  {
1090
1364
  className: (0, import_clsx4.clsx)(
@@ -1094,17 +1368,17 @@ function OrganizationSwitcher({
1094
1368
  children: org.name
1095
1369
  }
1096
1370
  ),
1097
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "text-[10px] text-[#475569] capitalize", children: org.role })
1371
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "text-[10px] text-[#475569] capitalize", children: org.role })
1098
1372
  ] }),
1099
- isActive && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "w-1.5 h-1.5 rounded-full bg-purple-400 shrink-0" }),
1100
- switching === org.id && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(MiniSpinner, {})
1373
+ isActive && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "w-1.5 h-1.5 rounded-full bg-purple-400 shrink-0" }),
1374
+ switching === org.id && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(MiniSpinner, {})
1101
1375
  ]
1102
1376
  },
1103
1377
  org.id
1104
1378
  );
1105
1379
  })
1106
1380
  ] }),
1107
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "p-2 border-t border-white/8", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
1381
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "p-2 border-t border-white/8", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1108
1382
  "button",
1109
1383
  {
1110
1384
  onClick: () => {
@@ -1112,7 +1386,7 @@ function OrganizationSwitcher({
1112
1386
  },
1113
1387
  className: "w-full flex items-center gap-2 px-3 py-2 rounded-xl text-sm text-[#475569] hover:text-white hover:bg-white/8 transition-colors",
1114
1388
  children: [
1115
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(PlusIcon, {}),
1389
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PlusIcon, {}),
1116
1390
  "Create organization"
1117
1391
  ]
1118
1392
  }
@@ -1126,7 +1400,7 @@ function OrgAvatar({
1126
1400
  initials,
1127
1401
  active = false
1128
1402
  }) {
1129
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1403
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1130
1404
  "div",
1131
1405
  {
1132
1406
  className: (0, import_clsx4.clsx)(
@@ -1138,7 +1412,7 @@ function OrgAvatar({
1138
1412
  );
1139
1413
  }
1140
1414
  function ChevronIcon() {
1141
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1415
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1142
1416
  "svg",
1143
1417
  {
1144
1418
  width: "12",
@@ -1150,29 +1424,34 @@ function ChevronIcon() {
1150
1424
  strokeLinecap: "round",
1151
1425
  strokeLinejoin: "round",
1152
1426
  className: "text-[#475569] shrink-0",
1153
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("polyline", { points: "6 9 12 15 18 9" })
1427
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("polyline", { points: "6 9 12 15 18 9" })
1154
1428
  }
1155
1429
  );
1156
1430
  }
1157
1431
  function PlusIcon() {
1158
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1159
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: "12", y1: "5", x2: "12", y2: "19" }),
1160
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("line", { x1: "5", y1: "12", x2: "19", y2: "12" })
1432
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
1433
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: "12", y1: "5", x2: "12", y2: "19" }),
1434
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: "5", y1: "12", x2: "19", y2: "12" })
1161
1435
  ] });
1162
1436
  }
1163
1437
  function MiniSpinner() {
1164
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("svg", { className: "animate-spin h-3 w-3 text-[#475569]", xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", children: [
1165
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }),
1166
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" })
1438
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("svg", { className: "animate-spin h-3 w-3 text-[#475569]", xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", children: [
1439
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }),
1440
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" })
1167
1441
  ] });
1168
1442
  }
1169
1443
  // Annotate the CommonJS export names for ESM import in node:
1170
1444
  0 && (module.exports = {
1171
1445
  OrganizationSwitcher,
1446
+ RedirectToSignIn,
1447
+ RedirectToSignUp,
1172
1448
  SignIn,
1173
1449
  SignUp,
1450
+ SignedIn,
1451
+ SignedOut,
1174
1452
  UserButton,
1175
1453
  VaultixProvider,
1454
+ useAuth,
1176
1455
  useOrganization,
1177
1456
  useSession,
1178
1457
  useUser,