@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.
- package/LICENSE +13 -0
- package/README.md +391 -0
- package/dist/components/AuthButton.d.ts +51 -0
- package/dist/components/AuthButton.js +60 -0
- package/dist/components/AuthModal.d.ts +20 -0
- package/dist/components/AuthModal.js +62 -0
- package/dist/components/BuyButton.d.ts +47 -0
- package/dist/components/BuyButton.js +74 -0
- package/dist/components/ErrorBoundary.d.ts +13 -0
- package/dist/components/ErrorBoundary.js +24 -0
- package/dist/components/HyperyModals.d.ts +30 -0
- package/dist/components/HyperyModals.js +30 -0
- package/dist/components/InsufficientCreditsAlert.d.ts +11 -0
- package/dist/components/InsufficientCreditsAlert.js +12 -0
- package/dist/components/ModernAuthForm.d.ts +52 -0
- package/dist/components/ModernAuthForm.js +83 -0
- package/dist/components/RestrictionModal.d.ts +43 -0
- package/dist/components/RestrictionModal.js +167 -0
- package/dist/components/SignIn.d.ts +26 -0
- package/dist/components/SignIn.js +47 -0
- package/dist/components/SignInForm.d.ts +41 -0
- package/dist/components/SignInForm.js +86 -0
- package/dist/components/SignUp.d.ts +26 -0
- package/dist/components/SignUp.js +52 -0
- package/dist/components/SpendingLimitAlert.d.ts +12 -0
- package/dist/components/SpendingLimitAlert.js +14 -0
- package/dist/components/UserButton.d.ts +26 -0
- package/dist/components/UserButton.js +35 -0
- package/dist/components/UserProfile.d.ts +22 -0
- package/dist/components/UserProfile.js +26 -0
- package/dist/components/WorkspaceSwitcher.d.ts +29 -0
- package/dist/components/WorkspaceSwitcher.js +66 -0
- package/dist/components/control.d.ts +64 -0
- package/dist/components/control.js +89 -0
- package/dist/components/ui/dialog.d.ts +19 -0
- package/dist/components/ui/dialog.js +53 -0
- package/dist/hooks/index.d.ts +22 -0
- package/dist/hooks/index.js +23 -0
- package/dist/hooks/useBuyerWallet.d.ts +37 -0
- package/dist/hooks/useBuyerWallet.js +66 -0
- package/dist/hooks/useCheckout.d.ts +27 -0
- package/dist/hooks/useCheckout.js +246 -0
- package/dist/hooks/useError.d.ts +19 -0
- package/dist/hooks/useError.js +36 -0
- package/dist/hooks/useMarketplace.d.ts +50 -0
- package/dist/hooks/useMarketplace.js +69 -0
- package/dist/hooks/useMemberships.d.ts +71 -0
- package/dist/hooks/useMemberships.js +138 -0
- package/dist/hooks/useWallet.d.ts +62 -0
- package/dist/hooks/useWallet.js +123 -0
- package/dist/index.d.ts +55 -0
- package/dist/index.js +52 -0
- package/dist/lib/context.d.ts +50 -0
- package/dist/lib/context.js +409 -0
- package/dist/lib/oauth.d.ts +49 -0
- package/dist/lib/oauth.js +149 -0
- package/dist/lib/parse-error.d.ts +45 -0
- package/dist/lib/parse-error.js +185 -0
- package/dist/lib/parse-stream.d.ts +43 -0
- package/dist/lib/parse-stream.js +89 -0
- package/dist/lib/popup.d.ts +42 -0
- package/dist/lib/popup.js +80 -0
- package/dist/lib/storage.d.ts +43 -0
- package/dist/lib/storage.js +125 -0
- package/dist/lib/utils.d.ts +8 -0
- package/dist/lib/utils.js +11 -0
- package/dist/types/index.d.ts +191 -0
- package/dist/types/index.js +4 -0
- package/package.json +78 -0
- package/src/components/AuthButton.tsx +123 -0
- package/src/components/AuthModal.tsx +240 -0
- package/src/components/BuyButton.tsx +150 -0
- package/src/components/ErrorBoundary.tsx +97 -0
- package/src/components/HyperyModals.tsx +83 -0
- package/src/components/InsufficientCreditsAlert.tsx +73 -0
- package/src/components/ModernAuthForm.tsx +322 -0
- package/src/components/RestrictionModal.tsx +373 -0
- package/src/components/SignIn.tsx +84 -0
- package/src/components/SignInForm.tsx +256 -0
- package/src/components/SignUp.tsx +86 -0
- package/src/components/SpendingLimitAlert.tsx +91 -0
- package/src/components/UserButton.tsx +106 -0
- package/src/components/UserProfile.tsx +106 -0
- package/src/components/WorkspaceSwitcher.tsx +199 -0
- package/src/components/control.tsx +133 -0
- package/src/components/ui/dialog.tsx +151 -0
- package/src/hooks/index.ts +65 -0
- package/src/hooks/useBuyerWallet.ts +117 -0
- package/src/hooks/useCheckout.ts +312 -0
- package/src/hooks/useError.ts +60 -0
- package/src/hooks/useMarketplace.ts +129 -0
- package/src/hooks/useMemberships.ts +203 -0
- package/src/hooks/useWallet.ts +170 -0
- package/src/index.ts +147 -0
- package/src/lib/context.tsx +478 -0
- package/src/lib/oauth.ts +215 -0
- package/src/lib/parse-error.ts +224 -0
- package/src/lib/parse-stream.ts +121 -0
- package/src/lib/popup.ts +98 -0
- package/src/lib/storage.ts +140 -0
- package/src/lib/utils.ts +14 -0
- package/src/types/index.ts +216 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memberships + active-workspace hooks for consumer apps.
|
|
3
|
+
*
|
|
4
|
+
* These power the <WorkspaceSwitcher /> component. They fetch the user's
|
|
5
|
+
* full team → workspace tree from the gateway and let the caller switch
|
|
6
|
+
* the active workspace. The active workspace is stored on the OAuth
|
|
7
|
+
* session server-side so it's persistent across page reloads + devices.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
'use client';
|
|
11
|
+
|
|
12
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
13
|
+
import { useHyperyAuth } from '../lib/context';
|
|
14
|
+
|
|
15
|
+
export interface MembershipWorkspace {
|
|
16
|
+
id: string;
|
|
17
|
+
name: string;
|
|
18
|
+
slug: string;
|
|
19
|
+
isDefault: boolean;
|
|
20
|
+
icon: string | null;
|
|
21
|
+
role: 'owner' | 'admin' | 'developer' | 'viewer';
|
|
22
|
+
isActive: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface MembershipTeam {
|
|
26
|
+
id: string;
|
|
27
|
+
name: string;
|
|
28
|
+
slug: string;
|
|
29
|
+
isPersonal: boolean;
|
|
30
|
+
role: 'owner' | 'admin' | 'developer' | 'viewer';
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface MembershipEntry {
|
|
34
|
+
team: MembershipTeam;
|
|
35
|
+
workspaces: MembershipWorkspace[];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface MembershipsResponse {
|
|
39
|
+
activeOrganizationId: string | null;
|
|
40
|
+
activeWorkspaceId: string | null;
|
|
41
|
+
memberships: MembershipEntry[];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface MembershipsState {
|
|
45
|
+
data: MembershipsResponse | null;
|
|
46
|
+
isLoading: boolean;
|
|
47
|
+
error: string | null;
|
|
48
|
+
reload: () => Promise<void>;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Hook: list every team + workspace the current user belongs to. Returns a
|
|
53
|
+
* flat memberships array that's easy to render in a switcher dropdown.
|
|
54
|
+
*/
|
|
55
|
+
export function useMemberships(): MembershipsState {
|
|
56
|
+
const { isAuthenticated, getAccessToken } = useHyperyAuth();
|
|
57
|
+
const [data, setData] = useState<MembershipsResponse | null>(null);
|
|
58
|
+
const [isLoading, setIsLoading] = useState<boolean>(true);
|
|
59
|
+
const [error, setError] = useState<string | null>(null);
|
|
60
|
+
|
|
61
|
+
// We can't pull the gatewayUrl off the public context value (intentionally),
|
|
62
|
+
// so we rely on the `Origin` header path: in practice every consumer is
|
|
63
|
+
// configured to point its access-token Bearer at the same gateway, so a
|
|
64
|
+
// simple relative fetch on the gateway's domain works. Consumer apps that
|
|
65
|
+
// run on a different origin override this by setting NEXT_PUBLIC_GATEWAY_URL.
|
|
66
|
+
const gatewayUrl =
|
|
67
|
+
(typeof process !== 'undefined' &&
|
|
68
|
+
process.env?.NEXT_PUBLIC_GATEWAY_URL) ||
|
|
69
|
+
'';
|
|
70
|
+
|
|
71
|
+
const load = useCallback(async () => {
|
|
72
|
+
if (!isAuthenticated) {
|
|
73
|
+
setData(null);
|
|
74
|
+
setIsLoading(false);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
setIsLoading(true);
|
|
78
|
+
setError(null);
|
|
79
|
+
try {
|
|
80
|
+
const token = await getAccessToken();
|
|
81
|
+
if (!token) {
|
|
82
|
+
setData(null);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const url = `${gatewayUrl}/api/auth/list_memberships`;
|
|
86
|
+
const res = await fetch(url, {
|
|
87
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
88
|
+
credentials: 'include',
|
|
89
|
+
});
|
|
90
|
+
if (!res.ok) {
|
|
91
|
+
throw new Error(`Memberships request failed: ${res.status}`);
|
|
92
|
+
}
|
|
93
|
+
const body = (await res.json()) as MembershipsResponse;
|
|
94
|
+
// Decorate isActive on each workspace from the response's pointers.
|
|
95
|
+
const decorated: MembershipsResponse = {
|
|
96
|
+
...body,
|
|
97
|
+
memberships: body.memberships.map((m) => ({
|
|
98
|
+
...m,
|
|
99
|
+
workspaces: m.workspaces.map((w) => ({
|
|
100
|
+
...w,
|
|
101
|
+
isActive: w.id === body.activeWorkspaceId,
|
|
102
|
+
})),
|
|
103
|
+
})),
|
|
104
|
+
};
|
|
105
|
+
setData(decorated);
|
|
106
|
+
} catch (err) {
|
|
107
|
+
setError(err instanceof Error ? err.message : 'Unknown error');
|
|
108
|
+
} finally {
|
|
109
|
+
setIsLoading(false);
|
|
110
|
+
}
|
|
111
|
+
}, [isAuthenticated, getAccessToken, gatewayUrl]);
|
|
112
|
+
|
|
113
|
+
useEffect(() => {
|
|
114
|
+
void load();
|
|
115
|
+
}, [load]);
|
|
116
|
+
|
|
117
|
+
return { data, isLoading, error, reload: load };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export interface ActiveWorkspace {
|
|
121
|
+
teamId: string;
|
|
122
|
+
teamName: string;
|
|
123
|
+
workspaceId: string;
|
|
124
|
+
workspaceName: string;
|
|
125
|
+
role: 'owner' | 'admin' | 'developer' | 'viewer';
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Hook: resolves the user's currently-active team + workspace from the
|
|
130
|
+
* server-side OAuth session. Returns null until memberships have loaded.
|
|
131
|
+
*/
|
|
132
|
+
export function useActiveWorkspace(): {
|
|
133
|
+
active: ActiveWorkspace | null;
|
|
134
|
+
isLoading: boolean;
|
|
135
|
+
} {
|
|
136
|
+
const { data, isLoading } = useMemberships();
|
|
137
|
+
if (!data) return { active: null, isLoading };
|
|
138
|
+
|
|
139
|
+
// Prefer the explicit activeWorkspaceId pointer. Fall back to the personal
|
|
140
|
+
// team's default workspace so first-time users see something sensible.
|
|
141
|
+
const flat = data.memberships.flatMap((m) =>
|
|
142
|
+
m.workspaces.map((w) => ({ membership: m, workspace: w })),
|
|
143
|
+
);
|
|
144
|
+
let entry =
|
|
145
|
+
flat.find((e) => e.workspace.id === data.activeWorkspaceId) ?? null;
|
|
146
|
+
if (!entry) {
|
|
147
|
+
const personal = data.memberships.find((m) => m.team.isPersonal);
|
|
148
|
+
const personalDefault = personal?.workspaces.find((w) => w.isDefault);
|
|
149
|
+
if (personal && personalDefault) {
|
|
150
|
+
entry = { membership: personal, workspace: personalDefault };
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (!entry) return { active: null, isLoading: false };
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
isLoading: false,
|
|
157
|
+
active: {
|
|
158
|
+
teamId: entry.membership.team.id,
|
|
159
|
+
teamName: entry.membership.team.name,
|
|
160
|
+
workspaceId: entry.workspace.id,
|
|
161
|
+
workspaceName: entry.workspace.name,
|
|
162
|
+
role: entry.workspace.role,
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Mutation: persist a new active team+workspace on the OAuth session. The
|
|
169
|
+
* gateway updates the session record, future requests' Bearer tokens carry
|
|
170
|
+
* the new claims, and consumer apps see the change on their next render.
|
|
171
|
+
*/
|
|
172
|
+
export async function setActiveWorkspace(
|
|
173
|
+
opts: {
|
|
174
|
+
teamId: string;
|
|
175
|
+
workspaceId: string;
|
|
176
|
+
getAccessToken: () => Promise<string | null>;
|
|
177
|
+
gatewayUrl?: string;
|
|
178
|
+
},
|
|
179
|
+
): Promise<void> {
|
|
180
|
+
const token = await opts.getAccessToken();
|
|
181
|
+
if (!token) throw new Error('No access token; user is not signed in');
|
|
182
|
+
const base =
|
|
183
|
+
opts.gatewayUrl ??
|
|
184
|
+
(typeof process !== 'undefined'
|
|
185
|
+
? process.env?.NEXT_PUBLIC_GATEWAY_URL ?? ''
|
|
186
|
+
: '');
|
|
187
|
+
const res = await fetch(`${base}/api/oauth/session`, {
|
|
188
|
+
method: 'PATCH',
|
|
189
|
+
headers: {
|
|
190
|
+
Authorization: `Bearer ${token}`,
|
|
191
|
+
'Content-Type': 'application/json',
|
|
192
|
+
},
|
|
193
|
+
credentials: 'include',
|
|
194
|
+
body: JSON.stringify({
|
|
195
|
+
activeOrganizationId: opts.teamId,
|
|
196
|
+
activeWorkspaceId: opts.workspaceId,
|
|
197
|
+
}),
|
|
198
|
+
});
|
|
199
|
+
if (!res.ok) {
|
|
200
|
+
const detail = await res.text().catch(() => res.statusText);
|
|
201
|
+
throw new Error(`Failed to switch workspace: ${detail}`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wallet hook for consumer apps.
|
|
3
|
+
*
|
|
4
|
+
* Reads the mode-aware wallet snapshot from the gateway (GET /api/wallet/state)
|
|
5
|
+
* and exposes the two funding actions the embeddable widget needs:
|
|
6
|
+
* - addFunds(usd) → 1-click off-session charge of the card on file
|
|
7
|
+
* - addPaymentMethod() → Stripe-hosted Checkout (setup) in a popup
|
|
8
|
+
*
|
|
9
|
+
* Works in both billing modes; the widget chooses which action to show from
|
|
10
|
+
* `wallet.mode` and `wallet.paymentMethod.exists`.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
'use client';
|
|
14
|
+
|
|
15
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
16
|
+
import { useHyperyAuth } from '../lib/context';
|
|
17
|
+
|
|
18
|
+
export type BillingMode = 'metered' | 'prepaid';
|
|
19
|
+
|
|
20
|
+
export interface WalletTier {
|
|
21
|
+
name: string;
|
|
22
|
+
usdAmount: number;
|
|
23
|
+
credits: number;
|
|
24
|
+
bonus: number;
|
|
25
|
+
popular?: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface WalletState {
|
|
29
|
+
mode: BillingMode;
|
|
30
|
+
balance: {
|
|
31
|
+
current: number;
|
|
32
|
+
reserved: number;
|
|
33
|
+
monthlySpent: number;
|
|
34
|
+
monthlyLimit: number;
|
|
35
|
+
};
|
|
36
|
+
paymentMethod: { exists: boolean; last4?: string; brand?: string };
|
|
37
|
+
autoTopUp: { enabled: boolean; threshold?: number; amount?: number };
|
|
38
|
+
lowBalance: { isLow: boolean; threshold: number; current: number };
|
|
39
|
+
topupTiers: WalletTier[];
|
|
40
|
+
settingsUrls: { billing: string; topup: string; addPaymentMethod: string };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface UseWalletReturn {
|
|
44
|
+
wallet: WalletState | null;
|
|
45
|
+
isLoading: boolean;
|
|
46
|
+
error: string | null;
|
|
47
|
+
reload: () => Promise<void>;
|
|
48
|
+
/** 1-click: charge the card on file for `usd` dollars and credit the team. */
|
|
49
|
+
addFunds: (usd: number) => Promise<void>;
|
|
50
|
+
/** Open Stripe-hosted Checkout to add a card; resolves true once added. */
|
|
51
|
+
addPaymentMethod: () => Promise<boolean>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function resolveGatewayUrl(explicit?: string): string {
|
|
55
|
+
return (
|
|
56
|
+
explicit ||
|
|
57
|
+
(typeof process !== 'undefined' && process.env?.NEXT_PUBLIC_GATEWAY_URL) ||
|
|
58
|
+
''
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function useWallet(opts: { gatewayUrl?: string } = {}): UseWalletReturn {
|
|
63
|
+
const { isAuthenticated, getAccessToken } = useHyperyAuth();
|
|
64
|
+
const gatewayUrl = resolveGatewayUrl(opts.gatewayUrl);
|
|
65
|
+
|
|
66
|
+
const [wallet, setWallet] = useState<WalletState | null>(null);
|
|
67
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
68
|
+
const [error, setError] = useState<string | null>(null);
|
|
69
|
+
|
|
70
|
+
const reload = useCallback(async () => {
|
|
71
|
+
if (!isAuthenticated) {
|
|
72
|
+
setWallet(null);
|
|
73
|
+
setIsLoading(false);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
setIsLoading(true);
|
|
77
|
+
setError(null);
|
|
78
|
+
try {
|
|
79
|
+
const token = await getAccessToken();
|
|
80
|
+
if (!token) {
|
|
81
|
+
setWallet(null);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const res = await fetch(`${gatewayUrl}/api/wallet/state`, {
|
|
85
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
86
|
+
});
|
|
87
|
+
if (!res.ok) throw new Error(`Wallet state request failed: ${res.status}`);
|
|
88
|
+
const body = (await res.json()) as WalletState & { success: boolean };
|
|
89
|
+
setWallet(body);
|
|
90
|
+
} catch (err) {
|
|
91
|
+
setError(err instanceof Error ? err.message : 'Unknown error');
|
|
92
|
+
} finally {
|
|
93
|
+
setIsLoading(false);
|
|
94
|
+
}
|
|
95
|
+
}, [isAuthenticated, getAccessToken, gatewayUrl]);
|
|
96
|
+
|
|
97
|
+
useEffect(() => {
|
|
98
|
+
void reload();
|
|
99
|
+
}, [reload]);
|
|
100
|
+
|
|
101
|
+
const addFunds = useCallback(
|
|
102
|
+
async (usd: number) => {
|
|
103
|
+
const token = await getAccessToken();
|
|
104
|
+
if (!token) throw new Error('Not authenticated');
|
|
105
|
+
const res = await fetch(`${gatewayUrl}/api/wallet/topup`, {
|
|
106
|
+
method: 'POST',
|
|
107
|
+
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
|
|
108
|
+
body: JSON.stringify({ amount: usd }),
|
|
109
|
+
});
|
|
110
|
+
const body = await res.json().catch(() => ({}));
|
|
111
|
+
if (!res.ok || !body?.success) {
|
|
112
|
+
throw new Error(body?.error || `Top-up failed: ${res.status}`);
|
|
113
|
+
}
|
|
114
|
+
await reload();
|
|
115
|
+
},
|
|
116
|
+
[getAccessToken, gatewayUrl, reload]
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
const addPaymentMethod = useCallback(async (): Promise<boolean> => {
|
|
120
|
+
const token = await getAccessToken();
|
|
121
|
+
if (!token) throw new Error('Not authenticated');
|
|
122
|
+
|
|
123
|
+
const res = await fetch(`${gatewayUrl}/api/payments/stripe/checkout-setup`, {
|
|
124
|
+
method: 'POST',
|
|
125
|
+
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
|
|
126
|
+
body: JSON.stringify({}),
|
|
127
|
+
});
|
|
128
|
+
const body = await res.json().catch(() => ({}));
|
|
129
|
+
if (!res.ok || !body?.url) {
|
|
130
|
+
throw new Error(body?.error || `Could not start checkout: ${res.status}`);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const popup =
|
|
134
|
+
typeof window !== 'undefined'
|
|
135
|
+
? window.open(body.url, 'hypery-add-card', 'width=480,height=720')
|
|
136
|
+
: null;
|
|
137
|
+
|
|
138
|
+
// Resolve when the hosted-Checkout return page messages us, or when the
|
|
139
|
+
// popup closes (fall back to a state reload to detect the new card).
|
|
140
|
+
return new Promise<boolean>((resolve) => {
|
|
141
|
+
let settled = false;
|
|
142
|
+
const finish = async (added: boolean) => {
|
|
143
|
+
if (settled) return;
|
|
144
|
+
settled = true;
|
|
145
|
+
window.removeEventListener('message', onMessage);
|
|
146
|
+
clearInterval(poll);
|
|
147
|
+
await reload();
|
|
148
|
+
resolve(added);
|
|
149
|
+
};
|
|
150
|
+
// Only trust the completion message from the gateway's own origin.
|
|
151
|
+
const expectedOrigin = gatewayUrl
|
|
152
|
+
? new URL(gatewayUrl).origin
|
|
153
|
+
: typeof window !== 'undefined'
|
|
154
|
+
? window.location.origin
|
|
155
|
+
: '';
|
|
156
|
+
const onMessage = (event: MessageEvent) => {
|
|
157
|
+
if (event.origin !== expectedOrigin) return;
|
|
158
|
+
if (event?.data?.type === 'hypery:payment-method') {
|
|
159
|
+
void finish(event.data.status === 'added');
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
window.addEventListener('message', onMessage);
|
|
163
|
+
const poll = setInterval(() => {
|
|
164
|
+
if (popup && popup.closed) void finish(false);
|
|
165
|
+
}, 700);
|
|
166
|
+
});
|
|
167
|
+
}, [getAccessToken, gatewayUrl, reload]);
|
|
168
|
+
|
|
169
|
+
return { wallet, isLoading, error, reload, addFunds, addPaymentMethod };
|
|
170
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @hyperyai/sdk
|
|
3
|
+
* Drop-in authentication and error handling library for Hypery apps
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```tsx
|
|
7
|
+
* import { HyperyProvider, useUser, SignedIn, SignedOut } from '@hyperyai/sdk';
|
|
8
|
+
*
|
|
9
|
+
* <HyperyProvider config={{ ... }}>
|
|
10
|
+
* <SignedIn>
|
|
11
|
+
* <App />
|
|
12
|
+
* </SignedIn>
|
|
13
|
+
* <SignedOut>
|
|
14
|
+
* <SignIn />
|
|
15
|
+
* </SignedOut>
|
|
16
|
+
* </HyperyProvider>
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
// Provider & Hooks
|
|
21
|
+
export { HyperyProvider, useHyperyAuth, useUser } from './lib/context';
|
|
22
|
+
export {
|
|
23
|
+
useAuth,
|
|
24
|
+
useMemberships,
|
|
25
|
+
useActiveWorkspace,
|
|
26
|
+
setActiveWorkspace,
|
|
27
|
+
useWallet,
|
|
28
|
+
useBuyerWallet,
|
|
29
|
+
useMarketplace,
|
|
30
|
+
useCheckout,
|
|
31
|
+
} from './hooks';
|
|
32
|
+
export type {
|
|
33
|
+
MembershipEntry,
|
|
34
|
+
MembershipTeam,
|
|
35
|
+
MembershipWorkspace,
|
|
36
|
+
MembershipsResponse,
|
|
37
|
+
ActiveWorkspace,
|
|
38
|
+
BillingMode,
|
|
39
|
+
WalletState,
|
|
40
|
+
WalletTier,
|
|
41
|
+
UseWalletReturn,
|
|
42
|
+
BuyerPaymentMethod,
|
|
43
|
+
AddCardOptions,
|
|
44
|
+
UseBuyerWalletReturn,
|
|
45
|
+
BuyInput,
|
|
46
|
+
BuySuccess,
|
|
47
|
+
BuyFailure,
|
|
48
|
+
BuyResult,
|
|
49
|
+
UseMarketplaceReturn,
|
|
50
|
+
CheckoutInput,
|
|
51
|
+
CheckoutStatus,
|
|
52
|
+
CheckoutResult,
|
|
53
|
+
UseCheckoutReturn,
|
|
54
|
+
} from './hooks';
|
|
55
|
+
|
|
56
|
+
// Auth Components
|
|
57
|
+
export { SignIn } from './components/SignIn';
|
|
58
|
+
export { SignUp } from './components/SignUp';
|
|
59
|
+
export { SignInForm } from './components/SignInForm';
|
|
60
|
+
export { UserButton } from './components/UserButton';
|
|
61
|
+
export { UserProfile } from './components/UserProfile';
|
|
62
|
+
export { SignedIn, SignedOut, RedirectToSignIn, Protect } from './components/control';
|
|
63
|
+
export { AuthModal } from './components/AuthModal';
|
|
64
|
+
export type { AuthModalProps } from './components/AuthModal';
|
|
65
|
+
export { AuthButton } from './components/AuthButton';
|
|
66
|
+
export type { AuthButtonProps } from './components/AuthButton';
|
|
67
|
+
export { ModernAuthForm } from './components/ModernAuthForm';
|
|
68
|
+
export type { ModernAuthFormProps } from './components/ModernAuthForm';
|
|
69
|
+
export { WorkspaceSwitcher } from './components/WorkspaceSwitcher';
|
|
70
|
+
export type { WorkspaceSwitcherProps } from './components/WorkspaceSwitcher';
|
|
71
|
+
|
|
72
|
+
// "Buy with Hypery" marketplace checkout button.
|
|
73
|
+
export { BuyButton } from './components/BuyButton';
|
|
74
|
+
export type { BuyButtonProps } from './components/BuyButton';
|
|
75
|
+
|
|
76
|
+
// Error Components
|
|
77
|
+
export { RestrictionModal } from './components/RestrictionModal';
|
|
78
|
+
export type { RestrictionError } from './components/RestrictionModal';
|
|
79
|
+
|
|
80
|
+
// Turnkey modal host — mount once for automatic billing/auth modals driven by
|
|
81
|
+
// authenticatedFetch (issue #42).
|
|
82
|
+
export { HyperyModals } from './components/HyperyModals';
|
|
83
|
+
export type { HyperyModalsProps } from './components/HyperyModals';
|
|
84
|
+
|
|
85
|
+
export { SpendingLimitAlert } from './components/SpendingLimitAlert';
|
|
86
|
+
export type { SpendingLimitAlertProps } from './components/SpendingLimitAlert';
|
|
87
|
+
|
|
88
|
+
export { InsufficientCreditsAlert } from './components/InsufficientCreditsAlert';
|
|
89
|
+
export type { InsufficientCreditsAlertProps } from './components/InsufficientCreditsAlert';
|
|
90
|
+
|
|
91
|
+
export { ErrorBoundary } from './components/ErrorBoundary';
|
|
92
|
+
export type { ErrorBoundaryProps } from './components/ErrorBoundary';
|
|
93
|
+
|
|
94
|
+
// Error Hook
|
|
95
|
+
export { useError } from './hooks/useError';
|
|
96
|
+
export type { UseErrorReturn } from './hooks/useError';
|
|
97
|
+
|
|
98
|
+
// Types
|
|
99
|
+
export type {
|
|
100
|
+
BrandingConfig,
|
|
101
|
+
HyperyAuthConfig,
|
|
102
|
+
InteractionMode,
|
|
103
|
+
ResolvedMode,
|
|
104
|
+
PopupAuthResult,
|
|
105
|
+
User,
|
|
106
|
+
AuthTokens,
|
|
107
|
+
AuthState,
|
|
108
|
+
AuthContextValue,
|
|
109
|
+
SpendingLimitErrorData,
|
|
110
|
+
InsufficientCreditsErrorData,
|
|
111
|
+
PaymentMethodRequiredErrorData,
|
|
112
|
+
PaymentDeclinedErrorData,
|
|
113
|
+
AuthenticationErrorData,
|
|
114
|
+
GenericErrorData,
|
|
115
|
+
ErrorData,
|
|
116
|
+
ErrorResponse,
|
|
117
|
+
ParsedError,
|
|
118
|
+
} from './types';
|
|
119
|
+
|
|
120
|
+
// Utilities (for advanced usage)
|
|
121
|
+
export { TokenStorage } from './lib/storage';
|
|
122
|
+
export {
|
|
123
|
+
getAuthorizationUrl,
|
|
124
|
+
exchangeCodeForToken,
|
|
125
|
+
refreshAccessToken,
|
|
126
|
+
getUserInfo,
|
|
127
|
+
} from './lib/oauth';
|
|
128
|
+
|
|
129
|
+
// Error Utilities
|
|
130
|
+
export {
|
|
131
|
+
parseError,
|
|
132
|
+
isSpendingLimitError,
|
|
133
|
+
isInsufficientCreditsError,
|
|
134
|
+
isPaymentMethodRequiredError,
|
|
135
|
+
isPaymentDeclinedError,
|
|
136
|
+
isAuthError,
|
|
137
|
+
isPermissionDeniedError,
|
|
138
|
+
isRateLimitError,
|
|
139
|
+
isBillingRestriction,
|
|
140
|
+
formatTimeUntilReset,
|
|
141
|
+
} from './lib/parse-error';
|
|
142
|
+
|
|
143
|
+
// Streaming (SSE) error classification — surface a terminal mid-stream error in
|
|
144
|
+
// the same classified shape as a non-streaming failure (issue #44).
|
|
145
|
+
export { parseSSEError, parseSSEFrame, consumeSSEStream } from './lib/parse-stream';
|
|
146
|
+
export type { SSEEvent, SSEStreamHandlers } from './lib/parse-stream';
|
|
147
|
+
|