@tenxyte/core 0.1.5 → 0.9.0

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.
@@ -0,0 +1,25 @@
1
+ export function bufferToBase64url(buffer: ArrayBuffer | Uint8Array): string {
2
+ const bytes = new Uint8Array(buffer);
3
+ let binary = '';
4
+ for (let i = 0; i < bytes.byteLength; i++) {
5
+ binary += String.fromCharCode(bytes[i]);
6
+ }
7
+ return btoa(binary)
8
+ .replace(/\+/g, '-')
9
+ .replace(/\//g, '_')
10
+ .replace(/=/g, '');
11
+ }
12
+
13
+ export function base64urlToBuffer(base64url: string): Uint8Array {
14
+ const base64 = base64url
15
+ .replace(/-/g, '+')
16
+ .replace(/_/g, '/');
17
+ const padLen = (4 - (base64.length % 4)) % 4;
18
+ const padded = base64 + '='.repeat(padLen);
19
+ const binary = atob(padded);
20
+ const bytes = new Uint8Array(binary.length);
21
+ for (let i = 0; i < binary.length; i++) {
22
+ bytes[i] = binary.charCodeAt(i);
23
+ }
24
+ return bytes;
25
+ }
@@ -1,94 +1,94 @@
1
- /**
2
- * Helper utility to build the device fingerprint required by Tenxyte security features.
3
- * Format: `v=1|os=windows;osv=11|device=desktop|arch=x64|app=tenxyte;appv=1.0.0|runtime=chrome;rtv=122|tz=Europe/Paris`
4
- */
5
- export interface CustomDeviceInfo {
6
- os?: string;
7
- osVersion?: string;
8
- device?: string;
9
- arch?: string;
10
- app?: string;
11
- appVersion?: string;
12
- runtime?: string;
13
- runtimeVersion?: string;
14
- timezone?: string;
15
- }
16
-
17
- export function buildDeviceInfo(customInfo: CustomDeviceInfo = {}): string {
18
- // Try to determine automatically from navigator
19
- const autoInfo = getAutoInfo();
20
-
21
- const v = '1';
22
- const os = customInfo.os || autoInfo.os;
23
- const osv = customInfo.osVersion || autoInfo.osVersion;
24
- const device = customInfo.device || autoInfo.device;
25
- const arch = customInfo.arch || autoInfo.arch;
26
- const app = customInfo.app || autoInfo.app;
27
- const appv = customInfo.appVersion || autoInfo.appVersion;
28
- const runtime = customInfo.runtime || autoInfo.runtime;
29
- const rtv = customInfo.runtimeVersion || autoInfo.runtimeVersion;
30
- const tz = customInfo.timezone || autoInfo.timezone;
31
-
32
- const parts = [
33
- `v=${v}`,
34
- `os=${os}` + (osv ? `;osv=${osv}` : ''),
35
- `device=${device}`,
36
- arch ? `arch=${arch}` : '',
37
- app ? `app=${app}${appv ? `;appv=${appv}` : ''}` : '',
38
- `runtime=${runtime}` + (rtv ? `;rtv=${rtv}` : ''),
39
- tz ? `tz=${tz}` : ''
40
- ];
41
-
42
- return parts.filter(Boolean).join('|');
43
- }
44
-
45
- function getAutoInfo() {
46
- const info = {
47
- os: 'unknown',
48
- osVersion: '',
49
- device: 'desktop', // default
50
- arch: '',
51
- app: 'sdk',
52
- appVersion: '0.1.0',
53
- runtime: 'unknown',
54
- runtimeVersion: '',
55
- timezone: ''
56
- };
57
-
58
- try {
59
- if (typeof Intl !== 'undefined') {
60
- info.timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
61
- }
62
-
63
- if (typeof process !== 'undefined' && process.version) {
64
- info.runtime = 'node';
65
- info.runtimeVersion = process.version;
66
- info.os = process.platform;
67
- info.arch = process.arch;
68
- info.device = 'server';
69
- } else if (typeof window !== 'undefined' && window.navigator) {
70
- const ua = window.navigator.userAgent.toLowerCase();
71
-
72
- // Basic OS detection
73
- if (ua.includes('windows')) info.os = 'windows';
74
- else if (ua.includes('mac')) info.os = 'macos';
75
- else if (ua.includes('linux')) info.os = 'linux';
76
- else if (ua.includes('android')) info.os = 'android';
77
- else if (ua.includes('ios') || ua.includes('iphone') || ua.includes('ipad')) info.os = 'ios';
78
-
79
- // Basic Device Type
80
- if (/mobi|android|touch|mini/i.test(ua)) info.device = 'mobile';
81
- if (/tablet|ipad/i.test(ua)) info.device = 'tablet';
82
-
83
- // Basic Runtime (Browser)
84
- if (ua.includes('firefox')) info.runtime = 'firefox';
85
- else if (ua.includes('edg/')) info.runtime = 'edge';
86
- else if (ua.includes('chrome')) info.runtime = 'chrome';
87
- else if (ua.includes('safari')) info.runtime = 'safari';
88
- }
89
- } catch (e) {
90
- // Ignore context extraction errors
91
- }
92
-
93
- return info;
94
- }
1
+ /**
2
+ * Helper utility to build the device fingerprint required by Tenxyte security features.
3
+ * Format: `v=1|os=windows;osv=11|device=desktop|arch=x64|app=tenxyte;appv=1.0.0|runtime=chrome;rtv=122|tz=Europe/Paris`
4
+ */
5
+ export interface CustomDeviceInfo {
6
+ os?: string;
7
+ osVersion?: string;
8
+ device?: string;
9
+ arch?: string;
10
+ app?: string;
11
+ appVersion?: string;
12
+ runtime?: string;
13
+ runtimeVersion?: string;
14
+ timezone?: string;
15
+ }
16
+
17
+ export function buildDeviceInfo(customInfo: CustomDeviceInfo = {}): string {
18
+ // Try to determine automatically from navigator
19
+ const autoInfo = getAutoInfo();
20
+
21
+ const v = '1';
22
+ const os = customInfo.os || autoInfo.os;
23
+ const osv = customInfo.osVersion || autoInfo.osVersion;
24
+ const device = customInfo.device || autoInfo.device;
25
+ const arch = customInfo.arch || autoInfo.arch;
26
+ const app = customInfo.app || autoInfo.app;
27
+ const appv = customInfo.appVersion || autoInfo.appVersion;
28
+ const runtime = customInfo.runtime || autoInfo.runtime;
29
+ const rtv = customInfo.runtimeVersion || autoInfo.runtimeVersion;
30
+ const tz = customInfo.timezone || autoInfo.timezone;
31
+
32
+ const parts = [
33
+ `v=${v}`,
34
+ `os=${os}` + (osv ? `;osv=${osv}` : ''),
35
+ `device=${device}`,
36
+ arch ? `arch=${arch}` : '',
37
+ app ? `app=${app}${appv ? `;appv=${appv}` : ''}` : '',
38
+ `runtime=${runtime}` + (rtv ? `;rtv=${rtv}` : ''),
39
+ tz ? `tz=${tz}` : ''
40
+ ];
41
+
42
+ return parts.filter(Boolean).join('|');
43
+ }
44
+
45
+ function getAutoInfo() {
46
+ const info = {
47
+ os: 'unknown',
48
+ osVersion: '',
49
+ device: 'desktop', // default
50
+ arch: '',
51
+ app: 'sdk',
52
+ appVersion: '0.1.0',
53
+ runtime: 'unknown',
54
+ runtimeVersion: '',
55
+ timezone: ''
56
+ };
57
+
58
+ try {
59
+ if (typeof Intl !== 'undefined') {
60
+ info.timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
61
+ }
62
+
63
+ if (typeof process !== 'undefined' && process.version) {
64
+ info.runtime = 'node';
65
+ info.runtimeVersion = process.version;
66
+ info.os = process.platform;
67
+ info.arch = process.arch;
68
+ info.device = 'server';
69
+ } else if (typeof window !== 'undefined' && window.navigator) {
70
+ const ua = window.navigator.userAgent.toLowerCase();
71
+
72
+ // Basic OS detection
73
+ if (ua.includes('windows')) info.os = 'windows';
74
+ else if (ua.includes('mac')) info.os = 'macos';
75
+ else if (ua.includes('linux')) info.os = 'linux';
76
+ else if (ua.includes('android')) info.os = 'android';
77
+ else if (ua.includes('ios') || ua.includes('iphone') || ua.includes('ipad')) info.os = 'ios';
78
+
79
+ // Basic Device Type
80
+ if (/mobi|android|touch|mini/i.test(ua)) info.device = 'mobile';
81
+ if (/tablet|ipad/i.test(ua)) info.device = 'tablet';
82
+
83
+ // Basic Runtime (Browser)
84
+ if (ua.includes('firefox')) info.runtime = 'firefox';
85
+ else if (ua.includes('edg/')) info.runtime = 'edge';
86
+ else if (ua.includes('chrome')) info.runtime = 'chrome';
87
+ else if (ua.includes('safari')) info.runtime = 'safari';
88
+ }
89
+ } catch (e) {
90
+ // Ignore context extraction errors
91
+ }
92
+
93
+ return info;
94
+ }
@@ -1,71 +1,71 @@
1
- /**
2
- * Lightweight EventEmitter for TenxyteClient.
3
- * Provides `.on`, `.once`, `.off`, and `.emit`.
4
- */
5
- export class EventEmitter<Events extends Record<string, any>> {
6
- private events: Map<keyof Events, Array<Function>>;
7
-
8
- constructor() {
9
- this.events = new Map();
10
- }
11
-
12
- /**
13
- * Subscribe to an event.
14
- * @param event The event name
15
- * @param callback The callback function
16
- * @returns Unsubscribe function
17
- */
18
- on<K extends keyof Events>(event: K, callback: (payload: Events[K]) => void): () => void {
19
- if (!this.events.has(event)) {
20
- this.events.set(event, []);
21
- }
22
- this.events.get(event)!.push(callback);
23
- return () => this.off(event, callback);
24
- }
25
-
26
- /**
27
- * Unsubscribe from an event.
28
- * @param event The event name
29
- * @param callback The exact callback function that was passed to .on()
30
- */
31
- off<K extends keyof Events>(event: K, callback: (payload: Events[K]) => void): void {
32
- const callbacks = this.events.get(event);
33
- if (!callbacks) return;
34
- const index = callbacks.indexOf(callback);
35
- if (index !== -1) {
36
- callbacks.splice(index, 1);
37
- }
38
- }
39
-
40
- /**
41
- * Subscribe to an event exactly once.
42
- */
43
- once<K extends keyof Events>(event: K, callback: (payload: Events[K]) => void): () => void {
44
- const wrapped = (payload: Events[K]) => {
45
- this.off(event, wrapped);
46
- callback(payload);
47
- };
48
- return this.on(event, wrapped);
49
- }
50
-
51
- /**
52
- * Emit an event internally.
53
- */
54
- emit<K extends keyof Events>(event: K, payload: Events[K]): void {
55
- const callbacks = this.events.get(event);
56
- if (!callbacks) return;
57
- // Copy array to prevent mutation issues during emission
58
- const copy = [...callbacks];
59
- for (const callback of copy) {
60
- try {
61
- callback(payload);
62
- } catch (err) {
63
- console.error(`[Tenxyte EventEmitter] Error executing callback for event ${String(event)}`, err);
64
- }
65
- }
66
- }
67
-
68
- removeAllListeners(): void {
69
- this.events.clear();
70
- }
71
- }
1
+ /**
2
+ * Lightweight EventEmitter for TenxyteClient.
3
+ * Provides `.on`, `.once`, `.off`, and `.emit`.
4
+ */
5
+ export class EventEmitter<Events extends Record<string, any>> {
6
+ private events: Map<keyof Events, Array<Function>>;
7
+
8
+ constructor() {
9
+ this.events = new Map();
10
+ }
11
+
12
+ /**
13
+ * Subscribe to an event.
14
+ * @param event The event name
15
+ * @param callback The callback function
16
+ * @returns Unsubscribe function
17
+ */
18
+ on<K extends keyof Events>(event: K, callback: (payload: Events[K]) => void): () => void {
19
+ if (!this.events.has(event)) {
20
+ this.events.set(event, []);
21
+ }
22
+ this.events.get(event)!.push(callback);
23
+ return () => this.off(event, callback);
24
+ }
25
+
26
+ /**
27
+ * Unsubscribe from an event.
28
+ * @param event The event name
29
+ * @param callback The exact callback function that was passed to .on()
30
+ */
31
+ off<K extends keyof Events>(event: K, callback: (payload: Events[K]) => void): void {
32
+ const callbacks = this.events.get(event);
33
+ if (!callbacks) return;
34
+ const index = callbacks.indexOf(callback);
35
+ if (index !== -1) {
36
+ callbacks.splice(index, 1);
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Subscribe to an event exactly once.
42
+ */
43
+ once<K extends keyof Events>(event: K, callback: (payload: Events[K]) => void): () => void {
44
+ const wrapped = (payload: Events[K]) => {
45
+ this.off(event, wrapped);
46
+ callback(payload);
47
+ };
48
+ return this.on(event, wrapped);
49
+ }
50
+
51
+ /**
52
+ * Emit an event internally.
53
+ */
54
+ emit<K extends keyof Events>(event: K, payload: Events[K]): void {
55
+ const callbacks = this.events.get(event);
56
+ if (!callbacks) return;
57
+ // Copy array to prevent mutation issues during emission
58
+ const copy = [...callbacks];
59
+ for (const callback of copy) {
60
+ try {
61
+ callback(payload);
62
+ } catch (err) {
63
+ console.error(`[Tenxyte EventEmitter] Error executing callback for event ${String(event)}`, err);
64
+ }
65
+ }
66
+ }
67
+
68
+ removeAllListeners(): void {
69
+ this.events.clear();
70
+ }
71
+ }
package/src/utils/jwt.ts CHANGED
@@ -1,51 +1,51 @@
1
- export interface DecodedTenxyteToken {
2
- exp?: number;
3
- iat?: number;
4
- sub?: string;
5
- roles?: string[];
6
- permissions?: string[];
7
- [key: string]: any;
8
- }
9
-
10
- /**
11
- * Decodes the payload of a JWT without verifying the signature.
12
- * Suitable for client-side routing and UI state.
13
- */
14
- export function decodeJwt(token: string): DecodedTenxyteToken | null {
15
- try {
16
- const parts = token.split('.');
17
- if (parts.length !== 3) {
18
- return null;
19
- }
20
-
21
- let base64Url = parts[1];
22
- if (!base64Url) return null;
23
-
24
- let base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
25
-
26
- // Pad with standard base64 padding
27
- while (base64.length % 4) {
28
- base64 += '=';
29
- }
30
-
31
- const isBrowser = typeof window !== 'undefined' && typeof window.atob === 'function';
32
- let jsonPayload: string;
33
-
34
- if (isBrowser) {
35
- // Browser decode
36
- jsonPayload = decodeURIComponent(
37
- window.atob(base64)
38
- .split('')
39
- .map((c) => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))
40
- .join('')
41
- );
42
- } else {
43
- // Node.js decode
44
- jsonPayload = Buffer.from(base64, 'base64').toString('utf8');
45
- }
46
-
47
- return JSON.parse(jsonPayload);
48
- } catch (e) {
49
- return null;
50
- }
51
- }
1
+ export interface DecodedTenxyteToken {
2
+ exp?: number;
3
+ iat?: number;
4
+ sub?: string;
5
+ roles?: string[];
6
+ permissions?: string[];
7
+ [key: string]: any;
8
+ }
9
+
10
+ /**
11
+ * Decodes the payload of a JWT without verifying the signature.
12
+ * Suitable for client-side routing and UI state.
13
+ */
14
+ export function decodeJwt(token: string): DecodedTenxyteToken | null {
15
+ try {
16
+ const parts = token.split('.');
17
+ if (parts.length !== 3) {
18
+ return null;
19
+ }
20
+
21
+ let base64Url = parts[1];
22
+ if (!base64Url) return null;
23
+
24
+ let base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
25
+
26
+ // Pad with standard base64 padding
27
+ while (base64.length % 4) {
28
+ base64 += '=';
29
+ }
30
+
31
+ const isBrowser = typeof window !== 'undefined' && typeof window.atob === 'function';
32
+ let jsonPayload: string;
33
+
34
+ if (isBrowser) {
35
+ // Browser decode
36
+ jsonPayload = decodeURIComponent(
37
+ window.atob(base64)
38
+ .split('')
39
+ .map((c) => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))
40
+ .join('')
41
+ );
42
+ } else {
43
+ // Node.js decode
44
+ jsonPayload = Buffer.from(base64, 'base64').toString('utf8');
45
+ }
46
+
47
+ return JSON.parse(jsonPayload);
48
+ } catch (e) {
49
+ return null;
50
+ }
51
+ }