@pocketping/sdk-node 0.1.0 → 1.0.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,421 @@
1
+ import { IncomingMessage, ServerResponse } from 'http';
2
+
3
+ /**
4
+ * Storage adapter interface.
5
+ * Implement this interface to use any database with PocketPing.
6
+ */
7
+ interface Storage {
8
+ createSession(session: Session): Promise<void>;
9
+ getSession(sessionId: string): Promise<Session | null>;
10
+ getSessionByVisitorId?(visitorId: string): Promise<Session | null>;
11
+ updateSession(session: Session): Promise<void>;
12
+ deleteSession(sessionId: string): Promise<void>;
13
+ saveMessage(message: Message): Promise<void>;
14
+ getMessages(sessionId: string, after?: string, limit?: number): Promise<Message[]>;
15
+ getMessage(messageId: string): Promise<Message | null>;
16
+ cleanupOldSessions?(olderThan: Date): Promise<number>;
17
+ }
18
+
19
+ /**
20
+ * Bridge interface for notification channels.
21
+ * Implement this interface to add support for Telegram, Discord, Slack, etc.
22
+ */
23
+ interface Bridge {
24
+ /** Unique name for this bridge */
25
+ name: string;
26
+ /** Called when the bridge is added to PocketPing */
27
+ init?(pocketping: PocketPing): void | Promise<void>;
28
+ /** Called when a new chat session is created */
29
+ onNewSession?(session: Session): void | Promise<void>;
30
+ /** Called when a visitor sends a message */
31
+ onVisitorMessage?(message: Message, session: Session): void | Promise<void>;
32
+ /** Called when an operator sends a message (for cross-bridge sync) */
33
+ onOperatorMessage?(message: Message, session: Session, sourceBridge?: string, operatorName?: string): void | Promise<void>;
34
+ /** Called when visitor starts/stops typing */
35
+ onTyping?(sessionId: string, isTyping: boolean): void | Promise<void>;
36
+ /** Called when messages are marked as delivered/read */
37
+ onMessageRead?(sessionId: string, messageIds: string[], status: MessageStatus, session: Session): void | Promise<void>;
38
+ /** Called when a custom event is triggered from the widget */
39
+ onCustomEvent?(event: CustomEvent, session: Session): void | Promise<void>;
40
+ /** Called when a user identifies themselves via PocketPing.identify() */
41
+ onIdentityUpdate?(session: Session): void | Promise<void>;
42
+ /** Cleanup when bridge is removed */
43
+ destroy?(): void | Promise<void>;
44
+ }
45
+
46
+ /**
47
+ * AI provider interface.
48
+ * Implement this to add support for OpenAI, Gemini, Claude, or local models.
49
+ */
50
+ interface AIProvider {
51
+ /** Provider name */
52
+ name: string;
53
+ /** Generate a response to the conversation */
54
+ generateResponse(messages: Message[], systemPrompt?: string): Promise<string>;
55
+ /** Check if the provider is available */
56
+ isAvailable(): Promise<boolean>;
57
+ }
58
+
59
+ interface PocketPingConfig {
60
+ /** Storage adapter for sessions and messages */
61
+ storage?: Storage | 'memory';
62
+ /** Notification bridges (Telegram, Discord, etc.) */
63
+ bridges?: Bridge[];
64
+ /** AI fallback configuration */
65
+ ai?: AIConfig;
66
+ /** Welcome message shown to new visitors */
67
+ welcomeMessage?: string;
68
+ /** Seconds of inactivity before AI takes over (default: 300) */
69
+ aiTakeoverDelay?: number;
70
+ /** Callback when a new session is created */
71
+ onNewSession?: (session: Session) => void | Promise<void>;
72
+ /** Callback when a message is received */
73
+ onMessage?: (message: Message, session: Session) => void | Promise<void>;
74
+ /** Callback when a custom event is received from widget */
75
+ onEvent?: (event: CustomEvent, session: Session) => void | Promise<void>;
76
+ /** Callback when a user identifies themselves */
77
+ onIdentify?: (session: Session) => void | Promise<void>;
78
+ /** Webhook URL to forward custom events (Zapier, Make, n8n, etc.) */
79
+ webhookUrl?: string;
80
+ /** Secret key for HMAC-SHA256 signature (X-PocketPing-Signature header) */
81
+ webhookSecret?: string;
82
+ /** Webhook request timeout in milliseconds (default: 5000) */
83
+ webhookTimeout?: number;
84
+ /** Minimum supported widget version (e.g., "0.2.0") */
85
+ minWidgetVersion?: string;
86
+ /** Latest available widget version (e.g., "0.3.0") */
87
+ latestWidgetVersion?: string;
88
+ /** Custom message for version warnings */
89
+ versionWarningMessage?: string;
90
+ /** URL to upgrade instructions */
91
+ versionUpgradeUrl?: string;
92
+ }
93
+ interface AIConfig {
94
+ provider: AIProvider | 'openai' | 'gemini' | 'anthropic';
95
+ apiKey?: string;
96
+ model?: string;
97
+ systemPrompt?: string;
98
+ fallbackAfter?: number;
99
+ }
100
+ /** User identity data from PocketPing.identify() */
101
+ interface UserIdentity {
102
+ /** Required unique user identifier */
103
+ id: string;
104
+ /** User's email address */
105
+ email?: string;
106
+ /** User's display name */
107
+ name?: string;
108
+ /** Any custom fields (plan, company, etc.) */
109
+ [key: string]: unknown;
110
+ }
111
+ interface Session {
112
+ id: string;
113
+ visitorId: string;
114
+ createdAt: Date;
115
+ lastActivity: Date;
116
+ operatorOnline: boolean;
117
+ aiActive: boolean;
118
+ metadata?: SessionMetadata;
119
+ /** User identity if identified via PocketPing.identify() */
120
+ identity?: UserIdentity;
121
+ }
122
+ interface SessionMetadata {
123
+ url?: string;
124
+ referrer?: string;
125
+ pageTitle?: string;
126
+ userAgent?: string;
127
+ timezone?: string;
128
+ language?: string;
129
+ screenResolution?: string;
130
+ ip?: string;
131
+ country?: string;
132
+ city?: string;
133
+ deviceType?: 'desktop' | 'mobile' | 'tablet';
134
+ browser?: string;
135
+ os?: string;
136
+ [key: string]: unknown;
137
+ }
138
+ type MessageStatus = 'sending' | 'sent' | 'delivered' | 'read';
139
+ interface Message {
140
+ id: string;
141
+ sessionId: string;
142
+ content: string;
143
+ sender: 'visitor' | 'operator' | 'ai';
144
+ timestamp: Date;
145
+ replyTo?: string;
146
+ metadata?: Record<string, unknown>;
147
+ status?: MessageStatus;
148
+ deliveredAt?: Date;
149
+ readAt?: Date;
150
+ }
151
+ interface ConnectRequest {
152
+ visitorId: string;
153
+ sessionId?: string;
154
+ metadata?: SessionMetadata;
155
+ /** User identity if already identified */
156
+ identity?: UserIdentity;
157
+ }
158
+ /** Tracked element configuration (for SaaS auto-tracking) */
159
+ interface TrackedElement {
160
+ /** CSS selector for the element(s) to track */
161
+ selector: string;
162
+ /** DOM event to listen for (default: 'click') */
163
+ event?: 'click' | 'submit' | 'focus' | 'change' | 'mouseenter';
164
+ /** Event name sent to backend */
165
+ name: string;
166
+ /** If provided, opens widget with this message when triggered */
167
+ widgetMessage?: string;
168
+ /** Additional data to send with the event */
169
+ data?: Record<string, unknown>;
170
+ }
171
+ /** Options for trigger() method */
172
+ interface TriggerOptions {
173
+ /** If provided, opens the widget and shows this message */
174
+ widgetMessage?: string;
175
+ }
176
+ interface ConnectResponse {
177
+ sessionId: string;
178
+ visitorId: string;
179
+ operatorOnline: boolean;
180
+ welcomeMessage?: string;
181
+ messages: Message[];
182
+ /** Tracked elements configuration (for SaaS auto-tracking) */
183
+ trackedElements?: TrackedElement[];
184
+ }
185
+ interface SendMessageRequest {
186
+ sessionId: string;
187
+ content: string;
188
+ sender: 'visitor' | 'operator';
189
+ replyTo?: string;
190
+ }
191
+ interface SendMessageResponse {
192
+ messageId: string;
193
+ timestamp: string;
194
+ }
195
+ interface GetMessagesRequest {
196
+ sessionId: string;
197
+ after?: string;
198
+ limit?: number;
199
+ }
200
+ interface GetMessagesResponse {
201
+ messages: Message[];
202
+ hasMore: boolean;
203
+ }
204
+ interface TypingRequest {
205
+ sessionId: string;
206
+ sender: 'visitor' | 'operator';
207
+ isTyping?: boolean;
208
+ }
209
+ interface ReadRequest {
210
+ sessionId: string;
211
+ messageIds: string[];
212
+ status?: MessageStatus;
213
+ }
214
+ interface ReadResponse {
215
+ updated: number;
216
+ }
217
+ interface IdentifyRequest {
218
+ sessionId: string;
219
+ identity: UserIdentity;
220
+ }
221
+ interface IdentifyResponse {
222
+ ok: boolean;
223
+ }
224
+ interface PresenceResponse {
225
+ online: boolean;
226
+ operators?: Array<{
227
+ id: string;
228
+ name: string;
229
+ avatar?: string;
230
+ }>;
231
+ aiEnabled: boolean;
232
+ aiActiveAfter?: number;
233
+ }
234
+ /** Custom event sent from widget to backend or vice versa */
235
+ interface CustomEvent {
236
+ /** Event name (e.g., 'clicked_pricing', 'show_offer') */
237
+ name: string;
238
+ /** Event payload */
239
+ data?: Record<string, unknown>;
240
+ /** Timestamp of the event */
241
+ timestamp: string;
242
+ /** Session ID (populated by SDK when event comes from widget) */
243
+ sessionId?: string;
244
+ }
245
+ /** Handler for custom events */
246
+ type CustomEventHandler = (event: CustomEvent, session: Session) => void | Promise<void>;
247
+ type VersionStatus = 'ok' | 'outdated' | 'deprecated' | 'unsupported';
248
+ interface VersionCheckResult {
249
+ status: VersionStatus;
250
+ message?: string;
251
+ minVersion?: string;
252
+ latestVersion?: string;
253
+ canContinue: boolean;
254
+ }
255
+ /** Payload sent to webhook URL */
256
+ interface WebhookPayload {
257
+ /** The custom event */
258
+ event: CustomEvent;
259
+ /** Session information */
260
+ session: {
261
+ id: string;
262
+ visitorId: string;
263
+ metadata?: SessionMetadata;
264
+ identity?: UserIdentity;
265
+ };
266
+ /** Timestamp when webhook was sent */
267
+ sentAt: string;
268
+ }
269
+
270
+ declare class PocketPing {
271
+ private storage;
272
+ private bridges;
273
+ private config;
274
+ private wss;
275
+ private sessionSockets;
276
+ private operatorOnline;
277
+ private eventHandlers;
278
+ constructor(config?: PocketPingConfig);
279
+ private initStorage;
280
+ middleware(): (req: IncomingMessage & {
281
+ body?: unknown;
282
+ query?: Record<string, string>;
283
+ }, res: ServerResponse, next?: () => void) => void;
284
+ private parseBody;
285
+ attachWebSocket(server: any): void;
286
+ private handleWebSocketMessage;
287
+ private handleCustomEvent;
288
+ private broadcastToSession;
289
+ handleConnect(request: ConnectRequest): Promise<ConnectResponse>;
290
+ handleMessage(request: SendMessageRequest): Promise<SendMessageResponse>;
291
+ handleGetMessages(request: GetMessagesRequest): Promise<GetMessagesResponse>;
292
+ handleTyping(request: TypingRequest): Promise<{
293
+ ok: boolean;
294
+ }>;
295
+ handlePresence(): Promise<PresenceResponse>;
296
+ handleRead(request: ReadRequest): Promise<ReadResponse>;
297
+ /**
298
+ * Handle user identification from widget
299
+ * Called when visitor calls PocketPing.identify()
300
+ */
301
+ handleIdentify(request: IdentifyRequest): Promise<IdentifyResponse>;
302
+ /**
303
+ * Get a session by ID
304
+ */
305
+ getSession(sessionId: string): Promise<Session | null>;
306
+ sendOperatorMessage(sessionId: string, content: string): Promise<Message>;
307
+ setOperatorOnline(online: boolean): void;
308
+ /**
309
+ * Subscribe to custom events from widgets
310
+ * @param eventName - The name of the event to listen for, or '*' for all events
311
+ * @param handler - Callback function when event is received
312
+ * @returns Unsubscribe function
313
+ * @example
314
+ * // Listen for specific event
315
+ * pp.onEvent('clicked_pricing', async (event, session) => {
316
+ * console.log(`User ${session.visitorId} clicked pricing: ${event.data?.plan}`)
317
+ * })
318
+ *
319
+ * // Listen for all events
320
+ * pp.onEvent('*', async (event, session) => {
321
+ * console.log(`Event: ${event.name}`, event.data)
322
+ * })
323
+ */
324
+ onEvent(eventName: string, handler: CustomEventHandler): () => void;
325
+ /**
326
+ * Unsubscribe from a custom event
327
+ * @param eventName - The name of the event
328
+ * @param handler - The handler to remove
329
+ */
330
+ offEvent(eventName: string, handler: CustomEventHandler): void;
331
+ /**
332
+ * Send a custom event to a specific widget/session
333
+ * @param sessionId - The session ID to send the event to
334
+ * @param eventName - The name of the event
335
+ * @param data - Optional payload to send with the event
336
+ * @example
337
+ * // Send a promotion offer to a specific user
338
+ * pp.emitEvent('session-123', 'show_offer', {
339
+ * discount: 20,
340
+ * code: 'SAVE20',
341
+ * message: 'Special offer just for you!'
342
+ * })
343
+ */
344
+ emitEvent(sessionId: string, eventName: string, data?: Record<string, unknown>): void;
345
+ /**
346
+ * Broadcast a custom event to all connected widgets
347
+ * @param eventName - The name of the event
348
+ * @param data - Optional payload to send with the event
349
+ * @example
350
+ * // Notify all users about maintenance
351
+ * pp.broadcastEvent('maintenance_warning', {
352
+ * message: 'Site will be down for maintenance in 5 minutes'
353
+ * })
354
+ */
355
+ broadcastEvent(eventName: string, data?: Record<string, unknown>): void;
356
+ /**
357
+ * Process a custom event server-side (runs handlers, bridges, webhooks)
358
+ * Useful for server-side automation or triggering events programmatically
359
+ * @param sessionId - The session ID to associate with the event
360
+ * @param eventName - The name of the event
361
+ * @param data - Optional payload for the event
362
+ * @example
363
+ * // Trigger event from backend logic (e.g., after purchase)
364
+ * await pp.triggerEvent('session-123', 'purchase_completed', {
365
+ * orderId: 'order-456',
366
+ * amount: 99.99
367
+ * })
368
+ */
369
+ triggerEvent(sessionId: string, eventName: string, data?: Record<string, unknown>): Promise<void>;
370
+ private notifyBridges;
371
+ private notifyBridgesRead;
372
+ private notifyBridgesEvent;
373
+ private notifyBridgesIdentity;
374
+ /**
375
+ * Forward custom event to configured webhook URL (non-blocking)
376
+ * Used for integrations with Zapier, Make, n8n, or custom backends
377
+ */
378
+ private forwardToWebhook;
379
+ /**
380
+ * Forward identity update to webhook as a special event
381
+ */
382
+ private forwardIdentityToWebhook;
383
+ addBridge(bridge: Bridge): void;
384
+ private generateId;
385
+ getStorage(): Storage;
386
+ /**
387
+ * Check widget version against configured min/latest versions
388
+ * @param widgetVersion - Version from X-PocketPing-Version header
389
+ * @returns Version check result with status and headers to set
390
+ */
391
+ checkWidgetVersion(widgetVersion: string | undefined): VersionCheckResult;
392
+ /**
393
+ * Set version warning headers on HTTP response
394
+ */
395
+ private setVersionHeaders;
396
+ /**
397
+ * Send version warning via WebSocket to a session
398
+ */
399
+ sendVersionWarning(sessionId: string, versionCheck: VersionCheckResult): void;
400
+ }
401
+
402
+ /**
403
+ * In-memory storage adapter.
404
+ * Useful for development and testing. Data is lost on restart.
405
+ */
406
+ declare class MemoryStorage implements Storage {
407
+ private sessions;
408
+ private messages;
409
+ private messageById;
410
+ createSession(session: Session): Promise<void>;
411
+ getSession(sessionId: string): Promise<Session | null>;
412
+ getSessionByVisitorId(visitorId: string): Promise<Session | null>;
413
+ updateSession(session: Session): Promise<void>;
414
+ deleteSession(sessionId: string): Promise<void>;
415
+ saveMessage(message: Message): Promise<void>;
416
+ getMessages(sessionId: string, after?: string, limit?: number): Promise<Message[]>;
417
+ getMessage(messageId: string): Promise<Message | null>;
418
+ cleanupOldSessions(olderThan: Date): Promise<number>;
419
+ }
420
+
421
+ export { type AIProvider, type Bridge, type ConnectRequest, type ConnectResponse, type CustomEvent, type CustomEventHandler, MemoryStorage, type Message, PocketPing, type PocketPingConfig, type PresenceResponse, type SendMessageRequest, type SendMessageResponse, type Session, type Storage, type TrackedElement, type TriggerOptions, type WebhookPayload };