@insforge/sdk 1.2.6 → 1.2.8-dev.0
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/README.md +74 -78
- package/dist/index.d.mts +45 -14
- package/dist/index.d.ts +45 -14
- package/dist/index.js +267 -125
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +267 -125
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -325,6 +325,7 @@ var TokenManager = class {
|
|
|
325
325
|
// src/lib/http-client.ts
|
|
326
326
|
var RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([500, 502, 503, 504]);
|
|
327
327
|
var IDEMPOTENT_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD", "PUT", "DELETE", "OPTIONS"]);
|
|
328
|
+
var REFRESHABLE_AUTH_ERROR_CODE = "AUTH_UNAUTHORIZED";
|
|
328
329
|
function serializeBody(method, body, headers) {
|
|
329
330
|
if (body === void 0) return void 0;
|
|
330
331
|
if (method === "GET" || method === "HEAD") return void 0;
|
|
@@ -379,12 +380,11 @@ var HttpClient = class {
|
|
|
379
380
|
*/
|
|
380
381
|
constructor(config, tokenManager, logger) {
|
|
381
382
|
this.userToken = null;
|
|
382
|
-
this.autoRefreshToken = true;
|
|
383
383
|
this.isRefreshing = false;
|
|
384
384
|
this.refreshPromise = null;
|
|
385
385
|
this.refreshToken = null;
|
|
386
|
+
this.config = config;
|
|
386
387
|
this.baseUrl = config.baseUrl || "http://localhost:7130";
|
|
387
|
-
this.autoRefreshToken = config.autoRefreshToken ?? true;
|
|
388
388
|
this.fetch = config.fetch || (globalThis.fetch ? globalThis.fetch.bind(globalThis) : void 0);
|
|
389
389
|
this.anonKey = config.anonKey;
|
|
390
390
|
this.defaultHeaders = {
|
|
@@ -434,48 +434,19 @@ var HttpClient = class {
|
|
|
434
434
|
const jitter = base * (0.85 + Math.random() * 0.3);
|
|
435
435
|
return Math.round(jitter);
|
|
436
436
|
}
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
* @param method - HTTP method (GET, POST, PUT, PATCH, DELETE).
|
|
442
|
-
* @param path - API path relative to the base URL.
|
|
443
|
-
* @param options - Optional request configuration including headers, body, and query params.
|
|
444
|
-
* @returns Parsed response data.
|
|
445
|
-
* @throws {InsForgeError} On timeout, network failure, or HTTP error responses.
|
|
446
|
-
*/
|
|
447
|
-
async handleRequest(method, path, options = {}) {
|
|
437
|
+
shouldRefreshAccessToken(statusCode, errorCode, options = {}) {
|
|
438
|
+
return statusCode === 401 && errorCode === REFRESHABLE_AUTH_ERROR_CODE && !(this.config.isServerMode ?? !!this.config.edgeFunctionToken) && !options.skipAuthRefresh && this.userToken !== null;
|
|
439
|
+
}
|
|
440
|
+
async fetchWithRetry(args) {
|
|
448
441
|
const {
|
|
449
|
-
|
|
450
|
-
|
|
442
|
+
method,
|
|
443
|
+
url,
|
|
444
|
+
headers,
|
|
451
445
|
body,
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
const startTime = Date.now();
|
|
457
|
-
const canRetry = IDEMPOTENT_METHODS.has(method.toUpperCase()) || options.idempotent === true;
|
|
458
|
-
const maxAttempts = canRetry ? this.retryCount : 0;
|
|
459
|
-
const requestHeaders = {
|
|
460
|
-
...this.defaultHeaders
|
|
461
|
-
};
|
|
462
|
-
const authToken = this.userToken || this.anonKey;
|
|
463
|
-
if (authToken) {
|
|
464
|
-
requestHeaders["Authorization"] = `Bearer ${authToken}`;
|
|
465
|
-
}
|
|
466
|
-
const processedBody = serializeBody(method, body, requestHeaders);
|
|
467
|
-
if (headers instanceof Headers) {
|
|
468
|
-
headers.forEach((value, key) => {
|
|
469
|
-
requestHeaders[key] = value;
|
|
470
|
-
});
|
|
471
|
-
} else if (Array.isArray(headers)) {
|
|
472
|
-
headers.forEach(([key, value]) => {
|
|
473
|
-
requestHeaders[key] = value;
|
|
474
|
-
});
|
|
475
|
-
} else {
|
|
476
|
-
Object.assign(requestHeaders, headers);
|
|
477
|
-
}
|
|
478
|
-
this.logger.logRequest(method, url, requestHeaders, processedBody);
|
|
446
|
+
fetchOptions,
|
|
447
|
+
callerSignal,
|
|
448
|
+
maxAttempts
|
|
449
|
+
} = args;
|
|
479
450
|
let lastError;
|
|
480
451
|
for (let attempt = 0; attempt <= maxAttempts; attempt++) {
|
|
481
452
|
if (attempt > 0) {
|
|
@@ -527,8 +498,8 @@ var HttpClient = class {
|
|
|
527
498
|
try {
|
|
528
499
|
const response = await this.fetch(url, {
|
|
529
500
|
method,
|
|
530
|
-
headers
|
|
531
|
-
body
|
|
501
|
+
headers,
|
|
502
|
+
body,
|
|
532
503
|
...fetchOptions,
|
|
533
504
|
...controller ? { signal: controller.signal } : {}
|
|
534
505
|
});
|
|
@@ -542,31 +513,8 @@ var HttpClient = class {
|
|
|
542
513
|
);
|
|
543
514
|
continue;
|
|
544
515
|
}
|
|
545
|
-
let data;
|
|
546
|
-
try {
|
|
547
|
-
data = await parseResponse(response);
|
|
548
|
-
} catch (err) {
|
|
549
|
-
if (timer !== void 0) clearTimeout(timer);
|
|
550
|
-
if (err instanceof InsForgeError) {
|
|
551
|
-
this.logger.logResponse(
|
|
552
|
-
method,
|
|
553
|
-
url,
|
|
554
|
-
err.statusCode || response.status,
|
|
555
|
-
Date.now() - startTime,
|
|
556
|
-
err
|
|
557
|
-
);
|
|
558
|
-
}
|
|
559
|
-
throw err;
|
|
560
|
-
}
|
|
561
516
|
if (timer !== void 0) clearTimeout(timer);
|
|
562
|
-
|
|
563
|
-
method,
|
|
564
|
-
url,
|
|
565
|
-
response.status,
|
|
566
|
-
Date.now() - startTime,
|
|
567
|
-
data
|
|
568
|
-
);
|
|
569
|
-
return data;
|
|
517
|
+
return response;
|
|
570
518
|
} catch (err) {
|
|
571
519
|
if (timer !== void 0) clearTimeout(timer);
|
|
572
520
|
if (err?.name === "AbortError") {
|
|
@@ -579,9 +527,6 @@ var HttpClient = class {
|
|
|
579
527
|
}
|
|
580
528
|
throw err;
|
|
581
529
|
}
|
|
582
|
-
if (err instanceof InsForgeError) {
|
|
583
|
-
throw err;
|
|
584
|
-
}
|
|
585
530
|
if (attempt < maxAttempts) {
|
|
586
531
|
lastError = err;
|
|
587
532
|
continue;
|
|
@@ -599,33 +544,176 @@ var HttpClient = class {
|
|
|
599
544
|
"NETWORK_ERROR"
|
|
600
545
|
);
|
|
601
546
|
}
|
|
547
|
+
/**
|
|
548
|
+
* Performs an HTTP request with automatic retry and timeout handling.
|
|
549
|
+
* Retries on network errors and 5xx server errors with exponential backoff.
|
|
550
|
+
* Client errors (4xx) and timeouts are thrown immediately without retry.
|
|
551
|
+
* @param method - HTTP method (GET, POST, PUT, PATCH, DELETE).
|
|
552
|
+
* @param path - API path relative to the base URL.
|
|
553
|
+
* @param options - Optional request configuration including headers, body, and query params.
|
|
554
|
+
* @returns Parsed response data.
|
|
555
|
+
* @throws {InsForgeError} On timeout, network failure, or HTTP error responses.
|
|
556
|
+
*/
|
|
557
|
+
async handleRequest(method, path, options = {}) {
|
|
558
|
+
const {
|
|
559
|
+
params,
|
|
560
|
+
headers = {},
|
|
561
|
+
body,
|
|
562
|
+
skipAuthRefresh: _skipAuthRefresh,
|
|
563
|
+
signal: callerSignal,
|
|
564
|
+
...fetchOptions
|
|
565
|
+
} = options;
|
|
566
|
+
const url = this.buildUrl(path, params);
|
|
567
|
+
const startTime = Date.now();
|
|
568
|
+
const canRetry = IDEMPOTENT_METHODS.has(method.toUpperCase()) || options.idempotent === true;
|
|
569
|
+
const maxAttempts = canRetry ? this.retryCount : 0;
|
|
570
|
+
const requestHeaders = {
|
|
571
|
+
...this.defaultHeaders
|
|
572
|
+
};
|
|
573
|
+
const authToken = this.userToken || this.anonKey;
|
|
574
|
+
if (authToken) {
|
|
575
|
+
requestHeaders["Authorization"] = `Bearer ${authToken}`;
|
|
576
|
+
}
|
|
577
|
+
const processedBody = serializeBody(method, body, requestHeaders);
|
|
578
|
+
if (headers instanceof Headers) {
|
|
579
|
+
headers.forEach((value, key) => {
|
|
580
|
+
requestHeaders[key] = value;
|
|
581
|
+
});
|
|
582
|
+
} else if (Array.isArray(headers)) {
|
|
583
|
+
headers.forEach(([key, value]) => {
|
|
584
|
+
requestHeaders[key] = value;
|
|
585
|
+
});
|
|
586
|
+
} else {
|
|
587
|
+
Object.assign(requestHeaders, headers);
|
|
588
|
+
}
|
|
589
|
+
this.logger.logRequest(method, url, requestHeaders, processedBody);
|
|
590
|
+
const response = await this.fetchWithRetry({
|
|
591
|
+
method,
|
|
592
|
+
url,
|
|
593
|
+
headers: requestHeaders,
|
|
594
|
+
body: processedBody,
|
|
595
|
+
fetchOptions,
|
|
596
|
+
callerSignal,
|
|
597
|
+
maxAttempts
|
|
598
|
+
});
|
|
599
|
+
let data;
|
|
600
|
+
try {
|
|
601
|
+
data = await parseResponse(response);
|
|
602
|
+
} catch (err) {
|
|
603
|
+
if (err instanceof InsForgeError) {
|
|
604
|
+
this.logger.logResponse(
|
|
605
|
+
method,
|
|
606
|
+
url,
|
|
607
|
+
err.statusCode || response.status,
|
|
608
|
+
Date.now() - startTime,
|
|
609
|
+
err
|
|
610
|
+
);
|
|
611
|
+
}
|
|
612
|
+
throw err;
|
|
613
|
+
}
|
|
614
|
+
this.logger.logResponse(
|
|
615
|
+
method,
|
|
616
|
+
url,
|
|
617
|
+
response.status,
|
|
618
|
+
Date.now() - startTime,
|
|
619
|
+
data
|
|
620
|
+
);
|
|
621
|
+
return data;
|
|
622
|
+
}
|
|
602
623
|
async request(method, path, options = {}) {
|
|
603
624
|
try {
|
|
604
625
|
return await this.handleRequest(method, path, { ...options });
|
|
605
626
|
} catch (error) {
|
|
606
|
-
if (error instanceof InsForgeError && error.statusCode
|
|
627
|
+
if (error instanceof InsForgeError && this.shouldRefreshAccessToken(error.statusCode, error.error, options)) {
|
|
607
628
|
try {
|
|
608
|
-
|
|
609
|
-
this.setAuthToken(newTokenData.accessToken);
|
|
610
|
-
this.tokenManager.saveSession(newTokenData);
|
|
611
|
-
if (newTokenData.csrfToken) {
|
|
612
|
-
setCsrfToken(newTokenData.csrfToken);
|
|
613
|
-
}
|
|
614
|
-
if (newTokenData.refreshToken) {
|
|
615
|
-
this.setRefreshToken(newTokenData.refreshToken);
|
|
616
|
-
}
|
|
617
|
-
return await this.handleRequest(method, path, { ...options });
|
|
629
|
+
await this.refreshAndSaveSession();
|
|
618
630
|
} catch (error2) {
|
|
619
|
-
this.
|
|
620
|
-
this.userToken = null;
|
|
621
|
-
this.refreshToken = null;
|
|
622
|
-
clearCsrfToken();
|
|
631
|
+
this.clearAuthSession();
|
|
623
632
|
throw error2;
|
|
624
633
|
}
|
|
634
|
+
return await this.handleRequest(method, path, { ...options });
|
|
625
635
|
}
|
|
626
636
|
throw error;
|
|
627
637
|
}
|
|
628
638
|
}
|
|
639
|
+
/**
|
|
640
|
+
* Performs an SDK-configured fetch and returns the raw Response.
|
|
641
|
+
* This is used by clients such as postgrest-js that need to own response
|
|
642
|
+
* parsing while still sharing SDK auth and refresh behavior.
|
|
643
|
+
*/
|
|
644
|
+
async rawFetch(input, init, options = {}) {
|
|
645
|
+
const request = typeof Request !== "undefined" && input instanceof Request ? input : void 0;
|
|
646
|
+
const {
|
|
647
|
+
method: initMethod,
|
|
648
|
+
headers: initHeaders,
|
|
649
|
+
body: initBody,
|
|
650
|
+
signal: initSignal,
|
|
651
|
+
...fetchOptions
|
|
652
|
+
} = init ?? {};
|
|
653
|
+
const method = initMethod ?? request?.method ?? "GET";
|
|
654
|
+
const url = request?.url ?? input.toString();
|
|
655
|
+
const startTime = Date.now();
|
|
656
|
+
const headers = new Headers(this.getHeaders());
|
|
657
|
+
request?.headers.forEach((value, key) => {
|
|
658
|
+
headers.set(key, value);
|
|
659
|
+
});
|
|
660
|
+
new Headers(initHeaders).forEach((value, key) => {
|
|
661
|
+
headers.set(key, value);
|
|
662
|
+
});
|
|
663
|
+
const requestHeaders = {};
|
|
664
|
+
headers.forEach((value, key) => {
|
|
665
|
+
requestHeaders[key] = value;
|
|
666
|
+
});
|
|
667
|
+
const body = initBody ?? request?.body ?? void 0;
|
|
668
|
+
const callerSignal = initSignal ?? request?.signal;
|
|
669
|
+
const maxAttempts = IDEMPOTENT_METHODS.has(method.toUpperCase()) ? this.retryCount : 0;
|
|
670
|
+
this.logger.logRequest(method, url, requestHeaders, body);
|
|
671
|
+
const response = await this.fetchWithRetry({
|
|
672
|
+
method,
|
|
673
|
+
url,
|
|
674
|
+
headers: requestHeaders,
|
|
675
|
+
body,
|
|
676
|
+
fetchOptions,
|
|
677
|
+
callerSignal,
|
|
678
|
+
maxAttempts
|
|
679
|
+
});
|
|
680
|
+
this.logger.logResponse(
|
|
681
|
+
method,
|
|
682
|
+
url,
|
|
683
|
+
response.status,
|
|
684
|
+
Date.now() - startTime
|
|
685
|
+
);
|
|
686
|
+
let errorCode = null;
|
|
687
|
+
if (response.status === 401) {
|
|
688
|
+
try {
|
|
689
|
+
const data = await response.clone().json();
|
|
690
|
+
if (data && typeof data === "object") {
|
|
691
|
+
const candidate = data.error ?? data.code;
|
|
692
|
+
if (typeof candidate === "string") {
|
|
693
|
+
errorCode = candidate;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
} catch {
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
if (!this.shouldRefreshAccessToken(response.status, errorCode, options)) {
|
|
700
|
+
return response;
|
|
701
|
+
}
|
|
702
|
+
let newTokenData;
|
|
703
|
+
try {
|
|
704
|
+
newTokenData = await this.refreshAndSaveSession();
|
|
705
|
+
} catch (error) {
|
|
706
|
+
this.clearAuthSession();
|
|
707
|
+
throw error;
|
|
708
|
+
}
|
|
709
|
+
const retryHeaders = new Headers(initHeaders);
|
|
710
|
+
retryHeaders.set("Authorization", `Bearer ${newTokenData.accessToken}`);
|
|
711
|
+
return await this.rawFetch(
|
|
712
|
+
input,
|
|
713
|
+
{ ...init, headers: retryHeaders },
|
|
714
|
+
{ skipAuthRefresh: true }
|
|
715
|
+
);
|
|
716
|
+
}
|
|
629
717
|
/** Performs a GET request. */
|
|
630
718
|
get(path, options) {
|
|
631
719
|
return this.request("GET", path, options);
|
|
@@ -662,7 +750,7 @@ var HttpClient = class {
|
|
|
662
750
|
}
|
|
663
751
|
return headers;
|
|
664
752
|
}
|
|
665
|
-
async
|
|
753
|
+
async refreshAccessToken() {
|
|
666
754
|
if (this.isRefreshing) {
|
|
667
755
|
return this.refreshPromise;
|
|
668
756
|
}
|
|
@@ -673,7 +761,7 @@ var HttpClient = class {
|
|
|
673
761
|
const body = this.refreshToken ? { refreshToken: this.refreshToken } : void 0;
|
|
674
762
|
const response = await this.handleRequest(
|
|
675
763
|
"POST",
|
|
676
|
-
"/api/auth/
|
|
764
|
+
this.refreshToken ? "/api/auth/refresh?client_type=mobile" : "/api/auth/refresh",
|
|
677
765
|
{
|
|
678
766
|
body,
|
|
679
767
|
headers: csrfToken ? { "X-CSRF-Token": csrfToken } : {},
|
|
@@ -688,6 +776,24 @@ var HttpClient = class {
|
|
|
688
776
|
})();
|
|
689
777
|
return this.refreshPromise;
|
|
690
778
|
}
|
|
779
|
+
async refreshAndSaveSession() {
|
|
780
|
+
const newTokenData = await this.refreshAccessToken();
|
|
781
|
+
this.setAuthToken(newTokenData.accessToken);
|
|
782
|
+
this.tokenManager.saveSession(newTokenData);
|
|
783
|
+
if (newTokenData.csrfToken) {
|
|
784
|
+
setCsrfToken(newTokenData.csrfToken);
|
|
785
|
+
}
|
|
786
|
+
if (newTokenData.refreshToken) {
|
|
787
|
+
this.setRefreshToken(newTokenData.refreshToken);
|
|
788
|
+
}
|
|
789
|
+
return newTokenData;
|
|
790
|
+
}
|
|
791
|
+
clearAuthSession() {
|
|
792
|
+
this.tokenManager.clearSession();
|
|
793
|
+
this.userToken = null;
|
|
794
|
+
this.refreshToken = null;
|
|
795
|
+
clearCsrfToken();
|
|
796
|
+
}
|
|
691
797
|
};
|
|
692
798
|
|
|
693
799
|
// src/modules/auth/helpers.ts
|
|
@@ -816,7 +922,7 @@ var Auth = class {
|
|
|
816
922
|
const response = await this.http.post(
|
|
817
923
|
this.isServerMode() ? "/api/auth/users?client_type=mobile" : "/api/auth/users",
|
|
818
924
|
request,
|
|
819
|
-
{ credentials: "include" }
|
|
925
|
+
{ credentials: "include", skipAuthRefresh: true }
|
|
820
926
|
);
|
|
821
927
|
if (response.accessToken && response.user) {
|
|
822
928
|
this.saveSessionFromResponse(response);
|
|
@@ -834,7 +940,7 @@ var Auth = class {
|
|
|
834
940
|
const response = await this.http.post(
|
|
835
941
|
this.isServerMode() ? "/api/auth/sessions?client_type=mobile" : "/api/auth/sessions",
|
|
836
942
|
request,
|
|
837
|
-
{ credentials: "include" }
|
|
943
|
+
{ credentials: "include", skipAuthRefresh: true }
|
|
838
944
|
);
|
|
839
945
|
this.saveSessionFromResponse(response);
|
|
840
946
|
if (response.refreshToken) {
|
|
@@ -851,7 +957,7 @@ var Auth = class {
|
|
|
851
957
|
await this.http.post(
|
|
852
958
|
this.isServerMode() ? "/api/auth/logout?client_type=mobile" : "/api/auth/logout",
|
|
853
959
|
void 0,
|
|
854
|
-
{ credentials: "include" }
|
|
960
|
+
{ credentials: "include", skipAuthRefresh: true }
|
|
855
961
|
);
|
|
856
962
|
} catch {
|
|
857
963
|
}
|
|
@@ -888,7 +994,8 @@ var Auth = class {
|
|
|
888
994
|
);
|
|
889
995
|
const oauthPath = isBuiltInProvider ? `/api/auth/oauth/${providerKey}` : `/api/auth/oauth/custom/${providerKey}`;
|
|
890
996
|
const response = await this.http.get(oauthPath, {
|
|
891
|
-
params
|
|
997
|
+
params,
|
|
998
|
+
skipAuthRefresh: true
|
|
892
999
|
});
|
|
893
1000
|
if (!this.isServerMode() && typeof window !== "undefined" && !skipBrowserRedirect) {
|
|
894
1001
|
window.location.href = response.authUrl;
|
|
@@ -936,7 +1043,7 @@ var Auth = class {
|
|
|
936
1043
|
const response = await this.http.post(
|
|
937
1044
|
this.isServerMode() ? "/api/auth/oauth/exchange?client_type=mobile" : "/api/auth/oauth/exchange",
|
|
938
1045
|
request,
|
|
939
|
-
{ credentials: "include" }
|
|
1046
|
+
{ credentials: "include", skipAuthRefresh: true }
|
|
940
1047
|
);
|
|
941
1048
|
this.saveSessionFromResponse(response);
|
|
942
1049
|
return {
|
|
@@ -963,7 +1070,7 @@ var Auth = class {
|
|
|
963
1070
|
const response = await this.http.post(
|
|
964
1071
|
"/api/auth/id-token?client_type=mobile",
|
|
965
1072
|
{ provider, token },
|
|
966
|
-
{ credentials: "include" }
|
|
1073
|
+
{ credentials: "include", skipAuthRefresh: true }
|
|
967
1074
|
);
|
|
968
1075
|
this.saveSessionFromResponse(response);
|
|
969
1076
|
if (response.refreshToken) {
|
|
@@ -1010,7 +1117,8 @@ var Auth = class {
|
|
|
1010
1117
|
this.isServerMode() ? { refresh_token: options?.refreshToken } : void 0,
|
|
1011
1118
|
{
|
|
1012
1119
|
headers: csrfToken ? { "X-CSRF-Token": csrfToken } : {},
|
|
1013
|
-
credentials: "include"
|
|
1120
|
+
credentials: "include",
|
|
1121
|
+
skipAuthRefresh: true
|
|
1014
1122
|
}
|
|
1015
1123
|
);
|
|
1016
1124
|
if (response.accessToken) {
|
|
@@ -1113,7 +1221,9 @@ var Auth = class {
|
|
|
1113
1221
|
// ============================================================================
|
|
1114
1222
|
async resendVerificationEmail(request) {
|
|
1115
1223
|
try {
|
|
1116
|
-
const response = await this.http.post("/api/auth/email/send-verification", request
|
|
1224
|
+
const response = await this.http.post("/api/auth/email/send-verification", request, {
|
|
1225
|
+
skipAuthRefresh: true
|
|
1226
|
+
});
|
|
1117
1227
|
return { data: response, error: null };
|
|
1118
1228
|
} catch (error) {
|
|
1119
1229
|
return wrapError(
|
|
@@ -1127,7 +1237,7 @@ var Auth = class {
|
|
|
1127
1237
|
const response = await this.http.post(
|
|
1128
1238
|
this.isServerMode() ? "/api/auth/email/verify?client_type=mobile" : "/api/auth/email/verify",
|
|
1129
1239
|
request,
|
|
1130
|
-
{ credentials: "include" }
|
|
1240
|
+
{ credentials: "include", skipAuthRefresh: true }
|
|
1131
1241
|
);
|
|
1132
1242
|
this.saveSessionFromResponse(response);
|
|
1133
1243
|
if (response.refreshToken) {
|
|
@@ -1146,7 +1256,9 @@ var Auth = class {
|
|
|
1146
1256
|
// ============================================================================
|
|
1147
1257
|
async sendResetPasswordEmail(request) {
|
|
1148
1258
|
try {
|
|
1149
|
-
const response = await this.http.post("/api/auth/email/send-reset-password", request
|
|
1259
|
+
const response = await this.http.post("/api/auth/email/send-reset-password", request, {
|
|
1260
|
+
skipAuthRefresh: true
|
|
1261
|
+
});
|
|
1150
1262
|
return { data: response, error: null };
|
|
1151
1263
|
} catch (error) {
|
|
1152
1264
|
return wrapError(
|
|
@@ -1159,7 +1271,8 @@ var Auth = class {
|
|
|
1159
1271
|
try {
|
|
1160
1272
|
const response = await this.http.post(
|
|
1161
1273
|
"/api/auth/email/exchange-reset-password-token",
|
|
1162
|
-
request
|
|
1274
|
+
request,
|
|
1275
|
+
{ skipAuthRefresh: true }
|
|
1163
1276
|
);
|
|
1164
1277
|
return { data: response, error: null };
|
|
1165
1278
|
} catch (error) {
|
|
@@ -1173,7 +1286,8 @@ var Auth = class {
|
|
|
1173
1286
|
try {
|
|
1174
1287
|
const response = await this.http.post(
|
|
1175
1288
|
"/api/auth/email/reset-password",
|
|
1176
|
-
request
|
|
1289
|
+
request,
|
|
1290
|
+
{ skipAuthRefresh: true }
|
|
1177
1291
|
);
|
|
1178
1292
|
return { data: response, error: null };
|
|
1179
1293
|
} catch (error) {
|
|
@@ -1189,7 +1303,8 @@ var Auth = class {
|
|
|
1189
1303
|
async getPublicAuthConfig() {
|
|
1190
1304
|
try {
|
|
1191
1305
|
const response = await this.http.get(
|
|
1192
|
-
"/api/auth/public-config"
|
|
1306
|
+
"/api/auth/public-config",
|
|
1307
|
+
{ skipAuthRefresh: true }
|
|
1193
1308
|
);
|
|
1194
1309
|
return { data: response, error: null };
|
|
1195
1310
|
} catch (error) {
|
|
@@ -1203,7 +1318,7 @@ var Auth = class {
|
|
|
1203
1318
|
|
|
1204
1319
|
// src/modules/database-postgrest.ts
|
|
1205
1320
|
var import_postgrest_js = require("@supabase/postgrest-js");
|
|
1206
|
-
function createInsForgePostgrestFetch(httpClient
|
|
1321
|
+
function createInsForgePostgrestFetch(httpClient) {
|
|
1207
1322
|
return async (input, init) => {
|
|
1208
1323
|
const url = typeof input === "string" ? input : input.toString();
|
|
1209
1324
|
const urlObj = new URL(url);
|
|
@@ -1211,14 +1326,11 @@ function createInsForgePostgrestFetch(httpClient, tokenManager) {
|
|
|
1211
1326
|
const rpcMatch = pathname.match(/^rpc\/(.+)$/);
|
|
1212
1327
|
const endpoint = rpcMatch ? `/api/database/rpc/${rpcMatch[1]}` : `/api/database/records/${pathname}`;
|
|
1213
1328
|
const insforgeUrl = `${httpClient.baseUrl}${endpoint}${urlObj.search}`;
|
|
1214
|
-
const
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
headers.set("Authorization", `Bearer ${authToken}`);
|
|
1220
|
-
}
|
|
1221
|
-
const response = await fetch(insforgeUrl, {
|
|
1329
|
+
const headers = new Headers(httpClient.getHeaders());
|
|
1330
|
+
new Headers(init?.headers).forEach((value, key) => {
|
|
1331
|
+
headers.set(key, value);
|
|
1332
|
+
});
|
|
1333
|
+
const response = await httpClient.rawFetch(insforgeUrl, {
|
|
1222
1334
|
...init,
|
|
1223
1335
|
headers
|
|
1224
1336
|
});
|
|
@@ -1226,42 +1338,42 @@ function createInsForgePostgrestFetch(httpClient, tokenManager) {
|
|
|
1226
1338
|
};
|
|
1227
1339
|
}
|
|
1228
1340
|
var Database = class {
|
|
1229
|
-
constructor(httpClient
|
|
1341
|
+
constructor(httpClient) {
|
|
1230
1342
|
this.postgrest = new import_postgrest_js.PostgrestClient("http://dummy", {
|
|
1231
|
-
fetch: createInsForgePostgrestFetch(httpClient
|
|
1343
|
+
fetch: createInsForgePostgrestFetch(httpClient),
|
|
1232
1344
|
headers: {}
|
|
1233
1345
|
});
|
|
1234
1346
|
}
|
|
1235
1347
|
/**
|
|
1236
1348
|
* Create a query builder for a table
|
|
1237
|
-
*
|
|
1349
|
+
*
|
|
1238
1350
|
* @example
|
|
1239
1351
|
* // Basic query
|
|
1240
1352
|
* const { data, error } = await client.database
|
|
1241
1353
|
* .from('posts')
|
|
1242
1354
|
* .select('*')
|
|
1243
1355
|
* .eq('user_id', userId);
|
|
1244
|
-
*
|
|
1356
|
+
*
|
|
1245
1357
|
* // With count (Supabase style!)
|
|
1246
1358
|
* const { data, error, count } = await client.database
|
|
1247
1359
|
* .from('posts')
|
|
1248
1360
|
* .select('*', { count: 'exact' })
|
|
1249
1361
|
* .range(0, 9);
|
|
1250
|
-
*
|
|
1362
|
+
*
|
|
1251
1363
|
* // Just get count, no data
|
|
1252
1364
|
* const { count } = await client.database
|
|
1253
1365
|
* .from('posts')
|
|
1254
1366
|
* .select('*', { count: 'exact', head: true });
|
|
1255
|
-
*
|
|
1367
|
+
*
|
|
1256
1368
|
* // Complex queries with OR
|
|
1257
1369
|
* const { data } = await client.database
|
|
1258
1370
|
* .from('posts')
|
|
1259
1371
|
* .select('*, users!inner(*)')
|
|
1260
1372
|
* .or('status.eq.active,status.eq.pending');
|
|
1261
|
-
*
|
|
1373
|
+
*
|
|
1262
1374
|
* // All features work:
|
|
1263
1375
|
* - Nested selects
|
|
1264
|
-
* - Foreign key expansion
|
|
1376
|
+
* - Foreign key expansion
|
|
1265
1377
|
* - OR/AND/NOT conditions
|
|
1266
1378
|
* - Count with head
|
|
1267
1379
|
* - Range pagination
|
|
@@ -2302,8 +2414,7 @@ var Payments = class {
|
|
|
2302
2414
|
*
|
|
2303
2415
|
* @example
|
|
2304
2416
|
* ```typescript
|
|
2305
|
-
* const { data, error } = await client.payments.createCheckoutSession({
|
|
2306
|
-
* environment: 'test',
|
|
2417
|
+
* const { data, error } = await client.payments.createCheckoutSession('test', {
|
|
2307
2418
|
* mode: 'payment',
|
|
2308
2419
|
* lineItems: [{ stripePriceId: 'price_123', quantity: 1 }],
|
|
2309
2420
|
* successUrl: `${window.location.origin}/success`,
|
|
@@ -2315,10 +2426,10 @@ var Payments = class {
|
|
|
2315
2426
|
* }
|
|
2316
2427
|
* ```
|
|
2317
2428
|
*/
|
|
2318
|
-
async createCheckoutSession(request) {
|
|
2429
|
+
async createCheckoutSession(environment, request) {
|
|
2319
2430
|
try {
|
|
2320
2431
|
const data = await this.http.post(
|
|
2321
|
-
|
|
2432
|
+
`/api/payments/${encodeURIComponent(environment)}/checkout-sessions`,
|
|
2322
2433
|
request,
|
|
2323
2434
|
{ idempotent: !!request.idempotencyKey }
|
|
2324
2435
|
);
|
|
@@ -2333,10 +2444,10 @@ var Payments = class {
|
|
|
2333
2444
|
/**
|
|
2334
2445
|
* Create a Stripe Billing Portal Session for a mapped billing subject.
|
|
2335
2446
|
*/
|
|
2336
|
-
async createCustomerPortalSession(request) {
|
|
2447
|
+
async createCustomerPortalSession(environment, request) {
|
|
2337
2448
|
try {
|
|
2338
2449
|
const data = await this.http.post(
|
|
2339
|
-
|
|
2450
|
+
`/api/payments/${encodeURIComponent(environment)}/customer-portal-sessions`,
|
|
2340
2451
|
request
|
|
2341
2452
|
);
|
|
2342
2453
|
return { data, error: null };
|
|
@@ -2362,7 +2473,7 @@ var InsForgeClient = class {
|
|
|
2362
2473
|
this.auth = new Auth(this.http, this.tokenManager, {
|
|
2363
2474
|
isServerMode: config.isServerMode ?? !!config.edgeFunctionToken
|
|
2364
2475
|
});
|
|
2365
|
-
this.database = new Database(this.http
|
|
2476
|
+
this.database = new Database(this.http);
|
|
2366
2477
|
this.storage = new Storage(this.http);
|
|
2367
2478
|
this.ai = new AI(this.http);
|
|
2368
2479
|
this.functions = new Functions(this.http, config.functionsUrl);
|
|
@@ -2386,6 +2497,37 @@ var InsForgeClient = class {
|
|
|
2386
2497
|
getHttpClient() {
|
|
2387
2498
|
return this.http;
|
|
2388
2499
|
}
|
|
2500
|
+
/**
|
|
2501
|
+
* Set the access token used by every SDK surface. Updates both the HTTP
|
|
2502
|
+
* client (database / storage / functions / AI / emails) and the realtime
|
|
2503
|
+
* token manager (which fires `onTokenChange` to reconnect the WebSocket
|
|
2504
|
+
* with the new bearer). Pass `null` to clear.
|
|
2505
|
+
*
|
|
2506
|
+
* Use this when an external auth provider (Better Auth, Clerk, Auth0,
|
|
2507
|
+
* WorkOS, Kinde, Stytch, …) issues the JWT and you need to keep the
|
|
2508
|
+
* long-lived InsForge client in sync. Without this, you'd have to call
|
|
2509
|
+
* `client.getHttpClient().setAuthToken(token)` AND reach into the private
|
|
2510
|
+
* `client.realtime.tokenManager.setAccessToken(token)` separately —
|
|
2511
|
+
* forgetting the second one silently breaks realtime auth.
|
|
2512
|
+
*
|
|
2513
|
+
* @example
|
|
2514
|
+
* ```typescript
|
|
2515
|
+
* // Refresh a third-party-issued JWT periodically
|
|
2516
|
+
* const { token } = await fetch('/api/insforge-token').then((r) => r.json());
|
|
2517
|
+
* client.setAccessToken(token);
|
|
2518
|
+
*
|
|
2519
|
+
* // Sign-out
|
|
2520
|
+
* client.setAccessToken(null);
|
|
2521
|
+
* ```
|
|
2522
|
+
*/
|
|
2523
|
+
setAccessToken(token) {
|
|
2524
|
+
this.http.setAuthToken(token);
|
|
2525
|
+
if (token === null) {
|
|
2526
|
+
this.tokenManager.clearSession();
|
|
2527
|
+
} else {
|
|
2528
|
+
this.tokenManager.setAccessToken(token);
|
|
2529
|
+
}
|
|
2530
|
+
}
|
|
2389
2531
|
/**
|
|
2390
2532
|
* Future modules will be added here:
|
|
2391
2533
|
* - database: Database operations
|