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.
- package/README.md +547 -55
- package/cpp/bindings/HybridAmplitudeWorker.cpp +27 -7
- 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 +155 -9
- 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/transport/http.js +8 -2
- package/lib/commonjs/experiment/transport/http.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 +387 -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.js +23 -8
- package/lib/commonjs/native/http.js.map +1 -1
- package/lib/commonjs/native/http.web.js +17 -0
- package/lib/commonjs/native/http.web.js.map +1 -0
- 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/native/storage.web.js +135 -0
- package/lib/commonjs/native/storage.web.js.map +1 -0
- 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 +154 -9
- 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/transport/http.js +8 -2
- package/lib/module/experiment/transport/http.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 +60 -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.js +23 -8
- package/lib/module/native/http.js.map +1 -1
- package/lib/module/native/http.web.js +12 -0
- package/lib/module/native/http.web.js.map +1 -0
- 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/native/storage.web.js +128 -0
- package/lib/module/native/storage.web.js.map +1 -0
- 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 +46 -6
- 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/transport/http.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 +37 -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.d.ts.map +1 -1
- 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/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/native/storage.web.d.ts +30 -0
- package/lib/typescript/native/storage.web.d.ts.map +1 -0
- 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 +4 -2
- 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 +238 -9
- 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/transport/http.ts +10 -2
- 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 +89 -14
- package/src/native/context.web.ts +38 -0
- package/src/native/http.ts +38 -8
- package/src/native/http.web.ts +24 -0
- package/src/native/hybrid.web.ts +21 -0
- package/src/native/storage.ts +27 -25
- package/src/native/storage.web.ts +152 -0
- 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
|
}
|
|
@@ -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
|
+
}
|
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();
|