@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/reviews/index.mjs
CHANGED
|
@@ -730,6 +730,7 @@ var requestStoreReview = async (store) => {
|
|
|
730
730
|
};
|
|
731
731
|
|
|
732
732
|
// src/analytics/reportClientEvent.ts
|
|
733
|
+
var makeSessionId = () => `wire_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
733
734
|
var buildEventsRequest = (target, events) => {
|
|
734
735
|
if (!(target == null ? void 0 : target.serverUrl) || events.length === 0) return null;
|
|
735
736
|
try {
|
|
@@ -742,6 +743,35 @@ var buildEventsRequest = (target, events) => {
|
|
|
742
743
|
}
|
|
743
744
|
};
|
|
744
745
|
|
|
746
|
+
// src/analytics/currentSession.ts
|
|
747
|
+
var CURRENT_SESSION_ID_SLOT = /* @__PURE__ */ Symbol.for(
|
|
748
|
+
"@wireai/activation:currentSessionId"
|
|
749
|
+
);
|
|
750
|
+
var globalSlot = globalThis;
|
|
751
|
+
var getCurrentSessionId = () => globalSlot[CURRENT_SESSION_ID_SLOT];
|
|
752
|
+
var warnInDev = (message) => {
|
|
753
|
+
if (typeof __DEV__ !== "undefined" && __DEV__ && typeof console !== "undefined" && console.warn) {
|
|
754
|
+
console.warn(message);
|
|
755
|
+
return true;
|
|
756
|
+
}
|
|
757
|
+
return false;
|
|
758
|
+
};
|
|
759
|
+
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.";
|
|
760
|
+
var MINT_WARNED_SLOT = /* @__PURE__ */ Symbol.for(
|
|
761
|
+
"@wireai/activation:currentSessionIdMintWarned"
|
|
762
|
+
);
|
|
763
|
+
var warnSlot = globalThis;
|
|
764
|
+
var ensureCurrentSessionId = () => {
|
|
765
|
+
const existing = globalSlot[CURRENT_SESSION_ID_SLOT];
|
|
766
|
+
if (typeof existing === "string" && existing.length > 0) return existing;
|
|
767
|
+
const minted = makeSessionId();
|
|
768
|
+
globalSlot[CURRENT_SESSION_ID_SLOT] = minted;
|
|
769
|
+
if (!warnSlot[MINT_WARNED_SLOT] && warnInDev(MINT_WARNING)) {
|
|
770
|
+
warnSlot[MINT_WARNED_SLOT] = true;
|
|
771
|
+
}
|
|
772
|
+
return minted;
|
|
773
|
+
};
|
|
774
|
+
|
|
745
775
|
// src/reviews/transport.ts
|
|
746
776
|
var submitReview = (target, review) => {
|
|
747
777
|
if (!(target == null ? void 0 : target.serverUrl)) return;
|
|
@@ -779,13 +809,15 @@ var fetchReviewDecision = async (target, options = {}) => {
|
|
|
779
809
|
}
|
|
780
810
|
};
|
|
781
811
|
var reportAppEvent = (target, name, options = {}) => {
|
|
812
|
+
var _a;
|
|
782
813
|
if (!(target == null ? void 0 : target.serverUrl) || !name) return;
|
|
783
814
|
try {
|
|
815
|
+
const supplied = (_a = options.sessionId) != null ? _a : "";
|
|
784
816
|
const event = {
|
|
785
817
|
event_type: "app_event",
|
|
786
|
-
question_key: name
|
|
818
|
+
question_key: name,
|
|
819
|
+
session_id: supplied.trim().length > 0 ? supplied : ensureCurrentSessionId()
|
|
787
820
|
};
|
|
788
|
-
if (options.sessionId) event.session_id = options.sessionId;
|
|
789
821
|
if (options.deviceKey) event.user_context = { device_key: options.deviceKey };
|
|
790
822
|
if (options.meta && Object.keys(options.meta).length > 0) {
|
|
791
823
|
event.meta = JSON.stringify(options.meta);
|
|
@@ -1049,14 +1081,21 @@ var styles6 = StyleSheet.create({
|
|
|
1049
1081
|
});
|
|
1050
1082
|
|
|
1051
1083
|
// src/coachmarks/runtime.ts
|
|
1052
|
-
var
|
|
1053
|
-
var
|
|
1054
|
-
var
|
|
1055
|
-
|
|
1084
|
+
var COACHMARK_RUNTIME_SLOT = /* @__PURE__ */ Symbol.for("@wireai/activation:coachmarkRuntime");
|
|
1085
|
+
var runtimeGlobal = globalThis;
|
|
1086
|
+
var coachmarkRuntime = () => {
|
|
1087
|
+
const existing = runtimeGlobal[COACHMARK_RUNTIME_SLOT];
|
|
1088
|
+
if (existing) return existing;
|
|
1089
|
+
const created = { storage: null, testing: false, coachmarksEnabled: true };
|
|
1090
|
+
runtimeGlobal[COACHMARK_RUNTIME_SLOT] = created;
|
|
1091
|
+
return created;
|
|
1092
|
+
};
|
|
1093
|
+
var getCoachmarkStorage = () => coachmarkRuntime().storage;
|
|
1094
|
+
var isCoachmarkTesting = () => coachmarkRuntime().testing;
|
|
1056
1095
|
var SEEN_VALUE = "1";
|
|
1057
1096
|
var hasSeenGate = (key, storageOverride, testingOverride) => {
|
|
1058
|
-
if (testingOverride != null ? testingOverride :
|
|
1059
|
-
const storage = storageOverride != null ? storageOverride :
|
|
1097
|
+
if (testingOverride != null ? testingOverride : isCoachmarkTesting()) return false;
|
|
1098
|
+
const storage = storageOverride != null ? storageOverride : getCoachmarkStorage();
|
|
1060
1099
|
if (!storage) return false;
|
|
1061
1100
|
try {
|
|
1062
1101
|
return storage.getItem(key) === SEEN_VALUE;
|
|
@@ -1065,8 +1104,8 @@ var hasSeenGate = (key, storageOverride, testingOverride) => {
|
|
|
1065
1104
|
}
|
|
1066
1105
|
};
|
|
1067
1106
|
var markSeenGate = (key, storageOverride, testingOverride) => {
|
|
1068
|
-
if (testingOverride != null ? testingOverride :
|
|
1069
|
-
const storage = storageOverride != null ? storageOverride :
|
|
1107
|
+
if (testingOverride != null ? testingOverride : isCoachmarkTesting()) return;
|
|
1108
|
+
const storage = storageOverride != null ? storageOverride : getCoachmarkStorage();
|
|
1070
1109
|
if (!storage) return;
|
|
1071
1110
|
try {
|
|
1072
1111
|
storage.setItem(key, SEEN_VALUE);
|
|
@@ -1252,7 +1291,22 @@ var sameDecision = (a, b) => {
|
|
|
1252
1291
|
var reviewSeenKey = (id, version) => version ? `wire_review_${id}_${version}_seen` : `wire_review_${id}_seen`;
|
|
1253
1292
|
var reviewLastShownKey = (id) => `wire_review_${id}_last`;
|
|
1254
1293
|
var reviewSessionsKey = (id) => `wire_review_${id}_sessions`;
|
|
1294
|
+
var reviewSessionOpenKey = (id) => `wire_review_${id}_open`;
|
|
1255
1295
|
var resolveStorage = (override) => override != null ? override : getCoachmarkStorage();
|
|
1296
|
+
var NO_STORAGE_WARNED_SLOT = /* @__PURE__ */ Symbol.for(
|
|
1297
|
+
"@wireai/activation:gateStorageWarned"
|
|
1298
|
+
);
|
|
1299
|
+
var gateWarnGlobal = globalThis;
|
|
1300
|
+
var warnMissingGateStorage = (storage, gate) => {
|
|
1301
|
+
if (storage) return;
|
|
1302
|
+
if (gateWarnGlobal[NO_STORAGE_WARNED_SLOT]) return;
|
|
1303
|
+
if (typeof __DEV__ === "undefined" || !__DEV__) return;
|
|
1304
|
+
if (typeof console === "undefined" || !console.warn) return;
|
|
1305
|
+
gateWarnGlobal[NO_STORAGE_WARNED_SLOT] = true;
|
|
1306
|
+
console.warn(
|
|
1307
|
+
`[wireai] the ${gate} gate has no sync storage, so its app-open counter is stuck at 1 and the fail-closed minSessions rule can never be satisfied \u2014 the gate will never fire. Mount CoachmarkProvider with a sync storage adapter at your app root, or pass \`storage\` to the gate.`
|
|
1308
|
+
);
|
|
1309
|
+
};
|
|
1256
1310
|
var readInt = (storage, key) => {
|
|
1257
1311
|
if (!storage) return 0;
|
|
1258
1312
|
try {
|
|
@@ -1270,6 +1324,39 @@ var writeInt = (storage, key, value) => {
|
|
|
1270
1324
|
} catch {
|
|
1271
1325
|
}
|
|
1272
1326
|
};
|
|
1327
|
+
var readStr = (storage, key) => {
|
|
1328
|
+
if (!storage) return void 0;
|
|
1329
|
+
try {
|
|
1330
|
+
const raw = storage.getItem(key);
|
|
1331
|
+
return typeof raw === "string" && raw.length > 0 ? raw : void 0;
|
|
1332
|
+
} catch {
|
|
1333
|
+
return void 0;
|
|
1334
|
+
}
|
|
1335
|
+
};
|
|
1336
|
+
var PROCESS_OPEN_ID_SLOT = /* @__PURE__ */ Symbol.for("@wireai/activation:processOpenId");
|
|
1337
|
+
var openIdGlobal = globalThis;
|
|
1338
|
+
var currentOpenId = () => {
|
|
1339
|
+
var _a;
|
|
1340
|
+
const existing = openIdGlobal[PROCESS_OPEN_ID_SLOT];
|
|
1341
|
+
if (existing) return existing;
|
|
1342
|
+
const resolved = (_a = getCurrentSessionId()) != null ? _a : makeSessionId();
|
|
1343
|
+
openIdGlobal[PROCESS_OPEN_ID_SLOT] = resolved;
|
|
1344
|
+
return resolved;
|
|
1345
|
+
};
|
|
1346
|
+
var bumpSessionCount = (storage, sessionsKey, openKey, openId = currentOpenId()) => {
|
|
1347
|
+
const lastOpen = readStr(storage, openKey);
|
|
1348
|
+
const stored = readInt(storage, sessionsKey);
|
|
1349
|
+
if (lastOpen === openId) return stored > 0 ? stored : 1;
|
|
1350
|
+
const next = stored + 1;
|
|
1351
|
+
writeInt(storage, sessionsKey, next);
|
|
1352
|
+
if (storage) {
|
|
1353
|
+
try {
|
|
1354
|
+
storage.setItem(openKey, openId);
|
|
1355
|
+
} catch {
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
return next;
|
|
1359
|
+
};
|
|
1273
1360
|
|
|
1274
1361
|
// src/reviews/useReviewGate.ts
|
|
1275
1362
|
var useStableValue2 = (value, isEqual) => {
|
|
@@ -1292,15 +1379,15 @@ var useReviewGate = ({
|
|
|
1292
1379
|
(a, b) => sameDecision(a, b)
|
|
1293
1380
|
);
|
|
1294
1381
|
const featureEnabled = useResolvedFeatures({ flags: features, config: featuresConfig }).review.enabled;
|
|
1295
|
-
const
|
|
1382
|
+
const testing = isTesting != null ? isTesting : isCoachmarkTesting();
|
|
1296
1383
|
const seenKey = reviewSeenKey(config.id, config.oncePerVersion === false ? void 0 : config.appVersion);
|
|
1297
1384
|
const lastKey = reviewLastShownKey(config.id);
|
|
1298
1385
|
const sessionsKey = reviewSessionsKey(config.id);
|
|
1386
|
+
const sessionOpenKey = reviewSessionOpenKey(config.id);
|
|
1299
1387
|
const sessions = useState(() => {
|
|
1300
1388
|
const store = resolveStorage(storage);
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
return next;
|
|
1389
|
+
warnMissingGateStorage(store, "review");
|
|
1390
|
+
return bumpSessionCount(store, sessionsKey, sessionOpenKey);
|
|
1304
1391
|
})[0];
|
|
1305
1392
|
const hasServerDecision = decision != null;
|
|
1306
1393
|
const timeoutMs = config.timeoutFallbackMs;
|
|
@@ -1316,7 +1403,7 @@ var useReviewGate = ({
|
|
|
1316
1403
|
}, [waits, timeoutMs]);
|
|
1317
1404
|
const { visible, verdict } = useMemo(() => {
|
|
1318
1405
|
if (!featureEnabled) return { visible: false, verdict: { fire: false, reason: "feature_disabled" } };
|
|
1319
|
-
if (
|
|
1406
|
+
if (testing) return { visible: true, verdict: { fire: true, reason: "testing" } };
|
|
1320
1407
|
const store = resolveStorage(storage);
|
|
1321
1408
|
const local = evaluateGate(resolveRules(config), {
|
|
1322
1409
|
sessions,
|
|
@@ -1329,7 +1416,7 @@ var useReviewGate = ({
|
|
|
1329
1416
|
const alreadySeen = hasSeenGate(seenKey, storage, isTesting);
|
|
1330
1417
|
const ready = hasServerDecision || elapsed;
|
|
1331
1418
|
return { visible: ready && resolved.fire && !alreadySeen, verdict: resolved };
|
|
1332
|
-
}, [featureEnabled,
|
|
1419
|
+
}, [featureEnabled, testing, config, decision, events, sessions, elapsed, hasServerDecision, seenKey, lastKey, storage, isTesting]);
|
|
1333
1420
|
const shownRef = useRef(false);
|
|
1334
1421
|
const markShown = useCallback(() => {
|
|
1335
1422
|
if (shownRef.current) return;
|