@contractkit/plugin-bruno
Advanced tools
| <!doctype html> | ||
| <html lang="en"> | ||
| <head> | ||
| <title>Code coverage report for src/index.ts</title> | ||
| <meta charset="utf-8" /> | ||
| <link rel="stylesheet" href="../prettify.css" /> | ||
| <link rel="stylesheet" href="../base.css" /> | ||
| <link rel="shortcut icon" type="image/x-icon" href="../favicon.png" /> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1" /> | ||
| <style type='text/css'> | ||
| .coverage-summary .sorter { | ||
| background-image: url(../sort-arrow-sprite.png); | ||
| } | ||
| </style> | ||
| </head> | ||
| <body> | ||
| <div class='wrapper'> | ||
| <div class='pad1'> | ||
| <h1><a href="../index.html">All files</a> / <a href="index.html">src</a> index.ts</h1> | ||
| <div class='clearfix'> | ||
| <div class='fl pad1y space-right2'> | ||
| <span class="strong">26.92% </span> | ||
| <span class="quiet">Statements</span> | ||
| <span class='fraction'>14/52</span> | ||
| </div> | ||
| <div class='fl pad1y space-right2'> | ||
| <span class="strong">35.89% </span> | ||
| <span class="quiet">Branches</span> | ||
| <span class='fraction'>14/39</span> | ||
| </div> | ||
| <div class='fl pad1y space-right2'> | ||
| <span class="strong">33.33% </span> | ||
| <span class="quiet">Functions</span> | ||
| <span class='fraction'>2/6</span> | ||
| </div> | ||
| <div class='fl pad1y space-right2'> | ||
| <span class="strong">25% </span> | ||
| <span class="quiet">Lines</span> | ||
| <span class='fraction'>12/48</span> | ||
| </div> | ||
| </div> | ||
| <p class="quiet"> | ||
| Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block. | ||
| </p> | ||
| <template id="filterTemplate"> | ||
| <div class="quiet"> | ||
| Filter: | ||
| <input type="search" id="fileSearch"> | ||
| </div> | ||
| </template> | ||
| </div> | ||
| <div class='status-line low'></div> | ||
| <pre><table class="coverage"> | ||
| <tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a> | ||
| <a name='L2'></a><a href='#L2'>2</a> | ||
| <a name='L3'></a><a href='#L3'>3</a> | ||
| <a name='L4'></a><a href='#L4'>4</a> | ||
| <a name='L5'></a><a href='#L5'>5</a> | ||
| <a name='L6'></a><a href='#L6'>6</a> | ||
| <a name='L7'></a><a href='#L7'>7</a> | ||
| <a name='L8'></a><a href='#L8'>8</a> | ||
| <a name='L9'></a><a href='#L9'>9</a> | ||
| <a name='L10'></a><a href='#L10'>10</a> | ||
| <a name='L11'></a><a href='#L11'>11</a> | ||
| <a name='L12'></a><a href='#L12'>12</a> | ||
| <a name='L13'></a><a href='#L13'>13</a> | ||
| <a name='L14'></a><a href='#L14'>14</a> | ||
| <a name='L15'></a><a href='#L15'>15</a> | ||
| <a name='L16'></a><a href='#L16'>16</a> | ||
| <a name='L17'></a><a href='#L17'>17</a> | ||
| <a name='L18'></a><a href='#L18'>18</a> | ||
| <a name='L19'></a><a href='#L19'>19</a> | ||
| <a name='L20'></a><a href='#L20'>20</a> | ||
| <a name='L21'></a><a href='#L21'>21</a> | ||
| <a name='L22'></a><a href='#L22'>22</a> | ||
| <a name='L23'></a><a href='#L23'>23</a> | ||
| <a name='L24'></a><a href='#L24'>24</a> | ||
| <a name='L25'></a><a href='#L25'>25</a> | ||
| <a name='L26'></a><a href='#L26'>26</a> | ||
| <a name='L27'></a><a href='#L27'>27</a> | ||
| <a name='L28'></a><a href='#L28'>28</a> | ||
| <a name='L29'></a><a href='#L29'>29</a> | ||
| <a name='L30'></a><a href='#L30'>30</a> | ||
| <a name='L31'></a><a href='#L31'>31</a> | ||
| <a name='L32'></a><a href='#L32'>32</a> | ||
| <a name='L33'></a><a href='#L33'>33</a> | ||
| <a name='L34'></a><a href='#L34'>34</a> | ||
| <a name='L35'></a><a href='#L35'>35</a> | ||
| <a name='L36'></a><a href='#L36'>36</a> | ||
| <a name='L37'></a><a href='#L37'>37</a> | ||
| <a name='L38'></a><a href='#L38'>38</a> | ||
| <a name='L39'></a><a href='#L39'>39</a> | ||
| <a name='L40'></a><a href='#L40'>40</a> | ||
| <a name='L41'></a><a href='#L41'>41</a> | ||
| <a name='L42'></a><a href='#L42'>42</a> | ||
| <a name='L43'></a><a href='#L43'>43</a> | ||
| <a name='L44'></a><a href='#L44'>44</a> | ||
| <a name='L45'></a><a href='#L45'>45</a> | ||
| <a name='L46'></a><a href='#L46'>46</a> | ||
| <a name='L47'></a><a href='#L47'>47</a> | ||
| <a name='L48'></a><a href='#L48'>48</a> | ||
| <a name='L49'></a><a href='#L49'>49</a> | ||
| <a name='L50'></a><a href='#L50'>50</a> | ||
| <a name='L51'></a><a href='#L51'>51</a> | ||
| <a name='L52'></a><a href='#L52'>52</a> | ||
| <a name='L53'></a><a href='#L53'>53</a> | ||
| <a name='L54'></a><a href='#L54'>54</a> | ||
| <a name='L55'></a><a href='#L55'>55</a> | ||
| <a name='L56'></a><a href='#L56'>56</a> | ||
| <a name='L57'></a><a href='#L57'>57</a> | ||
| <a name='L58'></a><a href='#L58'>58</a> | ||
| <a name='L59'></a><a href='#L59'>59</a> | ||
| <a name='L60'></a><a href='#L60'>60</a> | ||
| <a name='L61'></a><a href='#L61'>61</a> | ||
| <a name='L62'></a><a href='#L62'>62</a> | ||
| <a name='L63'></a><a href='#L63'>63</a> | ||
| <a name='L64'></a><a href='#L64'>64</a> | ||
| <a name='L65'></a><a href='#L65'>65</a> | ||
| <a name='L66'></a><a href='#L66'>66</a> | ||
| <a name='L67'></a><a href='#L67'>67</a> | ||
| <a name='L68'></a><a href='#L68'>68</a> | ||
| <a name='L69'></a><a href='#L69'>69</a> | ||
| <a name='L70'></a><a href='#L70'>70</a> | ||
| <a name='L71'></a><a href='#L71'>71</a> | ||
| <a name='L72'></a><a href='#L72'>72</a> | ||
| <a name='L73'></a><a href='#L73'>73</a> | ||
| <a name='L74'></a><a href='#L74'>74</a> | ||
| <a name='L75'></a><a href='#L75'>75</a> | ||
| <a name='L76'></a><a href='#L76'>76</a> | ||
| <a name='L77'></a><a href='#L77'>77</a> | ||
| <a name='L78'></a><a href='#L78'>78</a> | ||
| <a name='L79'></a><a href='#L79'>79</a> | ||
| <a name='L80'></a><a href='#L80'>80</a> | ||
| <a name='L81'></a><a href='#L81'>81</a> | ||
| <a name='L82'></a><a href='#L82'>82</a> | ||
| <a name='L83'></a><a href='#L83'>83</a> | ||
| <a name='L84'></a><a href='#L84'>84</a> | ||
| <a name='L85'></a><a href='#L85'>85</a> | ||
| <a name='L86'></a><a href='#L86'>86</a> | ||
| <a name='L87'></a><a href='#L87'>87</a> | ||
| <a name='L88'></a><a href='#L88'>88</a> | ||
| <a name='L89'></a><a href='#L89'>89</a> | ||
| <a name='L90'></a><a href='#L90'>90</a> | ||
| <a name='L91'></a><a href='#L91'>91</a> | ||
| <a name='L92'></a><a href='#L92'>92</a> | ||
| <a name='L93'></a><a href='#L93'>93</a> | ||
| <a name='L94'></a><a href='#L94'>94</a> | ||
| <a name='L95'></a><a href='#L95'>95</a> | ||
| <a name='L96'></a><a href='#L96'>96</a> | ||
| <a name='L97'></a><a href='#L97'>97</a> | ||
| <a name='L98'></a><a href='#L98'>98</a> | ||
| <a name='L99'></a><a href='#L99'>99</a> | ||
| <a name='L100'></a><a href='#L100'>100</a> | ||
| <a name='L101'></a><a href='#L101'>101</a> | ||
| <a name='L102'></a><a href='#L102'>102</a> | ||
| <a name='L103'></a><a href='#L103'>103</a> | ||
| <a name='L104'></a><a href='#L104'>104</a> | ||
| <a name='L105'></a><a href='#L105'>105</a> | ||
| <a name='L106'></a><a href='#L106'>106</a> | ||
| <a name='L107'></a><a href='#L107'>107</a> | ||
| <a name='L108'></a><a href='#L108'>108</a> | ||
| <a name='L109'></a><a href='#L109'>109</a> | ||
| <a name='L110'></a><a href='#L110'>110</a> | ||
| <a name='L111'></a><a href='#L111'>111</a> | ||
| <a name='L112'></a><a href='#L112'>112</a> | ||
| <a name='L113'></a><a href='#L113'>113</a> | ||
| <a name='L114'></a><a href='#L114'>114</a> | ||
| <a name='L115'></a><a href='#L115'>115</a> | ||
| <a name='L116'></a><a href='#L116'>116</a> | ||
| <a name='L117'></a><a href='#L117'>117</a> | ||
| <a name='L118'></a><a href='#L118'>118</a> | ||
| <a name='L119'></a><a href='#L119'>119</a> | ||
| <a name='L120'></a><a href='#L120'>120</a> | ||
| <a name='L121'></a><a href='#L121'>121</a> | ||
| <a name='L122'></a><a href='#L122'>122</a> | ||
| <a name='L123'></a><a href='#L123'>123</a> | ||
| <a name='L124'></a><a href='#L124'>124</a> | ||
| <a name='L125'></a><a href='#L125'>125</a> | ||
| <a name='L126'></a><a href='#L126'>126</a> | ||
| <a name='L127'></a><a href='#L127'>127</a> | ||
| <a name='L128'></a><a href='#L128'>128</a> | ||
| <a name='L129'></a><a href='#L129'>129</a> | ||
| <a name='L130'></a><a href='#L130'>130</a> | ||
| <a name='L131'></a><a href='#L131'>131</a> | ||
| <a name='L132'></a><a href='#L132'>132</a> | ||
| <a name='L133'></a><a href='#L133'>133</a> | ||
| <a name='L134'></a><a href='#L134'>134</a> | ||
| <a name='L135'></a><a href='#L135'>135</a> | ||
| <a name='L136'></a><a href='#L136'>136</a> | ||
| <a name='L137'></a><a href='#L137'>137</a> | ||
| <a name='L138'></a><a href='#L138'>138</a> | ||
| <a name='L139'></a><a href='#L139'>139</a> | ||
| <a name='L140'></a><a href='#L140'>140</a> | ||
| <a name='L141'></a><a href='#L141'>141</a> | ||
| <a name='L142'></a><a href='#L142'>142</a> | ||
| <a name='L143'></a><a href='#L143'>143</a> | ||
| <a name='L144'></a><a href='#L144'>144</a> | ||
| <a name='L145'></a><a href='#L145'>145</a> | ||
| <a name='L146'></a><a href='#L146'>146</a> | ||
| <a name='L147'></a><a href='#L147'>147</a> | ||
| <a name='L148'></a><a href='#L148'>148</a> | ||
| <a name='L149'></a><a href='#L149'>149</a> | ||
| <a name='L150'></a><a href='#L150'>150</a> | ||
| <a name='L151'></a><a href='#L151'>151</a> | ||
| <a name='L152'></a><a href='#L152'>152</a> | ||
| <a name='L153'></a><a href='#L153'>153</a> | ||
| <a name='L154'></a><a href='#L154'>154</a> | ||
| <a name='L155'></a><a href='#L155'>155</a> | ||
| <a name='L156'></a><a href='#L156'>156</a> | ||
| <a name='L157'></a><a href='#L157'>157</a> | ||
| <a name='L158'></a><a href='#L158'>158</a> | ||
| <a name='L159'></a><a href='#L159'>159</a> | ||
| <a name='L160'></a><a href='#L160'>160</a> | ||
| <a name='L161'></a><a href='#L161'>161</a> | ||
| <a name='L162'></a><a href='#L162'>162</a> | ||
| <a name='L163'></a><a href='#L163'>163</a> | ||
| <a name='L164'></a><a href='#L164'>164</a> | ||
| <a name='L165'></a><a href='#L165'>165</a> | ||
| <a name='L166'></a><a href='#L166'>166</a> | ||
| <a name='L167'></a><a href='#L167'>167</a> | ||
| <a name='L168'></a><a href='#L168'>168</a> | ||
| <a name='L169'></a><a href='#L169'>169</a> | ||
| <a name='L170'></a><a href='#L170'>170</a> | ||
| <a name='L171'></a><a href='#L171'>171</a> | ||
| <a name='L172'></a><a href='#L172'>172</a> | ||
| <a name='L173'></a><a href='#L173'>173</a> | ||
| <a name='L174'></a><a href='#L174'>174</a> | ||
| <a name='L175'></a><a href='#L175'>175</a> | ||
| <a name='L176'></a><a href='#L176'>176</a> | ||
| <a name='L177'></a><a href='#L177'>177</a> | ||
| <a name='L178'></a><a href='#L178'>178</a> | ||
| <a name='L179'></a><a href='#L179'>179</a> | ||
| <a name='L180'></a><a href='#L180'>180</a> | ||
| <a name='L181'></a><a href='#L181'>181</a> | ||
| <a name='L182'></a><a href='#L182'>182</a> | ||
| <a name='L183'></a><a href='#L183'>183</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-yes">6x</span> | ||
| <span class="cline-any cline-yes">2x</span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-yes">4x</span> | ||
| <span class="cline-any cline-yes">4x</span> | ||
| <span class="cline-any cline-yes">4x</span> | ||
| <span class="cline-any cline-yes">3x</span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-yes">1x</span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-yes">4x</span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-yes">3x</span> | ||
| <span class="cline-any cline-yes">3x</span> | ||
| <span class="cline-any cline-yes">2x</span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-yes">1x</span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-no"> </span> | ||
| <span class="cline-any cline-no"> </span> | ||
| <span class="cline-any cline-no"> </span> | ||
| <span class="cline-any cline-no"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-no"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-no"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-no"> </span> | ||
| <span class="cline-any cline-no"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-no"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-no"> </span> | ||
| <span class="cline-any cline-no"> </span> | ||
| <span class="cline-any cline-no"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-no"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-no"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-no"> </span> | ||
| <span class="cline-any cline-no"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-no"> </span> | ||
| <span class="cline-any cline-no"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-no"> </span> | ||
| <span class="cline-any cline-no"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-no"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-no"> </span> | ||
| <span class="cline-any cline-no"> </span> | ||
| <span class="cline-any cline-no"> </span> | ||
| <span class="cline-any cline-no"> </span> | ||
| <span class="cline-any cline-no"> </span> | ||
| <span class="cline-any cline-no"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-no"> </span> | ||
| <span class="cline-any cline-no"> </span> | ||
| <span class="cline-any cline-no"> </span> | ||
| <span class="cline-any cline-no"> </span> | ||
| <span class="cline-any cline-no"> </span> | ||
| <span class="cline-any cline-no"> </span> | ||
| <span class="cline-any cline-no"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-no"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-no"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span> | ||
| <span class="cline-any cline-neutral"> </span></td><td class="text"><pre class="prettyprint lang-js">import { resolve, basename, dirname } from 'node:path'; | ||
| import { existsSync, readFileSync, rmSync, readdirSync, rmdirSync } from 'node:fs'; | ||
| import { generateOpenCollection, MANIFEST_FILENAME, parseManifest } from './codegen-bruno.js'; | ||
| import type { BrunoSecurityScheme } from './codegen-bruno.js'; | ||
| import type { ContractKitPlugin, PluginValue } from '@contractkit/core'; | ||
| | ||
| /** Configuration accepted by the Bruno plugin, both via `contractkit.config.json` and `createBrunoPlugin`. */ | ||
| export interface BrunoPluginConfig { | ||
| baseDir?: string; | ||
| output?: string; | ||
| collectionName?: string; | ||
| /** | ||
| * When true (default), example values use Bruno's faker templates | ||
| * (`{{$randomUUID}}`, `{{$randomEmail}}`, etc.) so each send produces | ||
| * fresh data. Set to false for deterministic placeholders. | ||
| */ | ||
| randomExamples?: boolean; | ||
| /** | ||
| * Whether to generate request files for operations marked `internal`. Defaults to | ||
| * `true` — Bruno collections are typically used by the team that owns the API and | ||
| * benefit from full coverage. Set to `false` to omit internal ops. | ||
| */ | ||
| includeInternal?: boolean; | ||
| /** | ||
| * Map of environment name → variables. Each entry produces a | ||
| * `environments/<name>.yml` file. When omitted, a default `local.yml` is | ||
| * emitted with `baseUrl=http://localhost:3000` and any auth env-var | ||
| * placeholders. When provided, the default is replaced entirely; include | ||
| * auth variables (e.g. `token`) explicitly if you need them. | ||
| */ | ||
| environments?: Record<string, Record<string, unknown>>; | ||
| } | ||
| | ||
| /** Full plugin options shape read from `ctx.options` — extends {@link BrunoPluginConfig} with the `auth` block. */ | ||
| export interface BrunoPluginOptions extends BrunoPluginConfig { | ||
| auth?: { defaultScheme: string; schemes?: Record<string, BrunoSecurityScheme> }; | ||
| } | ||
| | ||
| // ─── Default export: loaded via plugins array, reads config from ctx.options ─ | ||
| | ||
| /** | ||
| * Validates a `plugins.bruno` extension entry on an operation. The expected shape is | ||
| * `{ template?: string }`, where `template` is a YAML fragment to deep-merge into the | ||
| * generated request file (typically a `file://...` URL whose contents have already | ||
| * been loaded by the CLI resolver). | ||
| */ | ||
| export function validateBrunoExtension(value: PluginValue): { errors?: string[] } | void { | ||
| if (value === null || typeof value !== 'object' || Array.isArray(value)) { | ||
| return { errors: [`expected an object, got ${describe(value)}`] }; | ||
| } | ||
| const errors: string[] = []; | ||
| for (const [key, val] of Object.entries(value)) { | ||
| if (key === 'template') { | ||
| if (typeof val !== 'string') errors.push(`'template' must be a string, got ${describe(val)}`); | ||
| } else { | ||
| errors.push(`unknown field '${key}' (allowed: template)`); | ||
| } | ||
| } | ||
| return errors.length ? { errors } : undefined; | ||
| } | ||
| | ||
| function describe(value: PluginValue): string { | ||
| <span class="missing-if-branch" title="if path not taken" >I</span>if (value === null) <span class="cstat-no" title="statement not covered" >return 'null';</span> | ||
| if (Array.isArray(value)) return 'array'; | ||
| return typeof value; | ||
| } | ||
| | ||
| const plugin: ContractKitPlugin = { | ||
| name: 'bruno', | ||
| cacheKey: 'bruno', | ||
| validateExtension: validateBrunoExtension, | ||
| async <span class="fstat-no" title="function not covered" >generateTargets({</span> opRoots, contractRoots }, ctx) { | ||
| const { auth, ...config } = <span class="cstat-no" title="statement not covered" >ctx.options </span>as BrunoPluginOptions; | ||
| const base = <span class="cstat-no" title="statement not covered" >config.baseDir ? resolve(ctx.rootDir, config.baseDir) : ctx.rootDir;</span> | ||
| const <span class="cstat-no" title="statement not covered" >outDir = resolve(base, config.output ?? 'bruno-collection');</span> | ||
| const collectionName = <span class="cstat-no" title="statement not covered" >config.collectionName ?? basename(ctx.rootDir);</span> | ||
| | ||
| <span class="cstat-no" title="statement not covered" > cleanupTrackedFiles(outDir);</span> | ||
| | ||
| const <span class="cstat-no" title="statement not covered" >files = generateOpenCollection(opRoots, {</span> | ||
| collectionName, | ||
| contractRoots, | ||
| auth, | ||
| randomExamples: config.randomExamples ?? true, | ||
| includeInternal: config.includeInternal, | ||
| environments: config.environments, | ||
| }); | ||
| <span class="cstat-no" title="statement not covered" > for (const { relativePath, content } of files) {</span> | ||
| <span class="cstat-no" title="statement not covered" > ctx.emitFile(resolve(outDir, relativePath), content);</span> | ||
| } | ||
| }, | ||
| }; | ||
| | ||
| export default plugin; | ||
| | ||
| // ─── Factory: for programmatic use with explicit config ──────────────────── | ||
| | ||
| /** | ||
| * Creates a Bruno plugin instance with explicit configuration, for programmatic use. | ||
| * | ||
| * Prefer the default export when loading via `contractkit.config.json`. Use this | ||
| * factory when constructing the plugin in code (e.g. in tests or custom build scripts). | ||
| * | ||
| * @param config - Plugin configuration (output paths and feature flags). | ||
| * @param rootDir - Absolute path used to resolve relative paths in `config`. | ||
| * @param auth - Optional auth scheme configuration mirroring the `auth` key in plugin options. | ||
| */ | ||
| export function <span class="fstat-no" title="function not covered" >createBrunoPlugin(</span> | ||
| config: BrunoPluginConfig, | ||
| rootDir: string, | ||
| auth?: { defaultScheme: string; schemes?: Record<string, BrunoSecurityScheme> }, | ||
| ): ContractKitPlugin { | ||
| <span class="cstat-no" title="statement not covered" > return {</span> | ||
| name: 'bruno', | ||
| cacheKey: `bruno:${JSON.stringify(config)}`, | ||
| validateExtension: validateBrunoExtension, | ||
| async <span class="fstat-no" title="function not covered" >generateTargets({</span> opRoots, contractRoots }, ctx) { | ||
| const base = <span class="cstat-no" title="statement not covered" >config.baseDir ? resolve(rootDir, config.baseDir) : rootDir;</span> | ||
| const <span class="cstat-no" title="statement not covered" >outDir = resolve(base, config.output ?? 'bruno-collection');</span> | ||
| const collectionName = <span class="cstat-no" title="statement not covered" >config.collectionName ?? basename(rootDir);</span> | ||
| | ||
| <span class="cstat-no" title="statement not covered" > cleanupTrackedFiles(outDir);</span> | ||
| | ||
| const <span class="cstat-no" title="statement not covered" >files = generateOpenCollection(opRoots, {</span> | ||
| collectionName, | ||
| contractRoots, | ||
| auth, | ||
| randomExamples: config.randomExamples ?? true, | ||
| includeInternal: config.includeInternal, | ||
| environments: config.environments, | ||
| }); | ||
| <span class="cstat-no" title="statement not covered" > for (const { relativePath, content } of files) {</span> | ||
| <span class="cstat-no" title="statement not covered" > ctx.emitFile(resolve(outDir, relativePath), content);</span> | ||
| } | ||
| }, | ||
| }; | ||
| } | ||
| | ||
| /** | ||
| * Delete files this plugin generated on the previous run, leaving anything | ||
| * the user added (custom .bru files, scripts, secrets, etc.) untouched. | ||
| * | ||
| * On first run — or after manual deletion of the manifest — nothing is | ||
| * removed; stale files from prior versions linger until manually cleaned. | ||
| */ | ||
| function <span class="fstat-no" title="function not covered" >cleanupTrackedFiles(o</span>utDir: string): void { | ||
| const <span class="cstat-no" title="statement not covered" >manifestPath = resolve(outDir, MANIFEST_FILENAME);</span> | ||
| <span class="cstat-no" title="statement not covered" > if (!existsSync(manifestPath)) <span class="cstat-no" title="statement not covered" >return;</span></span> | ||
| | ||
| let tracked: string[]; | ||
| <span class="cstat-no" title="statement not covered" > try {</span> | ||
| <span class="cstat-no" title="statement not covered" > tracked = parseManifest(readFileSync(manifestPath, 'utf-8'));</span> | ||
| } catch { | ||
| <span class="cstat-no" title="statement not covered" > return;</span> | ||
| } | ||
| | ||
| const removedDirs = <span class="cstat-no" title="statement not covered" >new Set<string>();</span> | ||
| <span class="cstat-no" title="statement not covered" > for (const rel of tracked) {</span> | ||
| const <span class="cstat-no" title="statement not covered" >abs = resolve(outDir, rel);</span> | ||
| <span class="cstat-no" title="statement not covered" > if (existsSync(abs)) {</span> | ||
| <span class="cstat-no" title="statement not covered" > rmSync(abs, { force: true });</span> | ||
| <span class="cstat-no" title="statement not covered" > removedDirs.add(dirname(abs));</span> | ||
| } | ||
| } | ||
| | ||
| // Walk up from each affected directory and remove it if empty, stopping at outDir. | ||
| <span class="cstat-no" title="statement not covered" > for (const dir of removedDirs) {</span> | ||
| let current = <span class="cstat-no" title="statement not covered" >dir;</span> | ||
| <span class="cstat-no" title="statement not covered" > while (current.startsWith(outDir) && current !== outDir) {</span> | ||
| <span class="cstat-no" title="statement not covered" > try {</span> | ||
| <span class="cstat-no" title="statement not covered" > if (readdirSync(current).length === 0) {</span> | ||
| <span class="cstat-no" title="statement not covered" > rmdirSync(current);</span> | ||
| <span class="cstat-no" title="statement not covered" > current = dirname(current);</span> | ||
| } else { | ||
| <span class="cstat-no" title="statement not covered" > break;</span> | ||
| } | ||
| } catch { | ||
| <span class="cstat-no" title="statement not covered" > break;</span> | ||
| } | ||
| } | ||
| } | ||
| } | ||
| </pre></td></tr></table></pre> | ||
| <div class='push'></div><!-- for sticky footer --> | ||
| </div><!-- /wrapper --> | ||
| <div class='footer quiet pad2 space-top1 center small'> | ||
| Code coverage generated by | ||
| <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a> | ||
| at 2026-05-06T12:37:12.770Z | ||
| </div> | ||
| <script src="../prettify.js"></script> | ||
| <script> | ||
| window.onload = function () { | ||
| prettyPrint(); | ||
| }; | ||
| </script> | ||
| <script src="../sorter.js"></script> | ||
| <script src="../block-navigation.js"></script> | ||
| </body> | ||
| </html> | ||
| > @contractkit/plugin-bruno@1.2.0 build:ci /home/runner/work/ContractKit/ContractKit/packages/plugin-bruno | ||
| > @contractkit/plugin-bruno@1.3.0 build:ci /home/runner/work/ContractKit/ContractKit/packages/plugin-bruno | ||
| > eslint --max-warnings=0 && pnpm run build | ||
| > @contractkit/plugin-bruno@1.2.0 build /home/runner/work/ContractKit/ContractKit/packages/plugin-bruno | ||
| > @contractkit/plugin-bruno@1.3.0 build /home/runner/work/ContractKit/ContractKit/packages/plugin-bruno | ||
| > tsup src/index.ts --format esm --sourcemap --dts && tsc --emitDeclarationOnly --declaration | ||
@@ -14,7 +14,7 @@ | ||
| [34mESM[39m Build start | ||
| [32mESM[39m [1mdist/index.js [22m[32m22.42 KB[39m | ||
| [32mESM[39m [1mdist/index.js.map [22m[32m54.71 KB[39m | ||
| [32mESM[39m ⚡️ Build success in 130ms | ||
| [32mESM[39m [1mdist/index.js [22m[32m23.43 KB[39m | ||
| [32mESM[39m [1mdist/index.js.map [22m[32m56.92 KB[39m | ||
| [32mESM[39m ⚡️ Build success in 174ms | ||
| [34mDTS[39m Build start | ||
| [32mDTS[39m ⚡️ Build success in 3824ms | ||
| [32mDTS[39m [1mdist/index.d.ts [22m[32m2.52 KB[39m | ||
| [32mDTS[39m ⚡️ Build success in 3925ms | ||
| [32mDTS[39m [1mdist/index.d.ts [22m[32m2.95 KB[39m |
| > @contractkit/plugin-bruno@1.2.0 test:ci /home/runner/work/ContractKit/ContractKit/packages/plugin-bruno | ||
| > @contractkit/plugin-bruno@1.3.0 test:ci /home/runner/work/ContractKit/ContractKit/packages/plugin-bruno | ||
| > vitest run --coverage | ||
@@ -9,8 +9,8 @@ | ||
| [32m✓[39m tests/codegen-bruno.test.ts [2m([22m[2m102 tests[22m[2m)[22m[33m 424[2mms[22m[39m | ||
| [32m✓[39m tests/codegen-bruno.test.ts [2m([22m[2m108 tests[22m[2m)[22m[32m 207[2mms[22m[39m | ||
| [2m Test Files [22m [1m[32m1 passed[39m[22m[90m (1)[39m | ||
| [2m Tests [22m [1m[32m102 passed[39m[22m[90m (102)[39m | ||
| [2m Start at [22m 19:55:22 | ||
| [2m Duration [22m 5.19s[2m (transform 1.36s, setup 0ms, import 3.56s, tests 424ms, environment 0ms)[22m | ||
| [2m Tests [22m [1m[32m108 passed[39m[22m[90m (108)[39m | ||
| [2m Start at [22m 12:37:07 | ||
| [2m Duration [22m 5.13s[2m (transform 1.70s, setup 0ms, import 3.83s, tests 207ms, environment 0ms)[22m | ||
@@ -21,7 +21,8 @@ [34m % [39m[2mCoverage report from [22m[33mv8[39m | ||
| -------------------|---------|----------|---------|---------|---------------------------------------------------------------------------------------------------- | ||
| All files | 85.82 | 76.41 | 86.84 | 87.42 | | ||
| src | 87.27 | 76.29 | 92.15 | 88.54 | | ||
| codegen-bruno.ts | 87.27 | 76.29 | 92.15 | 88.54 | 98,334-335,419,427,480-483,490-497,517,523,527-529,545,555,579,589-591,597-599,604,608-616,625,629 | ||
| All files | 78.68 | 72.17 | 82.92 | 79.46 | | ||
| src | 79.11 | 71.65 | 85.96 | 79.52 | | ||
| codegen-bruno.ts | 87.31 | 76.72 | 92.15 | 88.58 | 98,337-338,422,430,483-486,493-500,520,526,530-532,548,558,582,592-594,600-602,607,611-619,628,632 | ||
| index.ts | 26.92 | 35.89 | 33.33 | 25 | 73-178 | ||
| tests | 75 | 77.41 | 76 | 78.94 | | ||
| helpers.ts | 75 | 77.41 | 76 | 78.94 | 43-47,55-59,71,113,133,145 | ||
| -------------------|---------|----------|---------|---------|---------------------------------------------------------------------------------------------------- |
+15
-0
| # @contractkit/contractkit-plugin-bruno | ||
| ## 1.3.0 | ||
| ### Minor Changes | ||
| - a9e9ec0: Replace per-operation `pluginFiles` with structured `pluginExtensions`. The `plugins:` block on an operation now accepts JSON-like values (string, number, boolean, null, object, array) so each plugin owns its own schema for its entry. `file://` URLs in any string position are resolved relative to the `.ck` source file before plugins run, and `http://` / `https://` URLs are fetched via GET. `op.pluginExtensions` carries the resolved tree; the raw form lives at `op.plugins`. The Bruno plugin now expects `{ template: "file://..." }` (was a bare path string) and ships a `validateBrunoExtension` hook that fails compilation on unknown fields or non-string `template`. | ||
| Plugins can now implement `validateExtension(value)` on the `ContractKitPlugin` interface to surface compilation-time errors/warnings on their entry. | ||
| All CLI caching is unified under `<rootDir>/.contractkit/cache/` via a new `CacheService` class: `build.json` for file/plugin hashes and `http/<sha256(url)>` for fetched HTTP response bodies. The `cache: string` config field is reinterpreted as a custom cache **directory** (was a custom build-cache filename); previous file paths under `.contractkit-cache` and `.contractkit-http-cache/` are abandoned. Add `.contractkit/` to `.gitignore`. | ||
| ### Patch Changes | ||
| - Updated dependencies [a9e9ec0] | ||
| - @contractkit/core@0.14.0 | ||
| ## 1.2.0 | ||
@@ -4,0 +19,0 @@ |
+294
-242
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <coverage generated="1777665328025" clover="3.2.0"> | ||
| <project timestamp="1777665328025" name="All files"> | ||
| <metrics statements="326" coveredstatements="285" conditionals="301" coveredconditionals="230" methods="76" coveredmethods="66" elements="703" coveredelements="581" complexity="0" loc="326" ncloc="326" packages="2" files="2" classes="2"/> | ||
| <coverage generated="1778071032799" clover="3.2.0"> | ||
| <project timestamp="1778071032799" name="All files"> | ||
| <metrics statements="375" coveredstatements="298" conditionals="345" coveredconditionals="249" methods="82" coveredmethods="68" elements="802" coveredelements="615" complexity="0" loc="375" ncloc="375" packages="2" files="3" classes="3"/> | ||
| <package name="src"> | ||
| <metrics statements="288" coveredstatements="255" conditionals="270" coveredconditionals="206" methods="51" coveredmethods="47"/> | ||
| <metrics statements="337" coveredstatements="268" conditionals="314" coveredconditionals="225" methods="57" coveredmethods="49"/> | ||
| <file name="codegen-bruno.ts" path="/home/runner/work/ContractKit/ContractKit/packages/plugin-bruno/src/codegen-bruno.ts"> | ||
| <metrics statements="288" coveredstatements="255" conditionals="270" coveredconditionals="206" methods="51" coveredmethods="47"/> | ||
| <metrics statements="289" coveredstatements="256" conditionals="275" coveredconditionals="211" methods="51" coveredmethods="47"/> | ||
| <line num="23" count="1" type="stmt"/> | ||
@@ -47,51 +47,49 @@ <line num="77" count="93" type="stmt"/> | ||
| <line num="131" count="101" type="stmt"/> | ||
| <line num="132" count="101" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="133" count="5" type="stmt"/> | ||
| <line num="135" count="101" type="stmt"/> | ||
| <line num="136" count="101" type="stmt"/> | ||
| <line num="140" count="387" type="stmt"/> | ||
| <line num="141" count="95" type="stmt"/> | ||
| <line num="146" count="95" type="stmt"/> | ||
| <line num="151" count="5" type="stmt"/> | ||
| <line num="152" count="5" type="stmt"/> | ||
| <line num="153" count="7" type="cond" truecount="4" falsecount="0"/> | ||
| <line num="154" count="1" type="stmt"/> | ||
| <line num="159" count="4" type="stmt"/> | ||
| <line num="165" count="20" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="173" count="13" type="stmt"/> | ||
| <line num="174" count="13" type="stmt"/> | ||
| <line num="175" count="13" type="stmt"/> | ||
| <line num="132" count="101" type="cond" truecount="5" falsecount="0"/> | ||
| <line num="135" count="101" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="136" count="5" type="stmt"/> | ||
| <line num="138" count="101" type="stmt"/> | ||
| <line num="139" count="101" type="stmt"/> | ||
| <line num="143" count="387" type="stmt"/> | ||
| <line num="144" count="95" type="stmt"/> | ||
| <line num="149" count="95" type="stmt"/> | ||
| <line num="154" count="5" type="stmt"/> | ||
| <line num="155" count="5" type="stmt"/> | ||
| <line num="156" count="7" type="cond" truecount="4" falsecount="0"/> | ||
| <line num="157" count="1" type="stmt"/> | ||
| <line num="162" count="4" type="stmt"/> | ||
| <line num="168" count="20" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="176" count="13" type="stmt"/> | ||
| <line num="177" count="13" type="stmt"/> | ||
| <line num="179" count="7" type="stmt"/> | ||
| <line num="194" count="10" type="stmt"/> | ||
| <line num="195" count="10" type="cond" truecount="5" falsecount="0"/> | ||
| <line num="196" count="3" type="stmt"/> | ||
| <line num="198" count="7" type="stmt"/> | ||
| <line num="199" count="7" type="stmt"/> | ||
| <line num="205" count="93" type="stmt"/> | ||
| <line num="206" count="93" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="207" count="7" type="stmt"/> | ||
| <line num="208" count="7" type="stmt"/> | ||
| <line num="209" count="7" type="stmt"/> | ||
| <line num="211" count="93" type="stmt"/> | ||
| <line num="212" count="93" type="stmt"/> | ||
| <line num="219" count="93" type="cond" truecount="4" falsecount="0"/> | ||
| <line num="220" count="4" type="stmt"/> | ||
| <line num="225" count="90" type="stmt"/> | ||
| <line num="226" count="90" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="227" count="7" type="stmt"/> | ||
| <line num="229" count="90" type="stmt"/> | ||
| <line num="233" count="94" type="stmt"/> | ||
| <line num="234" count="94" type="stmt"/> | ||
| <line num="235" count="104" type="stmt"/> | ||
| <line num="236" count="104" type="stmt"/> | ||
| <line num="237" count="104" type="stmt"/> | ||
| <line num="239" count="94" type="stmt"/> | ||
| <line num="240" count="94" type="stmt"/> | ||
| <line num="244" count="4" type="stmt"/> | ||
| <line num="248" count="99" type="stmt"/> | ||
| <line num="261" count="101" type="stmt"/> | ||
| <line num="263" count="101" type="stmt"/> | ||
| <line num="178" count="13" type="stmt"/> | ||
| <line num="180" count="13" type="stmt"/> | ||
| <line num="182" count="7" type="stmt"/> | ||
| <line num="197" count="10" type="stmt"/> | ||
| <line num="198" count="10" type="cond" truecount="5" falsecount="0"/> | ||
| <line num="199" count="3" type="stmt"/> | ||
| <line num="201" count="7" type="stmt"/> | ||
| <line num="202" count="7" type="stmt"/> | ||
| <line num="208" count="93" type="stmt"/> | ||
| <line num="209" count="93" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="210" count="7" type="stmt"/> | ||
| <line num="211" count="7" type="stmt"/> | ||
| <line num="212" count="7" type="stmt"/> | ||
| <line num="214" count="93" type="stmt"/> | ||
| <line num="215" count="93" type="stmt"/> | ||
| <line num="222" count="93" type="cond" truecount="4" falsecount="0"/> | ||
| <line num="223" count="4" type="stmt"/> | ||
| <line num="228" count="90" type="stmt"/> | ||
| <line num="229" count="90" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="230" count="7" type="stmt"/> | ||
| <line num="232" count="90" type="stmt"/> | ||
| <line num="236" count="94" type="stmt"/> | ||
| <line num="237" count="94" type="stmt"/> | ||
| <line num="238" count="104" type="stmt"/> | ||
| <line num="239" count="104" type="stmt"/> | ||
| <line num="240" count="104" type="stmt"/> | ||
| <line num="242" count="94" type="stmt"/> | ||
| <line num="243" count="94" type="stmt"/> | ||
| <line num="247" count="4" type="stmt"/> | ||
| <line num="251" count="99" type="stmt"/> | ||
| <line num="264" count="101" type="stmt"/> | ||
| <line num="265" count="101" type="stmt"/> | ||
| <line num="266" count="101" type="stmt"/> | ||
@@ -102,199 +100,253 @@ <line num="267" count="101" type="stmt"/> | ||
| <line num="270" count="101" type="stmt"/> | ||
| <line num="274" count="101" type="stmt"/> | ||
| <line num="280" count="101" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="281" count="15" type="stmt"/> | ||
| <line num="283" count="101" type="stmt"/> | ||
| <line num="285" count="101" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="286" count="20" type="stmt"/> | ||
| <line num="287" count="20" type="stmt"/> | ||
| <line num="288" count="29" type="stmt"/> | ||
| <line num="289" count="29" type="stmt"/> | ||
| <line num="290" count="29" type="stmt"/> | ||
| <line num="291" count="29" type="cond" truecount="4" falsecount="0"/> | ||
| <line num="296" count="101" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="297" count="4" type="stmt"/> | ||
| <line num="298" count="4" type="cond" truecount="1" falsecount="1"/> | ||
| <line num="299" count="4" type="stmt"/> | ||
| <line num="271" count="101" type="stmt"/> | ||
| <line num="272" count="101" type="stmt"/> | ||
| <line num="273" count="101" type="stmt"/> | ||
| <line num="277" count="101" type="stmt"/> | ||
| <line num="283" count="101" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="284" count="15" type="stmt"/> | ||
| <line num="286" count="101" type="stmt"/> | ||
| <line num="288" count="101" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="289" count="20" type="stmt"/> | ||
| <line num="290" count="20" type="stmt"/> | ||
| <line num="291" count="29" type="stmt"/> | ||
| <line num="292" count="29" type="stmt"/> | ||
| <line num="293" count="29" type="stmt"/> | ||
| <line num="294" count="29" type="cond" truecount="4" falsecount="0"/> | ||
| <line num="299" count="101" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="300" count="4" type="stmt"/> | ||
| <line num="301" count="6" type="stmt"/> | ||
| <line num="302" count="6" type="stmt"/> | ||
| <line num="303" count="6" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="309" count="101" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="310" count="7" type="cond" truecount="1" falsecount="3"/> | ||
| <line num="311" count="7" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="312" count="1" type="stmt"/> | ||
| <line num="313" count="1" type="stmt"/> | ||
| <line num="315" count="6" type="stmt"/> | ||
| <line num="320" count="101" type="cond" truecount="4" falsecount="0"/> | ||
| <line num="321" count="19" type="stmt"/> | ||
| <line num="327" count="57" type="cond" truecount="1" falsecount="1"/> | ||
| <line num="329" count="19" type="stmt"/> | ||
| <line num="330" count="19" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="331" count="1" type="stmt"/> | ||
| <line num="332" count="1" type="stmt"/> | ||
| <line num="333" count="18" type="cond" truecount="1" falsecount="1"/> | ||
| <line num="334" count="0" type="stmt"/> | ||
| <line num="335" count="0" type="stmt"/> | ||
| <line num="337" count="18" type="stmt"/> | ||
| <line num="338" count="18" type="stmt"/> | ||
| <line num="339" count="18" type="stmt"/> | ||
| <line num="301" count="4" type="cond" truecount="1" falsecount="1"/> | ||
| <line num="302" count="4" type="stmt"/> | ||
| <line num="303" count="4" type="stmt"/> | ||
| <line num="304" count="6" type="stmt"/> | ||
| <line num="305" count="6" type="stmt"/> | ||
| <line num="306" count="6" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="312" count="101" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="313" count="7" type="cond" truecount="1" falsecount="3"/> | ||
| <line num="314" count="7" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="315" count="1" type="stmt"/> | ||
| <line num="316" count="1" type="stmt"/> | ||
| <line num="318" count="6" type="stmt"/> | ||
| <line num="323" count="101" type="cond" truecount="4" falsecount="0"/> | ||
| <line num="324" count="19" type="stmt"/> | ||
| <line num="330" count="57" type="cond" truecount="1" falsecount="1"/> | ||
| <line num="332" count="19" type="stmt"/> | ||
| <line num="333" count="19" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="334" count="1" type="stmt"/> | ||
| <line num="335" count="1" type="stmt"/> | ||
| <line num="336" count="18" type="cond" truecount="1" falsecount="1"/> | ||
| <line num="337" count="0" type="stmt"/> | ||
| <line num="338" count="0" type="stmt"/> | ||
| <line num="340" count="18" type="stmt"/> | ||
| <line num="341" count="60" type="stmt"/> | ||
| <line num="347" count="101" type="stmt"/> | ||
| <line num="348" count="101" type="stmt"/> | ||
| <line num="349" count="101" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="350" count="101" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="351" count="10" type="stmt"/> | ||
| <line num="352" count="10" type="stmt"/> | ||
| <line num="353" count="10" type="stmt"/> | ||
| <line num="341" count="18" type="stmt"/> | ||
| <line num="342" count="18" type="stmt"/> | ||
| <line num="343" count="18" type="stmt"/> | ||
| <line num="344" count="60" type="stmt"/> | ||
| <line num="350" count="101" type="stmt"/> | ||
| <line num="351" count="101" type="stmt"/> | ||
| <line num="352" count="101" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="353" count="101" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="354" count="10" type="stmt"/> | ||
| <line num="355" count="10" type="stmt"/> | ||
| <line num="356" count="10" type="stmt"/> | ||
| <line num="357" count="10" type="stmt"/> | ||
| <line num="358" count="10" type="stmt"/> | ||
| <line num="359" count="10" type="stmt"/> | ||
| <line num="360" count="1" type="stmt"/> | ||
| <line num="361" count="1" type="stmt"/> | ||
| <line num="362" count="1" type="stmt"/> | ||
| <line num="367" count="101" type="stmt"/> | ||
| <line num="368" count="101" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="369" count="3" type="stmt"/> | ||
| <line num="370" count="3" type="stmt"/> | ||
| <line num="371" count="3" type="stmt"/> | ||
| <line num="372" count="8" type="stmt"/> | ||
| <line num="376" count="101" type="stmt"/> | ||
| <line num="377" count="101" type="stmt"/> | ||
| <line num="382" count="101" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="383" count="101" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="388" count="101" type="stmt"/> | ||
| <line num="389" count="101" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="390" count="101" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="391" count="101" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="361" count="10" type="stmt"/> | ||
| <line num="362" count="10" type="stmt"/> | ||
| <line num="363" count="1" type="stmt"/> | ||
| <line num="364" count="1" type="stmt"/> | ||
| <line num="365" count="1" type="stmt"/> | ||
| <line num="370" count="101" type="stmt"/> | ||
| <line num="371" count="101" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="372" count="3" type="stmt"/> | ||
| <line num="373" count="3" type="stmt"/> | ||
| <line num="374" count="3" type="stmt"/> | ||
| <line num="375" count="8" type="stmt"/> | ||
| <line num="379" count="101" type="stmt"/> | ||
| <line num="380" count="101" type="stmt"/> | ||
| <line num="385" count="101" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="386" count="101" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="391" count="101" type="stmt"/> | ||
| <line num="392" count="101" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="393" count="1" type="stmt"/> | ||
| <line num="394" count="1" type="stmt"/> | ||
| <line num="395" count="2" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="396" count="2" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="397" count="2" type="stmt"/> | ||
| <line num="399" count="1" type="stmt"/> | ||
| <line num="401" count="101" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="408" count="7" type="stmt"/> | ||
| <line num="409" count="7" type="cond" truecount="4" falsecount="0"/> | ||
| <line num="410" count="5" type="stmt"/> | ||
| <line num="412" count="2" type="cond" truecount="4" falsecount="0"/> | ||
| <line num="413" count="1" type="stmt"/> | ||
| <line num="415" count="1" type="cond" truecount="3" falsecount="1"/> | ||
| <line num="416" count="1" type="cond" truecount="1" falsecount="1"/> | ||
| <line num="417" count="1" type="stmt"/> | ||
| <line num="419" count="0" type="stmt"/> | ||
| <line num="424" count="6" type="cond" truecount="4" falsecount="0"/> | ||
| <line num="425" count="2" type="cond" truecount="4" falsecount="0"/> | ||
| <line num="426" count="1" type="cond" truecount="1" falsecount="1"/> | ||
| <line num="427" count="0" type="stmt"/> | ||
| <line num="433" count="93" type="stmt"/> | ||
| <line num="434" count="93" type="stmt"/> | ||
| <line num="435" count="8" type="stmt"/> | ||
| <line num="436" count="9" type="stmt"/> | ||
| <line num="439" count="93" type="stmt"/> | ||
| <line num="444" count="9" type="stmt"/> | ||
| <line num="445" count="9" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="446" count="1" type="stmt"/> | ||
| <line num="447" count="1" type="stmt"/> | ||
| <line num="448" count="1" type="cond" truecount="1" falsecount="1"/> | ||
| <line num="451" count="9" type="stmt"/> | ||
| <line num="465" count="12" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="466" count="13" type="stmt"/> | ||
| <line num="468" count="5" type="cond" truecount="1" falsecount="1"/> | ||
| <line num="469" count="5" type="stmt"/> | ||
| <line num="470" count="5" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="471" count="3" type="stmt"/> | ||
| <line num="472" count="7" type="stmt"/> | ||
| <line num="473" count="6" type="stmt"/> | ||
| <line num="476" count="2" type="stmt"/> | ||
| <line num="477" count="2" type="stmt"/> | ||
| <line num="480" count="0" type="cond" truecount="0" falsecount="2"/> | ||
| <line num="481" count="0" type="stmt"/> | ||
| <line num="483" count="0" type="stmt"/> | ||
| <line num="488" count="14" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="489" count="6" type="cond" truecount="1" falsecount="1"/> | ||
| <line num="490" count="0" type="cond" truecount="0" falsecount="2"/> | ||
| <line num="491" count="0" type="stmt"/> | ||
| <line num="492" count="0" type="cond" truecount="0" falsecount="2"/> | ||
| <line num="494" count="0" type="cond" truecount="0" falsecount="4"/> | ||
| <line num="495" count="0" type="stmt"/> | ||
| <line num="497" count="0" type="stmt"/> | ||
| <line num="502" count="35" type="cond" truecount="1" falsecount="1"/> | ||
| <line num="503" count="35" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="504" count="25" type="cond" truecount="3" falsecount="1"/> | ||
| <line num="505" count="24" type="cond" truecount="1" falsecount="1"/> | ||
| <line num="506" count="24" type="cond" truecount="1" falsecount="1"/> | ||
| <line num="507" count="24" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="508" count="5" type="stmt"/> | ||
| <line num="509" count="5" type="cond" truecount="1" falsecount="1"/> | ||
| <line num="511" count="19" type="cond" truecount="7" falsecount="5"/> | ||
| <line num="513" count="5" type="stmt"/> | ||
| <line num="515" count="1" type="stmt"/> | ||
| <line num="517" count="0" type="stmt"/> | ||
| <line num="521" count="6" type="stmt"/> | ||
| <line num="523" count="0" type="stmt"/> | ||
| <line num="525" count="1" type="stmt"/> | ||
| <line num="527" count="0" type="stmt"/> | ||
| <line num="529" count="0" type="stmt"/> | ||
| <line num="531" count="1" type="stmt"/> | ||
| <line num="533" count="5" type="stmt"/> | ||
| <line num="539" count="5" type="cond" truecount="6" falsecount="3"/> | ||
| <line num="541" count="1" type="stmt"/> | ||
| <line num="543" count="1" type="stmt"/> | ||
| <line num="545" count="0" type="stmt"/> | ||
| <line num="549" count="1" type="stmt"/> | ||
| <line num="551" count="1" type="stmt"/> | ||
| <line num="553" count="1" type="stmt"/> | ||
| <line num="555" count="0" type="stmt"/> | ||
| <line num="571" count="38" type="cond" truecount="5" falsecount="8"/> | ||
| <line num="573" count="17" type="cond" truecount="8" falsecount="6"/> | ||
| <line num="575" count="7" type="stmt"/> | ||
| <line num="577" count="3" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="579" count="0" type="cond" truecount="0" falsecount="2"/> | ||
| <line num="581" count="2" type="cond" truecount="1" falsecount="1"/> | ||
| <line num="585" count="2" type="stmt"/> | ||
| <line num="587" count="1" type="stmt"/> | ||
| <line num="589" count="0" type="stmt"/> | ||
| <line num="591" count="0" type="stmt"/> | ||
| <line num="593" count="1" type="cond" truecount="1" falsecount="1"/> | ||
| <line num="595" count="1" type="stmt"/> | ||
| <line num="597" count="0" type="stmt"/> | ||
| <line num="599" count="0" type="stmt"/> | ||
| <line num="602" count="1" type="cond" truecount="1" falsecount="1"/> | ||
| <line num="604" count="0" type="stmt"/> | ||
| <line num="606" count="1" type="stmt"/> | ||
| <line num="608" count="0" type="stmt"/> | ||
| <line num="610" count="0" type="stmt"/> | ||
| <line num="612" count="0" type="cond" truecount="0" falsecount="2"/> | ||
| <line num="614" count="0" type="cond" truecount="0" falsecount="2"/> | ||
| <line num="616" count="0" type="stmt"/> | ||
| <line num="618" count="9" type="stmt"/> | ||
| <line num="619" count="9" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="621" count="5" type="cond" truecount="1" falsecount="1"/> | ||
| <line num="622" count="5" type="stmt"/> | ||
| <line num="625" count="0" type="stmt"/> | ||
| <line num="627" count="10" type="stmt"/> | ||
| <line num="629" count="0" type="stmt"/> | ||
| <line num="635" count="5" type="stmt"/> | ||
| <line num="640" count="15" type="stmt"/> | ||
| <line num="641" count="15" type="stmt"/> | ||
| <line num="642" count="29" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="643" count="26" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="644" count="6" type="stmt"/> | ||
| <line num="645" count="20" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="646" count="19" type="stmt"/> | ||
| <line num="649" count="15" type="stmt"/> | ||
| <line num="656" count="101" type="stmt"/> | ||
| <line num="661" count="14" type="stmt"/> | ||
| <line num="665" count="14" type="cond" truecount="1" falsecount="1"/> | ||
| <line num="670" count="101" type="stmt"/> | ||
| <line num="677" count="101" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="682" count="101" type="stmt"/> | ||
| <line num="687" count="92" type="stmt"/> | ||
| <line num="695" count="394" type="cond" truecount="4" falsecount="0"/> | ||
| <line num="696" count="115" type="stmt"/> | ||
| <line num="698" count="279" type="stmt"/> | ||
| <line num="393" count="101" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="394" count="101" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="395" count="101" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="396" count="1" type="stmt"/> | ||
| <line num="397" count="1" type="stmt"/> | ||
| <line num="398" count="2" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="399" count="2" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="400" count="2" type="stmt"/> | ||
| <line num="402" count="1" type="stmt"/> | ||
| <line num="404" count="101" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="411" count="7" type="stmt"/> | ||
| <line num="412" count="7" type="cond" truecount="4" falsecount="0"/> | ||
| <line num="413" count="5" type="stmt"/> | ||
| <line num="415" count="2" type="cond" truecount="4" falsecount="0"/> | ||
| <line num="416" count="1" type="stmt"/> | ||
| <line num="418" count="1" type="cond" truecount="3" falsecount="1"/> | ||
| <line num="419" count="1" type="cond" truecount="1" falsecount="1"/> | ||
| <line num="420" count="1" type="stmt"/> | ||
| <line num="422" count="0" type="stmt"/> | ||
| <line num="427" count="6" type="cond" truecount="4" falsecount="0"/> | ||
| <line num="428" count="2" type="cond" truecount="4" falsecount="0"/> | ||
| <line num="429" count="1" type="cond" truecount="1" falsecount="1"/> | ||
| <line num="430" count="0" type="stmt"/> | ||
| <line num="436" count="93" type="stmt"/> | ||
| <line num="437" count="93" type="stmt"/> | ||
| <line num="438" count="8" type="stmt"/> | ||
| <line num="439" count="9" type="stmt"/> | ||
| <line num="442" count="93" type="stmt"/> | ||
| <line num="447" count="9" type="stmt"/> | ||
| <line num="448" count="9" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="449" count="1" type="stmt"/> | ||
| <line num="450" count="1" type="stmt"/> | ||
| <line num="451" count="1" type="cond" truecount="1" falsecount="1"/> | ||
| <line num="454" count="9" type="stmt"/> | ||
| <line num="468" count="12" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="469" count="13" type="stmt"/> | ||
| <line num="471" count="5" type="cond" truecount="1" falsecount="1"/> | ||
| <line num="472" count="5" type="stmt"/> | ||
| <line num="473" count="5" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="474" count="3" type="stmt"/> | ||
| <line num="475" count="7" type="stmt"/> | ||
| <line num="476" count="6" type="stmt"/> | ||
| <line num="479" count="2" type="stmt"/> | ||
| <line num="480" count="2" type="stmt"/> | ||
| <line num="483" count="0" type="cond" truecount="0" falsecount="2"/> | ||
| <line num="484" count="0" type="stmt"/> | ||
| <line num="486" count="0" type="stmt"/> | ||
| <line num="491" count="14" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="492" count="6" type="cond" truecount="1" falsecount="1"/> | ||
| <line num="493" count="0" type="cond" truecount="0" falsecount="2"/> | ||
| <line num="494" count="0" type="stmt"/> | ||
| <line num="495" count="0" type="cond" truecount="0" falsecount="2"/> | ||
| <line num="497" count="0" type="cond" truecount="0" falsecount="4"/> | ||
| <line num="498" count="0" type="stmt"/> | ||
| <line num="500" count="0" type="stmt"/> | ||
| <line num="505" count="35" type="cond" truecount="1" falsecount="1"/> | ||
| <line num="506" count="35" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="507" count="25" type="cond" truecount="3" falsecount="1"/> | ||
| <line num="508" count="24" type="cond" truecount="1" falsecount="1"/> | ||
| <line num="509" count="24" type="cond" truecount="1" falsecount="1"/> | ||
| <line num="510" count="24" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="511" count="5" type="stmt"/> | ||
| <line num="512" count="5" type="cond" truecount="1" falsecount="1"/> | ||
| <line num="514" count="19" type="cond" truecount="7" falsecount="5"/> | ||
| <line num="516" count="5" type="stmt"/> | ||
| <line num="518" count="1" type="stmt"/> | ||
| <line num="520" count="0" type="stmt"/> | ||
| <line num="524" count="6" type="stmt"/> | ||
| <line num="526" count="0" type="stmt"/> | ||
| <line num="528" count="1" type="stmt"/> | ||
| <line num="530" count="0" type="stmt"/> | ||
| <line num="532" count="0" type="stmt"/> | ||
| <line num="534" count="1" type="stmt"/> | ||
| <line num="536" count="5" type="stmt"/> | ||
| <line num="542" count="5" type="cond" truecount="6" falsecount="3"/> | ||
| <line num="544" count="1" type="stmt"/> | ||
| <line num="546" count="1" type="stmt"/> | ||
| <line num="548" count="0" type="stmt"/> | ||
| <line num="552" count="1" type="stmt"/> | ||
| <line num="554" count="1" type="stmt"/> | ||
| <line num="556" count="1" type="stmt"/> | ||
| <line num="558" count="0" type="stmt"/> | ||
| <line num="574" count="38" type="cond" truecount="5" falsecount="8"/> | ||
| <line num="576" count="17" type="cond" truecount="8" falsecount="6"/> | ||
| <line num="578" count="7" type="stmt"/> | ||
| <line num="580" count="3" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="582" count="0" type="cond" truecount="0" falsecount="2"/> | ||
| <line num="584" count="2" type="cond" truecount="1" falsecount="1"/> | ||
| <line num="588" count="2" type="stmt"/> | ||
| <line num="590" count="1" type="stmt"/> | ||
| <line num="592" count="0" type="stmt"/> | ||
| <line num="594" count="0" type="stmt"/> | ||
| <line num="596" count="1" type="cond" truecount="1" falsecount="1"/> | ||
| <line num="598" count="1" type="stmt"/> | ||
| <line num="600" count="0" type="stmt"/> | ||
| <line num="602" count="0" type="stmt"/> | ||
| <line num="605" count="1" type="cond" truecount="1" falsecount="1"/> | ||
| <line num="607" count="0" type="stmt"/> | ||
| <line num="609" count="1" type="stmt"/> | ||
| <line num="611" count="0" type="stmt"/> | ||
| <line num="613" count="0" type="stmt"/> | ||
| <line num="615" count="0" type="cond" truecount="0" falsecount="2"/> | ||
| <line num="617" count="0" type="cond" truecount="0" falsecount="2"/> | ||
| <line num="619" count="0" type="stmt"/> | ||
| <line num="621" count="9" type="stmt"/> | ||
| <line num="622" count="9" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="624" count="5" type="cond" truecount="1" falsecount="1"/> | ||
| <line num="625" count="5" type="stmt"/> | ||
| <line num="628" count="0" type="stmt"/> | ||
| <line num="630" count="10" type="stmt"/> | ||
| <line num="632" count="0" type="stmt"/> | ||
| <line num="638" count="5" type="stmt"/> | ||
| <line num="643" count="15" type="stmt"/> | ||
| <line num="644" count="15" type="stmt"/> | ||
| <line num="645" count="29" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="646" count="26" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="647" count="6" type="stmt"/> | ||
| <line num="648" count="20" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="649" count="19" type="stmt"/> | ||
| <line num="652" count="15" type="stmt"/> | ||
| <line num="659" count="101" type="stmt"/> | ||
| <line num="664" count="14" type="stmt"/> | ||
| <line num="668" count="14" type="cond" truecount="1" falsecount="1"/> | ||
| <line num="673" count="101" type="stmt"/> | ||
| <line num="680" count="101" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="685" count="101" type="stmt"/> | ||
| <line num="690" count="92" type="stmt"/> | ||
| <line num="698" count="394" type="cond" truecount="4" falsecount="0"/> | ||
| <line num="699" count="115" type="stmt"/> | ||
| <line num="701" count="279" type="stmt"/> | ||
| </file> | ||
| <file name="index.ts" path="/home/runner/work/ContractKit/ContractKit/packages/plugin-bruno/src/index.ts"> | ||
| <metrics statements="48" coveredstatements="12" conditionals="39" coveredconditionals="14" methods="6" coveredmethods="2"/> | ||
| <line num="48" count="6" type="cond" truecount="5" falsecount="0"/> | ||
| <line num="49" count="2" type="stmt"/> | ||
| <line num="51" count="4" type="stmt"/> | ||
| <line num="52" count="4" type="stmt"/> | ||
| <line num="53" count="4" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="54" count="3" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="56" count="1" type="stmt"/> | ||
| <line num="59" count="4" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="63" count="3" type="cond" truecount="1" falsecount="1"/> | ||
| <line num="64" count="3" type="cond" truecount="2" falsecount="0"/> | ||
| <line num="65" count="2" type="stmt"/> | ||
| <line num="68" count="1" type="stmt"/> | ||
| <line num="73" count="0" type="stmt"/> | ||
| <line num="74" count="0" type="cond" truecount="0" falsecount="2"/> | ||
| <line num="75" count="0" type="cond" truecount="0" falsecount="2"/> | ||
| <line num="76" count="0" type="cond" truecount="0" falsecount="2"/> | ||
| <line num="78" count="0" type="stmt"/> | ||
| <line num="80" count="0" type="stmt"/> | ||
| <line num="88" count="0" type="stmt"/> | ||
| <line num="89" count="0" type="stmt"/> | ||
| <line num="113" count="0" type="stmt"/> | ||
| <line num="118" count="0" type="cond" truecount="0" falsecount="2"/> | ||
| <line num="119" count="0" type="cond" truecount="0" falsecount="2"/> | ||
| <line num="120" count="0" type="cond" truecount="0" falsecount="2"/> | ||
| <line num="122" count="0" type="stmt"/> | ||
| <line num="124" count="0" type="stmt"/> | ||
| <line num="132" count="0" type="stmt"/> | ||
| <line num="133" count="0" type="stmt"/> | ||
| <line num="147" count="0" type="stmt"/> | ||
| <line num="148" count="0" type="cond" truecount="0" falsecount="2"/> | ||
| <line num="151" count="0" type="stmt"/> | ||
| <line num="152" count="0" type="stmt"/> | ||
| <line num="154" count="0" type="stmt"/> | ||
| <line num="157" count="0" type="stmt"/> | ||
| <line num="158" count="0" type="stmt"/> | ||
| <line num="159" count="0" type="stmt"/> | ||
| <line num="160" count="0" type="cond" truecount="0" falsecount="2"/> | ||
| <line num="161" count="0" type="stmt"/> | ||
| <line num="162" count="0" type="stmt"/> | ||
| <line num="167" count="0" type="stmt"/> | ||
| <line num="168" count="0" type="stmt"/> | ||
| <line num="169" count="0" type="cond" truecount="0" falsecount="2"/> | ||
| <line num="170" count="0" type="stmt"/> | ||
| <line num="171" count="0" type="cond" truecount="0" falsecount="2"/> | ||
| <line num="172" count="0" type="stmt"/> | ||
| <line num="173" count="0" type="stmt"/> | ||
| <line num="175" count="0" type="stmt"/> | ||
| <line num="178" count="0" type="stmt"/> | ||
| </file> | ||
| </package> | ||
@@ -301,0 +353,0 @@ <package name="tests"> |
+21
-21
@@ -26,5 +26,5 @@ | ||
| <div class='fl pad1y space-right2'> | ||
| <span class="strong">85.82% </span> | ||
| <span class="strong">78.68% </span> | ||
| <span class="quiet">Statements</span> | ||
| <span class='fraction'>321/374</span> | ||
| <span class='fraction'>336/427</span> | ||
| </div> | ||
@@ -34,5 +34,5 @@ | ||
| <div class='fl pad1y space-right2'> | ||
| <span class="strong">76.41% </span> | ||
| <span class="strong">72.17% </span> | ||
| <span class="quiet">Branches</span> | ||
| <span class='fraction'>230/301</span> | ||
| <span class='fraction'>249/345</span> | ||
| </div> | ||
@@ -42,5 +42,5 @@ | ||
| <div class='fl pad1y space-right2'> | ||
| <span class="strong">86.84% </span> | ||
| <span class="strong">82.92% </span> | ||
| <span class="quiet">Functions</span> | ||
| <span class='fraction'>66/76</span> | ||
| <span class='fraction'>68/82</span> | ||
| </div> | ||
@@ -50,5 +50,5 @@ | ||
| <div class='fl pad1y space-right2'> | ||
| <span class="strong">87.42% </span> | ||
| <span class="strong">79.46% </span> | ||
| <span class="quiet">Lines</span> | ||
| <span class='fraction'>285/326</span> | ||
| <span class='fraction'>298/375</span> | ||
| </div> | ||
@@ -68,3 +68,3 @@ | ||
| </div> | ||
| <div class='status-line high'></div> | ||
| <div class='status-line medium'></div> | ||
| <div class="pad1"> | ||
@@ -87,14 +87,14 @@ <table class="coverage-summary"> | ||
| <tbody><tr> | ||
| <td class="file high" data-value="src"><a href="src/index.html">src</a></td> | ||
| <td data-value="87.27" class="pic high"> | ||
| <div class="chart"><div class="cover-fill" style="width: 87%"></div><div class="cover-empty" style="width: 13%"></div></div> | ||
| <td class="file medium" data-value="src"><a href="src/index.html">src</a></td> | ||
| <td data-value="79.11" class="pic medium"> | ||
| <div class="chart"><div class="cover-fill" style="width: 79%"></div><div class="cover-empty" style="width: 21%"></div></div> | ||
| </td> | ||
| <td data-value="87.27" class="pct high">87.27%</td> | ||
| <td data-value="330" class="abs high">288/330</td> | ||
| <td data-value="76.29" class="pct medium">76.29%</td> | ||
| <td data-value="270" class="abs medium">206/270</td> | ||
| <td data-value="92.15" class="pct high">92.15%</td> | ||
| <td data-value="51" class="abs high">47/51</td> | ||
| <td data-value="88.54" class="pct high">88.54%</td> | ||
| <td data-value="288" class="abs high">255/288</td> | ||
| <td data-value="79.11" class="pct medium">79.11%</td> | ||
| <td data-value="383" class="abs medium">303/383</td> | ||
| <td data-value="71.65" class="pct medium">71.65%</td> | ||
| <td data-value="314" class="abs medium">225/314</td> | ||
| <td data-value="85.96" class="pct high">85.96%</td> | ||
| <td data-value="57" class="abs high">49/57</td> | ||
| <td data-value="79.52" class="pct medium">79.52%</td> | ||
| <td data-value="337" class="abs medium">268/337</td> | ||
| </tr> | ||
@@ -125,3 +125,3 @@ | ||
| <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a> | ||
| at 2026-05-01T19:55:27.966Z | ||
| at 2026-05-06T12:37:12.770Z | ||
| </div> | ||
@@ -128,0 +128,0 @@ <script src="prettify.js"></script> |
+32
-17
@@ -26,5 +26,5 @@ | ||
| <div class='fl pad1y space-right2'> | ||
| <span class="strong">87.27% </span> | ||
| <span class="strong">79.11% </span> | ||
| <span class="quiet">Statements</span> | ||
| <span class='fraction'>288/330</span> | ||
| <span class='fraction'>303/383</span> | ||
| </div> | ||
@@ -34,5 +34,5 @@ | ||
| <div class='fl pad1y space-right2'> | ||
| <span class="strong">76.29% </span> | ||
| <span class="strong">71.65% </span> | ||
| <span class="quiet">Branches</span> | ||
| <span class='fraction'>206/270</span> | ||
| <span class='fraction'>225/314</span> | ||
| </div> | ||
@@ -42,5 +42,5 @@ | ||
| <div class='fl pad1y space-right2'> | ||
| <span class="strong">92.15% </span> | ||
| <span class="strong">85.96% </span> | ||
| <span class="quiet">Functions</span> | ||
| <span class='fraction'>47/51</span> | ||
| <span class='fraction'>49/57</span> | ||
| </div> | ||
@@ -50,5 +50,5 @@ | ||
| <div class='fl pad1y space-right2'> | ||
| <span class="strong">88.54% </span> | ||
| <span class="strong">79.52% </span> | ||
| <span class="quiet">Lines</span> | ||
| <span class='fraction'>255/288</span> | ||
| <span class='fraction'>268/337</span> | ||
| </div> | ||
@@ -68,3 +68,3 @@ | ||
| </div> | ||
| <div class='status-line high'></div> | ||
| <div class='status-line medium'></div> | ||
| <div class="pad1"> | ||
@@ -88,15 +88,30 @@ <table class="coverage-summary"> | ||
| <td class="file high" data-value="codegen-bruno.ts"><a href="codegen-bruno.ts.html">codegen-bruno.ts</a></td> | ||
| <td data-value="87.27" class="pic high"> | ||
| <td data-value="87.31" class="pic high"> | ||
| <div class="chart"><div class="cover-fill" style="width: 87%"></div><div class="cover-empty" style="width: 13%"></div></div> | ||
| </td> | ||
| <td data-value="87.27" class="pct high">87.27%</td> | ||
| <td data-value="330" class="abs high">288/330</td> | ||
| <td data-value="76.29" class="pct medium">76.29%</td> | ||
| <td data-value="270" class="abs medium">206/270</td> | ||
| <td data-value="87.31" class="pct high">87.31%</td> | ||
| <td data-value="331" class="abs high">289/331</td> | ||
| <td data-value="76.72" class="pct medium">76.72%</td> | ||
| <td data-value="275" class="abs medium">211/275</td> | ||
| <td data-value="92.15" class="pct high">92.15%</td> | ||
| <td data-value="51" class="abs high">47/51</td> | ||
| <td data-value="88.54" class="pct high">88.54%</td> | ||
| <td data-value="288" class="abs high">255/288</td> | ||
| <td data-value="88.58" class="pct high">88.58%</td> | ||
| <td data-value="289" class="abs high">256/289</td> | ||
| </tr> | ||
| <tr> | ||
| <td class="file low" data-value="index.ts"><a href="index.ts.html">index.ts</a></td> | ||
| <td data-value="26.92" class="pic low"> | ||
| <div class="chart"><div class="cover-fill" style="width: 26%"></div><div class="cover-empty" style="width: 74%"></div></div> | ||
| </td> | ||
| <td data-value="26.92" class="pct low">26.92%</td> | ||
| <td data-value="52" class="abs low">14/52</td> | ||
| <td data-value="35.89" class="pct low">35.89%</td> | ||
| <td data-value="39" class="abs low">14/39</td> | ||
| <td data-value="33.33" class="pct low">33.33%</td> | ||
| <td data-value="6" class="abs low">2/6</td> | ||
| <td data-value="25" class="pct low">25%</td> | ||
| <td data-value="48" class="abs low">12/48</td> | ||
| </tr> | ||
| </tbody> | ||
@@ -110,3 +125,3 @@ </table> | ||
| <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a> | ||
| at 2026-05-01T19:55:27.966Z | ||
| at 2026-05-06T12:37:12.770Z | ||
| </div> | ||
@@ -113,0 +128,0 @@ <script src="../prettify.js"></script> |
@@ -592,3 +592,3 @@ | ||
| <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a> | ||
| at 2026-05-01T19:55:27.966Z | ||
| at 2026-05-06T12:37:12.770Z | ||
| </div> | ||
@@ -595,0 +595,0 @@ <script src="../prettify.js"></script> |
@@ -104,3 +104,3 @@ | ||
| <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a> | ||
| at 2026-05-01T19:55:27.966Z | ||
| at 2026-05-06T12:37:12.770Z | ||
| </div> | ||
@@ -107,0 +107,0 @@ <script src="../prettify.js"></script> |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"codegen-bruno.d.ts","sourceRoot":"","sources":["../src/codegen-bruno.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACR,UAAU,EAMV,gBAAgB,EAGnB,MAAM,mBAAmB,CAAC;AAK3B,iGAAiG;AACjG,MAAM,WAAW,kBAAkB;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,8JAA8J;AAC9J,eAAO,MAAM,iBAAiB,qCAAqC,CAAC;AAEpE,mFAAmF;AACnF,MAAM,WAAW,mBAAmB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;CACf;AAED,kHAAkH;AAClH,MAAM,WAAW,gBAAgB;IAC7B,gEAAgE;IAChE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,uDAAuD;IACvD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;CACjD;AAED,qEAAqE;AACrE,MAAM,WAAW,qBAAqB;IAClC,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,gBAAgB,EAAE,CAAC;IACnC,IAAI,CAAC,EAAE,gBAAgB,CAAC;IACxB;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;;;;;;OASG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CAC1D;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,OAAO,EAAE,qBAAqB,GAAG,kBAAkB,EAAE,CAuEhH;AAED,sLAAsL;AACtL,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAUvD;AAsBD;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,aAAa,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,GAAG,MAAM,CAOxF;AA2cD,wEAAwE;AACxE,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMhD;AAED,uEAAuE;AACvE,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CASjD"} | ||
| {"version":3,"file":"codegen-bruno.d.ts","sourceRoot":"","sources":["../src/codegen-bruno.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACR,UAAU,EAMV,gBAAgB,EAGnB,MAAM,mBAAmB,CAAC;AAK3B,iGAAiG;AACjG,MAAM,WAAW,kBAAkB;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,8JAA8J;AAC9J,eAAO,MAAM,iBAAiB,qCAAqC,CAAC;AAEpE,mFAAmF;AACnF,MAAM,WAAW,mBAAmB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;CACf;AAED,kHAAkH;AAClH,MAAM,WAAW,gBAAgB;IAC7B,gEAAgE;IAChE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,uDAAuD;IACvD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;CACjD;AAED,qEAAqE;AACrE,MAAM,WAAW,qBAAqB;IAClC,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,gBAAgB,EAAE,CAAC;IACnC,IAAI,CAAC,EAAE,gBAAgB,CAAC;IACxB;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;;;;;;OASG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CAC1D;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,OAAO,EAAE,qBAAqB,GAAG,kBAAkB,EAAE,CA0EhH;AAED,sLAAsL;AACtL,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAUvD;AAsBD;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,aAAa,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,GAAG,MAAM,CAOxF;AA2cD,wEAAwE;AACxE,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMhD;AAED,uEAAuE;AACvE,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CASjD"} |
+10
-1
| import type { BrunoSecurityScheme } from './codegen-bruno.js'; | ||
| import type { ContractKitPlugin } from '@contractkit/core'; | ||
| import type { ContractKitPlugin, PluginValue } from '@contractkit/core'; | ||
| /** Configuration accepted by the Bruno plugin, both via `contractkit.config.json` and `createBrunoPlugin`. */ | ||
@@ -36,2 +36,11 @@ export interface BrunoPluginConfig { | ||
| } | ||
| /** | ||
| * Validates a `plugins.bruno` extension entry on an operation. The expected shape is | ||
| * `{ template?: string }`, where `template` is a YAML fragment to deep-merge into the | ||
| * generated request file (typically a `file://...` URL whose contents have already | ||
| * been loaded by the CLI resolver). | ||
| */ | ||
| export declare function validateBrunoExtension(value: PluginValue): { | ||
| errors?: string[]; | ||
| } | void; | ||
| declare const plugin: ContractKitPlugin; | ||
@@ -38,0 +47,0 @@ export default plugin; |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAE3D,8GAA8G;AAC9G,MAAM,WAAW,iBAAiB;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CAC1D;AAED,mHAAmH;AACnH,MAAM,WAAW,kBAAmB,SAAQ,iBAAiB;IACzD,IAAI,CAAC,EAAE;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAA;KAAE,CAAC;CACnF;AAID,QAAA,MAAM,MAAM,EAAE,iBAuBb,CAAC;AAEF,eAAe,MAAM,CAAC;AAItB;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAC7B,MAAM,EAAE,iBAAiB,EACzB,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAA;CAAE,GAChF,iBAAiB,CAwBnB"} | ||
| {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAExE,8GAA8G;AAC9G,MAAM,WAAW,iBAAiB;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CAC1D;AAED,mHAAmH;AACnH,MAAM,WAAW,kBAAmB,SAAQ,iBAAiB;IACzD,IAAI,CAAC,EAAE;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAA;KAAE,CAAC;CACnF;AAID;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,WAAW,GAAG;IAAE,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,IAAI,CAavF;AAQD,QAAA,MAAM,MAAM,EAAE,iBAwBb,CAAC;AAEF,eAAe,MAAM,CAAC;AAItB;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAC7B,MAAM,EAAE,iBAAiB,EACzB,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAA;CAAE,GAChF,iBAAiB,CAyBnB"} |
+34
-3
@@ -70,4 +70,5 @@ var __defProp = Object.defineProperty; | ||
| let content = generateRequestFile(route, op, requestName, seq, modelMap, root, defaultScheme, randomExamples); | ||
| const pluginOverride = op.pluginFiles?.["bruno"]; | ||
| if (pluginOverride !== void 0) { | ||
| const brunoExt = op.pluginExtensions?.["bruno"]; | ||
| const pluginOverride = brunoExt && typeof brunoExt === "object" && !Array.isArray(brunoExt) ? brunoExt["template"] : void 0; | ||
| if (typeof pluginOverride === "string") { | ||
| content = mergePluginFile(content, pluginOverride); | ||
@@ -614,5 +615,33 @@ } | ||
| // src/index.ts | ||
| function validateBrunoExtension(value) { | ||
| if (value === null || typeof value !== "object" || Array.isArray(value)) { | ||
| return { | ||
| errors: [ | ||
| `expected an object, got ${describe(value)}` | ||
| ] | ||
| }; | ||
| } | ||
| const errors = []; | ||
| for (const [key, val] of Object.entries(value)) { | ||
| if (key === "template") { | ||
| if (typeof val !== "string") errors.push(`'template' must be a string, got ${describe(val)}`); | ||
| } else { | ||
| errors.push(`unknown field '${key}' (allowed: template)`); | ||
| } | ||
| } | ||
| return errors.length ? { | ||
| errors | ||
| } : void 0; | ||
| } | ||
| __name(validateBrunoExtension, "validateBrunoExtension"); | ||
| function describe(value) { | ||
| if (value === null) return "null"; | ||
| if (Array.isArray(value)) return "array"; | ||
| return typeof value; | ||
| } | ||
| __name(describe, "describe"); | ||
| var plugin = { | ||
| name: "bruno", | ||
| cacheKey: "bruno", | ||
| validateExtension: validateBrunoExtension, | ||
| async generateTargets({ opRoots, contractRoots }, ctx) { | ||
@@ -642,2 +671,3 @@ const { auth, ...config } = ctx.options; | ||
| cacheKey: `bruno:${JSON.stringify(config)}`, | ||
| validateExtension: validateBrunoExtension, | ||
| async generateTargets({ opRoots, contractRoots }, ctx) { | ||
@@ -701,4 +731,5 @@ const base = config.baseDir ? resolve(rootDir, config.baseDir) : rootDir; | ||
| createBrunoPlugin, | ||
| index_default as default | ||
| index_default as default, | ||
| validateBrunoExtension | ||
| }; | ||
| //# sourceMappingURL=index.js.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/index.ts","../src/codegen-bruno.ts"],"sourcesContent":["import { resolve, basename, dirname } from 'node:path';\nimport { existsSync, readFileSync, rmSync, readdirSync, rmdirSync } from 'node:fs';\nimport { generateOpenCollection, MANIFEST_FILENAME, parseManifest } from './codegen-bruno.js';\nimport type { BrunoSecurityScheme } from './codegen-bruno.js';\nimport type { ContractKitPlugin } from '@contractkit/core';\n\n/** Configuration accepted by the Bruno plugin, both via `contractkit.config.json` and `createBrunoPlugin`. */\nexport interface BrunoPluginConfig {\n baseDir?: string;\n output?: string;\n collectionName?: string;\n /**\n * When true (default), example values use Bruno's faker templates\n * (`{{$randomUUID}}`, `{{$randomEmail}}`, etc.) so each send produces\n * fresh data. Set to false for deterministic placeholders.\n */\n randomExamples?: boolean;\n /**\n * Whether to generate request files for operations marked `internal`. Defaults to\n * `true` — Bruno collections are typically used by the team that owns the API and\n * benefit from full coverage. Set to `false` to omit internal ops.\n */\n includeInternal?: boolean;\n /**\n * Map of environment name → variables. Each entry produces a\n * `environments/<name>.yml` file. When omitted, a default `local.yml` is\n * emitted with `baseUrl=http://localhost:3000` and any auth env-var\n * placeholders. When provided, the default is replaced entirely; include\n * auth variables (e.g. `token`) explicitly if you need them.\n */\n environments?: Record<string, Record<string, unknown>>;\n}\n\n/** Full plugin options shape read from `ctx.options` — extends {@link BrunoPluginConfig} with the `auth` block. */\nexport interface BrunoPluginOptions extends BrunoPluginConfig {\n auth?: { defaultScheme: string; schemes?: Record<string, BrunoSecurityScheme> };\n}\n\n// ─── Default export: loaded via plugins array, reads config from ctx.options ─\n\nconst plugin: ContractKitPlugin = {\n name: 'bruno',\n cacheKey: 'bruno',\n async generateTargets({ opRoots, contractRoots }, ctx) {\n const { auth, ...config } = ctx.options as BrunoPluginOptions;\n const base = config.baseDir ? resolve(ctx.rootDir, config.baseDir) : ctx.rootDir;\n const outDir = resolve(base, config.output ?? 'bruno-collection');\n const collectionName = config.collectionName ?? basename(ctx.rootDir);\n\n cleanupTrackedFiles(outDir);\n\n const files = generateOpenCollection(opRoots, {\n collectionName,\n contractRoots,\n auth,\n randomExamples: config.randomExamples ?? true,\n includeInternal: config.includeInternal,\n environments: config.environments,\n });\n for (const { relativePath, content } of files) {\n ctx.emitFile(resolve(outDir, relativePath), content);\n }\n },\n};\n\nexport default plugin;\n\n// ─── Factory: for programmatic use with explicit config ────────────────────\n\n/**\n * Creates a Bruno plugin instance with explicit configuration, for programmatic use.\n *\n * Prefer the default export when loading via `contractkit.config.json`. Use this\n * factory when constructing the plugin in code (e.g. in tests or custom build scripts).\n *\n * @param config - Plugin configuration (output paths and feature flags).\n * @param rootDir - Absolute path used to resolve relative paths in `config`.\n * @param auth - Optional auth scheme configuration mirroring the `auth` key in plugin options.\n */\nexport function createBrunoPlugin(\n config: BrunoPluginConfig,\n rootDir: string,\n auth?: { defaultScheme: string; schemes?: Record<string, BrunoSecurityScheme> },\n): ContractKitPlugin {\n return {\n name: 'bruno',\n cacheKey: `bruno:${JSON.stringify(config)}`,\n async generateTargets({ opRoots, contractRoots }, ctx) {\n const base = config.baseDir ? resolve(rootDir, config.baseDir) : rootDir;\n const outDir = resolve(base, config.output ?? 'bruno-collection');\n const collectionName = config.collectionName ?? basename(rootDir);\n\n cleanupTrackedFiles(outDir);\n\n const files = generateOpenCollection(opRoots, {\n collectionName,\n contractRoots,\n auth,\n randomExamples: config.randomExamples ?? true,\n includeInternal: config.includeInternal,\n environments: config.environments,\n });\n for (const { relativePath, content } of files) {\n ctx.emitFile(resolve(outDir, relativePath), content);\n }\n },\n };\n}\n\n/**\n * Delete files this plugin generated on the previous run, leaving anything\n * the user added (custom .bru files, scripts, secrets, etc.) untouched.\n *\n * On first run — or after manual deletion of the manifest — nothing is\n * removed; stale files from prior versions linger until manually cleaned.\n */\nfunction cleanupTrackedFiles(outDir: string): void {\n const manifestPath = resolve(outDir, MANIFEST_FILENAME);\n if (!existsSync(manifestPath)) return;\n\n let tracked: string[];\n try {\n tracked = parseManifest(readFileSync(manifestPath, 'utf-8'));\n } catch {\n return;\n }\n\n const removedDirs = new Set<string>();\n for (const rel of tracked) {\n const abs = resolve(outDir, rel);\n if (existsSync(abs)) {\n rmSync(abs, { force: true });\n removedDirs.add(dirname(abs));\n }\n }\n\n // Walk up from each affected directory and remove it if empty, stopping at outDir.\n for (const dir of removedDirs) {\n let current = dir;\n while (current.startsWith(outDir) && current !== outDir) {\n try {\n if (readdirSync(current).length === 0) {\n rmdirSync(current);\n current = dirname(current);\n } else {\n break;\n }\n } catch {\n break;\n }\n }\n }\n}\n","import type {\n OpRootNode,\n OpRouteNode,\n OpOperationNode,\n OpResponseNode,\n ParamSource,\n ContractTypeNode,\n ContractRootNode,\n ModelNode,\n FieldNode,\n} from '@contractkit/core';\nimport { resolveSecurity, resolveModifiers, SECURITY_NONE } from '@contractkit/core';\nimport { basename } from 'path';\nimport { parse as parseYaml, stringify as stringifyYaml } from 'yaml';\n\n/** A single file produced by the Bruno codegen — a relative output path and its YAML content. */\nexport interface OpenCollectionFile {\n relativePath: string;\n content: string;\n}\n\n/** Manifest filename — tracks which files this plugin previously generated so subsequent runs can clean up only those, leaving any user-added files alone. */\nexport const MANIFEST_FILENAME = '.contractkit-bruno-manifest.json';\n\n/** Subset of a security scheme sufficient for Bruno auth generation (non-HMAC). */\nexport interface BrunoSecurityScheme {\n type: string; // \"http\" | \"apiKey\" | \"oauth2\" | \"openIdConnect\"\n scheme?: string; // \"bearer\" | \"basic\" (when type === \"http\")\n name?: string; // header/query param name (when type === \"apiKey\")\n in?: string; // \"header\" | \"query\" (when type === \"apiKey\")\n}\n\n/** Auth configuration passed to the Bruno codegen; drives collection-level auth and per-operation auth blocks. */\nexport interface BrunoAuthOptions {\n /** Name of the default scheme (from config.security.default) */\n defaultScheme?: string;\n /** Scheme definitions keyed by name (non-HMAC only) */\n schemes?: Record<string, BrunoSecurityScheme>;\n}\n\n/** Options controlling what {@link generateOpenCollection} emits. */\nexport interface OpenCollectionOptions {\n collectionName: string;\n contractRoots?: ContractRootNode[];\n auth?: BrunoAuthOptions;\n /**\n * When true, emit Bruno faker template strings (e.g. `{{$randomUUID}}`,\n * `{{$randomEmail}}`) for compatible scalar types so each send produces\n * fresh data. When false (default), use deterministic placeholders.\n */\n randomExamples?: boolean;\n /**\n * Whether to generate request files for operations marked `internal`. Defaults to\n * `true` — Bruno collections are typically used by the team that owns the API and\n * benefit from full coverage. Set to `false` to omit internal ops.\n */\n includeInternal?: boolean;\n /**\n * Map of environment name → variables. Each entry produces a\n * `environments/<name>.yml` file with the variables in declaration order.\n * Values are coerced to strings.\n *\n * When omitted, a default `environments/local.yml` is emitted with\n * `baseUrl=http://localhost:3000` plus any auth env-var placeholders the\n * default scheme requires. When provided, the default is replaced entirely\n * — auth vars are not auto-injected, so include them explicitly if needed.\n */\n environments?: Record<string, Record<string, unknown>>;\n}\n\n/**\n * Generates an OpenCollection (https://spec.opencollection.com/) API collection\n * from a set of operation roots. Produces opencollection.yml, an environment\n * file, and one .yml request file per operation.\n */\nexport function generateOpenCollection(roots: OpRootNode[], options: OpenCollectionOptions): OpenCollectionFile[] {\n const files: OpenCollectionFile[] = [];\n\n const modelMap = buildModelMap(options.contractRoots ?? []);\n const authOpts = options.auth;\n const defaultScheme = authOpts?.defaultScheme ? authOpts.schemes?.[authOpts.defaultScheme] : undefined;\n const randomExamples = options.randomExamples ?? false;\n const includeInternal = options.includeInternal ?? true;\n\n files.push({ relativePath: 'opencollection.yml', content: generateCollectionRoot(options.collectionName, defaultScheme) });\n for (const envFile of generateEnvFiles(options.environments, defaultScheme)) {\n files.push(envFile);\n }\n // Manifest is appended at the end so it lists every generated path including itself.\n\n // Roots are sorted by their top-level folder display name (then by subarea) so the\n // emitted `seq:` numbers — which drive Bruno's UI ordering — line up alphabetically.\n const sortedRoots = [...roots].sort((a, b) => {\n const aArea = a.meta['area'] ?? deriveFolderName(a.file);\n const bArea = b.meta['area'] ?? deriveFolderName(b.file);\n const cmp = aArea.localeCompare(bArea);\n if (cmp !== 0) return cmp;\n return (a.meta['subarea'] ?? '').localeCompare(b.meta['subarea'] ?? '');\n });\n\n for (let rootIdx = 0; rootIdx < sortedRoots.length; rootIdx++) {\n const root = sortedRoots[rootIdx]!;\n const folder = root.meta['area'] ? slugifyName(root.meta['area']) : deriveFolderName(root.file);\n const displayName = (root.meta['area'] ?? folder).charAt(0).toUpperCase() + (root.meta['area'] ?? folder).slice(1);\n\n files.push({ relativePath: `${folder}/folder.yml`, content: generateFolderFile(displayName, rootIdx + 1) });\n\n const subarea = root.meta['subarea'];\n const subareaSlug = subarea ? slugifyName(subarea) : undefined;\n const requestDir = subareaSlug ? `${folder}/${subareaSlug}` : folder;\n\n if (subareaSlug) {\n const subareaDisplayName = subarea!.charAt(0).toUpperCase() + subarea!.slice(1);\n files.push({ relativePath: `${requestDir}/folder.yml`, content: generateFolderFile(subareaDisplayName, 1) });\n }\n\n // Flatten and alphabetize within this folder before assigning `seq:`.\n const requests: Array<{ route: typeof root.routes[number]; op: typeof root.routes[number]['operations'][number]; requestName: string }> = [];\n for (const route of root.routes) {\n for (const op of route.operations) {\n if (!includeInternal && resolveModifiers(route, op).includes('internal')) continue;\n requests.push({ route, op, requestName: op.name ?? route.path });\n }\n }\n requests.sort((a, b) => a.requestName.localeCompare(b.requestName));\n\n let seq = 1;\n for (const { route, op, requestName } of requests) {\n const fileName = op.name ? `${slugifyName(op.name)}.yml` : `${op.method}-${sanitizePath(route.path)}.yml`;\n let content = generateRequestFile(route, op, requestName, seq, modelMap, root, defaultScheme, randomExamples);\n const pluginOverride = op.pluginFiles?.['bruno'];\n if (pluginOverride !== undefined) {\n content = mergePluginFile(content, pluginOverride);\n }\n files.push({ relativePath: `${requestDir}/${fileName}`, content });\n seq++;\n }\n }\n\n const trackedPaths = [...files.map(f => f.relativePath), MANIFEST_FILENAME].sort();\n files.push({\n relativePath: MANIFEST_FILENAME,\n content: JSON.stringify({ files: trackedPaths }, null, 2) + '\\n',\n });\n\n return files;\n}\n\n/** Parse a previously-written manifest. Returns the list of relative paths to clean up. Returns [] if missing or unreadable so a stale/garbled manifest never blocks regeneration. */\nexport function parseManifest(content: string): string[] {\n try {\n const parsed = JSON.parse(content);\n if (Array.isArray(parsed?.files) && parsed.files.every((f: unknown) => typeof f === 'string')) {\n return parsed.files as string[];\n }\n } catch {\n // fall through\n }\n return [];\n}\n\n// ─── Plugin file merge ─────────────────────────────────────────────────────\n\nfunction deepMerge(base: unknown, override: unknown): unknown {\n if (\n override !== null &&\n typeof override === 'object' &&\n !Array.isArray(override) &&\n base !== null &&\n typeof base === 'object' &&\n !Array.isArray(base)\n ) {\n const result: Record<string, unknown> = { ...(base as Record<string, unknown>) };\n for (const [key, val] of Object.entries(override as Record<string, unknown>)) {\n result[key] = deepMerge(result[key], val);\n }\n return result;\n }\n return override;\n}\n\n/**\n * Deep-merges a YAML override string into a generated YAML string.\n *\n * Objects are merged recursively; arrays and scalars in the override replace the\n * generated value entirely. If `pluginFileContent` is not a YAML mapping (e.g. it\n * is a scalar or a list), the generated content is returned unchanged.\n *\n * @param generatedYaml - The YAML string produced by the Bruno codegen.\n * @param pluginFileContent - The YAML override string to merge in.\n * @returns The merged YAML string, or `generatedYaml` if the override is not a mapping.\n */\nexport function mergePluginFile(generatedYaml: string, pluginFileContent: string): string {\n const overrideParsed = parseYaml(pluginFileContent);\n if (overrideParsed === null || typeof overrideParsed !== 'object' || Array.isArray(overrideParsed)) {\n return generatedYaml;\n }\n const merged = deepMerge(parseYaml(generatedYaml), overrideParsed);\n return stringifyYaml(merged, { lineWidth: 0 });\n}\n\n// ─── File generators ───────────────────────────────────────────────────────\n\nfunction generateCollectionRoot(name: string, scheme?: BrunoSecurityScheme): string {\n const lines = [`opencollection: \"1.0.0\"`, `info:`, ` name: ${yamlString(name)}`];\n if (scheme) {\n lines.push(``);\n lines.push(`request:`);\n lines.push(...renderAuthBlock(scheme, ' '));\n }\n lines.push(``);\n return lines.join('\\n');\n}\n\nfunction generateEnvFiles(\n environments: Record<string, Record<string, unknown>> | undefined,\n scheme: BrunoSecurityScheme | undefined,\n): OpenCollectionFile[] {\n if (environments && Object.keys(environments).length > 0) {\n return Object.entries(environments).map(([name, variables]) => ({\n relativePath: `environments/${name}.yml`,\n content: renderEnvFile(displayNameFor(name), variables),\n }));\n }\n const defaultVars: Record<string, string> = { baseUrl: 'http://localhost:3000' };\n if (scheme) {\n for (const varName of authEnvVarNames(scheme)) defaultVars[varName] = '';\n }\n return [{ relativePath: 'environments/local.yml', content: renderEnvFile('Local', defaultVars) }];\n}\n\nfunction renderEnvFile(name: string, variables: Record<string, unknown>): string {\n const lines = [`name: ${name}`, `variables:`];\n for (const [key, value] of Object.entries(variables)) {\n const escaped = String(value).replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"');\n lines.push(` - name: ${key}`);\n lines.push(` value: \"${escaped}\"`);\n }\n lines.push(``);\n return lines.join('\\n');\n}\n\nfunction displayNameFor(envKey: string): string {\n return envKey.charAt(0).toUpperCase() + envKey.slice(1);\n}\n\nfunction generateFolderFile(name: string, seq: number): string {\n return [`info:`, ` name: ${yamlString(name)}`, ` type: folder`, ` seq: ${seq}`, ``].join('\\n');\n}\n\nfunction generateRequestFile(\n route: OpRouteNode,\n op: OpOperationNode,\n name: string,\n seq: number,\n modelMap: Map<string, ModelNode>,\n root?: OpRootNode,\n defaultScheme?: BrunoSecurityScheme,\n randomExamples = false,\n): string {\n const lines: string[] = [];\n\n lines.push(`info:`);\n lines.push(` name: ${yamlString(name)}`);\n lines.push(` type: http`);\n lines.push(` seq: ${seq}`);\n lines.push(``);\n lines.push(`http:`);\n lines.push(` method: ${op.method.toUpperCase()}`);\n lines.push(` url: ${yamlString(`{{baseUrl}}${openCollectionPath(route.path)}`)}`);\n\n // Params — flat array with type: \"path\" | \"query\". Optional query params are\n // emitted with disabled: true so users opt in before sending.\n const pathParams: Array<ParamEntry & { kind: 'path' | 'query' }> = extractPathParamNames(route.path).map(n => ({\n name: n,\n type: findParamType(route.params, n, modelMap),\n optional: false,\n kind: 'path' as const,\n }));\n const queryParams: Array<ParamEntry & { kind: 'path' | 'query' }> = op.query\n ? expandParamSource(op.query, modelMap).map(e => ({ ...e, kind: 'query' as const }))\n : [];\n const allParams = [...pathParams, ...queryParams];\n\n if (allParams.length > 0) {\n lines.push(` params:`);\n for (const p of allParams) {\n lines.push(` - name: ${p.name}`);\n lines.push(` value: ${paramExampleValue(p.type, p.default, randomExamples)}`);\n lines.push(` type: ${p.kind}`);\n if (p.optional && p.kind === 'query') lines.push(` disabled: true`);\n }\n }\n\n // Headers\n if (op.headers) {\n const headerEntries = expandParamSource(op.headers, modelMap);\n if (headerEntries.length > 0) {\n lines.push(` headers:`);\n for (const h of headerEntries) {\n lines.push(` - name: ${h.name}`);\n lines.push(` value: ${paramExampleValue(h.type, h.default, randomExamples)}`);\n if (h.optional) lines.push(` disabled: true`);\n }\n }\n }\n\n // Auth — inside http block; inherit collection default unless this op is explicitly public\n if (defaultScheme) {\n const security = root ? resolveSecurity(route, op, root) : (op.security ?? route.security);\n if (security === SECURITY_NONE) {\n lines.push(` auth:`);\n lines.push(` type: none`);\n } else {\n lines.push(` auth: inherit`);\n }\n }\n\n // Body — Bruno supports a single body per request, so prefer JSON, then form-urlencoded, then multipart.\n if (op.request && op.request.bodies.length > 0) {\n const preferredOrder: Array<(typeof op.request.bodies)[number]['contentType']> = [\n 'application/json',\n 'application/x-www-form-urlencoded',\n 'multipart/form-data',\n ];\n const primary =\n preferredOrder.map(ct => op.request!.bodies.find(b => b.contentType === ct)).find(b => b !== undefined) ?? op.request.bodies[0]!;\n\n lines.push(` body:`);\n if (primary.contentType === 'multipart/form-data') {\n lines.push(` type: multipart-form`);\n lines.push(` data: []`);\n } else if (primary.contentType === 'application/x-www-form-urlencoded') {\n lines.push(` type: form-urlencoded`);\n lines.push(` data: []`);\n } else {\n const json = JSON.stringify(typeToExampleValue(primary.bodyType, modelMap, randomExamples), null, 2);\n lines.push(` type: json`);\n lines.push(` data: |`);\n for (const jsonLine of json.split('\\n')) {\n lines.push(` ${jsonLine}`);\n }\n }\n }\n\n // runtime.assertions — auto-generate a status-code check and presence checks for required response headers.\n const expectedStatus = pickAssertionStatus(op.responses);\n const assertedResponse = op.responses.find(r => r.statusCode === expectedStatus);\n const requiredHeaders = (assertedResponse?.headers ?? []).filter(h => !h.optional);\n if (expectedStatus !== undefined) {\n lines.push(``);\n lines.push(`runtime:`);\n lines.push(` assertions:`);\n lines.push(` - expression: res.status`);\n lines.push(` operator: eq`);\n // Always quote — the OpenCollection schema types `value` as a string,\n // so we must keep \"200\" from being parsed as YAML number 200.\n lines.push(` value: \"${expectedStatus}\"`);\n for (const h of requiredHeaders) {\n lines.push(` - expression: res.headers[\"${h.name.toLowerCase()}\"]`);\n lines.push(` operator: isDefined`);\n lines.push(` value: \"\"`);\n }\n }\n\n // docs — combine route- and operation-level descriptions plus the declared response-header summary.\n const docs = buildRequestDocs(route, op, assertedResponse);\n if (docs) {\n lines.push(``);\n lines.push(`docs: |-`);\n for (const docLine of docs.split('\\n')) {\n lines.push(` ${docLine}`);\n }\n }\n\n lines.push(``);\n return lines.join('\\n');\n}\n\n/** Pick the response whose status code we'll assert against. Prefers the first declared 2xx; otherwise falls back to the first declared response. Returns undefined if no responses are declared. */\nfunction pickAssertionStatus(responses: OpResponseNode[]): number | undefined {\n const success = responses.find(r => r.statusCode >= 200 && r.statusCode < 300);\n return success?.statusCode ?? responses[0]?.statusCode;\n}\n\n/** Build a markdown docs block from route- and operation-level descriptions, plus declared response-header summary. */\nfunction buildRequestDocs(route: OpRouteNode, op: OpOperationNode, assertedResponse?: OpResponseNode): string | undefined {\n const parts: string[] = [];\n if (route.description) parts.push(route.description.trim());\n if (op.description) parts.push(op.description.trim());\n const headers = assertedResponse?.headers ?? [];\n if (headers.length > 0) {\n const lines = ['**Response headers**', ''];\n for (const h of headers) {\n const tag = h.optional ? 'optional' : 'required';\n const desc = h.description ? ` — ${h.description}` : '';\n lines.push(`- \\`${h.name}\\` (${tag})${desc}`);\n }\n parts.push(lines.join('\\n'));\n }\n return parts.length > 0 ? parts.join('\\n\\n') : undefined;\n}\n\n// ─── Auth helpers ──────────────────────────────────────────────────────────\n\n/** Generate the YAML lines for an auth block (flat, per spec), indented by `indent`. */\nfunction renderAuthBlock(scheme: BrunoSecurityScheme, indent: string): string[] {\n const i = indent;\n if (scheme.type === 'http' && scheme.scheme === 'bearer') {\n return [`${i}auth:`, `${i} type: bearer`, `${i} token: \"{{token}}\"`];\n }\n if (scheme.type === 'http' && scheme.scheme === 'basic') {\n return [`${i}auth:`, `${i} type: basic`, `${i} username: \"{{username}}\"`, `${i} password: \"{{password}}\"`];\n }\n if (scheme.type === 'apiKey' && scheme.in === 'header') {\n const headerName = scheme.name ?? 'X-Api-Key';\n return [`${i}auth:`, `${i} type: apikey`, `${i} key: ${headerName}`, `${i} value: \"{{apiKey}}\"`, `${i} placement: header`];\n }\n return [];\n}\n\n/** Return the environment variable names needed for a given auth scheme. */\nfunction authEnvVarNames(scheme: BrunoSecurityScheme): string[] {\n if (scheme.type === 'http' && scheme.scheme === 'bearer') return ['token'];\n if (scheme.type === 'http' && scheme.scheme === 'basic') return ['username', 'password'];\n if (scheme.type === 'apiKey') return ['apiKey'];\n return [];\n}\n\n// ─── Model registry ────────────────────────────────────────────────────────\n\nfunction buildModelMap(contractRoots: ContractRootNode[]): Map<string, ModelNode> {\n const map = new Map<string, ModelNode>();\n for (const root of contractRoots) {\n for (const model of root.models) {\n map.set(model.name, model);\n }\n }\n return map;\n}\n\n/** Resolve all fields for a model, including inherited base fields (bases first, in declaration order). */\nfunction resolveModelFields(model: ModelNode, modelMap: Map<string, ModelNode>): FieldNode[] {\n const collected: FieldNode[] = [];\n if (model.bases) {\n for (const base of model.bases) {\n const baseModel = modelMap.get(base);\n if (baseModel) collected.push(...resolveModelFields(baseModel, modelMap));\n }\n }\n return [...collected, ...model.fields];\n}\n\n// ─── Param helpers ─────────────────────────────────────────────────────────\n\ninterface ParamEntry {\n name: string;\n type: ContractTypeNode | undefined;\n default?: string | number | boolean;\n optional: boolean;\n}\n\n/** Expand a ParamSource into a flat list of named entries with their types. */\nfunction expandParamSource(source: ParamSource, modelMap: Map<string, ModelNode>): ParamEntry[] {\n if (source.kind === 'params') {\n return source.nodes.map(n => ({ name: n.name, type: n.type, default: n.default, optional: n.optional }));\n }\n if (source.kind === 'ref') {\n const model = modelMap.get(source.name);\n if (model) {\n return resolveModelFields(model, modelMap)\n .filter(f => f.visibility !== 'readonly')\n .map(f => ({ name: f.name, type: f.type, default: f.default, optional: f.optional }));\n }\n // Fallback: single placeholder entry\n const name = source.name.charAt(0).toLowerCase() + source.name.slice(1);\n return [{ name, type: undefined, optional: false }];\n }\n // kind === 'type': if it's an inline object, expand its fields\n if (source.node.kind === 'inlineObject') {\n return source.node.fields.map(f => ({ name: f.name, type: f.type, default: f.default, optional: f.optional }));\n }\n return [];\n}\n\n/** Look up a named path param's type from route.params. */\nfunction findParamType(source: ParamSource | undefined, name: string, modelMap: Map<string, ModelNode>): ContractTypeNode | undefined {\n if (!source) return undefined;\n if (source.kind === 'params') return source.nodes.find(n => n.name === name)?.type;\n if (source.kind === 'ref') {\n const model = modelMap.get(source.name);\n if (model) return resolveModelFields(model, modelMap).find(f => f.name === name)?.type;\n }\n if (source.kind === 'type' && source.node.kind === 'inlineObject') {\n return source.node.fields.find(f => f.name === name)?.type;\n }\n return undefined;\n}\n\n/** Return a YAML-quoted example value string for a param, preferring a default value when provided. */\nfunction paramExampleValue(type: ContractTypeNode | undefined, defaultValue?: string | number | boolean, randomExamples = false): string {\n if (defaultValue !== undefined) return `\"${defaultValue}\"`;\n if (!type) return '\"\"';\n if (type.kind === 'enum') return type.values.length > 0 ? `\"${type.values[0]}\"` : '\"\"';\n if (type.kind === 'literal') return `\"${type.value}\"`;\n if (type.kind !== 'scalar') return '\"\"';\n if (randomExamples) {\n const random = randomScalarTemplate(type.name);\n if (random !== undefined) return `\"${random}\"`;\n }\n switch (type.name) {\n case 'uuid':\n return '\"00000000-0000-0000-0000-000000000000\"';\n case 'email':\n return '\"user@example.com\"';\n case 'url':\n return '\"https://example.com\"';\n case 'number':\n case 'int':\n case 'bigint':\n return '\"0\"';\n case 'boolean':\n return '\"true\"';\n case 'date':\n return '\"2024-01-01\"';\n case 'time':\n return '\"00:00:00\"';\n case 'datetime':\n return '\"2024-01-01T00:00:00Z\"';\n case 'duration':\n return '\"PT1H\"';\n default:\n return '\"\"';\n }\n}\n\n/** Bruno faker template for a scalar type, or undefined when no clean equivalent exists (date, time, duration, raw string). */\nfunction randomScalarTemplate(name: string): string | undefined {\n switch (name) {\n case 'uuid':\n return '{{$randomUUID}}';\n case 'email':\n return '{{$randomEmail}}';\n case 'url':\n return '{{$randomUrl}}';\n case 'number':\n case 'int':\n case 'bigint':\n return '{{$randomInt}}';\n case 'boolean':\n return '{{$randomBoolean}}';\n case 'datetime':\n return '{{$isoTimestamp}}';\n default:\n return undefined;\n }\n}\n\n// ─── Body helpers ──────────────────────────────────────────────────────────\n\n/**\n * Recursively build an example JSON value from a ContractTypeNode.\n *\n * When `randomExamples` is true we substitute Bruno faker templates only for\n * scalars whose JSON representation is a string (uuid/email/url/datetime).\n * Numbers and booleans stay deterministic — embedding `{{$randomInt}}` as a\n * bare JSON number would require sentinel-stripping the surrounding quotes,\n * and the body skeleton is meant as a starting point users edit anyway.\n */\nfunction typeToExampleValue(type: ContractTypeNode, modelMap: Map<string, ModelNode>, randomExamples = false): unknown {\n switch (type.kind) {\n case 'scalar':\n switch (type.name) {\n case 'string':\n return '';\n case 'email':\n return randomExamples ? '{{$randomEmail}}' : 'user@example.com';\n case 'url':\n return randomExamples ? '{{$randomUrl}}' : 'https://example.com';\n case 'uuid':\n return randomExamples ? '{{$randomUUID}}' : '00000000-0000-0000-0000-000000000000';\n case 'number':\n case 'int':\n case 'bigint':\n return 0;\n case 'boolean':\n return true;\n case 'date':\n return '2024-01-01';\n case 'time':\n return '00:00:00';\n case 'datetime':\n return randomExamples ? '{{$isoTimestamp}}' : '2024-01-01T00:00:00Z';\n case 'duration':\n return 'PT1H';\n case 'null':\n return null;\n default:\n return null;\n }\n case 'enum':\n return type.values[0] ?? '';\n case 'literal':\n return type.value;\n case 'array':\n return [typeToExampleValue(type.item, modelMap, randomExamples)];\n case 'tuple':\n return type.items.map(t => typeToExampleValue(t, modelMap, randomExamples));\n case 'record':\n return {};\n case 'union':\n return type.members.length > 0 ? typeToExampleValue(type.members[0]!, modelMap, randomExamples) : null;\n case 'discriminatedUnion':\n return type.members.length > 0 ? typeToExampleValue(type.members[0]!, modelMap, randomExamples) : null;\n case 'intersection':\n return {};\n case 'ref': {\n const model = modelMap.get(type.name);\n if (!model) return {};\n // Type alias — recurse into the aliased type\n if (model.type) return typeToExampleValue(model.type, modelMap, randomExamples);\n return modelToExampleObject(model, modelMap, randomExamples);\n }\n case 'lazy':\n return typeToExampleValue(type.inner, modelMap, randomExamples);\n case 'inlineObject':\n return fieldsToExampleObject(type.fields, modelMap, randomExamples);\n default:\n return null;\n }\n}\n\n/** Build an example object from a ModelNode's fields (including inherited base fields). */\nfunction modelToExampleObject(model: ModelNode, modelMap: Map<string, ModelNode>, randomExamples = false): Record<string, unknown> {\n return fieldsToExampleObject(resolveModelFields(model, modelMap), modelMap, randomExamples);\n}\n\n/** Build an example object from a list of FieldNodes. Excludes readonly fields; uses defaults when available; omits optional fields that have no default so they don't appear in the JSON output. */\nfunction fieldsToExampleObject(fields: FieldNode[], modelMap: Map<string, ModelNode>, randomExamples = false): Record<string, unknown> {\n const obj: Record<string, unknown> = {};\n for (const field of fields) {\n if (field.visibility === 'readonly') continue;\n if (field.default !== undefined) {\n obj[field.name] = field.default;\n } else if (!field.optional) {\n obj[field.name] = typeToExampleValue(field.type, modelMap, randomExamples);\n }\n }\n return obj;\n}\n\n// ─── Path helpers ──────────────────────────────────────────────────────────\n\n/** Convert /users/{id}/posts → /users/:id/posts (Bruno path parameter syntax) */\nfunction openCollectionPath(path: string): string {\n return path.replace(/\\{([a-zA-Z_][a-zA-Z0-9_]*)\\}/g, ':$1');\n}\n\n/** Convert \"Create an Offer\" → create-an-offer (for .yml file names) */\nexport function slugifyName(name: string): string {\n const result = name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-|-$/g, '');\n return result || 'request';\n}\n\n/** Convert /users/{id}/posts → users-id-posts (for .yml file names) */\nexport function sanitizePath(path: string): string {\n const result = path\n .replace(/^\\//, '')\n .replace(/\\{([a-zA-Z_][a-zA-Z0-9_]*)\\}/g, '$1')\n .replace(/\\//g, '-')\n .replace(/[^a-zA-Z0-9-]/g, '')\n .replace(/-+/g, '-')\n .replace(/^-|-$/g, '');\n return result || 'root';\n}\n\n/** Extract param names from a URL template, e.g. /users/{id} → ['id'] */\nfunction extractPathParamNames(path: string): string[] {\n return [...path.matchAll(/\\{([a-zA-Z_][a-zA-Z0-9_]*)\\}/g)].map(m => m[1]!);\n}\n\n/** Derive folder name from op file path, e.g. src/users.op → users */\nfunction deriveFolderName(file: string): string {\n return basename(file).replace(/\\.(op|ck)$/, '');\n}\n\n/**\n * Wrap a string in YAML double quotes if it contains characters that require quoting\n * (flow indicators, colons, braces, etc.).\n */\nfunction yamlString(value: string): string {\n if (/[:{}[\\],&*#?|<>=!%@`\"']/.test(value) || /^\\s|\\s$/.test(value)) {\n return `\"${value.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"')}\"`;\n }\n return value;\n}\n"],"mappings":";;;;AAAA,SAASA,SAASC,YAAAA,WAAUC,eAAe;AAC3C,SAASC,YAAYC,cAAcC,QAAQC,aAAaC,iBAAiB;;;ACUzE,SAASC,iBAAiBC,kBAAkBC,qBAAqB;AACjE,SAASC,gBAAgB;AACzB,SAASC,SAASC,WAAWC,aAAaC,qBAAqB;AASxD,IAAMC,oBAAoB;AAqD1B,SAASC,uBAAuBC,OAAqBC,SAA8B;AACtF,QAAMC,QAA8B,CAAA;AAEpC,QAAMC,WAAWC,cAAcH,QAAQI,iBAAiB,CAAA,CAAE;AAC1D,QAAMC,WAAWL,QAAQM;AACzB,QAAMC,gBAAgBF,UAAUE,gBAAgBF,SAASG,UAAUH,SAASE,aAAa,IAAIE;AAC7F,QAAMC,iBAAiBV,QAAQU,kBAAkB;AACjD,QAAMC,kBAAkBX,QAAQW,mBAAmB;AAEnDV,QAAMW,KAAK;IAAEC,cAAc;IAAsBC,SAASC,uBAAuBf,QAAQgB,gBAAgBT,aAAAA;EAAe,CAAA;AACxH,aAAWU,WAAWC,iBAAiBlB,QAAQmB,cAAcZ,aAAAA,GAAgB;AACzEN,UAAMW,KAAKK,OAAAA;EACf;AAKA,QAAMG,cAAc;OAAIrB;IAAOsB,KAAK,CAACC,GAAGC,MAAAA;AACpC,UAAMC,QAAQF,EAAEG,KAAK,MAAA,KAAWC,iBAAiBJ,EAAEK,IAAI;AACvD,UAAMC,QAAQL,EAAEE,KAAK,MAAA,KAAWC,iBAAiBH,EAAEI,IAAI;AACvD,UAAME,MAAML,MAAMM,cAAcF,KAAAA;AAChC,QAAIC,QAAQ,EAAG,QAAOA;AACtB,YAAQP,EAAEG,KAAK,SAAA,KAAc,IAAIK,cAAcP,EAAEE,KAAK,SAAA,KAAc,EAAA;EACxE,CAAA;AAEA,WAASM,UAAU,GAAGA,UAAUX,YAAYY,QAAQD,WAAW;AAC3D,UAAME,OAAOb,YAAYW,OAAAA;AACzB,UAAMG,SAASD,KAAKR,KAAK,MAAA,IAAUU,YAAYF,KAAKR,KAAK,MAAA,CAAO,IAAIC,iBAAiBO,KAAKN,IAAI;AAC9F,UAAMS,eAAeH,KAAKR,KAAK,MAAA,KAAWS,QAAQG,OAAO,CAAA,EAAGC,YAAW,KAAML,KAAKR,KAAK,MAAA,KAAWS,QAAQK,MAAM,CAAA;AAEhHtC,UAAMW,KAAK;MAAEC,cAAc,GAAGqB,MAAAA;MAAqBpB,SAAS0B,mBAAmBJ,aAAaL,UAAU,CAAA;IAAG,CAAA;AAEzG,UAAMU,UAAUR,KAAKR,KAAK,SAAA;AAC1B,UAAMiB,cAAcD,UAAUN,YAAYM,OAAAA,IAAWhC;AACrD,UAAMkC,aAAaD,cAAc,GAAGR,MAAAA,IAAUQ,WAAAA,KAAgBR;AAE9D,QAAIQ,aAAa;AACb,YAAME,qBAAqBH,QAASJ,OAAO,CAAA,EAAGC,YAAW,IAAKG,QAASF,MAAM,CAAA;AAC7EtC,YAAMW,KAAK;QAAEC,cAAc,GAAG8B,UAAAA;QAAyB7B,SAAS0B,mBAAmBI,oBAAoB,CAAA;MAAG,CAAA;IAC9G;AAGA,UAAMC,WAAoI,CAAA;AAC1I,eAAWC,SAASb,KAAKc,QAAQ;AAC7B,iBAAWC,MAAMF,MAAMG,YAAY;AAC/B,YAAI,CAACtC,mBAAmBuC,iBAAiBJ,OAAOE,EAAAA,EAAIG,SAAS,UAAA,EAAa;AAC1EN,iBAASjC,KAAK;UAAEkC;UAAOE;UAAII,aAAaJ,GAAGK,QAAQP,MAAMQ;QAAK,CAAA;MAClE;IACJ;AACAT,aAASxB,KAAK,CAACC,GAAGC,MAAMD,EAAE8B,YAAYtB,cAAcP,EAAE6B,WAAW,CAAA;AAEjE,QAAIG,MAAM;AACV,eAAW,EAAET,OAAOE,IAAII,YAAW,KAAMP,UAAU;AAC/C,YAAMW,WAAWR,GAAGK,OAAO,GAAGlB,YAAYa,GAAGK,IAAI,CAAA,SAAU,GAAGL,GAAGS,MAAM,IAAIC,aAAaZ,MAAMQ,IAAI,CAAA;AAClG,UAAIxC,UAAU6C,oBAAoBb,OAAOE,IAAII,aAAaG,KAAKrD,UAAU+B,MAAM1B,eAAeG,cAAAA;AAC9F,YAAMkD,iBAAiBZ,GAAGa,cAAc,OAAA;AACxC,UAAID,mBAAmBnD,QAAW;AAC9BK,kBAAUgD,gBAAgBhD,SAAS8C,cAAAA;MACvC;AACA3D,YAAMW,KAAK;QAAEC,cAAc,GAAG8B,UAAAA,IAAca,QAAAA;QAAY1C;MAAQ,CAAA;AAChEyC;IACJ;EACJ;AAEA,QAAMQ,eAAe;OAAI9D,MAAM+D,IAAIC,CAAAA,MAAKA,EAAEpD,YAAY;IAAGhB;IAAmBwB,KAAI;AAChFpB,QAAMW,KAAK;IACPC,cAAchB;IACdiB,SAASoD,KAAKC,UAAU;MAAElE,OAAO8D;IAAa,GAAG,MAAM,CAAA,IAAK;EAChE,CAAA;AAEA,SAAO9D;AACX;AAvEgBH;AA0ET,SAASsE,cAActD,SAAe;AACzC,MAAI;AACA,UAAMuD,SAASH,KAAKI,MAAMxD,OAAAA;AAC1B,QAAIyD,MAAMC,QAAQH,QAAQpE,KAAAA,KAAUoE,OAAOpE,MAAMwE,MAAM,CAACR,MAAe,OAAOA,MAAM,QAAA,GAAW;AAC3F,aAAOI,OAAOpE;IAClB;EACJ,QAAQ;EAER;AACA,SAAO,CAAA;AACX;AAVgBmE;AAchB,SAASM,UAAUC,MAAeC,UAAiB;AAC/C,MACIA,aAAa,QACb,OAAOA,aAAa,YACpB,CAACL,MAAMC,QAAQI,QAAAA,KACfD,SAAS,QACT,OAAOA,SAAS,YAChB,CAACJ,MAAMC,QAAQG,IAAAA,GACjB;AACE,UAAME,SAAkC;MAAE,GAAIF;IAAiC;AAC/E,eAAW,CAACG,KAAKC,GAAAA,KAAQC,OAAOC,QAAQL,QAAAA,GAAsC;AAC1EC,aAAOC,GAAAA,IAAOJ,UAAUG,OAAOC,GAAAA,GAAMC,GAAAA;IACzC;AACA,WAAOF;EACX;AACA,SAAOD;AACX;AAhBSF;AA6BF,SAASZ,gBAAgBoB,eAAuBC,mBAAyB;AAC5E,QAAMC,iBAAiBC,UAAUF,iBAAAA;AACjC,MAAIC,mBAAmB,QAAQ,OAAOA,mBAAmB,YAAYb,MAAMC,QAAQY,cAAAA,GAAiB;AAChG,WAAOF;EACX;AACA,QAAMI,SAASZ,UAAUW,UAAUH,aAAAA,GAAgBE,cAAAA;AACnD,SAAOG,cAAcD,QAAQ;IAAEE,WAAW;EAAE,CAAA;AAChD;AAPgB1B;AAWhB,SAAS/C,uBAAuBsC,MAAcoC,QAA4B;AACtE,QAAMC,QAAQ;IAAC;IAA2B;IAAS,WAAWC,WAAWtC,IAAAA,CAAAA;;AACzE,MAAIoC,QAAQ;AACRC,UAAM9E,KAAK,EAAE;AACb8E,UAAM9E,KAAK,UAAU;AACrB8E,UAAM9E,KAAI,GAAIgF,gBAAgBH,QAAQ,IAAA,CAAA;EAC1C;AACAC,QAAM9E,KAAK,EAAE;AACb,SAAO8E,MAAMG,KAAK,IAAA;AACtB;AATS9E;AAWT,SAASG,iBACLC,cACAsE,QAAuC;AAEvC,MAAItE,gBAAgB6D,OAAOc,KAAK3E,YAAAA,EAAca,SAAS,GAAG;AACtD,WAAOgD,OAAOC,QAAQ9D,YAAAA,EAAc6C,IAAI,CAAC,CAACX,MAAM0C,SAAAA,OAAgB;MAC5DlF,cAAc,gBAAgBwC,IAAAA;MAC9BvC,SAASkF,cAAcC,eAAe5C,IAAAA,GAAO0C,SAAAA;IACjD,EAAA;EACJ;AACA,QAAMG,cAAsC;IAAEC,SAAS;EAAwB;AAC/E,MAAIV,QAAQ;AACR,eAAWW,WAAWC,gBAAgBZ,MAAAA,EAASS,aAAYE,OAAAA,IAAW;EAC1E;AACA,SAAO;IAAC;MAAEvF,cAAc;MAA0BC,SAASkF,cAAc,SAASE,WAAAA;IAAa;;AACnG;AAfShF;AAiBT,SAAS8E,cAAc3C,MAAc0C,WAAkC;AACnE,QAAML,QAAQ;IAAC,SAASrC,IAAAA;IAAQ;;AAChC,aAAW,CAACyB,KAAKwB,KAAAA,KAAUtB,OAAOC,QAAQc,SAAAA,GAAY;AAClD,UAAMQ,UAAUC,OAAOF,KAAAA,EAAOG,QAAQ,OAAO,MAAA,EAAQA,QAAQ,MAAM,KAAA;AACnEf,UAAM9E,KAAK,aAAakE,GAAAA,EAAK;AAC7BY,UAAM9E,KAAK,eAAe2F,OAAAA,GAAU;EACxC;AACAb,QAAM9E,KAAK,EAAE;AACb,SAAO8E,MAAMG,KAAK,IAAA;AACtB;AATSG;AAWT,SAASC,eAAeS,QAAc;AAClC,SAAOA,OAAOrE,OAAO,CAAA,EAAGC,YAAW,IAAKoE,OAAOnE,MAAM,CAAA;AACzD;AAFS0D;AAIT,SAASzD,mBAAmBa,MAAcE,KAAW;AACjD,SAAO;IAAC;IAAS,WAAWoC,WAAWtC,IAAAA,CAAAA;IAAS;IAAkB,UAAUE,GAAAA;IAAO;IAAIsC,KAAK,IAAA;AAChG;AAFSrD;AAIT,SAASmB,oBACLb,OACAE,IACAK,MACAE,KACArD,UACA+B,MACA1B,eACAG,iBAAiB,OAAK;AAEtB,QAAMgF,QAAkB,CAAA;AAExBA,QAAM9E,KAAK,OAAO;AAClB8E,QAAM9E,KAAK,WAAW+E,WAAWtC,IAAAA,CAAAA,EAAO;AACxCqC,QAAM9E,KAAK,cAAc;AACzB8E,QAAM9E,KAAK,UAAU2C,GAAAA,EAAK;AAC1BmC,QAAM9E,KAAK,EAAE;AACb8E,QAAM9E,KAAK,OAAO;AAClB8E,QAAM9E,KAAK,aAAaoC,GAAGS,OAAOnB,YAAW,CAAA,EAAI;AACjDoD,QAAM9E,KAAK,UAAU+E,WAAW,cAAcgB,mBAAmB7D,MAAMQ,IAAI,CAAA,EAAG,CAAA,EAAG;AAIjF,QAAMsD,aAA6DC,sBAAsB/D,MAAMQ,IAAI,EAAEU,IAAI8C,CAAAA,OAAM;IAC3GzD,MAAMyD;IACNC,MAAMC,cAAclE,MAAMmE,QAAQH,GAAG5G,QAAAA;IACrCgH,UAAU;IACVC,MAAM;EACV,EAAA;AACA,QAAMC,cAA8DpE,GAAGqE,QACjEC,kBAAkBtE,GAAGqE,OAAOnH,QAAAA,EAAU8D,IAAIuD,CAAAA,OAAM;IAAE,GAAGA;IAAGJ,MAAM;EAAiB,EAAA,IAC/E,CAAA;AACN,QAAMK,YAAY;OAAIZ;OAAeQ;;AAErC,MAAII,UAAUxF,SAAS,GAAG;AACtB0D,UAAM9E,KAAK,WAAW;AACtB,eAAW6G,KAAKD,WAAW;AACvB9B,YAAM9E,KAAK,eAAe6G,EAAEpE,IAAI,EAAE;AAClCqC,YAAM9E,KAAK,gBAAgB8G,kBAAkBD,EAAEV,MAAMU,EAAEE,SAASjH,cAAAA,CAAAA,EAAiB;AACjFgF,YAAM9E,KAAK,eAAe6G,EAAEN,IAAI,EAAE;AAClC,UAAIM,EAAEP,YAAYO,EAAEN,SAAS,QAASzB,OAAM9E,KAAK,sBAAsB;IAC3E;EACJ;AAGA,MAAIoC,GAAG4E,SAAS;AACZ,UAAMC,gBAAgBP,kBAAkBtE,GAAG4E,SAAS1H,QAAAA;AACpD,QAAI2H,cAAc7F,SAAS,GAAG;AAC1B0D,YAAM9E,KAAK,YAAY;AACvB,iBAAWkH,KAAKD,eAAe;AAC3BnC,cAAM9E,KAAK,eAAekH,EAAEzE,IAAI,EAAE;AAClCqC,cAAM9E,KAAK,gBAAgB8G,kBAAkBI,EAAEf,MAAMe,EAAEH,SAASjH,cAAAA,CAAAA,EAAiB;AACjF,YAAIoH,EAAEZ,SAAUxB,OAAM9E,KAAK,sBAAsB;MACrD;IACJ;EACJ;AAGA,MAAIL,eAAe;AACf,UAAMwH,WAAW9F,OAAO+F,gBAAgBlF,OAAOE,IAAIf,IAAAA,IAASe,GAAG+E,YAAYjF,MAAMiF;AACjF,QAAIA,aAAaE,eAAe;AAC5BvC,YAAM9E,KAAK,SAAS;AACpB8E,YAAM9E,KAAK,gBAAgB;IAC/B,OAAO;AACH8E,YAAM9E,KAAK,iBAAiB;IAChC;EACJ;AAGA,MAAIoC,GAAGkF,WAAWlF,GAAGkF,QAAQC,OAAOnG,SAAS,GAAG;AAC5C,UAAMoG,iBAA2E;MAC7E;MACA;MACA;;AAEJ,UAAMC,UACFD,eAAepE,IAAIsE,CAAAA,OAAMtF,GAAGkF,QAASC,OAAOI,KAAKhH,CAAAA,MAAKA,EAAEiH,gBAAgBF,EAAAA,CAAAA,EAAKC,KAAKhH,CAAAA,MAAKA,MAAMd,MAAAA,KAAcuC,GAAGkF,QAAQC,OAAO,CAAA;AAEjIzC,UAAM9E,KAAK,SAAS;AACpB,QAAIyH,QAAQG,gBAAgB,uBAAuB;AAC/C9C,YAAM9E,KAAK,0BAA0B;AACrC8E,YAAM9E,KAAK,cAAc;IAC7B,WAAWyH,QAAQG,gBAAgB,qCAAqC;AACpE9C,YAAM9E,KAAK,2BAA2B;AACtC8E,YAAM9E,KAAK,cAAc;IAC7B,OAAO;AACH,YAAM6H,OAAOvE,KAAKC,UAAUuE,mBAAmBL,QAAQM,UAAUzI,UAAUQ,cAAAA,GAAiB,MAAM,CAAA;AAClGgF,YAAM9E,KAAK,gBAAgB;AAC3B8E,YAAM9E,KAAK,aAAa;AACxB,iBAAWgI,YAAYH,KAAKI,MAAM,IAAA,GAAO;AACrCnD,cAAM9E,KAAK,SAASgI,QAAAA,EAAU;MAClC;IACJ;EACJ;AAGA,QAAME,iBAAiBC,oBAAoB/F,GAAGgG,SAAS;AACvD,QAAMC,mBAAmBjG,GAAGgG,UAAUT,KAAKW,CAAAA,MAAKA,EAAEC,eAAeL,cAAAA;AACjE,QAAMM,mBAAmBH,kBAAkBrB,WAAW,CAAA,GAAIyB,OAAOvB,CAAAA,MAAK,CAACA,EAAEZ,QAAQ;AACjF,MAAI4B,mBAAmBrI,QAAW;AAC9BiF,UAAM9E,KAAK,EAAE;AACb8E,UAAM9E,KAAK,UAAU;AACrB8E,UAAM9E,KAAK,eAAe;AAC1B8E,UAAM9E,KAAK,8BAA8B;AACzC8E,UAAM9E,KAAK,oBAAoB;AAG/B8E,UAAM9E,KAAK,iBAAiBkI,cAAAA,GAAiB;AAC7C,eAAWhB,KAAKsB,iBAAiB;AAC7B1D,YAAM9E,KAAK,kCAAkCkH,EAAEzE,KAAKiG,YAAW,CAAA,IAAM;AACrE5D,YAAM9E,KAAK,2BAA2B;AACtC8E,YAAM9E,KAAK,iBAAiB;IAChC;EACJ;AAGA,QAAM2I,OAAOC,iBAAiB1G,OAAOE,IAAIiG,gBAAAA;AACzC,MAAIM,MAAM;AACN7D,UAAM9E,KAAK,EAAE;AACb8E,UAAM9E,KAAK,UAAU;AACrB,eAAW6I,WAAWF,KAAKV,MAAM,IAAA,GAAO;AACpCnD,YAAM9E,KAAK,KAAK6I,OAAAA,EAAS;IAC7B;EACJ;AAEA/D,QAAM9E,KAAK,EAAE;AACb,SAAO8E,MAAMG,KAAK,IAAA;AACtB;AA/HSlC;AAkIT,SAASoF,oBAAoBC,WAA2B;AACpD,QAAMU,UAAUV,UAAUT,KAAKW,CAAAA,MAAKA,EAAEC,cAAc,OAAOD,EAAEC,aAAa,GAAA;AAC1E,SAAOO,SAASP,cAAcH,UAAU,CAAA,GAAIG;AAChD;AAHSJ;AAMT,SAASS,iBAAiB1G,OAAoBE,IAAqBiG,kBAAiC;AAChG,QAAMU,QAAkB,CAAA;AACxB,MAAI7G,MAAM8G,YAAaD,OAAM/I,KAAKkC,MAAM8G,YAAYC,KAAI,CAAA;AACxD,MAAI7G,GAAG4G,YAAaD,OAAM/I,KAAKoC,GAAG4G,YAAYC,KAAI,CAAA;AAClD,QAAMjC,UAAUqB,kBAAkBrB,WAAW,CAAA;AAC7C,MAAIA,QAAQ5F,SAAS,GAAG;AACpB,UAAM0D,QAAQ;MAAC;MAAwB;;AACvC,eAAWoC,KAAKF,SAAS;AACrB,YAAMkC,MAAMhC,EAAEZ,WAAW,aAAa;AACtC,YAAM6C,OAAOjC,EAAE8B,cAAc,WAAM9B,EAAE8B,WAAW,KAAK;AACrDlE,YAAM9E,KAAK,OAAOkH,EAAEzE,IAAI,OAAOyG,GAAAA,IAAOC,IAAAA,EAAM;IAChD;AACAJ,UAAM/I,KAAK8E,MAAMG,KAAK,IAAA,CAAA;EAC1B;AACA,SAAO8D,MAAM3H,SAAS,IAAI2H,MAAM9D,KAAK,MAAA,IAAUpF;AACnD;AAfS+I;AAoBT,SAAS5D,gBAAgBH,QAA6BuE,QAAc;AAChE,QAAMC,IAAID;AACV,MAAIvE,OAAOsB,SAAS,UAAUtB,OAAOA,WAAW,UAAU;AACtD,WAAO;MAAC,GAAGwE,CAAAA;MAAU,GAAGA,CAAAA;MAAmB,GAAGA,CAAAA;;EAClD;AACA,MAAIxE,OAAOsB,SAAS,UAAUtB,OAAOA,WAAW,SAAS;AACrD,WAAO;MAAC,GAAGwE,CAAAA;MAAU,GAAGA,CAAAA;MAAkB,GAAGA,CAAAA;MAA+B,GAAGA,CAAAA;;EACnF;AACA,MAAIxE,OAAOsB,SAAS,YAAYtB,OAAOyE,OAAO,UAAU;AACpD,UAAMC,aAAa1E,OAAOpC,QAAQ;AAClC,WAAO;MAAC,GAAG4G,CAAAA;MAAU,GAAGA,CAAAA;MAAmB,GAAGA,CAAAA,UAAWE,UAAAA;MAAc,GAAGF,CAAAA;MAA0B,GAAGA,CAAAA;;EAC3G;AACA,SAAO,CAAA;AACX;AAbSrE;AAgBT,SAASS,gBAAgBZ,QAA2B;AAChD,MAAIA,OAAOsB,SAAS,UAAUtB,OAAOA,WAAW,SAAU,QAAO;IAAC;;AAClE,MAAIA,OAAOsB,SAAS,UAAUtB,OAAOA,WAAW,QAAS,QAAO;IAAC;IAAY;;AAC7E,MAAIA,OAAOsB,SAAS,SAAU,QAAO;IAAC;;AACtC,SAAO,CAAA;AACX;AALSV;AAST,SAASlG,cAAcC,eAAiC;AACpD,QAAM4D,MAAM,oBAAIoG,IAAAA;AAChB,aAAWnI,QAAQ7B,eAAe;AAC9B,eAAWiK,SAASpI,KAAKqI,QAAQ;AAC7BtG,UAAIuG,IAAIF,MAAMhH,MAAMgH,KAAAA;IACxB;EACJ;AACA,SAAOrG;AACX;AARS7D;AAWT,SAASqK,mBAAmBH,OAAkBnK,UAAgC;AAC1E,QAAMuK,YAAyB,CAAA;AAC/B,MAAIJ,MAAMK,OAAO;AACb,eAAW/F,QAAQ0F,MAAMK,OAAO;AAC5B,YAAMC,YAAYzK,SAAS0K,IAAIjG,IAAAA;AAC/B,UAAIgG,UAAWF,WAAU7J,KAAI,GAAI4J,mBAAmBG,WAAWzK,QAAAA,CAAAA;IACnE;EACJ;AACA,SAAO;OAAIuK;OAAcJ,MAAMQ;;AACnC;AATSL;AAqBT,SAASlD,kBAAkBwD,QAAqB5K,UAAgC;AAC5E,MAAI4K,OAAO3D,SAAS,UAAU;AAC1B,WAAO2D,OAAOC,MAAM/G,IAAI8C,CAAAA,OAAM;MAAEzD,MAAMyD,EAAEzD;MAAM0D,MAAMD,EAAEC;MAAMY,SAASb,EAAEa;MAAST,UAAUJ,EAAEI;IAAS,EAAA;EACzG;AACA,MAAI4D,OAAO3D,SAAS,OAAO;AACvB,UAAMkD,QAAQnK,SAAS0K,IAAIE,OAAOzH,IAAI;AACtC,QAAIgH,OAAO;AACP,aAAOG,mBAAmBH,OAAOnK,QAAAA,EAC5BmJ,OAAOpF,CAAAA,MAAKA,EAAE+G,eAAe,UAAA,EAC7BhH,IAAIC,CAAAA,OAAM;QAAEZ,MAAMY,EAAEZ;QAAM0D,MAAM9C,EAAE8C;QAAMY,SAAS1D,EAAE0D;QAAST,UAAUjD,EAAEiD;MAAS,EAAA;IAC1F;AAEA,UAAM7D,OAAOyH,OAAOzH,KAAKhB,OAAO,CAAA,EAAGiH,YAAW,IAAKwB,OAAOzH,KAAKd,MAAM,CAAA;AACrE,WAAO;MAAC;QAAEc;QAAM0D,MAAMtG;QAAWyG,UAAU;MAAM;;EACrD;AAEA,MAAI4D,OAAOG,KAAK9D,SAAS,gBAAgB;AACrC,WAAO2D,OAAOG,KAAKJ,OAAO7G,IAAIC,CAAAA,OAAM;MAAEZ,MAAMY,EAAEZ;MAAM0D,MAAM9C,EAAE8C;MAAMY,SAAS1D,EAAE0D;MAAST,UAAUjD,EAAEiD;IAAS,EAAA;EAC/G;AACA,SAAO,CAAA;AACX;AApBSI;AAuBT,SAASN,cAAc8D,QAAiCzH,MAAcnD,UAAgC;AAClG,MAAI,CAAC4K,OAAQ,QAAOrK;AACpB,MAAIqK,OAAO3D,SAAS,SAAU,QAAO2D,OAAOC,MAAMxC,KAAKzB,CAAAA,MAAKA,EAAEzD,SAASA,IAAAA,GAAO0D;AAC9E,MAAI+D,OAAO3D,SAAS,OAAO;AACvB,UAAMkD,QAAQnK,SAAS0K,IAAIE,OAAOzH,IAAI;AACtC,QAAIgH,MAAO,QAAOG,mBAAmBH,OAAOnK,QAAAA,EAAUqI,KAAKtE,CAAAA,MAAKA,EAAEZ,SAASA,IAAAA,GAAO0D;EACtF;AACA,MAAI+D,OAAO3D,SAAS,UAAU2D,OAAOG,KAAK9D,SAAS,gBAAgB;AAC/D,WAAO2D,OAAOG,KAAKJ,OAAOtC,KAAKtE,CAAAA,MAAKA,EAAEZ,SAASA,IAAAA,GAAO0D;EAC1D;AACA,SAAOtG;AACX;AAXSuG;AAcT,SAASU,kBAAkBX,MAAoCmE,cAA0CxK,iBAAiB,OAAK;AAC3H,MAAIwK,iBAAiBzK,OAAW,QAAO,IAAIyK,YAAAA;AAC3C,MAAI,CAACnE,KAAM,QAAO;AAClB,MAAIA,KAAKI,SAAS,OAAQ,QAAOJ,KAAKoE,OAAOnJ,SAAS,IAAI,IAAI+E,KAAKoE,OAAO,CAAA,CAAE,MAAM;AAClF,MAAIpE,KAAKI,SAAS,UAAW,QAAO,IAAIJ,KAAKT,KAAK;AAClD,MAAIS,KAAKI,SAAS,SAAU,QAAO;AACnC,MAAIzG,gBAAgB;AAChB,UAAM0K,SAASC,qBAAqBtE,KAAK1D,IAAI;AAC7C,QAAI+H,WAAW3K,OAAW,QAAO,IAAI2K,MAAAA;EACzC;AACA,UAAQrE,KAAK1D,MAAI;IACb,KAAK;AACD,aAAO;IACX,KAAK;AACD,aAAO;IACX,KAAK;AACD,aAAO;IACX,KAAK;IACL,KAAK;IACL,KAAK;AACD,aAAO;IACX,KAAK;AACD,aAAO;IACX,KAAK;AACD,aAAO;IACX,KAAK;AACD,aAAO;IACX,KAAK;AACD,aAAO;IACX,KAAK;AACD,aAAO;IACX;AACI,aAAO;EACf;AACJ;AAlCSqE;AAqCT,SAAS2D,qBAAqBhI,MAAY;AACtC,UAAQA,MAAAA;IACJ,KAAK;AACD,aAAO;IACX,KAAK;AACD,aAAO;IACX,KAAK;AACD,aAAO;IACX,KAAK;IACL,KAAK;IACL,KAAK;AACD,aAAO;IACX,KAAK;AACD,aAAO;IACX,KAAK;AACD,aAAO;IACX;AACI,aAAO5C;EACf;AACJ;AAnBS4K;AAgCT,SAAS3C,mBAAmB3B,MAAwB7G,UAAkCQ,iBAAiB,OAAK;AACxG,UAAQqG,KAAKI,MAAI;IACb,KAAK;AACD,cAAQJ,KAAK1D,MAAI;QACb,KAAK;AACD,iBAAO;QACX,KAAK;AACD,iBAAO3C,iBAAiB,qBAAqB;QACjD,KAAK;AACD,iBAAOA,iBAAiB,mBAAmB;QAC/C,KAAK;AACD,iBAAOA,iBAAiB,oBAAoB;QAChD,KAAK;QACL,KAAK;QACL,KAAK;AACD,iBAAO;QACX,KAAK;AACD,iBAAO;QACX,KAAK;AACD,iBAAO;QACX,KAAK;AACD,iBAAO;QACX,KAAK;AACD,iBAAOA,iBAAiB,sBAAsB;QAClD,KAAK;AACD,iBAAO;QACX,KAAK;AACD,iBAAO;QACX;AACI,iBAAO;MACf;IACJ,KAAK;AACD,aAAOqG,KAAKoE,OAAO,CAAA,KAAM;IAC7B,KAAK;AACD,aAAOpE,KAAKT;IAChB,KAAK;AACD,aAAO;QAACoC,mBAAmB3B,KAAKuE,MAAMpL,UAAUQ,cAAAA;;IACpD,KAAK;AACD,aAAOqG,KAAKwE,MAAMvH,IAAIwH,CAAAA,MAAK9C,mBAAmB8C,GAAGtL,UAAUQ,cAAAA,CAAAA;IAC/D,KAAK;AACD,aAAO,CAAC;IACZ,KAAK;AACD,aAAOqG,KAAK0E,QAAQzJ,SAAS,IAAI0G,mBAAmB3B,KAAK0E,QAAQ,CAAA,GAAKvL,UAAUQ,cAAAA,IAAkB;IACtG,KAAK;AACD,aAAOqG,KAAK0E,QAAQzJ,SAAS,IAAI0G,mBAAmB3B,KAAK0E,QAAQ,CAAA,GAAKvL,UAAUQ,cAAAA,IAAkB;IACtG,KAAK;AACD,aAAO,CAAC;IACZ,KAAK,OAAO;AACR,YAAM2J,QAAQnK,SAAS0K,IAAI7D,KAAK1D,IAAI;AACpC,UAAI,CAACgH,MAAO,QAAO,CAAC;AAEpB,UAAIA,MAAMtD,KAAM,QAAO2B,mBAAmB2B,MAAMtD,MAAM7G,UAAUQ,cAAAA;AAChE,aAAOgL,qBAAqBrB,OAAOnK,UAAUQ,cAAAA;IACjD;IACA,KAAK;AACD,aAAOgI,mBAAmB3B,KAAK4E,OAAOzL,UAAUQ,cAAAA;IACpD,KAAK;AACD,aAAOkL,sBAAsB7E,KAAK8D,QAAQ3K,UAAUQ,cAAAA;IACxD;AACI,aAAO;EACf;AACJ;AA7DSgI;AAgET,SAASgD,qBAAqBrB,OAAkBnK,UAAkCQ,iBAAiB,OAAK;AACpG,SAAOkL,sBAAsBpB,mBAAmBH,OAAOnK,QAAAA,GAAWA,UAAUQ,cAAAA;AAChF;AAFSgL;AAKT,SAASE,sBAAsBf,QAAqB3K,UAAkCQ,iBAAiB,OAAK;AACxG,QAAMmL,MAA+B,CAAC;AACtC,aAAWC,SAASjB,QAAQ;AACxB,QAAIiB,MAAMd,eAAe,WAAY;AACrC,QAAIc,MAAMnE,YAAYlH,QAAW;AAC7BoL,UAAIC,MAAMzI,IAAI,IAAIyI,MAAMnE;IAC5B,WAAW,CAACmE,MAAM5E,UAAU;AACxB2E,UAAIC,MAAMzI,IAAI,IAAIqF,mBAAmBoD,MAAM/E,MAAM7G,UAAUQ,cAAAA;IAC/D;EACJ;AACA,SAAOmL;AACX;AAXSD;AAgBT,SAASjF,mBAAmBrD,MAAY;AACpC,SAAOA,KAAKmD,QAAQ,iCAAiC,KAAA;AACzD;AAFSE;AAKF,SAASxE,YAAYkB,MAAY;AACpC,QAAMwB,SAASxB,KACViG,YAAW,EACX7C,QAAQ,eAAe,GAAA,EACvBA,QAAQ,UAAU,EAAA;AACvB,SAAO5B,UAAU;AACrB;AANgB1C;AAST,SAASuB,aAAaJ,MAAY;AACrC,QAAMuB,SAASvB,KACVmD,QAAQ,OAAO,EAAA,EACfA,QAAQ,iCAAiC,IAAA,EACzCA,QAAQ,OAAO,GAAA,EACfA,QAAQ,kBAAkB,EAAA,EAC1BA,QAAQ,OAAO,GAAA,EACfA,QAAQ,UAAU,EAAA;AACvB,SAAO5B,UAAU;AACrB;AATgBnB;AAYhB,SAASmD,sBAAsBvD,MAAY;AACvC,SAAO;OAAIA,KAAKyI,SAAS,+BAAA;IAAkC/H,IAAIgI,CAAAA,MAAKA,EAAE,CAAA,CAAE;AAC5E;AAFSnF;AAKT,SAASnF,iBAAiBC,MAAY;AAClC,SAAOsK,SAAStK,IAAAA,EAAM8E,QAAQ,cAAc,EAAA;AAChD;AAFS/E;AAQT,SAASiE,WAAWW,OAAa;AAC7B,MAAI,0BAA0B4F,KAAK5F,KAAAA,KAAU,UAAU4F,KAAK5F,KAAAA,GAAQ;AAChE,WAAO,IAAIA,MAAMG,QAAQ,OAAO,MAAA,EAAQA,QAAQ,MAAM,KAAA,CAAA;EAC1D;AACA,SAAOH;AACX;AALSX;;;AD7oBT,IAAMwG,SAA4B;EAC9BC,MAAM;EACNC,UAAU;EACV,MAAMC,gBAAgB,EAAEC,SAASC,cAAa,GAAIC,KAAG;AACjD,UAAM,EAAEC,MAAM,GAAGC,OAAAA,IAAWF,IAAIG;AAChC,UAAMC,OAAOF,OAAOG,UAAUC,QAAQN,IAAIO,SAASL,OAAOG,OAAO,IAAIL,IAAIO;AACzE,UAAMC,SAASF,QAAQF,MAAMF,OAAOO,UAAU,kBAAA;AAC9C,UAAMC,iBAAiBR,OAAOQ,kBAAkBC,UAASX,IAAIO,OAAO;AAEpEK,wBAAoBJ,MAAAA;AAEpB,UAAMK,QAAQC,uBAAuBhB,SAAS;MAC1CY;MACAX;MACAE;MACAc,gBAAgBb,OAAOa,kBAAkB;MACzCC,iBAAiBd,OAAOc;MACxBC,cAAcf,OAAOe;IACzB,CAAA;AACA,eAAW,EAAEC,cAAcC,QAAO,KAAMN,OAAO;AAC3Cb,UAAIoB,SAASd,QAAQE,QAAQU,YAAAA,GAAeC,OAAAA;IAChD;EACJ;AACJ;AAEA,IAAA,gBAAezB;AAcR,SAAS2B,kBACZnB,QACAK,SACAN,MAA+E;AAE/E,SAAO;IACHN,MAAM;IACNC,UAAU,SAAS0B,KAAKC,UAAUrB,MAAAA,CAAAA;IAClC,MAAML,gBAAgB,EAAEC,SAASC,cAAa,GAAIC,KAAG;AACjD,YAAMI,OAAOF,OAAOG,UAAUC,QAAQC,SAASL,OAAOG,OAAO,IAAIE;AACjE,YAAMC,SAASF,QAAQF,MAAMF,OAAOO,UAAU,kBAAA;AAC9C,YAAMC,iBAAiBR,OAAOQ,kBAAkBC,UAASJ,OAAAA;AAEzDK,0BAAoBJ,MAAAA;AAEpB,YAAMK,QAAQC,uBAAuBhB,SAAS;QAC1CY;QACAX;QACAE;QACAc,gBAAgBb,OAAOa,kBAAkB;QACzCC,iBAAiBd,OAAOc;QACxBC,cAAcf,OAAOe;MACzB,CAAA;AACA,iBAAW,EAAEC,cAAcC,QAAO,KAAMN,OAAO;AAC3Cb,YAAIoB,SAASd,QAAQE,QAAQU,YAAAA,GAAeC,OAAAA;MAChD;IACJ;EACJ;AACJ;AA5BgBE;AAqChB,SAAST,oBAAoBJ,QAAc;AACvC,QAAMgB,eAAelB,QAAQE,QAAQiB,iBAAAA;AACrC,MAAI,CAACC,WAAWF,YAAAA,EAAe;AAE/B,MAAIG;AACJ,MAAI;AACAA,cAAUC,cAAcC,aAAaL,cAAc,OAAA,CAAA;EACvD,QAAQ;AACJ;EACJ;AAEA,QAAMM,cAAc,oBAAIC,IAAAA;AACxB,aAAWC,OAAOL,SAAS;AACvB,UAAMM,MAAM3B,QAAQE,QAAQwB,GAAAA;AAC5B,QAAIN,WAAWO,GAAAA,GAAM;AACjBC,aAAOD,KAAK;QAAEE,OAAO;MAAK,CAAA;AAC1BL,kBAAYM,IAAIC,QAAQJ,GAAAA,CAAAA;IAC5B;EACJ;AAGA,aAAWK,OAAOR,aAAa;AAC3B,QAAIS,UAAUD;AACd,WAAOC,QAAQC,WAAWhC,MAAAA,KAAW+B,YAAY/B,QAAQ;AACrD,UAAI;AACA,YAAIiC,YAAYF,OAAAA,EAASG,WAAW,GAAG;AACnCC,oBAAUJ,OAAAA;AACVA,oBAAUF,QAAQE,OAAAA;QACtB,OAAO;AACH;QACJ;MACJ,QAAQ;AACJ;MACJ;IACJ;EACJ;AACJ;AApCS3B;","names":["resolve","basename","dirname","existsSync","readFileSync","rmSync","readdirSync","rmdirSync","resolveSecurity","resolveModifiers","SECURITY_NONE","basename","parse","parseYaml","stringify","stringifyYaml","MANIFEST_FILENAME","generateOpenCollection","roots","options","files","modelMap","buildModelMap","contractRoots","authOpts","auth","defaultScheme","schemes","undefined","randomExamples","includeInternal","push","relativePath","content","generateCollectionRoot","collectionName","envFile","generateEnvFiles","environments","sortedRoots","sort","a","b","aArea","meta","deriveFolderName","file","bArea","cmp","localeCompare","rootIdx","length","root","folder","slugifyName","displayName","charAt","toUpperCase","slice","generateFolderFile","subarea","subareaSlug","requestDir","subareaDisplayName","requests","route","routes","op","operations","resolveModifiers","includes","requestName","name","path","seq","fileName","method","sanitizePath","generateRequestFile","pluginOverride","pluginFiles","mergePluginFile","trackedPaths","map","f","JSON","stringify","parseManifest","parsed","parse","Array","isArray","every","deepMerge","base","override","result","key","val","Object","entries","generatedYaml","pluginFileContent","overrideParsed","parseYaml","merged","stringifyYaml","lineWidth","scheme","lines","yamlString","renderAuthBlock","join","keys","variables","renderEnvFile","displayNameFor","defaultVars","baseUrl","varName","authEnvVarNames","value","escaped","String","replace","envKey","openCollectionPath","pathParams","extractPathParamNames","n","type","findParamType","params","optional","kind","queryParams","query","expandParamSource","e","allParams","p","paramExampleValue","default","headers","headerEntries","h","security","resolveSecurity","SECURITY_NONE","request","bodies","preferredOrder","primary","ct","find","contentType","json","typeToExampleValue","bodyType","jsonLine","split","expectedStatus","pickAssertionStatus","responses","assertedResponse","r","statusCode","requiredHeaders","filter","toLowerCase","docs","buildRequestDocs","docLine","success","parts","description","trim","tag","desc","indent","i","in","headerName","Map","model","models","set","resolveModelFields","collected","bases","baseModel","get","fields","source","nodes","visibility","node","defaultValue","values","random","randomScalarTemplate","item","items","t","members","modelToExampleObject","inner","fieldsToExampleObject","obj","field","matchAll","m","basename","test","plugin","name","cacheKey","generateTargets","opRoots","contractRoots","ctx","auth","config","options","base","baseDir","resolve","rootDir","outDir","output","collectionName","basename","cleanupTrackedFiles","files","generateOpenCollection","randomExamples","includeInternal","environments","relativePath","content","emitFile","createBrunoPlugin","JSON","stringify","manifestPath","MANIFEST_FILENAME","existsSync","tracked","parseManifest","readFileSync","removedDirs","Set","rel","abs","rmSync","force","add","dirname","dir","current","startsWith","readdirSync","length","rmdirSync"]} | ||
| {"version":3,"sources":["../src/index.ts","../src/codegen-bruno.ts"],"sourcesContent":["import { resolve, basename, dirname } from 'node:path';\nimport { existsSync, readFileSync, rmSync, readdirSync, rmdirSync } from 'node:fs';\nimport { generateOpenCollection, MANIFEST_FILENAME, parseManifest } from './codegen-bruno.js';\nimport type { BrunoSecurityScheme } from './codegen-bruno.js';\nimport type { ContractKitPlugin, PluginValue } from '@contractkit/core';\n\n/** Configuration accepted by the Bruno plugin, both via `contractkit.config.json` and `createBrunoPlugin`. */\nexport interface BrunoPluginConfig {\n baseDir?: string;\n output?: string;\n collectionName?: string;\n /**\n * When true (default), example values use Bruno's faker templates\n * (`{{$randomUUID}}`, `{{$randomEmail}}`, etc.) so each send produces\n * fresh data. Set to false for deterministic placeholders.\n */\n randomExamples?: boolean;\n /**\n * Whether to generate request files for operations marked `internal`. Defaults to\n * `true` — Bruno collections are typically used by the team that owns the API and\n * benefit from full coverage. Set to `false` to omit internal ops.\n */\n includeInternal?: boolean;\n /**\n * Map of environment name → variables. Each entry produces a\n * `environments/<name>.yml` file. When omitted, a default `local.yml` is\n * emitted with `baseUrl=http://localhost:3000` and any auth env-var\n * placeholders. When provided, the default is replaced entirely; include\n * auth variables (e.g. `token`) explicitly if you need them.\n */\n environments?: Record<string, Record<string, unknown>>;\n}\n\n/** Full plugin options shape read from `ctx.options` — extends {@link BrunoPluginConfig} with the `auth` block. */\nexport interface BrunoPluginOptions extends BrunoPluginConfig {\n auth?: { defaultScheme: string; schemes?: Record<string, BrunoSecurityScheme> };\n}\n\n// ─── Default export: loaded via plugins array, reads config from ctx.options ─\n\n/**\n * Validates a `plugins.bruno` extension entry on an operation. The expected shape is\n * `{ template?: string }`, where `template` is a YAML fragment to deep-merge into the\n * generated request file (typically a `file://...` URL whose contents have already\n * been loaded by the CLI resolver).\n */\nexport function validateBrunoExtension(value: PluginValue): { errors?: string[] } | void {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) {\n return { errors: [`expected an object, got ${describe(value)}`] };\n }\n const errors: string[] = [];\n for (const [key, val] of Object.entries(value)) {\n if (key === 'template') {\n if (typeof val !== 'string') errors.push(`'template' must be a string, got ${describe(val)}`);\n } else {\n errors.push(`unknown field '${key}' (allowed: template)`);\n }\n }\n return errors.length ? { errors } : undefined;\n}\n\nfunction describe(value: PluginValue): string {\n if (value === null) return 'null';\n if (Array.isArray(value)) return 'array';\n return typeof value;\n}\n\nconst plugin: ContractKitPlugin = {\n name: 'bruno',\n cacheKey: 'bruno',\n validateExtension: validateBrunoExtension,\n async generateTargets({ opRoots, contractRoots }, ctx) {\n const { auth, ...config } = ctx.options as BrunoPluginOptions;\n const base = config.baseDir ? resolve(ctx.rootDir, config.baseDir) : ctx.rootDir;\n const outDir = resolve(base, config.output ?? 'bruno-collection');\n const collectionName = config.collectionName ?? basename(ctx.rootDir);\n\n cleanupTrackedFiles(outDir);\n\n const files = generateOpenCollection(opRoots, {\n collectionName,\n contractRoots,\n auth,\n randomExamples: config.randomExamples ?? true,\n includeInternal: config.includeInternal,\n environments: config.environments,\n });\n for (const { relativePath, content } of files) {\n ctx.emitFile(resolve(outDir, relativePath), content);\n }\n },\n};\n\nexport default plugin;\n\n// ─── Factory: for programmatic use with explicit config ────────────────────\n\n/**\n * Creates a Bruno plugin instance with explicit configuration, for programmatic use.\n *\n * Prefer the default export when loading via `contractkit.config.json`. Use this\n * factory when constructing the plugin in code (e.g. in tests or custom build scripts).\n *\n * @param config - Plugin configuration (output paths and feature flags).\n * @param rootDir - Absolute path used to resolve relative paths in `config`.\n * @param auth - Optional auth scheme configuration mirroring the `auth` key in plugin options.\n */\nexport function createBrunoPlugin(\n config: BrunoPluginConfig,\n rootDir: string,\n auth?: { defaultScheme: string; schemes?: Record<string, BrunoSecurityScheme> },\n): ContractKitPlugin {\n return {\n name: 'bruno',\n cacheKey: `bruno:${JSON.stringify(config)}`,\n validateExtension: validateBrunoExtension,\n async generateTargets({ opRoots, contractRoots }, ctx) {\n const base = config.baseDir ? resolve(rootDir, config.baseDir) : rootDir;\n const outDir = resolve(base, config.output ?? 'bruno-collection');\n const collectionName = config.collectionName ?? basename(rootDir);\n\n cleanupTrackedFiles(outDir);\n\n const files = generateOpenCollection(opRoots, {\n collectionName,\n contractRoots,\n auth,\n randomExamples: config.randomExamples ?? true,\n includeInternal: config.includeInternal,\n environments: config.environments,\n });\n for (const { relativePath, content } of files) {\n ctx.emitFile(resolve(outDir, relativePath), content);\n }\n },\n };\n}\n\n/**\n * Delete files this plugin generated on the previous run, leaving anything\n * the user added (custom .bru files, scripts, secrets, etc.) untouched.\n *\n * On first run — or after manual deletion of the manifest — nothing is\n * removed; stale files from prior versions linger until manually cleaned.\n */\nfunction cleanupTrackedFiles(outDir: string): void {\n const manifestPath = resolve(outDir, MANIFEST_FILENAME);\n if (!existsSync(manifestPath)) return;\n\n let tracked: string[];\n try {\n tracked = parseManifest(readFileSync(manifestPath, 'utf-8'));\n } catch {\n return;\n }\n\n const removedDirs = new Set<string>();\n for (const rel of tracked) {\n const abs = resolve(outDir, rel);\n if (existsSync(abs)) {\n rmSync(abs, { force: true });\n removedDirs.add(dirname(abs));\n }\n }\n\n // Walk up from each affected directory and remove it if empty, stopping at outDir.\n for (const dir of removedDirs) {\n let current = dir;\n while (current.startsWith(outDir) && current !== outDir) {\n try {\n if (readdirSync(current).length === 0) {\n rmdirSync(current);\n current = dirname(current);\n } else {\n break;\n }\n } catch {\n break;\n }\n }\n }\n}\n","import type {\n OpRootNode,\n OpRouteNode,\n OpOperationNode,\n OpResponseNode,\n ParamSource,\n ContractTypeNode,\n ContractRootNode,\n ModelNode,\n FieldNode,\n} from '@contractkit/core';\nimport { resolveSecurity, resolveModifiers, SECURITY_NONE } from '@contractkit/core';\nimport { basename } from 'path';\nimport { parse as parseYaml, stringify as stringifyYaml } from 'yaml';\n\n/** A single file produced by the Bruno codegen — a relative output path and its YAML content. */\nexport interface OpenCollectionFile {\n relativePath: string;\n content: string;\n}\n\n/** Manifest filename — tracks which files this plugin previously generated so subsequent runs can clean up only those, leaving any user-added files alone. */\nexport const MANIFEST_FILENAME = '.contractkit-bruno-manifest.json';\n\n/** Subset of a security scheme sufficient for Bruno auth generation (non-HMAC). */\nexport interface BrunoSecurityScheme {\n type: string; // \"http\" | \"apiKey\" | \"oauth2\" | \"openIdConnect\"\n scheme?: string; // \"bearer\" | \"basic\" (when type === \"http\")\n name?: string; // header/query param name (when type === \"apiKey\")\n in?: string; // \"header\" | \"query\" (when type === \"apiKey\")\n}\n\n/** Auth configuration passed to the Bruno codegen; drives collection-level auth and per-operation auth blocks. */\nexport interface BrunoAuthOptions {\n /** Name of the default scheme (from config.security.default) */\n defaultScheme?: string;\n /** Scheme definitions keyed by name (non-HMAC only) */\n schemes?: Record<string, BrunoSecurityScheme>;\n}\n\n/** Options controlling what {@link generateOpenCollection} emits. */\nexport interface OpenCollectionOptions {\n collectionName: string;\n contractRoots?: ContractRootNode[];\n auth?: BrunoAuthOptions;\n /**\n * When true, emit Bruno faker template strings (e.g. `{{$randomUUID}}`,\n * `{{$randomEmail}}`) for compatible scalar types so each send produces\n * fresh data. When false (default), use deterministic placeholders.\n */\n randomExamples?: boolean;\n /**\n * Whether to generate request files for operations marked `internal`. Defaults to\n * `true` — Bruno collections are typically used by the team that owns the API and\n * benefit from full coverage. Set to `false` to omit internal ops.\n */\n includeInternal?: boolean;\n /**\n * Map of environment name → variables. Each entry produces a\n * `environments/<name>.yml` file with the variables in declaration order.\n * Values are coerced to strings.\n *\n * When omitted, a default `environments/local.yml` is emitted with\n * `baseUrl=http://localhost:3000` plus any auth env-var placeholders the\n * default scheme requires. When provided, the default is replaced entirely\n * — auth vars are not auto-injected, so include them explicitly if needed.\n */\n environments?: Record<string, Record<string, unknown>>;\n}\n\n/**\n * Generates an OpenCollection (https://spec.opencollection.com/) API collection\n * from a set of operation roots. Produces opencollection.yml, an environment\n * file, and one .yml request file per operation.\n */\nexport function generateOpenCollection(roots: OpRootNode[], options: OpenCollectionOptions): OpenCollectionFile[] {\n const files: OpenCollectionFile[] = [];\n\n const modelMap = buildModelMap(options.contractRoots ?? []);\n const authOpts = options.auth;\n const defaultScheme = authOpts?.defaultScheme ? authOpts.schemes?.[authOpts.defaultScheme] : undefined;\n const randomExamples = options.randomExamples ?? false;\n const includeInternal = options.includeInternal ?? true;\n\n files.push({ relativePath: 'opencollection.yml', content: generateCollectionRoot(options.collectionName, defaultScheme) });\n for (const envFile of generateEnvFiles(options.environments, defaultScheme)) {\n files.push(envFile);\n }\n // Manifest is appended at the end so it lists every generated path including itself.\n\n // Roots are sorted by their top-level folder display name (then by subarea) so the\n // emitted `seq:` numbers — which drive Bruno's UI ordering — line up alphabetically.\n const sortedRoots = [...roots].sort((a, b) => {\n const aArea = a.meta['area'] ?? deriveFolderName(a.file);\n const bArea = b.meta['area'] ?? deriveFolderName(b.file);\n const cmp = aArea.localeCompare(bArea);\n if (cmp !== 0) return cmp;\n return (a.meta['subarea'] ?? '').localeCompare(b.meta['subarea'] ?? '');\n });\n\n for (let rootIdx = 0; rootIdx < sortedRoots.length; rootIdx++) {\n const root = sortedRoots[rootIdx]!;\n const folder = root.meta['area'] ? slugifyName(root.meta['area']) : deriveFolderName(root.file);\n const displayName = (root.meta['area'] ?? folder).charAt(0).toUpperCase() + (root.meta['area'] ?? folder).slice(1);\n\n files.push({ relativePath: `${folder}/folder.yml`, content: generateFolderFile(displayName, rootIdx + 1) });\n\n const subarea = root.meta['subarea'];\n const subareaSlug = subarea ? slugifyName(subarea) : undefined;\n const requestDir = subareaSlug ? `${folder}/${subareaSlug}` : folder;\n\n if (subareaSlug) {\n const subareaDisplayName = subarea!.charAt(0).toUpperCase() + subarea!.slice(1);\n files.push({ relativePath: `${requestDir}/folder.yml`, content: generateFolderFile(subareaDisplayName, 1) });\n }\n\n // Flatten and alphabetize within this folder before assigning `seq:`.\n const requests: Array<{ route: typeof root.routes[number]; op: typeof root.routes[number]['operations'][number]; requestName: string }> = [];\n for (const route of root.routes) {\n for (const op of route.operations) {\n if (!includeInternal && resolveModifiers(route, op).includes('internal')) continue;\n requests.push({ route, op, requestName: op.name ?? route.path });\n }\n }\n requests.sort((a, b) => a.requestName.localeCompare(b.requestName));\n\n let seq = 1;\n for (const { route, op, requestName } of requests) {\n const fileName = op.name ? `${slugifyName(op.name)}.yml` : `${op.method}-${sanitizePath(route.path)}.yml`;\n let content = generateRequestFile(route, op, requestName, seq, modelMap, root, defaultScheme, randomExamples);\n const brunoExt = op.pluginExtensions?.['bruno'];\n const pluginOverride = brunoExt && typeof brunoExt === 'object' && !Array.isArray(brunoExt)\n ? brunoExt['template']\n : undefined;\n if (typeof pluginOverride === 'string') {\n content = mergePluginFile(content, pluginOverride);\n }\n files.push({ relativePath: `${requestDir}/${fileName}`, content });\n seq++;\n }\n }\n\n const trackedPaths = [...files.map(f => f.relativePath), MANIFEST_FILENAME].sort();\n files.push({\n relativePath: MANIFEST_FILENAME,\n content: JSON.stringify({ files: trackedPaths }, null, 2) + '\\n',\n });\n\n return files;\n}\n\n/** Parse a previously-written manifest. Returns the list of relative paths to clean up. Returns [] if missing or unreadable so a stale/garbled manifest never blocks regeneration. */\nexport function parseManifest(content: string): string[] {\n try {\n const parsed = JSON.parse(content);\n if (Array.isArray(parsed?.files) && parsed.files.every((f: unknown) => typeof f === 'string')) {\n return parsed.files as string[];\n }\n } catch {\n // fall through\n }\n return [];\n}\n\n// ─── Plugin file merge ─────────────────────────────────────────────────────\n\nfunction deepMerge(base: unknown, override: unknown): unknown {\n if (\n override !== null &&\n typeof override === 'object' &&\n !Array.isArray(override) &&\n base !== null &&\n typeof base === 'object' &&\n !Array.isArray(base)\n ) {\n const result: Record<string, unknown> = { ...(base as Record<string, unknown>) };\n for (const [key, val] of Object.entries(override as Record<string, unknown>)) {\n result[key] = deepMerge(result[key], val);\n }\n return result;\n }\n return override;\n}\n\n/**\n * Deep-merges a YAML override string into a generated YAML string.\n *\n * Objects are merged recursively; arrays and scalars in the override replace the\n * generated value entirely. If `pluginFileContent` is not a YAML mapping (e.g. it\n * is a scalar or a list), the generated content is returned unchanged.\n *\n * @param generatedYaml - The YAML string produced by the Bruno codegen.\n * @param pluginFileContent - The YAML override string to merge in.\n * @returns The merged YAML string, or `generatedYaml` if the override is not a mapping.\n */\nexport function mergePluginFile(generatedYaml: string, pluginFileContent: string): string {\n const overrideParsed = parseYaml(pluginFileContent);\n if (overrideParsed === null || typeof overrideParsed !== 'object' || Array.isArray(overrideParsed)) {\n return generatedYaml;\n }\n const merged = deepMerge(parseYaml(generatedYaml), overrideParsed);\n return stringifyYaml(merged, { lineWidth: 0 });\n}\n\n// ─── File generators ───────────────────────────────────────────────────────\n\nfunction generateCollectionRoot(name: string, scheme?: BrunoSecurityScheme): string {\n const lines = [`opencollection: \"1.0.0\"`, `info:`, ` name: ${yamlString(name)}`];\n if (scheme) {\n lines.push(``);\n lines.push(`request:`);\n lines.push(...renderAuthBlock(scheme, ' '));\n }\n lines.push(``);\n return lines.join('\\n');\n}\n\nfunction generateEnvFiles(\n environments: Record<string, Record<string, unknown>> | undefined,\n scheme: BrunoSecurityScheme | undefined,\n): OpenCollectionFile[] {\n if (environments && Object.keys(environments).length > 0) {\n return Object.entries(environments).map(([name, variables]) => ({\n relativePath: `environments/${name}.yml`,\n content: renderEnvFile(displayNameFor(name), variables),\n }));\n }\n const defaultVars: Record<string, string> = { baseUrl: 'http://localhost:3000' };\n if (scheme) {\n for (const varName of authEnvVarNames(scheme)) defaultVars[varName] = '';\n }\n return [{ relativePath: 'environments/local.yml', content: renderEnvFile('Local', defaultVars) }];\n}\n\nfunction renderEnvFile(name: string, variables: Record<string, unknown>): string {\n const lines = [`name: ${name}`, `variables:`];\n for (const [key, value] of Object.entries(variables)) {\n const escaped = String(value).replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"');\n lines.push(` - name: ${key}`);\n lines.push(` value: \"${escaped}\"`);\n }\n lines.push(``);\n return lines.join('\\n');\n}\n\nfunction displayNameFor(envKey: string): string {\n return envKey.charAt(0).toUpperCase() + envKey.slice(1);\n}\n\nfunction generateFolderFile(name: string, seq: number): string {\n return [`info:`, ` name: ${yamlString(name)}`, ` type: folder`, ` seq: ${seq}`, ``].join('\\n');\n}\n\nfunction generateRequestFile(\n route: OpRouteNode,\n op: OpOperationNode,\n name: string,\n seq: number,\n modelMap: Map<string, ModelNode>,\n root?: OpRootNode,\n defaultScheme?: BrunoSecurityScheme,\n randomExamples = false,\n): string {\n const lines: string[] = [];\n\n lines.push(`info:`);\n lines.push(` name: ${yamlString(name)}`);\n lines.push(` type: http`);\n lines.push(` seq: ${seq}`);\n lines.push(``);\n lines.push(`http:`);\n lines.push(` method: ${op.method.toUpperCase()}`);\n lines.push(` url: ${yamlString(`{{baseUrl}}${openCollectionPath(route.path)}`)}`);\n\n // Params — flat array with type: \"path\" | \"query\". Optional query params are\n // emitted with disabled: true so users opt in before sending.\n const pathParams: Array<ParamEntry & { kind: 'path' | 'query' }> = extractPathParamNames(route.path).map(n => ({\n name: n,\n type: findParamType(route.params, n, modelMap),\n optional: false,\n kind: 'path' as const,\n }));\n const queryParams: Array<ParamEntry & { kind: 'path' | 'query' }> = op.query\n ? expandParamSource(op.query, modelMap).map(e => ({ ...e, kind: 'query' as const }))\n : [];\n const allParams = [...pathParams, ...queryParams];\n\n if (allParams.length > 0) {\n lines.push(` params:`);\n for (const p of allParams) {\n lines.push(` - name: ${p.name}`);\n lines.push(` value: ${paramExampleValue(p.type, p.default, randomExamples)}`);\n lines.push(` type: ${p.kind}`);\n if (p.optional && p.kind === 'query') lines.push(` disabled: true`);\n }\n }\n\n // Headers\n if (op.headers) {\n const headerEntries = expandParamSource(op.headers, modelMap);\n if (headerEntries.length > 0) {\n lines.push(` headers:`);\n for (const h of headerEntries) {\n lines.push(` - name: ${h.name}`);\n lines.push(` value: ${paramExampleValue(h.type, h.default, randomExamples)}`);\n if (h.optional) lines.push(` disabled: true`);\n }\n }\n }\n\n // Auth — inside http block; inherit collection default unless this op is explicitly public\n if (defaultScheme) {\n const security = root ? resolveSecurity(route, op, root) : (op.security ?? route.security);\n if (security === SECURITY_NONE) {\n lines.push(` auth:`);\n lines.push(` type: none`);\n } else {\n lines.push(` auth: inherit`);\n }\n }\n\n // Body — Bruno supports a single body per request, so prefer JSON, then form-urlencoded, then multipart.\n if (op.request && op.request.bodies.length > 0) {\n const preferredOrder: Array<(typeof op.request.bodies)[number]['contentType']> = [\n 'application/json',\n 'application/x-www-form-urlencoded',\n 'multipart/form-data',\n ];\n const primary =\n preferredOrder.map(ct => op.request!.bodies.find(b => b.contentType === ct)).find(b => b !== undefined) ?? op.request.bodies[0]!;\n\n lines.push(` body:`);\n if (primary.contentType === 'multipart/form-data') {\n lines.push(` type: multipart-form`);\n lines.push(` data: []`);\n } else if (primary.contentType === 'application/x-www-form-urlencoded') {\n lines.push(` type: form-urlencoded`);\n lines.push(` data: []`);\n } else {\n const json = JSON.stringify(typeToExampleValue(primary.bodyType, modelMap, randomExamples), null, 2);\n lines.push(` type: json`);\n lines.push(` data: |`);\n for (const jsonLine of json.split('\\n')) {\n lines.push(` ${jsonLine}`);\n }\n }\n }\n\n // runtime.assertions — auto-generate a status-code check and presence checks for required response headers.\n const expectedStatus = pickAssertionStatus(op.responses);\n const assertedResponse = op.responses.find(r => r.statusCode === expectedStatus);\n const requiredHeaders = (assertedResponse?.headers ?? []).filter(h => !h.optional);\n if (expectedStatus !== undefined) {\n lines.push(``);\n lines.push(`runtime:`);\n lines.push(` assertions:`);\n lines.push(` - expression: res.status`);\n lines.push(` operator: eq`);\n // Always quote — the OpenCollection schema types `value` as a string,\n // so we must keep \"200\" from being parsed as YAML number 200.\n lines.push(` value: \"${expectedStatus}\"`);\n for (const h of requiredHeaders) {\n lines.push(` - expression: res.headers[\"${h.name.toLowerCase()}\"]`);\n lines.push(` operator: isDefined`);\n lines.push(` value: \"\"`);\n }\n }\n\n // docs — combine route- and operation-level descriptions plus the declared response-header summary.\n const docs = buildRequestDocs(route, op, assertedResponse);\n if (docs) {\n lines.push(``);\n lines.push(`docs: |-`);\n for (const docLine of docs.split('\\n')) {\n lines.push(` ${docLine}`);\n }\n }\n\n lines.push(``);\n return lines.join('\\n');\n}\n\n/** Pick the response whose status code we'll assert against. Prefers the first declared 2xx; otherwise falls back to the first declared response. Returns undefined if no responses are declared. */\nfunction pickAssertionStatus(responses: OpResponseNode[]): number | undefined {\n const success = responses.find(r => r.statusCode >= 200 && r.statusCode < 300);\n return success?.statusCode ?? responses[0]?.statusCode;\n}\n\n/** Build a markdown docs block from route- and operation-level descriptions, plus declared response-header summary. */\nfunction buildRequestDocs(route: OpRouteNode, op: OpOperationNode, assertedResponse?: OpResponseNode): string | undefined {\n const parts: string[] = [];\n if (route.description) parts.push(route.description.trim());\n if (op.description) parts.push(op.description.trim());\n const headers = assertedResponse?.headers ?? [];\n if (headers.length > 0) {\n const lines = ['**Response headers**', ''];\n for (const h of headers) {\n const tag = h.optional ? 'optional' : 'required';\n const desc = h.description ? ` — ${h.description}` : '';\n lines.push(`- \\`${h.name}\\` (${tag})${desc}`);\n }\n parts.push(lines.join('\\n'));\n }\n return parts.length > 0 ? parts.join('\\n\\n') : undefined;\n}\n\n// ─── Auth helpers ──────────────────────────────────────────────────────────\n\n/** Generate the YAML lines for an auth block (flat, per spec), indented by `indent`. */\nfunction renderAuthBlock(scheme: BrunoSecurityScheme, indent: string): string[] {\n const i = indent;\n if (scheme.type === 'http' && scheme.scheme === 'bearer') {\n return [`${i}auth:`, `${i} type: bearer`, `${i} token: \"{{token}}\"`];\n }\n if (scheme.type === 'http' && scheme.scheme === 'basic') {\n return [`${i}auth:`, `${i} type: basic`, `${i} username: \"{{username}}\"`, `${i} password: \"{{password}}\"`];\n }\n if (scheme.type === 'apiKey' && scheme.in === 'header') {\n const headerName = scheme.name ?? 'X-Api-Key';\n return [`${i}auth:`, `${i} type: apikey`, `${i} key: ${headerName}`, `${i} value: \"{{apiKey}}\"`, `${i} placement: header`];\n }\n return [];\n}\n\n/** Return the environment variable names needed for a given auth scheme. */\nfunction authEnvVarNames(scheme: BrunoSecurityScheme): string[] {\n if (scheme.type === 'http' && scheme.scheme === 'bearer') return ['token'];\n if (scheme.type === 'http' && scheme.scheme === 'basic') return ['username', 'password'];\n if (scheme.type === 'apiKey') return ['apiKey'];\n return [];\n}\n\n// ─── Model registry ────────────────────────────────────────────────────────\n\nfunction buildModelMap(contractRoots: ContractRootNode[]): Map<string, ModelNode> {\n const map = new Map<string, ModelNode>();\n for (const root of contractRoots) {\n for (const model of root.models) {\n map.set(model.name, model);\n }\n }\n return map;\n}\n\n/** Resolve all fields for a model, including inherited base fields (bases first, in declaration order). */\nfunction resolveModelFields(model: ModelNode, modelMap: Map<string, ModelNode>): FieldNode[] {\n const collected: FieldNode[] = [];\n if (model.bases) {\n for (const base of model.bases) {\n const baseModel = modelMap.get(base);\n if (baseModel) collected.push(...resolveModelFields(baseModel, modelMap));\n }\n }\n return [...collected, ...model.fields];\n}\n\n// ─── Param helpers ─────────────────────────────────────────────────────────\n\ninterface ParamEntry {\n name: string;\n type: ContractTypeNode | undefined;\n default?: string | number | boolean;\n optional: boolean;\n}\n\n/** Expand a ParamSource into a flat list of named entries with their types. */\nfunction expandParamSource(source: ParamSource, modelMap: Map<string, ModelNode>): ParamEntry[] {\n if (source.kind === 'params') {\n return source.nodes.map(n => ({ name: n.name, type: n.type, default: n.default, optional: n.optional }));\n }\n if (source.kind === 'ref') {\n const model = modelMap.get(source.name);\n if (model) {\n return resolveModelFields(model, modelMap)\n .filter(f => f.visibility !== 'readonly')\n .map(f => ({ name: f.name, type: f.type, default: f.default, optional: f.optional }));\n }\n // Fallback: single placeholder entry\n const name = source.name.charAt(0).toLowerCase() + source.name.slice(1);\n return [{ name, type: undefined, optional: false }];\n }\n // kind === 'type': if it's an inline object, expand its fields\n if (source.node.kind === 'inlineObject') {\n return source.node.fields.map(f => ({ name: f.name, type: f.type, default: f.default, optional: f.optional }));\n }\n return [];\n}\n\n/** Look up a named path param's type from route.params. */\nfunction findParamType(source: ParamSource | undefined, name: string, modelMap: Map<string, ModelNode>): ContractTypeNode | undefined {\n if (!source) return undefined;\n if (source.kind === 'params') return source.nodes.find(n => n.name === name)?.type;\n if (source.kind === 'ref') {\n const model = modelMap.get(source.name);\n if (model) return resolveModelFields(model, modelMap).find(f => f.name === name)?.type;\n }\n if (source.kind === 'type' && source.node.kind === 'inlineObject') {\n return source.node.fields.find(f => f.name === name)?.type;\n }\n return undefined;\n}\n\n/** Return a YAML-quoted example value string for a param, preferring a default value when provided. */\nfunction paramExampleValue(type: ContractTypeNode | undefined, defaultValue?: string | number | boolean, randomExamples = false): string {\n if (defaultValue !== undefined) return `\"${defaultValue}\"`;\n if (!type) return '\"\"';\n if (type.kind === 'enum') return type.values.length > 0 ? `\"${type.values[0]}\"` : '\"\"';\n if (type.kind === 'literal') return `\"${type.value}\"`;\n if (type.kind !== 'scalar') return '\"\"';\n if (randomExamples) {\n const random = randomScalarTemplate(type.name);\n if (random !== undefined) return `\"${random}\"`;\n }\n switch (type.name) {\n case 'uuid':\n return '\"00000000-0000-0000-0000-000000000000\"';\n case 'email':\n return '\"user@example.com\"';\n case 'url':\n return '\"https://example.com\"';\n case 'number':\n case 'int':\n case 'bigint':\n return '\"0\"';\n case 'boolean':\n return '\"true\"';\n case 'date':\n return '\"2024-01-01\"';\n case 'time':\n return '\"00:00:00\"';\n case 'datetime':\n return '\"2024-01-01T00:00:00Z\"';\n case 'duration':\n return '\"PT1H\"';\n default:\n return '\"\"';\n }\n}\n\n/** Bruno faker template for a scalar type, or undefined when no clean equivalent exists (date, time, duration, raw string). */\nfunction randomScalarTemplate(name: string): string | undefined {\n switch (name) {\n case 'uuid':\n return '{{$randomUUID}}';\n case 'email':\n return '{{$randomEmail}}';\n case 'url':\n return '{{$randomUrl}}';\n case 'number':\n case 'int':\n case 'bigint':\n return '{{$randomInt}}';\n case 'boolean':\n return '{{$randomBoolean}}';\n case 'datetime':\n return '{{$isoTimestamp}}';\n default:\n return undefined;\n }\n}\n\n// ─── Body helpers ──────────────────────────────────────────────────────────\n\n/**\n * Recursively build an example JSON value from a ContractTypeNode.\n *\n * When `randomExamples` is true we substitute Bruno faker templates only for\n * scalars whose JSON representation is a string (uuid/email/url/datetime).\n * Numbers and booleans stay deterministic — embedding `{{$randomInt}}` as a\n * bare JSON number would require sentinel-stripping the surrounding quotes,\n * and the body skeleton is meant as a starting point users edit anyway.\n */\nfunction typeToExampleValue(type: ContractTypeNode, modelMap: Map<string, ModelNode>, randomExamples = false): unknown {\n switch (type.kind) {\n case 'scalar':\n switch (type.name) {\n case 'string':\n return '';\n case 'email':\n return randomExamples ? '{{$randomEmail}}' : 'user@example.com';\n case 'url':\n return randomExamples ? '{{$randomUrl}}' : 'https://example.com';\n case 'uuid':\n return randomExamples ? '{{$randomUUID}}' : '00000000-0000-0000-0000-000000000000';\n case 'number':\n case 'int':\n case 'bigint':\n return 0;\n case 'boolean':\n return true;\n case 'date':\n return '2024-01-01';\n case 'time':\n return '00:00:00';\n case 'datetime':\n return randomExamples ? '{{$isoTimestamp}}' : '2024-01-01T00:00:00Z';\n case 'duration':\n return 'PT1H';\n case 'null':\n return null;\n default:\n return null;\n }\n case 'enum':\n return type.values[0] ?? '';\n case 'literal':\n return type.value;\n case 'array':\n return [typeToExampleValue(type.item, modelMap, randomExamples)];\n case 'tuple':\n return type.items.map(t => typeToExampleValue(t, modelMap, randomExamples));\n case 'record':\n return {};\n case 'union':\n return type.members.length > 0 ? typeToExampleValue(type.members[0]!, modelMap, randomExamples) : null;\n case 'discriminatedUnion':\n return type.members.length > 0 ? typeToExampleValue(type.members[0]!, modelMap, randomExamples) : null;\n case 'intersection':\n return {};\n case 'ref': {\n const model = modelMap.get(type.name);\n if (!model) return {};\n // Type alias — recurse into the aliased type\n if (model.type) return typeToExampleValue(model.type, modelMap, randomExamples);\n return modelToExampleObject(model, modelMap, randomExamples);\n }\n case 'lazy':\n return typeToExampleValue(type.inner, modelMap, randomExamples);\n case 'inlineObject':\n return fieldsToExampleObject(type.fields, modelMap, randomExamples);\n default:\n return null;\n }\n}\n\n/** Build an example object from a ModelNode's fields (including inherited base fields). */\nfunction modelToExampleObject(model: ModelNode, modelMap: Map<string, ModelNode>, randomExamples = false): Record<string, unknown> {\n return fieldsToExampleObject(resolveModelFields(model, modelMap), modelMap, randomExamples);\n}\n\n/** Build an example object from a list of FieldNodes. Excludes readonly fields; uses defaults when available; omits optional fields that have no default so they don't appear in the JSON output. */\nfunction fieldsToExampleObject(fields: FieldNode[], modelMap: Map<string, ModelNode>, randomExamples = false): Record<string, unknown> {\n const obj: Record<string, unknown> = {};\n for (const field of fields) {\n if (field.visibility === 'readonly') continue;\n if (field.default !== undefined) {\n obj[field.name] = field.default;\n } else if (!field.optional) {\n obj[field.name] = typeToExampleValue(field.type, modelMap, randomExamples);\n }\n }\n return obj;\n}\n\n// ─── Path helpers ──────────────────────────────────────────────────────────\n\n/** Convert /users/{id}/posts → /users/:id/posts (Bruno path parameter syntax) */\nfunction openCollectionPath(path: string): string {\n return path.replace(/\\{([a-zA-Z_][a-zA-Z0-9_]*)\\}/g, ':$1');\n}\n\n/** Convert \"Create an Offer\" → create-an-offer (for .yml file names) */\nexport function slugifyName(name: string): string {\n const result = name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-|-$/g, '');\n return result || 'request';\n}\n\n/** Convert /users/{id}/posts → users-id-posts (for .yml file names) */\nexport function sanitizePath(path: string): string {\n const result = path\n .replace(/^\\//, '')\n .replace(/\\{([a-zA-Z_][a-zA-Z0-9_]*)\\}/g, '$1')\n .replace(/\\//g, '-')\n .replace(/[^a-zA-Z0-9-]/g, '')\n .replace(/-+/g, '-')\n .replace(/^-|-$/g, '');\n return result || 'root';\n}\n\n/** Extract param names from a URL template, e.g. /users/{id} → ['id'] */\nfunction extractPathParamNames(path: string): string[] {\n return [...path.matchAll(/\\{([a-zA-Z_][a-zA-Z0-9_]*)\\}/g)].map(m => m[1]!);\n}\n\n/** Derive folder name from op file path, e.g. src/users.op → users */\nfunction deriveFolderName(file: string): string {\n return basename(file).replace(/\\.(op|ck)$/, '');\n}\n\n/**\n * Wrap a string in YAML double quotes if it contains characters that require quoting\n * (flow indicators, colons, braces, etc.).\n */\nfunction yamlString(value: string): string {\n if (/[:{}[\\],&*#?|<>=!%@`\"']/.test(value) || /^\\s|\\s$/.test(value)) {\n return `\"${value.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"')}\"`;\n }\n return value;\n}\n"],"mappings":"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","names":["resolve","basename","dirname","existsSync","readFileSync","rmSync","readdirSync","rmdirSync","resolveSecurity","resolveModifiers","SECURITY_NONE","basename","parse","parseYaml","stringify","stringifyYaml","MANIFEST_FILENAME","generateOpenCollection","roots","options","files","modelMap","buildModelMap","contractRoots","authOpts","auth","defaultScheme","schemes","undefined","randomExamples","includeInternal","push","relativePath","content","generateCollectionRoot","collectionName","envFile","generateEnvFiles","environments","sortedRoots","sort","a","b","aArea","meta","deriveFolderName","file","bArea","cmp","localeCompare","rootIdx","length","root","folder","slugifyName","displayName","charAt","toUpperCase","slice","generateFolderFile","subarea","subareaSlug","requestDir","subareaDisplayName","requests","route","routes","op","operations","resolveModifiers","includes","requestName","name","path","seq","fileName","method","sanitizePath","generateRequestFile","brunoExt","pluginExtensions","pluginOverride","Array","isArray","mergePluginFile","trackedPaths","map","f","JSON","stringify","parseManifest","parsed","parse","every","deepMerge","base","override","result","key","val","Object","entries","generatedYaml","pluginFileContent","overrideParsed","parseYaml","merged","stringifyYaml","lineWidth","scheme","lines","yamlString","renderAuthBlock","join","keys","variables","renderEnvFile","displayNameFor","defaultVars","baseUrl","varName","authEnvVarNames","value","escaped","String","replace","envKey","openCollectionPath","pathParams","extractPathParamNames","n","type","findParamType","params","optional","kind","queryParams","query","expandParamSource","e","allParams","p","paramExampleValue","default","headers","headerEntries","h","security","resolveSecurity","SECURITY_NONE","request","bodies","preferredOrder","primary","ct","find","contentType","json","typeToExampleValue","bodyType","jsonLine","split","expectedStatus","pickAssertionStatus","responses","assertedResponse","r","statusCode","requiredHeaders","filter","toLowerCase","docs","buildRequestDocs","docLine","success","parts","description","trim","tag","desc","indent","i","in","headerName","Map","model","models","set","resolveModelFields","collected","bases","baseModel","get","fields","source","nodes","visibility","node","defaultValue","values","random","randomScalarTemplate","item","items","t","members","modelToExampleObject","inner","fieldsToExampleObject","obj","field","matchAll","m","basename","test","validateBrunoExtension","value","Array","isArray","errors","describe","key","val","Object","entries","push","length","undefined","plugin","name","cacheKey","validateExtension","generateTargets","opRoots","contractRoots","ctx","auth","config","options","base","baseDir","resolve","rootDir","outDir","output","collectionName","basename","cleanupTrackedFiles","files","generateOpenCollection","randomExamples","includeInternal","environments","relativePath","content","emitFile","createBrunoPlugin","JSON","stringify","manifestPath","MANIFEST_FILENAME","existsSync","tracked","parseManifest","readFileSync","removedDirs","Set","rel","abs","rmSync","force","add","dirname","dir","current","startsWith","readdirSync","rmdirSync"]} |
+4
-4
| { | ||
| "name": "@contractkit/plugin-bruno", | ||
| "version": "1.2.0", | ||
| "version": "1.3.0", | ||
| "description": "ContractKit built-in plugin: Bruno REST collection generation", | ||
@@ -30,7 +30,7 @@ "author": { | ||
| "yaml": "^2.8.3", | ||
| "@contractkit/core": "0.13.0" | ||
| "@contractkit/core": "0.14.0" | ||
| }, | ||
| "devDependencies": { | ||
| "@repo/config-eslint": "0.3.1", | ||
| "@repo/config-typescript": "0.1.0" | ||
| "@repo/config-typescript": "0.1.0", | ||
| "@repo/config-eslint": "0.3.1" | ||
| }, | ||
@@ -37,0 +37,0 @@ "scripts": { |
+7
-5
@@ -69,3 +69,3 @@ # @contractkit/contractkit-plugin-bruno | ||
| Add a `plugins` block to any operation in a `.ck` file to deep-merge a YAML file into the generated request: | ||
| Add a `plugins` block to any operation in a `.ck` file to deep-merge a YAML fragment into the generated request. The Bruno plugin reads `plugins.bruno.template`; the value is either an inline YAML string or a `file://`/`http(s)://` URL that the CLI resolves to the file/response body before the plugin runs: | ||
@@ -75,3 +75,3 @@ ``` | ||
| plugins: { | ||
| bruno: "overrides/auth-token.yml" | ||
| bruno: { template: "file://overrides/auth-token.yml" } | ||
| } | ||
@@ -82,3 +82,3 @@ response: { 200: AuthResponse } | ||
| The file path is relative to the `.ck` source file. Its content is deep-merged into the generated request YAML — objects recurse, arrays replace entirely: | ||
| The `file://` path is relative to the `.ck` source file. The resolved content is deep-merged into the generated request YAML — objects recurse, arrays replace entirely: | ||
@@ -93,4 +93,6 @@ ```yaml | ||
| Authoring tip: combine per-operation `plugins.bruno` paths with `{{var}}` substitution to factor out a shared override directory: | ||
| `validateBrunoExtension` (run during compilation) enforces the shape: the value must be an object whose only allowed key is `template: string`. Anything else fails the build with a precise error. | ||
| Authoring tip: combine per-operation `template` URLs with `{{var}}` substitution to factor out a shared override directory: | ||
| ``` | ||
@@ -103,3 +105,3 @@ options { | ||
| get: { | ||
| plugins: { bruno: "{{bruno}}/payments/get-payment.yml" } | ||
| plugins: { bruno: { template: "file://{{bruno}}/payments/get-payment.yml" } } | ||
| response: { 200: { application/json: Payment } } | ||
@@ -106,0 +108,0 @@ } |
@@ -131,4 +131,7 @@ import type { | ||
| let content = generateRequestFile(route, op, requestName, seq, modelMap, root, defaultScheme, randomExamples); | ||
| const pluginOverride = op.pluginFiles?.['bruno']; | ||
| if (pluginOverride !== undefined) { | ||
| const brunoExt = op.pluginExtensions?.['bruno']; | ||
| const pluginOverride = brunoExt && typeof brunoExt === 'object' && !Array.isArray(brunoExt) | ||
| ? brunoExt['template'] | ||
| : undefined; | ||
| if (typeof pluginOverride === 'string') { | ||
| content = mergePluginFile(content, pluginOverride); | ||
@@ -135,0 +138,0 @@ } |
+30
-1
@@ -5,3 +5,3 @@ import { resolve, basename, dirname } from 'node:path'; | ||
| import type { BrunoSecurityScheme } from './codegen-bruno.js'; | ||
| import type { ContractKitPlugin } from '@contractkit/core'; | ||
| import type { ContractKitPlugin, PluginValue } from '@contractkit/core'; | ||
@@ -42,5 +42,33 @@ /** Configuration accepted by the Bruno plugin, both via `contractkit.config.json` and `createBrunoPlugin`. */ | ||
| /** | ||
| * Validates a `plugins.bruno` extension entry on an operation. The expected shape is | ||
| * `{ template?: string }`, where `template` is a YAML fragment to deep-merge into the | ||
| * generated request file (typically a `file://...` URL whose contents have already | ||
| * been loaded by the CLI resolver). | ||
| */ | ||
| export function validateBrunoExtension(value: PluginValue): { errors?: string[] } | void { | ||
| if (value === null || typeof value !== 'object' || Array.isArray(value)) { | ||
| return { errors: [`expected an object, got ${describe(value)}`] }; | ||
| } | ||
| const errors: string[] = []; | ||
| for (const [key, val] of Object.entries(value)) { | ||
| if (key === 'template') { | ||
| if (typeof val !== 'string') errors.push(`'template' must be a string, got ${describe(val)}`); | ||
| } else { | ||
| errors.push(`unknown field '${key}' (allowed: template)`); | ||
| } | ||
| } | ||
| return errors.length ? { errors } : undefined; | ||
| } | ||
| function describe(value: PluginValue): string { | ||
| if (value === null) return 'null'; | ||
| if (Array.isArray(value)) return 'array'; | ||
| return typeof value; | ||
| } | ||
| const plugin: ContractKitPlugin = { | ||
| name: 'bruno', | ||
| cacheKey: 'bruno', | ||
| validateExtension: validateBrunoExtension, | ||
| async generateTargets({ opRoots, contractRoots }, ctx) { | ||
@@ -90,2 +118,3 @@ const { auth, ...config } = ctx.options as BrunoPluginOptions; | ||
| cacheKey: `bruno:${JSON.stringify(config)}`, | ||
| validateExtension: validateBrunoExtension, | ||
| async generateTargets({ opRoots, contractRoots }, ctx) { | ||
@@ -92,0 +121,0 @@ const base = config.baseDir ? resolve(rootDir, config.baseDir) : rootDir; |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
565688
11.02%33
3.13%3994
3.9%154
1.32%+ Added
- Removed
Updated