@alliance-droid/chat-widget 0.1.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.
Files changed (42) hide show
  1. package/README.md +102 -0
  2. package/dist/client/connection.d.ts +39 -0
  3. package/dist/client/connection.js +198 -0
  4. package/dist/client/index.d.ts +6 -0
  5. package/dist/client/index.js +6 -0
  6. package/dist/components/ChatInput.svelte +73 -0
  7. package/dist/components/ChatInput.svelte.d.ts +9 -0
  8. package/dist/components/ChatPanel.svelte +117 -0
  9. package/dist/components/ChatPanel.svelte.d.ts +20 -0
  10. package/dist/components/ChatWidget.svelte +160 -0
  11. package/dist/components/ChatWidget.svelte.d.ts +4 -0
  12. package/dist/components/MessageBubble.svelte +60 -0
  13. package/dist/components/MessageBubble.svelte.d.ts +8 -0
  14. package/dist/components/MessageList.svelte +58 -0
  15. package/dist/components/MessageList.svelte.d.ts +10 -0
  16. package/dist/components/SupportChat.svelte +133 -0
  17. package/dist/components/SupportChat.svelte.d.ts +4 -0
  18. package/dist/index.d.ts +15 -0
  19. package/dist/index.js +17 -0
  20. package/dist/server/ai/anthropic.d.ts +16 -0
  21. package/dist/server/ai/anthropic.js +109 -0
  22. package/dist/server/ai/openai.d.ts +16 -0
  23. package/dist/server/ai/openai.js +112 -0
  24. package/dist/server/ai/provider.d.ts +21 -0
  25. package/dist/server/ai/provider.js +6 -0
  26. package/dist/server/ai/xai.d.ts +16 -0
  27. package/dist/server/ai/xai.js +113 -0
  28. package/dist/server/escalation.d.ts +18 -0
  29. package/dist/server/escalation.js +61 -0
  30. package/dist/server/handler.d.ts +8 -0
  31. package/dist/server/handler.js +235 -0
  32. package/dist/server/index.d.ts +11 -0
  33. package/dist/server/index.js +10 -0
  34. package/dist/server/session.d.ts +52 -0
  35. package/dist/server/session.js +115 -0
  36. package/dist/stores/chat.d.ts +18 -0
  37. package/dist/stores/chat.js +40 -0
  38. package/dist/stores/connection.d.ts +20 -0
  39. package/dist/stores/connection.js +52 -0
  40. package/dist/types.d.ts +79 -0
  41. package/dist/types.js +4 -0
  42. package/package.json +73 -0
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Session Manager
3
+ *
4
+ * In-memory session storage (Redis adapter TODO)
5
+ */
6
+ export class SessionManager {
7
+ sessions = new Map();
8
+ ttl;
9
+ cleanupInterval;
10
+ constructor(ttlMs = 24 * 60 * 60 * 1000) {
11
+ this.ttl = ttlMs;
12
+ // Cleanup expired sessions every hour
13
+ this.cleanupInterval = setInterval(() => this.cleanup(), 60 * 60 * 1000);
14
+ }
15
+ /**
16
+ * Create a new session
17
+ */
18
+ create(userId, userMeta) {
19
+ const id = this.generateId();
20
+ const now = Date.now();
21
+ const session = {
22
+ id,
23
+ status: 'ai',
24
+ messages: [],
25
+ userId,
26
+ userMeta,
27
+ createdAt: now,
28
+ updatedAt: now
29
+ };
30
+ this.sessions.set(id, session);
31
+ return session;
32
+ }
33
+ /**
34
+ * Get a session by ID
35
+ */
36
+ get(id) {
37
+ const session = this.sessions.get(id);
38
+ if (session && this.isExpired(session)) {
39
+ this.sessions.delete(id);
40
+ return undefined;
41
+ }
42
+ return session;
43
+ }
44
+ /**
45
+ * Add a message to a session
46
+ */
47
+ addMessage(sessionId, message) {
48
+ const session = this.get(sessionId);
49
+ if (!session)
50
+ return;
51
+ session.messages.push(message);
52
+ session.updatedAt = Date.now();
53
+ }
54
+ /**
55
+ * Update session status
56
+ */
57
+ setStatus(sessionId, status) {
58
+ const session = this.get(sessionId);
59
+ if (!session)
60
+ return;
61
+ session.status = status;
62
+ session.updatedAt = Date.now();
63
+ }
64
+ /**
65
+ * Assign a support agent to a session
66
+ */
67
+ assignSupport(sessionId, agentId) {
68
+ const session = this.get(sessionId);
69
+ if (!session)
70
+ return false;
71
+ // Don't reassign if already assigned
72
+ if (session.supportAgentId)
73
+ return false;
74
+ session.supportAgentId = agentId;
75
+ session.status = 'human';
76
+ session.updatedAt = Date.now();
77
+ return true;
78
+ }
79
+ /**
80
+ * Delete a session
81
+ */
82
+ delete(id) {
83
+ this.sessions.delete(id);
84
+ }
85
+ /**
86
+ * Cleanup expired sessions
87
+ */
88
+ cleanup() {
89
+ const now = Date.now();
90
+ for (const [id, session] of this.sessions) {
91
+ if (this.isExpired(session)) {
92
+ this.sessions.delete(id);
93
+ }
94
+ }
95
+ }
96
+ /**
97
+ * Check if session is expired
98
+ */
99
+ isExpired(session) {
100
+ return Date.now() - session.updatedAt > this.ttl;
101
+ }
102
+ /**
103
+ * Generate unique session ID
104
+ */
105
+ generateId() {
106
+ return `${Date.now().toString(36)}-${Math.random().toString(36).substr(2, 9)}`;
107
+ }
108
+ /**
109
+ * Cleanup on shutdown
110
+ */
111
+ destroy() {
112
+ clearInterval(this.cleanupInterval);
113
+ this.sessions.clear();
114
+ }
115
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Chat Store
3
+ *
4
+ * Manages chat messages, status, and typing indicators
5
+ */
6
+ import type { Message, ChatStatus } from '../types.js';
7
+ export declare const chatStore: {
8
+ messages: import("svelte/store").Writable<Message[]>;
9
+ status: import("svelte/store").Writable<ChatStatus>;
10
+ isTyping: import("svelte/store").Writable<boolean>;
11
+ typingSender: import("svelte/store").Writable<"ai" | "human" | null>;
12
+ addMessage(message: Message): void;
13
+ updateMessage(id: string, updates: Partial<Message>): void;
14
+ setStatus(newStatus: ChatStatus): void;
15
+ setTyping(typing: boolean, sender?: "ai" | "human" | null): void;
16
+ clear(): void;
17
+ messageCount: import("svelte/store").Readable<number>;
18
+ };
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Chat Store
3
+ *
4
+ * Manages chat messages, status, and typing indicators
5
+ */
6
+ import { writable, derived } from 'svelte/store';
7
+ function createChatStore() {
8
+ const messages = writable([]);
9
+ const status = writable('idle');
10
+ const isTyping = writable(false);
11
+ const typingSender = writable(null);
12
+ return {
13
+ messages,
14
+ status,
15
+ isTyping,
16
+ typingSender,
17
+ addMessage(message) {
18
+ messages.update(msgs => [...msgs, message]);
19
+ },
20
+ updateMessage(id, updates) {
21
+ messages.update(msgs => msgs.map(msg => (msg.id === id ? { ...msg, ...updates } : msg)));
22
+ },
23
+ setStatus(newStatus) {
24
+ status.set(newStatus);
25
+ },
26
+ setTyping(typing, sender = null) {
27
+ isTyping.set(typing);
28
+ typingSender.set(sender);
29
+ },
30
+ clear() {
31
+ messages.set([]);
32
+ status.set('idle');
33
+ isTyping.set(false);
34
+ typingSender.set(null);
35
+ },
36
+ // Derived store for message count
37
+ messageCount: derived(messages, $messages => $messages.length)
38
+ };
39
+ }
40
+ export const chatStore = createChatStore();
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Connection Store
3
+ *
4
+ * Manages WebSocket connection state
5
+ */
6
+ import type { ConnectionState } from '../types.js';
7
+ export declare const connectionStore: {
8
+ state: import("svelte/store").Writable<ConnectionState>;
9
+ sessionId: import("svelte/store").Writable<string | null>;
10
+ error: import("svelte/store").Writable<string | null>;
11
+ reconnectAttempts: import("svelte/store").Writable<number>;
12
+ setConnected(newSessionId: string): void;
13
+ setConnecting(): void;
14
+ setReconnecting(): void;
15
+ setDisconnected(errorMessage?: string): void;
16
+ setError(errorMessage: string): void;
17
+ clearError(): void;
18
+ reset(): void;
19
+ isConnected: import("svelte/store").Readable<boolean>;
20
+ };
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Connection Store
3
+ *
4
+ * Manages WebSocket connection state
5
+ */
6
+ import { writable, derived } from 'svelte/store';
7
+ function createConnectionStore() {
8
+ const state = writable('disconnected');
9
+ const sessionId = writable(null);
10
+ const error = writable(null);
11
+ const reconnectAttempts = writable(0);
12
+ return {
13
+ state,
14
+ sessionId,
15
+ error,
16
+ reconnectAttempts,
17
+ setConnected(newSessionId) {
18
+ state.set('connected');
19
+ sessionId.set(newSessionId);
20
+ error.set(null);
21
+ reconnectAttempts.set(0);
22
+ },
23
+ setConnecting() {
24
+ state.set('connecting');
25
+ },
26
+ setReconnecting() {
27
+ state.set('reconnecting');
28
+ reconnectAttempts.update(n => n + 1);
29
+ },
30
+ setDisconnected(errorMessage) {
31
+ state.set('disconnected');
32
+ if (errorMessage) {
33
+ error.set(errorMessage);
34
+ }
35
+ },
36
+ setError(errorMessage) {
37
+ error.set(errorMessage);
38
+ },
39
+ clearError() {
40
+ error.set(null);
41
+ },
42
+ reset() {
43
+ state.set('disconnected');
44
+ sessionId.set(null);
45
+ error.set(null);
46
+ reconnectAttempts.set(0);
47
+ },
48
+ // Derived store for connection status
49
+ isConnected: derived(state, $state => $state === 'connected')
50
+ };
51
+ }
52
+ export const connectionStore = createConnectionStore();
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Chat Widget Types
3
+ */
4
+ export interface Message {
5
+ id: string;
6
+ content: string;
7
+ sender: 'user' | 'ai' | 'human' | 'system';
8
+ timestamp: number;
9
+ streaming?: boolean;
10
+ }
11
+ export interface ClientMessage {
12
+ type: 'message' | 'escalate' | 'typing' | 'read' | 'join';
13
+ sessionId?: string;
14
+ content?: string;
15
+ metadata?: Record<string, unknown>;
16
+ }
17
+ export interface ServerMessage {
18
+ type: 'message' | 'typing' | 'status' | 'error' | 'escalated' | 'connected' | 'auth_success' | 'auth_error';
19
+ id?: string;
20
+ content?: string;
21
+ sender?: 'user' | 'ai' | 'human' | 'system';
22
+ timestamp?: number;
23
+ streaming?: boolean;
24
+ sessionId?: string;
25
+ error?: string;
26
+ }
27
+ export type ConnectionState = 'disconnected' | 'connecting' | 'connected' | 'reconnecting';
28
+ export type ChatStatus = 'idle' | 'ai' | 'escalating' | 'human' | 'closed';
29
+ export interface ChatWidgetProps {
30
+ serverUrl: string;
31
+ apiKey?: string;
32
+ reconnect?: boolean;
33
+ reconnectInterval?: number;
34
+ theme?: 'light' | 'dark' | 'auto';
35
+ position?: 'bottom-right' | 'bottom-left';
36
+ greeting?: string;
37
+ placeholder?: string;
38
+ fabIcon?: string;
39
+ enableSounds?: boolean;
40
+ enableEscalation?: boolean;
41
+ escalationLabel?: string;
42
+ userId?: string;
43
+ userMeta?: Record<string, unknown>;
44
+ class?: string;
45
+ }
46
+ export interface SupportChatProps {
47
+ serverUrl: string;
48
+ sessionId: string;
49
+ class?: string;
50
+ }
51
+ export interface ChatServerConfig {
52
+ ai: AIProviderConfig;
53
+ escalation?: EscalationConfig;
54
+ storage?: 'memory' | 'redis';
55
+ sessionTTL?: number;
56
+ }
57
+ export interface AIProviderConfig {
58
+ provider: 'xai' | 'openai' | 'anthropic';
59
+ apiKey: string;
60
+ model?: string;
61
+ systemPrompt?: string;
62
+ }
63
+ export interface EscalationConfig {
64
+ telegram: {
65
+ botToken: string;
66
+ chatId: string;
67
+ };
68
+ joinUrl: (sessionId: string) => string;
69
+ }
70
+ export interface ChatSession {
71
+ id: string;
72
+ status: ChatStatus;
73
+ messages: Message[];
74
+ userId?: string;
75
+ userMeta?: Record<string, unknown>;
76
+ supportAgentId?: string;
77
+ createdAt: number;
78
+ updatedAt: number;
79
+ }
package/dist/types.js ADDED
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Chat Widget Types
3
+ */
4
+ export {};
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "@alliance-droid/chat-widget",
3
+ "version": "0.1.0",
4
+ "description": "Svelte chat widget with AI support and human escalation",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite dev",
8
+ "build": "vite build",
9
+ "build:package": "svelte-kit sync && svelte-package && publint",
10
+ "preview": "vite preview",
11
+ "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
12
+ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
13
+ "test": "vitest run",
14
+ "test:watch": "vitest",
15
+ "prepublishOnly": "npm run build:package"
16
+ },
17
+ "exports": {
18
+ ".": {
19
+ "types": "./dist/index.d.ts",
20
+ "svelte": "./dist/index.js",
21
+ "default": "./dist/index.js"
22
+ },
23
+ "./server": {
24
+ "types": "./dist/server/index.d.ts",
25
+ "default": "./dist/server/index.js"
26
+ }
27
+ },
28
+ "files": [
29
+ "dist"
30
+ ],
31
+ "svelte": "./dist/index.js",
32
+ "types": "./dist/index.d.ts",
33
+ "peerDependencies": {
34
+ "svelte": "^5.0.0"
35
+ },
36
+ "devDependencies": {
37
+ "@sveltejs/adapter-auto": "^4.0.0",
38
+ "@sveltejs/kit": "^2.16.0",
39
+ "@sveltejs/package": "^2.3.0",
40
+ "@sveltejs/vite-plugin-svelte": "^5.0.0",
41
+ "@types/node": "^22.0.0",
42
+ "@types/ws": "^8.5.0",
43
+ "autoprefixer": "^10.4.0",
44
+ "postcss": "^8.4.0",
45
+ "publint": "^0.3.0",
46
+ "svelte": "^5.0.0",
47
+ "svelte-check": "^4.0.0",
48
+ "tailwindcss": "^3.4.0",
49
+ "typescript": "^5.0.0",
50
+ "vite": "^6.0.0",
51
+ "vitest": "^2.0.0"
52
+ },
53
+ "dependencies": {
54
+ "@alliance-droid/svelte-component-library": "^0.1.0",
55
+ "ws": "^8.18.0"
56
+ },
57
+ "keywords": [
58
+ "svelte",
59
+ "chat",
60
+ "widget",
61
+ "ai",
62
+ "support",
63
+ "customer-service"
64
+ ],
65
+ "license": "MIT",
66
+ "publishConfig": {
67
+ "access": "restricted"
68
+ },
69
+ "repository": {
70
+ "type": "git",
71
+ "url": "https://github.com/alliance-droid/chat-widget"
72
+ }
73
+ }