@lytjs/reactivity 6.0.0 → 6.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/async.cjs +7 -7
- package/dist/async.cjs.map +1 -1
- package/dist/async.mjs +7 -7
- package/dist/async.mjs.map +1 -1
- package/dist/index.cjs +91 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.mjs +89 -20
- package/dist/index.mjs.map +1 -1
- package/dist/{types-CMYee6LB.d.cts → scope-BC3djHz7.d.cts} +83 -8
- package/dist/{types-CXeWWYm6.d.ts → scope-CTxSo201.d.ts} +83 -8
- package/dist/scope.d.cts +1 -70
- package/dist/scope.d.ts +1 -70
- package/dist/{signal-BOAyevht.d.cts → signal-DWrUYmvd.d.cts} +1 -1
- package/dist/{signal-BOAyevht.d.ts → signal-DWrUYmvd.d.ts} +1 -1
- package/dist/signal-component.d.cts +1 -1
- package/dist/signal-component.d.ts +1 -1
- package/dist/signal.cjs +7 -7
- package/dist/signal.cjs.map +1 -1
- package/dist/signal.d.cts +1 -1
- package/dist/signal.d.ts +1 -1
- package/dist/signal.mjs +7 -7
- package/dist/signal.mjs.map +1 -1
- package/package.json +7 -7
package/dist/signal.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/constants.ts","../src/effect.ts","../src/signal.ts"],"names":["REACTIVITY_MAX_TRIGGER_DEPTH","signalFn","computedFn","it","next","readonlyFn"],"mappings":";;;;;;AAeO,IAAM,YAAA,mBAA8B,MAAA,CAAwB,MAAS,CAAA;AACrE,IAAM,oBAAA,mBAAsC,MAAA,CAAiC,MAAS,CAAA;AAmBtF,IAAM,cAAA,GAAiB;AAAA,EAC5B,GAAA,EAAK,KAAA;AAAA,EACL,GAAA,EAAK,KAAA;AAAA,EACL,MAAA,EAAQ,QAAA;AAAA,EACR,KAAA,EAAO;AACT,CAAA;AC5BA,IAAI,YAAA;AAEJ,IAAM,SAAA,uBAAgB,OAAA,EAA2C;AAgGjE,IAAI,YAAA,GAAe,CAAA;AA0EZ,SAAS,OAAA,CACd,MAAA,EACA,IAAA,EACA,GAAA,EACA,WACA,SAAA,EACA;AACA,EAAA,MAAM,OAAA,GAAU,SAAA,CAAU,GAAA,CAAI,MAAM,CAAA;AACpC,EAAA,IAAI,CAAC,OAAA,EAAS;AAEd,EAAA,MAAM,OAA4B,EAAC;AAEnC,EAEO;AACL,IAAA,IAAI,QAAQ,MAAA,EAAW;AACrB,MAAA,IAAA,CAAK,IAAA,CAAK,OAAA,CAAQ,GAAA,CAAI,GAAG,CAAC,CAAA;AAAA,IAC5B;AAEA,IAU2B;AACzB,MAAA,IAAI,MAAM,OAAA,CAAQ,MAAM,CAAA,IAAK,YAAA,CAAa,GAAG,CAAA,EAAG;AAC9C,QAAA,IAAA,CAAK,IAAA,CAAK,OAAA,CAAQ,GAAA,CAAI,QAAQ,CAAC,CAAA;AAAA,MACjC;AAAA,IACF;AAAA,EACF;AAEA,EAAA,MAAM,UAA4B,EAAC;AACnC,EAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,IAAA,IAAI,GAAA,EAAK;AACP,MAAA,KAAA,MAAW,UAAU,GAAA,EAAK;AACxB,QAAA,OAAA,CAAQ,KAAK,MAAM,CAAA;AAAA,MACrB;AAAA,IACF;AAAA,EACF;AAGA,EAAA,cAAA,CAAe,CAAC,GAAG,IAAI,GAAA,CAAI,OAAO,CAAC,CAAC,CAAA;AACtC;AASO,SAAS,eAAe,OAAA,EAA2B;AACxD,EAAA,IAAI,eAAeA,4CAAA,EAA8B;AAW/C,IAAA;AAAA,EACF;AACA,EAAA,YAAA,EAAA;AACA,EAAA,IAAI;AACF,IAAA,KAAA,MAAW,UAAU,OAAA,EAAS;AAC5B,MAAA,IAAI,OAAO,QAAA,EAAU;AACnB,QAAA,aAAA,CAAc,MAAM,CAAA;AAAA,MACtB;AAAA,IACF;AACA,IAAA,KAAA,MAAW,UAAU,OAAA,EAAS;AAC5B,MAAA,IAAI,CAAC,OAAO,QAAA,EAAU;AACpB,QAAA,aAAA,CAAc,MAAM,CAAA;AAAA,MACtB;AAAA,IACF;AAAA,EACF,CAAA,SAAE;AACA,IAAA,YAAA,EAAA;AAAA,EACF;AACF;AAEA,SAAS,cAAc,MAAA,EAAwB;AAC7C,EAAA,IAAI,MAAA,KAAW,YAAA,IAAgB,MAAA,CAAO,YAAA,EAAc;AAClD,IAAA,IAAI,OAAO,SAAA,EAAW;AACpB,MAAA,MAAA,CAAO,SAAA,EAAU;AAAA,IACnB,CAAA,MAAO;AACL,MAAA,MAAA,CAAO,GAAA,EAAI;AAAA,IACb;AAAA,EACF;AACF;AAqWO,SAAS,aAAa,GAAA,EAAuB;AAClD,EAAA,OACE,OAAO,GAAA,KAAQ,QAAA,IACf,QAAQ,KAAA,IACR,GAAA,CAAI,CAAC,CAAA,KAAM,GAAA,IACX,KAAK,QAAA,CAAS,GAAA,EAAK,EAAE,CAAA,KAAM,GAAA,IAC3B,OAAO,aAAA,CAAc,MAAA,CAAO,GAAG,CAAC,CAAA;AAEpC;AClkBA,IAAI,gBAAA,GAAsC,IAAA;AAG1C,IAAI,WAAA,GAAc,KAAA;AAGlB,IAAI,eAAA,GACF,IAAA;AAGF,IAAI,UAAA,GAAa,CAAA;AAGjB,IAAM,eAAA,GAAkB,GAAA;AAGxB,IAAM,oBAAA,uBAA2B,GAAA,EAAgB;AAQjD,IAAM,iBAAA,uBAAwB,GAAA,EAG5B;AAGF,IAAI,WAAA,GAAc,KAAA;AAUX,SAAS,OAAU,YAAA,EAAoC;AAC5D,EAAA,IAAI,KAAA,GAAQ,YAAA;AACZ,EAAA,MAAM,WAAA,uBAAkB,GAAA,EAAgB;AACxC,EAAA,IAAI,QAAA,GAAW,KAAA;AAGf,EAAA,MAAM,QAA2B,EAAC;AAClC,EAAA,MAAM,UAAA,0BAAoB,cAAc,CAAA;AACxC,EAAA,KAAA,CAAM,UAAU,CAAA,GAAI,YAAA;AAEpB,EAAA,MAAM,QAAA,GAAW,SAASC,SAAAA,GAAc;AAItC,IAAA,MAAM,iBAAA,GAAoB,gBAAA;AAC1B,IAAA,IAAI,iBAAA,IAAqB,CAAC,WAAA,IAAe,CAAC,QAAA,EAAU;AAClD,MAAA,IAAI,CAAC,WAAA,CAAY,GAAA,CAAI,iBAAiB,CAAA,EAAG;AACvC,QAAA,WAAA,CAAY,IAAI,iBAAiB,CAAA;AACjC,QAAA,IAAI,eAAA,EAAiB;AACnB,UAAA,eAAA,CAAgBA,WAAqC,MAAM;AACzD,YAAA,WAAA,CAAY,OAAO,iBAAiB,CAAA;AAAA,UACtC,CAAC,CAAA;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAKA,IAAA,OAAO,KAAA;AAAA,EACT,CAAA;AAEA,EAAA,MAAA,CAAO,eAAe,QAAA,EAAU,YAAA,EAAc,EAAE,KAAA,EAAO,MAAM,CAAA;AAE7D,EAAA,QAAA,CAAS,GAAA,GAAM,CAAC,QAAA,KAAsB;AACpC,IAAA,IAAI,QAAA,EAAU;AACd,IAAA,IAAI,MAAA,CAAO,EAAA,CAAG,QAAA,EAAU,KAAK,CAAA,EAAG;AAChC,IAAA,KAAA,GAAQ,QAAA;AACR,IAAA,KAAA,CAAM,UAAU,CAAA,GAAI,QAAA;AACpB,IAAA,iBAAA,CAAkB,WAAA,EAAa,KAAA,EAAO,UAAA,EAAY,QAAQ,CAAA;AAAA,EAC5D,CAAA;AAEA,EAAA,QAAA,CAAS,MAAA,GAAS,CAAC,OAAA,KAAkC;AACnD,IAAA,IAAI,QAAA,EAAU;AACd,IAAA,MAAM,QAAA,GAAW,QAAQ,KAAK,CAAA;AAC9B,IAAA,IAAI,MAAA,CAAO,EAAA,CAAG,QAAA,EAAU,KAAK,CAAA,EAAG;AAChC,IAAA,KAAA,GAAQ,QAAA;AACR,IAAA,KAAA,CAAM,UAAU,CAAA,GAAI,QAAA;AACpB,IAAA,iBAAA,CAAkB,WAAA,EAAa,KAAA,EAAO,UAAA,EAAY,QAAQ,CAAA;AAAA,EAC5D,CAAA;AAEA,EAAA,QAAA,CAAS,UAAU,MAAY;AAC7B,IAAA,QAAA,GAAW,IAAA;AACX,IAAA,WAAA,CAAY,KAAA,EAAM;AAAA,EACpB,CAAA;AAGA,EAAA,QAAA,CAAS,UAAU,MAAY;AAE7B,IAAA,WAAA,CAAY,KAAA,EAAM;AAGlB,IAAA,OAAA,CAAQ,KAAA,EAAO,cAAA,CAAe,GAAA,EAAK,UAAU,CAAA;AAAA,EAC/C,CAAA;AAEA,EAAA,QAAA,CAAS,UAAA,GAAa,CAAC,UAAA,KAAyC;AAC9D,IAAA,IAAI,QAAA,SAAiB,MAAM;AAAA,IAAC,CAAA;AAC5B,IAAA,WAAA,CAAY,IAAI,UAAU,CAAA;AAC1B,IAAA,OAAO,MAAM,WAAA,CAAY,MAAA,CAAO,UAAU,CAAA;AAAA,EAC5C,CAAA;AAEA,EAAA,OAAO,QAAA;AACT;AAUA,SAAS,4BAAA,CACP,QACA,QAAA,EAKA;AACA,EAAA,IAAI,KAAA;AACJ,EAAA,IAAI,KAAA,GAAQ,IAAA;AACZ,EAAA,IAAI,WAAA,GAAc,KAAA;AAClB,EAAA,MAAM,YAAA,uBAAmB,GAAA,EAAyC;AAClE,EAAA,MAAM,WAAA,uBAAkB,GAAA,EAAgB;AACxC,EAAA,IAAI,QAAA,GAAW,KAAA;AAGf,EAAA,MAAM,QAAiC,EAAC;AACxC,EAAA,MAAM,mBAAA,0BAA6B,uBAAuB,CAAA;AAE1D,EAAA,MAAM,aAAa,MAAY;AAC7B,IAAA,IAAI,QAAA,EAAU;AACd,IAAA,KAAA,GAAQ,IAAA;AAER,IAAA,OAAA,CAAQ,KAAA,EAAO,cAAA,CAAe,GAAA,EAAK,mBAAmB,CAAA;AACtD,IAAA,MAAM,IAAA,GAAO,KAAA,CAAM,IAAA,CAAK,WAAW,CAAA;AACnC,IAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,MAAA,GAAA,EAAI;AAAA,IACN;AAAA,EACF,CAAA;AAEA,EAAA,MAAM,UAAA,GAAa,SAASC,WAAAA,GAAgB;AAC1C,IAAA,IAAI,UAAU,OAAO,KAAA;AAMrB,IAAA,IAAI,gBAAA,IAAoB,CAAC,WAAA,EAAa;AACpC,MAAA,WAAA,CAAY,IAAI,gBAAgB,CAAA;AAAA,IAClC;AAEA,IAAA,IAAI,KAAA,EAAO;AACT,MAAA,IAAI,WAAA,EAAa;AACf,QAAA,MAAM,IAAI,KAAA,CAAM,CAAA,+CAAA,EAAkD,QAAQ,CAAA,CAAA,CAAG,CAAA;AAAA,MAC/E;AACA,MAAA,WAAA,GAAc,IAAA;AACd,MAAA,IAAI;AAEF,QAAA,KAAA,MAAW,WAAA,IAAe,YAAA,CAAa,MAAA,EAAO,EAAG;AAC/C,UAAA,WAAA,EAAY;AAAA,QACd;AACA,QAAA,YAAA,CAAa,KAAA,EAAM;AAGnB,QAAA,MAAM,cAAA,GAAiB,gBAAA;AACvB,QAAA,MAAM,mBAAA,GAAsB,eAAA;AAC5B,QAAA,gBAAA,GAAmB,UAAA;AACnB,QAAA,eAAA,GAAkB,CAAC,KAA8B,WAAA,KAA4B;AAC3E,UAAA,YAAA,CAAa,GAAA,CAAI,KAAK,WAAW,CAAA;AAAA,QACnC,CAAA;AACA,QAAA,IAAI;AACF,UAAA,KAAA,GAAQ,MAAA,EAAO;AACf,UAAA,KAAA,GAAQ,KAAA;AAAA,QACV,CAAA,SAAE;AACA,UAAA,gBAAA,GAAmB,cAAA;AACnB,UAAA,eAAA,GAAkB,mBAAA;AAAA,QACpB;AAAA,MACF,CAAA,SAAE;AACA,QAAA,WAAA,GAAc,KAAA;AAAA,MAChB;AAAA,IACF;AAEA,IAAA,OAAO,KAAA;AAAA,EACT,CAAA;AAEA,EAAA,MAAA,CAAO,eAAe,UAAA,EAAY,oBAAA,EAAsB,EAAE,KAAA,EAAO,MAAM,CAAA;AAEvE,EAAA,MAAM,UAAU,MAAY;AAC1B,IAAA,QAAA,GAAW,IAAA;AACX,IAAA,KAAA,MAAW,WAAA,IAAe,YAAA,CAAa,MAAA,EAAO,EAAG;AAC/C,MAAA,WAAA,EAAY;AAAA,IACd;AACA,IAAA,YAAA,CAAa,KAAA,EAAM;AACnB,IAAA,WAAA,CAAY,KAAA,EAAM;AAAA,EACpB,CAAA;AAEA,EAAA,OAAO,EAAE,UAAA,EAAY,UAAA,EAAY,OAAA,EAAQ;AAC3C;AAMO,SAAS,SAAY,EAAA,EAAgC;AAC1D,EAAA,MAAM,EAAE,UAAA,EAAY,OAAA,EAAQ,GAAI,4BAAA,CAA6B,IAAI,iBAAiB,CAAA;AAElF,EAAA,UAAA,CAAW,OAAA,GAAU,OAAA;AACrB,EAAA,UAAA,CAAW,IAAA,GAAO,OAAA;AAElB,EAAA,OAAO,UAAA;AACT;AAWO,SAAS,sBAAA,CACd,QACA,MAAA,EAC2B;AAC3B,EAAA,MAAM,EAAE,UAAA,EAAY,OAAA,EAAQ,GAAI,4BAAA,CAA6B,QAAQ,0BAA0B,CAAA;AAE/F,EAAC,UAAA,CAAyC,GAAA,GAAM,CAAC,QAAA,KAAsB;AACrE,IAAA,MAAA,CAAO,QAAQ,CAAA;AAAA,EACjB,CAAA;AAEA,EAAA,UAAA,CAAW,OAAA,GAAU,OAAA;AACrB,EAAA,UAAA,CAAW,IAAA,GAAO,OAAA;AAElB,EAAA,OAAO,UAAA;AACT;AAUO,SAAS,YAAY,EAAA,EAAsB;AAEhD,EAAA,IAAI,cAAc,eAAA,EAAiB;AAOjC,IAAA,EAAA,EAAG;AACH,IAAA;AAAA,EACF;AACA,EAAA,UAAA,EAAA;AACA,EAAA,IAAI;AACF,IAAA,EAAA,EAAG;AAAA,EACL,CAAA,SAAE;AACA,IAAA,UAAA,EAAA;AACA,IAAA,IAAI,eAAe,CAAA,EAAG;AACpB,MAAA,yBAAA,EAA0B;AAAA,IAC5B;AAAA,EACF;AACF;AAUO,SAAS,cAAiB,EAAA,EAAgB;AAC/C,EAAA,MAAM,eAAA,GAAkB,WAAA;AACxB,EAAA,WAAA,GAAc,IAAA;AACd,EAAA,IAAI;AACF,IAAA,OAAO,EAAA,EAAG;AAAA,EACZ,CAAA,SAAE;AACA,IAAA,WAAA,GAAc,eAAA;AAAA,EAChB;AACF;AAGO,SAAS,kBAAA,GAA8B;AAC5C,EAAA,OAAO,WAAA;AACT;AAMA,SAAS,iBAAA,CACP,WAAA,EACA,KAAA,EACA,SAAA,EACA,QAAA,EACM;AAGN,EAAA,IAAI,UAAA,GAAa,KAAK,WAAA,EAAa;AACjC,IAAA,MAAMC,GAAAA,GAAK,YAAY,MAAA,EAAO;AAC9B,IAAA,IAAIC,KAAAA,GAAOD,IAAG,IAAA,EAAK;AACnB,IAAA,OAAO,CAACC,MAAK,IAAA,EAAM;AACjB,MAAA,oBAAA,CAAqB,GAAA,CAAIA,MAAK,KAAK,CAAA;AACnC,MAAAA,KAAAA,GAAOD,IAAG,IAAA,EAAK;AAAA,IACjB;AAEA,IAAA,IAAI,KAAA,IAAS,cAAc,MAAA,EAAW;AAEpC,MAAA,iBAAA,CAAkB,IAAI,SAAA,EAAW,EAAE,KAAA,EAAO,SAAA,EAAW,UAAU,CAAA;AAAA,IACjE;AACA,IAAA;AAAA,EACF;AACA,EAAA,MAAM,EAAA,GAAK,YAAY,MAAA,EAAO;AAC9B,EAAA,IAAI,IAAA,GAAO,GAAG,IAAA,EAAK;AACnB,EAAA,OAAO,CAAC,KAAK,IAAA,EAAM;AACjB,IAAA,IAAA,CAAK,KAAA,EAAM;AACX,IAAA,IAAA,GAAO,GAAG,IAAA,EAAK;AAAA,EACjB;AAEA,EAAA,IAAI,KAAA,IAAS,cAAc,MAAA,EAAW;AACpC,IAAA,OAAA,CAAQ,KAAA,EAAO,cAAA,CAAe,GAAA,EAAK,SAAmB,CAAA;AAAA,EACxD;AACF;AAEA,SAAS,yBAAA,GAAkC;AACzC,EAAA,IAAI,WAAA,EAAa;AACjB,EAAA,WAAA,GAAc,IAAA;AACd,EAAA,IAAI;AACF,IAAA,IAAI,UAAA,GAAa,CAAA;AACjB,IAAA,OAAA,CAAQ,qBAAqB,IAAA,GAAO,CAAA,IAAK,kBAAkB,IAAA,GAAO,CAAA,KAAM,aAAaH,4CAAAA,EAA8B;AACjH,MAAA,MAAM,aAAA,GAAgB,IAAI,GAAA,CAAI,oBAAoB,CAAA;AAClD,MAAA,MAAM,QAAA,GAAW,IAAI,GAAA,CAAI,iBAAA,CAAkB,QAAQ,CAAA;AACnD,MAAA,oBAAA,CAAqB,KAAA,EAAM;AAC3B,MAAA,iBAAA,CAAkB,KAAA,EAAM;AAExB,MAAA,MAAM,OAAA,GAAU,cAAc,MAAA,EAAO;AACrC,MAAA,IAAI,SAAA,GAAY,QAAQ,IAAA,EAAK;AAC7B,MAAA,OAAO,CAAC,UAAU,IAAA,EAAM;AACtB,QAAA,SAAA,CAAU,KAAA,EAAM;AAChB,QAAA,SAAA,GAAY,QAAQ,IAAA,EAAK;AAAA,MAC3B;AAGA,MAAA,MAAM,SAAA,GAAY,SAAS,MAAA,EAAO;AAClC,MAAA,IAAI,WAAA,GAAc,UAAU,IAAA,EAAK;AACjC,MAAA,OAAO,CAAC,YAAY,IAAA,EAAM;AACxB,QAAA,MAAM,EAAE,KAAA,EAAO,SAAA,EAAW,QAAA,KAAa,WAAA,CAAY,KAAA;AACnD,QAAA,OAAA,CAAQ,KAAA,EAAO,cAAA,CAAe,GAAA,EAAK,SAAA,EAAW,QAAQ,CAAA;AACtD,QAAA,WAAA,GAAc,UAAU,IAAA,EAAK;AAAA,MAC/B;AAEA,MAAA,UAAA,EAAA;AAAA,IACF;AAAA,EACF,CAAA,SAAE;AACA,IAAA,WAAA,GAAc,KAAA;AAAA,EAChB;AACF;AASO,SAAS,eAAkB,EAAA,EAAgC;AAChE,EAAA,OAAO,SAAS,EAAE,CAAA;AACpB;AAGO,SAAS,QAAW,GAAA,EAAmB;AAC5C,EAAA,OAAO,GAAA,EAAI;AACb;AAGO,SAAS,GAAA,CAAO,KAA4C,QAAA,EAAmB;AAEpF,EAAA,IAAI,gBAAA,CAAiB,GAAG,CAAA,EAAG;AACzB,IAAA,GAAA,CAAI,IAAI,QAAQ,CAAA;AAAA,EAClB;AAEF;AAGA,SAAS,iBAAoB,GAAA,EAAsE;AACjG,EAAA,OAAO,OAAO,GAAA,KAAQ,UAAA,IAAc,KAAA,IAAS,GAAA;AAC/C;AAGO,SAAS,MAAA,CAAU,KAAwB,OAAA,EAA+B;AAC/E,EAAA,GAAA,CAAI,OAAO,OAAO,CAAA;AACpB;AAGO,SAAS,eAAkB,GAAA,EAAmC;AACnE,EAAA,MAAM,UAAA,GAAa,SAASK,WAAAA,GAAgB;AAC1C,IAAA,OAAO,GAAA,EAAI;AAAA,EACb,CAAA;AACA,EAAA,MAAA,CAAO,eAAe,UAAA,EAAY,YAAA,EAAc,EAAE,KAAA,EAAO,MAAM,CAAA;AAC/D,EAAA,OAAO,UAAA;AACT;AAOO,SAAS,oBAAA,GAA0C;AACxD,EAAA,OAAO,gBAAA;AACT;AAGO,SAAS,cAAA,GAAyB;AACvC,EAAA,OAAO,UAAA;AACT;AAGO,SAAS,6BAAA,GAAwC;AACtD,EAAA,OAAO,oBAAA,CAAqB,IAAA;AAC9B;AAGO,SAAS,uBAAA,GAAgC;AAC9C,EAAA,gBAAA,GAAmB,IAAA;AACnB,EAAA,WAAA,GAAc,KAAA;AACd,EAAA,UAAA,GAAa,CAAA;AACb,EAAA,oBAAA,CAAqB,KAAA,EAAM;AAC3B,EAAA,WAAA,GAAc,KAAA;AAGd,EAAA,iBAAA,CAAkB,KAAA,EAAM;AAC1B","file":"signal.cjs","sourcesContent":["// src/constants.ts\r\n// 内部符号常量\r\n\r\n// DEV 检测方式说明:\r\n// 统一使用 typeof 检测方式,兼容编译时 define/replace 替换和未配置构建替换的场景。\r\n// 当打包工具(如 rollup/esbuild)通过 define 插件将 __DEV__ 替换为字面量 true/false 时,\r\n// typeof 检测会被优化为直接的字面量判断,实现死代码消除(DCE)。\r\n// 当未配置构建替换时,typeof 检测也能安全降级为 false,避免 ReferenceError。\r\nconst DEV = typeof __DEV__ !== 'undefined' ? __DEV__ : false;\r\n\r\nexport const RefSymbol: unique symbol = Symbol(DEV ? 'ref' : undefined);\r\nexport const ShallowRefSymbol: unique symbol = Symbol(DEV ? 'shallow_ref' : undefined);\r\nexport const ComputedRefSymbol: unique symbol = Symbol(DEV ? 'computed_ref' : undefined);\r\nexport const ReactiveSymbol: unique symbol = Symbol(DEV ? 'reactive' : undefined);\r\nexport const ReadonlySymbol: unique symbol = Symbol(DEV ? 'readonly' : undefined);\r\nexport const SignalSymbol: unique symbol = Symbol(DEV ? 'signal' : undefined);\r\nexport const ComputedSignalSymbol: unique symbol = Symbol(DEV ? 'computed_signal' : undefined);\r\n\r\n// ReactiveFlags - 用于 Proxy handler 内部标记\r\nexport const ReactiveFlags = {\r\n IS_REACTIVE: '__v_isReactive',\r\n IS_READONLY: '__v_isReadonly',\r\n IS_SHALLOW: '__v_isShallow',\r\n IS_REF: '__v_isRef',\r\n RAW: '__v_raw',\r\n SKIP: '__v_skip',\r\n} as const;\r\n\r\n// Track/Trigger 操作类型\r\nexport const TrackOpTypes = {\r\n GET: 'get',\r\n HAS: 'has',\r\n ITERATE: 'iterate',\r\n} as const;\r\n\r\nexport const TriggerOpTypes = {\r\n SET: 'set',\r\n ADD: 'add',\r\n DELETE: 'delete',\r\n CLEAR: 'clear',\r\n} as const;\r\n\r\n// 内部共享常量\r\nexport const ITERATE_KEY = Symbol('iterate');\r\n","// src/effect.ts\r\n// 响应式副作用系统核心\r\n\r\nimport { ITERATE_KEY } from './constants';\r\nimport type { ReactiveEffectRunner } from './types';\r\nimport { warn } from '@lytjs/common-error';\r\nimport { _isSignalUntracked } from './signal';\r\nimport { getActiveEffectScope } from './effect-scope';\r\nimport { REACTIVITY_MAX_TRIGGER_DEPTH } from '@lytjs/common-constants';\r\n\r\n// ==================== 全局状态 ====================\r\n\r\nlet activeEffect: ReactiveEffect | undefined;\r\nlet _trackDepth = 0;\r\nconst targetMap = new WeakMap<object, Map<string | symbol, Dep>>();\r\nlet shouldTrack = true;\r\nconst trackStack: boolean[] = [];\r\n\r\n// ==================== 首次渲染优化 ====================\r\n\r\n/** 标记当前是否处于首次渲染优化期间 */\r\nlet isFirstRenderPass = false;\r\n\r\n/** 记录被跳过的追踪次数(用于调试和测试验证) */\r\nlet skippedTrackingCount = 0;\r\n\r\n/**\r\n * 包裹首次渲染过程,期间禁用响应式依赖收集。\r\n * 支持嵌套调用:如果外层已经处于首次渲染优化期间,\r\n * 内层调用不会提前重置标志位。\r\n */\r\nexport function withFirstRenderOptimization<T>(fn: () => T): T {\r\n const wasFirstRender = isFirstRenderPass;\r\n isFirstRenderPass = true;\r\n try {\r\n return fn();\r\n } finally {\r\n if (!wasFirstRender) {\r\n isFirstRenderPass = false;\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * 检查当前是否应跳过响应式依赖收集。\r\n * 在 withFirstRenderOptimization 执行期间返回 true。\r\n */\r\nexport function shouldSkipTracking(): boolean {\r\n return isFirstRenderPass;\r\n}\r\n\r\n/**\r\n * 获取被跳过的追踪次数(用于调试和测试)。\r\n */\r\nexport function getSkippedTrackingCount(): number {\r\n return skippedTrackingCount;\r\n}\r\n\r\n/**\r\n * 重置被跳过的追踪计数(用于测试)。\r\n */\r\nexport function resetSkippedTrackingCount(): void {\r\n skippedTrackingCount = 0;\r\n}\r\n\r\n// 只读访问器,防止外部修改内部状态\r\n\r\n/**\r\n * 获取当前活跃的 ReactiveEffect 实例。\r\n * 在 effect 执行期间返回当前正在运行的 effect,否则返回 undefined。\r\n *\r\n * @returns 当前活跃的 effect,如果没有则返回 undefined\r\n */\r\nexport function getActiveEffect(): ReactiveEffect | undefined {\r\n return activeEffect;\r\n}\r\n\r\n/**\r\n * 获取当前是否应该进行依赖追踪。\r\n * 可通过 pauseTracking/enableTracking 控制。\r\n *\r\n * @returns 是否应该进行依赖追踪\r\n */\r\nexport function getShouldTrack(): boolean {\r\n return shouldTrack;\r\n}\r\n\r\n// ==================== Dep ====================\r\n\r\n/**\r\n * 依赖集合类型,存储订阅某个响应式属性的所有 ReactiveEffect。\r\n */\r\nexport type Dep = Set<ReactiveEffect>;\r\n\r\n/**\r\n * 创建一个新的 Dep(依赖集合)。\r\n *\r\n * @returns 新的空 Dep 实例\r\n */\r\nexport const createDep = (): Dep => {\r\n return new Set() as Dep;\r\n};\r\n\r\n// ==================== Track / Trigger ====================\r\n\r\n/**\r\n * Maximum depth for nested trigger() calls to prevent infinite reactivity loops.\r\n * When triggerDepth exceeds this limit, further triggers are silently dropped\r\n * and a warning is emitted in DEV mode.\r\n */\r\nlet triggerDepth = 0;\r\n\r\n/**\r\n * 追踪响应式属性的依赖关系。\r\n * 当响应式属性被读取时调用,将当前活跃的 effect 记录为该属性的依赖。\r\n *\r\n * @param target - 被追踪的响应式对象\r\n * @param _type - 追踪操作类型(如 'get'、'has'、'iterate')\r\n * @param key - 被追踪的属性键\r\n */\r\nexport function track(target: object, _type: string, key: string | symbol) {\r\n if (!shouldTrack || activeEffect === undefined) return;\r\n // signal untrack 桥接:signalUntrack 期间跳过 effect 系统的依赖收集\r\n if (_isSignalUntracked()) return;\r\n\r\n let depsMap = targetMap.get(target);\r\n if (!depsMap) {\r\n targetMap.set(target, (depsMap = new Map()));\r\n }\r\n\r\n let dep = depsMap.get(key);\r\n if (!dep) {\r\n depsMap.set(key, (dep = createDep()));\r\n }\r\n\r\n trackEffect(dep);\r\n\r\n // 调试:触发 onTrack\r\n if (__DEV__ && activeEffect.onTrack) {\r\n activeEffect.onTrack({\r\n target,\r\n type: _type,\r\n key,\r\n });\r\n }\r\n}\r\n\r\n/**\r\n * 将当前活跃的 effect 添加到指定的依赖集合中。\r\n * 在首次渲染优化期间会跳过依赖收集。\r\n *\r\n * @param dep - 目标依赖集合\r\n */\r\nexport function trackEffect(dep: Dep) {\r\n // 首次渲染优化:跳过依赖收集\r\n if (shouldSkipTracking()) {\r\n skippedTrackingCount++;\r\n return;\r\n }\r\n // FIX: P1-01 移除重复的 shouldTrack/activeEffect 检查,\r\n // 这些检查已在调用方 track() 中完成,此处只需关注 dep 操作\r\n // FIX: P0-5 添加防御性检查,避免非空断言在公共 API 调用时不安全\r\n if (activeEffect && !dep.has(activeEffect)) {\r\n dep.add(activeEffect);\r\n activeEffect.deps.push(dep);\r\n }\r\n}\r\n\r\n/**\r\n * 触发响应式属性的依赖更新。\r\n * 当响应式属性被修改时调用,通知所有依赖该属性的 effect 重新执行。\r\n *\r\n * 根据操作类型(add/delete/set/clear)会触发不同的依赖集合:\r\n * - `add`:触发属性本身 + ITERATE_KEY(或数组 length)\r\n * - `delete`:触发属性本身 + ITERATE_KEY\r\n * - `set`:触发属性本身 + 数组 length(如果是整数键)\r\n * - `clear`:触发所有属性的依赖\r\n *\r\n * @param target - 被修改的响应式对象\r\n * @param type - 触发操作类型('set' | 'add' | 'delete' | 'clear')\r\n * @param key - 被修改的属性键\r\n * @param _newValue - 新值(可选,用于调试)\r\n * @param _oldValue - 旧值(可选,用于调试)\r\n */\r\nexport function trigger(\r\n target: object,\r\n type: string,\r\n key?: string | symbol,\r\n _newValue?: unknown,\r\n _oldValue?: unknown,\r\n) {\r\n const depsMap = targetMap.get(target);\r\n if (!depsMap) return;\r\n\r\n const deps: (Dep | undefined)[] = [];\r\n\r\n if (type === 'clear') {\r\n deps.push(...depsMap.values());\r\n } else {\r\n if (key !== undefined) {\r\n deps.push(depsMap.get(key));\r\n }\r\n\r\n if (type === 'add') {\r\n if (Array.isArray(target)) {\r\n deps.push(depsMap.get('length'));\r\n } else {\r\n deps.push(depsMap.get(ITERATE_KEY));\r\n }\r\n } else if (type === 'delete') {\r\n if (!Array.isArray(target)) {\r\n deps.push(depsMap.get(ITERATE_KEY));\r\n }\r\n } else if (type === 'set') {\r\n if (Array.isArray(target) && isIntegerKey(key)) {\r\n deps.push(depsMap.get('length'));\r\n }\r\n }\r\n }\r\n\r\n const effects: ReactiveEffect[] = [];\r\n for (const dep of deps) {\r\n if (dep) {\r\n for (const effect of dep) {\r\n effects.push(effect);\r\n }\r\n }\r\n }\r\n\r\n // 去重:同一个 effect 可能同时存在于多个 dep 中\r\n triggerEffects([...new Set(effects)]);\r\n}\r\n\r\n/**\r\n * 执行一组 effect 的触发。\r\n * 优先执行 computed effect,再执行普通 effect。\r\n * 内置递归深度限制,超过最大深度时静默丢弃并发出警告。\r\n *\r\n * @param effects - 需要触发的 ReactiveEffect 数组\r\n */\r\nexport function triggerEffects(effects: ReactiveEffect[]) {\r\n if (triggerDepth > REACTIVITY_MAX_TRIGGER_DEPTH) {\r\n // FIX: P2-1 triggerDepth 超限时改为 warn + 静默丢弃,与 Vue 3 行为一致。\r\n // 之前直接 throw Error 过于激进,会导致整个响应式链断裂。\r\n // 改为仅发出警告并丢弃后续 trigger,避免因单个无限循环导致整个应用崩溃。\r\n if (__DEV__) {\r\n warn(\r\n `[lytjs/reactivity] Maximum trigger depth (${REACTIVITY_MAX_TRIGGER_DEPTH}) exceeded. ` +\r\n `Possible infinite reactivity loop detected. Further triggers are silently dropped. ` +\r\n `triggerDepth=${triggerDepth}`,\r\n );\r\n }\r\n return;\r\n }\r\n triggerDepth++;\r\n try {\r\n for (const effect of effects) {\r\n if (effect.computed) {\r\n triggerEffect(effect);\r\n }\r\n }\r\n for (const effect of effects) {\r\n if (!effect.computed) {\r\n triggerEffect(effect);\r\n }\r\n }\r\n } finally {\r\n triggerDepth--;\r\n }\r\n}\r\n\r\nfunction triggerEffect(effect: ReactiveEffect) {\r\n if (effect !== activeEffect || effect.allowRecurse) {\r\n if (effect.scheduler) {\r\n effect.scheduler();\r\n } else {\r\n effect.run();\r\n }\r\n }\r\n}\r\n\r\n// ==================== ReactiveEffect ====================\r\n\r\n/**\r\n * 响应式副作用类。\r\n * 封装一个副作用函数,支持依赖自动收集、调度执行和手动停止。\r\n *\r\n * 创建时会自动注册到当前活跃的 effectScope 中。\r\n *\r\n * @typeParam T - 副作用函数的返回值类型\r\n *\r\n * @example\r\n * ```ts\r\n * const eff = new ReactiveEffect(() => {\r\n * console.log(state.count);\r\n * });\r\n * eff.run(); // 执行副作用,同时收集依赖\r\n * eff.stop(); // 停止副作用,清理所有依赖\r\n * ```\r\n */\r\nexport class ReactiveEffect<T = unknown> {\r\n active = true;\r\n deps: Dep[] = [];\r\n parent: ReactiveEffect | undefined = undefined;\r\n computed?: boolean;\r\n allowRecurse?: boolean;\r\n onStop?: () => void;\r\n onTrack?: (event: { target: object; key: string | symbol; type: string }) => void;\r\n onTrigger?: (event: {\r\n target: object;\r\n key: string | symbol;\r\n type: string;\r\n newValue?: unknown;\r\n oldValue?: unknown;\r\n }) => void;\r\n onError?: (error: Error) => void;\r\n // 运行前清理(onEffectCleanup 注册的)\r\n _cleanups: Array<() => void> = [];\r\n\r\n constructor(\r\n public fn: () => T,\r\n public scheduler?: (...args: unknown[]) => unknown,\r\n ) {\r\n // 自动注册到当前活跃的 effectScope\r\n const scope = getActiveEffectScope();\r\n if (scope && scope.active) {\r\n scope.effects.push(this);\r\n }\r\n }\r\n\r\n run(): T | undefined {\r\n if (!this.active) {\r\n return undefined;\r\n }\r\n\r\n // 在重新执行前调用 cleanup\r\n if (this._cleanups.length > 0) {\r\n for (let i = 0; i < this._cleanups.length; i++) {\r\n this._cleanups[i]!();\r\n }\r\n this._cleanups.length = 0;\r\n }\r\n\r\n const prevShouldTrack = shouldTrack;\r\n try {\r\n this.parent = activeEffect;\r\n // eslint-disable-next-line @typescript-eslint/no-this-alias\r\n activeEffect = this;\r\n shouldTrack = true;\r\n _trackDepth++;\r\n\r\n return this.fn();\r\n } catch (error) {\r\n if (this.onError) {\r\n this.onError(error as Error);\r\n return undefined;\r\n }\r\n throw error;\r\n } finally {\r\n _trackDepth--;\r\n activeEffect = this.parent;\r\n shouldTrack = prevShouldTrack;\r\n this.parent = undefined;\r\n }\r\n }\r\n\r\n stop(): void {\r\n if (this.active) {\r\n cleanupEffect(this);\r\n if (this._cleanups.length > 0) {\r\n for (let i = 0; i < this._cleanups.length; i++) {\r\n this._cleanups[i]!();\r\n }\r\n this._cleanups.length = 0;\r\n }\r\n if (this.onStop) {\r\n this.onStop();\r\n }\r\n this.active = false;\r\n this.scheduler = undefined;\r\n }\r\n }\r\n}\r\n\r\nfunction cleanupEffect(effect: ReactiveEffect) {\r\n const { deps } = effect;\r\n for (let i = 0; i < deps.length; i++) {\r\n deps[i]!.delete(effect);\r\n }\r\n deps.length = 0;\r\n}\r\n\r\n// ==================== 公共 API ====================\r\n\r\n// Function overloads for effect()\r\n// Non-lazy effect: fn returns void, preventing accidental return value usage\r\n\r\n/**\r\n * 创建一个响应式副作用并立即执行。\r\n *\r\n * 副作用函数会在执行期间自动追踪所使用的响应式属性,\r\n * 当这些属性发生变化时,副作用会重新执行。\r\n *\r\n * @param fn - 副作用函数,返回 void\r\n * @param options - 配置选项\r\n * @param options.lazy - 是否延迟执行(false 时立即执行)\r\n * @param options.scheduler - 自定义调度器,替代默认的立即执行行为\r\n * @param options.allowRecurse - 是否允许副作用递归触发自身\r\n * @param options.onStop - 副作用停止时的回调\r\n * @param options.onTrack - 依赖被追踪时的调试回调\r\n * @param options.onTrigger - 依赖被触发时的调试回调\r\n * @returns 副作用运行器,可调用 run() 手动执行或 stop() 停止\r\n */\r\nexport function effect(\r\n fn: () => void,\r\n options?: {\r\n lazy?: false;\r\n scheduler?: (...args: unknown[]) => unknown;\r\n allowRecurse?: boolean;\r\n onStop?: () => void;\r\n onTrack?: (event: { target: object; key: string | symbol; type: string }) => void;\r\n onTrigger?: (event: {\r\n target: object;\r\n key: string | symbol;\r\n type: string;\r\n newValue?: unknown;\r\n oldValue?: unknown;\r\n }) => void;\r\n },\r\n): ReactiveEffectRunner<void>;\r\n\r\n// Lazy effect: preserves generic return value type (used by computed etc.)\r\nexport function effect<T>(\r\n fn: () => T,\r\n options: {\r\n lazy: true;\r\n scheduler?: (...args: unknown[]) => unknown;\r\n allowRecurse?: boolean;\r\n onStop?: () => void;\r\n onTrack?: (event: { target: object; key: string | symbol; type: string }) => void;\r\n onTrigger?: (event: {\r\n target: object;\r\n key: string | symbol;\r\n type: string;\r\n newValue?: unknown;\r\n oldValue?: unknown;\r\n }) => void;\r\n },\r\n): ReactiveEffectRunner<T>;\r\n\r\n// 统一实现签名\r\nexport function effect<T = unknown>(\r\n fn: () => T,\r\n options?: {\r\n lazy?: boolean;\r\n scheduler?: (...args: unknown[]) => unknown;\r\n allowRecurse?: boolean;\r\n onStop?: () => void;\r\n onTrack?: (event: { target: object; key: string | symbol; type: string }) => void;\r\n onTrigger?: (event: {\r\n target: object;\r\n key: string | symbol;\r\n type: string;\r\n newValue?: unknown;\r\n oldValue?: unknown;\r\n }) => void;\r\n onError?: (error: Error) => void;\r\n },\r\n): ReactiveEffectRunner<T> {\r\n const _effect = new ReactiveEffect(fn);\r\n if (options) {\r\n // 仅提取已知合法选项,防止覆盖内部属性(如 fn、active)\r\n _effect.scheduler = options.scheduler;\r\n _effect.allowRecurse = options.allowRecurse;\r\n _effect.onStop = options.onStop;\r\n _effect.onTrack = options.onTrack;\r\n _effect.onTrigger = options.onTrigger;\r\n _effect.onError = options.onError;\r\n }\r\n if (!options || !options.lazy) {\r\n _effect.run();\r\n }\r\n const runner = _effect.run.bind(_effect) as ReactiveEffectRunner<T>;\r\n runner.effect = _effect;\r\n return runner;\r\n}\r\n\r\n/**\r\n * 停止一个响应式副作用。\r\n * 清理所有依赖关系,并调用 onStop 回调。\r\n *\r\n * @param runner - 由 effect() 返回的副作用运行器\r\n */\r\nexport function stop(runner: ReactiveEffectRunner): void {\r\n runner.effect.stop();\r\n}\r\n\r\n/**\r\n * 暂停依赖追踪。\r\n * 调用后,响应式属性的读取不会建立依赖关系。\r\n * 可通过 resetTracking() 恢复。\r\n */\r\nexport function pauseTracking(): void {\r\n trackStack.push(shouldTrack);\r\n shouldTrack = false;\r\n}\r\n\r\n/**\r\n * 启用依赖追踪。\r\n * 将当前追踪状态压入栈中并设为 true。\r\n * 可通过 resetTracking() 恢复到之前的状态。\r\n */\r\nexport function enableTracking(): void {\r\n trackStack.push(shouldTrack);\r\n shouldTrack = true;\r\n}\r\n\r\n/**\r\n * 重置依赖追踪状态到上一次暂停/启用之前的状态。\r\n * 从追踪栈中弹出最近的状态并恢复。\r\n */\r\nexport function resetTracking(): void {\r\n const last = trackStack.pop();\r\n shouldTrack = last === undefined ? true : last;\r\n}\r\n\r\n/**\r\n * 批量执行函数,期间暂停依赖追踪。\r\n * 与 signalBatch 不同,batch 侧重于暂停追踪而非延迟通知。\r\n * 支持嵌套调用,内层 batch 不会提前恢复追踪状态。\r\n *\r\n * @param fn - 需要批量执行的函数\r\n *\r\n * @example\r\n * ```ts\r\n * batch(() => {\r\n * state.a = 1; // 不会触发依赖更新\r\n * state.b = 2; // 不会触发依赖更新\r\n * }); // 函数结束后恢复追踪\r\n * ```\r\n */\r\nexport function batch(fn: () => void): void {\r\n const stackLength = trackStack.length;\r\n pauseTracking();\r\n try {\r\n fn();\r\n } finally {\r\n // 恢复到 batch 调用前的 stack 长度,确保嵌套安全\r\n while (trackStack.length > stackLength) {\r\n trackStack.pop();\r\n }\r\n shouldTrack = trackStack.length > 0 ? trackStack[trackStack.length - 1]! : true;\r\n }\r\n}\r\n\r\n/**\r\n * batchAsync - like batch but supports async functions.\r\n * Pauses tracking during fn execution (including after await),\r\n * and restores tracking state when fn completes (or throws).\r\n * Returns a Promise.\r\n */\r\nexport function batchAsync(fn: () => void | Promise<void>): Promise<void> {\r\n const stackLength = trackStack.length;\r\n pauseTracking();\r\n const restoreTracking = () => {\r\n while (trackStack.length > stackLength) {\r\n trackStack.pop();\r\n }\r\n shouldTrack = trackStack.length > 0 ? trackStack[trackStack.length - 1]! : true;\r\n };\r\n try {\r\n const result = fn();\r\n if (result && typeof result === 'object' && 'then' in result) {\r\n return (result as Promise<void>).finally(restoreTracking);\r\n }\r\n // 同步路径:立即恢复 tracking 状态\r\n restoreTracking();\r\n return Promise.resolve();\r\n } catch (e) {\r\n restoreTracking();\r\n return Promise.reject(e);\r\n }\r\n}\r\n\r\n/**\r\n * untrack - execute fn without tracking dependencies.\r\n * Semantically different from batch: untrack means \"run but don't track\".\r\n * Returns the return value of fn.\r\n */\r\nexport function untrack<T>(fn: () => T): T {\r\n const stackLength = trackStack.length;\r\n pauseTracking();\r\n try {\r\n return fn();\r\n } finally {\r\n while (trackStack.length > stackLength) {\r\n trackStack.pop();\r\n }\r\n shouldTrack = trackStack.length > 0 ? trackStack[trackStack.length - 1]! : true;\r\n }\r\n}\r\n\r\n/**\r\n * 在当前活跃的 effect 上注册一个清理回调。\r\n * 该回调会在 effect 重新执行前或停止时被调用,用于清理副作用资源。\r\n *\r\n * @param fn - 清理回调函数\r\n * @param failSilently - 当没有活跃 effect 时是否静默失败(默认 false,开发模式下会发出警告)\r\n *\r\n * @example\r\n * ```ts\r\n * effect(() => {\r\n * const timer = setInterval(() => console.log('tick'), 1000);\r\n * onEffectCleanup(() => clearInterval(timer));\r\n * });\r\n * ```\r\n */\r\nexport function onEffectCleanup(fn: () => void, failSilently = false): void {\r\n if (activeEffect === undefined) {\r\n if (!failSilently && __DEV__) {\r\n warn('onEffectCleanup() was called when there was no active effect to associate with.');\r\n }\r\n return;\r\n }\r\n activeEffect._cleanups.push(fn);\r\n}\r\n\r\n// ==================== 辅助 ====================\r\n\r\n/**\r\n * 检查给定的键是否为有效的整数键。\r\n * 用于判断数组索引是否为合法的整数值。\r\n *\r\n * @param key - 需要检查的键值\r\n * @returns 如果是有效的整数键返回 true,否则返回 false\r\n */\r\nexport function isIntegerKey(key: unknown): boolean {\r\n return (\r\n typeof key === 'string' &&\r\n key !== 'NaN' &&\r\n key[0] !== '-' &&\r\n '' + parseInt(key, 10) === key &&\r\n Number.isSafeInteger(Number(key))\r\n );\r\n}\r\n","/**\r\n * @lytjs/reactivity - Signal\r\n * 独立自包含的 Signal 响应式原语。\r\n * 拥有独立的订阅/通知机制,同时桥接 effect 系统保持互操作性。\r\n */\r\n\r\nimport { SignalSymbol, ComputedSignalSymbol, TrackOpTypes, TriggerOpTypes } from './constants';\r\nimport { track, trigger } from './effect';\r\nimport { REACTIVITY_MAX_TRIGGER_DEPTH } from '@lytjs/common-constants';\r\nimport type { Subscriber } from './shared/types';\r\n\r\n// ============================================================\r\n// 类型定义\r\n// ============================================================\r\n\r\n/** 订阅者回调 */\r\nexport type { Subscriber };\r\n\r\n/** Signal 只读接口 */\r\nexport interface Signal<T = unknown> {\r\n /** 读取当前值 */\r\n (): T;\r\n readonly [SignalSymbol]: true;\r\n}\r\n\r\n/** WritableSignal 可写接口 */\r\nexport interface WritableSignal<T = unknown> extends Signal<T> {\r\n /** 设置新值 */\r\n set(newValue: T): void;\r\n /** 通过 updater 函数更新值 */\r\n update(updater: (prev: T) => T): void;\r\n /** 停止所有订阅通知,释放资源 */\r\n dispose(): void;\r\n /** @internal 订阅变更通知 */\r\n _subscribe(subscriber: Subscriber): () => void;\r\n /** FIX: P1-5 REACTIVITY-NEW-03 - 手动清理依赖,防止内存泄漏 */\r\n cleanup(): void;\r\n}\r\n\r\n/** ComputedSignal 计算信号接口 */\r\nexport interface ComputedSignal<T = unknown> extends Signal<T> {\r\n /** 停止计算信号的依赖追踪和更新 */\r\n dispose(): void;\r\n /** @deprecated 使用 dispose() */\r\n stop?: () => void;\r\n readonly [ComputedSignalSymbol]: true;\r\n}\r\n\r\n/** WritableComputedSignal 可写计算信号接口 */\r\nexport interface WritableComputedSignal<T = unknown> extends ComputedSignal<T> {\r\n /** 设置新值(通过 setter 函数) */\r\n set(newValue: T): void;\r\n}\r\n\r\n/** ReadonlySignal 只读信号接口 */\r\nexport interface ReadonlySignal<T = unknown> {\r\n /** 读取当前值 */\r\n (): T;\r\n readonly [SignalSymbol]: true;\r\n}\r\n\r\n// ============================================================\r\n// 全局追踪状态\r\n// ============================================================\r\n\r\n/** 当前活跃的订阅者(computed 读取 signal 时自动追踪) */\r\nlet activeSubscriber: Subscriber | null = null;\r\n\r\n/** 是否处于 untrack 模式 */\r\nlet isUntracked = false;\r\n\r\n/** 依赖追踪回调:computed 使用此回调记录 signal 依赖关系 */\r\nlet trackDependency: ((signal: WritableSignal<unknown>, unsubscribe: () => void) => void) | null =\r\n null;\r\n\r\n/** 当前 batch 嵌套深度 */\r\nlet batchDepth = 0;\r\n\r\n/** FIX: P2-05 batch 嵌套深度限制,防止无限递归 */\r\nconst MAX_BATCH_DEPTH = 100;\r\n\r\n/** batch 期间待通知的订阅者 */\r\nconst pendingNotifications = new Set<Subscriber>();\r\n\r\n/** batch 期间待执行的 effect 系统 trigger 操作(自动去重) */\r\n// FIX: P1-05 使用 Map<symbol,...> 替代 Map<string,...>,\r\n// 避免不同 signal 的 Symbol().toString() 产生相同的字符串 key 导致去重错误\r\n// FIX: P2-7 去重时保留最新 newValue:当同一个 signalKey 多次 set 时,\r\n// Map.set 会覆盖旧值,确保最终 trigger 使用最新的 newValue,\r\n// 避免因去重导致订阅者收到过期的旧值。\r\nconst pendingTriggerOps = new Map<\r\n symbol,\r\n { store: Record<symbol, unknown>; signalKey: symbol; newValue?: unknown }\r\n>();\r\n\r\n/** 是否正在执行通知 */\r\nlet isNotifying = false;\r\n\r\n// ============================================================\r\n// signal — 核心 Signal 原语\r\n// ============================================================\r\n\r\n/**\r\n * 创建一个可写 Signal。\r\n * 使用闭包变量存储值,Set 管理订阅者。\r\n */\r\nexport function signal<T>(initialValue: T): WritableSignal<T> {\r\n let value = initialValue;\r\n const subscribers = new Set<Subscriber>();\r\n let disposed = false;\r\n\r\n // effect 系统桥接:使用 store 对象作为 track/trigger 的 target\r\n const store: Record<symbol, T> = {};\r\n const SIGNAL_KEY = Symbol('signal_value');\r\n store[SIGNAL_KEY] = initialValue;\r\n\r\n const signalFn = function signalFn(): T {\r\n // Signal 内部追踪\r\n // FIX: P0-01 闭包捕获过期 activeSubscriber — 立即捕获当前订阅者引用,\r\n // 避免闭包中的 activeSubscriber 在异步回调中被修改后指向错误的订阅者\r\n const currentSubscriber = activeSubscriber;\r\n if (currentSubscriber && !isUntracked && !disposed) {\r\n if (!subscribers.has(currentSubscriber)) {\r\n subscribers.add(currentSubscriber);\r\n if (trackDependency) {\r\n trackDependency(signalFn as WritableSignal<unknown>, () => {\r\n subscribers.delete(currentSubscriber);\r\n });\r\n }\r\n }\r\n }\r\n // effect 系统桥接追踪\r\n if (!disposed) {\r\n track(store, TrackOpTypes.GET, SIGNAL_KEY);\r\n }\r\n return value;\r\n } as WritableSignal<T>;\r\n\r\n Object.defineProperty(signalFn, SignalSymbol, { value: true });\r\n\r\n signalFn.set = (newValue: T): void => {\r\n if (disposed) return;\r\n if (Object.is(newValue, value)) return;\r\n value = newValue;\r\n store[SIGNAL_KEY] = newValue;\r\n notifySubscribers(subscribers, store, SIGNAL_KEY, newValue);\r\n };\r\n\r\n signalFn.update = (updater: (prev: T) => T): void => {\r\n if (disposed) return;\r\n const newValue = updater(value);\r\n if (Object.is(newValue, value)) return;\r\n value = newValue;\r\n store[SIGNAL_KEY] = newValue;\r\n notifySubscribers(subscribers, store, SIGNAL_KEY, newValue);\r\n };\r\n\r\n signalFn.dispose = (): void => {\r\n disposed = true;\r\n subscribers.clear();\r\n };\r\n\r\n // FIX: P1-5 REACTIVITY-NEW-03 - 添加 cleanup 方法,允许手动清理依赖\r\n signalFn.cleanup = (): void => {\r\n // 清理所有订阅者,但保持 signal 可用\r\n subscribers.clear();\r\n // 清理 effect 系统桥接的依赖:直接触发 SET 通知使依赖失效,\r\n // 不传旧值以避免读取 signal 值\r\n trigger(store, TriggerOpTypes.SET, SIGNAL_KEY);\r\n };\r\n\r\n signalFn._subscribe = (subscriber: Subscriber): (() => void) => {\r\n if (disposed) return () => {};\r\n subscribers.add(subscriber);\r\n return () => subscribers.delete(subscriber);\r\n };\r\n\r\n return signalFn;\r\n}\r\n\r\n// ============================================================\r\n// computed — 独立计算信号\r\n// ============================================================\r\n\r\n/**\r\n * 内部工厂函数:创建计算信号的核心逻辑。\r\n * computed 和 writableComputedSignal 共享此实现,消除约 80 行重复代码。\r\n */\r\nfunction createComputedSignalInternal<T>(\r\n getter: () => T,\r\n typeName: string,\r\n): {\r\n computedFn: ComputedSignal<T>;\r\n invalidate: () => void;\r\n dispose: () => void;\r\n} {\r\n let value: T | undefined;\r\n let dirty = true;\r\n let isComputing = false;\r\n const dependencies = new Map<WritableSignal<unknown>, () => void>();\r\n const subscribers = new Set<Subscriber>();\r\n let disposed = false;\r\n\r\n // effect 系统桥接:使用 store 对象作为 track/trigger 的 target\r\n const store: Record<symbol, unknown> = {};\r\n const COMPUTED_SIGNAL_KEY = Symbol('computed_signal_value');\r\n\r\n const invalidate = (): void => {\r\n if (disposed) return;\r\n dirty = true;\r\n // effect 系统桥接触发\r\n trigger(store, TriggerOpTypes.SET, COMPUTED_SIGNAL_KEY);\r\n const subs = Array.from(subscribers);\r\n for (const sub of subs) {\r\n sub();\r\n }\r\n };\r\n\r\n const computedFn = function computedFn(): T {\r\n if (disposed) return value as T;\r\n\r\n // effect 系统桥接追踪\r\n track(store, TrackOpTypes.GET, COMPUTED_SIGNAL_KEY);\r\n\r\n // 追踪:如果有活跃订阅者,注册自身\r\n if (activeSubscriber && !isUntracked) {\r\n subscribers.add(activeSubscriber);\r\n }\r\n\r\n if (dirty) {\r\n if (isComputing) {\r\n throw new Error(`[lytjs/signal] Circular dependency detected in ${typeName}.`);\r\n }\r\n isComputing = true;\r\n try {\r\n // 清理旧依赖(调用 unsubscribe 函数)\r\n for (const unsubscribe of dependencies.values()) {\r\n unsubscribe();\r\n }\r\n dependencies.clear();\r\n\r\n // 在活跃订阅者上下文中执行 getter,自动追踪新依赖\r\n const prevSubscriber = activeSubscriber;\r\n const prevTrackDependency = trackDependency;\r\n activeSubscriber = invalidate; // 注册 invalidate 作为依赖的订阅者\r\n trackDependency = (dep: WritableSignal<unknown>, unsubscribe: () => void) => {\r\n dependencies.set(dep, unsubscribe);\r\n };\r\n try {\r\n value = getter();\r\n dirty = false;\r\n } finally {\r\n activeSubscriber = prevSubscriber;\r\n trackDependency = prevTrackDependency;\r\n }\r\n } finally {\r\n isComputing = false;\r\n }\r\n }\r\n\r\n return value as T;\r\n } as ComputedSignal<T>;\r\n\r\n Object.defineProperty(computedFn, ComputedSignalSymbol, { value: true });\r\n\r\n const dispose = (): void => {\r\n disposed = true;\r\n for (const unsubscribe of dependencies.values()) {\r\n unsubscribe();\r\n }\r\n dependencies.clear();\r\n subscribers.clear();\r\n };\r\n\r\n return { computedFn, invalidate, dispose };\r\n}\r\n\r\n/**\r\n * 创建一个计算信号。\r\n * 惰性求值、自动依赖追踪与清理、循环依赖检测。\r\n */\r\nexport function computed<T>(fn: () => T): ComputedSignal<T> {\r\n const { computedFn, dispose } = createComputedSignalInternal(fn, 'computed signal');\r\n\r\n computedFn.dispose = dispose;\r\n computedFn.stop = dispose;\r\n\r\n return computedFn;\r\n}\r\n\r\n// ============================================================\r\n// writableComputed — 可写计算信号\r\n// ============================================================\r\n\r\n/**\r\n * 创建一个可写计算信号。\r\n * 通过 getter 读取计算值,通过 setter 写入值。\r\n * setter 通常会间接更新 getter 依赖的底层 signal。\r\n */\r\nexport function writableComputedSignal<T>(\r\n getter: () => T,\r\n setter: (value: T) => void,\r\n): WritableComputedSignal<T> {\r\n const { computedFn, dispose } = createComputedSignalInternal(getter, 'writable computed signal');\r\n\r\n (computedFn as WritableComputedSignal<T>).set = (newValue: T): void => {\r\n setter(newValue);\r\n };\r\n\r\n computedFn.dispose = dispose;\r\n computedFn.stop = dispose;\r\n\r\n return computedFn as WritableComputedSignal<T>;\r\n}\r\n\r\n// ============================================================\r\n// batch — 批量更新\r\n// ============================================================\r\n\r\n/**\r\n * 在批处理中执行函数。\r\n * batch 内多次 signal.set 只在函数结束后统一触发一次通知。\r\n */\r\nexport function signalBatch(fn: () => void): void {\r\n // FIX: P2-05 batch 嵌套深度限制\r\n if (batchDepth >= MAX_BATCH_DEPTH) {\r\n if (__DEV__) {\r\n console.warn(\r\n `[lytjs/signal] signalBatch() nesting depth exceeded ${MAX_BATCH_DEPTH}. ` +\r\n `This may indicate an infinite loop. The batch call will be executed synchronously.`,\r\n );\r\n }\r\n fn();\r\n return;\r\n }\r\n batchDepth++;\r\n try {\r\n fn();\r\n } finally {\r\n batchDepth--;\r\n if (batchDepth === 0) {\r\n flushPendingNotifications();\r\n }\r\n }\r\n}\r\n\r\n// ============================================================\r\n// untrack — 取消追踪\r\n// ============================================================\r\n\r\n/**\r\n * 在取消追踪模式中执行函数。\r\n * 函数内读取 signal 不会建立依赖关系。\r\n */\r\nexport function signalUntrack<T>(fn: () => T): T {\r\n const prevIsUntracked = isUntracked;\r\n isUntracked = true;\r\n try {\r\n return fn();\r\n } finally {\r\n isUntracked = prevIsUntracked;\r\n }\r\n}\r\n\r\n/** @internal 检查当前是否处于 untrack 模式(供 effect 系统桥接使用) */\r\nexport function _isSignalUntracked(): boolean {\r\n return isUntracked;\r\n}\r\n\r\n// ============================================================\r\n// 内部通知机制\r\n// ============================================================\r\n\r\nfunction notifySubscribers(\r\n subscribers: Set<Subscriber>,\r\n store?: Record<symbol, unknown>,\r\n signalKey?: symbol,\r\n newValue?: unknown,\r\n): void {\r\n // FIX: P0-02 当 isNotifying 为 true 时也走 batch 路径,\r\n // 避免在 flushPendingNotifications 执行期间嵌套触发同步通知导致无限递归\r\n if (batchDepth > 0 || isNotifying) {\r\n const it = subscribers.values();\r\n let next = it.next();\r\n while (!next.done) {\r\n pendingNotifications.add(next.value);\r\n next = it.next();\r\n }\r\n // effect 系统桥接:batch 期间也延迟 trigger(去重)\r\n if (store && signalKey !== undefined) {\r\n // FIX: P1-05 直接使用 symbol 作为 key,避免 String() 转换导致的 key 冲突\r\n pendingTriggerOps.set(signalKey, { store, signalKey, newValue });\r\n }\r\n return;\r\n }\r\n const it = subscribers.values();\r\n let next = it.next();\r\n while (!next.done) {\r\n next.value();\r\n next = it.next();\r\n }\r\n // effect 系统桥接触发\r\n if (store && signalKey !== undefined) {\r\n trigger(store, TriggerOpTypes.SET, signalKey, newValue);\r\n }\r\n}\r\n\r\nfunction flushPendingNotifications(): void {\r\n if (isNotifying) return;\r\n isNotifying = true;\r\n try {\r\n let iterations = 0;\r\n while ((pendingNotifications.size > 0 || pendingTriggerOps.size > 0) && iterations < REACTIVITY_MAX_TRIGGER_DEPTH) {\r\n const notifications = new Set(pendingNotifications);\r\n const triggers = new Set(pendingTriggerOps.values());\r\n pendingNotifications.clear();\r\n pendingTriggerOps.clear();\r\n \r\n const notifIt = notifications.values();\r\n let notifNext = notifIt.next();\r\n while (!notifNext.done) {\r\n notifNext.value();\r\n notifNext = notifIt.next();\r\n }\r\n \r\n // 执行延迟的 effect 系统 trigger(已去重)\r\n const triggerIt = triggers.values();\r\n let triggerNext = triggerIt.next();\r\n while (!triggerNext.done) {\r\n const { store, signalKey, newValue } = triggerNext.value;\r\n trigger(store, TriggerOpTypes.SET, signalKey, newValue);\r\n triggerNext = triggerIt.next();\r\n }\r\n \r\n iterations++;\r\n }\r\n } finally {\r\n isNotifying = false;\r\n }\r\n}\r\n\r\n// ============================================================\r\n// 适配器层 — 旧 API 兼容\r\n// ============================================================\r\n\r\n/**\r\n * @deprecated 使用 computed() 代替\r\n */\r\nexport function computedSignal<T>(fn: () => T): ComputedSignal<T> {\r\n return computed(fn);\r\n}\r\n\r\n/** 读取 signal 值 */\r\nexport function valueOf<T>(sig: Signal<T>): T {\r\n return sig();\r\n}\r\n\r\n/** 设置 signal 值(适配器) */\r\nexport function set<T>(sig: WritableSignal<T> | ReadonlySignal<T>, newValue: T): void {\r\n // FIX: P2-35 使用类型守卫检查是否为 WritableSignal\r\n if (isWritableSignal(sig)) {\r\n sig.set(newValue);\r\n }\r\n // ReadonlySignal: 静默忽略(保持旧行为)\r\n}\r\n\r\n/** 类型守卫:检查信号是否为可写信号 */\r\nfunction isWritableSignal<T>(sig: WritableSignal<T> | ReadonlySignal<T>): sig is WritableSignal<T> {\r\n return typeof sig === 'function' && 'set' in sig;\r\n}\r\n\r\n/** 通过 updater 更新 signal 值(适配器) */\r\nexport function update<T>(sig: WritableSignal<T>, updater: (prev: T) => T): void {\r\n sig.update(updater);\r\n}\r\n\r\n/** 创建只读 signal(适配器) */\r\nexport function readonlySignal<T>(sig: Signal<T>): ReadonlySignal<T> {\r\n const readonlyFn = function readonlyFn(): T {\r\n return sig();\r\n } as ReadonlySignal<T>;\r\n Object.defineProperty(readonlyFn, SignalSymbol, { value: true });\r\n return readonlyFn;\r\n}\r\n\r\n// ============================================================\r\n// 调试/测试 API\r\n// ============================================================\r\n\r\n/** @internal 获取当前活跃订阅者(仅用于测试) */\r\nexport function _getActiveSubscriber(): Subscriber | null {\r\n return activeSubscriber;\r\n}\r\n\r\n/** @internal 获取 batch 深度(仅用于测试) */\r\nexport function _getBatchDepth(): number {\r\n return batchDepth;\r\n}\r\n\r\n/** @internal 获取待通知订阅者数量(仅用于测试) */\r\nexport function _getPendingNotificationsCount(): number {\r\n return pendingNotifications.size;\r\n}\r\n\r\n/** @internal 重置全局状态(仅用于测试) */\r\nexport function _resetSignalGlobalState(): void {\r\n activeSubscriber = null;\r\n isUntracked = false;\r\n batchDepth = 0;\r\n pendingNotifications.clear();\r\n isNotifying = false;\r\n // FIX: P1-4 REACTIVITY-NEW-05 - 遗漏 pendingTriggerOps 清理\r\n // 确保测试间状态完全隔离,避免 pendingTriggerOps 泄漏导致测试不稳定\r\n pendingTriggerOps.clear();\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/constants.ts","../src/effect.ts","../src/signal.ts"],"names":["REACTIVITY_MAX_TRIGGER_DEPTH","signalFn","computedFn","it","next","readonlyFn"],"mappings":"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","file":"signal.cjs","sourcesContent":["// src/constants.ts\r\n// 内部符号常量\r\n\r\n// DEV 检测方式说明:\r\n// 统一使用 typeof 检测方式,兼容编译时 define/replace 替换和未配置构建替换的场景。\r\n// 当打包工具(如 rollup/esbuild)通过 define 插件将 __DEV__ 替换为字面量 true/false 时,\r\n// typeof 检测会被优化为直接的字面量判断,实现死代码消除(DCE)。\r\n// 当未配置构建替换时,typeof 检测也能安全降级为 false,避免 ReferenceError。\r\nconst DEV = typeof __DEV__ !== 'undefined' ? __DEV__ : false;\r\n\r\nexport const RefSymbol: unique symbol = Symbol(DEV ? 'ref' : undefined);\r\nexport const ShallowRefSymbol: unique symbol = Symbol(DEV ? 'shallow_ref' : undefined);\r\nexport const ComputedRefSymbol: unique symbol = Symbol(DEV ? 'computed_ref' : undefined);\r\nexport const ReactiveSymbol: unique symbol = Symbol(DEV ? 'reactive' : undefined);\r\nexport const ReadonlySymbol: unique symbol = Symbol(DEV ? 'readonly' : undefined);\r\nexport const SignalSymbol: unique symbol = Symbol(DEV ? 'signal' : undefined);\r\nexport const ComputedSignalSymbol: unique symbol = Symbol(DEV ? 'computed_signal' : undefined);\r\n\r\n// ReactiveFlags - 用于 Proxy handler 内部标记\r\nexport const ReactiveFlags = {\r\n IS_REACTIVE: '__v_isReactive',\r\n IS_READONLY: '__v_isReadonly',\r\n IS_SHALLOW: '__v_isShallow',\r\n IS_REF: '__v_isRef',\r\n RAW: '__v_raw',\r\n SKIP: '__v_skip',\r\n} as const;\r\n\r\n// Track/Trigger 操作类型\r\nexport const TrackOpTypes = {\r\n GET: 'get',\r\n HAS: 'has',\r\n ITERATE: 'iterate',\r\n} as const;\r\n\r\nexport const TriggerOpTypes = {\r\n SET: 'set',\r\n ADD: 'add',\r\n DELETE: 'delete',\r\n CLEAR: 'clear',\r\n} as const;\r\n\r\n// 内部共享常量\r\nexport const ITERATE_KEY = Symbol('iterate');\r\n","// src/effect.ts\r\n// 响应式副作用系统核心\r\n\r\nimport { ITERATE_KEY } from './constants';\r\nimport type { ReactiveEffectRunner } from './types';\r\nimport { warn } from '@lytjs/common-error';\r\nimport { _isSignalUntracked } from './signal';\r\nimport { getActiveEffectScope } from './effect-scope';\r\nimport { REACTIVITY_MAX_TRIGGER_DEPTH } from '@lytjs/common-constants';\r\n\r\n// ==================== 全局状态 ====================\r\n\r\nlet activeEffect: ReactiveEffect | undefined;\r\nlet _trackDepth = 0;\r\nconst targetMap = new WeakMap<object, Map<string | symbol, Dep>>();\r\nlet shouldTrack = true;\r\nconst trackStack: boolean[] = [];\r\n\r\n// ==================== 首次渲染优化 ====================\r\n\r\n/** 标记当前是否处于首次渲染优化期间 */\r\nlet isFirstRenderPass = false;\r\n\r\n/** 记录被跳过的追踪次数(用于调试和测试验证) */\r\nlet skippedTrackingCount = 0;\r\n\r\n/**\r\n * 包裹首次渲染过程,期间禁用响应式依赖收集。\r\n * 支持嵌套调用:如果外层已经处于首次渲染优化期间,\r\n * 内层调用不会提前重置标志位。\r\n */\r\nexport function withFirstRenderOptimization<T>(fn: () => T): T {\r\n const wasFirstRender = isFirstRenderPass;\r\n isFirstRenderPass = true;\r\n try {\r\n return fn();\r\n } finally {\r\n if (!wasFirstRender) {\r\n isFirstRenderPass = false;\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * 检查当前是否应跳过响应式依赖收集。\r\n * 在 withFirstRenderOptimization 执行期间返回 true。\r\n */\r\nexport function shouldSkipTracking(): boolean {\r\n return isFirstRenderPass;\r\n}\r\n\r\n/**\r\n * 获取被跳过的追踪次数(用于调试和测试)。\r\n */\r\nexport function getSkippedTrackingCount(): number {\r\n return skippedTrackingCount;\r\n}\r\n\r\n/**\r\n * 重置被跳过的追踪计数(用于测试)。\r\n */\r\nexport function resetSkippedTrackingCount(): void {\r\n skippedTrackingCount = 0;\r\n}\r\n\r\n// 只读访问器,防止外部修改内部状态\r\n\r\n/**\r\n * 获取当前活跃的 ReactiveEffect 实例。\r\n * 在 effect 执行期间返回当前正在运行的 effect,否则返回 undefined。\r\n *\r\n * @returns 当前活跃的 effect,如果没有则返回 undefined\r\n */\r\nexport function getActiveEffect(): ReactiveEffect | undefined {\r\n return activeEffect;\r\n}\r\n\r\n/**\r\n * 获取当前是否应该进行依赖追踪。\r\n * 可通过 pauseTracking/enableTracking 控制。\r\n *\r\n * @returns 是否应该进行依赖追踪\r\n */\r\nexport function getShouldTrack(): boolean {\r\n return shouldTrack;\r\n}\r\n\r\n// ==================== Dep ====================\r\n\r\n/**\r\n * 依赖集合类型,存储订阅某个响应式属性的所有 ReactiveEffect。\r\n */\r\nexport type Dep = Set<ReactiveEffect>;\r\n\r\n/**\r\n * 创建一个新的 Dep(依赖集合)。\r\n *\r\n * @returns 新的空 Dep 实例\r\n */\r\nexport const createDep = (): Dep => {\r\n return new Set() as Dep;\r\n};\r\n\r\n// ==================== Track / Trigger ====================\r\n\r\n/**\r\n * Maximum depth for nested trigger() calls to prevent infinite reactivity loops.\r\n * When triggerDepth exceeds this limit, further triggers are silently dropped\r\n * and a warning is emitted in DEV mode.\r\n */\r\nlet triggerDepth = 0;\r\n\r\n/**\r\n * 追踪响应式属性的依赖关系。\r\n * 当响应式属性被读取时调用,将当前活跃的 effect 记录为该属性的依赖。\r\n *\r\n * @param target - 被追踪的响应式对象\r\n * @param _type - 追踪操作类型(如 'get'、'has'、'iterate')\r\n * @param key - 被追踪的属性键\r\n */\r\nexport function track(target: object, _type: string, key: string | symbol) {\r\n if (!shouldTrack || activeEffect === undefined) return;\r\n // signal untrack 桥接:signalUntrack 期间跳过 effect 系统的依赖收集\r\n if (_isSignalUntracked()) return;\r\n\r\n let depsMap = targetMap.get(target);\r\n if (!depsMap) {\r\n targetMap.set(target, (depsMap = new Map()));\r\n }\r\n\r\n let dep = depsMap.get(key);\r\n if (!dep) {\r\n depsMap.set(key, (dep = createDep()));\r\n }\r\n\r\n trackEffect(dep);\r\n\r\n // 调试:触发 onTrack\r\n if (__DEV__ && activeEffect.onTrack) {\r\n activeEffect.onTrack({\r\n effect: activeEffect,\r\n target,\r\n type: _type,\r\n key,\r\n });\r\n }\r\n}\r\n\r\n/**\r\n * 将当前活跃的 effect 添加到指定的依赖集合中。\r\n * 在首次渲染优化期间会跳过依赖收集。\r\n *\r\n * @param dep - 目标依赖集合\r\n */\r\nexport function trackEffect(dep: Dep) {\r\n // 首次渲染优化:跳过依赖收集\r\n if (shouldSkipTracking()) {\r\n skippedTrackingCount++;\r\n return;\r\n }\r\n // FIX: P1-01 移除重复的 shouldTrack/activeEffect 检查,\r\n // 这些检查已在调用方 track() 中完成,此处只需关注 dep 操作\r\n // FIX: P0-5 添加防御性检查,避免非空断言在公共 API 调用时不安全\r\n if (activeEffect && !dep.has(activeEffect)) {\r\n dep.add(activeEffect);\r\n activeEffect.deps.push(dep);\r\n }\r\n}\r\n\r\n/**\r\n * 触发响应式属性的依赖更新。\r\n * 当响应式属性被修改时调用,通知所有依赖该属性的 effect 重新执行。\r\n *\r\n * 根据操作类型(add/delete/set/clear)会触发不同的依赖集合:\r\n * - `add`:触发属性本身 + ITERATE_KEY(或数组 length)\r\n * - `delete`:触发属性本身 + ITERATE_KEY\r\n * - `set`:触发属性本身 + 数组 length(如果是整数键)\r\n * - `clear`:触发所有属性的依赖\r\n *\r\n * @param target - 被修改的响应式对象\r\n * @param type - 触发操作类型('set' | 'add' | 'delete' | 'clear')\r\n * @param key - 被修改的属性键\r\n * @param _newValue - 新值(可选,用于调试)\r\n * @param _oldValue - 旧值(可选,用于调试)\r\n */\r\nexport function trigger(\r\n target: object,\r\n type: string,\r\n key?: string | symbol,\r\n newValue?: unknown,\r\n oldValue?: unknown,\r\n) {\r\n const depsMap = targetMap.get(target);\r\n if (!depsMap) return;\r\n\r\n const deps: (Dep | undefined)[] = [];\r\n\r\n if (type === 'clear') {\r\n deps.push(...depsMap.values());\r\n } else {\r\n if (key !== undefined) {\r\n deps.push(depsMap.get(key));\r\n }\r\n\r\n if (type === 'add') {\r\n if (Array.isArray(target)) {\r\n deps.push(depsMap.get('length'));\r\n } else {\r\n deps.push(depsMap.get(ITERATE_KEY));\r\n }\r\n } else if (type === 'delete') {\r\n if (!Array.isArray(target)) {\r\n deps.push(depsMap.get(ITERATE_KEY));\r\n }\r\n } else if (type === 'set') {\r\n if (Array.isArray(target) && isIntegerKey(key)) {\r\n deps.push(depsMap.get('length'));\r\n }\r\n }\r\n }\r\n\r\n const effects: ReactiveEffect[] = [];\r\n for (const dep of deps) {\r\n if (dep) {\r\n for (const effect of dep) {\r\n effects.push(effect);\r\n }\r\n }\r\n }\r\n\r\n // 去重:同一个 effect 可能同时存在于多个 dep 中\r\n triggerEffects([...new Set(effects)], target, type, key, newValue, oldValue);\r\n}\r\n\r\n/**\r\n * 执行一组 effect 的触发。\r\n * 优先执行 computed effect,再执行普通 effect。\r\n * 内置递归深度限制,超过最大深度时静默丢弃并发出警告。\r\n *\r\n * @param effects - 需要触发的 ReactiveEffect 数组\r\n * @param target - 被修改的目标对象(用于调试回调)\r\n * @param type - 触发操作类型(用于调试回调)\r\n * @param key - 被修改的属性键(用于调试回调)\r\n * @param newValue - 新值(用于调试回调)\r\n * @param oldValue - 旧值(用于调试回调)\r\n */\r\nexport function triggerEffects(\r\n effects: ReactiveEffect[],\r\n target: object,\r\n type: string,\r\n key?: string | symbol,\r\n newValue?: unknown,\r\n oldValue?: unknown,\r\n) {\r\n if (triggerDepth > REACTIVITY_MAX_TRIGGER_DEPTH) {\r\n // FIX: P2-1 triggerDepth 超限时改为 warn + 静默丢弃,与 Vue 3 行为一致。\r\n // 之前直接 throw Error 过于激进,会导致整个响应式链断裂。\r\n // 改为仅发出警告并丢弃后续 trigger,避免因单个无限循环导致整个应用崩溃。\r\n if (__DEV__) {\r\n warn(\r\n `[lytjs/reactivity] Maximum trigger depth (${REACTIVITY_MAX_TRIGGER_DEPTH}) exceeded. ` +\r\n `Possible infinite reactivity loop detected. Further triggers are silently dropped. ` +\r\n `triggerDepth=${triggerDepth}`,\r\n );\r\n }\r\n return;\r\n }\r\n triggerDepth++;\r\n try {\r\n for (const effect of effects) {\r\n if (effect.computed) {\r\n triggerEffect(effect, target, type, key, newValue, oldValue);\r\n }\r\n }\r\n for (const effect of effects) {\r\n if (!effect.computed) {\r\n triggerEffect(effect, target, type, key, newValue, oldValue);\r\n }\r\n }\r\n } finally {\r\n triggerDepth--;\r\n }\r\n}\r\n\r\nfunction triggerEffect(\r\n effect: ReactiveEffect,\r\n target: object,\r\n type: string,\r\n key?: string | symbol,\r\n newValue?: unknown,\r\n oldValue?: unknown,\r\n) {\r\n if (effect !== activeEffect || effect.allowRecurse) {\r\n // 调用 onTrigger 回调(用于调试)\r\n if (__DEV__ && effect.onTrigger) {\r\n effect.onTrigger({\r\n effect,\r\n target,\r\n type,\r\n key,\r\n newValue,\r\n oldValue,\r\n });\r\n }\r\n if (effect.scheduler) {\r\n effect.scheduler();\r\n } else {\r\n effect.run();\r\n }\r\n }\r\n}\r\n\r\n// ==================== ReactiveEffect ====================\r\n\r\n/**\r\n * 响应式副作用类。\r\n * 封装一个副作用函数,支持依赖自动收集、调度执行和手动停止。\r\n *\r\n * 创建时会自动注册到当前活跃的 effectScope 中。\r\n *\r\n * @typeParam T - 副作用函数的返回值类型\r\n *\r\n * @example\r\n * ```ts\r\n * const eff = new ReactiveEffect(() => {\r\n * console.log(state.count);\r\n * });\r\n * eff.run(); // 执行副作用,同时收集依赖\r\n * eff.stop(); // 停止副作用,清理所有依赖\r\n * ```\r\n */\r\nexport class ReactiveEffect<T = unknown> {\r\n active = true;\r\n deps: Dep[] = [];\r\n parent: ReactiveEffect | undefined = undefined;\r\n computed?: boolean;\r\n allowRecurse?: boolean;\r\n onStop?: () => void;\r\n onTrack?: (event: { effect: ReactiveEffect; target: object; key?: string | symbol; type: string }) => void;\r\n onTrigger?: (event: {\r\n effect: ReactiveEffect;\r\n target: object;\r\n key?: string | symbol;\r\n type: string;\r\n newValue?: unknown;\r\n oldValue?: unknown;\r\n }) => void;\r\n onError?: (error: Error) => void;\r\n // 运行前清理(onEffectCleanup 注册的)\r\n _cleanups: Array<() => void> = [];\r\n\r\n constructor(\r\n public fn: () => T,\r\n public scheduler?: (...args: unknown[]) => unknown,\r\n ) {\r\n // 自动注册到当前活跃的 effectScope\r\n const scope = getActiveEffectScope();\r\n if (scope && scope.active) {\r\n scope.effects.push(this);\r\n }\r\n }\r\n\r\n run(): T | undefined {\r\n if (!this.active) {\r\n return undefined;\r\n }\r\n\r\n // 在重新执行前调用 cleanup\r\n if (this._cleanups.length > 0) {\r\n for (let i = 0; i < this._cleanups.length; i++) {\r\n this._cleanups[i]!();\r\n }\r\n this._cleanups.length = 0;\r\n }\r\n\r\n const prevShouldTrack = shouldTrack;\r\n try {\r\n this.parent = activeEffect;\r\n // eslint-disable-next-line @typescript-eslint/no-this-alias\r\n activeEffect = this;\r\n shouldTrack = true;\r\n _trackDepth++;\r\n\r\n return this.fn();\r\n } catch (error) {\r\n if (this.onError) {\r\n this.onError(error as Error);\r\n return undefined;\r\n }\r\n throw error;\r\n } finally {\r\n _trackDepth--;\r\n activeEffect = this.parent;\r\n shouldTrack = prevShouldTrack;\r\n this.parent = undefined;\r\n }\r\n }\r\n\r\n stop(): void {\r\n if (this.active) {\r\n cleanupEffect(this);\r\n if (this._cleanups.length > 0) {\r\n for (let i = 0; i < this._cleanups.length; i++) {\r\n this._cleanups[i]!();\r\n }\r\n this._cleanups.length = 0;\r\n }\r\n if (this.onStop) {\r\n this.onStop();\r\n }\r\n this.active = false;\r\n this.scheduler = undefined;\r\n }\r\n }\r\n}\r\n\r\nfunction cleanupEffect(effect: ReactiveEffect) {\r\n const { deps } = effect;\r\n for (let i = 0; i < deps.length; i++) {\r\n deps[i]!.delete(effect);\r\n }\r\n deps.length = 0;\r\n}\r\n\r\n// ==================== 公共 API ====================\r\n\r\n// Function overloads for effect()\r\n// Non-lazy effect: fn returns void, preventing accidental return value usage\r\n\r\n/**\r\n * 创建一个响应式副作用并立即执行。\r\n *\r\n * 副作用函数会在执行期间自动追踪所使用的响应式属性,\r\n * 当这些属性发生变化时,副作用会重新执行。\r\n *\r\n * @param fn - 副作用函数,返回 void\r\n * @param options - 配置选项\r\n * @param options.lazy - 是否延迟执行(false 时立即执行)\r\n * @param options.scheduler - 自定义调度器,替代默认的立即执行行为\r\n * @param options.allowRecurse - 是否允许副作用递归触发自身\r\n * @param options.onStop - 副作用停止时的回调\r\n * @param options.onTrack - 依赖被追踪时的调试回调\r\n * @param options.onTrigger - 依赖被触发时的调试回调\r\n * @returns 副作用运行器,可调用 run() 手动执行或 stop() 停止\r\n */\r\nexport function effect(\r\n fn: () => void,\r\n options?: {\r\n lazy?: false;\r\n scheduler?: (...args: unknown[]) => unknown;\r\n allowRecurse?: boolean;\r\n onStop?: () => void;\r\n onTrack?: (event: { effect: ReactiveEffect; target: object; key?: string | symbol; type: string }) => void;\r\n onTrigger?: (event: {\r\n effect: ReactiveEffect;\r\n target: object;\r\n key?: string | symbol;\r\n type: string;\r\n newValue?: unknown;\r\n oldValue?: unknown;\r\n }) => void;\r\n },\r\n): ReactiveEffectRunner<void>;\r\n\r\n// Lazy effect: preserves generic return value type (used by computed etc.)\r\nexport function effect<T>(\r\n fn: () => T,\r\n options: {\r\n lazy: true;\r\n scheduler?: (...args: unknown[]) => unknown;\r\n allowRecurse?: boolean;\r\n onStop?: () => void;\r\n onTrack?: (event: { effect: ReactiveEffect; target: object; key?: string | symbol; type: string }) => void;\r\n onTrigger?: (event: {\r\n effect: ReactiveEffect;\r\n target: object;\r\n key?: string | symbol;\r\n type: string;\r\n newValue?: unknown;\r\n oldValue?: unknown;\r\n }) => void;\r\n },\r\n): ReactiveEffectRunner<T>;\r\n\r\n// 统一实现签名\r\nexport function effect<T = unknown>(\r\n fn: () => T,\r\n options?: {\r\n lazy?: boolean;\r\n scheduler?: (...args: unknown[]) => unknown;\r\n allowRecurse?: boolean;\r\n onStop?: () => void;\r\n onTrack?: (event: { effect: ReactiveEffect; target: object; key?: string | symbol; type: string }) => void;\r\n onTrigger?: (event: {\r\n effect: ReactiveEffect;\r\n target: object;\r\n key?: string | symbol;\r\n type: string;\r\n newValue?: unknown;\r\n oldValue?: unknown;\r\n }) => void;\r\n onError?: (error: Error) => void;\r\n },\r\n): ReactiveEffectRunner<T> {\r\n const _effect = new ReactiveEffect(fn);\r\n if (options) {\r\n // 仅提取已知合法选项,防止覆盖内部属性(如 fn、active)\r\n _effect.scheduler = options.scheduler;\r\n _effect.allowRecurse = options.allowRecurse;\r\n _effect.onStop = options.onStop;\r\n _effect.onTrack = options.onTrack;\r\n _effect.onTrigger = options.onTrigger;\r\n _effect.onError = options.onError;\r\n }\r\n if (!options || !options.lazy) {\r\n _effect.run();\r\n }\r\n const runner = _effect.run.bind(_effect) as ReactiveEffectRunner<T>;\r\n runner.effect = _effect;\r\n return runner;\r\n}\r\n\r\n/**\r\n * 停止一个响应式副作用。\r\n * 清理所有依赖关系,并调用 onStop 回调。\r\n *\r\n * @param runner - 由 effect() 返回的副作用运行器\r\n */\r\nexport function stop(runner: ReactiveEffectRunner): void {\r\n runner.effect.stop();\r\n}\r\n\r\n/**\r\n * 暂停依赖追踪。\r\n * 调用后,响应式属性的读取不会建立依赖关系。\r\n * 可通过 resetTracking() 恢复。\r\n */\r\nexport function pauseTracking(): void {\r\n trackStack.push(shouldTrack);\r\n shouldTrack = false;\r\n}\r\n\r\n/**\r\n * 启用依赖追踪。\r\n * 将当前追踪状态压入栈中并设为 true。\r\n * 可通过 resetTracking() 恢复到之前的状态。\r\n */\r\nexport function enableTracking(): void {\r\n trackStack.push(shouldTrack);\r\n shouldTrack = true;\r\n}\r\n\r\n/**\r\n * 重置依赖追踪状态到上一次暂停/启用之前的状态。\r\n * 从追踪栈中弹出最近的状态并恢复。\r\n */\r\nexport function resetTracking(): void {\r\n const last = trackStack.pop();\r\n shouldTrack = last === undefined ? true : last;\r\n}\r\n\r\n/**\r\n * 批量执行函数,期间暂停依赖追踪。\r\n * 与 signalBatch 不同,batch 侧重于暂停追踪而非延迟通知。\r\n * 支持嵌套调用,内层 batch 不会提前恢复追踪状态。\r\n *\r\n * @param fn - 需要批量执行的函数\r\n *\r\n * @example\r\n * ```ts\r\n * batch(() => {\r\n * state.a = 1; // 不会触发依赖更新\r\n * state.b = 2; // 不会触发依赖更新\r\n * }); // 函数结束后恢复追踪\r\n * ```\r\n */\r\nexport function batch(fn: () => void): void {\r\n const stackLength = trackStack.length;\r\n pauseTracking();\r\n try {\r\n fn();\r\n } finally {\r\n // 恢复到 batch 调用前的 stack 长度,确保嵌套安全\r\n while (trackStack.length > stackLength) {\r\n trackStack.pop();\r\n }\r\n shouldTrack = trackStack.length > 0 ? trackStack[trackStack.length - 1]! : true;\r\n }\r\n}\r\n\r\n/**\r\n * batchAsync - like batch but supports async functions.\r\n * Pauses tracking during fn execution (including after await),\r\n * and restores tracking state when fn completes (or throws).\r\n * Returns a Promise.\r\n */\r\nexport function batchAsync(fn: () => void | Promise<void>): Promise<void> {\r\n const stackLength = trackStack.length;\r\n pauseTracking();\r\n const restoreTracking = () => {\r\n while (trackStack.length > stackLength) {\r\n trackStack.pop();\r\n }\r\n shouldTrack = trackStack.length > 0 ? trackStack[trackStack.length - 1]! : true;\r\n };\r\n try {\r\n const result = fn();\r\n if (result && typeof result === 'object' && 'then' in result) {\r\n return (result as Promise<void>).finally(restoreTracking);\r\n }\r\n // 同步路径:立即恢复 tracking 状态\r\n restoreTracking();\r\n return Promise.resolve();\r\n } catch (e) {\r\n restoreTracking();\r\n return Promise.reject(e);\r\n }\r\n}\r\n\r\n/**\r\n * untrack - execute fn without tracking dependencies.\r\n * Semantically different from batch: untrack means \"run but don't track\".\r\n * Returns the return value of fn.\r\n */\r\nexport function untrack<T>(fn: () => T): T {\r\n const stackLength = trackStack.length;\r\n pauseTracking();\r\n try {\r\n return fn();\r\n } finally {\r\n while (trackStack.length > stackLength) {\r\n trackStack.pop();\r\n }\r\n shouldTrack = trackStack.length > 0 ? trackStack[trackStack.length - 1]! : true;\r\n }\r\n}\r\n\r\n/**\r\n * 在当前活跃的 effect 上注册一个清理回调。\r\n * 该回调会在 effect 重新执行前或停止时被调用,用于清理副作用资源。\r\n *\r\n * @param fn - 清理回调函数\r\n * @param failSilently - 当没有活跃 effect 时是否静默失败(默认 false,开发模式下会发出警告)\r\n *\r\n * @example\r\n * ```ts\r\n * effect(() => {\r\n * const timer = setInterval(() => console.log('tick'), 1000);\r\n * onEffectCleanup(() => clearInterval(timer));\r\n * });\r\n * ```\r\n */\r\nexport function onEffectCleanup(fn: () => void, failSilently = false): void {\r\n if (activeEffect === undefined) {\r\n if (!failSilently && __DEV__) {\r\n warn('onEffectCleanup() was called when there was no active effect to associate with.');\r\n }\r\n return;\r\n }\r\n activeEffect._cleanups.push(fn);\r\n}\r\n\r\n// ==================== 辅助 ====================\r\n\r\n/**\r\n * 检查给定的键是否为有效的整数键。\r\n * 用于判断数组索引是否为合法的整数值。\r\n *\r\n * @param key - 需要检查的键值\r\n * @returns 如果是有效的整数键返回 true,否则返回 false\r\n */\r\nexport function isIntegerKey(key: unknown): boolean {\r\n return (\r\n typeof key === 'string' &&\r\n key !== 'NaN' &&\r\n key[0] !== '-' &&\r\n '' + parseInt(key, 10) === key &&\r\n Number.isSafeInteger(Number(key))\r\n );\r\n}\r\n","/**\r\n * @lytjs/reactivity - Signal\r\n * 独立自包含的 Signal 响应式原语。\r\n * 拥有独立的订阅/通知机制,同时桥接 effect 系统保持互操作性。\r\n */\r\n\r\nimport { SignalSymbol, ComputedSignalSymbol, TrackOpTypes, TriggerOpTypes } from './constants';\r\nimport { track, trigger } from './effect';\r\nimport { REACTIVITY_MAX_TRIGGER_DEPTH } from '@lytjs/common-constants';\r\nimport type { Subscriber } from './shared/types';\r\n\r\n// ============================================================\r\n// 类型定义\r\n// ============================================================\r\n\r\n/** 订阅者回调 */\r\nexport type { Subscriber };\r\n\r\n/** Signal 只读接口 */\r\nexport interface Signal<T = unknown> {\r\n /** 读取当前值 */\r\n (): T;\r\n readonly [SignalSymbol]: true;\r\n}\r\n\r\n/** WritableSignal 可写接口 */\r\nexport interface WritableSignal<T = unknown> extends Signal<T> {\r\n /** 设置新值 */\r\n set(newValue: T): void;\r\n /** 通过 updater 函数更新值 */\r\n update(updater: (prev: T) => T): void;\r\n /** 停止所有订阅通知,释放资源 */\r\n dispose(): void;\r\n /** @internal 订阅变更通知 */\r\n _subscribe(subscriber: Subscriber): () => void;\r\n /** FIX: P1-5 REACTIVITY-NEW-03 - 手动清理依赖,防止内存泄漏 */\r\n cleanup(): void;\r\n}\r\n\r\n/** ComputedSignal 计算信号接口 */\r\nexport interface ComputedSignal<T = unknown> extends Signal<T> {\r\n /** 停止计算信号的依赖追踪和更新 */\r\n dispose(): void;\r\n /** @deprecated 使用 dispose() */\r\n stop?: () => void;\r\n readonly [ComputedSignalSymbol]: true;\r\n}\r\n\r\n/** WritableComputedSignal 可写计算信号接口 */\r\nexport interface WritableComputedSignal<T = unknown> extends ComputedSignal<T> {\r\n /** 设置新值(通过 setter 函数) */\r\n set(newValue: T): void;\r\n}\r\n\r\n/** ReadonlySignal 只读信号接口 */\r\nexport interface ReadonlySignal<T = unknown> {\r\n /** 读取当前值 */\r\n (): T;\r\n readonly [SignalSymbol]: true;\r\n}\r\n\r\n// ============================================================\r\n// 全局追踪状态\r\n// ============================================================\r\n\r\n/** 当前活跃的订阅者(computed 读取 signal 时自动追踪) */\r\nlet activeSubscriber: Subscriber | null = null;\r\n\r\n/** 是否处于 untrack 模式 */\r\nlet isUntracked = false;\r\n\r\n/** 依赖追踪回调:computed 使用此回调记录 signal 依赖关系 */\r\nlet trackDependency: ((signal: WritableSignal<unknown>, unsubscribe: () => void) => void) | null =\r\n null;\r\n\r\n/** 当前 batch 嵌套深度 */\r\nlet batchDepth = 0;\r\n\r\n/** FIX: P2-05 batch 嵌套深度限制,防止无限递归 */\r\nconst MAX_BATCH_DEPTH = 100;\r\n\r\n/** batch 期间待通知的订阅者 */\r\nconst pendingNotifications = new Set<Subscriber>();\r\n\r\n/** batch 期间待执行的 effect 系统 trigger 操作(自动去重) */\r\n// FIX: P1-05 使用 Map<symbol,...> 替代 Map<string,...>,\r\n// 避免不同 signal 的 Symbol().toString() 产生相同的字符串 key 导致去重错误\r\n// FIX: P2-7 去重时保留最新 newValue:当同一个 signalKey 多次 set 时,\r\n// Map.set 会覆盖旧值,确保最终 trigger 使用最新的 newValue,\r\n// 避免因去重导致订阅者收到过期的旧值。\r\nconst pendingTriggerOps = new Map<\r\n symbol,\r\n { store: Record<symbol, unknown>; signalKey: symbol; newValue?: unknown }\r\n>();\r\n\r\n/** 是否正在执行通知 */\r\nlet isNotifying = false;\r\n\r\n// ============================================================\r\n// signal — 核心 Signal 原语\r\n// ============================================================\r\n\r\n/**\r\n * 创建一个可写 Signal。\r\n * 使用闭包变量存储值,Set 管理订阅者。\r\n */\r\nexport function signal<T>(initialValue: T): WritableSignal<T> {\r\n let value = initialValue;\r\n const subscribers = new Set<Subscriber>();\r\n let disposed = false;\r\n\r\n // effect 系统桥接:使用 store 对象作为 track/trigger 的 target\r\n const store: Record<symbol, T> = {};\r\n const SIGNAL_KEY = Symbol('signal_value');\r\n store[SIGNAL_KEY] = initialValue;\r\n\r\n const signalFn = function signalFn(): T {\r\n // Signal 内部追踪\r\n // FIX: P0-01 闭包捕获过期 activeSubscriber — 立即捕获当前订阅者引用,\r\n // 避免闭包中的 activeSubscriber 在异步回调中被修改后指向错误的订阅者\r\n const currentSubscriber = activeSubscriber;\r\n if (currentSubscriber && !isUntracked && !disposed) {\r\n if (!subscribers.has(currentSubscriber)) {\r\n subscribers.add(currentSubscriber);\r\n if (trackDependency) {\r\n trackDependency(signalFn as WritableSignal<unknown>, () => {\r\n subscribers.delete(currentSubscriber);\r\n });\r\n }\r\n }\r\n }\r\n // effect 系统桥接追踪\r\n if (!disposed) {\r\n track(store, TrackOpTypes.GET, SIGNAL_KEY);\r\n }\r\n return value;\r\n } as WritableSignal<T>;\r\n\r\n Object.defineProperty(signalFn, SignalSymbol, { value: true });\r\n\r\n signalFn.set = (newValue: T): void => {\r\n if (disposed) return;\r\n if (Object.is(newValue, value)) return;\r\n value = newValue;\r\n store[SIGNAL_KEY] = newValue;\r\n notifySubscribers(subscribers, store, SIGNAL_KEY, newValue);\r\n };\r\n\r\n signalFn.update = (updater: (prev: T) => T): void => {\r\n if (disposed) return;\r\n const newValue = updater(value);\r\n if (Object.is(newValue, value)) return;\r\n value = newValue;\r\n store[SIGNAL_KEY] = newValue;\r\n notifySubscribers(subscribers, store, SIGNAL_KEY, newValue);\r\n };\r\n\r\n signalFn.dispose = (): void => {\r\n disposed = true;\r\n subscribers.clear();\r\n };\r\n\r\n // FIX: P1-5 REACTIVITY-NEW-03 - 添加 cleanup 方法,允许手动清理依赖\r\n signalFn.cleanup = (): void => {\r\n // 清理所有订阅者,但保持 signal 可用\r\n subscribers.clear();\r\n // 清理 effect 系统桥接的依赖:直接触发 SET 通知使依赖失效,\r\n // 不传旧值以避免读取 signal 值\r\n trigger(store, TriggerOpTypes.SET, SIGNAL_KEY);\r\n };\r\n\r\n signalFn._subscribe = (subscriber: Subscriber): (() => void) => {\r\n if (disposed) return () => {};\r\n subscribers.add(subscriber);\r\n return () => subscribers.delete(subscriber);\r\n };\r\n\r\n return signalFn;\r\n}\r\n\r\n// ============================================================\r\n// computed — 独立计算信号\r\n// ============================================================\r\n\r\n/**\r\n * 内部工厂函数:创建计算信号的核心逻辑。\r\n * computed 和 writableComputedSignal 共享此实现,消除约 80 行重复代码。\r\n */\r\nfunction createComputedSignalInternal<T>(\r\n getter: () => T,\r\n typeName: string,\r\n): {\r\n computedFn: ComputedSignal<T>;\r\n invalidate: () => void;\r\n dispose: () => void;\r\n} {\r\n let value: T | undefined;\r\n let dirty = true;\r\n let isComputing = false;\r\n const dependencies = new Map<WritableSignal<unknown>, () => void>();\r\n const subscribers = new Set<Subscriber>();\r\n let disposed = false;\r\n\r\n // effect 系统桥接:使用 store 对象作为 track/trigger 的 target\r\n const store: Record<symbol, unknown> = {};\r\n const COMPUTED_SIGNAL_KEY = Symbol('computed_signal_value');\r\n\r\n const invalidate = (): void => {\r\n if (disposed) return;\r\n dirty = true;\r\n // effect 系统桥接触发\r\n trigger(store, TriggerOpTypes.SET, COMPUTED_SIGNAL_KEY);\r\n const subs = Array.from(subscribers);\r\n for (const sub of subs) {\r\n sub();\r\n }\r\n };\r\n\r\n const computedFn = function computedFn(): T {\r\n if (disposed) return value as T;\r\n\r\n // effect 系统桥接追踪\r\n track(store, TrackOpTypes.GET, COMPUTED_SIGNAL_KEY);\r\n\r\n // 追踪:如果有活跃订阅者,注册自身\r\n if (activeSubscriber && !isUntracked) {\r\n subscribers.add(activeSubscriber);\r\n }\r\n\r\n if (dirty) {\r\n if (isComputing) {\r\n throw new Error(`[lytjs/signal] Circular dependency detected in ${typeName}.`);\r\n }\r\n isComputing = true;\r\n try {\r\n // 清理旧依赖(调用 unsubscribe 函数)\r\n for (const unsubscribe of dependencies.values()) {\r\n unsubscribe();\r\n }\r\n dependencies.clear();\r\n\r\n // 在活跃订阅者上下文中执行 getter,自动追踪新依赖\r\n const prevSubscriber = activeSubscriber;\r\n const prevTrackDependency = trackDependency;\r\n activeSubscriber = invalidate; // 注册 invalidate 作为依赖的订阅者\r\n trackDependency = (dep: WritableSignal<unknown>, unsubscribe: () => void) => {\r\n dependencies.set(dep, unsubscribe);\r\n };\r\n try {\r\n value = getter();\r\n dirty = false;\r\n } finally {\r\n activeSubscriber = prevSubscriber;\r\n trackDependency = prevTrackDependency;\r\n }\r\n } finally {\r\n isComputing = false;\r\n }\r\n }\r\n\r\n return value as T;\r\n } as ComputedSignal<T>;\r\n\r\n Object.defineProperty(computedFn, ComputedSignalSymbol, { value: true });\r\n\r\n const dispose = (): void => {\r\n disposed = true;\r\n for (const unsubscribe of dependencies.values()) {\r\n unsubscribe();\r\n }\r\n dependencies.clear();\r\n subscribers.clear();\r\n };\r\n\r\n return { computedFn, invalidate, dispose };\r\n}\r\n\r\n/**\r\n * 创建一个计算信号。\r\n * 惰性求值、自动依赖追踪与清理、循环依赖检测。\r\n */\r\nexport function computed<T>(fn: () => T): ComputedSignal<T> {\r\n const { computedFn, dispose } = createComputedSignalInternal(fn, 'computed signal');\r\n\r\n computedFn.dispose = dispose;\r\n computedFn.stop = dispose;\r\n\r\n return computedFn;\r\n}\r\n\r\n// ============================================================\r\n// writableComputed — 可写计算信号\r\n// ============================================================\r\n\r\n/**\r\n * 创建一个可写计算信号。\r\n * 通过 getter 读取计算值,通过 setter 写入值。\r\n * setter 通常会间接更新 getter 依赖的底层 signal。\r\n */\r\nexport function writableComputedSignal<T>(\r\n getter: () => T,\r\n setter: (value: T) => void,\r\n): WritableComputedSignal<T> {\r\n const { computedFn, dispose } = createComputedSignalInternal(getter, 'writable computed signal');\r\n\r\n (computedFn as WritableComputedSignal<T>).set = (newValue: T): void => {\r\n setter(newValue);\r\n };\r\n\r\n computedFn.dispose = dispose;\r\n computedFn.stop = dispose;\r\n\r\n return computedFn as WritableComputedSignal<T>;\r\n}\r\n\r\n// ============================================================\r\n// batch — 批量更新\r\n// ============================================================\r\n\r\n/**\r\n * 在批处理中执行函数。\r\n * batch 内多次 signal.set 只在函数结束后统一触发一次通知。\r\n */\r\nexport function signalBatch(fn: () => void): void {\r\n // FIX: P2-05 batch 嵌套深度限制\r\n if (batchDepth >= MAX_BATCH_DEPTH) {\r\n if (__DEV__) {\r\n console.warn(\r\n `[lytjs/signal] signalBatch() nesting depth exceeded ${MAX_BATCH_DEPTH}. ` +\r\n `This may indicate an infinite loop. The batch call will be executed synchronously.`,\r\n );\r\n }\r\n fn();\r\n return;\r\n }\r\n batchDepth++;\r\n try {\r\n fn();\r\n } finally {\r\n batchDepth--;\r\n if (batchDepth === 0) {\r\n flushPendingNotifications();\r\n }\r\n }\r\n}\r\n\r\n// ============================================================\r\n// untrack — 取消追踪\r\n// ============================================================\r\n\r\n/**\r\n * 在取消追踪模式中执行函数。\r\n * 函数内读取 signal 不会建立依赖关系。\r\n */\r\nexport function signalUntrack<T>(fn: () => T): T {\r\n const prevIsUntracked = isUntracked;\r\n isUntracked = true;\r\n try {\r\n return fn();\r\n } finally {\r\n isUntracked = prevIsUntracked;\r\n }\r\n}\r\n\r\n/** @internal 检查当前是否处于 untrack 模式(供 effect 系统桥接使用) */\r\nexport function _isSignalUntracked(): boolean {\r\n return isUntracked;\r\n}\r\n\r\n// ============================================================\r\n// 内部通知机制\r\n// ============================================================\r\n\r\nfunction notifySubscribers(\r\n subscribers: Set<Subscriber>,\r\n store?: Record<symbol, unknown>,\r\n signalKey?: symbol,\r\n newValue?: unknown,\r\n): void {\r\n // FIX: P0-02 当 isNotifying 为 true 时也走 batch 路径,\r\n // 避免在 flushPendingNotifications 执行期间嵌套触发同步通知导致无限递归\r\n if (batchDepth > 0 || isNotifying) {\r\n const it = subscribers.values();\r\n let next = it.next();\r\n while (!next.done) {\r\n pendingNotifications.add(next.value);\r\n next = it.next();\r\n }\r\n // effect 系统桥接:batch 期间也延迟 trigger(去重)\r\n if (store && signalKey !== undefined) {\r\n // FIX: P1-05 直接使用 symbol 作为 key,避免 String() 转换导致的 key 冲突\r\n pendingTriggerOps.set(signalKey, { store, signalKey, newValue });\r\n }\r\n return;\r\n }\r\n const it = subscribers.values();\r\n let next = it.next();\r\n while (!next.done) {\r\n next.value();\r\n next = it.next();\r\n }\r\n // effect 系统桥接触发\r\n if (store && signalKey !== undefined) {\r\n trigger(store, TriggerOpTypes.SET, signalKey, newValue);\r\n }\r\n}\r\n\r\nfunction flushPendingNotifications(): void {\r\n if (isNotifying) return;\r\n isNotifying = true;\r\n try {\r\n let iterations = 0;\r\n while ((pendingNotifications.size > 0 || pendingTriggerOps.size > 0) && iterations < REACTIVITY_MAX_TRIGGER_DEPTH) {\r\n const notifications = new Set(pendingNotifications);\r\n const triggers = new Set(pendingTriggerOps.values());\r\n pendingNotifications.clear();\r\n pendingTriggerOps.clear();\r\n \r\n const notifIt = notifications.values();\r\n let notifNext = notifIt.next();\r\n while (!notifNext.done) {\r\n notifNext.value();\r\n notifNext = notifIt.next();\r\n }\r\n \r\n // 执行延迟的 effect 系统 trigger(已去重)\r\n const triggerIt = triggers.values();\r\n let triggerNext = triggerIt.next();\r\n while (!triggerNext.done) {\r\n const { store, signalKey, newValue } = triggerNext.value;\r\n trigger(store, TriggerOpTypes.SET, signalKey, newValue);\r\n triggerNext = triggerIt.next();\r\n }\r\n \r\n iterations++;\r\n }\r\n } finally {\r\n isNotifying = false;\r\n }\r\n}\r\n\r\n// ============================================================\r\n// 适配器层 — 旧 API 兼容\r\n// ============================================================\r\n\r\n/**\r\n * @deprecated 使用 computed() 代替\r\n */\r\nexport function computedSignal<T>(fn: () => T): ComputedSignal<T> {\r\n return computed(fn);\r\n}\r\n\r\n/** 读取 signal 值 */\r\nexport function valueOf<T>(sig: Signal<T>): T {\r\n return sig();\r\n}\r\n\r\n/** 设置 signal 值(适配器) */\r\nexport function set<T>(sig: WritableSignal<T> | ReadonlySignal<T>, newValue: T): void {\r\n // FIX: P2-35 使用类型守卫检查是否为 WritableSignal\r\n if (isWritableSignal(sig)) {\r\n sig.set(newValue);\r\n }\r\n // ReadonlySignal: 静默忽略(保持旧行为)\r\n}\r\n\r\n/** 类型守卫:检查信号是否为可写信号 */\r\nfunction isWritableSignal<T>(sig: WritableSignal<T> | ReadonlySignal<T>): sig is WritableSignal<T> {\r\n return typeof sig === 'function' && 'set' in sig;\r\n}\r\n\r\n/** 通过 updater 更新 signal 值(适配器) */\r\nexport function update<T>(sig: WritableSignal<T>, updater: (prev: T) => T): void {\r\n sig.update(updater);\r\n}\r\n\r\n/** 创建只读 signal(适配器) */\r\nexport function readonlySignal<T>(sig: Signal<T>): ReadonlySignal<T> {\r\n const readonlyFn = function readonlyFn(): T {\r\n return sig();\r\n } as ReadonlySignal<T>;\r\n Object.defineProperty(readonlyFn, SignalSymbol, { value: true });\r\n return readonlyFn;\r\n}\r\n\r\n// ============================================================\r\n// 调试/测试 API\r\n// ============================================================\r\n\r\n/** @internal 获取当前活跃订阅者(仅用于测试) */\r\nexport function _getActiveSubscriber(): Subscriber | null {\r\n return activeSubscriber;\r\n}\r\n\r\n/** @internal 获取 batch 深度(仅用于测试) */\r\nexport function _getBatchDepth(): number {\r\n return batchDepth;\r\n}\r\n\r\n/** @internal 获取待通知订阅者数量(仅用于测试) */\r\nexport function _getPendingNotificationsCount(): number {\r\n return pendingNotifications.size;\r\n}\r\n\r\n/** @internal 重置全局状态(仅用于测试) */\r\nexport function _resetSignalGlobalState(): void {\r\n activeSubscriber = null;\r\n isUntracked = false;\r\n batchDepth = 0;\r\n pendingNotifications.clear();\r\n isNotifying = false;\r\n // FIX: P1-4 REACTIVITY-NEW-05 - 遗漏 pendingTriggerOps 清理\r\n // 确保测试间状态完全隔离,避免 pendingTriggerOps 泄漏导致测试不稳定\r\n pendingTriggerOps.clear();\r\n}\r\n"]}
|
package/dist/signal.d.cts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { a as ComputedSignal, b as ReadonlySignal, e as Signal, f as Subscriber, W as WritableComputedSignal, g as WritableSignal, _ as _getActiveSubscriber,
|
|
1
|
+
export { a as ComputedSignal, b as ReadonlySignal, e as Signal, f as Subscriber, W as WritableComputedSignal, g as WritableSignal, _ as _getActiveSubscriber, h as _getBatchDepth, i as _getPendingNotificationsCount, j as _isSignalUntracked, k as _resetSignalGlobalState, l as computed, m as computedSignal, r as readonlySignal, s as set, n as signal, o as signalBatch, p as signalUntrack, u as update, v as valueOf, w as writableComputedSignal } from './signal-DWrUYmvd.cjs';
|
package/dist/signal.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { a as ComputedSignal, b as ReadonlySignal, e as Signal, f as Subscriber, W as WritableComputedSignal, g as WritableSignal, _ as _getActiveSubscriber,
|
|
1
|
+
export { a as ComputedSignal, b as ReadonlySignal, e as Signal, f as Subscriber, W as WritableComputedSignal, g as WritableSignal, _ as _getActiveSubscriber, h as _getBatchDepth, i as _getPendingNotificationsCount, j as _isSignalUntracked, k as _resetSignalGlobalState, l as computed, m as computedSignal, r as readonlySignal, s as set, n as signal, o as signalBatch, p as signalUntrack, u as update, v as valueOf, w as writableComputedSignal } from './signal-DWrUYmvd.js';
|
package/dist/signal.mjs
CHANGED
|
@@ -13,7 +13,7 @@ var TriggerOpTypes = {
|
|
|
13
13
|
var activeEffect;
|
|
14
14
|
var targetMap = /* @__PURE__ */ new WeakMap();
|
|
15
15
|
var triggerDepth = 0;
|
|
16
|
-
function trigger(target, type, key,
|
|
16
|
+
function trigger(target, type, key, newValue, oldValue) {
|
|
17
17
|
const depsMap = targetMap.get(target);
|
|
18
18
|
if (!depsMap) return;
|
|
19
19
|
const deps = [];
|
|
@@ -35,9 +35,9 @@ function trigger(target, type, key, _newValue, _oldValue) {
|
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
|
-
triggerEffects([...new Set(effects)]);
|
|
38
|
+
triggerEffects([...new Set(effects)], target, type, key, newValue, oldValue);
|
|
39
39
|
}
|
|
40
|
-
function triggerEffects(effects) {
|
|
40
|
+
function triggerEffects(effects, target, type, key, newValue, oldValue) {
|
|
41
41
|
if (triggerDepth > REACTIVITY_MAX_TRIGGER_DEPTH) {
|
|
42
42
|
return;
|
|
43
43
|
}
|
|
@@ -45,19 +45,19 @@ function triggerEffects(effects) {
|
|
|
45
45
|
try {
|
|
46
46
|
for (const effect of effects) {
|
|
47
47
|
if (effect.computed) {
|
|
48
|
-
triggerEffect(effect);
|
|
48
|
+
triggerEffect(effect, target, type, key, newValue, oldValue);
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
for (const effect of effects) {
|
|
52
52
|
if (!effect.computed) {
|
|
53
|
-
triggerEffect(effect);
|
|
53
|
+
triggerEffect(effect, target, type, key, newValue, oldValue);
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
} finally {
|
|
57
57
|
triggerDepth--;
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
|
-
function triggerEffect(effect) {
|
|
60
|
+
function triggerEffect(effect, target, type, key, newValue, oldValue) {
|
|
61
61
|
if (effect !== activeEffect || effect.allowRecurse) {
|
|
62
62
|
if (effect.scheduler) {
|
|
63
63
|
effect.scheduler();
|
|
@@ -255,7 +255,7 @@ function notifySubscribers(subscribers, store, signalKey, newValue) {
|
|
|
255
255
|
next = it.next();
|
|
256
256
|
}
|
|
257
257
|
if (store && signalKey !== void 0) {
|
|
258
|
-
trigger(store, TriggerOpTypes.SET, signalKey);
|
|
258
|
+
trigger(store, TriggerOpTypes.SET, signalKey, newValue);
|
|
259
259
|
}
|
|
260
260
|
}
|
|
261
261
|
function flushPendingNotifications() {
|
package/dist/signal.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/constants.ts","../src/effect.ts","../src/signal.ts"],"names":["signalFn","computedFn","it","next","REACTIVITY_MAX_TRIGGER_DEPTH","readonlyFn"],"mappings":"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","file":"signal.mjs","sourcesContent":["// src/constants.ts\r\n// 内部符号常量\r\n\r\n// DEV 检测方式说明:\r\n// 统一使用 typeof 检测方式,兼容编译时 define/replace 替换和未配置构建替换的场景。\r\n// 当打包工具(如 rollup/esbuild)通过 define 插件将 __DEV__ 替换为字面量 true/false 时,\r\n// typeof 检测会被优化为直接的字面量判断,实现死代码消除(DCE)。\r\n// 当未配置构建替换时,typeof 检测也能安全降级为 false,避免 ReferenceError。\r\nconst DEV = typeof __DEV__ !== 'undefined' ? __DEV__ : false;\r\n\r\nexport const RefSymbol: unique symbol = Symbol(DEV ? 'ref' : undefined);\r\nexport const ShallowRefSymbol: unique symbol = Symbol(DEV ? 'shallow_ref' : undefined);\r\nexport const ComputedRefSymbol: unique symbol = Symbol(DEV ? 'computed_ref' : undefined);\r\nexport const ReactiveSymbol: unique symbol = Symbol(DEV ? 'reactive' : undefined);\r\nexport const ReadonlySymbol: unique symbol = Symbol(DEV ? 'readonly' : undefined);\r\nexport const SignalSymbol: unique symbol = Symbol(DEV ? 'signal' : undefined);\r\nexport const ComputedSignalSymbol: unique symbol = Symbol(DEV ? 'computed_signal' : undefined);\r\n\r\n// ReactiveFlags - 用于 Proxy handler 内部标记\r\nexport const ReactiveFlags = {\r\n IS_REACTIVE: '__v_isReactive',\r\n IS_READONLY: '__v_isReadonly',\r\n IS_SHALLOW: '__v_isShallow',\r\n IS_REF: '__v_isRef',\r\n RAW: '__v_raw',\r\n SKIP: '__v_skip',\r\n} as const;\r\n\r\n// Track/Trigger 操作类型\r\nexport const TrackOpTypes = {\r\n GET: 'get',\r\n HAS: 'has',\r\n ITERATE: 'iterate',\r\n} as const;\r\n\r\nexport const TriggerOpTypes = {\r\n SET: 'set',\r\n ADD: 'add',\r\n DELETE: 'delete',\r\n CLEAR: 'clear',\r\n} as const;\r\n\r\n// 内部共享常量\r\nexport const ITERATE_KEY = Symbol('iterate');\r\n","// src/effect.ts\r\n// 响应式副作用系统核心\r\n\r\nimport { ITERATE_KEY } from './constants';\r\nimport type { ReactiveEffectRunner } from './types';\r\nimport { warn } from '@lytjs/common-error';\r\nimport { _isSignalUntracked } from './signal';\r\nimport { getActiveEffectScope } from './effect-scope';\r\nimport { REACTIVITY_MAX_TRIGGER_DEPTH } from '@lytjs/common-constants';\r\n\r\n// ==================== 全局状态 ====================\r\n\r\nlet activeEffect: ReactiveEffect | undefined;\r\nlet _trackDepth = 0;\r\nconst targetMap = new WeakMap<object, Map<string | symbol, Dep>>();\r\nlet shouldTrack = true;\r\nconst trackStack: boolean[] = [];\r\n\r\n// ==================== 首次渲染优化 ====================\r\n\r\n/** 标记当前是否处于首次渲染优化期间 */\r\nlet isFirstRenderPass = false;\r\n\r\n/** 记录被跳过的追踪次数(用于调试和测试验证) */\r\nlet skippedTrackingCount = 0;\r\n\r\n/**\r\n * 包裹首次渲染过程,期间禁用响应式依赖收集。\r\n * 支持嵌套调用:如果外层已经处于首次渲染优化期间,\r\n * 内层调用不会提前重置标志位。\r\n */\r\nexport function withFirstRenderOptimization<T>(fn: () => T): T {\r\n const wasFirstRender = isFirstRenderPass;\r\n isFirstRenderPass = true;\r\n try {\r\n return fn();\r\n } finally {\r\n if (!wasFirstRender) {\r\n isFirstRenderPass = false;\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * 检查当前是否应跳过响应式依赖收集。\r\n * 在 withFirstRenderOptimization 执行期间返回 true。\r\n */\r\nexport function shouldSkipTracking(): boolean {\r\n return isFirstRenderPass;\r\n}\r\n\r\n/**\r\n * 获取被跳过的追踪次数(用于调试和测试)。\r\n */\r\nexport function getSkippedTrackingCount(): number {\r\n return skippedTrackingCount;\r\n}\r\n\r\n/**\r\n * 重置被跳过的追踪计数(用于测试)。\r\n */\r\nexport function resetSkippedTrackingCount(): void {\r\n skippedTrackingCount = 0;\r\n}\r\n\r\n// 只读访问器,防止外部修改内部状态\r\n\r\n/**\r\n * 获取当前活跃的 ReactiveEffect 实例。\r\n * 在 effect 执行期间返回当前正在运行的 effect,否则返回 undefined。\r\n *\r\n * @returns 当前活跃的 effect,如果没有则返回 undefined\r\n */\r\nexport function getActiveEffect(): ReactiveEffect | undefined {\r\n return activeEffect;\r\n}\r\n\r\n/**\r\n * 获取当前是否应该进行依赖追踪。\r\n * 可通过 pauseTracking/enableTracking 控制。\r\n *\r\n * @returns 是否应该进行依赖追踪\r\n */\r\nexport function getShouldTrack(): boolean {\r\n return shouldTrack;\r\n}\r\n\r\n// ==================== Dep ====================\r\n\r\n/**\r\n * 依赖集合类型,存储订阅某个响应式属性的所有 ReactiveEffect。\r\n */\r\nexport type Dep = Set<ReactiveEffect>;\r\n\r\n/**\r\n * 创建一个新的 Dep(依赖集合)。\r\n *\r\n * @returns 新的空 Dep 实例\r\n */\r\nexport const createDep = (): Dep => {\r\n return new Set() as Dep;\r\n};\r\n\r\n// ==================== Track / Trigger ====================\r\n\r\n/**\r\n * Maximum depth for nested trigger() calls to prevent infinite reactivity loops.\r\n * When triggerDepth exceeds this limit, further triggers are silently dropped\r\n * and a warning is emitted in DEV mode.\r\n */\r\nlet triggerDepth = 0;\r\n\r\n/**\r\n * 追踪响应式属性的依赖关系。\r\n * 当响应式属性被读取时调用,将当前活跃的 effect 记录为该属性的依赖。\r\n *\r\n * @param target - 被追踪的响应式对象\r\n * @param _type - 追踪操作类型(如 'get'、'has'、'iterate')\r\n * @param key - 被追踪的属性键\r\n */\r\nexport function track(target: object, _type: string, key: string | symbol) {\r\n if (!shouldTrack || activeEffect === undefined) return;\r\n // signal untrack 桥接:signalUntrack 期间跳过 effect 系统的依赖收集\r\n if (_isSignalUntracked()) return;\r\n\r\n let depsMap = targetMap.get(target);\r\n if (!depsMap) {\r\n targetMap.set(target, (depsMap = new Map()));\r\n }\r\n\r\n let dep = depsMap.get(key);\r\n if (!dep) {\r\n depsMap.set(key, (dep = createDep()));\r\n }\r\n\r\n trackEffect(dep);\r\n\r\n // 调试:触发 onTrack\r\n if (__DEV__ && activeEffect.onTrack) {\r\n activeEffect.onTrack({\r\n target,\r\n type: _type,\r\n key,\r\n });\r\n }\r\n}\r\n\r\n/**\r\n * 将当前活跃的 effect 添加到指定的依赖集合中。\r\n * 在首次渲染优化期间会跳过依赖收集。\r\n *\r\n * @param dep - 目标依赖集合\r\n */\r\nexport function trackEffect(dep: Dep) {\r\n // 首次渲染优化:跳过依赖收集\r\n if (shouldSkipTracking()) {\r\n skippedTrackingCount++;\r\n return;\r\n }\r\n // FIX: P1-01 移除重复的 shouldTrack/activeEffect 检查,\r\n // 这些检查已在调用方 track() 中完成,此处只需关注 dep 操作\r\n // FIX: P0-5 添加防御性检查,避免非空断言在公共 API 调用时不安全\r\n if (activeEffect && !dep.has(activeEffect)) {\r\n dep.add(activeEffect);\r\n activeEffect.deps.push(dep);\r\n }\r\n}\r\n\r\n/**\r\n * 触发响应式属性的依赖更新。\r\n * 当响应式属性被修改时调用,通知所有依赖该属性的 effect 重新执行。\r\n *\r\n * 根据操作类型(add/delete/set/clear)会触发不同的依赖集合:\r\n * - `add`:触发属性本身 + ITERATE_KEY(或数组 length)\r\n * - `delete`:触发属性本身 + ITERATE_KEY\r\n * - `set`:触发属性本身 + 数组 length(如果是整数键)\r\n * - `clear`:触发所有属性的依赖\r\n *\r\n * @param target - 被修改的响应式对象\r\n * @param type - 触发操作类型('set' | 'add' | 'delete' | 'clear')\r\n * @param key - 被修改的属性键\r\n * @param _newValue - 新值(可选,用于调试)\r\n * @param _oldValue - 旧值(可选,用于调试)\r\n */\r\nexport function trigger(\r\n target: object,\r\n type: string,\r\n key?: string | symbol,\r\n _newValue?: unknown,\r\n _oldValue?: unknown,\r\n) {\r\n const depsMap = targetMap.get(target);\r\n if (!depsMap) return;\r\n\r\n const deps: (Dep | undefined)[] = [];\r\n\r\n if (type === 'clear') {\r\n deps.push(...depsMap.values());\r\n } else {\r\n if (key !== undefined) {\r\n deps.push(depsMap.get(key));\r\n }\r\n\r\n if (type === 'add') {\r\n if (Array.isArray(target)) {\r\n deps.push(depsMap.get('length'));\r\n } else {\r\n deps.push(depsMap.get(ITERATE_KEY));\r\n }\r\n } else if (type === 'delete') {\r\n if (!Array.isArray(target)) {\r\n deps.push(depsMap.get(ITERATE_KEY));\r\n }\r\n } else if (type === 'set') {\r\n if (Array.isArray(target) && isIntegerKey(key)) {\r\n deps.push(depsMap.get('length'));\r\n }\r\n }\r\n }\r\n\r\n const effects: ReactiveEffect[] = [];\r\n for (const dep of deps) {\r\n if (dep) {\r\n for (const effect of dep) {\r\n effects.push(effect);\r\n }\r\n }\r\n }\r\n\r\n // 去重:同一个 effect 可能同时存在于多个 dep 中\r\n triggerEffects([...new Set(effects)]);\r\n}\r\n\r\n/**\r\n * 执行一组 effect 的触发。\r\n * 优先执行 computed effect,再执行普通 effect。\r\n * 内置递归深度限制,超过最大深度时静默丢弃并发出警告。\r\n *\r\n * @param effects - 需要触发的 ReactiveEffect 数组\r\n */\r\nexport function triggerEffects(effects: ReactiveEffect[]) {\r\n if (triggerDepth > REACTIVITY_MAX_TRIGGER_DEPTH) {\r\n // FIX: P2-1 triggerDepth 超限时改为 warn + 静默丢弃,与 Vue 3 行为一致。\r\n // 之前直接 throw Error 过于激进,会导致整个响应式链断裂。\r\n // 改为仅发出警告并丢弃后续 trigger,避免因单个无限循环导致整个应用崩溃。\r\n if (__DEV__) {\r\n warn(\r\n `[lytjs/reactivity] Maximum trigger depth (${REACTIVITY_MAX_TRIGGER_DEPTH}) exceeded. ` +\r\n `Possible infinite reactivity loop detected. Further triggers are silently dropped. ` +\r\n `triggerDepth=${triggerDepth}`,\r\n );\r\n }\r\n return;\r\n }\r\n triggerDepth++;\r\n try {\r\n for (const effect of effects) {\r\n if (effect.computed) {\r\n triggerEffect(effect);\r\n }\r\n }\r\n for (const effect of effects) {\r\n if (!effect.computed) {\r\n triggerEffect(effect);\r\n }\r\n }\r\n } finally {\r\n triggerDepth--;\r\n }\r\n}\r\n\r\nfunction triggerEffect(effect: ReactiveEffect) {\r\n if (effect !== activeEffect || effect.allowRecurse) {\r\n if (effect.scheduler) {\r\n effect.scheduler();\r\n } else {\r\n effect.run();\r\n }\r\n }\r\n}\r\n\r\n// ==================== ReactiveEffect ====================\r\n\r\n/**\r\n * 响应式副作用类。\r\n * 封装一个副作用函数,支持依赖自动收集、调度执行和手动停止。\r\n *\r\n * 创建时会自动注册到当前活跃的 effectScope 中。\r\n *\r\n * @typeParam T - 副作用函数的返回值类型\r\n *\r\n * @example\r\n * ```ts\r\n * const eff = new ReactiveEffect(() => {\r\n * console.log(state.count);\r\n * });\r\n * eff.run(); // 执行副作用,同时收集依赖\r\n * eff.stop(); // 停止副作用,清理所有依赖\r\n * ```\r\n */\r\nexport class ReactiveEffect<T = unknown> {\r\n active = true;\r\n deps: Dep[] = [];\r\n parent: ReactiveEffect | undefined = undefined;\r\n computed?: boolean;\r\n allowRecurse?: boolean;\r\n onStop?: () => void;\r\n onTrack?: (event: { target: object; key: string | symbol; type: string }) => void;\r\n onTrigger?: (event: {\r\n target: object;\r\n key: string | symbol;\r\n type: string;\r\n newValue?: unknown;\r\n oldValue?: unknown;\r\n }) => void;\r\n onError?: (error: Error) => void;\r\n // 运行前清理(onEffectCleanup 注册的)\r\n _cleanups: Array<() => void> = [];\r\n\r\n constructor(\r\n public fn: () => T,\r\n public scheduler?: (...args: unknown[]) => unknown,\r\n ) {\r\n // 自动注册到当前活跃的 effectScope\r\n const scope = getActiveEffectScope();\r\n if (scope && scope.active) {\r\n scope.effects.push(this);\r\n }\r\n }\r\n\r\n run(): T | undefined {\r\n if (!this.active) {\r\n return undefined;\r\n }\r\n\r\n // 在重新执行前调用 cleanup\r\n if (this._cleanups.length > 0) {\r\n for (let i = 0; i < this._cleanups.length; i++) {\r\n this._cleanups[i]!();\r\n }\r\n this._cleanups.length = 0;\r\n }\r\n\r\n const prevShouldTrack = shouldTrack;\r\n try {\r\n this.parent = activeEffect;\r\n // eslint-disable-next-line @typescript-eslint/no-this-alias\r\n activeEffect = this;\r\n shouldTrack = true;\r\n _trackDepth++;\r\n\r\n return this.fn();\r\n } catch (error) {\r\n if (this.onError) {\r\n this.onError(error as Error);\r\n return undefined;\r\n }\r\n throw error;\r\n } finally {\r\n _trackDepth--;\r\n activeEffect = this.parent;\r\n shouldTrack = prevShouldTrack;\r\n this.parent = undefined;\r\n }\r\n }\r\n\r\n stop(): void {\r\n if (this.active) {\r\n cleanupEffect(this);\r\n if (this._cleanups.length > 0) {\r\n for (let i = 0; i < this._cleanups.length; i++) {\r\n this._cleanups[i]!();\r\n }\r\n this._cleanups.length = 0;\r\n }\r\n if (this.onStop) {\r\n this.onStop();\r\n }\r\n this.active = false;\r\n this.scheduler = undefined;\r\n }\r\n }\r\n}\r\n\r\nfunction cleanupEffect(effect: ReactiveEffect) {\r\n const { deps } = effect;\r\n for (let i = 0; i < deps.length; i++) {\r\n deps[i]!.delete(effect);\r\n }\r\n deps.length = 0;\r\n}\r\n\r\n// ==================== 公共 API ====================\r\n\r\n// Function overloads for effect()\r\n// Non-lazy effect: fn returns void, preventing accidental return value usage\r\n\r\n/**\r\n * 创建一个响应式副作用并立即执行。\r\n *\r\n * 副作用函数会在执行期间自动追踪所使用的响应式属性,\r\n * 当这些属性发生变化时,副作用会重新执行。\r\n *\r\n * @param fn - 副作用函数,返回 void\r\n * @param options - 配置选项\r\n * @param options.lazy - 是否延迟执行(false 时立即执行)\r\n * @param options.scheduler - 自定义调度器,替代默认的立即执行行为\r\n * @param options.allowRecurse - 是否允许副作用递归触发自身\r\n * @param options.onStop - 副作用停止时的回调\r\n * @param options.onTrack - 依赖被追踪时的调试回调\r\n * @param options.onTrigger - 依赖被触发时的调试回调\r\n * @returns 副作用运行器,可调用 run() 手动执行或 stop() 停止\r\n */\r\nexport function effect(\r\n fn: () => void,\r\n options?: {\r\n lazy?: false;\r\n scheduler?: (...args: unknown[]) => unknown;\r\n allowRecurse?: boolean;\r\n onStop?: () => void;\r\n onTrack?: (event: { target: object; key: string | symbol; type: string }) => void;\r\n onTrigger?: (event: {\r\n target: object;\r\n key: string | symbol;\r\n type: string;\r\n newValue?: unknown;\r\n oldValue?: unknown;\r\n }) => void;\r\n },\r\n): ReactiveEffectRunner<void>;\r\n\r\n// Lazy effect: preserves generic return value type (used by computed etc.)\r\nexport function effect<T>(\r\n fn: () => T,\r\n options: {\r\n lazy: true;\r\n scheduler?: (...args: unknown[]) => unknown;\r\n allowRecurse?: boolean;\r\n onStop?: () => void;\r\n onTrack?: (event: { target: object; key: string | symbol; type: string }) => void;\r\n onTrigger?: (event: {\r\n target: object;\r\n key: string | symbol;\r\n type: string;\r\n newValue?: unknown;\r\n oldValue?: unknown;\r\n }) => void;\r\n },\r\n): ReactiveEffectRunner<T>;\r\n\r\n// 统一实现签名\r\nexport function effect<T = unknown>(\r\n fn: () => T,\r\n options?: {\r\n lazy?: boolean;\r\n scheduler?: (...args: unknown[]) => unknown;\r\n allowRecurse?: boolean;\r\n onStop?: () => void;\r\n onTrack?: (event: { target: object; key: string | symbol; type: string }) => void;\r\n onTrigger?: (event: {\r\n target: object;\r\n key: string | symbol;\r\n type: string;\r\n newValue?: unknown;\r\n oldValue?: unknown;\r\n }) => void;\r\n onError?: (error: Error) => void;\r\n },\r\n): ReactiveEffectRunner<T> {\r\n const _effect = new ReactiveEffect(fn);\r\n if (options) {\r\n // 仅提取已知合法选项,防止覆盖内部属性(如 fn、active)\r\n _effect.scheduler = options.scheduler;\r\n _effect.allowRecurse = options.allowRecurse;\r\n _effect.onStop = options.onStop;\r\n _effect.onTrack = options.onTrack;\r\n _effect.onTrigger = options.onTrigger;\r\n _effect.onError = options.onError;\r\n }\r\n if (!options || !options.lazy) {\r\n _effect.run();\r\n }\r\n const runner = _effect.run.bind(_effect) as ReactiveEffectRunner<T>;\r\n runner.effect = _effect;\r\n return runner;\r\n}\r\n\r\n/**\r\n * 停止一个响应式副作用。\r\n * 清理所有依赖关系,并调用 onStop 回调。\r\n *\r\n * @param runner - 由 effect() 返回的副作用运行器\r\n */\r\nexport function stop(runner: ReactiveEffectRunner): void {\r\n runner.effect.stop();\r\n}\r\n\r\n/**\r\n * 暂停依赖追踪。\r\n * 调用后,响应式属性的读取不会建立依赖关系。\r\n * 可通过 resetTracking() 恢复。\r\n */\r\nexport function pauseTracking(): void {\r\n trackStack.push(shouldTrack);\r\n shouldTrack = false;\r\n}\r\n\r\n/**\r\n * 启用依赖追踪。\r\n * 将当前追踪状态压入栈中并设为 true。\r\n * 可通过 resetTracking() 恢复到之前的状态。\r\n */\r\nexport function enableTracking(): void {\r\n trackStack.push(shouldTrack);\r\n shouldTrack = true;\r\n}\r\n\r\n/**\r\n * 重置依赖追踪状态到上一次暂停/启用之前的状态。\r\n * 从追踪栈中弹出最近的状态并恢复。\r\n */\r\nexport function resetTracking(): void {\r\n const last = trackStack.pop();\r\n shouldTrack = last === undefined ? true : last;\r\n}\r\n\r\n/**\r\n * 批量执行函数,期间暂停依赖追踪。\r\n * 与 signalBatch 不同,batch 侧重于暂停追踪而非延迟通知。\r\n * 支持嵌套调用,内层 batch 不会提前恢复追踪状态。\r\n *\r\n * @param fn - 需要批量执行的函数\r\n *\r\n * @example\r\n * ```ts\r\n * batch(() => {\r\n * state.a = 1; // 不会触发依赖更新\r\n * state.b = 2; // 不会触发依赖更新\r\n * }); // 函数结束后恢复追踪\r\n * ```\r\n */\r\nexport function batch(fn: () => void): void {\r\n const stackLength = trackStack.length;\r\n pauseTracking();\r\n try {\r\n fn();\r\n } finally {\r\n // 恢复到 batch 调用前的 stack 长度,确保嵌套安全\r\n while (trackStack.length > stackLength) {\r\n trackStack.pop();\r\n }\r\n shouldTrack = trackStack.length > 0 ? trackStack[trackStack.length - 1]! : true;\r\n }\r\n}\r\n\r\n/**\r\n * batchAsync - like batch but supports async functions.\r\n * Pauses tracking during fn execution (including after await),\r\n * and restores tracking state when fn completes (or throws).\r\n * Returns a Promise.\r\n */\r\nexport function batchAsync(fn: () => void | Promise<void>): Promise<void> {\r\n const stackLength = trackStack.length;\r\n pauseTracking();\r\n const restoreTracking = () => {\r\n while (trackStack.length > stackLength) {\r\n trackStack.pop();\r\n }\r\n shouldTrack = trackStack.length > 0 ? trackStack[trackStack.length - 1]! : true;\r\n };\r\n try {\r\n const result = fn();\r\n if (result && typeof result === 'object' && 'then' in result) {\r\n return (result as Promise<void>).finally(restoreTracking);\r\n }\r\n // 同步路径:立即恢复 tracking 状态\r\n restoreTracking();\r\n return Promise.resolve();\r\n } catch (e) {\r\n restoreTracking();\r\n return Promise.reject(e);\r\n }\r\n}\r\n\r\n/**\r\n * untrack - execute fn without tracking dependencies.\r\n * Semantically different from batch: untrack means \"run but don't track\".\r\n * Returns the return value of fn.\r\n */\r\nexport function untrack<T>(fn: () => T): T {\r\n const stackLength = trackStack.length;\r\n pauseTracking();\r\n try {\r\n return fn();\r\n } finally {\r\n while (trackStack.length > stackLength) {\r\n trackStack.pop();\r\n }\r\n shouldTrack = trackStack.length > 0 ? trackStack[trackStack.length - 1]! : true;\r\n }\r\n}\r\n\r\n/**\r\n * 在当前活跃的 effect 上注册一个清理回调。\r\n * 该回调会在 effect 重新执行前或停止时被调用,用于清理副作用资源。\r\n *\r\n * @param fn - 清理回调函数\r\n * @param failSilently - 当没有活跃 effect 时是否静默失败(默认 false,开发模式下会发出警告)\r\n *\r\n * @example\r\n * ```ts\r\n * effect(() => {\r\n * const timer = setInterval(() => console.log('tick'), 1000);\r\n * onEffectCleanup(() => clearInterval(timer));\r\n * });\r\n * ```\r\n */\r\nexport function onEffectCleanup(fn: () => void, failSilently = false): void {\r\n if (activeEffect === undefined) {\r\n if (!failSilently && __DEV__) {\r\n warn('onEffectCleanup() was called when there was no active effect to associate with.');\r\n }\r\n return;\r\n }\r\n activeEffect._cleanups.push(fn);\r\n}\r\n\r\n// ==================== 辅助 ====================\r\n\r\n/**\r\n * 检查给定的键是否为有效的整数键。\r\n * 用于判断数组索引是否为合法的整数值。\r\n *\r\n * @param key - 需要检查的键值\r\n * @returns 如果是有效的整数键返回 true,否则返回 false\r\n */\r\nexport function isIntegerKey(key: unknown): boolean {\r\n return (\r\n typeof key === 'string' &&\r\n key !== 'NaN' &&\r\n key[0] !== '-' &&\r\n '' + parseInt(key, 10) === key &&\r\n Number.isSafeInteger(Number(key))\r\n );\r\n}\r\n","/**\r\n * @lytjs/reactivity - Signal\r\n * 独立自包含的 Signal 响应式原语。\r\n * 拥有独立的订阅/通知机制,同时桥接 effect 系统保持互操作性。\r\n */\r\n\r\nimport { SignalSymbol, ComputedSignalSymbol, TrackOpTypes, TriggerOpTypes } from './constants';\r\nimport { track, trigger } from './effect';\r\nimport { REACTIVITY_MAX_TRIGGER_DEPTH } from '@lytjs/common-constants';\r\nimport type { Subscriber } from './shared/types';\r\n\r\n// ============================================================\r\n// 类型定义\r\n// ============================================================\r\n\r\n/** 订阅者回调 */\r\nexport type { Subscriber };\r\n\r\n/** Signal 只读接口 */\r\nexport interface Signal<T = unknown> {\r\n /** 读取当前值 */\r\n (): T;\r\n readonly [SignalSymbol]: true;\r\n}\r\n\r\n/** WritableSignal 可写接口 */\r\nexport interface WritableSignal<T = unknown> extends Signal<T> {\r\n /** 设置新值 */\r\n set(newValue: T): void;\r\n /** 通过 updater 函数更新值 */\r\n update(updater: (prev: T) => T): void;\r\n /** 停止所有订阅通知,释放资源 */\r\n dispose(): void;\r\n /** @internal 订阅变更通知 */\r\n _subscribe(subscriber: Subscriber): () => void;\r\n /** FIX: P1-5 REACTIVITY-NEW-03 - 手动清理依赖,防止内存泄漏 */\r\n cleanup(): void;\r\n}\r\n\r\n/** ComputedSignal 计算信号接口 */\r\nexport interface ComputedSignal<T = unknown> extends Signal<T> {\r\n /** 停止计算信号的依赖追踪和更新 */\r\n dispose(): void;\r\n /** @deprecated 使用 dispose() */\r\n stop?: () => void;\r\n readonly [ComputedSignalSymbol]: true;\r\n}\r\n\r\n/** WritableComputedSignal 可写计算信号接口 */\r\nexport interface WritableComputedSignal<T = unknown> extends ComputedSignal<T> {\r\n /** 设置新值(通过 setter 函数) */\r\n set(newValue: T): void;\r\n}\r\n\r\n/** ReadonlySignal 只读信号接口 */\r\nexport interface ReadonlySignal<T = unknown> {\r\n /** 读取当前值 */\r\n (): T;\r\n readonly [SignalSymbol]: true;\r\n}\r\n\r\n// ============================================================\r\n// 全局追踪状态\r\n// ============================================================\r\n\r\n/** 当前活跃的订阅者(computed 读取 signal 时自动追踪) */\r\nlet activeSubscriber: Subscriber | null = null;\r\n\r\n/** 是否处于 untrack 模式 */\r\nlet isUntracked = false;\r\n\r\n/** 依赖追踪回调:computed 使用此回调记录 signal 依赖关系 */\r\nlet trackDependency: ((signal: WritableSignal<unknown>, unsubscribe: () => void) => void) | null =\r\n null;\r\n\r\n/** 当前 batch 嵌套深度 */\r\nlet batchDepth = 0;\r\n\r\n/** FIX: P2-05 batch 嵌套深度限制,防止无限递归 */\r\nconst MAX_BATCH_DEPTH = 100;\r\n\r\n/** batch 期间待通知的订阅者 */\r\nconst pendingNotifications = new Set<Subscriber>();\r\n\r\n/** batch 期间待执行的 effect 系统 trigger 操作(自动去重) */\r\n// FIX: P1-05 使用 Map<symbol,...> 替代 Map<string,...>,\r\n// 避免不同 signal 的 Symbol().toString() 产生相同的字符串 key 导致去重错误\r\n// FIX: P2-7 去重时保留最新 newValue:当同一个 signalKey 多次 set 时,\r\n// Map.set 会覆盖旧值,确保最终 trigger 使用最新的 newValue,\r\n// 避免因去重导致订阅者收到过期的旧值。\r\nconst pendingTriggerOps = new Map<\r\n symbol,\r\n { store: Record<symbol, unknown>; signalKey: symbol; newValue?: unknown }\r\n>();\r\n\r\n/** 是否正在执行通知 */\r\nlet isNotifying = false;\r\n\r\n// ============================================================\r\n// signal — 核心 Signal 原语\r\n// ============================================================\r\n\r\n/**\r\n * 创建一个可写 Signal。\r\n * 使用闭包变量存储值,Set 管理订阅者。\r\n */\r\nexport function signal<T>(initialValue: T): WritableSignal<T> {\r\n let value = initialValue;\r\n const subscribers = new Set<Subscriber>();\r\n let disposed = false;\r\n\r\n // effect 系统桥接:使用 store 对象作为 track/trigger 的 target\r\n const store: Record<symbol, T> = {};\r\n const SIGNAL_KEY = Symbol('signal_value');\r\n store[SIGNAL_KEY] = initialValue;\r\n\r\n const signalFn = function signalFn(): T {\r\n // Signal 内部追踪\r\n // FIX: P0-01 闭包捕获过期 activeSubscriber — 立即捕获当前订阅者引用,\r\n // 避免闭包中的 activeSubscriber 在异步回调中被修改后指向错误的订阅者\r\n const currentSubscriber = activeSubscriber;\r\n if (currentSubscriber && !isUntracked && !disposed) {\r\n if (!subscribers.has(currentSubscriber)) {\r\n subscribers.add(currentSubscriber);\r\n if (trackDependency) {\r\n trackDependency(signalFn as WritableSignal<unknown>, () => {\r\n subscribers.delete(currentSubscriber);\r\n });\r\n }\r\n }\r\n }\r\n // effect 系统桥接追踪\r\n if (!disposed) {\r\n track(store, TrackOpTypes.GET, SIGNAL_KEY);\r\n }\r\n return value;\r\n } as WritableSignal<T>;\r\n\r\n Object.defineProperty(signalFn, SignalSymbol, { value: true });\r\n\r\n signalFn.set = (newValue: T): void => {\r\n if (disposed) return;\r\n if (Object.is(newValue, value)) return;\r\n value = newValue;\r\n store[SIGNAL_KEY] = newValue;\r\n notifySubscribers(subscribers, store, SIGNAL_KEY, newValue);\r\n };\r\n\r\n signalFn.update = (updater: (prev: T) => T): void => {\r\n if (disposed) return;\r\n const newValue = updater(value);\r\n if (Object.is(newValue, value)) return;\r\n value = newValue;\r\n store[SIGNAL_KEY] = newValue;\r\n notifySubscribers(subscribers, store, SIGNAL_KEY, newValue);\r\n };\r\n\r\n signalFn.dispose = (): void => {\r\n disposed = true;\r\n subscribers.clear();\r\n };\r\n\r\n // FIX: P1-5 REACTIVITY-NEW-03 - 添加 cleanup 方法,允许手动清理依赖\r\n signalFn.cleanup = (): void => {\r\n // 清理所有订阅者,但保持 signal 可用\r\n subscribers.clear();\r\n // 清理 effect 系统桥接的依赖:直接触发 SET 通知使依赖失效,\r\n // 不传旧值以避免读取 signal 值\r\n trigger(store, TriggerOpTypes.SET, SIGNAL_KEY);\r\n };\r\n\r\n signalFn._subscribe = (subscriber: Subscriber): (() => void) => {\r\n if (disposed) return () => {};\r\n subscribers.add(subscriber);\r\n return () => subscribers.delete(subscriber);\r\n };\r\n\r\n return signalFn;\r\n}\r\n\r\n// ============================================================\r\n// computed — 独立计算信号\r\n// ============================================================\r\n\r\n/**\r\n * 内部工厂函数:创建计算信号的核心逻辑。\r\n * computed 和 writableComputedSignal 共享此实现,消除约 80 行重复代码。\r\n */\r\nfunction createComputedSignalInternal<T>(\r\n getter: () => T,\r\n typeName: string,\r\n): {\r\n computedFn: ComputedSignal<T>;\r\n invalidate: () => void;\r\n dispose: () => void;\r\n} {\r\n let value: T | undefined;\r\n let dirty = true;\r\n let isComputing = false;\r\n const dependencies = new Map<WritableSignal<unknown>, () => void>();\r\n const subscribers = new Set<Subscriber>();\r\n let disposed = false;\r\n\r\n // effect 系统桥接:使用 store 对象作为 track/trigger 的 target\r\n const store: Record<symbol, unknown> = {};\r\n const COMPUTED_SIGNAL_KEY = Symbol('computed_signal_value');\r\n\r\n const invalidate = (): void => {\r\n if (disposed) return;\r\n dirty = true;\r\n // effect 系统桥接触发\r\n trigger(store, TriggerOpTypes.SET, COMPUTED_SIGNAL_KEY);\r\n const subs = Array.from(subscribers);\r\n for (const sub of subs) {\r\n sub();\r\n }\r\n };\r\n\r\n const computedFn = function computedFn(): T {\r\n if (disposed) return value as T;\r\n\r\n // effect 系统桥接追踪\r\n track(store, TrackOpTypes.GET, COMPUTED_SIGNAL_KEY);\r\n\r\n // 追踪:如果有活跃订阅者,注册自身\r\n if (activeSubscriber && !isUntracked) {\r\n subscribers.add(activeSubscriber);\r\n }\r\n\r\n if (dirty) {\r\n if (isComputing) {\r\n throw new Error(`[lytjs/signal] Circular dependency detected in ${typeName}.`);\r\n }\r\n isComputing = true;\r\n try {\r\n // 清理旧依赖(调用 unsubscribe 函数)\r\n for (const unsubscribe of dependencies.values()) {\r\n unsubscribe();\r\n }\r\n dependencies.clear();\r\n\r\n // 在活跃订阅者上下文中执行 getter,自动追踪新依赖\r\n const prevSubscriber = activeSubscriber;\r\n const prevTrackDependency = trackDependency;\r\n activeSubscriber = invalidate; // 注册 invalidate 作为依赖的订阅者\r\n trackDependency = (dep: WritableSignal<unknown>, unsubscribe: () => void) => {\r\n dependencies.set(dep, unsubscribe);\r\n };\r\n try {\r\n value = getter();\r\n dirty = false;\r\n } finally {\r\n activeSubscriber = prevSubscriber;\r\n trackDependency = prevTrackDependency;\r\n }\r\n } finally {\r\n isComputing = false;\r\n }\r\n }\r\n\r\n return value as T;\r\n } as ComputedSignal<T>;\r\n\r\n Object.defineProperty(computedFn, ComputedSignalSymbol, { value: true });\r\n\r\n const dispose = (): void => {\r\n disposed = true;\r\n for (const unsubscribe of dependencies.values()) {\r\n unsubscribe();\r\n }\r\n dependencies.clear();\r\n subscribers.clear();\r\n };\r\n\r\n return { computedFn, invalidate, dispose };\r\n}\r\n\r\n/**\r\n * 创建一个计算信号。\r\n * 惰性求值、自动依赖追踪与清理、循环依赖检测。\r\n */\r\nexport function computed<T>(fn: () => T): ComputedSignal<T> {\r\n const { computedFn, dispose } = createComputedSignalInternal(fn, 'computed signal');\r\n\r\n computedFn.dispose = dispose;\r\n computedFn.stop = dispose;\r\n\r\n return computedFn;\r\n}\r\n\r\n// ============================================================\r\n// writableComputed — 可写计算信号\r\n// ============================================================\r\n\r\n/**\r\n * 创建一个可写计算信号。\r\n * 通过 getter 读取计算值,通过 setter 写入值。\r\n * setter 通常会间接更新 getter 依赖的底层 signal。\r\n */\r\nexport function writableComputedSignal<T>(\r\n getter: () => T,\r\n setter: (value: T) => void,\r\n): WritableComputedSignal<T> {\r\n const { computedFn, dispose } = createComputedSignalInternal(getter, 'writable computed signal');\r\n\r\n (computedFn as WritableComputedSignal<T>).set = (newValue: T): void => {\r\n setter(newValue);\r\n };\r\n\r\n computedFn.dispose = dispose;\r\n computedFn.stop = dispose;\r\n\r\n return computedFn as WritableComputedSignal<T>;\r\n}\r\n\r\n// ============================================================\r\n// batch — 批量更新\r\n// ============================================================\r\n\r\n/**\r\n * 在批处理中执行函数。\r\n * batch 内多次 signal.set 只在函数结束后统一触发一次通知。\r\n */\r\nexport function signalBatch(fn: () => void): void {\r\n // FIX: P2-05 batch 嵌套深度限制\r\n if (batchDepth >= MAX_BATCH_DEPTH) {\r\n if (__DEV__) {\r\n console.warn(\r\n `[lytjs/signal] signalBatch() nesting depth exceeded ${MAX_BATCH_DEPTH}. ` +\r\n `This may indicate an infinite loop. The batch call will be executed synchronously.`,\r\n );\r\n }\r\n fn();\r\n return;\r\n }\r\n batchDepth++;\r\n try {\r\n fn();\r\n } finally {\r\n batchDepth--;\r\n if (batchDepth === 0) {\r\n flushPendingNotifications();\r\n }\r\n }\r\n}\r\n\r\n// ============================================================\r\n// untrack — 取消追踪\r\n// ============================================================\r\n\r\n/**\r\n * 在取消追踪模式中执行函数。\r\n * 函数内读取 signal 不会建立依赖关系。\r\n */\r\nexport function signalUntrack<T>(fn: () => T): T {\r\n const prevIsUntracked = isUntracked;\r\n isUntracked = true;\r\n try {\r\n return fn();\r\n } finally {\r\n isUntracked = prevIsUntracked;\r\n }\r\n}\r\n\r\n/** @internal 检查当前是否处于 untrack 模式(供 effect 系统桥接使用) */\r\nexport function _isSignalUntracked(): boolean {\r\n return isUntracked;\r\n}\r\n\r\n// ============================================================\r\n// 内部通知机制\r\n// ============================================================\r\n\r\nfunction notifySubscribers(\r\n subscribers: Set<Subscriber>,\r\n store?: Record<symbol, unknown>,\r\n signalKey?: symbol,\r\n newValue?: unknown,\r\n): void {\r\n // FIX: P0-02 当 isNotifying 为 true 时也走 batch 路径,\r\n // 避免在 flushPendingNotifications 执行期间嵌套触发同步通知导致无限递归\r\n if (batchDepth > 0 || isNotifying) {\r\n const it = subscribers.values();\r\n let next = it.next();\r\n while (!next.done) {\r\n pendingNotifications.add(next.value);\r\n next = it.next();\r\n }\r\n // effect 系统桥接:batch 期间也延迟 trigger(去重)\r\n if (store && signalKey !== undefined) {\r\n // FIX: P1-05 直接使用 symbol 作为 key,避免 String() 转换导致的 key 冲突\r\n pendingTriggerOps.set(signalKey, { store, signalKey, newValue });\r\n }\r\n return;\r\n }\r\n const it = subscribers.values();\r\n let next = it.next();\r\n while (!next.done) {\r\n next.value();\r\n next = it.next();\r\n }\r\n // effect 系统桥接触发\r\n if (store && signalKey !== undefined) {\r\n trigger(store, TriggerOpTypes.SET, signalKey, newValue);\r\n }\r\n}\r\n\r\nfunction flushPendingNotifications(): void {\r\n if (isNotifying) return;\r\n isNotifying = true;\r\n try {\r\n let iterations = 0;\r\n while ((pendingNotifications.size > 0 || pendingTriggerOps.size > 0) && iterations < REACTIVITY_MAX_TRIGGER_DEPTH) {\r\n const notifications = new Set(pendingNotifications);\r\n const triggers = new Set(pendingTriggerOps.values());\r\n pendingNotifications.clear();\r\n pendingTriggerOps.clear();\r\n \r\n const notifIt = notifications.values();\r\n let notifNext = notifIt.next();\r\n while (!notifNext.done) {\r\n notifNext.value();\r\n notifNext = notifIt.next();\r\n }\r\n \r\n // 执行延迟的 effect 系统 trigger(已去重)\r\n const triggerIt = triggers.values();\r\n let triggerNext = triggerIt.next();\r\n while (!triggerNext.done) {\r\n const { store, signalKey, newValue } = triggerNext.value;\r\n trigger(store, TriggerOpTypes.SET, signalKey, newValue);\r\n triggerNext = triggerIt.next();\r\n }\r\n \r\n iterations++;\r\n }\r\n } finally {\r\n isNotifying = false;\r\n }\r\n}\r\n\r\n// ============================================================\r\n// 适配器层 — 旧 API 兼容\r\n// ============================================================\r\n\r\n/**\r\n * @deprecated 使用 computed() 代替\r\n */\r\nexport function computedSignal<T>(fn: () => T): ComputedSignal<T> {\r\n return computed(fn);\r\n}\r\n\r\n/** 读取 signal 值 */\r\nexport function valueOf<T>(sig: Signal<T>): T {\r\n return sig();\r\n}\r\n\r\n/** 设置 signal 值(适配器) */\r\nexport function set<T>(sig: WritableSignal<T> | ReadonlySignal<T>, newValue: T): void {\r\n // FIX: P2-35 使用类型守卫检查是否为 WritableSignal\r\n if (isWritableSignal(sig)) {\r\n sig.set(newValue);\r\n }\r\n // ReadonlySignal: 静默忽略(保持旧行为)\r\n}\r\n\r\n/** 类型守卫:检查信号是否为可写信号 */\r\nfunction isWritableSignal<T>(sig: WritableSignal<T> | ReadonlySignal<T>): sig is WritableSignal<T> {\r\n return typeof sig === 'function' && 'set' in sig;\r\n}\r\n\r\n/** 通过 updater 更新 signal 值(适配器) */\r\nexport function update<T>(sig: WritableSignal<T>, updater: (prev: T) => T): void {\r\n sig.update(updater);\r\n}\r\n\r\n/** 创建只读 signal(适配器) */\r\nexport function readonlySignal<T>(sig: Signal<T>): ReadonlySignal<T> {\r\n const readonlyFn = function readonlyFn(): T {\r\n return sig();\r\n } as ReadonlySignal<T>;\r\n Object.defineProperty(readonlyFn, SignalSymbol, { value: true });\r\n return readonlyFn;\r\n}\r\n\r\n// ============================================================\r\n// 调试/测试 API\r\n// ============================================================\r\n\r\n/** @internal 获取当前活跃订阅者(仅用于测试) */\r\nexport function _getActiveSubscriber(): Subscriber | null {\r\n return activeSubscriber;\r\n}\r\n\r\n/** @internal 获取 batch 深度(仅用于测试) */\r\nexport function _getBatchDepth(): number {\r\n return batchDepth;\r\n}\r\n\r\n/** @internal 获取待通知订阅者数量(仅用于测试) */\r\nexport function _getPendingNotificationsCount(): number {\r\n return pendingNotifications.size;\r\n}\r\n\r\n/** @internal 重置全局状态(仅用于测试) */\r\nexport function _resetSignalGlobalState(): void {\r\n activeSubscriber = null;\r\n isUntracked = false;\r\n batchDepth = 0;\r\n pendingNotifications.clear();\r\n isNotifying = false;\r\n // FIX: P1-4 REACTIVITY-NEW-05 - 遗漏 pendingTriggerOps 清理\r\n // 确保测试间状态完全隔离,避免 pendingTriggerOps 泄漏导致测试不稳定\r\n pendingTriggerOps.clear();\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/constants.ts","../src/effect.ts","../src/signal.ts"],"names":["signalFn","computedFn","it","next","REACTIVITY_MAX_TRIGGER_DEPTH","readonlyFn"],"mappings":"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","file":"signal.mjs","sourcesContent":["// src/constants.ts\r\n// 内部符号常量\r\n\r\n// DEV 检测方式说明:\r\n// 统一使用 typeof 检测方式,兼容编译时 define/replace 替换和未配置构建替换的场景。\r\n// 当打包工具(如 rollup/esbuild)通过 define 插件将 __DEV__ 替换为字面量 true/false 时,\r\n// typeof 检测会被优化为直接的字面量判断,实现死代码消除(DCE)。\r\n// 当未配置构建替换时,typeof 检测也能安全降级为 false,避免 ReferenceError。\r\nconst DEV = typeof __DEV__ !== 'undefined' ? __DEV__ : false;\r\n\r\nexport const RefSymbol: unique symbol = Symbol(DEV ? 'ref' : undefined);\r\nexport const ShallowRefSymbol: unique symbol = Symbol(DEV ? 'shallow_ref' : undefined);\r\nexport const ComputedRefSymbol: unique symbol = Symbol(DEV ? 'computed_ref' : undefined);\r\nexport const ReactiveSymbol: unique symbol = Symbol(DEV ? 'reactive' : undefined);\r\nexport const ReadonlySymbol: unique symbol = Symbol(DEV ? 'readonly' : undefined);\r\nexport const SignalSymbol: unique symbol = Symbol(DEV ? 'signal' : undefined);\r\nexport const ComputedSignalSymbol: unique symbol = Symbol(DEV ? 'computed_signal' : undefined);\r\n\r\n// ReactiveFlags - 用于 Proxy handler 内部标记\r\nexport const ReactiveFlags = {\r\n IS_REACTIVE: '__v_isReactive',\r\n IS_READONLY: '__v_isReadonly',\r\n IS_SHALLOW: '__v_isShallow',\r\n IS_REF: '__v_isRef',\r\n RAW: '__v_raw',\r\n SKIP: '__v_skip',\r\n} as const;\r\n\r\n// Track/Trigger 操作类型\r\nexport const TrackOpTypes = {\r\n GET: 'get',\r\n HAS: 'has',\r\n ITERATE: 'iterate',\r\n} as const;\r\n\r\nexport const TriggerOpTypes = {\r\n SET: 'set',\r\n ADD: 'add',\r\n DELETE: 'delete',\r\n CLEAR: 'clear',\r\n} as const;\r\n\r\n// 内部共享常量\r\nexport const ITERATE_KEY = Symbol('iterate');\r\n","// src/effect.ts\r\n// 响应式副作用系统核心\r\n\r\nimport { ITERATE_KEY } from './constants';\r\nimport type { ReactiveEffectRunner } from './types';\r\nimport { warn } from '@lytjs/common-error';\r\nimport { _isSignalUntracked } from './signal';\r\nimport { getActiveEffectScope } from './effect-scope';\r\nimport { REACTIVITY_MAX_TRIGGER_DEPTH } from '@lytjs/common-constants';\r\n\r\n// ==================== 全局状态 ====================\r\n\r\nlet activeEffect: ReactiveEffect | undefined;\r\nlet _trackDepth = 0;\r\nconst targetMap = new WeakMap<object, Map<string | symbol, Dep>>();\r\nlet shouldTrack = true;\r\nconst trackStack: boolean[] = [];\r\n\r\n// ==================== 首次渲染优化 ====================\r\n\r\n/** 标记当前是否处于首次渲染优化期间 */\r\nlet isFirstRenderPass = false;\r\n\r\n/** 记录被跳过的追踪次数(用于调试和测试验证) */\r\nlet skippedTrackingCount = 0;\r\n\r\n/**\r\n * 包裹首次渲染过程,期间禁用响应式依赖收集。\r\n * 支持嵌套调用:如果外层已经处于首次渲染优化期间,\r\n * 内层调用不会提前重置标志位。\r\n */\r\nexport function withFirstRenderOptimization<T>(fn: () => T): T {\r\n const wasFirstRender = isFirstRenderPass;\r\n isFirstRenderPass = true;\r\n try {\r\n return fn();\r\n } finally {\r\n if (!wasFirstRender) {\r\n isFirstRenderPass = false;\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * 检查当前是否应跳过响应式依赖收集。\r\n * 在 withFirstRenderOptimization 执行期间返回 true。\r\n */\r\nexport function shouldSkipTracking(): boolean {\r\n return isFirstRenderPass;\r\n}\r\n\r\n/**\r\n * 获取被跳过的追踪次数(用于调试和测试)。\r\n */\r\nexport function getSkippedTrackingCount(): number {\r\n return skippedTrackingCount;\r\n}\r\n\r\n/**\r\n * 重置被跳过的追踪计数(用于测试)。\r\n */\r\nexport function resetSkippedTrackingCount(): void {\r\n skippedTrackingCount = 0;\r\n}\r\n\r\n// 只读访问器,防止外部修改内部状态\r\n\r\n/**\r\n * 获取当前活跃的 ReactiveEffect 实例。\r\n * 在 effect 执行期间返回当前正在运行的 effect,否则返回 undefined。\r\n *\r\n * @returns 当前活跃的 effect,如果没有则返回 undefined\r\n */\r\nexport function getActiveEffect(): ReactiveEffect | undefined {\r\n return activeEffect;\r\n}\r\n\r\n/**\r\n * 获取当前是否应该进行依赖追踪。\r\n * 可通过 pauseTracking/enableTracking 控制。\r\n *\r\n * @returns 是否应该进行依赖追踪\r\n */\r\nexport function getShouldTrack(): boolean {\r\n return shouldTrack;\r\n}\r\n\r\n// ==================== Dep ====================\r\n\r\n/**\r\n * 依赖集合类型,存储订阅某个响应式属性的所有 ReactiveEffect。\r\n */\r\nexport type Dep = Set<ReactiveEffect>;\r\n\r\n/**\r\n * 创建一个新的 Dep(依赖集合)。\r\n *\r\n * @returns 新的空 Dep 实例\r\n */\r\nexport const createDep = (): Dep => {\r\n return new Set() as Dep;\r\n};\r\n\r\n// ==================== Track / Trigger ====================\r\n\r\n/**\r\n * Maximum depth for nested trigger() calls to prevent infinite reactivity loops.\r\n * When triggerDepth exceeds this limit, further triggers are silently dropped\r\n * and a warning is emitted in DEV mode.\r\n */\r\nlet triggerDepth = 0;\r\n\r\n/**\r\n * 追踪响应式属性的依赖关系。\r\n * 当响应式属性被读取时调用,将当前活跃的 effect 记录为该属性的依赖。\r\n *\r\n * @param target - 被追踪的响应式对象\r\n * @param _type - 追踪操作类型(如 'get'、'has'、'iterate')\r\n * @param key - 被追踪的属性键\r\n */\r\nexport function track(target: object, _type: string, key: string | symbol) {\r\n if (!shouldTrack || activeEffect === undefined) return;\r\n // signal untrack 桥接:signalUntrack 期间跳过 effect 系统的依赖收集\r\n if (_isSignalUntracked()) return;\r\n\r\n let depsMap = targetMap.get(target);\r\n if (!depsMap) {\r\n targetMap.set(target, (depsMap = new Map()));\r\n }\r\n\r\n let dep = depsMap.get(key);\r\n if (!dep) {\r\n depsMap.set(key, (dep = createDep()));\r\n }\r\n\r\n trackEffect(dep);\r\n\r\n // 调试:触发 onTrack\r\n if (__DEV__ && activeEffect.onTrack) {\r\n activeEffect.onTrack({\r\n effect: activeEffect,\r\n target,\r\n type: _type,\r\n key,\r\n });\r\n }\r\n}\r\n\r\n/**\r\n * 将当前活跃的 effect 添加到指定的依赖集合中。\r\n * 在首次渲染优化期间会跳过依赖收集。\r\n *\r\n * @param dep - 目标依赖集合\r\n */\r\nexport function trackEffect(dep: Dep) {\r\n // 首次渲染优化:跳过依赖收集\r\n if (shouldSkipTracking()) {\r\n skippedTrackingCount++;\r\n return;\r\n }\r\n // FIX: P1-01 移除重复的 shouldTrack/activeEffect 检查,\r\n // 这些检查已在调用方 track() 中完成,此处只需关注 dep 操作\r\n // FIX: P0-5 添加防御性检查,避免非空断言在公共 API 调用时不安全\r\n if (activeEffect && !dep.has(activeEffect)) {\r\n dep.add(activeEffect);\r\n activeEffect.deps.push(dep);\r\n }\r\n}\r\n\r\n/**\r\n * 触发响应式属性的依赖更新。\r\n * 当响应式属性被修改时调用,通知所有依赖该属性的 effect 重新执行。\r\n *\r\n * 根据操作类型(add/delete/set/clear)会触发不同的依赖集合:\r\n * - `add`:触发属性本身 + ITERATE_KEY(或数组 length)\r\n * - `delete`:触发属性本身 + ITERATE_KEY\r\n * - `set`:触发属性本身 + 数组 length(如果是整数键)\r\n * - `clear`:触发所有属性的依赖\r\n *\r\n * @param target - 被修改的响应式对象\r\n * @param type - 触发操作类型('set' | 'add' | 'delete' | 'clear')\r\n * @param key - 被修改的属性键\r\n * @param _newValue - 新值(可选,用于调试)\r\n * @param _oldValue - 旧值(可选,用于调试)\r\n */\r\nexport function trigger(\r\n target: object,\r\n type: string,\r\n key?: string | symbol,\r\n newValue?: unknown,\r\n oldValue?: unknown,\r\n) {\r\n const depsMap = targetMap.get(target);\r\n if (!depsMap) return;\r\n\r\n const deps: (Dep | undefined)[] = [];\r\n\r\n if (type === 'clear') {\r\n deps.push(...depsMap.values());\r\n } else {\r\n if (key !== undefined) {\r\n deps.push(depsMap.get(key));\r\n }\r\n\r\n if (type === 'add') {\r\n if (Array.isArray(target)) {\r\n deps.push(depsMap.get('length'));\r\n } else {\r\n deps.push(depsMap.get(ITERATE_KEY));\r\n }\r\n } else if (type === 'delete') {\r\n if (!Array.isArray(target)) {\r\n deps.push(depsMap.get(ITERATE_KEY));\r\n }\r\n } else if (type === 'set') {\r\n if (Array.isArray(target) && isIntegerKey(key)) {\r\n deps.push(depsMap.get('length'));\r\n }\r\n }\r\n }\r\n\r\n const effects: ReactiveEffect[] = [];\r\n for (const dep of deps) {\r\n if (dep) {\r\n for (const effect of dep) {\r\n effects.push(effect);\r\n }\r\n }\r\n }\r\n\r\n // 去重:同一个 effect 可能同时存在于多个 dep 中\r\n triggerEffects([...new Set(effects)], target, type, key, newValue, oldValue);\r\n}\r\n\r\n/**\r\n * 执行一组 effect 的触发。\r\n * 优先执行 computed effect,再执行普通 effect。\r\n * 内置递归深度限制,超过最大深度时静默丢弃并发出警告。\r\n *\r\n * @param effects - 需要触发的 ReactiveEffect 数组\r\n * @param target - 被修改的目标对象(用于调试回调)\r\n * @param type - 触发操作类型(用于调试回调)\r\n * @param key - 被修改的属性键(用于调试回调)\r\n * @param newValue - 新值(用于调试回调)\r\n * @param oldValue - 旧值(用于调试回调)\r\n */\r\nexport function triggerEffects(\r\n effects: ReactiveEffect[],\r\n target: object,\r\n type: string,\r\n key?: string | symbol,\r\n newValue?: unknown,\r\n oldValue?: unknown,\r\n) {\r\n if (triggerDepth > REACTIVITY_MAX_TRIGGER_DEPTH) {\r\n // FIX: P2-1 triggerDepth 超限时改为 warn + 静默丢弃,与 Vue 3 行为一致。\r\n // 之前直接 throw Error 过于激进,会导致整个响应式链断裂。\r\n // 改为仅发出警告并丢弃后续 trigger,避免因单个无限循环导致整个应用崩溃。\r\n if (__DEV__) {\r\n warn(\r\n `[lytjs/reactivity] Maximum trigger depth (${REACTIVITY_MAX_TRIGGER_DEPTH}) exceeded. ` +\r\n `Possible infinite reactivity loop detected. Further triggers are silently dropped. ` +\r\n `triggerDepth=${triggerDepth}`,\r\n );\r\n }\r\n return;\r\n }\r\n triggerDepth++;\r\n try {\r\n for (const effect of effects) {\r\n if (effect.computed) {\r\n triggerEffect(effect, target, type, key, newValue, oldValue);\r\n }\r\n }\r\n for (const effect of effects) {\r\n if (!effect.computed) {\r\n triggerEffect(effect, target, type, key, newValue, oldValue);\r\n }\r\n }\r\n } finally {\r\n triggerDepth--;\r\n }\r\n}\r\n\r\nfunction triggerEffect(\r\n effect: ReactiveEffect,\r\n target: object,\r\n type: string,\r\n key?: string | symbol,\r\n newValue?: unknown,\r\n oldValue?: unknown,\r\n) {\r\n if (effect !== activeEffect || effect.allowRecurse) {\r\n // 调用 onTrigger 回调(用于调试)\r\n if (__DEV__ && effect.onTrigger) {\r\n effect.onTrigger({\r\n effect,\r\n target,\r\n type,\r\n key,\r\n newValue,\r\n oldValue,\r\n });\r\n }\r\n if (effect.scheduler) {\r\n effect.scheduler();\r\n } else {\r\n effect.run();\r\n }\r\n }\r\n}\r\n\r\n// ==================== ReactiveEffect ====================\r\n\r\n/**\r\n * 响应式副作用类。\r\n * 封装一个副作用函数,支持依赖自动收集、调度执行和手动停止。\r\n *\r\n * 创建时会自动注册到当前活跃的 effectScope 中。\r\n *\r\n * @typeParam T - 副作用函数的返回值类型\r\n *\r\n * @example\r\n * ```ts\r\n * const eff = new ReactiveEffect(() => {\r\n * console.log(state.count);\r\n * });\r\n * eff.run(); // 执行副作用,同时收集依赖\r\n * eff.stop(); // 停止副作用,清理所有依赖\r\n * ```\r\n */\r\nexport class ReactiveEffect<T = unknown> {\r\n active = true;\r\n deps: Dep[] = [];\r\n parent: ReactiveEffect | undefined = undefined;\r\n computed?: boolean;\r\n allowRecurse?: boolean;\r\n onStop?: () => void;\r\n onTrack?: (event: { effect: ReactiveEffect; target: object; key?: string | symbol; type: string }) => void;\r\n onTrigger?: (event: {\r\n effect: ReactiveEffect;\r\n target: object;\r\n key?: string | symbol;\r\n type: string;\r\n newValue?: unknown;\r\n oldValue?: unknown;\r\n }) => void;\r\n onError?: (error: Error) => void;\r\n // 运行前清理(onEffectCleanup 注册的)\r\n _cleanups: Array<() => void> = [];\r\n\r\n constructor(\r\n public fn: () => T,\r\n public scheduler?: (...args: unknown[]) => unknown,\r\n ) {\r\n // 自动注册到当前活跃的 effectScope\r\n const scope = getActiveEffectScope();\r\n if (scope && scope.active) {\r\n scope.effects.push(this);\r\n }\r\n }\r\n\r\n run(): T | undefined {\r\n if (!this.active) {\r\n return undefined;\r\n }\r\n\r\n // 在重新执行前调用 cleanup\r\n if (this._cleanups.length > 0) {\r\n for (let i = 0; i < this._cleanups.length; i++) {\r\n this._cleanups[i]!();\r\n }\r\n this._cleanups.length = 0;\r\n }\r\n\r\n const prevShouldTrack = shouldTrack;\r\n try {\r\n this.parent = activeEffect;\r\n // eslint-disable-next-line @typescript-eslint/no-this-alias\r\n activeEffect = this;\r\n shouldTrack = true;\r\n _trackDepth++;\r\n\r\n return this.fn();\r\n } catch (error) {\r\n if (this.onError) {\r\n this.onError(error as Error);\r\n return undefined;\r\n }\r\n throw error;\r\n } finally {\r\n _trackDepth--;\r\n activeEffect = this.parent;\r\n shouldTrack = prevShouldTrack;\r\n this.parent = undefined;\r\n }\r\n }\r\n\r\n stop(): void {\r\n if (this.active) {\r\n cleanupEffect(this);\r\n if (this._cleanups.length > 0) {\r\n for (let i = 0; i < this._cleanups.length; i++) {\r\n this._cleanups[i]!();\r\n }\r\n this._cleanups.length = 0;\r\n }\r\n if (this.onStop) {\r\n this.onStop();\r\n }\r\n this.active = false;\r\n this.scheduler = undefined;\r\n }\r\n }\r\n}\r\n\r\nfunction cleanupEffect(effect: ReactiveEffect) {\r\n const { deps } = effect;\r\n for (let i = 0; i < deps.length; i++) {\r\n deps[i]!.delete(effect);\r\n }\r\n deps.length = 0;\r\n}\r\n\r\n// ==================== 公共 API ====================\r\n\r\n// Function overloads for effect()\r\n// Non-lazy effect: fn returns void, preventing accidental return value usage\r\n\r\n/**\r\n * 创建一个响应式副作用并立即执行。\r\n *\r\n * 副作用函数会在执行期间自动追踪所使用的响应式属性,\r\n * 当这些属性发生变化时,副作用会重新执行。\r\n *\r\n * @param fn - 副作用函数,返回 void\r\n * @param options - 配置选项\r\n * @param options.lazy - 是否延迟执行(false 时立即执行)\r\n * @param options.scheduler - 自定义调度器,替代默认的立即执行行为\r\n * @param options.allowRecurse - 是否允许副作用递归触发自身\r\n * @param options.onStop - 副作用停止时的回调\r\n * @param options.onTrack - 依赖被追踪时的调试回调\r\n * @param options.onTrigger - 依赖被触发时的调试回调\r\n * @returns 副作用运行器,可调用 run() 手动执行或 stop() 停止\r\n */\r\nexport function effect(\r\n fn: () => void,\r\n options?: {\r\n lazy?: false;\r\n scheduler?: (...args: unknown[]) => unknown;\r\n allowRecurse?: boolean;\r\n onStop?: () => void;\r\n onTrack?: (event: { effect: ReactiveEffect; target: object; key?: string | symbol; type: string }) => void;\r\n onTrigger?: (event: {\r\n effect: ReactiveEffect;\r\n target: object;\r\n key?: string | symbol;\r\n type: string;\r\n newValue?: unknown;\r\n oldValue?: unknown;\r\n }) => void;\r\n },\r\n): ReactiveEffectRunner<void>;\r\n\r\n// Lazy effect: preserves generic return value type (used by computed etc.)\r\nexport function effect<T>(\r\n fn: () => T,\r\n options: {\r\n lazy: true;\r\n scheduler?: (...args: unknown[]) => unknown;\r\n allowRecurse?: boolean;\r\n onStop?: () => void;\r\n onTrack?: (event: { effect: ReactiveEffect; target: object; key?: string | symbol; type: string }) => void;\r\n onTrigger?: (event: {\r\n effect: ReactiveEffect;\r\n target: object;\r\n key?: string | symbol;\r\n type: string;\r\n newValue?: unknown;\r\n oldValue?: unknown;\r\n }) => void;\r\n },\r\n): ReactiveEffectRunner<T>;\r\n\r\n// 统一实现签名\r\nexport function effect<T = unknown>(\r\n fn: () => T,\r\n options?: {\r\n lazy?: boolean;\r\n scheduler?: (...args: unknown[]) => unknown;\r\n allowRecurse?: boolean;\r\n onStop?: () => void;\r\n onTrack?: (event: { effect: ReactiveEffect; target: object; key?: string | symbol; type: string }) => void;\r\n onTrigger?: (event: {\r\n effect: ReactiveEffect;\r\n target: object;\r\n key?: string | symbol;\r\n type: string;\r\n newValue?: unknown;\r\n oldValue?: unknown;\r\n }) => void;\r\n onError?: (error: Error) => void;\r\n },\r\n): ReactiveEffectRunner<T> {\r\n const _effect = new ReactiveEffect(fn);\r\n if (options) {\r\n // 仅提取已知合法选项,防止覆盖内部属性(如 fn、active)\r\n _effect.scheduler = options.scheduler;\r\n _effect.allowRecurse = options.allowRecurse;\r\n _effect.onStop = options.onStop;\r\n _effect.onTrack = options.onTrack;\r\n _effect.onTrigger = options.onTrigger;\r\n _effect.onError = options.onError;\r\n }\r\n if (!options || !options.lazy) {\r\n _effect.run();\r\n }\r\n const runner = _effect.run.bind(_effect) as ReactiveEffectRunner<T>;\r\n runner.effect = _effect;\r\n return runner;\r\n}\r\n\r\n/**\r\n * 停止一个响应式副作用。\r\n * 清理所有依赖关系,并调用 onStop 回调。\r\n *\r\n * @param runner - 由 effect() 返回的副作用运行器\r\n */\r\nexport function stop(runner: ReactiveEffectRunner): void {\r\n runner.effect.stop();\r\n}\r\n\r\n/**\r\n * 暂停依赖追踪。\r\n * 调用后,响应式属性的读取不会建立依赖关系。\r\n * 可通过 resetTracking() 恢复。\r\n */\r\nexport function pauseTracking(): void {\r\n trackStack.push(shouldTrack);\r\n shouldTrack = false;\r\n}\r\n\r\n/**\r\n * 启用依赖追踪。\r\n * 将当前追踪状态压入栈中并设为 true。\r\n * 可通过 resetTracking() 恢复到之前的状态。\r\n */\r\nexport function enableTracking(): void {\r\n trackStack.push(shouldTrack);\r\n shouldTrack = true;\r\n}\r\n\r\n/**\r\n * 重置依赖追踪状态到上一次暂停/启用之前的状态。\r\n * 从追踪栈中弹出最近的状态并恢复。\r\n */\r\nexport function resetTracking(): void {\r\n const last = trackStack.pop();\r\n shouldTrack = last === undefined ? true : last;\r\n}\r\n\r\n/**\r\n * 批量执行函数,期间暂停依赖追踪。\r\n * 与 signalBatch 不同,batch 侧重于暂停追踪而非延迟通知。\r\n * 支持嵌套调用,内层 batch 不会提前恢复追踪状态。\r\n *\r\n * @param fn - 需要批量执行的函数\r\n *\r\n * @example\r\n * ```ts\r\n * batch(() => {\r\n * state.a = 1; // 不会触发依赖更新\r\n * state.b = 2; // 不会触发依赖更新\r\n * }); // 函数结束后恢复追踪\r\n * ```\r\n */\r\nexport function batch(fn: () => void): void {\r\n const stackLength = trackStack.length;\r\n pauseTracking();\r\n try {\r\n fn();\r\n } finally {\r\n // 恢复到 batch 调用前的 stack 长度,确保嵌套安全\r\n while (trackStack.length > stackLength) {\r\n trackStack.pop();\r\n }\r\n shouldTrack = trackStack.length > 0 ? trackStack[trackStack.length - 1]! : true;\r\n }\r\n}\r\n\r\n/**\r\n * batchAsync - like batch but supports async functions.\r\n * Pauses tracking during fn execution (including after await),\r\n * and restores tracking state when fn completes (or throws).\r\n * Returns a Promise.\r\n */\r\nexport function batchAsync(fn: () => void | Promise<void>): Promise<void> {\r\n const stackLength = trackStack.length;\r\n pauseTracking();\r\n const restoreTracking = () => {\r\n while (trackStack.length > stackLength) {\r\n trackStack.pop();\r\n }\r\n shouldTrack = trackStack.length > 0 ? trackStack[trackStack.length - 1]! : true;\r\n };\r\n try {\r\n const result = fn();\r\n if (result && typeof result === 'object' && 'then' in result) {\r\n return (result as Promise<void>).finally(restoreTracking);\r\n }\r\n // 同步路径:立即恢复 tracking 状态\r\n restoreTracking();\r\n return Promise.resolve();\r\n } catch (e) {\r\n restoreTracking();\r\n return Promise.reject(e);\r\n }\r\n}\r\n\r\n/**\r\n * untrack - execute fn without tracking dependencies.\r\n * Semantically different from batch: untrack means \"run but don't track\".\r\n * Returns the return value of fn.\r\n */\r\nexport function untrack<T>(fn: () => T): T {\r\n const stackLength = trackStack.length;\r\n pauseTracking();\r\n try {\r\n return fn();\r\n } finally {\r\n while (trackStack.length > stackLength) {\r\n trackStack.pop();\r\n }\r\n shouldTrack = trackStack.length > 0 ? trackStack[trackStack.length - 1]! : true;\r\n }\r\n}\r\n\r\n/**\r\n * 在当前活跃的 effect 上注册一个清理回调。\r\n * 该回调会在 effect 重新执行前或停止时被调用,用于清理副作用资源。\r\n *\r\n * @param fn - 清理回调函数\r\n * @param failSilently - 当没有活跃 effect 时是否静默失败(默认 false,开发模式下会发出警告)\r\n *\r\n * @example\r\n * ```ts\r\n * effect(() => {\r\n * const timer = setInterval(() => console.log('tick'), 1000);\r\n * onEffectCleanup(() => clearInterval(timer));\r\n * });\r\n * ```\r\n */\r\nexport function onEffectCleanup(fn: () => void, failSilently = false): void {\r\n if (activeEffect === undefined) {\r\n if (!failSilently && __DEV__) {\r\n warn('onEffectCleanup() was called when there was no active effect to associate with.');\r\n }\r\n return;\r\n }\r\n activeEffect._cleanups.push(fn);\r\n}\r\n\r\n// ==================== 辅助 ====================\r\n\r\n/**\r\n * 检查给定的键是否为有效的整数键。\r\n * 用于判断数组索引是否为合法的整数值。\r\n *\r\n * @param key - 需要检查的键值\r\n * @returns 如果是有效的整数键返回 true,否则返回 false\r\n */\r\nexport function isIntegerKey(key: unknown): boolean {\r\n return (\r\n typeof key === 'string' &&\r\n key !== 'NaN' &&\r\n key[0] !== '-' &&\r\n '' + parseInt(key, 10) === key &&\r\n Number.isSafeInteger(Number(key))\r\n );\r\n}\r\n","/**\r\n * @lytjs/reactivity - Signal\r\n * 独立自包含的 Signal 响应式原语。\r\n * 拥有独立的订阅/通知机制,同时桥接 effect 系统保持互操作性。\r\n */\r\n\r\nimport { SignalSymbol, ComputedSignalSymbol, TrackOpTypes, TriggerOpTypes } from './constants';\r\nimport { track, trigger } from './effect';\r\nimport { REACTIVITY_MAX_TRIGGER_DEPTH } from '@lytjs/common-constants';\r\nimport type { Subscriber } from './shared/types';\r\n\r\n// ============================================================\r\n// 类型定义\r\n// ============================================================\r\n\r\n/** 订阅者回调 */\r\nexport type { Subscriber };\r\n\r\n/** Signal 只读接口 */\r\nexport interface Signal<T = unknown> {\r\n /** 读取当前值 */\r\n (): T;\r\n readonly [SignalSymbol]: true;\r\n}\r\n\r\n/** WritableSignal 可写接口 */\r\nexport interface WritableSignal<T = unknown> extends Signal<T> {\r\n /** 设置新值 */\r\n set(newValue: T): void;\r\n /** 通过 updater 函数更新值 */\r\n update(updater: (prev: T) => T): void;\r\n /** 停止所有订阅通知,释放资源 */\r\n dispose(): void;\r\n /** @internal 订阅变更通知 */\r\n _subscribe(subscriber: Subscriber): () => void;\r\n /** FIX: P1-5 REACTIVITY-NEW-03 - 手动清理依赖,防止内存泄漏 */\r\n cleanup(): void;\r\n}\r\n\r\n/** ComputedSignal 计算信号接口 */\r\nexport interface ComputedSignal<T = unknown> extends Signal<T> {\r\n /** 停止计算信号的依赖追踪和更新 */\r\n dispose(): void;\r\n /** @deprecated 使用 dispose() */\r\n stop?: () => void;\r\n readonly [ComputedSignalSymbol]: true;\r\n}\r\n\r\n/** WritableComputedSignal 可写计算信号接口 */\r\nexport interface WritableComputedSignal<T = unknown> extends ComputedSignal<T> {\r\n /** 设置新值(通过 setter 函数) */\r\n set(newValue: T): void;\r\n}\r\n\r\n/** ReadonlySignal 只读信号接口 */\r\nexport interface ReadonlySignal<T = unknown> {\r\n /** 读取当前值 */\r\n (): T;\r\n readonly [SignalSymbol]: true;\r\n}\r\n\r\n// ============================================================\r\n// 全局追踪状态\r\n// ============================================================\r\n\r\n/** 当前活跃的订阅者(computed 读取 signal 时自动追踪) */\r\nlet activeSubscriber: Subscriber | null = null;\r\n\r\n/** 是否处于 untrack 模式 */\r\nlet isUntracked = false;\r\n\r\n/** 依赖追踪回调:computed 使用此回调记录 signal 依赖关系 */\r\nlet trackDependency: ((signal: WritableSignal<unknown>, unsubscribe: () => void) => void) | null =\r\n null;\r\n\r\n/** 当前 batch 嵌套深度 */\r\nlet batchDepth = 0;\r\n\r\n/** FIX: P2-05 batch 嵌套深度限制,防止无限递归 */\r\nconst MAX_BATCH_DEPTH = 100;\r\n\r\n/** batch 期间待通知的订阅者 */\r\nconst pendingNotifications = new Set<Subscriber>();\r\n\r\n/** batch 期间待执行的 effect 系统 trigger 操作(自动去重) */\r\n// FIX: P1-05 使用 Map<symbol,...> 替代 Map<string,...>,\r\n// 避免不同 signal 的 Symbol().toString() 产生相同的字符串 key 导致去重错误\r\n// FIX: P2-7 去重时保留最新 newValue:当同一个 signalKey 多次 set 时,\r\n// Map.set 会覆盖旧值,确保最终 trigger 使用最新的 newValue,\r\n// 避免因去重导致订阅者收到过期的旧值。\r\nconst pendingTriggerOps = new Map<\r\n symbol,\r\n { store: Record<symbol, unknown>; signalKey: symbol; newValue?: unknown }\r\n>();\r\n\r\n/** 是否正在执行通知 */\r\nlet isNotifying = false;\r\n\r\n// ============================================================\r\n// signal — 核心 Signal 原语\r\n// ============================================================\r\n\r\n/**\r\n * 创建一个可写 Signal。\r\n * 使用闭包变量存储值,Set 管理订阅者。\r\n */\r\nexport function signal<T>(initialValue: T): WritableSignal<T> {\r\n let value = initialValue;\r\n const subscribers = new Set<Subscriber>();\r\n let disposed = false;\r\n\r\n // effect 系统桥接:使用 store 对象作为 track/trigger 的 target\r\n const store: Record<symbol, T> = {};\r\n const SIGNAL_KEY = Symbol('signal_value');\r\n store[SIGNAL_KEY] = initialValue;\r\n\r\n const signalFn = function signalFn(): T {\r\n // Signal 内部追踪\r\n // FIX: P0-01 闭包捕获过期 activeSubscriber — 立即捕获当前订阅者引用,\r\n // 避免闭包中的 activeSubscriber 在异步回调中被修改后指向错误的订阅者\r\n const currentSubscriber = activeSubscriber;\r\n if (currentSubscriber && !isUntracked && !disposed) {\r\n if (!subscribers.has(currentSubscriber)) {\r\n subscribers.add(currentSubscriber);\r\n if (trackDependency) {\r\n trackDependency(signalFn as WritableSignal<unknown>, () => {\r\n subscribers.delete(currentSubscriber);\r\n });\r\n }\r\n }\r\n }\r\n // effect 系统桥接追踪\r\n if (!disposed) {\r\n track(store, TrackOpTypes.GET, SIGNAL_KEY);\r\n }\r\n return value;\r\n } as WritableSignal<T>;\r\n\r\n Object.defineProperty(signalFn, SignalSymbol, { value: true });\r\n\r\n signalFn.set = (newValue: T): void => {\r\n if (disposed) return;\r\n if (Object.is(newValue, value)) return;\r\n value = newValue;\r\n store[SIGNAL_KEY] = newValue;\r\n notifySubscribers(subscribers, store, SIGNAL_KEY, newValue);\r\n };\r\n\r\n signalFn.update = (updater: (prev: T) => T): void => {\r\n if (disposed) return;\r\n const newValue = updater(value);\r\n if (Object.is(newValue, value)) return;\r\n value = newValue;\r\n store[SIGNAL_KEY] = newValue;\r\n notifySubscribers(subscribers, store, SIGNAL_KEY, newValue);\r\n };\r\n\r\n signalFn.dispose = (): void => {\r\n disposed = true;\r\n subscribers.clear();\r\n };\r\n\r\n // FIX: P1-5 REACTIVITY-NEW-03 - 添加 cleanup 方法,允许手动清理依赖\r\n signalFn.cleanup = (): void => {\r\n // 清理所有订阅者,但保持 signal 可用\r\n subscribers.clear();\r\n // 清理 effect 系统桥接的依赖:直接触发 SET 通知使依赖失效,\r\n // 不传旧值以避免读取 signal 值\r\n trigger(store, TriggerOpTypes.SET, SIGNAL_KEY);\r\n };\r\n\r\n signalFn._subscribe = (subscriber: Subscriber): (() => void) => {\r\n if (disposed) return () => {};\r\n subscribers.add(subscriber);\r\n return () => subscribers.delete(subscriber);\r\n };\r\n\r\n return signalFn;\r\n}\r\n\r\n// ============================================================\r\n// computed — 独立计算信号\r\n// ============================================================\r\n\r\n/**\r\n * 内部工厂函数:创建计算信号的核心逻辑。\r\n * computed 和 writableComputedSignal 共享此实现,消除约 80 行重复代码。\r\n */\r\nfunction createComputedSignalInternal<T>(\r\n getter: () => T,\r\n typeName: string,\r\n): {\r\n computedFn: ComputedSignal<T>;\r\n invalidate: () => void;\r\n dispose: () => void;\r\n} {\r\n let value: T | undefined;\r\n let dirty = true;\r\n let isComputing = false;\r\n const dependencies = new Map<WritableSignal<unknown>, () => void>();\r\n const subscribers = new Set<Subscriber>();\r\n let disposed = false;\r\n\r\n // effect 系统桥接:使用 store 对象作为 track/trigger 的 target\r\n const store: Record<symbol, unknown> = {};\r\n const COMPUTED_SIGNAL_KEY = Symbol('computed_signal_value');\r\n\r\n const invalidate = (): void => {\r\n if (disposed) return;\r\n dirty = true;\r\n // effect 系统桥接触发\r\n trigger(store, TriggerOpTypes.SET, COMPUTED_SIGNAL_KEY);\r\n const subs = Array.from(subscribers);\r\n for (const sub of subs) {\r\n sub();\r\n }\r\n };\r\n\r\n const computedFn = function computedFn(): T {\r\n if (disposed) return value as T;\r\n\r\n // effect 系统桥接追踪\r\n track(store, TrackOpTypes.GET, COMPUTED_SIGNAL_KEY);\r\n\r\n // 追踪:如果有活跃订阅者,注册自身\r\n if (activeSubscriber && !isUntracked) {\r\n subscribers.add(activeSubscriber);\r\n }\r\n\r\n if (dirty) {\r\n if (isComputing) {\r\n throw new Error(`[lytjs/signal] Circular dependency detected in ${typeName}.`);\r\n }\r\n isComputing = true;\r\n try {\r\n // 清理旧依赖(调用 unsubscribe 函数)\r\n for (const unsubscribe of dependencies.values()) {\r\n unsubscribe();\r\n }\r\n dependencies.clear();\r\n\r\n // 在活跃订阅者上下文中执行 getter,自动追踪新依赖\r\n const prevSubscriber = activeSubscriber;\r\n const prevTrackDependency = trackDependency;\r\n activeSubscriber = invalidate; // 注册 invalidate 作为依赖的订阅者\r\n trackDependency = (dep: WritableSignal<unknown>, unsubscribe: () => void) => {\r\n dependencies.set(dep, unsubscribe);\r\n };\r\n try {\r\n value = getter();\r\n dirty = false;\r\n } finally {\r\n activeSubscriber = prevSubscriber;\r\n trackDependency = prevTrackDependency;\r\n }\r\n } finally {\r\n isComputing = false;\r\n }\r\n }\r\n\r\n return value as T;\r\n } as ComputedSignal<T>;\r\n\r\n Object.defineProperty(computedFn, ComputedSignalSymbol, { value: true });\r\n\r\n const dispose = (): void => {\r\n disposed = true;\r\n for (const unsubscribe of dependencies.values()) {\r\n unsubscribe();\r\n }\r\n dependencies.clear();\r\n subscribers.clear();\r\n };\r\n\r\n return { computedFn, invalidate, dispose };\r\n}\r\n\r\n/**\r\n * 创建一个计算信号。\r\n * 惰性求值、自动依赖追踪与清理、循环依赖检测。\r\n */\r\nexport function computed<T>(fn: () => T): ComputedSignal<T> {\r\n const { computedFn, dispose } = createComputedSignalInternal(fn, 'computed signal');\r\n\r\n computedFn.dispose = dispose;\r\n computedFn.stop = dispose;\r\n\r\n return computedFn;\r\n}\r\n\r\n// ============================================================\r\n// writableComputed — 可写计算信号\r\n// ============================================================\r\n\r\n/**\r\n * 创建一个可写计算信号。\r\n * 通过 getter 读取计算值,通过 setter 写入值。\r\n * setter 通常会间接更新 getter 依赖的底层 signal。\r\n */\r\nexport function writableComputedSignal<T>(\r\n getter: () => T,\r\n setter: (value: T) => void,\r\n): WritableComputedSignal<T> {\r\n const { computedFn, dispose } = createComputedSignalInternal(getter, 'writable computed signal');\r\n\r\n (computedFn as WritableComputedSignal<T>).set = (newValue: T): void => {\r\n setter(newValue);\r\n };\r\n\r\n computedFn.dispose = dispose;\r\n computedFn.stop = dispose;\r\n\r\n return computedFn as WritableComputedSignal<T>;\r\n}\r\n\r\n// ============================================================\r\n// batch — 批量更新\r\n// ============================================================\r\n\r\n/**\r\n * 在批处理中执行函数。\r\n * batch 内多次 signal.set 只在函数结束后统一触发一次通知。\r\n */\r\nexport function signalBatch(fn: () => void): void {\r\n // FIX: P2-05 batch 嵌套深度限制\r\n if (batchDepth >= MAX_BATCH_DEPTH) {\r\n if (__DEV__) {\r\n console.warn(\r\n `[lytjs/signal] signalBatch() nesting depth exceeded ${MAX_BATCH_DEPTH}. ` +\r\n `This may indicate an infinite loop. The batch call will be executed synchronously.`,\r\n );\r\n }\r\n fn();\r\n return;\r\n }\r\n batchDepth++;\r\n try {\r\n fn();\r\n } finally {\r\n batchDepth--;\r\n if (batchDepth === 0) {\r\n flushPendingNotifications();\r\n }\r\n }\r\n}\r\n\r\n// ============================================================\r\n// untrack — 取消追踪\r\n// ============================================================\r\n\r\n/**\r\n * 在取消追踪模式中执行函数。\r\n * 函数内读取 signal 不会建立依赖关系。\r\n */\r\nexport function signalUntrack<T>(fn: () => T): T {\r\n const prevIsUntracked = isUntracked;\r\n isUntracked = true;\r\n try {\r\n return fn();\r\n } finally {\r\n isUntracked = prevIsUntracked;\r\n }\r\n}\r\n\r\n/** @internal 检查当前是否处于 untrack 模式(供 effect 系统桥接使用) */\r\nexport function _isSignalUntracked(): boolean {\r\n return isUntracked;\r\n}\r\n\r\n// ============================================================\r\n// 内部通知机制\r\n// ============================================================\r\n\r\nfunction notifySubscribers(\r\n subscribers: Set<Subscriber>,\r\n store?: Record<symbol, unknown>,\r\n signalKey?: symbol,\r\n newValue?: unknown,\r\n): void {\r\n // FIX: P0-02 当 isNotifying 为 true 时也走 batch 路径,\r\n // 避免在 flushPendingNotifications 执行期间嵌套触发同步通知导致无限递归\r\n if (batchDepth > 0 || isNotifying) {\r\n const it = subscribers.values();\r\n let next = it.next();\r\n while (!next.done) {\r\n pendingNotifications.add(next.value);\r\n next = it.next();\r\n }\r\n // effect 系统桥接:batch 期间也延迟 trigger(去重)\r\n if (store && signalKey !== undefined) {\r\n // FIX: P1-05 直接使用 symbol 作为 key,避免 String() 转换导致的 key 冲突\r\n pendingTriggerOps.set(signalKey, { store, signalKey, newValue });\r\n }\r\n return;\r\n }\r\n const it = subscribers.values();\r\n let next = it.next();\r\n while (!next.done) {\r\n next.value();\r\n next = it.next();\r\n }\r\n // effect 系统桥接触发\r\n if (store && signalKey !== undefined) {\r\n trigger(store, TriggerOpTypes.SET, signalKey, newValue);\r\n }\r\n}\r\n\r\nfunction flushPendingNotifications(): void {\r\n if (isNotifying) return;\r\n isNotifying = true;\r\n try {\r\n let iterations = 0;\r\n while ((pendingNotifications.size > 0 || pendingTriggerOps.size > 0) && iterations < REACTIVITY_MAX_TRIGGER_DEPTH) {\r\n const notifications = new Set(pendingNotifications);\r\n const triggers = new Set(pendingTriggerOps.values());\r\n pendingNotifications.clear();\r\n pendingTriggerOps.clear();\r\n \r\n const notifIt = notifications.values();\r\n let notifNext = notifIt.next();\r\n while (!notifNext.done) {\r\n notifNext.value();\r\n notifNext = notifIt.next();\r\n }\r\n \r\n // 执行延迟的 effect 系统 trigger(已去重)\r\n const triggerIt = triggers.values();\r\n let triggerNext = triggerIt.next();\r\n while (!triggerNext.done) {\r\n const { store, signalKey, newValue } = triggerNext.value;\r\n trigger(store, TriggerOpTypes.SET, signalKey, newValue);\r\n triggerNext = triggerIt.next();\r\n }\r\n \r\n iterations++;\r\n }\r\n } finally {\r\n isNotifying = false;\r\n }\r\n}\r\n\r\n// ============================================================\r\n// 适配器层 — 旧 API 兼容\r\n// ============================================================\r\n\r\n/**\r\n * @deprecated 使用 computed() 代替\r\n */\r\nexport function computedSignal<T>(fn: () => T): ComputedSignal<T> {\r\n return computed(fn);\r\n}\r\n\r\n/** 读取 signal 值 */\r\nexport function valueOf<T>(sig: Signal<T>): T {\r\n return sig();\r\n}\r\n\r\n/** 设置 signal 值(适配器) */\r\nexport function set<T>(sig: WritableSignal<T> | ReadonlySignal<T>, newValue: T): void {\r\n // FIX: P2-35 使用类型守卫检查是否为 WritableSignal\r\n if (isWritableSignal(sig)) {\r\n sig.set(newValue);\r\n }\r\n // ReadonlySignal: 静默忽略(保持旧行为)\r\n}\r\n\r\n/** 类型守卫:检查信号是否为可写信号 */\r\nfunction isWritableSignal<T>(sig: WritableSignal<T> | ReadonlySignal<T>): sig is WritableSignal<T> {\r\n return typeof sig === 'function' && 'set' in sig;\r\n}\r\n\r\n/** 通过 updater 更新 signal 值(适配器) */\r\nexport function update<T>(sig: WritableSignal<T>, updater: (prev: T) => T): void {\r\n sig.update(updater);\r\n}\r\n\r\n/** 创建只读 signal(适配器) */\r\nexport function readonlySignal<T>(sig: Signal<T>): ReadonlySignal<T> {\r\n const readonlyFn = function readonlyFn(): T {\r\n return sig();\r\n } as ReadonlySignal<T>;\r\n Object.defineProperty(readonlyFn, SignalSymbol, { value: true });\r\n return readonlyFn;\r\n}\r\n\r\n// ============================================================\r\n// 调试/测试 API\r\n// ============================================================\r\n\r\n/** @internal 获取当前活跃订阅者(仅用于测试) */\r\nexport function _getActiveSubscriber(): Subscriber | null {\r\n return activeSubscriber;\r\n}\r\n\r\n/** @internal 获取 batch 深度(仅用于测试) */\r\nexport function _getBatchDepth(): number {\r\n return batchDepth;\r\n}\r\n\r\n/** @internal 获取待通知订阅者数量(仅用于测试) */\r\nexport function _getPendingNotificationsCount(): number {\r\n return pendingNotifications.size;\r\n}\r\n\r\n/** @internal 重置全局状态(仅用于测试) */\r\nexport function _resetSignalGlobalState(): void {\r\n activeSubscriber = null;\r\n isUntracked = false;\r\n batchDepth = 0;\r\n pendingNotifications.clear();\r\n isNotifying = false;\r\n // FIX: P1-4 REACTIVITY-NEW-05 - 遗漏 pendingTriggerOps 清理\r\n // 确保测试间状态完全隔离,避免 pendingTriggerOps 泄漏导致测试不稳定\r\n pendingTriggerOps.clear();\r\n}\r\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lytjs/reactivity",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.5.0",
|
|
4
4
|
"description": "LytJS reactive system - signals, refs, computed, watch, and effects",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -48,12 +48,12 @@
|
|
|
48
48
|
"clean": "rm -rf dist"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@lytjs/shared-types": "^6.
|
|
52
|
-
"@lytjs/common-is": "^6.
|
|
53
|
-
"@lytjs/common-scheduler": "^6.
|
|
54
|
-
"@lytjs/common-error": "^6.
|
|
55
|
-
"@lytjs/common-constants": "^6.
|
|
56
|
-
"@lytjs/common-assertions": "^6.
|
|
51
|
+
"@lytjs/shared-types": "^6.5.0",
|
|
52
|
+
"@lytjs/common-is": "^6.5.0",
|
|
53
|
+
"@lytjs/common-scheduler": "^6.5.0",
|
|
54
|
+
"@lytjs/common-error": "^6.5.0",
|
|
55
|
+
"@lytjs/common-constants": "^6.5.0",
|
|
56
|
+
"@lytjs/common-assertions": "^6.5.0"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
59
|
"tsup": "^8.0.0",
|