# Changelog

All notable changes to this package. It follows semantic versioning; before 1.0 a minor version may change an API.

## 0.11.0 (not published yet)

- **Onboarding with waits and conditions:** a flow from MRRocket › Conversion › Onboarding may hold a **wait** ("1 day": the next step shows that long after the one before was done; a short wait comes back on its own, a long one at the next visit) and a **condition** that forks it into a Yes and a No path: "Did <event> happen?" (the person's events from MRRocket, plus what they did on this device) or "Is on <page>?" (the path when they reach it; `*` at the end for everything below). Decided once per person and kept with their progress; like a journey's branch it ends its line. New `$onboarding` actions: a wait records `shown` and `completed`, a condition `answered` with `answer: "yes" | "no"`. The config's `onboarding[].nodes` carries the whole flow; `steps` still holds what older versions may show (the steps before the first wait or condition). Apps have no page path, so "Is on <page>?" answers no there.
- **Tour steps point at any element:** a step's target may be a CSS selector (`#new-project`, `.toolbar > button:nth-of-type(2)`) as well as a `data-ck-target` name.
- **Pick on your site:** opened by MRRocket's editor in a frame (`?ck_embed=1&ck_pick=1&ck_origin=…`), the page outlines the element under the pointer and sends the clicked one's selector back to the editor (postMessage to that origin only). Nothing happens on a normal visit.
- New exports in `flows`: `nodesOf`, `waitMs`, `pageMatches`, the types `OnboardingNode`, `OnboardingWait`, `OnboardingCondition`, `FlowProgress`; `FlowsEnv` takes `path()` and `now()`.
- Version reported in event batches: `0.11.0`.

## 0.10.0

- **Messenger, new look (web and apps):** Home opens under a head in your color with the greeting large on it, then cards: the recent chat with the team's mark, "Send us a message" with how soon you reply, and help search with the top articles. The chat has a round send button in one rounded field, softer bubbles and the reply time in its head; the launcher is larger, turns into a cross when open and takes a dark text on a light color. React Native draws the same: line icons with the optional `react-native-svg` (text glyphs without it).
- **More from MRRocket › Support › Messenger:** `replyTime` ("minutes", "hours", "day") and, for apps, `appLauncher` (a chat button in the app) plus the side, the button text and Sound (a light haptic in apps), which React Native now reads too. What the code passes still wins.
- New in `api-client`: `replyLine(replyTime)` and `inkOn(color)`.
- Version reported in event batches: `0.10.0`.

## 0.9.0

- **Messenger, after Featurebase's:** `<Messenger />` (Next.js / React), the script tag (`data-messenger="true"`) and React Native (`<MRRocketProvider messenger={{ launcher: true }}>`, `useMessenger()`) show a chat bubble with three tabs: **Home** (greeting, the latest chat, help search, top articles), **Messages** (the chat with your team; it lands in MRRocket › Support, answered by you or the Customer experience agent) and **Help** (your published help articles from Support › Help center, by collection, searchable, read in the panel with "Did this answer your question?"). An answer that drew on an article links it.
- **New answers while closed:** a short sound (web, Web Audio; `sound={false}`), a count on the bubble and a preview card above it (`preview={false}`); in apps a banner at the top and a light haptic with the optional `expo-haptics`. While the visitor waits for an answer the messenger checks every 8 seconds for ten minutes, then every 30 seconds; with the panel open every 4 seconds.
- One messenger for the web: plain DOM in `messenger-ui.ts`, loaded with a dynamic import by `<Messenger />` and as a fifth file `sdk-messenger.js` by the script tag (`data-messenger-title`, `-greeting`, `-label`, `-position`, and "false" for `-help`, `-sound`, `-preview`, `-ask-email`; `mrrocket.messenger.open()` / `close()` / `toggle()`). `open()` takes `"home"`, `"messages"`, `"help"` or `{ article: id }`. New props `launcher`, `help`, `sound`, `preview`.
- **React Native:** the survey (shown a few seconds after start; `surveys={false}`), the feedback sheet (`useFeedback().open()`: top requests with votes, suggest one) and the messenger, drawn by `MRRocketProvider` like the flows. New on the client: `api()`, `getUser()`, `isSurveyDone()` / `markSurveyDone()` (kept on the device with "persistent" consent). Optional peer `expo-haptics`.
- API: `GET /api/sdk/articles?key&q`, `GET /api/sdk/articles/:id` (blocks, never HTML), `POST /api/sdk/articles/:id/rate`; `GET` / `POST /api/sdk/messages`. `createApi(...)` gained `articles(q)`, `article(id)`, `rateArticle(id, helpful)`, `chat()`, `sendChat({ body, email })`; chat messages may carry `articles`. The fetch client (`api-client`) and the stylesheet (`styles-css`) no longer import React.
- **Look from MRRocket:** color, light or dark, side, title, greeting, button text and which parts show come from MRRocket › Support › Messenger (`GET /api/sdk/messenger`), so they change without new code; what the site's code passes still wins. The launcher waits up to 2.5 seconds for them.
- **Tap heatmaps in apps:** `MRRocketProvider` sends taps as `$click` on the current screen (and `$rageclick` on three quick taps on one spot), so app screens show in Heatmaps like pages; the provider then wraps the app in one `View` with `flex: 1` (`heatmaps={false}` leaves it as it is). New `client.tap(x, y)` and option `heatmaps`.
- **Session recordings in apps:** with the person's consent (`replay` on `MRRocketProvider`, `setReplay(true)`) and the product's recordings on, the app sends a small JPEG of the screen about once a second while it changes (optional peer `react-native-view-shot`), with screens, taps, rage taps and errors on the timeline; MRRocket plays them in Sessions. No frame while the keyboard is open; views wrapped in `<MRRocketMask>` are painted black on the server before a frame is stored, and a frame whose masks cannot be measured is not taken. Sampled like the web; other sessions keep the last minute in memory and upload it on a rage tap or an error. At most 30 minutes per recording.
- `RageDetector` moved to its own module (`rage.ts`) so the apps share it.
- Version reported in event batches: `0.9.0`.

## 0.8.0

- **Texts in React Native:** `useText(id, fallback)` and `client.getText(id, fallback)` / `client.peekText(id)` / `client.exposeText(id)` return the copy published in MRRocket for `id` with the person's variant of a running text test on top, the same rules as `<Text id>` on the web (`textChanges`). Paywall copy (`paywall.<placement>.headline`, `.body`, `.cta`) and text A/B tests now work in apps; showing a text records `$exposure` for every test that changes it, once per screen. Works from the cached config on a cold start.
- Version reported in event batches: `0.8.0`.

## 0.7.0

- **Cancel flow:** `useCancelFlow().open({ customer, price, onCancel, onSave, onClose })` (Next.js and React Native), `mrrocket.cancelFlow.open(…)` (script tag) show the flow built in MRRocket › Churn › Cancel flow from the config's new `cancelFlow`: business customers get the survey and the offer for their answer (discount, pause, downgrade, talk to us), anyone else the plain confirmation (§ 312k BGB); without a live flow the plain confirmation, so the Cancel button always works. Resolves with `{ outcome: "cancelled" | "saved" | "kept", reason, offer, comment, run }`. MRRocket never changes a subscription: your `onCancel` / `onSave` do, and only after they succeed is the outcome recorded (`$cancel_flow`); MRRocket shows it under Churn as a cancellation or a save.
- **Onboarding:** the tour, checklist and modal steps of live onboarding flows (config `onboarding`) start on their own when the signed-in person (or a visitor with `consent="cookies"`) does the trigger (`track("Signed up")`, the first page for "First launch"), once per person; `startOnboarding()` starts one by hand, `onboarding={false}` switches the automatic start off. Tour steps point at `data-ck-target` elements on the web; checklist items tick themselves on their event. Progress comes back from MRRocket for signed-in people (`GET /api/sdk/inapp`) and records as `$onboarding`.
- **In-app messages:** journey steps of the kind "In-app message" show to the signed-in person as a banner or a dialog (`$inapp` shown, clicked, dismissed); the journey moves them on.
- **Look:** `appearance={{ theme, accent, className }}`, CSS variables `--ckf-*` on `.ckf-root`; script tag `data-theme`, `data-accent`. The web dialogs load lazily (a dynamic import; `sdk-flows.js` for the script tag).
- React Native: `MRRocketProvider` draws them (`flows={false}` to opt out), new `useCancelFlow`, `useOnboarding`, `MRRocketFlows`, `client.openCancelFlow`, `client.startOnboarding`, `client.flows`.
- **Push notifications** for Lifecycle journeys' push steps. React Native and Expo: `usePush().register()` / `client.registerForPush({ ask, projectId })` after `identify` asks the permission, gets the Expo push token (`expo-notifications`, new optional peer dependency with `expo-constants`) and sends it to `POST /api/sdk/push`; `reset()` takes the token back; opens of MRRocket notifications record `$push {action: "opened"}` and open the step's link when it has a scheme. Web: `usePush().subscribe()` (Next.js), `mrrocket.push.subscribe()` (script tag) asks the permission from the click, registers the service worker the site hosts (`/mrrocket-sw.js`, built as `dist/browser/mrrocket-sw.js`; `serviceWorker: false` for sites that import it into their own) and sends the subscription made with the workspace's VAPID key (`GET /api/sdk/push`); `unsubscribe()` on sign-out. The sign-up code loads lazily (a dynamic import; `sdk-push.js` for the script tag).
- **Redirects:** the config's `redirects` (page redirects) and redirect tests (an experiment with `path`, variants with `url`) send the visitor on before the page shows, keeping the query and hash; a running test adds `ck_x=<test>:<variant>`, and the page it lands on records the exposure and drops the parameter. New `redirectFor`, `exposureFrom`, `EXPOSURE_PARAM`; `experimentRedirect(request, config, { distinctId })` and a re-exported `getCockpitConfig` in `@mrrocket/sdk/next/proxy` redirect in `proxy.ts` before the page renders. The script tag removes the `ck-hide` class from `<html>` once it decided (the optional anti-flicker snippet).
- Version reported in event batches: `0.7.0`.

## 0.6.0

- **Feedback board after Featurebase:** `<FeedbackBoard>` gets a top bar with your product's logo and name (from MRRocket), Feedback · Roadmap · Changelog, search, a light/dark toggle and "Submit feedback"; post cards with type chip, excerpt, author and votes; the Roadmap has four columns, Backlog (under consideration), Next up, In progress and Done; a post page with comments, upvote and status; Changelog with dates beside the entries; the submit form and dialogs rise as a sheet on phones, the roadmap scrolls column by column. New props `header` (default true; `false` keeps the old plain heading), `syncUrl` (`?view=` and `?post=` in the address, so a post can be linked), `fullPage` and `initialTab`.
- **Who posts:** a visitor gives a name and email once before posting, voting or commenting; the board keeps them for this site in localStorage (`ckw-board-who:<key>`) so their votes stay theirs. A visitor your app called `identify()` for with a name or email is not asked. Others see "First L.". The floating widget asks the same way.
- API: posts carry `author`; the list carries `product` ({ name, logo }); the roadmap carries a `review` column; `createApi(...).post(id)` reads one post. Older servers still work (the new fields are optional).
- Fix: primary buttons, the floating button and tabs keep their own colors on pages whose CSS styles every `button` (the widget's reset no longer outranks its own classes).

## 0.5.0

- **Published texts:** the config carries `texts`, the copy published in MRRocket for texts the site marks: a landing page's headline, subheadline and button (`<Text id="pricing.headline">`, `data-ck-text="pricing.headline"`; `/` is `home`, `/features/ai` is `features-ai`) and a paywall's (`paywall.<placement>.headline`, `.body`, `.cta`). `textChanges` (and with it `<Text>`, `getTexts` and the script tag) applies them first and a running test's variant on top; without them the page keeps its own words. Older versions ignore the field.
- **Experiments are served:** MRRocket's config now lists the product's running tests with their variants (`control`, `a`, `b` …) and splits, and a shipped winner for everyone; nothing changes in how a visitor is assigned (a stable hash of test and visitor id).
- `<Text id>` records the exposure of every running test that changes that text, as the script tag already did, so a test that only swaps texts is counted without `useVariant`. New export `experimentsOfText`.
- Version reported in event batches: `0.5.0`.

## 0.4.0

- **React Native and Expo:** new entry `@mrrocket/sdk/react-native` (one CommonJS file for Metro, no DOM, no `"use client"`, no rrweb). `MRRocketProvider`, `useMRRocket`, `useFlag`, `useFlagVariant`, `useVariant`, `createMRRocket` / `getMRRocket` outside React; `track`, `identify`, `reset`, `screen`, `setConsent`, `captureError`, `flush`. Screen views through `useExpoRouterTracking()` (Expo Router) or `useNavigationTracking()` (React Navigation `onStateChange`). Lifecycle events `$app_open` (with `cold`) and `$app_background`; a new session after 30 minutes in the background. Events are batched, sent every 10 s and when the app goes to the background, and kept on the device while offline (AsyncStorage when installed, else memory; at most 1,000, oldest dropped; retries with backoff). Consent `"anonymous"` (default: no id stored), `"persistent"` (a random install id stored on the device) or `"off"` (nothing sent, stored data deleted). Every event carries the app and device (`platform`, `appVersion`, `build`, `osVersion`, `deviceModel`, `deviceType`, `locale`) from `expo-application`, `expo-device` and `expo-localization` when installed. Uncaught JavaScript errors (through `ErrorUtils`, the app's own handler still runs) and `captureError` go to Build › Errors with the app version as release. The config for flags and experiments is cached, so a cold start shows the same variant as the last run. Install attribution: UTM and referral parameters of the deep link that opened the app the first time, sent once with `$install`; no IDFA, no MMP.
- **Script tag** for websites without npm (Webflow, Framer, WordPress, Shopify, plain HTML): `<script async src="https://app.mrrocket.dev/sdk.js" data-key="mrr_pub_…"></script>`, built from the same sources into `dist/browser/mrrocket.js` (about 8 KB gzip) and served by MRRocket at `/sdk.js`. It does what the Next.js provider does on the client: page views including History API navigation, `data-ck-goal` clicks, heatmap clicks and scroll depth, UTM and referrer, affiliate referrals, error tracking, feature flags, experiments on texts marked `data-ck-text`, and session recordings (the recorder, `sdk-replay.js`, about 26 KB gzip, loads only when a visit records). `window.mrrocket` takes `track`, `identify`, `reset`, `captureError`, `setConsent`, `getFlag`, `getFlagVariant`, `getVariant`, `ready`; calls made before it loaded wait in a queue stub. Settings as data attributes: `data-api-host`, `data-consent`, `data-replay`, `data-respect-dnt`, `data-heatmaps`, `data-errors`, `data-release`, `data-attribution`, `data-referral-param`.
- Shared code: batching (`batch.ts`) and the browser's visitor and session state (`web-state.ts`) moved out of the Next.js provider so the script tag uses the same code. The Next.js entry behaves as in 0.3.0.
- Peers: `next` and `react-dom` are optional now (an app needs neither); `react-native`, `expo-router`, `expo-device`, `expo-application`, `expo-localization` and `@react-native-async-storage/async-storage` are optional peers. `react` from 18.
- Event batches carry `sdk.platform` ("web", "ios" or "android"); MRRocket stores an app's device context with each event.
- Not in this version (coming later): session recordings and heatmaps in apps, the feedback and survey UI in apps, native modules (store install referrer, SKAdNetwork). `<Text>` and `<Experiment>` stay Next.js-only; the script tag swaps texts through `data-ck-text`.

## 0.3.0

- **Error tracking** for MRRocket › Build › Errors, on by default (`errors` prop): uncaught errors and unhandled promise rejections, with the page URL and the last 20 pages and clicks, go to `POST /api/sdk/errors` in batches every 2 s (`sendBeacon` on page hide), under the normal analytics consent and never from a preview. The same error once per page load, at most 20 per page load. `useCockpit().captureError(error)` reports one you caught; the `release` prop names your release. Independent of session recordings.
- **Feature flags** from MRRocket › Build › Feature flags: `useFlag(key)` and `useFlagVariant(key)` in the browser, `getFlag` and `getFlagVariant` on the server (`/next/server`). The config carries the Production flags; each is evaluated in the SDK from the person's `identify()` properties ("Email", "Plan" …, `@domain` matches an address, cohorts never match in the browser) and a stable bucket per flag and person. New exports `evaluateFlag`, `SDK_VERSION`, types `FlagConfig`, `FlagCondition`, `FlagConditionSet`, `FlagPerson`, `ErrorReport`.
- Event batches carry `sdk: { v, errors }`, so MRRocket can say which SDK a site runs and whether it reports errors.
- Fix: unselected stars of a 1–5 survey question are gray again (a button reset made them take the text color).

## 0.2.0

- **Session recordings** (rrweb 2.1.6 via `@rrweb/record`, pinned): recorded only with the visitor's own replay consent, separate from analytics and off by default: new `replay` prop and `setConsent({ replay })` (`setConsent` now also takes `{ analytics?, replay? }`; `useCockpit()` returns `replay`). The recorder is a dynamic import, loaded only when a visit records (about 26 KB gzip); pages without recording load about 1.9 KB more than 0.1.0.
- Privacy defaults: all inputs masked; password, hidden and card fields and payment iframes never recorded; `data-ck-mask` masks text, `data-ck-block` leaves elements out; no canvas, video, fonts, inlined images or network bodies; console only at error level, clipped.
- Sampling from the product's config (`replay.sampleRate`, default 0.1); other visits keep the last minute in memory and upload it on a JS error, a rage click or a `purchase…` / `checkout…` event. Chunks every 5 s or 64 KB, gzip'd, to `POST /api/sdk/replay`, `sendBeacon` on page hide, 30 minutes at most.
- **Heatmaps:** `$click`, `$rageclick` and `$scroll` events under the normal analytics consent (`heatmaps` prop, default true). Labels only for links and buttons, never typed text.
- New types `ReplayConfig`, `ConsentChoice`; `CockpitConfig.replay`.

## 0.1.0

First public release on npm as `@mrrocket/sdk`, MIT licensed. Next.js lives under `@mrrocket/sdk/next`
(`/next/server`, `/next/proxy`); the package root is kept for a framework-free core.

- Affiliate referrals: `?mrr_ref`/`mrr_click` (tracked links) and `?ref` (direct links) are kept in a first-party `mrr_ref` cookie following consent (`attribution` prop), a click beacon goes to `/api/sdk/affiliate/click`, `$referral` rides on `identify()` and own events. New exports `getReferral`, `referralForCheckout`, `referralLink`, `readReferralCookie`. Tests: `npm test`.

## 0.0.1 (never published)

First installable build, compiled to JavaScript (ESM) with type declarations: no `transpilePackages` needed.

- Page views, `data-ck-goal` clicks, `track`, `identify`, UTM and referrer capture, batched events.
- Consent modes `cookieless` (default), `cookies`, `off`, `respectDnt`.
- Experiments: `useVariant`, `<Experiment>`, `<Text>`, server helpers `getCockpitConfig`, `getVariant`, `getTexts`,
  `previewFrom` (`/next/server`), `withCockpitId` (`/next/proxy`), preview links for MRRocket.
- `<FeedbackBoard>`, `<FeedbackWidget>`, `useFeedback`, `<Survey>` (automatic via the provider).
- `host` and `apiKey` default to `NEXT_PUBLIC_MRROCKET_HOST` and `NEXT_PUBLIC_MRROCKET_KEY`.
- Server helpers run on Node, Edge and Cloudflare Workers; `getCockpitConfig` times out after 2.5 s and returns `null`
  (control variants) when MRRocket cannot be reached.
- Console warnings (once) when the host or key is wrong.
