@pocketping/widget 0.1.0 → 0.2.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/README.md +89 -2
- package/dist/index.d.mts +191 -1
- package/dist/index.d.ts +191 -1
- package/dist/index.global.js +264 -4
- package/dist/index.js +272 -5
- package/dist/index.mjs +264 -4
- package/package.json +8 -4
package/dist/index.global.js
CHANGED
|
@@ -24,10 +24,17 @@ var PocketPing = (() => {
|
|
|
24
24
|
close: () => close,
|
|
25
25
|
default: () => index_default,
|
|
26
26
|
destroy: () => destroy,
|
|
27
|
+
getIdentity: () => getIdentity,
|
|
28
|
+
identify: () => identify,
|
|
27
29
|
init: () => init,
|
|
30
|
+
offEvent: () => offEvent,
|
|
31
|
+
on: () => on,
|
|
32
|
+
onEvent: () => onEvent,
|
|
28
33
|
open: () => open,
|
|
34
|
+
reset: () => reset,
|
|
29
35
|
sendMessage: () => sendMessage,
|
|
30
|
-
toggle: () => toggle
|
|
36
|
+
toggle: () => toggle,
|
|
37
|
+
trigger: () => trigger
|
|
31
38
|
});
|
|
32
39
|
|
|
33
40
|
// ../../node_modules/.pnpm/preact@10.28.2/node_modules/preact/dist/preact.mjs
|
|
@@ -1036,6 +1043,9 @@ var PocketPing = (() => {
|
|
|
1036
1043
|
return null;
|
|
1037
1044
|
}
|
|
1038
1045
|
|
|
1046
|
+
// src/version.ts
|
|
1047
|
+
var VERSION = "0.2.0";
|
|
1048
|
+
|
|
1039
1049
|
// src/client.ts
|
|
1040
1050
|
var PocketPingClient = class {
|
|
1041
1051
|
constructor(config) {
|
|
@@ -1043,6 +1053,7 @@ var PocketPing = (() => {
|
|
|
1043
1053
|
this.ws = null;
|
|
1044
1054
|
this.isOpen = false;
|
|
1045
1055
|
this.listeners = /* @__PURE__ */ new Map();
|
|
1056
|
+
this.customEventHandlers = /* @__PURE__ */ new Map();
|
|
1046
1057
|
this.reconnectAttempts = 0;
|
|
1047
1058
|
this.maxReconnectAttempts = 5;
|
|
1048
1059
|
this.reconnectTimeout = null;
|
|
@@ -1054,6 +1065,7 @@ var PocketPing = (() => {
|
|
|
1054
1065
|
async connect() {
|
|
1055
1066
|
const visitorId = this.getOrCreateVisitorId();
|
|
1056
1067
|
const storedSessionId = this.getStoredSessionId();
|
|
1068
|
+
const storedIdentity = this.getStoredIdentity();
|
|
1057
1069
|
const response = await this.fetch("/connect", {
|
|
1058
1070
|
method: "POST",
|
|
1059
1071
|
body: JSON.stringify({
|
|
@@ -1067,14 +1079,17 @@ var PocketPing = (() => {
|
|
|
1067
1079
|
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
1068
1080
|
language: navigator.language,
|
|
1069
1081
|
screenResolution: `${window.screen.width}x${window.screen.height}`
|
|
1070
|
-
}
|
|
1082
|
+
},
|
|
1083
|
+
// Include stored identity if available
|
|
1084
|
+
identity: storedIdentity || void 0
|
|
1071
1085
|
})
|
|
1072
1086
|
});
|
|
1073
1087
|
this.session = {
|
|
1074
1088
|
sessionId: response.sessionId,
|
|
1075
1089
|
visitorId: response.visitorId,
|
|
1076
1090
|
operatorOnline: response.operatorOnline ?? false,
|
|
1077
|
-
messages: response.messages ?? []
|
|
1091
|
+
messages: response.messages ?? [],
|
|
1092
|
+
identity: response.identity || storedIdentity || void 0
|
|
1078
1093
|
};
|
|
1079
1094
|
this.storeSessionId(response.sessionId);
|
|
1080
1095
|
this.connectWebSocket();
|
|
@@ -1197,6 +1212,69 @@ var PocketPing = (() => {
|
|
|
1197
1212
|
return this.fetch("/presence", { method: "GET" });
|
|
1198
1213
|
}
|
|
1199
1214
|
// ─────────────────────────────────────────────────────────────────
|
|
1215
|
+
// User Identity
|
|
1216
|
+
// ─────────────────────────────────────────────────────────────────
|
|
1217
|
+
/**
|
|
1218
|
+
* Identify the current user with metadata
|
|
1219
|
+
* Call after user logs in or when user data becomes available
|
|
1220
|
+
* @param identity - User identity data with required id field
|
|
1221
|
+
* @example
|
|
1222
|
+
* PocketPing.identify({
|
|
1223
|
+
* id: 'user_123',
|
|
1224
|
+
* email: 'john@example.com',
|
|
1225
|
+
* name: 'John Doe',
|
|
1226
|
+
* plan: 'pro',
|
|
1227
|
+
* company: 'Acme Inc'
|
|
1228
|
+
* })
|
|
1229
|
+
*/
|
|
1230
|
+
async identify(identity) {
|
|
1231
|
+
if (!identity?.id) {
|
|
1232
|
+
throw new Error("[PocketPing] identity.id is required");
|
|
1233
|
+
}
|
|
1234
|
+
this.storeIdentity(identity);
|
|
1235
|
+
if (this.session) {
|
|
1236
|
+
try {
|
|
1237
|
+
await this.fetch("/identify", {
|
|
1238
|
+
method: "POST",
|
|
1239
|
+
body: JSON.stringify({
|
|
1240
|
+
sessionId: this.session.sessionId,
|
|
1241
|
+
identity
|
|
1242
|
+
})
|
|
1243
|
+
});
|
|
1244
|
+
this.session.identity = identity;
|
|
1245
|
+
this.emit("identify", identity);
|
|
1246
|
+
} catch (err) {
|
|
1247
|
+
console.error("[PocketPing] Failed to identify:", err);
|
|
1248
|
+
throw err;
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
/**
|
|
1253
|
+
* Reset the user identity and optionally start a new session
|
|
1254
|
+
* Call on user logout to clear user data
|
|
1255
|
+
* @param options - Optional settings: { newSession: boolean }
|
|
1256
|
+
*/
|
|
1257
|
+
async reset(options) {
|
|
1258
|
+
this.clearIdentity();
|
|
1259
|
+
if (this.session) {
|
|
1260
|
+
this.session.identity = void 0;
|
|
1261
|
+
}
|
|
1262
|
+
if (options?.newSession) {
|
|
1263
|
+
localStorage.removeItem("pocketping_session_id");
|
|
1264
|
+
localStorage.removeItem("pocketping_visitor_id");
|
|
1265
|
+
this.disconnect();
|
|
1266
|
+
await this.connect();
|
|
1267
|
+
}
|
|
1268
|
+
this.emit("reset", null);
|
|
1269
|
+
}
|
|
1270
|
+
/**
|
|
1271
|
+
* Get the current user identity
|
|
1272
|
+
* @returns UserIdentity or null if not identified
|
|
1273
|
+
*/
|
|
1274
|
+
getIdentity() {
|
|
1275
|
+
return this.session?.identity || this.getStoredIdentity();
|
|
1276
|
+
}
|
|
1277
|
+
// ─────────────────────────────────────────────────────────────────
|
|
1200
1278
|
// State
|
|
1201
1279
|
// ─────────────────────────────────────────────────────────────────
|
|
1202
1280
|
getSession() {
|
|
@@ -1239,6 +1317,67 @@ var PocketPing = (() => {
|
|
|
1239
1317
|
this.listeners.get(event)?.forEach((listener) => listener(data));
|
|
1240
1318
|
}
|
|
1241
1319
|
// ─────────────────────────────────────────────────────────────────
|
|
1320
|
+
// Custom Events (bidirectional)
|
|
1321
|
+
// ─────────────────────────────────────────────────────────────────
|
|
1322
|
+
/**
|
|
1323
|
+
* Trigger a custom event to the backend
|
|
1324
|
+
* @param eventName - The name of the event (e.g., 'clicked_pricing', 'viewed_demo')
|
|
1325
|
+
* @param data - Optional payload to send with the event
|
|
1326
|
+
* @example
|
|
1327
|
+
* PocketPing.trigger('clicked_cta', { button: 'signup', page: '/pricing' })
|
|
1328
|
+
*/
|
|
1329
|
+
trigger(eventName, data) {
|
|
1330
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
1331
|
+
console.warn("[PocketPing] Cannot trigger event: WebSocket not connected");
|
|
1332
|
+
return;
|
|
1333
|
+
}
|
|
1334
|
+
const event = {
|
|
1335
|
+
name: eventName,
|
|
1336
|
+
data,
|
|
1337
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1338
|
+
};
|
|
1339
|
+
this.ws.send(JSON.stringify({
|
|
1340
|
+
type: "event",
|
|
1341
|
+
data: event
|
|
1342
|
+
}));
|
|
1343
|
+
this.emit(`event:${eventName}`, event);
|
|
1344
|
+
}
|
|
1345
|
+
/**
|
|
1346
|
+
* Subscribe to custom events from the backend
|
|
1347
|
+
* @param eventName - The name of the event to listen for (e.g., 'show_offer', 'open_chat')
|
|
1348
|
+
* @param handler - Callback function when event is received
|
|
1349
|
+
* @returns Unsubscribe function
|
|
1350
|
+
* @example
|
|
1351
|
+
* const unsubscribe = PocketPing.onEvent('show_offer', (data) => {
|
|
1352
|
+
* showPopup(data.message)
|
|
1353
|
+
* })
|
|
1354
|
+
*/
|
|
1355
|
+
onEvent(eventName, handler) {
|
|
1356
|
+
if (!this.customEventHandlers.has(eventName)) {
|
|
1357
|
+
this.customEventHandlers.set(eventName, /* @__PURE__ */ new Set());
|
|
1358
|
+
}
|
|
1359
|
+
this.customEventHandlers.get(eventName).add(handler);
|
|
1360
|
+
return () => {
|
|
1361
|
+
this.customEventHandlers.get(eventName)?.delete(handler);
|
|
1362
|
+
};
|
|
1363
|
+
}
|
|
1364
|
+
/**
|
|
1365
|
+
* Unsubscribe from a custom event
|
|
1366
|
+
* @param eventName - The name of the event
|
|
1367
|
+
* @param handler - The handler to remove
|
|
1368
|
+
*/
|
|
1369
|
+
offEvent(eventName, handler) {
|
|
1370
|
+
this.customEventHandlers.get(eventName)?.delete(handler);
|
|
1371
|
+
}
|
|
1372
|
+
emitCustomEvent(event) {
|
|
1373
|
+
const handlers = this.customEventHandlers.get(event.name);
|
|
1374
|
+
if (handlers) {
|
|
1375
|
+
handlers.forEach((handler) => handler(event.data, event));
|
|
1376
|
+
}
|
|
1377
|
+
this.emit("event", event);
|
|
1378
|
+
this.emit(`event:${event.name}`, event);
|
|
1379
|
+
}
|
|
1380
|
+
// ─────────────────────────────────────────────────────────────────
|
|
1242
1381
|
// WebSocket
|
|
1243
1382
|
// ─────────────────────────────────────────────────────────────────
|
|
1244
1383
|
connectWebSocket() {
|
|
@@ -1336,6 +1475,40 @@ var PocketPing = (() => {
|
|
|
1336
1475
|
}
|
|
1337
1476
|
this.emit("read", readData);
|
|
1338
1477
|
break;
|
|
1478
|
+
case "event":
|
|
1479
|
+
const customEvent = event.data;
|
|
1480
|
+
this.emitCustomEvent(customEvent);
|
|
1481
|
+
break;
|
|
1482
|
+
case "version_warning":
|
|
1483
|
+
const versionWarning = event.data;
|
|
1484
|
+
this.handleVersionWarning(versionWarning);
|
|
1485
|
+
break;
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
// ─────────────────────────────────────────────────────────────────
|
|
1489
|
+
// Version Management
|
|
1490
|
+
// ─────────────────────────────────────────────────────────────────
|
|
1491
|
+
handleVersionWarning(warning) {
|
|
1492
|
+
const prefix = "[PocketPing]";
|
|
1493
|
+
const upgradeHint = warning.upgradeUrl ? ` Upgrade: ${warning.upgradeUrl}` : " Update your widget to the latest version.";
|
|
1494
|
+
switch (warning.severity) {
|
|
1495
|
+
case "error":
|
|
1496
|
+
console.error(`${prefix} \u{1F6A8} VERSION ERROR: ${warning.message}${upgradeHint}`);
|
|
1497
|
+
console.error(`${prefix} Current: ${warning.currentVersion}, Required: ${warning.minVersion || "unknown"}`);
|
|
1498
|
+
break;
|
|
1499
|
+
case "warning":
|
|
1500
|
+
console.warn(`${prefix} \u26A0\uFE0F VERSION WARNING: ${warning.message}${upgradeHint}`);
|
|
1501
|
+
console.warn(`${prefix} Current: ${warning.currentVersion}, Latest: ${warning.latestVersion || "unknown"}`);
|
|
1502
|
+
break;
|
|
1503
|
+
case "info":
|
|
1504
|
+
console.info(`${prefix} \u2139\uFE0F ${warning.message}`);
|
|
1505
|
+
break;
|
|
1506
|
+
}
|
|
1507
|
+
this.emit("versionWarning", warning);
|
|
1508
|
+
this.config.onVersionWarning?.(warning);
|
|
1509
|
+
if (!warning.canContinue) {
|
|
1510
|
+
console.error(`${prefix} Widget is incompatible with backend. Please update immediately.`);
|
|
1511
|
+
this.disconnect();
|
|
1339
1512
|
}
|
|
1340
1513
|
}
|
|
1341
1514
|
scheduleReconnect() {
|
|
@@ -1381,15 +1554,46 @@ var PocketPing = (() => {
|
|
|
1381
1554
|
...options,
|
|
1382
1555
|
headers: {
|
|
1383
1556
|
"Content-Type": "application/json",
|
|
1557
|
+
"X-PocketPing-Version": VERSION,
|
|
1384
1558
|
...options.headers
|
|
1385
1559
|
}
|
|
1386
1560
|
});
|
|
1561
|
+
this.checkVersionHeaders(response);
|
|
1387
1562
|
if (!response.ok) {
|
|
1388
1563
|
const error = await response.text();
|
|
1389
1564
|
throw new Error(`PocketPing API error: ${response.status} ${error}`);
|
|
1390
1565
|
}
|
|
1391
1566
|
return response.json();
|
|
1392
1567
|
}
|
|
1568
|
+
checkVersionHeaders(response) {
|
|
1569
|
+
const versionStatus = response.headers.get("X-PocketPing-Version-Status");
|
|
1570
|
+
const minVersion = response.headers.get("X-PocketPing-Min-Version");
|
|
1571
|
+
const latestVersion = response.headers.get("X-PocketPing-Latest-Version");
|
|
1572
|
+
const versionMessage = response.headers.get("X-PocketPing-Version-Message");
|
|
1573
|
+
if (!versionStatus || versionStatus === "ok") {
|
|
1574
|
+
return;
|
|
1575
|
+
}
|
|
1576
|
+
let severity = "info";
|
|
1577
|
+
let canContinue = true;
|
|
1578
|
+
if (versionStatus === "deprecated") {
|
|
1579
|
+
severity = "warning";
|
|
1580
|
+
} else if (versionStatus === "unsupported") {
|
|
1581
|
+
severity = "error";
|
|
1582
|
+
canContinue = false;
|
|
1583
|
+
} else if (versionStatus === "outdated") {
|
|
1584
|
+
severity = "info";
|
|
1585
|
+
}
|
|
1586
|
+
const warning = {
|
|
1587
|
+
severity,
|
|
1588
|
+
message: versionMessage || `Widget version ${VERSION} is ${versionStatus}`,
|
|
1589
|
+
currentVersion: VERSION,
|
|
1590
|
+
minVersion: minVersion || void 0,
|
|
1591
|
+
latestVersion: latestVersion || void 0,
|
|
1592
|
+
canContinue,
|
|
1593
|
+
upgradeUrl: "https://docs.pocketping.io/widget/installation"
|
|
1594
|
+
};
|
|
1595
|
+
this.handleVersionWarning(warning);
|
|
1596
|
+
}
|
|
1393
1597
|
// ─────────────────────────────────────────────────────────────────
|
|
1394
1598
|
// Storage
|
|
1395
1599
|
// ─────────────────────────────────────────────────────────────────
|
|
@@ -1408,6 +1612,20 @@ var PocketPing = (() => {
|
|
|
1408
1612
|
storeSessionId(sessionId) {
|
|
1409
1613
|
localStorage.setItem("pocketping_session_id", sessionId);
|
|
1410
1614
|
}
|
|
1615
|
+
getStoredIdentity() {
|
|
1616
|
+
try {
|
|
1617
|
+
const stored = localStorage.getItem("pocketping_user_identity");
|
|
1618
|
+
return stored ? JSON.parse(stored) : null;
|
|
1619
|
+
} catch {
|
|
1620
|
+
return null;
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
storeIdentity(identity) {
|
|
1624
|
+
localStorage.setItem("pocketping_user_identity", JSON.stringify(identity));
|
|
1625
|
+
}
|
|
1626
|
+
clearIdentity() {
|
|
1627
|
+
localStorage.removeItem("pocketping_user_identity");
|
|
1628
|
+
}
|
|
1411
1629
|
generateId() {
|
|
1412
1630
|
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 11)}`;
|
|
1413
1631
|
}
|
|
@@ -1460,6 +1678,48 @@ var PocketPing = (() => {
|
|
|
1460
1678
|
}
|
|
1461
1679
|
return client.sendMessage(content);
|
|
1462
1680
|
}
|
|
1681
|
+
function trigger(eventName, data) {
|
|
1682
|
+
if (!client) {
|
|
1683
|
+
console.warn("[PocketPing] Not initialized, cannot trigger event");
|
|
1684
|
+
return;
|
|
1685
|
+
}
|
|
1686
|
+
client.trigger(eventName, data);
|
|
1687
|
+
}
|
|
1688
|
+
function onEvent(eventName, handler) {
|
|
1689
|
+
if (!client) {
|
|
1690
|
+
console.warn("[PocketPing] Not initialized, cannot subscribe to event");
|
|
1691
|
+
return () => {
|
|
1692
|
+
};
|
|
1693
|
+
}
|
|
1694
|
+
return client.onEvent(eventName, handler);
|
|
1695
|
+
}
|
|
1696
|
+
function offEvent(eventName, handler) {
|
|
1697
|
+
client?.offEvent(eventName, handler);
|
|
1698
|
+
}
|
|
1699
|
+
async function identify(identity) {
|
|
1700
|
+
if (!client) {
|
|
1701
|
+
throw new Error("[PocketPing] Not initialized");
|
|
1702
|
+
}
|
|
1703
|
+
return client.identify(identity);
|
|
1704
|
+
}
|
|
1705
|
+
async function reset(options) {
|
|
1706
|
+
if (!client) {
|
|
1707
|
+
console.warn("[PocketPing] Not initialized");
|
|
1708
|
+
return;
|
|
1709
|
+
}
|
|
1710
|
+
return client.reset(options);
|
|
1711
|
+
}
|
|
1712
|
+
function getIdentity() {
|
|
1713
|
+
return client?.getIdentity() || null;
|
|
1714
|
+
}
|
|
1715
|
+
function on(eventName, handler) {
|
|
1716
|
+
if (!client) {
|
|
1717
|
+
console.warn("[PocketPing] Not initialized, cannot subscribe to event");
|
|
1718
|
+
return () => {
|
|
1719
|
+
};
|
|
1720
|
+
}
|
|
1721
|
+
return client.on(eventName, handler);
|
|
1722
|
+
}
|
|
1463
1723
|
if (typeof document !== "undefined") {
|
|
1464
1724
|
const script = document.currentScript;
|
|
1465
1725
|
if (script?.dataset.endpoint) {
|
|
@@ -1470,6 +1730,6 @@ var PocketPing = (() => {
|
|
|
1470
1730
|
});
|
|
1471
1731
|
}
|
|
1472
1732
|
}
|
|
1473
|
-
var index_default = { init, destroy, open, close, toggle, sendMessage };
|
|
1733
|
+
var index_default = { init, destroy, open, close, toggle, sendMessage, trigger, onEvent, offEvent, on, identify, reset, getIdentity };
|
|
1474
1734
|
return __toCommonJS(index_exports);
|
|
1475
1735
|
})();
|