@tenxyte/core 0.9.0 → 0.9.3

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.js CHANGED
@@ -1,12 +1,144 @@
1
+ // src/storage/memory.ts
2
+ var MemoryStorage = class {
3
+ store;
4
+ constructor() {
5
+ this.store = /* @__PURE__ */ new Map();
6
+ }
7
+ getItem(key) {
8
+ const value = this.store.get(key);
9
+ return value !== void 0 ? value : null;
10
+ }
11
+ setItem(key, value) {
12
+ this.store.set(key, value);
13
+ }
14
+ removeItem(key) {
15
+ this.store.delete(key);
16
+ }
17
+ clear() {
18
+ this.store.clear();
19
+ }
20
+ };
21
+
22
+ // src/storage/localStorage.ts
23
+ var LocalStorage = class {
24
+ fallbackMemoryStore = null;
25
+ isAvailable;
26
+ constructor() {
27
+ this.isAvailable = this.checkAvailability();
28
+ if (!this.isAvailable) {
29
+ this.fallbackMemoryStore = new MemoryStorage();
30
+ }
31
+ }
32
+ checkAvailability() {
33
+ try {
34
+ if (typeof window === "undefined" || !window.localStorage) {
35
+ return false;
36
+ }
37
+ const testKey = "__tenxyte_test__";
38
+ window.localStorage.setItem(testKey, "1");
39
+ window.localStorage.removeItem(testKey);
40
+ return true;
41
+ } catch (_e) {
42
+ return false;
43
+ }
44
+ }
45
+ getItem(key) {
46
+ if (!this.isAvailable && this.fallbackMemoryStore) {
47
+ return this.fallbackMemoryStore.getItem(key);
48
+ }
49
+ return window.localStorage.getItem(key);
50
+ }
51
+ setItem(key, value) {
52
+ if (!this.isAvailable && this.fallbackMemoryStore) {
53
+ this.fallbackMemoryStore.setItem(key, value);
54
+ return;
55
+ }
56
+ try {
57
+ window.localStorage.setItem(key, value);
58
+ } catch (_e) {
59
+ console.warn(`[Tenxyte SDK] Warning: failed to write to localStorage for key ${key}`);
60
+ }
61
+ }
62
+ removeItem(key) {
63
+ if (!this.isAvailable && this.fallbackMemoryStore) {
64
+ this.fallbackMemoryStore.removeItem(key);
65
+ return;
66
+ }
67
+ window.localStorage.removeItem(key);
68
+ }
69
+ clear() {
70
+ if (!this.isAvailable && this.fallbackMemoryStore) {
71
+ this.fallbackMemoryStore.clear();
72
+ return;
73
+ }
74
+ window.localStorage.clear();
75
+ }
76
+ };
77
+
78
+ // src/storage/cookie.ts
79
+ var CookieStorage = class {
80
+ defaultOptions;
81
+ constructor(options = {}) {
82
+ const secure = options.secure ?? true;
83
+ const sameSite = options.sameSite ?? "Lax";
84
+ this.defaultOptions = `path=/; SameSite=${sameSite}${secure ? "; Secure" : ""}`;
85
+ }
86
+ getItem(key) {
87
+ if (typeof document === "undefined") return null;
88
+ const match = document.cookie.match(new RegExp(`(^| )${key}=([^;]+)`));
89
+ return match ? decodeURIComponent(match[2]) : null;
90
+ }
91
+ setItem(key, value) {
92
+ if (typeof document === "undefined") return;
93
+ document.cookie = `${key}=${encodeURIComponent(value)}; ${this.defaultOptions}`;
94
+ }
95
+ removeItem(key) {
96
+ if (typeof document === "undefined") return;
97
+ document.cookie = `${key}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/`;
98
+ }
99
+ clear() {
100
+ this.removeItem("tx_access");
101
+ this.removeItem("tx_refresh");
102
+ }
103
+ };
104
+
105
+ // src/config.ts
106
+ var SDK_VERSION = "0.9.0";
107
+ var NOOP_LOGGER = {
108
+ debug() {
109
+ },
110
+ warn() {
111
+ },
112
+ error() {
113
+ }
114
+ };
115
+ function resolveConfig(config) {
116
+ return {
117
+ baseUrl: config.baseUrl,
118
+ headers: config.headers ?? {},
119
+ storage: config.storage ?? new MemoryStorage(),
120
+ autoRefresh: config.autoRefresh ?? true,
121
+ autoDeviceInfo: config.autoDeviceInfo ?? true,
122
+ timeoutMs: config.timeoutMs,
123
+ onSessionExpired: config.onSessionExpired,
124
+ logger: config.logger ?? NOOP_LOGGER,
125
+ logLevel: config.logLevel ?? "silent",
126
+ deviceInfoOverride: config.deviceInfoOverride,
127
+ retryConfig: config.retryConfig
128
+ };
129
+ }
130
+
1
131
  // src/http/client.ts
2
132
  var TenxyteHttpClient = class {
3
133
  baseUrl;
4
134
  defaultHeaders;
135
+ timeoutMs;
5
136
  // Interceptors
6
137
  requestInterceptors = [];
7
138
  responseInterceptors = [];
8
139
  constructor(options) {
9
140
  this.baseUrl = options.baseUrl.replace(/\/$/, "");
141
+ this.timeoutMs = options.timeoutMs;
10
142
  this.defaultHeaders = {
11
143
  "Content-Type": "application/json",
12
144
  Accept: "application/json",
@@ -25,7 +157,7 @@ var TenxyteHttpClient = class {
25
157
  */
26
158
  async request(endpoint, config = {}) {
27
159
  const urlStr = endpoint.startsWith("http") ? endpoint : `${this.baseUrl}${endpoint.startsWith("/") ? "" : "/"}${endpoint}`;
28
- let urlObj = new URL(urlStr);
160
+ const urlObj = new URL(urlStr);
29
161
  if (config.params) {
30
162
  Object.entries(config.params).forEach(([key, value]) => {
31
163
  if (value !== void 0 && value !== null) {
@@ -52,6 +184,13 @@ var TenxyteHttpClient = class {
52
184
  requestContext = await interceptor(requestContext);
53
185
  }
54
186
  const { url, ...fetchConfig } = requestContext;
187
+ let controller;
188
+ let timeoutId;
189
+ if (this.timeoutMs) {
190
+ controller = new AbortController();
191
+ fetchConfig.signal = controller.signal;
192
+ timeoutId = setTimeout(() => controller.abort(), this.timeoutMs);
193
+ }
55
194
  try {
56
195
  let response = await fetch(url, fetchConfig);
57
196
  for (const interceptor of this.responseInterceptors) {
@@ -69,6 +208,13 @@ var TenxyteHttpClient = class {
69
208
  }
70
209
  return await response.text();
71
210
  } catch (error) {
211
+ if (error?.name === "AbortError") {
212
+ throw {
213
+ error: `Request timed out after ${this.timeoutMs}ms`,
214
+ code: "TIMEOUT",
215
+ details: url
216
+ };
217
+ }
72
218
  if (error && error.code) {
73
219
  throw error;
74
220
  }
@@ -77,6 +223,10 @@ var TenxyteHttpClient = class {
77
223
  code: "NETWORK_ERROR",
78
224
  details: String(error)
79
225
  };
226
+ } finally {
227
+ if (timeoutId !== void 0) {
228
+ clearTimeout(timeoutId);
229
+ }
80
230
  }
81
231
  }
82
232
  async normalizeError(response) {
@@ -88,7 +238,7 @@ var TenxyteHttpClient = class {
88
238
  details: body.details || body,
89
239
  retry_after: response.headers.has("Retry-After") ? parseInt(response.headers.get("Retry-After"), 10) : void 0
90
240
  };
91
- } catch (e) {
241
+ } catch (_e) {
92
242
  return {
93
243
  error: `HTTP Error ${response.status}: ${response.statusText}`,
94
244
  code: `HTTP_${response.status}`
@@ -108,15 +258,248 @@ var TenxyteHttpClient = class {
108
258
  patch(endpoint, data, config) {
109
259
  return this.request(endpoint, { ...config, method: "PATCH", body: data });
110
260
  }
111
- delete(endpoint, config) {
112
- return this.request(endpoint, { ...config, method: "DELETE" });
261
+ delete(endpoint, data, config) {
262
+ return this.request(endpoint, { ...config, method: "DELETE", body: data });
113
263
  }
114
264
  };
115
265
 
266
+ // src/utils/device_info.ts
267
+ function buildDeviceInfo(customInfo = {}) {
268
+ const autoInfo = getAutoInfo();
269
+ const v = "1";
270
+ const os = customInfo.os || autoInfo.os;
271
+ const osv = customInfo.osVersion || autoInfo.osVersion;
272
+ const device = customInfo.device || autoInfo.device;
273
+ const arch = customInfo.arch || autoInfo.arch;
274
+ const app = customInfo.app || autoInfo.app;
275
+ const appv = customInfo.appVersion || autoInfo.appVersion;
276
+ const runtime = customInfo.runtime || autoInfo.runtime;
277
+ const rtv = customInfo.runtimeVersion || autoInfo.runtimeVersion;
278
+ const tz = customInfo.timezone || autoInfo.timezone;
279
+ const parts = [
280
+ `v=${v}`,
281
+ `os=${os}` + (osv ? `;osv=${osv}` : ""),
282
+ `device=${device}`,
283
+ arch ? `arch=${arch}` : "",
284
+ app ? `app=${app}${appv ? `;appv=${appv}` : ""}` : "",
285
+ `runtime=${runtime}` + (rtv ? `;rtv=${rtv}` : ""),
286
+ tz ? `tz=${tz}` : ""
287
+ ];
288
+ return parts.filter(Boolean).join("|");
289
+ }
290
+ function getAutoInfo() {
291
+ const info = {
292
+ os: "unknown",
293
+ osVersion: "",
294
+ device: "desktop",
295
+ // default
296
+ arch: "",
297
+ app: "sdk",
298
+ appVersion: "0.1.0",
299
+ runtime: "unknown",
300
+ runtimeVersion: "",
301
+ timezone: ""
302
+ };
303
+ try {
304
+ if (typeof Intl !== "undefined") {
305
+ info.timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
306
+ }
307
+ if (typeof process !== "undefined" && process.version) {
308
+ info.runtime = "node";
309
+ info.runtimeVersion = process.version;
310
+ info.os = process.platform;
311
+ info.arch = process.arch;
312
+ info.device = "server";
313
+ } else if (typeof window !== "undefined" && window.navigator) {
314
+ const ua = window.navigator.userAgent.toLowerCase();
315
+ if (ua.includes("windows")) info.os = "windows";
316
+ else if (ua.includes("mac")) info.os = "macos";
317
+ else if (ua.includes("linux")) info.os = "linux";
318
+ else if (ua.includes("android")) info.os = "android";
319
+ else if (ua.includes("ios") || ua.includes("iphone") || ua.includes("ipad")) info.os = "ios";
320
+ if (/mobi|android|touch|mini/i.test(ua)) info.device = "mobile";
321
+ if (/tablet|ipad/i.test(ua)) info.device = "tablet";
322
+ if (ua.includes("firefox")) info.runtime = "firefox";
323
+ else if (ua.includes("edg/")) info.runtime = "edge";
324
+ else if (ua.includes("chrome")) info.runtime = "chrome";
325
+ else if (ua.includes("safari")) info.runtime = "safari";
326
+ }
327
+ } catch (_e) {
328
+ }
329
+ return info;
330
+ }
331
+
332
+ // src/http/interceptors.ts
333
+ function createAuthInterceptor(storage, context) {
334
+ return async (request) => {
335
+ const token = await storage.getItem("tx_access");
336
+ const headers = { ...request.headers || {} };
337
+ if (token && !headers["Authorization"]) {
338
+ headers["Authorization"] = `Bearer ${token}`;
339
+ }
340
+ if (context.activeOrgSlug && !headers["X-Org-Slug"]) {
341
+ headers["X-Org-Slug"] = context.activeOrgSlug;
342
+ }
343
+ if (context.agentTraceId && !headers["X-Prompt-Trace-ID"]) {
344
+ headers["X-Prompt-Trace-ID"] = context.agentTraceId;
345
+ }
346
+ return { ...request, headers };
347
+ };
348
+ }
349
+ function createRefreshInterceptor(client, storage, onSessionExpired, onTokenRefreshed) {
350
+ let isRefreshing = false;
351
+ let refreshQueue = [];
352
+ const processQueue = (error, token = null) => {
353
+ refreshQueue.forEach((prom) => prom(token));
354
+ refreshQueue = [];
355
+ };
356
+ return async (response, request) => {
357
+ if (response.status === 401 && !request.url.includes("/auth/refresh") && !request.url.includes("/auth/login")) {
358
+ const refreshToken = await storage.getItem("tx_refresh");
359
+ if (!refreshToken) {
360
+ onSessionExpired();
361
+ return response;
362
+ }
363
+ if (isRefreshing) {
364
+ return new Promise((resolve) => {
365
+ refreshQueue.push((newToken) => {
366
+ if (newToken) {
367
+ const retryHeaders = { ...request.config.headers, Authorization: `Bearer ${newToken}` };
368
+ resolve(fetch(request.url, { ...request.config, headers: retryHeaders }));
369
+ } else {
370
+ resolve(response);
371
+ }
372
+ });
373
+ });
374
+ }
375
+ isRefreshing = true;
376
+ try {
377
+ const refreshResponse = await fetch(`${client["baseUrl"]}/auth/refresh/`, {
378
+ method: "POST",
379
+ headers: { "Content-Type": "application/json" },
380
+ body: JSON.stringify({ refresh_token: refreshToken })
381
+ });
382
+ if (!refreshResponse.ok) {
383
+ throw new Error("Refresh failed");
384
+ }
385
+ const data = await refreshResponse.json();
386
+ await storage.setItem("tx_access", data.access);
387
+ if (data.refresh) {
388
+ await storage.setItem("tx_refresh", data.refresh);
389
+ }
390
+ isRefreshing = false;
391
+ onTokenRefreshed?.(data.access, data.refresh);
392
+ processQueue(null, data.access);
393
+ const retryHeaders = { ...request.config.headers, Authorization: `Bearer ${data.access}` };
394
+ const r = await fetch(request.url, { ...request.config, headers: retryHeaders });
395
+ return r;
396
+ } catch (err) {
397
+ isRefreshing = false;
398
+ await storage.removeItem("tx_access");
399
+ await storage.removeItem("tx_refresh");
400
+ processQueue(err, null);
401
+ onSessionExpired();
402
+ return response;
403
+ }
404
+ }
405
+ return response;
406
+ };
407
+ }
408
+ var DEVICE_INFO_ENDPOINTS = [
409
+ "/login/email/",
410
+ "/login/phone/",
411
+ "/register/",
412
+ "/social/"
413
+ ];
414
+ var DEFAULT_RETRY = {
415
+ maxRetries: 3,
416
+ retryOn429: true,
417
+ retryOnNetworkError: true,
418
+ baseDelayMs: 1e3
419
+ };
420
+ function sleep(ms) {
421
+ return new Promise((resolve) => setTimeout(resolve, ms));
422
+ }
423
+ function createRetryInterceptor(config = {}, logger) {
424
+ const opts = { ...DEFAULT_RETRY, ...config };
425
+ return async (response, request) => {
426
+ const shouldRetry429 = opts.retryOn429 && response.status === 429;
427
+ const shouldRetryServer = response.status >= 500;
428
+ if (!shouldRetry429 && !shouldRetryServer) {
429
+ return response;
430
+ }
431
+ let lastResponse = response;
432
+ for (let attempt = 1; attempt <= opts.maxRetries; attempt++) {
433
+ let delayMs = opts.baseDelayMs * Math.pow(2, attempt - 1);
434
+ const retryAfter = lastResponse.headers.get("Retry-After");
435
+ if (retryAfter) {
436
+ const parsed = Number(retryAfter);
437
+ if (!isNaN(parsed)) {
438
+ delayMs = parsed * 1e3;
439
+ }
440
+ }
441
+ logger?.debug(`[Tenxyte Retry] Attempt ${attempt}/${opts.maxRetries} after ${delayMs}ms for ${request.url}`);
442
+ await sleep(delayMs);
443
+ try {
444
+ const retryResponse = await fetch(request.url, request.config);
445
+ if (retryResponse.status === 429 && opts.retryOn429 && attempt < opts.maxRetries) {
446
+ lastResponse = retryResponse;
447
+ continue;
448
+ }
449
+ if (retryResponse.status >= 500 && attempt < opts.maxRetries) {
450
+ lastResponse = retryResponse;
451
+ continue;
452
+ }
453
+ return retryResponse;
454
+ } catch (err) {
455
+ if (!opts.retryOnNetworkError || attempt >= opts.maxRetries) {
456
+ throw err;
457
+ }
458
+ logger?.warn(`[Tenxyte Retry] Network error on attempt ${attempt}/${opts.maxRetries}`, err);
459
+ }
460
+ }
461
+ return lastResponse;
462
+ };
463
+ }
464
+ function createDeviceInfoInterceptor(override) {
465
+ const fingerprint = buildDeviceInfo(override);
466
+ return (request) => {
467
+ const isPost = !request.method || request.method === "POST";
468
+ const matchesEndpoint = DEVICE_INFO_ENDPOINTS.some((ep) => request.url.includes(ep));
469
+ if (isPost && matchesEndpoint && request.body && typeof request.body === "object") {
470
+ const body = request.body;
471
+ if (!body.device_info) {
472
+ return { ...request, body: { ...body, device_info: fingerprint } };
473
+ }
474
+ }
475
+ return request;
476
+ };
477
+ }
478
+
116
479
  // src/modules/auth.ts
117
480
  var AuthModule = class {
118
- constructor(client) {
481
+ constructor(client, storage, onTokens, onLogout) {
119
482
  this.client = client;
483
+ this.storage = storage;
484
+ this.onTokens = onTokens;
485
+ this.onLogout = onLogout;
486
+ }
487
+ async clearTokens() {
488
+ if (this.storage) {
489
+ await this.storage.removeItem("tx_access");
490
+ await this.storage.removeItem("tx_refresh");
491
+ this.onLogout?.();
492
+ }
493
+ }
494
+ async persistTokens(tokens) {
495
+ if (this.storage) {
496
+ await this.storage.setItem("tx_access", tokens.access_token);
497
+ if (tokens.refresh_token) {
498
+ await this.storage.setItem("tx_refresh", tokens.refresh_token);
499
+ }
500
+ this.onTokens?.(tokens.access_token, tokens.refresh_token);
501
+ }
502
+ return tokens;
120
503
  }
121
504
  /**
122
505
  * Authenticate a user with their email and password.
@@ -125,7 +508,8 @@ var AuthModule = class {
125
508
  * @throws {TenxyteError} If credentials are invalid, or if `2FA_REQUIRED` without a valid `totp_code`.
126
509
  */
127
510
  async loginWithEmail(data) {
128
- return this.client.post("/api/v1/auth/login/email/", data);
511
+ const tokens = await this.client.post("/api/v1/auth/login/email/", data);
512
+ return this.persistTokens(tokens);
129
513
  }
130
514
  /**
131
515
  * Authenticate a user with an international phone number and password.
@@ -133,7 +517,8 @@ var AuthModule = class {
133
517
  * @returns A pair of Access and Refresh tokens.
134
518
  */
135
519
  async loginWithPhone(data) {
136
- return this.client.post("/api/v1/auth/login/phone/", data);
520
+ const tokens = await this.client.post("/api/v1/auth/login/phone/", data);
521
+ return this.persistTokens(tokens);
137
522
  }
138
523
  /**
139
524
  * Registers a new user account.
@@ -141,7 +526,11 @@ var AuthModule = class {
141
526
  * @returns The registered user data or a confirmation message.
142
527
  */
143
528
  async register(data) {
144
- return this.client.post("/api/v1/auth/register/", data);
529
+ const result = await this.client.post("/api/v1/auth/register/", data);
530
+ if (result?.access_token) {
531
+ await this.persistTokens(result);
532
+ }
533
+ return result;
145
534
  }
146
535
  /**
147
536
  * Logout from the current session.
@@ -149,14 +538,26 @@ var AuthModule = class {
149
538
  * @param refreshToken - The refresh token to revoke.
150
539
  */
151
540
  async logout(refreshToken) {
152
- return this.client.post("/api/v1/auth/logout/", { refresh_token: refreshToken });
541
+ await this.client.post("/api/v1/auth/logout/", { refresh_token: refreshToken });
542
+ await this.clearTokens();
153
543
  }
154
544
  /**
155
545
  * Logout from all sessions across all devices.
156
546
  * Revokes all refresh tokens currently assigned to the user.
157
547
  */
158
548
  async logoutAll() {
159
- return this.client.post("/api/v1/auth/logout/all/");
549
+ await this.client.post("/api/v1/auth/logout/all/");
550
+ await this.clearTokens();
551
+ }
552
+ /**
553
+ * Manually refresh the access token using a valid refresh token.
554
+ * The refresh token is automatically rotated for improved security.
555
+ * @param refreshToken - The current refresh token.
556
+ * @returns A new token pair (access + rotated refresh).
557
+ */
558
+ async refreshToken(refreshToken) {
559
+ const tokens = await this.client.post("/api/v1/auth/refresh/", { refresh_token: refreshToken });
560
+ return this.persistTokens(tokens);
160
561
  }
161
562
  /**
162
563
  * Request a Magic Link for passwordless sign-in.
@@ -171,7 +572,8 @@ var AuthModule = class {
171
572
  * @returns A session token pair if the token is valid and unexpired.
172
573
  */
173
574
  async verifyMagicLink(token) {
174
- return this.client.get(`/api/v1/auth/magic-link/verify/`, { params: { token } });
575
+ const tokens = await this.client.get(`/api/v1/auth/magic-link/verify/`, { params: { token } });
576
+ return this.persistTokens(tokens);
175
577
  }
176
578
  /**
177
579
  * Submits OAuth2 Social Authentication payloads to the backend.
@@ -181,7 +583,8 @@ var AuthModule = class {
181
583
  * @returns An active session token pair.
182
584
  */
183
585
  async loginWithSocial(provider, data) {
184
- return this.client.post(`/api/v1/auth/social/${provider}/`, data);
586
+ const tokens = await this.client.post(`/api/v1/auth/social/${provider}/`, data);
587
+ return this.persistTokens(tokens);
185
588
  }
186
589
  /**
187
590
  * Handle Social Auth Callbacks (Authorization Code flow).
@@ -191,9 +594,10 @@ var AuthModule = class {
191
594
  * @returns An active session token pair after successful code exchange.
192
595
  */
193
596
  async handleSocialCallback(provider, code, redirectUri) {
194
- return this.client.get(`/api/v1/auth/social/${provider}/callback/`, {
597
+ const tokens = await this.client.get(`/api/v1/auth/social/${provider}/callback/`, {
195
598
  params: { code, redirect_uri: redirectUri }
196
599
  });
600
+ return this.persistTokens(tokens);
197
601
  }
198
602
  };
199
603
 
@@ -415,7 +819,7 @@ function decodeJwt(token) {
415
819
  if (parts.length !== 3) {
416
820
  return null;
417
821
  }
418
- let base64Url = parts[1];
822
+ const base64Url = parts[1];
419
823
  if (!base64Url) return null;
420
824
  let base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
421
825
  while (base64.length % 4) {
@@ -431,7 +835,7 @@ function decodeJwt(token) {
431
835
  jsonPayload = Buffer.from(base64, "base64").toString("utf8");
432
836
  }
433
837
  return JSON.parse(jsonPayload);
434
- } catch (e) {
838
+ } catch (_e) {
435
839
  return null;
436
840
  }
437
841
  }
@@ -538,12 +942,7 @@ var RbacModule = class {
538
942
  return this.client.post(`/api/v1/auth/roles/${roleId}/permissions/`, { permission_codes });
539
943
  }
540
944
  async removePermissionsFromRole(roleId, permission_codes) {
541
- return this.client.delete(`/api/v1/auth/roles/${roleId}/permissions/`, {
542
- // Note: DELETE request with body is supported via our fetch wrapper if enabled,
543
- // or we might need to rely on query strings. The schema specifies body or query.
544
- // Let's pass it in body via a custom config or URL params.
545
- body: { permission_codes }
546
- });
945
+ return this.client.delete(`/api/v1/auth/roles/${roleId}/permissions/`, { permission_codes });
547
946
  }
548
947
  // --- Permissions CRUD --- //
549
948
  /** Enumerates all available fine-grained Permissions inside this Tenant scope. */
@@ -567,6 +966,20 @@ var RbacModule = class {
567
966
  return this.client.delete(`/api/v1/auth/permissions/${permissionId}/`);
568
967
  }
569
968
  // --- Direct Assignment (Users) --- //
969
+ /**
970
+ * Retrieve all roles assigned to a specific user.
971
+ * @param userId - The target user ID.
972
+ */
973
+ async getUserRoles(userId) {
974
+ return this.client.get(`/api/v1/auth/users/${userId}/roles/`);
975
+ }
976
+ /**
977
+ * Retrieve all permissions directly assigned to a specific user (excluding role-based permissions).
978
+ * @param userId - The target user ID.
979
+ */
980
+ async getUserPermissions(userId) {
981
+ return this.client.get(`/api/v1/auth/users/${userId}/permissions/`);
982
+ }
570
983
  /**
571
984
  * Attach a given Role globally to a user entity.
572
985
  * Use sparingly if B2B multi-tenancy contexts are preferred.
@@ -578,7 +991,7 @@ var RbacModule = class {
578
991
  * Unbind a global Role from a user entity.
579
992
  */
580
993
  async removeRoleFromUser(userId, roleCode) {
581
- return this.client.delete(`/api/v1/auth/users/${userId}/roles/`, {
994
+ return this.client.delete(`/api/v1/auth/users/${userId}/roles/`, void 0, {
582
995
  params: { role_code: roleCode }
583
996
  });
584
997
  }
@@ -592,9 +1005,7 @@ var RbacModule = class {
592
1005
  * Ad-Hoc strip direct granular Permissions bindings from a specific User.
593
1006
  */
594
1007
  async removePermissionsFromUser(userId, permissionCodes) {
595
- return this.client.delete(`/api/v1/auth/users/${userId}/permissions/`, {
596
- body: { permission_codes: permissionCodes }
597
- });
1008
+ return this.client.delete(`/api/v1/auth/users/${userId}/permissions/`, { permission_codes: permissionCodes });
598
1009
  }
599
1010
  };
600
1011
 
@@ -621,6 +1032,7 @@ var UserModule = class {
621
1032
  return this.client.patch("/api/v1/auth/me/", formData);
622
1033
  }
623
1034
  /**
1035
+ * @deprecated Use `gdpr.requestAccountDeletion()` instead. This proxy will be removed in a future release.
624
1036
  * Trigger self-deletion of an entire account data boundary.
625
1037
  * @param password - Requires the active system password as destructive proof of intent.
626
1038
  * @param otpCode - (Optional) If an OTP was queried prior to attempting account deletion.
@@ -631,6 +1043,13 @@ var UserModule = class {
631
1043
  otp_code: otpCode
632
1044
  });
633
1045
  }
1046
+ /**
1047
+ * Retrieve the roles and permissions of the currently authenticated user.
1048
+ * @returns An object containing `roles[]` and `permissions[]`.
1049
+ */
1050
+ async getMyRoles() {
1051
+ return this.client.get("/api/v1/auth/me/roles/");
1052
+ }
634
1053
  // --- Admin Actions Mapping --- //
635
1054
  /** (Admin only) Lists users paginated matching criteria. */
636
1055
  async listUsers(params) {
@@ -755,8 +1174,9 @@ var B2bModule = class {
755
1174
 
756
1175
  // src/modules/ai.ts
757
1176
  var AiModule = class {
758
- constructor(client) {
1177
+ constructor(client, logger) {
759
1178
  this.client = client;
1179
+ this.logger = logger;
760
1180
  this.client.addRequestInterceptor((config) => {
761
1181
  const headers = { ...config.headers };
762
1182
  if (this.agentToken) {
@@ -767,12 +1187,12 @@ var AiModule = class {
767
1187
  }
768
1188
  return { ...config, headers };
769
1189
  });
770
- this.client.addResponseInterceptor(async (response, request) => {
1190
+ this.client.addResponseInterceptor(async (response, _request) => {
771
1191
  if (response.status === 202) {
772
1192
  const cloned = response.clone();
773
1193
  try {
774
1194
  const data = await cloned.json();
775
- console.debug("[Tenxyte AI] Received 202 Awaiting Approval:", data);
1195
+ this.logger?.debug("[Tenxyte AI] Received 202 Awaiting Approval:", data);
776
1196
  } catch {
777
1197
  }
778
1198
  } else if (response.status === 403) {
@@ -780,9 +1200,9 @@ var AiModule = class {
780
1200
  try {
781
1201
  const data = await cloned.json();
782
1202
  if (data.code === "BUDGET_EXCEEDED") {
783
- console.warn("[Tenxyte AI] Network responded with Budget Exceeded for Agent.");
1203
+ this.logger?.warn("[Tenxyte AI] Network responded with Budget Exceeded for Agent.");
784
1204
  } else if (data.status === "suspended") {
785
- console.warn("[Tenxyte AI] Circuit breaker open for Agent.");
1205
+ this.logger?.warn("[Tenxyte AI] Circuit breaker open for Agent.");
786
1206
  }
787
1207
  } catch {
788
1208
  }
@@ -792,6 +1212,7 @@ var AiModule = class {
792
1212
  }
793
1213
  agentToken = null;
794
1214
  traceId = null;
1215
+ logger;
795
1216
  // ─── AgentToken Lifecycle ───
796
1217
  /**
797
1218
  * Create an AgentToken granting specific deterministic limits to an AI Agent.
@@ -871,8 +1292,340 @@ var AiModule = class {
871
1292
  }
872
1293
  };
873
1294
 
1295
+ // src/modules/applications.ts
1296
+ var ApplicationsModule = class {
1297
+ constructor(client) {
1298
+ this.client = client;
1299
+ }
1300
+ /**
1301
+ * List all registered applications (paginated).
1302
+ * @param params - Optional filters: `search`, `is_active`, `ordering`, `page`, `page_size`.
1303
+ * @returns A paginated list of applications.
1304
+ */
1305
+ async listApplications(params) {
1306
+ return this.client.get("/api/v1/auth/applications/", {
1307
+ params
1308
+ });
1309
+ }
1310
+ /**
1311
+ * Create a new application.
1312
+ * @param data - The application name and optional description.
1313
+ * @returns The created application including one-time `client_secret`.
1314
+ */
1315
+ async createApplication(data) {
1316
+ return this.client.post("/api/v1/auth/applications/", data);
1317
+ }
1318
+ /**
1319
+ * Get a single application by its ID.
1320
+ * @param appId - The application ID.
1321
+ * @returns The application details (secret is never included).
1322
+ */
1323
+ async getApplication(appId) {
1324
+ return this.client.get(`/api/v1/auth/applications/${appId}/`);
1325
+ }
1326
+ /**
1327
+ * Fully update an application (PUT — all fields replaced).
1328
+ * @param appId - The application ID.
1329
+ * @param data - The full updated application data.
1330
+ * @returns The updated application.
1331
+ */
1332
+ async updateApplication(appId, data) {
1333
+ return this.client.put(`/api/v1/auth/applications/${appId}/`, data);
1334
+ }
1335
+ /**
1336
+ * Partially update an application (PATCH — only provided fields are changed).
1337
+ * @param appId - The application ID.
1338
+ * @param data - The fields to update.
1339
+ * @returns The updated application.
1340
+ */
1341
+ async patchApplication(appId, data) {
1342
+ return this.client.patch(`/api/v1/auth/applications/${appId}/`, data);
1343
+ }
1344
+ /**
1345
+ * Delete an application permanently.
1346
+ * @param appId - The application ID.
1347
+ */
1348
+ async deleteApplication(appId) {
1349
+ return this.client.delete(`/api/v1/auth/applications/${appId}/`);
1350
+ }
1351
+ /**
1352
+ * Regenerate credentials for an application.
1353
+ * **Warning:** Old credentials are immediately invalidated. The new secret is shown only once.
1354
+ * @param appId - The application ID.
1355
+ * @param confirmation - Must be the string `"REGENERATE"` to confirm the irreversible action.
1356
+ * @returns The new credentials (access_key + access_secret shown once).
1357
+ */
1358
+ async regenerateCredentials(appId, confirmation = "REGENERATE") {
1359
+ return this.client.post(`/api/v1/auth/applications/${appId}/regenerate/`, { confirmation });
1360
+ }
1361
+ };
1362
+
1363
+ // src/modules/admin.ts
1364
+ var AdminModule = class {
1365
+ constructor(client) {
1366
+ this.client = client;
1367
+ }
1368
+ // ─── Audit Logs ───
1369
+ /**
1370
+ * List audit log entries (paginated).
1371
+ * @param params - Optional filters and pagination.
1372
+ */
1373
+ async listAuditLogs(params) {
1374
+ return this.client.get("/api/v1/auth/admin/audit-logs/", {
1375
+ params
1376
+ });
1377
+ }
1378
+ /**
1379
+ * Get a single audit log entry by ID.
1380
+ * @param logId - The audit log entry ID.
1381
+ */
1382
+ async getAuditLog(logId) {
1383
+ return this.client.get(`/api/v1/auth/admin/audit-logs/${logId}/`);
1384
+ }
1385
+ // ─── Login Attempts ───
1386
+ /**
1387
+ * List login attempt records (paginated).
1388
+ * @param params - Optional filters and pagination.
1389
+ */
1390
+ async listLoginAttempts(params) {
1391
+ return this.client.get("/api/v1/auth/admin/login-attempts/", {
1392
+ params
1393
+ });
1394
+ }
1395
+ // ─── Blacklisted Tokens ───
1396
+ /**
1397
+ * List blacklisted (revoked) JWT tokens (paginated).
1398
+ * @param params - Optional filters and pagination.
1399
+ */
1400
+ async listBlacklistedTokens(params) {
1401
+ return this.client.get("/api/v1/auth/admin/blacklisted-tokens/", {
1402
+ params
1403
+ });
1404
+ }
1405
+ /**
1406
+ * Remove expired blacklisted tokens.
1407
+ * @returns A summary object with cleanup results.
1408
+ */
1409
+ async cleanupBlacklistedTokens() {
1410
+ return this.client.post("/api/v1/auth/admin/blacklisted-tokens/cleanup/");
1411
+ }
1412
+ // ─── Refresh Tokens ───
1413
+ /**
1414
+ * List refresh tokens (admin view — token values are hidden).
1415
+ * @param params - Optional filters and pagination.
1416
+ */
1417
+ async listRefreshTokens(params) {
1418
+ return this.client.get("/api/v1/auth/admin/refresh-tokens/", {
1419
+ params
1420
+ });
1421
+ }
1422
+ /**
1423
+ * Revoke a specific refresh token.
1424
+ * @param tokenId - The refresh token ID.
1425
+ * @returns The updated refresh token record.
1426
+ */
1427
+ async revokeRefreshToken(tokenId) {
1428
+ return this.client.post(`/api/v1/auth/admin/refresh-tokens/${tokenId}/revoke/`);
1429
+ }
1430
+ };
1431
+
1432
+ // src/modules/gdpr.ts
1433
+ var GdprModule = class {
1434
+ constructor(client) {
1435
+ this.client = client;
1436
+ }
1437
+ // ─── User-facing ───
1438
+ /**
1439
+ * Request account deletion (GDPR-compliant).
1440
+ * Initiates a 30-day grace period during which the user can cancel.
1441
+ * @param data - Password (+ optional OTP code and reason).
1442
+ */
1443
+ async requestAccountDeletion(data) {
1444
+ return this.client.post("/api/v1/auth/request-account-deletion/", data);
1445
+ }
1446
+ /**
1447
+ * Confirm the account deletion using the token received by email.
1448
+ * The token is valid for 24 hours. After confirmation the account enters the 30-day grace period.
1449
+ * @param token - The confirmation token from the email.
1450
+ */
1451
+ async confirmAccountDeletion(token) {
1452
+ return this.client.post("/api/v1/auth/confirm-account-deletion/", { token });
1453
+ }
1454
+ /**
1455
+ * Cancel a pending account deletion during the grace period.
1456
+ * The account is immediately reactivated.
1457
+ * @param password - The current password for security.
1458
+ */
1459
+ async cancelAccountDeletion(password) {
1460
+ return this.client.post("/api/v1/auth/cancel-account-deletion/", { password });
1461
+ }
1462
+ /**
1463
+ * Get the deletion status for the current user.
1464
+ * Includes pending, confirmed, or cancelled requests.
1465
+ */
1466
+ async getAccountDeletionStatus() {
1467
+ return this.client.get("/api/v1/auth/account-deletion-status/");
1468
+ }
1469
+ /**
1470
+ * Export all personal data (GDPR right to data portability).
1471
+ * @param password - The current password for security.
1472
+ */
1473
+ async exportUserData(password) {
1474
+ return this.client.post("/api/v1/auth/export-user-data/", { password });
1475
+ }
1476
+ // ─── Admin-facing ───
1477
+ /**
1478
+ * List deletion requests (admin, paginated).
1479
+ * @param params - Optional filters and pagination.
1480
+ */
1481
+ async listDeletionRequests(params) {
1482
+ return this.client.get("/api/v1/auth/admin/deletion-requests/", {
1483
+ params
1484
+ });
1485
+ }
1486
+ /**
1487
+ * Get a single deletion request by ID.
1488
+ * @param requestId - The deletion request ID.
1489
+ */
1490
+ async getDeletionRequest(requestId) {
1491
+ return this.client.get(`/api/v1/auth/admin/deletion-requests/${requestId}/`);
1492
+ }
1493
+ /**
1494
+ * Process (execute) a confirmed deletion request.
1495
+ * **WARNING:** This is irreversible and permanently destroys all user data.
1496
+ * @param requestId - The deletion request ID.
1497
+ * @param data - Must include `{ confirmation: "PERMANENTLY DELETE" }`.
1498
+ */
1499
+ async processDeletionRequest(requestId, data) {
1500
+ return this.client.post(`/api/v1/auth/admin/deletion-requests/${requestId}/process/`, data);
1501
+ }
1502
+ /**
1503
+ * Batch-process all confirmed deletion requests whose 30-day grace period has expired.
1504
+ * Typically run by a daily cron job.
1505
+ */
1506
+ async processExpiredDeletions() {
1507
+ return this.client.post("/api/v1/auth/admin/deletion-requests/process-expired/");
1508
+ }
1509
+ };
1510
+
1511
+ // src/modules/dashboard.ts
1512
+ var DashboardModule = class {
1513
+ constructor(client) {
1514
+ this.client = client;
1515
+ }
1516
+ /**
1517
+ * Get global cross-module dashboard statistics.
1518
+ * Data varies based on the organizational context (`X-Org-Slug`) and permissions.
1519
+ * Covers users, authentication, applications, security, and GDPR metrics.
1520
+ * Charts span the last 7 days with previous-period comparisons.
1521
+ * @param params - Optional period and comparison flag.
1522
+ */
1523
+ async getStats(params) {
1524
+ return this.client.get("/api/v1/auth/dashboard/stats/", {
1525
+ params
1526
+ });
1527
+ }
1528
+ /**
1529
+ * Get authentication-specific statistics.
1530
+ * Includes login stats, methods breakdown, registrations, tokens, top failure reasons, and 7-day graphs.
1531
+ */
1532
+ async getAuthStats() {
1533
+ return this.client.get("/api/v1/auth/dashboard/auth/");
1534
+ }
1535
+ /**
1536
+ * Get security-specific statistics.
1537
+ * Includes audit summary, blacklisted tokens, suspicious activity, and 2FA adoption.
1538
+ */
1539
+ async getSecurityStats() {
1540
+ return this.client.get("/api/v1/auth/dashboard/security/");
1541
+ }
1542
+ /**
1543
+ * Get GDPR-specific statistics.
1544
+ * Includes deletion requests by status and data export metrics.
1545
+ */
1546
+ async getGdprStats() {
1547
+ return this.client.get("/api/v1/auth/dashboard/gdpr/");
1548
+ }
1549
+ /**
1550
+ * Get organization-specific statistics.
1551
+ * Includes organizations, members, roles, and top organizations.
1552
+ */
1553
+ async getOrganizationStats() {
1554
+ return this.client.get("/api/v1/auth/dashboard/organizations/");
1555
+ }
1556
+ };
1557
+
1558
+ // src/utils/events.ts
1559
+ var EventEmitter = class {
1560
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
1561
+ events;
1562
+ constructor() {
1563
+ this.events = /* @__PURE__ */ new Map();
1564
+ }
1565
+ /**
1566
+ * Subscribe to an event.
1567
+ * @param event The event name
1568
+ * @param callback The callback function
1569
+ * @returns Unsubscribe function
1570
+ */
1571
+ on(event, callback) {
1572
+ if (!this.events.has(event)) {
1573
+ this.events.set(event, []);
1574
+ }
1575
+ this.events.get(event).push(callback);
1576
+ return () => this.off(event, callback);
1577
+ }
1578
+ /**
1579
+ * Unsubscribe from an event.
1580
+ * @param event The event name
1581
+ * @param callback The exact callback function that was passed to .on()
1582
+ */
1583
+ off(event, callback) {
1584
+ const callbacks = this.events.get(event);
1585
+ if (!callbacks) return;
1586
+ const index = callbacks.indexOf(callback);
1587
+ if (index !== -1) {
1588
+ callbacks.splice(index, 1);
1589
+ }
1590
+ }
1591
+ /**
1592
+ * Subscribe to an event exactly once.
1593
+ */
1594
+ once(event, callback) {
1595
+ const wrapped = (payload) => {
1596
+ this.off(event, wrapped);
1597
+ callback(payload);
1598
+ };
1599
+ return this.on(event, wrapped);
1600
+ }
1601
+ /**
1602
+ * Emit an event internally.
1603
+ */
1604
+ emit(event, payload) {
1605
+ const callbacks = this.events.get(event);
1606
+ if (!callbacks) return;
1607
+ const copy = [...callbacks];
1608
+ for (const callback of copy) {
1609
+ try {
1610
+ callback(payload);
1611
+ } catch (err) {
1612
+ console.error(`[Tenxyte EventEmitter] Error executing callback for event ${String(event)}`, err);
1613
+ }
1614
+ }
1615
+ }
1616
+ removeAllListeners() {
1617
+ this.events.clear();
1618
+ }
1619
+ };
1620
+
874
1621
  // src/client.ts
875
1622
  var TenxyteClient = class {
1623
+ /** Fully resolved configuration (all defaults applied). */
1624
+ config;
1625
+ /** Persistent token storage back-end (defaults to MemoryStorage). */
1626
+ storage;
1627
+ /** Shared mutable context used by interceptors (org slug, agent trace ID). */
1628
+ context;
876
1629
  /** The core HTTP wrapper handling network interception and parsing */
877
1630
  http;
878
1631
  /** Authentication module (Login, Signup, Magic link, session handling) */
@@ -887,8 +1640,22 @@ var TenxyteClient = class {
887
1640
  b2b;
888
1641
  /** AIRS - AI Responsibility & Security module (Agent tokens, Circuit breakers, HITL) */
889
1642
  ai;
1643
+ /** Applications module (API client CRUD, credential management) */
1644
+ applications;
1645
+ /** Admin module (audit logs, login attempts, blacklisted tokens, refresh tokens) */
1646
+ admin;
1647
+ /** GDPR module (account deletion, data export, deletion request management) */
1648
+ gdpr;
1649
+ /** Dashboard module (global, auth, security, GDPR, organization statistics) */
1650
+ dashboard;
1651
+ /** Internal event emitter used via composition. */
1652
+ emitter;
890
1653
  /**
891
1654
  * Initializes the SDK with connection details for your Tenxyte-powered API.
1655
+ *
1656
+ * Accepts the full TenxyteClientConfig. Minimal usage with just { baseUrl }
1657
+ * is still supported for backward compatibility.
1658
+ *
892
1659
  * @param options Configuration options including `baseUrl` and custom headers like `X-Access-Key`
893
1660
  *
894
1661
  * @example
@@ -900,21 +1667,165 @@ var TenxyteClient = class {
900
1667
  * ```
901
1668
  */
902
1669
  constructor(options) {
903
- this.http = new TenxyteHttpClient(options);
904
- this.auth = new AuthModule(this.http);
905
- this.security = new SecurityModule(this.http);
1670
+ this.config = resolveConfig(options);
1671
+ this.storage = this.config.storage;
1672
+ this.emitter = new EventEmitter();
1673
+ this.context = { activeOrgSlug: null, agentTraceId: null };
1674
+ this.http = new TenxyteHttpClient({
1675
+ baseUrl: this.config.baseUrl,
1676
+ headers: this.config.headers,
1677
+ timeoutMs: this.config.timeoutMs
1678
+ });
1679
+ this.http.addRequestInterceptor(createAuthInterceptor(this.storage, this.context));
1680
+ if (this.config.autoDeviceInfo) {
1681
+ this.http.addRequestInterceptor(createDeviceInfoInterceptor(this.config.deviceInfoOverride));
1682
+ }
1683
+ if (this.config.retryConfig) {
1684
+ this.http.addResponseInterceptor(
1685
+ createRetryInterceptor(this.config.retryConfig, this.config.logger)
1686
+ );
1687
+ }
1688
+ if (this.config.autoRefresh) {
1689
+ this.http.addResponseInterceptor(
1690
+ createRefreshInterceptor(
1691
+ this.http,
1692
+ this.storage,
1693
+ () => {
1694
+ this.emit("session:expired", void 0);
1695
+ this.config.onSessionExpired?.();
1696
+ },
1697
+ (accessToken, refreshToken) => {
1698
+ this.rbac.setToken(accessToken);
1699
+ this.emit("token:refreshed", { accessToken });
1700
+ this.emit("token:stored", { accessToken, refreshToken });
1701
+ }
1702
+ )
1703
+ );
1704
+ }
906
1705
  this.rbac = new RbacModule(this.http);
1706
+ this.auth = new AuthModule(
1707
+ this.http,
1708
+ this.storage,
1709
+ (accessToken, refreshToken) => {
1710
+ this.rbac.setToken(accessToken);
1711
+ this.emit("token:stored", { accessToken, refreshToken });
1712
+ },
1713
+ () => {
1714
+ this.rbac.setToken(null);
1715
+ this.emit("session:expired", void 0);
1716
+ }
1717
+ );
1718
+ this.security = new SecurityModule(this.http);
907
1719
  this.user = new UserModule(this.http);
908
1720
  this.b2b = new B2bModule(this.http);
909
- this.ai = new AiModule(this.http);
1721
+ this.ai = new AiModule(this.http, this.config.logger);
1722
+ this.applications = new ApplicationsModule(this.http);
1723
+ this.admin = new AdminModule(this.http);
1724
+ this.gdpr = new GdprModule(this.http);
1725
+ this.dashboard = new DashboardModule(this.http);
1726
+ }
1727
+ // ─── Event delegation ───
1728
+ /** Subscribe to an SDK event. Returns an unsubscribe function. */
1729
+ on(event, callback) {
1730
+ return this.emitter.on(event, callback);
1731
+ }
1732
+ /** Subscribe to an SDK event exactly once. Returns an unsubscribe function. */
1733
+ once(event, callback) {
1734
+ return this.emitter.once(event, callback);
1735
+ }
1736
+ /** Unsubscribe a previously registered callback from an SDK event. */
1737
+ off(event, callback) {
1738
+ this.emitter.off(event, callback);
1739
+ }
1740
+ /** Emit an SDK event (internal use). */
1741
+ emit(event, payload) {
1742
+ this.emitter.emit(event, payload);
1743
+ }
1744
+ // ─── High-level helpers ───
1745
+ /**
1746
+ * Check whether a valid (non-expired) access token exists in storage.
1747
+ * Performs a synchronous JWT expiry check — no network call.
1748
+ */
1749
+ async isAuthenticated() {
1750
+ const token = await this.storage.getItem("tx_access");
1751
+ if (!token) return false;
1752
+ return !this.isTokenExpiredSync(token);
1753
+ }
1754
+ /**
1755
+ * Return the current access token from storage, or `null` if absent.
1756
+ */
1757
+ async getAccessToken() {
1758
+ return this.storage.getItem("tx_access");
1759
+ }
1760
+ /**
1761
+ * Decode the current access token and return the JWT payload.
1762
+ * Returns `null` if no token is stored or if decoding fails.
1763
+ * No network call is made — this reads from the cached JWT.
1764
+ */
1765
+ async getCurrentUser() {
1766
+ const token = await this.storage.getItem("tx_access");
1767
+ if (!token) return null;
1768
+ return decodeJwt(token);
1769
+ }
1770
+ /**
1771
+ * Check whether the stored access token is expired without making a network call.
1772
+ * Returns `true` if expired or if no token is present.
1773
+ */
1774
+ async isTokenExpired() {
1775
+ const token = await this.storage.getItem("tx_access");
1776
+ if (!token) return true;
1777
+ return this.isTokenExpiredSync(token);
1778
+ }
1779
+ /** Synchronous helper: checks JWT `exp` claim against current time. */
1780
+ isTokenExpiredSync(token) {
1781
+ const decoded = decodeJwt(token);
1782
+ if (!decoded?.exp) return true;
1783
+ return decoded.exp * 1e3 < Date.now() - 3e4;
1784
+ }
1785
+ // ─── Framework wrapper interface ───
1786
+ /**
1787
+ * Returns a synchronous snapshot of the SDK state.
1788
+ * Designed for consumption by framework wrappers (React, Vue, etc.).
1789
+ * Note: This is async because storage access may be async.
1790
+ */
1791
+ async getState() {
1792
+ const token = await this.storage.getItem("tx_access");
1793
+ const isAuthenticated = token ? !this.isTokenExpiredSync(token) : false;
1794
+ const user = token ? decodeJwt(token) : null;
1795
+ return {
1796
+ isAuthenticated,
1797
+ user,
1798
+ accessToken: token,
1799
+ activeOrg: this.context.activeOrgSlug,
1800
+ isAgentMode: this.ai.isAgentMode()
1801
+ };
910
1802
  }
911
1803
  };
912
1804
  export {
1805
+ AdminModule,
1806
+ AiModule,
1807
+ ApplicationsModule,
913
1808
  AuthModule,
1809
+ B2bModule,
1810
+ CookieStorage,
1811
+ DashboardModule,
1812
+ EventEmitter,
1813
+ GdprModule,
1814
+ LocalStorage,
1815
+ MemoryStorage,
1816
+ NOOP_LOGGER,
914
1817
  RbacModule,
1818
+ SDK_VERSION,
915
1819
  SecurityModule,
916
1820
  TenxyteClient,
917
1821
  TenxyteHttpClient,
918
- UserModule
1822
+ UserModule,
1823
+ buildDeviceInfo,
1824
+ createAuthInterceptor,
1825
+ createDeviceInfoInterceptor,
1826
+ createRefreshInterceptor,
1827
+ createRetryInterceptor,
1828
+ decodeJwt,
1829
+ resolveConfig
919
1830
  };
920
1831
  //# sourceMappingURL=index.js.map