@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.
Files changed (40) hide show
  1. package/LICENSE +21 -0
  2. package/lib/commonjs/index.js +2 -2
  3. package/lib/commonjs/index.js.map +1 -1
  4. package/lib/commonjs/plugins/identity.js +1 -1
  5. package/lib/commonjs/plugins/identity.js.map +1 -1
  6. package/lib/commonjs/version.js +1 -1
  7. package/lib/commonjs/version.js.map +1 -1
  8. package/lib/module/index.js +5 -3
  9. package/lib/module/index.js.map +1 -1
  10. package/lib/module/plugins/identity.js +1 -1
  11. package/lib/module/plugins/identity.js.map +1 -1
  12. package/lib/module/version.js +1 -1
  13. package/lib/module/version.js.map +1 -1
  14. package/lib/typescript/index.d.ts +2 -1
  15. package/lib/typescript/index.d.ts.map +1 -1
  16. package/lib/typescript/version.d.ts +1 -1
  17. package/lib/typescript/version.d.ts.map +1 -1
  18. package/package.json +8 -7
  19. package/src/attribution/campaign-parser.ts +78 -0
  20. package/src/attribution/campaign-tracker.ts +112 -0
  21. package/src/attribution/constants.ts +32 -0
  22. package/src/config.ts +227 -0
  23. package/src/cookie-migration/index.ts +54 -0
  24. package/src/index.ts +24 -0
  25. package/src/plugins/context.ts +106 -0
  26. package/src/plugins/identity.ts +21 -0
  27. package/src/react-native-client.ts +332 -0
  28. package/src/session-manager.ts +81 -0
  29. package/src/storage/cookie.ts +97 -0
  30. package/src/storage/local-storage.ts +67 -0
  31. package/src/storage/utm-cookie.ts +27 -0
  32. package/src/transports/fetch.ts +23 -0
  33. package/src/typings/browser-snippet.d.ts +7 -0
  34. package/src/typings/ua-parser.d.ts +4 -0
  35. package/src/utils/analytics-connector.ts +5 -0
  36. package/src/utils/cookie-name.ts +9 -0
  37. package/src/utils/language.ts +7 -0
  38. package/src/utils/platform.ts +9 -0
  39. package/src/utils/query-params.ts +18 -0
  40. package/src/version.ts +1 -0
@@ -0,0 +1,97 @@
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
+ /* istanbul ignore if */
13
+ if (isNative() || typeof window === 'undefined') {
14
+ return false;
15
+ }
16
+
17
+ const random = String(Date.now());
18
+ const testStrorage = new CookieStorage<string>();
19
+ const testKey = 'AMP_TEST';
20
+ try {
21
+ await testStrorage.set(testKey, random);
22
+ const value = await testStrorage.get(testKey);
23
+ return value === random;
24
+ } catch {
25
+ /* istanbul ignore next */
26
+ return false;
27
+ } finally {
28
+ await testStrorage.remove(testKey);
29
+ }
30
+ }
31
+
32
+ async get(key: string): Promise<T | undefined> {
33
+ let value = await this.getRaw(key);
34
+ if (!value) {
35
+ return undefined;
36
+ }
37
+ try {
38
+ try {
39
+ value = decodeURIComponent(atob(value));
40
+ } catch {
41
+ // value not encoded
42
+ }
43
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
44
+ return JSON.parse(value);
45
+ } catch {
46
+ /* istanbul ignore next */
47
+ return undefined;
48
+ }
49
+ }
50
+
51
+ async getRaw(key: string): Promise<string | undefined> {
52
+ const cookie = window.document.cookie.split('; ');
53
+ const match = cookie.find((c) => c.indexOf(key + '=') === 0);
54
+ if (!match) {
55
+ return undefined;
56
+ }
57
+ return match.substring(key.length + 1);
58
+ }
59
+
60
+ async set(key: string, value: T | null): Promise<void> {
61
+ try {
62
+ const expirationDays = this.options.expirationDays ?? 0;
63
+ const expires = value !== null ? expirationDays : -1;
64
+ let expireDate: Date | undefined = undefined;
65
+ if (expires) {
66
+ const date = new Date();
67
+ date.setTime(date.getTime() + expires * 24 * 60 * 60 * 1000);
68
+ expireDate = date;
69
+ }
70
+ let str = `${key}=${btoa(encodeURIComponent(JSON.stringify(value)))}`;
71
+ if (expireDate) {
72
+ str += `; expires=${expireDate.toUTCString()}`;
73
+ }
74
+ str += '; path=/';
75
+ if (this.options.domain) {
76
+ str += `; domain=${this.options.domain}`;
77
+ }
78
+ if (this.options.secure) {
79
+ str += '; Secure';
80
+ }
81
+ if (this.options.sameSite) {
82
+ str += `; SameSite=${this.options.sameSite}`;
83
+ }
84
+ window.document.cookie = str;
85
+ } catch {
86
+ //
87
+ }
88
+ }
89
+
90
+ async remove(key: string): Promise<void> {
91
+ await this.set(key, null);
92
+ }
93
+
94
+ async reset(): Promise<void> {
95
+ return;
96
+ }
97
+ }
@@ -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
+ }
@@ -0,0 +1,27 @@
1
+ import { CookieStorage } from './cookie';
2
+
3
+ export class UTMCookie extends CookieStorage<Record<string, string | undefined>> {
4
+ async get(key: string): Promise<Record<string, string | undefined> | undefined> {
5
+ try {
6
+ const value = await this.getRaw(key);
7
+ if (!value) {
8
+ return undefined;
9
+ }
10
+ const entries = value.split('.').splice(-1)[0].split('|');
11
+ return entries.reduce<Record<string, string | undefined>>((acc, curr) => {
12
+ const [key, value = ''] = curr.split('=', 2);
13
+ if (!value) {
14
+ return acc;
15
+ }
16
+ acc[key] = decodeURIComponent(value);
17
+ return acc;
18
+ }, {});
19
+ } catch {
20
+ return undefined;
21
+ }
22
+ }
23
+
24
+ async set(): Promise<void> {
25
+ return undefined;
26
+ }
27
+ }
@@ -0,0 +1,23 @@
1
+ import { BaseTransport } from '@amplitude/analytics-core';
2
+ import { Payload, Response, Transport } from '@amplitude/analytics-types';
3
+
4
+ export class FetchTransport extends BaseTransport implements Transport {
5
+ async send(serverUrl: string, payload: Payload): Promise<Response | null> {
6
+ /* istanbul ignore if */
7
+ if (typeof fetch === 'undefined') {
8
+ throw new Error('FetchTransport is not supported');
9
+ }
10
+ const options: RequestInit = {
11
+ headers: {
12
+ 'Content-Type': 'application/json',
13
+ Accept: '*/*',
14
+ },
15
+ body: JSON.stringify(payload),
16
+ method: 'POST',
17
+ };
18
+ const response = await fetch(serverUrl, options);
19
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
20
+ const responsePayload: Record<string, any> = await response.json();
21
+ return this.buildResponse(responsePayload);
22
+ }
23
+ }
@@ -0,0 +1,7 @@
1
+ import { InstanceProxy } from '@amplitude/analytics-types';
2
+
3
+ declare global {
4
+ // globalThis only includes `var` declarations
5
+ // eslint-disable-next-line no-var
6
+ var amplitude: InstanceProxy & { invoked: boolean };
7
+ }
@@ -0,0 +1,4 @@
1
+ declare module '@amplitude/ua-parser-js' {
2
+ import UAParser from 'ua-parser-js';
3
+ export = UAParser;
4
+ }
@@ -0,0 +1,5 @@
1
+ import { AnalyticsConnector } from '@amplitude/analytics-connector';
2
+
3
+ export const getAnalyticsConnector = (): AnalyticsConnector => {
4
+ return AnalyticsConnector.getInstance('$default_instance');
5
+ };
@@ -0,0 +1,9 @@
1
+ import { AMPLITUDE_PREFIX } from '@amplitude/analytics-core';
2
+
3
+ export const getCookieName = (apiKey: string, postKey = '', limit = 10) => {
4
+ return [AMPLITUDE_PREFIX, postKey, apiKey.substring(0, limit)].filter(Boolean).join('_');
5
+ };
6
+
7
+ export const getOldCookieName = (apiKey: string) => {
8
+ return `${AMPLITUDE_PREFIX.toLowerCase()}_${apiKey.substring(0, 6)}`;
9
+ };
@@ -0,0 +1,7 @@
1
+ export const getLanguage = (): string => {
2
+ if (typeof navigator === 'undefined') return '';
3
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
4
+ const userLanguage = (navigator as any).userLanguage as string | undefined;
5
+
6
+ return navigator.languages?.[0] ?? navigator.language ?? userLanguage ?? '';
7
+ };
@@ -0,0 +1,9 @@
1
+ import { Platform } from 'react-native';
2
+
3
+ export const isWeb = (): boolean => {
4
+ return Platform.OS === 'web';
5
+ };
6
+
7
+ export const isNative = (): boolean => {
8
+ return !isWeb();
9
+ };
@@ -0,0 +1,18 @@
1
+ import { isNative } from './platform';
2
+
3
+ export const getQueryParams = (): Record<string, string | undefined> => {
4
+ /* istanbul ignore if */
5
+ if (isNative() || typeof window === 'undefined') {
6
+ return {};
7
+ }
8
+ const pairs = window.location.search.substring(1).split('&').filter(Boolean);
9
+ const params = pairs.reduce<Record<string, string | undefined>>((acc, curr) => {
10
+ const [key, value = ''] = curr.split('=', 2);
11
+ if (!value) {
12
+ return acc;
13
+ }
14
+ acc[decodeURIComponent(key)] = decodeURIComponent(value);
15
+ return acc;
16
+ }, {});
17
+ return params;
18
+ };
package/src/version.ts ADDED
@@ -0,0 +1 @@
1
+ export const VERSION = '0.1.0';