@insforge/sdk 1.0.1-refresh.2 → 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.js CHANGED
@@ -27,16 +27,13 @@ __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,
34
34
  TokenManager: () => TokenManager,
35
35
  createClient: () => createClient,
36
- createSessionStorage: () => createSessionStorage,
37
- default: () => index_default,
38
- discoverCapabilities: () => discoverCapabilities,
39
- getDefaultCapabilities: () => getDefaultCapabilities
36
+ default: () => index_default
40
37
  });
41
38
  module.exports = __toCommonJS(index_exports);
42
39
 
@@ -127,11 +124,11 @@ var HttpClient = class {
127
124
  method,
128
125
  headers: requestHeaders,
129
126
  body: processedBody,
130
- credentials: "include",
131
- // Essential for httpOnly cookies (refresh token)
132
- ...fetchOptions
127
+ ...fetchOptions,
128
+ credentials: "include"
133
129
  });
134
- if (response.status === 401 && !isRetry && this.refreshCallback) {
130
+ const isRefreshEndpoint = path.includes("/api/auth/refresh") || path.includes("/api/auth/logout");
131
+ if (response.status === 401 && !isRetry && !isRefreshEndpoint && this.refreshCallback) {
135
132
  const newToken = await this.handleTokenRefresh();
136
133
  if (newToken) {
137
134
  this.setAuthToken(newToken);
@@ -181,7 +178,7 @@ var HttpClient = class {
181
178
  }
182
179
  this.isRefreshing = true;
183
180
  try {
184
- const newToken = await this.refreshCallback();
181
+ const newToken = await this.refreshCallback?.();
185
182
  this.refreshQueue.forEach(({ resolve, reject }) => {
186
183
  if (newToken) {
187
184
  resolve(newToken);
@@ -190,7 +187,7 @@ var HttpClient = class {
190
187
  }
191
188
  });
192
189
  this.refreshQueue = [];
193
- return newToken;
190
+ return newToken || null;
194
191
  } catch (error) {
195
192
  this.refreshQueue.forEach(({ reject }) => {
196
193
  reject(error instanceof Error ? error : new Error("Token refresh failed"));
@@ -242,10 +239,9 @@ var SecureSessionStorage = class {
242
239
  saveSession(session) {
243
240
  this.accessToken = session.accessToken;
244
241
  this.user = session.user;
245
- this.setAuthFlag(true);
246
242
  }
247
243
  getSession() {
248
- if (!this.accessToken) return null;
244
+ if (!this.accessToken || !this.user) return null;
249
245
  return {
250
246
  accessToken: this.accessToken,
251
247
  user: this.user
@@ -266,30 +262,22 @@ var SecureSessionStorage = class {
266
262
  clearSession() {
267
263
  this.accessToken = null;
268
264
  this.user = null;
269
- this.setAuthFlag(false);
270
265
  }
271
266
  shouldAttemptRefresh() {
272
267
  if (this.accessToken) return false;
273
268
  return this.hasAuthFlag();
274
269
  }
275
- // --- Private: Auth Flag Cookie Management ---
276
- setAuthFlag(authenticated) {
277
- if (typeof document === "undefined") return;
278
- if (authenticated) {
279
- const maxAge = 7 * 24 * 60 * 60;
280
- document.cookie = `${AUTH_FLAG_COOKIE}=true; path=/; max-age=${maxAge}; SameSite=Lax`;
281
- } else {
282
- document.cookie = `${AUTH_FLAG_COOKIE}=; path=/; max-age=0`;
283
- }
284
- }
270
+ // --- Private: Auth Flag Cookie Detection (read-only) ---
285
271
  hasAuthFlag() {
286
272
  if (typeof document === "undefined") return false;
287
- return document.cookie.includes(`${AUTH_FLAG_COOKIE}=true`);
273
+ return document.cookie.split(";").some(
274
+ (c) => c.trim().startsWith(`${AUTH_FLAG_COOKIE}=`)
275
+ );
288
276
  }
289
277
  };
290
- var PersistentSessionStorage = class {
278
+ var LocalSessionStorage = class {
291
279
  constructor(storage) {
292
- this.strategyId = "persistent";
280
+ this.strategyId = "local";
293
281
  if (storage) {
294
282
  this.storage = storage;
295
283
  } else if (typeof window !== "undefined" && window.localStorage) {
@@ -355,10 +343,10 @@ var PersistentSessionStorage = class {
355
343
  var TokenManager = class {
356
344
  /**
357
345
  * Create a new TokenManager
358
- * @param storage - Optional custom storage adapter (used for initial PersistentSessionStorage)
346
+ * @param storage - Optional custom storage adapter (used for initial LocalSessionStorage)
359
347
  */
360
348
  constructor(storage) {
361
- this.strategy = new PersistentSessionStorage(storage);
349
+ this.strategy = new LocalSessionStorage(storage);
362
350
  }
363
351
  /**
364
352
  * Set the storage strategy
@@ -430,41 +418,6 @@ var TokenManager = class {
430
418
  }
431
419
  };
432
420
 
433
- // src/lib/capability-discovery.ts
434
- var DEFAULT_CAPABILITIES = {
435
- secureSessionStorage: false,
436
- refreshTokens: false
437
- };
438
- async function discoverCapabilities(baseUrl, fetchImpl = globalThis.fetch) {
439
- try {
440
- const response = await fetchImpl(`${baseUrl}/api/health`, {
441
- method: "GET",
442
- headers: {
443
- "Accept": "application/json"
444
- }
445
- });
446
- if (!response.ok) {
447
- return DEFAULT_CAPABILITIES;
448
- }
449
- const health = await response.json();
450
- if (health.capabilities) {
451
- return health.capabilities;
452
- }
453
- return DEFAULT_CAPABILITIES;
454
- } catch {
455
- return DEFAULT_CAPABILITIES;
456
- }
457
- }
458
- function createSessionStorage(capabilities, storage) {
459
- if (capabilities.secureSessionStorage && capabilities.refreshTokens) {
460
- return new SecureSessionStorage();
461
- }
462
- return new PersistentSessionStorage(storage);
463
- }
464
- function getDefaultCapabilities() {
465
- return { ...DEFAULT_CAPABILITIES };
466
- }
467
-
468
421
  // src/modules/database-postgrest.ts
469
422
  var import_postgrest_js = require("@supabase/postgrest-js");
470
423
  function createInsForgePostgrestFetch(httpClient, tokenManager) {
@@ -574,62 +527,73 @@ var Auth = class {
574
527
  constructor(http, tokenManager) {
575
528
  this.http = http;
576
529
  this.tokenManager = tokenManager;
577
- this.initPromise = null;
578
530
  this.database = new Database(http, tokenManager);
531
+ this.detectAuthCallback();
579
532
  }
580
533
  /**
581
- * Check if an error represents an authentication failure
582
- * Used to determine appropriate HTTP status code (401 vs 500)
534
+ * Check if the isAuthenticated cookie flag exists
583
535
  */
584
- isAuthenticationError(error) {
585
- if (error instanceof Error) {
586
- const message = error.message.toLowerCase();
587
- const authKeywords = [
588
- "unauthorized",
589
- "invalid token",
590
- "expired token",
591
- "token expired",
592
- "invalid refresh token",
593
- "refresh token",
594
- "authentication",
595
- "not authenticated",
596
- "session expired"
597
- ];
598
- return authKeywords.some((keyword) => message.includes(keyword));
599
- }
600
- return false;
536
+ hasAuthenticatedCookie() {
537
+ if (typeof document === "undefined") return false;
538
+ return document.cookie.split(";").some(
539
+ (c) => c.trim().startsWith(`${AUTH_FLAG_COOKIE}=`)
540
+ );
601
541
  }
602
542
  /**
603
- * Set the initialization promise that auth operations should wait for
604
- * This ensures TokenManager mode is set before any auth operations
543
+ * Switch to SecureSessionStorage (cookie-based auth)
544
+ * Called when we detect backend supports secure cookie mode
545
+ * @internal
605
546
  */
606
- setInitPromise(promise) {
607
- this.initPromise = promise;
608
- this.detectAuthCallbackAsync();
547
+ _switchToSecureStorage() {
548
+ if (this.tokenManager.getStrategyId() === "secure") return;
549
+ const currentSession = this.tokenManager.getSession();
550
+ this.tokenManager.setStrategy(new SecureSessionStorage());
551
+ if (typeof localStorage !== "undefined") {
552
+ localStorage.removeItem(TOKEN_KEY);
553
+ localStorage.removeItem(USER_KEY);
554
+ }
555
+ if (currentSession) {
556
+ this.tokenManager.saveSession(currentSession);
557
+ }
609
558
  }
610
559
  /**
611
- * Wait for initialization to complete (if set)
560
+ * Switch to LocalSessionStorage (localStorage-based auth)
561
+ * Called when cookie-based auth fails (fallback)
562
+ * @internal
612
563
  */
613
- async waitForInit() {
614
- if (this.initPromise) {
615
- await this.initPromise;
564
+ _switchToLocalStorage() {
565
+ if (this.tokenManager.getStrategyId() === "local") return;
566
+ const currentSession = this.tokenManager.getSession();
567
+ this.tokenManager.setStrategy(new LocalSessionStorage());
568
+ if (currentSession) {
569
+ this.tokenManager.saveSession(currentSession);
616
570
  }
617
571
  }
618
572
  /**
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)
573
+ * Detect storage strategy after successful auth
574
+ * Checks for isAuthenticated cookie to determine backend mode
575
+ * @internal
622
576
  */
623
- async detectAuthCallbackAsync() {
577
+ _detectStorageAfterAuth() {
578
+ if (this.hasAuthenticatedCookie()) {
579
+ this._switchToSecureStorage();
580
+ }
581
+ }
582
+ /**
583
+ * Automatically detect and handle OAuth callback parameters in the URL
584
+ * This runs on initialization to seamlessly complete the OAuth flow
585
+ * Matches the backend's OAuth callback response (backend/src/api/routes/auth.ts:540-544)
586
+ */
587
+ detectAuthCallback() {
624
588
  if (typeof window === "undefined") return;
625
589
  try {
626
- await this.waitForInit();
627
590
  const params = new URLSearchParams(window.location.search);
628
591
  const accessToken = params.get("access_token");
629
592
  const userId = params.get("user_id");
630
593
  const email = params.get("email");
631
594
  const name = params.get("name");
632
595
  if (accessToken && userId && email) {
596
+ this._detectStorageAfterAuth();
633
597
  const session = {
634
598
  accessToken,
635
599
  user: {
@@ -655,7 +619,8 @@ var Auth = class {
655
619
  }
656
620
  window.history.replaceState({}, document.title, url.toString());
657
621
  }
658
- } catch {
622
+ } catch (error) {
623
+ console.debug("OAuth callback detection skipped:", error);
659
624
  }
660
625
  }
661
626
  /**
@@ -663,7 +628,6 @@ var Auth = class {
663
628
  */
664
629
  async signUp(request) {
665
630
  try {
666
- await this.waitForInit();
667
631
  const response = await this.http.post("/api/auth/users", request);
668
632
  if (response.accessToken && response.user) {
669
633
  const session = {
@@ -674,6 +638,7 @@ var Auth = class {
674
638
  this.tokenManager.saveSession(session);
675
639
  }
676
640
  this.http.setAuthToken(response.accessToken);
641
+ this._detectStorageAfterAuth();
677
642
  }
678
643
  return {
679
644
  data: response,
@@ -698,7 +663,6 @@ var Auth = class {
698
663
  */
699
664
  async signInWithPassword(request) {
700
665
  try {
701
- await this.waitForInit();
702
666
  const response = await this.http.post("/api/auth/sessions", request);
703
667
  const session = {
704
668
  accessToken: response.accessToken || "",
@@ -715,6 +679,7 @@ var Auth = class {
715
679
  this.tokenManager.saveSession(session);
716
680
  }
717
681
  this.http.setAuthToken(response.accessToken || "");
682
+ this._detectStorageAfterAuth();
718
683
  return {
719
684
  data: response,
720
685
  error: null
@@ -824,15 +789,9 @@ var Auth = class {
824
789
  }
825
790
  throw error;
826
791
  }
827
- const errorMessage = error instanceof Error ? error.message : "Token refresh failed";
828
- const isAuthError = this.isAuthenticationError(error);
829
- if (isAuthError) {
830
- this.tokenManager.clearSession();
831
- this.http.setAuthToken(null);
832
- }
833
792
  throw new InsForgeError(
834
- errorMessage,
835
- isAuthError ? 401 : 500,
793
+ "Token refresh failed",
794
+ 500,
836
795
  "REFRESH_FAILED"
837
796
  );
838
797
  }
@@ -877,14 +836,27 @@ var Auth = class {
877
836
  /**
878
837
  * Get the current user with full profile information
879
838
  * Returns both auth info (id, email, role) and profile data (dynamic fields from users table)
839
+ *
840
+ * In secure session mode (httpOnly cookie), this method will automatically attempt
841
+ * to refresh the session if no access token is available (e.g., after page reload).
880
842
  */
881
843
  async getCurrentUser() {
882
844
  try {
883
- const session = this.tokenManager.getSession();
884
- if (!session?.accessToken) {
845
+ let accessToken = this.tokenManager.getAccessToken();
846
+ if (!accessToken && this.tokenManager.shouldAttemptRefresh()) {
847
+ try {
848
+ accessToken = await this.refreshToken();
849
+ } catch (error) {
850
+ if (error instanceof InsForgeError && (error.statusCode === 401 || error.statusCode === 403)) {
851
+ return { data: null, error };
852
+ }
853
+ return { data: null, error: error instanceof InsForgeError ? error : new InsForgeError("Token refresh failed", 500, "REFRESH_FAILED") };
854
+ }
855
+ }
856
+ if (!accessToken) {
885
857
  return { data: null, error: null };
886
858
  }
887
- this.http.setAuthToken(session.accessToken);
859
+ this.http.setAuthToken(accessToken);
888
860
  const authResponse = await this.http.get("/api/auth/sessions/current");
889
861
  const { data: profile, error: profileError } = await this.database.from("users").select("*").eq("id", authResponse.user.id).single();
890
862
  if (profileError && profileError.code !== "PGRST116") {
@@ -1143,7 +1115,6 @@ var Auth = class {
1143
1115
  */
1144
1116
  async verifyEmail(request) {
1145
1117
  try {
1146
- await this.waitForInit();
1147
1118
  const response = await this.http.post(
1148
1119
  "/api/auth/email/verify",
1149
1120
  request
@@ -1698,13 +1669,19 @@ var Functions = class {
1698
1669
  };
1699
1670
 
1700
1671
  // src/client.ts
1672
+ function hasAuthenticatedCookie() {
1673
+ if (typeof document === "undefined") return false;
1674
+ return document.cookie.split(";").some(
1675
+ (c) => c.trim().startsWith(`${AUTH_FLAG_COOKIE}=`)
1676
+ );
1677
+ }
1701
1678
  var InsForgeClient = class {
1702
1679
  constructor(config = {}) {
1703
- this.initialized = false;
1704
- this.initializationPromise = null;
1705
- this.capabilities = null;
1706
1680
  this.http = new HttpClient(config);
1707
1681
  this.tokenManager = new TokenManager(config.storage);
1682
+ if (hasAuthenticatedCookie()) {
1683
+ this.tokenManager.setStrategy(new SecureSessionStorage());
1684
+ }
1708
1685
  if (config.edgeFunctionToken) {
1709
1686
  this.http.setAuthToken(config.edgeFunctionToken);
1710
1687
  this.tokenManager.saveSession({
@@ -1713,65 +1690,26 @@ var InsForgeClient = class {
1713
1690
  // Will be populated by getCurrentUser()
1714
1691
  });
1715
1692
  }
1716
- this.auth = new Auth(this.http, this.tokenManager);
1717
1693
  this.http.setRefreshCallback(async () => {
1718
1694
  try {
1719
1695
  return await this.auth.refreshToken();
1720
1696
  } catch {
1697
+ if (this.tokenManager.getStrategyId() === "secure") {
1698
+ this.auth._switchToLocalStorage();
1699
+ }
1721
1700
  return null;
1722
1701
  }
1723
1702
  });
1724
1703
  const existingSession = this.tokenManager.getSession();
1725
1704
  if (existingSession?.accessToken) {
1726
1705
  this.http.setAuthToken(existingSession.accessToken);
1706
+ } else if (this.tokenManager.getStrategyId() === "secure") {
1727
1707
  }
1708
+ this.auth = new Auth(this.http, this.tokenManager);
1728
1709
  this.database = new Database(this.http, this.tokenManager);
1729
1710
  this.storage = new Storage(this.http);
1730
1711
  this.ai = new AI(this.http);
1731
1712
  this.functions = new Functions(this.http);
1732
- this.initializationPromise = this.initializeAsync();
1733
- this.auth.setInitPromise(this.initializationPromise);
1734
- }
1735
- /**
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
- * ```
1744
- */
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;
1755
- try {
1756
- this.capabilities = await discoverCapabilities(
1757
- this.http.baseUrl,
1758
- this.http.fetch
1759
- );
1760
- const strategy = createSessionStorage(this.capabilities);
1761
- 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);
1769
- }
1770
- }
1771
- this.initialized = true;
1772
- } catch {
1773
- this.initialized = true;
1774
- }
1775
1713
  }
1776
1714
  /**
1777
1715
  * Get the underlying HTTP client for custom requests
@@ -1785,12 +1723,6 @@ var InsForgeClient = class {
1785
1723
  getHttpClient() {
1786
1724
  return this.http;
1787
1725
  }
1788
- /**
1789
- * Get the discovered backend capabilities
1790
- */
1791
- getCapabilities() {
1792
- return this.capabilities;
1793
- }
1794
1726
  /**
1795
1727
  * Get the current storage strategy identifier
1796
1728
  */
@@ -1798,11 +1730,13 @@ var InsForgeClient = class {
1798
1730
  return this.tokenManager.getStrategyId();
1799
1731
  }
1800
1732
  /**
1801
- * Check if the client has been fully initialized
1733
+ * Future modules will be added here:
1734
+ * - database: Database operations
1735
+ * - storage: File storage operations
1736
+ * - functions: Serverless functions
1737
+ * - tables: Table management
1738
+ * - metadata: Backend metadata
1802
1739
  */
1803
- isInitialized() {
1804
- return this.initialized;
1805
- }
1806
1740
  };
1807
1741
 
1808
1742
  // src/index.ts
@@ -1819,14 +1753,11 @@ var index_default = InsForgeClient;
1819
1753
  HttpClient,
1820
1754
  InsForgeClient,
1821
1755
  InsForgeError,
1822
- PersistentSessionStorage,
1756
+ LocalSessionStorage,
1823
1757
  SecureSessionStorage,
1824
1758
  Storage,
1825
1759
  StorageBucket,
1826
1760
  TokenManager,
1827
- createClient,
1828
- createSessionStorage,
1829
- discoverCapabilities,
1830
- getDefaultCapabilities
1761
+ createClient
1831
1762
  });
1832
1763
  //# sourceMappingURL=index.js.map