node-avo-inspector 2.0.0-beta.1 → 2.0.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/CHANGELOG.md +1 -1
- package/README.md +2 -0
- package/dist/AvoBatchQueue.d.ts +73 -0
- package/dist/AvoBatchQueue.js +239 -0
- package/dist/AvoDeduplicator.d.ts +15 -6
- package/dist/AvoDeduplicator.js +46 -24
- package/dist/AvoInspector.d.ts +136 -23
- package/dist/AvoInspector.js +777 -109
- package/dist/AvoInspectorVersion.d.ts +3 -0
- package/dist/AvoInspectorVersion.js +10 -0
- package/dist/AvoLog.d.ts +52 -0
- package/dist/AvoLog.js +246 -0
- package/dist/AvoNetworkCallsHandler.d.ts +55 -8
- package/dist/AvoNetworkCallsHandler.js +266 -92
- package/dist/AvoSchemaParser.d.ts +6 -0
- package/dist/AvoSchemaParser.js +138 -33
- package/dist/AvoStreamId.d.ts +2 -1
- package/dist/AvoStreamId.js +21 -2
- package/dist/eventSpec/AvoEventSpecCache.d.ts +28 -5
- package/dist/eventSpec/AvoEventSpecCache.js +66 -11
- package/dist/eventSpec/AvoEventSpecFetchTypes.d.ts +50 -18
- package/dist/eventSpec/AvoEventSpecFetcher.d.ts +29 -6
- package/dist/eventSpec/AvoEventSpecFetcher.js +279 -69
- package/dist/eventSpec/EventValidator.d.ts +16 -10
- package/dist/eventSpec/EventValidator.js +351 -38
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -1
- package/dist/utils.d.ts +29 -1
- package/dist/utils.js +86 -10
- package/package.json +2 -1
|
@@ -4,103 +4,242 @@ exports.AvoNetworkCallsHandler = void 0;
|
|
|
4
4
|
const AvoGuid_1 = require("./AvoGuid");
|
|
5
5
|
const AvoInspector_1 = require("./AvoInspector");
|
|
6
6
|
const AvoEncryption_1 = require("./AvoEncryption");
|
|
7
|
+
const AvoLog_1 = require("./AvoLog");
|
|
8
|
+
const AvoInspectorVersion_1 = require("./AvoInspectorVersion");
|
|
9
|
+
const utils_1 = require("./utils");
|
|
7
10
|
const https_1 = require("https");
|
|
11
|
+
const http_1 = require("http");
|
|
12
|
+
const zlib_1 = require("zlib");
|
|
8
13
|
class AvoNetworkCallsHandler {
|
|
9
14
|
constructor(apiKey, envName, appName, appVersion, libVersion, publicEncryptionKey) {
|
|
10
15
|
this.samplingRate = 1.0;
|
|
16
|
+
this.inFlightRequests = new Set();
|
|
17
|
+
this.aborted = false;
|
|
11
18
|
this.apiKey = apiKey;
|
|
12
19
|
this.envName = envName;
|
|
13
20
|
this.appName = appName;
|
|
14
21
|
this.appVersion = appVersion;
|
|
15
22
|
this.libVersion = libVersion;
|
|
16
23
|
this.publicEncryptionKey = publicEncryptionKey;
|
|
24
|
+
this.mockEndpoint = AvoNetworkCallsHandler.mockEndpointFor(envName);
|
|
17
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* The test-only endpoint override. Fail-closed: a prod instance never honors it,
|
|
28
|
+
* whatever the surrounding process environment says. A value that is not an http(s)
|
|
29
|
+
* URL is ignored (with a one-time warning), so callers only ever see a valid URL.
|
|
30
|
+
*/
|
|
31
|
+
static mockEndpointFor(envName) {
|
|
32
|
+
if (envName === "prod") {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
const override = process.env[AvoNetworkCallsHandler.mockEndpointEnvVar];
|
|
36
|
+
if (!override) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
// The warnings name the reason only, never the value: it may carry credentials or tokens.
|
|
40
|
+
// The value is used solely as the key that keeps each warning to one per value.
|
|
41
|
+
let url;
|
|
42
|
+
try {
|
|
43
|
+
url = new URL(override);
|
|
44
|
+
}
|
|
45
|
+
catch (e) {
|
|
46
|
+
AvoNetworkCallsHandler.warnOnce("invalid:" + override, "[Avo Inspector] Ignoring invalid AVO_INSPECTOR_MOCK_ENDPOINT: not a valid URL");
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
50
|
+
AvoNetworkCallsHandler.warnOnce("invalid:" + override, "[Avo Inspector] Ignoring invalid AVO_INSPECTOR_MOCK_ENDPOINT: unsupported protocol " + url.protocol);
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
return override;
|
|
54
|
+
}
|
|
55
|
+
// Printed on the first send redirected to the override.
|
|
56
|
+
static warnRedirect(url) {
|
|
57
|
+
// Scheme, host and port only: the path or query may carry sensitive values.
|
|
58
|
+
AvoNetworkCallsHandler.warnOnce("redirect", "[Avo Inspector] AVO_INSPECTOR_MOCK_ENDPOINT is set: sending to " + url.protocol + "//" +
|
|
59
|
+
url.host + " instead of api.avo.app (ignored in prod).");
|
|
60
|
+
}
|
|
61
|
+
static warnOnce(key, message) {
|
|
62
|
+
if (AvoNetworkCallsHandler.warnedMockEndpoint === null) {
|
|
63
|
+
AvoNetworkCallsHandler.warnedMockEndpoint = new Set();
|
|
64
|
+
}
|
|
65
|
+
if (!AvoNetworkCallsHandler.warnedMockEndpoint.has(key)) {
|
|
66
|
+
AvoNetworkCallsHandler.warnedMockEndpoint.add(key);
|
|
67
|
+
console.warn(message);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/** The sampling rate last returned by the Inspector API (1 until a response sets it). */
|
|
71
|
+
getSamplingRate() {
|
|
72
|
+
return this.samplingRate;
|
|
73
|
+
}
|
|
74
|
+
/** @internal Test-only hook used by the conformance harness. */
|
|
75
|
+
_setSamplingRateForTesting(samplingRate) {
|
|
76
|
+
this.samplingRate = samplingRate;
|
|
77
|
+
}
|
|
78
|
+
/** Aborts every request still in flight (used by AvoInspector.destroy). */
|
|
79
|
+
abortInFlight() {
|
|
80
|
+
// Also stops sends still being compressed from starting a request afterwards.
|
|
81
|
+
this.aborted = true;
|
|
82
|
+
const requests = Array.from(this.inFlightRequests);
|
|
83
|
+
this.inFlightRequests.clear();
|
|
84
|
+
requests.forEach((req) => req.destroy());
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* POSTs one batch. Resolves with the HTTP status code (the batch is never retried);
|
|
88
|
+
* rejects with "Request failed" / "Request timed out" on transport failures, or when
|
|
89
|
+
* a header value cannot be transmitted safely.
|
|
90
|
+
*/
|
|
18
91
|
callInspectorWithBatchBody(inEvents) {
|
|
19
92
|
const events = inEvents.filter((x) => x != null);
|
|
20
93
|
if (events.length === 0) {
|
|
21
94
|
return Promise.resolve();
|
|
22
95
|
}
|
|
23
|
-
if (Math.random() > this.samplingRate) {
|
|
24
|
-
if (AvoInspector_1.AvoInspector.shouldLog) {
|
|
25
|
-
console.log("Avo Inspector: last event schema dropped due to sampling rate.");
|
|
26
|
-
}
|
|
27
|
-
return Promise.resolve();
|
|
28
|
-
}
|
|
29
96
|
if (AvoInspector_1.AvoInspector.shouldLog) {
|
|
30
97
|
events.forEach(function (event) {
|
|
31
|
-
const eventProps = event.eventProperties
|
|
32
|
-
.map(p => '\t"' + p.propertyName + '": "' + p.propertyType + '"')
|
|
33
|
-
.join(";\n");
|
|
34
98
|
const validated = event.eventSpecMetadata ? " (validated)" : "";
|
|
35
99
|
console.log("Avo Inspector: Sending event " +
|
|
36
100
|
event.eventName + validated +
|
|
37
|
-
" with schema
|
|
101
|
+
" with schema " + (0, utils_1.formatSchema)(event.eventProperties));
|
|
38
102
|
});
|
|
39
103
|
}
|
|
104
|
+
const json = Buffer.from(JSON.stringify(events), "utf8");
|
|
105
|
+
// Compressed off the event loop; on a compression error the body is sent as-is.
|
|
106
|
+
const body = json.length >= AvoNetworkCallsHandler.gzipThresholdBytes
|
|
107
|
+
? AvoNetworkCallsHandler.gzipAsync(json).then((data) => ({ data, compressed: true }), () => ({ data: json, compressed: false }))
|
|
108
|
+
: Promise.resolve({ data: json, compressed: false });
|
|
109
|
+
return body.then(({ data, compressed }) => this.post(data, compressed));
|
|
110
|
+
}
|
|
111
|
+
static gzipAsync(input) {
|
|
40
112
|
return new Promise((resolve, reject) => {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
113
|
+
(0, zlib_1.gzip)(input, (err, output) => (err ? reject(err) : resolve(output)));
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
post(data, compressed) {
|
|
117
|
+
return new Promise((resolve, reject) => {
|
|
118
|
+
if (this.aborted) {
|
|
119
|
+
reject("Request failed");
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const headers = {
|
|
123
|
+
"api-key": this.apiKey,
|
|
124
|
+
"env": this.envName,
|
|
125
|
+
"X-Avo-Client": AvoInspectorVersion_1.LIB_PLATFORM,
|
|
126
|
+
"Accept": "application/json",
|
|
127
|
+
"Content-Type": "application/json",
|
|
128
|
+
"Content-Length": data.length,
|
|
52
129
|
};
|
|
130
|
+
if (compressed) {
|
|
131
|
+
headers["Content-Encoding"] = "gzip";
|
|
132
|
+
}
|
|
133
|
+
for (const name of Object.keys(headers)) {
|
|
134
|
+
const value = headers[name];
|
|
135
|
+
if (typeof value === "string" && ((0, utils_1.hasHeaderControlChar)(value) || (0, utils_1.hasNonLatin1Char)(value))) {
|
|
136
|
+
if (AvoInspector_1.AvoInspector.shouldLog) {
|
|
137
|
+
console.error("Avo Inspector: [network] Header " + name +
|
|
138
|
+
" contains a character that cannot be sent in a header. Batch dropped.");
|
|
139
|
+
}
|
|
140
|
+
reject("Request failed");
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
const url = new URL(this.mockEndpoint || AvoNetworkCallsHandler.trackingEndpoint);
|
|
145
|
+
if (this.mockEndpoint) {
|
|
146
|
+
AvoNetworkCallsHandler.warnRedirect(url);
|
|
147
|
+
}
|
|
148
|
+
const send = url.protocol === "http:" ? http_1.request : https_1.request;
|
|
53
149
|
if (AvoInspector_1.AvoInspector.shouldLog) {
|
|
54
|
-
|
|
55
|
-
console.log("Avo Inspector: [network]
|
|
150
|
+
// An override's path may carry a token, so only its origin is printed.
|
|
151
|
+
console.log("Avo Inspector: [network] POST " + url.origin + (this.mockEndpoint ? "" : url.pathname));
|
|
152
|
+
console.log("Avo Inspector: [network] Request body (" + data.length + " bytes" +
|
|
153
|
+
(compressed ? ", gzip" : "") + ")");
|
|
56
154
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
155
|
+
let settled = false;
|
|
156
|
+
const finish = (fn) => {
|
|
157
|
+
if (settled) {
|
|
158
|
+
return;
|
|
60
159
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
160
|
+
settled = true;
|
|
161
|
+
clearTimeout(timer);
|
|
162
|
+
this.inFlightRequests.delete(req);
|
|
163
|
+
fn();
|
|
164
|
+
};
|
|
165
|
+
let req;
|
|
166
|
+
try {
|
|
167
|
+
req = send(url, { method: "POST", headers }, (res) => {
|
|
168
|
+
if (AvoInspector_1.AvoInspector.shouldLog) {
|
|
169
|
+
console.log("Avo Inspector: [network] Response status: " + res.statusCode + " " + res.statusMessage);
|
|
170
|
+
}
|
|
171
|
+
const chunks = [];
|
|
172
|
+
res.on("data", (chunk) => chunks.push(chunk));
|
|
173
|
+
res.on("end", () => {
|
|
174
|
+
if (res.statusCode === 200) {
|
|
175
|
+
try {
|
|
176
|
+
const responseBody = Buffer.concat(chunks).toString();
|
|
177
|
+
const body = JSON.parse(responseBody);
|
|
178
|
+
if (body && typeof body.samplingRate === "number" && body.samplingRate >= 0 && body.samplingRate <= 1) {
|
|
179
|
+
this.samplingRate = body.samplingRate;
|
|
180
|
+
}
|
|
70
181
|
}
|
|
182
|
+
catch (e) {
|
|
183
|
+
if (AvoInspector_1.AvoInspector.shouldLog) {
|
|
184
|
+
console.warn("Avo Inspector: [network] Failed to parse response JSON: " + e);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
else if (AvoInspector_1.AvoInspector.shouldLog) {
|
|
189
|
+
console.warn("Avo Inspector: [network] Non-200 response: " + res.statusCode);
|
|
71
190
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
191
|
+
finish(() => resolve(res.statusCode));
|
|
192
|
+
});
|
|
193
|
+
// A response cut off mid-body emits neither "end" nor a request "error". Its status
|
|
194
|
+
// already arrived and decides, as for an unparseable body: a 200 is delivered (the
|
|
195
|
+
// sampling rate is unchanged), anything else a non-200.
|
|
196
|
+
const truncated = () => {
|
|
197
|
+
if (!res.complete) {
|
|
198
|
+
if (AvoInspector_1.AvoInspector.shouldLog && !settled) {
|
|
199
|
+
console.warn("Avo Inspector: [network] Response ended before its body was complete");
|
|
75
200
|
}
|
|
201
|
+
finish(() => resolve(res.statusCode));
|
|
76
202
|
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
resolve();
|
|
203
|
+
};
|
|
204
|
+
res.on("aborted", truncated);
|
|
205
|
+
res.on("error", truncated);
|
|
206
|
+
res.on("close", truncated);
|
|
82
207
|
});
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
208
|
+
}
|
|
209
|
+
catch (e) {
|
|
210
|
+
// A synchronous throw (for example invalid request options) is a failed send, with
|
|
211
|
+
// the documented reason; nothing was started, so there is nothing to clean up.
|
|
87
212
|
if (AvoInspector_1.AvoInspector.shouldLog) {
|
|
88
|
-
console.error("Avo Inspector: [network] Request
|
|
213
|
+
console.error("Avo Inspector: [network] Request could not be started");
|
|
89
214
|
}
|
|
90
215
|
reject("Request failed");
|
|
91
|
-
|
|
92
|
-
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
this.inFlightRequests.add(req);
|
|
219
|
+
// An in-flight send must not hold the process open: at exit the beforeExit drain
|
|
220
|
+
// keeps it alive, under one deadline, while it sends what is left.
|
|
221
|
+
req.on("socket", (socket) => socket.unref());
|
|
222
|
+
// A wall-clock budget for the whole request, not just socket idleness.
|
|
223
|
+
const timer = setTimeout(() => {
|
|
93
224
|
if (AvoInspector_1.AvoInspector.shouldLog) {
|
|
94
225
|
console.error("Avo Inspector: [network] Request timed out after 10s");
|
|
95
226
|
}
|
|
227
|
+
finish(() => reject("Request timed out"));
|
|
96
228
|
req.destroy();
|
|
97
|
-
|
|
229
|
+
}, AvoNetworkCallsHandler.requestTimeoutMs);
|
|
230
|
+
timer.unref();
|
|
231
|
+
req.on("error", (err) => {
|
|
232
|
+
if (AvoInspector_1.AvoInspector.shouldLog && !settled) {
|
|
233
|
+
console.error("Avo Inspector: [network] Request error: " + err);
|
|
234
|
+
}
|
|
235
|
+
finish(() => reject("Request failed"));
|
|
98
236
|
});
|
|
99
|
-
req.end();
|
|
237
|
+
req.end(data);
|
|
100
238
|
});
|
|
101
239
|
}
|
|
102
|
-
|
|
103
|
-
|
|
240
|
+
/** Builds the wire body for one event, encrypting property values when configured. */
|
|
241
|
+
bodyForEventSchemaCall(anonymousId, eventName, eventProperties, eventId, eventHash, rawEventProperties, gatewayFields, stamp) {
|
|
242
|
+
let eventSchemaBody = this.createBaseCallBody(anonymousId, gatewayFields, stamp);
|
|
104
243
|
eventSchemaBody.type = "event";
|
|
105
244
|
eventSchemaBody.eventName = eventName;
|
|
106
245
|
if (AvoEncryption_1.AvoEncryption.shouldEncrypt(this.envName, this.publicEncryptionKey) && rawEventProperties) {
|
|
@@ -109,51 +248,65 @@ class AvoNetworkCallsHandler {
|
|
|
109
248
|
else {
|
|
110
249
|
eventSchemaBody.eventProperties = eventProperties;
|
|
111
250
|
}
|
|
112
|
-
|
|
113
|
-
eventSchemaBody.avoFunction = true;
|
|
114
|
-
eventSchemaBody.eventId = eventId;
|
|
115
|
-
eventSchemaBody.eventHash = eventHash;
|
|
116
|
-
}
|
|
117
|
-
else {
|
|
118
|
-
eventSchemaBody.avoFunction = false;
|
|
119
|
-
eventSchemaBody.eventId = null;
|
|
120
|
-
eventSchemaBody.eventHash = null;
|
|
121
|
-
}
|
|
251
|
+
AvoNetworkCallsHandler.applyAvoFunctionFields(eventSchemaBody, eventId, eventHash);
|
|
122
252
|
return eventSchemaBody;
|
|
123
253
|
}
|
|
254
|
+
/** Returns the event properties, with values encrypted when encryption applies. */
|
|
124
255
|
buildEventProperties(eventProperties, rawEventProperties) {
|
|
125
256
|
if (AvoEncryption_1.AvoEncryption.shouldEncrypt(this.envName, this.publicEncryptionKey) && rawEventProperties) {
|
|
126
257
|
return this.encryptProperties(eventProperties, rawEventProperties);
|
|
127
258
|
}
|
|
128
259
|
return eventProperties;
|
|
129
260
|
}
|
|
130
|
-
|
|
131
|
-
|
|
261
|
+
/** Builds the wire body for one event, including its event-spec validation results. */
|
|
262
|
+
bodyForValidatedEventSchemaCall(anonymousId, eventName, eventProperties, eventId, eventHash, eventSpecMetadata, propertyResults, gatewayFields, stamp) {
|
|
132
263
|
const validationMap = new Map();
|
|
133
264
|
for (const result of propertyResults) {
|
|
134
265
|
validationMap.set(result.propertyName, result);
|
|
135
266
|
}
|
|
136
267
|
// Merge validation results into eventProperties (matching Android)
|
|
137
|
-
const mergedProperties = eventProperties.map((prop) =>
|
|
138
|
-
|
|
139
|
-
if (validation) {
|
|
140
|
-
const merged = Object.assign({}, prop);
|
|
141
|
-
if (validation.failedEventIds.length > 0) {
|
|
142
|
-
merged.failedEventIds = validation.failedEventIds;
|
|
143
|
-
}
|
|
144
|
-
if (validation.passedEventIds.length > 0) {
|
|
145
|
-
merged.passedEventIds = validation.passedEventIds;
|
|
146
|
-
}
|
|
147
|
-
return merged;
|
|
148
|
-
}
|
|
149
|
-
return prop;
|
|
150
|
-
});
|
|
151
|
-
let body = this.createBaseCallBody(anonymousId);
|
|
268
|
+
const mergedProperties = eventProperties.map((prop) => AvoNetworkCallsHandler.withValidation(prop, validationMap.get(prop.propertyName)));
|
|
269
|
+
let body = this.createBaseCallBody(anonymousId, gatewayFields, stamp);
|
|
152
270
|
body.type = "event";
|
|
153
271
|
body.eventName = eventName;
|
|
154
272
|
body.eventProperties = mergedProperties;
|
|
155
|
-
body.streamId = anonymousId;
|
|
156
273
|
body.eventSpecMetadata = eventSpecMetadata;
|
|
274
|
+
AvoNetworkCallsHandler.applyAvoFunctionFields(body, eventId, eventHash);
|
|
275
|
+
return body;
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* A property with its failed event IDs, and those of its object properties merged into its
|
|
279
|
+
* children. A list's children are its element schemas; each object element gets the
|
|
280
|
+
* results of the properties it has (a list of objects is validated across all its items).
|
|
281
|
+
*/
|
|
282
|
+
static withValidation(prop, outcome) {
|
|
283
|
+
if (outcome === undefined) {
|
|
284
|
+
return prop;
|
|
285
|
+
}
|
|
286
|
+
const merged = Object.assign({}, prop);
|
|
287
|
+
if (outcome.failedEventIds.length > 0) {
|
|
288
|
+
merged.failedEventIds = outcome.failedEventIds;
|
|
289
|
+
}
|
|
290
|
+
const childOutcomes = outcome.children;
|
|
291
|
+
if (childOutcomes !== undefined && Array.isArray(prop.children)) {
|
|
292
|
+
const mergeChild = (child) => {
|
|
293
|
+
if (Array.isArray(child)) {
|
|
294
|
+
return child.map(mergeChild);
|
|
295
|
+
}
|
|
296
|
+
if (child !== null && typeof child === "object" && typeof child.propertyName === "string") {
|
|
297
|
+
const childOutcome = Object.prototype.hasOwnProperty.call(childOutcomes, child.propertyName)
|
|
298
|
+
? childOutcomes[child.propertyName]
|
|
299
|
+
: undefined;
|
|
300
|
+
return AvoNetworkCallsHandler.withValidation(child, childOutcome);
|
|
301
|
+
}
|
|
302
|
+
return child;
|
|
303
|
+
};
|
|
304
|
+
merged.children = prop.children.map(mergeChild);
|
|
305
|
+
}
|
|
306
|
+
return merged;
|
|
307
|
+
}
|
|
308
|
+
// An event with an eventId came from an Avo function (Codegen): it carries the id and hash.
|
|
309
|
+
static applyAvoFunctionFields(body, eventId, eventHash) {
|
|
157
310
|
if (eventId != null) {
|
|
158
311
|
body.avoFunction = true;
|
|
159
312
|
body.eventId = eventId;
|
|
@@ -164,7 +317,6 @@ class AvoNetworkCallsHandler {
|
|
|
164
317
|
body.eventId = null;
|
|
165
318
|
body.eventHash = null;
|
|
166
319
|
}
|
|
167
|
-
return body;
|
|
168
320
|
}
|
|
169
321
|
encryptProperties(properties, rawEventProperties) {
|
|
170
322
|
var _a;
|
|
@@ -175,7 +327,16 @@ class AvoNetworkCallsHandler {
|
|
|
175
327
|
continue;
|
|
176
328
|
}
|
|
177
329
|
const rawValue = rawEventProperties[prop.propertyName];
|
|
178
|
-
|
|
330
|
+
let jsonValue;
|
|
331
|
+
try {
|
|
332
|
+
jsonValue = (_a = JSON.stringify(rawValue)) !== null && _a !== void 0 ? _a : "null";
|
|
333
|
+
}
|
|
334
|
+
catch (e) {
|
|
335
|
+
// A value that cannot be serialized (e.g. cyclic) is omitted, like an encryption failure.
|
|
336
|
+
// Only the error's type is printed: toJSON, getters and proxies can put the value in it.
|
|
337
|
+
console.warn(`[Avo Inspector] Warning: could not serialize property "${prop.propertyName}" for encryption, omitting it. (${AvoLog_1.AvoLog.errorType(e)})`);
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
179
340
|
const encrypted = AvoEncryption_1.AvoEncryption.encryptValue(jsonValue, this.publicEncryptionKey);
|
|
180
341
|
if (encrypted === null) {
|
|
181
342
|
// Encryption failure: omit the property (warning already logged by encryptValue)
|
|
@@ -185,21 +346,28 @@ class AvoNetworkCallsHandler {
|
|
|
185
346
|
}
|
|
186
347
|
return result;
|
|
187
348
|
}
|
|
188
|
-
|
|
349
|
+
// `stamp` defaults to now and the current sampling rate.
|
|
350
|
+
createBaseCallBody(anonymousId, gatewayFields, stamp) {
|
|
189
351
|
const body = {
|
|
190
352
|
apiKey: this.apiKey,
|
|
191
353
|
appName: this.appName,
|
|
192
|
-
appVersion: this.appVersion,
|
|
354
|
+
appVersion: gatewayFields ? gatewayFields.appVersion : this.appVersion,
|
|
193
355
|
libVersion: this.libVersion,
|
|
194
356
|
env: this.envName,
|
|
195
|
-
libPlatform:
|
|
357
|
+
libPlatform: AvoInspectorVersion_1.LIB_PLATFORM,
|
|
196
358
|
messageId: AvoGuid_1.AvoGuid.newGuid(),
|
|
197
|
-
|
|
198
|
-
sessionId: "",
|
|
359
|
+
streamId: anonymousId,
|
|
199
360
|
anonymousId: anonymousId,
|
|
200
|
-
createdAt: new Date().toISOString(),
|
|
201
|
-
samplingRate: this.samplingRate,
|
|
361
|
+
createdAt: stamp ? stamp.createdAt : new Date().toISOString(),
|
|
362
|
+
samplingRate: stamp ? stamp.samplingRate : this.samplingRate,
|
|
202
363
|
};
|
|
364
|
+
// Gateway coordinates are sent only when present; never as null or "".
|
|
365
|
+
if (gatewayFields && gatewayFields.outputReference !== undefined) {
|
|
366
|
+
body.outputReference = gatewayFields.outputReference;
|
|
367
|
+
}
|
|
368
|
+
if (gatewayFields && gatewayFields.originHint !== undefined) {
|
|
369
|
+
body.originHint = gatewayFields.originHint;
|
|
370
|
+
}
|
|
203
371
|
if (this.publicEncryptionKey && this.publicEncryptionKey.length > 0) {
|
|
204
372
|
body.publicEncryptionKey = this.publicEncryptionKey;
|
|
205
373
|
}
|
|
@@ -207,4 +375,10 @@ class AvoNetworkCallsHandler {
|
|
|
207
375
|
}
|
|
208
376
|
}
|
|
209
377
|
exports.AvoNetworkCallsHandler = AvoNetworkCallsHandler;
|
|
210
|
-
AvoNetworkCallsHandler.trackingEndpoint = "/inspector/
|
|
378
|
+
AvoNetworkCallsHandler.trackingEndpoint = "https://api.avo.app/inspector/v2/track";
|
|
379
|
+
AvoNetworkCallsHandler.mockEndpointEnvVar = "AVO_INSPECTOR_MOCK_ENDPOINT";
|
|
380
|
+
AvoNetworkCallsHandler.requestTimeoutMs = 10000;
|
|
381
|
+
AvoNetworkCallsHandler.gzipThresholdBytes = 1024;
|
|
382
|
+
// Warnings about the override print once per process, whatever the logging flag, on
|
|
383
|
+
// stderr (console.warn).
|
|
384
|
+
AvoNetworkCallsHandler.warnedMockEndpoint = null;
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
export declare class AvoSchemaParser {
|
|
2
|
+
/**
|
|
3
|
+
* Maps each property to its name, type and (for objects and lists) child schema.
|
|
4
|
+
* Values nested deeper than MAX_DEPTH, cyclic references, and complex values past the
|
|
5
|
+
* MAX_EXPANSIONS budget are reported as "object".
|
|
6
|
+
*/
|
|
2
7
|
static extractSchema(eventProperties: {
|
|
3
8
|
[propName: string]: any;
|
|
4
9
|
}): Array<{
|
|
@@ -7,6 +12,7 @@ export declare class AvoSchemaParser {
|
|
|
7
12
|
children?: any;
|
|
8
13
|
}>;
|
|
9
14
|
private static removeDuplicates;
|
|
15
|
+
private static childKey;
|
|
10
16
|
private static getBasicPropType;
|
|
11
17
|
private static getPropValueType;
|
|
12
18
|
}
|