@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
package/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Pay It Forward License (PIF)
2
+
3
+ Copyright (c) 2026 The Hypery Project Authors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ This license includes a perpetual, worldwide, non-exclusive, royalty-free, irrevocable (except as stated below) grant for patent claims necessarily infringed by the use of the Software or any contributions submitted to it; terminating automatically when an entity initiates patent litigation (including a cross-claim or counterclaim) alleging that the Software or any portion thereof infringes a patent.
10
+
11
+ The software is provided "as is," without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose, and non-infringement. In no event shall the authors or copyright holders be liable for any claim, damages, or other liability, whether in an action of contract, tort, or otherwise, arising from, out of, or in connection with the software or the use or other dealings in the software.
12
+
13
+ Recipients of the Software should pay it forward by contributing to the open-source community through actions including but not limited to providing code, documentation, tutorials, guides, support, feedback, or promoting open-source projects through advocacy or related efforts that advance innovation and community growth.
package/README.md ADDED
@@ -0,0 +1,391 @@
1
+ # @hyperyai/sdk
2
+
3
+ Drop-in authentication and error handling library for Hypery apps. Simple, secure, and built for React.
4
+
5
+ This package provides everything you need for OAuth authentication, user management, and structured error handling (spending limits, insufficient credits, etc.) in your Hypery applications.
6
+
7
+ Inspired by [Clerk](https://clerk.com/react-authentication), but purpose-built for Hypery's OAuth system.
8
+
9
+ ## 📸 Component gallery
10
+
11
+ See **[docs/COMPONENTS.md](./docs/COMPONENTS.md)** for a visual reference of every component with live screenshots and usage.
12
+
13
+ ![Auth components — SignInForm and AuthModal](./docs/screenshots/authmodal-open.png)
14
+
15
+ ## Features
16
+
17
+ - 🔐 **Secure OAuth 2.0 + PKCE** - Industry-standard authentication
18
+ - ⚛️ **React-first** - Hooks and components that feel natural
19
+ - 🎨 **Customizable** - Bring your own UI or use our defaults
20
+ - 📦 **Tiny** - Minimal dependencies, maximum performance
21
+ - 🔄 **Auto-refresh** - Seamless token management
22
+ - 💾 **Flexible storage** - localStorage, sessionStorage, or memory
23
+
24
+ ## Installation
25
+
26
+ ```bash
27
+ npm install @hyperyai/sdk
28
+ ```
29
+
30
+ The package ships compiled JavaScript with type declarations (plus the TypeScript
31
+ source under `src/` for reference), so it works out of the box — no
32
+ `transpilePackages` or build configuration needed.
33
+
34
+ ## Local development against this repo
35
+
36
+ To develop `@hyperyai/sdk` alongside a consuming app, use
37
+ [yalc](https://github.com/wclr/yalc) (npm link duplicates React and breaks hooks):
38
+
39
+ ```bash
40
+ # in hypery-sdk
41
+ npm i -g yalc
42
+ yalc publish # builds via prepack and stores the real pack output
43
+ # in your app
44
+ yalc add @hyperyai/sdk && npm install
45
+
46
+ # iterate: rebuild + push updates into consumers
47
+ yalc push # in hypery-sdk, after changes
48
+
49
+ # before committing your app
50
+ yalc remove --all && npm install
51
+ ```
52
+
53
+ ## Quick Start
54
+
55
+ ### 1. Wrap your app with `HyperyProvider`
56
+
57
+ ```tsx
58
+ import { HyperyProvider } from '@hyperyai/sdk';
59
+
60
+ function App() {
61
+ return (
62
+ <HyperyProvider
63
+ config={{
64
+ clientId: 'your-client-id',
65
+ redirectUri: 'http://localhost:3000/callback',
66
+ gatewayUrl: 'https://api.hypery.ai',
67
+ // Optional: defaults to ['read', 'write', 'ai:chat', 'ai:completions', 'ai:models', 'billing:read']
68
+ scopes: ['read', 'write', 'ai:chat', 'ai:completions', 'ai:models', 'billing:read'],
69
+ }}
70
+ >
71
+ <YourApp />
72
+ </HyperyProvider>
73
+ );
74
+ }
75
+ ```
76
+
77
+ ### 2. Use authentication components
78
+
79
+ ```tsx
80
+ import { SignedIn, SignedOut, SignIn, UserButton } from '@hyperyai/sdk';
81
+
82
+ function YourApp() {
83
+ return (
84
+ <>
85
+ <SignedIn>
86
+ <header>
87
+ <h1>Welcome!</h1>
88
+ <UserButton showUserInfo />
89
+ </header>
90
+ <Dashboard />
91
+ </SignedIn>
92
+
93
+ <SignedOut>
94
+ <div className="login-page">
95
+ <h1>Sign in to continue</h1>
96
+ <SignIn buttonText="Sign in with Hypery" />
97
+ </div>
98
+ </SignedOut>
99
+ </>
100
+ );
101
+ }
102
+ ```
103
+
104
+ ### 3. Access user data with hooks
105
+
106
+ ```tsx
107
+ import { useUser, useHyperyAuth } from '@hyperyai/sdk';
108
+
109
+ function Dashboard() {
110
+ const { user, isLoading } = useUser();
111
+ const { logout } = useHyperyAuth();
112
+
113
+ if (isLoading) return <div>Loading...</div>;
114
+
115
+ return (
116
+ <div>
117
+ <h1>Welcome, {user?.name}!</h1>
118
+ <p>Email: {user?.email}</p>
119
+ <button onClick={logout}>Sign out</button>
120
+ </div>
121
+ );
122
+ }
123
+ ```
124
+
125
+ ## Components
126
+
127
+ ### `<HyperyProvider />`
128
+
129
+ The main provider component. Wrap your app with this.
130
+
131
+ ```tsx
132
+ <HyperyProvider
133
+ config={{
134
+ clientId: string; // Your OAuth client ID
135
+ redirectUri: string; // Callback URL after auth
136
+ gatewayUrl: string; // Hypery API URL
137
+ scopes?: string[]; // OAuth scopes (default: ['read', 'write'])
138
+ storage?: 'localStorage' | 'sessionStorage' | 'memory'; // Storage type
139
+ }}
140
+ >
141
+ {children}
142
+ </HyperyProvider>
143
+ ```
144
+
145
+ ### Authentication Components
146
+
147
+ **`<SignIn />`** - Sign-in button
148
+
149
+ ```tsx
150
+ <SignIn
151
+ buttonText="Sign in with Hypery"
152
+ variant="primary" // 'primary' | 'secondary' | 'outline'
153
+ className="custom-button-class"
154
+ />
155
+ ```
156
+
157
+ **`<SignUp />`** - Sign-up button
158
+
159
+ ```tsx
160
+ <SignUp
161
+ buttonText="Get Started"
162
+ variant="primary"
163
+ onSignUpStart={() => console.log('Starting signup')}
164
+ />
165
+ ```
166
+
167
+ **`<SignInForm />`** - Embedded login form with social OAuth
168
+
169
+ Full-featured login form with GitHub, Google, and email/password authentication.
170
+
171
+ ```tsx
172
+ <SignInForm
173
+ showCard
174
+ showTitle
175
+ showSocial // Show GitHub and Google buttons
176
+ showEmailPassword // Show email/password form
177
+ title="Welcome back"
178
+ description="Sign in to your account"
179
+ onSuccess={() => console.log('Logged in!')}
180
+ onError={(error) => console.error(error)}
181
+ />
182
+ ```
183
+
184
+ ```tsx
185
+ // Social auth only (no email/password)
186
+ <SignInForm
187
+ showCard
188
+ showSocial={true}
189
+ showEmailPassword={false}
190
+ />
191
+ ```
192
+
193
+ **`<UserButton />`** - User avatar with dropdown menu
194
+
195
+ ```tsx
196
+ <UserButton
197
+ showUserInfo
198
+ size="md" // 'sm' | 'md' | 'lg'
199
+ renderDropdown={(user, logout) => (
200
+ <div>
201
+ <p>{user.name}</p>
202
+ <button onClick={logout}>Sign out</button>
203
+ </div>
204
+ )}
205
+ />
206
+ ```
207
+
208
+ **`<UserProfile />`** - User profile card
209
+
210
+ ```tsx
211
+ <UserProfile
212
+ showExtended
213
+ showLoading
214
+ />
215
+ ```
216
+
217
+ ### Control Components
218
+
219
+ **`<SignedIn>`** - Only renders when user is authenticated
220
+
221
+ ```tsx
222
+ <SignedIn>
223
+ <Dashboard />
224
+ </SignedIn>
225
+ ```
226
+
227
+ **`<SignedOut>`** - Only renders when user is NOT authenticated
228
+
229
+ ```tsx
230
+ <SignedOut>
231
+ <SignIn />
232
+ </SignedOut>
233
+ ```
234
+
235
+ **`<Protect>`** - Protects content and redirects if needed
236
+
237
+ ```tsx
238
+ <Protect fallback={<SignIn />}>
239
+ <ProtectedContent />
240
+ </Protect>
241
+ ```
242
+
243
+ **`<RedirectToSignIn />`** - Immediately redirects to sign in
244
+
245
+ ```tsx
246
+ <RedirectToSignIn />
247
+ ```
248
+
249
+ ## Hooks
250
+
251
+ ### `useAuth()` (Alias for `useHyperyAuth()`)
252
+
253
+ Full auth context with methods. Matches Clerk's API pattern.
254
+
255
+ ```tsx
256
+ const {
257
+ user, // Current user
258
+ isAuthenticated, // Auth status
259
+ isLoading, // Loading state
260
+ error, // Error message
261
+ login, // Initiate login
262
+ logout, // Sign out
263
+ refreshAuth, // Manually refresh
264
+ getAccessToken, // Get valid access token
265
+ } = useAuth();
266
+ ```
267
+
268
+ ### `useUser()`
269
+
270
+ Access current user data only.
271
+
272
+ ```tsx
273
+ const { user, isLoading } = useUser();
274
+
275
+ // user object contains:
276
+ // - id: string
277
+ // - name: string
278
+ // - email: string
279
+ // - image?: string
280
+ ```
281
+
282
+ ### `useHyperyAuth()`
283
+
284
+ Same as `useAuth()`, but with explicit naming.
285
+
286
+ ```tsx
287
+ const auth = useHyperyAuth();
288
+ ```
289
+
290
+ ## Advanced Usage
291
+
292
+ ### Custom storage
293
+
294
+ ```tsx
295
+ <HyperyProvider
296
+ config={{
297
+ // ...other config
298
+ storage: 'sessionStorage', // or 'memory' for SSR
299
+ }}
300
+ >
301
+ ```
302
+
303
+ ### Manual token management
304
+
305
+ ```tsx
306
+ import { useHyperyAuth } from '@hyperyai/sdk';
307
+
308
+ function MyComponent() {
309
+ const { getAccessToken } = useHyperyAuth();
310
+
311
+ const makeAuthenticatedRequest = async () => {
312
+ const token = await getAccessToken();
313
+
314
+ const response = await fetch('/api/data', {
315
+ headers: {
316
+ Authorization: `Bearer ${token}`,
317
+ },
318
+ });
319
+
320
+ return response.json();
321
+ };
322
+ }
323
+ ```
324
+
325
+ ### Custom UI
326
+
327
+ Build your own UI using the hooks:
328
+
329
+ ```tsx
330
+ import { useHyperyAuth } from '@hyperyai/sdk';
331
+
332
+ function CustomLoginButton() {
333
+ const { login, isLoading } = useHyperyAuth();
334
+
335
+ return (
336
+ <button
337
+ onClick={login}
338
+ disabled={isLoading}
339
+ className="my-custom-button"
340
+ >
341
+ {isLoading ? 'Loading...' : 'Sign in'}
342
+ </button>
343
+ );
344
+ }
345
+ ```
346
+
347
+ ## Environment Variables
348
+
349
+ Create a `.env.local` file:
350
+
351
+ ```env
352
+ NEXT_PUBLIC_OAUTH_CLIENT_ID=your_client_id
353
+ NEXT_PUBLIC_REDIRECT_URI=http://localhost:3000/callback
354
+ NEXT_PUBLIC_AUTH_URL=https://api.hypery.ai
355
+ ```
356
+
357
+ ## Error handling: payment & auth modals
358
+
359
+ When a gateway request is blocked mid-flow, the SDK lets you pop a **payment**
360
+ modal (out of credits / spending limit / card issue) or trigger **re-auth**
361
+ (expired/invalid token). This is **opt-in** — the SDK does not install a global
362
+ fetch interceptor, so you wire it into your own API calls.
363
+
364
+ Errors arrive as a JSON envelope. Classify on `error.code` (status is a
365
+ fallback only — spending-limit is `429`, insufficient-credits/payment is `402`,
366
+ auth is `401`):
367
+
368
+ ```jsonc
369
+ { "error": { "code": "INSUFFICIENT_CREDITS", "type": "insufficient_credits_error" } }
370
+ { "error": { "code": "SPENDING_LIMIT_EXCEEDED", "type": "spending_limit_error" } }
371
+ { "error": { "code": "PAYMENT_METHOD_REQUIRED", "type": "payment_method_required_error" } }
372
+ { "error": { "code": "PAYMENT_DECLINED", "type": "payment_declined_error" } }
373
+ { "error": { "code": "UNAUTHENTICATED", "type": "authentication_error" } }
374
+ ```
375
+
376
+ `useError().setError(body)` accepts the raw `{ error: { code } }` envelope **or**
377
+ an already-unwrapped object, and falls back to the HTTP status (`402`/`401`) when
378
+ the body has no recognized `code`. It exposes `isBillingRestriction` (any of the
379
+ payment/credit/limit cases) and `isAuth` (a `401`) so you can drive
380
+ `RestrictionModal` and `AuthModal` respectively. The guards
381
+ `isBillingRestriction(body)` and `isAuthError(body)` are also exported for use
382
+ outside the hook.
383
+
384
+ ## Examples
385
+
386
+ See the `/apps/chat` and `/apps/imagine` directories for complete working examples.
387
+
388
+ ## License
389
+
390
+ MIT
391
+
@@ -0,0 +1,51 @@
1
+ /**
2
+ * AuthButton Component
3
+ * Simple button that triggers authentication modal
4
+ * Drop this anywhere in your app for instant auth
5
+ */
6
+ import React from 'react';
7
+ export interface AuthButtonProps {
8
+ /** Button text */
9
+ children?: React.ReactNode;
10
+ /** Button variant */
11
+ variant?: 'primary' | 'secondary' | 'outline' | 'ghost';
12
+ /** Button size */
13
+ size?: 'sm' | 'md' | 'lg';
14
+ /** Custom className */
15
+ className?: string;
16
+ /** Initial auth mode */
17
+ mode?: 'signin' | 'signup';
18
+ /** Callback after successful auth */
19
+ onSuccess?: () => void;
20
+ /** Show social login options */
21
+ showSocial?: boolean;
22
+ /** Show email/password form */
23
+ showEmailPassword?: boolean;
24
+ /** Custom branding for modal */
25
+ branding?: {
26
+ logo?: string;
27
+ appName?: string;
28
+ primaryColor?: string;
29
+ };
30
+ }
31
+ /**
32
+ * Button that opens authentication modal
33
+ * Perfect for navbar, landing pages, or anywhere you need auth
34
+ *
35
+ * @example
36
+ * ```tsx
37
+ * // Simple usage
38
+ * <AuthButton>Sign In</AuthButton>
39
+ *
40
+ * // With customization
41
+ * <AuthButton
42
+ * variant="primary"
43
+ * mode="signup"
44
+ * onSuccess={() => router.push('/dashboard')}
45
+ * branding={{ appName: 'My App', primaryColor: '#8b5cf6' }}
46
+ * >
47
+ * Get Started
48
+ * </AuthButton>
49
+ * ```
50
+ */
51
+ export declare function AuthButton({ children, variant, size, className, mode, onSuccess, showSocial, showEmailPassword, branding, }: AuthButtonProps): React.JSX.Element;
@@ -0,0 +1,60 @@
1
+ /**
2
+ * AuthButton Component
3
+ * Simple button that triggers authentication modal
4
+ * Drop this anywhere in your app for instant auth
5
+ */
6
+ 'use client';
7
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
8
+ import { useState } from 'react';
9
+ import { AuthModal } from './AuthModal';
10
+ /**
11
+ * Button that opens authentication modal
12
+ * Perfect for navbar, landing pages, or anywhere you need auth
13
+ *
14
+ * @example
15
+ * ```tsx
16
+ * // Simple usage
17
+ * <AuthButton>Sign In</AuthButton>
18
+ *
19
+ * // With customization
20
+ * <AuthButton
21
+ * variant="primary"
22
+ * mode="signup"
23
+ * onSuccess={() => router.push('/dashboard')}
24
+ * branding={{ appName: 'My App', primaryColor: '#8b5cf6' }}
25
+ * >
26
+ * Get Started
27
+ * </AuthButton>
28
+ * ```
29
+ */
30
+ export function AuthButton({ children = 'Sign In', variant = 'primary', size = 'md', className = '', mode = 'signin', onSuccess, showSocial = true, showEmailPassword = true, branding, }) {
31
+ const [isOpen, setIsOpen] = useState(false);
32
+ const sizeClasses = {
33
+ sm: 'px-4 py-2 text-sm',
34
+ md: 'px-6 py-2.5 text-base',
35
+ lg: 'px-8 py-3 text-lg',
36
+ };
37
+ const variantClasses = {
38
+ primary: 'bg-purple-600 hover:bg-purple-700 text-white shadow-md hover:shadow-lg',
39
+ secondary: 'bg-gray-600 hover:bg-gray-700 text-white shadow-md hover:shadow-lg',
40
+ outline: 'border-2 border-purple-600 text-purple-600 hover:bg-purple-50 dark:hover:bg-purple-950/30',
41
+ ghost: 'hover:bg-gray-100 dark:hover:bg-gray-800 text-gray-700 dark:text-gray-300',
42
+ };
43
+ const handleClick = () => {
44
+ console.log('AuthButton clicked! Opening modal...', { isOpen, mode });
45
+ setIsOpen(true);
46
+ };
47
+ console.log('AuthButton render:', { isOpen, mode, variant, size });
48
+ return (_jsxs(_Fragment, { children: [_jsx("button", { onClick: handleClick, type: "button", className: `
49
+ ${sizeClasses[size]}
50
+ ${variantClasses[variant]}
51
+ rounded-lg font-medium transition-all
52
+ ${className}
53
+ `, children: children }), _jsx(AuthModal, { isOpen: isOpen, onClose: () => {
54
+ console.log('AuthModal closing...');
55
+ setIsOpen(false);
56
+ }, initialMode: mode, onSuccess: () => {
57
+ setIsOpen(false);
58
+ onSuccess?.();
59
+ }, showSocial: showSocial, showEmailPassword: showEmailPassword, branding: branding })] }));
60
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * AuthModal Component
3
+ * Modal dialog for authentication using shadcn Dialog
4
+ */
5
+ import React from 'react';
6
+ export interface AuthModalProps {
7
+ isOpen: boolean;
8
+ onClose: () => void;
9
+ initialMode?: 'signin' | 'signup';
10
+ onSuccess?: () => void;
11
+ onError?: (error: string) => void;
12
+ showSocial?: boolean;
13
+ showEmailPassword?: boolean;
14
+ branding?: {
15
+ logo?: string;
16
+ appName?: string;
17
+ primaryColor?: string;
18
+ };
19
+ }
20
+ export declare function AuthModal({ isOpen, onClose, initialMode, onSuccess, onError, showSocial, showEmailPassword, branding, }: AuthModalProps): React.JSX.Element;
@@ -0,0 +1,62 @@
1
+ /**
2
+ * AuthModal Component
3
+ * Modal dialog for authentication using shadcn Dialog
4
+ */
5
+ 'use client';
6
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
7
+ import { useState } from 'react';
8
+ import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from './ui/dialog';
9
+ import { useHyperyAuth } from '../lib/context';
10
+ export function AuthModal({ isOpen, onClose, initialMode = 'signin', onSuccess, onError, showSocial = true, showEmailPassword = false, branding, }) {
11
+ const { login } = useHyperyAuth();
12
+ const [mode, setMode] = useState(initialMode);
13
+ const [isLoading, setIsLoading] = useState(false);
14
+ const [loadingProvider, setLoadingProvider] = useState(null);
15
+ const [email, setEmail] = useState('');
16
+ const [password, setPassword] = useState('');
17
+ const [name, setName] = useState('');
18
+ const [error, setError] = useState('');
19
+ const handleSocialSignIn = async (provider) => {
20
+ console.log('[AuthModal] Social button clicked:', provider);
21
+ setLoadingProvider(provider);
22
+ setError('');
23
+ try {
24
+ console.log('[AuthModal] Calling login()...');
25
+ await login();
26
+ console.log('[AuthModal] Login completed');
27
+ if (onSuccess) {
28
+ onSuccess();
29
+ }
30
+ }
31
+ catch (err) {
32
+ console.error('[AuthModal] Login failed:', err);
33
+ const errorMessage = err instanceof Error ? err.message : `${provider} sign in failed`;
34
+ setError(errorMessage);
35
+ if (onError) {
36
+ onError(errorMessage);
37
+ }
38
+ setLoadingProvider(null);
39
+ }
40
+ };
41
+ const handleEmailSubmit = (e) => {
42
+ e.preventDefault();
43
+ setError('');
44
+ setIsLoading(true);
45
+ if (!email || !password) {
46
+ setError('Please fill in all fields');
47
+ setIsLoading(false);
48
+ return;
49
+ }
50
+ if (mode === 'signup' && !name) {
51
+ setError('Please enter your name');
52
+ setIsLoading(false);
53
+ return;
54
+ }
55
+ setError('Direct email authentication coming soon. Please use social sign-in.');
56
+ setIsLoading(false);
57
+ };
58
+ const primaryColor = branding?.primaryColor || '#8b5cf6';
59
+ return (_jsx(Dialog, { open: isOpen, onOpenChange: (open) => !open && onClose(), children: _jsxs(DialogContent, { className: "sm:max-w-md bg-white dark:bg-gray-900 text-gray-900 dark:text-white", children: [_jsxs(DialogHeader, { children: [branding?.logo && (_jsx("img", { src: branding.logo, alt: "Logo", className: "h-12 mx-auto mb-4" })), _jsx(DialogTitle, { className: "text-center text-2xl text-gray-900 dark:text-white", children: mode === 'signin' ? 'Welcome back' : 'Create your account' }), _jsx(DialogDescription, { className: "text-center text-sm text-gray-600 dark:text-gray-400", children: mode === 'signin'
60
+ ? `Sign in to ${branding?.appName || 'continue'}`
61
+ : `Get started with ${branding?.appName || 'your account'}` })] }), _jsxs("div", { className: "space-y-6 py-4", children: [error && (_jsx("div", { className: "p-3 rounded-lg bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 text-red-800 dark:text-red-200 text-sm", children: error })), showSocial && (_jsxs("div", { className: "space-y-2", children: [_jsxs("button", { onClick: () => handleSocialSignIn('google'), disabled: !!loadingProvider, className: "w-full bg-white dark:bg-gray-800 hover:bg-gray-50 dark:hover:bg-gray-750 border border-gray-300 dark:border-gray-700 text-gray-900 dark:text-white py-2 px-3 rounded flex items-center justify-center gap-2 text-sm font-medium transition-all disabled:opacity-50", children: [loadingProvider === 'google' ? (_jsx("div", { className: "w-4 h-4 border-2 border-gray-300 border-t-transparent rounded-full animate-spin" })) : (_jsxs("svg", { className: "w-4 h-4", viewBox: "0 0 24 24", children: [_jsx("path", { fill: "#4285F4", 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" }), _jsx("path", { fill: "#34A853", 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" }), _jsx("path", { fill: "#FBBC05", 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" }), _jsx("path", { fill: "#EA4335", 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" })] })), _jsx("span", { children: "Continue with Google" })] }), _jsxs("button", { onClick: () => handleSocialSignIn('github'), disabled: !!loadingProvider, className: "w-full bg-gray-900 dark:bg-gray-800 hover:bg-gray-800 dark:hover:bg-gray-750 text-white py-2 px-3 rounded flex items-center justify-center gap-2 text-sm font-medium transition-all disabled:opacity-50", children: [loadingProvider === 'github' ? (_jsx("div", { className: "w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin" })) : (_jsx("svg", { className: "w-4 h-4", fill: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { fillRule: "evenodd", d: "M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z", clipRule: "evenodd" }) })), _jsx("span", { children: "Continue with GitHub" })] })] })), showSocial && showEmailPassword && (_jsxs("div", { className: "relative", children: [_jsx("div", { className: "absolute inset-0 flex items-center", children: _jsx("div", { className: "w-full border-t border-gray-300 dark:border-gray-700" }) }), _jsx("div", { className: "relative flex justify-center text-xs uppercase", children: _jsx("span", { className: "bg-white dark:bg-gray-900 px-2 text-gray-500 dark:text-gray-400", children: "Or continue with email" }) })] })), showEmailPassword && (_jsxs("form", { onSubmit: handleEmailSubmit, className: "space-y-4", children: [mode === 'signup' && (_jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: "Full name" }), _jsx("input", { type: "text", value: name, onChange: (e) => setName(e.target.value), className: "w-full px-3 py-2 rounded-md border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 text-gray-900 dark:text-white", placeholder: "John Doe", disabled: isLoading })] })), _jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: "Email" }), _jsx("input", { type: "email", value: email, onChange: (e) => setEmail(e.target.value), className: "w-full px-3 py-2 rounded-md border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 text-gray-900 dark:text-white", placeholder: "you@example.com", disabled: isLoading })] }), _jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: "Password" }), _jsx("input", { type: "password", value: password, onChange: (e) => setPassword(e.target.value), className: "w-full px-3 py-2 rounded-md border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 text-gray-900 dark:text-white", placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022", disabled: isLoading })] }), _jsx("button", { type: "submit", disabled: isLoading, className: "w-full py-2 px-4 rounded-md font-medium text-white transition-all", style: { backgroundColor: primaryColor }, children: isLoading ? 'Processing...' : mode === 'signin' ? 'Sign in' : 'Create account' })] })), _jsxs("div", { className: "text-center text-sm", children: [_jsx("span", { className: "text-gray-600 dark:text-gray-400", children: mode === 'signin' ? "Don't have an account?" : 'Already have an account?' }), ' ', _jsx("button", { onClick: () => setMode(mode === 'signin' ? 'signup' : 'signin'), className: "font-medium hover:underline", style: { color: primaryColor }, children: mode === 'signin' ? 'Sign up' : 'Sign in' })] })] })] }) }));
62
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * BuyButton — "buy with Hypery" marketplace checkout button.
3
+ *
4
+ * On click it runs the full auth+charge flow via {@link useCheckout}: it logs
5
+ * the buyer in if needed, charges, and adds a card if none is on file — each
6
+ * step as a popup or a redirect per `config.interactionMode` (`auto` by default).
7
+ * So an unauthenticated visitor can click Buy and be walked through login → card
8
+ * → charge without leaving the page (popup mode). Never throws.
9
+ */
10
+ import type React from "react";
11
+ import { type BuySuccess } from "../hooks/useMarketplace";
12
+ import type { BrandingConfig, ParsedError } from "../types";
13
+ export interface BuyButtonProps {
14
+ /** Seller app the purchase is credited to. */
15
+ appId: string;
16
+ /** Amount to charge the buyer, in cents. */
17
+ amountCents: number;
18
+ /** Optional human-readable description of the purchase. */
19
+ description?: string;
20
+ /** Button label. Defaults to a price-formatted "Buy · $X.XX". */
21
+ label?: React.ReactNode;
22
+ /**
23
+ * Require an explicit per-transaction confirmation before charging (the card
24
+ * is charged off-session, so this gives the buyer a clear "yes"). Defaults to
25
+ * true: the first click arms the button ("Confirm · $X.XX" + Cancel), the
26
+ * second click charges. Set false to charge on a single click.
27
+ */
28
+ requireConfirmation?: boolean;
29
+ /** Called with the successful checkout result. */
30
+ onSuccess?: (result: BuySuccess) => void;
31
+ /** Called with a classified error for failures other than needs-a-card. */
32
+ onError?: (error: ParsedError) => void;
33
+ /** Custom className appended to the button. */
34
+ className?: string;
35
+ /** Optional branding (primaryColor is used as the button color). */
36
+ branding?: BrandingConfig;
37
+ }
38
+ /**
39
+ * Drop-in checkout button for the Hypery marketplace.
40
+ *
41
+ * @example
42
+ * ```tsx
43
+ * <BuyButton appId="app_123" amountCents={499} description="Pro upgrade"
44
+ * onSuccess={(r) => console.log('paid', r.paymentIntentId)} />
45
+ * ```
46
+ */
47
+ export declare function BuyButton({ appId, amountCents, description, label, onSuccess, onError, className, branding, requireConfirmation, }: BuyButtonProps): React.JSX.Element;