@enfin/chat-shared 1.2.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,27 @@
1
+ /**
2
+ * Generate a random API key for chat platform
3
+ * Format: chat_{tenantId}_{checksum}
4
+ */
5
+ export declare function generateApiKey(tenantId: string): string;
6
+ /**
7
+ * Generate a random tenant ID
8
+ */
9
+ export declare function generateTenantId(): string;
10
+ /**
11
+ * Validate API key format
12
+ * Returns true if format is correct: chat_{tenantId}_{random}
13
+ */
14
+ export declare function validateApiKeyFormat(apiKey: string): boolean;
15
+ /**
16
+ * Extract tenant ID from API key
17
+ * Format: chat_{tenantId}_{random}
18
+ */
19
+ export declare function extractTenantId(apiKey: string): string | null;
20
+ /**
21
+ * Generate a checksum for API key validation
22
+ */
23
+ export declare function generateChecksum(apiKey: string): string;
24
+ /**
25
+ * Validate API key with checksum
26
+ */
27
+ export declare function validateApiKeyWithChecksum(apiKey: string, expectedChecksum: string): boolean;
package/dist/apikey.js ADDED
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ // API Key generation and validation utilities
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.generateApiKey = generateApiKey;
5
+ exports.generateTenantId = generateTenantId;
6
+ exports.validateApiKeyFormat = validateApiKeyFormat;
7
+ exports.extractTenantId = extractTenantId;
8
+ exports.generateChecksum = generateChecksum;
9
+ exports.validateApiKeyWithChecksum = validateApiKeyWithChecksum;
10
+ const BASE62_CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
11
+ /**
12
+ * Generate a random API key for chat platform
13
+ * Format: chat_{tenantId}_{checksum}
14
+ */
15
+ function generateApiKey(tenantId) {
16
+ const randomPart = generateRandomString(10);
17
+ return `chat_${tenantId}_${randomPart}`;
18
+ }
19
+ /**
20
+ * Generate a random tenant ID
21
+ */
22
+ function generateTenantId() {
23
+ return generateRandomString(8);
24
+ }
25
+ /**
26
+ * Generate a random string of specified length using Base62
27
+ */
28
+ function generateRandomString(length) {
29
+ let result = '';
30
+ for (let i = 0; i < length; i++) {
31
+ result += BASE62_CHARS[Math.floor(Math.random() * BASE62_CHARS.length)];
32
+ }
33
+ return result;
34
+ }
35
+ /**
36
+ * Validate API key format
37
+ * Returns true if format is correct: chat_{tenantId}_{random}
38
+ */
39
+ function validateApiKeyFormat(apiKey) {
40
+ const parts = apiKey.split('_');
41
+ return (parts.length === 3 &&
42
+ parts[0] === 'chat' &&
43
+ parts[1].length >= 6 &&
44
+ parts[2].length >= 6);
45
+ }
46
+ /**
47
+ * Extract tenant ID from API key
48
+ * Format: chat_{tenantId}_{random}
49
+ */
50
+ function extractTenantId(apiKey) {
51
+ const parts = apiKey.split('_');
52
+ if (parts.length !== 3 || parts[0] !== 'chat')
53
+ return null;
54
+ return parts[1];
55
+ }
56
+ /**
57
+ * Generate a checksum for API key validation
58
+ */
59
+ function generateChecksum(apiKey) {
60
+ let hash = 0;
61
+ for (let i = 0; i < apiKey.length; i++) {
62
+ const char = apiKey.charCodeAt(i);
63
+ hash = (hash << 5) - hash + char;
64
+ hash = hash & hash;
65
+ }
66
+ return Math.abs(hash).toString(36);
67
+ }
68
+ /**
69
+ * Validate API key with checksum
70
+ */
71
+ function validateApiKeyWithChecksum(apiKey, expectedChecksum) {
72
+ return generateChecksum(apiKey) === expectedChecksum;
73
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Generate a random API key for chat platform
3
+ * Format: chat_{tenantId}_{checksum}
4
+ */
5
+ export declare function generateApiKey(tenantId: string): string;
6
+ /**
7
+ * Generate a random tenant ID
8
+ */
9
+ export declare function generateTenantId(): string;
10
+ /**
11
+ * Validate API key format
12
+ * Returns true if format is correct: chat_{tenantId}_{random}
13
+ */
14
+ export declare function validateApiKeyFormat(apiKey: string): boolean;
15
+ /**
16
+ * Extract tenant ID from API key
17
+ * Format: chat_{tenantId}_{random}
18
+ */
19
+ export declare function extractTenantId(apiKey: string): string | null;
20
+ /**
21
+ * Generate a checksum for API key validation
22
+ */
23
+ export declare function generateChecksum(apiKey: string): string;
24
+ /**
25
+ * Validate API key with checksum
26
+ */
27
+ export declare function validateApiKeyWithChecksum(apiKey: string, expectedChecksum: string): boolean;
@@ -0,0 +1,65 @@
1
+ // API Key generation and validation utilities
2
+ const BASE62_CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
3
+ /**
4
+ * Generate a random API key for chat platform
5
+ * Format: chat_{tenantId}_{checksum}
6
+ */
7
+ export function generateApiKey(tenantId) {
8
+ const randomPart = generateRandomString(10);
9
+ return `chat_${tenantId}_${randomPart}`;
10
+ }
11
+ /**
12
+ * Generate a random tenant ID
13
+ */
14
+ export function generateTenantId() {
15
+ return generateRandomString(8);
16
+ }
17
+ /**
18
+ * Generate a random string of specified length using Base62
19
+ */
20
+ function generateRandomString(length) {
21
+ let result = '';
22
+ for (let i = 0; i < length; i++) {
23
+ result += BASE62_CHARS[Math.floor(Math.random() * BASE62_CHARS.length)];
24
+ }
25
+ return result;
26
+ }
27
+ /**
28
+ * Validate API key format
29
+ * Returns true if format is correct: chat_{tenantId}_{random}
30
+ */
31
+ export function validateApiKeyFormat(apiKey) {
32
+ const parts = apiKey.split('_');
33
+ return (parts.length === 3 &&
34
+ parts[0] === 'chat' &&
35
+ parts[1].length >= 6 &&
36
+ parts[2].length >= 6);
37
+ }
38
+ /**
39
+ * Extract tenant ID from API key
40
+ * Format: chat_{tenantId}_{random}
41
+ */
42
+ export function extractTenantId(apiKey) {
43
+ const parts = apiKey.split('_');
44
+ if (parts.length !== 3 || parts[0] !== 'chat')
45
+ return null;
46
+ return parts[1];
47
+ }
48
+ /**
49
+ * Generate a checksum for API key validation
50
+ */
51
+ export function generateChecksum(apiKey) {
52
+ let hash = 0;
53
+ for (let i = 0; i < apiKey.length; i++) {
54
+ const char = apiKey.charCodeAt(i);
55
+ hash = (hash << 5) - hash + char;
56
+ hash = hash & hash;
57
+ }
58
+ return Math.abs(hash).toString(36);
59
+ }
60
+ /**
61
+ * Validate API key with checksum
62
+ */
63
+ export function validateApiKeyWithChecksum(apiKey, expectedChecksum) {
64
+ return generateChecksum(apiKey) === expectedChecksum;
65
+ }
@@ -0,0 +1,164 @@
1
+ export interface ApiKeyValidationResponse {
2
+ valid: boolean;
3
+ apiKey: string;
4
+ mode: 'managed' | 'external-db';
5
+ database?: string;
6
+ version: string;
7
+ tenantId: string;
8
+ error?: string;
9
+ }
10
+ export interface Message {
11
+ _id: string;
12
+ roomId: string;
13
+ senderId: string;
14
+ senderName: string;
15
+ senderAvatar?: string;
16
+ content: string;
17
+ fileUrl?: string;
18
+ fileType?: string;
19
+ fileName?: string;
20
+ createdAt: Date;
21
+ updatedAt: Date;
22
+ readBy: ReadReceipt[];
23
+ }
24
+ export interface ReadReceipt {
25
+ userId: string;
26
+ readAt: Date;
27
+ }
28
+ export interface Room {
29
+ _id: string;
30
+ name: string;
31
+ type: 'direct' | 'group';
32
+ members: string[];
33
+ createdBy: string;
34
+ createdAt: Date;
35
+ updatedAt: Date;
36
+ lastMessage?: string | Message;
37
+ lastActivity: Date;
38
+ avatar?: string;
39
+ }
40
+ export interface PresenceStatus {
41
+ userId: string;
42
+ status: 'online' | 'offline' | 'away';
43
+ lastSeen: Date;
44
+ typingIn?: string[];
45
+ }
46
+ export interface AudioCall {
47
+ _id: string;
48
+ roomId: string;
49
+ initiatorId: string;
50
+ participantId: string;
51
+ status: 'ringing' | 'active' | 'ended';
52
+ startedAt: Date;
53
+ endedAt?: Date;
54
+ duration?: number;
55
+ }
56
+ export interface SocketMessage {
57
+ type: string;
58
+ data: any;
59
+ timestamp: number;
60
+ }
61
+ export interface SendMessagePayload {
62
+ roomId: string;
63
+ content: string;
64
+ fileUrl?: string;
65
+ fileType?: string;
66
+ fileName?: string;
67
+ }
68
+ export interface TypingPayload {
69
+ roomId: string;
70
+ userId: string;
71
+ userName: string;
72
+ typing?: boolean;
73
+ content?: string;
74
+ }
75
+ export interface PresencePayload {
76
+ userId: string;
77
+ status: 'online' | 'offline' | 'away';
78
+ }
79
+ export interface CallSignalingPayload {
80
+ roomId: string;
81
+ initiatorId: string;
82
+ participantId: string;
83
+ offer?: RTCSessionDescription;
84
+ answer?: RTCSessionDescription;
85
+ candidate?: RTCIceCandidate;
86
+ }
87
+ export interface FileUploadPayload {
88
+ roomId: string;
89
+ fileName: string;
90
+ fileSize: number;
91
+ fileType: string;
92
+ }
93
+ export interface FileUploadResponse {
94
+ success: boolean;
95
+ fileUrl: string;
96
+ fileName: string;
97
+ error?: string;
98
+ }
99
+ export interface ChatConfig {
100
+ apiKey: string;
101
+ mongoUri?: string;
102
+ mode?: 'managed' | 'external-db';
103
+ version?: string;
104
+ validationUrl?: string;
105
+ jwtSecret?: string;
106
+ jwtIssuer?: string;
107
+ jwtAudience?: string;
108
+ iceServers?: RTCIceServer[];
109
+ turnServers?: TurnServerConfig[];
110
+ rateLimit?: {
111
+ messagesPerMinute?: number;
112
+ eventsPerMinute?: number;
113
+ };
114
+ hooks?: ChatHooks;
115
+ ringtoneUrl?: string;
116
+ }
117
+ export interface ChatHooks {
118
+ beforeMessageCreate?: (message: Message, user: AuthContext) => Message;
119
+ afterMessageCreate?: (message: Message) => void;
120
+ beforeRoomCreate?: (room: Room, user: AuthContext) => Room;
121
+ afterRoomCreate?: (room: Room) => void;
122
+ }
123
+ export declare const SDK_VERSION = "1.0.0";
124
+ export declare const SDK_MIN_COMPATIBLE_VERSION = "1.0.0";
125
+ export declare const SDK_MAX_COMPATIBLE_VERSION = "1.9.999";
126
+ export interface TurnServerConfig {
127
+ urls: string;
128
+ username?: string;
129
+ credential?: string;
130
+ }
131
+ export interface AuthContext {
132
+ userId: string;
133
+ tenantId: string;
134
+ roles: string[];
135
+ }
136
+ export interface VersionCheckResult {
137
+ compatible: boolean;
138
+ severity: 'ok' | 'warning' | 'error';
139
+ message: string;
140
+ }
141
+ export interface PaginatedMessages {
142
+ messages: Message[];
143
+ hasMore: boolean;
144
+ nextCursor: string | null;
145
+ }
146
+ export interface GetMessagesPayload {
147
+ roomId: string;
148
+ limit?: number;
149
+ beforeCursor?: string;
150
+ }
151
+ export interface RateLimitExceededPayload {
152
+ code: 'RATE_LIMIT_EXCEEDED';
153
+ message: string;
154
+ retryAfterMs?: number;
155
+ }
156
+ export interface AdminTenantStats {
157
+ tenantId: string;
158
+ totalMessages: number;
159
+ totalRooms: number;
160
+ activeConnections: number;
161
+ lastActivity: Date;
162
+ }
163
+ export declare function deriveDatabaseFromApiKey(apiKey: string): string | null;
164
+ export * from './apikey';
@@ -0,0 +1,19 @@
1
+ // ============================================
2
+ // @enfin/chat-shared - Core Types
3
+ // ============================================
4
+ // SDK Version
5
+ export const SDK_VERSION = '1.0.0';
6
+ export const SDK_MIN_COMPATIBLE_VERSION = '1.0.0';
7
+ export const SDK_MAX_COMPATIBLE_VERSION = '1.9.999';
8
+ // Derive tenant database name from API key
9
+ export function deriveDatabaseFromApiKey(apiKey) {
10
+ const parts = apiKey.split('_');
11
+ if (parts.length !== 3 || parts[0] !== 'chat')
12
+ return null;
13
+ const tenantId = parts[1];
14
+ if (!tenantId)
15
+ return null;
16
+ return `chat_${tenantId}`;
17
+ }
18
+ // Re-export API key utilities
19
+ export * from './apikey';
@@ -0,0 +1,164 @@
1
+ export interface ApiKeyValidationResponse {
2
+ valid: boolean;
3
+ apiKey: string;
4
+ mode: 'managed' | 'external-db';
5
+ database?: string;
6
+ version: string;
7
+ tenantId: string;
8
+ error?: string;
9
+ }
10
+ export interface Message {
11
+ _id: string;
12
+ roomId: string;
13
+ senderId: string;
14
+ senderName: string;
15
+ senderAvatar?: string;
16
+ content: string;
17
+ fileUrl?: string;
18
+ fileType?: string;
19
+ fileName?: string;
20
+ createdAt: Date;
21
+ updatedAt: Date;
22
+ readBy: ReadReceipt[];
23
+ }
24
+ export interface ReadReceipt {
25
+ userId: string;
26
+ readAt: Date;
27
+ }
28
+ export interface Room {
29
+ _id: string;
30
+ name: string;
31
+ type: 'direct' | 'group';
32
+ members: string[];
33
+ createdBy: string;
34
+ createdAt: Date;
35
+ updatedAt: Date;
36
+ lastMessage?: string | Message;
37
+ lastActivity: Date;
38
+ avatar?: string;
39
+ }
40
+ export interface PresenceStatus {
41
+ userId: string;
42
+ status: 'online' | 'offline' | 'away';
43
+ lastSeen: Date;
44
+ typingIn?: string[];
45
+ }
46
+ export interface AudioCall {
47
+ _id: string;
48
+ roomId: string;
49
+ initiatorId: string;
50
+ participantId: string;
51
+ status: 'ringing' | 'active' | 'ended';
52
+ startedAt: Date;
53
+ endedAt?: Date;
54
+ duration?: number;
55
+ }
56
+ export interface SocketMessage {
57
+ type: string;
58
+ data: any;
59
+ timestamp: number;
60
+ }
61
+ export interface SendMessagePayload {
62
+ roomId: string;
63
+ content: string;
64
+ fileUrl?: string;
65
+ fileType?: string;
66
+ fileName?: string;
67
+ }
68
+ export interface TypingPayload {
69
+ roomId: string;
70
+ userId: string;
71
+ userName: string;
72
+ typing?: boolean;
73
+ content?: string;
74
+ }
75
+ export interface PresencePayload {
76
+ userId: string;
77
+ status: 'online' | 'offline' | 'away';
78
+ }
79
+ export interface CallSignalingPayload {
80
+ roomId: string;
81
+ initiatorId: string;
82
+ participantId: string;
83
+ offer?: RTCSessionDescription;
84
+ answer?: RTCSessionDescription;
85
+ candidate?: RTCIceCandidate;
86
+ }
87
+ export interface FileUploadPayload {
88
+ roomId: string;
89
+ fileName: string;
90
+ fileSize: number;
91
+ fileType: string;
92
+ }
93
+ export interface FileUploadResponse {
94
+ success: boolean;
95
+ fileUrl: string;
96
+ fileName: string;
97
+ error?: string;
98
+ }
99
+ export interface ChatConfig {
100
+ apiKey: string;
101
+ mongoUri?: string;
102
+ mode?: 'managed' | 'external-db';
103
+ version?: string;
104
+ validationUrl?: string;
105
+ jwtSecret?: string;
106
+ jwtIssuer?: string;
107
+ jwtAudience?: string;
108
+ iceServers?: RTCIceServer[];
109
+ turnServers?: TurnServerConfig[];
110
+ rateLimit?: {
111
+ messagesPerMinute?: number;
112
+ eventsPerMinute?: number;
113
+ };
114
+ hooks?: ChatHooks;
115
+ ringtoneUrl?: string;
116
+ }
117
+ export interface ChatHooks {
118
+ beforeMessageCreate?: (message: Message, user: AuthContext) => Message;
119
+ afterMessageCreate?: (message: Message) => void;
120
+ beforeRoomCreate?: (room: Room, user: AuthContext) => Room;
121
+ afterRoomCreate?: (room: Room) => void;
122
+ }
123
+ export declare const SDK_VERSION = "1.0.0";
124
+ export declare const SDK_MIN_COMPATIBLE_VERSION = "1.0.0";
125
+ export declare const SDK_MAX_COMPATIBLE_VERSION = "1.9.999";
126
+ export interface TurnServerConfig {
127
+ urls: string;
128
+ username?: string;
129
+ credential?: string;
130
+ }
131
+ export interface AuthContext {
132
+ userId: string;
133
+ tenantId: string;
134
+ roles: string[];
135
+ }
136
+ export interface VersionCheckResult {
137
+ compatible: boolean;
138
+ severity: 'ok' | 'warning' | 'error';
139
+ message: string;
140
+ }
141
+ export interface PaginatedMessages {
142
+ messages: Message[];
143
+ hasMore: boolean;
144
+ nextCursor: string | null;
145
+ }
146
+ export interface GetMessagesPayload {
147
+ roomId: string;
148
+ limit?: number;
149
+ beforeCursor?: string;
150
+ }
151
+ export interface RateLimitExceededPayload {
152
+ code: 'RATE_LIMIT_EXCEEDED';
153
+ message: string;
154
+ retryAfterMs?: number;
155
+ }
156
+ export interface AdminTenantStats {
157
+ tenantId: string;
158
+ totalMessages: number;
159
+ totalRooms: number;
160
+ activeConnections: number;
161
+ lastActivity: Date;
162
+ }
163
+ export declare function deriveDatabaseFromApiKey(apiKey: string): string | null;
164
+ export * from './apikey';
package/dist/index.js ADDED
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ // ============================================
3
+ // @enfin/chat-shared - Core Types
4
+ // ============================================
5
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ var desc = Object.getOwnPropertyDescriptor(m, k);
8
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
9
+ desc = { enumerable: true, get: function() { return m[k]; } };
10
+ }
11
+ Object.defineProperty(o, k2, desc);
12
+ }) : (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ o[k2] = m[k];
15
+ }));
16
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
17
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
18
+ };
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.SDK_MAX_COMPATIBLE_VERSION = exports.SDK_MIN_COMPATIBLE_VERSION = exports.SDK_VERSION = void 0;
21
+ exports.deriveDatabaseFromApiKey = deriveDatabaseFromApiKey;
22
+ // SDK Version
23
+ exports.SDK_VERSION = '1.0.0';
24
+ exports.SDK_MIN_COMPATIBLE_VERSION = '1.0.0';
25
+ exports.SDK_MAX_COMPATIBLE_VERSION = '1.9.999';
26
+ // Derive tenant database name from API key
27
+ function deriveDatabaseFromApiKey(apiKey) {
28
+ const parts = apiKey.split('_');
29
+ if (parts.length !== 3 || parts[0] !== 'chat')
30
+ return null;
31
+ const tenantId = parts[1];
32
+ if (!tenantId)
33
+ return null;
34
+ return `chat_${tenantId}`;
35
+ }
36
+ // Re-export API key utilities
37
+ __exportStar(require("./apikey"), exports);
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@enfin/chat-shared",
3
+ "version": "1.2.0",
4
+ "description": "Shared types and utilities for chat platform SDKs",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "module": "dist/esm/index.js",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/esm/index.js",
11
+ "require": "./dist/index.js"
12
+ }
13
+ },
14
+ "scripts": {
15
+ "build": "tsc -p tsconfig.json && tsc -p tsconfig.json --module esnext --outDir dist/esm",
16
+ "prepublishOnly": "npm run build",
17
+ "dev": "tsc --watch",
18
+ "test": "jest"
19
+ },
20
+ "dependencies": {},
21
+ "devDependencies": {
22
+ "typescript": "^5.0.0",
23
+ "@types/node": "^20.0.0"
24
+ },
25
+ "files": [
26
+ "dist",
27
+ "src"
28
+ ]
29
+ }
package/src/apikey.ts ADDED
@@ -0,0 +1,74 @@
1
+ // API Key generation and validation utilities
2
+
3
+ const BASE62_CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
4
+
5
+ /**
6
+ * Generate a random API key for chat platform
7
+ * Format: chat_{tenantId}_{checksum}
8
+ */
9
+ export function generateApiKey(tenantId: string): string {
10
+ const randomPart = generateRandomString(10);
11
+ return `chat_${tenantId}_${randomPart}`;
12
+ }
13
+
14
+ /**
15
+ * Generate a random tenant ID
16
+ */
17
+ export function generateTenantId(): string {
18
+ return generateRandomString(8);
19
+ }
20
+
21
+ /**
22
+ * Generate a random string of specified length using Base62
23
+ */
24
+ function generateRandomString(length: number): string {
25
+ let result = '';
26
+ for (let i = 0; i < length; i++) {
27
+ result += BASE62_CHARS[Math.floor(Math.random() * BASE62_CHARS.length)];
28
+ }
29
+ return result;
30
+ }
31
+
32
+ /**
33
+ * Validate API key format
34
+ * Returns true if format is correct: chat_{tenantId}_{random}
35
+ */
36
+ export function validateApiKeyFormat(apiKey: string): boolean {
37
+ const parts = apiKey.split('_');
38
+ return (
39
+ parts.length === 3 &&
40
+ parts[0] === 'chat' &&
41
+ parts[1].length >= 6 &&
42
+ parts[2].length >= 6
43
+ );
44
+ }
45
+
46
+ /**
47
+ * Extract tenant ID from API key
48
+ * Format: chat_{tenantId}_{random}
49
+ */
50
+ export function extractTenantId(apiKey: string): string | null {
51
+ const parts = apiKey.split('_');
52
+ if (parts.length !== 3 || parts[0] !== 'chat') return null;
53
+ return parts[1];
54
+ }
55
+
56
+ /**
57
+ * Generate a checksum for API key validation
58
+ */
59
+ export function generateChecksum(apiKey: string): string {
60
+ let hash = 0;
61
+ for (let i = 0; i < apiKey.length; i++) {
62
+ const char = apiKey.charCodeAt(i);
63
+ hash = (hash << 5) - hash + char;
64
+ hash = hash & hash;
65
+ }
66
+ return Math.abs(hash).toString(36);
67
+ }
68
+
69
+ /**
70
+ * Validate API key with checksum
71
+ */
72
+ export function validateApiKeyWithChecksum(apiKey: string, expectedChecksum: string): boolean {
73
+ return generateChecksum(apiKey) === expectedChecksum;
74
+ }
package/src/index.ts ADDED
@@ -0,0 +1,216 @@
1
+ // ============================================
2
+ // @enfin/chat-shared - Core Types
3
+ // ============================================
4
+
5
+ // API Key Validation Types
6
+ export interface ApiKeyValidationResponse {
7
+ valid: boolean;
8
+ apiKey: string;
9
+ mode: 'managed' | 'external-db';
10
+ database?: string;
11
+ version: string;
12
+ tenantId: string;
13
+ error?: string;
14
+ }
15
+
16
+ // Message Types
17
+ export interface Message {
18
+ _id: string;
19
+ roomId: string;
20
+ senderId: string;
21
+ senderName: string;
22
+ senderAvatar?: string;
23
+ content: string;
24
+ fileUrl?: string;
25
+ fileType?: string;
26
+ fileName?: string;
27
+ createdAt: Date;
28
+ updatedAt: Date;
29
+ readBy: ReadReceipt[];
30
+ }
31
+
32
+ export interface ReadReceipt {
33
+ userId: string;
34
+ readAt: Date;
35
+ }
36
+
37
+ // Room Types
38
+ export interface Room {
39
+ _id: string;
40
+ name: string;
41
+ type: 'direct' | 'group';
42
+ members: string[];
43
+ createdBy: string;
44
+ createdAt: Date;
45
+ updatedAt: Date;
46
+ lastMessage?: string | Message;
47
+ lastActivity: Date;
48
+ avatar?: string;
49
+ }
50
+
51
+ // User Presence Types
52
+ export interface PresenceStatus {
53
+ userId: string;
54
+ status: 'online' | 'offline' | 'away';
55
+ lastSeen: Date;
56
+ typingIn?: string[];
57
+ }
58
+
59
+ // Audio Call Types
60
+ export interface AudioCall {
61
+ _id: string;
62
+ roomId: string;
63
+ initiatorId: string;
64
+ participantId: string;
65
+ status: 'ringing' | 'active' | 'ended';
66
+ startedAt: Date;
67
+ endedAt?: Date;
68
+ duration?: number;
69
+ }
70
+
71
+ // Socket Events
72
+ export interface SocketMessage {
73
+ type: string;
74
+ data: any;
75
+ timestamp: number;
76
+ }
77
+
78
+ export interface SendMessagePayload {
79
+ roomId: string;
80
+ content: string;
81
+ fileUrl?: string;
82
+ fileType?: string;
83
+ fileName?: string;
84
+ }
85
+
86
+ export interface TypingPayload {
87
+ roomId: string;
88
+ userId: string;
89
+ userName: string;
90
+ typing?: boolean;
91
+ content?: string;
92
+ }
93
+
94
+ export interface PresencePayload {
95
+ userId: string;
96
+ status: 'online' | 'offline' | 'away';
97
+ }
98
+
99
+ export interface CallSignalingPayload {
100
+ roomId: string;
101
+ initiatorId: string;
102
+ participantId: string;
103
+ offer?: RTCSessionDescription;
104
+ answer?: RTCSessionDescription;
105
+ candidate?: RTCIceCandidate;
106
+ }
107
+
108
+ // File Upload Types
109
+ export interface FileUploadPayload {
110
+ roomId: string;
111
+ fileName: string;
112
+ fileSize: number;
113
+ fileType: string;
114
+ }
115
+
116
+ export interface FileUploadResponse {
117
+ success: boolean;
118
+ fileUrl: string;
119
+ fileName: string;
120
+ error?: string;
121
+ }
122
+
123
+ // Configuration Types
124
+ export interface ChatConfig {
125
+ apiKey: string;
126
+ mongoUri?: string;
127
+ mode?: 'managed' | 'external-db';
128
+ version?: string;
129
+ validationUrl?: string;
130
+ jwtSecret?: string;
131
+ jwtIssuer?: string;
132
+ jwtAudience?: string;
133
+ iceServers?: RTCIceServer[];
134
+ turnServers?: TurnServerConfig[];
135
+ rateLimit?: {
136
+ messagesPerMinute?: number;
137
+ eventsPerMinute?: number;
138
+ };
139
+ hooks?: ChatHooks;
140
+ ringtoneUrl?: string;
141
+ }
142
+
143
+ // Chat Hooks for customization
144
+ export interface ChatHooks {
145
+ beforeMessageCreate?: (message: Message, user: AuthContext) => Message;
146
+ afterMessageCreate?: (message: Message) => void;
147
+ beforeRoomCreate?: (room: Room, user: AuthContext) => Room;
148
+ afterRoomCreate?: (room: Room) => void;
149
+ }
150
+
151
+ // SDK Version
152
+ export const SDK_VERSION = '1.0.0';
153
+ export const SDK_MIN_COMPATIBLE_VERSION = '1.0.0';
154
+ export const SDK_MAX_COMPATIBLE_VERSION = '1.9.999';
155
+
156
+ // TURN Server Config
157
+ export interface TurnServerConfig {
158
+ urls: string;
159
+ username?: string;
160
+ credential?: string;
161
+ }
162
+
163
+ // Auth Context
164
+ export interface AuthContext {
165
+ userId: string;
166
+ tenantId: string;
167
+ roles: string[];
168
+ }
169
+
170
+ // Version Check Result
171
+ export interface VersionCheckResult {
172
+ compatible: boolean;
173
+ severity: 'ok' | 'warning' | 'error';
174
+ message: string;
175
+ }
176
+
177
+ // Paginated Messages
178
+ export interface PaginatedMessages {
179
+ messages: Message[];
180
+ hasMore: boolean;
181
+ nextCursor: string | null;
182
+ }
183
+
184
+ export interface GetMessagesPayload {
185
+ roomId: string;
186
+ limit?: number;
187
+ beforeCursor?: string;
188
+ }
189
+
190
+ // Rate Limit Event
191
+ export interface RateLimitExceededPayload {
192
+ code: 'RATE_LIMIT_EXCEEDED';
193
+ message: string;
194
+ retryAfterMs?: number;
195
+ }
196
+
197
+ // Admin Tenant Stats
198
+ export interface AdminTenantStats {
199
+ tenantId: string;
200
+ totalMessages: number;
201
+ totalRooms: number;
202
+ activeConnections: number;
203
+ lastActivity: Date;
204
+ }
205
+
206
+ // Derive tenant database name from API key
207
+ export function deriveDatabaseFromApiKey(apiKey: string): string | null {
208
+ const parts = apiKey.split('_');
209
+ if (parts.length !== 3 || parts[0] !== 'chat') return null;
210
+ const tenantId = parts[1];
211
+ if (!tenantId) return null;
212
+ return `chat_${tenantId}`;
213
+ }
214
+
215
+ // Re-export API key utilities
216
+ export * from './apikey';