zone4code-sdk 1.0.4 → 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 +241 -94
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +72 -9
- package/dist/index.d.ts +72 -9
- package/dist/index.js +241 -94
- 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}`);
|
|
@@ -624,12 +748,37 @@ var EntityQueryBuilder = class {
|
|
|
624
748
|
}
|
|
625
749
|
/**
|
|
626
750
|
* Link this record to another entity record
|
|
751
|
+
* Supports:
|
|
752
|
+
* .link(id, 'in_category', categoryId)
|
|
753
|
+
* .link(id, 'in_category', 'category', categoryId)
|
|
754
|
+
* .link(id, { relationName: 'in_category', targetId: categoryId, targetTypeName: 'category' })
|
|
627
755
|
*/
|
|
628
|
-
async link(id,
|
|
756
|
+
async link(id, relationOrPayload, targetTypeNameOrId, maybeTargetId) {
|
|
757
|
+
let payload;
|
|
758
|
+
if (typeof relationOrPayload === "string") {
|
|
759
|
+
if (maybeTargetId) {
|
|
760
|
+
payload = {
|
|
761
|
+
relationName: relationOrPayload,
|
|
762
|
+
targetTypeName: targetTypeNameOrId,
|
|
763
|
+
targetId: maybeTargetId
|
|
764
|
+
};
|
|
765
|
+
} else {
|
|
766
|
+
payload = {
|
|
767
|
+
relationName: relationOrPayload,
|
|
768
|
+
targetId: targetTypeNameOrId
|
|
769
|
+
};
|
|
770
|
+
}
|
|
771
|
+
} else {
|
|
772
|
+
const p = relationOrPayload;
|
|
773
|
+
payload = {
|
|
774
|
+
relationName: p.relationName || p.relation_name || "",
|
|
775
|
+
targetId: p.targetId || p.target_id || p.child_id || p.childId || "",
|
|
776
|
+
targetTypeName: p.targetTypeName || p.target_type_name
|
|
777
|
+
};
|
|
778
|
+
}
|
|
629
779
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}/relations`;
|
|
630
|
-
const res = await this.
|
|
780
|
+
const res = await this.executeRequest(url, {
|
|
631
781
|
method: "POST",
|
|
632
|
-
headers: this.getHeaders(),
|
|
633
782
|
body: JSON.stringify(payload)
|
|
634
783
|
});
|
|
635
784
|
if (!res.ok) {
|
|
@@ -643,9 +792,8 @@ var EntityQueryBuilder = class {
|
|
|
643
792
|
*/
|
|
644
793
|
async setLinks(id, payload) {
|
|
645
794
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}/relations`;
|
|
646
|
-
const res = await this.
|
|
795
|
+
const res = await this.executeRequest(url, {
|
|
647
796
|
method: "PUT",
|
|
648
|
-
headers: this.getHeaders(),
|
|
649
797
|
body: JSON.stringify(payload)
|
|
650
798
|
});
|
|
651
799
|
if (!res.ok) {
|
|
@@ -659,9 +807,8 @@ var EntityQueryBuilder = class {
|
|
|
659
807
|
*/
|
|
660
808
|
async unlink(id, relationName, targetId, targetTypeName) {
|
|
661
809
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}/relations/${encodeURIComponent(relationName)}/${encodeURIComponent(targetId)}?targetTypeName=${encodeURIComponent(targetTypeName)}`;
|
|
662
|
-
const res = await this.
|
|
663
|
-
method: "DELETE"
|
|
664
|
-
headers: this.getHeaders()
|
|
810
|
+
const res = await this.executeRequest(url, {
|
|
811
|
+
method: "DELETE"
|
|
665
812
|
});
|
|
666
813
|
if (!res.ok) {
|
|
667
814
|
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
@@ -674,9 +821,8 @@ var EntityQueryBuilder = class {
|
|
|
674
821
|
*/
|
|
675
822
|
async action(actionName, payload) {
|
|
676
823
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/actions/${actionName}`;
|
|
677
|
-
const res = await this.
|
|
824
|
+
const res = await this.executeRequest(url, {
|
|
678
825
|
method: "POST",
|
|
679
|
-
headers: this.getHeaders(),
|
|
680
826
|
body: payload ? JSON.stringify(payload) : void 0
|
|
681
827
|
});
|
|
682
828
|
if (!res.ok) {
|
|
@@ -690,9 +836,8 @@ var EntityQueryBuilder = class {
|
|
|
690
836
|
*/
|
|
691
837
|
async instanceAction(id, actionName, payload) {
|
|
692
838
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}/actions/${actionName}`;
|
|
693
|
-
const res = await this.
|
|
839
|
+
const res = await this.executeRequest(url, {
|
|
694
840
|
method: "POST",
|
|
695
|
-
headers: this.getHeaders(),
|
|
696
841
|
body: payload ? JSON.stringify(payload) : void 0
|
|
697
842
|
});
|
|
698
843
|
if (!res.ok) {
|
|
@@ -707,9 +852,8 @@ var EntityQueryBuilder = class {
|
|
|
707
852
|
async exportCsv() {
|
|
708
853
|
const qs = this.queryParams.toString();
|
|
709
854
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/export/csv${qs ? `?${qs}` : ""}`;
|
|
710
|
-
const res = await this.
|
|
855
|
+
const res = await this.executeRequest(url, {
|
|
711
856
|
headers: {
|
|
712
|
-
...this.getHeaders(),
|
|
713
857
|
"Accept": "text/csv"
|
|
714
858
|
}
|
|
715
859
|
});
|
|
@@ -727,11 +871,13 @@ var SchemaClient = class {
|
|
|
727
871
|
tenantId;
|
|
728
872
|
getToken;
|
|
729
873
|
fetchFn;
|
|
874
|
+
fetchWithAuth;
|
|
730
875
|
constructor(options) {
|
|
731
876
|
this.gatewayUrl = options.gatewayUrl.replace(/\/+$/, "");
|
|
732
877
|
this.tenantId = options.tenantId;
|
|
733
|
-
this.getToken = options.getToken;
|
|
878
|
+
this.getToken = options.getToken || (() => null);
|
|
734
879
|
this.fetchFn = options.fetchFn || (typeof fetch !== "undefined" ? fetch : globalThis.fetch);
|
|
880
|
+
this.fetchWithAuth = options.fetchWithAuth;
|
|
735
881
|
}
|
|
736
882
|
getHeaders() {
|
|
737
883
|
const headers = {
|
|
@@ -749,11 +895,11 @@ var SchemaClient = class {
|
|
|
749
895
|
*/
|
|
750
896
|
async define(definition) {
|
|
751
897
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/definition`;
|
|
752
|
-
const
|
|
898
|
+
const init = {
|
|
753
899
|
method: "POST",
|
|
754
|
-
headers: this.getHeaders(),
|
|
755
900
|
body: JSON.stringify(definition)
|
|
756
|
-
}
|
|
901
|
+
};
|
|
902
|
+
const res = this.fetchWithAuth ? await this.fetchWithAuth(url, init) : await this.fetchFn(url, { ...init, headers: this.getHeaders() });
|
|
757
903
|
if (!res.ok) {
|
|
758
904
|
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
759
905
|
throw new Error(err.message || `Failed to define schema for ${definition.typeName}`);
|
|
@@ -807,13 +953,17 @@ var Zone4CodeClient = class {
|
|
|
807
953
|
storage: this.storage,
|
|
808
954
|
storageKey: config.storageKey,
|
|
809
955
|
initialToken: config.token,
|
|
810
|
-
fetchFn: this.fetchFn
|
|
956
|
+
fetchFn: this.fetchFn,
|
|
957
|
+
autoRefresh: config.autoRefresh,
|
|
958
|
+
tokenExpiryBuffer: config.tokenExpiryBuffer,
|
|
959
|
+
onSessionExpired: config.onSessionExpired
|
|
811
960
|
});
|
|
812
961
|
this.schema = new SchemaClient({
|
|
813
962
|
gatewayUrl: this.gatewayUrl,
|
|
814
963
|
tenantId: this.tenantId,
|
|
815
964
|
getToken: () => this.auth.getToken(),
|
|
816
|
-
fetchFn: this.fetchFn
|
|
965
|
+
fetchFn: this.fetchFn,
|
|
966
|
+
fetchWithAuth: (url, init) => this.auth.fetchWithAuth(url, init)
|
|
817
967
|
});
|
|
818
968
|
}
|
|
819
969
|
/**
|
|
@@ -825,7 +975,8 @@ var Zone4CodeClient = class {
|
|
|
825
975
|
tenantId: this.tenantId,
|
|
826
976
|
typeName,
|
|
827
977
|
getToken: () => this.auth.getToken(),
|
|
828
|
-
fetchFn: this.fetchFn
|
|
978
|
+
fetchFn: this.fetchFn,
|
|
979
|
+
fetchWithAuth: (url, init) => this.auth.fetchWithAuth(url, init)
|
|
829
980
|
});
|
|
830
981
|
}
|
|
831
982
|
/**
|
|
@@ -834,21 +985,17 @@ var Zone4CodeClient = class {
|
|
|
834
985
|
entities(typeName) {
|
|
835
986
|
return this.from(typeName);
|
|
836
987
|
}
|
|
988
|
+
/**
|
|
989
|
+
* Register a listener called whenever the session expires completely
|
|
990
|
+
*/
|
|
991
|
+
onSessionExpired(callback) {
|
|
992
|
+
return this.auth.onSessionExpired(callback);
|
|
993
|
+
}
|
|
837
994
|
/**
|
|
838
995
|
* Get current authenticated user profile and platform wallet
|
|
839
996
|
*/
|
|
840
997
|
async getMe() {
|
|
841
|
-
const
|
|
842
|
-
if (!token) {
|
|
843
|
-
throw new Error("Not authenticated: please call login() or setToken() first");
|
|
844
|
-
}
|
|
845
|
-
const res = await this.fetchFn(`${this.gatewayUrl}/generic/${this.tenantId}/me`, {
|
|
846
|
-
headers: {
|
|
847
|
-
"Content-Type": "application/json",
|
|
848
|
-
"Authorization": `Bearer ${token}`,
|
|
849
|
-
"x-tenant-id": this.tenantId
|
|
850
|
-
}
|
|
851
|
-
});
|
|
998
|
+
const res = await this.auth.fetchWithAuth(`${this.gatewayUrl}/generic/${this.tenantId}/me`);
|
|
852
999
|
if (!res.ok) {
|
|
853
1000
|
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
854
1001
|
throw new Error(err.message || `Failed to fetch profile & wallet (HTTP ${res.status})`);
|