@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.
- package/README.md +184 -0
- package/dist/index.cjs +951 -496
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1709 -1265
- package/dist/index.d.ts +1709 -1265
- package/dist/index.js +919 -464
- package/dist/index.js.map +1 -1
- package/package.json +70 -66
- package/src/client.ts +50 -21
- package/src/http/client.ts +162 -162
- package/src/http/index.ts +1 -1
- package/src/http/interceptors.ts +117 -117
- package/src/index.ts +7 -7
- package/src/modules/ai.ts +178 -0
- package/src/modules/auth.ts +116 -95
- package/src/modules/b2b.ts +177 -0
- package/src/modules/rbac.ts +207 -160
- package/src/modules/security.ts +313 -122
- package/src/modules/user.ts +95 -80
- package/src/storage/cookie.ts +39 -39
- package/src/storage/index.ts +29 -29
- package/src/storage/localStorage.ts +75 -75
- package/src/storage/memory.ts +30 -30
- package/src/types/index.ts +152 -150
- package/src/utils/base64url.ts +25 -0
- package/src/utils/device_info.ts +94 -94
- package/src/utils/events.ts +71 -71
- package/src/utils/jwt.ts +51 -51
- package/tests/http.test.ts +144 -144
- package/tests/modules/auth.test.ts +93 -93
- package/tests/modules/rbac.test.ts +95 -95
- package/tests/modules/security.test.ts +85 -75
- package/tests/modules/user.test.ts +76 -76
- package/tests/storage.test.ts +96 -96
- package/tests/utils.test.ts +71 -71
- package/tsup.config.ts +10 -10
- package/vitest.config.ts +7 -7
package/tests/storage.test.ts
CHANGED
|
@@ -1,96 +1,96 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
2
|
-
import { MemoryStorage, LocalStorage } from '../src/storage';
|
|
3
|
-
|
|
4
|
-
describe('Storage Abstractions', () => {
|
|
5
|
-
describe('MemoryStorage', () => {
|
|
6
|
-
let storage: MemoryStorage;
|
|
7
|
-
|
|
8
|
-
beforeEach(() => {
|
|
9
|
-
storage = new MemoryStorage();
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
it('should set and get items', () => {
|
|
13
|
-
storage.setItem('key1', 'value1');
|
|
14
|
-
expect(storage.getItem('key1')).toBe('value1');
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it('should return null for non-existent keys', () => {
|
|
18
|
-
expect(storage.getItem('key2')).toBeNull();
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it('should remove items', () => {
|
|
22
|
-
storage.setItem('key1', 'value1');
|
|
23
|
-
storage.removeItem('key1');
|
|
24
|
-
expect(storage.getItem('key1')).toBeNull();
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('should clear all items', () => {
|
|
28
|
-
storage.setItem('key1', 'value1');
|
|
29
|
-
storage.setItem('key2', 'value2');
|
|
30
|
-
storage.clear();
|
|
31
|
-
expect(storage.getItem('key1')).toBeNull();
|
|
32
|
-
expect(storage.getItem('key2')).toBeNull();
|
|
33
|
-
});
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
describe('LocalStorage', () => {
|
|
37
|
-
let storage: LocalStorage;
|
|
38
|
-
|
|
39
|
-
beforeEach(() => {
|
|
40
|
-
// Mock window.localStorage for node environment
|
|
41
|
-
const localStorageMock = (() => {
|
|
42
|
-
let store: Record<string, string> = {};
|
|
43
|
-
return {
|
|
44
|
-
getItem: vi.fn((key: string) => store[key] || null),
|
|
45
|
-
setItem: vi.fn((key: string, value: string) => {
|
|
46
|
-
if (key === 'fallback_key') throw new Error('Quota'); // For fallback test
|
|
47
|
-
store[key] = value.toString();
|
|
48
|
-
}),
|
|
49
|
-
removeItem: vi.fn((key: string) => {
|
|
50
|
-
delete store[key];
|
|
51
|
-
}),
|
|
52
|
-
clear: vi.fn(() => {
|
|
53
|
-
store = {};
|
|
54
|
-
}),
|
|
55
|
-
};
|
|
56
|
-
})();
|
|
57
|
-
|
|
58
|
-
Object.defineProperty(window, 'localStorage', {
|
|
59
|
-
value: localStorageMock,
|
|
60
|
-
writable: true,
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
storage = new LocalStorage();
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it('should set and get items in browser localStorage', () => {
|
|
67
|
-
storage.setItem('tx_test', 'value123');
|
|
68
|
-
expect(storage.getItem('tx_test')).toBe('value123');
|
|
69
|
-
expect(localStorage.getItem('tx_test')).toBe('value123');
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it('should return null for non-existent keys', () => {
|
|
73
|
-
expect(storage.getItem('missing')).toBeNull();
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it('should degrade to MemoryStorage if window.localStorage throws error', () => {
|
|
77
|
-
// The mock throws an error for 'fallback_key' specifically, but 'new LocalStorage()' checking availability passes.
|
|
78
|
-
// Wait, checkAvailability() tries to set '__tenxyte_test__'.
|
|
79
|
-
// To test constructor degradation, we can mock localStorage globally to throw on everything during setup:
|
|
80
|
-
Object.defineProperty(window, 'localStorage', {
|
|
81
|
-
value: {
|
|
82
|
-
setItem: () => { throw new Error('QuotaExceededError'); },
|
|
83
|
-
getItem: () => null,
|
|
84
|
-
removeItem: () => { },
|
|
85
|
-
clear: () => { }
|
|
86
|
-
},
|
|
87
|
-
writable: true,
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
const strictStorage = new LocalStorage();
|
|
91
|
-
|
|
92
|
-
strictStorage.setItem('fallback_key', 'test');
|
|
93
|
-
expect(strictStorage.getItem('fallback_key')).toBe('test');
|
|
94
|
-
});
|
|
95
|
-
});
|
|
96
|
-
});
|
|
1
|
+
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
2
|
+
import { MemoryStorage, LocalStorage } from '../src/storage';
|
|
3
|
+
|
|
4
|
+
describe('Storage Abstractions', () => {
|
|
5
|
+
describe('MemoryStorage', () => {
|
|
6
|
+
let storage: MemoryStorage;
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
storage = new MemoryStorage();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('should set and get items', () => {
|
|
13
|
+
storage.setItem('key1', 'value1');
|
|
14
|
+
expect(storage.getItem('key1')).toBe('value1');
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should return null for non-existent keys', () => {
|
|
18
|
+
expect(storage.getItem('key2')).toBeNull();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should remove items', () => {
|
|
22
|
+
storage.setItem('key1', 'value1');
|
|
23
|
+
storage.removeItem('key1');
|
|
24
|
+
expect(storage.getItem('key1')).toBeNull();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should clear all items', () => {
|
|
28
|
+
storage.setItem('key1', 'value1');
|
|
29
|
+
storage.setItem('key2', 'value2');
|
|
30
|
+
storage.clear();
|
|
31
|
+
expect(storage.getItem('key1')).toBeNull();
|
|
32
|
+
expect(storage.getItem('key2')).toBeNull();
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe('LocalStorage', () => {
|
|
37
|
+
let storage: LocalStorage;
|
|
38
|
+
|
|
39
|
+
beforeEach(() => {
|
|
40
|
+
// Mock window.localStorage for node environment
|
|
41
|
+
const localStorageMock = (() => {
|
|
42
|
+
let store: Record<string, string> = {};
|
|
43
|
+
return {
|
|
44
|
+
getItem: vi.fn((key: string) => store[key] || null),
|
|
45
|
+
setItem: vi.fn((key: string, value: string) => {
|
|
46
|
+
if (key === 'fallback_key') throw new Error('Quota'); // For fallback test
|
|
47
|
+
store[key] = value.toString();
|
|
48
|
+
}),
|
|
49
|
+
removeItem: vi.fn((key: string) => {
|
|
50
|
+
delete store[key];
|
|
51
|
+
}),
|
|
52
|
+
clear: vi.fn(() => {
|
|
53
|
+
store = {};
|
|
54
|
+
}),
|
|
55
|
+
};
|
|
56
|
+
})();
|
|
57
|
+
|
|
58
|
+
Object.defineProperty(window, 'localStorage', {
|
|
59
|
+
value: localStorageMock,
|
|
60
|
+
writable: true,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
storage = new LocalStorage();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should set and get items in browser localStorage', () => {
|
|
67
|
+
storage.setItem('tx_test', 'value123');
|
|
68
|
+
expect(storage.getItem('tx_test')).toBe('value123');
|
|
69
|
+
expect(localStorage.getItem('tx_test')).toBe('value123');
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should return null for non-existent keys', () => {
|
|
73
|
+
expect(storage.getItem('missing')).toBeNull();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('should degrade to MemoryStorage if window.localStorage throws error', () => {
|
|
77
|
+
// The mock throws an error for 'fallback_key' specifically, but 'new LocalStorage()' checking availability passes.
|
|
78
|
+
// Wait, checkAvailability() tries to set '__tenxyte_test__'.
|
|
79
|
+
// To test constructor degradation, we can mock localStorage globally to throw on everything during setup:
|
|
80
|
+
Object.defineProperty(window, 'localStorage', {
|
|
81
|
+
value: {
|
|
82
|
+
setItem: () => { throw new Error('QuotaExceededError'); },
|
|
83
|
+
getItem: () => null,
|
|
84
|
+
removeItem: () => { },
|
|
85
|
+
clear: () => { }
|
|
86
|
+
},
|
|
87
|
+
writable: true,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const strictStorage = new LocalStorage();
|
|
91
|
+
|
|
92
|
+
strictStorage.setItem('fallback_key', 'test');
|
|
93
|
+
expect(strictStorage.getItem('fallback_key')).toBe('test');
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
});
|
package/tests/utils.test.ts
CHANGED
|
@@ -1,71 +1,71 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
-
import { buildDeviceInfo } from '../src/utils/device_info';
|
|
3
|
-
import { EventEmitter } from '../src/utils/events';
|
|
4
|
-
|
|
5
|
-
describe('Utilities', () => {
|
|
6
|
-
describe('buildDeviceInfo', () => {
|
|
7
|
-
it('should respect custom properties', () => {
|
|
8
|
-
const info = buildDeviceInfo({
|
|
9
|
-
os: 'macos',
|
|
10
|
-
osVersion: '14.0',
|
|
11
|
-
device: 'desktop',
|
|
12
|
-
arch: 'arm64',
|
|
13
|
-
app: 'tenxyte-test',
|
|
14
|
-
appVersion: '2.0.0',
|
|
15
|
-
runtime: 'node',
|
|
16
|
-
runtimeVersion: 'v20.0.0',
|
|
17
|
-
timezone: 'America/New_York'
|
|
18
|
-
});
|
|
19
|
-
// v=1|os=macos;osv=14.0|device=desktop|arch=arm64|app=tenxyte-test;appv=2.0.0|runtime=node;rtv=v20.0.0|tz=America/New_York
|
|
20
|
-
expect(info).toContain('os=macos;osv=14.0');
|
|
21
|
-
expect(info).toContain('app=tenxyte-test;appv=2.0.0');
|
|
22
|
-
expect(info).toContain('runtime=node;rtv=v20.0.0');
|
|
23
|
-
expect(info).toContain('tz=America/New_York');
|
|
24
|
-
expect(info).toContain('v=1');
|
|
25
|
-
expect(info).toContain('device=desktop');
|
|
26
|
-
expect(info).toContain('arch=arm64');
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it('should generate sensible defaults in absence of custom info', () => {
|
|
30
|
-
const info = buildDeviceInfo();
|
|
31
|
-
expect(info).toContain('v=1');
|
|
32
|
-
// Should have parsed process or window depending on env
|
|
33
|
-
});
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
describe('EventEmitter', () => {
|
|
37
|
-
type TestEvents = {
|
|
38
|
-
'test:event': { data: string };
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
let emitter: EventEmitter<TestEvents>;
|
|
42
|
-
|
|
43
|
-
beforeEach(() => {
|
|
44
|
-
emitter = new EventEmitter();
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('should allow emitting and receiving events', () => {
|
|
48
|
-
const mockCb = vi.fn();
|
|
49
|
-
emitter.on('test:event', mockCb);
|
|
50
|
-
emitter.emit('test:event', { data: 'hello' });
|
|
51
|
-
expect(mockCb).toHaveBeenCalledWith({ data: 'hello' });
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('should allow unsubscribing', () => {
|
|
55
|
-
const mockCb = vi.fn();
|
|
56
|
-
const unsub = emitter.on('test:event', mockCb);
|
|
57
|
-
unsub();
|
|
58
|
-
emitter.emit('test:event', { data: 'hello' });
|
|
59
|
-
expect(mockCb).not.toHaveBeenCalled();
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it('should execute "once" subscriptions exactly once', () => {
|
|
63
|
-
const mockCb = vi.fn();
|
|
64
|
-
emitter.once('test:event', mockCb);
|
|
65
|
-
emitter.emit('test:event', { data: 'one' });
|
|
66
|
-
emitter.emit('test:event', { data: 'two' });
|
|
67
|
-
expect(mockCb).toHaveBeenCalledTimes(1);
|
|
68
|
-
expect(mockCb).toHaveBeenCalledWith({ data: 'one' });
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
});
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { buildDeviceInfo } from '../src/utils/device_info';
|
|
3
|
+
import { EventEmitter } from '../src/utils/events';
|
|
4
|
+
|
|
5
|
+
describe('Utilities', () => {
|
|
6
|
+
describe('buildDeviceInfo', () => {
|
|
7
|
+
it('should respect custom properties', () => {
|
|
8
|
+
const info = buildDeviceInfo({
|
|
9
|
+
os: 'macos',
|
|
10
|
+
osVersion: '14.0',
|
|
11
|
+
device: 'desktop',
|
|
12
|
+
arch: 'arm64',
|
|
13
|
+
app: 'tenxyte-test',
|
|
14
|
+
appVersion: '2.0.0',
|
|
15
|
+
runtime: 'node',
|
|
16
|
+
runtimeVersion: 'v20.0.0',
|
|
17
|
+
timezone: 'America/New_York'
|
|
18
|
+
});
|
|
19
|
+
// v=1|os=macos;osv=14.0|device=desktop|arch=arm64|app=tenxyte-test;appv=2.0.0|runtime=node;rtv=v20.0.0|tz=America/New_York
|
|
20
|
+
expect(info).toContain('os=macos;osv=14.0');
|
|
21
|
+
expect(info).toContain('app=tenxyte-test;appv=2.0.0');
|
|
22
|
+
expect(info).toContain('runtime=node;rtv=v20.0.0');
|
|
23
|
+
expect(info).toContain('tz=America/New_York');
|
|
24
|
+
expect(info).toContain('v=1');
|
|
25
|
+
expect(info).toContain('device=desktop');
|
|
26
|
+
expect(info).toContain('arch=arm64');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should generate sensible defaults in absence of custom info', () => {
|
|
30
|
+
const info = buildDeviceInfo();
|
|
31
|
+
expect(info).toContain('v=1');
|
|
32
|
+
// Should have parsed process or window depending on env
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe('EventEmitter', () => {
|
|
37
|
+
type TestEvents = {
|
|
38
|
+
'test:event': { data: string };
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
let emitter: EventEmitter<TestEvents>;
|
|
42
|
+
|
|
43
|
+
beforeEach(() => {
|
|
44
|
+
emitter = new EventEmitter();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should allow emitting and receiving events', () => {
|
|
48
|
+
const mockCb = vi.fn();
|
|
49
|
+
emitter.on('test:event', mockCb);
|
|
50
|
+
emitter.emit('test:event', { data: 'hello' });
|
|
51
|
+
expect(mockCb).toHaveBeenCalledWith({ data: 'hello' });
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should allow unsubscribing', () => {
|
|
55
|
+
const mockCb = vi.fn();
|
|
56
|
+
const unsub = emitter.on('test:event', mockCb);
|
|
57
|
+
unsub();
|
|
58
|
+
emitter.emit('test:event', { data: 'hello' });
|
|
59
|
+
expect(mockCb).not.toHaveBeenCalled();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should execute "once" subscriptions exactly once', () => {
|
|
63
|
+
const mockCb = vi.fn();
|
|
64
|
+
emitter.once('test:event', mockCb);
|
|
65
|
+
emitter.emit('test:event', { data: 'one' });
|
|
66
|
+
emitter.emit('test:event', { data: 'two' });
|
|
67
|
+
expect(mockCb).toHaveBeenCalledTimes(1);
|
|
68
|
+
expect(mockCb).toHaveBeenCalledWith({ data: 'one' });
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
});
|
package/tsup.config.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { defineConfig } from 'tsup';
|
|
2
|
-
|
|
3
|
-
export default defineConfig({
|
|
4
|
-
entry: ['src/index.ts'],
|
|
5
|
-
format: ['cjs', 'esm'],
|
|
6
|
-
dts: true,
|
|
7
|
-
splitting: false,
|
|
8
|
-
sourcemap: true,
|
|
9
|
-
clean: true,
|
|
10
|
-
});
|
|
1
|
+
import { defineConfig } from 'tsup';
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
entry: ['src/index.ts'],
|
|
5
|
+
format: ['cjs', 'esm'],
|
|
6
|
+
dts: true,
|
|
7
|
+
splitting: false,
|
|
8
|
+
sourcemap: true,
|
|
9
|
+
clean: true,
|
|
10
|
+
});
|
package/vitest.config.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { defineConfig } from 'vitest/config';
|
|
2
|
-
|
|
3
|
-
export default defineConfig({
|
|
4
|
-
test: {
|
|
5
|
-
environment: 'happy-dom',
|
|
6
|
-
},
|
|
7
|
-
});
|
|
1
|
+
import { defineConfig } from 'vitest/config';
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
test: {
|
|
5
|
+
environment: 'happy-dom',
|
|
6
|
+
},
|
|
7
|
+
});
|