@insforge/sdk 1.0.1-refresh.1 → 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.d.mts +131 -89
- package/dist/index.d.ts +131 -89
- package/dist/index.js +165 -100
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +162 -97
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -140,9 +140,11 @@ var HttpClient = class {
|
|
|
140
140
|
this.isRefreshing = true;
|
|
141
141
|
try {
|
|
142
142
|
const newToken = await this.refreshCallback();
|
|
143
|
-
this.refreshQueue.forEach(({ resolve }) => {
|
|
143
|
+
this.refreshQueue.forEach(({ resolve, reject }) => {
|
|
144
144
|
if (newToken) {
|
|
145
145
|
resolve(newToken);
|
|
146
|
+
} else {
|
|
147
|
+
reject(new Error("Token refresh failed"));
|
|
146
148
|
}
|
|
147
149
|
});
|
|
148
150
|
this.refreshQueue = [];
|
|
@@ -243,9 +245,9 @@ var SecureSessionStorage = class {
|
|
|
243
245
|
return document.cookie.includes(`${AUTH_FLAG_COOKIE}=true`);
|
|
244
246
|
}
|
|
245
247
|
};
|
|
246
|
-
var
|
|
248
|
+
var LocalSessionStorage = class {
|
|
247
249
|
constructor(storage) {
|
|
248
|
-
this.strategyId = "
|
|
250
|
+
this.strategyId = "local";
|
|
249
251
|
if (storage) {
|
|
250
252
|
this.storage = storage;
|
|
251
253
|
} else if (typeof window !== "undefined" && window.localStorage) {
|
|
@@ -311,10 +313,10 @@ var PersistentSessionStorage = class {
|
|
|
311
313
|
var TokenManager = class {
|
|
312
314
|
/**
|
|
313
315
|
* Create a new TokenManager
|
|
314
|
-
* @param storage - Optional custom storage adapter (used for initial
|
|
316
|
+
* @param storage - Optional custom storage adapter (used for initial LocalSessionStorage)
|
|
315
317
|
*/
|
|
316
318
|
constructor(storage) {
|
|
317
|
-
this.strategy = new
|
|
319
|
+
this.strategy = new LocalSessionStorage(storage);
|
|
318
320
|
}
|
|
319
321
|
/**
|
|
320
322
|
* Set the storage strategy
|
|
@@ -386,12 +388,12 @@ var TokenManager = class {
|
|
|
386
388
|
}
|
|
387
389
|
};
|
|
388
390
|
|
|
389
|
-
// src/lib/
|
|
390
|
-
var
|
|
391
|
+
// src/lib/backend-config.ts
|
|
392
|
+
var DEFAULT_CONFIG = {
|
|
391
393
|
secureSessionStorage: false,
|
|
392
394
|
refreshTokens: false
|
|
393
395
|
};
|
|
394
|
-
async function
|
|
396
|
+
async function discoverBackendConfig(baseUrl, fetchImpl = globalThis.fetch) {
|
|
395
397
|
try {
|
|
396
398
|
const response = await fetchImpl(`${baseUrl}/api/health`, {
|
|
397
399
|
method: "GET",
|
|
@@ -400,25 +402,25 @@ async function discoverCapabilities(baseUrl, fetchImpl = globalThis.fetch) {
|
|
|
400
402
|
}
|
|
401
403
|
});
|
|
402
404
|
if (!response.ok) {
|
|
403
|
-
return
|
|
405
|
+
return DEFAULT_CONFIG;
|
|
404
406
|
}
|
|
405
407
|
const health = await response.json();
|
|
406
|
-
if (health.
|
|
407
|
-
return health.
|
|
408
|
+
if (health.config) {
|
|
409
|
+
return health.config;
|
|
408
410
|
}
|
|
409
|
-
return
|
|
411
|
+
return DEFAULT_CONFIG;
|
|
410
412
|
} catch {
|
|
411
|
-
return
|
|
413
|
+
return DEFAULT_CONFIG;
|
|
412
414
|
}
|
|
413
415
|
}
|
|
414
|
-
function createSessionStorage(
|
|
415
|
-
if (
|
|
416
|
+
function createSessionStorage(config, storage) {
|
|
417
|
+
if (config.secureSessionStorage && config.refreshTokens) {
|
|
416
418
|
return new SecureSessionStorage();
|
|
417
419
|
}
|
|
418
|
-
return new
|
|
420
|
+
return new LocalSessionStorage(storage);
|
|
419
421
|
}
|
|
420
|
-
function
|
|
421
|
-
return { ...
|
|
422
|
+
function getDefaultBackendConfig() {
|
|
423
|
+
return { ...DEFAULT_CONFIG };
|
|
422
424
|
}
|
|
423
425
|
|
|
424
426
|
// src/modules/database-postgrest.ts
|
|
@@ -527,37 +529,102 @@ function isHostedAuthEnvironment() {
|
|
|
527
529
|
return false;
|
|
528
530
|
}
|
|
529
531
|
var Auth = class {
|
|
530
|
-
constructor(http, tokenManager) {
|
|
532
|
+
constructor(http, tokenManager, initializePromise) {
|
|
531
533
|
this.http = http;
|
|
532
534
|
this.tokenManager = tokenManager;
|
|
533
|
-
this.
|
|
535
|
+
this.authStateListeners = /* @__PURE__ */ new Set();
|
|
534
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
|
+
};
|
|
535
585
|
}
|
|
536
586
|
/**
|
|
537
|
-
*
|
|
538
|
-
*
|
|
587
|
+
* Emit auth state change to all listeners
|
|
588
|
+
* @internal
|
|
539
589
|
*/
|
|
540
|
-
|
|
541
|
-
this.
|
|
542
|
-
|
|
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
|
+
});
|
|
543
598
|
}
|
|
544
599
|
/**
|
|
545
|
-
*
|
|
600
|
+
* Check if an error represents an authentication failure
|
|
601
|
+
* Used to determine appropriate HTTP status code (401 vs 500)
|
|
546
602
|
*/
|
|
547
|
-
|
|
548
|
-
if (
|
|
549
|
-
|
|
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));
|
|
550
618
|
}
|
|
619
|
+
return false;
|
|
551
620
|
}
|
|
552
621
|
/**
|
|
553
|
-
*
|
|
554
|
-
*
|
|
555
|
-
* Matches the backend's OAuth callback response (backend/src/api/routes/auth.ts:540-544)
|
|
622
|
+
* Detect and handle OAuth callback parameters in the URL.
|
|
623
|
+
* Called by client after initialization.
|
|
556
624
|
*/
|
|
557
|
-
|
|
625
|
+
detectAuthCallback() {
|
|
558
626
|
if (typeof window === "undefined") return;
|
|
559
627
|
try {
|
|
560
|
-
await this.waitForInit();
|
|
561
628
|
const params = new URLSearchParams(window.location.search);
|
|
562
629
|
const accessToken = params.get("access_token");
|
|
563
630
|
const userId = params.get("user_id");
|
|
@@ -588,6 +655,7 @@ var Auth = class {
|
|
|
588
655
|
url.searchParams.delete("error");
|
|
589
656
|
}
|
|
590
657
|
window.history.replaceState({}, document.title, url.toString());
|
|
658
|
+
this._emitAuthStateChange("SIGNED_IN", session);
|
|
591
659
|
}
|
|
592
660
|
} catch {
|
|
593
661
|
}
|
|
@@ -597,7 +665,6 @@ var Auth = class {
|
|
|
597
665
|
*/
|
|
598
666
|
async signUp(request) {
|
|
599
667
|
try {
|
|
600
|
-
await this.waitForInit();
|
|
601
668
|
const response = await this.http.post("/api/auth/users", request);
|
|
602
669
|
if (response.accessToken && response.user) {
|
|
603
670
|
const session = {
|
|
@@ -608,6 +675,7 @@ var Auth = class {
|
|
|
608
675
|
this.tokenManager.saveSession(session);
|
|
609
676
|
}
|
|
610
677
|
this.http.setAuthToken(response.accessToken);
|
|
678
|
+
this._emitAuthStateChange("SIGNED_IN", session);
|
|
611
679
|
}
|
|
612
680
|
return {
|
|
613
681
|
data: response,
|
|
@@ -632,7 +700,6 @@ var Auth = class {
|
|
|
632
700
|
*/
|
|
633
701
|
async signInWithPassword(request) {
|
|
634
702
|
try {
|
|
635
|
-
await this.waitForInit();
|
|
636
703
|
const response = await this.http.post("/api/auth/sessions", request);
|
|
637
704
|
const session = {
|
|
638
705
|
accessToken: response.accessToken || "",
|
|
@@ -649,6 +716,7 @@ var Auth = class {
|
|
|
649
716
|
this.tokenManager.saveSession(session);
|
|
650
717
|
}
|
|
651
718
|
this.http.setAuthToken(response.accessToken || "");
|
|
719
|
+
this._emitAuthStateChange("SIGNED_IN", session);
|
|
652
720
|
return {
|
|
653
721
|
data: response,
|
|
654
722
|
error: null
|
|
@@ -715,6 +783,7 @@ var Auth = class {
|
|
|
715
783
|
}
|
|
716
784
|
this.tokenManager.clearSession();
|
|
717
785
|
this.http.setAuthToken(null);
|
|
786
|
+
this._emitAuthStateChange("SIGNED_OUT", null);
|
|
718
787
|
return { error: null };
|
|
719
788
|
} catch (error) {
|
|
720
789
|
return {
|
|
@@ -743,6 +812,8 @@ var Auth = class {
|
|
|
743
812
|
if (response.user) {
|
|
744
813
|
this.tokenManager.setUser(response.user);
|
|
745
814
|
}
|
|
815
|
+
const session = this.tokenManager.getSession();
|
|
816
|
+
this._emitAuthStateChange("TOKEN_REFRESHED", session);
|
|
746
817
|
return response.accessToken;
|
|
747
818
|
}
|
|
748
819
|
throw new InsForgeError(
|
|
@@ -751,14 +822,22 @@ var Auth = class {
|
|
|
751
822
|
"REFRESH_FAILED"
|
|
752
823
|
);
|
|
753
824
|
} catch (error) {
|
|
754
|
-
this.tokenManager.clearSession();
|
|
755
|
-
this.http.setAuthToken(null);
|
|
756
825
|
if (error instanceof InsForgeError) {
|
|
826
|
+
if (error.statusCode === 401 || error.statusCode === 403) {
|
|
827
|
+
this.tokenManager.clearSession();
|
|
828
|
+
this.http.setAuthToken(null);
|
|
829
|
+
}
|
|
757
830
|
throw error;
|
|
758
831
|
}
|
|
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
|
+
}
|
|
759
838
|
throw new InsForgeError(
|
|
760
|
-
|
|
761
|
-
401,
|
|
839
|
+
errorMessage,
|
|
840
|
+
isAuthError ? 401 : 500,
|
|
762
841
|
"REFRESH_FAILED"
|
|
763
842
|
);
|
|
764
843
|
}
|
|
@@ -1069,7 +1148,6 @@ var Auth = class {
|
|
|
1069
1148
|
*/
|
|
1070
1149
|
async verifyEmail(request) {
|
|
1071
1150
|
try {
|
|
1072
|
-
await this.waitForInit();
|
|
1073
1151
|
const response = await this.http.post(
|
|
1074
1152
|
"/api/auth/email/verify",
|
|
1075
1153
|
request
|
|
@@ -1081,6 +1159,7 @@ var Auth = class {
|
|
|
1081
1159
|
};
|
|
1082
1160
|
this.tokenManager.saveSession(session);
|
|
1083
1161
|
this.http.setAuthToken(response.accessToken);
|
|
1162
|
+
this._emitAuthStateChange("SIGNED_IN", session);
|
|
1084
1163
|
}
|
|
1085
1164
|
return {
|
|
1086
1165
|
data: response,
|
|
@@ -1626,11 +1705,13 @@ var Functions = class {
|
|
|
1626
1705
|
// src/client.ts
|
|
1627
1706
|
var InsForgeClient = class {
|
|
1628
1707
|
constructor(config = {}) {
|
|
1629
|
-
this.
|
|
1630
|
-
this.
|
|
1631
|
-
|
|
1708
|
+
this.backendConfig = null;
|
|
1709
|
+
this.initializePromise = new Promise((resolve) => {
|
|
1710
|
+
this.initializeResolve = resolve;
|
|
1711
|
+
});
|
|
1632
1712
|
this.http = new HttpClient(config);
|
|
1633
1713
|
this.tokenManager = new TokenManager(config.storage);
|
|
1714
|
+
this.auth = new Auth(this.http, this.tokenManager, this.initializePromise);
|
|
1634
1715
|
if (config.edgeFunctionToken) {
|
|
1635
1716
|
this.http.setAuthToken(config.edgeFunctionToken);
|
|
1636
1717
|
this.tokenManager.saveSession({
|
|
@@ -1639,7 +1720,6 @@ var InsForgeClient = class {
|
|
|
1639
1720
|
// Will be populated by getCurrentUser()
|
|
1640
1721
|
});
|
|
1641
1722
|
}
|
|
1642
|
-
this.auth = new Auth(this.http, this.tokenManager);
|
|
1643
1723
|
this.http.setRefreshCallback(async () => {
|
|
1644
1724
|
try {
|
|
1645
1725
|
return await this.auth.refreshToken();
|
|
@@ -1655,67 +1735,58 @@ var InsForgeClient = class {
|
|
|
1655
1735
|
this.storage = new Storage(this.http);
|
|
1656
1736
|
this.ai = new AI(this.http);
|
|
1657
1737
|
this.functions = new Functions(this.http);
|
|
1658
|
-
this.
|
|
1659
|
-
this.auth.setInitPromise(this.initializationPromise);
|
|
1738
|
+
this._initializeAsync();
|
|
1660
1739
|
}
|
|
1661
1740
|
/**
|
|
1662
|
-
*
|
|
1663
|
-
*
|
|
1664
|
-
*
|
|
1665
|
-
* @example
|
|
1666
|
-
* ```typescript
|
|
1667
|
-
* const client = new InsForgeClient({ baseUrl: 'https://api.example.com' });
|
|
1668
|
-
* await client.initialize(); // Wait for capability discovery
|
|
1669
|
-
* ```
|
|
1741
|
+
* Internal async initialization - discovers backend config and recovers session.
|
|
1742
|
+
* Emits INITIAL_SESSION event when complete.
|
|
1743
|
+
* @internal
|
|
1670
1744
|
*/
|
|
1671
|
-
async
|
|
1672
|
-
if (this.initializationPromise) {
|
|
1673
|
-
await this.initializationPromise;
|
|
1674
|
-
}
|
|
1675
|
-
}
|
|
1676
|
-
/**
|
|
1677
|
-
* Internal async initialization - discovers capabilities and configures storage strategy
|
|
1678
|
-
*/
|
|
1679
|
-
async initializeAsync() {
|
|
1680
|
-
if (this.initialized) return;
|
|
1745
|
+
async _initializeAsync() {
|
|
1681
1746
|
try {
|
|
1682
|
-
this.
|
|
1747
|
+
this.backendConfig = await discoverBackendConfig(
|
|
1683
1748
|
this.http.baseUrl,
|
|
1684
1749
|
this.http.fetch
|
|
1685
1750
|
);
|
|
1686
|
-
const strategy = createSessionStorage(this.
|
|
1751
|
+
const strategy = createSessionStorage(this.backendConfig);
|
|
1687
1752
|
this.tokenManager.setStrategy(strategy);
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
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
|
+
}
|
|
1695
1764
|
}
|
|
1696
1765
|
}
|
|
1697
|
-
this.
|
|
1766
|
+
this.initializeResolve();
|
|
1698
1767
|
} catch {
|
|
1699
|
-
this.
|
|
1768
|
+
this.auth.detectAuthCallback();
|
|
1769
|
+
this.initializeResolve();
|
|
1700
1770
|
}
|
|
1701
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
|
+
}
|
|
1702
1779
|
/**
|
|
1703
1780
|
* Get the underlying HTTP client for custom requests
|
|
1704
|
-
*
|
|
1705
|
-
* @example
|
|
1706
|
-
* ```typescript
|
|
1707
|
-
* const httpClient = client.getHttpClient();
|
|
1708
|
-
* const customData = await httpClient.get('/api/custom-endpoint');
|
|
1709
|
-
* ```
|
|
1710
1781
|
*/
|
|
1711
1782
|
getHttpClient() {
|
|
1712
1783
|
return this.http;
|
|
1713
1784
|
}
|
|
1714
1785
|
/**
|
|
1715
|
-
* Get the discovered backend
|
|
1786
|
+
* Get the discovered backend configuration
|
|
1716
1787
|
*/
|
|
1717
|
-
|
|
1718
|
-
return this.
|
|
1788
|
+
getBackendConfig() {
|
|
1789
|
+
return this.backendConfig;
|
|
1719
1790
|
}
|
|
1720
1791
|
/**
|
|
1721
1792
|
* Get the current storage strategy identifier
|
|
@@ -1723,18 +1794,12 @@ var InsForgeClient = class {
|
|
|
1723
1794
|
getStorageStrategy() {
|
|
1724
1795
|
return this.tokenManager.getStrategyId();
|
|
1725
1796
|
}
|
|
1726
|
-
/**
|
|
1727
|
-
* Check if the client has been fully initialized
|
|
1728
|
-
*/
|
|
1729
|
-
isInitialized() {
|
|
1730
|
-
return this.initialized;
|
|
1731
|
-
}
|
|
1732
1797
|
};
|
|
1733
|
-
|
|
1734
|
-
// src/index.ts
|
|
1735
|
-
function createClient(config) {
|
|
1798
|
+
function createClient(config = {}) {
|
|
1736
1799
|
return new InsForgeClient(config);
|
|
1737
1800
|
}
|
|
1801
|
+
|
|
1802
|
+
// src/index.ts
|
|
1738
1803
|
var index_default = InsForgeClient;
|
|
1739
1804
|
export {
|
|
1740
1805
|
AI,
|
|
@@ -1744,7 +1809,7 @@ export {
|
|
|
1744
1809
|
HttpClient,
|
|
1745
1810
|
InsForgeClient,
|
|
1746
1811
|
InsForgeError,
|
|
1747
|
-
|
|
1812
|
+
LocalSessionStorage,
|
|
1748
1813
|
SecureSessionStorage,
|
|
1749
1814
|
Storage,
|
|
1750
1815
|
StorageBucket,
|
|
@@ -1752,7 +1817,7 @@ export {
|
|
|
1752
1817
|
createClient,
|
|
1753
1818
|
createSessionStorage,
|
|
1754
1819
|
index_default as default,
|
|
1755
|
-
|
|
1756
|
-
|
|
1820
|
+
discoverBackendConfig,
|
|
1821
|
+
getDefaultBackendConfig
|
|
1757
1822
|
};
|
|
1758
1823
|
//# sourceMappingURL=index.mjs.map
|