zone4code-sdk 1.0.5 → 1.0.7
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 +217 -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 +217 -93
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -50,6 +50,22 @@ function decodeJwtPayload(token) {
|
|
|
50
50
|
return null;
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
|
+
function normalizeHeaders(headers) {
|
|
54
|
+
const result = {};
|
|
55
|
+
if (!headers) return result;
|
|
56
|
+
if (typeof Headers !== "undefined" && headers instanceof Headers) {
|
|
57
|
+
headers.forEach((value, key) => {
|
|
58
|
+
result[key] = value;
|
|
59
|
+
});
|
|
60
|
+
} else if (Array.isArray(headers)) {
|
|
61
|
+
for (const [key, value] of headers) {
|
|
62
|
+
result[key] = value;
|
|
63
|
+
}
|
|
64
|
+
} else {
|
|
65
|
+
Object.assign(result, headers);
|
|
66
|
+
}
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
53
69
|
var AuthClient = class _AuthClient {
|
|
54
70
|
gatewayUrl;
|
|
55
71
|
tenantId;
|
|
@@ -59,26 +75,39 @@ var AuthClient = class _AuthClient {
|
|
|
59
75
|
token = null;
|
|
60
76
|
refreshTokenValue = null;
|
|
61
77
|
fetchFn;
|
|
78
|
+
autoRefresh;
|
|
79
|
+
tokenExpiryBuffer;
|
|
80
|
+
onSessionExpiredCallback;
|
|
81
|
+
sessionExpiredListeners = [];
|
|
82
|
+
activeRefreshPromise = null;
|
|
83
|
+
storageLoadedPromise;
|
|
62
84
|
static TOKEN_KEY_PREFIX = "z4c_token_";
|
|
63
85
|
constructor(options) {
|
|
86
|
+
const storage = options.storage;
|
|
87
|
+
const storageKey = options.storageKey || _AuthClient.TOKEN_KEY_PREFIX + options.tenantId;
|
|
88
|
+
const refreshStorageKey = storageKey + "_refresh";
|
|
64
89
|
this.gatewayUrl = options.gatewayUrl.replace(/\/+$/, "");
|
|
65
90
|
this.tenantId = options.tenantId;
|
|
66
|
-
this.storage =
|
|
67
|
-
this.storageKey =
|
|
68
|
-
this.refreshStorageKey =
|
|
91
|
+
this.storage = storage;
|
|
92
|
+
this.storageKey = storageKey;
|
|
93
|
+
this.refreshStorageKey = refreshStorageKey;
|
|
69
94
|
this.fetchFn = options.fetchFn || (typeof fetch !== "undefined" ? fetch : globalThis.fetch);
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
95
|
+
this.autoRefresh = options.autoRefresh ?? true;
|
|
96
|
+
this.tokenExpiryBuffer = options.tokenExpiryBuffer ?? 30;
|
|
97
|
+
this.onSessionExpiredCallback = options.onSessionExpired;
|
|
98
|
+
this.storageLoadedPromise = (async () => {
|
|
99
|
+
if (options.initialToken) {
|
|
100
|
+
this.setToken(options.initialToken);
|
|
101
|
+
} else {
|
|
102
|
+
try {
|
|
103
|
+
const storedToken = await storage.getItem(storageKey);
|
|
104
|
+
if (storedToken) this.token = storedToken;
|
|
105
|
+
const storedRefresh = await storage.getItem(refreshStorageKey);
|
|
106
|
+
if (storedRefresh) this.refreshTokenValue = storedRefresh;
|
|
107
|
+
} catch {
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
})();
|
|
82
111
|
}
|
|
83
112
|
getToken() {
|
|
84
113
|
return this.token;
|
|
@@ -105,6 +134,94 @@ var AuthClient = class _AuthClient {
|
|
|
105
134
|
isAuthenticated() {
|
|
106
135
|
return !!this.token;
|
|
107
136
|
}
|
|
137
|
+
/**
|
|
138
|
+
* Check whether the current access token is expired or expiring within bufferSeconds
|
|
139
|
+
*/
|
|
140
|
+
isTokenExpired(bufferSeconds = this.tokenExpiryBuffer) {
|
|
141
|
+
if (!this.token) return true;
|
|
142
|
+
const payload = decodeJwtPayload(this.token);
|
|
143
|
+
if (!payload || typeof payload.exp !== "number") return false;
|
|
144
|
+
const nowSeconds = Math.floor(Date.now() / 1e3);
|
|
145
|
+
return nowSeconds >= payload.exp - bufferSeconds;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Returns a valid access token, proactively refreshing it if expired and autoRefresh is enabled
|
|
149
|
+
*/
|
|
150
|
+
async getValidToken() {
|
|
151
|
+
await this.storageLoadedPromise;
|
|
152
|
+
if (!this.token) return null;
|
|
153
|
+
if (!this.autoRefresh) return this.token;
|
|
154
|
+
if (this.isTokenExpired()) {
|
|
155
|
+
const refreshToken = this.getRefreshToken();
|
|
156
|
+
if (refreshToken) {
|
|
157
|
+
try {
|
|
158
|
+
const res = await this.refreshToken(refreshToken);
|
|
159
|
+
return res.token || this.token;
|
|
160
|
+
} catch {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
} else {
|
|
164
|
+
this.notifySessionExpired();
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return this.token;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Register a callback triggered when session has expired completely
|
|
172
|
+
*/
|
|
173
|
+
onSessionExpired(listener) {
|
|
174
|
+
this.sessionExpiredListeners.push(listener);
|
|
175
|
+
return () => {
|
|
176
|
+
this.sessionExpiredListeners = this.sessionExpiredListeners.filter((l) => l !== listener);
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
notifySessionExpired() {
|
|
180
|
+
this.logout();
|
|
181
|
+
for (const listener of this.sessionExpiredListeners) {
|
|
182
|
+
try {
|
|
183
|
+
listener();
|
|
184
|
+
} catch (e) {
|
|
185
|
+
console.error("Error in onSessionExpired listener:", e);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (this.onSessionExpiredCallback) {
|
|
189
|
+
try {
|
|
190
|
+
this.onSessionExpiredCallback();
|
|
191
|
+
} catch (e) {
|
|
192
|
+
console.error("Error in onSessionExpired callback:", e);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Perform an HTTP request with automatic Authorization injection, proactive refresh, and reactive 401 retry
|
|
198
|
+
*/
|
|
199
|
+
async fetchWithAuth(url, init = {}) {
|
|
200
|
+
await this.storageLoadedPromise;
|
|
201
|
+
const token = await this.getValidToken();
|
|
202
|
+
const headers = normalizeHeaders(init.headers);
|
|
203
|
+
if (!headers["x-tenant-id"] && !headers["X-Tenant-Id"]) {
|
|
204
|
+
headers["x-tenant-id"] = this.tenantId;
|
|
205
|
+
}
|
|
206
|
+
if (init.body && !headers["Content-Type"] && !headers["content-type"]) {
|
|
207
|
+
headers["Content-Type"] = "application/json";
|
|
208
|
+
}
|
|
209
|
+
if (token && !headers["Authorization"] && !headers["authorization"]) {
|
|
210
|
+
headers["Authorization"] = `Bearer ${token}`;
|
|
211
|
+
}
|
|
212
|
+
let res = await this.fetchFn(url, { ...init, headers });
|
|
213
|
+
if (res.status === 401 && this.autoRefresh && this.getRefreshToken()) {
|
|
214
|
+
try {
|
|
215
|
+
const refreshed = await this.refreshToken();
|
|
216
|
+
if (refreshed?.token) {
|
|
217
|
+
headers["Authorization"] = `Bearer ${refreshed.token}`;
|
|
218
|
+
res = await this.fetchFn(url, { ...init, headers });
|
|
219
|
+
}
|
|
220
|
+
} catch {
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return res;
|
|
224
|
+
}
|
|
108
225
|
/**
|
|
109
226
|
* Synchronously parse and return current authenticated user from JWT token
|
|
110
227
|
*/
|
|
@@ -260,36 +377,48 @@ var AuthClient = class _AuthClient {
|
|
|
260
377
|
async refreshToken(refreshToken) {
|
|
261
378
|
const tokenToUse = refreshToken || this.getRefreshToken();
|
|
262
379
|
if (!tokenToUse) {
|
|
380
|
+
this.notifySessionExpired();
|
|
263
381
|
throw new Error("No refresh token available");
|
|
264
382
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
method: "POST",
|
|
268
|
-
headers: {
|
|
269
|
-
"Content-Type": "application/json",
|
|
270
|
-
"x-tenant-id": this.tenantId
|
|
271
|
-
},
|
|
272
|
-
body: JSON.stringify({ refresh_token: tokenToUse })
|
|
273
|
-
});
|
|
274
|
-
if (!res.ok) {
|
|
275
|
-
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
276
|
-
throw new Error(err.message || err.error || `Token refresh failed with HTTP ${res.status}`);
|
|
383
|
+
if (this.activeRefreshPromise) {
|
|
384
|
+
return this.activeRefreshPromise;
|
|
277
385
|
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
386
|
+
this.activeRefreshPromise = (async () => {
|
|
387
|
+
try {
|
|
388
|
+
const url = `${this.gatewayUrl}/auth/${this.tenantId}/refresh`;
|
|
389
|
+
const res = await this.fetchFn(url, {
|
|
390
|
+
method: "POST",
|
|
391
|
+
headers: {
|
|
392
|
+
"Content-Type": "application/json",
|
|
393
|
+
"x-tenant-id": this.tenantId
|
|
394
|
+
},
|
|
395
|
+
body: JSON.stringify({ refresh_token: tokenToUse })
|
|
396
|
+
});
|
|
397
|
+
if (!res.ok) {
|
|
398
|
+
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
399
|
+
this.notifySessionExpired();
|
|
400
|
+
throw new Error(err.message || err.error || `Token refresh failed with HTTP ${res.status}`);
|
|
401
|
+
}
|
|
402
|
+
const json = await res.json();
|
|
403
|
+
const token = json.token || json.access_token;
|
|
404
|
+
if (token) {
|
|
405
|
+
this.setToken(token);
|
|
406
|
+
}
|
|
407
|
+
if (json.refresh_token) {
|
|
408
|
+
this.setRefreshToken(json.refresh_token);
|
|
409
|
+
}
|
|
410
|
+
const user = this.getUser() || json.user;
|
|
411
|
+
return {
|
|
412
|
+
token: token || "",
|
|
413
|
+
refreshToken: json.refresh_token,
|
|
414
|
+
user,
|
|
415
|
+
...json
|
|
416
|
+
};
|
|
417
|
+
} finally {
|
|
418
|
+
this.activeRefreshPromise = null;
|
|
419
|
+
}
|
|
420
|
+
})();
|
|
421
|
+
return this.activeRefreshPromise;
|
|
293
422
|
}
|
|
294
423
|
/**
|
|
295
424
|
* Request password reset email
|
|
@@ -418,13 +547,15 @@ var EntityQueryBuilder = class {
|
|
|
418
547
|
typeName;
|
|
419
548
|
getToken;
|
|
420
549
|
fetchFn;
|
|
550
|
+
fetchWithAuth;
|
|
421
551
|
queryParams = new URLSearchParams();
|
|
422
552
|
constructor(options) {
|
|
423
553
|
this.gatewayUrl = options.gatewayUrl.replace(/\/+$/, "");
|
|
424
554
|
this.tenantId = options.tenantId;
|
|
425
555
|
this.typeName = options.typeName;
|
|
426
|
-
this.getToken = options.getToken;
|
|
556
|
+
this.getToken = options.getToken || (() => null);
|
|
427
557
|
this.fetchFn = options.fetchFn || (typeof fetch !== "undefined" ? fetch : globalThis.fetch);
|
|
558
|
+
this.fetchWithAuth = options.fetchWithAuth;
|
|
428
559
|
}
|
|
429
560
|
getHeaders() {
|
|
430
561
|
const headers = {
|
|
@@ -437,6 +568,13 @@ var EntityQueryBuilder = class {
|
|
|
437
568
|
}
|
|
438
569
|
return headers;
|
|
439
570
|
}
|
|
571
|
+
async executeRequest(url, init = {}) {
|
|
572
|
+
if (this.fetchWithAuth) {
|
|
573
|
+
return this.fetchWithAuth(url, init);
|
|
574
|
+
}
|
|
575
|
+
const headers = { ...this.getHeaders(), ...init.headers || {} };
|
|
576
|
+
return this.fetchFn(url, { ...init, headers });
|
|
577
|
+
}
|
|
440
578
|
// ----------------------------------------------------
|
|
441
579
|
// Filter Operators
|
|
442
580
|
// ----------------------------------------------------
|
|
@@ -545,9 +683,7 @@ var EntityQueryBuilder = class {
|
|
|
545
683
|
async list() {
|
|
546
684
|
const qs = this.queryParams.toString();
|
|
547
685
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}${qs ? `?${qs}` : ""}`;
|
|
548
|
-
const res = await this.
|
|
549
|
-
headers: this.getHeaders()
|
|
550
|
-
});
|
|
686
|
+
const res = await this.executeRequest(url);
|
|
551
687
|
if (!res.ok) {
|
|
552
688
|
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
553
689
|
throw new Error(err.message || `Query failed with HTTP ${res.status}`);
|
|
@@ -570,9 +706,7 @@ var EntityQueryBuilder = class {
|
|
|
570
706
|
}
|
|
571
707
|
const qs = params.toString();
|
|
572
708
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}${qs ? `?${qs}` : ""}`;
|
|
573
|
-
const res = await this.
|
|
574
|
-
headers: this.getHeaders()
|
|
575
|
-
});
|
|
709
|
+
const res = await this.executeRequest(url);
|
|
576
710
|
if (!res.ok) {
|
|
577
711
|
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
578
712
|
throw new Error(err.message || `Failed to fetch ${this.typeName}/${id}`);
|
|
@@ -584,9 +718,8 @@ var EntityQueryBuilder = class {
|
|
|
584
718
|
*/
|
|
585
719
|
async create(data) {
|
|
586
720
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}`;
|
|
587
|
-
const res = await this.
|
|
721
|
+
const res = await this.executeRequest(url, {
|
|
588
722
|
method: "POST",
|
|
589
|
-
headers: this.getHeaders(),
|
|
590
723
|
body: JSON.stringify(data)
|
|
591
724
|
});
|
|
592
725
|
if (!res.ok) {
|
|
@@ -600,9 +733,8 @@ var EntityQueryBuilder = class {
|
|
|
600
733
|
*/
|
|
601
734
|
async update(id, partialData) {
|
|
602
735
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}`;
|
|
603
|
-
const res = await this.
|
|
736
|
+
const res = await this.executeRequest(url, {
|
|
604
737
|
method: "PATCH",
|
|
605
|
-
headers: this.getHeaders(),
|
|
606
738
|
body: JSON.stringify(partialData)
|
|
607
739
|
});
|
|
608
740
|
if (!res.ok) {
|
|
@@ -616,9 +748,8 @@ var EntityQueryBuilder = class {
|
|
|
616
748
|
*/
|
|
617
749
|
async delete(id) {
|
|
618
750
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}`;
|
|
619
|
-
const res = await this.
|
|
620
|
-
method: "DELETE"
|
|
621
|
-
headers: this.getHeaders()
|
|
751
|
+
const res = await this.executeRequest(url, {
|
|
752
|
+
method: "DELETE"
|
|
622
753
|
});
|
|
623
754
|
if (!res.ok) {
|
|
624
755
|
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
@@ -631,9 +762,7 @@ var EntityQueryBuilder = class {
|
|
|
631
762
|
*/
|
|
632
763
|
async revisions(id) {
|
|
633
764
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}/revisions`;
|
|
634
|
-
const res = await this.
|
|
635
|
-
headers: this.getHeaders()
|
|
636
|
-
});
|
|
765
|
+
const res = await this.executeRequest(url);
|
|
637
766
|
if (!res.ok) {
|
|
638
767
|
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
639
768
|
throw new Error(err.message || `Failed to fetch revisions for ${this.typeName}/${id}`);
|
|
@@ -646,9 +775,7 @@ var EntityQueryBuilder = class {
|
|
|
646
775
|
async getRelations(id, lang) {
|
|
647
776
|
const qs = lang ? `?lang=${encodeURIComponent(lang)}` : "";
|
|
648
777
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}/relations${qs}`;
|
|
649
|
-
const res = await this.
|
|
650
|
-
headers: this.getHeaders()
|
|
651
|
-
});
|
|
778
|
+
const res = await this.executeRequest(url);
|
|
652
779
|
if (!res.ok) {
|
|
653
780
|
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
654
781
|
throw new Error(err.message || `Failed to fetch relations for ${this.typeName}/${id}`);
|
|
@@ -686,9 +813,8 @@ var EntityQueryBuilder = class {
|
|
|
686
813
|
};
|
|
687
814
|
}
|
|
688
815
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}/relations`;
|
|
689
|
-
const res = await this.
|
|
816
|
+
const res = await this.executeRequest(url, {
|
|
690
817
|
method: "POST",
|
|
691
|
-
headers: this.getHeaders(),
|
|
692
818
|
body: JSON.stringify(payload)
|
|
693
819
|
});
|
|
694
820
|
if (!res.ok) {
|
|
@@ -702,9 +828,8 @@ var EntityQueryBuilder = class {
|
|
|
702
828
|
*/
|
|
703
829
|
async setLinks(id, payload) {
|
|
704
830
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}/relations`;
|
|
705
|
-
const res = await this.
|
|
831
|
+
const res = await this.executeRequest(url, {
|
|
706
832
|
method: "PUT",
|
|
707
|
-
headers: this.getHeaders(),
|
|
708
833
|
body: JSON.stringify(payload)
|
|
709
834
|
});
|
|
710
835
|
if (!res.ok) {
|
|
@@ -718,9 +843,8 @@ var EntityQueryBuilder = class {
|
|
|
718
843
|
*/
|
|
719
844
|
async unlink(id, relationName, targetId, targetTypeName) {
|
|
720
845
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}/relations/${encodeURIComponent(relationName)}/${encodeURIComponent(targetId)}?targetTypeName=${encodeURIComponent(targetTypeName)}`;
|
|
721
|
-
const res = await this.
|
|
722
|
-
method: "DELETE"
|
|
723
|
-
headers: this.getHeaders()
|
|
846
|
+
const res = await this.executeRequest(url, {
|
|
847
|
+
method: "DELETE"
|
|
724
848
|
});
|
|
725
849
|
if (!res.ok) {
|
|
726
850
|
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
@@ -733,9 +857,8 @@ var EntityQueryBuilder = class {
|
|
|
733
857
|
*/
|
|
734
858
|
async action(actionName, payload) {
|
|
735
859
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/actions/${actionName}`;
|
|
736
|
-
const res = await this.
|
|
860
|
+
const res = await this.executeRequest(url, {
|
|
737
861
|
method: "POST",
|
|
738
|
-
headers: this.getHeaders(),
|
|
739
862
|
body: payload ? JSON.stringify(payload) : void 0
|
|
740
863
|
});
|
|
741
864
|
if (!res.ok) {
|
|
@@ -749,9 +872,8 @@ var EntityQueryBuilder = class {
|
|
|
749
872
|
*/
|
|
750
873
|
async instanceAction(id, actionName, payload) {
|
|
751
874
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}/actions/${actionName}`;
|
|
752
|
-
const res = await this.
|
|
875
|
+
const res = await this.executeRequest(url, {
|
|
753
876
|
method: "POST",
|
|
754
|
-
headers: this.getHeaders(),
|
|
755
877
|
body: payload ? JSON.stringify(payload) : void 0
|
|
756
878
|
});
|
|
757
879
|
if (!res.ok) {
|
|
@@ -766,9 +888,8 @@ var EntityQueryBuilder = class {
|
|
|
766
888
|
async exportCsv() {
|
|
767
889
|
const qs = this.queryParams.toString();
|
|
768
890
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/export/csv${qs ? `?${qs}` : ""}`;
|
|
769
|
-
const res = await this.
|
|
891
|
+
const res = await this.executeRequest(url, {
|
|
770
892
|
headers: {
|
|
771
|
-
...this.getHeaders(),
|
|
772
893
|
"Accept": "text/csv"
|
|
773
894
|
}
|
|
774
895
|
});
|
|
@@ -786,11 +907,13 @@ var SchemaClient = class {
|
|
|
786
907
|
tenantId;
|
|
787
908
|
getToken;
|
|
788
909
|
fetchFn;
|
|
910
|
+
fetchWithAuth;
|
|
789
911
|
constructor(options) {
|
|
790
912
|
this.gatewayUrl = options.gatewayUrl.replace(/\/+$/, "");
|
|
791
913
|
this.tenantId = options.tenantId;
|
|
792
|
-
this.getToken = options.getToken;
|
|
914
|
+
this.getToken = options.getToken || (() => null);
|
|
793
915
|
this.fetchFn = options.fetchFn || (typeof fetch !== "undefined" ? fetch : globalThis.fetch);
|
|
916
|
+
this.fetchWithAuth = options.fetchWithAuth;
|
|
794
917
|
}
|
|
795
918
|
getHeaders() {
|
|
796
919
|
const headers = {
|
|
@@ -808,11 +931,11 @@ var SchemaClient = class {
|
|
|
808
931
|
*/
|
|
809
932
|
async define(definition) {
|
|
810
933
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/definition`;
|
|
811
|
-
const
|
|
934
|
+
const init = {
|
|
812
935
|
method: "POST",
|
|
813
|
-
headers: this.getHeaders(),
|
|
814
936
|
body: JSON.stringify(definition)
|
|
815
|
-
}
|
|
937
|
+
};
|
|
938
|
+
const res = this.fetchWithAuth ? await this.fetchWithAuth(url, init) : await this.fetchFn(url, { ...init, headers: this.getHeaders() });
|
|
816
939
|
if (!res.ok) {
|
|
817
940
|
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
818
941
|
throw new Error(err.message || `Failed to define schema for ${definition.typeName}`);
|
|
@@ -866,13 +989,17 @@ var Zone4CodeClient = class {
|
|
|
866
989
|
storage: this.storage,
|
|
867
990
|
storageKey: config.storageKey,
|
|
868
991
|
initialToken: config.token,
|
|
869
|
-
fetchFn: this.fetchFn
|
|
992
|
+
fetchFn: this.fetchFn,
|
|
993
|
+
autoRefresh: config.autoRefresh,
|
|
994
|
+
tokenExpiryBuffer: config.tokenExpiryBuffer,
|
|
995
|
+
onSessionExpired: config.onSessionExpired
|
|
870
996
|
});
|
|
871
997
|
this.schema = new SchemaClient({
|
|
872
998
|
gatewayUrl: this.gatewayUrl,
|
|
873
999
|
tenantId: this.tenantId,
|
|
874
1000
|
getToken: () => this.auth.getToken(),
|
|
875
|
-
fetchFn: this.fetchFn
|
|
1001
|
+
fetchFn: this.fetchFn,
|
|
1002
|
+
fetchWithAuth: (url, init) => this.auth.fetchWithAuth(url, init)
|
|
876
1003
|
});
|
|
877
1004
|
}
|
|
878
1005
|
/**
|
|
@@ -884,7 +1011,8 @@ var Zone4CodeClient = class {
|
|
|
884
1011
|
tenantId: this.tenantId,
|
|
885
1012
|
typeName,
|
|
886
1013
|
getToken: () => this.auth.getToken(),
|
|
887
|
-
fetchFn: this.fetchFn
|
|
1014
|
+
fetchFn: this.fetchFn,
|
|
1015
|
+
fetchWithAuth: (url, init) => this.auth.fetchWithAuth(url, init)
|
|
888
1016
|
});
|
|
889
1017
|
}
|
|
890
1018
|
/**
|
|
@@ -893,21 +1021,17 @@ var Zone4CodeClient = class {
|
|
|
893
1021
|
entities(typeName) {
|
|
894
1022
|
return this.from(typeName);
|
|
895
1023
|
}
|
|
1024
|
+
/**
|
|
1025
|
+
* Register a listener called whenever the session expires completely
|
|
1026
|
+
*/
|
|
1027
|
+
onSessionExpired(callback) {
|
|
1028
|
+
return this.auth.onSessionExpired(callback);
|
|
1029
|
+
}
|
|
896
1030
|
/**
|
|
897
1031
|
* Get current authenticated user profile and platform wallet
|
|
898
1032
|
*/
|
|
899
1033
|
async getMe() {
|
|
900
|
-
const
|
|
901
|
-
if (!token) {
|
|
902
|
-
throw new Error("Not authenticated: please call login() or setToken() first");
|
|
903
|
-
}
|
|
904
|
-
const res = await this.fetchFn(`${this.gatewayUrl}/generic/${this.tenantId}/me`, {
|
|
905
|
-
headers: {
|
|
906
|
-
"Content-Type": "application/json",
|
|
907
|
-
"Authorization": `Bearer ${token}`,
|
|
908
|
-
"x-tenant-id": this.tenantId
|
|
909
|
-
}
|
|
910
|
-
});
|
|
1034
|
+
const res = await this.auth.fetchWithAuth(`${this.gatewayUrl}/generic/${this.tenantId}/me`);
|
|
911
1035
|
if (!res.ok) {
|
|
912
1036
|
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
913
1037
|
throw new Error(err.message || `Failed to fetch profile & wallet (HTTP ${res.status})`);
|