@getpassage/react-native
Version:
Passage React Native SDK for mobile authentication
166 lines (165 loc) • 5.98 kB
JavaScript
import { logger } from "./logger";
import { extractSessionId } from "./jwt-utils";
// Analytics event types - all events start with SDK_
export const ANALYTICS_EVENTS = {
SDK_MODAL_OPENED: "SDK_MODAL_OPENED",
SDK_MODAL_CLOSED: "SDK_MODAL_CLOSED",
SDK_CONFIGURE_START: "SDK_CONFIGURE_START",
SDK_CONFIGURE_SUCCESS: "SDK_CONFIGURE_SUCCESS",
SDK_CONFIGURE_ERROR: "SDK_CONFIGURE_ERROR",
SDK_CONFIGURATION_REQUEST: "SDK_CONFIGURATION_REQUEST",
SDK_CONFIGURATION_SUCCESS: "SDK_CONFIGURATION_SUCCESS",
SDK_CONFIGURATION_ERROR: "SDK_CONFIGURATION_ERROR",
SDK_OPEN_REQUEST: "SDK_OPEN_REQUEST",
SDK_OPEN_SUCCESS: "SDK_OPEN_SUCCESS",
SDK_OPEN_ERROR: "SDK_OPEN_ERROR",
SDK_ON_SUCCESS: "SDK_ON_SUCCESS",
SDK_ON_ERROR: "SDK_ON_ERROR",
SDK_REMOTE_CONTROL_CONNECT_START: "SDK_REMOTE_CONTROL_CONNECT_START",
SDK_REMOTE_CONTROL_CONNECT_SUCCESS: "SDK_REMOTE_CONTROL_CONNECT_SUCCESS",
SDK_REMOTE_CONTROL_CONNECT_ERROR: "SDK_REMOTE_CONTROL_CONNECT_ERROR",
SDK_REMOTE_CONTROL_DISCONNECT: "SDK_REMOTE_CONTROL_DISCONNECT",
SDK_WEBVIEW_SWITCH: "SDK_WEBVIEW_SWITCH",
SDK_NAVIGATION_START: "SDK_NAVIGATION_START",
SDK_NAVIGATION_SUCCESS: "SDK_NAVIGATION_SUCCESS",
SDK_NAVIGATION_ERROR: "SDK_NAVIGATION_ERROR",
SDK_COMMAND_RECEIVED: "SDK_COMMAND_RECEIVED",
SDK_COMMAND_SUCCESS: "SDK_COMMAND_SUCCESS",
SDK_COMMAND_ERROR: "SDK_COMMAND_ERROR",
};
class AnalyticsManager {
constructor() {
this.enabled = false;
this.analyticsUrl = "https://api.getpassage.ai/analytics";
this.sdkName = "react-native";
this.sessionId = null; // Only set if extracted from intent token
this.eventQueue = [];
this.BATCH_SIZE = 10;
this.FLUSH_INTERVAL = 5000; // 5 seconds
// Don't generate sessionId by default - only set when extracted from intent token
try {
// Get SDK version from package.json if available
const packageJson = require("../package.json");
this.sdkVersion = packageJson.version;
}
catch (error) {
// Fallback if package.json is not available
this.sdkVersion = undefined;
}
}
/**
* Update intent token and extract session ID
*/
updateIntentToken(token) {
if (!token) {
this.intentToken = undefined;
this.sessionId = null;
return;
}
this.intentToken = token;
this.sessionId = extractSessionId(token); // Use JWT utility
logger.debug("[ANALYTICS] Intent token updated", {
sessionId: this.sessionId,
hasToken: !!token,
});
}
configure(config) {
var _a;
this.enabled = (_a = config.enabled) !== null && _a !== void 0 ? _a : true; // Default to enabled
logger.debug("[ANALYTICS] Configured analytics", {
enabled: this.enabled,
analyticsUrl: this.analyticsUrl,
sdkVersion: this.sdkVersion,
sdkName: this.sdkName,
sessionId: this.sessionId,
});
if (this.enabled) {
this.startBatchFlushTimer();
}
}
track(event, metadata) {
if (!this.enabled) {
return;
}
const analyticsEvent = {
event,
source: "sdk",
sdkName: this.sdkName,
sdkVersion: this.sdkVersion,
platform: "react-native",
timestamp: new Date().toISOString(),
metadata: metadata || {},
};
// Only include sessionId if it was successfully extracted from intent token
if (this.sessionId) {
analyticsEvent.sessionId = this.sessionId;
}
this.eventQueue.push(analyticsEvent);
logger.debug("[ANALYTICS] Event queued", {
event,
sessionId: this.sessionId,
queueSize: this.eventQueue.length,
});
// Flush immediately if queue is full
if (this.eventQueue.length >= this.BATCH_SIZE) {
this.flush();
}
}
async flush() {
if (!this.enabled || this.eventQueue.length === 0) {
return;
}
const eventsToSend = [...this.eventQueue];
this.eventQueue = [];
try {
logger.debug("[ANALYTICS] Flushing events", {
count: eventsToSend.length,
});
const response = await fetch(this.analyticsUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
events: eventsToSend,
}),
});
if (response.ok) {
logger.debug("[ANALYTICS] Events sent successfully", {
count: eventsToSend.length,
status: response.status,
});
}
else {
logger.error("[ANALYTICS] Failed to send events", {
status: response.status,
response: await response.text(),
});
// Re-queue events on failure
this.eventQueue.unshift(...eventsToSend);
}
}
catch (error) {
logger.error("[ANALYTICS] Error sending events", error);
// Re-queue events on error
this.eventQueue.unshift(...eventsToSend);
}
}
startBatchFlushTimer() {
if (this.flushTimer) {
clearInterval(this.flushTimer);
}
this.flushTimer = setInterval(() => {
this.flush();
}, this.FLUSH_INTERVAL);
}
destroy() {
if (this.flushTimer) {
clearInterval(this.flushTimer);
this.flushTimer = undefined;
}
// Flush any remaining events
this.flush();
}
}
export const analytics = new AnalyticsManager();