@amplitude/analytics-core
Version:
179 lines • 7.75 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.networkObserver = exports.NetworkObserver = exports.NetworkEventCallback = exports.getRequestBodyLength = void 0;
var tslib_1 = require("tslib");
var global_scope_1 = require("./global-scope");
var uuid_1 = require("./utils/uuid");
var MAXIMUM_ENTRIES = 100;
function getRequestBodyLength(body) {
var e_1, _a;
var global = (0, global_scope_1.getGlobalScope)();
if (!(global === null || global === void 0 ? void 0 : global.TextEncoder)) {
return;
}
var TextEncoder = global.TextEncoder;
if (typeof body === 'string') {
return new TextEncoder().encode(body).length;
}
else if (body instanceof Blob) {
return body.size;
}
else if (body instanceof URLSearchParams) {
return new TextEncoder().encode(body.toString()).length;
}
else if (body instanceof ArrayBuffer) {
return body.byteLength;
}
else if (ArrayBuffer.isView(body)) {
return body.byteLength;
}
else if (body instanceof FormData) {
// Estimating only for text parts; not accurate for files
var formData = body;
var total = 0;
var count = 0;
try {
for (var _b = tslib_1.__values(formData.entries()), _c = _b.next(); !_c.done; _c = _b.next()) {
var _d = tslib_1.__read(_c.value, 2), key = _d[0], value = _d[1];
total += key.length;
if (typeof value === 'string') {
total += new TextEncoder().encode(value).length;
}
else if (value.size) {
// if we encounter a "File" type, we should not count it and just return undefined
total += value.size;
}
// terminate if we reach the maximum number of entries
// to avoid performance issues in case of very large FormDataß
if (++count >= MAXIMUM_ENTRIES) {
return;
}
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
}
finally { if (e_1) throw e_1.error; }
}
return total;
}
// Stream or unknown
return;
}
exports.getRequestBodyLength = getRequestBodyLength;
var NetworkEventCallback = /** @class */ (function () {
function NetworkEventCallback(callback, id) {
if (id === void 0) { id = (0, uuid_1.UUID)(); }
this.callback = callback;
this.id = id;
}
return NetworkEventCallback;
}());
exports.NetworkEventCallback = NetworkEventCallback;
var NetworkObserver = /** @class */ (function () {
function NetworkObserver(logger) {
var _a;
this.eventCallbacks = new Map();
this.isObserving = false;
var globalScope = (0, global_scope_1.getGlobalScope)();
if (!NetworkObserver.isSupported()) {
/* istanbul ignore next */
logger === null || logger === void 0 ? void 0 : logger.error('Fetch API is not supported in this environment.');
return;
}
this.globalScope = globalScope;
/* istanbul ignore next */
this.originalFetch = (_a = this.globalScope) === null || _a === void 0 ? void 0 : _a.fetch;
}
NetworkObserver.isSupported = function () {
var globalScope = (0, global_scope_1.getGlobalScope)();
return !!globalScope && !!globalScope.fetch;
};
NetworkObserver.prototype.subscribe = function (eventCallback) {
this.eventCallbacks.set(eventCallback.id, eventCallback);
if (!this.isObserving) {
this.observeFetch();
this.isObserving = true;
}
};
NetworkObserver.prototype.unsubscribe = function (eventCallback) {
this.eventCallbacks.delete(eventCallback.id);
if (this.originalFetch && this.globalScope && this.eventCallbacks.size === 0 && this.isObserving) {
this.globalScope.fetch = this.originalFetch;
this.isObserving = false;
}
};
NetworkObserver.prototype.triggerEventCallbacks = function (event) {
this.eventCallbacks.forEach(function (callback) {
callback.callback(event);
});
};
NetworkObserver.prototype.observeFetch = function () {
var _this = this;
/* istanbul ignore next */
if (!this.globalScope || !this.originalFetch) {
return;
}
var originalFetch = this.globalScope.fetch;
this.globalScope.fetch = function (input, init) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
var startTime, requestEvent, response, endTime, headers_1, contentLength_1, error_1, endTime, typedError;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
startTime = Date.now();
requestEvent = {
timestamp: startTime,
startTime: startTime,
type: 'fetch',
method: (init === null || init === void 0 ? void 0 : init.method) || 'GET',
url: input.toString(),
requestHeaders: init === null || init === void 0 ? void 0 : init.headers,
requestBodySize: getRequestBodyLength(init === null || init === void 0 ? void 0 : init.body),
};
_a.label = 1;
case 1:
_a.trys.push([1, 3, , 4]);
return [4 /*yield*/, originalFetch(input, init)];
case 2:
response = _a.sent();
endTime = Date.now();
requestEvent.status = response.status;
requestEvent.duration = endTime - startTime;
requestEvent.startTime = startTime;
requestEvent.endTime = endTime;
headers_1 = {};
contentLength_1 = undefined;
response.headers.forEach(function (value, key) {
headers_1[key] = value;
if (key === 'content-length') {
contentLength_1 = parseInt(value, 10) || undefined;
}
});
requestEvent.responseHeaders = headers_1;
requestEvent.responseBodySize = contentLength_1;
this.triggerEventCallbacks(requestEvent);
return [2 /*return*/, response];
case 3:
error_1 = _a.sent();
endTime = Date.now();
requestEvent.duration = endTime - startTime;
typedError = error_1;
requestEvent.error = {
name: typedError.name || 'UnknownError',
message: typedError.message || 'An unknown error occurred',
};
this.triggerEventCallbacks(requestEvent);
throw error_1;
case 4: return [2 /*return*/];
}
});
}); };
};
return NetworkObserver;
}());
exports.NetworkObserver = NetworkObserver;
// singleton instance of NetworkObserver
exports.networkObserver = new NetworkObserver();
//# sourceMappingURL=network-observer.js.map
;