@wireai/activation 0.14.2 → 0.15.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 +1 -1
- package/CHANGELOG.md +144 -0
- package/README.md +23 -9
- package/dist/analytics/index.d.mts +5 -5
- package/dist/analytics/index.d.ts +5 -5
- package/dist/analytics/index.js.map +1 -1
- package/dist/analytics/index.mjs.map +1 -1
- package/dist/coachmarks/index.d.mts +13 -5
- package/dist/coachmarks/index.d.ts +13 -5
- package/dist/coachmarks/index.js +252 -58
- package/dist/coachmarks/index.js.map +1 -1
- package/dist/coachmarks/index.mjs +204 -9
- package/dist/coachmarks/index.mjs.map +1 -1
- package/dist/{currentSession-CW_5Mq4O.d.ts → currentSession-CFSRZ2wg.d.ts} +10 -4
- package/dist/{currentSession-CUvTOchb.d.mts → currentSession-orZy5p1e.d.mts} +10 -4
- package/dist/{decision-Bgo17oH7.d.mts → decision-3vWLuBlO.d.ts} +11 -2
- package/dist/{decision-Bkh_LigV.d.ts → decision-yBj2AyPW.d.mts} +11 -2
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/questionnaire/index.d.mts +3 -3
- package/dist/questionnaire/index.d.ts +3 -3
- package/dist/questionnaire/index.js +48 -3
- package/dist/questionnaire/index.js.map +1 -1
- package/dist/questionnaire/index.mjs +48 -3
- package/dist/questionnaire/index.mjs.map +1 -1
- package/dist/reviews/index.d.mts +10 -6
- package/dist/reviews/index.d.ts +10 -6
- package/dist/reviews/index.js +167 -18
- package/dist/reviews/index.js.map +1 -1
- package/dist/reviews/index.mjs +167 -18
- package/dist/reviews/index.mjs.map +1 -1
- package/dist/showcase/index.d.mts +1 -1
- package/dist/showcase/index.d.ts +1 -1
- package/dist/showcase/index.js +282 -48
- package/dist/showcase/index.js.map +1 -1
- package/dist/showcase/index.mjs +248 -12
- package/dist/showcase/index.mjs.map +1 -1
- package/dist/{transport-j5gFfJhK.d.mts → transport-s5QxA-ci.d.mts} +18 -13
- package/dist/{transport-B_0SgCBe.d.ts → transport-xqqSFqxs.d.ts} +18 -13
- package/dist/{types-Cju-1_jT.d.mts → types-Byx306Kv.d.mts} +25 -10
- package/dist/{types-BcmagF6K.d.mts → types-D_0B0yay.d.mts} +7 -2
- package/dist/{types-BcmagF6K.d.ts → types-D_0B0yay.d.ts} +7 -2
- package/dist/{types-h2BZvl1t.d.ts → types-tdATL5z0.d.ts} +25 -10
- package/metro/index.js +16 -5
- package/package.json +1 -1
- package/src/WireOnboarding.tsx +4 -1
- package/src/coachmarks/GestureHint.tsx +16 -7
- package/src/coachmarks/SpotlightOverlay.tsx +19 -13
- package/src/coachmarks/index.ts +7 -2
- package/src/coachmarks/reanimated.ts +342 -0
- package/src/coachmarks/runtime.ts +103 -2
- package/src/coachmarks/types.ts +7 -2
- package/src/context/deviceId.ts +32 -0
- package/src/reviews/ReviewGate.tsx +86 -37
- package/src/reviews/decision.ts +11 -1
- package/src/reviews/idempotency.ts +71 -0
- package/src/reviews/index.ts +4 -2
- package/src/reviews/runtime.ts +5 -3
- package/src/reviews/transport.ts +17 -12
- package/src/reviews/types.ts +7 -0
- package/src/showcase/FeatureShowcase.tsx +25 -9
- package/src/showcase/blazejOnboarding.ts +136 -0
- package/src/showcase/index.ts +8 -4
- package/src/types.ts +10 -4
- package/src/utils/submitResult.ts +17 -9
package/dist/reviews/index.mjs
CHANGED
|
@@ -540,6 +540,7 @@ var buildReviewSubmission = (input) => {
|
|
|
540
540
|
if (contact) body.contact = contact;
|
|
541
541
|
if (input.sessionId) body.session_id = input.sessionId;
|
|
542
542
|
if (input.source) body.source = input.source;
|
|
543
|
+
if (input.idempotencyKey) body.idempotency_key = input.idempotencyKey;
|
|
543
544
|
if (input.meta && Object.keys(input.meta).length > 0) body.meta = input.meta;
|
|
544
545
|
return body;
|
|
545
546
|
};
|
|
@@ -557,6 +558,96 @@ var feedbackInputHeights = (fonts, verticalPadding = 0) => {
|
|
|
557
558
|
};
|
|
558
559
|
};
|
|
559
560
|
|
|
561
|
+
// src/context/deviceId.ts
|
|
562
|
+
var AUTO_DEVICE_ID_PREFIX = "wdev_";
|
|
563
|
+
var deviceIdStorageKey = (appId) => `wireai:analytics:deviceKey:${appId != null ? appId : "default"}`;
|
|
564
|
+
var randomChunk = () => Math.floor(Math.random() * 4294967296).toString(36).padStart(6, "0");
|
|
565
|
+
var mintDeviceId = () => {
|
|
566
|
+
const time = Date.now().toString(36);
|
|
567
|
+
return `${AUTO_DEVICE_ID_PREFIX}${time}_${randomChunk()}${randomChunk()}`;
|
|
568
|
+
};
|
|
569
|
+
var AUTO_DEVICE_KEY_SLOT = /* @__PURE__ */ Symbol.for("@wireai/activation:autoDeviceKeys");
|
|
570
|
+
var deviceKeyGlobal = globalThis;
|
|
571
|
+
var autoDeviceKeyRegistry = () => {
|
|
572
|
+
const existing = deviceKeyGlobal[AUTO_DEVICE_KEY_SLOT];
|
|
573
|
+
if (existing) return existing;
|
|
574
|
+
const created = { keys: /* @__PURE__ */ new Map(), hydrating: /* @__PURE__ */ new Set() };
|
|
575
|
+
deviceKeyGlobal[AUTO_DEVICE_KEY_SLOT] = created;
|
|
576
|
+
return created;
|
|
577
|
+
};
|
|
578
|
+
var startHydration = (registry, appId, storage, minted) => {
|
|
579
|
+
if (!registry.pending) registry.pending = /* @__PURE__ */ new Map();
|
|
580
|
+
const existing = registry.pending.get(appId);
|
|
581
|
+
if (existing) return existing;
|
|
582
|
+
const slot = deviceIdStorageKey(appId);
|
|
583
|
+
const degraded = () => {
|
|
584
|
+
var _a;
|
|
585
|
+
return { value: (_a = registry.keys.get(appId)) != null ? _a : minted, durable: false };
|
|
586
|
+
};
|
|
587
|
+
const adopted = (value) => ({ value, durable: true });
|
|
588
|
+
let run;
|
|
589
|
+
try {
|
|
590
|
+
run = Promise.resolve(storage.getItem(slot)).then((saved) => {
|
|
591
|
+
const persisted = typeof saved === "string" && saved.trim() ? saved.trim() : void 0;
|
|
592
|
+
if (persisted) {
|
|
593
|
+
registry.keys.set(appId, persisted);
|
|
594
|
+
return adopted(persisted);
|
|
595
|
+
}
|
|
596
|
+
return Promise.resolve(storage.setItem(slot, minted)).then(
|
|
597
|
+
() => {
|
|
598
|
+
var _a;
|
|
599
|
+
return adopted((_a = registry.keys.get(appId)) != null ? _a : minted);
|
|
600
|
+
},
|
|
601
|
+
degraded
|
|
602
|
+
);
|
|
603
|
+
}).catch(degraded);
|
|
604
|
+
} catch {
|
|
605
|
+
run = Promise.resolve(degraded());
|
|
606
|
+
}
|
|
607
|
+
registry.pending.set(appId, run);
|
|
608
|
+
void run.then((outcome) => {
|
|
609
|
+
var _a;
|
|
610
|
+
if (outcome.durable) return;
|
|
611
|
+
(_a = registry.pending) == null ? void 0 : _a.delete(appId);
|
|
612
|
+
registry.hydrating.delete(appId);
|
|
613
|
+
});
|
|
614
|
+
return run;
|
|
615
|
+
};
|
|
616
|
+
var resolveAutoDeviceKey = (opts = {}) => {
|
|
617
|
+
var _a, _b;
|
|
618
|
+
const registry = autoDeviceKeyRegistry();
|
|
619
|
+
const appId = (_a = opts.appId) != null ? _a : "default";
|
|
620
|
+
let id = registry.keys.get(appId);
|
|
621
|
+
if (!id) {
|
|
622
|
+
id = mintDeviceId();
|
|
623
|
+
registry.keys.set(appId, id);
|
|
624
|
+
}
|
|
625
|
+
const storage = opts.storage;
|
|
626
|
+
if (storage && !registry.hydrating.has(appId)) {
|
|
627
|
+
registry.hydrating.add(appId);
|
|
628
|
+
void startHydration(registry, appId, storage, id);
|
|
629
|
+
}
|
|
630
|
+
return (_b = registry.keys.get(appId)) != null ? _b : id;
|
|
631
|
+
};
|
|
632
|
+
var ambientAutoDeviceKey = () => {
|
|
633
|
+
const registry = autoDeviceKeyRegistry();
|
|
634
|
+
if (registry.keys.size === 0) return resolveAutoDeviceKey();
|
|
635
|
+
if (registry.keys.size > 1) return void 0;
|
|
636
|
+
return [...registry.keys.values()][0];
|
|
637
|
+
};
|
|
638
|
+
|
|
639
|
+
// src/reviews/idempotency.ts
|
|
640
|
+
var REVIEW_IDEMPOTENCY_PREFIX = "rvw_";
|
|
641
|
+
var mintIdempotencyKey = () => `${REVIEW_IDEMPOTENCY_PREFIX}${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
642
|
+
var hasDeviceKey = (meta) => {
|
|
643
|
+
const value = meta == null ? void 0 : meta.device_key;
|
|
644
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
645
|
+
};
|
|
646
|
+
var createImpression = () => ({
|
|
647
|
+
idempotencyKey: mintIdempotencyKey(),
|
|
648
|
+
deviceKey: ambientAutoDeviceKey()
|
|
649
|
+
});
|
|
650
|
+
|
|
560
651
|
// src/motion/centeredModalMotion.ts
|
|
561
652
|
var CENTERED_MODAL_ANIM = {
|
|
562
653
|
/** Spring config for the card scale/opacity entrance (mirrors CompletionView's pop). */
|
|
@@ -895,6 +986,27 @@ var _ReviewGate = ({
|
|
|
895
986
|
const resolve = useCallback(() => {
|
|
896
987
|
onResolved == null ? void 0 : onResolved();
|
|
897
988
|
}, [onResolved]);
|
|
989
|
+
const impressionRef = React7.useRef(null);
|
|
990
|
+
const buildBody = useCallback(
|
|
991
|
+
(input) => {
|
|
992
|
+
let impression = impressionRef.current;
|
|
993
|
+
if (!impression) {
|
|
994
|
+
impression = createImpression();
|
|
995
|
+
impressionRef.current = impression;
|
|
996
|
+
}
|
|
997
|
+
const merged = { ...meta, ...input.extraMeta };
|
|
998
|
+
if (!hasDeviceKey(merged) && impression.deviceKey) merged.device_key = impression.deviceKey;
|
|
999
|
+
return buildReviewSubmission({
|
|
1000
|
+
stars: input.stars,
|
|
1001
|
+
feedbackText: input.feedbackText,
|
|
1002
|
+
suggestion: input.suggestion,
|
|
1003
|
+
sessionId,
|
|
1004
|
+
meta: merged,
|
|
1005
|
+
idempotencyKey: impression.idempotencyKey
|
|
1006
|
+
});
|
|
1007
|
+
},
|
|
1008
|
+
[meta, sessionId]
|
|
1009
|
+
);
|
|
898
1010
|
const postedRef = React7.useRef(false);
|
|
899
1011
|
const unackedRef = React7.useRef(null);
|
|
900
1012
|
const postOnce = useCallback(
|
|
@@ -922,12 +1034,11 @@ var _ReviewGate = ({
|
|
|
922
1034
|
}
|
|
923
1035
|
if (postedRef.current || stars < 1) return;
|
|
924
1036
|
postOnce(
|
|
925
|
-
|
|
1037
|
+
buildBody({
|
|
926
1038
|
stars,
|
|
927
1039
|
feedbackText: feedback,
|
|
928
1040
|
suggestion,
|
|
929
|
-
|
|
930
|
-
meta: { ...meta, abandoned: true }
|
|
1041
|
+
extraMeta: { abandoned: true }
|
|
931
1042
|
})
|
|
932
1043
|
);
|
|
933
1044
|
};
|
|
@@ -952,10 +1063,9 @@ var _ReviewGate = ({
|
|
|
952
1063
|
onEvent == null ? void 0 : onEvent({ name: "store_review_requested", id, stars: value });
|
|
953
1064
|
const route = await requestStoreReview(store);
|
|
954
1065
|
postOnce(
|
|
955
|
-
|
|
1066
|
+
buildBody({
|
|
956
1067
|
stars: value,
|
|
957
|
-
|
|
958
|
-
meta: { ...meta, store_route: route }
|
|
1068
|
+
extraMeta: { store_route: route }
|
|
959
1069
|
})
|
|
960
1070
|
);
|
|
961
1071
|
setPhase("thanks");
|
|
@@ -964,24 +1074,18 @@ var _ReviewGate = ({
|
|
|
964
1074
|
setPhase("feedback");
|
|
965
1075
|
}
|
|
966
1076
|
},
|
|
967
|
-
[onEvent, id, store, postOnce,
|
|
1077
|
+
[onEvent, id, store, postOnce, buildBody, resolveWithDelay]
|
|
968
1078
|
);
|
|
969
1079
|
const feedbackReady = feedback.trim().length > 0;
|
|
970
1080
|
const sendFeedback = useCallback(() => {
|
|
971
1081
|
if (!feedbackReady) return;
|
|
972
|
-
const body =
|
|
973
|
-
stars,
|
|
974
|
-
feedbackText: feedback,
|
|
975
|
-
suggestion,
|
|
976
|
-
sessionId,
|
|
977
|
-
meta
|
|
978
|
-
});
|
|
1082
|
+
const body = buildBody({ stars, feedbackText: feedback, suggestion });
|
|
979
1083
|
postOnce(body);
|
|
980
1084
|
onEvent == null ? void 0 : onEvent({ name: "review_feedback_submitted", id, stars });
|
|
981
1085
|
playHaptic("success");
|
|
982
1086
|
setPhase("thanks");
|
|
983
1087
|
resolveWithDelay();
|
|
984
|
-
}, [feedbackReady, stars, feedback, suggestion,
|
|
1088
|
+
}, [feedbackReady, stars, feedback, suggestion, buildBody, postOnce, onEvent, id, resolveWithDelay]);
|
|
985
1089
|
const dismiss = useCallback(() => {
|
|
986
1090
|
setPhase("thanks");
|
|
987
1091
|
resolveWithDelay();
|
|
@@ -1128,9 +1232,54 @@ var coachmarkRuntime = () => {
|
|
|
1128
1232
|
var getCoachmarkStorage = () => coachmarkRuntime().storage;
|
|
1129
1233
|
var isCoachmarkTesting = () => coachmarkRuntime().testing;
|
|
1130
1234
|
var SEEN_VALUE = "1";
|
|
1235
|
+
var GATE_PROBE_KEY = "wire_gate_probe";
|
|
1236
|
+
var ASYNC_GATE_STORAGE_WARNED_SLOT = /* @__PURE__ */ Symbol.for(
|
|
1237
|
+
"@wireai/activation:asyncGateStorageWarned"
|
|
1238
|
+
);
|
|
1239
|
+
var asyncGateWarnGlobal = globalThis;
|
|
1240
|
+
var closedGateStorage = Object.freeze({
|
|
1241
|
+
getItem: () => SEEN_VALUE,
|
|
1242
|
+
setItem: () => {
|
|
1243
|
+
}
|
|
1244
|
+
});
|
|
1245
|
+
var syncGateAdapters = /* @__PURE__ */ new WeakSet();
|
|
1246
|
+
var asyncGateAdapters = /* @__PURE__ */ new WeakSet();
|
|
1247
|
+
var warnAsyncGateStorage = () => {
|
|
1248
|
+
if (asyncGateWarnGlobal[ASYNC_GATE_STORAGE_WARNED_SLOT]) return;
|
|
1249
|
+
if (typeof __DEV__ === "undefined" || !__DEV__) return;
|
|
1250
|
+
if (typeof console === "undefined" || !console.warn) return;
|
|
1251
|
+
asyncGateWarnGlobal[ASYNC_GATE_STORAGE_WARNED_SLOT] = true;
|
|
1252
|
+
console.warn(
|
|
1253
|
+
"[wireai] the gate storage's getItem returned a Promise \u2014 an async adapter (e.g. AsyncStorage) cannot drive the once-per-user gates, so coachmark tours, showcases and the review/questionnaire gates are all DISABLED (fail closed: nothing shows, nothing fires) until a synchronous adapter is provided. Pass a sync adapter (e.g. MMKV: `{ getItem: (k) => mmkv.getString(k) ?? null, setItem: (k, v) => mmkv.set(k, v) }`) to CoachmarkProvider or the gate's `storage` prop."
|
|
1254
|
+
);
|
|
1255
|
+
};
|
|
1256
|
+
var validateGateStorage = (storage) => {
|
|
1257
|
+
if (!storage) return null;
|
|
1258
|
+
if (syncGateAdapters.has(storage)) return storage;
|
|
1259
|
+
if (asyncGateAdapters.has(storage)) return closedGateStorage;
|
|
1260
|
+
let probed;
|
|
1261
|
+
try {
|
|
1262
|
+
probed = storage.getItem(GATE_PROBE_KEY);
|
|
1263
|
+
} catch {
|
|
1264
|
+
syncGateAdapters.add(storage);
|
|
1265
|
+
return storage;
|
|
1266
|
+
}
|
|
1267
|
+
if (probed != null && typeof probed.then === "function") {
|
|
1268
|
+
asyncGateAdapters.add(storage);
|
|
1269
|
+
try {
|
|
1270
|
+
probed.then(void 0, () => {
|
|
1271
|
+
});
|
|
1272
|
+
} catch {
|
|
1273
|
+
}
|
|
1274
|
+
warnAsyncGateStorage();
|
|
1275
|
+
return closedGateStorage;
|
|
1276
|
+
}
|
|
1277
|
+
syncGateAdapters.add(storage);
|
|
1278
|
+
return storage;
|
|
1279
|
+
};
|
|
1131
1280
|
var hasSeenGate = (key, storageOverride, testingOverride) => {
|
|
1132
1281
|
if (testingOverride != null ? testingOverride : isCoachmarkTesting()) return false;
|
|
1133
|
-
const storage = storageOverride != null ? storageOverride : getCoachmarkStorage();
|
|
1282
|
+
const storage = validateGateStorage(storageOverride != null ? storageOverride : getCoachmarkStorage());
|
|
1134
1283
|
if (!storage) return false;
|
|
1135
1284
|
try {
|
|
1136
1285
|
return storage.getItem(key) === SEEN_VALUE;
|
|
@@ -1140,7 +1289,7 @@ var hasSeenGate = (key, storageOverride, testingOverride) => {
|
|
|
1140
1289
|
};
|
|
1141
1290
|
var markSeenGate = (key, storageOverride, testingOverride) => {
|
|
1142
1291
|
if (testingOverride != null ? testingOverride : isCoachmarkTesting()) return;
|
|
1143
|
-
const storage = storageOverride != null ? storageOverride : getCoachmarkStorage();
|
|
1292
|
+
const storage = validateGateStorage(storageOverride != null ? storageOverride : getCoachmarkStorage());
|
|
1144
1293
|
if (!storage) return;
|
|
1145
1294
|
try {
|
|
1146
1295
|
storage.setItem(key, SEEN_VALUE);
|
|
@@ -1327,7 +1476,7 @@ var reviewSeenKey = (id, version) => version ? `wire_review_${id}_${version}_see
|
|
|
1327
1476
|
var reviewLastShownKey = (id) => `wire_review_${id}_last`;
|
|
1328
1477
|
var reviewSessionsKey = (id) => `wire_review_${id}_sessions`;
|
|
1329
1478
|
var reviewSessionOpenKey = (id) => `wire_review_${id}_open`;
|
|
1330
|
-
var resolveStorage = (override) => override != null ? override : getCoachmarkStorage();
|
|
1479
|
+
var resolveStorage = (override) => validateGateStorage(override != null ? override : getCoachmarkStorage());
|
|
1331
1480
|
var NO_STORAGE_WARNED_SLOT = /* @__PURE__ */ Symbol.for(
|
|
1332
1481
|
"@wireai/activation:gateStorageWarned"
|
|
1333
1482
|
);
|