@tengence/track-sdk-web 1.0.0
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.
- package/README.md +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.mjs +1 -0
- package/dist/interface/base.d.ts +9 -0
- package/dist/interface/index.d.ts +39 -0
- package/dist/plugin/behavior.d.ts +3 -0
- package/dist/plugin/expose.d.ts +3 -0
- package/dist/plugin/index.d.ts +2 -0
- package/dist/utils/helper.d.ts +36 -0
- package/dist/utils/proxy.d.ts +3 -0
- package/dist/utils/request.d.ts +24 -0
- package/package.json +39 -0
package/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
**@tengence/track-sdk-web**
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { BaseOptions, ClientReportData, ReportFunction } from "./interface";
|
|
2
|
+
declare global {
|
|
3
|
+
interface Window {
|
|
4
|
+
trackMonitor: {
|
|
5
|
+
report: ReportFunction;
|
|
6
|
+
realtimeReport: ReportFunction;
|
|
7
|
+
bindUser: (id: string) => void;
|
|
8
|
+
unBindUser: () => void;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
declare const init: (options: BaseOptions) => {
|
|
13
|
+
bindUser: (id: string) => void;
|
|
14
|
+
unBindUser: () => void;
|
|
15
|
+
report: (data: ClientReportData) => void;
|
|
16
|
+
realtimeReport: (data: ClientReportData) => void;
|
|
17
|
+
};
|
|
18
|
+
export default init;
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=!!navigator?.sendBeacon,t=window.requestIdleCallback||window.requestAnimationFrame||(e=>setTimeout(e,17)),n=e=>{"complete"===document.readyState?setTimeout(e):window.addEventListener("pageshow",e,{once:!0,capture:!0})};function r(e,t=150,n=!1){let r=null;return function(...o){const a=this,i=n&&!r;r&&clearTimeout(r),r=setTimeout((()=>{r=null,n||e.apply(a,o)}),t),i&&e.apply(a,o)}}const o=(e,t=300)=>{let n=0;return function(...r){const o=Date.now();o-n>=t&&(n=o,e.apply(this,r))}};function a(e,t=""){if(!e)return t;const n=e.parentElement,{id:r}=e,o=e.tagName.toLowerCase(),i=t?`>${t}`:"";return n&&n!==document.documentElement&&n!==document.body?r?`#${r}${i}`:a(n,`${o}${i}`):`${o}${i}`}const i=()=>{if(window.performance?.navigation)return performance.navigation.type;if(window.performance?.getEntriesByType){const e=performance.getEntriesByType("navigation");if(e.length>0)return e[0].type}return 255},s=e=>{const t=history[e];return function(){const n=t.apply(this,arguments),r=new Event(e);return window.dispatchEvent(r),n}},c=e=>{window.addEventListener("replaceState",(t=>e(t)),!0),window.addEventListener("pushState",(t=>e(t)),!0)},l=e=>{window.addEventListener("popstate",(t=>e(t)),!0)},d=e=>{const t=o((t=>{const n=function(e){if(!e)return;if(["A","INPUT","TEXTAREA","BUTTON"].includes(e.tagName))return console.log("预设的可跟踪元素 ",e),e;if(e.hasAttribute("track-click"))return console.log("带有 track-click 属性的可跟踪元素 ",e),e;const t=e.closest("a, button, [track-click]");return t?(console.log("找到的可跟踪元素 ",t),t):void 0}(t.target);if(!n)return;const r={element_id:n.id,element_content:n.textContent?.trim()||null,element_class_name:n.className||null,element_name:n.getAttribute("name")||null,element_type:n.tagName.toLowerCase(),element_path:a(n),scroll_width:document.documentElement.scrollWidth,scroll_height:document.documentElement.scrollHeight,pageX:t.pageX,pageY:t.pageY,ratio:window.devicePixelRatio||1};e({data_type:"common",event_type:"click",event_detail:r})}));window.addEventListener("click",t,!0)},u=e=>{history.pushState=s("pushState"),history.replaceState=s("replaceState"),(e=>{const t=r((()=>{const{host:t,hostname:n,href:r,protocol:o,origin:a,port:s,pathname:c,search:l,hash:d}=window.location,u={host:t,hostname:n,href:r,protocol:o,origin:a,port:s,pathname:c,search:l,hash:d,title:document.title,language:navigator.language.substring(0,2),user_agent:navigator.userAgent,win_screen:`${window.screen.width}x${window.screen.height}`,doc_screen:`${document.documentElement.clientWidth}x${document.documentElement.clientHeight}`,referrer:document.referrer,type:i()};e({data_type:"common",event_type:"view",event_detail:u})}));n((()=>{t()})),l((()=>{t()})),c((()=>{t()}))})(e),(e=>{let t="",o="",a=0;const i=()=>{if(o&&a){const n=Date.now()-a;n>0&&e({data_type:"common",event_type:"leave",event_detail:{url:o,referrer:t,duration:n}})}a=Date.now(),t=o,o=window.location.href},s=r(i);n((()=>{s()})),l((()=>{s()})),c((()=>{s()})),document.addEventListener("visibilitychange",(()=>{"hidden"===document.visibilityState?i():a=Date.now()})),window.addEventListener("pagehide",(()=>{i()}),!0),window.addEventListener("pageshow",(()=>{a=Date.now()}),!0),window.addEventListener("beforeunload",(()=>{i()}),!0)})(e),d(e)};let m=null,h=null;const p="track-expose",w="track-view",g="TRACK_MONITOR_DATA",f="TRACK_MONITOR_USER";let v=null,y=null;const _=(e,t,n)=>({session_id:t.session_id,request_id:t.request_id,spm:t.spm,data_type:t.data_type||"behavior",event_type:t.event_type,event_detail:"string"==typeof t.event_detail?t.event_detail:JSON.stringify(t.event_detail),page_url:location.href,os:window.navigator.platform,user_id:localStorage.getItem(f)||void 0,user_agent:window.navigator.userAgent,screen_width:window.screen.width,screen_height:window.screen.height,lib_version:"1.0.0",lib_type:"js",event_time:(new Date).getTime().toString(),timezone_offset:(new Date).getTimezoneOffset().toString(),site_id:n,channel_id:e}),E=n=>{const{url:r="http://api.mossego.cn/push/track-data",accessKey:o,cacheNumber:a=50,reportInterval:i=3e4,maxCacheSize:s=56,maxRetries:c=10,log:l}=n,d=r.includes("?")?`${r}&ak=${o}`:`${r}?ak=${o}`;let E=0;const b=e=>{try{localStorage.setItem(f,e)}catch(e){console.error("monitor bind user fail: ",e)}},S=()=>{localStorage.removeItem(f)},T=()=>{let e=[];try{e=JSON.parse(localStorage.getItem(g)||"[]")}catch(e){console.error("get monitor data fail: ",e)}return e},A=e=>{try{localStorage.setItem(g,JSON.stringify(e))}catch(t){console.error("save monitor data error: ",e),localStorage.setItem(g,"[]")}},$=t=>{if(l&&console.log("report data ",t),e){const e=((e,t)=>navigator.sendBeacon(e,t))(d,new Blob([JSON.stringify(t)],{type:"application/json"}));if(e)return Promise.resolve()}return(({method:e,url:t,data:n,headers:r={},withCredentials:o=!1})=>new Promise(((a,i)=>{const s=new XMLHttpRequest;s.open(e,t,!0),Object.keys(r).forEach((e=>{s.setRequestHeader(e,r[e])})),s.withCredentials=!!o,s.onreadystatechange=()=>{if(4===s.readyState)if(s.status>=200&&s.status<300)try{const e=JSON.parse(s.responseText);a(e)}catch(e){i(e)}else i(new Error(`Request failed with status ${s.status}`))},s.onerror=()=>{i(new Error("Network error"))},s.ontimeout=()=>{i(new Error("Request timeout"))},n?s.send(n):s.send()})))({url:d,method:"POST",headers:{"Content-Type":"application/json"},data:JSON.stringify(t),withCredentials:!0})},L=(e=!1)=>{const t=T();if(t.length){if(!E&&(t.length>=a||e))return v&&(clearTimeout(v),v=null),A([]),void $(t).then((()=>{E=0})).catch((()=>{console.error("send report fail"),N(t)})).finally((()=>{L()}));if(null===v){v=setTimeout((()=>{const e=T();e.length?y||(A([]),y=$(e).then((()=>{E=0})).catch((()=>{N(e)})).finally((()=>{v=null,y=null,L()}))):v=null}),(E+1)*i)}}},N=e=>{if(E++,E>c)return void(E=0);const t=T();A(t.concat(e))},k=e=>{$([_(n.channelId,e,n.siteId)])},I=e=>{let t=[_(n.channelId,e,n.siteId)];try{const e=T();e&&(t=t.concat(e),A(t));const n=(e=>{const t=JSON.stringify(e);return(new TextEncoder).encode(t).length/1024})(t);L(n>=s)}catch(e){console.error("lazy report fail: ",e)}};return t((()=>{L(!0)})),(e=>{try{h=new WeakMap}catch(e){console.error("create WeakMap fail: ",e)}const t=e=>{if(h?.get(e))return;const t=e.dataset.duration||500;h?.set(e,{duration:t,timer:null}),m?.observe(e)},n=e=>{const t=h?.get(e);t&&(m?.unobserve(e),t?.timer&&clearTimeout(t.timer),h?.delete(e))},r=t=>{t.forEach((t=>{const r=t.target,o=h?.get(r);o&&(o.timer&&(clearTimeout(o.timer),o.timer=null),t.isIntersecting&&(o.timer=window.setTimeout((()=>{const t=r.dataset.eventDetail;r.hasAttribute(w)&&(e({spm:r.dataset.spm,data_type:"behavior",event_type:"view",event_detail:t}),r.removeAttribute(w)),r.hasAttribute(p)?e({spm:r.dataset.spm,data_type:"behavior",event_type:"expose",event_detail:t}):n(r)}),o.duration)))}))};m=(()=>{let e=null;try{e=new IntersectionObserver(r,{root:null,threshold:.5})}catch(e){console.error("create IntersectionObserver fail: ",e)}return e})(),document.querySelectorAll(`[${p}]`).forEach((e=>t(e))),document.querySelectorAll(`[${w}]`).forEach((e=>t(e))),new MutationObserver((e=>{e.forEach((e=>{if("attributes"===e.type&&e.target instanceof HTMLElement){const r=e.target;r.hasAttribute(p)||r.hasAttribute(w)?t(r):n(r)}else"childList"===e.type&&(e.addedNodes.forEach((e=>{e instanceof HTMLElement&&(e.hasAttribute(p)||e.hasAttribute(w)?t(e):(e.querySelectorAll(`[${p}]`).forEach((e=>t(e))),e.querySelectorAll(`[${w}]`).forEach((e=>t(e)))))})),e.removedNodes.forEach((e=>{if(e instanceof HTMLElement){const t=h?.get(e);t?n(e):(e.querySelectorAll(`[${p}]`).forEach((e=>n(e))),e.querySelectorAll(`[${w}]`).forEach((e=>n(e))))}})))}))})).observe(document.body,{attributes:!0,childList:!0,subtree:!0})})(I),!1!==n.behavior&&u(I),window.addEventListener("beforeunload",(()=>{L(!0)})),document.addEventListener("visibilitychange",(()=>{"hidden"===document.visibilityState&&L(!0)})),window.addEventListener("pagehide",(()=>{L(!0)})),window.trackMonitor={report:I,realtimeReport:k,bindUser:b,unBindUser:S},{bindUser:b,unBindUser:S,report:I,realtimeReport:k}};export{E as default};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export type DataType = "common" | "behavior" | "performance" | "error" | "custom";
|
|
2
|
+
export type EventType = "click" | "expose" | "view" | "cart" | "buy" | "pay" | "leave";
|
|
3
|
+
export interface BaseOptions {
|
|
4
|
+
url?: string;
|
|
5
|
+
accessKey: string;
|
|
6
|
+
cacheNumber?: number;
|
|
7
|
+
reportInterval?: number;
|
|
8
|
+
maxCacheSize?: number;
|
|
9
|
+
maxRetries?: number;
|
|
10
|
+
siteId?: string;
|
|
11
|
+
channelId: string;
|
|
12
|
+
log?: boolean;
|
|
13
|
+
behavior?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface SdkReportData {
|
|
16
|
+
page_url: string;
|
|
17
|
+
os: string;
|
|
18
|
+
user_id?: string;
|
|
19
|
+
user_agent: string;
|
|
20
|
+
screen_width: number;
|
|
21
|
+
screen_height: number;
|
|
22
|
+
lib_version: string;
|
|
23
|
+
lib_type: string;
|
|
24
|
+
event_time: string;
|
|
25
|
+
timezone_offset: string;
|
|
26
|
+
}
|
|
27
|
+
export interface ClientReportData {
|
|
28
|
+
session_id?: string;
|
|
29
|
+
request_id?: string;
|
|
30
|
+
spm?: string;
|
|
31
|
+
data_type?: DataType | string;
|
|
32
|
+
event_type: EventType | string;
|
|
33
|
+
event_detail: any;
|
|
34
|
+
site_id?: string;
|
|
35
|
+
channel_id?: string;
|
|
36
|
+
}
|
|
37
|
+
export interface ReportData extends SdkReportData, ClientReportData {
|
|
38
|
+
}
|
|
39
|
+
export type ReportFunction = (data: ClientReportData) => void;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export declare const nextTick: ((callback: IdleRequestCallback, options?: IdleRequestOptions | undefined) => number) & typeof requestIdleCallback;
|
|
2
|
+
/**
|
|
3
|
+
* 在页面完全加载后执行回调函数
|
|
4
|
+
*
|
|
5
|
+
* 该函数会检查文档的 readyState 状态:
|
|
6
|
+
* 1. 如果页面已经加载完成(readyState === 'complete'),则通过 setTimeout 异步执行回调
|
|
7
|
+
* 2. 如果页面仍在加载中,则添加一个一次性监听器,监听 'pageshow' 事件(页面完全加载并显示时触发)
|
|
8
|
+
*
|
|
9
|
+
* @param {Function} callback - 页面加载完成后要执行的回调函数
|
|
10
|
+
*
|
|
11
|
+
* 使用示例:
|
|
12
|
+
* afterLoad(() => {
|
|
13
|
+
* console.log('页面已完全加载');
|
|
14
|
+
* });
|
|
15
|
+
*/
|
|
16
|
+
export declare const afterLoad: (callback: any) => void;
|
|
17
|
+
/**
|
|
18
|
+
* 防抖函数
|
|
19
|
+
* @param func 需要防抖的函数
|
|
20
|
+
* @param delay 等待时间(毫秒)
|
|
21
|
+
* @param immediate 是否立即执行
|
|
22
|
+
* @returns 防抖后的函数
|
|
23
|
+
*/
|
|
24
|
+
export declare function debounce<T extends (...args: any[]) => any>(func: T, delay?: number, immediate?: boolean): (...args: Parameters<T>) => void;
|
|
25
|
+
/**
|
|
26
|
+
* 函数节流
|
|
27
|
+
*
|
|
28
|
+
* @export
|
|
29
|
+
* @param {Function} fn 需要节流的函数
|
|
30
|
+
* @param {number} delay 节流的时间间隔(毫秒)
|
|
31
|
+
* @return {(...args: any[]) => void} 返回一个包含节流功能的函数
|
|
32
|
+
*/
|
|
33
|
+
export declare const throttle: <T extends (...args: any[]) => void>(fn: T, delay?: number) => (...args: Parameters<T>) => void;
|
|
34
|
+
export declare function isTrackElement(element: HTMLElement): Element | undefined;
|
|
35
|
+
export declare function getNodeXPath(node: Element, curPath?: string): string;
|
|
36
|
+
export declare const getNavigationType: () => number | string;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
type RequestMethod = "GET" | "POST";
|
|
2
|
+
interface AjaxOptions {
|
|
3
|
+
method: RequestMethod;
|
|
4
|
+
url: string;
|
|
5
|
+
data?: any;
|
|
6
|
+
headers?: Record<string, string>;
|
|
7
|
+
withCredentials?: boolean;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* 是否支持sendBeacon
|
|
11
|
+
*/
|
|
12
|
+
export declare const isSupportBeacon = true;
|
|
13
|
+
/**
|
|
14
|
+
* ajax请求
|
|
15
|
+
* @param {AjaxOptions} params 请求配置
|
|
16
|
+
*/
|
|
17
|
+
export declare const ajax: <T>({ method, url, data, headers, withCredentials, }: AjaxOptions) => Promise<T>;
|
|
18
|
+
/**
|
|
19
|
+
* sendBeacon请求
|
|
20
|
+
* @param {string} url 接口地址
|
|
21
|
+
* @param {BodyInit | null} data 请求数据
|
|
22
|
+
*/
|
|
23
|
+
export declare const sendBeacon: (url: string, data?: BodyInit | null) => boolean;
|
|
24
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tengence/track-sdk-web",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "tengence web client track sdk",
|
|
5
|
+
"main": "dist/index.mjs",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"default": "./dist/index.mjs"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "rollup --c rollup.config.ts --configPlugin typescript",
|
|
17
|
+
"release": "npm publish --access public"
|
|
18
|
+
},
|
|
19
|
+
"private": false,
|
|
20
|
+
"author": "tengence",
|
|
21
|
+
"license": "ISC",
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@rollup/plugin-alias": "^5.1.1",
|
|
24
|
+
"@rollup/plugin-commonjs": "^28.0.1",
|
|
25
|
+
"@rollup/plugin-json": "^6.1.0",
|
|
26
|
+
"@rollup/plugin-node-resolve": "^15.3.0",
|
|
27
|
+
"@rollup/plugin-terser": "^0.4.4",
|
|
28
|
+
"@rollup/plugin-typescript": "^12.1.1",
|
|
29
|
+
"@types/node": "^22.13.1",
|
|
30
|
+
"cors": "^2.8.5",
|
|
31
|
+
"express": "^4.21.2",
|
|
32
|
+
"rollup": "^4.34.6",
|
|
33
|
+
"tslib": "^2.8.0",
|
|
34
|
+
"typescript": "^4.9.5"
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"dist"
|
|
38
|
+
]
|
|
39
|
+
}
|