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.
Files changed (155) hide show
  1. package/README.md +387 -8
  2. package/cpp/bindings/HybridAmplitudeWorker.cpp +5 -1
  3. package/lib/commonjs/analytics/config.js +31 -10
  4. package/lib/commonjs/analytics/config.js.map +1 -1
  5. package/lib/commonjs/analytics/index.js +8 -2
  6. package/lib/commonjs/analytics/index.js.map +1 -1
  7. package/lib/commonjs/analytics/network-guarded-fetch-transport.js +16 -0
  8. package/lib/commonjs/analytics/network-guarded-fetch-transport.js.map +1 -0
  9. package/lib/commonjs/analytics/nitro-transport.js +2 -0
  10. package/lib/commonjs/analytics/nitro-transport.js.map +1 -1
  11. package/lib/commonjs/analytics/plugins/context.js +7 -1
  12. package/lib/commonjs/analytics/plugins/context.js.map +1 -1
  13. package/lib/commonjs/analytics/react-native-client.js +142 -1
  14. package/lib/commonjs/analytics/react-native-client.js.map +1 -1
  15. package/lib/commonjs/diagnostics.js +92 -0
  16. package/lib/commonjs/diagnostics.js.map +1 -0
  17. package/lib/commonjs/errors.js +48 -0
  18. package/lib/commonjs/errors.js.map +1 -0
  19. package/lib/commonjs/experiment/experimentClient.js +84 -2
  20. package/lib/commonjs/experiment/experimentClient.js.map +1 -1
  21. package/lib/commonjs/experiment/index.js +12 -0
  22. package/lib/commonjs/experiment/index.js.map +1 -1
  23. package/lib/commonjs/experiment/stubClient.js +25 -0
  24. package/lib/commonjs/experiment/stubClient.js.map +1 -1
  25. package/lib/commonjs/experiment/typed-variants.js +52 -0
  26. package/lib/commonjs/experiment/typed-variants.js.map +1 -0
  27. package/lib/commonjs/experiment/types/config.js +32 -4
  28. package/lib/commonjs/experiment/types/config.js.map +1 -1
  29. package/lib/commonjs/index.js +105 -3
  30. package/lib/commonjs/index.js.map +1 -1
  31. package/lib/commonjs/index.web.js +110 -12
  32. package/lib/commonjs/index.web.js.map +1 -1
  33. package/lib/commonjs/native/http.js +23 -8
  34. package/lib/commonjs/native/http.js.map +1 -1
  35. package/lib/commonjs/native/http.web.js +2 -0
  36. package/lib/commonjs/native/http.web.js.map +1 -1
  37. package/lib/commonjs/native/hybrid.web.js +23 -0
  38. package/lib/commonjs/native/hybrid.web.js.map +1 -0
  39. package/lib/commonjs/native/storage.js +27 -15
  40. package/lib/commonjs/native/storage.js.map +1 -1
  41. package/lib/commonjs/network.js +154 -0
  42. package/lib/commonjs/network.js.map +1 -0
  43. package/lib/commonjs/presets.js +118 -0
  44. package/lib/commonjs/presets.js.map +1 -0
  45. package/lib/commonjs/testing.js +166 -0
  46. package/lib/commonjs/testing.js.map +1 -0
  47. package/lib/module/analytics/config.js +32 -11
  48. package/lib/module/analytics/config.js.map +1 -1
  49. package/lib/module/analytics/index.js +4 -1
  50. package/lib/module/analytics/index.js.map +1 -1
  51. package/lib/module/analytics/network-guarded-fetch-transport.js +11 -0
  52. package/lib/module/analytics/network-guarded-fetch-transport.js.map +1 -0
  53. package/lib/module/analytics/nitro-transport.js +2 -0
  54. package/lib/module/analytics/nitro-transport.js.map +1 -1
  55. package/lib/module/analytics/plugins/context.js +7 -1
  56. package/lib/module/analytics/plugins/context.js.map +1 -1
  57. package/lib/module/analytics/react-native-client.js +141 -1
  58. package/lib/module/analytics/react-native-client.js.map +1 -1
  59. package/lib/module/diagnostics.js +85 -0
  60. package/lib/module/diagnostics.js.map +1 -0
  61. package/lib/module/errors.js +41 -0
  62. package/lib/module/errors.js.map +1 -0
  63. package/lib/module/experiment/experimentClient.js +84 -2
  64. package/lib/module/experiment/experimentClient.js.map +1 -1
  65. package/lib/module/experiment/index.js +1 -0
  66. package/lib/module/experiment/index.js.map +1 -1
  67. package/lib/module/experiment/stubClient.js +25 -0
  68. package/lib/module/experiment/stubClient.js.map +1 -1
  69. package/lib/module/experiment/typed-variants.js +43 -0
  70. package/lib/module/experiment/typed-variants.js.map +1 -0
  71. package/lib/module/experiment/types/config.js +31 -4
  72. package/lib/module/experiment/types/config.js.map +1 -1
  73. package/lib/module/index.js +15 -3
  74. package/lib/module/index.js.map +1 -1
  75. package/lib/module/index.web.js +19 -6
  76. package/lib/module/index.web.js.map +1 -1
  77. package/lib/module/native/http.js +23 -8
  78. package/lib/module/native/http.js.map +1 -1
  79. package/lib/module/native/http.web.js +2 -0
  80. package/lib/module/native/http.web.js.map +1 -1
  81. package/lib/module/native/hybrid.web.js +16 -0
  82. package/lib/module/native/hybrid.web.js.map +1 -0
  83. package/lib/module/native/storage.js +27 -15
  84. package/lib/module/native/storage.js.map +1 -1
  85. package/lib/module/network.js +138 -0
  86. package/lib/module/network.js.map +1 -0
  87. package/lib/module/presets.js +110 -0
  88. package/lib/module/presets.js.map +1 -0
  89. package/lib/module/testing.js +160 -0
  90. package/lib/module/testing.js.map +1 -0
  91. package/lib/typescript/analytics/config.d.ts +19 -6
  92. package/lib/typescript/analytics/config.d.ts.map +1 -1
  93. package/lib/typescript/analytics/index.d.ts +1 -1
  94. package/lib/typescript/analytics/index.d.ts.map +1 -1
  95. package/lib/typescript/analytics/network-guarded-fetch-transport.d.ts +6 -0
  96. package/lib/typescript/analytics/network-guarded-fetch-transport.d.ts.map +1 -0
  97. package/lib/typescript/analytics/nitro-transport.d.ts.map +1 -1
  98. package/lib/typescript/analytics/plugins/context.d.ts +2 -0
  99. package/lib/typescript/analytics/plugins/context.d.ts.map +1 -1
  100. package/lib/typescript/analytics/react-native-client.d.ts +43 -0
  101. package/lib/typescript/analytics/react-native-client.d.ts.map +1 -1
  102. package/lib/typescript/diagnostics.d.ts +21 -0
  103. package/lib/typescript/diagnostics.d.ts.map +1 -0
  104. package/lib/typescript/errors.d.ts +9 -0
  105. package/lib/typescript/errors.d.ts.map +1 -0
  106. package/lib/typescript/experiment/experimentClient.d.ts +9 -1
  107. package/lib/typescript/experiment/experimentClient.d.ts.map +1 -1
  108. package/lib/typescript/experiment/index.d.ts +1 -0
  109. package/lib/typescript/experiment/index.d.ts.map +1 -1
  110. package/lib/typescript/experiment/stubClient.d.ts +6 -1
  111. package/lib/typescript/experiment/stubClient.d.ts.map +1 -1
  112. package/lib/typescript/experiment/typed-variants.d.ts +9 -0
  113. package/lib/typescript/experiment/typed-variants.d.ts.map +1 -0
  114. package/lib/typescript/experiment/types/client.d.ts +21 -0
  115. package/lib/typescript/experiment/types/client.d.ts.map +1 -1
  116. package/lib/typescript/experiment/types/config.d.ts.map +1 -1
  117. package/lib/typescript/index.d.ts +12 -3
  118. package/lib/typescript/index.d.ts.map +1 -1
  119. package/lib/typescript/index.web.d.ts +16 -6
  120. package/lib/typescript/index.web.d.ts.map +1 -1
  121. package/lib/typescript/native/http.d.ts.map +1 -1
  122. package/lib/typescript/native/http.web.d.ts.map +1 -1
  123. package/lib/typescript/native/hybrid.web.d.ts +8 -0
  124. package/lib/typescript/native/hybrid.web.d.ts.map +1 -0
  125. package/lib/typescript/native/storage.d.ts.map +1 -1
  126. package/lib/typescript/network.d.ts +50 -0
  127. package/lib/typescript/network.d.ts.map +1 -0
  128. package/lib/typescript/presets.d.ts +50 -0
  129. package/lib/typescript/presets.d.ts.map +1 -0
  130. package/lib/typescript/testing.d.ts +11 -0
  131. package/lib/typescript/testing.d.ts.map +1 -0
  132. package/package.json +1 -1
  133. package/src/analytics/config.ts +33 -8
  134. package/src/analytics/index.ts +3 -0
  135. package/src/analytics/network-guarded-fetch-transport.ts +10 -0
  136. package/src/analytics/nitro-transport.ts +2 -0
  137. package/src/analytics/plugins/context.ts +10 -1
  138. package/src/analytics/react-native-client.ts +217 -0
  139. package/src/diagnostics.ts +119 -0
  140. package/src/errors.ts +60 -0
  141. package/src/experiment/experimentClient.ts +116 -3
  142. package/src/experiment/index.ts +1 -0
  143. package/src/experiment/stubClient.ts +42 -1
  144. package/src/experiment/typed-variants.ts +68 -0
  145. package/src/experiment/types/client.ts +29 -0
  146. package/src/experiment/types/config.ts +29 -5
  147. package/src/index.ts +28 -2
  148. package/src/index.web.ts +33 -5
  149. package/src/native/http.ts +38 -8
  150. package/src/native/http.web.ts +2 -0
  151. package/src/native/hybrid.web.ts +21 -0
  152. package/src/native/storage.ts +27 -25
  153. package/src/network.ts +258 -0
  154. package/src/presets.ts +208 -0
  155. package/src/testing.ts +177 -0
@@ -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
- return getAmplitudeStorage().get(namespaceKey(this.namespace, key), true);
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().set(
34
- namespaceKey(this.namespace, key),
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().remove(namespaceKey(this.namespace, key), true);
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 = getAmplitudeStorage().getKeysByPrefix(prefix, true);
46
+ const keys = storage.getKeysByPrefix(prefix, true);
47
47
  if (keys.length > 0) {
48
- getAmplitudeStorage().removeBatch(keys, true);
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 value = getAmplitudeStorage().get(
58
- namespaceKey(this.namespace, key),
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
- getAmplitudeStorage().set(namespaceKey(this.namespace, key), value, true);
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().remove(namespaceKey(this.namespace, key), true);
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 = getAmplitudeStorage().getKeysByPrefix(prefix, true);
75
+ const keys = storage.getKeysByPrefix(prefix, true);
75
76
  if (keys.length > 0) {
76
- getAmplitudeStorage().removeBatch(keys, true);
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 value = getAmplitudeStorage().get(
86
- namespaceKey(this.namespace, key),
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
- getAmplitudeStorage().set(namespaceKey(this.namespace, key), value, false);
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().remove(namespaceKey(this.namespace, key), false);
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 = getAmplitudeStorage().getKeysByPrefix(prefix, false);
104
+ const keys = storage.getKeysByPrefix(prefix, false);
103
105
  if (keys.length > 0) {
104
- getAmplitudeStorage().removeBatch(keys, false);
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
+ }