@mrrocket/sdk 0.13.0 → 0.15.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 (45) hide show
  1. package/CHANGELOG.md +8 -1
  2. package/README.md +43 -3
  3. package/dist/api-client.d.ts +62 -1
  4. package/dist/api-client.js +28 -1
  5. package/dist/api-client.js.map +1 -1
  6. package/dist/browser/mrrocket-flows.js +11 -2
  7. package/dist/browser/mrrocket-messenger.js +304 -70
  8. package/dist/browser/mrrocket.js +1 -1
  9. package/dist/browser.d.ts +1 -1
  10. package/dist/browser.js +2 -1
  11. package/dist/browser.js.map +1 -1
  12. package/dist/client.d.ts +12 -0
  13. package/dist/client.js +22 -0
  14. package/dist/client.js.map +1 -1
  15. package/dist/context.d.ts +3 -0
  16. package/dist/context.js.map +1 -1
  17. package/dist/core.d.ts +4 -1
  18. package/dist/core.js +1 -1
  19. package/dist/core.js.map +1 -1
  20. package/dist/flows-ui.d.ts +6 -4
  21. package/dist/flows-ui.js +147 -5
  22. package/dist/flows-ui.js.map +1 -1
  23. package/dist/flows.d.ts +51 -1
  24. package/dist/flows.js +62 -21
  25. package/dist/flows.js.map +1 -1
  26. package/dist/index.d.ts +2 -1
  27. package/dist/index.js +4 -0
  28. package/dist/index.js.map +1 -1
  29. package/dist/messenger-ui.d.ts +37 -2
  30. package/dist/messenger-ui.js +597 -109
  31. package/dist/messenger-ui.js.map +1 -1
  32. package/dist/messenger.d.ts +11 -3
  33. package/dist/messenger.js +6 -3
  34. package/dist/messenger.js.map +1 -1
  35. package/dist/paywall.d.ts +123 -0
  36. package/dist/paywall.js +131 -0
  37. package/dist/paywall.js.map +1 -0
  38. package/dist/react-native/index.d.cts +235 -8
  39. package/dist/react-native/index.d.ts +235 -8
  40. package/dist/react-native/index.js +650 -184
  41. package/dist/react-native/index.js.map +1 -1
  42. package/dist/styles-css.d.ts +1 -1
  43. package/dist/styles-css.js +302 -68
  44. package/dist/styles-css.js.map +1 -1
  45. package/package.json +5 -1
@@ -78,6 +78,28 @@ interface SurveyDefinition {
78
78
  questions: SurveyQuestion[];
79
79
  }
80
80
  type SurveyAnswer = number | string | string[];
81
+ /** A file in the messenger's chat: a screenshot, a photo or a PDF the visitor sent (servers from SDK 0.15 on). */
82
+ interface ChatFile {
83
+ /** From an upload: the signed id a message sends. In a message: the file's own id (`attachmentUrl` reads it). */
84
+ id: string;
85
+ name: string;
86
+ type: "image/png" | "image/jpeg" | "image/webp" | "image/gif" | "application/pdf";
87
+ /** Bytes. */
88
+ size: number;
89
+ }
90
+ /** A file to upload: a Blob or File on the web, the image picker's `{ uri, name, type }` in React Native. */
91
+ type ChatUpload = Blob | {
92
+ uri: string;
93
+ name: string;
94
+ type: string;
95
+ };
96
+ /** The bug context a visitor message carries; the server fills browser and system from the user agent when left out. */
97
+ interface ChatContext {
98
+ browser?: string;
99
+ os?: string;
100
+ /** "1440×900". */
101
+ viewport?: string;
102
+ }
81
103
  /** One message of the messenger's chat. "visitor" is the person on the site; the rest come from the product's team. */
82
104
  interface ChatMessage {
83
105
  id: string;
@@ -89,6 +111,8 @@ interface ChatMessage {
89
111
  id: string;
90
112
  title: string;
91
113
  }[];
114
+ /** Files sent with it (servers from SDK 0.15 on). */
115
+ attachments?: ChatFile[];
92
116
  }
93
117
  /** A published help article in a list (the messenger's Help tab). */
94
118
  interface ArticleSummary {
@@ -126,11 +150,17 @@ interface Article {
126
150
  updatedAt: string;
127
151
  blocks: ArticleBlock[];
128
152
  }
153
+ /** Where the chat button sits: a corner, the middle of the top or bottom edge, or the middle of a side. */
154
+ type MessengerPosition = "top-left" | "top-center" | "top-right" | "middle-left" | "middle-right" | "bottom-left" | "bottom-center" | "bottom-right";
155
+ /** How round the messenger is. */
156
+ type MessengerCorners = "round" | "soft" | "square";
129
157
  /** The messenger's look and texts as set in MRRocket › Support › Messenger (servers from SDK 0.9 on). */
130
158
  interface MessengerLook {
131
159
  accent: string;
160
+ /** Up to two more colors blended into Home's background glow (MRRocket from 0.16; older servers leave it out). */
161
+ glow?: string[];
132
162
  theme: "light" | "dark" | "auto";
133
- position: "bottom-right" | "bottom-left";
163
+ position: MessengerPosition;
134
164
  title: string;
135
165
  greeting: string;
136
166
  label: string;
@@ -142,6 +172,16 @@ interface MessengerLook {
142
172
  sound: boolean;
143
173
  preview: boolean;
144
174
  askEmail: boolean;
175
+ /** How round panel, bubbles, cards and fields are (MRRocket from 0.15; older servers leave it out: round). */
176
+ corners?: MessengerCorners;
177
+ /** The site's own font (the default) or the system's (web only; from 0.15). */
178
+ font?: "site" | "system";
179
+ /** Home's top: plain like the rest, or in the accent color (from 0.15; older servers leave it out: plain). */
180
+ header?: "plain" | "color";
181
+ /** "Powered by MRRocket" at the bottom; the server sends false only on plans that may hide it (from 0.15). */
182
+ branding?: boolean;
183
+ /** The product's icon (its favicon) at the top left of Home; without it the title's first letter. */
184
+ logo?: string;
145
185
  }
146
186
  /** The visitor's newest chat (open or closed) and its messages. */
147
187
  interface ChatThread {
@@ -197,11 +237,18 @@ declare const createApi: (c: Conn) => {
197
237
  seconds: number;
198
238
  }) => Promise<unknown>;
199
239
  chat: (signal?: AbortSignal) => Promise<ChatThread>;
240
+ /** A message; `attachments` are upload ids from `uploadChatFile` (then the body may be empty). */
200
241
  sendChat: (m: {
201
242
  body: string;
202
243
  email?: string;
203
244
  url?: string;
245
+ attachments?: string[];
246
+ context?: ChatContext;
204
247
  }) => Promise<ChatThread>;
248
+ /** Uploads one picked file for the next message (pictures and PDFs, at most 10 MB); answers its upload id. */
249
+ uploadChatFile: (file: ChatUpload, signal?: AbortSignal) => Promise<ChatFile>;
250
+ /** Where a sent file of this visitor's chat is read (an image's src, a PDF's link). */
251
+ attachmentUrl: (id: string) => string;
205
252
  articles: (q?: string, signal?: AbortSignal) => Promise<{
206
253
  articles: ArticleSummary[];
207
254
  }>;
@@ -243,6 +290,29 @@ interface CancelOffer {
243
290
  plan?: string;
244
291
  planName?: string;
245
292
  }
293
+ /** 0.15.0: a second question after an answer ("What would be a fair price?"); no answers means a free text. */
294
+ interface CancelFollowUp {
295
+ question: string;
296
+ /** The choices, in order; empty for a free text. */
297
+ answers: string[];
298
+ }
299
+ /**
300
+ * 0.15.0: a different offer for some customers of one answer. Every condition that is set must hold; the first rule
301
+ * that matches wins, else the answer's own offer. `offer: null` means no offer (straight to the confirmation).
302
+ */
303
+ interface CancelRule {
304
+ when: {
305
+ /** The follow-up's answer, as written. */
306
+ answer?: string;
307
+ /** The customer's plan: its id or name as MRRocket knows it from the payment provider (any of a list). */
308
+ plan?: string | string[];
309
+ /** A customer for fewer than this many months. */
310
+ tenureUnder?: number;
311
+ /** A customer for at least this many months. */
312
+ tenureOver?: number;
313
+ };
314
+ offer: CancelOffer | null;
315
+ }
246
316
  /** The product's live cancel flow (MRRocket › Churn › Cancel flow). */
247
317
  interface CancelFlowConfig {
248
318
  /** Grows with every publish. */
@@ -255,6 +325,12 @@ interface CancelFlowConfig {
255
325
  }[];
256
326
  /** The offer per answer id. */
257
327
  offers: Record<string, CancelOffer>;
328
+ /** 0.15.0: the follow-up question per answer id. */
329
+ followUps?: Record<string, CancelFollowUp>;
330
+ /** 0.15.0: the rules per answer id, checked in order before its offer. */
331
+ rules?: Record<string, CancelRule[]>;
332
+ /** 0.15.0: what one answer offers after its first offer was declined. */
333
+ second?: Record<string, CancelOffer>;
258
334
  confirm: {
259
335
  title: string;
260
336
  body: string;
@@ -326,6 +402,8 @@ interface CancelFlowResult {
326
402
  outcome: "cancelled" | "saved" | "kept";
327
403
  reason?: string;
328
404
  reasonLabel?: string;
405
+ /** 0.15.0: the answer to the follow-up question. */
406
+ answer?: string;
329
407
  offer?: CancelOffer;
330
408
  /** What the customer wrote on the confirmation, if anything. */
331
409
  comment?: string;
@@ -344,6 +422,10 @@ interface CancelFlowOptions {
344
422
  currency: string;
345
423
  interval?: "month" | "year";
346
424
  };
425
+ /** 0.15.0: the customer's plan (its id or name in your billing), for offers that depend on it. */
426
+ plan?: string;
427
+ /** 0.15.0: since when they pay (a date, ISO string or ms), for offers that depend on how long. */
428
+ customerSince?: Date | string | number;
347
429
  /** The customer confirmed: cancel the subscription in your code. Throwing keeps the dialog open with an error. */
348
430
  onCancel?: (result: CancelFlowResult) => unknown;
349
431
  /** The customer took an offer: apply it in your code (coupon, pause, plan change). Throwing keeps the dialog open. */
@@ -354,11 +436,16 @@ interface CancelFlowOptions {
354
436
  /** What a renderer draws. */
355
437
  type FlowView = {
356
438
  type: "cancel";
357
- screen: "survey" | "offer" | "confirm";
439
+ screen: "survey" | "followup" | "offer" | "confirm";
358
440
  flow: CancelFlowConfig;
359
441
  /** The answer picked on the survey. */
360
442
  reason?: string;
443
+ /** 0.15.0: the follow-up question on its screen, and its answer so far. */
444
+ followUp?: CancelFollowUp;
445
+ answer?: string;
361
446
  offer?: CancelOffer;
447
+ /** 0.15.0: 2 for the offer after a declined one. */
448
+ round?: 1 | 2;
362
449
  price?: CancelFlowOptions["price"];
363
450
  busy: boolean;
364
451
  error?: string;
@@ -395,8 +482,10 @@ interface FlowsController {
395
482
  view(): FlowView | null;
396
483
  subscribe(listener: () => void): () => void;
397
484
  /** `accept(comment)`: a "Talk to us" offer sends what the customer wrote with it. */
485
+ /** `answer(text)`: the follow-up's choice or free text (0.15.0); `next()` moves on from the survey and the follow-up. */
398
486
  cancel: {
399
487
  pick(reason: string): void;
488
+ answer(text: string): void;
400
489
  next(): void;
401
490
  accept(comment?: string): void;
402
491
  decline(): void;
@@ -414,6 +503,116 @@ interface FlowsController {
414
503
  };
415
504
  }
416
505
 
506
+ /**
507
+ * Paywalls with several pages (SDK 0.14.0): what MRRocket's config serves under `paywalls` (one per placement, built
508
+ * in MRRocket › Conversion › Paywalls) and a small controller that walks the person through its pages, page after page,
509
+ * until the last one hands the chosen plan to the app. No DOM and no React: the web renderer (`mountPaywall` in
510
+ * flows-ui.ts, loaded only when a paywall opens) and the React Native one (native/flows-view.ts) draw `view()` and
511
+ * call the actions.
512
+ *
513
+ * MRRocket never takes a payment. The last page's button calls the app's `onPurchase(plan)`, which buys through the
514
+ * app's own store code (StoreKit or Play through RevenueCat or Adapty, or Stripe Checkout on the web); only when it
515
+ * resolves is the purchase recorded. It throwing keeps the paywall open with an error, resolving `false` (the person
516
+ * closed the store sheet) keeps it open without one.
517
+ *
518
+ * A running test on the paywall (`experiment`) gives each person their variant's pages, by the same stable hash as
519
+ * every other test (`assign`), and records `$exposure` when the paywall opens. What it records, as normal events under
520
+ * the SDK's analytics consent:
521
+ * - `$paywall` `{paywall, placement, variant, page, index, action: "page"}` the first time each page shows in a run,
522
+ * so MRRocket shows how many reach page 2 and 3;
523
+ * - `{action: "purchased", plan}` after `onPurchase` went through (a test's purchase goal counts it), and
524
+ * `{action: "closed", index}` when the person closed it.
525
+ */
526
+
527
+ /** One page of a paywall. Plans pages list the plans and buy; the others say why and go on. */
528
+ interface PaywallPage {
529
+ id: string;
530
+ /** "benefits": a list of what the person gets; "proof": quotes of happy customers; "plans": the plans and the buy button. */
531
+ kind: "benefits" | "proof" | "plans";
532
+ title: string;
533
+ body?: string;
534
+ /** Benefits: one line each. */
535
+ items?: string[];
536
+ /** Proof: what customers said, and who. */
537
+ quotes?: {
538
+ text: string;
539
+ by?: string;
540
+ }[];
541
+ /** The button: "Continue" on the way, the buy button on the plans page ("Start free trial"). */
542
+ button?: string;
543
+ }
544
+ /** A plan the paywall offers. `id` is what the app buys: the store product id or the Stripe price, as set in MRRocket. */
545
+ interface PaywallPlan {
546
+ id: string;
547
+ name: string;
548
+ /** As shown ("€59.99 / year"). The app's own store prices win when it passes `plans`. */
549
+ price: string;
550
+ /** A line under it ("€5 a month", "7 days free"). */
551
+ note?: string;
552
+ }
553
+ /** A published paywall as the config serves it. */
554
+ interface PaywallConfig {
555
+ id: string;
556
+ /** The placement's key (`onboarding_end`), what the app opens: `paywall.open("onboarding_end")`. */
557
+ placement: string;
558
+ /** The running test on it, a key of `experiments`; each of its variants has pages under `variants`. */
559
+ experiment?: string;
560
+ /** Control (the live paywall) first. */
561
+ variants: {
562
+ id: VariantId;
563
+ pages: PaywallPage[];
564
+ }[];
565
+ plans: PaywallPlan[];
566
+ /** The plan picked before the person picks one. */
567
+ highlight?: string;
568
+ }
569
+ interface PaywallResult {
570
+ /**
571
+ * "purchased": `onPurchase` went through; "chosen": the person picked a plan and there is no `onPurchase`, so the app
572
+ * buys it now (the store's purchase still counts for a test through RevenueCat or Adapty); "closed": the person
573
+ * closed it (or there was no paywall).
574
+ */
575
+ outcome: "purchased" | "chosen" | "closed";
576
+ plan?: PaywallPlan;
577
+ paywall?: string;
578
+ variant?: VariantId;
579
+ /** The page the person was on, from 0. */
580
+ index: number;
581
+ }
582
+ interface PaywallOptions {
583
+ /**
584
+ * Buy the plan in your code (RevenueCat `purchasePackage`, Adapty `makePurchase`, Stripe Checkout). Throwing keeps
585
+ * the paywall open with an error; `false` keeps it open without one (the person cancelled the store sheet).
586
+ */
587
+ onPurchase?: (plan: PaywallPlan) => unknown;
588
+ /** Your store's plans with their local prices, by the same ids; they replace what MRRocket has for them. */
589
+ plans?: PaywallPlan[];
590
+ onClose?: (result: PaywallResult) => void;
591
+ }
592
+ interface PaywallView {
593
+ paywall: PaywallConfig;
594
+ variant: VariantId;
595
+ page: PaywallPage;
596
+ index: number;
597
+ total: number;
598
+ last: boolean;
599
+ plans: PaywallPlan[];
600
+ /** The picked plan's id. */
601
+ plan?: string;
602
+ busy: boolean;
603
+ error?: string;
604
+ }
605
+ interface PaywallHost {
606
+ /** Opens the paywall of a placement; resolves when it is bought or closed (at once with "closed" when there is none). */
607
+ open(placement: string, options?: PaywallOptions): Promise<PaywallResult>;
608
+ view(): PaywallView | null;
609
+ subscribe(listener: () => void): () => void;
610
+ next(): void;
611
+ back(): void;
612
+ pick(planId: string): void;
613
+ close(): void;
614
+ }
615
+
417
616
  /**
418
617
  * The part of the SDK that runs anywhere: experiment config, variant assignment, preview links and event shapes.
419
618
  * No React, no browser APIs, so the server helper, the proxy helper and the client share it.
@@ -421,7 +620,7 @@ interface FlowsController {
421
620
 
422
621
  type VariantId = string;
423
622
  /** This build of the SDK. Sent with the config request so MRRocket knows what the site runs (error tracking, flags). */
424
- declare const SDK_VERSION = "0.13.0";
623
+ declare const SDK_VERSION = "0.15.0";
425
624
  /** One variant of an experiment. `changes` replaces texts that the page marks with `<Text id>`. */
426
625
  interface VariantConfig {
427
626
  id: VariantId;
@@ -478,6 +677,8 @@ interface CockpitConfig {
478
677
  messages?: boolean;
479
678
  /** Page redirects set in MRRocket; the SDK sends visitors on before the page shows (`redirectFor`). SDK 0.7.0+. */
480
679
  redirects?: RedirectConfig[];
680
+ /** Published paywalls with their pages, per placement (MRRocket › Conversion › Paywalls), for `paywall.open()`. SDK 0.14.0+. */
681
+ paywalls?: PaywallConfig[];
481
682
  }
482
683
  /** One condition of a flag's audience, on a property of the person (`identify(id, props)`), the id or the page. */
483
684
  interface FlagCondition {
@@ -668,6 +869,14 @@ interface MRRocketClient {
668
869
  openCancelFlow(options?: CancelFlowOptions): Promise<CancelFlowResult>;
669
870
  /** Starts an onboarding flow now (the first live one without an id). False when there is none. */
670
871
  startOnboarding(id?: string): boolean;
872
+ /**
873
+ * Shows the paywall of a placement built in MRRocket › Paywalls page by page (drawn by `<MRRocketProvider>`). Its
874
+ * last page hands the chosen plan to `onPurchase`, where your code buys it (RevenueCat, Adapty, StoreKit, Play);
875
+ * MRRocket never takes a payment. Pass your store's `plans` to show their local prices.
876
+ */
877
+ openPaywall(placement: string, options?: PaywallOptions): Promise<PaywallResult>;
878
+ /** The open paywall for a renderer (the provider's, or your own). */
879
+ paywall: PaywallHost;
671
880
  /**
672
881
  * Push notifications from your Lifecycle journeys for the signed-in person (call `identify` first). Asks the
673
882
  * permission (unless `ask: false`), gets the Expo push token and sends it to MRRocket. Needs `expo-notifications`.
@@ -720,8 +929,9 @@ interface NativeAppearance {
720
929
  theme?: "light" | "dark" | "auto";
721
930
  accent?: string;
722
931
  }
723
- declare const MRRocketFlows: ({ flows, appearance }: {
932
+ declare const MRRocketFlows: ({ flows, paywall, appearance }: {
724
933
  flows: FlowsController;
934
+ paywall?: PaywallHost;
725
935
  appearance?: NativeAppearance;
726
936
  }) => react.FunctionComponentElement<Record<string, unknown>> | null;
727
937
 
@@ -774,15 +984,22 @@ declare const useMessenger: () => {
774
984
  interface NativeMessengerOptions {
775
985
  /** A round chat button (default false: open it with `useMessenger().open()` from your own button). */
776
986
  launcher?: boolean;
777
- /** Where the chat button sits (default bottom right). */
778
- position?: "bottom-right" | "bottom-left";
987
+ /** Where the chat button sits: a corner, the middle of the top or bottom edge, or the middle of a side (default bottom right). */
988
+ position?: MessengerPosition;
779
989
  /** Text beside the chat button's icon; empty: the round button. */
780
990
  label?: string;
991
+ /** The name beside the team's mark at the top of Home (MRRocket fills in the product's name). */
781
992
  title?: string;
782
993
  /** The big line on Home and the first line of an empty chat. */
783
994
  greeting?: string;
784
- /** How soon the team usually answers, said under "Send us a message" and in the chat's head. */
995
+ /** How soon the team usually answers, said on Home. */
785
996
  replyTime?: "minutes" | "hours" | "day";
997
+ /** How round bubbles, cards and fields are (default round; the sheet itself keeps the system's corners). */
998
+ corners?: MessengerCorners;
999
+ /** Home's top: plain like the rest (default), or in the accent color with raised cards. */
1000
+ header?: "plain" | "color";
1001
+ /** The product's icon at the top left of Home (MRRocket sends the product's favicon). */
1002
+ logo?: string;
786
1003
  /** Ask people the app did not identify with an email for an email (optional for them). Default true. */
787
1004
  askEmail?: boolean;
788
1005
  /** The Help tab with your published help articles (default true; hidden while there are none). */
@@ -891,6 +1108,16 @@ declare const usePush: () => {
891
1108
  register: MRRocketClient["registerForPush"];
892
1109
  unregister: MRRocketClient["unregisterForPush"];
893
1110
  };
1111
+ /**
1112
+ * The paywalls built in MRRocket › Paywalls, page by page; the provider draws them. The last page hands the chosen plan
1113
+ * to `onPurchase`, where your code buys it. MRRocket never takes a payment.
1114
+ *
1115
+ * const paywall = usePaywall();
1116
+ * paywall.open("onboarding_end", { plans: storePlans, onPurchase: (plan) => Purchases.purchasePackage(packages[plan.id]) });
1117
+ */
1118
+ declare const usePaywall: () => {
1119
+ open: MRRocketClient["openPaywall"];
1120
+ };
894
1121
  /** Starts an onboarding flow now (the first live one without an id), whatever its trigger. */
895
1122
  declare const useOnboarding: () => {
896
1123
  start: (id?: string) => boolean;
@@ -929,4 +1156,4 @@ declare const useExpoRouterTracking: () => void;
929
1156
  */
930
1157
  declare const useNavigationTracking: () => (state: unknown) => void;
931
1158
 
932
- export { type AppContext, type CancelFlowConfig, type CancelFlowOptions, type CancelFlowResult, type CancelOffer, type ChatMessage, type ChatThread, type CockpitConfig, type FeedbackPost, type FlagConfig, type FlowView, type InAppMessage, type KeyValueStorage, type MRRocketClient, MRRocketFeedback, MRRocketFlows, MRRocketMask, MRRocketMessenger, type MRRocketOptions, MRRocketProvider, type MRRocketProviderProps, MRRocketSurveys, type NativeAppearance, type NativeConsent, type NativeFeedbackOptions, type NativeMessengerOptions, type NativePushResult, type NativePushStatus, type OnboardingFlow, type OnboardingStep, type RegisterForPushOptions, SDK_VERSION, type SurveyDefinition, type VariantId, activeRouteName, createMRRocket, getMRRocket, useCancelFlow, useExpoRouterTracking, useFeedback, useFlag, useFlagVariant, useMRRocket, useMessenger, useNavigationTracking, useOnboarding, usePush, useText, useVariant };
1159
+ export { type AppContext, type CancelFlowConfig, type CancelFlowOptions, type CancelFlowResult, type CancelOffer, type ChatMessage, type ChatThread, type CockpitConfig, type FeedbackPost, type FlagConfig, type FlowView, type InAppMessage, type KeyValueStorage, type MRRocketClient, MRRocketFeedback, MRRocketFlows, MRRocketMask, MRRocketMessenger, type MRRocketOptions, MRRocketProvider, type MRRocketProviderProps, MRRocketSurveys, type NativeAppearance, type NativeConsent, type NativeFeedbackOptions, type NativeMessengerOptions, type NativePushResult, type NativePushStatus, type OnboardingFlow, type OnboardingStep, type PaywallConfig, type PaywallOptions, type PaywallPage, type PaywallPlan, type PaywallResult, type RegisterForPushOptions, SDK_VERSION, type SurveyDefinition, type VariantId, activeRouteName, createMRRocket, getMRRocket, useCancelFlow, useExpoRouterTracking, useFeedback, useFlag, useFlagVariant, useMRRocket, useMessenger, useNavigationTracking, useOnboarding, usePaywall, usePush, useText, useVariant };