@wireai/activation 0.11.0 → 0.12.1
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 +62 -8
- package/CHANGELOG.md +188 -3
- package/INTEGRATION_PROMPT.md +25 -2
- package/README.md +112 -1
- package/dist/analytics/index.d.mts +18 -6
- package/dist/analytics/index.d.ts +18 -6
- package/dist/analytics/index.js +151 -41
- package/dist/analytics/index.js.map +1 -1
- package/dist/analytics/index.mjs +147 -42
- package/dist/analytics/index.mjs.map +1 -1
- package/dist/coachmarks/index.d.mts +16 -0
- package/dist/coachmarks/index.d.ts +16 -0
- package/dist/coachmarks/index.js +19 -13
- package/dist/coachmarks/index.js.map +1 -1
- package/dist/coachmarks/index.mjs +19 -13
- package/dist/coachmarks/index.mjs.map +1 -1
- package/dist/{currentSession-C0_odnIW.d.mts → currentSession-BlCeDP0f.d.mts} +145 -33
- package/dist/{currentSession-DdnUq2HQ.d.ts → currentSession-BxEB37xt.d.ts} +145 -33
- package/dist/index.d.mts +243 -53
- package/dist/index.d.ts +243 -53
- package/dist/index.js +531 -156
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +517 -157
- 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 +79 -14
- package/dist/questionnaire/index.js.map +1 -1
- package/dist/questionnaire/index.mjs +79 -14
- 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 +103 -16
- package/dist/reviews/index.js.map +1 -1
- package/dist/reviews/index.mjs +103 -16
- package/dist/reviews/index.mjs.map +1 -1
- package/dist/showcase/index.js +15 -6
- package/dist/showcase/index.js.map +1 -1
- package/dist/showcase/index.mjs +15 -6
- package/dist/showcase/index.mjs.map +1 -1
- package/dist/{transport-BGW9uXZJ.d.mts → transport-CF_eHwzC.d.mts} +15 -1
- package/dist/{transport-jUJd5kxu.d.ts → transport-DsRe4epC.d.ts} +15 -1
- package/llms.txt +3 -0
- package/package.json +1 -1
- package/src/WireOnboarding.tsx +140 -5
- package/src/activation/useWireActivation.ts +12 -1
- package/src/activation/wireActivation.ts +44 -25
- package/src/analytics/analyticsFacade.ts +54 -29
- package/src/analytics/currentSession.ts +83 -0
- package/src/analytics/eventQueue.ts +9 -1
- package/src/analytics/index.ts +20 -1
- package/src/analytics/reportClientEvent.ts +42 -0
- package/src/analytics/screenTracking.ts +6 -1
- package/src/analytics/useAnalytics.ts +22 -1
- package/src/coachmarks/runtime.ts +53 -17
- package/src/config/wireConfigFromEnv.ts +46 -2
- package/src/context/deviceId.ts +173 -0
- package/src/context/userContext.ts +18 -0
- package/src/index.ts +54 -2
- package/src/questionnaire/runtime.ts +13 -2
- package/src/questionnaire/useQuestionnaireGate.ts +11 -4
- 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/runtime.ts +153 -1
- package/src/reviews/transport.ts +21 -2
- package/src/reviews/useReviewGate.ts +14 -4
- package/src/session-analytics/lifecycle.ts +9 -2
- package/src/session-analytics/reportSessionStart.ts +15 -4
- package/src/session-analytics/useLifecycleEvents.ts +83 -27
- package/src/session-analytics/useSessionStart.ts +37 -1
- package/src/types.ts +41 -4
package/dist/analytics/index.mjs
CHANGED
|
@@ -411,6 +411,23 @@ var buildEventsRequest = (target, events) => {
|
|
|
411
411
|
return null;
|
|
412
412
|
}
|
|
413
413
|
};
|
|
414
|
+
var warnOnSkippedEvents = (res) => {
|
|
415
|
+
if (typeof __DEV__ === "undefined" || !__DEV__) return;
|
|
416
|
+
if (typeof console === "undefined" || !console.warn) return;
|
|
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
|
+
console.warn(
|
|
424
|
+
`[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.`
|
|
425
|
+
);
|
|
426
|
+
}).catch(() => {
|
|
427
|
+
});
|
|
428
|
+
} catch {
|
|
429
|
+
}
|
|
430
|
+
};
|
|
414
431
|
var reportClientEvents = (target, events) => {
|
|
415
432
|
try {
|
|
416
433
|
const req = buildEventsRequest(target, events);
|
|
@@ -433,15 +450,54 @@ var reportClientEventsAwait = async (target, events) => {
|
|
|
433
450
|
};
|
|
434
451
|
var reportClientEventAwait = (target, event) => reportClientEventsAwait(target, [event]);
|
|
435
452
|
|
|
453
|
+
// src/analytics/currentSession.ts
|
|
454
|
+
var CURRENT_SESSION_ID_SLOT = /* @__PURE__ */ Symbol.for(
|
|
455
|
+
"@wireai/activation:currentSessionId"
|
|
456
|
+
);
|
|
457
|
+
var globalSlot = globalThis;
|
|
458
|
+
var setCurrentSessionId = (id) => {
|
|
459
|
+
if (typeof id === "string" && id.length > 0) {
|
|
460
|
+
globalSlot[CURRENT_SESSION_ID_SLOT] = id;
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
var getCurrentSessionId = () => globalSlot[CURRENT_SESSION_ID_SLOT];
|
|
464
|
+
var resetCurrentSessionId = () => {
|
|
465
|
+
globalSlot[CURRENT_SESSION_ID_SLOT] = void 0;
|
|
466
|
+
};
|
|
467
|
+
var warnInDev = (message) => {
|
|
468
|
+
if (typeof __DEV__ !== "undefined" && __DEV__ && typeof console !== "undefined" && console.warn) {
|
|
469
|
+
console.warn(message);
|
|
470
|
+
return true;
|
|
471
|
+
}
|
|
472
|
+
return false;
|
|
473
|
+
};
|
|
474
|
+
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.";
|
|
475
|
+
var MINT_WARNED_SLOT = /* @__PURE__ */ Symbol.for(
|
|
476
|
+
"@wireai/activation:currentSessionIdMintWarned"
|
|
477
|
+
);
|
|
478
|
+
var warnSlot = globalThis;
|
|
479
|
+
var ensureCurrentSessionId = () => {
|
|
480
|
+
const existing = globalSlot[CURRENT_SESSION_ID_SLOT];
|
|
481
|
+
if (typeof existing === "string" && existing.length > 0) return existing;
|
|
482
|
+
const minted = makeSessionId();
|
|
483
|
+
globalSlot[CURRENT_SESSION_ID_SLOT] = minted;
|
|
484
|
+
if (!warnSlot[MINT_WARNED_SLOT] && warnInDev(MINT_WARNING)) {
|
|
485
|
+
warnSlot[MINT_WARNED_SLOT] = true;
|
|
486
|
+
}
|
|
487
|
+
return minted;
|
|
488
|
+
};
|
|
489
|
+
|
|
436
490
|
// src/reviews/transport.ts
|
|
437
491
|
var reportAppEvent = (target, name2, options = {}) => {
|
|
492
|
+
var _a2;
|
|
438
493
|
if (!(target == null ? void 0 : target.serverUrl) || !name2) return;
|
|
439
494
|
try {
|
|
495
|
+
const supplied = (_a2 = options.sessionId) != null ? _a2 : "";
|
|
440
496
|
const event = {
|
|
441
497
|
event_type: "app_event",
|
|
442
|
-
question_key: name2
|
|
498
|
+
question_key: name2,
|
|
499
|
+
session_id: supplied.trim().length > 0 ? supplied : ensureCurrentSessionId()
|
|
443
500
|
};
|
|
444
|
-
if (options.sessionId) event.session_id = options.sessionId;
|
|
445
501
|
if (options.deviceKey) event.user_context = { device_key: options.deviceKey };
|
|
446
502
|
if (options.meta && Object.keys(options.meta).length > 0) {
|
|
447
503
|
event.meta = JSON.stringify(options.meta);
|
|
@@ -877,6 +933,7 @@ var createEventQueue = (options) => {
|
|
|
877
933
|
const timer = setTimeout(() => controller == null ? void 0 : controller.abort(), 15e3);
|
|
878
934
|
try {
|
|
879
935
|
const res = await fetch(req.url, { ...req.init, signal: controller == null ? void 0 : controller.signal });
|
|
936
|
+
warnOnSkippedEvents(res);
|
|
880
937
|
return !!(res && res.ok);
|
|
881
938
|
} catch {
|
|
882
939
|
return false;
|
|
@@ -955,21 +1012,6 @@ var createEventQueue = (options) => {
|
|
|
955
1012
|
return { enqueue, flush, notifyOnline, size };
|
|
956
1013
|
};
|
|
957
1014
|
|
|
958
|
-
// src/analytics/currentSession.ts
|
|
959
|
-
var CURRENT_SESSION_ID_SLOT = /* @__PURE__ */ Symbol.for(
|
|
960
|
-
"@wireai/activation:currentSessionId"
|
|
961
|
-
);
|
|
962
|
-
var globalSlot = globalThis;
|
|
963
|
-
var setCurrentSessionId = (id) => {
|
|
964
|
-
if (typeof id === "string" && id.length > 0) {
|
|
965
|
-
globalSlot[CURRENT_SESSION_ID_SLOT] = id;
|
|
966
|
-
}
|
|
967
|
-
};
|
|
968
|
-
var getCurrentSessionId = () => globalSlot[CURRENT_SESSION_ID_SLOT];
|
|
969
|
-
var resetCurrentSessionId = () => {
|
|
970
|
-
globalSlot[CURRENT_SESSION_ID_SLOT] = void 0;
|
|
971
|
-
};
|
|
972
|
-
|
|
973
1015
|
// src/identity/userIdentity.ts
|
|
974
1016
|
var USER_ID_MAX_LENGTH = 128;
|
|
975
1017
|
var sanitizeUserId = (raw) => {
|
|
@@ -1066,34 +1108,90 @@ var mintDeviceId = () => {
|
|
|
1066
1108
|
const time = Date.now().toString(36);
|
|
1067
1109
|
return `${AUTO_DEVICE_ID_PREFIX}${time}_${randomChunk()}${randomChunk()}`;
|
|
1068
1110
|
};
|
|
1111
|
+
var AUTO_DEVICE_KEY_SLOT = /* @__PURE__ */ Symbol.for("@wireai/activation:autoDeviceKeys");
|
|
1112
|
+
var deviceKeyGlobal = globalThis;
|
|
1113
|
+
var autoDeviceKeyRegistry = () => {
|
|
1114
|
+
const existing = deviceKeyGlobal[AUTO_DEVICE_KEY_SLOT];
|
|
1115
|
+
if (existing) return existing;
|
|
1116
|
+
const created = { keys: /* @__PURE__ */ new Map(), hydrating: /* @__PURE__ */ new Set() };
|
|
1117
|
+
deviceKeyGlobal[AUTO_DEVICE_KEY_SLOT] = created;
|
|
1118
|
+
return created;
|
|
1119
|
+
};
|
|
1120
|
+
var startHydration = (registry, appId, storage, minted) => {
|
|
1121
|
+
if (!registry.pending) registry.pending = /* @__PURE__ */ new Map();
|
|
1122
|
+
const existing = registry.pending.get(appId);
|
|
1123
|
+
if (existing) return existing;
|
|
1124
|
+
const slot = deviceIdStorageKey(appId);
|
|
1125
|
+
const settled = () => {
|
|
1126
|
+
var _a2;
|
|
1127
|
+
return (_a2 = registry.keys.get(appId)) != null ? _a2 : minted;
|
|
1128
|
+
};
|
|
1129
|
+
let run;
|
|
1130
|
+
try {
|
|
1131
|
+
run = Promise.resolve(storage.getItem(slot)).then((saved) => {
|
|
1132
|
+
const persisted = typeof saved === "string" && saved.trim() ? saved.trim() : void 0;
|
|
1133
|
+
if (persisted) {
|
|
1134
|
+
registry.keys.set(appId, persisted);
|
|
1135
|
+
return persisted;
|
|
1136
|
+
}
|
|
1137
|
+
return Promise.resolve(storage.setItem(slot, minted)).then(settled, settled);
|
|
1138
|
+
}).catch(settled);
|
|
1139
|
+
} catch {
|
|
1140
|
+
run = Promise.resolve(settled());
|
|
1141
|
+
}
|
|
1142
|
+
registry.pending.set(appId, run);
|
|
1143
|
+
return run;
|
|
1144
|
+
};
|
|
1145
|
+
var resolveAutoDeviceKey = (opts = {}) => {
|
|
1146
|
+
var _a2, _b;
|
|
1147
|
+
const registry = autoDeviceKeyRegistry();
|
|
1148
|
+
const appId = (_a2 = opts.appId) != null ? _a2 : "default";
|
|
1149
|
+
let id = registry.keys.get(appId);
|
|
1150
|
+
if (!id) {
|
|
1151
|
+
id = mintDeviceId();
|
|
1152
|
+
registry.keys.set(appId, id);
|
|
1153
|
+
}
|
|
1154
|
+
const storage = opts.storage;
|
|
1155
|
+
if (storage && !registry.hydrating.has(appId)) {
|
|
1156
|
+
registry.hydrating.add(appId);
|
|
1157
|
+
void startHydration(registry, appId, storage, id);
|
|
1158
|
+
}
|
|
1159
|
+
return (_b = registry.keys.get(appId)) != null ? _b : id;
|
|
1160
|
+
};
|
|
1161
|
+
var resetAutoDeviceKeys = () => {
|
|
1162
|
+
var _a2;
|
|
1163
|
+
const registry = autoDeviceKeyRegistry();
|
|
1164
|
+
registry.keys.clear();
|
|
1165
|
+
registry.hydrating.clear();
|
|
1166
|
+
(_a2 = registry.pending) == null ? void 0 : _a2.clear();
|
|
1167
|
+
};
|
|
1069
1168
|
|
|
1070
1169
|
// src/analytics/analyticsFacade.ts
|
|
1071
|
-
var
|
|
1170
|
+
var warnInDev2 = (message) => {
|
|
1072
1171
|
if (typeof __DEV__ !== "undefined" && __DEV__ && typeof console !== "undefined" && console.warn) {
|
|
1073
1172
|
console.warn(message);
|
|
1074
1173
|
}
|
|
1075
1174
|
};
|
|
1076
1175
|
var createAnalytics = (config, options = {}) => {
|
|
1077
|
-
var _a2, _b, _c
|
|
1078
|
-
const instanceSessionId = (_a2 = config.sessionId) != null ? _a2 : makeSessionId();
|
|
1176
|
+
var _a2, _b, _c;
|
|
1079
1177
|
const resolveSessionId = () => {
|
|
1080
|
-
var _a3
|
|
1081
|
-
return (
|
|
1178
|
+
var _a3;
|
|
1179
|
+
return (_a3 = config.sessionId) != null ? _a3 : ensureCurrentSessionId();
|
|
1082
1180
|
};
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
const storage = config.storage;
|
|
1088
|
-
const deviceKey = deviceIdStorageKey(config.appId);
|
|
1089
|
-
void storage.getItem(deviceKey).then((saved) => {
|
|
1090
|
-
const persisted = typeof saved === "string" && saved.trim() ? saved.trim() : void 0;
|
|
1091
|
-
if (persisted) autoDeviceKey = persisted;
|
|
1092
|
-
else void storage.setItem(deviceKey, autoDeviceKey).catch(() => {
|
|
1093
|
-
});
|
|
1094
|
-
}).catch(() => {
|
|
1095
|
-
});
|
|
1181
|
+
if (config.sessionId) {
|
|
1182
|
+
warnInDev2(
|
|
1183
|
+
"[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."
|
|
1184
|
+
);
|
|
1096
1185
|
}
|
|
1186
|
+
const detectedAppVersion = detectAppVersion();
|
|
1187
|
+
let userContext = { ...(_a2 = config.userContext) != null ? _a2 : {} };
|
|
1188
|
+
const hostDeviceKeyAtInit = typeof ((_b = config.userContext) == null ? void 0 : _b.deviceKey) === "string" && config.userContext.deviceKey.trim() ? config.userContext.deviceKey.trim() : void 0;
|
|
1189
|
+
const autoDeviceKeyOptions = {
|
|
1190
|
+
appId: config.appId,
|
|
1191
|
+
// A host-supplied deviceKey opts out of minting AND persisting (unchanged contract).
|
|
1192
|
+
storage: hostDeviceKeyAtInit ? void 0 : config.storage
|
|
1193
|
+
};
|
|
1194
|
+
if (!hostDeviceKeyAtInit) resolveAutoDeviceKey(autoDeviceKeyOptions);
|
|
1097
1195
|
const envelope = () => {
|
|
1098
1196
|
var _a3;
|
|
1099
1197
|
return buildContextEnvelope({
|
|
@@ -1110,7 +1208,7 @@ var createAnalytics = (config, options = {}) => {
|
|
|
1110
1208
|
envelope,
|
|
1111
1209
|
...options
|
|
1112
1210
|
});
|
|
1113
|
-
let boundUserId = sanitizeUserId((
|
|
1211
|
+
let boundUserId = sanitizeUserId((_c = config.userContext) == null ? void 0 : _c.userId);
|
|
1114
1212
|
const storageKey = analyticsUserIdStorageKey(config.appId);
|
|
1115
1213
|
if (config.storage) {
|
|
1116
1214
|
void config.storage.getItem(storageKey).then((saved) => {
|
|
@@ -1119,20 +1217,21 @@ var createAnalytics = (config, options = {}) => {
|
|
|
1119
1217
|
});
|
|
1120
1218
|
}
|
|
1121
1219
|
const applyContext = (event) => {
|
|
1122
|
-
var _a3;
|
|
1220
|
+
var _a3, _b2;
|
|
1123
1221
|
const hostDeviceKey = typeof userContext.deviceKey === "string" && userContext.deviceKey.trim() ? userContext.deviceKey : void 0;
|
|
1124
1222
|
const resolved = resolveUserContext(
|
|
1125
|
-
|
|
1126
|
-
{
|
|
1223
|
+
// `??` is lazy on purpose: a host-supplied key must never even touch the auto registry.
|
|
1224
|
+
{ ...userContext, deviceKey: hostDeviceKey != null ? hostDeviceKey : resolveAutoDeviceKey(autoDeviceKeyOptions) },
|
|
1225
|
+
{ autoAppVersion: (_a3 = config.appVersion) != null ? _a3 : detectedAppVersion }
|
|
1127
1226
|
);
|
|
1128
1227
|
if (resolved.userContext) {
|
|
1129
|
-
event.user_context = { ...resolved.userContext, ...(
|
|
1228
|
+
event.user_context = { ...resolved.userContext, ...(_b2 = event.user_context) != null ? _b2 : {} };
|
|
1130
1229
|
}
|
|
1131
1230
|
if (boundUserId && !event.user_id) event.user_id = boundUserId;
|
|
1132
1231
|
};
|
|
1133
1232
|
const guardUserId = (clean) => {
|
|
1134
1233
|
if (config.allowEmailAsUserId || !looksLikeEmail(clean)) return clean;
|
|
1135
|
-
|
|
1234
|
+
warnInDev2(
|
|
1136
1235
|
"[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
1236
|
);
|
|
1138
1237
|
return void 0;
|
|
@@ -1215,6 +1314,7 @@ var createAnalytics = (config, options = {}) => {
|
|
|
1215
1314
|
};
|
|
1216
1315
|
};
|
|
1217
1316
|
var useAnalytics = (config, options = {}) => {
|
|
1317
|
+
var _a2;
|
|
1218
1318
|
const ref = useRef(void 0);
|
|
1219
1319
|
const prevKeys = useRef("");
|
|
1220
1320
|
const currentKeys = `${config.serverUrl}|${config.apiKey}|${config.appId}`;
|
|
@@ -1222,9 +1322,14 @@ var useAnalytics = (config, options = {}) => {
|
|
|
1222
1322
|
prevKeys.current = currentKeys;
|
|
1223
1323
|
ref.current = createAnalytics(config, options);
|
|
1224
1324
|
}
|
|
1325
|
+
const hostDeviceKey = typeof ((_a2 = config.userContext) == null ? void 0 : _a2.deviceKey) === "string" && config.userContext.deviceKey.trim() ? config.userContext.deviceKey.trim() : void 0;
|
|
1326
|
+
useEffect(() => {
|
|
1327
|
+
var _a3;
|
|
1328
|
+
if (hostDeviceKey) (_a3 = ref.current) == null ? void 0 : _a3.setUserContext({ deviceKey: hostDeviceKey });
|
|
1329
|
+
}, [hostDeviceKey]);
|
|
1225
1330
|
return ref.current;
|
|
1226
1331
|
};
|
|
1227
1332
|
|
|
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 };
|
|
1333
|
+
export { AUTO_DEVICE_ID_PREFIX, WIRE_ONBOARDING_EVENTS, analyticsUserIdStorageKey, buildContextEnvelope, clearPiiFromContext, clearUserContext, createAnalytics, createEventQueue, createScreenTracker, deviceIdStorageKey, ensureCurrentSessionId, getActiveRouteName, getCurrentSessionId, looksLikeEmail, makeSessionId, reportAppEvent, reportClientEvent, reportClientEventAwait, reportClientEvents, reportClientEventsAwait, resetAutoDeviceKeys, resetCurrentSessionId, resolveAutoDeviceKey, screenTrackingHandler, setCurrentSessionId, toAnalyticsEvent, useAnalytics, useScreenTracking };
|
|
1229
1334
|
//# sourceMappingURL=index.mjs.map
|
|
1230
1335
|
//# sourceMappingURL=index.mjs.map
|