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.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,94 @@ 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 (init.body && !headers["Content-Type"] && !headers["content-type"]) {
|
|
174
|
+
headers["Content-Type"] = "application/json";
|
|
175
|
+
}
|
|
176
|
+
if (token && !headers["Authorization"] && !headers["authorization"]) {
|
|
177
|
+
headers["Authorization"] = `Bearer ${token}`;
|
|
178
|
+
}
|
|
179
|
+
let res = await this.fetchFn(url, { ...init, headers });
|
|
180
|
+
if (res.status === 401 && this.autoRefresh && this.getRefreshToken()) {
|
|
181
|
+
try {
|
|
182
|
+
const refreshed = await this.refreshToken();
|
|
183
|
+
if (refreshed?.token) {
|
|
184
|
+
headers["Authorization"] = `Bearer ${refreshed.token}`;
|
|
185
|
+
res = await this.fetchFn(url, { ...init, headers });
|
|
186
|
+
}
|
|
187
|
+
} catch {
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return res;
|
|
191
|
+
}
|
|
75
192
|
/**
|
|
76
193
|
* Synchronously parse and return current authenticated user from JWT token
|
|
77
194
|
*/
|
|
@@ -227,36 +344,48 @@ var AuthClient = class _AuthClient {
|
|
|
227
344
|
async refreshToken(refreshToken) {
|
|
228
345
|
const tokenToUse = refreshToken || this.getRefreshToken();
|
|
229
346
|
if (!tokenToUse) {
|
|
347
|
+
this.notifySessionExpired();
|
|
230
348
|
throw new Error("No refresh token available");
|
|
231
349
|
}
|
|
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}`);
|
|
350
|
+
if (this.activeRefreshPromise) {
|
|
351
|
+
return this.activeRefreshPromise;
|
|
244
352
|
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
353
|
+
this.activeRefreshPromise = (async () => {
|
|
354
|
+
try {
|
|
355
|
+
const url = `${this.gatewayUrl}/auth/${this.tenantId}/refresh`;
|
|
356
|
+
const res = await this.fetchFn(url, {
|
|
357
|
+
method: "POST",
|
|
358
|
+
headers: {
|
|
359
|
+
"Content-Type": "application/json",
|
|
360
|
+
"x-tenant-id": this.tenantId
|
|
361
|
+
},
|
|
362
|
+
body: JSON.stringify({ refresh_token: tokenToUse })
|
|
363
|
+
});
|
|
364
|
+
if (!res.ok) {
|
|
365
|
+
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
366
|
+
this.notifySessionExpired();
|
|
367
|
+
throw new Error(err.message || err.error || `Token refresh failed with HTTP ${res.status}`);
|
|
368
|
+
}
|
|
369
|
+
const json = await res.json();
|
|
370
|
+
const token = json.token || json.access_token;
|
|
371
|
+
if (token) {
|
|
372
|
+
this.setToken(token);
|
|
373
|
+
}
|
|
374
|
+
if (json.refresh_token) {
|
|
375
|
+
this.setRefreshToken(json.refresh_token);
|
|
376
|
+
}
|
|
377
|
+
const user = this.getUser() || json.user;
|
|
378
|
+
return {
|
|
379
|
+
token: token || "",
|
|
380
|
+
refreshToken: json.refresh_token,
|
|
381
|
+
user,
|
|
382
|
+
...json
|
|
383
|
+
};
|
|
384
|
+
} finally {
|
|
385
|
+
this.activeRefreshPromise = null;
|
|
386
|
+
}
|
|
387
|
+
})();
|
|
388
|
+
return this.activeRefreshPromise;
|
|
260
389
|
}
|
|
261
390
|
/**
|
|
262
391
|
* Request password reset email
|
|
@@ -385,13 +514,15 @@ var EntityQueryBuilder = class {
|
|
|
385
514
|
typeName;
|
|
386
515
|
getToken;
|
|
387
516
|
fetchFn;
|
|
517
|
+
fetchWithAuth;
|
|
388
518
|
queryParams = new URLSearchParams();
|
|
389
519
|
constructor(options) {
|
|
390
520
|
this.gatewayUrl = options.gatewayUrl.replace(/\/+$/, "");
|
|
391
521
|
this.tenantId = options.tenantId;
|
|
392
522
|
this.typeName = options.typeName;
|
|
393
|
-
this.getToken = options.getToken;
|
|
523
|
+
this.getToken = options.getToken || (() => null);
|
|
394
524
|
this.fetchFn = options.fetchFn || (typeof fetch !== "undefined" ? fetch : globalThis.fetch);
|
|
525
|
+
this.fetchWithAuth = options.fetchWithAuth;
|
|
395
526
|
}
|
|
396
527
|
getHeaders() {
|
|
397
528
|
const headers = {
|
|
@@ -404,6 +535,13 @@ var EntityQueryBuilder = class {
|
|
|
404
535
|
}
|
|
405
536
|
return headers;
|
|
406
537
|
}
|
|
538
|
+
async executeRequest(url, init = {}) {
|
|
539
|
+
if (this.fetchWithAuth) {
|
|
540
|
+
return this.fetchWithAuth(url, init);
|
|
541
|
+
}
|
|
542
|
+
const headers = { ...this.getHeaders(), ...init.headers || {} };
|
|
543
|
+
return this.fetchFn(url, { ...init, headers });
|
|
544
|
+
}
|
|
407
545
|
// ----------------------------------------------------
|
|
408
546
|
// Filter Operators
|
|
409
547
|
// ----------------------------------------------------
|
|
@@ -512,9 +650,7 @@ var EntityQueryBuilder = class {
|
|
|
512
650
|
async list() {
|
|
513
651
|
const qs = this.queryParams.toString();
|
|
514
652
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}${qs ? `?${qs}` : ""}`;
|
|
515
|
-
const res = await this.
|
|
516
|
-
headers: this.getHeaders()
|
|
517
|
-
});
|
|
653
|
+
const res = await this.executeRequest(url);
|
|
518
654
|
if (!res.ok) {
|
|
519
655
|
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
520
656
|
throw new Error(err.message || `Query failed with HTTP ${res.status}`);
|
|
@@ -537,9 +673,7 @@ var EntityQueryBuilder = class {
|
|
|
537
673
|
}
|
|
538
674
|
const qs = params.toString();
|
|
539
675
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}${qs ? `?${qs}` : ""}`;
|
|
540
|
-
const res = await this.
|
|
541
|
-
headers: this.getHeaders()
|
|
542
|
-
});
|
|
676
|
+
const res = await this.executeRequest(url);
|
|
543
677
|
if (!res.ok) {
|
|
544
678
|
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
545
679
|
throw new Error(err.message || `Failed to fetch ${this.typeName}/${id}`);
|
|
@@ -551,9 +685,8 @@ var EntityQueryBuilder = class {
|
|
|
551
685
|
*/
|
|
552
686
|
async create(data) {
|
|
553
687
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}`;
|
|
554
|
-
const res = await this.
|
|
688
|
+
const res = await this.executeRequest(url, {
|
|
555
689
|
method: "POST",
|
|
556
|
-
headers: this.getHeaders(),
|
|
557
690
|
body: JSON.stringify(data)
|
|
558
691
|
});
|
|
559
692
|
if (!res.ok) {
|
|
@@ -567,9 +700,8 @@ var EntityQueryBuilder = class {
|
|
|
567
700
|
*/
|
|
568
701
|
async update(id, partialData) {
|
|
569
702
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}`;
|
|
570
|
-
const res = await this.
|
|
703
|
+
const res = await this.executeRequest(url, {
|
|
571
704
|
method: "PATCH",
|
|
572
|
-
headers: this.getHeaders(),
|
|
573
705
|
body: JSON.stringify(partialData)
|
|
574
706
|
});
|
|
575
707
|
if (!res.ok) {
|
|
@@ -583,9 +715,8 @@ var EntityQueryBuilder = class {
|
|
|
583
715
|
*/
|
|
584
716
|
async delete(id) {
|
|
585
717
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}`;
|
|
586
|
-
const res = await this.
|
|
587
|
-
method: "DELETE"
|
|
588
|
-
headers: this.getHeaders()
|
|
718
|
+
const res = await this.executeRequest(url, {
|
|
719
|
+
method: "DELETE"
|
|
589
720
|
});
|
|
590
721
|
if (!res.ok) {
|
|
591
722
|
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
@@ -598,9 +729,7 @@ var EntityQueryBuilder = class {
|
|
|
598
729
|
*/
|
|
599
730
|
async revisions(id) {
|
|
600
731
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}/revisions`;
|
|
601
|
-
const res = await this.
|
|
602
|
-
headers: this.getHeaders()
|
|
603
|
-
});
|
|
732
|
+
const res = await this.executeRequest(url);
|
|
604
733
|
if (!res.ok) {
|
|
605
734
|
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
606
735
|
throw new Error(err.message || `Failed to fetch revisions for ${this.typeName}/${id}`);
|
|
@@ -613,9 +742,7 @@ var EntityQueryBuilder = class {
|
|
|
613
742
|
async getRelations(id, lang) {
|
|
614
743
|
const qs = lang ? `?lang=${encodeURIComponent(lang)}` : "";
|
|
615
744
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}/relations${qs}`;
|
|
616
|
-
const res = await this.
|
|
617
|
-
headers: this.getHeaders()
|
|
618
|
-
});
|
|
745
|
+
const res = await this.executeRequest(url);
|
|
619
746
|
if (!res.ok) {
|
|
620
747
|
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
621
748
|
throw new Error(err.message || `Failed to fetch relations for ${this.typeName}/${id}`);
|
|
@@ -653,9 +780,8 @@ var EntityQueryBuilder = class {
|
|
|
653
780
|
};
|
|
654
781
|
}
|
|
655
782
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}/relations`;
|
|
656
|
-
const res = await this.
|
|
783
|
+
const res = await this.executeRequest(url, {
|
|
657
784
|
method: "POST",
|
|
658
|
-
headers: this.getHeaders(),
|
|
659
785
|
body: JSON.stringify(payload)
|
|
660
786
|
});
|
|
661
787
|
if (!res.ok) {
|
|
@@ -669,9 +795,8 @@ var EntityQueryBuilder = class {
|
|
|
669
795
|
*/
|
|
670
796
|
async setLinks(id, payload) {
|
|
671
797
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}/relations`;
|
|
672
|
-
const res = await this.
|
|
798
|
+
const res = await this.executeRequest(url, {
|
|
673
799
|
method: "PUT",
|
|
674
|
-
headers: this.getHeaders(),
|
|
675
800
|
body: JSON.stringify(payload)
|
|
676
801
|
});
|
|
677
802
|
if (!res.ok) {
|
|
@@ -685,9 +810,8 @@ var EntityQueryBuilder = class {
|
|
|
685
810
|
*/
|
|
686
811
|
async unlink(id, relationName, targetId, targetTypeName) {
|
|
687
812
|
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()
|
|
813
|
+
const res = await this.executeRequest(url, {
|
|
814
|
+
method: "DELETE"
|
|
691
815
|
});
|
|
692
816
|
if (!res.ok) {
|
|
693
817
|
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
@@ -700,9 +824,8 @@ var EntityQueryBuilder = class {
|
|
|
700
824
|
*/
|
|
701
825
|
async action(actionName, payload) {
|
|
702
826
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/actions/${actionName}`;
|
|
703
|
-
const res = await this.
|
|
827
|
+
const res = await this.executeRequest(url, {
|
|
704
828
|
method: "POST",
|
|
705
|
-
headers: this.getHeaders(),
|
|
706
829
|
body: payload ? JSON.stringify(payload) : void 0
|
|
707
830
|
});
|
|
708
831
|
if (!res.ok) {
|
|
@@ -716,9 +839,8 @@ var EntityQueryBuilder = class {
|
|
|
716
839
|
*/
|
|
717
840
|
async instanceAction(id, actionName, payload) {
|
|
718
841
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}/actions/${actionName}`;
|
|
719
|
-
const res = await this.
|
|
842
|
+
const res = await this.executeRequest(url, {
|
|
720
843
|
method: "POST",
|
|
721
|
-
headers: this.getHeaders(),
|
|
722
844
|
body: payload ? JSON.stringify(payload) : void 0
|
|
723
845
|
});
|
|
724
846
|
if (!res.ok) {
|
|
@@ -733,9 +855,8 @@ var EntityQueryBuilder = class {
|
|
|
733
855
|
async exportCsv() {
|
|
734
856
|
const qs = this.queryParams.toString();
|
|
735
857
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/export/csv${qs ? `?${qs}` : ""}`;
|
|
736
|
-
const res = await this.
|
|
858
|
+
const res = await this.executeRequest(url, {
|
|
737
859
|
headers: {
|
|
738
|
-
...this.getHeaders(),
|
|
739
860
|
"Accept": "text/csv"
|
|
740
861
|
}
|
|
741
862
|
});
|
|
@@ -753,11 +874,13 @@ var SchemaClient = class {
|
|
|
753
874
|
tenantId;
|
|
754
875
|
getToken;
|
|
755
876
|
fetchFn;
|
|
877
|
+
fetchWithAuth;
|
|
756
878
|
constructor(options) {
|
|
757
879
|
this.gatewayUrl = options.gatewayUrl.replace(/\/+$/, "");
|
|
758
880
|
this.tenantId = options.tenantId;
|
|
759
|
-
this.getToken = options.getToken;
|
|
881
|
+
this.getToken = options.getToken || (() => null);
|
|
760
882
|
this.fetchFn = options.fetchFn || (typeof fetch !== "undefined" ? fetch : globalThis.fetch);
|
|
883
|
+
this.fetchWithAuth = options.fetchWithAuth;
|
|
761
884
|
}
|
|
762
885
|
getHeaders() {
|
|
763
886
|
const headers = {
|
|
@@ -775,11 +898,11 @@ var SchemaClient = class {
|
|
|
775
898
|
*/
|
|
776
899
|
async define(definition) {
|
|
777
900
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/definition`;
|
|
778
|
-
const
|
|
901
|
+
const init = {
|
|
779
902
|
method: "POST",
|
|
780
|
-
headers: this.getHeaders(),
|
|
781
903
|
body: JSON.stringify(definition)
|
|
782
|
-
}
|
|
904
|
+
};
|
|
905
|
+
const res = this.fetchWithAuth ? await this.fetchWithAuth(url, init) : await this.fetchFn(url, { ...init, headers: this.getHeaders() });
|
|
783
906
|
if (!res.ok) {
|
|
784
907
|
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
785
908
|
throw new Error(err.message || `Failed to define schema for ${definition.typeName}`);
|
|
@@ -833,13 +956,17 @@ var Zone4CodeClient = class {
|
|
|
833
956
|
storage: this.storage,
|
|
834
957
|
storageKey: config.storageKey,
|
|
835
958
|
initialToken: config.token,
|
|
836
|
-
fetchFn: this.fetchFn
|
|
959
|
+
fetchFn: this.fetchFn,
|
|
960
|
+
autoRefresh: config.autoRefresh,
|
|
961
|
+
tokenExpiryBuffer: config.tokenExpiryBuffer,
|
|
962
|
+
onSessionExpired: config.onSessionExpired
|
|
837
963
|
});
|
|
838
964
|
this.schema = new SchemaClient({
|
|
839
965
|
gatewayUrl: this.gatewayUrl,
|
|
840
966
|
tenantId: this.tenantId,
|
|
841
967
|
getToken: () => this.auth.getToken(),
|
|
842
|
-
fetchFn: this.fetchFn
|
|
968
|
+
fetchFn: this.fetchFn,
|
|
969
|
+
fetchWithAuth: (url, init) => this.auth.fetchWithAuth(url, init)
|
|
843
970
|
});
|
|
844
971
|
}
|
|
845
972
|
/**
|
|
@@ -851,7 +978,8 @@ var Zone4CodeClient = class {
|
|
|
851
978
|
tenantId: this.tenantId,
|
|
852
979
|
typeName,
|
|
853
980
|
getToken: () => this.auth.getToken(),
|
|
854
|
-
fetchFn: this.fetchFn
|
|
981
|
+
fetchFn: this.fetchFn,
|
|
982
|
+
fetchWithAuth: (url, init) => this.auth.fetchWithAuth(url, init)
|
|
855
983
|
});
|
|
856
984
|
}
|
|
857
985
|
/**
|
|
@@ -860,21 +988,17 @@ var Zone4CodeClient = class {
|
|
|
860
988
|
entities(typeName) {
|
|
861
989
|
return this.from(typeName);
|
|
862
990
|
}
|
|
991
|
+
/**
|
|
992
|
+
* Register a listener called whenever the session expires completely
|
|
993
|
+
*/
|
|
994
|
+
onSessionExpired(callback) {
|
|
995
|
+
return this.auth.onSessionExpired(callback);
|
|
996
|
+
}
|
|
863
997
|
/**
|
|
864
998
|
* Get current authenticated user profile and platform wallet
|
|
865
999
|
*/
|
|
866
1000
|
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
|
-
});
|
|
1001
|
+
const res = await this.auth.fetchWithAuth(`${this.gatewayUrl}/generic/${this.tenantId}/me`);
|
|
878
1002
|
if (!res.ok) {
|
|
879
1003
|
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
880
1004
|
throw new Error(err.message || `Failed to fetch profile & wallet (HTTP ${res.status})`);
|