web-tracing-core 2.1.1 → 2.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/__test__/css/performance.css +3 -0
  2. package/__test__/err-batch.spec.ts +47 -0
  3. package/__test__/err.spec.ts +82 -0
  4. package/__test__/event.spec.ts +62 -0
  5. package/__test__/html/performance.html +57 -0
  6. package/__test__/html/recordscreen.html +39 -0
  7. package/__test__/http.spec.ts +143 -0
  8. package/__test__/img/performance.png +0 -0
  9. package/__test__/js/performance.js +3 -0
  10. package/__test__/performance.spec.ts +115 -0
  11. package/__test__/recordscreen.spec.ts +50 -0
  12. package/__test__/utils/index.ts +132 -0
  13. package/__test__/utils/pollify.ts +14 -0
  14. package/__test__/utils.spec.ts +18 -0
  15. package/{index.cjs → dist/index.cjs} +1 -1
  16. package/{index.iife.js → dist/index.iife.js} +1 -1
  17. package/{index.iife.min.js → dist/index.iife.min.js} +1 -1
  18. package/{index.mjs → dist/index.mjs} +1 -1
  19. package/dist/package.json +49 -0
  20. package/index.ts +76 -0
  21. package/package.json +9 -9
  22. package/src/common/config.ts +13 -0
  23. package/src/common/constant.ts +57 -0
  24. package/src/common/index.ts +2 -0
  25. package/src/lib/base.ts +129 -0
  26. package/src/lib/err-batch.ts +134 -0
  27. package/src/lib/err.ts +323 -0
  28. package/src/lib/event-dwell.ts +63 -0
  29. package/src/lib/event.ts +252 -0
  30. package/src/lib/eventBus.ts +97 -0
  31. package/src/lib/exportMethods.ts +208 -0
  32. package/src/lib/http.ts +197 -0
  33. package/src/lib/intersectionObserver.ts +169 -0
  34. package/src/lib/line-status.ts +45 -0
  35. package/src/lib/options.ts +325 -0
  36. package/src/lib/performance.ts +302 -0
  37. package/src/lib/pv.ts +199 -0
  38. package/src/lib/recordscreen.ts +169 -0
  39. package/src/lib/replace.ts +371 -0
  40. package/src/lib/sendData.ts +264 -0
  41. package/src/observer/computed.ts +52 -0
  42. package/src/observer/config.ts +1 -0
  43. package/src/observer/dep.ts +21 -0
  44. package/src/observer/index.ts +91 -0
  45. package/src/observer/ref.ts +80 -0
  46. package/src/observer/types.ts +22 -0
  47. package/src/observer/watch.ts +19 -0
  48. package/src/observer/watcher.ts +88 -0
  49. package/src/types/index.ts +126 -0
  50. package/src/utils/debug.ts +17 -0
  51. package/src/utils/element.ts +47 -0
  52. package/src/utils/fingerprintjs.ts +2132 -0
  53. package/src/utils/getIps.ts +127 -0
  54. package/src/utils/global.ts +50 -0
  55. package/src/utils/index.ts +552 -0
  56. package/src/utils/is.ts +78 -0
  57. package/src/utils/localStorage.ts +70 -0
  58. package/src/utils/polyfill.ts +11 -0
  59. package/src/utils/session.ts +29 -0
  60. /package/{LICENSE → dist/LICENSE} +0 -0
  61. /package/{README.md → dist/README.md} +0 -0
  62. /package/{index.d.ts → dist/index.d.ts} +0 -0
@@ -0,0 +1,91 @@
1
+ import { ref as _ref } from './ref'
2
+ import { computed as _computed } from './computed'
3
+ import { watch as _watch } from './watch'
4
+ import { ObserverValue, AnyFun, voidFun } from './types'
5
+
6
+ /**
7
+ * 响应式
8
+ * 说明:与vue用法相似,但不提供多样的写法,只完成了基础用法,observer文件并不引用其他文件,为了方便移植
9
+ * 完成功能:ref computed watch
10
+ * 兼容性:需要支持proxy,如不支持则响应式无效
11
+ *
12
+ * 不支持proxy时各个函数表现:
13
+ * ref:返回 { value: target } 对象
14
+ * computed:返回 { value: fun() } 对象
15
+ * watch:返回空函数
16
+ */
17
+
18
+ function hasProxy(): boolean {
19
+ return !!window.Proxy
20
+ }
21
+
22
+ function ref<T>(target: T) {
23
+ return hasProxy() ? _ref<T>(target) : { value: target }
24
+ }
25
+
26
+ function computed<T>(fun: AnyFun) {
27
+ return hasProxy() ? _computed<T>(fun) : { value: fun() }
28
+ }
29
+
30
+ function watch<T>(target: ObserverValue<T>, fun: voidFun<T>) {
31
+ return hasProxy() ? _watch<T>(target, fun) : () => ({})
32
+ }
33
+
34
+ export { ref, computed, watch }
35
+
36
+ // ---------------- demo 1 ----------------
37
+ // const data = {
38
+ // name: 'aaa',
39
+ // age: 1,
40
+ // cheng: {
41
+ // a: 1,
42
+ // b: 1,
43
+ // c: 1
44
+ // }
45
+ // }
46
+ // const a = ref(data)
47
+ // const b = ref({
48
+ // name: 'bbb',
49
+ // age: 2
50
+ // })
51
+ // const c = ref({
52
+ // name: 'ccc',
53
+ // age: 3
54
+ // })
55
+ // const d = computed<number>(() => a.value.age + b.value.age + c.value.age)
56
+
57
+ // watch(d, val => {
58
+ // console.log('val', val)
59
+ // })
60
+
61
+ // setTimeout(() => {
62
+ // a.value.age = 11
63
+ // console.log('d', d.value)
64
+ // }, 1000)
65
+
66
+ // ---------------- demo 2 ----------------
67
+ // const a = ref(1)
68
+ // const b = ref(2)
69
+ // const c = ref(3)
70
+
71
+ // const d = computed<number>(() => a.value + b.value) // 3
72
+ // const e = computed<number>(() => d.value + c.value) // 6
73
+ // const f = computed<number>(() => e.value + d.value) // 9
74
+
75
+ // c.value = 6
76
+
77
+ // setTimeout(() => {
78
+ // console.log('f', f.value) // 12
79
+ // }, 1000)
80
+
81
+ // ---------------- demo 3 ----------------
82
+ // const a = ref(1)
83
+ // const b = ref(2)
84
+ // const c = 3
85
+
86
+ // const d = computed<number>(() => a.value + b.value) // 3
87
+ // const e = computed<number>(() => d.value + c) // 6
88
+
89
+ // setTimeout(() => {
90
+ // console.log('e', e.value) // 6
91
+ // }, 1000)
@@ -0,0 +1,80 @@
1
+ import { Dep } from './dep'
2
+ import { ObserverValue, AnyFun } from './types'
3
+ import { OBSERVERSIGNBOARD } from './config'
4
+
5
+ function isRegExp(value: any) {
6
+ return Object.prototype.toString.call(value) === `[object RegExp]`
7
+ }
8
+
9
+ class Observer<T> {
10
+ target: ObserverValue<T>
11
+ constructor(target: ObserverValue<T>) {
12
+ this.target = target
13
+ }
14
+ defineReactive() {
15
+ const dep = new Dep()
16
+ const handlers = getHandlers(
17
+ () => {
18
+ dep.addSub()
19
+ },
20
+ (oldValue: any) => {
21
+ dep.notify(oldValue)
22
+ }
23
+ )
24
+ return new Proxy<ObserverValue<T>>(this.target, handlers)
25
+ }
26
+ }
27
+
28
+ function getHandlers(
29
+ getCallBack?: AnyFun,
30
+ setCallBack?: AnyFun
31
+ ): ProxyHandler<ObserverValue<any>> {
32
+ const proxyCache = new WeakMap<ObserverValue<any>, any>()
33
+ const handlers: ProxyHandler<ObserverValue<any>> = {
34
+ get(target, key: string, receiver) {
35
+ // console.log(`读取属性:${key}`)
36
+ const value = Reflect.get(target, key, receiver)
37
+ getCallBack && getCallBack()
38
+ if (typeof value === 'object' && value !== null && !isRegExp(value)) {
39
+ let proxy = proxyCache.get(value)
40
+ if (!proxy) {
41
+ proxy = new Proxy(value, handlers)
42
+ proxyCache.set(value, proxy)
43
+ }
44
+ return proxy
45
+ }
46
+ return value
47
+ },
48
+ set(target, key: string, value, receiver) {
49
+ const oldValue = Reflect.get(target, key, receiver)
50
+ if (oldValue === value) return oldValue
51
+ // console.log(`设置属性:${key}=${value}, oldValue:${oldValue}`)
52
+ const beforeTarget = JSON.parse(JSON.stringify(target))
53
+ const result = Reflect.set(target, key, value, receiver)
54
+ setCallBack && setCallBack(beforeTarget)
55
+ return result
56
+ }
57
+ }
58
+ return handlers
59
+ }
60
+
61
+ export const refMap = new WeakMap<any, ObserverValue<any>>()
62
+
63
+ export function ref<T>(target: T) {
64
+ const newObj: any = { value: target }
65
+ newObj[OBSERVERSIGNBOARD] = true
66
+
67
+ const ob = new Observer<T>(newObj)
68
+ const proxy = ob.defineReactive()
69
+
70
+ refMap.set(ob, proxy)
71
+ return proxy
72
+ }
73
+
74
+ export function isRef(ref: any) {
75
+ return !!ref[OBSERVERSIGNBOARD]
76
+ }
77
+
78
+ export function unRef(ref: any) {
79
+ return isRef(ref) ? ref.value : ref
80
+ }
@@ -0,0 +1,22 @@
1
+ export interface ObserverValue<T> {
2
+ value: T
3
+ }
4
+
5
+ export type AnyFun = {
6
+ (...args: any[]): any
7
+ }
8
+
9
+ export type voidFun<T> = {
10
+ (...args: T[]): void
11
+ }
12
+
13
+ export type Options = {
14
+ computed?: boolean
15
+ watch?: boolean
16
+ callback?: AnyFun
17
+ }
18
+
19
+ export type Proxy = {
20
+ value: any
21
+ dirty: boolean
22
+ }
@@ -0,0 +1,19 @@
1
+ import { Watcher } from './watcher'
2
+ import { isRef } from './ref'
3
+ import { ObserverValue, AnyFun, voidFun } from './types'
4
+
5
+ function watchInit(callback: AnyFun, getter: AnyFun) {
6
+ new Watcher('', { watch: true, callback }, getter)
7
+ }
8
+
9
+ export function watch<T>(target: ObserverValue<T>, fun: voidFun<T>) {
10
+ if (!isRef(target)) return
11
+ watchInit(
12
+ (newValue: T, oldValue: T) => {
13
+ fun(newValue, oldValue)
14
+ },
15
+ function () {
16
+ return target.value
17
+ }
18
+ )
19
+ }
@@ -0,0 +1,88 @@
1
+ import { Dep } from './dep'
2
+ import { computedMap } from './computed'
3
+ import { AnyFun, Options, Proxy } from './types'
4
+
5
+ const targetStack: Watcher[] = []
6
+ function pushTarget(_target: Watcher) {
7
+ if (Dep.target) targetStack.push(Dep.target)
8
+ Dep.target = _target
9
+ }
10
+ function popTarget() {
11
+ Dep.target = targetStack.pop()
12
+ }
13
+
14
+ export class Watcher {
15
+ vm: any
16
+ computed: boolean
17
+ watch: boolean
18
+ proxy: Proxy
19
+ dep: Dep | undefined
20
+ getter: AnyFun | undefined
21
+ callback: AnyFun | undefined
22
+ constructor(vm: any, options: Options, getter?: AnyFun) {
23
+ const { computed, watch, callback } = options
24
+ this.getter = getter // 获取值函数
25
+ this.computed = computed || false // 是否为计算属性
26
+ this.watch = watch || false // 是否为监听属性
27
+ this.callback = callback // 回调函数,专门给watch用的
28
+ this.proxy = {
29
+ value: '', // 存储这个属性的值,在不需要更新的时候会直接取这个值
30
+ dirty: true // 表示这个属性是否脏了(脏了代表需要重新运算更新这个值)
31
+ }
32
+ this.vm = vm
33
+
34
+ if (computed) {
35
+ this.dep = new Dep()
36
+ } else if (watch) {
37
+ this.watchGet()
38
+ } else {
39
+ this.get()
40
+ }
41
+ }
42
+ update(oldValue: any) {
43
+ if (this.computed) {
44
+ // 更新计算属性(不涉及渲染)
45
+ this.dep!.notify()
46
+ } else if (this.watch) {
47
+ // 触发watch
48
+ // this.watchGet()
49
+ if (oldValue !== this.proxy.value) {
50
+ this.callback && this.callback(this.proxy.value, oldValue)
51
+ }
52
+ } else {
53
+ // 更新data, 触发依赖其的属性更新
54
+ this.get()
55
+ }
56
+ }
57
+ get() {
58
+ // 存入当前上下文到依赖(表示当前是哪个属性在依赖其他属性,这样在其他属性发生变化时就知道应该通知谁了)
59
+ pushTarget(this)
60
+
61
+ // 目前只有计算属性才会调用 get 方法
62
+ const value = this.computed ? computedMap.get(this.vm)!.call(this.vm) : ''
63
+ if (value !== this.proxy.value) {
64
+ this.proxy.dirty = false // 标记为不是脏的数据
65
+ this.proxy.value = value // 缓存数据,在数据不脏的时候直接拿这个缓存值
66
+ }
67
+ popTarget() // 取出依赖
68
+ return value
69
+ }
70
+ /**
71
+ * 监听属性专用 - 拿到最新值并添加依赖
72
+ */
73
+ watchGet() {
74
+ pushTarget(this) // 将当前上下文放入 Dep.target
75
+ this.proxy.dirty = false // 设定不为脏数据
76
+ if (this.getter) {
77
+ this.proxy.value = this.getter() // 设定值(在这个过程中就给上了依赖)
78
+ }
79
+ popTarget() // 取出上面放入 Dep.target 的上下文
80
+ }
81
+ /**
82
+ * 计算属性专用 - 添加依赖
83
+ * 其他值用到了这个计算属性就会被记录添加到依赖中
84
+ */
85
+ depend() {
86
+ this.dep!.addSub()
87
+ }
88
+ }
@@ -0,0 +1,126 @@
1
+ import type { EventBus } from '../lib/eventBus'
2
+ import type { BaseInfo } from '../lib/base'
3
+ import type { ObserverValue } from '../observer/types'
4
+
5
+ export type WebTracing = {
6
+ eventBus: EventBus
7
+ baseInfo: BaseInfo
8
+ sendData: any
9
+ lineStatus: any
10
+ options: ObserverValue<InternalOptions> // 配置信息
11
+ firstScreen: any // 首屏信息
12
+ intersection: any // 曝光采集
13
+ }
14
+
15
+ interface Pv {
16
+ core?: boolean // 是否发送页面跳转相关数据
17
+ }
18
+ interface Performance {
19
+ core?: boolean // 是否采集静态资源、接口的相关数据
20
+ firstResource?: boolean // 是否采集首次进入页面的数据
21
+ server?: boolean // 是否采集接口请求
22
+ }
23
+ interface Error {
24
+ core?: boolean // 是否采集异常数据
25
+ server?: boolean // 是否采集报错接口数据
26
+ }
27
+ interface Event {
28
+ core?: boolean // 是否采集点击事件
29
+ }
30
+
31
+ /**
32
+ * sdk内部配置
33
+ */
34
+ export type InternalOptions = {
35
+ dsn: string // 上报地址
36
+ appName: string // 应用名称
37
+ appCode: string // 应用code
38
+ appVersion: string // 应用版本
39
+ userUuid: string // 用户id(外部填充进来的id)
40
+ sdkUserUuid: string // 用户id(sdk内部生成的id)
41
+ debug: boolean // 是否开启调试模式(控制台会输出sdk动作)
42
+ pv: Pv
43
+ performance: Performance
44
+ error: Error
45
+ event: Event
46
+ ext: AnyObj // 自定义全局附加参数(放在baseInfo中)
47
+ tracesSampleRate: number // 抽样发送
48
+ cacheMaxLength: number // 上报数据最大缓存数
49
+ cacheWatingTime: number // 上报数据最大等待时间
50
+ ignoreErrors: Array<string | RegExp> // 错误类型事件过滤
51
+ ignoreRequest: Array<string | RegExp> // 请求类型事件过滤
52
+ scopeError: boolean // 当某个时间段报错时,会将此类错误转为特殊错误类型,会新增错误持续时间范围
53
+ localization: boolean // 是否本地化:sdk不再主动发送事件,事件都存储在本地,由用户手动调用方法发送
54
+ sendTypeByXmlBody?: boolean // 是否强制指定发送形式为xml,body请求方式
55
+ // whiteScreen: boolean // 开启白屏检测
56
+ beforePushEventList: AnyFun[] // 添加到行为列表前的 hook (在这里面可以给出错误类型,然后就能达到用户想拿到是何种事件类型的触发)
57
+ beforeSendData: AnyFun[] // 数据上报前的 hook
58
+ afterSendData: AnyFun[] // 数据上报后的 hook
59
+ localizationOverFlow: VoidFun // 本地化存储溢出后的回调
60
+ recordScreen: boolean // 是否启动录屏
61
+ timeout?: number // 日志上报超时时间(毫秒)
62
+ maxQueueLength?: number // 上报接口异常,日志队列最大缓存数
63
+ checkRecoverInterval?: number // 多长时间检测一次上报接口是否恢复(分钟)
64
+ }
65
+
66
+ /**
67
+ * sdk初始化入参配置
68
+ */
69
+ export type InitOptions = {
70
+ dsn: string // 上报地址
71
+ appName: string // 应用名称
72
+ appCode?: string // 应用code
73
+ appVersion?: string // 应用版本
74
+ userUuid?: string // 用户id(外部填充进来的id)
75
+ debug?: boolean // 是否开启调试模式(控制台会输出sdk动作)
76
+ pv?: Pv | boolean
77
+ performance?: Performance | boolean
78
+ error?: Error | boolean
79
+ event?: Event | boolean
80
+ ext?: { [key: string]: any } // 自定义全局附加参数(放在baseInfo中)
81
+ tracesSampleRate?: number // 抽样发送
82
+ cacheMaxLength?: number // 上报数据最大缓存数
83
+ cacheWatingTime?: number // 上报数据最大等待时间
84
+ ignoreErrors?: Array<string | RegExp> // 错误类型事件过滤
85
+ ignoreRequest?: Array<string | RegExp> // 请求类型事件过滤
86
+ scopeError?: boolean // 当某个时间段报错时,会将此类错误转为特殊错误类型,会新增错误持续时间范围
87
+ localization?: boolean // 是否本地化:sdk不再主动发送事件,事件都存储在本地,由用户手动调用方法发送
88
+ sendTypeByXmlBody?: boolean // 是否强制指定发送形式为xml,body请求方式
89
+ // whiteScreen?: boolean // 开启白屏检测
90
+ beforePushEventList?: (data: any) => any // 添加到行为列表前的 hook (在这里面可以给出错误类型,然后就能达到用户想拿到是何种事件类型的触发)
91
+ beforeSendData?: (data: any) => any // 数据上报前的 hook
92
+ afterSendData?: (data: any) => void // 数据上报后的 hook
93
+ recordScreen?: boolean // 是否启动录屏
94
+ timeout?: number // 日志上报超时时间(毫秒)
95
+ maxQueueLength?: number // 上报接口异常,日志队列最大缓存数
96
+ checkRecoverInterval?: number // 多长时间检测一次上报接口是否恢复(分钟)
97
+ }
98
+
99
+ export type ElementOrList = Element | Element[]
100
+ export interface TargetGather {
101
+ target: ElementOrList
102
+ threshold: number
103
+ params?: AnyObj
104
+ }
105
+
106
+ export interface RecordEventScope {
107
+ scope: string
108
+ eventList: any[]
109
+ }
110
+
111
+ export type VoidFun = {
112
+ (...args: any[]): void
113
+ }
114
+
115
+ export type AnyFun = {
116
+ (...args: any[]): any
117
+ }
118
+
119
+ export type AnyObj<T = any> = {
120
+ [key: string]: T
121
+ }
122
+
123
+ export interface SendData {
124
+ baseInfo: object
125
+ eventInfo: unknown[]
126
+ }
@@ -0,0 +1,17 @@
1
+ import { options } from '../lib/options'
2
+
3
+ /**
4
+ * 控制台输出信息
5
+ * @param args 输出信息
6
+ */
7
+ export function debug(...args: any[]): void {
8
+ if (options.value.debug) console.log('@web-tracing: ', ...args)
9
+ }
10
+
11
+ /**
12
+ * 控制台输出错误信息
13
+ * @param args 错误信息
14
+ */
15
+ export function logError(...args: any[]): void {
16
+ console.error('@web-tracing: ', ...args)
17
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * 判断元素是否含有目标属性
3
+ */
4
+ export function getElByAttr(list: Element[], key: string): Element | undefined {
5
+ return list.find(item => item.hasAttribute && item.hasAttribute(key))
6
+ }
7
+
8
+ /**
9
+ * 是否为简单的标签
10
+ * 简单标签数组:['em', 'b', 'strong', 'span', 'img', 'i', 'code']
11
+ */
12
+ export function isSimpleEl(children: Element[]): boolean {
13
+ if (children.length > 0) {
14
+ const arr = ['em', 'b', 'strong', 'span', 'img', 'i', 'code']
15
+ const a = children.filter(
16
+ ({ tagName }) => arr.indexOf(tagName.toLowerCase()) >= 0
17
+ )
18
+ return a.length === children.length
19
+ }
20
+ return true
21
+ }
22
+
23
+ /**
24
+ * 获取元素的关系字符串(从子级一直递归到最外层)
25
+ * 例如两层div的关系会得到字符串: div>div
26
+ */
27
+ export function getNodeXPath(node: Element, curPath = ''): string {
28
+ if (!node) return curPath
29
+ const parent = node.parentElement
30
+ const { id } = node
31
+ const tagName = node.tagName.toLowerCase()
32
+ const path = curPath ? `>${curPath}` : ''
33
+
34
+ if (
35
+ !parent ||
36
+ parent === document.documentElement ||
37
+ parent === document.body
38
+ ) {
39
+ return `${tagName}${path}`
40
+ }
41
+
42
+ if (id) {
43
+ return `#${id}${path}` // 知道了id 就不需要获取上下级关系了(id是唯一的)
44
+ }
45
+
46
+ return getNodeXPath(parent, `${tagName}${path}`)
47
+ }