@maycombhq/atticus 2.0.0-beta.2 → 2.0.0-beta.21
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-265LWMOA.js +91 -0
- package/dist/chunk-26WBY34Y.js +37 -0
- package/dist/{chunk-UBBC2XSG.js → chunk-26Z7OGQW.js} +518 -93
- package/dist/chunk-2D7PWSOK.js +172 -0
- package/dist/{chunk-JCZIKA4C.js → chunk-2HH3UCZM.js} +4 -4
- package/dist/{chunk-GDLULHTC.js → chunk-2HTEMYTB.js} +12 -26
- package/dist/chunk-2UCRFXFZ.js +130 -0
- package/dist/{chunk-MVZVGYXH.js → chunk-2V2ZPQQU.js} +81 -13
- package/dist/{chunk-BOHJZTEQ.js → chunk-36RY7ZXM.js} +130 -71
- package/dist/{chunk-I3E7CX4H.js → chunk-37WE3LS2.js} +3 -3
- package/dist/{chunk-IPKHE5D6.js → chunk-3AQ2R5NL.js} +1520 -871
- package/dist/chunk-3G6S7LAA.js +43 -0
- package/dist/chunk-3J5I7ZOU.js +58 -0
- package/dist/{chunk-65NYPGRC.js → chunk-3LZ6P5EP.js} +1 -1
- package/dist/chunk-3SSTNB4E.js +191 -0
- package/dist/chunk-3TD6S4TC.js +49 -0
- package/dist/{chunk-3C7EYRFZ.js → chunk-3UTYPHN3.js} +1 -1
- package/dist/{chunk-G2B4ZQGH.js → chunk-3WON4G3M.js} +2 -2
- package/dist/{chunk-S3QNRWYR.js → chunk-42EPP7OB.js} +12 -12
- package/dist/chunk-455SYIIE.js +113 -0
- package/dist/{chunk-YISXLPHN.js → chunk-4B6BT75O.js} +166 -23
- package/dist/{chunk-C6EKFH7J.js → chunk-4CLK2RJO.js} +7 -7
- package/dist/chunk-4CYTJ5TZ.js +38 -0
- package/dist/{chunk-EYWIDJFU.js → chunk-4D62IZII.js} +5 -5
- package/dist/chunk-4FOAYLI4.js +328 -0
- package/dist/{chunk-7JUKDRSZ.js → chunk-4KNOJC6J.js} +294 -215
- package/dist/{chunk-YMZ3TNYB.js → chunk-4PYC2UGC.js} +2 -2
- package/dist/{chunk-27NKUS3X.js → chunk-4SLBCEPD.js} +2104 -726
- package/dist/chunk-4TKVU2I2.js +122 -0
- package/dist/{chunk-RUVFXE2L.js → chunk-4WXRPQDE.js} +448 -47
- package/dist/{chunk-7ZUIMTOU.js → chunk-5CMPVC3N.js} +24 -24
- package/dist/chunk-5G7ILBUG.js +66 -0
- package/dist/{chunk-HFQCFSJR.js → chunk-5PV7S2ZP.js} +3 -3
- package/dist/{chunk-FITWGOJ2.js → chunk-5QUOPNLN.js} +4 -4
- package/dist/{chunk-Z6LF7HQN.js → chunk-5TDRBOAV.js} +5 -5
- package/dist/{chunk-T6AIP7H5.js → chunk-5WERBKLE.js} +199 -63
- package/dist/{chunk-XPCM366L.js → chunk-62DLJSLV.js} +8 -8
- package/dist/{chunk-L24C55VN.js → chunk-64AADMTW.js} +99 -25
- package/dist/{chunk-K65BJIMT.js → chunk-67BP7HXL.js} +17 -17
- package/dist/chunk-6DQYZU2W.js +379 -0
- package/dist/{chunk-EDS2L2LD.js → chunk-6EXDI5GI.js} +52 -26
- package/dist/chunk-6FNAKCET.js +38 -0
- package/dist/{chunk-6IU67AEK.js → chunk-75ASLHBT.js} +21 -15
- package/dist/{chunk-A3GY6SOA.js → chunk-75ZOZFGH.js} +18 -18
- package/dist/{chunk-MGYZM27B.js → chunk-77O633L5.js} +49 -23
- package/dist/{chunk-GQ4N4WKF.js → chunk-7AB6OWTV.js} +3 -3
- package/dist/{chunk-M766ZJ6W.js → chunk-7CZHUZBL.js} +25 -25
- package/dist/{chunk-A7UPUQFY.js → chunk-7FHNFCHK.js} +643 -124
- package/dist/chunk-7FUIVIBG.js +415 -0
- package/dist/{chunk-6QSERWXH.js → chunk-7QPCGTPS.js} +21 -21
- package/dist/{chunk-LR3R6Z2A.js → chunk-A2G72UI4.js} +7 -7
- package/dist/{chunk-I4JRCD6M.js → chunk-AE4P57KK.js} +23 -17
- package/dist/{chunk-AVJDSJ2F.js → chunk-AHW3RY3X.js} +7 -7
- package/dist/chunk-AM6FP6JW.js +43 -0
- package/dist/{chunk-CSZ4C5N6.js → chunk-AOCWFA7T.js} +93 -10
- package/dist/{chunk-EPTBZ3GP.js → chunk-API5TOUB.js} +1 -1
- package/dist/{chunk-652QWTLZ.js → chunk-AZDITBCT.js} +3 -3
- package/dist/{chunk-DPUYDDQG.js → chunk-BA2HUMDN.js} +25 -3
- package/dist/chunk-BA3U7ALS.js +99 -0
- package/dist/{chunk-HVUPKCGJ.js → chunk-BC6KKZ67.js} +5 -5
- package/dist/{chunk-CKSITVS4.js → chunk-BEPCKDCP.js} +2554 -722
- package/dist/{chunk-3ALD2W2A.js → chunk-BPPU3PW7.js} +4 -4
- package/dist/{chunk-QRAOANID.js → chunk-BVWWPBKD.js} +21 -21
- package/dist/{chunk-ZDPAS56H.js → chunk-BXBCMGIQ.js} +7 -1
- package/dist/chunk-BXZ4PKIT.js +233 -0
- package/dist/chunk-C7CEK3KW.js +247 -0
- package/dist/chunk-CFH3QH26.js +29 -0
- package/dist/chunk-CG6CULP6.js +170 -0
- package/dist/chunk-CLD4RVTA.js +45 -0
- package/dist/{chunk-DKJA76CJ.js → chunk-CNPY4NTZ.js} +13 -13
- package/dist/chunk-CNU5Q2D5.js +575 -0
- package/dist/{chunk-T2UYZSCQ.js → chunk-COYN3UAN.js} +126 -23
- package/dist/{chunk-23W7VWKR.js → chunk-CPK7QSBG.js} +1360 -113
- package/dist/{chunk-4AK7IBVP.js → chunk-D2WSW4GU.js} +1 -1
- package/dist/{chunk-ABWHYWNI.js → chunk-D57MFJPP.js} +3 -3
- package/dist/chunk-D5KV5PSK.js +45 -0
- package/dist/chunk-D5MASS4I.js +48 -0
- package/dist/{chunk-7EOBHUMC.js → chunk-DCWN3HFV.js} +2 -2
- package/dist/{chunk-QSXK2RGH.js → chunk-DDJPM5YX.js} +2 -2
- package/dist/chunk-DN4A2Q2O.js +57 -0
- package/dist/{chunk-IHMLFRVV.js → chunk-DUPZPDCJ.js} +58 -16
- package/dist/{chunk-PRPPGYZG.js → chunk-E2VXBSIR.js} +4 -4
- package/dist/chunk-E57AOFYN.js +32 -0
- package/dist/{chunk-UGLN7QHV.js → chunk-ECLXZWAG.js} +12 -12
- package/dist/{chunk-5WG5YP4T.js → chunk-EDZ45WIR.js} +14 -14
- package/dist/chunk-EFCQSMXB.js +37 -0
- package/dist/{chunk-2ANOROX3.js → chunk-EZIN5E5B.js} +8 -8
- package/dist/{chunk-BWZ77RQT.js → chunk-F6H2RJ3N.js} +1 -1
- package/dist/{chunk-OLQJTJJU.js → chunk-FAJDKNSD.js} +7 -3
- package/dist/chunk-FGAGWYZB.js +47 -0
- package/dist/{chunk-4WBVUHXN.js → chunk-FLVZFTLQ.js} +18 -18
- package/dist/chunk-FQ6WH6PB.js +140 -0
- package/dist/{chunk-T6XQL73F.js → chunk-FRHD22WM.js} +61 -23
- package/dist/{chunk-XAKWMCXI.js → chunk-FVMPFCDS.js} +4 -4
- package/dist/{chunk-B2WLAWNJ.js → chunk-FY3QIRTL.js} +3 -3
- package/dist/{chunk-CPA6HPVR.js → chunk-FYBF5MB2.js} +15 -11
- package/dist/{chunk-V2YTAIQO.js → chunk-G5YJBNDZ.js} +21 -6
- package/dist/chunk-G6IQYM25.js +339 -0
- package/dist/chunk-G6X4YL4D.js +161 -0
- package/dist/{chunk-SJRGMG6A.js → chunk-GC2VK3JT.js} +25 -25
- package/dist/{chunk-SP2I63OO.js → chunk-GCS3FZOB.js} +77 -3
- package/dist/{chunk-6TACQN3N.js → chunk-GDZB57IU.js} +5 -5
- package/dist/chunk-GIFMBCTW.js +221 -0
- package/dist/{chunk-YY6ZQQBP.js → chunk-GK4PYVEC.js} +18 -8
- package/dist/{chunk-TDD55CJH.js → chunk-GNWAOVFQ.js} +6 -5
- package/dist/chunk-GS76RXQE.js +226 -0
- package/dist/{chunk-YYQN46FY.js → chunk-GSGIZ2OO.js} +23 -23
- package/dist/chunk-GUYL7RMK.js +60 -0
- package/dist/chunk-GX74YWR7.js +321 -0
- package/dist/chunk-HE3HRDNZ.js +122 -0
- package/dist/{chunk-AIOQPLA5.js → chunk-HGHRSF3E.js} +30 -12
- package/dist/{chunk-2RMYG36Q.js → chunk-HK3VOHIE.js} +198 -336
- package/dist/{chunk-QZSX3F7C.js → chunk-HS4BGB4G.js} +24 -22
- package/dist/{chunk-XY3P7JW7.js → chunk-HSZMPH5T.js} +834 -149
- package/dist/{chunk-L6SGQIR3.js → chunk-HWD4JPU7.js} +4 -2
- package/dist/{chunk-PCVDV3UZ.js → chunk-HXXP2NTE.js} +6 -6
- package/dist/{chunk-AA6YULYB.js → chunk-I2A3Z5AK.js} +7 -7
- package/dist/{chunk-N7IXEHK3.js → chunk-ICHDSWEY.js} +7 -7
- package/dist/chunk-IFFRGC3N.js +31 -0
- package/dist/{chunk-4BN53TG3.js → chunk-IFHDV6KQ.js} +31 -23
- package/dist/chunk-J2O4LFVI.js +80 -0
- package/dist/chunk-J5AAZ6OJ.js +161 -0
- package/dist/{chunk-ZMFPW66A.js → chunk-JAABT3DA.js} +3 -3
- package/dist/chunk-JITJP64K.js +34 -0
- package/dist/chunk-JKIGL3XQ.js +162 -0
- package/dist/chunk-JRVNXLZP.js +615 -0
- package/dist/chunk-JRXHON4E.js +41 -0
- package/dist/{chunk-DHPACDG7.js → chunk-JS6WMC3J.js} +8 -8
- package/dist/chunk-JU4QNJSR.js +62 -0
- package/dist/{chunk-PAMPPUR6.js → chunk-JYMIB5XY.js} +1697 -172
- package/dist/{chunk-RRTKKSLQ.js → chunk-JYTIHTIK.js} +8 -6
- package/dist/{chunk-3HI6Z55Y.js → chunk-K332XW7K.js} +324 -39
- package/dist/{chunk-GLUX4EEP.js → chunk-K6JB6XTX.js} +9 -9
- package/dist/chunk-KDAHXQIE.js +294 -0
- package/dist/chunk-KYZMRSPS.js +93 -0
- package/dist/chunk-L4CESDPB.js +22 -0
- package/dist/{chunk-NXFPS2LR.js → chunk-LEBR5NHW.js} +14 -14
- package/dist/chunk-LIJJCBQI.js +223 -0
- package/dist/{chunk-DE76JROX.js → chunk-LIRVU3HW.js} +16 -16
- package/dist/{chunk-JMSCMUCA.js → chunk-LN77DXBM.js} +145 -21
- package/dist/{chunk-JIITQXFB.js → chunk-LNWEWYCM.js} +2969 -788
- package/dist/{chunk-TANFV3MZ.js → chunk-LO3BLMVG.js} +3 -3
- package/dist/chunk-LPV6T362.js +250 -0
- package/dist/{chunk-2VHF7OXM.js → chunk-LQH4P45K.js} +1 -1
- package/dist/chunk-LU4MQVAL.js +41 -0
- package/dist/chunk-LWDNKE4X.js +273 -0
- package/dist/chunk-M4H2UG4R.js +218 -0
- package/dist/{chunk-6BVONWJ7.js → chunk-M6D2U3YM.js} +3 -3
- package/dist/chunk-MD7KUEJ3.js +514 -0
- package/dist/{chunk-66YVAGGM.js → chunk-MMZUN7ZO.js} +20 -20
- package/dist/chunk-MZGRDONP.js +916 -0
- package/dist/chunk-N3RUGTGV.js +39 -0
- package/dist/{chunk-JGZCG5VM.js → chunk-NCB6656Z.js} +7 -7
- package/dist/{chunk-4NDAAFQT.js → chunk-NRQ65LTQ.js} +7 -5
- package/dist/{chunk-UNM6ZOKP.js → chunk-NTAVOWYD.js} +145 -18
- package/dist/{chunk-3USYZGRF.js → chunk-NYOABJNK.js} +36 -27
- package/dist/{chunk-WVPJSRRJ.js → chunk-O4IKAIZ2.js} +24 -20
- package/dist/{chunk-D3PO4DOZ.js → chunk-ODMBPDDV.js} +51 -14
- package/dist/chunk-OKUB6RJA.js +533 -0
- package/dist/chunk-OMPENNWY.js +39 -0
- package/dist/chunk-OPQZISG3.js +25 -0
- package/dist/{chunk-5GGOLAKC.js → chunk-OV57AHCG.js} +26 -26
- package/dist/chunk-OZJ3JLYI.js +58 -0
- package/dist/{chunk-KEM4SDDP.js → chunk-P2VBXX7N.js} +11 -10
- package/dist/{chunk-S2A7YC3Q.js → chunk-PAINMSID.js} +10150 -1720
- package/dist/chunk-PKJAV25S.js +43 -0
- package/dist/{chunk-EBEW3XXQ.js → chunk-PNQLCRSS.js} +4 -4
- package/dist/{chunk-WY6PP6AA.js → chunk-PQH56YF6.js} +37 -5
- package/dist/chunk-PRTBPXYA.js +50 -0
- package/dist/{chunk-YEDDOYSC.js → chunk-PRW3HXM3.js} +2 -2
- package/dist/chunk-PRZ7KMWL.js +42 -0
- package/dist/{chunk-6M5NK3K3.js → chunk-PTQ3ZALZ.js} +42 -4
- package/dist/{chunk-5GDRCDQB.js → chunk-PUEWDYGP.js} +2 -2
- package/dist/chunk-Q26LDIUV.js +236 -0
- package/dist/{chunk-CR3MTH32.js → chunk-Q46QDDMF.js} +77 -7
- package/dist/chunk-QBNWF5ZU.js +19 -0
- package/dist/chunk-QE527XRY.js +342 -0
- package/dist/{chunk-CNUKCS2T.js → chunk-QHJJMZLX.js} +1518 -54
- package/dist/chunk-QJCJOMW3.js +425 -0
- package/dist/{chunk-VYDBJ73M.js → chunk-QKMRFNMA.js} +15 -15
- package/dist/chunk-QOOEYDNJ.js +317 -0
- package/dist/chunk-QSIDVFB7.js +45 -0
- package/dist/{chunk-R77KLRB3.js → chunk-RA33EYS4.js} +19 -18
- package/dist/{chunk-DFBCSHVT.js → chunk-RALY3E5U.js} +4 -4
- package/dist/{chunk-ZCJDOK2S.js → chunk-S2RY3BOD.js} +35 -21
- package/dist/chunk-S5LSDXV4.js +40 -0
- package/dist/chunk-SB5GRUFM.js +54 -0
- package/dist/chunk-SH27FYPF.js +150 -0
- package/dist/chunk-T74WQQGS.js +40 -0
- package/dist/{chunk-O3WI5KWW.js → chunk-TGVVUKTG.js} +2 -2
- package/dist/chunk-THN26IEF.js +48 -0
- package/dist/chunk-TJH2EUAZ.js +47 -0
- package/dist/{chunk-ZNCEKR4J.js → chunk-TQ4TG57V.js} +12 -8
- package/dist/{chunk-7FBGJFEE.js → chunk-TQTRWENN.js} +12 -12
- package/dist/{chunk-TYAID7B7.js → chunk-TSM6T6GQ.js} +23 -23
- package/dist/{chunk-DPZHZQKE.js → chunk-TW3KOSQK.js} +43 -10
- package/dist/{chunk-MC3H7736.js → chunk-TWQRPM76.js} +37 -4
- package/dist/chunk-TYD6I4CZ.js +109 -0
- package/dist/chunk-U3NBK4EU.js +420 -0
- package/dist/{chunk-6JCPI55B.js → chunk-UDXWIPCB.js} +47 -7
- package/dist/{chunk-MMOSZPWJ.js → chunk-UEVZZZU2.js} +4 -4
- package/dist/{chunk-JBTK5OP2.js → chunk-UF4UHCIG.js} +2 -2
- package/dist/{chunk-6JUBZBMW.js → chunk-UFE36WJH.js} +4 -4
- package/dist/{chunk-ONLS5QA5.js → chunk-URIKUID7.js} +12 -12
- package/dist/chunk-UYLBRRFK.js +1106 -0
- package/dist/chunk-UZACEG2C.js +515 -0
- package/dist/{chunk-7SW2QWLU.js → chunk-VAMHOHUU.js} +12 -12
- package/dist/{chunk-RZPYKDWS.js → chunk-VUVGX5L2.js} +1 -1
- package/dist/{chunk-Q4D5X7QB.js → chunk-VYOCPTUB.js} +44 -24
- package/dist/{chunk-CAQZUNOH.js → chunk-WAZEXEJH.js} +567 -3
- package/dist/chunk-WCEF7H6L.js +221 -0
- package/dist/{chunk-2DIQQJCB.js → chunk-WEOMJHPV.js} +12 -12
- package/dist/{chunk-FHH4I4ZV.js → chunk-WFU77T3A.js} +12 -6
- package/dist/chunk-WIWTJMMR.js +292 -0
- package/dist/{chunk-2BFCZ3RK.js → chunk-WNTIOBOG.js} +18 -18
- package/dist/{chunk-7WD63HU2.js → chunk-X2CUXTWU.js} +6 -6
- package/dist/chunk-X7EMBM5X.js +34 -0
- package/dist/chunk-XEQCQO66.js +33 -0
- package/dist/{chunk-F7HYOUFF.js → chunk-XG4WJHLQ.js} +20 -4
- package/dist/{chunk-EFHQ3TWP.js → chunk-XOL3GFW2.js} +171 -31
- package/dist/{chunk-NE2W3OO7.js → chunk-XQ3YZ4QW.js} +22 -22
- package/dist/{chunk-CEXSU4S6.js → chunk-Y6K3CJBU.js} +723 -85
- package/dist/{chunk-4RS5KZQD.js → chunk-YAH5IQAL.js} +5 -5
- package/dist/{chunk-64EQYWCA.js → chunk-YBOVNMHC.js} +51 -14
- package/dist/chunk-YF5DZCPN.js +1212 -0
- package/dist/chunk-YIGBJNH4.js +37 -0
- package/dist/{chunk-BKVWTZ6A.js → chunk-YJ3TBOTK.js} +25 -25
- package/dist/{chunk-Q5AMFEWD.js → chunk-YKTE2RU3.js} +2 -2
- package/dist/{chunk-7EKXJZ2Y.js → chunk-YO2PKTXT.js} +296 -37
- package/dist/{chunk-6J5OKA4Y.js → chunk-YTTNTQOX.js} +15 -15
- package/dist/{chunk-OT2O76OS.js → chunk-YWAKDNMH.js} +183 -33
- package/dist/{chunk-R6XF6UR3.js → chunk-YZDZOEV4.js} +4 -4
- package/dist/chunk-Z3QFH7YG.js +17 -0
- package/dist/{chunk-W5BVQF44.js → chunk-Z6VLC5X4.js} +17 -17
- package/dist/{chunk-HLJAXMM4.js → chunk-ZFQMMU5Y.js} +2 -2
- package/dist/{chunk-W3BQAZ3Q.js → chunk-ZKYRDV7A.js} +12 -12
- package/dist/{chunk-ZMAINWBW.js → chunk-ZLS6PU2O.js} +3 -3
- package/dist/{chunk-ZVM6AKKM.js → chunk-ZOSD4FXV.js} +3 -3
- package/dist/chunk-ZPDDEVQX.js +47 -0
- package/dist/{chunk-TXLH6WHQ.js → chunk-ZQLRCA2V.js} +11 -11
- package/dist/{chunk-HITVBIJS.js → chunk-ZX7235KR.js} +128 -54
- package/dist/{chunk-GT4I4ZWM.js → chunk-ZZFHLRQW.js} +18 -16
- package/dist/files.json +636 -364
- package/dist/packages/naysayer/constitution/constitution.yaml +19 -0
- package/dist/packages/sdk/npm/index.js +5614 -2343
- package/dist/packages/ui/dist/assets/{elk-api-tma0MCuh.js → elk-api-CowTnhEJ.js} +2 -2
- package/dist/packages/ui/dist/assets/index-Dw20IRhQ.css +1 -0
- package/dist/packages/ui/dist/assets/index-z2wv0d5d.js +47 -0
- package/dist/packages/ui/dist/index.html +2 -2
- package/package.json +4 -4
- 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-RVH5SUW5.js +0 -69
- package/dist/chunk-VN3DP4LQ.js +0 -43
- 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,27 +3,47 @@ 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-TWQRPM76.js";
|
|
6
9
|
import {
|
|
7
10
|
globToRegExp
|
|
8
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-7AB6OWTV.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
|
+
factIdPart,
|
|
29
|
+
forwardSlashes,
|
|
30
|
+
gatewayBundleTest,
|
|
31
|
+
isPatternPath,
|
|
16
32
|
isSecretPath,
|
|
17
33
|
lineNamesClaim,
|
|
34
|
+
linksFor,
|
|
18
35
|
normalizePath,
|
|
36
|
+
noteProblem,
|
|
19
37
|
partsBuildWord,
|
|
20
38
|
partsNamePath,
|
|
39
|
+
plainPathPrefix,
|
|
21
40
|
quoteMatches,
|
|
22
41
|
quoteNotRequired,
|
|
23
42
|
redactSecrets,
|
|
24
43
|
staleCite,
|
|
25
|
-
toCanonicalYaml
|
|
26
|
-
|
|
44
|
+
toCanonicalYaml,
|
|
45
|
+
underFolders
|
|
46
|
+
} from "./chunk-PAINMSID.js";
|
|
27
47
|
|
|
28
48
|
// packages/verify/dist/source.js
|
|
29
49
|
import { readdirSync, readFileSync, statSync } from "node:fs";
|
|
@@ -38,6 +58,13 @@ var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
|
38
58
|
".vscode",
|
|
39
59
|
".gradle",
|
|
40
60
|
".mvn",
|
|
61
|
+
"__pycache__",
|
|
62
|
+
".venv",
|
|
63
|
+
"venv",
|
|
64
|
+
".next",
|
|
65
|
+
".nuxt"
|
|
66
|
+
]);
|
|
67
|
+
var BUILD_OUTPUT = /* @__PURE__ */ new Set([
|
|
41
68
|
"target",
|
|
42
69
|
"build",
|
|
43
70
|
"dist",
|
|
@@ -45,13 +72,32 @@ var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
|
45
72
|
"bin",
|
|
46
73
|
"obj",
|
|
47
74
|
"vendor",
|
|
48
|
-
"__pycache__",
|
|
49
|
-
".venv",
|
|
50
|
-
"venv",
|
|
51
|
-
".next",
|
|
52
|
-
".nuxt",
|
|
53
75
|
"coverage"
|
|
54
76
|
]);
|
|
77
|
+
var DOTNET = /\.(csproj|fsproj|vbproj|sln)$/i;
|
|
78
|
+
var NODE = /^package\.json$/;
|
|
79
|
+
var MADE_BESIDE = {
|
|
80
|
+
bin: DOTNET,
|
|
81
|
+
obj: DOTNET,
|
|
82
|
+
vendor: /^(go\.mod|composer\.json|Gemfile)$/,
|
|
83
|
+
target: /^(pom\.xml|build\.sbt|Cargo\.toml)$/,
|
|
84
|
+
build: /^(build\.gradle(\.kts)?|settings\.gradle(\.kts)?|package\.json|CMakeLists\.txt)$/,
|
|
85
|
+
dist: NODE,
|
|
86
|
+
coverage: NODE,
|
|
87
|
+
out: /^(tsconfig\.json|package\.json)$|\.iml$/
|
|
88
|
+
};
|
|
89
|
+
var ONLY_BESIDE = /* @__PURE__ */ new Set(["bin", "obj", "vendor"]);
|
|
90
|
+
function skippedFolder(above, name, siblings) {
|
|
91
|
+
if (SKIP_DIRS.has(name))
|
|
92
|
+
return true;
|
|
93
|
+
if (!BUILD_OUTPUT.has(name))
|
|
94
|
+
return false;
|
|
95
|
+
if (siblings.some((x) => MADE_BESIDE[name].test(x)))
|
|
96
|
+
return true;
|
|
97
|
+
if (above.some((a) => a.toLowerCase() === "src"))
|
|
98
|
+
return false;
|
|
99
|
+
return !ONLY_BESIDE.has(name);
|
|
100
|
+
}
|
|
55
101
|
function insideRepo(path) {
|
|
56
102
|
if (path === "" || isAbsolute(path) || /^[A-Za-z]:/.test(path))
|
|
57
103
|
return false;
|
|
@@ -60,7 +106,7 @@ function insideRepo(path) {
|
|
|
60
106
|
}
|
|
61
107
|
function dirSource(root) {
|
|
62
108
|
let cache;
|
|
63
|
-
const walk = (dir, out) => {
|
|
109
|
+
const walk = (dir, above, out) => {
|
|
64
110
|
let names;
|
|
65
111
|
try {
|
|
66
112
|
names = readdirSync(dir).sort();
|
|
@@ -76,8 +122,8 @@ function dirSource(root) {
|
|
|
76
122
|
continue;
|
|
77
123
|
}
|
|
78
124
|
if (st.isDirectory()) {
|
|
79
|
-
if (!
|
|
80
|
-
walk(full, out);
|
|
125
|
+
if (!skippedFolder(above, name, names))
|
|
126
|
+
walk(full, [...above, name], out);
|
|
81
127
|
} else if (st.isFile())
|
|
82
128
|
out.push(relative(root, full).split(sep).join("/"));
|
|
83
129
|
}
|
|
@@ -86,7 +132,7 @@ function dirSource(root) {
|
|
|
86
132
|
list() {
|
|
87
133
|
if (!cache) {
|
|
88
134
|
cache = [];
|
|
89
|
-
walk(root, cache);
|
|
135
|
+
walk(root, [], cache);
|
|
90
136
|
}
|
|
91
137
|
return cache;
|
|
92
138
|
},
|
|
@@ -121,6 +167,11 @@ var FRAMEWORKS = [
|
|
|
121
167
|
var KNOWN = [
|
|
122
168
|
// Java and Kotlin
|
|
123
169
|
["spring-boot*", { name: "spring-boot", topic: "spring", shape: "rest" }],
|
|
170
|
+
// the annotations a Spring Boot app's main class carries, which a reader may cite instead
|
|
171
|
+
// (matched by `annotationKey`, so bare, qualified or with arguments)
|
|
172
|
+
["@springbootapplication", { name: "spring-boot", topic: "spring", shape: "rest" }],
|
|
173
|
+
["@enableautoconfiguration", { name: "spring-boot", topic: "spring", shape: "rest" }],
|
|
174
|
+
["@quarkusmain", { name: "quarkus", topic: "quarkus" }],
|
|
124
175
|
["spring-webmvc", { name: "spring", topic: "spring", shape: "rest" }],
|
|
125
176
|
["spring-webflux", { name: "spring", topic: "spring", shape: "rest" }],
|
|
126
177
|
["spring-web", { name: "spring", topic: "spring", shape: "rest" }],
|
|
@@ -172,23 +223,45 @@ function declarationKey(declared) {
|
|
|
172
223
|
const name = colon.split(/[=<>~!,\s[]/)[0] ?? colon;
|
|
173
224
|
return name.replace(/^['"]|['"]$/g, "");
|
|
174
225
|
}
|
|
175
|
-
function
|
|
226
|
+
function annotationKey(declared) {
|
|
227
|
+
const m = /^@?(?:[a-z_$][\w$]*\.)*([A-Z][\w$]*)\s*(?:\(.*)?$/s.exec(declared.trim());
|
|
228
|
+
return m ? `@${m[1].toLowerCase()}` : void 0;
|
|
229
|
+
}
|
|
230
|
+
function houseKnown(extra) {
|
|
231
|
+
return extra.map((e) => [
|
|
232
|
+
e.declares.toLowerCase(),
|
|
233
|
+
{
|
|
234
|
+
name: e.name,
|
|
235
|
+
...e.topic && FRAMEWORKS.includes(e.topic) ? { topic: e.topic } : {},
|
|
236
|
+
...e.shape ? { shape: e.shape } : {}
|
|
237
|
+
}
|
|
238
|
+
]);
|
|
239
|
+
}
|
|
240
|
+
function frameworkOf(declared, extra = []) {
|
|
176
241
|
const key = declarationKey(declared);
|
|
177
242
|
if (!key)
|
|
178
243
|
return void 0;
|
|
179
|
-
|
|
244
|
+
const table = extra.length ? [...houseKnown(extra), ...KNOWN] : KNOWN;
|
|
245
|
+
for (const [pattern, known] of table) {
|
|
180
246
|
const star = pattern.endsWith("*");
|
|
181
247
|
const p = star ? pattern.slice(0, -1) : pattern;
|
|
182
248
|
if (star ? key.startsWith(p) : key === p)
|
|
183
249
|
return known;
|
|
184
250
|
}
|
|
251
|
+
const ann = annotationKey(declared);
|
|
252
|
+
if (ann) {
|
|
253
|
+
for (const [pattern, known] of table)
|
|
254
|
+
if (pattern === ann)
|
|
255
|
+
return known;
|
|
256
|
+
}
|
|
185
257
|
return void 0;
|
|
186
258
|
}
|
|
187
|
-
function frameworkNameProblem(f) {
|
|
188
|
-
const known = frameworkOf(f.cite.value);
|
|
259
|
+
function frameworkNameProblem(f, extra = []) {
|
|
260
|
+
const known = frameworkOf(f.cite.value, extra);
|
|
189
261
|
const name = f.name.trim().toLowerCase();
|
|
190
262
|
if (known) {
|
|
191
|
-
|
|
263
|
+
const legacy = annotationKey(f.cite.value) === `@${name}`;
|
|
264
|
+
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
265
|
}
|
|
193
266
|
const key = declarationKey(f.cite.value);
|
|
194
267
|
if (declarationKey(name) === key || name === key)
|
|
@@ -196,13 +269,13 @@ function frameworkNameProblem(f) {
|
|
|
196
269
|
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
270
|
}
|
|
198
271
|
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) {
|
|
272
|
+
function frameworkTopics(facts, extra = []) {
|
|
200
273
|
const fws = frameworkFacts(facts);
|
|
201
274
|
if (!fws.length)
|
|
202
275
|
return void 0;
|
|
203
276
|
const topics = /* @__PURE__ */ new Set();
|
|
204
277
|
for (const f of fws) {
|
|
205
|
-
const known = frameworkOf(f.cite.value);
|
|
278
|
+
const known = frameworkOf(f.cite.value, extra);
|
|
206
279
|
if (!known?.topic)
|
|
207
280
|
topics.add("house");
|
|
208
281
|
else
|
|
@@ -228,7 +301,7 @@ var SHAPE_ASK = {
|
|
|
228
301
|
store: "which tables it reads and writes"
|
|
229
302
|
};
|
|
230
303
|
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) {
|
|
304
|
+
function frameworkGaps(facts, extra = []) {
|
|
232
305
|
const fws = frameworkFacts(facts);
|
|
233
306
|
if (!fws.length)
|
|
234
307
|
return [];
|
|
@@ -236,7 +309,7 @@ function frameworkGaps(facts) {
|
|
|
236
309
|
return [];
|
|
237
310
|
const out = [];
|
|
238
311
|
for (const f of fws) {
|
|
239
|
-
const shape2 = frameworkOf(f.cite.value)?.shape;
|
|
312
|
+
const shape2 = frameworkOf(f.cite.value, extra)?.shape;
|
|
240
313
|
if (!shape2 || holdsShape(facts, shape2))
|
|
241
314
|
continue;
|
|
242
315
|
out.push({
|
|
@@ -249,9 +322,14 @@ function frameworkGaps(facts) {
|
|
|
249
322
|
}
|
|
250
323
|
return out.sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
|
|
251
324
|
}
|
|
252
|
-
function
|
|
325
|
+
function isApigeeBundle(f) {
|
|
326
|
+
const file = f.cite.file.replace(/\\/g, "/").toLowerCase();
|
|
327
|
+
const said = `${f.cite.value} ${f.cite.quote ?? ""}`;
|
|
328
|
+
return /<\s*(APIProxy|SharedFlowBundle)\b/i.test(said) || /^(apiproxy|sharedflowbundle)$/i.test(f.cite.value.trim()) || /(^|\/)(apiproxy|sharedflowbundle|sharedflows)\//.test(file);
|
|
329
|
+
}
|
|
330
|
+
function frameworksToLearn(facts, known = [], extra = []) {
|
|
253
331
|
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) => ({
|
|
332
|
+
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
333
|
id: f.id,
|
|
256
334
|
framework: f.name,
|
|
257
335
|
cite: `${f.cite.file}:${f.cite.line}`,
|
|
@@ -260,6 +338,88 @@ function frameworksToLearn(facts, known = []) {
|
|
|
260
338
|
}));
|
|
261
339
|
}
|
|
262
340
|
|
|
341
|
+
// packages/verify/dist/reviewed.js
|
|
342
|
+
var LINE_RANGE = /^(.+)#L(\d+)-L(\d+)$/;
|
|
343
|
+
function lineRangeOf(path) {
|
|
344
|
+
const m = LINE_RANGE.exec(path);
|
|
345
|
+
return m ? { file: m[1], from: Number(m[2]), to: Number(m[3]) } : void 0;
|
|
346
|
+
}
|
|
347
|
+
var SHAPE = `{"path": "<file or glob>", "why": "..."}`;
|
|
348
|
+
function parseReviewed(text) {
|
|
349
|
+
const entries = [];
|
|
350
|
+
const bad = [];
|
|
351
|
+
text.split(/\r?\n/).forEach((raw, i) => {
|
|
352
|
+
if (!raw.trim())
|
|
353
|
+
return;
|
|
354
|
+
const line = i + 1;
|
|
355
|
+
let json2;
|
|
356
|
+
try {
|
|
357
|
+
json2 = JSON.parse(raw);
|
|
358
|
+
} catch (e) {
|
|
359
|
+
bad.push({ line, raw, message: `line ${line} is not JSON: ${e.message}` });
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
const o = json2;
|
|
363
|
+
const issues = [];
|
|
364
|
+
if (!o || typeof o !== "object" || Array.isArray(o))
|
|
365
|
+
issues.push("not an object");
|
|
366
|
+
else {
|
|
367
|
+
if (typeof o.path !== "string" || !o.path.trim())
|
|
368
|
+
issues.push("path: a file or glob");
|
|
369
|
+
if (typeof o.why !== "string" || !o.why.trim())
|
|
370
|
+
issues.push("why: why it holds nothing");
|
|
371
|
+
const extra = Object.keys(o).filter((k) => k !== "path" && k !== "why");
|
|
372
|
+
if (extra.length)
|
|
373
|
+
issues.push(`unknown keys: ${extra.join(", ")}`);
|
|
374
|
+
}
|
|
375
|
+
if (issues.length) {
|
|
376
|
+
bad.push({ line, raw, message: `line ${line} isn't ${SHAPE}: ${issues.join("; ")}` });
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
const path = forwardSlashes(o.path.trim());
|
|
380
|
+
entries.push({ line, path, why: o.why.trim() });
|
|
381
|
+
});
|
|
382
|
+
return { entries, bad };
|
|
383
|
+
}
|
|
384
|
+
var isGlob = (path) => /[*?]/.test(path);
|
|
385
|
+
|
|
386
|
+
// packages/verify/dist/conventions.js
|
|
387
|
+
var SPEC_NAME = /(^|\/)[^/]*(openapi|swagger|asyncapi|api-docs)[^/]*\.(ya?ml|json|xml)$|\.oas\.(ya?ml|json)$/i;
|
|
388
|
+
function notReadTest(folders) {
|
|
389
|
+
const multi = folders.filter((f) => f.includes("/"));
|
|
390
|
+
const multiRe = underFolders(multi);
|
|
391
|
+
const names = new Set(folders.filter((f) => !f.includes("/")).map((f) => f.toLowerCase()));
|
|
392
|
+
return {
|
|
393
|
+
test(path) {
|
|
394
|
+
if (multiRe.test(path))
|
|
395
|
+
return true;
|
|
396
|
+
const parts = path.split("/").slice(0, -1).map((x) => x.toLowerCase());
|
|
397
|
+
const src = parts.indexOf("src");
|
|
398
|
+
return parts.some((x, j) => names.has(x) && !(src >= 0 && j > src && BUILD_OUTPUT.has(x)));
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
function rulesOf(c) {
|
|
403
|
+
const folders = underFolders(c.apiSpecFolders, { tail: "(.+/)?[^/]+\\.(ya?ml|json)$" });
|
|
404
|
+
return {
|
|
405
|
+
apiSpec: new RegExp(`${SPEC_NAME.source}|${folders.source}`, "i"),
|
|
406
|
+
notProduct: underFolders(c.notProductFolders),
|
|
407
|
+
notRead: notReadTest(c.notReadFolders),
|
|
408
|
+
gatewayBundle: gatewayBundleTest(c.gatewayBundleFolders),
|
|
409
|
+
frameworks: c.frameworks
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
var DEFAULT_READ_RULES = rulesOf(DEFAULT_CONVENTIONS);
|
|
413
|
+
var made = /* @__PURE__ */ new WeakMap();
|
|
414
|
+
function readRulesOf(c) {
|
|
415
|
+
if (!c || c === DEFAULT_CONVENTIONS)
|
|
416
|
+
return DEFAULT_READ_RULES;
|
|
417
|
+
let r = made.get(c);
|
|
418
|
+
if (!r)
|
|
419
|
+
made.set(c, r = rulesOf(c));
|
|
420
|
+
return r;
|
|
421
|
+
}
|
|
422
|
+
|
|
263
423
|
// packages/verify/dist/apigee-policy.js
|
|
264
424
|
var MAKES = {
|
|
265
425
|
OAuthV1: ["oauth2"],
|
|
@@ -328,8 +488,57 @@ function authPolicyMismatch(auth, file, policy, lines) {
|
|
|
328
488
|
}
|
|
329
489
|
var authChecks = (auth) => CHECKS[auth] ?? auth;
|
|
330
490
|
|
|
491
|
+
// packages/verify/dist/call-security-lines.js
|
|
492
|
+
var CLIENT_CERT = /client[-_ .]?cert|keystore|key[-_ .]?store|ssl[-_ .]?bundle|\.pfx\b|\.p12\b|pkcs12|\bpfx\b|\bp12\b/i;
|
|
493
|
+
var CERT_AND_KEY = (l) => /\bcert\b/i.test(l) && /\bkey\b/i.test(l);
|
|
494
|
+
var KINDS = {
|
|
495
|
+
"tls mtls": {
|
|
496
|
+
test: (l) => CLIENT_CERT.test(l) || CERT_AND_KEY(l),
|
|
497
|
+
needs: "a client certificate, a key store or an SSL bundle"
|
|
498
|
+
},
|
|
499
|
+
"tls tls": { test: (l) => /https|ssl|tls/i.test(l), needs: "https, SSL or TLS" },
|
|
500
|
+
"tls none": {
|
|
501
|
+
test: (l) => !/https:\/\//i.test(l) && (/http:\/\//i.test(l) || /\b(false|disabled?|insecure|plain-?text|off)\b/i.test(l)),
|
|
502
|
+
needs: "a plain http:// URL or a TLS switch set off"
|
|
503
|
+
},
|
|
504
|
+
"auth oauth2": {
|
|
505
|
+
test: (l) => /oauth|token|bearer|jwt|authori[sz]|client[-_ ]?credentials|access[-_ ]?token/i.test(l),
|
|
506
|
+
needs: "a token (OAuth, bearer, JWT or an Authorization header)"
|
|
507
|
+
},
|
|
508
|
+
"auth api-key": {
|
|
509
|
+
test: (l) => /api[-_ ]?key|apikey|x-api|subscription[-_ ]?key|[a-z0-9][-_]key\b/i.test(l),
|
|
510
|
+
needs: "a key"
|
|
511
|
+
},
|
|
512
|
+
"auth basic": {
|
|
513
|
+
test: (l) => /basic|password|passwd|username|credential/i.test(l),
|
|
514
|
+
needs: "a password or basic credentials"
|
|
515
|
+
},
|
|
516
|
+
"auth mtls": {
|
|
517
|
+
test: (l) => CLIENT_CERT.test(l) || CERT_AND_KEY(l),
|
|
518
|
+
needs: "a client certificate, a key store or an SSL bundle"
|
|
519
|
+
}
|
|
520
|
+
};
|
|
521
|
+
function securityLineKind(what, claim, lines) {
|
|
522
|
+
const k = KINDS[`${what} ${claim}`];
|
|
523
|
+
if (!k)
|
|
524
|
+
return void 0;
|
|
525
|
+
return lines.some((l) => k.test(l)) ? void 0 : k.needs;
|
|
526
|
+
}
|
|
527
|
+
function lineOfValue(lines, line, value) {
|
|
528
|
+
if (!lines)
|
|
529
|
+
return void 0;
|
|
530
|
+
const v = value.toLowerCase();
|
|
531
|
+
for (const n of [line, line - 1, line + 1]) {
|
|
532
|
+
const l = lines[n - 1];
|
|
533
|
+
if (l !== void 0 && l.toLowerCase().includes(v))
|
|
534
|
+
return l;
|
|
535
|
+
}
|
|
536
|
+
return lines[line - 1];
|
|
537
|
+
}
|
|
538
|
+
|
|
331
539
|
// packages/verify/dist/corrections.js
|
|
332
|
-
function correctionFor(f,
|
|
540
|
+
function correctionFor(f, all2) {
|
|
541
|
+
const corrections = all2.filter((c) => c.action !== "link" && c.action !== "unlink");
|
|
333
542
|
const at = (c) => c.target.kind === f.kind && c.target.file === f.cite.file && c.target.line === f.cite.line;
|
|
334
543
|
const byId = corrections.filter((c) => c.fact === f.id && at(c));
|
|
335
544
|
const byCite = corrections.filter((c) => at(c) && c.target.value === f.cite.value);
|
|
@@ -337,6 +546,10 @@ function correctionFor(f, corrections) {
|
|
|
337
546
|
const moved = edited.filter((c) => (c.line ?? c.target.line) === f.cite.line);
|
|
338
547
|
return [...byId, ...byCite, ...moved].sort((a, b) => a.at < b.at ? 1 : a.at > b.at ? -1 : 0)[0];
|
|
339
548
|
}
|
|
549
|
+
function outsidePairingFor(f, all2) {
|
|
550
|
+
const newest = linksFor(f, all2)[0];
|
|
551
|
+
return newest?.link?.system ? newest : void 0;
|
|
552
|
+
}
|
|
340
553
|
function correctedFact(f, c) {
|
|
341
554
|
const raw = JSON.parse(JSON.stringify(f));
|
|
342
555
|
for (const [k, v] of Object.entries(c.set ?? {})) {
|
|
@@ -363,33 +576,64 @@ function applyCorrections(facts, corrections) {
|
|
|
363
576
|
const applied = [];
|
|
364
577
|
const used = /* @__PURE__ */ new Set();
|
|
365
578
|
const stale = [];
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
579
|
+
const met = /* @__PURE__ */ new Set();
|
|
580
|
+
const withPairing = (f, as, over) => {
|
|
581
|
+
const p = outsidePairingFor(as, corrections);
|
|
582
|
+
if (!p)
|
|
583
|
+
return f;
|
|
584
|
+
if (over && over.at > p.at && over.set?.to !== void 0)
|
|
585
|
+
return f;
|
|
586
|
+
const fixed = correctedFact(f, { ...p, action: "edit", set: { to: p.link.system } });
|
|
587
|
+
if (typeof fixed === "string") {
|
|
588
|
+
stale.push({
|
|
589
|
+
correction: p,
|
|
590
|
+
why: "invalid",
|
|
591
|
+
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`
|
|
592
|
+
});
|
|
593
|
+
return f;
|
|
594
|
+
}
|
|
595
|
+
applied.push({ fact: f.id, action: "link", by: p.by });
|
|
596
|
+
return { ...fixed, conf: "inferred" };
|
|
597
|
+
};
|
|
598
|
+
for (const f0 of facts) {
|
|
599
|
+
for (const l of linksFor(f0, corrections))
|
|
600
|
+
met.add(l);
|
|
601
|
+
const c = correctionFor(f0, corrections);
|
|
602
|
+
if (c?.action === "wrong") {
|
|
603
|
+
used.add(c);
|
|
604
|
+
applied.push({ fact: f0.id, action: "wrong", by: c.by });
|
|
370
605
|
continue;
|
|
371
606
|
}
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
applied.push({ fact: f.id, action: "wrong", by: c.by });
|
|
607
|
+
if (!c) {
|
|
608
|
+
out.push(withPairing(f0, f0));
|
|
375
609
|
continue;
|
|
376
610
|
}
|
|
377
|
-
|
|
611
|
+
used.add(c);
|
|
612
|
+
const fixed = correctedFact(f0, c);
|
|
378
613
|
if (typeof fixed === "string") {
|
|
379
614
|
stale.push({
|
|
380
615
|
correction: c,
|
|
381
616
|
why: "invalid",
|
|
382
|
-
message: `${c.by}'s correction of ${
|
|
617
|
+
message: `${c.by}'s correction of ${f0.kind} ${f0.id} doesn't make a fact (${fixed}); the fact is kept as it was`
|
|
383
618
|
});
|
|
384
|
-
out.push(
|
|
619
|
+
out.push(withPairing(f0, f0));
|
|
385
620
|
continue;
|
|
386
621
|
}
|
|
387
|
-
applied.push({ fact:
|
|
388
|
-
out.push(fixed);
|
|
622
|
+
applied.push({ fact: f0.id, action: "edit", by: c.by });
|
|
623
|
+
out.push(withPairing(fixed, f0, c));
|
|
389
624
|
}
|
|
390
625
|
for (const c of corrections) {
|
|
391
626
|
if (used.has(c))
|
|
392
627
|
continue;
|
|
628
|
+
if (c.action === "link" || c.action === "unlink") {
|
|
629
|
+
if (!met.has(c))
|
|
630
|
+
stale.push({
|
|
631
|
+
correction: c,
|
|
632
|
+
why: "none",
|
|
633
|
+
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`
|
|
634
|
+
});
|
|
635
|
+
continue;
|
|
636
|
+
}
|
|
393
637
|
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
638
|
if (replaced || c.action === "wrong")
|
|
395
639
|
continue;
|
|
@@ -1199,10 +1443,11 @@ function isLocaleBundle(path) {
|
|
|
1199
1443
|
}
|
|
1200
1444
|
var DATA_FILE_LINES = 2e3;
|
|
1201
1445
|
var DATA_FILE_CHARS = 2e5;
|
|
1446
|
+
var API_SPEC = DEFAULT_READ_RULES.apiSpec;
|
|
1202
1447
|
var DATA_EXT = /\.(json|xml)$/i;
|
|
1203
1448
|
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))
|
|
1449
|
+
function isDataFile(path, lines, rules = DEFAULT_READ_RULES) {
|
|
1450
|
+
if (!DATA_EXT.test(path) || KEEP_BIG.test(path) || rules.apiSpec.test(path))
|
|
1206
1451
|
return false;
|
|
1207
1452
|
if (lines.length - (lines[lines.length - 1] === "" ? 1 : 0) > DATA_FILE_LINES)
|
|
1208
1453
|
return true;
|
|
@@ -1252,66 +1497,502 @@ function pageRouteMatches(file, lines, exists, path) {
|
|
|
1252
1497
|
return norm(route) === norm(path);
|
|
1253
1498
|
}
|
|
1254
1499
|
|
|
1255
|
-
// packages/verify/dist/
|
|
1256
|
-
var
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1500
|
+
// packages/verify/dist/reachable.js
|
|
1501
|
+
var CODE = /\.(java|kt|kts|scala|groovy|cs|vb|fs|py|rb|go|rs|php|swift|ts|tsx|js|mjs|cjs|dart)$/i;
|
|
1502
|
+
var MODEL_DIR = /(^|\/)(models?|dtos?|payloads?|schemas?|pojos?|beans|viewmodels?)\//i;
|
|
1503
|
+
var MODEL_NAME = /(dto|request|response|payload|model|body)$/i;
|
|
1504
|
+
var MODEL_PART = /[._-](dto|model|schema|payload|request|response)\.[^/.]+$/i;
|
|
1505
|
+
var MAPS_TABLE = /@Entity\b|@Table\b|@Document\b|\[Table\(|__tablename__|models\.Model\b|@Entity\(|\bgorm\.Model\b/;
|
|
1506
|
+
var ROOT_PART = /[._-](controller|resolver|gateway|handlers?|router|routes?|consumer|listener|subscriber|client|publisher|producer|endpoints?)\.[^/.]+$/i;
|
|
1507
|
+
var CLIENT_NAME = /(client|gateway|publisher|producer|sender|feign|proxy|connector)$/i;
|
|
1508
|
+
var CLIENT_DIR = /(^|\/)(clients?|gateways?|integrations?|publishers?|producers?|outbound)\//i;
|
|
1509
|
+
var base = (path) => path.slice(path.lastIndexOf("/") + 1);
|
|
1510
|
+
var stem = (path) => {
|
|
1511
|
+
const b = base(path);
|
|
1512
|
+
const dot = b.indexOf(".");
|
|
1513
|
+
return dot > 0 ? b.slice(0, dot) : b;
|
|
1514
|
+
};
|
|
1515
|
+
function isModelFile(path, text) {
|
|
1516
|
+
if (!CODE.test(path) || readingRank(path) === 5)
|
|
1517
|
+
return false;
|
|
1518
|
+
if (!MODEL_DIR.test(path) && !MODEL_NAME.test(stem(path)) && !MODEL_PART.test(path))
|
|
1519
|
+
return false;
|
|
1520
|
+
return text === void 0 || !MAPS_TABLE.test(text);
|
|
1521
|
+
}
|
|
1522
|
+
var ENTRY_POINT_NAME = /(controller|resource|router|routes?|handlers?|listener|consumer|subscriber|endpoints?|servlet|webhook|resolver)$/i;
|
|
1523
|
+
var ENTRY_POINT_DIR = /(^|\/)(controllers?|routes|routers|handlers|endpoints|listeners|consumers)\//i;
|
|
1524
|
+
var ENTRY_POINT_PART = /[._-](controller|resolver|handlers?|router|routes?|consumer|listener|subscriber|endpoints?)\.[^/.]+$/i;
|
|
1525
|
+
function isEntryPointFile(path) {
|
|
1526
|
+
if (!CODE.test(path) || readingRank(path) === 5 || isModelFile(path))
|
|
1527
|
+
return false;
|
|
1528
|
+
return ENTRY_POINT_NAME.test(stem(path)) || ENTRY_POINT_DIR.test(path) || ENTRY_POINT_PART.test(path);
|
|
1529
|
+
}
|
|
1530
|
+
function isRootFile(path) {
|
|
1531
|
+
if (!CODE.test(path) || readingRank(path) === 5 || isModelFile(path))
|
|
1532
|
+
return false;
|
|
1533
|
+
return readingRank(path) === 2 || isEntryPointFile(path) || ROOT_PART.test(path) || CLIENT_NAME.test(stem(path)) || CLIENT_DIR.test(path);
|
|
1534
|
+
}
|
|
1535
|
+
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;
|
|
1536
|
+
var IDENT = /[A-Za-z_][A-Za-z0-9_]*/g;
|
|
1537
|
+
function declaredTypes(path, text) {
|
|
1538
|
+
const out = [];
|
|
1539
|
+
const s = stem(path);
|
|
1540
|
+
if (/^[A-Z][A-Za-z0-9_]*$/.test(s))
|
|
1541
|
+
out.push(s);
|
|
1542
|
+
for (const m of text.matchAll(DECLARES)) {
|
|
1543
|
+
const n = m[1] ?? m[2];
|
|
1544
|
+
if (n && !out.includes(n))
|
|
1545
|
+
out.push(n);
|
|
1546
|
+
}
|
|
1547
|
+
return out;
|
|
1548
|
+
}
|
|
1549
|
+
function identifiers(text) {
|
|
1550
|
+
return [...new Set(text.match(IDENT) ?? [])];
|
|
1551
|
+
}
|
|
1552
|
+
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";
|
|
1553
|
+
function reachableModels(o) {
|
|
1554
|
+
const texts = /* @__PURE__ */ new Map();
|
|
1555
|
+
const textOf = (p) => {
|
|
1556
|
+
if (!texts.has(p))
|
|
1557
|
+
texts.set(p, o.read(p) ?? "");
|
|
1558
|
+
return texts.get(p);
|
|
1559
|
+
};
|
|
1560
|
+
const ordered = readingOrder(o.paths);
|
|
1561
|
+
const models = ordered.filter((p) => isModelFile(p, textOf(p))).sort();
|
|
1562
|
+
const isModel = new Set(models);
|
|
1563
|
+
const byName = /* @__PURE__ */ new Map();
|
|
1564
|
+
const declared = /* @__PURE__ */ new Map();
|
|
1565
|
+
for (const p of models) {
|
|
1566
|
+
const names = declaredTypes(p, textOf(p));
|
|
1567
|
+
declared.set(p, names);
|
|
1568
|
+
for (const n of names)
|
|
1569
|
+
byName.set(n, [...byName.get(n) ?? [], p]);
|
|
1570
|
+
}
|
|
1571
|
+
const known = new Set(o.paths);
|
|
1572
|
+
const roots = [];
|
|
1573
|
+
const addRoot = (p) => {
|
|
1574
|
+
if (known.has(p) && !isModel.has(p) && !roots.includes(p))
|
|
1575
|
+
roots.push(p);
|
|
1576
|
+
};
|
|
1577
|
+
const facts = o.facts ?? [];
|
|
1578
|
+
const entries = o.from === "entries";
|
|
1579
|
+
const calls2 = new Set(facts.filter((f) => f.kind === "call").map((f) => f.id));
|
|
1580
|
+
for (const f of facts)
|
|
1581
|
+
if (f.kind === "entry" || f.kind === "call" && !entries)
|
|
1582
|
+
for (const p of citedFiles(f))
|
|
1583
|
+
addRoot(p);
|
|
1584
|
+
for (const p of ordered)
|
|
1585
|
+
if (entries ? isEntryPointFile(p) : isRootFile(p))
|
|
1586
|
+
addRoot(p);
|
|
1587
|
+
const reached = /* @__PURE__ */ new Map();
|
|
1588
|
+
const queue = [];
|
|
1589
|
+
const reach = (names, from, own = []) => {
|
|
1590
|
+
for (const n of names) {
|
|
1591
|
+
if (own.includes(n))
|
|
1592
|
+
continue;
|
|
1593
|
+
for (const p of byName.get(n) ?? []) {
|
|
1594
|
+
const r = reached.get(p);
|
|
1595
|
+
if (r) {
|
|
1596
|
+
if (r.from === from && !r.types.includes(n))
|
|
1597
|
+
r.types.push(n);
|
|
1598
|
+
continue;
|
|
1599
|
+
}
|
|
1600
|
+
reached.set(p, { path: p, types: [n], from });
|
|
1601
|
+
queue.push(p);
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
};
|
|
1605
|
+
for (const p of roots)
|
|
1606
|
+
reach(identifiers(textOf(p)), p);
|
|
1607
|
+
for (const f of facts) {
|
|
1608
|
+
for (const p of citedFiles(f))
|
|
1609
|
+
if (isModel.has(p) && !reached.has(p)) {
|
|
1610
|
+
reached.set(p, { path: p, types: [...(declared.get(p) ?? []).slice(0, 1)], from: f.id });
|
|
1611
|
+
queue.push(p);
|
|
1612
|
+
}
|
|
1613
|
+
if (f.kind === "field" && !(entries && calls2.has(f.owner)))
|
|
1614
|
+
reach(identifiers([f.type ?? "", f.transform === "message type" ? f.name : ""].join(" ")), f.id);
|
|
1615
|
+
}
|
|
1616
|
+
const rooted = roots.length > 0 || reached.size > 0;
|
|
1617
|
+
if (!rooted)
|
|
1618
|
+
return {
|
|
1619
|
+
rooted,
|
|
1620
|
+
roots,
|
|
1621
|
+
reached: models.map((p) => ({ path: p, types: declared.get(p) ?? [], from: "" })),
|
|
1622
|
+
notReached: [],
|
|
1623
|
+
models: models.length
|
|
1624
|
+
};
|
|
1625
|
+
for (let i = 0; i < queue.length; i++) {
|
|
1626
|
+
const p = queue[i];
|
|
1627
|
+
reach(identifiers(textOf(p)), p, declared.get(p));
|
|
1628
|
+
}
|
|
1629
|
+
return {
|
|
1630
|
+
rooted,
|
|
1631
|
+
roots,
|
|
1632
|
+
reached: queue.map((p) => reached.get(p)),
|
|
1633
|
+
notReached: models.filter((p) => !reached.has(p)),
|
|
1634
|
+
models: models.length
|
|
1635
|
+
};
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1638
|
+
// packages/verify/dist/method.js
|
|
1639
|
+
var HTTP_METHODS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]);
|
|
1640
|
+
function wordsOf(line) {
|
|
1641
|
+
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);
|
|
1642
|
+
}
|
|
1643
|
+
function methodOnLines(method, lines) {
|
|
1644
|
+
const m = method.toLowerCase();
|
|
1645
|
+
return lines.some((l) => wordsOf(l).includes(m));
|
|
1646
|
+
}
|
|
1647
|
+
var CHAIN_LINES = 8;
|
|
1648
|
+
var CONTINUES = /^\s*\??\./;
|
|
1649
|
+
function chainOf(file, line) {
|
|
1650
|
+
const at = (n) => file?.[n - 1];
|
|
1651
|
+
const cited = at(line);
|
|
1652
|
+
if (cited === void 0)
|
|
1653
|
+
return [];
|
|
1654
|
+
const out = [cited];
|
|
1655
|
+
for (let n = line, k = 0; k < CHAIN_LINES && CONTINUES.test(at(n) ?? ""); k++, n--) {
|
|
1656
|
+
const above = at(n - 1);
|
|
1657
|
+
if (above === void 0)
|
|
1658
|
+
break;
|
|
1659
|
+
out.push(above);
|
|
1660
|
+
}
|
|
1661
|
+
for (let n = line + 1, k = 0; k < CHAIN_LINES && CONTINUES.test(at(n) ?? ""); k++, n++)
|
|
1662
|
+
out.push(at(n));
|
|
1663
|
+
return out;
|
|
1664
|
+
}
|
|
1665
|
+
function methodUnseenOf(f, lines, factOf) {
|
|
1666
|
+
if (f.kind !== "entry" && f.kind !== "call")
|
|
1667
|
+
return void 0;
|
|
1668
|
+
if (!f.method || !HTTP_METHODS.has(f.method.toUpperCase()))
|
|
1669
|
+
return void 0;
|
|
1670
|
+
const at = (file, line) => lines(file)?.[line - 1];
|
|
1671
|
+
const route = f.kind === "call" && f.from ? factOf?.(f.from) : void 0;
|
|
1672
|
+
const forwards = route?.kind === "entry" && route.method?.toUpperCase() === f.method.toUpperCase();
|
|
1673
|
+
const looked = [
|
|
1674
|
+
...chainOf(lines(f.cite.file), f.cite.line),
|
|
1675
|
+
at(f.cite.file, f.cite.line + 1),
|
|
1676
|
+
...(f.cite.with ?? []).map((p) => at(p.file, p.line)),
|
|
1677
|
+
...forwards ? [at(route.cite.file, route.cite.line), at(route.cite.file, route.cite.line + 1)] : []
|
|
1678
|
+
].filter((l) => l !== void 0);
|
|
1679
|
+
if (methodOnLines(f.method, looked))
|
|
1680
|
+
return void 0;
|
|
1681
|
+
const cite2 = `${f.cite.file}:${f.cite.line}`;
|
|
1682
|
+
const method = f.method.toUpperCase();
|
|
1683
|
+
return {
|
|
1684
|
+
id: f.id,
|
|
1685
|
+
kind: f.kind,
|
|
1686
|
+
method,
|
|
1687
|
+
cite: cite2,
|
|
1688
|
+
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`
|
|
1689
|
+
};
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1692
|
+
// packages/verify/dist/skipped.js
|
|
1693
|
+
function notReadForSkipped(path, rules = DEFAULT_READ_RULES) {
|
|
1694
|
+
return rules.notRead.test(path);
|
|
1695
|
+
}
|
|
1696
|
+
var isCommentLine = (l) => /^\s*(\/\/|\/\*|\*|<!--|-- |#(?!\s*(region|endregion|if|elif|else|endif|define|undef|pragma|nullable|include|import)\b)(?!!))/.test(l);
|
|
1697
|
+
var IMPORT_LINE = /^\s*(import|export\s+[^=]*\bfrom\b|from\s+\S+\s+import|package|using|require|#\s*include|@import|@use|use|library)\b/;
|
|
1698
|
+
var IMPORT_CALL = /\brequire\s*\(|\bimport\s*\(|\bfrom\s+["'`]/;
|
|
1699
|
+
var NAMESPACE_LINE = /\bxmlns(:\w+)?\s*=|schemaLocation\s*=|<!DOCTYPE|^\s*<\?xml/i;
|
|
1700
|
+
var URL_RE = /\b(?:https?|wss?|grpcs?|amqps?):\/\/[^\s"'`<>()[\],;\\|^]+/gi;
|
|
1701
|
+
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;
|
|
1702
|
+
var FS_ROOT = /^\/(usr|etc|var|tmp|bin|sbin|opt|home|dev|proc|sys|lib|lib64|mnt|root|Users|private|Library|System|Applications|Volumes)(\/|$)/;
|
|
1703
|
+
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;
|
|
1704
|
+
var PATH_SEG = "[A-Za-z0-9_.~{}$:<>*%@+=-]+";
|
|
1705
|
+
var PATH_RE = new RegExp(`^(?:\\$\\{[^}]+\\}|%s|\\{[A-Za-z_]+\\})?((?:\\/${PATH_SEG})+\\/?)$`);
|
|
1706
|
+
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(" "));
|
|
1707
|
+
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(" "));
|
|
1708
|
+
var DOTTED_TOPIC = /^[a-z][a-z0-9_-]*(\.[a-z][a-z0-9_-]*){2,}$/;
|
|
1709
|
+
var SEPARATED_NAME = /^[A-Za-z][A-Za-z0-9_]*([._:-][A-Za-z0-9_]+)+$/;
|
|
1710
|
+
var QUOTED = /(["'`])((?:\\.|(?!\1)[^\\\n])*)\1/g;
|
|
1711
|
+
var KEY_VALUE = /^\s*-?\s*["']?([A-Za-z0-9_.\-[\]]+)["']?\s*[:=]\s*([^\s"'#]+)\s*$/;
|
|
1712
|
+
var ELEMENT_TEXT = /<([A-Za-z][\w:.-]*)[^>]*>([^<\s]+)<\/\1>/g;
|
|
1713
|
+
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);
|
|
1714
|
+
var TOPIC_WORDS = /* @__PURE__ */ new Set([
|
|
1715
|
+
"topic",
|
|
1716
|
+
"topics",
|
|
1717
|
+
"queue",
|
|
1718
|
+
"queues",
|
|
1719
|
+
"channel",
|
|
1720
|
+
"channels",
|
|
1721
|
+
"destination",
|
|
1722
|
+
"destinations",
|
|
1723
|
+
"exchange",
|
|
1724
|
+
"exchanges"
|
|
1725
|
+
]);
|
|
1726
|
+
var topicContext = (line) => {
|
|
1727
|
+
const w = wordsOf2(line);
|
|
1728
|
+
return w.some((x) => TOPIC_WORDS.has(x)) || w.join(" ").includes("routing key");
|
|
1729
|
+
};
|
|
1730
|
+
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(" "));
|
|
1731
|
+
var BUILD_BLOCK_TAG = /<(\/?)(repositories|repository|pluginRepositories|pluginRepository|snapshotRepository|distributionManagement|scm|mirrors|mirror|site)\b[^>]*?(\/?)>/gi;
|
|
1732
|
+
var BUILD_BLOCK_OPEN = /\b(repositories|publishing|pluginManagement|dependencyResolutionManagement)\s*\{/;
|
|
1733
|
+
var keysBefore = (prefix) => [
|
|
1734
|
+
...[...prefix.matchAll(/<([A-Za-z][\w:.-]*)/g)].map((m) => m[1]),
|
|
1735
|
+
...[...prefix.matchAll(/["']?([A-Za-z][\w.-]*)["']?\s*[:=]/g)].map((m) => m[1]),
|
|
1736
|
+
...[...prefix.matchAll(/\b([A-Za-z]\w*)\s*\{/g)].map((m) => m[1])
|
|
1737
|
+
];
|
|
1738
|
+
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)));
|
|
1739
|
+
var HEADER_ARG = /header\w*\s*(?:\.\s*\w+\s*)?\(\s*(?:(?:value|name)\s*=\s*)?$/i;
|
|
1740
|
+
var isHeaderName = (s, prefix) => /^x-/i.test(s) || HEADER_ARG.test(prefix.replace(/["'`]$/, ""));
|
|
1741
|
+
var cleanUrl = (u) => u.replace(/[.:]+$/, "").replace(/^([a-z0-9+.-]+:\/\/)[^/@\s]+@/i, "$1<redacted>@");
|
|
1742
|
+
var hostOf = (u) => /^[a-z0-9+.-]+:\/\/([^/:?#]+)/i.exec(u)?.[1] ?? "";
|
|
1743
|
+
function pathOf(s) {
|
|
1744
|
+
const m = PATH_RE.exec(s);
|
|
1745
|
+
if (!m)
|
|
1746
|
+
return void 0;
|
|
1747
|
+
const p = m[1];
|
|
1748
|
+
if (p.length < 2 || p.includes("//") || !/[A-Za-z]/.test(p))
|
|
1749
|
+
return void 0;
|
|
1750
|
+
if (FS_ROOT.test(p) || FILE_EXT.test(p))
|
|
1751
|
+
return void 0;
|
|
1752
|
+
return p;
|
|
1753
|
+
}
|
|
1754
|
+
function topicOf(s, context) {
|
|
1755
|
+
if (DOTTED_TOPIC.test(s)) {
|
|
1756
|
+
const parts = s.split(".");
|
|
1757
|
+
if (!NOT_TOPIC_LAST.has(parts.at(-1)) && !NOT_TOPIC_FIRST.has(parts[0]))
|
|
1758
|
+
return s;
|
|
1759
|
+
}
|
|
1760
|
+
if (context && SEPARATED_NAME.test(s) && !FILE_EXT.test(s)) {
|
|
1761
|
+
const last = s.split(".").at(-1);
|
|
1762
|
+
if (!s.includes(".") || !NOT_TOPIC_LAST.has(last))
|
|
1763
|
+
return s;
|
|
1764
|
+
}
|
|
1765
|
+
return void 0;
|
|
1766
|
+
}
|
|
1767
|
+
function codeOf(raw, state) {
|
|
1768
|
+
let s = raw;
|
|
1769
|
+
let out = "";
|
|
1770
|
+
for (; ; ) {
|
|
1771
|
+
if (state.block) {
|
|
1772
|
+
const close = state.block === "c" ? "*/" : "-->";
|
|
1773
|
+
const i = s.indexOf(close);
|
|
1774
|
+
if (i < 0)
|
|
1775
|
+
return out;
|
|
1776
|
+
s = s.slice(i + close.length);
|
|
1777
|
+
state.block = void 0;
|
|
1778
|
+
}
|
|
1779
|
+
const m = /(^|\s)(\/\*|<!--)/.exec(s);
|
|
1780
|
+
if (!m)
|
|
1781
|
+
return out + s;
|
|
1782
|
+
out += s.slice(0, m.index + m[1].length);
|
|
1783
|
+
s = s.slice(m.index + m[0].length);
|
|
1784
|
+
state.block = m[2] === "/*" ? "c" : "xml";
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
function candidatesIn(path, lines) {
|
|
1788
|
+
const out = [];
|
|
1789
|
+
const state = {};
|
|
1790
|
+
let xmlDepth = 0;
|
|
1791
|
+
let braceDepth = 0;
|
|
1792
|
+
lines.forEach((raw, i) => {
|
|
1793
|
+
const wasBlock = state.block !== void 0;
|
|
1794
|
+
let code2 = codeOf(raw, state);
|
|
1795
|
+
const inBuild = xmlDepth > 0 || braceDepth > 0;
|
|
1796
|
+
for (const m of code2.matchAll(BUILD_BLOCK_TAG))
|
|
1797
|
+
if (!m[3])
|
|
1798
|
+
xmlDepth = Math.max(0, xmlDepth + (m[1] ? -1 : 1));
|
|
1799
|
+
if (braceDepth > 0 || BUILD_BLOCK_OPEN.test(code2)) {
|
|
1800
|
+
const from = braceDepth > 0 ? code2 : code2.slice(BUILD_BLOCK_OPEN.exec(code2).index);
|
|
1801
|
+
braceDepth = Math.max(0, braceDepth + (from.match(/\{/g)?.length ?? 0) - (from.match(/\}/g)?.length ?? 0));
|
|
1802
|
+
}
|
|
1803
|
+
if (!code2.trim())
|
|
1262
1804
|
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}` });
|
|
1805
|
+
if (!wasBlock && isCommentLine(raw))
|
|
1806
|
+
return;
|
|
1807
|
+
if (IMPORT_LINE.test(code2) || IMPORT_CALL.test(code2) || NAMESPACE_LINE.test(code2))
|
|
1269
1808
|
return;
|
|
1809
|
+
code2 = code2.replace(/(^|[\s;,)}])\/\/.*$/, "$1").replace(/\s#\s.*$/, "");
|
|
1810
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1811
|
+
const text = redactSecrets(cleanUrlIn(raw.trim())).slice(0, 200);
|
|
1812
|
+
const add = (kind, value) => {
|
|
1813
|
+
const key = `${kind} ${value}`;
|
|
1814
|
+
if (seen.has(key))
|
|
1815
|
+
return;
|
|
1816
|
+
seen.add(key);
|
|
1817
|
+
out.push({ line: i + 1, kind, value, text });
|
|
1818
|
+
};
|
|
1819
|
+
const context = topicContext(code2);
|
|
1820
|
+
const urls = [];
|
|
1821
|
+
const opened = inBuild || xmlDepth > 0 || braceDepth > 0;
|
|
1822
|
+
for (const m of code2.matchAll(URL_RE)) {
|
|
1823
|
+
const u = cleanUrl(m[0]);
|
|
1824
|
+
const build = opened || isBuildUrl(u, code2.slice(0, m.index));
|
|
1825
|
+
if (!build && !STANDARD_HOST.test(hostOf(u)))
|
|
1826
|
+
add("url", u);
|
|
1827
|
+
urls.push(m[0]);
|
|
1270
1828
|
}
|
|
1271
|
-
const
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
if (
|
|
1282
|
-
|
|
1829
|
+
const strings2 = [...code2.matchAll(QUOTED)].map((m) => ({
|
|
1830
|
+
s: m[2],
|
|
1831
|
+
before: code2.slice(0, m.index + 1)
|
|
1832
|
+
}));
|
|
1833
|
+
const kv = KEY_VALUE.exec(code2);
|
|
1834
|
+
if (kv)
|
|
1835
|
+
strings2.push({ s: kv[2], before: "" });
|
|
1836
|
+
for (const m of code2.matchAll(ELEMENT_TEXT))
|
|
1837
|
+
strings2.push({ s: m[2], before: "" });
|
|
1838
|
+
for (const { s, before } of strings2) {
|
|
1839
|
+
if (urls.some((u) => s.includes(u) || u.includes(s)))
|
|
1840
|
+
continue;
|
|
1841
|
+
const p = pathOf(s);
|
|
1842
|
+
if (p) {
|
|
1843
|
+
add("path", p);
|
|
1844
|
+
continue;
|
|
1845
|
+
}
|
|
1846
|
+
const t = topicOf(s, context);
|
|
1847
|
+
if (t && !isHeaderName(t, before))
|
|
1848
|
+
add("topic", t);
|
|
1283
1849
|
}
|
|
1284
|
-
|
|
1285
|
-
|
|
1850
|
+
});
|
|
1851
|
+
return out;
|
|
1852
|
+
}
|
|
1853
|
+
var cleanUrlIn = (line) => line.replace(/\b([a-z0-9+.-]+:\/\/)[^/@\s"'`]+@/gi, "$1<redacted>@");
|
|
1854
|
+
var normPath = (p) => normalizePath(p.replace(/(^|\/)%[sd](?=\/|$)/g, "$1{}"));
|
|
1855
|
+
function indexFacts(facts) {
|
|
1856
|
+
const at = /* @__PURE__ */ new Set();
|
|
1857
|
+
const strings2 = /* @__PURE__ */ new Set();
|
|
1858
|
+
const walk = (v) => {
|
|
1859
|
+
if (typeof v === "string") {
|
|
1860
|
+
if (v.length >= 3)
|
|
1861
|
+
strings2.add(v);
|
|
1286
1862
|
return;
|
|
1287
1863
|
}
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1864
|
+
if (Array.isArray(v))
|
|
1865
|
+
return v.forEach(walk);
|
|
1866
|
+
if (v && typeof v === "object") {
|
|
1867
|
+
const o = v;
|
|
1868
|
+
if (typeof o.file === "string" && typeof o.line === "number")
|
|
1869
|
+
at.add(`${o.file}:${o.line}`);
|
|
1870
|
+
Object.values(o).forEach(walk);
|
|
1871
|
+
}
|
|
1872
|
+
};
|
|
1873
|
+
facts.forEach(walk);
|
|
1874
|
+
const paths = /* @__PURE__ */ new Set();
|
|
1875
|
+
for (const s of strings2) {
|
|
1876
|
+
if (/\s/.test(s))
|
|
1877
|
+
continue;
|
|
1878
|
+
const u = /^[a-z0-9+.-]+:\/\/[^/]+(\/.*)?$/i.exec(s);
|
|
1879
|
+
const p = u ? u[1] : s.includes("/") ? s : void 0;
|
|
1880
|
+
if (p && p.length > 1)
|
|
1881
|
+
paths.add(`${normPath(p)}/`);
|
|
1882
|
+
}
|
|
1883
|
+
return { at, strings: [...strings2].map((s) => s.toLowerCase()), paths: [...paths] };
|
|
1884
|
+
}
|
|
1885
|
+
function factsCover(file, c, ix) {
|
|
1886
|
+
if ([c.line - 1, c.line, c.line + 1].some((n) => ix.at.has(`${file}:${n}`)))
|
|
1887
|
+
return true;
|
|
1888
|
+
if (c.kind === "path") {
|
|
1889
|
+
const p = `${normPath(c.value)}/`;
|
|
1890
|
+
return ix.paths.some((f) => f.includes(p));
|
|
1891
|
+
}
|
|
1892
|
+
const v = c.value.toLowerCase().replace(/\/+$/, "");
|
|
1893
|
+
return ix.strings.some((s) => s.includes(v));
|
|
1894
|
+
}
|
|
1895
|
+
var skippedOf = (file, c) => ({
|
|
1896
|
+
cite: `${file}:${c.line}`,
|
|
1897
|
+
kind: c.kind,
|
|
1898
|
+
value: c.value,
|
|
1899
|
+
text: c.text
|
|
1900
|
+
});
|
|
1901
|
+
var SPEC_MARK = /^\s*["']?(openapi|swagger)["']?\s*:/m;
|
|
1902
|
+
var SPEC_EXT = /\.(ya?ml|json)$/i;
|
|
1903
|
+
function specOpsUncoveredIn(file, text, facts) {
|
|
1904
|
+
if (!SPEC_EXT.test(file) || !SPEC_MARK.test(text.slice(0, 4e3)))
|
|
1905
|
+
return void 0;
|
|
1906
|
+
let doc;
|
|
1907
|
+
try {
|
|
1908
|
+
doc = readOpenApiDocument(text);
|
|
1909
|
+
} catch {
|
|
1910
|
+
return void 0;
|
|
1911
|
+
}
|
|
1912
|
+
if (!doc || !doc.operations.length)
|
|
1913
|
+
return void 0;
|
|
1914
|
+
const server = (() => {
|
|
1915
|
+
const s = doc.servers[0];
|
|
1916
|
+
if (!s)
|
|
1917
|
+
return "";
|
|
1918
|
+
const p = /^[a-z0-9+.-]+:\/\/[^/]+(\/.*)?$/i.exec(s)?.[1] ?? (s.startsWith("/") ? s : "");
|
|
1919
|
+
return p.replace(/\/+$/, "");
|
|
1920
|
+
})();
|
|
1921
|
+
const entries = facts.filter((f) => f.kind === "entry");
|
|
1922
|
+
const atLines = /* @__PURE__ */ new Set();
|
|
1923
|
+
for (const e of entries)
|
|
1924
|
+
for (const k of indexFacts([e]).at)
|
|
1925
|
+
atLines.add(k);
|
|
1926
|
+
const out = [];
|
|
1927
|
+
for (const op of doc.operations) {
|
|
1928
|
+
const lines = [op.methodLine, op.line].filter((n) => n !== void 0);
|
|
1929
|
+
if (lines.some((n) => [n - 1, n, n + 1].some((x) => atLines.has(`${file}:${x}`))))
|
|
1930
|
+
continue;
|
|
1931
|
+
const method = op.method.toUpperCase();
|
|
1932
|
+
const own = normPath(op.path);
|
|
1933
|
+
const full = normPath(`${server}${op.path}`);
|
|
1934
|
+
const covered = entries.some((e) => {
|
|
1935
|
+
if (e.method && e.method.toUpperCase() !== method)
|
|
1936
|
+
return false;
|
|
1937
|
+
const p = normPath(e.path);
|
|
1938
|
+
return p === own || p === full || p.endsWith(own) || p.split("/").length > 2 && own.endsWith(p);
|
|
1939
|
+
});
|
|
1940
|
+
if (covered)
|
|
1941
|
+
continue;
|
|
1942
|
+
const opId = op.node?.get?.("operationId");
|
|
1943
|
+
const line = op.methodLine ?? op.line ?? 1;
|
|
1944
|
+
out.push({
|
|
1945
|
+
spec: file,
|
|
1946
|
+
method,
|
|
1947
|
+
path: op.path,
|
|
1948
|
+
...typeof opId === "string" ? { operationId: opId } : {},
|
|
1949
|
+
cite: `${file}:${line}`,
|
|
1950
|
+
message: `${file}:${line} declares ${method} ${op.path}${typeof opId === "string" ? ` (${opId})` : ""}, which no entry covers`
|
|
1951
|
+
});
|
|
1952
|
+
}
|
|
1953
|
+
return out;
|
|
1954
|
+
}
|
|
1955
|
+
var REVIEWED_HOLDS_SHOWN = 5;
|
|
1956
|
+
function reviewedHoldsOf(r, skipped) {
|
|
1957
|
+
const n = skipped.length;
|
|
1958
|
+
const first = skipped.slice(0, 3).map((s) => `${s.cite} (${s.value})`);
|
|
1959
|
+
return {
|
|
1960
|
+
path: r.path,
|
|
1961
|
+
why: r.why,
|
|
1962
|
+
count: n,
|
|
1963
|
+
skipped: skipped.slice(0, REVIEWED_HOLDS_SHOWN),
|
|
1964
|
+
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` : ""}`
|
|
1965
|
+
};
|
|
1291
1966
|
}
|
|
1292
|
-
var isGlob = (path) => /[*?]/.test(path);
|
|
1293
1967
|
|
|
1294
1968
|
// packages/verify/dist/verify.js
|
|
1295
1969
|
var FOLLOWED_TYPES = ["event-topic", "mq-queue", "rest", "grpc"];
|
|
1296
|
-
function entriesNoFlowFollows(facts) {
|
|
1970
|
+
function entriesNoFlowFollows(facts, rules = DEFAULT_READ_RULES) {
|
|
1297
1971
|
const started = new Set(facts.flatMap((f) => f.kind === "flow" ? [f.entry] : []));
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1972
|
+
const forwarded = new Set(facts.flatMap((f) => f.kind === "call" && f.from ? [f.from] : []));
|
|
1973
|
+
return facts.flatMap((f) => {
|
|
1974
|
+
if (f.kind !== "entry" || !FOLLOWED_TYPES.includes(f.entryType))
|
|
1975
|
+
return [];
|
|
1976
|
+
const route = rules.gatewayBundle.test(f.cite.file);
|
|
1977
|
+
if (route ? forwarded.has(f.id) : started.has(f.id))
|
|
1978
|
+
return [];
|
|
1979
|
+
return [
|
|
1980
|
+
{
|
|
1981
|
+
id: f.id,
|
|
1982
|
+
entryType: f.entryType,
|
|
1983
|
+
...f.method ? { method: f.method } : {},
|
|
1984
|
+
path: f.path,
|
|
1985
|
+
cite: `${f.cite.file}:${f.cite.line}`,
|
|
1986
|
+
...route ? { route: true } : {}
|
|
1987
|
+
}
|
|
1988
|
+
];
|
|
1989
|
+
}).sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
|
|
1308
1990
|
}
|
|
1309
1991
|
var LINES_HELD = 64;
|
|
1310
1992
|
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
1993
|
var SOURCE_NAME = /(^|\/)(Dockerfile[^/]*|Makefile|Jenkinsfile|Procfile|Containerfile)$/;
|
|
1312
1994
|
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
1995
|
var NOT_PRODUCT = [
|
|
1314
|
-
/(^|\/)(src\/test|src\/it|test|tests|__tests__|__mocks__|spec|specs|e2e|testdata|test-data|fixtures)\//i,
|
|
1315
1996
|
/(\.|_|-)(test|spec|tests|specs)\.[a-z0-9]+$/i,
|
|
1316
1997
|
/(Test|Tests|IT|Spec)\.(java|kt|scala|groovy|cs|vb)$/,
|
|
1317
1998
|
/(^|\/)test_[^/]+\.py$/,
|
|
@@ -1320,19 +2001,30 @@ var NOT_PRODUCT = [
|
|
|
1320
2001
|
/(^|\/)messages(_[A-Za-z_]+)?\.properties$/,
|
|
1321
2002
|
/\.(resx|po|pot)$/i
|
|
1322
2003
|
];
|
|
1323
|
-
function isSourceFile(path, firstLine = "", lines) {
|
|
2004
|
+
function isSourceFile(path, firstLine = "", lines, rules = DEFAULT_READ_RULES) {
|
|
1324
2005
|
if (isSecretPath(path) || LOCKFILE.test(path))
|
|
1325
2006
|
return false;
|
|
1326
|
-
if (NOT_PRODUCT.some((re) => re.test(path))
|
|
2007
|
+
if (rules.notProduct.test(path) || NOT_PRODUCT.some((re) => re.test(path)))
|
|
1327
2008
|
return false;
|
|
1328
|
-
if (
|
|
2009
|
+
if (isLocaleBundle(path))
|
|
2010
|
+
return false;
|
|
2011
|
+
if (lines && isDataFile(path, lines, rules))
|
|
1329
2012
|
return false;
|
|
1330
2013
|
if (quoteNotRequired(path, firstLine))
|
|
1331
2014
|
return false;
|
|
1332
2015
|
return SOURCE_EXT.test(path) || SOURCE_NAME.test(path);
|
|
1333
2016
|
}
|
|
1334
2017
|
var lastPart = (s) => s.split(".").pop() ?? s;
|
|
1335
|
-
var
|
|
2018
|
+
var TEMPLATED = /\{\{|\}\}|\$\{/;
|
|
2019
|
+
function pathIsAName(path) {
|
|
2020
|
+
const p = (path ?? "").trim().replace(/\(\s*[^)]*\)$/, "");
|
|
2021
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(p) && /[a-z][A-Z]|_|\$/.test(p);
|
|
2022
|
+
}
|
|
2023
|
+
var serviceAddress = (f) => f.host !== void 0 && /^service$/i.test(f.resourceType ?? "") ? clusterServiceAddress(f.host) : void 0;
|
|
2024
|
+
var repoModule = (exposes) => {
|
|
2025
|
+
const m = /^([^/]+)\/([^/]+)$/.exec(exposes.trim());
|
|
2026
|
+
return m ? { repo: m[1], module: m[2] } : void 0;
|
|
2027
|
+
};
|
|
1336
2028
|
var routeOrWord = (v) => v.includes("/") ? { kind: "route", value: v } : { kind: "word", value: v };
|
|
1337
2029
|
function claimsOf(f) {
|
|
1338
2030
|
switch (f.kind) {
|
|
@@ -1368,6 +2060,12 @@ function claimsOf(f) {
|
|
|
1368
2060
|
}
|
|
1369
2061
|
case "flow":
|
|
1370
2062
|
return [routeOrWord(f.cite.value)];
|
|
2063
|
+
// ADR 0163: the value as written, on its line.
|
|
2064
|
+
case "term":
|
|
2065
|
+
return [{ kind: "word", value: f.value }];
|
|
2066
|
+
// ADR 0180: the signal the type rests on, as written on its line.
|
|
2067
|
+
case "component":
|
|
2068
|
+
return [{ kind: "word", value: f.cite.value }];
|
|
1371
2069
|
}
|
|
1372
2070
|
}
|
|
1373
2071
|
var sameCommit = (a, b) => a.startsWith(b) || b.startsWith(a);
|
|
@@ -1403,6 +2101,7 @@ function refsOf(f) {
|
|
|
1403
2101
|
return out;
|
|
1404
2102
|
}
|
|
1405
2103
|
async function verifyFacts(o) {
|
|
2104
|
+
const rules = readRulesOf(o.conventions);
|
|
1406
2105
|
const carriedFrom = /* @__PURE__ */ new Map();
|
|
1407
2106
|
const stale = /* @__PURE__ */ new Map();
|
|
1408
2107
|
const corrected = applyCorrections(o.facts, o.corrections ?? []);
|
|
@@ -1504,7 +2203,7 @@ async function verifyFacts(o) {
|
|
|
1504
2203
|
].flatMap((at) => derivedNamesAt(at.file, linesOf2(at.file) ?? [], at.line, deriveEnv));
|
|
1505
2204
|
const holds = (c) => claimOnLines(lines, cite2.line, c) || // a framework that routes by file place (Razor Pages, WebForms, Next.js)
|
|
1506
2205
|
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);
|
|
2206
|
+
c.kind === "word" && partsBuildWord(c.value, parts.map((p) => p.value), near(lines, cite2.line))) || o.derivedNames !== false && derivedHolds(c, derivedHere(), partLines, partsNamePath);
|
|
1508
2207
|
if (claims && !claims.some(holds))
|
|
1509
2208
|
return {
|
|
1510
2209
|
code: "claim-not-on-line",
|
|
@@ -1541,6 +2240,9 @@ async function verifyFacts(o) {
|
|
|
1541
2240
|
return value.includes("/") ? [routeOrWord(value), { kind: "word", value }] : [routeOrWord(value)];
|
|
1542
2241
|
return NEVER_CHECKED_STEPS.includes(kind) ? void 0 : [routeOrWord(value)];
|
|
1543
2242
|
};
|
|
2243
|
+
const estate = o.estate ? new Set(o.estate.components.map(factIdPart)) : void 0;
|
|
2244
|
+
const serves2 = facts.some((f) => f.kind === "entry");
|
|
2245
|
+
const repoComponents = new Map(Object.entries(o.estate?.repoComponents ?? {}).map(([r, cs]) => [factIdPart(r), new Set(cs.map(factIdPart))]));
|
|
1544
2246
|
for (const f of facts) {
|
|
1545
2247
|
const label = `${f.kind} ${f.id}`;
|
|
1546
2248
|
if (o.commit && !sameCommit(f.commit, o.commit)) {
|
|
@@ -1566,8 +2268,80 @@ async function verifyFacts(o) {
|
|
|
1566
2268
|
(f.scopes ?? []).forEach((s, i) => add(f, checkCite(s, [{ kind: "word", value: s.value }], `${label} scope ${i + 1} ("${s.value}")`)));
|
|
1567
2269
|
if (f.kind === "entry")
|
|
1568
2270
|
(f.callers ?? []).forEach((c, i) => add(f, checkCite(c, [{ kind: "word", value: c.value }], `${label} caller ${i + 1} (${c.as} "${c.value}")`)));
|
|
2271
|
+
if (f.kind === "call" && (f.callType === "rest" || f.callType === "soap") && pathIsAName(f.path))
|
|
2272
|
+
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`);
|
|
2273
|
+
if (f.kind === "deploy" && (f.host !== void 0 || f.basePath !== void 0)) {
|
|
2274
|
+
if (f.host !== void 0 && TEMPLATED.test(f.host))
|
|
2275
|
+
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)`);
|
|
2276
|
+
else if (f.host !== void 0 && serviceAddress(f) !== void 0) {
|
|
2277
|
+
const a = serviceAddress(f);
|
|
2278
|
+
const parts = [
|
|
2279
|
+
["name", a.name],
|
|
2280
|
+
["namespace", a.namespace],
|
|
2281
|
+
...a.port !== void 0 ? [["port", String(a.port)]] : []
|
|
2282
|
+
];
|
|
2283
|
+
for (const [what, value] of parts)
|
|
2284
|
+
add(f, checkCite(f.cite, [{ kind: "word", value }], `${label} host's ${what}`, false));
|
|
2285
|
+
} else if (f.host !== void 0)
|
|
2286
|
+
add(f, checkCite(f.cite, [{ kind: "word", value: f.host }], `${label} host`, false));
|
|
2287
|
+
if (f.basePath !== void 0 && isPatternPath(f.basePath))
|
|
2288
|
+
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`);
|
|
2289
|
+
else if (f.basePath !== void 0)
|
|
2290
|
+
add(f, checkCite(f.cite, [routeOrWord(f.basePath), { kind: "word", value: f.basePath }], `${label} basePath`, false));
|
|
2291
|
+
const inModule = f.exposes !== void 0 ? repoModule(f.exposes) : void 0;
|
|
2292
|
+
if (inModule && estate && estate.has(factIdPart(inModule.repo))) {
|
|
2293
|
+
const known = repoComponents.get(factIdPart(inModule.repo));
|
|
2294
|
+
if (known && !known.has(factIdPart(inModule.module)))
|
|
2295
|
+
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`);
|
|
2296
|
+
} else if (f.exposes !== void 0 && estate && !estate.has(factIdPart(inModule?.repo ?? f.exposes)))
|
|
2297
|
+
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`);
|
|
2298
|
+
if (f.exposes === void 0 && !serves2)
|
|
2299
|
+
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`);
|
|
2300
|
+
}
|
|
2301
|
+
if (f.kind === "call") {
|
|
2302
|
+
for (const [what, claim, at] of [
|
|
2303
|
+
["auth", f.auth, f.authCite],
|
|
2304
|
+
["tls", f.tls, f.tlsCite]
|
|
2305
|
+
]) {
|
|
2306
|
+
if (claim !== void 0 && !at && f.by !== "person")
|
|
2307
|
+
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`);
|
|
2308
|
+
if (!at)
|
|
2309
|
+
continue;
|
|
2310
|
+
const bad = checkCite({ ...at }, [{ kind: "word", value: at.value }], `${label} ${what}Cite${claim ? ` (${what} ${claim})` : ""}`, false);
|
|
2311
|
+
add(f, bad);
|
|
2312
|
+
if (bad || claim === void 0)
|
|
2313
|
+
continue;
|
|
2314
|
+
const lines = [at, ...at.with ?? []].flatMap((p) => {
|
|
2315
|
+
const l = lineOfValue(linesOf2(p.file), p.line, p.value);
|
|
2316
|
+
return l === void 0 ? [] : [l];
|
|
2317
|
+
});
|
|
2318
|
+
const needs = securityLineKind(what, claim, lines);
|
|
2319
|
+
if (needs)
|
|
2320
|
+
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`);
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2323
|
+
if (f.kind === "term") {
|
|
2324
|
+
const m = f.meaning;
|
|
2325
|
+
if (m && m.file !== f.cite.file)
|
|
2326
|
+
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`);
|
|
2327
|
+
else if (m && m.line > f.cite.line)
|
|
2328
|
+
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`);
|
|
2329
|
+
else if (m)
|
|
2330
|
+
add(f, checkCite({ ...m }, [{ kind: "word", value: m.value }], `${label} meaning`, false));
|
|
2331
|
+
const bad = f.note !== void 0 && o.notes ? noteProblem(f.note, o.notes) : void 0;
|
|
2332
|
+
if (bad)
|
|
2333
|
+
problem(f, bad.code, `${label} ${bad.message}`);
|
|
2334
|
+
}
|
|
2335
|
+
if (f.kind === "component") {
|
|
2336
|
+
if (o.componentTypes && !o.componentTypes.has(f.type))
|
|
2337
|
+
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(", ")}`);
|
|
2338
|
+
const subject = factIdPart(f.component ?? f.repo);
|
|
2339
|
+
const first = facts.find((x) => x.kind === "component" && factIdPart(x.component ?? x.repo) === subject && (x === f || !found.has(x.id)));
|
|
2340
|
+
if (first && first !== f)
|
|
2341
|
+
problem(f, "component-type", `${label} says what ${subject} is a second time (${first.id} already does): one fact says what a component is`);
|
|
2342
|
+
}
|
|
1569
2343
|
if (f.kind === "framework") {
|
|
1570
|
-
const bad = frameworkNameProblem(f);
|
|
2344
|
+
const bad = frameworkNameProblem(f, rules.frameworks);
|
|
1571
2345
|
if (bad)
|
|
1572
2346
|
problem(f, "framework-name", `${label} ${bad}`);
|
|
1573
2347
|
if (f.version !== void 0)
|
|
@@ -1661,23 +2435,29 @@ async function verifyFacts(o) {
|
|
|
1661
2435
|
return { ...c, quote: q };
|
|
1662
2436
|
};
|
|
1663
2437
|
const authUncited = [];
|
|
2438
|
+
const methodUnseen = [];
|
|
2439
|
+
const kept = new Map(facts.filter((f) => !rejected.has(f.id)).map((f) => [f.id, f]));
|
|
2440
|
+
const factById = (id) => kept.get(id);
|
|
1664
2441
|
const accepted = facts.filter((f) => !rejected.has(f.id)).map((f) => {
|
|
1665
2442
|
o.room?.();
|
|
1666
2443
|
const verified = f.by === "person" && f.verified !== "none" ? f.verified : "line";
|
|
1667
|
-
const
|
|
1668
|
-
if (
|
|
2444
|
+
const base2 = { ...f, verified, cite: fixQuote(f.cite) };
|
|
2445
|
+
if (base2.kind === "entry" && base2.auth !== void 0 && !base2.authCite && f.by !== "person")
|
|
1669
2446
|
authUncited.push({
|
|
1670
|
-
id:
|
|
1671
|
-
auth:
|
|
1672
|
-
cite: `${
|
|
2447
|
+
id: base2.id,
|
|
2448
|
+
auth: base2.auth,
|
|
2449
|
+
cite: `${base2.cite.file}:${base2.cite.line}`
|
|
1673
2450
|
});
|
|
1674
|
-
|
|
2451
|
+
const unseen = methodUnseenOf(base2, linesOf2, factById);
|
|
2452
|
+
if (unseen)
|
|
2453
|
+
methodUnseen.push(unseen);
|
|
2454
|
+
if (base2.kind === "flow")
|
|
1675
2455
|
return {
|
|
1676
|
-
...
|
|
1677
|
-
steps:
|
|
1678
|
-
errors:
|
|
2456
|
+
...base2,
|
|
2457
|
+
steps: base2.steps.map((s) => ({ ...s, cite: fixQuote(s.cite) })),
|
|
2458
|
+
errors: base2.errors.map((e) => ({ ...e, cite: fixQuote(e.cite) }))
|
|
1679
2459
|
};
|
|
1680
|
-
return
|
|
2460
|
+
return base2;
|
|
1681
2461
|
});
|
|
1682
2462
|
const excludeRes = (o.exclude ?? []).map(globToRegExp);
|
|
1683
2463
|
const sources = [];
|
|
@@ -1685,7 +2465,7 @@ async function verifyFacts(o) {
|
|
|
1685
2465
|
for (const p of o.source.list()) {
|
|
1686
2466
|
o.room?.();
|
|
1687
2467
|
const ls = readLines(p) ?? [""];
|
|
1688
|
-
if (!isSourceFile(p, ls[0] ?? "", ls))
|
|
2468
|
+
if (!isSourceFile(p, ls[0] ?? "", ls, rules))
|
|
1689
2469
|
continue;
|
|
1690
2470
|
if (excludeRes.some((re) => re.test(p)))
|
|
1691
2471
|
excluded.push(p);
|
|
@@ -1701,6 +2481,8 @@ async function verifyFacts(o) {
|
|
|
1701
2481
|
const reviewedSet = /* @__PURE__ */ new Set();
|
|
1702
2482
|
const reviewed = [];
|
|
1703
2483
|
const reviewedProblems = [];
|
|
2484
|
+
const reviewedDropped = [];
|
|
2485
|
+
const reviewedGlobs = [];
|
|
1704
2486
|
for (const r of o.reviewed ?? []) {
|
|
1705
2487
|
const refuse = (message) => reviewedProblems.push({
|
|
1706
2488
|
...r.line !== void 0 ? { line: r.line } : {},
|
|
@@ -1716,36 +2498,105 @@ async function verifyFacts(o) {
|
|
|
1716
2498
|
refuse("is a secret's file, which is never read into a map");
|
|
1717
2499
|
continue;
|
|
1718
2500
|
}
|
|
1719
|
-
const
|
|
1720
|
-
|
|
2501
|
+
const range = lineRangeOf(r.path);
|
|
2502
|
+
if (range) {
|
|
2503
|
+
const text = allFiles.includes(range.file) ? o.source.read(range.file) : void 0;
|
|
2504
|
+
if (text === void 0) {
|
|
2505
|
+
refuse("is not in the repo at this commit; name a file you read");
|
|
2506
|
+
continue;
|
|
2507
|
+
}
|
|
2508
|
+
const last = text.split(/\r?\n/).length;
|
|
2509
|
+
if (range.from < 1 || range.from > range.to || range.to > last) {
|
|
2510
|
+
refuse(`is past the file's last line (${last}) or out of order; name the lines you read`);
|
|
2511
|
+
continue;
|
|
2512
|
+
}
|
|
2513
|
+
}
|
|
2514
|
+
const target = range ? range.file : r.path;
|
|
2515
|
+
const re = !range && isGlob(r.path) ? globToRegExp(r.path) : void 0;
|
|
2516
|
+
const matched = re ? allFiles.filter((p) => re.test(p)) : allFiles.filter((p) => p === target);
|
|
1721
2517
|
if (!matched.length) {
|
|
1722
|
-
|
|
2518
|
+
if (re)
|
|
2519
|
+
reviewedDropped.push({
|
|
2520
|
+
...r.line !== void 0 ? { line: r.line } : {},
|
|
2521
|
+
path: r.path,
|
|
2522
|
+
why: r.why,
|
|
2523
|
+
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)`
|
|
2524
|
+
});
|
|
2525
|
+
else
|
|
2526
|
+
refuse("is not in the repo at this commit; name a file you read");
|
|
2527
|
+
continue;
|
|
2528
|
+
}
|
|
2529
|
+
const entryPoints = re ? matched.filter((p) => isSource.has(p) && !touched.has(p) && isEntryPointFile(p)) : [];
|
|
2530
|
+
if (entryPoints.length) {
|
|
2531
|
+
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
2532
|
continue;
|
|
1724
2533
|
}
|
|
1725
2534
|
const files = matched.filter((p) => isSource.has(p) && !touched.has(p)).sort();
|
|
1726
2535
|
for (const p of files)
|
|
1727
2536
|
reviewedSet.add(p);
|
|
1728
2537
|
reviewed.push({ path: r.path, why: r.why, files });
|
|
2538
|
+
if (re)
|
|
2539
|
+
reviewedGlobs.push({ entry: r, files });
|
|
1729
2540
|
}
|
|
1730
2541
|
const read = sources.filter((p) => touched.has(p)).sort();
|
|
1731
2542
|
const reviewedFiles = sources.filter((p) => reviewedSet.has(p)).sort();
|
|
1732
|
-
const
|
|
2543
|
+
const leftOut = new Set(o.notReached ?? []);
|
|
2544
|
+
const notReached = sources.filter((p) => leftOut.has(p) && !touched.has(p) && !reviewedSet.has(p)).sort();
|
|
2545
|
+
const unknown = sources.filter((p) => !touched.has(p) && !reviewedSet.has(p) && !leftOut.has(p)).sort();
|
|
1733
2546
|
const total = read.length + reviewedFiles.length + unknown.length;
|
|
1734
2547
|
const ratio = total === 0 ? 1 : Math.round(read.length / total * 1e3) / 1e3;
|
|
2548
|
+
const ix = indexFacts(accepted);
|
|
2549
|
+
const specOpsUncovered = [];
|
|
2550
|
+
const specs = /* @__PURE__ */ new Set();
|
|
2551
|
+
for (const p of sources) {
|
|
2552
|
+
if (notReadForSkipped(p, rules))
|
|
2553
|
+
continue;
|
|
2554
|
+
o.room?.();
|
|
2555
|
+
const text = o.readers !== false && /\.(ya?ml|json)$/i.test(p) ? o.source.read(p) : void 0;
|
|
2556
|
+
const ops = text === void 0 ? void 0 : specOpsUncoveredIn(p, text, accepted);
|
|
2557
|
+
if (!ops)
|
|
2558
|
+
continue;
|
|
2559
|
+
specs.add(p);
|
|
2560
|
+
specOpsUncovered.push(...ops);
|
|
2561
|
+
}
|
|
2562
|
+
const skippedIn = (p) => {
|
|
2563
|
+
if (specs.has(p) || notReadForSkipped(p, rules))
|
|
2564
|
+
return [];
|
|
2565
|
+
o.room?.();
|
|
2566
|
+
const ls = readLines(p);
|
|
2567
|
+
return ls ? candidatesIn(p, ls).flatMap((c) => factsCover(p, c, ix) ? [] : [skippedOf(p, c)]) : [];
|
|
2568
|
+
};
|
|
2569
|
+
const skipped = read.flatMap(skippedIn);
|
|
2570
|
+
const reviewedHolds = reviewedGlobs.flatMap(({ entry, files }) => {
|
|
2571
|
+
const held = files.flatMap(skippedIn);
|
|
2572
|
+
return held.length ? [reviewedHoldsOf(entry, held)] : [];
|
|
2573
|
+
});
|
|
1735
2574
|
return {
|
|
1736
2575
|
accepted,
|
|
1737
2576
|
// a rejected fact goes back as it was read, at its own commit, even one that was carried
|
|
1738
2577
|
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: {
|
|
2578
|
+
coverage: {
|
|
2579
|
+
read,
|
|
2580
|
+
reviewed: reviewedFiles,
|
|
2581
|
+
unknown,
|
|
2582
|
+
excluded: excluded.sort(),
|
|
2583
|
+
notReached,
|
|
2584
|
+
ratio
|
|
2585
|
+
},
|
|
1740
2586
|
redacted,
|
|
1741
2587
|
quotesDropped,
|
|
1742
2588
|
carried: accepted.flatMap((f) => carriedFrom.has(f.id) ? [{ id: f.id, from: carriedFrom.get(f.id) }] : []),
|
|
1743
2589
|
corrections: { applied: corrected.applied, stale: corrected.stale },
|
|
1744
|
-
entriesNoFlowFollows: entriesNoFlowFollows(accepted),
|
|
2590
|
+
entriesNoFlowFollows: entriesNoFlowFollows(accepted, rules),
|
|
1745
2591
|
reviewed,
|
|
1746
2592
|
reviewedProblems,
|
|
2593
|
+
reviewedDropped,
|
|
1747
2594
|
authUncited: authUncited.sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0),
|
|
1748
|
-
|
|
2595
|
+
methodUnseen: methodUnseen.sort((a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0),
|
|
2596
|
+
skipped,
|
|
2597
|
+
reviewedHolds,
|
|
2598
|
+
specOpsUncovered,
|
|
2599
|
+
frameworkGaps: frameworkGaps(accepted, rules.frameworks)
|
|
1749
2600
|
};
|
|
1750
2601
|
}
|
|
1751
2602
|
|
|
@@ -1758,11 +2609,11 @@ var ENTRY_DIR = /(^|\/)(controllers?|routes|routers|handlers|api|endpoints|jobs|
|
|
|
1758
2609
|
var BOUNDARY_NAME = /(client|gateway|repository|repo|dao|mapper|entity|model|dto|request|response|publisher|producer|store|cache|schema)$/i;
|
|
1759
2610
|
var BOUNDARY_FILE = /\.(sql|proto|graphql|gql|avsc|wsdl|xsd)$|(^|\/)(openapi|swagger)[^/]*\.(ya?ml|json)$/i;
|
|
1760
2611
|
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 ?
|
|
2612
|
+
var TEST = /(^|\/)(test|tests|__tests__|spec|specs|e2e|it)\/|(^|\/)(test|tests)[-_][a-z0-9_-]+\/|[._-](test|spec)\.[^/.]+$|(Test|Tests|IT|Spec)\.[^/.]+$|(^|\/)test_[^/]+$|_test\.go$/;
|
|
2613
|
+
var stem2 = (path) => {
|
|
2614
|
+
const base2 = path.slice(path.lastIndexOf("/") + 1);
|
|
2615
|
+
const dot = base2.indexOf(".");
|
|
2616
|
+
return dot > 0 ? base2.slice(0, dot) : base2;
|
|
1766
2617
|
};
|
|
1767
2618
|
function readingRank(path) {
|
|
1768
2619
|
if (TEST.test(path))
|
|
@@ -1771,7 +2622,7 @@ function readingRank(path) {
|
|
|
1771
2622
|
return 0;
|
|
1772
2623
|
if (CONFIG.test(path))
|
|
1773
2624
|
return 1;
|
|
1774
|
-
const s =
|
|
2625
|
+
const s = stem2(path);
|
|
1775
2626
|
if (ENTRY_NAME.test(s) || ENTRY_DIR.test(path))
|
|
1776
2627
|
return 2;
|
|
1777
2628
|
if (BOUNDARY_NAME.test(s) || BOUNDARY_FILE.test(path) || BOUNDARY_DIR.test(path))
|
|
@@ -1819,9 +2670,9 @@ var CONFIG_CODE = /(^|\/)(config|configuration|settings|environments?)(\/|\.[^/]
|
|
|
1819
2670
|
var IAC = /\.(tf|tfvars|hcl|bicep)$|(^|\/)(serverless|template)[^/]*\.ya?ml$|(^|\/)(cdk|pulumi|terraform)\//;
|
|
1820
2671
|
var DATA_FILE = /\.sql$|(^|\/)(migrations?|db|database|changelogs?|liquibase|flyway)\//i;
|
|
1821
2672
|
var CONTRACT = /\.(proto|avsc|wsdl|xsd)$/i;
|
|
1822
|
-
var API_SCHEMA = /\.(graphql|gql)
|
|
2673
|
+
var API_SCHEMA = /\.(graphql|gql)$/i;
|
|
1823
2674
|
var STRUCTURED = /\.(ya?ml|json|xml|config|properties|toml|ini|conf)$/i;
|
|
1824
|
-
function fileKinds(path) {
|
|
2675
|
+
function fileKinds(path, rules = DEFAULT_READ_RULES) {
|
|
1825
2676
|
if (GATEWAY_FILE.test(path))
|
|
1826
2677
|
return [...GATEWAY_KINDS];
|
|
1827
2678
|
const k = /* @__PURE__ */ new Set();
|
|
@@ -1837,7 +2688,7 @@ function fileKinds(path) {
|
|
|
1837
2688
|
add(["data"]);
|
|
1838
2689
|
if (CONTRACT.test(path))
|
|
1839
2690
|
add(["protocols"]);
|
|
1840
|
-
if (API_SCHEMA.test(path))
|
|
2691
|
+
if (API_SCHEMA.test(path) || rules.apiSpec.test(path))
|
|
1841
2692
|
add(["routes", "protocols"]);
|
|
1842
2693
|
const isCode = SERVER_CODE.test(path) || DEVICE_CODE.test(path) || SCRIPT_CODE.test(path);
|
|
1843
2694
|
if (PAGE_CODE.test(path) || DEVICE_CODE.test(path))
|
|
@@ -1884,8 +2735,11 @@ ${numberedSlice(source.read(s.path) ?? "", s)}`;
|
|
|
1884
2735
|
var tokensOf = (chars) => Math.ceil(chars / 4);
|
|
1885
2736
|
function planBatches(source, o) {
|
|
1886
2737
|
const room = Math.max(1, o.room);
|
|
2738
|
+
const rules = readRulesOf(o.conventions);
|
|
1887
2739
|
const excludeRes = (o.exclude ?? []).map(globToRegExp);
|
|
1888
2740
|
const excluded = [];
|
|
2741
|
+
const leaveOut = new Set(o.reach?.leaveOut ?? []);
|
|
2742
|
+
const notReached = [];
|
|
1889
2743
|
const paths = [];
|
|
1890
2744
|
const texts = /* @__PURE__ */ new Map();
|
|
1891
2745
|
for (const p of source.list()) {
|
|
@@ -1893,7 +2747,7 @@ function planBatches(source, o) {
|
|
|
1893
2747
|
if (text === void 0)
|
|
1894
2748
|
continue;
|
|
1895
2749
|
const ls = linesOf(text);
|
|
1896
|
-
if (!isSourceFile(p, ls[0] ?? "", ls))
|
|
2750
|
+
if (!isSourceFile(p, ls[0] ?? "", ls, rules))
|
|
1897
2751
|
continue;
|
|
1898
2752
|
if (excludeRes.some((re) => re.test(p))) {
|
|
1899
2753
|
excluded.push(p);
|
|
@@ -1901,6 +2755,10 @@ function planBatches(source, o) {
|
|
|
1901
2755
|
}
|
|
1902
2756
|
if (!text.trim())
|
|
1903
2757
|
continue;
|
|
2758
|
+
if (leaveOut.has(p)) {
|
|
2759
|
+
notReached.push(p);
|
|
2760
|
+
continue;
|
|
2761
|
+
}
|
|
1904
2762
|
paths.push(p);
|
|
1905
2763
|
texts.set(p, text);
|
|
1906
2764
|
}
|
|
@@ -1916,8 +2774,8 @@ function planBatches(source, o) {
|
|
|
1916
2774
|
const slices = [];
|
|
1917
2775
|
let lines = 0;
|
|
1918
2776
|
let leastRoom = room;
|
|
1919
|
-
for (const path of
|
|
1920
|
-
const kinds2 = fileKinds(path);
|
|
2777
|
+
for (const path of reachOrder(paths, o.reach?.first)) {
|
|
2778
|
+
const kinds2 = fileKinds(path, rules);
|
|
1921
2779
|
const left = room - overhead(kinds2);
|
|
1922
2780
|
leastRoom = Math.min(leastRoom, left);
|
|
1923
2781
|
const fileRoom = Math.max(1, left);
|
|
@@ -1965,9 +2823,20 @@ function planBatches(source, o) {
|
|
|
1965
2823
|
tokens: slices.reduce((n, s) => n + s.tokens, 0),
|
|
1966
2824
|
batches,
|
|
1967
2825
|
excluded: excluded.sort(),
|
|
1968
|
-
leastRoom
|
|
2826
|
+
leastRoom,
|
|
2827
|
+
notReached: notReached.sort()
|
|
1969
2828
|
};
|
|
1970
2829
|
}
|
|
2830
|
+
function reachOrder(paths, first) {
|
|
2831
|
+
const order = readingOrder(paths);
|
|
2832
|
+
if (!first?.length)
|
|
2833
|
+
return order;
|
|
2834
|
+
const has = new Set(paths);
|
|
2835
|
+
const head = order.filter((p) => readingRank(p) <= 1);
|
|
2836
|
+
const lead = [...new Set(first)].filter((p) => has.has(p) && readingRank(p) > 1);
|
|
2837
|
+
const taken = /* @__PURE__ */ new Set([...head, ...lead]);
|
|
2838
|
+
return [...head, ...lead, ...order.filter((p) => !taken.has(p))];
|
|
2839
|
+
}
|
|
1971
2840
|
|
|
1972
2841
|
// packages/verify/dist/vendored.js
|
|
1973
2842
|
var LICENSE_FILE = /^(LICEN[CS]E|COPYING)([._-][^/]*)?$/i;
|
|
@@ -2032,10 +2901,10 @@ function buildModules(path, text) {
|
|
|
2032
2901
|
return found.filter((m) => !m.startsWith("!")).map(clean).filter(Boolean).map((m) => dir ? `${dir}/${m}` : m);
|
|
2033
2902
|
}
|
|
2034
2903
|
var JAVA_LIKE = /\.(java|kt|kts|scala|groovy)$/i;
|
|
2035
|
-
var
|
|
2904
|
+
var DOTNET2 = /\.(cs|vb|fs)$/i;
|
|
2036
2905
|
var rootOf = (name) => name.split(".").slice(0, 2).join(".").toLowerCase();
|
|
2037
2906
|
function packageRoot(path, text) {
|
|
2038
|
-
const re = JAVA_LIKE.test(path) ? /^\s*package\s+([A-Za-z_][\w.]*)/m :
|
|
2907
|
+
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
2908
|
const m = re?.exec(text);
|
|
2040
2909
|
return m ? rootOf(m[1]) : void 0;
|
|
2041
2910
|
}
|
|
@@ -2053,7 +2922,7 @@ function packageRoots(paths, inits, read) {
|
|
|
2053
2922
|
}
|
|
2054
2923
|
if (top)
|
|
2055
2924
|
roots.add(nameOf(top).toLowerCase());
|
|
2056
|
-
} else if ((JAVA_LIKE.test(p) ||
|
|
2925
|
+
} else if ((JAVA_LIKE.test(p) || DOTNET2.test(p)) && readCount < PACKAGE_SAMPLE) {
|
|
2057
2926
|
readCount++;
|
|
2058
2927
|
const r = packageRoot(p, read(p) ?? "");
|
|
2059
2928
|
if (r)
|
|
@@ -2233,6 +3102,27 @@ function exclusionYaml(proposals) {
|
|
|
2233
3102
|
].join("\n");
|
|
2234
3103
|
}
|
|
2235
3104
|
var SESSION_TOKENS = 15e4;
|
|
3105
|
+
var SESSION_SHARE = 0.45;
|
|
3106
|
+
function sessionTokensFor(limit) {
|
|
3107
|
+
return limit && limit > 0 ? Math.floor(limit * SESSION_SHARE) : SESSION_TOKENS;
|
|
3108
|
+
}
|
|
3109
|
+
var RANGE_OVERLAP_LINES = 20;
|
|
3110
|
+
var RANGE_OVERLAP_SHARE = 0.02;
|
|
3111
|
+
function rangeOverlap(each) {
|
|
3112
|
+
return Math.min(Math.floor(each / 5), Math.max(RANGE_OVERLAP_LINES, Math.ceil(RANGE_OVERLAP_SHARE * each)));
|
|
3113
|
+
}
|
|
3114
|
+
function lineRanges(f, room) {
|
|
3115
|
+
const n = Math.max(1, Math.min(f.lines, Math.ceil(f.tokens / (0.8 * room))));
|
|
3116
|
+
const each = Math.ceil(f.lines / n);
|
|
3117
|
+
const overlap = rangeOverlap(each);
|
|
3118
|
+
const out = [];
|
|
3119
|
+
for (let start = 1; start <= f.lines; start += each) {
|
|
3120
|
+
const from = start === 1 ? 1 : start - overlap;
|
|
3121
|
+
const to = Math.min(f.lines, start + each - 1);
|
|
3122
|
+
out.push({ from, to, start, tokens: Math.ceil(f.tokens * (to - from + 1) / f.lines) });
|
|
3123
|
+
}
|
|
3124
|
+
return out;
|
|
3125
|
+
}
|
|
2236
3126
|
function unitsOf(dir, fs, room) {
|
|
2237
3127
|
const prefix = dir ? `${dir}/` : "";
|
|
2238
3128
|
const direct = [];
|
|
@@ -2254,15 +3144,105 @@ function unitsOf(dir, fs, room) {
|
|
|
2254
3144
|
out.push(...unitsOf(child, children.get(child), room));
|
|
2255
3145
|
return out;
|
|
2256
3146
|
}
|
|
2257
|
-
function
|
|
3147
|
+
function groupUnits(dir, fs, room, holds) {
|
|
3148
|
+
const prefix = dir ? `${dir}/` : "";
|
|
3149
|
+
const direct = [];
|
|
3150
|
+
const children = /* @__PURE__ */ new Map();
|
|
3151
|
+
for (const f of fs) {
|
|
3152
|
+
const rest = f.path.slice(prefix.length);
|
|
3153
|
+
const slash = rest.indexOf("/");
|
|
3154
|
+
if (slash < 0)
|
|
3155
|
+
direct.push(f);
|
|
3156
|
+
else {
|
|
3157
|
+
const child = prefix + rest.slice(0, slash);
|
|
3158
|
+
children.set(child, [...children.get(child) ?? [], f]);
|
|
3159
|
+
}
|
|
3160
|
+
}
|
|
3161
|
+
if (dir && holds(dir, true, fs.length) && (sum(fs) <= room || !children.size))
|
|
3162
|
+
return [{ glob: `${dir}/**`, files: fs.length, tokens: sum(fs) }];
|
|
3163
|
+
const out = [];
|
|
3164
|
+
if (direct.length) {
|
|
3165
|
+
if (holds(dir, false, direct.length))
|
|
3166
|
+
out.push({ glob: `${prefix}*`, files: direct.length, tokens: sum(direct) });
|
|
3167
|
+
else
|
|
3168
|
+
for (const f of [...direct].sort((a, b) => byPath2(a.path, b.path)))
|
|
3169
|
+
out.push({ glob: f.path, files: 1, tokens: f.tokens });
|
|
3170
|
+
}
|
|
3171
|
+
for (const child of [...children.keys()].sort(byPath2))
|
|
3172
|
+
out.push(...groupUnits(child, children.get(child), room, holds));
|
|
3173
|
+
return out;
|
|
3174
|
+
}
|
|
3175
|
+
function rangeUnits(f, room) {
|
|
3176
|
+
return lineRanges({ ...f, lines: f.lines }, room).map((r, i) => ({
|
|
3177
|
+
glob: `${f.path}#L${r.from}-L${r.to}`,
|
|
3178
|
+
files: i === 0 ? 1 : 0,
|
|
3179
|
+
tokens: r.tokens
|
|
3180
|
+
}));
|
|
3181
|
+
}
|
|
3182
|
+
function orderedUnits(files, room, o) {
|
|
3183
|
+
const isBig = (f) => f.tokens > room && (f.lines ?? 0) > 1;
|
|
3184
|
+
const byFile = new Map(files.map((f) => [f.path, f]));
|
|
3185
|
+
const head = new Set(o.head.filter((p) => byFile.has(p)));
|
|
3186
|
+
const models = [...new Set(o.models)].filter((p) => byFile.has(p) && !head.has(p));
|
|
3187
|
+
const inModels = new Set(models);
|
|
3188
|
+
const rest = files.filter((f) => !head.has(f.path) && !inModels.has(f.path));
|
|
3189
|
+
const all2 = [...files.map((f) => f.path), ...o.leftOut];
|
|
3190
|
+
const counts = () => {
|
|
3191
|
+
const deep = /* @__PURE__ */ new Map();
|
|
3192
|
+
const own = /* @__PURE__ */ new Map();
|
|
3193
|
+
const count = (m, k) => m.set(k, (m.get(k) ?? 0) + 1);
|
|
3194
|
+
for (const p of all2) {
|
|
3195
|
+
const parts = p.split("/");
|
|
3196
|
+
count(own, parts.slice(0, -1).join("/"));
|
|
3197
|
+
for (let d = 1; d < parts.length; d++)
|
|
3198
|
+
count(deep, parts.slice(0, d).join("/"));
|
|
3199
|
+
}
|
|
3200
|
+
return (dir, isDeep, n) => (isDeep ? deep.get(dir) : own.get(dir)) === n;
|
|
3201
|
+
};
|
|
3202
|
+
const holds = counts();
|
|
3203
|
+
const unitsOfGroup = (group) => {
|
|
3204
|
+
const small = group.filter((f) => !isBig(f)).sort((a, b) => byPath2(a.path, b.path));
|
|
3205
|
+
const out = groupUnits("", small, room, holds);
|
|
3206
|
+
for (const f of group.filter(isBig))
|
|
3207
|
+
out.push(...rangeUnits(f, room));
|
|
3208
|
+
return out;
|
|
3209
|
+
};
|
|
3210
|
+
return [
|
|
3211
|
+
...unitsOfGroup(files.filter((f) => head.has(f.path))),
|
|
3212
|
+
...models.flatMap((p) => {
|
|
3213
|
+
const f = byFile.get(p);
|
|
3214
|
+
return isBig(f) ? rangeUnits(f, room) : [{ glob: p, files: 1, tokens: f.tokens }];
|
|
3215
|
+
}),
|
|
3216
|
+
...unitsOfGroup(rest)
|
|
3217
|
+
];
|
|
3218
|
+
}
|
|
3219
|
+
function planParts(files, room = SESSION_TOKENS, order) {
|
|
2258
3220
|
const total = sum(files);
|
|
2259
3221
|
if (total <= room)
|
|
2260
3222
|
return [];
|
|
2261
|
-
|
|
3223
|
+
let units;
|
|
3224
|
+
if (order)
|
|
3225
|
+
units = orderedUnits(files, room, order);
|
|
3226
|
+
else {
|
|
3227
|
+
const big = files.filter((f) => f.tokens > room && (f.lines ?? 0) > 1);
|
|
3228
|
+
const rest = files.filter((f) => !big.includes(f));
|
|
3229
|
+
const ranges = [];
|
|
3230
|
+
for (const f of big)
|
|
3231
|
+
for (const [i, r] of lineRanges({ ...f, lines: f.lines }, room).entries())
|
|
3232
|
+
ranges.push({
|
|
3233
|
+
glob: `${f.path}#L${r.from}-L${r.to}`,
|
|
3234
|
+
files: i === 0 ? 1 : 0,
|
|
3235
|
+
tokens: r.tokens,
|
|
3236
|
+
key: `${f.path}\0${String(r.start).padStart(12, "0")}`
|
|
3237
|
+
});
|
|
3238
|
+
const sorted = [...rest].sort((a, b) => byPath2(a.path, b.path));
|
|
3239
|
+
const keyOf = (g) => g.replace(/\/?\*\*?$/, "");
|
|
3240
|
+
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);
|
|
3241
|
+
}
|
|
2262
3242
|
const target = total / Math.ceil(total / room);
|
|
2263
3243
|
const parts = [];
|
|
2264
3244
|
let current;
|
|
2265
|
-
for (const u of
|
|
3245
|
+
for (const u of units) {
|
|
2266
3246
|
if (current && (current.tokens + u.tokens > room || current.tokens >= target)) {
|
|
2267
3247
|
parts.push(current);
|
|
2268
3248
|
current = void 0;
|
|
@@ -2277,13 +3257,104 @@ function planParts(files, room = SESSION_TOKENS) {
|
|
|
2277
3257
|
return parts;
|
|
2278
3258
|
}
|
|
2279
3259
|
|
|
3260
|
+
// packages/verify/dist/gaps.js
|
|
3261
|
+
var GAP_CONTEXT_LINES = 20;
|
|
3262
|
+
var byPath3 = (a, b) => a < b ? -1 : a > b ? 1 : 0;
|
|
3263
|
+
function quoted2(v) {
|
|
3264
|
+
return JSON.stringify(v).replace(/[\u007f-\u009f\u2028\u2029]/g, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, "0")}`);
|
|
3265
|
+
}
|
|
3266
|
+
function split(cite2) {
|
|
3267
|
+
const m = /^(.+):(\d+)$/.exec(cite2);
|
|
3268
|
+
return m ? { file: m[1], line: Number(m[2]) } : void 0;
|
|
3269
|
+
}
|
|
3270
|
+
function planGaps(from, size, room, o = {}) {
|
|
3271
|
+
const whole = new Set(from.unknown ?? []);
|
|
3272
|
+
const lines = /* @__PURE__ */ new Map();
|
|
3273
|
+
const note = (cite2, why) => {
|
|
3274
|
+
const at = split(cite2);
|
|
3275
|
+
if (!at)
|
|
3276
|
+
return;
|
|
3277
|
+
const of = lines.get(at.file) ?? /* @__PURE__ */ new Map();
|
|
3278
|
+
of.set(at.line, [...of.get(at.line) ?? [], why]);
|
|
3279
|
+
lines.set(at.file, of);
|
|
3280
|
+
};
|
|
3281
|
+
for (const s of from.skipped ?? [])
|
|
3282
|
+
note(s.cite, `writes the ${s.kind} ${quoted2(s.value)}, which no fact cites`);
|
|
3283
|
+
for (const h of from.reviewedHolds ?? [])
|
|
3284
|
+
for (const s of h.skipped)
|
|
3285
|
+
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`);
|
|
3286
|
+
for (const o2 of from.specOpsUncovered ?? [])
|
|
3287
|
+
note(o2.cite, `declares ${o2.method} ${quoted2(o2.path)}, which no entry covers: find the code that serves it`);
|
|
3288
|
+
for (const e of from.entriesNoFlowFollows ?? [])
|
|
3289
|
+
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`);
|
|
3290
|
+
const unlisted = Math.max(0, (from.skippedCount ?? 0) - (from.skipped?.length ?? 0)) + Math.max(0, (from.specOpsUncoveredCount ?? 0) - (from.specOpsUncovered?.length ?? 0));
|
|
3291
|
+
const gone = [];
|
|
3292
|
+
const reads = [];
|
|
3293
|
+
const named = [.../* @__PURE__ */ new Set([...whole, ...lines.keys()])].sort(byPath3);
|
|
3294
|
+
const files = o.first?.length ? reachOrder(named, o.first) : named;
|
|
3295
|
+
for (const file of files) {
|
|
3296
|
+
const sz = size(file);
|
|
3297
|
+
if (!sz) {
|
|
3298
|
+
gone.push(file);
|
|
3299
|
+
continue;
|
|
3300
|
+
}
|
|
3301
|
+
const at = lines.get(file) ?? /* @__PURE__ */ new Map();
|
|
3302
|
+
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}`));
|
|
3303
|
+
const share = (a, b) => Math.ceil(sz.tokens * (b - a + 1) / sz.lines);
|
|
3304
|
+
if (whole.has(file)) {
|
|
3305
|
+
const head = "no fact cites it: read it whole";
|
|
3306
|
+
if (sz.tokens <= room || sz.lines <= 1) {
|
|
3307
|
+
reads.push({ path: file, file, tokens: sz.tokens, why: [head, ...whys(1, sz.lines)] });
|
|
3308
|
+
continue;
|
|
3309
|
+
}
|
|
3310
|
+
for (const r of lineRanges(sz, room))
|
|
3311
|
+
reads.push({
|
|
3312
|
+
path: `${file}#L${r.from}-L${r.to}`,
|
|
3313
|
+
file,
|
|
3314
|
+
tokens: r.tokens,
|
|
3315
|
+
// R4: a line the ranges share is named in the range it is its own in, the earlier one.
|
|
3316
|
+
why: ["no fact cites the file: read these lines", ...whys(r.start, r.to)]
|
|
3317
|
+
});
|
|
3318
|
+
continue;
|
|
3319
|
+
}
|
|
3320
|
+
const windows = [];
|
|
3321
|
+
for (const l of [...at.keys()].sort((a, b) => a - b)) {
|
|
3322
|
+
const a = Math.max(1, l - GAP_CONTEXT_LINES);
|
|
3323
|
+
const b = Math.min(sz.lines, l + GAP_CONTEXT_LINES);
|
|
3324
|
+
const last = windows.at(-1);
|
|
3325
|
+
if (last && a <= last[1] + 1)
|
|
3326
|
+
last[1] = Math.max(last[1], b);
|
|
3327
|
+
else
|
|
3328
|
+
windows.push([a, b]);
|
|
3329
|
+
}
|
|
3330
|
+
for (const [a, b] of windows)
|
|
3331
|
+
reads.push({ path: `${file}#L${a}-L${b}`, file, tokens: share(a, b), why: whys(a, b) });
|
|
3332
|
+
}
|
|
3333
|
+
const sessions = [];
|
|
3334
|
+
for (const r of reads) {
|
|
3335
|
+
const last = sessions.at(-1);
|
|
3336
|
+
if (last && last.tokens + r.tokens <= room) {
|
|
3337
|
+
last.reads.push(r);
|
|
3338
|
+
last.tokens += r.tokens;
|
|
3339
|
+
} else
|
|
3340
|
+
sessions.push({ reads: [r], tokens: r.tokens });
|
|
3341
|
+
}
|
|
3342
|
+
return {
|
|
3343
|
+
sessions,
|
|
3344
|
+
files: new Set(reads.map((r) => r.file)).size,
|
|
3345
|
+
tokens: reads.reduce((n, r) => n + r.tokens, 0),
|
|
3346
|
+
unlisted,
|
|
3347
|
+
gone
|
|
3348
|
+
};
|
|
3349
|
+
}
|
|
3350
|
+
|
|
2280
3351
|
// packages/diagrams/dist/views.js
|
|
2281
3352
|
var VIEW_NAMES = ["context", "container", "data", "event", "sequence"];
|
|
2282
3353
|
var ViewError = class extends Error {
|
|
2283
3354
|
};
|
|
2284
3355
|
var cmp = (a, b) => a < b ? -1 : a > b ? 1 : 0;
|
|
2285
3356
|
var slug = (s) => s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
2286
|
-
var
|
|
3357
|
+
var normPath2 = (p) => p.replace(/\{[^}]*\}/g, "{}").replace(/\/+$/, "") || "/";
|
|
2287
3358
|
function legendOf(status) {
|
|
2288
3359
|
if (status === "planned-new")
|
|
2289
3360
|
return "new";
|
|
@@ -2372,7 +3443,7 @@ var Catalog = class {
|
|
|
2372
3443
|
what = this.topicOf(i);
|
|
2373
3444
|
else {
|
|
2374
3445
|
const ep = i.endpoint ? this.endpoints.get(i.endpoint) : void 0;
|
|
2375
|
-
const path = ep?.endpointType === "rest" &&
|
|
3446
|
+
const path = ep?.endpointType === "rest" && normPath2(ep.pathOrTopic) === normPath2(i.normalizedPath) ? ep.pathOrTopic : i.normalizedPath;
|
|
2376
3447
|
what = [i.method?.toUpperCase(), path].filter(Boolean).join(" ");
|
|
2377
3448
|
}
|
|
2378
3449
|
return [i.pattern?.code, what].filter(Boolean).join(" ");
|
|
@@ -2776,8 +3847,8 @@ function changeView(records2, change, flows, known = []) {
|
|
|
2776
3847
|
const nodes = [];
|
|
2777
3848
|
for (const id of comps) {
|
|
2778
3849
|
const sys = c.components.has(id) ? c.systemOf(id) : void 0;
|
|
2779
|
-
const
|
|
2780
|
-
const { legend: _status, ...rest } =
|
|
3850
|
+
const base2 = c.components.has(id) || c.isSystem(id) ? c.endNode(id, sys) : { id, label: id, kind: "component" };
|
|
3851
|
+
const { legend: _status, ...rest } = base2;
|
|
2781
3852
|
nodes.push({ ...rest, label: marked(rest.label, legends.get(id)), ...withMark(id) });
|
|
2782
3853
|
}
|
|
2783
3854
|
for (const t of tables) {
|
|
@@ -2873,14 +3944,14 @@ var Ids = class {
|
|
|
2873
3944
|
const known = this.map.get(key);
|
|
2874
3945
|
if (known)
|
|
2875
3946
|
return known;
|
|
2876
|
-
let
|
|
2877
|
-
if (!/^[A-Za-z]/.test(
|
|
2878
|
-
|
|
2879
|
-
if (RESERVED.has(
|
|
2880
|
-
|
|
2881
|
-
let id =
|
|
3947
|
+
let base2 = raw.replace(/[^A-Za-z0-9_]/g, "_");
|
|
3948
|
+
if (!/^[A-Za-z]/.test(base2))
|
|
3949
|
+
base2 = `n_${base2}`;
|
|
3950
|
+
if (RESERVED.has(base2.toLowerCase()))
|
|
3951
|
+
base2 = `${base2}_`;
|
|
3952
|
+
let id = base2;
|
|
2882
3953
|
for (let n = 2; this.used.has(id); n++)
|
|
2883
|
-
id = `${
|
|
3954
|
+
id = `${base2}_${n}`;
|
|
2884
3955
|
this.used.add(id);
|
|
2885
3956
|
this.map.set(key, id);
|
|
2886
3957
|
return id;
|
|
@@ -3069,8 +4140,8 @@ function nodeStyle(n) {
|
|
|
3069
4140
|
return styleText(SHAPES.topic, legend);
|
|
3070
4141
|
if (n.boundary === "vendor")
|
|
3071
4142
|
return styleText(SHAPES.vendor, n.kind === "component" ? { atticusKind: "component" } : {}, legend);
|
|
3072
|
-
const
|
|
3073
|
-
return styleText(
|
|
4143
|
+
const base2 = n.kind === "system" ? SHAPES.system : SHAPES.component;
|
|
4144
|
+
return styleText(base2, n.boundary ? BOUNDARY[n.boundary] : void 0, legend);
|
|
3074
4145
|
}
|
|
3075
4146
|
function groupStyle(g) {
|
|
3076
4147
|
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 +4232,14 @@ function edgeStyle(e) {
|
|
|
3161
4232
|
}
|
|
3162
4233
|
var compress = (xml) => deflateRawSync(Buffer.from(encodeURIComponent(xml), "utf8"), { level: 9 }).toString("base64");
|
|
3163
4234
|
function shapeLibrary() {
|
|
3164
|
-
const
|
|
4235
|
+
const base2 = [
|
|
3165
4236
|
["System", SHAPES.system, W, H],
|
|
3166
4237
|
["Component", SHAPES.component, W, H],
|
|
3167
4238
|
["Vendor", SHAPES.vendor, W, H + 20],
|
|
3168
4239
|
["Data store", SHAPES.datastore, 120, 80],
|
|
3169
4240
|
["Topic", SHAPES.topic, W, H]
|
|
3170
4241
|
];
|
|
3171
|
-
const entries =
|
|
4242
|
+
const entries = base2.flatMap(([title, style, w, h]) => [void 0, "new", "modify", "retire"].map((legend) => {
|
|
3172
4243
|
const value = attr(title);
|
|
3173
4244
|
const st = attr(styleText(style, legend ? LEGEND[legend] : void 0));
|
|
3174
4245
|
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 +4378,91 @@ var STATE = {
|
|
|
3307
4378
|
};
|
|
3308
4379
|
function recordGaps(r) {
|
|
3309
4380
|
const g = [];
|
|
3310
|
-
if (r.confidence === "inferred")
|
|
4381
|
+
if (r.confidence === "inferred" && !r.flags?.includes(JOINED_BY_PERSON))
|
|
3311
4382
|
g.push("inferred");
|
|
3312
4383
|
for (const f of r.flags ?? [])
|
|
3313
|
-
|
|
4384
|
+
if (!JOIN_HOW_FLAGS.has(f) && !LINK_EVIDENCE_NOT_GAPS.has(f))
|
|
4385
|
+
g.push(f);
|
|
3314
4386
|
return g;
|
|
3315
4387
|
}
|
|
4388
|
+
var MAX_JOINS = 20;
|
|
4389
|
+
var HOW_OF_FLAG = {
|
|
4390
|
+
"joined-by-path": "path",
|
|
4391
|
+
"joined-by-label": "label",
|
|
4392
|
+
"joined-by-platform-setting": "platform-setting",
|
|
4393
|
+
"joined-by-folder-name": "folder-name",
|
|
4394
|
+
"joined-despite-conflict": "conflict",
|
|
4395
|
+
"joined-by-service-name": "service-name",
|
|
4396
|
+
// G1 (ADR 0174): a person paired the call with the entry on the canvas.
|
|
4397
|
+
"joined-by-person": "person",
|
|
4398
|
+
// ADR 0181: the AI paired it, applied by Atticus from its run: the AI's word, not a person's.
|
|
4399
|
+
[JOINED_BY_AI]: "ai",
|
|
4400
|
+
"joined-by-answer": "answer",
|
|
4401
|
+
"joined-by-name": "named",
|
|
4402
|
+
"joined-by-repo": "repo",
|
|
4403
|
+
"joined-by-host": "host",
|
|
4404
|
+
"declared-caller": "declared",
|
|
4405
|
+
// ADR 0162 §6 (F8): a topic or queue the profile names as an outside system's: a person's word.
|
|
4406
|
+
[DECLARED_TOPIC]: "declared",
|
|
4407
|
+
// ADR 0160 (O3): answered by an entry of another team's shared map.
|
|
4408
|
+
[JOINED_TO_SHARED_MAP]: "shared"
|
|
4409
|
+
};
|
|
4410
|
+
function joinHow(i) {
|
|
4411
|
+
for (const f of i.flags ?? [])
|
|
4412
|
+
if (HOW_OF_FLAG[f])
|
|
4413
|
+
return HOW_OF_FLAG[f];
|
|
4414
|
+
if (i.via === "event-bus" || i.via === "mq")
|
|
4415
|
+
return "topic";
|
|
4416
|
+
return "unknown";
|
|
4417
|
+
}
|
|
4418
|
+
function joinOf(i) {
|
|
4419
|
+
const side = (s) => evidenceOf(i.evidence.filter((e) => e.side === s));
|
|
4420
|
+
const sided = /* @__PURE__ */ new Set(["caller", "callee", "gateway"]);
|
|
4421
|
+
return {
|
|
4422
|
+
interface: i.id,
|
|
4423
|
+
from: i.from,
|
|
4424
|
+
to: i.to,
|
|
4425
|
+
how: joinHow(i),
|
|
4426
|
+
gateway: i.via === "gateway",
|
|
4427
|
+
verified: i.confidence === "verified",
|
|
4428
|
+
...i.method ? { method: i.method } : {},
|
|
4429
|
+
...i.normalizedPath ? { path: i.normalizedPath } : {},
|
|
4430
|
+
caller: side("caller"),
|
|
4431
|
+
callee: side("callee"),
|
|
4432
|
+
hops: side("gateway"),
|
|
4433
|
+
other: evidenceOf(i.evidence.filter((e) => !e.side || !sided.has(e.side))),
|
|
4434
|
+
...sharedOf(i),
|
|
4435
|
+
...aiRunOf(i)
|
|
4436
|
+
};
|
|
4437
|
+
}
|
|
4438
|
+
function aiRunOf(i) {
|
|
4439
|
+
if (!i.flags?.includes(JOINED_BY_AI))
|
|
4440
|
+
return {};
|
|
4441
|
+
const run = i.evidence.find((e) => e.source === "ai" && e.run)?.run;
|
|
4442
|
+
return run ? { run } : {};
|
|
4443
|
+
}
|
|
4444
|
+
function sharedOf(i) {
|
|
4445
|
+
if (!i.flags?.includes(JOINED_TO_SHARED_MAP))
|
|
4446
|
+
return {};
|
|
4447
|
+
const e = i.evidence.find((x) => x.extractor === SHARED_MAP_EVIDENCE && x.side === "callee" && x.repo && x.path);
|
|
4448
|
+
if (!e)
|
|
4449
|
+
return {};
|
|
4450
|
+
return {
|
|
4451
|
+
shared: {
|
|
4452
|
+
team: e.by ?? "",
|
|
4453
|
+
repo: e.repo,
|
|
4454
|
+
at: e.line === void 0 ? e.path : `${e.path}:${e.line}`,
|
|
4455
|
+
...e.commit ? { commit: e.commit } : {}
|
|
4456
|
+
}
|
|
4457
|
+
};
|
|
4458
|
+
}
|
|
4459
|
+
function mergeJoins(a, b) {
|
|
4460
|
+
const by = new Map(a.map((j) => [j.interface, j]));
|
|
4461
|
+
for (const j of b)
|
|
4462
|
+
if (!by.has(j.interface))
|
|
4463
|
+
by.set(j.interface, j);
|
|
4464
|
+
return [...by.values()].sort((x, y) => x.interface < y.interface ? -1 : x.interface > y.interface ? 1 : 0).slice(0, MAX_JOINS);
|
|
4465
|
+
}
|
|
3316
4466
|
var props = (o) => {
|
|
3317
4467
|
const out = {};
|
|
3318
4468
|
for (const [k, v] of Object.entries(o)) {
|
|
@@ -3462,6 +4612,7 @@ function buildEstate(records2) {
|
|
|
3462
4612
|
add(fromRecord(c, `component:${c.id}`, "component", c.id, parent, { type: c.type, runtime: c.runtime, repo: c.repo, path: c.path }, gaps));
|
|
3463
4613
|
}
|
|
3464
4614
|
const endpointNode = /* @__PURE__ */ new Map();
|
|
4615
|
+
const topicByPath = /* @__PURE__ */ new Map();
|
|
3465
4616
|
for (const e of all(records2, "Endpoint")) {
|
|
3466
4617
|
const topic = TOPIC_TYPES.has(e.endpointType);
|
|
3467
4618
|
const id = `${topic ? "topic" : "endpoint"}:${e.id}`;
|
|
@@ -3478,7 +4629,30 @@ function buildEstate(records2) {
|
|
|
3478
4629
|
auth: e.auth
|
|
3479
4630
|
}, gaps));
|
|
3480
4631
|
endpointNode.set(e.id, id);
|
|
4632
|
+
if (topic)
|
|
4633
|
+
topicByPath.set(e.pathOrTopic, id);
|
|
3481
4634
|
}
|
|
4635
|
+
const busTopic = (name, parent, ev = []) => {
|
|
4636
|
+
const known = topicByPath.get(name);
|
|
4637
|
+
if (known)
|
|
4638
|
+
return known;
|
|
4639
|
+
const id = add({
|
|
4640
|
+
id: `topic:bus.${slug2(name)}`,
|
|
4641
|
+
kind: "topic",
|
|
4642
|
+
label: name,
|
|
4643
|
+
parent,
|
|
4644
|
+
state: "current",
|
|
4645
|
+
gaps: [],
|
|
4646
|
+
evidence: ev,
|
|
4647
|
+
properties: { pathOrTopic: name },
|
|
4648
|
+
next: {
|
|
4649
|
+
command: `atticus query "${name}"`,
|
|
4650
|
+
why: "List the records that name this topic or queue."
|
|
4651
|
+
}
|
|
4652
|
+
});
|
|
4653
|
+
topicByPath.set(name, id);
|
|
4654
|
+
return id;
|
|
4655
|
+
};
|
|
3482
4656
|
const storeNode = /* @__PURE__ */ new Map();
|
|
3483
4657
|
for (const d of all(records2, "DataStore")) {
|
|
3484
4658
|
const owner = d.ownerComponent && components.has(d.ownerComponent);
|
|
@@ -3523,9 +4697,33 @@ function buildEstate(records2) {
|
|
|
3523
4697
|
const ev = evidenceOf(i.evidence);
|
|
3524
4698
|
const gaps = [...recordGaps(i), ...i.conflicts.length ? ["conflicts"] : []];
|
|
3525
4699
|
const target = i.endpoint ? endpointNode.get(i.endpoint) : void 0;
|
|
4700
|
+
const join2 = joinOf(i);
|
|
3526
4701
|
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 });
|
|
4702
|
+
atoms.push({ from, to: target, type: "publishes", evidence: ev, gaps, join: join2 });
|
|
4703
|
+
atoms.push({ from: to, to: target, type: "subscribes", evidence: ev, gaps, join: join2 });
|
|
4704
|
+
continue;
|
|
4705
|
+
}
|
|
4706
|
+
if (!i.endpoint && (i.via === "event-bus" || i.via === "mq") && i.normalizedPath) {
|
|
4707
|
+
const isSystem = (n) => n.startsWith("system:") || n.startsWith("unknown:");
|
|
4708
|
+
const [home, homeRef] = !isSystem(to) && isSystem(from) ? [from, i.from] : [to, i.to];
|
|
4709
|
+
const t = busTopic(i.normalizedPath, home);
|
|
4710
|
+
const owner = nodes.get(t);
|
|
4711
|
+
if (owner.parent === home && isSystem(home)) {
|
|
4712
|
+
owner.properties = { ...owner.properties, owner: nodes.get(home)?.label ?? home };
|
|
4713
|
+
const b = nodes.get(home)?.properties.boundary;
|
|
4714
|
+
if (outside.has(homeRef) || b === "vendor" || b === "outside")
|
|
4715
|
+
owner.properties.ownerOutside = true;
|
|
4716
|
+
}
|
|
4717
|
+
if (join2.shared)
|
|
4718
|
+
owner.properties = { ...owner.properties, owner: join2.shared.team, ownerOutside: true };
|
|
4719
|
+
if (i.flags?.includes(DECLARED_TOPIC)) {
|
|
4720
|
+
owner.properties = { ...owner.properties, declared: true };
|
|
4721
|
+
owner.gaps = [.../* @__PURE__ */ new Set([...owner.gaps, DECLARED_TOPIC])];
|
|
4722
|
+
owner.evidence = mergeEvidence(owner.evidence, evidenceOf(i.evidence.filter((e) => e.source === "manual")));
|
|
4723
|
+
}
|
|
4724
|
+
atoms.push({ from, to: t, type: "publishes", evidence: ev, gaps, join: join2 });
|
|
4725
|
+
if (to.startsWith("component:"))
|
|
4726
|
+
atoms.push({ from: to, to: t, type: "subscribes", evidence: ev, gaps, join: join2 });
|
|
3529
4727
|
continue;
|
|
3530
4728
|
}
|
|
3531
4729
|
let caller = from;
|
|
@@ -3556,7 +4754,24 @@ function buildEstate(records2) {
|
|
|
3556
4754
|
sync: i.sync,
|
|
3557
4755
|
via: i.via,
|
|
3558
4756
|
evidence: ev,
|
|
3559
|
-
gaps: [...gaps, ...i.endpoint && !target ? ["no-endpoint"] : []]
|
|
4757
|
+
gaps: [...gaps, ...i.endpoint && !target ? ["no-endpoint"] : []],
|
|
4758
|
+
join: join2
|
|
4759
|
+
});
|
|
4760
|
+
}
|
|
4761
|
+
for (const u of all(records2, "Unresolved")) {
|
|
4762
|
+
if (u.unresolvedKind !== "event-publish" && u.unresolvedKind !== "event-subscribe")
|
|
4763
|
+
continue;
|
|
4764
|
+
if (!u.raw || u.raw.includes("${"))
|
|
4765
|
+
continue;
|
|
4766
|
+
const owner = u.owner ? partNode(u.owner) : "estate";
|
|
4767
|
+
const ev = evidenceOf(u.evidence);
|
|
4768
|
+
const t = busTopic(u.raw, u.unresolvedKind === "event-subscribe" ? owner : "estate", ev);
|
|
4769
|
+
atoms.push({
|
|
4770
|
+
from: owner,
|
|
4771
|
+
to: t,
|
|
4772
|
+
type: u.unresolvedKind === "event-publish" ? "publishes" : "subscribes",
|
|
4773
|
+
evidence: ev,
|
|
4774
|
+
gaps: recordGaps(u)
|
|
3560
4775
|
});
|
|
3561
4776
|
}
|
|
3562
4777
|
for (const c of components.values())
|
|
@@ -3611,6 +4826,37 @@ function buildEstate(records2) {
|
|
|
3611
4826
|
gaps: recordGaps(k)
|
|
3612
4827
|
});
|
|
3613
4828
|
}
|
|
4829
|
+
const busy = /* @__PURE__ */ new Set();
|
|
4830
|
+
const internal = (id) => {
|
|
4831
|
+
const n = nodes.get(id);
|
|
4832
|
+
return !!n && n.kind !== "unknown" && n.properties.boundary !== "outside" && n.properties.boundary !== "vendor";
|
|
4833
|
+
};
|
|
4834
|
+
for (const a of atoms)
|
|
4835
|
+
if (a.type === "publishes" || a.type === "subscribes") {
|
|
4836
|
+
if (internal(a.from))
|
|
4837
|
+
busy.add(`${a.type}|${a.to}`);
|
|
4838
|
+
}
|
|
4839
|
+
for (const n of nodes.values()) {
|
|
4840
|
+
if (n.kind !== "topic")
|
|
4841
|
+
continue;
|
|
4842
|
+
if (n.properties.owner === void 0) {
|
|
4843
|
+
let p = n.parent ? nodes.get(n.parent) : void 0;
|
|
4844
|
+
while (p && p.kind !== "system" && p.kind !== "repo" && p.kind !== "estate")
|
|
4845
|
+
p = p.parent ? nodes.get(p.parent) : void 0;
|
|
4846
|
+
if (p && p.kind !== "estate") {
|
|
4847
|
+
let sys = p;
|
|
4848
|
+
while (sys && sys.kind !== "system")
|
|
4849
|
+
sys = sys.parent ? nodes.get(sys.parent) : void 0;
|
|
4850
|
+
n.properties = { ...n.properties, owner: (sys ?? p).label };
|
|
4851
|
+
if (sys && (sys.properties.boundary === "outside" || sys.properties.boundary === "vendor"))
|
|
4852
|
+
n.properties.ownerOutside = true;
|
|
4853
|
+
}
|
|
4854
|
+
}
|
|
4855
|
+
if (!busy.has(`publishes|${n.id}`))
|
|
4856
|
+
n.gaps.push("no-publisher");
|
|
4857
|
+
if (!busy.has(`subscribes|${n.id}`))
|
|
4858
|
+
n.gaps.push("no-listener");
|
|
4859
|
+
}
|
|
3614
4860
|
const kids = /* @__PURE__ */ new Map();
|
|
3615
4861
|
for (const n of nodes.values()) {
|
|
3616
4862
|
if (!n.parent)
|
|
@@ -3705,10 +4951,13 @@ function merge(mapped) {
|
|
|
3705
4951
|
count: 1,
|
|
3706
4952
|
state: "current",
|
|
3707
4953
|
gaps: [...new Set(atom.gaps)].sort(),
|
|
3708
|
-
evidence: mergeEvidence(atom.evidence)
|
|
4954
|
+
evidence: mergeEvidence(atom.evidence),
|
|
4955
|
+
...atom.join ? { joins: [atom.join] } : {}
|
|
3709
4956
|
});
|
|
3710
4957
|
continue;
|
|
3711
4958
|
}
|
|
4959
|
+
if (atom.join)
|
|
4960
|
+
e.joins = mergeJoins(e.joins ?? [], [atom.join]);
|
|
3712
4961
|
e.count++;
|
|
3713
4962
|
if (atom.sync && e.sync !== atom.sync)
|
|
3714
4963
|
e.sync = "mixed";
|
|
@@ -3772,16 +5021,27 @@ function nodeView(est, id, o) {
|
|
|
3772
5021
|
return ax[i] ?? x;
|
|
3773
5022
|
};
|
|
3774
5023
|
const types = LENS_EDGES[lens];
|
|
5024
|
+
const onBus = (atom, b) => (atom.type === "publishes" || atom.type === "subscribes") && est.nodes.get(b)?.kind === "topic" && b !== id;
|
|
5025
|
+
const drawnTopics = /* @__PURE__ */ new Set();
|
|
5026
|
+
for (const atom of est.atoms) {
|
|
5027
|
+
if (!types.has(atom.type))
|
|
5028
|
+
continue;
|
|
5029
|
+
const a = lift(est, lens, atom.from);
|
|
5030
|
+
const b = lift(est, lens, atom.to);
|
|
5031
|
+
if (onBus(atom, b) && (inside(a) || inside(b)))
|
|
5032
|
+
drawnTopics.add(b);
|
|
5033
|
+
}
|
|
3775
5034
|
const mapped = [];
|
|
3776
5035
|
for (const atom of est.atoms) {
|
|
3777
5036
|
if (!types.has(atom.type))
|
|
3778
5037
|
continue;
|
|
3779
5038
|
const a = lift(est, lens, atom.from);
|
|
3780
5039
|
const b = lift(est, lens, atom.to);
|
|
3781
|
-
|
|
5040
|
+
const bus = onBus(atom, b);
|
|
5041
|
+
if (!inside(a) && !inside(b) && !(bus && drawnTopics.has(b)))
|
|
3782
5042
|
continue;
|
|
3783
5043
|
const from = place(a);
|
|
3784
|
-
const to = place(b);
|
|
5044
|
+
const to = bus ? b : place(b);
|
|
3785
5045
|
if (from === to)
|
|
3786
5046
|
continue;
|
|
3787
5047
|
mapped.push({ from, to, atom });
|
|
@@ -3790,10 +5050,12 @@ function nodeView(est, id, o) {
|
|
|
3790
5050
|
let hidden = 0;
|
|
3791
5051
|
if (!o.all)
|
|
3792
5052
|
({ edges, hidden } = reduceUses(edges));
|
|
3793
|
-
const
|
|
3794
|
-
const
|
|
5053
|
+
const ends = [...new Set(edges.flatMap((e) => [e.from, e.to]))].filter((n) => n !== id && !shownSet.has(n));
|
|
5054
|
+
const deeper = ends.filter((n) => drawnTopics.has(n) && inside(n)).sort();
|
|
5055
|
+
const outside = ends.filter((n) => !deeper.includes(n)).sort();
|
|
5056
|
+
const children = [...shown, ...deeper].map((n) => toNode(est, lens, n));
|
|
3795
5057
|
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;
|
|
5058
|
+
const suggest = lens === "data" && firstTable ? { mode: "focus", id: firstTable.id } : shown.length + deeper.length + outside.length > DETAILED_LIMIT ? { mode: "group" } : void 0;
|
|
3797
5059
|
return {
|
|
3798
5060
|
lens,
|
|
3799
5061
|
node: toNode(est, lens, id),
|
|
@@ -3931,13 +5193,13 @@ function codeLinkerFor(o) {
|
|
|
3931
5193
|
web = `https://${scp[1]}/${scp[2]}`;
|
|
3932
5194
|
}
|
|
3933
5195
|
if (web) {
|
|
3934
|
-
const
|
|
3935
|
-
const ado = /(^https?:\/\/dev\.azure\.com\/)|(\.visualstudio\.com\/)/i.test(
|
|
5196
|
+
const base2 = web.replace(/\/+$/, "").replace(/\.git$/, "");
|
|
5197
|
+
const ado = /(^https?:\/\/dev\.azure\.com\/)|(\.visualstudio\.com\/)/i.test(base2);
|
|
3936
5198
|
return (path, line, commit) => {
|
|
3937
5199
|
const p = encodeURI(path);
|
|
3938
5200
|
if (ado)
|
|
3939
|
-
return `${
|
|
3940
|
-
return `${
|
|
5201
|
+
return `${base2}?path=/${p}${commit ? `&version=GC${commit}` : ""}${line ? `&line=${line}` : ""}`;
|
|
5202
|
+
return `${base2}/blob/${commit ?? "HEAD"}/${p}${line ? `#L${line}` : ""}`;
|
|
3941
5203
|
};
|
|
3942
5204
|
}
|
|
3943
5205
|
const dir = (o.dir ?? "").replace(/\/+$/, "");
|
|
@@ -4152,6 +5414,27 @@ function config(c) {
|
|
|
4152
5414
|
return `- ${code(`${s.key} = ${s.valueOrRef}`)} (${notes.join("; ")}): ${cite(c, ownEvidence(s, c.i.repo))}`;
|
|
4153
5415
|
});
|
|
4154
5416
|
}
|
|
5417
|
+
function terms(c) {
|
|
5418
|
+
const ts = c.i.terms ?? [];
|
|
5419
|
+
const owners = /* @__PURE__ */ new Map();
|
|
5420
|
+
for (const r of records(c, "Component"))
|
|
5421
|
+
for (const f of r.flags ?? []) {
|
|
5422
|
+
const m = /^term:[^=]+=(.+)$/.exec(f);
|
|
5423
|
+
if (m && !c.comps.has(r.id))
|
|
5424
|
+
owners.set(m[1], [...owners.get(m[1]) ?? [], r.id]);
|
|
5425
|
+
}
|
|
5426
|
+
const listing = /* @__PURE__ */ new Map();
|
|
5427
|
+
for (const g of records(c, "AccessGrant"))
|
|
5428
|
+
if (g.flags?.some((f) => f.startsWith("term:"))) {
|
|
5429
|
+
const route = c.byId.get(g.resource);
|
|
5430
|
+
const by = route?.kind === "GatewayRoute" ? route.proxy : g.resource;
|
|
5431
|
+
listing.set(g.subject, [.../* @__PURE__ */ new Set([...listing.get(g.subject) ?? [], by])]);
|
|
5432
|
+
}
|
|
5433
|
+
return [...ts].sort((a, b) => cmp2(a.term, b.term) || cmp2(a.value, b.value)).map((t) => {
|
|
5434
|
+
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)` : ""}`;
|
|
5435
|
+
return `- ${code(t.term)} ${code(t.value)}: ${means} ${cite(c, t.cite)}${t.meaning ? ` (its role from ${cite(c, t.meaning)})` : ""}`;
|
|
5436
|
+
});
|
|
5437
|
+
}
|
|
4155
5438
|
var flowFile = (f) => `flows/${f.id}.md`;
|
|
4156
5439
|
function flowsList(c) {
|
|
4157
5440
|
if (!c.i.flows.length)
|
|
@@ -4365,8 +5648,8 @@ function ctxOf(i, file) {
|
|
|
4365
5648
|
}
|
|
4366
5649
|
function renderMap(i) {
|
|
4367
5650
|
const files = {};
|
|
4368
|
-
const
|
|
4369
|
-
for (const comp of [...
|
|
5651
|
+
const base2 = ctxOf(i, "map.md");
|
|
5652
|
+
for (const comp of [...base2.comps].sort(cmp2)) {
|
|
4370
5653
|
const yaml = openApiOf(i, comp);
|
|
4371
5654
|
if (yaml)
|
|
4372
5655
|
files[`contracts/${comp}.openapi.yaml`] = yaml;
|
|
@@ -4380,17 +5663,18 @@ function renderMap(i) {
|
|
|
4380
5663
|
HEADER2(i.repo, "the catalog and its specs"),
|
|
4381
5664
|
"",
|
|
4382
5665
|
...aboutLine(i.about, i.repo),
|
|
4383
|
-
...builtOnLine(
|
|
5666
|
+
...builtOnLine(base2),
|
|
4384
5667
|
trustLine(i.trust, i.repo),
|
|
4385
5668
|
"",
|
|
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("
|
|
5669
|
+
...section("What it serves", serves(base2)),
|
|
5670
|
+
...section("What it calls", calls(base2)),
|
|
5671
|
+
...section("What it stores", stores(base2)),
|
|
5672
|
+
...section("Where it runs", deploys(base2)),
|
|
5673
|
+
...section("Config that names other systems", config(base2)),
|
|
5674
|
+
...i.terms?.length ? section("Terms", terms(base2)) : [],
|
|
5675
|
+
...section("Flows", flowsList(base2)),
|
|
5676
|
+
...section("Contracts", contracts(base2, files)),
|
|
5677
|
+
...section("Open questions", openQuestions(base2))
|
|
4394
5678
|
].join("\n");
|
|
4395
5679
|
return Object.fromEntries(Object.entries(files).sort(([a], [b]) => cmp2(a, b)));
|
|
4396
5680
|
}
|
|
@@ -4430,7 +5714,7 @@ function rereadPlan(o) {
|
|
|
4430
5714
|
if (text === void 0 || !text.trim())
|
|
4431
5715
|
return false;
|
|
4432
5716
|
const ls = text.split(/\r?\n/);
|
|
4433
|
-
if (!isSourceFile(p, ls[0] ?? "", ls))
|
|
5717
|
+
if (!isSourceFile(p, ls[0] ?? "", ls, readRulesOf(o.conventions)))
|
|
4434
5718
|
return false;
|
|
4435
5719
|
return !excludeRes.some((re) => re.test(p));
|
|
4436
5720
|
});
|
|
@@ -4454,10 +5738,241 @@ function onlyFiles(source, paths) {
|
|
|
4454
5738
|
return { list: () => source.list().filter((p) => keep.has(p)), read: (p) => source.read(p) };
|
|
4455
5739
|
}
|
|
4456
5740
|
|
|
5741
|
+
// packages/verify/dist/slips.js
|
|
5742
|
+
function slugFactId(id) {
|
|
5743
|
+
return 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(/-+$/, "").slice(0, 120);
|
|
5744
|
+
}
|
|
5745
|
+
var isId = (x) => typeof x === "string" && FactId.safeParse(x).success;
|
|
5746
|
+
var isObj = (x) => !!x && typeof x === "object" && !Array.isArray(x);
|
|
5747
|
+
var REF_FIELDS = ["from", "under", "owner", "mappedTo", "entry"];
|
|
5748
|
+
function repairFormSlips(facts, bad, onFile = []) {
|
|
5749
|
+
const slips = [];
|
|
5750
|
+
const cands = bad.flatMap((b) => {
|
|
5751
|
+
if (b.issues.some((i) => i.code === "not-json" || i.code === "duplicate-id"))
|
|
5752
|
+
return [];
|
|
5753
|
+
let json2;
|
|
5754
|
+
try {
|
|
5755
|
+
json2 = JSON.parse(b.raw);
|
|
5756
|
+
} catch {
|
|
5757
|
+
return [];
|
|
5758
|
+
}
|
|
5759
|
+
return isObj(json2) ? [{ b, json: json2 }] : [];
|
|
5760
|
+
});
|
|
5761
|
+
const taken = /* @__PURE__ */ new Map();
|
|
5762
|
+
for (const f of facts)
|
|
5763
|
+
taken.set(f.id, (taken.get(f.id) ?? 0) + 1);
|
|
5764
|
+
for (const { json: json2 } of cands)
|
|
5765
|
+
if (isId(json2.id))
|
|
5766
|
+
taken.set(json2.id, (taken.get(json2.id) ?? 0) + 1);
|
|
5767
|
+
const wanted = /* @__PURE__ */ new Map();
|
|
5768
|
+
for (const { json: json2 } of cands)
|
|
5769
|
+
if (typeof json2.id === "string" && !isId(json2.id)) {
|
|
5770
|
+
const s = slugFactId(json2.id);
|
|
5771
|
+
if (isId(s))
|
|
5772
|
+
wanted.set(s, (wanted.get(s) ?? 0) + 1);
|
|
5773
|
+
}
|
|
5774
|
+
const known = new Set([...facts, ...onFile].map((f) => f.id));
|
|
5775
|
+
const renames = /* @__PURE__ */ new Map();
|
|
5776
|
+
const collided = /* @__PURE__ */ new Map();
|
|
5777
|
+
for (const { b, json: json2 } of cands) {
|
|
5778
|
+
if (typeof json2.id !== "string" || isId(json2.id))
|
|
5779
|
+
continue;
|
|
5780
|
+
const s = slugFactId(json2.id);
|
|
5781
|
+
if (!isId(s))
|
|
5782
|
+
continue;
|
|
5783
|
+
if (taken.has(s) || (wanted.get(s) ?? 0) > 1) {
|
|
5784
|
+
const message = `line ${b.line}: fact id ${json2.id} would slug to ${s}, which another fact already has; rename one of them`;
|
|
5785
|
+
slips.push({ line: b.line, code: "id-collision", fact: json2.id, message });
|
|
5786
|
+
collided.set(b, message);
|
|
5787
|
+
continue;
|
|
5788
|
+
}
|
|
5789
|
+
renames.set(json2.id, s);
|
|
5790
|
+
}
|
|
5791
|
+
const rewritten = cands.map(({ b, json: json2 }) => {
|
|
5792
|
+
const o = structuredClone(json2);
|
|
5793
|
+
const fixes = [];
|
|
5794
|
+
if (typeof o.id === "string" && renames.has(o.id)) {
|
|
5795
|
+
fixes.push({
|
|
5796
|
+
line: b.line,
|
|
5797
|
+
code: "id-slugged",
|
|
5798
|
+
fact: renames.get(o.id),
|
|
5799
|
+
message: `line ${b.line}: fact id ${o.id} slugged to ${renames.get(o.id)}, and every reference to it`
|
|
5800
|
+
});
|
|
5801
|
+
o.id = renames.get(o.id);
|
|
5802
|
+
}
|
|
5803
|
+
const ref = (v) => {
|
|
5804
|
+
if (typeof v !== "string")
|
|
5805
|
+
return void 0;
|
|
5806
|
+
if (renames.has(v))
|
|
5807
|
+
return renames.get(v);
|
|
5808
|
+
if (isId(v))
|
|
5809
|
+
return void 0;
|
|
5810
|
+
const s = slugFactId(v);
|
|
5811
|
+
return known.has(s) ? s : void 0;
|
|
5812
|
+
};
|
|
5813
|
+
const said = (field, from, to) => fixes.push({
|
|
5814
|
+
line: b.line,
|
|
5815
|
+
code: "ref-rewritten",
|
|
5816
|
+
fact: String(o.id),
|
|
5817
|
+
message: `line ${b.line}: ${String(o.id)}'s ${field} ${from} rewritten as ${to}`
|
|
5818
|
+
});
|
|
5819
|
+
for (const k of REF_FIELDS) {
|
|
5820
|
+
const to = ref(o[k]);
|
|
5821
|
+
if (to) {
|
|
5822
|
+
said(k, o[k], to);
|
|
5823
|
+
o[k] = to;
|
|
5824
|
+
}
|
|
5825
|
+
}
|
|
5826
|
+
if (Array.isArray(o.steps))
|
|
5827
|
+
o.steps.forEach((st, i) => {
|
|
5828
|
+
if (!isObj(st))
|
|
5829
|
+
return;
|
|
5830
|
+
const to = ref(st.ref);
|
|
5831
|
+
if (to) {
|
|
5832
|
+
said(`step ${i + 1} ref`, st.ref, to);
|
|
5833
|
+
st.ref = to;
|
|
5834
|
+
}
|
|
5835
|
+
});
|
|
5836
|
+
return { b, o, fixes };
|
|
5837
|
+
});
|
|
5838
|
+
const entries = /* @__PURE__ */ new Map();
|
|
5839
|
+
for (const f of [...onFile, ...facts])
|
|
5840
|
+
if (f.kind === "entry")
|
|
5841
|
+
entries.set(f.id, f);
|
|
5842
|
+
for (const { o } of rewritten)
|
|
5843
|
+
if (o.kind === "entry" && typeof o.id === "string" && isObj(o.cite))
|
|
5844
|
+
entries.set(o.id, o);
|
|
5845
|
+
for (const r of rewritten) {
|
|
5846
|
+
const { o, b } = r;
|
|
5847
|
+
if (o.kind !== "flow" || o.cite !== void 0 || typeof o.entry !== "string")
|
|
5848
|
+
continue;
|
|
5849
|
+
const e = entries.get(o.entry);
|
|
5850
|
+
if (!e || !isObj(e.cite))
|
|
5851
|
+
continue;
|
|
5852
|
+
o.cite = structuredClone(e.cite);
|
|
5853
|
+
const c = e.cite;
|
|
5854
|
+
r.fixes.push({
|
|
5855
|
+
line: b.line,
|
|
5856
|
+
code: "flow-cite-from-entry",
|
|
5857
|
+
fact: String(o.id),
|
|
5858
|
+
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)})`
|
|
5859
|
+
});
|
|
5860
|
+
}
|
|
5861
|
+
const kept = [...facts];
|
|
5862
|
+
const seen = new Set(facts.map((f) => f.id));
|
|
5863
|
+
const fixedLines = /* @__PURE__ */ new Set();
|
|
5864
|
+
for (const { b, o, fixes } of rewritten) {
|
|
5865
|
+
if (!fixes.length || collided.has(b))
|
|
5866
|
+
continue;
|
|
5867
|
+
const p = Fact.safeParse(o);
|
|
5868
|
+
if (!p.success || seen.has(p.data.id))
|
|
5869
|
+
continue;
|
|
5870
|
+
seen.add(p.data.id);
|
|
5871
|
+
kept.push(p.data);
|
|
5872
|
+
fixedLines.add(b);
|
|
5873
|
+
slips.push(...fixes);
|
|
5874
|
+
}
|
|
5875
|
+
const still = bad.filter((b) => !fixedLines.has(b)).map((b) => collided.has(b) ? {
|
|
5876
|
+
...b,
|
|
5877
|
+
issues: [
|
|
5878
|
+
...b.issues,
|
|
5879
|
+
{ line: b.line, code: "id-collision", message: collided.get(b) }
|
|
5880
|
+
]
|
|
5881
|
+
} : b);
|
|
5882
|
+
slips.sort((x, y) => x.line - y.line);
|
|
5883
|
+
return { facts: kept, bad: still, slips };
|
|
5884
|
+
}
|
|
5885
|
+
|
|
5886
|
+
// packages/verify/dist/house-imports.js
|
|
5887
|
+
var CODE2 = /\.(java|kt|kts|scala|groovy|cs|vb|fs|ts|tsx|js|jsx|mjs|cjs|vue|svelte|py|go|rb|php|swift)$/i;
|
|
5888
|
+
var IMPORTS = [
|
|
5889
|
+
// Java, Kotlin, Scala, Groovy, Python's `import a.b`, Go's one-line import
|
|
5890
|
+
/^\s*import\s+(?:static\s+)?([A-Za-z_][\w.]*)/,
|
|
5891
|
+
// C#, F#
|
|
5892
|
+
/^\s*(?:global\s+)?using\s+(?:static\s+)?([A-Za-z_][\w.]*)\s*;/,
|
|
5893
|
+
/^\s*open\s+([A-Za-z_][\w.]*)\s*$/,
|
|
5894
|
+
// Python
|
|
5895
|
+
/^\s*from\s+([A-Za-z_][\w.]*)\s+import\b/,
|
|
5896
|
+
// JavaScript, TypeScript: import ... from "x", export ... from "x", import "x", require("x"), import("x")
|
|
5897
|
+
/\bfrom\s+["']([^"'\s]+)["']/,
|
|
5898
|
+
/^\s*import\s+["']([^"'\s]+)["']/,
|
|
5899
|
+
/\brequire\s*\(\s*["']([^"'\s]+)["']\s*\)/,
|
|
5900
|
+
/\bimport\s*\(\s*["']([^"'\s]+)["']\s*\)/
|
|
5901
|
+
];
|
|
5902
|
+
var GO_IMPORT = /^\s*(?:import\s+)?(?:[\w.]+\s+)?"([\w.-]+(?:\/[\w.-]+)+)"\s*$/;
|
|
5903
|
+
function modulesOf(line, go) {
|
|
5904
|
+
const out = [];
|
|
5905
|
+
for (const re of go ? [...IMPORTS, GO_IMPORT] : IMPORTS) {
|
|
5906
|
+
const m = re.exec(line);
|
|
5907
|
+
if (m?.[1])
|
|
5908
|
+
out.push(m[1]);
|
|
5909
|
+
}
|
|
5910
|
+
return out;
|
|
5911
|
+
}
|
|
5912
|
+
var lower = (s) => s.toLowerCase();
|
|
5913
|
+
var under3 = (module, pkg) => module === pkg || module.startsWith(pkg) && (/[./]$/.test(pkg) || /[./]/.test(module[pkg.length] ?? ""));
|
|
5914
|
+
function packageOf(module, prefix) {
|
|
5915
|
+
let i = prefix.length;
|
|
5916
|
+
while (i < module.length && /[./]/.test(module[i]))
|
|
5917
|
+
i++;
|
|
5918
|
+
const rest = module.slice(i);
|
|
5919
|
+
const part = /^[^./]*/.exec(rest)?.[0] ?? "";
|
|
5920
|
+
return module.slice(0, i + part.length).replace(/[./]+$/, "");
|
|
5921
|
+
}
|
|
5922
|
+
function nameOf2(pkg) {
|
|
5923
|
+
const parts = lower(pkg).split(/[^a-z0-9]+/).filter(Boolean);
|
|
5924
|
+
if (parts.length > 2 && /^(com|org|net|io|dev|co|uk|de|fr|nl|ch)$/.test(parts[0]))
|
|
5925
|
+
parts.shift();
|
|
5926
|
+
return parts.join("-") || "house-framework";
|
|
5927
|
+
}
|
|
5928
|
+
function houseImportsUntaught(source, o) {
|
|
5929
|
+
if (!o.prefixes.length)
|
|
5930
|
+
return [];
|
|
5931
|
+
const rules = o.rules ?? DEFAULT_READ_RULES;
|
|
5932
|
+
const prefixes = o.prefixes.map(lower);
|
|
5933
|
+
const taught = o.taught.map(lower);
|
|
5934
|
+
const found = /* @__PURE__ */ new Map();
|
|
5935
|
+
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();
|
|
5936
|
+
for (const p of paths) {
|
|
5937
|
+
const text = source.read(p);
|
|
5938
|
+
if (text === void 0)
|
|
5939
|
+
continue;
|
|
5940
|
+
const lines = text.split(/\r?\n/);
|
|
5941
|
+
const go = /\.go$/i.test(p);
|
|
5942
|
+
lines.forEach((line, i) => {
|
|
5943
|
+
if (!/\b(import|using|from|require|open)\b/.test(line) && !(go && /^\s*"/.test(line)))
|
|
5944
|
+
return;
|
|
5945
|
+
for (const m of modulesOf(line, go)) {
|
|
5946
|
+
const ml = lower(m);
|
|
5947
|
+
const prefix = prefixes.find((x) => ml.startsWith(x));
|
|
5948
|
+
if (!prefix)
|
|
5949
|
+
continue;
|
|
5950
|
+
if (taught.some((t) => under3(ml, t)))
|
|
5951
|
+
continue;
|
|
5952
|
+
const pkg = packageOf(m, prefix);
|
|
5953
|
+
const f = found.get(pkg) ?? { files: /* @__PURE__ */ new Set(), first: `${p}:${i + 1}` };
|
|
5954
|
+
f.files.add(p);
|
|
5955
|
+
found.set(pkg, f);
|
|
5956
|
+
}
|
|
5957
|
+
});
|
|
5958
|
+
}
|
|
5959
|
+
return [...found.entries()].map(([pkg, f]) => {
|
|
5960
|
+
const name = nameOf2(pkg);
|
|
5961
|
+
return {
|
|
5962
|
+
package: pkg,
|
|
5963
|
+
name,
|
|
5964
|
+
files: f.files.size,
|
|
5965
|
+
first: f.first,
|
|
5966
|
+
command: `atticus frameworks learn ${name} --from <its source, JAR or npm package> --package ${pkg}`
|
|
5967
|
+
};
|
|
5968
|
+
}).sort((a, b) => b.files - a.files || (a.package < b.package ? -1 : a.package > b.package ? 1 : 0));
|
|
5969
|
+
}
|
|
5970
|
+
|
|
4457
5971
|
export {
|
|
4458
5972
|
insideRepo,
|
|
4459
5973
|
dirSource,
|
|
4460
5974
|
correctionFor,
|
|
5975
|
+
outsidePairingFor,
|
|
4461
5976
|
correctedFact,
|
|
4462
5977
|
applyCorrections,
|
|
4463
5978
|
frameworkFacts,
|
|
@@ -4465,20 +5980,28 @@ export {
|
|
|
4465
5980
|
frameworkTopicsOfType,
|
|
4466
5981
|
frameworkGaps,
|
|
4467
5982
|
frameworksToLearn,
|
|
5983
|
+
readRulesOf,
|
|
5984
|
+
lineRangeOf,
|
|
4468
5985
|
parseReviewed,
|
|
4469
|
-
|
|
4470
|
-
staleMessage,
|
|
4471
|
-
verifyFacts,
|
|
5986
|
+
readingRank,
|
|
4472
5987
|
FILE_KINDS,
|
|
5988
|
+
fileKinds,
|
|
4473
5989
|
kindsOf,
|
|
4474
5990
|
numberedSlice,
|
|
4475
5991
|
sliceText,
|
|
4476
5992
|
planBatches,
|
|
5993
|
+
isEntryPointFile,
|
|
5994
|
+
NOT_REACHED_WHY,
|
|
5995
|
+
reachableModels,
|
|
5996
|
+
sameCommit,
|
|
5997
|
+
staleMessage,
|
|
5998
|
+
verifyFacts,
|
|
4477
5999
|
SHOWN_PROPOSALS,
|
|
4478
6000
|
proposeExclusions,
|
|
4479
6001
|
exclusionYaml,
|
|
4480
|
-
|
|
6002
|
+
sessionTokensFor,
|
|
4481
6003
|
planParts,
|
|
6004
|
+
planGaps,
|
|
4482
6005
|
VIEW_NAMES,
|
|
4483
6006
|
ViewError,
|
|
4484
6007
|
changeView,
|
|
@@ -4496,5 +6019,7 @@ export {
|
|
|
4496
6019
|
codeLinkerFor,
|
|
4497
6020
|
renderMap,
|
|
4498
6021
|
rereadPlan,
|
|
4499
|
-
onlyFiles
|
|
6022
|
+
onlyFiles,
|
|
6023
|
+
repairFormSlips,
|
|
6024
|
+
houseImportsUntaught
|
|
4500
6025
|
};
|