@maycombhq/atticus 2.0.0-beta.2 → 2.0.0-beta.22
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/bin.js +8 -4
- package/dist/chunk-23XVSEV5.js +49 -0
- package/dist/{chunk-ZDPAS56H.js → chunk-24OXGB3P.js} +8 -2
- package/dist/{chunk-7EOBHUMC.js → chunk-24XLDU72.js} +4 -3
- package/dist/{chunk-CR3MTH32.js → chunk-2F4CYZKJ.js} +86 -11
- package/dist/{chunk-6IU67AEK.js → chunk-2GF7OLF3.js} +23 -15
- package/dist/chunk-2IIMFOPM.js +41 -0
- package/dist/chunk-2LGRMVCA.js +617 -0
- package/dist/{chunk-XPCM366L.js → chunk-2MMCFP5C.js} +10 -8
- package/dist/{chunk-JBTK5OP2.js → chunk-2Q56PQPS.js} +6 -4
- package/dist/{chunk-Z6LF7HQN.js → chunk-2Q5EDGEO.js} +7 -5
- package/dist/{chunk-3USYZGRF.js → chunk-2RLKFUUL.js} +38 -27
- package/dist/chunk-2SKB4O2E.js +59 -0
- package/dist/{chunk-SP2I63OO.js → chunk-34S2WGOB.js} +87 -4
- package/dist/{chunk-GQ4N4WKF.js → chunk-3NFFJL5D.js} +33 -27
- package/dist/{chunk-SJRGMG6A.js → chunk-3NWDLGG6.js} +28 -25
- package/dist/{chunk-4WBVUHXN.js → chunk-3RM4UK6W.js} +20 -18
- package/dist/chunk-42SIDKEU.js +583 -0
- package/dist/{chunk-GLUX4EEP.js → chunk-4EURQWQD.js} +20 -15
- package/dist/chunk-4U276ICE.js +39 -0
- package/dist/{chunk-ZMFPW66A.js → chunk-53T4O55J.js} +4 -3
- package/dist/{chunk-6J5OKA4Y.js → chunk-57TDNMI3.js} +17 -15
- package/dist/chunk-5BD2KYLE.js +57 -0
- package/dist/{chunk-7EFILTCI.js → chunk-5F7GFN2F.js} +28 -0
- package/dist/chunk-5NMKONLP.js +95 -0
- package/dist/{chunk-7ZUIMTOU.js → chunk-5SJZW52K.js} +26 -24
- package/dist/{chunk-4RS5KZQD.js → chunk-63YRMR6J.js} +6 -5
- package/dist/chunk-66PPNOON.js +62 -0
- package/dist/chunk-6H3Z5JHH.js +64 -0
- package/dist/{chunk-CEXSU4S6.js → chunk-6MUGR4LS.js} +775 -89
- package/dist/{chunk-QSXK2RGH.js → chunk-6OJQOPUN.js} +5 -3
- package/dist/{chunk-MVZVGYXH.js → chunk-6PPOWY5E.js} +84 -14
- package/dist/{chunk-7EKXJZ2Y.js → chunk-6ZGLAACW.js} +299 -38
- package/dist/chunk-6ZJ5A7AF.js +222 -0
- package/dist/{chunk-L24C55VN.js → chunk-745DAZ4L.js} +142 -30
- package/dist/{chunk-Q4D5X7QB.js → chunk-7ASNL4Q4.js} +46 -24
- package/dist/chunk-7BR7IPOH.js +35 -0
- package/dist/{chunk-7WD63HU2.js → chunk-7DOR27HL.js} +6 -6
- package/dist/chunk-7FTTA3HD.js +341 -0
- package/dist/{chunk-OLQJTJJU.js → chunk-7GJS6DNX.js} +12 -3
- package/dist/{chunk-7JUKDRSZ.js → chunk-7IGKHZ4Z.js} +347 -226
- package/dist/{chunk-G2B4ZQGH.js → chunk-7IOWA5D5.js} +11 -6
- package/dist/{chunk-EBEW3XXQ.js → chunk-7JITZFJC.js} +7 -5
- package/dist/chunk-7MJSQTL5.js +174 -0
- package/dist/{chunk-I4JRCD6M.js → chunk-7UOMVMFG.js} +25 -17
- package/dist/{chunk-YY6ZQQBP.js → chunk-AFWUDXEM.js} +19 -7
- package/dist/chunk-AFZ3QA6W.js +47 -0
- package/dist/chunk-AJQ72EBM.js +420 -0
- package/dist/chunk-AJYXNTNZ.js +317 -0
- package/dist/chunk-AVTBS4OW.js +49 -0
- package/dist/{chunk-FHH4I4ZV.js → chunk-BDKC6H5P.js} +12 -6
- package/dist/{chunk-IPKHE5D6.js → chunk-BEVTIBRH.js} +1526 -875
- package/dist/chunk-BHT7QK4G.js +162 -0
- package/dist/{chunk-6JUBZBMW.js → chunk-BIGDQDHD.js} +6 -4
- package/dist/{chunk-2BFCZ3RK.js → chunk-BO4KX37X.js} +20 -18
- package/dist/chunk-BPK5P77O.js +115 -0
- package/dist/chunk-BQ4SQQC6.js +59 -0
- package/dist/chunk-BQGDB6ZL.js +7342 -0
- package/dist/{chunk-2ANOROX3.js → chunk-BTGPE6CR.js} +11 -9
- package/dist/chunk-BTY3HZBO.js +93 -0
- package/dist/{chunk-DPZHZQKE.js → chunk-BXYMYEJ4.js} +44 -10
- package/dist/{chunk-2RMYG36Q.js → chunk-C3RY6KU6.js} +309 -346
- package/dist/chunk-C4R4JLHE.js +40 -0
- package/dist/chunk-CADWLE2T.js +18 -0
- package/dist/chunk-CBGGOQWU.js +82 -0
- package/dist/chunk-CMDXGQTS.js +47 -0
- package/dist/{chunk-MC3H7736.js → chunk-CQDGOPGH.js} +58 -18
- package/dist/{chunk-HFQCFSJR.js → chunk-CX4RZ2KS.js} +4 -3
- package/dist/{chunk-PAMPPUR6.js → chunk-CXJ6FUKN.js} +1852 -207
- package/dist/{chunk-ABWHYWNI.js → chunk-D73UEXYW.js} +3 -3
- package/dist/chunk-DCZWQLYT.js +1205 -0
- package/dist/chunk-DLOBCJL6.js +48 -0
- package/dist/{chunk-4BN53TG3.js → chunk-DOBQKHIF.js} +31 -23
- package/dist/chunk-E3ISYXO3.js +423 -0
- package/dist/{chunk-7SW2QWLU.js → chunk-E6ZIEVOI.js} +16 -13
- package/dist/chunk-EBL2R3YG.js +43 -0
- package/dist/{chunk-AIOQPLA5.js → chunk-EGQRREQH.js} +41 -15
- package/dist/chunk-EHA7RMZ3.js +58 -0
- package/dist/{chunk-M766ZJ6W.js → chunk-EIPTLPFC.js} +38 -27
- package/dist/chunk-EMRIQUND.js +517 -0
- package/dist/{chunk-DFBCSHVT.js → chunk-EPCJQQWD.js} +6 -4
- package/dist/{chunk-RZPYKDWS.js → chunk-F3P7N7SF.js} +1 -1
- package/dist/{chunk-R77KLRB3.js → chunk-FFLNS6VP.js} +21 -18
- package/dist/chunk-FFTRTL6R.js +26 -0
- package/dist/{chunk-IHMLFRVV.js → chunk-FPJPLKDW.js} +62 -16
- package/dist/{chunk-JCZIKA4C.js → chunk-FSMSFDLD.js} +5 -4
- package/dist/{chunk-AVJDSJ2F.js → chunk-FUJM6RTF.js} +8 -7
- package/dist/chunk-FZOJFH7Q.js +132 -0
- package/dist/{chunk-QRAOANID.js → chunk-G6X77UJZ.js} +23 -21
- package/dist/{chunk-3ALD2W2A.js → chunk-GCWRWSU7.js} +10 -4
- package/dist/chunk-GIW4ZVDB.js +39 -0
- package/dist/{chunk-B2WLAWNJ.js → chunk-GUX3PTYM.js} +3 -3
- package/dist/{chunk-DPUYDDQG.js → chunk-GZY6RUZ6.js} +25 -3
- package/dist/chunk-H4VO6WG3.js +10 -0
- package/dist/{chunk-MMOSZPWJ.js → chunk-H5HBGNXU.js} +5 -4
- package/dist/chunk-H7KM7LZE.js +229 -0
- package/dist/{chunk-AA6YULYB.js → chunk-HNM2YQT2.js} +7 -7
- package/dist/{chunk-3C7EYRFZ.js → chunk-HYU5VLGA.js} +1 -1
- package/dist/{chunk-XY3P7JW7.js → chunk-IDKCWSM5.js} +1032 -172
- package/dist/chunk-IFKEI4SA.js +237 -0
- package/dist/{chunk-F7HYOUFF.js → chunk-IGOE6KPG.js} +67 -22
- package/dist/{chunk-TDD55CJH.js → chunk-IH2XURVQ.js} +6 -5
- package/dist/chunk-INQHESFB.js +38 -0
- package/dist/chunk-IP74LCOQ.js +57 -0
- package/dist/chunk-IPHHDDC6.js +45 -0
- package/dist/{chunk-T6AIP7H5.js → chunk-ISUAAMGS.js} +110 -153
- package/dist/{chunk-A7UPUQFY.js → chunk-IUPG3UKI.js} +624 -319
- package/dist/{chunk-L6SGQIR3.js → chunk-IVBOQAGB.js} +7 -3
- package/dist/{chunk-27NKUS3X.js → chunk-J2JAKCIW.js} +2205 -796
- package/dist/{chunk-V2YTAIQO.js → chunk-J3IOLXWM.js} +24 -7
- package/dist/chunk-JA5FI3QV.js +142 -0
- package/dist/chunk-JAVI7JSP.js +49 -0
- package/dist/{chunk-HLJAXMM4.js → chunk-JB5LINEM.js} +6 -2
- package/dist/{chunk-2VHF7OXM.js → chunk-JENSEMXF.js} +1 -1
- package/dist/{chunk-6BVONWJ7.js → chunk-JJF6HALY.js} +4 -3
- package/dist/chunk-JLUCKAEL.js +38 -0
- package/dist/chunk-JRBFANFE.js +221 -0
- package/dist/{chunk-R6XF6UR3.js → chunk-KDNTAPLX.js} +4 -4
- package/dist/chunk-KFAVQZSX.js +33 -0
- package/dist/chunk-KJXD4PNL.js +50 -0
- package/dist/chunk-KN5BXMWN.js +44 -0
- package/dist/{chunk-PCVDV3UZ.js → chunk-KQGURXFU.js} +9 -7
- package/dist/{chunk-WY6PP6AA.js → chunk-KXITGYLF.js} +40 -6
- package/dist/{chunk-66YVAGGM.js → chunk-L27SV425.js} +22 -20
- package/dist/{chunk-NE2W3OO7.js → chunk-LDLL7LB3.js} +24 -22
- package/dist/chunk-LFGTYX5U.js +379 -0
- package/dist/{chunk-EYWIDJFU.js → chunk-LGBMTVNT.js} +7 -5
- package/dist/{chunk-Q5AMFEWD.js → chunk-LGFL54FP.js} +3 -2
- package/dist/{chunk-6TACQN3N.js → chunk-LJXMYKZJ.js} +7 -5
- package/dist/{chunk-BKVWTZ6A.js → chunk-LKOR5UET.js} +29 -26
- package/dist/chunk-LNBQOP7D.js +294 -0
- package/dist/{chunk-O3WI5KWW.js → chunk-LO6P2VYG.js} +2 -2
- package/dist/chunk-LQJPN536.js +122 -0
- package/dist/{chunk-YEDDOYSC.js → chunk-LYBNHMVZ.js} +29 -5
- package/dist/{chunk-A3GY6SOA.js → chunk-LZ7NUC62.js} +20 -18
- package/dist/chunk-M32BLWDH.js +37 -0
- package/dist/{chunk-VYDBJ73M.js → chunk-M3NANSDZ.js} +17 -15
- package/dist/chunk-MFHL5URL.js +31 -0
- package/dist/chunk-MFR5OPBD.js +109 -0
- package/dist/{chunk-DE76JROX.js → chunk-MG4ZL5RW.js} +32 -20
- package/dist/{chunk-N7IXEHK3.js → chunk-MO6P2JZH.js} +10 -8
- package/dist/{chunk-65NYPGRC.js → chunk-MUWDB4R4.js} +1 -1
- package/dist/chunk-MZCGSXVR.js +34 -0
- package/dist/{chunk-MGYZM27B.js → chunk-N3XIYPU3.js} +51 -23
- package/dist/chunk-NDCF4R6S.js +172 -0
- package/dist/{chunk-DHPACDG7.js → chunk-NR2UJWOC.js} +10 -8
- package/dist/chunk-NXJ5URP6.js +51 -0
- package/dist/{chunk-UNM6ZOKP.js → chunk-NY2XSR5K.js} +147 -18
- package/dist/{chunk-5GDRCDQB.js → chunk-O45HFTFS.js} +3 -2
- package/dist/{chunk-ONLS5QA5.js → chunk-O4GXGGBX.js} +14 -12
- package/dist/chunk-O5PXQR67.js +249 -0
- package/dist/{chunk-PRPPGYZG.js → chunk-O66DM6DJ.js} +6 -4
- package/dist/{chunk-KEM4SDDP.js → chunk-O7FDLW7L.js} +11 -8
- package/dist/{chunk-T2UYZSCQ.js → chunk-O7XLYEFA.js} +169 -29
- package/dist/{chunk-FITWGOJ2.js → chunk-OHNCWBKF.js} +4 -4
- package/dist/{chunk-652QWTLZ.js → chunk-OHVTTOPS.js} +3 -3
- package/dist/chunk-OMI6FPPN.js +43 -0
- package/dist/chunk-ONUPY3H6.js +53 -0
- package/dist/chunk-OTLDSZDV.js +69 -0
- package/dist/chunk-OUBGGKDU.js +22 -0
- package/dist/chunk-OWNCM4ZN.js +330 -0
- package/dist/{chunk-YISXLPHN.js → chunk-P2V7QW4D.js} +235 -35
- package/dist/{chunk-CNUKCS2T.js → chunk-P32RUW3B.js} +1632 -93
- package/dist/{chunk-BWZ77RQT.js → chunk-PAQ2AS5E.js} +1 -1
- package/dist/{chunk-ZMAINWBW.js → chunk-PBPBMP2B.js} +4 -3
- package/dist/{chunk-ZVM6AKKM.js → chunk-PJSJPMJI.js} +3 -3
- package/dist/{chunk-EFHQ3TWP.js → chunk-PKBHS4AA.js} +233 -56
- package/dist/{chunk-GT4I4ZWM.js → chunk-PMUAJGLI.js} +20 -16
- package/dist/chunk-PPD7HTDP.js +152 -0
- package/dist/chunk-PSOFBQWB.js +42 -0
- package/dist/{chunk-S2A7YC3Q.js → chunk-Q2TECDIT.js} +10694 -8896
- package/dist/chunk-Q4K5WB2W.js +42 -0
- package/dist/{chunk-UGLN7QHV.js → chunk-Q6DFNVRJ.js} +14 -12
- package/dist/chunk-QA4O2GGC.js +265 -0
- package/dist/chunk-QAXW2N3B.js +1216 -0
- package/dist/chunk-QBNWF5ZU.js +19 -0
- package/dist/chunk-QFWA2DPH.js +224 -0
- package/dist/{chunk-23W7VWKR.js → chunk-QKUJMING.js} +1363 -114
- package/dist/{chunk-NXFPS2LR.js → chunk-R44CYFPV.js} +16 -13
- package/dist/{chunk-CKSITVS4.js → chunk-R7NYHMUU.js} +2687 -786
- package/dist/chunk-REWWDVQB.js +162 -0
- package/dist/{chunk-JMSCMUCA.js → chunk-RGERPOIV.js} +145 -21
- package/dist/{chunk-6JCPI55B.js → chunk-RLDGWLQ7.js} +49 -7
- package/dist/chunk-RUGXBPWI.js +384 -0
- package/dist/{chunk-CPA6HPVR.js → chunk-RXPJMEOW.js} +18 -12
- package/dist/{chunk-3HI6Z55Y.js → chunk-S4PMJU6H.js} +339 -43
- package/dist/chunk-S5LSDXV4.js +40 -0
- package/dist/chunk-S7FCV7S5.js +51 -0
- package/dist/{chunk-5GGOLAKC.js → chunk-SDKLKB6I.js} +28 -26
- package/dist/chunk-SFSPGFCV.js +62 -0
- package/dist/{chunk-ZNCEKR4J.js → chunk-SFUP7TTO.js} +12 -8
- package/dist/{chunk-64EQYWCA.js → chunk-SLXZOMA6.js} +69 -19
- package/dist/chunk-SQV37XO2.js +273 -0
- package/dist/{chunk-K65BJIMT.js → chunk-SU74NDMQ.js} +19 -17
- package/dist/chunk-SYV5LHML.js +165 -0
- package/dist/chunk-T4YPZIQN.js +36 -0
- package/dist/chunk-T6DQR5TV.js +323 -0
- package/dist/{chunk-JIITQXFB.js → chunk-THDMSZ3R.js} +3075 -802
- package/dist/{chunk-JGZCG5VM.js → chunk-TNR75OXT.js} +7 -7
- package/dist/{chunk-D3PO4DOZ.js → chunk-TUULXQRW.js} +174 -15
- package/dist/chunk-TYQBFTBR.js +516 -0
- package/dist/chunk-U76OWS3Q.js +49 -0
- package/dist/{chunk-YYQN46FY.js → chunk-UCROWG2D.js} +27 -24
- package/dist/{chunk-6M5NK3K3.js → chunk-UF4AZOZB.js} +50 -10
- package/dist/chunk-UF4GLFBX.js +345 -0
- package/dist/{chunk-OT2O76OS.js → chunk-UGUP3ZM6.js} +240 -39
- package/dist/chunk-UNAGJYET.js +45 -0
- package/dist/{chunk-5WG5YP4T.js → chunk-UOR2DTHH.js} +16 -14
- package/dist/{chunk-S3QNRWYR.js → chunk-UPOY4YSQ.js} +14 -12
- package/dist/{chunk-EPTBZ3GP.js → chunk-UPS2DGFC.js} +1 -1
- package/dist/{chunk-4AK7IBVP.js → chunk-UUU4CLJG.js} +4 -2
- package/dist/{chunk-W3BQAZ3Q.js → chunk-UWMJKX5U.js} +14 -12
- package/dist/{chunk-QZSX3F7C.js → chunk-UWOJLHFT.js} +27 -23
- package/dist/chunk-VBTP56CE.js +535 -0
- package/dist/chunk-VCTWPLAO.js +211 -0
- package/dist/chunk-VNU26RCO.js +1215 -0
- package/dist/{chunk-CSZ4C5N6.js → chunk-VTZMAJ3W.js} +93 -10
- package/dist/{chunk-TXLH6WHQ.js → chunk-VW62D6QE.js} +14 -12
- package/dist/{chunk-CAQZUNOH.js → chunk-VY72R2XZ.js} +610 -3
- package/dist/{chunk-2DIQQJCB.js → chunk-VZALIKIF.js} +18 -15
- package/dist/{chunk-TYAID7B7.js → chunk-W62H2XSO.js} +25 -23
- package/dist/{chunk-HVUPKCGJ.js → chunk-W7VWI2YH.js} +9 -6
- package/dist/{chunk-TANFV3MZ.js → chunk-WERM6KPJ.js} +7 -4
- package/dist/{chunk-BOHJZTEQ.js → chunk-WF7B67YE.js} +208 -87
- package/dist/{chunk-7FBGJFEE.js → chunk-WG5U3F5V.js} +14 -12
- package/dist/{chunk-4NDAAFQT.js → chunk-WJF2CS6M.js} +8 -5
- package/dist/{chunk-XAKWMCXI.js → chunk-WKUJ3KPV.js} +4 -4
- package/dist/chunk-WMTRHUI5.js +103 -0
- package/dist/chunk-WNKPRSGI.js +36 -0
- package/dist/chunk-WXFLMQSR.js +223 -0
- package/dist/{chunk-RRTKKSLQ.js → chunk-X3WY6MN3.js} +8 -6
- package/dist/{chunk-ZCJDOK2S.js → chunk-X4CXA2IL.js} +47 -23
- package/dist/{chunk-UBBC2XSG.js → chunk-X5F3Q5LV.js} +560 -105
- package/dist/{chunk-C6EKFH7J.js → chunk-XD75VZIH.js} +9 -7
- package/dist/chunk-XERYFDB5.js +297 -0
- package/dist/chunk-XI2LCCYQ.js +420 -0
- package/dist/chunk-Y2EQNWKV.js +933 -0
- package/dist/{chunk-LR3R6Z2A.js → chunk-YHCMZC2J.js} +7 -7
- package/dist/chunk-YYY3AZFE.js +122 -0
- package/dist/{chunk-GDLULHTC.js → chunk-Z5IA3WDR.js} +19 -31
- package/dist/{chunk-T6XQL73F.js → chunk-ZFFYNRIO.js} +63 -23
- package/dist/{chunk-EDS2L2LD.js → chunk-ZFL7NY4F.js} +56 -27
- package/dist/{chunk-YMZ3TNYB.js → chunk-ZGULQAPZ.js} +7 -4
- package/dist/{chunk-4SKZDI62.js → chunk-ZIUFM5HT.js} +3 -1
- package/dist/{chunk-DKJA76CJ.js → chunk-ZJFRMSJT.js} +15 -13
- package/dist/chunk-ZJXLNDH4.js +239 -0
- package/dist/chunk-ZQ3ON2VQ.js +252 -0
- package/dist/{chunk-HITVBIJS.js → chunk-ZQ7SKESZ.js} +150 -415
- package/dist/{chunk-6QSERWXH.js → chunk-ZQXZ2SES.js} +24 -21
- package/dist/chunk-ZX57KSEQ.js +39 -0
- package/dist/{chunk-I3E7CX4H.js → chunk-ZZK56BLP.js} +5 -3
- package/dist/files.json +664 -368
- package/dist/packages/naysayer/constitution/constitution.yaml +19 -0
- package/dist/packages/sdk/npm/index.js +6194 -2542
- package/dist/packages/ui/dist/assets/{elk-api-tma0MCuh.js → elk-api-CIf-0gz5.js} +2 -2
- package/dist/packages/ui/dist/assets/index-CoK_Dl0D.js +47 -0
- package/dist/packages/ui/dist/assets/index-Dw20IRhQ.css +1 -0
- package/dist/packages/ui/dist/index.html +3 -2
- package/package.json +5 -5
- package/dist/chunk-2BF2EYJV.js +0 -174
- package/dist/chunk-42EVSKME.js +0 -37
- package/dist/chunk-4BKGMUIK.js +0 -53
- package/dist/chunk-5KLGBXG5.js +0 -48
- package/dist/chunk-6BNC6EQ4.js +0 -41
- package/dist/chunk-6D2CQ5II.js +0 -41
- package/dist/chunk-7AVHLZGD.js +0 -45
- package/dist/chunk-D46WUV3T.js +0 -33
- package/dist/chunk-E36QOJMD.js +0 -47
- package/dist/chunk-FQXYPF7E.js +0 -147
- package/dist/chunk-GBRFVT63.js +0 -45
- package/dist/chunk-ICY36XCH.js +0 -34
- package/dist/chunk-J4K3KVLD.js +0 -131
- package/dist/chunk-JZU3H6UQ.js +0 -54
- package/dist/chunk-NN2ZQEB4.js +0 -38
- package/dist/chunk-PCOCACY3.js +0 -385
- package/dist/chunk-RTEYOSJY.js +0 -29
- package/dist/chunk-RUVFXE2L.js +0 -666
- package/dist/chunk-RVH5SUW5.js +0 -69
- package/dist/chunk-VN3DP4LQ.js +0 -43
- package/dist/chunk-W5BVQF44.js +0 -36
- package/dist/chunk-WVPJSRRJ.js +0 -52
- package/dist/chunk-XPTOBS2D.js +0 -48
- package/dist/packages/ui/dist/assets/index-B54fm9FQ.js +0 -29
- package/dist/packages/ui/dist/assets/index-DD_hufQz.css +0 -1
|
@@ -3,30 +3,57 @@ import { fileURLToPath as __atticusFileURLToPath } from "node:url";
|
|
|
3
3
|
var require = __atticusCreateRequire(import.meta.url);
|
|
4
4
|
var __atticusUrl = (p) => new URL("./" + p, import.meta.url).href;
|
|
5
5
|
var __atticusPath = (p) => __atticusFileURLToPath(__atticusUrl(p));
|
|
6
|
+
import {
|
|
7
|
+
readOpenApiDocument
|
|
8
|
+
} from "./chunk-CQDGOPGH.js";
|
|
6
9
|
import {
|
|
7
10
|
globToRegExp
|
|
8
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-3NFFJL5D.js";
|
|
9
12
|
import {
|
|
10
|
-
|
|
13
|
+
DECLARED_TOPIC,
|
|
14
|
+
DEFAULT_CONVENTIONS,
|
|
11
15
|
Fact,
|
|
16
|
+
FactId,
|
|
17
|
+
JOINED_BY_AI,
|
|
18
|
+
JOINED_BY_PERSON,
|
|
19
|
+
JOINED_TO_SHARED_MAP,
|
|
20
|
+
JOIN_HOW_FLAGS,
|
|
21
|
+
LINK_EVIDENCE_NOT_GAPS,
|
|
12
22
|
MOCK_FILE,
|
|
13
23
|
NEVER_CHECKED_STEPS,
|
|
24
|
+
SHARED_MAP_EVIDENCE,
|
|
14
25
|
citedFiles,
|
|
15
26
|
claimOnLines,
|
|
27
|
+
clusterServiceAddress,
|
|
28
|
+
decodeText,
|
|
29
|
+
factIdPart,
|
|
30
|
+
forwardSlashes,
|
|
31
|
+
gatewayBundleTest,
|
|
32
|
+
isBinaryText,
|
|
33
|
+
isLfsPointer,
|
|
34
|
+
isPatternPath,
|
|
16
35
|
isSecretPath,
|
|
17
36
|
lineNamesClaim,
|
|
37
|
+
linksFor,
|
|
38
|
+
listRepoFilesSync,
|
|
18
39
|
normalizePath,
|
|
40
|
+
notDeviceName,
|
|
41
|
+
noteProblem,
|
|
19
42
|
partsBuildWord,
|
|
20
43
|
partsNamePath,
|
|
44
|
+
plainPathPrefix,
|
|
45
|
+
quoteHoldsSecret,
|
|
21
46
|
quoteMatches,
|
|
22
47
|
quoteNotRequired,
|
|
23
48
|
redactSecrets,
|
|
24
49
|
staleCite,
|
|
25
|
-
toCanonicalYaml
|
|
26
|
-
|
|
50
|
+
toCanonicalYaml,
|
|
51
|
+
tokenUnits,
|
|
52
|
+
underFolders
|
|
53
|
+
} from "./chunk-Q2TECDIT.js";
|
|
27
54
|
|
|
28
55
|
// packages/verify/dist/source.js
|
|
29
|
-
import {
|
|
56
|
+
import { lstatSync, readFileSync, realpathSync, statSync } from "node:fs";
|
|
30
57
|
import { isAbsolute, join, normalize, relative, sep } from "node:path";
|
|
31
58
|
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
32
59
|
".git",
|
|
@@ -38,6 +65,13 @@ var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
|
38
65
|
".vscode",
|
|
39
66
|
".gradle",
|
|
40
67
|
".mvn",
|
|
68
|
+
"__pycache__",
|
|
69
|
+
".venv",
|
|
70
|
+
"venv",
|
|
71
|
+
".next",
|
|
72
|
+
".nuxt"
|
|
73
|
+
]);
|
|
74
|
+
var BUILD_OUTPUT = /* @__PURE__ */ new Set([
|
|
41
75
|
"target",
|
|
42
76
|
"build",
|
|
43
77
|
"dist",
|
|
@@ -45,62 +79,92 @@ var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
|
45
79
|
"bin",
|
|
46
80
|
"obj",
|
|
47
81
|
"vendor",
|
|
48
|
-
"__pycache__",
|
|
49
|
-
".venv",
|
|
50
|
-
"venv",
|
|
51
|
-
".next",
|
|
52
|
-
".nuxt",
|
|
53
82
|
"coverage"
|
|
54
83
|
]);
|
|
84
|
+
var DOTNET = /\.(csproj|fsproj|vbproj|sln)$/i;
|
|
85
|
+
var NODE = /^package\.json$/;
|
|
86
|
+
var MADE_BESIDE = {
|
|
87
|
+
bin: DOTNET,
|
|
88
|
+
obj: DOTNET,
|
|
89
|
+
vendor: /^(go\.mod|composer\.json|Gemfile)$/,
|
|
90
|
+
target: /^(pom\.xml|build\.sbt|Cargo\.toml)$/,
|
|
91
|
+
build: /^(build\.gradle(\.kts)?|settings\.gradle(\.kts)?|package\.json|CMakeLists\.txt)$/,
|
|
92
|
+
dist: NODE,
|
|
93
|
+
coverage: NODE,
|
|
94
|
+
out: /^(tsconfig\.json|package\.json)$|\.iml$/
|
|
95
|
+
};
|
|
96
|
+
var ONLY_BESIDE = /* @__PURE__ */ new Set(["bin", "obj", "vendor"]);
|
|
97
|
+
function skippedFolder(above, name, siblings) {
|
|
98
|
+
if (SKIP_DIRS.has(name))
|
|
99
|
+
return true;
|
|
100
|
+
if (!BUILD_OUTPUT.has(name))
|
|
101
|
+
return false;
|
|
102
|
+
if (siblings.some((x) => MADE_BESIDE[name].test(x)))
|
|
103
|
+
return true;
|
|
104
|
+
if (above.some((a) => a.toLowerCase() === "src"))
|
|
105
|
+
return false;
|
|
106
|
+
return !ONLY_BESIDE.has(name);
|
|
107
|
+
}
|
|
55
108
|
function insideRepo(path) {
|
|
56
|
-
|
|
109
|
+
const p = path.replace(/\\/g, "/");
|
|
110
|
+
if (p === "" || p.includes(":") || isAbsolute(p) || p.startsWith("/"))
|
|
57
111
|
return false;
|
|
58
|
-
|
|
112
|
+
if (p.split("/").some((x) => x !== "." && x !== ".." && /[. ]$/.test(x)))
|
|
113
|
+
return false;
|
|
114
|
+
const n = normalize(p).split(sep).join("/");
|
|
59
115
|
return n !== ".." && !n.startsWith("../");
|
|
60
116
|
}
|
|
61
117
|
function dirSource(root) {
|
|
62
118
|
let cache;
|
|
63
|
-
|
|
64
|
-
|
|
119
|
+
let realRoot;
|
|
120
|
+
const fileAt = (path) => {
|
|
121
|
+
if (!insideRepo(path))
|
|
122
|
+
return void 0;
|
|
123
|
+
const full = join(root, ...path.split("/"));
|
|
65
124
|
try {
|
|
66
|
-
|
|
125
|
+
if (!lstatSync(full).isFile())
|
|
126
|
+
return void 0;
|
|
127
|
+
realRoot ??= realpathSync(root);
|
|
128
|
+
const r = relative(realRoot, realpathSync(full));
|
|
129
|
+
if (!r || r.startsWith("..") || isAbsolute(r))
|
|
130
|
+
return void 0;
|
|
131
|
+
return full;
|
|
67
132
|
} catch {
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
for (const name of names) {
|
|
71
|
-
const full = join(dir, name);
|
|
72
|
-
let st;
|
|
73
|
-
try {
|
|
74
|
-
st = statSync(full);
|
|
75
|
-
} catch {
|
|
76
|
-
continue;
|
|
77
|
-
}
|
|
78
|
-
if (st.isDirectory()) {
|
|
79
|
-
if (!SKIP_DIRS.has(name))
|
|
80
|
-
walk(full, out);
|
|
81
|
-
} else if (st.isFile())
|
|
82
|
-
out.push(relative(root, full).split(sep).join("/"));
|
|
133
|
+
return void 0;
|
|
83
134
|
}
|
|
84
135
|
};
|
|
85
136
|
return {
|
|
86
137
|
list() {
|
|
87
|
-
|
|
88
|
-
cache = [];
|
|
89
|
-
walk(root, cache);
|
|
90
|
-
}
|
|
138
|
+
cache ??= listRepoFilesSync(root, { skipFolder: skippedFolder });
|
|
91
139
|
return cache;
|
|
92
140
|
},
|
|
93
141
|
read(path) {
|
|
94
|
-
|
|
142
|
+
const full = fileAt(path);
|
|
143
|
+
if (!full)
|
|
144
|
+
return void 0;
|
|
145
|
+
try {
|
|
146
|
+
return decodeText(readFileSync(full));
|
|
147
|
+
} catch {
|
|
148
|
+
return void 0;
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
size(path) {
|
|
152
|
+
const full = fileAt(path);
|
|
153
|
+
if (!full)
|
|
95
154
|
return void 0;
|
|
96
155
|
try {
|
|
97
|
-
return
|
|
156
|
+
return statSync(full).size;
|
|
98
157
|
} catch {
|
|
99
158
|
return void 0;
|
|
100
159
|
}
|
|
101
160
|
}
|
|
102
161
|
};
|
|
103
162
|
}
|
|
163
|
+
var MAX_SOURCE_BYTES = 5 * 1024 * 1024;
|
|
164
|
+
function tooBigToRead(source, path) {
|
|
165
|
+
const n = source.size?.(path);
|
|
166
|
+
return n !== void 0 && n > MAX_SOURCE_BYTES;
|
|
167
|
+
}
|
|
104
168
|
|
|
105
169
|
// packages/verify/dist/frameworks.js
|
|
106
170
|
var FRAMEWORKS = [
|
|
@@ -121,6 +185,11 @@ var FRAMEWORKS = [
|
|
|
121
185
|
var KNOWN = [
|
|
122
186
|
// Java and Kotlin
|
|
123
187
|
["spring-boot*", { name: "spring-boot", topic: "spring", shape: "rest" }],
|
|
188
|
+
// the annotations a Spring Boot app's main class carries, which a reader may cite instead
|
|
189
|
+
// (matched by `annotationKey`, so bare, qualified or with arguments)
|
|
190
|
+
["@springbootapplication", { name: "spring-boot", topic: "spring", shape: "rest" }],
|
|
191
|
+
["@enableautoconfiguration", { name: "spring-boot", topic: "spring", shape: "rest" }],
|
|
192
|
+
["@quarkusmain", { name: "quarkus", topic: "quarkus" }],
|
|
124
193
|
["spring-webmvc", { name: "spring", topic: "spring", shape: "rest" }],
|
|
125
194
|
["spring-webflux", { name: "spring", topic: "spring", shape: "rest" }],
|
|
126
195
|
["spring-web", { name: "spring", topic: "spring", shape: "rest" }],
|
|
@@ -172,23 +241,45 @@ function declarationKey(declared) {
|
|
|
172
241
|
const name = colon.split(/[=<>~!,\s[]/)[0] ?? colon;
|
|
173
242
|
return name.replace(/^['"]|['"]$/g, "");
|
|
174
243
|
}
|
|
175
|
-
function
|
|
244
|
+
function annotationKey(declared) {
|
|
245
|
+
const m = /^@?(?:[a-z_$][\w$]*\.)*([A-Z][\w$]*)\s*(?:\(.*)?$/s.exec(declared.trim());
|
|
246
|
+
return m ? `@${m[1].toLowerCase()}` : void 0;
|
|
247
|
+
}
|
|
248
|
+
function houseKnown(extra) {
|
|
249
|
+
return extra.map((e) => [
|
|
250
|
+
e.declares.toLowerCase(),
|
|
251
|
+
{
|
|
252
|
+
name: e.name,
|
|
253
|
+
...e.topic && FRAMEWORKS.includes(e.topic) ? { topic: e.topic } : {},
|
|
254
|
+
...e.shape ? { shape: e.shape } : {}
|
|
255
|
+
}
|
|
256
|
+
]);
|
|
257
|
+
}
|
|
258
|
+
function frameworkOf(declared, extra = []) {
|
|
176
259
|
const key = declarationKey(declared);
|
|
177
260
|
if (!key)
|
|
178
261
|
return void 0;
|
|
179
|
-
|
|
262
|
+
const table = extra.length ? [...houseKnown(extra), ...KNOWN] : KNOWN;
|
|
263
|
+
for (const [pattern, known] of table) {
|
|
180
264
|
const star = pattern.endsWith("*");
|
|
181
265
|
const p = star ? pattern.slice(0, -1) : pattern;
|
|
182
266
|
if (star ? key.startsWith(p) : key === p)
|
|
183
267
|
return known;
|
|
184
268
|
}
|
|
269
|
+
const ann = annotationKey(declared);
|
|
270
|
+
if (ann) {
|
|
271
|
+
for (const [pattern, known] of table)
|
|
272
|
+
if (pattern === ann)
|
|
273
|
+
return known;
|
|
274
|
+
}
|
|
185
275
|
return void 0;
|
|
186
276
|
}
|
|
187
|
-
function frameworkNameProblem(f) {
|
|
188
|
-
const known = frameworkOf(f.cite.value);
|
|
277
|
+
function frameworkNameProblem(f, extra = []) {
|
|
278
|
+
const known = frameworkOf(f.cite.value, extra);
|
|
189
279
|
const name = f.name.trim().toLowerCase();
|
|
190
280
|
if (known) {
|
|
191
|
-
|
|
281
|
+
const legacy = annotationKey(f.cite.value) === `@${name}`;
|
|
282
|
+
return name === known.name.toLowerCase() || legacy ? void 0 : `names the framework "${f.name}", but ${f.cite.file}:${f.cite.line} declares ${f.cite.value}, which is ${known.name}`;
|
|
192
283
|
}
|
|
193
284
|
const key = declarationKey(f.cite.value);
|
|
194
285
|
if (declarationKey(name) === key || name === key)
|
|
@@ -196,13 +287,13 @@ function frameworkNameProblem(f) {
|
|
|
196
287
|
return `names the framework "${f.name}", which ${f.cite.file}:${f.cite.line} doesn't declare: it declares ${f.cite.value}, and Atticus doesn't know that one, so name it as the build file writes it (${key})`;
|
|
197
288
|
}
|
|
198
289
|
var frameworkFacts = (facts) => facts.filter((f) => f.kind === "framework").sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
|
|
199
|
-
function frameworkTopics(facts) {
|
|
290
|
+
function frameworkTopics(facts, extra = []) {
|
|
200
291
|
const fws = frameworkFacts(facts);
|
|
201
292
|
if (!fws.length)
|
|
202
293
|
return void 0;
|
|
203
294
|
const topics = /* @__PURE__ */ new Set();
|
|
204
295
|
for (const f of fws) {
|
|
205
|
-
const known = frameworkOf(f.cite.value);
|
|
296
|
+
const known = frameworkOf(f.cite.value, extra);
|
|
206
297
|
if (!known?.topic)
|
|
207
298
|
topics.add("house");
|
|
208
299
|
else
|
|
@@ -228,7 +319,7 @@ var SHAPE_ASK = {
|
|
|
228
319
|
store: "which tables it reads and writes"
|
|
229
320
|
};
|
|
230
321
|
var holdsShape = (facts, shape2) => facts.some((f) => shape2 === "store" ? f.kind === "store" : f.kind === "entry" && f.entryType === (shape2 === "rest" ? "rest" : shape2));
|
|
231
|
-
function frameworkGaps(facts) {
|
|
322
|
+
function frameworkGaps(facts, extra = []) {
|
|
232
323
|
const fws = frameworkFacts(facts);
|
|
233
324
|
if (!fws.length)
|
|
234
325
|
return [];
|
|
@@ -236,7 +327,7 @@ function frameworkGaps(facts) {
|
|
|
236
327
|
return [];
|
|
237
328
|
const out = [];
|
|
238
329
|
for (const f of fws) {
|
|
239
|
-
const shape2 = frameworkOf(f.cite.value)?.shape;
|
|
330
|
+
const shape2 = frameworkOf(f.cite.value, extra)?.shape;
|
|
240
331
|
if (!shape2 || holdsShape(facts, shape2))
|
|
241
332
|
continue;
|
|
242
333
|
out.push({
|
|
@@ -249,9 +340,14 @@ function frameworkGaps(facts) {
|
|
|
249
340
|
}
|
|
250
341
|
return out.sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
|
|
251
342
|
}
|
|
252
|
-
function
|
|
343
|
+
function isApigeeBundle(f) {
|
|
344
|
+
const file = f.cite.file.replace(/\\/g, "/").toLowerCase();
|
|
345
|
+
const said = `${f.cite.value} ${f.cite.quote ?? ""}`;
|
|
346
|
+
return /<\s*(APIProxy|SharedFlowBundle)\b/i.test(said) || /^(apiproxy|sharedflowbundle)$/i.test(f.cite.value.trim()) || /(^|\/)(apiproxy|sharedflowbundle|sharedflows)\//.test(file);
|
|
347
|
+
}
|
|
348
|
+
function frameworksToLearn(facts, known = [], extra = []) {
|
|
253
349
|
const have = new Set(known.map(declarationKey));
|
|
254
|
-
return frameworkFacts(facts).filter((f) => !frameworkOf(f.cite.value) && !have.has(declarationKey(f.name)) && !have.has(declarationKey(f.cite.value))).map((f) => ({
|
|
350
|
+
return frameworkFacts(facts).filter((f) => !isApigeeBundle(f) && !frameworkOf(f.cite.value, extra) && !have.has(declarationKey(f.name)) && !have.has(declarationKey(f.cite.value))).map((f) => ({
|
|
255
351
|
id: f.id,
|
|
256
352
|
framework: f.name,
|
|
257
353
|
cite: `${f.cite.file}:${f.cite.line}`,
|
|
@@ -260,6 +356,88 @@ function frameworksToLearn(facts, known = []) {
|
|
|
260
356
|
}));
|
|
261
357
|
}
|
|
262
358
|
|
|
359
|
+
// packages/verify/dist/reviewed.js
|
|
360
|
+
var LINE_RANGE = /^(.+)#L(\d+)-L(\d+)$/;
|
|
361
|
+
function lineRangeOf(path) {
|
|
362
|
+
const m = LINE_RANGE.exec(path);
|
|
363
|
+
return m ? { file: m[1], from: Number(m[2]), to: Number(m[3]) } : void 0;
|
|
364
|
+
}
|
|
365
|
+
var SHAPE = `{"path": "<file or glob>", "why": "..."}`;
|
|
366
|
+
function parseReviewed(text) {
|
|
367
|
+
const entries = [];
|
|
368
|
+
const bad = [];
|
|
369
|
+
text.split(/\r?\n/).forEach((raw, i) => {
|
|
370
|
+
if (!raw.trim())
|
|
371
|
+
return;
|
|
372
|
+
const line = i + 1;
|
|
373
|
+
let json2;
|
|
374
|
+
try {
|
|
375
|
+
json2 = JSON.parse(raw);
|
|
376
|
+
} catch (e) {
|
|
377
|
+
bad.push({ line, raw, message: `line ${line} is not JSON: ${e.message}` });
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
const o = json2;
|
|
381
|
+
const issues = [];
|
|
382
|
+
if (!o || typeof o !== "object" || Array.isArray(o))
|
|
383
|
+
issues.push("not an object");
|
|
384
|
+
else {
|
|
385
|
+
if (typeof o.path !== "string" || !o.path.trim())
|
|
386
|
+
issues.push("path: a file or glob");
|
|
387
|
+
if (typeof o.why !== "string" || !o.why.trim())
|
|
388
|
+
issues.push("why: why it holds nothing");
|
|
389
|
+
const extra = Object.keys(o).filter((k) => k !== "path" && k !== "why");
|
|
390
|
+
if (extra.length)
|
|
391
|
+
issues.push(`unknown keys: ${extra.join(", ")}`);
|
|
392
|
+
}
|
|
393
|
+
if (issues.length) {
|
|
394
|
+
bad.push({ line, raw, message: `line ${line} isn't ${SHAPE}: ${issues.join("; ")}` });
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
const path = forwardSlashes(o.path.trim());
|
|
398
|
+
entries.push({ line, path, why: o.why.trim() });
|
|
399
|
+
});
|
|
400
|
+
return { entries, bad };
|
|
401
|
+
}
|
|
402
|
+
var isGlob = (path) => /[*?]/.test(path);
|
|
403
|
+
|
|
404
|
+
// packages/verify/dist/conventions.js
|
|
405
|
+
var SPEC_NAME = /(^|\/)[^/]*(openapi|swagger|asyncapi|api-docs)[^/]*\.(ya?ml|json|xml)$|\.oas\.(ya?ml|json)$/i;
|
|
406
|
+
function notReadTest(folders) {
|
|
407
|
+
const multi = folders.filter((f) => f.includes("/"));
|
|
408
|
+
const multiRe = underFolders(multi);
|
|
409
|
+
const names = new Set(folders.filter((f) => !f.includes("/")).map((f) => f.toLowerCase()));
|
|
410
|
+
return {
|
|
411
|
+
test(path) {
|
|
412
|
+
if (multiRe.test(path))
|
|
413
|
+
return true;
|
|
414
|
+
const parts = path.split("/").slice(0, -1).map((x) => x.toLowerCase());
|
|
415
|
+
const src = parts.indexOf("src");
|
|
416
|
+
return parts.some((x, j) => names.has(x) && !(src >= 0 && j > src && BUILD_OUTPUT.has(x)));
|
|
417
|
+
}
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
function rulesOf(c) {
|
|
421
|
+
const folders = underFolders(c.apiSpecFolders, { tail: "(.+/)?[^/]+\\.(ya?ml|json)$" });
|
|
422
|
+
return {
|
|
423
|
+
apiSpec: new RegExp(`${SPEC_NAME.source}|${folders.source}`, "i"),
|
|
424
|
+
notProduct: underFolders(c.notProductFolders),
|
|
425
|
+
notRead: notReadTest(c.notReadFolders),
|
|
426
|
+
gatewayBundle: gatewayBundleTest(c.gatewayBundleFolders),
|
|
427
|
+
frameworks: c.frameworks
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
var DEFAULT_READ_RULES = rulesOf(DEFAULT_CONVENTIONS);
|
|
431
|
+
var made = /* @__PURE__ */ new WeakMap();
|
|
432
|
+
function readRulesOf(c) {
|
|
433
|
+
if (!c || c === DEFAULT_CONVENTIONS)
|
|
434
|
+
return DEFAULT_READ_RULES;
|
|
435
|
+
let r = made.get(c);
|
|
436
|
+
if (!r)
|
|
437
|
+
made.set(c, r = rulesOf(c));
|
|
438
|
+
return r;
|
|
439
|
+
}
|
|
440
|
+
|
|
263
441
|
// packages/verify/dist/apigee-policy.js
|
|
264
442
|
var MAKES = {
|
|
265
443
|
OAuthV1: ["oauth2"],
|
|
@@ -327,9 +505,77 @@ function authPolicyMismatch(auth, file, policy, lines) {
|
|
|
327
505
|
return MAKES_NONE.has(found.element) ? found : void 0;
|
|
328
506
|
}
|
|
329
507
|
var authChecks = (auth) => CHECKS[auth] ?? auth;
|
|
508
|
+
var bare = (p) => p.trim().replace(/\/+$/, "");
|
|
509
|
+
function apigeePassThrough(o, file, lines, line, path) {
|
|
510
|
+
if (!bundleRootOf(file))
|
|
511
|
+
return false;
|
|
512
|
+
const p = path.trim();
|
|
513
|
+
const under4 = p.endsWith("/**") ? bare(p.slice(0, -3)) : void 0;
|
|
514
|
+
const near = lines.slice(Math.max(0, line - 2), line + 1);
|
|
515
|
+
const element = (tag) => near.flatMap((l) => {
|
|
516
|
+
const m = new RegExp(`<${tag}>\\s*([^<]*?)\\s*</${tag}>`).exec(l);
|
|
517
|
+
return m ? [m[1]] : [];
|
|
518
|
+
});
|
|
519
|
+
if (o.kind === "entry")
|
|
520
|
+
return under4 !== void 0 && element("BasePath").some((b) => bare(b) === under4);
|
|
521
|
+
if (!o.forwards)
|
|
522
|
+
return false;
|
|
523
|
+
const target = under4 ?? bare(p);
|
|
524
|
+
const urlPath = (u) => bare(u.replace(/^[a-z][a-z0-9+.-]*:\/\/[^/]*/i, ""));
|
|
525
|
+
return element("URL").some((u) => urlPath(u) === target) || element("Path").some((t) => bare(t) === target) || target === "" && near.some((l) => /<RouteRule[\s>]/.test(l));
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// packages/verify/dist/call-security-lines.js
|
|
529
|
+
var CLIENT_CERT = /client[-_ .]?cert|keystore|key[-_ .]?store|ssl[-_ .]?bundle|\.pfx\b|\.p12\b|pkcs12|\bpfx\b|\bp12\b/i;
|
|
530
|
+
var CERT_AND_KEY = (l) => /\bcert\b/i.test(l) && /\bkey\b/i.test(l);
|
|
531
|
+
var KINDS = {
|
|
532
|
+
"tls mtls": {
|
|
533
|
+
test: (l) => CLIENT_CERT.test(l) || CERT_AND_KEY(l),
|
|
534
|
+
needs: "a client certificate, a key store or an SSL bundle"
|
|
535
|
+
},
|
|
536
|
+
"tls tls": { test: (l) => /https|ssl|tls/i.test(l), needs: "https, SSL or TLS" },
|
|
537
|
+
"tls none": {
|
|
538
|
+
test: (l) => !/https:\/\//i.test(l) && (/http:\/\//i.test(l) || /\b(false|disabled?|insecure|plain-?text|off)\b/i.test(l)),
|
|
539
|
+
needs: "a plain http:// URL or a TLS switch set off"
|
|
540
|
+
},
|
|
541
|
+
"auth oauth2": {
|
|
542
|
+
test: (l) => /oauth|token|bearer|jwt|authori[sz]|client[-_ ]?credentials|access[-_ ]?token/i.test(l),
|
|
543
|
+
needs: "a token (OAuth, bearer, JWT or an Authorization header)"
|
|
544
|
+
},
|
|
545
|
+
"auth api-key": {
|
|
546
|
+
test: (l) => /api[-_ ]?key|apikey|x-api|subscription[-_ ]?key|[a-z0-9][-_]key\b/i.test(l),
|
|
547
|
+
needs: "a key"
|
|
548
|
+
},
|
|
549
|
+
"auth basic": {
|
|
550
|
+
test: (l) => /basic|password|passwd|username|credential/i.test(l),
|
|
551
|
+
needs: "a password or basic credentials"
|
|
552
|
+
},
|
|
553
|
+
"auth mtls": {
|
|
554
|
+
test: (l) => CLIENT_CERT.test(l) || CERT_AND_KEY(l),
|
|
555
|
+
needs: "a client certificate, a key store or an SSL bundle"
|
|
556
|
+
}
|
|
557
|
+
};
|
|
558
|
+
function securityLineKind(what, claim, lines) {
|
|
559
|
+
const k = KINDS[`${what} ${claim}`];
|
|
560
|
+
if (!k)
|
|
561
|
+
return void 0;
|
|
562
|
+
return lines.some((l) => k.test(l)) ? void 0 : k.needs;
|
|
563
|
+
}
|
|
564
|
+
function lineOfValue(lines, line, value) {
|
|
565
|
+
if (!lines)
|
|
566
|
+
return void 0;
|
|
567
|
+
const v = value.toLowerCase();
|
|
568
|
+
for (const n of [line, line - 1, line + 1]) {
|
|
569
|
+
const l = lines[n - 1];
|
|
570
|
+
if (l !== void 0 && l.toLowerCase().includes(v))
|
|
571
|
+
return l;
|
|
572
|
+
}
|
|
573
|
+
return lines[line - 1];
|
|
574
|
+
}
|
|
330
575
|
|
|
331
576
|
// packages/verify/dist/corrections.js
|
|
332
|
-
function correctionFor(f,
|
|
577
|
+
function correctionFor(f, all2) {
|
|
578
|
+
const corrections = all2.filter((c) => c.action !== "link" && c.action !== "unlink");
|
|
333
579
|
const at = (c) => c.target.kind === f.kind && c.target.file === f.cite.file && c.target.line === f.cite.line;
|
|
334
580
|
const byId = corrections.filter((c) => c.fact === f.id && at(c));
|
|
335
581
|
const byCite = corrections.filter((c) => at(c) && c.target.value === f.cite.value);
|
|
@@ -337,6 +583,10 @@ function correctionFor(f, corrections) {
|
|
|
337
583
|
const moved = edited.filter((c) => (c.line ?? c.target.line) === f.cite.line);
|
|
338
584
|
return [...byId, ...byCite, ...moved].sort((a, b) => a.at < b.at ? 1 : a.at > b.at ? -1 : 0)[0];
|
|
339
585
|
}
|
|
586
|
+
function outsidePairingFor(f, all2) {
|
|
587
|
+
const newest = linksFor(f, all2)[0];
|
|
588
|
+
return newest?.link?.system ? newest : void 0;
|
|
589
|
+
}
|
|
340
590
|
function correctedFact(f, c) {
|
|
341
591
|
const raw = JSON.parse(JSON.stringify(f));
|
|
342
592
|
for (const [k, v] of Object.entries(c.set ?? {})) {
|
|
@@ -363,33 +613,64 @@ function applyCorrections(facts, corrections) {
|
|
|
363
613
|
const applied = [];
|
|
364
614
|
const used = /* @__PURE__ */ new Set();
|
|
365
615
|
const stale = [];
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
616
|
+
const met = /* @__PURE__ */ new Set();
|
|
617
|
+
const withPairing = (f, as, over) => {
|
|
618
|
+
const p = outsidePairingFor(as, corrections);
|
|
619
|
+
if (!p)
|
|
620
|
+
return f;
|
|
621
|
+
if (over && over.at > p.at && over.set?.to !== void 0)
|
|
622
|
+
return f;
|
|
623
|
+
const fixed = correctedFact(f, { ...p, action: "edit", set: { to: p.link.system } });
|
|
624
|
+
if (typeof fixed === "string") {
|
|
625
|
+
stale.push({
|
|
626
|
+
correction: p,
|
|
627
|
+
why: "invalid",
|
|
628
|
+
message: `${p.by}'s pairing of ${f.kind} ${f.id} with ${p.link.system} doesn't make a fact (${fixed}); the call is kept as it was`
|
|
629
|
+
});
|
|
630
|
+
return f;
|
|
631
|
+
}
|
|
632
|
+
applied.push({ fact: f.id, action: "link", by: p.by });
|
|
633
|
+
return { ...fixed, conf: "inferred" };
|
|
634
|
+
};
|
|
635
|
+
for (const f0 of facts) {
|
|
636
|
+
for (const l of linksFor(f0, corrections))
|
|
637
|
+
met.add(l);
|
|
638
|
+
const c = correctionFor(f0, corrections);
|
|
639
|
+
if (c?.action === "wrong") {
|
|
640
|
+
used.add(c);
|
|
641
|
+
applied.push({ fact: f0.id, action: "wrong", by: c.by });
|
|
370
642
|
continue;
|
|
371
643
|
}
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
applied.push({ fact: f.id, action: "wrong", by: c.by });
|
|
644
|
+
if (!c) {
|
|
645
|
+
out.push(withPairing(f0, f0));
|
|
375
646
|
continue;
|
|
376
647
|
}
|
|
377
|
-
|
|
648
|
+
used.add(c);
|
|
649
|
+
const fixed = correctedFact(f0, c);
|
|
378
650
|
if (typeof fixed === "string") {
|
|
379
651
|
stale.push({
|
|
380
652
|
correction: c,
|
|
381
653
|
why: "invalid",
|
|
382
|
-
message: `${c.by}'s correction of ${
|
|
654
|
+
message: `${c.by}'s correction of ${f0.kind} ${f0.id} doesn't make a fact (${fixed}); the fact is kept as it was`
|
|
383
655
|
});
|
|
384
|
-
out.push(
|
|
656
|
+
out.push(withPairing(f0, f0));
|
|
385
657
|
continue;
|
|
386
658
|
}
|
|
387
|
-
applied.push({ fact:
|
|
388
|
-
out.push(fixed);
|
|
659
|
+
applied.push({ fact: f0.id, action: "edit", by: c.by });
|
|
660
|
+
out.push(withPairing(fixed, f0, c));
|
|
389
661
|
}
|
|
390
662
|
for (const c of corrections) {
|
|
391
663
|
if (used.has(c))
|
|
392
664
|
continue;
|
|
665
|
+
if (c.action === "link" || c.action === "unlink") {
|
|
666
|
+
if (!met.has(c))
|
|
667
|
+
stale.push({
|
|
668
|
+
correction: c,
|
|
669
|
+
why: "none",
|
|
670
|
+
message: `${c.by}'s pairing of call ${c.fact} (${c.target.file}:${c.target.line}) with ${[c.link?.method, c.link?.path].filter(Boolean).join(" ")} on ${c.link?.repo ?? c.link?.system} meets no call in this read; it is kept in the file and applied when the call is read again`
|
|
671
|
+
});
|
|
672
|
+
continue;
|
|
673
|
+
}
|
|
393
674
|
const replaced = [...used].some((u) => (u.fact === c.fact || u.target.value === c.target.value) && u.target.kind === c.target.kind && u.target.file === c.target.file && u.target.line === c.target.line);
|
|
394
675
|
if (replaced || c.action === "wrong")
|
|
395
676
|
continue;
|
|
@@ -1199,10 +1480,11 @@ function isLocaleBundle(path) {
|
|
|
1199
1480
|
}
|
|
1200
1481
|
var DATA_FILE_LINES = 2e3;
|
|
1201
1482
|
var DATA_FILE_CHARS = 2e5;
|
|
1483
|
+
var API_SPEC = DEFAULT_READ_RULES.apiSpec;
|
|
1202
1484
|
var DATA_EXT = /\.(json|xml)$/i;
|
|
1203
1485
|
var KEEP_BIG = /(^|\/)(pom\.xml|package\.json|composer\.json|angular\.json|project\.json|nx\.json|lerna\.json|workspace\.json|tsconfig[^/]*\.json|jsconfig\.json|appsettings[^/]*\.json|launchSettings\.json|[^/]*(openapi|swagger|asyncapi|api-docs)[^/]*\.(json|xml)|[^/]*mapper[^/]*\.xml)$|(^|\/)(config|configuration|migrations?|db|database|changelogs?|liquibase|flyway)\//i;
|
|
1204
|
-
function isDataFile(path, lines) {
|
|
1205
|
-
if (!DATA_EXT.test(path) || KEEP_BIG.test(path))
|
|
1486
|
+
function isDataFile(path, lines, rules = DEFAULT_READ_RULES) {
|
|
1487
|
+
if (!DATA_EXT.test(path) || KEEP_BIG.test(path) || rules.apiSpec.test(path))
|
|
1206
1488
|
return false;
|
|
1207
1489
|
if (lines.length - (lines[lines.length - 1] === "" ? 1 : 0) > DATA_FILE_LINES)
|
|
1208
1490
|
return true;
|
|
@@ -1252,66 +1534,502 @@ function pageRouteMatches(file, lines, exists, path) {
|
|
|
1252
1534
|
return norm(route) === norm(path);
|
|
1253
1535
|
}
|
|
1254
1536
|
|
|
1255
|
-
// packages/verify/dist/
|
|
1256
|
-
var
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1537
|
+
// packages/verify/dist/reachable.js
|
|
1538
|
+
var CODE = /\.(java|kt|kts|scala|groovy|cs|vb|fs|py|rb|go|rs|php|swift|ts|tsx|js|mjs|cjs|dart)$/i;
|
|
1539
|
+
var MODEL_DIR = /(^|\/)(models?|dtos?|payloads?|schemas?|pojos?|beans|viewmodels?)\//i;
|
|
1540
|
+
var MODEL_NAME = /(dto|request|response|payload|model|body)$/i;
|
|
1541
|
+
var MODEL_PART = /[._-](dto|model|schema|payload|request|response)\.[^/.]+$/i;
|
|
1542
|
+
var MAPS_TABLE = /@Entity\b|@Table\b|@Document\b|\[Table\(|__tablename__|models\.Model\b|@Entity\(|\bgorm\.Model\b/;
|
|
1543
|
+
var ROOT_PART = /[._-](controller|resolver|gateway|handlers?|router|routes?|consumer|listener|subscriber|client|publisher|producer|endpoints?)\.[^/.]+$/i;
|
|
1544
|
+
var CLIENT_NAME = /(client|gateway|publisher|producer|sender|feign|proxy|connector)$/i;
|
|
1545
|
+
var CLIENT_DIR = /(^|\/)(clients?|gateways?|integrations?|publishers?|producers?|outbound)\//i;
|
|
1546
|
+
var base = (path) => path.slice(path.lastIndexOf("/") + 1);
|
|
1547
|
+
var stem = (path) => {
|
|
1548
|
+
const b = base(path);
|
|
1549
|
+
const dot = b.indexOf(".");
|
|
1550
|
+
return dot > 0 ? b.slice(0, dot) : b;
|
|
1551
|
+
};
|
|
1552
|
+
function isModelFile(path, text) {
|
|
1553
|
+
if (!CODE.test(path) || readingRank(path) === 5)
|
|
1554
|
+
return false;
|
|
1555
|
+
if (!MODEL_DIR.test(path) && !MODEL_NAME.test(stem(path)) && !MODEL_PART.test(path))
|
|
1556
|
+
return false;
|
|
1557
|
+
return text === void 0 || !MAPS_TABLE.test(text);
|
|
1558
|
+
}
|
|
1559
|
+
var ENTRY_POINT_NAME = /(controller|resource|router|routes?|handlers?|listener|consumer|subscriber|endpoints?|servlet|webhook|resolver)$/i;
|
|
1560
|
+
var ENTRY_POINT_DIR = /(^|\/)(controllers?|routes|routers|handlers|endpoints|listeners|consumers)\//i;
|
|
1561
|
+
var ENTRY_POINT_PART = /[._-](controller|resolver|handlers?|router|routes?|consumer|listener|subscriber|endpoints?)\.[^/.]+$/i;
|
|
1562
|
+
function isEntryPointFile(path) {
|
|
1563
|
+
if (!CODE.test(path) || readingRank(path) === 5 || isModelFile(path))
|
|
1564
|
+
return false;
|
|
1565
|
+
return ENTRY_POINT_NAME.test(stem(path)) || ENTRY_POINT_DIR.test(path) || ENTRY_POINT_PART.test(path);
|
|
1566
|
+
}
|
|
1567
|
+
function isRootFile(path) {
|
|
1568
|
+
if (!CODE.test(path) || readingRank(path) === 5 || isModelFile(path))
|
|
1569
|
+
return false;
|
|
1570
|
+
return readingRank(path) === 2 || isEntryPointFile(path) || ROOT_PART.test(path) || CLIENT_NAME.test(stem(path)) || CLIENT_DIR.test(path);
|
|
1571
|
+
}
|
|
1572
|
+
var DECLARES = /\b(?:class|record|interface|struct|enum|object)\s+([A-Za-z_][A-Za-z0-9_]*)|^\s*(?:export\s+)?(?:declare\s+)?type\s+([A-Za-z_][A-Za-z0-9_]*)/gm;
|
|
1573
|
+
var IDENT = /[A-Za-z_][A-Za-z0-9_]*/g;
|
|
1574
|
+
function declaredTypes(path, text) {
|
|
1575
|
+
const out = [];
|
|
1576
|
+
const s = stem(path);
|
|
1577
|
+
if (/^[A-Z][A-Za-z0-9_]*$/.test(s))
|
|
1578
|
+
out.push(s);
|
|
1579
|
+
for (const m of text.matchAll(DECLARES)) {
|
|
1580
|
+
const n = m[1] ?? m[2];
|
|
1581
|
+
if (n && !out.includes(n))
|
|
1582
|
+
out.push(n);
|
|
1583
|
+
}
|
|
1584
|
+
return out;
|
|
1585
|
+
}
|
|
1586
|
+
function identifiers(text) {
|
|
1587
|
+
return [...new Set(text.match(IDENT) ?? [])];
|
|
1588
|
+
}
|
|
1589
|
+
var NOT_REACHED_WHY = "model classes no entry point, call or field fact reaches (ADR 0169): left out of the read; atticus map <repo> --models all reads them";
|
|
1590
|
+
function reachableModels(o) {
|
|
1591
|
+
const texts = /* @__PURE__ */ new Map();
|
|
1592
|
+
const textOf = (p) => {
|
|
1593
|
+
if (!texts.has(p))
|
|
1594
|
+
texts.set(p, o.read(p) ?? "");
|
|
1595
|
+
return texts.get(p);
|
|
1596
|
+
};
|
|
1597
|
+
const ordered = readingOrder(o.paths);
|
|
1598
|
+
const models = ordered.filter((p) => isModelFile(p, textOf(p))).sort();
|
|
1599
|
+
const isModel = new Set(models);
|
|
1600
|
+
const byName = /* @__PURE__ */ new Map();
|
|
1601
|
+
const declared = /* @__PURE__ */ new Map();
|
|
1602
|
+
for (const p of models) {
|
|
1603
|
+
const names = declaredTypes(p, textOf(p));
|
|
1604
|
+
declared.set(p, names);
|
|
1605
|
+
for (const n of names)
|
|
1606
|
+
byName.set(n, [...byName.get(n) ?? [], p]);
|
|
1607
|
+
}
|
|
1608
|
+
const known = new Set(o.paths);
|
|
1609
|
+
const roots = [];
|
|
1610
|
+
const addRoot = (p) => {
|
|
1611
|
+
if (known.has(p) && !isModel.has(p) && !roots.includes(p))
|
|
1612
|
+
roots.push(p);
|
|
1613
|
+
};
|
|
1614
|
+
const facts = o.facts ?? [];
|
|
1615
|
+
const entries = o.from === "entries";
|
|
1616
|
+
const calls2 = new Set(facts.filter((f) => f.kind === "call").map((f) => f.id));
|
|
1617
|
+
for (const f of facts)
|
|
1618
|
+
if (f.kind === "entry" || f.kind === "call" && !entries)
|
|
1619
|
+
for (const p of citedFiles(f))
|
|
1620
|
+
addRoot(p);
|
|
1621
|
+
for (const p of ordered)
|
|
1622
|
+
if (entries ? isEntryPointFile(p) : isRootFile(p))
|
|
1623
|
+
addRoot(p);
|
|
1624
|
+
const reached = /* @__PURE__ */ new Map();
|
|
1625
|
+
const queue = [];
|
|
1626
|
+
const reach = (names, from, own = []) => {
|
|
1627
|
+
for (const n of names) {
|
|
1628
|
+
if (own.includes(n))
|
|
1629
|
+
continue;
|
|
1630
|
+
for (const p of byName.get(n) ?? []) {
|
|
1631
|
+
const r = reached.get(p);
|
|
1632
|
+
if (r) {
|
|
1633
|
+
if (r.from === from && !r.types.includes(n))
|
|
1634
|
+
r.types.push(n);
|
|
1635
|
+
continue;
|
|
1636
|
+
}
|
|
1637
|
+
reached.set(p, { path: p, types: [n], from });
|
|
1638
|
+
queue.push(p);
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
};
|
|
1642
|
+
for (const p of roots)
|
|
1643
|
+
reach(identifiers(textOf(p)), p);
|
|
1644
|
+
for (const f of facts) {
|
|
1645
|
+
for (const p of citedFiles(f))
|
|
1646
|
+
if (isModel.has(p) && !reached.has(p)) {
|
|
1647
|
+
reached.set(p, { path: p, types: [...(declared.get(p) ?? []).slice(0, 1)], from: f.id });
|
|
1648
|
+
queue.push(p);
|
|
1649
|
+
}
|
|
1650
|
+
if (f.kind === "field" && !(entries && calls2.has(f.owner)))
|
|
1651
|
+
reach(identifiers([f.type ?? "", f.transform === "message type" ? f.name : ""].join(" ")), f.id);
|
|
1652
|
+
}
|
|
1653
|
+
const rooted = roots.length > 0 || reached.size > 0;
|
|
1654
|
+
if (!rooted)
|
|
1655
|
+
return {
|
|
1656
|
+
rooted,
|
|
1657
|
+
roots,
|
|
1658
|
+
reached: models.map((p) => ({ path: p, types: declared.get(p) ?? [], from: "" })),
|
|
1659
|
+
notReached: [],
|
|
1660
|
+
models: models.length
|
|
1661
|
+
};
|
|
1662
|
+
for (let i = 0; i < queue.length; i++) {
|
|
1663
|
+
const p = queue[i];
|
|
1664
|
+
reach(identifiers(textOf(p)), p, declared.get(p));
|
|
1665
|
+
}
|
|
1666
|
+
return {
|
|
1667
|
+
rooted,
|
|
1668
|
+
roots,
|
|
1669
|
+
reached: queue.map((p) => reached.get(p)),
|
|
1670
|
+
notReached: models.filter((p) => !reached.has(p)),
|
|
1671
|
+
models: models.length
|
|
1672
|
+
};
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
// packages/verify/dist/method.js
|
|
1676
|
+
var HTTP_METHODS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
|
|
1677
|
+
function wordsOf(line) {
|
|
1678
|
+
return line.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").toLowerCase().split(/[^a-z]+/).filter(Boolean);
|
|
1679
|
+
}
|
|
1680
|
+
function methodOnLines(method, lines) {
|
|
1681
|
+
const m = method.toLowerCase();
|
|
1682
|
+
return lines.some((l) => wordsOf(l).includes(m));
|
|
1683
|
+
}
|
|
1684
|
+
var CHAIN_LINES = 8;
|
|
1685
|
+
var CONTINUES = /^\s*\??\./;
|
|
1686
|
+
function chainOf(file, line) {
|
|
1687
|
+
const at = (n) => file?.[n - 1];
|
|
1688
|
+
const cited = at(line);
|
|
1689
|
+
if (cited === void 0)
|
|
1690
|
+
return [];
|
|
1691
|
+
const out = [cited];
|
|
1692
|
+
for (let n = line, k = 0; k < CHAIN_LINES && CONTINUES.test(at(n) ?? ""); k++, n--) {
|
|
1693
|
+
const above = at(n - 1);
|
|
1694
|
+
if (above === void 0)
|
|
1695
|
+
break;
|
|
1696
|
+
out.push(above);
|
|
1697
|
+
}
|
|
1698
|
+
for (let n = line + 1, k = 0; k < CHAIN_LINES && CONTINUES.test(at(n) ?? ""); k++, n++)
|
|
1699
|
+
out.push(at(n));
|
|
1700
|
+
return out;
|
|
1701
|
+
}
|
|
1702
|
+
function methodUnseenOf(f, lines, factOf) {
|
|
1703
|
+
if (f.kind !== "entry" && f.kind !== "call")
|
|
1704
|
+
return void 0;
|
|
1705
|
+
if (!f.method || !HTTP_METHODS.has(f.method.toUpperCase()))
|
|
1706
|
+
return void 0;
|
|
1707
|
+
const at = (file, line) => lines(file)?.[line - 1];
|
|
1708
|
+
const route = f.kind === "call" && f.from ? factOf?.(f.from) : void 0;
|
|
1709
|
+
const forwards = route?.kind === "entry" && route.method?.toUpperCase() === f.method.toUpperCase();
|
|
1710
|
+
const looked = [
|
|
1711
|
+
...chainOf(lines(f.cite.file), f.cite.line),
|
|
1712
|
+
at(f.cite.file, f.cite.line + 1),
|
|
1713
|
+
...(f.cite.with ?? []).map((p) => at(p.file, p.line)),
|
|
1714
|
+
...forwards ? [at(route.cite.file, route.cite.line), at(route.cite.file, route.cite.line + 1)] : []
|
|
1715
|
+
].filter((l) => l !== void 0);
|
|
1716
|
+
if (methodOnLines(f.method, looked))
|
|
1717
|
+
return void 0;
|
|
1718
|
+
const cite2 = `${f.cite.file}:${f.cite.line}`;
|
|
1719
|
+
const method = f.method.toUpperCase();
|
|
1720
|
+
return {
|
|
1721
|
+
id: f.id,
|
|
1722
|
+
kind: f.kind,
|
|
1723
|
+
method,
|
|
1724
|
+
cite: cite2,
|
|
1725
|
+
message: `${f.id} claims ${method}, which neither ${cite2}, the chain it is in, the next line, a cite.with line nor the route it forwards writes`
|
|
1726
|
+
};
|
|
1727
|
+
}
|
|
1728
|
+
|
|
1729
|
+
// packages/verify/dist/skipped.js
|
|
1730
|
+
function notReadForSkipped(path, rules = DEFAULT_READ_RULES) {
|
|
1731
|
+
return rules.notRead.test(path);
|
|
1732
|
+
}
|
|
1733
|
+
var isCommentLine = (l) => /^\s*(\/\/|\/\*|\*|<!--|-- |#(?!\s*(region|endregion|if|elif|else|endif|define|undef|pragma|nullable|include|import)\b)(?!!))/.test(l);
|
|
1734
|
+
var IMPORT_LINE = /^\s*(import|export\s+[^=]*\bfrom\b|from\s+\S+\s+import|package|using|require|#\s*include|@import|@use|use|library)\b/;
|
|
1735
|
+
var IMPORT_CALL = /\brequire\s*\(|\bimport\s*\(|\bfrom\s+["'`]/;
|
|
1736
|
+
var NAMESPACE_LINE = /\bxmlns(:\w+)?\s*=|schemaLocation\s*=|<!DOCTYPE|^\s*<\?xml/i;
|
|
1737
|
+
var URL_RE = /\b(?:https?|wss?|grpcs?|amqps?):\/\/[^\s"'`<>()[\],;\\|^]+/gi;
|
|
1738
|
+
var STANDARD_HOST = /^(www\.)?(w3\.org|json-schema\.org|maven\.apache\.org|apache\.org|springframework\.org|java\.sun\.com|xmlns\.jcp\.org|purl\.org|opensource\.org|gnu\.org|schema\.org|schemas\.[^/]+|xmlns\.[^/]+|ietf\.org|tools\.ietf\.org|creativecommons\.org)$/i;
|
|
1739
|
+
var FS_ROOT = /^\/(usr|etc|var|tmp|bin|sbin|opt|home|dev|proc|sys|lib|lib64|mnt|root|Users|private|Library|System|Applications|Volumes)(\/|$)/;
|
|
1740
|
+
var FILE_EXT = /\.(m?js|cjs|jsx|tsx?|css|scss|less|png|jpe?g|gif|svg|ico|webp|bmp|woff2?|ttf|eot|otf|map|jar|war|ear|class|so|dll|exe|sh|bat|ps1|py|rb|txt|log|pem|crt|key|lock|ya?ml|properties|conf|ini|env|md|mp[34]|wav|pdf|zip|gz|tar)$/i;
|
|
1741
|
+
var PATH_SEG = "[A-Za-z0-9_.~{}$:<>*%@+=-]+";
|
|
1742
|
+
var PATH_RE = new RegExp(`^(?:\\$\\{[^}]+\\}|%s|\\{[A-Za-z_]+\\})?((?:\\/${PATH_SEG})+\\/?)$`);
|
|
1743
|
+
var NOT_TOPIC_LAST = new Set("com org net io dev app ai co uk de fr in jp us eu example internal local localhost test cloud corp lan svc java kt scala cs ts js json xml yml yaml properties html htm txt md sql proto csv log jar dll exe sh py rb go class png svg gif jpg css".split(" "));
|
|
1744
|
+
var NOT_TOPIC_FIRST = new Set("com org net io java javax jakarta android androidx kotlin kotlinx scala sun microsoft system google apache spring springframework management server logging hibernate jdk www".split(" "));
|
|
1745
|
+
var DOTTED_TOPIC = /^[a-z][a-z0-9_-]*(\.[a-z][a-z0-9_-]*){2,}$/;
|
|
1746
|
+
var SEPARATED_NAME = /^[A-Za-z][A-Za-z0-9_]*([._:-][A-Za-z0-9_]+)+$/;
|
|
1747
|
+
var QUOTED = /(["'`])((?:\\.|(?!\1)[^\\\n])*)\1/g;
|
|
1748
|
+
var KEY_VALUE = /^\s*-?\s*["']?([A-Za-z0-9_.\-[\]]+)["']?\s*[:=]\s*([^\s"'#]+)\s*$/;
|
|
1749
|
+
var ELEMENT_TEXT = /<([A-Za-z][\w:.-]*)[^>]*>([^<\s]+)<\/\1>/g;
|
|
1750
|
+
var wordsOf2 = (line) => line.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").toLowerCase().split(/[^a-z]+/).filter(Boolean);
|
|
1751
|
+
var TOPIC_WORDS = /* @__PURE__ */ new Set([
|
|
1752
|
+
"topic",
|
|
1753
|
+
"topics",
|
|
1754
|
+
"queue",
|
|
1755
|
+
"queues",
|
|
1756
|
+
"channel",
|
|
1757
|
+
"channels",
|
|
1758
|
+
"destination",
|
|
1759
|
+
"destinations",
|
|
1760
|
+
"exchange",
|
|
1761
|
+
"exchanges"
|
|
1762
|
+
]);
|
|
1763
|
+
var topicContext = (line) => {
|
|
1764
|
+
const w = wordsOf2(line);
|
|
1765
|
+
return w.some((x) => TOPIC_WORDS.has(x)) || w.join(" ").includes("routing key");
|
|
1766
|
+
};
|
|
1767
|
+
var BUILD_KEY_WORDS = new Set("scm git gitlab github bitbucket repository repositories repo registry mirror mirrors nexus artifactory snapshot snapshots distribution maven npm nuget pypi sonar sonarqube jenkins ci".split(" "));
|
|
1768
|
+
var BUILD_BLOCK_TAG = /<(\/?)(repositories|repository|pluginRepositories|pluginRepository|snapshotRepository|distributionManagement|scm|mirrors|mirror|site)\b[^>]*?(\/?)>/gi;
|
|
1769
|
+
var BUILD_BLOCK_OPEN = /\b(repositories|publishing|pluginManagement|dependencyResolutionManagement)\s*\{/;
|
|
1770
|
+
var keysBefore = (prefix) => [
|
|
1771
|
+
...[...prefix.matchAll(/<([A-Za-z][\w:.-]*)/g)].map((m) => m[1]),
|
|
1772
|
+
...[...prefix.matchAll(/["']?([A-Za-z][\w.-]*)["']?\s*[:=]/g)].map((m) => m[1]),
|
|
1773
|
+
...[...prefix.matchAll(/\b([A-Za-z]\w*)\s*\{/g)].map((m) => m[1])
|
|
1774
|
+
];
|
|
1775
|
+
var isBuildUrl = (u, prefix) => /\.git\/?$/i.test(u) || /\bscm:\w+:$/i.test(prefix) || keysBefore(prefix).some((k) => wordsOf2(k).some((w) => BUILD_KEY_WORDS.has(w)));
|
|
1776
|
+
var HEADER_ARG = /header\w*\s*(?:\.\s*\w+\s*)?\(\s*(?:(?:value|name)\s*=\s*)?$/i;
|
|
1777
|
+
var isHeaderName = (s, prefix) => /^x-/i.test(s) || HEADER_ARG.test(prefix.replace(/["'`]$/, ""));
|
|
1778
|
+
var cleanUrl = (u) => u.replace(/[.:]+$/, "").replace(/^([a-z0-9+.-]+:\/\/)[^/@\s]+@/i, "$1<redacted>@");
|
|
1779
|
+
var hostOf = (u) => /^[a-z0-9+.-]+:\/\/([^/:?#]+)/i.exec(u)?.[1] ?? "";
|
|
1780
|
+
function pathOf(s) {
|
|
1781
|
+
const m = PATH_RE.exec(s);
|
|
1782
|
+
if (!m)
|
|
1783
|
+
return void 0;
|
|
1784
|
+
const p = m[1];
|
|
1785
|
+
if (p.length < 2 || p.includes("//") || !/[A-Za-z]/.test(p))
|
|
1786
|
+
return void 0;
|
|
1787
|
+
if (FS_ROOT.test(p) || FILE_EXT.test(p))
|
|
1788
|
+
return void 0;
|
|
1789
|
+
return p;
|
|
1790
|
+
}
|
|
1791
|
+
function topicOf(s, context) {
|
|
1792
|
+
if (DOTTED_TOPIC.test(s)) {
|
|
1793
|
+
const parts = s.split(".");
|
|
1794
|
+
if (!NOT_TOPIC_LAST.has(parts.at(-1)) && !NOT_TOPIC_FIRST.has(parts[0]))
|
|
1795
|
+
return s;
|
|
1796
|
+
}
|
|
1797
|
+
if (context && SEPARATED_NAME.test(s) && !FILE_EXT.test(s)) {
|
|
1798
|
+
const last = s.split(".").at(-1);
|
|
1799
|
+
if (!s.includes(".") || !NOT_TOPIC_LAST.has(last))
|
|
1800
|
+
return s;
|
|
1801
|
+
}
|
|
1802
|
+
return void 0;
|
|
1803
|
+
}
|
|
1804
|
+
function codeOf(raw, state) {
|
|
1805
|
+
let s = raw;
|
|
1806
|
+
let out = "";
|
|
1807
|
+
for (; ; ) {
|
|
1808
|
+
if (state.block) {
|
|
1809
|
+
const close = state.block === "c" ? "*/" : "-->";
|
|
1810
|
+
const i = s.indexOf(close);
|
|
1811
|
+
if (i < 0)
|
|
1812
|
+
return out;
|
|
1813
|
+
s = s.slice(i + close.length);
|
|
1814
|
+
state.block = void 0;
|
|
1815
|
+
}
|
|
1816
|
+
const m = /(^|\s)(\/\*|<!--)/.exec(s);
|
|
1817
|
+
if (!m)
|
|
1818
|
+
return out + s;
|
|
1819
|
+
out += s.slice(0, m.index + m[1].length);
|
|
1820
|
+
s = s.slice(m.index + m[0].length);
|
|
1821
|
+
state.block = m[2] === "/*" ? "c" : "xml";
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
function candidatesIn(path, lines) {
|
|
1825
|
+
const out = [];
|
|
1826
|
+
const state = {};
|
|
1827
|
+
let xmlDepth = 0;
|
|
1828
|
+
let braceDepth = 0;
|
|
1829
|
+
lines.forEach((raw, i) => {
|
|
1830
|
+
const wasBlock = state.block !== void 0;
|
|
1831
|
+
let code2 = codeOf(raw, state);
|
|
1832
|
+
const inBuild = xmlDepth > 0 || braceDepth > 0;
|
|
1833
|
+
for (const m of code2.matchAll(BUILD_BLOCK_TAG))
|
|
1834
|
+
if (!m[3])
|
|
1835
|
+
xmlDepth = Math.max(0, xmlDepth + (m[1] ? -1 : 1));
|
|
1836
|
+
if (braceDepth > 0 || BUILD_BLOCK_OPEN.test(code2)) {
|
|
1837
|
+
const from = braceDepth > 0 ? code2 : code2.slice(BUILD_BLOCK_OPEN.exec(code2).index);
|
|
1838
|
+
braceDepth = Math.max(0, braceDepth + (from.match(/\{/g)?.length ?? 0) - (from.match(/\}/g)?.length ?? 0));
|
|
1839
|
+
}
|
|
1840
|
+
if (!code2.trim())
|
|
1262
1841
|
return;
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
json2 = JSON.parse(raw);
|
|
1267
|
-
} catch (e) {
|
|
1268
|
-
bad.push({ line, raw, message: `line ${line} is not JSON: ${e.message}` });
|
|
1842
|
+
if (!wasBlock && isCommentLine(raw))
|
|
1843
|
+
return;
|
|
1844
|
+
if (IMPORT_LINE.test(code2) || IMPORT_CALL.test(code2) || NAMESPACE_LINE.test(code2))
|
|
1269
1845
|
return;
|
|
1846
|
+
code2 = code2.replace(/(^|[\s;,)}])\/\/.*$/, "$1").replace(/\s#\s.*$/, "");
|
|
1847
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1848
|
+
const text = redactSecrets(cleanUrlIn(raw.trim())).slice(0, 200);
|
|
1849
|
+
const add = (kind, value) => {
|
|
1850
|
+
const key = `${kind} ${value}`;
|
|
1851
|
+
if (seen.has(key))
|
|
1852
|
+
return;
|
|
1853
|
+
seen.add(key);
|
|
1854
|
+
out.push({ line: i + 1, kind, value, text });
|
|
1855
|
+
};
|
|
1856
|
+
const context = topicContext(code2);
|
|
1857
|
+
const urls = [];
|
|
1858
|
+
const opened = inBuild || xmlDepth > 0 || braceDepth > 0;
|
|
1859
|
+
for (const m of code2.matchAll(URL_RE)) {
|
|
1860
|
+
const u = cleanUrl(m[0]);
|
|
1861
|
+
const build = opened || isBuildUrl(u, code2.slice(0, m.index));
|
|
1862
|
+
if (!build && !STANDARD_HOST.test(hostOf(u)))
|
|
1863
|
+
add("url", u);
|
|
1864
|
+
urls.push(m[0]);
|
|
1270
1865
|
}
|
|
1271
|
-
const
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
if (
|
|
1282
|
-
|
|
1866
|
+
const strings2 = [...code2.matchAll(QUOTED)].map((m) => ({
|
|
1867
|
+
s: m[2],
|
|
1868
|
+
before: code2.slice(0, m.index + 1)
|
|
1869
|
+
}));
|
|
1870
|
+
const kv = KEY_VALUE.exec(code2);
|
|
1871
|
+
if (kv)
|
|
1872
|
+
strings2.push({ s: kv[2], before: "" });
|
|
1873
|
+
for (const m of code2.matchAll(ELEMENT_TEXT))
|
|
1874
|
+
strings2.push({ s: m[2], before: "" });
|
|
1875
|
+
for (const { s, before } of strings2) {
|
|
1876
|
+
if (urls.some((u) => s.includes(u) || u.includes(s)))
|
|
1877
|
+
continue;
|
|
1878
|
+
const p = pathOf(s);
|
|
1879
|
+
if (p) {
|
|
1880
|
+
add("path", p);
|
|
1881
|
+
continue;
|
|
1882
|
+
}
|
|
1883
|
+
const t = topicOf(s, context);
|
|
1884
|
+
if (t && !isHeaderName(t, before))
|
|
1885
|
+
add("topic", t);
|
|
1283
1886
|
}
|
|
1284
|
-
|
|
1285
|
-
|
|
1887
|
+
});
|
|
1888
|
+
return out;
|
|
1889
|
+
}
|
|
1890
|
+
var cleanUrlIn = (line) => line.replace(/\b([a-z0-9+.-]+:\/\/)[^/@\s"'`]+@/gi, "$1<redacted>@");
|
|
1891
|
+
var normPath = (p) => normalizePath(p.replace(/(^|\/)%[sd](?=\/|$)/g, "$1{}"));
|
|
1892
|
+
function indexFacts(facts) {
|
|
1893
|
+
const at = /* @__PURE__ */ new Set();
|
|
1894
|
+
const strings2 = /* @__PURE__ */ new Set();
|
|
1895
|
+
const walk = (v) => {
|
|
1896
|
+
if (typeof v === "string") {
|
|
1897
|
+
if (v.length >= 3)
|
|
1898
|
+
strings2.add(v);
|
|
1286
1899
|
return;
|
|
1287
1900
|
}
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1901
|
+
if (Array.isArray(v))
|
|
1902
|
+
return v.forEach(walk);
|
|
1903
|
+
if (v && typeof v === "object") {
|
|
1904
|
+
const o = v;
|
|
1905
|
+
if (typeof o.file === "string" && typeof o.line === "number")
|
|
1906
|
+
at.add(`${o.file}:${o.line}`);
|
|
1907
|
+
Object.values(o).forEach(walk);
|
|
1908
|
+
}
|
|
1909
|
+
};
|
|
1910
|
+
facts.forEach(walk);
|
|
1911
|
+
const paths = /* @__PURE__ */ new Set();
|
|
1912
|
+
for (const s of strings2) {
|
|
1913
|
+
if (/\s/.test(s))
|
|
1914
|
+
continue;
|
|
1915
|
+
const u = /^[a-z0-9+.-]+:\/\/[^/]+(\/.*)?$/i.exec(s);
|
|
1916
|
+
const p = u ? u[1] : s.includes("/") ? s : void 0;
|
|
1917
|
+
if (p && p.length > 1)
|
|
1918
|
+
paths.add(`${normPath(p)}/`);
|
|
1919
|
+
}
|
|
1920
|
+
return { at, strings: [...strings2].map((s) => s.toLowerCase()), paths: [...paths] };
|
|
1921
|
+
}
|
|
1922
|
+
function factsCover(file, c, ix) {
|
|
1923
|
+
if ([c.line - 1, c.line, c.line + 1].some((n) => ix.at.has(`${file}:${n}`)))
|
|
1924
|
+
return true;
|
|
1925
|
+
if (c.kind === "path") {
|
|
1926
|
+
const p = `${normPath(c.value)}/`;
|
|
1927
|
+
return ix.paths.some((f) => f.includes(p));
|
|
1928
|
+
}
|
|
1929
|
+
const v = c.value.toLowerCase().replace(/\/+$/, "");
|
|
1930
|
+
return ix.strings.some((s) => s.includes(v));
|
|
1931
|
+
}
|
|
1932
|
+
var skippedOf = (file, c) => ({
|
|
1933
|
+
cite: `${file}:${c.line}`,
|
|
1934
|
+
kind: c.kind,
|
|
1935
|
+
value: c.value,
|
|
1936
|
+
text: c.text
|
|
1937
|
+
});
|
|
1938
|
+
var SPEC_MARK = /^\s*["']?(openapi|swagger)["']?\s*:/m;
|
|
1939
|
+
var SPEC_EXT = /\.(ya?ml|json)$/i;
|
|
1940
|
+
function specOpsUncoveredIn(file, text, facts) {
|
|
1941
|
+
if (!SPEC_EXT.test(file) || !SPEC_MARK.test(text.slice(0, 4e3)))
|
|
1942
|
+
return void 0;
|
|
1943
|
+
let doc;
|
|
1944
|
+
try {
|
|
1945
|
+
doc = readOpenApiDocument(text);
|
|
1946
|
+
} catch {
|
|
1947
|
+
return void 0;
|
|
1948
|
+
}
|
|
1949
|
+
if (!doc || !doc.operations.length)
|
|
1950
|
+
return void 0;
|
|
1951
|
+
const server = (() => {
|
|
1952
|
+
const s = doc.servers[0];
|
|
1953
|
+
if (!s)
|
|
1954
|
+
return "";
|
|
1955
|
+
const p = /^[a-z0-9+.-]+:\/\/[^/]+(\/.*)?$/i.exec(s)?.[1] ?? (s.startsWith("/") ? s : "");
|
|
1956
|
+
return p.replace(/\/+$/, "");
|
|
1957
|
+
})();
|
|
1958
|
+
const entries = facts.filter((f) => f.kind === "entry");
|
|
1959
|
+
const atLines = /* @__PURE__ */ new Set();
|
|
1960
|
+
for (const e of entries)
|
|
1961
|
+
for (const k of indexFacts([e]).at)
|
|
1962
|
+
atLines.add(k);
|
|
1963
|
+
const out = [];
|
|
1964
|
+
for (const op of doc.operations) {
|
|
1965
|
+
const lines = [op.methodLine, op.line].filter((n) => n !== void 0);
|
|
1966
|
+
if (lines.some((n) => [n - 1, n, n + 1].some((x) => atLines.has(`${file}:${x}`))))
|
|
1967
|
+
continue;
|
|
1968
|
+
const method = op.method.toUpperCase();
|
|
1969
|
+
const own = normPath(op.path);
|
|
1970
|
+
const full = normPath(`${server}${op.path}`);
|
|
1971
|
+
const covered = entries.some((e) => {
|
|
1972
|
+
if (e.method && e.method.toUpperCase() !== method)
|
|
1973
|
+
return false;
|
|
1974
|
+
const p = normPath(e.path);
|
|
1975
|
+
return p === own || p === full || p.endsWith(own) || p.split("/").length > 2 && own.endsWith(p);
|
|
1976
|
+
});
|
|
1977
|
+
if (covered)
|
|
1978
|
+
continue;
|
|
1979
|
+
const opId = op.node?.get?.("operationId");
|
|
1980
|
+
const line = op.methodLine ?? op.line ?? 1;
|
|
1981
|
+
out.push({
|
|
1982
|
+
spec: file,
|
|
1983
|
+
method,
|
|
1984
|
+
path: op.path,
|
|
1985
|
+
...typeof opId === "string" ? { operationId: opId } : {},
|
|
1986
|
+
cite: `${file}:${line}`,
|
|
1987
|
+
message: `${file}:${line} declares ${method} ${op.path}${typeof opId === "string" ? ` (${opId})` : ""}, which no entry covers`
|
|
1988
|
+
});
|
|
1989
|
+
}
|
|
1990
|
+
return out;
|
|
1991
|
+
}
|
|
1992
|
+
var REVIEWED_HOLDS_SHOWN = 5;
|
|
1993
|
+
function reviewedHoldsOf(r, skipped) {
|
|
1994
|
+
const n = skipped.length;
|
|
1995
|
+
const first = skipped.slice(0, 3).map((s) => `${s.cite} (${s.value})`);
|
|
1996
|
+
return {
|
|
1997
|
+
path: r.path,
|
|
1998
|
+
why: r.why,
|
|
1999
|
+
count: n,
|
|
2000
|
+
skipped: skipped.slice(0, REVIEWED_HOLDS_SHOWN),
|
|
2001
|
+
message: `${r.line !== void 0 ? `line ${r.line}: ` : ""}${r.path} is reviewed as holding nothing to record, but ${n} ${n === 1 ? "line in it writes" : "lines in it write"} a path, URL or topic: ${first.join(", ")}${n > 3 ? ` and ${n - 3} more` : ""}`
|
|
2002
|
+
};
|
|
1291
2003
|
}
|
|
1292
|
-
var isGlob = (path) => /[*?]/.test(path);
|
|
1293
2004
|
|
|
1294
2005
|
// packages/verify/dist/verify.js
|
|
1295
2006
|
var FOLLOWED_TYPES = ["event-topic", "mq-queue", "rest", "grpc"];
|
|
1296
|
-
function entriesNoFlowFollows(facts) {
|
|
2007
|
+
function entriesNoFlowFollows(facts, rules = DEFAULT_READ_RULES) {
|
|
1297
2008
|
const started = new Set(facts.flatMap((f) => f.kind === "flow" ? [f.entry] : []));
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
2009
|
+
const forwarded = new Set(facts.flatMap((f) => f.kind === "call" && f.from ? [f.from] : []));
|
|
2010
|
+
return facts.flatMap((f) => {
|
|
2011
|
+
if (f.kind !== "entry" || !FOLLOWED_TYPES.includes(f.entryType))
|
|
2012
|
+
return [];
|
|
2013
|
+
const route = rules.gatewayBundle.test(f.cite.file);
|
|
2014
|
+
if (route ? forwarded.has(f.id) : started.has(f.id))
|
|
2015
|
+
return [];
|
|
2016
|
+
return [
|
|
2017
|
+
{
|
|
2018
|
+
id: f.id,
|
|
2019
|
+
entryType: f.entryType,
|
|
2020
|
+
...f.method ? { method: f.method } : {},
|
|
2021
|
+
path: f.path,
|
|
2022
|
+
cite: `${f.cite.file}:${f.cite.line}`,
|
|
2023
|
+
...route ? { route: true } : {}
|
|
2024
|
+
}
|
|
2025
|
+
];
|
|
2026
|
+
}).sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
|
|
1308
2027
|
}
|
|
1309
2028
|
var LINES_HELD = 64;
|
|
1310
2029
|
var SOURCE_EXT = /\.(java|kt|kts|scala|groovy|cs|vb|fs|ts|tsx|js|jsx|mjs|cjs|py|rb|go|rs|php|swift|m|mm|c|cc|cpp|h|hpp|sql|graphql|gql|proto|ya?ml|json|xml|properties|toml|ini|conf|tf|tfvars|hcl|bicep|sh|ps1|bat|cmd|gradle|html?|vue|svelte|cshtml|razor|aspx|jsp|ftl|hbs|cob|cbl|cpy|jcl|avsc|wsdl|xsd|csproj|sln|config)$/i;
|
|
1311
2030
|
var SOURCE_NAME = /(^|\/)(Dockerfile[^/]*|Makefile|Jenkinsfile|Procfile|Containerfile)$/;
|
|
1312
2031
|
var LOCKFILE = /(^|\/)(package-lock\.json|npm-shrinkwrap\.json|yarn\.lock|pnpm-lock\.yaml|composer\.lock|Gemfile\.lock|Cargo\.lock|poetry\.lock|go\.sum|packages\.lock\.json)$/;
|
|
1313
2032
|
var NOT_PRODUCT = [
|
|
1314
|
-
/(^|\/)(src\/test|src\/it|test|tests|__tests__|__mocks__|spec|specs|e2e|testdata|test-data|fixtures)\//i,
|
|
1315
2033
|
/(\.|_|-)(test|spec|tests|specs)\.[a-z0-9]+$/i,
|
|
1316
2034
|
/(Test|Tests|IT|Spec)\.(java|kt|scala|groovy|cs|vb)$/,
|
|
1317
2035
|
/(^|\/)test_[^/]+\.py$/,
|
|
@@ -1320,19 +2038,44 @@ var NOT_PRODUCT = [
|
|
|
1320
2038
|
/(^|\/)messages(_[A-Za-z_]+)?\.properties$/,
|
|
1321
2039
|
/\.(resx|po|pot)$/i
|
|
1322
2040
|
];
|
|
1323
|
-
function isSourceFile(path, firstLine = "", lines) {
|
|
2041
|
+
function isSourceFile(path, firstLine = "", lines, rules = DEFAULT_READ_RULES) {
|
|
1324
2042
|
if (isSecretPath(path) || LOCKFILE.test(path))
|
|
1325
2043
|
return false;
|
|
1326
|
-
if (NOT_PRODUCT.some((re) => re.test(path))
|
|
2044
|
+
if (rules.notProduct.test(path) || NOT_PRODUCT.some((re) => re.test(path)))
|
|
1327
2045
|
return false;
|
|
1328
|
-
if (
|
|
2046
|
+
if (isLocaleBundle(path))
|
|
2047
|
+
return false;
|
|
2048
|
+
if (lines && isDataFile(path, lines, rules))
|
|
1329
2049
|
return false;
|
|
1330
2050
|
if (quoteNotRequired(path, firstLine))
|
|
1331
2051
|
return false;
|
|
2052
|
+
if (isLfsPointer(firstLine))
|
|
2053
|
+
return false;
|
|
2054
|
+
if (isBinaryText(firstLine) || lines && binaryLines(lines))
|
|
2055
|
+
return false;
|
|
1332
2056
|
return SOURCE_EXT.test(path) || SOURCE_NAME.test(path);
|
|
1333
2057
|
}
|
|
2058
|
+
function binaryLines(lines) {
|
|
2059
|
+
let n = 0;
|
|
2060
|
+
for (const l of lines) {
|
|
2061
|
+
if (isBinaryText(l))
|
|
2062
|
+
return true;
|
|
2063
|
+
if ((n += l.length + 1) > 8e3)
|
|
2064
|
+
return false;
|
|
2065
|
+
}
|
|
2066
|
+
return false;
|
|
2067
|
+
}
|
|
1334
2068
|
var lastPart = (s) => s.split(".").pop() ?? s;
|
|
1335
|
-
var
|
|
2069
|
+
var TEMPLATED = /\{\{|\}\}|\$\{/;
|
|
2070
|
+
function pathIsAName(path) {
|
|
2071
|
+
const p = (path ?? "").trim().replace(/\(\s*[^)]*\)$/, "");
|
|
2072
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(p) && /[a-z][A-Z]|_|\$/.test(p);
|
|
2073
|
+
}
|
|
2074
|
+
var serviceAddress = (f) => f.host !== void 0 && /^service$/i.test(f.resourceType ?? "") ? clusterServiceAddress(f.host) : void 0;
|
|
2075
|
+
var repoModule = (exposes) => {
|
|
2076
|
+
const m = /^([^/]+)\/([^/]+)$/.exec(exposes.trim());
|
|
2077
|
+
return m ? { repo: m[1], module: m[2] } : void 0;
|
|
2078
|
+
};
|
|
1336
2079
|
var routeOrWord = (v) => v.includes("/") ? { kind: "route", value: v } : { kind: "word", value: v };
|
|
1337
2080
|
function claimsOf(f) {
|
|
1338
2081
|
switch (f.kind) {
|
|
@@ -1368,6 +2111,12 @@ function claimsOf(f) {
|
|
|
1368
2111
|
}
|
|
1369
2112
|
case "flow":
|
|
1370
2113
|
return [routeOrWord(f.cite.value)];
|
|
2114
|
+
// ADR 0163: the value as written, on its line.
|
|
2115
|
+
case "term":
|
|
2116
|
+
return [{ kind: "word", value: f.value }];
|
|
2117
|
+
// ADR 0180: the signal the type rests on, as written on its line.
|
|
2118
|
+
case "component":
|
|
2119
|
+
return [{ kind: "word", value: f.cite.value }];
|
|
1371
2120
|
}
|
|
1372
2121
|
}
|
|
1373
2122
|
var sameCommit = (a, b) => a.startsWith(b) || b.startsWith(a);
|
|
@@ -1402,11 +2151,47 @@ function refsOf(f) {
|
|
|
1402
2151
|
}
|
|
1403
2152
|
return out;
|
|
1404
2153
|
}
|
|
2154
|
+
function withCanonicalFiles(f, canonical) {
|
|
2155
|
+
const walk = (x) => {
|
|
2156
|
+
if (Array.isArray(x)) {
|
|
2157
|
+
const out2 = x.map(walk);
|
|
2158
|
+
return out2.some((v, i) => v !== x[i]) ? out2 : x;
|
|
2159
|
+
}
|
|
2160
|
+
if (!x || typeof x !== "object")
|
|
2161
|
+
return x;
|
|
2162
|
+
const o = x;
|
|
2163
|
+
let out;
|
|
2164
|
+
for (const [k, v] of Object.entries(o)) {
|
|
2165
|
+
const w = k === "file" && typeof v === "string" && typeof o.line === "number" ? canonical(v) : walk(v);
|
|
2166
|
+
if (w !== v)
|
|
2167
|
+
(out ??= { ...o })[k] = w;
|
|
2168
|
+
}
|
|
2169
|
+
return out ?? o;
|
|
2170
|
+
};
|
|
2171
|
+
return walk(f);
|
|
2172
|
+
}
|
|
1405
2173
|
async function verifyFacts(o) {
|
|
2174
|
+
const rules = readRulesOf(o.conventions);
|
|
1406
2175
|
const carriedFrom = /* @__PURE__ */ new Map();
|
|
1407
2176
|
const stale = /* @__PURE__ */ new Map();
|
|
1408
2177
|
const corrected = applyCorrections(o.facts, o.corrections ?? []);
|
|
1409
|
-
|
|
2178
|
+
let fileList;
|
|
2179
|
+
const listed2 = () => fileList ??= o.source.list();
|
|
2180
|
+
let listedSet;
|
|
2181
|
+
let folded;
|
|
2182
|
+
const fold = (p) => p.replace(/\\/g, "/").toLowerCase();
|
|
2183
|
+
const canonical = (path) => {
|
|
2184
|
+
listedSet ??= new Set(listed2());
|
|
2185
|
+
if (listedSet.has(path))
|
|
2186
|
+
return path;
|
|
2187
|
+
if (!folded) {
|
|
2188
|
+
folded = /* @__PURE__ */ new Map();
|
|
2189
|
+
for (const p of listed2())
|
|
2190
|
+
folded.set(fold(p), folded.has(fold(p)) ? null : p);
|
|
2191
|
+
}
|
|
2192
|
+
return folded.get(fold(path)) ?? path;
|
|
2193
|
+
};
|
|
2194
|
+
const facts = [...corrected.facts].sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0).map((f) => withCanonicalFiles(f, canonical)).map((f) => {
|
|
1410
2195
|
if (!o.commit || sameCommit(f.commit, o.commit))
|
|
1411
2196
|
return f;
|
|
1412
2197
|
const changes = changesFor(o.carry, f.commit);
|
|
@@ -1421,7 +2206,8 @@ async function verifyFacts(o) {
|
|
|
1421
2206
|
return { ...f, commit: o.commit };
|
|
1422
2207
|
});
|
|
1423
2208
|
const readLines = (path) => {
|
|
1424
|
-
|
|
2209
|
+
listedSet ??= new Set(listed2());
|
|
2210
|
+
const text = insideRepo(path) && !isSecretPath(path) && listedSet.has(path) ? o.source.read(path) : void 0;
|
|
1425
2211
|
return text === void 0 ? void 0 : text.replace(/\r\n?/g, "\n").split("\n");
|
|
1426
2212
|
};
|
|
1427
2213
|
const linesCache = /* @__PURE__ */ new Map();
|
|
@@ -1438,9 +2224,8 @@ async function verifyFacts(o) {
|
|
|
1438
2224
|
linesCache.delete(linesCache.keys().next().value);
|
|
1439
2225
|
return lines;
|
|
1440
2226
|
};
|
|
1441
|
-
|
|
1442
|
-
const
|
|
1443
|
-
const checkCite = (cite2, claims, label, withHint = true) => {
|
|
2227
|
+
const deriveEnv = { lines: linesOf2, files: listed2 };
|
|
2228
|
+
const checkCite = (cite2, claims, label, withHint = true, alsoHolds) => {
|
|
1444
2229
|
const where = `${cite2.file}:${cite2.line}`;
|
|
1445
2230
|
if (!insideRepo(cite2.file))
|
|
1446
2231
|
return {
|
|
@@ -1504,7 +2289,7 @@ async function verifyFacts(o) {
|
|
|
1504
2289
|
].flatMap((at) => derivedNamesAt(at.file, linesOf2(at.file) ?? [], at.line, deriveEnv));
|
|
1505
2290
|
const holds = (c) => claimOnLines(lines, cite2.line, c) || // a framework that routes by file place (Razor Pages, WebForms, Next.js)
|
|
1506
2291
|
c.kind === "route" && pageRouteMatches(cite2.file, lines, (p) => linesOf2(p) !== void 0, c.value) || parts.length > 0 && (c.kind === "route" ? partsNamePath([...near(lines, cite2.line), ...partLines], c.value) : partLines.some((l) => lineNamesClaim(l, c)) || // a word put together from parts (`namespace:task`, a key a helper prefixes)
|
|
1507
|
-
c.kind === "word" && partsBuildWord(c.value, parts.map((p) => p.value), near(lines, cite2.line))) || derivedHolds(c, derivedHere(), partLines, partsNamePath);
|
|
2292
|
+
c.kind === "word" && partsBuildWord(c.value, parts.map((p) => p.value), near(lines, cite2.line))) || o.derivedNames !== false && derivedHolds(c, derivedHere(), partLines, partsNamePath) || alsoHolds !== void 0 && alsoHolds(lines, cite2.line);
|
|
1508
2293
|
if (claims && !claims.some(holds))
|
|
1509
2294
|
return {
|
|
1510
2295
|
code: "claim-not-on-line",
|
|
@@ -1541,6 +2326,9 @@ async function verifyFacts(o) {
|
|
|
1541
2326
|
return value.includes("/") ? [routeOrWord(value), { kind: "word", value }] : [routeOrWord(value)];
|
|
1542
2327
|
return NEVER_CHECKED_STEPS.includes(kind) ? void 0 : [routeOrWord(value)];
|
|
1543
2328
|
};
|
|
2329
|
+
const estate = o.estate ? new Set(o.estate.components.map(factIdPart)) : void 0;
|
|
2330
|
+
const serves2 = facts.some((f) => f.kind === "entry");
|
|
2331
|
+
const repoComponents = new Map(Object.entries(o.estate?.repoComponents ?? {}).map(([r, cs]) => [factIdPart(r), new Set(cs.map(factIdPart))]));
|
|
1544
2332
|
for (const f of facts) {
|
|
1545
2333
|
const label = `${f.kind} ${f.id}`;
|
|
1546
2334
|
if (o.commit && !sameCommit(f.commit, o.commit)) {
|
|
@@ -1554,7 +2342,7 @@ async function verifyFacts(o) {
|
|
|
1554
2342
|
problem(f, "wrong-commit", `${label} was read at ${f.commit}, not at ${o.commit}; read it again`);
|
|
1555
2343
|
continue;
|
|
1556
2344
|
}
|
|
1557
|
-
add(f, checkCite(f.cite, claimsOf(f), label));
|
|
2345
|
+
add(f, checkCite(f.cite, claimsOf(f), label, true, f.kind === "entry" || f.kind === "call" ? (ls, n) => apigeePassThrough({ kind: f.kind, forwards: f.kind === "call" && !!f.from }, f.cite.file, ls, n, f.path) : void 0));
|
|
1558
2346
|
if (f.kind === "entry" && f.authCite)
|
|
1559
2347
|
add(f, checkCite({ ...f.authCite }, [{ kind: "word", value: f.authCite.value }], `${label} authCite${f.auth ? ` (auth ${f.auth})` : ""}`, false));
|
|
1560
2348
|
if (f.kind === "entry" && f.authCite && f.auth) {
|
|
@@ -1566,8 +2354,80 @@ async function verifyFacts(o) {
|
|
|
1566
2354
|
(f.scopes ?? []).forEach((s, i) => add(f, checkCite(s, [{ kind: "word", value: s.value }], `${label} scope ${i + 1} ("${s.value}")`)));
|
|
1567
2355
|
if (f.kind === "entry")
|
|
1568
2356
|
(f.callers ?? []).forEach((c, i) => add(f, checkCite(c, [{ kind: "word", value: c.value }], `${label} caller ${i + 1} (${c.as} "${c.value}")`)));
|
|
2357
|
+
if (f.kind === "call" && (f.callType === "rest" || f.callType === "soap") && pathIsAName(f.path))
|
|
2358
|
+
problem(f, "call-path-is-a-name", `${label} path "${f.path}" is the name of what holds the path, not the path: follow it to where it is set and write the literal path, or the setting's placeholder with its whole key (\`\${forge.uris.refund}\`) with the line that sets it in cite.with, and record the setting as a config fact`);
|
|
2359
|
+
if (f.kind === "deploy" && (f.host !== void 0 || f.basePath !== void 0)) {
|
|
2360
|
+
if (f.host !== void 0 && TEMPLATED.test(f.host))
|
|
2361
|
+
problem(f, "exposure-template", `${label} host "${f.host}" is a template: give the host the values file sets, with that line in cite.with (one fact per environment's values file, each with its env)`);
|
|
2362
|
+
else if (f.host !== void 0 && serviceAddress(f) !== void 0) {
|
|
2363
|
+
const a = serviceAddress(f);
|
|
2364
|
+
const parts = [
|
|
2365
|
+
["name", a.name],
|
|
2366
|
+
["namespace", a.namespace],
|
|
2367
|
+
...a.port !== void 0 ? [["port", String(a.port)]] : []
|
|
2368
|
+
];
|
|
2369
|
+
for (const [what, value] of parts)
|
|
2370
|
+
add(f, checkCite(f.cite, [{ kind: "word", value }], `${label} host's ${what}`, false));
|
|
2371
|
+
} else if (f.host !== void 0)
|
|
2372
|
+
add(f, checkCite(f.cite, [{ kind: "word", value: f.host }], `${label} host`, false));
|
|
2373
|
+
if (f.basePath !== void 0 && isPatternPath(f.basePath))
|
|
2374
|
+
problem(f, "exposure-regex", `${label} basePath "${f.basePath}" is a regular expression: write its plain prefix, "${plainPathPrefix(f.basePath)}" (the segments before the first pattern), and keep the line that writes the expression cited`);
|
|
2375
|
+
else if (f.basePath !== void 0)
|
|
2376
|
+
add(f, checkCite(f.cite, [routeOrWord(f.basePath), { kind: "word", value: f.basePath }], `${label} basePath`, false));
|
|
2377
|
+
const inModule = f.exposes !== void 0 ? repoModule(f.exposes) : void 0;
|
|
2378
|
+
if (inModule && estate && estate.has(factIdPart(inModule.repo))) {
|
|
2379
|
+
const known = repoComponents.get(factIdPart(inModule.repo));
|
|
2380
|
+
if (known && !known.has(factIdPart(inModule.module)))
|
|
2381
|
+
problem(f, "exposure-component", `${label} exposes "${f.exposes}": ${inModule.repo}'s facts name no component ${inModule.module} (they name ${[...known].sort().join(", ")}); name the module as its facts do, or write a question when no file says which`);
|
|
2382
|
+
} else if (f.exposes !== void 0 && estate && !estate.has(factIdPart(inModule?.repo ?? f.exposes)))
|
|
2383
|
+
problem(f, "exposure-component", `${label} exposes "${f.exposes}", which is no component or repo of the estate (repos.manifest.yaml): name the component the route reaches, or write a question when no file says which`);
|
|
2384
|
+
if (f.exposes === void 0 && !serves2)
|
|
2385
|
+
problem(f, "exposure-component", `${label} says where a component is reached and names none: this repo's facts serve nothing, so its routes reach another repo's component; name it in exposes (one of the estate's components), or write a question when no file says which`);
|
|
2386
|
+
}
|
|
2387
|
+
if (f.kind === "call") {
|
|
2388
|
+
for (const [what, claim, at] of [
|
|
2389
|
+
["auth", f.auth, f.authCite],
|
|
2390
|
+
["tls", f.tls, f.tlsCite]
|
|
2391
|
+
]) {
|
|
2392
|
+
if (claim !== void 0 && !at && f.by !== "person")
|
|
2393
|
+
problem(f, "security-uncited", `${label} claims ${what} ${claim} and cites no line for it: give ${what}Cite, the line that sets it (a client certificate, a token, an https URL) and what it writes there, or leave ${what} out`);
|
|
2394
|
+
if (!at)
|
|
2395
|
+
continue;
|
|
2396
|
+
const bad = checkCite({ ...at }, [{ kind: "word", value: at.value }], `${label} ${what}Cite${claim ? ` (${what} ${claim})` : ""}`, false);
|
|
2397
|
+
add(f, bad);
|
|
2398
|
+
if (bad || claim === void 0)
|
|
2399
|
+
continue;
|
|
2400
|
+
const lines = [at, ...at.with ?? []].flatMap((p) => {
|
|
2401
|
+
const l = lineOfValue(linesOf2(p.file), p.line, p.value);
|
|
2402
|
+
return l === void 0 ? [] : [l];
|
|
2403
|
+
});
|
|
2404
|
+
const needs = securityLineKind(what, claim, lines);
|
|
2405
|
+
if (needs)
|
|
2406
|
+
problem(f, "security-line-kind", `${label} claims ${what} ${claim} at ${at.file}:${at.line}${at.with?.length ? " and its parts" : ""}, which write${at.with?.length ? "" : "s"} no ${needs}: cite the line that sets it (with the line that builds it in ${what}Cite.with), or write the ${what} the line sets`);
|
|
2407
|
+
}
|
|
2408
|
+
}
|
|
2409
|
+
if (f.kind === "term") {
|
|
2410
|
+
const m = f.meaning;
|
|
2411
|
+
if (m && m.file !== f.cite.file)
|
|
2412
|
+
problem(f, "term-meaning", `${label} says ${m.file}:${m.line} gives "${f.value}" its meaning, and the value is in ${f.cite.file}: cite the line of the same file (the list's or the block's key) that does`);
|
|
2413
|
+
else if (m && m.line > f.cite.line)
|
|
2414
|
+
problem(f, "term-meaning", `${label} says line ${m.line} gives "${f.value}" its meaning, below the value's line ${f.cite.line}: cite the line above it (the list's or the block's key) that does`);
|
|
2415
|
+
else if (m)
|
|
2416
|
+
add(f, checkCite({ ...m }, [{ kind: "word", value: m.value }], `${label} meaning`, false));
|
|
2417
|
+
const bad = f.note !== void 0 && o.notes ? noteProblem(f.note, o.notes) : void 0;
|
|
2418
|
+
if (bad)
|
|
2419
|
+
problem(f, bad.code, `${label} ${bad.message}`);
|
|
2420
|
+
}
|
|
2421
|
+
if (f.kind === "component") {
|
|
2422
|
+
if (o.componentTypes && !o.componentTypes.has(f.type))
|
|
2423
|
+
problem(f, "component-type", `${label} says the component is a ${f.type}, which isn't one of the organisation's component types: add it (org-pack/component-types/${f.type}/type.yaml) or say one of ${[...o.componentTypes].join(", ")}`);
|
|
2424
|
+
const subject = factIdPart(f.component ?? f.repo);
|
|
2425
|
+
const first = facts.find((x) => x.kind === "component" && factIdPart(x.component ?? x.repo) === subject && (x === f || !found.has(x.id)));
|
|
2426
|
+
if (first && first !== f)
|
|
2427
|
+
problem(f, "component-type", `${label} says what ${subject} is a second time (${first.id} already does): one fact says what a component is`);
|
|
2428
|
+
}
|
|
1569
2429
|
if (f.kind === "framework") {
|
|
1570
|
-
const bad = frameworkNameProblem(f);
|
|
2430
|
+
const bad = frameworkNameProblem(f, rules.frameworks);
|
|
1571
2431
|
if (bad)
|
|
1572
2432
|
problem(f, "framework-name", `${label} ${bad}`);
|
|
1573
2433
|
if (f.version !== void 0)
|
|
@@ -1656,36 +2516,49 @@ async function verifyFacts(o) {
|
|
|
1656
2516
|
return rest;
|
|
1657
2517
|
}
|
|
1658
2518
|
const q = redactSecrets(c.quote);
|
|
2519
|
+
if (q === c.quote && quoteHoldsSecret(q)) {
|
|
2520
|
+
redacted++;
|
|
2521
|
+
const { quote: _secret, ...rest } = c;
|
|
2522
|
+
return rest;
|
|
2523
|
+
}
|
|
1659
2524
|
if (q !== c.quote)
|
|
1660
2525
|
redacted++;
|
|
1661
2526
|
return { ...c, quote: q };
|
|
1662
2527
|
};
|
|
1663
2528
|
const authUncited = [];
|
|
2529
|
+
const methodUnseen = [];
|
|
2530
|
+
const kept = new Map(facts.filter((f) => !rejected.has(f.id)).map((f) => [f.id, f]));
|
|
2531
|
+
const factById = (id) => kept.get(id);
|
|
1664
2532
|
const accepted = facts.filter((f) => !rejected.has(f.id)).map((f) => {
|
|
1665
2533
|
o.room?.();
|
|
1666
2534
|
const verified = f.by === "person" && f.verified !== "none" ? f.verified : "line";
|
|
1667
|
-
const
|
|
1668
|
-
if (
|
|
2535
|
+
const base2 = { ...f, verified, cite: fixQuote(f.cite) };
|
|
2536
|
+
if (base2.kind === "entry" && base2.auth !== void 0 && !base2.authCite && f.by !== "person")
|
|
1669
2537
|
authUncited.push({
|
|
1670
|
-
id:
|
|
1671
|
-
auth:
|
|
1672
|
-
cite: `${
|
|
2538
|
+
id: base2.id,
|
|
2539
|
+
auth: base2.auth,
|
|
2540
|
+
cite: `${base2.cite.file}:${base2.cite.line}`
|
|
1673
2541
|
});
|
|
1674
|
-
|
|
2542
|
+
const unseen = methodUnseenOf(base2, linesOf2, factById);
|
|
2543
|
+
if (unseen)
|
|
2544
|
+
methodUnseen.push(unseen);
|
|
2545
|
+
if (base2.kind === "flow")
|
|
1675
2546
|
return {
|
|
1676
|
-
...
|
|
1677
|
-
steps:
|
|
1678
|
-
errors:
|
|
2547
|
+
...base2,
|
|
2548
|
+
steps: base2.steps.map((s) => ({ ...s, cite: fixQuote(s.cite) })),
|
|
2549
|
+
errors: base2.errors.map((e) => ({ ...e, cite: fixQuote(e.cite) }))
|
|
1679
2550
|
};
|
|
1680
|
-
return
|
|
2551
|
+
return base2;
|
|
1681
2552
|
});
|
|
1682
2553
|
const excludeRes = (o.exclude ?? []).map(globToRegExp);
|
|
1683
2554
|
const sources = [];
|
|
1684
2555
|
const excluded = [];
|
|
1685
2556
|
for (const p of o.source.list()) {
|
|
1686
2557
|
o.room?.();
|
|
2558
|
+
if (!isSourceFile(p, "", void 0, rules) || tooBigToRead(o.source, p))
|
|
2559
|
+
continue;
|
|
1687
2560
|
const ls = readLines(p) ?? [""];
|
|
1688
|
-
if (!isSourceFile(p, ls[0] ?? "", ls))
|
|
2561
|
+
if (!isSourceFile(p, ls[0] ?? "", ls, rules))
|
|
1689
2562
|
continue;
|
|
1690
2563
|
if (excludeRes.some((re) => re.test(p)))
|
|
1691
2564
|
excluded.push(p);
|
|
@@ -1701,6 +2574,8 @@ async function verifyFacts(o) {
|
|
|
1701
2574
|
const reviewedSet = /* @__PURE__ */ new Set();
|
|
1702
2575
|
const reviewed = [];
|
|
1703
2576
|
const reviewedProblems = [];
|
|
2577
|
+
const reviewedDropped = [];
|
|
2578
|
+
const reviewedGlobs = [];
|
|
1704
2579
|
for (const r of o.reviewed ?? []) {
|
|
1705
2580
|
const refuse = (message) => reviewedProblems.push({
|
|
1706
2581
|
...r.line !== void 0 ? { line: r.line } : {},
|
|
@@ -1716,36 +2591,105 @@ async function verifyFacts(o) {
|
|
|
1716
2591
|
refuse("is a secret's file, which is never read into a map");
|
|
1717
2592
|
continue;
|
|
1718
2593
|
}
|
|
1719
|
-
const
|
|
1720
|
-
|
|
2594
|
+
const range = lineRangeOf(r.path);
|
|
2595
|
+
if (range) {
|
|
2596
|
+
const text = allFiles.includes(range.file) ? o.source.read(range.file) : void 0;
|
|
2597
|
+
if (text === void 0) {
|
|
2598
|
+
refuse("is not in the repo at this commit; name a file you read");
|
|
2599
|
+
continue;
|
|
2600
|
+
}
|
|
2601
|
+
const last = text.split(/\r?\n/).length;
|
|
2602
|
+
if (range.from < 1 || range.from > range.to || range.to > last) {
|
|
2603
|
+
refuse(`is past the file's last line (${last}) or out of order; name the lines you read`);
|
|
2604
|
+
continue;
|
|
2605
|
+
}
|
|
2606
|
+
}
|
|
2607
|
+
const target = range ? range.file : r.path;
|
|
2608
|
+
const re = !range && isGlob(r.path) ? globToRegExp(r.path) : void 0;
|
|
2609
|
+
const matched = re ? allFiles.filter((p) => re.test(p)) : allFiles.filter((p) => p === target);
|
|
1721
2610
|
if (!matched.length) {
|
|
1722
|
-
|
|
2611
|
+
if (re)
|
|
2612
|
+
reviewedDropped.push({
|
|
2613
|
+
...r.line !== void 0 ? { line: r.line } : {},
|
|
2614
|
+
path: r.path,
|
|
2615
|
+
why: r.why,
|
|
2616
|
+
message: `${r.line !== void 0 ? `line ${r.line}: ` : ""}${r.path} matches no file in the repo at this commit; dropped (it stands for no file)`
|
|
2617
|
+
});
|
|
2618
|
+
else
|
|
2619
|
+
refuse("is not in the repo at this commit; name a file you read");
|
|
2620
|
+
continue;
|
|
2621
|
+
}
|
|
2622
|
+
const entryPoints = re ? matched.filter((p) => isSource.has(p) && !touched.has(p) && isEntryPointFile(p)) : [];
|
|
2623
|
+
if (entryPoints.length) {
|
|
2624
|
+
refuse(`covers ${entryPoints[0]}, an entry point (a controller, route, handler or listener)${entryPoints.length > 1 ? ` and ${entryPoints.length - 1} more` : ""} no fact cites: read it, and write its entries and the fields of the classes it takes and returns; never mark an entry point reviewed with a glob (name it alone if it truly holds nothing)`);
|
|
1723
2625
|
continue;
|
|
1724
2626
|
}
|
|
1725
2627
|
const files = matched.filter((p) => isSource.has(p) && !touched.has(p)).sort();
|
|
1726
2628
|
for (const p of files)
|
|
1727
2629
|
reviewedSet.add(p);
|
|
1728
2630
|
reviewed.push({ path: r.path, why: r.why, files });
|
|
2631
|
+
if (re)
|
|
2632
|
+
reviewedGlobs.push({ entry: r, files });
|
|
1729
2633
|
}
|
|
1730
2634
|
const read = sources.filter((p) => touched.has(p)).sort();
|
|
1731
2635
|
const reviewedFiles = sources.filter((p) => reviewedSet.has(p)).sort();
|
|
1732
|
-
const
|
|
2636
|
+
const leftOut = new Set(o.notReached ?? []);
|
|
2637
|
+
const notReached = sources.filter((p) => leftOut.has(p) && !touched.has(p) && !reviewedSet.has(p)).sort();
|
|
2638
|
+
const unknown = sources.filter((p) => !touched.has(p) && !reviewedSet.has(p) && !leftOut.has(p)).sort();
|
|
1733
2639
|
const total = read.length + reviewedFiles.length + unknown.length;
|
|
1734
2640
|
const ratio = total === 0 ? 1 : Math.round(read.length / total * 1e3) / 1e3;
|
|
2641
|
+
const ix = indexFacts(accepted);
|
|
2642
|
+
const specOpsUncovered = [];
|
|
2643
|
+
const specs = /* @__PURE__ */ new Set();
|
|
2644
|
+
for (const p of sources) {
|
|
2645
|
+
if (notReadForSkipped(p, rules))
|
|
2646
|
+
continue;
|
|
2647
|
+
o.room?.();
|
|
2648
|
+
const text = o.readers !== false && /\.(ya?ml|json)$/i.test(p) ? o.source.read(p) : void 0;
|
|
2649
|
+
const ops = text === void 0 ? void 0 : specOpsUncoveredIn(p, text, accepted);
|
|
2650
|
+
if (!ops)
|
|
2651
|
+
continue;
|
|
2652
|
+
specs.add(p);
|
|
2653
|
+
specOpsUncovered.push(...ops);
|
|
2654
|
+
}
|
|
2655
|
+
const skippedIn = (p) => {
|
|
2656
|
+
if (specs.has(p) || notReadForSkipped(p, rules))
|
|
2657
|
+
return [];
|
|
2658
|
+
o.room?.();
|
|
2659
|
+
const ls = readLines(p);
|
|
2660
|
+
return ls ? candidatesIn(p, ls).flatMap((c) => factsCover(p, c, ix) ? [] : [skippedOf(p, c)]) : [];
|
|
2661
|
+
};
|
|
2662
|
+
const skipped = read.flatMap(skippedIn);
|
|
2663
|
+
const reviewedHolds = reviewedGlobs.flatMap(({ entry, files }) => {
|
|
2664
|
+
const held = files.flatMap(skippedIn);
|
|
2665
|
+
return held.length ? [reviewedHoldsOf(entry, held)] : [];
|
|
2666
|
+
});
|
|
1735
2667
|
return {
|
|
1736
2668
|
accepted,
|
|
1737
2669
|
// a rejected fact goes back as it was read, at its own commit, even one that was carried
|
|
1738
2670
|
rejected: [...rejected.values()].map((r) => carriedFrom.has(r.fact.id) ? { ...r, fact: { ...r.fact, commit: carriedFrom.get(r.fact.id) } } : r).sort((a, b) => a.fact.id < b.fact.id ? -1 : a.fact.id > b.fact.id ? 1 : 0),
|
|
1739
|
-
coverage: {
|
|
2671
|
+
coverage: {
|
|
2672
|
+
read,
|
|
2673
|
+
reviewed: reviewedFiles,
|
|
2674
|
+
unknown,
|
|
2675
|
+
excluded: excluded.sort(),
|
|
2676
|
+
notReached,
|
|
2677
|
+
ratio
|
|
2678
|
+
},
|
|
1740
2679
|
redacted,
|
|
1741
2680
|
quotesDropped,
|
|
1742
2681
|
carried: accepted.flatMap((f) => carriedFrom.has(f.id) ? [{ id: f.id, from: carriedFrom.get(f.id) }] : []),
|
|
1743
2682
|
corrections: { applied: corrected.applied, stale: corrected.stale },
|
|
1744
|
-
entriesNoFlowFollows: entriesNoFlowFollows(accepted),
|
|
2683
|
+
entriesNoFlowFollows: entriesNoFlowFollows(accepted, rules),
|
|
1745
2684
|
reviewed,
|
|
1746
2685
|
reviewedProblems,
|
|
2686
|
+
reviewedDropped,
|
|
1747
2687
|
authUncited: authUncited.sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0),
|
|
1748
|
-
|
|
2688
|
+
methodUnseen: methodUnseen.sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0),
|
|
2689
|
+
skipped,
|
|
2690
|
+
reviewedHolds,
|
|
2691
|
+
specOpsUncovered,
|
|
2692
|
+
frameworkGaps: frameworkGaps(accepted, rules.frameworks)
|
|
1749
2693
|
};
|
|
1750
2694
|
}
|
|
1751
2695
|
|
|
@@ -1758,11 +2702,11 @@ var ENTRY_DIR = /(^|\/)(controllers?|routes|routers|handlers|api|endpoints|jobs|
|
|
|
1758
2702
|
var BOUNDARY_NAME = /(client|gateway|repository|repo|dao|mapper|entity|model|dto|request|response|publisher|producer|store|cache|schema)$/i;
|
|
1759
2703
|
var BOUNDARY_FILE = /\.(sql|proto|graphql|gql|avsc|wsdl|xsd)$|(^|\/)(openapi|swagger)[^/]*\.(ya?ml|json)$/i;
|
|
1760
2704
|
var BOUNDARY_DIR = /(^|\/)(clients?|gateways?|repositories|dao|models?|entities|dto|migrations?)\//i;
|
|
1761
|
-
var TEST = /(^|\/)(test|tests|__tests__|spec|specs|e2e|it)\/|[._-](test|spec)\.[^/.]+$|(Test|Tests|IT|Spec)\.[^/.]+$|(^|\/)test_[^/]+$|_test\.go$/;
|
|
1762
|
-
var
|
|
1763
|
-
const
|
|
1764
|
-
const dot =
|
|
1765
|
-
return dot > 0 ?
|
|
2705
|
+
var TEST = /(^|\/)(test|tests|__tests__|spec|specs|e2e|it)\/|(^|\/)(test|tests)[-_][a-z0-9_-]+\/|[._-](test|spec)\.[^/.]+$|(Test|Tests|IT|Spec)\.[^/.]+$|(^|\/)test_[^/]+$|_test\.go$/;
|
|
2706
|
+
var stem2 = (path) => {
|
|
2707
|
+
const base2 = path.slice(path.lastIndexOf("/") + 1);
|
|
2708
|
+
const dot = base2.indexOf(".");
|
|
2709
|
+
return dot > 0 ? base2.slice(0, dot) : base2;
|
|
1766
2710
|
};
|
|
1767
2711
|
function readingRank(path) {
|
|
1768
2712
|
if (TEST.test(path))
|
|
@@ -1771,7 +2715,7 @@ function readingRank(path) {
|
|
|
1771
2715
|
return 0;
|
|
1772
2716
|
if (CONFIG.test(path))
|
|
1773
2717
|
return 1;
|
|
1774
|
-
const s =
|
|
2718
|
+
const s = stem2(path);
|
|
1775
2719
|
if (ENTRY_NAME.test(s) || ENTRY_DIR.test(path))
|
|
1776
2720
|
return 2;
|
|
1777
2721
|
if (BOUNDARY_NAME.test(s) || BOUNDARY_FILE.test(path) || BOUNDARY_DIR.test(path))
|
|
@@ -1819,9 +2763,9 @@ var CONFIG_CODE = /(^|\/)(config|configuration|settings|environments?)(\/|\.[^/]
|
|
|
1819
2763
|
var IAC = /\.(tf|tfvars|hcl|bicep)$|(^|\/)(serverless|template)[^/]*\.ya?ml$|(^|\/)(cdk|pulumi|terraform)\//;
|
|
1820
2764
|
var DATA_FILE = /\.sql$|(^|\/)(migrations?|db|database|changelogs?|liquibase|flyway)\//i;
|
|
1821
2765
|
var CONTRACT = /\.(proto|avsc|wsdl|xsd)$/i;
|
|
1822
|
-
var API_SCHEMA = /\.(graphql|gql)
|
|
2766
|
+
var API_SCHEMA = /\.(graphql|gql)$/i;
|
|
1823
2767
|
var STRUCTURED = /\.(ya?ml|json|xml|config|properties|toml|ini|conf)$/i;
|
|
1824
|
-
function fileKinds(path) {
|
|
2768
|
+
function fileKinds(path, rules = DEFAULT_READ_RULES) {
|
|
1825
2769
|
if (GATEWAY_FILE.test(path))
|
|
1826
2770
|
return [...GATEWAY_KINDS];
|
|
1827
2771
|
const k = /* @__PURE__ */ new Set();
|
|
@@ -1837,7 +2781,7 @@ function fileKinds(path) {
|
|
|
1837
2781
|
add(["data"]);
|
|
1838
2782
|
if (CONTRACT.test(path))
|
|
1839
2783
|
add(["protocols"]);
|
|
1840
|
-
if (API_SCHEMA.test(path))
|
|
2784
|
+
if (API_SCHEMA.test(path) || rules.apiSpec.test(path))
|
|
1841
2785
|
add(["routes", "protocols"]);
|
|
1842
2786
|
const isCode = SERVER_CODE.test(path) || DEVICE_CODE.test(path) || SCRIPT_CODE.test(path);
|
|
1843
2787
|
if (PAGE_CODE.test(path) || DEVICE_CODE.test(path))
|
|
@@ -1873,27 +2817,45 @@ var linesOf = (text) => {
|
|
|
1873
2817
|
const t = text.replace(/\r\n?/g, "\n");
|
|
1874
2818
|
return (t.endsWith("\n") ? t.slice(0, -1) : t).split("\n");
|
|
1875
2819
|
};
|
|
2820
|
+
var LONG_LINE = 1e3;
|
|
2821
|
+
function shownLine(line) {
|
|
2822
|
+
if (line.length <= LONG_LINE)
|
|
2823
|
+
return line;
|
|
2824
|
+
const code2 = line.charCodeAt(LONG_LINE - 1);
|
|
2825
|
+
const at = code2 >= 55296 && code2 <= 56319 ? LONG_LINE - 1 : LONG_LINE;
|
|
2826
|
+
return `${line.slice(0, at)} \u2026 [line cut: ${line.length} characters]`;
|
|
2827
|
+
}
|
|
1876
2828
|
function numberedSlice(text, s) {
|
|
1877
|
-
return linesOf(text).slice(s.from - 1, s.to).map((l, i) => `${s.from + i}| ${l}`).join("\n");
|
|
2829
|
+
return linesOf(text).slice(s.from - 1, s.to).map((l, i) => `${s.from + i}| ${shownLine(l)}`).join("\n");
|
|
1878
2830
|
}
|
|
1879
2831
|
var header = (s) => `${s.path} lines ${s.from}-${s.to} of ${s.of} (numbered as in the file)`;
|
|
1880
2832
|
function sliceText(source, s) {
|
|
1881
2833
|
return `${header(s)}
|
|
1882
2834
|
${numberedSlice(source.read(s.path) ?? "", s)}`;
|
|
1883
2835
|
}
|
|
1884
|
-
var tokensOf = (
|
|
2836
|
+
var tokensOf = (units) => Math.ceil(units / 4);
|
|
2837
|
+
var wrapUnits = (path) => tokenUnits(`<atticus-data id="1-file-0000" kind="code" origin="${path}#L0000000-0000000">
|
|
2838
|
+
|
|
2839
|
+
</atticus-data>
|
|
2840
|
+
|
|
2841
|
+
`);
|
|
1885
2842
|
function planBatches(source, o) {
|
|
1886
2843
|
const room = Math.max(1, o.room);
|
|
2844
|
+
const rules = readRulesOf(o.conventions);
|
|
1887
2845
|
const excludeRes = (o.exclude ?? []).map(globToRegExp);
|
|
1888
2846
|
const excluded = [];
|
|
2847
|
+
const leaveOut = new Set(o.reach?.leaveOut ?? []);
|
|
2848
|
+
const notReached = [];
|
|
1889
2849
|
const paths = [];
|
|
1890
2850
|
const texts = /* @__PURE__ */ new Map();
|
|
1891
2851
|
for (const p of source.list()) {
|
|
2852
|
+
if (!isSourceFile(p, "", void 0, rules) || tooBigToRead(source, p))
|
|
2853
|
+
continue;
|
|
1892
2854
|
const text = source.read(p);
|
|
1893
2855
|
if (text === void 0)
|
|
1894
2856
|
continue;
|
|
1895
2857
|
const ls = linesOf(text);
|
|
1896
|
-
if (!isSourceFile(p, ls[0] ?? "", ls))
|
|
2858
|
+
if (!isSourceFile(p, ls[0] ?? "", ls, rules))
|
|
1897
2859
|
continue;
|
|
1898
2860
|
if (excludeRes.some((re) => re.test(p))) {
|
|
1899
2861
|
excluded.push(p);
|
|
@@ -1901,6 +2863,10 @@ function planBatches(source, o) {
|
|
|
1901
2863
|
}
|
|
1902
2864
|
if (!text.trim())
|
|
1903
2865
|
continue;
|
|
2866
|
+
if (leaveOut.has(p)) {
|
|
2867
|
+
notReached.push(p);
|
|
2868
|
+
continue;
|
|
2869
|
+
}
|
|
1904
2870
|
paths.push(p);
|
|
1905
2871
|
texts.set(p, text);
|
|
1906
2872
|
}
|
|
@@ -1916,16 +2882,17 @@ function planBatches(source, o) {
|
|
|
1916
2882
|
const slices = [];
|
|
1917
2883
|
let lines = 0;
|
|
1918
2884
|
let leastRoom = room;
|
|
1919
|
-
for (const path of
|
|
1920
|
-
const kinds2 = fileKinds(path);
|
|
2885
|
+
for (const path of reachOrder(paths, o.reach?.first)) {
|
|
2886
|
+
const kinds2 = fileKinds(path, rules);
|
|
1921
2887
|
const left = room - overhead(kinds2);
|
|
1922
2888
|
leastRoom = Math.min(leastRoom, left);
|
|
1923
2889
|
const fileRoom = Math.max(1, left);
|
|
1924
2890
|
const all2 = linesOf(texts.get(path));
|
|
1925
2891
|
const of = all2.length;
|
|
1926
2892
|
lines += of;
|
|
1927
|
-
const sizes = all2.map((l, i) => `${i + 1}| ${l}
|
|
1928
|
-
const
|
|
2893
|
+
const sizes = all2.map((l, i) => tokenUnits(`${i + 1}| ${shownLine(l)}`) + 1);
|
|
2894
|
+
const wrap = wrapUnits(path);
|
|
2895
|
+
const cost = (from2, units) => tokensOf(tokenUnits(header({ path, from: from2, to: of, of })) + wrap + units);
|
|
1929
2896
|
const total = sizes.reduce((n, c) => n + c, 0);
|
|
1930
2897
|
if (cost(1, total) <= fileRoom) {
|
|
1931
2898
|
slices.push({ path, from: 1, to: of, of, tokens: cost(1, total), kinds: kinds2 });
|
|
@@ -1965,9 +2932,20 @@ function planBatches(source, o) {
|
|
|
1965
2932
|
tokens: slices.reduce((n, s) => n + s.tokens, 0),
|
|
1966
2933
|
batches,
|
|
1967
2934
|
excluded: excluded.sort(),
|
|
1968
|
-
leastRoom
|
|
2935
|
+
leastRoom,
|
|
2936
|
+
notReached: notReached.sort()
|
|
1969
2937
|
};
|
|
1970
2938
|
}
|
|
2939
|
+
function reachOrder(paths, first) {
|
|
2940
|
+
const order = readingOrder(paths);
|
|
2941
|
+
if (!first?.length)
|
|
2942
|
+
return order;
|
|
2943
|
+
const has = new Set(paths);
|
|
2944
|
+
const head = order.filter((p) => readingRank(p) <= 1);
|
|
2945
|
+
const lead = [...new Set(first)].filter((p) => has.has(p) && readingRank(p) > 1);
|
|
2946
|
+
const taken = /* @__PURE__ */ new Set([...head, ...lead]);
|
|
2947
|
+
return [...head, ...lead, ...order.filter((p) => !taken.has(p))];
|
|
2948
|
+
}
|
|
1971
2949
|
|
|
1972
2950
|
// packages/verify/dist/vendored.js
|
|
1973
2951
|
var LICENSE_FILE = /^(LICEN[CS]E|COPYING)([._-][^/]*)?$/i;
|
|
@@ -2032,10 +3010,10 @@ function buildModules(path, text) {
|
|
|
2032
3010
|
return found.filter((m) => !m.startsWith("!")).map(clean).filter(Boolean).map((m) => dir ? `${dir}/${m}` : m);
|
|
2033
3011
|
}
|
|
2034
3012
|
var JAVA_LIKE = /\.(java|kt|kts|scala|groovy)$/i;
|
|
2035
|
-
var
|
|
3013
|
+
var DOTNET2 = /\.(cs|vb|fs)$/i;
|
|
2036
3014
|
var rootOf = (name) => name.split(".").slice(0, 2).join(".").toLowerCase();
|
|
2037
3015
|
function packageRoot(path, text) {
|
|
2038
|
-
const re = JAVA_LIKE.test(path) ? /^\s*package\s+([A-Za-z_][\w.]*)/m :
|
|
3016
|
+
const re = JAVA_LIKE.test(path) ? /^\s*package\s+([A-Za-z_][\w.]*)/m : DOTNET2.test(path) ? /^\s*namespace\s+([A-Za-z_][\w.]*)/m : void 0;
|
|
2039
3017
|
const m = re?.exec(text);
|
|
2040
3018
|
return m ? rootOf(m[1]) : void 0;
|
|
2041
3019
|
}
|
|
@@ -2053,7 +3031,7 @@ function packageRoots(paths, inits, read) {
|
|
|
2053
3031
|
}
|
|
2054
3032
|
if (top)
|
|
2055
3033
|
roots.add(nameOf(top).toLowerCase());
|
|
2056
|
-
} else if ((JAVA_LIKE.test(p) ||
|
|
3034
|
+
} else if ((JAVA_LIKE.test(p) || DOTNET2.test(p)) && readCount < PACKAGE_SAMPLE) {
|
|
2057
3035
|
readCount++;
|
|
2058
3036
|
const r = packageRoot(p, read(p) ?? "");
|
|
2059
3037
|
if (r)
|
|
@@ -2233,6 +3211,27 @@ function exclusionYaml(proposals) {
|
|
|
2233
3211
|
].join("\n");
|
|
2234
3212
|
}
|
|
2235
3213
|
var SESSION_TOKENS = 15e4;
|
|
3214
|
+
var SESSION_SHARE = 0.45;
|
|
3215
|
+
function sessionTokensFor(limit) {
|
|
3216
|
+
return limit && limit > 0 ? Math.floor(limit * SESSION_SHARE) : SESSION_TOKENS;
|
|
3217
|
+
}
|
|
3218
|
+
var RANGE_OVERLAP_LINES = 20;
|
|
3219
|
+
var RANGE_OVERLAP_SHARE = 0.02;
|
|
3220
|
+
function rangeOverlap(each) {
|
|
3221
|
+
return Math.min(Math.floor(each / 5), Math.max(RANGE_OVERLAP_LINES, Math.ceil(RANGE_OVERLAP_SHARE * each)));
|
|
3222
|
+
}
|
|
3223
|
+
function lineRanges(f, room) {
|
|
3224
|
+
const n = Math.max(1, Math.min(f.lines, Math.ceil(f.tokens / (0.8 * room))));
|
|
3225
|
+
const each = Math.ceil(f.lines / n);
|
|
3226
|
+
const overlap = rangeOverlap(each);
|
|
3227
|
+
const out = [];
|
|
3228
|
+
for (let start = 1; start <= f.lines; start += each) {
|
|
3229
|
+
const from = start === 1 ? 1 : start - overlap;
|
|
3230
|
+
const to = Math.min(f.lines, start + each - 1);
|
|
3231
|
+
out.push({ from, to, start, tokens: Math.ceil(f.tokens * (to - from + 1) / f.lines) });
|
|
3232
|
+
}
|
|
3233
|
+
return out;
|
|
3234
|
+
}
|
|
2236
3235
|
function unitsOf(dir, fs, room) {
|
|
2237
3236
|
const prefix = dir ? `${dir}/` : "";
|
|
2238
3237
|
const direct = [];
|
|
@@ -2254,15 +3253,105 @@ function unitsOf(dir, fs, room) {
|
|
|
2254
3253
|
out.push(...unitsOf(child, children.get(child), room));
|
|
2255
3254
|
return out;
|
|
2256
3255
|
}
|
|
2257
|
-
function
|
|
3256
|
+
function groupUnits(dir, fs, room, holds) {
|
|
3257
|
+
const prefix = dir ? `${dir}/` : "";
|
|
3258
|
+
const direct = [];
|
|
3259
|
+
const children = /* @__PURE__ */ new Map();
|
|
3260
|
+
for (const f of fs) {
|
|
3261
|
+
const rest = f.path.slice(prefix.length);
|
|
3262
|
+
const slash = rest.indexOf("/");
|
|
3263
|
+
if (slash < 0)
|
|
3264
|
+
direct.push(f);
|
|
3265
|
+
else {
|
|
3266
|
+
const child = prefix + rest.slice(0, slash);
|
|
3267
|
+
children.set(child, [...children.get(child) ?? [], f]);
|
|
3268
|
+
}
|
|
3269
|
+
}
|
|
3270
|
+
if (dir && holds(dir, true, fs.length) && (sum(fs) <= room || !children.size))
|
|
3271
|
+
return [{ glob: `${dir}/**`, files: fs.length, tokens: sum(fs) }];
|
|
3272
|
+
const out = [];
|
|
3273
|
+
if (direct.length) {
|
|
3274
|
+
if (holds(dir, false, direct.length))
|
|
3275
|
+
out.push({ glob: `${prefix}*`, files: direct.length, tokens: sum(direct) });
|
|
3276
|
+
else
|
|
3277
|
+
for (const f of [...direct].sort((a, b) => byPath2(a.path, b.path)))
|
|
3278
|
+
out.push({ glob: f.path, files: 1, tokens: f.tokens });
|
|
3279
|
+
}
|
|
3280
|
+
for (const child of [...children.keys()].sort(byPath2))
|
|
3281
|
+
out.push(...groupUnits(child, children.get(child), room, holds));
|
|
3282
|
+
return out;
|
|
3283
|
+
}
|
|
3284
|
+
function rangeUnits(f, room) {
|
|
3285
|
+
return lineRanges({ ...f, lines: f.lines }, room).map((r, i) => ({
|
|
3286
|
+
glob: `${f.path}#L${r.from}-L${r.to}`,
|
|
3287
|
+
files: i === 0 ? 1 : 0,
|
|
3288
|
+
tokens: r.tokens
|
|
3289
|
+
}));
|
|
3290
|
+
}
|
|
3291
|
+
function orderedUnits(files, room, o) {
|
|
3292
|
+
const isBig = (f) => f.tokens > room && (f.lines ?? 0) > 1;
|
|
3293
|
+
const byFile = new Map(files.map((f) => [f.path, f]));
|
|
3294
|
+
const head = new Set(o.head.filter((p) => byFile.has(p)));
|
|
3295
|
+
const models = [...new Set(o.models)].filter((p) => byFile.has(p) && !head.has(p));
|
|
3296
|
+
const inModels = new Set(models);
|
|
3297
|
+
const rest = files.filter((f) => !head.has(f.path) && !inModels.has(f.path));
|
|
3298
|
+
const all2 = [...files.map((f) => f.path), ...o.leftOut];
|
|
3299
|
+
const counts = () => {
|
|
3300
|
+
const deep = /* @__PURE__ */ new Map();
|
|
3301
|
+
const own = /* @__PURE__ */ new Map();
|
|
3302
|
+
const count = (m, k) => m.set(k, (m.get(k) ?? 0) + 1);
|
|
3303
|
+
for (const p of all2) {
|
|
3304
|
+
const parts = p.split("/");
|
|
3305
|
+
count(own, parts.slice(0, -1).join("/"));
|
|
3306
|
+
for (let d = 1; d < parts.length; d++)
|
|
3307
|
+
count(deep, parts.slice(0, d).join("/"));
|
|
3308
|
+
}
|
|
3309
|
+
return (dir, isDeep, n) => (isDeep ? deep.get(dir) : own.get(dir)) === n;
|
|
3310
|
+
};
|
|
3311
|
+
const holds = counts();
|
|
3312
|
+
const unitsOfGroup = (group) => {
|
|
3313
|
+
const small = group.filter((f) => !isBig(f)).sort((a, b) => byPath2(a.path, b.path));
|
|
3314
|
+
const out = groupUnits("", small, room, holds);
|
|
3315
|
+
for (const f of group.filter(isBig))
|
|
3316
|
+
out.push(...rangeUnits(f, room));
|
|
3317
|
+
return out;
|
|
3318
|
+
};
|
|
3319
|
+
return [
|
|
3320
|
+
...unitsOfGroup(files.filter((f) => head.has(f.path))),
|
|
3321
|
+
...models.flatMap((p) => {
|
|
3322
|
+
const f = byFile.get(p);
|
|
3323
|
+
return isBig(f) ? rangeUnits(f, room) : [{ glob: p, files: 1, tokens: f.tokens }];
|
|
3324
|
+
}),
|
|
3325
|
+
...unitsOfGroup(rest)
|
|
3326
|
+
];
|
|
3327
|
+
}
|
|
3328
|
+
function planParts(files, room = SESSION_TOKENS, order) {
|
|
2258
3329
|
const total = sum(files);
|
|
2259
3330
|
if (total <= room)
|
|
2260
3331
|
return [];
|
|
2261
|
-
|
|
3332
|
+
let units;
|
|
3333
|
+
if (order)
|
|
3334
|
+
units = orderedUnits(files, room, order);
|
|
3335
|
+
else {
|
|
3336
|
+
const big = files.filter((f) => f.tokens > room && (f.lines ?? 0) > 1);
|
|
3337
|
+
const rest = files.filter((f) => !big.includes(f));
|
|
3338
|
+
const ranges = [];
|
|
3339
|
+
for (const f of big)
|
|
3340
|
+
for (const [i, r] of lineRanges({ ...f, lines: f.lines }, room).entries())
|
|
3341
|
+
ranges.push({
|
|
3342
|
+
glob: `${f.path}#L${r.from}-L${r.to}`,
|
|
3343
|
+
files: i === 0 ? 1 : 0,
|
|
3344
|
+
tokens: r.tokens,
|
|
3345
|
+
key: `${f.path}\0${String(r.start).padStart(12, "0")}`
|
|
3346
|
+
});
|
|
3347
|
+
const sorted = [...rest].sort((a, b) => byPath2(a.path, b.path));
|
|
3348
|
+
const keyOf = (g) => g.replace(/\/?\*\*?$/, "");
|
|
3349
|
+
units = [...unitsOf("", sorted, room).map((u) => ({ ...u, key: keyOf(u.glob) })), ...ranges].sort((a, b) => byPath2(a.key, b.key)).map(({ key: _k, ...u }) => u);
|
|
3350
|
+
}
|
|
2262
3351
|
const target = total / Math.ceil(total / room);
|
|
2263
3352
|
const parts = [];
|
|
2264
3353
|
let current;
|
|
2265
|
-
for (const u of
|
|
3354
|
+
for (const u of units) {
|
|
2266
3355
|
if (current && (current.tokens + u.tokens > room || current.tokens >= target)) {
|
|
2267
3356
|
parts.push(current);
|
|
2268
3357
|
current = void 0;
|
|
@@ -2277,13 +3366,104 @@ function planParts(files, room = SESSION_TOKENS) {
|
|
|
2277
3366
|
return parts;
|
|
2278
3367
|
}
|
|
2279
3368
|
|
|
3369
|
+
// packages/verify/dist/gaps.js
|
|
3370
|
+
var GAP_CONTEXT_LINES = 20;
|
|
3371
|
+
var byPath3 = (a, b) => a < b ? -1 : a > b ? 1 : 0;
|
|
3372
|
+
function quoted2(v) {
|
|
3373
|
+
return JSON.stringify(v).replace(/[\u007f-\u009f\u2028\u2029]/g, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, "0")}`);
|
|
3374
|
+
}
|
|
3375
|
+
function split(cite2) {
|
|
3376
|
+
const m = /^(.+):(\d+)$/.exec(cite2);
|
|
3377
|
+
return m ? { file: m[1], line: Number(m[2]) } : void 0;
|
|
3378
|
+
}
|
|
3379
|
+
function planGaps(from, size, room, o = {}) {
|
|
3380
|
+
const whole = new Set(from.unknown ?? []);
|
|
3381
|
+
const lines = /* @__PURE__ */ new Map();
|
|
3382
|
+
const note = (cite2, why) => {
|
|
3383
|
+
const at = split(cite2);
|
|
3384
|
+
if (!at)
|
|
3385
|
+
return;
|
|
3386
|
+
const of = lines.get(at.file) ?? /* @__PURE__ */ new Map();
|
|
3387
|
+
of.set(at.line, [...of.get(at.line) ?? [], why]);
|
|
3388
|
+
lines.set(at.file, of);
|
|
3389
|
+
};
|
|
3390
|
+
for (const s of from.skipped ?? [])
|
|
3391
|
+
note(s.cite, `writes the ${s.kind} ${quoted2(s.value)}, which no fact cites`);
|
|
3392
|
+
for (const h of from.reviewedHolds ?? [])
|
|
3393
|
+
for (const s of h.skipped)
|
|
3394
|
+
note(s.cite, `writes the ${s.kind} ${quoted2(s.value)}, but ${quoted2(h.path)} is reviewed as ${quoted2(h.why)}: write its fact, or narrow the reviewed line`);
|
|
3395
|
+
for (const o2 of from.specOpsUncovered ?? [])
|
|
3396
|
+
note(o2.cite, `declares ${o2.method} ${quoted2(o2.path)}, which no entry covers: find the code that serves it`);
|
|
3397
|
+
for (const e of from.entriesNoFlowFollows ?? [])
|
|
3398
|
+
note(e.cite, e.route ? `is ${e.id} (${[e.method, quoted2(e.path)].filter(Boolean).join(" ")}), a gateway route with no forwarding call: write the call it forwards to its RouteRule's target, \`from\` it, as the gateway rules say` : `is ${e.id} (${[e.entryType, e.method, quoted2(e.path)].filter(Boolean).join(" ")}), which no flow follows: write its flow`);
|
|
3399
|
+
const unlisted = Math.max(0, (from.skippedCount ?? 0) - (from.skipped?.length ?? 0)) + Math.max(0, (from.specOpsUncoveredCount ?? 0) - (from.specOpsUncovered?.length ?? 0));
|
|
3400
|
+
const gone = [];
|
|
3401
|
+
const reads = [];
|
|
3402
|
+
const named = [.../* @__PURE__ */ new Set([...whole, ...lines.keys()])].sort(byPath3);
|
|
3403
|
+
const files = o.first?.length ? reachOrder(named, o.first) : named;
|
|
3404
|
+
for (const file of files) {
|
|
3405
|
+
const sz = size(file);
|
|
3406
|
+
if (!sz) {
|
|
3407
|
+
gone.push(file);
|
|
3408
|
+
continue;
|
|
3409
|
+
}
|
|
3410
|
+
const at = lines.get(file) ?? /* @__PURE__ */ new Map();
|
|
3411
|
+
const whys = (a, b) => [...at.entries()].filter(([l]) => l >= a && l <= b).sort((x, y) => x[0] - y[0]).flatMap(([l, ws]) => [...new Set(ws)].sort().map((w) => `line ${l} ${w}`));
|
|
3412
|
+
const share = (a, b) => Math.ceil(sz.tokens * (b - a + 1) / sz.lines);
|
|
3413
|
+
if (whole.has(file)) {
|
|
3414
|
+
const head = "no fact cites it: read it whole";
|
|
3415
|
+
if (sz.tokens <= room || sz.lines <= 1) {
|
|
3416
|
+
reads.push({ path: file, file, tokens: sz.tokens, why: [head, ...whys(1, sz.lines)] });
|
|
3417
|
+
continue;
|
|
3418
|
+
}
|
|
3419
|
+
for (const r of lineRanges(sz, room))
|
|
3420
|
+
reads.push({
|
|
3421
|
+
path: `${file}#L${r.from}-L${r.to}`,
|
|
3422
|
+
file,
|
|
3423
|
+
tokens: r.tokens,
|
|
3424
|
+
// R4: a line the ranges share is named in the range it is its own in, the earlier one.
|
|
3425
|
+
why: ["no fact cites the file: read these lines", ...whys(r.start, r.to)]
|
|
3426
|
+
});
|
|
3427
|
+
continue;
|
|
3428
|
+
}
|
|
3429
|
+
const windows = [];
|
|
3430
|
+
for (const l of [...at.keys()].sort((a, b) => a - b)) {
|
|
3431
|
+
const a = Math.max(1, l - GAP_CONTEXT_LINES);
|
|
3432
|
+
const b = Math.min(sz.lines, l + GAP_CONTEXT_LINES);
|
|
3433
|
+
const last = windows.at(-1);
|
|
3434
|
+
if (last && a <= last[1] + 1)
|
|
3435
|
+
last[1] = Math.max(last[1], b);
|
|
3436
|
+
else
|
|
3437
|
+
windows.push([a, b]);
|
|
3438
|
+
}
|
|
3439
|
+
for (const [a, b] of windows)
|
|
3440
|
+
reads.push({ path: `${file}#L${a}-L${b}`, file, tokens: share(a, b), why: whys(a, b) });
|
|
3441
|
+
}
|
|
3442
|
+
const sessions = [];
|
|
3443
|
+
for (const r of reads) {
|
|
3444
|
+
const last = sessions.at(-1);
|
|
3445
|
+
if (last && last.tokens + r.tokens <= room) {
|
|
3446
|
+
last.reads.push(r);
|
|
3447
|
+
last.tokens += r.tokens;
|
|
3448
|
+
} else
|
|
3449
|
+
sessions.push({ reads: [r], tokens: r.tokens });
|
|
3450
|
+
}
|
|
3451
|
+
return {
|
|
3452
|
+
sessions,
|
|
3453
|
+
files: new Set(reads.map((r) => r.file)).size,
|
|
3454
|
+
tokens: reads.reduce((n, r) => n + r.tokens, 0),
|
|
3455
|
+
unlisted,
|
|
3456
|
+
gone
|
|
3457
|
+
};
|
|
3458
|
+
}
|
|
3459
|
+
|
|
2280
3460
|
// packages/diagrams/dist/views.js
|
|
2281
3461
|
var VIEW_NAMES = ["context", "container", "data", "event", "sequence"];
|
|
2282
3462
|
var ViewError = class extends Error {
|
|
2283
3463
|
};
|
|
2284
3464
|
var cmp = (a, b) => a < b ? -1 : a > b ? 1 : 0;
|
|
2285
3465
|
var slug = (s) => s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
2286
|
-
var
|
|
3466
|
+
var normPath2 = (p) => p.replace(/\{[^}]*\}/g, "{}").replace(/\/+$/, "") || "/";
|
|
2287
3467
|
function legendOf(status) {
|
|
2288
3468
|
if (status === "planned-new")
|
|
2289
3469
|
return "new";
|
|
@@ -2372,7 +3552,7 @@ var Catalog = class {
|
|
|
2372
3552
|
what = this.topicOf(i);
|
|
2373
3553
|
else {
|
|
2374
3554
|
const ep = i.endpoint ? this.endpoints.get(i.endpoint) : void 0;
|
|
2375
|
-
const path = ep?.endpointType === "rest" &&
|
|
3555
|
+
const path = ep?.endpointType === "rest" && normPath2(ep.pathOrTopic) === normPath2(i.normalizedPath) ? ep.pathOrTopic : i.normalizedPath;
|
|
2376
3556
|
what = [i.method?.toUpperCase(), path].filter(Boolean).join(" ");
|
|
2377
3557
|
}
|
|
2378
3558
|
return [i.pattern?.code, what].filter(Boolean).join(" ");
|
|
@@ -2776,8 +3956,8 @@ function changeView(records2, change, flows, known = []) {
|
|
|
2776
3956
|
const nodes = [];
|
|
2777
3957
|
for (const id of comps) {
|
|
2778
3958
|
const sys = c.components.has(id) ? c.systemOf(id) : void 0;
|
|
2779
|
-
const
|
|
2780
|
-
const { legend: _status, ...rest } =
|
|
3959
|
+
const base2 = c.components.has(id) || c.isSystem(id) ? c.endNode(id, sys) : { id, label: id, kind: "component" };
|
|
3960
|
+
const { legend: _status, ...rest } = base2;
|
|
2781
3961
|
nodes.push({ ...rest, label: marked(rest.label, legends.get(id)), ...withMark(id) });
|
|
2782
3962
|
}
|
|
2783
3963
|
for (const t of tables) {
|
|
@@ -2873,14 +4053,14 @@ var Ids = class {
|
|
|
2873
4053
|
const known = this.map.get(key);
|
|
2874
4054
|
if (known)
|
|
2875
4055
|
return known;
|
|
2876
|
-
let
|
|
2877
|
-
if (!/^[A-Za-z]/.test(
|
|
2878
|
-
|
|
2879
|
-
if (RESERVED.has(
|
|
2880
|
-
|
|
2881
|
-
let id =
|
|
4056
|
+
let base2 = raw.replace(/[^A-Za-z0-9_]/g, "_");
|
|
4057
|
+
if (!/^[A-Za-z]/.test(base2))
|
|
4058
|
+
base2 = `n_${base2}`;
|
|
4059
|
+
if (RESERVED.has(base2.toLowerCase()))
|
|
4060
|
+
base2 = `${base2}_`;
|
|
4061
|
+
let id = base2;
|
|
2882
4062
|
for (let n = 2; this.used.has(id); n++)
|
|
2883
|
-
id = `${
|
|
4063
|
+
id = `${base2}_${n}`;
|
|
2884
4064
|
this.used.add(id);
|
|
2885
4065
|
this.map.set(key, id);
|
|
2886
4066
|
return id;
|
|
@@ -3069,8 +4249,8 @@ function nodeStyle(n) {
|
|
|
3069
4249
|
return styleText(SHAPES.topic, legend);
|
|
3070
4250
|
if (n.boundary === "vendor")
|
|
3071
4251
|
return styleText(SHAPES.vendor, n.kind === "component" ? { atticusKind: "component" } : {}, legend);
|
|
3072
|
-
const
|
|
3073
|
-
return styleText(
|
|
4252
|
+
const base2 = n.kind === "system" ? SHAPES.system : SHAPES.component;
|
|
4253
|
+
return styleText(base2, n.boundary ? BOUNDARY[n.boundary] : void 0, legend);
|
|
3074
4254
|
}
|
|
3075
4255
|
function groupStyle(g) {
|
|
3076
4256
|
return styleText(SHAPES.group, g.kind === "datastore" ? { atticusKind: "datastore-group" } : {}, g.boundary ? { strokeColor: BOUNDARY[g.boundary].strokeColor } : void 0, g.legend ? LEGEND[g.legend] : void 0);
|
|
@@ -3161,14 +4341,14 @@ function edgeStyle(e) {
|
|
|
3161
4341
|
}
|
|
3162
4342
|
var compress = (xml) => deflateRawSync(Buffer.from(encodeURIComponent(xml), "utf8"), { level: 9 }).toString("base64");
|
|
3163
4343
|
function shapeLibrary() {
|
|
3164
|
-
const
|
|
4344
|
+
const base2 = [
|
|
3165
4345
|
["System", SHAPES.system, W, H],
|
|
3166
4346
|
["Component", SHAPES.component, W, H],
|
|
3167
4347
|
["Vendor", SHAPES.vendor, W, H + 20],
|
|
3168
4348
|
["Data store", SHAPES.datastore, 120, 80],
|
|
3169
4349
|
["Topic", SHAPES.topic, W, H]
|
|
3170
4350
|
];
|
|
3171
|
-
const entries =
|
|
4351
|
+
const entries = base2.flatMap(([title, style, w, h]) => [void 0, "new", "modify", "retire"].map((legend) => {
|
|
3172
4352
|
const value = attr(title);
|
|
3173
4353
|
const st = attr(styleText(style, legend ? LEGEND[legend] : void 0));
|
|
3174
4354
|
const xml = `<mxGraphModel><root><mxCell id="0"/><mxCell id="1" parent="0"/><mxCell id="2" value="${value}" style="${st}" vertex="1" parent="1"><mxGeometry width="${w}" height="${h}" as="geometry"/></mxCell></root></mxGraphModel>`;
|
|
@@ -3307,12 +4487,91 @@ var STATE = {
|
|
|
3307
4487
|
};
|
|
3308
4488
|
function recordGaps(r) {
|
|
3309
4489
|
const g = [];
|
|
3310
|
-
if (r.confidence === "inferred")
|
|
4490
|
+
if (r.confidence === "inferred" && !r.flags?.includes(JOINED_BY_PERSON))
|
|
3311
4491
|
g.push("inferred");
|
|
3312
4492
|
for (const f of r.flags ?? [])
|
|
3313
|
-
|
|
4493
|
+
if (!JOIN_HOW_FLAGS.has(f) && !LINK_EVIDENCE_NOT_GAPS.has(f))
|
|
4494
|
+
g.push(f);
|
|
3314
4495
|
return g;
|
|
3315
4496
|
}
|
|
4497
|
+
var MAX_JOINS = 20;
|
|
4498
|
+
var HOW_OF_FLAG = {
|
|
4499
|
+
"joined-by-path": "path",
|
|
4500
|
+
"joined-by-label": "label",
|
|
4501
|
+
"joined-by-platform-setting": "platform-setting",
|
|
4502
|
+
"joined-by-folder-name": "folder-name",
|
|
4503
|
+
"joined-despite-conflict": "conflict",
|
|
4504
|
+
"joined-by-service-name": "service-name",
|
|
4505
|
+
// G1 (ADR 0174): a person paired the call with the entry on the canvas.
|
|
4506
|
+
"joined-by-person": "person",
|
|
4507
|
+
// ADR 0181: the AI paired it, applied by Atticus from its run: the AI's word, not a person's.
|
|
4508
|
+
[JOINED_BY_AI]: "ai",
|
|
4509
|
+
"joined-by-answer": "answer",
|
|
4510
|
+
"joined-by-name": "named",
|
|
4511
|
+
"joined-by-repo": "repo",
|
|
4512
|
+
"joined-by-host": "host",
|
|
4513
|
+
"declared-caller": "declared",
|
|
4514
|
+
// ADR 0162 §6 (F8): a topic or queue the profile names as an outside system's: a person's word.
|
|
4515
|
+
[DECLARED_TOPIC]: "declared",
|
|
4516
|
+
// ADR 0160 (O3): answered by an entry of another team's shared map.
|
|
4517
|
+
[JOINED_TO_SHARED_MAP]: "shared"
|
|
4518
|
+
};
|
|
4519
|
+
function joinHow(i) {
|
|
4520
|
+
for (const f of i.flags ?? [])
|
|
4521
|
+
if (HOW_OF_FLAG[f])
|
|
4522
|
+
return HOW_OF_FLAG[f];
|
|
4523
|
+
if (i.via === "event-bus" || i.via === "mq")
|
|
4524
|
+
return "topic";
|
|
4525
|
+
return "unknown";
|
|
4526
|
+
}
|
|
4527
|
+
function joinOf(i) {
|
|
4528
|
+
const side = (s) => evidenceOf(i.evidence.filter((e) => e.side === s));
|
|
4529
|
+
const sided = /* @__PURE__ */ new Set(["caller", "callee", "gateway"]);
|
|
4530
|
+
return {
|
|
4531
|
+
interface: i.id,
|
|
4532
|
+
from: i.from,
|
|
4533
|
+
to: i.to,
|
|
4534
|
+
how: joinHow(i),
|
|
4535
|
+
gateway: i.via === "gateway",
|
|
4536
|
+
verified: i.confidence === "verified",
|
|
4537
|
+
...i.method ? { method: i.method } : {},
|
|
4538
|
+
...i.normalizedPath ? { path: i.normalizedPath } : {},
|
|
4539
|
+
caller: side("caller"),
|
|
4540
|
+
callee: side("callee"),
|
|
4541
|
+
hops: side("gateway"),
|
|
4542
|
+
other: evidenceOf(i.evidence.filter((e) => !e.side || !sided.has(e.side))),
|
|
4543
|
+
...sharedOf(i),
|
|
4544
|
+
...aiRunOf(i)
|
|
4545
|
+
};
|
|
4546
|
+
}
|
|
4547
|
+
function aiRunOf(i) {
|
|
4548
|
+
if (!i.flags?.includes(JOINED_BY_AI))
|
|
4549
|
+
return {};
|
|
4550
|
+
const run = i.evidence.find((e) => e.source === "ai" && e.run)?.run;
|
|
4551
|
+
return run ? { run } : {};
|
|
4552
|
+
}
|
|
4553
|
+
function sharedOf(i) {
|
|
4554
|
+
if (!i.flags?.includes(JOINED_TO_SHARED_MAP))
|
|
4555
|
+
return {};
|
|
4556
|
+
const e = i.evidence.find((x) => x.extractor === SHARED_MAP_EVIDENCE && x.side === "callee" && x.repo && x.path);
|
|
4557
|
+
if (!e)
|
|
4558
|
+
return {};
|
|
4559
|
+
return {
|
|
4560
|
+
shared: {
|
|
4561
|
+
team: e.by ?? "",
|
|
4562
|
+
repo: e.repo,
|
|
4563
|
+
at: e.line === void 0 ? e.path : `${e.path}:${e.line}`,
|
|
4564
|
+
...e.commit ? { commit: e.commit } : {}
|
|
4565
|
+
}
|
|
4566
|
+
};
|
|
4567
|
+
}
|
|
4568
|
+
function mergeJoins(a, b) {
|
|
4569
|
+
const by = new Map(a.map((j) => [j.interface, j]));
|
|
4570
|
+
for (const j of b)
|
|
4571
|
+
if (!by.has(j.interface))
|
|
4572
|
+
by.set(j.interface, j);
|
|
4573
|
+
return [...by.values()].sort((x, y) => x.interface < y.interface ? -1 : x.interface > y.interface ? 1 : 0).slice(0, MAX_JOINS);
|
|
4574
|
+
}
|
|
3316
4575
|
var props = (o) => {
|
|
3317
4576
|
const out = {};
|
|
3318
4577
|
for (const [k, v] of Object.entries(o)) {
|
|
@@ -3462,6 +4721,7 @@ function buildEstate(records2) {
|
|
|
3462
4721
|
add(fromRecord(c, `component:${c.id}`, "component", c.id, parent, { type: c.type, runtime: c.runtime, repo: c.repo, path: c.path }, gaps));
|
|
3463
4722
|
}
|
|
3464
4723
|
const endpointNode = /* @__PURE__ */ new Map();
|
|
4724
|
+
const topicByPath = /* @__PURE__ */ new Map();
|
|
3465
4725
|
for (const e of all(records2, "Endpoint")) {
|
|
3466
4726
|
const topic = TOPIC_TYPES.has(e.endpointType);
|
|
3467
4727
|
const id = `${topic ? "topic" : "endpoint"}:${e.id}`;
|
|
@@ -3478,7 +4738,30 @@ function buildEstate(records2) {
|
|
|
3478
4738
|
auth: e.auth
|
|
3479
4739
|
}, gaps));
|
|
3480
4740
|
endpointNode.set(e.id, id);
|
|
4741
|
+
if (topic)
|
|
4742
|
+
topicByPath.set(e.pathOrTopic, id);
|
|
3481
4743
|
}
|
|
4744
|
+
const busTopic = (name, parent, ev = []) => {
|
|
4745
|
+
const known = topicByPath.get(name);
|
|
4746
|
+
if (known)
|
|
4747
|
+
return known;
|
|
4748
|
+
const id = add({
|
|
4749
|
+
id: `topic:bus.${slug2(name)}`,
|
|
4750
|
+
kind: "topic",
|
|
4751
|
+
label: name,
|
|
4752
|
+
parent,
|
|
4753
|
+
state: "current",
|
|
4754
|
+
gaps: [],
|
|
4755
|
+
evidence: ev,
|
|
4756
|
+
properties: { pathOrTopic: name },
|
|
4757
|
+
next: {
|
|
4758
|
+
command: `atticus query "${name}"`,
|
|
4759
|
+
why: "List the records that name this topic or queue."
|
|
4760
|
+
}
|
|
4761
|
+
});
|
|
4762
|
+
topicByPath.set(name, id);
|
|
4763
|
+
return id;
|
|
4764
|
+
};
|
|
3482
4765
|
const storeNode = /* @__PURE__ */ new Map();
|
|
3483
4766
|
for (const d of all(records2, "DataStore")) {
|
|
3484
4767
|
const owner = d.ownerComponent && components.has(d.ownerComponent);
|
|
@@ -3523,9 +4806,33 @@ function buildEstate(records2) {
|
|
|
3523
4806
|
const ev = evidenceOf(i.evidence);
|
|
3524
4807
|
const gaps = [...recordGaps(i), ...i.conflicts.length ? ["conflicts"] : []];
|
|
3525
4808
|
const target = i.endpoint ? endpointNode.get(i.endpoint) : void 0;
|
|
4809
|
+
const join2 = joinOf(i);
|
|
3526
4810
|
if (target?.startsWith("topic:")) {
|
|
3527
|
-
atoms.push({ from, to: target, type: "publishes", evidence: ev, gaps });
|
|
3528
|
-
atoms.push({ from: to, to: target, type: "subscribes", evidence: ev, gaps });
|
|
4811
|
+
atoms.push({ from, to: target, type: "publishes", evidence: ev, gaps, join: join2 });
|
|
4812
|
+
atoms.push({ from: to, to: target, type: "subscribes", evidence: ev, gaps, join: join2 });
|
|
4813
|
+
continue;
|
|
4814
|
+
}
|
|
4815
|
+
if (!i.endpoint && (i.via === "event-bus" || i.via === "mq") && i.normalizedPath) {
|
|
4816
|
+
const isSystem = (n) => n.startsWith("system:") || n.startsWith("unknown:");
|
|
4817
|
+
const [home, homeRef] = !isSystem(to) && isSystem(from) ? [from, i.from] : [to, i.to];
|
|
4818
|
+
const t = busTopic(i.normalizedPath, home);
|
|
4819
|
+
const owner = nodes.get(t);
|
|
4820
|
+
if (owner.parent === home && isSystem(home)) {
|
|
4821
|
+
owner.properties = { ...owner.properties, owner: nodes.get(home)?.label ?? home };
|
|
4822
|
+
const b = nodes.get(home)?.properties.boundary;
|
|
4823
|
+
if (outside.has(homeRef) || b === "vendor" || b === "outside")
|
|
4824
|
+
owner.properties.ownerOutside = true;
|
|
4825
|
+
}
|
|
4826
|
+
if (join2.shared)
|
|
4827
|
+
owner.properties = { ...owner.properties, owner: join2.shared.team, ownerOutside: true };
|
|
4828
|
+
if (i.flags?.includes(DECLARED_TOPIC)) {
|
|
4829
|
+
owner.properties = { ...owner.properties, declared: true };
|
|
4830
|
+
owner.gaps = [.../* @__PURE__ */ new Set([...owner.gaps, DECLARED_TOPIC])];
|
|
4831
|
+
owner.evidence = mergeEvidence(owner.evidence, evidenceOf(i.evidence.filter((e) => e.source === "manual")));
|
|
4832
|
+
}
|
|
4833
|
+
atoms.push({ from, to: t, type: "publishes", evidence: ev, gaps, join: join2 });
|
|
4834
|
+
if (to.startsWith("component:"))
|
|
4835
|
+
atoms.push({ from: to, to: t, type: "subscribes", evidence: ev, gaps, join: join2 });
|
|
3529
4836
|
continue;
|
|
3530
4837
|
}
|
|
3531
4838
|
let caller = from;
|
|
@@ -3556,7 +4863,24 @@ function buildEstate(records2) {
|
|
|
3556
4863
|
sync: i.sync,
|
|
3557
4864
|
via: i.via,
|
|
3558
4865
|
evidence: ev,
|
|
3559
|
-
gaps: [...gaps, ...i.endpoint && !target ? ["no-endpoint"] : []]
|
|
4866
|
+
gaps: [...gaps, ...i.endpoint && !target ? ["no-endpoint"] : []],
|
|
4867
|
+
join: join2
|
|
4868
|
+
});
|
|
4869
|
+
}
|
|
4870
|
+
for (const u of all(records2, "Unresolved")) {
|
|
4871
|
+
if (u.unresolvedKind !== "event-publish" && u.unresolvedKind !== "event-subscribe")
|
|
4872
|
+
continue;
|
|
4873
|
+
if (!u.raw || u.raw.includes("${"))
|
|
4874
|
+
continue;
|
|
4875
|
+
const owner = u.owner ? partNode(u.owner) : "estate";
|
|
4876
|
+
const ev = evidenceOf(u.evidence);
|
|
4877
|
+
const t = busTopic(u.raw, u.unresolvedKind === "event-subscribe" ? owner : "estate", ev);
|
|
4878
|
+
atoms.push({
|
|
4879
|
+
from: owner,
|
|
4880
|
+
to: t,
|
|
4881
|
+
type: u.unresolvedKind === "event-publish" ? "publishes" : "subscribes",
|
|
4882
|
+
evidence: ev,
|
|
4883
|
+
gaps: recordGaps(u)
|
|
3560
4884
|
});
|
|
3561
4885
|
}
|
|
3562
4886
|
for (const c of components.values())
|
|
@@ -3611,6 +4935,37 @@ function buildEstate(records2) {
|
|
|
3611
4935
|
gaps: recordGaps(k)
|
|
3612
4936
|
});
|
|
3613
4937
|
}
|
|
4938
|
+
const busy = /* @__PURE__ */ new Set();
|
|
4939
|
+
const internal = (id) => {
|
|
4940
|
+
const n = nodes.get(id);
|
|
4941
|
+
return !!n && n.kind !== "unknown" && n.properties.boundary !== "outside" && n.properties.boundary !== "vendor";
|
|
4942
|
+
};
|
|
4943
|
+
for (const a of atoms)
|
|
4944
|
+
if (a.type === "publishes" || a.type === "subscribes") {
|
|
4945
|
+
if (internal(a.from))
|
|
4946
|
+
busy.add(`${a.type}|${a.to}`);
|
|
4947
|
+
}
|
|
4948
|
+
for (const n of nodes.values()) {
|
|
4949
|
+
if (n.kind !== "topic")
|
|
4950
|
+
continue;
|
|
4951
|
+
if (n.properties.owner === void 0) {
|
|
4952
|
+
let p = n.parent ? nodes.get(n.parent) : void 0;
|
|
4953
|
+
while (p && p.kind !== "system" && p.kind !== "repo" && p.kind !== "estate")
|
|
4954
|
+
p = p.parent ? nodes.get(p.parent) : void 0;
|
|
4955
|
+
if (p && p.kind !== "estate") {
|
|
4956
|
+
let sys = p;
|
|
4957
|
+
while (sys && sys.kind !== "system")
|
|
4958
|
+
sys = sys.parent ? nodes.get(sys.parent) : void 0;
|
|
4959
|
+
n.properties = { ...n.properties, owner: (sys ?? p).label };
|
|
4960
|
+
if (sys && (sys.properties.boundary === "outside" || sys.properties.boundary === "vendor"))
|
|
4961
|
+
n.properties.ownerOutside = true;
|
|
4962
|
+
}
|
|
4963
|
+
}
|
|
4964
|
+
if (!busy.has(`publishes|${n.id}`))
|
|
4965
|
+
n.gaps.push("no-publisher");
|
|
4966
|
+
if (!busy.has(`subscribes|${n.id}`))
|
|
4967
|
+
n.gaps.push("no-listener");
|
|
4968
|
+
}
|
|
3614
4969
|
const kids = /* @__PURE__ */ new Map();
|
|
3615
4970
|
for (const n of nodes.values()) {
|
|
3616
4971
|
if (!n.parent)
|
|
@@ -3705,10 +5060,13 @@ function merge(mapped) {
|
|
|
3705
5060
|
count: 1,
|
|
3706
5061
|
state: "current",
|
|
3707
5062
|
gaps: [...new Set(atom.gaps)].sort(),
|
|
3708
|
-
evidence: mergeEvidence(atom.evidence)
|
|
5063
|
+
evidence: mergeEvidence(atom.evidence),
|
|
5064
|
+
...atom.join ? { joins: [atom.join] } : {}
|
|
3709
5065
|
});
|
|
3710
5066
|
continue;
|
|
3711
5067
|
}
|
|
5068
|
+
if (atom.join)
|
|
5069
|
+
e.joins = mergeJoins(e.joins ?? [], [atom.join]);
|
|
3712
5070
|
e.count++;
|
|
3713
5071
|
if (atom.sync && e.sync !== atom.sync)
|
|
3714
5072
|
e.sync = "mixed";
|
|
@@ -3772,16 +5130,27 @@ function nodeView(est, id, o) {
|
|
|
3772
5130
|
return ax[i] ?? x;
|
|
3773
5131
|
};
|
|
3774
5132
|
const types = LENS_EDGES[lens];
|
|
5133
|
+
const onBus = (atom, b) => (atom.type === "publishes" || atom.type === "subscribes") && est.nodes.get(b)?.kind === "topic" && b !== id;
|
|
5134
|
+
const drawnTopics = /* @__PURE__ */ new Set();
|
|
5135
|
+
for (const atom of est.atoms) {
|
|
5136
|
+
if (!types.has(atom.type))
|
|
5137
|
+
continue;
|
|
5138
|
+
const a = lift(est, lens, atom.from);
|
|
5139
|
+
const b = lift(est, lens, atom.to);
|
|
5140
|
+
if (onBus(atom, b) && (inside(a) || inside(b)))
|
|
5141
|
+
drawnTopics.add(b);
|
|
5142
|
+
}
|
|
3775
5143
|
const mapped = [];
|
|
3776
5144
|
for (const atom of est.atoms) {
|
|
3777
5145
|
if (!types.has(atom.type))
|
|
3778
5146
|
continue;
|
|
3779
5147
|
const a = lift(est, lens, atom.from);
|
|
3780
5148
|
const b = lift(est, lens, atom.to);
|
|
3781
|
-
|
|
5149
|
+
const bus = onBus(atom, b);
|
|
5150
|
+
if (!inside(a) && !inside(b) && !(bus && drawnTopics.has(b)))
|
|
3782
5151
|
continue;
|
|
3783
5152
|
const from = place(a);
|
|
3784
|
-
const to = place(b);
|
|
5153
|
+
const to = bus ? b : place(b);
|
|
3785
5154
|
if (from === to)
|
|
3786
5155
|
continue;
|
|
3787
5156
|
mapped.push({ from, to, atom });
|
|
@@ -3790,10 +5159,12 @@ function nodeView(est, id, o) {
|
|
|
3790
5159
|
let hidden = 0;
|
|
3791
5160
|
if (!o.all)
|
|
3792
5161
|
({ edges, hidden } = reduceUses(edges));
|
|
3793
|
-
const
|
|
3794
|
-
const
|
|
5162
|
+
const ends = [...new Set(edges.flatMap((e) => [e.from, e.to]))].filter((n) => n !== id && !shownSet.has(n));
|
|
5163
|
+
const deeper = ends.filter((n) => drawnTopics.has(n) && inside(n)).sort();
|
|
5164
|
+
const outside = ends.filter((n) => !deeper.includes(n)).sort();
|
|
5165
|
+
const children = [...shown, ...deeper].map((n) => toNode(est, lens, n));
|
|
3795
5166
|
const firstTable = children.find((c) => c.kind === "table");
|
|
3796
|
-
const suggest = lens === "data" && firstTable ? { mode: "focus", id: firstTable.id } : shown.length + outside.length > DETAILED_LIMIT ? { mode: "group" } : void 0;
|
|
5167
|
+
const suggest = lens === "data" && firstTable ? { mode: "focus", id: firstTable.id } : shown.length + deeper.length + outside.length > DETAILED_LIMIT ? { mode: "group" } : void 0;
|
|
3797
5168
|
return {
|
|
3798
5169
|
lens,
|
|
3799
5170
|
node: toNode(est, lens, id),
|
|
@@ -3931,13 +5302,13 @@ function codeLinkerFor(o) {
|
|
|
3931
5302
|
web = `https://${scp[1]}/${scp[2]}`;
|
|
3932
5303
|
}
|
|
3933
5304
|
if (web) {
|
|
3934
|
-
const
|
|
3935
|
-
const ado = /(^https?:\/\/dev\.azure\.com\/)|(\.visualstudio\.com\/)/i.test(
|
|
5305
|
+
const base2 = web.replace(/\/+$/, "").replace(/\.git$/, "");
|
|
5306
|
+
const ado = /(^https?:\/\/dev\.azure\.com\/)|(\.visualstudio\.com\/)/i.test(base2);
|
|
3936
5307
|
return (path, line, commit) => {
|
|
3937
5308
|
const p = encodeURI(path);
|
|
3938
5309
|
if (ado)
|
|
3939
|
-
return `${
|
|
3940
|
-
return `${
|
|
5310
|
+
return `${base2}?path=/${p}${commit ? `&version=GC${commit}` : ""}${line ? `&line=${line}` : ""}`;
|
|
5311
|
+
return `${base2}/blob/${commit ?? "HEAD"}/${p}${line ? `#L${line}` : ""}`;
|
|
3941
5312
|
};
|
|
3942
5313
|
}
|
|
3943
5314
|
const dir = (o.dir ?? "").replace(/\/+$/, "");
|
|
@@ -4152,6 +5523,27 @@ function config(c) {
|
|
|
4152
5523
|
return `- ${code(`${s.key} = ${s.valueOrRef}`)} (${notes.join("; ")}): ${cite(c, ownEvidence(s, c.i.repo))}`;
|
|
4153
5524
|
});
|
|
4154
5525
|
}
|
|
5526
|
+
function terms(c) {
|
|
5527
|
+
const ts = c.i.terms ?? [];
|
|
5528
|
+
const owners = /* @__PURE__ */ new Map();
|
|
5529
|
+
for (const r of records(c, "Component"))
|
|
5530
|
+
for (const f of r.flags ?? []) {
|
|
5531
|
+
const m = /^term:[^=]+=(.+)$/.exec(f);
|
|
5532
|
+
if (m && !c.comps.has(r.id))
|
|
5533
|
+
owners.set(m[1], [...owners.get(m[1]) ?? [], r.id]);
|
|
5534
|
+
}
|
|
5535
|
+
const listing = /* @__PURE__ */ new Map();
|
|
5536
|
+
for (const g of records(c, "AccessGrant"))
|
|
5537
|
+
if (g.flags?.some((f) => f.startsWith("term:"))) {
|
|
5538
|
+
const route = c.byId.get(g.resource);
|
|
5539
|
+
const by = route?.kind === "GatewayRoute" ? route.proxy : g.resource;
|
|
5540
|
+
listing.set(g.subject, [.../* @__PURE__ */ new Set([...listing.get(g.subject) ?? [], by])]);
|
|
5541
|
+
}
|
|
5542
|
+
return [...ts].sort((a, b) => cmp2(a.term, b.term) || cmp2(a.value, b.value)).map((t) => {
|
|
5543
|
+
const means = t.role === "owner" ? `this repo's own id${listing.get(t.value)?.length ? `; let in by ${list(listing.get(t.value).sort())}` : ""}` : t.role === "consumer" ? `a consumer it lets in${owners.get(t.value)?.length ? `: ${list(owners.get(t.value).sort())}` : ", whose own repo no fact names yet"}` : t.role === "none" ? "names no system" : `**open:** nothing says what it means${owners.get(t.value)?.length ? `; the same value is ${list(owners.get(t.value).sort())}'s own id (a possible link)` : ""}`;
|
|
5544
|
+
return `- ${code(t.term)} ${code(t.value)}: ${means} ${cite(c, t.cite)}${t.meaning ? ` (its role from ${cite(c, t.meaning)})` : ""}`;
|
|
5545
|
+
});
|
|
5546
|
+
}
|
|
4155
5547
|
var flowFile = (f) => `flows/${f.id}.md`;
|
|
4156
5548
|
function flowsList(c) {
|
|
4157
5549
|
if (!c.i.flows.length)
|
|
@@ -4365,8 +5757,8 @@ function ctxOf(i, file) {
|
|
|
4365
5757
|
}
|
|
4366
5758
|
function renderMap(i) {
|
|
4367
5759
|
const files = {};
|
|
4368
|
-
const
|
|
4369
|
-
for (const comp of [...
|
|
5760
|
+
const base2 = ctxOf(i, "map.md");
|
|
5761
|
+
for (const comp of [...base2.comps].sort(cmp2)) {
|
|
4370
5762
|
const yaml = openApiOf(i, comp);
|
|
4371
5763
|
if (yaml)
|
|
4372
5764
|
files[`contracts/${comp}.openapi.yaml`] = yaml;
|
|
@@ -4380,17 +5772,18 @@ function renderMap(i) {
|
|
|
4380
5772
|
HEADER2(i.repo, "the catalog and its specs"),
|
|
4381
5773
|
"",
|
|
4382
5774
|
...aboutLine(i.about, i.repo),
|
|
4383
|
-
...builtOnLine(
|
|
5775
|
+
...builtOnLine(base2),
|
|
4384
5776
|
trustLine(i.trust, i.repo),
|
|
4385
5777
|
"",
|
|
4386
|
-
...section("What it serves", serves(
|
|
4387
|
-
...section("What it calls", calls(
|
|
4388
|
-
...section("What it stores", stores(
|
|
4389
|
-
...section("Where it runs", deploys(
|
|
4390
|
-
...section("Config that names other systems", config(
|
|
4391
|
-
...section("
|
|
4392
|
-
...section("
|
|
4393
|
-
...section("
|
|
5778
|
+
...section("What it serves", serves(base2)),
|
|
5779
|
+
...section("What it calls", calls(base2)),
|
|
5780
|
+
...section("What it stores", stores(base2)),
|
|
5781
|
+
...section("Where it runs", deploys(base2)),
|
|
5782
|
+
...section("Config that names other systems", config(base2)),
|
|
5783
|
+
...i.terms?.length ? section("Terms", terms(base2)) : [],
|
|
5784
|
+
...section("Flows", flowsList(base2)),
|
|
5785
|
+
...section("Contracts", contracts(base2, files)),
|
|
5786
|
+
...section("Open questions", openQuestions(base2))
|
|
4394
5787
|
].join("\n");
|
|
4395
5788
|
return Object.fromEntries(Object.entries(files).sort(([a], [b]) => cmp2(a, b)));
|
|
4396
5789
|
}
|
|
@@ -4422,15 +5815,25 @@ function rereadPlan(o) {
|
|
|
4422
5815
|
const excludeRes = (o.exclude ?? []).map(globToRegExp);
|
|
4423
5816
|
const status = /* @__PURE__ */ new Map();
|
|
4424
5817
|
for (const changes of used.values())
|
|
4425
|
-
for (const c of changes)
|
|
4426
|
-
if (c.status
|
|
5818
|
+
for (const c of changes) {
|
|
5819
|
+
if (c.status === "deleted")
|
|
5820
|
+
continue;
|
|
5821
|
+
if (c.gitlink) {
|
|
5822
|
+
for (const p of o.source.list())
|
|
5823
|
+
if (p.startsWith(`${c.path}/`))
|
|
5824
|
+
status.set(p, "modified");
|
|
5825
|
+
} else
|
|
4427
5826
|
status.set(c.path, c.status);
|
|
5827
|
+
}
|
|
5828
|
+
const rules = readRulesOf(o.conventions);
|
|
4428
5829
|
const readable = [...status.keys()].filter((p) => {
|
|
5830
|
+
if (!isSourceFile(p, "", void 0, rules) || tooBigToRead(o.source, p))
|
|
5831
|
+
return false;
|
|
4429
5832
|
const text = o.source.read(p);
|
|
4430
5833
|
if (text === void 0 || !text.trim())
|
|
4431
5834
|
return false;
|
|
4432
5835
|
const ls = text.split(/\r?\n/);
|
|
4433
|
-
if (!isSourceFile(p, ls[0] ?? "", ls))
|
|
5836
|
+
if (!isSourceFile(p, ls[0] ?? "", ls, readRulesOf(o.conventions)))
|
|
4434
5837
|
return false;
|
|
4435
5838
|
return !excludeRes.some((re) => re.test(p));
|
|
4436
5839
|
});
|
|
@@ -4454,10 +5857,242 @@ function onlyFiles(source, paths) {
|
|
|
4454
5857
|
return { list: () => source.list().filter((p) => keep.has(p)), read: (p) => source.read(p) };
|
|
4455
5858
|
}
|
|
4456
5859
|
|
|
5860
|
+
// packages/verify/dist/slips.js
|
|
5861
|
+
function slugFactId(id) {
|
|
5862
|
+
const slug3 = id.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1-$2").toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/-{2,}/g, "-").replace(/^[^a-z0-9]+/, "").replace(/-+$/, "");
|
|
5863
|
+
return notDeviceName(slug3).slice(0, 120);
|
|
5864
|
+
}
|
|
5865
|
+
var isId = (x) => typeof x === "string" && FactId.safeParse(x).success;
|
|
5866
|
+
var isObj = (x) => !!x && typeof x === "object" && !Array.isArray(x);
|
|
5867
|
+
var REF_FIELDS = ["from", "under", "owner", "mappedTo", "entry"];
|
|
5868
|
+
function repairFormSlips(facts, bad, onFile = []) {
|
|
5869
|
+
const slips = [];
|
|
5870
|
+
const cands = bad.flatMap((b) => {
|
|
5871
|
+
if (b.issues.some((i) => i.code === "not-json" || i.code === "duplicate-id"))
|
|
5872
|
+
return [];
|
|
5873
|
+
let json2;
|
|
5874
|
+
try {
|
|
5875
|
+
json2 = JSON.parse(b.raw);
|
|
5876
|
+
} catch {
|
|
5877
|
+
return [];
|
|
5878
|
+
}
|
|
5879
|
+
return isObj(json2) ? [{ b, json: json2 }] : [];
|
|
5880
|
+
});
|
|
5881
|
+
const taken = /* @__PURE__ */ new Map();
|
|
5882
|
+
for (const f of facts)
|
|
5883
|
+
taken.set(f.id, (taken.get(f.id) ?? 0) + 1);
|
|
5884
|
+
for (const { json: json2 } of cands)
|
|
5885
|
+
if (isId(json2.id))
|
|
5886
|
+
taken.set(json2.id, (taken.get(json2.id) ?? 0) + 1);
|
|
5887
|
+
const wanted = /* @__PURE__ */ new Map();
|
|
5888
|
+
for (const { json: json2 } of cands)
|
|
5889
|
+
if (typeof json2.id === "string" && !isId(json2.id)) {
|
|
5890
|
+
const s = slugFactId(json2.id);
|
|
5891
|
+
if (isId(s))
|
|
5892
|
+
wanted.set(s, (wanted.get(s) ?? 0) + 1);
|
|
5893
|
+
}
|
|
5894
|
+
const known = new Set([...facts, ...onFile].map((f) => f.id));
|
|
5895
|
+
const renames = /* @__PURE__ */ new Map();
|
|
5896
|
+
const collided = /* @__PURE__ */ new Map();
|
|
5897
|
+
for (const { b, json: json2 } of cands) {
|
|
5898
|
+
if (typeof json2.id !== "string" || isId(json2.id))
|
|
5899
|
+
continue;
|
|
5900
|
+
const s = slugFactId(json2.id);
|
|
5901
|
+
if (!isId(s))
|
|
5902
|
+
continue;
|
|
5903
|
+
if (taken.has(s) || (wanted.get(s) ?? 0) > 1) {
|
|
5904
|
+
const message = `line ${b.line}: fact id ${json2.id} would slug to ${s}, which another fact already has; rename one of them`;
|
|
5905
|
+
slips.push({ line: b.line, code: "id-collision", fact: json2.id, message });
|
|
5906
|
+
collided.set(b, message);
|
|
5907
|
+
continue;
|
|
5908
|
+
}
|
|
5909
|
+
renames.set(json2.id, s);
|
|
5910
|
+
}
|
|
5911
|
+
const rewritten = cands.map(({ b, json: json2 }) => {
|
|
5912
|
+
const o = structuredClone(json2);
|
|
5913
|
+
const fixes = [];
|
|
5914
|
+
if (typeof o.id === "string" && renames.has(o.id)) {
|
|
5915
|
+
fixes.push({
|
|
5916
|
+
line: b.line,
|
|
5917
|
+
code: "id-slugged",
|
|
5918
|
+
fact: renames.get(o.id),
|
|
5919
|
+
message: `line ${b.line}: fact id ${o.id} slugged to ${renames.get(o.id)}, and every reference to it`
|
|
5920
|
+
});
|
|
5921
|
+
o.id = renames.get(o.id);
|
|
5922
|
+
}
|
|
5923
|
+
const ref = (v) => {
|
|
5924
|
+
if (typeof v !== "string")
|
|
5925
|
+
return void 0;
|
|
5926
|
+
if (renames.has(v))
|
|
5927
|
+
return renames.get(v);
|
|
5928
|
+
if (isId(v))
|
|
5929
|
+
return void 0;
|
|
5930
|
+
const s = slugFactId(v);
|
|
5931
|
+
return known.has(s) ? s : void 0;
|
|
5932
|
+
};
|
|
5933
|
+
const said = (field, from, to) => fixes.push({
|
|
5934
|
+
line: b.line,
|
|
5935
|
+
code: "ref-rewritten",
|
|
5936
|
+
fact: String(o.id),
|
|
5937
|
+
message: `line ${b.line}: ${String(o.id)}'s ${field} ${from} rewritten as ${to}`
|
|
5938
|
+
});
|
|
5939
|
+
for (const k of REF_FIELDS) {
|
|
5940
|
+
const to = ref(o[k]);
|
|
5941
|
+
if (to) {
|
|
5942
|
+
said(k, o[k], to);
|
|
5943
|
+
o[k] = to;
|
|
5944
|
+
}
|
|
5945
|
+
}
|
|
5946
|
+
if (Array.isArray(o.steps))
|
|
5947
|
+
o.steps.forEach((st, i) => {
|
|
5948
|
+
if (!isObj(st))
|
|
5949
|
+
return;
|
|
5950
|
+
const to = ref(st.ref);
|
|
5951
|
+
if (to) {
|
|
5952
|
+
said(`step ${i + 1} ref`, st.ref, to);
|
|
5953
|
+
st.ref = to;
|
|
5954
|
+
}
|
|
5955
|
+
});
|
|
5956
|
+
return { b, o, fixes };
|
|
5957
|
+
});
|
|
5958
|
+
const entries = /* @__PURE__ */ new Map();
|
|
5959
|
+
for (const f of [...onFile, ...facts])
|
|
5960
|
+
if (f.kind === "entry")
|
|
5961
|
+
entries.set(f.id, f);
|
|
5962
|
+
for (const { o } of rewritten)
|
|
5963
|
+
if (o.kind === "entry" && typeof o.id === "string" && isObj(o.cite))
|
|
5964
|
+
entries.set(o.id, o);
|
|
5965
|
+
for (const r of rewritten) {
|
|
5966
|
+
const { o, b } = r;
|
|
5967
|
+
if (o.kind !== "flow" || o.cite !== void 0 || typeof o.entry !== "string")
|
|
5968
|
+
continue;
|
|
5969
|
+
const e = entries.get(o.entry);
|
|
5970
|
+
if (!e || !isObj(e.cite))
|
|
5971
|
+
continue;
|
|
5972
|
+
o.cite = structuredClone(e.cite);
|
|
5973
|
+
const c = e.cite;
|
|
5974
|
+
r.fixes.push({
|
|
5975
|
+
line: b.line,
|
|
5976
|
+
code: "flow-cite-from-entry",
|
|
5977
|
+
fact: String(o.id),
|
|
5978
|
+
message: `line ${b.line}: flow ${String(o.id)} had no cite; it takes its entry ${o.entry}'s line (${String(c.file)}:${String(c.line)})`
|
|
5979
|
+
});
|
|
5980
|
+
}
|
|
5981
|
+
const kept = [...facts];
|
|
5982
|
+
const seen = new Set(facts.map((f) => f.id));
|
|
5983
|
+
const fixedLines = /* @__PURE__ */ new Set();
|
|
5984
|
+
for (const { b, o, fixes } of rewritten) {
|
|
5985
|
+
if (!fixes.length || collided.has(b))
|
|
5986
|
+
continue;
|
|
5987
|
+
const p = Fact.safeParse(o);
|
|
5988
|
+
if (!p.success || seen.has(p.data.id))
|
|
5989
|
+
continue;
|
|
5990
|
+
seen.add(p.data.id);
|
|
5991
|
+
kept.push(p.data);
|
|
5992
|
+
fixedLines.add(b);
|
|
5993
|
+
slips.push(...fixes);
|
|
5994
|
+
}
|
|
5995
|
+
const still = bad.filter((b) => !fixedLines.has(b)).map((b) => collided.has(b) ? {
|
|
5996
|
+
...b,
|
|
5997
|
+
issues: [
|
|
5998
|
+
...b.issues,
|
|
5999
|
+
{ line: b.line, code: "id-collision", message: collided.get(b) }
|
|
6000
|
+
]
|
|
6001
|
+
} : b);
|
|
6002
|
+
slips.sort((x, y) => x.line - y.line);
|
|
6003
|
+
return { facts: kept, bad: still, slips };
|
|
6004
|
+
}
|
|
6005
|
+
|
|
6006
|
+
// packages/verify/dist/house-imports.js
|
|
6007
|
+
var CODE2 = /\.(java|kt|kts|scala|groovy|cs|vb|fs|ts|tsx|js|jsx|mjs|cjs|vue|svelte|py|go|rb|php|swift)$/i;
|
|
6008
|
+
var IMPORTS = [
|
|
6009
|
+
// Java, Kotlin, Scala, Groovy, Python's `import a.b`, Go's one-line import
|
|
6010
|
+
/^\s*import\s+(?:static\s+)?([A-Za-z_][\w.]*)/,
|
|
6011
|
+
// C#, F#
|
|
6012
|
+
/^\s*(?:global\s+)?using\s+(?:static\s+)?([A-Za-z_][\w.]*)\s*;/,
|
|
6013
|
+
/^\s*open\s+([A-Za-z_][\w.]*)\s*$/,
|
|
6014
|
+
// Python
|
|
6015
|
+
/^\s*from\s+([A-Za-z_][\w.]*)\s+import\b/,
|
|
6016
|
+
// JavaScript, TypeScript: import ... from "x", export ... from "x", import "x", require("x"), import("x")
|
|
6017
|
+
/\bfrom\s+["']([^"'\s]+)["']/,
|
|
6018
|
+
/^\s*import\s+["']([^"'\s]+)["']/,
|
|
6019
|
+
/\brequire\s*\(\s*["']([^"'\s]+)["']\s*\)/,
|
|
6020
|
+
/\bimport\s*\(\s*["']([^"'\s]+)["']\s*\)/
|
|
6021
|
+
];
|
|
6022
|
+
var GO_IMPORT = /^\s*(?:import\s+)?(?:[\w.]+\s+)?"([\w.-]+(?:\/[\w.-]+)+)"\s*$/;
|
|
6023
|
+
function modulesOf(line, go) {
|
|
6024
|
+
const out = [];
|
|
6025
|
+
for (const re of go ? [...IMPORTS, GO_IMPORT] : IMPORTS) {
|
|
6026
|
+
const m = re.exec(line);
|
|
6027
|
+
if (m?.[1])
|
|
6028
|
+
out.push(m[1]);
|
|
6029
|
+
}
|
|
6030
|
+
return out;
|
|
6031
|
+
}
|
|
6032
|
+
var lower = (s) => s.toLowerCase();
|
|
6033
|
+
var under3 = (module, pkg) => module === pkg || module.startsWith(pkg) && (/[./]$/.test(pkg) || /[./]/.test(module[pkg.length] ?? ""));
|
|
6034
|
+
function packageOf(module, prefix) {
|
|
6035
|
+
let i = prefix.length;
|
|
6036
|
+
while (i < module.length && /[./]/.test(module[i]))
|
|
6037
|
+
i++;
|
|
6038
|
+
const rest = module.slice(i);
|
|
6039
|
+
const part = /^[^./]*/.exec(rest)?.[0] ?? "";
|
|
6040
|
+
return module.slice(0, i + part.length).replace(/[./]+$/, "");
|
|
6041
|
+
}
|
|
6042
|
+
function nameOf2(pkg) {
|
|
6043
|
+
const parts = lower(pkg).split(/[^a-z0-9]+/).filter(Boolean);
|
|
6044
|
+
if (parts.length > 2 && /^(com|org|net|io|dev|co|uk|de|fr|nl|ch)$/.test(parts[0]))
|
|
6045
|
+
parts.shift();
|
|
6046
|
+
return parts.join("-") || "house-framework";
|
|
6047
|
+
}
|
|
6048
|
+
function houseImportsUntaught(source, o) {
|
|
6049
|
+
if (!o.prefixes.length)
|
|
6050
|
+
return [];
|
|
6051
|
+
const rules = o.rules ?? DEFAULT_READ_RULES;
|
|
6052
|
+
const prefixes = o.prefixes.map(lower);
|
|
6053
|
+
const taught = o.taught.map(lower);
|
|
6054
|
+
const found = /* @__PURE__ */ new Map();
|
|
6055
|
+
const paths = source.list().filter((p) => CODE2.test(p) && !rules.notRead.test(p) && !(o.exclude ?? []).some((re) => re.test(p)) && isSourceFile(p, "", void 0, rules)).sort();
|
|
6056
|
+
for (const p of paths) {
|
|
6057
|
+
const text = source.read(p);
|
|
6058
|
+
if (text === void 0)
|
|
6059
|
+
continue;
|
|
6060
|
+
const lines = text.split(/\r?\n/);
|
|
6061
|
+
const go = /\.go$/i.test(p);
|
|
6062
|
+
lines.forEach((line, i) => {
|
|
6063
|
+
if (!/\b(import|using|from|require|open)\b/.test(line) && !(go && /^\s*"/.test(line)))
|
|
6064
|
+
return;
|
|
6065
|
+
for (const m of modulesOf(line, go)) {
|
|
6066
|
+
const ml = lower(m);
|
|
6067
|
+
const prefix = prefixes.find((x) => ml.startsWith(x));
|
|
6068
|
+
if (!prefix)
|
|
6069
|
+
continue;
|
|
6070
|
+
if (taught.some((t) => under3(ml, t)))
|
|
6071
|
+
continue;
|
|
6072
|
+
const pkg = packageOf(m, prefix);
|
|
6073
|
+
const f = found.get(pkg) ?? { files: /* @__PURE__ */ new Set(), first: `${p}:${i + 1}` };
|
|
6074
|
+
f.files.add(p);
|
|
6075
|
+
found.set(pkg, f);
|
|
6076
|
+
}
|
|
6077
|
+
});
|
|
6078
|
+
}
|
|
6079
|
+
return [...found.entries()].map(([pkg, f]) => {
|
|
6080
|
+
const name = nameOf2(pkg);
|
|
6081
|
+
return {
|
|
6082
|
+
package: pkg,
|
|
6083
|
+
name,
|
|
6084
|
+
files: f.files.size,
|
|
6085
|
+
first: f.first,
|
|
6086
|
+
command: `atticus frameworks learn ${name} --from <its source, JAR or npm package> --package ${pkg}`
|
|
6087
|
+
};
|
|
6088
|
+
}).sort((a, b) => b.files - a.files || (a.package < b.package ? -1 : a.package > b.package ? 1 : 0));
|
|
6089
|
+
}
|
|
6090
|
+
|
|
4457
6091
|
export {
|
|
4458
6092
|
insideRepo,
|
|
4459
6093
|
dirSource,
|
|
4460
6094
|
correctionFor,
|
|
6095
|
+
outsidePairingFor,
|
|
4461
6096
|
correctedFact,
|
|
4462
6097
|
applyCorrections,
|
|
4463
6098
|
frameworkFacts,
|
|
@@ -4465,20 +6100,28 @@ export {
|
|
|
4465
6100
|
frameworkTopicsOfType,
|
|
4466
6101
|
frameworkGaps,
|
|
4467
6102
|
frameworksToLearn,
|
|
6103
|
+
readRulesOf,
|
|
6104
|
+
lineRangeOf,
|
|
4468
6105
|
parseReviewed,
|
|
4469
|
-
|
|
4470
|
-
staleMessage,
|
|
4471
|
-
verifyFacts,
|
|
6106
|
+
readingRank,
|
|
4472
6107
|
FILE_KINDS,
|
|
6108
|
+
fileKinds,
|
|
4473
6109
|
kindsOf,
|
|
4474
6110
|
numberedSlice,
|
|
4475
6111
|
sliceText,
|
|
4476
6112
|
planBatches,
|
|
6113
|
+
isEntryPointFile,
|
|
6114
|
+
NOT_REACHED_WHY,
|
|
6115
|
+
reachableModels,
|
|
6116
|
+
sameCommit,
|
|
6117
|
+
staleMessage,
|
|
6118
|
+
verifyFacts,
|
|
4477
6119
|
SHOWN_PROPOSALS,
|
|
4478
6120
|
proposeExclusions,
|
|
4479
6121
|
exclusionYaml,
|
|
4480
|
-
|
|
6122
|
+
sessionTokensFor,
|
|
4481
6123
|
planParts,
|
|
6124
|
+
planGaps,
|
|
4482
6125
|
VIEW_NAMES,
|
|
4483
6126
|
ViewError,
|
|
4484
6127
|
changeView,
|
|
@@ -4496,5 +6139,7 @@ export {
|
|
|
4496
6139
|
codeLinkerFor,
|
|
4497
6140
|
renderMap,
|
|
4498
6141
|
rereadPlan,
|
|
4499
|
-
onlyFiles
|
|
6142
|
+
onlyFiles,
|
|
6143
|
+
repairFormSlips,
|
|
6144
|
+
houseImportsUntaught
|
|
4500
6145
|
};
|