@wireai/activation 0.14.3 → 0.16.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 +96 -21
- package/CHANGELOG.md +780 -0
- package/INTEGRATION_PROMPT.md +61 -23
- package/README.md +110 -31
- package/dist/analytics/index.d.mts +35 -13
- package/dist/analytics/index.d.ts +35 -13
- package/dist/analytics/index.js +288 -127
- package/dist/analytics/index.js.map +1 -1
- package/dist/analytics/index.mjs +288 -127
- package/dist/analytics/index.mjs.map +1 -1
- package/dist/coachmarks/index.d.mts +15 -1
- package/dist/coachmarks/index.d.ts +15 -1
- package/dist/coachmarks/index.js +120 -22
- package/dist/coachmarks/index.js.map +1 -1
- package/dist/coachmarks/index.mjs +120 -22
- package/dist/coachmarks/index.mjs.map +1 -1
- package/dist/{currentSession-CUvTOchb.d.mts → currentSession-Bz7G6lno.d.mts} +35 -39
- package/dist/{currentSession-CW_5Mq4O.d.ts → currentSession-z-CZ55ad.d.ts} +35 -39
- 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 +5 -2
- package/dist/index.d.ts +5 -2
- package/dist/index.js +125 -36
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +125 -36
- package/dist/index.mjs.map +1 -1
- package/dist/questionnaire/index.d.mts +3 -16
- package/dist/questionnaire/index.d.ts +3 -16
- package/dist/questionnaire/index.js +202 -46
- package/dist/questionnaire/index.js.map +1 -1
- package/dist/questionnaire/index.mjs +203 -47
- 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 +269 -52
- package/dist/reviews/index.js.map +1 -1
- package/dist/reviews/index.mjs +270 -53
- 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 +106 -20
- package/dist/showcase/index.js.map +1 -1
- package/dist/showcase/index.mjs +107 -21
- 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/llms.txt +9 -9
- package/package.json +6 -9
- package/src/WireOnboarding.tsx +4 -1
- package/src/analytics/currentSession.ts +141 -4
- package/src/analytics/index.ts +6 -1
- package/src/analytics/reportClientEvent.ts +19 -10
- package/src/analytics/useAnalytics.ts +74 -15
- package/src/analytics/wireDoctor.ts +152 -7
- package/src/coachmarks/CoachmarkProvider.tsx +26 -5
- package/src/coachmarks/runtime.ts +156 -2
- package/src/coachmarks/types.ts +7 -2
- package/src/coachmarks/useCoachmarkTour.ts +51 -1
- package/src/context/deviceId.ts +72 -6
- package/src/features/WireFeaturesProvider.tsx +72 -12
- package/src/features/fetchWireFeatures.ts +49 -11
- package/src/features/useWireFeatures.ts +39 -3
- package/src/identity/identityRecord.ts +15 -2
- package/src/questionnaire/QuestionnaireGate.tsx +40 -1
- package/src/questionnaire/transport.ts +22 -8
- package/src/questionnaire/useQuestionnaireGate.ts +58 -7
- package/src/reviews/ReviewGate.tsx +125 -37
- package/src/reviews/decision.ts +11 -1
- package/src/reviews/idempotency.ts +109 -0
- package/src/reviews/index.ts +4 -2
- package/src/reviews/runtime.ts +44 -13
- package/src/reviews/transport.ts +39 -20
- package/src/reviews/types.ts +7 -0
- package/src/reviews/useReviewGate.ts +57 -7
- package/src/session-analytics/lifecycle.ts +16 -0
- package/src/session-analytics/useLifecycleEvents.ts +30 -2
- package/src/session-analytics/useSessionStart.ts +22 -2
- package/src/showcase/FeatureShowcase.tsx +50 -3
- package/src/types.ts +15 -8
- package/src/utils/submitResult.ts +17 -9
- package/src/utils/withDeadline.ts +70 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React__default from 'react';
|
|
2
2
|
import { ImageSourcePropType } from 'react-native';
|
|
3
|
-
import { G as GestureKind, C as CoachmarkStorage } from '../types-
|
|
3
|
+
import { G as GestureKind, C as CoachmarkStorage } from '../types-D_0B0yay.mjs';
|
|
4
4
|
import { O as OnboardingTheme } from '../types-BKfpdZzX.mjs';
|
|
5
5
|
import { a as WireFeatures, b as WireFeaturesConfig } from '../types-BpwiRpA8.mjs';
|
|
6
6
|
|
package/dist/showcase/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React__default from 'react';
|
|
2
2
|
import { ImageSourcePropType } from 'react-native';
|
|
3
|
-
import { G as GestureKind, C as CoachmarkStorage } from '../types-
|
|
3
|
+
import { G as GestureKind, C as CoachmarkStorage } from '../types-D_0B0yay.js';
|
|
4
4
|
import { O as OnboardingTheme } from '../types-BKfpdZzX.js';
|
|
5
5
|
import { a as WireFeatures, b as WireFeaturesConfig } from '../types-BpwiRpA8.js';
|
|
6
6
|
|
package/dist/showcase/index.js
CHANGED
|
@@ -389,9 +389,54 @@ var getCoachmarkStorage = () => coachmarkRuntime().storage;
|
|
|
389
389
|
var isCoachmarkTesting = () => coachmarkRuntime().testing;
|
|
390
390
|
var showcaseGateKey = (showcaseId) => `wire_showcase_${showcaseId}_seen`;
|
|
391
391
|
var SEEN_VALUE = "1";
|
|
392
|
+
var GATE_PROBE_KEY = "wire_gate_probe";
|
|
393
|
+
var ASYNC_GATE_STORAGE_WARNED_SLOT = /* @__PURE__ */ Symbol.for(
|
|
394
|
+
"@wireai/activation:asyncGateStorageWarned"
|
|
395
|
+
);
|
|
396
|
+
var asyncGateWarnGlobal = globalThis;
|
|
397
|
+
var closedGateStorage = Object.freeze({
|
|
398
|
+
getItem: () => SEEN_VALUE,
|
|
399
|
+
setItem: () => {
|
|
400
|
+
}
|
|
401
|
+
});
|
|
402
|
+
var syncGateAdapters = /* @__PURE__ */ new WeakSet();
|
|
403
|
+
var asyncGateAdapters = /* @__PURE__ */ new WeakSet();
|
|
404
|
+
var warnAsyncGateStorage = () => {
|
|
405
|
+
if (asyncGateWarnGlobal[ASYNC_GATE_STORAGE_WARNED_SLOT]) return;
|
|
406
|
+
if (typeof __DEV__ === "undefined" || !__DEV__) return;
|
|
407
|
+
if (typeof console === "undefined" || !console.warn) return;
|
|
408
|
+
asyncGateWarnGlobal[ASYNC_GATE_STORAGE_WARNED_SLOT] = true;
|
|
409
|
+
console.warn(
|
|
410
|
+
"[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."
|
|
411
|
+
);
|
|
412
|
+
};
|
|
413
|
+
var validateGateStorage = (storage) => {
|
|
414
|
+
if (!storage) return null;
|
|
415
|
+
if (syncGateAdapters.has(storage)) return storage;
|
|
416
|
+
if (asyncGateAdapters.has(storage)) return closedGateStorage;
|
|
417
|
+
let probed;
|
|
418
|
+
try {
|
|
419
|
+
probed = storage.getItem(GATE_PROBE_KEY);
|
|
420
|
+
} catch {
|
|
421
|
+
syncGateAdapters.add(storage);
|
|
422
|
+
return storage;
|
|
423
|
+
}
|
|
424
|
+
if (probed != null && typeof probed.then === "function") {
|
|
425
|
+
asyncGateAdapters.add(storage);
|
|
426
|
+
try {
|
|
427
|
+
probed.then(void 0, () => {
|
|
428
|
+
});
|
|
429
|
+
} catch {
|
|
430
|
+
}
|
|
431
|
+
warnAsyncGateStorage();
|
|
432
|
+
return closedGateStorage;
|
|
433
|
+
}
|
|
434
|
+
syncGateAdapters.add(storage);
|
|
435
|
+
return storage;
|
|
436
|
+
};
|
|
392
437
|
var hasSeenGate = (key, storageOverride, testingOverride) => {
|
|
393
438
|
if (testingOverride != null ? testingOverride : isCoachmarkTesting()) return false;
|
|
394
|
-
const storage = storageOverride != null ? storageOverride : getCoachmarkStorage();
|
|
439
|
+
const storage = validateGateStorage(storageOverride != null ? storageOverride : getCoachmarkStorage());
|
|
395
440
|
if (!storage) return false;
|
|
396
441
|
try {
|
|
397
442
|
return storage.getItem(key) === SEEN_VALUE;
|
|
@@ -401,7 +446,7 @@ var hasSeenGate = (key, storageOverride, testingOverride) => {
|
|
|
401
446
|
};
|
|
402
447
|
var markSeenGate = (key, storageOverride, testingOverride) => {
|
|
403
448
|
if (testingOverride != null ? testingOverride : isCoachmarkTesting()) return;
|
|
404
|
-
const storage = storageOverride != null ? storageOverride : getCoachmarkStorage();
|
|
449
|
+
const storage = validateGateStorage(storageOverride != null ? storageOverride : getCoachmarkStorage());
|
|
405
450
|
if (!storage) return;
|
|
406
451
|
try {
|
|
407
452
|
storage.setItem(key, SEEN_VALUE);
|
|
@@ -489,17 +534,31 @@ var failOpen = async (storage, key) => {
|
|
|
489
534
|
const cached3 = await readCachedFeatures(storage, key);
|
|
490
535
|
return (_a = cached3 == null ? void 0 : cached3.features) != null ? _a : defaultWireFeatures;
|
|
491
536
|
};
|
|
492
|
-
var
|
|
537
|
+
var NO_ANSWER = /* @__PURE__ */ Symbol("wire-features-no-answer");
|
|
538
|
+
var fetchFeaturesJson = async (url, apiKey, timeoutMs) => {
|
|
493
539
|
const controller = typeof AbortController !== "undefined" ? new AbortController() : void 0;
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
540
|
+
let timer;
|
|
541
|
+
const expired = new Promise((resolve) => {
|
|
542
|
+
timer = setTimeout(() => {
|
|
543
|
+
controller == null ? void 0 : controller.abort();
|
|
544
|
+
resolve(NO_ANSWER);
|
|
545
|
+
}, timeoutMs);
|
|
546
|
+
});
|
|
547
|
+
const exchange = (async () => {
|
|
548
|
+
const res = await fetch(url, {
|
|
497
549
|
method: "GET",
|
|
498
550
|
headers: { Accept: "application/json", Authorization: `Bearer ${apiKey}` },
|
|
499
551
|
signal: controller == null ? void 0 : controller.signal
|
|
500
552
|
});
|
|
553
|
+
if (!res || !res.ok) return NO_ANSWER;
|
|
554
|
+
return await res.json();
|
|
555
|
+
})();
|
|
556
|
+
try {
|
|
557
|
+
return await Promise.race([exchange, expired]);
|
|
501
558
|
} finally {
|
|
502
559
|
clearTimeout(timer);
|
|
560
|
+
void exchange.catch(() => {
|
|
561
|
+
});
|
|
503
562
|
}
|
|
504
563
|
};
|
|
505
564
|
var fetchWireFeatures = async (config) => {
|
|
@@ -509,9 +568,8 @@ var fetchWireFeatures = async (config) => {
|
|
|
509
568
|
const url = `${config.serverUrl.replace(/\/$/, "")}/v1/features`;
|
|
510
569
|
const timeoutMs = config.timeoutMs && config.timeoutMs > 0 ? config.timeoutMs : DEFAULT_TIMEOUT_MS;
|
|
511
570
|
try {
|
|
512
|
-
const
|
|
513
|
-
if (
|
|
514
|
-
const json = await res.json();
|
|
571
|
+
const json = await fetchFeaturesJson(url, config.apiKey, timeoutMs);
|
|
572
|
+
if (json === NO_ANSWER) return failOpen(storage, key);
|
|
515
573
|
const features = parseWireFeatures(json);
|
|
516
574
|
if (storage) writeCachedFeatures(storage, key, features);
|
|
517
575
|
return features;
|
|
@@ -527,41 +585,56 @@ var useStableValue = (value, isEqual) => {
|
|
|
527
585
|
return ref.current;
|
|
528
586
|
};
|
|
529
587
|
var sameFetchKey = (a, b) => (a == null ? void 0 : a.serverUrl) === (b == null ? void 0 : b.serverUrl) && (a == null ? void 0 : a.apiKey) === (b == null ? void 0 : b.apiKey) && (a == null ? void 0 : a.appId) === (b == null ? void 0 : b.appId);
|
|
530
|
-
var
|
|
588
|
+
var useWireFeaturesState = (config) => {
|
|
531
589
|
const stableConfig = useStableValue(config, sameFetchKey);
|
|
532
590
|
const [flags, setFlags] = React.useState(defaultWireFeatures);
|
|
533
591
|
const canFetch = !!(stableConfig == null ? void 0 : stableConfig.serverUrl) && !!(stableConfig == null ? void 0 : stableConfig.apiKey);
|
|
592
|
+
const [settled, setSettled] = React.useState(!canFetch);
|
|
534
593
|
React.useEffect(() => {
|
|
535
594
|
if (!canFetch) {
|
|
536
595
|
setFlags((prev) => featuresEqual(prev, defaultWireFeatures) ? prev : defaultWireFeatures);
|
|
596
|
+
setSettled(true);
|
|
537
597
|
return;
|
|
538
598
|
}
|
|
539
599
|
let alive = true;
|
|
540
600
|
void fetchWireFeatures(stableConfig).then((next) => {
|
|
541
601
|
if (!alive) return;
|
|
542
602
|
setFlags((prev) => featuresEqual(prev, next) ? prev : next);
|
|
603
|
+
setSettled(true);
|
|
543
604
|
});
|
|
544
605
|
return () => {
|
|
545
606
|
alive = false;
|
|
546
607
|
};
|
|
547
608
|
}, [canFetch, stableConfig]);
|
|
548
|
-
return flags;
|
|
609
|
+
return React.useMemo(() => ({ flags, settled }), [flags, settled]);
|
|
549
610
|
};
|
|
550
611
|
var CONTEXT_SYMBOL = /* @__PURE__ */ Symbol.for("wireai.features.context");
|
|
551
|
-
var
|
|
552
|
-
if (!
|
|
553
|
-
|
|
612
|
+
var featuresContextGlobal = globalThis;
|
|
613
|
+
if (!featuresContextGlobal[CONTEXT_SYMBOL]) {
|
|
614
|
+
featuresContextGlobal[CONTEXT_SYMBOL] = React.createContext(null);
|
|
554
615
|
}
|
|
555
|
-
var WireFeaturesContext =
|
|
616
|
+
var WireFeaturesContext = featuresContextGlobal[CONTEXT_SYMBOL];
|
|
617
|
+
var SETTLED_SYMBOL = /* @__PURE__ */ Symbol.for("wireai.features.settled.context");
|
|
618
|
+
var settledContextGlobal = globalThis;
|
|
619
|
+
if (!settledContextGlobal[SETTLED_SYMBOL]) {
|
|
620
|
+
settledContextGlobal[SETTLED_SYMBOL] = React.createContext(true);
|
|
621
|
+
}
|
|
622
|
+
var WireFeaturesSettledContext = settledContextGlobal[SETTLED_SYMBOL];
|
|
556
623
|
var useWireFeaturesContext = () => React.useContext(WireFeaturesContext);
|
|
557
|
-
var
|
|
624
|
+
var useResolvedFeaturesState = (options) => {
|
|
558
625
|
var _a, _b, _c;
|
|
559
626
|
const ctx = useWireFeaturesContext();
|
|
627
|
+
const ctxSettled = React.useContext(WireFeaturesSettledContext);
|
|
560
628
|
const shouldFetch = !(options == null ? void 0 : options.flags) && ctx == null;
|
|
561
|
-
const fetched =
|
|
562
|
-
|
|
629
|
+
const fetched = useWireFeaturesState(shouldFetch ? options == null ? void 0 : options.config : void 0);
|
|
630
|
+
const flags = (_c = (_b = (_a = options == null ? void 0 : options.flags) != null ? _a : ctx) != null ? _b : fetched.flags) != null ? _c : defaultWireFeatures;
|
|
631
|
+
const settled = (options == null ? void 0 : options.flags) ? true : ctx != null ? ctxSettled : fetched.settled;
|
|
632
|
+
return React.useMemo(() => ({ flags, settled }), [flags, settled]);
|
|
563
633
|
};
|
|
564
634
|
|
|
635
|
+
// src/session/persistedSession.ts
|
|
636
|
+
var READ_TIMEOUT_MS2 = 1500;
|
|
637
|
+
|
|
565
638
|
// src/theme/defaultTheme.ts
|
|
566
639
|
var defaultOnboardingTheme = {
|
|
567
640
|
colors: {
|
|
@@ -721,7 +794,10 @@ var _FeatureShowcase = ({
|
|
|
721
794
|
[active, themeOverride]
|
|
722
795
|
);
|
|
723
796
|
const accent = accentColor != null ? accentColor : t.colors.primary;
|
|
724
|
-
const
|
|
797
|
+
const { flags, settled } = useResolvedFeaturesState({
|
|
798
|
+
flags: features,
|
|
799
|
+
config: featuresConfig
|
|
800
|
+
});
|
|
725
801
|
const disabled = !flags.showcase.enabled;
|
|
726
802
|
const Onboarding = React.useMemo(() => resolveShowcaseOnboarding(), []);
|
|
727
803
|
const gateKey = showcaseGateKey(config.id);
|
|
@@ -730,6 +806,14 @@ var _FeatureShowcase = ({
|
|
|
730
806
|
[gateKey, storage, isTesting]
|
|
731
807
|
);
|
|
732
808
|
const skip = seen || disabled || !Onboarding;
|
|
809
|
+
const flagsCouldMatter = !seen && !!Onboarding;
|
|
810
|
+
const [holdExpired, setHoldExpired] = React.useState(false);
|
|
811
|
+
React.useEffect(() => {
|
|
812
|
+
if (settled || holdExpired || !flagsCouldMatter) return void 0;
|
|
813
|
+
const timer = setTimeout(() => setHoldExpired(true), READ_TIMEOUT_MS2);
|
|
814
|
+
return () => clearTimeout(timer);
|
|
815
|
+
}, [settled, holdExpired, flagsCouldMatter]);
|
|
816
|
+
const holding = flagsCouldMatter && !settled && !holdExpired;
|
|
733
817
|
const doneRef = React.useRef(false);
|
|
734
818
|
const finish = React.useCallback(() => {
|
|
735
819
|
if (doneRef.current) return;
|
|
@@ -738,11 +822,12 @@ var _FeatureShowcase = ({
|
|
|
738
822
|
onDone();
|
|
739
823
|
}, [gateKey, storage, isTesting, onDone]);
|
|
740
824
|
React.useEffect(() => {
|
|
825
|
+
if (holding) return;
|
|
741
826
|
if (skip && !doneRef.current) {
|
|
742
827
|
doneRef.current = true;
|
|
743
828
|
onDone();
|
|
744
829
|
}
|
|
745
|
-
}, [skip, onDone]);
|
|
830
|
+
}, [holding, skip, onDone]);
|
|
746
831
|
const [activeIndex, setActiveIndex] = React.useState(0);
|
|
747
832
|
const steps = React.useMemo(
|
|
748
833
|
() => config.slides.map((slide) => {
|
|
@@ -809,6 +894,7 @@ var _FeatureShowcase = ({
|
|
|
809
894
|
}),
|
|
810
895
|
[t]
|
|
811
896
|
);
|
|
897
|
+
if (holding) return null;
|
|
812
898
|
if (skip || !Onboarding) return null;
|
|
813
899
|
const activeGesture = (_a = config.slides[activeIndex]) == null ? void 0 : _a.gesture;
|
|
814
900
|
const showGesture = !!activeGesture && activeGesture !== "none" && activeGesture !== "tap" && activeGesture !== "double_tap";
|