sibujs 1.0.2 → 1.0.3
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/browser.cjs +18 -0
- package/dist/browser.d.cts +19 -1
- package/dist/browser.d.ts +19 -1
- package/dist/browser.js +5 -1
- package/dist/build.cjs +17 -8
- package/dist/build.js +3 -3
- package/dist/cdn.global.js +4 -4
- package/dist/{chunk-ONCYDOK6.js → chunk-4EI4AG32.js} +17 -1
- package/dist/{chunk-MJ4LVHGX.js → chunk-7V26P53V.js} +1 -1
- package/dist/{chunk-6JGMNCD6.js → chunk-EWFVA3TJ.js} +1 -1
- package/dist/{chunk-HS6OOE3Q.js → chunk-G3BOQPVO.js} +1 -1
- package/dist/{chunk-QVKGGB2S.js → chunk-MDVXJWFN.js} +7 -3
- package/dist/{chunk-24WSRM54.js → chunk-MEZVEBPN.js} +13 -7
- package/dist/{chunk-OHQQWZ7P.js → chunk-QBMDLBU2.js} +10 -4
- package/dist/data.cjs +10 -4
- package/dist/data.d.cts +2 -0
- package/dist/data.d.ts +2 -0
- package/dist/data.js +1 -1
- package/dist/ecosystem.cjs +7 -3
- package/dist/ecosystem.d.cts +1 -1
- package/dist/ecosystem.d.ts +1 -1
- package/dist/ecosystem.js +2 -2
- package/dist/extras.cjs +35 -7
- package/dist/extras.d.cts +2 -2
- package/dist/extras.d.ts +2 -2
- package/dist/extras.js +10 -6
- package/dist/index.cjs +18 -8
- package/dist/index.d.cts +22 -3
- package/dist/index.d.ts +22 -3
- package/dist/index.js +5 -3
- package/dist/plugins.cjs +7 -3
- package/dist/plugins.js +2 -2
- package/dist/ssr.cjs +7 -3
- package/dist/ssr.js +3 -3
- package/dist/{tagFactory-D9e0QGIE.d.cts → tagFactory-Dl8QCLga.d.cts} +2 -2
- package/dist/{tagFactory-D9e0QGIE.d.ts → tagFactory-Dl8QCLga.d.ts} +2 -2
- package/package.json +1 -1
- package/dist/chunk-27QC4FPL.js +0 -67
- package/dist/chunk-2ABBWCGC.js +0 -65
- package/dist/chunk-2GLUWW3D.js +0 -567
- package/dist/chunk-2HYHL6VW.js +0 -269
- package/dist/chunk-2MUNQYZ7.js +0 -26
- package/dist/chunk-2PFRWZIE.js +0 -65
- package/dist/chunk-2PSPKNUI.js +0 -1711
- package/dist/chunk-2X5NDXNG.js +0 -877
- package/dist/chunk-34TOFKL7.js +0 -1803
- package/dist/chunk-353KB3DI.js +0 -271
- package/dist/chunk-35CDLDX5.js +0 -1758
- package/dist/chunk-36MU4CFV.js +0 -41
- package/dist/chunk-37NSPDPQ.js +0 -284
- package/dist/chunk-3CSF4TOG.js +0 -282
- package/dist/chunk-3FIQOFI6.js +0 -182
- package/dist/chunk-3GHNC2BN.js +0 -28
- package/dist/chunk-3GZTRYSY.js +0 -33
- package/dist/chunk-3HLWWEPU.js +0 -909
- package/dist/chunk-3IVI3J54.js +0 -252
- package/dist/chunk-3KZ72WNW.js +0 -944
- package/dist/chunk-4AU64SQV.js +0 -182
- package/dist/chunk-4MOK7HAR.js +0 -84
- package/dist/chunk-4QK6FBDH.js +0 -1429
- package/dist/chunk-526ENZ77.js +0 -58
- package/dist/chunk-566Z7HXB.js +0 -737
- package/dist/chunk-5CRBB7XP.js +0 -358
- package/dist/chunk-5G67D3IZ.js +0 -168
- package/dist/chunk-5KBEED6D.js +0 -361
- package/dist/chunk-5LJMSKBB.js +0 -26
- package/dist/chunk-5M7LJFWV.js +0 -271
- package/dist/chunk-5NCPAWBE.js +0 -99
- package/dist/chunk-5O2RKXR3.js +0 -1444
- package/dist/chunk-5OCBERWW.js +0 -1094
- package/dist/chunk-5XUAA53E.js +0 -99
- package/dist/chunk-5ZFVYPDG.js +0 -97
- package/dist/chunk-6BCDNGIH.js +0 -1839
- package/dist/chunk-6BDUQJ7K.js +0 -949
- package/dist/chunk-6BTBDO6A.js +0 -633
- package/dist/chunk-6DJQF4Z7.js +0 -33
- package/dist/chunk-6EYXAMBN.js +0 -1857
- package/dist/chunk-6IWEHW57.js +0 -43
- package/dist/chunk-6J6S4NI7.js +0 -398
- package/dist/chunk-6NBHAKHS.js +0 -1997
- package/dist/chunk-6QRLJNXR.js +0 -1425
- package/dist/chunk-6SNF5MZK.js +0 -282
- package/dist/chunk-7FYJUE77.js +0 -282
- package/dist/chunk-7HM5UE5T.js +0 -270
- package/dist/chunk-7JOLTGUH.js +0 -58
- package/dist/chunk-7LS3ITZB.js +0 -567
- package/dist/chunk-7MCWJCQK.js +0 -909
- package/dist/chunk-7RIIFP3E.js +0 -1758
- package/dist/chunk-7UASYN3G.js +0 -254
- package/dist/chunk-7W2WYHDI.js +0 -741
- package/dist/chunk-7XFBKLFB.js +0 -90
- package/dist/chunk-7Y35RDSJ.js +0 -872
- package/dist/chunk-A65GFJBL.js +0 -65
- package/dist/chunk-AD6ZIEDK.js +0 -67
- package/dist/chunk-AIPXZK5O.js +0 -295
- package/dist/chunk-AK5Y72F3.js +0 -1426
- package/dist/chunk-ANRSGGFC.js +0 -725
- package/dist/chunk-APOMMWH4.js +0 -282
- package/dist/chunk-ARZVTWIQ.js +0 -1750
- package/dist/chunk-AWWBM2BI.js +0 -664
- package/dist/chunk-AX5VEQTY.js +0 -58
- package/dist/chunk-AYTXVOW3.js +0 -1708
- package/dist/chunk-B4ASD5VC.js +0 -295
- package/dist/chunk-BG3HMX4C.js +0 -271
- package/dist/chunk-BG4A246G.js +0 -1746
- package/dist/chunk-BNFJJA2L.js +0 -1425
- package/dist/chunk-BPKPBVU5.js +0 -59
- package/dist/chunk-BPKPPSXC.js +0 -282
- package/dist/chunk-BPWKKK7F.js +0 -1711
- package/dist/chunk-BS5EYKCJ.js +0 -26
- package/dist/chunk-BS6ETCJ7.js +0 -949
- package/dist/chunk-BTTBAGD3.js +0 -365
- package/dist/chunk-BWBWJYBK.js +0 -271
- package/dist/chunk-CCKX6YTC.js +0 -1735
- package/dist/chunk-CIF5Z3MP.js +0 -58
- package/dist/chunk-CQNVF2FD.js +0 -282
- package/dist/chunk-CRTHZHU7.js +0 -26
- package/dist/chunk-CSXYU7IO.js +0 -457
- package/dist/chunk-D6JD4FDC.js +0 -26
- package/dist/chunk-DAUTHAYH.js +0 -20
- package/dist/chunk-DB243LB7.js +0 -877
- package/dist/chunk-DEQLPROY.js +0 -1094
- package/dist/chunk-DGUYCZJH.js +0 -654
- package/dist/chunk-DI3Z32TV.js +0 -1780
- package/dist/chunk-DKGHW6MX.js +0 -90
- package/dist/chunk-DKXVN442.js +0 -90
- package/dist/chunk-DLWLJZB3.js +0 -712
- package/dist/chunk-DNTTC4V5.js +0 -1803
- package/dist/chunk-DWQQ2GZK.js +0 -1787
- package/dist/chunk-DZZMIMGL.js +0 -725
- package/dist/chunk-E5SMAKQQ.js +0 -336
- package/dist/chunk-E6BNBVMK.js +0 -457
- package/dist/chunk-E7NGA7X2.js +0 -59
- package/dist/chunk-EEPPJKAE.js +0 -443
- package/dist/chunk-EGZGFIXV.js +0 -90
- package/dist/chunk-EJMSUPXQ.js +0 -346
- package/dist/chunk-EJMYGAGQ.js +0 -717
- package/dist/chunk-EL6Z5MDY.js +0 -55
- package/dist/chunk-EP7VRLEB.js +0 -41
- package/dist/chunk-EQACK65C.js +0 -63
- package/dist/chunk-ESPA7SCH.js +0 -567
- package/dist/chunk-ETMEC6FH.js +0 -99
- package/dist/chunk-EUFENPGJ.js +0 -290
- package/dist/chunk-EZ2WHYVL.js +0 -65
- package/dist/chunk-EZRVMSZK.js +0 -67
- package/dist/chunk-F25CZRUH.js +0 -282
- package/dist/chunk-F2TRGINX.js +0 -254
- package/dist/chunk-F4IYF3YU.js +0 -266
- package/dist/chunk-F5JCIH3Q.js +0 -642
- package/dist/chunk-FBNDOAHU.js +0 -54
- package/dist/chunk-FDRU7W6W.js +0 -365
- package/dist/chunk-FDW3E54J.js +0 -1767
- package/dist/chunk-FEPU4KL6.js +0 -63
- package/dist/chunk-FGK3JKMN.js +0 -909
- package/dist/chunk-FQWPKSTD.js +0 -1437
- package/dist/chunk-FWHVLMCI.js +0 -26
- package/dist/chunk-FYIGTFXO.js +0 -712
- package/dist/chunk-G4AX3DQ4.js +0 -54
- package/dist/chunk-GBEYQRO2.js +0 -303
- package/dist/chunk-GBLES3NK.js +0 -248
- package/dist/chunk-GQVGUQW6.js +0 -1436
- package/dist/chunk-GVDEIJ3G.js +0 -291
- package/dist/chunk-GXIM4TC3.js +0 -346
- package/dist/chunk-GY5AAP4H.js +0 -316
- package/dist/chunk-HCV2T76T.js +0 -457
- package/dist/chunk-HCWPQCZP.js +0 -279
- package/dist/chunk-HFHOFG6W.js +0 -282
- package/dist/chunk-HGKEBAW3.js +0 -63
- package/dist/chunk-HJNW3HLI.js +0 -255
- package/dist/chunk-HKUNBYN6.js +0 -949
- package/dist/chunk-HRIIH4JX.js +0 -712
- package/dist/chunk-HS7ZKVPR.js +0 -182
- package/dist/chunk-HXDVV7HZ.js +0 -909
- package/dist/chunk-HZWNWUPJ.js +0 -466
- package/dist/chunk-I6EZTMPB.js +0 -949
- package/dist/chunk-IB23VMO3.js +0 -1746
- package/dist/chunk-IC4K4EOD.js +0 -26
- package/dist/chunk-IEMZ7RTT.js +0 -99
- package/dist/chunk-IJU6TOZ6.js +0 -65
- package/dist/chunk-IK3NFZOA.js +0 -466
- package/dist/chunk-IPGRSN42.js +0 -1750
- package/dist/chunk-IVMOK2QN.js +0 -1750
- package/dist/chunk-J7PD6L4B.js +0 -1767
- package/dist/chunk-JCLGQO7T.js +0 -443
- package/dist/chunk-JDXL7KDB.js +0 -1436
- package/dist/chunk-JIIFW636.js +0 -270
- package/dist/chunk-JLOASJXU.js +0 -654
- package/dist/chunk-JRWAHOMS.js +0 -41
- package/dist/chunk-JWGEEH7H.js +0 -944
- package/dist/chunk-K2BESAG7.js +0 -1688
- package/dist/chunk-K2U5YGF4.js +0 -877
- package/dist/chunk-K45FQ4Y4.js +0 -175
- package/dist/chunk-K7BPE427.js +0 -1432
- package/dist/chunk-K7JUDY3C.js +0 -364
- package/dist/chunk-KALKXPC3.js +0 -297
- package/dist/chunk-KKUV25KB.js +0 -361
- package/dist/chunk-KKV3DOAW.js +0 -466
- package/dist/chunk-KL3266RS.js +0 -26
- package/dist/chunk-KNN4P7DZ.js +0 -84
- package/dist/chunk-KP2DZH5Q.js +0 -254
- package/dist/chunk-KZHAJSQR.js +0 -1636
- package/dist/chunk-L36YN45V.js +0 -949
- package/dist/chunk-L5IBT37O.js +0 -263
- package/dist/chunk-LAX5H35R.js +0 -90
- package/dist/chunk-LBKGHMQV.js +0 -1750
- package/dist/chunk-LBTEPL7A.js +0 -1731
- package/dist/chunk-LEBBPTDB.js +0 -1444
- package/dist/chunk-LHI25KS2.js +0 -654
- package/dist/chunk-LHNJSLC6.js +0 -297
- package/dist/chunk-LLH63WVQ.js +0 -98
- package/dist/chunk-LWNJN3IW.js +0 -295
- package/dist/chunk-LWVR2C4G.js +0 -1711
- package/dist/chunk-M3MDTVV2.js +0 -896
- package/dist/chunk-M3QSBCXC.js +0 -52
- package/dist/chunk-M3ZKZQNY.js +0 -282
- package/dist/chunk-M5GNLDEO.js +0 -303
- package/dist/chunk-MBINISSE.js +0 -35
- package/dist/chunk-MFHVGKET.js +0 -267
- package/dist/chunk-MGWSG3PM.js +0 -358
- package/dist/chunk-MGYRVG2A.js +0 -282
- package/dist/chunk-MJNB47HB.js +0 -19
- package/dist/chunk-MLN3JZ2E.js +0 -398
- package/dist/chunk-MM46JVR2.js +0 -359
- package/dist/chunk-MQWTY3JY.js +0 -944
- package/dist/chunk-MWE3PK5S.js +0 -551
- package/dist/chunk-MXR7LXGC.js +0 -1084
- package/dist/chunk-MZZOQHNI.js +0 -642
- package/dist/chunk-N6BRVUAP.js +0 -1805
- package/dist/chunk-N7UVR6SG.js +0 -877
- package/dist/chunk-NIHWGZS4.js +0 -1426
- package/dist/chunk-NMTRH4Q3.js +0 -97
- package/dist/chunk-NSVVHQK5.js +0 -41
- package/dist/chunk-NVI2WE7D.js +0 -443
- package/dist/chunk-NZNNXPEE.js +0 -56
- package/dist/chunk-O7QBO3PH.js +0 -58
- package/dist/chunk-OAUPQBO2.js +0 -270
- package/dist/chunk-OB2LMD7C.js +0 -297
- package/dist/chunk-OEGAHDEU.js +0 -1094
- package/dist/chunk-OHEYBWQU.js +0 -58
- package/dist/chunk-OI5B74HF.js +0 -26
- package/dist/chunk-OI6OXUHJ.js +0 -443
- package/dist/chunk-ONOO74UN.js +0 -361
- package/dist/chunk-OWLQ4HZI.js +0 -282
- package/dist/chunk-OX2VMRMV.js +0 -633
- package/dist/chunk-OXYMB6VB.js +0 -466
- package/dist/chunk-OZS4YOOP.js +0 -59
- package/dist/chunk-P4FYE5TX.js +0 -866
- package/dist/chunk-P5KFWM4H.js +0 -98
- package/dist/chunk-PLFQZ7SS.js +0 -90
- package/dist/chunk-PMVU6BI4.js +0 -1847
- package/dist/chunk-PPNH4Y6U.js +0 -282
- package/dist/chunk-PUMLE7RJ.js +0 -1711
- package/dist/chunk-Q4MFANBF.js +0 -282
- package/dist/chunk-Q54YZ4X3.js +0 -26
- package/dist/chunk-Q6XACQW3.js +0 -1847
- package/dist/chunk-QI6ZDDUR.js +0 -35
- package/dist/chunk-QJ5KLY2S.js +0 -1821
- package/dist/chunk-QLEKZMMU.js +0 -282
- package/dist/chunk-QLNWSBWK.js +0 -567
- package/dist/chunk-QQS7BSPA.js +0 -26
- package/dist/chunk-QUGIYF3F.js +0 -712
- package/dist/chunk-QYSW3K7A.js +0 -282
- package/dist/chunk-QZWTKX5K.js +0 -321
- package/dist/chunk-RGGNGVO3.js +0 -98
- package/dist/chunk-RKJDRVV6.js +0 -443
- package/dist/chunk-RUAJTILD.js +0 -26
- package/dist/chunk-S5BHU353.js +0 -43
- package/dist/chunk-S6YEHFC7.js +0 -654
- package/dist/chunk-SHQUSFH7.js +0 -1426
- package/dist/chunk-SMB4DBMD.js +0 -182
- package/dist/chunk-SNYHQP3D.js +0 -743
- package/dist/chunk-SR2DPCTT.js +0 -1803
- package/dist/chunk-SU63HSUU.js +0 -58
- package/dist/chunk-SXXKZJI2.js +0 -303
- package/dist/chunk-T24L3TBF.js +0 -1717
- package/dist/chunk-T2SPHHYU.js +0 -365
- package/dist/chunk-TAQNSOKT.js +0 -692
- package/dist/chunk-TBA46SVX.js +0 -1805
- package/dist/chunk-TCWKKKPP.js +0 -90
- package/dist/chunk-TDNY4SUA.js +0 -41
- package/dist/chunk-TK4P3V5G.js +0 -56
- package/dist/chunk-TNNF56IQ.js +0 -1750
- package/dist/chunk-TR7E6LYX.js +0 -457
- package/dist/chunk-TVV4JK5G.js +0 -282
- package/dist/chunk-UATXXETW.js +0 -278
- package/dist/chunk-UHFUQWST.js +0 -65
- package/dist/chunk-UKDBQVM3.js +0 -346
- package/dist/chunk-URWUFH45.js +0 -98
- package/dist/chunk-USPT25TC.js +0 -365
- package/dist/chunk-UUSIH3XH.js +0 -1429
- package/dist/chunk-UXDIPRCG.js +0 -26
- package/dist/chunk-UYFNXLKR.js +0 -1436
- package/dist/chunk-UZZFP2EE.js +0 -35
- package/dist/chunk-V4G5Y6YM.js +0 -1094
- package/dist/chunk-V6F7KUWD.js +0 -270
- package/dist/chunk-V6P4LDAV.js +0 -65
- package/dist/chunk-VAA4FG3Z.js +0 -365
- package/dist/chunk-VCZLXRMR.js +0 -254
- package/dist/chunk-VDHXSSBT.js +0 -1426
- package/dist/chunk-VI4KRBSU.js +0 -291
- package/dist/chunk-VM4QMKVK.js +0 -254
- package/dist/chunk-VUXB655T.js +0 -1999
- package/dist/chunk-VWGYKYL2.js +0 -737
- package/dist/chunk-VX2OFBJN.js +0 -1426
- package/dist/chunk-VXVIE6DG.js +0 -84
- package/dist/chunk-W3QH7BF5.js +0 -282
- package/dist/chunk-W4OH7HG4.js +0 -40
- package/dist/chunk-WBVJX4GZ.js +0 -98
- package/dist/chunk-WDU2ZV4I.js +0 -1426
- package/dist/chunk-WGCQNOEQ.js +0 -712
- package/dist/chunk-WZQNHS4J.js +0 -52
- package/dist/chunk-X6VUCICU.js +0 -457
- package/dist/chunk-XAY7FM7Y.js +0 -618
- package/dist/chunk-XFW7B23S.js +0 -282
- package/dist/chunk-XJ3MNGY5.js +0 -58
- package/dist/chunk-XJZ5Z2CM.js +0 -642
- package/dist/chunk-XKVFQTJJ.js +0 -254
- package/dist/chunk-XRLFASCY.js +0 -22
- package/dist/chunk-XZGY5ZJ4.js +0 -35
- package/dist/chunk-Y745CBVB.js +0 -944
- package/dist/chunk-YLBJSXYY.js +0 -944
- package/dist/chunk-YQJIKVPZ.js +0 -1429
- package/dist/chunk-YRM2VCZF.js +0 -457
- package/dist/chunk-YS33KBVJ.js +0 -944
- package/dist/chunk-Z27DZPDG.js +0 -41
- package/dist/chunk-Z6GZHJW5.js +0 -877
- package/dist/chunk-ZXQ5NAEN.js +0 -32
- package/dist/contracts-B552GopR.d.cts +0 -245
- package/dist/contracts-B552GopR.d.ts +0 -245
- package/dist/contracts-Bg1ECISC.d.cts +0 -245
- package/dist/contracts-Bg1ECISC.d.ts +0 -245
- package/dist/contracts-CMriKJ6P.d.cts +0 -245
- package/dist/contracts-CMriKJ6P.d.ts +0 -245
- package/dist/src/components/ErrorBoundary.d.ts +0 -15
- package/dist/src/components/ErrorBoundary.js +0 -119
- package/dist/src/core/catch.d.ts +0 -11
- package/dist/src/core/catch.js +0 -28
- package/dist/src/core/each.d.ts +0 -13
- package/dist/src/core/each.js +0 -68
- package/dist/src/core/for.d.ts +0 -12
- package/dist/src/core/for.js +0 -67
- package/dist/src/core/html.d.ts +0 -137
- package/dist/src/core/html.js +0 -155
- package/dist/src/core/htmlIf.d.ts +0 -11
- package/dist/src/core/htmlIf.js +0 -18
- package/dist/src/core/lazy.d.ts +0 -7
- package/dist/src/core/lazy.js +0 -16
- package/dist/src/core/mount.d.ts +0 -7
- package/dist/src/core/mount.js +0 -12
- package/dist/src/core/slots.d.ts +0 -3
- package/dist/src/core/slots.js +0 -3
- package/dist/src/core/suspense.d.ts +0 -10
- package/dist/src/core/suspense.js +0 -33
- package/dist/src/core/tagFactory.d.ts +0 -13
- package/dist/src/core/tagFactory.js +0 -86
- package/dist/src/core/test.d.ts +0 -11
- package/dist/src/core/test.js +0 -28
- package/dist/src/core/types.d.ts +0 -2
- package/dist/src/core/types.js +0 -1
- package/dist/src/core/useComputed.d.ts +0 -6
- package/dist/src/core/useComputed.js +0 -30
- package/dist/src/core/useEffect.d.ts +0 -6
- package/dist/src/core/useEffect.js +0 -23
- package/dist/src/core/useState.d.ts +0 -10
- package/dist/src/core/useState.js +0 -34
- package/dist/src/core/useStore.d.ts +0 -19
- package/dist/src/core/useStore.js +0 -53
- package/dist/src/core/useWatch.d.ts +0 -8
- package/dist/src/core/useWatch.js +0 -23
- package/dist/src/plugins/i18n.d.ts +0 -6
- package/dist/src/plugins/i18n.js +0 -16
- package/dist/src/plugins/router.d.ts +0 -188
- package/dist/src/plugins/router.js +0 -1178
- package/dist/src/reactivity/bindAttribute.d.ts +0 -5
- package/dist/src/reactivity/bindAttribute.js +0 -31
- package/dist/src/reactivity/bindChildNode.d.ts +0 -10
- package/dist/src/reactivity/bindChildNode.js +0 -46
- package/dist/src/reactivity/bindTextNode.d.ts +0 -10
- package/dist/src/reactivity/bindTextNode.js +0 -27
- package/dist/src/reactivity/signal.d.ts +0 -3
- package/dist/src/reactivity/signal.js +0 -1
- package/dist/src/reactivity/track.d.ts +0 -18
- package/dist/src/reactivity/track.js +0 -73
- package/dist/src/reactivity/useComputed.d.ts +0 -6
- package/dist/src/reactivity/useComputed.js +0 -30
- package/dist/src/reactivity/useEffect.d.ts +0 -6
- package/dist/src/reactivity/useEffect.js +0 -23
- package/dist/src/reactivity/useState.d.ts +0 -10
- package/dist/src/reactivity/useState.js +0 -34
- package/dist/src/reactivity/useStore.d.ts +0 -19
- package/dist/src/reactivity/useStore.js +0 -53
- package/dist/src/reactivity/useWatch.d.ts +0 -8
- package/dist/src/reactivity/useWatch.js +0 -23
- package/dist/src/utils/sanitize.d.ts +0 -1
- package/dist/src/utils/sanitize.js +0 -8
- package/dist/ssr-27FOM46T.js +0 -35
- package/dist/ssr-GEOEYQV7.js +0 -35
- package/dist/ssr-GFUTTSJD.js +0 -22
- package/dist/ssr-K7DCR6BZ.js +0 -35
- package/dist/ssr-NFAIHWJJ.js +0 -35
- package/dist/ssr-O6LFMRFP.js +0 -35
- package/dist/ssr-QZEVGMMK.js +0 -35
- package/dist/ssr-SGVBCAGC.js +0 -35
- package/dist/ssr-UB2IXCYX.js +0 -35
- package/dist/ssr-XBZQNV4O.js +0 -22
- package/dist/ssr-Y76FSXDU.js +0 -35
- package/dist/ssr-YQJ4AYBD.js +0 -35
- package/dist/tagFactory-BDN7gDEp.d.cts +0 -21
- package/dist/tagFactory-BDN7gDEp.d.ts +0 -21
- package/dist/tagFactory-CZPO4RXF.d.cts +0 -34
- package/dist/tagFactory-CZPO4RXF.d.ts +0 -34
- package/dist/tagFactory-CgImPVMY.d.cts +0 -22
- package/dist/tagFactory-CgImPVMY.d.ts +0 -22
- package/dist/tagFactory-Cw1iv5if.d.cts +0 -22
- package/dist/tagFactory-Cw1iv5if.d.ts +0 -22
- package/dist/tagFactory-DeAXq9ef.d.cts +0 -30
- package/dist/tagFactory-DeAXq9ef.d.ts +0 -30
- package/dist/tagFactory-SkY0a7L1.d.cts +0 -22
- package/dist/tagFactory-SkY0a7L1.d.ts +0 -22
|
@@ -1,1178 +0,0 @@
|
|
|
1
|
-
import { useState } from "../core/useState";
|
|
2
|
-
import { track } from "../reactivity/track";
|
|
3
|
-
// ============================================================================
|
|
4
|
-
// UTILITY CLASSES
|
|
5
|
-
// ============================================================================
|
|
6
|
-
/**
|
|
7
|
-
* LRU Cache implementation with automatic cleanup
|
|
8
|
-
*/
|
|
9
|
-
class LRUCache {
|
|
10
|
-
constructor(maxSize = 100) {
|
|
11
|
-
this.cache = new Map();
|
|
12
|
-
this.maxSize = Math.max(1, maxSize);
|
|
13
|
-
}
|
|
14
|
-
get(key) {
|
|
15
|
-
const value = this.cache.get(key);
|
|
16
|
-
if (value !== undefined) {
|
|
17
|
-
// Move to end (most recently used)
|
|
18
|
-
this.cache.delete(key);
|
|
19
|
-
this.cache.set(key, value);
|
|
20
|
-
}
|
|
21
|
-
return value;
|
|
22
|
-
}
|
|
23
|
-
set(key, value) {
|
|
24
|
-
if (this.cache.has(key)) {
|
|
25
|
-
this.cache.delete(key);
|
|
26
|
-
}
|
|
27
|
-
else if (this.cache.size >= this.maxSize) {
|
|
28
|
-
// Remove least recently used
|
|
29
|
-
const firstKey = this.cache.keys().next().value;
|
|
30
|
-
if (firstKey !== undefined) {
|
|
31
|
-
this.cache.delete(firstKey);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
this.cache.set(key, value);
|
|
35
|
-
}
|
|
36
|
-
has(key) {
|
|
37
|
-
return this.cache.has(key);
|
|
38
|
-
}
|
|
39
|
-
delete(key) {
|
|
40
|
-
return this.cache.delete(key);
|
|
41
|
-
}
|
|
42
|
-
clear() {
|
|
43
|
-
this.cache.clear();
|
|
44
|
-
}
|
|
45
|
-
get size() {
|
|
46
|
-
return this.cache.size;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Navigation controller for handling concurrent navigation attempts
|
|
51
|
-
*/
|
|
52
|
-
class NavigationController {
|
|
53
|
-
constructor() {
|
|
54
|
-
this.currentController = null;
|
|
55
|
-
}
|
|
56
|
-
async navigate(navigationFn) {
|
|
57
|
-
// Cancel current navigation
|
|
58
|
-
if (this.currentController) {
|
|
59
|
-
this.currentController.abort();
|
|
60
|
-
}
|
|
61
|
-
this.currentController = new AbortController();
|
|
62
|
-
const signal = this.currentController.signal;
|
|
63
|
-
try {
|
|
64
|
-
await navigationFn(signal);
|
|
65
|
-
}
|
|
66
|
-
catch (error) {
|
|
67
|
-
if (error instanceof Error && error.name === 'AbortError') {
|
|
68
|
-
// Navigation was cancelled, this is expected
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
throw error;
|
|
72
|
-
}
|
|
73
|
-
finally {
|
|
74
|
-
if (this.currentController?.signal === signal) {
|
|
75
|
-
this.currentController = null;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
abort() {
|
|
80
|
-
if (this.currentController) {
|
|
81
|
-
this.currentController.abort();
|
|
82
|
-
this.currentController = null;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
get isNavigating() {
|
|
86
|
-
return this.currentController !== null;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* Enhanced route matcher with trie optimization
|
|
91
|
-
*/
|
|
92
|
-
class RouteMatcher {
|
|
93
|
-
constructor(routes) {
|
|
94
|
-
this.routeTrie = new Map();
|
|
95
|
-
this.namedRoutes = new Map();
|
|
96
|
-
this.compiledPatterns = new LRUCache(50);
|
|
97
|
-
this.buildIndex(routes);
|
|
98
|
-
}
|
|
99
|
-
buildIndex(routes, parentPath = '') {
|
|
100
|
-
for (const route of routes) {
|
|
101
|
-
const fullPath = parentPath + route.path;
|
|
102
|
-
// Index by path
|
|
103
|
-
this.routeTrie.set(fullPath, route);
|
|
104
|
-
// Index by name
|
|
105
|
-
if (route.name) {
|
|
106
|
-
this.namedRoutes.set(route.name, route);
|
|
107
|
-
}
|
|
108
|
-
// Handle aliases
|
|
109
|
-
if (route.alias) {
|
|
110
|
-
const aliases = Array.isArray(route.alias) ? route.alias : [route.alias];
|
|
111
|
-
for (const alias of aliases) {
|
|
112
|
-
this.routeTrie.set(parentPath + alias, route);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
// Index children
|
|
116
|
-
if (route.children?.length) {
|
|
117
|
-
this.buildIndex(route.children, fullPath);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
match(path) {
|
|
122
|
-
// Try exact match first
|
|
123
|
-
const exactMatch = this.routeTrie.get(path);
|
|
124
|
-
if (exactMatch) {
|
|
125
|
-
return { route: exactMatch, params: {}, matched: [exactMatch] };
|
|
126
|
-
}
|
|
127
|
-
// Try pattern matching
|
|
128
|
-
for (const [routePath, route] of this.routeTrie) {
|
|
129
|
-
const match = this.matchPattern(path, routePath);
|
|
130
|
-
if (match) {
|
|
131
|
-
return { route, params: match.params, matched: [route] };
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
return null;
|
|
135
|
-
}
|
|
136
|
-
findByName(name) {
|
|
137
|
-
return this.namedRoutes.get(name) || null;
|
|
138
|
-
}
|
|
139
|
-
matchPattern(path, routePath) {
|
|
140
|
-
// Handle wildcard routes
|
|
141
|
-
if (routePath === '*' || routePath.endsWith('/*')) {
|
|
142
|
-
const basePath = routePath.slice(0, -2);
|
|
143
|
-
if (path.startsWith(basePath)) {
|
|
144
|
-
return { params: { pathMatch: path.slice(basePath.length) } };
|
|
145
|
-
}
|
|
146
|
-
return null;
|
|
147
|
-
}
|
|
148
|
-
// Get or compile pattern
|
|
149
|
-
let compiled = this.compiledPatterns.get(routePath);
|
|
150
|
-
if (!compiled) {
|
|
151
|
-
compiled = this.compileRoute(routePath);
|
|
152
|
-
this.compiledPatterns.set(routePath, compiled);
|
|
153
|
-
}
|
|
154
|
-
const match = path.match(compiled.regex);
|
|
155
|
-
if (match) {
|
|
156
|
-
const params = {};
|
|
157
|
-
compiled.keys.forEach((key, i) => {
|
|
158
|
-
if (match[i + 1] !== undefined) {
|
|
159
|
-
params[key] = decodeURIComponent(match[i + 1]);
|
|
160
|
-
}
|
|
161
|
-
});
|
|
162
|
-
return { params };
|
|
163
|
-
}
|
|
164
|
-
return null;
|
|
165
|
-
}
|
|
166
|
-
compileRoute(routePath) {
|
|
167
|
-
const keys = [];
|
|
168
|
-
// Handle optional parameters: /users/:id?
|
|
169
|
-
let pattern = routePath.replace(/\/:[^/]+\?/g, '(?:/([^/]+))?');
|
|
170
|
-
// Handle regular parameters: /users/:id
|
|
171
|
-
pattern = pattern.replace(/:([^/]+)/g, (match, key) => {
|
|
172
|
-
keys.push(key);
|
|
173
|
-
return '([^/]+)';
|
|
174
|
-
});
|
|
175
|
-
return {
|
|
176
|
-
regex: new RegExp(`^${pattern}$`),
|
|
177
|
-
keys
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
rebuild(routes) {
|
|
181
|
-
this.routeTrie.clear();
|
|
182
|
-
this.namedRoutes.clear();
|
|
183
|
-
this.compiledPatterns.clear();
|
|
184
|
-
this.buildIndex(routes);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
/**
|
|
188
|
-
* Guard manager with timeout and error handling
|
|
189
|
-
*/
|
|
190
|
-
class GuardManager {
|
|
191
|
-
constructor(timeout = 5000) {
|
|
192
|
-
this.beforeEachGuards = [];
|
|
193
|
-
this.beforeResolveGuards = [];
|
|
194
|
-
this.afterEachHooks = [];
|
|
195
|
-
this.timeout = timeout;
|
|
196
|
-
}
|
|
197
|
-
async runBeforeEach(to, from, signal) {
|
|
198
|
-
for (const guard of this.beforeEachGuards) {
|
|
199
|
-
if (signal.aborted)
|
|
200
|
-
throw new Error('Navigation aborted');
|
|
201
|
-
const result = await this.runGuard(guard, to, from, signal);
|
|
202
|
-
if (result !== true)
|
|
203
|
-
return result;
|
|
204
|
-
}
|
|
205
|
-
return true;
|
|
206
|
-
}
|
|
207
|
-
async runBeforeResolve(to, from, signal) {
|
|
208
|
-
for (const guard of this.beforeResolveGuards) {
|
|
209
|
-
if (signal.aborted)
|
|
210
|
-
throw new Error('Navigation aborted');
|
|
211
|
-
const result = await this.runGuard(guard, to, from, signal);
|
|
212
|
-
if (result !== true)
|
|
213
|
-
return result;
|
|
214
|
-
}
|
|
215
|
-
return true;
|
|
216
|
-
}
|
|
217
|
-
runAfterEach(to, from) {
|
|
218
|
-
for (const hook of this.afterEachHooks) {
|
|
219
|
-
try {
|
|
220
|
-
hook(to, from);
|
|
221
|
-
}
|
|
222
|
-
catch (error) {
|
|
223
|
-
console.error('[Router] AfterEach hook error:', error);
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
runGuard(guard, to, from, signal) {
|
|
228
|
-
return new Promise((resolve, reject) => {
|
|
229
|
-
if (signal.aborted) {
|
|
230
|
-
reject(new Error('Navigation aborted'));
|
|
231
|
-
return;
|
|
232
|
-
}
|
|
233
|
-
let resolved = false;
|
|
234
|
-
const next = (result) => {
|
|
235
|
-
if (resolved || signal.aborted)
|
|
236
|
-
return;
|
|
237
|
-
resolved = true;
|
|
238
|
-
if (result instanceof Error) {
|
|
239
|
-
reject(result);
|
|
240
|
-
}
|
|
241
|
-
else if (result === false) {
|
|
242
|
-
resolve(false);
|
|
243
|
-
}
|
|
244
|
-
else if (typeof result === 'string') {
|
|
245
|
-
resolve(result);
|
|
246
|
-
}
|
|
247
|
-
else {
|
|
248
|
-
resolve(true);
|
|
249
|
-
}
|
|
250
|
-
};
|
|
251
|
-
// Set up abort handler
|
|
252
|
-
const abortHandler = () => {
|
|
253
|
-
if (!resolved) {
|
|
254
|
-
resolved = true;
|
|
255
|
-
reject(new Error('Navigation aborted'));
|
|
256
|
-
}
|
|
257
|
-
};
|
|
258
|
-
signal.addEventListener('abort', abortHandler);
|
|
259
|
-
// Set up timeout
|
|
260
|
-
const timeoutId = setTimeout(() => {
|
|
261
|
-
if (!resolved) {
|
|
262
|
-
resolved = true;
|
|
263
|
-
signal.removeEventListener('abort', abortHandler);
|
|
264
|
-
reject(new Error('Guard timeout'));
|
|
265
|
-
}
|
|
266
|
-
}, this.timeout);
|
|
267
|
-
try {
|
|
268
|
-
guard(to, from, next);
|
|
269
|
-
}
|
|
270
|
-
catch (error) {
|
|
271
|
-
clearTimeout(timeoutId);
|
|
272
|
-
signal.removeEventListener('abort', abortHandler);
|
|
273
|
-
if (!resolved) {
|
|
274
|
-
resolved = true;
|
|
275
|
-
reject(error instanceof Error ? error : new Error(String(error)));
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
// Cleanup when resolved
|
|
279
|
-
Promise.resolve().then(() => {
|
|
280
|
-
if (resolved) {
|
|
281
|
-
clearTimeout(timeoutId);
|
|
282
|
-
signal.removeEventListener('abort', abortHandler);
|
|
283
|
-
}
|
|
284
|
-
});
|
|
285
|
-
});
|
|
286
|
-
}
|
|
287
|
-
addBeforeEach(guard) {
|
|
288
|
-
this.beforeEachGuards.push(guard);
|
|
289
|
-
return () => {
|
|
290
|
-
const index = this.beforeEachGuards.indexOf(guard);
|
|
291
|
-
if (index > -1)
|
|
292
|
-
this.beforeEachGuards.splice(index, 1);
|
|
293
|
-
};
|
|
294
|
-
}
|
|
295
|
-
addBeforeResolve(guard) {
|
|
296
|
-
this.beforeResolveGuards.push(guard);
|
|
297
|
-
return () => {
|
|
298
|
-
const index = this.beforeResolveGuards.indexOf(guard);
|
|
299
|
-
if (index > -1)
|
|
300
|
-
this.beforeResolveGuards.splice(index, 1);
|
|
301
|
-
};
|
|
302
|
-
}
|
|
303
|
-
addAfterEach(hook) {
|
|
304
|
-
this.afterEachHooks.push(hook);
|
|
305
|
-
return () => {
|
|
306
|
-
const index = this.afterEachHooks.indexOf(hook);
|
|
307
|
-
if (index > -1)
|
|
308
|
-
this.afterEachHooks.splice(index, 1);
|
|
309
|
-
};
|
|
310
|
-
}
|
|
311
|
-
clear() {
|
|
312
|
-
this.beforeEachGuards = [];
|
|
313
|
-
this.beforeResolveGuards = [];
|
|
314
|
-
this.afterEachHooks = [];
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
/**
|
|
318
|
-
* Component loader with caching and error recovery
|
|
319
|
-
*/
|
|
320
|
-
class ComponentLoader {
|
|
321
|
-
constructor(cacheSize = 50, retryDelay = 1000) {
|
|
322
|
-
this.errorCache = new Map();
|
|
323
|
-
this.loadingPromises = new Map();
|
|
324
|
-
this.componentCache = new LRUCache(cacheSize);
|
|
325
|
-
this.retryDelay = retryDelay;
|
|
326
|
-
}
|
|
327
|
-
async loadComponent(route, routePath) {
|
|
328
|
-
if (!('component' in route)) {
|
|
329
|
-
throw new Error(`Route ${routePath} does not have a component`);
|
|
330
|
-
}
|
|
331
|
-
const comp = route.component;
|
|
332
|
-
// Return cached component
|
|
333
|
-
const cached = this.componentCache.get(routePath);
|
|
334
|
-
if (cached)
|
|
335
|
-
return cached;
|
|
336
|
-
// Check if there's already a loading promise
|
|
337
|
-
const existingPromise = this.loadingPromises.get(routePath);
|
|
338
|
-
if (existingPromise)
|
|
339
|
-
return existingPromise;
|
|
340
|
-
// Check error cache
|
|
341
|
-
const errorInfo = this.errorCache.get(routePath);
|
|
342
|
-
if (errorInfo && Date.now() - errorInfo.timestamp < this.retryDelay) {
|
|
343
|
-
throw new Error(`Component loading failed recently, retry in ${this.retryDelay}ms`);
|
|
344
|
-
}
|
|
345
|
-
// Create loading promise
|
|
346
|
-
const loadingPromise = this.doLoadComponent(comp, routePath);
|
|
347
|
-
this.loadingPromises.set(routePath, loadingPromise);
|
|
348
|
-
try {
|
|
349
|
-
const component = await loadingPromise;
|
|
350
|
-
this.componentCache.set(routePath, component);
|
|
351
|
-
this.errorCache.delete(routePath); // Clear error on success
|
|
352
|
-
return component;
|
|
353
|
-
}
|
|
354
|
-
catch (error) {
|
|
355
|
-
// Update error cache
|
|
356
|
-
const currentError = this.errorCache.get(routePath) || { timestamp: 0, count: 0 };
|
|
357
|
-
this.errorCache.set(routePath, {
|
|
358
|
-
timestamp: Date.now(),
|
|
359
|
-
count: currentError.count + 1
|
|
360
|
-
});
|
|
361
|
-
throw error;
|
|
362
|
-
}
|
|
363
|
-
finally {
|
|
364
|
-
this.loadingPromises.delete(routePath);
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
async doLoadComponent(comp, routePath) {
|
|
368
|
-
// Synchronous component
|
|
369
|
-
if (!this.isAsyncComponent(comp)) {
|
|
370
|
-
const result = comp();
|
|
371
|
-
if (!this.isHTMLElement(result)) {
|
|
372
|
-
throw new Error(`Component for route "${routePath}" must return HTMLElement, got ${typeof result}`);
|
|
373
|
-
}
|
|
374
|
-
return comp;
|
|
375
|
-
}
|
|
376
|
-
// Async component
|
|
377
|
-
try {
|
|
378
|
-
const result = await comp();
|
|
379
|
-
const component = this.extractComponent(result, routePath);
|
|
380
|
-
// Validate component
|
|
381
|
-
const testElement = component();
|
|
382
|
-
if (!this.isHTMLElement(testElement)) {
|
|
383
|
-
throw new Error(`Component for route "${routePath}" must return HTMLElement, got ${typeof testElement}`);
|
|
384
|
-
}
|
|
385
|
-
return component;
|
|
386
|
-
}
|
|
387
|
-
catch (error) {
|
|
388
|
-
throw new Error(`Failed to load component for route "${routePath}": ${error instanceof Error ? error.message : String(error)}`);
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
isAsyncComponent(comp) {
|
|
392
|
-
return comp.constructor.name === 'AsyncFunction' ||
|
|
393
|
-
(typeof comp === 'function' && comp.toString().includes('import('));
|
|
394
|
-
}
|
|
395
|
-
isHTMLElement(value) {
|
|
396
|
-
return value instanceof HTMLElement;
|
|
397
|
-
}
|
|
398
|
-
extractComponent(result, routePath) {
|
|
399
|
-
if ('default' in result && typeof result.default === 'function') {
|
|
400
|
-
return result.default;
|
|
401
|
-
}
|
|
402
|
-
if (typeof result === 'function') {
|
|
403
|
-
return result;
|
|
404
|
-
}
|
|
405
|
-
if (this.isHTMLElement(result)) {
|
|
406
|
-
return () => result;
|
|
407
|
-
}
|
|
408
|
-
throw new Error(`Invalid component module for route "${routePath}"`);
|
|
409
|
-
}
|
|
410
|
-
clearErrors() {
|
|
411
|
-
this.errorCache.clear();
|
|
412
|
-
}
|
|
413
|
-
clearCache() {
|
|
414
|
-
this.componentCache.clear();
|
|
415
|
-
this.errorCache.clear();
|
|
416
|
-
this.loadingPromises.clear();
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
// ============================================================================
|
|
420
|
-
// MAIN ROUTER CLASS
|
|
421
|
-
// ============================================================================
|
|
422
|
-
export class SibuRouter {
|
|
423
|
-
constructor(routes, options = {}) {
|
|
424
|
-
// Event listeners cleanup
|
|
425
|
-
this.cleanup = [];
|
|
426
|
-
this.options = {
|
|
427
|
-
mode: 'history',
|
|
428
|
-
base: '',
|
|
429
|
-
linkActiveClass: 'router-link-active',
|
|
430
|
-
linkExactActiveClass: 'router-link-exact-active',
|
|
431
|
-
fallback: true,
|
|
432
|
-
guardTimeout: 5000,
|
|
433
|
-
cacheSize: 50,
|
|
434
|
-
errorRetryDelay: 1000,
|
|
435
|
-
preloadStrategy: 'none',
|
|
436
|
-
...options
|
|
437
|
-
};
|
|
438
|
-
// Initialize state properly
|
|
439
|
-
const [currentRouteState, setCurrentRouteState] = useState(this.createInitialRoute());
|
|
440
|
-
const [isReadyState, setIsReadyState] = useState(false);
|
|
441
|
-
this.currentRouteGetter = currentRouteState;
|
|
442
|
-
this.currentRouteSetter = setCurrentRouteState;
|
|
443
|
-
this.isReadyGetter = isReadyState;
|
|
444
|
-
this.isReadySetter = setIsReadyState;
|
|
445
|
-
this.matcher = new RouteMatcher(routes);
|
|
446
|
-
this.guards = new GuardManager(this.options.guardTimeout);
|
|
447
|
-
this.loader = new ComponentLoader(this.options.cacheSize, this.options.errorRetryDelay);
|
|
448
|
-
this.navigator = new NavigationController();
|
|
449
|
-
this.initialize();
|
|
450
|
-
}
|
|
451
|
-
initialize() {
|
|
452
|
-
// Set up event listeners
|
|
453
|
-
if (this.options.mode === 'history') {
|
|
454
|
-
const popstateHandler = () => this.handleLocationChange();
|
|
455
|
-
window.addEventListener('popstate', popstateHandler);
|
|
456
|
-
this.cleanup.push(() => window.removeEventListener('popstate', popstateHandler));
|
|
457
|
-
}
|
|
458
|
-
else {
|
|
459
|
-
const hashHandler = () => this.handleLocationChange();
|
|
460
|
-
window.addEventListener('hashchange', hashHandler);
|
|
461
|
-
this.cleanup.push(() => window.removeEventListener('hashchange', hashHandler));
|
|
462
|
-
}
|
|
463
|
-
// Set initial route
|
|
464
|
-
queueMicrotask(() => {
|
|
465
|
-
this.handleLocationChange();
|
|
466
|
-
this.isReadySetter(true);
|
|
467
|
-
});
|
|
468
|
-
}
|
|
469
|
-
createInitialRoute() {
|
|
470
|
-
return {
|
|
471
|
-
path: '/',
|
|
472
|
-
params: {},
|
|
473
|
-
query: {},
|
|
474
|
-
hash: '',
|
|
475
|
-
meta: {},
|
|
476
|
-
matched: []
|
|
477
|
-
};
|
|
478
|
-
}
|
|
479
|
-
handleLocationChange() {
|
|
480
|
-
const path = this.getCurrentPath();
|
|
481
|
-
const context = this.createRouteContext(path);
|
|
482
|
-
this.currentRouteSetter(context);
|
|
483
|
-
}
|
|
484
|
-
getCurrentPath() {
|
|
485
|
-
const { mode, base } = this.options;
|
|
486
|
-
if (mode === 'hash') {
|
|
487
|
-
return window.location.hash.slice(1) || '/';
|
|
488
|
-
}
|
|
489
|
-
let path = window.location.pathname;
|
|
490
|
-
if (base && path.startsWith(base)) {
|
|
491
|
-
path = path.slice(base.length);
|
|
492
|
-
}
|
|
493
|
-
return (path || '/') + window.location.search + window.location.hash;
|
|
494
|
-
}
|
|
495
|
-
createRouteContext(fullPath) {
|
|
496
|
-
const [pathWithQuery, hash = ''] = fullPath.split('#');
|
|
497
|
-
const [path, queryString = ''] = pathWithQuery.split('?');
|
|
498
|
-
const query = Object.fromEntries(new URLSearchParams(queryString));
|
|
499
|
-
const match = this.matcher.match(path || '/');
|
|
500
|
-
const params = match?.params || {};
|
|
501
|
-
const meta = match?.route.meta || {};
|
|
502
|
-
const matched = match ? [match.route] : [];
|
|
503
|
-
return {
|
|
504
|
-
path: path || '/',
|
|
505
|
-
params,
|
|
506
|
-
query,
|
|
507
|
-
hash,
|
|
508
|
-
meta,
|
|
509
|
-
matched
|
|
510
|
-
};
|
|
511
|
-
}
|
|
512
|
-
// Public API
|
|
513
|
-
async navigate(to, options = {}) {
|
|
514
|
-
try {
|
|
515
|
-
const result = await this.navigator.navigate(async (signal) => {
|
|
516
|
-
const targetPath = this.resolvePath(to);
|
|
517
|
-
const from = this.currentRouteGetter();
|
|
518
|
-
const toContext = this.createRouteContext(targetPath);
|
|
519
|
-
// Check for duplicate navigation
|
|
520
|
-
if (this.isSameRoute(from, toContext)) {
|
|
521
|
-
throw new NavigationFailureError('duplicated', from, toContext);
|
|
522
|
-
}
|
|
523
|
-
await this.performNavigation(toContext, from, options, signal);
|
|
524
|
-
});
|
|
525
|
-
return { success: true, route: this.currentRouteGetter() };
|
|
526
|
-
}
|
|
527
|
-
catch (error) {
|
|
528
|
-
if (error instanceof NavigationFailureError) {
|
|
529
|
-
return { success: false, failure: error.toFailure() };
|
|
530
|
-
}
|
|
531
|
-
const failure = {
|
|
532
|
-
type: 'aborted',
|
|
533
|
-
from: this.currentRouteGetter(),
|
|
534
|
-
to: this.createRouteContext(this.resolvePath(to)),
|
|
535
|
-
error: error instanceof Error ? error : new Error(String(error))
|
|
536
|
-
};
|
|
537
|
-
return { success: false, failure };
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
async performNavigation(to, from, options, signal) {
|
|
541
|
-
// Run beforeEach guards
|
|
542
|
-
const beforeEachResult = await this.guards.runBeforeEach(to, from, signal);
|
|
543
|
-
if (beforeEachResult !== true) {
|
|
544
|
-
if (typeof beforeEachResult === 'string') {
|
|
545
|
-
return this.performNavigation(this.createRouteContext(beforeEachResult), from, options, signal);
|
|
546
|
-
}
|
|
547
|
-
throw new NavigationFailureError('aborted', from, to);
|
|
548
|
-
}
|
|
549
|
-
// Handle route-specific logic
|
|
550
|
-
const match = this.matcher.match(to.path);
|
|
551
|
-
if (match) {
|
|
552
|
-
const { route } = match;
|
|
553
|
-
// Run beforeEnter guards
|
|
554
|
-
if ('beforeEnter' in route && route.beforeEnter) {
|
|
555
|
-
const guards = Array.isArray(route.beforeEnter) ? route.beforeEnter : [route.beforeEnter];
|
|
556
|
-
for (const guard of guards) {
|
|
557
|
-
if (signal.aborted)
|
|
558
|
-
throw new Error('Navigation aborted');
|
|
559
|
-
const result = await guard(to, from);
|
|
560
|
-
if (result !== true) {
|
|
561
|
-
if (typeof result === 'string') {
|
|
562
|
-
return this.performNavigation(this.createRouteContext(result), from, options, signal);
|
|
563
|
-
}
|
|
564
|
-
throw new NavigationFailureError('aborted', from, to);
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
// Handle redirects
|
|
569
|
-
if ('redirect' in route) {
|
|
570
|
-
const redirectPath = typeof route.redirect === 'function'
|
|
571
|
-
? route.redirect(to)
|
|
572
|
-
: route.redirect;
|
|
573
|
-
return this.performNavigation(this.createRouteContext(redirectPath), from, options, signal);
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
// Run beforeResolve guards
|
|
577
|
-
const beforeResolveResult = await this.guards.runBeforeResolve(to, from, signal);
|
|
578
|
-
if (beforeResolveResult !== true) {
|
|
579
|
-
if (typeof beforeResolveResult === 'string') {
|
|
580
|
-
return this.performNavigation(this.createRouteContext(beforeResolveResult), from, options, signal);
|
|
581
|
-
}
|
|
582
|
-
throw new NavigationFailureError('aborted', from, to);
|
|
583
|
-
}
|
|
584
|
-
// Update browser history
|
|
585
|
-
this.updateHistory(to, options);
|
|
586
|
-
// Update current route
|
|
587
|
-
this.currentRouteSetter(to);
|
|
588
|
-
// Run afterEach hooks
|
|
589
|
-
this.guards.runAfterEach(to, from);
|
|
590
|
-
// Handle scroll behavior
|
|
591
|
-
this.handleScrollBehavior(to, from);
|
|
592
|
-
}
|
|
593
|
-
resolvePath(to) {
|
|
594
|
-
if (typeof to === 'string')
|
|
595
|
-
return to;
|
|
596
|
-
let path = to.path || '';
|
|
597
|
-
// Handle named routes
|
|
598
|
-
if (to.name && !path) {
|
|
599
|
-
const namedRoute = this.matcher.findByName(to.name);
|
|
600
|
-
if (namedRoute) {
|
|
601
|
-
path = namedRoute.path;
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
// Replace parameters
|
|
605
|
-
if (to.params) {
|
|
606
|
-
for (const [key, value] of Object.entries(to.params)) {
|
|
607
|
-
path = path.replace(`:${key}`, encodeURIComponent(value));
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
// Add query parameters
|
|
611
|
-
if (to.query && Object.keys(to.query).length > 0) {
|
|
612
|
-
path += '?' + new URLSearchParams(to.query).toString();
|
|
613
|
-
}
|
|
614
|
-
// Add hash
|
|
615
|
-
if (to.hash) {
|
|
616
|
-
path += '#' + to.hash;
|
|
617
|
-
}
|
|
618
|
-
return path;
|
|
619
|
-
}
|
|
620
|
-
isSameRoute(from, to) {
|
|
621
|
-
return from.path === to.path &&
|
|
622
|
-
JSON.stringify(from.params) === JSON.stringify(to.params) &&
|
|
623
|
-
JSON.stringify(from.query) === JSON.stringify(to.query) &&
|
|
624
|
-
from.hash === to.hash;
|
|
625
|
-
}
|
|
626
|
-
updateHistory(to, options) {
|
|
627
|
-
const fullPath = this.options.base + to.path +
|
|
628
|
-
(Object.keys(to.query).length ? '?' + new URLSearchParams(to.query).toString() : '') +
|
|
629
|
-
(to.hash ? '#' + to.hash : '');
|
|
630
|
-
if (options.replace) {
|
|
631
|
-
history.replaceState(options.state || {}, '', fullPath);
|
|
632
|
-
}
|
|
633
|
-
else {
|
|
634
|
-
history.pushState(options.state || {}, '', fullPath);
|
|
635
|
-
}
|
|
636
|
-
}
|
|
637
|
-
handleScrollBehavior(to, from) {
|
|
638
|
-
if (this.options.scrollBehavior) {
|
|
639
|
-
const scrollTo = this.options.scrollBehavior(to, from, null);
|
|
640
|
-
if (scrollTo) {
|
|
641
|
-
requestAnimationFrame(() => {
|
|
642
|
-
window.scrollTo(scrollTo.x, scrollTo.y);
|
|
643
|
-
});
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
// Component loading
|
|
648
|
-
async loadComponent(route, routePath) {
|
|
649
|
-
return this.loader.loadComponent(route, routePath);
|
|
650
|
-
}
|
|
651
|
-
// Guards API
|
|
652
|
-
beforeEach(guard) {
|
|
653
|
-
return this.guards.addBeforeEach(guard);
|
|
654
|
-
}
|
|
655
|
-
beforeResolve(guard) {
|
|
656
|
-
return this.guards.addBeforeResolve(guard);
|
|
657
|
-
}
|
|
658
|
-
afterEach(hook) {
|
|
659
|
-
return this.guards.addAfterEach(hook);
|
|
660
|
-
}
|
|
661
|
-
// Utility methods
|
|
662
|
-
push(to) {
|
|
663
|
-
return this.navigate(to);
|
|
664
|
-
}
|
|
665
|
-
replace(to) {
|
|
666
|
-
return this.navigate(to, { replace: true });
|
|
667
|
-
}
|
|
668
|
-
go(delta) {
|
|
669
|
-
history.go(delta);
|
|
670
|
-
}
|
|
671
|
-
back() {
|
|
672
|
-
history.back();
|
|
673
|
-
}
|
|
674
|
-
forward() {
|
|
675
|
-
history.forward();
|
|
676
|
-
}
|
|
677
|
-
// State getters
|
|
678
|
-
get currentRoute() {
|
|
679
|
-
return this.currentRouteGetter();
|
|
680
|
-
}
|
|
681
|
-
get isReady() {
|
|
682
|
-
return this.isReadyGetter();
|
|
683
|
-
}
|
|
684
|
-
get isNavigating() {
|
|
685
|
-
return this.navigator.isNavigating;
|
|
686
|
-
}
|
|
687
|
-
// Cleanup
|
|
688
|
-
destroy() {
|
|
689
|
-
this.navigator.abort();
|
|
690
|
-
this.cleanup.forEach(fn => fn());
|
|
691
|
-
this.cleanup = [];
|
|
692
|
-
this.guards.clear();
|
|
693
|
-
this.loader.clearCache();
|
|
694
|
-
this.isReadySetter(false);
|
|
695
|
-
}
|
|
696
|
-
// Cache management
|
|
697
|
-
clearCache() {
|
|
698
|
-
this.loader.clearCache();
|
|
699
|
-
}
|
|
700
|
-
clearErrorCache() {
|
|
701
|
-
this.loader.clearErrors();
|
|
702
|
-
}
|
|
703
|
-
// Route management
|
|
704
|
-
updateRoutes(routes) {
|
|
705
|
-
this.matcher.rebuild(routes);
|
|
706
|
-
this.clearCache();
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
|
-
// ============================================================================
|
|
710
|
-
// NAVIGATION FAILURE CLASS
|
|
711
|
-
// ============================================================================
|
|
712
|
-
class NavigationFailureError extends Error {
|
|
713
|
-
constructor(type, from, to, error) {
|
|
714
|
-
super(`Navigation ${type}: from ${from.path} to ${to.path}`);
|
|
715
|
-
this.type = type;
|
|
716
|
-
this.from = from;
|
|
717
|
-
this.to = to;
|
|
718
|
-
this.name = 'NavigationFailureError';
|
|
719
|
-
if (error) {
|
|
720
|
-
this.cause = error;
|
|
721
|
-
}
|
|
722
|
-
}
|
|
723
|
-
toFailure() {
|
|
724
|
-
return {
|
|
725
|
-
type: this.type,
|
|
726
|
-
from: this.from,
|
|
727
|
-
to: this.to,
|
|
728
|
-
error: this.cause instanceof Error ? this.cause : undefined
|
|
729
|
-
};
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
// ============================================================================
|
|
733
|
-
// GLOBAL ROUTER INSTANCE (for compatibility)
|
|
734
|
-
// ============================================================================
|
|
735
|
-
let globalRouter = null;
|
|
736
|
-
export function createRouter(routes, options = {}) {
|
|
737
|
-
if (globalRouter) {
|
|
738
|
-
globalRouter.destroy();
|
|
739
|
-
}
|
|
740
|
-
globalRouter = new SibuRouter(routes, options);
|
|
741
|
-
return globalRouter;
|
|
742
|
-
}
|
|
743
|
-
// ============================================================================
|
|
744
|
-
// COMPATIBILITY API (uses global router instance)
|
|
745
|
-
// ============================================================================
|
|
746
|
-
export function useRoute() {
|
|
747
|
-
if (!globalRouter)
|
|
748
|
-
throw new Error('Router not initialized. Call createRouter() first.');
|
|
749
|
-
return globalRouter.currentRoute;
|
|
750
|
-
}
|
|
751
|
-
export function useRouter() {
|
|
752
|
-
if (!globalRouter)
|
|
753
|
-
throw new Error('Router not initialized. Call createRouter() first.');
|
|
754
|
-
return {
|
|
755
|
-
currentRoute: globalRouter.currentRoute,
|
|
756
|
-
isReady: globalRouter.isReady,
|
|
757
|
-
isNavigating: globalRouter.isNavigating,
|
|
758
|
-
push: (to) => globalRouter.push(to),
|
|
759
|
-
replace: (to) => globalRouter.replace(to),
|
|
760
|
-
go: (delta) => globalRouter.go(delta),
|
|
761
|
-
back: () => globalRouter.back(),
|
|
762
|
-
forward: () => globalRouter.forward(),
|
|
763
|
-
beforeEach: (guard) => globalRouter.beforeEach(guard),
|
|
764
|
-
beforeResolve: (guard) => globalRouter.beforeResolve(guard),
|
|
765
|
-
afterEach: (hook) => globalRouter.afterEach(hook),
|
|
766
|
-
};
|
|
767
|
-
}
|
|
768
|
-
export function navigate(to, options) {
|
|
769
|
-
if (!globalRouter)
|
|
770
|
-
throw new Error('Router not initialized. Call createRouter() first.');
|
|
771
|
-
return globalRouter.navigate(to, options);
|
|
772
|
-
}
|
|
773
|
-
export function push(to) {
|
|
774
|
-
if (!globalRouter)
|
|
775
|
-
throw new Error('Router not initialized. Call createRouter() first.');
|
|
776
|
-
return globalRouter.push(to);
|
|
777
|
-
}
|
|
778
|
-
export function replace(to) {
|
|
779
|
-
if (!globalRouter)
|
|
780
|
-
throw new Error('Router not initialized. Call createRouter() first.');
|
|
781
|
-
return globalRouter.replace(to);
|
|
782
|
-
}
|
|
783
|
-
export function go(delta) {
|
|
784
|
-
if (!globalRouter)
|
|
785
|
-
throw new Error('Router not initialized. Call createRouter() first.');
|
|
786
|
-
globalRouter.go(delta);
|
|
787
|
-
}
|
|
788
|
-
export function back() {
|
|
789
|
-
if (!globalRouter)
|
|
790
|
-
throw new Error('Router not initialized. Call createRouter() first.');
|
|
791
|
-
globalRouter.back();
|
|
792
|
-
}
|
|
793
|
-
export function forward() {
|
|
794
|
-
if (!globalRouter)
|
|
795
|
-
throw new Error('Router not initialized. Call createRouter() first.');
|
|
796
|
-
globalRouter.forward();
|
|
797
|
-
}
|
|
798
|
-
export function beforeEach(guard) {
|
|
799
|
-
if (!globalRouter)
|
|
800
|
-
throw new Error('Router not initialized. Call createRouter() first.');
|
|
801
|
-
return globalRouter.beforeEach(guard);
|
|
802
|
-
}
|
|
803
|
-
export function beforeResolve(guard) {
|
|
804
|
-
if (!globalRouter)
|
|
805
|
-
throw new Error('Router not initialized. Call createRouter() first.');
|
|
806
|
-
return globalRouter.beforeResolve(guard);
|
|
807
|
-
}
|
|
808
|
-
export function afterEach(hook) {
|
|
809
|
-
if (!globalRouter)
|
|
810
|
-
throw new Error('Router not initialized. Call createRouter() first.');
|
|
811
|
-
return globalRouter.afterEach(hook);
|
|
812
|
-
}
|
|
813
|
-
// ============================================================================
|
|
814
|
-
// ROUTE COMPONENT
|
|
815
|
-
// ============================================================================
|
|
816
|
-
export function Route() {
|
|
817
|
-
const anchor = document.createComment("route-outlet");
|
|
818
|
-
let currentNode = null;
|
|
819
|
-
let loadingNode = null;
|
|
820
|
-
let errorNode = null;
|
|
821
|
-
let isUpdating = false;
|
|
822
|
-
let currentPath = '';
|
|
823
|
-
const cleanupNodes = () => {
|
|
824
|
-
[currentNode, loadingNode, errorNode].forEach(node => {
|
|
825
|
-
if (node?.parentNode) {
|
|
826
|
-
node.parentNode.removeChild(node);
|
|
827
|
-
}
|
|
828
|
-
});
|
|
829
|
-
currentNode = null;
|
|
830
|
-
loadingNode = null;
|
|
831
|
-
errorNode = null;
|
|
832
|
-
};
|
|
833
|
-
const showLoading = () => {
|
|
834
|
-
if (!loadingNode && anchor.parentNode) {
|
|
835
|
-
loadingNode = document.createElement('div');
|
|
836
|
-
loadingNode.className = 'route-loading';
|
|
837
|
-
loadingNode.setAttribute('role', 'status');
|
|
838
|
-
loadingNode.setAttribute('aria-label', 'Loading route');
|
|
839
|
-
const spinner = document.createElement('div');
|
|
840
|
-
spinner.className = 'route-loading-spinner';
|
|
841
|
-
spinner.setAttribute('aria-hidden', 'true');
|
|
842
|
-
const text = document.createElement('span');
|
|
843
|
-
text.textContent = 'Loading...';
|
|
844
|
-
text.className = 'route-loading-text';
|
|
845
|
-
loadingNode.appendChild(spinner);
|
|
846
|
-
loadingNode.appendChild(text);
|
|
847
|
-
anchor.parentNode.insertBefore(loadingNode, anchor.nextSibling);
|
|
848
|
-
}
|
|
849
|
-
};
|
|
850
|
-
const hideLoading = () => {
|
|
851
|
-
if (loadingNode?.parentNode) {
|
|
852
|
-
loadingNode.parentNode.removeChild(loadingNode);
|
|
853
|
-
loadingNode = null;
|
|
854
|
-
}
|
|
855
|
-
};
|
|
856
|
-
const showError = (error) => {
|
|
857
|
-
if (!anchor.parentNode)
|
|
858
|
-
return;
|
|
859
|
-
cleanupNodes();
|
|
860
|
-
errorNode = document.createElement('div');
|
|
861
|
-
errorNode.className = 'route-error';
|
|
862
|
-
errorNode.setAttribute('role', 'alert');
|
|
863
|
-
errorNode.setAttribute('aria-live', 'assertive');
|
|
864
|
-
// SECURITY FIX: Use textContent instead of innerHTML to prevent XSS
|
|
865
|
-
const title = document.createElement('h3');
|
|
866
|
-
title.textContent = 'Route Error';
|
|
867
|
-
title.className = 'route-error-title';
|
|
868
|
-
const message = document.createElement('p');
|
|
869
|
-
message.textContent = error.message || 'Failed to load route component';
|
|
870
|
-
message.className = 'route-error-message';
|
|
871
|
-
const retryButton = document.createElement('button');
|
|
872
|
-
retryButton.textContent = 'Retry';
|
|
873
|
-
retryButton.className = 'route-error-retry';
|
|
874
|
-
retryButton.type = 'button';
|
|
875
|
-
retryButton.addEventListener('click', () => {
|
|
876
|
-
if (globalRouter) {
|
|
877
|
-
globalRouter.clearErrorCache();
|
|
878
|
-
update(); // Trigger retry
|
|
879
|
-
}
|
|
880
|
-
});
|
|
881
|
-
errorNode.appendChild(title);
|
|
882
|
-
errorNode.appendChild(message);
|
|
883
|
-
errorNode.appendChild(retryButton);
|
|
884
|
-
anchor.parentNode.insertBefore(errorNode, anchor.nextSibling);
|
|
885
|
-
};
|
|
886
|
-
const update = async () => {
|
|
887
|
-
if (!globalRouter || isUpdating)
|
|
888
|
-
return;
|
|
889
|
-
const route = globalRouter.currentRoute;
|
|
890
|
-
// Avoid unnecessary updates
|
|
891
|
-
if (route.path === currentPath && currentNode)
|
|
892
|
-
return;
|
|
893
|
-
isUpdating = true;
|
|
894
|
-
currentPath = route.path;
|
|
895
|
-
try {
|
|
896
|
-
const match = globalRouter['matcher'].match(route.path);
|
|
897
|
-
if (!match) {
|
|
898
|
-
cleanupNodes();
|
|
899
|
-
return;
|
|
900
|
-
}
|
|
901
|
-
const { route: routeDef } = match;
|
|
902
|
-
// Handle redirect routes (should be handled by router, but safety check)
|
|
903
|
-
if ('redirect' in routeDef) {
|
|
904
|
-
const redirectPath = typeof routeDef.redirect === 'function'
|
|
905
|
-
? routeDef.redirect(route)
|
|
906
|
-
: routeDef.redirect;
|
|
907
|
-
queueMicrotask(() => globalRouter.navigate(redirectPath));
|
|
908
|
-
return;
|
|
909
|
-
}
|
|
910
|
-
// Handle component routes
|
|
911
|
-
if ('component' in routeDef) {
|
|
912
|
-
try {
|
|
913
|
-
// Show loading for async components
|
|
914
|
-
const isAsync = routeDef.component.constructor.name === 'AsyncFunction' ||
|
|
915
|
-
routeDef.component.toString().includes('import(');
|
|
916
|
-
if (isAsync) {
|
|
917
|
-
showLoading();
|
|
918
|
-
}
|
|
919
|
-
const component = await globalRouter.loadComponent(routeDef, route.path);
|
|
920
|
-
const node = component();
|
|
921
|
-
if (node && anchor.parentNode && route.path === currentPath) {
|
|
922
|
-
cleanupNodes();
|
|
923
|
-
anchor.parentNode.insertBefore(node, anchor.nextSibling);
|
|
924
|
-
currentNode = node;
|
|
925
|
-
}
|
|
926
|
-
}
|
|
927
|
-
catch (error) {
|
|
928
|
-
hideLoading();
|
|
929
|
-
showError(error instanceof Error ? error : new Error(String(error)));
|
|
930
|
-
}
|
|
931
|
-
}
|
|
932
|
-
}
|
|
933
|
-
catch (error) {
|
|
934
|
-
console.error('[Route] Update failed:', error);
|
|
935
|
-
showError(error instanceof Error ? error : new Error(String(error)));
|
|
936
|
-
}
|
|
937
|
-
finally {
|
|
938
|
-
isUpdating = false;
|
|
939
|
-
}
|
|
940
|
-
};
|
|
941
|
-
// Set up reactive tracking
|
|
942
|
-
queueMicrotask(() => track(update, update));
|
|
943
|
-
return anchor;
|
|
944
|
-
}
|
|
945
|
-
// ============================================================================
|
|
946
|
-
// ROUTER LINK COMPONENT
|
|
947
|
-
// ============================================================================
|
|
948
|
-
export function RouterLink(props) {
|
|
949
|
-
if (!globalRouter)
|
|
950
|
-
throw new Error('Router not initialized. Call createRouter() first.');
|
|
951
|
-
const { to, replace = false, activeClass, exactActiveClass, children, target, rel, ...attrs } = props;
|
|
952
|
-
const route = globalRouter.currentRoute;
|
|
953
|
-
const href = globalRouter['resolvePath'](to);
|
|
954
|
-
// Calculate active states
|
|
955
|
-
const isActive = route.path.startsWith(href.split('?')[0].split('#')[0]);
|
|
956
|
-
const isExactActive = route.path === href.split('?')[0].split('#')[0];
|
|
957
|
-
const link = document.createElement('a');
|
|
958
|
-
link.href = href;
|
|
959
|
-
// Set target and rel for security
|
|
960
|
-
if (target) {
|
|
961
|
-
link.target = target;
|
|
962
|
-
// Security: Add rel="noopener noreferrer" for external links
|
|
963
|
-
if (target === '_blank') {
|
|
964
|
-
link.rel = rel ? `${rel} noopener noreferrer` : 'noopener noreferrer';
|
|
965
|
-
}
|
|
966
|
-
else if (rel) {
|
|
967
|
-
link.rel = rel;
|
|
968
|
-
}
|
|
969
|
-
}
|
|
970
|
-
else if (rel) {
|
|
971
|
-
link.rel = rel;
|
|
972
|
-
}
|
|
973
|
-
// Set active classes
|
|
974
|
-
const classes = [];
|
|
975
|
-
const options = globalRouter['options'];
|
|
976
|
-
if (isActive) {
|
|
977
|
-
if (activeClass)
|
|
978
|
-
classes.push(activeClass);
|
|
979
|
-
else if (options.linkActiveClass)
|
|
980
|
-
classes.push(options.linkActiveClass);
|
|
981
|
-
}
|
|
982
|
-
if (isExactActive) {
|
|
983
|
-
if (exactActiveClass)
|
|
984
|
-
classes.push(exactActiveClass);
|
|
985
|
-
else if (options.linkExactActiveClass)
|
|
986
|
-
classes.push(options.linkExactActiveClass);
|
|
987
|
-
}
|
|
988
|
-
if (classes.length > 0) {
|
|
989
|
-
link.className = classes.join(' ');
|
|
990
|
-
}
|
|
991
|
-
// Set other attributes (sanitize to prevent XSS)
|
|
992
|
-
Object.entries(attrs).forEach(([key, value]) => {
|
|
993
|
-
if (key.startsWith('on') || key === 'href')
|
|
994
|
-
return; // Skip event handlers and href
|
|
995
|
-
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
|
996
|
-
link.setAttribute(key, String(value));
|
|
997
|
-
}
|
|
998
|
-
});
|
|
999
|
-
// Set content
|
|
1000
|
-
if (typeof children === 'string') {
|
|
1001
|
-
link.textContent = children;
|
|
1002
|
-
}
|
|
1003
|
-
else if (children instanceof Node) {
|
|
1004
|
-
link.appendChild(children);
|
|
1005
|
-
}
|
|
1006
|
-
else if (Array.isArray(children)) {
|
|
1007
|
-
children.forEach(child => {
|
|
1008
|
-
if (typeof child === 'string') {
|
|
1009
|
-
link.appendChild(document.createTextNode(child));
|
|
1010
|
-
}
|
|
1011
|
-
else if (child instanceof Node) {
|
|
1012
|
-
link.appendChild(child);
|
|
1013
|
-
}
|
|
1014
|
-
});
|
|
1015
|
-
}
|
|
1016
|
-
// Handle click for internal navigation
|
|
1017
|
-
link.addEventListener('click', (e) => {
|
|
1018
|
-
// Let browser handle external links, modified clicks, or when target is set
|
|
1019
|
-
if (target ||
|
|
1020
|
-
e.metaKey ||
|
|
1021
|
-
e.ctrlKey ||
|
|
1022
|
-
e.shiftKey ||
|
|
1023
|
-
e.altKey ||
|
|
1024
|
-
e.button !== 0) {
|
|
1025
|
-
return;
|
|
1026
|
-
}
|
|
1027
|
-
e.preventDefault();
|
|
1028
|
-
globalRouter.navigate(to, { replace });
|
|
1029
|
-
});
|
|
1030
|
-
return link;
|
|
1031
|
-
}
|
|
1032
|
-
// ============================================================================
|
|
1033
|
-
// SUSPENSE COMPONENT (for code splitting)
|
|
1034
|
-
// ============================================================================
|
|
1035
|
-
export function Suspense(props) {
|
|
1036
|
-
const anchor = document.createComment("suspense-boundary");
|
|
1037
|
-
let currentNode = null;
|
|
1038
|
-
let fallbackNode = null;
|
|
1039
|
-
let isLoading = false;
|
|
1040
|
-
const cleanupNodes = () => {
|
|
1041
|
-
[currentNode, fallbackNode].forEach(node => {
|
|
1042
|
-
if (node?.parentNode) {
|
|
1043
|
-
node.parentNode.removeChild(node);
|
|
1044
|
-
}
|
|
1045
|
-
});
|
|
1046
|
-
currentNode = null;
|
|
1047
|
-
fallbackNode = null;
|
|
1048
|
-
};
|
|
1049
|
-
const showFallback = () => {
|
|
1050
|
-
if (fallbackNode || !props.fallback || !anchor.parentNode)
|
|
1051
|
-
return;
|
|
1052
|
-
try {
|
|
1053
|
-
const fallback = typeof props.fallback === 'function'
|
|
1054
|
-
? props.fallback()
|
|
1055
|
-
: props.fallback;
|
|
1056
|
-
if (fallback instanceof HTMLElement) {
|
|
1057
|
-
fallbackNode = fallback;
|
|
1058
|
-
anchor.parentNode.insertBefore(fallbackNode, anchor.nextSibling);
|
|
1059
|
-
}
|
|
1060
|
-
}
|
|
1061
|
-
catch (error) {
|
|
1062
|
-
console.error('[Suspense] Fallback error:', error);
|
|
1063
|
-
}
|
|
1064
|
-
};
|
|
1065
|
-
const hideFallback = () => {
|
|
1066
|
-
if (fallbackNode?.parentNode) {
|
|
1067
|
-
fallbackNode.parentNode.removeChild(fallbackNode);
|
|
1068
|
-
fallbackNode = null;
|
|
1069
|
-
}
|
|
1070
|
-
};
|
|
1071
|
-
const render = async () => {
|
|
1072
|
-
if (isLoading)
|
|
1073
|
-
return;
|
|
1074
|
-
isLoading = true;
|
|
1075
|
-
try {
|
|
1076
|
-
const result = props.children();
|
|
1077
|
-
if (result instanceof Promise) {
|
|
1078
|
-
showFallback();
|
|
1079
|
-
const element = await result;
|
|
1080
|
-
if (anchor.parentNode) {
|
|
1081
|
-
cleanupNodes();
|
|
1082
|
-
anchor.parentNode.insertBefore(element, anchor.nextSibling);
|
|
1083
|
-
currentNode = element;
|
|
1084
|
-
}
|
|
1085
|
-
}
|
|
1086
|
-
else {
|
|
1087
|
-
if (anchor.parentNode) {
|
|
1088
|
-
cleanupNodes();
|
|
1089
|
-
anchor.parentNode.insertBefore(result, anchor.nextSibling);
|
|
1090
|
-
currentNode = result;
|
|
1091
|
-
}
|
|
1092
|
-
}
|
|
1093
|
-
}
|
|
1094
|
-
catch (error) {
|
|
1095
|
-
hideFallback();
|
|
1096
|
-
console.error('[Suspense] Children error:', error);
|
|
1097
|
-
// Show error in place of content
|
|
1098
|
-
if (anchor.parentNode) {
|
|
1099
|
-
const errorElement = document.createElement('div');
|
|
1100
|
-
errorElement.className = 'suspense-error';
|
|
1101
|
-
errorElement.textContent = error instanceof Error ? error.message : 'Failed to load';
|
|
1102
|
-
cleanupNodes();
|
|
1103
|
-
anchor.parentNode.insertBefore(errorElement, anchor.nextSibling);
|
|
1104
|
-
currentNode = errorElement;
|
|
1105
|
-
}
|
|
1106
|
-
}
|
|
1107
|
-
finally {
|
|
1108
|
-
isLoading = false;
|
|
1109
|
-
}
|
|
1110
|
-
};
|
|
1111
|
-
queueMicrotask(render);
|
|
1112
|
-
return anchor;
|
|
1113
|
-
}
|
|
1114
|
-
// ============================================================================
|
|
1115
|
-
// UTILITY FUNCTIONS
|
|
1116
|
-
// ============================================================================
|
|
1117
|
-
/**
|
|
1118
|
-
* Creates a lazy-loaded component
|
|
1119
|
-
*/
|
|
1120
|
-
export function lazy(importFn) {
|
|
1121
|
-
return importFn;
|
|
1122
|
-
}
|
|
1123
|
-
/**
|
|
1124
|
-
* Preloads a route component
|
|
1125
|
-
*/
|
|
1126
|
-
export async function preloadRoute(to) {
|
|
1127
|
-
if (!globalRouter)
|
|
1128
|
-
throw new Error('Router not initialized. Call createRouter() first.');
|
|
1129
|
-
const path = globalRouter['resolvePath'](to);
|
|
1130
|
-
const match = globalRouter['matcher'].match(path.split('?')[0].split('#')[0]);
|
|
1131
|
-
if (match && 'component' in match.route) {
|
|
1132
|
-
try {
|
|
1133
|
-
await globalRouter.loadComponent(match.route, path);
|
|
1134
|
-
}
|
|
1135
|
-
catch (error) {
|
|
1136
|
-
console.warn('[Router] Preload failed:', error);
|
|
1137
|
-
}
|
|
1138
|
-
}
|
|
1139
|
-
}
|
|
1140
|
-
/**
|
|
1141
|
-
* Validates if a route exists
|
|
1142
|
-
*/
|
|
1143
|
-
export function hasRoute(name) {
|
|
1144
|
-
if (!globalRouter)
|
|
1145
|
-
return false;
|
|
1146
|
-
return globalRouter['matcher'].findByName(name) !== null;
|
|
1147
|
-
}
|
|
1148
|
-
/**
|
|
1149
|
-
* Gets route information by name
|
|
1150
|
-
*/
|
|
1151
|
-
export function getRouteInfo(name) {
|
|
1152
|
-
if (!globalRouter)
|
|
1153
|
-
return null;
|
|
1154
|
-
return globalRouter['matcher'].findByName(name);
|
|
1155
|
-
}
|
|
1156
|
-
/**
|
|
1157
|
-
* Builds a URL for a route
|
|
1158
|
-
*/
|
|
1159
|
-
export function buildURL(to) {
|
|
1160
|
-
if (!globalRouter)
|
|
1161
|
-
throw new Error('Router not initialized. Call createRouter() first.');
|
|
1162
|
-
return globalRouter['resolvePath'](to);
|
|
1163
|
-
}
|
|
1164
|
-
// ============================================================================
|
|
1165
|
-
// CLEANUP
|
|
1166
|
-
// ============================================================================
|
|
1167
|
-
export function destroyRouter() {
|
|
1168
|
-
if (globalRouter) {
|
|
1169
|
-
globalRouter.destroy();
|
|
1170
|
-
globalRouter = null;
|
|
1171
|
-
}
|
|
1172
|
-
}
|
|
1173
|
-
// Cleanup on page unload
|
|
1174
|
-
if (typeof window !== 'undefined') {
|
|
1175
|
-
window.addEventListener('beforeunload', () => {
|
|
1176
|
-
destroyRouter();
|
|
1177
|
-
});
|
|
1178
|
-
}
|