@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.
- package/CHANGELOG.md +8 -1
- package/README.md +43 -3
- package/dist/api-client.d.ts +62 -1
- package/dist/api-client.js +28 -1
- package/dist/api-client.js.map +1 -1
- package/dist/browser/mrrocket-flows.js +11 -2
- package/dist/browser/mrrocket-messenger.js +304 -70
- package/dist/browser/mrrocket.js +1 -1
- package/dist/browser.d.ts +1 -1
- package/dist/browser.js +2 -1
- package/dist/browser.js.map +1 -1
- package/dist/client.d.ts +12 -0
- package/dist/client.js +22 -0
- package/dist/client.js.map +1 -1
- package/dist/context.d.ts +3 -0
- package/dist/context.js.map +1 -1
- package/dist/core.d.ts +4 -1
- package/dist/core.js +1 -1
- package/dist/core.js.map +1 -1
- package/dist/flows-ui.d.ts +6 -4
- package/dist/flows-ui.js +147 -5
- package/dist/flows-ui.js.map +1 -1
- package/dist/flows.d.ts +51 -1
- package/dist/flows.js +62 -21
- package/dist/flows.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/messenger-ui.d.ts +37 -2
- package/dist/messenger-ui.js +597 -109
- package/dist/messenger-ui.js.map +1 -1
- package/dist/messenger.d.ts +11 -3
- package/dist/messenger.js +6 -3
- package/dist/messenger.js.map +1 -1
- package/dist/paywall.d.ts +123 -0
- package/dist/paywall.js +131 -0
- package/dist/paywall.js.map +1 -0
- package/dist/react-native/index.d.cts +235 -8
- package/dist/react-native/index.d.ts +235 -8
- package/dist/react-native/index.js +650 -184
- package/dist/react-native/index.js.map +1 -1
- package/dist/styles-css.d.ts +1 -1
- package/dist/styles-css.js +302 -68
- package/dist/styles-css.js.map +1 -1
- package/package.json +5 -1
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Paywalls with several pages (SDK 0.14.0): what MRRocket's config serves under `paywalls` (one per placement, built
|
|
3
|
+
* in MRRocket › Conversion › Paywalls) and a small controller that walks the person through its pages, page after page,
|
|
4
|
+
* until the last one hands the chosen plan to the app. No DOM and no React: the web renderer (`mountPaywall` in
|
|
5
|
+
* flows-ui.ts, loaded only when a paywall opens) and the React Native one (native/flows-view.ts) draw `view()` and
|
|
6
|
+
* call the actions.
|
|
7
|
+
*
|
|
8
|
+
* MRRocket never takes a payment. The last page's button calls the app's `onPurchase(plan)`, which buys through the
|
|
9
|
+
* app's own store code (StoreKit or Play through RevenueCat or Adapty, or Stripe Checkout on the web); only when it
|
|
10
|
+
* resolves is the purchase recorded. It throwing keeps the paywall open with an error, resolving `false` (the person
|
|
11
|
+
* closed the store sheet) keeps it open without one.
|
|
12
|
+
*
|
|
13
|
+
* A running test on the paywall (`experiment`) gives each person their variant's pages, by the same stable hash as
|
|
14
|
+
* every other test (`assign`), and records `$exposure` when the paywall opens. What it records, as normal events under
|
|
15
|
+
* the SDK's analytics consent:
|
|
16
|
+
* - `$paywall` `{paywall, placement, variant, page, index, action: "page"}` the first time each page shows in a run,
|
|
17
|
+
* so MRRocket shows how many reach page 2 and 3;
|
|
18
|
+
* - `{action: "purchased", plan}` after `onPurchase` went through (a test's purchase goal counts it), and
|
|
19
|
+
* `{action: "closed", index}` when the person closed it.
|
|
20
|
+
*/
|
|
21
|
+
import { type CockpitConfig, type VariantId } from "./core.js";
|
|
22
|
+
export declare const PAYWALL_EVENT = "$paywall";
|
|
23
|
+
/** One page of a paywall. Plans pages list the plans and buy; the others say why and go on. */
|
|
24
|
+
export interface PaywallPage {
|
|
25
|
+
id: string;
|
|
26
|
+
/** "benefits": a list of what the person gets; "proof": quotes of happy customers; "plans": the plans and the buy button. */
|
|
27
|
+
kind: "benefits" | "proof" | "plans";
|
|
28
|
+
title: string;
|
|
29
|
+
body?: string;
|
|
30
|
+
/** Benefits: one line each. */
|
|
31
|
+
items?: string[];
|
|
32
|
+
/** Proof: what customers said, and who. */
|
|
33
|
+
quotes?: {
|
|
34
|
+
text: string;
|
|
35
|
+
by?: string;
|
|
36
|
+
}[];
|
|
37
|
+
/** The button: "Continue" on the way, the buy button on the plans page ("Start free trial"). */
|
|
38
|
+
button?: string;
|
|
39
|
+
}
|
|
40
|
+
/** A plan the paywall offers. `id` is what the app buys: the store product id or the Stripe price, as set in MRRocket. */
|
|
41
|
+
export interface PaywallPlan {
|
|
42
|
+
id: string;
|
|
43
|
+
name: string;
|
|
44
|
+
/** As shown ("€59.99 / year"). The app's own store prices win when it passes `plans`. */
|
|
45
|
+
price: string;
|
|
46
|
+
/** A line under it ("€5 a month", "7 days free"). */
|
|
47
|
+
note?: string;
|
|
48
|
+
}
|
|
49
|
+
/** A published paywall as the config serves it. */
|
|
50
|
+
export interface PaywallConfig {
|
|
51
|
+
id: string;
|
|
52
|
+
/** The placement's key (`onboarding_end`), what the app opens: `paywall.open("onboarding_end")`. */
|
|
53
|
+
placement: string;
|
|
54
|
+
/** The running test on it, a key of `experiments`; each of its variants has pages under `variants`. */
|
|
55
|
+
experiment?: string;
|
|
56
|
+
/** Control (the live paywall) first. */
|
|
57
|
+
variants: {
|
|
58
|
+
id: VariantId;
|
|
59
|
+
pages: PaywallPage[];
|
|
60
|
+
}[];
|
|
61
|
+
plans: PaywallPlan[];
|
|
62
|
+
/** The plan picked before the person picks one. */
|
|
63
|
+
highlight?: string;
|
|
64
|
+
}
|
|
65
|
+
export interface PaywallResult {
|
|
66
|
+
/**
|
|
67
|
+
* "purchased": `onPurchase` went through; "chosen": the person picked a plan and there is no `onPurchase`, so the app
|
|
68
|
+
* buys it now (the store's purchase still counts for a test through RevenueCat or Adapty); "closed": the person
|
|
69
|
+
* closed it (or there was no paywall).
|
|
70
|
+
*/
|
|
71
|
+
outcome: "purchased" | "chosen" | "closed";
|
|
72
|
+
plan?: PaywallPlan;
|
|
73
|
+
paywall?: string;
|
|
74
|
+
variant?: VariantId;
|
|
75
|
+
/** The page the person was on, from 0. */
|
|
76
|
+
index: number;
|
|
77
|
+
}
|
|
78
|
+
export interface PaywallOptions {
|
|
79
|
+
/**
|
|
80
|
+
* Buy the plan in your code (RevenueCat `purchasePackage`, Adapty `makePurchase`, Stripe Checkout). Throwing keeps
|
|
81
|
+
* the paywall open with an error; `false` keeps it open without one (the person cancelled the store sheet).
|
|
82
|
+
*/
|
|
83
|
+
onPurchase?: (plan: PaywallPlan) => unknown;
|
|
84
|
+
/** Your store's plans with their local prices, by the same ids; they replace what MRRocket has for them. */
|
|
85
|
+
plans?: PaywallPlan[];
|
|
86
|
+
onClose?: (result: PaywallResult) => void;
|
|
87
|
+
}
|
|
88
|
+
export interface PaywallView {
|
|
89
|
+
paywall: PaywallConfig;
|
|
90
|
+
variant: VariantId;
|
|
91
|
+
page: PaywallPage;
|
|
92
|
+
index: number;
|
|
93
|
+
total: number;
|
|
94
|
+
last: boolean;
|
|
95
|
+
plans: PaywallPlan[];
|
|
96
|
+
/** The picked plan's id. */
|
|
97
|
+
plan?: string;
|
|
98
|
+
busy: boolean;
|
|
99
|
+
error?: string;
|
|
100
|
+
}
|
|
101
|
+
export interface PaywallEnv {
|
|
102
|
+
config: () => CockpitConfig | null;
|
|
103
|
+
distinctId: () => string;
|
|
104
|
+
/** Records an event under the SDK's consent. */
|
|
105
|
+
track: (event: string, props?: Record<string, unknown>) => void;
|
|
106
|
+
}
|
|
107
|
+
export interface PaywallHost {
|
|
108
|
+
/** Opens the paywall of a placement; resolves when it is bought or closed (at once with "closed" when there is none). */
|
|
109
|
+
open(placement: string, options?: PaywallOptions): Promise<PaywallResult>;
|
|
110
|
+
view(): PaywallView | null;
|
|
111
|
+
subscribe(listener: () => void): () => void;
|
|
112
|
+
next(): void;
|
|
113
|
+
back(): void;
|
|
114
|
+
pick(planId: string): void;
|
|
115
|
+
close(): void;
|
|
116
|
+
}
|
|
117
|
+
/** The pages of a person's variant: the test's when it runs (or shipped), else control's. */
|
|
118
|
+
export declare const paywallPages: (config: CockpitConfig | null, w: PaywallConfig, distinctId: string) => {
|
|
119
|
+
variant: string;
|
|
120
|
+
pages: PaywallPage[];
|
|
121
|
+
test: string | undefined;
|
|
122
|
+
} | null;
|
|
123
|
+
export declare const createPaywallHost: (env: PaywallEnv) => PaywallHost;
|
package/dist/paywall.js
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
// src/paywall.ts
|
|
2
|
+
import { assign } from "./core.js";
|
|
3
|
+
var PAYWALL_EVENT = "$paywall";
|
|
4
|
+
var paywallPages = (config, w, distinctId) => {
|
|
5
|
+
const e = w.experiment ? config?.experiments.find((x) => x.key === w.experiment) : void 0;
|
|
6
|
+
const id = e ? assign(e, distinctId) : w.variants[0]?.id;
|
|
7
|
+
const v = w.variants.find((x) => x.id === id) ?? w.variants[0];
|
|
8
|
+
return v ? { variant: v.id, pages: v.pages, test: e?.status === "running" && !e.winner ? e.key : void 0 } : null;
|
|
9
|
+
};
|
|
10
|
+
var createPaywallHost = (env) => {
|
|
11
|
+
let run = null;
|
|
12
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
13
|
+
const emit = () => {
|
|
14
|
+
for (const l of [...listeners]) {
|
|
15
|
+
try {
|
|
16
|
+
l();
|
|
17
|
+
} catch {
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
const base = () => run ? { paywall: run.w.id, placement: run.w.placement, variant: run.variant } : {};
|
|
22
|
+
const show = () => {
|
|
23
|
+
if (run && !run.seen.has(run.index)) {
|
|
24
|
+
run.seen.add(run.index);
|
|
25
|
+
env.track(PAYWALL_EVENT, { ...base(), page: run.pages[run.index].id, index: run.index, action: "page" });
|
|
26
|
+
}
|
|
27
|
+
emit();
|
|
28
|
+
};
|
|
29
|
+
const end = (r) => {
|
|
30
|
+
const c = run;
|
|
31
|
+
if (!c) return;
|
|
32
|
+
run = null;
|
|
33
|
+
if (r.outcome === "closed") c.options.onClose?.(r);
|
|
34
|
+
c.resolve(r);
|
|
35
|
+
emit();
|
|
36
|
+
};
|
|
37
|
+
const result = (outcome) => ({
|
|
38
|
+
outcome,
|
|
39
|
+
index: run?.index ?? 0,
|
|
40
|
+
...run ? { paywall: run.w.id, variant: run.variant } : {},
|
|
41
|
+
...outcome !== "closed" && run ? { plan: run.plans.find((p) => p.id === run.plan) } : {}
|
|
42
|
+
});
|
|
43
|
+
const buy = () => {
|
|
44
|
+
const c = run;
|
|
45
|
+
const plan = c?.plans.find((p) => p.id === c.plan);
|
|
46
|
+
if (!c || !plan || c.busy) return;
|
|
47
|
+
if (!c.options.onPurchase) return end(result("chosen"));
|
|
48
|
+
c.busy = true;
|
|
49
|
+
c.error = void 0;
|
|
50
|
+
emit();
|
|
51
|
+
Promise.resolve().then(() => c.options.onPurchase?.(plan)).then(
|
|
52
|
+
(ok) => {
|
|
53
|
+
c.busy = false;
|
|
54
|
+
if (run !== c) return;
|
|
55
|
+
if (ok === false) return emit();
|
|
56
|
+
env.track(PAYWALL_EVENT, { ...base(), page: c.pages[c.index].id, index: c.index, plan: plan.id, action: "purchased" });
|
|
57
|
+
end(result("purchased"));
|
|
58
|
+
},
|
|
59
|
+
() => {
|
|
60
|
+
c.busy = false;
|
|
61
|
+
c.error = "The purchase did not go through. Please try again.";
|
|
62
|
+
emit();
|
|
63
|
+
}
|
|
64
|
+
);
|
|
65
|
+
};
|
|
66
|
+
return {
|
|
67
|
+
open: (placement, options = {}) => {
|
|
68
|
+
if (run) return run.promise;
|
|
69
|
+
const config = env.config();
|
|
70
|
+
const w = config?.paywalls?.find((x) => x.placement === placement);
|
|
71
|
+
const picked = w ? paywallPages(config, w, env.distinctId()) : null;
|
|
72
|
+
if (!w || !picked?.pages.length) return Promise.resolve({ outcome: "closed", index: 0 });
|
|
73
|
+
const own = new Map((options.plans ?? []).map((p) => [p.id, p]));
|
|
74
|
+
const plans = w.plans.map((p) => own.get(p.id) ?? p);
|
|
75
|
+
let resolve = () => {
|
|
76
|
+
};
|
|
77
|
+
const promise = new Promise((r) => resolve = r);
|
|
78
|
+
const plan = plans.some((p) => p.id === w.highlight) ? w.highlight : plans[0]?.id;
|
|
79
|
+
run = { w, variant: picked.variant, pages: picked.pages, plans, index: 0, plan, busy: false, seen: /* @__PURE__ */ new Set(), options, resolve, promise };
|
|
80
|
+
if (picked.test) env.track("$exposure", { experiment: picked.test, variant: picked.variant });
|
|
81
|
+
show();
|
|
82
|
+
return promise;
|
|
83
|
+
},
|
|
84
|
+
view: () => run ? {
|
|
85
|
+
paywall: run.w,
|
|
86
|
+
variant: run.variant,
|
|
87
|
+
page: run.pages[run.index],
|
|
88
|
+
index: run.index,
|
|
89
|
+
total: run.pages.length,
|
|
90
|
+
last: run.index === run.pages.length - 1,
|
|
91
|
+
plans: run.plans,
|
|
92
|
+
plan: run.plan,
|
|
93
|
+
busy: run.busy,
|
|
94
|
+
error: run.error
|
|
95
|
+
} : null,
|
|
96
|
+
subscribe: (l) => {
|
|
97
|
+
listeners.add(l);
|
|
98
|
+
return () => listeners.delete(l);
|
|
99
|
+
},
|
|
100
|
+
next: () => {
|
|
101
|
+
if (!run || run.busy) return;
|
|
102
|
+
if (run.pages[run.index].kind === "plans" || run.index === run.pages.length - 1) return buy();
|
|
103
|
+
run.index++;
|
|
104
|
+
run.error = void 0;
|
|
105
|
+
show();
|
|
106
|
+
},
|
|
107
|
+
back: () => {
|
|
108
|
+
if (!run || run.busy || !run.index) return;
|
|
109
|
+
run.index--;
|
|
110
|
+
run.error = void 0;
|
|
111
|
+
emit();
|
|
112
|
+
},
|
|
113
|
+
pick: (id) => {
|
|
114
|
+
if (!run || run.busy || !run.plans.some((p) => p.id === id)) return;
|
|
115
|
+
run.plan = id;
|
|
116
|
+
run.error = void 0;
|
|
117
|
+
emit();
|
|
118
|
+
},
|
|
119
|
+
close: () => {
|
|
120
|
+
if (!run || run.busy) return;
|
|
121
|
+
env.track(PAYWALL_EVENT, { ...base(), index: run.index, action: "closed" });
|
|
122
|
+
end(result("closed"));
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
};
|
|
126
|
+
export {
|
|
127
|
+
PAYWALL_EVENT,
|
|
128
|
+
createPaywallHost,
|
|
129
|
+
paywallPages
|
|
130
|
+
};
|
|
131
|
+
//# sourceMappingURL=paywall.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/paywall.ts"],"sourcesContent":["/**\n * Paywalls with several pages (SDK 0.14.0): what MRRocket's config serves under `paywalls` (one per placement, built\n * in MRRocket › Conversion › Paywalls) and a small controller that walks the person through its pages, page after page,\n * until the last one hands the chosen plan to the app. No DOM and no React: the web renderer (`mountPaywall` in\n * flows-ui.ts, loaded only when a paywall opens) and the React Native one (native/flows-view.ts) draw `view()` and\n * call the actions.\n *\n * MRRocket never takes a payment. The last page's button calls the app's `onPurchase(plan)`, which buys through the\n * app's own store code (StoreKit or Play through RevenueCat or Adapty, or Stripe Checkout on the web); only when it\n * resolves is the purchase recorded. It throwing keeps the paywall open with an error, resolving `false` (the person\n * closed the store sheet) keeps it open without one.\n *\n * A running test on the paywall (`experiment`) gives each person their variant's pages, by the same stable hash as\n * every other test (`assign`), and records `$exposure` when the paywall opens. What it records, as normal events under\n * the SDK's analytics consent:\n * - `$paywall` `{paywall, placement, variant, page, index, action: \"page\"}` the first time each page shows in a run,\n * so MRRocket shows how many reach page 2 and 3;\n * - `{action: \"purchased\", plan}` after `onPurchase` went through (a test's purchase goal counts it), and\n * `{action: \"closed\", index}` when the person closed it.\n */\nimport { type CockpitConfig, type VariantId, assign } from \"./core\";\n\nexport const PAYWALL_EVENT = \"$paywall\";\n\n/** One page of a paywall. Plans pages list the plans and buy; the others say why and go on. */\nexport interface PaywallPage {\n id: string;\n /** \"benefits\": a list of what the person gets; \"proof\": quotes of happy customers; \"plans\": the plans and the buy button. */\n kind: \"benefits\" | \"proof\" | \"plans\";\n title: string;\n body?: string;\n /** Benefits: one line each. */\n items?: string[];\n /** Proof: what customers said, and who. */\n quotes?: { text: string; by?: string }[];\n /** The button: \"Continue\" on the way, the buy button on the plans page (\"Start free trial\"). */\n button?: string;\n}\n\n/** A plan the paywall offers. `id` is what the app buys: the store product id or the Stripe price, as set in MRRocket. */\nexport interface PaywallPlan {\n id: string;\n name: string;\n /** As shown (\"€59.99 / year\"). The app's own store prices win when it passes `plans`. */\n price: string;\n /** A line under it (\"€5 a month\", \"7 days free\"). */\n note?: string;\n}\n\n/** A published paywall as the config serves it. */\nexport interface PaywallConfig {\n id: string;\n /** The placement's key (`onboarding_end`), what the app opens: `paywall.open(\"onboarding_end\")`. */\n placement: string;\n /** The running test on it, a key of `experiments`; each of its variants has pages under `variants`. */\n experiment?: string;\n /** Control (the live paywall) first. */\n variants: { id: VariantId; pages: PaywallPage[] }[];\n plans: PaywallPlan[];\n /** The plan picked before the person picks one. */\n highlight?: string;\n}\n\nexport interface PaywallResult {\n /**\n * \"purchased\": `onPurchase` went through; \"chosen\": the person picked a plan and there is no `onPurchase`, so the app\n * buys it now (the store's purchase still counts for a test through RevenueCat or Adapty); \"closed\": the person\n * closed it (or there was no paywall).\n */\n outcome: \"purchased\" | \"chosen\" | \"closed\";\n plan?: PaywallPlan;\n paywall?: string;\n variant?: VariantId;\n /** The page the person was on, from 0. */\n index: number;\n}\n\nexport interface PaywallOptions {\n /**\n * Buy the plan in your code (RevenueCat `purchasePackage`, Adapty `makePurchase`, Stripe Checkout). Throwing keeps\n * the paywall open with an error; `false` keeps it open without one (the person cancelled the store sheet).\n */\n onPurchase?: (plan: PaywallPlan) => unknown;\n /** Your store's plans with their local prices, by the same ids; they replace what MRRocket has for them. */\n plans?: PaywallPlan[];\n onClose?: (result: PaywallResult) => void;\n}\n\nexport interface PaywallView {\n paywall: PaywallConfig;\n variant: VariantId;\n page: PaywallPage;\n index: number;\n total: number;\n last: boolean;\n plans: PaywallPlan[];\n /** The picked plan's id. */\n plan?: string;\n busy: boolean;\n error?: string;\n}\n\nexport interface PaywallEnv {\n config: () => CockpitConfig | null;\n distinctId: () => string;\n /** Records an event under the SDK's consent. */\n track: (event: string, props?: Record<string, unknown>) => void;\n}\n\nexport interface PaywallHost {\n /** Opens the paywall of a placement; resolves when it is bought or closed (at once with \"closed\" when there is none). */\n open(placement: string, options?: PaywallOptions): Promise<PaywallResult>;\n view(): PaywallView | null;\n subscribe(listener: () => void): () => void;\n next(): void;\n back(): void;\n pick(planId: string): void;\n close(): void;\n}\n\n/** The pages of a person's variant: the test's when it runs (or shipped), else control's. */\nexport const paywallPages = (config: CockpitConfig | null, w: PaywallConfig, distinctId: string) => {\n const e = w.experiment ? config?.experiments.find((x) => x.key === w.experiment) : undefined;\n const id = e ? assign(e, distinctId) : w.variants[0]?.id;\n const v = w.variants.find((x) => x.id === id) ?? w.variants[0];\n return v ? { variant: v.id, pages: v.pages, test: e?.status === \"running\" && !e.winner ? e.key : undefined } : null;\n};\n\nexport const createPaywallHost = (env: PaywallEnv): PaywallHost => {\n let run: {\n w: PaywallConfig;\n variant: VariantId;\n pages: PaywallPage[];\n plans: PaywallPlan[];\n index: number;\n plan?: string;\n busy: boolean;\n error?: string;\n seen: Set<number>;\n options: PaywallOptions;\n resolve: (r: PaywallResult) => void;\n promise: Promise<PaywallResult>;\n } | null = null;\n const listeners = new Set<() => void>();\n const emit = () => {\n for (const l of [...listeners]) {\n try {\n l();\n } catch {\n // One renderer must not stop another.\n }\n }\n };\n const base = () => (run ? { paywall: run.w.id, placement: run.w.placement, variant: run.variant } : {});\n const show = () => {\n if (run && !run.seen.has(run.index)) {\n run.seen.add(run.index);\n env.track(PAYWALL_EVENT, { ...base(), page: run.pages[run.index].id, index: run.index, action: \"page\" });\n }\n emit();\n };\n const end = (r: PaywallResult) => {\n const c = run;\n if (!c) return;\n run = null;\n if (r.outcome === \"closed\") c.options.onClose?.(r);\n c.resolve(r);\n emit();\n };\n const result = (outcome: PaywallResult[\"outcome\"]): PaywallResult => ({\n outcome,\n index: run?.index ?? 0,\n ...(run ? { paywall: run.w.id, variant: run.variant } : {}),\n ...(outcome !== \"closed\" && run ? { plan: run.plans.find((p) => p.id === run!.plan) } : {}),\n });\n\n const buy = () => {\n const c = run;\n const plan = c?.plans.find((p) => p.id === c.plan);\n if (!c || !plan || c.busy) return;\n if (!c.options.onPurchase) return end(result(\"chosen\"));\n c.busy = true;\n c.error = undefined;\n emit();\n Promise.resolve()\n .then(() => c.options.onPurchase?.(plan))\n .then(\n (ok) => {\n c.busy = false;\n if (run !== c) return;\n if (ok === false) return emit();\n env.track(PAYWALL_EVENT, { ...base(), page: c.pages[c.index].id, index: c.index, plan: plan.id, action: \"purchased\" });\n end(result(\"purchased\"));\n },\n () => {\n c.busy = false;\n c.error = \"The purchase did not go through. Please try again.\";\n emit();\n },\n );\n };\n\n return {\n open: (placement, options = {}) => {\n if (run) return run.promise;\n const config = env.config();\n const w = config?.paywalls?.find((x) => x.placement === placement);\n const picked = w ? paywallPages(config, w, env.distinctId()) : null;\n if (!w || !picked?.pages.length) return Promise.resolve({ outcome: \"closed\", index: 0 });\n // The app's store prices replace MRRocket's for the same plans.\n const own = new Map((options.plans ?? []).map((p) => [p.id, p]));\n const plans = w.plans.map((p) => own.get(p.id) ?? p);\n let resolve: (r: PaywallResult) => void = () => {};\n const promise = new Promise<PaywallResult>((r) => (resolve = r));\n const plan = plans.some((p) => p.id === w.highlight) ? w.highlight : plans[0]?.id;\n run = { w, variant: picked.variant, pages: picked.pages, plans, index: 0, plan, busy: false, seen: new Set(), options, resolve, promise };\n if (picked.test) env.track(\"$exposure\", { experiment: picked.test, variant: picked.variant });\n show();\n return promise;\n },\n view: () =>\n run\n ? {\n paywall: run.w,\n variant: run.variant,\n page: run.pages[run.index],\n index: run.index,\n total: run.pages.length,\n last: run.index === run.pages.length - 1,\n plans: run.plans,\n plan: run.plan,\n busy: run.busy,\n error: run.error,\n }\n : null,\n subscribe: (l) => {\n listeners.add(l);\n return () => listeners.delete(l);\n },\n next: () => {\n if (!run || run.busy) return;\n // A plans page or the last page buys; any other page goes on.\n if (run.pages[run.index].kind === \"plans\" || run.index === run.pages.length - 1) return buy();\n run.index++;\n run.error = undefined;\n show();\n },\n back: () => {\n if (!run || run.busy || !run.index) return;\n run.index--;\n run.error = undefined;\n emit();\n },\n pick: (id) => {\n if (!run || run.busy || !run.plans.some((p) => p.id === id)) return;\n run.plan = id;\n run.error = undefined;\n emit();\n },\n close: () => {\n if (!run || run.busy) return;\n env.track(PAYWALL_EVENT, { ...base(), index: run.index, action: \"closed\" });\n end(result(\"closed\"));\n },\n };\n};\n"],"mappings":"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","names":[]}
|