@chatwidgetai/chat-widget 0.1.0 → 0.1.1

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.
@@ -8,6 +8,15 @@ export interface ApiClientConfig {
8
8
  apiKey: string;
9
9
  apiUrl: string;
10
10
  }
11
+ export declare class ApiError extends Error {
12
+ status: number;
13
+ details?: unknown;
14
+ retryAfterMs?: number;
15
+ constructor(message: string, status: number, options?: {
16
+ details?: unknown;
17
+ retryAfterMs?: number;
18
+ });
19
+ }
11
20
  export declare class WidgetApiClient {
12
21
  private config;
13
22
  constructor(config: ApiClientConfig);
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAE7D,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAkB;gBAEpB,MAAM,EAAE,eAAe;IAInC;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,YAAY,CAAC;IAoBlC,uBAAuB,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,mBAAmB,EAAE,CAAA;KAAC,CAAC;IAqB9G;;OAEG;IACG,UAAU,CAAC,cAAc,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAyB7G;;;OAGG;IACG,WAAW,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAyB9G;;OAEG;IACG,gBAAgB,CACpB,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,MAAM,EAAE,GACjB,OAAO,CAAC,GAAG,CAAC;IA4Cf;;OAEG;IACG,cAAc,CAClB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,UAAU,GAAG,UAAU,GAChC,OAAO,CAAC,IAAI,CAAC;IA4BhB;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC;CAsBzC"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAE7D,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,QAAS,SAAQ,KAAK;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;gBAEV,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE;CAOpG;AAwDD,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAkB;gBAEpB,MAAM,EAAE,eAAe;IAInC;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,YAAY,CAAC;IAoBlC,uBAAuB,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,mBAAmB,EAAE,CAAA;KAAE,CAAC;IAqBhH;;OAEG;IACG,UAAU,CAAC,cAAc,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAwB7G;;;OAGG;IACG,WAAW,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAwB9G;;OAEG;IACG,gBAAgB,CACpB,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,MAAM,EAAE,GACjB,OAAO,CAAC,GAAG,CAAC;IA2Cf;;OAEG;IACG,cAAc,CAClB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,UAAU,GAAG,UAAU,GAChC,OAAO,CAAC,IAAI,CAAC;IA4BhB;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC;CAsBzC"}
@@ -1 +1 @@
1
- {"version":3,"file":"ChatWidget.d.ts","sourceRoot":"","sources":["../../src/components/ChatWidget.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAA8B,MAAM,OAAO,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAGvC,OAAO,sBAAsB,CAAC;AAE9B,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,WAAW,CAkI5C,CAAC"}
1
+ {"version":3,"file":"ChatWidget.d.ts","sourceRoot":"","sources":["../../src/components/ChatWidget.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAA8B,MAAM,OAAO,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAGvC,OAAO,sBAAsB,CAAC;AAE9B,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,WAAW,CA8H5C,CAAC"}
@@ -11,8 +11,6 @@ export interface ChatWindowProps {
11
11
  error: string | null;
12
12
  config: WidgetConfig | null;
13
13
  onSendMessage: (message: string) => void;
14
- onApproveAction: () => void;
15
- onRejectAction: () => void;
16
14
  onClose: () => void;
17
15
  onFeedback: (messageId: string, feedback: 'positive' | 'negative') => void;
18
16
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ChatWindow.d.ts","sourceRoot":"","sources":["../../src/components/ChatWindow.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAA8B,MAAM,OAAO,CAAC;AACnD,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAK7D,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,YAAY,GAAG,IAAI,CAAC;IAC5B,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,UAAU,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,GAAG,UAAU,KAAK,IAAI,CAAC;CAC5E;AAED,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAgIhD,CAAC"}
1
+ {"version":3,"file":"ChatWindow.d.ts","sourceRoot":"","sources":["../../src/components/ChatWindow.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAA8B,MAAM,OAAO,CAAC;AACnD,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAK7D,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,YAAY,GAAG,IAAI,CAAC;IAC5B,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,UAAU,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,GAAG,UAAU,KAAK,IAAI,CAAC;CAC5E;AAED,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAgIhD,CAAC"}
@@ -3,7 +3,6 @@
3
3
  * Main state management for chat functionality
4
4
  */
5
5
  import { ConversationMessage, WidgetConfig } from '../types';
6
- import { ClientAction } from '../utils/actionExecutor';
7
6
  export interface UseChatOptions {
8
7
  widgetId: string;
9
8
  apiKey: string;
@@ -11,12 +10,6 @@ export interface UseChatOptions {
11
10
  onMessage?: (message: ConversationMessage) => void;
12
11
  onError?: (error: Error) => void;
13
12
  }
14
- export interface PendingAction {
15
- action: ClientAction;
16
- message: string;
17
- continueToken: string;
18
- messageId: string;
19
- }
20
13
  export interface UseChatReturn {
21
14
  messages: ConversationMessage[];
22
15
  isLoading: boolean;
@@ -24,10 +17,7 @@ export interface UseChatReturn {
24
17
  error: string | null;
25
18
  config: WidgetConfig | null;
26
19
  conversationId: string;
27
- pendingAction: PendingAction | null;
28
20
  sendMessage: (content: string) => Promise<void>;
29
- approveAction: () => Promise<void>;
30
- rejectAction: () => void;
31
21
  clearMessages: () => void;
32
22
  submitFeedback: (messageId: string, feedback: 'positive' | 'negative') => Promise<void>;
33
23
  }
@@ -1 +1 @@
1
- {"version":3,"file":"useChat.d.ts","sourceRoot":"","sources":["../../src/hooks/useChat.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAa,MAAM,UAAU,CAAC;AAIxE,OAAO,EAAuB,YAAY,EAAgB,MAAM,yBAAyB,CAAC;AAE1F,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,IAAI,CAAC;IACnD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,YAAY,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,YAAY,GAAG,IAAI,CAAC;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,aAAa,GAAG,IAAI,CAAC;IACpC,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,cAAc,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,GAAG,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACzF;AAED,wBAAgB,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,aAAa,CAoZ9D"}
1
+ {"version":3,"file":"useChat.d.ts","sourceRoot":"","sources":["../../src/hooks/useChat.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAa,MAAM,UAAU,CAAC;AAKxE,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,IAAI,CAAC;IACnD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,YAAY,GAAG,IAAI,CAAC;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,cAAc,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,GAAG,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACzF;AAkED,wBAAgB,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,aAAa,CAyS9D"}
package/dist/index.d.ts CHANGED
@@ -4,6 +4,7 @@
4
4
  */
5
5
  export { ChatWidget } from './components/ChatWidget';
6
6
  export { useChat } from './hooks/useChat';
7
+ export { ApiError } from './api/client';
7
8
  export type { WidgetProps, WidgetTheme, WidgetPosition, WidgetSize, WidgetAppearance, WidgetBehavior, WidgetConfig, ConversationMessage, ChatMessage, KbDocument, SourceDisplayMode, ChatState, } from './types';
8
9
  export type { UseChatOptions, UseChatReturn, } from './hooks/useChat';
9
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAG1C,YAAY,EACV,WAAW,EACX,WAAW,EACX,cAAc,EACd,UAAU,EACV,gBAAgB,EAChB,cAAc,EACd,YAAY,EACZ,mBAAmB,EACnB,WAAW,EACX,UAAU,EACV,iBAAiB,EACjB,SAAS,GACV,MAAM,SAAS,CAAC;AAEjB,YAAY,EACV,cAAc,EACd,aAAa,GACd,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAGxC,YAAY,EACV,WAAW,EACX,WAAW,EACX,cAAc,EACd,UAAU,EACV,gBAAgB,EAChB,cAAc,EACd,YAAY,EACZ,mBAAmB,EACnB,WAAW,EACX,UAAU,EACV,iBAAiB,EACjB,SAAS,GACV,MAAM,SAAS,CAAC;AAEjB,YAAY,EACV,cAAc,EACd,aAAa,GACd,MAAM,iBAAiB,CAAC"}
package/dist/index.esm.js CHANGED
@@ -5,6 +5,65 @@ import { useState, useRef, useEffect, useCallback, useMemo } from 'react';
5
5
  * API Client for Widget Communication
6
6
  * Handles all HTTP requests to the widget API
7
7
  */
8
+ class ApiError extends Error {
9
+ constructor(message, status, options) {
10
+ super(message);
11
+ this.name = 'ApiError';
12
+ this.status = status;
13
+ this.details = options?.details;
14
+ this.retryAfterMs = options?.retryAfterMs;
15
+ }
16
+ }
17
+ function parseRetryAfter(headerValue) {
18
+ if (!headerValue)
19
+ return undefined;
20
+ const seconds = Number(headerValue);
21
+ if (!Number.isNaN(seconds) && seconds >= 0) {
22
+ return seconds * 1000;
23
+ }
24
+ const date = Date.parse(headerValue);
25
+ if (!Number.isNaN(date)) {
26
+ return Math.max(0, date - Date.now());
27
+ }
28
+ return undefined;
29
+ }
30
+ async function buildApiError(response, defaultMessage) {
31
+ let parsedBody = null;
32
+ let message = defaultMessage;
33
+ try {
34
+ const raw = await response.text();
35
+ if (raw) {
36
+ try {
37
+ parsedBody = JSON.parse(raw);
38
+ if (typeof parsedBody === 'object' && parsedBody !== null) {
39
+ const body = parsedBody;
40
+ message = typeof body.error === 'string'
41
+ ? body.error
42
+ : typeof body.message === 'string'
43
+ ? body.message
44
+ : defaultMessage;
45
+ }
46
+ else {
47
+ message = raw;
48
+ }
49
+ }
50
+ catch {
51
+ message = raw;
52
+ }
53
+ }
54
+ }
55
+ catch {
56
+ // ignore body parsing errors
57
+ }
58
+ let retryAfterMs = parseRetryAfter(response.headers.get('retry-after'));
59
+ if (retryAfterMs === undefined) {
60
+ retryAfterMs = parseRetryAfter(response.headers.get('x-ratelimit-reset'));
61
+ }
62
+ return new ApiError(message || defaultMessage, response.status, {
63
+ details: parsedBody,
64
+ retryAfterMs,
65
+ });
66
+ }
8
67
  class WidgetApiClient {
9
68
  constructor(config) {
10
69
  this.config = config;
@@ -21,23 +80,24 @@ class WidgetApiClient {
21
80
  },
22
81
  });
23
82
  if (!response.ok) {
24
- throw new Error(`Failed to fetch config: ${response.statusText}`);
83
+ throw await buildApiError(response, 'Failed to fetch config');
25
84
  }
26
85
  const json = await response.json();
27
86
  return json;
28
87
  }
29
88
  async getOrCreateConversation(conversationId) {
30
- const response = await fetch(`${this.config.apiUrl}/api/widget/${this.config.widgetId}/conversation/${conversationId || '0'}`, {
89
+ const baseUrl = `${this.config.apiUrl}/api/widget/${this.config.widgetId}/conversation`;
90
+ const query = conversationId ? `?conversationId=${encodeURIComponent(conversationId)}` : '';
91
+ const response = await fetch(`${baseUrl}${query}`, {
31
92
  method: 'GET',
32
93
  headers: {
33
94
  'Authorization': `Bearer ${this.config.apiKey}`,
34
- }
95
+ },
35
96
  });
36
97
  if (!response.ok) {
37
- const error = await response.json().catch(() => ({ error: response.statusText }));
38
- throw new Error(error.error || 'Failed to upload file');
98
+ throw await buildApiError(response, 'Failed to load conversation');
39
99
  }
40
- return await response.json();
100
+ return response.json();
41
101
  }
42
102
  /**
43
103
  * Upload a file
@@ -54,8 +114,7 @@ class WidgetApiClient {
54
114
  body: formData,
55
115
  });
56
116
  if (!response.ok) {
57
- const error = await response.json().catch(() => ({ error: response.statusText }));
58
- throw new Error(error.error || 'Failed to upload file');
117
+ throw await buildApiError(response, 'Failed to upload file');
59
118
  }
60
119
  const result = await response.json();
61
120
  return result.file;
@@ -78,8 +137,7 @@ class WidgetApiClient {
78
137
  }),
79
138
  });
80
139
  if (!response.ok) {
81
- const error = await response.json().catch(() => ({ error: response.statusText }));
82
- throw new Error(error.error || 'Failed to send message');
140
+ throw await buildApiError(response, 'Failed to send message');
83
141
  }
84
142
  return response.json();
85
143
  }
@@ -101,8 +159,7 @@ class WidgetApiClient {
101
159
  }),
102
160
  });
103
161
  if (!response.ok) {
104
- const error = await response.json().catch(() => ({ error: response.statusText }));
105
- throw new Error(error.error || `Agent request failed with status ${response.status}`);
162
+ throw await buildApiError(response, `Agent request failed with status ${response.status}`);
106
163
  }
107
164
  const data = await response.json();
108
165
  // Check if response indicates an error
@@ -114,7 +171,7 @@ class WidgetApiClient {
114
171
  catch (error) {
115
172
  // Enhance error messages
116
173
  if (error instanceof TypeError && error.message.includes('fetch')) {
117
- throw new Error('Network error: Unable to reach the server');
174
+ throw new ApiError('Network error: Unable to reach the server', 0);
118
175
  }
119
176
  throw error;
120
177
  }
@@ -136,13 +193,13 @@ class WidgetApiClient {
136
193
  }),
137
194
  });
138
195
  if (!response.ok) {
139
- const errorData = await response.json().catch(() => ({ error: 'Unknown error' }));
196
+ const apiError = await buildApiError(response, 'Failed to submit feedback');
140
197
  console.error('Feedback submission failed:', {
141
198
  status: response.status,
142
- error: errorData,
199
+ error: apiError.details,
143
200
  payload: { session_id: sessionId, message_id: messageId, feedback }
144
201
  });
145
- throw new Error(errorData.error || 'Failed to submit feedback');
202
+ throw apiError;
146
203
  }
147
204
  }
148
205
  /**
@@ -250,111 +307,70 @@ function isStorageAvailable() {
250
307
  }
251
308
 
252
309
  /**
253
- * Client Action Executor
254
- * Handles execution of client-side actions
255
- */
256
- /**
257
- * Execute a client-side action
310
+ * useChat Hook
311
+ * Main state management for chat functionality
258
312
  */
259
- async function executeClientAction(action) {
260
- try {
261
- console.log(`[ActionExecutor] Executing action: ${action.implementation}`, action.parameters);
262
- switch (action.implementation) {
263
- case 'redirect':
264
- return executeRedirect(action.parameters);
265
- case 'show_modal':
266
- return executeShowModal(action.parameters);
267
- case 'copy_to_clipboard':
268
- return executeCopyToClipboard(action.parameters);
269
- case 'open_chat':
270
- return executeOpenChat(action.parameters);
313
+ function deriveErrorInfo(error) {
314
+ if (error instanceof ApiError) {
315
+ const retryAfterSeconds = typeof error.retryAfterMs === 'number'
316
+ ? Math.max(1, Math.ceil(error.retryAfterMs / 1000))
317
+ : undefined;
318
+ const lowerMessage = (error.message || '').toLowerCase();
319
+ let message;
320
+ switch (error.status) {
321
+ case 429: {
322
+ const isPerUser = lowerMessage.includes('user');
323
+ const base = isPerUser
324
+ ? 'You have reached the per-user rate limit.'
325
+ : 'This widget has received too many requests.';
326
+ if (retryAfterSeconds) {
327
+ message = `${base} Please wait ${retryAfterSeconds} second${retryAfterSeconds === 1 ? '' : 's'} before trying again.`;
328
+ }
329
+ else {
330
+ message = `${base} Please wait a moment and try again.`;
331
+ }
332
+ break;
333
+ }
334
+ case 401:
335
+ message = 'Authentication failed. Please refresh the page or verify your API key.';
336
+ break;
337
+ case 403:
338
+ message = 'Access to this widget is restricted. Please contact the site owner if you believe this is an error.';
339
+ break;
340
+ case 404:
341
+ message = 'We could not find this widget. It may have been removed.';
342
+ break;
271
343
  default:
272
- console.warn(`[ActionExecutor] Unknown action: ${action.implementation}`);
273
- return {
274
- success: false,
275
- error: `Unknown action type: ${action.implementation}`
276
- };
344
+ if (error.status >= 500) {
345
+ message = 'The server encountered an error. Please try again shortly.';
346
+ }
347
+ else if (error.status > 0) {
348
+ message = error.message || 'Something went wrong. Please try again.';
349
+ }
350
+ else {
351
+ message = error.message || 'Unable to connect to the server. Please check your internet connection.';
352
+ }
277
353
  }
354
+ return { message, retryAfterSeconds, status: error.status };
278
355
  }
279
- catch (error) {
280
- console.error(`[ActionExecutor] Error executing action:`, error);
281
- return {
282
- success: false,
283
- error: error instanceof Error ? error.message : 'Unknown error'
284
- };
285
- }
286
- }
287
- /**
288
- * Redirect to a URL
289
- */
290
- function executeRedirect(params) {
291
- const url = params.url;
292
- if (!url) {
293
- return { success: false, error: 'URL parameter is required' };
294
- }
295
- try {
296
- // Validate URL
297
- new URL(url);
298
- // Redirect
299
- window.location.href = url;
300
- return { success: true, result: { redirected_to: url } };
301
- }
302
- catch (error) {
303
- return { success: false, error: 'Invalid URL' };
304
- }
305
- }
306
- /**
307
- * Show a modal
308
- */
309
- function executeShowModal(params) {
310
- const { title, content } = params;
311
- if (!title || !content) {
312
- return { success: false, error: 'Title and content are required' };
313
- }
314
- // Use browser's alert as a simple modal
315
- // In a real implementation, you'd use a proper modal component
316
- alert(`${title}\n\n${content}`);
317
- return { success: true, result: { shown: true } };
318
- }
319
- /**
320
- * Copy text to clipboard
321
- */
322
- async function executeCopyToClipboard(params) {
323
- const text = params.text;
324
- if (!text) {
325
- return { success: false, error: 'Text parameter is required' };
326
- }
327
- try {
328
- await navigator.clipboard.writeText(text);
329
- return { success: true, result: { copied: text } };
330
- }
331
- catch (error) {
332
- return {
333
- success: false,
334
- error: 'Failed to copy to clipboard. Please check browser permissions.'
335
- };
336
- }
337
- }
338
- /**
339
- * Open chat with a specific message
340
- */
341
- function executeOpenChat(params) {
342
- const message = params.message;
343
- if (!message) {
344
- return { success: false, error: 'Message parameter is required' };
356
+ if (error instanceof Error) {
357
+ const lower = error.message.toLowerCase();
358
+ if (lower.includes('network')) {
359
+ return { message: 'Unable to connect to the server. Please check your internet connection.' };
360
+ }
361
+ if (lower.includes('timeout')) {
362
+ return { message: 'The request timed out. Please try again.' };
363
+ }
364
+ if (lower.includes('unauthorized') || lower.includes('401')) {
365
+ return { message: 'Authentication failed. Please refresh the page or verify your API key.' };
366
+ }
367
+ if (lower.includes('internal server error') || lower.includes('500')) {
368
+ return { message: 'The server encountered an error. Please try again shortly.' };
369
+ }
370
+ return { message: error.message || 'Something went wrong. Please try again.' };
345
371
  }
346
- // Dispatch a custom event that the chat widget can listen to
347
- const event = new CustomEvent('widget:open-chat', {
348
- detail: { message }
349
- });
350
- window.dispatchEvent(event);
351
- return { success: true, result: { message } };
372
+ return { message: 'Something went wrong. Please try again.' };
352
373
  }
353
-
354
- /**
355
- * useChat Hook
356
- * Main state management for chat functionality
357
- */
358
374
  function useChat(options) {
359
375
  const { widgetId, apiKey, apiUrl, onMessage, onError, } = options;
360
376
  const [state, setState] = useState({
@@ -366,7 +382,6 @@ function useChat(options) {
366
382
  conversationId: '', // Will be set after loading conversation
367
383
  config: null,
368
384
  });
369
- const [pendingAction, setPendingAction] = useState(null);
370
385
  const apiClient = useRef(new WidgetApiClient({ widgetId, apiKey, apiUrl }));
371
386
  // Load configuration and conversation on mount
372
387
  useEffect(() => {
@@ -391,8 +406,9 @@ function useChat(options) {
391
406
  }));
392
407
  }
393
408
  catch (error) {
394
- const err = error instanceof Error ? error : new Error('Failed to initialize');
395
- setState(prev => ({ ...prev, error: err.message }));
409
+ const errorInfo = deriveErrorInfo(error);
410
+ const err = error instanceof Error ? error : new Error(errorInfo.message);
411
+ setState(prev => ({ ...prev, error: errorInfo.message }));
396
412
  onError?.(err);
397
413
  }
398
414
  };
@@ -447,7 +463,6 @@ function useChat(options) {
447
463
  }
448
464
  // Determine if widget has actions (use agent endpoint)
449
465
  const useAgent = state.config?.behavior.agentic || (state.config?.actions && state.config.actions.length > 0);
450
- console.log(useAgent);
451
466
  let response;
452
467
  if (useAgent) {
453
468
  // Use agent endpoint - returns ConversationMessage[]
@@ -516,26 +531,9 @@ function useChat(options) {
516
531
  }
517
532
  }
518
533
  catch (error) {
519
- const err = error instanceof Error ? error : new Error('Failed to send message');
520
- // Determine user-friendly error message
521
- let userMessage = err.message;
522
- // Handle specific error types
523
- if (err.message.includes('Network') || err.message.includes('fetch')) {
524
- userMessage = 'Unable to connect to the server. Please check your internet connection.';
525
- }
526
- else if (err.message.includes('401') || err.message.includes('Unauthorized')) {
527
- userMessage = 'Authentication failed. Please refresh the page.';
528
- }
529
- else if (err.message.includes('500') || err.message.includes('Internal Server Error')) {
530
- userMessage = 'The server encountered an error. Please try again later.';
531
- }
532
- else if (err.message.includes('timeout')) {
533
- userMessage = 'Request timed out. Please try again.';
534
- }
535
- // Use fallback message if configured, otherwise use error message
534
+ const errorInfo = deriveErrorInfo(error);
535
+ const err = error instanceof Error ? error : new Error(errorInfo.message);
536
536
  const fallbackMessage = state.config?.behavior?.fallbackMessage;
537
- const errorMessage = fallbackMessage || userMessage;
538
- // If fallback message is configured, add it as an assistant message
539
537
  if (fallbackMessage) {
540
538
  const fallbackAssistantMessage = {
541
539
  id: generateMessageId(),
@@ -551,17 +549,16 @@ function useChat(options) {
551
549
  messages: [...prev.messages, fallbackAssistantMessage],
552
550
  isLoading: false,
553
551
  isTyping: false,
554
- error: null,
552
+ error: errorInfo.message,
555
553
  }));
556
554
  onMessage?.(fallbackAssistantMessage);
557
555
  }
558
556
  else {
559
- // Show error message as assistant message for better UX
560
557
  const errorAssistantMessage = {
561
558
  id: generateMessageId(),
562
559
  message: {
563
560
  type: 'ai',
564
- content: `⚠️ ${errorMessage}`,
561
+ content: `⚠️ ${errorInfo.message}`,
565
562
  },
566
563
  timestamp: new Date().toISOString(),
567
564
  sources: [],
@@ -571,7 +568,7 @@ function useChat(options) {
571
568
  messages: [...prev.messages, errorAssistantMessage],
572
569
  isLoading: false,
573
570
  isTyping: false,
574
- error: errorMessage,
571
+ error: errorInfo.message,
575
572
  }));
576
573
  onMessage?.(errorAssistantMessage);
577
574
  }
@@ -609,81 +606,12 @@ function useChat(options) {
609
606
  }));
610
607
  }
611
608
  catch (error) {
612
- const err = error instanceof Error ? error : new Error('Failed to submit feedback');
609
+ const errorInfo = deriveErrorInfo(error);
610
+ const err = error instanceof Error ? error : new Error(errorInfo.message);
611
+ setState(prev => ({ ...prev, error: errorInfo.message }));
613
612
  onError?.(err);
614
613
  }
615
614
  }, [state.conversationId, onError]);
616
- /**
617
- * Approve and execute pending action
618
- */
619
- const approveAction = useCallback(async () => {
620
- if (!pendingAction)
621
- return;
622
- setState(prev => ({ ...prev, isLoading: true, isTyping: true }));
623
- try {
624
- // Execute the client action
625
- const actionResult = await executeClientAction(pendingAction.action);
626
- // Update the pending action message to show execution result
627
- const executionMessage = actionResult.success
628
- ? `✓ Action executed successfully`
629
- : `✗ Action failed: ${actionResult.error}`;
630
- setState(prev => ({
631
- ...prev,
632
- messages: prev.messages.map(msg => msg.id === pendingAction.messageId
633
- ? { ...msg, message: { ...msg.message, content: `${msg.message.content}\n\n${executionMessage}` } }
634
- : msg),
635
- }));
636
- // Clear pending action
637
- setPendingAction(null);
638
- // TODO: Implement continueAgent in API client if actions are needed
639
- // For now, just show success message
640
- const assistantMessage = {
641
- id: generateMessageId(),
642
- message: {
643
- type: 'ai',
644
- content: 'Action completed successfully.',
645
- },
646
- timestamp: new Date().toISOString(),
647
- sources: [],
648
- };
649
- setState(prev => ({
650
- ...prev,
651
- messages: [...prev.messages, assistantMessage],
652
- isLoading: false,
653
- isTyping: false,
654
- }));
655
- onMessage?.(assistantMessage);
656
- }
657
- catch (error) {
658
- const err = error instanceof Error ? error : new Error('Failed to execute action');
659
- setState(prev => ({
660
- ...prev,
661
- isLoading: false,
662
- isTyping: false,
663
- error: err.message,
664
- messages: prev.messages.map(msg => msg.id === pendingAction.messageId
665
- ? { ...msg, message: { ...msg.message, content: `${msg.message.content}\n\n✗ Failed to execute action` } }
666
- : msg),
667
- }));
668
- setPendingAction(null);
669
- onError?.(err);
670
- }
671
- }, [pendingAction, state.conversationId, onMessage, onError]);
672
- /**
673
- * Reject pending action
674
- */
675
- const rejectAction = useCallback(() => {
676
- if (!pendingAction)
677
- return;
678
- // Update message to show rejection
679
- setState(prev => ({
680
- ...prev,
681
- messages: prev.messages.map(msg => msg.id === pendingAction.messageId
682
- ? { ...msg, message: { ...msg.message, content: `${msg.message.content}\n\n✗ Action cancelled by user` } }
683
- : msg),
684
- }));
685
- setPendingAction(null);
686
- }, [pendingAction]);
687
615
  return {
688
616
  messages: state.messages,
689
617
  isLoading: state.isLoading,
@@ -691,10 +619,7 @@ function useChat(options) {
691
619
  error: state.error,
692
620
  config: state.config,
693
621
  conversationId: state.conversationId,
694
- pendingAction,
695
622
  sendMessage,
696
- approveAction,
697
- rejectAction,
698
623
  clearMessages,
699
624
  submitFeedback,
700
625
  };
@@ -21609,7 +21534,7 @@ styleInject(css_248z);
21609
21534
 
21610
21535
  const ChatWidget = ({ widgetId, apiKey, apiUrl = window.location.origin, position = 'bottom-right', theme: themeOverride, primaryColor, onOpen, onClose, onMessage, onError, }) => {
21611
21536
  const [isOpen, setIsOpen] = useState(false);
21612
- const { messages, isLoading, isTyping, error, config, sendMessage, approveAction, rejectAction, submitFeedback, } = useChat({
21537
+ const { messages, isLoading, isTyping, error, config, sendMessage, submitFeedback, } = useChat({
21613
21538
  widgetId,
21614
21539
  apiKey,
21615
21540
  apiUrl,
@@ -21659,11 +21584,11 @@ const ChatWidget = ({ widgetId, apiKey, apiUrl = window.location.origin, positio
21659
21584
  const handleFeedback = async (messageId, feedback) => {
21660
21585
  await submitFeedback(messageId, feedback);
21661
21586
  };
21662
- return (jsx("div", { className: `ai-chat-widget ${effectiveTheme}`, style: customStyles, children: jsx("div", { className: `ai-chat-widget-container ${effectivePosition}`, children: isOpen ? (jsx(ChatWindow, { messages: messages, isLoading: isLoading, isTyping: isTyping, error: error, config: config, onSendMessage: sendMessage, onApproveAction: approveAction, onRejectAction: rejectAction, onClose: handleToggle, onFeedback: handleFeedback })) : (jsx("button", { className: "ai-chat-button", onClick: handleToggle, "aria-label": "Open chat", style: {
21587
+ return (jsx("div", { className: `ai-chat-widget ${effectiveTheme}`, style: customStyles, children: jsx("div", { className: `ai-chat-widget-container ${effectivePosition}`, children: isOpen ? (jsx(ChatWindow, { messages: messages, isLoading: isLoading, isTyping: isTyping, error: error, config: config, onSendMessage: sendMessage, onClose: handleToggle, onFeedback: handleFeedback })) : (jsx("button", { className: "ai-chat-button", onClick: handleToggle, "aria-label": "Open chat", style: {
21663
21588
  width: config?.appearance.buttonSize || 60,
21664
21589
  height: config?.appearance.buttonSize || 60,
21665
21590
  }, children: config?.appearance.buttonIcon ? (jsx("span", { className: "ai-chat-button-icon", children: config.appearance.buttonIcon })) : (jsx("svg", { className: "ai-chat-button-svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: jsx("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }) })) })) }) }));
21666
21591
  };
21667
21592
 
21668
- export { ChatWidget, useChat };
21593
+ export { ApiError, ChatWidget, useChat };
21669
21594
  //# sourceMappingURL=index.esm.js.map