@insforge/sdk 1.0.1-refresh.3 → 1.0.1-refresh.4

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/dist/index.mjs CHANGED
@@ -85,11 +85,11 @@ var HttpClient = class {
85
85
  method,
86
86
  headers: requestHeaders,
87
87
  body: processedBody,
88
- credentials: "include",
89
- // Essential for httpOnly cookies (refresh token)
90
- ...fetchOptions
88
+ ...fetchOptions,
89
+ credentials: "include"
91
90
  });
92
- if (response.status === 401 && !isRetry && this.refreshCallback) {
91
+ const isRefreshEndpoint = path.includes("/api/auth/refresh") || path.includes("/api/auth/logout");
92
+ if (response.status === 401 && !isRetry && !isRefreshEndpoint && this.refreshCallback) {
93
93
  const newToken = await this.handleTokenRefresh();
94
94
  if (newToken) {
95
95
  this.setAuthToken(newToken);
@@ -139,7 +139,7 @@ var HttpClient = class {
139
139
  }
140
140
  this.isRefreshing = true;
141
141
  try {
142
- const newToken = await this.refreshCallback();
142
+ const newToken = await this.refreshCallback?.();
143
143
  this.refreshQueue.forEach(({ resolve, reject }) => {
144
144
  if (newToken) {
145
145
  resolve(newToken);
@@ -148,7 +148,7 @@ var HttpClient = class {
148
148
  }
149
149
  });
150
150
  this.refreshQueue = [];
151
- return newToken;
151
+ return newToken || null;
152
152
  } catch (error) {
153
153
  this.refreshQueue.forEach(({ reject }) => {
154
154
  reject(error instanceof Error ? error : new Error("Token refresh failed"));
@@ -200,10 +200,9 @@ var SecureSessionStorage = class {
200
200
  saveSession(session) {
201
201
  this.accessToken = session.accessToken;
202
202
  this.user = session.user;
203
- this.setAuthFlag(true);
204
203
  }
205
204
  getSession() {
206
- if (!this.accessToken) return null;
205
+ if (!this.accessToken || !this.user) return null;
207
206
  return {
208
207
  accessToken: this.accessToken,
209
208
  user: this.user
@@ -224,25 +223,17 @@ var SecureSessionStorage = class {
224
223
  clearSession() {
225
224
  this.accessToken = null;
226
225
  this.user = null;
227
- this.setAuthFlag(false);
228
226
  }
229
227
  shouldAttemptRefresh() {
230
228
  if (this.accessToken) return false;
231
229
  return this.hasAuthFlag();
232
230
  }
233
- // --- Private: Auth Flag Cookie Management ---
234
- setAuthFlag(authenticated) {
235
- if (typeof document === "undefined") return;
236
- if (authenticated) {
237
- const maxAge = 7 * 24 * 60 * 60;
238
- document.cookie = `${AUTH_FLAG_COOKIE}=true; path=/; max-age=${maxAge}; SameSite=Lax`;
239
- } else {
240
- document.cookie = `${AUTH_FLAG_COOKIE}=; path=/; max-age=0`;
241
- }
242
- }
231
+ // --- Private: Auth Flag Cookie Detection (read-only) ---
243
232
  hasAuthFlag() {
244
233
  if (typeof document === "undefined") return false;
245
- return document.cookie.includes(`${AUTH_FLAG_COOKIE}=true`);
234
+ return document.cookie.split(";").some(
235
+ (c) => c.trim().startsWith(`${AUTH_FLAG_COOKIE}=`)
236
+ );
246
237
  }
247
238
  };
248
239
  var LocalSessionStorage = class {
@@ -388,41 +379,6 @@ var TokenManager = class {
388
379
  }
389
380
  };
390
381
 
391
- // src/lib/backend-config.ts
392
- var DEFAULT_CONFIG = {
393
- secureSessionStorage: false,
394
- refreshTokens: false
395
- };
396
- async function discoverBackendConfig(baseUrl, fetchImpl = globalThis.fetch) {
397
- try {
398
- const response = await fetchImpl(`${baseUrl}/api/health`, {
399
- method: "GET",
400
- headers: {
401
- "Accept": "application/json"
402
- }
403
- });
404
- if (!response.ok) {
405
- return DEFAULT_CONFIG;
406
- }
407
- const health = await response.json();
408
- if (health.config) {
409
- return health.config;
410
- }
411
- return DEFAULT_CONFIG;
412
- } catch {
413
- return DEFAULT_CONFIG;
414
- }
415
- }
416
- function createSessionStorage(config, storage) {
417
- if (config.secureSessionStorage && config.refreshTokens) {
418
- return new SecureSessionStorage();
419
- }
420
- return new LocalSessionStorage(storage);
421
- }
422
- function getDefaultBackendConfig() {
423
- return { ...DEFAULT_CONFIG };
424
- }
425
-
426
382
  // src/modules/database-postgrest.ts
427
383
  import { PostgrestClient } from "@supabase/postgrest-js";
428
384
  function createInsForgePostgrestFetch(httpClient, tokenManager) {
@@ -529,99 +485,66 @@ function isHostedAuthEnvironment() {
529
485
  return false;
530
486
  }
531
487
  var Auth = class {
532
- constructor(http, tokenManager, initializePromise) {
488
+ constructor(http, tokenManager) {
533
489
  this.http = http;
534
490
  this.tokenManager = tokenManager;
535
- this.authStateListeners = /* @__PURE__ */ new Set();
536
491
  this.database = new Database(http, tokenManager);
537
- this.initializePromise = initializePromise ?? Promise.resolve();
492
+ this.detectAuthCallback();
538
493
  }
539
494
  /**
540
- * Subscribe to auth state changes
541
- *
542
- * New subscribers will receive an INITIAL_SESSION event after initialization completes.
543
- * This ensures no race condition where subscribers miss the initial state.
544
- *
545
- * @param callback - Function called when auth state changes
546
- * @returns Unsubscribe function
547
- *
548
- * @example
549
- * ```typescript
550
- * const { data: { subscription } } = client.auth.onAuthStateChange((event, session) => {
551
- * if (event === 'SIGNED_IN') {
552
- * console.log('User signed in:', session?.user.email);
553
- * } else if (event === 'SIGNED_OUT') {
554
- * console.log('User signed out');
555
- * }
556
- * });
557
- *
558
- * // Later: unsubscribe
559
- * subscription.unsubscribe();
560
- * ```
495
+ * Check if the isAuthenticated cookie flag exists
561
496
  */
562
- onAuthStateChange(callback) {
563
- this.authStateListeners.add(callback);
564
- ;
565
- (async () => {
566
- await this.initializePromise;
567
- if (this.authStateListeners.has(callback)) {
568
- const session = this.tokenManager.getSession();
569
- try {
570
- callback("INITIAL_SESSION", session);
571
- } catch (error) {
572
- console.error("[Auth] Error in auth state change listener:", error);
573
- }
574
- }
575
- })();
576
- return {
577
- data: {
578
- subscription: {
579
- unsubscribe: () => {
580
- this.authStateListeners.delete(callback);
581
- }
582
- }
583
- }
584
- };
497
+ hasAuthenticatedCookie() {
498
+ if (typeof document === "undefined") return false;
499
+ return document.cookie.split(";").some(
500
+ (c) => c.trim().startsWith(`${AUTH_FLAG_COOKIE}=`)
501
+ );
585
502
  }
586
503
  /**
587
- * Emit auth state change to all listeners
504
+ * Switch to SecureSessionStorage (cookie-based auth)
505
+ * Called when we detect backend supports secure cookie mode
588
506
  * @internal
589
507
  */
590
- _emitAuthStateChange(event, session) {
591
- this.authStateListeners.forEach((callback) => {
592
- try {
593
- callback(event, session);
594
- } catch (error) {
595
- console.error("[Auth] Error in auth state change listener:", error);
596
- }
597
- });
508
+ _switchToSecureStorage() {
509
+ if (this.tokenManager.getStrategyId() === "secure") return;
510
+ const currentSession = this.tokenManager.getSession();
511
+ this.tokenManager.setStrategy(new SecureSessionStorage());
512
+ if (typeof localStorage !== "undefined") {
513
+ localStorage.removeItem(TOKEN_KEY);
514
+ localStorage.removeItem(USER_KEY);
515
+ }
516
+ if (currentSession) {
517
+ this.tokenManager.saveSession(currentSession);
518
+ }
598
519
  }
599
520
  /**
600
- * Check if an error represents an authentication failure
601
- * Used to determine appropriate HTTP status code (401 vs 500)
521
+ * Switch to LocalSessionStorage (localStorage-based auth)
522
+ * Called when cookie-based auth fails (fallback)
523
+ * @internal
602
524
  */
603
- isAuthenticationError(error) {
604
- if (error instanceof Error) {
605
- const message = error.message.toLowerCase();
606
- const authKeywords = [
607
- "unauthorized",
608
- "invalid token",
609
- "expired token",
610
- "token expired",
611
- "invalid refresh token",
612
- "refresh token",
613
- "authentication",
614
- "not authenticated",
615
- "session expired"
616
- ];
617
- return authKeywords.some((keyword) => message.includes(keyword));
525
+ _switchToLocalStorage() {
526
+ if (this.tokenManager.getStrategyId() === "local") return;
527
+ const currentSession = this.tokenManager.getSession();
528
+ this.tokenManager.setStrategy(new LocalSessionStorage());
529
+ if (currentSession) {
530
+ this.tokenManager.saveSession(currentSession);
618
531
  }
619
- return false;
620
532
  }
621
533
  /**
622
- * Detect and handle OAuth callback parameters in the URL.
623
- * Called by client after initialization.
534
+ * Detect storage strategy after successful auth
535
+ * Checks for isAuthenticated cookie to determine backend mode
536
+ * @internal
624
537
  */
538
+ _detectStorageAfterAuth() {
539
+ if (this.hasAuthenticatedCookie()) {
540
+ this._switchToSecureStorage();
541
+ }
542
+ }
543
+ /**
544
+ * Automatically detect and handle OAuth callback parameters in the URL
545
+ * This runs on initialization to seamlessly complete the OAuth flow
546
+ * Matches the backend's OAuth callback response (backend/src/api/routes/auth.ts:540-544)
547
+ */
625
548
  detectAuthCallback() {
626
549
  if (typeof window === "undefined") return;
627
550
  try {
@@ -631,6 +554,7 @@ var Auth = class {
631
554
  const email = params.get("email");
632
555
  const name = params.get("name");
633
556
  if (accessToken && userId && email) {
557
+ this._detectStorageAfterAuth();
634
558
  const session = {
635
559
  accessToken,
636
560
  user: {
@@ -655,9 +579,9 @@ var Auth = class {
655
579
  url.searchParams.delete("error");
656
580
  }
657
581
  window.history.replaceState({}, document.title, url.toString());
658
- this._emitAuthStateChange("SIGNED_IN", session);
659
582
  }
660
- } catch {
583
+ } catch (error) {
584
+ console.debug("OAuth callback detection skipped:", error);
661
585
  }
662
586
  }
663
587
  /**
@@ -675,7 +599,7 @@ var Auth = class {
675
599
  this.tokenManager.saveSession(session);
676
600
  }
677
601
  this.http.setAuthToken(response.accessToken);
678
- this._emitAuthStateChange("SIGNED_IN", session);
602
+ this._detectStorageAfterAuth();
679
603
  }
680
604
  return {
681
605
  data: response,
@@ -716,7 +640,7 @@ var Auth = class {
716
640
  this.tokenManager.saveSession(session);
717
641
  }
718
642
  this.http.setAuthToken(response.accessToken || "");
719
- this._emitAuthStateChange("SIGNED_IN", session);
643
+ this._detectStorageAfterAuth();
720
644
  return {
721
645
  data: response,
722
646
  error: null
@@ -783,7 +707,6 @@ var Auth = class {
783
707
  }
784
708
  this.tokenManager.clearSession();
785
709
  this.http.setAuthToken(null);
786
- this._emitAuthStateChange("SIGNED_OUT", null);
787
710
  return { error: null };
788
711
  } catch (error) {
789
712
  return {
@@ -812,8 +735,6 @@ var Auth = class {
812
735
  if (response.user) {
813
736
  this.tokenManager.setUser(response.user);
814
737
  }
815
- const session = this.tokenManager.getSession();
816
- this._emitAuthStateChange("TOKEN_REFRESHED", session);
817
738
  return response.accessToken;
818
739
  }
819
740
  throw new InsForgeError(
@@ -829,15 +750,9 @@ var Auth = class {
829
750
  }
830
751
  throw error;
831
752
  }
832
- const errorMessage = error instanceof Error ? error.message : "Token refresh failed";
833
- const isAuthError = this.isAuthenticationError(error);
834
- if (isAuthError) {
835
- this.tokenManager.clearSession();
836
- this.http.setAuthToken(null);
837
- }
838
753
  throw new InsForgeError(
839
- errorMessage,
840
- isAuthError ? 401 : 500,
754
+ "Token refresh failed",
755
+ 500,
841
756
  "REFRESH_FAILED"
842
757
  );
843
758
  }
@@ -882,14 +797,27 @@ var Auth = class {
882
797
  /**
883
798
  * Get the current user with full profile information
884
799
  * Returns both auth info (id, email, role) and profile data (dynamic fields from users table)
800
+ *
801
+ * In secure session mode (httpOnly cookie), this method will automatically attempt
802
+ * to refresh the session if no access token is available (e.g., after page reload).
885
803
  */
886
804
  async getCurrentUser() {
887
805
  try {
888
- const session = this.tokenManager.getSession();
889
- if (!session?.accessToken) {
806
+ let accessToken = this.tokenManager.getAccessToken();
807
+ if (!accessToken && this.tokenManager.shouldAttemptRefresh()) {
808
+ try {
809
+ accessToken = await this.refreshToken();
810
+ } catch (error) {
811
+ if (error instanceof InsForgeError && (error.statusCode === 401 || error.statusCode === 403)) {
812
+ return { data: null, error };
813
+ }
814
+ return { data: null, error: error instanceof InsForgeError ? error : new InsForgeError("Token refresh failed", 500, "REFRESH_FAILED") };
815
+ }
816
+ }
817
+ if (!accessToken) {
890
818
  return { data: null, error: null };
891
819
  }
892
- this.http.setAuthToken(session.accessToken);
820
+ this.http.setAuthToken(accessToken);
893
821
  const authResponse = await this.http.get("/api/auth/sessions/current");
894
822
  const { data: profile, error: profileError } = await this.database.from("users").select("*").eq("id", authResponse.user.id).single();
895
823
  if (profileError && profileError.code !== "PGRST116") {
@@ -1159,7 +1087,6 @@ var Auth = class {
1159
1087
  };
1160
1088
  this.tokenManager.saveSession(session);
1161
1089
  this.http.setAuthToken(response.accessToken);
1162
- this._emitAuthStateChange("SIGNED_IN", session);
1163
1090
  }
1164
1091
  return {
1165
1092
  data: response,
@@ -1703,15 +1630,19 @@ var Functions = class {
1703
1630
  };
1704
1631
 
1705
1632
  // src/client.ts
1633
+ function hasAuthenticatedCookie() {
1634
+ if (typeof document === "undefined") return false;
1635
+ return document.cookie.split(";").some(
1636
+ (c) => c.trim().startsWith(`${AUTH_FLAG_COOKIE}=`)
1637
+ );
1638
+ }
1706
1639
  var InsForgeClient = class {
1707
1640
  constructor(config = {}) {
1708
- this.backendConfig = null;
1709
- this.initializePromise = new Promise((resolve) => {
1710
- this.initializeResolve = resolve;
1711
- });
1712
1641
  this.http = new HttpClient(config);
1713
1642
  this.tokenManager = new TokenManager(config.storage);
1714
- this.auth = new Auth(this.http, this.tokenManager, this.initializePromise);
1643
+ if (hasAuthenticatedCookie()) {
1644
+ this.tokenManager.setStrategy(new SecureSessionStorage());
1645
+ }
1715
1646
  if (config.edgeFunctionToken) {
1716
1647
  this.http.setAuthToken(config.edgeFunctionToken);
1717
1648
  this.tokenManager.saveSession({
@@ -1724,82 +1655,55 @@ var InsForgeClient = class {
1724
1655
  try {
1725
1656
  return await this.auth.refreshToken();
1726
1657
  } catch {
1658
+ if (this.tokenManager.getStrategyId() === "secure") {
1659
+ this.auth._switchToLocalStorage();
1660
+ }
1727
1661
  return null;
1728
1662
  }
1729
1663
  });
1730
1664
  const existingSession = this.tokenManager.getSession();
1731
1665
  if (existingSession?.accessToken) {
1732
1666
  this.http.setAuthToken(existingSession.accessToken);
1667
+ } else if (this.tokenManager.getStrategyId() === "secure") {
1733
1668
  }
1669
+ this.auth = new Auth(this.http, this.tokenManager);
1734
1670
  this.database = new Database(this.http, this.tokenManager);
1735
1671
  this.storage = new Storage(this.http);
1736
1672
  this.ai = new AI(this.http);
1737
1673
  this.functions = new Functions(this.http);
1738
- this._initializeAsync();
1739
- }
1740
- /**
1741
- * Internal async initialization - discovers backend config and recovers session.
1742
- * Emits INITIAL_SESSION event when complete.
1743
- * @internal
1744
- */
1745
- async _initializeAsync() {
1746
- try {
1747
- this.backendConfig = await discoverBackendConfig(
1748
- this.http.baseUrl,
1749
- this.http.fetch
1750
- );
1751
- const strategy = createSessionStorage(this.backendConfig);
1752
- this.tokenManager.setStrategy(strategy);
1753
- this.auth.detectAuthCallback();
1754
- let currentSession = this.tokenManager.getSession();
1755
- if (!currentSession?.accessToken && this.backendConfig.refreshTokens) {
1756
- if (this.tokenManager.shouldAttemptRefresh()) {
1757
- try {
1758
- await this.auth.refreshToken();
1759
- currentSession = this.tokenManager.getSession();
1760
- } catch {
1761
- this.tokenManager.clearSession();
1762
- this.http.setAuthToken(null);
1763
- }
1764
- }
1765
- }
1766
- this.initializeResolve();
1767
- } catch {
1768
- this.auth.detectAuthCallback();
1769
- this.initializeResolve();
1770
- }
1771
- }
1772
- /**
1773
- * Wait for client initialization to complete
1774
- * @returns Promise that resolves when initialization is done
1775
- */
1776
- async waitForInitialization() {
1777
- return this.initializePromise;
1778
1674
  }
1779
1675
  /**
1780
1676
  * Get the underlying HTTP client for custom requests
1677
+ *
1678
+ * @example
1679
+ * ```typescript
1680
+ * const httpClient = client.getHttpClient();
1681
+ * const customData = await httpClient.get('/api/custom-endpoint');
1682
+ * ```
1781
1683
  */
1782
1684
  getHttpClient() {
1783
1685
  return this.http;
1784
1686
  }
1785
- /**
1786
- * Get the discovered backend configuration
1787
- */
1788
- getBackendConfig() {
1789
- return this.backendConfig;
1790
- }
1791
1687
  /**
1792
1688
  * Get the current storage strategy identifier
1793
1689
  */
1794
1690
  getStorageStrategy() {
1795
1691
  return this.tokenManager.getStrategyId();
1796
1692
  }
1693
+ /**
1694
+ * Future modules will be added here:
1695
+ * - database: Database operations
1696
+ * - storage: File storage operations
1697
+ * - functions: Serverless functions
1698
+ * - tables: Table management
1699
+ * - metadata: Backend metadata
1700
+ */
1797
1701
  };
1798
- function createClient(config = {}) {
1799
- return new InsForgeClient(config);
1800
- }
1801
1702
 
1802
1703
  // src/index.ts
1704
+ function createClient(config) {
1705
+ return new InsForgeClient(config);
1706
+ }
1803
1707
  var index_default = InsForgeClient;
1804
1708
  export {
1805
1709
  AI,
@@ -1815,9 +1719,6 @@ export {
1815
1719
  StorageBucket,
1816
1720
  TokenManager,
1817
1721
  createClient,
1818
- createSessionStorage,
1819
- index_default as default,
1820
- discoverBackendConfig,
1821
- getDefaultBackendConfig
1722
+ index_default as default
1822
1723
  };
1823
1724
  //# sourceMappingURL=index.mjs.map