@wireai/activation 0.1.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 +73 -0
- package/CHANGELOG.md +572 -0
- package/INTEGRATION_PROMPT.md +55 -0
- package/LICENSE +21 -0
- package/README.md +923 -0
- package/dist/CenteredModal-C3qQBHsA.d.mts +38 -0
- package/dist/CenteredModal-Cdgns6--.d.ts +38 -0
- package/dist/coachmarks/index.d.mts +352 -0
- package/dist/coachmarks/index.d.ts +352 -0
- package/dist/coachmarks/index.js +944 -0
- package/dist/coachmarks/index.js.map +1 -0
- package/dist/coachmarks/index.mjs +918 -0
- package/dist/coachmarks/index.mjs.map +1 -0
- package/dist/decision-CFvGY6nP.d.ts +228 -0
- package/dist/decision-Cl8OFYzu.d.mts +228 -0
- package/dist/index.d.mts +1390 -0
- package/dist/index.d.ts +1390 -0
- package/dist/index.js +3260 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +3186 -0
- package/dist/index.mjs.map +1 -0
- package/dist/questionnaire/index.d.mts +249 -0
- package/dist/questionnaire/index.d.ts +249 -0
- package/dist/questionnaire/index.js +922 -0
- package/dist/questionnaire/index.js.map +1 -0
- package/dist/questionnaire/index.mjs +906 -0
- package/dist/questionnaire/index.mjs.map +1 -0
- package/dist/reviews/index.d.mts +213 -0
- package/dist/reviews/index.d.ts +213 -0
- package/dist/reviews/index.js +1001 -0
- package/dist/reviews/index.js.map +1 -0
- package/dist/reviews/index.mjs +971 -0
- package/dist/reviews/index.mjs.map +1 -0
- package/dist/showcase/index.d.mts +119 -0
- package/dist/showcase/index.d.ts +119 -0
- package/dist/showcase/index.js +660 -0
- package/dist/showcase/index.js.map +1 -0
- package/dist/showcase/index.mjs +651 -0
- package/dist/showcase/index.mjs.map +1 -0
- package/dist/types-BKfpdZzX.d.mts +84 -0
- package/dist/types-BKfpdZzX.d.ts +84 -0
- package/dist/types-CMuOexw0.d.mts +116 -0
- package/dist/types-CMuOexw0.d.ts +116 -0
- package/dist/types-GL_hQ0TN.d.mts +46 -0
- package/dist/types-GL_hQ0TN.d.ts +46 -0
- package/llms.txt +36 -0
- package/metro/index.d.ts +32 -0
- package/metro/index.js +83 -0
- package/package.json +171 -0
- package/src/DemoOnboarding.tsx +133 -0
- package/src/OnboardingFlow.tsx +504 -0
- package/src/WireOnboarding.tsx +248 -0
- package/src/analytics/analyticsEvent.ts +69 -0
- package/src/analytics/reportClientEvent.ts +119 -0
- package/src/analytics/sendPreview.ts +73 -0
- package/src/attribution/attribution.ts +46 -0
- package/src/cards/ChipSelectCard.tsx +265 -0
- package/src/cards/InterstitialCard.tsx +299 -0
- package/src/cards/NumberStepperCard.tsx +121 -0
- package/src/cards/SelectionCard.tsx +270 -0
- package/src/cards/StatusCard.tsx +170 -0
- package/src/cards/TextInputCard.tsx +147 -0
- package/src/cards/index.ts +23 -0
- package/src/coachmarks/CoachmarkOverlayHost.tsx +44 -0
- package/src/coachmarks/CoachmarkProvider.tsx +108 -0
- package/src/coachmarks/GestureHint.tsx +331 -0
- package/src/coachmarks/SpotlightOverlay.tsx +254 -0
- package/src/coachmarks/coachmarkAnchorRegistry.ts +70 -0
- package/src/coachmarks/coachmarkOverlayStore.ts +63 -0
- package/src/coachmarks/index.ts +62 -0
- package/src/coachmarks/runtime.ts +103 -0
- package/src/coachmarks/selectTourSteps.ts +45 -0
- package/src/coachmarks/types.ts +58 -0
- package/src/coachmarks/useCoachmarkAnchor.ts +49 -0
- package/src/coachmarks/useCoachmarkTour.ts +200 -0
- package/src/components/AnimatedSparkle.tsx +101 -0
- package/src/components/Button.tsx +129 -0
- package/src/components/CardHandoff.tsx +212 -0
- package/src/components/CardLayout.tsx +103 -0
- package/src/components/CenteredModal.tsx +184 -0
- package/src/components/CompletionView.tsx +186 -0
- package/src/components/DoneBlock.tsx +37 -0
- package/src/components/ErrorBlock.tsx +42 -0
- package/src/components/Illustration.tsx +36 -0
- package/src/components/LoadingBlock.tsx +182 -0
- package/src/components/LoadingScreen.tsx +35 -0
- package/src/components/OnboardingScaffold.tsx +105 -0
- package/src/components/StepProgress.tsx +85 -0
- package/src/components/loaderChrome.ts +28 -0
- package/src/config/onboardingFlag.ts +39 -0
- package/src/config/wireConfigFromEnv.ts +52 -0
- package/src/device/deviceContext.ts +158 -0
- package/src/features/WireFeaturesProvider.tsx +81 -0
- package/src/features/cache.ts +92 -0
- package/src/features/defaults.ts +67 -0
- package/src/features/fetchWireFeatures.ts +86 -0
- package/src/features/index.ts +39 -0
- package/src/features/types.ts +61 -0
- package/src/features/useWireFeatures.ts +57 -0
- package/src/identity/userIdentity.ts +100 -0
- package/src/illustrations/defaultIllustrations.tsx +99 -0
- package/src/index.ts +159 -0
- package/src/motion/centeredModalMotion.ts +45 -0
- package/src/motion/motionSpec.ts +133 -0
- package/src/motion/useReducedMotion.ts +43 -0
- package/src/questionnaire/QuestionnaireGate.tsx +235 -0
- package/src/questionnaire/decision.ts +63 -0
- package/src/questionnaire/index.ts +54 -0
- package/src/questionnaire/runtime.ts +20 -0
- package/src/questionnaire/transport.ts +83 -0
- package/src/questionnaire/types.ts +182 -0
- package/src/questionnaire/useQuestionnaireGate.ts +159 -0
- package/src/reviews/ReviewGate.tsx +316 -0
- package/src/reviews/ReviewModal.tsx +13 -0
- package/src/reviews/decision.ts +120 -0
- package/src/reviews/equality.ts +54 -0
- package/src/reviews/expo-store-review.d.ts +16 -0
- package/src/reviews/feedbackForm.ts +41 -0
- package/src/reviews/index.ts +78 -0
- package/src/reviews/presentation.ts +29 -0
- package/src/reviews/runtime.ts +45 -0
- package/src/reviews/storeReview.ts +62 -0
- package/src/reviews/transport.ts +89 -0
- package/src/reviews/types.ts +172 -0
- package/src/reviews/useReviewGate.ts +137 -0
- package/src/session/persistedSession.ts +143 -0
- package/src/session-analytics/index.ts +18 -0
- package/src/session-analytics/reportSessionStart.ts +144 -0
- package/src/session-analytics/useSessionStart.ts +114 -0
- package/src/showcase/FeatureShowcase.tsx +233 -0
- package/src/showcase/index.ts +19 -0
- package/src/showcase/selectShowcaseSlides.ts +45 -0
- package/src/showcase/showcaseColors.ts +91 -0
- package/src/showcase/types.ts +74 -0
- package/src/theme/ThemeContext.tsx +38 -0
- package/src/theme/defaultTheme.ts +70 -0
- package/src/theme/themeFromBrand.ts +93 -0
- package/src/theme/types.ts +88 -0
- package/src/theme/typography.ts +46 -0
- package/src/types.ts +215 -0
- package/src/utils/deriveAnswers.ts +43 -0
- package/src/utils/readProgress.ts +33 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/theme/defaultTheme.ts","../../src/theme/ThemeContext.tsx","../../src/theme/typography.ts","../../src/motion/motionSpec.ts","../../src/motion/useReducedMotion.ts","../../src/components/Button.tsx","../../src/motion/centeredModalMotion.ts","../../src/components/CenteredModal.tsx","../../src/reviews/feedbackForm.ts","../../src/reviews/decision.ts","../../src/questionnaire/decision.ts","../../src/questionnaire/transport.ts","../../src/questionnaire/QuestionnaireGate.tsx","../../src/coachmarks/runtime.ts","../../src/features/defaults.ts","../../src/features/cache.ts","../../src/features/fetchWireFeatures.ts","../../src/features/useWireFeatures.ts","../../src/features/WireFeaturesProvider.tsx","../../src/reviews/equality.ts","../../src/reviews/runtime.ts","../../src/questionnaire/runtime.ts","../../src/questionnaire/useQuestionnaireGate.ts"],"names":["createContext","useContext","useState","useEffect","AccessibilityInfo","useRef","Animated","useCallback","jsx","Pressable","Text","React","StyleSheet","forwardRef","Easing","useImperativeHandle","BackHandler","jsxs","Modal","styles","SafeAreaView","useMemo","_a","_b","Fragment","TextInput","useStableValue","testing"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * defaultOnboardingTheme — a neutral, light, brand-agnostic baseline. Consumers\n * pass a `Partial<OnboardingTheme>` that is deep-merged over this (see\n * `mergeTheme`), so they only override what differs from neutral.\n */\nimport type { OnboardingTheme } from \"./types\";\n\nexport const defaultOnboardingTheme: OnboardingTheme = {\n colors: {\n background: \"#FFFFFF\",\n surface: \"#F7F7F8\",\n text: \"#101114\",\n textMuted: \"#6B7280\",\n primary: \"#111111\",\n onPrimary: \"#FFFFFF\",\n primarySoft: \"rgba(17,17,17,0.06)\",\n border: \"#E4E4E7\",\n borderStrong: \"#C7C7CC\",\n disabled: \"#EBEBEB\",\n success: \"#16A34A\",\n warning: \"#D97706\",\n error: \"#DC2626\",\n info: \"#2563EB\",\n },\n fonts: {\n regular: undefined,\n medium: undefined,\n bold: undefined,\n baseSize: 16,\n scale: 1,\n },\n radius: {\n sm: 8,\n md: 12,\n lg: 16,\n full: 9999,\n },\n spacing: {\n xs: 4,\n sm: 8,\n md: 16,\n lg: 24,\n xl: 32,\n },\n // Circled (pill) CTA by default; an app can pass `button: { radius: 12 }` for\n // a rounded rectangle, or override height/paddingX to match its design system.\n button: {\n radius: 9999,\n height: 52,\n paddingX: 24,\n },\n};\n\n/**\n * Deep-merge a partial theme over the default. Two levels deep is enough — the\n * theme is intentionally shallow (colors/fonts/radius/spacing), so we merge each\n * known sub-object and leave scalars to overwrite.\n */\nexport const mergeTheme = (\n override?: Partial<OnboardingTheme>,\n): OnboardingTheme => {\n if (!override) return defaultOnboardingTheme;\n return {\n colors: { ...defaultOnboardingTheme.colors, ...override.colors },\n fonts: { ...defaultOnboardingTheme.fonts, ...override.fonts },\n radius: { ...defaultOnboardingTheme.radius, ...override.radius },\n spacing: { ...defaultOnboardingTheme.spacing, ...override.spacing },\n button: { ...defaultOnboardingTheme.button, ...override.button },\n };\n};\n","/**\n * ThemeContext — provides the resolved OnboardingTheme to every shell element and\n * themed card via `useOnboardingTheme()`. WHY a context rather than prop-drilling:\n * the cards are mounted by the SDK's ComponentRenderer (not by us), so we can't\n * pass them props directly — they must read the theme from context.\n */\nimport React, { createContext, useContext, useMemo } from \"react\";\nimport { mergeTheme } from \"./defaultTheme\";\nimport type { OnboardingTheme } from \"./types\";\n\nconst OnboardingThemeContext = createContext<OnboardingTheme | null>(null);\n\nexport type OnboardingThemeProviderProps = {\n /** Partial overrides merged over the neutral default theme. */\n theme?: Partial<OnboardingTheme>;\n children: React.ReactNode;\n};\n\nexport const OnboardingThemeProvider: React.FC<OnboardingThemeProviderProps> = ({\n theme,\n children,\n}) => {\n const resolved = useMemo(() => mergeTheme(theme), [theme]);\n return (\n <OnboardingThemeContext.Provider value={resolved}>\n {children}\n </OnboardingThemeContext.Provider>\n );\n};\n\n/**\n * Read the active theme. Falls back to the default theme when used outside a\n * provider so a stray card never crashes — it just renders neutral.\n */\nexport const useOnboardingTheme = (): OnboardingTheme => {\n const ctx = useContext(OnboardingThemeContext);\n return ctx ?? mergeTheme();\n};\n","/**\n * typography — derive concrete RN text styles from the theme's font config, so\n * every label/title/caption in the kit scales from one `baseSize` + `scale` and\n * picks the right family. Cards call these instead of hardcoding fontSize/weight.\n */\nimport type { TextStyle } from \"react-native\";\nimport type { OnboardingFonts } from \"./types\";\n\nconst round = (n: number) => Math.round(n);\n\n/** Body text — the reference size. */\nexport const bodyStyle = (f: OnboardingFonts): TextStyle => ({\n fontFamily: f.regular,\n fontSize: round(f.baseSize * f.scale),\n lineHeight: round(f.baseSize * f.scale * 1.5),\n});\n\n/** Slightly smaller secondary/hint text. */\nexport const captionStyle = (f: OnboardingFonts): TextStyle => ({\n fontFamily: f.regular,\n fontSize: round(f.baseSize * 0.8125 * f.scale),\n lineHeight: round(f.baseSize * 0.8125 * f.scale * 1.5),\n});\n\n/** Emphasised label (e.g. a card question). */\nexport const labelStyle = (f: OnboardingFonts): TextStyle => ({\n fontFamily: f.medium ?? f.bold,\n fontSize: round(f.baseSize * f.scale),\n lineHeight: round(f.baseSize * f.scale * 1.4),\n fontWeight: f.medium ? undefined : (\"600\" as const),\n});\n\n/** Heading (status title, done block). */\nexport const headingStyle = (f: OnboardingFonts): TextStyle => ({\n fontFamily: f.bold,\n fontSize: round(f.baseSize * 1.25 * f.scale),\n lineHeight: round(f.baseSize * 1.25 * f.scale * 1.3),\n fontWeight: f.bold ? undefined : (\"700\" as const),\n});\n\n/** Button label. */\nexport const buttonLabelStyle = (f: OnboardingFonts): TextStyle => ({\n fontFamily: f.bold ?? f.medium,\n fontSize: round(f.baseSize * f.scale),\n fontWeight: f.bold ? undefined : (\"600\" as const),\n});\n","/**\n * motionSpec — the single source of truth for every animation value in the kit,\n * extracted 1:1 from the Wire Activation card-interactions design canvas\n * (see design-refs/EXTRACTED-SPEC.md for the row-by-row mapping).\n *\n * Pure constants + pure helpers only — NO react-native import — so the spec is\n * unit-testable under node:test and components can't drift from the design\n * without a red test.\n */\n\n/** The design system's \"ease-wire\" curve: cubic-bezier(0.16, 1, 0.3, 1). */\nexport const WIRE_BEZIER = [0.16, 1, 0.3, 1] as const;\n\n/** Design-system duration tokens (colors_and_type.css --duration-*). */\nexport const DURATION_FAST_MS = 150;\nexport const DURATION_BASE_MS = 300;\nexport const DURATION_SLOW_MS = 500;\n\n/** Reduce-motion fallback: a short fade to the final frame, never a frozen mid-state. */\nexport const REDUCED_FADE_MS = 120;\n\n// ── Press feedback (spec rows 1, 4, 20) ─────────────────────────────────────\nexport const ROW_PRESS_SCALE = 0.975;\nexport const CHIP_PRESS_SCALE = 0.94;\nexport const BUTTON_PRESS_SCALE = 0.975;\n/** Reduce-motion press feedback: opacity dip instead of scale. */\nexport const REDUCED_PRESS_OPACITY = 0.85;\nexport const PRESS_SPRING = { friction: 8, tension: 220 } as const;\nexport const BUTTON_PRESS_SPRING = { friction: 8, tension: 240 } as const;\n\n// ── Selection state changes (spec rows 2, 3, 5, 6) ──────────────────────────\nexport const SELECT_FILL_MS = 180;\nexport const CHIP_FILL_MS = 160;\nexport const CHECK_SPRING = { friction: 6, tension: 180 } as const;\nexport const CHECK_SCALE_FROM = 0.6;\n\n// ── Card-to-card handoff (spec rows 7-10) ───────────────────────────────────\nexport const CARD_ENTER_MS = 220;\nexport const CARD_EXIT_MS = 180;\n/** The exit starts this long BEFORE the enter (enter is delayed by this). */\nexport const CARD_EXIT_LEAD_MS = 80;\nexport const CARD_SLIDE_PX = 26;\nexport const CARD_ENTER_SCALE_FROM = 0.96;\n/** The interstitial keeps the shell's springier value-beat (AnimatedCard idiom). */\nexport const CARD_SPRING = { damping: 14, stiffness: 180, mass: 1 } as const;\n/** Loader-to-card handoff: the loader just fades out. */\nexport const LOADER_HANDOFF_FADE_MS = 120;\n\n// ── InterstitialCard reveal (spec rows 11-13) ───────────────────────────────\nexport const INTERSTITIAL_HEAD_MS = 500;\nexport const INTERSTITIAL_HEAD_STAGGER_MS = 100;\nexport const INTERSTITIAL_ITEM_MS = 300;\nexport const INTERSTITIAL_ITEM_STAGGER_MS = 90;\nexport const INTERSTITIAL_ITEM_BASE_DELAY_MS = 200;\nexport const INTERSTITIAL_CHECK_DELAY_MS = 200;\nexport const INTERSTITIAL_CHECK_SCALE_FROM = 0.4;\n\n/** Delay before work item `index` begins its reveal (row lands after the head). */\nexport const interstitialItemDelay = (index: number): number =>\n INTERSTITIAL_ITEM_BASE_DELAY_MS + index * INTERSTITIAL_ITEM_STAGGER_MS;\n\n/** Delay before work item `index`'s check pops (200ms after its row starts). */\nexport const interstitialCheckDelay = (index: number): number =>\n interstitialItemDelay(index) + INTERSTITIAL_CHECK_DELAY_MS;\n\n// ── Status / completion payoff (spec rows 14-16) ────────────────────────────\nexport const STATUS_POP_SPRING = { friction: 6, tension: 200 } as const;\nexport const STATUS_POP_SCALE_FROM = 0.4;\nexport const STATUS_BURST_MS = 500;\nexport const STATUS_BURST_SCALE_FROM = 0.4;\nexport const STATUS_BURST_SCALE_TO = 1.5;\nexport const STATUS_BURST_OPACITY_FROM = 0.5;\nexport const STATUS_COPY_MS = 300;\nexport const STATUS_COPY_STAGGER_MS = 80;\n/** Copy starts settling once the badge pop is visibly underway. */\nexport const STATUS_COPY_BASE_DELAY_MS = 200;\n\n/** Delay before payoff copy line `index` fades up. */\nexport const statusCopyDelay = (index: number): number =>\n STATUS_COPY_BASE_DELAY_MS + index * STATUS_COPY_STAGGER_MS;\n\n// ── Loading treatment (spec rows 17-19) ─────────────────────────────────────\nexport const LOADER_PULSE_MS = 1600;\nexport const LOADER_PULSE_MIN_OPACITY = 0.35;\nexport const LOADER_ORBIT_MS = 2400;\n/** Skeleton lines appear only when the fetch runs past this. */\nexport const SKELETON_DELAY_MS = 300;\nexport const SKELETON_PULSE_MS = 1500;\nexport const SKELETON_MIN_OPACITY = 0.45;\n\n// ── Progress bar (spec row 21) ──────────────────────────────────────────────\nexport const PROGRESS_MS = 400;\n\n// ── Dev/QA duration scale (slow-mo for judging motion) ──────────────────────\n// A module-level multiplier applied to every timing DURATION (and stagger\n// delay) at animation start. 1 = design speed; 2 = half-speed slow-mo. Springs\n// are physics-based (friction/tension) and do NOT scale. This is a dev/QA tool\n// (the playground's tweak panel drives it) — production code never calls the\n// setter, so the default 1 keeps shipped motion exactly on spec.\n\nlet durationScale = 1;\n\n/** Set the global duration multiplier (clamped to 0.1-10; invalid resets to 1). */\nexport const setMotionDurationScale = (scale: number): void => {\n durationScale = Number.isFinite(scale) && scale > 0 ? Math.min(10, Math.max(0.1, scale)) : 1;\n};\n\nexport const getMotionDurationScale = (): number => durationScale;\n\n/** Apply the current duration scale to a spec duration/delay (rounded ms). */\nexport const scaledMs = (ms: number): number => Math.round(ms * durationScale);\n\n// ── Reduce-motion helpers (pure, shared by every animated component) ────────\n\nexport type EntranceSpec = {\n /** Duration to animate with (falls back to the short fade under reduce motion). */\n duration: number;\n /** Whether transforms (translate/scale) may move; false = opacity-only fade. */\n useTransform: boolean;\n};\n\n/** Entrances: full motion normally; a 120ms opacity-only fade under reduce motion. */\nexport const entranceSpec = (reduced: boolean, durationMs: number): EntranceSpec =>\n reduced\n ? { duration: REDUCED_FADE_MS, useTransform: false }\n : { duration: durationMs, useTransform: true };\n\n/** State changes (select fills etc.): snap instantly under reduce motion. */\nexport const stateChangeDuration = (reduced: boolean, durationMs: number): number =>\n reduced ? 0 : durationMs;\n\n/** Looping/ambient animations run only when motion is allowed. */\nexport const loopEnabled = (reduced: boolean): boolean => !reduced;\n","/**\n * useReducedMotion — the kit's ONE reduce-motion gate. Reads the OS setting via\n * AccessibilityInfo and stays subscribed to `reduceMotionChanged`, so every\n * animated component lands on its final frame (or a ~120ms fade) the moment the\n * user flips the setting — no restart needed.\n *\n * This closes the gap documented in ai_rules/rules/performance.md §3: every\n * animation in the kit must consume this hook (usually through the pure helpers\n * in motionSpec.ts: `entranceSpec` / `stateChangeDuration` / `loopEnabled`).\n */\nimport { useEffect, useState } from \"react\";\nimport { AccessibilityInfo } from \"react-native\";\n\n// Last-known value, module-level: cards remount every turn (keyed on message id),\n// and a `useState(false)` default would flash one animated frame per turn for a\n// reduce-motion user before the async read resolves.\nlet lastKnown = false;\n\nexport const useReducedMotion = (): boolean => {\n const [reduced, setReduced] = useState(lastKnown);\n\n useEffect(() => {\n let mounted = true;\n AccessibilityInfo.isReduceMotionEnabled()\n .then((value) => {\n lastKnown = value;\n if (mounted) setReduced(value);\n })\n .catch(() => {\n // Unavailable (e.g. some test envs): keep the last-known value.\n });\n const sub = AccessibilityInfo.addEventListener(\"reduceMotionChanged\", (value) => {\n lastKnown = value;\n setReduced(value);\n });\n return () => {\n mounted = false;\n sub?.remove();\n };\n }, []);\n\n return reduced;\n};\n","/**\n * Button — the kit's themed primary/outline button, replacing the SDK's hardcoded\n * `Btn`. Every color/radius/font comes from the active theme.\n *\n * Motion (design-refs/EXTRACTED-SPEC.md row 20): \"a footer button that answers\n * the tap\" — scale 1→0.975 spring on press-in, back on release, native driver.\n * Reduce motion: an opacity dip to 0.85 while pressed, no scale. Disabled keeps\n * the `disabled` token fill with no press handler (unchanged).\n */\nimport React, { useCallback, useRef } from \"react\";\nimport { Animated, Pressable, StyleSheet, Text } from \"react-native\";\nimport { useOnboardingTheme } from \"../theme/ThemeContext\";\nimport { buttonLabelStyle } from \"../theme/typography\";\nimport {\n BUTTON_PRESS_SCALE,\n BUTTON_PRESS_SPRING,\n REDUCED_FADE_MS,\n REDUCED_PRESS_OPACITY,\n scaledMs,\n} from \"../motion/motionSpec\";\nimport { useReducedMotion } from \"../motion/useReducedMotion\";\n\ntype Variant = \"primary\" | \"outline\";\n\ntype ButtonProps = {\n title: string;\n onPress?: () => void;\n variant?: Variant;\n disabled?: boolean;\n /** Stretch to the container width — for form-style CTAs (e.g. the review form's\n * \"Send feedback\"). Default false keeps the content-width pill used by cards. */\n fullWidth?: boolean;\n};\n\nconst _Button: React.FC<ButtonProps> = ({\n title,\n onPress,\n variant = \"primary\",\n disabled = false,\n fullWidth = false,\n}) => {\n const t = useOnboardingTheme();\n const reduced = useReducedMotion();\n const isPrimary = variant === \"primary\";\n\n // 0 = at rest, 1 = pressed. Scale normally; opacity dip under reduce motion.\n const pressT = useRef(new Animated.Value(0)).current;\n const pressTo = useCallback(\n (to: number) => {\n if (reduced) {\n Animated.timing(pressT, {\n toValue: to,\n duration: scaledMs(REDUCED_FADE_MS),\n useNativeDriver: true,\n }).start();\n return;\n }\n Animated.spring(pressT, {\n toValue: to,\n friction: BUTTON_PRESS_SPRING.friction,\n tension: BUTTON_PRESS_SPRING.tension,\n useNativeDriver: true,\n }).start();\n },\n [pressT, reduced],\n );\n const handlePressIn = useCallback(() => pressTo(1), [pressTo]);\n const handlePressOut = useCallback(() => pressTo(0), [pressTo]);\n\n const scale = pressT.interpolate({\n inputRange: [0, 1],\n outputRange: [1, reduced ? 1 : BUTTON_PRESS_SCALE],\n });\n const opacity = pressT.interpolate({\n inputRange: [0, 1],\n outputRange: [1, reduced ? REDUCED_PRESS_OPACITY : 1],\n });\n\n return (\n <Pressable\n onPress={disabled ? undefined : onPress}\n onPressIn={disabled ? undefined : handlePressIn}\n onPressOut={disabled ? undefined : handlePressOut}\n disabled={disabled}\n accessibilityRole=\"button\"\n accessibilityState={{ disabled }}\n style={fullWidth ? styles.fill : undefined}\n >\n <Animated.View\n style={[\n styles.base,\n fullWidth && styles.fill,\n {\n borderRadius: t.button.radius,\n minHeight: t.button.height,\n paddingHorizontal: t.button.paddingX,\n opacity,\n transform: [{ scale }],\n },\n isPrimary\n ? { backgroundColor: disabled ? t.colors.disabled : t.colors.primary }\n : { borderWidth: 1.5, borderColor: disabled ? t.colors.border : t.colors.primary },\n ]}\n >\n <Text\n style={[\n buttonLabelStyle(t.fonts),\n {\n color: disabled\n ? t.colors.textMuted\n : isPrimary\n ? t.colors.onPrimary\n : t.colors.primary,\n },\n ]}\n >\n {title}\n </Text>\n </Animated.View>\n </Pressable>\n );\n};\n\nexport const Button = React.memo(_Button);\n\nconst styles = StyleSheet.create({\n base: { alignItems: \"center\", justifyContent: \"center\" },\n fill: { width: \"100%\" },\n});\n","/**\n * centeredModalMotion - the pure entrance spec for the content-agnostic CenteredModal shell.\n *\n * WHY split out: the shell UI (CenteredModal) is RN and cannot run under the kit's node:test\n * runner, but the entrance math is pure and worth pinning. Keep the animation spec here so both\n * the component and the tests read one source, and so any module (reviews, questionnaire) can\n * reuse the shell without depending on another feature module.\n *\n * The centered popup uses RN's built-in `Animated` (native driver), the same idiom as\n * CompletionView/CardHandoff, so the shell stays dependency-free (no reanimated).\n *\n * NOTE: `reviews/presentation.ts` re-exports these under their original names\n * (`REVIEW_MODAL_ANIM`, `entranceFrame`) for back-compat with the reviews public API.\n */\n\n/**\n * The entrance spec for the centered popup. Values chosen to match the boilerplate's\n * `animated-modal` feel (a springy zoom-in) with the kit's own native-driver idiom:\n * • backdrop fades 0 -> `backdropOpacity`\n * • card springs from `scaleFrom` -> 1 and fades 0 -> 1\n */\nexport const CENTERED_MODAL_ANIM = {\n /** Spring config for the card scale/opacity entrance (mirrors CompletionView's pop). */\n enterSpring: { friction: 7, tension: 80 },\n /** Exit timing in ms (fade + shrink back out before unmount). */\n exitMs: 160,\n /** Card scale at rest (progress 0); grows to 1 at progress 1. */\n scaleFrom: 0.9,\n /** Peak dim of the backdrop at progress 1. */\n backdropOpacity: 0.55,\n} as const;\n\n/** A single frame of the entrance, for a progress value in [0..1] (clamped). Pure, testable. */\nexport const centeredModalFrame = (\n progress: number,\n backdropMax: number = CENTERED_MODAL_ANIM.backdropOpacity,\n): { cardOpacity: number; cardScale: number; backdropOpacity: number } => {\n const p = progress < 0 ? 0 : progress > 1 ? 1 : progress;\n const { scaleFrom } = CENTERED_MODAL_ANIM;\n return {\n cardOpacity: p,\n cardScale: scaleFrom + (1 - scaleFrom) * p,\n backdropOpacity: backdropMax * p,\n };\n};\n","/**\n * CenteredModal - the content-agnostic centered-popup shell.\n *\n * Renders its children as an alert-style card in the MIDDLE of the screen, over a dimmed\n * backdrop, with a spring/fade entrance. It knows nothing about what it presents: the reviews\n * gate uses it (via the `ReviewModal` back-compat re-export) and the questionnaire module reuses\n * the same shell, neither depends on the other.\n *\n * Adapted from the mobile-launcher boilerplate's `animated-modal` (transparent RN Modal +\n * centered container + backdrop-tap dismiss + content stop-propagation + springy zoom-in), but\n * reimplemented on RN's built-in `Animated` (native driver) so the shell stays dependency-free:\n * the boilerplate uses react-native-reanimated, which the kit does not.\n *\n * Dismissal (backdrop tap / Android back / the imperative `close()` handle) animates the card\n * out, then fires `onDismiss`. It never auto-closes on resolve: the host keeps the card showing\n * whatever payoff it wants (a thank-you, a summary) until the user taps the backdrop to close,\n * exactly like CompletionView.\n */\nimport React, {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n} from \"react\";\nimport { Animated, BackHandler, Easing, Modal, Pressable, StyleSheet } from \"react-native\";\nimport { SafeAreaView } from \"react-native-safe-area-context\";\n\nimport type { OnboardingTheme } from \"../theme/types\";\nimport { REDUCED_FADE_MS } from \"../motion/motionSpec\";\nimport { useReducedMotion } from \"../motion/useReducedMotion\";\nimport { CENTERED_MODAL_ANIM } from \"../motion/centeredModalMotion\";\n\n/** Imperative handle so an in-card \"Not now\"/\"Skip\" link can trigger the exit animation. */\nexport type CenteredModalHandle = { close: () => void };\n\nexport type CenteredModalProps = {\n /** The (already merged) active theme - card surface/radius/spacing come from here. */\n theme: OnboardingTheme;\n /** Whether a backdrop tap / back press dismisses. Default true (never a dead end). */\n dismissible?: boolean;\n /** Fired once, AFTER the exit animation, on any dismiss. Wire to the host's resolve. */\n onDismiss: () => void;\n children: React.ReactNode;\n};\n\nconst _CenteredModal = forwardRef<CenteredModalHandle, CenteredModalProps>(\n ({ theme, dismissible = true, onDismiss, children }, ref) => {\n const progress = useRef(new Animated.Value(0)).current;\n const [open, setOpen] = useState(true);\n const closingRef = useRef(false);\n const reduced = useReducedMotion();\n\n // Entrance: spring the card in (scale + fade); the backdrop fades up in lock-step.\n // Reduce motion: a short fade to the settled card, no spring/scale.\n useEffect(() => {\n const anim = reduced\n ? Animated.timing(progress, {\n toValue: 1,\n duration: REDUCED_FADE_MS,\n useNativeDriver: true,\n })\n : Animated.spring(progress, {\n toValue: 1,\n useNativeDriver: true,\n friction: CENTERED_MODAL_ANIM.enterSpring.friction,\n tension: CENTERED_MODAL_ANIM.enterSpring.tension,\n });\n anim.start();\n return () => anim.stop();\n }, [progress, reduced]);\n\n const close = useCallback(() => {\n if (closingRef.current) return;\n closingRef.current = true;\n Animated.timing(progress, {\n toValue: 0,\n duration: reduced ? REDUCED_FADE_MS : CENTERED_MODAL_ANIM.exitMs,\n easing: Easing.in(Easing.cubic),\n useNativeDriver: true,\n }).start(() => {\n setOpen(false);\n onDismiss();\n });\n }, [progress, onDismiss, reduced]);\n\n useImperativeHandle(ref, () => ({ close }), [close]);\n\n // Android hardware back -> dismiss (when allowed). Always consume so it never pops a route.\n useEffect(() => {\n const sub = BackHandler.addEventListener(\"hardwareBackPress\", () => {\n if (dismissible) close();\n return true;\n });\n return () => sub.remove();\n }, [dismissible, close]);\n\n const onBackdrop = useCallback(() => {\n if (dismissible) close();\n }, [dismissible, close]);\n\n const backdropOpacity = progress.interpolate({\n inputRange: [0, 1],\n outputRange: [0, CENTERED_MODAL_ANIM.backdropOpacity],\n });\n // Reduce motion: the card fades in already settled (no zoom).\n const cardScale = reduced\n ? 1\n : progress.interpolate({\n inputRange: [0, 1],\n outputRange: [CENTERED_MODAL_ANIM.scaleFrom, 1],\n });\n\n return (\n <Modal\n visible={open}\n transparent\n animationType=\"none\"\n statusBarTranslucent\n onRequestClose={dismissible ? close : undefined}\n >\n {/* Dimmed backdrop (fades with the entrance). */}\n <Animated.View\n style={[styles.backdrop, { opacity: backdropOpacity }]}\n pointerEvents=\"none\"\n />\n\n {/* Full-screen tap target: a tap outside the card dismisses. */}\n <Pressable\n style={styles.fill}\n onPress={onBackdrop}\n accessibilityRole=\"button\"\n accessibilityLabel=\"Dismiss\"\n >\n <SafeAreaView style={styles.center} pointerEvents=\"box-none\">\n {/* Inner Pressable swallows taps so the card itself never dismisses. */}\n <Pressable onPress={() => {}} style={styles.cardWrap}>\n <Animated.View\n accessibilityViewIsModal\n accessibilityRole=\"alert\"\n style={[\n styles.card,\n {\n backgroundColor: theme.colors.surface,\n borderRadius: theme.radius.lg,\n padding: theme.spacing.lg,\n gap: theme.spacing.md,\n opacity: progress,\n transform: [{ scale: cardScale }],\n },\n ]}\n >\n {children}\n </Animated.View>\n </Pressable>\n </SafeAreaView>\n </Pressable>\n </Modal>\n );\n },\n);\n\n_CenteredModal.displayName = \"CenteredModal\";\n\nexport const CenteredModal = React.memo(_CenteredModal);\n\nconst styles = StyleSheet.create({\n fill: { flex: 1 },\n backdrop: { position: \"absolute\", top: 0, left: 0, right: 0, bottom: 0, backgroundColor: \"#000\" },\n center: { flex: 1, alignItems: \"center\", justifyContent: \"center\", padding: 24 },\n cardWrap: { width: \"100%\", maxWidth: 420 },\n card: {\n width: \"100%\",\n // Soft elevation so the popup reads as lifted above the app in both themes.\n shadowColor: \"#000\",\n shadowOffset: { width: 0, height: 8 },\n shadowOpacity: 0.28,\n shadowRadius: 24,\n elevation: 12,\n },\n});\n\nexport default CenteredModal;\n","/**\n * feedbackForm — pure sizing helpers for the 1–4★ feedback form, kept out of the\n * React component so the \"≈4 lines tall, capped at 5\" rule is verifiable in\n * isolation (the reviews module's correctness lives in these pure functions, the\n * same convention as `decision.ts` / `presentation.ts`).\n *\n * Malik's ask: the feedback field should be a multiline box \"around 3 to 5 lines\"\n * tall, not a single-line input. We size it from the theme's body line-height so\n * it scales with the app's font config.\n */\nimport type { OnboardingFonts } from \"../theme/types\";\nimport { bodyStyle } from \"../theme/typography\";\n\n/** Lines the box shows at rest, and the cap it can grow to. */\nexport const FEEDBACK_MIN_LINES = 4;\nexport const FEEDBACK_MAX_LINES = 5;\n\nexport interface FeedbackInputHeights {\n /** Resting height — the box opens at ~4 lines so it reads as \"write a few words\". */\n minHeight: number;\n /** Growth cap — it never balloons past ~5 lines; scroll takes over beyond that. */\n maxHeight: number;\n}\n\n/**\n * Derive the multiline feedback box's min/max height from the theme's body\n * line-height. `minHeight = MIN_LINES × lineHeight`, capped at\n * `MAX_LINES × lineHeight`, plus the field's vertical padding (top + bottom) so\n * the requested number of text lines is actually visible inside the padding.\n */\nexport const feedbackInputHeights = (\n fonts: OnboardingFonts,\n verticalPadding = 0,\n): FeedbackInputHeights => {\n const lineHeight = bodyStyle(fonts).lineHeight ?? Math.round(fonts.baseSize * fonts.scale * 1.5);\n const pad = verticalPadding * 2;\n return {\n minHeight: lineHeight * FEEDBACK_MIN_LINES + pad,\n maxHeight: lineHeight * FEEDBACK_MAX_LINES + pad,\n };\n};\n","/**\n * decision.ts — the pure decision logic for the review gate.\n *\n * `decideReview` is the AI seam, mirroring `selectTourSteps` exactly: ONE function that\n * turns an optional injected decision into the live verdict. No injected decision → the\n * local rules stand; a server decision present → it OVERRIDES them. Building the seam now\n * is the whole cost of being AI-ready: today the host passes local rules only; later the\n * Wire server computes `{fire, reason}` from the onboarding learnings and the host passes\n * it straight in — zero app changes.\n *\n * // v1 (local rules only)\n * const verdict = decideReview(evaluateGate(rules, signals));\n *\n * // AI on (server-provided decision wins)\n * const verdict = decideReview(evaluateGate(rules, signals), serverDecision);\n *\n * `evaluateGate`, `routeRating`, and `buildReviewSubmission` are pure so the gate logic\n * is verifiable in isolation (the kit has no test runner; correctness lives in these).\n */\nimport type {\n RatingRoute,\n ReviewDecision,\n ReviewSubmission,\n} from \"./types\";\n\n/**\n * The seam. `decision` absent → the local verdict stands; present → it overrides.\n * Same contract philosophy as `selectTourSteps(catalog, selection?)`.\n */\nexport const decideReview = (\n local: ReviewDecision,\n decision?: ReviewDecision,\n): ReviewDecision => decision ?? local;\n\n/** The signals the local rules evaluate against (read from injected sync storage + host). */\nexport interface GateSignals {\n /** Sessions observed on this device (incl. the current one). */\n sessions: number;\n /** Tracked app events observed (host-owned). */\n events: number;\n /** Epoch ms of the last time the gate was shown, or null if never. */\n lastShownAt: number | null;\n /** Whether the once-per-version gate is already satisfied. */\n seen: boolean;\n /** Current epoch ms (injected so the function stays pure). */\n now: number;\n}\n\n/** The resolved local rules (defaults applied). */\nexport interface GateRules {\n enabled: boolean;\n minSessions: number;\n minEvents: number;\n cooldownDays: number;\n oncePerVersion: boolean;\n}\n\n/** Fill a partial `ReviewConfig` with the rule defaults. */\nexport const resolveRules = (config: {\n enabled?: boolean;\n minSessions?: number;\n minEvents?: number;\n cooldownDays?: number;\n oncePerVersion?: boolean;\n}): GateRules => ({\n enabled: config.enabled ?? true,\n minSessions: config.minSessions ?? 0,\n minEvents: config.minEvents ?? 0,\n cooldownDays: config.cooldownDays ?? 0,\n oncePerVersion: config.oncePerVersion ?? true,\n});\n\nconst MS_PER_DAY = 86_400_000;\n\n/**\n * Deterministic v1 local decision. Order: disabled → once-gate → min-sessions →\n * min-events → cooldown → fire. Reasons mirror the server evaluator's vocabulary.\n */\nexport const evaluateGate = (rules: GateRules, s: GateSignals): ReviewDecision => {\n if (!rules.enabled) return { fire: false, reason: \"disabled\" };\n if (rules.oncePerVersion && s.seen) return { fire: false, reason: \"already_shown\" };\n if (s.sessions < rules.minSessions) return { fire: false, reason: \"min_sessions\" };\n if (s.events < rules.minEvents) return { fire: false, reason: \"min_events\" };\n if (rules.cooldownDays > 0 && s.lastShownAt != null) {\n const elapsedDays = (s.now - s.lastShownAt) / MS_PER_DAY;\n if (elapsedDays < rules.cooldownDays) return { fire: false, reason: \"cooldown\" };\n }\n return { fire: true, reason: \"local_rules_met\" };\n};\n\n/**\n * Route a rating. 5 stars → the native store review; 1-4 → the feedback form. The\n * sentiment question itself is neutral — this is the ONLY place 5 is treated specially,\n * never a button labeled \"rate us 5 stars\" (see the store-policy notes in the README).\n */\nexport const routeRating = (stars: number): RatingRoute =>\n stars >= 5 ? \"store\" : \"feedback\";\n\n/** Build the `POST /v1/reviews` body. Feedback text/contact belong ONLY here, never in events. */\nexport const buildReviewSubmission = (input: {\n stars: number;\n feedbackText?: string;\n suggestion?: string;\n contact?: string;\n sessionId?: string;\n source?: string;\n meta?: Record<string, unknown>;\n}): ReviewSubmission => {\n const body: ReviewSubmission = { stars: input.stars };\n const text = input.feedbackText?.trim();\n if (text) body.feedback_text = text;\n const suggestion = input.suggestion?.trim();\n if (suggestion) body.suggestion = suggestion;\n const contact = input.contact?.trim();\n if (contact) body.contact = contact;\n if (input.sessionId) body.session_id = input.sessionId;\n if (input.source) body.source = input.source;\n if (input.meta && Object.keys(input.meta).length > 0) body.meta = input.meta;\n return body;\n};\n","/**\n * decision.ts - pure decision logic for the questionnaire gate.\n *\n * `decideQuestionnaire` is the AI seam, mirroring `decideReview` exactly: ONE function\n * that turns an optional injected decision into the live verdict. No injected decision →\n * the local rules stand; a server decision present → it OVERRIDES them. The local firing\n * rules (`evaluateGate` / `resolveRules`) and their signals are the SAME generic, pure\n * helpers the review gate uses - reused here rather than duplicated, since a questionnaire\n * verdict is structurally the same `{fire, reason}` contract.\n *\n * `buildQuestionnaireSubmission` is pure too, so the \"opinion required, empties omitted\"\n * shaping of the POST body is verifiable in isolation (the kit has no runtime test runner).\n */\nimport { decideReview, evaluateGate, resolveRules } from \"../reviews/decision\";\nimport type {\n QuestionnaireAnswers,\n QuestionnaireDecision,\n QuestionnaireMeta,\n QuestionnaireSubmission,\n} from \"./types\";\n\n// Re-export the generic local-rule helpers under the questionnaire's own namespace so a\n// host wires the fallback without reaching into the reviews module.\nexport { evaluateGate, resolveRules } from \"../reviews/decision\";\nexport type { GateSignals, GateRules } from \"../reviews/decision\";\n\n/**\n * The seam. `decision` absent → the local verdict stands; present → it overrides. Same\n * contract philosophy as `decideReview(local, decision?)`.\n */\nexport const decideQuestionnaire = (\n local: QuestionnaireDecision,\n decision?: QuestionnaireDecision,\n): QuestionnaireDecision => decideReview(local, decision);\n\n/**\n * Build the `POST /v1/questionnaires/{id}/responses` body. `opinion` is the only mandatory\n * answer (trimmed); `improve`/`suggestions` are trimmed and sent as `null` when empty. The\n * `meta` bucket carries the non-PII correlation fields (user_id / device_key / session_count),\n * each defaulting to null. Free text lives ONLY here, never in analytics events.\n */\nexport const buildQuestionnaireSubmission = (input: {\n answers: QuestionnaireAnswers;\n sessionId?: string | null;\n meta?: QuestionnaireMeta;\n}): QuestionnaireSubmission => {\n const opinion = input.answers.opinion.trim();\n const improve = input.answers.improve?.trim();\n const suggestions = input.answers.suggestions?.trim();\n return {\n session_id: input.sessionId ?? null,\n answers: {\n opinion,\n improve: improve ? improve : null,\n suggestions: suggestions ? suggestions : null,\n },\n meta: {\n user_id: input.meta?.user_id ?? null,\n device_key: input.meta?.device_key ?? null,\n session_count: input.meta?.session_count ?? null,\n },\n };\n};\n","/**\n * transport.ts - kit → Wire server requests for the questionnaire module, all fire-and-forget\n * (a pre-onboarding questionnaire must NEVER break the app). Mirrors `submitReview`: a thin\n * fetch wrapper, Bearer tenant key, swallow every error.\n *\n * • submitQuestionnaireResponse → POST {serverUrl}/v1/questionnaires/{id}/responses\n * • fetchQuestionnaireDecision → GET {serverUrl}/v1/questionnaires/decision (best-effort)\n *\n * The decision fetch is HARD fail-open on the \"never show\" side: an unreachable endpoint, a\n * non-2xx (incl. 404), a missing `fetch`, or bad JSON all resolve to null, so a host wiring\n * pure server-directed firing simply shows nothing - exactly the reviews decision seam.\n */\nimport type {\n QuestionnaireDecisionResponse,\n QuestionnaireSubmission,\n QuestionnaireTarget,\n} from \"./types\";\n\n/**\n * POST a questionnaire response. Fire-and-forget: a missing target, a missing id, a build\n * error, a missing `fetch`, or a network failure is swallowed and the call returns immediately.\n */\nexport const submitQuestionnaireResponse = (\n target: QuestionnaireTarget | undefined,\n id: string,\n submission: QuestionnaireSubmission,\n): void => {\n if (!target?.serverUrl || !id) return;\n try {\n const base = target.serverUrl.replace(/\\/$/, \"\");\n const url = `${base}/v1/questionnaires/${encodeURIComponent(id)}/responses`;\n const headers: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (target.apiKey) headers.Authorization = `Bearer ${target.apiKey}`;\n void fetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(submission),\n }).catch(() => {\n /* best-effort, swallow */\n });\n } catch {\n /* URL/JSON/missing-fetch - swallow */\n }\n};\n\n/** Options for the best-effort decision fetch. `deviceKey` groups a device's sessions server-side. */\nexport interface FetchQuestionnaireDecisionOptions {\n /** The onboarding/session id to correlate with, when known. */\n sessionId?: string;\n /** A stable, non-PII device id - the decision endpoint reads it for min-sessions. */\n deviceKey?: string;\n}\n\n/**\n * Best-effort fetch of the server firing decision. Returns the parsed\n * `{ fire, reason, questionnaire }` on a 2xx, else null. NEVER throws - any failure resolves\n * to null (fail-safe: the host shows nothing). The kit does not call this internally; a host\n * that wants server-directed firing awaits it and passes the result to `useQuestionnaireGate`\n * (the same AI seam the reviews gate uses).\n */\nexport const fetchQuestionnaireDecision = async (\n target: QuestionnaireTarget | undefined,\n options: FetchQuestionnaireDecisionOptions = {},\n): Promise<QuestionnaireDecisionResponse | null> => {\n if (!target?.serverUrl) return null;\n try {\n const base = target.serverUrl.replace(/\\/$/, \"\");\n const params = new URLSearchParams();\n if (options.sessionId) params.set(\"session_id\", options.sessionId);\n if (options.deviceKey) params.set(\"device_key\", options.deviceKey);\n const qs = params.toString();\n const url = `${base}/v1/questionnaires/decision${qs ? `?${qs}` : \"\"}`;\n const headers: Record<string, string> = {};\n if (target.apiKey) headers.Authorization = `Bearer ${target.apiKey}`;\n const res = await fetch(url, { headers });\n if (!res || !res.ok) return null;\n const json = (await res.json()) as QuestionnaireDecisionResponse | null;\n return json ?? null;\n } catch {\n /* unreachable / non-2xx / bad JSON / missing-fetch - never show */\n return null;\n }\n};\n","/**\n * QuestionnaireGate - the skippable pre-onboarding questionnaire popup.\n *\n * A centered popup (over the shared `CenteredModal` shell) with three inputs:\n * • a MANDATORY multiline \"what do you think about the app\" box (~4 lines), and\n * • two OPTIONAL smaller boxes (\"help us improve\" + \"suggestions\").\n * Send is disabled ONLY until the opinion box has text; the two optional boxes never gate it.\n * The gate is ALWAYS skippable (a \"Not now\"/\"Skip\" link plays the popup exit), so it is never\n * a dead end. On send it builds the contract body and fire-and-forgets it to the Wire server,\n * then shows a warm thank-you until the user taps the backdrop to close.\n *\n * The form NEVER asks for identity (no name / email / contact field): the host already\n * identifies the user via `sessionId` + `meta` (user_id, device_key, session_count), which is\n * all the POST body carries alongside the answers. Every visual value comes from the\n * OnboardingTheme; analytics are callback-based (no PII - answers go ONLY in the POST body).\n */\nimport React, { useCallback, useMemo, useState } from \"react\";\nimport { Pressable, StyleSheet, Text, TextInput, View } from \"react-native\";\n\nimport { Button } from \"../components/Button\";\nimport { CenteredModal, type CenteredModalHandle } from \"../components/CenteredModal\";\nimport { feedbackInputHeights } from \"../reviews/feedbackForm\";\nimport { useOnboardingTheme } from \"../theme/ThemeContext\";\nimport type { OnboardingTheme } from \"../theme/types\";\nimport { bodyStyle, captionStyle, headingStyle } from \"../theme/typography\";\nimport { buildQuestionnaireSubmission } from \"./decision\";\nimport { submitQuestionnaireResponse } from \"./transport\";\nimport type { QuestionnaireGateProps } from \"./types\";\n\nconst mergeThemeOver = (\n base: OnboardingTheme,\n override?: Partial<OnboardingTheme>,\n): OnboardingTheme => {\n if (!override) return base;\n return {\n colors: { ...base.colors, ...override.colors },\n fonts: { ...base.fonts, ...override.fonts },\n radius: { ...base.radius, ...override.radius },\n spacing: { ...base.spacing, ...override.spacing },\n button: { ...base.button, ...override.button },\n };\n};\n\ntype Phase = \"form\" | \"thanks\";\n\nexport const QuestionnaireGate: React.FC<QuestionnaireGateProps> = ({\n questionnaire,\n target,\n sessionId,\n meta,\n onEvent,\n onResolved,\n onShown,\n theme: themeOverride,\n dismissible = true,\n}) => {\n const active = useOnboardingTheme();\n const t = useMemo(() => mergeThemeOver(active, themeOverride), [active, themeOverride]);\n const modalRef = React.useRef<CenteredModalHandle>(null);\n\n const id = questionnaire.id;\n const [phase, setPhase] = useState<Phase>(\"form\");\n const [opinion, setOpinion] = useState(\"\");\n const [improve, setImprove] = useState(\"\");\n const [suggestions, setSuggestions] = useState(\"\");\n\n // Fire questionnaire_prompt_shown exactly ONCE per mount. The callbacks are read through refs\n // (not effect deps) so an inline `onShown`/`onEvent` - recreated every render, as real hosts\n // idiomatically pass - never re-runs the effect. Empty deps + the shownRef guard make this a\n // true fire-once, immune to prop-identity churn.\n const shownRef = React.useRef(false);\n const onShownRef = React.useRef(onShown);\n const onEventRef = React.useRef(onEvent);\n onShownRef.current = onShown;\n onEventRef.current = onEvent;\n React.useEffect(() => {\n if (shownRef.current) return;\n shownRef.current = true;\n onShownRef.current?.();\n onEventRef.current?.({ name: \"questionnaire_prompt_shown\", id });\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const resolve = useCallback(() => {\n onResolved?.();\n }, [onResolved]);\n\n // The opinion box is the ONLY gate on Send. The two optional boxes never disable it.\n const canSend = opinion.trim().length > 0;\n\n const send = useCallback(() => {\n if (!canSend) return;\n // Identity is NEVER collected here - the host already sends user_id / device_key / session\n // via `sessionId` + `meta`. We POST only the three answers.\n const submission = buildQuestionnaireSubmission({\n answers: { opinion, improve, suggestions },\n sessionId,\n meta,\n });\n submitQuestionnaireResponse(target, id, submission);\n onEvent?.({ name: \"questionnaire_submitted\", id });\n setPhase(\"thanks\");\n resolve();\n }, [canSend, opinion, improve, suggestions, sessionId, meta, target, id, onEvent, resolve]);\n\n // The in-card \"Not now\"/\"Skip\" link plays the popup's exit animation (which resolves on\n // completion, the same path a backdrop tap takes). Emit the dismissed moment first.\n const handleDismissTap = useCallback(() => {\n onEvent?.({ name: \"questionnaire_dismissed\", id });\n modalRef.current?.close();\n }, [onEvent, id]);\n\n const title = questionnaire.title ?? \"How's it going so far?\";\n const opinionLabel = questionnaire.opinion_label ?? \"What do you think about the app?\";\n const improveLabel = questionnaire.improve_label ?? \"How can we improve? (optional)\";\n const suggestionsLabel = questionnaire.suggestions_label ?? \"Any suggestions? (optional)\";\n\n // A multiline box ~4 lines tall (capped at ~5), derived from the theme's body line-height so\n // it scales with the app's font config; padding is folded in so the lines are visible.\n const opinionBox = feedbackInputHeights(t.fonts, t.spacing.md);\n\n const content = (\n <>\n <Text style={[headingStyle(t.fonts), { color: t.colors.text }]}>{title}</Text>\n\n {phase === \"form\" && (\n <>\n <Text style={[captionStyle(t.fonts), { color: t.colors.textMuted }]}>\n A few words is plenty. Only the first box is required.\n </Text>\n\n {/* MANDATORY opinion box - the only gate on Send. */}\n <Text style={[captionStyle(t.fonts), { color: t.colors.textMuted }]}>{opinionLabel}</Text>\n <TextInput\n value={opinion}\n onChangeText={setOpinion}\n placeholder=\"e.g. it feels fast and clear\"\n placeholderTextColor={t.colors.textMuted}\n multiline\n textAlignVertical=\"top\"\n style={[\n bodyStyle(t.fonts),\n styles.input,\n {\n color: t.colors.text,\n backgroundColor: t.colors.background,\n borderRadius: t.radius.md,\n borderColor: t.colors.border,\n padding: t.spacing.md,\n minHeight: opinionBox.minHeight,\n maxHeight: opinionBox.maxHeight,\n },\n ]}\n />\n\n {/* OPTIONAL \"help us improve\" box - never gates Send. */}\n <Text style={[captionStyle(t.fonts), { color: t.colors.textMuted }]}>{improveLabel}</Text>\n <TextInput\n value={improve}\n onChangeText={setImprove}\n placeholder=\"e.g. fewer steps to start\"\n placeholderTextColor={t.colors.textMuted}\n style={[\n bodyStyle(t.fonts),\n styles.input,\n {\n color: t.colors.text,\n backgroundColor: t.colors.background,\n borderRadius: t.radius.md,\n borderColor: t.colors.border,\n paddingHorizontal: t.spacing.md,\n paddingVertical: t.spacing.sm,\n minHeight: t.button.height,\n },\n ]}\n />\n\n {/* OPTIONAL \"suggestions\" box - never gates Send. */}\n <Text style={[captionStyle(t.fonts), { color: t.colors.textMuted }]}>\n {suggestionsLabel}\n </Text>\n <TextInput\n value={suggestions}\n onChangeText={setSuggestions}\n placeholder=\"e.g. a dark theme\"\n placeholderTextColor={t.colors.textMuted}\n style={[\n bodyStyle(t.fonts),\n styles.input,\n {\n color: t.colors.text,\n backgroundColor: t.colors.background,\n borderRadius: t.radius.md,\n borderColor: t.colors.border,\n paddingHorizontal: t.spacing.md,\n paddingVertical: t.spacing.sm,\n minHeight: t.button.height,\n },\n ]}\n />\n\n <Button title=\"Send\" onPress={send} variant=\"primary\" fullWidth disabled={!canSend} />\n {dismissible && (\n <Pressable onPress={handleDismissTap} hitSlop={8}>\n <Text style={[captionStyle(t.fonts), styles.link, { color: t.colors.textMuted }]}>\n Not now\n </Text>\n </Pressable>\n )}\n </>\n )}\n\n {phase === \"thanks\" && (\n <Text style={[bodyStyle(t.fonts), { color: t.colors.textMuted }]}>\n Thank you. This helps us make it better.\n </Text>\n )}\n </>\n );\n\n return (\n <CenteredModal ref={modalRef} theme={t} dismissible={dismissible} onDismiss={resolve}>\n {content}\n </CenteredModal>\n );\n};\n\nQuestionnaireGate.displayName = \"QuestionnaireGate\";\n\nconst styles = StyleSheet.create({\n input: { borderWidth: 1 },\n link: { textAlign: \"center\", textDecorationLine: \"underline\" },\n});\n\nexport default QuestionnaireGate;\n","/**\n * Module-level runtime for the coachmark engine: the injected sync storage, the\n * global `isTestingCoachmark` flag, and the seen-gate helpers built on top.\n *\n * WHY module singletons (not just context): the tour hooks and the anchor\n * registry are driven imperatively from anywhere in the tree (a screen deep\n * under the navigator), while the storage + testing flag are configured once at\n * the root by `CoachmarkProvider`. Singletons let a hook read the gate without\n * every call site threading the storage through props. The provider is still the\n * single writer — it calls `setCoachmarkStorage` / `setCoachmarkTesting` on mount.\n */\nimport type { CoachmarkStorage } from \"./types\";\n\nlet storageSingleton: CoachmarkStorage | null = null;\nlet testing = false;\n// The coachmarks feature kill switch (from GET /v1/features). Default true = fail-open: with no\n// flags fetched, coachmarks behave exactly as before. CoachmarkProvider writes it from the\n// resolved flags. Disabled → the tour never ARMS and the overlay `show()` is a no-op, so nothing\n// paints and — critically — no once-gate is written, so re-enabling replays the tour correctly.\nlet coachmarksEnabled = true;\n\n/**\n * Set the coachmarks master switch (from the resolved feature flags). Default true (fail-open).\n * Written by CoachmarkProvider; read imperatively by the tour arm + the overlay store.\n */\nexport const setCoachmarksEnabled = (value: boolean): void => {\n coachmarksEnabled = value;\n};\n\n/** Whether the coachmarks module is enabled. False → tours/overlays are silently skipped. */\nexport const areCoachmarksEnabled = (): boolean => coachmarksEnabled;\n\n/** Set (or clear) the injected sync gate storage. Called by CoachmarkProvider. */\nexport const setCoachmarkStorage = (storage: CoachmarkStorage | null): void => {\n storageSingleton = storage;\n};\n\n/** The currently-injected gate storage, or null if no provider is mounted. */\nexport const getCoachmarkStorage = (): CoachmarkStorage | null => storageSingleton;\n\n/**\n * Toggle the global QA replay flag. When true, EVERY seen-gate reads as unseen\n * and every seen-write is suppressed, so every tour + showcase replays on each\n * mount. The app flips ONE boolean to QA the whole coachmark surface.\n */\nexport const setCoachmarkTesting = (value: boolean): void => {\n testing = value;\n};\n\n/** Whether replay-everything QA mode is on. */\nexport const isCoachmarkTesting = (): boolean => testing;\n\n/** Gate key for a tour: `wire_coachmark_<tourId>_seen`. */\nexport const coachmarkGateKey = (tourId: string): string =>\n `wire_coachmark_${tourId}_seen`;\n\n/** Gate key for a feature showcase: `wire_showcase_<id>_seen`. */\nexport const showcaseGateKey = (showcaseId: string): string =>\n `wire_showcase_${showcaseId}_seen`;\n\n/** Marker value written for a seen gate. */\nconst SEEN_VALUE = \"1\";\n\n/**\n * Read a seen gate. Testing mode ALWAYS reports unseen (so everything replays).\n * A missing storage (no provider) also reports unseen — the safe default is to\n * show the hint rather than silently swallow it. `storageOverride` /\n * `testingOverride` let a self-contained component (e.g. FeatureShowcase) pass\n * its own storage without relying on the provider singleton.\n */\nexport const hasSeenGate = (\n key: string,\n storageOverride?: CoachmarkStorage | null,\n testingOverride?: boolean,\n): boolean => {\n if (testingOverride ?? testing) return false;\n const storage = storageOverride ?? storageSingleton;\n if (!storage) return false;\n try {\n return storage.getItem(key) === SEEN_VALUE;\n } catch {\n return false;\n }\n};\n\n/**\n * Mark a gate seen. Suppressed in testing mode (so replay never persists) and a\n * no-op without storage. Best-effort — a throwing adapter never breaks the tour.\n */\nexport const markSeenGate = (\n key: string,\n storageOverride?: CoachmarkStorage | null,\n testingOverride?: boolean,\n): void => {\n if (testingOverride ?? testing) return;\n const storage = storageOverride ?? storageSingleton;\n if (!storage) return;\n try {\n storage.setItem(key, SEEN_VALUE);\n } catch {\n // Best-effort: a failed write just means the hint may show once more.\n }\n};\n","/**\n * The fail-open defaults + a tolerant parser for the server's flags payload.\n *\n * THE CONTRACT: fail-open, never fail-dark. An absent server object, a missing module key, a\n * malformed value, or an unreachable endpoint must resolve to the MOST PERMISSIVE state — every\n * module ON, onboarding `ai` — so a control-plane outage can only ever be more permissive than\n * the dashboard intends. It can never dark a tenant's activation. Matches the server's own\n * documented fail-open default (FEATURES.md: \"local defaults — every module on, onboarding ai\").\n */\nimport type { OnboardingMode, WireFeatures } from \"./types\";\n\n/**\n * ALL ON, onboarding `ai`. Frozen + module-level so its reference is stable across every call\n * (consumers that list resolved flags in an effect dep array never loop on a fresh identity).\n */\nexport const defaultWireFeatures: WireFeatures = Object.freeze({\n coachmarks: Object.freeze({ enabled: true }),\n showcase: Object.freeze({ enabled: true }),\n onboarding: Object.freeze({ mode: \"ai\" as OnboardingMode }),\n review: Object.freeze({ enabled: true }),\n questionnaire: Object.freeze({ enabled: true }),\n announcements: Object.freeze({ enabled: true }),\n}) as WireFeatures;\n\nconst VALID_MODES: readonly OnboardingMode[] = [\"ai\", \"static\", \"off\"];\n\n/** Read a `{ enabled: boolean }` module, defaulting to ON when absent/malformed (fail-open). */\nconst readEnabled = (raw: unknown): { enabled: boolean } => {\n if (raw && typeof raw === \"object\" && typeof (raw as { enabled?: unknown }).enabled === \"boolean\") {\n return { enabled: (raw as { enabled: boolean }).enabled };\n }\n return { enabled: true };\n};\n\n/**\n * Narrow the server's (untrusted) JSON to a complete `WireFeatures`. Every field is filled with\n * its fail-open default when the server omits it or sends the wrong type, so a partial or\n * malformed payload degrades PERMISSIVELY rather than throwing. A non-object input (null, array,\n * string) → the full defaults. Never throws.\n */\nexport const parseWireFeatures = (raw: unknown): WireFeatures => {\n if (!raw || typeof raw !== \"object\" || Array.isArray(raw)) return defaultWireFeatures;\n const src = raw as Record<string, unknown>;\n const modeRaw = (src.onboarding as { mode?: unknown } | undefined)?.mode;\n const mode: OnboardingMode =\n typeof modeRaw === \"string\" && VALID_MODES.includes(modeRaw as OnboardingMode)\n ? (modeRaw as OnboardingMode)\n : \"ai\";\n return {\n coachmarks: readEnabled(src.coachmarks),\n showcase: readEnabled(src.showcase),\n onboarding: { mode },\n review: readEnabled(src.review),\n questionnaire: readEnabled(src.questionnaire),\n announcements: readEnabled(src.announcements),\n };\n};\n\n/** Value-equality of two resolved flags — lets the hook hold a stable reference across churn. */\nexport const featuresEqual = (a: WireFeatures, b: WireFeatures): boolean =>\n a === b ||\n (a.coachmarks.enabled === b.coachmarks.enabled &&\n a.showcase.enabled === b.showcase.enabled &&\n a.onboarding.mode === b.onboarding.mode &&\n a.review.enabled === b.review.enabled &&\n a.questionnaire.enabled === b.questionnaire.enabled &&\n a.announcements.enabled === b.announcements.enabled);\n","/**\n * The last-known-flags cache — persisted through the host's optional `storage`.\n *\n * WHY: the fetch is best-effort and fail-open. When the control plane is unreachable we serve the\n * LAST value we saw rather than snapping to defaults, so a tenant that intentionally disabled a\n * module during an incident keeps it disabled across an outage instead of flapping back on. That\n * is \"stale-while-error\": a fresh cache (< TTL) is trusted, and on a fetch error even a STALE\n * cache is served (any age beats a guess). Every operation is best-effort — a broken/hung storage\n * degrades to defaults, never a throw and never a stuck gate (mirrors persistedSession).\n */\nimport { parseWireFeatures } from \"./defaults\";\nimport type { WireFeatures } from \"./types\";\nimport type { WireOnboardingStorage } from \"../session/persistedSession\";\n\n/**\n * TTL for a cached flags entry, aligned to the server's `Cache-Control: private, max-age=300`.\n * A cache younger than this is \"fresh\"; older is \"stale\" (still served on a fetch error).\n */\nexport const DEFAULT_FEATURES_TTL_MS = 300_000;\n\n/** Ceiling on the storage read so a hung adapter degrades to defaults, never a stuck resolve. */\nconst READ_TIMEOUT_MS = 1_500;\n\n/** Storage key for a tenant's cached flags, e.g. `wireai:features:myelino`. */\nexport const featuresCacheKey = (appId?: string): string =>\n `wireai:features:${appId && appId.length > 0 ? appId : \"default\"}`;\n\n/** A cached flags entry: the resolved flags + the epoch-ms they were fetched. */\nexport interface CachedFeatures {\n features: WireFeatures;\n /** Age of the entry in ms at read time. */\n ageMs: number;\n}\n\nconst withTimeout = <T>(p: Promise<T>, ms: number): Promise<T | undefined> => {\n let timer: ReturnType<typeof setTimeout>;\n const timeout = new Promise<undefined>((resolve) => {\n timer = setTimeout(() => resolve(undefined), ms);\n });\n return Promise.race([p, timeout]).finally(() => clearTimeout(timer));\n};\n\n/** Whether a cache entry is still within the TTL (server max-age aligned). */\nexport const isFeaturesFresh = (ageMs: number, ttlMs: number = DEFAULT_FEATURES_TTL_MS): boolean =>\n ageMs >= 0 && ageMs < ttlMs;\n\n/**\n * Read the cached flags for `key`, or `undefined` when absent/corrupt/timed-out. Returns the\n * value AND its age so a caller can apply a TTL (fresh) or ignore it (stale-while-error). Never\n * throws; never hangs past the read timeout. The stored value is re-parsed through\n * `parseWireFeatures`, so a partially-corrupt entry still degrades fail-open.\n */\nexport const readCachedFeatures = async (\n storage: WireOnboardingStorage,\n key: string,\n): Promise<CachedFeatures | undefined> => {\n let raw: string | null | undefined;\n try {\n raw = await withTimeout(storage.getItem(key), READ_TIMEOUT_MS);\n } catch {\n return undefined;\n }\n if (!raw) return undefined;\n try {\n const parsed: unknown = JSON.parse(raw);\n if (parsed && typeof parsed === \"object\" && typeof (parsed as { ts?: unknown }).ts === \"number\") {\n const ts = (parsed as { ts: number }).ts;\n return {\n features: parseWireFeatures((parsed as { features?: unknown }).features),\n ageMs: Date.now() - ts,\n };\n }\n } catch {\n // Corrupt entry → treat as absent; the caller falls back to defaults / a fresh fetch.\n }\n return undefined;\n};\n\n/** Persist `{ features, ts }` under `key` — fire-and-forget, all errors swallowed. */\nexport const writeCachedFeatures = (\n storage: WireOnboardingStorage,\n key: string,\n features: WireFeatures,\n): void => {\n try {\n void storage\n .setItem(key, JSON.stringify({ features, ts: Date.now() }))\n .catch(() => {});\n } catch {\n // Best-effort: a failed write just means the next launch has no cache to fall back to.\n }\n};\n","/**\n * fetchWireFeatures — read the per-module kill switches from `GET {serverUrl}/v1/features`.\n *\n * Contract (server: chohra-med/wireai-onboarding-server, routers/onboarding.py):\n * GET {serverUrl}/v1/features\n * Authorization: Bearer {apiKey}\n * 200 → { coachmarks:{enabled}, showcase:{enabled}, onboarding:{mode}, review:{enabled},\n * announcements:{enabled} } (Cache-Control: private, max-age=300)\n *\n * THE CONTRACT — fail-open, never fail-dark. Unreachable, timed-out, unauthorized (401),\n * server-error (5xx), or malformed JSON all resolve to the LAST CACHED value, else the\n * all-on defaults. No throw ever reaches the host: this is timeout-capped and swallows every\n * error, exactly like the session's best-effort reads. A control-plane outage can only ever be\n * MORE permissive than the dashboard intends — it can never dark a tenant's activation.\n */\nimport { readCachedFeatures, featuresCacheKey, writeCachedFeatures } from \"./cache\";\nimport { defaultWireFeatures, parseWireFeatures } from \"./defaults\";\nimport type { WireFeatures, WireFeaturesConfig } from \"./types\";\nimport type { WireOnboardingStorage } from \"../session/persistedSession\";\n\nconst DEFAULT_TIMEOUT_MS = 4_000;\n\n/** The fail-open fallback: last cached value (any age — stale-while-error), else defaults. */\nconst failOpen = async (\n storage: WireOnboardingStorage | undefined,\n key: string,\n): Promise<WireFeatures> => {\n if (!storage) return defaultWireFeatures;\n const cached = await readCachedFeatures(storage, key);\n return cached?.features ?? defaultWireFeatures;\n};\n\n/** GET with a hard timeout via AbortController (falls back to no-abort where unsupported). */\nconst fetchWithTimeout = async (\n url: string,\n apiKey: string,\n timeoutMs: number,\n): Promise<Response> => {\n const controller =\n typeof AbortController !== \"undefined\" ? new AbortController() : undefined;\n const timer = setTimeout(() => controller?.abort(), timeoutMs);\n try {\n return await fetch(url, {\n method: \"GET\",\n headers: { Accept: \"application/json\", Authorization: `Bearer ${apiKey}` },\n signal: controller?.signal,\n });\n } finally {\n clearTimeout(timer);\n }\n};\n\n/**\n * Resolve the tenant's feature flags. Attempts the network once; on ANY failure returns the\n * cached value (any age), else the all-on defaults. On success, persists the fresh value to the\n * cache (fire-and-forget) and returns it. Never throws.\n *\n * const flags = await fetchWireFeatures({ serverUrl, apiKey, appId, storage });\n * if (!flags.coachmarks.enabled) { /* skip the tour * / }\n */\nexport const fetchWireFeatures = async (\n config?: WireFeaturesConfig,\n): Promise<WireFeatures> => {\n const key = featuresCacheKey(config?.appId);\n const storage = config?.storage;\n\n // Nothing to fetch (no creds) → cache, else defaults. Lets a host mount the provider before\n // its config is ready without ever darking.\n if (!config?.serverUrl || !config?.apiKey) return failOpen(storage, key);\n\n const url = `${config.serverUrl.replace(/\\/$/, \"\")}/v1/features`;\n const timeoutMs = config.timeoutMs && config.timeoutMs > 0 ? config.timeoutMs : DEFAULT_TIMEOUT_MS;\n\n try {\n const res = await fetchWithTimeout(url, config.apiKey, timeoutMs);\n // 401 (bad/rotated key), 5xx, any non-2xx → last-known, never dark.\n if (!res || !res.ok) return failOpen(storage, key);\n const json: unknown = await res.json(); // throws on malformed body → caught below\n const features = parseWireFeatures(json);\n if (storage) writeCachedFeatures(storage, key, features);\n return features;\n } catch {\n // Timeout/abort, network error, missing fetch, or malformed JSON — all fail open.\n return failOpen(storage, key);\n }\n};\n","/**\n * useWireFeatures — resolve the tenant's feature flags for a component tree, once per mount.\n *\n * Returns the all-on defaults immediately (fail-open), then swaps to the fetched value when the\n * network resolves. When no `serverUrl`+`apiKey` is supplied it never fetches and just returns\n * defaults, so a host that never adopts flags sees ZERO behavior change and ZERO network calls.\n *\n * frequent_rules #11 (unstable inline props): the config is idiomatically passed inline\n * (`config={{ serverUrl, apiKey }}`), a fresh identity every render. We stabilize the fetch key\n * by VALUE so the effect fires once per real credential change, and we only swap the returned\n * flags object when its VALUES change — so a host that lists the returned flags in an effect dep\n * array settles in a bounded number of commits instead of looping (enforced by the canary suite).\n */\nimport { useEffect, useRef, useState } from \"react\";\n\nimport { defaultWireFeatures, featuresEqual } from \"./defaults\";\nimport { fetchWireFeatures } from \"./fetchWireFeatures\";\nimport type { WireFeatures, WireFeaturesConfig } from \"./types\";\n\n/** Hold a stable reference for a value while its CONTENT is unchanged (see reviews/equality.ts). */\nconst useStableValue = <T,>(value: T, isEqual: (a: T, b: T) => boolean): T => {\n const ref = useRef(value);\n if (!isEqual(ref.current, value)) ref.current = value;\n return ref.current;\n};\n\n/** The credential slice that actually drives a refetch — stabilized by value. */\nconst sameFetchKey = (a: WireFeaturesConfig | undefined, b: WireFeaturesConfig | undefined): boolean =>\n a?.serverUrl === b?.serverUrl && a?.apiKey === b?.apiKey && a?.appId === b?.appId;\n\nexport const useWireFeatures = (config?: WireFeaturesConfig): WireFeatures => {\n const stableConfig = useStableValue(config, sameFetchKey);\n const [flags, setFlags] = useState<WireFeatures>(defaultWireFeatures);\n\n const canFetch = !!stableConfig?.serverUrl && !!stableConfig?.apiKey;\n\n useEffect(() => {\n if (!canFetch) {\n // No creds → stay on the all-on defaults (and reset if creds were removed).\n setFlags((prev) => (featuresEqual(prev, defaultWireFeatures) ? prev : defaultWireFeatures));\n return;\n }\n let alive = true;\n // fetchWireFeatures never throws — the .then always runs with a fail-open value.\n void fetchWireFeatures(stableConfig).then((next) => {\n if (!alive) return;\n // Only adopt a NEW reference on a real value change, so a host effect keyed on the\n // returned flags doesn't loop (frequent_rules #11).\n setFlags((prev) => (featuresEqual(prev, next) ? prev : next));\n });\n return () => {\n alive = false;\n };\n }, [canFetch, stableConfig]);\n\n return flags;\n};\n","/**\n * WireFeaturesProvider + useResolvedFeatures — the OPTIONAL one-fetch-serves-all-modules layer.\n *\n * Mount `WireFeaturesProvider` once near your app root with the tenant `config` and it fetches the\n * flags ONCE; every gated surface below it (coachmarks / showcase / reviews) reads the same\n * resolved value from context instead of each fetching its own. It is entirely optional: with no\n * provider, every surface falls back to its own lazy fetch (when given a config) or to the all-on\n * defaults, so modules work with zero setup and a host that never adopts flags is unchanged.\n *\n * NOTE on the built npm subpaths: React context is shared across the kit's subpath entries in the\n * SOURCE-consumption path (the monorepo apps) and in this test suite — one module instance, one\n * context. If you consume the built `wireai-onboarding/*` subpaths as separate bundles, pass the\n * resolved flags (or a `featuresConfig`) to each surface explicitly instead of relying on context.\n */\nimport React, { createContext, useContext, useMemo } from \"react\";\n\nimport { defaultWireFeatures } from \"./defaults\";\nimport { useWireFeatures } from \"./useWireFeatures\";\nimport type { WireFeatures, WireFeaturesConfig } from \"./types\";\n\n/** null = no provider mounted (the signal `useResolvedFeatures` uses to fall back). */\nconst WireFeaturesContext = createContext<WireFeatures | null>(null);\n\nexport interface WireFeaturesProviderProps {\n /** Tenant creds (+ optional storage) to fetch the flags once for the whole tree. */\n config?: WireFeaturesConfig;\n /**\n * Pre-resolved flags to provide directly instead of fetching (e.g. you already have them, or\n * you drive them from your own remote-config). Takes precedence over `config`.\n */\n flags?: WireFeatures;\n children: React.ReactNode;\n}\n\n/**\n * Fetch (or accept) the flags once and broadcast them to descendants. The provider VALUE is\n * memoized (performance rule #6) so a fresh object each render doesn't re-render every consumer.\n */\nexport const WireFeaturesProvider: React.FC<WireFeaturesProviderProps> = ({\n config,\n flags,\n children,\n}) => {\n // Always call the hook (rules of hooks); when `flags` is supplied we pass no config so it\n // never fetches and the explicit flags win below.\n const fetched = useWireFeatures(flags ? undefined : config);\n const value = flags ?? fetched;\n const stable = useMemo(() => value, [value]);\n return <WireFeaturesContext.Provider value={stable}>{children}</WireFeaturesContext.Provider>;\n};\n\nWireFeaturesProvider.displayName = \"WireFeaturesProvider\";\n\n/** Raw context read — `null` when no `WireFeaturesProvider` is mounted above. */\nexport const useWireFeaturesContext = (): WireFeatures | null =>\n useContext(WireFeaturesContext);\n\n/** Options for `useResolvedFeatures`: an explicit override and/or a lazy-fetch config. */\nexport interface ResolveFeaturesOptions {\n /** Explicit flags — win over everything (a surface's own `features` prop). */\n flags?: WireFeatures;\n /** Lazy-fetch config, used ONLY when there is no explicit `flags` and no provider above. */\n config?: WireFeaturesConfig;\n}\n\n/**\n * The resolution every gated surface uses. Precedence:\n * 1. explicit `options.flags` (a surface's own prop) →\n * 2. the nearest `WireFeaturesProvider` (context) →\n * 3. a lazy fetch from `options.config` (serverUrl + apiKey) →\n * 4. the all-on defaults.\n * Always fail-open, always a stable reference for a given value (so it's safe in effect deps).\n */\nexport const useResolvedFeatures = (options?: ResolveFeaturesOptions): WireFeatures => {\n const ctx = useWireFeaturesContext();\n // Only lazily fetch when nothing else provides the flags — otherwise pass no config so the\n // hook stays inert (defaults, no network). The hook is always called (rules of hooks).\n const shouldFetch = !options?.flags && ctx == null;\n const fetched = useWireFeatures(shouldFetch ? options?.config : undefined);\n return options?.flags ?? ctx ?? fetched ?? defaultWireFeatures;\n};\n","/**\n * equality.ts — tiny, dependency-free value-equality helpers used to STABILIZE\n * references across renders.\n *\n * WHY this exists: real integrators pass inline objects — `config={{ id: 'x' }}`,\n * `decision={{ fire: true }}` — whose IDENTITY changes every render even though\n * their VALUES do not. If the review hook let those unstable references flow into\n * a `useMemo`/`useEffect` dependency list (or handed a fresh verdict object back\n * every render), a host that depends on the returned value would re-run its own\n * effect forever → \"Maximum update depth exceeded\". A library must tolerate inline\n * props without looping; these helpers are how the hook holds a stable reference\n * whenever the underlying values are unchanged.\n *\n * Pure (no React import) so it runs under the kit's node:test runner.\n */\nimport type { ReviewDecision } from \"./types\";\n\n/**\n * One-level value-equality of two plain objects (or primitives). Same-reference is\n * equal; two objects are equal when they have the same keys and each value is\n * `Object.is`-equal. Good enough for the flat `config` / `decision` shapes the gate\n * accepts (all leaf values are primitives).\n */\nexport const shallowEqual = (a: unknown, b: unknown): boolean => {\n if (Object.is(a, b)) return true;\n if (\n a == null ||\n b == null ||\n typeof a !== \"object\" ||\n typeof b !== \"object\"\n ) {\n return false;\n }\n const ak = Object.keys(a as Record<string, unknown>);\n const bk = Object.keys(b as Record<string, unknown>);\n if (ak.length !== bk.length) return false;\n for (const k of ak) {\n if (!Object.is((a as Record<string, unknown>)[k], (b as Record<string, unknown>)[k])) {\n return false;\n }\n }\n return true;\n};\n\n/**\n * Structural equality of two review verdicts by their contract fields (`fire`,\n * `reason`). A `null`/`undefined` verdict equals only another absent one. Used to\n * decide whether the hook can keep returning the SAME verdict object (so a host\n * that lists `controller.decision` in an effect dep array never loops).\n */\nexport const sameDecision = (a?: ReviewDecision | null, b?: ReviewDecision | null): boolean => {\n if (a == null || b == null) return a == b;\n return a.fire === b.fire && a.reason === b.reason;\n};\n","/**\n * Runtime helpers for the review gate: the `wire_review_*` gate keys and small\n * typed storage accessors. The gate reuses the SAME injected sync storage + global\n * testing flag as the coachmark engine (see coachmarks/runtime), so a host that\n * mounted CoachmarkProvider gets review gating for free — no second storage to wire.\n */\nimport type { CoachmarkStorage } from \"../coachmarks/types\";\nimport { getCoachmarkStorage } from \"../coachmarks/runtime\";\n\n/** Once-gate key. Keyed by app version when `oncePerVersion` is on, so a new release re-enables. */\nexport const reviewSeenKey = (id: string, version?: string): string =>\n version ? `wire_review_${id}_${version}_seen` : `wire_review_${id}_seen`;\n\n/** Last-shown timestamp key (epoch ms), for the cooldown rule. */\nexport const reviewLastShownKey = (id: string): string => `wire_review_${id}_last`;\n\n/** Session-count key, incremented once per gate mount, for the min-sessions rule. */\nexport const reviewSessionsKey = (id: string): string => `wire_review_${id}_sessions`;\n\n/** Resolve the storage to use: an explicit override, else the provider-injected singleton. */\nexport const resolveStorage = (\n override?: CoachmarkStorage | null,\n): CoachmarkStorage | null => override ?? getCoachmarkStorage();\n\n/** Read an integer from sync storage (0 on a missing/unparseable/throwing read). */\nexport const readInt = (storage: CoachmarkStorage | null, key: string): number => {\n if (!storage) return 0;\n try {\n const raw = storage.getItem(key);\n const n = raw == null ? 0 : parseInt(raw, 10);\n return Number.isFinite(n) ? n : 0;\n } catch {\n return 0;\n }\n};\n\n/** Write an integer to sync storage. Best-effort — a throwing adapter never breaks the gate. */\nexport const writeInt = (storage: CoachmarkStorage | null, key: string, value: number): void => {\n if (!storage) return;\n try {\n storage.setItem(key, String(value));\n } catch {\n // Best-effort.\n }\n};\n","/**\n * Runtime helpers for the questionnaire gate: the `wire_questionnaire_*` gate keys. The\n * gate reuses the SAME injected sync storage + global testing flag as the coachmark engine\n * (via `resolveStorage`/`readInt`/`writeInt`, re-exported from the reviews runtime), so a\n * host that mounted CoachmarkProvider gets questionnaire gating for free - no second storage\n * to wire. Once-per-user is keyed on the questionnaire `id`.\n */\nexport { resolveStorage, readInt, writeInt } from \"../reviews/runtime\";\n\n/** Once-gate key. Keyed by app version when `oncePerVersion` is on, so a new release re-enables. */\nexport const questionnaireSeenKey = (id: string, version?: string): string =>\n version ? `wire_questionnaire_${id}_${version}_seen` : `wire_questionnaire_${id}_seen`;\n\n/** Last-shown timestamp key (epoch ms), for the cooldown rule. */\nexport const questionnaireLastShownKey = (id: string): string =>\n `wire_questionnaire_${id}_last`;\n\n/** Session-count key, incremented once per gate mount, for the min-sessions rule. */\nexport const questionnaireSessionsKey = (id: string): string =>\n `wire_questionnaire_${id}_sessions`;\n","/**\n * useQuestionnaireGate - owns WHEN the questionnaire gate shows.\n *\n * v1 decision = local rules from `config` (enabled / minSessions / minEvents / cooldownDays\n * / oncePerVersion), evaluated against the same injected sync storage as the coachmark\n * engine (`wire_questionnaire_*` keys). The AI seam: pass a server-provided `decision` and it\n * OVERRIDES the local rules (see decideQuestionnaire). The whole cost of being AI-ready is\n * passing that object straight through - mirroring `useReviewGate` exactly.\n *\n * Fallback chain (matches the server design): (1) a server `decision` wins immediately;\n * (2) no decision → after `timeoutFallbackMs` the local rules take over; (3) the once-gate\n * (`wire_questionnaire_<id>_seen`) always applies locally, so a server bug can never re-spam\n * the popup. The global `isTestingCoachmark` flag force-shows the gate for QA replay, and the\n * `questionnaire` master switch (a disabled tenant) wins over everything.\n */\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\n\nimport { hasSeenGate, isCoachmarkTesting, markSeenGate } from \"../coachmarks/runtime\";\nimport { useResolvedFeatures } from \"../features/WireFeaturesProvider\";\nimport { sameDecision, shallowEqual } from \"../reviews/equality\";\nimport { decideQuestionnaire, evaluateGate, resolveRules } from \"./decision\";\nimport {\n questionnaireLastShownKey,\n questionnaireSeenKey,\n questionnaireSessionsKey,\n readInt,\n resolveStorage,\n writeInt,\n} from \"./runtime\";\nimport type {\n QuestionnaireDecision,\n QuestionnaireGateController,\n UseQuestionnaireGateOptions,\n} from \"./types\";\n\n/**\n * Hold a stable reference for a value while its CONTENT is unchanged. Integrators pass inline\n * `config` / `decision` objects (idiomatic React) whose identity flips every render; feeding\n * those straight into the deps below would recompute the verdict - and mint a fresh verdict\n * object - on every render, which loops any host that lists the returned verdict in its own\n * effect. Comparing by value here keeps the reference (and everything derived from it) stable.\n */\nconst useStableValue = <T,>(value: T, isEqual: (a: T, b: T) => boolean): T => {\n const ref = useRef(value);\n if (!isEqual(ref.current, value)) ref.current = value;\n return ref.current;\n};\n\nexport const useQuestionnaireGate = ({\n config: configProp,\n decision: decisionProp,\n events = 0,\n storage,\n isTesting,\n features,\n featuresConfig,\n}: UseQuestionnaireGateOptions): QuestionnaireGateController => {\n // Tolerate inline-identity props: only adopt a new reference on a real value change.\n const config = useStableValue(configProp, shallowEqual);\n const decision = useStableValue(decisionProp, (a, b) => sameDecision(a, b));\n\n // The questionnaire MASTER switch. Disabled → the gate never fires, composing as an extra\n // AND over the local rules + the server decision seam below (fail-open: defaults to enabled).\n const featureEnabled = useResolvedFeatures({ flags: features, config: featuresConfig })\n .questionnaire.enabled;\n\n const testing = isTesting ?? isCoachmarkTesting();\n const seenKey = questionnaireSeenKey(\n config.id,\n config.oncePerVersion === false ? undefined : config.appVersion,\n );\n const lastKey = questionnaireLastShownKey(config.id);\n const sessionsKey = questionnaireSessionsKey(config.id);\n\n // Read (and bump) the session counter ONCE per mount: this mount is a new session.\n const sessions = useState(() => {\n const store = resolveStorage(storage);\n const next = readInt(store, sessionsKey) + 1;\n writeInt(store, sessionsKey, next);\n return next;\n })[0];\n\n // Gate the local rules behind an optional client-side timeout, so a reachable server gets a\n // window to answer first. A present `decision` bypasses the wait entirely.\n const hasServerDecision = decision != null;\n const timeoutMs = config.timeoutFallbackMs;\n const waits = !hasServerDecision && timeoutMs != null && timeoutMs > 0;\n const [elapsed, setElapsed] = useState(!waits);\n useEffect(() => {\n if (!waits) {\n setElapsed(true);\n return;\n }\n const timer = setTimeout(() => setElapsed(true), timeoutMs as number);\n return () => clearTimeout(timer);\n }, [waits, timeoutMs]);\n\n const { visible, verdict } = useMemo(() => {\n // Master kill switch wins over everything, including QA replay: a tenant that turned the\n // questionnaire OFF must never see it, whatever the rules, the server decision, or testing say.\n if (!featureEnabled)\n return {\n visible: false,\n verdict: { fire: false, reason: \"feature_disabled\" } as QuestionnaireDecision,\n };\n\n // QA replay: force-show, bypassing rules + gates (safe: only via __DEV__ && bool).\n if (testing)\n return {\n visible: true,\n verdict: { fire: true, reason: \"testing\" } as QuestionnaireDecision,\n };\n\n const store = resolveStorage(storage);\n const local = evaluateGate(resolveRules(config), {\n sessions,\n events,\n lastShownAt: readInt(store, lastKey) || null,\n seen: hasSeenGate(seenKey, storage, isTesting),\n now: Date.now(),\n });\n const resolved = decideQuestionnaire(local, decision);\n // (3) the once-gate always wins locally, even over a server \"fire\".\n const alreadySeen = hasSeenGate(seenKey, storage, isTesting);\n const ready = hasServerDecision || elapsed;\n return { visible: ready && resolved.fire && !alreadySeen, verdict: resolved };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [\n featureEnabled,\n testing,\n config,\n decision,\n events,\n sessions,\n elapsed,\n hasServerDecision,\n seenKey,\n lastKey,\n storage,\n isTesting,\n ]);\n\n // Stable callbacks + a stable controller object, so a host can safely list any of them in its\n // own effect deps without the effect re-firing every render.\n const shownRef = useRef(false);\n const markShown = useCallback(() => {\n if (shownRef.current) return;\n shownRef.current = true;\n writeInt(resolveStorage(storage), lastKey, Date.now());\n }, [storage, lastKey]);\n const markResolved = useCallback(() => {\n markSeenGate(seenKey, storage, isTesting);\n }, [seenKey, storage, isTesting]);\n\n return useMemo<QuestionnaireGateController>(\n () => ({ visible, decision: verdict, markShown, markResolved }),\n [visible, verdict, markShown, markResolved],\n );\n};\n"]}
|