@genation/sdk 0.1.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.
@@ -0,0 +1,626 @@
1
+ class w extends Error {
2
+ code;
3
+ cause;
4
+ constructor(e, t, r) {
5
+ super(e), this.name = "GenationError", this.code = t, this.cause = r;
6
+ }
7
+ }
8
+ class h extends w {
9
+ constructor(e, t, r) {
10
+ super(e, t, r), this.name = "AuthError";
11
+ }
12
+ static invalidGrant(e = "Invalid authorization code or refresh token") {
13
+ return new h(e, "invalid_grant");
14
+ }
15
+ static accessDenied(e = "User denied access") {
16
+ return new h(e, "access_denied");
17
+ }
18
+ static expiredToken(e = "Token has expired") {
19
+ return new h(e, "expired_token");
20
+ }
21
+ static invalidState(e = "State mismatch, possible CSRF attack") {
22
+ return new h(e, "invalid_state");
23
+ }
24
+ static pkceVerificationFailed(e = "PKCE verification failed") {
25
+ return new h(e, "pkce_verification_failed");
26
+ }
27
+ }
28
+ class c extends w {
29
+ status;
30
+ constructor(e, t, r) {
31
+ super(e, "network_error", r), this.name = "NetworkError", this.status = t;
32
+ }
33
+ static fromResponse(e) {
34
+ return new c(
35
+ `HTTP ${e.status}: ${e.statusText}`,
36
+ e.status
37
+ );
38
+ }
39
+ }
40
+ class u extends w {
41
+ constructor(e) {
42
+ super(e, "config_error"), this.name = "ConfigError";
43
+ }
44
+ static missingField(e) {
45
+ return new u(`Missing required config field: ${e}`);
46
+ }
47
+ }
48
+ class p {
49
+ baseUrl;
50
+ timeout;
51
+ constructor(e) {
52
+ this.baseUrl = e.baseUrl.replace(/\/$/, ""), this.timeout = e.timeout ?? 3e4;
53
+ }
54
+ /**
55
+ * Make an HTTP request
56
+ */
57
+ async request(e, t = {}) {
58
+ const { method: r = "GET", headers: n = {}, body: a, params: l } = t;
59
+ let o = `${this.baseUrl}${e}`;
60
+ if (l) {
61
+ const i = new URLSearchParams(l);
62
+ o += `?${i.toString()}`;
63
+ }
64
+ const m = new AbortController(), y = setTimeout(() => m.abort(), this.timeout);
65
+ try {
66
+ const i = await fetch(o, {
67
+ method: r,
68
+ headers: {
69
+ "Content-Type": "application/json",
70
+ ...n
71
+ },
72
+ body: a ? JSON.stringify(a) : void 0,
73
+ signal: m.signal
74
+ });
75
+ if (clearTimeout(y), !i.ok)
76
+ throw c.fromResponse(i);
77
+ return await i.json();
78
+ } catch (i) {
79
+ throw clearTimeout(y), i instanceof c ? i : i instanceof Error && i.name === "AbortError" ? new c("Request timeout", void 0, i) : new c("Network request failed", void 0, i);
80
+ }
81
+ }
82
+ /**
83
+ * POST request with form data (for OAuth token exchange)
84
+ */
85
+ async postForm(e, t, r = {}) {
86
+ const n = `${this.baseUrl}${e}`, a = new AbortController(), l = setTimeout(() => a.abort(), this.timeout);
87
+ try {
88
+ const o = await fetch(n, {
89
+ method: "POST",
90
+ headers: {
91
+ "Content-Type": "application/x-www-form-urlencoded",
92
+ ...r
93
+ },
94
+ body: new URLSearchParams(t).toString(),
95
+ signal: a.signal
96
+ });
97
+ if (clearTimeout(l), !o.ok)
98
+ throw c.fromResponse(o);
99
+ return await o.json();
100
+ } catch (o) {
101
+ throw clearTimeout(l), o instanceof c ? o : new c("Network request failed", void 0, o);
102
+ }
103
+ }
104
+ }
105
+ function k(s) {
106
+ return btoa(String.fromCharCode(...s)).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
107
+ }
108
+ function T() {
109
+ const s = new Uint8Array(32);
110
+ return crypto.getRandomValues(s), k(s);
111
+ }
112
+ async function b(s) {
113
+ const t = new TextEncoder().encode(s), r = await crypto.subtle.digest("SHA-256", t);
114
+ return k(new Uint8Array(r));
115
+ }
116
+ async function v() {
117
+ const s = T(), e = await b(s);
118
+ return {
119
+ codeVerifier: s,
120
+ codeChallenge: e,
121
+ codeChallengeMethod: "S256"
122
+ };
123
+ }
124
+ function _() {
125
+ const s = new Uint8Array(16);
126
+ return crypto.getRandomValues(s), k(s);
127
+ }
128
+ const d = "tokens", g = "pkce", f = "state";
129
+ class C {
130
+ storage;
131
+ constructor(e) {
132
+ this.storage = e;
133
+ }
134
+ /**
135
+ * Store token set
136
+ */
137
+ async setTokens(e) {
138
+ await this.storage.set(d, JSON.stringify(e));
139
+ }
140
+ /**
141
+ * Get stored tokens
142
+ */
143
+ async getTokens() {
144
+ const e = await this.storage.get(d);
145
+ if (!e) return null;
146
+ try {
147
+ return JSON.parse(e);
148
+ } catch {
149
+ return null;
150
+ }
151
+ }
152
+ /**
153
+ * Clear stored tokens
154
+ */
155
+ async clearTokens() {
156
+ await this.storage.remove(d);
157
+ }
158
+ /**
159
+ * Check if access token is expired
160
+ */
161
+ async isTokenExpired() {
162
+ const e = await this.getTokens();
163
+ if (!e) return !0;
164
+ const t = e.issuedAt + e.expiresIn * 1e3;
165
+ return Date.now() > t - 6e4;
166
+ }
167
+ /**
168
+ * Store PKCE verifier for later validation
169
+ */
170
+ async setPKCE(e) {
171
+ await this.storage.set(g, e);
172
+ }
173
+ /**
174
+ * Get and clear stored PKCE verifier
175
+ */
176
+ async consumePKCE() {
177
+ const e = await this.storage.get(g);
178
+ return e && await this.storage.remove(g), e;
179
+ }
180
+ /**
181
+ * Store state for CSRF validation
182
+ */
183
+ async setState(e) {
184
+ await this.storage.set(f, e);
185
+ }
186
+ /**
187
+ * Get and clear stored state
188
+ */
189
+ async consumeState() {
190
+ const e = await this.storage.get(f);
191
+ return e && await this.storage.remove(f), e;
192
+ }
193
+ /**
194
+ * Clear all auth-related data
195
+ */
196
+ async clearAll() {
197
+ await this.storage.clear();
198
+ }
199
+ }
200
+ const I = "https://mnnoheowoowbtpuoguul.supabase.co/auth/v1";
201
+ class U {
202
+ config;
203
+ http;
204
+ tokenManager;
205
+ constructor(e, t) {
206
+ this.config = {
207
+ clientId: e.clientId,
208
+ clientSecret: e.clientSecret,
209
+ redirectUri: e.redirectUri,
210
+ scopes: e.scopes,
211
+ authUrl: e.authUrl ?? I
212
+ }, this.http = new p({ baseUrl: this.config.authUrl }), this.tokenManager = t;
213
+ }
214
+ /**
215
+ * Generate authorization URL for OAuth flow
216
+ * Stores PKCE verifier and state for later validation
217
+ */
218
+ async getAuthorizationUrl() {
219
+ const e = await v(), t = _();
220
+ await this.tokenManager.setPKCE(e.codeVerifier), await this.tokenManager.setState(t);
221
+ const r = new URLSearchParams({
222
+ response_type: "code",
223
+ client_id: this.config.clientId,
224
+ redirect_uri: this.config.redirectUri,
225
+ state: t,
226
+ code_challenge: e.codeChallenge,
227
+ code_challenge_method: e.codeChallengeMethod
228
+ });
229
+ return this.config.scopes && this.config.scopes.length > 0 && r.append("scope", this.config.scopes.join(" ")), `${this.config.authUrl}/oauth/authorize?${r.toString()}`;
230
+ }
231
+ /**
232
+ * Exchange authorization code for tokens
233
+ */
234
+ async exchangeCode(e, t) {
235
+ const r = await this.tokenManager.consumeState();
236
+ if (!r || r !== t)
237
+ throw h.invalidState();
238
+ const n = await this.tokenManager.consumePKCE();
239
+ if (!n)
240
+ throw h.pkceVerificationFailed("Missing code verifier");
241
+ const a = await this.http.postForm(
242
+ "/oauth/token",
243
+ {
244
+ grant_type: "authorization_code",
245
+ code: e,
246
+ redirect_uri: this.config.redirectUri,
247
+ client_id: this.config.clientId,
248
+ client_secret: this.config.clientSecret,
249
+ code_verifier: n
250
+ }
251
+ ), l = this.mapTokenResponse(a);
252
+ return await this.tokenManager.setTokens(l), l;
253
+ }
254
+ /**
255
+ * Refresh access token using refresh token
256
+ */
257
+ async refreshToken() {
258
+ const e = await this.tokenManager.getTokens();
259
+ if (!e?.refreshToken)
260
+ throw h.invalidGrant("No refresh token available");
261
+ const t = await this.http.postForm(
262
+ "/oauth/token",
263
+ {
264
+ grant_type: "refresh_token",
265
+ refresh_token: e.refreshToken,
266
+ client_id: this.config.clientId,
267
+ client_secret: this.config.clientSecret
268
+ }
269
+ ), r = this.mapTokenResponse(t);
270
+ return await this.tokenManager.setTokens(r), r;
271
+ }
272
+ /**
273
+ * Revoke current tokens
274
+ */
275
+ async revokeToken() {
276
+ const e = await this.tokenManager.getTokens();
277
+ if (e)
278
+ try {
279
+ await this.http.postForm("/oauth/revoke", {
280
+ token: e.accessToken,
281
+ client_id: this.config.clientId,
282
+ client_secret: this.config.clientSecret
283
+ });
284
+ } finally {
285
+ await this.tokenManager.clearTokens();
286
+ }
287
+ }
288
+ /**
289
+ * Map OAuth token response to TokenSet
290
+ */
291
+ mapTokenResponse(e) {
292
+ return {
293
+ accessToken: e.access_token,
294
+ refreshToken: e.refresh_token,
295
+ tokenType: e.token_type,
296
+ expiresIn: e.expires_in,
297
+ issuedAt: Date.now(),
298
+ scope: e.scope
299
+ };
300
+ }
301
+ }
302
+ class x {
303
+ store = /* @__PURE__ */ new Map();
304
+ async get(e) {
305
+ return this.store.get(e) ?? null;
306
+ }
307
+ async set(e, t) {
308
+ this.store.set(e, t);
309
+ }
310
+ async remove(e) {
311
+ this.store.delete(e);
312
+ }
313
+ async clear() {
314
+ this.store.clear();
315
+ }
316
+ }
317
+ class S {
318
+ prefix;
319
+ constructor(e = "genation") {
320
+ this.prefix = e;
321
+ }
322
+ getKey(e) {
323
+ return `${this.prefix}:${e}`;
324
+ }
325
+ async get(e) {
326
+ return typeof window > "u" ? null : localStorage.getItem(this.getKey(e));
327
+ }
328
+ async set(e, t) {
329
+ typeof window > "u" || localStorage.setItem(this.getKey(e), t);
330
+ }
331
+ async remove(e) {
332
+ typeof window > "u" || localStorage.removeItem(this.getKey(e));
333
+ }
334
+ async clear() {
335
+ if (typeof window > "u") return;
336
+ Object.keys(localStorage).filter(
337
+ (t) => t.startsWith(`${this.prefix}:`)
338
+ ).forEach((t) => localStorage.removeItem(t));
339
+ }
340
+ }
341
+ class E {
342
+ prefix;
343
+ constructor(e = "genation") {
344
+ this.prefix = e;
345
+ }
346
+ getKey(e) {
347
+ return `${this.prefix}:${e}`;
348
+ }
349
+ async get(e) {
350
+ return typeof window > "u" ? null : sessionStorage.getItem(this.getKey(e));
351
+ }
352
+ async set(e, t) {
353
+ typeof window > "u" || sessionStorage.setItem(this.getKey(e), t);
354
+ }
355
+ async remove(e) {
356
+ typeof window > "u" || sessionStorage.removeItem(this.getKey(e));
357
+ }
358
+ async clear() {
359
+ if (typeof window > "u") return;
360
+ Object.keys(sessionStorage).filter(
361
+ (t) => t.startsWith(`${this.prefix}:`)
362
+ ).forEach((t) => sessionStorage.removeItem(t));
363
+ }
364
+ }
365
+ function A(s = "localStorage") {
366
+ switch (s) {
367
+ case "memory":
368
+ return new x();
369
+ case "localStorage":
370
+ return new S();
371
+ case "sessionStorage":
372
+ return new E();
373
+ default:
374
+ return new S();
375
+ }
376
+ }
377
+ class M {
378
+ oauth;
379
+ tokenManager;
380
+ http;
381
+ httpServer;
382
+ listeners = /* @__PURE__ */ new Set();
383
+ initialized = !1;
384
+ constructor(e) {
385
+ this.validateConfig(e);
386
+ const t = typeof e.storage == "object" ? e.storage : A(e.storage);
387
+ this.tokenManager = new C(t), this.oauth = new U(e, this.tokenManager), this.http = new p({
388
+ baseUrl: e.authUrl ?? "https://mnnoheowoowbtpuoguul.supabase.co/auth/v1"
389
+ }), this.httpServer = new p({
390
+ baseUrl: "https://ff-api.genation.ai/api/v2/client"
391
+ });
392
+ }
393
+ validateConfig(e) {
394
+ if (!e.clientId) throw u.missingField("clientId");
395
+ if (!e.clientSecret)
396
+ throw u.missingField("clientSecret");
397
+ if (!e.redirectUri) throw u.missingField("redirectUri");
398
+ }
399
+ /**
400
+ * Emit auth state change event to all listeners
401
+ */
402
+ async emitAuthStateChange(e) {
403
+ const t = await this.getSession();
404
+ this.listeners.forEach((r) => {
405
+ try {
406
+ r(e, t);
407
+ } catch (n) {
408
+ console.error("Error in auth state change callback:", n);
409
+ }
410
+ });
411
+ }
412
+ /**
413
+ * Listen to authentication state changes
414
+ *
415
+ * Register a callback that fires when:
416
+ * - `INITIAL_SESSION`: On first subscription, with current session state
417
+ * - `SIGNED_IN`: User successfully authenticated
418
+ * - `SIGNED_OUT`: User logged out or session expired
419
+ * - `TOKEN_REFRESHED`: Access token was automatically refreshed
420
+ *
421
+ * @param callback - Function called on each auth state change
422
+ * @returns Object containing subscription with `unsubscribe()` method
423
+ *
424
+ * @example
425
+ * ```typescript
426
+ * const { subscription } = client.onAuthStateChange((event, session) => {
427
+ * console.log('Auth event:', event);
428
+ *
429
+ * if (event === 'INITIAL_SESSION') {
430
+ * // Check if user was previously logged in
431
+ * if (session) {
432
+ * console.log('Welcome back!', session.user);
433
+ * }
434
+ * } else if (event === 'SIGNED_IN') {
435
+ * // User just signed in
436
+ * console.log('Signed in:', session?.user);
437
+ * } else if (event === 'SIGNED_OUT') {
438
+ * // Clear app state, redirect to login
439
+ * console.log('Signed out');
440
+ * }
441
+ * });
442
+ *
443
+ * // Cleanup when component unmounts
444
+ * subscription.unsubscribe();
445
+ * ```
446
+ */
447
+ onAuthStateChange(e) {
448
+ return this.listeners.add(e), this.initialized ? setTimeout(() => {
449
+ this.emitAuthStateChange("INITIAL_SESSION");
450
+ }, 0) : (this.initialized = !0, setTimeout(() => {
451
+ this.emitAuthStateChange("INITIAL_SESSION");
452
+ }, 0)), {
453
+ subscription: {
454
+ unsubscribe: () => {
455
+ this.listeners.delete(e);
456
+ }
457
+ }
458
+ };
459
+ }
460
+ /**
461
+ * Start OAuth sign-in flow
462
+ *
463
+ * Generates authorization URL with PKCE challenge.
464
+ * Redirect user to this URL to start authentication.
465
+ *
466
+ * @returns Authorization URL to redirect user to
467
+ *
468
+ * @example
469
+ * ```typescript
470
+ * async function handleLogin() {
471
+ * const url = await client.signIn();
472
+ * window.location.href = url;
473
+ * }
474
+ * ```
475
+ */
476
+ async signIn() {
477
+ return this.oauth.getAuthorizationUrl();
478
+ }
479
+ /**
480
+ * Handle OAuth callback after user authentication
481
+ *
482
+ * Call this on your redirect URI page to exchange
483
+ * the authorization code for access tokens.
484
+ * Triggers `SIGNED_IN` event on success.
485
+ *
486
+ * @param code - Authorization code from URL query params
487
+ * @param state - State parameter for CSRF validation
488
+ * @returns Token set with access and refresh tokens
489
+ * @throws {AuthError} If state mismatch or code exchange fails
490
+ *
491
+ * @example
492
+ * ```typescript
493
+ * // On your /callback page
494
+ * async function handleCallback() {
495
+ * const params = new URLSearchParams(window.location.search);
496
+ * const code = params.get('code');
497
+ * const state = params.get('state');
498
+ *
499
+ * if (code && state) {
500
+ * await client.handleCallback(code, state);
501
+ * // onAuthStateChange will fire with SIGNED_IN event
502
+ * }
503
+ * }
504
+ * ```
505
+ */
506
+ async handleCallback(e, t) {
507
+ const r = await this.oauth.exchangeCode(e, t);
508
+ try {
509
+ const n = await this.fetchUser(r.accessToken);
510
+ console.log("Debug: handleCallback fetched user:", n);
511
+ } catch (n) {
512
+ console.error("Debug: handleCallback fetchUser failed:", n);
513
+ }
514
+ return await this.emitAuthStateChange("SIGNED_IN"), r;
515
+ }
516
+ /**
517
+ * Sign out and revoke tokens
518
+ *
519
+ * Clears local session and revokes tokens on server.
520
+ * Triggers `SIGNED_OUT` event for all listeners.
521
+ *
522
+ * @example
523
+ * ```typescript
524
+ * async function handleLogout() {
525
+ * await client.signOut();
526
+ * // onAuthStateChange will fire with SIGNED_OUT event
527
+ * }
528
+ * ```
529
+ */
530
+ async signOut() {
531
+ await this.oauth.revokeToken(), await this.emitAuthStateChange("SIGNED_OUT");
532
+ }
533
+ /**
534
+ * Get current session
535
+ *
536
+ * Returns session with access token and user info.
537
+ * Automatically refreshes token if expired.
538
+ *
539
+ * @returns Current session or null if not authenticated
540
+ *
541
+ * @example
542
+ * ```typescript
543
+ * const session = await client.getSession();
544
+ * if (session) {
545
+ * console.log('Logged in as:', session.user?.email);
546
+ *
547
+ * // Use access token for API calls
548
+ * fetch('/api/data', {
549
+ * headers: { Authorization: `Bearer ${session.accessToken}` }
550
+ * });
551
+ * }
552
+ * ```
553
+ */
554
+ async getSession() {
555
+ if (await this.tokenManager.isTokenExpired())
556
+ try {
557
+ await this.oauth.refreshToken();
558
+ } catch {
559
+ return null;
560
+ }
561
+ const t = await this.tokenManager.getTokens();
562
+ if (!t) return null;
563
+ const r = await this.fetchUser(t.accessToken);
564
+ return {
565
+ accessToken: t.accessToken,
566
+ refreshToken: t.refreshToken,
567
+ expiresIn: t.expiresIn,
568
+ expiresAt: t.issuedAt + t.expiresIn * 1e3,
569
+ user: r
570
+ };
571
+ }
572
+ /**
573
+ * Get licenses
574
+ * @param accessToken - The access token to use for the request
575
+ * @param options - The options for the request
576
+ * @param options.expiresAfter - Query licenses that are expired after the given date, default set to today to get all valid licenses (unexpired licenses)
577
+ * @returns The licenses
578
+ */
579
+ async getLicenses(e = {}) {
580
+ const t = await this.getSession();
581
+ if (!t)
582
+ return null;
583
+ const r = t.accessToken, { expiresAfter: n = /* @__PURE__ */ new Date() } = e, a = await this.httpServer.request("/licenses", {
584
+ headers: { Authorization: `Bearer ${r}` },
585
+ params: { expiresAfter: n.toISOString() }
586
+ });
587
+ return a.ok ? a.data : (console.error("GenationClient: Error fetching licenses:", a.error), null);
588
+ }
589
+ /**
590
+ * Fetch user info from auth server
591
+ */
592
+ async fetchUser(e) {
593
+ try {
594
+ const t = await this.http.request("/oauth/userinfo", {
595
+ headers: { Authorization: `Bearer ${e}` }
596
+ });
597
+ return {
598
+ sub: t.sub,
599
+ name: t.name,
600
+ picture: t.picture,
601
+ email: t.email,
602
+ email_verified: t.email_verified,
603
+ phone_number: t.phone_number,
604
+ phone_number_verified: t.phone_number_verified
605
+ };
606
+ } catch (t) {
607
+ return console.error("GenationClient: Error fetching user:", t), null;
608
+ }
609
+ }
610
+ }
611
+ function K(s) {
612
+ return new M(s);
613
+ }
614
+ export {
615
+ h as AuthError,
616
+ u as ConfigError,
617
+ M as GenationClient,
618
+ w as GenationError,
619
+ S as LocalStorage,
620
+ x as MemoryStorage,
621
+ c as NetworkError,
622
+ E as SessionStorage,
623
+ K as createClient,
624
+ A as createStorage
625
+ };
626
+ //# sourceMappingURL=genation.es.js.map