@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.js CHANGED
@@ -27,7 +27,7 @@ __export(index_exports, {
27
27
  HttpClient: () => HttpClient,
28
28
  InsForgeClient: () => InsForgeClient,
29
29
  InsForgeError: () => InsForgeError,
30
- PersistentSessionStorage: () => PersistentSessionStorage,
30
+ LocalSessionStorage: () => LocalSessionStorage,
31
31
  SecureSessionStorage: () => SecureSessionStorage,
32
32
  Storage: () => Storage,
33
33
  StorageBucket: () => StorageBucket,
@@ -35,8 +35,8 @@ __export(index_exports, {
35
35
  createClient: () => createClient,
36
36
  createSessionStorage: () => createSessionStorage,
37
37
  default: () => index_default,
38
- discoverCapabilities: () => discoverCapabilities,
39
- getDefaultCapabilities: () => getDefaultCapabilities
38
+ discoverBackendConfig: () => discoverBackendConfig,
39
+ getDefaultBackendConfig: () => getDefaultBackendConfig
40
40
  });
41
41
  module.exports = __toCommonJS(index_exports);
42
42
 
@@ -287,9 +287,9 @@ var SecureSessionStorage = class {
287
287
  return document.cookie.includes(`${AUTH_FLAG_COOKIE}=true`);
288
288
  }
289
289
  };
290
- var PersistentSessionStorage = class {
290
+ var LocalSessionStorage = class {
291
291
  constructor(storage) {
292
- this.strategyId = "persistent";
292
+ this.strategyId = "local";
293
293
  if (storage) {
294
294
  this.storage = storage;
295
295
  } else if (typeof window !== "undefined" && window.localStorage) {
@@ -355,10 +355,10 @@ var PersistentSessionStorage = class {
355
355
  var TokenManager = class {
356
356
  /**
357
357
  * Create a new TokenManager
358
- * @param storage - Optional custom storage adapter (used for initial PersistentSessionStorage)
358
+ * @param storage - Optional custom storage adapter (used for initial LocalSessionStorage)
359
359
  */
360
360
  constructor(storage) {
361
- this.strategy = new PersistentSessionStorage(storage);
361
+ this.strategy = new LocalSessionStorage(storage);
362
362
  }
363
363
  /**
364
364
  * Set the storage strategy
@@ -430,12 +430,12 @@ var TokenManager = class {
430
430
  }
431
431
  };
432
432
 
433
- // src/lib/capability-discovery.ts
434
- var DEFAULT_CAPABILITIES = {
433
+ // src/lib/backend-config.ts
434
+ var DEFAULT_CONFIG = {
435
435
  secureSessionStorage: false,
436
436
  refreshTokens: false
437
437
  };
438
- async function discoverCapabilities(baseUrl, fetchImpl = globalThis.fetch) {
438
+ async function discoverBackendConfig(baseUrl, fetchImpl = globalThis.fetch) {
439
439
  try {
440
440
  const response = await fetchImpl(`${baseUrl}/api/health`, {
441
441
  method: "GET",
@@ -444,25 +444,25 @@ async function discoverCapabilities(baseUrl, fetchImpl = globalThis.fetch) {
444
444
  }
445
445
  });
446
446
  if (!response.ok) {
447
- return DEFAULT_CAPABILITIES;
447
+ return DEFAULT_CONFIG;
448
448
  }
449
449
  const health = await response.json();
450
- if (health.capabilities) {
451
- return health.capabilities;
450
+ if (health.config) {
451
+ return health.config;
452
452
  }
453
- return DEFAULT_CAPABILITIES;
453
+ return DEFAULT_CONFIG;
454
454
  } catch {
455
- return DEFAULT_CAPABILITIES;
455
+ return DEFAULT_CONFIG;
456
456
  }
457
457
  }
458
- function createSessionStorage(capabilities, storage) {
459
- if (capabilities.secureSessionStorage && capabilities.refreshTokens) {
458
+ function createSessionStorage(config, storage) {
459
+ if (config.secureSessionStorage && config.refreshTokens) {
460
460
  return new SecureSessionStorage();
461
461
  }
462
- return new PersistentSessionStorage(storage);
462
+ return new LocalSessionStorage(storage);
463
463
  }
464
- function getDefaultCapabilities() {
465
- return { ...DEFAULT_CAPABILITIES };
464
+ function getDefaultBackendConfig() {
465
+ return { ...DEFAULT_CONFIG };
466
466
  }
467
467
 
468
468
  // src/modules/database-postgrest.ts
@@ -571,11 +571,72 @@ function isHostedAuthEnvironment() {
571
571
  return false;
572
572
  }
573
573
  var Auth = class {
574
- constructor(http, tokenManager) {
574
+ constructor(http, tokenManager, initializePromise) {
575
575
  this.http = http;
576
576
  this.tokenManager = tokenManager;
577
- this.initPromise = null;
577
+ this.authStateListeners = /* @__PURE__ */ new Set();
578
578
  this.database = new Database(http, tokenManager);
579
+ this.initializePromise = initializePromise ?? Promise.resolve();
580
+ }
581
+ /**
582
+ * Subscribe to auth state changes
583
+ *
584
+ * New subscribers will receive an INITIAL_SESSION event after initialization completes.
585
+ * This ensures no race condition where subscribers miss the initial state.
586
+ *
587
+ * @param callback - Function called when auth state changes
588
+ * @returns Unsubscribe function
589
+ *
590
+ * @example
591
+ * ```typescript
592
+ * const { data: { subscription } } = client.auth.onAuthStateChange((event, session) => {
593
+ * if (event === 'SIGNED_IN') {
594
+ * console.log('User signed in:', session?.user.email);
595
+ * } else if (event === 'SIGNED_OUT') {
596
+ * console.log('User signed out');
597
+ * }
598
+ * });
599
+ *
600
+ * // Later: unsubscribe
601
+ * subscription.unsubscribe();
602
+ * ```
603
+ */
604
+ onAuthStateChange(callback) {
605
+ this.authStateListeners.add(callback);
606
+ ;
607
+ (async () => {
608
+ await this.initializePromise;
609
+ if (this.authStateListeners.has(callback)) {
610
+ const session = this.tokenManager.getSession();
611
+ try {
612
+ callback("INITIAL_SESSION", session);
613
+ } catch (error) {
614
+ console.error("[Auth] Error in auth state change listener:", error);
615
+ }
616
+ }
617
+ })();
618
+ return {
619
+ data: {
620
+ subscription: {
621
+ unsubscribe: () => {
622
+ this.authStateListeners.delete(callback);
623
+ }
624
+ }
625
+ }
626
+ };
627
+ }
628
+ /**
629
+ * Emit auth state change to all listeners
630
+ * @internal
631
+ */
632
+ _emitAuthStateChange(event, session) {
633
+ this.authStateListeners.forEach((callback) => {
634
+ try {
635
+ callback(event, session);
636
+ } catch (error) {
637
+ console.error("[Auth] Error in auth state change listener:", error);
638
+ }
639
+ });
579
640
  }
580
641
  /**
581
642
  * Check if an error represents an authentication failure
@@ -600,30 +661,12 @@ var Auth = class {
600
661
  return false;
601
662
  }
602
663
  /**
603
- * Set the initialization promise that auth operations should wait for
604
- * This ensures TokenManager mode is set before any auth operations
664
+ * Detect and handle OAuth callback parameters in the URL.
665
+ * Called by client after initialization.
605
666
  */
606
- setInitPromise(promise) {
607
- this.initPromise = promise;
608
- this.detectAuthCallbackAsync();
609
- }
610
- /**
611
- * Wait for initialization to complete (if set)
612
- */
613
- async waitForInit() {
614
- if (this.initPromise) {
615
- await this.initPromise;
616
- }
617
- }
618
- /**
619
- * Automatically detect and handle OAuth callback parameters in the URL
620
- * This runs after initialization to seamlessly complete the OAuth flow
621
- * Matches the backend's OAuth callback response (backend/src/api/routes/auth.ts:540-544)
622
- */
623
- async detectAuthCallbackAsync() {
667
+ detectAuthCallback() {
624
668
  if (typeof window === "undefined") return;
625
669
  try {
626
- await this.waitForInit();
627
670
  const params = new URLSearchParams(window.location.search);
628
671
  const accessToken = params.get("access_token");
629
672
  const userId = params.get("user_id");
@@ -654,6 +697,7 @@ var Auth = class {
654
697
  url.searchParams.delete("error");
655
698
  }
656
699
  window.history.replaceState({}, document.title, url.toString());
700
+ this._emitAuthStateChange("SIGNED_IN", session);
657
701
  }
658
702
  } catch {
659
703
  }
@@ -663,7 +707,6 @@ var Auth = class {
663
707
  */
664
708
  async signUp(request) {
665
709
  try {
666
- await this.waitForInit();
667
710
  const response = await this.http.post("/api/auth/users", request);
668
711
  if (response.accessToken && response.user) {
669
712
  const session = {
@@ -674,6 +717,7 @@ var Auth = class {
674
717
  this.tokenManager.saveSession(session);
675
718
  }
676
719
  this.http.setAuthToken(response.accessToken);
720
+ this._emitAuthStateChange("SIGNED_IN", session);
677
721
  }
678
722
  return {
679
723
  data: response,
@@ -698,7 +742,6 @@ var Auth = class {
698
742
  */
699
743
  async signInWithPassword(request) {
700
744
  try {
701
- await this.waitForInit();
702
745
  const response = await this.http.post("/api/auth/sessions", request);
703
746
  const session = {
704
747
  accessToken: response.accessToken || "",
@@ -715,6 +758,7 @@ var Auth = class {
715
758
  this.tokenManager.saveSession(session);
716
759
  }
717
760
  this.http.setAuthToken(response.accessToken || "");
761
+ this._emitAuthStateChange("SIGNED_IN", session);
718
762
  return {
719
763
  data: response,
720
764
  error: null
@@ -781,6 +825,7 @@ var Auth = class {
781
825
  }
782
826
  this.tokenManager.clearSession();
783
827
  this.http.setAuthToken(null);
828
+ this._emitAuthStateChange("SIGNED_OUT", null);
784
829
  return { error: null };
785
830
  } catch (error) {
786
831
  return {
@@ -809,6 +854,8 @@ var Auth = class {
809
854
  if (response.user) {
810
855
  this.tokenManager.setUser(response.user);
811
856
  }
857
+ const session = this.tokenManager.getSession();
858
+ this._emitAuthStateChange("TOKEN_REFRESHED", session);
812
859
  return response.accessToken;
813
860
  }
814
861
  throw new InsForgeError(
@@ -1143,7 +1190,6 @@ var Auth = class {
1143
1190
  */
1144
1191
  async verifyEmail(request) {
1145
1192
  try {
1146
- await this.waitForInit();
1147
1193
  const response = await this.http.post(
1148
1194
  "/api/auth/email/verify",
1149
1195
  request
@@ -1155,6 +1201,7 @@ var Auth = class {
1155
1201
  };
1156
1202
  this.tokenManager.saveSession(session);
1157
1203
  this.http.setAuthToken(response.accessToken);
1204
+ this._emitAuthStateChange("SIGNED_IN", session);
1158
1205
  }
1159
1206
  return {
1160
1207
  data: response,
@@ -1700,11 +1747,13 @@ var Functions = class {
1700
1747
  // src/client.ts
1701
1748
  var InsForgeClient = class {
1702
1749
  constructor(config = {}) {
1703
- this.initialized = false;
1704
- this.initializationPromise = null;
1705
- this.capabilities = null;
1750
+ this.backendConfig = null;
1751
+ this.initializePromise = new Promise((resolve) => {
1752
+ this.initializeResolve = resolve;
1753
+ });
1706
1754
  this.http = new HttpClient(config);
1707
1755
  this.tokenManager = new TokenManager(config.storage);
1756
+ this.auth = new Auth(this.http, this.tokenManager, this.initializePromise);
1708
1757
  if (config.edgeFunctionToken) {
1709
1758
  this.http.setAuthToken(config.edgeFunctionToken);
1710
1759
  this.tokenManager.saveSession({
@@ -1713,7 +1762,6 @@ var InsForgeClient = class {
1713
1762
  // Will be populated by getCurrentUser()
1714
1763
  });
1715
1764
  }
1716
- this.auth = new Auth(this.http, this.tokenManager);
1717
1765
  this.http.setRefreshCallback(async () => {
1718
1766
  try {
1719
1767
  return await this.auth.refreshToken();
@@ -1729,67 +1777,58 @@ var InsForgeClient = class {
1729
1777
  this.storage = new Storage(this.http);
1730
1778
  this.ai = new AI(this.http);
1731
1779
  this.functions = new Functions(this.http);
1732
- this.initializationPromise = this.initializeAsync();
1733
- this.auth.setInitPromise(this.initializationPromise);
1780
+ this._initializeAsync();
1734
1781
  }
1735
1782
  /**
1736
- * Initialize the client by discovering backend capabilities
1737
- * This is called automatically on construction but can be awaited for guaranteed initialization
1738
- *
1739
- * @example
1740
- * ```typescript
1741
- * const client = new InsForgeClient({ baseUrl: 'https://api.example.com' });
1742
- * await client.initialize(); // Wait for capability discovery
1743
- * ```
1783
+ * Internal async initialization - discovers backend config and recovers session.
1784
+ * Emits INITIAL_SESSION event when complete.
1785
+ * @internal
1744
1786
  */
1745
- async initialize() {
1746
- if (this.initializationPromise) {
1747
- await this.initializationPromise;
1748
- }
1749
- }
1750
- /**
1751
- * Internal async initialization - discovers capabilities and configures storage strategy
1752
- */
1753
- async initializeAsync() {
1754
- if (this.initialized) return;
1787
+ async _initializeAsync() {
1755
1788
  try {
1756
- this.capabilities = await discoverCapabilities(
1789
+ this.backendConfig = await discoverBackendConfig(
1757
1790
  this.http.baseUrl,
1758
1791
  this.http.fetch
1759
1792
  );
1760
- const strategy = createSessionStorage(this.capabilities);
1793
+ const strategy = createSessionStorage(this.backendConfig);
1761
1794
  this.tokenManager.setStrategy(strategy);
1762
- if (this.capabilities.refreshTokens && this.tokenManager.shouldAttemptRefresh()) {
1763
- try {
1764
- const newToken = await this.auth.refreshToken();
1765
- this.http.setAuthToken(newToken);
1766
- } catch {
1767
- this.tokenManager.clearSession();
1768
- this.http.setAuthToken(null);
1795
+ this.auth.detectAuthCallback();
1796
+ let currentSession = this.tokenManager.getSession();
1797
+ if (!currentSession?.accessToken && this.backendConfig.refreshTokens) {
1798
+ if (this.tokenManager.shouldAttemptRefresh()) {
1799
+ try {
1800
+ await this.auth.refreshToken();
1801
+ currentSession = this.tokenManager.getSession();
1802
+ } catch {
1803
+ this.tokenManager.clearSession();
1804
+ this.http.setAuthToken(null);
1805
+ }
1769
1806
  }
1770
1807
  }
1771
- this.initialized = true;
1808
+ this.initializeResolve();
1772
1809
  } catch {
1773
- this.initialized = true;
1810
+ this.auth.detectAuthCallback();
1811
+ this.initializeResolve();
1774
1812
  }
1775
1813
  }
1814
+ /**
1815
+ * Wait for client initialization to complete
1816
+ * @returns Promise that resolves when initialization is done
1817
+ */
1818
+ async waitForInitialization() {
1819
+ return this.initializePromise;
1820
+ }
1776
1821
  /**
1777
1822
  * Get the underlying HTTP client for custom requests
1778
- *
1779
- * @example
1780
- * ```typescript
1781
- * const httpClient = client.getHttpClient();
1782
- * const customData = await httpClient.get('/api/custom-endpoint');
1783
- * ```
1784
1823
  */
1785
1824
  getHttpClient() {
1786
1825
  return this.http;
1787
1826
  }
1788
1827
  /**
1789
- * Get the discovered backend capabilities
1828
+ * Get the discovered backend configuration
1790
1829
  */
1791
- getCapabilities() {
1792
- return this.capabilities;
1830
+ getBackendConfig() {
1831
+ return this.backendConfig;
1793
1832
  }
1794
1833
  /**
1795
1834
  * Get the current storage strategy identifier
@@ -1797,18 +1836,12 @@ var InsForgeClient = class {
1797
1836
  getStorageStrategy() {
1798
1837
  return this.tokenManager.getStrategyId();
1799
1838
  }
1800
- /**
1801
- * Check if the client has been fully initialized
1802
- */
1803
- isInitialized() {
1804
- return this.initialized;
1805
- }
1806
1839
  };
1807
-
1808
- // src/index.ts
1809
- function createClient(config) {
1840
+ function createClient(config = {}) {
1810
1841
  return new InsForgeClient(config);
1811
1842
  }
1843
+
1844
+ // src/index.ts
1812
1845
  var index_default = InsForgeClient;
1813
1846
  // Annotate the CommonJS export names for ESM import in node:
1814
1847
  0 && (module.exports = {
@@ -1819,14 +1852,14 @@ var index_default = InsForgeClient;
1819
1852
  HttpClient,
1820
1853
  InsForgeClient,
1821
1854
  InsForgeError,
1822
- PersistentSessionStorage,
1855
+ LocalSessionStorage,
1823
1856
  SecureSessionStorage,
1824
1857
  Storage,
1825
1858
  StorageBucket,
1826
1859
  TokenManager,
1827
1860
  createClient,
1828
1861
  createSessionStorage,
1829
- discoverCapabilities,
1830
- getDefaultCapabilities
1862
+ discoverBackendConfig,
1863
+ getDefaultBackendConfig
1831
1864
  });
1832
1865
  //# sourceMappingURL=index.js.map