@wireai/activation 0.10.0 → 0.11.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 (42) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/README.md +33 -4
  3. package/dist/analytics/index.d.mts +21 -3
  4. package/dist/analytics/index.d.ts +21 -3
  5. package/dist/analytics/index.js +94 -61
  6. package/dist/analytics/index.js.map +1 -1
  7. package/dist/analytics/index.mjs +91 -62
  8. package/dist/analytics/index.mjs.map +1 -1
  9. package/dist/{currentSession-DdDkprpM.d.mts → currentSession-C0_odnIW.d.mts} +101 -1
  10. package/dist/{currentSession-D0Vq7_VE.d.ts → currentSession-DdnUq2HQ.d.ts} +101 -1
  11. package/dist/index.d.mts +3 -49
  12. package/dist/index.d.ts +3 -49
  13. package/dist/index.js +50 -41
  14. package/dist/index.js.map +1 -1
  15. package/dist/index.mjs +47 -42
  16. package/dist/index.mjs.map +1 -1
  17. package/dist/questionnaire/index.js +10 -2
  18. package/dist/questionnaire/index.js.map +1 -1
  19. package/dist/questionnaire/index.mjs +10 -2
  20. package/dist/questionnaire/index.mjs.map +1 -1
  21. package/dist/reviews/index.d.mts +1 -1
  22. package/dist/reviews/index.d.ts +1 -1
  23. package/dist/reviews/index.js +24 -9
  24. package/dist/reviews/index.js.map +1 -1
  25. package/dist/reviews/index.mjs +24 -9
  26. package/dist/reviews/index.mjs.map +1 -1
  27. package/dist/{transport-Bzb-bcB2.d.mts → transport-BGW9uXZJ.d.mts} +0 -15
  28. package/dist/{transport-B31G0Cib.d.ts → transport-jUJd5kxu.d.ts} +0 -15
  29. package/llms.txt +8 -0
  30. package/package.json +1 -1
  31. package/src/analytics/analyticsFacade.ts +72 -9
  32. package/src/analytics/eventQueue.ts +12 -11
  33. package/src/analytics/index.ts +9 -0
  34. package/src/analytics/reportClientEvent.ts +12 -2
  35. package/src/context/userContext.ts +55 -0
  36. package/src/identity/userIdentity.ts +10 -0
  37. package/src/index.ts +5 -1
  38. package/src/questionnaire/transport.ts +5 -1
  39. package/src/reviews/decision.ts +8 -1
  40. package/src/reviews/transport.ts +6 -8
  41. package/src/session-analytics/lifecycle.ts +6 -11
  42. package/src/session-analytics/useLifecycleEvents.ts +41 -27
@@ -398,13 +398,45 @@ var init_Constants = __esm({
398
398
  }
399
399
  });
400
400
 
401
- // src/reviews/transport.ts
402
- var reportAppEvent = (target, name2, options = {}) => {
403
- if (!(target == null ? void 0 : target.serverUrl) || !name2) return;
401
+ // src/analytics/reportClientEvent.ts
402
+ var makeSessionId = () => `wire_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
403
+ var buildEventsRequest = (target, events) => {
404
+ if (!(target == null ? void 0 : target.serverUrl) || events.length === 0) return null;
404
405
  try {
405
406
  const url = `${target.serverUrl.replace(/\/$/, "")}/v1/events`;
406
407
  const headers = { "Content-Type": "application/json" };
407
408
  if (target.apiKey) headers.Authorization = `Bearer ${target.apiKey}`;
409
+ return { url, init: { method: "POST", headers, body: JSON.stringify({ events }) } };
410
+ } catch {
411
+ return null;
412
+ }
413
+ };
414
+ var reportClientEvents = (target, events) => {
415
+ try {
416
+ const req = buildEventsRequest(target, events);
417
+ if (!req) return;
418
+ void fetch(req.url, req.init).catch(() => {
419
+ });
420
+ } catch {
421
+ }
422
+ };
423
+ var reportClientEvent = (target, event) => reportClientEvents(target, [event]);
424
+ var reportClientEventsAwait = async (target, events) => {
425
+ try {
426
+ const req = buildEventsRequest(target, events);
427
+ if (!req) return false;
428
+ const res = await fetch(req.url, req.init);
429
+ return Boolean(res && res.ok);
430
+ } catch {
431
+ return false;
432
+ }
433
+ };
434
+ var reportClientEventAwait = (target, event) => reportClientEventsAwait(target, [event]);
435
+
436
+ // src/reviews/transport.ts
437
+ var reportAppEvent = (target, name2, options = {}) => {
438
+ if (!(target == null ? void 0 : target.serverUrl) || !name2) return;
439
+ try {
408
440
  const event = {
409
441
  event_type: "app_event",
410
442
  question_key: name2
@@ -414,11 +446,9 @@ var reportAppEvent = (target, name2, options = {}) => {
414
446
  if (options.meta && Object.keys(options.meta).length > 0) {
415
447
  event.meta = JSON.stringify(options.meta);
416
448
  }
417
- void fetch(url, {
418
- method: "POST",
419
- headers,
420
- body: JSON.stringify({ events: [event] })
421
- }).catch(() => {
449
+ const req = buildEventsRequest(target, [event]);
450
+ if (!req) return;
451
+ void fetch(req.url, req.init).catch(() => {
422
452
  });
423
453
  } catch {
424
454
  }
@@ -495,41 +525,6 @@ var useScreenTracking = (navigationRef, options = {}) => {
495
525
  }, [navigationRef]);
496
526
  };
497
527
 
498
- // src/analytics/reportClientEvent.ts
499
- var makeSessionId = () => `wire_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
500
- var buildEventsRequest = (target, events) => {
501
- if (!(target == null ? void 0 : target.serverUrl) || events.length === 0) return null;
502
- try {
503
- const url = `${target.serverUrl.replace(/\/$/, "")}/v1/events`;
504
- const headers = { "Content-Type": "application/json" };
505
- if (target.apiKey) headers.Authorization = `Bearer ${target.apiKey}`;
506
- return { url, init: { method: "POST", headers, body: JSON.stringify({ events }) } };
507
- } catch {
508
- return null;
509
- }
510
- };
511
- var reportClientEvents = (target, events) => {
512
- try {
513
- const req = buildEventsRequest(target, events);
514
- if (!req) return;
515
- void fetch(req.url, req.init).catch(() => {
516
- });
517
- } catch {
518
- }
519
- };
520
- var reportClientEvent = (target, event) => reportClientEvents(target, [event]);
521
- var reportClientEventsAwait = async (target, events) => {
522
- try {
523
- const req = buildEventsRequest(target, events);
524
- if (!req) return false;
525
- const res = await fetch(req.url, req.init);
526
- return Boolean(res && res.ok);
527
- } catch {
528
- return false;
529
- }
530
- };
531
- var reportClientEventAwait = (target, event) => reportClientEventsAwait(target, [event]);
532
-
533
528
  // src/analytics/analyticsEvent.ts
534
529
  var WIRE_ONBOARDING_EVENTS = {
535
530
  started: "wire_onboarding_started",
@@ -820,6 +815,7 @@ var createEventQueue = (options) => {
820
815
  var _a3;
821
816
  const env = resolveEnvelope();
822
817
  const stamped = { ...event };
818
+ if (stamped.ts === void 0) stamped.ts = Date.now();
823
819
  if (!env) return stamped;
824
820
  if (!stamped.device && env.device) stamped.device = env.device;
825
821
  if (!stamped.session_id && env.sessionId) stamped.session_id = env.sessionId;
@@ -875,19 +871,12 @@ var createEventQueue = (options) => {
875
871
  }
876
872
  })();
877
873
  const postBatch = async (events) => {
878
- if (!(target == null ? void 0 : target.serverUrl) || events.length === 0) return false;
874
+ const req = buildEventsRequest(target, events);
875
+ if (!req) return false;
879
876
  const controller = typeof AbortController !== "undefined" ? new AbortController() : void 0;
880
877
  const timer = setTimeout(() => controller == null ? void 0 : controller.abort(), 15e3);
881
878
  try {
882
- const url = `${target.serverUrl.replace(/\/$/, "")}/v1/events`;
883
- const headers = { "Content-Type": "application/json" };
884
- if (target.apiKey) headers.Authorization = `Bearer ${target.apiKey}`;
885
- const res = await fetch(url, {
886
- method: "POST",
887
- headers,
888
- body: JSON.stringify({ events }),
889
- signal: controller == null ? void 0 : controller.signal
890
- });
879
+ const res = await fetch(req.url, { ...req.init, signal: controller == null ? void 0 : controller.signal });
891
880
  return !!(res && res.ok);
892
881
  } catch {
893
882
  return false;
@@ -989,6 +978,7 @@ var sanitizeUserId = (raw) => {
989
978
  if (!trimmed) return void 0;
990
979
  return trimmed.length > USER_ID_MAX_LENGTH ? trimmed.slice(0, USER_ID_MAX_LENGTH) : trimmed;
991
980
  };
981
+ var looksLikeEmail = (value) => typeof value === "string" && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value.trim());
992
982
 
993
983
  // src/context/userContext.ts
994
984
  var EXTRA_KEY_PREFIX = "custom.";
@@ -1023,6 +1013,21 @@ var namespaceExtra = (extra) => {
1023
1013
  }
1024
1014
  return out;
1025
1015
  };
1016
+ var analyticsUserIdStorageKey = (appId) => `wireai:analytics:userId:${appId != null ? appId : "default"}`;
1017
+ var clearPiiFromContext = (ctx = {}) => {
1018
+ const rest = {};
1019
+ if (typeof ctx.appVersion === "string") rest.appVersion = ctx.appVersion;
1020
+ if (typeof ctx.deviceKey === "string") rest.deviceKey = ctx.deviceKey;
1021
+ return rest;
1022
+ };
1023
+ var clearUserContext = async (opts = {}) => {
1024
+ const storage = opts.storage;
1025
+ if (!storage) return;
1026
+ try {
1027
+ await storage.removeItem(analyticsUserIdStorageKey(opts.appId));
1028
+ } catch {
1029
+ }
1030
+ };
1026
1031
  var resolveUserContext = (ctx = {}, opts = {}) => {
1027
1032
  var _a2;
1028
1033
  const result = {};
@@ -1063,8 +1068,13 @@ var mintDeviceId = () => {
1063
1068
  };
1064
1069
 
1065
1070
  // src/analytics/analyticsFacade.ts
1071
+ var warnInDev = (message) => {
1072
+ if (typeof __DEV__ !== "undefined" && __DEV__ && typeof console !== "undefined" && console.warn) {
1073
+ console.warn(message);
1074
+ }
1075
+ };
1066
1076
  var createAnalytics = (config, options = {}) => {
1067
- var _a2, _b, _c, _d, _e;
1077
+ var _a2, _b, _c, _d;
1068
1078
  const instanceSessionId = (_a2 = config.sessionId) != null ? _a2 : makeSessionId();
1069
1079
  const resolveSessionId = () => {
1070
1080
  var _a3, _b2;
@@ -1101,7 +1111,7 @@ var createAnalytics = (config, options = {}) => {
1101
1111
  ...options
1102
1112
  });
1103
1113
  let boundUserId = sanitizeUserId((_d = config.userContext) == null ? void 0 : _d.userId);
1104
- const storageKey = `wireai:analytics:userId:${(_e = config.appId) != null ? _e : "default"}`;
1114
+ const storageKey = analyticsUserIdStorageKey(config.appId);
1105
1115
  if (config.storage) {
1106
1116
  void config.storage.getItem(storageKey).then((saved) => {
1107
1117
  if (saved && !boundUserId) boundUserId = saved;
@@ -1120,6 +1130,13 @@ var createAnalytics = (config, options = {}) => {
1120
1130
  }
1121
1131
  if (boundUserId && !event.user_id) event.user_id = boundUserId;
1122
1132
  };
1133
+ const guardUserId = (clean) => {
1134
+ if (config.allowEmailAsUserId || !looksLikeEmail(clean)) return clean;
1135
+ warnInDev(
1136
+ "[wireai] identify() was called with an email-shaped id. A raw email must NOT be the opaque user_id (PII leak) \u2014 pass it as userContext.userEmail instead. Binding was skipped. Set allowEmailAsUserId:true on createAnalytics if your user id genuinely is an email."
1137
+ );
1138
+ return void 0;
1139
+ };
1123
1140
  const setUserContext = (partial) => {
1124
1141
  var _a3, _b2;
1125
1142
  if (!partial || typeof partial !== "object") return;
@@ -1128,11 +1145,20 @@ var createAnalytics = (config, options = {}) => {
1128
1145
  if (mergedExtra) userContext.extra = mergedExtra;
1129
1146
  const uid = sanitizeUserId(partial.userId);
1130
1147
  if (uid) {
1131
- boundUserId = uid;
1132
- if (config.storage) void config.storage.setItem(storageKey, uid).catch(() => {
1133
- });
1148
+ const bindable = guardUserId(uid);
1149
+ if (bindable) {
1150
+ boundUserId = bindable;
1151
+ if (config.storage) void config.storage.setItem(storageKey, bindable).catch(() => {
1152
+ });
1153
+ }
1134
1154
  }
1135
1155
  };
1156
+ const reset = () => {
1157
+ boundUserId = void 0;
1158
+ userContext = clearPiiFromContext(userContext);
1159
+ if (config.storage) void config.storage.removeItem(storageKey).catch(() => {
1160
+ });
1161
+ };
1136
1162
  const track = (event, props) => {
1137
1163
  if (!event) return;
1138
1164
  const clientEvent = {
@@ -1159,9 +1185,11 @@ var createAnalytics = (config, options = {}) => {
1159
1185
  const identify = (userId, traits) => {
1160
1186
  const clean = sanitizeUserId(userId);
1161
1187
  if (!clean) return;
1162
- boundUserId = clean;
1188
+ const bindable = guardUserId(clean);
1189
+ if (!bindable) return;
1190
+ boundUserId = bindable;
1163
1191
  if (config.storage) {
1164
- void config.storage.setItem(storageKey, clean).catch(() => {
1192
+ void config.storage.setItem(storageKey, bindable).catch(() => {
1165
1193
  });
1166
1194
  }
1167
1195
  const clientEvent = {
@@ -1169,7 +1197,7 @@ var createAnalytics = (config, options = {}) => {
1169
1197
  // Reuse the LIVE per-open session id (see `resolveSessionId`) so the server binds identity to
1170
1198
  // the session it already saw instead of back-filling a phantom `session_started`.
1171
1199
  session_id: resolveSessionId(),
1172
- user_id: clean
1200
+ user_id: bindable
1173
1201
  };
1174
1202
  if (traits && Object.keys(traits).length > 0) clientEvent.meta = JSON.stringify(traits);
1175
1203
  applyContext(clientEvent);
@@ -1180,6 +1208,7 @@ var createAnalytics = (config, options = {}) => {
1180
1208
  screen,
1181
1209
  identify,
1182
1210
  setUserContext,
1211
+ reset,
1183
1212
  flush: queue.flush,
1184
1213
  notifyOnline: queue.notifyOnline,
1185
1214
  size: queue.size
@@ -1196,6 +1225,6 @@ var useAnalytics = (config, options = {}) => {
1196
1225
  return ref.current;
1197
1226
  };
1198
1227
 
1199
- export { WIRE_ONBOARDING_EVENTS, buildContextEnvelope, createAnalytics, createEventQueue, createScreenTracker, getActiveRouteName, getCurrentSessionId, makeSessionId, reportAppEvent, reportClientEvent, reportClientEventAwait, reportClientEvents, reportClientEventsAwait, resetCurrentSessionId, screenTrackingHandler, setCurrentSessionId, toAnalyticsEvent, useAnalytics, useScreenTracking };
1228
+ export { WIRE_ONBOARDING_EVENTS, analyticsUserIdStorageKey, buildContextEnvelope, clearPiiFromContext, clearUserContext, createAnalytics, createEventQueue, createScreenTracker, getActiveRouteName, getCurrentSessionId, looksLikeEmail, makeSessionId, reportAppEvent, reportClientEvent, reportClientEventAwait, reportClientEvents, reportClientEventsAwait, resetCurrentSessionId, screenTrackingHandler, setCurrentSessionId, toAnalyticsEvent, useAnalytics, useScreenTracking };
1200
1229
  //# sourceMappingURL=index.mjs.map
1201
1230
  //# sourceMappingURL=index.mjs.map