@developer.notchatbot/webchat 1.7.1 → 2.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 (37) hide show
  1. package/README.md +2 -0
  2. package/dist/components/ChatBot.d.ts +3 -4
  3. package/dist/components/ContactCapture.d.ts +7 -0
  4. package/dist/components/MessageActions.d.ts +8 -0
  5. package/dist/components/MessageAlertSettings.d.ts +5 -0
  6. package/dist/components/MessageSelectionActions.d.ts +4 -0
  7. package/dist/components/ReplyQuote.d.ts +8 -0
  8. package/dist/components/UnreadMessagePreview.d.ts +15 -0
  9. package/dist/components/embedchat/EmbedChat.d.ts +6 -2
  10. package/dist/config/endpoints.d.ts +0 -5
  11. package/dist/hooks/useMessageAlerts.d.ts +11 -0
  12. package/dist/hooks/useMessageGesture.d.ts +14 -0
  13. package/dist/hooks/useTeamAvailability.d.ts +2 -0
  14. package/dist/hooks/useVisitorChat.d.ts +48 -0
  15. package/dist/types.d.ts +86 -14
  16. package/dist/utils/cart-model.d.ts +23 -0
  17. package/dist/utils/contact-link.d.ts +2 -0
  18. package/dist/utils/convex.d.ts +2 -3
  19. package/dist/utils/markdown.d.ts +2 -0
  20. package/dist/utils/message-alerts.d.ts +15 -0
  21. package/dist/utils/message-colors.d.ts +3 -0
  22. package/dist/utils/message-id.d.ts +2 -0
  23. package/dist/utils/phone.d.ts +16 -0
  24. package/dist/utils/shopify-attribution.d.ts +12 -0
  25. package/dist/utils/storefront-cart.d.ts +6 -0
  26. package/dist/utils/storefront-identity.d.ts +23 -0
  27. package/dist/utils/tiendanube-attribution.d.ts +8 -0
  28. package/dist/utils/transcript.d.ts +23 -0
  29. package/dist/utils/visitor-presence.d.ts +30 -0
  30. package/dist/utils/visitor-session.d.ts +31 -0
  31. package/dist/utils/vtex-attribution.d.ts +13 -0
  32. package/dist/webchat-bundle.min.js +8707 -5192
  33. package/dist/webchat-bundle.min.umd.cjs +81 -55
  34. package/package.json +8 -4
  35. package/dist/components/ConversationRating.d.ts +0 -7
  36. package/dist/utils/notchatbot-cookie.d.ts +0 -32
  37. package/dist/utils/survey.d.ts +0 -30
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # 🔷 WebChat - Embedded Chat
2
2
 
3
+ Webchat 2.0.0 uses origin-bound visitor credentials and requires the matching NotChatbot backend and Cloudflare presence service. See [the rollout guide](../next/apps/next/docs/webchat-v2-rollout.md) before upgrading an existing install.
4
+
3
5
  ## 📦 Quick Start
4
6
 
5
7
  ### 1. Add the widget to your website
@@ -1,6 +1,5 @@
1
- import { default as React } from 'react';
2
1
  import { ChatBotProps } from '../types';
3
- declare const ChatBot: React.FC<ChatBotProps & {
4
- exposeControl?: (api: any) => void;
5
- }>;
2
+ declare const ChatBot: ({ config, exposeControl }: ChatBotProps & {
3
+ exposeControl?: (api: Record<string, unknown>) => void;
4
+ }) => import("react/jsx-runtime").JSX.Element | null;
6
5
  export default ChatBot;
@@ -0,0 +1,7 @@
1
+ interface Props {
2
+ uid: string;
3
+ conversationId?: string;
4
+ }
5
+ /** Save intentional contact edits after a pause; passive identity hints never authorize follow-up. */
6
+ export declare function ContactCapture({ uid, conversationId }: Props): import("react/jsx-runtime").JSX.Element | null;
7
+ export {};
@@ -0,0 +1,8 @@
1
+ export default function MessageActions({ anchor, selectionMode, reaction, onReact, onReply, onClose }: {
2
+ selectionMode?: boolean;
3
+ anchor: HTMLElement;
4
+ reaction?: string | null;
5
+ onReact?: (emoji: string | null) => void;
6
+ onReply?: () => void;
7
+ onClose: () => void;
8
+ }): import('react').ReactPortal;
@@ -0,0 +1,5 @@
1
+ import { MessageAlertControls } from '../hooks/useMessageAlerts';
2
+ /** Customer-controlled alert preferences. Opening this menu never requests browser permission. */
3
+ export default function MessageAlertSettings({ controls }: {
4
+ controls: MessageAlertControls;
5
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,4 @@
1
+ export default function MessageSelectionActions({ onReply, onCancel }: {
2
+ onReply: () => void;
3
+ onCancel: () => void;
4
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,8 @@
1
+ import { Message } from '../types';
2
+ export declare const quoteName: (message: Message) => string;
3
+ export declare const quoteText: (message: Pick<Message, "type" | "text">) => string;
4
+ export default function ReplyQuote({ message, onClick, textColor }: {
5
+ message: Message;
6
+ onClick?: () => void;
7
+ textColor?: string;
8
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,15 @@
1
+ import { Message } from '../types';
2
+ interface Props {
3
+ messages: Message[];
4
+ isStaffTyping?: boolean;
5
+ staffTypingAuthor?: Message["author"];
6
+ title: string;
7
+ avatar?: string;
8
+ position: 'bottom-left' | 'bottom-right';
9
+ marginBottom: number;
10
+ marginSide: number;
11
+ onOpen: () => void;
12
+ }
13
+ /** Real unread replies in transcript order. Previewing or dismissing never sends viewed receipts. */
14
+ export default function UnreadMessagePreview({ messages, isStaffTyping, staffTypingAuthor, title, avatar, position, marginBottom, marginSide, onOpen }: Props): import("react/jsx-runtime").JSX.Element | null;
15
+ export {};
@@ -1,4 +1,8 @@
1
- import { default as React } from 'react';
2
1
  import { EmbedChatProps } from '../../types';
3
- declare const EmbedChat: React.FC<EmbedChatProps>;
2
+ declare const EmbedChat: ({ config, exposeControl }: EmbedChatProps & {
3
+ exposeControl?: (api: {
4
+ restart: () => void;
5
+ identify: (hint: import('../../utils/storefront-identity').CustomerHint) => void;
6
+ }) => void;
7
+ }) => import("react/jsx-runtime").JSX.Element | null;
4
8
  export default EmbedChat;
@@ -2,7 +2,6 @@ export interface EndpointConfig {
2
2
  baseUrl: string;
3
3
  chatEndpoint: string;
4
4
  uploadEndpoint: string;
5
- rateEndpoint: string;
6
5
  realtimeUrl: string;
7
6
  }
8
7
  /**
@@ -30,10 +29,6 @@ export declare function getChatEndpoint(): string;
30
29
  * Obtiene el endpoint de upload
31
30
  */
32
31
  export declare function getUploadEndpoint(): string;
33
- /**
34
- * Obtiene el endpoint de rating
35
- */
36
- export declare function getRateEndpoint(): string;
37
32
  /**
38
33
  * Obtiene la URL base según el ambiente
39
34
  */
@@ -0,0 +1,11 @@
1
+ import { Message } from '../types';
2
+ export interface MessageAlertControls {
3
+ soundEnabled: boolean;
4
+ toggleSound: () => void;
5
+ notificationsEnabled: boolean;
6
+ notificationsSupported: boolean;
7
+ permission: NotificationPermission | 'unsupported';
8
+ toggleNotifications: () => Promise<void>;
9
+ }
10
+ /** Alerts use the existing unread feed; old history, typing and duplicate snapshots remain silent. */
11
+ export declare function useMessageAlerts(uid: string, conversationId: string | undefined, messages: Message[], unreadCount: number, isOpen: boolean, title: string, onOpen: () => void): MessageAlertControls;
@@ -0,0 +1,14 @@
1
+ import { default as React } from 'react';
2
+ export declare function useMessageGesture(enabled: boolean, onReply: () => void, onHold: (touch: boolean) => void): {
3
+ offset: number;
4
+ gestures: {
5
+ onPointerDown: (event: React.PointerEvent<HTMLDivElement>) => void;
6
+ onPointerMove: (event: React.PointerEvent) => void;
7
+ onPointerUp: (event: React.PointerEvent) => void;
8
+ onPointerCancel: () => void;
9
+ onLostPointerCapture: () => void;
10
+ onClickCapture: (event: React.MouseEvent) => void;
11
+ onKeyDown: (event: React.KeyboardEvent) => void;
12
+ onContextMenu: (event: React.MouseEvent) => void;
13
+ };
14
+ };
@@ -0,0 +1,2 @@
1
+ /** Before engagement, an open widget reads only a cached anonymous edge boolean. */
2
+ export declare function useTeamAvailability(uid: string, open: boolean, engaged: boolean, liveOnline: boolean): boolean;
@@ -0,0 +1,48 @@
1
+ import { VisitorSession } from '../utils/visitor-session';
2
+ import { Transcript } from '../utils/transcript';
3
+ import { FileWithBlobUrl, Message, WebChatConfig } from '../types';
4
+ import { CustomerHint } from '../utils/storefront-identity';
5
+ interface PendingSend {
6
+ createdAt?: string;
7
+ requestId: string;
8
+ conversationId: string;
9
+ chatbotUid: string;
10
+ newMessages: Array<{
11
+ id: string;
12
+ role: 'user';
13
+ content: string;
14
+ type: string;
15
+ metaContent?: string;
16
+ replyToId?: string;
17
+ }>;
18
+ currentUrl: string;
19
+ }
20
+ /** Owns the visitor lifecycle shared by floating and embedded chat surfaces. */
21
+ export declare function useVisitorChat(config: WebChatConfig, isOpen: boolean): {
22
+ react: (messageId: string, emoji: string | null) => Promise<boolean>;
23
+ teamOnline: boolean;
24
+ staffTypingAuthor: {
25
+ name: string;
26
+ avatarUrl?: string;
27
+ } | undefined;
28
+ staffTyping: boolean;
29
+ unreadMessages: Message[];
30
+ unreadMessage: Message;
31
+ session: VisitorSession | null;
32
+ draftRevision: number;
33
+ messages: Message[];
34
+ sending: boolean;
35
+ error: string;
36
+ unread: boolean;
37
+ unreadCount: number;
38
+ state: Partial<Transcript>;
39
+ connected: boolean;
40
+ pending: PendingSend;
41
+ send: (text: string, files?: FileWithBlobUrl[], replyTo?: Message) => false | Promise<boolean | undefined>;
42
+ restart: (forgetContact?: boolean) => void;
43
+ viewed: (ids: string[]) => void;
44
+ typing: (active: boolean) => void | undefined;
45
+ identify: (hint: CustomerHint) => void;
46
+ retry: () => void;
47
+ };
48
+ export {};
package/dist/types.d.ts CHANGED
@@ -5,6 +5,10 @@ export interface WebChatPositionConfig {
5
5
  showPopup?: boolean;
6
6
  }
7
7
  export interface WebChatConfig {
8
+ active?: boolean;
9
+ quickReplies?: string[];
10
+ contactCapture?: boolean;
11
+ identityEndpoint?: string;
8
12
  elementId: string;
9
13
  title?: string;
10
14
  placeholder?: string;
@@ -58,6 +62,11 @@ export interface EmbedChatInputConfig {
58
62
  iconColor?: string;
59
63
  }
60
64
  export interface EmbedChatConfig {
65
+ apiBaseUrl?: string;
66
+ realtimeUrl?: string;
67
+ contactCapture?: boolean;
68
+ quickReplies?: string[];
69
+ identityEndpoint?: string;
61
70
  elementId: string;
62
71
  title?: string;
63
72
  placeholder?: string;
@@ -95,14 +104,28 @@ export interface EmbedChatProps {
95
104
  config: EmbedChatConfig;
96
105
  }
97
106
  export interface EmbedChatWindowProps {
107
+ onReact?: (messageId: string, emoji: string | null) => Promise<boolean>;
108
+ alertSettings?: React.ReactNode;
109
+ onTyping?: (active: boolean) => void;
110
+ contactMessage?: React.ReactNode;
111
+ children?: React.ReactNode;
112
+ onViewed?: (ids: string[]) => void;
113
+ sending?: boolean;
114
+ draftRevision?: number;
98
115
  config: EmbedChatConfig;
99
116
  messages: Message[];
100
- onSendMessage: (message: string, files?: FileWithBlobUrl[]) => void;
117
+ onSendMessage: (message: string, files?: FileWithBlobUrl[], replyTo?: Message) => void | boolean | Promise<void | boolean>;
101
118
  isLoading?: boolean;
102
119
  isAiTyping?: boolean;
120
+ teamOnline?: boolean;
121
+ isStaffTyping?: boolean;
122
+ staffTypingAuthor?: Message["author"];
103
123
  chatbotActivated?: boolean;
104
124
  }
105
125
  export interface EmbedChatHeaderProps {
126
+ selectionActions?: React.ReactNode;
127
+ teamOnline?: boolean;
128
+ alertSettings?: React.ReactNode;
106
129
  title: string;
107
130
  primaryColor?: string;
108
131
  avatar?: string;
@@ -113,12 +136,29 @@ export interface EmbedChatFooterProps {
113
136
  footerColor?: string;
114
137
  }
115
138
  export interface EmbedChatInstance {
139
+ logout?: () => void;
140
+ setCart: (cart: Omit<import('./utils/cart-model').WebchatCart, 'itemCount' | 'truncated'> | null) => void;
141
+ identify?: (hint: import('./utils/storefront-identity').CustomerHint) => void;
116
142
  destroy: () => void;
117
143
  updateConfig?: (newConfig: Partial<EmbedChatConfig>) => void;
118
144
  injectCSS?: (css: string) => void;
119
145
  removeCustomCSS?: () => void;
120
146
  }
121
147
  export interface Message {
148
+ reaction?: string;
149
+ replyToId?: string;
150
+ replyTo?: {
151
+ id: string;
152
+ content: string;
153
+ role: string;
154
+ type: string;
155
+ };
156
+ author?: {
157
+ name: string;
158
+ avatarUrl?: string;
159
+ };
160
+ contactPromptAfter?: boolean;
161
+ delivery?: 'sending' | 'accepted' | 'failed';
122
162
  id: string;
123
163
  text: string;
124
164
  sender: 'user' | 'bot';
@@ -138,37 +178,44 @@ export interface ChatButtonProps {
138
178
  marginBottom?: number;
139
179
  marginSide?: number;
140
180
  hasNewMessages?: boolean;
181
+ unreadCount?: number;
141
182
  isMobile?: boolean;
142
183
  isMenuOpen?: boolean;
143
184
  hasWhatsAppMenu?: boolean;
144
185
  }
145
186
  export interface ChatWindowProps {
187
+ onReact?: (messageId: string, emoji: string | null) => Promise<boolean>;
188
+ alertSettings?: React.ReactNode;
189
+ onTyping?: (active: boolean) => void;
190
+ draftRevision?: number;
191
+ onViewed?: (ids: string[]) => void;
192
+ sendError?: string;
193
+ onRetry?: () => void;
194
+ sending?: boolean;
195
+ contactCaptured?: boolean;
196
+ handoffRequested?: boolean;
197
+ engaged?: boolean;
146
198
  isOpen: boolean;
147
199
  onClose: () => void;
148
200
  config: WebChatConfig;
149
201
  messages: Message[];
150
- onSendMessage: (message: string, files?: FileWithBlobUrl[]) => void;
202
+ onSendMessage: (message: string, files?: FileWithBlobUrl[], replyTo?: Message) => void | boolean | Promise<void | boolean>;
151
203
  isLoading?: boolean;
152
204
  isAiTyping?: boolean;
205
+ teamOnline?: boolean;
206
+ isStaffTyping?: boolean;
207
+ staffTypingAuthor?: Message["author"];
153
208
  position?: 'bottom-right' | 'bottom-left';
154
209
  marginBottom?: number;
155
210
  marginSide?: number;
156
211
  isMobile?: boolean;
157
212
  chatbotActivated?: boolean;
158
- isRated?: boolean;
159
- onRatingChange?: (isRated: boolean) => void;
160
213
  conversationId?: string;
161
- /** Configurable auto-survey threshold from the backend; undefined = legacy default (2). */
162
- surveyAfterUserMessages?: number;
163
- /** Timestamp of the last manual survey request from Livechat; undefined = never requested. */
164
- webchatSurveyRequestedAt?: number;
165
- }
166
- /** Survey-related conversation state forwarded from the Convex realtime subscription. */
167
- export interface ConversationSurveyState {
168
- surveyAfterUserMessages?: number;
169
- webchatSurveyRequestedAt?: number;
170
214
  }
171
215
  export interface ChatHeaderProps {
216
+ selectionActions?: React.ReactNode;
217
+ teamOnline?: boolean;
218
+ alertSettings?: React.ReactNode;
172
219
  title: string;
173
220
  onClose: () => void;
174
221
  primaryColor?: string;
@@ -178,21 +225,40 @@ export interface ChatHeaderProps {
178
225
  closeButtonCustomIcon?: string;
179
226
  }
180
227
  export interface MessageListProps {
228
+ selectedMessageId?: string;
229
+ onSelectMessage?: (message: Message | undefined) => void;
230
+ onReact?: (messageId: string, emoji: string | null) => Promise<boolean>;
231
+ onReply?: (message: Message) => void;
232
+ contactMessage?: React.ReactNode;
233
+ onViewed?: (ids: string[]) => void;
181
234
  messages: Message[];
182
235
  isTyping: boolean;
236
+ teamOnline?: boolean;
237
+ isStaffTyping?: boolean;
238
+ staffTypingAuthor?: Message["author"];
183
239
  avatar?: string;
184
240
  textColor?: string;
185
241
  bubbleUserColor?: string;
186
242
  chatbotActivated?: boolean;
187
243
  }
188
244
  export interface MessageProps {
245
+ selectedMessageId?: string;
246
+ onSelectMessage?: (message: Message | undefined) => void;
247
+ onReact?: (messageId: string, emoji: string | null) => Promise<boolean>;
248
+ onReply?: (message: Message) => void;
249
+ referencedMessage?: Message;
250
+ onJump?: (id: string) => void;
189
251
  message: Message;
190
252
  avatar?: string;
191
253
  textColor?: string;
192
254
  bubbleUserColor?: string;
193
255
  }
194
256
  export interface MessageInputProps {
195
- onSendMessage: (message: string, files?: FileWithBlobUrl[]) => void;
257
+ replyTo?: Message;
258
+ onCancelReply?: () => void;
259
+ onTyping?: (active: boolean) => void;
260
+ draftScope?: string;
261
+ onSendMessage: (message: string, files?: FileWithBlobUrl[], replyTo?: Message) => void | boolean | Promise<void | boolean>;
196
262
  placeholder?: string;
197
263
  primaryColor?: string;
198
264
  disabled?: boolean;
@@ -201,6 +267,9 @@ export interface MessageInputProps {
201
267
  }
202
268
  export interface TypingIndicatorProps {
203
269
  avatar?: string;
270
+ author?: Message["author"];
271
+ human?: boolean;
272
+ compact?: boolean;
204
273
  }
205
274
  export interface ChatBubblePopoverProps {
206
275
  initialMessage?: string;
@@ -214,6 +283,9 @@ export interface ChatBubblePopoverProps {
214
283
  onPopoverClick: () => void;
215
284
  }
216
285
  export interface WebChatInstance {
286
+ logout?: () => void;
287
+ setCart: (cart: Omit<import('./utils/cart-model').WebchatCart, 'itemCount' | 'truncated'> | null) => void;
288
+ identify?: (hint: import('./utils/storefront-identity').CustomerHint) => void;
217
289
  root: any;
218
290
  destroy: () => void;
219
291
  getConversationId: () => string | null;
@@ -0,0 +1,23 @@
1
+ /** Cart amounts are integer minor currency units. Storefront hints never authorize orders or prices. */
2
+ export interface WebchatCartItem {
3
+ id: string;
4
+ title: string;
5
+ quantity: number;
6
+ unitPrice: number;
7
+ variant?: string;
8
+ imageUrl?: string;
9
+ url?: string;
10
+ }
11
+ export interface WebchatCart {
12
+ currency: string;
13
+ total: number;
14
+ itemCount: number;
15
+ items: WebchatCartItem[];
16
+ truncated: boolean;
17
+ }
18
+ export interface WebchatCartSnapshot {
19
+ cart: WebchatCart | null;
20
+ updatedAt: number;
21
+ }
22
+ /** Allowlist and bound storefront context before sending it to staff or edge storage. */
23
+ export declare function sanitizeWebchatCart(value: unknown, origin: string): WebchatCart | null;
@@ -0,0 +1,2 @@
1
+ /** Remove the bearer credential before widget analytics or presence can use the URL. */
2
+ export declare function takeContactLink(chatbotUid: string): string | null;
@@ -1,5 +1,5 @@
1
1
  import { ConvexReactClient } from 'convex/react';
2
- import { ConversationSurveyState, Message } from '../types';
2
+ import { Message } from '../types';
3
3
  export interface ConvexConnectionData {
4
4
  client: ConvexReactClient;
5
5
  unsubscribe: (() => void) | null;
@@ -11,10 +11,9 @@ export interface ConvexConnectionData {
11
11
  * @param conversationId Conversation ID
12
12
  * @param onMessage Callback for new messages
13
13
  * @param onChatbotToggle Callback for chatbot activation changes
14
- * @param onSurveyStateChange Callback for survey state changes (threshold + manual trigger)
15
14
  * @returns Promise that resolves to Convex connection data
16
15
  */
17
- export declare const initializeConvexConnection: (realtimeEndpoint: string, _chatbotUid: string, conversationId: string, onMessage: (message: Message) => void, onChatbotToggle?: (chatbotActivated: boolean) => void, onSurveyStateChange?: (surveyState: ConversationSurveyState) => void) => Promise<ConvexConnectionData>;
16
+ export declare const initializeConvexConnection: (realtimeEndpoint: string, _chatbotUid: string, conversationId: string, onMessage: (message: Message) => void, onChatbotToggle?: (chatbotActivated: boolean) => void) => Promise<ConvexConnectionData>;
18
17
  /**
19
18
  * Fetches initial conversation data with messages from Convex
20
19
  * @param realtimeEndpoint Realtime service deployment URL
@@ -0,0 +1,2 @@
1
+ /** Both visitor and assistant text are untrusted, including raw HTML inside Markdown. */
2
+ export declare function renderMessageMarkdown(text: string): string;
@@ -0,0 +1,15 @@
1
+ /** Keep notification counters out of the page metadata sent to Livechat. */
2
+ export declare const getStorefrontTitle: () => string;
3
+ /** Preserve storefront title changes, including client-side navigation, while showing unread replies. */
4
+ export declare function createUnreadTitle(): {
5
+ update(next: number): void;
6
+ close(): void;
7
+ };
8
+ /** Share a bounded receipt ledger between storefront tabs; Web Locks prevent simultaneous alerts. */
9
+ export declare function claimMessageAlert(uid: string, conversationId: string, ids: string[]): Promise<any>;
10
+ /** Unlock audio only on customer interaction; unsupported/autoplay-blocked browsers remain silent. */
11
+ export declare function createMessageSound(): {
12
+ unlock: () => void;
13
+ play(): void;
14
+ close(): void;
15
+ };
@@ -0,0 +1,3 @@
1
+ /** Store theme colors are opaque hex colors; keep a custom text color only when legible. */
2
+ export declare function messageTextColor(background: string, preferred?: string): string;
3
+ export declare function messageMetaColor(background: string, foreground: string): string;
@@ -0,0 +1,2 @@
1
+ /** Mirrors the API's conversation/request/index ID, so optimistic bubbles reconcile before HTTP completes. */
2
+ export declare function webchatMessageId(conversationId: string, requestId: string, index: number): Promise<string>;
@@ -0,0 +1,16 @@
1
+ import { getCountryCallingCode, CountryCode } from 'libphonenumber-js/min';
2
+ export { getCountryCallingCode, type CountryCode };
3
+ export declare const phoneCountries: CountryCode[];
4
+ export declare const isPhoneCountry: (value: unknown) => value is CountryCode;
5
+ export declare const countryFlag: (country: CountryCode) => string;
6
+ /** Browser hints are a convenience, not geolocation. No request or permission prompt is needed. */
7
+ export declare function suggestPhoneCountry(languages: readonly string[], timeZone: string): CountryCode;
8
+ /** Split pasted/saved international numbers without duplicating their country calling code. */
9
+ export declare function splitPhone(value: string, fallback: CountryCode): {
10
+ value: string;
11
+ country: CountryCode;
12
+ };
13
+ /** Normalize national input (including trunk prefixes) for the existing E.164 contact endpoint. */
14
+ export declare function normalizePhone(value: string, country: CountryCode): string | null;
15
+ export declare const countryName: (country: CountryCode) => string;
16
+ export declare const sortedPhoneCountries: CountryCode[];
@@ -0,0 +1,12 @@
1
+ export interface ShopifyAttributionReference {
2
+ conversationId: string;
3
+ expiresAt: number;
4
+ }
5
+ /** Cart API bridge. Only runs network work after engagement or for cleanup of our saved reference. */
6
+ export declare function observeShopifyAttribution(doc: Document, readReference: () => ShopifyAttributionReference | null, storage: {
7
+ read(): string | null;
8
+ write(value: string): void;
9
+ }, request?: typeof fetch): {
10
+ refresh: () => Promise<void>;
11
+ dispose: () => void;
12
+ };
@@ -0,0 +1,6 @@
1
+ import { WebchatCart } from './cart-model';
2
+ export type { WebchatCart } from './cart-model';
3
+ /** Commerce adapters call this on initial load and cart changes. No network until chat engagement. */
4
+ export declare function setStorefrontCart(uid: string, input: unknown): void;
5
+ export declare const getStorefrontCart: (uid: string) => WebchatCart | null | undefined;
6
+ export declare function subscribeStorefrontCart(uid: string, listener: () => void): () => void;
@@ -0,0 +1,23 @@
1
+ import { VisitorSession } from './visitor-session';
2
+ export interface CustomerHint {
3
+ platform?: 'tiendanube' | 'shopify' | 'vtex' | 'custom';
4
+ subject?: string;
5
+ email?: string;
6
+ name?: string;
7
+ phone?: string;
8
+ }
9
+ interface VerifiedIdentity extends CustomerHint {
10
+ verified: boolean;
11
+ expiresAt: number;
12
+ }
13
+ /** Hints only prefill the form; they do not authenticate or link customer records. */
14
+ export declare function saveCustomerHint(uid: string, hint: CustomerHint): void;
15
+ /** The configured same-origin merchant route must validate its own login before signing. */
16
+ export declare function refreshStorefrontIdentity(session: VisitorSession, endpoint: string): Promise<{
17
+ changed: boolean;
18
+ identity: null;
19
+ } | {
20
+ changed: boolean;
21
+ identity: VerifiedIdentity;
22
+ }>;
23
+ export {};
@@ -0,0 +1,8 @@
1
+ export interface AttributionReference {
2
+ token: string;
3
+ expiresAt: number;
4
+ }
5
+ /** Attach only to same-origin Tiendanube purchase forms; unrelated forms remain untouched. */
6
+ export declare function attachTiendanubeAttribution(doc: Document, token: string): number;
7
+ /** Owns field lifecycle across cart rendering; never intercepts payment or blocks navigation. */
8
+ export declare function observeTiendanubeAttribution(doc: Document, reference: AttributionReference, storeId: string, readPlatform: () => unknown, onRetired?: () => void): () => void;
@@ -0,0 +1,23 @@
1
+ import { VisitorSession } from './visitor-session';
2
+ import { Message } from '../types';
3
+ export interface Transcript {
4
+ messages: Array<{
5
+ reaction?: string;
6
+ replyToId?: string;
7
+ replyTo?: Message["replyTo"];
8
+ id: string;
9
+ role: string;
10
+ content: string;
11
+ createdAt: string;
12
+ type?: string;
13
+ metaContent?: string;
14
+ contactPromptAfter?: boolean;
15
+ author?: Message['author'];
16
+ }>;
17
+ chatbotActivated: boolean;
18
+ contactCaptured?: boolean;
19
+ handoffRequested?: boolean;
20
+ }
21
+ export declare const transcriptMessages: (data: Transcript) => Message[];
22
+ /** One bounded subscription supplies both initial history and live replies, avoiding snapshot races. */
23
+ export declare function subscribeTranscript(session: VisitorSession, onData: (data: Transcript) => void, onError: () => void): () => void;
@@ -0,0 +1,30 @@
1
+ import { VisitorSession } from './visitor-session';
2
+ export interface EdgeReply {
3
+ id: string;
4
+ at: number;
5
+ receipt: 'available' | 'delivered' | 'viewed';
6
+ }
7
+ export interface EdgeState {
8
+ staffTyping?: {
9
+ name: string;
10
+ avatarUrl?: string;
11
+ } | null;
12
+ staffTypingUntil?: number;
13
+ typingUntil?: number;
14
+ page?: {
15
+ url: string;
16
+ title: string;
17
+ } | null;
18
+ type: 'state';
19
+ presence: string;
20
+ replies: EdgeReply[];
21
+ observedAt: number;
22
+ }
23
+ /** Reconnectable metadata channel. Transcript content is only loaded while the chat is open. */
24
+ export declare function connectVisitorPresence(session: VisitorSession, onState: (state: EdgeState) => void, onConnection: (connected: boolean) => void, onTeamAvailability?: (online: boolean) => void): {
25
+ setTyping: (active: boolean) => void;
26
+ setOpen(value: boolean): void;
27
+ delivered(ids: string[]): void;
28
+ viewed(ids: string[]): void;
29
+ close(): void;
30
+ };
@@ -0,0 +1,31 @@
1
+ export interface VisitorSession {
2
+ chatbotUid: string;
3
+ conversationId: string;
4
+ visitorId: string;
5
+ token: string;
6
+ expiresAt: number;
7
+ presenceUrl?: string | null;
8
+ started?: boolean;
9
+ contactConversationId?: string;
10
+ contactSession?: {
11
+ token: string;
12
+ expiresAt: number;
13
+ };
14
+ }
15
+ export declare const visitorStorageKey: (uid: string, item: string) => string;
16
+ /** Tenant/site-scoped storage with a stable in-memory fallback for blocked browser storage. */
17
+ export declare function readVisitorData(uid: string, item: string): string | null;
18
+ export declare function writeVisitorData(uid: string, item: string, value: string | null): void;
19
+ export declare function getVisitorSession(uid: string): VisitorSession | null;
20
+ export declare function getContactSession(uid: string): {
21
+ token: string;
22
+ expiresAt: number;
23
+ } | null;
24
+ export declare function saveVisitorSession(session: VisitorSession): void;
25
+ /** Called on first send/upload, never on page load or merely opening the launcher. */
26
+ export declare function ensureVisitorSession(uid: string): Promise<VisitorSession>;
27
+ export declare function resetVisitorSession(uid: string, forgetContact?: boolean): void;
28
+ export declare function visitorHeaders(session: VisitorSession): {
29
+ 'Content-Type': string;
30
+ Authorization: string;
31
+ };
@@ -0,0 +1,13 @@
1
+ interface Reference {
2
+ conversationId: string;
3
+ expiresAt: number;
4
+ }
5
+ /** Uses the store's existing vtexjs checkout transport. Configure its shared queue at integration time. */
6
+ export declare function observeVtexAttribution(doc: Document, readReference: () => Reference | null, storage: {
7
+ read(): string | null;
8
+ write(value: string): void;
9
+ }): {
10
+ refresh: () => Promise<void>;
11
+ dispose: () => void;
12
+ };
13
+ export {};