@base44-preview/sdk 0.8.57-pr.359.9ff5e66 → 0.8.57-pr.360.71a2580
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/dist/modules/analytics.d.ts +3 -0
- package/dist/modules/analytics.js +127 -17
- package/dist/modules/app-logs.js +4 -0
- package/package.json +1 -1
|
@@ -6,6 +6,9 @@ export declare const ANALYTICS_INITIALIZATION_EVENT_NAME = "__initialization_eve
|
|
|
6
6
|
export declare const ANALYTICS_SESSION_DURATION_EVENT_NAME = "__session_duration_event__";
|
|
7
7
|
export declare const ANALYTICS_CONFIG_ENABLE_URL_PARAM_KEY = "analytics-enable";
|
|
8
8
|
export declare const ANALYTICS_SESSION_ID_LOCAL_STORAGE_KEY = "base44_analytics_session_id";
|
|
9
|
+
export declare const ANALYTICS_MODE_META_NAME = "base44-analytics";
|
|
10
|
+
/** Whether analytics may run and store the session id on this page. */
|
|
11
|
+
export declare function isAnalyticsAllowed(): boolean;
|
|
9
12
|
export interface AnalyticsModuleArgs {
|
|
10
13
|
axiosClient: AxiosInstance;
|
|
11
14
|
serverUrl: string;
|
|
@@ -5,6 +5,25 @@ export const ANALYTICS_INITIALIZATION_EVENT_NAME = "__initialization_event__";
|
|
|
5
5
|
export const ANALYTICS_SESSION_DURATION_EVENT_NAME = "__session_duration_event__";
|
|
6
6
|
export const ANALYTICS_CONFIG_ENABLE_URL_PARAM_KEY = "analytics-enable";
|
|
7
7
|
export const ANALYTICS_SESSION_ID_LOCAL_STORAGE_KEY = "base44_analytics_session_id";
|
|
8
|
+
// Set by the Base44 server from the app owner's choice: "off" never tracks,
|
|
9
|
+
// "consent" waits for the page's Google Consent Mode `analytics_storage` grant,
|
|
10
|
+
// "on" tracks until that consent is denied. Absent: track as before.
|
|
11
|
+
export const ANALYTICS_MODE_META_NAME = "base44-analytics";
|
|
12
|
+
function readAnalyticsMode() {
|
|
13
|
+
var _a;
|
|
14
|
+
if (typeof document === "undefined")
|
|
15
|
+
return null;
|
|
16
|
+
try {
|
|
17
|
+
const content = (_a = document
|
|
18
|
+
.querySelector(`meta[name="${ANALYTICS_MODE_META_NAME}"]`)) === null || _a === void 0 ? void 0 : _a.getAttribute("content");
|
|
19
|
+
return content === "off" || content === "consent" || content === "on"
|
|
20
|
+
? content
|
|
21
|
+
: null;
|
|
22
|
+
}
|
|
23
|
+
catch (_b) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
8
27
|
const defaultConfiguration = {
|
|
9
28
|
// default to enabled //
|
|
10
29
|
enabled: true,
|
|
@@ -33,15 +52,80 @@ const analyticsSharedState = getSharedInstance(ANALYTICS_SHARED_STATE_NAME, () =
|
|
|
33
52
|
...getAnalyticsConfigFromUrlParams(),
|
|
34
53
|
},
|
|
35
54
|
}));
|
|
55
|
+
///////////////////////////////////////////////
|
|
56
|
+
//// visitor consent ////
|
|
57
|
+
///////////////////////////////////////////////
|
|
58
|
+
// Its own shared instance: an older SDK copy on the page may own "analytics".
|
|
59
|
+
const consentSharedState = getSharedInstance("analytics-consent", () => {
|
|
60
|
+
const analyticsMode = readAnalyticsMode();
|
|
61
|
+
return {
|
|
62
|
+
analyticsMode,
|
|
63
|
+
// Whether the visitor may be tracked and the session id stored.
|
|
64
|
+
consentGranted: analyticsMode !== "consent",
|
|
65
|
+
isWatchingConsent: false,
|
|
66
|
+
listeners: [],
|
|
67
|
+
};
|
|
68
|
+
});
|
|
69
|
+
/** Whether analytics may run and store the session id on this page. */
|
|
70
|
+
export function isAnalyticsAllowed() {
|
|
71
|
+
const { analyticsMode, consentGranted } = consentSharedState;
|
|
72
|
+
return analyticsMode !== "off" && consentGranted;
|
|
73
|
+
}
|
|
74
|
+
function setConsentGranted(granted) {
|
|
75
|
+
var _a;
|
|
76
|
+
if (consentSharedState.consentGranted === granted)
|
|
77
|
+
return;
|
|
78
|
+
consentSharedState.consentGranted = granted;
|
|
79
|
+
if (!granted) {
|
|
80
|
+
try {
|
|
81
|
+
const sessionId = localStorage.getItem(ANALYTICS_SESSION_ID_LOCAL_STORAGE_KEY);
|
|
82
|
+
// Same id for the rest of the tab, so an anonymous agent chat keeps its owner.
|
|
83
|
+
if (sessionId)
|
|
84
|
+
(_a = analyticsSharedState.fallbackSessionId) !== null && _a !== void 0 ? _a : (analyticsSharedState.fallbackSessionId = sessionId);
|
|
85
|
+
localStorage.removeItem(ANALYTICS_SESSION_ID_LOCAL_STORAGE_KEY);
|
|
86
|
+
}
|
|
87
|
+
catch (_b) {
|
|
88
|
+
// storage unavailable: nothing was stored
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
for (const listener of [...consentSharedState.listeners]) {
|
|
92
|
+
listener(granted);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Subscribes once to the consent listener Base44 injects next to the meta tag.
|
|
96
|
+
// Without it (e.g. a CSP blocked the inline script) "consent" never grants.
|
|
97
|
+
function watchAnalyticsConsent() {
|
|
98
|
+
const state = consentSharedState;
|
|
99
|
+
if (state.isWatchingConsent)
|
|
100
|
+
return;
|
|
101
|
+
state.isWatchingConsent = true;
|
|
102
|
+
if (state.analyticsMode !== "consent" && state.analyticsMode !== "on")
|
|
103
|
+
return;
|
|
104
|
+
const b44Consent = typeof window !== "undefined" ? window.__b44Consent : undefined;
|
|
105
|
+
b44Consent === null || b44Consent === void 0 ? void 0 : b44Consent.on("analytics_storage", () => setConsentGranted(true), () => setConsentGranted(false));
|
|
106
|
+
}
|
|
107
|
+
function onConsentChange(listener) {
|
|
108
|
+
consentSharedState.listeners.push(listener);
|
|
109
|
+
return () => {
|
|
110
|
+
const listeners = consentSharedState.listeners;
|
|
111
|
+
const index = listeners.indexOf(listener);
|
|
112
|
+
if (index >= 0)
|
|
113
|
+
listeners.splice(index, 1);
|
|
114
|
+
};
|
|
115
|
+
}
|
|
36
116
|
export const createAnalyticsModule = ({ axiosClient, serverUrl, appId, userAuthModule, enabled, }) => {
|
|
37
117
|
var _a;
|
|
38
118
|
// prevent overflow of events //
|
|
39
119
|
const { maxQueueSize, throttleTime, batchSize } = analyticsSharedState.config;
|
|
120
|
+
watchAnalyticsConsent();
|
|
40
121
|
// Disable analytics on React Native. It defines `window` but not `document`,
|
|
41
122
|
// so the per-callsite `typeof window` guards below aren't enough to keep it
|
|
42
123
|
// from touching `document` (e.g. `document.referrer` on init). Node/SSR is
|
|
43
124
|
// still handled by those `window` guards, so this doesn't affect it.
|
|
44
|
-
if (!enabled ||
|
|
125
|
+
if (!enabled ||
|
|
126
|
+
!((_a = analyticsSharedState.config) === null || _a === void 0 ? void 0 : _a.enabled) ||
|
|
127
|
+
isReactNative ||
|
|
128
|
+
consentSharedState.analyticsMode === "off") {
|
|
45
129
|
return {
|
|
46
130
|
track: () => { },
|
|
47
131
|
cleanup: () => { },
|
|
@@ -70,7 +154,8 @@ export const createAnalyticsModule = ({ axiosClient, serverUrl, appId, userAuthM
|
|
|
70
154
|
}
|
|
71
155
|
};
|
|
72
156
|
const flush = async (eventsData, options = {}) => {
|
|
73
|
-
|
|
157
|
+
// Consent may have been withdrawn while these events waited.
|
|
158
|
+
if (eventsData.length === 0 || !isAnalyticsAllowed())
|
|
74
159
|
return;
|
|
75
160
|
const sessionContext_ = await getSessionContext(userAuthModule);
|
|
76
161
|
const events = eventsData.map(transformEventDataToApiRequestData(sessionContext_));
|
|
@@ -90,7 +175,8 @@ export const createAnalyticsModule = ({ axiosClient, serverUrl, appId, userAuthM
|
|
|
90
175
|
});
|
|
91
176
|
};
|
|
92
177
|
const track = (params) => {
|
|
93
|
-
if (
|
|
178
|
+
if (!isAnalyticsAllowed() ||
|
|
179
|
+
analyticsSharedState.requestsQueue.length >= maxQueueSize) {
|
|
94
180
|
return;
|
|
95
181
|
}
|
|
96
182
|
const intrinsicData = getEventIntrinsicData();
|
|
@@ -126,23 +212,45 @@ export const createAnalyticsModule = ({ axiosClient, serverUrl, appId, userAuthM
|
|
|
126
212
|
onDocVisible();
|
|
127
213
|
}
|
|
128
214
|
};
|
|
129
|
-
|
|
215
|
+
let isStarted = false;
|
|
216
|
+
const start = () => {
|
|
217
|
+
if (isStarted)
|
|
218
|
+
return;
|
|
219
|
+
isStarted = true;
|
|
220
|
+
// start the flusing process ///
|
|
221
|
+
startProcessing();
|
|
222
|
+
// start the heart beat processor //
|
|
223
|
+
clearHeartBeatProcessor = startHeartBeatProcessor(track);
|
|
224
|
+
// track the referrer event //
|
|
225
|
+
trackInitializationEvent(track);
|
|
226
|
+
// start the visibility change listener //
|
|
227
|
+
if (typeof window !== "undefined") {
|
|
228
|
+
window.addEventListener("visibilitychange", onVisibilityChange);
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
const stop = () => {
|
|
232
|
+
isStarted = false;
|
|
130
233
|
stopAnalyticsProcessor();
|
|
131
234
|
clearHeartBeatProcessor === null || clearHeartBeatProcessor === void 0 ? void 0 : clearHeartBeatProcessor();
|
|
132
235
|
if (typeof window !== "undefined") {
|
|
133
236
|
window.removeEventListener("visibilitychange", onVisibilityChange);
|
|
134
237
|
}
|
|
135
238
|
};
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
239
|
+
const stopWatchingConsent = onConsentChange((granted) => {
|
|
240
|
+
if (granted) {
|
|
241
|
+
start();
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
stop();
|
|
245
|
+
analyticsSharedState.requestsQueue.splice(0);
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
const cleanup = () => {
|
|
249
|
+
stopWatchingConsent();
|
|
250
|
+
stop();
|
|
251
|
+
};
|
|
252
|
+
if (isAnalyticsAllowed())
|
|
253
|
+
start();
|
|
146
254
|
return {
|
|
147
255
|
track,
|
|
148
256
|
cleanup,
|
|
@@ -338,19 +446,21 @@ function getFallbackSessionId() {
|
|
|
338
446
|
return ((_a = analyticsSharedState.fallbackSessionId) !== null && _a !== void 0 ? _a : (analyticsSharedState.fallbackSessionId = generateUuid()));
|
|
339
447
|
}
|
|
340
448
|
export function getAnalyticsSessionId() {
|
|
341
|
-
|
|
449
|
+
var _a;
|
|
450
|
+
if (typeof window === "undefined" || !isAnalyticsAllowed()) {
|
|
342
451
|
return getFallbackSessionId();
|
|
343
452
|
}
|
|
344
453
|
try {
|
|
345
454
|
const sessionId = localStorage.getItem(ANALYTICS_SESSION_ID_LOCAL_STORAGE_KEY);
|
|
346
455
|
if (!sessionId) {
|
|
347
|
-
|
|
456
|
+
// An id used before consent was granted carries on, so it is stored now.
|
|
457
|
+
const newSessionId = (_a = analyticsSharedState.fallbackSessionId) !== null && _a !== void 0 ? _a : generateUuid();
|
|
348
458
|
localStorage.setItem(ANALYTICS_SESSION_ID_LOCAL_STORAGE_KEY, newSessionId);
|
|
349
459
|
return newSessionId;
|
|
350
460
|
}
|
|
351
461
|
return sessionId;
|
|
352
462
|
}
|
|
353
|
-
catch (
|
|
463
|
+
catch (_b) {
|
|
354
464
|
return getFallbackSessionId();
|
|
355
465
|
}
|
|
356
466
|
}
|
package/dist/modules/app-logs.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isAnalyticsAllowed } from "./analytics.js";
|
|
1
2
|
/**
|
|
2
3
|
* Creates the app logs module for the Base44 SDK.
|
|
3
4
|
*
|
|
@@ -11,6 +12,9 @@ export function createAppLogsModule(axios, appId) {
|
|
|
11
12
|
return {
|
|
12
13
|
// Log user activity in the app
|
|
13
14
|
async logUserInApp(pageName) {
|
|
15
|
+
// A page visit feeds App Analytics, so it follows the same consent.
|
|
16
|
+
if (!isAnalyticsAllowed())
|
|
17
|
+
return;
|
|
14
18
|
await axios.post(`${baseURL}/log-user-in-app/${pageName}`);
|
|
15
19
|
},
|
|
16
20
|
// Fetch app logs with optional parameters
|