@apm-insight-web/rangers-mini-sdk
Advanced tools
Comparing version 0.0.5 to 0.0.6
@@ -96,2 +96,6 @@ 'use strict'; | ||
ReportEventType["JS_ERROR"] = "js_error"; | ||
ReportEventType["REQUEST"] = "ajax"; | ||
ReportEventType["SET_DATA"] = "set_data"; | ||
ReportEventType["ON_READY"] = "on_ready"; | ||
ReportEventType["FIRST_LOAD"] = "first_load"; | ||
})(exports.ReportEventType || (exports.ReportEventType = {})); | ||
@@ -103,7 +107,137 @@ var BackendEventType; | ||
BackendEventType["ENV_INFO"] = "env_info"; | ||
BackendEventType["PATH"] = "path"; | ||
BackendEventType["NETWORK_STATUS"] = "network_status"; | ||
BackendEventType["JS_ERROR"] = "js_error"; | ||
BackendEventType["GLOBAL_JS_ERROR"] = "global_js_error"; | ||
BackendEventType["APP_SESSION"] = "app_session"; | ||
BackendEventType["PAGE_SESSION"] = "page_session"; | ||
BackendEventType["REQUEST"] = "request"; | ||
BackendEventType["SET_DATA"] = "set_data"; | ||
BackendEventType["PAGE_ON_READY"] = "page_on_ready"; | ||
BackendEventType["PAGE_FIRST_LOAD"] = "page_first_load"; | ||
})(BackendEventType || (BackendEventType = {})); | ||
/* eslint-disable @typescript-eslint/ban-types */ | ||
function isObject(what) { | ||
return typeof what === 'object' && what !== null && !isArray(what); | ||
} | ||
function isString(what) { | ||
return Object.prototype.toString.call(what) === '[object String]'; | ||
} | ||
function isArray(what) { | ||
return Object.prototype.toString.call(what) === '[object Array]'; | ||
} | ||
function isUndefined(what) { | ||
return what === undefined; | ||
} | ||
function isNull(what) { | ||
return what === null; | ||
} | ||
var BaseClient = /** @class */ (function () { | ||
function BaseClient() { | ||
} | ||
// 发送事件统一入口 | ||
BaseClient.prototype.sendEvent = function (event) { | ||
var _modifiedOptions = this.getEventToBeSent(event); | ||
if (_modifiedOptions) { | ||
this.idleSendEvent(_modifiedOptions); | ||
} | ||
}; | ||
BaseClient.prototype.getEventToBeSent = function (event) { | ||
var _modifiedOptions = this._modifyEvent(event); | ||
if (!this._shouldSend(_modifiedOptions)) { | ||
return; | ||
} | ||
return _modifiedOptions; | ||
}; | ||
// 修改 event 内容 | ||
BaseClient.prototype._modifyEvent = function (event) { | ||
return event; | ||
}; | ||
// event 是否发送 | ||
BaseClient.prototype._shouldSend = function (_event) { | ||
return true; | ||
}; | ||
// 需要实现的发送方法 | ||
BaseClient.prototype._send = function (_event) { | ||
return; | ||
}; | ||
// 主进程空闲时间发送 | ||
BaseClient.prototype.idleSendEvent = function (event) { | ||
this._send(event); | ||
}; | ||
return BaseClient; | ||
}()); | ||
var MAX_BATCH_REPORT_LENGTH = 10; | ||
var BATCH_REPORT_WAIT = 1000; | ||
// T: monitor 上报的数据类型, 如 ISendParams | ||
// B: 接口上报的每个 item 的数据类型, 如 BatchEventItem | ||
var BatchClient = /** @class */ (function (_super) { | ||
__extends(BatchClient, _super); | ||
function BatchClient(props) { | ||
var _a, _b; | ||
var _this = _super.call(this) || this; | ||
_this.reportQueue = []; | ||
_this.isReady = true; | ||
_this.batchReportLength = (_a = props.maxBatchReportLength) !== null && _a !== void 0 ? _a : MAX_BATCH_REPORT_LENGTH; | ||
_this.batchReportWait = (_b = props.batchReportWait) !== null && _b !== void 0 ? _b : BATCH_REPORT_WAIT; | ||
_this.batchReportTimeout = null; | ||
return _this; | ||
} | ||
Object.defineProperty(BatchClient.prototype, "ready", { | ||
/** | ||
* 上传锁,设置为 false 时所有请求将会被推进列队,等待设置回 true 时一次发出。 | ||
* | ||
* 默认为 true 不上锁,以保证旧代码不受影响。 | ||
* | ||
*/ | ||
get: function () { | ||
return this.isReady; | ||
}, | ||
set: function (v) { | ||
this.isReady = v; | ||
if (this.isReady) { | ||
this._uploadQueue(); | ||
} | ||
}, | ||
enumerable: false, | ||
configurable: true | ||
}); | ||
BatchClient.prototype._send = function (options) { | ||
var _this = this; | ||
var params = this.buildParams(options); | ||
if (isNull(params) || isUndefined(params)) | ||
return; | ||
this.reportQueue.push(params); | ||
if (!this.isReady) | ||
return; | ||
if (this.reportQueue.length >= this.batchReportLength) { | ||
this._uploadQueue(); | ||
} | ||
if (this.batchReportTimeout) { | ||
clearTimeout(this.batchReportTimeout); | ||
} | ||
this.batchReportTimeout = setTimeout(function () { | ||
_this._uploadQueue(); | ||
}, this.batchReportWait); | ||
}; | ||
BatchClient.prototype._uploadQueue = function () { | ||
if (!this.reportQueue.length || !this.ready) { | ||
return; | ||
} | ||
var body = { | ||
ev_type: 'batch', | ||
list: this.reportQueue, | ||
}; | ||
this.reportQueue = []; | ||
this._request({ event: body, type: 'post' }); | ||
}; | ||
// 需要实现的方法 | ||
BatchClient.prototype._request = function (_options) { | ||
return; | ||
}; | ||
return BatchClient; | ||
}(BaseClient)); | ||
/** | ||
@@ -180,5 +314,37 @@ * 生成uuid | ||
}; | ||
var pick = function (obj, keys) { | ||
var newObj = {}; | ||
keys.forEach(function (key) { | ||
newObj[key] = obj[key]; | ||
}); | ||
return newObj; | ||
}; | ||
// https://stackoverflow.com/a/12205668 | ||
var strSize = function (str) { | ||
return encodeURI(str).split(/%(?:u[0-9A-F]{2})?[0-9A-F]{2}|./).length - 1; | ||
}; | ||
var getDataLength = function (d) { | ||
return d | ||
? d instanceof ArrayBuffer | ||
? d.byteLength | ||
: isObject(d) | ||
? strSize(serialize(d)) | ||
: d.length | ||
? d.length | ||
: 0 | ||
: 0; | ||
}; | ||
var getContentLength = function (res) { | ||
var _a, _b, _c; | ||
var contentLengthKey = (_b = Object.keys((_a = res.header) !== null && _a !== void 0 ? _a : {}).find(function (k) { return k.toLowerCase() === 'content-legth'; })) !== null && _b !== void 0 ? _b : ''; | ||
if ((_c = res.header) === null || _c === void 0 ? void 0 : _c[contentLengthKey]) { | ||
return parseInt(res.header[contentLengthKey]); | ||
} | ||
return getDataLength(res.data); | ||
}; | ||
var UnhandledRejection = 'UnhandledRejection'; | ||
var DEVICE_ID_STORAGE_KEY = 'RANGERS_MINI_SDK_DEVICE_ID'; | ||
var RANGERS_PAGE_SESSION_ID = 'RANGERS_PAGE_SESSION_ID'; | ||
var RANGERS_PAGE_TIMING = 'RANGERS_PAGE_TIMING'; | ||
@@ -212,4 +378,3 @@ var BackendType; | ||
(function (SDKMethod) { | ||
SDKMethod["onError"] = "onError"; | ||
SDKMethod["onUnhandledRejection"] = "onUnhandledRejection"; | ||
SDKMethod["request"] = "request"; | ||
})(SDKMethod || (SDKMethod = {})); | ||
@@ -226,2 +391,27 @@ | ||
}; | ||
var hookComponentLifeCycles = function (lifeCycleConfigFunction, methods, wrapFn) { | ||
return function (config) { | ||
if (!config.methods) { | ||
config.methods = {}; | ||
} | ||
methods.forEach(function (method) { | ||
var originalHandler = config.methods[method]; | ||
config.methods[method] = wrapFn(method, originalHandler); | ||
}); | ||
return lifeCycleConfigFunction(config); | ||
}; | ||
}; | ||
var hookObjectMethods = function (target, methods, wrapFn) { | ||
methods.forEach(function (method) { | ||
if (target[method]) { | ||
var originalHandler = target[method]; | ||
Object.defineProperty(target, method, { | ||
enumerable: false, | ||
configurable: false, | ||
writable: false, | ||
value: wrapFn(method, originalHandler).bind(target), | ||
}); | ||
} | ||
}); | ||
}; | ||
var unknown = 'unknown'; | ||
@@ -340,3 +530,11 @@ var getHostPlatform = function () { | ||
this.originalPageFn = Page; | ||
this.reportUrl = ''; | ||
this.deviceId = ''; | ||
this.appSessionId = ''; | ||
this.pageSessionId = ''; | ||
this.capturedContext = null; | ||
this.taskStack = []; | ||
this.taskTracks = {}; | ||
this.options = options; | ||
this.pid = options.pid; | ||
this.callback = callback; | ||
@@ -348,5 +546,7 @@ // init later so the subclass can inject sdk | ||
this.transport = this.setupTransport(); | ||
this.collectDeviceId(); | ||
App = this.hookAppFn(App); | ||
Page = this.hookPageFn(Page); | ||
this.collectDeviceId(); | ||
Component = this.hookComponentFn(Component); | ||
this.hookSdk(); | ||
if (this.sdk.onNetworkStatusChange) { | ||
@@ -419,8 +619,8 @@ this.sdk.onNetworkStatusChange(function (options) { | ||
Backend.prototype.setupTransport = function () { | ||
var that = this; | ||
var reportUrl = getBatchReportURL(this.options.reportDomain, this.options.reportPath); | ||
var _this = this; | ||
this.reportUrl = getBatchReportURL(this.options.reportDomain, this.options.reportPath); | ||
return { | ||
sendEvent: function (event) { | ||
that.sdk.request({ | ||
url: reportUrl, | ||
_this.sdk.request({ | ||
url: _this.reportUrl, | ||
method: 'POST', | ||
@@ -431,3 +631,3 @@ data: event, | ||
request: function (options) { | ||
that.sdk.request(options); | ||
_this.sdk.request(options); | ||
}, | ||
@@ -437,16 +637,21 @@ }; | ||
Backend.prototype.hookAppFn = function (App) { | ||
var that = this; | ||
var callback = this.callback; | ||
return hookLifeCycles(App, [ | ||
AppMethod.onLaunch, | ||
AppMethod.onShow, | ||
AppMethod.onHide, | ||
AppMethod.onError, | ||
AppMethod.onPageNotFound, | ||
AppMethod.onUnhandledRejection, | ||
], function (method, originalHandler) { | ||
return function (options) { | ||
return hookLifeCycles(App, [AppMethod.onLaunch, AppMethod.onShow, AppMethod.onHide, AppMethod.onError, AppMethod.onUnhandledRejection], function (method, originalHandler) { | ||
function wrappedAppHandler(options) { | ||
switch (method) { | ||
case AppMethod.onShow: { | ||
var appSessionId = uuid(); | ||
that.appSessionId = appSessionId; | ||
callback({ | ||
ev_type: BackendEventType.APP_SESSION, | ||
data: { | ||
appSessionId: appSessionId, | ||
}, | ||
}); | ||
break; | ||
} | ||
case AppMethod.onError: { | ||
callback({ | ||
ev_type: BackendEventType.JS_ERROR, | ||
ev_type: BackendEventType.GLOBAL_JS_ERROR, | ||
data: makeError(options), | ||
@@ -459,3 +664,3 @@ }); | ||
callback({ | ||
ev_type: BackendEventType.JS_ERROR, | ||
ev_type: BackendEventType.GLOBAL_JS_ERROR, | ||
data: data, | ||
@@ -468,29 +673,271 @@ }); | ||
originalHandler === null || originalHandler === void 0 ? void 0 : originalHandler.call(this, options); | ||
}; | ||
} | ||
return wrappedAppHandler; | ||
}); | ||
}; | ||
Backend.prototype.hookPageFn = function (Page) { | ||
return hookLifeCycles(Page, [PageMethod.onLoad, PageMethod.onShow, PageMethod.onReady], this._pageLifeCycleHandlers()); | ||
}; | ||
Backend.prototype.hookComponentFn = function (Component) { | ||
return hookComponentLifeCycles(Component, [PageMethod.onLoad, PageMethod.onShow, PageMethod.onReady], this._pageLifeCycleHandlers()); | ||
}; | ||
Backend.prototype._pageLifeCycleHandlers = function () { | ||
var that = this; | ||
var callback = this.callback; | ||
return hookLifeCycles(Page, [ | ||
PageMethod.onLoad, | ||
PageMethod.onShow, | ||
PageMethod.onReady, | ||
PageMethod.onHide, | ||
PageMethod.onUnload, | ||
PageMethod.onPullDownRefresh, | ||
], function (method, originalHandler) { | ||
return function (options) { | ||
return function (method, originalHandler) { | ||
function callOrigin(options) { | ||
try { | ||
originalHandler === null || originalHandler === void 0 ? void 0 : originalHandler.call(this, options); | ||
} | ||
catch (err) { | ||
callback({ | ||
ev_type: BackendEventType.JS_ERROR, | ||
data: { | ||
name: err.name, | ||
message: err.message, | ||
stack: err.stack, | ||
rawMessage: String(err), | ||
}, | ||
context: that.getContext(), | ||
}); | ||
} | ||
} | ||
function wrappedPageHandler(options) { | ||
var pid = getPid(); | ||
switch (method) { | ||
case PageMethod.onLoad: { | ||
that.pid = pid; | ||
that.hookSetData(this); | ||
var onLoadStart = Date.now(); | ||
var pageSessionId = [that.deviceId, that.appSessionId, that.pid, onLoadStart].join('|'); | ||
that.pageSessionId = pageSessionId; | ||
that.taskTracks[pageSessionId] = { | ||
tasks: [], | ||
timing: { | ||
firstReqStart: onLoadStart, | ||
firstReqEnd: 0, | ||
firstLoadStart: onLoadStart, | ||
firstLoadEnd: 0, | ||
}, | ||
ready: false, | ||
ended: false, | ||
}; | ||
callback({ | ||
ev_type: BackendEventType.PAGE_SESSION, | ||
data: { | ||
pid: pid, | ||
pageSessionId: pageSessionId, | ||
}, | ||
}); | ||
callOrigin.call(this, options); | ||
this[RANGERS_PAGE_SESSION_ID] = pageSessionId; | ||
this[RANGERS_PAGE_TIMING] = { | ||
onLoadStart: onLoadStart, | ||
onLoadEnd: Date.now(), | ||
onShowStart: 0, | ||
onShowEnd: 0, | ||
onReadyStart: 0, | ||
onReadyEnd: 0, | ||
}; | ||
return; | ||
} | ||
case PageMethod.onShow: { | ||
var pid = getPid(); | ||
var pageSessionId = this[RANGERS_PAGE_SESSION_ID]; | ||
that.pageSessionId = pageSessionId; | ||
callback({ | ||
ev_type: BackendEventType.PATH, | ||
ev_type: BackendEventType.PAGE_SESSION, | ||
data: { | ||
pid: pid, | ||
pageSessionId: pageSessionId, | ||
}, | ||
}); | ||
break; | ||
var timing = this[RANGERS_PAGE_TIMING]; | ||
timing.onShowStart = timing.onShowStart || Date.now(); | ||
callOrigin.call(this, options); | ||
timing.onShowEnd = timing.onShowEnd || Date.now(); | ||
return; | ||
} | ||
case PageMethod.onReady: { | ||
var timing = this[RANGERS_PAGE_TIMING]; | ||
timing.onReadyStart = Date.now(); | ||
callOrigin.call(this, options); | ||
timing.onReadyEnd = Date.now(); | ||
callback({ | ||
ev_type: BackendEventType.PAGE_ON_READY, | ||
data: timing, | ||
}); | ||
var taskTrack = that.getTaskTrack(); | ||
taskTrack.ready = true; | ||
if (!taskTrack.timing.firstReqEnd && taskTrack.tasks.every(function (t) { return t.completed || t.type === 'SET_DATA'; })) { | ||
taskTrack.timing.firstReqEnd = taskTrack.timing.firstReqStart; // no first req | ||
} | ||
if (taskTrack.tasks.every(function (t) { return t.completed; })) { | ||
taskTrack.timing.firstLoadEnd = timing.onReadyEnd; | ||
that.endTrack(taskTrack); | ||
} | ||
return; | ||
} | ||
} | ||
originalHandler === null || originalHandler === void 0 ? void 0 : originalHandler.call(this, options); | ||
callOrigin.call(this, options); | ||
} | ||
return wrappedPageHandler; | ||
}; | ||
}; | ||
Backend.prototype.hookSdk = function () { | ||
this.hookRequest(); | ||
}; | ||
Backend.prototype.hookRequest = function () { | ||
var _this = this; | ||
var that = this; | ||
var callback = this.callback; | ||
hookObjectMethods(this.sdk, [SDKMethod.request], function (_, request) { return function (options) { | ||
var _b; | ||
if (_this.shouldIgnoreUrl(options.url)) { | ||
return request(options); | ||
} | ||
var originalSuccess = options.success; | ||
var originalFail = options.fail; | ||
var originalComplete = options.complete; | ||
var req_size = getDataLength((_b = options.data) !== null && _b !== void 0 ? _b : ''); | ||
var taskId = _this.addTask('REQUEST'); | ||
var req_start_time = Date.now(); | ||
var success = _this.captureContext(function (res) { | ||
var _a, _b; | ||
var req_end_time = Date.now(); | ||
callback({ | ||
ev_type: BackendEventType.REQUEST, | ||
data: { | ||
req_url: options.url, | ||
method: (_a = options.method) !== null && _a !== void 0 ? _a : 'GET', | ||
err_msg: res.statusCode >= 300 ? serialize(res.data) : '', | ||
req_status: 1, | ||
req_start_time: req_start_time, | ||
req_end_time: req_end_time, | ||
resp_status: res.statusCode, | ||
header: (_b = options.header) !== null && _b !== void 0 ? _b : {}, | ||
req_param: res.statusCode >= 300 ? serialize(options.data) : '', | ||
req_size: req_size, | ||
resp_size: getContentLength(res), | ||
perf: res.profile | ||
? pick(res.profile, [ | ||
'redirectStart', | ||
'redirectEnd', | ||
'fetchStart', | ||
'domainLookupStart', | ||
'domainLookupEnd', | ||
'connectStart', | ||
'connectEnd', | ||
'SSLconnectionStart', | ||
'SSLconnectionEnd', | ||
'requestStart', | ||
'requestEnd', | ||
'responseStart', | ||
'responseEnd', | ||
'rtt', | ||
'httpRttEstimate', | ||
'transportRttEstimate', | ||
]) | ||
: {}, | ||
}, | ||
context: that.capturedContext, | ||
}); | ||
that.taskStack.push(taskId); | ||
try { | ||
originalSuccess === null || originalSuccess === void 0 ? void 0 : originalSuccess.call(this, res); | ||
} | ||
finally { | ||
that.taskStack.pop(); | ||
} | ||
}); | ||
var fail = _this.captureContext(function (res) { | ||
var _a, _b, _c; | ||
var req_end_time = Date.now(); | ||
callback({ | ||
ev_type: BackendEventType.REQUEST, | ||
data: { | ||
req_url: options.url, | ||
method: (_a = options.method) !== null && _a !== void 0 ? _a : 'GET', | ||
err_msg: (_b = res.errMsg) !== null && _b !== void 0 ? _b : '', | ||
req_status: 0, | ||
req_start_time: req_start_time, | ||
req_end_time: req_end_time, | ||
resp_status: 0, | ||
req_param: serialize(options.data), | ||
header: (_c = options.header) !== null && _c !== void 0 ? _c : {}, | ||
req_size: req_size, | ||
resp_size: 0, | ||
perf: {}, | ||
}, | ||
context: that.capturedContext, | ||
}); | ||
that.taskStack.push(taskId); | ||
try { | ||
originalFail === null || originalFail === void 0 ? void 0 : originalFail.call(this, res); | ||
} | ||
finally { | ||
that.taskStack.pop(); | ||
} | ||
}); | ||
var complete = _this.captureContext(function (res) { | ||
that.taskStack.push(taskId); | ||
try { | ||
originalComplete === null || originalComplete === void 0 ? void 0 : originalComplete.call(this, res); | ||
} | ||
finally { | ||
that.taskStack.pop(); | ||
} | ||
that.endTask(taskId, Date.now()); | ||
}); | ||
var newOptions = __assign(__assign({}, options), { success: success, | ||
fail: fail, | ||
complete: complete }); | ||
return request(newOptions); | ||
}; }); | ||
}; | ||
Backend.prototype.shouldIgnoreUrl = function (url) { | ||
return url === this.reportUrl; | ||
}; | ||
Backend.prototype.hookSetData = function (page) { | ||
var that = this; | ||
hookObjectMethods(page, ['setData'], function (_, setData) { | ||
return function wrappedSetData(data, callback) { | ||
var _this = this; | ||
var originalCallback = callback; | ||
var taskId = that.addTask('SET_DATA'); | ||
var setDataStart = Date.now(); | ||
var wrappedCallback = that.captureContext(function () { | ||
var setDataEnd = Date.now(); | ||
var dataJson = serialize(data); | ||
that.callback({ | ||
ev_type: BackendEventType.SET_DATA, | ||
data: { | ||
setDataStart: setDataStart, | ||
setDataEnd: setDataEnd, | ||
setDataSize: strSize(dataJson), | ||
data: dataJson, | ||
}, | ||
context: that.capturedContext, | ||
}); | ||
that.taskStack.push(taskId); | ||
try { | ||
originalCallback === null || originalCallback === void 0 ? void 0 : originalCallback.call(_this); | ||
} | ||
catch (err) { | ||
that.callback({ | ||
ev_type: BackendEventType.JS_ERROR, | ||
data: { | ||
name: err.name, | ||
message: err.message, | ||
stack: err.stack, | ||
rawMessage: String(err), | ||
}, | ||
context: that.getContext(), | ||
}); | ||
} | ||
finally { | ||
that.taskStack.pop(); | ||
} | ||
that.endTask(taskId, setDataEnd); | ||
}); | ||
setData.call(this, data, wrappedCallback); | ||
}; | ||
@@ -505,2 +952,3 @@ }); | ||
} | ||
this.deviceId = deviceId; | ||
this.callback({ | ||
@@ -513,2 +961,95 @@ ev_type: BackendEventType.DEVICE_ID, | ||
}; | ||
Backend.prototype.getContext = function () { | ||
var _a; | ||
return (_a = this.capturedContext) !== null && _a !== void 0 ? _a : this.currentContext(); | ||
}; | ||
Backend.prototype.currentContext = function () { | ||
return { | ||
pid: this.pid, | ||
pageSessionId: this.pageSessionId, | ||
context: this.options.context, | ||
}; | ||
}; | ||
Backend.prototype.captureContext = function (fn) { | ||
var that = this; | ||
var context = this.getContext(); | ||
// log('captureContext', context) | ||
return function () { | ||
var args = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
args[_i] = arguments[_i]; | ||
} | ||
// log('call with capturedContext', context) | ||
// log('currentContext', that.currentContext()) | ||
that.capturedContext = context; | ||
var res; | ||
try { | ||
res = fn.apply(this, args); | ||
} | ||
catch (err) { | ||
that.callback({ | ||
ev_type: BackendEventType.JS_ERROR, | ||
data: { | ||
name: err.name, | ||
message: err.message, | ||
stack: err.stack, | ||
rawMessage: String(err), | ||
}, | ||
context: context, | ||
}); | ||
} | ||
finally { | ||
that.capturedContext = null; | ||
} | ||
return res; | ||
}; | ||
}; | ||
Backend.prototype.addTask = function (type) { | ||
var taskTrack = this.getTaskTrack(); | ||
if (!taskTrack || taskTrack.ended) { | ||
return 0; | ||
} | ||
var parentTaskId = this.taskStack.length && this.taskStack[this.taskStack.length - 1]; | ||
if (taskTrack.ready && (!parentTaskId || !taskTrack.tasks.some(function (t) { return t.id === parentTaskId; }))) { | ||
return 0; | ||
} | ||
var id = taskTrack.tasks.length + 1; | ||
taskTrack.tasks.push({ | ||
id: id, | ||
type: type, | ||
completed: false, | ||
}); | ||
return id; | ||
}; | ||
Backend.prototype.endTask = function (id, now) { | ||
var taskTrack = this.getTaskTrack(); | ||
if (id <= 0 || !taskTrack || taskTrack.ended) { | ||
return; | ||
} | ||
var task = taskTrack.tasks[id - 1]; | ||
task.completed = true; | ||
if (!taskTrack.timing.firstReqEnd && task.type === 'REQUEST') { | ||
taskTrack.timing.firstReqEnd = now; | ||
} | ||
if (taskTrack.tasks.every(function (t) { return t.completed; })) { | ||
taskTrack.timing.firstLoadEnd = now; | ||
this.endTrack(taskTrack); | ||
} | ||
}; | ||
Backend.prototype.endTrack = function (taskTrack) { | ||
if (!taskTrack.ready) { | ||
return; | ||
} | ||
taskTrack.ended = true; | ||
this.callback({ | ||
ev_type: BackendEventType.PAGE_FIRST_LOAD, | ||
data: taskTrack.timing, | ||
context: this.getContext(), | ||
}); | ||
}; | ||
Backend.prototype.getTaskTrack = function () { | ||
var context = this.getContext(); | ||
var pageSessionId = context.pageSessionId; | ||
return this.taskTracks[pageSessionId]; | ||
}; | ||
return Backend; | ||
@@ -647,126 +1188,2 @@ }()); | ||
/* eslint-disable @typescript-eslint/ban-types */ | ||
function isObject(what) { | ||
return typeof what === 'object' && what !== null && !isArray(what); | ||
} | ||
function isString(what) { | ||
return Object.prototype.toString.call(what) === '[object String]'; | ||
} | ||
function isArray(what) { | ||
return Object.prototype.toString.call(what) === '[object Array]'; | ||
} | ||
function isUndefined(what) { | ||
return what === undefined; | ||
} | ||
function isNull(what) { | ||
return what === null; | ||
} | ||
var BaseClient = /** @class */ (function () { | ||
function BaseClient() { | ||
} | ||
// 发送事件统一入口 | ||
BaseClient.prototype.sendEvent = function (event) { | ||
var _modifiedOptions = this.getEventToBeSent(event); | ||
if (_modifiedOptions) { | ||
this.idleSendEvent(_modifiedOptions); | ||
} | ||
}; | ||
BaseClient.prototype.getEventToBeSent = function (event) { | ||
var _modifiedOptions = this._modifyEvent(event); | ||
if (!this._shouldSend(_modifiedOptions)) { | ||
return; | ||
} | ||
return _modifiedOptions; | ||
}; | ||
// 修改 event 内容 | ||
BaseClient.prototype._modifyEvent = function (event) { | ||
return event; | ||
}; | ||
// event 是否发送 | ||
BaseClient.prototype._shouldSend = function (_event) { | ||
return true; | ||
}; | ||
// 需要实现的发送方法 | ||
BaseClient.prototype._send = function (_event) { | ||
return; | ||
}; | ||
// 主进程空闲时间发送 | ||
BaseClient.prototype.idleSendEvent = function (event) { | ||
this._send(event); | ||
}; | ||
return BaseClient; | ||
}()); | ||
var MAX_BATCH_REPORT_LENGTH = 10; | ||
var BATCH_REPORT_WAIT = 1000; | ||
// T: monitor 上报的数据类型, 如 ISendParams | ||
// B: 接口上报的每个 item 的数据类型, 如 BatchEventItem | ||
var BatchClient = /** @class */ (function (_super) { | ||
__extends(BatchClient, _super); | ||
function BatchClient(props) { | ||
var _a, _b; | ||
var _this = _super.call(this) || this; | ||
_this.reportQueue = []; | ||
_this.isReady = true; | ||
_this.batchReportLength = (_a = props.maxBatchReportLength) !== null && _a !== void 0 ? _a : MAX_BATCH_REPORT_LENGTH; | ||
_this.batchReportWait = (_b = props.batchReportWait) !== null && _b !== void 0 ? _b : BATCH_REPORT_WAIT; | ||
_this.batchReportTimeout = null; | ||
return _this; | ||
} | ||
Object.defineProperty(BatchClient.prototype, "ready", { | ||
/** | ||
* 上传锁,设置为 false 时所有请求将会被推进列队,等待设置回 true 时一次发出。 | ||
* | ||
* 默认为 true 不上锁,以保证旧代码不受影响。 | ||
* | ||
*/ | ||
get: function () { | ||
return this.isReady; | ||
}, | ||
set: function (v) { | ||
this.isReady = v; | ||
if (this.isReady) { | ||
this._uploadQueue(); | ||
} | ||
}, | ||
enumerable: false, | ||
configurable: true | ||
}); | ||
BatchClient.prototype._send = function (options) { | ||
var _this = this; | ||
var params = this.buildParams(options); | ||
if (isNull(params) || isUndefined(params)) | ||
return; | ||
this.reportQueue.push(params); | ||
if (!this.isReady) | ||
return; | ||
if (this.reportQueue.length >= this.batchReportLength) { | ||
this._uploadQueue(); | ||
} | ||
if (this.batchReportTimeout) { | ||
clearTimeout(this.batchReportTimeout); | ||
} | ||
this.batchReportTimeout = setTimeout(function () { | ||
_this._uploadQueue(); | ||
}, this.batchReportWait); | ||
}; | ||
BatchClient.prototype._uploadQueue = function () { | ||
if (!this.reportQueue.length || !this.ready) { | ||
return; | ||
} | ||
var body = { | ||
ev_type: 'batch', | ||
list: this.reportQueue, | ||
}; | ||
this.reportQueue = []; | ||
this._request({ event: body, type: 'post' }); | ||
}; | ||
// 需要实现的方法 | ||
BatchClient.prototype._request = function (_options) { | ||
return; | ||
}; | ||
return BatchClient; | ||
}(BaseClient)); | ||
var DEFAULT_REPORT_DOMAIN = "" + "tbm.snssdk.com"; | ||
@@ -848,2 +1265,4 @@ var DEFAULT_REPORT_BATCH_PATH = '/monitor_microapp/collect'; | ||
_this.envInfo = {}; | ||
_this.appSessionId = ''; | ||
_this.pageSessionId = ''; | ||
_this.preQueue = []; | ||
@@ -869,3 +1288,3 @@ _this.ready = false; // lock the client | ||
}; | ||
MiniProgramClient.prototype.reportError = function (error) { | ||
MiniProgramClient.prototype.reportError = function (error, context) { | ||
var _a; | ||
@@ -880,11 +1299,11 @@ this.reportEvent({ | ||
breadcrumbs: __spread(this.breadcrumbs), | ||
}); | ||
}, context); | ||
}; | ||
MiniProgramClient.prototype.reportEvent = function (event) { | ||
MiniProgramClient.prototype.reportEvent = function (event, context) { | ||
if (!this.ready) { | ||
// 状态没有 ready 的时候,先存入队列 | ||
this.preQueue.push(event); | ||
this.preQueue.push(__assign(__assign({}, event), { context: context })); | ||
} | ||
else { | ||
_super.prototype.sendEvent.call(this, event); | ||
_super.prototype.sendEvent.call(this, __assign(__assign({}, event), { context: context })); | ||
} | ||
@@ -930,4 +1349,24 @@ }; | ||
} | ||
case BackendEventType.PATH: { | ||
if (this.pid !== event.data.pid) { | ||
case BackendEventType.GLOBAL_JS_ERROR: { | ||
this.reportEvent({ | ||
ev_type: exports.ReportEventType.JS_ERROR, | ||
event: event.data, | ||
breadcrumbs: __spread(this.breadcrumbs), | ||
}); | ||
break; | ||
} | ||
case BackendEventType.JS_ERROR: { | ||
this.reportEvent({ | ||
ev_type: exports.ReportEventType.JS_ERROR, | ||
event: event.data, | ||
breadcrumbs: __spread(this.breadcrumbs), | ||
}, event.context); | ||
break; | ||
} | ||
case BackendEventType.APP_SESSION: { | ||
this.appSessionId = event.data.appSessionId; | ||
break; | ||
} | ||
case BackendEventType.PAGE_SESSION: { | ||
if (this.pageSessionId !== event.data.pageSessionId) { | ||
this.addBreadcrumb(exports.BreadcrumbType.DOM, { | ||
@@ -938,2 +1377,3 @@ category: BreadcrumbCategory.ROUTE, | ||
this.pid = event.data.pid; | ||
this.pageSessionId = event.data.pageSessionId; | ||
this.sendPageView(); | ||
@@ -943,10 +1383,30 @@ } | ||
} | ||
case BackendEventType.JS_ERROR: { | ||
case BackendEventType.REQUEST: { | ||
this.reportEvent({ | ||
ev_type: exports.ReportEventType.JS_ERROR, | ||
ev_type: exports.ReportEventType.REQUEST, | ||
event: event.data, | ||
breadcrumbs: __spread(this.breadcrumbs), | ||
}, event.context); | ||
break; | ||
} | ||
case BackendEventType.SET_DATA: { | ||
this.reportEvent({ | ||
ev_type: exports.ReportEventType.SET_DATA, | ||
event: event.data, | ||
}, event.context); | ||
break; | ||
} | ||
case BackendEventType.PAGE_ON_READY: { | ||
this.reportEvent({ | ||
ev_type: exports.ReportEventType.ON_READY, | ||
event: event.data, | ||
}); | ||
break; | ||
} | ||
case BackendEventType.PAGE_FIRST_LOAD: { | ||
this.reportEvent({ | ||
ev_type: exports.ReportEventType.FIRST_LOAD, | ||
event: event.data, | ||
}, event.context); | ||
break; | ||
} | ||
} | ||
@@ -958,3 +1418,4 @@ }; | ||
MiniProgramClient.prototype.buildParams = function (options) { | ||
return __assign(__assign({}, options), { common: __assign(__assign({}, this.envInfo), { aid: this.config.aid, pid: this.pid, rangers_sdk_version: this.config.version, timestamp: Date.now(), user_unique_id: this.finderConfig ? this.finderConfig.user_unique_id : this.config.userId || this.deviceId, device_id: this.deviceId, session_id: this.config.sessionId, context: this.config.context }) }); | ||
var _a, _b, _c, _d, _e, _f; | ||
return __assign(__assign({}, options), { common: __assign(__assign({}, this.envInfo), { aid: this.config.aid, pid: (_b = (_a = options.context) === null || _a === void 0 ? void 0 : _a.pid) !== null && _b !== void 0 ? _b : this.pid, rangers_sdk_version: this.config.version, timestamp: Date.now(), user_unique_id: this.finderConfig ? this.finderConfig.user_unique_id : this.config.userId || this.deviceId, device_id: this.deviceId, session_id: this.config.sessionId, app_session_id: this.appSessionId, page_session_id: (_d = (_c = options.context) === null || _c === void 0 ? void 0 : _c.pageSessionId) !== null && _d !== void 0 ? _d : this.pageSessionId, context: (_f = (_e = options.context) === null || _e === void 0 ? void 0 : _e.context) !== null && _f !== void 0 ? _f : this.config.context }), context: undefined }); | ||
}; | ||
@@ -988,3 +1449,3 @@ /** | ||
function Rangers(options) { | ||
this.version = '0.0.5'; | ||
this.version = '0.0.6'; | ||
this.Backend = options.Backend; | ||
@@ -991,0 +1452,0 @@ this.defaultConfig = getDefaultConfig(); |
@@ -1,1 +0,1 @@ | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).RangersMiniSDK={})}(this,function(t){"use strict";var o=function(e,t){return(o=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])})(e,t)};function e(e,t){function n(){this.constructor=e}o(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}var n,r,a,i=function(){return(i=Object.assign||function(e){for(var t,n=1,o=arguments.length;n<o;n++)for(var r in t=arguments[n])Object.prototype.hasOwnProperty.call(t,r)&&(e[r]=t[r]);return e}).apply(this,arguments)};function s(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var o,r,i=n.call(e),s=[];try{for(;(void 0===t||0<t--)&&!(o=i.next()).done;)s.push(o.value)}catch(e){r={error:e}}finally{try{o&&!o.done&&(n=i.return)&&n.call(i)}finally{if(r)throw r.error}}return s}function c(){for(var e=[],t=0;t<arguments.length;t++)e=e.concat(s(arguments[t]));return e}function u(){var e=function(){for(var e=new Array(16),t=0,n=0;n<16;n++)0==(3&n)&&(t=4294967296*Math.random()),e[n]=t>>>((3&n)<<3)&255;return e}();return e[6]=15&e[6]|64,e[8]=63&e[8]|128,function(e){for(var t=[],n=0;n<256;++n)t[n]=(n+256).toString(16).substr(1);var o=0;return[t[e[o++]],t[e[o++]],t[e[o++]],t[e[o++]],"-",t[e[o++]],t[e[o++]],"-",t[e[o++]],t[e[o++]],"-",t[e[o++]],t[e[o++]],"-",t[e[o++]],t[e[o++]],t[e[o++]],t[e[o++]],t[e[+o]],t[e[15]]].join("")}(e)}(P=t.BreadcrumbType||(t.BreadcrumbType={})).DOM="dom",P.HTTP="http",(n=n||{}).ROUTE="route",(r=r||{}).BATCH="batch",(_=t.ReportEventType||(t.ReportEventType={})).PAGEVIEW="pageview",_.JS_ERROR="js_error",(P=a=a||{}).DEVICE_ID="device_id",P.FINDER_CONFIG="finder_config",P.ENV_INFO="env_info",P.PATH="path",P.NETWORK_STATUS="network_status",P.JS_ERROR="js_error";var p,d,f=u,h="RANGERS_MINI_SDK_DEVICE_ID";(_={}).TT="tt",_.WX="wx",_.MY="my",_.SWAN="swan",(P=p=p||{}).onLaunch="onLaunch",P.onShow="onShow",P.onHide="onHide",P.onError="onError",P.onUnhandledRejection="onUnhandledRejection",P.onPageNotFound="onPageNotFound",(_=d=d||{}).onLoad="onLoad",_.onShow="onShow",_.onReady="onReady",_.onHide="onHide",_.onUnload="onUnload",_.onPullDownRefresh="onPullDownRefresh",(P={}).onError="onError",P.onUnhandledRejection="onUnhandledRejection";function l(e,t,o){return function(n){return t.forEach(function(e){var t=n[e];n[e]=o(e,t)}),e(n)}}function v(){return{host_platform:"undefined"!=typeof wx?"wx":"undefined"!=typeof tt?"tt":"undefined"!=typeof my?"my":"undefined"!=typeof swan?"swan":y,host_app_name:y,host_app_version:y,platform:y,sdk_version:y,os_name:y,os_version:y,device_brand:y,device_model:y,screen_height:0,screen_width:0,resolution:y,pixel_ratio:0,network_type:y}}var y="unknown",_=(g.prototype.init=function(){var t=this;this.transport=this.setupTransport(),App=this.hookAppFn(App),Page=this.hookPageFn(Page),this.collectDeviceId(),this.sdk.onNetworkStatusChange&&this.sdk.onNetworkStatusChange(function(e){t.callback({ev_type:a.NETWORK_STATUS,data:{network_type:e.networkType}})})},g.prototype.sendEvent=function(e){var t;null!==(t=this.transport)&&void 0!==t&&t.sendEvent(e)},g.prototype.collectEnvInfo=function(){var n=this;Promise.all([new Promise(function(e,t){n.sdk.getSystemInfo({success:e,fail:t})}),new Promise(function(e,t){n.sdk.getNetworkType({success:e,fail:t})})]).then(function(e){var t=s(e,2),e=t[0],t=t[1];e&&t&&n.callback({ev_type:a.ENV_INFO,data:n.normalizeEnvInfo(e,t)})}).catch(function(){n.callback({ev_type:a.ENV_INFO,data:v()})})},g.prototype.collectFinderConfig=function(){var t=this;this.options.finderInstance&&this.options.finderInstance.getToken(function(e){t.callback({ev_type:a.FINDER_CONFIG,data:{user_unique_id:e.user_unique_id}})})},g.prototype.getStorage=function(e){return this.sdk.getStorageSync(e)},g.prototype.setStorage=function(e,t){return this.sdk.setStorageSync(e,t)},g.prototype.setupTransport=function(){var e,t,n=this,o=(e=this.options.reportDomain,t=this.options.reportPath,"https://"+e+t);return{sendEvent:function(e){n.sdk.request({url:o,method:"POST",data:e})},request:function(e){n.sdk.request(e)}}},g.prototype.hookAppFn=function(e){var s=this.callback;return l(e,[p.onLaunch,p.onShow,p.onHide,p.onError,p.onPageNotFound,p.onUnhandledRejection],function(r,i){return function(e){switch(r){case p.onError:s({ev_type:a.JS_ERROR,data:{name:(o=(n=e).split("\n"))[0],message:o[1],stack:o.slice(1).join("\n"),rawMessage:n}});break;case p.onUnhandledRejection:n=(o=e).promise,o=o.reason,t=String(o),n.catch(function(e){return{name:"UnhandledRejection",message:String(e),stack:(null==e?void 0:e.stack)||"",rawMessage:t}}).then(function(e){s({ev_type:a.JS_ERROR,data:e})})}var t,n,o;null!=i&&i.call(this,e)}})},g.prototype.hookPageFn=function(e){var r=this.callback;return l(e,[d.onLoad,d.onShow,d.onReady,d.onHide,d.onUnload,d.onPullDownRefresh],function(n,o){return function(e){var t;n===d.onShow&&(t=(t=getCurrentPages())[t.length-1].route,r({ev_type:a.PATH,data:{pid:t}})),null!=o&&o.call(this,e)}})},g.prototype.collectDeviceId=function(){var e=this.getStorage(h);e||(e=u(),this.setStorage(h,e)),this.callback({ev_type:a.DEVICE_ID,data:{deviceId:e}})},g);function g(e,t){this.originalAppFn=App,this.originalPageFn=Page,this.options=e,this.callback=t}var m,b=(e(w,m=_),w.prototype.normalizeEnvInfo=function(e,t){return{host_platform:"my",host_app_name:e.app,host_app_version:e.version,platform:e.platform,sdk_version:e.SDKVersion,os_name:e.platform,os_version:e.system,device_brand:e.brand,device_model:e.model,screen_width:e.screenWidth,screen_height:e.screenHeight,resolution:e.screenWidth+"x"+e.screenHeight,pixel_ratio:e.pixelRatio,network_type:t.networkType}},w);function w(){var e=null!==m&&m.apply(this,arguments)||this;return e.sdk=my,e}var E,k=(e(R,E=_),R.prototype.normalizeEnvInfo=function(e,t){return{host_platform:"swan",host_app_name:e.host,host_app_version:e.version,platform:e.platform,sdk_version:e.SDKVersion,os_name:e.platform,os_version:e.system,device_brand:e.brand,device_model:e.model,screen_width:e.screenWidth,screen_height:e.screenHeight,resolution:e.screenWidth+"x"+e.screenHeight,pixel_ratio:e.pixelRatio,network_type:t.networkType}},R);function R(){var e=null!==E&&E.apply(this,arguments)||this;return e.sdk=swan,e}var S,C=(e(I,S=_),I.prototype.normalizeEnvInfo=function(e,t){return{host_platform:"tt",host_app_name:e.appName,host_app_version:e.version,platform:e.platform,sdk_version:e.SDKVersion,os_name:e.platform,os_version:e.system,device_brand:e.brand,device_model:e.model,screen_width:e.screenWidth,screen_height:e.screenHeight,resolution:e.screenWidth+"x"+e.screenHeight,pixel_ratio:e.pixelRatio,network_type:t.networkType}},I);function I(){var e=null!==S&&S.apply(this,arguments)||this;return e.sdk=tt,e}var x,T=(e(O,x=_),O.prototype.normalizeEnvInfo=function(e,t){return{host_platform:"wx",host_app_name:"Weixin",host_app_version:e.version,platform:e.platform,sdk_version:e.SDKVersion,os_name:e.system.split(" ")[0],os_version:e.system.split(" ")[1],device_brand:e.brand,device_model:e.model,screen_width:e.screenWidth,screen_height:e.screenHeight,resolution:e.screenWidth+"x"+e.screenHeight,pixel_ratio:e.pixelRatio,network_type:t.networkType}},O);function O(){var e=null!==x&&x.apply(this,arguments)||this;return e.sdk=wx,e}var P=function(){if("undefined"!=typeof tt)return C;if("undefined"!=typeof my)return b;if("undefined"!=typeof swan)return k;if("undefined"!=typeof wx)return T;throw new Error("unsupported mini program")}();function j(e){return"object"==typeof e&&null!==e&&(e=e,"[object Array]"!==Object.prototype.toString.call(e))}N.prototype.sendEvent=function(e){e=this.getEventToBeSent(e);e&&this.idleSendEvent(e)},N.prototype.getEventToBeSent=function(e){e=this._modifyEvent(e);if(this._shouldSend(e))return e},N.prototype._modifyEvent=function(e){return e},N.prototype._shouldSend=function(e){return!0},N.prototype._send=function(e){},N.prototype.idleSendEvent=function(e){this._send(e)},_=N;function N(){}var D,_=(e(A,D=_),Object.defineProperty(A.prototype,"ready",{get:function(){return this.isReady},set:function(e){this.isReady=e,this.isReady&&this._uploadQueue()},enumerable:!1,configurable:!0}),A.prototype._send=function(e){var t=this,e=this.buildParams(e);null!=e&&(this.reportQueue.push(e),this.isReady&&(this.reportQueue.length>=this.batchReportLength&&this._uploadQueue(),this.batchReportTimeout&&clearTimeout(this.batchReportTimeout),this.batchReportTimeout=setTimeout(function(){t._uploadQueue()},this.batchReportWait)))},A.prototype._uploadQueue=function(){var e;this.reportQueue.length&&this.ready&&(e={ev_type:"batch",list:this.reportQueue},this.reportQueue=[],this._request({event:e,type:"post"}))},A.prototype._request=function(e){},A);function A(e){var t,n=D.call(this)||this;return n.reportQueue=[],n.isReady=!0,n.batchReportLength=null!==(t=e.maxBatchReportLength)&&void 0!==t?t:10,n.batchReportWait=null!==(e=e.batchReportWait)&&void 0!==e?e:1e3,n.batchReportTimeout=null,n}var B="tbm.snssdk.com",F="/monitor_microapp/collect",H="/",W=20,V=10,Q=1e3,U="cannot get context before init";function q(e,t,n){var o,r,i=Object.assign({},e,t,n),s={};if(i.context&&j(i.context))try{for(var a=function(e){var t="function"==typeof Symbol&&Symbol.iterator,n=t&&e[t],o=0;if(n)return n.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&o>=e.length&&(e=void 0),{value:e&&e[o++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}(Object.keys(i.context)),c=a.next();!c.done;c=a.next()){var u=c.value,p=i.context[u];s[u]=(u=p,"[object String]"===Object.prototype.toString.call(u)?p:function(t){try{return JSON.stringify(t)}catch(e){return String(t)}}(p))}}catch(e){o={error:e}}finally{try{c&&!c.done&&(r=a.return)&&r.call(a)}finally{if(o)throw o.error}}return i.context=s,i}var L,K=(e(M,L=_),M.prototype.updateConfig=function(e){var t;this.config=(t=this.config,e=e,Object.assign({},t,e)),this.finderConfig&&(this.config.userId=this.finderConfig.user_unique_id)},M.prototype.reportError=function(e){this.reportEvent({ev_type:t.ReportEventType.JS_ERROR,event:{name:e.name,message:e.message,stack:null!==(e=e.stack)&&void 0!==e?e:""},breadcrumbs:c(this.breadcrumbs)})},M.prototype.reportEvent=function(e){this.ready?L.prototype.sendEvent.call(this,e):this.preQueue.push(e)},M.prototype.addBreadcrumb=function(e,t){var n=this.config.maxBreadcrumbs,e=i(i({},t),{type:e,timestamp:Date.now()});this.breadcrumbs=0<=n?c(this.breadcrumbs,[e]).slice(-n):c(this.breadcrumbs,[e])},M.prototype.flush=function(){this._uploadQueue()},M.prototype.sendPageView=function(){this.reportEvent({ev_type:t.ReportEventType.PAGEVIEW})},M.prototype.reportBackendEvent=function(e){switch(e.ev_type){case a.ENV_INFO:this.envInfo=e.data,this.unlock();break;case a.DEVICE_ID:this.deviceId=e.data.deviceId;break;case a.FINDER_CONFIG:this.finderConfig=e.data;break;case a.NETWORK_STATUS:this.envInfo&&(this.envInfo=i(i({},this.envInfo),{network_type:e.data.network_type}));break;case a.PATH:this.pid!==e.data.pid&&(this.addBreadcrumb(t.BreadcrumbType.DOM,{category:n.ROUTE,message:e.data.pid}),this.pid=e.data.pid,this.sendPageView());break;case a.JS_ERROR:this.reportEvent({ev_type:t.ReportEventType.JS_ERROR,event:e.data,breadcrumbs:c(this.breadcrumbs)})}},M.prototype.buildParams=function(e){return i(i({},e),{common:i(i({},this.envInfo),{aid:this.config.aid,pid:this.pid,rangers_sdk_version:this.config.version,timestamp:Date.now(),user_unique_id:this.finderConfig?this.finderConfig.user_unique_id:this.config.userId||this.deviceId,device_id:this.deviceId,session_id:this.config.sessionId,context:this.config.context})})},M.prototype._request=function(e){var t;e&&e.event&&(t=e.type,e=e.event,"post"===t&&this.backend.sendEvent({ev_type:r.BATCH,list:e.list}))},M.prototype.unlock=function(){var t=this;this.ready=!0,this.preQueue.forEach(function(e){L.prototype.sendEvent.call(t,e)}),this.preQueue=[]},M);function M(e,t){var n=L.call(this,e)||this;n.finderConfig=null,n.deviceId="",n.envInfo={},n.preQueue=[],n.ready=!1,n.config=e,n.pid=e.pid;e=n.config,e=Object.assign({},e);return n.backend=new t(e,n.reportBackendEvent.bind(n)),n.backend.init(),n.breadcrumbs=[],n.backend.collectEnvInfo(),n.config.finderInstance&&n.backend.collectFinderConfig(),n}function J(e){this.version="0.0.5",this.Backend=e.Backend,this.defaultConfig={context:{},maxBatchReportLength:V,batchReportWait:Q,userId:"",sessionId:f(),reportDomain:B,reportPath:F,pid:H,maxBreadcrumbs:W,hookPath:!0,hookRequest:!0,enableCapture:!0}}P=new(J.prototype.init=function(e){if(!this.client){if(null==e||!e.aid)throw new Error("cannot init without aid");this.userConfig=e;e=q(this.defaultConfig,this.userConfig,{version:this.version});this.client=new K(e,this.Backend)}},J.prototype.config=function(e){if(!this.userConfig||!this.client)throw new Error("cannot config before init");var t;this.userConfig=(t=this.userConfig,e=e,Object.assign({},t,e)),this.updateClientConfig()},J.prototype.capture=function(e){var t;null!==(t=this.client)&&void 0!==t&&t.reportError(e)},J.prototype.report=function(e){var t;null!==(t=this.client)&&void 0!==t&&t.reportEvent(e)},J.prototype.addBreadcrumb=function(e,t){var n;null!==(n=this.client)&&void 0!==n&&n.addBreadcrumb(e,t)},J.prototype.sendNow=function(){var e;null!==(e=this.client)&&void 0!==e&&e.flush()},Object.defineProperty(J.prototype,"context",{get:function(){var n=this,o={set:function(e,t){if(!n.userConfig)throw new Error(U);return n.userConfig.context||(n.userConfig.context={}),n.userConfig.context[e]=t,n.updateClientConfig(),o},delete:function(e){if(!n.userConfig)throw new Error(U);return n.userConfig.context&&e in n.userConfig.context&&delete n.userConfig.context[e],n.updateClientConfig(),o},clear:function(){if(!n.userConfig)throw new Error(U);return n.userConfig.context={},n.updateClientConfig(),o},get:function(e){var t;if(!n.userConfig)throw new Error(U);return null===(t=n.userConfig.context)||void 0===t?void 0:t[e]},toObject:function(){if(!n.userConfig)throw new Error(U);return i({},n.userConfig.context)}};return o},enumerable:!1,configurable:!0}),J.prototype.updateClientConfig=function(){var e;this.userConfig&&this.client&&(e=q(this.defaultConfig,this.userConfig,{version:this.version}),this.client.updateConfig(e))},J)({Backend:P});t.default=P,Object.defineProperty(t,"__esModule",{value:!0})}); | ||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).RangersMiniSDK={})}(this,function(n){"use strict";var o=function(t,e){return(o=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])})(t,e)};function t(t,e){function n(){this.constructor=t}o(t,e),t.prototype=null===e?Object.create(e):(n.prototype=e.prototype,new n)}var e,r,f,v=function(){return(v=Object.assign||function(t){for(var e,n=1,o=arguments.length;n<o;n++)for(var r in e=arguments[n])Object.prototype.hasOwnProperty.call(e,r)&&(t[r]=e[r]);return t}).apply(this,arguments)};function i(t,e){var n="function"==typeof Symbol&&t[Symbol.iterator];if(!n)return t;var o,r,i=n.call(t),a=[];try{for(;(void 0===e||0<e--)&&!(o=i.next()).done;)a.push(o.value)}catch(t){r={error:t}}finally{try{o&&!o.done&&(n=i.return)&&n.call(i)}finally{if(r)throw r.error}}return a}function a(){for(var t=[],e=0;e<arguments.length;e++)t=t.concat(i(arguments[e]));return t}function u(t){return"object"==typeof t&&null!==t&&(t=t,"[object Array]"!==Object.prototype.toString.call(t))}(d=n.BreadcrumbType||(n.BreadcrumbType={})).DOM="dom",d.HTTP="http",(e=e||{}).ROUTE="route",(r=r||{}).BATCH="batch",(s=n.ReportEventType||(n.ReportEventType={})).PAGEVIEW="pageview",s.JS_ERROR="js_error",s.REQUEST="ajax",s.SET_DATA="set_data",s.ON_READY="on_ready",s.FIRST_LOAD="first_load",(d=f=f||{}).DEVICE_ID="device_id",d.FINDER_CONFIG="finder_config",d.ENV_INFO="env_info",d.NETWORK_STATUS="network_status",d.JS_ERROR="js_error",d.GLOBAL_JS_ERROR="global_js_error",d.APP_SESSION="app_session",d.PAGE_SESSION="page_session",d.REQUEST="request",d.SET_DATA="set_data",d.PAGE_ON_READY="page_on_ready",d.PAGE_FIRST_LOAD="page_first_load";var s=(c.prototype.sendEvent=function(t){t=this.getEventToBeSent(t);t&&this.idleSendEvent(t)},c.prototype.getEventToBeSent=function(t){t=this._modifyEvent(t);if(this._shouldSend(t))return t},c.prototype._modifyEvent=function(t){return t},c.prototype._shouldSend=function(t){return!0},c.prototype._send=function(t){},c.prototype.idleSendEvent=function(t){this._send(t)},c);function c(){}var p,d=(t(h,p=s),Object.defineProperty(h.prototype,"ready",{get:function(){return this.isReady},set:function(t){this.isReady=t,this.isReady&&this._uploadQueue()},enumerable:!1,configurable:!0}),h.prototype._send=function(t){var e=this,t=this.buildParams(t);null!=t&&(this.reportQueue.push(t),this.isReady&&(this.reportQueue.length>=this.batchReportLength&&this._uploadQueue(),this.batchReportTimeout&&clearTimeout(this.batchReportTimeout),this.batchReportTimeout=setTimeout(function(){e._uploadQueue()},this.batchReportWait)))},h.prototype._uploadQueue=function(){var t;this.reportQueue.length&&this.ready&&(t={ev_type:"batch",list:this.reportQueue},this.reportQueue=[],this._request({event:t,type:"post"}))},h.prototype._request=function(t){},h);function h(t){var e,n=p.call(this)||this;return n.reportQueue=[],n.isReady=!0,n.batchReportLength=null!==(e=t.maxBatchReportLength)&&void 0!==e?e:10,n.batchReportWait=null!==(t=t.batchReportWait)&&void 0!==t?t:1e3,n.batchReportTimeout=null,n}function l(){var t=function(){for(var t=new Array(16),e=0,n=0;n<16;n++)0==(3&n)&&(e=4294967296*Math.random()),t[n]=e>>>((3&n)<<3)&255;return t}();return t[6]=15&t[6]|64,t[8]=63&t[8]|128,function(t){for(var e=[],n=0;n<256;++n)e[n]=(n+256).toString(16).substr(1);var o=0;return[e[t[o++]],e[t[o++]],e[t[o++]],e[t[o++]],"-",e[t[o++]],e[t[o++]],"-",e[t[o++]],e[t[o++]],"-",e[t[o++]],e[t[o++]],"-",e[t[o++]],e[t[o++]],e[t[o++]],e[t[o++]],e[t[+o]],e[t[15]]].join("")}(t)}function _(e){try{return JSON.stringify(e)}catch(t){return String(e)}}function y(t){return encodeURI(t).split(/%(?:u[0-9A-F]{2})?[0-9A-F]{2}|./).length-1}function g(t){return t?t instanceof ArrayBuffer?t.byteLength:u(t)?y(_(t)):t.length||0:0}var S,m,E,k=l,R="RANGERS_MINI_SDK_DEVICE_ID",b="RANGERS_PAGE_SESSION_ID",w="RANGERS_PAGE_TIMING";(s={}).TT="tt",s.WX="wx",s.MY="my",s.SWAN="swan",(s=S=S||{}).onLaunch="onLaunch",s.onShow="onShow",s.onHide="onHide",s.onError="onError",s.onUnhandledRejection="onUnhandledRejection",s.onPageNotFound="onPageNotFound",(s=m=m||{}).onLoad="onLoad",s.onShow="onShow",s.onReady="onReady",s.onHide="onHide",s.onUnload="onUnload",s.onPullDownRefresh="onPullDownRefresh",(E=E||{}).request="request";function T(t,e,o){return function(n){return e.forEach(function(t){var e=n[t];n[t]=o(t,e)}),t(n)}}function I(n,t,o){t.forEach(function(t){var e;n[t]&&(e=n[t],Object.defineProperty(n,t,{enumerable:!1,configurable:!1,writable:!1,value:o(t,e).bind(n)}))})}function x(){return{host_platform:"undefined"!=typeof wx?"wx":"undefined"!=typeof tt?"tt":"undefined"!=typeof my?"my":"undefined"!=typeof swan?"swan":C,host_app_name:C,host_app_version:C,platform:C,sdk_version:C,os_name:C,os_version:C,device_brand:C,device_model:C,screen_height:0,screen_width:0,resolution:C,pixel_ratio:0,network_type:C}}var C="unknown",s=(O.prototype.init=function(){var e=this;this.transport=this.setupTransport(),this.collectDeviceId(),App=this.hookAppFn(App),Page=this.hookPageFn(Page),Component=this.hookComponentFn(Component),this.hookSdk(),this.sdk.onNetworkStatusChange&&this.sdk.onNetworkStatusChange(function(t){e.callback({ev_type:f.NETWORK_STATUS,data:{network_type:t.networkType}})})},O.prototype.sendEvent=function(t){var e;null!==(e=this.transport)&&void 0!==e&&e.sendEvent(t)},O.prototype.collectEnvInfo=function(){var n=this;Promise.all([new Promise(function(t,e){n.sdk.getSystemInfo({success:t,fail:e})}),new Promise(function(t,e){n.sdk.getNetworkType({success:t,fail:e})})]).then(function(t){var e=i(t,2),t=e[0],e=e[1];t&&e&&n.callback({ev_type:f.ENV_INFO,data:n.normalizeEnvInfo(t,e)})}).catch(function(){n.callback({ev_type:f.ENV_INFO,data:x()})})},O.prototype.collectFinderConfig=function(){var e=this;this.options.finderInstance&&this.options.finderInstance.getToken(function(t){e.callback({ev_type:f.FINDER_CONFIG,data:{user_unique_id:t.user_unique_id}})})},O.prototype.getStorage=function(t){return this.sdk.getStorageSync(t)},O.prototype.setStorage=function(t,e){return this.sdk.setStorageSync(t,e)},O.prototype.setupTransport=function(){var t,e,n=this;return this.reportUrl=(t=this.options.reportDomain,e=this.options.reportPath,"https://"+t+e),{sendEvent:function(t){n.sdk.request({url:n.reportUrl,method:"POST",data:t})},request:function(t){n.sdk.request(t)}}},O.prototype.hookAppFn=function(t){var s=this,c=this.callback;return T(t,[S.onLaunch,S.onShow,S.onHide,S.onError,S.onUnhandledRejection],function(i,a){return function(t){switch(i){case S.onShow:var e=l();s.appSessionId=e,c({ev_type:f.APP_SESSION,data:{appSessionId:e}});break;case S.onError:c({ev_type:f.GLOBAL_JS_ERROR,data:{name:(r=(o=t).split("\n"))[0],message:r[1],stack:r.slice(1).join("\n"),rawMessage:o}});break;case S.onUnhandledRejection:o=(r=t).promise,r=r.reason,n=String(r),o.catch(function(t){return{name:"UnhandledRejection",message:String(t),stack:(null==t?void 0:t.stack)||"",rawMessage:n}}).then(function(t){c({ev_type:f.GLOBAL_JS_ERROR,data:t})})}var n,o,r;null!=a&&a.call(this,t)}})},O.prototype.hookPageFn=function(t){return T(t,[m.onLoad,m.onShow,m.onReady],this._pageLifeCycleHandlers())},O.prototype.hookComponentFn=function(t){return e=t,o=[m.onLoad,m.onShow,m.onReady],r=this._pageLifeCycleHandlers(),function(n){return n.methods||(n.methods={}),o.forEach(function(t){var e=n.methods[t];n.methods[t]=r(t,e)}),e(n)};var e,o,r},O.prototype._pageLifeCycleHandlers=function(){var c=this,p=this.callback;return function(a,e){function s(t){try{null!=e&&e.call(this,t)}catch(t){p({ev_type:f.JS_ERROR,data:{name:t.name,message:t.message,stack:t.stack,rawMessage:String(t)},context:c.getContext()})}}return function(t){var e,n=(e=getCurrentPages())[e.length-1].route;switch(a){case m.onLoad:c.pid=n,c.hookSetData(this);var o=Date.now(),r=[c.deviceId,c.appSessionId,c.pid,o].join("|");return c.pageSessionId=r,c.taskTracks[r]={tasks:[],timing:{firstReqStart:o,firstReqEnd:0,firstLoadStart:o,firstLoadEnd:0},ready:!1,ended:!1},p({ev_type:f.PAGE_SESSION,data:{pid:n,pageSessionId:r}}),s.call(this,t),this[b]=r,void(this[w]={onLoadStart:o,onLoadEnd:Date.now(),onShowStart:0,onShowEnd:0,onReadyStart:0,onReadyEnd:0});case m.onShow:var i,r=this[b];return c.pageSessionId=r,p({ev_type:f.PAGE_SESSION,data:{pid:n,pageSessionId:r}}),(i=this[w]).onShowStart=i.onShowStart||Date.now(),s.call(this,t),void(i.onShowEnd=i.onShowEnd||Date.now());case m.onReady:(i=this[w]).onReadyStart=Date.now(),s.call(this,t),i.onReadyEnd=Date.now(),p({ev_type:f.PAGE_ON_READY,data:i});r=c.getTaskTrack();return r.ready=!0,!r.timing.firstReqEnd&&r.tasks.every(function(t){return t.completed||"SET_DATA"===t.type})&&(r.timing.firstReqEnd=r.timing.firstReqStart),void(r.tasks.every(function(t){return t.completed})&&(r.timing.firstLoadEnd=i.onReadyEnd,c.endTrack(r)))}s.call(this,t)}}},O.prototype.hookSdk=function(){this.hookRequest()},O.prototype.hookRequest=function(){var u=this,h=this,l=this.callback;I(this.sdk,[E.request],function(t,i){return function(a){if(u.shouldIgnoreUrl(a.url))return i(a);var s=a.success,o=a.fail,e=a.complete,c=g(null!==(r=a.data)&&void 0!==r?r:""),p=u.addTask("REQUEST"),d=Date.now(),t=u.captureContext(function(t){var e,n,o,r,i=Date.now();l({ev_type:f.REQUEST,data:{req_url:a.url,method:null!==(o=a.method)&&void 0!==o?o:"GET",err_msg:300<=t.statusCode?_(t.data):"",req_status:1,req_start_time:d,req_end_time:i,resp_status:t.statusCode,header:null!==(r=a.header)&&void 0!==r?r:{},req_param:300<=t.statusCode?_(a.data):"",req_size:c,resp_size:(o=t,i=null!==(r=Object.keys(null!==(i=o.header)&&void 0!==i?i:{}).find(function(t){return"content-legth"===t.toLowerCase()}))&&void 0!==r?r:"",null!==(r=o.header)&&void 0!==r&&r[i]?parseInt(o.header[i]):g(o.data)),perf:t.profile?(e=t.profile,n={},["redirectStart","redirectEnd","fetchStart","domainLookupStart","domainLookupEnd","connectStart","connectEnd","SSLconnectionStart","SSLconnectionEnd","requestStart","requestEnd","responseStart","responseEnd","rtt","httpRttEstimate","transportRttEstimate"].forEach(function(t){n[t]=e[t]}),n):{}},context:h.capturedContext}),h.taskStack.push(p);try{null!=s&&s.call(this,t)}finally{h.taskStack.pop()}}),n=u.captureContext(function(t){var e,n=Date.now();l({ev_type:f.REQUEST,data:{req_url:a.url,method:null!==(e=a.method)&&void 0!==e?e:"GET",err_msg:null!==(e=t.errMsg)&&void 0!==e?e:"",req_status:0,req_start_time:d,req_end_time:n,resp_status:0,req_param:_(a.data),header:null!==(n=a.header)&&void 0!==n?n:{},req_size:c,resp_size:0,perf:{}},context:h.capturedContext}),h.taskStack.push(p);try{null!=o&&o.call(this,t)}finally{h.taskStack.pop()}}),r=u.captureContext(function(t){h.taskStack.push(p);try{null!=e&&e.call(this,t)}finally{h.taskStack.pop()}h.endTask(p,Date.now())}),r=v(v({},a),{success:t,fail:n,complete:r});return i(r)}})},O.prototype.shouldIgnoreUrl=function(t){return t===this.reportUrl},O.prototype.hookSetData=function(t){var s=this;I(t,["setData"],function(t,e){return function(n,t){var o=this,r=t,i=s.addTask("SET_DATA"),a=Date.now(),t=s.captureContext(function(){var t=Date.now(),e=_(n);s.callback({ev_type:f.SET_DATA,data:{setDataStart:a,setDataEnd:t,setDataSize:y(e),data:e},context:s.capturedContext}),s.taskStack.push(i);try{null!=r&&r.call(o)}catch(t){s.callback({ev_type:f.JS_ERROR,data:{name:t.name,message:t.message,stack:t.stack,rawMessage:String(t)},context:s.getContext()})}finally{s.taskStack.pop()}s.endTask(i,t)});e.call(this,n,t)}})},O.prototype.collectDeviceId=function(){var t=this.getStorage(R);t||(t=l(),this.setStorage(R,t)),this.deviceId=t,this.callback({ev_type:f.DEVICE_ID,data:{deviceId:t}})},O.prototype.getContext=function(){var t;return null!==(t=this.capturedContext)&&void 0!==t?t:this.currentContext()},O.prototype.currentContext=function(){return{pid:this.pid,pageSessionId:this.pageSessionId,context:this.options.context}},O.prototype.captureContext=function(o){var r=this,i=this.getContext();return function(){for(var t,e=[],n=0;n<arguments.length;n++)e[n]=arguments[n];r.capturedContext=i;try{t=o.apply(this,e)}catch(t){r.callback({ev_type:f.JS_ERROR,data:{name:t.name,message:t.message,stack:t.stack,rawMessage:String(t)},context:i})}finally{r.capturedContext=null}return t}},O.prototype.addTask=function(t){var e=this.getTaskTrack();if(!e||e.ended)return 0;var n=this.taskStack.length&&this.taskStack[this.taskStack.length-1];if(e.ready&&(!n||!e.tasks.some(function(t){return t.id===n})))return 0;var o=e.tasks.length+1;return e.tasks.push({id:o,type:t,completed:!1}),o},O.prototype.endTask=function(t,e){var n=this.getTaskTrack();t<=0||!n||n.ended||((t=n.tasks[t-1]).completed=!0,n.timing.firstReqEnd||"REQUEST"!==t.type||(n.timing.firstReqEnd=e),n.tasks.every(function(t){return t.completed})&&(n.timing.firstLoadEnd=e,this.endTrack(n)))},O.prototype.endTrack=function(t){t.ready&&(t.ended=!0,this.callback({ev_type:f.PAGE_FIRST_LOAD,data:t.timing,context:this.getContext()}))},O.prototype.getTaskTrack=function(){var t=this.getContext().pageSessionId;return this.taskTracks[t]},O);function O(t,e){this.originalAppFn=App,this.originalPageFn=Page,this.reportUrl="",this.deviceId="",this.appSessionId="",this.pageSessionId="",this.capturedContext=null,this.taskStack=[],this.taskTracks={},this.options=t,this.pid=t.pid,this.callback=e}var A,D=(t(P,A=s),P.prototype.normalizeEnvInfo=function(t,e){return{host_platform:"my",host_app_name:t.app,host_app_version:t.version,platform:t.platform,sdk_version:t.SDKVersion,os_name:t.platform,os_version:t.system,device_brand:t.brand,device_model:t.model,screen_width:t.screenWidth,screen_height:t.screenHeight,resolution:t.screenWidth+"x"+t.screenHeight,pixel_ratio:t.pixelRatio,network_type:e.networkType}},P);function P(){var t=null!==A&&A.apply(this,arguments)||this;return t.sdk=my,t}var N,L=(t(q,N=s),q.prototype.normalizeEnvInfo=function(t,e){return{host_platform:"swan",host_app_name:t.host,host_app_version:t.version,platform:t.platform,sdk_version:t.SDKVersion,os_name:t.platform,os_version:t.system,device_brand:t.brand,device_model:t.model,screen_width:t.screenWidth,screen_height:t.screenHeight,resolution:t.screenWidth+"x"+t.screenHeight,pixel_ratio:t.pixelRatio,network_type:e.networkType}},q);function q(){var t=null!==N&&N.apply(this,arguments)||this;return t.sdk=swan,t}var j,F=(t(G,j=s),G.prototype.normalizeEnvInfo=function(t,e){return{host_platform:"tt",host_app_name:t.appName,host_app_version:t.version,platform:t.platform,sdk_version:t.SDKVersion,os_name:t.platform,os_version:t.system,device_brand:t.brand,device_model:t.model,screen_width:t.screenWidth,screen_height:t.screenHeight,resolution:t.screenWidth+"x"+t.screenHeight,pixel_ratio:t.pixelRatio,network_type:e.networkType}},G);function G(){var t=null!==j&&j.apply(this,arguments)||this;return t.sdk=tt,t}var U,B=(t(Q,U=s),Q.prototype.normalizeEnvInfo=function(t,e){return{host_platform:"wx",host_app_name:"Weixin",host_app_version:t.version,platform:t.platform,sdk_version:t.SDKVersion,os_name:t.system.split(" ")[0],os_version:t.system.split(" ")[1],device_brand:t.brand,device_model:t.model,screen_width:t.screenWidth,screen_height:t.screenHeight,resolution:t.screenWidth+"x"+t.screenHeight,pixel_ratio:t.pixelRatio,network_type:e.networkType}},Q);function Q(){var t=null!==U&&U.apply(this,arguments)||this;return t.sdk=wx,t}var s=function(){if("undefined"!=typeof tt)return F;if("undefined"!=typeof my)return D;if("undefined"!=typeof swan)return L;if("undefined"!=typeof wx)return B;throw new Error("unsupported mini program")}(),W="tbm.snssdk.com",H="/monitor_microapp/collect",V="/",J=20,M=10,z=1e3,K="cannot get context before init";function Y(t,e,n){var o,r,i=Object.assign({},t,e,n),a={};if(i.context&&u(i.context))try{for(var s=function(t){var e="function"==typeof Symbol&&Symbol.iterator,n=e&&t[e],o=0;if(n)return n.call(t);if(t&&"number"==typeof t.length)return{next:function(){return t&&o>=t.length&&(t=void 0),{value:t&&t[o++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")}(Object.keys(i.context)),c=s.next();!c.done;c=s.next()){var p=c.value,d=i.context[p];a[p]=(p=d,"[object String]"===Object.prototype.toString.call(p)?d:_(d))}}catch(t){o={error:t}}finally{try{c&&!c.done&&(r=s.return)&&r.call(s)}finally{if(o)throw o.error}}return i.context=a,i}var X,Z=(t($,X=d),$.prototype.updateConfig=function(t){var e;this.config=(e=this.config,t=t,Object.assign({},e,t)),this.finderConfig&&(this.config.userId=this.finderConfig.user_unique_id)},$.prototype.reportError=function(t,e){this.reportEvent({ev_type:n.ReportEventType.JS_ERROR,event:{name:t.name,message:t.message,stack:null!==(t=t.stack)&&void 0!==t?t:""},breadcrumbs:a(this.breadcrumbs)},e)},$.prototype.reportEvent=function(t,e){this.ready?X.prototype.sendEvent.call(this,v(v({},t),{context:e})):this.preQueue.push(v(v({},t),{context:e}))},$.prototype.addBreadcrumb=function(t,e){var n=this.config.maxBreadcrumbs,t=v(v({},e),{type:t,timestamp:Date.now()});this.breadcrumbs=0<=n?a(this.breadcrumbs,[t]).slice(-n):a(this.breadcrumbs,[t])},$.prototype.flush=function(){this._uploadQueue()},$.prototype.sendPageView=function(){this.reportEvent({ev_type:n.ReportEventType.PAGEVIEW})},$.prototype.reportBackendEvent=function(t){switch(t.ev_type){case f.ENV_INFO:this.envInfo=t.data,this.unlock();break;case f.DEVICE_ID:this.deviceId=t.data.deviceId;break;case f.FINDER_CONFIG:this.finderConfig=t.data;break;case f.NETWORK_STATUS:this.envInfo&&(this.envInfo=v(v({},this.envInfo),{network_type:t.data.network_type}));break;case f.GLOBAL_JS_ERROR:this.reportEvent({ev_type:n.ReportEventType.JS_ERROR,event:t.data,breadcrumbs:a(this.breadcrumbs)});break;case f.JS_ERROR:this.reportEvent({ev_type:n.ReportEventType.JS_ERROR,event:t.data,breadcrumbs:a(this.breadcrumbs)},t.context);break;case f.APP_SESSION:this.appSessionId=t.data.appSessionId;break;case f.PAGE_SESSION:this.pageSessionId!==t.data.pageSessionId&&(this.addBreadcrumb(n.BreadcrumbType.DOM,{category:e.ROUTE,message:t.data.pid}),this.pid=t.data.pid,this.pageSessionId=t.data.pageSessionId,this.sendPageView());break;case f.REQUEST:this.reportEvent({ev_type:n.ReportEventType.REQUEST,event:t.data},t.context);break;case f.SET_DATA:this.reportEvent({ev_type:n.ReportEventType.SET_DATA,event:t.data},t.context);break;case f.PAGE_ON_READY:this.reportEvent({ev_type:n.ReportEventType.ON_READY,event:t.data});break;case f.PAGE_FIRST_LOAD:this.reportEvent({ev_type:n.ReportEventType.FIRST_LOAD,event:t.data},t.context)}},$.prototype.buildParams=function(t){var e;return v(v({},t),{common:v(v({},this.envInfo),{aid:this.config.aid,pid:null!==(e=null===(e=t.context)||void 0===e?void 0:e.pid)&&void 0!==e?e:this.pid,rangers_sdk_version:this.config.version,timestamp:Date.now(),user_unique_id:this.finderConfig?this.finderConfig.user_unique_id:this.config.userId||this.deviceId,device_id:this.deviceId,session_id:this.config.sessionId,app_session_id:this.appSessionId,page_session_id:null!==(e=null===(e=t.context)||void 0===e?void 0:e.pageSessionId)&&void 0!==e?e:this.pageSessionId,context:null!==(t=null===(t=t.context)||void 0===t?void 0:t.context)&&void 0!==t?t:this.config.context}),context:void 0})},$.prototype._request=function(t){var e;t&&t.event&&(e=t.type,t=t.event,"post"===e&&this.backend.sendEvent({ev_type:r.BATCH,list:t.list}))},$.prototype.unlock=function(){var e=this;this.ready=!0,this.preQueue.forEach(function(t){X.prototype.sendEvent.call(e,t)}),this.preQueue=[]},$);function $(t,e){var n=X.call(this,t)||this;n.finderConfig=null,n.deviceId="",n.envInfo={},n.appSessionId="",n.pageSessionId="",n.preQueue=[],n.ready=!1,n.config=t,n.pid=t.pid;t=n.config,t=Object.assign({},t);return n.backend=new e(t,n.reportBackendEvent.bind(n)),n.backend.init(),n.breadcrumbs=[],n.backend.collectEnvInfo(),n.config.finderInstance&&n.backend.collectFinderConfig(),n}function et(t){this.version="0.0.6",this.Backend=t.Backend,this.defaultConfig={context:{},maxBatchReportLength:M,batchReportWait:z,userId:"",sessionId:k(),reportDomain:W,reportPath:H,pid:V,maxBreadcrumbs:J,hookPath:!0,hookRequest:!0,enableCapture:!0}}s=new(et.prototype.init=function(t){if(!this.client){if(null==t||!t.aid)throw new Error("cannot init without aid");this.userConfig=t;t=Y(this.defaultConfig,this.userConfig,{version:this.version});this.client=new Z(t,this.Backend)}},et.prototype.config=function(t){if(!this.userConfig||!this.client)throw new Error("cannot config before init");var e;this.userConfig=(e=this.userConfig,t=t,Object.assign({},e,t)),this.updateClientConfig()},et.prototype.capture=function(t){var e;null!==(e=this.client)&&void 0!==e&&e.reportError(t)},et.prototype.report=function(t){var e;null!==(e=this.client)&&void 0!==e&&e.reportEvent(t)},et.prototype.addBreadcrumb=function(t,e){var n;null!==(n=this.client)&&void 0!==n&&n.addBreadcrumb(t,e)},et.prototype.sendNow=function(){var t;null!==(t=this.client)&&void 0!==t&&t.flush()},Object.defineProperty(et.prototype,"context",{get:function(){var n=this,o={set:function(t,e){if(!n.userConfig)throw new Error(K);return n.userConfig.context||(n.userConfig.context={}),n.userConfig.context[t]=e,n.updateClientConfig(),o},delete:function(t){if(!n.userConfig)throw new Error(K);return n.userConfig.context&&t in n.userConfig.context&&delete n.userConfig.context[t],n.updateClientConfig(),o},clear:function(){if(!n.userConfig)throw new Error(K);return n.userConfig.context={},n.updateClientConfig(),o},get:function(t){var e;if(!n.userConfig)throw new Error(K);return null===(e=n.userConfig.context)||void 0===e?void 0:e[t]},toObject:function(){if(!n.userConfig)throw new Error(K);return v({},n.userConfig.context)}};return o},enumerable:!1,configurable:!0}),et.prototype.updateClientConfig=function(){var t;this.userConfig&&this.client&&(t=Y(this.defaultConfig,this.userConfig,{version:this.version}),this.client.updateConfig(t))},et)({Backend:s});n.default=s,Object.defineProperty(n,"__esModule",{value:!0})}); |
@@ -48,25 +48,2 @@ import { BatchProps } from '@slardar/sdk-core'; | ||
declare type EnvInfo = SystemInfo & NetworkStatus; | ||
interface SystemInfo { | ||
host_platform: string; | ||
host_app_name: string; | ||
host_app_version: string; | ||
platform: string; | ||
sdk_version: string; | ||
os_name: string; | ||
os_version: string; | ||
device_brand: string; | ||
device_model: string; | ||
resolution: string; | ||
screen_width: number; | ||
screen_height: number; | ||
pixel_ratio: number; | ||
} | ||
interface NetworkStatus { | ||
network_type: string; | ||
} | ||
interface DataFinderConfig { | ||
user_unique_id: string; | ||
} | ||
declare type SendEvent = BatchEvent; | ||
@@ -91,8 +68,15 @@ declare enum SendEventType { | ||
session_id: string; | ||
context: Record<string, string>; | ||
app_session_id: string; | ||
page_session_id: string; | ||
context: Context; | ||
}; | ||
declare type ReportEvent = PageviewReportEvent | JsErrorReportEvent; | ||
declare type Context = Record<string, string>; | ||
declare type ReportEvent = PageviewReportEvent | JsErrorReportEvent | RequestReportEvent | SetDataReportEvent | PageFirstLoadReportEvent | PageOnReadyReportEvent; | ||
declare enum ReportEventType { | ||
PAGEVIEW = "pageview", | ||
JS_ERROR = "js_error" | ||
JS_ERROR = "js_error", | ||
REQUEST = "ajax", | ||
SET_DATA = "set_data", | ||
ON_READY = "on_ready", | ||
FIRST_LOAD = "first_load" | ||
} | ||
@@ -113,3 +97,71 @@ interface PageviewReportEvent { | ||
} | ||
declare type BackendEvent = BackendDeviceIdEvent | BackendEnvInfoEvent | BackendFinderConfigEvent | BackendNetworkStatusEvent | BackendPathEvent | BackendJsErrorEvent; | ||
interface RequestReportEvent { | ||
ev_type: ReportEventType.REQUEST; | ||
event: RequestEvent; | ||
} | ||
interface RequestEvent { | ||
req_url: string; | ||
method: string; | ||
req_status: 0 | 1; | ||
err_msg: string; | ||
resp_status: number; | ||
req_start_time: number; | ||
req_end_time: number; | ||
header: Record<string, string>; | ||
req_param: string; | ||
perf: ReqPerf | {}; | ||
req_size: number; | ||
resp_size: number; | ||
} | ||
interface ReqPerf { | ||
redirectStart: number; | ||
redirectEnd: number; | ||
fetchStart: number; | ||
domainLookupStart: number; | ||
domainLookupEnd: number; | ||
connectStart: number; | ||
connectEnd: number; | ||
SSLconnectionStart: number; | ||
SSLconnectionEnd: number; | ||
requestStart: number; | ||
requestEnd: number; | ||
responseStart: number; | ||
responseEnd: number; | ||
rtt: number; | ||
httpRttEstimate: number; | ||
transportRttEstimate: number; | ||
} | ||
interface PageOnReadyReportEvent { | ||
ev_type: ReportEventType.ON_READY; | ||
event: PageOnReadyEvent; | ||
} | ||
interface PageOnReadyEvent { | ||
onLoadStart: number; | ||
onLoadEnd: number; | ||
onShowStart: number; | ||
onShowEnd: number; | ||
onReadyStart: number; | ||
onReadyEnd: number; | ||
} | ||
interface PageFirstLoadReportEvent { | ||
ev_type: ReportEventType.FIRST_LOAD; | ||
event: PageFirstLoadEvent; | ||
} | ||
interface PageFirstLoadEvent { | ||
firstReqStart: number; | ||
firstReqEnd: number; | ||
firstLoadStart: number; | ||
firstLoadEnd: number; | ||
} | ||
interface SetDataReportEvent { | ||
ev_type: ReportEventType.SET_DATA; | ||
event: SetDataEvent; | ||
} | ||
interface SetDataEvent { | ||
setDataStart: number; | ||
setDataEnd: number; | ||
setDataSize: number; | ||
data: string; | ||
} | ||
declare type BackendEvent = BackendDeviceIdEvent | BackendEnvInfoEvent | BackendFinderConfigEvent | BackendNetworkStatusEvent | BackendGlobalJsErrorEvent | BackendJsErrorEvent | BackendAppSessionEvent | BackendPageSessionEvent | BackendRequestEvent | BackendSetDataEvent | BackendPageOnReadyEvent | BackendPageFirstLoadEvent; | ||
declare enum BackendEventType { | ||
@@ -119,5 +171,11 @@ DEVICE_ID = "device_id", | ||
ENV_INFO = "env_info", | ||
PATH = "path", | ||
NETWORK_STATUS = "network_status", | ||
JS_ERROR = "js_error" | ||
JS_ERROR = "js_error", | ||
GLOBAL_JS_ERROR = "global_js_error", | ||
APP_SESSION = "app_session", | ||
PAGE_SESSION = "page_session", | ||
REQUEST = "request", | ||
SET_DATA = "set_data", | ||
PAGE_ON_READY = "page_on_ready", | ||
PAGE_FIRST_LOAD = "page_first_load" | ||
} | ||
@@ -142,23 +200,171 @@ interface BackendDeviceIdEvent { | ||
} | ||
interface BackendPathEvent { | ||
ev_type: BackendEventType.PATH; | ||
interface BackendJsErrorEvent { | ||
ev_type: BackendEventType.JS_ERROR; | ||
data: JsErrorEvent; | ||
context: EventContext; | ||
} | ||
interface BackendGlobalJsErrorEvent { | ||
ev_type: BackendEventType.GLOBAL_JS_ERROR; | ||
data: JsErrorEvent; | ||
} | ||
interface BackendAppSessionEvent { | ||
ev_type: BackendEventType.APP_SESSION; | ||
data: { | ||
appSessionId: string; | ||
}; | ||
} | ||
interface BackendPageSessionEvent { | ||
ev_type: BackendEventType.PAGE_SESSION; | ||
data: { | ||
pid: string; | ||
pageSessionId: string; | ||
}; | ||
} | ||
interface BackendJsErrorEvent { | ||
ev_type: BackendEventType.JS_ERROR; | ||
data: JsErrorEvent; | ||
interface BackendRequestEvent { | ||
ev_type: BackendEventType.REQUEST; | ||
data: RequestEvent; | ||
context: EventContext; | ||
} | ||
interface BackendSetDataEvent { | ||
ev_type: BackendEventType.SET_DATA; | ||
data: SetDataEvent; | ||
context: EventContext; | ||
} | ||
interface BackendPageOnReadyEvent { | ||
ev_type: BackendEventType.PAGE_ON_READY; | ||
data: PageOnReadyEvent; | ||
} | ||
interface BackendPageFirstLoadEvent { | ||
ev_type: BackendEventType.PAGE_FIRST_LOAD; | ||
data: PageFirstLoadEvent; | ||
context: EventContext; | ||
} | ||
interface EventContext { | ||
pid: string; | ||
pageSessionId: string; | ||
context: Context; | ||
} | ||
declare type EnvInfo = SystemInfo & NetworkStatus; | ||
interface SystemInfo { | ||
host_platform: string; | ||
host_app_name: string; | ||
host_app_version: string; | ||
platform: string; | ||
sdk_version: string; | ||
os_name: string; | ||
os_version: string; | ||
device_brand: string; | ||
device_model: string; | ||
resolution: string; | ||
screen_width: number; | ||
screen_height: number; | ||
pixel_ratio: number; | ||
} | ||
interface NetworkStatus { | ||
network_type: string; | ||
} | ||
interface DataFinderConfig { | ||
user_unique_id: string; | ||
} | ||
interface Response { | ||
statusCode: any; | ||
status: any; | ||
header: any; | ||
headers: any; | ||
data: any; | ||
errMsg: any; | ||
error: any; | ||
duration: number; | ||
declare type AnyObject = Record<string, any>; | ||
declare type LifeCycleHandler<T> = (this: any, options?: T) => void; | ||
interface AppLifeCycles { | ||
onLaunch: LifeCycleHandler<LaunchShowOptions>; | ||
onShow: LifeCycleHandler<LaunchShowOptions>; | ||
onHide: LifeCycleHandler<void>; | ||
onError: LifeCycleHandler<string>; | ||
onPageNotFount: LifeCycleHandler<PageNotFoundOptions>; | ||
onUnhandledRejection: LifeCycleHandler<{ | ||
promise: Promise<unknown>; | ||
reason: unknown; | ||
}>; | ||
} | ||
declare type AppInitOptions = AppLifeCycles & { | ||
[key: string]: any; | ||
}; | ||
interface LaunchShowOptions { | ||
path: string; | ||
query: AnyObject; | ||
scene: number; | ||
shareTicket: string; | ||
referrerInfo?: { | ||
appId: string; | ||
extraData?: any; | ||
}; | ||
} | ||
interface PageNotFoundOptions { | ||
path: string; | ||
query: AnyObject; | ||
isEntryPage: boolean; | ||
} | ||
interface PageLifeCycles { | ||
onLoad: LifeCycleHandler<{ | ||
query: Record<string, string | undefined>; | ||
}>; | ||
onShow: LifeCycleHandler<never>; | ||
onReady: LifeCycleHandler<never>; | ||
onHide: LifeCycleHandler<never>; | ||
onUnload: LifeCycleHandler<never>; | ||
onPullDownRefresh: LifeCycleHandler<never>; | ||
} | ||
declare type PageInitOptions = PageLifeCycles & { | ||
data: AnyObject; | ||
[key: string]: any; | ||
}; | ||
interface ComponentInitOptions { | ||
behaviors: any[]; | ||
data: AnyObject; | ||
properties: AnyObject; | ||
methods: PageLifeCycles & { | ||
[key: string]: any; | ||
}; | ||
} | ||
declare type AppInitFn = (options: Partial<AppInitOptions>) => void; | ||
declare type PageInitFn = (options: PageInitOptions) => void; | ||
declare type ComponentInitFn = (options: ComponentInitOptions) => void; | ||
interface RequestOptions { | ||
url: string; | ||
method?: 'GET' | 'POST' | 'OPTIONS' | 'PUT' | 'HEAD' | 'DELETE' | 'TRACE' | 'CONNECT'; | ||
data?: string | AnyObject | ArrayBuffer; | ||
header?: Record<string, string>; | ||
dataType?: 'json' | string; | ||
responseType?: 'text' | 'arraybuffer'; | ||
success?: (res: RequestResult) => void; | ||
fail?: (res: GeneralResult) => void; | ||
complete?: (res: GeneralResult) => void; | ||
} | ||
interface RequestResult { | ||
data: string | AnyObject | ArrayBuffer; | ||
header?: Record<string, string>; | ||
cookies?: string[]; | ||
profile?: RequestResultProfile; | ||
statusCode: number; | ||
errMsg: string; | ||
} | ||
interface RequestResultProfile { | ||
redirectStart: number; | ||
redirectEnd: number; | ||
fetchStart: number; | ||
domainLookupStart: number; | ||
domainLookupEnd: number; | ||
connectStart: number; | ||
connectEnd: number; | ||
SSLconnectionStart: number; | ||
SSLconnectionEnd: number; | ||
requestStart: number; | ||
requestEnd: number; | ||
responseStart: number; | ||
responseEnd: number; | ||
rtt: number; | ||
estimate_nettype: string; | ||
httpRttEstimate: number; | ||
transportRttEstimate: number; | ||
downstreamThroughputKbpsEstimate: number; | ||
throughputKbps: number; | ||
} | ||
interface GeneralResult { | ||
errMsg: string; | ||
} | ||
interface Transport { | ||
@@ -177,17 +383,3 @@ /** | ||
} | ||
interface RequestOptions { | ||
url: string; | ||
method?: 'GET' | 'POST' | 'OPTIONS' | 'PUT' | 'HEAD'; | ||
data?: any; | ||
header?: Record<string, string>; | ||
dataType?: 'json' | string; | ||
responseType?: 'text' | 'arraybuffer'; | ||
success?: (res: Response) => void; | ||
fail?: (res: Response) => void; | ||
complete?: (res: Response) => void; | ||
} | ||
declare type AppInitFn = (options: any) => void; | ||
declare type PageInitFn = (options: any) => void; | ||
declare type BackendOptions = ClientConfig; | ||
@@ -205,2 +397,10 @@ declare type BackendClass = new (options: BackendOptions, callback: BackendCallback) => Backend; | ||
protected transport?: Transport; | ||
private reportUrl; | ||
private pid; | ||
private deviceId; | ||
private appSessionId; | ||
private pageSessionId; | ||
private capturedContext; | ||
private readonly taskStack; | ||
private taskTracks; | ||
constructor(options: BackendOptions, callback: BackendCallback); | ||
@@ -214,9 +414,22 @@ init(): void; | ||
protected setupTransport(): { | ||
sendEvent(event: SendEvent): void; | ||
request(options: RequestOptions): void; | ||
sendEvent: (event: SendEvent) => void; | ||
request: (options: RequestOptions) => void; | ||
}; | ||
protected hookAppFn(App: AppInitFn): (config: any) => any; | ||
protected hookPageFn(Page: PageInitFn): (config: any) => any; | ||
protected hookAppFn(App: AppInitFn): (config: any) => void; | ||
protected hookPageFn(Page: PageInitFn): (config: any) => void; | ||
protected hookComponentFn(Component: ComponentInitFn): (config: any) => void; | ||
protected _pageLifeCycleHandlers(): (method: string, originalHandler?: LifeCycleHandler<any> | undefined) => (this: any, options: any) => void; | ||
protected hookSdk(): void; | ||
protected hookRequest(): void; | ||
protected shouldIgnoreUrl(url: string): boolean; | ||
protected hookSetData(page: any): void; | ||
protected collectDeviceId(): void; | ||
protected abstract normalizeEnvInfo(systemInfo: any, networkStatus: any): EnvInfo; | ||
private collectDeviceId; | ||
private getContext; | ||
private currentContext; | ||
private captureContext; | ||
private addTask; | ||
private endTask; | ||
private endTrack; | ||
private getTaskTrack; | ||
} | ||
@@ -223,0 +436,0 @@ |
@@ -94,2 +94,6 @@ /*! ***************************************************************************** | ||
ReportEventType["JS_ERROR"] = "js_error"; | ||
ReportEventType["REQUEST"] = "ajax"; | ||
ReportEventType["SET_DATA"] = "set_data"; | ||
ReportEventType["ON_READY"] = "on_ready"; | ||
ReportEventType["FIRST_LOAD"] = "first_load"; | ||
})(ReportEventType || (ReportEventType = {})); | ||
@@ -101,7 +105,137 @@ var BackendEventType; | ||
BackendEventType["ENV_INFO"] = "env_info"; | ||
BackendEventType["PATH"] = "path"; | ||
BackendEventType["NETWORK_STATUS"] = "network_status"; | ||
BackendEventType["JS_ERROR"] = "js_error"; | ||
BackendEventType["GLOBAL_JS_ERROR"] = "global_js_error"; | ||
BackendEventType["APP_SESSION"] = "app_session"; | ||
BackendEventType["PAGE_SESSION"] = "page_session"; | ||
BackendEventType["REQUEST"] = "request"; | ||
BackendEventType["SET_DATA"] = "set_data"; | ||
BackendEventType["PAGE_ON_READY"] = "page_on_ready"; | ||
BackendEventType["PAGE_FIRST_LOAD"] = "page_first_load"; | ||
})(BackendEventType || (BackendEventType = {})); | ||
/* eslint-disable @typescript-eslint/ban-types */ | ||
function isObject(what) { | ||
return typeof what === 'object' && what !== null && !isArray(what); | ||
} | ||
function isString(what) { | ||
return Object.prototype.toString.call(what) === '[object String]'; | ||
} | ||
function isArray(what) { | ||
return Object.prototype.toString.call(what) === '[object Array]'; | ||
} | ||
function isUndefined(what) { | ||
return what === undefined; | ||
} | ||
function isNull(what) { | ||
return what === null; | ||
} | ||
var BaseClient = /** @class */ (function () { | ||
function BaseClient() { | ||
} | ||
// 发送事件统一入口 | ||
BaseClient.prototype.sendEvent = function (event) { | ||
var _modifiedOptions = this.getEventToBeSent(event); | ||
if (_modifiedOptions) { | ||
this.idleSendEvent(_modifiedOptions); | ||
} | ||
}; | ||
BaseClient.prototype.getEventToBeSent = function (event) { | ||
var _modifiedOptions = this._modifyEvent(event); | ||
if (!this._shouldSend(_modifiedOptions)) { | ||
return; | ||
} | ||
return _modifiedOptions; | ||
}; | ||
// 修改 event 内容 | ||
BaseClient.prototype._modifyEvent = function (event) { | ||
return event; | ||
}; | ||
// event 是否发送 | ||
BaseClient.prototype._shouldSend = function (_event) { | ||
return true; | ||
}; | ||
// 需要实现的发送方法 | ||
BaseClient.prototype._send = function (_event) { | ||
return; | ||
}; | ||
// 主进程空闲时间发送 | ||
BaseClient.prototype.idleSendEvent = function (event) { | ||
this._send(event); | ||
}; | ||
return BaseClient; | ||
}()); | ||
var MAX_BATCH_REPORT_LENGTH = 10; | ||
var BATCH_REPORT_WAIT = 1000; | ||
// T: monitor 上报的数据类型, 如 ISendParams | ||
// B: 接口上报的每个 item 的数据类型, 如 BatchEventItem | ||
var BatchClient = /** @class */ (function (_super) { | ||
__extends(BatchClient, _super); | ||
function BatchClient(props) { | ||
var _a, _b; | ||
var _this = _super.call(this) || this; | ||
_this.reportQueue = []; | ||
_this.isReady = true; | ||
_this.batchReportLength = (_a = props.maxBatchReportLength) !== null && _a !== void 0 ? _a : MAX_BATCH_REPORT_LENGTH; | ||
_this.batchReportWait = (_b = props.batchReportWait) !== null && _b !== void 0 ? _b : BATCH_REPORT_WAIT; | ||
_this.batchReportTimeout = null; | ||
return _this; | ||
} | ||
Object.defineProperty(BatchClient.prototype, "ready", { | ||
/** | ||
* 上传锁,设置为 false 时所有请求将会被推进列队,等待设置回 true 时一次发出。 | ||
* | ||
* 默认为 true 不上锁,以保证旧代码不受影响。 | ||
* | ||
*/ | ||
get: function () { | ||
return this.isReady; | ||
}, | ||
set: function (v) { | ||
this.isReady = v; | ||
if (this.isReady) { | ||
this._uploadQueue(); | ||
} | ||
}, | ||
enumerable: false, | ||
configurable: true | ||
}); | ||
BatchClient.prototype._send = function (options) { | ||
var _this = this; | ||
var params = this.buildParams(options); | ||
if (isNull(params) || isUndefined(params)) | ||
return; | ||
this.reportQueue.push(params); | ||
if (!this.isReady) | ||
return; | ||
if (this.reportQueue.length >= this.batchReportLength) { | ||
this._uploadQueue(); | ||
} | ||
if (this.batchReportTimeout) { | ||
clearTimeout(this.batchReportTimeout); | ||
} | ||
this.batchReportTimeout = setTimeout(function () { | ||
_this._uploadQueue(); | ||
}, this.batchReportWait); | ||
}; | ||
BatchClient.prototype._uploadQueue = function () { | ||
if (!this.reportQueue.length || !this.ready) { | ||
return; | ||
} | ||
var body = { | ||
ev_type: 'batch', | ||
list: this.reportQueue, | ||
}; | ||
this.reportQueue = []; | ||
this._request({ event: body, type: 'post' }); | ||
}; | ||
// 需要实现的方法 | ||
BatchClient.prototype._request = function (_options) { | ||
return; | ||
}; | ||
return BatchClient; | ||
}(BaseClient)); | ||
/** | ||
@@ -178,5 +312,37 @@ * 生成uuid | ||
}; | ||
var pick = function (obj, keys) { | ||
var newObj = {}; | ||
keys.forEach(function (key) { | ||
newObj[key] = obj[key]; | ||
}); | ||
return newObj; | ||
}; | ||
// https://stackoverflow.com/a/12205668 | ||
var strSize = function (str) { | ||
return encodeURI(str).split(/%(?:u[0-9A-F]{2})?[0-9A-F]{2}|./).length - 1; | ||
}; | ||
var getDataLength = function (d) { | ||
return d | ||
? d instanceof ArrayBuffer | ||
? d.byteLength | ||
: isObject(d) | ||
? strSize(serialize(d)) | ||
: d.length | ||
? d.length | ||
: 0 | ||
: 0; | ||
}; | ||
var getContentLength = function (res) { | ||
var _a, _b, _c; | ||
var contentLengthKey = (_b = Object.keys((_a = res.header) !== null && _a !== void 0 ? _a : {}).find(function (k) { return k.toLowerCase() === 'content-legth'; })) !== null && _b !== void 0 ? _b : ''; | ||
if ((_c = res.header) === null || _c === void 0 ? void 0 : _c[contentLengthKey]) { | ||
return parseInt(res.header[contentLengthKey]); | ||
} | ||
return getDataLength(res.data); | ||
}; | ||
var UnhandledRejection = 'UnhandledRejection'; | ||
var DEVICE_ID_STORAGE_KEY = 'RANGERS_MINI_SDK_DEVICE_ID'; | ||
var RANGERS_PAGE_SESSION_ID = 'RANGERS_PAGE_SESSION_ID'; | ||
var RANGERS_PAGE_TIMING = 'RANGERS_PAGE_TIMING'; | ||
@@ -210,4 +376,3 @@ var BackendType; | ||
(function (SDKMethod) { | ||
SDKMethod["onError"] = "onError"; | ||
SDKMethod["onUnhandledRejection"] = "onUnhandledRejection"; | ||
SDKMethod["request"] = "request"; | ||
})(SDKMethod || (SDKMethod = {})); | ||
@@ -224,2 +389,27 @@ | ||
}; | ||
var hookComponentLifeCycles = function (lifeCycleConfigFunction, methods, wrapFn) { | ||
return function (config) { | ||
if (!config.methods) { | ||
config.methods = {}; | ||
} | ||
methods.forEach(function (method) { | ||
var originalHandler = config.methods[method]; | ||
config.methods[method] = wrapFn(method, originalHandler); | ||
}); | ||
return lifeCycleConfigFunction(config); | ||
}; | ||
}; | ||
var hookObjectMethods = function (target, methods, wrapFn) { | ||
methods.forEach(function (method) { | ||
if (target[method]) { | ||
var originalHandler = target[method]; | ||
Object.defineProperty(target, method, { | ||
enumerable: false, | ||
configurable: false, | ||
writable: false, | ||
value: wrapFn(method, originalHandler).bind(target), | ||
}); | ||
} | ||
}); | ||
}; | ||
var unknown = 'unknown'; | ||
@@ -338,3 +528,11 @@ var getHostPlatform = function () { | ||
this.originalPageFn = Page; | ||
this.reportUrl = ''; | ||
this.deviceId = ''; | ||
this.appSessionId = ''; | ||
this.pageSessionId = ''; | ||
this.capturedContext = null; | ||
this.taskStack = []; | ||
this.taskTracks = {}; | ||
this.options = options; | ||
this.pid = options.pid; | ||
this.callback = callback; | ||
@@ -346,5 +544,7 @@ // init later so the subclass can inject sdk | ||
this.transport = this.setupTransport(); | ||
this.collectDeviceId(); | ||
App = this.hookAppFn(App); | ||
Page = this.hookPageFn(Page); | ||
this.collectDeviceId(); | ||
Component = this.hookComponentFn(Component); | ||
this.hookSdk(); | ||
if (this.sdk.onNetworkStatusChange) { | ||
@@ -417,8 +617,8 @@ this.sdk.onNetworkStatusChange(function (options) { | ||
Backend.prototype.setupTransport = function () { | ||
var that = this; | ||
var reportUrl = getBatchReportURL(this.options.reportDomain, this.options.reportPath); | ||
var _this = this; | ||
this.reportUrl = getBatchReportURL(this.options.reportDomain, this.options.reportPath); | ||
return { | ||
sendEvent: function (event) { | ||
that.sdk.request({ | ||
url: reportUrl, | ||
_this.sdk.request({ | ||
url: _this.reportUrl, | ||
method: 'POST', | ||
@@ -429,3 +629,3 @@ data: event, | ||
request: function (options) { | ||
that.sdk.request(options); | ||
_this.sdk.request(options); | ||
}, | ||
@@ -435,16 +635,21 @@ }; | ||
Backend.prototype.hookAppFn = function (App) { | ||
var that = this; | ||
var callback = this.callback; | ||
return hookLifeCycles(App, [ | ||
AppMethod.onLaunch, | ||
AppMethod.onShow, | ||
AppMethod.onHide, | ||
AppMethod.onError, | ||
AppMethod.onPageNotFound, | ||
AppMethod.onUnhandledRejection, | ||
], function (method, originalHandler) { | ||
return function (options) { | ||
return hookLifeCycles(App, [AppMethod.onLaunch, AppMethod.onShow, AppMethod.onHide, AppMethod.onError, AppMethod.onUnhandledRejection], function (method, originalHandler) { | ||
function wrappedAppHandler(options) { | ||
switch (method) { | ||
case AppMethod.onShow: { | ||
var appSessionId = uuid(); | ||
that.appSessionId = appSessionId; | ||
callback({ | ||
ev_type: BackendEventType.APP_SESSION, | ||
data: { | ||
appSessionId: appSessionId, | ||
}, | ||
}); | ||
break; | ||
} | ||
case AppMethod.onError: { | ||
callback({ | ||
ev_type: BackendEventType.JS_ERROR, | ||
ev_type: BackendEventType.GLOBAL_JS_ERROR, | ||
data: makeError(options), | ||
@@ -457,3 +662,3 @@ }); | ||
callback({ | ||
ev_type: BackendEventType.JS_ERROR, | ||
ev_type: BackendEventType.GLOBAL_JS_ERROR, | ||
data: data, | ||
@@ -466,29 +671,271 @@ }); | ||
originalHandler === null || originalHandler === void 0 ? void 0 : originalHandler.call(this, options); | ||
}; | ||
} | ||
return wrappedAppHandler; | ||
}); | ||
}; | ||
Backend.prototype.hookPageFn = function (Page) { | ||
return hookLifeCycles(Page, [PageMethod.onLoad, PageMethod.onShow, PageMethod.onReady], this._pageLifeCycleHandlers()); | ||
}; | ||
Backend.prototype.hookComponentFn = function (Component) { | ||
return hookComponentLifeCycles(Component, [PageMethod.onLoad, PageMethod.onShow, PageMethod.onReady], this._pageLifeCycleHandlers()); | ||
}; | ||
Backend.prototype._pageLifeCycleHandlers = function () { | ||
var that = this; | ||
var callback = this.callback; | ||
return hookLifeCycles(Page, [ | ||
PageMethod.onLoad, | ||
PageMethod.onShow, | ||
PageMethod.onReady, | ||
PageMethod.onHide, | ||
PageMethod.onUnload, | ||
PageMethod.onPullDownRefresh, | ||
], function (method, originalHandler) { | ||
return function (options) { | ||
return function (method, originalHandler) { | ||
function callOrigin(options) { | ||
try { | ||
originalHandler === null || originalHandler === void 0 ? void 0 : originalHandler.call(this, options); | ||
} | ||
catch (err) { | ||
callback({ | ||
ev_type: BackendEventType.JS_ERROR, | ||
data: { | ||
name: err.name, | ||
message: err.message, | ||
stack: err.stack, | ||
rawMessage: String(err), | ||
}, | ||
context: that.getContext(), | ||
}); | ||
} | ||
} | ||
function wrappedPageHandler(options) { | ||
var pid = getPid(); | ||
switch (method) { | ||
case PageMethod.onLoad: { | ||
that.pid = pid; | ||
that.hookSetData(this); | ||
var onLoadStart = Date.now(); | ||
var pageSessionId = [that.deviceId, that.appSessionId, that.pid, onLoadStart].join('|'); | ||
that.pageSessionId = pageSessionId; | ||
that.taskTracks[pageSessionId] = { | ||
tasks: [], | ||
timing: { | ||
firstReqStart: onLoadStart, | ||
firstReqEnd: 0, | ||
firstLoadStart: onLoadStart, | ||
firstLoadEnd: 0, | ||
}, | ||
ready: false, | ||
ended: false, | ||
}; | ||
callback({ | ||
ev_type: BackendEventType.PAGE_SESSION, | ||
data: { | ||
pid: pid, | ||
pageSessionId: pageSessionId, | ||
}, | ||
}); | ||
callOrigin.call(this, options); | ||
this[RANGERS_PAGE_SESSION_ID] = pageSessionId; | ||
this[RANGERS_PAGE_TIMING] = { | ||
onLoadStart: onLoadStart, | ||
onLoadEnd: Date.now(), | ||
onShowStart: 0, | ||
onShowEnd: 0, | ||
onReadyStart: 0, | ||
onReadyEnd: 0, | ||
}; | ||
return; | ||
} | ||
case PageMethod.onShow: { | ||
var pid = getPid(); | ||
var pageSessionId = this[RANGERS_PAGE_SESSION_ID]; | ||
that.pageSessionId = pageSessionId; | ||
callback({ | ||
ev_type: BackendEventType.PATH, | ||
ev_type: BackendEventType.PAGE_SESSION, | ||
data: { | ||
pid: pid, | ||
pageSessionId: pageSessionId, | ||
}, | ||
}); | ||
break; | ||
var timing = this[RANGERS_PAGE_TIMING]; | ||
timing.onShowStart = timing.onShowStart || Date.now(); | ||
callOrigin.call(this, options); | ||
timing.onShowEnd = timing.onShowEnd || Date.now(); | ||
return; | ||
} | ||
case PageMethod.onReady: { | ||
var timing = this[RANGERS_PAGE_TIMING]; | ||
timing.onReadyStart = Date.now(); | ||
callOrigin.call(this, options); | ||
timing.onReadyEnd = Date.now(); | ||
callback({ | ||
ev_type: BackendEventType.PAGE_ON_READY, | ||
data: timing, | ||
}); | ||
var taskTrack = that.getTaskTrack(); | ||
taskTrack.ready = true; | ||
if (!taskTrack.timing.firstReqEnd && taskTrack.tasks.every(function (t) { return t.completed || t.type === 'SET_DATA'; })) { | ||
taskTrack.timing.firstReqEnd = taskTrack.timing.firstReqStart; // no first req | ||
} | ||
if (taskTrack.tasks.every(function (t) { return t.completed; })) { | ||
taskTrack.timing.firstLoadEnd = timing.onReadyEnd; | ||
that.endTrack(taskTrack); | ||
} | ||
return; | ||
} | ||
} | ||
originalHandler === null || originalHandler === void 0 ? void 0 : originalHandler.call(this, options); | ||
callOrigin.call(this, options); | ||
} | ||
return wrappedPageHandler; | ||
}; | ||
}; | ||
Backend.prototype.hookSdk = function () { | ||
this.hookRequest(); | ||
}; | ||
Backend.prototype.hookRequest = function () { | ||
var _this = this; | ||
var that = this; | ||
var callback = this.callback; | ||
hookObjectMethods(this.sdk, [SDKMethod.request], function (_, request) { return function (options) { | ||
var _b; | ||
if (_this.shouldIgnoreUrl(options.url)) { | ||
return request(options); | ||
} | ||
var originalSuccess = options.success; | ||
var originalFail = options.fail; | ||
var originalComplete = options.complete; | ||
var req_size = getDataLength((_b = options.data) !== null && _b !== void 0 ? _b : ''); | ||
var taskId = _this.addTask('REQUEST'); | ||
var req_start_time = Date.now(); | ||
var success = _this.captureContext(function (res) { | ||
var _a, _b; | ||
var req_end_time = Date.now(); | ||
callback({ | ||
ev_type: BackendEventType.REQUEST, | ||
data: { | ||
req_url: options.url, | ||
method: (_a = options.method) !== null && _a !== void 0 ? _a : 'GET', | ||
err_msg: res.statusCode >= 300 ? serialize(res.data) : '', | ||
req_status: 1, | ||
req_start_time: req_start_time, | ||
req_end_time: req_end_time, | ||
resp_status: res.statusCode, | ||
header: (_b = options.header) !== null && _b !== void 0 ? _b : {}, | ||
req_param: res.statusCode >= 300 ? serialize(options.data) : '', | ||
req_size: req_size, | ||
resp_size: getContentLength(res), | ||
perf: res.profile | ||
? pick(res.profile, [ | ||
'redirectStart', | ||
'redirectEnd', | ||
'fetchStart', | ||
'domainLookupStart', | ||
'domainLookupEnd', | ||
'connectStart', | ||
'connectEnd', | ||
'SSLconnectionStart', | ||
'SSLconnectionEnd', | ||
'requestStart', | ||
'requestEnd', | ||
'responseStart', | ||
'responseEnd', | ||
'rtt', | ||
'httpRttEstimate', | ||
'transportRttEstimate', | ||
]) | ||
: {}, | ||
}, | ||
context: that.capturedContext, | ||
}); | ||
that.taskStack.push(taskId); | ||
try { | ||
originalSuccess === null || originalSuccess === void 0 ? void 0 : originalSuccess.call(this, res); | ||
} | ||
finally { | ||
that.taskStack.pop(); | ||
} | ||
}); | ||
var fail = _this.captureContext(function (res) { | ||
var _a, _b, _c; | ||
var req_end_time = Date.now(); | ||
callback({ | ||
ev_type: BackendEventType.REQUEST, | ||
data: { | ||
req_url: options.url, | ||
method: (_a = options.method) !== null && _a !== void 0 ? _a : 'GET', | ||
err_msg: (_b = res.errMsg) !== null && _b !== void 0 ? _b : '', | ||
req_status: 0, | ||
req_start_time: req_start_time, | ||
req_end_time: req_end_time, | ||
resp_status: 0, | ||
req_param: serialize(options.data), | ||
header: (_c = options.header) !== null && _c !== void 0 ? _c : {}, | ||
req_size: req_size, | ||
resp_size: 0, | ||
perf: {}, | ||
}, | ||
context: that.capturedContext, | ||
}); | ||
that.taskStack.push(taskId); | ||
try { | ||
originalFail === null || originalFail === void 0 ? void 0 : originalFail.call(this, res); | ||
} | ||
finally { | ||
that.taskStack.pop(); | ||
} | ||
}); | ||
var complete = _this.captureContext(function (res) { | ||
that.taskStack.push(taskId); | ||
try { | ||
originalComplete === null || originalComplete === void 0 ? void 0 : originalComplete.call(this, res); | ||
} | ||
finally { | ||
that.taskStack.pop(); | ||
} | ||
that.endTask(taskId, Date.now()); | ||
}); | ||
var newOptions = __assign(__assign({}, options), { success: success, | ||
fail: fail, | ||
complete: complete }); | ||
return request(newOptions); | ||
}; }); | ||
}; | ||
Backend.prototype.shouldIgnoreUrl = function (url) { | ||
return url === this.reportUrl; | ||
}; | ||
Backend.prototype.hookSetData = function (page) { | ||
var that = this; | ||
hookObjectMethods(page, ['setData'], function (_, setData) { | ||
return function wrappedSetData(data, callback) { | ||
var _this = this; | ||
var originalCallback = callback; | ||
var taskId = that.addTask('SET_DATA'); | ||
var setDataStart = Date.now(); | ||
var wrappedCallback = that.captureContext(function () { | ||
var setDataEnd = Date.now(); | ||
var dataJson = serialize(data); | ||
that.callback({ | ||
ev_type: BackendEventType.SET_DATA, | ||
data: { | ||
setDataStart: setDataStart, | ||
setDataEnd: setDataEnd, | ||
setDataSize: strSize(dataJson), | ||
data: dataJson, | ||
}, | ||
context: that.capturedContext, | ||
}); | ||
that.taskStack.push(taskId); | ||
try { | ||
originalCallback === null || originalCallback === void 0 ? void 0 : originalCallback.call(_this); | ||
} | ||
catch (err) { | ||
that.callback({ | ||
ev_type: BackendEventType.JS_ERROR, | ||
data: { | ||
name: err.name, | ||
message: err.message, | ||
stack: err.stack, | ||
rawMessage: String(err), | ||
}, | ||
context: that.getContext(), | ||
}); | ||
} | ||
finally { | ||
that.taskStack.pop(); | ||
} | ||
that.endTask(taskId, setDataEnd); | ||
}); | ||
setData.call(this, data, wrappedCallback); | ||
}; | ||
@@ -503,2 +950,3 @@ }); | ||
} | ||
this.deviceId = deviceId; | ||
this.callback({ | ||
@@ -511,2 +959,95 @@ ev_type: BackendEventType.DEVICE_ID, | ||
}; | ||
Backend.prototype.getContext = function () { | ||
var _a; | ||
return (_a = this.capturedContext) !== null && _a !== void 0 ? _a : this.currentContext(); | ||
}; | ||
Backend.prototype.currentContext = function () { | ||
return { | ||
pid: this.pid, | ||
pageSessionId: this.pageSessionId, | ||
context: this.options.context, | ||
}; | ||
}; | ||
Backend.prototype.captureContext = function (fn) { | ||
var that = this; | ||
var context = this.getContext(); | ||
// log('captureContext', context) | ||
return function () { | ||
var args = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
args[_i] = arguments[_i]; | ||
} | ||
// log('call with capturedContext', context) | ||
// log('currentContext', that.currentContext()) | ||
that.capturedContext = context; | ||
var res; | ||
try { | ||
res = fn.apply(this, args); | ||
} | ||
catch (err) { | ||
that.callback({ | ||
ev_type: BackendEventType.JS_ERROR, | ||
data: { | ||
name: err.name, | ||
message: err.message, | ||
stack: err.stack, | ||
rawMessage: String(err), | ||
}, | ||
context: context, | ||
}); | ||
} | ||
finally { | ||
that.capturedContext = null; | ||
} | ||
return res; | ||
}; | ||
}; | ||
Backend.prototype.addTask = function (type) { | ||
var taskTrack = this.getTaskTrack(); | ||
if (!taskTrack || taskTrack.ended) { | ||
return 0; | ||
} | ||
var parentTaskId = this.taskStack.length && this.taskStack[this.taskStack.length - 1]; | ||
if (taskTrack.ready && (!parentTaskId || !taskTrack.tasks.some(function (t) { return t.id === parentTaskId; }))) { | ||
return 0; | ||
} | ||
var id = taskTrack.tasks.length + 1; | ||
taskTrack.tasks.push({ | ||
id: id, | ||
type: type, | ||
completed: false, | ||
}); | ||
return id; | ||
}; | ||
Backend.prototype.endTask = function (id, now) { | ||
var taskTrack = this.getTaskTrack(); | ||
if (id <= 0 || !taskTrack || taskTrack.ended) { | ||
return; | ||
} | ||
var task = taskTrack.tasks[id - 1]; | ||
task.completed = true; | ||
if (!taskTrack.timing.firstReqEnd && task.type === 'REQUEST') { | ||
taskTrack.timing.firstReqEnd = now; | ||
} | ||
if (taskTrack.tasks.every(function (t) { return t.completed; })) { | ||
taskTrack.timing.firstLoadEnd = now; | ||
this.endTrack(taskTrack); | ||
} | ||
}; | ||
Backend.prototype.endTrack = function (taskTrack) { | ||
if (!taskTrack.ready) { | ||
return; | ||
} | ||
taskTrack.ended = true; | ||
this.callback({ | ||
ev_type: BackendEventType.PAGE_FIRST_LOAD, | ||
data: taskTrack.timing, | ||
context: this.getContext(), | ||
}); | ||
}; | ||
Backend.prototype.getTaskTrack = function () { | ||
var context = this.getContext(); | ||
var pageSessionId = context.pageSessionId; | ||
return this.taskTracks[pageSessionId]; | ||
}; | ||
return Backend; | ||
@@ -645,126 +1186,2 @@ }()); | ||
/* eslint-disable @typescript-eslint/ban-types */ | ||
function isObject(what) { | ||
return typeof what === 'object' && what !== null && !isArray(what); | ||
} | ||
function isString(what) { | ||
return Object.prototype.toString.call(what) === '[object String]'; | ||
} | ||
function isArray(what) { | ||
return Object.prototype.toString.call(what) === '[object Array]'; | ||
} | ||
function isUndefined(what) { | ||
return what === undefined; | ||
} | ||
function isNull(what) { | ||
return what === null; | ||
} | ||
var BaseClient = /** @class */ (function () { | ||
function BaseClient() { | ||
} | ||
// 发送事件统一入口 | ||
BaseClient.prototype.sendEvent = function (event) { | ||
var _modifiedOptions = this.getEventToBeSent(event); | ||
if (_modifiedOptions) { | ||
this.idleSendEvent(_modifiedOptions); | ||
} | ||
}; | ||
BaseClient.prototype.getEventToBeSent = function (event) { | ||
var _modifiedOptions = this._modifyEvent(event); | ||
if (!this._shouldSend(_modifiedOptions)) { | ||
return; | ||
} | ||
return _modifiedOptions; | ||
}; | ||
// 修改 event 内容 | ||
BaseClient.prototype._modifyEvent = function (event) { | ||
return event; | ||
}; | ||
// event 是否发送 | ||
BaseClient.prototype._shouldSend = function (_event) { | ||
return true; | ||
}; | ||
// 需要实现的发送方法 | ||
BaseClient.prototype._send = function (_event) { | ||
return; | ||
}; | ||
// 主进程空闲时间发送 | ||
BaseClient.prototype.idleSendEvent = function (event) { | ||
this._send(event); | ||
}; | ||
return BaseClient; | ||
}()); | ||
var MAX_BATCH_REPORT_LENGTH = 10; | ||
var BATCH_REPORT_WAIT = 1000; | ||
// T: monitor 上报的数据类型, 如 ISendParams | ||
// B: 接口上报的每个 item 的数据类型, 如 BatchEventItem | ||
var BatchClient = /** @class */ (function (_super) { | ||
__extends(BatchClient, _super); | ||
function BatchClient(props) { | ||
var _a, _b; | ||
var _this = _super.call(this) || this; | ||
_this.reportQueue = []; | ||
_this.isReady = true; | ||
_this.batchReportLength = (_a = props.maxBatchReportLength) !== null && _a !== void 0 ? _a : MAX_BATCH_REPORT_LENGTH; | ||
_this.batchReportWait = (_b = props.batchReportWait) !== null && _b !== void 0 ? _b : BATCH_REPORT_WAIT; | ||
_this.batchReportTimeout = null; | ||
return _this; | ||
} | ||
Object.defineProperty(BatchClient.prototype, "ready", { | ||
/** | ||
* 上传锁,设置为 false 时所有请求将会被推进列队,等待设置回 true 时一次发出。 | ||
* | ||
* 默认为 true 不上锁,以保证旧代码不受影响。 | ||
* | ||
*/ | ||
get: function () { | ||
return this.isReady; | ||
}, | ||
set: function (v) { | ||
this.isReady = v; | ||
if (this.isReady) { | ||
this._uploadQueue(); | ||
} | ||
}, | ||
enumerable: false, | ||
configurable: true | ||
}); | ||
BatchClient.prototype._send = function (options) { | ||
var _this = this; | ||
var params = this.buildParams(options); | ||
if (isNull(params) || isUndefined(params)) | ||
return; | ||
this.reportQueue.push(params); | ||
if (!this.isReady) | ||
return; | ||
if (this.reportQueue.length >= this.batchReportLength) { | ||
this._uploadQueue(); | ||
} | ||
if (this.batchReportTimeout) { | ||
clearTimeout(this.batchReportTimeout); | ||
} | ||
this.batchReportTimeout = setTimeout(function () { | ||
_this._uploadQueue(); | ||
}, this.batchReportWait); | ||
}; | ||
BatchClient.prototype._uploadQueue = function () { | ||
if (!this.reportQueue.length || !this.ready) { | ||
return; | ||
} | ||
var body = { | ||
ev_type: 'batch', | ||
list: this.reportQueue, | ||
}; | ||
this.reportQueue = []; | ||
this._request({ event: body, type: 'post' }); | ||
}; | ||
// 需要实现的方法 | ||
BatchClient.prototype._request = function (_options) { | ||
return; | ||
}; | ||
return BatchClient; | ||
}(BaseClient)); | ||
var DEFAULT_REPORT_DOMAIN = "" + "tbm.snssdk.com"; | ||
@@ -846,2 +1263,4 @@ var DEFAULT_REPORT_BATCH_PATH = '/monitor_microapp/collect'; | ||
_this.envInfo = {}; | ||
_this.appSessionId = ''; | ||
_this.pageSessionId = ''; | ||
_this.preQueue = []; | ||
@@ -867,3 +1286,3 @@ _this.ready = false; // lock the client | ||
}; | ||
MiniProgramClient.prototype.reportError = function (error) { | ||
MiniProgramClient.prototype.reportError = function (error, context) { | ||
var _a; | ||
@@ -878,11 +1297,11 @@ this.reportEvent({ | ||
breadcrumbs: __spread(this.breadcrumbs), | ||
}); | ||
}, context); | ||
}; | ||
MiniProgramClient.prototype.reportEvent = function (event) { | ||
MiniProgramClient.prototype.reportEvent = function (event, context) { | ||
if (!this.ready) { | ||
// 状态没有 ready 的时候,先存入队列 | ||
this.preQueue.push(event); | ||
this.preQueue.push(__assign(__assign({}, event), { context: context })); | ||
} | ||
else { | ||
_super.prototype.sendEvent.call(this, event); | ||
_super.prototype.sendEvent.call(this, __assign(__assign({}, event), { context: context })); | ||
} | ||
@@ -928,4 +1347,24 @@ }; | ||
} | ||
case BackendEventType.PATH: { | ||
if (this.pid !== event.data.pid) { | ||
case BackendEventType.GLOBAL_JS_ERROR: { | ||
this.reportEvent({ | ||
ev_type: ReportEventType.JS_ERROR, | ||
event: event.data, | ||
breadcrumbs: __spread(this.breadcrumbs), | ||
}); | ||
break; | ||
} | ||
case BackendEventType.JS_ERROR: { | ||
this.reportEvent({ | ||
ev_type: ReportEventType.JS_ERROR, | ||
event: event.data, | ||
breadcrumbs: __spread(this.breadcrumbs), | ||
}, event.context); | ||
break; | ||
} | ||
case BackendEventType.APP_SESSION: { | ||
this.appSessionId = event.data.appSessionId; | ||
break; | ||
} | ||
case BackendEventType.PAGE_SESSION: { | ||
if (this.pageSessionId !== event.data.pageSessionId) { | ||
this.addBreadcrumb(BreadcrumbType.DOM, { | ||
@@ -936,2 +1375,3 @@ category: BreadcrumbCategory.ROUTE, | ||
this.pid = event.data.pid; | ||
this.pageSessionId = event.data.pageSessionId; | ||
this.sendPageView(); | ||
@@ -941,10 +1381,30 @@ } | ||
} | ||
case BackendEventType.JS_ERROR: { | ||
case BackendEventType.REQUEST: { | ||
this.reportEvent({ | ||
ev_type: ReportEventType.JS_ERROR, | ||
ev_type: ReportEventType.REQUEST, | ||
event: event.data, | ||
breadcrumbs: __spread(this.breadcrumbs), | ||
}, event.context); | ||
break; | ||
} | ||
case BackendEventType.SET_DATA: { | ||
this.reportEvent({ | ||
ev_type: ReportEventType.SET_DATA, | ||
event: event.data, | ||
}, event.context); | ||
break; | ||
} | ||
case BackendEventType.PAGE_ON_READY: { | ||
this.reportEvent({ | ||
ev_type: ReportEventType.ON_READY, | ||
event: event.data, | ||
}); | ||
break; | ||
} | ||
case BackendEventType.PAGE_FIRST_LOAD: { | ||
this.reportEvent({ | ||
ev_type: ReportEventType.FIRST_LOAD, | ||
event: event.data, | ||
}, event.context); | ||
break; | ||
} | ||
} | ||
@@ -956,3 +1416,4 @@ }; | ||
MiniProgramClient.prototype.buildParams = function (options) { | ||
return __assign(__assign({}, options), { common: __assign(__assign({}, this.envInfo), { aid: this.config.aid, pid: this.pid, rangers_sdk_version: this.config.version, timestamp: Date.now(), user_unique_id: this.finderConfig ? this.finderConfig.user_unique_id : this.config.userId || this.deviceId, device_id: this.deviceId, session_id: this.config.sessionId, context: this.config.context }) }); | ||
var _a, _b, _c, _d, _e, _f; | ||
return __assign(__assign({}, options), { common: __assign(__assign({}, this.envInfo), { aid: this.config.aid, pid: (_b = (_a = options.context) === null || _a === void 0 ? void 0 : _a.pid) !== null && _b !== void 0 ? _b : this.pid, rangers_sdk_version: this.config.version, timestamp: Date.now(), user_unique_id: this.finderConfig ? this.finderConfig.user_unique_id : this.config.userId || this.deviceId, device_id: this.deviceId, session_id: this.config.sessionId, app_session_id: this.appSessionId, page_session_id: (_d = (_c = options.context) === null || _c === void 0 ? void 0 : _c.pageSessionId) !== null && _d !== void 0 ? _d : this.pageSessionId, context: (_f = (_e = options.context) === null || _e === void 0 ? void 0 : _e.context) !== null && _f !== void 0 ? _f : this.config.context }), context: undefined }); | ||
}; | ||
@@ -986,3 +1447,3 @@ /** | ||
function Rangers(options) { | ||
this.version = '0.0.5'; | ||
this.version = '0.0.6'; | ||
this.Backend = options.Backend; | ||
@@ -989,0 +1450,0 @@ this.defaultConfig = getDefaultConfig(); |
801
cn/index.js
@@ -96,2 +96,6 @@ 'use strict'; | ||
ReportEventType["JS_ERROR"] = "js_error"; | ||
ReportEventType["REQUEST"] = "ajax"; | ||
ReportEventType["SET_DATA"] = "set_data"; | ||
ReportEventType["ON_READY"] = "on_ready"; | ||
ReportEventType["FIRST_LOAD"] = "first_load"; | ||
})(exports.ReportEventType || (exports.ReportEventType = {})); | ||
@@ -103,7 +107,137 @@ var BackendEventType; | ||
BackendEventType["ENV_INFO"] = "env_info"; | ||
BackendEventType["PATH"] = "path"; | ||
BackendEventType["NETWORK_STATUS"] = "network_status"; | ||
BackendEventType["JS_ERROR"] = "js_error"; | ||
BackendEventType["GLOBAL_JS_ERROR"] = "global_js_error"; | ||
BackendEventType["APP_SESSION"] = "app_session"; | ||
BackendEventType["PAGE_SESSION"] = "page_session"; | ||
BackendEventType["REQUEST"] = "request"; | ||
BackendEventType["SET_DATA"] = "set_data"; | ||
BackendEventType["PAGE_ON_READY"] = "page_on_ready"; | ||
BackendEventType["PAGE_FIRST_LOAD"] = "page_first_load"; | ||
})(BackendEventType || (BackendEventType = {})); | ||
/* eslint-disable @typescript-eslint/ban-types */ | ||
function isObject(what) { | ||
return typeof what === 'object' && what !== null && !isArray(what); | ||
} | ||
function isString(what) { | ||
return Object.prototype.toString.call(what) === '[object String]'; | ||
} | ||
function isArray(what) { | ||
return Object.prototype.toString.call(what) === '[object Array]'; | ||
} | ||
function isUndefined(what) { | ||
return what === undefined; | ||
} | ||
function isNull(what) { | ||
return what === null; | ||
} | ||
var BaseClient = /** @class */ (function () { | ||
function BaseClient() { | ||
} | ||
// 发送事件统一入口 | ||
BaseClient.prototype.sendEvent = function (event) { | ||
var _modifiedOptions = this.getEventToBeSent(event); | ||
if (_modifiedOptions) { | ||
this.idleSendEvent(_modifiedOptions); | ||
} | ||
}; | ||
BaseClient.prototype.getEventToBeSent = function (event) { | ||
var _modifiedOptions = this._modifyEvent(event); | ||
if (!this._shouldSend(_modifiedOptions)) { | ||
return; | ||
} | ||
return _modifiedOptions; | ||
}; | ||
// 修改 event 内容 | ||
BaseClient.prototype._modifyEvent = function (event) { | ||
return event; | ||
}; | ||
// event 是否发送 | ||
BaseClient.prototype._shouldSend = function (_event) { | ||
return true; | ||
}; | ||
// 需要实现的发送方法 | ||
BaseClient.prototype._send = function (_event) { | ||
return; | ||
}; | ||
// 主进程空闲时间发送 | ||
BaseClient.prototype.idleSendEvent = function (event) { | ||
this._send(event); | ||
}; | ||
return BaseClient; | ||
}()); | ||
var MAX_BATCH_REPORT_LENGTH = 10; | ||
var BATCH_REPORT_WAIT = 1000; | ||
// T: monitor 上报的数据类型, 如 ISendParams | ||
// B: 接口上报的每个 item 的数据类型, 如 BatchEventItem | ||
var BatchClient = /** @class */ (function (_super) { | ||
__extends(BatchClient, _super); | ||
function BatchClient(props) { | ||
var _a, _b; | ||
var _this = _super.call(this) || this; | ||
_this.reportQueue = []; | ||
_this.isReady = true; | ||
_this.batchReportLength = (_a = props.maxBatchReportLength) !== null && _a !== void 0 ? _a : MAX_BATCH_REPORT_LENGTH; | ||
_this.batchReportWait = (_b = props.batchReportWait) !== null && _b !== void 0 ? _b : BATCH_REPORT_WAIT; | ||
_this.batchReportTimeout = null; | ||
return _this; | ||
} | ||
Object.defineProperty(BatchClient.prototype, "ready", { | ||
/** | ||
* 上传锁,设置为 false 时所有请求将会被推进列队,等待设置回 true 时一次发出。 | ||
* | ||
* 默认为 true 不上锁,以保证旧代码不受影响。 | ||
* | ||
*/ | ||
get: function () { | ||
return this.isReady; | ||
}, | ||
set: function (v) { | ||
this.isReady = v; | ||
if (this.isReady) { | ||
this._uploadQueue(); | ||
} | ||
}, | ||
enumerable: false, | ||
configurable: true | ||
}); | ||
BatchClient.prototype._send = function (options) { | ||
var _this = this; | ||
var params = this.buildParams(options); | ||
if (isNull(params) || isUndefined(params)) | ||
return; | ||
this.reportQueue.push(params); | ||
if (!this.isReady) | ||
return; | ||
if (this.reportQueue.length >= this.batchReportLength) { | ||
this._uploadQueue(); | ||
} | ||
if (this.batchReportTimeout) { | ||
clearTimeout(this.batchReportTimeout); | ||
} | ||
this.batchReportTimeout = setTimeout(function () { | ||
_this._uploadQueue(); | ||
}, this.batchReportWait); | ||
}; | ||
BatchClient.prototype._uploadQueue = function () { | ||
if (!this.reportQueue.length || !this.ready) { | ||
return; | ||
} | ||
var body = { | ||
ev_type: 'batch', | ||
list: this.reportQueue, | ||
}; | ||
this.reportQueue = []; | ||
this._request({ event: body, type: 'post' }); | ||
}; | ||
// 需要实现的方法 | ||
BatchClient.prototype._request = function (_options) { | ||
return; | ||
}; | ||
return BatchClient; | ||
}(BaseClient)); | ||
/** | ||
@@ -180,5 +314,37 @@ * 生成uuid | ||
}; | ||
var pick = function (obj, keys) { | ||
var newObj = {}; | ||
keys.forEach(function (key) { | ||
newObj[key] = obj[key]; | ||
}); | ||
return newObj; | ||
}; | ||
// https://stackoverflow.com/a/12205668 | ||
var strSize = function (str) { | ||
return encodeURI(str).split(/%(?:u[0-9A-F]{2})?[0-9A-F]{2}|./).length - 1; | ||
}; | ||
var getDataLength = function (d) { | ||
return d | ||
? d instanceof ArrayBuffer | ||
? d.byteLength | ||
: isObject(d) | ||
? strSize(serialize(d)) | ||
: d.length | ||
? d.length | ||
: 0 | ||
: 0; | ||
}; | ||
var getContentLength = function (res) { | ||
var _a, _b, _c; | ||
var contentLengthKey = (_b = Object.keys((_a = res.header) !== null && _a !== void 0 ? _a : {}).find(function (k) { return k.toLowerCase() === 'content-legth'; })) !== null && _b !== void 0 ? _b : ''; | ||
if ((_c = res.header) === null || _c === void 0 ? void 0 : _c[contentLengthKey]) { | ||
return parseInt(res.header[contentLengthKey]); | ||
} | ||
return getDataLength(res.data); | ||
}; | ||
var UnhandledRejection = 'UnhandledRejection'; | ||
var DEVICE_ID_STORAGE_KEY = 'RANGERS_MINI_SDK_DEVICE_ID'; | ||
var RANGERS_PAGE_SESSION_ID = 'RANGERS_PAGE_SESSION_ID'; | ||
var RANGERS_PAGE_TIMING = 'RANGERS_PAGE_TIMING'; | ||
@@ -212,4 +378,3 @@ var BackendType; | ||
(function (SDKMethod) { | ||
SDKMethod["onError"] = "onError"; | ||
SDKMethod["onUnhandledRejection"] = "onUnhandledRejection"; | ||
SDKMethod["request"] = "request"; | ||
})(SDKMethod || (SDKMethod = {})); | ||
@@ -226,2 +391,27 @@ | ||
}; | ||
var hookComponentLifeCycles = function (lifeCycleConfigFunction, methods, wrapFn) { | ||
return function (config) { | ||
if (!config.methods) { | ||
config.methods = {}; | ||
} | ||
methods.forEach(function (method) { | ||
var originalHandler = config.methods[method]; | ||
config.methods[method] = wrapFn(method, originalHandler); | ||
}); | ||
return lifeCycleConfigFunction(config); | ||
}; | ||
}; | ||
var hookObjectMethods = function (target, methods, wrapFn) { | ||
methods.forEach(function (method) { | ||
if (target[method]) { | ||
var originalHandler = target[method]; | ||
Object.defineProperty(target, method, { | ||
enumerable: false, | ||
configurable: false, | ||
writable: false, | ||
value: wrapFn(method, originalHandler).bind(target), | ||
}); | ||
} | ||
}); | ||
}; | ||
var unknown = 'unknown'; | ||
@@ -340,3 +530,11 @@ var getHostPlatform = function () { | ||
this.originalPageFn = Page; | ||
this.reportUrl = ''; | ||
this.deviceId = ''; | ||
this.appSessionId = ''; | ||
this.pageSessionId = ''; | ||
this.capturedContext = null; | ||
this.taskStack = []; | ||
this.taskTracks = {}; | ||
this.options = options; | ||
this.pid = options.pid; | ||
this.callback = callback; | ||
@@ -348,5 +546,7 @@ // init later so the subclass can inject sdk | ||
this.transport = this.setupTransport(); | ||
this.collectDeviceId(); | ||
App = this.hookAppFn(App); | ||
Page = this.hookPageFn(Page); | ||
this.collectDeviceId(); | ||
Component = this.hookComponentFn(Component); | ||
this.hookSdk(); | ||
if (this.sdk.onNetworkStatusChange) { | ||
@@ -419,8 +619,8 @@ this.sdk.onNetworkStatusChange(function (options) { | ||
Backend.prototype.setupTransport = function () { | ||
var that = this; | ||
var reportUrl = getBatchReportURL(this.options.reportDomain, this.options.reportPath); | ||
var _this = this; | ||
this.reportUrl = getBatchReportURL(this.options.reportDomain, this.options.reportPath); | ||
return { | ||
sendEvent: function (event) { | ||
that.sdk.request({ | ||
url: reportUrl, | ||
_this.sdk.request({ | ||
url: _this.reportUrl, | ||
method: 'POST', | ||
@@ -431,3 +631,3 @@ data: event, | ||
request: function (options) { | ||
that.sdk.request(options); | ||
_this.sdk.request(options); | ||
}, | ||
@@ -437,16 +637,21 @@ }; | ||
Backend.prototype.hookAppFn = function (App) { | ||
var that = this; | ||
var callback = this.callback; | ||
return hookLifeCycles(App, [ | ||
AppMethod.onLaunch, | ||
AppMethod.onShow, | ||
AppMethod.onHide, | ||
AppMethod.onError, | ||
AppMethod.onPageNotFound, | ||
AppMethod.onUnhandledRejection, | ||
], function (method, originalHandler) { | ||
return function (options) { | ||
return hookLifeCycles(App, [AppMethod.onLaunch, AppMethod.onShow, AppMethod.onHide, AppMethod.onError, AppMethod.onUnhandledRejection], function (method, originalHandler) { | ||
function wrappedAppHandler(options) { | ||
switch (method) { | ||
case AppMethod.onShow: { | ||
var appSessionId = uuid(); | ||
that.appSessionId = appSessionId; | ||
callback({ | ||
ev_type: BackendEventType.APP_SESSION, | ||
data: { | ||
appSessionId: appSessionId, | ||
}, | ||
}); | ||
break; | ||
} | ||
case AppMethod.onError: { | ||
callback({ | ||
ev_type: BackendEventType.JS_ERROR, | ||
ev_type: BackendEventType.GLOBAL_JS_ERROR, | ||
data: makeError(options), | ||
@@ -459,3 +664,3 @@ }); | ||
callback({ | ||
ev_type: BackendEventType.JS_ERROR, | ||
ev_type: BackendEventType.GLOBAL_JS_ERROR, | ||
data: data, | ||
@@ -468,29 +673,271 @@ }); | ||
originalHandler === null || originalHandler === void 0 ? void 0 : originalHandler.call(this, options); | ||
}; | ||
} | ||
return wrappedAppHandler; | ||
}); | ||
}; | ||
Backend.prototype.hookPageFn = function (Page) { | ||
return hookLifeCycles(Page, [PageMethod.onLoad, PageMethod.onShow, PageMethod.onReady], this._pageLifeCycleHandlers()); | ||
}; | ||
Backend.prototype.hookComponentFn = function (Component) { | ||
return hookComponentLifeCycles(Component, [PageMethod.onLoad, PageMethod.onShow, PageMethod.onReady], this._pageLifeCycleHandlers()); | ||
}; | ||
Backend.prototype._pageLifeCycleHandlers = function () { | ||
var that = this; | ||
var callback = this.callback; | ||
return hookLifeCycles(Page, [ | ||
PageMethod.onLoad, | ||
PageMethod.onShow, | ||
PageMethod.onReady, | ||
PageMethod.onHide, | ||
PageMethod.onUnload, | ||
PageMethod.onPullDownRefresh, | ||
], function (method, originalHandler) { | ||
return function (options) { | ||
return function (method, originalHandler) { | ||
function callOrigin(options) { | ||
try { | ||
originalHandler === null || originalHandler === void 0 ? void 0 : originalHandler.call(this, options); | ||
} | ||
catch (err) { | ||
callback({ | ||
ev_type: BackendEventType.JS_ERROR, | ||
data: { | ||
name: err.name, | ||
message: err.message, | ||
stack: err.stack, | ||
rawMessage: String(err), | ||
}, | ||
context: that.getContext(), | ||
}); | ||
} | ||
} | ||
function wrappedPageHandler(options) { | ||
var pid = getPid(); | ||
switch (method) { | ||
case PageMethod.onLoad: { | ||
that.pid = pid; | ||
that.hookSetData(this); | ||
var onLoadStart = Date.now(); | ||
var pageSessionId = [that.deviceId, that.appSessionId, that.pid, onLoadStart].join('|'); | ||
that.pageSessionId = pageSessionId; | ||
that.taskTracks[pageSessionId] = { | ||
tasks: [], | ||
timing: { | ||
firstReqStart: onLoadStart, | ||
firstReqEnd: 0, | ||
firstLoadStart: onLoadStart, | ||
firstLoadEnd: 0, | ||
}, | ||
ready: false, | ||
ended: false, | ||
}; | ||
callback({ | ||
ev_type: BackendEventType.PAGE_SESSION, | ||
data: { | ||
pid: pid, | ||
pageSessionId: pageSessionId, | ||
}, | ||
}); | ||
callOrigin.call(this, options); | ||
this[RANGERS_PAGE_SESSION_ID] = pageSessionId; | ||
this[RANGERS_PAGE_TIMING] = { | ||
onLoadStart: onLoadStart, | ||
onLoadEnd: Date.now(), | ||
onShowStart: 0, | ||
onShowEnd: 0, | ||
onReadyStart: 0, | ||
onReadyEnd: 0, | ||
}; | ||
return; | ||
} | ||
case PageMethod.onShow: { | ||
var pid = getPid(); | ||
var pageSessionId = this[RANGERS_PAGE_SESSION_ID]; | ||
that.pageSessionId = pageSessionId; | ||
callback({ | ||
ev_type: BackendEventType.PATH, | ||
ev_type: BackendEventType.PAGE_SESSION, | ||
data: { | ||
pid: pid, | ||
pageSessionId: pageSessionId, | ||
}, | ||
}); | ||
break; | ||
var timing = this[RANGERS_PAGE_TIMING]; | ||
timing.onShowStart = timing.onShowStart || Date.now(); | ||
callOrigin.call(this, options); | ||
timing.onShowEnd = timing.onShowEnd || Date.now(); | ||
return; | ||
} | ||
case PageMethod.onReady: { | ||
var timing = this[RANGERS_PAGE_TIMING]; | ||
timing.onReadyStart = Date.now(); | ||
callOrigin.call(this, options); | ||
timing.onReadyEnd = Date.now(); | ||
callback({ | ||
ev_type: BackendEventType.PAGE_ON_READY, | ||
data: timing, | ||
}); | ||
var taskTrack = that.getTaskTrack(); | ||
taskTrack.ready = true; | ||
if (!taskTrack.timing.firstReqEnd && taskTrack.tasks.every(function (t) { return t.completed || t.type === 'SET_DATA'; })) { | ||
taskTrack.timing.firstReqEnd = taskTrack.timing.firstReqStart; // no first req | ||
} | ||
if (taskTrack.tasks.every(function (t) { return t.completed; })) { | ||
taskTrack.timing.firstLoadEnd = timing.onReadyEnd; | ||
that.endTrack(taskTrack); | ||
} | ||
return; | ||
} | ||
} | ||
originalHandler === null || originalHandler === void 0 ? void 0 : originalHandler.call(this, options); | ||
callOrigin.call(this, options); | ||
} | ||
return wrappedPageHandler; | ||
}; | ||
}; | ||
Backend.prototype.hookSdk = function () { | ||
this.hookRequest(); | ||
}; | ||
Backend.prototype.hookRequest = function () { | ||
var _this = this; | ||
var that = this; | ||
var callback = this.callback; | ||
hookObjectMethods(this.sdk, [SDKMethod.request], function (_, request) { return function (options) { | ||
var _b; | ||
if (_this.shouldIgnoreUrl(options.url)) { | ||
return request(options); | ||
} | ||
var originalSuccess = options.success; | ||
var originalFail = options.fail; | ||
var originalComplete = options.complete; | ||
var req_size = getDataLength((_b = options.data) !== null && _b !== void 0 ? _b : ''); | ||
var taskId = _this.addTask('REQUEST'); | ||
var req_start_time = Date.now(); | ||
var success = _this.captureContext(function (res) { | ||
var _a, _b; | ||
var req_end_time = Date.now(); | ||
callback({ | ||
ev_type: BackendEventType.REQUEST, | ||
data: { | ||
req_url: options.url, | ||
method: (_a = options.method) !== null && _a !== void 0 ? _a : 'GET', | ||
err_msg: res.statusCode >= 300 ? serialize(res.data) : '', | ||
req_status: 1, | ||
req_start_time: req_start_time, | ||
req_end_time: req_end_time, | ||
resp_status: res.statusCode, | ||
header: (_b = options.header) !== null && _b !== void 0 ? _b : {}, | ||
req_param: res.statusCode >= 300 ? serialize(options.data) : '', | ||
req_size: req_size, | ||
resp_size: getContentLength(res), | ||
perf: res.profile | ||
? pick(res.profile, [ | ||
'redirectStart', | ||
'redirectEnd', | ||
'fetchStart', | ||
'domainLookupStart', | ||
'domainLookupEnd', | ||
'connectStart', | ||
'connectEnd', | ||
'SSLconnectionStart', | ||
'SSLconnectionEnd', | ||
'requestStart', | ||
'requestEnd', | ||
'responseStart', | ||
'responseEnd', | ||
'rtt', | ||
'httpRttEstimate', | ||
'transportRttEstimate', | ||
]) | ||
: {}, | ||
}, | ||
context: that.capturedContext, | ||
}); | ||
that.taskStack.push(taskId); | ||
try { | ||
originalSuccess === null || originalSuccess === void 0 ? void 0 : originalSuccess.call(this, res); | ||
} | ||
finally { | ||
that.taskStack.pop(); | ||
} | ||
}); | ||
var fail = _this.captureContext(function (res) { | ||
var _a, _b, _c; | ||
var req_end_time = Date.now(); | ||
callback({ | ||
ev_type: BackendEventType.REQUEST, | ||
data: { | ||
req_url: options.url, | ||
method: (_a = options.method) !== null && _a !== void 0 ? _a : 'GET', | ||
err_msg: (_b = res.errMsg) !== null && _b !== void 0 ? _b : '', | ||
req_status: 0, | ||
req_start_time: req_start_time, | ||
req_end_time: req_end_time, | ||
resp_status: 0, | ||
req_param: serialize(options.data), | ||
header: (_c = options.header) !== null && _c !== void 0 ? _c : {}, | ||
req_size: req_size, | ||
resp_size: 0, | ||
perf: {}, | ||
}, | ||
context: that.capturedContext, | ||
}); | ||
that.taskStack.push(taskId); | ||
try { | ||
originalFail === null || originalFail === void 0 ? void 0 : originalFail.call(this, res); | ||
} | ||
finally { | ||
that.taskStack.pop(); | ||
} | ||
}); | ||
var complete = _this.captureContext(function (res) { | ||
that.taskStack.push(taskId); | ||
try { | ||
originalComplete === null || originalComplete === void 0 ? void 0 : originalComplete.call(this, res); | ||
} | ||
finally { | ||
that.taskStack.pop(); | ||
} | ||
that.endTask(taskId, Date.now()); | ||
}); | ||
var newOptions = __assign(__assign({}, options), { success: success, | ||
fail: fail, | ||
complete: complete }); | ||
return request(newOptions); | ||
}; }); | ||
}; | ||
Backend.prototype.shouldIgnoreUrl = function (url) { | ||
return url === this.reportUrl; | ||
}; | ||
Backend.prototype.hookSetData = function (page) { | ||
var that = this; | ||
hookObjectMethods(page, ['setData'], function (_, setData) { | ||
return function wrappedSetData(data, callback) { | ||
var _this = this; | ||
var originalCallback = callback; | ||
var taskId = that.addTask('SET_DATA'); | ||
var setDataStart = Date.now(); | ||
var wrappedCallback = that.captureContext(function () { | ||
var setDataEnd = Date.now(); | ||
var dataJson = serialize(data); | ||
that.callback({ | ||
ev_type: BackendEventType.SET_DATA, | ||
data: { | ||
setDataStart: setDataStart, | ||
setDataEnd: setDataEnd, | ||
setDataSize: strSize(dataJson), | ||
data: dataJson, | ||
}, | ||
context: that.capturedContext, | ||
}); | ||
that.taskStack.push(taskId); | ||
try { | ||
originalCallback === null || originalCallback === void 0 ? void 0 : originalCallback.call(_this); | ||
} | ||
catch (err) { | ||
that.callback({ | ||
ev_type: BackendEventType.JS_ERROR, | ||
data: { | ||
name: err.name, | ||
message: err.message, | ||
stack: err.stack, | ||
rawMessage: String(err), | ||
}, | ||
context: that.getContext(), | ||
}); | ||
} | ||
finally { | ||
that.taskStack.pop(); | ||
} | ||
that.endTask(taskId, setDataEnd); | ||
}); | ||
setData.call(this, data, wrappedCallback); | ||
}; | ||
@@ -505,2 +952,3 @@ }); | ||
} | ||
this.deviceId = deviceId; | ||
this.callback({ | ||
@@ -513,2 +961,95 @@ ev_type: BackendEventType.DEVICE_ID, | ||
}; | ||
Backend.prototype.getContext = function () { | ||
var _a; | ||
return (_a = this.capturedContext) !== null && _a !== void 0 ? _a : this.currentContext(); | ||
}; | ||
Backend.prototype.currentContext = function () { | ||
return { | ||
pid: this.pid, | ||
pageSessionId: this.pageSessionId, | ||
context: this.options.context, | ||
}; | ||
}; | ||
Backend.prototype.captureContext = function (fn) { | ||
var that = this; | ||
var context = this.getContext(); | ||
// log('captureContext', context) | ||
return function () { | ||
var args = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
args[_i] = arguments[_i]; | ||
} | ||
// log('call with capturedContext', context) | ||
// log('currentContext', that.currentContext()) | ||
that.capturedContext = context; | ||
var res; | ||
try { | ||
res = fn.apply(this, args); | ||
} | ||
catch (err) { | ||
that.callback({ | ||
ev_type: BackendEventType.JS_ERROR, | ||
data: { | ||
name: err.name, | ||
message: err.message, | ||
stack: err.stack, | ||
rawMessage: String(err), | ||
}, | ||
context: context, | ||
}); | ||
} | ||
finally { | ||
that.capturedContext = null; | ||
} | ||
return res; | ||
}; | ||
}; | ||
Backend.prototype.addTask = function (type) { | ||
var taskTrack = this.getTaskTrack(); | ||
if (!taskTrack || taskTrack.ended) { | ||
return 0; | ||
} | ||
var parentTaskId = this.taskStack.length && this.taskStack[this.taskStack.length - 1]; | ||
if (taskTrack.ready && (!parentTaskId || !taskTrack.tasks.some(function (t) { return t.id === parentTaskId; }))) { | ||
return 0; | ||
} | ||
var id = taskTrack.tasks.length + 1; | ||
taskTrack.tasks.push({ | ||
id: id, | ||
type: type, | ||
completed: false, | ||
}); | ||
return id; | ||
}; | ||
Backend.prototype.endTask = function (id, now) { | ||
var taskTrack = this.getTaskTrack(); | ||
if (id <= 0 || !taskTrack || taskTrack.ended) { | ||
return; | ||
} | ||
var task = taskTrack.tasks[id - 1]; | ||
task.completed = true; | ||
if (!taskTrack.timing.firstReqEnd && task.type === 'REQUEST') { | ||
taskTrack.timing.firstReqEnd = now; | ||
} | ||
if (taskTrack.tasks.every(function (t) { return t.completed; })) { | ||
taskTrack.timing.firstLoadEnd = now; | ||
this.endTrack(taskTrack); | ||
} | ||
}; | ||
Backend.prototype.endTrack = function (taskTrack) { | ||
if (!taskTrack.ready) { | ||
return; | ||
} | ||
taskTrack.ended = true; | ||
this.callback({ | ||
ev_type: BackendEventType.PAGE_FIRST_LOAD, | ||
data: taskTrack.timing, | ||
context: this.getContext(), | ||
}); | ||
}; | ||
Backend.prototype.getTaskTrack = function () { | ||
var context = this.getContext(); | ||
var pageSessionId = context.pageSessionId; | ||
return this.taskTracks[pageSessionId]; | ||
}; | ||
return Backend; | ||
@@ -647,126 +1188,2 @@ }()); | ||
/* eslint-disable @typescript-eslint/ban-types */ | ||
function isObject(what) { | ||
return typeof what === 'object' && what !== null && !isArray(what); | ||
} | ||
function isString(what) { | ||
return Object.prototype.toString.call(what) === '[object String]'; | ||
} | ||
function isArray(what) { | ||
return Object.prototype.toString.call(what) === '[object Array]'; | ||
} | ||
function isUndefined(what) { | ||
return what === undefined; | ||
} | ||
function isNull(what) { | ||
return what === null; | ||
} | ||
var BaseClient = /** @class */ (function () { | ||
function BaseClient() { | ||
} | ||
// 发送事件统一入口 | ||
BaseClient.prototype.sendEvent = function (event) { | ||
var _modifiedOptions = this.getEventToBeSent(event); | ||
if (_modifiedOptions) { | ||
this.idleSendEvent(_modifiedOptions); | ||
} | ||
}; | ||
BaseClient.prototype.getEventToBeSent = function (event) { | ||
var _modifiedOptions = this._modifyEvent(event); | ||
if (!this._shouldSend(_modifiedOptions)) { | ||
return; | ||
} | ||
return _modifiedOptions; | ||
}; | ||
// 修改 event 内容 | ||
BaseClient.prototype._modifyEvent = function (event) { | ||
return event; | ||
}; | ||
// event 是否发送 | ||
BaseClient.prototype._shouldSend = function (_event) { | ||
return true; | ||
}; | ||
// 需要实现的发送方法 | ||
BaseClient.prototype._send = function (_event) { | ||
return; | ||
}; | ||
// 主进程空闲时间发送 | ||
BaseClient.prototype.idleSendEvent = function (event) { | ||
this._send(event); | ||
}; | ||
return BaseClient; | ||
}()); | ||
var MAX_BATCH_REPORT_LENGTH = 10; | ||
var BATCH_REPORT_WAIT = 1000; | ||
// T: monitor 上报的数据类型, 如 ISendParams | ||
// B: 接口上报的每个 item 的数据类型, 如 BatchEventItem | ||
var BatchClient = /** @class */ (function (_super) { | ||
__extends(BatchClient, _super); | ||
function BatchClient(props) { | ||
var _a, _b; | ||
var _this = _super.call(this) || this; | ||
_this.reportQueue = []; | ||
_this.isReady = true; | ||
_this.batchReportLength = (_a = props.maxBatchReportLength) !== null && _a !== void 0 ? _a : MAX_BATCH_REPORT_LENGTH; | ||
_this.batchReportWait = (_b = props.batchReportWait) !== null && _b !== void 0 ? _b : BATCH_REPORT_WAIT; | ||
_this.batchReportTimeout = null; | ||
return _this; | ||
} | ||
Object.defineProperty(BatchClient.prototype, "ready", { | ||
/** | ||
* 上传锁,设置为 false 时所有请求将会被推进列队,等待设置回 true 时一次发出。 | ||
* | ||
* 默认为 true 不上锁,以保证旧代码不受影响。 | ||
* | ||
*/ | ||
get: function () { | ||
return this.isReady; | ||
}, | ||
set: function (v) { | ||
this.isReady = v; | ||
if (this.isReady) { | ||
this._uploadQueue(); | ||
} | ||
}, | ||
enumerable: false, | ||
configurable: true | ||
}); | ||
BatchClient.prototype._send = function (options) { | ||
var _this = this; | ||
var params = this.buildParams(options); | ||
if (isNull(params) || isUndefined(params)) | ||
return; | ||
this.reportQueue.push(params); | ||
if (!this.isReady) | ||
return; | ||
if (this.reportQueue.length >= this.batchReportLength) { | ||
this._uploadQueue(); | ||
} | ||
if (this.batchReportTimeout) { | ||
clearTimeout(this.batchReportTimeout); | ||
} | ||
this.batchReportTimeout = setTimeout(function () { | ||
_this._uploadQueue(); | ||
}, this.batchReportWait); | ||
}; | ||
BatchClient.prototype._uploadQueue = function () { | ||
if (!this.reportQueue.length || !this.ready) { | ||
return; | ||
} | ||
var body = { | ||
ev_type: 'batch', | ||
list: this.reportQueue, | ||
}; | ||
this.reportQueue = []; | ||
this._request({ event: body, type: 'post' }); | ||
}; | ||
// 需要实现的方法 | ||
BatchClient.prototype._request = function (_options) { | ||
return; | ||
}; | ||
return BatchClient; | ||
}(BaseClient)); | ||
var DEFAULT_REPORT_DOMAIN = "" + "tbm.snssdk.com"; | ||
@@ -848,2 +1265,4 @@ var DEFAULT_REPORT_BATCH_PATH = '/monitor_microapp/collect'; | ||
_this.envInfo = {}; | ||
_this.appSessionId = ''; | ||
_this.pageSessionId = ''; | ||
_this.preQueue = []; | ||
@@ -869,3 +1288,3 @@ _this.ready = false; // lock the client | ||
}; | ||
MiniProgramClient.prototype.reportError = function (error) { | ||
MiniProgramClient.prototype.reportError = function (error, context) { | ||
var _a; | ||
@@ -880,11 +1299,11 @@ this.reportEvent({ | ||
breadcrumbs: __spread(this.breadcrumbs), | ||
}); | ||
}, context); | ||
}; | ||
MiniProgramClient.prototype.reportEvent = function (event) { | ||
MiniProgramClient.prototype.reportEvent = function (event, context) { | ||
if (!this.ready) { | ||
// 状态没有 ready 的时候,先存入队列 | ||
this.preQueue.push(event); | ||
this.preQueue.push(__assign(__assign({}, event), { context: context })); | ||
} | ||
else { | ||
_super.prototype.sendEvent.call(this, event); | ||
_super.prototype.sendEvent.call(this, __assign(__assign({}, event), { context: context })); | ||
} | ||
@@ -930,4 +1349,24 @@ }; | ||
} | ||
case BackendEventType.PATH: { | ||
if (this.pid !== event.data.pid) { | ||
case BackendEventType.GLOBAL_JS_ERROR: { | ||
this.reportEvent({ | ||
ev_type: exports.ReportEventType.JS_ERROR, | ||
event: event.data, | ||
breadcrumbs: __spread(this.breadcrumbs), | ||
}); | ||
break; | ||
} | ||
case BackendEventType.JS_ERROR: { | ||
this.reportEvent({ | ||
ev_type: exports.ReportEventType.JS_ERROR, | ||
event: event.data, | ||
breadcrumbs: __spread(this.breadcrumbs), | ||
}, event.context); | ||
break; | ||
} | ||
case BackendEventType.APP_SESSION: { | ||
this.appSessionId = event.data.appSessionId; | ||
break; | ||
} | ||
case BackendEventType.PAGE_SESSION: { | ||
if (this.pageSessionId !== event.data.pageSessionId) { | ||
this.addBreadcrumb(exports.BreadcrumbType.DOM, { | ||
@@ -938,2 +1377,3 @@ category: BreadcrumbCategory.ROUTE, | ||
this.pid = event.data.pid; | ||
this.pageSessionId = event.data.pageSessionId; | ||
this.sendPageView(); | ||
@@ -943,10 +1383,30 @@ } | ||
} | ||
case BackendEventType.JS_ERROR: { | ||
case BackendEventType.REQUEST: { | ||
this.reportEvent({ | ||
ev_type: exports.ReportEventType.JS_ERROR, | ||
ev_type: exports.ReportEventType.REQUEST, | ||
event: event.data, | ||
breadcrumbs: __spread(this.breadcrumbs), | ||
}, event.context); | ||
break; | ||
} | ||
case BackendEventType.SET_DATA: { | ||
this.reportEvent({ | ||
ev_type: exports.ReportEventType.SET_DATA, | ||
event: event.data, | ||
}, event.context); | ||
break; | ||
} | ||
case BackendEventType.PAGE_ON_READY: { | ||
this.reportEvent({ | ||
ev_type: exports.ReportEventType.ON_READY, | ||
event: event.data, | ||
}); | ||
break; | ||
} | ||
case BackendEventType.PAGE_FIRST_LOAD: { | ||
this.reportEvent({ | ||
ev_type: exports.ReportEventType.FIRST_LOAD, | ||
event: event.data, | ||
}, event.context); | ||
break; | ||
} | ||
} | ||
@@ -958,3 +1418,4 @@ }; | ||
MiniProgramClient.prototype.buildParams = function (options) { | ||
return __assign(__assign({}, options), { common: __assign(__assign({}, this.envInfo), { aid: this.config.aid, pid: this.pid, rangers_sdk_version: this.config.version, timestamp: Date.now(), user_unique_id: this.finderConfig ? this.finderConfig.user_unique_id : this.config.userId || this.deviceId, device_id: this.deviceId, session_id: this.config.sessionId, context: this.config.context }) }); | ||
var _a, _b, _c, _d, _e, _f; | ||
return __assign(__assign({}, options), { common: __assign(__assign({}, this.envInfo), { aid: this.config.aid, pid: (_b = (_a = options.context) === null || _a === void 0 ? void 0 : _a.pid) !== null && _b !== void 0 ? _b : this.pid, rangers_sdk_version: this.config.version, timestamp: Date.now(), user_unique_id: this.finderConfig ? this.finderConfig.user_unique_id : this.config.userId || this.deviceId, device_id: this.deviceId, session_id: this.config.sessionId, app_session_id: this.appSessionId, page_session_id: (_d = (_c = options.context) === null || _c === void 0 ? void 0 : _c.pageSessionId) !== null && _d !== void 0 ? _d : this.pageSessionId, context: (_f = (_e = options.context) === null || _e === void 0 ? void 0 : _e.context) !== null && _f !== void 0 ? _f : this.config.context }), context: undefined }); | ||
}; | ||
@@ -988,3 +1449,3 @@ /** | ||
function Rangers(options) { | ||
this.version = '0.0.5'; | ||
this.version = '0.0.6'; | ||
this.Backend = options.Backend; | ||
@@ -991,0 +1452,0 @@ this.defaultConfig = getDefaultConfig(); |
{ | ||
"name": "@apm-insight-web/rangers-mini-sdk", | ||
"version": "0.0.5", | ||
"version": "0.0.6", | ||
"main": "cn/index.cjs.js", | ||
@@ -54,3 +54,3 @@ "module": "cn/index.esm.js", | ||
}, | ||
"gitHead": "9493fb4d321223a4d28371c7ea8478073b47abff" | ||
"gitHead": "377cb6adaa2749efe1831d73b273adfb111b2d45" | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
621944
5224