@pocketping/widget 0.1.0 → 0.3.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/LICENSE +21 -0
- package/README.md +89 -2
- package/dist/index.d.mts +199 -4
- package/dist/index.d.ts +199 -4
- package/dist/index.js +292 -14
- package/dist/index.mjs +284 -13
- package/dist/pocketping.min.global.js +329 -0
- package/package.json +12 -8
- package/dist/index.global.js +0 -1475
package/dist/index.mjs
CHANGED
|
@@ -599,6 +599,9 @@ function StatusIcon({ status }) {
|
|
|
599
599
|
return null;
|
|
600
600
|
}
|
|
601
601
|
|
|
602
|
+
// src/version.ts
|
|
603
|
+
var VERSION = "0.3.0";
|
|
604
|
+
|
|
602
605
|
// src/client.ts
|
|
603
606
|
var PocketPingClient = class {
|
|
604
607
|
constructor(config) {
|
|
@@ -606,6 +609,7 @@ var PocketPingClient = class {
|
|
|
606
609
|
this.ws = null;
|
|
607
610
|
this.isOpen = false;
|
|
608
611
|
this.listeners = /* @__PURE__ */ new Map();
|
|
612
|
+
this.customEventHandlers = /* @__PURE__ */ new Map();
|
|
609
613
|
this.reconnectAttempts = 0;
|
|
610
614
|
this.maxReconnectAttempts = 5;
|
|
611
615
|
this.reconnectTimeout = null;
|
|
@@ -617,6 +621,7 @@ var PocketPingClient = class {
|
|
|
617
621
|
async connect() {
|
|
618
622
|
const visitorId = this.getOrCreateVisitorId();
|
|
619
623
|
const storedSessionId = this.getStoredSessionId();
|
|
624
|
+
const storedIdentity = this.getStoredIdentity();
|
|
620
625
|
const response = await this.fetch("/connect", {
|
|
621
626
|
method: "POST",
|
|
622
627
|
body: JSON.stringify({
|
|
@@ -630,14 +635,17 @@ var PocketPingClient = class {
|
|
|
630
635
|
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
631
636
|
language: navigator.language,
|
|
632
637
|
screenResolution: `${window.screen.width}x${window.screen.height}`
|
|
633
|
-
}
|
|
638
|
+
},
|
|
639
|
+
// Include stored identity if available
|
|
640
|
+
identity: storedIdentity || void 0
|
|
634
641
|
})
|
|
635
642
|
});
|
|
636
643
|
this.session = {
|
|
637
644
|
sessionId: response.sessionId,
|
|
638
645
|
visitorId: response.visitorId,
|
|
639
646
|
operatorOnline: response.operatorOnline ?? false,
|
|
640
|
-
messages: response.messages ?? []
|
|
647
|
+
messages: response.messages ?? [],
|
|
648
|
+
identity: response.identity || storedIdentity || void 0
|
|
641
649
|
};
|
|
642
650
|
this.storeSessionId(response.sessionId);
|
|
643
651
|
this.connectWebSocket();
|
|
@@ -760,6 +768,69 @@ var PocketPingClient = class {
|
|
|
760
768
|
return this.fetch("/presence", { method: "GET" });
|
|
761
769
|
}
|
|
762
770
|
// ─────────────────────────────────────────────────────────────────
|
|
771
|
+
// User Identity
|
|
772
|
+
// ─────────────────────────────────────────────────────────────────
|
|
773
|
+
/**
|
|
774
|
+
* Identify the current user with metadata
|
|
775
|
+
* Call after user logs in or when user data becomes available
|
|
776
|
+
* @param identity - User identity data with required id field
|
|
777
|
+
* @example
|
|
778
|
+
* PocketPing.identify({
|
|
779
|
+
* id: 'user_123',
|
|
780
|
+
* email: 'john@example.com',
|
|
781
|
+
* name: 'John Doe',
|
|
782
|
+
* plan: 'pro',
|
|
783
|
+
* company: 'Acme Inc'
|
|
784
|
+
* })
|
|
785
|
+
*/
|
|
786
|
+
async identify(identity) {
|
|
787
|
+
if (!identity?.id) {
|
|
788
|
+
throw new Error("[PocketPing] identity.id is required");
|
|
789
|
+
}
|
|
790
|
+
this.storeIdentity(identity);
|
|
791
|
+
if (this.session) {
|
|
792
|
+
try {
|
|
793
|
+
await this.fetch("/identify", {
|
|
794
|
+
method: "POST",
|
|
795
|
+
body: JSON.stringify({
|
|
796
|
+
sessionId: this.session.sessionId,
|
|
797
|
+
identity
|
|
798
|
+
})
|
|
799
|
+
});
|
|
800
|
+
this.session.identity = identity;
|
|
801
|
+
this.emit("identify", identity);
|
|
802
|
+
} catch (err) {
|
|
803
|
+
console.error("[PocketPing] Failed to identify:", err);
|
|
804
|
+
throw err;
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
/**
|
|
809
|
+
* Reset the user identity and optionally start a new session
|
|
810
|
+
* Call on user logout to clear user data
|
|
811
|
+
* @param options - Optional settings: { newSession: boolean }
|
|
812
|
+
*/
|
|
813
|
+
async reset(options) {
|
|
814
|
+
this.clearIdentity();
|
|
815
|
+
if (this.session) {
|
|
816
|
+
this.session.identity = void 0;
|
|
817
|
+
}
|
|
818
|
+
if (options?.newSession) {
|
|
819
|
+
localStorage.removeItem("pocketping_session_id");
|
|
820
|
+
localStorage.removeItem("pocketping_visitor_id");
|
|
821
|
+
this.disconnect();
|
|
822
|
+
await this.connect();
|
|
823
|
+
}
|
|
824
|
+
this.emit("reset", null);
|
|
825
|
+
}
|
|
826
|
+
/**
|
|
827
|
+
* Get the current user identity
|
|
828
|
+
* @returns UserIdentity or null if not identified
|
|
829
|
+
*/
|
|
830
|
+
getIdentity() {
|
|
831
|
+
return this.session?.identity || this.getStoredIdentity();
|
|
832
|
+
}
|
|
833
|
+
// ─────────────────────────────────────────────────────────────────
|
|
763
834
|
// State
|
|
764
835
|
// ─────────────────────────────────────────────────────────────────
|
|
765
836
|
getSession() {
|
|
@@ -802,6 +873,67 @@ var PocketPingClient = class {
|
|
|
802
873
|
this.listeners.get(event)?.forEach((listener) => listener(data));
|
|
803
874
|
}
|
|
804
875
|
// ─────────────────────────────────────────────────────────────────
|
|
876
|
+
// Custom Events (bidirectional)
|
|
877
|
+
// ─────────────────────────────────────────────────────────────────
|
|
878
|
+
/**
|
|
879
|
+
* Trigger a custom event to the backend
|
|
880
|
+
* @param eventName - The name of the event (e.g., 'clicked_pricing', 'viewed_demo')
|
|
881
|
+
* @param data - Optional payload to send with the event
|
|
882
|
+
* @example
|
|
883
|
+
* PocketPing.trigger('clicked_cta', { button: 'signup', page: '/pricing' })
|
|
884
|
+
*/
|
|
885
|
+
trigger(eventName, data) {
|
|
886
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
887
|
+
console.warn("[PocketPing] Cannot trigger event: WebSocket not connected");
|
|
888
|
+
return;
|
|
889
|
+
}
|
|
890
|
+
const event = {
|
|
891
|
+
name: eventName,
|
|
892
|
+
data,
|
|
893
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
894
|
+
};
|
|
895
|
+
this.ws.send(JSON.stringify({
|
|
896
|
+
type: "event",
|
|
897
|
+
data: event
|
|
898
|
+
}));
|
|
899
|
+
this.emit(`event:${eventName}`, event);
|
|
900
|
+
}
|
|
901
|
+
/**
|
|
902
|
+
* Subscribe to custom events from the backend
|
|
903
|
+
* @param eventName - The name of the event to listen for (e.g., 'show_offer', 'open_chat')
|
|
904
|
+
* @param handler - Callback function when event is received
|
|
905
|
+
* @returns Unsubscribe function
|
|
906
|
+
* @example
|
|
907
|
+
* const unsubscribe = PocketPing.onEvent('show_offer', (data) => {
|
|
908
|
+
* showPopup(data.message)
|
|
909
|
+
* })
|
|
910
|
+
*/
|
|
911
|
+
onEvent(eventName, handler) {
|
|
912
|
+
if (!this.customEventHandlers.has(eventName)) {
|
|
913
|
+
this.customEventHandlers.set(eventName, /* @__PURE__ */ new Set());
|
|
914
|
+
}
|
|
915
|
+
this.customEventHandlers.get(eventName).add(handler);
|
|
916
|
+
return () => {
|
|
917
|
+
this.customEventHandlers.get(eventName)?.delete(handler);
|
|
918
|
+
};
|
|
919
|
+
}
|
|
920
|
+
/**
|
|
921
|
+
* Unsubscribe from a custom event
|
|
922
|
+
* @param eventName - The name of the event
|
|
923
|
+
* @param handler - The handler to remove
|
|
924
|
+
*/
|
|
925
|
+
offEvent(eventName, handler) {
|
|
926
|
+
this.customEventHandlers.get(eventName)?.delete(handler);
|
|
927
|
+
}
|
|
928
|
+
emitCustomEvent(event) {
|
|
929
|
+
const handlers = this.customEventHandlers.get(event.name);
|
|
930
|
+
if (handlers) {
|
|
931
|
+
handlers.forEach((handler) => handler(event.data, event));
|
|
932
|
+
}
|
|
933
|
+
this.emit("event", event);
|
|
934
|
+
this.emit(`event:${event.name}`, event);
|
|
935
|
+
}
|
|
936
|
+
// ─────────────────────────────────────────────────────────────────
|
|
805
937
|
// WebSocket
|
|
806
938
|
// ─────────────────────────────────────────────────────────────────
|
|
807
939
|
connectWebSocket() {
|
|
@@ -899,6 +1031,40 @@ var PocketPingClient = class {
|
|
|
899
1031
|
}
|
|
900
1032
|
this.emit("read", readData);
|
|
901
1033
|
break;
|
|
1034
|
+
case "event":
|
|
1035
|
+
const customEvent = event.data;
|
|
1036
|
+
this.emitCustomEvent(customEvent);
|
|
1037
|
+
break;
|
|
1038
|
+
case "version_warning":
|
|
1039
|
+
const versionWarning = event.data;
|
|
1040
|
+
this.handleVersionWarning(versionWarning);
|
|
1041
|
+
break;
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
// ─────────────────────────────────────────────────────────────────
|
|
1045
|
+
// Version Management
|
|
1046
|
+
// ─────────────────────────────────────────────────────────────────
|
|
1047
|
+
handleVersionWarning(warning) {
|
|
1048
|
+
const prefix = "[PocketPing]";
|
|
1049
|
+
const upgradeHint = warning.upgradeUrl ? ` Upgrade: ${warning.upgradeUrl}` : " Update your widget to the latest version.";
|
|
1050
|
+
switch (warning.severity) {
|
|
1051
|
+
case "error":
|
|
1052
|
+
console.error(`${prefix} \u{1F6A8} VERSION ERROR: ${warning.message}${upgradeHint}`);
|
|
1053
|
+
console.error(`${prefix} Current: ${warning.currentVersion}, Required: ${warning.minVersion || "unknown"}`);
|
|
1054
|
+
break;
|
|
1055
|
+
case "warning":
|
|
1056
|
+
console.warn(`${prefix} \u26A0\uFE0F VERSION WARNING: ${warning.message}${upgradeHint}`);
|
|
1057
|
+
console.warn(`${prefix} Current: ${warning.currentVersion}, Latest: ${warning.latestVersion || "unknown"}`);
|
|
1058
|
+
break;
|
|
1059
|
+
case "info":
|
|
1060
|
+
console.info(`${prefix} \u2139\uFE0F ${warning.message}`);
|
|
1061
|
+
break;
|
|
1062
|
+
}
|
|
1063
|
+
this.emit("versionWarning", warning);
|
|
1064
|
+
this.config.onVersionWarning?.(warning);
|
|
1065
|
+
if (!warning.canContinue) {
|
|
1066
|
+
console.error(`${prefix} Widget is incompatible with backend. Please update immediately.`);
|
|
1067
|
+
this.disconnect();
|
|
902
1068
|
}
|
|
903
1069
|
}
|
|
904
1070
|
scheduleReconnect() {
|
|
@@ -944,15 +1110,46 @@ var PocketPingClient = class {
|
|
|
944
1110
|
...options,
|
|
945
1111
|
headers: {
|
|
946
1112
|
"Content-Type": "application/json",
|
|
1113
|
+
"X-PocketPing-Version": VERSION,
|
|
947
1114
|
...options.headers
|
|
948
1115
|
}
|
|
949
1116
|
});
|
|
1117
|
+
this.checkVersionHeaders(response);
|
|
950
1118
|
if (!response.ok) {
|
|
951
1119
|
const error = await response.text();
|
|
952
1120
|
throw new Error(`PocketPing API error: ${response.status} ${error}`);
|
|
953
1121
|
}
|
|
954
1122
|
return response.json();
|
|
955
1123
|
}
|
|
1124
|
+
checkVersionHeaders(response) {
|
|
1125
|
+
const versionStatus = response.headers.get("X-PocketPing-Version-Status");
|
|
1126
|
+
const minVersion = response.headers.get("X-PocketPing-Min-Version");
|
|
1127
|
+
const latestVersion = response.headers.get("X-PocketPing-Latest-Version");
|
|
1128
|
+
const versionMessage = response.headers.get("X-PocketPing-Version-Message");
|
|
1129
|
+
if (!versionStatus || versionStatus === "ok") {
|
|
1130
|
+
return;
|
|
1131
|
+
}
|
|
1132
|
+
let severity = "info";
|
|
1133
|
+
let canContinue = true;
|
|
1134
|
+
if (versionStatus === "deprecated") {
|
|
1135
|
+
severity = "warning";
|
|
1136
|
+
} else if (versionStatus === "unsupported") {
|
|
1137
|
+
severity = "error";
|
|
1138
|
+
canContinue = false;
|
|
1139
|
+
} else if (versionStatus === "outdated") {
|
|
1140
|
+
severity = "info";
|
|
1141
|
+
}
|
|
1142
|
+
const warning = {
|
|
1143
|
+
severity,
|
|
1144
|
+
message: versionMessage || `Widget version ${VERSION} is ${versionStatus}`,
|
|
1145
|
+
currentVersion: VERSION,
|
|
1146
|
+
minVersion: minVersion || void 0,
|
|
1147
|
+
latestVersion: latestVersion || void 0,
|
|
1148
|
+
canContinue,
|
|
1149
|
+
upgradeUrl: "https://docs.pocketping.io/widget/installation"
|
|
1150
|
+
};
|
|
1151
|
+
this.handleVersionWarning(warning);
|
|
1152
|
+
}
|
|
956
1153
|
// ─────────────────────────────────────────────────────────────────
|
|
957
1154
|
// Storage
|
|
958
1155
|
// ─────────────────────────────────────────────────────────────────
|
|
@@ -971,6 +1168,20 @@ var PocketPingClient = class {
|
|
|
971
1168
|
storeSessionId(sessionId) {
|
|
972
1169
|
localStorage.setItem("pocketping_session_id", sessionId);
|
|
973
1170
|
}
|
|
1171
|
+
getStoredIdentity() {
|
|
1172
|
+
try {
|
|
1173
|
+
const stored = localStorage.getItem("pocketping_user_identity");
|
|
1174
|
+
return stored ? JSON.parse(stored) : null;
|
|
1175
|
+
} catch {
|
|
1176
|
+
return null;
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
storeIdentity(identity) {
|
|
1180
|
+
localStorage.setItem("pocketping_user_identity", JSON.stringify(identity));
|
|
1181
|
+
}
|
|
1182
|
+
clearIdentity() {
|
|
1183
|
+
localStorage.removeItem("pocketping_user_identity");
|
|
1184
|
+
}
|
|
974
1185
|
generateId() {
|
|
975
1186
|
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 11)}`;
|
|
976
1187
|
}
|
|
@@ -979,15 +1190,21 @@ var PocketPingClient = class {
|
|
|
979
1190
|
// src/index.ts
|
|
980
1191
|
var client = null;
|
|
981
1192
|
var container = null;
|
|
1193
|
+
var SAAS_API_BASE = "https://app.pocketping.io/api/widget";
|
|
982
1194
|
function init(config) {
|
|
983
1195
|
if (client) {
|
|
984
1196
|
console.warn("[PocketPing] Already initialized");
|
|
985
1197
|
return client;
|
|
986
1198
|
}
|
|
987
|
-
|
|
988
|
-
|
|
1199
|
+
let resolvedEndpoint = config.endpoint;
|
|
1200
|
+
if (!resolvedEndpoint && config.projectId) {
|
|
1201
|
+
resolvedEndpoint = `${SAAS_API_BASE}/${config.projectId}`;
|
|
1202
|
+
}
|
|
1203
|
+
if (!resolvedEndpoint) {
|
|
1204
|
+
throw new Error("[PocketPing] endpoint or projectId is required");
|
|
989
1205
|
}
|
|
990
|
-
|
|
1206
|
+
const resolvedConfig = { ...config, endpoint: resolvedEndpoint };
|
|
1207
|
+
client = new PocketPingClient(resolvedConfig);
|
|
991
1208
|
container = document.createElement("div");
|
|
992
1209
|
container.id = "pocketping-container";
|
|
993
1210
|
document.body.appendChild(container);
|
|
@@ -1023,23 +1240,77 @@ function sendMessage(content) {
|
|
|
1023
1240
|
}
|
|
1024
1241
|
return client.sendMessage(content);
|
|
1025
1242
|
}
|
|
1243
|
+
function trigger(eventName, data) {
|
|
1244
|
+
if (!client) {
|
|
1245
|
+
console.warn("[PocketPing] Not initialized, cannot trigger event");
|
|
1246
|
+
return;
|
|
1247
|
+
}
|
|
1248
|
+
client.trigger(eventName, data);
|
|
1249
|
+
}
|
|
1250
|
+
function onEvent(eventName, handler) {
|
|
1251
|
+
if (!client) {
|
|
1252
|
+
console.warn("[PocketPing] Not initialized, cannot subscribe to event");
|
|
1253
|
+
return () => {
|
|
1254
|
+
};
|
|
1255
|
+
}
|
|
1256
|
+
return client.onEvent(eventName, handler);
|
|
1257
|
+
}
|
|
1258
|
+
function offEvent(eventName, handler) {
|
|
1259
|
+
client?.offEvent(eventName, handler);
|
|
1260
|
+
}
|
|
1261
|
+
async function identify(identity) {
|
|
1262
|
+
if (!client) {
|
|
1263
|
+
throw new Error("[PocketPing] Not initialized");
|
|
1264
|
+
}
|
|
1265
|
+
return client.identify(identity);
|
|
1266
|
+
}
|
|
1267
|
+
async function reset(options) {
|
|
1268
|
+
if (!client) {
|
|
1269
|
+
console.warn("[PocketPing] Not initialized");
|
|
1270
|
+
return;
|
|
1271
|
+
}
|
|
1272
|
+
return client.reset(options);
|
|
1273
|
+
}
|
|
1274
|
+
function getIdentity() {
|
|
1275
|
+
return client?.getIdentity() || null;
|
|
1276
|
+
}
|
|
1277
|
+
function on(eventName, handler) {
|
|
1278
|
+
if (!client) {
|
|
1279
|
+
console.warn("[PocketPing] Not initialized, cannot subscribe to event");
|
|
1280
|
+
return () => {
|
|
1281
|
+
};
|
|
1282
|
+
}
|
|
1283
|
+
return client.on(eventName, handler);
|
|
1284
|
+
}
|
|
1026
1285
|
if (typeof document !== "undefined") {
|
|
1027
1286
|
const script = document.currentScript;
|
|
1028
|
-
if (script
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1287
|
+
if (script) {
|
|
1288
|
+
const projectId = script.dataset.key;
|
|
1289
|
+
const endpoint = script.dataset.endpoint;
|
|
1290
|
+
if (projectId || endpoint) {
|
|
1291
|
+
init({
|
|
1292
|
+
projectId,
|
|
1293
|
+
endpoint,
|
|
1294
|
+
theme: script.dataset.theme || "auto",
|
|
1295
|
+
position: script.dataset.position || "bottom-right"
|
|
1296
|
+
});
|
|
1297
|
+
}
|
|
1034
1298
|
}
|
|
1035
1299
|
}
|
|
1036
|
-
var index_default = { init, destroy, open, close, toggle, sendMessage };
|
|
1300
|
+
var index_default = { init, destroy, open, close, toggle, sendMessage, trigger, onEvent, offEvent, on, identify, reset, getIdentity };
|
|
1037
1301
|
export {
|
|
1038
1302
|
close,
|
|
1039
1303
|
index_default as default,
|
|
1040
1304
|
destroy,
|
|
1305
|
+
getIdentity,
|
|
1306
|
+
identify,
|
|
1041
1307
|
init,
|
|
1308
|
+
offEvent,
|
|
1309
|
+
on,
|
|
1310
|
+
onEvent,
|
|
1042
1311
|
open,
|
|
1312
|
+
reset,
|
|
1043
1313
|
sendMessage,
|
|
1044
|
-
toggle
|
|
1314
|
+
toggle,
|
|
1315
|
+
trigger
|
|
1045
1316
|
};
|