@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.mjs
CHANGED
|
@@ -284,6 +284,7 @@ var TokenManager = class {
|
|
|
284
284
|
// src/lib/http-client.ts
|
|
285
285
|
var RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([500, 502, 503, 504]);
|
|
286
286
|
var IDEMPOTENT_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD", "PUT", "DELETE", "OPTIONS"]);
|
|
287
|
+
var REFRESHABLE_AUTH_ERROR_CODE = "AUTH_UNAUTHORIZED";
|
|
287
288
|
function serializeBody(method, body, headers) {
|
|
288
289
|
if (body === void 0) return void 0;
|
|
289
290
|
if (method === "GET" || method === "HEAD") return void 0;
|
|
@@ -338,12 +339,11 @@ var HttpClient = class {
|
|
|
338
339
|
*/
|
|
339
340
|
constructor(config, tokenManager, logger) {
|
|
340
341
|
this.userToken = null;
|
|
341
|
-
this.autoRefreshToken = true;
|
|
342
342
|
this.isRefreshing = false;
|
|
343
343
|
this.refreshPromise = null;
|
|
344
344
|
this.refreshToken = null;
|
|
345
|
+
this.config = config;
|
|
345
346
|
this.baseUrl = config.baseUrl || "http://localhost:7130";
|
|
346
|
-
this.autoRefreshToken = config.autoRefreshToken ?? true;
|
|
347
347
|
this.fetch = config.fetch || (globalThis.fetch ? globalThis.fetch.bind(globalThis) : void 0);
|
|
348
348
|
this.anonKey = config.anonKey;
|
|
349
349
|
this.defaultHeaders = {
|
|
@@ -393,48 +393,19 @@ var HttpClient = class {
|
|
|
393
393
|
const jitter = base * (0.85 + Math.random() * 0.3);
|
|
394
394
|
return Math.round(jitter);
|
|
395
395
|
}
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
* @param method - HTTP method (GET, POST, PUT, PATCH, DELETE).
|
|
401
|
-
* @param path - API path relative to the base URL.
|
|
402
|
-
* @param options - Optional request configuration including headers, body, and query params.
|
|
403
|
-
* @returns Parsed response data.
|
|
404
|
-
* @throws {InsForgeError} On timeout, network failure, or HTTP error responses.
|
|
405
|
-
*/
|
|
406
|
-
async handleRequest(method, path, options = {}) {
|
|
396
|
+
shouldRefreshAccessToken(statusCode, errorCode, options = {}) {
|
|
397
|
+
return statusCode === 401 && errorCode === REFRESHABLE_AUTH_ERROR_CODE && !(this.config.isServerMode ?? !!this.config.edgeFunctionToken) && !options.skipAuthRefresh && this.userToken !== null;
|
|
398
|
+
}
|
|
399
|
+
async fetchWithRetry(args) {
|
|
407
400
|
const {
|
|
408
|
-
|
|
409
|
-
|
|
401
|
+
method,
|
|
402
|
+
url,
|
|
403
|
+
headers,
|
|
410
404
|
body,
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
const startTime = Date.now();
|
|
416
|
-
const canRetry = IDEMPOTENT_METHODS.has(method.toUpperCase()) || options.idempotent === true;
|
|
417
|
-
const maxAttempts = canRetry ? this.retryCount : 0;
|
|
418
|
-
const requestHeaders = {
|
|
419
|
-
...this.defaultHeaders
|
|
420
|
-
};
|
|
421
|
-
const authToken = this.userToken || this.anonKey;
|
|
422
|
-
if (authToken) {
|
|
423
|
-
requestHeaders["Authorization"] = `Bearer ${authToken}`;
|
|
424
|
-
}
|
|
425
|
-
const processedBody = serializeBody(method, body, requestHeaders);
|
|
426
|
-
if (headers instanceof Headers) {
|
|
427
|
-
headers.forEach((value, key) => {
|
|
428
|
-
requestHeaders[key] = value;
|
|
429
|
-
});
|
|
430
|
-
} else if (Array.isArray(headers)) {
|
|
431
|
-
headers.forEach(([key, value]) => {
|
|
432
|
-
requestHeaders[key] = value;
|
|
433
|
-
});
|
|
434
|
-
} else {
|
|
435
|
-
Object.assign(requestHeaders, headers);
|
|
436
|
-
}
|
|
437
|
-
this.logger.logRequest(method, url, requestHeaders, processedBody);
|
|
405
|
+
fetchOptions,
|
|
406
|
+
callerSignal,
|
|
407
|
+
maxAttempts
|
|
408
|
+
} = args;
|
|
438
409
|
let lastError;
|
|
439
410
|
for (let attempt = 0; attempt <= maxAttempts; attempt++) {
|
|
440
411
|
if (attempt > 0) {
|
|
@@ -486,8 +457,8 @@ var HttpClient = class {
|
|
|
486
457
|
try {
|
|
487
458
|
const response = await this.fetch(url, {
|
|
488
459
|
method,
|
|
489
|
-
headers
|
|
490
|
-
body
|
|
460
|
+
headers,
|
|
461
|
+
body,
|
|
491
462
|
...fetchOptions,
|
|
492
463
|
...controller ? { signal: controller.signal } : {}
|
|
493
464
|
});
|
|
@@ -501,31 +472,8 @@ var HttpClient = class {
|
|
|
501
472
|
);
|
|
502
473
|
continue;
|
|
503
474
|
}
|
|
504
|
-
let data;
|
|
505
|
-
try {
|
|
506
|
-
data = await parseResponse(response);
|
|
507
|
-
} catch (err) {
|
|
508
|
-
if (timer !== void 0) clearTimeout(timer);
|
|
509
|
-
if (err instanceof InsForgeError) {
|
|
510
|
-
this.logger.logResponse(
|
|
511
|
-
method,
|
|
512
|
-
url,
|
|
513
|
-
err.statusCode || response.status,
|
|
514
|
-
Date.now() - startTime,
|
|
515
|
-
err
|
|
516
|
-
);
|
|
517
|
-
}
|
|
518
|
-
throw err;
|
|
519
|
-
}
|
|
520
475
|
if (timer !== void 0) clearTimeout(timer);
|
|
521
|
-
|
|
522
|
-
method,
|
|
523
|
-
url,
|
|
524
|
-
response.status,
|
|
525
|
-
Date.now() - startTime,
|
|
526
|
-
data
|
|
527
|
-
);
|
|
528
|
-
return data;
|
|
476
|
+
return response;
|
|
529
477
|
} catch (err) {
|
|
530
478
|
if (timer !== void 0) clearTimeout(timer);
|
|
531
479
|
if (err?.name === "AbortError") {
|
|
@@ -538,9 +486,6 @@ var HttpClient = class {
|
|
|
538
486
|
}
|
|
539
487
|
throw err;
|
|
540
488
|
}
|
|
541
|
-
if (err instanceof InsForgeError) {
|
|
542
|
-
throw err;
|
|
543
|
-
}
|
|
544
489
|
if (attempt < maxAttempts) {
|
|
545
490
|
lastError = err;
|
|
546
491
|
continue;
|
|
@@ -558,33 +503,176 @@ var HttpClient = class {
|
|
|
558
503
|
"NETWORK_ERROR"
|
|
559
504
|
);
|
|
560
505
|
}
|
|
506
|
+
/**
|
|
507
|
+
* Performs an HTTP request with automatic retry and timeout handling.
|
|
508
|
+
* Retries on network errors and 5xx server errors with exponential backoff.
|
|
509
|
+
* Client errors (4xx) and timeouts are thrown immediately without retry.
|
|
510
|
+
* @param method - HTTP method (GET, POST, PUT, PATCH, DELETE).
|
|
511
|
+
* @param path - API path relative to the base URL.
|
|
512
|
+
* @param options - Optional request configuration including headers, body, and query params.
|
|
513
|
+
* @returns Parsed response data.
|
|
514
|
+
* @throws {InsForgeError} On timeout, network failure, or HTTP error responses.
|
|
515
|
+
*/
|
|
516
|
+
async handleRequest(method, path, options = {}) {
|
|
517
|
+
const {
|
|
518
|
+
params,
|
|
519
|
+
headers = {},
|
|
520
|
+
body,
|
|
521
|
+
skipAuthRefresh: _skipAuthRefresh,
|
|
522
|
+
signal: callerSignal,
|
|
523
|
+
...fetchOptions
|
|
524
|
+
} = options;
|
|
525
|
+
const url = this.buildUrl(path, params);
|
|
526
|
+
const startTime = Date.now();
|
|
527
|
+
const canRetry = IDEMPOTENT_METHODS.has(method.toUpperCase()) || options.idempotent === true;
|
|
528
|
+
const maxAttempts = canRetry ? this.retryCount : 0;
|
|
529
|
+
const requestHeaders = {
|
|
530
|
+
...this.defaultHeaders
|
|
531
|
+
};
|
|
532
|
+
const authToken = this.userToken || this.anonKey;
|
|
533
|
+
if (authToken) {
|
|
534
|
+
requestHeaders["Authorization"] = `Bearer ${authToken}`;
|
|
535
|
+
}
|
|
536
|
+
const processedBody = serializeBody(method, body, requestHeaders);
|
|
537
|
+
if (headers instanceof Headers) {
|
|
538
|
+
headers.forEach((value, key) => {
|
|
539
|
+
requestHeaders[key] = value;
|
|
540
|
+
});
|
|
541
|
+
} else if (Array.isArray(headers)) {
|
|
542
|
+
headers.forEach(([key, value]) => {
|
|
543
|
+
requestHeaders[key] = value;
|
|
544
|
+
});
|
|
545
|
+
} else {
|
|
546
|
+
Object.assign(requestHeaders, headers);
|
|
547
|
+
}
|
|
548
|
+
this.logger.logRequest(method, url, requestHeaders, processedBody);
|
|
549
|
+
const response = await this.fetchWithRetry({
|
|
550
|
+
method,
|
|
551
|
+
url,
|
|
552
|
+
headers: requestHeaders,
|
|
553
|
+
body: processedBody,
|
|
554
|
+
fetchOptions,
|
|
555
|
+
callerSignal,
|
|
556
|
+
maxAttempts
|
|
557
|
+
});
|
|
558
|
+
let data;
|
|
559
|
+
try {
|
|
560
|
+
data = await parseResponse(response);
|
|
561
|
+
} catch (err) {
|
|
562
|
+
if (err instanceof InsForgeError) {
|
|
563
|
+
this.logger.logResponse(
|
|
564
|
+
method,
|
|
565
|
+
url,
|
|
566
|
+
err.statusCode || response.status,
|
|
567
|
+
Date.now() - startTime,
|
|
568
|
+
err
|
|
569
|
+
);
|
|
570
|
+
}
|
|
571
|
+
throw err;
|
|
572
|
+
}
|
|
573
|
+
this.logger.logResponse(
|
|
574
|
+
method,
|
|
575
|
+
url,
|
|
576
|
+
response.status,
|
|
577
|
+
Date.now() - startTime,
|
|
578
|
+
data
|
|
579
|
+
);
|
|
580
|
+
return data;
|
|
581
|
+
}
|
|
561
582
|
async request(method, path, options = {}) {
|
|
562
583
|
try {
|
|
563
584
|
return await this.handleRequest(method, path, { ...options });
|
|
564
585
|
} catch (error) {
|
|
565
|
-
if (error instanceof InsForgeError && error.statusCode
|
|
586
|
+
if (error instanceof InsForgeError && this.shouldRefreshAccessToken(error.statusCode, error.error, options)) {
|
|
566
587
|
try {
|
|
567
|
-
|
|
568
|
-
this.setAuthToken(newTokenData.accessToken);
|
|
569
|
-
this.tokenManager.saveSession(newTokenData);
|
|
570
|
-
if (newTokenData.csrfToken) {
|
|
571
|
-
setCsrfToken(newTokenData.csrfToken);
|
|
572
|
-
}
|
|
573
|
-
if (newTokenData.refreshToken) {
|
|
574
|
-
this.setRefreshToken(newTokenData.refreshToken);
|
|
575
|
-
}
|
|
576
|
-
return await this.handleRequest(method, path, { ...options });
|
|
588
|
+
await this.refreshAndSaveSession();
|
|
577
589
|
} catch (error2) {
|
|
578
|
-
this.
|
|
579
|
-
this.userToken = null;
|
|
580
|
-
this.refreshToken = null;
|
|
581
|
-
clearCsrfToken();
|
|
590
|
+
this.clearAuthSession();
|
|
582
591
|
throw error2;
|
|
583
592
|
}
|
|
593
|
+
return await this.handleRequest(method, path, { ...options });
|
|
584
594
|
}
|
|
585
595
|
throw error;
|
|
586
596
|
}
|
|
587
597
|
}
|
|
598
|
+
/**
|
|
599
|
+
* Performs an SDK-configured fetch and returns the raw Response.
|
|
600
|
+
* This is used by clients such as postgrest-js that need to own response
|
|
601
|
+
* parsing while still sharing SDK auth and refresh behavior.
|
|
602
|
+
*/
|
|
603
|
+
async rawFetch(input, init, options = {}) {
|
|
604
|
+
const request = typeof Request !== "undefined" && input instanceof Request ? input : void 0;
|
|
605
|
+
const {
|
|
606
|
+
method: initMethod,
|
|
607
|
+
headers: initHeaders,
|
|
608
|
+
body: initBody,
|
|
609
|
+
signal: initSignal,
|
|
610
|
+
...fetchOptions
|
|
611
|
+
} = init ?? {};
|
|
612
|
+
const method = initMethod ?? request?.method ?? "GET";
|
|
613
|
+
const url = request?.url ?? input.toString();
|
|
614
|
+
const startTime = Date.now();
|
|
615
|
+
const headers = new Headers(this.getHeaders());
|
|
616
|
+
request?.headers.forEach((value, key) => {
|
|
617
|
+
headers.set(key, value);
|
|
618
|
+
});
|
|
619
|
+
new Headers(initHeaders).forEach((value, key) => {
|
|
620
|
+
headers.set(key, value);
|
|
621
|
+
});
|
|
622
|
+
const requestHeaders = {};
|
|
623
|
+
headers.forEach((value, key) => {
|
|
624
|
+
requestHeaders[key] = value;
|
|
625
|
+
});
|
|
626
|
+
const body = initBody ?? request?.body ?? void 0;
|
|
627
|
+
const callerSignal = initSignal ?? request?.signal;
|
|
628
|
+
const maxAttempts = IDEMPOTENT_METHODS.has(method.toUpperCase()) ? this.retryCount : 0;
|
|
629
|
+
this.logger.logRequest(method, url, requestHeaders, body);
|
|
630
|
+
const response = await this.fetchWithRetry({
|
|
631
|
+
method,
|
|
632
|
+
url,
|
|
633
|
+
headers: requestHeaders,
|
|
634
|
+
body,
|
|
635
|
+
fetchOptions,
|
|
636
|
+
callerSignal,
|
|
637
|
+
maxAttempts
|
|
638
|
+
});
|
|
639
|
+
this.logger.logResponse(
|
|
640
|
+
method,
|
|
641
|
+
url,
|
|
642
|
+
response.status,
|
|
643
|
+
Date.now() - startTime
|
|
644
|
+
);
|
|
645
|
+
let errorCode = null;
|
|
646
|
+
if (response.status === 401) {
|
|
647
|
+
try {
|
|
648
|
+
const data = await response.clone().json();
|
|
649
|
+
if (data && typeof data === "object") {
|
|
650
|
+
const candidate = data.error ?? data.code;
|
|
651
|
+
if (typeof candidate === "string") {
|
|
652
|
+
errorCode = candidate;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
} catch {
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
if (!this.shouldRefreshAccessToken(response.status, errorCode, options)) {
|
|
659
|
+
return response;
|
|
660
|
+
}
|
|
661
|
+
let newTokenData;
|
|
662
|
+
try {
|
|
663
|
+
newTokenData = await this.refreshAndSaveSession();
|
|
664
|
+
} catch (error) {
|
|
665
|
+
this.clearAuthSession();
|
|
666
|
+
throw error;
|
|
667
|
+
}
|
|
668
|
+
const retryHeaders = new Headers(initHeaders);
|
|
669
|
+
retryHeaders.set("Authorization", `Bearer ${newTokenData.accessToken}`);
|
|
670
|
+
return await this.rawFetch(
|
|
671
|
+
input,
|
|
672
|
+
{ ...init, headers: retryHeaders },
|
|
673
|
+
{ skipAuthRefresh: true }
|
|
674
|
+
);
|
|
675
|
+
}
|
|
588
676
|
/** Performs a GET request. */
|
|
589
677
|
get(path, options) {
|
|
590
678
|
return this.request("GET", path, options);
|
|
@@ -621,7 +709,7 @@ var HttpClient = class {
|
|
|
621
709
|
}
|
|
622
710
|
return headers;
|
|
623
711
|
}
|
|
624
|
-
async
|
|
712
|
+
async refreshAccessToken() {
|
|
625
713
|
if (this.isRefreshing) {
|
|
626
714
|
return this.refreshPromise;
|
|
627
715
|
}
|
|
@@ -632,7 +720,7 @@ var HttpClient = class {
|
|
|
632
720
|
const body = this.refreshToken ? { refreshToken: this.refreshToken } : void 0;
|
|
633
721
|
const response = await this.handleRequest(
|
|
634
722
|
"POST",
|
|
635
|
-
"/api/auth/
|
|
723
|
+
this.refreshToken ? "/api/auth/refresh?client_type=mobile" : "/api/auth/refresh",
|
|
636
724
|
{
|
|
637
725
|
body,
|
|
638
726
|
headers: csrfToken ? { "X-CSRF-Token": csrfToken } : {},
|
|
@@ -647,6 +735,24 @@ var HttpClient = class {
|
|
|
647
735
|
})();
|
|
648
736
|
return this.refreshPromise;
|
|
649
737
|
}
|
|
738
|
+
async refreshAndSaveSession() {
|
|
739
|
+
const newTokenData = await this.refreshAccessToken();
|
|
740
|
+
this.setAuthToken(newTokenData.accessToken);
|
|
741
|
+
this.tokenManager.saveSession(newTokenData);
|
|
742
|
+
if (newTokenData.csrfToken) {
|
|
743
|
+
setCsrfToken(newTokenData.csrfToken);
|
|
744
|
+
}
|
|
745
|
+
if (newTokenData.refreshToken) {
|
|
746
|
+
this.setRefreshToken(newTokenData.refreshToken);
|
|
747
|
+
}
|
|
748
|
+
return newTokenData;
|
|
749
|
+
}
|
|
750
|
+
clearAuthSession() {
|
|
751
|
+
this.tokenManager.clearSession();
|
|
752
|
+
this.userToken = null;
|
|
753
|
+
this.refreshToken = null;
|
|
754
|
+
clearCsrfToken();
|
|
755
|
+
}
|
|
650
756
|
};
|
|
651
757
|
|
|
652
758
|
// src/modules/auth/helpers.ts
|
|
@@ -775,7 +881,7 @@ var Auth = class {
|
|
|
775
881
|
const response = await this.http.post(
|
|
776
882
|
this.isServerMode() ? "/api/auth/users?client_type=mobile" : "/api/auth/users",
|
|
777
883
|
request,
|
|
778
|
-
{ credentials: "include" }
|
|
884
|
+
{ credentials: "include", skipAuthRefresh: true }
|
|
779
885
|
);
|
|
780
886
|
if (response.accessToken && response.user) {
|
|
781
887
|
this.saveSessionFromResponse(response);
|
|
@@ -793,7 +899,7 @@ var Auth = class {
|
|
|
793
899
|
const response = await this.http.post(
|
|
794
900
|
this.isServerMode() ? "/api/auth/sessions?client_type=mobile" : "/api/auth/sessions",
|
|
795
901
|
request,
|
|
796
|
-
{ credentials: "include" }
|
|
902
|
+
{ credentials: "include", skipAuthRefresh: true }
|
|
797
903
|
);
|
|
798
904
|
this.saveSessionFromResponse(response);
|
|
799
905
|
if (response.refreshToken) {
|
|
@@ -810,7 +916,7 @@ var Auth = class {
|
|
|
810
916
|
await this.http.post(
|
|
811
917
|
this.isServerMode() ? "/api/auth/logout?client_type=mobile" : "/api/auth/logout",
|
|
812
918
|
void 0,
|
|
813
|
-
{ credentials: "include" }
|
|
919
|
+
{ credentials: "include", skipAuthRefresh: true }
|
|
814
920
|
);
|
|
815
921
|
} catch {
|
|
816
922
|
}
|
|
@@ -847,7 +953,8 @@ var Auth = class {
|
|
|
847
953
|
);
|
|
848
954
|
const oauthPath = isBuiltInProvider ? `/api/auth/oauth/${providerKey}` : `/api/auth/oauth/custom/${providerKey}`;
|
|
849
955
|
const response = await this.http.get(oauthPath, {
|
|
850
|
-
params
|
|
956
|
+
params,
|
|
957
|
+
skipAuthRefresh: true
|
|
851
958
|
});
|
|
852
959
|
if (!this.isServerMode() && typeof window !== "undefined" && !skipBrowserRedirect) {
|
|
853
960
|
window.location.href = response.authUrl;
|
|
@@ -895,7 +1002,7 @@ var Auth = class {
|
|
|
895
1002
|
const response = await this.http.post(
|
|
896
1003
|
this.isServerMode() ? "/api/auth/oauth/exchange?client_type=mobile" : "/api/auth/oauth/exchange",
|
|
897
1004
|
request,
|
|
898
|
-
{ credentials: "include" }
|
|
1005
|
+
{ credentials: "include", skipAuthRefresh: true }
|
|
899
1006
|
);
|
|
900
1007
|
this.saveSessionFromResponse(response);
|
|
901
1008
|
return {
|
|
@@ -922,7 +1029,7 @@ var Auth = class {
|
|
|
922
1029
|
const response = await this.http.post(
|
|
923
1030
|
"/api/auth/id-token?client_type=mobile",
|
|
924
1031
|
{ provider, token },
|
|
925
|
-
{ credentials: "include" }
|
|
1032
|
+
{ credentials: "include", skipAuthRefresh: true }
|
|
926
1033
|
);
|
|
927
1034
|
this.saveSessionFromResponse(response);
|
|
928
1035
|
if (response.refreshToken) {
|
|
@@ -969,7 +1076,8 @@ var Auth = class {
|
|
|
969
1076
|
this.isServerMode() ? { refresh_token: options?.refreshToken } : void 0,
|
|
970
1077
|
{
|
|
971
1078
|
headers: csrfToken ? { "X-CSRF-Token": csrfToken } : {},
|
|
972
|
-
credentials: "include"
|
|
1079
|
+
credentials: "include",
|
|
1080
|
+
skipAuthRefresh: true
|
|
973
1081
|
}
|
|
974
1082
|
);
|
|
975
1083
|
if (response.accessToken) {
|
|
@@ -1072,7 +1180,9 @@ var Auth = class {
|
|
|
1072
1180
|
// ============================================================================
|
|
1073
1181
|
async resendVerificationEmail(request) {
|
|
1074
1182
|
try {
|
|
1075
|
-
const response = await this.http.post("/api/auth/email/send-verification", request
|
|
1183
|
+
const response = await this.http.post("/api/auth/email/send-verification", request, {
|
|
1184
|
+
skipAuthRefresh: true
|
|
1185
|
+
});
|
|
1076
1186
|
return { data: response, error: null };
|
|
1077
1187
|
} catch (error) {
|
|
1078
1188
|
return wrapError(
|
|
@@ -1086,7 +1196,7 @@ var Auth = class {
|
|
|
1086
1196
|
const response = await this.http.post(
|
|
1087
1197
|
this.isServerMode() ? "/api/auth/email/verify?client_type=mobile" : "/api/auth/email/verify",
|
|
1088
1198
|
request,
|
|
1089
|
-
{ credentials: "include" }
|
|
1199
|
+
{ credentials: "include", skipAuthRefresh: true }
|
|
1090
1200
|
);
|
|
1091
1201
|
this.saveSessionFromResponse(response);
|
|
1092
1202
|
if (response.refreshToken) {
|
|
@@ -1105,7 +1215,9 @@ var Auth = class {
|
|
|
1105
1215
|
// ============================================================================
|
|
1106
1216
|
async sendResetPasswordEmail(request) {
|
|
1107
1217
|
try {
|
|
1108
|
-
const response = await this.http.post("/api/auth/email/send-reset-password", request
|
|
1218
|
+
const response = await this.http.post("/api/auth/email/send-reset-password", request, {
|
|
1219
|
+
skipAuthRefresh: true
|
|
1220
|
+
});
|
|
1109
1221
|
return { data: response, error: null };
|
|
1110
1222
|
} catch (error) {
|
|
1111
1223
|
return wrapError(
|
|
@@ -1118,7 +1230,8 @@ var Auth = class {
|
|
|
1118
1230
|
try {
|
|
1119
1231
|
const response = await this.http.post(
|
|
1120
1232
|
"/api/auth/email/exchange-reset-password-token",
|
|
1121
|
-
request
|
|
1233
|
+
request,
|
|
1234
|
+
{ skipAuthRefresh: true }
|
|
1122
1235
|
);
|
|
1123
1236
|
return { data: response, error: null };
|
|
1124
1237
|
} catch (error) {
|
|
@@ -1132,7 +1245,8 @@ var Auth = class {
|
|
|
1132
1245
|
try {
|
|
1133
1246
|
const response = await this.http.post(
|
|
1134
1247
|
"/api/auth/email/reset-password",
|
|
1135
|
-
request
|
|
1248
|
+
request,
|
|
1249
|
+
{ skipAuthRefresh: true }
|
|
1136
1250
|
);
|
|
1137
1251
|
return { data: response, error: null };
|
|
1138
1252
|
} catch (error) {
|
|
@@ -1148,7 +1262,8 @@ var Auth = class {
|
|
|
1148
1262
|
async getPublicAuthConfig() {
|
|
1149
1263
|
try {
|
|
1150
1264
|
const response = await this.http.get(
|
|
1151
|
-
"/api/auth/public-config"
|
|
1265
|
+
"/api/auth/public-config",
|
|
1266
|
+
{ skipAuthRefresh: true }
|
|
1152
1267
|
);
|
|
1153
1268
|
return { data: response, error: null };
|
|
1154
1269
|
} catch (error) {
|
|
@@ -1162,7 +1277,7 @@ var Auth = class {
|
|
|
1162
1277
|
|
|
1163
1278
|
// src/modules/database-postgrest.ts
|
|
1164
1279
|
import { PostgrestClient } from "@supabase/postgrest-js";
|
|
1165
|
-
function createInsForgePostgrestFetch(httpClient
|
|
1280
|
+
function createInsForgePostgrestFetch(httpClient) {
|
|
1166
1281
|
return async (input, init) => {
|
|
1167
1282
|
const url = typeof input === "string" ? input : input.toString();
|
|
1168
1283
|
const urlObj = new URL(url);
|
|
@@ -1170,14 +1285,11 @@ function createInsForgePostgrestFetch(httpClient, tokenManager) {
|
|
|
1170
1285
|
const rpcMatch = pathname.match(/^rpc\/(.+)$/);
|
|
1171
1286
|
const endpoint = rpcMatch ? `/api/database/rpc/${rpcMatch[1]}` : `/api/database/records/${pathname}`;
|
|
1172
1287
|
const insforgeUrl = `${httpClient.baseUrl}${endpoint}${urlObj.search}`;
|
|
1173
|
-
const
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
headers.set("Authorization", `Bearer ${authToken}`);
|
|
1179
|
-
}
|
|
1180
|
-
const response = await fetch(insforgeUrl, {
|
|
1288
|
+
const headers = new Headers(httpClient.getHeaders());
|
|
1289
|
+
new Headers(init?.headers).forEach((value, key) => {
|
|
1290
|
+
headers.set(key, value);
|
|
1291
|
+
});
|
|
1292
|
+
const response = await httpClient.rawFetch(insforgeUrl, {
|
|
1181
1293
|
...init,
|
|
1182
1294
|
headers
|
|
1183
1295
|
});
|
|
@@ -1185,42 +1297,42 @@ function createInsForgePostgrestFetch(httpClient, tokenManager) {
|
|
|
1185
1297
|
};
|
|
1186
1298
|
}
|
|
1187
1299
|
var Database = class {
|
|
1188
|
-
constructor(httpClient
|
|
1300
|
+
constructor(httpClient) {
|
|
1189
1301
|
this.postgrest = new PostgrestClient("http://dummy", {
|
|
1190
|
-
fetch: createInsForgePostgrestFetch(httpClient
|
|
1302
|
+
fetch: createInsForgePostgrestFetch(httpClient),
|
|
1191
1303
|
headers: {}
|
|
1192
1304
|
});
|
|
1193
1305
|
}
|
|
1194
1306
|
/**
|
|
1195
1307
|
* Create a query builder for a table
|
|
1196
|
-
*
|
|
1308
|
+
*
|
|
1197
1309
|
* @example
|
|
1198
1310
|
* // Basic query
|
|
1199
1311
|
* const { data, error } = await client.database
|
|
1200
1312
|
* .from('posts')
|
|
1201
1313
|
* .select('*')
|
|
1202
1314
|
* .eq('user_id', userId);
|
|
1203
|
-
*
|
|
1315
|
+
*
|
|
1204
1316
|
* // With count (Supabase style!)
|
|
1205
1317
|
* const { data, error, count } = await client.database
|
|
1206
1318
|
* .from('posts')
|
|
1207
1319
|
* .select('*', { count: 'exact' })
|
|
1208
1320
|
* .range(0, 9);
|
|
1209
|
-
*
|
|
1321
|
+
*
|
|
1210
1322
|
* // Just get count, no data
|
|
1211
1323
|
* const { count } = await client.database
|
|
1212
1324
|
* .from('posts')
|
|
1213
1325
|
* .select('*', { count: 'exact', head: true });
|
|
1214
|
-
*
|
|
1326
|
+
*
|
|
1215
1327
|
* // Complex queries with OR
|
|
1216
1328
|
* const { data } = await client.database
|
|
1217
1329
|
* .from('posts')
|
|
1218
1330
|
* .select('*, users!inner(*)')
|
|
1219
1331
|
* .or('status.eq.active,status.eq.pending');
|
|
1220
|
-
*
|
|
1332
|
+
*
|
|
1221
1333
|
* // All features work:
|
|
1222
1334
|
* - Nested selects
|
|
1223
|
-
* - Foreign key expansion
|
|
1335
|
+
* - Foreign key expansion
|
|
1224
1336
|
* - OR/AND/NOT conditions
|
|
1225
1337
|
* - Count with head
|
|
1226
1338
|
* - Range pagination
|
|
@@ -2261,8 +2373,7 @@ var Payments = class {
|
|
|
2261
2373
|
*
|
|
2262
2374
|
* @example
|
|
2263
2375
|
* ```typescript
|
|
2264
|
-
* const { data, error } = await client.payments.createCheckoutSession({
|
|
2265
|
-
* environment: 'test',
|
|
2376
|
+
* const { data, error } = await client.payments.createCheckoutSession('test', {
|
|
2266
2377
|
* mode: 'payment',
|
|
2267
2378
|
* lineItems: [{ stripePriceId: 'price_123', quantity: 1 }],
|
|
2268
2379
|
* successUrl: `${window.location.origin}/success`,
|
|
@@ -2274,10 +2385,10 @@ var Payments = class {
|
|
|
2274
2385
|
* }
|
|
2275
2386
|
* ```
|
|
2276
2387
|
*/
|
|
2277
|
-
async createCheckoutSession(request) {
|
|
2388
|
+
async createCheckoutSession(environment, request) {
|
|
2278
2389
|
try {
|
|
2279
2390
|
const data = await this.http.post(
|
|
2280
|
-
|
|
2391
|
+
`/api/payments/${encodeURIComponent(environment)}/checkout-sessions`,
|
|
2281
2392
|
request,
|
|
2282
2393
|
{ idempotent: !!request.idempotencyKey }
|
|
2283
2394
|
);
|
|
@@ -2292,10 +2403,10 @@ var Payments = class {
|
|
|
2292
2403
|
/**
|
|
2293
2404
|
* Create a Stripe Billing Portal Session for a mapped billing subject.
|
|
2294
2405
|
*/
|
|
2295
|
-
async createCustomerPortalSession(request) {
|
|
2406
|
+
async createCustomerPortalSession(environment, request) {
|
|
2296
2407
|
try {
|
|
2297
2408
|
const data = await this.http.post(
|
|
2298
|
-
|
|
2409
|
+
`/api/payments/${encodeURIComponent(environment)}/customer-portal-sessions`,
|
|
2299
2410
|
request
|
|
2300
2411
|
);
|
|
2301
2412
|
return { data, error: null };
|
|
@@ -2321,7 +2432,7 @@ var InsForgeClient = class {
|
|
|
2321
2432
|
this.auth = new Auth(this.http, this.tokenManager, {
|
|
2322
2433
|
isServerMode: config.isServerMode ?? !!config.edgeFunctionToken
|
|
2323
2434
|
});
|
|
2324
|
-
this.database = new Database(this.http
|
|
2435
|
+
this.database = new Database(this.http);
|
|
2325
2436
|
this.storage = new Storage(this.http);
|
|
2326
2437
|
this.ai = new AI(this.http);
|
|
2327
2438
|
this.functions = new Functions(this.http, config.functionsUrl);
|
|
@@ -2345,6 +2456,37 @@ var InsForgeClient = class {
|
|
|
2345
2456
|
getHttpClient() {
|
|
2346
2457
|
return this.http;
|
|
2347
2458
|
}
|
|
2459
|
+
/**
|
|
2460
|
+
* Set the access token used by every SDK surface. Updates both the HTTP
|
|
2461
|
+
* client (database / storage / functions / AI / emails) and the realtime
|
|
2462
|
+
* token manager (which fires `onTokenChange` to reconnect the WebSocket
|
|
2463
|
+
* with the new bearer). Pass `null` to clear.
|
|
2464
|
+
*
|
|
2465
|
+
* Use this when an external auth provider (Better Auth, Clerk, Auth0,
|
|
2466
|
+
* WorkOS, Kinde, Stytch, …) issues the JWT and you need to keep the
|
|
2467
|
+
* long-lived InsForge client in sync. Without this, you'd have to call
|
|
2468
|
+
* `client.getHttpClient().setAuthToken(token)` AND reach into the private
|
|
2469
|
+
* `client.realtime.tokenManager.setAccessToken(token)` separately —
|
|
2470
|
+
* forgetting the second one silently breaks realtime auth.
|
|
2471
|
+
*
|
|
2472
|
+
* @example
|
|
2473
|
+
* ```typescript
|
|
2474
|
+
* // Refresh a third-party-issued JWT periodically
|
|
2475
|
+
* const { token } = await fetch('/api/insforge-token').then((r) => r.json());
|
|
2476
|
+
* client.setAccessToken(token);
|
|
2477
|
+
*
|
|
2478
|
+
* // Sign-out
|
|
2479
|
+
* client.setAccessToken(null);
|
|
2480
|
+
* ```
|
|
2481
|
+
*/
|
|
2482
|
+
setAccessToken(token) {
|
|
2483
|
+
this.http.setAuthToken(token);
|
|
2484
|
+
if (token === null) {
|
|
2485
|
+
this.tokenManager.clearSession();
|
|
2486
|
+
} else {
|
|
2487
|
+
this.tokenManager.setAccessToken(token);
|
|
2488
|
+
}
|
|
2489
|
+
}
|
|
2348
2490
|
/**
|
|
2349
2491
|
* Future modules will be added here:
|
|
2350
2492
|
* - database: Database operations
|