avo-inspector 3.2.0 → 3.2.3
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 +10 -0
- package/dist/AvoBatcher.d.ts +9 -3
- package/dist/AvoInspector.d.ts +4 -0
- package/dist/AvoNetworkCallsHandler.d.ts +80 -5
- package/dist/__tests__/UnboundPublicMethods_test.d.ts +1 -0
- package/dist/__tests__/constants.d.ts +2 -1
- package/dist/__tests__/helpers/internalGateway.d.ts +23 -0
- package/dist/eventSpec/AvoEventSpecCache.d.ts +7 -4
- package/dist/eventSpec/AvoEventSpecFetchTypes.d.ts +12 -1
- package/dist/eventSpec/AvoEventSpecFetcher.d.ts +6 -1
- package/dist/index.js +1 -1
- package/dist/lite/AvoBatcherLite.d.ts +9 -3
- package/dist/lite/AvoBatcherLite.js +46 -7
- package/dist/lite/AvoInspectorLite.d.ts +2 -0
- package/dist/lite/AvoInspectorLite.js +95 -15
- package/dist/lite/AvoNetworkCallsHandlerLite.d.ts +79 -5
- package/dist/lite/AvoNetworkCallsHandlerLite.js +300 -20
- package/package.json +10 -1
- package/dist/AvoBatcher.js +0 -87
- package/dist/AvoDeduplicator.js +0 -142
- package/dist/AvoEncryption.js +0 -176
- package/dist/AvoInspector.js +0 -435
- package/dist/AvoNetworkCallsHandler.js +0 -229
- package/dist/AvoSchemaParser.js +0 -95
- package/dist/AvoStreamId.js +0 -82
- package/dist/__mocks__/xhr.js +0 -14
- package/dist/__mocks__/xhrError.js +0 -14
- package/dist/__tests__/AvoEncryption.js +0 -272
- package/dist/__tests__/AvoEncryptionIntegration.js +0 -191
- package/dist/__tests__/AvoEventSpecFetcher.js +0 -377
- package/dist/__tests__/AvoInspector.js +0 -335
- package/dist/__tests__/Batcher.js +0 -214
- package/dist/__tests__/BatcherPublic.js +0 -139
- package/dist/__tests__/Deduplicator.js +0 -190
- package/dist/__tests__/EventSpecCache.js +0 -143
- package/dist/__tests__/EventValidator.js +0 -543
- package/dist/__tests__/InstallationId.js +0 -60
- package/dist/__tests__/NetworkCallsHandler.js +0 -222
- package/dist/__tests__/NetworkCallsHandlerModelA.js +0 -206
- package/dist/__tests__/Parsing.js +0 -115
- package/dist/__tests__/Sessions.d.ts +0 -1
- package/dist/__tests__/Sessions.js +0 -10
- package/dist/__tests__/Storage.d.ts +0 -1
- package/dist/__tests__/Storage.js +0 -173
- package/dist/__tests__/StreamId.d.ts +0 -1
- package/dist/__tests__/StreamId.js +0 -153
- package/dist/__tests__/constants.js +0 -48
- package/dist/esm/AvoBatcher.js +0 -75
- package/dist/esm/AvoDeduplicator.js +0 -137
- package/dist/esm/AvoEncryption.js +0 -277
- package/dist/esm/AvoGuid.js +0 -9
- package/dist/esm/AvoInspector.js +0 -470
- package/dist/esm/AvoInspectorEnv.js +0 -5
- package/dist/esm/AvoNetworkCallsHandler.js +0 -181
- package/dist/esm/AvoSchemaParser.js +0 -128
- package/dist/esm/AvoStorage.js +0 -176
- package/dist/esm/AvoStreamId.js +0 -140
- package/dist/esm/eventSpec/AvoEventSpecCache.js +0 -165
- package/dist/esm/eventSpec/AvoEventSpecFetchTypes.js +0 -6
- package/dist/esm/eventSpec/AvoEventSpecFetcher.js +0 -203
- package/dist/esm/eventSpec/EventValidator.js +0 -687
- package/dist/esm/eventSpec/index.js +0 -4
- package/dist/esm/index.js +0 -3
- package/dist/esm/utils.js +0 -38
- package/dist/esm/version.js +0 -2
- package/dist/eventSpec/AvoEventSpecCache.js +0 -181
- package/dist/eventSpec/AvoEventSpecFetcher.js +0 -290
- package/dist/eventSpec/EventValidator.js +0 -846
- package/dist/eventSpec/index.js +0 -20
- package/dist/version.d.ts +0 -1
- package/dist/version.js +0 -5
- /package/dist/__tests__/{AvoEncryption.d.ts → ApiKeyHeaderInjection_test.d.ts} +0 -0
- /package/dist/__tests__/{AvoEncryptionIntegration.d.ts → BrowserBootstrap_test.d.ts} +0 -0
- /package/dist/__tests__/{AvoEventSpecFetcher.d.ts → EventSpecGatewayCoordinates_test.d.ts} +0 -0
- /package/dist/__tests__/{AvoInspector.d.ts → InFlightQueueGaps_test.d.ts} +0 -0
- /package/dist/__tests__/{Batcher.d.ts → InFlightQueue_test.d.ts} +0 -0
- /package/dist/__tests__/{BatcherPublic.d.ts → NetworkCallsHandlerLite_test.d.ts} +0 -0
- /package/dist/__tests__/{Deduplicator.d.ts → PublicSurface_test.d.ts} +0 -0
- /package/dist/__tests__/{EventSpecCache.d.ts → TrackOptionsIntegration_test.d.ts} +0 -0
- /package/dist/__tests__/{EventValidator.d.ts → TrackOptionsTransport_test.d.ts} +0 -0
- /package/dist/__tests__/{InstallationId.d.ts → TrackingHeaders_test.d.ts} +0 -0
- /package/dist/__tests__/{NetworkCallsHandler.d.ts → V1WireBaseline_test.d.ts} +0 -0
- /package/dist/__tests__/{NetworkCallsHandlerModelA.d.ts → V2QueuedAttribution_test.d.ts} +0 -0
- /package/dist/__tests__/{Parsing.d.ts → V2StopSending_test.d.ts} +0 -0
|
@@ -40,8 +40,75 @@ exports.AvoNetworkCallsHandler = exports.AvoNetworkCallsHandlerLite = void 0;
|
|
|
40
40
|
// LITE COPY of src/AvoNetworkCallsHandler.ts — Sync: review src/AvoNetworkCallsHandler.ts changes for applicability here
|
|
41
41
|
var AvoGuid_1 = require("../AvoGuid");
|
|
42
42
|
var AvoInspectorLite_1 = require("./AvoInspectorLite");
|
|
43
|
+
/**
|
|
44
|
+
* Normalizes a hint value: strings are trimmed; anything else (numbers,
|
|
45
|
+
* booleans, null, undefined, objects, arrays) is treated as absent.
|
|
46
|
+
*/
|
|
47
|
+
function normalizeHint(value) {
|
|
48
|
+
if (typeof value !== "string")
|
|
49
|
+
return undefined;
|
|
50
|
+
var trimmed = value.trim();
|
|
51
|
+
return trimmed === "" ? undefined : trimmed;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The only client that selects the v2 transport: the one the web GTM tag template
|
|
55
|
+
* writes to `window.inspector.__CLIENT__`. It is also the only `X-Avo-Client`
|
|
56
|
+
* value this SDK ever sends.
|
|
57
|
+
*/
|
|
58
|
+
var webGtmTemplateClient = "gtm-web";
|
|
59
|
+
/**
|
|
60
|
+
* Normalizes the client, which is also what selects the transport. It returns
|
|
61
|
+
* "gtm-web" — v2 — only when the client is a string that is exactly "gtm-web"
|
|
62
|
+
* after trimming (a value copied out of a config keeps its trailing newline, so
|
|
63
|
+
* trimming keeps that attribution). Anything else — absent, blank, non-string,
|
|
64
|
+
* "web", another platform token, a different case — returns undefined: v1,
|
|
65
|
+
* exactly as 3.2.0 sent it. Every caller other than the web GTM tag template
|
|
66
|
+
* stays on v1.
|
|
67
|
+
*
|
|
68
|
+
* Returning only the constant also means no caller-supplied string ever becomes
|
|
69
|
+
* a request header, so an unusable value can never make setRequestHeader throw
|
|
70
|
+
* during synchronous request setup.
|
|
71
|
+
*/
|
|
72
|
+
function normalizeClient(client) {
|
|
73
|
+
return normalizeHint(client) === webGtmTemplateClient
|
|
74
|
+
? webGtmTemplateClient
|
|
75
|
+
: undefined;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Whether a value can be sent as a request header as it is. Browsers throw from
|
|
79
|
+
* setRequestHeader for a value containing NUL, CR or LF, or any code point above
|
|
80
|
+
* U+00FF. Only v2 sends the api key and env as headers.
|
|
81
|
+
*/
|
|
82
|
+
function isHeaderValue(value) {
|
|
83
|
+
return (typeof value === "string" && !/[\u0000\n\r]|[^\u0000-\u00ff]/.test(value));
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* v2 only: after this many permanent failures in a row (a request the browser
|
|
87
|
+
* refuses to build, or a 4xx other than 408 and 429), the handler stops trying.
|
|
88
|
+
* For this instance's own events it stops sending for the rest of the page,
|
|
89
|
+
* while the batcher keeps queueing them, so a later page load can still deliver
|
|
90
|
+
* them. For events queued under another api key and env it drops that group,
|
|
91
|
+
* which is the only case where events are discarded: nothing on a later page can
|
|
92
|
+
* change the key they carry.
|
|
93
|
+
*/
|
|
94
|
+
var maxConsecutivePermanentFailures = 3;
|
|
43
95
|
/** Bodies smaller than this are sent uncompressed — gzip overhead outweighs the gain. */
|
|
44
96
|
var gzipMinBodyLength = 1024;
|
|
97
|
+
/**
|
|
98
|
+
* Lowest timeout, in milliseconds, for every send in dev and staging: immediate
|
|
99
|
+
* (validated) sends and batches. A send that times out is retried, and a
|
|
100
|
+
* request that was slow but did reach the server would then be ingested twice,
|
|
101
|
+
* so outside prod the client waits longer for the answer instead of giving up at
|
|
102
|
+
* networkTimeout. A larger AvoInspector.networkTimeout still applies, and so
|
|
103
|
+
* does 0 (no timeout). Prod batches keep networkTimeout.
|
|
104
|
+
*/
|
|
105
|
+
var validatedSendMinTimeout = 10000;
|
|
106
|
+
/** networkTimeout raised to validatedSendMinTimeout; 0 means no timeout for XMLHttpRequest, so it stays 0. */
|
|
107
|
+
function nonProdSendTimeout() {
|
|
108
|
+
return AvoInspectorLite_1.AvoInspector.networkTimeout === 0
|
|
109
|
+
? 0
|
|
110
|
+
: Math.max(AvoInspectorLite_1.AvoInspector.networkTimeout, validatedSendMinTimeout);
|
|
111
|
+
}
|
|
45
112
|
/**
|
|
46
113
|
* Gzips a string body using the browser-native CompressionStream API.
|
|
47
114
|
* Returns null if compression fails for any reason.
|
|
@@ -89,15 +156,58 @@ function gzip(body) {
|
|
|
89
156
|
});
|
|
90
157
|
}
|
|
91
158
|
var AvoNetworkCallsHandlerLite = /** @class */ (function () {
|
|
92
|
-
function AvoNetworkCallsHandlerLite(apiKey, envName, appName, appVersion, libVersion) {
|
|
159
|
+
function AvoNetworkCallsHandlerLite(apiKey, envName, appName, appVersion, libVersion, client) {
|
|
93
160
|
this.samplingRate = 1.0;
|
|
94
161
|
this.sending = false;
|
|
162
|
+
/** v2 only: permanent failures in a row for this instance's own events. */
|
|
163
|
+
this.permanentFailures = 0;
|
|
164
|
+
/** v2 only: permanent failures in a row per other api key and env, keyed as the send groups are. */
|
|
165
|
+
this.foreignFailures = {};
|
|
166
|
+
/** v2 only: set at most once, when sending can no longer succeed on this page. */
|
|
167
|
+
this.sendingDisabled = false;
|
|
168
|
+
/** v2 only: set when the events themselves can never be sent, so queueing them would only grow the stored queue. */
|
|
169
|
+
this.queueingDisabled = false;
|
|
95
170
|
this.apiKey = apiKey;
|
|
96
171
|
this.envName = envName;
|
|
97
172
|
this.appName = appName;
|
|
98
173
|
this.appVersion = appVersion;
|
|
99
174
|
this.libVersion = libVersion;
|
|
175
|
+
this.client = normalizeClient(client);
|
|
176
|
+
this.trackingEndpoint =
|
|
177
|
+
this.client === undefined
|
|
178
|
+
? "https://api.avo.app/inspector/v1/track"
|
|
179
|
+
: "https://api.avo.app/inspector/v2/track";
|
|
180
|
+
// v2 sends the api key and env as headers. One the browser refuses would fail
|
|
181
|
+
// every send, so v2 stops before queueing anything instead of retrying.
|
|
182
|
+
if (this.client !== undefined &&
|
|
183
|
+
!(isHeaderValue(apiKey) && isHeaderValue(envName))) {
|
|
184
|
+
this.disableSending("the api key cannot be sent as a request header");
|
|
185
|
+
// Every event this instance builds would carry that key, so queueing is pointless too.
|
|
186
|
+
this.queueingDisabled = true;
|
|
187
|
+
}
|
|
100
188
|
}
|
|
189
|
+
/**
|
|
190
|
+
* v2 only: true once sending has stopped for this page (see
|
|
191
|
+
* maxConsecutivePermanentFailures). Always false on v1.
|
|
192
|
+
*/
|
|
193
|
+
AvoNetworkCallsHandlerLite.prototype.isSendingDisabled = function () {
|
|
194
|
+
return this.sendingDisabled;
|
|
195
|
+
};
|
|
196
|
+
/**
|
|
197
|
+
* v2 only: true when queueing events would only grow the stored queue, because
|
|
198
|
+
* they could never be sent under any configuration. Always false on v1.
|
|
199
|
+
*/
|
|
200
|
+
AvoNetworkCallsHandlerLite.prototype.isQueueingDisabled = function () {
|
|
201
|
+
return this.queueingDisabled;
|
|
202
|
+
};
|
|
203
|
+
AvoNetworkCallsHandlerLite.prototype.disableSending = function (reason) {
|
|
204
|
+
this.sendingDisabled = true;
|
|
205
|
+
console.error("[Avo Inspector] Stopped sending events on this page: " + reason + ".");
|
|
206
|
+
};
|
|
207
|
+
/** True while a batch sent through callInspectorWithBatchBody awaits its response. */
|
|
208
|
+
AvoNetworkCallsHandlerLite.prototype.isBatchInFlight = function () {
|
|
209
|
+
return this.sending;
|
|
210
|
+
};
|
|
101
211
|
AvoNetworkCallsHandlerLite.prototype.callInspectorWithBatchBody = function (inEvents, onCompleted) {
|
|
102
212
|
var _this = this;
|
|
103
213
|
if (this.sending) {
|
|
@@ -130,10 +240,16 @@ var AvoNetworkCallsHandlerLite = /** @class */ (function () {
|
|
|
130
240
|
});
|
|
131
241
|
}
|
|
132
242
|
this.sending = true;
|
|
133
|
-
this.
|
|
243
|
+
var timeout = this.envName === "prod" ? AvoInspectorLite_1.AvoInspector.networkTimeout : nonProdSendTimeout();
|
|
244
|
+
this.callInspectorApi(events, function (error, retryEvents) {
|
|
134
245
|
_this.sending = false;
|
|
135
|
-
|
|
136
|
-
|
|
246
|
+
if (retryEvents === undefined) {
|
|
247
|
+
onCompleted(error);
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
onCompleted(error, retryEvents);
|
|
251
|
+
}
|
|
252
|
+
}, timeout);
|
|
137
253
|
};
|
|
138
254
|
AvoNetworkCallsHandlerLite.prototype.fixStreamIds = function (_events) {
|
|
139
255
|
// No-op in lite build: streamId is a dev/staging debugger feature
|
|
@@ -143,7 +259,7 @@ var AvoNetworkCallsHandlerLite = /** @class */ (function () {
|
|
|
143
259
|
sessionBody.type = "sessionStarted";
|
|
144
260
|
return sessionBody;
|
|
145
261
|
};
|
|
146
|
-
AvoNetworkCallsHandlerLite.prototype.bodyForEventSchemaCall = function (eventName, eventProperties, eventId, eventHash, eventSpecMetadata, validatedBranchId) {
|
|
262
|
+
AvoNetworkCallsHandlerLite.prototype.bodyForEventSchemaCall = function (eventName, eventProperties, eventId, eventHash, eventSpecMetadata, validatedBranchId, options) {
|
|
147
263
|
var eventSchemaBody = this.createBaseCallBody();
|
|
148
264
|
eventSchemaBody.type = "event";
|
|
149
265
|
eventSchemaBody.eventName = eventName;
|
|
@@ -166,6 +282,26 @@ var AvoNetworkCallsHandlerLite = /** @class */ (function () {
|
|
|
166
282
|
if (validatedBranchId) {
|
|
167
283
|
eventSchemaBody.validatedBranchId = validatedBranchId;
|
|
168
284
|
}
|
|
285
|
+
// Gateway options belong to the v2 transport. Without a client they are
|
|
286
|
+
// ignored entirely — the appVersion override included — so the body is
|
|
287
|
+
// exactly the one 3.2.0 built for the same call.
|
|
288
|
+
if (this.client !== undefined) {
|
|
289
|
+
var outputReference = normalizeHint(options === null || options === void 0 ? void 0 : options.outputReference);
|
|
290
|
+
var originHint = normalizeHint(options === null || options === void 0 ? void 0 : options.originHint);
|
|
291
|
+
var appVersion = normalizeHint(options === null || options === void 0 ? void 0 : options.appVersion);
|
|
292
|
+
if (outputReference !== undefined) {
|
|
293
|
+
eventSchemaBody.outputReference = outputReference;
|
|
294
|
+
}
|
|
295
|
+
if (originHint !== undefined) {
|
|
296
|
+
eventSchemaBody.originHint = originHint;
|
|
297
|
+
// An origin hint marks an event from another source, whose app version is
|
|
298
|
+
// unrelated to this SDK instance's configured version.
|
|
299
|
+
eventSchemaBody.appVersion = appVersion !== undefined ? appVersion : null;
|
|
300
|
+
}
|
|
301
|
+
else if (appVersion !== undefined) {
|
|
302
|
+
eventSchemaBody.appVersion = appVersion;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
169
305
|
return eventSchemaBody;
|
|
170
306
|
};
|
|
171
307
|
AvoNetworkCallsHandlerLite.prototype.createBaseCallBody = function () {
|
|
@@ -195,7 +331,7 @@ var AvoNetworkCallsHandlerLite = /** @class */ (function () {
|
|
|
195
331
|
console.log("Avo Inspector: calling inspector immediately (with validation)", eventBody.eventName);
|
|
196
332
|
console.log("Avo Inspector: event body", eventBody);
|
|
197
333
|
}
|
|
198
|
-
this.callInspectorApi([eventBody], onCompleted);
|
|
334
|
+
this.callInspectorApi([eventBody], onCompleted, nonProdSendTimeout());
|
|
199
335
|
};
|
|
200
336
|
/**
|
|
201
337
|
* Check if event should be dropped based on sampling rate.
|
|
@@ -211,36 +347,181 @@ var AvoNetworkCallsHandlerLite = /** @class */ (function () {
|
|
|
211
347
|
* compression fails, the body is sent uncompressed exactly as before —
|
|
212
348
|
* synchronously in the unavailable case, so legacy timing is preserved.
|
|
213
349
|
*/
|
|
214
|
-
AvoNetworkCallsHandlerLite.prototype.callInspectorApi = function (events, onCompleted) {
|
|
350
|
+
AvoNetworkCallsHandlerLite.prototype.callInspectorApi = function (events, onCompleted, timeout) {
|
|
351
|
+
var _this = this;
|
|
352
|
+
if (timeout === void 0) { timeout = AvoInspectorLite_1.AvoInspector.networkTimeout; }
|
|
353
|
+
if (this.client === undefined) {
|
|
354
|
+
this.sendEvents(events, this.apiKey, this.envName, timeout, function (error) {
|
|
355
|
+
onCompleted(error);
|
|
356
|
+
});
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
// v2 takes the api key and env from the headers, for every event in the
|
|
360
|
+
// request, so each event is sent under the ones it was queued with. They
|
|
361
|
+
// differ from this instance's only for events persisted by an earlier page
|
|
362
|
+
// with another configuration — a GTM Preview load flushing a published
|
|
363
|
+
// container's queue, or the reverse. One request per pair, in order.
|
|
364
|
+
var groups = [];
|
|
365
|
+
var groupKeys = [];
|
|
366
|
+
events.forEach(function (event) {
|
|
367
|
+
var key = JSON.stringify([event.apiKey, event.env]);
|
|
368
|
+
var index = groupKeys.indexOf(key);
|
|
369
|
+
if (index === -1) {
|
|
370
|
+
index = groupKeys.push(key) - 1;
|
|
371
|
+
groups.push([]);
|
|
372
|
+
}
|
|
373
|
+
groups[index].push(event);
|
|
374
|
+
});
|
|
375
|
+
var retryEvents = [];
|
|
376
|
+
var firstError = null;
|
|
377
|
+
var sendGroup = function (index) {
|
|
378
|
+
if (index === groups.length) {
|
|
379
|
+
if (firstError === null) {
|
|
380
|
+
onCompleted(null);
|
|
381
|
+
}
|
|
382
|
+
else {
|
|
383
|
+
onCompleted(firstError, retryEvents);
|
|
384
|
+
}
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
var group = groups[index];
|
|
388
|
+
var groupKey = groupKeys[index];
|
|
389
|
+
var apiKey = group[0].apiKey;
|
|
390
|
+
var env = group[0].env;
|
|
391
|
+
var own = apiKey === _this.apiKey && env === _this.envName;
|
|
392
|
+
var completed = function (error, permanent) {
|
|
393
|
+
if (error === null) {
|
|
394
|
+
if (own) {
|
|
395
|
+
_this.permanentFailures = 0;
|
|
396
|
+
}
|
|
397
|
+
else {
|
|
398
|
+
delete _this.foreignFailures[groupKey];
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
else {
|
|
402
|
+
firstError = firstError || error;
|
|
403
|
+
if (!permanent) {
|
|
404
|
+
Array.prototype.push.apply(retryEvents, group);
|
|
405
|
+
}
|
|
406
|
+
else if (own) {
|
|
407
|
+
// Kept: the api key and env are this page's, and a later page load —
|
|
408
|
+
// or the server catching up on a key it has not seen yet — can still
|
|
409
|
+
// deliver them. Sending stops, queueing does not.
|
|
410
|
+
Array.prototype.push.apply(retryEvents, group);
|
|
411
|
+
_this.permanentFailures += 1;
|
|
412
|
+
if (!_this.sendingDisabled &&
|
|
413
|
+
_this.permanentFailures >= maxConsecutivePermanentFailures) {
|
|
414
|
+
_this.disableSending("the Avo Inspector API refused its events " +
|
|
415
|
+
maxConsecutivePermanentFailures +
|
|
416
|
+
" times in a row");
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
else {
|
|
420
|
+
// Queued under another api key and env, which a retry cannot change.
|
|
421
|
+
// Retried a few times anyway, in case the refusal was about the
|
|
422
|
+
// moment rather than the key, then dropped.
|
|
423
|
+
var failures = (_this.foreignFailures[groupKey] || 0) + 1;
|
|
424
|
+
_this.foreignFailures[groupKey] = failures;
|
|
425
|
+
if (failures < maxConsecutivePermanentFailures) {
|
|
426
|
+
Array.prototype.push.apply(retryEvents, group);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
sendGroup(index + 1);
|
|
431
|
+
};
|
|
432
|
+
if (_this.sendingDisabled) {
|
|
433
|
+
completed(new Error("Sending events is stopped on this page"));
|
|
434
|
+
}
|
|
435
|
+
else if (!isHeaderValue(apiKey) || !isHeaderValue(env)) {
|
|
436
|
+
completed(new Error("Failed to send request: the api key or env cannot be sent as a request header"), true);
|
|
437
|
+
}
|
|
438
|
+
else {
|
|
439
|
+
_this.sendEvents(group, apiKey, env, timeout, completed);
|
|
440
|
+
}
|
|
441
|
+
};
|
|
442
|
+
sendGroup(0);
|
|
443
|
+
};
|
|
444
|
+
AvoNetworkCallsHandlerLite.prototype.sendEvents = function (events, apiKey, env, timeout, onCompleted) {
|
|
215
445
|
var _this = this;
|
|
216
446
|
var body = JSON.stringify(events);
|
|
217
447
|
if (typeof CompressionStream === "undefined" ||
|
|
218
448
|
body.length < gzipMinBodyLength) {
|
|
219
|
-
this.sendTrackingRequest(body, false, onCompleted);
|
|
449
|
+
this.sendTrackingRequest(body, false, apiKey, env, timeout, onCompleted);
|
|
220
450
|
return;
|
|
221
451
|
}
|
|
222
452
|
gzip(body).then(function (compressed) {
|
|
223
453
|
if (compressed !== null) {
|
|
224
|
-
_this.sendTrackingRequest(compressed, true, onCompleted);
|
|
454
|
+
_this.sendTrackingRequest(compressed, true, apiKey, env, timeout, onCompleted);
|
|
225
455
|
}
|
|
226
456
|
else {
|
|
227
|
-
_this.sendTrackingRequest(body, false, onCompleted);
|
|
457
|
+
_this.sendTrackingRequest(body, false, apiKey, env, timeout, onCompleted);
|
|
228
458
|
}
|
|
229
459
|
});
|
|
230
460
|
};
|
|
231
|
-
|
|
461
|
+
/**
|
|
462
|
+
* Reports a failure as permanent (second argument true) when retrying the same
|
|
463
|
+
* request cannot succeed: the browser refused to build it, or the API answered
|
|
464
|
+
* a 4xx other than 408 and 429.
|
|
465
|
+
*/
|
|
466
|
+
AvoNetworkCallsHandlerLite.prototype.sendTrackingRequest = function (body, isGzipped, apiKey, env, timeout, onCompleted) {
|
|
232
467
|
var _this = this;
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
468
|
+
// Everything from construction up to and including send() runs synchronously,
|
|
469
|
+
// and any of it can throw: a header value the browser rejects, or a send() the
|
|
470
|
+
// environment refuses. callInspectorWithBatchBody clears its `sending`
|
|
471
|
+
// re-entrancy guard only from onCompleted, so an exception escaping this method
|
|
472
|
+
// would latch the guard and silently cancel every later batch for the lifetime
|
|
473
|
+
// of the page. Reporting through onCompleted keeps that path recoverable — the
|
|
474
|
+
// batcher puts the events back and retries with the next batch.
|
|
475
|
+
//
|
|
476
|
+
// The XMLHttpRequest construction is inside the try for the same reason. What
|
|
477
|
+
// that buys is a precise guarantee, worth stating exactly: no synchronous XHR
|
|
478
|
+
// setup step can throw out of this method, on either the direct or the gzipped
|
|
479
|
+
// path. It is NOT a claim that the method never throws — onCompleted is the
|
|
480
|
+
// caller's own callback, typed to return any, and a throw from it propagates.
|
|
481
|
+
// That case is harmless here: callInspectorWithBatchBody clears `sending`
|
|
482
|
+
// before forwarding to its caller's callback, so a throwing callback cannot
|
|
483
|
+
// latch the guard; on the gzipped path it surfaces as an unhandled rejection.
|
|
484
|
+
// The event handlers below are assigned after the try deliberately — they run
|
|
485
|
+
// in a later task, which no try/catch here could cover.
|
|
486
|
+
var xmlhttp;
|
|
487
|
+
try {
|
|
488
|
+
xmlhttp = new XMLHttpRequest();
|
|
489
|
+
xmlhttp.open("POST", this.trackingEndpoint, true);
|
|
490
|
+
if (this.client === undefined) {
|
|
491
|
+
// v1, byte for byte the 3.2.0 request: the api key and env travel only in
|
|
492
|
+
// the body, and text/plain keeps an uncompressed send inside the CORS
|
|
493
|
+
// safelist, so it is not preflighted.
|
|
494
|
+
xmlhttp.setRequestHeader("Content-Type", "text/plain");
|
|
495
|
+
}
|
|
496
|
+
else {
|
|
497
|
+
// v2 reads the api key and env from headers (the body keeps carrying both,
|
|
498
|
+
// so one body shape serves every endpoint version) and attributes traffic
|
|
499
|
+
// by X-Avo-Client without decoding a body. None of the three is
|
|
500
|
+
// CORS-safelisted, so a browser preflights every v2 send — which is why the
|
|
501
|
+
// body is not `text/plain` here: that content type existed only to stay
|
|
502
|
+
// inside the safelist and dodge the preflight, a saving custom headers
|
|
503
|
+
// cancel out, and v2's body reader parses a text/plain body twice and throws.
|
|
504
|
+
xmlhttp.setRequestHeader("Content-Type", "application/json");
|
|
505
|
+
xmlhttp.setRequestHeader("api-key", apiKey);
|
|
506
|
+
xmlhttp.setRequestHeader("env", env);
|
|
507
|
+
xmlhttp.setRequestHeader("X-Avo-Client", this.client);
|
|
508
|
+
}
|
|
509
|
+
if (isGzipped) {
|
|
510
|
+
xmlhttp.setRequestHeader("Content-Encoding", "gzip");
|
|
511
|
+
}
|
|
512
|
+
xmlhttp.timeout = timeout;
|
|
513
|
+
xmlhttp.send(body);
|
|
514
|
+
}
|
|
515
|
+
catch (e) {
|
|
516
|
+
onCompleted(new Error("Failed to send request: ".concat(e instanceof Error ? e.message : String(e))), true);
|
|
517
|
+
return;
|
|
238
518
|
}
|
|
239
|
-
xmlhttp.timeout = AvoInspectorLite_1.AvoInspector.networkTimeout;
|
|
240
|
-
xmlhttp.send(body);
|
|
241
519
|
xmlhttp.onload = function () {
|
|
242
520
|
if (xmlhttp.status !== 200) {
|
|
243
|
-
onCompleted(new Error("Error ".concat(xmlhttp.status, ": ").concat(xmlhttp.statusText))
|
|
521
|
+
onCompleted(new Error("Error ".concat(xmlhttp.status, ": ").concat(xmlhttp.statusText)), xmlhttp.status >= 400 &&
|
|
522
|
+
xmlhttp.status < 500 &&
|
|
523
|
+
xmlhttp.status !== 408 &&
|
|
524
|
+
xmlhttp.status !== 429);
|
|
244
525
|
}
|
|
245
526
|
else {
|
|
246
527
|
var response = void 0;
|
|
@@ -266,7 +547,6 @@ var AvoNetworkCallsHandlerLite = /** @class */ (function () {
|
|
|
266
547
|
onCompleted(new Error("Request timed out"));
|
|
267
548
|
};
|
|
268
549
|
};
|
|
269
|
-
AvoNetworkCallsHandlerLite.trackingEndpoint = "https://api.avo.app/inspector/v1/track";
|
|
270
550
|
return AvoNetworkCallsHandlerLite;
|
|
271
551
|
}());
|
|
272
552
|
exports.AvoNetworkCallsHandlerLite = AvoNetworkCallsHandlerLite;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "avo-inspector",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.3",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"browser": "dist/index.js",
|
|
@@ -64,6 +64,15 @@
|
|
|
64
64
|
"@noble/curves": "^1.4.0",
|
|
65
65
|
"safe-regex2": "^5.0.0"
|
|
66
66
|
},
|
|
67
|
+
"resolutions": {
|
|
68
|
+
"handlebars": "4.7.9",
|
|
69
|
+
"form-data": "4.0.6",
|
|
70
|
+
"serialize-javascript": "7.0.3",
|
|
71
|
+
"fast-uri": "3.1.2",
|
|
72
|
+
"flatted": "3.4.2",
|
|
73
|
+
"@babel/plugin-transform-modules-systemjs": "7.29.4",
|
|
74
|
+
"minimatch": "3.1.4"
|
|
75
|
+
},
|
|
67
76
|
"devDependencies": {
|
|
68
77
|
"@babel/core": "^7.25.2",
|
|
69
78
|
"@babel/preset-env": "^7.25.3",
|
package/dist/AvoBatcher.js
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.AvoBatcher = void 0;
|
|
4
|
-
var AvoInspector_1 = require("./AvoInspector");
|
|
5
|
-
var AvoBatcher = /** @class */ (function () {
|
|
6
|
-
function AvoBatcher(networkCallsHandler) {
|
|
7
|
-
var _this = this;
|
|
8
|
-
this.events = [];
|
|
9
|
-
this.networkCallsHandler = networkCallsHandler;
|
|
10
|
-
this.batchFlushAttemptTimestamp = Date.now();
|
|
11
|
-
AvoInspector_1.AvoInspector.avoStorage
|
|
12
|
-
.getItemAsync(AvoBatcher.cacheKey)
|
|
13
|
-
.then(function (savedEvents) {
|
|
14
|
-
if (savedEvents !== null) {
|
|
15
|
-
var nonNullSavedEvents = savedEvents.filter(function (event) { return event !== null; });
|
|
16
|
-
_this.events = _this.events.concat(nonNullSavedEvents);
|
|
17
|
-
_this.checkIfBatchNeedsToBeSent();
|
|
18
|
-
}
|
|
19
|
-
})
|
|
20
|
-
.catch(function (error) {
|
|
21
|
-
if (AvoInspector_1.AvoInspector.shouldLog) {
|
|
22
|
-
console.error("Avo Inspector: failed to load cached events.", error);
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
AvoBatcher.prototype.handleTrackSchema = function (eventName, schema, eventId, eventHash, eventProps) {
|
|
27
|
-
var _this = this;
|
|
28
|
-
this.networkCallsHandler.bodyForEventSchemaCall(eventName, schema, eventId, eventHash, eventProps).then(function (eventBody) {
|
|
29
|
-
_this.events.push(eventBody);
|
|
30
|
-
_this.saveEvents();
|
|
31
|
-
if (AvoInspector_1.AvoInspector.shouldLog) {
|
|
32
|
-
console.log("Avo Inspector: saved event " +
|
|
33
|
-
eventName +
|
|
34
|
-
" with schema " +
|
|
35
|
-
JSON.stringify(schema));
|
|
36
|
-
}
|
|
37
|
-
_this.checkIfBatchNeedsToBeSent();
|
|
38
|
-
}).catch(function (error) {
|
|
39
|
-
console.error("Avo Inspector: failed to prepare event body for " + eventName + ".", error);
|
|
40
|
-
});
|
|
41
|
-
};
|
|
42
|
-
AvoBatcher.prototype.checkIfBatchNeedsToBeSent = function () {
|
|
43
|
-
var batchSize = this.events.length;
|
|
44
|
-
var now = Date.now();
|
|
45
|
-
var timeSinceLastFlushAttempt = now - this.batchFlushAttemptTimestamp;
|
|
46
|
-
var sendBySize = batchSize % AvoInspector_1.AvoInspector.batchSize == 0;
|
|
47
|
-
var sendByTime = timeSinceLastFlushAttempt >= AvoInspector_1.AvoInspector.batchFlushSeconds * 1000;
|
|
48
|
-
var avoBatcher = this;
|
|
49
|
-
if (sendBySize || sendByTime) {
|
|
50
|
-
this.batchFlushAttemptTimestamp = now;
|
|
51
|
-
var sendingEvents_1 = avoBatcher.events;
|
|
52
|
-
avoBatcher.events = [];
|
|
53
|
-
this.networkCallsHandler.callInspectorWithBatchBody(sendingEvents_1, function (error) {
|
|
54
|
-
if (error != null) {
|
|
55
|
-
avoBatcher.events = avoBatcher.events.concat(sendingEvents_1);
|
|
56
|
-
if (AvoInspector_1.AvoInspector.shouldLog) {
|
|
57
|
-
console.log("Avo Inspector: batch sending failed: " +
|
|
58
|
-
error +
|
|
59
|
-
". We will attempt to send your schemas with next batch");
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
else {
|
|
63
|
-
if (AvoInspector_1.AvoInspector.shouldLog) {
|
|
64
|
-
console.log("Avo Inspector: batch sent successfully.");
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
avoBatcher.saveEvents();
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
};
|
|
71
|
-
AvoBatcher.prototype.saveEvents = function () {
|
|
72
|
-
if (this.events.length > 1000) {
|
|
73
|
-
var extraElements = this.events.length - 1000;
|
|
74
|
-
this.events.splice(0, extraElements);
|
|
75
|
-
}
|
|
76
|
-
AvoInspector_1.AvoInspector.avoStorage.setItem(AvoBatcher.cacheKey, this.events);
|
|
77
|
-
};
|
|
78
|
-
Object.defineProperty(AvoBatcher, "cacheKey", {
|
|
79
|
-
get: function () {
|
|
80
|
-
return "AvoInspectorEvents";
|
|
81
|
-
},
|
|
82
|
-
enumerable: false,
|
|
83
|
-
configurable: true
|
|
84
|
-
});
|
|
85
|
-
return AvoBatcher;
|
|
86
|
-
}());
|
|
87
|
-
exports.AvoBatcher = AvoBatcher;
|
package/dist/AvoDeduplicator.js
DELETED
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.AvoDeduplicator = void 0;
|
|
4
|
-
var AvoSchemaParser_1 = require("./AvoSchemaParser");
|
|
5
|
-
var utils_1 = require("./utils");
|
|
6
|
-
var AvoDeduplicator = /** @class */ (function () {
|
|
7
|
-
function AvoDeduplicator() {
|
|
8
|
-
this.avoFunctionsEvents = {};
|
|
9
|
-
this.manualEvents = {};
|
|
10
|
-
this.avoFunctionsEventsParams = {};
|
|
11
|
-
this.manualEventsParams = {};
|
|
12
|
-
}
|
|
13
|
-
AvoDeduplicator.prototype.shouldRegisterEvent = function (eventName, params, fromAvoFunction) {
|
|
14
|
-
this.clearOldEvents();
|
|
15
|
-
if (fromAvoFunction) {
|
|
16
|
-
this.avoFunctionsEvents[Date.now()] = eventName;
|
|
17
|
-
this.avoFunctionsEventsParams[eventName] = params;
|
|
18
|
-
}
|
|
19
|
-
else {
|
|
20
|
-
this.manualEvents[Date.now()] = eventName;
|
|
21
|
-
this.manualEventsParams[eventName] = params;
|
|
22
|
-
}
|
|
23
|
-
var checkInAvoFunctions = !fromAvoFunction;
|
|
24
|
-
return !this.hasSameEventAs(eventName, params, checkInAvoFunctions);
|
|
25
|
-
};
|
|
26
|
-
AvoDeduplicator.prototype.hasSameEventAs = function (eventName, params, checkInAvoFunctions) {
|
|
27
|
-
var result = false;
|
|
28
|
-
if (checkInAvoFunctions) {
|
|
29
|
-
if (this.lookForEventIn(eventName, params, this.avoFunctionsEventsParams)) {
|
|
30
|
-
result = true;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
else {
|
|
34
|
-
if (this.lookForEventIn(eventName, params, this.manualEventsParams)) {
|
|
35
|
-
result = true;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
if (result) {
|
|
39
|
-
delete this.avoFunctionsEventsParams[eventName];
|
|
40
|
-
delete this.manualEventsParams[eventName];
|
|
41
|
-
}
|
|
42
|
-
return result;
|
|
43
|
-
};
|
|
44
|
-
AvoDeduplicator.prototype.lookForEventIn = function (eventName, params, eventsStorage) {
|
|
45
|
-
for (var otherEventName in eventsStorage) {
|
|
46
|
-
if (eventsStorage.hasOwnProperty(otherEventName)) {
|
|
47
|
-
if (otherEventName === eventName) {
|
|
48
|
-
var otherParams = eventsStorage[eventName];
|
|
49
|
-
if (otherParams && (0, utils_1.deepEquals)(params, otherParams)) {
|
|
50
|
-
return true;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
return false;
|
|
56
|
-
};
|
|
57
|
-
AvoDeduplicator.prototype.hasSeenEventParams = function (params, checkInAvoFunctions) {
|
|
58
|
-
var result = false;
|
|
59
|
-
if (checkInAvoFunctions) {
|
|
60
|
-
if (this.lookForEventParamsIn(params, this.avoFunctionsEventsParams)) {
|
|
61
|
-
result = true;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
else {
|
|
65
|
-
if (this.lookForEventParamsIn(params, this.manualEventsParams)) {
|
|
66
|
-
result = true;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
return result;
|
|
70
|
-
};
|
|
71
|
-
AvoDeduplicator.prototype.lookForEventParamsIn = function (params, eventsStorage) {
|
|
72
|
-
for (var otherEventName in eventsStorage) {
|
|
73
|
-
if (eventsStorage.hasOwnProperty(otherEventName)) {
|
|
74
|
-
var otherParams = eventsStorage[otherEventName];
|
|
75
|
-
if (otherParams && (0, utils_1.deepEquals)(params, otherParams)) {
|
|
76
|
-
return true;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
return false;
|
|
81
|
-
};
|
|
82
|
-
AvoDeduplicator.prototype.shouldRegisterSchemaFromManually = function (eventName, eventSchema) {
|
|
83
|
-
this.clearOldEvents();
|
|
84
|
-
return !this.hasSameShapeInAvoFunctionsAs(eventName, eventSchema);
|
|
85
|
-
};
|
|
86
|
-
AvoDeduplicator.prototype.hasSameShapeInAvoFunctionsAs = function (eventName, eventSchema) {
|
|
87
|
-
var result = false;
|
|
88
|
-
if (this.lookForEventSchemaIn(eventName, eventSchema, this.avoFunctionsEventsParams)) {
|
|
89
|
-
result = true;
|
|
90
|
-
}
|
|
91
|
-
if (result) {
|
|
92
|
-
delete this.avoFunctionsEventsParams[eventName];
|
|
93
|
-
}
|
|
94
|
-
return result;
|
|
95
|
-
};
|
|
96
|
-
AvoDeduplicator.prototype.lookForEventSchemaIn = function (eventName, eventSchema, eventsStorage) {
|
|
97
|
-
for (var otherEventName in eventsStorage) {
|
|
98
|
-
if (eventsStorage.hasOwnProperty(otherEventName)) {
|
|
99
|
-
if (otherEventName === eventName) {
|
|
100
|
-
var otherSchema = AvoSchemaParser_1.AvoSchemaParser.extractSchema(eventsStorage[eventName]);
|
|
101
|
-
if (otherSchema && (0, utils_1.deepEquals)(eventSchema, otherSchema)) {
|
|
102
|
-
return true;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
return false;
|
|
108
|
-
};
|
|
109
|
-
AvoDeduplicator.prototype.clearOldEvents = function () {
|
|
110
|
-
var now = Date.now();
|
|
111
|
-
var msToConsiderOld = 300;
|
|
112
|
-
for (var time in this.avoFunctionsEvents) {
|
|
113
|
-
if (this.avoFunctionsEvents.hasOwnProperty(time)) {
|
|
114
|
-
var timestamp = Number(time) || 0;
|
|
115
|
-
if (now - timestamp > msToConsiderOld) {
|
|
116
|
-
var eventName = this.avoFunctionsEvents[time];
|
|
117
|
-
delete this.avoFunctionsEvents[time];
|
|
118
|
-
delete this.avoFunctionsEventsParams[eventName];
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
for (var time in this.manualEvents) {
|
|
123
|
-
if (this.manualEvents.hasOwnProperty(time)) {
|
|
124
|
-
var timestamp = Number(time) || 0;
|
|
125
|
-
if (now - timestamp > msToConsiderOld) {
|
|
126
|
-
var eventName = this.manualEvents[time];
|
|
127
|
-
delete this.manualEvents[time];
|
|
128
|
-
delete this.manualEventsParams[eventName];
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
};
|
|
133
|
-
// used in tests
|
|
134
|
-
AvoDeduplicator.prototype._clearEvents = function () {
|
|
135
|
-
this.avoFunctionsEvents = {};
|
|
136
|
-
this.manualEvents = {};
|
|
137
|
-
this.avoFunctionsEventsParams = {};
|
|
138
|
-
this.manualEventsParams = {};
|
|
139
|
-
};
|
|
140
|
-
return AvoDeduplicator;
|
|
141
|
-
}());
|
|
142
|
-
exports.AvoDeduplicator = AvoDeduplicator;
|