@alibarbar/common 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 +338 -0
- package/dist/algorithm.d.mts +66 -0
- package/dist/algorithm.d.ts +66 -0
- package/dist/algorithm.js +44 -0
- package/dist/algorithm.js.map +1 -0
- package/dist/algorithm.mjs +3 -0
- package/dist/algorithm.mjs.map +1 -0
- package/dist/array.d.mts +139 -0
- package/dist/array.d.ts +139 -0
- package/dist/array.js +84 -0
- package/dist/array.js.map +1 -0
- package/dist/array.mjs +3 -0
- package/dist/array.mjs.map +1 -0
- package/dist/chunk-27UDDVLZ.js +259 -0
- package/dist/chunk-27UDDVLZ.js.map +1 -0
- package/dist/chunk-2FFSQ573.mjs +138 -0
- package/dist/chunk-2FFSQ573.mjs.map +1 -0
- package/dist/chunk-4RGXV4SJ.js +106 -0
- package/dist/chunk-4RGXV4SJ.js.map +1 -0
- package/dist/chunk-56W6YECK.js +374 -0
- package/dist/chunk-56W6YECK.js.map +1 -0
- package/dist/chunk-5BGSUGTI.mjs +128 -0
- package/dist/chunk-5BGSUGTI.mjs.map +1 -0
- package/dist/chunk-7E6GELHJ.mjs +302 -0
- package/dist/chunk-7E6GELHJ.mjs.map +1 -0
- package/dist/chunk-7V5UQXIO.js +89 -0
- package/dist/chunk-7V5UQXIO.js.map +1 -0
- package/dist/chunk-A4SWQXX7.mjs +484 -0
- package/dist/chunk-A4SWQXX7.mjs.map +1 -0
- package/dist/chunk-ALDC6LRJ.mjs +85 -0
- package/dist/chunk-ALDC6LRJ.mjs.map +1 -0
- package/dist/chunk-BHCRFURU.js +491 -0
- package/dist/chunk-BHCRFURU.js.map +1 -0
- package/dist/chunk-D7CS5EKF.js +110 -0
- package/dist/chunk-D7CS5EKF.js.map +1 -0
- package/dist/chunk-DYBSRI7V.js +189 -0
- package/dist/chunk-DYBSRI7V.js.map +1 -0
- package/dist/chunk-F3LAGHPG.js +332 -0
- package/dist/chunk-F3LAGHPG.js.map +1 -0
- package/dist/chunk-HLDFI7R2.mjs +175 -0
- package/dist/chunk-HLDFI7R2.mjs.map +1 -0
- package/dist/chunk-HME2N3VY.mjs +354 -0
- package/dist/chunk-HME2N3VY.mjs.map +1 -0
- package/dist/chunk-I3L42475.js +145 -0
- package/dist/chunk-I3L42475.js.map +1 -0
- package/dist/chunk-JBLX27WD.mjs +240 -0
- package/dist/chunk-JBLX27WD.mjs.map +1 -0
- package/dist/chunk-JHZ7M2MR.mjs +133 -0
- package/dist/chunk-JHZ7M2MR.mjs.map +1 -0
- package/dist/chunk-JK2SE3I2.js +100 -0
- package/dist/chunk-JK2SE3I2.js.map +1 -0
- package/dist/chunk-JQZBPAPO.js +157 -0
- package/dist/chunk-JQZBPAPO.js.map +1 -0
- package/dist/chunk-JXYGC2C5.mjs +100 -0
- package/dist/chunk-JXYGC2C5.mjs.map +1 -0
- package/dist/chunk-KGFTD255.js +104 -0
- package/dist/chunk-KGFTD255.js.map +1 -0
- package/dist/chunk-LBHBNPNJ.mjs +148 -0
- package/dist/chunk-LBHBNPNJ.mjs.map +1 -0
- package/dist/chunk-LCXGZISK.js +158 -0
- package/dist/chunk-LCXGZISK.js.map +1 -0
- package/dist/chunk-LF4CILQS.mjs +87 -0
- package/dist/chunk-LF4CILQS.mjs.map +1 -0
- package/dist/chunk-MMR6XQNX.js +98 -0
- package/dist/chunk-MMR6XQNX.js.map +1 -0
- package/dist/chunk-NSSDYX2U.mjs +80 -0
- package/dist/chunk-NSSDYX2U.mjs.map +1 -0
- package/dist/chunk-O3O67R4I.js +143 -0
- package/dist/chunk-O3O67R4I.js.map +1 -0
- package/dist/chunk-OX5PLOWB.js +90 -0
- package/dist/chunk-OX5PLOWB.js.map +1 -0
- package/dist/chunk-PJ7UCTX4.mjs +362 -0
- package/dist/chunk-PJ7UCTX4.mjs.map +1 -0
- package/dist/chunk-QIBE7GVN.mjs +81 -0
- package/dist/chunk-QIBE7GVN.mjs.map +1 -0
- package/dist/chunk-QIOC54LQ.mjs +130 -0
- package/dist/chunk-QIOC54LQ.mjs.map +1 -0
- package/dist/chunk-QV6MIQ7H.mjs +328 -0
- package/dist/chunk-QV6MIQ7H.mjs.map +1 -0
- package/dist/chunk-TQN37HIN.js +94 -0
- package/dist/chunk-TQN37HIN.js.map +1 -0
- package/dist/chunk-XJTZDXSR.mjs +94 -0
- package/dist/chunk-XJTZDXSR.mjs.map +1 -0
- package/dist/chunk-XVUE53T3.js +361 -0
- package/dist/chunk-XVUE53T3.js.map +1 -0
- package/dist/chunk-Y364QIQH.js +139 -0
- package/dist/chunk-Y364QIQH.js.map +1 -0
- package/dist/chunk-YXM6Q4JS.mjs +94 -0
- package/dist/chunk-YXM6Q4JS.mjs.map +1 -0
- package/dist/chunk-ZDMFMUDR.js +309 -0
- package/dist/chunk-ZDMFMUDR.js.map +1 -0
- package/dist/chunk-ZVJ6NQUM.mjs +82 -0
- package/dist/chunk-ZVJ6NQUM.mjs.map +1 -0
- package/dist/color.d.mts +74 -0
- package/dist/color.d.ts +74 -0
- package/dist/color.js +40 -0
- package/dist/color.js.map +1 -0
- package/dist/color.mjs +3 -0
- package/dist/color.mjs.map +1 -0
- package/dist/crypto.d.mts +92 -0
- package/dist/crypto.d.ts +92 -0
- package/dist/crypto.js +60 -0
- package/dist/crypto.js.map +1 -0
- package/dist/crypto.mjs +3 -0
- package/dist/crypto.mjs.map +1 -0
- package/dist/data-structure.d.mts +213 -0
- package/dist/data-structure.d.ts +213 -0
- package/dist/data-structure.js +32 -0
- package/dist/data-structure.js.map +1 -0
- package/dist/data-structure.mjs +3 -0
- package/dist/data-structure.mjs.map +1 -0
- package/dist/date.d.mts +108 -0
- package/dist/date.d.ts +108 -0
- package/dist/date.js +72 -0
- package/dist/date.js.map +1 -0
- package/dist/date.mjs +3 -0
- package/dist/date.mjs.map +1 -0
- package/dist/dom.d.mts +92 -0
- package/dist/dom.d.ts +92 -0
- package/dist/dom.js +56 -0
- package/dist/dom.js.map +1 -0
- package/dist/dom.mjs +3 -0
- package/dist/dom.mjs.map +1 -0
- package/dist/file.d.mts +44 -0
- package/dist/file.d.ts +44 -0
- package/dist/file.js +32 -0
- package/dist/file.js.map +1 -0
- package/dist/file.mjs +3 -0
- package/dist/file.mjs.map +1 -0
- package/dist/i18n.d.mts +77 -0
- package/dist/i18n.d.ts +77 -0
- package/dist/i18n.js +40 -0
- package/dist/i18n.js.map +1 -0
- package/dist/i18n.mjs +3 -0
- package/dist/i18n.mjs.map +1 -0
- package/dist/index.d.mts +155 -0
- package/dist/index.d.ts +155 -0
- package/dist/index.js +839 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +22 -0
- package/dist/index.mjs.map +1 -0
- package/dist/network.d.mts +47 -0
- package/dist/network.d.ts +47 -0
- package/dist/network.js +28 -0
- package/dist/network.js.map +1 -0
- package/dist/network.mjs +3 -0
- package/dist/network.mjs.map +1 -0
- package/dist/number.d.mts +100 -0
- package/dist/number.d.ts +100 -0
- package/dist/number.js +56 -0
- package/dist/number.js.map +1 -0
- package/dist/number.mjs +3 -0
- package/dist/number.mjs.map +1 -0
- package/dist/object.d.mts +132 -0
- package/dist/object.d.ts +132 -0
- package/dist/object.js +80 -0
- package/dist/object.js.map +1 -0
- package/dist/object.mjs +3 -0
- package/dist/object.mjs.map +1 -0
- package/dist/performance.d.mts +85 -0
- package/dist/performance.d.ts +85 -0
- package/dist/performance.js +40 -0
- package/dist/performance.js.map +1 -0
- package/dist/performance.mjs +3 -0
- package/dist/performance.mjs.map +1 -0
- package/dist/storage.d.mts +176 -0
- package/dist/storage.d.ts +176 -0
- package/dist/storage.js +33 -0
- package/dist/storage.js.map +1 -0
- package/dist/storage.mjs +4 -0
- package/dist/storage.mjs.map +1 -0
- package/dist/string.d.mts +105 -0
- package/dist/string.d.ts +105 -0
- package/dist/string.js +68 -0
- package/dist/string.js.map +1 -0
- package/dist/string.mjs +3 -0
- package/dist/string.mjs.map +1 -0
- package/dist/tracking.d.mts +182 -0
- package/dist/tracking.d.ts +182 -0
- package/dist/tracking.js +52 -0
- package/dist/tracking.js.map +1 -0
- package/dist/tracking.mjs +3 -0
- package/dist/tracking.mjs.map +1 -0
- package/dist/transform.d.mts +53 -0
- package/dist/transform.d.ts +53 -0
- package/dist/transform.js +32 -0
- package/dist/transform.js.map +1 -0
- package/dist/transform.mjs +3 -0
- package/dist/transform.mjs.map +1 -0
- package/dist/upload-DzlQtUBc.d.mts +202 -0
- package/dist/upload-DzlQtUBc.d.ts +202 -0
- package/dist/upload.d.mts +1 -0
- package/dist/upload.d.ts +1 -0
- package/dist/upload.js +17 -0
- package/dist/upload.js.map +1 -0
- package/dist/upload.mjs +4 -0
- package/dist/upload.mjs.map +1 -0
- package/dist/url.d.mts +82 -0
- package/dist/url.d.ts +82 -0
- package/dist/url.js +44 -0
- package/dist/url.js.map +1 -0
- package/dist/url.mjs +3 -0
- package/dist/url.mjs.map +1 -0
- package/dist/validation.d.mts +83 -0
- package/dist/validation.d.ts +83 -0
- package/dist/validation.js +60 -0
- package/dist/validation.js.map +1 -0
- package/dist/validation.mjs +3 -0
- package/dist/validation.mjs.map +1 -0
- package/package.json +170 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/array.ts"],"names":["shuffled"],"mappings":";AASO,SAAS,OAAU,GAAA,EAAe;AACvC,EAAA,OAAO,KAAA,CAAM,IAAA,CAAK,IAAI,GAAA,CAAI,GAAG,CAAC,CAAA;AAChC;AAQO,SAAS,OAAA,CACd,KACA,KAAA,EACgB;AAChB,EAAA,MAAM,MAAA,GAAS,OAAO,KAAA,KAAU,QAAA,GAC5B,CAAC,IAAA,KAAa,IAAA,CAA2B,KAAK,CAAA,GAC9C,KAAA;AAEJ,EAAA,OAAO,GAAA,CAAI,MAAA;AAAA,IACT,CAAC,KAAK,IAAA,KAAS;AACb,MAAA,MAAM,GAAA,GAAM,OAAO,IAAI,CAAA;AACvB,MAAA,IAAI,CAAC,GAAA,CAAI,GAAG,CAAA,EAAG;AACb,QAAA,GAAA,CAAI,GAAG,IAAI,EAAC;AAAA,MACd;AACA,MAAA,GAAA,CAAI,GAAG,CAAA,CAAE,IAAA,CAAK,IAAI,CAAA;AAClB,MAAA,OAAO,GAAA;AAAA,IACT,CAAA;AAAA,IACA;AAAC,GACH;AACF;AAQO,SAAS,KAAA,CAAS,KAAU,IAAA,EAAqB;AACtD,EAAA,MAAM,SAAgB,EAAC;AACvB,EAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,GAAA,CAAI,MAAA,EAAQ,KAAK,IAAA,EAAM;AACzC,IAAA,MAAA,CAAO,KAAK,GAAA,CAAI,KAAA,CAAM,CAAA,EAAG,CAAA,GAAI,IAAI,CAAC,CAAA;AAAA,EACpC;AACA,EAAA,OAAO,MAAA;AACT;AAQO,SAAS,OAAA,CAAW,GAAA,EAAkB,KAAA,GAAQ,CAAA,EAAQ;AAC3D,EAAA,OAAO,KAAA,GAAQ,IACX,GAAA,CAAI,MAAA;AAAA,IACF,CAAC,GAAA,EAAK,GAAA,KAAQ,GAAA,CAAI,OAAO,KAAA,CAAM,OAAA,CAAQ,GAAG,CAAA,GAAI,OAAA,CAAQ,GAAA,EAAK,KAAA,GAAQ,CAAC,IAAI,GAAG,CAAA;AAAA,IAC3E;AAAC,GACH,GACC,GAAA;AACP;AAOO,SAAS,QAAW,GAAA,EAAe;AACxC,EAAA,MAAM,MAAA,GAAS,CAAC,GAAG,GAAG,CAAA;AACtB,EAAA,KAAA,IAAS,IAAI,MAAA,CAAO,MAAA,GAAS,CAAA,EAAG,CAAA,GAAI,GAAG,CAAA,EAAA,EAAK;AAC1C,IAAA,MAAM,IAAI,IAAA,CAAK,KAAA,CAAM,KAAK,MAAA,EAAO,IAAK,IAAI,CAAA,CAAE,CAAA;AAC5C,IAAA,CAAC,MAAA,CAAO,CAAC,CAAA,EAAG,MAAA,CAAO,CAAC,CAAC,CAAA,GAAI,CAAC,MAAA,CAAO,CAAC,CAAA,EAAG,MAAA,CAAO,CAAC,CAAC,CAAA;AAAA,EAChD;AACA,EAAA,OAAO,MAAA;AACT;AAUO,SAAS,MAAA,CAAU,GAAA,EAAU,KAAA,GAAQ,CAAA,EAAY;AACtD,EAAA,IAAI,GAAA,CAAI,WAAW,CAAA,EAAG;AACpB,IAAA,MAAM,IAAI,MAAM,gCAAgC,CAAA;AAAA,EAClD;AAEA,EAAA,IAAI,UAAU,CAAA,EAAG;AACf,IAAA,MAAMA,SAAAA,GAAW,QAAQ,GAAG,CAAA;AAC5B,IAAA,OAAOA,UAAS,CAAC,CAAA;AAAA,EACnB;AAEA,EAAA,IAAI,SAAS,GAAA,CAAI,MAAA,EAAQ,OAAO,CAAC,GAAG,GAAG,CAAA;AACvC,EAAA,MAAM,QAAA,GAAW,QAAQ,GAAG,CAAA;AAC5B,EAAA,OAAO,QAAA,CAAS,KAAA,CAAM,CAAA,EAAG,KAAK,CAAA;AAChC;AAQO,SAAS,UAAA,CAAc,MAAW,IAAA,EAAgB;AACvD,EAAA,MAAM,IAAA,GAAO,IAAI,GAAA,CAAI,IAAI,CAAA;AACzB,EAAA,OAAO,KAAK,MAAA,CAAO,CAAA,IAAA,KAAQ,CAAC,IAAA,CAAK,GAAA,CAAI,IAAI,CAAC,CAAA;AAC5C;AAQO,SAAS,YAAA,CAAgB,MAAW,IAAA,EAAgB;AACzD,EAAA,MAAM,IAAA,GAAO,IAAI,GAAA,CAAI,IAAI,CAAA;AACzB,EAAA,OAAO,KAAK,MAAA,CAAO,CAAA,IAAA,KAAQ,IAAA,CAAK,GAAA,CAAI,IAAI,CAAC,CAAA;AAC3C;AAQO,SAAS,KAAA,CAAS,MAAW,IAAA,EAAgB;AAClD,EAAA,OAAO,OAAO,CAAC,GAAG,IAAA,EAAM,GAAG,IAAI,CAAC,CAAA;AAClC;AASO,SAAS,MAAA,CACd,GAAA,EACA,KAAA,EACA,KAAA,GAAwB,KAAA,EACnB;AACL,EAAA,MAAM,MAAA,GAAS,OAAO,KAAA,KAAU,QAAA,GAC5B,CAAC,IAAA,KAAa,IAAA,CAA2B,KAAK,CAAA,GAC9C,KAAA;AAEJ,EAAA,MAAM,MAAA,GAAS,CAAC,GAAG,GAAG,CAAA;AACtB,EAAA,MAAA,CAAO,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAM;AACpB,IAAA,MAAM,IAAA,GAAO,OAAO,CAAC,CAAA;AACrB,IAAA,MAAM,IAAA,GAAO,OAAO,CAAC,CAAA;AACrB,IAAA,IAAI,IAAA,GAAO,IAAA,EAAM,OAAO,KAAA,KAAU,QAAQ,EAAA,GAAK,CAAA;AAC/C,IAAA,IAAI,IAAA,GAAO,IAAA,EAAM,OAAO,KAAA,KAAU,QAAQ,CAAA,GAAI,EAAA;AAC9C,IAAA,OAAO,CAAA;AAAA,EACT,CAAC,CAAA;AACD,EAAA,OAAO,MAAA;AACT;AAQO,SAAS,WAAA,CAAe,KAAU,SAAA,EAAyC;AAChF,EAAA,OAAO,GAAA,CAAI,UAAU,SAAS,CAAA;AAChC;AAQO,SAAS,SAAA,CAAa,KAAU,SAAA,EAA6C;AAClF,EAAA,MAAM,SAAc,EAAC;AACrB,EAAA,MAAM,QAAa,EAAC;AACpB,EAAA,GAAA,CAAI,QAAQ,CAAA,IAAA,KAAQ;AAClB,IAAA,IAAI,SAAA,CAAU,IAAI,CAAA,EAAG;AACnB,MAAA,MAAA,CAAO,KAAK,IAAI,CAAA;AAAA,IAClB,CAAA,MAAO;AACL,MAAA,KAAA,CAAM,KAAK,IAAI,CAAA;AAAA,IACjB;AAAA,EACF,CAAC,CAAA;AACD,EAAA,OAAO,CAAC,QAAQ,KAAK,CAAA;AACvB;AAOO,SAAS,OACX,MAAA,EACuE;AAC1E,EAAA,MAAM,SAAA,GAAY,KAAK,GAAA,CAAI,GAAG,OAAO,GAAA,CAAI,CAAA,GAAA,KAAQ,GAAA,CAAkB,MAAM,CAAC,CAAA;AAC1E,EAAA,MAAM,SAAmF,EAAC;AAC1F,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,SAAA,EAAW,CAAA,EAAA,EAAK;AAClC,IAAA,MAAA,CAAO,IAAA;AAAA,MACL,MAAA,CAAO,GAAA,CAAI,CAAA,GAAA,KAAQ,GAAA,CAAkB,CAAC,CAAC;AAAA,KAGzC;AAAA,EACF;AACA,EAAA,OAAO,MAAA;AACT;AAOO,SAAS,MACd,KAAA,EACG;AACH,EAAA,IAAI,KAAA,CAAM,MAAA,KAAW,CAAA,EAAG,OAAO,EAAC;AAChC,EAAA,MAAM,MAAA,GAAU,KAAA,CAAM,CAAC,CAAA,CAAgB,MAAA;AACvC,EAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,EAAE,QAAO,EAAG,MAAM,EAAE,CAAA;AAC9C,EAAA,KAAA,CAAM,QAAQ,CAAA,IAAA,KAAQ;AACpB,IAAC,IAAA,CAAmB,OAAA,CAAQ,CAAC,GAAA,EAAK,KAAA,KAAU;AAC1C,MAAA,MAAA,CAAO,KAAK,CAAA,CAAE,IAAA,CAAK,GAAG,CAAA;AAAA,IACxB,CAAC,CAAA;AAAA,EACH,CAAC,CAAA;AACD,EAAA,OAAO,MAAA;AACT;AAOO,SAAS,QAAW,GAAA,EAAqD;AAC9E,EAAA,OAAO,IAAI,MAAA,CAAO,CAAC,IAAA,KAAoB,OAAA,CAAQ,IAAI,CAAC,CAAA;AACtD;AAQO,SAAS,IAAA,CAAQ,KAAU,CAAA,EAAgB;AAChD,EAAA,OAAO,GAAA,CAAI,KAAA,CAAM,CAAA,EAAG,CAAC,CAAA;AACvB;AAQO,SAAS,IAAA,CAAQ,KAAU,CAAA,EAAgB;AAChD,EAAA,OAAO,GAAA,CAAI,MAAM,CAAC,CAAA;AACpB;AAQO,SAAS,SAAA,CAAa,KAAU,SAAA,EAAsC;AAC3E,EAAA,MAAM,SAAc,EAAC;AACrB,EAAA,KAAA,MAAW,QAAQ,GAAA,EAAK;AACtB,IAAA,IAAI,CAAC,SAAA,CAAU,IAAI,CAAA,EAAG;AACtB,IAAA,MAAA,CAAO,KAAK,IAAI,CAAA;AAAA,EAClB;AACA,EAAA,OAAO,MAAA;AACT;AAQO,SAAS,SAAA,CAAa,KAAU,SAAA,EAAsC;AAC3E,EAAA,IAAI,KAAA,GAAQ,CAAA;AACZ,EAAA,OAAO,QAAQ,GAAA,CAAI,MAAA,IAAU,UAAU,GAAA,CAAI,KAAK,CAAC,CAAA,EAAG;AAClD,IAAA,KAAA,EAAA;AAAA,EACF;AACA,EAAA,OAAO,GAAA,CAAI,MAAM,KAAK,CAAA;AACxB","file":"chunk-2FFSQ573.mjs","sourcesContent":["/**\n * 数组工具函数\n */\n\n/**\n * 数组去重\n * @param arr - 输入数组\n * @returns 去重后的数组\n */\nexport function unique<T>(arr: T[]): T[] {\n return Array.from(new Set(arr));\n}\n\n/**\n * 数组分组\n * @param arr - 输入数组\n * @param keyFn - 分组键函数或属性名\n * @returns 分组后的对象\n */\nexport function groupBy<T, K extends string | number>(\n arr: T[],\n keyFn: ((item: T) => K) | string\n): Record<K, T[]> {\n const getKey = typeof keyFn === 'string' \n ? (item: T) => (item as Record<string, K>)[keyFn] as K\n : keyFn;\n \n return arr.reduce(\n (acc, item) => {\n const key = getKey(item);\n if (!acc[key]) {\n acc[key] = [];\n }\n acc[key].push(item);\n return acc;\n },\n {} as Record<K, T[]>\n );\n}\n\n/**\n * 数组分块\n * @param arr - 输入数组\n * @param size - 每块大小\n * @returns 分块后的二维数组\n */\nexport function chunk<T>(arr: T[], size: number): T[][] {\n const chunks: T[][] = [];\n for (let i = 0; i < arr.length; i += size) {\n chunks.push(arr.slice(i, i + size));\n }\n return chunks;\n}\n\n/**\n * 数组扁平化\n * @param arr - 输入数组\n * @param depth - 扁平化深度,默认为 1\n * @returns 扁平化后的数组\n */\nexport function flatten<T>(arr: (T | T[])[], depth = 1): T[] {\n return depth > 0\n ? arr.reduce<T[]>(\n (acc, val) => acc.concat(Array.isArray(val) ? flatten(val, depth - 1) : val),\n []\n )\n : (arr as T[]);\n}\n\n/**\n * 数组随机打乱(Fisher-Yates算法)\n * @param arr - 输入数组\n * @returns 打乱后的新数组\n */\nexport function shuffle<T>(arr: T[]): T[] {\n const result = [...arr];\n for (let i = result.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n [result[i], result[j]] = [result[j], result[i]];\n }\n return result;\n}\n\n/**\n * 随机取样\n * @param arr - 输入数组\n * @param count - 取样数量,默认为 1。当 count=1 时返回单个元素,否则返回数组\n * @returns 随机取样的数组或单个元素\n */\nexport function sample<T>(arr: T[]): T;\nexport function sample<T>(arr: T[], count: number): T | T[];\nexport function sample<T>(arr: T[], count = 1): T | T[] {\n if (arr.length === 0) {\n throw new Error('Cannot sample from empty array');\n }\n \n if (count === 1) {\n const shuffled = shuffle(arr);\n return shuffled[0];\n }\n \n if (count >= arr.length) return [...arr];\n const shuffled = shuffle(arr);\n return shuffled.slice(0, count);\n}\n\n/**\n * 数组差集(在arr1中但不在arr2中的元素)\n * @param arr1 - 第一个数组\n * @param arr2 - 第二个数组\n * @returns 差集数组\n */\nexport function difference<T>(arr1: T[], arr2: T[]): T[] {\n const set2 = new Set(arr2);\n return arr1.filter(item => !set2.has(item));\n}\n\n/**\n * 数组交集\n * @param arr1 - 第一个数组\n * @param arr2 - 第二个数组\n * @returns 交集数组\n */\nexport function intersection<T>(arr1: T[], arr2: T[]): T[] {\n const set2 = new Set(arr2);\n return arr1.filter(item => set2.has(item));\n}\n\n/**\n * 数组并集\n * @param arr1 - 第一个数组\n * @param arr2 - 第二个数组\n * @returns 并集数组\n */\nexport function union<T>(arr1: T[], arr2: T[]): T[] {\n return unique([...arr1, ...arr2]);\n}\n\n/**\n * 按属性排序\n * @param arr - 输入数组\n * @param keyFn - 获取排序键的函数或属性名\n * @param order - 排序顺序,'asc' 或 'desc',默认为 'asc'\n * @returns 排序后的新数组\n */\nexport function sortBy<T, K extends string | number>(\n arr: T[],\n keyFn: ((item: T) => K) | string,\n order: 'asc' | 'desc' = 'asc'\n): T[] {\n const getKey = typeof keyFn === 'string'\n ? (item: T) => (item as Record<string, K>)[keyFn] as K\n : keyFn;\n \n const result = [...arr];\n result.sort((a, b) => {\n const keyA = getKey(a);\n const keyB = getKey(b);\n if (keyA < keyB) return order === 'asc' ? -1 : 1;\n if (keyA > keyB) return order === 'asc' ? 1 : -1;\n return 0;\n });\n return result;\n}\n\n/**\n * 按条件查找索引\n * @param arr - 输入数组\n * @param predicate - 条件函数\n * @returns 找到的索引,未找到返回 -1\n */\nexport function findIndexBy<T>(arr: T[], predicate: (item: T) => boolean): number {\n return arr.findIndex(predicate);\n}\n\n/**\n * 数组分割(满足条件/不满足条件)\n * @param arr - 输入数组\n * @param predicate - 条件函数\n * @returns 包含两个数组的元组:[满足条件的数组, 不满足条件的数组]\n */\nexport function partition<T>(arr: T[], predicate: (item: T) => boolean): [T[], T[]] {\n const truthy: T[] = [];\n const falsy: T[] = [];\n arr.forEach(item => {\n if (predicate(item)) {\n truthy.push(item);\n } else {\n falsy.push(item);\n }\n });\n return [truthy, falsy];\n}\n\n/**\n * 数组压缩(将多个数组合并成元组数组)\n * @param arrays - 要压缩的数组\n * @returns 压缩后的元组数组\n */\nexport function zip<T extends readonly unknown[]>(\n ...arrays: T\n): Array<{ [K in keyof T]: T[K] extends readonly (infer U)[] ? U : never }> {\n const maxLength = Math.max(...arrays.map(arr => (arr as unknown[]).length));\n const result: Array<{ [K in keyof T]: T[K] extends readonly (infer U)[] ? U : never }> = [];\n for (let i = 0; i < maxLength; i++) {\n result.push(\n arrays.map(arr => (arr as unknown[])[i]) as {\n [K in keyof T]: T[K] extends readonly (infer U)[] ? U : never;\n }\n );\n }\n return result;\n}\n\n/**\n * 数组解压(将元组数组拆分成多个数组)\n * @param array - 要解压的数组\n * @returns 解压后的数组元组\n */\nexport function unzip<T extends readonly unknown[]>(\n array: Array<{ [K in keyof T]: T[number] extends readonly (infer U)[] ? U : never }>\n): T {\n if (array.length === 0) return [] as unknown as T;\n const length = (array[0] as unknown[]).length;\n const result = Array.from({ length }, () => []) as unknown as unknown[][];\n array.forEach(item => {\n (item as unknown[]).forEach((val, index) => {\n result[index].push(val);\n });\n });\n return result as unknown as T;\n}\n\n/**\n * 移除假值(null, undefined, false, 0, '')\n * @param arr - 输入数组\n * @returns 移除假值后的数组\n */\nexport function compact<T>(arr: (T | null | undefined | false | 0 | '')[]): T[] {\n return arr.filter((item): item is T => Boolean(item));\n}\n\n/**\n * 取前N个元素\n * @param arr - 输入数组\n * @param n - 要取的元素数量\n * @returns 前N个元素的数组\n */\nexport function take<T>(arr: T[], n: number): T[] {\n return arr.slice(0, n);\n}\n\n/**\n * 跳过前N个元素\n * @param arr - 输入数组\n * @param n - 要跳过的元素数量\n * @returns 跳过后的数组\n */\nexport function drop<T>(arr: T[], n: number): T[] {\n return arr.slice(n);\n}\n\n/**\n * 条件取元素(直到条件不满足)\n * @param arr - 输入数组\n * @param predicate - 条件函数\n * @returns 满足条件的连续元素数组\n */\nexport function takeWhile<T>(arr: T[], predicate: (item: T) => boolean): T[] {\n const result: T[] = [];\n for (const item of arr) {\n if (!predicate(item)) break;\n result.push(item);\n }\n return result;\n}\n\n/**\n * 条件跳过元素(直到条件不满足)\n * @param arr - 输入数组\n * @param predicate - 条件函数\n * @returns 跳过后的数组\n */\nexport function dropWhile<T>(arr: T[], predicate: (item: T) => boolean): T[] {\n let index = 0;\n while (index < arr.length && predicate(arr[index])) {\n index++;\n }\n return arr.slice(index);\n}\n"]}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/browser/network.ts
|
|
4
|
+
async function fetchWithRetry(url, options = {}, retryCount = 3, retryDelay = 1e3) {
|
|
5
|
+
let lastError = null;
|
|
6
|
+
for (let i = 0; i <= retryCount; i++) {
|
|
7
|
+
try {
|
|
8
|
+
const response = await fetch(url, options);
|
|
9
|
+
if (response.ok) {
|
|
10
|
+
return response;
|
|
11
|
+
}
|
|
12
|
+
if (i < retryCount) {
|
|
13
|
+
await new Promise((resolve) => setTimeout(resolve, retryDelay));
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
return response;
|
|
17
|
+
} catch (error) {
|
|
18
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
19
|
+
if (i < retryCount) {
|
|
20
|
+
await new Promise((resolve) => setTimeout(resolve, retryDelay));
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
throw lastError || new Error("Fetch failed after retries");
|
|
25
|
+
}
|
|
26
|
+
async function fetchWithTimeout(url, options = {}, timeout = 5e3) {
|
|
27
|
+
const controller = new AbortController();
|
|
28
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
29
|
+
try {
|
|
30
|
+
const response = await fetch(url, {
|
|
31
|
+
...options,
|
|
32
|
+
signal: controller.signal
|
|
33
|
+
});
|
|
34
|
+
clearTimeout(timeoutId);
|
|
35
|
+
return response;
|
|
36
|
+
} catch (error) {
|
|
37
|
+
clearTimeout(timeoutId);
|
|
38
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
39
|
+
throw new Error(`Request timeout after ${timeout}ms`);
|
|
40
|
+
}
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async function downloadFile(url, filename) {
|
|
45
|
+
if (typeof window === "undefined") {
|
|
46
|
+
throw new Error("downloadFile is only available in browser environment");
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
const response = await fetch(url);
|
|
50
|
+
if (!response.ok) {
|
|
51
|
+
throw new Error(`Failed to download file: ${response.statusText}`);
|
|
52
|
+
}
|
|
53
|
+
const blob = await response.blob();
|
|
54
|
+
const downloadUrl = window.URL.createObjectURL(blob);
|
|
55
|
+
const link = document.createElement("a");
|
|
56
|
+
link.href = downloadUrl;
|
|
57
|
+
link.download = filename || url.split("/").pop() || "download";
|
|
58
|
+
document.body.appendChild(link);
|
|
59
|
+
link.click();
|
|
60
|
+
document.body.removeChild(link);
|
|
61
|
+
window.URL.revokeObjectURL(downloadUrl);
|
|
62
|
+
} catch (error) {
|
|
63
|
+
throw error instanceof Error ? error : new Error(String(error));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function checkOnline() {
|
|
67
|
+
if (typeof navigator === "undefined") return true;
|
|
68
|
+
return navigator.onLine;
|
|
69
|
+
}
|
|
70
|
+
async function request(url, options = {}) {
|
|
71
|
+
const { method = "GET", headers = {}, body, timeout, retry = 0 } = options;
|
|
72
|
+
const fetchOptions = {
|
|
73
|
+
method,
|
|
74
|
+
headers: {
|
|
75
|
+
"Content-Type": "application/json",
|
|
76
|
+
...headers
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
if (body) {
|
|
80
|
+
fetchOptions.body = typeof body === "string" ? body : JSON.stringify(body);
|
|
81
|
+
}
|
|
82
|
+
let fetchFn = fetch;
|
|
83
|
+
if (timeout) {
|
|
84
|
+
fetchFn = (url2, opts) => fetchWithTimeout(url2, opts, timeout);
|
|
85
|
+
}
|
|
86
|
+
if (retry > 0) {
|
|
87
|
+
fetchFn = (url2, opts) => fetchWithRetry(url2, opts, retry);
|
|
88
|
+
}
|
|
89
|
+
const response = await fetchFn(url, fetchOptions);
|
|
90
|
+
if (!response.ok) {
|
|
91
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
92
|
+
}
|
|
93
|
+
const contentType = response.headers.get("content-type");
|
|
94
|
+
if (contentType && contentType.includes("application/json")) {
|
|
95
|
+
return response.json();
|
|
96
|
+
}
|
|
97
|
+
return response.text();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
exports.checkOnline = checkOnline;
|
|
101
|
+
exports.downloadFile = downloadFile;
|
|
102
|
+
exports.fetchWithRetry = fetchWithRetry;
|
|
103
|
+
exports.fetchWithTimeout = fetchWithTimeout;
|
|
104
|
+
exports.request = request;
|
|
105
|
+
//# sourceMappingURL=chunk-4RGXV4SJ.js.map
|
|
106
|
+
//# sourceMappingURL=chunk-4RGXV4SJ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/browser/network.ts"],"names":["url"],"mappings":"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","file":"chunk-4RGXV4SJ.js","sourcesContent":["/**\n * 网络工具函数\n */\n\n/**\n * 带重试的fetch\n * @param url - 请求URL\n * @param options - fetch选项\n * @param retryCount - 重试次数,默认为 3\n * @param retryDelay - 重试延迟(毫秒),默认为 1000\n * @returns Promise<Response>\n */\nexport async function fetchWithRetry(\n url: string,\n options: RequestInit = {},\n retryCount = 3,\n retryDelay = 1000\n): Promise<Response> {\n let lastError: Error | null = null;\n\n for (let i = 0; i <= retryCount; i++) {\n try {\n const response = await fetch(url, options);\n if (response.ok) {\n return response;\n }\n // 如果不是最后一次尝试,继续重试\n if (i < retryCount) {\n await new Promise(resolve => setTimeout(resolve, retryDelay));\n continue;\n }\n return response;\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n if (i < retryCount) {\n await new Promise(resolve => setTimeout(resolve, retryDelay));\n }\n }\n }\n\n throw lastError || new Error('Fetch failed after retries');\n}\n\n/**\n * 带超时的fetch\n * @param url - 请求URL\n * @param options - fetch选项\n * @param timeout - 超时时间(毫秒),默认为 5000\n * @returns Promise<Response>\n */\nexport async function fetchWithTimeout(\n url: string,\n options: RequestInit = {},\n timeout = 5000\n): Promise<Response> {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const response = await fetch(url, {\n ...options,\n signal: controller.signal,\n });\n clearTimeout(timeoutId);\n return response;\n } catch (error) {\n clearTimeout(timeoutId);\n if (error instanceof Error && error.name === 'AbortError') {\n throw new Error(`Request timeout after ${timeout}ms`);\n }\n throw error;\n }\n}\n\n/**\n * 文件下载\n * @param url - 文件URL\n * @param filename - 文件名,可选\n * @returns Promise<void>\n */\nexport async function downloadFile(url: string, filename?: string): Promise<void> {\n if (typeof window === 'undefined') {\n throw new Error('downloadFile is only available in browser environment');\n }\n\n try {\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(`Failed to download file: ${response.statusText}`);\n }\n\n const blob = await response.blob();\n const downloadUrl = window.URL.createObjectURL(blob);\n const link = document.createElement('a');\n link.href = downloadUrl;\n link.download = filename || url.split('/').pop() || 'download';\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n window.URL.revokeObjectURL(downloadUrl);\n } catch (error) {\n throw error instanceof Error ? error : new Error(String(error));\n }\n}\n\n/**\n * 检查网络状态\n * @returns 是否在线\n */\nexport function checkOnline(): boolean {\n if (typeof navigator === 'undefined') return true;\n return navigator.onLine;\n}\n\n/**\n * 统一的HTTP请求封装\n * @param url - 请求URL\n * @param options - 请求选项\n * @returns Promise<T>\n */\nexport async function request<T = unknown>(\n url: string,\n options: {\n method?: string;\n headers?: Record<string, string>;\n body?: unknown;\n timeout?: number;\n retry?: number;\n } = {}\n): Promise<T> {\n const { method = 'GET', headers = {}, body, timeout, retry = 0 } = options;\n\n const fetchOptions: RequestInit = {\n method,\n headers: {\n 'Content-Type': 'application/json',\n ...headers,\n },\n };\n\n if (body) {\n fetchOptions.body = typeof body === 'string' ? body : JSON.stringify(body);\n }\n\n let fetchFn: (url: string, options: RequestInit) => Promise<Response> = fetch;\n\n if (timeout) {\n fetchFn = (url, opts) => fetchWithTimeout(url, opts, timeout);\n }\n\n if (retry > 0) {\n fetchFn = (url, opts) => fetchWithRetry(url, opts, retry);\n }\n\n const response = await fetchFn(url, fetchOptions);\n\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n\n const contentType = response.headers.get('content-type');\n if (contentType && contentType.includes('application/json')) {\n return response.json() as Promise<T>;\n }\n\n return response.text() as Promise<T>;\n}\n"]}
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/helper/tracking.ts
|
|
4
|
+
var Tracker = class {
|
|
5
|
+
constructor(options) {
|
|
6
|
+
this.eventQueue = [];
|
|
7
|
+
this.batchTimer = null;
|
|
8
|
+
this.userInfo = {};
|
|
9
|
+
this.exposureObservers = /* @__PURE__ */ new Map();
|
|
10
|
+
this.exposureTimers = /* @__PURE__ */ new Map();
|
|
11
|
+
this.exposedElements = /* @__PURE__ */ new Set();
|
|
12
|
+
if (!options.endpoint && !options.customSend) {
|
|
13
|
+
throw new Error("Either endpoint or customSend must be provided");
|
|
14
|
+
}
|
|
15
|
+
this.options = {
|
|
16
|
+
endpoint: options.endpoint || "",
|
|
17
|
+
batchSize: options.batchSize ?? 10,
|
|
18
|
+
batchDelay: options.batchDelay ?? 3e3,
|
|
19
|
+
autoTrackPageView: options.autoTrackPageView ?? true,
|
|
20
|
+
autoTrackClick: options.autoTrackClick ?? false,
|
|
21
|
+
commonParams: options.commonParams || {},
|
|
22
|
+
customSend: options.customSend,
|
|
23
|
+
debug: options.debug ?? false
|
|
24
|
+
};
|
|
25
|
+
this.sessionId = this.generateSessionId();
|
|
26
|
+
if (this.options.autoTrackPageView && typeof window !== "undefined") {
|
|
27
|
+
this.trackPageView();
|
|
28
|
+
this.setupPageViewListener();
|
|
29
|
+
}
|
|
30
|
+
if (this.options.autoTrackClick && typeof window !== "undefined") {
|
|
31
|
+
this.setupClickListener();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* 生成会话ID
|
|
36
|
+
*/
|
|
37
|
+
generateSessionId() {
|
|
38
|
+
return `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* 获取公共数据
|
|
42
|
+
*/
|
|
43
|
+
getCommonData() {
|
|
44
|
+
const data = {
|
|
45
|
+
timestamp: Date.now(),
|
|
46
|
+
sessionId: this.sessionId,
|
|
47
|
+
...this.options.commonParams,
|
|
48
|
+
...this.userInfo
|
|
49
|
+
};
|
|
50
|
+
if (typeof window !== "undefined") {
|
|
51
|
+
data.url = window.location.href;
|
|
52
|
+
data.userAgent = navigator.userAgent;
|
|
53
|
+
}
|
|
54
|
+
return data;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* 添加事件到队列
|
|
58
|
+
*/
|
|
59
|
+
enqueue(event) {
|
|
60
|
+
this.eventQueue.push(event);
|
|
61
|
+
if (this.options.debug) {
|
|
62
|
+
console.log("[Tracker] Event enqueued:", event);
|
|
63
|
+
}
|
|
64
|
+
if (this.eventQueue.length >= this.options.batchSize) {
|
|
65
|
+
this.flush();
|
|
66
|
+
} else {
|
|
67
|
+
this.scheduleBatch();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* 安排批量上报
|
|
72
|
+
*/
|
|
73
|
+
scheduleBatch() {
|
|
74
|
+
if (this.batchTimer) {
|
|
75
|
+
clearTimeout(this.batchTimer);
|
|
76
|
+
}
|
|
77
|
+
this.batchTimer = setTimeout(() => {
|
|
78
|
+
this.flush();
|
|
79
|
+
}, this.options.batchDelay);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* 上报事件
|
|
83
|
+
*/
|
|
84
|
+
async sendEvents(events) {
|
|
85
|
+
if (events.length === 0) return;
|
|
86
|
+
try {
|
|
87
|
+
if (this.options.customSend) {
|
|
88
|
+
await this.options.customSend(events);
|
|
89
|
+
} else if (this.options.endpoint) {
|
|
90
|
+
await this.sendToEndpoint(events);
|
|
91
|
+
}
|
|
92
|
+
if (this.options.debug) {
|
|
93
|
+
console.log("[Tracker] Events sent:", events);
|
|
94
|
+
}
|
|
95
|
+
} catch (error) {
|
|
96
|
+
console.error("[Tracker] Failed to send events:", error);
|
|
97
|
+
this.eventQueue.unshift(...events);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* 发送到上报接口
|
|
102
|
+
*/
|
|
103
|
+
async sendToEndpoint(events) {
|
|
104
|
+
const response = await fetch(this.options.endpoint, {
|
|
105
|
+
method: "POST",
|
|
106
|
+
headers: {
|
|
107
|
+
"Content-Type": "application/json"
|
|
108
|
+
},
|
|
109
|
+
body: JSON.stringify({ events })
|
|
110
|
+
});
|
|
111
|
+
if (!response.ok) {
|
|
112
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* 立即上报所有待上报事件
|
|
117
|
+
*/
|
|
118
|
+
async flush() {
|
|
119
|
+
if (this.batchTimer) {
|
|
120
|
+
clearTimeout(this.batchTimer);
|
|
121
|
+
this.batchTimer = null;
|
|
122
|
+
}
|
|
123
|
+
if (this.eventQueue.length === 0) return;
|
|
124
|
+
const events = [...this.eventQueue];
|
|
125
|
+
this.eventQueue = [];
|
|
126
|
+
await this.sendEvents(events);
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* 追踪事件
|
|
130
|
+
*/
|
|
131
|
+
trackEvent(name, params) {
|
|
132
|
+
const event = {
|
|
133
|
+
type: "event",
|
|
134
|
+
name,
|
|
135
|
+
params,
|
|
136
|
+
...this.getCommonData()
|
|
137
|
+
};
|
|
138
|
+
this.enqueue(event);
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* 追踪页面浏览
|
|
142
|
+
*/
|
|
143
|
+
trackPageView(params) {
|
|
144
|
+
const event = {
|
|
145
|
+
type: "pageview",
|
|
146
|
+
name: "pageview",
|
|
147
|
+
params: {
|
|
148
|
+
path: typeof window !== "undefined" ? window.location.pathname : void 0,
|
|
149
|
+
search: typeof window !== "undefined" ? window.location.search : void 0,
|
|
150
|
+
hash: typeof window !== "undefined" ? window.location.hash : void 0,
|
|
151
|
+
...params
|
|
152
|
+
},
|
|
153
|
+
...this.getCommonData()
|
|
154
|
+
};
|
|
155
|
+
this.enqueue(event);
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* 追踪点击事件
|
|
159
|
+
*/
|
|
160
|
+
trackClick(element, params) {
|
|
161
|
+
const el = typeof element === "string" ? document.querySelector(element) : element;
|
|
162
|
+
if (!el) {
|
|
163
|
+
if (this.options.debug) {
|
|
164
|
+
console.warn("[Tracker] Element not found:", element);
|
|
165
|
+
}
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
const event = {
|
|
169
|
+
type: "click",
|
|
170
|
+
name: "click",
|
|
171
|
+
params: {
|
|
172
|
+
element: el.tagName.toLowerCase(),
|
|
173
|
+
id: el.id || void 0,
|
|
174
|
+
className: el.className || void 0,
|
|
175
|
+
text: el.textContent?.trim().substring(0, 100) || void 0,
|
|
176
|
+
...params
|
|
177
|
+
},
|
|
178
|
+
...this.getCommonData()
|
|
179
|
+
};
|
|
180
|
+
this.enqueue(event);
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* 追踪曝光事件
|
|
184
|
+
*/
|
|
185
|
+
trackExposure(element, options, params) {
|
|
186
|
+
if (typeof window === "undefined" || !window.IntersectionObserver) {
|
|
187
|
+
if (this.options.debug) {
|
|
188
|
+
console.warn("[Tracker] IntersectionObserver is not supported");
|
|
189
|
+
}
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
const el = typeof element === "string" ? document.querySelector(element) : element;
|
|
193
|
+
if (!el) {
|
|
194
|
+
if (this.options.debug) {
|
|
195
|
+
console.warn("[Tracker] Element not found:", element);
|
|
196
|
+
}
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
const { threshold = 0.5, duration = 1e3, once = true } = options || {};
|
|
200
|
+
const elementId = `${Date.now()}-${Math.random()}`;
|
|
201
|
+
if (once && this.exposedElements.has(el)) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
let exposureTimer = null;
|
|
205
|
+
let startTime = null;
|
|
206
|
+
const observer = new IntersectionObserver(
|
|
207
|
+
(entries) => {
|
|
208
|
+
entries.forEach((entry) => {
|
|
209
|
+
if (entry.isIntersecting && entry.intersectionRatio >= threshold) {
|
|
210
|
+
if (!startTime) {
|
|
211
|
+
startTime = Date.now();
|
|
212
|
+
const currentStartTime = startTime;
|
|
213
|
+
exposureTimer = setTimeout(() => {
|
|
214
|
+
const event = {
|
|
215
|
+
type: "exposure",
|
|
216
|
+
name: "exposure",
|
|
217
|
+
params: {
|
|
218
|
+
element: el.tagName.toLowerCase(),
|
|
219
|
+
id: el.id || void 0,
|
|
220
|
+
className: el.className || void 0,
|
|
221
|
+
duration: currentStartTime ? Date.now() - currentStartTime : duration,
|
|
222
|
+
...params
|
|
223
|
+
},
|
|
224
|
+
...this.getCommonData()
|
|
225
|
+
};
|
|
226
|
+
this.enqueue(event);
|
|
227
|
+
this.exposedElements.add(el);
|
|
228
|
+
observer.disconnect();
|
|
229
|
+
this.exposureObservers.delete(elementId);
|
|
230
|
+
if (exposureTimer) {
|
|
231
|
+
clearTimeout(exposureTimer);
|
|
232
|
+
this.exposureTimers.delete(elementId);
|
|
233
|
+
}
|
|
234
|
+
}, duration);
|
|
235
|
+
this.exposureTimers.set(elementId, exposureTimer);
|
|
236
|
+
}
|
|
237
|
+
} else {
|
|
238
|
+
if (exposureTimer) {
|
|
239
|
+
clearTimeout(exposureTimer);
|
|
240
|
+
this.exposureTimers.delete(elementId);
|
|
241
|
+
}
|
|
242
|
+
startTime = null;
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
threshold: [threshold]
|
|
248
|
+
}
|
|
249
|
+
);
|
|
250
|
+
observer.observe(el);
|
|
251
|
+
this.exposureObservers.set(elementId, observer);
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* 设置用户信息
|
|
255
|
+
*/
|
|
256
|
+
setUserInfo(userInfo) {
|
|
257
|
+
this.userInfo = { ...this.userInfo, ...userInfo };
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* 设置公共参数
|
|
261
|
+
*/
|
|
262
|
+
setCommonParams(params) {
|
|
263
|
+
this.options.commonParams = { ...this.options.commonParams, ...params };
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* 设置页面浏览监听器(SPA应用)
|
|
267
|
+
*/
|
|
268
|
+
setupPageViewListener() {
|
|
269
|
+
if (typeof window === "undefined") return;
|
|
270
|
+
window.addEventListener("popstate", () => {
|
|
271
|
+
this.trackPageView();
|
|
272
|
+
});
|
|
273
|
+
const originalPushState = history.pushState;
|
|
274
|
+
const originalReplaceState = history.replaceState;
|
|
275
|
+
history.pushState = function(...args) {
|
|
276
|
+
originalPushState.apply(history, args);
|
|
277
|
+
window.dispatchEvent(new Event("pushstate"));
|
|
278
|
+
};
|
|
279
|
+
history.replaceState = function(...args) {
|
|
280
|
+
originalReplaceState.apply(history, args);
|
|
281
|
+
window.dispatchEvent(new Event("replacestate"));
|
|
282
|
+
};
|
|
283
|
+
window.addEventListener("pushstate", () => {
|
|
284
|
+
this.trackPageView();
|
|
285
|
+
});
|
|
286
|
+
window.addEventListener("replacestate", () => {
|
|
287
|
+
this.trackPageView();
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* 设置点击事件监听器
|
|
292
|
+
*/
|
|
293
|
+
setupClickListener() {
|
|
294
|
+
if (typeof window === "undefined" || typeof document === "undefined") return;
|
|
295
|
+
document.addEventListener(
|
|
296
|
+
"click",
|
|
297
|
+
(event) => {
|
|
298
|
+
const target = event.target;
|
|
299
|
+
if (target) {
|
|
300
|
+
this.trackClick(target);
|
|
301
|
+
}
|
|
302
|
+
},
|
|
303
|
+
true
|
|
304
|
+
// 使用捕获阶段
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* 清理资源
|
|
309
|
+
*/
|
|
310
|
+
destroy() {
|
|
311
|
+
if (this.batchTimer) {
|
|
312
|
+
clearTimeout(this.batchTimer);
|
|
313
|
+
this.batchTimer = null;
|
|
314
|
+
}
|
|
315
|
+
this.exposureObservers.forEach((observer) => observer.disconnect());
|
|
316
|
+
this.exposureObservers.clear();
|
|
317
|
+
this.exposureTimers.forEach((timer) => clearTimeout(timer));
|
|
318
|
+
this.exposureTimers.clear();
|
|
319
|
+
this.flush().catch(console.error);
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
function createTracker(options) {
|
|
323
|
+
return new Tracker(options);
|
|
324
|
+
}
|
|
325
|
+
var defaultTracker = null;
|
|
326
|
+
function initTracker(options) {
|
|
327
|
+
if (defaultTracker) {
|
|
328
|
+
console.warn("[Tracker] Default tracker already initialized");
|
|
329
|
+
return defaultTracker;
|
|
330
|
+
}
|
|
331
|
+
defaultTracker = new Tracker(options);
|
|
332
|
+
return defaultTracker;
|
|
333
|
+
}
|
|
334
|
+
function getTracker() {
|
|
335
|
+
if (!defaultTracker) {
|
|
336
|
+
throw new Error("Tracker not initialized. Call initTracker() first.");
|
|
337
|
+
}
|
|
338
|
+
return defaultTracker;
|
|
339
|
+
}
|
|
340
|
+
function trackEvent(name, params) {
|
|
341
|
+
getTracker().trackEvent(name, params);
|
|
342
|
+
}
|
|
343
|
+
function trackPageView(params) {
|
|
344
|
+
getTracker().trackPageView(params);
|
|
345
|
+
}
|
|
346
|
+
function trackClick(element, params) {
|
|
347
|
+
getTracker().trackClick(element, params);
|
|
348
|
+
}
|
|
349
|
+
function trackExposure(element, options, params) {
|
|
350
|
+
getTracker().trackExposure(element, options, params);
|
|
351
|
+
}
|
|
352
|
+
function setUserInfo(userInfo) {
|
|
353
|
+
getTracker().setUserInfo(userInfo);
|
|
354
|
+
}
|
|
355
|
+
function setCommonParams(params) {
|
|
356
|
+
getTracker().setCommonParams(params);
|
|
357
|
+
}
|
|
358
|
+
async function flush() {
|
|
359
|
+
await getTracker().flush();
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
exports.Tracker = Tracker;
|
|
363
|
+
exports.createTracker = createTracker;
|
|
364
|
+
exports.flush = flush;
|
|
365
|
+
exports.getTracker = getTracker;
|
|
366
|
+
exports.initTracker = initTracker;
|
|
367
|
+
exports.setCommonParams = setCommonParams;
|
|
368
|
+
exports.setUserInfo = setUserInfo;
|
|
369
|
+
exports.trackClick = trackClick;
|
|
370
|
+
exports.trackEvent = trackEvent;
|
|
371
|
+
exports.trackExposure = trackExposure;
|
|
372
|
+
exports.trackPageView = trackPageView;
|
|
373
|
+
//# sourceMappingURL=chunk-56W6YECK.js.map
|
|
374
|
+
//# sourceMappingURL=chunk-56W6YECK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/helper/tracking.ts"],"names":[],"mappings":";;;AAsEO,IAAM,UAAN,MAAc;AAAA,EAgBnB,YAAY,OAAA,EAAyB;AARrC,IAAA,IAAA,CAAQ,aAA8B,EAAC;AACvC,IAAA,IAAA,CAAQ,UAAA,GAAmD,IAAA;AAC3D,IAAA,IAAA,CAAQ,WAAoC,EAAC;AAE7C,IAAA,IAAA,CAAQ,iBAAA,uBAA2D,GAAA,EAAI;AACvE,IAAA,IAAA,CAAQ,cAAA,uBAAiE,GAAA,EAAI;AAC7E,IAAA,IAAA,CAAQ,eAAA,uBAAoC,GAAA,EAAI;AAG9C,IAAA,IAAI,CAAC,OAAA,CAAQ,QAAA,IAAY,CAAC,QAAQ,UAAA,EAAY;AAC5C,MAAA,MAAM,IAAI,MAAM,gDAAgD,CAAA;AAAA,IAClE;AAEA,IAAA,IAAA,CAAK,OAAA,GAAU;AAAA,MACb,QAAA,EAAU,QAAQ,QAAA,IAAY,EAAA;AAAA,MAC9B,SAAA,EAAW,QAAQ,SAAA,IAAa,EAAA;AAAA,MAChC,UAAA,EAAY,QAAQ,UAAA,IAAc,GAAA;AAAA,MAClC,iBAAA,EAAmB,QAAQ,iBAAA,IAAqB,IAAA;AAAA,MAChD,cAAA,EAAgB,QAAQ,cAAA,IAAkB,KAAA;AAAA,MAC1C,YAAA,EAAc,OAAA,CAAQ,YAAA,IAAgB,EAAC;AAAA,MACvC,YAAY,OAAA,CAAQ,UAAA;AAAA,MACpB,KAAA,EAAO,QAAQ,KAAA,IAAS;AAAA,KAC1B;AAGA,IAAA,IAAA,CAAK,SAAA,GAAY,KAAK,iBAAA,EAAkB;AAGxC,IAAA,IAAI,IAAA,CAAK,OAAA,CAAQ,iBAAA,IAAqB,OAAO,WAAW,WAAA,EAAa;AACnE,MAAA,IAAA,CAAK,aAAA,EAAc;AAEnB,MAAA,IAAA,CAAK,qBAAA,EAAsB;AAAA,IAC7B;AAGA,IAAA,IAAI,IAAA,CAAK,OAAA,CAAQ,cAAA,IAAkB,OAAO,WAAW,WAAA,EAAa;AAChE,MAAA,IAAA,CAAK,kBAAA,EAAmB;AAAA,IAC1B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,iBAAA,GAA4B;AAClC,IAAA,OAAO,CAAA,EAAG,IAAA,CAAK,GAAA,EAAK,IAAI,IAAA,CAAK,MAAA,EAAO,CAAE,QAAA,CAAS,EAAE,CAAA,CAAE,SAAA,CAAU,CAAA,EAAG,CAAC,CAAC,CAAA,CAAA;AAAA,EACpE;AAAA;AAAA;AAAA;AAAA,EAKQ,aAAA,GACkD;AACxD,IAAA,MAAM,IAAA,GACsD;AAAA,MAC1D,SAAA,EAAW,KAAK,GAAA,EAAI;AAAA,MACpB,WAAW,IAAA,CAAK,SAAA;AAAA,MAChB,GAAG,KAAK,OAAA,CAAQ,YAAA;AAAA,MAChB,GAAG,IAAA,CAAK;AAAA,KACV;AAEA,IAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AACjC,MAAA,IAAA,CAAK,GAAA,GAAM,OAAO,QAAA,CAAS,IAAA;AAC3B,MAAA,IAAA,CAAK,YAAY,SAAA,CAAU,SAAA;AAAA,IAC7B;AAEA,IAAA,OAAO,IAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,QAAQ,KAAA,EAA4B;AAC1C,IAAA,IAAA,CAAK,UAAA,CAAW,KAAK,KAAK,CAAA;AAE1B,IAAA,IAAI,IAAA,CAAK,QAAQ,KAAA,EAAO;AACtB,MAAA,OAAA,CAAQ,GAAA,CAAI,6BAA6B,KAAK,CAAA;AAAA,IAChD;AAGA,IAAA,IAAI,IAAA,CAAK,UAAA,CAAW,MAAA,IAAU,IAAA,CAAK,QAAQ,SAAA,EAAW;AACpD,MAAA,IAAA,CAAK,KAAA,EAAM;AAAA,IACb,CAAA,MAAO;AAEL,MAAA,IAAA,CAAK,aAAA,EAAc;AAAA,IACrB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,aAAA,GAAsB;AAC5B,IAAA,IAAI,KAAK,UAAA,EAAY;AACnB,MAAA,YAAA,CAAa,KAAK,UAAU,CAAA;AAAA,IAC9B;AAEA,IAAA,IAAA,CAAK,UAAA,GAAa,WAAW,MAAM;AACjC,MAAA,IAAA,CAAK,KAAA,EAAM;AAAA,IACb,CAAA,EAAG,IAAA,CAAK,OAAA,CAAQ,UAAU,CAAA;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,WAAW,MAAA,EAAwC;AAC/D,IAAA,IAAI,MAAA,CAAO,WAAW,CAAA,EAAG;AAEzB,IAAA,IAAI;AACF,MAAA,IAAI,IAAA,CAAK,QAAQ,UAAA,EAAY;AAC3B,QAAA,MAAM,IAAA,CAAK,OAAA,CAAQ,UAAA,CAAW,MAAM,CAAA;AAAA,MACtC,CAAA,MAAA,IAAW,IAAA,CAAK,OAAA,CAAQ,QAAA,EAAU;AAChC,QAAA,MAAM,IAAA,CAAK,eAAe,MAAM,CAAA;AAAA,MAClC;AAEA,MAAA,IAAI,IAAA,CAAK,QAAQ,KAAA,EAAO;AACtB,QAAA,OAAA,CAAQ,GAAA,CAAI,0BAA0B,MAAM,CAAA;AAAA,MAC9C;AAAA,IACF,SAAS,KAAA,EAAO;AACd,MAAA,OAAA,CAAQ,KAAA,CAAM,oCAAoC,KAAK,CAAA;AAEvD,MAAA,IAAA,CAAK,UAAA,CAAW,OAAA,CAAQ,GAAG,MAAM,CAAA;AAAA,IACnC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,eAAe,MAAA,EAAwC;AACnE,IAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,IAAA,CAAK,QAAQ,QAAA,EAAU;AAAA,MAClD,MAAA,EAAQ,MAAA;AAAA,MACR,OAAA,EAAS;AAAA,QACP,cAAA,EAAgB;AAAA,OAClB;AAAA,MACA,IAAA,EAAM,IAAA,CAAK,SAAA,CAAU,EAAE,QAAQ;AAAA,KAChC,CAAA;AAED,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,oBAAA,EAAuB,QAAA,CAAS,MAAM,CAAA,CAAE,CAAA;AAAA,IAC1D;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,KAAA,GAAuB;AAC3B,IAAA,IAAI,KAAK,UAAA,EAAY;AACnB,MAAA,YAAA,CAAa,KAAK,UAAU,CAAA;AAC5B,MAAA,IAAA,CAAK,UAAA,GAAa,IAAA;AAAA,IACpB;AAEA,IAAA,IAAI,IAAA,CAAK,UAAA,CAAW,MAAA,KAAW,CAAA,EAAG;AAElC,IAAA,MAAM,MAAA,GAAS,CAAC,GAAG,IAAA,CAAK,UAAU,CAAA;AAClC,IAAA,IAAA,CAAK,aAAa,EAAC;AAEnB,IAAA,MAAM,IAAA,CAAK,WAAW,MAAM,CAAA;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA,EAKA,UAAA,CAAW,MAAc,MAAA,EAAwC;AAC/D,IAAA,MAAM,KAAA,GAAuB;AAAA,MAC3B,IAAA,EAAM,OAAA;AAAA,MACN,IAAA;AAAA,MACA,MAAA;AAAA,MACA,GAAG,KAAK,aAAA;AAAc,KACxB;AAEA,IAAA,IAAA,CAAK,QAAQ,KAAK,CAAA;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA,EAKA,cAAc,MAAA,EAAwC;AACpD,IAAA,MAAM,KAAA,GAAuB;AAAA,MAC3B,IAAA,EAAM,UAAA;AAAA,MACN,IAAA,EAAM,UAAA;AAAA,MACN,MAAA,EAAQ;AAAA,QACN,MAAM,OAAO,MAAA,KAAW,WAAA,GAAc,MAAA,CAAO,SAAS,QAAA,GAAW,MAAA;AAAA,QACjE,QAAQ,OAAO,MAAA,KAAW,WAAA,GAAc,MAAA,CAAO,SAAS,MAAA,GAAS,MAAA;AAAA,QACjE,MAAM,OAAO,MAAA,KAAW,WAAA,GAAc,MAAA,CAAO,SAAS,IAAA,GAAO,MAAA;AAAA,QAC7D,GAAG;AAAA,OACL;AAAA,MACA,GAAG,KAAK,aAAA;AAAc,KACxB;AAEA,IAAA,IAAA,CAAK,QAAQ,KAAK,CAAA;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA,EAKA,UAAA,CAAW,SAA2B,MAAA,EAAwC;AAC5E,IAAA,MAAM,KAAK,OAAO,OAAA,KAAY,WAAW,QAAA,CAAS,aAAA,CAAc,OAAO,CAAA,GAAI,OAAA;AAC3E,IAAA,IAAI,CAAC,EAAA,EAAI;AACP,MAAA,IAAI,IAAA,CAAK,QAAQ,KAAA,EAAO;AACtB,QAAA,OAAA,CAAQ,IAAA,CAAK,gCAAgC,OAAO,CAAA;AAAA,MACtD;AACA,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,KAAA,GAAuB;AAAA,MAC3B,IAAA,EAAM,OAAA;AAAA,MACN,IAAA,EAAM,OAAA;AAAA,MACN,MAAA,EAAQ;AAAA,QACN,OAAA,EAAS,EAAA,CAAG,OAAA,CAAQ,WAAA,EAAY;AAAA,QAChC,EAAA,EAAI,GAAG,EAAA,IAAM,MAAA;AAAA,QACb,SAAA,EAAW,GAAG,SAAA,IAAa,MAAA;AAAA,QAC3B,IAAA,EAAM,GAAG,WAAA,EAAa,IAAA,GAAO,SAAA,CAAU,CAAA,EAAG,GAAG,CAAA,IAAK,MAAA;AAAA,QAClD,GAAG;AAAA,OACL;AAAA,MACA,GAAG,KAAK,aAAA;AAAc,KACxB;AAEA,IAAA,IAAA,CAAK,QAAQ,KAAK,CAAA;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA,EAKA,aAAA,CACE,OAAA,EACA,OAAA,EACA,MAAA,EACM;AACN,IAAA,IAAI,OAAO,MAAA,KAAW,WAAA,IAAe,CAAC,OAAO,oBAAA,EAAsB;AACjE,MAAA,IAAI,IAAA,CAAK,QAAQ,KAAA,EAAO;AACtB,QAAA,OAAA,CAAQ,KAAK,iDAAiD,CAAA;AAAA,MAChE;AACA,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,KAAK,OAAO,OAAA,KAAY,WAAW,QAAA,CAAS,aAAA,CAAc,OAAO,CAAA,GAAI,OAAA;AAC3E,IAAA,IAAI,CAAC,EAAA,EAAI;AACP,MAAA,IAAI,IAAA,CAAK,QAAQ,KAAA,EAAO;AACtB,QAAA,OAAA,CAAQ,IAAA,CAAK,gCAAgC,OAAO,CAAA;AAAA,MACtD;AACA,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,EAAE,YAAY,GAAA,EAAK,QAAA,GAAW,KAAM,IAAA,GAAO,IAAA,EAAK,GAAI,OAAA,IAAW,EAAC;AAEtE,IAAA,MAAM,SAAA,GAAY,GAAG,IAAA,CAAK,GAAA,EAAK,CAAA,CAAA,EAAI,IAAA,CAAK,QAAQ,CAAA,CAAA;AAGhD,IAAA,IAAI,IAAA,IAAQ,IAAA,CAAK,eAAA,CAAgB,GAAA,CAAI,EAAE,CAAA,EAAG;AACxC,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,aAAA,GAAsD,IAAA;AAC1D,IAAA,IAAI,SAAA,GAA2B,IAAA;AAE/B,IAAA,MAAM,WAAW,IAAI,oBAAA;AAAA,MACnB,CAAA,OAAA,KAAW;AACT,QAAA,OAAA,CAAQ,QAAQ,CAAA,KAAA,KAAS;AACvB,UAAA,IAAI,KAAA,CAAM,cAAA,IAAkB,KAAA,CAAM,iBAAA,IAAqB,SAAA,EAAW;AAEhE,YAAA,IAAI,CAAC,SAAA,EAAW;AACd,cAAA,SAAA,GAAY,KAAK,GAAA,EAAI;AACrB,cAAA,MAAM,gBAAA,GAAmB,SAAA;AACzB,cAAA,aAAA,GAAgB,WAAW,MAAM;AAE/B,gBAAA,MAAM,KAAA,GAAuB;AAAA,kBAC3B,IAAA,EAAM,UAAA;AAAA,kBACN,IAAA,EAAM,UAAA;AAAA,kBACN,MAAA,EAAQ;AAAA,oBACN,OAAA,EAAS,EAAA,CAAG,OAAA,CAAQ,WAAA,EAAY;AAAA,oBAChC,EAAA,EAAI,GAAG,EAAA,IAAM,MAAA;AAAA,oBACb,SAAA,EAAW,GAAG,SAAA,IAAa,MAAA;AAAA,oBAC3B,QAAA,EAAU,gBAAA,GAAmB,IAAA,CAAK,GAAA,KAAQ,gBAAA,GAAmB,QAAA;AAAA,oBAC7D,GAAG;AAAA,mBACL;AAAA,kBACA,GAAG,KAAK,aAAA;AAAc,iBACxB;AAEA,gBAAA,IAAA,CAAK,QAAQ,KAAK,CAAA;AAClB,gBAAA,IAAA,CAAK,eAAA,CAAgB,IAAI,EAAE,CAAA;AAG3B,gBAAA,QAAA,CAAS,UAAA,EAAW;AACpB,gBAAA,IAAA,CAAK,iBAAA,CAAkB,OAAO,SAAS,CAAA;AACvC,gBAAA,IAAI,aAAA,EAAe;AACjB,kBAAA,YAAA,CAAa,aAAa,CAAA;AAC1B,kBAAA,IAAA,CAAK,cAAA,CAAe,OAAO,SAAS,CAAA;AAAA,gBACtC;AAAA,cACF,GAAG,QAAQ,CAAA;AACX,cAAA,IAAA,CAAK,cAAA,CAAe,GAAA,CAAI,SAAA,EAAW,aAAa,CAAA;AAAA,YAClD;AAAA,UACF,CAAA,MAAO;AAEL,YAAA,IAAI,aAAA,EAAe;AACjB,cAAA,YAAA,CAAa,aAAa,CAAA;AAC1B,cAAA,IAAA,CAAK,cAAA,CAAe,OAAO,SAAS,CAAA;AAAA,YACtC;AACA,YAAA,SAAA,GAAY,IAAA;AAAA,UACd;AAAA,QACF,CAAC,CAAA;AAAA,MACH,CAAA;AAAA,MACA;AAAA,QACE,SAAA,EAAW,CAAC,SAAS;AAAA;AACvB,KACF;AAEA,IAAA,QAAA,CAAS,QAAQ,EAAE,CAAA;AACnB,IAAA,IAAA,CAAK,iBAAA,CAAkB,GAAA,CAAI,SAAA,EAAW,QAAQ,CAAA;AAAA,EAChD;AAAA;AAAA;AAAA;AAAA,EAKA,YAAY,QAAA,EAAyC;AACnD,IAAA,IAAA,CAAK,WAAW,EAAE,GAAG,IAAA,CAAK,QAAA,EAAU,GAAG,QAAA,EAAS;AAAA,EAClD;AAAA;AAAA;AAAA;AAAA,EAKA,gBAAgB,MAAA,EAAuC;AACrD,IAAA,IAAA,CAAK,OAAA,CAAQ,eAAe,EAAE,GAAG,KAAK,OAAA,CAAQ,YAAA,EAAc,GAAG,MAAA,EAAO;AAAA,EACxE;AAAA;AAAA;AAAA;AAAA,EAKQ,qBAAA,GAA8B;AACpC,IAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AAGnC,IAAA,MAAA,CAAO,gBAAA,CAAiB,YAAY,MAAM;AACxC,MAAA,IAAA,CAAK,aAAA,EAAc;AAAA,IACrB,CAAC,CAAA;AAGD,IAAA,MAAM,oBAAoB,OAAA,CAAQ,SAAA;AAClC,IAAA,MAAM,uBAAuB,OAAA,CAAQ,YAAA;AAErC,IAAA,OAAA,CAAQ,SAAA,GAAY,YAAa,IAAA,EAAM;AACrC,MAAA,iBAAA,CAAkB,KAAA,CAAM,SAAS,IAAI,CAAA;AACrC,MAAA,MAAA,CAAO,aAAA,CAAc,IAAI,KAAA,CAAM,WAAW,CAAC,CAAA;AAAA,IAC7C,CAAA;AAEA,IAAA,OAAA,CAAQ,YAAA,GAAe,YAAa,IAAA,EAAM;AACxC,MAAA,oBAAA,CAAqB,KAAA,CAAM,SAAS,IAAI,CAAA;AACxC,MAAA,MAAA,CAAO,aAAA,CAAc,IAAI,KAAA,CAAM,cAAc,CAAC,CAAA;AAAA,IAChD,CAAA;AAEA,IAAA,MAAA,CAAO,gBAAA,CAAiB,aAAa,MAAM;AACzC,MAAA,IAAA,CAAK,aAAA,EAAc;AAAA,IACrB,CAAC,CAAA;AAED,IAAA,MAAA,CAAO,gBAAA,CAAiB,gBAAgB,MAAM;AAC5C,MAAA,IAAA,CAAK,aAAA,EAAc;AAAA,IACrB,CAAC,CAAA;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKQ,kBAAA,GAA2B;AACjC,IAAA,IAAI,OAAO,MAAA,KAAW,WAAA,IAAe,OAAO,aAAa,WAAA,EAAa;AAEtE,IAAA,QAAA,CAAS,gBAAA;AAAA,MACP,OAAA;AAAA,MACA,CAAA,KAAA,KAAS;AACP,QAAA,MAAM,SAAS,KAAA,CAAM,MAAA;AACrB,QAAA,IAAI,MAAA,EAAQ;AACV,UAAA,IAAA,CAAK,WAAW,MAAM,CAAA;AAAA,QACxB;AAAA,MACF,CAAA;AAAA,MACA;AAAA;AAAA,KACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,OAAA,GAAgB;AAEd,IAAA,IAAI,KAAK,UAAA,EAAY;AACnB,MAAA,YAAA,CAAa,KAAK,UAAU,CAAA;AAC5B,MAAA,IAAA,CAAK,UAAA,GAAa,IAAA;AAAA,IACpB;AAGA,IAAA,IAAA,CAAK,iBAAA,CAAkB,OAAA,CAAQ,CAAA,QAAA,KAAY,QAAA,CAAS,YAAY,CAAA;AAChE,IAAA,IAAA,CAAK,kBAAkB,KAAA,EAAM;AAG7B,IAAA,IAAA,CAAK,cAAA,CAAe,OAAA,CAAQ,CAAA,KAAA,KAAS,YAAA,CAAa,KAAK,CAAC,CAAA;AACxD,IAAA,IAAA,CAAK,eAAe,KAAA,EAAM;AAG1B,IAAA,IAAA,CAAK,KAAA,EAAM,CAAE,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA;AAAA,EAClC;AACF;AAKO,SAAS,cAAc,OAAA,EAAkC;AAC9D,EAAA,OAAO,IAAI,QAAQ,OAAO,CAAA;AAC5B;AAKA,IAAI,cAAA,GAAiC,IAAA;AAK9B,SAAS,YAAY,OAAA,EAAkC;AAC5D,EAAA,IAAI,cAAA,EAAgB;AAClB,IAAA,OAAA,CAAQ,KAAK,+CAA+C,CAAA;AAC5D,IAAA,OAAO,cAAA;AAAA,EACT;AACA,EAAA,cAAA,GAAiB,IAAI,QAAQ,OAAO,CAAA;AACpC,EAAA,OAAO,cAAA;AACT;AAKO,SAAS,UAAA,GAAsB;AACpC,EAAA,IAAI,CAAC,cAAA,EAAgB;AACnB,IAAA,MAAM,IAAI,MAAM,oDAAoD,CAAA;AAAA,EACtE;AACA,EAAA,OAAO,cAAA;AACT;AAKO,SAAS,UAAA,CAAW,MAAc,MAAA,EAAwC;AAC/E,EAAA,UAAA,EAAW,CAAE,UAAA,CAAW,IAAA,EAAM,MAAM,CAAA;AACtC;AAKO,SAAS,cAAc,MAAA,EAAwC;AACpE,EAAA,UAAA,EAAW,CAAE,cAAc,MAAM,CAAA;AACnC;AAKO,SAAS,UAAA,CAAW,SAA2B,MAAA,EAAwC;AAC5F,EAAA,UAAA,EAAW,CAAE,UAAA,CAAW,OAAA,EAAS,MAAM,CAAA;AACzC;AAKO,SAAS,aAAA,CACd,OAAA,EACA,OAAA,EACA,MAAA,EACM;AACN,EAAA,UAAA,EAAW,CAAE,aAAA,CAAc,OAAA,EAAS,OAAA,EAAS,MAAM,CAAA;AACrD;AAKO,SAAS,YAAY,QAAA,EAAyC;AACnE,EAAA,UAAA,EAAW,CAAE,YAAY,QAAQ,CAAA;AACnC;AAKO,SAAS,gBAAgB,MAAA,EAAuC;AACrE,EAAA,UAAA,EAAW,CAAE,gBAAgB,MAAM,CAAA;AACrC;AAKA,eAAsB,KAAA,GAAuB;AAC3C,EAAA,MAAM,UAAA,GAAa,KAAA,EAAM;AAC3B","file":"chunk-56W6YECK.js","sourcesContent":["/**\n * 埋点追踪工具\n */\n\n/**\n * 埋点事件类型\n */\nexport type TrackingEventType = 'event' | 'pageview' | 'click' | 'exposure' | 'custom';\n\n/**\n * 埋点事件数据\n */\nexport interface TrackingEvent {\n /** 事件类型 */\n type: TrackingEventType;\n /** 事件名称 */\n name: string;\n /** 事件参数 */\n params?: Record<string, unknown>;\n /** 时间戳 */\n timestamp: number;\n /** 页面URL */\n url?: string;\n /** 用户代理 */\n userAgent?: string;\n /** 用户ID */\n userId?: string;\n /** 会话ID */\n sessionId?: string;\n /** 其他自定义字段 */\n [key: string]: unknown;\n}\n\n/**\n * 埋点配置选项\n */\nexport interface TrackerOptions {\n /** 上报接口URL */\n endpoint: string;\n /** 批量上报大小,默认10 */\n batchSize?: number;\n /** 批量上报延迟(毫秒),默认3000 */\n batchDelay?: number;\n /** 是否自动追踪页面浏览,默认true */\n autoTrackPageView?: boolean;\n /** 是否自动追踪点击事件,默认false */\n autoTrackClick?: boolean;\n /** 公共参数 */\n commonParams?: Record<string, unknown>;\n /** 自定义上报函数 */\n customSend?: (events: TrackingEvent[]) => Promise<void>;\n /** 是否启用调试模式,默认false */\n debug?: boolean;\n}\n\n/**\n * 曝光追踪选项\n */\nexport interface ExposureOptions {\n /** 曝光阈值(0-1),元素可见比例达到此值才触发,默认0.5 */\n threshold?: number;\n /** 曝光时长(毫秒),元素可见时长达到此值才触发,默认1000 */\n duration?: number;\n /** 是否只触发一次,默认true */\n once?: boolean;\n}\n\n/**\n * 埋点追踪器类\n */\nexport class Tracker {\n private options: Required<\n Pick<\n TrackerOptions,\n 'batchSize' | 'batchDelay' | 'autoTrackPageView' | 'autoTrackClick' | 'debug'\n >\n > &\n Pick<TrackerOptions, 'endpoint' | 'commonParams' | 'customSend'>;\n private eventQueue: TrackingEvent[] = [];\n private batchTimer: ReturnType<typeof setTimeout> | null = null;\n private userInfo: Record<string, unknown> = {};\n private sessionId: string;\n private exposureObservers: Map<string, IntersectionObserver> = new Map();\n private exposureTimers: Map<string, ReturnType<typeof setTimeout>> = new Map();\n private exposedElements: Set<Element> = new Set();\n\n constructor(options: TrackerOptions) {\n if (!options.endpoint && !options.customSend) {\n throw new Error('Either endpoint or customSend must be provided');\n }\n\n this.options = {\n endpoint: options.endpoint || '',\n batchSize: options.batchSize ?? 10,\n batchDelay: options.batchDelay ?? 3000,\n autoTrackPageView: options.autoTrackPageView ?? true,\n autoTrackClick: options.autoTrackClick ?? false,\n commonParams: options.commonParams || {},\n customSend: options.customSend,\n debug: options.debug ?? false,\n };\n\n // 生成会话ID\n this.sessionId = this.generateSessionId();\n\n // 自动追踪页面浏览\n if (this.options.autoTrackPageView && typeof window !== 'undefined') {\n this.trackPageView();\n // 监听路由变化(SPA应用)\n this.setupPageViewListener();\n }\n\n // 自动追踪点击事件\n if (this.options.autoTrackClick && typeof window !== 'undefined') {\n this.setupClickListener();\n }\n }\n\n /**\n * 生成会话ID\n */\n private generateSessionId(): string {\n return `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;\n }\n\n /**\n * 获取公共数据\n */\n private getCommonData(): Pick<TrackingEvent, 'timestamp' | 'sessionId'> &\n Partial<Omit<TrackingEvent, 'timestamp' | 'sessionId'>> {\n const data: Pick<TrackingEvent, 'timestamp' | 'sessionId'> &\n Partial<Omit<TrackingEvent, 'timestamp' | 'sessionId'>> = {\n timestamp: Date.now(),\n sessionId: this.sessionId,\n ...this.options.commonParams,\n ...this.userInfo,\n };\n\n if (typeof window !== 'undefined') {\n data.url = window.location.href;\n data.userAgent = navigator.userAgent;\n }\n\n return data;\n }\n\n /**\n * 添加事件到队列\n */\n private enqueue(event: TrackingEvent): void {\n this.eventQueue.push(event);\n\n if (this.options.debug) {\n console.log('[Tracker] Event enqueued:', event);\n }\n\n // 如果队列达到批量大小,立即上报\n if (this.eventQueue.length >= this.options.batchSize) {\n this.flush();\n } else {\n // 否则设置延迟上报\n this.scheduleBatch();\n }\n }\n\n /**\n * 安排批量上报\n */\n private scheduleBatch(): void {\n if (this.batchTimer) {\n clearTimeout(this.batchTimer);\n }\n\n this.batchTimer = setTimeout(() => {\n this.flush();\n }, this.options.batchDelay);\n }\n\n /**\n * 上报事件\n */\n private async sendEvents(events: TrackingEvent[]): Promise<void> {\n if (events.length === 0) return;\n\n try {\n if (this.options.customSend) {\n await this.options.customSend(events);\n } else if (this.options.endpoint) {\n await this.sendToEndpoint(events);\n }\n\n if (this.options.debug) {\n console.log('[Tracker] Events sent:', events);\n }\n } catch (error) {\n console.error('[Tracker] Failed to send events:', error);\n // 失败后重新加入队列(可选:限制重试次数)\n this.eventQueue.unshift(...events);\n }\n }\n\n /**\n * 发送到上报接口\n */\n private async sendToEndpoint(events: TrackingEvent[]): Promise<void> {\n const response = await fetch(this.options.endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ events }),\n });\n\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n }\n\n /**\n * 立即上报所有待上报事件\n */\n async flush(): Promise<void> {\n if (this.batchTimer) {\n clearTimeout(this.batchTimer);\n this.batchTimer = null;\n }\n\n if (this.eventQueue.length === 0) return;\n\n const events = [...this.eventQueue];\n this.eventQueue = [];\n\n await this.sendEvents(events);\n }\n\n /**\n * 追踪事件\n */\n trackEvent(name: string, params?: Record<string, unknown>): void {\n const event: TrackingEvent = {\n type: 'event',\n name,\n params,\n ...this.getCommonData(),\n };\n\n this.enqueue(event);\n }\n\n /**\n * 追踪页面浏览\n */\n trackPageView(params?: Record<string, unknown>): void {\n const event: TrackingEvent = {\n type: 'pageview',\n name: 'pageview',\n params: {\n path: typeof window !== 'undefined' ? window.location.pathname : undefined,\n search: typeof window !== 'undefined' ? window.location.search : undefined,\n hash: typeof window !== 'undefined' ? window.location.hash : undefined,\n ...params,\n },\n ...this.getCommonData(),\n };\n\n this.enqueue(event);\n }\n\n /**\n * 追踪点击事件\n */\n trackClick(element: Element | string, params?: Record<string, unknown>): void {\n const el = typeof element === 'string' ? document.querySelector(element) : element;\n if (!el) {\n if (this.options.debug) {\n console.warn('[Tracker] Element not found:', element);\n }\n return;\n }\n\n const event: TrackingEvent = {\n type: 'click',\n name: 'click',\n params: {\n element: el.tagName.toLowerCase(),\n id: el.id || undefined,\n className: el.className || undefined,\n text: el.textContent?.trim().substring(0, 100) || undefined,\n ...params,\n },\n ...this.getCommonData(),\n };\n\n this.enqueue(event);\n }\n\n /**\n * 追踪曝光事件\n */\n trackExposure(\n element: Element | string,\n options?: ExposureOptions,\n params?: Record<string, unknown>\n ): void {\n if (typeof window === 'undefined' || !window.IntersectionObserver) {\n if (this.options.debug) {\n console.warn('[Tracker] IntersectionObserver is not supported');\n }\n return;\n }\n\n const el = typeof element === 'string' ? document.querySelector(element) : element;\n if (!el) {\n if (this.options.debug) {\n console.warn('[Tracker] Element not found:', element);\n }\n return;\n }\n\n const { threshold = 0.5, duration = 1000, once = true } = options || {};\n\n const elementId = `${Date.now()}-${Math.random()}`;\n\n // 如果已经曝光过且只触发一次,直接返回\n if (once && this.exposedElements.has(el)) {\n return;\n }\n\n let exposureTimer: ReturnType<typeof setTimeout> | null = null;\n let startTime: number | null = null;\n\n const observer = new IntersectionObserver(\n entries => {\n entries.forEach(entry => {\n if (entry.isIntersecting && entry.intersectionRatio >= threshold) {\n // 开始计时\n if (!startTime) {\n startTime = Date.now();\n const currentStartTime = startTime;\n exposureTimer = setTimeout(() => {\n // 触发曝光事件\n const event: TrackingEvent = {\n type: 'exposure',\n name: 'exposure',\n params: {\n element: el.tagName.toLowerCase(),\n id: el.id || undefined,\n className: el.className || undefined,\n duration: currentStartTime ? Date.now() - currentStartTime : duration,\n ...params,\n },\n ...this.getCommonData(),\n };\n\n this.enqueue(event);\n this.exposedElements.add(el);\n\n // 清理\n observer.disconnect();\n this.exposureObservers.delete(elementId);\n if (exposureTimer) {\n clearTimeout(exposureTimer);\n this.exposureTimers.delete(elementId);\n }\n }, duration);\n this.exposureTimers.set(elementId, exposureTimer);\n }\n } else {\n // 不可见,重置计时\n if (exposureTimer) {\n clearTimeout(exposureTimer);\n this.exposureTimers.delete(elementId);\n }\n startTime = null;\n }\n });\n },\n {\n threshold: [threshold],\n }\n );\n\n observer.observe(el);\n this.exposureObservers.set(elementId, observer);\n }\n\n /**\n * 设置用户信息\n */\n setUserInfo(userInfo: Record<string, unknown>): void {\n this.userInfo = { ...this.userInfo, ...userInfo };\n }\n\n /**\n * 设置公共参数\n */\n setCommonParams(params: Record<string, unknown>): void {\n this.options.commonParams = { ...this.options.commonParams, ...params };\n }\n\n /**\n * 设置页面浏览监听器(SPA应用)\n */\n private setupPageViewListener(): void {\n if (typeof window === 'undefined') return;\n\n // 监听popstate事件(浏览器前进后退)\n window.addEventListener('popstate', () => {\n this.trackPageView();\n });\n\n // 监听pushState和replaceState(需要重写history方法)\n const originalPushState = history.pushState;\n const originalReplaceState = history.replaceState;\n\n history.pushState = function (...args) {\n originalPushState.apply(history, args);\n window.dispatchEvent(new Event('pushstate'));\n };\n\n history.replaceState = function (...args) {\n originalReplaceState.apply(history, args);\n window.dispatchEvent(new Event('replacestate'));\n };\n\n window.addEventListener('pushstate', () => {\n this.trackPageView();\n });\n\n window.addEventListener('replacestate', () => {\n this.trackPageView();\n });\n }\n\n /**\n * 设置点击事件监听器\n */\n private setupClickListener(): void {\n if (typeof window === 'undefined' || typeof document === 'undefined') return;\n\n document.addEventListener(\n 'click',\n event => {\n const target = event.target as Element;\n if (target) {\n this.trackClick(target);\n }\n },\n true // 使用捕获阶段\n );\n }\n\n /**\n * 清理资源\n */\n destroy(): void {\n // 清理定时器\n if (this.batchTimer) {\n clearTimeout(this.batchTimer);\n this.batchTimer = null;\n }\n\n // 清理曝光观察器\n this.exposureObservers.forEach(observer => observer.disconnect());\n this.exposureObservers.clear();\n\n // 清理曝光定时器\n this.exposureTimers.forEach(timer => clearTimeout(timer));\n this.exposureTimers.clear();\n\n // 上报剩余事件\n this.flush().catch(console.error);\n }\n}\n\n/**\n * 创建埋点追踪器实例\n */\nexport function createTracker(options: TrackerOptions): Tracker {\n return new Tracker(options);\n}\n\n/**\n * 默认追踪器实例(单例模式)\n */\nlet defaultTracker: Tracker | null = null;\n\n/**\n * 初始化默认追踪器\n */\nexport function initTracker(options: TrackerOptions): Tracker {\n if (defaultTracker) {\n console.warn('[Tracker] Default tracker already initialized');\n return defaultTracker;\n }\n defaultTracker = new Tracker(options);\n return defaultTracker;\n}\n\n/**\n * 获取默认追踪器\n */\nexport function getTracker(): Tracker {\n if (!defaultTracker) {\n throw new Error('Tracker not initialized. Call initTracker() first.');\n }\n return defaultTracker;\n}\n\n/**\n * 追踪事件(使用默认追踪器)\n */\nexport function trackEvent(name: string, params?: Record<string, unknown>): void {\n getTracker().trackEvent(name, params);\n}\n\n/**\n * 追踪页面浏览(使用默认追踪器)\n */\nexport function trackPageView(params?: Record<string, unknown>): void {\n getTracker().trackPageView(params);\n}\n\n/**\n * 追踪点击事件(使用默认追踪器)\n */\nexport function trackClick(element: Element | string, params?: Record<string, unknown>): void {\n getTracker().trackClick(element, params);\n}\n\n/**\n * 追踪曝光事件(使用默认追踪器)\n */\nexport function trackExposure(\n element: Element | string,\n options?: ExposureOptions,\n params?: Record<string, unknown>\n): void {\n getTracker().trackExposure(element, options, params);\n}\n\n/**\n * 设置用户信息(使用默认追踪器)\n */\nexport function setUserInfo(userInfo: Record<string, unknown>): void {\n getTracker().setUserInfo(userInfo);\n}\n\n/**\n * 设置公共参数(使用默认追踪器)\n */\nexport function setCommonParams(params: Record<string, unknown>): void {\n getTracker().setCommonParams(params);\n}\n\n/**\n * 立即上报(使用默认追踪器)\n */\nexport async function flush(): Promise<void> {\n await getTracker().flush();\n}\n"]}
|