react-native-nitro-amplitude 0.1.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 (176) hide show
  1. package/README.md +547 -55
  2. package/cpp/bindings/HybridAmplitudeWorker.cpp +27 -7
  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 +155 -9
  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/transport/http.js +8 -2
  26. package/lib/commonjs/experiment/transport/http.js.map +1 -1
  27. package/lib/commonjs/experiment/typed-variants.js +52 -0
  28. package/lib/commonjs/experiment/typed-variants.js.map +1 -0
  29. package/lib/commonjs/experiment/types/config.js +32 -4
  30. package/lib/commonjs/experiment/types/config.js.map +1 -1
  31. package/lib/commonjs/index.js +105 -3
  32. package/lib/commonjs/index.js.map +1 -1
  33. package/lib/commonjs/index.web.js +387 -13
  34. package/lib/commonjs/index.web.js.map +1 -1
  35. package/lib/commonjs/native/context.web.js +26 -0
  36. package/lib/commonjs/native/context.web.js.map +1 -0
  37. package/lib/commonjs/native/http.js +23 -8
  38. package/lib/commonjs/native/http.js.map +1 -1
  39. package/lib/commonjs/native/http.web.js +17 -0
  40. package/lib/commonjs/native/http.web.js.map +1 -0
  41. package/lib/commonjs/native/hybrid.web.js +23 -0
  42. package/lib/commonjs/native/hybrid.web.js.map +1 -0
  43. package/lib/commonjs/native/storage.js +27 -15
  44. package/lib/commonjs/native/storage.js.map +1 -1
  45. package/lib/commonjs/native/storage.web.js +135 -0
  46. package/lib/commonjs/native/storage.web.js.map +1 -0
  47. package/lib/commonjs/network.js +154 -0
  48. package/lib/commonjs/network.js.map +1 -0
  49. package/lib/commonjs/presets.js +118 -0
  50. package/lib/commonjs/presets.js.map +1 -0
  51. package/lib/commonjs/testing.js +166 -0
  52. package/lib/commonjs/testing.js.map +1 -0
  53. package/lib/module/analytics/config.js +32 -11
  54. package/lib/module/analytics/config.js.map +1 -1
  55. package/lib/module/analytics/index.js +4 -1
  56. package/lib/module/analytics/index.js.map +1 -1
  57. package/lib/module/analytics/network-guarded-fetch-transport.js +11 -0
  58. package/lib/module/analytics/network-guarded-fetch-transport.js.map +1 -0
  59. package/lib/module/analytics/nitro-transport.js +2 -0
  60. package/lib/module/analytics/nitro-transport.js.map +1 -1
  61. package/lib/module/analytics/plugins/context.js +7 -1
  62. package/lib/module/analytics/plugins/context.js.map +1 -1
  63. package/lib/module/analytics/react-native-client.js +154 -9
  64. package/lib/module/analytics/react-native-client.js.map +1 -1
  65. package/lib/module/diagnostics.js +85 -0
  66. package/lib/module/diagnostics.js.map +1 -0
  67. package/lib/module/errors.js +41 -0
  68. package/lib/module/errors.js.map +1 -0
  69. package/lib/module/experiment/experimentClient.js +84 -2
  70. package/lib/module/experiment/experimentClient.js.map +1 -1
  71. package/lib/module/experiment/index.js +1 -0
  72. package/lib/module/experiment/index.js.map +1 -1
  73. package/lib/module/experiment/stubClient.js +25 -0
  74. package/lib/module/experiment/stubClient.js.map +1 -1
  75. package/lib/module/experiment/transport/http.js +8 -2
  76. package/lib/module/experiment/transport/http.js.map +1 -1
  77. package/lib/module/experiment/typed-variants.js +43 -0
  78. package/lib/module/experiment/typed-variants.js.map +1 -0
  79. package/lib/module/experiment/types/config.js +31 -4
  80. package/lib/module/experiment/types/config.js.map +1 -1
  81. package/lib/module/index.js +15 -3
  82. package/lib/module/index.js.map +1 -1
  83. package/lib/module/index.web.js +60 -11
  84. package/lib/module/index.web.js.map +1 -1
  85. package/lib/module/native/context.web.js +18 -0
  86. package/lib/module/native/context.web.js.map +1 -0
  87. package/lib/module/native/http.js +23 -8
  88. package/lib/module/native/http.js.map +1 -1
  89. package/lib/module/native/http.web.js +12 -0
  90. package/lib/module/native/http.web.js.map +1 -0
  91. package/lib/module/native/hybrid.web.js +16 -0
  92. package/lib/module/native/hybrid.web.js.map +1 -0
  93. package/lib/module/native/storage.js +27 -15
  94. package/lib/module/native/storage.js.map +1 -1
  95. package/lib/module/native/storage.web.js +128 -0
  96. package/lib/module/native/storage.web.js.map +1 -0
  97. package/lib/module/network.js +138 -0
  98. package/lib/module/network.js.map +1 -0
  99. package/lib/module/presets.js +110 -0
  100. package/lib/module/presets.js.map +1 -0
  101. package/lib/module/testing.js +160 -0
  102. package/lib/module/testing.js.map +1 -0
  103. package/lib/typescript/analytics/config.d.ts +19 -6
  104. package/lib/typescript/analytics/config.d.ts.map +1 -1
  105. package/lib/typescript/analytics/index.d.ts +1 -1
  106. package/lib/typescript/analytics/index.d.ts.map +1 -1
  107. package/lib/typescript/analytics/network-guarded-fetch-transport.d.ts +6 -0
  108. package/lib/typescript/analytics/network-guarded-fetch-transport.d.ts.map +1 -0
  109. package/lib/typescript/analytics/nitro-transport.d.ts.map +1 -1
  110. package/lib/typescript/analytics/plugins/context.d.ts +2 -0
  111. package/lib/typescript/analytics/plugins/context.d.ts.map +1 -1
  112. package/lib/typescript/analytics/react-native-client.d.ts +46 -6
  113. package/lib/typescript/analytics/react-native-client.d.ts.map +1 -1
  114. package/lib/typescript/diagnostics.d.ts +21 -0
  115. package/lib/typescript/diagnostics.d.ts.map +1 -0
  116. package/lib/typescript/errors.d.ts +9 -0
  117. package/lib/typescript/errors.d.ts.map +1 -0
  118. package/lib/typescript/experiment/experimentClient.d.ts +9 -1
  119. package/lib/typescript/experiment/experimentClient.d.ts.map +1 -1
  120. package/lib/typescript/experiment/index.d.ts +1 -0
  121. package/lib/typescript/experiment/index.d.ts.map +1 -1
  122. package/lib/typescript/experiment/stubClient.d.ts +6 -1
  123. package/lib/typescript/experiment/stubClient.d.ts.map +1 -1
  124. package/lib/typescript/experiment/transport/http.d.ts.map +1 -1
  125. package/lib/typescript/experiment/typed-variants.d.ts +9 -0
  126. package/lib/typescript/experiment/typed-variants.d.ts.map +1 -0
  127. package/lib/typescript/experiment/types/client.d.ts +21 -0
  128. package/lib/typescript/experiment/types/client.d.ts.map +1 -1
  129. package/lib/typescript/experiment/types/config.d.ts.map +1 -1
  130. package/lib/typescript/index.d.ts +12 -3
  131. package/lib/typescript/index.d.ts.map +1 -1
  132. package/lib/typescript/index.web.d.ts +37 -8
  133. package/lib/typescript/index.web.d.ts.map +1 -1
  134. package/lib/typescript/native/context.web.d.ts +8 -0
  135. package/lib/typescript/native/context.web.d.ts.map +1 -0
  136. package/lib/typescript/native/http.d.ts.map +1 -1
  137. package/lib/typescript/native/http.web.d.ts +6 -0
  138. package/lib/typescript/native/http.web.d.ts.map +1 -0
  139. package/lib/typescript/native/hybrid.web.d.ts +8 -0
  140. package/lib/typescript/native/hybrid.web.d.ts.map +1 -0
  141. package/lib/typescript/native/storage.d.ts.map +1 -1
  142. package/lib/typescript/native/storage.web.d.ts +30 -0
  143. package/lib/typescript/native/storage.web.d.ts.map +1 -0
  144. package/lib/typescript/network.d.ts +50 -0
  145. package/lib/typescript/network.d.ts.map +1 -0
  146. package/lib/typescript/presets.d.ts +50 -0
  147. package/lib/typescript/presets.d.ts.map +1 -0
  148. package/lib/typescript/testing.d.ts +11 -0
  149. package/lib/typescript/testing.d.ts.map +1 -0
  150. package/package.json +4 -2
  151. package/src/analytics/config.ts +33 -8
  152. package/src/analytics/index.ts +3 -0
  153. package/src/analytics/network-guarded-fetch-transport.ts +10 -0
  154. package/src/analytics/nitro-transport.ts +2 -0
  155. package/src/analytics/plugins/context.ts +10 -1
  156. package/src/analytics/react-native-client.ts +238 -9
  157. package/src/diagnostics.ts +119 -0
  158. package/src/errors.ts +60 -0
  159. package/src/experiment/experimentClient.ts +116 -3
  160. package/src/experiment/index.ts +1 -0
  161. package/src/experiment/stubClient.ts +42 -1
  162. package/src/experiment/transport/http.ts +10 -2
  163. package/src/experiment/typed-variants.ts +68 -0
  164. package/src/experiment/types/client.ts +29 -0
  165. package/src/experiment/types/config.ts +29 -5
  166. package/src/index.ts +28 -2
  167. package/src/index.web.ts +89 -14
  168. package/src/native/context.web.ts +38 -0
  169. package/src/native/http.ts +38 -8
  170. package/src/native/http.web.ts +24 -0
  171. package/src/native/hybrid.web.ts +21 -0
  172. package/src/native/storage.ts +27 -25
  173. package/src/native/storage.web.ts +152 -0
  174. package/src/network.ts +258 -0
  175. package/src/presets.ts +208 -0
  176. package/src/testing.ts +177 -0
@@ -1,4 +1,9 @@
1
- import { Client, FetchOptions } from "./types/client";
1
+ import {
2
+ Client,
3
+ ExperimentFetchResult,
4
+ ExperimentVariantResult,
5
+ FetchOptions,
6
+ } from "./types/client";
2
7
  import { Defaults } from "./types/config";
3
8
  import { ExperimentUser, ExperimentUserProvider } from "./types/user";
4
9
  import { Variant, Variants } from "./types/variant";
@@ -33,6 +38,20 @@ export class StubExperimentClient implements Client {
33
38
  return this;
34
39
  }
35
40
 
41
+ public async fetchWithMetadata(
42
+ _user?: ExperimentUser,
43
+ _options?: FetchOptions,
44
+ ): Promise<ExperimentFetchResult> {
45
+ return {
46
+ fetched: false,
47
+ flagKeys: [],
48
+ cacheHit: false,
49
+ durationMillis: 0,
50
+ source: "cache",
51
+ failureReason: "stub_client",
52
+ };
53
+ }
54
+
36
55
  public getUserProvider(): ExperimentUserProvider {
37
56
  return {
38
57
  async getUser(): Promise<ExperimentUser> {
@@ -51,11 +70,33 @@ export class StubExperimentClient implements Client {
51
70
  return Defaults.fallbackVariant ?? {};
52
71
  }
53
72
 
73
+ public variantWithMetadata(
74
+ _key: string,
75
+ _fallback?: string | Variant,
76
+ ): ExperimentVariantResult {
77
+ return {
78
+ variant: Defaults.fallbackVariant ?? {},
79
+ fallback: true,
80
+ stale: false,
81
+ reason: "fallback",
82
+ };
83
+ }
84
+
54
85
  public all(): Variants {
55
86
  return {};
56
87
  }
57
88
 
58
89
  public clear(): void {}
59
90
 
91
+ public clearVariants(): void {}
92
+
93
+ public hasCachedVariant(_key: string): boolean {
94
+ return false;
95
+ }
96
+
97
+ public getLastFetchTime(): number | undefined {
98
+ return undefined;
99
+ }
100
+
60
101
  public exposure(_key: string): void {}
61
102
  }
@@ -66,11 +66,19 @@ const _request = (
66
66
  ): Promise<SimpleResponse> => {
67
67
  const abortController = getAbortController();
68
68
  const call = async () => {
69
- const response = await getFetch()(requestUrl, {
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,
@@ -0,0 +1,68 @@
1
+ import type { Client } from "./types/client";
2
+ import type { Variant } from "./types/variant";
3
+
4
+ export function variantString(
5
+ client: Pick<Client, "variant">,
6
+ key: string,
7
+ fallback: string,
8
+ ): string {
9
+ const value = client.variant(key, fallback).value;
10
+ return value ?? fallback;
11
+ }
12
+
13
+ export function variantBoolean(
14
+ client: Pick<Client, "variant">,
15
+ key: string,
16
+ fallback: boolean,
17
+ ): boolean {
18
+ const value = client.variant(key, String(fallback)).value;
19
+ if (value === "true") {
20
+ return true;
21
+ }
22
+ if (value === "false") {
23
+ return false;
24
+ }
25
+ return fallback;
26
+ }
27
+
28
+ export function variantNumber(
29
+ client: Pick<Client, "variant">,
30
+ key: string,
31
+ fallback: number,
32
+ ): number {
33
+ const value = client.variant(key, String(fallback)).value;
34
+ if (value === undefined) {
35
+ return fallback;
36
+ }
37
+ const parsed = Number(value);
38
+ return Number.isFinite(parsed) ? parsed : fallback;
39
+ }
40
+
41
+ export function variantPayload<T>(
42
+ client: Pick<Client, "variant">,
43
+ key: string,
44
+ fallback: T,
45
+ ): T {
46
+ const payload = client.variant(key).payload;
47
+ return payload === undefined ? fallback : (payload as T);
48
+ }
49
+
50
+ export function variantJson<T>(
51
+ client: Pick<Client, "variant">,
52
+ key: string,
53
+ fallback: T,
54
+ ): T {
55
+ const variant = client.variant(key);
56
+ return parseVariantJson(variant, fallback);
57
+ }
58
+
59
+ export function parseVariantJson<T>(variant: Variant, fallback: T): T {
60
+ if (typeof variant.value !== "string") {
61
+ return fallback;
62
+ }
63
+ try {
64
+ return JSON.parse(variant.value) as T;
65
+ } catch {
66
+ return fallback;
67
+ }
68
+ }
@@ -1,5 +1,6 @@
1
1
  import { ExperimentUser, ExperimentUserProvider } from "./user";
2
2
  import { Variant, Variants } from "./variant";
3
+ import { VariantSource } from "./source";
3
4
 
4
5
  /**
5
6
  * Interface for the main client.
@@ -9,10 +10,21 @@ export interface Client {
9
10
  start(user?: ExperimentUser): Promise<void>;
10
11
  stop(): void;
11
12
  fetch(user?: ExperimentUser, options?: FetchOptions): Promise<Client>;
13
+ fetchWithMetadata(
14
+ user?: ExperimentUser,
15
+ options?: FetchOptions,
16
+ ): Promise<ExperimentFetchResult>;
12
17
  fetchOrThrow(user?: ExperimentUser, options?: FetchOptions): Promise<Client>;
13
18
  variant(key: string, fallback?: string | Variant): Variant;
19
+ variantWithMetadata(
20
+ key: string,
21
+ fallback?: string | Variant,
22
+ ): ExperimentVariantResult;
14
23
  all(): Variants;
15
24
  clear(): void;
25
+ clearVariants(): void;
26
+ hasCachedVariant(key: string): boolean;
27
+ getLastFetchTime(): number | undefined;
16
28
  exposure(key: string): void;
17
29
  getUser(): ExperimentUser;
18
30
  setUser(user: ExperimentUser): void;
@@ -36,3 +48,20 @@ export type FetchOptions = {
36
48
  */
37
49
  flagKeys?: string[];
38
50
  };
51
+
52
+ export type ExperimentFetchResult = {
53
+ fetched: boolean;
54
+ flagKeys: string[];
55
+ cacheHit: boolean;
56
+ durationMillis: number;
57
+ source: "network" | "cache";
58
+ failureReason?: string;
59
+ };
60
+
61
+ export type ExperimentVariantResult = {
62
+ variant: Variant;
63
+ source?: VariantSource;
64
+ fallback: boolean;
65
+ stale: boolean;
66
+ reason?: "missing_flag" | "fetch_failure" | "no_assignment" | "fallback";
67
+ };
@@ -1,6 +1,4 @@
1
- import { nitroHttpClient } from "../../native/http";
2
- import { NitroMemoryStorage } from "../../native/storage";
3
-
1
+ import { isNative } from "../util/platform";
4
2
  import { ExposureTrackingProvider } from "./exposure";
5
3
  import { Logger, LogLevel } from "./logger";
6
4
  import { Source } from "./source";
@@ -9,6 +7,28 @@ import { HttpClient } from "./transport";
9
7
  import { ExperimentUserProvider } from "./user";
10
8
  import { Variant, Variants } from "./variant";
11
9
 
10
+ function createDefaultHttpClient(): HttpClient {
11
+ if (isNative()) {
12
+ const { nitroHttpClient } =
13
+ require("../../native/http") as typeof import("../../native/http");
14
+ return nitroHttpClient;
15
+ }
16
+ const { nitroHttpClient } =
17
+ require("../../native/http.web") as typeof import("../../native/http");
18
+ return nitroHttpClient;
19
+ }
20
+
21
+ function createDefaultStorage(): Storage {
22
+ if (isNative()) {
23
+ const { NitroMemoryStorage } =
24
+ require("../../native/storage") as typeof import("../../native/storage");
25
+ return new NitroMemoryStorage("experiment");
26
+ }
27
+ const { NitroMemoryStorage } =
28
+ require("../../native/storage.web") as typeof import("../../native/storage");
29
+ return new NitroMemoryStorage("experiment");
30
+ }
31
+
12
32
  /**
13
33
  * @category Configuration
14
34
  */
@@ -205,6 +225,10 @@ export const Defaults: ExperimentConfig = {
205
225
  automaticFetchOnAmplitudeIdentityChange: false,
206
226
  userProvider: null,
207
227
  exposureTrackingProvider: null,
208
- httpClient: nitroHttpClient,
209
- storage: new NitroMemoryStorage("experiment"),
228
+ get httpClient() {
229
+ return createDefaultHttpClient();
230
+ },
231
+ get storage() {
232
+ return createDefaultStorage();
233
+ },
210
234
  };
package/src/index.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import analyticsClient, {
2
2
  createInstance,
3
3
  } from "./analytics/react-native-client";
4
+ import type { AmplitudeDiagnostics } from "./diagnostics";
5
+ import { getAmplitudeDiagnostics } from "./diagnostics";
4
6
  import { prefetchNativeContext } from "./native/context";
5
7
 
6
8
  import * as AnalyticsTypes from "./analytics/types";
@@ -27,19 +29,42 @@ export const {
27
29
  shutdown,
28
30
  track,
29
31
  extendSession,
32
+ flushWithResult,
33
+ healthCheck,
30
34
  } = analyticsClient;
31
35
 
36
+ export function getDiagnostics(): AmplitudeDiagnostics {
37
+ return getAmplitudeDiagnostics(analyticsClient);
38
+ }
39
+
32
40
  export { Revenue, Identify } from "@amplitude/analytics-core";
33
41
  export {
34
42
  InMemoryStorage,
35
43
  LocalStorage,
36
44
  MemoryStorage,
37
45
  } from "./analytics/storage/local-storage";
38
- export { NitroAnalyticsStorage, NitroMemoryStorage } from "./native/storage";
46
+ export {
47
+ NitroAnalyticsStorage,
48
+ NitroExperimentStorage,
49
+ NitroMemoryStorage,
50
+ } from "./native/storage";
39
51
  export { nitroHttpClient } from "./native/http";
40
52
  export { nitroTransport } from "./analytics/nitro-transport";
41
53
  export { prefetchNativeContext };
42
54
  export { AnalyticsTypes as Types };
55
+ export {
56
+ getAmplitudeDiagnostics,
57
+ getLastNativeError,
58
+ getNativeStartupDiagnostics,
59
+ } from "./diagnostics";
60
+ export type {
61
+ AmplitudeDiagnostics,
62
+ NativeStartupDiagnostics,
63
+ } from "./diagnostics";
64
+ export * from "./errors";
65
+ export * from "./network";
66
+ export * from "./presets";
67
+ export * from "./testing";
43
68
 
44
69
  export * from "./experiment/types/config";
45
70
  export { Experiment } from "./experiment/factory";
@@ -58,9 +83,10 @@ export {
58
83
  LocalStorage as ExperimentLocalStorage,
59
84
  MemoryStorage as ExperimentMemoryStorage,
60
85
  } from "./experiment/storage/local-storage";
86
+ export * from "./experiment/typed-variants";
61
87
 
62
88
  export type { AmplitudeContext } from "./AmplitudeContext.nitro";
63
89
  export type { AmplitudeStorage } from "./AmplitudeStorage.nitro";
64
90
  export type { AmplitudeWorker } from "./AmplitudeWorker.nitro";
65
91
 
66
- export const VERSION = "0.1.0";
92
+ export const VERSION = "0.5.0";
package/src/index.web.ts CHANGED
@@ -1,19 +1,94 @@
1
- const unsupported = (name: string): never => {
2
- throw new Error(
3
- `react-native-nitro-amplitude: ${name} is not supported on web. Use native iOS/Android builds.`,
4
- );
5
- };
1
+ import analyticsClient, {
2
+ createInstance,
3
+ } from "./analytics/react-native-client";
4
+ import type { Transport } from "@amplitude/analytics-core";
5
+ import { NetworkGuardedFetchTransport } from "./analytics/network-guarded-fetch-transport";
6
+ import type { AmplitudeDiagnostics } from "./diagnostics";
7
+ import { getAmplitudeDiagnostics } from "./diagnostics";
8
+ import { prefetchNativeContext } from "./native/context.web";
6
9
 
7
- export const init = () => unsupported("init");
8
- export const track = () => unsupported("track");
9
- export const identify = () => unsupported("identify");
10
- export const createInstance = () => unsupported("createInstance");
11
- export const Experiment = {
12
- initialize: () => unsupported("Experiment.initialize"),
13
- initializeWithAmplitudeAnalytics: () =>
14
- unsupported("Experiment.initializeWithAmplitudeAnalytics"),
15
- };
10
+ import * as AnalyticsTypes from "./analytics/types";
11
+
12
+ export { createInstance };
13
+ export const {
14
+ add,
15
+ flush,
16
+ getDeviceId,
17
+ getSessionId,
18
+ getUserId,
19
+ groupIdentify,
20
+ identify,
21
+ init,
22
+ logEvent,
23
+ remove,
24
+ reset,
25
+ revenue,
26
+ setDeviceId,
27
+ setGroup,
28
+ setOptOut,
29
+ setSessionId,
30
+ setUserId,
31
+ shutdown,
32
+ track,
33
+ extendSession,
34
+ flushWithResult,
35
+ healthCheck,
36
+ } = analyticsClient;
37
+
38
+ export function getDiagnostics(): AmplitudeDiagnostics {
39
+ return getAmplitudeDiagnostics(analyticsClient);
40
+ }
41
+
42
+ export { Revenue, Identify } from "@amplitude/analytics-core";
43
+ export {
44
+ InMemoryStorage,
45
+ LocalStorage,
46
+ MemoryStorage,
47
+ } from "./analytics/storage/local-storage";
48
+ export {
49
+ NitroAnalyticsStorage,
50
+ NitroExperimentStorage,
51
+ NitroMemoryStorage,
52
+ } from "./native/storage.web";
53
+ export { nitroHttpClient } from "./native/http.web";
54
+ export const nitroTransport: Transport = new NetworkGuardedFetchTransport();
55
+ export { prefetchNativeContext };
56
+ export { AnalyticsTypes as Types };
57
+ export {
58
+ getAmplitudeDiagnostics,
59
+ getLastNativeError,
60
+ getNativeStartupDiagnostics,
61
+ } from "./diagnostics";
62
+ export type {
63
+ AmplitudeDiagnostics,
64
+ NativeStartupDiagnostics,
65
+ } from "./diagnostics";
66
+ export * from "./errors";
67
+ export * from "./network";
68
+ export * from "./presets";
69
+ export * from "./testing";
70
+
71
+ export * from "./experiment/types/config";
72
+ export { Experiment } from "./experiment/factory";
73
+ export { StubExperimentClient } from "./experiment/stubClient";
74
+ export { ExperimentClient } from "./experiment/experimentClient";
75
+ export * from "./experiment/types/client";
76
+ export { Source } from "./experiment/types/source";
77
+ export * from "./experiment/types/user";
78
+ export * from "./experiment/types/variant";
79
+ export * from "./experiment/types/exposure";
80
+ export * from "./experiment/types/storage";
81
+ export { LogLevel } from "./experiment/types/logger";
82
+ export type { Logger } from "./experiment/types/logger";
83
+ export { ConsoleLogger } from "./experiment/logger/consoleLogger";
84
+ export {
85
+ LocalStorage as ExperimentLocalStorage,
86
+ MemoryStorage as ExperimentMemoryStorage,
87
+ } from "./experiment/storage/local-storage";
88
+ export * from "./experiment/typed-variants";
16
89
 
17
90
  export type { AmplitudeContext } from "./AmplitudeContext.nitro";
18
91
  export type { AmplitudeStorage } from "./AmplitudeStorage.nitro";
19
92
  export type { AmplitudeWorker } from "./AmplitudeWorker.nitro";
93
+
94
+ export const VERSION = "0.5.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 {}
@@ -1,5 +1,8 @@
1
1
  import type { HttpClient, SimpleResponse } from "../experiment/types/transport";
2
+ import { createAmplitudeError, getAmplitudeErrorCode } from "../errors";
3
+ import { assertNetworkEnabled } from "../network";
2
4
  import { getAmplitudeWorker } from "./hybrid";
5
+ import type { AmplitudeWorker } from "../AmplitudeWorker.nitro";
3
6
 
4
7
  type PendingRequest = {
5
8
  resolve: (response: SimpleResponse) => void;
@@ -9,13 +12,18 @@ type PendingRequest = {
9
12
 
10
13
  const pendingRequests = new Map<string, PendingRequest>();
11
14
  let listenerInstalled = false;
15
+ let listenerWorker: AmplitudeWorker | undefined;
16
+ const DEFAULT_TIMEOUT_MILLIS = 10000;
17
+ const MAX_TIMEOUT_MILLIS = 300000;
12
18
 
13
19
  function ensureListener(): void {
14
- if (listenerInstalled) {
20
+ const worker = getAmplitudeWorker();
21
+ if (listenerInstalled && listenerWorker === worker) {
15
22
  return;
16
23
  }
17
24
  listenerInstalled = true;
18
- getAmplitudeWorker().addOnComplete((requestId, statusCode, body, error) => {
25
+ listenerWorker = worker;
26
+ worker.addOnComplete((requestId, statusCode, body, error) => {
19
27
  const pending = pendingRequests.get(requestId);
20
28
  if (!pending) {
21
29
  return;
@@ -23,7 +31,9 @@ function ensureListener(): void {
23
31
  pendingRequests.delete(requestId);
24
32
  clearTimeout(pending.timeoutId);
25
33
  if (error) {
26
- pending.reject(new Error(error));
34
+ pending.reject(
35
+ createAmplitudeError(getAmplitudeErrorCode(new Error(error)), error),
36
+ );
27
37
  return;
28
38
  }
29
39
  pending.resolve({
@@ -37,22 +47,36 @@ function createRequestId(): string {
37
47
  return `req_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
38
48
  }
39
49
 
50
+ function normalizeTimeoutMillis(timeoutMillis: number): number {
51
+ if (!Number.isFinite(timeoutMillis) || timeoutMillis <= 0) {
52
+ return DEFAULT_TIMEOUT_MILLIS;
53
+ }
54
+ return Math.min(Math.ceil(timeoutMillis), MAX_TIMEOUT_MILLIS);
55
+ }
56
+
40
57
  export class NitroHttpClient implements HttpClient {
41
58
  async request(
42
59
  requestUrl: string,
43
60
  method: string,
44
61
  headers: Record<string, string>,
45
62
  data: string | null,
46
- timeoutMillis = 10000,
63
+ timeoutMillis = DEFAULT_TIMEOUT_MILLIS,
47
64
  ): Promise<SimpleResponse> {
65
+ assertNetworkEnabled();
48
66
  ensureListener();
49
67
  const requestId = createRequestId();
68
+ const normalizedTimeoutMillis = normalizeTimeoutMillis(timeoutMillis);
50
69
  return new Promise<SimpleResponse>((resolve, reject) => {
51
70
  const timeoutId = setTimeout(() => {
52
71
  pendingRequests.delete(requestId);
53
72
  getAmplitudeWorker().cancel(requestId);
54
- reject(new Error(`Request timed out after ${timeoutMillis}ms`));
55
- }, timeoutMillis + 250);
73
+ reject(
74
+ createAmplitudeError(
75
+ "timeout",
76
+ `Request timed out after ${normalizedTimeoutMillis}ms`,
77
+ ),
78
+ );
79
+ }, normalizedTimeoutMillis + 250);
56
80
 
57
81
  pendingRequests.set(requestId, { resolve, reject, timeoutId });
58
82
 
@@ -63,12 +87,18 @@ export class NitroHttpClient implements HttpClient {
63
87
  method,
64
88
  JSON.stringify(headers),
65
89
  data ?? "",
66
- timeoutMillis,
90
+ normalizedTimeoutMillis,
67
91
  );
68
92
  } catch (error) {
69
93
  pendingRequests.delete(requestId);
70
94
  clearTimeout(timeoutId);
71
- reject(error instanceof Error ? error : new Error(String(error)));
95
+ reject(
96
+ createAmplitudeError(
97
+ getAmplitudeErrorCode(error),
98
+ error instanceof Error ? error.message : String(error),
99
+ error,
100
+ ),
101
+ );
72
102
  }
73
103
  });
74
104
  }
@@ -0,0 +1,24 @@
1
+ import { FetchHttpClient } from "../experiment/transport/http";
2
+ import type { HttpClient, SimpleResponse } from "../experiment/types/transport";
3
+ import { assertNetworkEnabled } from "../network";
4
+
5
+ export class NitroHttpClient implements HttpClient {
6
+ request(
7
+ requestUrl: string,
8
+ method: string,
9
+ headers: Record<string, string>,
10
+ data: string | null,
11
+ timeoutMillis = 10000,
12
+ ): Promise<SimpleResponse> {
13
+ assertNetworkEnabled();
14
+ return FetchHttpClient.request(
15
+ requestUrl,
16
+ method,
17
+ headers,
18
+ data ?? "",
19
+ timeoutMillis,
20
+ );
21
+ }
22
+ }
23
+
24
+ export const nitroHttpClient = new NitroHttpClient();
@@ -0,0 +1,21 @@
1
+ import type { AmplitudeContext } from "../AmplitudeContext.nitro";
2
+ import type { AmplitudeStorage } from "../AmplitudeStorage.nitro";
3
+ import type { AmplitudeWorker } from "../AmplitudeWorker.nitro";
4
+
5
+ function unavailable(): never {
6
+ throw new Error("Nitro Amplitude native bindings are not available on web");
7
+ }
8
+
9
+ export function getAmplitudeContext(): AmplitudeContext {
10
+ return unavailable();
11
+ }
12
+
13
+ export function getAmplitudeStorage(): AmplitudeStorage {
14
+ return unavailable();
15
+ }
16
+
17
+ export function getAmplitudeWorker(): AmplitudeWorker {
18
+ return unavailable();
19
+ }
20
+
21
+ export function resetHybridInstancesForTests(): void {}