avo-inspector
Version:
[](https://badge.fury.io/js/avo-inspector)
274 lines (273 loc) • 12.8 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AvoNetworkCallsHandler = exports.AvoNetworkCallsHandlerLite = void 0;
// LITE COPY of src/AvoNetworkCallsHandler.ts — Sync: review src/AvoNetworkCallsHandler.ts changes for applicability here
var AvoGuid_1 = require("../AvoGuid");
var AvoInspectorLite_1 = require("./AvoInspectorLite");
/** Bodies smaller than this are sent uncompressed — gzip overhead outweighs the gain. */
var gzipMinBodyLength = 1024;
/**
* Gzips a string body using the browser-native CompressionStream API.
* Returns null if compression fails for any reason.
*/
function gzip(body) {
return __awaiter(this, void 0, void 0, function () {
var stream, writer, reader, chunks, totalLength, _a, done, value, result_1, offset_1, _b;
return __generator(this, function (_c) {
switch (_c.label) {
case 0:
_c.trys.push([0, 4, , 5]);
stream = new CompressionStream("gzip");
writer = stream.writable.getWriter();
writer.write(new TextEncoder().encode(body)).catch(function () { });
writer.close().catch(function () { });
reader = stream.readable.getReader();
chunks = [];
totalLength = 0;
_c.label = 1;
case 1:
if (!true) return [3 /*break*/, 3];
return [4 /*yield*/, reader.read()];
case 2:
_a = _c.sent(), done = _a.done, value = _a.value;
if (done) {
return [3 /*break*/, 3];
}
chunks.push(value);
totalLength += value.length;
return [3 /*break*/, 1];
case 3:
result_1 = new Uint8Array(totalLength);
offset_1 = 0;
chunks.forEach(function (chunk) {
result_1.set(chunk, offset_1);
offset_1 += chunk.length;
});
return [2 /*return*/, result_1];
case 4:
_b = _c.sent();
return [2 /*return*/, null];
case 5: return [2 /*return*/];
}
});
});
}
var AvoNetworkCallsHandlerLite = /** @class */ (function () {
function AvoNetworkCallsHandlerLite(apiKey, envName, appName, appVersion, libVersion) {
this.samplingRate = 1.0;
this.sending = false;
this.apiKey = apiKey;
this.envName = envName;
this.appName = appName;
this.appVersion = appVersion;
this.libVersion = libVersion;
}
AvoNetworkCallsHandlerLite.prototype.callInspectorWithBatchBody = function (inEvents, onCompleted) {
var _this = this;
if (this.sending) {
onCompleted(new Error("Batch sending cancelled because another batch sending is in progress. Your events will be sent with next batch."));
return;
}
var events = inEvents.filter(function (x) { return x != null; });
this.fixStreamIds(events);
if (events.length === 0) {
return;
}
if (this.shouldDropBySampling()) {
if (AvoInspectorLite_1.AvoInspector.shouldLog) {
console.log("Avo Inspector: last event schema dropped due to sampling rate.");
}
return;
}
if (AvoInspectorLite_1.AvoInspector.shouldLog) {
console.log("Avo Inspector: events", events);
events.forEach(function (event) {
if (event.type === "sessionStarted") {
console.log("Avo Inspector: sending session started event.");
}
else if (event.type === "event") {
console.log("Avo Inspector: sending event " +
event.eventName +
" with schema " +
JSON.stringify(event.eventProperties));
}
});
}
this.sending = true;
this.callInspectorApi(events, function (error) {
_this.sending = false;
onCompleted(error);
});
};
AvoNetworkCallsHandlerLite.prototype.fixStreamIds = function (_events) {
// No-op in lite build: streamId is a dev/staging debugger feature
};
AvoNetworkCallsHandlerLite.prototype.bodyForSessionStartedCall = function () {
var sessionBody = this.createBaseCallBody();
sessionBody.type = "sessionStarted";
return sessionBody;
};
AvoNetworkCallsHandlerLite.prototype.bodyForEventSchemaCall = function (eventName, eventProperties, eventId, eventHash, eventSpecMetadata, validatedBranchId) {
var eventSchemaBody = this.createBaseCallBody();
eventSchemaBody.type = "event";
eventSchemaBody.eventName = eventName;
eventSchemaBody.eventProperties = eventProperties;
if (eventId != null) {
eventSchemaBody.avoFunction = true;
eventSchemaBody.eventId = eventId;
eventSchemaBody.eventHash = eventHash;
}
else {
eventSchemaBody.avoFunction = false;
eventSchemaBody.eventId = null;
eventSchemaBody.eventHash = null;
}
// Set metadata on base body if provided
if (eventSpecMetadata) {
eventSchemaBody.eventSpecMetadata = eventSpecMetadata;
}
// Set validated branch ID if value validation was performed
if (validatedBranchId) {
eventSchemaBody.validatedBranchId = validatedBranchId;
}
return eventSchemaBody;
};
AvoNetworkCallsHandlerLite.prototype.createBaseCallBody = function () {
var body = {
apiKey: this.apiKey,
appName: this.appName,
appVersion: this.appVersion,
libVersion: this.libVersion,
env: this.envName,
libPlatform: "web",
messageId: AvoGuid_1.default.newGuid(),
trackingId: "",
createdAt: new Date().toISOString(),
sessionId: "",
streamId: "",
samplingRate: this.samplingRate
};
return body;
};
/**
* Calls Inspector API immediately with a single event (bypasses batching).
* Used when event spec validation is available.
* Note: Does not drop due to sampling - validated events are always sent.
*/
AvoNetworkCallsHandlerLite.prototype.callInspectorImmediately = function (eventBody, onCompleted) {
if (AvoInspectorLite_1.AvoInspector.shouldLog) {
console.log("Avo Inspector: calling inspector immediately (with validation)", eventBody.eventName);
console.log("Avo Inspector: event body", eventBody);
}
this.callInspectorApi([eventBody], onCompleted);
};
/**
* Check if event should be dropped based on sampling rate.
*/
AvoNetworkCallsHandlerLite.prototype.shouldDropBySampling = function () {
return Math.random() > this.samplingRate;
};
/**
* Core Inspector API call logic shared by batch and immediate calls.
*
* Bodies large enough to be worth it are gzipped (with Content-Encoding: gzip)
* to cut network volume ~10x. When CompressionStream is unavailable or
* compression fails, the body is sent uncompressed exactly as before —
* synchronously in the unavailable case, so legacy timing is preserved.
*/
AvoNetworkCallsHandlerLite.prototype.callInspectorApi = function (events, onCompleted) {
var _this = this;
var body = JSON.stringify(events);
if (typeof CompressionStream === "undefined" ||
body.length < gzipMinBodyLength) {
this.sendTrackingRequest(body, false, onCompleted);
return;
}
gzip(body).then(function (compressed) {
if (compressed !== null) {
_this.sendTrackingRequest(compressed, true, onCompleted);
}
else {
_this.sendTrackingRequest(body, false, onCompleted);
}
});
};
AvoNetworkCallsHandlerLite.prototype.sendTrackingRequest = function (body, isGzipped, onCompleted) {
var _this = this;
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("POST", AvoNetworkCallsHandlerLite.trackingEndpoint, true);
xmlhttp.setRequestHeader("Content-Type", "text/plain");
if (isGzipped) {
xmlhttp.setRequestHeader("Content-Encoding", "gzip");
}
xmlhttp.timeout = AvoInspectorLite_1.AvoInspector.networkTimeout;
xmlhttp.send(body);
xmlhttp.onload = function () {
if (xmlhttp.status !== 200) {
onCompleted(new Error("Error ".concat(xmlhttp.status, ": ").concat(xmlhttp.statusText)));
}
else {
var response = void 0;
try {
response = JSON.parse(xmlhttp.response);
}
catch (e) {
onCompleted(new Error("Failed to parse response: ".concat(e instanceof Error ? e.message : String(e))));
return;
}
if (response != null &&
typeof response.samplingRate === "number" &&
!isNaN(response.samplingRate)) {
_this.samplingRate = response.samplingRate;
}
onCompleted(null);
}
};
xmlhttp.onerror = function () {
onCompleted(new Error("Request failed"));
};
xmlhttp.ontimeout = function () {
onCompleted(new Error("Request timed out"));
};
};
AvoNetworkCallsHandlerLite.trackingEndpoint = "https://api.avo.app/inspector/v1/track";
return AvoNetworkCallsHandlerLite;
}());
exports.AvoNetworkCallsHandlerLite = AvoNetworkCallsHandlerLite;
exports.AvoNetworkCallsHandler = AvoNetworkCallsHandlerLite;