zone4code-sdk 1.0.5 → 1.0.6
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.cjs +214 -93
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +51 -2
- package/dist/index.d.ts +51 -2
- package/dist/index.js +214 -93
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -17,6 +17,22 @@ function decodeJwtPayload(token) {
|
|
|
17
17
|
return null;
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
|
+
function normalizeHeaders(headers) {
|
|
21
|
+
const result = {};
|
|
22
|
+
if (!headers) return result;
|
|
23
|
+
if (typeof Headers !== "undefined" && headers instanceof Headers) {
|
|
24
|
+
headers.forEach((value, key) => {
|
|
25
|
+
result[key] = value;
|
|
26
|
+
});
|
|
27
|
+
} else if (Array.isArray(headers)) {
|
|
28
|
+
for (const [key, value] of headers) {
|
|
29
|
+
result[key] = value;
|
|
30
|
+
}
|
|
31
|
+
} else {
|
|
32
|
+
Object.assign(result, headers);
|
|
33
|
+
}
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
20
36
|
var AuthClient = class _AuthClient {
|
|
21
37
|
gatewayUrl;
|
|
22
38
|
tenantId;
|
|
@@ -26,26 +42,39 @@ var AuthClient = class _AuthClient {
|
|
|
26
42
|
token = null;
|
|
27
43
|
refreshTokenValue = null;
|
|
28
44
|
fetchFn;
|
|
45
|
+
autoRefresh;
|
|
46
|
+
tokenExpiryBuffer;
|
|
47
|
+
onSessionExpiredCallback;
|
|
48
|
+
sessionExpiredListeners = [];
|
|
49
|
+
activeRefreshPromise = null;
|
|
50
|
+
storageLoadedPromise;
|
|
29
51
|
static TOKEN_KEY_PREFIX = "z4c_token_";
|
|
30
52
|
constructor(options) {
|
|
53
|
+
const storage = options.storage;
|
|
54
|
+
const storageKey = options.storageKey || _AuthClient.TOKEN_KEY_PREFIX + options.tenantId;
|
|
55
|
+
const refreshStorageKey = storageKey + "_refresh";
|
|
31
56
|
this.gatewayUrl = options.gatewayUrl.replace(/\/+$/, "");
|
|
32
57
|
this.tenantId = options.tenantId;
|
|
33
|
-
this.storage =
|
|
34
|
-
this.storageKey =
|
|
35
|
-
this.refreshStorageKey =
|
|
58
|
+
this.storage = storage;
|
|
59
|
+
this.storageKey = storageKey;
|
|
60
|
+
this.refreshStorageKey = refreshStorageKey;
|
|
36
61
|
this.fetchFn = options.fetchFn || (typeof fetch !== "undefined" ? fetch : globalThis.fetch);
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
62
|
+
this.autoRefresh = options.autoRefresh ?? true;
|
|
63
|
+
this.tokenExpiryBuffer = options.tokenExpiryBuffer ?? 30;
|
|
64
|
+
this.onSessionExpiredCallback = options.onSessionExpired;
|
|
65
|
+
this.storageLoadedPromise = (async () => {
|
|
66
|
+
if (options.initialToken) {
|
|
67
|
+
this.setToken(options.initialToken);
|
|
68
|
+
} else {
|
|
69
|
+
try {
|
|
70
|
+
const storedToken = await storage.getItem(storageKey);
|
|
71
|
+
if (storedToken) this.token = storedToken;
|
|
72
|
+
const storedRefresh = await storage.getItem(refreshStorageKey);
|
|
73
|
+
if (storedRefresh) this.refreshTokenValue = storedRefresh;
|
|
74
|
+
} catch {
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
})();
|
|
49
78
|
}
|
|
50
79
|
getToken() {
|
|
51
80
|
return this.token;
|
|
@@ -72,6 +101,91 @@ var AuthClient = class _AuthClient {
|
|
|
72
101
|
isAuthenticated() {
|
|
73
102
|
return !!this.token;
|
|
74
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* Check whether the current access token is expired or expiring within bufferSeconds
|
|
106
|
+
*/
|
|
107
|
+
isTokenExpired(bufferSeconds = this.tokenExpiryBuffer) {
|
|
108
|
+
if (!this.token) return true;
|
|
109
|
+
const payload = decodeJwtPayload(this.token);
|
|
110
|
+
if (!payload || typeof payload.exp !== "number") return false;
|
|
111
|
+
const nowSeconds = Math.floor(Date.now() / 1e3);
|
|
112
|
+
return nowSeconds >= payload.exp - bufferSeconds;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Returns a valid access token, proactively refreshing it if expired and autoRefresh is enabled
|
|
116
|
+
*/
|
|
117
|
+
async getValidToken() {
|
|
118
|
+
await this.storageLoadedPromise;
|
|
119
|
+
if (!this.token) return null;
|
|
120
|
+
if (!this.autoRefresh) return this.token;
|
|
121
|
+
if (this.isTokenExpired()) {
|
|
122
|
+
const refreshToken = this.getRefreshToken();
|
|
123
|
+
if (refreshToken) {
|
|
124
|
+
try {
|
|
125
|
+
const res = await this.refreshToken(refreshToken);
|
|
126
|
+
return res.token || this.token;
|
|
127
|
+
} catch {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
} else {
|
|
131
|
+
this.notifySessionExpired();
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return this.token;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Register a callback triggered when session has expired completely
|
|
139
|
+
*/
|
|
140
|
+
onSessionExpired(listener) {
|
|
141
|
+
this.sessionExpiredListeners.push(listener);
|
|
142
|
+
return () => {
|
|
143
|
+
this.sessionExpiredListeners = this.sessionExpiredListeners.filter((l) => l !== listener);
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
notifySessionExpired() {
|
|
147
|
+
this.logout();
|
|
148
|
+
for (const listener of this.sessionExpiredListeners) {
|
|
149
|
+
try {
|
|
150
|
+
listener();
|
|
151
|
+
} catch (e) {
|
|
152
|
+
console.error("Error in onSessionExpired listener:", e);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (this.onSessionExpiredCallback) {
|
|
156
|
+
try {
|
|
157
|
+
this.onSessionExpiredCallback();
|
|
158
|
+
} catch (e) {
|
|
159
|
+
console.error("Error in onSessionExpired callback:", e);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Perform an HTTP request with automatic Authorization injection, proactive refresh, and reactive 401 retry
|
|
165
|
+
*/
|
|
166
|
+
async fetchWithAuth(url, init = {}) {
|
|
167
|
+
await this.storageLoadedPromise;
|
|
168
|
+
const token = await this.getValidToken();
|
|
169
|
+
const headers = normalizeHeaders(init.headers);
|
|
170
|
+
if (!headers["x-tenant-id"] && !headers["X-Tenant-Id"]) {
|
|
171
|
+
headers["x-tenant-id"] = this.tenantId;
|
|
172
|
+
}
|
|
173
|
+
if (token && !headers["Authorization"] && !headers["authorization"]) {
|
|
174
|
+
headers["Authorization"] = `Bearer ${token}`;
|
|
175
|
+
}
|
|
176
|
+
let res = await this.fetchFn(url, { ...init, headers });
|
|
177
|
+
if (res.status === 401 && this.autoRefresh && this.getRefreshToken()) {
|
|
178
|
+
try {
|
|
179
|
+
const refreshed = await this.refreshToken();
|
|
180
|
+
if (refreshed?.token) {
|
|
181
|
+
headers["Authorization"] = `Bearer ${refreshed.token}`;
|
|
182
|
+
res = await this.fetchFn(url, { ...init, headers });
|
|
183
|
+
}
|
|
184
|
+
} catch {
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return res;
|
|
188
|
+
}
|
|
75
189
|
/**
|
|
76
190
|
* Synchronously parse and return current authenticated user from JWT token
|
|
77
191
|
*/
|
|
@@ -227,36 +341,48 @@ var AuthClient = class _AuthClient {
|
|
|
227
341
|
async refreshToken(refreshToken) {
|
|
228
342
|
const tokenToUse = refreshToken || this.getRefreshToken();
|
|
229
343
|
if (!tokenToUse) {
|
|
344
|
+
this.notifySessionExpired();
|
|
230
345
|
throw new Error("No refresh token available");
|
|
231
346
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
method: "POST",
|
|
235
|
-
headers: {
|
|
236
|
-
"Content-Type": "application/json",
|
|
237
|
-
"x-tenant-id": this.tenantId
|
|
238
|
-
},
|
|
239
|
-
body: JSON.stringify({ refresh_token: tokenToUse })
|
|
240
|
-
});
|
|
241
|
-
if (!res.ok) {
|
|
242
|
-
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
243
|
-
throw new Error(err.message || err.error || `Token refresh failed with HTTP ${res.status}`);
|
|
244
|
-
}
|
|
245
|
-
const json = await res.json();
|
|
246
|
-
const token = json.token || json.access_token;
|
|
247
|
-
if (token) {
|
|
248
|
-
this.setToken(token);
|
|
249
|
-
}
|
|
250
|
-
if (json.refresh_token) {
|
|
251
|
-
this.setRefreshToken(json.refresh_token);
|
|
347
|
+
if (this.activeRefreshPromise) {
|
|
348
|
+
return this.activeRefreshPromise;
|
|
252
349
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
350
|
+
this.activeRefreshPromise = (async () => {
|
|
351
|
+
try {
|
|
352
|
+
const url = `${this.gatewayUrl}/auth/${this.tenantId}/refresh`;
|
|
353
|
+
const res = await this.fetchFn(url, {
|
|
354
|
+
method: "POST",
|
|
355
|
+
headers: {
|
|
356
|
+
"Content-Type": "application/json",
|
|
357
|
+
"x-tenant-id": this.tenantId
|
|
358
|
+
},
|
|
359
|
+
body: JSON.stringify({ refresh_token: tokenToUse })
|
|
360
|
+
});
|
|
361
|
+
if (!res.ok) {
|
|
362
|
+
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
363
|
+
this.notifySessionExpired();
|
|
364
|
+
throw new Error(err.message || err.error || `Token refresh failed with HTTP ${res.status}`);
|
|
365
|
+
}
|
|
366
|
+
const json = await res.json();
|
|
367
|
+
const token = json.token || json.access_token;
|
|
368
|
+
if (token) {
|
|
369
|
+
this.setToken(token);
|
|
370
|
+
}
|
|
371
|
+
if (json.refresh_token) {
|
|
372
|
+
this.setRefreshToken(json.refresh_token);
|
|
373
|
+
}
|
|
374
|
+
const user = this.getUser() || json.user;
|
|
375
|
+
return {
|
|
376
|
+
token: token || "",
|
|
377
|
+
refreshToken: json.refresh_token,
|
|
378
|
+
user,
|
|
379
|
+
...json
|
|
380
|
+
};
|
|
381
|
+
} finally {
|
|
382
|
+
this.activeRefreshPromise = null;
|
|
383
|
+
}
|
|
384
|
+
})();
|
|
385
|
+
return this.activeRefreshPromise;
|
|
260
386
|
}
|
|
261
387
|
/**
|
|
262
388
|
* Request password reset email
|
|
@@ -385,13 +511,15 @@ var EntityQueryBuilder = class {
|
|
|
385
511
|
typeName;
|
|
386
512
|
getToken;
|
|
387
513
|
fetchFn;
|
|
514
|
+
fetchWithAuth;
|
|
388
515
|
queryParams = new URLSearchParams();
|
|
389
516
|
constructor(options) {
|
|
390
517
|
this.gatewayUrl = options.gatewayUrl.replace(/\/+$/, "");
|
|
391
518
|
this.tenantId = options.tenantId;
|
|
392
519
|
this.typeName = options.typeName;
|
|
393
|
-
this.getToken = options.getToken;
|
|
520
|
+
this.getToken = options.getToken || (() => null);
|
|
394
521
|
this.fetchFn = options.fetchFn || (typeof fetch !== "undefined" ? fetch : globalThis.fetch);
|
|
522
|
+
this.fetchWithAuth = options.fetchWithAuth;
|
|
395
523
|
}
|
|
396
524
|
getHeaders() {
|
|
397
525
|
const headers = {
|
|
@@ -404,6 +532,13 @@ var EntityQueryBuilder = class {
|
|
|
404
532
|
}
|
|
405
533
|
return headers;
|
|
406
534
|
}
|
|
535
|
+
async executeRequest(url, init = {}) {
|
|
536
|
+
if (this.fetchWithAuth) {
|
|
537
|
+
return this.fetchWithAuth(url, init);
|
|
538
|
+
}
|
|
539
|
+
const headers = { ...this.getHeaders(), ...init.headers || {} };
|
|
540
|
+
return this.fetchFn(url, { ...init, headers });
|
|
541
|
+
}
|
|
407
542
|
// ----------------------------------------------------
|
|
408
543
|
// Filter Operators
|
|
409
544
|
// ----------------------------------------------------
|
|
@@ -512,9 +647,7 @@ var EntityQueryBuilder = class {
|
|
|
512
647
|
async list() {
|
|
513
648
|
const qs = this.queryParams.toString();
|
|
514
649
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}${qs ? `?${qs}` : ""}`;
|
|
515
|
-
const res = await this.
|
|
516
|
-
headers: this.getHeaders()
|
|
517
|
-
});
|
|
650
|
+
const res = await this.executeRequest(url);
|
|
518
651
|
if (!res.ok) {
|
|
519
652
|
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
520
653
|
throw new Error(err.message || `Query failed with HTTP ${res.status}`);
|
|
@@ -537,9 +670,7 @@ var EntityQueryBuilder = class {
|
|
|
537
670
|
}
|
|
538
671
|
const qs = params.toString();
|
|
539
672
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}${qs ? `?${qs}` : ""}`;
|
|
540
|
-
const res = await this.
|
|
541
|
-
headers: this.getHeaders()
|
|
542
|
-
});
|
|
673
|
+
const res = await this.executeRequest(url);
|
|
543
674
|
if (!res.ok) {
|
|
544
675
|
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
545
676
|
throw new Error(err.message || `Failed to fetch ${this.typeName}/${id}`);
|
|
@@ -551,9 +682,8 @@ var EntityQueryBuilder = class {
|
|
|
551
682
|
*/
|
|
552
683
|
async create(data) {
|
|
553
684
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}`;
|
|
554
|
-
const res = await this.
|
|
685
|
+
const res = await this.executeRequest(url, {
|
|
555
686
|
method: "POST",
|
|
556
|
-
headers: this.getHeaders(),
|
|
557
687
|
body: JSON.stringify(data)
|
|
558
688
|
});
|
|
559
689
|
if (!res.ok) {
|
|
@@ -567,9 +697,8 @@ var EntityQueryBuilder = class {
|
|
|
567
697
|
*/
|
|
568
698
|
async update(id, partialData) {
|
|
569
699
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}`;
|
|
570
|
-
const res = await this.
|
|
700
|
+
const res = await this.executeRequest(url, {
|
|
571
701
|
method: "PATCH",
|
|
572
|
-
headers: this.getHeaders(),
|
|
573
702
|
body: JSON.stringify(partialData)
|
|
574
703
|
});
|
|
575
704
|
if (!res.ok) {
|
|
@@ -583,9 +712,8 @@ var EntityQueryBuilder = class {
|
|
|
583
712
|
*/
|
|
584
713
|
async delete(id) {
|
|
585
714
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}`;
|
|
586
|
-
const res = await this.
|
|
587
|
-
method: "DELETE"
|
|
588
|
-
headers: this.getHeaders()
|
|
715
|
+
const res = await this.executeRequest(url, {
|
|
716
|
+
method: "DELETE"
|
|
589
717
|
});
|
|
590
718
|
if (!res.ok) {
|
|
591
719
|
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
@@ -598,9 +726,7 @@ var EntityQueryBuilder = class {
|
|
|
598
726
|
*/
|
|
599
727
|
async revisions(id) {
|
|
600
728
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}/revisions`;
|
|
601
|
-
const res = await this.
|
|
602
|
-
headers: this.getHeaders()
|
|
603
|
-
});
|
|
729
|
+
const res = await this.executeRequest(url);
|
|
604
730
|
if (!res.ok) {
|
|
605
731
|
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
606
732
|
throw new Error(err.message || `Failed to fetch revisions for ${this.typeName}/${id}`);
|
|
@@ -613,9 +739,7 @@ var EntityQueryBuilder = class {
|
|
|
613
739
|
async getRelations(id, lang) {
|
|
614
740
|
const qs = lang ? `?lang=${encodeURIComponent(lang)}` : "";
|
|
615
741
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}/relations${qs}`;
|
|
616
|
-
const res = await this.
|
|
617
|
-
headers: this.getHeaders()
|
|
618
|
-
});
|
|
742
|
+
const res = await this.executeRequest(url);
|
|
619
743
|
if (!res.ok) {
|
|
620
744
|
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
621
745
|
throw new Error(err.message || `Failed to fetch relations for ${this.typeName}/${id}`);
|
|
@@ -653,9 +777,8 @@ var EntityQueryBuilder = class {
|
|
|
653
777
|
};
|
|
654
778
|
}
|
|
655
779
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}/relations`;
|
|
656
|
-
const res = await this.
|
|
780
|
+
const res = await this.executeRequest(url, {
|
|
657
781
|
method: "POST",
|
|
658
|
-
headers: this.getHeaders(),
|
|
659
782
|
body: JSON.stringify(payload)
|
|
660
783
|
});
|
|
661
784
|
if (!res.ok) {
|
|
@@ -669,9 +792,8 @@ var EntityQueryBuilder = class {
|
|
|
669
792
|
*/
|
|
670
793
|
async setLinks(id, payload) {
|
|
671
794
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}/relations`;
|
|
672
|
-
const res = await this.
|
|
795
|
+
const res = await this.executeRequest(url, {
|
|
673
796
|
method: "PUT",
|
|
674
|
-
headers: this.getHeaders(),
|
|
675
797
|
body: JSON.stringify(payload)
|
|
676
798
|
});
|
|
677
799
|
if (!res.ok) {
|
|
@@ -685,9 +807,8 @@ var EntityQueryBuilder = class {
|
|
|
685
807
|
*/
|
|
686
808
|
async unlink(id, relationName, targetId, targetTypeName) {
|
|
687
809
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}/relations/${encodeURIComponent(relationName)}/${encodeURIComponent(targetId)}?targetTypeName=${encodeURIComponent(targetTypeName)}`;
|
|
688
|
-
const res = await this.
|
|
689
|
-
method: "DELETE"
|
|
690
|
-
headers: this.getHeaders()
|
|
810
|
+
const res = await this.executeRequest(url, {
|
|
811
|
+
method: "DELETE"
|
|
691
812
|
});
|
|
692
813
|
if (!res.ok) {
|
|
693
814
|
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
@@ -700,9 +821,8 @@ var EntityQueryBuilder = class {
|
|
|
700
821
|
*/
|
|
701
822
|
async action(actionName, payload) {
|
|
702
823
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/actions/${actionName}`;
|
|
703
|
-
const res = await this.
|
|
824
|
+
const res = await this.executeRequest(url, {
|
|
704
825
|
method: "POST",
|
|
705
|
-
headers: this.getHeaders(),
|
|
706
826
|
body: payload ? JSON.stringify(payload) : void 0
|
|
707
827
|
});
|
|
708
828
|
if (!res.ok) {
|
|
@@ -716,9 +836,8 @@ var EntityQueryBuilder = class {
|
|
|
716
836
|
*/
|
|
717
837
|
async instanceAction(id, actionName, payload) {
|
|
718
838
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}/actions/${actionName}`;
|
|
719
|
-
const res = await this.
|
|
839
|
+
const res = await this.executeRequest(url, {
|
|
720
840
|
method: "POST",
|
|
721
|
-
headers: this.getHeaders(),
|
|
722
841
|
body: payload ? JSON.stringify(payload) : void 0
|
|
723
842
|
});
|
|
724
843
|
if (!res.ok) {
|
|
@@ -733,9 +852,8 @@ var EntityQueryBuilder = class {
|
|
|
733
852
|
async exportCsv() {
|
|
734
853
|
const qs = this.queryParams.toString();
|
|
735
854
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/export/csv${qs ? `?${qs}` : ""}`;
|
|
736
|
-
const res = await this.
|
|
855
|
+
const res = await this.executeRequest(url, {
|
|
737
856
|
headers: {
|
|
738
|
-
...this.getHeaders(),
|
|
739
857
|
"Accept": "text/csv"
|
|
740
858
|
}
|
|
741
859
|
});
|
|
@@ -753,11 +871,13 @@ var SchemaClient = class {
|
|
|
753
871
|
tenantId;
|
|
754
872
|
getToken;
|
|
755
873
|
fetchFn;
|
|
874
|
+
fetchWithAuth;
|
|
756
875
|
constructor(options) {
|
|
757
876
|
this.gatewayUrl = options.gatewayUrl.replace(/\/+$/, "");
|
|
758
877
|
this.tenantId = options.tenantId;
|
|
759
|
-
this.getToken = options.getToken;
|
|
878
|
+
this.getToken = options.getToken || (() => null);
|
|
760
879
|
this.fetchFn = options.fetchFn || (typeof fetch !== "undefined" ? fetch : globalThis.fetch);
|
|
880
|
+
this.fetchWithAuth = options.fetchWithAuth;
|
|
761
881
|
}
|
|
762
882
|
getHeaders() {
|
|
763
883
|
const headers = {
|
|
@@ -775,11 +895,11 @@ var SchemaClient = class {
|
|
|
775
895
|
*/
|
|
776
896
|
async define(definition) {
|
|
777
897
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/definition`;
|
|
778
|
-
const
|
|
898
|
+
const init = {
|
|
779
899
|
method: "POST",
|
|
780
|
-
headers: this.getHeaders(),
|
|
781
900
|
body: JSON.stringify(definition)
|
|
782
|
-
}
|
|
901
|
+
};
|
|
902
|
+
const res = this.fetchWithAuth ? await this.fetchWithAuth(url, init) : await this.fetchFn(url, { ...init, headers: this.getHeaders() });
|
|
783
903
|
if (!res.ok) {
|
|
784
904
|
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
785
905
|
throw new Error(err.message || `Failed to define schema for ${definition.typeName}`);
|
|
@@ -833,13 +953,17 @@ var Zone4CodeClient = class {
|
|
|
833
953
|
storage: this.storage,
|
|
834
954
|
storageKey: config.storageKey,
|
|
835
955
|
initialToken: config.token,
|
|
836
|
-
fetchFn: this.fetchFn
|
|
956
|
+
fetchFn: this.fetchFn,
|
|
957
|
+
autoRefresh: config.autoRefresh,
|
|
958
|
+
tokenExpiryBuffer: config.tokenExpiryBuffer,
|
|
959
|
+
onSessionExpired: config.onSessionExpired
|
|
837
960
|
});
|
|
838
961
|
this.schema = new SchemaClient({
|
|
839
962
|
gatewayUrl: this.gatewayUrl,
|
|
840
963
|
tenantId: this.tenantId,
|
|
841
964
|
getToken: () => this.auth.getToken(),
|
|
842
|
-
fetchFn: this.fetchFn
|
|
965
|
+
fetchFn: this.fetchFn,
|
|
966
|
+
fetchWithAuth: (url, init) => this.auth.fetchWithAuth(url, init)
|
|
843
967
|
});
|
|
844
968
|
}
|
|
845
969
|
/**
|
|
@@ -851,7 +975,8 @@ var Zone4CodeClient = class {
|
|
|
851
975
|
tenantId: this.tenantId,
|
|
852
976
|
typeName,
|
|
853
977
|
getToken: () => this.auth.getToken(),
|
|
854
|
-
fetchFn: this.fetchFn
|
|
978
|
+
fetchFn: this.fetchFn,
|
|
979
|
+
fetchWithAuth: (url, init) => this.auth.fetchWithAuth(url, init)
|
|
855
980
|
});
|
|
856
981
|
}
|
|
857
982
|
/**
|
|
@@ -860,21 +985,17 @@ var Zone4CodeClient = class {
|
|
|
860
985
|
entities(typeName) {
|
|
861
986
|
return this.from(typeName);
|
|
862
987
|
}
|
|
988
|
+
/**
|
|
989
|
+
* Register a listener called whenever the session expires completely
|
|
990
|
+
*/
|
|
991
|
+
onSessionExpired(callback) {
|
|
992
|
+
return this.auth.onSessionExpired(callback);
|
|
993
|
+
}
|
|
863
994
|
/**
|
|
864
995
|
* Get current authenticated user profile and platform wallet
|
|
865
996
|
*/
|
|
866
997
|
async getMe() {
|
|
867
|
-
const
|
|
868
|
-
if (!token) {
|
|
869
|
-
throw new Error("Not authenticated: please call login() or setToken() first");
|
|
870
|
-
}
|
|
871
|
-
const res = await this.fetchFn(`${this.gatewayUrl}/generic/${this.tenantId}/me`, {
|
|
872
|
-
headers: {
|
|
873
|
-
"Content-Type": "application/json",
|
|
874
|
-
"Authorization": `Bearer ${token}`,
|
|
875
|
-
"x-tenant-id": this.tenantId
|
|
876
|
-
}
|
|
877
|
-
});
|
|
998
|
+
const res = await this.auth.fetchWithAuth(`${this.gatewayUrl}/generic/${this.tenantId}/me`);
|
|
878
999
|
if (!res.ok) {
|
|
879
1000
|
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
880
1001
|
throw new Error(err.message || `Failed to fetch profile & wallet (HTTP ${res.status})`);
|