@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.cjs CHANGED
@@ -20,24 +20,175 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ AdminModule: () => AdminModule,
24
+ AiModule: () => AiModule,
25
+ ApplicationsModule: () => ApplicationsModule,
23
26
  AuthModule: () => AuthModule,
27
+ B2bModule: () => B2bModule,
28
+ CookieStorage: () => CookieStorage,
29
+ DashboardModule: () => DashboardModule,
30
+ EventEmitter: () => EventEmitter,
31
+ GdprModule: () => GdprModule,
32
+ LocalStorage: () => LocalStorage,
33
+ MemoryStorage: () => MemoryStorage,
34
+ NOOP_LOGGER: () => NOOP_LOGGER,
24
35
  RbacModule: () => RbacModule,
36
+ SDK_VERSION: () => SDK_VERSION,
25
37
  SecurityModule: () => SecurityModule,
26
38
  TenxyteClient: () => TenxyteClient,
27
39
  TenxyteHttpClient: () => TenxyteHttpClient,
28
- UserModule: () => UserModule
40
+ UserModule: () => UserModule,
41
+ buildDeviceInfo: () => buildDeviceInfo,
42
+ createAuthInterceptor: () => createAuthInterceptor,
43
+ createDeviceInfoInterceptor: () => createDeviceInfoInterceptor,
44
+ createRefreshInterceptor: () => createRefreshInterceptor,
45
+ createRetryInterceptor: () => createRetryInterceptor,
46
+ decodeJwt: () => decodeJwt,
47
+ resolveConfig: () => resolveConfig
29
48
  });
30
49
  module.exports = __toCommonJS(index_exports);
31
50
 
51
+ // src/storage/memory.ts
52
+ var MemoryStorage = class {
53
+ store;
54
+ constructor() {
55
+ this.store = /* @__PURE__ */ new Map();
56
+ }
57
+ getItem(key) {
58
+ const value = this.store.get(key);
59
+ return value !== void 0 ? value : null;
60
+ }
61
+ setItem(key, value) {
62
+ this.store.set(key, value);
63
+ }
64
+ removeItem(key) {
65
+ this.store.delete(key);
66
+ }
67
+ clear() {
68
+ this.store.clear();
69
+ }
70
+ };
71
+
72
+ // src/storage/localStorage.ts
73
+ var LocalStorage = class {
74
+ fallbackMemoryStore = null;
75
+ isAvailable;
76
+ constructor() {
77
+ this.isAvailable = this.checkAvailability();
78
+ if (!this.isAvailable) {
79
+ this.fallbackMemoryStore = new MemoryStorage();
80
+ }
81
+ }
82
+ checkAvailability() {
83
+ try {
84
+ if (typeof window === "undefined" || !window.localStorage) {
85
+ return false;
86
+ }
87
+ const testKey = "__tenxyte_test__";
88
+ window.localStorage.setItem(testKey, "1");
89
+ window.localStorage.removeItem(testKey);
90
+ return true;
91
+ } catch (_e) {
92
+ return false;
93
+ }
94
+ }
95
+ getItem(key) {
96
+ if (!this.isAvailable && this.fallbackMemoryStore) {
97
+ return this.fallbackMemoryStore.getItem(key);
98
+ }
99
+ return window.localStorage.getItem(key);
100
+ }
101
+ setItem(key, value) {
102
+ if (!this.isAvailable && this.fallbackMemoryStore) {
103
+ this.fallbackMemoryStore.setItem(key, value);
104
+ return;
105
+ }
106
+ try {
107
+ window.localStorage.setItem(key, value);
108
+ } catch (_e) {
109
+ console.warn(`[Tenxyte SDK] Warning: failed to write to localStorage for key ${key}`);
110
+ }
111
+ }
112
+ removeItem(key) {
113
+ if (!this.isAvailable && this.fallbackMemoryStore) {
114
+ this.fallbackMemoryStore.removeItem(key);
115
+ return;
116
+ }
117
+ window.localStorage.removeItem(key);
118
+ }
119
+ clear() {
120
+ if (!this.isAvailable && this.fallbackMemoryStore) {
121
+ this.fallbackMemoryStore.clear();
122
+ return;
123
+ }
124
+ window.localStorage.clear();
125
+ }
126
+ };
127
+
128
+ // src/storage/cookie.ts
129
+ var CookieStorage = class {
130
+ defaultOptions;
131
+ constructor(options = {}) {
132
+ const secure = options.secure ?? true;
133
+ const sameSite = options.sameSite ?? "Lax";
134
+ this.defaultOptions = `path=/; SameSite=${sameSite}${secure ? "; Secure" : ""}`;
135
+ }
136
+ getItem(key) {
137
+ if (typeof document === "undefined") return null;
138
+ const match = document.cookie.match(new RegExp(`(^| )${key}=([^;]+)`));
139
+ return match ? decodeURIComponent(match[2]) : null;
140
+ }
141
+ setItem(key, value) {
142
+ if (typeof document === "undefined") return;
143
+ document.cookie = `${key}=${encodeURIComponent(value)}; ${this.defaultOptions}`;
144
+ }
145
+ removeItem(key) {
146
+ if (typeof document === "undefined") return;
147
+ document.cookie = `${key}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/`;
148
+ }
149
+ clear() {
150
+ this.removeItem("tx_access");
151
+ this.removeItem("tx_refresh");
152
+ }
153
+ };
154
+
155
+ // src/config.ts
156
+ var SDK_VERSION = "0.9.0";
157
+ var NOOP_LOGGER = {
158
+ debug() {
159
+ },
160
+ warn() {
161
+ },
162
+ error() {
163
+ }
164
+ };
165
+ function resolveConfig(config) {
166
+ return {
167
+ baseUrl: config.baseUrl,
168
+ headers: config.headers ?? {},
169
+ storage: config.storage ?? new MemoryStorage(),
170
+ autoRefresh: config.autoRefresh ?? true,
171
+ autoDeviceInfo: config.autoDeviceInfo ?? true,
172
+ timeoutMs: config.timeoutMs,
173
+ onSessionExpired: config.onSessionExpired,
174
+ logger: config.logger ?? NOOP_LOGGER,
175
+ logLevel: config.logLevel ?? "silent",
176
+ deviceInfoOverride: config.deviceInfoOverride,
177
+ retryConfig: config.retryConfig
178
+ };
179
+ }
180
+
32
181
  // src/http/client.ts
33
182
  var TenxyteHttpClient = class {
34
183
  baseUrl;
35
184
  defaultHeaders;
185
+ timeoutMs;
36
186
  // Interceptors
37
187
  requestInterceptors = [];
38
188
  responseInterceptors = [];
39
189
  constructor(options) {
40
190
  this.baseUrl = options.baseUrl.replace(/\/$/, "");
191
+ this.timeoutMs = options.timeoutMs;
41
192
  this.defaultHeaders = {
42
193
  "Content-Type": "application/json",
43
194
  Accept: "application/json",
@@ -56,7 +207,7 @@ var TenxyteHttpClient = class {
56
207
  */
57
208
  async request(endpoint, config = {}) {
58
209
  const urlStr = endpoint.startsWith("http") ? endpoint : `${this.baseUrl}${endpoint.startsWith("/") ? "" : "/"}${endpoint}`;
59
- let urlObj = new URL(urlStr);
210
+ const urlObj = new URL(urlStr);
60
211
  if (config.params) {
61
212
  Object.entries(config.params).forEach(([key, value]) => {
62
213
  if (value !== void 0 && value !== null) {
@@ -83,6 +234,13 @@ var TenxyteHttpClient = class {
83
234
  requestContext = await interceptor(requestContext);
84
235
  }
85
236
  const { url, ...fetchConfig } = requestContext;
237
+ let controller;
238
+ let timeoutId;
239
+ if (this.timeoutMs) {
240
+ controller = new AbortController();
241
+ fetchConfig.signal = controller.signal;
242
+ timeoutId = setTimeout(() => controller.abort(), this.timeoutMs);
243
+ }
86
244
  try {
87
245
  let response = await fetch(url, fetchConfig);
88
246
  for (const interceptor of this.responseInterceptors) {
@@ -100,6 +258,13 @@ var TenxyteHttpClient = class {
100
258
  }
101
259
  return await response.text();
102
260
  } catch (error) {
261
+ if (error?.name === "AbortError") {
262
+ throw {
263
+ error: `Request timed out after ${this.timeoutMs}ms`,
264
+ code: "TIMEOUT",
265
+ details: url
266
+ };
267
+ }
103
268
  if (error && error.code) {
104
269
  throw error;
105
270
  }
@@ -108,6 +273,10 @@ var TenxyteHttpClient = class {
108
273
  code: "NETWORK_ERROR",
109
274
  details: String(error)
110
275
  };
276
+ } finally {
277
+ if (timeoutId !== void 0) {
278
+ clearTimeout(timeoutId);
279
+ }
111
280
  }
112
281
  }
113
282
  async normalizeError(response) {
@@ -119,7 +288,7 @@ var TenxyteHttpClient = class {
119
288
  details: body.details || body,
120
289
  retry_after: response.headers.has("Retry-After") ? parseInt(response.headers.get("Retry-After"), 10) : void 0
121
290
  };
122
- } catch (e) {
291
+ } catch (_e) {
123
292
  return {
124
293
  error: `HTTP Error ${response.status}: ${response.statusText}`,
125
294
  code: `HTTP_${response.status}`
@@ -139,15 +308,248 @@ var TenxyteHttpClient = class {
139
308
  patch(endpoint, data, config) {
140
309
  return this.request(endpoint, { ...config, method: "PATCH", body: data });
141
310
  }
142
- delete(endpoint, config) {
143
- return this.request(endpoint, { ...config, method: "DELETE" });
311
+ delete(endpoint, data, config) {
312
+ return this.request(endpoint, { ...config, method: "DELETE", body: data });
144
313
  }
145
314
  };
146
315
 
316
+ // src/utils/device_info.ts
317
+ function buildDeviceInfo(customInfo = {}) {
318
+ const autoInfo = getAutoInfo();
319
+ const v = "1";
320
+ const os = customInfo.os || autoInfo.os;
321
+ const osv = customInfo.osVersion || autoInfo.osVersion;
322
+ const device = customInfo.device || autoInfo.device;
323
+ const arch = customInfo.arch || autoInfo.arch;
324
+ const app = customInfo.app || autoInfo.app;
325
+ const appv = customInfo.appVersion || autoInfo.appVersion;
326
+ const runtime = customInfo.runtime || autoInfo.runtime;
327
+ const rtv = customInfo.runtimeVersion || autoInfo.runtimeVersion;
328
+ const tz = customInfo.timezone || autoInfo.timezone;
329
+ const parts = [
330
+ `v=${v}`,
331
+ `os=${os}` + (osv ? `;osv=${osv}` : ""),
332
+ `device=${device}`,
333
+ arch ? `arch=${arch}` : "",
334
+ app ? `app=${app}${appv ? `;appv=${appv}` : ""}` : "",
335
+ `runtime=${runtime}` + (rtv ? `;rtv=${rtv}` : ""),
336
+ tz ? `tz=${tz}` : ""
337
+ ];
338
+ return parts.filter(Boolean).join("|");
339
+ }
340
+ function getAutoInfo() {
341
+ const info = {
342
+ os: "unknown",
343
+ osVersion: "",
344
+ device: "desktop",
345
+ // default
346
+ arch: "",
347
+ app: "sdk",
348
+ appVersion: "0.1.0",
349
+ runtime: "unknown",
350
+ runtimeVersion: "",
351
+ timezone: ""
352
+ };
353
+ try {
354
+ if (typeof Intl !== "undefined") {
355
+ info.timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
356
+ }
357
+ if (typeof process !== "undefined" && process.version) {
358
+ info.runtime = "node";
359
+ info.runtimeVersion = process.version;
360
+ info.os = process.platform;
361
+ info.arch = process.arch;
362
+ info.device = "server";
363
+ } else if (typeof window !== "undefined" && window.navigator) {
364
+ const ua = window.navigator.userAgent.toLowerCase();
365
+ if (ua.includes("windows")) info.os = "windows";
366
+ else if (ua.includes("mac")) info.os = "macos";
367
+ else if (ua.includes("linux")) info.os = "linux";
368
+ else if (ua.includes("android")) info.os = "android";
369
+ else if (ua.includes("ios") || ua.includes("iphone") || ua.includes("ipad")) info.os = "ios";
370
+ if (/mobi|android|touch|mini/i.test(ua)) info.device = "mobile";
371
+ if (/tablet|ipad/i.test(ua)) info.device = "tablet";
372
+ if (ua.includes("firefox")) info.runtime = "firefox";
373
+ else if (ua.includes("edg/")) info.runtime = "edge";
374
+ else if (ua.includes("chrome")) info.runtime = "chrome";
375
+ else if (ua.includes("safari")) info.runtime = "safari";
376
+ }
377
+ } catch (_e) {
378
+ }
379
+ return info;
380
+ }
381
+
382
+ // src/http/interceptors.ts
383
+ function createAuthInterceptor(storage, context) {
384
+ return async (request) => {
385
+ const token = await storage.getItem("tx_access");
386
+ const headers = { ...request.headers || {} };
387
+ if (token && !headers["Authorization"]) {
388
+ headers["Authorization"] = `Bearer ${token}`;
389
+ }
390
+ if (context.activeOrgSlug && !headers["X-Org-Slug"]) {
391
+ headers["X-Org-Slug"] = context.activeOrgSlug;
392
+ }
393
+ if (context.agentTraceId && !headers["X-Prompt-Trace-ID"]) {
394
+ headers["X-Prompt-Trace-ID"] = context.agentTraceId;
395
+ }
396
+ return { ...request, headers };
397
+ };
398
+ }
399
+ function createRefreshInterceptor(client, storage, onSessionExpired, onTokenRefreshed) {
400
+ let isRefreshing = false;
401
+ let refreshQueue = [];
402
+ const processQueue = (error, token = null) => {
403
+ refreshQueue.forEach((prom) => prom(token));
404
+ refreshQueue = [];
405
+ };
406
+ return async (response, request) => {
407
+ if (response.status === 401 && !request.url.includes("/auth/refresh") && !request.url.includes("/auth/login")) {
408
+ const refreshToken = await storage.getItem("tx_refresh");
409
+ if (!refreshToken) {
410
+ onSessionExpired();
411
+ return response;
412
+ }
413
+ if (isRefreshing) {
414
+ return new Promise((resolve) => {
415
+ refreshQueue.push((newToken) => {
416
+ if (newToken) {
417
+ const retryHeaders = { ...request.config.headers, Authorization: `Bearer ${newToken}` };
418
+ resolve(fetch(request.url, { ...request.config, headers: retryHeaders }));
419
+ } else {
420
+ resolve(response);
421
+ }
422
+ });
423
+ });
424
+ }
425
+ isRefreshing = true;
426
+ try {
427
+ const refreshResponse = await fetch(`${client["baseUrl"]}/auth/refresh/`, {
428
+ method: "POST",
429
+ headers: { "Content-Type": "application/json" },
430
+ body: JSON.stringify({ refresh_token: refreshToken })
431
+ });
432
+ if (!refreshResponse.ok) {
433
+ throw new Error("Refresh failed");
434
+ }
435
+ const data = await refreshResponse.json();
436
+ await storage.setItem("tx_access", data.access);
437
+ if (data.refresh) {
438
+ await storage.setItem("tx_refresh", data.refresh);
439
+ }
440
+ isRefreshing = false;
441
+ onTokenRefreshed?.(data.access, data.refresh);
442
+ processQueue(null, data.access);
443
+ const retryHeaders = { ...request.config.headers, Authorization: `Bearer ${data.access}` };
444
+ const r = await fetch(request.url, { ...request.config, headers: retryHeaders });
445
+ return r;
446
+ } catch (err) {
447
+ isRefreshing = false;
448
+ await storage.removeItem("tx_access");
449
+ await storage.removeItem("tx_refresh");
450
+ processQueue(err, null);
451
+ onSessionExpired();
452
+ return response;
453
+ }
454
+ }
455
+ return response;
456
+ };
457
+ }
458
+ var DEVICE_INFO_ENDPOINTS = [
459
+ "/login/email/",
460
+ "/login/phone/",
461
+ "/register/",
462
+ "/social/"
463
+ ];
464
+ var DEFAULT_RETRY = {
465
+ maxRetries: 3,
466
+ retryOn429: true,
467
+ retryOnNetworkError: true,
468
+ baseDelayMs: 1e3
469
+ };
470
+ function sleep(ms) {
471
+ return new Promise((resolve) => setTimeout(resolve, ms));
472
+ }
473
+ function createRetryInterceptor(config = {}, logger) {
474
+ const opts = { ...DEFAULT_RETRY, ...config };
475
+ return async (response, request) => {
476
+ const shouldRetry429 = opts.retryOn429 && response.status === 429;
477
+ const shouldRetryServer = response.status >= 500;
478
+ if (!shouldRetry429 && !shouldRetryServer) {
479
+ return response;
480
+ }
481
+ let lastResponse = response;
482
+ for (let attempt = 1; attempt <= opts.maxRetries; attempt++) {
483
+ let delayMs = opts.baseDelayMs * Math.pow(2, attempt - 1);
484
+ const retryAfter = lastResponse.headers.get("Retry-After");
485
+ if (retryAfter) {
486
+ const parsed = Number(retryAfter);
487
+ if (!isNaN(parsed)) {
488
+ delayMs = parsed * 1e3;
489
+ }
490
+ }
491
+ logger?.debug(`[Tenxyte Retry] Attempt ${attempt}/${opts.maxRetries} after ${delayMs}ms for ${request.url}`);
492
+ await sleep(delayMs);
493
+ try {
494
+ const retryResponse = await fetch(request.url, request.config);
495
+ if (retryResponse.status === 429 && opts.retryOn429 && attempt < opts.maxRetries) {
496
+ lastResponse = retryResponse;
497
+ continue;
498
+ }
499
+ if (retryResponse.status >= 500 && attempt < opts.maxRetries) {
500
+ lastResponse = retryResponse;
501
+ continue;
502
+ }
503
+ return retryResponse;
504
+ } catch (err) {
505
+ if (!opts.retryOnNetworkError || attempt >= opts.maxRetries) {
506
+ throw err;
507
+ }
508
+ logger?.warn(`[Tenxyte Retry] Network error on attempt ${attempt}/${opts.maxRetries}`, err);
509
+ }
510
+ }
511
+ return lastResponse;
512
+ };
513
+ }
514
+ function createDeviceInfoInterceptor(override) {
515
+ const fingerprint = buildDeviceInfo(override);
516
+ return (request) => {
517
+ const isPost = !request.method || request.method === "POST";
518
+ const matchesEndpoint = DEVICE_INFO_ENDPOINTS.some((ep) => request.url.includes(ep));
519
+ if (isPost && matchesEndpoint && request.body && typeof request.body === "object") {
520
+ const body = request.body;
521
+ if (!body.device_info) {
522
+ return { ...request, body: { ...body, device_info: fingerprint } };
523
+ }
524
+ }
525
+ return request;
526
+ };
527
+ }
528
+
147
529
  // src/modules/auth.ts
148
530
  var AuthModule = class {
149
- constructor(client) {
531
+ constructor(client, storage, onTokens, onLogout) {
150
532
  this.client = client;
533
+ this.storage = storage;
534
+ this.onTokens = onTokens;
535
+ this.onLogout = onLogout;
536
+ }
537
+ async clearTokens() {
538
+ if (this.storage) {
539
+ await this.storage.removeItem("tx_access");
540
+ await this.storage.removeItem("tx_refresh");
541
+ this.onLogout?.();
542
+ }
543
+ }
544
+ async persistTokens(tokens) {
545
+ if (this.storage) {
546
+ await this.storage.setItem("tx_access", tokens.access_token);
547
+ if (tokens.refresh_token) {
548
+ await this.storage.setItem("tx_refresh", tokens.refresh_token);
549
+ }
550
+ this.onTokens?.(tokens.access_token, tokens.refresh_token);
551
+ }
552
+ return tokens;
151
553
  }
152
554
  /**
153
555
  * Authenticate a user with their email and password.
@@ -156,7 +558,8 @@ var AuthModule = class {
156
558
  * @throws {TenxyteError} If credentials are invalid, or if `2FA_REQUIRED` without a valid `totp_code`.
157
559
  */
158
560
  async loginWithEmail(data) {
159
- return this.client.post("/api/v1/auth/login/email/", data);
561
+ const tokens = await this.client.post("/api/v1/auth/login/email/", data);
562
+ return this.persistTokens(tokens);
160
563
  }
161
564
  /**
162
565
  * Authenticate a user with an international phone number and password.
@@ -164,7 +567,8 @@ var AuthModule = class {
164
567
  * @returns A pair of Access and Refresh tokens.
165
568
  */
166
569
  async loginWithPhone(data) {
167
- return this.client.post("/api/v1/auth/login/phone/", data);
570
+ const tokens = await this.client.post("/api/v1/auth/login/phone/", data);
571
+ return this.persistTokens(tokens);
168
572
  }
169
573
  /**
170
574
  * Registers a new user account.
@@ -172,7 +576,11 @@ var AuthModule = class {
172
576
  * @returns The registered user data or a confirmation message.
173
577
  */
174
578
  async register(data) {
175
- return this.client.post("/api/v1/auth/register/", data);
579
+ const result = await this.client.post("/api/v1/auth/register/", data);
580
+ if (result?.access_token) {
581
+ await this.persistTokens(result);
582
+ }
583
+ return result;
176
584
  }
177
585
  /**
178
586
  * Logout from the current session.
@@ -180,14 +588,26 @@ var AuthModule = class {
180
588
  * @param refreshToken - The refresh token to revoke.
181
589
  */
182
590
  async logout(refreshToken) {
183
- return this.client.post("/api/v1/auth/logout/", { refresh_token: refreshToken });
591
+ await this.client.post("/api/v1/auth/logout/", { refresh_token: refreshToken });
592
+ await this.clearTokens();
184
593
  }
185
594
  /**
186
595
  * Logout from all sessions across all devices.
187
596
  * Revokes all refresh tokens currently assigned to the user.
188
597
  */
189
598
  async logoutAll() {
190
- return this.client.post("/api/v1/auth/logout/all/");
599
+ await this.client.post("/api/v1/auth/logout/all/");
600
+ await this.clearTokens();
601
+ }
602
+ /**
603
+ * Manually refresh the access token using a valid refresh token.
604
+ * The refresh token is automatically rotated for improved security.
605
+ * @param refreshToken - The current refresh token.
606
+ * @returns A new token pair (access + rotated refresh).
607
+ */
608
+ async refreshToken(refreshToken) {
609
+ const tokens = await this.client.post("/api/v1/auth/refresh/", { refresh_token: refreshToken });
610
+ return this.persistTokens(tokens);
191
611
  }
192
612
  /**
193
613
  * Request a Magic Link for passwordless sign-in.
@@ -202,7 +622,8 @@ var AuthModule = class {
202
622
  * @returns A session token pair if the token is valid and unexpired.
203
623
  */
204
624
  async verifyMagicLink(token) {
205
- return this.client.get(`/api/v1/auth/magic-link/verify/`, { params: { token } });
625
+ const tokens = await this.client.get(`/api/v1/auth/magic-link/verify/`, { params: { token } });
626
+ return this.persistTokens(tokens);
206
627
  }
207
628
  /**
208
629
  * Submits OAuth2 Social Authentication payloads to the backend.
@@ -212,7 +633,8 @@ var AuthModule = class {
212
633
  * @returns An active session token pair.
213
634
  */
214
635
  async loginWithSocial(provider, data) {
215
- return this.client.post(`/api/v1/auth/social/${provider}/`, data);
636
+ const tokens = await this.client.post(`/api/v1/auth/social/${provider}/`, data);
637
+ return this.persistTokens(tokens);
216
638
  }
217
639
  /**
218
640
  * Handle Social Auth Callbacks (Authorization Code flow).
@@ -222,9 +644,10 @@ var AuthModule = class {
222
644
  * @returns An active session token pair after successful code exchange.
223
645
  */
224
646
  async handleSocialCallback(provider, code, redirectUri) {
225
- return this.client.get(`/api/v1/auth/social/${provider}/callback/`, {
647
+ const tokens = await this.client.get(`/api/v1/auth/social/${provider}/callback/`, {
226
648
  params: { code, redirect_uri: redirectUri }
227
649
  });
650
+ return this.persistTokens(tokens);
228
651
  }
229
652
  };
230
653
 
@@ -446,7 +869,7 @@ function decodeJwt(token) {
446
869
  if (parts.length !== 3) {
447
870
  return null;
448
871
  }
449
- let base64Url = parts[1];
872
+ const base64Url = parts[1];
450
873
  if (!base64Url) return null;
451
874
  let base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
452
875
  while (base64.length % 4) {
@@ -462,7 +885,7 @@ function decodeJwt(token) {
462
885
  jsonPayload = Buffer.from(base64, "base64").toString("utf8");
463
886
  }
464
887
  return JSON.parse(jsonPayload);
465
- } catch (e) {
888
+ } catch (_e) {
466
889
  return null;
467
890
  }
468
891
  }
@@ -569,12 +992,7 @@ var RbacModule = class {
569
992
  return this.client.post(`/api/v1/auth/roles/${roleId}/permissions/`, { permission_codes });
570
993
  }
571
994
  async removePermissionsFromRole(roleId, permission_codes) {
572
- return this.client.delete(`/api/v1/auth/roles/${roleId}/permissions/`, {
573
- // Note: DELETE request with body is supported via our fetch wrapper if enabled,
574
- // or we might need to rely on query strings. The schema specifies body or query.
575
- // Let's pass it in body via a custom config or URL params.
576
- body: { permission_codes }
577
- });
995
+ return this.client.delete(`/api/v1/auth/roles/${roleId}/permissions/`, { permission_codes });
578
996
  }
579
997
  // --- Permissions CRUD --- //
580
998
  /** Enumerates all available fine-grained Permissions inside this Tenant scope. */
@@ -598,6 +1016,20 @@ var RbacModule = class {
598
1016
  return this.client.delete(`/api/v1/auth/permissions/${permissionId}/`);
599
1017
  }
600
1018
  // --- Direct Assignment (Users) --- //
1019
+ /**
1020
+ * Retrieve all roles assigned to a specific user.
1021
+ * @param userId - The target user ID.
1022
+ */
1023
+ async getUserRoles(userId) {
1024
+ return this.client.get(`/api/v1/auth/users/${userId}/roles/`);
1025
+ }
1026
+ /**
1027
+ * Retrieve all permissions directly assigned to a specific user (excluding role-based permissions).
1028
+ * @param userId - The target user ID.
1029
+ */
1030
+ async getUserPermissions(userId) {
1031
+ return this.client.get(`/api/v1/auth/users/${userId}/permissions/`);
1032
+ }
601
1033
  /**
602
1034
  * Attach a given Role globally to a user entity.
603
1035
  * Use sparingly if B2B multi-tenancy contexts are preferred.
@@ -609,7 +1041,7 @@ var RbacModule = class {
609
1041
  * Unbind a global Role from a user entity.
610
1042
  */
611
1043
  async removeRoleFromUser(userId, roleCode) {
612
- return this.client.delete(`/api/v1/auth/users/${userId}/roles/`, {
1044
+ return this.client.delete(`/api/v1/auth/users/${userId}/roles/`, void 0, {
613
1045
  params: { role_code: roleCode }
614
1046
  });
615
1047
  }
@@ -623,9 +1055,7 @@ var RbacModule = class {
623
1055
  * Ad-Hoc strip direct granular Permissions bindings from a specific User.
624
1056
  */
625
1057
  async removePermissionsFromUser(userId, permissionCodes) {
626
- return this.client.delete(`/api/v1/auth/users/${userId}/permissions/`, {
627
- body: { permission_codes: permissionCodes }
628
- });
1058
+ return this.client.delete(`/api/v1/auth/users/${userId}/permissions/`, { permission_codes: permissionCodes });
629
1059
  }
630
1060
  };
631
1061
 
@@ -652,6 +1082,7 @@ var UserModule = class {
652
1082
  return this.client.patch("/api/v1/auth/me/", formData);
653
1083
  }
654
1084
  /**
1085
+ * @deprecated Use `gdpr.requestAccountDeletion()` instead. This proxy will be removed in a future release.
655
1086
  * Trigger self-deletion of an entire account data boundary.
656
1087
  * @param password - Requires the active system password as destructive proof of intent.
657
1088
  * @param otpCode - (Optional) If an OTP was queried prior to attempting account deletion.
@@ -662,6 +1093,13 @@ var UserModule = class {
662
1093
  otp_code: otpCode
663
1094
  });
664
1095
  }
1096
+ /**
1097
+ * Retrieve the roles and permissions of the currently authenticated user.
1098
+ * @returns An object containing `roles[]` and `permissions[]`.
1099
+ */
1100
+ async getMyRoles() {
1101
+ return this.client.get("/api/v1/auth/me/roles/");
1102
+ }
665
1103
  // --- Admin Actions Mapping --- //
666
1104
  /** (Admin only) Lists users paginated matching criteria. */
667
1105
  async listUsers(params) {
@@ -786,8 +1224,9 @@ var B2bModule = class {
786
1224
 
787
1225
  // src/modules/ai.ts
788
1226
  var AiModule = class {
789
- constructor(client) {
1227
+ constructor(client, logger) {
790
1228
  this.client = client;
1229
+ this.logger = logger;
791
1230
  this.client.addRequestInterceptor((config) => {
792
1231
  const headers = { ...config.headers };
793
1232
  if (this.agentToken) {
@@ -798,12 +1237,12 @@ var AiModule = class {
798
1237
  }
799
1238
  return { ...config, headers };
800
1239
  });
801
- this.client.addResponseInterceptor(async (response, request) => {
1240
+ this.client.addResponseInterceptor(async (response, _request) => {
802
1241
  if (response.status === 202) {
803
1242
  const cloned = response.clone();
804
1243
  try {
805
1244
  const data = await cloned.json();
806
- console.debug("[Tenxyte AI] Received 202 Awaiting Approval:", data);
1245
+ this.logger?.debug("[Tenxyte AI] Received 202 Awaiting Approval:", data);
807
1246
  } catch {
808
1247
  }
809
1248
  } else if (response.status === 403) {
@@ -811,9 +1250,9 @@ var AiModule = class {
811
1250
  try {
812
1251
  const data = await cloned.json();
813
1252
  if (data.code === "BUDGET_EXCEEDED") {
814
- console.warn("[Tenxyte AI] Network responded with Budget Exceeded for Agent.");
1253
+ this.logger?.warn("[Tenxyte AI] Network responded with Budget Exceeded for Agent.");
815
1254
  } else if (data.status === "suspended") {
816
- console.warn("[Tenxyte AI] Circuit breaker open for Agent.");
1255
+ this.logger?.warn("[Tenxyte AI] Circuit breaker open for Agent.");
817
1256
  }
818
1257
  } catch {
819
1258
  }
@@ -823,6 +1262,7 @@ var AiModule = class {
823
1262
  }
824
1263
  agentToken = null;
825
1264
  traceId = null;
1265
+ logger;
826
1266
  // ─── AgentToken Lifecycle ───
827
1267
  /**
828
1268
  * Create an AgentToken granting specific deterministic limits to an AI Agent.
@@ -902,8 +1342,340 @@ var AiModule = class {
902
1342
  }
903
1343
  };
904
1344
 
1345
+ // src/modules/applications.ts
1346
+ var ApplicationsModule = class {
1347
+ constructor(client) {
1348
+ this.client = client;
1349
+ }
1350
+ /**
1351
+ * List all registered applications (paginated).
1352
+ * @param params - Optional filters: `search`, `is_active`, `ordering`, `page`, `page_size`.
1353
+ * @returns A paginated list of applications.
1354
+ */
1355
+ async listApplications(params) {
1356
+ return this.client.get("/api/v1/auth/applications/", {
1357
+ params
1358
+ });
1359
+ }
1360
+ /**
1361
+ * Create a new application.
1362
+ * @param data - The application name and optional description.
1363
+ * @returns The created application including one-time `client_secret`.
1364
+ */
1365
+ async createApplication(data) {
1366
+ return this.client.post("/api/v1/auth/applications/", data);
1367
+ }
1368
+ /**
1369
+ * Get a single application by its ID.
1370
+ * @param appId - The application ID.
1371
+ * @returns The application details (secret is never included).
1372
+ */
1373
+ async getApplication(appId) {
1374
+ return this.client.get(`/api/v1/auth/applications/${appId}/`);
1375
+ }
1376
+ /**
1377
+ * Fully update an application (PUT — all fields replaced).
1378
+ * @param appId - The application ID.
1379
+ * @param data - The full updated application data.
1380
+ * @returns The updated application.
1381
+ */
1382
+ async updateApplication(appId, data) {
1383
+ return this.client.put(`/api/v1/auth/applications/${appId}/`, data);
1384
+ }
1385
+ /**
1386
+ * Partially update an application (PATCH — only provided fields are changed).
1387
+ * @param appId - The application ID.
1388
+ * @param data - The fields to update.
1389
+ * @returns The updated application.
1390
+ */
1391
+ async patchApplication(appId, data) {
1392
+ return this.client.patch(`/api/v1/auth/applications/${appId}/`, data);
1393
+ }
1394
+ /**
1395
+ * Delete an application permanently.
1396
+ * @param appId - The application ID.
1397
+ */
1398
+ async deleteApplication(appId) {
1399
+ return this.client.delete(`/api/v1/auth/applications/${appId}/`);
1400
+ }
1401
+ /**
1402
+ * Regenerate credentials for an application.
1403
+ * **Warning:** Old credentials are immediately invalidated. The new secret is shown only once.
1404
+ * @param appId - The application ID.
1405
+ * @param confirmation - Must be the string `"REGENERATE"` to confirm the irreversible action.
1406
+ * @returns The new credentials (access_key + access_secret shown once).
1407
+ */
1408
+ async regenerateCredentials(appId, confirmation = "REGENERATE") {
1409
+ return this.client.post(`/api/v1/auth/applications/${appId}/regenerate/`, { confirmation });
1410
+ }
1411
+ };
1412
+
1413
+ // src/modules/admin.ts
1414
+ var AdminModule = class {
1415
+ constructor(client) {
1416
+ this.client = client;
1417
+ }
1418
+ // ─── Audit Logs ───
1419
+ /**
1420
+ * List audit log entries (paginated).
1421
+ * @param params - Optional filters and pagination.
1422
+ */
1423
+ async listAuditLogs(params) {
1424
+ return this.client.get("/api/v1/auth/admin/audit-logs/", {
1425
+ params
1426
+ });
1427
+ }
1428
+ /**
1429
+ * Get a single audit log entry by ID.
1430
+ * @param logId - The audit log entry ID.
1431
+ */
1432
+ async getAuditLog(logId) {
1433
+ return this.client.get(`/api/v1/auth/admin/audit-logs/${logId}/`);
1434
+ }
1435
+ // ─── Login Attempts ───
1436
+ /**
1437
+ * List login attempt records (paginated).
1438
+ * @param params - Optional filters and pagination.
1439
+ */
1440
+ async listLoginAttempts(params) {
1441
+ return this.client.get("/api/v1/auth/admin/login-attempts/", {
1442
+ params
1443
+ });
1444
+ }
1445
+ // ─── Blacklisted Tokens ───
1446
+ /**
1447
+ * List blacklisted (revoked) JWT tokens (paginated).
1448
+ * @param params - Optional filters and pagination.
1449
+ */
1450
+ async listBlacklistedTokens(params) {
1451
+ return this.client.get("/api/v1/auth/admin/blacklisted-tokens/", {
1452
+ params
1453
+ });
1454
+ }
1455
+ /**
1456
+ * Remove expired blacklisted tokens.
1457
+ * @returns A summary object with cleanup results.
1458
+ */
1459
+ async cleanupBlacklistedTokens() {
1460
+ return this.client.post("/api/v1/auth/admin/blacklisted-tokens/cleanup/");
1461
+ }
1462
+ // ─── Refresh Tokens ───
1463
+ /**
1464
+ * List refresh tokens (admin view — token values are hidden).
1465
+ * @param params - Optional filters and pagination.
1466
+ */
1467
+ async listRefreshTokens(params) {
1468
+ return this.client.get("/api/v1/auth/admin/refresh-tokens/", {
1469
+ params
1470
+ });
1471
+ }
1472
+ /**
1473
+ * Revoke a specific refresh token.
1474
+ * @param tokenId - The refresh token ID.
1475
+ * @returns The updated refresh token record.
1476
+ */
1477
+ async revokeRefreshToken(tokenId) {
1478
+ return this.client.post(`/api/v1/auth/admin/refresh-tokens/${tokenId}/revoke/`);
1479
+ }
1480
+ };
1481
+
1482
+ // src/modules/gdpr.ts
1483
+ var GdprModule = class {
1484
+ constructor(client) {
1485
+ this.client = client;
1486
+ }
1487
+ // ─── User-facing ───
1488
+ /**
1489
+ * Request account deletion (GDPR-compliant).
1490
+ * Initiates a 30-day grace period during which the user can cancel.
1491
+ * @param data - Password (+ optional OTP code and reason).
1492
+ */
1493
+ async requestAccountDeletion(data) {
1494
+ return this.client.post("/api/v1/auth/request-account-deletion/", data);
1495
+ }
1496
+ /**
1497
+ * Confirm the account deletion using the token received by email.
1498
+ * The token is valid for 24 hours. After confirmation the account enters the 30-day grace period.
1499
+ * @param token - The confirmation token from the email.
1500
+ */
1501
+ async confirmAccountDeletion(token) {
1502
+ return this.client.post("/api/v1/auth/confirm-account-deletion/", { token });
1503
+ }
1504
+ /**
1505
+ * Cancel a pending account deletion during the grace period.
1506
+ * The account is immediately reactivated.
1507
+ * @param password - The current password for security.
1508
+ */
1509
+ async cancelAccountDeletion(password) {
1510
+ return this.client.post("/api/v1/auth/cancel-account-deletion/", { password });
1511
+ }
1512
+ /**
1513
+ * Get the deletion status for the current user.
1514
+ * Includes pending, confirmed, or cancelled requests.
1515
+ */
1516
+ async getAccountDeletionStatus() {
1517
+ return this.client.get("/api/v1/auth/account-deletion-status/");
1518
+ }
1519
+ /**
1520
+ * Export all personal data (GDPR right to data portability).
1521
+ * @param password - The current password for security.
1522
+ */
1523
+ async exportUserData(password) {
1524
+ return this.client.post("/api/v1/auth/export-user-data/", { password });
1525
+ }
1526
+ // ─── Admin-facing ───
1527
+ /**
1528
+ * List deletion requests (admin, paginated).
1529
+ * @param params - Optional filters and pagination.
1530
+ */
1531
+ async listDeletionRequests(params) {
1532
+ return this.client.get("/api/v1/auth/admin/deletion-requests/", {
1533
+ params
1534
+ });
1535
+ }
1536
+ /**
1537
+ * Get a single deletion request by ID.
1538
+ * @param requestId - The deletion request ID.
1539
+ */
1540
+ async getDeletionRequest(requestId) {
1541
+ return this.client.get(`/api/v1/auth/admin/deletion-requests/${requestId}/`);
1542
+ }
1543
+ /**
1544
+ * Process (execute) a confirmed deletion request.
1545
+ * **WARNING:** This is irreversible and permanently destroys all user data.
1546
+ * @param requestId - The deletion request ID.
1547
+ * @param data - Must include `{ confirmation: "PERMANENTLY DELETE" }`.
1548
+ */
1549
+ async processDeletionRequest(requestId, data) {
1550
+ return this.client.post(`/api/v1/auth/admin/deletion-requests/${requestId}/process/`, data);
1551
+ }
1552
+ /**
1553
+ * Batch-process all confirmed deletion requests whose 30-day grace period has expired.
1554
+ * Typically run by a daily cron job.
1555
+ */
1556
+ async processExpiredDeletions() {
1557
+ return this.client.post("/api/v1/auth/admin/deletion-requests/process-expired/");
1558
+ }
1559
+ };
1560
+
1561
+ // src/modules/dashboard.ts
1562
+ var DashboardModule = class {
1563
+ constructor(client) {
1564
+ this.client = client;
1565
+ }
1566
+ /**
1567
+ * Get global cross-module dashboard statistics.
1568
+ * Data varies based on the organizational context (`X-Org-Slug`) and permissions.
1569
+ * Covers users, authentication, applications, security, and GDPR metrics.
1570
+ * Charts span the last 7 days with previous-period comparisons.
1571
+ * @param params - Optional period and comparison flag.
1572
+ */
1573
+ async getStats(params) {
1574
+ return this.client.get("/api/v1/auth/dashboard/stats/", {
1575
+ params
1576
+ });
1577
+ }
1578
+ /**
1579
+ * Get authentication-specific statistics.
1580
+ * Includes login stats, methods breakdown, registrations, tokens, top failure reasons, and 7-day graphs.
1581
+ */
1582
+ async getAuthStats() {
1583
+ return this.client.get("/api/v1/auth/dashboard/auth/");
1584
+ }
1585
+ /**
1586
+ * Get security-specific statistics.
1587
+ * Includes audit summary, blacklisted tokens, suspicious activity, and 2FA adoption.
1588
+ */
1589
+ async getSecurityStats() {
1590
+ return this.client.get("/api/v1/auth/dashboard/security/");
1591
+ }
1592
+ /**
1593
+ * Get GDPR-specific statistics.
1594
+ * Includes deletion requests by status and data export metrics.
1595
+ */
1596
+ async getGdprStats() {
1597
+ return this.client.get("/api/v1/auth/dashboard/gdpr/");
1598
+ }
1599
+ /**
1600
+ * Get organization-specific statistics.
1601
+ * Includes organizations, members, roles, and top organizations.
1602
+ */
1603
+ async getOrganizationStats() {
1604
+ return this.client.get("/api/v1/auth/dashboard/organizations/");
1605
+ }
1606
+ };
1607
+
1608
+ // src/utils/events.ts
1609
+ var EventEmitter = class {
1610
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
1611
+ events;
1612
+ constructor() {
1613
+ this.events = /* @__PURE__ */ new Map();
1614
+ }
1615
+ /**
1616
+ * Subscribe to an event.
1617
+ * @param event The event name
1618
+ * @param callback The callback function
1619
+ * @returns Unsubscribe function
1620
+ */
1621
+ on(event, callback) {
1622
+ if (!this.events.has(event)) {
1623
+ this.events.set(event, []);
1624
+ }
1625
+ this.events.get(event).push(callback);
1626
+ return () => this.off(event, callback);
1627
+ }
1628
+ /**
1629
+ * Unsubscribe from an event.
1630
+ * @param event The event name
1631
+ * @param callback The exact callback function that was passed to .on()
1632
+ */
1633
+ off(event, callback) {
1634
+ const callbacks = this.events.get(event);
1635
+ if (!callbacks) return;
1636
+ const index = callbacks.indexOf(callback);
1637
+ if (index !== -1) {
1638
+ callbacks.splice(index, 1);
1639
+ }
1640
+ }
1641
+ /**
1642
+ * Subscribe to an event exactly once.
1643
+ */
1644
+ once(event, callback) {
1645
+ const wrapped = (payload) => {
1646
+ this.off(event, wrapped);
1647
+ callback(payload);
1648
+ };
1649
+ return this.on(event, wrapped);
1650
+ }
1651
+ /**
1652
+ * Emit an event internally.
1653
+ */
1654
+ emit(event, payload) {
1655
+ const callbacks = this.events.get(event);
1656
+ if (!callbacks) return;
1657
+ const copy = [...callbacks];
1658
+ for (const callback of copy) {
1659
+ try {
1660
+ callback(payload);
1661
+ } catch (err) {
1662
+ console.error(`[Tenxyte EventEmitter] Error executing callback for event ${String(event)}`, err);
1663
+ }
1664
+ }
1665
+ }
1666
+ removeAllListeners() {
1667
+ this.events.clear();
1668
+ }
1669
+ };
1670
+
905
1671
  // src/client.ts
906
1672
  var TenxyteClient = class {
1673
+ /** Fully resolved configuration (all defaults applied). */
1674
+ config;
1675
+ /** Persistent token storage back-end (defaults to MemoryStorage). */
1676
+ storage;
1677
+ /** Shared mutable context used by interceptors (org slug, agent trace ID). */
1678
+ context;
907
1679
  /** The core HTTP wrapper handling network interception and parsing */
908
1680
  http;
909
1681
  /** Authentication module (Login, Signup, Magic link, session handling) */
@@ -918,8 +1690,22 @@ var TenxyteClient = class {
918
1690
  b2b;
919
1691
  /** AIRS - AI Responsibility & Security module (Agent tokens, Circuit breakers, HITL) */
920
1692
  ai;
1693
+ /** Applications module (API client CRUD, credential management) */
1694
+ applications;
1695
+ /** Admin module (audit logs, login attempts, blacklisted tokens, refresh tokens) */
1696
+ admin;
1697
+ /** GDPR module (account deletion, data export, deletion request management) */
1698
+ gdpr;
1699
+ /** Dashboard module (global, auth, security, GDPR, organization statistics) */
1700
+ dashboard;
1701
+ /** Internal event emitter used via composition. */
1702
+ emitter;
921
1703
  /**
922
1704
  * Initializes the SDK with connection details for your Tenxyte-powered API.
1705
+ *
1706
+ * Accepts the full TenxyteClientConfig. Minimal usage with just { baseUrl }
1707
+ * is still supported for backward compatibility.
1708
+ *
923
1709
  * @param options Configuration options including `baseUrl` and custom headers like `X-Access-Key`
924
1710
  *
925
1711
  * @example
@@ -931,22 +1717,166 @@ var TenxyteClient = class {
931
1717
  * ```
932
1718
  */
933
1719
  constructor(options) {
934
- this.http = new TenxyteHttpClient(options);
935
- this.auth = new AuthModule(this.http);
936
- this.security = new SecurityModule(this.http);
1720
+ this.config = resolveConfig(options);
1721
+ this.storage = this.config.storage;
1722
+ this.emitter = new EventEmitter();
1723
+ this.context = { activeOrgSlug: null, agentTraceId: null };
1724
+ this.http = new TenxyteHttpClient({
1725
+ baseUrl: this.config.baseUrl,
1726
+ headers: this.config.headers,
1727
+ timeoutMs: this.config.timeoutMs
1728
+ });
1729
+ this.http.addRequestInterceptor(createAuthInterceptor(this.storage, this.context));
1730
+ if (this.config.autoDeviceInfo) {
1731
+ this.http.addRequestInterceptor(createDeviceInfoInterceptor(this.config.deviceInfoOverride));
1732
+ }
1733
+ if (this.config.retryConfig) {
1734
+ this.http.addResponseInterceptor(
1735
+ createRetryInterceptor(this.config.retryConfig, this.config.logger)
1736
+ );
1737
+ }
1738
+ if (this.config.autoRefresh) {
1739
+ this.http.addResponseInterceptor(
1740
+ createRefreshInterceptor(
1741
+ this.http,
1742
+ this.storage,
1743
+ () => {
1744
+ this.emit("session:expired", void 0);
1745
+ this.config.onSessionExpired?.();
1746
+ },
1747
+ (accessToken, refreshToken) => {
1748
+ this.rbac.setToken(accessToken);
1749
+ this.emit("token:refreshed", { accessToken });
1750
+ this.emit("token:stored", { accessToken, refreshToken });
1751
+ }
1752
+ )
1753
+ );
1754
+ }
937
1755
  this.rbac = new RbacModule(this.http);
1756
+ this.auth = new AuthModule(
1757
+ this.http,
1758
+ this.storage,
1759
+ (accessToken, refreshToken) => {
1760
+ this.rbac.setToken(accessToken);
1761
+ this.emit("token:stored", { accessToken, refreshToken });
1762
+ },
1763
+ () => {
1764
+ this.rbac.setToken(null);
1765
+ this.emit("session:expired", void 0);
1766
+ }
1767
+ );
1768
+ this.security = new SecurityModule(this.http);
938
1769
  this.user = new UserModule(this.http);
939
1770
  this.b2b = new B2bModule(this.http);
940
- this.ai = new AiModule(this.http);
1771
+ this.ai = new AiModule(this.http, this.config.logger);
1772
+ this.applications = new ApplicationsModule(this.http);
1773
+ this.admin = new AdminModule(this.http);
1774
+ this.gdpr = new GdprModule(this.http);
1775
+ this.dashboard = new DashboardModule(this.http);
1776
+ }
1777
+ // ─── Event delegation ───
1778
+ /** Subscribe to an SDK event. Returns an unsubscribe function. */
1779
+ on(event, callback) {
1780
+ return this.emitter.on(event, callback);
1781
+ }
1782
+ /** Subscribe to an SDK event exactly once. Returns an unsubscribe function. */
1783
+ once(event, callback) {
1784
+ return this.emitter.once(event, callback);
1785
+ }
1786
+ /** Unsubscribe a previously registered callback from an SDK event. */
1787
+ off(event, callback) {
1788
+ this.emitter.off(event, callback);
1789
+ }
1790
+ /** Emit an SDK event (internal use). */
1791
+ emit(event, payload) {
1792
+ this.emitter.emit(event, payload);
1793
+ }
1794
+ // ─── High-level helpers ───
1795
+ /**
1796
+ * Check whether a valid (non-expired) access token exists in storage.
1797
+ * Performs a synchronous JWT expiry check — no network call.
1798
+ */
1799
+ async isAuthenticated() {
1800
+ const token = await this.storage.getItem("tx_access");
1801
+ if (!token) return false;
1802
+ return !this.isTokenExpiredSync(token);
1803
+ }
1804
+ /**
1805
+ * Return the current access token from storage, or `null` if absent.
1806
+ */
1807
+ async getAccessToken() {
1808
+ return this.storage.getItem("tx_access");
1809
+ }
1810
+ /**
1811
+ * Decode the current access token and return the JWT payload.
1812
+ * Returns `null` if no token is stored or if decoding fails.
1813
+ * No network call is made — this reads from the cached JWT.
1814
+ */
1815
+ async getCurrentUser() {
1816
+ const token = await this.storage.getItem("tx_access");
1817
+ if (!token) return null;
1818
+ return decodeJwt(token);
1819
+ }
1820
+ /**
1821
+ * Check whether the stored access token is expired without making a network call.
1822
+ * Returns `true` if expired or if no token is present.
1823
+ */
1824
+ async isTokenExpired() {
1825
+ const token = await this.storage.getItem("tx_access");
1826
+ if (!token) return true;
1827
+ return this.isTokenExpiredSync(token);
1828
+ }
1829
+ /** Synchronous helper: checks JWT `exp` claim against current time. */
1830
+ isTokenExpiredSync(token) {
1831
+ const decoded = decodeJwt(token);
1832
+ if (!decoded?.exp) return true;
1833
+ return decoded.exp * 1e3 < Date.now() - 3e4;
1834
+ }
1835
+ // ─── Framework wrapper interface ───
1836
+ /**
1837
+ * Returns a synchronous snapshot of the SDK state.
1838
+ * Designed for consumption by framework wrappers (React, Vue, etc.).
1839
+ * Note: This is async because storage access may be async.
1840
+ */
1841
+ async getState() {
1842
+ const token = await this.storage.getItem("tx_access");
1843
+ const isAuthenticated = token ? !this.isTokenExpiredSync(token) : false;
1844
+ const user = token ? decodeJwt(token) : null;
1845
+ return {
1846
+ isAuthenticated,
1847
+ user,
1848
+ accessToken: token,
1849
+ activeOrg: this.context.activeOrgSlug,
1850
+ isAgentMode: this.ai.isAgentMode()
1851
+ };
941
1852
  }
942
1853
  };
943
1854
  // Annotate the CommonJS export names for ESM import in node:
944
1855
  0 && (module.exports = {
1856
+ AdminModule,
1857
+ AiModule,
1858
+ ApplicationsModule,
945
1859
  AuthModule,
1860
+ B2bModule,
1861
+ CookieStorage,
1862
+ DashboardModule,
1863
+ EventEmitter,
1864
+ GdprModule,
1865
+ LocalStorage,
1866
+ MemoryStorage,
1867
+ NOOP_LOGGER,
946
1868
  RbacModule,
1869
+ SDK_VERSION,
947
1870
  SecurityModule,
948
1871
  TenxyteClient,
949
1872
  TenxyteHttpClient,
950
- UserModule
1873
+ UserModule,
1874
+ buildDeviceInfo,
1875
+ createAuthInterceptor,
1876
+ createDeviceInfoInterceptor,
1877
+ createRefreshInterceptor,
1878
+ createRetryInterceptor,
1879
+ decodeJwt,
1880
+ resolveConfig
951
1881
  });
952
1882
  //# sourceMappingURL=index.cjs.map