@file-ud.js/core 0.1.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utils/md5Worker.ts","../../src/utils/index.ts","../../src/utils/logger/index.ts","../../src/utils/fileCache.ts","../../src/utils/upload-monitor/index.ts","../../src/utils/network.ts"],"names":["LogLevel","record","calculateFileMD5InWorker","loadNext"],"mappings":";;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAuIA,SAAS,mBAA2B;AAClC,MAAI,CAAC,gBAAgB;AACnB,UAAM,OAAO,IAAI,KAAK,CAAC,QAAQ,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAC7D,qBAAiB,IAAI,gBAAgB,IAAI;AAAA,EAC3C;AACA,SAAO;AACT;AAUA,eAAsB,yBACpB,MACA,QACiB;AACjB,QAAM,oBAAoB;AAE1B,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AAEtC,UAAM,SAAS,IAAI,WAAW;AAE9B,UAAM,eAAe,MAAM;AACzB,aAAO,MAAM;AACb,aAAO,IAAI,MAAM,oCAAW,CAAC;AAAA,IAC/B;AAEA,QAAI,QAAQ;AACV,aAAO,iBAAiB,SAAS,cAAc,EAAE,MAAM,KAAK,CAAC;AAAA,IAC/D;AAEA,WAAO,SAAS,MAAM;AACpB,UAAI,QAAQ;AACV,eAAO,oBAAoB,SAAS,YAAY;AAAA,MAClD;AAEA,YAAM,aAAa,OAAO;AAG1B,YAAM,YAAY,iBAAiB;AACnC,YAAM,SAAS,IAAI,OAAO,SAAS;AAEnC,YAAM,UAAU,WAAW,MAAM;AAC/B,eAAO,UAAU;AACjB,eAAO,IAAI,MAAM,qCAAiB,CAAC;AAAA,MACrC,GAAG,iBAAiB;AAEpB,aAAO,YAAY,CAAC,MAAmE;AACrF,qBAAa,OAAO;AACpB,eAAO,UAAU;AACjB,cAAM,EAAE,OAAO,KAAK,IAAI,EAAE;AAC1B,YAAI,OAAO;AACT,iBAAO,IAAI,MAAM,KAAK,CAAC;AAAA,QACzB,OAAO;AACL,kBAAQ,IAAK;AAAA,QACf;AAAA,MACF;AAEA,aAAO,UAAU,CAAC,QAAQ;AACxB,qBAAa,OAAO;AACpB,eAAO,UAAU;AACjB,eAAO,IAAI,MAAM,IAAI,WAAW,yBAAe,CAAC;AAAA,MAClD;AAGA,aAAO,YAAY,EAAE,WAAW,GAAG,CAAC,UAAU,CAAC;AAAA,IACjD;AAEA,WAAO,UAAU,MAAM;AACrB,UAAI,QAAQ;AACV,eAAO,oBAAoB,SAAS,YAAY;AAAA,MAClD;AACA,aAAO,IAAI,MAAM,sCAAQ,CAAC;AAAA,IAC5B;AAGA,WAAO,kBAAkB,IAAI;AAAA,EAC/B,CAAC;AACH;AAKO,SAAS,mBAAyB;AACvC,MAAI,gBAAgB;AAClB,QAAI,gBAAgB,cAAc;AAClC,qBAAiB;AAAA,EACnB;AACF;AAnOA,IAWM,UA0HF;AArIJ;AAAA;AAAA;AAWA,IAAM,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA0HjB,IAAI,iBAAgC;AAAA;AAAA;;;AC9HpC,OAAO,cAAc;;;ACqBd,IAAK,WAAL,kBAAKA,cAAL;AACL,EAAAA,oBAAA,WAAQ,KAAR;AACA,EAAAA,oBAAA,UAAO,KAAP;AACA,EAAAA,oBAAA,UAAO,KAAP;AACA,EAAAA,oBAAA,WAAQ,KAAR;AAJU,SAAAA;AAAA,GAAA;AA+CZ,IAAM,eAAyC;AAAA,EAC7C,CAAC,aAAc,GAAG;AAAA,EAClB,CAAC,YAAa,GAAG;AAAA,EACjB,CAAC,YAAa,GAAG;AAAA,EACjB,CAAC,aAAc,GAAG;AACpB;AAKA,IAAM,eAAyC;AAAA,EAC7C,CAAC,aAAc,GAAG;AAAA;AAAA,EAClB,CAAC,YAAa,GAAG;AAAA;AAAA,EACjB,CAAC,YAAa,GAAG;AAAA;AAAA,EACjB,CAAC,aAAc,GAAG;AAAA;AACpB;AAEA,IAAM,cAAc;AAKpB,IAAM,kBAAiC;AAAA,EACrC,SAAS;AAAA,EACT,OAAO,OAAO,YAAY,eAAe,QAAQ,KAAK,aAAa,eAC/D,eACA;AAAA,EACJ,eAAe;AAAA,EACf,cAAc,OAAO,YAAY,eAAe,QAAQ,KAAK,aAAa;AAC5E;AAKA,IAAI,iBAAgC,EAAE,GAAG,gBAAgB;AAKzD,IAAM,gBAAwC,CAAC;AAK/C,SAAS,qBAA2C;AAClD,MAAI,OAAO,YAAY,eAAe,CAAC,QAAQ;AAAK,WAAO;AAE3D,QAAM,WAAW,QAAQ,IAAI,kBAAkB,QAAQ,IAAI;AAC3D,MAAI,CAAC;AAAU,WAAO;AAEtB,QAAM,WAAqC;AAAA,IACzC,OAAO;AAAA,IACP,MAAM;AAAA,IACN,MAAM;AAAA,IACN,OAAO;AAAA,EACT;AAEA,SAAO,SAAS,SAAS,YAAY,CAAC;AACxC;AAMO,SAAS,WAAW,SAA+B;AACxD,QAAM,WAAW,mBAAmB;AAEpC,mBAAiB;AAAA,IACf,GAAG;AAAA,IACH,GAAI,WAAW,CAAC;AAAA;AAAA,IAEhB,GAAI,aAAa,SAAY,EAAE,OAAO,SAAS,IAAI,CAAC;AAAA,EACtD;AAEA,UAAQ,IAAI,oCAAoC,aAAa,eAAe,KAAM,CAAC,EAAE;AACvF;AAMO,SAAS,YAAY,OAAuB;AACjD,iBAAe,QAAQ;AACzB;AAqBO,SAAS,gBAAgB,UAA4C;AAC1E,gBAAc,KAAK,QAAQ;AAG3B,SAAO,MAAM;AACX,UAAM,QAAQ,cAAc,QAAQ,QAAQ;AAC5C,QAAI,QAAQ,IAAI;AACd,oBAAc,OAAO,OAAO,CAAC;AAAA,IAC/B;AAAA,EACF;AACF;AAKO,SAAS,qBAA2B;AACzC,gBAAc,SAAS;AACzB;AASA,SAAS,cACP,OACA,QACA,SACA,MACQ;AACR,QAAM,QAAkB,CAAC;AAGzB,MAAI,eAAe,eAAe;AAChC,UAAM,MAAM,oBAAI,KAAK;AAErB,UAAM,OAAO,IAAI,YAAY;AAC7B,UAAM,QAAQ,OAAO,IAAI,SAAS,IAAI,CAAC,EAAE,SAAS,GAAG,GAAG;AACxD,UAAM,MAAM,OAAO,IAAI,QAAQ,CAAC,EAAE,SAAS,GAAG,GAAG;AACjD,UAAM,QAAQ,OAAO,IAAI,SAAS,CAAC,EAAE,SAAS,GAAG,GAAG;AACpD,UAAM,UAAU,OAAO,IAAI,WAAW,CAAC,EAAE,SAAS,GAAG,GAAG;AACxD,UAAM,UAAU,OAAO,IAAI,WAAW,CAAC,EAAE,SAAS,GAAG,GAAG;AACxD,UAAM,eAAe,OAAO,IAAI,gBAAgB,CAAC,EAAE,SAAS,GAAG,GAAG;AAClE,UAAM,UAAU,GAAG,IAAI,IAAI,KAAK,IAAI,GAAG,IAAI,KAAK,IAAI,OAAO,IAAI,OAAO,IAAI,YAAY;AACtF,UAAM,KAAK,IAAI,OAAO,GAAG;AAAA,EAC3B;AAGA,QAAM,QAAQ,aAAa,KAAK;AAChC,MAAI,eAAe,cAAc;AAC/B,UAAM,KAAK,GAAG,aAAa,KAAK,CAAC,IAAI,KAAK,IAAI,WAAW,EAAE;AAAA,EAC7D,OAAO;AACL,UAAM,KAAK,IAAI,KAAK,GAAG;AAAA,EACzB;AAGA,QAAM,KAAK,IAAI,MAAM,GAAG;AAGxB,QAAM,KAAK,OAAO;AAElB,SAAO,MAAM,KAAK,GAAG;AACvB;AAOA,SAAS,aAAa,KAAyB;AAC7C,MAAI,eAAe,OAAO;AACxB,WAAO,IAAI,SAAS,IAAI;AAAA,EAC1B;AACA,SAAO;AACT;AASA,SAAS,IAAI,OAAiB,QAAgB,YAAoB,MAAmB;AAEnF,MAAI,CAAC,eAAe,SAAS;AAC3B;AAAA,EACF;AAGA,MAAI,SAAS,eAAe,SAAS,gBAAiB;AACpD;AAAA,EACF;AAEA,QAAM,mBAAmB,cAAc,OAAO,QAAQ,SAAS,IAAI;AAInE,MAAI;AACJ,MAAI;AACF,UAAM,QAAQ,IAAI,MAAM;AACxB,UAAM,aAAa,MAAM,OAAO,MAAM,IAAI,KAAK,CAAC;AAOhD,QAAI,WAAW,UAAU,GAAG;AAC1B,kBAAY,WAAW,CAAC,EAAE,KAAK;AAAA,IACjC;AAAA,EACF,SAAS,GAAG;AAAA,EAEZ;AAIA,QAAM,aAAa,YAAY,CAAC,GAAG,MAAM;AAAA,MAAS,SAAS,EAAE,IAAI;AAGjE,UAAQ,OAAO;AAAA,IACb,KAAK;AACH,cAAQ,MAAM,kBAAkB,GAAG,UAAU;AAC7C;AAAA,IACF,KAAK;AACH,cAAQ,KAAK,kBAAkB,GAAG,UAAU;AAC5C;AAAA,IACF,KAAK;AACH,cAAQ,KAAK,kBAAkB,GAAG,UAAU;AAC5C;AAAA,IACF,KAAK;AACH,cAAQ,MAAM,kBAAkB,GAAG,UAAU;AAC7C;AAAA,EACJ;AAGA,MAAI,cAAc,SAAS,GAAG;AAC5B,UAAM,QAAkB;AAAA,MACtB,WAAW,KAAK,IAAI;AAAA,MACpB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAGA,UAAM,QAAQ,KAAK,KAAK,YAAY;AACpC,QAAI,OAAO;AACT,YAAM,QAAQ,aAAa,KAAK,KAAK;AAAA,IACvC;AAGA,kBAAc,QAAQ,CAAC,cAAc;AACnC,UAAI;AACF,cAAM,SAAS,UAAU,KAAK;AAE9B,YAAI,kBAAkB,SAAS;AAC7B,iBAAO,MAAM,CAAC,UAAU;AACtB,oBAAQ,MAAM,6BAA6B,KAAK;AAAA,UAClD,CAAC;AAAA,QACH;AAAA,MACF,SAAS,OAAO;AACd,gBAAQ,MAAM,6BAA6B,KAAK;AAAA,MAClD;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAKO,IAAM,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOpB,OAAO,CAAC,QAAgB,YAAoB,SAAgB;AAC1D,cAAU,aAAM,OAAO;AACvB,QAAI,eAAgB,QAAQ,SAAS,GAAG,IAAI;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,CAAC,QAAgB,YAAoB,SAAgB;AACzD,cAAU,UAAK,OAAO;AACtB,QAAI,cAAe,QAAQ,SAAS,GAAG,IAAI;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,CAAC,QAAgB,YAAoB,SAAgB;AACzD,cAAU,gBAAM,OAAO;AACvB,QAAI,cAAe,QAAQ,SAAS,GAAG,IAAI;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,OAAO,CAAC,QAAgB,YAAoB,SAAgB;AAC1D,cAAU,UAAK,OAAO;AACtB,QAAI,eAAgB,QAAQ,SAAS,GAAG,IAAI;AAAA,EAC9C;AACF;;;ACpYA,IAAM,UAAU;AAChB,IAAM,aAAa;AACnB,IAAM,aAAa;AACnB,IAAM,0BAA0B;AAChC,IAAM,oBAAoB;AAgB1B,IAAI,aAAiC;AACrC,IAAI,mBAAgD;AAKpD,SAAS,SAA+B;AAEtC,MAAI,cAAc,WAAW,YAAY,YAAY;AACnD,QAAI;AAEF,iBAAW,YAAY,CAAC,UAAU,GAAG,UAAU;AAC/C,aAAO,QAAQ,QAAQ,UAAU;AAAA,IACnC,SAAS,OAAO;AAEd,aAAO,KAAK,aAAa,kFAAiB,KAAK;AAC/C,mBAAa;AACb,yBAAmB;AAAA,IACrB;AAAA,EACF;AAGA,MAAI,kBAAkB;AACpB,WAAO;AAAA,EACT;AAGA,qBAAmB,IAAI,QAAQ,CAAC,SAAS,WAAW;AAClD,UAAM,UAAU,UAAU,KAAK,SAAS,UAAU;AAElD,YAAQ,UAAU,MAAM;AACtB,aAAO,MAAM,aAAa,uCAAmB,QAAQ,KAAK;AAC1D,yBAAmB;AACnB,aAAO,QAAQ,KAAK;AAAA,IACtB;AAEA,YAAQ,YAAY,MAAM;AACxB,mBAAa,QAAQ;AACrB,yBAAmB;AAGnB,iBAAW,UAAU,MAAM;AACzB,eAAO,KAAK,aAAa,kDAAU;AACnC,qBAAa;AAAA,MACf;AAGA,iBAAW,UAAU,CAAC,UAAU;AAC9B,eAAO,MAAM,aAAa,8CAAW,KAAK;AAC1C,qBAAa;AAAA,MACf;AAEA,cAAQ,UAAU;AAAA,IACpB;AAEA,YAAQ,kBAAkB,CAAC,UAAU;AACnC,YAAM,KAAM,MAAM,OAA4B;AAG9C,UAAI,CAAC,GAAG,iBAAiB,SAAS,UAAU,GAAG;AAC7C,cAAM,QAAQ,GAAG,kBAAkB,YAAY,EAAE,SAAS,WAAW,CAAC;AAGtE,cAAM,YAAY,YAAY,YAAY,EAAE,QAAQ,MAAM,CAAC;AAC3D,cAAM,YAAY,aAAa,aAAa,EAAE,QAAQ,MAAM,CAAC;AAC7D,cAAM,YAAY,kBAAkB,kBAAkB,EAAE,QAAQ,MAAM,CAAC;AAEvE,eAAO,KAAK,aAAa,8EAAuB;AAAA,MAClD;AAGA,UAAI,CAAC,GAAG,iBAAiB,SAAS,uBAAuB,GAAG;AAC1D,cAAM,gBAAgB,GAAG,kBAAkB,yBAAyB,EAAE,SAAS,WAAW,CAAC;AAC3F,sBAAc,YAAY,aAAa,aAAa,EAAE,QAAQ,MAAM,CAAC;AAErE,eAAO,KAAK,aAAa,8EAAuB;AAAA,MAClD;AAGA,UAAI,CAAC,GAAG,iBAAiB,SAAS,iBAAiB,GAAG;AACpD,cAAM,cAAc,GAAG,kBAAkB,mBAAmB,EAAE,SAAS,WAAW,CAAC;AACnF,oBAAY,YAAY,aAAa,aAAa,EAAE,QAAQ,MAAM,CAAC;AAEnE,eAAO,KAAK,aAAa,8EAAuB;AAAA,MAClD;AAAA,IACF;AAAA,EACF,CAAC;AAED,SAAO;AACT;AAKO,SAAS,UAAgB;AAC9B,MAAI,YAAY;AACd,QAAI;AACF,iBAAW,MAAM;AACjB,aAAO,MAAM,aAAa,kDAAU;AAAA,IACtC,SAAS,OAAO;AACd,aAAO,KAAK,aAAa,gEAAc,KAAK;AAAA,IAC9C,UAAE;AACA,mBAAa;AACb,yBAAmB;AAAA,IACrB;AAAA,EACF;AACF;AAOA,eAAe,mBACb,WACY;AACZ,MAAI;AACF,UAAM,KAAK,MAAM,OAAO;AACxB,WAAO,MAAM,UAAU,EAAE;AAAA,EAC3B,SAAS,OAAO;AAEd,QAAI,iBAAiB,gBAAgB,MAAM,SAAS,qBAAqB;AACvE,aAAO,KAAK,aAAa,0GAAqB,KAAK;AACnD,cAAQ;AAGR,YAAM,KAAK,MAAM,OAAO;AACxB,aAAO,MAAM,UAAU,EAAE;AAAA,IAC3B;AACA,UAAM;AAAA,EACR;AACF;AASA,eAAsB,gBAAgB,UAAkB,MAA2B;AACjF,MAAI;AAEF,UAAM,cAAc,MAAM,KAAK,YAAY;AAE3C,UAAM,SAA0B;AAAA,MAC9B;AAAA,MACA,UAAU,KAAK;AAAA,MACf,UAAU,KAAK;AAAA,MACf,UAAU,KAAK;AAAA,MACf,MAAM;AAAA,MACN,WAAW,KAAK,IAAI;AAAA,MACpB,gBAAgB,KAAK,IAAI;AAAA,IAC3B;AAEA,UAAM,mBAAmB,OAAO,OAAO;AACrC,aAAO,IAAI,QAAc,CAAC,SAAS,WAAW;AAC5C,cAAM,cAAc,GAAG,YAAY,CAAC,UAAU,GAAG,WAAW;AAC5D,cAAM,QAAQ,YAAY,YAAY,UAAU;AAChD,cAAM,UAAU,MAAM,IAAI,MAAM;AAEhC,gBAAQ,YAAY,MAAM;AACxB,iBAAO,MAAM,aAAa,mCAAU,KAAK,IAAI,KAAK,eAAe,KAAK,IAAI,CAAC,KAAK;AAAA,YAC9E;AAAA,YACA,UAAU,KAAK;AAAA,UACjB,CAAC;AACD,kBAAQ;AAAA,QACV;AAEA,gBAAQ,UAAU,MAAM;AACtB,iBAAO,MAAM,aAAa,oDAAY,QAAQ,KAAK;AACnD,iBAAO,QAAQ,KAAK;AAAA,QACtB;AAEA,oBAAY,UAAU,MAAM;AAC1B,iBAAO,MAAM,aAAa,wCAAU,YAAY,KAAK;AACrD,iBAAO,YAAY,KAAK;AAAA,QAC1B;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH,SAAS,OAAO;AACd,WAAO,MAAM,aAAa,oDAAY,KAAK;AAC3C,UAAM;AAAA,EACR;AACF;AAQA,eAAsB,qBAAqB,UAAwC;AACjF,MAAI;AACF,WAAO,MAAM,mBAAmB,OAAO,OAAO;AAC5C,aAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,cAAM,cAAc,GAAG,YAAY,CAAC,UAAU,GAAG,UAAU;AAC3D,cAAM,QAAQ,YAAY,YAAY,UAAU;AAChD,cAAM,UAAU,MAAM,IAAI,QAAQ;AAElC,gBAAQ,YAAY,MAAM;AACxB,gBAAM,SAAsC,QAAQ;AAEpD,cAAI,CAAC,QAAQ;AACX,mBAAO,MAAM,aAAa,+CAAY,QAAQ,EAAE;AAChD,oBAAQ,IAAI;AACZ;AAAA,UACF;AAGA,iCAAuB,QAAQ;AAG/B,gBAAM,OAAO,IAAI,KAAK,CAAC,OAAO,IAAI,GAAG,EAAE,MAAM,OAAO,SAAS,CAAC;AAC9D,gBAAM,OAAO,IAAI,KAAK,CAAC,IAAI,GAAG,OAAO,UAAU,EAAE,MAAM,OAAO,SAAS,CAAC;AAExE,iBAAO,MAAM,aAAa,qDAAa,OAAO,QAAQ,IAAI;AAAA,YACxD;AAAA,YACA,UAAU,OAAO;AAAA,UACnB,CAAC;AAED,kBAAQ,IAAI;AAAA,QACd;AAEA,gBAAQ,UAAU,MAAM;AACtB,iBAAO,MAAM,aAAa,oDAAY,QAAQ,KAAK;AACnD,iBAAO,QAAQ,KAAK;AAAA,QACtB;AAEA,oBAAY,UAAU,MAAM;AAC1B,iBAAO,MAAM,aAAa,wCAAU,YAAY,KAAK;AACrD,iBAAO,YAAY,KAAK;AAAA,QAC1B;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH,SAAS,OAAO;AACd,WAAO,MAAM,aAAa,oDAAY,KAAK;AAC3C,UAAM;AAAA,EACR;AACF;AAQA,eAAsB,oBAAoB,UAAiC;AACzE,MAAI;AACF,UAAM,mBAAmB,OAAO,OAAO;AACrC,aAAO,IAAI,QAAc,CAAC,SAAS,WAAW;AAC5C,cAAM,cAAc,GAAG,YAAY,CAAC,UAAU,GAAG,WAAW;AAC5D,cAAM,QAAQ,YAAY,YAAY,UAAU;AAChD,cAAM,UAAU,MAAM,OAAO,QAAQ;AAErC,gBAAQ,YAAY,MAAM;AACxB,iBAAO,MAAM,aAAa,+CAAY,QAAQ,EAAE;AAChD,kBAAQ;AAAA,QACV;AAEA,gBAAQ,UAAU,MAAM;AACtB,iBAAO,MAAM,aAAa,oDAAY,QAAQ,KAAK;AACnD,iBAAO,QAAQ,KAAK;AAAA,QACtB;AAEA,oBAAY,UAAU,MAAM;AAC1B,iBAAO,MAAM,aAAa,wCAAU,YAAY,KAAK;AACrD,iBAAO,YAAY,KAAK;AAAA,QAC1B;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH,SAAS,OAAO;AACd,WAAO,MAAM,aAAa,oDAAY,KAAK;AAC3C,UAAM;AAAA,EACR;AACF;AAOA,eAAsB,oBAAmC;AACvD,MAAI;AACF,UAAM,mBAAmB,OAAO,OAAO;AACrC,aAAO,IAAI,QAAc,CAAC,SAAS,WAAW;AAC5C,cAAM,cAAc,GAAG,YAAY,CAAC,UAAU,GAAG,WAAW;AAC5D,cAAM,QAAQ,YAAY,YAAY,UAAU;AAChD,cAAM,UAAU,MAAM,MAAM;AAE5B,gBAAQ,YAAY,MAAM;AACxB,iBAAO,KAAK,aAAa,wDAAW;AACpC,kBAAQ;AAAA,QACV;AAEA,gBAAQ,UAAU,MAAM;AACtB,iBAAO,MAAM,aAAa,oDAAY,QAAQ,KAAK;AACnD,iBAAO,QAAQ,KAAK;AAAA,QACtB;AAEA,oBAAY,UAAU,MAAM;AAC1B,iBAAO,MAAM,aAAa,wCAAU,YAAY,KAAK;AACrD,iBAAO,YAAY,KAAK;AAAA,QAC1B;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH,SAAS,OAAO;AACd,WAAO,MAAM,aAAa,oDAAY,KAAK;AAC3C,UAAM;AAAA,EACR;AACF;AAOA,eAAsB,gBAA+D;AACnF,MAAI;AACF,WAAO,MAAM,mBAAmB,OAAO,OAAO;AAC5C,aAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,cAAM,cAAc,GAAG,YAAY,CAAC,UAAU,GAAG,UAAU;AAC3D,cAAM,QAAQ,YAAY,YAAY,UAAU;AAChD,cAAM,UAAU,MAAM,OAAO;AAE7B,gBAAQ,YAAY,MAAM;AACxB,gBAAM,UAA6B,QAAQ,UAAU,CAAC;AACtD,gBAAM,YAAY,QAAQ,OAAO,CAAC,KAAK,WAAW,MAAM,OAAO,UAAU,CAAC;AAE1E,kBAAQ;AAAA,YACN,OAAO,QAAQ;AAAA,YACf;AAAA,UACF,CAAC;AAAA,QACH;AAEA,gBAAQ,UAAU,MAAM;AACtB,iBAAO,MAAM,aAAa,oDAAY,QAAQ,KAAK;AACnD,iBAAO,QAAQ,KAAK;AAAA,QACtB;AAEA,oBAAY,UAAU,MAAM;AAC1B,iBAAO,MAAM,aAAa,wCAAU,YAAY,KAAK;AACrD,iBAAO,YAAY,KAAK;AAAA,QAC1B;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH,SAAS,OAAO;AACd,WAAO,MAAM,aAAa,oDAAY,KAAK;AAC3C,UAAM;AAAA,EACR;AACF;AAQA,eAAsB,kBAAkB,OAAe,GAAoB;AACzE,MAAI;AACF,UAAM,aAAa,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,KAAK;AAEtD,WAAO,MAAM,mBAAmB,OAAO,OAAO;AAC5C,aAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,cAAM,cAAc,GAAG,YAAY,CAAC,UAAU,GAAG,WAAW;AAC5D,cAAM,QAAQ,YAAY,YAAY,UAAU;AAChD,cAAM,QAAQ,MAAM,MAAM,gBAAgB;AAC1C,cAAM,UAAU,MAAM,OAAO;AAE7B,gBAAQ,YAAY,MAAM;AACxB,gBAAM,UAA6B,QAAQ,UAAU,CAAC;AACtD,gBAAM,iBAAiB,QAAQ;AAAA,YAC7B,CAAC,WAAW,OAAO,iBAAiB;AAAA,UACtC;AAEA,cAAI,eAAe;AAGnB,gBAAM,iBAAiB,eAAe,IAAI,CAAC,WAAW;AACpD,mBAAO,IAAI,QAAc,CAAC,eAAe,iBAAiB;AACxD,oBAAM,gBAAgB,MAAM,OAAO,OAAO,QAAQ;AAClD,4BAAc,YAAY,MAAM;AAC9B;AACA,8BAAc;AAAA,cAChB;AACA,4BAAc,UAAU,MAAM;AAC5B,uBAAO,MAAM,aAAa,qDAAa,OAAO,QAAQ,IAAI,cAAc,KAAK;AAC7E,8BAAc;AAAA,cAChB;AAAA,YACF,CAAC;AAAA,UACH,CAAC;AAED,kBAAQ,IAAI,cAAc,EAAE,KAAK,MAAM;AACrC,gBAAI,eAAe,GAAG;AACpB,qBAAO,KAAK,aAAa,sBAAO,YAAY,iCAAQ;AAAA,YACtD;AACA,oBAAQ,YAAY;AAAA,UACtB,CAAC;AAAA,QACH;AAEA,gBAAQ,UAAU,MAAM;AACtB,iBAAO,MAAM,aAAa,oDAAY,QAAQ,KAAK;AACnD,iBAAO,QAAQ,KAAK;AAAA,QACtB;AAEA,oBAAY,UAAU,MAAM;AAC1B,iBAAO,MAAM,aAAa,wCAAU,YAAY,KAAK;AACrD,iBAAO,YAAY,KAAK;AAAA,QAC1B;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH,SAAS,OAAO;AACd,WAAO,MAAM,aAAa,oDAAY,KAAK;AAC3C,UAAM;AAAA,EACR;AACF;AAOA,eAAe,uBAAuB,UAAiC;AACrE,MAAI;AACF,UAAM,mBAAmB,OAAO,OAAO;AACrC,aAAO,IAAI,QAAc,CAAC,SAAS,WAAW;AAC5C,cAAM,cAAc,GAAG,YAAY,CAAC,UAAU,GAAG,WAAW;AAC5D,cAAM,QAAQ,YAAY,YAAY,UAAU;AAChD,cAAM,UAAU,MAAM,IAAI,QAAQ;AAElC,gBAAQ,YAAY,MAAM;AACxB,gBAAM,SAAsC,QAAQ;AAEpD,cAAI,QAAQ;AACV,mBAAO,iBAAiB,KAAK,IAAI;AACjC,kBAAM,gBAAgB,MAAM,IAAI,MAAM;AAEtC,0BAAc,YAAY,MAAM,QAAQ;AACxC,0BAAc,UAAU,MAAM;AAC5B,qBAAO,KAAK,aAAa,oDAAY,cAAc,KAAK;AACxD,sBAAQ;AAAA,YACV;AAAA,UACF,OAAO;AACL,oBAAQ;AAAA,UACV;AAAA,QACF;AAEA,gBAAQ,UAAU,MAAM;AACtB,iBAAO,KAAK,aAAa,wCAAU,QAAQ,KAAK;AAChD,kBAAQ;AAAA,QACV;AAEA,oBAAY,UAAU,MAAM;AAC1B,iBAAO,KAAK,aAAa,wCAAU,YAAY,KAAK;AACpD,kBAAQ;AAAA,QACV;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH,SAAS,OAAO;AACd,WAAO,KAAK,aAAa,oDAAY,KAAK;AAAA,EAE5C;AACF;AAwKA,eAAsB,eACpB,UACA,YACe;AACf,MAAI;AACF,UAAM,SAA2B;AAAA,MAC/B;AAAA,MACA;AAAA,MACA,WAAW,KAAK,IAAI;AAAA,IACtB;AAEA,UAAM,mBAAmB,OAAO,OAAO;AACrC,aAAO,IAAI,QAAc,CAAC,SAAS,WAAW;AAC5C,cAAM,cAAc,GAAG,YAAY,CAAC,iBAAiB,GAAG,WAAW;AACnE,cAAM,QAAQ,YAAY,YAAY,iBAAiB;AACvD,cAAM,UAAU,MAAM,IAAI,MAAM;AAEhC,gBAAQ,YAAY,MAAM;AACxB,iBAAO,MAAM,aAAa,qDAAa,QAAQ,EAAE;AACjD,kBAAQ;AAAA,QACV;AAEA,gBAAQ,UAAU,MAAM;AACtB,iBAAO,KAAK,aAAa,oDAAY,QAAQ,KAAK;AAClD,iBAAO,QAAQ,KAAK;AAAA,QACtB;AAEA,oBAAY,UAAU,MAAM;AAC1B,iBAAO,KAAK,aAAa,wCAAU,YAAY,KAAK;AACpD,iBAAO,YAAY,KAAK;AAAA,QAC1B;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH,SAAS,OAAO;AACd,WAAO,KAAK,aAAa,oDAAY,KAAK;AAAA,EAC5C;AACF;AAMA,eAAsB,eACpB,UACsC;AACtC,MAAI;AACF,WAAO,MAAM,mBAAmB,OAAO,OAAO;AAC5C,aAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,cAAM,cAAc,GAAG,YAAY,CAAC,iBAAiB,GAAG,UAAU;AAClE,cAAM,QAAQ,YAAY,YAAY,iBAAiB;AACvD,cAAM,UAAU,MAAM,IAAI,QAAQ;AAElC,gBAAQ,YAAY,MAAM;AACxB,gBAAM,SAAuC,QAAQ;AAErD,cAAI,CAAC,QAAQ;AACX,mBAAO,MAAM,aAAa,iEAAe,QAAQ,EAAE;AACnD,oBAAQ,IAAI;AACZ;AAAA,UACF;AAGA,cAAI;AAEF,mBAAO,WAAW,QAAQ,EAAE;AAAA,cAC1B,MAAM;AACJ,uBAAO,MAAM,aAAa,yCAAW,QAAQ,EAAE;AAC/C,wBAAQ,OAAO,UAAU;AAAA,cAC3B;AAAA,cACA,MAAM;AACJ,uBAAO,KAAK,aAAa,mFAAkB,QAAQ,EAAE;AAErD,iCAAiB,QAAQ;AACzB,wBAAQ,IAAI;AAAA,cACd;AAAA,YACF;AAAA,UACF,QAAQ;AAEN,6BAAiB,QAAQ;AACzB,oBAAQ,IAAI;AAAA,UACd;AAAA,QACF;AAEA,gBAAQ,UAAU,MAAM;AACtB,iBAAO,KAAK,aAAa,oDAAY,QAAQ,KAAK;AAClD,iBAAO,QAAQ,KAAK;AAAA,QACtB;AAEA,oBAAY,UAAU,MAAM;AAC1B,iBAAO,KAAK,aAAa,wCAAU,YAAY,KAAK;AACpD,iBAAO,YAAY,KAAK;AAAA,QAC1B;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH,SAAS,OAAO;AACd,WAAO,KAAK,aAAa,oDAAY,KAAK;AAC1C,WAAO;AAAA,EACT;AACF;AAKA,eAAsB,iBAAiB,UAAiC;AACtE,MAAI;AACF,UAAM,mBAAmB,OAAO,OAAO;AACrC,aAAO,IAAI,QAAc,CAAC,SAAS,WAAW;AAC5C,cAAM,cAAc,GAAG,YAAY,CAAC,iBAAiB,GAAG,WAAW;AACnE,cAAM,QAAQ,YAAY,YAAY,iBAAiB;AACvD,cAAM,UAAU,MAAM,OAAO,QAAQ;AAErC,gBAAQ,YAAY,MAAM;AACxB,iBAAO,MAAM,aAAa,+CAAY,QAAQ,EAAE;AAChD,kBAAQ;AAAA,QACV;AAEA,gBAAQ,UAAU,MAAM;AACtB,iBAAO,KAAK,aAAa,oDAAY,QAAQ,KAAK;AAClD,iBAAO,QAAQ,KAAK;AAAA,QACtB;AAEA,oBAAY,UAAU,MAAM;AAC1B,iBAAO,KAAK,aAAa,wCAAU,YAAY,KAAK;AACpD,iBAAO,YAAY,KAAK;AAAA,QAC1B;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH,SAAS,OAAO;AACd,WAAO,KAAK,aAAa,oDAAY,KAAK;AAAA,EAC5C;AACF;;;ACnrBA,IAAM,gBAAN,MAAoB;AAAA,EAUlB,cAAc;AARd;AAAA,SAAQ,UAA0B,CAAC;AAGnC;AAAA,SAAQ,gBAAgB,oBAAI,IAA0B;AAGtD;AAAA,SAAiB,cAAc;AAG7B,SAAK,KAAK;AAAA,EACZ;AAAA;AAAA;AAAA;AAAA,EAKQ,OAAO;AACb,oBAAgB,CAAC,UAAU;AACzB,WAAK,WAAW,KAAK;AAAA,IACvB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKQ,WAAW,OAAiB;AAElC,QAAI,MAAM,QAAQ,SAAS,cAAI,KAAK,MAAM,QAAQ,SAAS,cAAI,GAAG;AAChE,cAAQ,IAAI,6CAAyB;AAAA,QACnC,QAAQ,MAAM;AAAA,QACd,SAAS,MAAM;AAAA,QACf,MAAM,MAAM;AAAA,QACZ,WAAW,MAAM;AAAA,MACnB,CAAC;AAAA,IACH;AAGA,QAAI,MAAM,QAAQ,SAAS,sCAAQ,KAAK,CAAC,MAAM,QAAQ,SAAS,cAAI,GAAG;AACrE,YAAM,SAAS,KAAK,cAAc,KAAK;AACvC,cAAQ,IAAI,+DAA4B,EAAE,QAAQ,WAAW,CAAC,CAAC,OAAO,CAAC;AAEvE,UAAI,UAAU,CAAC,KAAK,cAAc,IAAI,MAAM,GAAG;AAC7C,cAAM,SAAuB;AAAA,UAC3B;AAAA,UACA,UAAU,KAAK,gBAAgB,KAAK,KAAK;AAAA,UACzC,UAAU,KAAK,gBAAgB,KAAK,KAAK;AAAA,UACzC,WAAW,MAAM;AAAA,UACjB,SAAS;AAAA,UACT,YAAY;AAAA,QACd;AAEA,aAAK,cAAc,IAAI,QAAQ,MAAM;AAAA,MACvC;AAAA,IACF;AAGA,QAAI,MAAM,QAAQ,SAAS,sCAAQ,GAAG;AACpC,YAAM,SAAS,KAAK,cAAc,KAAK;AACvC,cAAQ,IAAI,+DAA4B,EAAE,QAAQ,WAAW,CAAC,CAAC,OAAO,CAAC;AACvE,UAAI,QAAQ;AAEV,YAAI,CAAC,KAAK,cAAc,IAAI,MAAM,GAAG;AACnC,gBAAMC,UAAuB;AAAA,YAC3B;AAAA,YACA,UAAU,KAAK,gBAAgB,KAAK,KAAK;AAAA,YACzC,UAAU,KAAK,gBAAgB,KAAK,KAAK;AAAA,YACzC,WAAW;AAAA;AAAA,YACX,SAAS,MAAM;AAAA,YACf,SAAS;AAAA,YACT,YAAY;AAAA,UACd;AAGA,eAAK,eAAeA,OAAM;AAC1B;AAAA,QACF;AAEA,cAAM,SAAS,KAAK,cAAc,IAAI,MAAM;AAC5C,eAAO,UAAU,MAAM;AACvB,eAAO,UAAU;AAGjB,YAAI,OAAO,aAAa,GAAG;AACzB,gBAAM,gBAAgB,KAAK,gBAAgB,KAAK;AAChD,cAAI,eAAe;AACjB,mBAAO,WAAW;AAAA,UACpB;AAAA,QACF;AAEA,aAAK,eAAe,MAAM;AAC1B,aAAK,cAAc,OAAO,MAAM;AAAA,MAClC;AAAA,IACF;AAGA,QAAI,MAAM,yBAA0B;AAClC,YAAM,SAAS,KAAK,cAAc,KAAK;AAGvC,UAAI,MAAM,WAAW,sBAAsB;AACzC,YAAI,UAAU,KAAK,cAAc,IAAI,MAAM,GAAG;AAC5C,gBAAM,SAAS,KAAK,cAAc,IAAI,MAAM;AAG5C,cAAI,CAAC,OAAO,cAAc;AACxB,mBAAO,eAAe,oBAAI,IAAoB;AAAA,UAChD;AACA,iBAAO,aAAa,IAAI,uBAAuB,OAAO,aAAa,IAAI,oBAAoB,KAAK,KAAK,CAAC;AAGtG,cAAI,MAAM,QAAQ,SAAS,cAAI,KAAK,MAAM,QAAQ,SAAS,cAAI,GAAG;AAChE,mBAAO,gBAAgB,OAAO,gBAAgB,KAAK;AAAA,UACrD;AAGA,cAAI,MAAM,QAAQ,SAAS,0BAAM,GAAG;AAClC,mBAAO,UAAU,MAAM;AACvB,mBAAO,UAAU;AACjB,mBAAO,QAAQ,MAAM;AAGrB,gBAAI,OAAO,aAAa,GAAG;AACzB,oBAAM,gBAAgB,KAAK,gBAAgB,KAAK;AAChD,kBAAI,eAAe;AACjB,uBAAO,WAAW;AAAA,cACpB;AAAA,YACF;AAEA,iBAAK,eAAe,MAAM;AAC1B,iBAAK,cAAc,OAAO,MAAM;AAAA,UAClC;AAAA,QACF;AAAA,MACF;AAGA,UAAI,MAAM,WAAW,cAAc;AACjC,YAAI,UAAU,KAAK,cAAc,IAAI,MAAM,GAAG;AAC5C,gBAAM,SAAS,KAAK,cAAc,IAAI,MAAM;AAG5C,cAAI,CAAC,OAAO,cAAc;AACxB,mBAAO,eAAe,oBAAI,IAAoB;AAAA,UAChD;AACA,iBAAO,aAAa,IAAI,eAAe,OAAO,aAAa,IAAI,YAAY,KAAK,KAAK,CAAC;AAEtF,iBAAO,UAAU,MAAM;AACvB,iBAAO,UAAU;AACjB,iBAAO,QAAQ,MAAM;AAGrB,cAAI,OAAO,aAAa,GAAG;AACzB,kBAAM,gBAAgB,KAAK,gBAAgB,KAAK;AAChD,gBAAI,eAAe;AACjB,qBAAO,WAAW;AAAA,YACpB;AAAA,UACF;AAEA,eAAK,eAAe,MAAM;AAC1B,eAAK,cAAc,OAAO,MAAM;AAAA,QAClC;AAAA,MACF;AAAA,IACF;AAGA,QAAI,MAAM,QAAQ,SAAS,cAAI,GAAG;AAChC,YAAM,SAAS,KAAK,cAAc,KAAK;AACvC,UAAI,UAAU,KAAK,cAAc,IAAI,MAAM,GAAG;AAC5C,cAAM,SAAS,KAAK,cAAc,IAAI,MAAM;AAC5C,eAAO;AAAA,MACT;AAAA,IACF;AAGA,QAAI,MAAM,WAAW,wBAAwB,MAAM,QAAQ,SAAS,sCAAQ,GAAG;AAC7E,YAAM,SAAS,KAAK,cAAc,KAAK;AACvC,UAAI,UAAU,KAAK,cAAc,IAAI,MAAM,GAAG;AAC5C,cAAM,SAAS,KAAK,cAAc,IAAI,MAAM;AAC5C,cAAM,cAAc,KAAK,mBAAmB,KAAK;AACjD,YAAI,aAAa;AACf,iBAAO,cAAc;AACrB,iBAAO,SAAS;AAChB,iBAAO,eAAe;AAAA,QACxB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,eAAe,QAAsB;AAE3C,QAAI,OAAO,aAAa;AACtB,aAAO,SAAS,OAAO,eAAe,OAAO,gBAAgB;AAAA,IAC/D;AAGA,UAAM,gBAAgB,KAAK,QAAQ,UAAU,OAAK,EAAE,WAAW,OAAO,MAAM;AAC5E,QAAI,kBAAkB,IAAI;AAExB,WAAK,QAAQ,aAAa,IAAI;AAAA,IAChC,OAAO;AAEL,WAAK,QAAQ,KAAK,MAAM;AAAA,IAC1B;AAGA,QAAI,KAAK,QAAQ,SAAS,KAAK,aAAa;AAC1C,WAAK,QAAQ,MAAM;AAAA,IACrB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,cAAc,OAAgC;AAEpD,QAAI,MAAM,MAAM,QAAQ;AACtB,iBAAW,OAAO,MAAM,MAAM;AAC5B,YAAI,OAAO,OAAO,QAAQ,YAAY,YAAY,KAAK;AACrD,iBAAO,OAAO,IAAI,MAAM;AAAA,QAC1B;AAAA,MACF;AAAA,IACF;AAGA,UAAM,QAAQ,MAAM,QAAQ,MAAM,oBAAoB;AACtD,QAAI,OAAO;AACT,aAAO,MAAM,CAAC;AAAA,IAChB;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,gBAAgB,OAAgC;AACtD,QAAI,MAAM,MAAM,QAAQ;AACtB,iBAAW,OAAO,MAAM,MAAM;AAE5B,YAAI,OAAO,QAAQ,UAAU;AAC3B,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,gBAAgB,OAAgC;AACtD,QAAI,MAAM,MAAM,QAAQ;AACtB,iBAAW,OAAO,MAAM,MAAM;AAE5B,YAAI,OAAO,OAAO,QAAQ,YAAY,UAAU,OAAO,OAAO,IAAI,SAAS,UAAU;AACnF,iBAAO,IAAI;AAAA,QACb;AAEA,YAAI,OAAO,OAAO,QAAQ,YAAY,cAAc,OAAO,OAAO,IAAI,aAAa,UAAU;AAC3F,iBAAO,IAAI;AAAA,QACb;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,mBAAmB,OAAgC;AACzD,QAAI,MAAM,MAAM,QAAQ;AACtB,iBAAW,OAAO,MAAM,MAAM;AAC5B,YAAI,OAAO,OAAO,QAAQ,YAAY,iBAAiB,OAAO,OAAO,IAAI,gBAAgB,UAAU;AACjG,iBAAO,IAAI;AAAA,QACb;AAAA,MACF;AAAA,IACF;AAGA,UAAM,QAAQ,MAAM,QAAQ,MAAM,kBAAkB;AACpD,QAAI,OAAO;AACT,aAAO,SAAS,MAAM,CAAC,GAAG,EAAE;AAAA,IAC9B;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKO,WAAwB;AAC7B,UAAM,mBAAmB,KAAK,QAAQ,OAAO,OAAK,EAAE,OAAO;AAE3D,QAAI,iBAAiB,WAAW,GAAG;AACjC,aAAO;AAAA,QACL,cAAc;AAAA,QACd,mBAAmB;AAAA,QACnB,eAAe;AAAA,QACf,aAAa;AAAA,QACb,iBAAiB;AAAA,QACjB,aAAa;AAAA,QACb,aAAa;AAAA,QACb,gBAAgB;AAAA,QAChB,iBAAiB;AAAA,QACjB,oBAAoB;AAAA,QACpB,cAAc;AAAA,QACd,cAAc,oBAAI,IAAI;AAAA,QACtB,gBAAgB,oBAAI,IAAI;AAAA,QACxB,gBAAgB;AAAA,MAClB;AAAA,IACF;AAEA,UAAM,aAAa,iBAAiB,OAAO,OAAK,EAAE,OAAO;AACzD,UAAM,SAAS,iBAAiB,OAAO,OAAK,CAAC,EAAE,OAAO;AAGtD,UAAM,YAAY,iBACf,OAAO,OAAK,EAAE,YAAY,KAAK,EAAE,OAAO,EACxC,IAAI,OAAK,EAAE,UAAW,EAAE,SAAS,EACjC,OAAO,OAAK,IAAI,CAAC;AAEpB,UAAM,kBAAkB,UAAU,SAAS,IACvC,KAAK,MAAM,UAAU,OAAO,CAAC,GAAG,MAAM,IAAI,GAAG,CAAC,IAAI,UAAU,MAAM,IAClE;AAEJ,UAAM,cAAc,UAAU,SAAS,IAAI,KAAK,IAAI,GAAG,SAAS,IAAI;AACpE,UAAM,cAAc,UAAU,SAAS,IAAI,KAAK,IAAI,GAAG,SAAS,IAAI;AAGpE,UAAM,kBAAkB,CAAC,GAAG,SAAS,EAAE,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AAC3D,UAAM,iBAAiB,gBAAgB,SAAS,IAC5C,gBAAgB,KAAK,MAAM,gBAAgB,SAAS,CAAC,CAAC,IACtD;AAGJ,UAAM,YAAY,iBAAiB,IAAI,OAAK,EAAE,QAAQ,EAAE,OAAO,OAAK,IAAI,CAAC;AACzE,UAAM,kBAAkB,UAAU,SAAS,IACvC,KAAK,MAAM,UAAU,OAAO,CAAC,GAAG,MAAM,IAAI,GAAG,CAAC,IAAI,UAAU,MAAM,IAClE;AAEJ,UAAM,qBAAqB,WAAW,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,UAAU,CAAC;AAG5E,UAAM,gBAAgB,UAAU,OAAO,CAAC,GAAG,MAAM,IAAI,GAAG,CAAC;AACzD,UAAM,eAAe,gBAAgB,IACjC,KAAK,MAAO,sBAAsB,gBAAgB,IAAM,IACxD;AAGJ,UAAM,eAAe,oBAAI,IAAoB;AAC7C,UAAM,iBAAiB,oBAAI,IAAoB;AAE/C,WAAO,QAAQ,YAAU;AACvB,UAAI,OAAO,OAAO;AAEhB,cAAM,YAAY,KAAK,cAAc,OAAO,KAAK;AACjD,qBAAa,IAAI,YAAY,aAAa,IAAI,SAAS,KAAK,KAAK,CAAC;AAAA,MACpE;AAGA,UAAI,OAAO,cAAc;AACvB,eAAO,aAAa,QAAQ,CAAC,OAAO,WAAW;AAC7C,yBAAe,IAAI,SAAS,eAAe,IAAI,MAAM,KAAK,KAAK,KAAK;AAAA,QACtE,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAGD,UAAM,eAAe,iBAAiB,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,YAAY,CAAC;AAC9E,UAAM,iBAAiB,iBAAiB,SAAS,IAC7C,KAAK,MAAO,eAAe,iBAAiB,SAAU,GAAG,IAAI,MAC7D;AAGJ,UAAM,eAAe,iBAAiB,OAAO,OAAK,EAAE,WAAW;AAC/D,QAAI;AAEJ,QAAI,aAAa,SAAS,GAAG;AAC3B,YAAM,cAAc,aAAa,OAAO,CAAC,KAAK,MAAM,OAAO,EAAE,eAAe,IAAI,CAAC;AACjF,YAAM,mBAAmB,aAAa,OAAO,CAAC,KAAK,MAAM,OAAO,EAAE,UAAU,IAAI,CAAC;AACjF,YAAM,eAAe,aAAa,OAAO,CAAC,KAAK,MAAM,OAAO,EAAE,gBAAgB,IAAI,CAAC;AAEnF,yBAAmB;AAAA,QACjB;AAAA,QACA;AAAA,QACA;AAAA,QACA,kBAAkB,cAAc,IAC5B,KAAK,MAAO,mBAAmB,cAAe,GAAK,IAAI,MACvD;AAAA,MACN;AAAA,IACF;AAEA,WAAO;AAAA,MACL,cAAc,iBAAiB;AAAA,MAC/B,mBAAmB,WAAW;AAAA,MAC9B,eAAe,OAAO;AAAA,MACtB,aAAa,KAAK,MAAO,WAAW,SAAS,iBAAiB,SAAU,GAAK,IAAI;AAAA,MACjF;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,cAAc,cAA8B;AAClD,QAAI,aAAa,SAAS,cAAI,KAAK,aAAa,SAAS,SAAS,GAAG;AACnE,aAAO;AAAA,IACT;AACA,QAAI,aAAa,SAAS,oBAAK,KAAK,aAAa,SAAS,KAAK,GAAG;AAChE,aAAO;AAAA,IACT;AACA,QAAI,aAAa,SAAS,oBAAK,KAAK,aAAa,SAAS,KAAK,GAAG;AAChE,aAAO;AAAA,IACT;AACA,QAAI,aAAa,SAAS,cAAI,GAAG;AAC/B,aAAO;AAAA,IACT;AACA,QAAI,aAAa,SAAS,cAAI,GAAG;AAC/B,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKO,iBAAiB,QAAgB,IAAoB;AAC1D,WAAO,KAAK,QAAQ,MAAM,CAAC,KAAK,EAAE,QAAQ;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA,EAKO,mBAA8C;AACnD,WAAO,IAAI,IAAI,KAAK,aAAa;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA,EAKO,QAAc;AACnB,SAAK,UAAU,CAAC;AAChB,SAAK,cAAc,MAAM;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA,EAKO,eAAuB;AAC5B,UAAM,QAAQ,KAAK,SAAS;AAG5B,UAAM,oBAAoB;AAAA,MACxB,GAAG;AAAA,MACH,cAAc,OAAO,YAAY,MAAM,YAAY;AAAA,MACnD,gBAAgB,OAAO,YAAY,MAAM,cAAc;AAAA,MACvD,aAAY,oBAAI,KAAK,GAAE,YAAY;AAAA,IACrC;AAEA,WAAO,KAAK,UAAU,mBAAmB,MAAM,CAAC;AAAA,EAClD;AAAA;AAAA;AAAA;AAAA,EAKO,cAAoB;AACzB,UAAM,QAAQ,KAAK,SAAS;AAE5B,YAAQ,MAAM,4DAAa;AAC3B,YAAQ,IAAI,6BAAS,MAAM,YAAY;AACvC,YAAQ,IAAI,6BAAS,MAAM,iBAAiB;AAC5C,YAAQ,IAAI,6BAAS,MAAM,aAAa;AACxC,YAAQ,IAAI,uBAAQ,GAAG,MAAM,WAAW,GAAG;AAC3C,YAAQ,IAAI,6BAAS,GAAG,MAAM,eAAe,IAAI;AACjD,YAAQ,IAAI,6BAAS,GAAG,MAAM,WAAW,IAAI;AAC7C,YAAQ,IAAI,6BAAS,GAAG,MAAM,WAAW,IAAI;AAC7C,YAAQ,IAAI,mCAAU,GAAG,MAAM,cAAc,IAAI;AACjD,YAAQ,IAAI,yCAAW,GAAG,MAAM,eAAe,QAAQ;AACvD,YAAQ,IAAI,yCAAW,GAAG,MAAM,kBAAkB,QAAQ;AAC1D,YAAQ,IAAI,yCAAW,GAAG,MAAM,YAAY,UAAU;AACtD,YAAQ,IAAI,yCAAW,MAAM,cAAc;AAE3C,QAAI,MAAM,kBAAkB;AAC1B,cAAQ,MAAM,sCAAQ;AACtB,cAAQ,IAAI,6BAAS,MAAM,iBAAiB,WAAW;AACvD,cAAQ,IAAI,6BAAS,MAAM,iBAAiB,gBAAgB;AAC5D,cAAQ,IAAI,6BAAS,MAAM,iBAAiB,YAAY;AACxD,cAAQ,IAAI,mCAAU,GAAG,MAAM,iBAAiB,gBAAgB,GAAG;AACnE,cAAQ,SAAS;AAAA,IACnB;AAEA,QAAI,MAAM,aAAa,OAAO,GAAG;AAC/B,cAAQ,MAAM,sCAAQ;AACtB,YAAM,aAAa,QAAQ,CAAC,OAAO,SAAS;AAC1C,gBAAQ,IAAI,GAAG,IAAI,KAAK,KAAK;AAAA,MAC/B,CAAC;AACD,cAAQ,SAAS;AAAA,IACnB;AAGA,QAAI,MAAM,eAAe,OAAO,GAAG;AACjC,cAAQ,MAAM,sCAAQ;AACtB,YAAM,eAAe,QAAQ,CAAC,OAAO,WAAW;AAC9C,gBAAQ,IAAI,GAAG,MAAM,KAAK,KAAK;AAAA,MACjC,CAAC;AACD,cAAQ,SAAS;AAAA,IACnB;AAEA,YAAQ,SAAS;AAAA,EACnB;AACF;AAGO,IAAM,gBAAgB,IAAI,cAAc;;;ACzlBxC,SAAS,qBAAyC;AACvD,QAAM,SAA6B;AAAA,IACjC,QAAQ,UAAU;AAAA,IAClB,WAAW,KAAK,IAAI;AAAA,EACtB;AAEA,MAAI,CAAC,OAAO,QAAQ;AAClB,WAAO,QAAQ;AAAA,EACjB;AAEA,SAAO;AACT;AAQA,eAAsB,yBACpB,SACA,UAAkB,KACW;AAE7B,QAAM,aAAa,mBAAmB;AACtC,MAAI,CAAC,WAAW,QAAQ;AACtB,WAAO;AAAA,EACT;AAGA,QAAM,MAAM,WAAW,OAAO,SAAS;AAEvC,MAAI;AAEF,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,YAAY,WAAW,MAAM,WAAW,MAAM,GAAG,OAAO;AAE9D,UAAM,MAAM,KAAK;AAAA,MACf,QAAQ;AAAA,MACR,QAAQ,WAAW;AAAA,MACnB,OAAO;AAAA,IACT,CAAC;AAED,iBAAa,SAAS;AAEtB,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,WAAW,KAAK,IAAI;AAAA,IACtB;AAAA,EACF,SAAS,OAAO;AACd,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,WAAW,KAAK,IAAI;AAAA,MACpB,OAAO,iBAAiB,QAAQ,MAAM,UAAU;AAAA,IAClD;AAAA,EACF;AACF;AAOO,SAAS,mBACd,UACY;AACZ,QAAM,eAAe,MAAM,SAAS,IAAI;AACxC,QAAM,gBAAgB,MAAM,SAAS,KAAK;AAE1C,SAAO,iBAAiB,UAAU,YAAY;AAC9C,SAAO,iBAAiB,WAAW,aAAa;AAGhD,SAAO,MAAM;AACX,WAAO,oBAAoB,UAAU,YAAY;AACjD,WAAO,oBAAoB,WAAW,aAAa;AAAA,EACrD;AACF;;;AJkXA;AAxaO,SAAS,iBAAiB;AAC/B,QAAM,YAAY,KAAK,IAAI,EAAE,SAAS,EAAE;AACxC,QAAM,SAAS,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,UAAU,GAAG,EAAE;AACzD,SAAO,QAAQ,SAAS,IAAI,MAAM;AACpC;AAEO,SAAS,aACd,QACA,QACG;AAEH,MAAI,CAAC,UAAU,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,GAAG;AAClE,UAAM,IAAI,UAAU,+BAA+B;AAAA,EACrD;AAGA,MAAI,WAAW,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,IAAI;AACnE,UAAM,IAAI,UAAU,8CAA8C;AAAA,EACpE;AAGA,MAAI,CAAC,QAAQ;AACX,WAAO,EAAE,GAAG,OAAO;AAAA,EACrB;AAGA,SAAO,EAAE,GAAG,QAAQ,GAAG,OAAO;AAChC;AAEO,SAAS,WAAW,MAA6B;AACtD,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,QAAI,KAAK,OAAO,KAAK,OAAO,MAAM;AAChC,UAAI;AACF,gBAAQ,IAAI,gBAAgB,IAAI,CAAC;AAAA,MACnC,SAAS,OAAY;AACnB,eAAO,IAAI,MAAM,uCAAmB,MAAM,OAAO,EAAE,CAAC;AAAA,MACtD;AAAA,IACF,OAAO;AACL,YAAM,SAAS,IAAI,WAAW;AAC9B,aAAO,SAAS,CAAC,MAAM;AACrB,gBAAQ,EAAE,QAAQ,MAAgB;AAAA,MACpC;AACA,aAAO,UAAU,MAAM;AACrB,eAAO,IAAI,MAAM,sCAAQ,CAAC;AAAA,MAC5B;AAEA,aAAO,UAAU,MAAM;AACrB,eAAO,IAAI,MAAM,4CAAS,CAAC;AAAA,MAC7B;AACA,aAAO,cAAc,IAAI;AAAA,IAC3B;AAAA,EACF,CAAC;AACH;AAEO,SAAS,oBAAoB,cAAwB;AAC1D,SAAO;AAAA,IACL,QAAQ;AAEN,YAAM,MAAM,KAAK,IAAI;AAErB,mBAAa,YAAY;AACzB,mBAAa,UAAU;AACvB,mBAAa,WAAW;AACxB,mBAAa,oBAAoB;AAAA,IACnC;AAAA,IACA,MAAM;AAEJ,YAAM,UAAU,KAAK,IAAI;AACzB,YAAM,WAAW,UAAU,aAAa;AAExC,mBAAa,UAAU;AACvB,mBAAa,WAAW;AACxB,mBAAa,oBAAoB,eAAe,QAAQ;AAAA,IAC1D;AAAA,EACF;AACF;AAEO,SAAS,iBAAiB,UAAmB;AAClD,QAAM,QAAQ,YAAY,UAAU,MAAM,GAAG;AAC7C,SAAO,SAAS,MAAM,SAAS,IAAI,MAAM,MAAM,SAAS,CAAC,IAAI;AAC/D;AAQO,SAAS,aAAa,MAAkC;AAC7D,SAAO,CAAC,aAAa,UAAU,QAAQ,SAAS,EAAE,SAAS,KAAK,MAAO;AACzE;AAOO,SAAS,eAAe,OAAY,WAAW,GAAG;AAEvD,MAAI,UAAU,QAAQ,UAAU,UAAa,MAAM,OAAO,KAAK,CAAC,GAAG;AACjE,WAAO;AAAA,EACT;AAGA,UAAQ,OAAO,KAAK;AAGpB,MAAI,MAAM,KAAK;AAAG,WAAO;AACzB,MAAI,UAAU;AAAG,WAAO;AAGxB,MAAI,QAAQ,GAAG;AACb,YAAQ,KAAK,wDAA0B,KAAK;AAC5C,WAAO;AAAA,EACT;AAEA,QAAM,IAAI;AACV,QAAM,QAAQ,CAAC,SAAS,MAAM,MAAM,MAAM,IAAI;AAC9C,QAAM,IAAI,KAAK,MAAM,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI,CAAC,CAAC;AAIlD,UAAQ,QAAQ,KAAK,IAAI,GAAG,CAAC,GAAG,QAAQ,QAAQ,IAAI,MAAM,MAAM,CAAC;AACnE;AAEO,IAAM,YAAY;AAAA,EACvB,MAAM,OAAe,QAAgB;AACnC,WAAO,SAAS;AAAA,EAClB;AAAA,EACA,KAAK,UAAkB,SAAiB;AACtC,WAAO,YAAY;AAAA,EACrB;AAAA,EACA,KAAK,QAAkB,MAAyB;AAC9C,QAAI,OAAO,SAAS,GAAG;AACrB,aAAO,OAAO,KAAK,CAAC,eAAe;AAEjC,YAAI,WAAW,SAAS,IAAI,GAAG;AAC7B,gBAAM,WAAW,WAAW,MAAM,GAAG,EAAE,CAAC;AACxC,iBAAO,KAAK,MAAM,KAAK,WAAW,QAAQ;AAAA,QAC5C;AAEA,YAAI,WAAW,WAAW,GAAG,GAAG;AAC9B,iBAAO,KAAK,SAAS,YAAY,EAAE,SAAS,WAAW,YAAY,CAAC;AAAA,QACtE;AAEA,eAAO,KAAK,MAAM,SAAS;AAAA,MAC7B,CAAC;AAAA,IACH;AACA,WAAO;AAAA,EACT;AACF;AASA,SAAS,mBAGP,MAAS,SAAe;AAExB,SAAO,eAAe,MAAM,YAAY;AAAA,IACtC,OAAO;AAAA,IACP,YAAY;AAAA;AAAA,IACZ,UAAU;AAAA,IACV,cAAc;AAAA,EAChB,CAAC;AAED,SAAO,IAAI,MAAM,MAAM;AAAA,IACrB,IAAI,QAAQ,MAAM,WAAW;AAG3B,YAAM,QAAQ,QAAQ,IAAI,QAAQ,IAAI;AAItC,UAAI,OAAO,UAAU,YAAY;AAC/B,eAAO,YAAa,MAAa;AAC/B,gBAAM,SAAS,MAAM,MAAM,QAAQ,IAAI;AACvC,iBAAO;AAAA,QACT;AAAA,MACF;AAEA,aAAO;AAAA,IACT;AAAA,IAEA,IAAI,QAAQ,MAAM,OAAO,WAAW;AAElC,YAAM,WAAW,QAAQ,IAAI,QAAQ,IAAI;AAGzC,YAAM,SAAS,QAAQ,IAAI,QAAQ,MAAM,KAAK;AAG9C,UAAI,aAAa,OAAO;AAEtB,gBAAQ,cAAc;AAAA,MACxB;AAEA,aAAO;AAAA,IACT;AAAA;AAAA,IAGA,eAAe,QAAQ,MAAM;AAC3B,YAAM,SAAS,QAAQ,eAAe,QAAQ,IAAI;AAElD,cAAQ,cAAc;AACtB,aAAO;AAAA,IACT;AAAA,EACF,CAAC;AACH;AAQO,SAAS,yBACd,MACA,UACY;AACZ,SAAO,mBAAmB,MAAM,QAAQ;AAC1C;AAQO,SAAS,2BACd,MACA,YACc;AACd,SAAO,mBAAmB,MAAM,UAAU;AAC5C;AAOO,SAAS,wBAAwB,KAA4B;AAElE,QAAM,UAAU;AAChB,QAAM,QAAQ,IAAI,MAAM,OAAO;AAE/B,SAAO,QAAQ,MAAM,CAAC,IAAI;AAC5B;AAGO,SAAS,cAAc,OAA0C;AACtE,SAAO,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAOO,SAAS,YAAY,gBAAgC;AAC1D,MAAI,mBAAmB,GAAG;AACxB,WAAO;AAAA,EACT;AAEA,QAAM,QAAQ,CAAC,OAAO,QAAQ,QAAQ,MAAM;AAC5C,MAAI,QAAQ;AACZ,MAAI,YAAY;AAEhB,SAAO,SAAS,QAAQ,YAAY,MAAM,SAAS,GAAG;AACpD,aAAS;AACT;AAAA,EACF;AAEA,SAAO,GAAG,MAAM,QAAQ,CAAC,CAAC,IAAI,MAAM,SAAS,CAAC;AAChD;AAOO,SAAS,eAAe,cAA8B;AAC3D,MAAI,iBAAiB,GAAG;AACtB,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,KAAK,MAAM,eAAe,GAAI;AAC9C,QAAM,UAAU,KAAK,MAAM,UAAU,EAAE;AACvC,QAAM,QAAQ,KAAK,MAAM,UAAU,EAAE;AAErC,MAAI,QAAQ,GAAG;AACb,UAAM,mBAAmB,UAAU;AACnC,UAAM,mBAAmB,UAAU;AACnC,WAAO,GAAG,KAAK,KAAK,gBAAgB,KAAK,gBAAgB;AAAA,EAC3D,WAAW,UAAU,GAAG;AACtB,UAAM,mBAAmB,UAAU;AACnC,WAAO,GAAG,OAAO,KAAK,gBAAgB;AAAA,EACxC,OAAO;AACL,WAAO,GAAG,OAAO;AAAA,EACnB;AACF;AAOO,SAAS,MAAM,IAA2B;AAC/C,SAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AACzD;AAGA,IAAI,mBAAwD;AAC5D,eAAe,qBAAqB;AAClC,MAAI,CAAC,kBAAkB;AACrB,uBAAmB,MAAM;AAAA,EAC3B;AACA,SAAO;AACT;AAeA,eAAsB,iBACpB,MACA,YACA,QACiB;AACjB,QAAM,kBAAkB,IAAI,OAAO;AACnC,QAAM,YACJ,CAAC;AAAA,EACD,OAAO,WAAW,eAClB,KAAK,QAAQ;AAEf,MAAI,WAAW;AACb,QAAI;AACF,YAAM,EAAE,0BAAAC,0BAAyB,IAAI,MAAM,mBAAmB;AAC9D,aAAO,MAAMA,0BAAyB,MAAM,MAAM;AAAA,IACpD,SAAS,WAAW;AAElB,cAAQ,KAAK,iGAA0C,SAAS;AAAA,IAClE;AAAA,EACF;AAGA,SAAO,IAAI,QAAQ,OAAO,SAAS,WAAW;AAC5C,QAAI;AA2CF,UAASC,YAAT,WAAoB;AAClB,YAAI,QAAQ,SAAS;AACnB,iBAAO,IAAI,MAAM,oCAAW,CAAC;AAC7B;AAAA,QACF;AAEA,cAAM,QAAQ,eAAe;AAC7B,cAAM,MAAM,KAAK,IAAI,QAAQ,WAAW,KAAK,IAAI;AACjD,mBAAW,kBAAkB,KAAK,MAAM,OAAO,GAAG,CAAC;AAAA,MACrD;AATS,qBAAAA;AA1CT,YAAM,YAAY,IAAI,OAAO;AAC7B,YAAM,SAAS,KAAK,KAAK,KAAK,OAAO,SAAS;AAC9C,UAAI,eAAe;AAEnB,YAAM,QAAQ,IAAI,SAAS,YAAY;AACvC,YAAM,aAAa,IAAI,WAAW;AAElC,UAAI,QAAQ;AACV,eAAO,iBAAiB,SAAS,MAAM;AACrC,qBAAW,MAAM;AACjB,iBAAO,IAAI,MAAM,oCAAW,CAAC;AAAA,QAC/B,CAAC;AAAA,MACH;AAEA,iBAAW,SAAS,CAAC,MAAM;AACzB,YAAI,QAAQ,SAAS;AACnB,qBAAW,MAAM;AACjB,iBAAO,IAAI,MAAM,oCAAW,CAAC;AAC7B;AAAA,QACF;AAEA,YAAI,EAAE,QAAQ,QAAQ;AACpB,gBAAM,OAAO,EAAE,OAAO,MAAqB;AAC3C;AAEA,cAAI,YAAY;AACd,kBAAM,UAAU,KAAK,MAAO,eAAe,SAAU,GAAG;AACxD,uBAAW,OAAO;AAAA,UACpB;AAEA,cAAI,eAAe,QAAQ;AACzB,YAAAA,UAAS;AAAA,UACX,OAAO;AACL,oBAAQ,MAAM,IAAI,CAAC;AAAA,UACrB;AAAA,QACF;AAAA,MACF;AAEA,iBAAW,UAAU,MAAM;AACzB,eAAO,IAAI,MAAM,sCAAQ,CAAC;AAAA,MAC5B;AAaA,MAAAA,UAAS;AAAA,IACX,SAAS,OAAO;AACd,cAAQ,MAAM,mDAAgB,KAAK;AACnC,aAAO,IAAI,MAAM,gDAAa,CAAC;AAAA,IACjC;AAAA,EACF,CAAC;AACH","sourcesContent":["/**\n * Web Worker 内联 MD5 计算 — 将 CPU 密集型哈希计算移出主线程,避免 UI 卡顿。\n *\n * 策略:\n * - 文件 ≤ 50MB:通过 Blob URL Worker 异步计算,主线程零阻塞\n * - 文件 > 50MB 或 Worker 不可用:回退到主线程 SparkMD5 流式计算\n * - 小文件 (<5MB):直接用主线程,省去 Worker 创建开销\n */\n\n// ---- 内联 MD5 实现(RFC 1321,纯 JS,无依赖)----\n\nconst MD5_IMPL = `\nfunction md5(data) {\n function rotateLeft(v, n) { return (v << n) | (v >>> (32 - n)); }\n function addUnsigned(x, y) { return ((x & 0x7fffffff) + (y & 0x7fffffff)) ^ (x & 0x80000000) ^ (y & 0x80000000); }\n\n var F = function(x,y,z) { return (x & y) | ((~x) & z); };\n var G = function(x,y,z) { return (x & z) | (y & (~z)); };\n var H = function(x,y,z) { return x ^ y ^ z; };\n var I = function(x,y,z) { return y ^ (x | (~z)); };\n\n function transform(func, a, b, c, d, x, s, ac) {\n a = addUnsigned(a, addUnsigned(addUnsigned(func(b, c, d), x), ac));\n return addUnsigned(rotateLeft(a, s), b);\n }\n\n // Convert string/array to word array\n var len = data.length;\n var words = [];\n for (var i = 0; i < len; i++) {\n words[i >> 2] |= data[i] << ((i % 4) * 8);\n }\n words[len >> 2] |= 0x80 << ((len % 4) * 8);\n words[(((len + 8) >>> 6) << 4) + 14] = len * 8;\n\n var a = 0x67452301, b = 0xefcdab89, c = 0x98badcfe, d = 0x10325476;\n\n for (var i = 0; i < words.length; i += 16) {\n var oldA = a, oldB = b, oldC = c, oldD = d;\n // Round 1\n a = transform(F, a, b, c, d, words[i+0], 7, 0xd76aa478);\n d = transform(F, d, a, b, c, words[i+1], 12, 0xe8c7b756);\n c = transform(F, c, d, a, b, words[i+2], 17, 0x242070db);\n b = transform(F, b, c, d, a, words[i+3], 22, 0xc1bdceee);\n a = transform(F, a, b, c, d, words[i+4], 7, 0xf57c0faf);\n d = transform(F, d, a, b, c, words[i+5], 12, 0x4787c62a);\n c = transform(F, c, d, a, b, words[i+6], 17, 0xa8304613);\n b = transform(F, b, c, d, a, words[i+7], 22, 0xfd469501);\n a = transform(F, a, b, c, d, words[i+8], 7, 0x698098d8);\n d = transform(F, d, a, b, c, words[i+9], 12, 0x8b44f7af);\n c = transform(F, c, d, a, b, words[i+10],17, 0xffff5bb1);\n b = transform(F, b, c, d, a, words[i+11],22, 0x895cd7be);\n a = transform(F, a, b, c, d, words[i+12], 7, 0x6b901122);\n d = transform(F, d, a, b, c, words[i+13],12, 0xfd987193);\n c = transform(F, c, d, a, b, words[i+14],17, 0xa679438e);\n b = transform(F, b, c, d, a, words[i+15],22, 0x49b40821);\n // Round 2\n a = transform(G, a, b, c, d, words[i+1], 5, 0xf61e2562);\n d = transform(G, d, a, b, c, words[i+6], 9, 0xc040b340);\n c = transform(G, c, d, a, b, words[i+11],14, 0x265e5a51);\n b = transform(G, b, c, d, a, words[i+0], 20, 0xe9b6c7aa);\n a = transform(G, a, b, c, d, words[i+5], 5, 0xd62f105d);\n d = transform(G, d, a, b, c, words[i+10], 9, 0x02441453);\n c = transform(G, c, d, a, b, words[i+15],14, 0xd8a1e681);\n b = transform(G, b, c, d, a, words[i+4], 20, 0xe7d3fbc8);\n a = transform(G, a, b, c, d, words[i+9], 5, 0x21e1cde6);\n d = transform(G, d, a, b, c, words[i+14], 9, 0xc33707d6);\n c = transform(G, c, d, a, b, words[i+3], 14, 0xf4d50d87);\n b = transform(G, b, c, d, a, words[i+8], 20, 0x455a14ed);\n a = transform(G, a, b, c, d, words[i+13], 5, 0xa9e3e905);\n d = transform(G, d, a, b, c, words[i+2], 9, 0xfcefa3f8);\n c = transform(G, c, d, a, b, words[i+7], 14, 0x676f02d9);\n b = transform(G, b, c, d, a, words[i+12],20, 0x8d2a4c8a);\n // Round 3\n a = transform(H, a, b, c, d, words[i+5], 4, 0xfffa3942);\n d = transform(H, d, a, b, c, words[i+8], 11, 0x8771f681);\n c = transform(H, c, d, a, b, words[i+11],16, 0x6d9d6122);\n b = transform(H, b, c, d, a, words[i+14],23, 0xfde5380c);\n a = transform(H, a, b, c, d, words[i+1], 4, 0xa4beea44);\n d = transform(H, d, a, b, c, words[i+4], 11, 0x4bdecfa9);\n c = transform(H, c, d, a, b, words[i+7], 16, 0xf6bb4b60);\n b = transform(H, b, c, d, a, words[i+10],23, 0xbebfbc70);\n a = transform(H, a, b, c, d, words[i+13], 4, 0x289b7ec6);\n d = transform(H, d, a, b, c, words[i+0], 11, 0xeaa127fa);\n c = transform(H, c, d, a, b, words[i+3], 16, 0xd4ef3085);\n b = transform(H, b, c, d, a, words[i+6], 23, 0x04881d05);\n a = transform(H, a, b, c, d, words[i+9], 4, 0xd9d4d039);\n d = transform(H, d, a, b, c, words[i+12],11, 0xe6db99e5);\n c = transform(H, c, d, a, b, words[i+15],16, 0x1fa27cf8);\n b = transform(H, b, c, d, a, words[i+2], 23, 0xc4ac5665);\n // Round 4\n a = transform(I, a, b, c, d, words[i+0], 6, 0xf4292244);\n d = transform(I, d, a, b, c, words[i+7], 10, 0x432aff97);\n c = transform(I, c, d, a, b, words[i+14],15, 0xab9423a7);\n b = transform(I, b, c, d, a, words[i+5], 21, 0xfc93a039);\n a = transform(I, a, b, c, d, words[i+12], 6, 0x655b59c3);\n d = transform(I, d, a, b, c, words[i+3], 10, 0x8f0ccc92);\n c = transform(I, c, d, a, b, words[i+10],15, 0xffeff47d);\n b = transform(I, b, c, d, a, words[i+1], 21, 0x85845dd1);\n a = transform(I, a, b, c, d, words[i+8], 6, 0x6fa87e4f);\n d = transform(I, d, a, b, c, words[i+15],10, 0xfe2ce6e0);\n c = transform(I, c, d, a, b, words[i+6], 15, 0xa3014314);\n b = transform(I, b, c, d, a, words[i+13],21, 0x4e0811a1);\n a = transform(I, a, b, c, d, words[i+4], 6, 0xf7537e82);\n d = transform(I, d, a, b, c, words[i+11],10, 0xbd3af235);\n c = transform(I, c, d, a, b, words[i+2], 15, 0x2ad7d2bb);\n b = transform(I, b, c, d, a, words[i+9], 21, 0xeb86d391);\n a = addUnsigned(a, oldA);\n b = addUnsigned(b, oldB);\n c = addUnsigned(c, oldC);\n d = addUnsigned(d, oldD);\n }\n\n function toHex(v) {\n var h = ''; for (var j = 0; j < 4; j++) h += ((v >> (j * 8)) & 0xff).toString(16).padStart(2, '0'); return h;\n }\n return toHex(a) + toHex(b) + toHex(c) + toHex(d);\n}\n\nself.onmessage = async function(e) {\n try {\n var fileBuffer = e.data.fileBuffer;\n var hash = md5(new Uint8Array(fileBuffer));\n self.postMessage({ error: null, hash: hash });\n } catch (err) {\n self.postMessage({ error: err.message, hash: null });\n }\n};\n`;\n\n// ---- Worker 创建与管理 ----\n\n/** 已缓存的 Blob URL,避免每次调用都创建新 URL */\nlet _workerBlobUrl: string | null = null;\n\nfunction getWorkerBlobUrl(): string {\n if (!_workerBlobUrl) {\n const blob = new Blob([MD5_IMPL], { type: \"text/javascript\" });\n _workerBlobUrl = URL.createObjectURL(blob);\n }\n return _workerBlobUrl;\n}\n\n/**\n * 使用 Web Worker 异步计算文件 MD5。\n *\n * 仅在上传端使用(有 File 对象时);下载端正用此函数的地方\n * 传入的是 FileSystemFileHandle.getFile() 返回的 File,同样可用。\n *\n * @returns MD5 hex 字符串\n */\nexport async function calculateFileMD5InWorker(\n file: File,\n signal?: AbortSignal,\n): Promise<string> {\n const WORKER_TIMEOUT_MS = 60_000; // 1 分钟超时\n\n return new Promise((resolve, reject) => {\n // 1. 读取文件为 ArrayBuffer(在 Worker 外部,因为 FileReader 在主线程更快)\n const reader = new FileReader();\n\n const abortHandler = () => {\n reader.abort();\n reject(new Error(\"MD5 计算已取消\"));\n };\n\n if (signal) {\n signal.addEventListener(\"abort\", abortHandler, { once: true });\n }\n\n reader.onload = () => {\n if (signal) {\n signal.removeEventListener(\"abort\", abortHandler);\n }\n\n const fileBuffer = reader.result as ArrayBuffer;\n\n // 2. 创建 Worker\n const workerUrl = getWorkerBlobUrl();\n const worker = new Worker(workerUrl);\n\n const timeout = setTimeout(() => {\n worker.terminate();\n reject(new Error(\"MD5 Worker 计算超时\"));\n }, WORKER_TIMEOUT_MS);\n\n worker.onmessage = (e: MessageEvent<{ error: string | null; hash: string | null }>) => {\n clearTimeout(timeout);\n worker.terminate();\n const { error, hash } = e.data;\n if (error) {\n reject(new Error(error));\n } else {\n resolve(hash!);\n }\n };\n\n worker.onerror = (err) => {\n clearTimeout(timeout);\n worker.terminate();\n reject(new Error(err.message || \"MD5 Worker 异常\"));\n };\n\n // 3. 将 ArrayBuffer 转移给 Worker(零拷贝)\n worker.postMessage({ fileBuffer }, [fileBuffer]);\n };\n\n reader.onerror = () => {\n if (signal) {\n signal.removeEventListener(\"abort\", abortHandler);\n }\n reject(new Error(\"文件读取失败\"));\n };\n\n // 一次性读取整个文件(已由调用方保证 ≤50MB)\n reader.readAsArrayBuffer(file);\n });\n}\n\n/**\n * 全局清理:释放缓存的 Blob URL(SPA 卸载时调用)\n */\nexport function disposeMD5Worker(): void {\n if (_workerBlobUrl) {\n URL.revokeObjectURL(_workerBlobUrl);\n _workerBlobUrl = null;\n }\n}\n","import Downloader from \"../downloader\";\nimport DownloadFile from \"../downloader/DownloadFile\";\nimport Transfer from \"../transfer/Transfer\";\nimport TransferFile from \"../transfer/TransferFile\";\nimport { IFile, TimeInfo } from \"../types\";\nimport Uploader from \"../uploader\";\nimport UploadFile from \"../uploader/UploadFile\";\nimport SparkMD5 from \"spark-md5\";\n// 导出日志工具\nexport {\n logger,\n initLogger,\n setLogLevel,\n LogLevel,\n addLogCollector,\n clearLogCollectors,\n} from \"./logger\";\nexport type { LoggerOptions, LogEntry, LogCollectorCallback } from \"./logger\";\n\n// ✅ 导出文件缓存工具\nexport {\n saveFileToCache,\n restoreFileFromCache,\n removeFileFromCache,\n clearAllFileCache,\n getCacheStats,\n cleanExpiredCache,\n // 🔑 文件句柄持久化(跨会话复用)\n saveFileHandle,\n loadFileHandle,\n removeFileHandle,\n} from \"./fileCache\";\n\n// 导出上传监控工具\nexport { uploadMonitor } from \"./upload-monitor\";\nexport type { UploadStats, UploadRecord } from \"./upload-monitor\";\n\n// ✅ 导出网络检查工具\nexport {\n checkNetworkStatus,\n checkNetworkConnectivity,\n watchNetworkStatus,\n} from \"./network\";\nexport type { NetworkCheckResult } from \"./network\";\n\nexport function generateFileId() {\n const timestamp = Date.now().toString(36);\n const random = Math.random().toString(36).substring(2, 10);\n return `file_${timestamp}_${random}`;\n}\n\nexport function mergeObjects<T extends Record<string, any>>(\n target: T,\n source?: Partial<T>,\n): T {\n // Type checking for target\n if (!target || typeof target !== \"object\" || Array.isArray(target)) {\n throw new TypeError(\"Target must be a valid object\");\n }\n\n // Type checking for source (allow undefined/null)\n if (source && (typeof source !== \"object\" || Array.isArray(source))) {\n throw new TypeError(\"Source must be an object, undefined, or null\");\n }\n\n // If source is empty, return shallow copy of target\n if (!source) {\n return { ...target };\n }\n\n // Perform shallow merge\n return { ...target, ...source };\n}\n\nexport function handleFile(file: File): Promise<string> {\n return new Promise((resolve, reject) => {\n if (file.size > 10 * 1024 * 1024) {\n try {\n resolve(URL.createObjectURL(file));\n } catch (error: any) {\n reject(new Error(`创建Object URL失败: ${error.message}`));\n }\n } else {\n const reader = new FileReader();\n reader.onload = (e) => {\n resolve(e.target?.result as string);\n };\n reader.onerror = () => {\n reject(new Error(\"文件读取错误\"));\n };\n\n reader.onabort = () => {\n reject(new Error(\"文件读取被中止\"));\n };\n reader.readAsDataURL(file);\n }\n });\n}\n\nexport function computeTransferTime(transferTime: TimeInfo) {\n return {\n start() {\n // 记录全局上传开始时间\n const now = Date.now();\n // ✅ 直接修改对象属性,而不是重新赋值引用\n transferTime.startTime = now;\n transferTime.endTime = 0;\n transferTime.duration = 0;\n transferTime.durationFormatted = \"0s\";\n },\n end() {\n // 记录上传结束时间并计算持续时间\n const endTime = Date.now();\n const duration = endTime - transferTime.startTime;\n // ✅ 直接修改对象属性,而不是重新赋值引用\n transferTime.endTime = endTime;\n transferTime.duration = duration;\n transferTime.durationFormatted = formatDuration(duration);\n },\n };\n}\n\nexport function getFileExtension(filename?: string) {\n const parts = filename && filename?.split(\".\");\n return parts && parts.length > 1 ? parts[parts.length - 1] : \"\";\n}\n\n/**\n * 判断文件是否处于活跃上传状态(统一判断逻辑)\n * @param file 文件实例\n * @returns 是否为活跃上传状态\n * @private\n */\nexport function isFileActive(file: TransferFile<any>): boolean {\n return [\"UDLoading\", \"paused\", \"fail\", \"merging\"].includes(file.status!);\n}\n/**\n * 将文件大小转换为易读的格式\n * @param {number} bytes - 文件大小的字节数\n * @param {number} [decimals=2] - 保留的小数位数,默认为2\n * @returns {string} 转换后的文件大小字符串\n */\nexport function formatFileSize(bytes: any, decimals = 2) {\n // ✅ 防御性检查:处理 undefined、null、NaN 等情况\n if (bytes === null || bytes === undefined || isNaN(Number(bytes))) {\n return \"0 B\";\n }\n\n // 转换为数字\n bytes = Number(bytes);\n\n // 检查是否为有效数字\n if (isNaN(bytes)) return \"0 B\";\n if (bytes === 0) return \"0 B\";\n\n // ✅ 处理负数情况(理论上不应该出现,但做防御性编程)\n if (bytes < 0) {\n console.warn(\"formatFileSize: 接收到负数值\", bytes);\n return \"0 B\";\n }\n\n const k = 1024;\n const sizes = [\"Bytes\", \"KB\", \"MB\", \"GB\", \"TB\"];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n\n // ✅ 关键修复:去掉 parseFloat(),保留 toFixed() 的精度\n // parseFloat(\"1.90\") → 1.9 ← 会去掉末尾的 0,导致精度不一致\n return (bytes / Math.pow(k, i)).toFixed(decimals) + \" \" + sizes[i];\n}\n\nexport const validator = {\n limit(limit: number, length: number) {\n return length < limit;\n },\n size(fileSize: number, maxSize: number) {\n return fileSize <= maxSize;\n },\n type(accept: string[], file: TransferFile<any>) {\n if (accept.length > 0) {\n return accept.some((acceptType) => {\n // 处理通配符,如 'image/*'\n if (acceptType.endsWith(\"/*\")) {\n const category = acceptType.split(\"/\")[0];\n return file.File?.type.startsWith(category);\n }\n // 处理扩展名,如 '.jpg'\n if (acceptType.startsWith(\".\")) {\n return file.fileName.toLowerCase().endsWith(acceptType.toLowerCase());\n }\n // 处理 MIME 类型\n return file.File?.type === acceptType;\n });\n }\n return true;\n },\n};\n\n/**\n * 创建响应式文件实例的通用工厂函数\n * @param file 原始的文件实例(UploadFile 或 DownloadFile)\n * @param manager 管理器实例(Uploader 或 Downloader)\n * @param managerKey 管理器在文件上的属性名('transfer' 或 '__downloader__')\n * @returns Proxy 包装后的文件实例\n */\nfunction createReactiveFile<\n T extends object,\n M extends { triggerUpdate: () => void },\n>(file: T, manager: M): T {\n // 添加一个内部属性指向管理器\n Object.defineProperty(file, \"transfer\", {\n value: manager,\n enumerable: false, // 不可枚举,避免循环引用\n writable: false,\n configurable: true,\n });\n\n return new Proxy(file, {\n get(target, prop, _receiver) {\n // 🔑 不传 receiver,避免 Vue reactive proxy 双重代理时\n // 对 non-configurable / non-writable 属性的读取报错\n const value = Reflect.get(target, prop);\n\n // 🔑 函数必须绑定 target 作为 this!\n // 否则 row.resume() 中 this 是 Vue proxy → this.transfer 触发 Proxy 报错\n if (typeof value === \"function\") {\n return function (...args: any[]) {\n const result = value.apply(target, args);\n return result;\n };\n }\n\n return value;\n },\n\n set(target, prop, value, _receiver) {\n // 🔑 不传 receiver,原因同上\n const oldValue = Reflect.get(target, prop);\n\n // 设置新值\n const result = Reflect.set(target, prop, value);\n\n // 如果值真的变化了,触发更新\n if (oldValue !== value) {\n // 触发 update 回调(使用防抖优化)\n manager.triggerUpdate();\n }\n\n return result;\n },\n\n // 添加 deleteProperty 陷阱,支持删除操作\n deleteProperty(target, prop) {\n const result = Reflect.deleteProperty(target, prop);\n // 触发更新回调\n manager.triggerUpdate();\n return result;\n },\n }) as T;\n}\n\n/**\n * 创建响应式的 UploadFile 实例\n * @param uploadFile 原始的 UploadFile 实例\n * @param uploader Uploader 实例\n * @returns Proxy 包装后的 UploadFile\n */\nexport function createReactiveUploadFile(\n file: UploadFile,\n uploader: Transfer,\n): UploadFile {\n return createReactiveFile(file, uploader);\n}\n\n/**\n * 创建响应式的 DownloadFile 实例\n * @param file 原始的 DownloadFile 实例\n * @param downloader Downloader 实例\n * @returns Proxy 包装后的 DownloadFile\n */\nexport function createReactiveDownloadFile(\n file: DownloadFile,\n downloader: Transfer,\n): DownloadFile {\n return createReactiveFile(file, downloader);\n}\n\n/**\n * 提取函数中的路径\n * @param str 函数字符串\n * @returns 提取到的路径,如果没有找到返回 null\n */\nexport function extractPathFromFunction(str: string): string | null {\n // 匹配以 / 开头的路径(支持单引号、双引号、反引号)\n const pattern = /['\"`](\\/[^'\"`]*)['\"`]/;\n const match = str.match(pattern);\n\n return match ? match[1] : null;\n}\n\n/** 判断一个值是否为纯对象(非 null、非数组、typeof === \"object\") */\nexport function isPlainObject(value: any): value is Record<string, any> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\n/**\n * 格式化上传/下载速度显示\n * @param bytesPerSecond 每秒字节数\n * @returns 格式化后的速度字符串\n */\nexport function formatSpeed(bytesPerSecond: number): string {\n if (bytesPerSecond === 0) {\n return \"0 B/s\";\n }\n\n const units = [\"B/s\", \"KB/s\", \"MB/s\", \"GB/s\"];\n let value = bytesPerSecond;\n let unitIndex = 0;\n\n while (value >= 1024 && unitIndex < units.length - 1) {\n value /= 1024;\n unitIndex++;\n }\n\n return `${value.toFixed(2)} ${units[unitIndex]}`;\n}\n\n/**\n * 格式化时间 duration 显示\n * @param milliseconds 毫秒数\n * @returns 格式化后的时间字符串\n */\nexport function formatDuration(milliseconds: number): string {\n if (milliseconds === 0) {\n return \"0s\";\n }\n\n const seconds = Math.floor(milliseconds / 1000);\n const minutes = Math.floor(seconds / 60);\n const hours = Math.floor(minutes / 60);\n\n if (hours > 0) {\n const remainingMinutes = minutes % 60;\n const remainingSeconds = seconds % 60;\n return `${hours}h ${remainingMinutes}m ${remainingSeconds}s`;\n } else if (minutes > 0) {\n const remainingSeconds = seconds % 60;\n return `${minutes}m ${remainingSeconds}s`;\n } else {\n return `${seconds}s`;\n }\n}\n\n/**\n * 睡眠工具函数\n * @param ms 毫秒数\n * @returns Promise\n */\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n// 🔧 Worker 模式的 MD5 计算(内部导入,避免循环引用)\nlet _md5WorkerModule: typeof import(\"./md5Worker\") | null = null;\nasync function getMD5WorkerModule() {\n if (!_md5WorkerModule) {\n _md5WorkerModule = await import(\"./md5Worker\");\n }\n return _md5WorkerModule;\n}\n\n/**\n * 计算文件的 MD5 哈希值\n *\n * 策略(自动选择最优路径):\n * - 文件 > 50MB:Worker 路径(完整读取至 Worker 计算,主线程零阻塞)\n * - 文件 5~50MB:Worker 路径,无进度回调时优先\n * - 文件 ≤ 5MB 或有 onProgress 回调:主线程 SparkMD5 流式计算\n * - Worker 不可用:回退到主线程 SparkMD5\n *\n * @param file 文件对象\n * @param onProgress 可选的进度回调函数,接收当前进度百分比 (0-100)\n * @returns MD5 哈希字符串\n */\nexport async function calculateFileMD5(\n file: File,\n onProgress?: (percent: number) => void,\n signal?: AbortSignal,\n): Promise<string> {\n const WORKER_MIN_SIZE = 5 * 1024 * 1024; // 5MB 以上才启用 Worker(省去小文件创建开销)\n const useWorker =\n !onProgress && // 有进度回调时不用 Worker(Worker 不提供分片进度)\n typeof Worker !== \"undefined\" &&\n file.size >= WORKER_MIN_SIZE;\n\n if (useWorker) {\n try {\n const { calculateFileMD5InWorker } = await getMD5WorkerModule();\n return await calculateFileMD5InWorker(file, signal);\n } catch (workerErr) {\n // Worker 创建/计算失败 → 静默回退到主线程\n console.warn(\"[calculateFileMD5] Worker 路径失败,回退到主线程:\", workerErr);\n }\n }\n\n // ---- 主线程 SparkMD5 流式回退 ----\n return new Promise(async (resolve, reject) => {\n try {\n const chunkSize = 2 * 1024 * 1024; // 2MB 分片读取\n const chunks = Math.ceil(file.size / chunkSize);\n let currentChunk = 0;\n\n const spark = new SparkMD5.ArrayBuffer();\n const fileReader = new FileReader();\n\n if (signal) {\n signal.addEventListener(\"abort\", () => {\n fileReader.abort();\n reject(new Error(\"MD5 计算已取消\"));\n });\n }\n\n fileReader.onload = (e) => {\n if (signal?.aborted) {\n fileReader.abort();\n reject(new Error(\"MD5 计算已取消\"));\n return;\n }\n\n if (e.target?.result) {\n spark.append(e.target.result as ArrayBuffer);\n currentChunk++;\n\n if (onProgress) {\n const percent = Math.round((currentChunk / chunks) * 100);\n onProgress(percent);\n }\n\n if (currentChunk < chunks) {\n loadNext();\n } else {\n resolve(spark.end());\n }\n }\n };\n\n fileReader.onerror = () => {\n reject(new Error(\"文件读取失败\"));\n };\n\n function loadNext() {\n if (signal?.aborted) {\n reject(new Error(\"MD5 计算已取消\"));\n return;\n }\n\n const start = currentChunk * chunkSize;\n const end = Math.min(start + chunkSize, file.size);\n fileReader.readAsArrayBuffer(file.slice(start, end));\n }\n\n loadNext();\n } catch (error) {\n console.error(\"MD5 计算库加载失败:\", error);\n reject(new Error(\"MD5 计算库加载失败\"));\n }\n });\n}\n\n// 导出 Worker 相关工具(供 App 层清理资源)\nexport { calculateFileMD5InWorker, disposeMD5Worker } from \"./md5Worker\";\n","/**\n * 统一日志工具模块\n * \n * 提供环境感知的日志输出,支持级别控制和格式化\n * \n * @example\n * ```typescript\n * import { logger } from './utils/logger';\n * \n * logger.info('uploadChunkManager', '开始上传分片');\n * logger.debug('uploadChunkManager', '分片详情', { index: 0, size: 1024 });\n * logger.warn('uploadChunkManager', '更新状态失败:', error);\n * logger.error('uploadChunkManager', '上传失败', error);\n * ```\n */\n\n// Node.js process 类型声明(兼容浏览器环境)\ndeclare const process: {\n env?: {\n NODE_ENV?: string;\n VITE_LOG_LEVEL?: string;\n LOG_LEVEL?: string;\n };\n};\n\n/**\n * 日志级别枚举\n */\nexport enum LogLevel {\n DEBUG = 0, // 调试信息(仅开发环境)\n INFO = 1, // 一般信息\n WARN = 2, // 警告信息\n ERROR = 3, // 错误信息\n}\n\n/**\n * 日志配置选项\n */\nexport interface LoggerOptions {\n /** 是否启用日志输出(默认 true) */\n enabled?: boolean;\n /** 最低日志级别 */\n level?: LogLevel;\n /** 是否显示时间戳 */\n showTimestamp?: boolean;\n /** 是否启用颜色输出 */\n enableColors?: boolean;\n}\n\n/**\n * 日志条目接口\n */\nexport interface LogEntry {\n /** 时间戳 */\n timestamp: number;\n /** 日志级别 */\n level: LogLevel;\n /** 模块名称 */\n module: string;\n /** 消息内容 */\n message: string;\n /** 额外参数 */\n args?: any[];\n /** 错误堆栈(如果是 Error 对象) */\n stack?: string;\n}\n\n/**\n * 日志收集器回调函数类型\n */\nexport type LogCollectorCallback = (entry: LogEntry) => void | Promise<void>;\n\n/**\n * 日志级别标签映射\n */\nconst LEVEL_LABELS: Record<LogLevel, string> = {\n [LogLevel.DEBUG]: 'DEBUG',\n [LogLevel.INFO]: 'INFO',\n [LogLevel.WARN]: 'WARN',\n [LogLevel.ERROR]: 'ERROR',\n};\n\n/**\n * 日志级别颜色映射(ANSI 颜色代码)\n */\nconst LEVEL_COLORS: Record<LogLevel, string> = {\n [LogLevel.DEBUG]: '\\x1b[36m', // 青色\n [LogLevel.INFO]: '\\x1b[32m', // 绿色\n [LogLevel.WARN]: '\\x1b[33m', // 黄色\n [LogLevel.ERROR]: '\\x1b[31m', // 红色\n};\n\nconst RESET_COLOR = '\\x1b[0m';\n\n/**\n * 默认配置\n */\nconst DEFAULT_OPTIONS: LoggerOptions = {\n enabled: false,\n level: typeof process !== 'undefined' && process.env?.NODE_ENV === 'production' \n ? LogLevel.WARN \n : LogLevel.DEBUG,\n showTimestamp: true,\n enableColors: typeof process !== 'undefined' && process.env?.NODE_ENV !== 'production',\n};\n\n/**\n * 当前日志配置\n */\nlet currentOptions: LoggerOptions = { ...DEFAULT_OPTIONS };\n\n/**\n * 日志收集器回调列表\n */\nconst logCollectors: LogCollectorCallback[] = [];\n\n/**\n * 从环境变量读取日志级别\n */\nfunction getLogLevelFromEnv(): LogLevel | undefined {\n if (typeof process === 'undefined' || !process.env) return undefined;\n \n const envLevel = process.env.VITE_LOG_LEVEL || process.env.LOG_LEVEL;\n if (!envLevel) return undefined;\n\n const levelMap: Record<string, LogLevel> = {\n debug: LogLevel.DEBUG,\n info: LogLevel.INFO,\n warn: LogLevel.WARN,\n error: LogLevel.ERROR,\n };\n\n return levelMap[envLevel.toLowerCase()];\n}\n\n/**\n * 初始化日志配置\n * @param options 日志配置选项\n */\nexport function initLogger(options?: LoggerOptions): void {\n const envLevel = getLogLevelFromEnv();\n \n currentOptions = {\n ...DEFAULT_OPTIONS,\n ...(options || {}),\n // 环境变量优先级最高\n ...(envLevel !== undefined ? { level: envLevel } : {}),\n };\n\n console.log(`[Logger] Initialized with level: ${LEVEL_LABELS[currentOptions.level!]}`);\n}\n\n/**\n * 动态设置日志级别\n * @param level 日志级别\n */\nexport function setLogLevel(level: LogLevel): void {\n currentOptions.level = level;\n}\n\n/**\n * 注册日志收集器\n * @param callback 收集器回调函数\n * @returns 取消注册的函数\n * \n * @example\n * ```typescript\n * // 注册收集器\n * const unsubscribe = addLogCollector((entry) => {\n * if (entry.level === LogLevel.ERROR) {\n * // 上报到监控平台\n * reportToSentry(entry);\n * }\n * });\n * \n * // 取消注册\n * unsubscribe();\n * ```\n */\nexport function addLogCollector(callback: LogCollectorCallback): () => void {\n logCollectors.push(callback);\n \n // 返回取消注册的函数\n return () => {\n const index = logCollectors.indexOf(callback);\n if (index > -1) {\n logCollectors.splice(index, 1);\n }\n };\n}\n\n/**\n * 清除所有日志收集器\n */\nexport function clearLogCollectors(): void {\n logCollectors.length = 0;\n}\n\n/**\n * 格式化日志消息\n * @param level 日志级别\n * @param module 模块名称\n * @param message 消息内容\n * @param args 额外参数\n */\nfunction formatMessage(\n level: LogLevel,\n module: string,\n message: string,\n args: any[]\n): string {\n const parts: string[] = [];\n\n // 时间戳\n if (currentOptions.showTimestamp) {\n const now = new Date();\n // ✅ 使用本地时间格式化,而不是 toISOString()(UTC 时间)\n const year = now.getFullYear();\n const month = String(now.getMonth() + 1).padStart(2, '0');\n const day = String(now.getDate()).padStart(2, '0');\n const hours = String(now.getHours()).padStart(2, '0');\n const minutes = String(now.getMinutes()).padStart(2, '0');\n const seconds = String(now.getSeconds()).padStart(2, '0');\n const milliseconds = String(now.getMilliseconds()).padStart(3, '0');\n const timeStr = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${milliseconds}`;\n parts.push(`[${timeStr}]`);\n }\n\n // 级别标签\n const label = LEVEL_LABELS[level];\n if (currentOptions.enableColors) {\n parts.push(`${LEVEL_COLORS[level]}[${label}]${RESET_COLOR}`);\n } else {\n parts.push(`[${label}]`);\n }\n\n // 模块名称\n parts.push(`[${module}]`);\n\n // 消息内容\n parts.push(message);\n\n return parts.join(' ');\n}\n\n/**\n * 提取错误堆栈\n * @param arg 参数\n * @returns 堆栈字符串或 null\n */\nfunction extractStack(arg: any): string | null {\n if (arg instanceof Error) {\n return arg.stack || arg.message;\n }\n return null;\n}\n\n/**\n * 输出日志并触发收集器\n * @param level 日志级别\n * @param module 模块名称\n * @param message 消息内容\n * @param args 额外参数\n */\nfunction log(level: LogLevel, module: string, message: string, ...args: any[]): void {\n // 检查是否启用日志\n if (!currentOptions.enabled) {\n return;\n }\n\n // 检查日志级别\n if (level < (currentOptions.level ?? LogLevel.DEBUG)) {\n return;\n }\n\n const formattedMessage = formatMessage(level, module, message, args);\n\n // ✅ 关键使用 Error().stack 获取真实的调用位置\n // 这样浏览器控制台的代码跳转会指向调用日志的位置,而不是 logger 文件\n let stackInfo: string | undefined;\n try {\n const error = new Error();\n const stackLines = error.stack?.split('\\n') || [];\n // stack 格式: \n // Error\n // at log (logger/index.ts:xxx:yy) ← 当前函数\n // at Object.info (logger/index.ts:xxx:yy) ← logger 对象方法\n // at actualCaller (caller.ts:xxx:yy) ← 真实调用者(我们要这个!)\n // 跳过前两行,取第三行作为调用位置\n if (stackLines.length >= 3) {\n stackInfo = stackLines[3].trim();\n }\n } catch (e) {\n // 忽略错误\n }\n\n // ✅ 将调用位置信息添加到参数中\n // 浏览器控制台会自动解析这个位置并支持点击跳转\n const outputArgs = stackInfo ? [...args, `\\n ${stackInfo}`] : args;\n\n // 根据级别选择输出方法\n switch (level) {\n case LogLevel.DEBUG:\n console.debug(formattedMessage, ...outputArgs);\n break;\n case LogLevel.INFO:\n console.info(formattedMessage, ...outputArgs);\n break;\n case LogLevel.WARN:\n console.warn(formattedMessage, ...outputArgs);\n break;\n case LogLevel.ERROR:\n console.error(formattedMessage, ...outputArgs);\n break;\n }\n\n // 触发日志收集器\n if (logCollectors.length > 0) {\n const entry: LogEntry = {\n timestamp: Date.now(),\n level,\n module,\n message,\n args,\n };\n\n // 提取错误堆栈\n const stack = args.find(extractStack);\n if (stack) {\n entry.stack = extractStack(stack) || undefined;\n }\n\n // 异步调用所有收集器\n logCollectors.forEach((collector) => {\n try {\n const result = collector(entry);\n // 如果返回 Promise,捕获可能的错误\n if (result instanceof Promise) {\n result.catch((error) => {\n console.error('[Logger] Collector error:', error);\n });\n }\n } catch (error) {\n console.error('[Logger] Collector error:', error);\n }\n });\n }\n}\n\n/**\n * 日志工具对象\n */\nexport const logger = {\n /**\n * 调试日志(仅开发环境)\n * @param module 模块名称\n * @param message 消息内容\n * @param args 额外参数\n */\n debug: (module: string, message: string, ...args: any[]) => {\n message = `🔍 ${message}`;\n log(LogLevel.DEBUG, module, message, ...args);\n },\n\n /**\n * 信息日志\n * @param module 模块名称\n * @param message 消息内容\n * @param args 额外参数\n */\n info: (module: string, message: string, ...args: any[]) => {\n message = `✅ ${message}`;\n log(LogLevel.INFO, module, message, ...args);\n },\n\n /**\n * 警告日志\n * @param module 模块名称\n * @param message 消息内容\n * @param args 额外参数\n */\n warn: (module: string, message: string, ...args: any[]) => {\n message = `⚠️ ${message}`;\n log(LogLevel.WARN, module, message, ...args);\n },\n\n /**\n * 错误日志\n * @param module 模块名称\n * @param message 消息内容\n * @param args 额外参数\n */\n error: (module: string, message: string, ...args: any[]) => {\n message = `❌ ${message}`;\n log(LogLevel.ERROR, module, message, ...args);\n },\n};\n\n","/**\n * IndexedDB 文件缓存工具\n * 用于持久化存储 File 对象,支持断点续传时的文件恢复\n */\n\nimport { formatFileSize } from '.';\nimport { logger } from './logger';\n\nconst DB_NAME = 'file-ud-cache';\nconst DB_VERSION = 3;\nconst STORE_NAME = 'files';\nconst DOWNLOAD_PROGRESS_STORE = 'download-progress';\nconst FILE_HANDLE_STORE = 'file-handles';\n\n/**\n * 文件缓存记录结构\n */\ninterface FileCacheRecord {\n fileHash: string; // 文件哈希(主键)\n fileName: string; // 文件名\n fileType: string; // 文件类型\n fileSize: number; // 文件大小\n data: ArrayBuffer; // 文件二进制数据\n createdAt: number; // 创建时间戳\n lastAccessedAt: number; // 最后访问时间戳\n}\n\n// ✅ 数据库连接单例\nlet dbInstance: IDBDatabase | null = null;\nlet dbOpeningPromise: Promise<IDBDatabase> | null = null;\n\n/**\n * 打开或创建 IndexedDB 数据库(单例模式)\n */\nfunction openDB(): Promise<IDBDatabase> {\n // ✅ 如果已有有效连接,直接返回\n if (dbInstance && dbInstance.version === DB_VERSION) {\n try {\n // 测试连接是否仍然有效\n dbInstance.transaction([STORE_NAME], 'readonly');\n return Promise.resolve(dbInstance);\n } catch (error) {\n // 连接已失效,重置\n logger.warn('FileCache', '数据库连接已失效,重新打开', error);\n dbInstance = null;\n dbOpeningPromise = null;\n }\n }\n\n // ✅ 如果正在打开中,返回同一个 Promise\n if (dbOpeningPromise) {\n return dbOpeningPromise;\n }\n\n // ✅ 创建新的打开请求\n dbOpeningPromise = new Promise((resolve, reject) => {\n const request = indexedDB.open(DB_NAME, DB_VERSION);\n\n request.onerror = () => {\n logger.error('FileCache', '打开 IndexedDB 失败', request.error);\n dbOpeningPromise = null;\n reject(request.error);\n };\n\n request.onsuccess = () => {\n dbInstance = request.result;\n dbOpeningPromise = null;\n \n // ✅ 监听连接关闭事件\n dbInstance.onclose = () => {\n logger.warn('FileCache', '数据库连接已关闭');\n dbInstance = null;\n };\n \n // ✅ 监听连接异常事件\n dbInstance.onerror = (event) => {\n logger.error('FileCache', '数据库连接错误', event);\n dbInstance = null;\n };\n \n resolve(dbInstance);\n };\n\n request.onupgradeneeded = (event) => {\n const db = (event.target as IDBOpenDBRequest).result;\n\n // 创建文件缓存存储空间\n if (!db.objectStoreNames.contains(STORE_NAME)) {\n const store = db.createObjectStore(STORE_NAME, { keyPath: 'fileHash' });\n\n // 创建索引\n store.createIndex('fileName', 'fileName', { unique: false });\n store.createIndex('createdAt', 'createdAt', { unique: false });\n store.createIndex('lastAccessedAt', 'lastAccessedAt', { unique: false });\n\n logger.info('FileCache', 'IndexedDB 文件缓存存储初始化成功');\n }\n\n // 创建下载进度存储空间\n if (!db.objectStoreNames.contains(DOWNLOAD_PROGRESS_STORE)) {\n const progressStore = db.createObjectStore(DOWNLOAD_PROGRESS_STORE, { keyPath: 'fileHash' });\n progressStore.createIndex('updatedAt', 'updatedAt', { unique: false });\n\n logger.info('FileCache', 'IndexedDB 下载进度存储初始化成功');\n }\n\n // 🔑 创建文件句柄存储空间(跨会话复用 File Handle)\n if (!db.objectStoreNames.contains(FILE_HANDLE_STORE)) {\n const handleStore = db.createObjectStore(FILE_HANDLE_STORE, { keyPath: 'fileName' });\n handleStore.createIndex('updatedAt', 'updatedAt', { unique: false });\n\n logger.info('FileCache', 'IndexedDB 文件句柄存储初始化成功');\n }\n };\n });\n\n return dbOpeningPromise;\n}\n\n/**\n * 关闭数据库连接(用于清理资源)\n */\nexport function closeDB(): void {\n if (dbInstance) {\n try {\n dbInstance.close();\n logger.debug('FileCache', '数据库连接已关闭');\n } catch (error) {\n logger.warn('FileCache', '关闭数据库连接时出错', error);\n } finally {\n dbInstance = null;\n dbOpeningPromise = null;\n }\n }\n}\n\n/**\n * 执行 IndexedDB 事务的通用包装器\n * @param operation - 事务操作函数\n * @returns Promise<T>\n */\nasync function executeTransaction<T>(\n operation: (db: IDBDatabase) => Promise<T>\n): Promise<T> {\n try {\n const db = await openDB();\n return await operation(db);\n } catch (error) {\n // ✅ 捕获 InvalidStateError,重置连接并重试一次\n if (error instanceof DOMException && error.name === 'InvalidStateError') {\n logger.warn('FileCache', '检测到无效状态错误,重置连接后重试', error);\n closeDB();\n \n // 重试一次\n const db = await openDB();\n return await operation(db);\n }\n throw error;\n }\n}\n\n/**\n * 保存 File 对象到 IndexedDB\n * \n * @param fileHash - 文件哈希值(作为主键)\n * @param file - File 对象\n * @returns Promise<void>\n */\nexport async function saveFileToCache(fileHash: string, file: File): Promise<void> {\n try {\n // 将 File 转换为 ArrayBuffer\n const arrayBuffer = await file.arrayBuffer();\n \n const record: FileCacheRecord = {\n fileHash,\n fileName: file.name,\n fileType: file.type,\n fileSize: file.size,\n data: arrayBuffer,\n createdAt: Date.now(),\n lastAccessedAt: Date.now(),\n };\n\n await executeTransaction(async (db) => {\n return new Promise<void>((resolve, reject) => {\n const transaction = db.transaction([STORE_NAME], 'readwrite');\n const store = transaction.objectStore(STORE_NAME);\n const request = store.put(record);\n\n request.onsuccess = () => {\n logger.debug('FileCache', `文件已缓存: ${file.name} (${formatFileSize(file.size)})`, {\n fileHash,\n fileSize: file.size,\n });\n resolve();\n };\n\n request.onerror = () => {\n logger.error('FileCache', '保存文件缓存失败', request.error);\n reject(request.error);\n };\n \n transaction.onerror = () => {\n logger.error('FileCache', '事务执行失败', transaction.error);\n reject(transaction.error);\n };\n });\n });\n } catch (error) {\n logger.error('FileCache', '保存文件缓存异常', error);\n throw error;\n }\n}\n\n/**\n * 从 IndexedDB 恢复 File 对象\n * \n * @param fileHash - 文件哈希值\n * @returns Promise<File | null> - 如果找到则返回 File 对象,否则返回 null\n */\nexport async function restoreFileFromCache(fileHash: string): Promise<File | null> {\n try {\n return await executeTransaction(async (db) => {\n return new Promise((resolve, reject) => {\n const transaction = db.transaction([STORE_NAME], 'readonly');\n const store = transaction.objectStore(STORE_NAME);\n const request = store.get(fileHash);\n\n request.onsuccess = () => {\n const record: FileCacheRecord | undefined = request.result;\n \n if (!record) {\n logger.debug('FileCache', `未找到缓存文件: ${fileHash}`);\n resolve(null);\n return;\n }\n\n // 更新最后访问时间\n updateLastAccessedTime(fileHash);\n\n // 将 ArrayBuffer 转换回 File\n const blob = new Blob([record.data], { type: record.fileType });\n const file = new File([blob], record.fileName, { type: record.fileType });\n\n logger.debug('FileCache', `成功恢复缓存文件: ${record.fileName}`, {\n fileHash,\n fileSize: record.fileSize,\n });\n\n resolve(file);\n };\n\n request.onerror = () => {\n logger.error('FileCache', '恢复文件缓存失败', request.error);\n reject(request.error);\n };\n \n transaction.onerror = () => {\n logger.error('FileCache', '事务执行失败', transaction.error);\n reject(transaction.error);\n };\n });\n });\n } catch (error) {\n logger.error('FileCache', '恢复文件缓存异常', error);\n throw error;\n }\n}\n\n/**\n * 删除指定文件的缓存\n * \n * @param fileHash - 文件哈希值\n * @returns Promise<void>\n */\nexport async function removeFileFromCache(fileHash: string): Promise<void> {\n try {\n await executeTransaction(async (db) => {\n return new Promise<void>((resolve, reject) => {\n const transaction = db.transaction([STORE_NAME], 'readwrite');\n const store = transaction.objectStore(STORE_NAME);\n const request = store.delete(fileHash);\n\n request.onsuccess = () => {\n logger.debug('FileCache', `已删除文件缓存: ${fileHash}`);\n resolve();\n };\n\n request.onerror = () => {\n logger.error('FileCache', '删除文件缓存失败', request.error);\n reject(request.error);\n };\n \n transaction.onerror = () => {\n logger.error('FileCache', '事务执行失败', transaction.error);\n reject(transaction.error);\n };\n });\n });\n } catch (error) {\n logger.error('FileCache', '删除文件缓存异常', error);\n throw error;\n }\n}\n\n/**\n * 清空所有文件缓存\n * \n * @returns Promise<void>\n */\nexport async function clearAllFileCache(): Promise<void> {\n try {\n await executeTransaction(async (db) => {\n return new Promise<void>((resolve, reject) => {\n const transaction = db.transaction([STORE_NAME], 'readwrite');\n const store = transaction.objectStore(STORE_NAME);\n const request = store.clear();\n\n request.onsuccess = () => {\n logger.info('FileCache', '已清空所有文件缓存');\n resolve();\n };\n\n request.onerror = () => {\n logger.error('FileCache', '清空文件缓存失败', request.error);\n reject(request.error);\n };\n \n transaction.onerror = () => {\n logger.error('FileCache', '事务执行失败', transaction.error);\n reject(transaction.error);\n };\n });\n });\n } catch (error) {\n logger.error('FileCache', '清空文件缓存异常', error);\n throw error;\n }\n}\n\n/**\n * 获取缓存统计信息\n * \n * @returns Promise<{ count: number; totalSize: number }>\n */\nexport async function getCacheStats(): Promise<{ count: number; totalSize: number }> {\n try {\n return await executeTransaction(async (db) => {\n return new Promise((resolve, reject) => {\n const transaction = db.transaction([STORE_NAME], 'readonly');\n const store = transaction.objectStore(STORE_NAME);\n const request = store.getAll();\n\n request.onsuccess = () => {\n const records: FileCacheRecord[] = request.result || [];\n const totalSize = records.reduce((sum, record) => sum + record.fileSize, 0);\n \n resolve({\n count: records.length,\n totalSize,\n });\n };\n\n request.onerror = () => {\n logger.error('FileCache', '获取缓存统计失败', request.error);\n reject(request.error);\n };\n \n transaction.onerror = () => {\n logger.error('FileCache', '事务执行失败', transaction.error);\n reject(transaction.error);\n };\n });\n });\n } catch (error) {\n logger.error('FileCache', '获取缓存统计异常', error);\n throw error;\n }\n}\n\n/**\n * 清理过期缓存(超过指定天数的缓存)\n * \n * @param days - 保留天数,默认 7 天\n * @returns Promise<number> - 删除的记录数\n */\nexport async function cleanExpiredCache(days: number = 7): Promise<number> {\n try {\n const cutoffTime = Date.now() - days * 24 * 60 * 60 * 1000;\n\n return await executeTransaction(async (db) => {\n return new Promise((resolve, reject) => {\n const transaction = db.transaction([STORE_NAME], 'readwrite');\n const store = transaction.objectStore(STORE_NAME);\n const index = store.index('lastAccessedAt');\n const request = index.getAll();\n\n request.onsuccess = () => {\n const records: FileCacheRecord[] = request.result || [];\n const expiredRecords = records.filter(\n (record) => record.lastAccessedAt < cutoffTime\n );\n\n let deletedCount = 0;\n\n // 删除过期记录\n const deletePromises = expiredRecords.map((record) => {\n return new Promise<void>((resolveDelete, rejectDelete) => {\n const deleteRequest = store.delete(record.fileHash);\n deleteRequest.onsuccess = () => {\n deletedCount++;\n resolveDelete();\n };\n deleteRequest.onerror = () => {\n logger.error('FileCache', `删除过期缓存失败: ${record.fileHash}`, deleteRequest.error);\n resolveDelete(); // 继续处理其他记录\n };\n });\n });\n\n Promise.all(deletePromises).then(() => {\n if (deletedCount > 0) {\n logger.info('FileCache', `已清理 ${deletedCount} 个过期缓存`);\n }\n resolve(deletedCount);\n });\n };\n\n request.onerror = () => {\n logger.error('FileCache', '查询过期缓存失败', request.error);\n reject(request.error);\n };\n \n transaction.onerror = () => {\n logger.error('FileCache', '事务执行失败', transaction.error);\n reject(transaction.error);\n };\n });\n });\n } catch (error) {\n logger.error('FileCache', '清理过期缓存异常', error);\n throw error;\n }\n}\n\n/**\n * 更新文件的最后访问时间\n * \n * @param fileHash - 文件哈希值\n */\nasync function updateLastAccessedTime(fileHash: string): Promise<void> {\n try {\n await executeTransaction(async (db) => {\n return new Promise<void>((resolve, reject) => {\n const transaction = db.transaction([STORE_NAME], 'readwrite');\n const store = transaction.objectStore(STORE_NAME);\n const request = store.get(fileHash);\n\n request.onsuccess = () => {\n const record: FileCacheRecord | undefined = request.result;\n \n if (record) {\n record.lastAccessedAt = Date.now();\n const updateRequest = store.put(record);\n \n updateRequest.onsuccess = () => resolve();\n updateRequest.onerror = () => {\n logger.warn('FileCache', '更新访问时间失败', updateRequest.error);\n resolve(); // 不阻塞主流程\n };\n } else {\n resolve();\n }\n };\n\n request.onerror = () => {\n logger.warn('FileCache', '查询记录失败', request.error);\n resolve(); // 不阻塞主流程\n };\n \n transaction.onerror = () => {\n logger.warn('FileCache', '事务执行失败', transaction.error);\n resolve(); // 不阻塞主流程\n };\n });\n });\n } catch (error) {\n logger.warn('FileCache', '更新访问时间异常', error);\n // 不抛出错误,避免影响主流程\n }\n}\n\n// ==================== 下载进度缓存 ====================\n\n/**\n * 下载进度缓存记录结构\n */\ninterface DownloadProgressRecord {\n fileHash: string; // 文件唯一标识\n fileName: string;\n fileSize: number;\n totalChunks: number;\n completedChunks: number;\n chunkIndexes: number[]; // 已下载的分片索引\n updatedAt: number;\n}\n\n/**\n * 保存下载进度到 IndexedDB\n */\nexport async function saveDownloadProgress(\n fileHash: string,\n fileName: string,\n fileSize: number,\n totalChunks: number,\n chunkIndexes: number[],\n): Promise<void> {\n try {\n const record: DownloadProgressRecord = {\n fileHash,\n fileName,\n fileSize,\n totalChunks,\n completedChunks: chunkIndexes.length,\n chunkIndexes,\n updatedAt: Date.now(),\n };\n\n await executeTransaction(async (db) => {\n return new Promise<void>((resolve, reject) => {\n const transaction = db.transaction([DOWNLOAD_PROGRESS_STORE], 'readwrite');\n const store = transaction.objectStore(DOWNLOAD_PROGRESS_STORE);\n const request = store.put(record);\n\n request.onsuccess = () => {\n logger.debug('FileCache', `下载进度已保存: ${fileName} (${chunkIndexes.length}/${totalChunks})`);\n resolve();\n };\n\n request.onerror = () => {\n logger.warn('FileCache', '保存下载进度失败', request.error);\n reject(request.error);\n };\n\n transaction.onerror = () => {\n logger.warn('FileCache', '事务执行失败', transaction.error);\n reject(transaction.error);\n };\n });\n });\n } catch (error) {\n logger.warn('FileCache', '保存下载进度异常', error);\n }\n}\n\n/**\n * 从 IndexedDB 加载下载进度\n * @returns 已完成分片索引数组,未找到则返回 null\n */\nexport async function loadDownloadProgress(\n fileHash: string,\n): Promise<{\n fileName: string;\n fileSize: number;\n totalChunks: number;\n chunkIndexes: number[];\n} | null> {\n try {\n return await executeTransaction(async (db) => {\n return new Promise((resolve, reject) => {\n const transaction = db.transaction([DOWNLOAD_PROGRESS_STORE], 'readonly');\n const store = transaction.objectStore(DOWNLOAD_PROGRESS_STORE);\n const request = store.get(fileHash);\n\n request.onsuccess = () => {\n const record: DownloadProgressRecord | undefined = request.result;\n\n if (!record) {\n logger.debug('FileCache', `未找到下载进度: ${fileHash}`);\n resolve(null);\n return;\n }\n\n logger.info('FileCache', `加载下载进度: ${record.fileName} (${record.completedChunks}/${record.totalChunks})`);\n\n resolve({\n fileName: record.fileName,\n fileSize: record.fileSize,\n totalChunks: record.totalChunks,\n chunkIndexes: record.chunkIndexes,\n });\n };\n\n request.onerror = () => {\n logger.warn('FileCache', '加载下载进度失败', request.error);\n reject(request.error);\n };\n\n transaction.onerror = () => {\n logger.warn('FileCache', '事务执行失败', transaction.error);\n reject(transaction.error);\n };\n });\n });\n } catch (error) {\n logger.warn('FileCache', '加载下载进度异常', error);\n return null;\n }\n}\n\n/**\n * 删除指定文件的下载进度记录\n */\nexport async function removeDownloadProgress(fileHash: string): Promise<void> {\n try {\n await executeTransaction(async (db) => {\n return new Promise<void>((resolve, reject) => {\n const transaction = db.transaction([DOWNLOAD_PROGRESS_STORE], 'readwrite');\n const store = transaction.objectStore(DOWNLOAD_PROGRESS_STORE);\n const request = store.delete(fileHash);\n\n request.onsuccess = () => {\n logger.debug('FileCache', `已删除下载进度: ${fileHash}`);\n resolve();\n };\n\n request.onerror = () => {\n logger.warn('FileCache', '删除下载进度失败', request.error);\n reject(request.error);\n };\n\n transaction.onerror = () => {\n logger.warn('FileCache', '事务执行失败', transaction.error);\n reject(transaction.error);\n };\n });\n });\n } catch (error) {\n logger.warn('FileCache', '删除下载进度异常', error);\n }\n}\n\n// ==================== 文件句柄持久化(跨会话复用 File System Access Handle) ====================\n\n/**\n * 文件句柄缓存记录结构\n */\ninterface FileHandleRecord {\n fileName: string; // 文件名(主键)\n fileHandle: FileSystemFileHandle; // 文件系统句柄(IndexedDB 可序列化)\n updatedAt: number;\n}\n\n/**\n * 将 FileSystemFileHandle 持久化到 IndexedDB(跨页面刷新复用)\n *\n * 关键是避免重复调用 showSaveFilePicker() 导致 Chrome 截断已有文件为 0 字节。\n */\nexport async function saveFileHandle(\n fileName: string,\n fileHandle: FileSystemFileHandle,\n): Promise<void> {\n try {\n const record: FileHandleRecord = {\n fileName,\n fileHandle,\n updatedAt: Date.now(),\n };\n\n await executeTransaction(async (db) => {\n return new Promise<void>((resolve, reject) => {\n const transaction = db.transaction([FILE_HANDLE_STORE], 'readwrite');\n const store = transaction.objectStore(FILE_HANDLE_STORE);\n const request = store.put(record);\n\n request.onsuccess = () => {\n logger.debug('FileCache', `文件句柄已持久化: ${fileName}`);\n resolve();\n };\n\n request.onerror = () => {\n logger.warn('FileCache', '保存文件句柄失败', request.error);\n reject(request.error);\n };\n\n transaction.onerror = () => {\n logger.warn('FileCache', '事务执行失败', transaction.error);\n reject(transaction.error);\n };\n });\n });\n } catch (error) {\n logger.warn('FileCache', '保存文件句柄异常', error);\n }\n}\n\n/**\n * 从 IndexedDB 加载持久化的 FileSystemFileHandle\n * @returns FileSystemFileHandle(未找到或已失效则返回 null)\n */\nexport async function loadFileHandle(\n fileName: string,\n): Promise<FileSystemFileHandle | null> {\n try {\n return await executeTransaction(async (db) => {\n return new Promise((resolve, reject) => {\n const transaction = db.transaction([FILE_HANDLE_STORE], 'readonly');\n const store = transaction.objectStore(FILE_HANDLE_STORE);\n const request = store.get(fileName);\n\n request.onsuccess = () => {\n const record: FileHandleRecord | undefined = request.result;\n\n if (!record) {\n logger.debug('FileCache', `未找到持久化文件句柄: ${fileName}`);\n resolve(null);\n return;\n }\n\n // 🔑 验证句柄仍有效\n try {\n // 简单的权限检查:尝试获取文件信息\n record.fileHandle.getFile().then(\n () => {\n logger.debug('FileCache', `文件句柄有效: ${fileName}`);\n resolve(record.fileHandle);\n },\n () => {\n logger.warn('FileCache', `文件句柄已失效(权限撤销): ${fileName}`);\n // 异步清理失效句柄\n removeFileHandle(fileName);\n resolve(null);\n },\n );\n } catch {\n // 同步方式失败\n removeFileHandle(fileName);\n resolve(null);\n }\n };\n\n request.onerror = () => {\n logger.warn('FileCache', '加载文件句柄失败', request.error);\n reject(request.error);\n };\n\n transaction.onerror = () => {\n logger.warn('FileCache', '事务执行失败', transaction.error);\n reject(transaction.error);\n };\n });\n });\n } catch (error) {\n logger.warn('FileCache', '加载文件句柄异常', error);\n return null;\n }\n}\n\n/**\n * 删除指定文件的句柄记录\n */\nexport async function removeFileHandle(fileName: string): Promise<void> {\n try {\n await executeTransaction(async (db) => {\n return new Promise<void>((resolve, reject) => {\n const transaction = db.transaction([FILE_HANDLE_STORE], 'readwrite');\n const store = transaction.objectStore(FILE_HANDLE_STORE);\n const request = store.delete(fileName);\n\n request.onsuccess = () => {\n logger.debug('FileCache', `已删除文件句柄: ${fileName}`);\n resolve();\n };\n\n request.onerror = () => {\n logger.warn('FileCache', '删除文件句柄失败', request.error);\n reject(request.error);\n };\n\n transaction.onerror = () => {\n logger.warn('FileCache', '事务执行失败', transaction.error);\n reject(transaction.error);\n };\n });\n });\n } catch (error) {\n logger.warn('FileCache', '删除文件句柄异常', error);\n }\n}\n","/**\n * 上传性能监控模块\n * \n * 提供上传成功率、耗时统计、错误分析等监控功能\n * \n * @example\n * ```typescript\n * import { uploadMonitor } from '@core/utils';\n * \n * // 获取实时统计数据\n * console.log(uploadMonitor.getStats());\n * \n * // 重置统计数据\n * uploadMonitor.reset();\n * ```\n */\n\nimport { addLogCollector, LogLevel, LogEntry } from '../logger';\n\n/**\n * 单个文件的上传记录\n */\nexport interface UploadRecord {\n /** 文件唯一标识 */\n fileId: string;\n /** 文件名 */\n fileName: string;\n /** 文件大小(字节) */\n fileSize: number;\n /** 开始时间戳 */\n startTime: number;\n /** 结束时间戳 */\n endTime?: number;\n /** 是否成功 */\n success: boolean;\n /** 失败原因(如果失败) */\n error?: string;\n /** 重试次数 */\n retryCount: number;\n /** 分片总数(如果是分片上传) */\n totalChunks?: number;\n /** 成功分片数 */\n chunks?: number;\n /** 失败分片数 */\n failedChunks?: number;\n /** ✅ 各模块的错误次数统计 */\n errorModules?: Map<string, number>;\n}\n\n/**\n * 上传统计数据\n */\nexport interface UploadStats {\n /** 总上传数 */\n totalUploads: number;\n /** 成功上传数 */\n successfulUploads: number;\n /** 失败上传数 */\n failedUploads: number;\n /** 成功率(0-100) */\n successRate: number;\n /** 平均耗时(毫秒) */\n averageDuration: number;\n /** 最短耗时(毫秒) */\n minDuration: number;\n /** 最长耗时(毫秒) */\n maxDuration: number;\n /** 中位数耗时(毫秒) */\n medianDuration: number;\n /** 平均文件大小(字节) */\n averageFileSize: number;\n /** 总上传数据量(字节) */\n totalBytesUploaded: number;\n /** 平均上传速度(字节/秒) */\n averageSpeed: number;\n /** 按错误类型统计 */\n errorsByType: Map<string, number>;\n /** 按模块统计错误 */\n errorsByModule: Map<string, number>;\n /** 平均重试次数 */\n averageRetries: number;\n /** 分片上传统计 */\n chunkUploadStats?: {\n /** 总分片数 */\n totalChunks: number;\n /** 成功分片数 */\n successfulChunks: number;\n /** 失败分片数 */\n failedChunks: number;\n /** 分片成功率 */\n chunkSuccessRate: number;\n };\n}\n\n/**\n * 上传性能监控类\n */\nclass UploadMonitor {\n /** 上传记录列表 */\n private records: UploadRecord[] = [];\n \n /** 当前活跃的上传任务 */\n private activeUploads = new Map<string, UploadRecord>();\n \n /** 最大保留记录数 */\n private readonly MAX_RECORDS = 1000;\n \n constructor() {\n this.init();\n }\n \n /**\n * 初始化日志收集器\n */\n private init() {\n addLogCollector((entry) => {\n this.processLog(entry);\n });\n }\n \n /**\n * 处理日志条目\n */\n private processLog(entry: LogEntry) {\n // ✅ 调试:打印所有接收到的日志\n if (entry.message.includes('上传') || entry.message.includes('文件')) {\n console.log('[Monitor Debug] 收到日志:', {\n module: entry.module,\n message: entry.message,\n args: entry.args,\n timestamp: entry.timestamp,\n });\n }\n \n // ✅ 追踪上传开始(支持 uploadChunkManager 分片上传 和 UploadFile 普通上传)\n if (entry.message.includes('开始上传文件') && !entry.message.includes('分片')) {\n const fileId = this.extractFileId(entry);\n console.log('[Monitor Debug] 匹配到开始上传:', { fileId, hasFileId: !!fileId });\n // ✅ 基于 fileId 去重:如果已经有记录,跳过(避免重复统计)\n if (fileId && !this.activeUploads.has(fileId)) {\n const record: UploadRecord = {\n fileId,\n fileName: this.extractFileName(entry) || 'unknown',\n fileSize: this.extractFileSize(entry) || 0,\n startTime: entry.timestamp,\n success: false,\n retryCount: 0,\n };\n \n this.activeUploads.set(fileId, record);\n }\n }\n \n // ✅ 追踪上传成功(支持文件级别的成功)\n if (entry.message.includes('文件传输成功')) {\n const fileId = this.extractFileId(entry);\n console.log('[Monitor Debug] 匹配到上传成功:', { fileId, hasFileId: !!fileId });\n if (fileId) {\n // ✅ 如果 activeUploads 中没有记录,创建新记录(兜底逻辑)\n if (!this.activeUploads.has(fileId)) {\n const record: UploadRecord = {\n fileId,\n fileName: this.extractFileName(entry) || 'unknown',\n fileSize: this.extractFileSize(entry) || 0,\n startTime: 0, // ✅ 标记为未知,避免错误的耗时计算\n endTime: entry.timestamp,\n success: true,\n retryCount: 0,\n };\n \n // ✅ 直接完成记录,不经过 activeUploads\n this.finalizeRecord(record);\n return; // 提前返回,不再执行后续逻辑\n }\n \n const record = this.activeUploads.get(fileId)!;\n record.endTime = entry.timestamp;\n record.success = true;\n \n // ✅ 如果 fileSize 为 0,尝试从日志中提取(兜底)\n if (record.fileSize === 0) {\n const extractedSize = this.extractFileSize(entry);\n if (extractedSize) {\n record.fileSize = extractedSize;\n }\n }\n \n this.finalizeRecord(record);\n this.activeUploads.delete(fileId);\n }\n }\n \n // 追踪上传失败\n if (entry.level === LogLevel.ERROR) {\n const fileId = this.extractFileId(entry);\n \n // uploadChunkManager 级别的错误(分片上传)\n if (entry.module === 'uploadChunkManager') {\n if (fileId && this.activeUploads.has(fileId)) {\n const record = this.activeUploads.get(fileId)!;\n \n // ✅ 记录 uploadChunkManager 模块的错误\n if (!record.errorModules) {\n record.errorModules = new Map<string, number>();\n }\n record.errorModules.set('uploadChunkManager', (record.errorModules.get('uploadChunkManager') || 0) + 1);\n \n // 统计分片失败\n if (entry.message.includes('分片') && entry.message.includes('失败')) {\n record.failedChunks = (record.failedChunks || 0) + 1;\n }\n \n // 如果是最终失败(非重试)\n if (entry.message.includes('最终失败')) {\n record.endTime = entry.timestamp;\n record.success = false;\n record.error = entry.message;\n \n // ✅ 如果 fileSize 为 0,尝试从日志中提取\n if (record.fileSize === 0) {\n const extractedSize = this.extractFileSize(entry);\n if (extractedSize) {\n record.fileSize = extractedSize;\n }\n }\n \n this.finalizeRecord(record);\n this.activeUploads.delete(fileId);\n }\n }\n }\n \n // UploadFile 级别的错误(普通上传或分片上传的文件级别错误)\n if (entry.module === 'UploadFile') {\n if (fileId && this.activeUploads.has(fileId)) {\n const record = this.activeUploads.get(fileId)!;\n \n // ✅ 记录 UploadFile 模块的错误\n if (!record.errorModules) {\n record.errorModules = new Map<string, number>();\n }\n record.errorModules.set('UploadFile', (record.errorModules.get('UploadFile') || 0) + 1);\n \n record.endTime = entry.timestamp;\n record.success = false;\n record.error = entry.message;\n \n // ✅ 如果 fileSize 为 0,尝试从日志中提取\n if (record.fileSize === 0) {\n const extractedSize = this.extractFileSize(entry);\n if (extractedSize) {\n record.fileSize = extractedSize;\n }\n }\n \n this.finalizeRecord(record);\n this.activeUploads.delete(fileId);\n }\n }\n }\n \n // 追踪重试\n if (entry.message.includes('重试')) {\n const fileId = this.extractFileId(entry);\n if (fileId && this.activeUploads.has(fileId)) {\n const record = this.activeUploads.get(fileId)!;\n record.retryCount++;\n }\n }\n \n // 追踪分片信息\n if (entry.module === 'uploadChunkManager' && entry.message.includes('分片切割完成')) {\n const fileId = this.extractFileId(entry);\n if (fileId && this.activeUploads.has(fileId)) {\n const record = this.activeUploads.get(fileId)!;\n const totalChunks = this.extractTotalChunks(entry);\n if (totalChunks) {\n record.totalChunks = totalChunks;\n record.chunks = 0;\n record.failedChunks = 0;\n }\n }\n }\n }\n \n /**\n * 完成记录并添加到历史\n */\n private finalizeRecord(record: UploadRecord) {\n // 计算分片统计\n if (record.totalChunks) {\n record.chunks = record.totalChunks - (record.failedChunks || 0);\n }\n \n // ✅ 去重:检查 records 中是否已存在相同的 fileId\n const existingIndex = this.records.findIndex(r => r.fileId === record.fileId);\n if (existingIndex !== -1) {\n // 如果已存在,替换旧记录(保留最新的)\n this.records[existingIndex] = record;\n } else {\n // 如果不存在,添加新记录\n this.records.push(record);\n }\n \n // 限制记录数量\n if (this.records.length > this.MAX_RECORDS) {\n this.records.shift();\n }\n }\n \n /**\n * 从日志条目中提取 fileId\n */\n private extractFileId(entry: LogEntry): string | null {\n // 尝试从 args 中提取\n if (entry.args?.length) {\n for (const arg of entry.args) {\n if (arg && typeof arg === 'object' && 'fileId' in arg) {\n return String(arg.fileId);\n }\n }\n }\n \n // 尝试从消息中提取(如果有)\n const match = entry.message.match(/fileId[=:]\\s*(\\w+)/);\n if (match) {\n return match[1];\n }\n \n return null;\n }\n \n /**\n * 从日志条目中提取文件名\n */\n private extractFileName(entry: LogEntry): string | null {\n if (entry.args?.length) {\n for (const arg of entry.args) {\n // ✅ 只要 args 中有字符串,就认为是文件名\n if (typeof arg === 'string') {\n return arg;\n }\n }\n }\n return null;\n }\n \n /**\n * 从日志条目中提取文件大小\n */\n private extractFileSize(entry: LogEntry): number | null {\n if (entry.args?.length) {\n for (const arg of entry.args) {\n // ✅ 支持直接的对象属性\n if (arg && typeof arg === 'object' && 'size' in arg && typeof arg.size === 'number') {\n return arg.size;\n }\n // ✅ 支持嵌套对象的 fileSize 属性\n if (arg && typeof arg === 'object' && 'fileSize' in arg && typeof arg.fileSize === 'number') {\n return arg.fileSize;\n }\n }\n }\n return null;\n }\n \n /**\n * 从日志条目中提取分片总数\n */\n private extractTotalChunks(entry: LogEntry): number | null {\n if (entry.args?.length) {\n for (const arg of entry.args) {\n if (arg && typeof arg === 'object' && 'totalChunks' in arg && typeof arg.totalChunks === 'number') {\n return arg.totalChunks;\n }\n }\n }\n \n // 尝试从消息中提取\n const match = entry.message.match(/(\\d+)\\s*\\/\\s*\\d+/);\n if (match) {\n return parseInt(match[1], 10);\n }\n \n return null;\n }\n \n /**\n * 获取上传统计数据\n */\n public getStats(): UploadStats {\n const completedRecords = this.records.filter(r => r.endTime);\n \n if (completedRecords.length === 0) {\n return {\n totalUploads: 0,\n successfulUploads: 0,\n failedUploads: 0,\n successRate: 0,\n averageDuration: 0,\n minDuration: 0,\n maxDuration: 0,\n medianDuration: 0,\n averageFileSize: 0,\n totalBytesUploaded: 0,\n averageSpeed: 0,\n errorsByType: new Map(),\n errorsByModule: new Map(),\n averageRetries: 0,\n };\n }\n \n const successful = completedRecords.filter(r => r.success);\n const failed = completedRecords.filter(r => !r.success);\n \n // ✅ 计算耗时统计:只保留有真实开始时间的记录(startTime > 0)\n const durations = completedRecords\n .filter(r => r.startTime > 0 && r.endTime) // ✅ 过滤掉 startTime = 0 的兜底记录\n .map(r => r.endTime! - r.startTime)\n .filter(d => d > 0); // 再次过滤掉异常数据\n \n const averageDuration = durations.length > 0\n ? Math.round(durations.reduce((a, b) => a + b, 0) / durations.length)\n : 0;\n \n const minDuration = durations.length > 0 ? Math.min(...durations) : 0;\n const maxDuration = durations.length > 0 ? Math.max(...durations) : 0;\n \n // 计算中位数\n const sortedDurations = [...durations].sort((a, b) => a - b);\n const medianDuration = sortedDurations.length > 0\n ? sortedDurations[Math.floor(sortedDurations.length / 2)]\n : 0;\n \n // 计算文件大小统计\n const fileSizes = completedRecords.map(r => r.fileSize).filter(s => s > 0);\n const averageFileSize = fileSizes.length > 0\n ? Math.round(fileSizes.reduce((a, b) => a + b, 0) / fileSizes.length)\n : 0;\n \n const totalBytesUploaded = successful.reduce((sum, r) => sum + r.fileSize, 0);\n \n // ✅ 计算平均上传速度:只基于有真实耗时的记录\n const totalDuration = durations.reduce((a, b) => a + b, 0);\n const averageSpeed = totalDuration > 0\n ? Math.round((totalBytesUploaded / (totalDuration / 1000)))\n : 0;\n \n // 统计错误类型\n const errorsByType = new Map<string, number>();\n const errorsByModule = new Map<string, number>();\n \n failed.forEach(record => {\n if (record.error) {\n // 按错误类型统计\n const errorType = this.classifyError(record.error);\n errorsByType.set(errorType, (errorsByType.get(errorType) || 0) + 1);\n }\n \n // ✅ 按模块统计错误数\n if (record.errorModules) {\n record.errorModules.forEach((count, module) => {\n errorsByModule.set(module, (errorsByModule.get(module) || 0) + count);\n });\n }\n });\n \n // 计算平均重试次数\n const totalRetries = completedRecords.reduce((sum, r) => sum + r.retryCount, 0);\n const averageRetries = completedRecords.length > 0\n ? Math.round((totalRetries / completedRecords.length) * 100) / 100\n : 0;\n \n // 分片上传统计\n const chunkRecords = completedRecords.filter(r => r.totalChunks);\n let chunkUploadStats: UploadStats['chunkUploadStats'] | undefined;\n \n if (chunkRecords.length > 0) {\n const totalChunks = chunkRecords.reduce((sum, r) => sum + (r.totalChunks || 0), 0);\n const successfulChunks = chunkRecords.reduce((sum, r) => sum + (r.chunks || 0), 0);\n const failedChunks = chunkRecords.reduce((sum, r) => sum + (r.failedChunks || 0), 0);\n \n chunkUploadStats = {\n totalChunks,\n successfulChunks,\n failedChunks,\n chunkSuccessRate: totalChunks > 0\n ? Math.round((successfulChunks / totalChunks) * 10000) / 100\n : 0,\n };\n }\n \n return {\n totalUploads: completedRecords.length,\n successfulUploads: successful.length,\n failedUploads: failed.length,\n successRate: Math.round((successful.length / completedRecords.length) * 10000) / 100,\n averageDuration,\n minDuration,\n maxDuration,\n medianDuration,\n averageFileSize,\n totalBytesUploaded,\n averageSpeed,\n errorsByType,\n errorsByModule,\n averageRetries,\n chunkUploadStats,\n };\n }\n \n /**\n * 分类错误类型\n */\n private classifyError(errorMessage: string): string {\n if (errorMessage.includes('网络') || errorMessage.includes('timeout')) {\n return 'NETWORK_ERROR';\n }\n if (errorMessage.includes('服务器') || errorMessage.includes('500')) {\n return 'SERVER_ERROR';\n }\n if (errorMessage.includes('未授权') || errorMessage.includes('401')) {\n return 'AUTH_ERROR';\n }\n if (errorMessage.includes('分片')) {\n return 'CHUNK_ERROR';\n }\n if (errorMessage.includes('合并')) {\n return 'MERGE_ERROR';\n }\n return 'UNKNOWN_ERROR';\n }\n \n /**\n * 获取最近 N 条上传记录\n */\n public getRecentRecords(count: number = 10): UploadRecord[] {\n return this.records.slice(-count).reverse();\n }\n \n /**\n * 获取活跃上传任务\n */\n public getActiveUploads(): Map<string, UploadRecord> {\n return new Map(this.activeUploads);\n }\n \n /**\n * 重置所有统计数据\n */\n public reset(): void {\n this.records = [];\n this.activeUploads.clear();\n }\n \n /**\n * 导出统计数据为 JSON\n */\n public exportToJSON(): string {\n const stats = this.getStats();\n \n // 转换 Map 为普通对象\n const serializableStats = {\n ...stats,\n errorsByType: Object.fromEntries(stats.errorsByType),\n errorsByModule: Object.fromEntries(stats.errorsByModule),\n exportedAt: new Date().toISOString(),\n };\n \n return JSON.stringify(serializableStats, null, 2);\n }\n \n /**\n * 打印统计报告到控制台\n */\n public printReport(): void {\n const stats = this.getStats();\n \n console.group('📊 上传性能监控报告');\n console.log('总上传数:', stats.totalUploads);\n console.log('成功上传:', stats.successfulUploads);\n console.log('失败上传:', stats.failedUploads);\n console.log('成功率:', `${stats.successRate}%`);\n console.log('平均耗时:', `${stats.averageDuration}ms`);\n console.log('最短耗时:', `${stats.minDuration}ms`);\n console.log('最长耗时:', `${stats.maxDuration}ms`);\n console.log('中位数耗时:', `${stats.medianDuration}ms`);\n console.log('平均文件大小:', `${stats.averageFileSize} bytes`);\n console.log('总上传数据量:', `${stats.totalBytesUploaded} bytes`);\n console.log('平均上传速度:', `${stats.averageSpeed} bytes/s`);\n console.log('平均重试次数:', stats.averageRetries);\n \n if (stats.chunkUploadStats) {\n console.group('分片上传统计');\n console.log('总分片数:', stats.chunkUploadStats.totalChunks);\n console.log('成功分片:', stats.chunkUploadStats.successfulChunks);\n console.log('失败分片:', stats.chunkUploadStats.failedChunks);\n console.log('分片成功率:', `${stats.chunkUploadStats.chunkSuccessRate}%`);\n console.groupEnd();\n }\n \n if (stats.errorsByType.size > 0) {\n console.group('错误类型分布');\n stats.errorsByType.forEach((count, type) => {\n console.log(`${type}:`, count);\n });\n console.groupEnd();\n }\n \n // ✅ 添加错误模块分布的打印\n if (stats.errorsByModule.size > 0) {\n console.group('错误模块分布');\n stats.errorsByModule.forEach((count, module) => {\n console.log(`${module}:`, count);\n });\n console.groupEnd();\n }\n \n console.groupEnd();\n }\n}\n\n// 单例实例\nexport const uploadMonitor = new UploadMonitor();\n","/**\n * 网络状态检查工具\n * \n * 提供上传前的网络状态检查功能\n */\n\n/**\n * 网络检查结果\n */\nexport interface NetworkCheckResult {\n /** 是否在线 */\n online: boolean;\n /** 检查时间戳 */\n timestamp: number;\n /** 错误信息(如果离线) */\n error?: string;\n}\n\n/**\n * 检查网络连接状态\n * @returns 网络检查结果\n */\nexport function checkNetworkStatus(): NetworkCheckResult {\n const result: NetworkCheckResult = {\n online: navigator.onLine,\n timestamp: Date.now(),\n };\n\n if (!result.online) {\n result.error = '网络连接已断开,请检查网络设置后重试';\n }\n\n return result;\n}\n\n/**\n * 异步检查网络连通性(通过实际请求测试)\n * @param testUrl 测试用的 URL(可选,默认为当前域名)\n * @param timeout 超时时间(毫秒),默认 5000ms\n * @returns Promise<NetworkCheckResult>\n */\nexport async function checkNetworkConnectivity(\n testUrl?: string,\n timeout: number = 5000\n): Promise<NetworkCheckResult> {\n // 首先检查基本在线状态\n const basicCheck = checkNetworkStatus();\n if (!basicCheck.online) {\n return basicCheck;\n }\n\n // 如果没有提供测试 URL,使用当前域名\n const url = testUrl || window.location.origin;\n\n try {\n // 使用 HEAD 请求进行轻量级测试\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n await fetch(url, {\n method: 'HEAD',\n signal: controller.signal,\n cache: 'no-cache',\n });\n\n clearTimeout(timeoutId);\n\n return {\n online: true,\n timestamp: Date.now(),\n };\n } catch (error) {\n return {\n online: false,\n timestamp: Date.now(),\n error: error instanceof Error ? error.message : '网络连接测试失败',\n };\n }\n}\n\n/**\n * 监听网络状态变化\n * @param callback 网络状态变化回调\n * @returns 取消监听的函数\n */\nexport function watchNetworkStatus(\n callback: (online: boolean) => void\n): () => void {\n const handleOnline = () => callback(true);\n const handleOffline = () => callback(false);\n\n window.addEventListener('online', handleOnline);\n window.addEventListener('offline', handleOffline);\n\n // 返回取消监听的函数\n return () => {\n window.removeEventListener('online', handleOnline);\n window.removeEventListener('offline', handleOffline);\n };\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,94 @@
1
+ {
2
+ "name": "@file-ud.js/core",
3
+ "version": "0.1.0",
4
+ "description": "Browser file upload and download SDK with chunked transfer, resume support, instant transfer, and plugins.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/ChYuanJinlin/file-ud.js.git",
9
+ "directory": "packages/core"
10
+ },
11
+ "homepage": "https://chyuanjinlin.github.io/file-ud.js/",
12
+ "bugs": {
13
+ "url": "https://github.com/ChYuanJinlin/file-ud.js/issues"
14
+ },
15
+ "keywords": [
16
+ "upload",
17
+ "download",
18
+ "chunk",
19
+ "resume",
20
+ "file",
21
+ "typescript"
22
+ ],
23
+ "main": "./dist/index.js",
24
+ "module": "./dist/index.mjs",
25
+ "types": "./dist/index.d.ts",
26
+ "files": [
27
+ "dist",
28
+ "README.md",
29
+ "README_UPLOADER.md",
30
+ "README_DOWNLOADER.md"
31
+ ],
32
+ "sideEffects": [
33
+ "./dist/index.js",
34
+ "./dist/index.mjs",
35
+ "./dist/uploader/index.js",
36
+ "./dist/uploader/index.mjs",
37
+ "./dist/downloader/index.js",
38
+ "./dist/downloader/index.mjs"
39
+ ],
40
+ "exports": {
41
+ ".": {
42
+ "types": "./dist/index.d.ts",
43
+ "import": "./dist/index.mjs",
44
+ "require": "./dist/index.js",
45
+ "default": "./dist/index.mjs"
46
+ },
47
+ "./uploader": {
48
+ "types": "./dist/uploader/index.d.ts",
49
+ "import": "./dist/uploader/index.mjs",
50
+ "require": "./dist/uploader/index.js",
51
+ "default": "./dist/uploader/index.mjs"
52
+ },
53
+ "./downloader": {
54
+ "types": "./dist/downloader/index.d.ts",
55
+ "import": "./dist/downloader/index.mjs",
56
+ "require": "./dist/downloader/index.js",
57
+ "default": "./dist/downloader/index.mjs"
58
+ },
59
+ "./utils": {
60
+ "types": "./dist/utils/index.d.ts",
61
+ "import": "./dist/utils/index.mjs",
62
+ "require": "./dist/utils/index.js",
63
+ "default": "./dist/utils/index.mjs"
64
+ },
65
+ "./types": {
66
+ "types": "./dist/index.d.ts",
67
+ "import": "./dist/index.mjs",
68
+ "require": "./dist/index.js",
69
+ "default": "./dist/index.mjs"
70
+ }
71
+ },
72
+ "devDependencies": {
73
+ "@types/spark-md5": "^3.0.5",
74
+ "@vitest/coverage-v8": "^2.1.0",
75
+ "tsup": "^7.2.0",
76
+ "typescript": "^5.2.0",
77
+ "vitest": "^2.1.0"
78
+ },
79
+ "publishConfig": {
80
+ "access": "public",
81
+ "registry": "https://registry.npmjs.org/"
82
+ },
83
+ "dependencies": {
84
+ "axios": "^1.13.6",
85
+ "spark-md5": "^3.0.2"
86
+ },
87
+ "scripts": {
88
+ "build": "tsup && node scripts/write-subpath-wrappers.mjs",
89
+ "dev": "tsup --watch",
90
+ "test": "vitest run",
91
+ "test:watch": "vitest",
92
+ "test:coverage": "vitest run --coverage"
93
+ }
94
+ }