@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.mjs
CHANGED
|
@@ -498,7 +498,14 @@ var resolveRules = (config) => {
|
|
|
498
498
|
var _a, _b, _c, _d, _e;
|
|
499
499
|
return {
|
|
500
500
|
enabled: (_a = config.enabled) != null ? _a : true,
|
|
501
|
-
|
|
501
|
+
// FAIL-CLOSED on the first session (behavior change, 0.11.0). An unconfigured host with no
|
|
502
|
+
// server decision must NOT prompt on the very first mount: `minSessions` defaults to 2 so the
|
|
503
|
+
// gate needs at least a second session before the LOCAL rules can fire. This is the client-side
|
|
504
|
+
// floor only — a server `decision` (from `fetchReviewDecision`) still OVERRIDES everything, and a
|
|
505
|
+
// host that genuinely wants first-session prompting can set `minSessions: 1` (or `0`) explicitly.
|
|
506
|
+
// Mirrors the 2026-07-16 incident: a first-session user, no server decision, got the prompt and
|
|
507
|
+
// left 1 star. See `evaluateGate` (`min_sessions` reason) and the CHANGELOG.
|
|
508
|
+
minSessions: (_b = config.minSessions) != null ? _b : 2,
|
|
502
509
|
minEvents: (_c = config.minEvents) != null ? _c : 0,
|
|
503
510
|
cooldownDays: (_d = config.cooldownDays) != null ? _d : 0,
|
|
504
511
|
oncePerVersion: (_e = config.oncePerVersion) != null ? _e : true
|
|
@@ -722,6 +729,49 @@ var requestStoreReview = async (store) => {
|
|
|
722
729
|
return opened ? "store_url" : "none";
|
|
723
730
|
};
|
|
724
731
|
|
|
732
|
+
// src/analytics/reportClientEvent.ts
|
|
733
|
+
var makeSessionId = () => `wire_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
734
|
+
var buildEventsRequest = (target, events) => {
|
|
735
|
+
if (!(target == null ? void 0 : target.serverUrl) || events.length === 0) return null;
|
|
736
|
+
try {
|
|
737
|
+
const url = `${target.serverUrl.replace(/\/$/, "")}/v1/events`;
|
|
738
|
+
const headers = { "Content-Type": "application/json" };
|
|
739
|
+
if (target.apiKey) headers.Authorization = `Bearer ${target.apiKey}`;
|
|
740
|
+
return { url, init: { method: "POST", headers, body: JSON.stringify({ events }) } };
|
|
741
|
+
} catch {
|
|
742
|
+
return null;
|
|
743
|
+
}
|
|
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
|
+
|
|
725
775
|
// src/reviews/transport.ts
|
|
726
776
|
var submitReview = (target, review) => {
|
|
727
777
|
if (!(target == null ? void 0 : target.serverUrl)) return;
|
|
@@ -759,25 +809,22 @@ var fetchReviewDecision = async (target, options = {}) => {
|
|
|
759
809
|
}
|
|
760
810
|
};
|
|
761
811
|
var reportAppEvent = (target, name, options = {}) => {
|
|
812
|
+
var _a;
|
|
762
813
|
if (!(target == null ? void 0 : target.serverUrl) || !name) return;
|
|
763
814
|
try {
|
|
764
|
-
const
|
|
765
|
-
const headers = { "Content-Type": "application/json" };
|
|
766
|
-
if (target.apiKey) headers.Authorization = `Bearer ${target.apiKey}`;
|
|
815
|
+
const supplied = (_a = options.sessionId) != null ? _a : "";
|
|
767
816
|
const event = {
|
|
768
817
|
event_type: "app_event",
|
|
769
|
-
question_key: name
|
|
818
|
+
question_key: name,
|
|
819
|
+
session_id: supplied.trim().length > 0 ? supplied : ensureCurrentSessionId()
|
|
770
820
|
};
|
|
771
|
-
if (options.sessionId) event.session_id = options.sessionId;
|
|
772
821
|
if (options.deviceKey) event.user_context = { device_key: options.deviceKey };
|
|
773
822
|
if (options.meta && Object.keys(options.meta).length > 0) {
|
|
774
823
|
event.meta = JSON.stringify(options.meta);
|
|
775
824
|
}
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
body: JSON.stringify({ events: [event] })
|
|
780
|
-
}).catch(() => {
|
|
825
|
+
const req = buildEventsRequest(target, [event]);
|
|
826
|
+
if (!req) return;
|
|
827
|
+
void fetch(req.url, req.init).catch(() => {
|
|
781
828
|
});
|
|
782
829
|
} catch {
|
|
783
830
|
}
|
|
@@ -1237,6 +1284,7 @@ var sameDecision = (a, b) => {
|
|
|
1237
1284
|
var reviewSeenKey = (id, version) => version ? `wire_review_${id}_${version}_seen` : `wire_review_${id}_seen`;
|
|
1238
1285
|
var reviewLastShownKey = (id) => `wire_review_${id}_last`;
|
|
1239
1286
|
var reviewSessionsKey = (id) => `wire_review_${id}_sessions`;
|
|
1287
|
+
var reviewSessionOpenKey = (id) => `wire_review_${id}_open`;
|
|
1240
1288
|
var resolveStorage = (override) => override != null ? override : getCoachmarkStorage();
|
|
1241
1289
|
var readInt = (storage, key) => {
|
|
1242
1290
|
if (!storage) return 0;
|
|
@@ -1255,6 +1303,40 @@ var writeInt = (storage, key, value) => {
|
|
|
1255
1303
|
} catch {
|
|
1256
1304
|
}
|
|
1257
1305
|
};
|
|
1306
|
+
var readStr = (storage, key) => {
|
|
1307
|
+
if (!storage) return void 0;
|
|
1308
|
+
try {
|
|
1309
|
+
const raw = storage.getItem(key);
|
|
1310
|
+
return typeof raw === "string" && raw.length > 0 ? raw : void 0;
|
|
1311
|
+
} catch {
|
|
1312
|
+
return void 0;
|
|
1313
|
+
}
|
|
1314
|
+
};
|
|
1315
|
+
var PROCESS_OPEN_ID_SLOT = /* @__PURE__ */ Symbol.for("@wireai/activation:processOpenId");
|
|
1316
|
+
var openIdGlobal = globalThis;
|
|
1317
|
+
var currentOpenId = () => {
|
|
1318
|
+
const live = getCurrentSessionId();
|
|
1319
|
+
if (live) return live;
|
|
1320
|
+
const existing = openIdGlobal[PROCESS_OPEN_ID_SLOT];
|
|
1321
|
+
if (existing) return existing;
|
|
1322
|
+
const created = makeSessionId();
|
|
1323
|
+
openIdGlobal[PROCESS_OPEN_ID_SLOT] = created;
|
|
1324
|
+
return created;
|
|
1325
|
+
};
|
|
1326
|
+
var bumpSessionCount = (storage, sessionsKey, openKey, openId = currentOpenId()) => {
|
|
1327
|
+
const lastOpen = readStr(storage, openKey);
|
|
1328
|
+
const stored = readInt(storage, sessionsKey);
|
|
1329
|
+
if (lastOpen === openId) return stored > 0 ? stored : 1;
|
|
1330
|
+
const next = stored + 1;
|
|
1331
|
+
writeInt(storage, sessionsKey, next);
|
|
1332
|
+
if (storage) {
|
|
1333
|
+
try {
|
|
1334
|
+
storage.setItem(openKey, openId);
|
|
1335
|
+
} catch {
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
return next;
|
|
1339
|
+
};
|
|
1258
1340
|
|
|
1259
1341
|
// src/reviews/useReviewGate.ts
|
|
1260
1342
|
var useStableValue2 = (value, isEqual) => {
|
|
@@ -1281,12 +1363,10 @@ var useReviewGate = ({
|
|
|
1281
1363
|
const seenKey = reviewSeenKey(config.id, config.oncePerVersion === false ? void 0 : config.appVersion);
|
|
1282
1364
|
const lastKey = reviewLastShownKey(config.id);
|
|
1283
1365
|
const sessionsKey = reviewSessionsKey(config.id);
|
|
1284
|
-
const
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
return next;
|
|
1289
|
-
})[0];
|
|
1366
|
+
const sessionOpenKey = reviewSessionOpenKey(config.id);
|
|
1367
|
+
const sessions = useState(
|
|
1368
|
+
() => bumpSessionCount(resolveStorage(storage), sessionsKey, sessionOpenKey)
|
|
1369
|
+
)[0];
|
|
1290
1370
|
const hasServerDecision = decision != null;
|
|
1291
1371
|
const timeoutMs = config.timeoutFallbackMs;
|
|
1292
1372
|
const waits = !hasServerDecision && timeoutMs != null && timeoutMs > 0;
|