@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.d.mts
CHANGED
|
@@ -4,7 +4,7 @@ export { e as RatingRoute, f as ReviewConfig, g as ReviewDecisionResponse, h as
|
|
|
4
4
|
export { C as ReviewModal, a as ReviewModalHandle, b as ReviewModalProps } from '../CenteredModal-C3qQBHsA.mjs';
|
|
5
5
|
export { G as GateRules, a as GateSignals, b as buildReviewSubmission, d as decideReview, e as evaluateGate, r as resolveRules, c as routeRating } from '../decision-Bl_M2y3r.mjs';
|
|
6
6
|
import { c as OnboardingFonts } from '../types-BKfpdZzX.mjs';
|
|
7
|
-
export { F as FetchReviewDecisionOptions, R as ReportAppEventOptions, f as fetchReviewDecision, r as reportAppEvent, s as submitReview } from '../transport-
|
|
7
|
+
export { F as FetchReviewDecisionOptions, R as ReportAppEventOptions, f as fetchReviewDecision, r as reportAppEvent, s as submitReview } from '../transport-CF_eHwzC.mjs';
|
|
8
8
|
import '../types-BcmagF6K.mjs';
|
|
9
9
|
import '../types-CKFhyrMu.mjs';
|
|
10
10
|
|
|
@@ -19,7 +19,7 @@ import '../types-CKFhyrMu.mjs';
|
|
|
19
19
|
declare const reviewSeenKey: (id: string, version?: string) => string;
|
|
20
20
|
/** Last-shown timestamp key (epoch ms), for the cooldown rule. */
|
|
21
21
|
declare const reviewLastShownKey: (id: string) => string;
|
|
22
|
-
/** Session-count key, incremented once per
|
|
22
|
+
/** Session-count key, incremented once per APP-OPEN (not per mount), for the min-sessions rule. */
|
|
23
23
|
declare const reviewSessionsKey: (id: string) => string;
|
|
24
24
|
|
|
25
25
|
/**
|
package/dist/reviews/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export { e as RatingRoute, f as ReviewConfig, g as ReviewDecisionResponse, h as
|
|
|
4
4
|
export { C as ReviewModal, a as ReviewModalHandle, b as ReviewModalProps } from '../CenteredModal-Cdgns6--.js';
|
|
5
5
|
export { G as GateRules, a as GateSignals, b as buildReviewSubmission, d as decideReview, e as evaluateGate, r as resolveRules, c as routeRating } from '../decision-Cau5KmP6.js';
|
|
6
6
|
import { c as OnboardingFonts } from '../types-BKfpdZzX.js';
|
|
7
|
-
export { F as FetchReviewDecisionOptions, R as ReportAppEventOptions, f as fetchReviewDecision, r as reportAppEvent, s as submitReview } from '../transport-
|
|
7
|
+
export { F as FetchReviewDecisionOptions, R as ReportAppEventOptions, f as fetchReviewDecision, r as reportAppEvent, s as submitReview } from '../transport-DsRe4epC.js';
|
|
8
8
|
import '../types-BcmagF6K.js';
|
|
9
9
|
import '../types-CKFhyrMu.js';
|
|
10
10
|
|
|
@@ -19,7 +19,7 @@ import '../types-CKFhyrMu.js';
|
|
|
19
19
|
declare const reviewSeenKey: (id: string, version?: string) => string;
|
|
20
20
|
/** Last-shown timestamp key (epoch ms), for the cooldown rule. */
|
|
21
21
|
declare const reviewLastShownKey: (id: string) => string;
|
|
22
|
-
/** Session-count key, incremented once per
|
|
22
|
+
/** Session-count key, incremented once per APP-OPEN (not per mount), for the min-sessions rule. */
|
|
23
23
|
declare const reviewSessionsKey: (id: string) => string;
|
|
24
24
|
|
|
25
25
|
/**
|
package/dist/reviews/index.js
CHANGED
|
@@ -736,6 +736,7 @@ var requestStoreReview = async (store) => {
|
|
|
736
736
|
};
|
|
737
737
|
|
|
738
738
|
// src/analytics/reportClientEvent.ts
|
|
739
|
+
var makeSessionId = () => `wire_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
739
740
|
var buildEventsRequest = (target, events) => {
|
|
740
741
|
if (!(target == null ? void 0 : target.serverUrl) || events.length === 0) return null;
|
|
741
742
|
try {
|
|
@@ -748,6 +749,35 @@ var buildEventsRequest = (target, events) => {
|
|
|
748
749
|
}
|
|
749
750
|
};
|
|
750
751
|
|
|
752
|
+
// src/analytics/currentSession.ts
|
|
753
|
+
var CURRENT_SESSION_ID_SLOT = /* @__PURE__ */ Symbol.for(
|
|
754
|
+
"@wireai/activation:currentSessionId"
|
|
755
|
+
);
|
|
756
|
+
var globalSlot = globalThis;
|
|
757
|
+
var getCurrentSessionId = () => globalSlot[CURRENT_SESSION_ID_SLOT];
|
|
758
|
+
var warnInDev = (message) => {
|
|
759
|
+
if (typeof __DEV__ !== "undefined" && __DEV__ && typeof console !== "undefined" && console.warn) {
|
|
760
|
+
console.warn(message);
|
|
761
|
+
return true;
|
|
762
|
+
}
|
|
763
|
+
return false;
|
|
764
|
+
};
|
|
765
|
+
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.";
|
|
766
|
+
var MINT_WARNED_SLOT = /* @__PURE__ */ Symbol.for(
|
|
767
|
+
"@wireai/activation:currentSessionIdMintWarned"
|
|
768
|
+
);
|
|
769
|
+
var warnSlot = globalThis;
|
|
770
|
+
var ensureCurrentSessionId = () => {
|
|
771
|
+
const existing = globalSlot[CURRENT_SESSION_ID_SLOT];
|
|
772
|
+
if (typeof existing === "string" && existing.length > 0) return existing;
|
|
773
|
+
const minted = makeSessionId();
|
|
774
|
+
globalSlot[CURRENT_SESSION_ID_SLOT] = minted;
|
|
775
|
+
if (!warnSlot[MINT_WARNED_SLOT] && warnInDev(MINT_WARNING)) {
|
|
776
|
+
warnSlot[MINT_WARNED_SLOT] = true;
|
|
777
|
+
}
|
|
778
|
+
return minted;
|
|
779
|
+
};
|
|
780
|
+
|
|
751
781
|
// src/reviews/transport.ts
|
|
752
782
|
var submitReview = (target, review) => {
|
|
753
783
|
if (!(target == null ? void 0 : target.serverUrl)) return;
|
|
@@ -785,13 +815,15 @@ var fetchReviewDecision = async (target, options = {}) => {
|
|
|
785
815
|
}
|
|
786
816
|
};
|
|
787
817
|
var reportAppEvent = (target, name, options = {}) => {
|
|
818
|
+
var _a;
|
|
788
819
|
if (!(target == null ? void 0 : target.serverUrl) || !name) return;
|
|
789
820
|
try {
|
|
821
|
+
const supplied = (_a = options.sessionId) != null ? _a : "";
|
|
790
822
|
const event = {
|
|
791
823
|
event_type: "app_event",
|
|
792
|
-
question_key: name
|
|
824
|
+
question_key: name,
|
|
825
|
+
session_id: supplied.trim().length > 0 ? supplied : ensureCurrentSessionId()
|
|
793
826
|
};
|
|
794
|
-
if (options.sessionId) event.session_id = options.sessionId;
|
|
795
827
|
if (options.deviceKey) event.user_context = { device_key: options.deviceKey };
|
|
796
828
|
if (options.meta && Object.keys(options.meta).length > 0) {
|
|
797
829
|
event.meta = JSON.stringify(options.meta);
|
|
@@ -1055,14 +1087,21 @@ var styles6 = reactNative.StyleSheet.create({
|
|
|
1055
1087
|
});
|
|
1056
1088
|
|
|
1057
1089
|
// src/coachmarks/runtime.ts
|
|
1058
|
-
var
|
|
1059
|
-
var
|
|
1060
|
-
var
|
|
1061
|
-
|
|
1090
|
+
var COACHMARK_RUNTIME_SLOT = /* @__PURE__ */ Symbol.for("@wireai/activation:coachmarkRuntime");
|
|
1091
|
+
var runtimeGlobal = globalThis;
|
|
1092
|
+
var coachmarkRuntime = () => {
|
|
1093
|
+
const existing = runtimeGlobal[COACHMARK_RUNTIME_SLOT];
|
|
1094
|
+
if (existing) return existing;
|
|
1095
|
+
const created = { storage: null, testing: false, coachmarksEnabled: true };
|
|
1096
|
+
runtimeGlobal[COACHMARK_RUNTIME_SLOT] = created;
|
|
1097
|
+
return created;
|
|
1098
|
+
};
|
|
1099
|
+
var getCoachmarkStorage = () => coachmarkRuntime().storage;
|
|
1100
|
+
var isCoachmarkTesting = () => coachmarkRuntime().testing;
|
|
1062
1101
|
var SEEN_VALUE = "1";
|
|
1063
1102
|
var hasSeenGate = (key, storageOverride, testingOverride) => {
|
|
1064
|
-
if (testingOverride != null ? testingOverride :
|
|
1065
|
-
const storage = storageOverride != null ? storageOverride :
|
|
1103
|
+
if (testingOverride != null ? testingOverride : isCoachmarkTesting()) return false;
|
|
1104
|
+
const storage = storageOverride != null ? storageOverride : getCoachmarkStorage();
|
|
1066
1105
|
if (!storage) return false;
|
|
1067
1106
|
try {
|
|
1068
1107
|
return storage.getItem(key) === SEEN_VALUE;
|
|
@@ -1071,8 +1110,8 @@ var hasSeenGate = (key, storageOverride, testingOverride) => {
|
|
|
1071
1110
|
}
|
|
1072
1111
|
};
|
|
1073
1112
|
var markSeenGate = (key, storageOverride, testingOverride) => {
|
|
1074
|
-
if (testingOverride != null ? testingOverride :
|
|
1075
|
-
const storage = storageOverride != null ? storageOverride :
|
|
1113
|
+
if (testingOverride != null ? testingOverride : isCoachmarkTesting()) return;
|
|
1114
|
+
const storage = storageOverride != null ? storageOverride : getCoachmarkStorage();
|
|
1076
1115
|
if (!storage) return;
|
|
1077
1116
|
try {
|
|
1078
1117
|
storage.setItem(key, SEEN_VALUE);
|
|
@@ -1258,7 +1297,22 @@ var sameDecision = (a, b) => {
|
|
|
1258
1297
|
var reviewSeenKey = (id, version) => version ? `wire_review_${id}_${version}_seen` : `wire_review_${id}_seen`;
|
|
1259
1298
|
var reviewLastShownKey = (id) => `wire_review_${id}_last`;
|
|
1260
1299
|
var reviewSessionsKey = (id) => `wire_review_${id}_sessions`;
|
|
1300
|
+
var reviewSessionOpenKey = (id) => `wire_review_${id}_open`;
|
|
1261
1301
|
var resolveStorage = (override) => override != null ? override : getCoachmarkStorage();
|
|
1302
|
+
var NO_STORAGE_WARNED_SLOT = /* @__PURE__ */ Symbol.for(
|
|
1303
|
+
"@wireai/activation:gateStorageWarned"
|
|
1304
|
+
);
|
|
1305
|
+
var gateWarnGlobal = globalThis;
|
|
1306
|
+
var warnMissingGateStorage = (storage, gate) => {
|
|
1307
|
+
if (storage) return;
|
|
1308
|
+
if (gateWarnGlobal[NO_STORAGE_WARNED_SLOT]) return;
|
|
1309
|
+
if (typeof __DEV__ === "undefined" || !__DEV__) return;
|
|
1310
|
+
if (typeof console === "undefined" || !console.warn) return;
|
|
1311
|
+
gateWarnGlobal[NO_STORAGE_WARNED_SLOT] = true;
|
|
1312
|
+
console.warn(
|
|
1313
|
+
`[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.`
|
|
1314
|
+
);
|
|
1315
|
+
};
|
|
1262
1316
|
var readInt = (storage, key) => {
|
|
1263
1317
|
if (!storage) return 0;
|
|
1264
1318
|
try {
|
|
@@ -1276,6 +1330,39 @@ var writeInt = (storage, key, value) => {
|
|
|
1276
1330
|
} catch {
|
|
1277
1331
|
}
|
|
1278
1332
|
};
|
|
1333
|
+
var readStr = (storage, key) => {
|
|
1334
|
+
if (!storage) return void 0;
|
|
1335
|
+
try {
|
|
1336
|
+
const raw = storage.getItem(key);
|
|
1337
|
+
return typeof raw === "string" && raw.length > 0 ? raw : void 0;
|
|
1338
|
+
} catch {
|
|
1339
|
+
return void 0;
|
|
1340
|
+
}
|
|
1341
|
+
};
|
|
1342
|
+
var PROCESS_OPEN_ID_SLOT = /* @__PURE__ */ Symbol.for("@wireai/activation:processOpenId");
|
|
1343
|
+
var openIdGlobal = globalThis;
|
|
1344
|
+
var currentOpenId = () => {
|
|
1345
|
+
var _a;
|
|
1346
|
+
const existing = openIdGlobal[PROCESS_OPEN_ID_SLOT];
|
|
1347
|
+
if (existing) return existing;
|
|
1348
|
+
const resolved = (_a = getCurrentSessionId()) != null ? _a : makeSessionId();
|
|
1349
|
+
openIdGlobal[PROCESS_OPEN_ID_SLOT] = resolved;
|
|
1350
|
+
return resolved;
|
|
1351
|
+
};
|
|
1352
|
+
var bumpSessionCount = (storage, sessionsKey, openKey, openId = currentOpenId()) => {
|
|
1353
|
+
const lastOpen = readStr(storage, openKey);
|
|
1354
|
+
const stored = readInt(storage, sessionsKey);
|
|
1355
|
+
if (lastOpen === openId) return stored > 0 ? stored : 1;
|
|
1356
|
+
const next = stored + 1;
|
|
1357
|
+
writeInt(storage, sessionsKey, next);
|
|
1358
|
+
if (storage) {
|
|
1359
|
+
try {
|
|
1360
|
+
storage.setItem(openKey, openId);
|
|
1361
|
+
} catch {
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
return next;
|
|
1365
|
+
};
|
|
1279
1366
|
|
|
1280
1367
|
// src/reviews/useReviewGate.ts
|
|
1281
1368
|
var useStableValue2 = (value, isEqual) => {
|
|
@@ -1298,15 +1385,15 @@ var useReviewGate = ({
|
|
|
1298
1385
|
(a, b) => sameDecision(a, b)
|
|
1299
1386
|
);
|
|
1300
1387
|
const featureEnabled = useResolvedFeatures({ flags: features, config: featuresConfig }).review.enabled;
|
|
1301
|
-
const
|
|
1388
|
+
const testing = isTesting != null ? isTesting : isCoachmarkTesting();
|
|
1302
1389
|
const seenKey = reviewSeenKey(config.id, config.oncePerVersion === false ? void 0 : config.appVersion);
|
|
1303
1390
|
const lastKey = reviewLastShownKey(config.id);
|
|
1304
1391
|
const sessionsKey = reviewSessionsKey(config.id);
|
|
1392
|
+
const sessionOpenKey = reviewSessionOpenKey(config.id);
|
|
1305
1393
|
const sessions = React7.useState(() => {
|
|
1306
1394
|
const store = resolveStorage(storage);
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
return next;
|
|
1395
|
+
warnMissingGateStorage(store, "review");
|
|
1396
|
+
return bumpSessionCount(store, sessionsKey, sessionOpenKey);
|
|
1310
1397
|
})[0];
|
|
1311
1398
|
const hasServerDecision = decision != null;
|
|
1312
1399
|
const timeoutMs = config.timeoutFallbackMs;
|
|
@@ -1322,7 +1409,7 @@ var useReviewGate = ({
|
|
|
1322
1409
|
}, [waits, timeoutMs]);
|
|
1323
1410
|
const { visible, verdict } = React7.useMemo(() => {
|
|
1324
1411
|
if (!featureEnabled) return { visible: false, verdict: { fire: false, reason: "feature_disabled" } };
|
|
1325
|
-
if (
|
|
1412
|
+
if (testing) return { visible: true, verdict: { fire: true, reason: "testing" } };
|
|
1326
1413
|
const store = resolveStorage(storage);
|
|
1327
1414
|
const local = evaluateGate(resolveRules(config), {
|
|
1328
1415
|
sessions,
|
|
@@ -1335,7 +1422,7 @@ var useReviewGate = ({
|
|
|
1335
1422
|
const alreadySeen = hasSeenGate(seenKey, storage, isTesting);
|
|
1336
1423
|
const ready = hasServerDecision || elapsed;
|
|
1337
1424
|
return { visible: ready && resolved.fire && !alreadySeen, verdict: resolved };
|
|
1338
|
-
}, [featureEnabled,
|
|
1425
|
+
}, [featureEnabled, testing, config, decision, events, sessions, elapsed, hasServerDecision, seenKey, lastKey, storage, isTesting]);
|
|
1339
1426
|
const shownRef = React7.useRef(false);
|
|
1340
1427
|
const markShown = React7.useCallback(() => {
|
|
1341
1428
|
if (shownRef.current) return;
|