react-native-nitro-amplitude 0.2.0 → 0.5.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 +387 -8
- package/cpp/bindings/HybridAmplitudeWorker.cpp +5 -1
- package/lib/commonjs/analytics/config.js +31 -10
- package/lib/commonjs/analytics/config.js.map +1 -1
- package/lib/commonjs/analytics/index.js +8 -2
- package/lib/commonjs/analytics/index.js.map +1 -1
- package/lib/commonjs/analytics/network-guarded-fetch-transport.js +16 -0
- package/lib/commonjs/analytics/network-guarded-fetch-transport.js.map +1 -0
- package/lib/commonjs/analytics/nitro-transport.js +2 -0
- package/lib/commonjs/analytics/nitro-transport.js.map +1 -1
- package/lib/commonjs/analytics/plugins/context.js +7 -1
- package/lib/commonjs/analytics/plugins/context.js.map +1 -1
- package/lib/commonjs/analytics/react-native-client.js +142 -1
- package/lib/commonjs/analytics/react-native-client.js.map +1 -1
- package/lib/commonjs/diagnostics.js +92 -0
- package/lib/commonjs/diagnostics.js.map +1 -0
- package/lib/commonjs/errors.js +48 -0
- package/lib/commonjs/errors.js.map +1 -0
- package/lib/commonjs/experiment/experimentClient.js +84 -2
- package/lib/commonjs/experiment/experimentClient.js.map +1 -1
- package/lib/commonjs/experiment/index.js +12 -0
- package/lib/commonjs/experiment/index.js.map +1 -1
- package/lib/commonjs/experiment/stubClient.js +25 -0
- package/lib/commonjs/experiment/stubClient.js.map +1 -1
- package/lib/commonjs/experiment/typed-variants.js +52 -0
- package/lib/commonjs/experiment/typed-variants.js.map +1 -0
- package/lib/commonjs/experiment/types/config.js +32 -4
- package/lib/commonjs/experiment/types/config.js.map +1 -1
- package/lib/commonjs/index.js +105 -3
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.web.js +110 -12
- package/lib/commonjs/index.web.js.map +1 -1
- package/lib/commonjs/native/http.js +23 -8
- package/lib/commonjs/native/http.js.map +1 -1
- package/lib/commonjs/native/http.web.js +2 -0
- package/lib/commonjs/native/http.web.js.map +1 -1
- package/lib/commonjs/native/hybrid.web.js +23 -0
- package/lib/commonjs/native/hybrid.web.js.map +1 -0
- package/lib/commonjs/native/storage.js +27 -15
- package/lib/commonjs/native/storage.js.map +1 -1
- package/lib/commonjs/network.js +154 -0
- package/lib/commonjs/network.js.map +1 -0
- package/lib/commonjs/presets.js +118 -0
- package/lib/commonjs/presets.js.map +1 -0
- package/lib/commonjs/testing.js +166 -0
- package/lib/commonjs/testing.js.map +1 -0
- package/lib/module/analytics/config.js +32 -11
- package/lib/module/analytics/config.js.map +1 -1
- package/lib/module/analytics/index.js +4 -1
- package/lib/module/analytics/index.js.map +1 -1
- package/lib/module/analytics/network-guarded-fetch-transport.js +11 -0
- package/lib/module/analytics/network-guarded-fetch-transport.js.map +1 -0
- package/lib/module/analytics/nitro-transport.js +2 -0
- package/lib/module/analytics/nitro-transport.js.map +1 -1
- package/lib/module/analytics/plugins/context.js +7 -1
- package/lib/module/analytics/plugins/context.js.map +1 -1
- package/lib/module/analytics/react-native-client.js +141 -1
- package/lib/module/analytics/react-native-client.js.map +1 -1
- package/lib/module/diagnostics.js +85 -0
- package/lib/module/diagnostics.js.map +1 -0
- package/lib/module/errors.js +41 -0
- package/lib/module/errors.js.map +1 -0
- package/lib/module/experiment/experimentClient.js +84 -2
- package/lib/module/experiment/experimentClient.js.map +1 -1
- package/lib/module/experiment/index.js +1 -0
- package/lib/module/experiment/index.js.map +1 -1
- package/lib/module/experiment/stubClient.js +25 -0
- package/lib/module/experiment/stubClient.js.map +1 -1
- package/lib/module/experiment/typed-variants.js +43 -0
- package/lib/module/experiment/typed-variants.js.map +1 -0
- package/lib/module/experiment/types/config.js +31 -4
- package/lib/module/experiment/types/config.js.map +1 -1
- package/lib/module/index.js +15 -3
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.web.js +19 -6
- package/lib/module/index.web.js.map +1 -1
- package/lib/module/native/http.js +23 -8
- package/lib/module/native/http.js.map +1 -1
- package/lib/module/native/http.web.js +2 -0
- package/lib/module/native/http.web.js.map +1 -1
- package/lib/module/native/hybrid.web.js +16 -0
- package/lib/module/native/hybrid.web.js.map +1 -0
- package/lib/module/native/storage.js +27 -15
- package/lib/module/native/storage.js.map +1 -1
- package/lib/module/network.js +138 -0
- package/lib/module/network.js.map +1 -0
- package/lib/module/presets.js +110 -0
- package/lib/module/presets.js.map +1 -0
- package/lib/module/testing.js +160 -0
- package/lib/module/testing.js.map +1 -0
- package/lib/typescript/analytics/config.d.ts +19 -6
- package/lib/typescript/analytics/config.d.ts.map +1 -1
- package/lib/typescript/analytics/index.d.ts +1 -1
- package/lib/typescript/analytics/index.d.ts.map +1 -1
- package/lib/typescript/analytics/network-guarded-fetch-transport.d.ts +6 -0
- package/lib/typescript/analytics/network-guarded-fetch-transport.d.ts.map +1 -0
- package/lib/typescript/analytics/nitro-transport.d.ts.map +1 -1
- package/lib/typescript/analytics/plugins/context.d.ts +2 -0
- package/lib/typescript/analytics/plugins/context.d.ts.map +1 -1
- package/lib/typescript/analytics/react-native-client.d.ts +43 -0
- package/lib/typescript/analytics/react-native-client.d.ts.map +1 -1
- package/lib/typescript/diagnostics.d.ts +21 -0
- package/lib/typescript/diagnostics.d.ts.map +1 -0
- package/lib/typescript/errors.d.ts +9 -0
- package/lib/typescript/errors.d.ts.map +1 -0
- package/lib/typescript/experiment/experimentClient.d.ts +9 -1
- package/lib/typescript/experiment/experimentClient.d.ts.map +1 -1
- package/lib/typescript/experiment/index.d.ts +1 -0
- package/lib/typescript/experiment/index.d.ts.map +1 -1
- package/lib/typescript/experiment/stubClient.d.ts +6 -1
- package/lib/typescript/experiment/stubClient.d.ts.map +1 -1
- package/lib/typescript/experiment/typed-variants.d.ts +9 -0
- package/lib/typescript/experiment/typed-variants.d.ts.map +1 -0
- package/lib/typescript/experiment/types/client.d.ts +21 -0
- package/lib/typescript/experiment/types/client.d.ts.map +1 -1
- package/lib/typescript/experiment/types/config.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +12 -3
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/index.web.d.ts +16 -6
- package/lib/typescript/index.web.d.ts.map +1 -1
- package/lib/typescript/native/http.d.ts.map +1 -1
- package/lib/typescript/native/http.web.d.ts.map +1 -1
- package/lib/typescript/native/hybrid.web.d.ts +8 -0
- package/lib/typescript/native/hybrid.web.d.ts.map +1 -0
- package/lib/typescript/native/storage.d.ts.map +1 -1
- package/lib/typescript/network.d.ts +50 -0
- package/lib/typescript/network.d.ts.map +1 -0
- package/lib/typescript/presets.d.ts +50 -0
- package/lib/typescript/presets.d.ts.map +1 -0
- package/lib/typescript/testing.d.ts +11 -0
- package/lib/typescript/testing.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/analytics/config.ts +33 -8
- package/src/analytics/index.ts +3 -0
- package/src/analytics/network-guarded-fetch-transport.ts +10 -0
- package/src/analytics/nitro-transport.ts +2 -0
- package/src/analytics/plugins/context.ts +10 -1
- package/src/analytics/react-native-client.ts +217 -0
- package/src/diagnostics.ts +119 -0
- package/src/errors.ts +60 -0
- package/src/experiment/experimentClient.ts +116 -3
- package/src/experiment/index.ts +1 -0
- package/src/experiment/stubClient.ts +42 -1
- package/src/experiment/typed-variants.ts +68 -0
- package/src/experiment/types/client.ts +29 -0
- package/src/experiment/types/config.ts +29 -5
- package/src/index.ts +28 -2
- package/src/index.web.ts +33 -5
- package/src/native/http.ts +38 -8
- package/src/native/http.web.ts +2 -0
- package/src/native/hybrid.web.ts +21 -0
- package/src/native/storage.ts +27 -25
- package/src/network.ts +258 -0
- package/src/presets.ts +208 -0
- package/src/testing.ts +177 -0
package/src/native/storage.ts
CHANGED
|
@@ -26,26 +26,26 @@ export class NitroAnalyticsStorage<T> implements AnalyticsStorage<T> {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
async getRaw(key: string): Promise<string | undefined> {
|
|
29
|
-
|
|
29
|
+
const storage = getAmplitudeStorage();
|
|
30
|
+
return storage.get(namespaceKey(this.namespace, key), true);
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
async set(key: string, value: T): Promise<void> {
|
|
33
|
-
getAmplitudeStorage()
|
|
34
|
-
|
|
35
|
-
JSON.stringify(value),
|
|
36
|
-
true,
|
|
37
|
-
);
|
|
34
|
+
const storage = getAmplitudeStorage();
|
|
35
|
+
storage.set(namespaceKey(this.namespace, key), JSON.stringify(value), true);
|
|
38
36
|
}
|
|
39
37
|
|
|
40
38
|
async remove(key: string): Promise<void> {
|
|
41
|
-
getAmplitudeStorage()
|
|
39
|
+
const storage = getAmplitudeStorage();
|
|
40
|
+
storage.remove(namespaceKey(this.namespace, key), true);
|
|
42
41
|
}
|
|
43
42
|
|
|
44
43
|
async reset(): Promise<void> {
|
|
44
|
+
const storage = getAmplitudeStorage();
|
|
45
45
|
const prefix = `${this.namespace}::`;
|
|
46
|
-
const keys =
|
|
46
|
+
const keys = storage.getKeysByPrefix(prefix, true);
|
|
47
47
|
if (keys.length > 0) {
|
|
48
|
-
|
|
48
|
+
storage.removeBatch(keys, true);
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
}
|
|
@@ -54,26 +54,27 @@ export class NitroExperimentStorage implements ExperimentStorage {
|
|
|
54
54
|
constructor(private readonly namespace: string) {}
|
|
55
55
|
|
|
56
56
|
async get(key: string): Promise<string | null> {
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
true,
|
|
60
|
-
);
|
|
57
|
+
const storage = getAmplitudeStorage();
|
|
58
|
+
const value = storage.get(namespaceKey(this.namespace, key), true);
|
|
61
59
|
return value ?? null;
|
|
62
60
|
}
|
|
63
61
|
|
|
64
62
|
async put(key: string, value: string): Promise<void> {
|
|
65
|
-
|
|
63
|
+
const storage = getAmplitudeStorage();
|
|
64
|
+
storage.set(namespaceKey(this.namespace, key), value, true);
|
|
66
65
|
}
|
|
67
66
|
|
|
68
67
|
async delete(key: string): Promise<void> {
|
|
69
|
-
getAmplitudeStorage()
|
|
68
|
+
const storage = getAmplitudeStorage();
|
|
69
|
+
storage.remove(namespaceKey(this.namespace, key), true);
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
async reset(): Promise<void> {
|
|
73
|
+
const storage = getAmplitudeStorage();
|
|
73
74
|
const prefix = `${this.namespace}::`;
|
|
74
|
-
const keys =
|
|
75
|
+
const keys = storage.getKeysByPrefix(prefix, true);
|
|
75
76
|
if (keys.length > 0) {
|
|
76
|
-
|
|
77
|
+
storage.removeBatch(keys, true);
|
|
77
78
|
}
|
|
78
79
|
}
|
|
79
80
|
}
|
|
@@ -82,26 +83,27 @@ export class NitroMemoryStorage implements ExperimentStorage {
|
|
|
82
83
|
constructor(private readonly namespace: string) {}
|
|
83
84
|
|
|
84
85
|
async get(key: string): Promise<string | null> {
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
false,
|
|
88
|
-
);
|
|
86
|
+
const storage = getAmplitudeStorage();
|
|
87
|
+
const value = storage.get(namespaceKey(this.namespace, key), false);
|
|
89
88
|
return value ?? null;
|
|
90
89
|
}
|
|
91
90
|
|
|
92
91
|
async put(key: string, value: string): Promise<void> {
|
|
93
|
-
|
|
92
|
+
const storage = getAmplitudeStorage();
|
|
93
|
+
storage.set(namespaceKey(this.namespace, key), value, false);
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
async delete(key: string): Promise<void> {
|
|
97
|
-
getAmplitudeStorage()
|
|
97
|
+
const storage = getAmplitudeStorage();
|
|
98
|
+
storage.remove(namespaceKey(this.namespace, key), false);
|
|
98
99
|
}
|
|
99
100
|
|
|
100
101
|
async reset(): Promise<void> {
|
|
102
|
+
const storage = getAmplitudeStorage();
|
|
101
103
|
const prefix = `${this.namespace}::`;
|
|
102
|
-
const keys =
|
|
104
|
+
const keys = storage.getKeysByPrefix(prefix, false);
|
|
103
105
|
if (keys.length > 0) {
|
|
104
|
-
|
|
106
|
+
storage.removeBatch(keys, false);
|
|
105
107
|
}
|
|
106
108
|
}
|
|
107
109
|
}
|
package/src/network.ts
ADDED
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import { BaseTransport } from "@amplitude/analytics-core";
|
|
2
|
+
import type { Payload, Response, Transport } from "@amplitude/analytics-core";
|
|
3
|
+
import type { HttpClient, SimpleResponse } from "./experiment/types/transport";
|
|
4
|
+
import { createAmplitudeError } from "./errors";
|
|
5
|
+
|
|
6
|
+
let networkEnabled = true;
|
|
7
|
+
|
|
8
|
+
export type DryRunRequest = {
|
|
9
|
+
url: string;
|
|
10
|
+
method: string;
|
|
11
|
+
headers: Record<string, string>;
|
|
12
|
+
data: string | null;
|
|
13
|
+
timeoutMillis: number;
|
|
14
|
+
createdAt: number;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type DryRunEvent = {
|
|
18
|
+
serverUrl: string;
|
|
19
|
+
payload: Payload;
|
|
20
|
+
createdAt: number;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type AmplitudeNetworkTiming = {
|
|
24
|
+
kind: "analytics" | "experiment";
|
|
25
|
+
url: string;
|
|
26
|
+
method: string;
|
|
27
|
+
startedAt: number;
|
|
28
|
+
finishedAt: number;
|
|
29
|
+
durationMillis: number;
|
|
30
|
+
status?: number | string;
|
|
31
|
+
error?: string;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export type AmplitudeNetworkTimingRecorder = (
|
|
35
|
+
timing: AmplitudeNetworkTiming,
|
|
36
|
+
) => void;
|
|
37
|
+
|
|
38
|
+
export type AmplitudeNetworkTimingBuffer = {
|
|
39
|
+
record: AmplitudeNetworkTimingRecorder;
|
|
40
|
+
getTimings: () => AmplitudeNetworkTiming[];
|
|
41
|
+
clear: () => void;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const dryRunRequests: DryRunRequest[] = [];
|
|
45
|
+
const dryRunEvents: DryRunEvent[] = [];
|
|
46
|
+
|
|
47
|
+
export function setNetworkEnabled(enabled: boolean): void {
|
|
48
|
+
networkEnabled = enabled;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function getNetworkEnabled(): boolean {
|
|
52
|
+
return networkEnabled;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function clearDryRunTransportRecords(): void {
|
|
56
|
+
dryRunRequests.length = 0;
|
|
57
|
+
dryRunEvents.length = 0;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function getDryRunTransportRecords(): DryRunRequest[] {
|
|
61
|
+
return dryRunRequests.map((request) => ({ ...request }));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function getDryRunAnalyticsEvents(): DryRunEvent[] {
|
|
65
|
+
return dryRunEvents.map((event) => ({
|
|
66
|
+
...event,
|
|
67
|
+
payload: { ...event.payload },
|
|
68
|
+
}));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function assertNetworkEnabled(): void {
|
|
72
|
+
if (!networkEnabled) {
|
|
73
|
+
throw createAmplitudeError(
|
|
74
|
+
"network_error",
|
|
75
|
+
"Amplitude network traffic is disabled",
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function nowMillis(): number {
|
|
81
|
+
return typeof performance !== "undefined" && performance.now
|
|
82
|
+
? performance.now()
|
|
83
|
+
: Date.now();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function createTiming(
|
|
87
|
+
kind: AmplitudeNetworkTiming["kind"],
|
|
88
|
+
url: string,
|
|
89
|
+
method: string,
|
|
90
|
+
startedAt: number,
|
|
91
|
+
status?: number | string,
|
|
92
|
+
error?: unknown,
|
|
93
|
+
): AmplitudeNetworkTiming {
|
|
94
|
+
const finishedAt = nowMillis();
|
|
95
|
+
return {
|
|
96
|
+
kind,
|
|
97
|
+
url,
|
|
98
|
+
method,
|
|
99
|
+
startedAt,
|
|
100
|
+
finishedAt,
|
|
101
|
+
durationMillis: Math.round((finishedAt - startedAt) * 10) / 10,
|
|
102
|
+
status,
|
|
103
|
+
error:
|
|
104
|
+
error === undefined
|
|
105
|
+
? undefined
|
|
106
|
+
: error instanceof Error
|
|
107
|
+
? error.message
|
|
108
|
+
: String(error),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function createNetworkTimingBuffer(
|
|
113
|
+
limit = 20,
|
|
114
|
+
): AmplitudeNetworkTimingBuffer {
|
|
115
|
+
const timings: AmplitudeNetworkTiming[] = [];
|
|
116
|
+
const normalizedLimit = Number.isFinite(limit) && limit > 0 ? limit : 20;
|
|
117
|
+
return {
|
|
118
|
+
record: (timing) => {
|
|
119
|
+
timings.push(timing);
|
|
120
|
+
while (timings.length > normalizedLimit) {
|
|
121
|
+
timings.shift();
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
getTimings: () => timings.map((timing) => ({ ...timing })),
|
|
125
|
+
clear: () => {
|
|
126
|
+
timings.length = 0;
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function createTimedAnalyticsTransport(
|
|
132
|
+
transport: Transport,
|
|
133
|
+
record: AmplitudeNetworkTimingRecorder,
|
|
134
|
+
): Transport {
|
|
135
|
+
return {
|
|
136
|
+
async send(
|
|
137
|
+
serverUrl: string,
|
|
138
|
+
payload: Payload,
|
|
139
|
+
enableRequestBodyCompression?: boolean,
|
|
140
|
+
): Promise<Response | null> {
|
|
141
|
+
const startedAt = nowMillis();
|
|
142
|
+
try {
|
|
143
|
+
const response = await transport.send(
|
|
144
|
+
serverUrl,
|
|
145
|
+
payload,
|
|
146
|
+
enableRequestBodyCompression,
|
|
147
|
+
);
|
|
148
|
+
record(
|
|
149
|
+
createTiming(
|
|
150
|
+
"analytics",
|
|
151
|
+
serverUrl,
|
|
152
|
+
"POST",
|
|
153
|
+
startedAt,
|
|
154
|
+
response?.status,
|
|
155
|
+
),
|
|
156
|
+
);
|
|
157
|
+
return response;
|
|
158
|
+
} catch (error) {
|
|
159
|
+
record(
|
|
160
|
+
createTiming(
|
|
161
|
+
"analytics",
|
|
162
|
+
serverUrl,
|
|
163
|
+
"POST",
|
|
164
|
+
startedAt,
|
|
165
|
+
undefined,
|
|
166
|
+
error,
|
|
167
|
+
),
|
|
168
|
+
);
|
|
169
|
+
throw error;
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function createTimedHttpClient(
|
|
176
|
+
httpClient: HttpClient,
|
|
177
|
+
record: AmplitudeNetworkTimingRecorder,
|
|
178
|
+
): HttpClient {
|
|
179
|
+
return {
|
|
180
|
+
async request(
|
|
181
|
+
requestUrl: string,
|
|
182
|
+
method: string,
|
|
183
|
+
headers: Record<string, string>,
|
|
184
|
+
data: string | null,
|
|
185
|
+
timeoutMillis?: number,
|
|
186
|
+
): Promise<SimpleResponse> {
|
|
187
|
+
const startedAt = nowMillis();
|
|
188
|
+
try {
|
|
189
|
+
const response = await httpClient.request(
|
|
190
|
+
requestUrl,
|
|
191
|
+
method,
|
|
192
|
+
headers,
|
|
193
|
+
data,
|
|
194
|
+
timeoutMillis,
|
|
195
|
+
);
|
|
196
|
+
record(
|
|
197
|
+
createTiming(
|
|
198
|
+
"experiment",
|
|
199
|
+
requestUrl,
|
|
200
|
+
method,
|
|
201
|
+
startedAt,
|
|
202
|
+
response.status,
|
|
203
|
+
),
|
|
204
|
+
);
|
|
205
|
+
return response;
|
|
206
|
+
} catch (error) {
|
|
207
|
+
record(
|
|
208
|
+
createTiming(
|
|
209
|
+
"experiment",
|
|
210
|
+
requestUrl,
|
|
211
|
+
method,
|
|
212
|
+
startedAt,
|
|
213
|
+
undefined,
|
|
214
|
+
error,
|
|
215
|
+
),
|
|
216
|
+
);
|
|
217
|
+
throw error;
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export class DryRunHttpClient implements HttpClient {
|
|
224
|
+
async request(
|
|
225
|
+
requestUrl: string,
|
|
226
|
+
method: string,
|
|
227
|
+
headers: Record<string, string>,
|
|
228
|
+
data: string | null,
|
|
229
|
+
timeoutMillis = 10000,
|
|
230
|
+
): Promise<SimpleResponse> {
|
|
231
|
+
dryRunRequests.push({
|
|
232
|
+
url: requestUrl,
|
|
233
|
+
method,
|
|
234
|
+
headers: { ...headers },
|
|
235
|
+
data,
|
|
236
|
+
timeoutMillis,
|
|
237
|
+
createdAt: Date.now(),
|
|
238
|
+
});
|
|
239
|
+
return {
|
|
240
|
+
status: 202,
|
|
241
|
+
body: JSON.stringify({ code: 202, dryRun: true }),
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export class DryRunTransport extends BaseTransport implements Transport {
|
|
247
|
+
async send(serverUrl: string, payload: Payload): Promise<Response | null> {
|
|
248
|
+
dryRunEvents.push({
|
|
249
|
+
serverUrl,
|
|
250
|
+
payload: { ...payload },
|
|
251
|
+
createdAt: Date.now(),
|
|
252
|
+
});
|
|
253
|
+
return this.buildResponse({ code: 202, dryRun: true });
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export const dryRunHttpClient = new DryRunHttpClient();
|
|
258
|
+
export const dryRunTransport = new DryRunTransport();
|
package/src/presets.ts
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Event,
|
|
3
|
+
ReactNativeOptions,
|
|
4
|
+
UserSession,
|
|
5
|
+
} from "@amplitude/analytics-core";
|
|
6
|
+
import { getAnalyticsConnector } from "@amplitude/analytics-core";
|
|
7
|
+
import { createInstance } from "./analytics/react-native-client";
|
|
8
|
+
import type { AmplitudeReactNativeClient } from "./analytics/react-native-client";
|
|
9
|
+
import { NetworkGuardedFetchTransport } from "./analytics/network-guarded-fetch-transport";
|
|
10
|
+
import { isNative } from "./analytics/utils/platform";
|
|
11
|
+
import { Experiment } from "./experiment/factory";
|
|
12
|
+
import type { ExperimentClient } from "./experiment/experimentClient";
|
|
13
|
+
import type { ExperimentConfig } from "./experiment/types/config";
|
|
14
|
+
import type { ExperimentUser } from "./experiment/types/user";
|
|
15
|
+
import { dryRunHttpClient, dryRunTransport } from "./network";
|
|
16
|
+
|
|
17
|
+
function getStorageModule(): typeof import("./native/storage") {
|
|
18
|
+
if (isNative()) {
|
|
19
|
+
return require("./native/storage") as typeof import("./native/storage");
|
|
20
|
+
}
|
|
21
|
+
return require("./native/storage.web") as typeof import("./native/storage");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function getAnalyticsTransport() {
|
|
25
|
+
if (isNative()) {
|
|
26
|
+
const { nitroTransport } =
|
|
27
|
+
require("./analytics/nitro-transport") as typeof import("./analytics/nitro-transport");
|
|
28
|
+
return nitroTransport;
|
|
29
|
+
}
|
|
30
|
+
return new NetworkGuardedFetchTransport();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type DurableAmplitudeStoragePreset = {
|
|
34
|
+
analytics: Pick<
|
|
35
|
+
ReactNativeOptions,
|
|
36
|
+
"storageProvider" | "cookieStorage" | "transportProvider"
|
|
37
|
+
>;
|
|
38
|
+
experiment: Pick<ExperimentConfig, "storage">;
|
|
39
|
+
clear: () => Promise<void>;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export type DurableAmplitudeStoragePresetOptions = {
|
|
43
|
+
namespace?: string;
|
|
44
|
+
dryRun?: boolean;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export type AmplitudeCombinedClientConfig = {
|
|
48
|
+
analyticsApiKey: string;
|
|
49
|
+
experimentDeploymentKey?: string;
|
|
50
|
+
userId?: string;
|
|
51
|
+
instanceName?: string;
|
|
52
|
+
analytics?: ReactNativeOptions;
|
|
53
|
+
experiment?: ExperimentConfig;
|
|
54
|
+
durableStorage?: boolean | DurableAmplitudeStoragePresetOptions;
|
|
55
|
+
dryRun?: boolean;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export type AmplitudeCombinedClient = {
|
|
59
|
+
analytics: AmplitudeReactNativeClient;
|
|
60
|
+
experiment?: ExperimentClient;
|
|
61
|
+
init: (user?: ExperimentUser) => Promise<AmplitudeCombinedClient>;
|
|
62
|
+
flush: () => Promise<void>;
|
|
63
|
+
reset: () => void;
|
|
64
|
+
getUserId: () => string | undefined;
|
|
65
|
+
getDeviceId: () => string | undefined;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export type AmplitudeCombinedClientConfigWithExperiment =
|
|
69
|
+
AmplitudeCombinedClientConfig & {
|
|
70
|
+
experimentDeploymentKey: string;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export type AmplitudeCombinedClientWithExperiment = Omit<
|
|
74
|
+
AmplitudeCombinedClient,
|
|
75
|
+
"experiment" | "init"
|
|
76
|
+
> & {
|
|
77
|
+
experiment: ExperimentClient;
|
|
78
|
+
init: (
|
|
79
|
+
user?: ExperimentUser,
|
|
80
|
+
) => Promise<AmplitudeCombinedClientWithExperiment>;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export function createDurableAmplitudeStoragePreset(
|
|
84
|
+
options: DurableAmplitudeStoragePresetOptions = {},
|
|
85
|
+
): DurableAmplitudeStoragePreset {
|
|
86
|
+
const namespace = options.namespace ?? "default";
|
|
87
|
+
const { NitroAnalyticsStorage, NitroExperimentStorage } = getStorageModule();
|
|
88
|
+
const analyticsEvents = new NitroAnalyticsStorage<Event[]>(
|
|
89
|
+
`${namespace}:analytics-events`,
|
|
90
|
+
);
|
|
91
|
+
const analyticsSession = new NitroAnalyticsStorage<UserSession>(
|
|
92
|
+
`${namespace}:analytics-session`,
|
|
93
|
+
);
|
|
94
|
+
const experimentVariants = new NitroExperimentStorage(
|
|
95
|
+
`${namespace}:experiment-variants`,
|
|
96
|
+
);
|
|
97
|
+
return {
|
|
98
|
+
analytics: {
|
|
99
|
+
storageProvider: analyticsEvents,
|
|
100
|
+
cookieStorage: analyticsSession,
|
|
101
|
+
transportProvider: options.dryRun
|
|
102
|
+
? dryRunTransport
|
|
103
|
+
: getAnalyticsTransport(),
|
|
104
|
+
},
|
|
105
|
+
experiment: {
|
|
106
|
+
storage: experimentVariants,
|
|
107
|
+
},
|
|
108
|
+
clear: async () => {
|
|
109
|
+
await analyticsEvents.reset();
|
|
110
|
+
await analyticsSession.reset();
|
|
111
|
+
await experimentVariants.reset?.();
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function createPersistentAmplitudeConfig(
|
|
117
|
+
namespaceOrOptions?: string | DurableAmplitudeStoragePresetOptions,
|
|
118
|
+
): DurableAmplitudeStoragePreset {
|
|
119
|
+
if (typeof namespaceOrOptions === "string") {
|
|
120
|
+
return createDurableAmplitudeStoragePreset({
|
|
121
|
+
namespace: namespaceOrOptions,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
return createDurableAmplitudeStoragePreset(namespaceOrOptions);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function createExperimentUser(user: ExperimentUser): ExperimentUser {
|
|
128
|
+
return {
|
|
129
|
+
...user,
|
|
130
|
+
user_properties: user.user_properties
|
|
131
|
+
? { ...user.user_properties }
|
|
132
|
+
: undefined,
|
|
133
|
+
groups: user.groups ? { ...user.groups } : undefined,
|
|
134
|
+
group_properties: user.group_properties
|
|
135
|
+
? { ...user.group_properties }
|
|
136
|
+
: undefined,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export function createAmplitudeClient(
|
|
141
|
+
config: AmplitudeCombinedClientConfigWithExperiment,
|
|
142
|
+
): AmplitudeCombinedClientWithExperiment;
|
|
143
|
+
export function createAmplitudeClient(
|
|
144
|
+
config: AmplitudeCombinedClientConfig,
|
|
145
|
+
): AmplitudeCombinedClient;
|
|
146
|
+
export function createAmplitudeClient(config: AmplitudeCombinedClientConfig) {
|
|
147
|
+
const instanceName = config.instanceName ?? "$default_instance";
|
|
148
|
+
const dryRunEnabled =
|
|
149
|
+
config.dryRun ||
|
|
150
|
+
(typeof config.durableStorage === "object" && config.durableStorage.dryRun);
|
|
151
|
+
const storagePreset =
|
|
152
|
+
config.durableStorage === false
|
|
153
|
+
? undefined
|
|
154
|
+
: createDurableAmplitudeStoragePreset({
|
|
155
|
+
namespace:
|
|
156
|
+
typeof config.durableStorage === "object"
|
|
157
|
+
? config.durableStorage.namespace
|
|
158
|
+
: instanceName,
|
|
159
|
+
dryRun: dryRunEnabled,
|
|
160
|
+
});
|
|
161
|
+
const analytics = createInstance();
|
|
162
|
+
const experiment =
|
|
163
|
+
config.experimentDeploymentKey !== undefined
|
|
164
|
+
? Experiment.initializeWithAmplitudeAnalytics(
|
|
165
|
+
config.experimentDeploymentKey,
|
|
166
|
+
{
|
|
167
|
+
...storagePreset?.experiment,
|
|
168
|
+
...config.experiment,
|
|
169
|
+
instanceName,
|
|
170
|
+
httpClient: dryRunEnabled
|
|
171
|
+
? (config.experiment?.httpClient ?? dryRunHttpClient)
|
|
172
|
+
: config.experiment?.httpClient,
|
|
173
|
+
},
|
|
174
|
+
)
|
|
175
|
+
: undefined;
|
|
176
|
+
const combined = {
|
|
177
|
+
analytics,
|
|
178
|
+
experiment,
|
|
179
|
+
init: async (user?: ExperimentUser) => {
|
|
180
|
+
await analytics.init(config.analyticsApiKey, config.userId, {
|
|
181
|
+
...storagePreset?.analytics,
|
|
182
|
+
...config.analytics,
|
|
183
|
+
instanceName,
|
|
184
|
+
transportProvider: dryRunEnabled
|
|
185
|
+
? (config.analytics?.transportProvider ?? dryRunTransport)
|
|
186
|
+
: (config.analytics?.transportProvider ??
|
|
187
|
+
storagePreset?.analytics.transportProvider),
|
|
188
|
+
}).promise;
|
|
189
|
+
if (experiment && user) {
|
|
190
|
+
experiment.setUser(user);
|
|
191
|
+
}
|
|
192
|
+
return combined;
|
|
193
|
+
},
|
|
194
|
+
flush: async () => analytics.flush().promise,
|
|
195
|
+
reset: () => analytics.reset(),
|
|
196
|
+
getUserId: () => analytics.getUserId(),
|
|
197
|
+
getDeviceId: () => analytics.getDeviceId(),
|
|
198
|
+
} as AmplitudeCombinedClient;
|
|
199
|
+
|
|
200
|
+
return combined;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export function getConnectorIdentity(instanceName = "$default_instance"): {
|
|
204
|
+
userId?: string;
|
|
205
|
+
deviceId?: string;
|
|
206
|
+
} {
|
|
207
|
+
return getAnalyticsConnector(instanceName).identityStore.getIdentity();
|
|
208
|
+
}
|