@reauth-dev/sdk 0.2.0 → 0.3.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.
@@ -1,336 +0,0 @@
1
- import {
2
- DEFAULT_TIMEOUT_MS
3
- } from "./chunk-EY5LQCDG.mjs";
4
-
5
- // src/client.ts
6
- function assertHttpsUrl(url) {
7
- let parsed;
8
- try {
9
- parsed = new URL(url);
10
- } catch {
11
- throw new Error("URL must use HTTPS");
12
- }
13
- if (parsed.protocol !== "https:") {
14
- throw new Error("URL must use HTTPS");
15
- }
16
- }
17
- function createReauthClient(config) {
18
- const { domain } = config;
19
- if (config.timeout !== void 0 && (!Number.isFinite(config.timeout) || config.timeout <= 0)) {
20
- throw new Error("timeout must be a positive finite number in milliseconds");
21
- }
22
- const timeoutMs = config.timeout ?? DEFAULT_TIMEOUT_MS;
23
- const baseUrl = `https://reauth.${domain}/api/public`;
24
- return {
25
- /**
26
- * Redirect the user to the reauth.dev login page.
27
- * After successful login, they'll be redirected back to your configured redirect URL.
28
- */
29
- login() {
30
- if (typeof window === "undefined") {
31
- throw new Error("login() can only be called in browser");
32
- }
33
- window.location.href = `https://reauth.${domain}/`;
34
- },
35
- /**
36
- * Check if the user is authenticated.
37
- * Returns session info including user ID, email, and roles.
38
- */
39
- async getSession() {
40
- const res = await fetch(`${baseUrl}/auth/session`, {
41
- credentials: "include",
42
- signal: AbortSignal.timeout(timeoutMs)
43
- });
44
- if (!res.ok) {
45
- throw new Error(`Failed to get session: ${res.status}`);
46
- }
47
- return res.json();
48
- },
49
- /**
50
- * Refresh the access token using the refresh token.
51
- * Call this when getSession() returns valid: false but no error_code.
52
- * @throws Error on failed refresh (401) or server error
53
- */
54
- async refresh() {
55
- const res = await fetch(`${baseUrl}/auth/refresh`, {
56
- method: "POST",
57
- credentials: "include",
58
- signal: AbortSignal.timeout(timeoutMs)
59
- });
60
- if (!res.ok) {
61
- throw new Error(`Failed to refresh: ${res.status}`);
62
- }
63
- },
64
- /**
65
- * Get an access token for Bearer authentication.
66
- * Use this when calling your own API that uses local token verification.
67
- *
68
- * @returns TokenResponse with access token, or null if not authenticated or network unreachable
69
- * @throws Error on server errors (non-401 HTTP status codes) or request timeout
70
- *
71
- * @example
72
- * ```typescript
73
- * try {
74
- * const tokenResponse = await reauth.getToken();
75
- * if (tokenResponse) {
76
- * fetch('/api/data', {
77
- * headers: { Authorization: `Bearer ${tokenResponse.accessToken}` }
78
- * });
79
- * }
80
- * } catch (err) {
81
- * // Server error or timeout — do not log the user out
82
- * }
83
- * ```
84
- */
85
- async getToken() {
86
- try {
87
- const res = await fetch(`${baseUrl}/auth/token`, {
88
- method: "GET",
89
- credentials: "include",
90
- signal: AbortSignal.timeout(timeoutMs)
91
- });
92
- if (!res.ok) {
93
- if (res.status === 401) return null;
94
- throw new Error(`Failed to get token: ${res.status}`);
95
- }
96
- const data = await res.json();
97
- return {
98
- accessToken: data.access_token,
99
- expiresIn: data.expires_in,
100
- tokenType: data.token_type
101
- };
102
- } catch (err) {
103
- if (err instanceof TypeError) return null;
104
- throw err;
105
- }
106
- },
107
- /**
108
- * Log out the user by clearing all session cookies.
109
- * @throws Error on server error
110
- */
111
- async logout() {
112
- const res = await fetch(`${baseUrl}/auth/logout`, {
113
- method: "POST",
114
- credentials: "include",
115
- signal: AbortSignal.timeout(timeoutMs)
116
- });
117
- if (!res.ok) {
118
- throw new Error(`Failed to logout: ${res.status}`);
119
- }
120
- },
121
- /**
122
- * Delete the user's own account (self-service).
123
- * @throws Error on permission denied or server error
124
- */
125
- async deleteAccount() {
126
- const res = await fetch(`${baseUrl}/auth/account`, {
127
- method: "DELETE",
128
- credentials: "include",
129
- signal: AbortSignal.timeout(timeoutMs)
130
- });
131
- if (!res.ok) {
132
- throw new Error(`Failed to delete account: ${res.status}`);
133
- }
134
- },
135
- // ========================================================================
136
- // Billing Methods
137
- // ========================================================================
138
- /**
139
- * Get available subscription plans for the domain.
140
- * Only returns public plans sorted by display order.
141
- * @throws Error on server error
142
- */
143
- async getPlans() {
144
- const res = await fetch(`${baseUrl}/billing/plans`, {
145
- credentials: "include",
146
- signal: AbortSignal.timeout(timeoutMs)
147
- });
148
- if (!res.ok) {
149
- throw new Error(`Failed to get plans: ${res.status}`);
150
- }
151
- const data = await res.json();
152
- return data.map(
153
- (p) => ({
154
- id: p.id,
155
- code: p.code,
156
- name: p.name,
157
- description: p.description,
158
- priceCents: p.price_cents,
159
- currency: p.currency,
160
- interval: p.interval,
161
- intervalCount: p.interval_count,
162
- trialDays: p.trial_days,
163
- features: p.features.map((f) => ({
164
- code: f.code,
165
- name: f.name,
166
- featureType: f.feature_type,
167
- numericValue: f.numeric_value,
168
- unitLabel: f.unit_label
169
- })),
170
- displayOrder: p.display_order,
171
- creditsAmount: p.credits_amount,
172
- planType: p.plan_type,
173
- contactUrl: p.contact_url
174
- })
175
- );
176
- },
177
- /**
178
- * Get the current user's subscription status.
179
- */
180
- async getSubscription() {
181
- const res = await fetch(`${baseUrl}/billing/subscription`, {
182
- credentials: "include",
183
- signal: AbortSignal.timeout(timeoutMs)
184
- });
185
- if (!res.ok) {
186
- throw new Error(`Failed to get subscription: ${res.status}`);
187
- }
188
- const data = await res.json();
189
- return {
190
- id: data.id,
191
- planCode: data.plan_code,
192
- planName: data.plan_name,
193
- status: data.status,
194
- currentPeriodEnd: data.current_period_end,
195
- trialEnd: data.trial_end,
196
- cancelAtPeriodEnd: data.cancel_at_period_end
197
- };
198
- },
199
- /**
200
- * Create a Stripe checkout session to subscribe to a plan.
201
- * @param planCode The plan code to subscribe to
202
- * @param successUrl URL to redirect to after successful payment
203
- * @param cancelUrl URL to redirect to if checkout is canceled
204
- * @returns Checkout session with URL to redirect the user to
205
- */
206
- async createCheckout(planCode, successUrl, cancelUrl) {
207
- const res = await fetch(`${baseUrl}/billing/checkout`, {
208
- method: "POST",
209
- headers: { "Content-Type": "application/json" },
210
- credentials: "include",
211
- signal: AbortSignal.timeout(timeoutMs),
212
- body: JSON.stringify({
213
- plan_code: planCode,
214
- success_url: successUrl,
215
- cancel_url: cancelUrl
216
- })
217
- });
218
- if (!res.ok) {
219
- const err = await res.json().catch(() => ({}));
220
- throw new Error(err.message || "Failed to create checkout session");
221
- }
222
- const data = await res.json();
223
- return { checkoutUrl: data.checkout_url };
224
- },
225
- /**
226
- * Redirect user to subscribe to a plan.
227
- * Creates a checkout session and redirects to Stripe.
228
- * @param planCode The plan code to subscribe to
229
- */
230
- async subscribe(planCode) {
231
- if (typeof window === "undefined") {
232
- throw new Error("subscribe() can only be called in browser");
233
- }
234
- const currentUrl = window.location.href;
235
- const { checkoutUrl } = await this.createCheckout(
236
- planCode,
237
- currentUrl,
238
- currentUrl
239
- );
240
- assertHttpsUrl(checkoutUrl);
241
- window.location.href = checkoutUrl;
242
- },
243
- /**
244
- * Open the Stripe customer portal for managing subscription.
245
- * @param returnUrl URL to return to after leaving the portal
246
- */
247
- async openBillingPortal(returnUrl) {
248
- if (typeof window === "undefined") {
249
- throw new Error("openBillingPortal() can only be called in browser");
250
- }
251
- const res = await fetch(`${baseUrl}/billing/portal`, {
252
- method: "POST",
253
- headers: { "Content-Type": "application/json" },
254
- credentials: "include",
255
- signal: AbortSignal.timeout(timeoutMs),
256
- body: JSON.stringify({
257
- return_url: returnUrl || window.location.href
258
- })
259
- });
260
- if (!res.ok) {
261
- throw new Error("Failed to open billing portal");
262
- }
263
- const data = await res.json();
264
- assertHttpsUrl(data.portal_url);
265
- window.location.href = data.portal_url;
266
- },
267
- /**
268
- * Cancel the user's subscription at period end.
269
- * @throws Error on failure or server error
270
- */
271
- async cancelSubscription() {
272
- const res = await fetch(`${baseUrl}/billing/cancel`, {
273
- method: "POST",
274
- credentials: "include",
275
- signal: AbortSignal.timeout(timeoutMs)
276
- });
277
- if (!res.ok) {
278
- throw new Error(`Failed to cancel subscription: ${res.status}`);
279
- }
280
- },
281
- // ========================================================================
282
- // Balance Methods
283
- // ========================================================================
284
- /**
285
- * Get the current user's balance.
286
- * @returns Object with the current balance
287
- */
288
- async getBalance() {
289
- const res = await fetch(`${baseUrl}/balance`, {
290
- credentials: "include",
291
- signal: AbortSignal.timeout(timeoutMs)
292
- });
293
- if (!res.ok) {
294
- if (res.status === 401) throw new Error("Not authenticated");
295
- throw new Error(`Failed to get balance: ${res.status}`);
296
- }
297
- const data = await res.json();
298
- return { balance: data.balance };
299
- },
300
- /**
301
- * Get the current user's balance transaction history.
302
- * @param opts - Optional pagination (limit, offset)
303
- * @returns Object with array of transactions (newest first)
304
- */
305
- async getTransactions(opts) {
306
- const params = new URLSearchParams();
307
- if (opts?.limit !== void 0) params.set("limit", String(opts.limit));
308
- if (opts?.offset !== void 0) params.set("offset", String(opts.offset));
309
- const qs = params.toString();
310
- const res = await fetch(
311
- `${baseUrl}/balance/transactions${qs ? `?${qs}` : ""}`,
312
- { credentials: "include", signal: AbortSignal.timeout(timeoutMs) }
313
- );
314
- if (!res.ok) {
315
- if (res.status === 401) throw new Error("Not authenticated");
316
- throw new Error(`Failed to get transactions: ${res.status}`);
317
- }
318
- const data = await res.json();
319
- return {
320
- transactions: data.transactions.map(
321
- (t) => ({
322
- id: t.id,
323
- amountDelta: t.amount_delta,
324
- reason: t.reason,
325
- balanceAfter: t.balance_after,
326
- createdAt: t.created_at
327
- })
328
- )
329
- };
330
- }
331
- };
332
- }
333
-
334
- export {
335
- createReauthClient
336
- };