react-native-nitro-amplitude 0.1.0 → 0.2.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 +166 -53
- package/cpp/bindings/HybridAmplitudeWorker.cpp +22 -6
- package/lib/commonjs/analytics/react-native-client.js +13 -8
- package/lib/commonjs/analytics/react-native-client.js.map +1 -1
- package/lib/commonjs/experiment/transport/http.js +8 -2
- package/lib/commonjs/experiment/transport/http.js.map +1 -1
- package/lib/commonjs/index.js +1 -1
- package/lib/commonjs/index.web.js +289 -13
- package/lib/commonjs/index.web.js.map +1 -1
- package/lib/commonjs/native/context.web.js +26 -0
- package/lib/commonjs/native/context.web.js.map +1 -0
- package/lib/commonjs/native/http.web.js +15 -0
- package/lib/commonjs/native/http.web.js.map +1 -0
- package/lib/commonjs/native/storage.web.js +135 -0
- package/lib/commonjs/native/storage.web.js.map +1 -0
- package/lib/module/analytics/react-native-client.js +13 -8
- package/lib/module/analytics/react-native-client.js.map +1 -1
- package/lib/module/experiment/transport/http.js +8 -2
- package/lib/module/experiment/transport/http.js.map +1 -1
- package/lib/module/index.js +1 -1
- package/lib/module/index.web.js +47 -11
- package/lib/module/index.web.js.map +1 -1
- package/lib/module/native/context.web.js +18 -0
- package/lib/module/native/context.web.js.map +1 -0
- package/lib/module/native/http.web.js +10 -0
- package/lib/module/native/http.web.js.map +1 -0
- package/lib/module/native/storage.web.js +128 -0
- package/lib/module/native/storage.web.js.map +1 -0
- package/lib/typescript/analytics/react-native-client.d.ts +3 -6
- package/lib/typescript/analytics/react-native-client.d.ts.map +1 -1
- package/lib/typescript/experiment/transport/http.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +1 -1
- package/lib/typescript/index.web.d.ts +27 -8
- package/lib/typescript/index.web.d.ts.map +1 -1
- package/lib/typescript/native/context.web.d.ts +8 -0
- package/lib/typescript/native/context.web.d.ts.map +1 -0
- package/lib/typescript/native/http.web.d.ts +6 -0
- package/lib/typescript/native/http.web.d.ts.map +1 -0
- package/lib/typescript/native/storage.web.d.ts +30 -0
- package/lib/typescript/native/storage.web.d.ts.map +1 -0
- package/package.json +4 -2
- package/src/analytics/react-native-client.ts +21 -9
- package/src/experiment/transport/http.ts +10 -2
- package/src/index.ts +1 -1
- package/src/index.web.ts +61 -14
- package/src/native/context.web.ts +38 -0
- package/src/native/http.web.ts +22 -0
- package/src/native/storage.web.ts +152 -0
|
@@ -48,7 +48,7 @@ export type AmplitudeReactNativeClient = ReactNativeClient & {
|
|
|
48
48
|
};
|
|
49
49
|
|
|
50
50
|
let nextConnectorOwnerId = 0;
|
|
51
|
-
|
|
51
|
+
const activeConnectorOwnerIds = new Map<string, number>();
|
|
52
52
|
|
|
53
53
|
export class AmplitudeReactNative
|
|
54
54
|
extends AmplitudeCore
|
|
@@ -62,7 +62,7 @@ export class AmplitudeReactNative
|
|
|
62
62
|
|
|
63
63
|
// @ts-ignore
|
|
64
64
|
config: ReactNativeConfig;
|
|
65
|
-
userProperties:
|
|
65
|
+
userProperties: Record<string, unknown> | undefined;
|
|
66
66
|
|
|
67
67
|
init(apiKey = "", userId?: string, options?: ReactNativeOptions) {
|
|
68
68
|
this.initPromise =
|
|
@@ -99,7 +99,8 @@ export class AmplitudeReactNative
|
|
|
99
99
|
// Set up the analytics connector to integrate with the experiment SDK.
|
|
100
100
|
// Send events from the experiment SDK and forward identifies to the
|
|
101
101
|
// identity store.
|
|
102
|
-
const
|
|
102
|
+
const connectorInstanceName = this.getConnectorInstanceName();
|
|
103
|
+
const connector = getAnalyticsConnector(connectorInstanceName);
|
|
103
104
|
connector.identityStore.setIdentity({
|
|
104
105
|
userId: this.config.userId,
|
|
105
106
|
deviceId: this.config.deviceId,
|
|
@@ -140,7 +141,7 @@ export class AmplitudeReactNative
|
|
|
140
141
|
this.track(event.eventType, event.eventProperties).promise,
|
|
141
142
|
);
|
|
142
143
|
});
|
|
143
|
-
|
|
144
|
+
activeConnectorOwnerIds.set(connectorInstanceName, this.connectorOwnerId);
|
|
144
145
|
} catch (error) {
|
|
145
146
|
if (appStateHandlerInstalled) {
|
|
146
147
|
this.appStateChangeHandler?.remove();
|
|
@@ -162,11 +163,18 @@ export class AmplitudeReactNative
|
|
|
162
163
|
this.dispatchQ = [];
|
|
163
164
|
this.isReady = false;
|
|
164
165
|
|
|
165
|
-
|
|
166
|
-
|
|
166
|
+
const connectorInstanceName = this.config
|
|
167
|
+
? this.getConnectorInstanceName()
|
|
168
|
+
: undefined;
|
|
169
|
+
if (
|
|
170
|
+
connectorInstanceName &&
|
|
171
|
+
activeConnectorOwnerIds.get(connectorInstanceName) ===
|
|
172
|
+
this.connectorOwnerId
|
|
173
|
+
) {
|
|
174
|
+
const connector = getAnalyticsConnector(connectorInstanceName);
|
|
167
175
|
connector.eventBridge.setEventReceiver(() => undefined);
|
|
168
176
|
connector.identityStore.setIdentity({});
|
|
169
|
-
|
|
177
|
+
activeConnectorOwnerIds.delete(connectorInstanceName);
|
|
170
178
|
}
|
|
171
179
|
}
|
|
172
180
|
|
|
@@ -178,6 +186,10 @@ export class AmplitudeReactNative
|
|
|
178
186
|
});
|
|
179
187
|
}
|
|
180
188
|
|
|
189
|
+
private getConnectorInstanceName() {
|
|
190
|
+
return this.config.instanceName ?? "$default_instance";
|
|
191
|
+
}
|
|
192
|
+
|
|
181
193
|
private cancelDestinationFlushes() {
|
|
182
194
|
this.timeline.plugins.forEach((plugin) => {
|
|
183
195
|
if (plugin.type !== "destination") {
|
|
@@ -232,7 +244,7 @@ export class AmplitudeReactNative
|
|
|
232
244
|
return;
|
|
233
245
|
}
|
|
234
246
|
this.config.userId = userId;
|
|
235
|
-
setConnectorUserId(userId);
|
|
247
|
+
setConnectorUserId(userId, this.getConnectorInstanceName());
|
|
236
248
|
}
|
|
237
249
|
|
|
238
250
|
getDeviceId() {
|
|
@@ -245,7 +257,7 @@ export class AmplitudeReactNative
|
|
|
245
257
|
return;
|
|
246
258
|
}
|
|
247
259
|
this.config.deviceId = deviceId;
|
|
248
|
-
setConnectorDeviceId(deviceId);
|
|
260
|
+
setConnectorDeviceId(deviceId, this.getConnectorInstanceName());
|
|
249
261
|
}
|
|
250
262
|
|
|
251
263
|
identify(identify: IIdentify, eventOptions?: EventOptions) {
|
|
@@ -66,11 +66,19 @@ const _request = (
|
|
|
66
66
|
): Promise<SimpleResponse> => {
|
|
67
67
|
const abortController = getAbortController();
|
|
68
68
|
const call = async () => {
|
|
69
|
-
const
|
|
69
|
+
const upperMethod = method.toUpperCase();
|
|
70
|
+
const init: RequestInit = {
|
|
70
71
|
method: method,
|
|
71
72
|
headers: headers,
|
|
72
|
-
body: data,
|
|
73
73
|
signal: abortController?.signal,
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
if (upperMethod !== "GET" && upperMethod !== "HEAD") {
|
|
77
|
+
init.body = data;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const response = await getFetch()(requestUrl, {
|
|
81
|
+
...init,
|
|
74
82
|
});
|
|
75
83
|
const simpleResponse: SimpleResponse = {
|
|
76
84
|
status: response.status,
|
package/src/index.ts
CHANGED
|
@@ -63,4 +63,4 @@ export type { AmplitudeContext } from "./AmplitudeContext.nitro";
|
|
|
63
63
|
export type { AmplitudeStorage } from "./AmplitudeStorage.nitro";
|
|
64
64
|
export type { AmplitudeWorker } from "./AmplitudeWorker.nitro";
|
|
65
65
|
|
|
66
|
-
export const VERSION = "0.
|
|
66
|
+
export const VERSION = "0.2.0";
|
package/src/index.web.ts
CHANGED
|
@@ -1,19 +1,66 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
};
|
|
1
|
+
import analyticsClient, {
|
|
2
|
+
createInstance,
|
|
3
|
+
} from "./analytics/react-native-client";
|
|
4
|
+
import { prefetchNativeContext } from "./native/context";
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export
|
|
10
|
-
export const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
6
|
+
import * as AnalyticsTypes from "./analytics/types";
|
|
7
|
+
|
|
8
|
+
export { createInstance };
|
|
9
|
+
export const {
|
|
10
|
+
add,
|
|
11
|
+
flush,
|
|
12
|
+
getDeviceId,
|
|
13
|
+
getSessionId,
|
|
14
|
+
getUserId,
|
|
15
|
+
groupIdentify,
|
|
16
|
+
identify,
|
|
17
|
+
init,
|
|
18
|
+
logEvent,
|
|
19
|
+
remove,
|
|
20
|
+
reset,
|
|
21
|
+
revenue,
|
|
22
|
+
setDeviceId,
|
|
23
|
+
setGroup,
|
|
24
|
+
setOptOut,
|
|
25
|
+
setSessionId,
|
|
26
|
+
setUserId,
|
|
27
|
+
shutdown,
|
|
28
|
+
track,
|
|
29
|
+
extendSession,
|
|
30
|
+
} = analyticsClient;
|
|
31
|
+
|
|
32
|
+
export { Revenue, Identify } from "@amplitude/analytics-core";
|
|
33
|
+
export {
|
|
34
|
+
InMemoryStorage,
|
|
35
|
+
LocalStorage,
|
|
36
|
+
MemoryStorage,
|
|
37
|
+
} from "./analytics/storage/local-storage";
|
|
38
|
+
export { NitroAnalyticsStorage, NitroMemoryStorage } from "./native/storage";
|
|
39
|
+
export { nitroHttpClient } from "./native/http";
|
|
40
|
+
export { nitroTransport } from "./analytics/nitro-transport";
|
|
41
|
+
export { prefetchNativeContext };
|
|
42
|
+
export { AnalyticsTypes as Types };
|
|
43
|
+
|
|
44
|
+
export * from "./experiment/types/config";
|
|
45
|
+
export { Experiment } from "./experiment/factory";
|
|
46
|
+
export { StubExperimentClient } from "./experiment/stubClient";
|
|
47
|
+
export { ExperimentClient } from "./experiment/experimentClient";
|
|
48
|
+
export * from "./experiment/types/client";
|
|
49
|
+
export { Source } from "./experiment/types/source";
|
|
50
|
+
export * from "./experiment/types/user";
|
|
51
|
+
export * from "./experiment/types/variant";
|
|
52
|
+
export * from "./experiment/types/exposure";
|
|
53
|
+
export * from "./experiment/types/storage";
|
|
54
|
+
export { LogLevel } from "./experiment/types/logger";
|
|
55
|
+
export type { Logger } from "./experiment/types/logger";
|
|
56
|
+
export { ConsoleLogger } from "./experiment/logger/consoleLogger";
|
|
57
|
+
export {
|
|
58
|
+
LocalStorage as ExperimentLocalStorage,
|
|
59
|
+
MemoryStorage as ExperimentMemoryStorage,
|
|
60
|
+
} from "./experiment/storage/local-storage";
|
|
16
61
|
|
|
17
62
|
export type { AmplitudeContext } from "./AmplitudeContext.nitro";
|
|
18
63
|
export type { AmplitudeStorage } from "./AmplitudeStorage.nitro";
|
|
19
64
|
export type { AmplitudeWorker } from "./AmplitudeWorker.nitro";
|
|
65
|
+
|
|
66
|
+
export const VERSION = "0.2.0";
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { ReactNativeTrackingOptions } from "@amplitude/analytics-core";
|
|
2
|
+
import type { LegacySessionData, NativeApplicationContext } from "./context";
|
|
3
|
+
|
|
4
|
+
type NavigatorWithLanguage = Navigator & {
|
|
5
|
+
userLanguage?: string;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export function prefetchNativeContext(): void {}
|
|
9
|
+
|
|
10
|
+
export function getNativeApplicationContext(
|
|
11
|
+
_options: ReactNativeTrackingOptions,
|
|
12
|
+
): NativeApplicationContext {
|
|
13
|
+
const browserNavigator =
|
|
14
|
+
typeof navigator === "undefined"
|
|
15
|
+
? undefined
|
|
16
|
+
: (navigator as NavigatorWithLanguage);
|
|
17
|
+
return {
|
|
18
|
+
platform: "Web",
|
|
19
|
+
language: browserNavigator?.language ?? browserNavigator?.userLanguage,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function getLegacySessionData(_instanceName: string): LegacySessionData {
|
|
24
|
+
return {};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function getLegacyEvents(
|
|
28
|
+
_instanceName: string,
|
|
29
|
+
_eventKind: string,
|
|
30
|
+
): string[] {
|
|
31
|
+
return [];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function removeLegacyEvent(
|
|
35
|
+
_instanceName: string,
|
|
36
|
+
_eventKind: string,
|
|
37
|
+
_eventId: number,
|
|
38
|
+
): void {}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { FetchHttpClient } from "../experiment/transport/http";
|
|
2
|
+
import type { HttpClient, SimpleResponse } from "../experiment/types/transport";
|
|
3
|
+
|
|
4
|
+
export class NitroHttpClient implements HttpClient {
|
|
5
|
+
request(
|
|
6
|
+
requestUrl: string,
|
|
7
|
+
method: string,
|
|
8
|
+
headers: Record<string, string>,
|
|
9
|
+
data: string | null,
|
|
10
|
+
timeoutMillis = 10000,
|
|
11
|
+
): Promise<SimpleResponse> {
|
|
12
|
+
return FetchHttpClient.request(
|
|
13
|
+
requestUrl,
|
|
14
|
+
method,
|
|
15
|
+
headers,
|
|
16
|
+
data ?? "",
|
|
17
|
+
timeoutMillis,
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const nitroHttpClient = new NitroHttpClient();
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import type { Storage as AnalyticsStorage } from "@amplitude/analytics-core";
|
|
2
|
+
import type { Storage as ExperimentStorage } from "../experiment/types/storage";
|
|
3
|
+
|
|
4
|
+
function namespaceKey(namespace: string, key: string): string {
|
|
5
|
+
return `${namespace}::${key}`;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
class WebStringStorage {
|
|
9
|
+
private static readonly memory = new Map<string, string>();
|
|
10
|
+
|
|
11
|
+
constructor(private readonly namespace: string) {}
|
|
12
|
+
|
|
13
|
+
get(key: string): string | undefined {
|
|
14
|
+
const storageKey = namespaceKey(this.namespace, key);
|
|
15
|
+
try {
|
|
16
|
+
if (typeof localStorage !== "undefined") {
|
|
17
|
+
return localStorage.getItem(storageKey) ?? undefined;
|
|
18
|
+
}
|
|
19
|
+
} catch {}
|
|
20
|
+
return WebStringStorage.memory.get(storageKey);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
set(key: string, value: string): void {
|
|
24
|
+
const storageKey = namespaceKey(this.namespace, key);
|
|
25
|
+
WebStringStorage.memory.set(storageKey, value);
|
|
26
|
+
try {
|
|
27
|
+
if (typeof localStorage !== "undefined") {
|
|
28
|
+
localStorage.setItem(storageKey, value);
|
|
29
|
+
}
|
|
30
|
+
} catch {}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
remove(key: string): void {
|
|
34
|
+
const storageKey = namespaceKey(this.namespace, key);
|
|
35
|
+
WebStringStorage.memory.delete(storageKey);
|
|
36
|
+
try {
|
|
37
|
+
if (typeof localStorage !== "undefined") {
|
|
38
|
+
localStorage.removeItem(storageKey);
|
|
39
|
+
}
|
|
40
|
+
} catch {}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
reset(): void {
|
|
44
|
+
const prefix = `${this.namespace}::`;
|
|
45
|
+
for (const key of WebStringStorage.memory.keys()) {
|
|
46
|
+
if (key.startsWith(prefix)) {
|
|
47
|
+
WebStringStorage.memory.delete(key);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
if (typeof localStorage !== "undefined") {
|
|
52
|
+
for (let index = localStorage.length - 1; index >= 0; index--) {
|
|
53
|
+
const key = localStorage.key(index);
|
|
54
|
+
if (key?.startsWith(prefix)) {
|
|
55
|
+
localStorage.removeItem(key);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
} catch {}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export class NitroAnalyticsStorage<T> implements AnalyticsStorage<T> {
|
|
64
|
+
private readonly storage: WebStringStorage;
|
|
65
|
+
|
|
66
|
+
constructor(namespace: string) {
|
|
67
|
+
this.storage = new WebStringStorage(namespace);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async isEnabled(): Promise<boolean> {
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async get(key: string): Promise<T | undefined> {
|
|
75
|
+
const raw = await this.getRaw(key);
|
|
76
|
+
if (!raw) {
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
return JSON.parse(raw) as T;
|
|
81
|
+
} catch {
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async getRaw(key: string): Promise<string | undefined> {
|
|
87
|
+
return this.storage.get(key);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async set(key: string, value: T): Promise<void> {
|
|
91
|
+
this.storage.set(key, JSON.stringify(value));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async remove(key: string): Promise<void> {
|
|
95
|
+
this.storage.remove(key);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async reset(): Promise<void> {
|
|
99
|
+
this.storage.reset();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export class NitroExperimentStorage implements ExperimentStorage {
|
|
104
|
+
private readonly storage: WebStringStorage;
|
|
105
|
+
|
|
106
|
+
constructor(namespace: string) {
|
|
107
|
+
this.storage = new WebStringStorage(namespace);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async get(key: string): Promise<string | null> {
|
|
111
|
+
return this.storage.get(key) ?? null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async put(key: string, value: string): Promise<void> {
|
|
115
|
+
this.storage.set(key, value);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async delete(key: string): Promise<void> {
|
|
119
|
+
this.storage.remove(key);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async reset(): Promise<void> {
|
|
123
|
+
this.storage.reset();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export class NitroMemoryStorage implements ExperimentStorage {
|
|
128
|
+
private readonly values = new Map<string, string>();
|
|
129
|
+
|
|
130
|
+
constructor(private readonly namespace: string) {}
|
|
131
|
+
|
|
132
|
+
async get(key: string): Promise<string | null> {
|
|
133
|
+
return this.values.get(namespaceKey(this.namespace, key)) ?? null;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async put(key: string, value: string): Promise<void> {
|
|
137
|
+
this.values.set(namespaceKey(this.namespace, key), value);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async delete(key: string): Promise<void> {
|
|
141
|
+
this.values.delete(namespaceKey(this.namespace, key));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async reset(): Promise<void> {
|
|
145
|
+
const prefix = `${this.namespace}::`;
|
|
146
|
+
for (const key of this.values.keys()) {
|
|
147
|
+
if (key.startsWith(prefix)) {
|
|
148
|
+
this.values.delete(key);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|