@mrrocket/sdk 0.2.0 → 0.4.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 +16 -0
- package/README.md +142 -2
- package/dist/batch.d.ts +33 -0
- package/dist/batch.js +50 -0
- package/dist/batch.js.map +1 -0
- package/dist/browser/mrrocket-replay.js +78 -0
- package/dist/browser/mrrocket.js +1 -0
- package/dist/browser.d.ts +1 -1
- package/dist/browser.js +3 -1
- package/dist/browser.js.map +1 -1
- package/dist/client.d.ts +23 -3
- package/dist/client.js +44 -83
- package/dist/client.js.map +1 -1
- package/dist/context.d.ts +2 -0
- package/dist/context.js.map +1 -1
- package/dist/core.d.ts +94 -1
- package/dist/core.js +88 -4
- package/dist/core.js.map +1 -1
- package/dist/errors.d.ts +35 -0
- package/dist/errors.js +83 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/react-native/index.d.cts +259 -0
- package/dist/react-native/index.d.ts +259 -0
- package/dist/react-native/index.js +922 -0
- package/dist/react-native/index.js.map +1 -0
- package/dist/react-native/package.json +3 -0
- package/dist/referral.d.ts +6 -0
- package/dist/referral.js +7 -3
- package/dist/referral.js.map +1 -1
- package/dist/server.d.ts +13 -1
- package/dist/server.js +8 -1
- package/dist/server.js.map +1 -1
- package/dist/styles.d.ts +1 -1
- package/dist/styles.js +2 -2
- package/dist/styles.js.map +1 -1
- package/dist/web-state.d.ts +36 -0
- package/dist/web-state.js +64 -0
- package/dist/web-state.js.map +1 -0
- package/package.json +56 -6
- package/react-native/package.json +7 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The browser state both web runtimes share, the Next.js provider (client.tsx) and the script tag (script/index.ts):
|
|
3
|
+
* the visitor and session cookies, the in-memory id of this page load, the current page's URL, referrer and campaign,
|
|
4
|
+
* and the browser's privacy signal. Plain functions, no React; only called in the browser (effects, event handlers).
|
|
5
|
+
*/
|
|
6
|
+
import { type ConsentMode, type UtmKey } from "./core.js";
|
|
7
|
+
export declare const YEAR_S: number;
|
|
8
|
+
export declare const readCookie: (name: string) => string | undefined;
|
|
9
|
+
export declare const writeCookie: (name: string, value: string, maxAge: number) => void;
|
|
10
|
+
export declare const deleteCookie: (name: string) => void;
|
|
11
|
+
/**
|
|
12
|
+
* What lives for one page load, outside React so it survives a provider that remounts on navigation: the in-memory
|
|
13
|
+
* visitor id, the session, the current page's URL and referrer, and the campaign (UTM) parameters.
|
|
14
|
+
*/
|
|
15
|
+
export declare const page: {
|
|
16
|
+
id?: string;
|
|
17
|
+
session?: {
|
|
18
|
+
id: string;
|
|
19
|
+
last: number;
|
|
20
|
+
};
|
|
21
|
+
url?: string;
|
|
22
|
+
referrer?: string;
|
|
23
|
+
utm: Partial<Record<UtmKey, string>>;
|
|
24
|
+
};
|
|
25
|
+
export declare const memoryId: () => string;
|
|
26
|
+
/**
|
|
27
|
+
* The visitor id for experiments, surveys and feedback. Only "cookies" reads or writes `ck_id`; the other modes keep a
|
|
28
|
+
* random id in memory for this page load. The page id follows the cookie, so leaving "cookies" keeps the same variants.
|
|
29
|
+
*/
|
|
30
|
+
export declare const visitorId: (mode: ConsentMode, write: boolean) => string;
|
|
31
|
+
/** The session id for an event. Every event renews it; after 30 minutes without one a new session starts. */
|
|
32
|
+
export declare const touchSession: (persist: boolean) => string;
|
|
33
|
+
/** On every route change: the previous in-app URL becomes the referrer, and a URL with UTM parameters replaces the old ones. */
|
|
34
|
+
export declare const enterPage: () => void;
|
|
35
|
+
/** Do Not Track or Global Privacy Control. */
|
|
36
|
+
export declare const privacySignal: () => boolean;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// src/web-state.ts
|
|
2
|
+
import { ID_COOKIE, SESSION_COOKIE, SESSION_TIMEOUT_MS, newDistinctId, readUtm } from "./core.js";
|
|
3
|
+
var YEAR_S = 60 * 60 * 24 * 365;
|
|
4
|
+
var readCookie = (name) => {
|
|
5
|
+
if (typeof document === "undefined") return void 0;
|
|
6
|
+
const raw = document.cookie.split("; ").find((c) => c.startsWith(`${name}=`))?.slice(name.length + 1);
|
|
7
|
+
if (!raw) return void 0;
|
|
8
|
+
try {
|
|
9
|
+
return decodeURIComponent(raw);
|
|
10
|
+
} catch {
|
|
11
|
+
return raw;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
var writeCookie = (name, value, maxAge) => {
|
|
15
|
+
const secure = window.location.protocol === "https:" ? "; secure" : "";
|
|
16
|
+
document.cookie = `${name}=${encodeURIComponent(value)}; path=/; max-age=${maxAge}; samesite=lax${secure}`;
|
|
17
|
+
};
|
|
18
|
+
var deleteCookie = (name) => {
|
|
19
|
+
document.cookie = `${name}=; path=/; max-age=0; samesite=lax`;
|
|
20
|
+
};
|
|
21
|
+
var page = { utm: {} };
|
|
22
|
+
var memoryId = () => page.id ??= newDistinctId();
|
|
23
|
+
var visitorId = (mode, write) => {
|
|
24
|
+
if (mode !== "cookies") return memoryId();
|
|
25
|
+
const stored = readCookie(ID_COOKIE);
|
|
26
|
+
if (stored) return page.id = stored;
|
|
27
|
+
const id = memoryId();
|
|
28
|
+
if (write) writeCookie(ID_COOKIE, id, YEAR_S);
|
|
29
|
+
return id;
|
|
30
|
+
};
|
|
31
|
+
var touchSession = (persist) => {
|
|
32
|
+
const now = Date.now();
|
|
33
|
+
const s = page.session;
|
|
34
|
+
const id = (persist ? readCookie(SESSION_COOKIE) : void 0) ?? (s && now - s.last < SESSION_TIMEOUT_MS ? s.id : newDistinctId());
|
|
35
|
+
page.session = { id, last: now };
|
|
36
|
+
if (persist) writeCookie(SESSION_COOKIE, id, SESSION_TIMEOUT_MS / 1e3);
|
|
37
|
+
return id;
|
|
38
|
+
};
|
|
39
|
+
var enterPage = () => {
|
|
40
|
+
const href = window.location.href;
|
|
41
|
+
if (page.url === href) return;
|
|
42
|
+
page.referrer = page.url ?? (document.referrer || void 0);
|
|
43
|
+
page.url = href;
|
|
44
|
+
const utm = readUtm(window.location.search);
|
|
45
|
+
if (Object.keys(utm).length) page.utm = utm;
|
|
46
|
+
};
|
|
47
|
+
var privacySignal = () => {
|
|
48
|
+
const nav = navigator;
|
|
49
|
+
const win = window;
|
|
50
|
+
return nav.doNotTrack === "1" || win.doNotTrack === "1" || nav.globalPrivacyControl === true;
|
|
51
|
+
};
|
|
52
|
+
export {
|
|
53
|
+
YEAR_S,
|
|
54
|
+
deleteCookie,
|
|
55
|
+
enterPage,
|
|
56
|
+
memoryId,
|
|
57
|
+
page,
|
|
58
|
+
privacySignal,
|
|
59
|
+
readCookie,
|
|
60
|
+
touchSession,
|
|
61
|
+
visitorId,
|
|
62
|
+
writeCookie
|
|
63
|
+
};
|
|
64
|
+
//# sourceMappingURL=web-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/web-state.ts"],"sourcesContent":["/**\n * The browser state both web runtimes share, the Next.js provider (client.tsx) and the script tag (script/index.ts):\n * the visitor and session cookies, the in-memory id of this page load, the current page's URL, referrer and campaign,\n * and the browser's privacy signal. Plain functions, no React; only called in the browser (effects, event handlers).\n */\nimport { type ConsentMode, ID_COOKIE, SESSION_COOKIE, SESSION_TIMEOUT_MS, type UtmKey, newDistinctId, readUtm } from \"./core\";\n\nexport const YEAR_S = 60 * 60 * 24 * 365;\n\nexport const readCookie = (name: string) => {\n if (typeof document === \"undefined\") return undefined;\n const raw = document.cookie\n .split(\"; \")\n .find((c) => c.startsWith(`${name}=`))\n ?.slice(name.length + 1);\n if (!raw) return undefined;\n try {\n return decodeURIComponent(raw);\n } catch {\n return raw;\n }\n};\n\nexport const writeCookie = (name: string, value: string, maxAge: number) => {\n const secure = window.location.protocol === \"https:\" ? \"; secure\" : \"\";\n document.cookie = `${name}=${encodeURIComponent(value)}; path=/; max-age=${maxAge}; samesite=lax${secure}`;\n};\n\nexport const deleteCookie = (name: string) => {\n document.cookie = `${name}=; path=/; max-age=0; samesite=lax`;\n};\n\n/**\n * What lives for one page load, outside React so it survives a provider that remounts on navigation: the in-memory\n * visitor id, the session, the current page's URL and referrer, and the campaign (UTM) parameters.\n */\nexport const page: {\n id?: string;\n session?: { id: string; last: number };\n url?: string;\n referrer?: string;\n utm: Partial<Record<UtmKey, string>>;\n} = { utm: {} };\n\nexport const memoryId = () => (page.id ??= newDistinctId());\n\n/**\n * The visitor id for experiments, surveys and feedback. Only \"cookies\" reads or writes `ck_id`; the other modes keep a\n * random id in memory for this page load. The page id follows the cookie, so leaving \"cookies\" keeps the same variants.\n */\nexport const visitorId = (mode: ConsentMode, write: boolean) => {\n if (mode !== \"cookies\") return memoryId();\n const stored = readCookie(ID_COOKIE);\n if (stored) return (page.id = stored);\n const id = memoryId();\n if (write) writeCookie(ID_COOKIE, id, YEAR_S);\n return id;\n};\n\n/** The session id for an event. Every event renews it; after 30 minutes without one a new session starts. */\nexport const touchSession = (persist: boolean) => {\n const now = Date.now();\n const s = page.session;\n // In \"cookies\" mode the cookie wins, so tabs share a session.\n const id = (persist ? readCookie(SESSION_COOKIE) : undefined) ?? (s && now - s.last < SESSION_TIMEOUT_MS ? s.id : newDistinctId());\n page.session = { id, last: now };\n if (persist) writeCookie(SESSION_COOKIE, id, SESSION_TIMEOUT_MS / 1000);\n return id;\n};\n\n/** On every route change: the previous in-app URL becomes the referrer, and a URL with UTM parameters replaces the old ones. */\nexport const enterPage = () => {\n const href = window.location.href;\n if (page.url === href) return;\n page.referrer = page.url ?? (document.referrer || undefined);\n page.url = href;\n const utm = readUtm(window.location.search);\n if (Object.keys(utm).length) page.utm = utm;\n};\n\n/** Do Not Track or Global Privacy Control. */\nexport const privacySignal = () => {\n const nav = navigator as Navigator & { doNotTrack?: string | null; globalPrivacyControl?: boolean };\n const win = window as unknown as { doNotTrack?: string | null };\n return nav.doNotTrack === \"1\" || win.doNotTrack === \"1\" || nav.globalPrivacyControl === true;\n};\n"],"mappings":";AAKA,SAA2B,WAAW,gBAAgB,oBAAiC,eAAe,eAAe;AAE9G,IAAM,SAAS,KAAK,KAAK,KAAK;AAE9B,IAAM,aAAa,CAAC,SAAiB;AACxC,MAAI,OAAO,aAAa,YAAa,QAAO;AAC5C,QAAM,MAAM,SAAS,OAChB,MAAM,IAAI,EACV,KAAK,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,GAAG,CAAC,GACnC,MAAM,KAAK,SAAS,CAAC;AAC3B,MAAI,CAAC,IAAK,QAAO;AACjB,MAAI;AACA,WAAO,mBAAmB,GAAG;AAAA,EACjC,QAAQ;AACJ,WAAO;AAAA,EACX;AACJ;AAEO,IAAM,cAAc,CAAC,MAAc,OAAe,WAAmB;AACxE,QAAM,SAAS,OAAO,SAAS,aAAa,WAAW,aAAa;AACpE,WAAS,SAAS,GAAG,IAAI,IAAI,mBAAmB,KAAK,CAAC,qBAAqB,MAAM,iBAAiB,MAAM;AAC5G;AAEO,IAAM,eAAe,CAAC,SAAiB;AAC1C,WAAS,SAAS,GAAG,IAAI;AAC7B;AAMO,IAAM,OAMT,EAAE,KAAK,CAAC,EAAE;AAEP,IAAM,WAAW,MAAO,KAAK,OAAO,cAAc;AAMlD,IAAM,YAAY,CAAC,MAAmB,UAAmB;AAC5D,MAAI,SAAS,UAAW,QAAO,SAAS;AACxC,QAAM,SAAS,WAAW,SAAS;AACnC,MAAI,OAAQ,QAAQ,KAAK,KAAK;AAC9B,QAAM,KAAK,SAAS;AACpB,MAAI,MAAO,aAAY,WAAW,IAAI,MAAM;AAC5C,SAAO;AACX;AAGO,IAAM,eAAe,CAAC,YAAqB;AAC9C,QAAM,MAAM,KAAK,IAAI;AACrB,QAAM,IAAI,KAAK;AAEf,QAAM,MAAM,UAAU,WAAW,cAAc,IAAI,YAAe,KAAK,MAAM,EAAE,OAAO,qBAAqB,EAAE,KAAK,cAAc;AAChI,OAAK,UAAU,EAAE,IAAI,MAAM,IAAI;AAC/B,MAAI,QAAS,aAAY,gBAAgB,IAAI,qBAAqB,GAAI;AACtE,SAAO;AACX;AAGO,IAAM,YAAY,MAAM;AAC3B,QAAM,OAAO,OAAO,SAAS;AAC7B,MAAI,KAAK,QAAQ,KAAM;AACvB,OAAK,WAAW,KAAK,QAAQ,SAAS,YAAY;AAClD,OAAK,MAAM;AACX,QAAM,MAAM,QAAQ,OAAO,SAAS,MAAM;AAC1C,MAAI,OAAO,KAAK,GAAG,EAAE,OAAQ,MAAK,MAAM;AAC5C;AAGO,IAAM,gBAAgB,MAAM;AAC/B,QAAM,MAAM;AACZ,QAAM,MAAM;AACZ,SAAO,IAAI,eAAe,OAAO,IAAI,eAAe,OAAO,IAAI,yBAAyB;AAC5F;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrrocket/sdk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "MRRocket SDK: page views, events, heatmap clicks, session recordings, A/B tests, feedback board and surveys that your MRRocket agents can run and read. Next.js
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "MRRocket SDK: page views, events, heatmap clicks, session recordings, A/B tests, feature flags, error tracking, feedback board and surveys that your MRRocket agents can run and read. Next.js (@mrrocket/sdk/next), React Native and Expo (@mrrocket/sdk/react-native), and a script tag for any website.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"nextjs",
|
|
7
7
|
"analytics",
|
|
@@ -14,7 +14,10 @@
|
|
|
14
14
|
"mrrocket",
|
|
15
15
|
"cloudflare-workers",
|
|
16
16
|
"react",
|
|
17
|
-
"sdk"
|
|
17
|
+
"sdk",
|
|
18
|
+
"react-native",
|
|
19
|
+
"expo",
|
|
20
|
+
"script-tag"
|
|
18
21
|
],
|
|
19
22
|
"license": "MIT",
|
|
20
23
|
"type": "module",
|
|
@@ -35,6 +38,14 @@
|
|
|
35
38
|
"import": "./dist/proxy.js",
|
|
36
39
|
"default": "./dist/proxy.js"
|
|
37
40
|
},
|
|
41
|
+
"./react-native": {
|
|
42
|
+
"types": "./dist/react-native/index.d.ts",
|
|
43
|
+
"react-native": "./dist/react-native/index.js",
|
|
44
|
+
"require": "./dist/react-native/index.js",
|
|
45
|
+
"import": "./dist/react-native/index.js",
|
|
46
|
+
"default": "./dist/react-native/index.js"
|
|
47
|
+
},
|
|
48
|
+
"./browser": "./dist/browser/mrrocket.js",
|
|
38
49
|
"./package.json": "./package.json"
|
|
39
50
|
},
|
|
40
51
|
"typesVersions": {
|
|
@@ -47,11 +58,15 @@
|
|
|
47
58
|
],
|
|
48
59
|
"next/proxy": [
|
|
49
60
|
"./dist/proxy.d.ts"
|
|
61
|
+
],
|
|
62
|
+
"react-native": [
|
|
63
|
+
"./dist/react-native/index.d.ts"
|
|
50
64
|
]
|
|
51
65
|
}
|
|
52
66
|
},
|
|
53
67
|
"files": [
|
|
54
68
|
"dist",
|
|
69
|
+
"react-native",
|
|
55
70
|
"README.md",
|
|
56
71
|
"LICENSE",
|
|
57
72
|
"CHANGELOG.md"
|
|
@@ -63,20 +78,55 @@
|
|
|
63
78
|
"access": "public"
|
|
64
79
|
},
|
|
65
80
|
"scripts": {
|
|
66
|
-
"build": "tsup && node scripts/build-types.mjs && node scripts/check-dist.mjs",
|
|
81
|
+
"build": "tsup && tsup --config tsup.native.config.mjs && tsup --config tsup.browser.config.mjs && node scripts/build-types.mjs && node scripts/check-dist.mjs",
|
|
67
82
|
"clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
68
83
|
"typecheck": "tsc --noEmit",
|
|
69
84
|
"test": "node --import tsx --test test/*.test.ts",
|
|
70
85
|
"check:dist": "node scripts/check-dist.mjs",
|
|
71
86
|
"check:publish": "node scripts/check-publish.mjs",
|
|
72
|
-
"prepublishOnly": "npm run check:publish && npm run typecheck && npm run build"
|
|
87
|
+
"prepublishOnly": "npm run check:publish && npm run typecheck && npm run build",
|
|
88
|
+
"build:script": "tsup --config tsup.browser.config.mjs",
|
|
89
|
+
"sync:app": "npm run build && node scripts/sync-app.mjs"
|
|
73
90
|
},
|
|
74
91
|
"dependencies": {
|
|
75
92
|
"@rrweb/record": "2.1.6"
|
|
76
93
|
},
|
|
77
94
|
"peerDependencies": {
|
|
95
|
+
"@react-native-async-storage/async-storage": ">=1.17",
|
|
96
|
+
"expo-application": ">=5",
|
|
97
|
+
"expo-device": ">=5",
|
|
98
|
+
"expo-localization": ">=14",
|
|
99
|
+
"expo-router": ">=3",
|
|
78
100
|
"next": ">=15",
|
|
79
|
-
"react": ">=
|
|
101
|
+
"react": ">=18",
|
|
102
|
+
"react-dom": ">=18",
|
|
103
|
+
"react-native": ">=0.72"
|
|
104
|
+
},
|
|
105
|
+
"peerDependenciesMeta": {
|
|
106
|
+
"@react-native-async-storage/async-storage": {
|
|
107
|
+
"optional": true
|
|
108
|
+
},
|
|
109
|
+
"expo-application": {
|
|
110
|
+
"optional": true
|
|
111
|
+
},
|
|
112
|
+
"expo-device": {
|
|
113
|
+
"optional": true
|
|
114
|
+
},
|
|
115
|
+
"expo-localization": {
|
|
116
|
+
"optional": true
|
|
117
|
+
},
|
|
118
|
+
"expo-router": {
|
|
119
|
+
"optional": true
|
|
120
|
+
},
|
|
121
|
+
"next": {
|
|
122
|
+
"optional": true
|
|
123
|
+
},
|
|
124
|
+
"react-dom": {
|
|
125
|
+
"optional": true
|
|
126
|
+
},
|
|
127
|
+
"react-native": {
|
|
128
|
+
"optional": true
|
|
129
|
+
}
|
|
80
130
|
},
|
|
81
131
|
"devDependencies": {
|
|
82
132
|
"@types/node": "^22",
|