@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/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
|
@@ -504,7 +504,14 @@ var resolveRules = (config) => {
|
|
|
504
504
|
var _a, _b, _c, _d, _e;
|
|
505
505
|
return {
|
|
506
506
|
enabled: (_a = config.enabled) != null ? _a : true,
|
|
507
|
-
|
|
507
|
+
// FAIL-CLOSED on the first session (behavior change, 0.11.0). An unconfigured host with no
|
|
508
|
+
// server decision must NOT prompt on the very first mount: `minSessions` defaults to 2 so the
|
|
509
|
+
// gate needs at least a second session before the LOCAL rules can fire. This is the client-side
|
|
510
|
+
// floor only — a server `decision` (from `fetchReviewDecision`) still OVERRIDES everything, and a
|
|
511
|
+
// host that genuinely wants first-session prompting can set `minSessions: 1` (or `0`) explicitly.
|
|
512
|
+
// Mirrors the 2026-07-16 incident: a first-session user, no server decision, got the prompt and
|
|
513
|
+
// left 1 star. See `evaluateGate` (`min_sessions` reason) and the CHANGELOG.
|
|
514
|
+
minSessions: (_b = config.minSessions) != null ? _b : 2,
|
|
508
515
|
minEvents: (_c = config.minEvents) != null ? _c : 0,
|
|
509
516
|
cooldownDays: (_d = config.cooldownDays) != null ? _d : 0,
|
|
510
517
|
oncePerVersion: (_e = config.oncePerVersion) != null ? _e : true
|
|
@@ -728,6 +735,49 @@ var requestStoreReview = async (store) => {
|
|
|
728
735
|
return opened ? "store_url" : "none";
|
|
729
736
|
};
|
|
730
737
|
|
|
738
|
+
// src/analytics/reportClientEvent.ts
|
|
739
|
+
var makeSessionId = () => `wire_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
740
|
+
var buildEventsRequest = (target, events) => {
|
|
741
|
+
if (!(target == null ? void 0 : target.serverUrl) || events.length === 0) return null;
|
|
742
|
+
try {
|
|
743
|
+
const url = `${target.serverUrl.replace(/\/$/, "")}/v1/events`;
|
|
744
|
+
const headers = { "Content-Type": "application/json" };
|
|
745
|
+
if (target.apiKey) headers.Authorization = `Bearer ${target.apiKey}`;
|
|
746
|
+
return { url, init: { method: "POST", headers, body: JSON.stringify({ events }) } };
|
|
747
|
+
} catch {
|
|
748
|
+
return null;
|
|
749
|
+
}
|
|
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
|
+
|
|
731
781
|
// src/reviews/transport.ts
|
|
732
782
|
var submitReview = (target, review) => {
|
|
733
783
|
if (!(target == null ? void 0 : target.serverUrl)) return;
|
|
@@ -765,25 +815,22 @@ var fetchReviewDecision = async (target, options = {}) => {
|
|
|
765
815
|
}
|
|
766
816
|
};
|
|
767
817
|
var reportAppEvent = (target, name, options = {}) => {
|
|
818
|
+
var _a;
|
|
768
819
|
if (!(target == null ? void 0 : target.serverUrl) || !name) return;
|
|
769
820
|
try {
|
|
770
|
-
const
|
|
771
|
-
const headers = { "Content-Type": "application/json" };
|
|
772
|
-
if (target.apiKey) headers.Authorization = `Bearer ${target.apiKey}`;
|
|
821
|
+
const supplied = (_a = options.sessionId) != null ? _a : "";
|
|
773
822
|
const event = {
|
|
774
823
|
event_type: "app_event",
|
|
775
|
-
question_key: name
|
|
824
|
+
question_key: name,
|
|
825
|
+
session_id: supplied.trim().length > 0 ? supplied : ensureCurrentSessionId()
|
|
776
826
|
};
|
|
777
|
-
if (options.sessionId) event.session_id = options.sessionId;
|
|
778
827
|
if (options.deviceKey) event.user_context = { device_key: options.deviceKey };
|
|
779
828
|
if (options.meta && Object.keys(options.meta).length > 0) {
|
|
780
829
|
event.meta = JSON.stringify(options.meta);
|
|
781
830
|
}
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
body: JSON.stringify({ events: [event] })
|
|
786
|
-
}).catch(() => {
|
|
831
|
+
const req = buildEventsRequest(target, [event]);
|
|
832
|
+
if (!req) return;
|
|
833
|
+
void fetch(req.url, req.init).catch(() => {
|
|
787
834
|
});
|
|
788
835
|
} catch {
|
|
789
836
|
}
|
|
@@ -1243,6 +1290,7 @@ var sameDecision = (a, b) => {
|
|
|
1243
1290
|
var reviewSeenKey = (id, version) => version ? `wire_review_${id}_${version}_seen` : `wire_review_${id}_seen`;
|
|
1244
1291
|
var reviewLastShownKey = (id) => `wire_review_${id}_last`;
|
|
1245
1292
|
var reviewSessionsKey = (id) => `wire_review_${id}_sessions`;
|
|
1293
|
+
var reviewSessionOpenKey = (id) => `wire_review_${id}_open`;
|
|
1246
1294
|
var resolveStorage = (override) => override != null ? override : getCoachmarkStorage();
|
|
1247
1295
|
var readInt = (storage, key) => {
|
|
1248
1296
|
if (!storage) return 0;
|
|
@@ -1261,6 +1309,40 @@ var writeInt = (storage, key, value) => {
|
|
|
1261
1309
|
} catch {
|
|
1262
1310
|
}
|
|
1263
1311
|
};
|
|
1312
|
+
var readStr = (storage, key) => {
|
|
1313
|
+
if (!storage) return void 0;
|
|
1314
|
+
try {
|
|
1315
|
+
const raw = storage.getItem(key);
|
|
1316
|
+
return typeof raw === "string" && raw.length > 0 ? raw : void 0;
|
|
1317
|
+
} catch {
|
|
1318
|
+
return void 0;
|
|
1319
|
+
}
|
|
1320
|
+
};
|
|
1321
|
+
var PROCESS_OPEN_ID_SLOT = /* @__PURE__ */ Symbol.for("@wireai/activation:processOpenId");
|
|
1322
|
+
var openIdGlobal = globalThis;
|
|
1323
|
+
var currentOpenId = () => {
|
|
1324
|
+
const live = getCurrentSessionId();
|
|
1325
|
+
if (live) return live;
|
|
1326
|
+
const existing = openIdGlobal[PROCESS_OPEN_ID_SLOT];
|
|
1327
|
+
if (existing) return existing;
|
|
1328
|
+
const created = makeSessionId();
|
|
1329
|
+
openIdGlobal[PROCESS_OPEN_ID_SLOT] = created;
|
|
1330
|
+
return created;
|
|
1331
|
+
};
|
|
1332
|
+
var bumpSessionCount = (storage, sessionsKey, openKey, openId = currentOpenId()) => {
|
|
1333
|
+
const lastOpen = readStr(storage, openKey);
|
|
1334
|
+
const stored = readInt(storage, sessionsKey);
|
|
1335
|
+
if (lastOpen === openId) return stored > 0 ? stored : 1;
|
|
1336
|
+
const next = stored + 1;
|
|
1337
|
+
writeInt(storage, sessionsKey, next);
|
|
1338
|
+
if (storage) {
|
|
1339
|
+
try {
|
|
1340
|
+
storage.setItem(openKey, openId);
|
|
1341
|
+
} catch {
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
return next;
|
|
1345
|
+
};
|
|
1264
1346
|
|
|
1265
1347
|
// src/reviews/useReviewGate.ts
|
|
1266
1348
|
var useStableValue2 = (value, isEqual) => {
|
|
@@ -1287,12 +1369,10 @@ var useReviewGate = ({
|
|
|
1287
1369
|
const seenKey = reviewSeenKey(config.id, config.oncePerVersion === false ? void 0 : config.appVersion);
|
|
1288
1370
|
const lastKey = reviewLastShownKey(config.id);
|
|
1289
1371
|
const sessionsKey = reviewSessionsKey(config.id);
|
|
1290
|
-
const
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
return next;
|
|
1295
|
-
})[0];
|
|
1372
|
+
const sessionOpenKey = reviewSessionOpenKey(config.id);
|
|
1373
|
+
const sessions = React7.useState(
|
|
1374
|
+
() => bumpSessionCount(resolveStorage(storage), sessionsKey, sessionOpenKey)
|
|
1375
|
+
)[0];
|
|
1296
1376
|
const hasServerDecision = decision != null;
|
|
1297
1377
|
const timeoutMs = config.timeoutFallbackMs;
|
|
1298
1378
|
const waits = !hasServerDecision && timeoutMs != null && timeoutMs > 0;
|