@mrrocket/sdk 0.15.0 → 0.17.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 +29 -0
- package/README.md +47 -3
- package/dist/api-client.d.ts +12 -5
- package/dist/api-client.js +12 -8
- package/dist/api-client.js.map +1 -1
- package/dist/api.d.ts +4 -1
- package/dist/api.js +2 -1
- package/dist/api.js.map +1 -1
- package/dist/batch.d.ts +6 -4
- package/dist/batch.js +3 -3
- package/dist/batch.js.map +1 -1
- package/dist/browser/mrrocket-flows.js +9 -2
- package/dist/browser/mrrocket-forms.js +1 -1
- package/dist/browser/mrrocket-messenger.js +42 -43
- package/dist/browser/mrrocket-push.js +1 -1
- package/dist/browser/mrrocket-replay.js +3 -3
- package/dist/browser/mrrocket.js +1 -1
- package/dist/client.d.ts +10 -2
- package/dist/client.js +36 -15
- package/dist/client.js.map +1 -1
- package/dist/context.d.ts +5 -1
- package/dist/context.js.map +1 -1
- package/dist/core.d.ts +1 -1
- package/dist/core.js +1 -1
- package/dist/core.js.map +1 -1
- package/dist/errors.d.ts +5 -1
- package/dist/errors.js +8 -2
- package/dist/errors.js.map +1 -1
- package/dist/flows-ui.d.ts +1 -1
- package/dist/flows-ui.js +64 -14
- package/dist/flows-ui.js.map +1 -1
- package/dist/flows-web.d.ts +2 -0
- package/dist/flows-web.js +4 -1
- package/dist/flows-web.js.map +1 -1
- package/dist/flows.d.ts +59 -3
- package/dist/flows.js +88 -7
- package/dist/flows.js.map +1 -1
- package/dist/forms.d.ts +4 -0
- package/dist/forms.js +4 -1
- package/dist/forms.js.map +1 -1
- package/dist/identity.d.ts +42 -0
- package/dist/identity.js +24 -0
- package/dist/identity.js.map +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/messenger-ui.d.ts +12 -2
- package/dist/messenger-ui.js +58 -47
- package/dist/messenger-ui.js.map +1 -1
- package/dist/messenger.js +3 -2
- package/dist/messenger.js.map +1 -1
- package/dist/push-web.d.ts +2 -13
- package/dist/push-web.js +4 -2
- package/dist/push-web.js.map +1 -1
- package/dist/react-native/index.d.cts +105 -14
- package/dist/react-native/index.d.ts +105 -14
- package/dist/react-native/index.js +238 -55
- package/dist/react-native/index.js.map +1 -1
- package/dist/replay.d.ts +4 -1
- package/dist/replay.js +8 -2
- package/dist/replay.js.map +1 -1
- package/dist/server.d.ts +2 -0
- package/dist/server.js +2 -0
- package/dist/server.js.map +1 -1
- package/dist/styles-css.d.ts +1 -1
- package/dist/styles-css.js +40 -41
- package/dist/styles-css.js.map +1 -1
- package/dist/user-hash.d.ts +27 -0
- package/dist/user-hash.js +24 -0
- package/dist/user-hash.js.map +1 -0
- package/package.json +5 -1
package/dist/push-web.d.ts
CHANGED
|
@@ -1,16 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Web push in the browser (SDK 0.7.0): signs the signed-in person up for push notifications from the product's Lifecycle
|
|
3
|
-
* journeys. Loaded only when the site calls `push.subscribe()` (a dynamic import in Next.js, `sdk-push.js` for the script
|
|
4
|
-
* tag), so pages that never ask carry none of it.
|
|
5
|
-
*
|
|
6
|
-
* 1. The browser's permission (asked only from a click, and only when the site says so; `ask: false` only re-sends an
|
|
7
|
-
* existing subscription).
|
|
8
|
-
* 2. The service worker the site hosts (`/mrrocket-sw.js`, a copy of the file MRRocket serves), registered with the
|
|
9
|
-
* public key and MRRocket's address in its URL, so it can report opened notifications. A site with its own service
|
|
10
|
-
* worker imports ours there and passes `serviceWorker: false`.
|
|
11
|
-
* 3. The workspace's VAPID key from `GET /api/sdk/push`, the browser's subscription with it, and the subscription sent to
|
|
12
|
-
* `POST /api/sdk/push` for this person. A subscription made with another key is replaced.
|
|
13
|
-
*/
|
|
14
1
|
export type PushStatus = "subscribed" | "denied" | "not-asked" | "unsupported" | "not-signed-in" | "error";
|
|
15
2
|
export interface PushResult {
|
|
16
3
|
status: PushStatus;
|
|
@@ -31,6 +18,8 @@ export interface WebPushContext {
|
|
|
31
18
|
apiKey: string;
|
|
32
19
|
/** The identify() id; push needs a signed-in person. */
|
|
33
20
|
distinctId: string | undefined;
|
|
21
|
+
/** The identity hash of that id (0.16.0), when the site gave one to identify. */
|
|
22
|
+
userHash?: string;
|
|
34
23
|
/** The permission, asked synchronously by the caller inside the click (Safari needs that), or the current one. */
|
|
35
24
|
permission?: Promise<NotificationPermission>;
|
|
36
25
|
}
|
package/dist/push-web.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// src/push-web.ts
|
|
2
|
+
import { warnUnsigned } from "./identity.js";
|
|
2
3
|
var b64urlBytes = (s) => {
|
|
3
4
|
const bin = atob(s.replace(/-/g, "+").replace(/_/g, "/").padEnd(Math.ceil(s.length / 4) * 4, "="));
|
|
4
5
|
const out = new Uint8Array(bin.length);
|
|
@@ -49,8 +50,9 @@ var subscribeWebPush = async (ctx, options = {}) => {
|
|
|
49
50
|
const saved = await fetch(`${ctx.host}/api/sdk/push`, {
|
|
50
51
|
method: "POST",
|
|
51
52
|
headers: { "content-type": "application/json" },
|
|
52
|
-
body: JSON.stringify({ key: ctx.apiKey, distinctId: ctx.distinctId, platform: "web", subscription: sub.toJSON(), host: ctx.host })
|
|
53
|
+
body: JSON.stringify({ key: ctx.apiKey, distinctId: ctx.distinctId, userHash: ctx.userHash, platform: "web", subscription: sub.toJSON(), host: ctx.host })
|
|
53
54
|
});
|
|
55
|
+
warnUnsigned(saved.status);
|
|
54
56
|
return saved.ok ? { status: "subscribed" } : { status: "error", reason: `MRRocket answered ${saved.status}.` };
|
|
55
57
|
} catch (e) {
|
|
56
58
|
return { status: "error", reason: e instanceof Error ? e.message : String(e) };
|
|
@@ -65,7 +67,7 @@ var unsubscribeWebPush = async (ctx) => {
|
|
|
65
67
|
await fetch(`${ctx.host}/api/sdk/push`, {
|
|
66
68
|
method: "POST",
|
|
67
69
|
headers: { "content-type": "application/json" },
|
|
68
|
-
body: JSON.stringify({ key: ctx.apiKey, distinctId: ctx.distinctId, platform: "web", remove: true, endpoint: sub.endpoint })
|
|
70
|
+
body: JSON.stringify({ key: ctx.apiKey, distinctId: ctx.distinctId, userHash: ctx.userHash, platform: "web", remove: true, endpoint: sub.endpoint })
|
|
69
71
|
}).catch(() => null);
|
|
70
72
|
return await sub.unsubscribe();
|
|
71
73
|
} catch {
|
package/dist/push-web.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/push-web.ts"],"sourcesContent":["/**\n * Web push in the browser (SDK 0.7.0): signs the signed-in person up for push notifications from the product's Lifecycle\n * journeys. Loaded only when the site calls `push.subscribe()` (a dynamic import in Next.js, `sdk-push.js` for the script\n * tag), so pages that never ask carry none of it.\n *\n * 1. The browser's permission (asked only from a click, and only when the site says so; `ask: false` only re-sends an\n * existing subscription).\n * 2. The service worker the site hosts (`/mrrocket-sw.js`, a copy of the file MRRocket serves), registered with the\n * public key and MRRocket's address in its URL, so it can report opened notifications. A site with its own service\n * worker imports ours there and passes `serviceWorker: false`.\n * 3. The workspace's VAPID key from `GET /api/sdk/push`, the browser's subscription with it, and the subscription sent to\n * `POST /api/sdk/push` for this person. A subscription made with another key is replaced.\n */\nexport type PushStatus = \"subscribed\" | \"denied\" | \"not-asked\" | \"unsupported\" | \"not-signed-in\" | \"error\";\n\nexport interface PushResult {\n status: PushStatus;\n /** What went wrong, for \"error\" and \"unsupported\". */\n reason?: string;\n}\n\nexport interface WebPushOptions {\n /**\n * The service worker's path on your site (default \"/mrrocket-sw.js\"), or false when your site already has one that\n * imports ours (`importScripts(\"/mrrocket-sw.js\")`): the SDK then uses the one that is active.\n */\n serviceWorker?: string | false;\n /** Ask for the permission when it was not given yet (default true). False: only re-send an existing subscription. */\n ask?: boolean;\n}\n\nexport interface WebPushContext {\n host: string;\n apiKey: string;\n /** The identify() id; push needs a signed-in person. */\n distinctId: string | undefined;\n /** The permission, asked synchronously by the caller inside the click (Safari needs that), or the current one. */\n permission?: Promise<NotificationPermission>;\n}\n\nconst b64urlBytes = (s: string) => {\n const bin = atob(s.replace(/-/g, \"+\").replace(/_/g, \"/\").padEnd(Math.ceil(s.length / 4) * 4, \"=\"));\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n return out;\n};\n\nconst sameKey = (a: ArrayBuffer | null | undefined, b: Uint8Array) => {\n if (!a) return false;\n const x = new Uint8Array(a);\n return x.length === b.length && x.every((v, i) => v === b[i]);\n};\n\n/** True where the browser can receive web push (Safari 16.4+ only for sites added to the Home Screen on iOS). */\nexport const webPushSupported = () =>\n typeof window !== \"undefined\" && \"serviceWorker\" in navigator && \"PushManager\" in window && \"Notification\" in window;\n\n/** The permission, asked right away when `ask` and not given yet. Call it synchronously in the click handler. */\nexport const askPermission = (ask = true): Promise<NotificationPermission> => {\n if (!webPushSupported()) return Promise.resolve(\"denied\");\n if (Notification.permission !== \"default\" || !ask) return Promise.resolve(Notification.permission);\n try {\n return Promise.resolve(Notification.requestPermission());\n } catch {\n return Promise.resolve(\"denied\");\n }\n};\n\nconst registration = async (host: string, apiKey: string, sw: string | false) => {\n if (sw !== false) {\n const url = `${sw}?key=${encodeURIComponent(apiKey)}&host=${encodeURIComponent(host)}`;\n await navigator.serviceWorker.register(url);\n }\n return navigator.serviceWorker.ready;\n};\n\nexport const subscribeWebPush = async (ctx: WebPushContext, options: WebPushOptions = {}): Promise<PushResult> => {\n if (!webPushSupported()) return { status: \"unsupported\", reason: \"This browser has no web push.\" };\n if (!ctx.distinctId) return { status: \"not-signed-in\" };\n const ask = options.ask !== false;\n const permission = await (ctx.permission ?? askPermission(ask));\n if (permission === \"denied\") return { status: \"denied\" };\n if (permission !== \"granted\") return { status: ask ? \"denied\" : \"not-asked\" };\n try {\n const r = await fetch(`${ctx.host}/api/sdk/push?key=${encodeURIComponent(ctx.apiKey)}`);\n const vapid = r.ok ? ((await r.json()) as { web?: { vapidKey?: string } }).web?.vapidKey : undefined;\n if (!vapid) return { status: \"error\", reason: `MRRocket answered ${r.status} for the push key.` };\n const key = b64urlBytes(vapid);\n const reg = await registration(ctx.host, ctx.apiKey, options.serviceWorker ?? \"/mrrocket-sw.js\");\n let sub = await reg.pushManager.getSubscription();\n if (sub && !sameKey(sub.options?.applicationServerKey, key)) {\n await sub.unsubscribe().catch(() => false);\n sub = null;\n }\n sub ??= await reg.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: key });\n const saved = await fetch(`${ctx.host}/api/sdk/push`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ key: ctx.apiKey, distinctId: ctx.distinctId, platform: \"web\", subscription: sub.toJSON(), host: ctx.host }),\n });\n return saved.ok ? { status: \"subscribed\" } : { status: \"error\", reason: `MRRocket answered ${saved.status}.` };\n } catch (e) {\n return { status: \"error\", reason: e instanceof Error ? e.message : String(e) };\n }\n};\n\n/** Ends this browser's subscription and tells MRRocket (sign-out, or the person turned push off). */\nexport const unsubscribeWebPush = async (ctx: WebPushContext): Promise<boolean> => {\n if (!webPushSupported()) return false;\n try {\n const reg = await navigator.serviceWorker.getRegistration();\n const sub = await reg?.pushManager.getSubscription();\n if (!sub) return false;\n await fetch(`${ctx.host}/api/sdk/push`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ key: ctx.apiKey, distinctId: ctx.distinctId, platform: \"web\", remove: true, endpoint: sub.endpoint }),\n }).catch(() => null);\n return await sub.unsubscribe();\n } catch {\n return false;\n }\n};\n"],"mappings":";
|
|
1
|
+
{"version":3,"sources":["../src/push-web.ts"],"sourcesContent":["/**\n * Web push in the browser (SDK 0.7.0): signs the signed-in person up for push notifications from the product's Lifecycle\n * journeys. Loaded only when the site calls `push.subscribe()` (a dynamic import in Next.js, `sdk-push.js` for the script\n * tag), so pages that never ask carry none of it.\n *\n * 1. The browser's permission (asked only from a click, and only when the site says so; `ask: false` only re-sends an\n * existing subscription).\n * 2. The service worker the site hosts (`/mrrocket-sw.js`, a copy of the file MRRocket serves), registered with the\n * public key and MRRocket's address in its URL, so it can report opened notifications. A site with its own service\n * worker imports ours there and passes `serviceWorker: false`.\n * 3. The workspace's VAPID key from `GET /api/sdk/push`, the browser's subscription with it, and the subscription sent to\n * `POST /api/sdk/push` for this person. A subscription made with another key is replaced.\n */\nimport { warnUnsigned } from \"./identity\";\n\nexport type PushStatus = \"subscribed\" | \"denied\" | \"not-asked\" | \"unsupported\" | \"not-signed-in\" | \"error\";\n\nexport interface PushResult {\n status: PushStatus;\n /** What went wrong, for \"error\" and \"unsupported\". */\n reason?: string;\n}\n\nexport interface WebPushOptions {\n /**\n * The service worker's path on your site (default \"/mrrocket-sw.js\"), or false when your site already has one that\n * imports ours (`importScripts(\"/mrrocket-sw.js\")`): the SDK then uses the one that is active.\n */\n serviceWorker?: string | false;\n /** Ask for the permission when it was not given yet (default true). False: only re-send an existing subscription. */\n ask?: boolean;\n}\n\nexport interface WebPushContext {\n host: string;\n apiKey: string;\n /** The identify() id; push needs a signed-in person. */\n distinctId: string | undefined;\n /** The identity hash of that id (0.16.0), when the site gave one to identify. */\n userHash?: string;\n /** The permission, asked synchronously by the caller inside the click (Safari needs that), or the current one. */\n permission?: Promise<NotificationPermission>;\n}\n\nconst b64urlBytes = (s: string) => {\n const bin = atob(s.replace(/-/g, \"+\").replace(/_/g, \"/\").padEnd(Math.ceil(s.length / 4) * 4, \"=\"));\n const out = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);\n return out;\n};\n\nconst sameKey = (a: ArrayBuffer | null | undefined, b: Uint8Array) => {\n if (!a) return false;\n const x = new Uint8Array(a);\n return x.length === b.length && x.every((v, i) => v === b[i]);\n};\n\n/** True where the browser can receive web push (Safari 16.4+ only for sites added to the Home Screen on iOS). */\nexport const webPushSupported = () =>\n typeof window !== \"undefined\" && \"serviceWorker\" in navigator && \"PushManager\" in window && \"Notification\" in window;\n\n/** The permission, asked right away when `ask` and not given yet. Call it synchronously in the click handler. */\nexport const askPermission = (ask = true): Promise<NotificationPermission> => {\n if (!webPushSupported()) return Promise.resolve(\"denied\");\n if (Notification.permission !== \"default\" || !ask) return Promise.resolve(Notification.permission);\n try {\n return Promise.resolve(Notification.requestPermission());\n } catch {\n return Promise.resolve(\"denied\");\n }\n};\n\nconst registration = async (host: string, apiKey: string, sw: string | false) => {\n if (sw !== false) {\n const url = `${sw}?key=${encodeURIComponent(apiKey)}&host=${encodeURIComponent(host)}`;\n await navigator.serviceWorker.register(url);\n }\n return navigator.serviceWorker.ready;\n};\n\nexport const subscribeWebPush = async (ctx: WebPushContext, options: WebPushOptions = {}): Promise<PushResult> => {\n if (!webPushSupported()) return { status: \"unsupported\", reason: \"This browser has no web push.\" };\n if (!ctx.distinctId) return { status: \"not-signed-in\" };\n const ask = options.ask !== false;\n const permission = await (ctx.permission ?? askPermission(ask));\n if (permission === \"denied\") return { status: \"denied\" };\n if (permission !== \"granted\") return { status: ask ? \"denied\" : \"not-asked\" };\n try {\n const r = await fetch(`${ctx.host}/api/sdk/push?key=${encodeURIComponent(ctx.apiKey)}`);\n const vapid = r.ok ? ((await r.json()) as { web?: { vapidKey?: string } }).web?.vapidKey : undefined;\n if (!vapid) return { status: \"error\", reason: `MRRocket answered ${r.status} for the push key.` };\n const key = b64urlBytes(vapid);\n const reg = await registration(ctx.host, ctx.apiKey, options.serviceWorker ?? \"/mrrocket-sw.js\");\n let sub = await reg.pushManager.getSubscription();\n if (sub && !sameKey(sub.options?.applicationServerKey, key)) {\n await sub.unsubscribe().catch(() => false);\n sub = null;\n }\n sub ??= await reg.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: key });\n const saved = await fetch(`${ctx.host}/api/sdk/push`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ key: ctx.apiKey, distinctId: ctx.distinctId, userHash: ctx.userHash, platform: \"web\", subscription: sub.toJSON(), host: ctx.host }),\n });\n warnUnsigned(saved.status);\n return saved.ok ? { status: \"subscribed\" } : { status: \"error\", reason: `MRRocket answered ${saved.status}.` };\n } catch (e) {\n return { status: \"error\", reason: e instanceof Error ? e.message : String(e) };\n }\n};\n\n/** Ends this browser's subscription and tells MRRocket (sign-out, or the person turned push off). */\nexport const unsubscribeWebPush = async (ctx: WebPushContext): Promise<boolean> => {\n if (!webPushSupported()) return false;\n try {\n const reg = await navigator.serviceWorker.getRegistration();\n const sub = await reg?.pushManager.getSubscription();\n if (!sub) return false;\n await fetch(`${ctx.host}/api/sdk/push`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ key: ctx.apiKey, distinctId: ctx.distinctId, userHash: ctx.userHash, platform: \"web\", remove: true, endpoint: sub.endpoint }),\n }).catch(() => null);\n return await sub.unsubscribe();\n } catch {\n return false;\n }\n};\n"],"mappings":"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","names":[]}
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
* The SDK's fetch client and the shapes it reads, without React, so the script tag's widgets (src/script) use it too.
|
|
6
|
-
* `./api` re-exports all of it with the `useApi` hook.
|
|
7
|
-
*/
|
|
8
4
|
type PostStatus = "review" | "planned" | "progress" | "shipped";
|
|
9
5
|
interface FeedbackBoardInfo {
|
|
10
6
|
id: string;
|
|
@@ -180,8 +176,14 @@ interface MessengerLook {
|
|
|
180
176
|
header?: "plain" | "color";
|
|
181
177
|
/** "Powered by MRRocket" at the bottom; the server sends false only on plans that may hide it (from 0.15). */
|
|
182
178
|
branding?: boolean;
|
|
183
|
-
/** The
|
|
179
|
+
/** The logo at the top left of Home: the one uploaded in MRRocket, else the product's favicon; without it the title's first letter. */
|
|
184
180
|
logo?: string;
|
|
181
|
+
/** The product's favicon, the round mark in the chat's head (from 0.17; without it the title's first letter). */
|
|
182
|
+
icon?: string;
|
|
183
|
+
/** The AI agent's name on its answers (from 0.17; empty or left out: "AI agent"). */
|
|
184
|
+
agentName?: string;
|
|
185
|
+
/** The AI agent's picture beside its answers (from 0.17). */
|
|
186
|
+
agentPhoto?: string;
|
|
185
187
|
}
|
|
186
188
|
/** The visitor's newest chat (open or closed) and its messages. */
|
|
187
189
|
interface ChatThread {
|
|
@@ -201,6 +203,11 @@ interface Conn {
|
|
|
201
203
|
id?: string;
|
|
202
204
|
[key: string]: unknown;
|
|
203
205
|
};
|
|
206
|
+
/**
|
|
207
|
+
* The identity hash of `distinctId` (0.16.0, see identity.ts), sent with every request that names the visitor: a
|
|
208
|
+
* JSON field next to `distinctId`, a query parameter, a form field. Leave it out for any other id.
|
|
209
|
+
*/
|
|
210
|
+
userHash?: string;
|
|
204
211
|
}
|
|
205
212
|
/** The small fetch client behind the widgets. Every read takes an AbortSignal so unmounting cancels it. */
|
|
206
213
|
declare const createApi: (c: Conn) => {
|
|
@@ -273,7 +280,8 @@ type Api = ReturnType<typeof createApi>;
|
|
|
273
280
|
* `onCancel`, and only after they succeed is the outcome recorded.
|
|
274
281
|
* - `$onboarding` `{flow, step?, action: "started" | "shown" | "completed" | "dismissed"}`; since 0.11.0 a wait records
|
|
275
282
|
* "shown" when the person reaches it and "completed" when it is over, a condition `{action: "answered", answer: "yes"
|
|
276
|
-
* | "no"}` once it is decided.
|
|
283
|
+
* | "no"}` once it is decided; since 0.17.0 a tour a checklist task started records `{action: "started", from}` with
|
|
284
|
+
* the checklist's id.
|
|
277
285
|
* - `$inapp` `{message, journey, action: "shown" | "clicked" | "dismissed"}`; the journey moves the person on.
|
|
278
286
|
*/
|
|
279
287
|
|
|
@@ -343,6 +351,17 @@ interface CancelFlowConfig {
|
|
|
343
351
|
offersFor: "business";
|
|
344
352
|
}
|
|
345
353
|
type OnboardingStepKind = "tour" | "checklist" | "modal";
|
|
354
|
+
/** 0.17.0: where a tour bubble sits next to its element; "auto" (and no value) puts it below, else above, as before. */
|
|
355
|
+
type TourPlacement = "auto" | "top" | "bottom" | "left" | "right";
|
|
356
|
+
/**
|
|
357
|
+
* 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
|
|
358
|
+
* through their privacy-friendly players, see `videoEmbed`) or a video file (mp4, webm).
|
|
359
|
+
*/
|
|
360
|
+
interface FlowMedia {
|
|
361
|
+
kind: "image" | "video";
|
|
362
|
+
url: string;
|
|
363
|
+
alt?: string;
|
|
364
|
+
}
|
|
346
365
|
interface OnboardingStep {
|
|
347
366
|
id: string;
|
|
348
367
|
kind: OnboardingStepKind;
|
|
@@ -353,6 +372,14 @@ interface OnboardingStep {
|
|
|
353
372
|
target?: string;
|
|
354
373
|
/** Checklist items: the event that ticks them (`track("<event>")`). Without it the person ticks it themselves. */
|
|
355
374
|
event?: string;
|
|
375
|
+
/** 0.17.0, tour steps: which side of the element the bubble takes (flips when there is no room). */
|
|
376
|
+
placement?: TourPlacement;
|
|
377
|
+
/** 0.17.0, tour steps and modals: a picture or a video above the text. */
|
|
378
|
+
media?: FlowMedia;
|
|
379
|
+
/** 0.17.0, modals: where the button goes (http(s) or a path of the app). */
|
|
380
|
+
url?: string;
|
|
381
|
+
/** 0.17.0, checklist items: the id of a tour (another flow) the item starts; finishing the tour ticks the item. */
|
|
382
|
+
startsTour?: string;
|
|
356
383
|
}
|
|
357
384
|
/** A pause in a flow (0.11.0): the next step shows this long after the one before was done. */
|
|
358
385
|
interface OnboardingWait {
|
|
@@ -378,9 +405,15 @@ type OnboardingNode = OnboardingStep | OnboardingWait | OnboardingCondition;
|
|
|
378
405
|
/** A live onboarding flow's in-app steps (MRRocket › Conversion › Onboarding); its emails go out from MRRocket. */
|
|
379
406
|
interface OnboardingFlow {
|
|
380
407
|
id: string;
|
|
408
|
+
/** A checklist's heading; for tours and modals the name MRRocket knows them by. */
|
|
381
409
|
name: string;
|
|
382
|
-
/**
|
|
410
|
+
/**
|
|
411
|
+
* When it starts: an event's words ("Signed up", "First export"), "First launch" / "First visit", since 0.17.0 a page
|
|
412
|
+
* of the app ("/settings", "/projects/*"), or "From a checklist" (only a checklist task starts it).
|
|
413
|
+
*/
|
|
383
414
|
trigger: string;
|
|
415
|
+
/** 0.17.0: "signed_in" starts it on its own only for someone the app identified. */
|
|
416
|
+
audience?: "signed_in";
|
|
384
417
|
/** What every version shows: with `nodes`, only the in-app steps before the first wait or condition. */
|
|
385
418
|
steps: OnboardingStep[];
|
|
386
419
|
/** 0.11.0+: the whole flow with its waits and conditions. Read instead of `steps` when it is there. */
|
|
@@ -459,12 +492,15 @@ type FlowView = {
|
|
|
459
492
|
index: number;
|
|
460
493
|
total: number;
|
|
461
494
|
last: boolean;
|
|
462
|
-
}
|
|
495
|
+
}
|
|
496
|
+
/** `tour`: the tour the item starts (0.17.0), when the config has it. */
|
|
497
|
+
| {
|
|
463
498
|
type: "checklist";
|
|
464
499
|
flow: OnboardingFlow;
|
|
465
500
|
items: {
|
|
466
501
|
step: OnboardingStep;
|
|
467
502
|
done: boolean;
|
|
503
|
+
tour?: OnboardingFlow;
|
|
468
504
|
}[];
|
|
469
505
|
} | {
|
|
470
506
|
type: "message";
|
|
@@ -492,10 +528,12 @@ interface FlowsController {
|
|
|
492
528
|
confirm(comment?: string): void;
|
|
493
529
|
close(): void;
|
|
494
530
|
};
|
|
531
|
+
/** `startTour(stepId)` (0.17.0): the tour a checklist item starts; the checklist comes back when it ends. */
|
|
495
532
|
onboarding: {
|
|
496
533
|
next(): void;
|
|
497
534
|
check(stepId: string): void;
|
|
498
535
|
dismiss(): void;
|
|
536
|
+
startTour(stepId: string): boolean;
|
|
499
537
|
};
|
|
500
538
|
message: {
|
|
501
539
|
click(): void;
|
|
@@ -620,7 +658,7 @@ interface PaywallHost {
|
|
|
620
658
|
|
|
621
659
|
type VariantId = string;
|
|
622
660
|
/** This build of the SDK. Sent with the config request so MRRocket knows what the site runs (error tracking, flags). */
|
|
623
|
-
declare const SDK_VERSION = "0.
|
|
661
|
+
declare const SDK_VERSION = "0.17.0";
|
|
624
662
|
/** One variant of an experiment. `changes` replaces texts that the page marks with `<Text id>`. */
|
|
625
663
|
interface VariantConfig {
|
|
626
664
|
id: VariantId;
|
|
@@ -761,6 +799,27 @@ interface AppContext {
|
|
|
761
799
|
locale?: string;
|
|
762
800
|
}
|
|
763
801
|
|
|
802
|
+
/**
|
|
803
|
+
* Identity verification (0.16.0), the browser and app side. After `identify(id)` the SDK's id is your app's own user
|
|
804
|
+
* id, and that id is no secret (a Clerk id shows in URLs and logs), so on its own it proves nothing: anyone who knows it
|
|
805
|
+
* could read that person's support chat or post as them. Your server signs the id with the product's server key
|
|
806
|
+
* (`userHash(user.id)` from `@mrrocket/sdk/next/server`) and you hand the result to `identify(id, props, { hash })`.
|
|
807
|
+
* From then on every request for that id carries it as `userHash` (a JSON field, a query parameter or a form field), and
|
|
808
|
+
* MRRocket checks it.
|
|
809
|
+
*
|
|
810
|
+
* The hash belongs to one id: it is forgotten on `reset()` (sign-out) and when `identify` is called for another id
|
|
811
|
+
* without a hash, so it never goes out with someone else's id (MRRocket refuses a hash that does not match with 401).
|
|
812
|
+
* Shared by the Next.js provider, the script tag and React Native: no React, no browser APIs.
|
|
813
|
+
*/
|
|
814
|
+
/** The third argument of `identify`. */
|
|
815
|
+
interface IdentifyOptions {
|
|
816
|
+
/**
|
|
817
|
+
* The id signed by your server: `await userHash(user.id)` from `@mrrocket/sdk/next/server` (or the same HMAC in any
|
|
818
|
+
* language). Needed when MRRocket › Settings › Products › Sign-in › "Only signed users" is on.
|
|
819
|
+
*/
|
|
820
|
+
hash?: string;
|
|
821
|
+
}
|
|
822
|
+
|
|
764
823
|
/**
|
|
765
824
|
* The React Native client without React: the event queue (batched, persisted for offline use, retried with backoff),
|
|
766
825
|
* the app lifecycle (`$app_open`, `$app_background`, a new session after 30 minutes away), screens, identity and
|
|
@@ -822,6 +881,13 @@ interface RegisterForPushOptions {
|
|
|
822
881
|
projectId?: string;
|
|
823
882
|
}
|
|
824
883
|
type NativePushStatus = "registered" | "denied" | "not-asked" | "unavailable" | "not-signed-in" | "error";
|
|
884
|
+
/**
|
|
885
|
+
* 0.16.0, what `requestReview()` did: "requested" the store's rating dialog was asked for (the store decides whether it
|
|
886
|
+
* shows; Apple shows it at most three times a year); "not-now" no live review request may ask this person now (not a
|
|
887
|
+
* customer, asked in the last 120 days, no request live); "unavailable" the device has no dialog or `expo-store-review`
|
|
888
|
+
* is not installed; "not-signed-in" call `identify` first.
|
|
889
|
+
*/
|
|
890
|
+
type NativeReviewResult = "requested" | "not-now" | "unavailable" | "not-signed-in";
|
|
825
891
|
interface NativePushResult {
|
|
826
892
|
status: NativePushStatus;
|
|
827
893
|
/** The Expo push token, once registered. */
|
|
@@ -838,8 +904,14 @@ interface MRRocketClient {
|
|
|
838
904
|
tap(x: number, y: number): void;
|
|
839
905
|
/** The person's answer to session recordings (`replay` in the options); false stops and forgets what was not sent. */
|
|
840
906
|
setReplay(on: boolean): void;
|
|
841
|
-
|
|
842
|
-
|
|
907
|
+
/**
|
|
908
|
+
* Who the person is once they sign in. `{ hash }` (0.16.0) is the id signed by your backend (`userHash(id)` from
|
|
909
|
+
* `@mrrocket/sdk/next/server`, or the same HMAC in any language), fetched after sign-in; sent with every request for
|
|
910
|
+
* that id so MRRocket knows it is really them. Needed with "Only signed users" (MRRocket › Settings › Products ›
|
|
911
|
+
* Sign-in). Not stored on the device: call identify with it on every start.
|
|
912
|
+
*/
|
|
913
|
+
identify(id: string, props?: Record<string, unknown>, options?: IdentifyOptions): void;
|
|
914
|
+
/** Forgets the signed-in person and their hash (sign-out): a new anonymous id and a new session. */
|
|
843
915
|
reset(): void;
|
|
844
916
|
setConsent(consent: NativeConsent): void;
|
|
845
917
|
captureError(error: unknown): void;
|
|
@@ -886,6 +958,12 @@ interface MRRocketClient {
|
|
|
886
958
|
registerForPush(options?: RegisterForPushOptions): Promise<NativePushResult>;
|
|
887
959
|
/** Removes this device's token for the signed-in person (sign-out does it on its own in `reset`). */
|
|
888
960
|
unregisterForPush(): Promise<boolean>;
|
|
961
|
+
/**
|
|
962
|
+
* 0.16.0: asks for a store review at the moment your app picked (the trigger of a review request in MRRocket ›
|
|
963
|
+
* Reviews › Requests). MRRocket says whether this signed-in person may be asked now; if so the App Store's or Google
|
|
964
|
+
* Play's own rating dialog opens (needs `expo-store-review`) and MRRocket counts the request. Never filtered by mood.
|
|
965
|
+
*/
|
|
966
|
+
requestReview(): Promise<NativeReviewResult>;
|
|
889
967
|
/** The cancel flow, onboarding and messages for a renderer (the provider's, or your own). */
|
|
890
968
|
flows: FlowsController;
|
|
891
969
|
/** Sends what is queued now. */
|
|
@@ -998,8 +1076,10 @@ interface NativeMessengerOptions {
|
|
|
998
1076
|
corners?: MessengerCorners;
|
|
999
1077
|
/** Home's top: plain like the rest (default), or in the accent color with raised cards. */
|
|
1000
1078
|
header?: "plain" | "color";
|
|
1001
|
-
/** The
|
|
1079
|
+
/** The logo at the top left of Home (MRRocket sends the uploaded logo, else the product's favicon). */
|
|
1002
1080
|
logo?: string;
|
|
1081
|
+
/** The AI agent's name on its answers (default "AI agent"). */
|
|
1082
|
+
agentName?: string;
|
|
1003
1083
|
/** Ask people the app did not identify with an email for an email (optional for them). Default true. */
|
|
1004
1084
|
askEmail?: boolean;
|
|
1005
1085
|
/** The Help tab with your published help articles (default true; hidden while there are none). */
|
|
@@ -1075,7 +1155,7 @@ declare const MRRocketProvider: ({ client: given, children, consent, flows, appe
|
|
|
1075
1155
|
declare const useMRRocket: () => {
|
|
1076
1156
|
track: (event: string, props?: Record<string, unknown>) => void;
|
|
1077
1157
|
screen: (name: string, props?: Record<string, unknown>) => void;
|
|
1078
|
-
identify: (id: string, props?: Record<string, unknown
|
|
1158
|
+
identify: (id: string, props?: Record<string, unknown>, options?: IdentifyOptions) => void;
|
|
1079
1159
|
reset: () => void;
|
|
1080
1160
|
setConsent: (consent: NativeConsent) => void;
|
|
1081
1161
|
setReplay: (on: boolean) => void;
|
|
@@ -1118,6 +1198,17 @@ declare const usePush: () => {
|
|
|
1118
1198
|
declare const usePaywall: () => {
|
|
1119
1199
|
open: MRRocketClient["openPaywall"];
|
|
1120
1200
|
};
|
|
1201
|
+
/**
|
|
1202
|
+
* 0.16.0: store reviews from a review request in MRRocket › Reviews › Requests (needs `expo-store-review`). Call
|
|
1203
|
+
* `request()` at the moment the request names (after the third finished challenge, a 7-day streak …): MRRocket says
|
|
1204
|
+
* whether this signed-in person may be asked now, and the App Store's or Google Play's own rating dialog opens.
|
|
1205
|
+
*
|
|
1206
|
+
* const review = useReview();
|
|
1207
|
+
* if (challengesDone === 3) void review.request();
|
|
1208
|
+
*/
|
|
1209
|
+
declare const useReview: () => {
|
|
1210
|
+
request: MRRocketClient["requestReview"];
|
|
1211
|
+
};
|
|
1121
1212
|
/** Starts an onboarding flow now (the first live one without an id), whatever its trigger. */
|
|
1122
1213
|
declare const useOnboarding: () => {
|
|
1123
1214
|
start: (id?: string) => boolean;
|
|
@@ -1156,4 +1247,4 @@ declare const useExpoRouterTracking: () => void;
|
|
|
1156
1247
|
*/
|
|
1157
1248
|
declare const useNavigationTracking: () => (state: unknown) => void;
|
|
1158
1249
|
|
|
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 };
|
|
1250
|
+
export { type AppContext, type CancelFlowConfig, type CancelFlowOptions, type CancelFlowResult, type CancelOffer, type ChatMessage, type ChatThread, type CockpitConfig, type FeedbackPost, type FlagConfig, type FlowMedia, type FlowView, type IdentifyOptions, 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 NativeReviewResult, type OnboardingFlow, type OnboardingStep, type PaywallConfig, type PaywallOptions, type PaywallPage, type PaywallPlan, type PaywallResult, type RegisterForPushOptions, SDK_VERSION, type SurveyDefinition, type TourPlacement, type VariantId, activeRouteName, createMRRocket, getMRRocket, useCancelFlow, useExpoRouterTracking, useFeedback, useFlag, useFlagVariant, useMRRocket, useMessenger, useNavigationTracking, useOnboarding, usePaywall, usePush, useReview, useText, useVariant };
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
* The SDK's fetch client and the shapes it reads, without React, so the script tag's widgets (src/script) use it too.
|
|
6
|
-
* `./api` re-exports all of it with the `useApi` hook.
|
|
7
|
-
*/
|
|
8
4
|
type PostStatus = "review" | "planned" | "progress" | "shipped";
|
|
9
5
|
interface FeedbackBoardInfo {
|
|
10
6
|
id: string;
|
|
@@ -180,8 +176,14 @@ interface MessengerLook {
|
|
|
180
176
|
header?: "plain" | "color";
|
|
181
177
|
/** "Powered by MRRocket" at the bottom; the server sends false only on plans that may hide it (from 0.15). */
|
|
182
178
|
branding?: boolean;
|
|
183
|
-
/** The
|
|
179
|
+
/** The logo at the top left of Home: the one uploaded in MRRocket, else the product's favicon; without it the title's first letter. */
|
|
184
180
|
logo?: string;
|
|
181
|
+
/** The product's favicon, the round mark in the chat's head (from 0.17; without it the title's first letter). */
|
|
182
|
+
icon?: string;
|
|
183
|
+
/** The AI agent's name on its answers (from 0.17; empty or left out: "AI agent"). */
|
|
184
|
+
agentName?: string;
|
|
185
|
+
/** The AI agent's picture beside its answers (from 0.17). */
|
|
186
|
+
agentPhoto?: string;
|
|
185
187
|
}
|
|
186
188
|
/** The visitor's newest chat (open or closed) and its messages. */
|
|
187
189
|
interface ChatThread {
|
|
@@ -201,6 +203,11 @@ interface Conn {
|
|
|
201
203
|
id?: string;
|
|
202
204
|
[key: string]: unknown;
|
|
203
205
|
};
|
|
206
|
+
/**
|
|
207
|
+
* The identity hash of `distinctId` (0.16.0, see identity.ts), sent with every request that names the visitor: a
|
|
208
|
+
* JSON field next to `distinctId`, a query parameter, a form field. Leave it out for any other id.
|
|
209
|
+
*/
|
|
210
|
+
userHash?: string;
|
|
204
211
|
}
|
|
205
212
|
/** The small fetch client behind the widgets. Every read takes an AbortSignal so unmounting cancels it. */
|
|
206
213
|
declare const createApi: (c: Conn) => {
|
|
@@ -273,7 +280,8 @@ type Api = ReturnType<typeof createApi>;
|
|
|
273
280
|
* `onCancel`, and only after they succeed is the outcome recorded.
|
|
274
281
|
* - `$onboarding` `{flow, step?, action: "started" | "shown" | "completed" | "dismissed"}`; since 0.11.0 a wait records
|
|
275
282
|
* "shown" when the person reaches it and "completed" when it is over, a condition `{action: "answered", answer: "yes"
|
|
276
|
-
* | "no"}` once it is decided.
|
|
283
|
+
* | "no"}` once it is decided; since 0.17.0 a tour a checklist task started records `{action: "started", from}` with
|
|
284
|
+
* the checklist's id.
|
|
277
285
|
* - `$inapp` `{message, journey, action: "shown" | "clicked" | "dismissed"}`; the journey moves the person on.
|
|
278
286
|
*/
|
|
279
287
|
|
|
@@ -343,6 +351,17 @@ interface CancelFlowConfig {
|
|
|
343
351
|
offersFor: "business";
|
|
344
352
|
}
|
|
345
353
|
type OnboardingStepKind = "tour" | "checklist" | "modal";
|
|
354
|
+
/** 0.17.0: where a tour bubble sits next to its element; "auto" (and no value) puts it below, else above, as before. */
|
|
355
|
+
type TourPlacement = "auto" | "top" | "bottom" | "left" | "right";
|
|
356
|
+
/**
|
|
357
|
+
* 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
|
|
358
|
+
* through their privacy-friendly players, see `videoEmbed`) or a video file (mp4, webm).
|
|
359
|
+
*/
|
|
360
|
+
interface FlowMedia {
|
|
361
|
+
kind: "image" | "video";
|
|
362
|
+
url: string;
|
|
363
|
+
alt?: string;
|
|
364
|
+
}
|
|
346
365
|
interface OnboardingStep {
|
|
347
366
|
id: string;
|
|
348
367
|
kind: OnboardingStepKind;
|
|
@@ -353,6 +372,14 @@ interface OnboardingStep {
|
|
|
353
372
|
target?: string;
|
|
354
373
|
/** Checklist items: the event that ticks them (`track("<event>")`). Without it the person ticks it themselves. */
|
|
355
374
|
event?: string;
|
|
375
|
+
/** 0.17.0, tour steps: which side of the element the bubble takes (flips when there is no room). */
|
|
376
|
+
placement?: TourPlacement;
|
|
377
|
+
/** 0.17.0, tour steps and modals: a picture or a video above the text. */
|
|
378
|
+
media?: FlowMedia;
|
|
379
|
+
/** 0.17.0, modals: where the button goes (http(s) or a path of the app). */
|
|
380
|
+
url?: string;
|
|
381
|
+
/** 0.17.0, checklist items: the id of a tour (another flow) the item starts; finishing the tour ticks the item. */
|
|
382
|
+
startsTour?: string;
|
|
356
383
|
}
|
|
357
384
|
/** A pause in a flow (0.11.0): the next step shows this long after the one before was done. */
|
|
358
385
|
interface OnboardingWait {
|
|
@@ -378,9 +405,15 @@ type OnboardingNode = OnboardingStep | OnboardingWait | OnboardingCondition;
|
|
|
378
405
|
/** A live onboarding flow's in-app steps (MRRocket › Conversion › Onboarding); its emails go out from MRRocket. */
|
|
379
406
|
interface OnboardingFlow {
|
|
380
407
|
id: string;
|
|
408
|
+
/** A checklist's heading; for tours and modals the name MRRocket knows them by. */
|
|
381
409
|
name: string;
|
|
382
|
-
/**
|
|
410
|
+
/**
|
|
411
|
+
* When it starts: an event's words ("Signed up", "First export"), "First launch" / "First visit", since 0.17.0 a page
|
|
412
|
+
* of the app ("/settings", "/projects/*"), or "From a checklist" (only a checklist task starts it).
|
|
413
|
+
*/
|
|
383
414
|
trigger: string;
|
|
415
|
+
/** 0.17.0: "signed_in" starts it on its own only for someone the app identified. */
|
|
416
|
+
audience?: "signed_in";
|
|
384
417
|
/** What every version shows: with `nodes`, only the in-app steps before the first wait or condition. */
|
|
385
418
|
steps: OnboardingStep[];
|
|
386
419
|
/** 0.11.0+: the whole flow with its waits and conditions. Read instead of `steps` when it is there. */
|
|
@@ -459,12 +492,15 @@ type FlowView = {
|
|
|
459
492
|
index: number;
|
|
460
493
|
total: number;
|
|
461
494
|
last: boolean;
|
|
462
|
-
}
|
|
495
|
+
}
|
|
496
|
+
/** `tour`: the tour the item starts (0.17.0), when the config has it. */
|
|
497
|
+
| {
|
|
463
498
|
type: "checklist";
|
|
464
499
|
flow: OnboardingFlow;
|
|
465
500
|
items: {
|
|
466
501
|
step: OnboardingStep;
|
|
467
502
|
done: boolean;
|
|
503
|
+
tour?: OnboardingFlow;
|
|
468
504
|
}[];
|
|
469
505
|
} | {
|
|
470
506
|
type: "message";
|
|
@@ -492,10 +528,12 @@ interface FlowsController {
|
|
|
492
528
|
confirm(comment?: string): void;
|
|
493
529
|
close(): void;
|
|
494
530
|
};
|
|
531
|
+
/** `startTour(stepId)` (0.17.0): the tour a checklist item starts; the checklist comes back when it ends. */
|
|
495
532
|
onboarding: {
|
|
496
533
|
next(): void;
|
|
497
534
|
check(stepId: string): void;
|
|
498
535
|
dismiss(): void;
|
|
536
|
+
startTour(stepId: string): boolean;
|
|
499
537
|
};
|
|
500
538
|
message: {
|
|
501
539
|
click(): void;
|
|
@@ -620,7 +658,7 @@ interface PaywallHost {
|
|
|
620
658
|
|
|
621
659
|
type VariantId = string;
|
|
622
660
|
/** This build of the SDK. Sent with the config request so MRRocket knows what the site runs (error tracking, flags). */
|
|
623
|
-
declare const SDK_VERSION = "0.
|
|
661
|
+
declare const SDK_VERSION = "0.17.0";
|
|
624
662
|
/** One variant of an experiment. `changes` replaces texts that the page marks with `<Text id>`. */
|
|
625
663
|
interface VariantConfig {
|
|
626
664
|
id: VariantId;
|
|
@@ -761,6 +799,27 @@ interface AppContext {
|
|
|
761
799
|
locale?: string;
|
|
762
800
|
}
|
|
763
801
|
|
|
802
|
+
/**
|
|
803
|
+
* Identity verification (0.16.0), the browser and app side. After `identify(id)` the SDK's id is your app's own user
|
|
804
|
+
* id, and that id is no secret (a Clerk id shows in URLs and logs), so on its own it proves nothing: anyone who knows it
|
|
805
|
+
* could read that person's support chat or post as them. Your server signs the id with the product's server key
|
|
806
|
+
* (`userHash(user.id)` from `@mrrocket/sdk/next/server`) and you hand the result to `identify(id, props, { hash })`.
|
|
807
|
+
* From then on every request for that id carries it as `userHash` (a JSON field, a query parameter or a form field), and
|
|
808
|
+
* MRRocket checks it.
|
|
809
|
+
*
|
|
810
|
+
* The hash belongs to one id: it is forgotten on `reset()` (sign-out) and when `identify` is called for another id
|
|
811
|
+
* without a hash, so it never goes out with someone else's id (MRRocket refuses a hash that does not match with 401).
|
|
812
|
+
* Shared by the Next.js provider, the script tag and React Native: no React, no browser APIs.
|
|
813
|
+
*/
|
|
814
|
+
/** The third argument of `identify`. */
|
|
815
|
+
interface IdentifyOptions {
|
|
816
|
+
/**
|
|
817
|
+
* The id signed by your server: `await userHash(user.id)` from `@mrrocket/sdk/next/server` (or the same HMAC in any
|
|
818
|
+
* language). Needed when MRRocket › Settings › Products › Sign-in › "Only signed users" is on.
|
|
819
|
+
*/
|
|
820
|
+
hash?: string;
|
|
821
|
+
}
|
|
822
|
+
|
|
764
823
|
/**
|
|
765
824
|
* The React Native client without React: the event queue (batched, persisted for offline use, retried with backoff),
|
|
766
825
|
* the app lifecycle (`$app_open`, `$app_background`, a new session after 30 minutes away), screens, identity and
|
|
@@ -822,6 +881,13 @@ interface RegisterForPushOptions {
|
|
|
822
881
|
projectId?: string;
|
|
823
882
|
}
|
|
824
883
|
type NativePushStatus = "registered" | "denied" | "not-asked" | "unavailable" | "not-signed-in" | "error";
|
|
884
|
+
/**
|
|
885
|
+
* 0.16.0, what `requestReview()` did: "requested" the store's rating dialog was asked for (the store decides whether it
|
|
886
|
+
* shows; Apple shows it at most three times a year); "not-now" no live review request may ask this person now (not a
|
|
887
|
+
* customer, asked in the last 120 days, no request live); "unavailable" the device has no dialog or `expo-store-review`
|
|
888
|
+
* is not installed; "not-signed-in" call `identify` first.
|
|
889
|
+
*/
|
|
890
|
+
type NativeReviewResult = "requested" | "not-now" | "unavailable" | "not-signed-in";
|
|
825
891
|
interface NativePushResult {
|
|
826
892
|
status: NativePushStatus;
|
|
827
893
|
/** The Expo push token, once registered. */
|
|
@@ -838,8 +904,14 @@ interface MRRocketClient {
|
|
|
838
904
|
tap(x: number, y: number): void;
|
|
839
905
|
/** The person's answer to session recordings (`replay` in the options); false stops and forgets what was not sent. */
|
|
840
906
|
setReplay(on: boolean): void;
|
|
841
|
-
|
|
842
|
-
|
|
907
|
+
/**
|
|
908
|
+
* Who the person is once they sign in. `{ hash }` (0.16.0) is the id signed by your backend (`userHash(id)` from
|
|
909
|
+
* `@mrrocket/sdk/next/server`, or the same HMAC in any language), fetched after sign-in; sent with every request for
|
|
910
|
+
* that id so MRRocket knows it is really them. Needed with "Only signed users" (MRRocket › Settings › Products ›
|
|
911
|
+
* Sign-in). Not stored on the device: call identify with it on every start.
|
|
912
|
+
*/
|
|
913
|
+
identify(id: string, props?: Record<string, unknown>, options?: IdentifyOptions): void;
|
|
914
|
+
/** Forgets the signed-in person and their hash (sign-out): a new anonymous id and a new session. */
|
|
843
915
|
reset(): void;
|
|
844
916
|
setConsent(consent: NativeConsent): void;
|
|
845
917
|
captureError(error: unknown): void;
|
|
@@ -886,6 +958,12 @@ interface MRRocketClient {
|
|
|
886
958
|
registerForPush(options?: RegisterForPushOptions): Promise<NativePushResult>;
|
|
887
959
|
/** Removes this device's token for the signed-in person (sign-out does it on its own in `reset`). */
|
|
888
960
|
unregisterForPush(): Promise<boolean>;
|
|
961
|
+
/**
|
|
962
|
+
* 0.16.0: asks for a store review at the moment your app picked (the trigger of a review request in MRRocket ›
|
|
963
|
+
* Reviews › Requests). MRRocket says whether this signed-in person may be asked now; if so the App Store's or Google
|
|
964
|
+
* Play's own rating dialog opens (needs `expo-store-review`) and MRRocket counts the request. Never filtered by mood.
|
|
965
|
+
*/
|
|
966
|
+
requestReview(): Promise<NativeReviewResult>;
|
|
889
967
|
/** The cancel flow, onboarding and messages for a renderer (the provider's, or your own). */
|
|
890
968
|
flows: FlowsController;
|
|
891
969
|
/** Sends what is queued now. */
|
|
@@ -998,8 +1076,10 @@ interface NativeMessengerOptions {
|
|
|
998
1076
|
corners?: MessengerCorners;
|
|
999
1077
|
/** Home's top: plain like the rest (default), or in the accent color with raised cards. */
|
|
1000
1078
|
header?: "plain" | "color";
|
|
1001
|
-
/** The
|
|
1079
|
+
/** The logo at the top left of Home (MRRocket sends the uploaded logo, else the product's favicon). */
|
|
1002
1080
|
logo?: string;
|
|
1081
|
+
/** The AI agent's name on its answers (default "AI agent"). */
|
|
1082
|
+
agentName?: string;
|
|
1003
1083
|
/** Ask people the app did not identify with an email for an email (optional for them). Default true. */
|
|
1004
1084
|
askEmail?: boolean;
|
|
1005
1085
|
/** The Help tab with your published help articles (default true; hidden while there are none). */
|
|
@@ -1075,7 +1155,7 @@ declare const MRRocketProvider: ({ client: given, children, consent, flows, appe
|
|
|
1075
1155
|
declare const useMRRocket: () => {
|
|
1076
1156
|
track: (event: string, props?: Record<string, unknown>) => void;
|
|
1077
1157
|
screen: (name: string, props?: Record<string, unknown>) => void;
|
|
1078
|
-
identify: (id: string, props?: Record<string, unknown
|
|
1158
|
+
identify: (id: string, props?: Record<string, unknown>, options?: IdentifyOptions) => void;
|
|
1079
1159
|
reset: () => void;
|
|
1080
1160
|
setConsent: (consent: NativeConsent) => void;
|
|
1081
1161
|
setReplay: (on: boolean) => void;
|
|
@@ -1118,6 +1198,17 @@ declare const usePush: () => {
|
|
|
1118
1198
|
declare const usePaywall: () => {
|
|
1119
1199
|
open: MRRocketClient["openPaywall"];
|
|
1120
1200
|
};
|
|
1201
|
+
/**
|
|
1202
|
+
* 0.16.0: store reviews from a review request in MRRocket › Reviews › Requests (needs `expo-store-review`). Call
|
|
1203
|
+
* `request()` at the moment the request names (after the third finished challenge, a 7-day streak …): MRRocket says
|
|
1204
|
+
* whether this signed-in person may be asked now, and the App Store's or Google Play's own rating dialog opens.
|
|
1205
|
+
*
|
|
1206
|
+
* const review = useReview();
|
|
1207
|
+
* if (challengesDone === 3) void review.request();
|
|
1208
|
+
*/
|
|
1209
|
+
declare const useReview: () => {
|
|
1210
|
+
request: MRRocketClient["requestReview"];
|
|
1211
|
+
};
|
|
1121
1212
|
/** Starts an onboarding flow now (the first live one without an id), whatever its trigger. */
|
|
1122
1213
|
declare const useOnboarding: () => {
|
|
1123
1214
|
start: (id?: string) => boolean;
|
|
@@ -1156,4 +1247,4 @@ declare const useExpoRouterTracking: () => void;
|
|
|
1156
1247
|
*/
|
|
1157
1248
|
declare const useNavigationTracking: () => (state: unknown) => void;
|
|
1158
1249
|
|
|
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 };
|
|
1250
|
+
export { type AppContext, type CancelFlowConfig, type CancelFlowOptions, type CancelFlowResult, type CancelOffer, type ChatMessage, type ChatThread, type CockpitConfig, type FeedbackPost, type FlagConfig, type FlowMedia, type FlowView, type IdentifyOptions, 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 NativeReviewResult, type OnboardingFlow, type OnboardingStep, type PaywallConfig, type PaywallOptions, type PaywallPage, type PaywallPlan, type PaywallResult, type RegisterForPushOptions, SDK_VERSION, type SurveyDefinition, type TourPlacement, type VariantId, activeRouteName, createMRRocket, getMRRocket, useCancelFlow, useExpoRouterTracking, useFeedback, useFlag, useFlagVariant, useMRRocket, useMessenger, useNavigationTracking, useOnboarding, usePaywall, usePush, useReview, useText, useVariant };
|