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.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,91 @@ 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 (token && !headers["Authorization"] && !headers["authorization"]) {
|
|
207
|
+
headers["Authorization"] = `Bearer ${token}`;
|
|
208
|
+
}
|
|
209
|
+
let res = await this.fetchFn(url, { ...init, headers });
|
|
210
|
+
if (res.status === 401 && this.autoRefresh && this.getRefreshToken()) {
|
|
211
|
+
try {
|
|
212
|
+
const refreshed = await this.refreshToken();
|
|
213
|
+
if (refreshed?.token) {
|
|
214
|
+
headers["Authorization"] = `Bearer ${refreshed.token}`;
|
|
215
|
+
res = await this.fetchFn(url, { ...init, headers });
|
|
216
|
+
}
|
|
217
|
+
} catch {
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return res;
|
|
221
|
+
}
|
|
108
222
|
/**
|
|
109
223
|
* Synchronously parse and return current authenticated user from JWT token
|
|
110
224
|
*/
|
|
@@ -260,36 +374,48 @@ var AuthClient = class _AuthClient {
|
|
|
260
374
|
async refreshToken(refreshToken) {
|
|
261
375
|
const tokenToUse = refreshToken || this.getRefreshToken();
|
|
262
376
|
if (!tokenToUse) {
|
|
377
|
+
this.notifySessionExpired();
|
|
263
378
|
throw new Error("No refresh token available");
|
|
264
379
|
}
|
|
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}`);
|
|
277
|
-
}
|
|
278
|
-
const json = await res.json();
|
|
279
|
-
const token = json.token || json.access_token;
|
|
280
|
-
if (token) {
|
|
281
|
-
this.setToken(token);
|
|
282
|
-
}
|
|
283
|
-
if (json.refresh_token) {
|
|
284
|
-
this.setRefreshToken(json.refresh_token);
|
|
380
|
+
if (this.activeRefreshPromise) {
|
|
381
|
+
return this.activeRefreshPromise;
|
|
285
382
|
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
383
|
+
this.activeRefreshPromise = (async () => {
|
|
384
|
+
try {
|
|
385
|
+
const url = `${this.gatewayUrl}/auth/${this.tenantId}/refresh`;
|
|
386
|
+
const res = await this.fetchFn(url, {
|
|
387
|
+
method: "POST",
|
|
388
|
+
headers: {
|
|
389
|
+
"Content-Type": "application/json",
|
|
390
|
+
"x-tenant-id": this.tenantId
|
|
391
|
+
},
|
|
392
|
+
body: JSON.stringify({ refresh_token: tokenToUse })
|
|
393
|
+
});
|
|
394
|
+
if (!res.ok) {
|
|
395
|
+
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
396
|
+
this.notifySessionExpired();
|
|
397
|
+
throw new Error(err.message || err.error || `Token refresh failed with HTTP ${res.status}`);
|
|
398
|
+
}
|
|
399
|
+
const json = await res.json();
|
|
400
|
+
const token = json.token || json.access_token;
|
|
401
|
+
if (token) {
|
|
402
|
+
this.setToken(token);
|
|
403
|
+
}
|
|
404
|
+
if (json.refresh_token) {
|
|
405
|
+
this.setRefreshToken(json.refresh_token);
|
|
406
|
+
}
|
|
407
|
+
const user = this.getUser() || json.user;
|
|
408
|
+
return {
|
|
409
|
+
token: token || "",
|
|
410
|
+
refreshToken: json.refresh_token,
|
|
411
|
+
user,
|
|
412
|
+
...json
|
|
413
|
+
};
|
|
414
|
+
} finally {
|
|
415
|
+
this.activeRefreshPromise = null;
|
|
416
|
+
}
|
|
417
|
+
})();
|
|
418
|
+
return this.activeRefreshPromise;
|
|
293
419
|
}
|
|
294
420
|
/**
|
|
295
421
|
* Request password reset email
|
|
@@ -418,13 +544,15 @@ var EntityQueryBuilder = class {
|
|
|
418
544
|
typeName;
|
|
419
545
|
getToken;
|
|
420
546
|
fetchFn;
|
|
547
|
+
fetchWithAuth;
|
|
421
548
|
queryParams = new URLSearchParams();
|
|
422
549
|
constructor(options) {
|
|
423
550
|
this.gatewayUrl = options.gatewayUrl.replace(/\/+$/, "");
|
|
424
551
|
this.tenantId = options.tenantId;
|
|
425
552
|
this.typeName = options.typeName;
|
|
426
|
-
this.getToken = options.getToken;
|
|
553
|
+
this.getToken = options.getToken || (() => null);
|
|
427
554
|
this.fetchFn = options.fetchFn || (typeof fetch !== "undefined" ? fetch : globalThis.fetch);
|
|
555
|
+
this.fetchWithAuth = options.fetchWithAuth;
|
|
428
556
|
}
|
|
429
557
|
getHeaders() {
|
|
430
558
|
const headers = {
|
|
@@ -437,6 +565,13 @@ var EntityQueryBuilder = class {
|
|
|
437
565
|
}
|
|
438
566
|
return headers;
|
|
439
567
|
}
|
|
568
|
+
async executeRequest(url, init = {}) {
|
|
569
|
+
if (this.fetchWithAuth) {
|
|
570
|
+
return this.fetchWithAuth(url, init);
|
|
571
|
+
}
|
|
572
|
+
const headers = { ...this.getHeaders(), ...init.headers || {} };
|
|
573
|
+
return this.fetchFn(url, { ...init, headers });
|
|
574
|
+
}
|
|
440
575
|
// ----------------------------------------------------
|
|
441
576
|
// Filter Operators
|
|
442
577
|
// ----------------------------------------------------
|
|
@@ -545,9 +680,7 @@ var EntityQueryBuilder = class {
|
|
|
545
680
|
async list() {
|
|
546
681
|
const qs = this.queryParams.toString();
|
|
547
682
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}${qs ? `?${qs}` : ""}`;
|
|
548
|
-
const res = await this.
|
|
549
|
-
headers: this.getHeaders()
|
|
550
|
-
});
|
|
683
|
+
const res = await this.executeRequest(url);
|
|
551
684
|
if (!res.ok) {
|
|
552
685
|
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
553
686
|
throw new Error(err.message || `Query failed with HTTP ${res.status}`);
|
|
@@ -570,9 +703,7 @@ var EntityQueryBuilder = class {
|
|
|
570
703
|
}
|
|
571
704
|
const qs = params.toString();
|
|
572
705
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}${qs ? `?${qs}` : ""}`;
|
|
573
|
-
const res = await this.
|
|
574
|
-
headers: this.getHeaders()
|
|
575
|
-
});
|
|
706
|
+
const res = await this.executeRequest(url);
|
|
576
707
|
if (!res.ok) {
|
|
577
708
|
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
578
709
|
throw new Error(err.message || `Failed to fetch ${this.typeName}/${id}`);
|
|
@@ -584,9 +715,8 @@ var EntityQueryBuilder = class {
|
|
|
584
715
|
*/
|
|
585
716
|
async create(data) {
|
|
586
717
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}`;
|
|
587
|
-
const res = await this.
|
|
718
|
+
const res = await this.executeRequest(url, {
|
|
588
719
|
method: "POST",
|
|
589
|
-
headers: this.getHeaders(),
|
|
590
720
|
body: JSON.stringify(data)
|
|
591
721
|
});
|
|
592
722
|
if (!res.ok) {
|
|
@@ -600,9 +730,8 @@ var EntityQueryBuilder = class {
|
|
|
600
730
|
*/
|
|
601
731
|
async update(id, partialData) {
|
|
602
732
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}`;
|
|
603
|
-
const res = await this.
|
|
733
|
+
const res = await this.executeRequest(url, {
|
|
604
734
|
method: "PATCH",
|
|
605
|
-
headers: this.getHeaders(),
|
|
606
735
|
body: JSON.stringify(partialData)
|
|
607
736
|
});
|
|
608
737
|
if (!res.ok) {
|
|
@@ -616,9 +745,8 @@ var EntityQueryBuilder = class {
|
|
|
616
745
|
*/
|
|
617
746
|
async delete(id) {
|
|
618
747
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}`;
|
|
619
|
-
const res = await this.
|
|
620
|
-
method: "DELETE"
|
|
621
|
-
headers: this.getHeaders()
|
|
748
|
+
const res = await this.executeRequest(url, {
|
|
749
|
+
method: "DELETE"
|
|
622
750
|
});
|
|
623
751
|
if (!res.ok) {
|
|
624
752
|
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
@@ -631,9 +759,7 @@ var EntityQueryBuilder = class {
|
|
|
631
759
|
*/
|
|
632
760
|
async revisions(id) {
|
|
633
761
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}/revisions`;
|
|
634
|
-
const res = await this.
|
|
635
|
-
headers: this.getHeaders()
|
|
636
|
-
});
|
|
762
|
+
const res = await this.executeRequest(url);
|
|
637
763
|
if (!res.ok) {
|
|
638
764
|
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
639
765
|
throw new Error(err.message || `Failed to fetch revisions for ${this.typeName}/${id}`);
|
|
@@ -646,9 +772,7 @@ var EntityQueryBuilder = class {
|
|
|
646
772
|
async getRelations(id, lang) {
|
|
647
773
|
const qs = lang ? `?lang=${encodeURIComponent(lang)}` : "";
|
|
648
774
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}/relations${qs}`;
|
|
649
|
-
const res = await this.
|
|
650
|
-
headers: this.getHeaders()
|
|
651
|
-
});
|
|
775
|
+
const res = await this.executeRequest(url);
|
|
652
776
|
if (!res.ok) {
|
|
653
777
|
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
654
778
|
throw new Error(err.message || `Failed to fetch relations for ${this.typeName}/${id}`);
|
|
@@ -657,12 +781,37 @@ var EntityQueryBuilder = class {
|
|
|
657
781
|
}
|
|
658
782
|
/**
|
|
659
783
|
* Link this record to another entity record
|
|
784
|
+
* Supports:
|
|
785
|
+
* .link(id, 'in_category', categoryId)
|
|
786
|
+
* .link(id, 'in_category', 'category', categoryId)
|
|
787
|
+
* .link(id, { relationName: 'in_category', targetId: categoryId, targetTypeName: 'category' })
|
|
660
788
|
*/
|
|
661
|
-
async link(id,
|
|
789
|
+
async link(id, relationOrPayload, targetTypeNameOrId, maybeTargetId) {
|
|
790
|
+
let payload;
|
|
791
|
+
if (typeof relationOrPayload === "string") {
|
|
792
|
+
if (maybeTargetId) {
|
|
793
|
+
payload = {
|
|
794
|
+
relationName: relationOrPayload,
|
|
795
|
+
targetTypeName: targetTypeNameOrId,
|
|
796
|
+
targetId: maybeTargetId
|
|
797
|
+
};
|
|
798
|
+
} else {
|
|
799
|
+
payload = {
|
|
800
|
+
relationName: relationOrPayload,
|
|
801
|
+
targetId: targetTypeNameOrId
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
} else {
|
|
805
|
+
const p = relationOrPayload;
|
|
806
|
+
payload = {
|
|
807
|
+
relationName: p.relationName || p.relation_name || "",
|
|
808
|
+
targetId: p.targetId || p.target_id || p.child_id || p.childId || "",
|
|
809
|
+
targetTypeName: p.targetTypeName || p.target_type_name
|
|
810
|
+
};
|
|
811
|
+
}
|
|
662
812
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}/relations`;
|
|
663
|
-
const res = await this.
|
|
813
|
+
const res = await this.executeRequest(url, {
|
|
664
814
|
method: "POST",
|
|
665
|
-
headers: this.getHeaders(),
|
|
666
815
|
body: JSON.stringify(payload)
|
|
667
816
|
});
|
|
668
817
|
if (!res.ok) {
|
|
@@ -676,9 +825,8 @@ var EntityQueryBuilder = class {
|
|
|
676
825
|
*/
|
|
677
826
|
async setLinks(id, payload) {
|
|
678
827
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}/relations`;
|
|
679
|
-
const res = await this.
|
|
828
|
+
const res = await this.executeRequest(url, {
|
|
680
829
|
method: "PUT",
|
|
681
|
-
headers: this.getHeaders(),
|
|
682
830
|
body: JSON.stringify(payload)
|
|
683
831
|
});
|
|
684
832
|
if (!res.ok) {
|
|
@@ -692,9 +840,8 @@ var EntityQueryBuilder = class {
|
|
|
692
840
|
*/
|
|
693
841
|
async unlink(id, relationName, targetId, targetTypeName) {
|
|
694
842
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}/relations/${encodeURIComponent(relationName)}/${encodeURIComponent(targetId)}?targetTypeName=${encodeURIComponent(targetTypeName)}`;
|
|
695
|
-
const res = await this.
|
|
696
|
-
method: "DELETE"
|
|
697
|
-
headers: this.getHeaders()
|
|
843
|
+
const res = await this.executeRequest(url, {
|
|
844
|
+
method: "DELETE"
|
|
698
845
|
});
|
|
699
846
|
if (!res.ok) {
|
|
700
847
|
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
@@ -707,9 +854,8 @@ var EntityQueryBuilder = class {
|
|
|
707
854
|
*/
|
|
708
855
|
async action(actionName, payload) {
|
|
709
856
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/actions/${actionName}`;
|
|
710
|
-
const res = await this.
|
|
857
|
+
const res = await this.executeRequest(url, {
|
|
711
858
|
method: "POST",
|
|
712
|
-
headers: this.getHeaders(),
|
|
713
859
|
body: payload ? JSON.stringify(payload) : void 0
|
|
714
860
|
});
|
|
715
861
|
if (!res.ok) {
|
|
@@ -723,9 +869,8 @@ var EntityQueryBuilder = class {
|
|
|
723
869
|
*/
|
|
724
870
|
async instanceAction(id, actionName, payload) {
|
|
725
871
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}/actions/${actionName}`;
|
|
726
|
-
const res = await this.
|
|
872
|
+
const res = await this.executeRequest(url, {
|
|
727
873
|
method: "POST",
|
|
728
|
-
headers: this.getHeaders(),
|
|
729
874
|
body: payload ? JSON.stringify(payload) : void 0
|
|
730
875
|
});
|
|
731
876
|
if (!res.ok) {
|
|
@@ -740,9 +885,8 @@ var EntityQueryBuilder = class {
|
|
|
740
885
|
async exportCsv() {
|
|
741
886
|
const qs = this.queryParams.toString();
|
|
742
887
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/export/csv${qs ? `?${qs}` : ""}`;
|
|
743
|
-
const res = await this.
|
|
888
|
+
const res = await this.executeRequest(url, {
|
|
744
889
|
headers: {
|
|
745
|
-
...this.getHeaders(),
|
|
746
890
|
"Accept": "text/csv"
|
|
747
891
|
}
|
|
748
892
|
});
|
|
@@ -760,11 +904,13 @@ var SchemaClient = class {
|
|
|
760
904
|
tenantId;
|
|
761
905
|
getToken;
|
|
762
906
|
fetchFn;
|
|
907
|
+
fetchWithAuth;
|
|
763
908
|
constructor(options) {
|
|
764
909
|
this.gatewayUrl = options.gatewayUrl.replace(/\/+$/, "");
|
|
765
910
|
this.tenantId = options.tenantId;
|
|
766
|
-
this.getToken = options.getToken;
|
|
911
|
+
this.getToken = options.getToken || (() => null);
|
|
767
912
|
this.fetchFn = options.fetchFn || (typeof fetch !== "undefined" ? fetch : globalThis.fetch);
|
|
913
|
+
this.fetchWithAuth = options.fetchWithAuth;
|
|
768
914
|
}
|
|
769
915
|
getHeaders() {
|
|
770
916
|
const headers = {
|
|
@@ -782,11 +928,11 @@ var SchemaClient = class {
|
|
|
782
928
|
*/
|
|
783
929
|
async define(definition) {
|
|
784
930
|
const url = `${this.gatewayUrl}/generic/${this.tenantId}/definition`;
|
|
785
|
-
const
|
|
931
|
+
const init = {
|
|
786
932
|
method: "POST",
|
|
787
|
-
headers: this.getHeaders(),
|
|
788
933
|
body: JSON.stringify(definition)
|
|
789
|
-
}
|
|
934
|
+
};
|
|
935
|
+
const res = this.fetchWithAuth ? await this.fetchWithAuth(url, init) : await this.fetchFn(url, { ...init, headers: this.getHeaders() });
|
|
790
936
|
if (!res.ok) {
|
|
791
937
|
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
792
938
|
throw new Error(err.message || `Failed to define schema for ${definition.typeName}`);
|
|
@@ -840,13 +986,17 @@ var Zone4CodeClient = class {
|
|
|
840
986
|
storage: this.storage,
|
|
841
987
|
storageKey: config.storageKey,
|
|
842
988
|
initialToken: config.token,
|
|
843
|
-
fetchFn: this.fetchFn
|
|
989
|
+
fetchFn: this.fetchFn,
|
|
990
|
+
autoRefresh: config.autoRefresh,
|
|
991
|
+
tokenExpiryBuffer: config.tokenExpiryBuffer,
|
|
992
|
+
onSessionExpired: config.onSessionExpired
|
|
844
993
|
});
|
|
845
994
|
this.schema = new SchemaClient({
|
|
846
995
|
gatewayUrl: this.gatewayUrl,
|
|
847
996
|
tenantId: this.tenantId,
|
|
848
997
|
getToken: () => this.auth.getToken(),
|
|
849
|
-
fetchFn: this.fetchFn
|
|
998
|
+
fetchFn: this.fetchFn,
|
|
999
|
+
fetchWithAuth: (url, init) => this.auth.fetchWithAuth(url, init)
|
|
850
1000
|
});
|
|
851
1001
|
}
|
|
852
1002
|
/**
|
|
@@ -858,7 +1008,8 @@ var Zone4CodeClient = class {
|
|
|
858
1008
|
tenantId: this.tenantId,
|
|
859
1009
|
typeName,
|
|
860
1010
|
getToken: () => this.auth.getToken(),
|
|
861
|
-
fetchFn: this.fetchFn
|
|
1011
|
+
fetchFn: this.fetchFn,
|
|
1012
|
+
fetchWithAuth: (url, init) => this.auth.fetchWithAuth(url, init)
|
|
862
1013
|
});
|
|
863
1014
|
}
|
|
864
1015
|
/**
|
|
@@ -867,21 +1018,17 @@ var Zone4CodeClient = class {
|
|
|
867
1018
|
entities(typeName) {
|
|
868
1019
|
return this.from(typeName);
|
|
869
1020
|
}
|
|
1021
|
+
/**
|
|
1022
|
+
* Register a listener called whenever the session expires completely
|
|
1023
|
+
*/
|
|
1024
|
+
onSessionExpired(callback) {
|
|
1025
|
+
return this.auth.onSessionExpired(callback);
|
|
1026
|
+
}
|
|
870
1027
|
/**
|
|
871
1028
|
* Get current authenticated user profile and platform wallet
|
|
872
1029
|
*/
|
|
873
1030
|
async getMe() {
|
|
874
|
-
const
|
|
875
|
-
if (!token) {
|
|
876
|
-
throw new Error("Not authenticated: please call login() or setToken() first");
|
|
877
|
-
}
|
|
878
|
-
const res = await this.fetchFn(`${this.gatewayUrl}/generic/${this.tenantId}/me`, {
|
|
879
|
-
headers: {
|
|
880
|
-
"Content-Type": "application/json",
|
|
881
|
-
"Authorization": `Bearer ${token}`,
|
|
882
|
-
"x-tenant-id": this.tenantId
|
|
883
|
-
}
|
|
884
|
-
});
|
|
1031
|
+
const res = await this.auth.fetchWithAuth(`${this.gatewayUrl}/generic/${this.tenantId}/me`);
|
|
885
1032
|
if (!res.ok) {
|
|
886
1033
|
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
887
1034
|
throw new Error(err.message || `Failed to fetch profile & wallet (HTTP ${res.status})`);
|