@hyperyai/sdk 1.0.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.
Files changed (102) hide show
  1. package/LICENSE +13 -0
  2. package/README.md +391 -0
  3. package/dist/components/AuthButton.d.ts +51 -0
  4. package/dist/components/AuthButton.js +60 -0
  5. package/dist/components/AuthModal.d.ts +20 -0
  6. package/dist/components/AuthModal.js +62 -0
  7. package/dist/components/BuyButton.d.ts +47 -0
  8. package/dist/components/BuyButton.js +74 -0
  9. package/dist/components/ErrorBoundary.d.ts +13 -0
  10. package/dist/components/ErrorBoundary.js +24 -0
  11. package/dist/components/HyperyModals.d.ts +30 -0
  12. package/dist/components/HyperyModals.js +30 -0
  13. package/dist/components/InsufficientCreditsAlert.d.ts +11 -0
  14. package/dist/components/InsufficientCreditsAlert.js +12 -0
  15. package/dist/components/ModernAuthForm.d.ts +52 -0
  16. package/dist/components/ModernAuthForm.js +83 -0
  17. package/dist/components/RestrictionModal.d.ts +43 -0
  18. package/dist/components/RestrictionModal.js +167 -0
  19. package/dist/components/SignIn.d.ts +26 -0
  20. package/dist/components/SignIn.js +47 -0
  21. package/dist/components/SignInForm.d.ts +41 -0
  22. package/dist/components/SignInForm.js +86 -0
  23. package/dist/components/SignUp.d.ts +26 -0
  24. package/dist/components/SignUp.js +52 -0
  25. package/dist/components/SpendingLimitAlert.d.ts +12 -0
  26. package/dist/components/SpendingLimitAlert.js +14 -0
  27. package/dist/components/UserButton.d.ts +26 -0
  28. package/dist/components/UserButton.js +35 -0
  29. package/dist/components/UserProfile.d.ts +22 -0
  30. package/dist/components/UserProfile.js +26 -0
  31. package/dist/components/WorkspaceSwitcher.d.ts +29 -0
  32. package/dist/components/WorkspaceSwitcher.js +66 -0
  33. package/dist/components/control.d.ts +64 -0
  34. package/dist/components/control.js +89 -0
  35. package/dist/components/ui/dialog.d.ts +19 -0
  36. package/dist/components/ui/dialog.js +53 -0
  37. package/dist/hooks/index.d.ts +22 -0
  38. package/dist/hooks/index.js +23 -0
  39. package/dist/hooks/useBuyerWallet.d.ts +37 -0
  40. package/dist/hooks/useBuyerWallet.js +66 -0
  41. package/dist/hooks/useCheckout.d.ts +27 -0
  42. package/dist/hooks/useCheckout.js +246 -0
  43. package/dist/hooks/useError.d.ts +19 -0
  44. package/dist/hooks/useError.js +36 -0
  45. package/dist/hooks/useMarketplace.d.ts +50 -0
  46. package/dist/hooks/useMarketplace.js +69 -0
  47. package/dist/hooks/useMemberships.d.ts +71 -0
  48. package/dist/hooks/useMemberships.js +138 -0
  49. package/dist/hooks/useWallet.d.ts +62 -0
  50. package/dist/hooks/useWallet.js +123 -0
  51. package/dist/index.d.ts +55 -0
  52. package/dist/index.js +52 -0
  53. package/dist/lib/context.d.ts +50 -0
  54. package/dist/lib/context.js +409 -0
  55. package/dist/lib/oauth.d.ts +49 -0
  56. package/dist/lib/oauth.js +149 -0
  57. package/dist/lib/parse-error.d.ts +45 -0
  58. package/dist/lib/parse-error.js +185 -0
  59. package/dist/lib/parse-stream.d.ts +43 -0
  60. package/dist/lib/parse-stream.js +89 -0
  61. package/dist/lib/popup.d.ts +42 -0
  62. package/dist/lib/popup.js +80 -0
  63. package/dist/lib/storage.d.ts +43 -0
  64. package/dist/lib/storage.js +125 -0
  65. package/dist/lib/utils.d.ts +8 -0
  66. package/dist/lib/utils.js +11 -0
  67. package/dist/types/index.d.ts +191 -0
  68. package/dist/types/index.js +4 -0
  69. package/package.json +78 -0
  70. package/src/components/AuthButton.tsx +123 -0
  71. package/src/components/AuthModal.tsx +240 -0
  72. package/src/components/BuyButton.tsx +150 -0
  73. package/src/components/ErrorBoundary.tsx +97 -0
  74. package/src/components/HyperyModals.tsx +83 -0
  75. package/src/components/InsufficientCreditsAlert.tsx +73 -0
  76. package/src/components/ModernAuthForm.tsx +322 -0
  77. package/src/components/RestrictionModal.tsx +373 -0
  78. package/src/components/SignIn.tsx +84 -0
  79. package/src/components/SignInForm.tsx +256 -0
  80. package/src/components/SignUp.tsx +86 -0
  81. package/src/components/SpendingLimitAlert.tsx +91 -0
  82. package/src/components/UserButton.tsx +106 -0
  83. package/src/components/UserProfile.tsx +106 -0
  84. package/src/components/WorkspaceSwitcher.tsx +199 -0
  85. package/src/components/control.tsx +133 -0
  86. package/src/components/ui/dialog.tsx +151 -0
  87. package/src/hooks/index.ts +65 -0
  88. package/src/hooks/useBuyerWallet.ts +117 -0
  89. package/src/hooks/useCheckout.ts +312 -0
  90. package/src/hooks/useError.ts +60 -0
  91. package/src/hooks/useMarketplace.ts +129 -0
  92. package/src/hooks/useMemberships.ts +203 -0
  93. package/src/hooks/useWallet.ts +170 -0
  94. package/src/index.ts +147 -0
  95. package/src/lib/context.tsx +478 -0
  96. package/src/lib/oauth.ts +215 -0
  97. package/src/lib/parse-error.ts +224 -0
  98. package/src/lib/parse-stream.ts +121 -0
  99. package/src/lib/popup.ts +98 -0
  100. package/src/lib/storage.ts +140 -0
  101. package/src/lib/utils.ts +14 -0
  102. package/src/types/index.ts +216 -0
@@ -0,0 +1,106 @@
1
+ /**
2
+ * UserProfile Component
3
+ * Display user profile information in a card format
4
+ */
5
+
6
+ 'use client';
7
+
8
+ import React from 'react';
9
+ import { useUser } from '../lib/context';
10
+
11
+ export interface UserProfileProps {
12
+ /** Show extended profile information */
13
+ showExtended?: boolean;
14
+ /** Custom CSS class */
15
+ className?: string;
16
+ /** Show loading state */
17
+ showLoading?: boolean;
18
+ }
19
+
20
+ /**
21
+ * User profile card component
22
+ *
23
+ * @example
24
+ * ```tsx
25
+ * <UserProfile showExtended />
26
+ * ```
27
+ */
28
+ export function UserProfile({
29
+ showExtended = true,
30
+ className,
31
+ showLoading = true,
32
+ }: UserProfileProps) {
33
+ const { user, isLoading } = useUser();
34
+
35
+ if (isLoading && showLoading) {
36
+ return (
37
+ <div className={className || 'bg-white rounded-lg shadow p-6'}>
38
+ <div className="animate-pulse">
39
+ <div className="flex items-center space-x-4">
40
+ <div className="w-16 h-16 bg-gray-200 rounded-full" />
41
+ <div className="flex-1 space-y-2">
42
+ <div className="h-4 bg-gray-200 rounded w-3/4" />
43
+ <div className="h-3 bg-gray-200 rounded w-1/2" />
44
+ </div>
45
+ </div>
46
+ </div>
47
+ </div>
48
+ );
49
+ }
50
+
51
+ if (!user) {
52
+ return null;
53
+ }
54
+
55
+ const defaultStyles = 'bg-white rounded-lg shadow p-6';
56
+
57
+ return (
58
+ <div className={className || defaultStyles}>
59
+ <div className="flex items-start space-x-4">
60
+ {/* Avatar */}
61
+ {user.image ? (
62
+ <img
63
+ src={user.image}
64
+ alt={user.name}
65
+ className="w-16 h-16 rounded-full object-cover"
66
+ />
67
+ ) : (
68
+ <div className="w-16 h-16 bg-gradient-to-br from-blue-600 to-purple-600 rounded-full flex items-center justify-center text-white text-2xl font-bold">
69
+ {user.name.charAt(0).toUpperCase()}
70
+ </div>
71
+ )}
72
+
73
+ {/* User Info */}
74
+ <div className="flex-1 min-w-0">
75
+ <h3 className="text-xl font-semibold text-gray-900 truncate">
76
+ {user.name}
77
+ </h3>
78
+ <p className="text-sm text-gray-600 truncate">{user.email}</p>
79
+
80
+ {showExtended && (
81
+ <div className="mt-4 space-y-2">
82
+ <div className="flex items-center justify-between text-sm">
83
+ <span className="text-gray-500">User ID</span>
84
+ <code className="text-xs bg-gray-100 px-2 py-1 rounded font-mono">
85
+ {user.id}
86
+ </code>
87
+ </div>
88
+ </div>
89
+ )}
90
+ </div>
91
+ </div>
92
+
93
+ {showExtended && (
94
+ <div className="mt-6 pt-6 border-t border-gray-200">
95
+ <div className="flex items-center justify-between">
96
+ <span className="text-sm text-gray-500">Profile Status</span>
97
+ <span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">
98
+ Active
99
+ </span>
100
+ </div>
101
+ </div>
102
+ )}
103
+ </div>
104
+ );
105
+ }
106
+
@@ -0,0 +1,199 @@
1
+ /**
2
+ * <WorkspaceSwitcher /> — drop-in dropdown that lists every workspace the
3
+ * user belongs to (across all orgs/teams) and switches the active one.
4
+ *
5
+ * Designed for the top-bar of consumer apps. Headless-ish: ships with
6
+ * sensible default styles via tailwind utility classes but they can be
7
+ * fully overridden via `className`.
8
+ *
9
+ * Example:
10
+ * <WorkspaceSwitcher
11
+ * onSwitched={(teamId, workspaceId) => router.refresh()}
12
+ * />
13
+ *
14
+ * The component groups workspaces by team. Personal team is labelled
15
+ * "Personal" rather than the literal "{Name}'s Workspace" team name to
16
+ * keep the switcher compact.
17
+ */
18
+
19
+ 'use client';
20
+
21
+ import React, { useCallback, useState } from 'react';
22
+ import { ChevronsUpDown, Check, Building2, User as UserIcon } from 'lucide-react';
23
+ import { useHyperyAuth } from '../lib/context';
24
+ import {
25
+ useMemberships,
26
+ setActiveWorkspace,
27
+ type MembershipEntry,
28
+ type MembershipWorkspace,
29
+ } from '../hooks/useMemberships';
30
+
31
+ export interface WorkspaceSwitcherProps {
32
+ /** Called after a successful switch — typically `router.refresh()`. */
33
+ onSwitched?: (teamId: string, workspaceId: string) => void;
34
+ /** Override the Hypery base URL (defaults to NEXT_PUBLIC_GATEWAY_URL). */
35
+ gatewayUrl?: string;
36
+ /** Extra classes appended to the trigger button. */
37
+ className?: string;
38
+ /** Optional aria-label for the trigger. */
39
+ ariaLabel?: string;
40
+ }
41
+
42
+ export function WorkspaceSwitcher({
43
+ onSwitched,
44
+ gatewayUrl,
45
+ className = '',
46
+ ariaLabel = 'Switch workspace',
47
+ }: WorkspaceSwitcherProps) {
48
+ const { getAccessToken } = useHyperyAuth();
49
+ const { data, isLoading, reload } = useMemberships();
50
+ const [open, setOpen] = useState(false);
51
+ const [switching, setSwitching] = useState<string | null>(null);
52
+
53
+ const activeWorkspaceId = data?.activeWorkspaceId ?? null;
54
+
55
+ // Resolve the currently-active entry for the trigger label.
56
+ const flat: Array<{
57
+ membership: MembershipEntry;
58
+ workspace: MembershipWorkspace;
59
+ }> =
60
+ data?.memberships.flatMap((m) =>
61
+ m.workspaces.map((w) => ({ membership: m, workspace: w })),
62
+ ) ?? [];
63
+ const activeEntry =
64
+ flat.find((e) => e.workspace.id === activeWorkspaceId) ?? flat[0];
65
+
66
+ const handleSwitch = useCallback(
67
+ async (teamId: string, workspaceId: string) => {
68
+ setSwitching(workspaceId);
69
+ try {
70
+ await setActiveWorkspace({
71
+ teamId,
72
+ workspaceId,
73
+ getAccessToken,
74
+ gatewayUrl,
75
+ });
76
+ await reload();
77
+ setOpen(false);
78
+ onSwitched?.(teamId, workspaceId);
79
+ } catch (err) {
80
+ console.error('[WorkspaceSwitcher] switch failed:', err);
81
+ } finally {
82
+ setSwitching(null);
83
+ }
84
+ },
85
+ [getAccessToken, gatewayUrl, onSwitched, reload],
86
+ );
87
+
88
+ if (isLoading && !data) {
89
+ return (
90
+ <button
91
+ type="button"
92
+ disabled
93
+ className={`inline-flex items-center gap-2 rounded-md border border-zinc-200 dark:border-zinc-800 bg-white dark:bg-zinc-900 px-3 py-1.5 text-sm text-zinc-500 ${className}`}
94
+ >
95
+ <span className="size-4 animate-pulse rounded-sm bg-zinc-300 dark:bg-zinc-700" />
96
+ Loading workspaces…
97
+ </button>
98
+ );
99
+ }
100
+
101
+ if (!data || data.memberships.length === 0) {
102
+ return null;
103
+ }
104
+
105
+ return (
106
+ <div className="relative inline-block text-left">
107
+ <button
108
+ type="button"
109
+ onClick={() => setOpen((v) => !v)}
110
+ aria-label={ariaLabel}
111
+ aria-haspopup="listbox"
112
+ aria-expanded={open}
113
+ className={`inline-flex items-center gap-2 rounded-md border border-zinc-200 dark:border-zinc-800 bg-white dark:bg-zinc-900 px-3 py-1.5 text-sm font-medium text-zinc-900 dark:text-zinc-100 hover:bg-zinc-50 dark:hover:bg-zinc-800 focus:outline-none focus:ring-2 focus:ring-blue-500 ${className}`}
114
+ >
115
+ {activeEntry?.membership.team.isPersonal ? (
116
+ <UserIcon className="h-4 w-4 text-zinc-500" aria-hidden />
117
+ ) : (
118
+ <Building2 className="h-4 w-4 text-zinc-500" aria-hidden />
119
+ )}
120
+ <span className="truncate max-w-[180px]">
121
+ {activeEntry ? (
122
+ <>
123
+ <span className="text-zinc-500">
124
+ {activeEntry.membership.team.isPersonal
125
+ ? 'Personal'
126
+ : activeEntry.membership.team.name}
127
+ </span>
128
+ <span className="mx-1.5 text-zinc-300 dark:text-zinc-600">·</span>
129
+ <span>{activeEntry.workspace.name}</span>
130
+ </>
131
+ ) : (
132
+ 'Select workspace'
133
+ )}
134
+ </span>
135
+ <ChevronsUpDown className="h-3.5 w-3.5 text-zinc-400" aria-hidden />
136
+ </button>
137
+
138
+ {open && (
139
+ <>
140
+ {/* Click-away */}
141
+ <button
142
+ type="button"
143
+ aria-hidden
144
+ tabIndex={-1}
145
+ onClick={() => setOpen(false)}
146
+ className="fixed inset-0 z-40 cursor-default"
147
+ />
148
+ <div
149
+ role="listbox"
150
+ className="absolute z-50 mt-1 max-h-[28rem] w-72 overflow-y-auto rounded-md border border-zinc-200 dark:border-zinc-800 bg-white dark:bg-zinc-900 shadow-lg p-1"
151
+ >
152
+ {data.memberships.map((m) => (
153
+ <div key={m.team.id} className="py-1">
154
+ <div className="px-2 pt-1 pb-1 text-[10px] font-semibold uppercase tracking-wider text-zinc-500 flex items-center gap-1.5">
155
+ {m.team.isPersonal ? (
156
+ <UserIcon className="h-3 w-3" />
157
+ ) : (
158
+ <Building2 className="h-3 w-3" />
159
+ )}
160
+ {m.team.isPersonal ? 'Personal' : m.team.name}
161
+ </div>
162
+ {m.workspaces.map((w) => {
163
+ const isActive = w.id === activeWorkspaceId;
164
+ const isSwitching = switching === w.id;
165
+ return (
166
+ <button
167
+ key={w.id}
168
+ type="button"
169
+ role="option"
170
+ aria-selected={isActive}
171
+ disabled={isSwitching}
172
+ onClick={() => handleSwitch(m.team.id, w.id)}
173
+ className={`flex w-full items-center justify-between gap-2 rounded-sm px-2 py-1.5 text-sm text-left hover:bg-zinc-100 dark:hover:bg-zinc-800 ${
174
+ isActive ? 'bg-zinc-50 dark:bg-zinc-800/60' : ''
175
+ } disabled:opacity-50`}
176
+ >
177
+ <span className="flex items-center gap-1.5 truncate">
178
+ {w.icon && <span aria-hidden>{w.icon}</span>}
179
+ <span className="truncate">{w.name}</span>
180
+ {w.isDefault && (
181
+ <span className="ml-1 text-[9px] uppercase tracking-wider text-zinc-400">
182
+ default
183
+ </span>
184
+ )}
185
+ </span>
186
+ {isActive && (
187
+ <Check className="h-3.5 w-3.5 text-blue-600" />
188
+ )}
189
+ </button>
190
+ );
191
+ })}
192
+ </div>
193
+ ))}
194
+ </div>
195
+ </>
196
+ )}
197
+ </div>
198
+ );
199
+ }
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Control Components
3
+ * Similar to Clerk's <SignedIn />, <SignedOut />, etc.
4
+ */
5
+
6
+ 'use client';
7
+
8
+ import type { ReactNode } from 'react';
9
+ import { useHyperyAuth } from '../lib/context';
10
+
11
+ export interface SignedInProps {
12
+ children: ReactNode;
13
+ /** Fallback content to show while loading */
14
+ fallback?: ReactNode;
15
+ }
16
+
17
+ /**
18
+ * Only renders children when user is authenticated
19
+ *
20
+ * @example
21
+ * ```tsx
22
+ * <SignedIn>
23
+ * <Dashboard />
24
+ * </SignedIn>
25
+ * ```
26
+ */
27
+ export function SignedIn({ children, fallback }: SignedInProps) {
28
+ const { isAuthenticated, isLoading } = useHyperyAuth();
29
+
30
+ if (isLoading) {
31
+ return fallback || null;
32
+ }
33
+
34
+ return isAuthenticated ? children : null;
35
+ }
36
+
37
+ export interface SignedOutProps {
38
+ children: ReactNode;
39
+ /** Fallback content to show while loading */
40
+ fallback?: ReactNode;
41
+ }
42
+
43
+ /**
44
+ * Only renders children when user is NOT authenticated
45
+ *
46
+ * @example
47
+ * ```tsx
48
+ * <SignedOut>
49
+ * <SignIn />
50
+ * </SignedOut>
51
+ * ```
52
+ */
53
+ export function SignedOut({ children, fallback }: SignedOutProps) {
54
+ const { isAuthenticated, isLoading } = useHyperyAuth();
55
+
56
+ if (isLoading) {
57
+ return fallback || null;
58
+ }
59
+
60
+ return !isAuthenticated ? children : null;
61
+ }
62
+
63
+ /**
64
+ * Redirects to sign in when user is not authenticated
65
+ *
66
+ * @example
67
+ * ```tsx
68
+ * <RedirectToSignIn />
69
+ * ```
70
+ */
71
+ export function RedirectToSignIn() {
72
+ const { login, isLoading, isAuthenticated } = useHyperyAuth();
73
+
74
+ if (!isLoading && !isAuthenticated) {
75
+ login();
76
+ }
77
+
78
+ return null;
79
+ }
80
+
81
+ export interface ProtectProps {
82
+ children: ReactNode;
83
+ /** Fallback to show when not authenticated */
84
+ fallback?: ReactNode;
85
+ /** Custom redirect logic */
86
+ onUnauthenticated?: () => void;
87
+ }
88
+
89
+ /**
90
+ * Protects content - redirects to sign in if not authenticated
91
+ *
92
+ * @example
93
+ * ```tsx
94
+ * <Protect fallback={<SignIn />}>
95
+ * <ProtectedContent />
96
+ * </Protect>
97
+ * ```
98
+ */
99
+ export function Protect({
100
+ children,
101
+ fallback,
102
+ onUnauthenticated,
103
+ }: ProtectProps) {
104
+ const { isAuthenticated, isLoading, login, isLoggingOut } = useHyperyAuth();
105
+
106
+ if (isLoading) {
107
+ return fallback || null;
108
+ }
109
+
110
+ // Don't trigger login if we're in the middle of logging out
111
+ if (!isAuthenticated && !isLoggingOut) {
112
+ if (onUnauthenticated) {
113
+ onUnauthenticated();
114
+ return null;
115
+ }
116
+
117
+ if (fallback) {
118
+ return fallback;
119
+ }
120
+
121
+ // Default: initiate login
122
+ login();
123
+ return null;
124
+ }
125
+
126
+ // If logging out, show fallback or nothing
127
+ if (isLoggingOut) {
128
+ return fallback || null;
129
+ }
130
+
131
+ return children;
132
+ }
133
+
@@ -0,0 +1,151 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as DialogPrimitive from "@radix-ui/react-dialog"
5
+ import { X } from "lucide-react"
6
+
7
+ import { cn } from "../../lib/utils"
8
+
9
+ const Dialog = DialogPrimitive.Root
10
+
11
+ const DialogTrigger = DialogPrimitive.Trigger
12
+
13
+ const DialogPortal = DialogPrimitive.Portal
14
+
15
+ const DialogClose = DialogPrimitive.Close
16
+
17
+ const DialogOverlay = React.forwardRef<
18
+ React.ElementRef<typeof DialogPrimitive.Overlay>,
19
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
20
+ >(({ className, style, ...props }, ref) => (
21
+ <DialogPrimitive.Overlay
22
+ ref={ref}
23
+ style={{
24
+ position: 'fixed',
25
+ inset: 0,
26
+ zIndex: 50,
27
+ backgroundColor: 'rgba(0, 0, 0, 0.8)',
28
+ ...style,
29
+ }}
30
+ className={className}
31
+ {...props}
32
+ />
33
+ ))
34
+ DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
35
+
36
+ const DialogContent = React.forwardRef<
37
+ React.ElementRef<typeof DialogPrimitive.Content>,
38
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
39
+ >(({ className, children, style, ...props }, ref) => (
40
+ <DialogPortal>
41
+ <DialogOverlay />
42
+ <DialogPrimitive.Content
43
+ ref={ref}
44
+ style={{
45
+ position: 'fixed',
46
+ left: '50%',
47
+ top: '50%',
48
+ zIndex: 50,
49
+ display: 'grid',
50
+ width: '100%',
51
+ maxWidth: '32rem',
52
+ transform: 'translate(-50%, -50%)',
53
+ gap: '1rem',
54
+ border: '1px solid #e5e7eb',
55
+ backgroundColor: '#ffffff',
56
+ padding: '1.5rem',
57
+ boxShadow: '0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)',
58
+ borderRadius: '0.5rem',
59
+ ...style,
60
+ }}
61
+ className={className}
62
+ {...props}
63
+ >
64
+ {children}
65
+ <DialogPrimitive.Close
66
+ style={{
67
+ position: 'absolute',
68
+ right: '1rem',
69
+ top: '1rem',
70
+ opacity: 0.7,
71
+ cursor: 'pointer',
72
+ border: 'none',
73
+ background: 'transparent',
74
+ }}
75
+ >
76
+ <X style={{ width: '1rem', height: '1rem' }} />
77
+ <span style={{ position: 'absolute', width: '1px', height: '1px', padding: 0, margin: '-1px', overflow: 'hidden', clip: 'rect(0, 0, 0, 0)', whiteSpace: 'nowrap', borderWidth: 0 }}>Close</span>
78
+ </DialogPrimitive.Close>
79
+ </DialogPrimitive.Content>
80
+ </DialogPortal>
81
+ ))
82
+ DialogContent.displayName = DialogPrimitive.Content.displayName
83
+
84
+ const DialogHeader = ({
85
+ className,
86
+ ...props
87
+ }: React.HTMLAttributes<HTMLDivElement>) => (
88
+ <div
89
+ className={cn(
90
+ "flex flex-col space-y-1.5 text-center sm:text-left",
91
+ className
92
+ )}
93
+ {...props}
94
+ />
95
+ )
96
+ DialogHeader.displayName = "DialogHeader"
97
+
98
+ const DialogFooter = ({
99
+ className,
100
+ ...props
101
+ }: React.HTMLAttributes<HTMLDivElement>) => (
102
+ <div
103
+ className={cn(
104
+ "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
105
+ className
106
+ )}
107
+ {...props}
108
+ />
109
+ )
110
+ DialogFooter.displayName = "DialogFooter"
111
+
112
+ const DialogTitle = React.forwardRef<
113
+ React.ElementRef<typeof DialogPrimitive.Title>,
114
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
115
+ >(({ className, ...props }, ref) => (
116
+ <DialogPrimitive.Title
117
+ ref={ref}
118
+ className={cn(
119
+ "text-lg font-semibold leading-none tracking-tight",
120
+ className
121
+ )}
122
+ {...props}
123
+ />
124
+ ))
125
+ DialogTitle.displayName = DialogPrimitive.Title.displayName
126
+
127
+ const DialogDescription = React.forwardRef<
128
+ React.ElementRef<typeof DialogPrimitive.Description>,
129
+ React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
130
+ >(({ className, ...props }, ref) => (
131
+ <DialogPrimitive.Description
132
+ ref={ref}
133
+ className={cn("text-sm text-muted-foreground", className)}
134
+ {...props}
135
+ />
136
+ ))
137
+ DialogDescription.displayName = DialogPrimitive.Description.displayName
138
+
139
+ export {
140
+ Dialog,
141
+ DialogPortal,
142
+ DialogOverlay,
143
+ DialogTrigger,
144
+ DialogClose,
145
+ DialogContent,
146
+ DialogHeader,
147
+ DialogFooter,
148
+ DialogTitle,
149
+ DialogDescription,
150
+ }
151
+
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Re-export hooks for convenience
3
+ */
4
+
5
+ export { useHyperyAuth, useUser } from '../lib/context';
6
+
7
+ /**
8
+ * Alias for useHyperyAuth to match Clerk's API
9
+ * @example
10
+ * ```tsx
11
+ * const { isLoaded, isAuthenticated, user } = useAuth();
12
+ * ```
13
+ */
14
+ export { useHyperyAuth as useAuth } from '../lib/context';
15
+
16
+ // Workspace + membership hooks for the WorkspaceSwitcher and consumer apps.
17
+ export {
18
+ useMemberships,
19
+ useActiveWorkspace,
20
+ setActiveWorkspace,
21
+ } from './useMemberships';
22
+ export type {
23
+ MembershipEntry,
24
+ MembershipTeam,
25
+ MembershipWorkspace,
26
+ MembershipsResponse,
27
+ ActiveWorkspace,
28
+ } from './useMemberships';
29
+
30
+ // Mode-aware wallet hook (balance, 1-click add funds, add payment method).
31
+ export { useWallet } from './useWallet';
32
+ export type {
33
+ BillingMode,
34
+ WalletState,
35
+ WalletTier,
36
+ UseWalletReturn,
37
+ } from './useWallet';
38
+
39
+ // "Buy with Hypery" marketplace layer (Stripe Connect, consumer side).
40
+ export { useBuyerWallet } from './useBuyerWallet';
41
+ export type {
42
+ BuyerPaymentMethod,
43
+ AddCardOptions,
44
+ UseBuyerWalletReturn,
45
+ } from './useBuyerWallet';
46
+ export { useMarketplace } from './useMarketplace';
47
+ export type {
48
+ BuyInput,
49
+ BuySuccess,
50
+ BuyFailure,
51
+ BuyResult,
52
+ UseMarketplaceReturn,
53
+ } from './useMarketplace';
54
+
55
+ // Seamless auth + charge orchestrator (login → charge → add-card → retry) in
56
+ // popup or redirect mode. Powers <BuyButton>; use directly for custom buttons
57
+ // and for AI-credit top-ups (kind: 'topup').
58
+ export { useCheckout } from './useCheckout';
59
+ export type {
60
+ CheckoutInput,
61
+ CheckoutStatus,
62
+ CheckoutResult,
63
+ UseCheckoutReturn,
64
+ } from './useCheckout';
65
+