@wireai/activation 0.10.0 → 0.12.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/AGENTS.md +51 -0
- package/CHANGELOG.md +111 -1
- package/INTEGRATION_PROMPT.md +13 -1
- package/README.md +106 -4
- package/dist/analytics/index.d.mts +35 -6
- package/dist/analytics/index.d.ts +35 -6
- package/dist/analytics/index.js +222 -94
- package/dist/analytics/index.js.map +1 -1
- package/dist/analytics/index.mjs +214 -95
- package/dist/analytics/index.mjs.map +1 -1
- package/dist/{currentSession-D0Vq7_VE.d.ts → currentSession-D6RiVtc8.d.ts} +187 -29
- package/dist/{currentSession-DdDkprpM.d.mts → currentSession-DsSDHqor.d.mts} +187 -29
- package/dist/index.d.mts +236 -82
- package/dist/index.d.ts +236 -82
- package/dist/index.js +330 -60
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +314 -61
- package/dist/index.mjs.map +1 -1
- package/dist/questionnaire/index.d.mts +1 -1
- package/dist/questionnaire/index.d.ts +1 -1
- package/dist/questionnaire/index.js +59 -8
- package/dist/questionnaire/index.js.map +1 -1
- package/dist/questionnaire/index.mjs +59 -8
- package/dist/questionnaire/index.mjs.map +1 -1
- package/dist/reviews/index.d.mts +2 -2
- package/dist/reviews/index.d.ts +2 -2
- package/dist/reviews/index.js +97 -17
- package/dist/reviews/index.js.map +1 -1
- package/dist/reviews/index.mjs +97 -17
- package/dist/reviews/index.mjs.map +1 -1
- package/dist/{transport-Bzb-bcB2.d.mts → transport-CF_eHwzC.d.mts} +15 -16
- package/dist/{transport-B31G0Cib.d.ts → transport-DsRe4epC.d.ts} +15 -16
- package/llms.txt +9 -0
- package/package.json +1 -1
- package/src/activation/useWireActivation.ts +12 -1
- package/src/activation/wireActivation.ts +36 -24
- package/src/analytics/analyticsFacade.ts +113 -29
- package/src/analytics/currentSession.ts +83 -0
- package/src/analytics/eventQueue.ts +20 -11
- package/src/analytics/index.ts +29 -1
- package/src/analytics/reportClientEvent.ts +50 -2
- package/src/analytics/screenTracking.ts +6 -1
- package/src/analytics/useAnalytics.ts +22 -1
- package/src/context/deviceId.ts +109 -0
- package/src/context/userContext.ts +73 -0
- package/src/identity/userIdentity.ts +10 -0
- package/src/index.ts +50 -2
- package/src/questionnaire/runtime.ts +12 -2
- package/src/questionnaire/transport.ts +5 -1
- package/src/questionnaire/useQuestionnaireGate.ts +9 -7
- package/src/revenuecat/index.ts +55 -0
- package/src/revenuecat/purchaseEvents.ts +167 -0
- package/src/revenuecat/revenueCatBridge.ts +221 -0
- package/src/revenuecat/types.ts +95 -0
- package/src/reviews/decision.ts +8 -1
- package/src/reviews/runtime.ts +92 -1
- package/src/reviews/transport.ts +27 -10
- package/src/reviews/useReviewGate.ts +12 -7
- package/src/session-analytics/lifecycle.ts +15 -13
- package/src/session-analytics/reportSessionStart.ts +15 -4
- package/src/session-analytics/useLifecycleEvents.ts +68 -28
package/dist/analytics/index.js
CHANGED
|
@@ -400,27 +400,113 @@ var init_Constants = __esm({
|
|
|
400
400
|
}
|
|
401
401
|
});
|
|
402
402
|
|
|
403
|
-
// src/
|
|
404
|
-
var
|
|
405
|
-
|
|
403
|
+
// src/analytics/reportClientEvent.ts
|
|
404
|
+
var makeSessionId = () => `wire_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
405
|
+
var buildEventsRequest = (target, events) => {
|
|
406
|
+
if (!(target == null ? void 0 : target.serverUrl) || events.length === 0) return null;
|
|
406
407
|
try {
|
|
407
408
|
const url = `${target.serverUrl.replace(/\/$/, "")}/v1/events`;
|
|
408
409
|
const headers = { "Content-Type": "application/json" };
|
|
409
410
|
if (target.apiKey) headers.Authorization = `Bearer ${target.apiKey}`;
|
|
411
|
+
return { url, init: { method: "POST", headers, body: JSON.stringify({ events }) } };
|
|
412
|
+
} catch {
|
|
413
|
+
return null;
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
var warnOnSkippedEvents = (res) => {
|
|
417
|
+
try {
|
|
418
|
+
const json = res == null ? void 0 : res.json;
|
|
419
|
+
if (typeof json !== "function") return;
|
|
420
|
+
void Promise.resolve(json.call(res)).then((body) => {
|
|
421
|
+
const skipped = body == null ? void 0 : body.skipped;
|
|
422
|
+
if (typeof skipped !== "number" || skipped <= 0) return;
|
|
423
|
+
if (typeof __DEV__ !== "undefined" && __DEV__ && typeof console !== "undefined" && console.warn) {
|
|
424
|
+
console.warn(
|
|
425
|
+
`[wireai] the server ACCEPTED the /v1/events POST but DISCARDED ${skipped} event(s) (skipped in the response body) \u2014 they are gone, not retried. The usual cause is an event with a missing or empty session_id.`
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
}).catch(() => {
|
|
429
|
+
});
|
|
430
|
+
} catch {
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
var reportClientEvents = (target, events) => {
|
|
434
|
+
try {
|
|
435
|
+
const req = buildEventsRequest(target, events);
|
|
436
|
+
if (!req) return;
|
|
437
|
+
void fetch(req.url, req.init).catch(() => {
|
|
438
|
+
});
|
|
439
|
+
} catch {
|
|
440
|
+
}
|
|
441
|
+
};
|
|
442
|
+
var reportClientEvent = (target, event) => reportClientEvents(target, [event]);
|
|
443
|
+
var reportClientEventsAwait = async (target, events) => {
|
|
444
|
+
try {
|
|
445
|
+
const req = buildEventsRequest(target, events);
|
|
446
|
+
if (!req) return false;
|
|
447
|
+
const res = await fetch(req.url, req.init);
|
|
448
|
+
return Boolean(res && res.ok);
|
|
449
|
+
} catch {
|
|
450
|
+
return false;
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
var reportClientEventAwait = (target, event) => reportClientEventsAwait(target, [event]);
|
|
454
|
+
|
|
455
|
+
// src/analytics/currentSession.ts
|
|
456
|
+
var CURRENT_SESSION_ID_SLOT = /* @__PURE__ */ Symbol.for(
|
|
457
|
+
"@wireai/activation:currentSessionId"
|
|
458
|
+
);
|
|
459
|
+
var globalSlot = globalThis;
|
|
460
|
+
var setCurrentSessionId = (id) => {
|
|
461
|
+
if (typeof id === "string" && id.length > 0) {
|
|
462
|
+
globalSlot[CURRENT_SESSION_ID_SLOT] = id;
|
|
463
|
+
}
|
|
464
|
+
};
|
|
465
|
+
var getCurrentSessionId = () => globalSlot[CURRENT_SESSION_ID_SLOT];
|
|
466
|
+
var resetCurrentSessionId = () => {
|
|
467
|
+
globalSlot[CURRENT_SESSION_ID_SLOT] = void 0;
|
|
468
|
+
};
|
|
469
|
+
var warnInDev = (message) => {
|
|
470
|
+
if (typeof __DEV__ !== "undefined" && __DEV__ && typeof console !== "undefined" && console.warn) {
|
|
471
|
+
console.warn(message);
|
|
472
|
+
return true;
|
|
473
|
+
}
|
|
474
|
+
return false;
|
|
475
|
+
};
|
|
476
|
+
var MINT_WARNING = "[wireai] No app-open session was registered, so a session id was minted for this event (the server drops an event that has no session_id, and still answers 200). Mount useLifecycleEvents at your app root so events correlate to a real app-open.";
|
|
477
|
+
var MINT_WARNED_SLOT = /* @__PURE__ */ Symbol.for(
|
|
478
|
+
"@wireai/activation:currentSessionIdMintWarned"
|
|
479
|
+
);
|
|
480
|
+
var warnSlot = globalThis;
|
|
481
|
+
var ensureCurrentSessionId = () => {
|
|
482
|
+
const existing = globalSlot[CURRENT_SESSION_ID_SLOT];
|
|
483
|
+
if (typeof existing === "string" && existing.length > 0) return existing;
|
|
484
|
+
const minted = makeSessionId();
|
|
485
|
+
globalSlot[CURRENT_SESSION_ID_SLOT] = minted;
|
|
486
|
+
if (!warnSlot[MINT_WARNED_SLOT] && warnInDev(MINT_WARNING)) {
|
|
487
|
+
warnSlot[MINT_WARNED_SLOT] = true;
|
|
488
|
+
}
|
|
489
|
+
return minted;
|
|
490
|
+
};
|
|
491
|
+
|
|
492
|
+
// src/reviews/transport.ts
|
|
493
|
+
var reportAppEvent = (target, name2, options = {}) => {
|
|
494
|
+
var _a2;
|
|
495
|
+
if (!(target == null ? void 0 : target.serverUrl) || !name2) return;
|
|
496
|
+
try {
|
|
497
|
+
const supplied = (_a2 = options.sessionId) != null ? _a2 : "";
|
|
410
498
|
const event = {
|
|
411
499
|
event_type: "app_event",
|
|
412
|
-
question_key: name2
|
|
500
|
+
question_key: name2,
|
|
501
|
+
session_id: supplied.trim().length > 0 ? supplied : ensureCurrentSessionId()
|
|
413
502
|
};
|
|
414
|
-
if (options.sessionId) event.session_id = options.sessionId;
|
|
415
503
|
if (options.deviceKey) event.user_context = { device_key: options.deviceKey };
|
|
416
504
|
if (options.meta && Object.keys(options.meta).length > 0) {
|
|
417
505
|
event.meta = JSON.stringify(options.meta);
|
|
418
506
|
}
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
body: JSON.stringify({ events: [event] })
|
|
423
|
-
}).catch(() => {
|
|
507
|
+
const req = buildEventsRequest(target, [event]);
|
|
508
|
+
if (!req) return;
|
|
509
|
+
void fetch(req.url, req.init).catch(() => {
|
|
424
510
|
});
|
|
425
511
|
} catch {
|
|
426
512
|
}
|
|
@@ -497,41 +583,6 @@ var useScreenTracking = (navigationRef, options = {}) => {
|
|
|
497
583
|
}, [navigationRef]);
|
|
498
584
|
};
|
|
499
585
|
|
|
500
|
-
// src/analytics/reportClientEvent.ts
|
|
501
|
-
var makeSessionId = () => `wire_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
502
|
-
var buildEventsRequest = (target, events) => {
|
|
503
|
-
if (!(target == null ? void 0 : target.serverUrl) || events.length === 0) return null;
|
|
504
|
-
try {
|
|
505
|
-
const url = `${target.serverUrl.replace(/\/$/, "")}/v1/events`;
|
|
506
|
-
const headers = { "Content-Type": "application/json" };
|
|
507
|
-
if (target.apiKey) headers.Authorization = `Bearer ${target.apiKey}`;
|
|
508
|
-
return { url, init: { method: "POST", headers, body: JSON.stringify({ events }) } };
|
|
509
|
-
} catch {
|
|
510
|
-
return null;
|
|
511
|
-
}
|
|
512
|
-
};
|
|
513
|
-
var reportClientEvents = (target, events) => {
|
|
514
|
-
try {
|
|
515
|
-
const req = buildEventsRequest(target, events);
|
|
516
|
-
if (!req) return;
|
|
517
|
-
void fetch(req.url, req.init).catch(() => {
|
|
518
|
-
});
|
|
519
|
-
} catch {
|
|
520
|
-
}
|
|
521
|
-
};
|
|
522
|
-
var reportClientEvent = (target, event) => reportClientEvents(target, [event]);
|
|
523
|
-
var reportClientEventsAwait = async (target, events) => {
|
|
524
|
-
try {
|
|
525
|
-
const req = buildEventsRequest(target, events);
|
|
526
|
-
if (!req) return false;
|
|
527
|
-
const res = await fetch(req.url, req.init);
|
|
528
|
-
return Boolean(res && res.ok);
|
|
529
|
-
} catch {
|
|
530
|
-
return false;
|
|
531
|
-
}
|
|
532
|
-
};
|
|
533
|
-
var reportClientEventAwait = (target, event) => reportClientEventsAwait(target, [event]);
|
|
534
|
-
|
|
535
586
|
// src/analytics/analyticsEvent.ts
|
|
536
587
|
var WIRE_ONBOARDING_EVENTS = {
|
|
537
588
|
started: "wire_onboarding_started",
|
|
@@ -822,6 +873,7 @@ var createEventQueue = (options) => {
|
|
|
822
873
|
var _a3;
|
|
823
874
|
const env = resolveEnvelope();
|
|
824
875
|
const stamped = { ...event };
|
|
876
|
+
if (stamped.ts === void 0) stamped.ts = Date.now();
|
|
825
877
|
if (!env) return stamped;
|
|
826
878
|
if (!stamped.device && env.device) stamped.device = env.device;
|
|
827
879
|
if (!stamped.session_id && env.sessionId) stamped.session_id = env.sessionId;
|
|
@@ -877,19 +929,13 @@ var createEventQueue = (options) => {
|
|
|
877
929
|
}
|
|
878
930
|
})();
|
|
879
931
|
const postBatch = async (events) => {
|
|
880
|
-
|
|
932
|
+
const req = buildEventsRequest(target, events);
|
|
933
|
+
if (!req) return false;
|
|
881
934
|
const controller = typeof AbortController !== "undefined" ? new AbortController() : void 0;
|
|
882
935
|
const timer = setTimeout(() => controller == null ? void 0 : controller.abort(), 15e3);
|
|
883
936
|
try {
|
|
884
|
-
const
|
|
885
|
-
|
|
886
|
-
if (target.apiKey) headers.Authorization = `Bearer ${target.apiKey}`;
|
|
887
|
-
const res = await fetch(url, {
|
|
888
|
-
method: "POST",
|
|
889
|
-
headers,
|
|
890
|
-
body: JSON.stringify({ events }),
|
|
891
|
-
signal: controller == null ? void 0 : controller.signal
|
|
892
|
-
});
|
|
937
|
+
const res = await fetch(req.url, { ...req.init, signal: controller == null ? void 0 : controller.signal });
|
|
938
|
+
warnOnSkippedEvents(res);
|
|
893
939
|
return !!(res && res.ok);
|
|
894
940
|
} catch {
|
|
895
941
|
return false;
|
|
@@ -968,21 +1014,6 @@ var createEventQueue = (options) => {
|
|
|
968
1014
|
return { enqueue, flush, notifyOnline, size };
|
|
969
1015
|
};
|
|
970
1016
|
|
|
971
|
-
// src/analytics/currentSession.ts
|
|
972
|
-
var CURRENT_SESSION_ID_SLOT = /* @__PURE__ */ Symbol.for(
|
|
973
|
-
"@wireai/activation:currentSessionId"
|
|
974
|
-
);
|
|
975
|
-
var globalSlot = globalThis;
|
|
976
|
-
var setCurrentSessionId = (id) => {
|
|
977
|
-
if (typeof id === "string" && id.length > 0) {
|
|
978
|
-
globalSlot[CURRENT_SESSION_ID_SLOT] = id;
|
|
979
|
-
}
|
|
980
|
-
};
|
|
981
|
-
var getCurrentSessionId = () => globalSlot[CURRENT_SESSION_ID_SLOT];
|
|
982
|
-
var resetCurrentSessionId = () => {
|
|
983
|
-
globalSlot[CURRENT_SESSION_ID_SLOT] = void 0;
|
|
984
|
-
};
|
|
985
|
-
|
|
986
1017
|
// src/identity/userIdentity.ts
|
|
987
1018
|
var USER_ID_MAX_LENGTH = 128;
|
|
988
1019
|
var sanitizeUserId = (raw) => {
|
|
@@ -991,6 +1022,7 @@ var sanitizeUserId = (raw) => {
|
|
|
991
1022
|
if (!trimmed) return void 0;
|
|
992
1023
|
return trimmed.length > USER_ID_MAX_LENGTH ? trimmed.slice(0, USER_ID_MAX_LENGTH) : trimmed;
|
|
993
1024
|
};
|
|
1025
|
+
var looksLikeEmail = (value) => typeof value === "string" && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value.trim());
|
|
994
1026
|
|
|
995
1027
|
// src/context/userContext.ts
|
|
996
1028
|
var EXTRA_KEY_PREFIX = "custom.";
|
|
@@ -1025,6 +1057,21 @@ var namespaceExtra = (extra) => {
|
|
|
1025
1057
|
}
|
|
1026
1058
|
return out;
|
|
1027
1059
|
};
|
|
1060
|
+
var analyticsUserIdStorageKey = (appId) => `wireai:analytics:userId:${appId != null ? appId : "default"}`;
|
|
1061
|
+
var clearPiiFromContext = (ctx = {}) => {
|
|
1062
|
+
const rest = {};
|
|
1063
|
+
if (typeof ctx.appVersion === "string") rest.appVersion = ctx.appVersion;
|
|
1064
|
+
if (typeof ctx.deviceKey === "string") rest.deviceKey = ctx.deviceKey;
|
|
1065
|
+
return rest;
|
|
1066
|
+
};
|
|
1067
|
+
var clearUserContext = async (opts = {}) => {
|
|
1068
|
+
const storage = opts.storage;
|
|
1069
|
+
if (!storage) return;
|
|
1070
|
+
try {
|
|
1071
|
+
await storage.removeItem(analyticsUserIdStorageKey(opts.appId));
|
|
1072
|
+
} catch {
|
|
1073
|
+
}
|
|
1074
|
+
};
|
|
1028
1075
|
var resolveUserContext = (ctx = {}, opts = {}) => {
|
|
1029
1076
|
var _a2;
|
|
1030
1077
|
const result = {};
|
|
@@ -1063,29 +1110,75 @@ var mintDeviceId = () => {
|
|
|
1063
1110
|
const time = Date.now().toString(36);
|
|
1064
1111
|
return `${AUTO_DEVICE_ID_PREFIX}${time}_${randomChunk()}${randomChunk()}`;
|
|
1065
1112
|
};
|
|
1113
|
+
var AUTO_DEVICE_KEY_SLOT = /* @__PURE__ */ Symbol.for("@wireai/activation:autoDeviceKeys");
|
|
1114
|
+
var deviceKeyGlobal = globalThis;
|
|
1115
|
+
var autoDeviceKeyRegistry = () => {
|
|
1116
|
+
const existing = deviceKeyGlobal[AUTO_DEVICE_KEY_SLOT];
|
|
1117
|
+
if (existing) return existing;
|
|
1118
|
+
const created = { keys: /* @__PURE__ */ new Map(), hydrating: /* @__PURE__ */ new Set() };
|
|
1119
|
+
deviceKeyGlobal[AUTO_DEVICE_KEY_SLOT] = created;
|
|
1120
|
+
return created;
|
|
1121
|
+
};
|
|
1122
|
+
var resolveAutoDeviceKey = (opts = {}) => {
|
|
1123
|
+
var _a2, _b;
|
|
1124
|
+
const registry = autoDeviceKeyRegistry();
|
|
1125
|
+
const appId = (_a2 = opts.appId) != null ? _a2 : "default";
|
|
1126
|
+
let id = registry.keys.get(appId);
|
|
1127
|
+
if (!id) {
|
|
1128
|
+
id = mintDeviceId();
|
|
1129
|
+
registry.keys.set(appId, id);
|
|
1130
|
+
}
|
|
1131
|
+
const storage = opts.storage;
|
|
1132
|
+
if (storage && !registry.hydrating.has(appId)) {
|
|
1133
|
+
registry.hydrating.add(appId);
|
|
1134
|
+
const slot = deviceIdStorageKey(appId);
|
|
1135
|
+
const minted = id;
|
|
1136
|
+
try {
|
|
1137
|
+
void Promise.resolve(storage.getItem(slot)).then((saved) => {
|
|
1138
|
+
const persisted = typeof saved === "string" && saved.trim() ? saved.trim() : void 0;
|
|
1139
|
+
if (persisted) registry.keys.set(appId, persisted);
|
|
1140
|
+
else void Promise.resolve(storage.setItem(slot, minted)).catch(() => {
|
|
1141
|
+
});
|
|
1142
|
+
}).catch(() => {
|
|
1143
|
+
});
|
|
1144
|
+
} catch {
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
return (_b = registry.keys.get(appId)) != null ? _b : id;
|
|
1148
|
+
};
|
|
1149
|
+
var resetAutoDeviceKeys = () => {
|
|
1150
|
+
const registry = autoDeviceKeyRegistry();
|
|
1151
|
+
registry.keys.clear();
|
|
1152
|
+
registry.hydrating.clear();
|
|
1153
|
+
};
|
|
1066
1154
|
|
|
1067
1155
|
// src/analytics/analyticsFacade.ts
|
|
1156
|
+
var warnInDev2 = (message) => {
|
|
1157
|
+
if (typeof __DEV__ !== "undefined" && __DEV__ && typeof console !== "undefined" && console.warn) {
|
|
1158
|
+
console.warn(message);
|
|
1159
|
+
}
|
|
1160
|
+
};
|
|
1068
1161
|
var createAnalytics = (config, options = {}) => {
|
|
1069
|
-
var _a2, _b, _c, _d
|
|
1162
|
+
var _a2, _b, _c, _d;
|
|
1070
1163
|
const instanceSessionId = (_a2 = config.sessionId) != null ? _a2 : makeSessionId();
|
|
1071
1164
|
const resolveSessionId = () => {
|
|
1072
1165
|
var _a3, _b2;
|
|
1073
1166
|
return (_b2 = (_a3 = config.sessionId) != null ? _a3 : getCurrentSessionId()) != null ? _b2 : instanceSessionId;
|
|
1074
1167
|
};
|
|
1168
|
+
if (config.sessionId) {
|
|
1169
|
+
warnInDev2(
|
|
1170
|
+
"[wireai] createAnalytics({ sessionId }) PINS every event from this instance to that one frozen id and opts out of the live per-open session (app.session_started) \u2014 lifecycle analytics collapse onto a single device-scoped id. Remove it unless your host runs its own session lifecycle."
|
|
1171
|
+
);
|
|
1172
|
+
}
|
|
1173
|
+
const detectedAppVersion = detectAppVersion();
|
|
1075
1174
|
let userContext = { ...(_b = config.userContext) != null ? _b : {} };
|
|
1076
1175
|
const hostDeviceKeyAtInit = typeof ((_c = config.userContext) == null ? void 0 : _c.deviceKey) === "string" && config.userContext.deviceKey.trim() ? config.userContext.deviceKey.trim() : void 0;
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
if (persisted) autoDeviceKey = persisted;
|
|
1084
|
-
else void storage.setItem(deviceKey, autoDeviceKey).catch(() => {
|
|
1085
|
-
});
|
|
1086
|
-
}).catch(() => {
|
|
1087
|
-
});
|
|
1088
|
-
}
|
|
1176
|
+
const autoDeviceKeyOptions = {
|
|
1177
|
+
appId: config.appId,
|
|
1178
|
+
// A host-supplied deviceKey opts out of minting AND persisting (unchanged contract).
|
|
1179
|
+
storage: hostDeviceKeyAtInit ? void 0 : config.storage
|
|
1180
|
+
};
|
|
1181
|
+
if (!hostDeviceKeyAtInit) resolveAutoDeviceKey(autoDeviceKeyOptions);
|
|
1089
1182
|
const envelope = () => {
|
|
1090
1183
|
var _a3;
|
|
1091
1184
|
return buildContextEnvelope({
|
|
@@ -1103,7 +1196,7 @@ var createAnalytics = (config, options = {}) => {
|
|
|
1103
1196
|
...options
|
|
1104
1197
|
});
|
|
1105
1198
|
let boundUserId = sanitizeUserId((_d = config.userContext) == null ? void 0 : _d.userId);
|
|
1106
|
-
const storageKey =
|
|
1199
|
+
const storageKey = analyticsUserIdStorageKey(config.appId);
|
|
1107
1200
|
if (config.storage) {
|
|
1108
1201
|
void config.storage.getItem(storageKey).then((saved) => {
|
|
1109
1202
|
if (saved && !boundUserId) boundUserId = saved;
|
|
@@ -1111,17 +1204,25 @@ var createAnalytics = (config, options = {}) => {
|
|
|
1111
1204
|
});
|
|
1112
1205
|
}
|
|
1113
1206
|
const applyContext = (event) => {
|
|
1114
|
-
var _a3;
|
|
1207
|
+
var _a3, _b2;
|
|
1115
1208
|
const hostDeviceKey = typeof userContext.deviceKey === "string" && userContext.deviceKey.trim() ? userContext.deviceKey : void 0;
|
|
1116
1209
|
const resolved = resolveUserContext(
|
|
1117
|
-
|
|
1118
|
-
{
|
|
1210
|
+
// `??` is lazy on purpose: a host-supplied key must never even touch the auto registry.
|
|
1211
|
+
{ ...userContext, deviceKey: hostDeviceKey != null ? hostDeviceKey : resolveAutoDeviceKey(autoDeviceKeyOptions) },
|
|
1212
|
+
{ autoAppVersion: (_a3 = config.appVersion) != null ? _a3 : detectedAppVersion }
|
|
1119
1213
|
);
|
|
1120
1214
|
if (resolved.userContext) {
|
|
1121
|
-
event.user_context = { ...resolved.userContext, ...(
|
|
1215
|
+
event.user_context = { ...resolved.userContext, ...(_b2 = event.user_context) != null ? _b2 : {} };
|
|
1122
1216
|
}
|
|
1123
1217
|
if (boundUserId && !event.user_id) event.user_id = boundUserId;
|
|
1124
1218
|
};
|
|
1219
|
+
const guardUserId = (clean) => {
|
|
1220
|
+
if (config.allowEmailAsUserId || !looksLikeEmail(clean)) return clean;
|
|
1221
|
+
warnInDev2(
|
|
1222
|
+
"[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."
|
|
1223
|
+
);
|
|
1224
|
+
return void 0;
|
|
1225
|
+
};
|
|
1125
1226
|
const setUserContext = (partial) => {
|
|
1126
1227
|
var _a3, _b2;
|
|
1127
1228
|
if (!partial || typeof partial !== "object") return;
|
|
@@ -1130,11 +1231,20 @@ var createAnalytics = (config, options = {}) => {
|
|
|
1130
1231
|
if (mergedExtra) userContext.extra = mergedExtra;
|
|
1131
1232
|
const uid = sanitizeUserId(partial.userId);
|
|
1132
1233
|
if (uid) {
|
|
1133
|
-
|
|
1134
|
-
if (
|
|
1135
|
-
|
|
1234
|
+
const bindable = guardUserId(uid);
|
|
1235
|
+
if (bindable) {
|
|
1236
|
+
boundUserId = bindable;
|
|
1237
|
+
if (config.storage) void config.storage.setItem(storageKey, bindable).catch(() => {
|
|
1238
|
+
});
|
|
1239
|
+
}
|
|
1136
1240
|
}
|
|
1137
1241
|
};
|
|
1242
|
+
const reset = () => {
|
|
1243
|
+
boundUserId = void 0;
|
|
1244
|
+
userContext = clearPiiFromContext(userContext);
|
|
1245
|
+
if (config.storage) void config.storage.removeItem(storageKey).catch(() => {
|
|
1246
|
+
});
|
|
1247
|
+
};
|
|
1138
1248
|
const track = (event, props) => {
|
|
1139
1249
|
if (!event) return;
|
|
1140
1250
|
const clientEvent = {
|
|
@@ -1161,9 +1271,11 @@ var createAnalytics = (config, options = {}) => {
|
|
|
1161
1271
|
const identify = (userId, traits) => {
|
|
1162
1272
|
const clean = sanitizeUserId(userId);
|
|
1163
1273
|
if (!clean) return;
|
|
1164
|
-
|
|
1274
|
+
const bindable = guardUserId(clean);
|
|
1275
|
+
if (!bindable) return;
|
|
1276
|
+
boundUserId = bindable;
|
|
1165
1277
|
if (config.storage) {
|
|
1166
|
-
void config.storage.setItem(storageKey,
|
|
1278
|
+
void config.storage.setItem(storageKey, bindable).catch(() => {
|
|
1167
1279
|
});
|
|
1168
1280
|
}
|
|
1169
1281
|
const clientEvent = {
|
|
@@ -1171,7 +1283,7 @@ var createAnalytics = (config, options = {}) => {
|
|
|
1171
1283
|
// Reuse the LIVE per-open session id (see `resolveSessionId`) so the server binds identity to
|
|
1172
1284
|
// the session it already saw instead of back-filling a phantom `session_started`.
|
|
1173
1285
|
session_id: resolveSessionId(),
|
|
1174
|
-
user_id:
|
|
1286
|
+
user_id: bindable
|
|
1175
1287
|
};
|
|
1176
1288
|
if (traits && Object.keys(traits).length > 0) clientEvent.meta = JSON.stringify(traits);
|
|
1177
1289
|
applyContext(clientEvent);
|
|
@@ -1182,12 +1294,14 @@ var createAnalytics = (config, options = {}) => {
|
|
|
1182
1294
|
screen,
|
|
1183
1295
|
identify,
|
|
1184
1296
|
setUserContext,
|
|
1297
|
+
reset,
|
|
1185
1298
|
flush: queue.flush,
|
|
1186
1299
|
notifyOnline: queue.notifyOnline,
|
|
1187
1300
|
size: queue.size
|
|
1188
1301
|
};
|
|
1189
1302
|
};
|
|
1190
1303
|
var useAnalytics = (config, options = {}) => {
|
|
1304
|
+
var _a2;
|
|
1191
1305
|
const ref = react.useRef(void 0);
|
|
1192
1306
|
const prevKeys = react.useRef("");
|
|
1193
1307
|
const currentKeys = `${config.serverUrl}|${config.apiKey}|${config.appId}`;
|
|
@@ -1195,23 +1309,37 @@ var useAnalytics = (config, options = {}) => {
|
|
|
1195
1309
|
prevKeys.current = currentKeys;
|
|
1196
1310
|
ref.current = createAnalytics(config, options);
|
|
1197
1311
|
}
|
|
1312
|
+
const hostDeviceKey = typeof ((_a2 = config.userContext) == null ? void 0 : _a2.deviceKey) === "string" && config.userContext.deviceKey.trim() ? config.userContext.deviceKey.trim() : void 0;
|
|
1313
|
+
react.useEffect(() => {
|
|
1314
|
+
var _a3;
|
|
1315
|
+
if (hostDeviceKey) (_a3 = ref.current) == null ? void 0 : _a3.setUserContext({ deviceKey: hostDeviceKey });
|
|
1316
|
+
}, [hostDeviceKey]);
|
|
1198
1317
|
return ref.current;
|
|
1199
1318
|
};
|
|
1200
1319
|
|
|
1320
|
+
exports.AUTO_DEVICE_ID_PREFIX = AUTO_DEVICE_ID_PREFIX;
|
|
1201
1321
|
exports.WIRE_ONBOARDING_EVENTS = WIRE_ONBOARDING_EVENTS;
|
|
1322
|
+
exports.analyticsUserIdStorageKey = analyticsUserIdStorageKey;
|
|
1202
1323
|
exports.buildContextEnvelope = buildContextEnvelope;
|
|
1324
|
+
exports.clearPiiFromContext = clearPiiFromContext;
|
|
1325
|
+
exports.clearUserContext = clearUserContext;
|
|
1203
1326
|
exports.createAnalytics = createAnalytics;
|
|
1204
1327
|
exports.createEventQueue = createEventQueue;
|
|
1205
1328
|
exports.createScreenTracker = createScreenTracker;
|
|
1329
|
+
exports.deviceIdStorageKey = deviceIdStorageKey;
|
|
1330
|
+
exports.ensureCurrentSessionId = ensureCurrentSessionId;
|
|
1206
1331
|
exports.getActiveRouteName = getActiveRouteName;
|
|
1207
1332
|
exports.getCurrentSessionId = getCurrentSessionId;
|
|
1333
|
+
exports.looksLikeEmail = looksLikeEmail;
|
|
1208
1334
|
exports.makeSessionId = makeSessionId;
|
|
1209
1335
|
exports.reportAppEvent = reportAppEvent;
|
|
1210
1336
|
exports.reportClientEvent = reportClientEvent;
|
|
1211
1337
|
exports.reportClientEventAwait = reportClientEventAwait;
|
|
1212
1338
|
exports.reportClientEvents = reportClientEvents;
|
|
1213
1339
|
exports.reportClientEventsAwait = reportClientEventsAwait;
|
|
1340
|
+
exports.resetAutoDeviceKeys = resetAutoDeviceKeys;
|
|
1214
1341
|
exports.resetCurrentSessionId = resetCurrentSessionId;
|
|
1342
|
+
exports.resolveAutoDeviceKey = resolveAutoDeviceKey;
|
|
1215
1343
|
exports.screenTrackingHandler = screenTrackingHandler;
|
|
1216
1344
|
exports.setCurrentSessionId = setCurrentSessionId;
|
|
1217
1345
|
exports.toAnalyticsEvent = toAnalyticsEvent;
|