@wireai/activation 0.13.0 → 0.13.3

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.
Files changed (82) hide show
  1. package/AGENTS.md +7 -4
  2. package/CHANGELOG.md +341 -4
  3. package/README.md +98 -2
  4. package/dist/analytics/index.d.mts +4 -4
  5. package/dist/analytics/index.d.ts +4 -4
  6. package/dist/analytics/index.js +164 -471
  7. package/dist/analytics/index.js.map +1 -1
  8. package/dist/analytics/index.mjs +162 -469
  9. package/dist/analytics/index.mjs.map +1 -1
  10. package/dist/coachmarks/index.d.mts +14 -1
  11. package/dist/coachmarks/index.d.ts +14 -1
  12. package/dist/coachmarks/index.js +58 -14
  13. package/dist/coachmarks/index.js.map +1 -1
  14. package/dist/coachmarks/index.mjs +58 -14
  15. package/dist/coachmarks/index.mjs.map +1 -1
  16. package/dist/{currentSession-_GynvhzT.d.mts → currentSession-BoWtr3Jp.d.mts} +299 -14
  17. package/dist/{currentSession-D7zabMXK.d.ts → currentSession-CCOMlaQ2.d.ts} +299 -14
  18. package/dist/{decision-Bl_M2y3r.d.mts → decision-Cjw0jbEj.d.mts} +1 -1
  19. package/dist/{decision-Cau5KmP6.d.ts → decision-GfpXAsk_.d.ts} +1 -1
  20. package/dist/index.d.mts +218 -4
  21. package/dist/index.d.ts +218 -4
  22. package/dist/index.js +1104 -888
  23. package/dist/index.js.map +1 -1
  24. package/dist/index.mjs +887 -692
  25. package/dist/index.mjs.map +1 -1
  26. package/dist/questionnaire/index.d.mts +42 -10
  27. package/dist/questionnaire/index.d.ts +42 -10
  28. package/dist/questionnaire/index.js +45 -13
  29. package/dist/questionnaire/index.js.map +1 -1
  30. package/dist/questionnaire/index.mjs +45 -13
  31. package/dist/questionnaire/index.mjs.map +1 -1
  32. package/dist/reviews/index.d.mts +4 -4
  33. package/dist/reviews/index.d.ts +4 -4
  34. package/dist/reviews/index.js +39 -8
  35. package/dist/reviews/index.js.map +1 -1
  36. package/dist/reviews/index.mjs +39 -8
  37. package/dist/reviews/index.mjs.map +1 -1
  38. package/dist/showcase/index.js +7 -2
  39. package/dist/showcase/index.js.map +1 -1
  40. package/dist/showcase/index.mjs +7 -2
  41. package/dist/showcase/index.mjs.map +1 -1
  42. package/dist/{transport-DsRe4epC.d.ts → transport-CefdERPs.d.mts} +44 -5
  43. package/dist/{transport-CF_eHwzC.d.mts → transport-DFuPyCoJ.d.ts} +44 -5
  44. package/dist/{types-Buj9Lw9t.d.ts → types-UVYt9BJe.d.ts} +41 -1
  45. package/dist/{types-CNUqMK0D.d.mts → types-l2tfg23P.d.mts} +41 -1
  46. package/package.json +4 -1
  47. package/src/OnboardingFlow.tsx +175 -15
  48. package/src/WireOnboarding.tsx +70 -8
  49. package/src/activation/wireActivation.ts +3 -3
  50. package/src/analytics/analyticsEvent.ts +16 -1
  51. package/src/analytics/analyticsFacade.ts +2 -2
  52. package/src/analytics/eventQueue.ts +85 -20
  53. package/src/analytics/reportClientEvent.ts +68 -12
  54. package/src/cards/CardGridSelectCard.tsx +1 -1
  55. package/src/cards/ChipSelectCard.tsx +2 -2
  56. package/src/cards/PermissionCard.tsx +438 -0
  57. package/src/cards/SelectionCard.tsx +1 -1
  58. package/src/cards/index.ts +7 -0
  59. package/src/coachmarks/runtime.ts +62 -2
  60. package/src/coachmarks/useCoachmarkTour.ts +62 -14
  61. package/src/context/deviceId.ts +4 -4
  62. package/src/features/WireFeaturesProvider.tsx +4 -1
  63. package/src/identity/userIdentity.ts +1 -1
  64. package/src/illustrations/defaultIllustrations.tsx +44 -3
  65. package/src/index.ts +38 -0
  66. package/src/permissions/index.ts +64 -0
  67. package/src/permissions/permissionCopy.ts +87 -0
  68. package/src/permissions/permissionEvents.ts +76 -0
  69. package/src/permissions/permissionMemory.ts +88 -0
  70. package/src/permissions/placement.ts +88 -0
  71. package/src/permissions/types.ts +131 -0
  72. package/src/questionnaire/QuestionnaireGate.tsx +90 -10
  73. package/src/questionnaire/index.ts +9 -1
  74. package/src/questionnaire/transport.ts +46 -11
  75. package/src/reviews/ReviewGate.tsx +61 -6
  76. package/src/reviews/index.ts +10 -1
  77. package/src/reviews/transport.ts +58 -12
  78. package/src/session/persistedSession.ts +10 -3
  79. package/src/session-analytics/useLifecycleEvents.ts +1 -1
  80. package/src/theme/ThemeContext.tsx +42 -1
  81. package/src/types.ts +66 -4
  82. package/src/utils/submitResult.ts +39 -0
package/dist/index.d.mts CHANGED
@@ -1,11 +1,11 @@
1
1
  import * as React from 'react';
2
2
  import React__default, { ReactNode } from 'react';
3
- import { I as WireOnboardingProps, J as WireOnboardingConfig, O as OnboardingResult, S as StepValidator, K as OnboardingEvent, L as OnboardingCopy, c as ClientEventTarget, M as DeviceContext, W as WireUserContext, b as ClientEvent, g as EnvelopeSource } from './currentSession-_GynvhzT.mjs';
4
- export { A as AUTO_DEVICE_ID_PREFIX, a as AnalyticsEvent, C as ClearUserContextOptions, d as ClientEventType, N as DeviceFormFactor, D as DeviceKeyStorage, P as EXTRA_KEY_PREFIX, Q as IdentifyOnboardingBinding, T as IdentifyOnboardingOptions, U as IdentityRecord, V as IdentitySource, X as IdentitySpace, Y as OnboardingProgress, R as ResolveAutoDeviceKeyOptions, Z as ResolveUserContextOptions, _ as ResolvedUserContext, $ as USER_ID_MAX_LENGTH, i as WIRE_ONBOARDING_EVENTS, j as WireOnboardingEventName, a0 as activationJoinContext, k as analyticsUserIdStorageKey, m as clearPiiFromContext, n as clearUserContext, a1 as collectDeviceContext, p as deviceIdStorageKey, q as ensureCurrentSessionId, r as getCurrentSessionId, a2 as hashEmailFnv1a, a3 as hostIdentity, a4 as hydrateAutoDeviceKey, a5 as hydrateDeviceIdentity, a6 as identifyOnboarding, a7 as isWireScalar, s as looksLikeEmail, t as makeSessionId, a8 as mintDeviceId, a9 as namespaceExtra, u as reportClientEvent, v as reportClientEventAwait, w as reportClientEvents, x as reportClientEventsAwait, y as resetAutoDeviceKeys, z as resetCurrentSessionId, aa as resetIdentityProvenance, F as resolveAutoDeviceKey, ab as resolveIdentity, ac as resolveUserContext, ad as sanitizeUserId, G as setCurrentSessionId, H as toAnalyticsEvent } from './currentSession-_GynvhzT.mjs';
3
+ import { I as WireOnboardingProps, J as WireOnboardingConfig, O as OnboardingResult, K as WirePermissionStatus, P as PermissionStage, L as WirePermissionOutcome, S as StepValidator, M as OnboardingEvent, N as OnboardingCopy, c as ClientEventTarget, Q as DeviceContext, T as PermissionScreenConfig, W as WireUserContext, U as PermissionPlacement, b as ClientEvent, g as EnvelopeSource } from './currentSession-BoWtr3Jp.mjs';
4
+ export { A as AUTO_DEVICE_ID_PREFIX, a as AnalyticsEvent, C as ClearUserContextOptions, d as ClientEventType, V as DEFAULT_PERMISSION_COPY, X as DeviceFormFactor, D as DeviceKeyStorage, Y as EXTRA_KEY_PREFIX, Z as GENERIC_PERMISSION_COPY, _ as IdentifyOnboardingBinding, $ as IdentifyOnboardingOptions, a0 as IdentityRecord, a1 as IdentitySource, a2 as IdentitySpace, a3 as NOTIFICATIONS_PERMISSION_COPY, a4 as OnboardingProgress, a5 as PermissionScreenCopy, R as ResolveAutoDeviceKeyOptions, a6 as ResolveUserContextOptions, a7 as ResolvedUserContext, a8 as USER_ID_MAX_LENGTH, i as WIRE_ONBOARDING_EVENTS, a9 as WIRE_PERMISSION_EVENTS, j as WireOnboardingEventName, aa as WirePermissionEventName, ab as WirePermissionKind, ac as activationJoinContext, k as analyticsUserIdStorageKey, m as clearPiiFromContext, n as clearUserContext, ad as collectDeviceContext, p as deviceIdStorageKey, q as ensureCurrentSessionId, r as getCurrentSessionId, ae as hashEmailFnv1a, af as hostIdentity, ag as hydrateAutoDeviceKey, ah as hydrateDeviceIdentity, ai as identifyOnboarding, aj as isWireScalar, s as looksLikeEmail, t as makeSessionId, ak as mintDeviceId, al as namespaceExtra, am as normalizePermissionStatus, an as permissionEventName, ao as permissionEventProps, u as reportClientEvent, v as reportClientEventAwait, w as reportClientEvents, x as reportClientEventsAwait, y as resetAutoDeviceKeys, z as resetCurrentSessionId, ap as resetIdentityProvenance, F as resolveAutoDeviceKey, aq as resolveIdentity, ar as resolvePermissionCopy, as as resolveUserContext, at as sanitizeUserId, G as setCurrentSessionId, H as toAnalyticsEvent } from './currentSession-BoWtr3Jp.mjs';
5
5
  import { O as OnboardingTheme } from './types-BKfpdZzX.mjs';
6
6
  export { a as OnboardingButtonStyle, b as OnboardingColors, c as OnboardingFonts, d as OnboardingRadius, e as OnboardingSpacing } from './types-BKfpdZzX.mjs';
7
7
  export { C as CenteredModal, a as CenteredModalHandle, b as CenteredModalProps } from './CenteredModal-C3qQBHsA.mjs';
8
- import { WireAIComponent, Message, WireAIResponse } from 'wireai-rn';
8
+ import { WireAIComponent, InjectedProps, Message, WireAIResponse } from 'wireai-rn';
9
9
  import { a as WireFeaturesConfig, W as WireFeatures, b as WireOnboardingStorage } from './types-CKFhyrMu.mjs';
10
10
  export { D as DEFAULT_SESSION_TTL_MS, L as LoadedSession, O as OnboardingMode, c as clearPersistedSession, l as loadPersistedSession, p as peekPersistedSession, s as savePersistedSession, d as sessionStorageKey } from './types-CKFhyrMu.mjs';
11
11
  import { z } from 'zod';
@@ -48,6 +48,29 @@ declare function DemoOnboarding({ config, theme, illustrations, validators, copy
48
48
  * themed card via `useOnboardingTheme()`. WHY a context rather than prop-drilling:
49
49
  * the cards are mounted by the SDK's ComponentRenderer (not by us), so we can't
50
50
  * pass them props directly — they must read the theme from context.
51
+ *
52
+ * ── WHY THE CONTEXT LIVES ON A `Symbol.for` SLOT ───────────────────────────────────────────────
53
+ *
54
+ * It was a plain `createContext(...)`, and tsup inlines this module into FIVE dist bundles
55
+ * (`.`, `./reviews`, `./questionnaire`, `./coachmarks`, `./showcase`) — so each one carried its own
56
+ * context instance. `OnboardingThemeProvider` is exported ONLY from the root barrel, so on any
57
+ * `dist` resolution path (plain RN CLI, `unstable_enablePackageExports: false`, node, jest, SSR) a
58
+ * host that wrapped its tree in `<OnboardingThemeProvider>` and rendered `<ReviewGate>` from
59
+ * `@wireai/activation/reviews` was writing one context and reading another. Nothing crashed and
60
+ * nothing warned: `useOnboardingTheme`'s `ctx ?? mergeTheme()` fallback quietly served the NEUTRAL
61
+ * default theme, so the tenant's brand simply did not apply to the gate. On-device this was masked
62
+ * only because Metro's `react-native` export condition collapses every subpath back to this one
63
+ * `src/` file — the same "bundler accident, not a guarantee" the `currentSession` header names, and
64
+ * the same root cause as the optional-peers bug 0.13.3 fixed.
65
+ *
66
+ * The bundler-agnostic fix, exactly as `coachmarks/runtime`, `analytics/currentSession`,
67
+ * `context/deviceId` and `features/WireFeaturesProvider` already do it: park the ONE context on a
68
+ * well-known `globalThis` slot keyed by `Symbol.for(...)`. `Symbol.for` uses the runtime-global
69
+ * symbol registry, so every inlined copy of this module resolves the SAME symbol, finds the slot
70
+ * already filled, and reuses that instance — one context identity however many copies exist.
71
+ * `globalThis` is present and identical in Hermes/React Native, Node and SSR (we never touch
72
+ * `window`), so this is safe on every host. `test/canary/themeContextIdentity.test.tsx` measures it
73
+ * in the BUILT artifact, because the defect is invisible in source.
51
74
  */
52
75
 
53
76
  type OnboardingThemeProviderProps = {
@@ -550,6 +573,96 @@ declare const NumberStepperCard: WireAIComponent;
550
573
 
551
574
  declare const InterstitialCard: WireAIComponent;
552
575
 
576
+ /**
577
+ * PermissionCard - the priming screen that stands in front of an OS permission dialog.
578
+ *
579
+ * THE PRIMING PATTERN, which is the entire point: the OS dialog opens from the primary press
580
+ * handler and from nowhere else. There is no mount effect, no auto-fire, no timer, and no code path
581
+ * from render to `request()`. iOS grants an app exactly ONE native notification prompt for its
582
+ * whole lifetime, so this cheap in-app screen spends first and only forwards the users who said
583
+ * yes. The secondary ("Maybe later") advances the flow with the prompt still unspent.
584
+ *
585
+ * THREE PRIMARY ACTIONS, picked from the non-prompting `getStatus` probe (never from `request`):
586
+ * • ask (the default, and the only branch that can reach `request`)
587
+ * • settings (status `blocked`: the OS would show nothing, so the only route left is Settings)
588
+ * • continue (already granted, or a host that supplied no `request` at all - the kit declines to
589
+ * fabricate a prompt it has no way to open)
590
+ *
591
+ * IT IS NOT A QUESTION. It sends nothing to the backend, appends nothing to the thread, and mints
592
+ * no `key` / `slot_id`, so `deriveAnswers` and every completion semantic are untouched. Every
593
+ * outcome, including a denial, continues the flow.
594
+ *
595
+ * Motion: the same register as the other value beats. The illustration springs in
596
+ * (STATUS_POP_SPRING, the StatusCard glyph pop) and the copy rises behind it on the interstitial
597
+ * head stagger. Reduce motion: final frame at once, like every other card.
598
+ *
599
+ * Registered as a `WireAIComponent` so a later server-emitted placement (AI-chosen timing) renders
600
+ * through the same component with no rewrite. It is deliberately NOT in `onboardingComponents`:
601
+ * that array is what the device ADVERTISES as renderable, and a backend told it may emit this card
602
+ * could emit one for a host that wired no `request`.
603
+ */
604
+
605
+ /** The name the card is registered under, and the `component` stamped on its events. */
606
+ declare const PERMISSION_CARD_NAME = "PermissionCard";
607
+ /**
608
+ * The COPY half of the card, i.e. everything a server could legitimately author. The capability
609
+ * half (`request` / `getStatus` / `openSettings`) is host-injected and deliberately absent here:
610
+ * a schema field can only ever carry data, never a function, and the kit imports no native module.
611
+ */
612
+ declare const schema: z.ZodObject<{
613
+ permission: z.ZodString;
614
+ title: z.ZodString;
615
+ message: z.ZodString;
616
+ primaryLabel: z.ZodString;
617
+ secondaryLabel: z.ZodString;
618
+ blockedTitle: z.ZodOptional<z.ZodString>;
619
+ blockedMessage: z.ZodOptional<z.ZodString>;
620
+ settingsLabel: z.ZodOptional<z.ZodString>;
621
+ continueLabel: z.ZodOptional<z.ZodString>;
622
+ illustration: z.ZodOptional<z.ZodString>;
623
+ }, "strip", z.ZodTypeAny, {
624
+ title: string;
625
+ message: string;
626
+ primaryLabel: string;
627
+ secondaryLabel: string;
628
+ permission: string;
629
+ blockedTitle?: string | undefined;
630
+ blockedMessage?: string | undefined;
631
+ settingsLabel?: string | undefined;
632
+ continueLabel?: string | undefined;
633
+ illustration?: string | undefined;
634
+ }, {
635
+ title: string;
636
+ message: string;
637
+ primaryLabel: string;
638
+ secondaryLabel: string;
639
+ permission: string;
640
+ blockedTitle?: string | undefined;
641
+ blockedMessage?: string | undefined;
642
+ settingsLabel?: string | undefined;
643
+ continueLabel?: string | undefined;
644
+ illustration?: string | undefined;
645
+ }>;
646
+ type PermissionCardProps = z.infer<typeof schema> & Partial<InjectedProps> & {
647
+ /** THE ONLY function that can open an OS dialog. Called from the primary handler alone. */
648
+ request?: () => Promise<WirePermissionStatus>;
649
+ /** NON-PROMPTING status read. Decides which primary action is offered, nothing else. */
650
+ getStatus?: () => Promise<WirePermissionStatus>;
651
+ /** Open the OS settings page. Only reachable on the `blocked` route. */
652
+ openSettings?: () => void | Promise<void>;
653
+ /** Reports each moment for analytics. Never control flow. */
654
+ onStage?: (stage: PermissionStage, status?: WirePermissionStatus) => void;
655
+ /** Fires exactly once, with the outcome this screen produced. The flow advances on it. */
656
+ onSettled?: (outcome: WirePermissionOutcome) => void;
657
+ };
658
+ /** The typed component the flow renders directly (host-injected placement). */
659
+ declare const PermissionCardView: React__default.NamedExoticComponent<PermissionCardProps>;
660
+ /**
661
+ * The SDK registration object, so a server-emitted placement can adopt this exact screen later.
662
+ * Not part of `onboardingComponents` on purpose - see the file header.
663
+ */
664
+ declare const PermissionCard: WireAIComponent;
665
+
553
666
  /**
554
667
  * The shared option shape for the choice cards (SelectionCard, CardGridSelectCard).
555
668
  *
@@ -1120,6 +1233,25 @@ type OnboardingFlowProps = {
1120
1233
  * `started` so host funnels don't double-count the session.
1121
1234
  */
1122
1235
  resumed?: boolean;
1236
+ /**
1237
+ * Host-declared permission screens injected into this server-driven stream. See
1238
+ * `permissions/types.ts`: the OS dialog is only ever reached from the primary tap.
1239
+ */
1240
+ permissionScreens?: PermissionScreenConfig[];
1241
+ /**
1242
+ * Screen ids already settled in THIS session, restored from `storage` by WireOnboarding so an
1243
+ * app kill mid-flow does not re-ask. Empty (the default) means "nothing settled yet".
1244
+ */
1245
+ settledPermissions?: readonly string[];
1246
+ /**
1247
+ * True while that restore is still in flight. Permission screens are suppressed until it lands,
1248
+ * because showing one before the record is read is exactly the re-ask the record prevents. The
1249
+ * read is short (one storage get, timeout-capped) and cannot outlive the first card's round trip
1250
+ * in practice, so this normally never shows up as a delay.
1251
+ */
1252
+ permissionsPending?: boolean;
1253
+ /** Persist one settled screen id (fire-and-forget; WireOnboarding owns the storage). */
1254
+ onPermissionSettled?: (id: string, outcome: WirePermissionOutcome) => void;
1123
1255
  };
1124
1256
  declare const OnboardingFlow: React__default.FC<OnboardingFlowProps>;
1125
1257
 
@@ -1755,6 +1887,88 @@ interface RevenueCatBridge {
1755
1887
  */
1756
1888
  declare const createRevenueCatBridge: (config: RevenueCatBridgeConfig) => RevenueCatBridge;
1757
1889
 
1890
+ /**
1891
+ * placement - WHERE an injected permission screen lands in a stream nobody knows the length of.
1892
+ *
1893
+ * The card stream is server-driven: the backend decides how many questions this user gets, and the
1894
+ * count differs per user and per run. So a placement is never an index into a known list; it is a
1895
+ * predicate evaluated against the card the flow is ABOUT to render, on every turn.
1896
+ *
1897
+ * THE CLAMP is the whole reason this is a function and not a comparison. `{ afterCard: 4 }` against
1898
+ * a stream that ended after 3 would simply never fire, and "the screen silently never showed" is
1899
+ * the exact failure a host cannot see. So an unreached target degrades to `"beforeEnd"`: the
1900
+ * terminal card is the last position where the screen can still exist, so that is where it goes.
1901
+ *
1902
+ * PURE, no React, no React Native - the placement math is the part worth unit-testing.
1903
+ */
1904
+
1905
+ /** Where a screen sits when the host does not say. High intent, and it can never be clamped away. */
1906
+ declare const DEFAULT_PERMISSION_PLACEMENT: PermissionPlacement;
1907
+ /** The flow position a placement is resolved against. */
1908
+ type FlowPosition = {
1909
+ /** 0-based index of the card about to render. `-1` when no card has arrived yet. */
1910
+ cardIndex: number;
1911
+ /** Whether that card is the terminal recap, i.e. the flow's last screen. */
1912
+ isTerminal: boolean;
1913
+ };
1914
+ /** `afterCard` from a host is arbitrary input: floor it, floor it at 0, and never let NaN through. */
1915
+ declare const normalizeAfterCard: (value: number) => number;
1916
+ /**
1917
+ * Is a screen with this placement due at this position?
1918
+ *
1919
+ * `>=` rather than `===` on purpose: due-ness is re-evaluated every render and a screen is settled
1920
+ * the moment it is answered, so a target the flow has already passed (because a screen ahead of it
1921
+ * in the array was on screen for those turns) still fires instead of being skipped forever.
1922
+ */
1923
+ declare const isPermissionDue: (placement: PermissionPlacement, position: FlowPosition) => boolean;
1924
+ /**
1925
+ * The stable id a screen is remembered by across a resume. Defaults to `<permission>:<index>`,
1926
+ * which is right until the array is reordered - hence the documented `id` escape hatch.
1927
+ */
1928
+ declare const permissionScreenId: (screen: PermissionScreenConfig, index: number) => string;
1929
+ /** A due screen and the id it is remembered by. */
1930
+ type DuePermissionScreen = {
1931
+ screen: PermissionScreenConfig;
1932
+ id: string;
1933
+ };
1934
+ /**
1935
+ * The FIRST configured screen that is due here and has not already been settled in this session.
1936
+ * One at a time, in array order, so two screens due at the same position queue rather than collide.
1937
+ */
1938
+ declare const selectDuePermissionScreen: (screens: readonly PermissionScreenConfig[] | undefined, settled: readonly string[], position: FlowPosition) => DuePermissionScreen | undefined;
1939
+
1940
+ /**
1941
+ * permissionMemory - "shown exactly once per session", kept true across an app kill.
1942
+ *
1943
+ * The in-memory guard alone is not enough. The kit already resumes a killed onboarding into the
1944
+ * SAME backend session (see `persistedSession.ts`), so without a persisted record the resumed mount
1945
+ * would re-show a permission screen the user had already answered - and on the ask path that means
1946
+ * a second dialog attempt against an OS that grants exactly one.
1947
+ *
1948
+ * The record is SCOPED TO THE SESSION ID, one entry, never a growing map: a record whose
1949
+ * `sessionId` is not the session being resumed is stale by definition and reads as empty. That is
1950
+ * also what makes a genuinely new onboarding start clean without anything having to expire it.
1951
+ *
1952
+ * Same host-injected `WireOnboardingStorage` and the same best-effort discipline as the session
1953
+ * seed: reads race the shared ceiling and degrade to "nothing remembered", writes swallow every
1954
+ * error. A broken storage adapter must never gate or break onboarding.
1955
+ */
1956
+
1957
+ /** Storage key for an app's settled permission screens, e.g. `wireai:permissions:acme`. */
1958
+ declare const permissionStorageKey: (appId: string) => string;
1959
+ /**
1960
+ * PURE seam: which screen ids a stored blob contributes to THIS session. A corrupt entry, a
1961
+ * malformed shape, or a record belonging to a different session all read as "nothing remembered",
1962
+ * because re-showing a screen is a far cheaper failure than trusting another session's answers.
1963
+ */
1964
+ declare const readSettledPermissions: (raw: string | null | undefined, sessionId: string) => string[];
1965
+ /** Read the settled ids for `sessionId`. Never throws, never hangs past the shared read ceiling. */
1966
+ declare const loadSettledPermissions: (storage: WireOnboardingStorage, key: string, sessionId: string) => Promise<string[]>;
1967
+ /** Persist the settled ids for `sessionId` - fire-and-forget, all errors swallowed. */
1968
+ declare const saveSettledPermissions: (storage: WireOnboardingStorage, key: string, sessionId: string, ids: readonly string[]) => void;
1969
+ /** Drop the record (fired alongside the session seed on completion) - fire-and-forget. */
1970
+ declare const clearSettledPermissions: (storage: WireOnboardingStorage, key: string) => void;
1971
+
1758
1972
  /** The canonical event name for an app-open. A trigger keys off this exact string. */
1759
1973
  declare const SESSION_STARTED_EVENT: "app.session_started";
1760
1974
  /** Options for {@link reportSessionStart}. Everything except `target` is optional so a pre-auth
@@ -1986,4 +2200,4 @@ interface UseLifecycleEventsOptions {
1986
2200
  */
1987
2201
  declare const useLifecycleEvents: (config: LifecycleConfig | undefined, options?: UseLifecycleEventsOptions) => void;
1988
2202
 
1989
- export { AnimatedSparkle, BACKGROUND_SESSION_MS, type CachedFeatures, CardGridSelectCard, CardHandoff, type CardHandoffProps, type CardHandoffVariant, type CardOption, ChipSelectCard, ClientEvent, ClientEventTarget, CompletionView, DEFAULT_FEATURES_TTL_MS, DemoOnboarding, type DemoOnboardingProps, DeviceContext, ErrorBlock, FIRST_OPEN_EVENT, IconRegistryProvider, IllustrationProvider, type IllustrationRegistry, InterstitialCard, type LifecycleConfig, type LifecycleEventInput, LoadingBlock, LoadingScreen, NumberStepperCard, type OnboardingAttribution, Button as OnboardingButton, OnboardingCopy, OnboardingEvent, type OnboardingFlagOptions, OnboardingFlow, OnboardingResult, OnboardingScaffold, OnboardingTheme, OnboardingThemeProvider, PLAN_TIER_CONTEXT_KEY, type PlanTier, type PurchaseProps, type ReportFirstOpenOptions, type ReportSessionStartOptions, type ResolveFeaturesOptions, type RevenueCatBridge, type RevenueCatBridgeConfig, type RevenueCatCustomerInfoLike, type RevenueCatEntitlementLike, type RevenueCatErrorLike, type RevenueCatOfferingLike, type RevenueCatPackageLike, type RevenueCatProductLike, type RevenueCatSink, SESSION_STARTED_EVENT, SelectionCard, type SessionStartConfig, StatusCard, StepProgress, StepValidator, TextInputCard, type ThemeFromBrandInput, type UseLifecycleEventsOptions, type UseSessionStartOptions, type UseWireActivation, WIRE_ENV_VARS, WIRE_ICON_GLYPHS, WIRE_ICON_NAMES, WIRE_PURCHASE_EVENTS, type WireActivation, type WireActivationConfig, type WireConfigOverrides, WireFeatures, WireFeaturesConfig, WireFeaturesProvider, type WireFeaturesProviderProps, WireIcon, type WireIconFamily, type WireIconGlyph, type WireIconName, type WireIconProps, type WireIconRegistry, type WireLifecycleOptions, WireOnboarding, WireOnboardingConfig, WireOnboardingProps, WireOnboardingStorage, type WirePurchaseEventName, WireUserContext, activeEntitlement, attributionMetadata, bumpActivationRevalidation, createRevenueCatBridge, createWireActivation, defaultIllustrations, defaultOnboardingTheme, defaultWireFeatures, deriveAnswers, describeEntitlement, describeFailure, describePackage, detectAppVersion, detectNativeModel, featuresCacheKey, featuresEqual, fetchWireFeatures, firstOpenStorageKey, getActivationRevalidationVersion, isFeaturesFresh, isOnboardingEnabled, isUserCancelled, lookupIconGlyph, mergeTheme, motionSpec, onboardingComponents, parseWireFeatures, readCachedFeatures, readProgress, reportFirstOpen, reportSessionStart, resetActivationRevalidation, resetFirstOpenLatch, resetSessionStartGuard, resolvePlanTier, subscribeActivationRevalidation, themeFromBrand, useActivationRevalidation, useHostIcon, useIllustration, useLifecycleEvents, useOnboardingTheme, useReducedMotion, useResolvedFeatures, useSessionStart, useWireActivation, useWireFeatures, useWireFeaturesContext, wireConfigFromEnv, wireLifecycleEvents, writeCachedFeatures };
2203
+ export { AnimatedSparkle, BACKGROUND_SESSION_MS, type CachedFeatures, CardGridSelectCard, CardHandoff, type CardHandoffProps, type CardHandoffVariant, type CardOption, ChipSelectCard, ClientEvent, ClientEventTarget, CompletionView, DEFAULT_FEATURES_TTL_MS, DEFAULT_PERMISSION_PLACEMENT, DemoOnboarding, type DemoOnboardingProps, DeviceContext, type DuePermissionScreen, ErrorBlock, FIRST_OPEN_EVENT, type FlowPosition, IconRegistryProvider, IllustrationProvider, type IllustrationRegistry, InterstitialCard, type LifecycleConfig, type LifecycleEventInput, LoadingBlock, LoadingScreen, NumberStepperCard, type OnboardingAttribution, Button as OnboardingButton, OnboardingCopy, OnboardingEvent, type OnboardingFlagOptions, OnboardingFlow, OnboardingResult, OnboardingScaffold, OnboardingTheme, OnboardingThemeProvider, PERMISSION_CARD_NAME, PLAN_TIER_CONTEXT_KEY, PermissionCard, type PermissionCardProps, PermissionCardView, PermissionPlacement, PermissionScreenConfig, PermissionStage, type PlanTier, type PurchaseProps, type ReportFirstOpenOptions, type ReportSessionStartOptions, type ResolveFeaturesOptions, type RevenueCatBridge, type RevenueCatBridgeConfig, type RevenueCatCustomerInfoLike, type RevenueCatEntitlementLike, type RevenueCatErrorLike, type RevenueCatOfferingLike, type RevenueCatPackageLike, type RevenueCatProductLike, type RevenueCatSink, SESSION_STARTED_EVENT, SelectionCard, type SessionStartConfig, StatusCard, StepProgress, StepValidator, TextInputCard, type ThemeFromBrandInput, type UseLifecycleEventsOptions, type UseSessionStartOptions, type UseWireActivation, WIRE_ENV_VARS, WIRE_ICON_GLYPHS, WIRE_ICON_NAMES, WIRE_PURCHASE_EVENTS, type WireActivation, type WireActivationConfig, type WireConfigOverrides, WireFeatures, WireFeaturesConfig, WireFeaturesProvider, type WireFeaturesProviderProps, WireIcon, type WireIconFamily, type WireIconGlyph, type WireIconName, type WireIconProps, type WireIconRegistry, type WireLifecycleOptions, WireOnboarding, WireOnboardingConfig, WireOnboardingProps, WireOnboardingStorage, WirePermissionOutcome, WirePermissionStatus, type WirePurchaseEventName, WireUserContext, activeEntitlement, attributionMetadata, bumpActivationRevalidation, clearSettledPermissions, createRevenueCatBridge, createWireActivation, defaultIllustrations, defaultOnboardingTheme, defaultWireFeatures, deriveAnswers, describeEntitlement, describeFailure, describePackage, detectAppVersion, detectNativeModel, featuresCacheKey, featuresEqual, fetchWireFeatures, firstOpenStorageKey, getActivationRevalidationVersion, isFeaturesFresh, isOnboardingEnabled, isPermissionDue, isUserCancelled, loadSettledPermissions, lookupIconGlyph, mergeTheme, motionSpec, normalizeAfterCard, onboardingComponents, parseWireFeatures, permissionScreenId, permissionStorageKey, readCachedFeatures, readProgress, readSettledPermissions, reportFirstOpen, reportSessionStart, resetActivationRevalidation, resetFirstOpenLatch, resetSessionStartGuard, resolvePlanTier, saveSettledPermissions, selectDuePermissionScreen, subscribeActivationRevalidation, themeFromBrand, useActivationRevalidation, useHostIcon, useIllustration, useLifecycleEvents, useOnboardingTheme, useReducedMotion, useResolvedFeatures, useSessionStart, useWireActivation, useWireFeatures, useWireFeaturesContext, wireConfigFromEnv, wireLifecycleEvents, writeCachedFeatures };
package/dist/index.d.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  import * as React from 'react';
2
2
  import React__default, { ReactNode } from 'react';
3
- import { I as WireOnboardingProps, J as WireOnboardingConfig, O as OnboardingResult, S as StepValidator, K as OnboardingEvent, L as OnboardingCopy, c as ClientEventTarget, M as DeviceContext, W as WireUserContext, b as ClientEvent, g as EnvelopeSource } from './currentSession-D7zabMXK.js';
4
- export { A as AUTO_DEVICE_ID_PREFIX, a as AnalyticsEvent, C as ClearUserContextOptions, d as ClientEventType, N as DeviceFormFactor, D as DeviceKeyStorage, P as EXTRA_KEY_PREFIX, Q as IdentifyOnboardingBinding, T as IdentifyOnboardingOptions, U as IdentityRecord, V as IdentitySource, X as IdentitySpace, Y as OnboardingProgress, R as ResolveAutoDeviceKeyOptions, Z as ResolveUserContextOptions, _ as ResolvedUserContext, $ as USER_ID_MAX_LENGTH, i as WIRE_ONBOARDING_EVENTS, j as WireOnboardingEventName, a0 as activationJoinContext, k as analyticsUserIdStorageKey, m as clearPiiFromContext, n as clearUserContext, a1 as collectDeviceContext, p as deviceIdStorageKey, q as ensureCurrentSessionId, r as getCurrentSessionId, a2 as hashEmailFnv1a, a3 as hostIdentity, a4 as hydrateAutoDeviceKey, a5 as hydrateDeviceIdentity, a6 as identifyOnboarding, a7 as isWireScalar, s as looksLikeEmail, t as makeSessionId, a8 as mintDeviceId, a9 as namespaceExtra, u as reportClientEvent, v as reportClientEventAwait, w as reportClientEvents, x as reportClientEventsAwait, y as resetAutoDeviceKeys, z as resetCurrentSessionId, aa as resetIdentityProvenance, F as resolveAutoDeviceKey, ab as resolveIdentity, ac as resolveUserContext, ad as sanitizeUserId, G as setCurrentSessionId, H as toAnalyticsEvent } from './currentSession-D7zabMXK.js';
3
+ import { I as WireOnboardingProps, J as WireOnboardingConfig, O as OnboardingResult, K as WirePermissionStatus, P as PermissionStage, L as WirePermissionOutcome, S as StepValidator, M as OnboardingEvent, N as OnboardingCopy, c as ClientEventTarget, Q as DeviceContext, T as PermissionScreenConfig, W as WireUserContext, U as PermissionPlacement, b as ClientEvent, g as EnvelopeSource } from './currentSession-CCOMlaQ2.js';
4
+ export { A as AUTO_DEVICE_ID_PREFIX, a as AnalyticsEvent, C as ClearUserContextOptions, d as ClientEventType, V as DEFAULT_PERMISSION_COPY, X as DeviceFormFactor, D as DeviceKeyStorage, Y as EXTRA_KEY_PREFIX, Z as GENERIC_PERMISSION_COPY, _ as IdentifyOnboardingBinding, $ as IdentifyOnboardingOptions, a0 as IdentityRecord, a1 as IdentitySource, a2 as IdentitySpace, a3 as NOTIFICATIONS_PERMISSION_COPY, a4 as OnboardingProgress, a5 as PermissionScreenCopy, R as ResolveAutoDeviceKeyOptions, a6 as ResolveUserContextOptions, a7 as ResolvedUserContext, a8 as USER_ID_MAX_LENGTH, i as WIRE_ONBOARDING_EVENTS, a9 as WIRE_PERMISSION_EVENTS, j as WireOnboardingEventName, aa as WirePermissionEventName, ab as WirePermissionKind, ac as activationJoinContext, k as analyticsUserIdStorageKey, m as clearPiiFromContext, n as clearUserContext, ad as collectDeviceContext, p as deviceIdStorageKey, q as ensureCurrentSessionId, r as getCurrentSessionId, ae as hashEmailFnv1a, af as hostIdentity, ag as hydrateAutoDeviceKey, ah as hydrateDeviceIdentity, ai as identifyOnboarding, aj as isWireScalar, s as looksLikeEmail, t as makeSessionId, ak as mintDeviceId, al as namespaceExtra, am as normalizePermissionStatus, an as permissionEventName, ao as permissionEventProps, u as reportClientEvent, v as reportClientEventAwait, w as reportClientEvents, x as reportClientEventsAwait, y as resetAutoDeviceKeys, z as resetCurrentSessionId, ap as resetIdentityProvenance, F as resolveAutoDeviceKey, aq as resolveIdentity, ar as resolvePermissionCopy, as as resolveUserContext, at as sanitizeUserId, G as setCurrentSessionId, H as toAnalyticsEvent } from './currentSession-CCOMlaQ2.js';
5
5
  import { O as OnboardingTheme } from './types-BKfpdZzX.js';
6
6
  export { a as OnboardingButtonStyle, b as OnboardingColors, c as OnboardingFonts, d as OnboardingRadius, e as OnboardingSpacing } from './types-BKfpdZzX.js';
7
7
  export { C as CenteredModal, a as CenteredModalHandle, b as CenteredModalProps } from './CenteredModal-Cdgns6--.js';
8
- import { WireAIComponent, Message, WireAIResponse } from 'wireai-rn';
8
+ import { WireAIComponent, InjectedProps, Message, WireAIResponse } from 'wireai-rn';
9
9
  import { a as WireFeaturesConfig, W as WireFeatures, b as WireOnboardingStorage } from './types-CKFhyrMu.js';
10
10
  export { D as DEFAULT_SESSION_TTL_MS, L as LoadedSession, O as OnboardingMode, c as clearPersistedSession, l as loadPersistedSession, p as peekPersistedSession, s as savePersistedSession, d as sessionStorageKey } from './types-CKFhyrMu.js';
11
11
  import { z } from 'zod';
@@ -48,6 +48,29 @@ declare function DemoOnboarding({ config, theme, illustrations, validators, copy
48
48
  * themed card via `useOnboardingTheme()`. WHY a context rather than prop-drilling:
49
49
  * the cards are mounted by the SDK's ComponentRenderer (not by us), so we can't
50
50
  * pass them props directly — they must read the theme from context.
51
+ *
52
+ * ── WHY THE CONTEXT LIVES ON A `Symbol.for` SLOT ───────────────────────────────────────────────
53
+ *
54
+ * It was a plain `createContext(...)`, and tsup inlines this module into FIVE dist bundles
55
+ * (`.`, `./reviews`, `./questionnaire`, `./coachmarks`, `./showcase`) — so each one carried its own
56
+ * context instance. `OnboardingThemeProvider` is exported ONLY from the root barrel, so on any
57
+ * `dist` resolution path (plain RN CLI, `unstable_enablePackageExports: false`, node, jest, SSR) a
58
+ * host that wrapped its tree in `<OnboardingThemeProvider>` and rendered `<ReviewGate>` from
59
+ * `@wireai/activation/reviews` was writing one context and reading another. Nothing crashed and
60
+ * nothing warned: `useOnboardingTheme`'s `ctx ?? mergeTheme()` fallback quietly served the NEUTRAL
61
+ * default theme, so the tenant's brand simply did not apply to the gate. On-device this was masked
62
+ * only because Metro's `react-native` export condition collapses every subpath back to this one
63
+ * `src/` file — the same "bundler accident, not a guarantee" the `currentSession` header names, and
64
+ * the same root cause as the optional-peers bug 0.13.3 fixed.
65
+ *
66
+ * The bundler-agnostic fix, exactly as `coachmarks/runtime`, `analytics/currentSession`,
67
+ * `context/deviceId` and `features/WireFeaturesProvider` already do it: park the ONE context on a
68
+ * well-known `globalThis` slot keyed by `Symbol.for(...)`. `Symbol.for` uses the runtime-global
69
+ * symbol registry, so every inlined copy of this module resolves the SAME symbol, finds the slot
70
+ * already filled, and reuses that instance — one context identity however many copies exist.
71
+ * `globalThis` is present and identical in Hermes/React Native, Node and SSR (we never touch
72
+ * `window`), so this is safe on every host. `test/canary/themeContextIdentity.test.tsx` measures it
73
+ * in the BUILT artifact, because the defect is invisible in source.
51
74
  */
52
75
 
53
76
  type OnboardingThemeProviderProps = {
@@ -550,6 +573,96 @@ declare const NumberStepperCard: WireAIComponent;
550
573
 
551
574
  declare const InterstitialCard: WireAIComponent;
552
575
 
576
+ /**
577
+ * PermissionCard - the priming screen that stands in front of an OS permission dialog.
578
+ *
579
+ * THE PRIMING PATTERN, which is the entire point: the OS dialog opens from the primary press
580
+ * handler and from nowhere else. There is no mount effect, no auto-fire, no timer, and no code path
581
+ * from render to `request()`. iOS grants an app exactly ONE native notification prompt for its
582
+ * whole lifetime, so this cheap in-app screen spends first and only forwards the users who said
583
+ * yes. The secondary ("Maybe later") advances the flow with the prompt still unspent.
584
+ *
585
+ * THREE PRIMARY ACTIONS, picked from the non-prompting `getStatus` probe (never from `request`):
586
+ * • ask (the default, and the only branch that can reach `request`)
587
+ * • settings (status `blocked`: the OS would show nothing, so the only route left is Settings)
588
+ * • continue (already granted, or a host that supplied no `request` at all - the kit declines to
589
+ * fabricate a prompt it has no way to open)
590
+ *
591
+ * IT IS NOT A QUESTION. It sends nothing to the backend, appends nothing to the thread, and mints
592
+ * no `key` / `slot_id`, so `deriveAnswers` and every completion semantic are untouched. Every
593
+ * outcome, including a denial, continues the flow.
594
+ *
595
+ * Motion: the same register as the other value beats. The illustration springs in
596
+ * (STATUS_POP_SPRING, the StatusCard glyph pop) and the copy rises behind it on the interstitial
597
+ * head stagger. Reduce motion: final frame at once, like every other card.
598
+ *
599
+ * Registered as a `WireAIComponent` so a later server-emitted placement (AI-chosen timing) renders
600
+ * through the same component with no rewrite. It is deliberately NOT in `onboardingComponents`:
601
+ * that array is what the device ADVERTISES as renderable, and a backend told it may emit this card
602
+ * could emit one for a host that wired no `request`.
603
+ */
604
+
605
+ /** The name the card is registered under, and the `component` stamped on its events. */
606
+ declare const PERMISSION_CARD_NAME = "PermissionCard";
607
+ /**
608
+ * The COPY half of the card, i.e. everything a server could legitimately author. The capability
609
+ * half (`request` / `getStatus` / `openSettings`) is host-injected and deliberately absent here:
610
+ * a schema field can only ever carry data, never a function, and the kit imports no native module.
611
+ */
612
+ declare const schema: z.ZodObject<{
613
+ permission: z.ZodString;
614
+ title: z.ZodString;
615
+ message: z.ZodString;
616
+ primaryLabel: z.ZodString;
617
+ secondaryLabel: z.ZodString;
618
+ blockedTitle: z.ZodOptional<z.ZodString>;
619
+ blockedMessage: z.ZodOptional<z.ZodString>;
620
+ settingsLabel: z.ZodOptional<z.ZodString>;
621
+ continueLabel: z.ZodOptional<z.ZodString>;
622
+ illustration: z.ZodOptional<z.ZodString>;
623
+ }, "strip", z.ZodTypeAny, {
624
+ title: string;
625
+ message: string;
626
+ primaryLabel: string;
627
+ secondaryLabel: string;
628
+ permission: string;
629
+ blockedTitle?: string | undefined;
630
+ blockedMessage?: string | undefined;
631
+ settingsLabel?: string | undefined;
632
+ continueLabel?: string | undefined;
633
+ illustration?: string | undefined;
634
+ }, {
635
+ title: string;
636
+ message: string;
637
+ primaryLabel: string;
638
+ secondaryLabel: string;
639
+ permission: string;
640
+ blockedTitle?: string | undefined;
641
+ blockedMessage?: string | undefined;
642
+ settingsLabel?: string | undefined;
643
+ continueLabel?: string | undefined;
644
+ illustration?: string | undefined;
645
+ }>;
646
+ type PermissionCardProps = z.infer<typeof schema> & Partial<InjectedProps> & {
647
+ /** THE ONLY function that can open an OS dialog. Called from the primary handler alone. */
648
+ request?: () => Promise<WirePermissionStatus>;
649
+ /** NON-PROMPTING status read. Decides which primary action is offered, nothing else. */
650
+ getStatus?: () => Promise<WirePermissionStatus>;
651
+ /** Open the OS settings page. Only reachable on the `blocked` route. */
652
+ openSettings?: () => void | Promise<void>;
653
+ /** Reports each moment for analytics. Never control flow. */
654
+ onStage?: (stage: PermissionStage, status?: WirePermissionStatus) => void;
655
+ /** Fires exactly once, with the outcome this screen produced. The flow advances on it. */
656
+ onSettled?: (outcome: WirePermissionOutcome) => void;
657
+ };
658
+ /** The typed component the flow renders directly (host-injected placement). */
659
+ declare const PermissionCardView: React__default.NamedExoticComponent<PermissionCardProps>;
660
+ /**
661
+ * The SDK registration object, so a server-emitted placement can adopt this exact screen later.
662
+ * Not part of `onboardingComponents` on purpose - see the file header.
663
+ */
664
+ declare const PermissionCard: WireAIComponent;
665
+
553
666
  /**
554
667
  * The shared option shape for the choice cards (SelectionCard, CardGridSelectCard).
555
668
  *
@@ -1120,6 +1233,25 @@ type OnboardingFlowProps = {
1120
1233
  * `started` so host funnels don't double-count the session.
1121
1234
  */
1122
1235
  resumed?: boolean;
1236
+ /**
1237
+ * Host-declared permission screens injected into this server-driven stream. See
1238
+ * `permissions/types.ts`: the OS dialog is only ever reached from the primary tap.
1239
+ */
1240
+ permissionScreens?: PermissionScreenConfig[];
1241
+ /**
1242
+ * Screen ids already settled in THIS session, restored from `storage` by WireOnboarding so an
1243
+ * app kill mid-flow does not re-ask. Empty (the default) means "nothing settled yet".
1244
+ */
1245
+ settledPermissions?: readonly string[];
1246
+ /**
1247
+ * True while that restore is still in flight. Permission screens are suppressed until it lands,
1248
+ * because showing one before the record is read is exactly the re-ask the record prevents. The
1249
+ * read is short (one storage get, timeout-capped) and cannot outlive the first card's round trip
1250
+ * in practice, so this normally never shows up as a delay.
1251
+ */
1252
+ permissionsPending?: boolean;
1253
+ /** Persist one settled screen id (fire-and-forget; WireOnboarding owns the storage). */
1254
+ onPermissionSettled?: (id: string, outcome: WirePermissionOutcome) => void;
1123
1255
  };
1124
1256
  declare const OnboardingFlow: React__default.FC<OnboardingFlowProps>;
1125
1257
 
@@ -1755,6 +1887,88 @@ interface RevenueCatBridge {
1755
1887
  */
1756
1888
  declare const createRevenueCatBridge: (config: RevenueCatBridgeConfig) => RevenueCatBridge;
1757
1889
 
1890
+ /**
1891
+ * placement - WHERE an injected permission screen lands in a stream nobody knows the length of.
1892
+ *
1893
+ * The card stream is server-driven: the backend decides how many questions this user gets, and the
1894
+ * count differs per user and per run. So a placement is never an index into a known list; it is a
1895
+ * predicate evaluated against the card the flow is ABOUT to render, on every turn.
1896
+ *
1897
+ * THE CLAMP is the whole reason this is a function and not a comparison. `{ afterCard: 4 }` against
1898
+ * a stream that ended after 3 would simply never fire, and "the screen silently never showed" is
1899
+ * the exact failure a host cannot see. So an unreached target degrades to `"beforeEnd"`: the
1900
+ * terminal card is the last position where the screen can still exist, so that is where it goes.
1901
+ *
1902
+ * PURE, no React, no React Native - the placement math is the part worth unit-testing.
1903
+ */
1904
+
1905
+ /** Where a screen sits when the host does not say. High intent, and it can never be clamped away. */
1906
+ declare const DEFAULT_PERMISSION_PLACEMENT: PermissionPlacement;
1907
+ /** The flow position a placement is resolved against. */
1908
+ type FlowPosition = {
1909
+ /** 0-based index of the card about to render. `-1` when no card has arrived yet. */
1910
+ cardIndex: number;
1911
+ /** Whether that card is the terminal recap, i.e. the flow's last screen. */
1912
+ isTerminal: boolean;
1913
+ };
1914
+ /** `afterCard` from a host is arbitrary input: floor it, floor it at 0, and never let NaN through. */
1915
+ declare const normalizeAfterCard: (value: number) => number;
1916
+ /**
1917
+ * Is a screen with this placement due at this position?
1918
+ *
1919
+ * `>=` rather than `===` on purpose: due-ness is re-evaluated every render and a screen is settled
1920
+ * the moment it is answered, so a target the flow has already passed (because a screen ahead of it
1921
+ * in the array was on screen for those turns) still fires instead of being skipped forever.
1922
+ */
1923
+ declare const isPermissionDue: (placement: PermissionPlacement, position: FlowPosition) => boolean;
1924
+ /**
1925
+ * The stable id a screen is remembered by across a resume. Defaults to `<permission>:<index>`,
1926
+ * which is right until the array is reordered - hence the documented `id` escape hatch.
1927
+ */
1928
+ declare const permissionScreenId: (screen: PermissionScreenConfig, index: number) => string;
1929
+ /** A due screen and the id it is remembered by. */
1930
+ type DuePermissionScreen = {
1931
+ screen: PermissionScreenConfig;
1932
+ id: string;
1933
+ };
1934
+ /**
1935
+ * The FIRST configured screen that is due here and has not already been settled in this session.
1936
+ * One at a time, in array order, so two screens due at the same position queue rather than collide.
1937
+ */
1938
+ declare const selectDuePermissionScreen: (screens: readonly PermissionScreenConfig[] | undefined, settled: readonly string[], position: FlowPosition) => DuePermissionScreen | undefined;
1939
+
1940
+ /**
1941
+ * permissionMemory - "shown exactly once per session", kept true across an app kill.
1942
+ *
1943
+ * The in-memory guard alone is not enough. The kit already resumes a killed onboarding into the
1944
+ * SAME backend session (see `persistedSession.ts`), so without a persisted record the resumed mount
1945
+ * would re-show a permission screen the user had already answered - and on the ask path that means
1946
+ * a second dialog attempt against an OS that grants exactly one.
1947
+ *
1948
+ * The record is SCOPED TO THE SESSION ID, one entry, never a growing map: a record whose
1949
+ * `sessionId` is not the session being resumed is stale by definition and reads as empty. That is
1950
+ * also what makes a genuinely new onboarding start clean without anything having to expire it.
1951
+ *
1952
+ * Same host-injected `WireOnboardingStorage` and the same best-effort discipline as the session
1953
+ * seed: reads race the shared ceiling and degrade to "nothing remembered", writes swallow every
1954
+ * error. A broken storage adapter must never gate or break onboarding.
1955
+ */
1956
+
1957
+ /** Storage key for an app's settled permission screens, e.g. `wireai:permissions:acme`. */
1958
+ declare const permissionStorageKey: (appId: string) => string;
1959
+ /**
1960
+ * PURE seam: which screen ids a stored blob contributes to THIS session. A corrupt entry, a
1961
+ * malformed shape, or a record belonging to a different session all read as "nothing remembered",
1962
+ * because re-showing a screen is a far cheaper failure than trusting another session's answers.
1963
+ */
1964
+ declare const readSettledPermissions: (raw: string | null | undefined, sessionId: string) => string[];
1965
+ /** Read the settled ids for `sessionId`. Never throws, never hangs past the shared read ceiling. */
1966
+ declare const loadSettledPermissions: (storage: WireOnboardingStorage, key: string, sessionId: string) => Promise<string[]>;
1967
+ /** Persist the settled ids for `sessionId` - fire-and-forget, all errors swallowed. */
1968
+ declare const saveSettledPermissions: (storage: WireOnboardingStorage, key: string, sessionId: string, ids: readonly string[]) => void;
1969
+ /** Drop the record (fired alongside the session seed on completion) - fire-and-forget. */
1970
+ declare const clearSettledPermissions: (storage: WireOnboardingStorage, key: string) => void;
1971
+
1758
1972
  /** The canonical event name for an app-open. A trigger keys off this exact string. */
1759
1973
  declare const SESSION_STARTED_EVENT: "app.session_started";
1760
1974
  /** Options for {@link reportSessionStart}. Everything except `target` is optional so a pre-auth
@@ -1986,4 +2200,4 @@ interface UseLifecycleEventsOptions {
1986
2200
  */
1987
2201
  declare const useLifecycleEvents: (config: LifecycleConfig | undefined, options?: UseLifecycleEventsOptions) => void;
1988
2202
 
1989
- export { AnimatedSparkle, BACKGROUND_SESSION_MS, type CachedFeatures, CardGridSelectCard, CardHandoff, type CardHandoffProps, type CardHandoffVariant, type CardOption, ChipSelectCard, ClientEvent, ClientEventTarget, CompletionView, DEFAULT_FEATURES_TTL_MS, DemoOnboarding, type DemoOnboardingProps, DeviceContext, ErrorBlock, FIRST_OPEN_EVENT, IconRegistryProvider, IllustrationProvider, type IllustrationRegistry, InterstitialCard, type LifecycleConfig, type LifecycleEventInput, LoadingBlock, LoadingScreen, NumberStepperCard, type OnboardingAttribution, Button as OnboardingButton, OnboardingCopy, OnboardingEvent, type OnboardingFlagOptions, OnboardingFlow, OnboardingResult, OnboardingScaffold, OnboardingTheme, OnboardingThemeProvider, PLAN_TIER_CONTEXT_KEY, type PlanTier, type PurchaseProps, type ReportFirstOpenOptions, type ReportSessionStartOptions, type ResolveFeaturesOptions, type RevenueCatBridge, type RevenueCatBridgeConfig, type RevenueCatCustomerInfoLike, type RevenueCatEntitlementLike, type RevenueCatErrorLike, type RevenueCatOfferingLike, type RevenueCatPackageLike, type RevenueCatProductLike, type RevenueCatSink, SESSION_STARTED_EVENT, SelectionCard, type SessionStartConfig, StatusCard, StepProgress, StepValidator, TextInputCard, type ThemeFromBrandInput, type UseLifecycleEventsOptions, type UseSessionStartOptions, type UseWireActivation, WIRE_ENV_VARS, WIRE_ICON_GLYPHS, WIRE_ICON_NAMES, WIRE_PURCHASE_EVENTS, type WireActivation, type WireActivationConfig, type WireConfigOverrides, WireFeatures, WireFeaturesConfig, WireFeaturesProvider, type WireFeaturesProviderProps, WireIcon, type WireIconFamily, type WireIconGlyph, type WireIconName, type WireIconProps, type WireIconRegistry, type WireLifecycleOptions, WireOnboarding, WireOnboardingConfig, WireOnboardingProps, WireOnboardingStorage, type WirePurchaseEventName, WireUserContext, activeEntitlement, attributionMetadata, bumpActivationRevalidation, createRevenueCatBridge, createWireActivation, defaultIllustrations, defaultOnboardingTheme, defaultWireFeatures, deriveAnswers, describeEntitlement, describeFailure, describePackage, detectAppVersion, detectNativeModel, featuresCacheKey, featuresEqual, fetchWireFeatures, firstOpenStorageKey, getActivationRevalidationVersion, isFeaturesFresh, isOnboardingEnabled, isUserCancelled, lookupIconGlyph, mergeTheme, motionSpec, onboardingComponents, parseWireFeatures, readCachedFeatures, readProgress, reportFirstOpen, reportSessionStart, resetActivationRevalidation, resetFirstOpenLatch, resetSessionStartGuard, resolvePlanTier, subscribeActivationRevalidation, themeFromBrand, useActivationRevalidation, useHostIcon, useIllustration, useLifecycleEvents, useOnboardingTheme, useReducedMotion, useResolvedFeatures, useSessionStart, useWireActivation, useWireFeatures, useWireFeaturesContext, wireConfigFromEnv, wireLifecycleEvents, writeCachedFeatures };
2203
+ export { AnimatedSparkle, BACKGROUND_SESSION_MS, type CachedFeatures, CardGridSelectCard, CardHandoff, type CardHandoffProps, type CardHandoffVariant, type CardOption, ChipSelectCard, ClientEvent, ClientEventTarget, CompletionView, DEFAULT_FEATURES_TTL_MS, DEFAULT_PERMISSION_PLACEMENT, DemoOnboarding, type DemoOnboardingProps, DeviceContext, type DuePermissionScreen, ErrorBlock, FIRST_OPEN_EVENT, type FlowPosition, IconRegistryProvider, IllustrationProvider, type IllustrationRegistry, InterstitialCard, type LifecycleConfig, type LifecycleEventInput, LoadingBlock, LoadingScreen, NumberStepperCard, type OnboardingAttribution, Button as OnboardingButton, OnboardingCopy, OnboardingEvent, type OnboardingFlagOptions, OnboardingFlow, OnboardingResult, OnboardingScaffold, OnboardingTheme, OnboardingThemeProvider, PERMISSION_CARD_NAME, PLAN_TIER_CONTEXT_KEY, PermissionCard, type PermissionCardProps, PermissionCardView, PermissionPlacement, PermissionScreenConfig, PermissionStage, type PlanTier, type PurchaseProps, type ReportFirstOpenOptions, type ReportSessionStartOptions, type ResolveFeaturesOptions, type RevenueCatBridge, type RevenueCatBridgeConfig, type RevenueCatCustomerInfoLike, type RevenueCatEntitlementLike, type RevenueCatErrorLike, type RevenueCatOfferingLike, type RevenueCatPackageLike, type RevenueCatProductLike, type RevenueCatSink, SESSION_STARTED_EVENT, SelectionCard, type SessionStartConfig, StatusCard, StepProgress, StepValidator, TextInputCard, type ThemeFromBrandInput, type UseLifecycleEventsOptions, type UseSessionStartOptions, type UseWireActivation, WIRE_ENV_VARS, WIRE_ICON_GLYPHS, WIRE_ICON_NAMES, WIRE_PURCHASE_EVENTS, type WireActivation, type WireActivationConfig, type WireConfigOverrides, WireFeatures, WireFeaturesConfig, WireFeaturesProvider, type WireFeaturesProviderProps, WireIcon, type WireIconFamily, type WireIconGlyph, type WireIconName, type WireIconProps, type WireIconRegistry, type WireLifecycleOptions, WireOnboarding, WireOnboardingConfig, WireOnboardingProps, WireOnboardingStorage, WirePermissionOutcome, WirePermissionStatus, type WirePurchaseEventName, WireUserContext, activeEntitlement, attributionMetadata, bumpActivationRevalidation, clearSettledPermissions, createRevenueCatBridge, createWireActivation, defaultIllustrations, defaultOnboardingTheme, defaultWireFeatures, deriveAnswers, describeEntitlement, describeFailure, describePackage, detectAppVersion, detectNativeModel, featuresCacheKey, featuresEqual, fetchWireFeatures, firstOpenStorageKey, getActivationRevalidationVersion, isFeaturesFresh, isOnboardingEnabled, isPermissionDue, isUserCancelled, loadSettledPermissions, lookupIconGlyph, mergeTheme, motionSpec, normalizeAfterCard, onboardingComponents, parseWireFeatures, permissionScreenId, permissionStorageKey, readCachedFeatures, readProgress, readSettledPermissions, reportFirstOpen, reportSessionStart, resetActivationRevalidation, resetFirstOpenLatch, resetSessionStartGuard, resolvePlanTier, saveSettledPermissions, selectDuePermissionScreen, subscribeActivationRevalidation, themeFromBrand, useActivationRevalidation, useHostIcon, useIllustration, useLifecycleEvents, useOnboardingTheme, useReducedMotion, useResolvedFeatures, useSessionStart, useWireActivation, useWireFeatures, useWireFeaturesContext, wireConfigFromEnv, wireLifecycleEvents, writeCachedFeatures };