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.
Files changed (84) hide show
  1. package/README.md +10 -0
  2. package/dist/AvoBatcher.d.ts +9 -3
  3. package/dist/AvoInspector.d.ts +4 -0
  4. package/dist/AvoNetworkCallsHandler.d.ts +80 -5
  5. package/dist/__tests__/UnboundPublicMethods_test.d.ts +1 -0
  6. package/dist/__tests__/constants.d.ts +2 -1
  7. package/dist/__tests__/helpers/internalGateway.d.ts +23 -0
  8. package/dist/eventSpec/AvoEventSpecCache.d.ts +7 -4
  9. package/dist/eventSpec/AvoEventSpecFetchTypes.d.ts +12 -1
  10. package/dist/eventSpec/AvoEventSpecFetcher.d.ts +6 -1
  11. package/dist/index.js +1 -1
  12. package/dist/lite/AvoBatcherLite.d.ts +9 -3
  13. package/dist/lite/AvoBatcherLite.js +46 -7
  14. package/dist/lite/AvoInspectorLite.d.ts +2 -0
  15. package/dist/lite/AvoInspectorLite.js +95 -15
  16. package/dist/lite/AvoNetworkCallsHandlerLite.d.ts +79 -5
  17. package/dist/lite/AvoNetworkCallsHandlerLite.js +300 -20
  18. package/package.json +10 -1
  19. package/dist/AvoBatcher.js +0 -87
  20. package/dist/AvoDeduplicator.js +0 -142
  21. package/dist/AvoEncryption.js +0 -176
  22. package/dist/AvoInspector.js +0 -435
  23. package/dist/AvoNetworkCallsHandler.js +0 -229
  24. package/dist/AvoSchemaParser.js +0 -95
  25. package/dist/AvoStreamId.js +0 -82
  26. package/dist/__mocks__/xhr.js +0 -14
  27. package/dist/__mocks__/xhrError.js +0 -14
  28. package/dist/__tests__/AvoEncryption.js +0 -272
  29. package/dist/__tests__/AvoEncryptionIntegration.js +0 -191
  30. package/dist/__tests__/AvoEventSpecFetcher.js +0 -377
  31. package/dist/__tests__/AvoInspector.js +0 -335
  32. package/dist/__tests__/Batcher.js +0 -214
  33. package/dist/__tests__/BatcherPublic.js +0 -139
  34. package/dist/__tests__/Deduplicator.js +0 -190
  35. package/dist/__tests__/EventSpecCache.js +0 -143
  36. package/dist/__tests__/EventValidator.js +0 -543
  37. package/dist/__tests__/InstallationId.js +0 -60
  38. package/dist/__tests__/NetworkCallsHandler.js +0 -222
  39. package/dist/__tests__/NetworkCallsHandlerModelA.js +0 -206
  40. package/dist/__tests__/Parsing.js +0 -115
  41. package/dist/__tests__/Sessions.d.ts +0 -1
  42. package/dist/__tests__/Sessions.js +0 -10
  43. package/dist/__tests__/Storage.d.ts +0 -1
  44. package/dist/__tests__/Storage.js +0 -173
  45. package/dist/__tests__/StreamId.d.ts +0 -1
  46. package/dist/__tests__/StreamId.js +0 -153
  47. package/dist/__tests__/constants.js +0 -48
  48. package/dist/esm/AvoBatcher.js +0 -75
  49. package/dist/esm/AvoDeduplicator.js +0 -137
  50. package/dist/esm/AvoEncryption.js +0 -277
  51. package/dist/esm/AvoGuid.js +0 -9
  52. package/dist/esm/AvoInspector.js +0 -470
  53. package/dist/esm/AvoInspectorEnv.js +0 -5
  54. package/dist/esm/AvoNetworkCallsHandler.js +0 -181
  55. package/dist/esm/AvoSchemaParser.js +0 -128
  56. package/dist/esm/AvoStorage.js +0 -176
  57. package/dist/esm/AvoStreamId.js +0 -140
  58. package/dist/esm/eventSpec/AvoEventSpecCache.js +0 -165
  59. package/dist/esm/eventSpec/AvoEventSpecFetchTypes.js +0 -6
  60. package/dist/esm/eventSpec/AvoEventSpecFetcher.js +0 -203
  61. package/dist/esm/eventSpec/EventValidator.js +0 -687
  62. package/dist/esm/eventSpec/index.js +0 -4
  63. package/dist/esm/index.js +0 -3
  64. package/dist/esm/utils.js +0 -38
  65. package/dist/esm/version.js +0 -2
  66. package/dist/eventSpec/AvoEventSpecCache.js +0 -181
  67. package/dist/eventSpec/AvoEventSpecFetcher.js +0 -290
  68. package/dist/eventSpec/EventValidator.js +0 -846
  69. package/dist/eventSpec/index.js +0 -20
  70. package/dist/version.d.ts +0 -1
  71. package/dist/version.js +0 -5
  72. /package/dist/__tests__/{AvoEncryption.d.ts → ApiKeyHeaderInjection_test.d.ts} +0 -0
  73. /package/dist/__tests__/{AvoEncryptionIntegration.d.ts → BrowserBootstrap_test.d.ts} +0 -0
  74. /package/dist/__tests__/{AvoEventSpecFetcher.d.ts → EventSpecGatewayCoordinates_test.d.ts} +0 -0
  75. /package/dist/__tests__/{AvoInspector.d.ts → InFlightQueueGaps_test.d.ts} +0 -0
  76. /package/dist/__tests__/{Batcher.d.ts → InFlightQueue_test.d.ts} +0 -0
  77. /package/dist/__tests__/{BatcherPublic.d.ts → NetworkCallsHandlerLite_test.d.ts} +0 -0
  78. /package/dist/__tests__/{Deduplicator.d.ts → PublicSurface_test.d.ts} +0 -0
  79. /package/dist/__tests__/{EventSpecCache.d.ts → TrackOptionsIntegration_test.d.ts} +0 -0
  80. /package/dist/__tests__/{EventValidator.d.ts → TrackOptionsTransport_test.d.ts} +0 -0
  81. /package/dist/__tests__/{InstallationId.d.ts → TrackingHeaders_test.d.ts} +0 -0
  82. /package/dist/__tests__/{NetworkCallsHandler.d.ts → V1WireBaseline_test.d.ts} +0 -0
  83. /package/dist/__tests__/{NetworkCallsHandlerModelA.d.ts → V2QueuedAttribution_test.d.ts} +0 -0
  84. /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.callInspectorApi(events, function (error) {
243
+ var timeout = this.envName === "prod" ? AvoInspectorLite_1.AvoInspector.networkTimeout : nonProdSendTimeout();
244
+ this.callInspectorApi(events, function (error, retryEvents) {
134
245
  _this.sending = false;
135
- onCompleted(error);
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
- AvoNetworkCallsHandlerLite.prototype.sendTrackingRequest = function (body, isGzipped, onCompleted) {
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
- var xmlhttp = new XMLHttpRequest();
234
- xmlhttp.open("POST", AvoNetworkCallsHandlerLite.trackingEndpoint, true);
235
- xmlhttp.setRequestHeader("Content-Type", "text/plain");
236
- if (isGzipped) {
237
- xmlhttp.setRequestHeader("Content-Encoding", "gzip");
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.0",
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",
@@ -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;
@@ -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;