@amplitude/analytics-react-native 0.0.1-dev.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.
Files changed (180) hide show
  1. package/README.md +0 -0
  2. package/amplitude-react-native.podspec +21 -0
  3. package/android/build.gradle +61 -0
  4. package/android/gradle.properties +3 -0
  5. package/android/src/main/AndroidManifest.xml +4 -0
  6. package/android/src/main/java/com/amplitude/reactnative/AmplitudeReactNativeModule.kt +36 -0
  7. package/android/src/main/java/com/amplitude/reactnative/AmplitudeReactNativePackage.java +28 -0
  8. package/android/src/main/java/com/amplitude/reactnative/AndroidContextProvider.kt +415 -0
  9. package/android/src/main/java/com/amplitude/reactnative/AndroidLogger.kt +56 -0
  10. package/android/src/main/java/com/amplitude/reactnative/Utils.kt +34 -0
  11. package/ios/AmplitudeReactNative-Bridging-Header.h +5 -0
  12. package/ios/AmplitudeReactNative.m +7 -0
  13. package/ios/AmplitudeReactNative.swift +29 -0
  14. package/ios/AmplitudeReactNative.xcodeproj/project.pbxproj +293 -0
  15. package/ios/AppleContextProvider.swift +219 -0
  16. package/lib/commonjs/attribution/campaign-parser.js +74 -0
  17. package/lib/commonjs/attribution/campaign-parser.js.map +1 -0
  18. package/lib/commonjs/attribution/campaign-tracker.js +133 -0
  19. package/lib/commonjs/attribution/campaign-tracker.js.map +1 -0
  20. package/lib/commonjs/attribution/constants.js +47 -0
  21. package/lib/commonjs/attribution/constants.js.map +1 -0
  22. package/lib/commonjs/config.js +257 -0
  23. package/lib/commonjs/config.js.map +1 -0
  24. package/lib/commonjs/constants.js +31 -0
  25. package/lib/commonjs/constants.js.map +1 -0
  26. package/lib/commonjs/cookie-migration/index.js +74 -0
  27. package/lib/commonjs/cookie-migration/index.js.map +1 -0
  28. package/lib/commonjs/index.js +141 -0
  29. package/lib/commonjs/index.js.map +1 -0
  30. package/lib/commonjs/plugins/context.js +136 -0
  31. package/lib/commonjs/plugins/context.js.map +1 -0
  32. package/lib/commonjs/react-native-client.js +362 -0
  33. package/lib/commonjs/react-native-client.js.map +1 -0
  34. package/lib/commonjs/session-manager.js +114 -0
  35. package/lib/commonjs/session-manager.js.map +1 -0
  36. package/lib/commonjs/storage/cookie.js +124 -0
  37. package/lib/commonjs/storage/cookie.js.map +1 -0
  38. package/lib/commonjs/storage/local-storage.js +79 -0
  39. package/lib/commonjs/storage/local-storage.js.map +1 -0
  40. package/lib/commonjs/storage/utm-cookie.js +42 -0
  41. package/lib/commonjs/storage/utm-cookie.js.map +1 -0
  42. package/lib/commonjs/transports/fetch.js +34 -0
  43. package/lib/commonjs/transports/fetch.js.map +1 -0
  44. package/lib/commonjs/transports/send-beacon.js +43 -0
  45. package/lib/commonjs/transports/send-beacon.js.map +1 -0
  46. package/lib/commonjs/transports/xhr.js +54 -0
  47. package/lib/commonjs/transports/xhr.js.map +1 -0
  48. package/lib/commonjs/typings/browser-snippet.d.js +6 -0
  49. package/lib/commonjs/typings/browser-snippet.d.js.map +1 -0
  50. package/lib/commonjs/typings/ua-parser.d.js +2 -0
  51. package/lib/commonjs/typings/ua-parser.d.js.map +1 -0
  52. package/lib/commonjs/utils/cookie-name.js +23 -0
  53. package/lib/commonjs/utils/cookie-name.js.map +1 -0
  54. package/lib/commonjs/utils/language.js +18 -0
  55. package/lib/commonjs/utils/language.js.map +1 -0
  56. package/lib/commonjs/utils/platform.js +21 -0
  57. package/lib/commonjs/utils/platform.js.map +1 -0
  58. package/lib/commonjs/utils/query-params.js +36 -0
  59. package/lib/commonjs/utils/query-params.js.map +1 -0
  60. package/lib/commonjs/utils/snippet-helper.js +56 -0
  61. package/lib/commonjs/utils/snippet-helper.js.map +1 -0
  62. package/lib/commonjs/version.js +9 -0
  63. package/lib/commonjs/version.js.map +1 -0
  64. package/lib/module/attribution/campaign-parser.js +62 -0
  65. package/lib/module/attribution/campaign-parser.js.map +1 -0
  66. package/lib/module/attribution/campaign-tracker.js +120 -0
  67. package/lib/module/attribution/campaign-tracker.js.map +1 -0
  68. package/lib/module/attribution/constants.js +26 -0
  69. package/lib/module/attribution/constants.js.map +1 -0
  70. package/lib/module/config.js +217 -0
  71. package/lib/module/config.js.map +1 -0
  72. package/lib/module/constants.js +13 -0
  73. package/lib/module/constants.js.map +1 -0
  74. package/lib/module/cookie-migration/index.js +56 -0
  75. package/lib/module/cookie-migration/index.js.map +1 -0
  76. package/lib/module/index.js +6 -0
  77. package/lib/module/index.js.map +1 -0
  78. package/lib/module/plugins/context.js +118 -0
  79. package/lib/module/plugins/context.js.map +1 -0
  80. package/lib/module/react-native-client.js +329 -0
  81. package/lib/module/react-native-client.js.map +1 -0
  82. package/lib/module/session-manager.js +104 -0
  83. package/lib/module/session-manager.js.map +1 -0
  84. package/lib/module/storage/cookie.js +114 -0
  85. package/lib/module/storage/cookie.js.map +1 -0
  86. package/lib/module/storage/local-storage.js +67 -0
  87. package/lib/module/storage/local-storage.js.map +1 -0
  88. package/lib/module/storage/utm-cookie.js +32 -0
  89. package/lib/module/storage/utm-cookie.js.map +1 -0
  90. package/lib/module/transports/fetch.js +24 -0
  91. package/lib/module/transports/fetch.js.map +1 -0
  92. package/lib/module/transports/send-beacon.js +33 -0
  93. package/lib/module/transports/send-beacon.js.map +1 -0
  94. package/lib/module/transports/xhr.js +44 -0
  95. package/lib/module/transports/xhr.js.map +1 -0
  96. package/lib/module/typings/browser-snippet.d.js +2 -0
  97. package/lib/module/typings/browser-snippet.d.js.map +1 -0
  98. package/lib/module/typings/ua-parser.d.js +2 -0
  99. package/lib/module/typings/ua-parser.d.js.map +1 -0
  100. package/lib/module/utils/cookie-name.js +10 -0
  101. package/lib/module/utils/cookie-name.js.map +1 -0
  102. package/lib/module/utils/language.js +9 -0
  103. package/lib/module/utils/language.js.map +1 -0
  104. package/lib/module/utils/platform.js +8 -0
  105. package/lib/module/utils/platform.js.map +1 -0
  106. package/lib/module/utils/query-params.js +26 -0
  107. package/lib/module/utils/query-params.js.map +1 -0
  108. package/lib/module/utils/snippet-helper.js +41 -0
  109. package/lib/module/utils/snippet-helper.js.map +1 -0
  110. package/lib/module/version.js +2 -0
  111. package/lib/module/version.js.map +1 -0
  112. package/lib/typescript/attribution/campaign-parser.d.ts +10 -0
  113. package/lib/typescript/attribution/campaign-parser.d.ts.map +1 -0
  114. package/lib/typescript/attribution/campaign-tracker.d.ts +72 -0
  115. package/lib/typescript/attribution/campaign-tracker.d.ts.map +1 -0
  116. package/lib/typescript/attribution/constants.d.ts +17 -0
  117. package/lib/typescript/attribution/constants.d.ts.map +1 -0
  118. package/lib/typescript/config.d.ts +90 -0
  119. package/lib/typescript/config.d.ts.map +1 -0
  120. package/lib/typescript/constants.d.ts +13 -0
  121. package/lib/typescript/constants.d.ts.map +1 -0
  122. package/lib/typescript/cookie-migration/index.d.ts +5 -0
  123. package/lib/typescript/cookie-migration/index.d.ts.map +1 -0
  124. package/lib/typescript/index.d.ts +6 -0
  125. package/lib/typescript/index.d.ts.map +1 -0
  126. package/lib/typescript/plugins/context.d.ts +31 -0
  127. package/lib/typescript/plugins/context.d.ts.map +1 -0
  128. package/lib/typescript/react-native-client.d.ts +209 -0
  129. package/lib/typescript/react-native-client.d.ts.map +1 -0
  130. package/lib/typescript/session-manager.d.ts +28 -0
  131. package/lib/typescript/session-manager.d.ts.map +1 -0
  132. package/lib/typescript/storage/cookie.d.ts +12 -0
  133. package/lib/typescript/storage/cookie.d.ts.map +1 -0
  134. package/lib/typescript/storage/local-storage.d.ts +10 -0
  135. package/lib/typescript/storage/local-storage.d.ts.map +1 -0
  136. package/lib/typescript/storage/utm-cookie.d.ts +6 -0
  137. package/lib/typescript/storage/utm-cookie.d.ts.map +1 -0
  138. package/lib/typescript/transports/fetch.d.ts +6 -0
  139. package/lib/typescript/transports/fetch.d.ts.map +1 -0
  140. package/lib/typescript/transports/send-beacon.d.ts +6 -0
  141. package/lib/typescript/transports/send-beacon.d.ts.map +1 -0
  142. package/lib/typescript/transports/xhr.d.ts +7 -0
  143. package/lib/typescript/transports/xhr.d.ts.map +1 -0
  144. package/lib/typescript/utils/cookie-name.d.ts +3 -0
  145. package/lib/typescript/utils/cookie-name.d.ts.map +1 -0
  146. package/lib/typescript/utils/language.d.ts +2 -0
  147. package/lib/typescript/utils/language.d.ts.map +1 -0
  148. package/lib/typescript/utils/platform.d.ts +3 -0
  149. package/lib/typescript/utils/platform.d.ts.map +1 -0
  150. package/lib/typescript/utils/query-params.d.ts +2 -0
  151. package/lib/typescript/utils/query-params.d.ts.map +1 -0
  152. package/lib/typescript/utils/snippet-helper.d.ts +16 -0
  153. package/lib/typescript/utils/snippet-helper.d.ts.map +1 -0
  154. package/lib/typescript/version.d.ts +2 -0
  155. package/lib/typescript/version.d.ts.map +1 -0
  156. package/package.json +93 -0
  157. package/src/attribution/campaign-parser.ts +78 -0
  158. package/src/attribution/campaign-tracker.ts +112 -0
  159. package/src/attribution/constants.ts +32 -0
  160. package/src/config.ts +210 -0
  161. package/src/constants.ts +14 -0
  162. package/src/cookie-migration/index.ts +54 -0
  163. package/src/index.ts +23 -0
  164. package/src/plugins/context.ts +106 -0
  165. package/src/react-native-client.ts +349 -0
  166. package/src/session-manager.ts +81 -0
  167. package/src/storage/cookie.ts +95 -0
  168. package/src/storage/local-storage.ts +67 -0
  169. package/src/storage/utm-cookie.ts +27 -0
  170. package/src/transports/fetch.ts +23 -0
  171. package/src/transports/send-beacon.ts +34 -0
  172. package/src/transports/xhr.ts +36 -0
  173. package/src/typings/browser-snippet.d.ts +7 -0
  174. package/src/typings/ua-parser.d.ts +4 -0
  175. package/src/utils/cookie-name.ts +9 -0
  176. package/src/utils/language.ts +7 -0
  177. package/src/utils/platform.ts +9 -0
  178. package/src/utils/query-params.ts +21 -0
  179. package/src/utils/snippet-helper.ts +35 -0
  180. package/src/version.ts +1 -0
@@ -0,0 +1,106 @@
1
+ import { BeforePlugin, ReactNativeConfig, Event, PluginType } from '@amplitude/analytics-types';
2
+ import UAParser from '@amplitude/ua-parser-js';
3
+ import { UUID } from '@amplitude/analytics-core';
4
+ import { getLanguage } from '../utils/language';
5
+ import { VERSION } from '../version';
6
+ import { NativeModules } from 'react-native';
7
+
8
+ const BROWSER_PLATFORM = 'Web';
9
+ const IP_ADDRESS = '$remote';
10
+
11
+ type NativeContext = {
12
+ version: string;
13
+ platform: string;
14
+ language: string;
15
+ os_name: string;
16
+ os_version: string;
17
+ device_brand: string;
18
+ device_manufacturer: string;
19
+ device_model: string;
20
+ carrier: string;
21
+ };
22
+
23
+ export interface AmplitudeReactNative {
24
+ getApplicationContext(): Promise<NativeContext>;
25
+ }
26
+
27
+ export class Context implements BeforePlugin {
28
+ name = 'context';
29
+ type = PluginType.BEFORE as const;
30
+
31
+ // this.config is defined in setup() which will always be called first
32
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
33
+ // @ts-ignore
34
+ config: ReactNativeConfig;
35
+ eventId = 0;
36
+ uaResult: UAParser.IResult;
37
+ nativeModule: AmplitudeReactNative | undefined = NativeModules.AmplitudeReactNative as
38
+ | AmplitudeReactNative
39
+ | undefined;
40
+ library = `amplitude-ts/${VERSION}`;
41
+
42
+ constructor() {
43
+ let agent: string | undefined;
44
+ /* istanbul ignore else */
45
+ if (typeof navigator !== 'undefined') {
46
+ agent = navigator.userAgent;
47
+ }
48
+ this.uaResult = new UAParser(agent).getResult();
49
+ }
50
+
51
+ setup(config: ReactNativeConfig): Promise<undefined> {
52
+ this.config = config;
53
+ return Promise.resolve(undefined);
54
+ }
55
+
56
+ async execute(context: Event): Promise<Event> {
57
+ /**
58
+ * Manages user session triggered by new events
59
+ */
60
+ if (!this.isSessionValid()) {
61
+ // Creates new session
62
+ this.config.sessionId = Date.now();
63
+ } // else use previously creates session
64
+ // Updates last event time to extend time-based session
65
+ this.config.lastEventTime = Date.now();
66
+ const time = new Date().getTime();
67
+ const nativeContext = await this.nativeModule?.getApplicationContext();
68
+ const appVersion = nativeContext?.version || this.config.appVersion;
69
+ const platform = nativeContext?.platform || BROWSER_PLATFORM;
70
+ const osName = nativeContext?.os_name || this.uaResult.browser.name;
71
+ const osVersion = nativeContext?.os_version || this.uaResult.browser.version;
72
+ const deviceVendor = nativeContext?.device_manufacturer || this.uaResult.device.vendor;
73
+ const deviceModel = nativeContext?.device_model || this.uaResult.device.model || this.uaResult.os.name;
74
+ const language = nativeContext?.language || getLanguage();
75
+ const carrier = nativeContext?.carrier;
76
+
77
+ const event: Event = {
78
+ user_id: this.config.userId,
79
+ device_id: this.config.deviceId,
80
+ session_id: this.config.sessionId,
81
+ time,
82
+ ...(this.config.appVersion && { app_version: appVersion }),
83
+ ...(this.config.trackingOptions.platform && { platform: platform }),
84
+ ...(this.config.trackingOptions.osName && { os_name: osName }),
85
+ ...(this.config.trackingOptions.osVersion && { os_version: osVersion }),
86
+ ...(this.config.trackingOptions.deviceManufacturer && { device_manufacturer: deviceVendor }),
87
+ ...(this.config.trackingOptions.deviceModel && { device_model: deviceModel }),
88
+ ...(this.config.trackingOptions.language && { language: language }),
89
+ ...(this.config.trackingOptions.carrier && { carrier: carrier }),
90
+ ...(this.config.trackingOptions.ipAddress && { ip: IP_ADDRESS }),
91
+ insert_id: UUID(),
92
+ partner_id: this.config.partnerId,
93
+ plan: this.config.plan,
94
+ ...context,
95
+ event_id: this.eventId++,
96
+ library: this.library,
97
+ };
98
+ return event;
99
+ }
100
+
101
+ isSessionValid() {
102
+ const lastEventTime = this.config.lastEventTime || Date.now();
103
+ const timeSinceLastEvent = Date.now() - lastEventTime;
104
+ return timeSinceLastEvent < this.config.sessionTimeout;
105
+ }
106
+ }
@@ -0,0 +1,349 @@
1
+ import { AmplitudeCore, Destination, Identify, Revenue, returnWrapper } from '@amplitude/analytics-core';
2
+ import {
3
+ ReactNativeConfig,
4
+ Campaign,
5
+ EventOptions,
6
+ Identify as IIdentify,
7
+ Result,
8
+ Revenue as IRevenue,
9
+ TransportType,
10
+ ReactNativeOptions,
11
+ AdditionalReactNativeOptions,
12
+ AttributionReactNativeOptions,
13
+ } from '@amplitude/analytics-types';
14
+ import { convertProxyObjectToRealObject, isInstanceProxy } from './utils/snippet-helper';
15
+ import { Context } from './plugins/context';
16
+ import { useReactNativeConfig, createTransport, createDeviceId, createFlexibleStorage } from './config';
17
+ import { parseOldCookies } from './cookie-migration';
18
+ import { CampaignTracker } from './attribution/campaign-tracker';
19
+
20
+ export class AmplitudeReactNative extends AmplitudeCore<ReactNativeConfig> {
21
+ async init(apiKey: string, userId?: string, options?: ReactNativeOptions & AdditionalReactNativeOptions) {
22
+ // Step 1: Read cookies stored by old SDK
23
+ const oldCookies = await parseOldCookies(apiKey, options);
24
+
25
+ // Step 2: Create react native config
26
+ const reactNativeOptions = await useReactNativeConfig(apiKey, userId || oldCookies.userId, {
27
+ ...options,
28
+ deviceId: oldCookies.deviceId ?? options?.deviceId,
29
+ sessionId: oldCookies.sessionId ?? options?.sessionId,
30
+ optOut: options?.optOut ?? oldCookies.optOut,
31
+ lastEventTime: oldCookies.lastEventTime,
32
+ });
33
+ await super.init(undefined, undefined, reactNativeOptions);
34
+
35
+ // Step 3: Manage session
36
+ let isNewSession = false;
37
+ if (
38
+ !this.config.sessionId ||
39
+ (this.config.lastEventTime && Date.now() - this.config.lastEventTime > this.config.sessionTimeout)
40
+ ) {
41
+ // Either
42
+ // 1) No previous session; or
43
+ // 2) Previous session expired
44
+ this.config.sessionId = Date.now();
45
+ isNewSession = true;
46
+ }
47
+
48
+ // Step 4: Install plugins
49
+ // Do not track any events before this
50
+ await this.add(new Context());
51
+ await this.add(new Destination());
52
+
53
+ // Step 5: Track attributions
54
+ await this.runAttributionStrategy(options?.attribution, isNewSession);
55
+ }
56
+
57
+ async runAttributionStrategy(attributionConfig?: AttributionReactNativeOptions, isNewSession = false) {
58
+ const track = this.track.bind(this);
59
+ const onNewCampaign = this.setSessionId.bind(this, Date.now());
60
+
61
+ const storage = await createFlexibleStorage<Campaign>(this.config);
62
+ const campaignTracker = new CampaignTracker(this.config.apiKey, {
63
+ ...attributionConfig,
64
+ storage,
65
+ track,
66
+ onNewCampaign,
67
+ });
68
+
69
+ await campaignTracker.send(isNewSession);
70
+ }
71
+
72
+ getUserId() {
73
+ return this.config.userId;
74
+ }
75
+
76
+ setUserId(userId: string | undefined) {
77
+ this.config.userId = userId;
78
+ }
79
+
80
+ getDeviceId() {
81
+ return this.config.deviceId;
82
+ }
83
+
84
+ setDeviceId(deviceId: string) {
85
+ this.config.deviceId = deviceId;
86
+ }
87
+
88
+ regenerateDeviceId() {
89
+ const deviceId = createDeviceId();
90
+ this.setDeviceId(deviceId);
91
+ }
92
+
93
+ getSessionId() {
94
+ return this.config.sessionId;
95
+ }
96
+
97
+ setSessionId(sessionId: number) {
98
+ this.config.sessionId = sessionId;
99
+ }
100
+
101
+ setOptOut(optOut: boolean) {
102
+ this.config.optOut = optOut;
103
+ }
104
+
105
+ setTransport(transport: TransportType) {
106
+ this.config.transportProvider = createTransport(transport);
107
+ }
108
+
109
+ identify(identify: IIdentify, eventOptions?: EventOptions): Promise<Result> {
110
+ if (isInstanceProxy(identify)) {
111
+ const queue = identify._q;
112
+ identify._q = [];
113
+ identify = convertProxyObjectToRealObject(new Identify(), queue);
114
+ }
115
+ return super.identify(identify, eventOptions);
116
+ }
117
+
118
+ groupIdentify(
119
+ groupType: string,
120
+ groupName: string | string[],
121
+ identify: IIdentify,
122
+ eventOptions?: EventOptions,
123
+ ): Promise<Result> {
124
+ if (isInstanceProxy(identify)) {
125
+ const queue = identify._q;
126
+ identify._q = [];
127
+ identify = convertProxyObjectToRealObject(new Identify(), queue);
128
+ }
129
+ return super.groupIdentify(groupType, groupName, identify, eventOptions);
130
+ }
131
+
132
+ revenue(revenue: IRevenue, eventOptions?: EventOptions) {
133
+ if (isInstanceProxy(revenue)) {
134
+ const queue = revenue._q;
135
+ revenue._q = [];
136
+ revenue = convertProxyObjectToRealObject(new Revenue(), queue);
137
+ }
138
+ return super.revenue(revenue, eventOptions);
139
+ }
140
+ }
141
+
142
+ const client = new AmplitudeReactNative();
143
+
144
+ /**
145
+ * Initializes the Amplitude SDK with your apiKey, userId and optional configurations.
146
+ * This method must be called before any other operations.
147
+ *
148
+ * ```typescript
149
+ * await init(API_KEY, USER_ID, options).promise;
150
+ * ```
151
+ */
152
+ export const init = returnWrapper(client.init.bind(client));
153
+
154
+ /**
155
+ * Adds a new plugin.
156
+ *
157
+ * ```typescript
158
+ * const plugin = {...};
159
+ * amplitude.add(plugin);
160
+ * ```
161
+ */
162
+ export const add = returnWrapper(client.add.bind(client));
163
+
164
+ /**
165
+ * Removes a plugin.
166
+ *
167
+ * ```typescript
168
+ * amplitude.remove('myPlugin');
169
+ * ```
170
+ */
171
+ export const remove = returnWrapper(client.remove.bind(client));
172
+
173
+ /**
174
+ * Tracks user-defined event, with specified type, optional event properties and optional overwrites.
175
+ *
176
+ * ```typescript
177
+ * // event tracking with event type only
178
+ * track('Page Load');
179
+ *
180
+ * // event tracking with event type and additional event properties
181
+ * track('Page Load', { loadTime: 1000 });
182
+ *
183
+ * // event tracking with event type, additional event properties, and overwritten event options
184
+ * track('Page Load', { loadTime: 1000 }, { sessionId: -1 });
185
+ *
186
+ * // alternatively, this tracking method is awaitable
187
+ * const result = await track('Page Load').promise;
188
+ * console.log(result.event); // {...}
189
+ * console.log(result.code); // 200
190
+ * console.log(result.message); // "Event tracked successfully"
191
+ * ```
192
+ */
193
+ export const track = returnWrapper(client.track.bind(client));
194
+
195
+ /**
196
+ * Alias for track()
197
+ */
198
+ export const logEvent = returnWrapper(client.logEvent.bind(client));
199
+
200
+ /**
201
+ * Sends an identify event containing user property operations
202
+ *
203
+ * ```typescript
204
+ * const id = new Identify();
205
+ * id.set('colors', ['rose', 'gold']);
206
+ * identify(id);
207
+ *
208
+ * // alternatively, this tracking method is awaitable
209
+ * const result = await identify(id).promise;
210
+ * console.log(result.event); // {...}
211
+ * console.log(result.code); // 200
212
+ * console.log(result.message); // "Event tracked successfully"
213
+ * ```
214
+ */
215
+ export const identify = returnWrapper(client.identify.bind(client));
216
+
217
+ /**
218
+ * Sends a group identify event containing group property operations.
219
+ *
220
+ * ```typescript
221
+ * const id = new Identify();
222
+ * id.set('skills', ['js', 'ts']);
223
+ * const groupType = 'org';
224
+ * const groupName = 'engineering';
225
+ * groupIdentify(groupType, groupName, id);
226
+ *
227
+ * // alternatively, this tracking method is awaitable
228
+ * const result = await groupIdentify(groupType, groupName, id).promise;
229
+ * console.log(result.event); // {...}
230
+ * console.log(result.code); // 200
231
+ * console.log(result.message); // "Event tracked successfully"
232
+ * ```
233
+ */
234
+ export const groupIdentify = returnWrapper(client.groupIdentify.bind(client));
235
+ export const setGroup = returnWrapper(client.setGroup.bind(client));
236
+
237
+ /**
238
+ * Sends a revenue event containing revenue property operations.
239
+ *
240
+ * ```typescript
241
+ * const rev = new Revenue();
242
+ * rev.setRevenue(100);
243
+ * revenue(rev);
244
+ *
245
+ * // alternatively, this tracking method is awaitable
246
+ * const result = await revenue(rev).promise;
247
+ * console.log(result.event); // {...}
248
+ * console.log(result.code); // 200
249
+ * console.log(result.message); // "Event tracked successfully"
250
+ * ```
251
+ */
252
+ export const revenue = returnWrapper(client.revenue.bind(client));
253
+
254
+ /**
255
+ * Returns current user ID.
256
+ *
257
+ * ```typescript
258
+ * const userId = getUserId();
259
+ * ```
260
+ */
261
+ export const getUserId = client.getUserId.bind(client);
262
+
263
+ /**
264
+ * Sets a new user ID.
265
+ *
266
+ * ```typescript
267
+ * setUserId('userId');
268
+ * ```
269
+ */
270
+ export const setUserId = client.setUserId.bind(client);
271
+
272
+ /**
273
+ * Returns current device ID.
274
+ *
275
+ * ```typescript
276
+ * const deviceId = getDeviceId();
277
+ * ```
278
+ */
279
+ export const getDeviceId = client.getDeviceId.bind(client);
280
+
281
+ /**
282
+ * Sets a new device ID.
283
+ * When setting a custom device ID, make sure the value is sufficiently unique.
284
+ * A uuid is recommended.
285
+ *
286
+ * ```typescript
287
+ * setDeviceId('deviceId');
288
+ * ```
289
+ */
290
+ export const setDeviceId = client.setDeviceId.bind(client);
291
+
292
+ /**
293
+ * Regenerates a new random deviceId for current user. Note: this is not recommended unless you know what you
294
+ * are doing. This can be used in conjunction with `setUserId(undefined)` to anonymize users after they log out.
295
+ * With an `unefined` userId and a completely new deviceId, the current user would appear as a brand new user in dashboard.
296
+ *
297
+ * ```typescript
298
+ * regenerateDeviceId();
299
+ * ```
300
+ */
301
+ export const regenerateDeviceId = client.regenerateDeviceId.bind(client);
302
+
303
+ /**
304
+ * Returns current session ID.
305
+ *
306
+ * ```typescript
307
+ * const sessionId = getSessionId();
308
+ * ```
309
+ */
310
+ export const getSessionId = client.getSessionId.bind(client);
311
+
312
+ /**
313
+ * Sets a new session ID.
314
+ * When settign a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).
315
+ *
316
+ * ```typescript
317
+ * setSessionId(Date.now());
318
+ * ```
319
+ */
320
+ export const setSessionId = client.setSessionId.bind(client);
321
+
322
+ /**
323
+ * Sets a new optOut config value. This toggles event tracking on/off.
324
+ *
325
+ *```typescript
326
+ * // Stops tracking
327
+ * setOptOut(true);
328
+ *
329
+ * // Starts/resumes tracking
330
+ * setOptOut(false);
331
+ * ```
332
+ */
333
+ export const setOptOut = client.setOptOut.bind(client);
334
+
335
+ /**
336
+ * Sets the network transport type for events.
337
+ *
338
+ * ```typescript
339
+ * // Use Fetch API
340
+ * setTransport('fetch');
341
+ *
342
+ * // Use XMLHttpRequest API
343
+ * setTransport('xhr');
344
+ *
345
+ * // Use navigator.sendBeacon API
346
+ * setTransport('beacon');
347
+ * ```
348
+ */
349
+ export const setTransport = client.setTransport.bind(client);
@@ -0,0 +1,81 @@
1
+ import { UserSession, Storage, SessionManager as ISessionManager } from '@amplitude/analytics-types';
2
+ import { getCookieName as getStorageKey } from './utils/cookie-name';
3
+
4
+ export class SessionManager implements ISessionManager {
5
+ storageKey: string;
6
+ cache: UserSession;
7
+ isSessionCacheValid = true;
8
+
9
+ constructor(private storage: Storage<UserSession>, apiKey: string) {
10
+ this.storageKey = getStorageKey(apiKey);
11
+ this.cache = { optOut: false };
12
+ }
13
+
14
+ /**
15
+ * load() must be called immediately after instantation
16
+ *
17
+ * ```ts
18
+ * await new SessionManager(...).load();
19
+ * ```
20
+ */
21
+ async load() {
22
+ this.cache = (await this.storage.get(this.storageKey)) ?? {
23
+ optOut: false,
24
+ };
25
+ return this;
26
+ }
27
+
28
+ setSession(session: Partial<UserSession>) {
29
+ this.cache = { ...this.cache, ...session };
30
+ void this.storage.set(this.storageKey, this.cache);
31
+ }
32
+
33
+ getSessionId() {
34
+ this.isSessionCacheValid = true;
35
+ void this.storage.get(this.storageKey).then((userSession) => {
36
+ // Checks if session id has been set since the last get
37
+ if (this.isSessionCacheValid) {
38
+ this.cache.sessionId = userSession?.sessionId;
39
+ }
40
+ });
41
+ return this.cache.sessionId;
42
+ }
43
+
44
+ setSessionId(sessionId: number) {
45
+ // Flags session id has been set
46
+ this.isSessionCacheValid = false;
47
+ this.setSession({ sessionId });
48
+ }
49
+
50
+ getDeviceId(): string | undefined {
51
+ return this.cache.deviceId;
52
+ }
53
+
54
+ setDeviceId(deviceId: string): void {
55
+ this.setSession({ deviceId });
56
+ }
57
+
58
+ getUserId(): string | undefined {
59
+ return this.cache.userId;
60
+ }
61
+
62
+ setUserId(userId: string): void {
63
+ this.setSession({ userId });
64
+ }
65
+
66
+ getLastEventTime() {
67
+ return this.cache.lastEventTime;
68
+ }
69
+
70
+ setLastEventTime(lastEventTime: number) {
71
+ this.setSession({ lastEventTime });
72
+ }
73
+
74
+ getOptOut(): boolean {
75
+ return this.cache.optOut;
76
+ }
77
+
78
+ setOptOut(optOut: boolean): void {
79
+ this.setSession({ optOut });
80
+ }
81
+ }
@@ -0,0 +1,95 @@
1
+ import { Storage, CookieStorageOptions } from '@amplitude/analytics-types';
2
+ import { isNative } from '../utils/platform';
3
+
4
+ export class CookieStorage<T> implements Storage<T> {
5
+ options: CookieStorageOptions;
6
+
7
+ constructor(options?: CookieStorageOptions) {
8
+ this.options = { ...options };
9
+ }
10
+
11
+ async isEnabled(): Promise<boolean> {
12
+ if (isNative()) {
13
+ return false;
14
+ }
15
+ /* istanbul ignore if */
16
+ if (typeof window === 'undefined') {
17
+ return false;
18
+ }
19
+
20
+ const random = String(Date.now());
21
+ const testStrorage = new CookieStorage<string>();
22
+ const testKey = 'AMP_TEST';
23
+ try {
24
+ await testStrorage.set(testKey, random);
25
+ const value = await testStrorage.get(testKey);
26
+ return value === random;
27
+ } catch {
28
+ /* istanbul ignore next */
29
+ return false;
30
+ } finally {
31
+ await testStrorage.remove(testKey);
32
+ }
33
+ }
34
+
35
+ async get(key: string): Promise<T | undefined> {
36
+ const value = await this.getRaw(key);
37
+ if (!value) {
38
+ return undefined;
39
+ }
40
+ try {
41
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
42
+ return JSON.parse(value);
43
+ } catch {
44
+ /* istanbul ignore next */
45
+ return undefined;
46
+ }
47
+ }
48
+
49
+ async getRaw(key: string): Promise<string | undefined> {
50
+ const cookie = window.document.cookie.split('; ');
51
+ const match = cookie.find((c) => c.indexOf(key + '=') === 0);
52
+ if (!match) {
53
+ return undefined;
54
+ }
55
+ return match.substring(key.length + 1);
56
+ }
57
+
58
+ async set(key: string, value: T | null): Promise<void> {
59
+ try {
60
+ const expirationDays = this.options.expirationDays ?? 0;
61
+ const expires = value !== null ? expirationDays : -1;
62
+ let expireDate: Date | undefined = undefined;
63
+ if (expires) {
64
+ const date = new Date();
65
+ date.setTime(date.getTime() + expires * 24 * 60 * 60 * 1000);
66
+ expireDate = date;
67
+ }
68
+ let str = `${key}=${JSON.stringify(value)}`;
69
+ if (expireDate) {
70
+ str += `; expires=${expireDate.toUTCString()}`;
71
+ }
72
+ str += '; path=/';
73
+ if (this.options.domain) {
74
+ str += `; domain=${this.options.domain}`;
75
+ }
76
+ if (this.options.secure) {
77
+ str += '; Secure';
78
+ }
79
+ if (this.options.sameSite) {
80
+ str += `; SameSite=${this.options.sameSite}`;
81
+ }
82
+ window.document.cookie = str;
83
+ } catch {
84
+ //
85
+ }
86
+ }
87
+
88
+ async remove(key: string): Promise<void> {
89
+ await this.set(key, null);
90
+ }
91
+
92
+ async reset(): Promise<void> {
93
+ return;
94
+ }
95
+ }
@@ -0,0 +1,67 @@
1
+ import { Storage } from '@amplitude/analytics-types';
2
+ import AsyncStorage from '@react-native-async-storage/async-storage';
3
+
4
+ export class LocalStorage<T> implements Storage<T> {
5
+ async isEnabled(): Promise<boolean> {
6
+ /* istanbul ignore if */
7
+ if (typeof window === 'undefined') {
8
+ return false;
9
+ }
10
+
11
+ const random = String(Date.now());
12
+ const testStorage = new LocalStorage<string>();
13
+ const testKey = 'AMP_TEST';
14
+ try {
15
+ await testStorage.set(testKey, random);
16
+ const value = await testStorage.get(testKey);
17
+ return value === random;
18
+ } catch {
19
+ /* istanbul ignore next */
20
+ return false;
21
+ } finally {
22
+ await testStorage.remove(testKey);
23
+ }
24
+ }
25
+
26
+ async get(key: string): Promise<T | undefined> {
27
+ try {
28
+ const value = await this.getRaw(key);
29
+ if (!value) {
30
+ return undefined;
31
+ }
32
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
33
+ return JSON.parse(value);
34
+ } catch {
35
+ /* istanbul ignore next */
36
+ return undefined;
37
+ }
38
+ }
39
+
40
+ async getRaw(key: string): Promise<string | undefined> {
41
+ return (await AsyncStorage.getItem(key)) || undefined;
42
+ }
43
+
44
+ async set(key: string, value: T): Promise<void> {
45
+ try {
46
+ await AsyncStorage.setItem(key, JSON.stringify(value));
47
+ } catch {
48
+ //
49
+ }
50
+ }
51
+
52
+ async remove(key: string): Promise<void> {
53
+ try {
54
+ await AsyncStorage.removeItem(key);
55
+ } catch {
56
+ //
57
+ }
58
+ }
59
+
60
+ async reset(): Promise<void> {
61
+ try {
62
+ await AsyncStorage.clear();
63
+ } catch {
64
+ //
65
+ }
66
+ }
67
+ }