@formo/analytics-react-native 0.1.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/README.md +302 -0
- package/lib/commonjs/FormoAnalytics.js +526 -0
- package/lib/commonjs/FormoAnalytics.js.map +1 -0
- package/lib/commonjs/FormoAnalyticsProvider.js +265 -0
- package/lib/commonjs/FormoAnalyticsProvider.js.map +1 -0
- package/lib/commonjs/constants/config.js +69 -0
- package/lib/commonjs/constants/config.js.map +1 -0
- package/lib/commonjs/constants/events.js +30 -0
- package/lib/commonjs/constants/events.js.map +1 -0
- package/lib/commonjs/constants/index.js +39 -0
- package/lib/commonjs/constants/index.js.map +1 -0
- package/lib/commonjs/constants/storage.js +23 -0
- package/lib/commonjs/constants/storage.js.map +1 -0
- package/lib/commonjs/index.js +65 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/lib/consent/index.js +56 -0
- package/lib/commonjs/lib/consent/index.js.map +1 -0
- package/lib/commonjs/lib/event/EventFactory.js +493 -0
- package/lib/commonjs/lib/event/EventFactory.js.map +1 -0
- package/lib/commonjs/lib/event/EventManager.js +46 -0
- package/lib/commonjs/lib/event/EventManager.js.map +1 -0
- package/lib/commonjs/lib/event/EventQueue.js +290 -0
- package/lib/commonjs/lib/event/EventQueue.js.map +1 -0
- package/lib/commonjs/lib/event/index.js +50 -0
- package/lib/commonjs/lib/event/index.js.map +1 -0
- package/lib/commonjs/lib/event/types.js +6 -0
- package/lib/commonjs/lib/event/types.js.map +1 -0
- package/lib/commonjs/lib/lifecycle/index.js +196 -0
- package/lib/commonjs/lib/lifecycle/index.js.map +1 -0
- package/lib/commonjs/lib/logger/index.js +48 -0
- package/lib/commonjs/lib/logger/index.js.map +1 -0
- package/lib/commonjs/lib/session/index.js +109 -0
- package/lib/commonjs/lib/session/index.js.map +1 -0
- package/lib/commonjs/lib/storage/AsyncStorageAdapter.js +164 -0
- package/lib/commonjs/lib/storage/AsyncStorageAdapter.js.map +1 -0
- package/lib/commonjs/lib/storage/MemoryStorage.js +41 -0
- package/lib/commonjs/lib/storage/MemoryStorage.js.map +1 -0
- package/lib/commonjs/lib/storage/StorageBlueprint.js +24 -0
- package/lib/commonjs/lib/storage/StorageBlueprint.js.map +1 -0
- package/lib/commonjs/lib/storage/StorageManager.js +126 -0
- package/lib/commonjs/lib/storage/StorageManager.js.map +1 -0
- package/lib/commonjs/lib/storage/index.js +49 -0
- package/lib/commonjs/lib/storage/index.js.map +1 -0
- package/lib/commonjs/lib/storage/types.js +2 -0
- package/lib/commonjs/lib/storage/types.js.map +1 -0
- package/lib/commonjs/lib/wagmi/WagmiEventHandler.js +445 -0
- package/lib/commonjs/lib/wagmi/WagmiEventHandler.js.map +1 -0
- package/lib/commonjs/lib/wagmi/index.js +28 -0
- package/lib/commonjs/lib/wagmi/index.js.map +1 -0
- package/lib/commonjs/lib/wagmi/types.js +2 -0
- package/lib/commonjs/lib/wagmi/types.js.map +1 -0
- package/lib/commonjs/types/base.js +6 -0
- package/lib/commonjs/types/base.js.map +1 -0
- package/lib/commonjs/types/events.js +22 -0
- package/lib/commonjs/types/events.js.map +1 -0
- package/lib/commonjs/types/index.js +28 -0
- package/lib/commonjs/types/index.js.map +1 -0
- package/lib/commonjs/utils/address.js +82 -0
- package/lib/commonjs/utils/address.js.map +1 -0
- package/lib/commonjs/utils/hash.js +30 -0
- package/lib/commonjs/utils/hash.js.map +1 -0
- package/lib/commonjs/utils/helpers.js +116 -0
- package/lib/commonjs/utils/helpers.js.map +1 -0
- package/lib/commonjs/utils/index.js +61 -0
- package/lib/commonjs/utils/index.js.map +1 -0
- package/lib/commonjs/utils/timestamp.js +34 -0
- package/lib/commonjs/utils/timestamp.js.map +1 -0
- package/lib/commonjs/utils/trafficSource.js +147 -0
- package/lib/commonjs/utils/trafficSource.js.map +1 -0
- package/lib/commonjs/version.js +10 -0
- package/lib/commonjs/version.js.map +1 -0
- package/lib/module/FormoAnalytics.js +519 -0
- package/lib/module/FormoAnalytics.js.map +1 -0
- package/lib/module/FormoAnalyticsProvider.js +256 -0
- package/lib/module/FormoAnalyticsProvider.js.map +1 -0
- package/lib/module/constants/config.js +62 -0
- package/lib/module/constants/config.js.map +1 -0
- package/lib/module/constants/events.js +24 -0
- package/lib/module/constants/events.js.map +1 -0
- package/lib/module/constants/index.js +4 -0
- package/lib/module/constants/index.js.map +1 -0
- package/lib/module/constants/storage.js +17 -0
- package/lib/module/constants/storage.js.map +1 -0
- package/lib/module/index.js +51 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/lib/consent/index.js +49 -0
- package/lib/module/lib/consent/index.js.map +1 -0
- package/lib/module/lib/event/EventFactory.js +488 -0
- package/lib/module/lib/event/EventFactory.js.map +1 -0
- package/lib/module/lib/event/EventManager.js +41 -0
- package/lib/module/lib/event/EventManager.js.map +1 -0
- package/lib/module/lib/event/EventQueue.js +283 -0
- package/lib/module/lib/event/EventQueue.js.map +1 -0
- package/lib/module/lib/event/index.js +5 -0
- package/lib/module/lib/event/index.js.map +1 -0
- package/lib/module/lib/event/types.js +2 -0
- package/lib/module/lib/event/types.js.map +1 -0
- package/lib/module/lib/lifecycle/index.js +190 -0
- package/lib/module/lib/lifecycle/index.js.map +1 -0
- package/lib/module/lib/logger/index.js +42 -0
- package/lib/module/lib/logger/index.js.map +1 -0
- package/lib/module/lib/session/index.js +92 -0
- package/lib/module/lib/session/index.js.map +1 -0
- package/lib/module/lib/storage/AsyncStorageAdapter.js +158 -0
- package/lib/module/lib/storage/AsyncStorageAdapter.js.map +1 -0
- package/lib/module/lib/storage/MemoryStorage.js +35 -0
- package/lib/module/lib/storage/MemoryStorage.js.map +1 -0
- package/lib/module/lib/storage/StorageBlueprint.js +18 -0
- package/lib/module/lib/storage/StorageBlueprint.js.map +1 -0
- package/lib/module/lib/storage/StorageManager.js +115 -0
- package/lib/module/lib/storage/StorageManager.js.map +1 -0
- package/lib/module/lib/storage/index.js +5 -0
- package/lib/module/lib/storage/index.js.map +1 -0
- package/lib/module/lib/storage/types.js +2 -0
- package/lib/module/lib/storage/types.js.map +1 -0
- package/lib/module/lib/wagmi/WagmiEventHandler.js +439 -0
- package/lib/module/lib/wagmi/WagmiEventHandler.js.map +1 -0
- package/lib/module/lib/wagmi/index.js +3 -0
- package/lib/module/lib/wagmi/index.js.map +1 -0
- package/lib/module/lib/wagmi/types.js +2 -0
- package/lib/module/lib/wagmi/types.js.map +1 -0
- package/lib/module/types/base.js +2 -0
- package/lib/module/types/base.js.map +1 -0
- package/lib/module/types/events.js +17 -0
- package/lib/module/types/events.js.map +1 -0
- package/lib/module/types/index.js +3 -0
- package/lib/module/types/index.js.map +1 -0
- package/lib/module/utils/address.js +74 -0
- package/lib/module/utils/address.js.map +1 -0
- package/lib/module/utils/hash.js +24 -0
- package/lib/module/utils/hash.js.map +1 -0
- package/lib/module/utils/helpers.js +105 -0
- package/lib/module/utils/helpers.js.map +1 -0
- package/lib/module/utils/index.js +6 -0
- package/lib/module/utils/index.js.map +1 -0
- package/lib/module/utils/timestamp.js +26 -0
- package/lib/module/utils/timestamp.js.map +1 -0
- package/lib/module/utils/trafficSource.js +137 -0
- package/lib/module/utils/trafficSource.js.map +1 -0
- package/lib/module/version.js +4 -0
- package/lib/module/version.js.map +1 -0
- package/lib/typescript/FormoAnalytics.d.ts +163 -0
- package/lib/typescript/FormoAnalytics.d.ts.map +1 -0
- package/lib/typescript/FormoAnalyticsProvider.d.ts +29 -0
- package/lib/typescript/FormoAnalyticsProvider.d.ts.map +1 -0
- package/lib/typescript/constants/config.d.ts +8 -0
- package/lib/typescript/constants/config.d.ts.map +1 -0
- package/lib/typescript/constants/events.d.ts +23 -0
- package/lib/typescript/constants/events.d.ts.map +1 -0
- package/lib/typescript/constants/index.d.ts +4 -0
- package/lib/typescript/constants/index.d.ts.map +1 -0
- package/lib/typescript/constants/storage.d.ts +10 -0
- package/lib/typescript/constants/storage.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +44 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/lib/typescript/lib/consent/index.d.ts +13 -0
- package/lib/typescript/lib/consent/index.d.ts.map +1 -0
- package/lib/typescript/lib/event/EventFactory.d.ts +61 -0
- package/lib/typescript/lib/event/EventFactory.d.ts.map +1 -0
- package/lib/typescript/lib/event/EventManager.d.ts +17 -0
- package/lib/typescript/lib/event/EventManager.d.ts.map +1 -0
- package/lib/typescript/lib/event/EventQueue.d.ts +74 -0
- package/lib/typescript/lib/event/EventQueue.d.ts.map +1 -0
- package/lib/typescript/lib/event/index.d.ts +5 -0
- package/lib/typescript/lib/event/index.d.ts.map +1 -0
- package/lib/typescript/lib/event/types.d.ts +23 -0
- package/lib/typescript/lib/event/types.d.ts.map +1 -0
- package/lib/typescript/lib/lifecycle/index.d.ts +46 -0
- package/lib/typescript/lib/lifecycle/index.d.ts.map +1 -0
- package/lib/typescript/lib/logger/index.d.ts +19 -0
- package/lib/typescript/lib/logger/index.d.ts.map +1 -0
- package/lib/typescript/lib/session/index.d.ts +41 -0
- package/lib/typescript/lib/session/index.d.ts.map +1 -0
- package/lib/typescript/lib/storage/AsyncStorageAdapter.d.ts +48 -0
- package/lib/typescript/lib/storage/AsyncStorageAdapter.d.ts.map +1 -0
- package/lib/typescript/lib/storage/MemoryStorage.d.ts +18 -0
- package/lib/typescript/lib/storage/MemoryStorage.d.ts.map +1 -0
- package/lib/typescript/lib/storage/StorageBlueprint.d.ts +21 -0
- package/lib/typescript/lib/storage/StorageBlueprint.d.ts.map +1 -0
- package/lib/typescript/lib/storage/StorageManager.d.ts +45 -0
- package/lib/typescript/lib/storage/StorageManager.d.ts.map +1 -0
- package/lib/typescript/lib/storage/index.d.ts +5 -0
- package/lib/typescript/lib/storage/index.d.ts.map +1 -0
- package/lib/typescript/lib/storage/types.d.ts +22 -0
- package/lib/typescript/lib/storage/types.d.ts.map +1 -0
- package/lib/typescript/lib/wagmi/WagmiEventHandler.d.ts +104 -0
- package/lib/typescript/lib/wagmi/WagmiEventHandler.d.ts.map +1 -0
- package/lib/typescript/lib/wagmi/index.d.ts +3 -0
- package/lib/typescript/lib/wagmi/index.d.ts.map +1 -0
- package/lib/typescript/lib/wagmi/types.d.ts +54 -0
- package/lib/typescript/lib/wagmi/types.d.ts.map +1 -0
- package/lib/typescript/types/base.d.ts +219 -0
- package/lib/typescript/types/base.d.ts.map +1 -0
- package/lib/typescript/types/events.d.ts +111 -0
- package/lib/typescript/types/events.d.ts.map +1 -0
- package/lib/typescript/types/index.d.ts +3 -0
- package/lib/typescript/types/index.d.ts.map +1 -0
- package/lib/typescript/utils/address.d.ts +25 -0
- package/lib/typescript/utils/address.d.ts.map +1 -0
- package/lib/typescript/utils/hash.d.ts +10 -0
- package/lib/typescript/utils/hash.d.ts.map +1 -0
- package/lib/typescript/utils/helpers.d.ts +26 -0
- package/lib/typescript/utils/helpers.d.ts.map +1 -0
- package/lib/typescript/utils/index.d.ts +6 -0
- package/lib/typescript/utils/index.d.ts.map +1 -0
- package/lib/typescript/utils/timestamp.d.ts +13 -0
- package/lib/typescript/utils/timestamp.d.ts.map +1 -0
- package/lib/typescript/utils/trafficSource.d.ts +30 -0
- package/lib/typescript/utils/trafficSource.d.ts.map +1 -0
- package/lib/typescript/version.d.ts +2 -0
- package/lib/typescript/version.d.ts.map +1 -0
- package/package.json +143 -0
- package/src/FormoAnalytics.ts +685 -0
- package/src/FormoAnalyticsProvider.tsx +296 -0
- package/src/constants/config.ts +62 -0
- package/src/constants/events.ts +26 -0
- package/src/constants/index.ts +3 -0
- package/src/constants/storage.ts +16 -0
- package/src/index.ts +55 -0
- package/src/lib/consent/index.ts +52 -0
- package/src/lib/event/EventFactory.ts +682 -0
- package/src/lib/event/EventManager.ts +50 -0
- package/src/lib/event/EventQueue.ts +371 -0
- package/src/lib/event/index.ts +4 -0
- package/src/lib/event/types.ts +107 -0
- package/src/lib/lifecycle/index.ts +215 -0
- package/src/lib/logger/index.ts +56 -0
- package/src/lib/session/index.ts +103 -0
- package/src/lib/storage/AsyncStorageAdapter.ts +173 -0
- package/src/lib/storage/MemoryStorage.ts +43 -0
- package/src/lib/storage/StorageBlueprint.ts +30 -0
- package/src/lib/storage/StorageManager.ts +121 -0
- package/src/lib/storage/index.ts +4 -0
- package/src/lib/storage/types.ts +23 -0
- package/src/lib/wagmi/WagmiEventHandler.ts +574 -0
- package/src/lib/wagmi/index.ts +2 -0
- package/src/lib/wagmi/types.ts +71 -0
- package/src/types/base.ts +287 -0
- package/src/types/events.ts +140 -0
- package/src/types/index.ts +2 -0
- package/src/utils/address.ts +84 -0
- package/src/utils/hash.ts +23 -0
- package/src/utils/helpers.ts +139 -0
- package/src/utils/index.ts +5 -0
- package/src/utils/timestamp.ts +25 -0
- package/src/utils/trafficSource.ts +153 -0
- package/src/version.ts +3 -0
|
@@ -0,0 +1,682 @@
|
|
|
1
|
+
import { Platform, NativeModules, Dimensions } from "react-native";
|
|
2
|
+
import NetInfo from "@react-native-community/netinfo";
|
|
3
|
+
|
|
4
|
+
// Lazy load device info to handle Expo Go where native modules may not exist
|
|
5
|
+
let DeviceInfo: typeof import("react-native-device-info").default | null = null;
|
|
6
|
+
let ExpoDevice: typeof import("expo-device") | null = null;
|
|
7
|
+
let ExpoApplication: typeof import("expo-application") | null = null;
|
|
8
|
+
|
|
9
|
+
// Try to load react-native-device-info (works in bare RN and dev builds)
|
|
10
|
+
try {
|
|
11
|
+
DeviceInfo = require("react-native-device-info").default;
|
|
12
|
+
} catch {
|
|
13
|
+
// Not available - try Expo alternatives
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Try to load Expo modules (works in Expo Go and Expo dev builds)
|
|
17
|
+
try {
|
|
18
|
+
ExpoDevice = require("expo-device");
|
|
19
|
+
} catch {
|
|
20
|
+
// Not available
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
ExpoApplication = require("expo-application");
|
|
25
|
+
} catch {
|
|
26
|
+
// Not available
|
|
27
|
+
}
|
|
28
|
+
import { COUNTRY_LIST, LOCAL_ANONYMOUS_ID_KEY, CHANNEL, VERSION } from "../../constants";
|
|
29
|
+
import {
|
|
30
|
+
Address,
|
|
31
|
+
APIEvent,
|
|
32
|
+
ChainID,
|
|
33
|
+
IFormoEvent,
|
|
34
|
+
IFormoEventContext,
|
|
35
|
+
IFormoEventProperties,
|
|
36
|
+
Nullable,
|
|
37
|
+
Options,
|
|
38
|
+
SignatureStatus,
|
|
39
|
+
TransactionStatus,
|
|
40
|
+
} from "../../types";
|
|
41
|
+
import {
|
|
42
|
+
toChecksumAddress,
|
|
43
|
+
getValidAddress,
|
|
44
|
+
toSnakeCase,
|
|
45
|
+
mergeDeepRight,
|
|
46
|
+
getStoredTrafficSource,
|
|
47
|
+
} from "../../utils";
|
|
48
|
+
import { getCurrentTimeFormatted } from "../../utils/timestamp";
|
|
49
|
+
import { generateUUID } from "../../utils/hash";
|
|
50
|
+
import { logger } from "../logger";
|
|
51
|
+
import { storage } from "../storage";
|
|
52
|
+
import { IEventFactory } from "./types";
|
|
53
|
+
import { version as SDK_VERSION } from "../../version";
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Generate or retrieve anonymous ID
|
|
57
|
+
*/
|
|
58
|
+
function generateAnonymousId(key: string): string {
|
|
59
|
+
const existing = storage().get(key);
|
|
60
|
+
if (existing) {
|
|
61
|
+
return existing;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const newId = generateUUID();
|
|
65
|
+
storage().set(key, newId);
|
|
66
|
+
return newId;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Event factory for React Native
|
|
71
|
+
* Creates event payloads with mobile-specific context
|
|
72
|
+
*/
|
|
73
|
+
class EventFactory implements IEventFactory {
|
|
74
|
+
private options?: Options;
|
|
75
|
+
|
|
76
|
+
constructor(options?: Options) {
|
|
77
|
+
this.options = options;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Get device timezone
|
|
82
|
+
*/
|
|
83
|
+
private getTimezone(): string {
|
|
84
|
+
try {
|
|
85
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
86
|
+
} catch (error) {
|
|
87
|
+
logger.error("Error resolving timezone:", error);
|
|
88
|
+
return "";
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Get location from timezone
|
|
94
|
+
*/
|
|
95
|
+
private getLocation(): string {
|
|
96
|
+
try {
|
|
97
|
+
const timezone = this.getTimezone();
|
|
98
|
+
if (timezone in COUNTRY_LIST) {
|
|
99
|
+
return COUNTRY_LIST[timezone] ?? timezone;
|
|
100
|
+
}
|
|
101
|
+
return timezone;
|
|
102
|
+
} catch (error) {
|
|
103
|
+
logger.error("Error resolving location:", error);
|
|
104
|
+
return "";
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Get device language/locale
|
|
110
|
+
*/
|
|
111
|
+
private getLanguage(): string {
|
|
112
|
+
try {
|
|
113
|
+
// Try to get the device locale
|
|
114
|
+
const locale =
|
|
115
|
+
Platform.OS === "ios"
|
|
116
|
+
? NativeModules.SettingsManager?.settings?.AppleLocale ||
|
|
117
|
+
NativeModules.SettingsManager?.settings?.AppleLanguages?.[0]
|
|
118
|
+
: NativeModules.I18nManager?.localeIdentifier;
|
|
119
|
+
|
|
120
|
+
return locale || "en";
|
|
121
|
+
} catch (error) {
|
|
122
|
+
logger.debug("Error resolving language:", error);
|
|
123
|
+
return "en";
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Get screen dimensions
|
|
129
|
+
*/
|
|
130
|
+
private getScreen(): {
|
|
131
|
+
screen_width: number;
|
|
132
|
+
screen_height: number;
|
|
133
|
+
screen_density: number;
|
|
134
|
+
} {
|
|
135
|
+
try {
|
|
136
|
+
const { width, height, scale } = Dimensions.get("screen");
|
|
137
|
+
return {
|
|
138
|
+
screen_width: Math.round(width),
|
|
139
|
+
screen_height: Math.round(height),
|
|
140
|
+
screen_density: scale,
|
|
141
|
+
};
|
|
142
|
+
} catch (error) {
|
|
143
|
+
logger.error("Error resolving screen properties:", error);
|
|
144
|
+
return {
|
|
145
|
+
screen_width: 0,
|
|
146
|
+
screen_height: 0,
|
|
147
|
+
screen_density: 1,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Get network information
|
|
154
|
+
*/
|
|
155
|
+
private async getNetworkInfo(): Promise<{
|
|
156
|
+
network_wifi?: boolean;
|
|
157
|
+
network_cellular?: boolean;
|
|
158
|
+
network_carrier?: string;
|
|
159
|
+
}> {
|
|
160
|
+
try {
|
|
161
|
+
const netState = await NetInfo.fetch();
|
|
162
|
+
|
|
163
|
+
const networkInfo: {
|
|
164
|
+
network_wifi?: boolean;
|
|
165
|
+
network_cellular?: boolean;
|
|
166
|
+
network_carrier?: string;
|
|
167
|
+
} = {};
|
|
168
|
+
|
|
169
|
+
// Set connection type flags
|
|
170
|
+
if (netState.type === "wifi") {
|
|
171
|
+
networkInfo.network_wifi = true;
|
|
172
|
+
networkInfo.network_cellular = false;
|
|
173
|
+
} else if (netState.type === "cellular") {
|
|
174
|
+
networkInfo.network_wifi = false;
|
|
175
|
+
networkInfo.network_cellular = true;
|
|
176
|
+
|
|
177
|
+
// Get carrier name for cellular connections
|
|
178
|
+
if (netState.details && "carrier" in netState.details) {
|
|
179
|
+
networkInfo.network_carrier = netState.details.carrier || undefined;
|
|
180
|
+
}
|
|
181
|
+
} else {
|
|
182
|
+
// Other types (ethernet, bluetooth, wimax, vpn, other, unknown, none)
|
|
183
|
+
networkInfo.network_wifi = false;
|
|
184
|
+
networkInfo.network_cellular = false;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return networkInfo;
|
|
188
|
+
} catch (error) {
|
|
189
|
+
logger.debug("Error getting network info:", error);
|
|
190
|
+
return {};
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Get device information
|
|
196
|
+
* Supports both react-native-device-info (bare RN) and expo-device/expo-application (Expo Go)
|
|
197
|
+
*/
|
|
198
|
+
private async getDeviceInfo(): Promise<{
|
|
199
|
+
os_name: string;
|
|
200
|
+
os_version: string;
|
|
201
|
+
device_model: string;
|
|
202
|
+
device_manufacturer: string;
|
|
203
|
+
device_name: string;
|
|
204
|
+
device_type: string;
|
|
205
|
+
user_agent: string;
|
|
206
|
+
app_name: string;
|
|
207
|
+
app_version: string;
|
|
208
|
+
app_build: string;
|
|
209
|
+
app_bundle_id: string;
|
|
210
|
+
}> {
|
|
211
|
+
// Try react-native-device-info first (bare RN and Expo dev builds)
|
|
212
|
+
if (DeviceInfo) {
|
|
213
|
+
try {
|
|
214
|
+
const [model, manufacturer, deviceName, userAgent, isTablet] = await Promise.all([
|
|
215
|
+
DeviceInfo.getModel(),
|
|
216
|
+
DeviceInfo.getManufacturer(),
|
|
217
|
+
DeviceInfo.getDeviceName(),
|
|
218
|
+
DeviceInfo.getUserAgent(),
|
|
219
|
+
DeviceInfo.isTablet(),
|
|
220
|
+
]);
|
|
221
|
+
|
|
222
|
+
return {
|
|
223
|
+
os_name: Platform.OS,
|
|
224
|
+
os_version: DeviceInfo.getSystemVersion(),
|
|
225
|
+
device_model: model,
|
|
226
|
+
device_manufacturer: manufacturer,
|
|
227
|
+
device_name: deviceName,
|
|
228
|
+
device_type: isTablet ? "tablet" : "mobile",
|
|
229
|
+
user_agent: userAgent,
|
|
230
|
+
app_name: DeviceInfo.getApplicationName(),
|
|
231
|
+
app_version: DeviceInfo.getVersion(),
|
|
232
|
+
app_build: DeviceInfo.getBuildNumber(),
|
|
233
|
+
app_bundle_id: DeviceInfo.getBundleId(),
|
|
234
|
+
};
|
|
235
|
+
} catch (error) {
|
|
236
|
+
logger.debug("Error using react-native-device-info, falling back:", error);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Fall back to Expo modules (Expo Go)
|
|
241
|
+
if (ExpoDevice || ExpoApplication) {
|
|
242
|
+
try {
|
|
243
|
+
const isTablet = ExpoDevice?.deviceType === ExpoDevice?.DeviceType?.TABLET;
|
|
244
|
+
return {
|
|
245
|
+
os_name: ExpoDevice?.osName || Platform.OS,
|
|
246
|
+
os_version: ExpoDevice?.osVersion || String(Platform.Version),
|
|
247
|
+
device_model: ExpoDevice?.modelName || "Unknown",
|
|
248
|
+
device_manufacturer: ExpoDevice?.manufacturer || "Unknown",
|
|
249
|
+
device_name: ExpoDevice?.deviceName || "Unknown Device",
|
|
250
|
+
device_type: isTablet ? "tablet" : "mobile",
|
|
251
|
+
user_agent: "",
|
|
252
|
+
app_name: ExpoApplication?.applicationName || "",
|
|
253
|
+
app_version: ExpoApplication?.nativeApplicationVersion || "",
|
|
254
|
+
app_build: ExpoApplication?.nativeBuildVersion || "",
|
|
255
|
+
app_bundle_id: ExpoApplication?.applicationId || "",
|
|
256
|
+
};
|
|
257
|
+
} catch (error) {
|
|
258
|
+
logger.debug("Error using Expo device modules:", error);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Final fallback - minimal info from Platform
|
|
263
|
+
logger.debug("No device info modules available, using Platform defaults");
|
|
264
|
+
return {
|
|
265
|
+
os_name: Platform.OS,
|
|
266
|
+
os_version: String(Platform.Version),
|
|
267
|
+
device_model: "Unknown",
|
|
268
|
+
device_manufacturer: "Unknown",
|
|
269
|
+
device_name: "Unknown Device",
|
|
270
|
+
device_type: "mobile",
|
|
271
|
+
user_agent: "",
|
|
272
|
+
app_name: "",
|
|
273
|
+
app_version: "",
|
|
274
|
+
app_build: "",
|
|
275
|
+
app_bundle_id: "",
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Generate context with mobile-specific metadata
|
|
281
|
+
*/
|
|
282
|
+
private async generateContext(
|
|
283
|
+
context?: IFormoEventContext
|
|
284
|
+
): Promise<IFormoEventContext> {
|
|
285
|
+
const language = this.getLanguage();
|
|
286
|
+
const timezone = this.getTimezone();
|
|
287
|
+
const location = this.getLocation();
|
|
288
|
+
const deviceInfo = await this.getDeviceInfo();
|
|
289
|
+
const networkInfo = await this.getNetworkInfo();
|
|
290
|
+
const screenInfo = this.getScreen();
|
|
291
|
+
|
|
292
|
+
// Get stored traffic source from session (UTM params, referrer from deep links)
|
|
293
|
+
const storedTrafficSource = getStoredTrafficSource();
|
|
294
|
+
|
|
295
|
+
const defaultContext: IFormoEventContext = {
|
|
296
|
+
locale: language,
|
|
297
|
+
timezone,
|
|
298
|
+
location,
|
|
299
|
+
library_name: "Formo React Native SDK",
|
|
300
|
+
library_version: SDK_VERSION,
|
|
301
|
+
...deviceInfo,
|
|
302
|
+
...networkInfo,
|
|
303
|
+
...screenInfo,
|
|
304
|
+
// App info from options (overrides auto-detected values)
|
|
305
|
+
...(this.options?.app?.name && { app_name: this.options.app.name }),
|
|
306
|
+
...(this.options?.app?.version && { app_version: this.options.app.version }),
|
|
307
|
+
...(this.options?.app?.build && { app_build: this.options.app.build }),
|
|
308
|
+
...(this.options?.app?.bundleId && { app_bundle_id: this.options.app.bundleId }),
|
|
309
|
+
// Traffic source (UTM params, referrer) from session
|
|
310
|
+
...(storedTrafficSource || {}),
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
const mergedContext = mergeDeepRight(
|
|
314
|
+
defaultContext,
|
|
315
|
+
context || {}
|
|
316
|
+
) as IFormoEventContext;
|
|
317
|
+
|
|
318
|
+
return mergedContext;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Create enriched event with common properties
|
|
323
|
+
*/
|
|
324
|
+
private async getEnrichedEvent(
|
|
325
|
+
formoEvent: Partial<IFormoEvent>,
|
|
326
|
+
context?: IFormoEventContext
|
|
327
|
+
): Promise<IFormoEvent> {
|
|
328
|
+
const commonEventData: Partial<IFormoEvent> = {
|
|
329
|
+
context: await this.generateContext(context),
|
|
330
|
+
original_timestamp: getCurrentTimeFormatted(),
|
|
331
|
+
user_id: formoEvent.user_id,
|
|
332
|
+
type: formoEvent.type,
|
|
333
|
+
channel: CHANNEL,
|
|
334
|
+
version: VERSION,
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
commonEventData.anonymous_id = generateAnonymousId(LOCAL_ANONYMOUS_ID_KEY);
|
|
338
|
+
|
|
339
|
+
// Handle address - convert undefined to null for consistency
|
|
340
|
+
const validAddress = getValidAddress(formoEvent.address);
|
|
341
|
+
if (validAddress) {
|
|
342
|
+
commonEventData.address = toChecksumAddress(validAddress);
|
|
343
|
+
} else {
|
|
344
|
+
commonEventData.address = null;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const processedEvent = mergeDeepRight(
|
|
348
|
+
formoEvent as Record<string, unknown>,
|
|
349
|
+
commonEventData as Record<string, unknown>
|
|
350
|
+
) as unknown as IFormoEvent;
|
|
351
|
+
|
|
352
|
+
if (processedEvent.event === undefined) {
|
|
353
|
+
processedEvent.event = null;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (processedEvent.properties === undefined) {
|
|
357
|
+
processedEvent.properties = null;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// Extract function_args before snake_case conversion to preserve ABI parameter names
|
|
361
|
+
// (e.g., "tokenId" should not become "token_id" since it's a contract ABI name)
|
|
362
|
+
const functionArgs = (processedEvent.properties as Record<string, unknown>)?.function_args;
|
|
363
|
+
|
|
364
|
+
const converted = toSnakeCase(processedEvent as unknown as Record<string, unknown>) as unknown as IFormoEvent;
|
|
365
|
+
|
|
366
|
+
// Re-attach function_args with original key casing
|
|
367
|
+
if (functionArgs && converted.properties) {
|
|
368
|
+
(converted.properties as Record<string, unknown>).function_args = functionArgs;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
return converted;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Generate screen view event (mobile equivalent of page)
|
|
376
|
+
*/
|
|
377
|
+
async generateScreenEvent(
|
|
378
|
+
name: string,
|
|
379
|
+
category?: string,
|
|
380
|
+
properties?: IFormoEventProperties,
|
|
381
|
+
context?: IFormoEventContext
|
|
382
|
+
): Promise<IFormoEvent> {
|
|
383
|
+
const props = { ...(properties ?? {}), name, ...(category && { category }) };
|
|
384
|
+
|
|
385
|
+
const screenEvent: Partial<IFormoEvent> = {
|
|
386
|
+
properties: props,
|
|
387
|
+
type: "screen",
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
return this.getEnrichedEvent(screenEvent, context);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
async generateDetectWalletEvent(
|
|
394
|
+
providerName: string,
|
|
395
|
+
rdns: string,
|
|
396
|
+
properties?: IFormoEventProperties,
|
|
397
|
+
context?: IFormoEventContext
|
|
398
|
+
): Promise<IFormoEvent> {
|
|
399
|
+
const detectEvent: Partial<IFormoEvent> = {
|
|
400
|
+
properties: {
|
|
401
|
+
providerName,
|
|
402
|
+
rdns,
|
|
403
|
+
...properties,
|
|
404
|
+
},
|
|
405
|
+
type: "detect",
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
return this.getEnrichedEvent(detectEvent, context);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
async generateIdentifyEvent(
|
|
412
|
+
providerName: string,
|
|
413
|
+
rdns: string,
|
|
414
|
+
address: Nullable<Address>,
|
|
415
|
+
userId?: Nullable<string>,
|
|
416
|
+
properties?: IFormoEventProperties,
|
|
417
|
+
context?: IFormoEventContext
|
|
418
|
+
): Promise<IFormoEvent> {
|
|
419
|
+
const identifyEvent: Partial<IFormoEvent> = {
|
|
420
|
+
properties: {
|
|
421
|
+
providerName,
|
|
422
|
+
rdns,
|
|
423
|
+
...properties,
|
|
424
|
+
},
|
|
425
|
+
user_id: userId,
|
|
426
|
+
address,
|
|
427
|
+
type: "identify",
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
return this.getEnrichedEvent(identifyEvent, context);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
async generateConnectEvent(
|
|
434
|
+
chainId: ChainID,
|
|
435
|
+
address: Address,
|
|
436
|
+
properties?: IFormoEventProperties,
|
|
437
|
+
context?: IFormoEventContext
|
|
438
|
+
): Promise<IFormoEvent> {
|
|
439
|
+
const connectEvent: Partial<IFormoEvent> = {
|
|
440
|
+
properties: {
|
|
441
|
+
chainId,
|
|
442
|
+
...properties,
|
|
443
|
+
},
|
|
444
|
+
address,
|
|
445
|
+
type: "connect",
|
|
446
|
+
};
|
|
447
|
+
|
|
448
|
+
return this.getEnrichedEvent(connectEvent, context);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
async generateDisconnectEvent(
|
|
452
|
+
chainId?: ChainID,
|
|
453
|
+
address?: Address,
|
|
454
|
+
properties?: IFormoEventProperties,
|
|
455
|
+
context?: IFormoEventContext
|
|
456
|
+
): Promise<IFormoEvent> {
|
|
457
|
+
const disconnectEvent: Partial<IFormoEvent> = {
|
|
458
|
+
properties: {
|
|
459
|
+
chainId,
|
|
460
|
+
...properties,
|
|
461
|
+
},
|
|
462
|
+
address,
|
|
463
|
+
type: "disconnect",
|
|
464
|
+
};
|
|
465
|
+
|
|
466
|
+
return this.getEnrichedEvent(disconnectEvent, context);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
async generateChainChangedEvent(
|
|
470
|
+
chainId: ChainID,
|
|
471
|
+
address: Address,
|
|
472
|
+
properties?: IFormoEventProperties,
|
|
473
|
+
context?: IFormoEventContext
|
|
474
|
+
): Promise<IFormoEvent> {
|
|
475
|
+
const chainEvent: Partial<IFormoEvent> = {
|
|
476
|
+
properties: {
|
|
477
|
+
chainId,
|
|
478
|
+
...properties,
|
|
479
|
+
},
|
|
480
|
+
address,
|
|
481
|
+
type: "chain",
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
return this.getEnrichedEvent(chainEvent, context);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
async generateSignatureEvent(
|
|
488
|
+
status: SignatureStatus,
|
|
489
|
+
chainId: ChainID | undefined,
|
|
490
|
+
address: Address,
|
|
491
|
+
message: string,
|
|
492
|
+
signatureHash?: string,
|
|
493
|
+
properties?: IFormoEventProperties,
|
|
494
|
+
context?: IFormoEventContext
|
|
495
|
+
): Promise<IFormoEvent> {
|
|
496
|
+
const signatureEvent: Partial<IFormoEvent> = {
|
|
497
|
+
properties: {
|
|
498
|
+
status,
|
|
499
|
+
...(chainId !== undefined && chainId !== null && { chainId }),
|
|
500
|
+
message,
|
|
501
|
+
...(signatureHash && { signatureHash }),
|
|
502
|
+
...properties,
|
|
503
|
+
},
|
|
504
|
+
address,
|
|
505
|
+
type: "signature",
|
|
506
|
+
};
|
|
507
|
+
|
|
508
|
+
return this.getEnrichedEvent(signatureEvent, context);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
async generateTransactionEvent(
|
|
512
|
+
status: TransactionStatus,
|
|
513
|
+
chainId: ChainID,
|
|
514
|
+
address: Address,
|
|
515
|
+
data?: string,
|
|
516
|
+
to?: string,
|
|
517
|
+
value?: string,
|
|
518
|
+
transactionHash?: string,
|
|
519
|
+
function_name?: string,
|
|
520
|
+
function_args?: Record<string, unknown>,
|
|
521
|
+
properties?: IFormoEventProperties,
|
|
522
|
+
context?: IFormoEventContext
|
|
523
|
+
): Promise<IFormoEvent> {
|
|
524
|
+
const transactionEvent: Partial<IFormoEvent> = {
|
|
525
|
+
properties: {
|
|
526
|
+
status,
|
|
527
|
+
chainId,
|
|
528
|
+
data,
|
|
529
|
+
to,
|
|
530
|
+
value,
|
|
531
|
+
...(transactionHash && { transactionHash }),
|
|
532
|
+
...(function_name && { function_name }),
|
|
533
|
+
...(function_args && { function_args }),
|
|
534
|
+
...properties,
|
|
535
|
+
},
|
|
536
|
+
address,
|
|
537
|
+
type: "transaction",
|
|
538
|
+
};
|
|
539
|
+
|
|
540
|
+
return this.getEnrichedEvent(transactionEvent, context);
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
async generateTrackEvent(
|
|
544
|
+
event: string,
|
|
545
|
+
properties?: IFormoEventProperties,
|
|
546
|
+
context?: IFormoEventContext
|
|
547
|
+
): Promise<IFormoEvent> {
|
|
548
|
+
const trackEvent: Partial<IFormoEvent> = {
|
|
549
|
+
properties: {
|
|
550
|
+
...properties,
|
|
551
|
+
...(properties?.revenue !== undefined && {
|
|
552
|
+
revenue: Number(properties.revenue),
|
|
553
|
+
currency: (typeof properties?.currency === "string"
|
|
554
|
+
? properties.currency
|
|
555
|
+
: "USD"
|
|
556
|
+
).toLowerCase(),
|
|
557
|
+
}),
|
|
558
|
+
...(properties?.points !== undefined && {
|
|
559
|
+
points: Number(properties.points),
|
|
560
|
+
}),
|
|
561
|
+
...(properties?.volume !== undefined && {
|
|
562
|
+
volume: Number(properties.volume),
|
|
563
|
+
}),
|
|
564
|
+
},
|
|
565
|
+
event,
|
|
566
|
+
type: "track",
|
|
567
|
+
};
|
|
568
|
+
|
|
569
|
+
return this.getEnrichedEvent(trackEvent, context);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* Create event from API event type
|
|
574
|
+
*/
|
|
575
|
+
async create(
|
|
576
|
+
event: APIEvent,
|
|
577
|
+
address?: Address,
|
|
578
|
+
userId?: string
|
|
579
|
+
): Promise<IFormoEvent> {
|
|
580
|
+
let formoEvent: Partial<IFormoEvent> = {};
|
|
581
|
+
|
|
582
|
+
switch (event.type) {
|
|
583
|
+
case "screen":
|
|
584
|
+
formoEvent = await this.generateScreenEvent(
|
|
585
|
+
event.name,
|
|
586
|
+
event.category,
|
|
587
|
+
event.properties,
|
|
588
|
+
event.context
|
|
589
|
+
);
|
|
590
|
+
break;
|
|
591
|
+
case "detect":
|
|
592
|
+
formoEvent = await this.generateDetectWalletEvent(
|
|
593
|
+
event.providerName,
|
|
594
|
+
event.rdns,
|
|
595
|
+
event.properties,
|
|
596
|
+
event.context
|
|
597
|
+
);
|
|
598
|
+
break;
|
|
599
|
+
case "identify":
|
|
600
|
+
formoEvent = await this.generateIdentifyEvent(
|
|
601
|
+
event.providerName,
|
|
602
|
+
event.rdns,
|
|
603
|
+
event.address,
|
|
604
|
+
event.userId,
|
|
605
|
+
event.properties,
|
|
606
|
+
event.context
|
|
607
|
+
);
|
|
608
|
+
break;
|
|
609
|
+
case "chain":
|
|
610
|
+
formoEvent = await this.generateChainChangedEvent(
|
|
611
|
+
event.chainId,
|
|
612
|
+
event.address,
|
|
613
|
+
event.properties,
|
|
614
|
+
event.context
|
|
615
|
+
);
|
|
616
|
+
break;
|
|
617
|
+
case "connect":
|
|
618
|
+
formoEvent = await this.generateConnectEvent(
|
|
619
|
+
event.chainId,
|
|
620
|
+
event.address,
|
|
621
|
+
event.properties,
|
|
622
|
+
event.context
|
|
623
|
+
);
|
|
624
|
+
break;
|
|
625
|
+
case "disconnect":
|
|
626
|
+
formoEvent = await this.generateDisconnectEvent(
|
|
627
|
+
event.chainId,
|
|
628
|
+
event.address,
|
|
629
|
+
event.properties,
|
|
630
|
+
event.context
|
|
631
|
+
);
|
|
632
|
+
break;
|
|
633
|
+
case "signature":
|
|
634
|
+
formoEvent = await this.generateSignatureEvent(
|
|
635
|
+
event.status,
|
|
636
|
+
event.chainId,
|
|
637
|
+
event.address,
|
|
638
|
+
event.message,
|
|
639
|
+
event.signatureHash,
|
|
640
|
+
event.properties,
|
|
641
|
+
event.context
|
|
642
|
+
);
|
|
643
|
+
break;
|
|
644
|
+
case "transaction":
|
|
645
|
+
formoEvent = await this.generateTransactionEvent(
|
|
646
|
+
event.status,
|
|
647
|
+
event.chainId,
|
|
648
|
+
event.address,
|
|
649
|
+
event.data,
|
|
650
|
+
event.to,
|
|
651
|
+
event.value,
|
|
652
|
+
event.transactionHash,
|
|
653
|
+
event.function_name,
|
|
654
|
+
event.function_args,
|
|
655
|
+
event.properties,
|
|
656
|
+
event.context
|
|
657
|
+
);
|
|
658
|
+
break;
|
|
659
|
+
case "track":
|
|
660
|
+
default:
|
|
661
|
+
formoEvent = await this.generateTrackEvent(
|
|
662
|
+
event.event,
|
|
663
|
+
event.properties,
|
|
664
|
+
event.context
|
|
665
|
+
);
|
|
666
|
+
break;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
// Set address if not already set by the specific event generator
|
|
670
|
+
if (formoEvent.address === undefined || formoEvent.address === null) {
|
|
671
|
+
const validAddress = getValidAddress(address);
|
|
672
|
+
formoEvent.address = validAddress
|
|
673
|
+
? toChecksumAddress(validAddress)
|
|
674
|
+
: null;
|
|
675
|
+
}
|
|
676
|
+
formoEvent.user_id = userId || null;
|
|
677
|
+
|
|
678
|
+
return formoEvent as IFormoEvent;
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
export { EventFactory };
|