@amplitude/analytics-react-native 0.0.1-dev.7 → 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/LICENSE +21 -0
- package/lib/commonjs/index.js +2 -2
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/plugins/identity.js +1 -1
- package/lib/commonjs/plugins/identity.js.map +1 -1
- package/lib/commonjs/version.js +1 -1
- package/lib/commonjs/version.js.map +1 -1
- package/lib/module/index.js +5 -3
- package/lib/module/index.js.map +1 -1
- package/lib/module/plugins/identity.js +1 -1
- package/lib/module/plugins/identity.js.map +1 -1
- package/lib/module/version.js +1 -1
- package/lib/module/version.js.map +1 -1
- package/lib/typescript/index.d.ts +2 -1
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/version.d.ts +1 -1
- package/lib/typescript/version.d.ts.map +1 -1
- package/package.json +8 -7
- package/src/attribution/campaign-parser.ts +78 -0
- package/src/attribution/campaign-tracker.ts +112 -0
- package/src/attribution/constants.ts +32 -0
- package/src/config.ts +227 -0
- package/src/cookie-migration/index.ts +54 -0
- package/src/index.ts +24 -0
- package/src/plugins/context.ts +106 -0
- package/src/plugins/identity.ts +21 -0
- package/src/react-native-client.ts +332 -0
- package/src/session-manager.ts +81 -0
- package/src/storage/cookie.ts +97 -0
- package/src/storage/local-storage.ts +67 -0
- package/src/storage/utm-cookie.ts +27 -0
- package/src/transports/fetch.ts +23 -0
- package/src/typings/browser-snippet.d.ts +7 -0
- package/src/typings/ua-parser.d.ts +4 -0
- package/src/utils/analytics-connector.ts +5 -0
- package/src/utils/cookie-name.ts +9 -0
- package/src/utils/language.ts +7 -0
- package/src/utils/platform.ts +9 -0
- package/src/utils/query-params.ts +18 -0
- package/src/version.ts +1 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { BrowserOptions, Storage, UserSession } from '@amplitude/analytics-types';
|
|
2
|
+
import { getOldCookieName } from '../utils/cookie-name';
|
|
3
|
+
import { LocalStorage } from '../storage/local-storage';
|
|
4
|
+
import { CookieStorage } from '../storage/cookie';
|
|
5
|
+
|
|
6
|
+
export const parseOldCookies = async (apiKey: string, options?: BrowserOptions): Promise<UserSession> => {
|
|
7
|
+
let storage: Storage<string> = new CookieStorage<string>();
|
|
8
|
+
if (!(await storage.isEnabled()) || options?.disableCookies) {
|
|
9
|
+
storage = new LocalStorage<string>();
|
|
10
|
+
}
|
|
11
|
+
if (!(await storage.isEnabled())) {
|
|
12
|
+
return {
|
|
13
|
+
optOut: false,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const oldCookieName = getOldCookieName(apiKey);
|
|
18
|
+
const cookies = await storage.getRaw(oldCookieName);
|
|
19
|
+
|
|
20
|
+
if (!cookies) {
|
|
21
|
+
return {
|
|
22
|
+
optOut: false,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
await storage.remove(oldCookieName);
|
|
27
|
+
const [deviceId, userId, optOut, sessionId, lastEventTime] = cookies.split('.');
|
|
28
|
+
return {
|
|
29
|
+
deviceId,
|
|
30
|
+
userId: decode(userId),
|
|
31
|
+
sessionId: parseTime(sessionId),
|
|
32
|
+
lastEventTime: parseTime(lastEventTime),
|
|
33
|
+
optOut: Boolean(optOut),
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const parseTime = (num: string) => {
|
|
38
|
+
const integer = parseInt(num, 32);
|
|
39
|
+
if (isNaN(integer)) {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
return integer;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const decode = (value?: string): string | undefined => {
|
|
46
|
+
if (!atob || !escape || !value) {
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
return decodeURIComponent(escape(atob(value)));
|
|
51
|
+
} catch {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export {
|
|
2
|
+
add,
|
|
3
|
+
flush,
|
|
4
|
+
getDeviceId,
|
|
5
|
+
getSessionId,
|
|
6
|
+
getUserId,
|
|
7
|
+
groupIdentify,
|
|
8
|
+
identify,
|
|
9
|
+
init,
|
|
10
|
+
logEvent,
|
|
11
|
+
remove,
|
|
12
|
+
revenue,
|
|
13
|
+
setDeviceId,
|
|
14
|
+
setGroup,
|
|
15
|
+
setOptOut,
|
|
16
|
+
setSessionId,
|
|
17
|
+
setUserId,
|
|
18
|
+
track,
|
|
19
|
+
} from './react-native-client';
|
|
20
|
+
export { Revenue, Identify } from '@amplitude/analytics-core';
|
|
21
|
+
// Hack - react-native apps have trouble with:
|
|
22
|
+
// export * as Types from '@amplitude/analytics-types
|
|
23
|
+
import * as Types from '@amplitude/analytics-types';
|
|
24
|
+
export { Types };
|
|
@@ -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
|
+
osName: string;
|
|
16
|
+
osVersion: string;
|
|
17
|
+
deviceBrand: string;
|
|
18
|
+
deviceManufacturer: string;
|
|
19
|
+
deviceModel: 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-react-native-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?.osName || this.uaResult.browser.name;
|
|
71
|
+
const osVersion = nativeContext?.osVersion || this.uaResult.browser.version;
|
|
72
|
+
const deviceVendor = nativeContext?.deviceManufacturer || this.uaResult.device.vendor;
|
|
73
|
+
const deviceModel = nativeContext?.deviceModel || 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,21 @@
|
|
|
1
|
+
import { BeforePlugin, PluginType, Event, Config } from '@amplitude/analytics-types';
|
|
2
|
+
import { getAnalyticsConnector } from '../utils/analytics-connector';
|
|
3
|
+
|
|
4
|
+
export class IdentityEventSender implements BeforePlugin {
|
|
5
|
+
name = 'identity';
|
|
6
|
+
type = PluginType.BEFORE as const;
|
|
7
|
+
|
|
8
|
+
identityStore = getAnalyticsConnector().identityStore;
|
|
9
|
+
|
|
10
|
+
async execute(context: Event): Promise<Event> {
|
|
11
|
+
const userProperties = context.user_properties as Record<string, any>;
|
|
12
|
+
if (userProperties) {
|
|
13
|
+
this.identityStore.editIdentity().updateUserProperties(userProperties).commit();
|
|
14
|
+
}
|
|
15
|
+
return context;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
setup(_: Config): Promise<undefined> {
|
|
19
|
+
return Promise.resolve(undefined);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import { AmplitudeCore, Destination, returnWrapper } from '@amplitude/analytics-core';
|
|
2
|
+
import {
|
|
3
|
+
ReactNativeConfig,
|
|
4
|
+
Campaign,
|
|
5
|
+
ReactNativeOptions,
|
|
6
|
+
AdditionalReactNativeOptions,
|
|
7
|
+
AttributionReactNativeOptions,
|
|
8
|
+
} from '@amplitude/analytics-types';
|
|
9
|
+
import { Context } from './plugins/context';
|
|
10
|
+
import { useReactNativeConfig, createDeviceId, createFlexibleStorage } from './config';
|
|
11
|
+
import { parseOldCookies } from './cookie-migration';
|
|
12
|
+
import { CampaignTracker } from './attribution/campaign-tracker';
|
|
13
|
+
import { isNative } from './utils/platform';
|
|
14
|
+
import { IdentityEventSender } from './plugins/identity';
|
|
15
|
+
import { getAnalyticsConnector } from './utils/analytics-connector';
|
|
16
|
+
|
|
17
|
+
export class AmplitudeReactNative extends AmplitudeCore<ReactNativeConfig> {
|
|
18
|
+
async init(apiKey: string, userId?: string, options?: ReactNativeOptions & AdditionalReactNativeOptions) {
|
|
19
|
+
// Step 1: Read cookies stored by old SDK
|
|
20
|
+
const oldCookies = await parseOldCookies(apiKey, options);
|
|
21
|
+
|
|
22
|
+
// Step 2: Create react native config
|
|
23
|
+
const reactNativeOptions = await useReactNativeConfig(apiKey, userId || oldCookies.userId, {
|
|
24
|
+
...options,
|
|
25
|
+
deviceId: oldCookies.deviceId ?? options?.deviceId,
|
|
26
|
+
sessionId: oldCookies.sessionId ?? options?.sessionId,
|
|
27
|
+
optOut: options?.optOut ?? oldCookies.optOut,
|
|
28
|
+
lastEventTime: oldCookies.lastEventTime,
|
|
29
|
+
});
|
|
30
|
+
await super.init(undefined, undefined, reactNativeOptions);
|
|
31
|
+
|
|
32
|
+
// Step 3: Manage session
|
|
33
|
+
let isNewSession = false;
|
|
34
|
+
if (
|
|
35
|
+
!this.config.sessionId ||
|
|
36
|
+
(this.config.lastEventTime && Date.now() - this.config.lastEventTime > this.config.sessionTimeout)
|
|
37
|
+
) {
|
|
38
|
+
// Either
|
|
39
|
+
// 1) No previous session; or
|
|
40
|
+
// 2) Previous session expired
|
|
41
|
+
this.config.sessionId = Date.now();
|
|
42
|
+
isNewSession = true;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const connector = getAnalyticsConnector();
|
|
46
|
+
connector.eventBridge.setEventReceiver((event) => {
|
|
47
|
+
void this.track(event.eventType, event.eventProperties);
|
|
48
|
+
});
|
|
49
|
+
connector.identityStore.setIdentity({
|
|
50
|
+
userId: this.config.userId,
|
|
51
|
+
deviceId: this.config.deviceId,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Step 4: Install plugins
|
|
55
|
+
// Do not track any events before this
|
|
56
|
+
await this.add(new Context());
|
|
57
|
+
await this.add(new IdentityEventSender());
|
|
58
|
+
await this.add(new Destination());
|
|
59
|
+
|
|
60
|
+
// Step 5: Track attributions
|
|
61
|
+
await this.runAttributionStrategy(options?.attribution, isNewSession);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async runAttributionStrategy(attributionConfig?: AttributionReactNativeOptions, isNewSession = false) {
|
|
65
|
+
if (isNative()) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const track = this.track.bind(this);
|
|
69
|
+
const onNewCampaign = this.setSessionId.bind(this, Date.now());
|
|
70
|
+
|
|
71
|
+
const storage = await createFlexibleStorage<Campaign>(this.config);
|
|
72
|
+
const campaignTracker = new CampaignTracker(this.config.apiKey, {
|
|
73
|
+
...attributionConfig,
|
|
74
|
+
storage,
|
|
75
|
+
track,
|
|
76
|
+
onNewCampaign,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
await campaignTracker.send(isNewSession);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
getUserId() {
|
|
83
|
+
return this.config.userId;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
setUserId(userId: string | undefined) {
|
|
87
|
+
this.config.userId = userId;
|
|
88
|
+
getAnalyticsConnector()
|
|
89
|
+
.identityStore.editIdentity()
|
|
90
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
91
|
+
// @ts-ignore
|
|
92
|
+
.setUserId(userId)
|
|
93
|
+
.commit();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
getDeviceId() {
|
|
97
|
+
return this.config.deviceId;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
setDeviceId(deviceId: string) {
|
|
101
|
+
this.config.deviceId = deviceId;
|
|
102
|
+
getAnalyticsConnector()
|
|
103
|
+
.identityStore.editIdentity()
|
|
104
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
105
|
+
// @ts-ignore
|
|
106
|
+
.setDeviceId(deviceId)
|
|
107
|
+
.commit();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
regenerateDeviceId() {
|
|
111
|
+
const deviceId = createDeviceId();
|
|
112
|
+
this.setDeviceId(deviceId);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
getSessionId() {
|
|
116
|
+
return this.config.sessionId;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
setSessionId(sessionId: number) {
|
|
120
|
+
this.config.sessionId = sessionId;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
setOptOut(optOut: boolean) {
|
|
124
|
+
this.config.optOut = optOut;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const client = new AmplitudeReactNative();
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Initializes the Amplitude SDK with your apiKey, userId and optional configurations.
|
|
132
|
+
* This method must be called before any other operations.
|
|
133
|
+
*
|
|
134
|
+
* ```typescript
|
|
135
|
+
* await init(API_KEY, USER_ID, options).promise;
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
export const init = returnWrapper(client.init.bind(client));
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Adds a new plugin.
|
|
142
|
+
*
|
|
143
|
+
* ```typescript
|
|
144
|
+
* const plugin = {...};
|
|
145
|
+
* amplitude.add(plugin);
|
|
146
|
+
* ```
|
|
147
|
+
*/
|
|
148
|
+
export const add = returnWrapper(client.add.bind(client));
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Removes a plugin.
|
|
152
|
+
*
|
|
153
|
+
* ```typescript
|
|
154
|
+
* amplitude.remove('myPlugin');
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
export const remove = returnWrapper(client.remove.bind(client));
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Tracks user-defined event, with specified type, optional event properties and optional overwrites.
|
|
161
|
+
*
|
|
162
|
+
* ```typescript
|
|
163
|
+
* // event tracking with event type only
|
|
164
|
+
* track('Page Load');
|
|
165
|
+
*
|
|
166
|
+
* // event tracking with event type and additional event properties
|
|
167
|
+
* track('Page Load', { loadTime: 1000 });
|
|
168
|
+
*
|
|
169
|
+
* // event tracking with event type, additional event properties, and overwritten event options
|
|
170
|
+
* track('Page Load', { loadTime: 1000 }, { sessionId: -1 });
|
|
171
|
+
*
|
|
172
|
+
* // alternatively, this tracking method is awaitable
|
|
173
|
+
* const result = await track('Page Load').promise;
|
|
174
|
+
* console.log(result.event); // {...}
|
|
175
|
+
* console.log(result.code); // 200
|
|
176
|
+
* console.log(result.message); // "Event tracked successfully"
|
|
177
|
+
* ```
|
|
178
|
+
*/
|
|
179
|
+
export const track = returnWrapper(client.track.bind(client));
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Alias for track()
|
|
183
|
+
*/
|
|
184
|
+
export const logEvent = returnWrapper(client.logEvent.bind(client));
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Sends an identify event containing user property operations
|
|
188
|
+
*
|
|
189
|
+
* ```typescript
|
|
190
|
+
* const id = new Identify();
|
|
191
|
+
* id.set('colors', ['rose', 'gold']);
|
|
192
|
+
* identify(id);
|
|
193
|
+
*
|
|
194
|
+
* // alternatively, this tracking method is awaitable
|
|
195
|
+
* const result = await identify(id).promise;
|
|
196
|
+
* console.log(result.event); // {...}
|
|
197
|
+
* console.log(result.code); // 200
|
|
198
|
+
* console.log(result.message); // "Event tracked successfully"
|
|
199
|
+
* ```
|
|
200
|
+
*/
|
|
201
|
+
export const identify = returnWrapper(client.identify.bind(client));
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Sends a group identify event containing group property operations.
|
|
205
|
+
*
|
|
206
|
+
* ```typescript
|
|
207
|
+
* const id = new Identify();
|
|
208
|
+
* id.set('skills', ['js', 'ts']);
|
|
209
|
+
* const groupType = 'org';
|
|
210
|
+
* const groupName = 'engineering';
|
|
211
|
+
* groupIdentify(groupType, groupName, id);
|
|
212
|
+
*
|
|
213
|
+
* // alternatively, this tracking method is awaitable
|
|
214
|
+
* const result = await groupIdentify(groupType, groupName, id).promise;
|
|
215
|
+
* console.log(result.event); // {...}
|
|
216
|
+
* console.log(result.code); // 200
|
|
217
|
+
* console.log(result.message); // "Event tracked successfully"
|
|
218
|
+
* ```
|
|
219
|
+
*/
|
|
220
|
+
export const groupIdentify = returnWrapper(client.groupIdentify.bind(client));
|
|
221
|
+
export const setGroup = returnWrapper(client.setGroup.bind(client));
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Sends a revenue event containing revenue property operations.
|
|
225
|
+
*
|
|
226
|
+
* ```typescript
|
|
227
|
+
* const rev = new Revenue();
|
|
228
|
+
* rev.setRevenue(100);
|
|
229
|
+
* revenue(rev);
|
|
230
|
+
*
|
|
231
|
+
* // alternatively, this tracking method is awaitable
|
|
232
|
+
* const result = await revenue(rev).promise;
|
|
233
|
+
* console.log(result.event); // {...}
|
|
234
|
+
* console.log(result.code); // 200
|
|
235
|
+
* console.log(result.message); // "Event tracked successfully"
|
|
236
|
+
* ```
|
|
237
|
+
*/
|
|
238
|
+
export const revenue = returnWrapper(client.revenue.bind(client));
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Returns current user ID.
|
|
242
|
+
*
|
|
243
|
+
* ```typescript
|
|
244
|
+
* const userId = getUserId();
|
|
245
|
+
* ```
|
|
246
|
+
*/
|
|
247
|
+
export const getUserId = client.getUserId.bind(client);
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Sets a new user ID.
|
|
251
|
+
*
|
|
252
|
+
* ```typescript
|
|
253
|
+
* setUserId('userId');
|
|
254
|
+
* ```
|
|
255
|
+
*/
|
|
256
|
+
export const setUserId = client.setUserId.bind(client);
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Returns current device ID.
|
|
260
|
+
*
|
|
261
|
+
* ```typescript
|
|
262
|
+
* const deviceId = getDeviceId();
|
|
263
|
+
* ```
|
|
264
|
+
*/
|
|
265
|
+
export const getDeviceId = client.getDeviceId.bind(client);
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Sets a new device ID.
|
|
269
|
+
* When setting a custom device ID, make sure the value is sufficiently unique.
|
|
270
|
+
* A uuid is recommended.
|
|
271
|
+
*
|
|
272
|
+
* ```typescript
|
|
273
|
+
* setDeviceId('deviceId');
|
|
274
|
+
* ```
|
|
275
|
+
*/
|
|
276
|
+
export const setDeviceId = client.setDeviceId.bind(client);
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Regenerates a new random deviceId for current user. Note: this is not recommended unless you know what you
|
|
280
|
+
* are doing. This can be used in conjunction with `setUserId(undefined)` to anonymize users after they log out.
|
|
281
|
+
* With an `unefined` userId and a completely new deviceId, the current user would appear as a brand new user in dashboard.
|
|
282
|
+
*
|
|
283
|
+
* ```typescript
|
|
284
|
+
* regenerateDeviceId();
|
|
285
|
+
* ```
|
|
286
|
+
*/
|
|
287
|
+
export const regenerateDeviceId = client.regenerateDeviceId.bind(client);
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Returns current session ID.
|
|
291
|
+
*
|
|
292
|
+
* ```typescript
|
|
293
|
+
* const sessionId = getSessionId();
|
|
294
|
+
* ```
|
|
295
|
+
*/
|
|
296
|
+
export const getSessionId = client.getSessionId.bind(client);
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Sets a new session ID.
|
|
300
|
+
* When settign a custom session ID, make sure the value is in milliseconds since epoch (Unix Timestamp).
|
|
301
|
+
*
|
|
302
|
+
* ```typescript
|
|
303
|
+
* setSessionId(Date.now());
|
|
304
|
+
* ```
|
|
305
|
+
*/
|
|
306
|
+
export const setSessionId = client.setSessionId.bind(client);
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Sets a new optOut config value. This toggles event tracking on/off.
|
|
310
|
+
*
|
|
311
|
+
*```typescript
|
|
312
|
+
* // Stops tracking
|
|
313
|
+
* setOptOut(true);
|
|
314
|
+
*
|
|
315
|
+
* // Starts/resumes tracking
|
|
316
|
+
* setOptOut(false);
|
|
317
|
+
* ```
|
|
318
|
+
*/
|
|
319
|
+
export const setOptOut = client.setOptOut.bind(client);
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Flush and send all the events which haven't been sent.
|
|
323
|
+
*
|
|
324
|
+
*```typescript
|
|
325
|
+
* // Send all the unsent events
|
|
326
|
+
* flush();
|
|
327
|
+
*
|
|
328
|
+
* // alternatively, this tracking method is awaitable
|
|
329
|
+
* await flush().promise;
|
|
330
|
+
* ```
|
|
331
|
+
*/
|
|
332
|
+
export const flush = returnWrapper(client.flush.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
|
+
}
|