@mrrocket/sdk 0.17.0 → 0.18.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.
Files changed (43) hide show
  1. package/CHANGELOG.md +7 -1
  2. package/dist/browser/mrrocket-flows.js +9 -2
  3. package/dist/browser/mrrocket-messenger.js +3 -3
  4. package/dist/browser/mrrocket.js +1 -1
  5. package/dist/browser.d.ts +1 -1
  6. package/dist/browser.js +2 -1
  7. package/dist/browser.js.map +1 -1
  8. package/dist/client.d.ts +23 -3
  9. package/dist/client.js +15 -1
  10. package/dist/client.js.map +1 -1
  11. package/dist/context.d.ts +3 -1
  12. package/dist/context.js.map +1 -1
  13. package/dist/core.d.ts +3 -1
  14. package/dist/core.js +1 -1
  15. package/dist/core.js.map +1 -1
  16. package/dist/finish-web.d.ts +18 -0
  17. package/dist/finish-web.js +139 -0
  18. package/dist/finish-web.js.map +1 -0
  19. package/dist/flows-ui.d.ts +1 -10
  20. package/dist/flows-ui.js +105 -6
  21. package/dist/flows-ui.js.map +1 -1
  22. package/dist/flows-web.d.ts +8 -1
  23. package/dist/flows-web.js +8 -0
  24. package/dist/flows-web.js.map +1 -1
  25. package/dist/flows.d.ts +159 -7
  26. package/dist/flows.js +240 -28
  27. package/dist/flows.js.map +1 -1
  28. package/dist/icons.d.ts +24 -0
  29. package/dist/icons.js +61 -0
  30. package/dist/icons.js.map +1 -0
  31. package/dist/index.d.ts +4 -2
  32. package/dist/index.js +5 -0
  33. package/dist/index.js.map +1 -1
  34. package/dist/messenger-ui.js +78 -53
  35. package/dist/messenger-ui.js.map +1 -1
  36. package/dist/onboarding-state.d.ts +23 -0
  37. package/dist/onboarding-state.js +43 -0
  38. package/dist/onboarding-state.js.map +1 -0
  39. package/dist/react-native/index.d.cts +177 -15
  40. package/dist/react-native/index.d.ts +177 -15
  41. package/dist/react-native/index.js +776 -270
  42. package/dist/react-native/index.js.map +1 -1
  43. package/package.json +1 -1
package/dist/flows.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/flows.ts"],"sourcesContent":["/**\n * The cancel flow, onboarding and in-app messages (SDK 0.7.0): what MRRocket's config and `GET /api/sdk/inapp` serve,\n * and one controller that decides what shows when and records what the person did. No DOM and no React: the web\n * renderer (flows-ui.ts, loaded only when something shows) and the React Native one (native/flows-view.ts) draw\n * `view()` and call the actions.\n *\n * What it records, as normal events under the SDK's analytics consent (nothing with consent \"off\"):\n * - `$cancel_flow` `{action: \"shown\" | \"answered\" | \"offer\" | \"finished\", run, version, …}`. MRRocket turns a\n * `finished` with `outcome: \"saved\"` into a save and with `outcome: \"cancelled\"` into a cancellation on the Churn page.\n * MRRocket never changes a subscription: an accepted offer goes to the app's `onSave`, a confirmed cancel to its\n * `onCancel`, and only after they succeed is the outcome recorded.\n * - `$onboarding` `{flow, step?, action: \"started\" | \"shown\" | \"completed\" | \"dismissed\"}`; since 0.11.0 a wait records\n * \"shown\" when the person reaches it and \"completed\" when it is over, a condition `{action: \"answered\", answer: \"yes\"\n * | \"no\"}` once it is decided; since 0.17.0 a tour a checklist task started records `{action: \"started\", from}` with\n * the checklist's id.\n * - `$inapp` `{message, journey, action: \"shown\" | \"clicked\" | \"dismissed\"}`; the journey moves the person on.\n */\nimport type { CockpitConfig } from \"./core\";\n\nexport const CANCEL_EVENT = \"$cancel_flow\";\nexport const ONBOARDING_EVENT = \"$onboarding\";\nexport const INAPP_EVENT = \"$inapp\";\n/** 0.16.0: the app asked the store's rating dialog for a review request (or found none on the device). */\nexport const REVIEW_EVENT = \"$review_prompt\";\n\nexport type CancelOfferKind = \"discount\" | \"pause\" | \"downgrade\" | \"contact\";\n\n/** What one answer of the survey offers before the confirmation. An answer without one goes straight to it. */\nexport interface CancelOffer {\n kind: CancelOfferKind;\n headline: string;\n /** Discount: percent off. */\n percent?: number;\n /** Discount: for how many months; pause: up to how many months. */\n months?: number;\n /** Downgrade: the plan to switch to, as MRRocket knows it from the payment provider. */\n plan?: string;\n planName?: string;\n}\n\n/** 0.15.0: a second question after an answer (\"What would be a fair price?\"); no answers means a free text. */\nexport interface CancelFollowUp {\n question: string;\n /** The choices, in order; empty for a free text. */\n answers: string[];\n}\n\n/**\n * 0.15.0: a different offer for some customers of one answer. Every condition that is set must hold; the first rule\n * that matches wins, else the answer's own offer. `offer: null` means no offer (straight to the confirmation).\n */\nexport interface CancelRule {\n when: {\n /** The follow-up's answer, as written. */\n answer?: string;\n /** The customer's plan: its id or name as MRRocket knows it from the payment provider (any of a list). */\n plan?: string | string[];\n /** A customer for fewer than this many months. */\n tenureUnder?: number;\n /** A customer for at least this many months. */\n tenureOver?: number;\n };\n offer: CancelOffer | null;\n}\n\n/** The product's live cancel flow (MRRocket › Churn › Cancel flow). */\nexport interface CancelFlowConfig {\n /** Grows with every publish. */\n version: number;\n question: string;\n /** The survey's answers, in order. */\n reasons: { id: string; label: string }[];\n /** The offer per answer id. */\n offers: Record<string, CancelOffer>;\n /** 0.15.0: the follow-up question per answer id. */\n followUps?: Record<string, CancelFollowUp>;\n /** 0.15.0: the rules per answer id, checked in order before its offer. */\n rules?: Record<string, CancelRule[]>;\n /** 0.15.0: what one answer offers after its first offer was declined. */\n second?: Record<string, CancelOffer>;\n confirm: { title: string; body: string; button: string };\n /**\n * Who gets the survey and the offers: business customers only. Anyone else (a consumer, or a customer the app does\n * not say) gets the plain confirmation, with nothing in the way of cancelling (§ 312k BGB).\n */\n offersFor: \"business\";\n}\n\nexport type OnboardingStepKind = \"tour\" | \"checklist\" | \"modal\";\n\n/** 0.17.0: where a tour bubble sits next to its element; \"auto\" (and no value) puts it below, else above, as before. */\nexport type TourPlacement = \"auto\" | \"top\" | \"bottom\" | \"left\" | \"right\";\n\n/**\n * 0.17.0: a picture or a video above a tour step's or a modal's text. A video is a YouTube, Vimeo or Loom link (shown\n * through their privacy-friendly players, see `videoEmbed`) or a video file (mp4, webm).\n */\nexport interface FlowMedia {\n kind: \"image\" | \"video\";\n url: string;\n alt?: string;\n}\n\nexport interface OnboardingStep {\n id: string;\n kind: OnboardingStepKind;\n title: string;\n body?: string;\n button?: string;\n /** Tour steps: the element they point at, marked `data-ck-target=\"<target>\"` in the app. Without it: a card. */\n target?: string;\n /** Checklist items: the event that ticks them (`track(\"<event>\")`). Without it the person ticks it themselves. */\n event?: string;\n /** 0.17.0, tour steps: which side of the element the bubble takes (flips when there is no room). */\n placement?: TourPlacement;\n /** 0.17.0, tour steps and modals: a picture or a video above the text. */\n media?: FlowMedia;\n /** 0.17.0, modals: where the button goes (http(s) or a path of the app). */\n url?: string;\n /** 0.17.0, checklist items: the id of a tour (another flow) the item starts; finishing the tour ticks the item. */\n startsTour?: string;\n}\n\n/** A pause in a flow (0.11.0): the next step shows this long after the one before was done. */\nexport interface OnboardingWait {\n id: string;\n kind: \"wait\";\n amount: number;\n unit: \"hours\" | \"days\";\n}\n\n/**\n * A fork in a flow (0.11.0): \"Did <event> happen?\" (the person's events, here and on MRRocket) or \"Is on <page>?\" (the\n * page they are on when they reach it; `*` at the end for everything below). Decided once, when the person reaches it;\n * like a journey's branch it ends its line, the rest of the flow lives in its two paths.\n */\nexport interface OnboardingCondition {\n id: string;\n kind: \"condition\";\n event?: string;\n page?: string;\n yes: OnboardingNode[];\n no: OnboardingNode[];\n}\n\nexport type OnboardingNode = OnboardingStep | OnboardingWait | OnboardingCondition;\n\n/** A live onboarding flow's in-app steps (MRRocket › Conversion › Onboarding); its emails go out from MRRocket. */\nexport interface OnboardingFlow {\n id: string;\n /** A checklist's heading; for tours and modals the name MRRocket knows them by. */\n name: string;\n /**\n * When it starts: an event's words (\"Signed up\", \"First export\"), \"First launch\" / \"First visit\", since 0.17.0 a page\n * of the app (\"/settings\", \"/projects/*\"), or \"From a checklist\" (only a checklist task starts it).\n */\n trigger: string;\n /** 0.17.0: \"signed_in\" starts it on its own only for someone the app identified. */\n audience?: \"signed_in\";\n /** What every version shows: with `nodes`, only the in-app steps before the first wait or condition. */\n steps: OnboardingStep[];\n /** 0.11.0+: the whole flow with its waits and conditions. Read instead of `steps` when it is there. */\n nodes?: OnboardingNode[];\n}\n\n/** An in-app message a Lifecycle journey has for this person. */\nexport interface InAppMessage {\n id: string;\n journey?: string;\n title: string;\n body?: string;\n button?: string;\n /** Where the button goes (http(s) or a path of the app). */\n url?: string;\n style: \"banner\" | \"modal\";\n}\n\n/** One flow's progress: the steps done, whether the person closed it, and (0.11.0) its decided conditions and times. */\nexport interface FlowProgress {\n done: string[];\n dismissed?: boolean;\n /** Each decided condition's path. */\n answers?: Record<string, \"yes\" | \"no\">;\n /** When each step was done and each wait was reached (ms since 1970). */\n at?: Record<string, number>;\n reached?: Record<string, number>;\n}\n\n/** What `GET /api/sdk/inapp` answers for one signed-in person. */\nexport interface InAppState {\n /** Progress per onboarding flow: the steps done and whether the person closed it. */\n onboarding: Record<string, FlowProgress>;\n messages: InAppMessage[];\n /** 0.11.0: the events the flows' conditions ask about that this person did (as `eventSlug`s). */\n events?: string[];\n /**\n * 0.16.0: a live review request may ask this person now. The React Native client's `requestReview()` opens the\n * store's own rating dialog (expo-store-review) and reports `$review_prompt`; websites have no store dialog.\n */\n review?: { campaign: string; store: string };\n}\n\nexport interface CancelFlowResult {\n /** \"cancelled\": confirmed; \"saved\": took the offer; \"kept\": closed the dialog without cancelling. */\n outcome: \"cancelled\" | \"saved\" | \"kept\";\n reason?: string;\n reasonLabel?: string;\n /** 0.15.0: the answer to the follow-up question. */\n answer?: string;\n offer?: CancelOffer;\n /** What the customer wrote on the confirmation, if anything. */\n comment?: string;\n /** This run's id, the same as in the events. */\n run: string;\n}\n\nexport interface CancelFlowOptions {\n /**\n * \"business\" shows the survey and the offers; anything else (the default) only the plain confirmation, so a\n * consumer can always cancel in one step (§ 312k BGB).\n */\n customer?: \"business\" | \"consumer\";\n /** The customer's current price, to show what a discount costs (`{ amount: 29, currency: \"EUR\" }`). */\n price?: { amount: number; currency: string; interval?: \"month\" | \"year\" };\n /** 0.15.0: the customer's plan (its id or name in your billing), for offers that depend on it. */\n plan?: string;\n /** 0.15.0: since when they pay (a date, ISO string or ms), for offers that depend on how long. */\n customerSince?: Date | string | number;\n /** The customer confirmed: cancel the subscription in your code. Throwing keeps the dialog open with an error. */\n onCancel?: (result: CancelFlowResult) => unknown;\n /** The customer took an offer: apply it in your code (coupon, pause, plan change). Throwing keeps the dialog open. */\n onSave?: (result: CancelFlowResult) => unknown;\n /** Closed without cancelling or taking an offer. */\n onClose?: (result: CancelFlowResult) => void;\n}\n\n/** What a renderer draws. */\nexport type FlowView =\n | {\n type: \"cancel\";\n screen: \"survey\" | \"followup\" | \"offer\" | \"confirm\";\n flow: CancelFlowConfig;\n /** The answer picked on the survey. */\n reason?: string;\n /** 0.15.0: the follow-up question on its screen, and its answer so far. */\n followUp?: CancelFollowUp;\n answer?: string;\n offer?: CancelOffer;\n /** 0.15.0: 2 for the offer after a declined one. */\n round?: 1 | 2;\n price?: CancelFlowOptions[\"price\"];\n busy: boolean;\n error?: string;\n /** The step for the dots, 1 to `steps`. */\n step: number;\n steps: number;\n }\n | { type: \"step\"; flow: OnboardingFlow; step: OnboardingStep; index: number; total: number; last: boolean }\n /** `tour`: the tour the item starts (0.17.0), when the config has it. */\n | { type: \"checklist\"; flow: OnboardingFlow; items: { step: OnboardingStep; done: boolean; tour?: OnboardingFlow }[] }\n | { type: \"message\"; message: InAppMessage };\n\nexport interface FlowsEnv {\n /** Records an event under the SDK's consent (the normal queue). */\n track(event: string, props?: Record<string, unknown>): void;\n /** Whose messages and progress these are: the identify() id; undefined while nobody is signed in. */\n personId(): string | undefined;\n /** `GET /api/sdk/inapp` for that person; null when it fails. */\n fetchInApp(personId: string): Promise<InAppState | null>;\n /** Keeps onboarding progress on the device; only while the consent lets the SDK store something. */\n storage?: () => { get(key: string): string | null | Promise<string | null>; set(key: string, value: string): void } | null;\n /** Opens a message's link. */\n openUrl?: (url: string) => void;\n /** Start onboarding flows by their trigger on its own (default true). */\n auto?: boolean;\n /** The page the person is on (a path), for \"Is on <page>?\" conditions; without it they answer no. */\n path?: () => string | undefined;\n /** The clock, for tests. */\n now?: () => number;\n}\n\nexport interface FlowsController {\n setConfig(config: CockpitConfig | null): void;\n /** The person changed (identify, reset, consent): loads their messages and onboarding progress. */\n refresh(): Promise<void>;\n /** An event the app recorded: starts flows whose trigger it is and ticks checklist items. */\n onEvent(name: string): void;\n openCancelFlow(options?: CancelFlowOptions): Promise<CancelFlowResult>;\n /** Starts an onboarding flow now (the first live one without an id). False when there is none to start. */\n startOnboarding(id?: string): boolean;\n view(): FlowView | null;\n subscribe(listener: () => void): () => void;\n /** `accept(comment)`: a \"Talk to us\" offer sends what the customer wrote with it. */\n /** `answer(text)`: the follow-up's choice or free text (0.15.0); `next()` moves on from the survey and the follow-up. */\n cancel: {\n pick(reason: string): void;\n answer(text: string): void;\n next(): void;\n accept(comment?: string): void;\n decline(): void;\n confirm(comment?: string): void;\n close(): void;\n };\n /** `startTour(stepId)` (0.17.0): the tour a checklist item starts; the checklist comes back when it ends. */\n onboarding: { next(): void; check(stepId: string): void; dismiss(): void; startTour(stepId: string): boolean };\n message: { click(): void; dismiss(): void };\n}\n\n/** The plain confirmation, when the product has no live cancel flow (or MRRocket was not reachable). */\nexport const PLAIN_CANCEL: CancelFlowConfig = {\n version: 0,\n question: \"\",\n reasons: [],\n offers: {},\n confirm: { title: \"Cancel your subscription?\", body: \"You keep access until the end of the billing period.\", button: \"Cancel subscription\" },\n offersFor: \"business\",\n};\n\nconst FAILED = \"Something went wrong. Please try again.\";\nconst PROGRESS_KEY = \"mrrocket:onboarding\";\n\n/** `\"Signed up\"` → `signed_up`, the way MRRocket names events from words. */\nexport const eventSlug = (s: string) =>\n s\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"_\")\n .replace(/^_+|_+$/g, \"\");\n\nconst SIGNUP = new Set([\"signed_up\", \"signup\", \"sign_up\", \"registered\", \"account_created\", \"user_signed_up\"]);\n\n/** Whether recording `event` is the trigger's moment (\"Signed up\" is any of the usual signup event names). */\nexport const matchesTrigger = (trigger: string, event: string) => {\n const t = eventSlug(trigger);\n const e = eventSlug(event);\n if (!t || !e) return false;\n return t === e || (SIGNUP.has(t) && SIGNUP.has(e));\n};\n\n/** Triggers that mean \"the first time the person opens the app\": the flow starts on load until it is done. */\nexport const startsOnLoad = (trigger: string) =>\n /^(first (launch|visit|open|session|login|sign ?in|app open)|app (opened|launched)|on load|every visit|always)?$/i.test(trigger.trim());\n\n/** 0.17.0: the trigger of a tour only a checklist task starts. */\nexport const FROM_CHECKLIST = \"From a checklist\";\n\n/** 0.17.0: a trigger that is a page of the app (\"/settings\", \"/projects/*\"): the flow starts when the person opens it. */\nexport const isPageTrigger = (trigger: string) => trigger.trim().startsWith(\"/\");\n\n/** A \"From a checklist\" tour never starts on its own. */\nconst manual = (trigger: string) => eventSlug(trigger) === \"from_a_checklist\";\n\n/**\n * 0.17.0: how a video link plays. YouTube, Vimeo and Loom through their players without tracking cookies\n * (youtube-nocookie.com, Vimeo's `dnt=1`), a file (mp4, webm, mov, or an upload in MRRocket) in a plain video element;\n * anything else is not shown. Regular expressions only, so React Native can use it too.\n */\nexport const videoEmbed = (url: string | undefined): { kind: \"iframe\" | \"file\"; src: string } | null => {\n const u = (url ?? \"\").trim();\n if (!/^https?:\\/\\//i.test(u)) return null;\n let m = u.match(/^https?:\\/\\/(?:www\\.|m\\.)?(?:youtube\\.com\\/(?:watch\\?(?:.*&)?v=|embed\\/|shorts\\/|live\\/)|youtu\\.be\\/|youtube-nocookie\\.com\\/embed\\/)([\\w-]{6,20})/i);\n if (m) return { kind: \"iframe\", src: `https://www.youtube-nocookie.com/embed/${m[1]}?rel=0` };\n m = u.match(/^https?:\\/\\/(?:www\\.|player\\.)?vimeo\\.com\\/(?:video\\/)?(\\d{4,12})/i);\n if (m) return { kind: \"iframe\", src: `https://player.vimeo.com/video/${m[1]}?dnt=1` };\n m = u.match(/^https?:\\/\\/(?:www\\.)?loom\\.com\\/(?:share|embed)\\/([\\w-]{8,64})/i);\n if (m) return { kind: \"iframe\", src: `https://www.loom.com/embed/${m[1]}` };\n if (/\\.(mp4|webm|mov|m4v)(\\?|#|$)/i.test(u) || /\\/media\\/uploads\\//.test(u)) return { kind: \"file\", src: u };\n return null;\n};\n\n/** 0.17.0: a step's picture or video when the dialogs may load it (http(s), a video they can play), else undefined. */\nexport const mediaOf = (step: Pick<OnboardingStep, \"media\">): FlowMedia | undefined => {\n const m = step.media;\n if (!m || (m.kind !== \"image\" && m.kind !== \"video\") || !/^https?:\\/\\//i.test(m.url ?? \"\")) return undefined;\n return m.kind === \"video\" && !videoEmbed(m.url) ? undefined : m;\n};\n\n/** A random id for one cancel flow run. */\nexport const runId = () => `cf_${Math.random().toString(36).slice(2, 10)}${Date.now().toString(36)}`;\n\n/** The money a discount leaves: `29 EUR` at 30 % → `20.30`. */\nexport const discounted = (amount: number, percent: number) => Math.round(amount * (1 - percent / 100) * 100) / 100;\n\n/** \"€20.30\" in the customer's own currency, without a library. */\nexport const money = (amount: number, currency: string) => {\n try {\n return new Intl.NumberFormat(undefined, { style: \"currency\", currency }).format(amount);\n } catch {\n return `${amount.toFixed(2)} ${currency}`;\n }\n};\n\n/** The button of an offer. */\nexport const offerButton = (o: CancelOffer) =>\n o.kind === \"contact\" ? \"Send to the team\" : o.kind === \"pause\" ? \"Pause instead\" : o.kind === \"downgrade\" ? \"Switch plan\" : \"Accept offer\";\n\n/** A link a message may open: http(s), or a path of the app. Nothing else (no `javascript:`). */\nexport const safeUrl = (url: string | undefined) => {\n const u = (url ?? \"\").trim();\n return /^https?:\\/\\//i.test(u) || /^\\/(?!\\/)/.test(u) ? u : undefined;\n};\n\ninterface Progress {\n done: Set<string>;\n dismissed: boolean;\n answers: Map<string, boolean>;\n at: Map<string, number>;\n reached: Map<string, number>;\n}\n\n/** The flow's nodes: the whole tree (0.11.0 config), or the plain steps of an older one. */\nexport const nodesOf = (f: OnboardingFlow): OnboardingNode[] => f.nodes ?? f.steps;\n\n/** How long a wait lasts. */\nexport const waitMs = (w: Pick<OnboardingWait, \"amount\" | \"unit\">) => Math.max(0, w.amount) * (w.unit === \"hours\" ? 3_600_000 : 86_400_000);\n\n/** Whether a path matches a condition's page: the same path, or below it for a pattern ending in `*`. */\nexport const pageMatches = (pattern: string, path: string) => {\n const p = pattern.trim();\n const at = (path.split(/[?#]/)[0] || \"/\").replace(/\\/+$/, \"\") || \"/\";\n if (!p) return false;\n if (p.endsWith(\"*\")) {\n const base = p.slice(0, -1).replace(/\\/+$/, \"\");\n return !base || at === base || at.startsWith(`${base}/`);\n }\n return at === (p.replace(/\\/+$/, \"\") || \"/\");\n};\n\n/** Every in-app step of a tree, in reading order (for ticking checklist items by their event). */\nconst stepsIn = (nodes: OnboardingNode[]): OnboardingStep[] =>\n nodes.flatMap((n) => (n.kind === \"condition\" ? [...stepsIn(n.yes), ...stepsIn(n.no)] : n.kind === \"wait\" ? [] : [n]));\n\ntype Walked =\n | { at: \"step\"; step: OnboardingStep; line: OnboardingNode[]; index: number; before: number }\n | { at: \"wait\"; until: number }\n | { at: \"pending\" }\n | { at: \"end\" };\n\n/** Whole months since a date the app passed (NaN without one, which no tenure condition holds for). */\nexport const monthsSince = (since: CancelFlowOptions[\"customerSince\"], now = Date.now()) =>\n since == null || since === \"\" ? NaN : Math.floor((now - new Date(since).getTime()) / 2_630_016_000);\n\nconst low = (x?: string) => x?.trim().toLowerCase();\n\n/** Whether a rule holds for this customer; a condition about something the app did not pass does not hold. */\nexport const ruleMatches = ({ when: w = {} }: CancelRule, c: { answer?: string; plan?: string; months: number }) =>\n (w.answer == null || low(w.answer) === low(c.answer)) &&\n (w.plan == null || [w.plan].flat().some((x) => low(x) === low(c.plan))) &&\n !(c.months >= w.tenureUnder!) &&\n !(c.months < w.tenureOver!) &&\n (w.tenureUnder == null || c.months >= 0) &&\n (w.tenureOver == null || c.months >= 0);\n\ninterface CancelRun {\n flow: CancelFlowConfig;\n options: CancelFlowOptions;\n plain: boolean;\n screen: \"survey\" | \"followup\" | \"offer\" | \"confirm\";\n reason?: string;\n answer?: string;\n /** The offer on the offer screen, which rule picked it, and which round it is. */\n offer?: CancelOffer;\n rule?: number;\n round: 1 | 2;\n run: string;\n busy: boolean;\n error?: string;\n resolve: (r: CancelFlowResult) => void;\n promise: Promise<CancelFlowResult>;\n}\n\nexport const createFlows = (env: FlowsEnv): FlowsController => {\n let config: CockpitConfig | null = null;\n let cancel: CancelRun | null = null;\n let active: string | null = null;\n let messages: InAppMessage[] = [];\n let known = false;\n let loading: Promise<void> | null = null;\n let loadedFor: string | undefined;\n let progressFor: string | undefined | null = null;\n const progress = new Map<string, Progress>();\n const handled = new Set<string>();\n const shown = new Set<string>();\n const listeners = new Set<() => void>();\n const auto = env.auto !== false;\n /** Events the person did (as slugs), for \"Did <event> happen?\": from MRRocket and from this device. */\n const seenEvents = new Set<string>();\n const now = () => env.now?.() ?? Date.now();\n let timer: ReturnType<typeof setTimeout> | null = null;\n /** 0.17.0: the checklist (and its item) a running tour was started from; it comes back when the tour ends. */\n let returnTo: { flow: string; step: string } | null = null;\n\n const flows = () => config?.onboarding ?? [];\n const flowById = (id: string | null) => (id ? flows().find((f) => f.id === id) : undefined);\n const prog = (id: string) => {\n let p = progress.get(id);\n if (!p) progress.set(id, (p = { done: new Set(), dismissed: false, answers: new Map(), at: new Map(), reached: new Map() }));\n return p;\n };\n\n let dirty = false;\n const record = (props: Record<string, unknown>) => {\n env.track(ONBOARDING_EVENT, props);\n dirty = true;\n };\n const markDone = (p: Progress, id: string) => {\n p.done.add(id);\n p.at.set(id, now());\n };\n\n /** A condition's answer for this person, or undefined while what it asks is not known yet. */\n const decide = (c: OnboardingCondition): boolean | undefined => {\n if (c.page?.trim()) {\n const path = env.path?.();\n return path === undefined ? false : pageMatches(c.page, path);\n }\n if (c.event?.trim()) {\n // The person's events from MRRocket arrive with their progress: before that, wait.\n if (!known) return undefined;\n for (const e of seenEvents) if (matchesTrigger(c.event, e)) return true;\n return false;\n }\n return false;\n };\n\n /**\n * Where the person is in a line of the flow. `live`: it is their flow now, so reaching a wait starts it, a decided\n * condition is kept and both are recorded; otherwise nothing changes (to ask whether a flow is finished).\n */\n const walkLine = (flow: OnboardingFlow, line: OnboardingNode[], p: Progress, live: boolean, before = 0, last?: number): Walked => {\n for (let i = 0; i < line.length; i++) {\n const n = line[i];\n if (n.kind === \"wait\") {\n if (p.done.has(n.id)) {\n last = p.at.get(n.id) ?? last;\n continue;\n }\n let since = p.reached.get(n.id);\n if (since === undefined) {\n if (!live) return { at: \"pending\" };\n // Counted from when the step before was done; a flow that opens with a wait counts from now.\n since = last ?? now();\n p.reached.set(n.id, since);\n record({ flow: flow.id, step: n.id, kind: \"wait\", action: \"shown\" });\n }\n const until = since + waitMs(n);\n if (now() < until) return { at: \"wait\", until };\n if (!live) return { at: \"pending\" };\n markDone(p, n.id);\n record({ flow: flow.id, step: n.id, kind: \"wait\", action: \"completed\" });\n last = until;\n continue;\n }\n if (n.kind === \"condition\") {\n let answer = p.answers.get(n.id);\n if (answer === undefined) {\n if (!live) return { at: \"pending\" };\n answer = decide(n);\n if (answer === undefined) return { at: \"pending\" };\n p.answers.set(n.id, answer);\n record({ flow: flow.id, step: n.id, kind: \"condition\", action: \"answered\", answer: answer ? \"yes\" : \"no\" });\n }\n return walkLine(flow, answer ? n.yes : n.no, p, live, before, last);\n }\n if (!p.done.has(n.id)) return { at: \"step\", step: n, line, index: i, before };\n last = p.at.get(n.id) ?? last;\n before++;\n }\n return { at: \"end\" };\n };\n\n const finished = (f: OnboardingFlow) => {\n const p = progress.get(f.id);\n return !!p && (p.dismissed || walkLine(f, nodesOf(f), p, false).at === \"end\");\n };\n\n const keyOf = (pid: string | undefined) => `${PROGRESS_KEY}:${pid ?? \"visitor\"}`;\n const eventsKeyOf = (pid: string | undefined) => `${PROGRESS_KEY}:events:${pid ?? \"visitor\"}`;\n\n const save = () => {\n dirty = false;\n const store = env.storage?.();\n if (!store) return;\n const out: Record<string, FlowProgress> = {};\n for (const [id, p] of progress)\n out[id] = {\n done: [...p.done],\n ...(p.dismissed ? { dismissed: true } : {}),\n ...(p.answers.size ? { answers: Object.fromEntries([...p.answers].map(([k, v]) => [k, v ? \"yes\" : \"no\"])) } : {}),\n ...(p.at.size ? { at: Object.fromEntries(p.at) } : {}),\n ...(p.reached.size ? { reached: Object.fromEntries(p.reached) } : {}),\n };\n try {\n store.set(keyOf(env.personId()), JSON.stringify(out));\n if (seenEvents.size) store.set(eventsKeyOf(env.personId()), JSON.stringify([...seenEvents].slice(-200)));\n } catch {\n // Storage full or gone: progress stays in memory.\n }\n };\n\n const merge = (state: InAppState[\"onboarding\"] | undefined) => {\n for (const [id, s] of Object.entries(state ?? {})) {\n const p = prog(id);\n for (const d of s.done ?? []) p.done.add(d);\n if (s.dismissed) p.dismissed = true;\n // A condition decided elsewhere keeps its path; the earliest time wins for waits.\n for (const [k, v] of Object.entries(s.answers ?? {})) if (!p.answers.has(k)) p.answers.set(k, v === \"yes\");\n for (const [k, v] of Object.entries(s.at ?? {})) if (typeof v === \"number\" && !p.at.has(k)) p.at.set(k, v);\n for (const [k, v] of Object.entries(s.reached ?? {})) if (typeof v === \"number\" && (!p.reached.has(k) || v < p.reached.get(k)!)) p.reached.set(k, v);\n }\n };\n\n // ---------- What shows ----------\n\n /** Comes back when a short wait is over, so the next step shows without a reload (long waits wait for the next visit). */\n const wakeAt = (until: number) => {\n if (timer) clearTimeout(timer);\n timer = null;\n const ms = until - now();\n if (ms > 0 && ms <= 86_400_000) {\n timer = setTimeout(() => ((timer = null), emit()), ms + 50);\n // Never what keeps a process alive (tests, server rendering).\n (timer as { unref?: () => void }).unref?.();\n }\n };\n\n /** The active flow waits (a wait step, or a condition whose answer is not known yet): it stays the active one. */\n let holding = false;\n\n const onboardingView = (): FlowView | null => {\n holding = false;\n const flow = flowById(active);\n if (!flow) return null;\n const p = prog(flow.id);\n if (p.dismissed) return null;\n const w = walkLine(flow, nodesOf(flow), p, true);\n if (dirty) save();\n if (w.at === \"wait\") wakeAt(w.until);\n holding = w.at === \"wait\" || w.at === \"pending\";\n if (w.at !== \"step\") return null;\n const { step, line, index } = w;\n const isStep = (n: OnboardingNode | undefined): n is OnboardingStep => !!n && n.kind !== \"wait\" && n.kind !== \"condition\";\n if (step.kind === \"checklist\") {\n let from = index;\n while (from > 0 && isStep(line[from - 1]) && line[from - 1].kind === \"checklist\") from--;\n let to = index;\n while (to + 1 < line.length && isStep(line[to + 1]) && line[to + 1].kind === \"checklist\") to++;\n const items = line.slice(from, to + 1).filter(isStep);\n for (const i of items)\n if (!p.done.has(i.id) && !shown.has(`s:${i.id}`)) {\n shown.add(`s:${i.id}`);\n env.track(ONBOARDING_EVENT, { flow: flow.id, step: i.id, kind: \"checklist\", action: \"shown\" });\n }\n return {\n type: \"checklist\",\n flow,\n items: items.map((s) => {\n const tour = s.startsTour && s.startsTour !== flow.id ? flowById(s.startsTour) : undefined;\n return { step: s, done: p.done.has(s.id), ...(tour && nodesOf(tour).length ? { tour } : {}) };\n }),\n };\n }\n if (!shown.has(`s:${step.id}`)) {\n shown.add(`s:${step.id}`);\n env.track(ONBOARDING_EVENT, { flow: flow.id, step: step.id, kind: step.kind, action: \"shown\" });\n }\n // The dots: the steps behind on the person's path, this one, and the ones left on its line before any fork.\n const rest = line.slice(index + 1);\n const fork = rest.findIndex((n) => n.kind === \"condition\");\n const ahead = (fork < 0 ? rest : rest.slice(0, fork)).filter(isStep).length;\n return { type: \"step\", flow, step, index: w.before, total: w.before + 1 + ahead, last: !ahead && fork < 0 };\n };\n\n const view = (): FlowView | null => {\n if (cancel) {\n const c = cancel;\n const followUp = c.flow.followUps?.[c.reason!];\n // The dots: the survey, a follow-up, one or two offers, the confirmation.\n const f = followUp ? 1 : 0;\n const steps = c.plain ? 1 : 3 + f + (c.flow.second?.[c.reason!] ? 1 : 0);\n return {\n type: \"cancel\",\n screen: c.screen,\n flow: c.flow,\n reason: c.reason,\n ...(c.screen === \"followup\" && followUp ? { followUp, answer: c.answer } : {}),\n offer: c.screen === \"offer\" ? c.offer : undefined,\n ...(c.screen === \"offer\" ? { round: c.round } : {}),\n price: c.options.price,\n busy: c.busy,\n error: c.error,\n step: c.screen === \"survey\" ? 1 : c.screen === \"followup\" ? 2 : c.screen === \"offer\" ? 1 + f + c.round : steps,\n steps,\n };\n }\n const ob = onboardingView();\n if (ob) return ob;\n // A flow that just ended lets the next thing show; one that waits keeps its place.\n if (active && !holding) {\n const back = returnTo;\n const ended = active;\n returnTo = null;\n active = null;\n const list = back ? flowById(back.flow) : undefined;\n if (back && list) {\n // The tour a checklist item started is over: the checklist comes back, and a finished tour ticks an item\n // that has no event of its own (a closed one does not).\n const item = stepsIn(nodesOf(list)).find((s) => s.id === back.step);\n const p = prog(list.id);\n if (item && !item.event && !progress.get(ended)?.dismissed && !p.done.has(item.id)) {\n markDone(p, item.id);\n record({ flow: list.id, step: item.id, kind: \"checklist\", action: \"completed\" });\n }\n active = list.id;\n const again = onboardingView();\n if (again) return again;\n if (!holding) active = null;\n } else if (known) {\n // 0.17.0: the next flow whose moment has come (another \"First launch\", this page's, or one of the same\n // trigger as the one that ended) follows right away.\n const after = flowById(ended)?.trigger;\n Promise.resolve().then(() => autoStart(after));\n }\n }\n const m = messages.find((x) => !handled.has(x.id));\n return m ? { type: \"message\", message: m } : null;\n };\n\n /** Tells the renderers, and records what became visible (once per step or message per page load). */\n const emit = () => {\n // Onboarding steps record their \"shown\" where they are worked out (onboardingView), so a step a wait let through\n // counts also when nothing else changed.\n const v = view();\n if (v?.type === \"message\" && !shown.has(`m:${v.message.id}`)) {\n shown.add(`m:${v.message.id}`);\n env.track(INAPP_EVENT, { message: v.message.id, journey: v.message.journey, action: \"shown\" });\n }\n for (const l of [...listeners]) {\n try {\n l();\n } catch {\n // One renderer must not stop another.\n }\n }\n };\n\n const start = (flow: OnboardingFlow) => {\n if (active === flow.id) return true;\n if (active && flowById(active) && !finished(flowById(active)!)) return false;\n active = flow.id;\n env.track(ONBOARDING_EVENT, { flow: flow.id, action: \"started\" });\n emit();\n return true;\n };\n\n /** 0.17.0: a flow for signed-in people starts on its own only for someone the app identified. */\n const allowed = (f: OnboardingFlow) => f.audience !== \"signed_in\" || !!env.personId();\n\n /**\n * Flows whose trigger is \"the first launch\" (or, 0.17.0, the page the person is on) start once their progress is\n * known (never twice for a done flow). `after`: the trigger of a flow that just ended, whose next unfinished flow\n * follows it (onboarding items made from one older flow share its trigger).\n */\n const autoStart = (after?: string) => {\n if (!auto || active || !known) return;\n // Without an id or storage a \"first launch\" could not be told from any other: no automatic start then.\n if (!env.personId() && !env.storage?.()) return;\n const path = env.path?.();\n const same = after && !manual(after) ? eventSlug(after) : \"\";\n const next = flows().find(\n (f) =>\n (isPageTrigger(f.trigger) ? path !== undefined && pageMatches(f.trigger, path) : startsOnLoad(f.trigger) || (!!same && eventSlug(f.trigger) === same)) &&\n allowed(f) &&\n nodesOf(f).length &&\n !finished(f),\n );\n if (next) start(next);\n };\n\n const refresh = () => {\n const pid = env.personId();\n // Another person on this device: nothing of the one before carries over.\n if (progressFor !== null && progressFor !== pid) {\n progress.clear();\n seenEvents.clear();\n handled.clear();\n active = null;\n returnTo = null;\n loadedFor = undefined;\n }\n progressFor = pid;\n const run = (async () => {\n const store = env.storage?.();\n if (store) {\n try {\n const raw = await store.get(keyOf(pid));\n if (raw) merge(JSON.parse(raw) as InAppState[\"onboarding\"]);\n const seen = await store.get(eventsKeyOf(pid));\n if (seen) for (const e of JSON.parse(seen) as string[]) if (typeof e === \"string\") seenEvents.add(e);\n } catch {\n // A damaged entry is ignored.\n }\n }\n const wants = flows().length > 0 || !!config?.messages;\n if (pid && wants && loadedFor !== pid) {\n loadedFor = pid;\n const state = await env.fetchInApp(pid).catch(() => null);\n if (state && env.personId() === pid) {\n merge(state.onboarding);\n for (const e of state.events ?? []) if (typeof e === \"string\") seenEvents.add(eventSlug(e));\n messages = (state.messages ?? []).filter((m) => m && m.id && m.title);\n }\n }\n if (!pid) messages = [];\n known = true;\n autoStart();\n emit();\n })();\n loading = run.finally(() => {\n if (loading === run) loading = null;\n });\n return loading;\n };\n\n // ---------- Cancel flow ----------\n\n /** Which offer of the answer was on screen, for MRRocket to know what was saved: a rule's or the second. */\n const offerAt = (c: CancelRun) => (c.round > 1 ? { round: 2 } : c.rule! >= 0 ? { rule: c.rule } : {});\n\n /** Into the offer screen with the answer's first offer (the first rule that holds, else its own), or the confirmation. */\n const toOffer = (c: CancelRun) => {\n const who = { answer: c.answer, plan: c.options.plan, months: monthsSince(c.options.customerSince, env.now?.()) };\n const rules = c.flow.rules?.[c.reason!] ?? [];\n c.rule = rules.findIndex((r) => ruleMatches(r, who));\n c.offer = c.rule < 0 ? c.flow.offers[c.reason!] : (rules[c.rule].offer ?? undefined);\n c.round = 1;\n c.screen = c.offer ? \"offer\" : \"confirm\";\n };\n\n const result = (c: CancelRun, outcome: CancelFlowResult[\"outcome\"], comment?: string): CancelFlowResult => {\n const offer = c.screen === \"offer\" ? c.offer : undefined;\n return {\n outcome,\n run: c.run,\n ...(c.reason ? { reason: c.reason, reasonLabel: c.flow.reasons.find((r) => r.id === c.reason)?.label } : {}),\n ...(c.answer ? { answer: c.answer } : {}),\n ...(outcome === \"saved\" && offer ? { offer } : {}),\n ...(comment?.trim() ? { comment: comment.trim().slice(0, 500) } : {}),\n };\n };\n\n const finish = (c: CancelRun, r: CancelFlowResult) => {\n env.track(CANCEL_EVENT, {\n action: \"finished\",\n run: c.run,\n version: c.flow.version,\n outcome: r.outcome,\n customer: c.options.customer ?? \"consumer\",\n ...(r.reason ? { reason: r.reason } : {}),\n ...(r.answer ? { answer: r.answer } : {}),\n ...(r.offer ? { offer: r.offer.kind, ...offerAt(c) } : {}),\n ...(r.comment ? { comment: r.comment } : {}),\n });\n if (cancel === c) cancel = null;\n c.resolve(r);\n emit();\n };\n\n /** Runs the app's callback; the outcome is recorded only once it went through. */\n const settle = (c: CancelRun, outcome: \"saved\" | \"cancelled\", comment?: string) => {\n if (c.busy) return;\n const r = result(c, outcome, comment);\n const fn = outcome === \"saved\" ? c.options.onSave : c.options.onCancel;\n c.busy = true;\n c.error = undefined;\n emit();\n Promise.resolve()\n .then(() => fn?.(r))\n .then(\n () => finish(c, r),\n () => {\n c.busy = false;\n c.error = FAILED;\n emit();\n },\n );\n };\n\n const openCancelFlow = (options: CancelFlowOptions = {}) => {\n if (cancel) return cancel.promise;\n const flow = config?.cancelFlow ?? PLAIN_CANCEL;\n const plain = options.customer !== \"business\" || flow.reasons.length < 2;\n let resolve: (r: CancelFlowResult) => void = () => {};\n const promise = new Promise<CancelFlowResult>((r) => (resolve = r));\n const c: CancelRun = { flow, options, plain, screen: plain ? \"confirm\" : \"survey\", round: 1, run: runId(), busy: false, resolve, promise };\n cancel = c;\n env.track(CANCEL_EVENT, { action: \"shown\", run: c.run, version: flow.version, customer: options.customer ?? \"consumer\", plain });\n emit();\n return promise;\n };\n\n const controller: FlowsController = {\n setConfig: (c) => {\n config = c;\n if (active && !flowById(active)) active = null;\n emit();\n },\n refresh,\n onEvent: (name) => {\n // 0.17.0: a page view may be the moment of a flow with a page trigger.\n if (name === \"$pageview\") autoStart();\n if (!name || name.startsWith(\"$\")) return;\n // Remembered for \"Did <event> happen?\" conditions the person reaches later.\n const slug = eventSlug(name);\n const fresh = !!slug && !seenEvents.has(slug);\n if (fresh) seenEvents.add(slug);\n const flow = flowById(active);\n let ticked = false;\n if (flow) {\n const p = prog(flow.id);\n for (const s of stepsIn(nodesOf(flow)))\n if (s.kind === \"checklist\" && s.event && !p.done.has(s.id) && matchesTrigger(s.event, name)) {\n markDone(p, s.id);\n ticked = true;\n env.track(ONBOARDING_EVENT, { flow: flow.id, step: s.id, kind: s.kind, action: \"completed\" });\n }\n }\n if (ticked || (fresh && flow)) {\n save();\n emit();\n } else if (fresh) save();\n // Like the first-launch start: only for someone whose progress is kept, so nobody gets a flow twice.\n if (!auto || (!env.personId() && !env.storage?.())) return;\n const next = flows().find((f) => nodesOf(f).length && !manual(f.trigger) && matchesTrigger(f.trigger, name) && allowed(f) && !finished(f));\n if (next) start(next);\n },\n openCancelFlow,\n startOnboarding: (id) => {\n const f = id ? flowById(id) : flows().find((x) => nodesOf(x).length && !manual(x.trigger) && !finished(x));\n if (!f || !nodesOf(f).length) return false;\n const p = prog(f.id);\n // Started by hand: a flow the person closed before shows again.\n p.dismissed = false;\n return start(f);\n },\n view,\n subscribe: (l) => {\n listeners.add(l);\n return () => listeners.delete(l);\n },\n cancel: {\n pick: (reason) => {\n if (!cancel || cancel.busy || cancel.screen !== \"survey\" || !cancel.flow.reasons.some((r) => r.id === reason)) return;\n if (cancel.reason !== reason) cancel.answer = undefined;\n cancel.reason = reason;\n cancel.error = undefined;\n emit();\n },\n answer: (text) => {\n const c = cancel;\n if (!c || c.busy || c.screen !== \"followup\") return;\n const t = text.slice(0, 500);\n c.answer = t.trim() ? t : undefined;\n emit();\n },\n next: () => {\n const c = cancel;\n if (!c || !c.reason) return;\n // The follow-up's answer rides along on the finished event.\n if (c.screen === \"survey\") {\n env.track(CANCEL_EVENT, { action: \"answered\", run: c.run, version: c.flow.version, reason: c.reason });\n if (c.flow.followUps?.[c.reason]) c.screen = \"followup\";\n else toOffer(c);\n } else if (c.screen === \"followup\") {\n c.answer = c.answer?.trim();\n toOffer(c);\n } else return;\n emit();\n },\n accept: (comment) => {\n const c = cancel;\n if (!c || c.screen !== \"offer\" || !c.reason) return;\n if (!c.busy)\n env.track(CANCEL_EVENT, {\n action: \"offer\",\n run: c.run,\n version: c.flow.version,\n reason: c.reason,\n offer: c.offer?.kind,\n ...offerAt(c),\n outcome: \"accepted\",\n });\n settle(c, \"saved\", comment);\n },\n decline: () => {\n const c = cancel;\n if (!c || c.screen !== \"offer\" || c.busy) return;\n env.track(CANCEL_EVENT, {\n action: \"offer\",\n run: c.run,\n version: c.flow.version,\n reason: c.reason,\n offer: c.offer?.kind,\n ...offerAt(c),\n outcome: \"declined\",\n });\n // A second offer once, then the confirmation.\n const second = c.round === 1 && c.reason ? c.flow.second?.[c.reason] : undefined;\n if (second) {\n c.offer = second;\n c.rule = undefined;\n c.round = 2;\n } else c.screen = \"confirm\";\n c.error = undefined;\n emit();\n },\n confirm: (comment) => {\n const c = cancel;\n if (!c || c.screen !== \"confirm\") return;\n settle(c, \"cancelled\", comment);\n },\n close: () => {\n const c = cancel;\n if (!c || c.busy) return;\n const r = result(c, \"kept\");\n c.options.onClose?.(r);\n finish(c, r);\n },\n },\n onboarding: {\n next: () => {\n const v = onboardingView();\n if (v?.type !== \"step\") return;\n markDone(prog(v.flow.id), v.step.id);\n env.track(ONBOARDING_EVENT, { flow: v.flow.id, step: v.step.id, kind: v.step.kind, action: \"completed\" });\n save();\n emit();\n // 0.17.0: a modal's button may lead somewhere.\n const url = v.step.kind === \"modal\" ? safeUrl(v.step.url) : undefined;\n if (url) env.openUrl?.(url);\n },\n check: (stepId) => {\n const v = onboardingView();\n if (v?.type !== \"checklist\") return;\n const item = v.items.find((i) => i.step.id === stepId);\n if (!item || item.done) return;\n markDone(prog(v.flow.id), stepId);\n env.track(ONBOARDING_EVENT, { flow: v.flow.id, step: stepId, kind: \"checklist\", action: \"completed\" });\n save();\n emit();\n },\n dismiss: () => {\n const v = onboardingView();\n if (!v || (v.type !== \"step\" && v.type !== \"checklist\")) return;\n prog(v.flow.id).dismissed = true;\n env.track(ONBOARDING_EVENT, { flow: v.flow.id, ...(v.type === \"step\" ? { step: v.step.id } : {}), action: \"dismissed\" });\n // A tour started from a checklist was closed: the checklist comes back, its item stays open.\n active = returnTo && returnTo.flow !== v.flow.id && flowById(returnTo.flow) ? returnTo.flow : null;\n returnTo = null;\n save();\n emit();\n },\n startTour: (stepId) => {\n const v = onboardingView();\n if (v?.type !== \"checklist\") return false;\n const tour = v.items.find((i) => i.step.id === stepId)?.tour;\n if (!tour) return false;\n // Started by hand: from its first step, also when the person saw or closed it before.\n const p = prog(tour.id);\n p.done.clear();\n p.at.clear();\n p.dismissed = false;\n returnTo = { flow: v.flow.id, step: stepId };\n active = tour.id;\n env.track(ONBOARDING_EVENT, { flow: tour.id, action: \"started\", from: v.flow.id });\n save();\n emit();\n return true;\n },\n },\n message: {\n click: () => {\n const v = view();\n if (v?.type !== \"message\") return;\n handled.add(v.message.id);\n env.track(INAPP_EVENT, { message: v.message.id, journey: v.message.journey, action: \"clicked\" });\n const url = safeUrl(v.message.url);\n emit();\n if (url) env.openUrl?.(url);\n },\n dismiss: () => {\n const v = view();\n if (v?.type !== \"message\") return;\n handled.add(v.message.id);\n env.track(INAPP_EVENT, { message: v.message.id, journey: v.message.journey, action: \"dismissed\" });\n emit();\n },\n },\n };\n return controller;\n};\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/flows.ts"],"sourcesContent":["/**\n * The cancel flow, onboarding and in-app messages (SDK 0.7.0): what MRRocket's config and `GET /api/sdk/inapp` serve,\n * and one controller that decides what shows when and records what the person did. No DOM and no React: the web\n * renderer (flows-ui.ts, loaded only when something shows) and the React Native one (native/flows-view.ts) draw\n * `view()` and call the actions.\n *\n * What it records, as normal events under the SDK's analytics consent (nothing with consent \"off\"):\n * - `$cancel_flow` `{action: \"shown\" | \"answered\" | \"offer\" | \"finished\", run, version, …}`. MRRocket turns a\n * `finished` with `outcome: \"saved\"` into a save and with `outcome: \"cancelled\"` into a cancellation on the Churn page.\n * MRRocket never changes a subscription: an accepted offer goes to the app's `onSave`, a confirmed cancel to its\n * `onCancel`, and only after they succeed is the outcome recorded.\n * - `$onboarding` `{flow, step?, action: \"started\" | \"shown\" | \"completed\" | \"dismissed\"}`; since 0.11.0 a wait records\n * \"shown\" when the person reaches it and \"completed\" when it is over, a condition `{action: \"answered\", answer: \"yes\"\n * | \"no\"}` once it is decided; since 0.17.0 a tour a checklist task started records `{action: \"started\", from}` with\n * the checklist's id; since 0.18.0 a question records `{kind: \"question\", action: \"completed\", question, answer,\n * label}` (the question's key, the option's id or the typed text, the option's words), which MRRocket keeps on the\n * contact, and a checklist task that opens something `{action: \"opened\", opens}`.\n * - `$inapp` `{message, journey, action: \"shown\" | \"clicked\" | \"dismissed\"}`; the journey moves the person on.\n */\nimport type { CockpitConfig } from \"./core\";\n\nexport const CANCEL_EVENT = \"$cancel_flow\";\nexport const ONBOARDING_EVENT = \"$onboarding\";\nexport const INAPP_EVENT = \"$inapp\";\n/** 0.16.0: the app asked the store's rating dialog for a review request (or found none on the device). */\nexport const REVIEW_EVENT = \"$review_prompt\";\n\nexport type CancelOfferKind = \"discount\" | \"pause\" | \"downgrade\" | \"contact\";\n\n/** What one answer of the survey offers before the confirmation. An answer without one goes straight to it. */\nexport interface CancelOffer {\n kind: CancelOfferKind;\n headline: string;\n /** Discount: percent off. */\n percent?: number;\n /** Discount: for how many months; pause: up to how many months. */\n months?: number;\n /** Downgrade: the plan to switch to, as MRRocket knows it from the payment provider. */\n plan?: string;\n planName?: string;\n}\n\n/** 0.15.0: a second question after an answer (\"What would be a fair price?\"); no answers means a free text. */\nexport interface CancelFollowUp {\n question: string;\n /** The choices, in order; empty for a free text. */\n answers: string[];\n}\n\n/**\n * 0.15.0: a different offer for some customers of one answer. Every condition that is set must hold; the first rule\n * that matches wins, else the answer's own offer. `offer: null` means no offer (straight to the confirmation).\n */\nexport interface CancelRule {\n when: {\n /** The follow-up's answer, as written. */\n answer?: string;\n /** The customer's plan: its id or name as MRRocket knows it from the payment provider (any of a list). */\n plan?: string | string[];\n /** A customer for fewer than this many months. */\n tenureUnder?: number;\n /** A customer for at least this many months. */\n tenureOver?: number;\n };\n offer: CancelOffer | null;\n}\n\n/** The product's live cancel flow (MRRocket › Churn › Cancel flow). */\nexport interface CancelFlowConfig {\n /** Grows with every publish. */\n version: number;\n question: string;\n /** The survey's answers, in order. */\n reasons: { id: string; label: string }[];\n /** The offer per answer id. */\n offers: Record<string, CancelOffer>;\n /** 0.15.0: the follow-up question per answer id. */\n followUps?: Record<string, CancelFollowUp>;\n /** 0.15.0: the rules per answer id, checked in order before its offer. */\n rules?: Record<string, CancelRule[]>;\n /** 0.15.0: what one answer offers after its first offer was declined. */\n second?: Record<string, CancelOffer>;\n confirm: { title: string; body: string; button: string };\n /**\n * Who gets the survey and the offers: business customers only. Anyone else (a consumer, or a customer the app does\n * not say) gets the plain confirmation, with nothing in the way of cancelling (§ 312k BGB).\n */\n offersFor: \"business\";\n}\n\n/** \"question\" (0.18.0): one question of a Questions item, a slide of its window. */\nexport type OnboardingStepKind = \"tour\" | \"checklist\" | \"modal\" | \"question\";\n\n/**\n * 0.18.0: what a checklist task opens. `page`: a path or address on the web, a deep link (`myapp://…`) or a screen name\n * in an app, handed to the app's `onNavigate` when it passes one; `tour` / `modal`: another item of the config, after\n * which the checklist comes back; `code`: the app's `onTask(taskId, item)`.\n */\nexport type TaskOpens = { kind: \"page\"; target: string } | { kind: \"tour\"; id: string } | { kind: \"modal\"; id: string } | { kind: \"code\" };\n\n/**\n * 0.18.0: a small picture beside a checklist task or above a modal's or the questions' heading: one of the SDK's own\n * line icons (`FLOW_ICON_NAMES`, icons.ts), an emoji, or a picture (http(s)).\n */\nexport type FlowIcon = { kind: \"builtin\"; name: string } | { kind: \"emoji\"; value: string } | { kind: \"image\"; url: string };\n\n/** 0.18.0: what plays when someone finishes a checklist, a tour or the questions. */\nexport type FinishAnimation = \"none\" | \"confetti\" | \"fireworks\" | \"stars\";\n\n/** 0.18.0: one question of a Questions item: one choice of 2 to 6 options, or a short text. */\nexport interface OnboardingQuestion {\n /** The answer's name on the contact (`onboarding.<key>`) and in \"People who answered …\". */\n key: string;\n type: \"choice\" | \"text\";\n /** Choice: the options, in order; `id` is what is stored, `label` what people read. */\n options?: { id: string; label: string }[];\n /** Text: the field's hint. */\n placeholder?: string;\n}\n\n/** 0.17.0: where a tour bubble sits next to its element; \"auto\" (and no value) puts it below, else above, as before. */\nexport type TourPlacement = \"auto\" | \"top\" | \"bottom\" | \"left\" | \"right\";\n\n/**\n * 0.17.0: a picture or a video above a tour step's or a modal's text. A video is a YouTube, Vimeo or Loom link (shown\n * through their privacy-friendly players, see `videoEmbed`) or a video file (mp4, webm).\n */\nexport interface FlowMedia {\n kind: \"image\" | \"video\";\n url: string;\n alt?: string;\n}\n\nexport interface OnboardingStep {\n id: string;\n kind: OnboardingStepKind;\n title: string;\n body?: string;\n button?: string;\n /** Tour steps: the element they point at, marked `data-ck-target=\"<target>\"` in the app. Without it: a card. */\n target?: string;\n /** Checklist items: the event that ticks them (`track(\"<event>\")`). Without it the person ticks it themselves. */\n event?: string;\n /** 0.17.0, tour steps: which side of the element the bubble takes (flips when there is no room). */\n placement?: TourPlacement;\n /** 0.17.0, tour steps and modals: a picture or a video above the text. */\n media?: FlowMedia;\n /** 0.17.0, modals: where the button goes (http(s) or a path of the app). */\n url?: string;\n /** 0.17.0, checklist items: the id of a tour (another flow) the item starts; finishing the tour ticks the item. */\n startsTour?: string;\n /** 0.18.0, checklist items: what the task opens (read before `startsTour`, which MRRocket still sends for a tour). */\n opens?: TaskOpens;\n /** 0.18.0, checklist items: the task's icon. */\n icon?: FlowIcon;\n /** 0.18.0, question steps: the question (the step's `title` is what people read). */\n question?: OnboardingQuestion;\n}\n\n/** A pause in a flow (0.11.0): the next step shows this long after the one before was done. */\nexport interface OnboardingWait {\n id: string;\n kind: \"wait\";\n amount: number;\n unit: \"hours\" | \"days\";\n}\n\n/**\n * A fork in a flow (0.11.0): \"Did <event> happen?\" (the person's events, here and on MRRocket) or \"Is on <page>?\" (the\n * page they are on when they reach it; `*` at the end for everything below). Decided once, when the person reaches it;\n * like a journey's branch it ends its line, the rest of the flow lives in its two paths.\n */\nexport interface OnboardingCondition {\n id: string;\n kind: \"condition\";\n event?: string;\n page?: string;\n yes: OnboardingNode[];\n no: OnboardingNode[];\n}\n\nexport type OnboardingNode = OnboardingStep | OnboardingWait | OnboardingCondition;\n\n/** A live onboarding flow's in-app steps (MRRocket › Conversion › Onboarding); its emails go out from MRRocket. */\nexport interface OnboardingFlow {\n id: string;\n /** A checklist's heading; for tours and modals the name MRRocket knows them by. */\n name: string;\n /**\n * When it starts: an event's words (\"Signed up\", \"First export\"), \"First launch\" / \"First visit\", since 0.17.0 a page\n * of the app (\"/settings\", \"/projects/*\"), or \"From a checklist\" (only a checklist task starts it).\n */\n trigger: string;\n /**\n * 0.17.0: \"signed_in\" starts it on its own only for someone the app identified. 0.18.0: \"answered\" only for someone\n * who gave `answered.option` to the question `answered.question` (its key).\n */\n audience?: \"signed_in\" | \"answered\";\n /** 0.18.0: with `audience: \"answered\"`, the question's key and the option's id (or the typed text). */\n answered?: { question: string; option: string };\n /** What every version shows: with `nodes`, only the in-app steps before the first wait or condition. */\n steps: OnboardingStep[];\n /** 0.18.0: the icon above a modal's or the questions' heading. */\n icon?: FlowIcon;\n /** 0.18.0: what plays when the person finishes it (a checklist, a tour, the questions). */\n finish?: FinishAnimation;\n /**\n * 0.18.0: \"code\": the app draws it itself (`useOnboarding(id)`, `mrrocket.onboarding.get(id)`); the SDK draws\n * nothing for it, never starts it, and still records what the app reports. Such an item's `trigger` is\n * \"From your code\" (older versions never start it).\n */\n shownBy?: \"code\";\n /**\n * 0.18.0: the item's real moment (read instead of `trigger`, see `momentOf`) when `trigger` is one older versions\n * never start: \"From your code\", or `NEEDS_NEWER` for questions and items that follow another or ask for an answer.\n */\n when?: string;\n /** 0.11.0+: the whole flow with its waits and conditions. Read instead of `steps` when it is there. */\n nodes?: OnboardingNode[];\n}\n\n/** An in-app message a Lifecycle journey has for this person. */\nexport interface InAppMessage {\n id: string;\n journey?: string;\n title: string;\n body?: string;\n button?: string;\n /** Where the button goes (http(s) or a path of the app). */\n url?: string;\n style: \"banner\" | \"modal\";\n}\n\n/** One flow's progress: the steps done, whether the person closed it, and (0.11.0) its decided conditions and times. */\nexport interface FlowProgress {\n done: string[];\n dismissed?: boolean;\n /** Each decided condition's path. */\n answers?: Record<string, \"yes\" | \"no\">;\n /** When each step was done and each wait was reached (ms since 1970). */\n at?: Record<string, number>;\n reached?: Record<string, number>;\n}\n\n/** What `GET /api/sdk/inapp` answers for one signed-in person. */\nexport interface InAppState {\n /** Progress per onboarding flow: the steps done and whether the person closed it. */\n onboarding: Record<string, FlowProgress>;\n messages: InAppMessage[];\n /** 0.11.0: the events the flows' conditions ask about that this person did (as `eventSlug`s). */\n events?: string[];\n /** 0.18.0: the person's answers to onboarding questions, by the question's key (an option's id or the text). */\n answers?: Record<string, string>;\n /**\n * 0.16.0: a live review request may ask this person now. The React Native client's `requestReview()` opens the\n * store's own rating dialog (expo-store-review) and reports `$review_prompt`; websites have no store dialog.\n */\n review?: { campaign: string; store: string };\n}\n\nexport interface CancelFlowResult {\n /** \"cancelled\": confirmed; \"saved\": took the offer; \"kept\": closed the dialog without cancelling. */\n outcome: \"cancelled\" | \"saved\" | \"kept\";\n reason?: string;\n reasonLabel?: string;\n /** 0.15.0: the answer to the follow-up question. */\n answer?: string;\n offer?: CancelOffer;\n /** What the customer wrote on the confirmation, if anything. */\n comment?: string;\n /** This run's id, the same as in the events. */\n run: string;\n}\n\nexport interface CancelFlowOptions {\n /**\n * \"business\" shows the survey and the offers; anything else (the default) only the plain confirmation, so a\n * consumer can always cancel in one step (§ 312k BGB).\n */\n customer?: \"business\" | \"consumer\";\n /** The customer's current price, to show what a discount costs (`{ amount: 29, currency: \"EUR\" }`). */\n price?: { amount: number; currency: string; interval?: \"month\" | \"year\" };\n /** 0.15.0: the customer's plan (its id or name in your billing), for offers that depend on it. */\n plan?: string;\n /** 0.15.0: since when they pay (a date, ISO string or ms), for offers that depend on how long. */\n customerSince?: Date | string | number;\n /** The customer confirmed: cancel the subscription in your code. Throwing keeps the dialog open with an error. */\n onCancel?: (result: CancelFlowResult) => unknown;\n /** The customer took an offer: apply it in your code (coupon, pause, plan change). Throwing keeps the dialog open. */\n onSave?: (result: CancelFlowResult) => unknown;\n /** Closed without cancelling or taking an offer. */\n onClose?: (result: CancelFlowResult) => void;\n}\n\n/** What a renderer draws. */\nexport type FlowView =\n | {\n type: \"cancel\";\n screen: \"survey\" | \"followup\" | \"offer\" | \"confirm\";\n flow: CancelFlowConfig;\n /** The answer picked on the survey. */\n reason?: string;\n /** 0.15.0: the follow-up question on its screen, and its answer so far. */\n followUp?: CancelFollowUp;\n answer?: string;\n offer?: CancelOffer;\n /** 0.15.0: 2 for the offer after a declined one. */\n round?: 1 | 2;\n price?: CancelFlowOptions[\"price\"];\n busy: boolean;\n error?: string;\n /** The step for the dots, 1 to `steps`. */\n step: number;\n steps: number;\n }\n /** `answer` (0.18.0): a question's answer so far (an option's id or the typed text); `back`: an earlier question to go back to. */\n | { type: \"step\"; flow: OnboardingFlow; step: OnboardingStep; index: number; total: number; last: boolean; answer?: string; back?: boolean }\n /**\n * `tour`: the tour the item starts (0.17.0), when the config has it. `opens` (0.18.0): what tapping the task opens\n * (a tour or modal only when the config has it); `target` the item it opens.\n */\n | { type: \"checklist\"; flow: OnboardingFlow; items: { step: OnboardingStep; done: boolean; tour?: OnboardingFlow; opens?: TaskOpens; target?: OnboardingFlow }[] }\n | { type: \"message\"; message: InAppMessage };\n\nexport interface FlowsEnv {\n /** Records an event under the SDK's consent (the normal queue). */\n track(event: string, props?: Record<string, unknown>): void;\n /** Whose messages and progress these are: the identify() id; undefined while nobody is signed in. */\n personId(): string | undefined;\n /** `GET /api/sdk/inapp` for that person; null when it fails. */\n fetchInApp(personId: string): Promise<InAppState | null>;\n /** Keeps onboarding progress on the device; only while the consent lets the SDK store something. */\n storage?: () => { get(key: string): string | null | Promise<string | null>; set(key: string, value: string): void } | null;\n /** Opens a message's link. */\n openUrl?: (url: string) => void;\n /** Start onboarding flows by their trigger on its own (default true). */\n auto?: boolean;\n /** The page the person is on (a path), for \"Is on <page>?\" conditions; without it they answer no. */\n path?: () => string | undefined;\n /**\n * 0.18.0: opens what a checklist task's \"page or screen\" names (a path, an address, a deep link, a screen name).\n * The web and React Native clients pass the app's `onNavigate`, else their own default.\n */\n navigate?: (target: string) => void;\n /** 0.18.0: a checklist task that runs the app's own code (`onTask(taskId, item)`). */\n task?: (taskId: string, item: OnboardingFlow) => void;\n /** The clock, for tests. */\n now?: () => number;\n}\n\nexport interface FlowsController {\n setConfig(config: CockpitConfig | null): void;\n /** The person changed (identify, reset, consent): loads their messages and onboarding progress. */\n refresh(): Promise<void>;\n /** An event the app recorded: starts flows whose trigger it is and ticks checklist items. */\n onEvent(name: string): void;\n openCancelFlow(options?: CancelFlowOptions): Promise<CancelFlowResult>;\n /** Starts an onboarding flow now (the first live one without an id). False when there is none to start. */\n startOnboarding(id?: string): boolean;\n view(): FlowView | null;\n subscribe(listener: () => void): () => void;\n /** `accept(comment)`: a \"Talk to us\" offer sends what the customer wrote with it. */\n /** `answer(text)`: the follow-up's choice or free text (0.15.0); `next()` moves on from the survey and the follow-up. */\n cancel: {\n pick(reason: string): void;\n answer(text: string): void;\n next(): void;\n accept(comment?: string): void;\n decline(): void;\n confirm(comment?: string): void;\n close(): void;\n };\n /**\n * `startTour(stepId)` (0.17.0): the tour a checklist item starts; the checklist comes back when it ends.\n * 0.18.0: `open(stepId)` does what the task opens (a page, a tour, a modal, the app's code); `answer(value)` picks an\n * option or types the text of the question on screen, `back()` returns to the question before. `item(id)` and the\n * calls after it are for an item the app draws itself (`shownBy: \"code\"`).\n */\n onboarding: {\n next(): void;\n check(stepId: string): void;\n dismiss(): void;\n startTour(stepId: string): boolean;\n open(stepId: string): boolean;\n answer(value: string): void;\n back(): void;\n item(id: string): OnboardingItemState | null;\n seen(id: string): void;\n complete(id: string, stepId: string): void;\n dismissItem(id: string): void;\n answerItem(id: string, question: string, value: string): void;\n };\n message: { click(): void; dismiss(): void };\n /** 0.18.0: the animation to play now that someone finished an item (a new `key` each time), or null. */\n finale(): { kind: Exclude<FinishAnimation, \"none\">; key: string } | null;\n /** 0.18.0: the product's brand color from MRRocket, for the dialogs when the app passes no accent of its own. */\n accent(): string | undefined;\n /** 0.18.0: the person's answers to onboarding questions, by key. */\n answers(): Record<string, string>;\n}\n\n/** 0.18.0: an item the app draws itself (`shownBy: \"code\"`), with the person's progress. */\nexport interface OnboardingItemState {\n item: OnboardingFlow;\n /** Its steps (a tour's steps, a checklist's tasks, the questions), each with whether the person did it. */\n steps: (OnboardingStep & { done: boolean; answer?: string })[];\n /** Every step is done. */\n done: boolean;\n /** The person closed it. */\n dismissed: boolean;\n /** Its moment has come for this person (the trigger MRRocket set, and who it is for); your code may ignore it. */\n eligible: boolean;\n}\n\n/** The plain confirmation, when the product has no live cancel flow (or MRRocket was not reachable). */\nexport const PLAIN_CANCEL: CancelFlowConfig = {\n version: 0,\n question: \"\",\n reasons: [],\n offers: {},\n confirm: { title: \"Cancel your subscription?\", body: \"You keep access until the end of the billing period.\", button: \"Cancel subscription\" },\n offersFor: \"business\",\n};\n\nconst FAILED = \"Something went wrong. Please try again.\";\nconst PROGRESS_KEY = \"mrrocket:onboarding\";\n\n/** `\"Signed up\"` → `signed_up`, the way MRRocket names events from words. */\nexport const eventSlug = (s: string) =>\n s\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"_\")\n .replace(/^_+|_+$/g, \"\");\n\nconst SIGNUP = new Set([\"signed_up\", \"signup\", \"sign_up\", \"registered\", \"account_created\", \"user_signed_up\"]);\n\n/** Whether recording `event` is the trigger's moment (\"Signed up\" is any of the usual signup event names). */\nexport const matchesTrigger = (trigger: string, event: string) => {\n const t = eventSlug(trigger);\n const e = eventSlug(event);\n if (!t || !e) return false;\n return t === e || (SIGNUP.has(t) && SIGNUP.has(e));\n};\n\n/** Triggers that mean \"the first time the person opens the app\": the flow starts on load until it is done. */\nexport const startsOnLoad = (trigger: string) =>\n /^(first (launch|visit|open|session|login|sign ?in|app open)|app (opened|launched)|on load|every visit|always)?$/i.test(trigger.trim());\n\n/** 0.17.0: the trigger of a tour only a checklist task starts. */\nexport const FROM_CHECKLIST = \"From a checklist\";\n\n/** 0.17.0: a trigger that is a page of the app (\"/settings\", \"/projects/*\"): the flow starts when the person opens it. */\nexport const isPageTrigger = (trigger: string) => trigger.trim().startsWith(\"/\");\n\n/** 0.18.0: the trigger of an item the app draws itself (`shownBy: \"code\"`); older versions never start it. */\nexport const FROM_CODE = \"From your code\";\n\n/**\n * 0.18.0: the trigger MRRocket sends for an item older versions could not show right (questions, \"After <item> ends\",\n * \"People who answered …\"), so they never start it; its real moment is in `when`.\n */\nexport const NEEDS_NEWER = \"Needs tracking code 0.18.0\";\n\n/** An item's moment: `when` (0.18.0) where MRRocket sent one, else its trigger. */\nexport const momentOf = (f: Pick<OnboardingFlow, \"trigger\" | \"when\">) => f.when ?? f.trigger;\n\n/** A \"From a checklist\" tour (and an item the app draws, 0.18.0) never starts on its own. */\nconst manual = (trigger: string) => eventSlug(trigger) === \"from_a_checklist\" || eventSlug(trigger) === \"from_your_code\" || eventSlug(trigger) === eventSlug(NEEDS_NEWER);\n\n/** 0.18.0: the trigger \"after:<item id>\": the item starts once that item ended (finished or closed). */\nexport const AFTER_PREFIX = \"after:\";\n\n/** The item an \"after:<id>\" trigger follows, or null. */\nexport const afterOf = (trigger: string | undefined) => {\n const t = (trigger ?? \"\").trim();\n return t.toLowerCase().startsWith(AFTER_PREFIX) ? t.slice(AFTER_PREFIX.length).trim() || null : null;\n};\n\n/** 0.18.0: what a task opens; a 0.17.0 `startsTour` reads as opening that tour. */\nexport const opensOf = (step: Pick<OnboardingStep, \"opens\" | \"startsTour\">): TaskOpens | undefined =>\n step.opens ?? (step.startsTour ? { kind: \"tour\", id: step.startsTour } : undefined);\n\n/** 0.18.0: a usable answer to a question: one of its options' ids, or a text of 1 to 500 characters. */\nexport const answerOk = (q: OnboardingQuestion | undefined, value: string | undefined) => {\n if (!q || value == null) return false;\n if (q.type === \"choice\") return (q.options ?? []).some((o) => o.id === value);\n return value.trim().length > 0 && value.length <= 500;\n};\n\n/** The option's words for an answer (a text answer is its own words). */\nexport const answerLabel = (q: OnboardingQuestion | undefined, value: string) =>\n q?.type === \"choice\" ? (q.options?.find((o) => o.id === value)?.label ?? value) : value.trim();\n\n/**\n * 0.17.0: how a video link plays. YouTube, Vimeo and Loom through their players without tracking cookies\n * (youtube-nocookie.com, Vimeo's `dnt=1`), a file (mp4, webm, mov, or an upload in MRRocket) in a plain video element;\n * anything else is not shown. Regular expressions only, so React Native can use it too.\n */\nexport const videoEmbed = (url: string | undefined): { kind: \"iframe\" | \"file\"; src: string } | null => {\n const u = (url ?? \"\").trim();\n if (!/^https?:\\/\\//i.test(u)) return null;\n let m = u.match(/^https?:\\/\\/(?:www\\.|m\\.)?(?:youtube\\.com\\/(?:watch\\?(?:.*&)?v=|embed\\/|shorts\\/|live\\/)|youtu\\.be\\/|youtube-nocookie\\.com\\/embed\\/)([\\w-]{6,20})/i);\n if (m) return { kind: \"iframe\", src: `https://www.youtube-nocookie.com/embed/${m[1]}?rel=0` };\n m = u.match(/^https?:\\/\\/(?:www\\.|player\\.)?vimeo\\.com\\/(?:video\\/)?(\\d{4,12})/i);\n if (m) return { kind: \"iframe\", src: `https://player.vimeo.com/video/${m[1]}?dnt=1` };\n m = u.match(/^https?:\\/\\/(?:www\\.)?loom\\.com\\/(?:share|embed)\\/([\\w-]{8,64})/i);\n if (m) return { kind: \"iframe\", src: `https://www.loom.com/embed/${m[1]}` };\n if (/\\.(mp4|webm|mov|m4v)(\\?|#|$)/i.test(u) || /\\/media\\/uploads\\//.test(u)) return { kind: \"file\", src: u };\n return null;\n};\n\n/** 0.17.0: a step's picture or video when the dialogs may load it (http(s), a video they can play), else undefined. */\nexport const mediaOf = (step: Pick<OnboardingStep, \"media\">): FlowMedia | undefined => {\n const m = step.media;\n if (!m || (m.kind !== \"image\" && m.kind !== \"video\") || !/^https?:\\/\\//i.test(m.url ?? \"\")) return undefined;\n return m.kind === \"video\" && !videoEmbed(m.url) ? undefined : m;\n};\n\n/** A random id for one cancel flow run. */\nexport const runId = () => `cf_${Math.random().toString(36).slice(2, 10)}${Date.now().toString(36)}`;\n\n/** The money a discount leaves: `29 EUR` at 30 % → `20.30`. */\nexport const discounted = (amount: number, percent: number) => Math.round(amount * (1 - percent / 100) * 100) / 100;\n\n/** \"€20.30\" in the customer's own currency, without a library. */\nexport const money = (amount: number, currency: string) => {\n try {\n return new Intl.NumberFormat(undefined, { style: \"currency\", currency }).format(amount);\n } catch {\n return `${amount.toFixed(2)} ${currency}`;\n }\n};\n\n/** The button of an offer. */\nexport const offerButton = (o: CancelOffer) =>\n o.kind === \"contact\" ? \"Send to the team\" : o.kind === \"pause\" ? \"Pause instead\" : o.kind === \"downgrade\" ? \"Switch plan\" : \"Accept offer\";\n\n/** A link a message may open: http(s), or a path of the app. Nothing else (no `javascript:`). */\nexport const safeUrl = (url: string | undefined) => {\n const u = (url ?? \"\").trim();\n return /^https?:\\/\\//i.test(u) || /^\\/(?!\\/)/.test(u) ? u : undefined;\n};\n\ninterface Progress {\n done: Set<string>;\n dismissed: boolean;\n answers: Map<string, boolean>;\n at: Map<string, number>;\n reached: Map<string, number>;\n}\n\n/** The flow's nodes: the whole tree (0.11.0 config), or the plain steps of an older one. */\nexport const nodesOf = (f: OnboardingFlow): OnboardingNode[] => f.nodes ?? f.steps;\n\n/** How long a wait lasts. */\nexport const waitMs = (w: Pick<OnboardingWait, \"amount\" | \"unit\">) => Math.max(0, w.amount) * (w.unit === \"hours\" ? 3_600_000 : 86_400_000);\n\n/** Whether a path matches a condition's page: the same path, or below it for a pattern ending in `*`. */\nexport const pageMatches = (pattern: string, path: string) => {\n const p = pattern.trim();\n const at = (path.split(/[?#]/)[0] || \"/\").replace(/\\/+$/, \"\") || \"/\";\n if (!p) return false;\n if (p.endsWith(\"*\")) {\n const base = p.slice(0, -1).replace(/\\/+$/, \"\");\n return !base || at === base || at.startsWith(`${base}/`);\n }\n return at === (p.replace(/\\/+$/, \"\") || \"/\");\n};\n\n/** Every in-app step of a tree, in reading order (for ticking checklist items by their event). */\nconst stepsIn = (nodes: OnboardingNode[]): OnboardingStep[] =>\n nodes.flatMap((n) => (n.kind === \"condition\" ? [...stepsIn(n.yes), ...stepsIn(n.no)] : n.kind === \"wait\" ? [] : [n]));\n\ntype Walked =\n | { at: \"step\"; step: OnboardingStep; line: OnboardingNode[]; index: number; before: number }\n | { at: \"wait\"; until: number }\n | { at: \"pending\" }\n | { at: \"end\" };\n\n/** Whole months since a date the app passed (NaN without one, which no tenure condition holds for). */\nexport const monthsSince = (since: CancelFlowOptions[\"customerSince\"], now = Date.now()) =>\n since == null || since === \"\" ? NaN : Math.floor((now - new Date(since).getTime()) / 2_630_016_000);\n\nconst low = (x?: string) => x?.trim().toLowerCase();\n\n/** Whether a rule holds for this customer; a condition about something the app did not pass does not hold. */\nexport const ruleMatches = ({ when: w = {} }: CancelRule, c: { answer?: string; plan?: string; months: number }) =>\n (w.answer == null || low(w.answer) === low(c.answer)) &&\n (w.plan == null || [w.plan].flat().some((x) => low(x) === low(c.plan))) &&\n !(c.months >= w.tenureUnder!) &&\n !(c.months < w.tenureOver!) &&\n (w.tenureUnder == null || c.months >= 0) &&\n (w.tenureOver == null || c.months >= 0);\n\ninterface CancelRun {\n flow: CancelFlowConfig;\n options: CancelFlowOptions;\n plain: boolean;\n screen: \"survey\" | \"followup\" | \"offer\" | \"confirm\";\n reason?: string;\n answer?: string;\n /** The offer on the offer screen, which rule picked it, and which round it is. */\n offer?: CancelOffer;\n rule?: number;\n round: 1 | 2;\n run: string;\n busy: boolean;\n error?: string;\n resolve: (r: CancelFlowResult) => void;\n promise: Promise<CancelFlowResult>;\n}\n\nexport const createFlows = (env: FlowsEnv): FlowsController => {\n let config: CockpitConfig | null = null;\n let cancel: CancelRun | null = null;\n let active: string | null = null;\n let messages: InAppMessage[] = [];\n let known = false;\n let loading: Promise<void> | null = null;\n let loadedFor: string | undefined;\n let progressFor: string | undefined | null = null;\n const progress = new Map<string, Progress>();\n const handled = new Set<string>();\n const shown = new Set<string>();\n const listeners = new Set<() => void>();\n const auto = env.auto !== false;\n /** Events the person did (as slugs), for \"Did <event> happen?\": from MRRocket and from this device. */\n const seenEvents = new Set<string>();\n const now = () => env.now?.() ?? Date.now();\n let timer: ReturnType<typeof setTimeout> | null = null;\n /** 0.17.0: the checklist (and its item) a running tour was started from; it comes back when the tour ends. */\n let returnTo: { flow: string; step: string } | null = null;\n /** 0.18.0: the person's answers by question key, the answers typed so far on screen (by step), and the finale. */\n const answers = new Map<string, string>();\n const drafts = new Map<string, string>();\n const celebrated = new Set<string>();\n let finaleNow: { kind: Exclude<FinishAnimation, \"none\">; key: string } | null = null;\n\n const flows = () => config?.onboarding ?? [];\n /** The items the SDK draws (0.18.0: not those the app draws itself). */\n const drawn = () => flows().filter((f) => f.shownBy !== \"code\");\n const flowById = (id: string | null) => (id ? flows().find((f) => f.id === id) : undefined);\n const prog = (id: string) => {\n let p = progress.get(id);\n if (!p) progress.set(id, (p = { done: new Set(), dismissed: false, answers: new Map(), at: new Map(), reached: new Map() }));\n return p;\n };\n\n let dirty = false;\n const record = (props: Record<string, unknown>) => {\n env.track(ONBOARDING_EVENT, props);\n dirty = true;\n };\n const markDone = (p: Progress, id: string) => {\n p.done.add(id);\n p.at.set(id, now());\n };\n\n /** A condition's answer for this person, or undefined while what it asks is not known yet. */\n const decide = (c: OnboardingCondition): boolean | undefined => {\n if (c.page?.trim()) {\n const path = env.path?.();\n return path === undefined ? false : pageMatches(c.page, path);\n }\n if (c.event?.trim()) {\n // The person's events from MRRocket arrive with their progress: before that, wait.\n if (!known) return undefined;\n for (const e of seenEvents) if (matchesTrigger(c.event, e)) return true;\n return false;\n }\n return false;\n };\n\n /**\n * Where the person is in a line of the flow. `live`: it is their flow now, so reaching a wait starts it, a decided\n * condition is kept and both are recorded; otherwise nothing changes (to ask whether a flow is finished).\n */\n const walkLine = (flow: OnboardingFlow, line: OnboardingNode[], p: Progress, live: boolean, before = 0, last?: number): Walked => {\n for (let i = 0; i < line.length; i++) {\n const n = line[i];\n if (n.kind === \"wait\") {\n if (p.done.has(n.id)) {\n last = p.at.get(n.id) ?? last;\n continue;\n }\n let since = p.reached.get(n.id);\n if (since === undefined) {\n if (!live) return { at: \"pending\" };\n // Counted from when the step before was done; a flow that opens with a wait counts from now.\n since = last ?? now();\n p.reached.set(n.id, since);\n record({ flow: flow.id, step: n.id, kind: \"wait\", action: \"shown\" });\n }\n const until = since + waitMs(n);\n if (now() < until) return { at: \"wait\", until };\n if (!live) return { at: \"pending\" };\n markDone(p, n.id);\n record({ flow: flow.id, step: n.id, kind: \"wait\", action: \"completed\" });\n last = until;\n continue;\n }\n if (n.kind === \"condition\") {\n let answer = p.answers.get(n.id);\n if (answer === undefined) {\n if (!live) return { at: \"pending\" };\n answer = decide(n);\n if (answer === undefined) return { at: \"pending\" };\n p.answers.set(n.id, answer);\n record({ flow: flow.id, step: n.id, kind: \"condition\", action: \"answered\", answer: answer ? \"yes\" : \"no\" });\n }\n return walkLine(flow, answer ? n.yes : n.no, p, live, before, last);\n }\n if (!p.done.has(n.id)) return { at: \"step\", step: n, line, index: i, before };\n last = p.at.get(n.id) ?? last;\n before++;\n }\n return { at: \"end\" };\n };\n\n const finished = (f: OnboardingFlow) => {\n const p = progress.get(f.id);\n return !!p && (p.dismissed || walkLine(f, nodesOf(f), p, false).at === \"end\");\n };\n\n const keyOf = (pid: string | undefined) => `${PROGRESS_KEY}:${pid ?? \"visitor\"}`;\n const eventsKeyOf = (pid: string | undefined) => `${PROGRESS_KEY}:events:${pid ?? \"visitor\"}`;\n const answersKeyOf = (pid: string | undefined) => `${PROGRESS_KEY}:answers:${pid ?? \"visitor\"}`;\n\n /** 0.18.0: an item the person just finished (not closed) plays its finish animation, once per page. */\n const celebrate = (f: OnboardingFlow) => {\n if (!f.finish || f.finish === \"none\" || f.shownBy === \"code\" || celebrated.has(f.id)) return;\n const p = progress.get(f.id);\n if (!p || p.dismissed || walkLine(f, nodesOf(f), p, false).at !== \"end\") return;\n celebrated.add(f.id);\n finaleNow = { kind: f.finish, key: `${f.id}:${now()}` };\n };\n\n const save = () => {\n dirty = false;\n const store = env.storage?.();\n if (!store) return;\n const out: Record<string, FlowProgress> = {};\n for (const [id, p] of progress)\n out[id] = {\n done: [...p.done],\n ...(p.dismissed ? { dismissed: true } : {}),\n ...(p.answers.size ? { answers: Object.fromEntries([...p.answers].map(([k, v]) => [k, v ? \"yes\" : \"no\"])) } : {}),\n ...(p.at.size ? { at: Object.fromEntries(p.at) } : {}),\n ...(p.reached.size ? { reached: Object.fromEntries(p.reached) } : {}),\n };\n try {\n store.set(keyOf(env.personId()), JSON.stringify(out));\n if (answers.size) store.set(answersKeyOf(env.personId()), JSON.stringify(Object.fromEntries(answers)));\n if (seenEvents.size) store.set(eventsKeyOf(env.personId()), JSON.stringify([...seenEvents].slice(-200)));\n } catch {\n // Storage full or gone: progress stays in memory.\n }\n };\n\n const merge = (state: InAppState[\"onboarding\"] | undefined) => {\n for (const [id, s] of Object.entries(state ?? {})) {\n const p = prog(id);\n for (const d of s.done ?? []) p.done.add(d);\n if (s.dismissed) p.dismissed = true;\n // A condition decided elsewhere keeps its path; the earliest time wins for waits.\n for (const [k, v] of Object.entries(s.answers ?? {})) if (!p.answers.has(k)) p.answers.set(k, v === \"yes\");\n for (const [k, v] of Object.entries(s.at ?? {})) if (typeof v === \"number\" && !p.at.has(k)) p.at.set(k, v);\n for (const [k, v] of Object.entries(s.reached ?? {})) if (typeof v === \"number\" && (!p.reached.has(k) || v < p.reached.get(k)!)) p.reached.set(k, v);\n }\n };\n\n // ---------- What shows ----------\n\n /** Comes back when a short wait is over, so the next step shows without a reload (long waits wait for the next visit). */\n const wakeAt = (until: number) => {\n if (timer) clearTimeout(timer);\n timer = null;\n const ms = until - now();\n if (ms > 0 && ms <= 86_400_000) {\n timer = setTimeout(() => ((timer = null), emit()), ms + 50);\n // Never what keeps a process alive (tests, server rendering).\n (timer as { unref?: () => void }).unref?.();\n }\n };\n\n /** The active flow waits (a wait step, or a condition whose answer is not known yet): it stays the active one. */\n let holding = false;\n\n const onboardingView = (): FlowView | null => {\n holding = false;\n const flow = flowById(active);\n if (!flow) return null;\n const p = prog(flow.id);\n if (p.dismissed) return null;\n const w = walkLine(flow, nodesOf(flow), p, true);\n if (dirty) save();\n if (w.at === \"wait\") wakeAt(w.until);\n holding = w.at === \"wait\" || w.at === \"pending\";\n if (w.at !== \"step\") return null;\n const { step, line, index } = w;\n const isStep = (n: OnboardingNode | undefined): n is OnboardingStep => !!n && n.kind !== \"wait\" && n.kind !== \"condition\";\n if (step.kind === \"checklist\") {\n let from = index;\n while (from > 0 && isStep(line[from - 1]) && line[from - 1].kind === \"checklist\") from--;\n let to = index;\n while (to + 1 < line.length && isStep(line[to + 1]) && line[to + 1].kind === \"checklist\") to++;\n const items = line.slice(from, to + 1).filter(isStep);\n for (const i of items)\n if (!p.done.has(i.id) && !shown.has(`s:${i.id}`)) {\n shown.add(`s:${i.id}`);\n env.track(ONBOARDING_EVENT, { flow: flow.id, step: i.id, kind: \"checklist\", action: \"shown\" });\n }\n return {\n type: \"checklist\",\n flow,\n items: items.map((s) => {\n // 0.18.0: what the task opens; a tour or modal only when the config has it and the SDK draws it.\n const o = opensOf(s);\n const t = o && (o.kind === \"tour\" || o.kind === \"modal\") && o.id !== flow.id ? flowById(o.id) : undefined;\n const target = t && t.shownBy !== \"code\" && nodesOf(t).length ? t : undefined;\n const opens = o && (o.kind === \"page\" ? !!o.target.trim() : o.kind === \"code\" || !!target) ? o : undefined;\n return {\n step: s,\n done: p.done.has(s.id),\n ...(target && o?.kind === \"tour\" ? { tour: target } : {}),\n ...(opens ? { opens } : {}),\n ...(target ? { target } : {}),\n };\n }),\n };\n }\n if (!shown.has(`s:${step.id}`)) {\n shown.add(`s:${step.id}`);\n env.track(ONBOARDING_EVENT, { flow: flow.id, step: step.id, kind: step.kind, action: \"shown\" });\n }\n // The dots: the steps behind on the person's path, this one, and the ones left on its line before any fork.\n const rest = line.slice(index + 1);\n const fork = rest.findIndex((n) => n.kind === \"condition\");\n const ahead = (fork < 0 ? rest : rest.slice(0, fork)).filter(isStep).length;\n return {\n type: \"step\",\n flow,\n step,\n index: w.before,\n total: w.before + 1 + ahead,\n last: !ahead && fork < 0,\n // 0.18.0: a question's answer so far, and whether there is one before it to go back to.\n ...(step.kind === \"question\" ? { answer: drafts.get(step.id) ?? (step.question ? answers.get(step.question.key) : undefined), back: w.before > 0 } : {}),\n };\n };\n\n const view = (): FlowView | null => {\n if (cancel) {\n const c = cancel;\n const followUp = c.flow.followUps?.[c.reason!];\n // The dots: the survey, a follow-up, one or two offers, the confirmation.\n const f = followUp ? 1 : 0;\n const steps = c.plain ? 1 : 3 + f + (c.flow.second?.[c.reason!] ? 1 : 0);\n return {\n type: \"cancel\",\n screen: c.screen,\n flow: c.flow,\n reason: c.reason,\n ...(c.screen === \"followup\" && followUp ? { followUp, answer: c.answer } : {}),\n offer: c.screen === \"offer\" ? c.offer : undefined,\n ...(c.screen === \"offer\" ? { round: c.round } : {}),\n price: c.options.price,\n busy: c.busy,\n error: c.error,\n step: c.screen === \"survey\" ? 1 : c.screen === \"followup\" ? 2 : c.screen === \"offer\" ? 1 + f + c.round : steps,\n steps,\n };\n }\n const ob = onboardingView();\n if (ob) return ob;\n // A flow that just ended lets the next thing show; one that waits keeps its place.\n if (active && !holding) {\n const back = returnTo;\n const ended = active;\n returnTo = null;\n active = null;\n const list = back ? flowById(back.flow) : undefined;\n if (back && list) {\n // The tour a checklist item started is over: the checklist comes back, and a finished tour ticks an item\n // that has no event of its own (a closed one does not).\n const item = stepsIn(nodesOf(list)).find((s) => s.id === back.step);\n const p = prog(list.id);\n if (item && !item.event && !progress.get(ended)?.dismissed && !p.done.has(item.id)) {\n markDone(p, item.id);\n record({ flow: list.id, step: item.id, kind: \"checklist\", action: \"completed\" });\n celebrate(list);\n }\n active = list.id;\n const again = onboardingView();\n if (again) return again;\n if (!holding) active = null;\n } else if (known) {\n // 0.17.0: the next flow whose moment has come (another \"First launch\", this page's, or one of the same\n // trigger as the one that ended) follows right away.\n const was = flowById(ended);\n const after = was ? momentOf(was) : undefined;\n Promise.resolve().then(() => autoStart(after));\n }\n }\n const m = messages.find((x) => !handled.has(x.id));\n return m ? { type: \"message\", message: m } : null;\n };\n\n /** Tells the renderers, and records what became visible (once per step or message per page load). */\n const emit = () => {\n // Onboarding steps record their \"shown\" where they are worked out (onboardingView), so a step a wait let through\n // counts also when nothing else changed.\n const v = view();\n if (v?.type === \"message\" && !shown.has(`m:${v.message.id}`)) {\n shown.add(`m:${v.message.id}`);\n env.track(INAPP_EVENT, { message: v.message.id, journey: v.message.journey, action: \"shown\" });\n }\n for (const l of [...listeners]) {\n try {\n l();\n } catch {\n // One renderer must not stop another.\n }\n }\n };\n\n const start = (flow: OnboardingFlow) => {\n if (active === flow.id) return true;\n if (active && flowById(active) && !finished(flowById(active)!)) return false;\n active = flow.id;\n env.track(ONBOARDING_EVENT, { flow: flow.id, action: \"started\" });\n emit();\n return true;\n };\n\n /**\n * 0.17.0: a flow for signed-in people starts on its own only for someone the app identified; 0.18.0: one for the\n * people who gave an answer only for them.\n */\n const allowed = (f: OnboardingFlow) =>\n f.audience === \"answered\" ? !!f.answered && answers.get(f.answered.question) === f.answered.option : f.audience !== \"signed_in\" || !!env.personId();\n\n /** 0.18.0: an \"after:<id>\" item's moment: that item ended (finished or closed), or it is not live (then like the first launch). */\n const predecessorEnded = (id: string) => {\n const g = flowById(id);\n return !g || finished(g);\n };\n\n /** Whether the item's own moment has come now (its trigger), apart from who it is for. */\n const due = (f: OnboardingFlow, trigger = momentOf(f)) => {\n const path = env.path?.();\n const pred = afterOf(trigger);\n if (pred) return predecessorEnded(pred);\n if (manual(trigger)) return false;\n if (isPageTrigger(trigger)) return path !== undefined && pageMatches(trigger, path);\n if (startsOnLoad(trigger)) return true;\n for (const e of seenEvents) if (matchesTrigger(trigger, e)) return true;\n return false;\n };\n\n /**\n * Flows whose trigger is \"the first launch\" (or, 0.17.0, the page the person is on) start once their progress is\n * known (never twice for a done flow). `after`: the trigger of a flow that just ended, whose next unfinished flow\n * follows it (onboarding items made from one older flow share its trigger).\n */\n const autoStart = (after?: string) => {\n if (!auto || active || !known) return;\n // Without an id or storage a \"first launch\" could not be told from any other: no automatic start then.\n if (!env.personId() && !env.storage?.()) return;\n const path = env.path?.();\n const same = after && !manual(after) ? eventSlug(after) : \"\";\n const next = drawn().find(\n (f) =>\n (afterOf(momentOf(f))\n ? predecessorEnded(afterOf(momentOf(f))!)\n : isPageTrigger(momentOf(f))\n ? path !== undefined && pageMatches(momentOf(f), path)\n : startsOnLoad(momentOf(f)) || (!!same && eventSlug(momentOf(f)) === same)) &&\n allowed(f) &&\n nodesOf(f).length &&\n !finished(f),\n );\n if (next) start(next);\n };\n\n /**\n * 0.17.0/0.18.0: a checklist task opens another item (a tour or a modal): from its first step, also when the person\n * saw or closed it before; the checklist comes back when it ends.\n */\n const openItem = (list: OnboardingFlow, stepId: string, target: OnboardingFlow) => {\n const p = prog(target.id);\n p.done.clear();\n p.at.clear();\n p.dismissed = false;\n returnTo = { flow: list.id, step: stepId };\n active = target.id;\n env.track(ONBOARDING_EVENT, { flow: target.id, action: \"started\", from: list.id });\n save();\n emit();\n };\n\n /** 0.18.0: one question answered: kept for the person (`answers`), done, and recorded with its key and words. */\n const answerStep = (flow: OnboardingFlow, step: OnboardingStep, raw: string) => {\n const q = step.question!;\n const value = q.type === \"text\" ? raw.trim().slice(0, 500) : raw;\n answers.set(q.key, value);\n drafts.delete(step.id);\n markDone(prog(flow.id), step.id);\n env.track(ONBOARDING_EVENT, { flow: flow.id, step: step.id, kind: \"question\", action: \"completed\", question: q.key, answer: value, label: answerLabel(q, value) });\n celebrate(flow);\n save();\n };\n\n /** 0.18.0: something changed on an item the app draws: saved, and the next item may follow it. */\n const ended = (f: OnboardingFlow) => {\n save();\n if (finished(f)) Promise.resolve().then(() => autoStart());\n emit();\n };\n\n const refresh = () => {\n const pid = env.personId();\n // Another person on this device: nothing of the one before carries over.\n if (progressFor !== null && progressFor !== pid) {\n progress.clear();\n seenEvents.clear();\n answers.clear();\n drafts.clear();\n handled.clear();\n active = null;\n returnTo = null;\n loadedFor = undefined;\n }\n progressFor = pid;\n const run = (async () => {\n const store = env.storage?.();\n if (store) {\n try {\n const raw = await store.get(keyOf(pid));\n if (raw) merge(JSON.parse(raw) as InAppState[\"onboarding\"]);\n const seen = await store.get(eventsKeyOf(pid));\n if (seen) for (const e of JSON.parse(seen) as string[]) if (typeof e === \"string\") seenEvents.add(e);\n const said = await store.get(answersKeyOf(pid));\n if (said) for (const [k, v] of Object.entries(JSON.parse(said) as Record<string, unknown>)) if (typeof v === \"string\") answers.set(k, v);\n } catch {\n // A damaged entry is ignored.\n }\n }\n const wants = flows().length > 0 || !!config?.messages;\n if (pid && wants && loadedFor !== pid) {\n loadedFor = pid;\n const state = await env.fetchInApp(pid).catch(() => null);\n if (state && env.personId() === pid) {\n merge(state.onboarding);\n for (const e of state.events ?? []) if (typeof e === \"string\") seenEvents.add(eventSlug(e));\n // 0.18.0: answers given on another device; an answer given here stays.\n for (const [k, v] of Object.entries(state.answers ?? {})) if (typeof v === \"string\" && !answers.has(k)) answers.set(k, v);\n messages = (state.messages ?? []).filter((m) => m && m.id && m.title);\n }\n }\n if (!pid) messages = [];\n known = true;\n autoStart();\n emit();\n })();\n loading = run.finally(() => {\n if (loading === run) loading = null;\n });\n return loading;\n };\n\n // ---------- Cancel flow ----------\n\n /** Which offer of the answer was on screen, for MRRocket to know what was saved: a rule's or the second. */\n const offerAt = (c: CancelRun) => (c.round > 1 ? { round: 2 } : c.rule! >= 0 ? { rule: c.rule } : {});\n\n /** Into the offer screen with the answer's first offer (the first rule that holds, else its own), or the confirmation. */\n const toOffer = (c: CancelRun) => {\n const who = { answer: c.answer, plan: c.options.plan, months: monthsSince(c.options.customerSince, env.now?.()) };\n const rules = c.flow.rules?.[c.reason!] ?? [];\n c.rule = rules.findIndex((r) => ruleMatches(r, who));\n c.offer = c.rule < 0 ? c.flow.offers[c.reason!] : (rules[c.rule].offer ?? undefined);\n c.round = 1;\n c.screen = c.offer ? \"offer\" : \"confirm\";\n };\n\n const result = (c: CancelRun, outcome: CancelFlowResult[\"outcome\"], comment?: string): CancelFlowResult => {\n const offer = c.screen === \"offer\" ? c.offer : undefined;\n return {\n outcome,\n run: c.run,\n ...(c.reason ? { reason: c.reason, reasonLabel: c.flow.reasons.find((r) => r.id === c.reason)?.label } : {}),\n ...(c.answer ? { answer: c.answer } : {}),\n ...(outcome === \"saved\" && offer ? { offer } : {}),\n ...(comment?.trim() ? { comment: comment.trim().slice(0, 500) } : {}),\n };\n };\n\n const finish = (c: CancelRun, r: CancelFlowResult) => {\n env.track(CANCEL_EVENT, {\n action: \"finished\",\n run: c.run,\n version: c.flow.version,\n outcome: r.outcome,\n customer: c.options.customer ?? \"consumer\",\n ...(r.reason ? { reason: r.reason } : {}),\n ...(r.answer ? { answer: r.answer } : {}),\n ...(r.offer ? { offer: r.offer.kind, ...offerAt(c) } : {}),\n ...(r.comment ? { comment: r.comment } : {}),\n });\n if (cancel === c) cancel = null;\n c.resolve(r);\n emit();\n };\n\n /** Runs the app's callback; the outcome is recorded only once it went through. */\n const settle = (c: CancelRun, outcome: \"saved\" | \"cancelled\", comment?: string) => {\n if (c.busy) return;\n const r = result(c, outcome, comment);\n const fn = outcome === \"saved\" ? c.options.onSave : c.options.onCancel;\n c.busy = true;\n c.error = undefined;\n emit();\n Promise.resolve()\n .then(() => fn?.(r))\n .then(\n () => finish(c, r),\n () => {\n c.busy = false;\n c.error = FAILED;\n emit();\n },\n );\n };\n\n const openCancelFlow = (options: CancelFlowOptions = {}) => {\n if (cancel) return cancel.promise;\n const flow = config?.cancelFlow ?? PLAIN_CANCEL;\n const plain = options.customer !== \"business\" || flow.reasons.length < 2;\n let resolve: (r: CancelFlowResult) => void = () => {};\n const promise = new Promise<CancelFlowResult>((r) => (resolve = r));\n const c: CancelRun = { flow, options, plain, screen: plain ? \"confirm\" : \"survey\", round: 1, run: runId(), busy: false, resolve, promise };\n cancel = c;\n env.track(CANCEL_EVENT, { action: \"shown\", run: c.run, version: flow.version, customer: options.customer ?? \"consumer\", plain });\n emit();\n return promise;\n };\n\n const controller: FlowsController = {\n setConfig: (c) => {\n config = c;\n if (active && !flowById(active)) active = null;\n emit();\n },\n refresh,\n onEvent: (name) => {\n // 0.17.0: a page view may be the moment of a flow with a page trigger.\n if (name === \"$pageview\") autoStart();\n if (!name || name.startsWith(\"$\")) return;\n // Remembered for \"Did <event> happen?\" conditions the person reaches later.\n const slug = eventSlug(name);\n const fresh = !!slug && !seenEvents.has(slug);\n if (fresh) seenEvents.add(slug);\n const flow = flowById(active);\n let ticked = false;\n // The checklist on screen, and (0.18.0) those the app draws itself, tick their tasks by their event.\n const lists = [...(flow ? [flow] : []), ...flows().filter((f) => f.shownBy === \"code\" && f.id !== flow?.id)];\n for (const l of lists) {\n const p = prog(l.id);\n let here = false;\n for (const s of stepsIn(nodesOf(l)))\n if (s.kind === \"checklist\" && s.event && !p.done.has(s.id) && matchesTrigger(s.event, name)) {\n markDone(p, s.id);\n here = true;\n env.track(ONBOARDING_EVENT, { flow: l.id, step: s.id, kind: s.kind, action: \"completed\" });\n }\n if (here) {\n ticked = true;\n celebrate(l);\n }\n }\n if (ticked || (fresh && (flow || lists.length))) {\n save();\n emit();\n } else if (fresh) save();\n // Like the first-launch start: only for someone whose progress is kept, so nobody gets a flow twice.\n if (!auto || (!env.personId() && !env.storage?.())) return;\n const next = drawn().find((f) => nodesOf(f).length && !manual(momentOf(f)) && matchesTrigger(momentOf(f), name) && allowed(f) && !finished(f));\n if (next) start(next);\n },\n openCancelFlow,\n startOnboarding: (id) => {\n const f = id ? flowById(id) : drawn().find((x) => nodesOf(x).length && !manual(momentOf(x)) && !afterOf(momentOf(x)) && !finished(x));\n // An item the app draws itself (0.18.0) is never started here.\n if (!f || !nodesOf(f).length || f.shownBy === \"code\") return false;\n const p = prog(f.id);\n // Started by hand: a flow the person closed before shows again.\n p.dismissed = false;\n return start(f);\n },\n view,\n subscribe: (l) => {\n listeners.add(l);\n return () => listeners.delete(l);\n },\n cancel: {\n pick: (reason) => {\n if (!cancel || cancel.busy || cancel.screen !== \"survey\" || !cancel.flow.reasons.some((r) => r.id === reason)) return;\n if (cancel.reason !== reason) cancel.answer = undefined;\n cancel.reason = reason;\n cancel.error = undefined;\n emit();\n },\n answer: (text) => {\n const c = cancel;\n if (!c || c.busy || c.screen !== \"followup\") return;\n const t = text.slice(0, 500);\n c.answer = t.trim() ? t : undefined;\n emit();\n },\n next: () => {\n const c = cancel;\n if (!c || !c.reason) return;\n // The follow-up's answer rides along on the finished event.\n if (c.screen === \"survey\") {\n env.track(CANCEL_EVENT, { action: \"answered\", run: c.run, version: c.flow.version, reason: c.reason });\n if (c.flow.followUps?.[c.reason]) c.screen = \"followup\";\n else toOffer(c);\n } else if (c.screen === \"followup\") {\n c.answer = c.answer?.trim();\n toOffer(c);\n } else return;\n emit();\n },\n accept: (comment) => {\n const c = cancel;\n if (!c || c.screen !== \"offer\" || !c.reason) return;\n if (!c.busy)\n env.track(CANCEL_EVENT, {\n action: \"offer\",\n run: c.run,\n version: c.flow.version,\n reason: c.reason,\n offer: c.offer?.kind,\n ...offerAt(c),\n outcome: \"accepted\",\n });\n settle(c, \"saved\", comment);\n },\n decline: () => {\n const c = cancel;\n if (!c || c.screen !== \"offer\" || c.busy) return;\n env.track(CANCEL_EVENT, {\n action: \"offer\",\n run: c.run,\n version: c.flow.version,\n reason: c.reason,\n offer: c.offer?.kind,\n ...offerAt(c),\n outcome: \"declined\",\n });\n // A second offer once, then the confirmation.\n const second = c.round === 1 && c.reason ? c.flow.second?.[c.reason] : undefined;\n if (second) {\n c.offer = second;\n c.rule = undefined;\n c.round = 2;\n } else c.screen = \"confirm\";\n c.error = undefined;\n emit();\n },\n confirm: (comment) => {\n const c = cancel;\n if (!c || c.screen !== \"confirm\") return;\n settle(c, \"cancelled\", comment);\n },\n close: () => {\n const c = cancel;\n if (!c || c.busy) return;\n const r = result(c, \"kept\");\n c.options.onClose?.(r);\n finish(c, r);\n },\n },\n onboarding: {\n next: () => {\n const v = onboardingView();\n if (v?.type !== \"step\") return;\n if (v.step.kind === \"question\") {\n // 0.18.0: only with an answer; it is kept for the person and goes to MRRocket with the step.\n const q = v.step.question;\n const a = drafts.get(v.step.id) ?? (q ? answers.get(q.key) : undefined);\n if (!q || !answerOk(q, a)) return;\n answerStep(v.flow, v.step, a!);\n emit();\n return;\n }\n markDone(prog(v.flow.id), v.step.id);\n env.track(ONBOARDING_EVENT, { flow: v.flow.id, step: v.step.id, kind: v.step.kind, action: \"completed\" });\n celebrate(v.flow);\n save();\n emit();\n // 0.17.0: a modal's button may lead somewhere.\n const url = v.step.kind === \"modal\" ? safeUrl(v.step.url) : undefined;\n if (url) env.openUrl?.(url);\n },\n check: (stepId) => {\n const v = onboardingView();\n if (v?.type !== \"checklist\") return;\n const item = v.items.find((i) => i.step.id === stepId);\n if (!item || item.done) return;\n markDone(prog(v.flow.id), stepId);\n env.track(ONBOARDING_EVENT, { flow: v.flow.id, step: stepId, kind: \"checklist\", action: \"completed\" });\n celebrate(v.flow);\n save();\n emit();\n },\n dismiss: () => {\n const v = onboardingView();\n if (!v || (v.type !== \"step\" && v.type !== \"checklist\")) return;\n prog(v.flow.id).dismissed = true;\n env.track(ONBOARDING_EVENT, { flow: v.flow.id, ...(v.type === \"step\" ? { step: v.step.id } : {}), action: \"dismissed\" });\n // A tour started from a checklist was closed: the checklist comes back, its item stays open.\n active = returnTo && returnTo.flow !== v.flow.id && flowById(returnTo.flow) ? returnTo.flow : null;\n returnTo = null;\n save();\n emit();\n },\n startTour: (stepId) => {\n const v = onboardingView();\n if (v?.type !== \"checklist\") return false;\n const target = v.items.find((i) => i.step.id === stepId)?.target;\n if (!target) return false;\n openItem(v.flow, stepId, target);\n return true;\n },\n open: (stepId) => {\n const v = onboardingView();\n if (v?.type !== \"checklist\") return false;\n const it = v.items.find((i) => i.step.id === stepId);\n const o = it?.opens;\n if (!it || !o) return false;\n env.track(ONBOARDING_EVENT, { flow: v.flow.id, step: stepId, kind: \"checklist\", action: \"opened\", opens: o.kind });\n if ((o.kind === \"tour\" || o.kind === \"modal\") && it.target) {\n openItem(v.flow, stepId, it.target);\n return true;\n }\n // A page or the app's code: a task without an event of its own is done once it is opened (saved before a\n // page change can unload this one).\n if (!it.done && !it.step.event) {\n markDone(prog(v.flow.id), stepId);\n env.track(ONBOARDING_EVENT, { flow: v.flow.id, step: stepId, kind: \"checklist\", action: \"completed\" });\n celebrate(v.flow);\n }\n save();\n emit();\n if (o.kind === \"page\") env.navigate?.(o.target.trim());\n else env.task?.(stepId, v.flow);\n return true;\n },\n answer: (value) => {\n const v = onboardingView();\n if (v?.type !== \"step\" || v.step.kind !== \"question\") return;\n drafts.set(v.step.id, String(value).slice(0, 500));\n emit();\n },\n back: () => {\n const v = onboardingView();\n if (v?.type !== \"step\" || v.step.kind !== \"question\" || !v.back) return;\n // The question before on the person's way through the item is open again, with its answer filled in.\n const line = stepsIn(nodesOf(v.flow));\n const at = line.findIndex((s) => s.id === v.step.id);\n const p = prog(v.flow.id);\n for (let i = at - 1; i >= 0; i--)\n if (p.done.has(line[i].id)) {\n p.done.delete(line[i].id);\n p.at.delete(line[i].id);\n break;\n }\n save();\n emit();\n },\n item: (id) => {\n const f = flowById(id);\n if (!f) return null;\n const p = progress.get(f.id);\n const steps = stepsIn(nodesOf(f)).map((s) => {\n const a = s.question ? answers.get(s.question.key) : undefined;\n return { ...s, done: !!p?.done.has(s.id), ...(a !== undefined ? { answer: a } : {}) };\n });\n const done = steps.length > 0 && steps.every((s) => s.done);\n const dismissed = !!p?.dismissed;\n return { item: f, steps, done, dismissed, eligible: !done && !dismissed && allowed(f) && due(f) };\n },\n seen: (id) => {\n const f = flowById(id);\n if (!f || f.shownBy !== \"code\" || !known) return;\n if (!shown.has(`f:${f.id}`)) {\n shown.add(`f:${f.id}`);\n env.track(ONBOARDING_EVENT, { flow: f.id, action: \"started\" });\n }\n const p = progress.get(f.id);\n for (const s of stepsIn(nodesOf(f)))\n if (!p?.done.has(s.id) && !shown.has(`s:${s.id}`)) {\n shown.add(`s:${s.id}`);\n env.track(ONBOARDING_EVENT, { flow: f.id, step: s.id, kind: s.kind, action: \"shown\" });\n // A tour or the questions show one step at a time: only the first open one counts as seen.\n if (s.kind !== \"checklist\") break;\n }\n },\n complete: (id, stepId) => {\n const f = flowById(id);\n const s = f && stepsIn(nodesOf(f)).find((x) => x.id === stepId);\n if (!f || !s || s.kind === \"question\") return;\n const p = prog(f.id);\n if (p.done.has(s.id)) return;\n markDone(p, s.id);\n env.track(ONBOARDING_EVENT, { flow: f.id, step: s.id, kind: s.kind, action: \"completed\" });\n ended(f);\n },\n dismissItem: (id) => {\n const f = flowById(id);\n if (!f) return;\n const p = prog(f.id);\n if (p.dismissed) return;\n p.dismissed = true;\n env.track(ONBOARDING_EVENT, { flow: f.id, action: \"dismissed\" });\n if (active === f.id) active = null;\n ended(f);\n },\n answerItem: (id, question, value) => {\n const f = flowById(id);\n const s = f && stepsIn(nodesOf(f)).find((x) => x.kind === \"question\" && (x.id === question || x.question?.key === question));\n if (!f || !s?.question || !answerOk(s.question, value)) return;\n answerStep(f, s, value);\n ended(f);\n },\n },\n finale: () => finaleNow,\n accent: () => config?.onboardingAccent,\n answers: () => Object.fromEntries(answers),\n message: {\n click: () => {\n const v = view();\n if (v?.type !== \"message\") return;\n handled.add(v.message.id);\n env.track(INAPP_EVENT, { message: v.message.id, journey: v.message.journey, action: \"clicked\" });\n const url = safeUrl(v.message.url);\n emit();\n if (url) env.openUrl?.(url);\n },\n dismiss: () => {\n const v = view();\n if (v?.type !== \"message\") return;\n handled.add(v.message.id);\n env.track(INAPP_EVENT, { message: v.message.id, journey: v.message.journey, action: \"dismissed\" });\n emit();\n },\n },\n };\n return controller;\n};\n"],"mappings":"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","names":["ended"]}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * 0.18.0: the SDK's own small line icons for checklist tasks and the heading of a modal or the questions
3
+ * (`FlowIcon { kind: "builtin", name }`). Drawn by the SDK itself (an inline SVG on the web, react-native-svg in apps,
4
+ * the emoji when an app has no react-native-svg), and by MRRocket's editor from this same list, so what the owner picks
5
+ * is what people see. Each is a 24×24 outline, stroked at 2 with round ends, in the text color or the accent.
6
+ */
7
+ export interface BuiltinIcon {
8
+ /** What the editor calls it. */
9
+ label: string;
10
+ /** Path data, drawn stroked (no fill), on a 24×24 grid. */
11
+ paths: string[];
12
+ /** Shown instead in an app without react-native-svg. */
13
+ emoji: string;
14
+ }
15
+ export declare const FLOW_ICONS: Record<string, BuiltinIcon>;
16
+ /** The built-in icons' names, in the editor's order. */
17
+ export declare const FLOW_ICON_NAMES: string[];
18
+ /** An icon as it may be shown: a known built-in, an emoji (up to 8 code points, so joined ones fit), or an http(s) picture; else undefined. */
19
+ export declare const iconOf: <T extends {
20
+ kind: string;
21
+ name?: string;
22
+ value?: string;
23
+ url?: string;
24
+ }>(icon: T | undefined) => T | undefined;
package/dist/icons.js ADDED
@@ -0,0 +1,61 @@
1
+ // src/icons.ts
2
+ var FLOW_ICONS = {
3
+ check: { label: "Check", paths: ["M5 12.5l4.5 4.5L19 7.5"], emoji: "\u2705" },
4
+ star: { label: "Star", paths: ["M12 3.5l2.6 5.3 5.9.9-4.3 4.1 1 5.8L12 16.9l-5.2 2.7 1-5.8-4.3-4.1 5.9-.9z"], emoji: "\u2B50" },
5
+ rocket: {
6
+ label: "Rocket",
7
+ paths: ["M12 3c3 2 4.5 5.5 4 10l-2 3h-4l-2-3c-.5-4.5 1-8 4-10z", "M8 13.5l-3 3 3.5.5", "M16 13.5l3 3-3.5.5", "M13.5 9.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0z", "M11 19.5v1.5M13 19.5v1.5"],
8
+ emoji: "\u{1F680}"
9
+ },
10
+ user: { label: "Person", paths: ["M15.5 8a3.5 3.5 0 1 1-7 0 3.5 3.5 0 0 1 7 0z", "M5 20c.8-3.5 3.6-5.5 7-5.5s6.2 2 7 5.5"], emoji: "\u{1F464}" },
11
+ users: {
12
+ label: "Team",
13
+ paths: ["M13 8a3 3 0 1 1-6 0 3 3 0 0 1 6 0z", "M3.5 19c.6-3 3-4.8 6.5-4.8s5.9 1.8 6.5 4.8", "M16 5.2a3 3 0 0 1 0 5.6", "M18 14.6c1.5.6 2.4 2 2.7 4.4"],
14
+ emoji: "\u{1F465}"
15
+ },
16
+ settings: {
17
+ label: "Settings",
18
+ paths: ["M12 2.8v2.4M12 18.8v2.4M2.8 12h2.4M18.8 12h2.4M5.5 5.5l1.7 1.7M16.8 16.8l1.7 1.7M5.5 18.5l1.7-1.7M16.8 7.2l1.7-1.7", "M17 12a5 5 0 1 1-10 0 5 5 0 0 1 10 0z", "M14 12a2 2 0 1 1-4 0 2 2 0 0 1 4 0z"],
19
+ emoji: "\u2699\uFE0F"
20
+ },
21
+ bell: { label: "Bell", paths: ["M6 16v-5a6 6 0 0 1 12 0v5l1.5 2h-15z", "M10 20.5a2 2 0 0 0 4 0"], emoji: "\u{1F514}" },
22
+ mail: { label: "Mail", paths: ["M4 6h16v12H4z", "M4 7l8 6 8-6"], emoji: "\u2709\uFE0F" },
23
+ chat: { label: "Chat", paths: ["M4 5h16v11H9l-5 4z"], emoji: "\u{1F4AC}" },
24
+ heart: { label: "Heart", paths: ["M12 20s-7-4.4-7-10a4 4 0 0 1 7-2.6A4 4 0 0 1 19 10c0 5.6-7 10-7 10z"], emoji: "\u2764\uFE0F" },
25
+ flag: { label: "Flag", paths: ["M5 21V4", "M5 4h11l-2 4 2 4H5"], emoji: "\u{1F6A9}" },
26
+ bolt: { label: "Bolt", paths: ["M13 3L5 13.5h6L10 21l8-10.5h-6z"], emoji: "\u26A1" },
27
+ book: { label: "Book", paths: ["M4 5.5C6.5 4 9.5 4 12 6c2.5-2 5.5-2 8-.5V19c-2.5-1.5-5.5-1.5-8 .5-2.5-2-5.5-2-8-.5z", "M12 6v13.5"], emoji: "\u{1F4D6}" },
28
+ calendar: { label: "Calendar", paths: ["M4 6h16v14H4z", "M4 10h16", "M8 3.5v4M16 3.5v4"], emoji: "\u{1F4C5}" },
29
+ camera: { label: "Camera", paths: ["M4 8h3.5L9 5.5h6L16.5 8H20v11H4z", "M15.5 13a3.5 3.5 0 1 1-7 0 3.5 3.5 0 0 1 7 0z"], emoji: "\u{1F4F7}" },
30
+ cart: { label: "Cart", paths: ["M3.5 4.5H6l2 10h10l2-7H7", "M9.5 19.5a1 1 0 1 1-2 0 1 1 0 0 1 2 0z", "M18.5 19.5a1 1 0 1 1-2 0 1 1 0 0 1 2 0z"], emoji: "\u{1F6D2}" },
31
+ chart: { label: "Chart", paths: ["M4 20h16", "M7 16v-4M12 16V8M17 16V9"], emoji: "\u{1F4CA}" },
32
+ gift: { label: "Gift", paths: ["M4 10h16v10H4z", "M3 7h18v3H3z", "M12 7v13", "M12 7c-1.5-3-5-3-5-1s3.5 1 5 1z", "M12 7c1.5-3 5-3 5-1s-3.5 1-5 1z"], emoji: "\u{1F381}" },
33
+ home: { label: "Home", paths: ["M4 11l8-7 8 7", "M6 9.5V20h12V9.5", "M10 20v-6h4v6"], emoji: "\u{1F3E0}" },
34
+ link: { label: "Link", paths: ["M10 14a4 4 0 0 0 5.7 0l3-3a4 4 0 0 0-5.7-5.7l-1 1", "M14 10a4 4 0 0 0-5.7 0l-3 3a4 4 0 0 0 5.7 5.7l1-1"], emoji: "\u{1F517}" },
35
+ lock: { label: "Lock", paths: ["M6 11h12v9H6z", "M8.5 11V8a3.5 3.5 0 0 1 7 0v3"], emoji: "\u{1F512}" },
36
+ pencil: { label: "Pencil", paths: ["M4 20l1-4L16 5l3 3L8 19z", "M14 7l3 3"], emoji: "\u270F\uFE0F" },
37
+ plus: { label: "Plus", paths: ["M12 5v14M5 12h14"], emoji: "\u2795" },
38
+ search: { label: "Search", paths: ["M16 10.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0z", "M14.5 14.5L20 20"], emoji: "\u{1F50D}" },
39
+ upload: { label: "Upload", paths: ["M12 15V4", "M7.5 8.5L12 4l4.5 4.5", "M4 15v5h16v-5"], emoji: "\u2B06\uFE0F" },
40
+ play: { label: "Play", paths: ["M8 5l11 7-11 7z"], emoji: "\u25B6\uFE0F" },
41
+ sparkle: { label: "Sparkle", paths: ["M12 3l1.8 5.2L19 10l-5.2 1.8L12 17l-1.8-5.2L5 10l5.2-1.8z", "M19 16l.7 2 2 .7-2 .7-.7 2-.7-2-2-.7 2-.7z"], emoji: "\u2728" },
42
+ globe: {
43
+ label: "Globe",
44
+ paths: ["M20 12a8 8 0 1 1-16 0 8 8 0 0 1 16 0z", "M4 12h16", "M12 4c2.2 2.2 3.3 4.9 3.3 8s-1.1 5.8-3.3 8c-2.2-2.2-3.3-4.9-3.3-8S9.8 6.2 12 4z"],
45
+ emoji: "\u{1F310}"
46
+ }
47
+ };
48
+ var FLOW_ICON_NAMES = Object.keys(FLOW_ICONS);
49
+ var iconOf = (icon) => {
50
+ if (!icon) return void 0;
51
+ if (icon.kind === "builtin") return icon.name && FLOW_ICONS[icon.name] ? icon : void 0;
52
+ if (icon.kind === "emoji") return icon.value && icon.value.trim() && [...icon.value.trim()].length <= 8 ? icon : void 0;
53
+ if (icon.kind === "image") return icon.url && /^https?:\/\//i.test(icon.url) ? icon : void 0;
54
+ return void 0;
55
+ };
56
+ export {
57
+ FLOW_ICONS,
58
+ FLOW_ICON_NAMES,
59
+ iconOf
60
+ };
61
+ //# sourceMappingURL=icons.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/icons.ts"],"sourcesContent":["/**\n * 0.18.0: the SDK's own small line icons for checklist tasks and the heading of a modal or the questions\n * (`FlowIcon { kind: \"builtin\", name }`). Drawn by the SDK itself (an inline SVG on the web, react-native-svg in apps,\n * the emoji when an app has no react-native-svg), and by MRRocket's editor from this same list, so what the owner picks\n * is what people see. Each is a 24×24 outline, stroked at 2 with round ends, in the text color or the accent.\n */\n\nexport interface BuiltinIcon {\n /** What the editor calls it. */\n label: string;\n /** Path data, drawn stroked (no fill), on a 24×24 grid. */\n paths: string[];\n /** Shown instead in an app without react-native-svg. */\n emoji: string;\n}\n\nexport const FLOW_ICONS: Record<string, BuiltinIcon> = {\n check: { label: \"Check\", paths: [\"M5 12.5l4.5 4.5L19 7.5\"], emoji: \"✅\" },\n star: { label: \"Star\", paths: [\"M12 3.5l2.6 5.3 5.9.9-4.3 4.1 1 5.8L12 16.9l-5.2 2.7 1-5.8-4.3-4.1 5.9-.9z\"], emoji: \"⭐\" },\n rocket: {\n label: \"Rocket\",\n paths: [\"M12 3c3 2 4.5 5.5 4 10l-2 3h-4l-2-3c-.5-4.5 1-8 4-10z\", \"M8 13.5l-3 3 3.5.5\", \"M16 13.5l3 3-3.5.5\", \"M13.5 9.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0z\", \"M11 19.5v1.5M13 19.5v1.5\"],\n emoji: \"🚀\",\n },\n user: { label: \"Person\", paths: [\"M15.5 8a3.5 3.5 0 1 1-7 0 3.5 3.5 0 0 1 7 0z\", \"M5 20c.8-3.5 3.6-5.5 7-5.5s6.2 2 7 5.5\"], emoji: \"👤\" },\n users: {\n label: \"Team\",\n paths: [\"M13 8a3 3 0 1 1-6 0 3 3 0 0 1 6 0z\", \"M3.5 19c.6-3 3-4.8 6.5-4.8s5.9 1.8 6.5 4.8\", \"M16 5.2a3 3 0 0 1 0 5.6\", \"M18 14.6c1.5.6 2.4 2 2.7 4.4\"],\n emoji: \"👥\",\n },\n settings: {\n label: \"Settings\",\n paths: [\"M12 2.8v2.4M12 18.8v2.4M2.8 12h2.4M18.8 12h2.4M5.5 5.5l1.7 1.7M16.8 16.8l1.7 1.7M5.5 18.5l1.7-1.7M16.8 7.2l1.7-1.7\", \"M17 12a5 5 0 1 1-10 0 5 5 0 0 1 10 0z\", \"M14 12a2 2 0 1 1-4 0 2 2 0 0 1 4 0z\"],\n emoji: \"⚙️\",\n },\n bell: { label: \"Bell\", paths: [\"M6 16v-5a6 6 0 0 1 12 0v5l1.5 2h-15z\", \"M10 20.5a2 2 0 0 0 4 0\"], emoji: \"🔔\" },\n mail: { label: \"Mail\", paths: [\"M4 6h16v12H4z\", \"M4 7l8 6 8-6\"], emoji: \"✉️\" },\n chat: { label: \"Chat\", paths: [\"M4 5h16v11H9l-5 4z\"], emoji: \"💬\" },\n heart: { label: \"Heart\", paths: [\"M12 20s-7-4.4-7-10a4 4 0 0 1 7-2.6A4 4 0 0 1 19 10c0 5.6-7 10-7 10z\"], emoji: \"❤️\" },\n flag: { label: \"Flag\", paths: [\"M5 21V4\", \"M5 4h11l-2 4 2 4H5\"], emoji: \"🚩\" },\n bolt: { label: \"Bolt\", paths: [\"M13 3L5 13.5h6L10 21l8-10.5h-6z\"], emoji: \"⚡\" },\n book: { label: \"Book\", paths: [\"M4 5.5C6.5 4 9.5 4 12 6c2.5-2 5.5-2 8-.5V19c-2.5-1.5-5.5-1.5-8 .5-2.5-2-5.5-2-8-.5z\", \"M12 6v13.5\"], emoji: \"📖\" },\n calendar: { label: \"Calendar\", paths: [\"M4 6h16v14H4z\", \"M4 10h16\", \"M8 3.5v4M16 3.5v4\"], emoji: \"📅\" },\n camera: { label: \"Camera\", paths: [\"M4 8h3.5L9 5.5h6L16.5 8H20v11H4z\", \"M15.5 13a3.5 3.5 0 1 1-7 0 3.5 3.5 0 0 1 7 0z\"], emoji: \"📷\" },\n cart: { label: \"Cart\", paths: [\"M3.5 4.5H6l2 10h10l2-7H7\", \"M9.5 19.5a1 1 0 1 1-2 0 1 1 0 0 1 2 0z\", \"M18.5 19.5a1 1 0 1 1-2 0 1 1 0 0 1 2 0z\"], emoji: \"🛒\" },\n chart: { label: \"Chart\", paths: [\"M4 20h16\", \"M7 16v-4M12 16V8M17 16V9\"], emoji: \"📊\" },\n gift: { label: \"Gift\", paths: [\"M4 10h16v10H4z\", \"M3 7h18v3H3z\", \"M12 7v13\", \"M12 7c-1.5-3-5-3-5-1s3.5 1 5 1z\", \"M12 7c1.5-3 5-3 5-1s-3.5 1-5 1z\"], emoji: \"🎁\" },\n home: { label: \"Home\", paths: [\"M4 11l8-7 8 7\", \"M6 9.5V20h12V9.5\", \"M10 20v-6h4v6\"], emoji: \"🏠\" },\n link: { label: \"Link\", paths: [\"M10 14a4 4 0 0 0 5.7 0l3-3a4 4 0 0 0-5.7-5.7l-1 1\", \"M14 10a4 4 0 0 0-5.7 0l-3 3a4 4 0 0 0 5.7 5.7l1-1\"], emoji: \"🔗\" },\n lock: { label: \"Lock\", paths: [\"M6 11h12v9H6z\", \"M8.5 11V8a3.5 3.5 0 0 1 7 0v3\"], emoji: \"🔒\" },\n pencil: { label: \"Pencil\", paths: [\"M4 20l1-4L16 5l3 3L8 19z\", \"M14 7l3 3\"], emoji: \"✏️\" },\n plus: { label: \"Plus\", paths: [\"M12 5v14M5 12h14\"], emoji: \"➕\" },\n search: { label: \"Search\", paths: [\"M16 10.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0z\", \"M14.5 14.5L20 20\"], emoji: \"🔍\" },\n upload: { label: \"Upload\", paths: [\"M12 15V4\", \"M7.5 8.5L12 4l4.5 4.5\", \"M4 15v5h16v-5\"], emoji: \"⬆️\" },\n play: { label: \"Play\", paths: [\"M8 5l11 7-11 7z\"], emoji: \"▶️\" },\n sparkle: { label: \"Sparkle\", paths: [\"M12 3l1.8 5.2L19 10l-5.2 1.8L12 17l-1.8-5.2L5 10l5.2-1.8z\", \"M19 16l.7 2 2 .7-2 .7-.7 2-.7-2-2-.7 2-.7z\"], emoji: \"✨\" },\n globe: {\n label: \"Globe\",\n paths: [\"M20 12a8 8 0 1 1-16 0 8 8 0 0 1 16 0z\", \"M4 12h16\", \"M12 4c2.2 2.2 3.3 4.9 3.3 8s-1.1 5.8-3.3 8c-2.2-2.2-3.3-4.9-3.3-8S9.8 6.2 12 4z\"],\n emoji: \"🌐\",\n },\n};\n\n/** The built-in icons' names, in the editor's order. */\nexport const FLOW_ICON_NAMES = Object.keys(FLOW_ICONS);\n\n/** An icon as it may be shown: a known built-in, an emoji (up to 8 code points, so joined ones fit), or an http(s) picture; else undefined. */\nexport const iconOf = <T extends { kind: string; name?: string; value?: string; url?: string }>(icon: T | undefined): T | undefined => {\n if (!icon) return undefined;\n if (icon.kind === \"builtin\") return icon.name && FLOW_ICONS[icon.name] ? icon : undefined;\n if (icon.kind === \"emoji\") return icon.value && icon.value.trim() && [...icon.value.trim()].length <= 8 ? icon : undefined;\n if (icon.kind === \"image\") return icon.url && /^https?:\\/\\//i.test(icon.url) ? icon : undefined;\n return undefined;\n};\n"],"mappings":"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","names":[]}
package/dist/index.d.ts CHANGED
@@ -1,9 +1,11 @@
1
- export { type ChangelogEntry, type ChatMessage, type ChatThread, type CockpitProviderProps, type CockpitUser, type FeedbackBoardInfo, type FeedbackBoardProps, type FeedbackComment, type FeedbackList, type FeedbackPost, type FeedbackRoadmap, type FeedbackWidgetProps, type MessengerProps, type PostStatus, type SurveyAnswer, type SurveyDefinition, type SurveyQuestion, type SurveyQuestionKind, CockpitProvider, Experiment, FeedbackBoard, FeedbackWidget, Messenger, Survey, Text, WIDGET_CSS, createApi, useCancelFlow, useCockpit, useFeedback, useFlag, useFlagVariant, useMessenger, usePaywall, usePush, useStyles, useVariant, } from "./browser.js";
1
+ export { type ChangelogEntry, type ChatMessage, type ChatThread, type CockpitProviderProps, type CockpitUser, type FeedbackBoardInfo, type FeedbackBoardProps, type FeedbackComment, type FeedbackList, type FeedbackPost, type FeedbackRoadmap, type FeedbackWidgetProps, type MessengerProps, type PostStatus, type SurveyAnswer, type SurveyDefinition, type SurveyQuestion, type SurveyQuestionKind, CockpitProvider, Experiment, FeedbackBoard, FeedbackWidget, Messenger, Survey, Text, WIDGET_CSS, createApi, useCancelFlow, useCockpit, useFeedback, useFlag, useFlagVariant, useMessenger, useOnboarding, usePaywall, usePush, useStyles, useVariant, } from "./browser.js";
2
2
  export type { PushResult, PushStatus, WebPushOptions } from "./push-web.js";
3
3
  /** `identify(id, props, { hash })` (0.16.0); the hash comes from `userHash()` in `@mrrocket/sdk/next/server`. */
4
4
  export type { IdentifyOptions } from "./identity.js";
5
5
  export { type CockpitConfig, type CockpitEvent, type ConsentChoice, type ConsentMode, type ReplayConfig, type ErrorReport, type ExperimentConfig, type FlagCondition, type FlagConditionSet, type FlagConfig, type FlagPerson, type RedirectConfig, type RedirectDecision, type VariantConfig, type VariantId, EMBED_PARAM, EXPOSURE_PARAM, SDK_VERSION, ID_COOKIE, PREVIEW_PARAM, SESSION_COOKIE, assign, evaluateFlag, parsePreview, previewHref, redirectFor, } from "./core.js";
6
- export { type CancelFlowConfig, type CancelFlowOptions, type CancelFlowResult, type CancelOffer, type InAppMessage, type OnboardingFlow, type OnboardingStep, type FlowMedia, type TourPlacement, videoEmbed, } from "./flows.js";
6
+ export { type CancelFlowConfig, type CancelFlowOptions, type CancelFlowResult, type CancelOffer, type InAppMessage, type OnboardingFlow, type OnboardingStep, type FlowMedia, type TourPlacement, type TaskOpens, type FlowIcon, type FinishAnimation, type OnboardingQuestion, type OnboardingItemState, videoEmbed, } from "./flows.js";
7
+ export type { UseOnboarding } from "./onboarding-state.js";
8
+ export { FLOW_ICONS, FLOW_ICON_NAMES, type BuiltinIcon } from "./icons.js";
7
9
  export type { FlowsTheme } from "./flows-ui.js";
8
10
  export { PAYWALL_EVENT, type PaywallConfig, type PaywallOptions, type PaywallPage, type PaywallPlan, type PaywallResult } from "./paywall.js";
9
11
  export { type AttributionMode, type CheckoutReferral, type Referral, REFERRAL_COOKIE, getReferral, readReferralCookie, referralForCheckout, referralLink, } from "./referral.js";
package/dist/index.js CHANGED
@@ -15,6 +15,7 @@ import {
15
15
  useFlag,
16
16
  useFlagVariant,
17
17
  useMessenger,
18
+ useOnboarding,
18
19
  usePaywall,
19
20
  usePush,
20
21
  useStyles,
@@ -36,6 +37,7 @@ import {
36
37
  import {
37
38
  videoEmbed
38
39
  } from "./flows.js";
40
+ import { FLOW_ICONS, FLOW_ICON_NAMES } from "./icons.js";
39
41
  import { PAYWALL_EVENT } from "./paywall.js";
40
42
  import {
41
43
  REFERRAL_COOKIE,
@@ -49,6 +51,8 @@ export {
49
51
  EMBED_PARAM,
50
52
  EXPOSURE_PARAM,
51
53
  Experiment,
54
+ FLOW_ICONS,
55
+ FLOW_ICON_NAMES,
52
56
  FeedbackBoard,
53
57
  FeedbackWidget,
54
58
  ID_COOKIE,
@@ -77,6 +81,7 @@ export {
77
81
  useFlag,
78
82
  useFlagVariant,
79
83
  useMessenger,
84
+ useOnboarding,
80
85
  usePaywall,
81
86
  usePush,
82
87
  useStyles,