@pocketping/sdk-node 1.8.0 → 1.9.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.
package/dist/index.cjs CHANGED
@@ -1897,6 +1897,12 @@ var PocketPing = class {
1897
1897
  case "delete":
1898
1898
  result = await this.handleDeleteMessage(body);
1899
1899
  break;
1900
+ case "disconnect":
1901
+ result = await this.handleDisconnect(body);
1902
+ break;
1903
+ case "visibility":
1904
+ result = await this.handleVisibility(body);
1905
+ break;
1900
1906
  default:
1901
1907
  if (next) {
1902
1908
  next();
@@ -2217,6 +2223,58 @@ var PocketPing = class {
2217
2223
  this.forwardIdentityToWebhook(session);
2218
2224
  return { ok: true };
2219
2225
  }
2226
+ /**
2227
+ * Handle visitor disconnect (page unload or inactivity)
2228
+ * Notifies bridges and triggers callback
2229
+ */
2230
+ async handleDisconnect(request) {
2231
+ const session = await this.storage.getSession(request.sessionId);
2232
+ if (!session) {
2233
+ throw new Error("Session not found");
2234
+ }
2235
+ const formatDuration = (seconds) => {
2236
+ if (seconds < 60) return `${seconds}s`;
2237
+ if (seconds < 3600) return `${Math.floor(seconds / 60)} min`;
2238
+ const hours = Math.floor(seconds / 3600);
2239
+ const mins = Math.floor(seconds % 3600 / 60);
2240
+ return mins > 0 ? `${hours}h ${mins}min` : `${hours}h`;
2241
+ };
2242
+ const visitorName = session.identity?.name || session.identity?.email?.split("@")[0] || "Visitor";
2243
+ const durationText = formatDuration(request.duration);
2244
+ const message = `\u{1F44B} ${visitorName} left (was here for ${durationText})`;
2245
+ await this.notifyBridgesDisconnect(session, message);
2246
+ await this.config.onVisitorDisconnect?.(session, request.duration);
2247
+ return { ok: true };
2248
+ }
2249
+ /**
2250
+ * Handle visibility change (tab focus/blur)
2251
+ * Used for inactivity tracking
2252
+ */
2253
+ async handleVisibility(request) {
2254
+ const session = await this.storage.getSession(request.sessionId);
2255
+ if (!session) {
2256
+ throw new Error("Session not found");
2257
+ }
2258
+ if (request.state === "visible") {
2259
+ session.lastActivity = /* @__PURE__ */ new Date();
2260
+ await this.storage.updateSession(session);
2261
+ }
2262
+ return { ok: true };
2263
+ }
2264
+ /**
2265
+ * Notify all bridges when visitor disconnects
2266
+ */
2267
+ async notifyBridgesDisconnect(session, message) {
2268
+ for (const bridge of this.bridges) {
2269
+ try {
2270
+ if ("notifyDisconnect" in bridge && typeof bridge.notifyDisconnect === "function") {
2271
+ await bridge.notifyDisconnect(session, message);
2272
+ }
2273
+ } catch (error) {
2274
+ console.error(`[PocketPing] Bridge disconnect notification failed:`, error);
2275
+ }
2276
+ }
2277
+ }
2220
2278
  /**
2221
2279
  * Get a session by ID
2222
2280
  */
package/dist/index.d.cts CHANGED
@@ -63,6 +63,20 @@ declare class PocketPing {
63
63
  * Called when visitor calls PocketPing.identify()
64
64
  */
65
65
  handleIdentify(request: IdentifyRequest): Promise<IdentifyResponse>;
66
+ /**
67
+ * Handle visitor disconnect (page unload or inactivity)
68
+ * Notifies bridges and triggers callback
69
+ */
70
+ handleDisconnect(request: DisconnectRequest): Promise<DisconnectResponse>;
71
+ /**
72
+ * Handle visibility change (tab focus/blur)
73
+ * Used for inactivity tracking
74
+ */
75
+ handleVisibility(request: VisibilityRequest): Promise<VisibilityResponse>;
76
+ /**
77
+ * Notify all bridges when visitor disconnects
78
+ */
79
+ private notifyBridgesDisconnect;
66
80
  /**
67
81
  * Get a session by ID
68
82
  */
@@ -384,6 +398,8 @@ interface PocketPingConfig {
384
398
  welcomeMessage?: string;
385
399
  /** Seconds of inactivity before AI takes over (default: 300) */
386
400
  aiTakeoverDelay?: number;
401
+ /** Seconds of visitor inactivity before notifying bridges (0 = disabled) */
402
+ visitorInactivityTimeout?: number;
387
403
  /** Callback when a new session is created */
388
404
  onNewSession?: (session: Session) => void | Promise<void>;
389
405
  /** Callback when a message is received */
@@ -392,6 +408,8 @@ interface PocketPingConfig {
392
408
  onEvent?: (event: CustomEvent, session: Session) => void | Promise<void>;
393
409
  /** Callback when a user identifies themselves */
394
410
  onIdentify?: (session: Session) => void | Promise<void>;
411
+ /** Callback when a visitor disconnects (leaves the page or goes inactive) */
412
+ onVisitorDisconnect?: (session: Session, duration: number) => void | Promise<void>;
395
413
  /** Webhook URL to forward custom events (Zapier, Make, n8n, etc.) */
396
414
  webhookUrl?: string;
397
415
  /** Secret key for HMAC-SHA256 signature (X-PocketPing-Signature header) */
@@ -602,6 +620,22 @@ interface IdentifyRequest {
602
620
  interface IdentifyResponse {
603
621
  ok: boolean;
604
622
  }
623
+ interface DisconnectRequest {
624
+ sessionId: string;
625
+ duration: number;
626
+ reason: 'page_unload' | 'inactivity' | 'manual';
627
+ }
628
+ interface DisconnectResponse {
629
+ ok: boolean;
630
+ }
631
+ interface VisibilityRequest {
632
+ sessionId: string;
633
+ state: 'hidden' | 'visible';
634
+ timestamp: number;
635
+ }
636
+ interface VisibilityResponse {
637
+ ok: boolean;
638
+ }
605
639
  interface PresenceResponse {
606
640
  online: boolean;
607
641
  operators?: Array<{
@@ -1065,4 +1099,4 @@ declare class WebhookHandler {
1065
1099
  private getSlackUserName;
1066
1100
  }
1067
1101
 
1068
- export { type AIProvider, type Bridge, type BridgeMessageIds, type BridgeMessageResult, type ConnectRequest, type ConnectResponse, type CustomEvent, type CustomEventHandler, DEFAULT_BOT_PATTERNS, type DeleteMessageRequest, type DeleteMessageResponse, type DiscordBotOptions, DiscordBridge, type DiscordWebhookOptions, type EditMessageRequest, type EditMessageResponse, type IpFilterConfig, type IpFilterLogEvent, type IpFilterMode, MemoryStorage, type Message, type OperatorAttachment, type OperatorMessageCallback, type OperatorMessageDeleteCallback, type OperatorMessageEditCallback, PocketPing, type PocketPingConfig, type PresenceResponse, type SendMessageRequest, type SendMessageResponse, type Session, type SlackBotOptions, SlackBridge, type SlackWebhookOptions, type Storage, TelegramBridge, type TelegramBridgeOptions, type TrackedElement, type TriggerOptions, type UaFilterConfig, type UaFilterLogEvent, type UaFilterMode, type UaFilterReason, type UaFilterResult, type WebhookConfig, WebhookHandler, type WebhookPayload, checkIpFilter, checkUaFilter, ipMatchesAny, ipMatchesCidr, isBot, matchesAnyPattern };
1102
+ export { type AIProvider, type Bridge, type BridgeMessageIds, type BridgeMessageResult, type ConnectRequest, type ConnectResponse, type CustomEvent, type CustomEventHandler, DEFAULT_BOT_PATTERNS, type DeleteMessageRequest, type DeleteMessageResponse, type DisconnectRequest, type DisconnectResponse, type DiscordBotOptions, DiscordBridge, type DiscordWebhookOptions, type EditMessageRequest, type EditMessageResponse, type IpFilterConfig, type IpFilterLogEvent, type IpFilterMode, MemoryStorage, type Message, type OperatorAttachment, type OperatorMessageCallback, type OperatorMessageDeleteCallback, type OperatorMessageEditCallback, PocketPing, type PocketPingConfig, type PresenceResponse, type SendMessageRequest, type SendMessageResponse, type Session, type SlackBotOptions, SlackBridge, type SlackWebhookOptions, type Storage, TelegramBridge, type TelegramBridgeOptions, type TrackedElement, type TriggerOptions, type UaFilterConfig, type UaFilterLogEvent, type UaFilterMode, type UaFilterReason, type UaFilterResult, type VisibilityRequest, type VisibilityResponse, type WebhookConfig, WebhookHandler, type WebhookPayload, checkIpFilter, checkUaFilter, ipMatchesAny, ipMatchesCidr, isBot, matchesAnyPattern };
package/dist/index.d.ts CHANGED
@@ -63,6 +63,20 @@ declare class PocketPing {
63
63
  * Called when visitor calls PocketPing.identify()
64
64
  */
65
65
  handleIdentify(request: IdentifyRequest): Promise<IdentifyResponse>;
66
+ /**
67
+ * Handle visitor disconnect (page unload or inactivity)
68
+ * Notifies bridges and triggers callback
69
+ */
70
+ handleDisconnect(request: DisconnectRequest): Promise<DisconnectResponse>;
71
+ /**
72
+ * Handle visibility change (tab focus/blur)
73
+ * Used for inactivity tracking
74
+ */
75
+ handleVisibility(request: VisibilityRequest): Promise<VisibilityResponse>;
76
+ /**
77
+ * Notify all bridges when visitor disconnects
78
+ */
79
+ private notifyBridgesDisconnect;
66
80
  /**
67
81
  * Get a session by ID
68
82
  */
@@ -384,6 +398,8 @@ interface PocketPingConfig {
384
398
  welcomeMessage?: string;
385
399
  /** Seconds of inactivity before AI takes over (default: 300) */
386
400
  aiTakeoverDelay?: number;
401
+ /** Seconds of visitor inactivity before notifying bridges (0 = disabled) */
402
+ visitorInactivityTimeout?: number;
387
403
  /** Callback when a new session is created */
388
404
  onNewSession?: (session: Session) => void | Promise<void>;
389
405
  /** Callback when a message is received */
@@ -392,6 +408,8 @@ interface PocketPingConfig {
392
408
  onEvent?: (event: CustomEvent, session: Session) => void | Promise<void>;
393
409
  /** Callback when a user identifies themselves */
394
410
  onIdentify?: (session: Session) => void | Promise<void>;
411
+ /** Callback when a visitor disconnects (leaves the page or goes inactive) */
412
+ onVisitorDisconnect?: (session: Session, duration: number) => void | Promise<void>;
395
413
  /** Webhook URL to forward custom events (Zapier, Make, n8n, etc.) */
396
414
  webhookUrl?: string;
397
415
  /** Secret key for HMAC-SHA256 signature (X-PocketPing-Signature header) */
@@ -602,6 +620,22 @@ interface IdentifyRequest {
602
620
  interface IdentifyResponse {
603
621
  ok: boolean;
604
622
  }
623
+ interface DisconnectRequest {
624
+ sessionId: string;
625
+ duration: number;
626
+ reason: 'page_unload' | 'inactivity' | 'manual';
627
+ }
628
+ interface DisconnectResponse {
629
+ ok: boolean;
630
+ }
631
+ interface VisibilityRequest {
632
+ sessionId: string;
633
+ state: 'hidden' | 'visible';
634
+ timestamp: number;
635
+ }
636
+ interface VisibilityResponse {
637
+ ok: boolean;
638
+ }
605
639
  interface PresenceResponse {
606
640
  online: boolean;
607
641
  operators?: Array<{
@@ -1065,4 +1099,4 @@ declare class WebhookHandler {
1065
1099
  private getSlackUserName;
1066
1100
  }
1067
1101
 
1068
- export { type AIProvider, type Bridge, type BridgeMessageIds, type BridgeMessageResult, type ConnectRequest, type ConnectResponse, type CustomEvent, type CustomEventHandler, DEFAULT_BOT_PATTERNS, type DeleteMessageRequest, type DeleteMessageResponse, type DiscordBotOptions, DiscordBridge, type DiscordWebhookOptions, type EditMessageRequest, type EditMessageResponse, type IpFilterConfig, type IpFilterLogEvent, type IpFilterMode, MemoryStorage, type Message, type OperatorAttachment, type OperatorMessageCallback, type OperatorMessageDeleteCallback, type OperatorMessageEditCallback, PocketPing, type PocketPingConfig, type PresenceResponse, type SendMessageRequest, type SendMessageResponse, type Session, type SlackBotOptions, SlackBridge, type SlackWebhookOptions, type Storage, TelegramBridge, type TelegramBridgeOptions, type TrackedElement, type TriggerOptions, type UaFilterConfig, type UaFilterLogEvent, type UaFilterMode, type UaFilterReason, type UaFilterResult, type WebhookConfig, WebhookHandler, type WebhookPayload, checkIpFilter, checkUaFilter, ipMatchesAny, ipMatchesCidr, isBot, matchesAnyPattern };
1102
+ export { type AIProvider, type Bridge, type BridgeMessageIds, type BridgeMessageResult, type ConnectRequest, type ConnectResponse, type CustomEvent, type CustomEventHandler, DEFAULT_BOT_PATTERNS, type DeleteMessageRequest, type DeleteMessageResponse, type DisconnectRequest, type DisconnectResponse, type DiscordBotOptions, DiscordBridge, type DiscordWebhookOptions, type EditMessageRequest, type EditMessageResponse, type IpFilterConfig, type IpFilterLogEvent, type IpFilterMode, MemoryStorage, type Message, type OperatorAttachment, type OperatorMessageCallback, type OperatorMessageDeleteCallback, type OperatorMessageEditCallback, PocketPing, type PocketPingConfig, type PresenceResponse, type SendMessageRequest, type SendMessageResponse, type Session, type SlackBotOptions, SlackBridge, type SlackWebhookOptions, type Storage, TelegramBridge, type TelegramBridgeOptions, type TrackedElement, type TriggerOptions, type UaFilterConfig, type UaFilterLogEvent, type UaFilterMode, type UaFilterReason, type UaFilterResult, type VisibilityRequest, type VisibilityResponse, type WebhookConfig, WebhookHandler, type WebhookPayload, checkIpFilter, checkUaFilter, ipMatchesAny, ipMatchesCidr, isBot, matchesAnyPattern };
package/dist/index.js CHANGED
@@ -1859,6 +1859,12 @@ var PocketPing = class {
1859
1859
  case "delete":
1860
1860
  result = await this.handleDeleteMessage(body);
1861
1861
  break;
1862
+ case "disconnect":
1863
+ result = await this.handleDisconnect(body);
1864
+ break;
1865
+ case "visibility":
1866
+ result = await this.handleVisibility(body);
1867
+ break;
1862
1868
  default:
1863
1869
  if (next) {
1864
1870
  next();
@@ -2179,6 +2185,58 @@ var PocketPing = class {
2179
2185
  this.forwardIdentityToWebhook(session);
2180
2186
  return { ok: true };
2181
2187
  }
2188
+ /**
2189
+ * Handle visitor disconnect (page unload or inactivity)
2190
+ * Notifies bridges and triggers callback
2191
+ */
2192
+ async handleDisconnect(request) {
2193
+ const session = await this.storage.getSession(request.sessionId);
2194
+ if (!session) {
2195
+ throw new Error("Session not found");
2196
+ }
2197
+ const formatDuration = (seconds) => {
2198
+ if (seconds < 60) return `${seconds}s`;
2199
+ if (seconds < 3600) return `${Math.floor(seconds / 60)} min`;
2200
+ const hours = Math.floor(seconds / 3600);
2201
+ const mins = Math.floor(seconds % 3600 / 60);
2202
+ return mins > 0 ? `${hours}h ${mins}min` : `${hours}h`;
2203
+ };
2204
+ const visitorName = session.identity?.name || session.identity?.email?.split("@")[0] || "Visitor";
2205
+ const durationText = formatDuration(request.duration);
2206
+ const message = `\u{1F44B} ${visitorName} left (was here for ${durationText})`;
2207
+ await this.notifyBridgesDisconnect(session, message);
2208
+ await this.config.onVisitorDisconnect?.(session, request.duration);
2209
+ return { ok: true };
2210
+ }
2211
+ /**
2212
+ * Handle visibility change (tab focus/blur)
2213
+ * Used for inactivity tracking
2214
+ */
2215
+ async handleVisibility(request) {
2216
+ const session = await this.storage.getSession(request.sessionId);
2217
+ if (!session) {
2218
+ throw new Error("Session not found");
2219
+ }
2220
+ if (request.state === "visible") {
2221
+ session.lastActivity = /* @__PURE__ */ new Date();
2222
+ await this.storage.updateSession(session);
2223
+ }
2224
+ return { ok: true };
2225
+ }
2226
+ /**
2227
+ * Notify all bridges when visitor disconnects
2228
+ */
2229
+ async notifyBridgesDisconnect(session, message) {
2230
+ for (const bridge of this.bridges) {
2231
+ try {
2232
+ if ("notifyDisconnect" in bridge && typeof bridge.notifyDisconnect === "function") {
2233
+ await bridge.notifyDisconnect(session, message);
2234
+ }
2235
+ } catch (error) {
2236
+ console.error(`[PocketPing] Bridge disconnect notification failed:`, error);
2237
+ }
2238
+ }
2239
+ }
2182
2240
  /**
2183
2241
  * Get a session by ID
2184
2242
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pocketping/sdk-node",
3
- "version": "1.8.0",
3
+ "version": "1.9.0",
4
4
  "type": "module",
5
5
  "description": "Node.js SDK for implementing PocketPing protocol",
6
6
  "main": "dist/index.cjs",