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

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
@@ -245,9 +245,9 @@ var SecureSessionStorage = class {
245
245
  return document.cookie.includes(`${AUTH_FLAG_COOKIE}=true`);
246
246
  }
247
247
  };
248
- var PersistentSessionStorage = class {
248
+ var LocalSessionStorage = class {
249
249
  constructor(storage) {
250
- this.strategyId = "persistent";
250
+ this.strategyId = "local";
251
251
  if (storage) {
252
252
  this.storage = storage;
253
253
  } else if (typeof window !== "undefined" && window.localStorage) {
@@ -313,10 +313,10 @@ var PersistentSessionStorage = class {
313
313
  var TokenManager = class {
314
314
  /**
315
315
  * Create a new TokenManager
316
- * @param storage - Optional custom storage adapter (used for initial PersistentSessionStorage)
316
+ * @param storage - Optional custom storage adapter (used for initial LocalSessionStorage)
317
317
  */
318
318
  constructor(storage) {
319
- this.strategy = new PersistentSessionStorage(storage);
319
+ this.strategy = new LocalSessionStorage(storage);
320
320
  }
321
321
  /**
322
322
  * Set the storage strategy
@@ -388,12 +388,12 @@ var TokenManager = class {
388
388
  }
389
389
  };
390
390
 
391
- // src/lib/capability-discovery.ts
392
- var DEFAULT_CAPABILITIES = {
391
+ // src/lib/backend-config.ts
392
+ var DEFAULT_CONFIG = {
393
393
  secureSessionStorage: false,
394
394
  refreshTokens: false
395
395
  };
396
- async function discoverCapabilities(baseUrl, fetchImpl = globalThis.fetch) {
396
+ async function discoverBackendConfig(baseUrl, fetchImpl = globalThis.fetch) {
397
397
  try {
398
398
  const response = await fetchImpl(`${baseUrl}/api/health`, {
399
399
  method: "GET",
@@ -402,25 +402,25 @@ async function discoverCapabilities(baseUrl, fetchImpl = globalThis.fetch) {
402
402
  }
403
403
  });
404
404
  if (!response.ok) {
405
- return DEFAULT_CAPABILITIES;
405
+ return DEFAULT_CONFIG;
406
406
  }
407
407
  const health = await response.json();
408
- if (health.capabilities) {
409
- return health.capabilities;
408
+ if (health.config) {
409
+ return health.config;
410
410
  }
411
- return DEFAULT_CAPABILITIES;
411
+ return DEFAULT_CONFIG;
412
412
  } catch {
413
- return DEFAULT_CAPABILITIES;
413
+ return DEFAULT_CONFIG;
414
414
  }
415
415
  }
416
- function createSessionStorage(capabilities, storage) {
417
- if (capabilities.secureSessionStorage && capabilities.refreshTokens) {
416
+ function createSessionStorage(config, storage) {
417
+ if (config.secureSessionStorage && config.refreshTokens) {
418
418
  return new SecureSessionStorage();
419
419
  }
420
- return new PersistentSessionStorage(storage);
420
+ return new LocalSessionStorage(storage);
421
421
  }
422
- function getDefaultCapabilities() {
423
- return { ...DEFAULT_CAPABILITIES };
422
+ function getDefaultBackendConfig() {
423
+ return { ...DEFAULT_CONFIG };
424
424
  }
425
425
 
426
426
  // src/modules/database-postgrest.ts
@@ -529,11 +529,72 @@ function isHostedAuthEnvironment() {
529
529
  return false;
530
530
  }
531
531
  var Auth = class {
532
- constructor(http, tokenManager) {
532
+ constructor(http, tokenManager, initializePromise) {
533
533
  this.http = http;
534
534
  this.tokenManager = tokenManager;
535
- this.initPromise = null;
535
+ this.authStateListeners = /* @__PURE__ */ new Set();
536
536
  this.database = new Database(http, tokenManager);
537
+ this.initializePromise = initializePromise ?? Promise.resolve();
538
+ }
539
+ /**
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
+ * ```
561
+ */
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
+ };
585
+ }
586
+ /**
587
+ * Emit auth state change to all listeners
588
+ * @internal
589
+ */
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
+ });
537
598
  }
538
599
  /**
539
600
  * Check if an error represents an authentication failure
@@ -558,30 +619,12 @@ var Auth = class {
558
619
  return false;
559
620
  }
560
621
  /**
561
- * Set the initialization promise that auth operations should wait for
562
- * This ensures TokenManager mode is set before any auth operations
622
+ * Detect and handle OAuth callback parameters in the URL.
623
+ * Called by client after initialization.
563
624
  */
564
- setInitPromise(promise) {
565
- this.initPromise = promise;
566
- this.detectAuthCallbackAsync();
567
- }
568
- /**
569
- * Wait for initialization to complete (if set)
570
- */
571
- async waitForInit() {
572
- if (this.initPromise) {
573
- await this.initPromise;
574
- }
575
- }
576
- /**
577
- * Automatically detect and handle OAuth callback parameters in the URL
578
- * This runs after initialization to seamlessly complete the OAuth flow
579
- * Matches the backend's OAuth callback response (backend/src/api/routes/auth.ts:540-544)
580
- */
581
- async detectAuthCallbackAsync() {
625
+ detectAuthCallback() {
582
626
  if (typeof window === "undefined") return;
583
627
  try {
584
- await this.waitForInit();
585
628
  const params = new URLSearchParams(window.location.search);
586
629
  const accessToken = params.get("access_token");
587
630
  const userId = params.get("user_id");
@@ -612,6 +655,7 @@ var Auth = class {
612
655
  url.searchParams.delete("error");
613
656
  }
614
657
  window.history.replaceState({}, document.title, url.toString());
658
+ this._emitAuthStateChange("SIGNED_IN", session);
615
659
  }
616
660
  } catch {
617
661
  }
@@ -621,7 +665,6 @@ var Auth = class {
621
665
  */
622
666
  async signUp(request) {
623
667
  try {
624
- await this.waitForInit();
625
668
  const response = await this.http.post("/api/auth/users", request);
626
669
  if (response.accessToken && response.user) {
627
670
  const session = {
@@ -632,6 +675,7 @@ var Auth = class {
632
675
  this.tokenManager.saveSession(session);
633
676
  }
634
677
  this.http.setAuthToken(response.accessToken);
678
+ this._emitAuthStateChange("SIGNED_IN", session);
635
679
  }
636
680
  return {
637
681
  data: response,
@@ -656,7 +700,6 @@ var Auth = class {
656
700
  */
657
701
  async signInWithPassword(request) {
658
702
  try {
659
- await this.waitForInit();
660
703
  const response = await this.http.post("/api/auth/sessions", request);
661
704
  const session = {
662
705
  accessToken: response.accessToken || "",
@@ -673,6 +716,7 @@ var Auth = class {
673
716
  this.tokenManager.saveSession(session);
674
717
  }
675
718
  this.http.setAuthToken(response.accessToken || "");
719
+ this._emitAuthStateChange("SIGNED_IN", session);
676
720
  return {
677
721
  data: response,
678
722
  error: null
@@ -739,6 +783,7 @@ var Auth = class {
739
783
  }
740
784
  this.tokenManager.clearSession();
741
785
  this.http.setAuthToken(null);
786
+ this._emitAuthStateChange("SIGNED_OUT", null);
742
787
  return { error: null };
743
788
  } catch (error) {
744
789
  return {
@@ -767,6 +812,8 @@ var Auth = class {
767
812
  if (response.user) {
768
813
  this.tokenManager.setUser(response.user);
769
814
  }
815
+ const session = this.tokenManager.getSession();
816
+ this._emitAuthStateChange("TOKEN_REFRESHED", session);
770
817
  return response.accessToken;
771
818
  }
772
819
  throw new InsForgeError(
@@ -1101,7 +1148,6 @@ var Auth = class {
1101
1148
  */
1102
1149
  async verifyEmail(request) {
1103
1150
  try {
1104
- await this.waitForInit();
1105
1151
  const response = await this.http.post(
1106
1152
  "/api/auth/email/verify",
1107
1153
  request
@@ -1113,6 +1159,7 @@ var Auth = class {
1113
1159
  };
1114
1160
  this.tokenManager.saveSession(session);
1115
1161
  this.http.setAuthToken(response.accessToken);
1162
+ this._emitAuthStateChange("SIGNED_IN", session);
1116
1163
  }
1117
1164
  return {
1118
1165
  data: response,
@@ -1658,11 +1705,13 @@ var Functions = class {
1658
1705
  // src/client.ts
1659
1706
  var InsForgeClient = class {
1660
1707
  constructor(config = {}) {
1661
- this.initialized = false;
1662
- this.initializationPromise = null;
1663
- this.capabilities = null;
1708
+ this.backendConfig = null;
1709
+ this.initializePromise = new Promise((resolve) => {
1710
+ this.initializeResolve = resolve;
1711
+ });
1664
1712
  this.http = new HttpClient(config);
1665
1713
  this.tokenManager = new TokenManager(config.storage);
1714
+ this.auth = new Auth(this.http, this.tokenManager, this.initializePromise);
1666
1715
  if (config.edgeFunctionToken) {
1667
1716
  this.http.setAuthToken(config.edgeFunctionToken);
1668
1717
  this.tokenManager.saveSession({
@@ -1671,7 +1720,6 @@ var InsForgeClient = class {
1671
1720
  // Will be populated by getCurrentUser()
1672
1721
  });
1673
1722
  }
1674
- this.auth = new Auth(this.http, this.tokenManager);
1675
1723
  this.http.setRefreshCallback(async () => {
1676
1724
  try {
1677
1725
  return await this.auth.refreshToken();
@@ -1687,67 +1735,58 @@ var InsForgeClient = class {
1687
1735
  this.storage = new Storage(this.http);
1688
1736
  this.ai = new AI(this.http);
1689
1737
  this.functions = new Functions(this.http);
1690
- this.initializationPromise = this.initializeAsync();
1691
- this.auth.setInitPromise(this.initializationPromise);
1738
+ this._initializeAsync();
1692
1739
  }
1693
1740
  /**
1694
- * Initialize the client by discovering backend capabilities
1695
- * This is called automatically on construction but can be awaited for guaranteed initialization
1696
- *
1697
- * @example
1698
- * ```typescript
1699
- * const client = new InsForgeClient({ baseUrl: 'https://api.example.com' });
1700
- * await client.initialize(); // Wait for capability discovery
1701
- * ```
1741
+ * Internal async initialization - discovers backend config and recovers session.
1742
+ * Emits INITIAL_SESSION event when complete.
1743
+ * @internal
1702
1744
  */
1703
- async initialize() {
1704
- if (this.initializationPromise) {
1705
- await this.initializationPromise;
1706
- }
1707
- }
1708
- /**
1709
- * Internal async initialization - discovers capabilities and configures storage strategy
1710
- */
1711
- async initializeAsync() {
1712
- if (this.initialized) return;
1745
+ async _initializeAsync() {
1713
1746
  try {
1714
- this.capabilities = await discoverCapabilities(
1747
+ this.backendConfig = await discoverBackendConfig(
1715
1748
  this.http.baseUrl,
1716
1749
  this.http.fetch
1717
1750
  );
1718
- const strategy = createSessionStorage(this.capabilities);
1751
+ const strategy = createSessionStorage(this.backendConfig);
1719
1752
  this.tokenManager.setStrategy(strategy);
1720
- if (this.capabilities.refreshTokens && this.tokenManager.shouldAttemptRefresh()) {
1721
- try {
1722
- const newToken = await this.auth.refreshToken();
1723
- this.http.setAuthToken(newToken);
1724
- } catch {
1725
- this.tokenManager.clearSession();
1726
- this.http.setAuthToken(null);
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
+ }
1727
1764
  }
1728
1765
  }
1729
- this.initialized = true;
1766
+ this.initializeResolve();
1730
1767
  } catch {
1731
- this.initialized = true;
1768
+ this.auth.detectAuthCallback();
1769
+ this.initializeResolve();
1732
1770
  }
1733
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
+ }
1734
1779
  /**
1735
1780
  * Get the underlying HTTP client for custom requests
1736
- *
1737
- * @example
1738
- * ```typescript
1739
- * const httpClient = client.getHttpClient();
1740
- * const customData = await httpClient.get('/api/custom-endpoint');
1741
- * ```
1742
1781
  */
1743
1782
  getHttpClient() {
1744
1783
  return this.http;
1745
1784
  }
1746
1785
  /**
1747
- * Get the discovered backend capabilities
1786
+ * Get the discovered backend configuration
1748
1787
  */
1749
- getCapabilities() {
1750
- return this.capabilities;
1788
+ getBackendConfig() {
1789
+ return this.backendConfig;
1751
1790
  }
1752
1791
  /**
1753
1792
  * Get the current storage strategy identifier
@@ -1755,18 +1794,12 @@ var InsForgeClient = class {
1755
1794
  getStorageStrategy() {
1756
1795
  return this.tokenManager.getStrategyId();
1757
1796
  }
1758
- /**
1759
- * Check if the client has been fully initialized
1760
- */
1761
- isInitialized() {
1762
- return this.initialized;
1763
- }
1764
1797
  };
1765
-
1766
- // src/index.ts
1767
- function createClient(config) {
1798
+ function createClient(config = {}) {
1768
1799
  return new InsForgeClient(config);
1769
1800
  }
1801
+
1802
+ // src/index.ts
1770
1803
  var index_default = InsForgeClient;
1771
1804
  export {
1772
1805
  AI,
@@ -1776,7 +1809,7 @@ export {
1776
1809
  HttpClient,
1777
1810
  InsForgeClient,
1778
1811
  InsForgeError,
1779
- PersistentSessionStorage,
1812
+ LocalSessionStorage,
1780
1813
  SecureSessionStorage,
1781
1814
  Storage,
1782
1815
  StorageBucket,
@@ -1784,7 +1817,7 @@ export {
1784
1817
  createClient,
1785
1818
  createSessionStorage,
1786
1819
  index_default as default,
1787
- discoverCapabilities,
1788
- getDefaultCapabilities
1820
+ discoverBackendConfig,
1821
+ getDefaultBackendConfig
1789
1822
  };
1790
1823
  //# sourceMappingURL=index.mjs.map