whistle.mock-plugins 1.2.2-beta.25 → 1.2.2-beta.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -72,6 +72,8 @@
72
72
 
73
73
  /**![appstore](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTQ2NCAxNDRIMTYwYy04LjggMC0xNiA3LjItMTYgMTZ2MzA0YzAgOC44IDcuMiAxNiAxNiAxNmgzMDRjOC44IDAgMTYtNy4yIDE2LTE2VjE2MGMwLTguOC03LjItMTYtMTYtMTZ6bS01MiAyNjhIMjEyVjIxMmgyMDB2MjAwem00NTItMjY4SDU2MGMtOC44IDAtMTYgNy4yLTE2IDE2djMwNGMwIDguOCA3LjIgMTYgMTYgMTZoMzA0YzguOCAwIDE2LTcuMiAxNi0xNlYxNjBjMC04LjgtNy4yLTE2LTE2LTE2em0tNTIgMjY4SDYxMlYyMTJoMjAwdjIwMHpNNDY0IDU0NEgxNjBjLTguOCAwLTE2IDcuMi0xNiAxNnYzMDRjMCA4LjggNy4yIDE2IDE2IDE2aDMwNGM4LjggMCAxNi03LjIgMTYtMTZWNTYwYzAtOC44LTcuMi0xNi0xNi0xNnptLTUyIDI2OEgyMTJWNjEyaDIwMHYyMDB6bTQ1Mi0yNjhINTYwYy04LjggMC0xNiA3LjItMTYgMTZ2MzA0YzAgOC44IDcuMiAxNiAxNiAxNmgzMDRjOC44IDAgMTYtNy4yIDE2LTE2VjU2MGMwLTguOC03LjItMTYtMTYtMTZ6bS01MiAyNjhINjEyVjYxMmgyMDB2MjAweiIgLz48L3N2Zz4=) */
74
74
 
75
+ /**![bar-chart](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTg4OCA3OTJIMjAwVjE2OGMwLTQuNC0zLjYtOC04LThoLTU2Yy00LjQgMC04IDMuNi04IDh2Njg4YzAgNC40IDMuNiA4IDggOGg3NTJjNC40IDAgOC0zLjYgOC04di01NmMwLTQuNC0zLjYtOC04LTh6bS02MDAtODBoNTZjNC40IDAgOC0zLjYgOC04VjU2MGMwLTQuNC0zLjYtOC04LThoLTU2Yy00LjQgMC04IDMuNi04IDh2MTQ0YzAgNC40IDMuNiA4IDggOHptMTUyIDBoNTZjNC40IDAgOC0zLjYgOC04VjM4NGMwLTQuNC0zLjYtOC04LThoLTU2Yy00LjQgMC04IDMuNi04IDh2MzIwYzAgNC40IDMuNiA4IDggOHptMTUyIDBoNTZjNC40IDAgOC0zLjYgOC04VjQ2MmMwLTQuNC0zLjYtOC04LThoLTU2Yy00LjQgMC04IDMuNi04IDh2MjQyYzAgNC40IDMuNiA4IDggOHptMTUyIDBoNTZjNC40IDAgOC0zLjYgOC04VjMwNGMwLTQuNC0zLjYtOC04LThoLTU2Yy00LjQgMC04IDMuNi04IDh2NDAwYzAgNC40IDMuNiA4IDggOHoiIC8+PC9zdmc+) */
76
+
75
77
  /**![bars](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjAgMCAxMDI0IDEwMjQiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTkxMiAxOTJIMzI4Yy00LjQgMC04IDMuNi04IDh2NTZjMCA0LjQgMy42IDggOCA4aDU4NGM0LjQgMCA4LTMuNiA4LTh2LTU2YzAtNC40LTMuNi04LTgtOHptMCAyODRIMzI4Yy00LjQgMC04IDMuNi04IDh2NTZjMCA0LjQgMy42IDggOCA4aDU4NGM0LjQgMCA4LTMuNiA4LTh2LTU2YzAtNC40LTMuNi04LTgtOHptMCAyODRIMzI4Yy00LjQgMC04IDMuNi04IDh2NTZjMCA0LjQgMy42IDggOCA4aDU4NGM0LjQgMCA4LTMuNiA4LTh2LTU2YzAtNC40LTMuNi04LTgtOHpNMTA0IDIyOGE1NiA1NiAwIDEwMTEyIDAgNTYgNTYgMCAxMC0xMTIgMHptMCAyODRhNTYgNTYgMCAxMDExMiAwIDU2IDU2IDAgMTAtMTEyIDB6bTAgMjg0YTU2IDU2IDAgMTAxMTIgMCA1NiA1NiAwIDEwLTExMiAweiIgLz48L3N2Zz4=) */
76
78
 
77
79
  /**![calendar](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTg4MCAxODRINzEydi02NGMwLTQuNC0zLjYtOC04LThoLTU2Yy00LjQgMC04IDMuNi04IDh2NjRIMzg0di02NGMwLTQuNC0zLjYtOC04LThoLTU2Yy00LjQgMC04IDMuNi04IDh2NjRIMTQ0Yy0xNy43IDAtMzIgMTQuMy0zMiAzMnY2NjRjMCAxNy43IDE0LjMgMzIgMzIgMzJoNzM2YzE3LjcgMCAzMi0xNC4zIDMyLTMyVjIxNmMwLTE3LjctMTQuMy0zMi0zMi0zMnptLTQwIDY1NkgxODRWNDYwaDY1NnYzODB6TTE4NCAzOTJWMjU2aDEyOHY0OGMwIDQuNCAzLjYgOCA4IDhoNTZjNC40IDAgOC0zLjYgOC04di00OGgyNTZ2NDhjMCA0LjQgMy42IDggOCA4aDU2YzQuNCAwIDgtMy42IDgtOHYtNDhoMTI4djEzNkgxODR6IiAvPjwvc3ZnPg==) */
@@ -86,6 +88,10 @@
86
88
 
87
89
  /**![check](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTkxMiAxOTBoLTY5LjljLTkuOCAwLTE5LjEgNC41LTI1LjEgMTIuMkw0MDQuNyA3MjQuNSAyMDcgNDc0YTMyIDMyIDAgMDAtMjUuMS0xMi4ySDExMmMtNi43IDAtMTAuNCA3LjctNi4zIDEyLjlsMjczLjkgMzQ3YzEyLjggMTYuMiAzNy40IDE2LjIgNTAuMyAwbDQ4OC40LTYxOC45YzQuMS01LjEuNC0xMi44LTYuMy0xMi44eiIgLz48L3N2Zz4=) */
88
90
 
91
+ /**![clear](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PHN0eWxlIC8+PC9kZWZzPjxwYXRoIGQ9Ik04OTkuMSA4NjkuNmwtNTMtMzA1LjZIODY0YzE0LjQgMCAyNi0xMS42IDI2LTI2VjM0NmMwLTE0LjQtMTEuNi0yNi0yNi0yNkg2MThWMTM4YzAtMTQuNC0xMS42LTI2LTI2LTI2SDQzMmMtMTQuNCAwLTI2IDExLjYtMjYgMjZ2MTgySDE2MGMtMTQuNCAwLTI2IDExLjYtMjYgMjZ2MTkyYzAgMTQuNCAxMS42IDI2IDI2IDI2aDE3LjlsLTUzIDMwNS42YTI1Ljk1IDI1Ljk1IDAgMDAyNS42IDMwLjRoNzIzYzEuNSAwIDMtLjEgNC40LS40YTI1Ljg4IDI1Ljg4IDAgMDAyMS4yLTMwek0yMDQgMzkwaDI3MlYxODJoNzJ2MjA4aDI3MnYxMDRIMjA0VjM5MHptNDY4IDQ0MFY2NzRjMC00LjQtMy42LTgtOC04aC00OGMtNC40IDAtOCAzLjYtOCA4djE1Nkg0MTZWNjc0YzAtNC40LTMuNi04LTgtOGgtNDhjLTQuNCAwLTggMy42LTggOHYxNTZIMjAyLjhsNDUuMS0yNjBINzc2bDQ1LjEgMjYwSDY3MnoiIC8+PC9zdmc+) */
92
+
93
+ /**![clock-circle](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTUxMiA2NEMyNjQuNiA2NCA2NCAyNjQuNiA2NCA1MTJzMjAwLjYgNDQ4IDQ0OCA0NDggNDQ4LTIwMC42IDQ0OC00NDhTNzU5LjQgNjQgNTEyIDY0em0wIDgyMGMtMjA1LjQgMC0zNzItMTY2LjYtMzcyLTM3MnMxNjYuNi0zNzIgMzcyLTM3MiAzNzIgMTY2LjYgMzcyIDM3Mi0xNjYuNiAzNzItMzcyIDM3MnoiIC8+PHBhdGggZD0iTTY4Ni43IDYzOC42TDU0NC4xIDUzNS41VjI4OGMwLTQuNC0zLjYtOC04LThINDg4Yy00LjQgMC04IDMuNi04IDh2Mjc1LjRjMCAyLjYgMS4yIDUgMy4zIDYuNWwxNjUuNCAxMjAuNmMzLjYgMi42IDguNiAxLjggMTEuMi0xLjdsMjguNi0zOWMyLjYtMy43IDEuOC04LjctMS44LTExLjJ6IiAvPjwvc3ZnPg==) */
94
+
89
95
  /**![close-circle](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIGZpbGwtcnVsZT0iZXZlbm9kZCIgdmlld0JveD0iNjQgNjQgODk2IDg5NiIgZm9jdXNhYmxlPSJmYWxzZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNNTEyIDY0YzI0Ny40IDAgNDQ4IDIwMC42IDQ0OCA0NDhTNzU5LjQgOTYwIDUxMiA5NjAgNjQgNzU5LjQgNjQgNTEyIDI2NC42IDY0IDUxMiA2NHptMTI3Ljk4IDI3NC44MmgtLjA0bC0uMDguMDZMNTEyIDQ2Ni43NSAzODQuMTQgMzM4Ljg4Yy0uMDQtLjA1LS4wNi0uMDYtLjA4LS4wNmEuMTIuMTIgMCAwMC0uMDcgMGMtLjAzIDAtLjA1LjAxLS4wOS4wNWwtNDUuMDIgNDUuMDJhLjIuMiAwIDAwLS4wNS4wOS4xMi4xMiAwIDAwMCAuMDd2LjAyYS4yNy4yNyAwIDAwLjA2LjA2TDQ2Ni43NSA1MTIgMzM4Ljg4IDYzOS44NmMtLjA1LjA0LS4wNi4wNi0uMDYuMDhhLjEyLjEyIDAgMDAwIC4wN2MwIC4wMy4wMS4wNS4wNS4wOWw0NS4wMiA0NS4wMmEuMi4yIDAgMDAuMDkuMDUuMTIuMTIgMCAwMC4wNyAwYy4wMiAwIC4wNC0uMDEuMDgtLjA1TDUxMiA1NTcuMjVsMTI3Ljg2IDEyNy44N2MuMDQuMDQuMDYuMDUuMDguMDVhLjEyLjEyIDAgMDAuMDcgMGMuMDMgMCAuMDUtLjAxLjA5LS4wNWw0NS4wMi00NS4wMmEuMi4yIDAgMDAuMDUtLjA5LjEyLjEyIDAgMDAwLS4wN3YtLjAyYS4yNy4yNyAwIDAwLS4wNS0uMDZMNTU3LjI1IDUxMmwxMjcuODctMTI3Ljg2Yy4wNC0uMDQuMDUtLjA2LjA1LS4wOGEuMTIuMTIgMCAwMDAtLjA3YzAtLjAzLS4wMS0uMDUtLjA1LS4wOWwtNDUuMDItNDUuMDJhLjIuMiAwIDAwLS4wOS0uMDUuMTIuMTIgMCAwMC0uMDcgMHoiIC8+PC9zdmc+) */
90
96
 
91
97
  /**![close](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIGZpbGwtcnVsZT0iZXZlbm9kZCIgdmlld0JveD0iNjQgNjQgODk2IDg5NiIgZm9jdXNhYmxlPSJmYWxzZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNNzk5Ljg2IDE2Ni4zMWMuMDIgMCAuMDQuMDIuMDguMDZsNTcuNjkgNTcuN2MuMDQuMDMuMDUuMDUuMDYuMDhhLjEyLjEyIDAgMDEwIC4wNmMwIC4wMy0uMDIuMDUtLjA2LjA5TDU2OS45MyA1MTJsMjg3LjcgMjg3LjdjLjA0LjA0LjA1LjA2LjA2LjA5YS4xMi4xMiAwIDAxMCAuMDdjMCAuMDItLjAyLjA0LS4wNi4wOGwtNTcuNyA1Ny42OWMtLjAzLjA0LS4wNS4wNS0uMDcuMDZhLjEyLjEyIDAgMDEtLjA3IDBjLS4wMyAwLS4wNS0uMDItLjA5LS4wNkw1MTIgNTY5LjkzbC0yODcuNyAyODcuN2MtLjA0LjA0LS4wNi4wNS0uMDkuMDZhLjEyLjEyIDAgMDEtLjA3IDBjLS4wMiAwLS4wNC0uMDItLjA4LS4wNmwtNTcuNjktNTcuN2MtLjA0LS4wMy0uMDUtLjA1LS4wNi0uMDdhLjEyLjEyIDAgMDEwLS4wN2MwLS4wMy4wMi0uMDUuMDYtLjA5TDQ1NC4wNyA1MTJsLTI4Ny43LTI4Ny43Yy0uMDQtLjA0LS4wNS0uMDYtLjA2LS4wOWEuMTIuMTIgMCAwMTAtLjA3YzAtLjAyLjAyLS4wNC4wNi0uMDhsNTcuNy01Ny42OWMuMDMtLjA0LjA1LS4wNS4wNy0uMDZhLjEyLjEyIDAgMDEuMDcgMGMuMDMgMCAuMDUuMDIuMDkuMDZMNTEyIDQ1NC4wN2wyODcuNy0yODcuN2MuMDQtLjA0LjA2LS4wNS4wOS0uMDZhLjEyLjEyIDAgMDEuMDcgMHoiIC8+PC9zdmc+) */
@@ -136,6 +142,8 @@
136
142
 
137
143
  /**![fullscreen](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTI5MCAyMzYuNGw0My45LTQzLjlhOC4wMSA4LjAxIDAgMDAtNC43LTEzLjZMMTY5IDE2MGMtNS4xLS42LTkuNSAzLjctOC45IDguOUwxNzkgMzI5LjFjLjggNi42IDguOSA5LjQgMTMuNiA0LjdsNDMuNy00My43TDM3MCA0MjMuN2MzLjEgMy4xIDguMiAzLjEgMTEuMyAwbDQyLjQtNDIuM2MzLjEtMy4xIDMuMS04LjIgMC0xMS4zTDI5MCAyMzYuNHptMzUyLjcgMTg3LjNjMy4xIDMuMSA4LjIgMy4xIDExLjMgMGwxMzMuNy0xMzMuNiA0My43IDQzLjdhOC4wMSA4LjAxIDAgMDAxMy42LTQuN0w4NjMuOSAxNjljLjYtNS4xLTMuNy05LjUtOC45LTguOUw2OTQuOCAxNzljLTYuNi44LTkuNCA4LjktNC43IDEzLjZsNDMuOSA0My45TDYwMC4zIDM3MGE4LjAzIDguMDMgMCAwMDAgMTEuM2w0Mi40IDQyLjR6TTg0NSA2OTQuOWMtLjgtNi42LTguOS05LjQtMTMuNi00LjdsLTQzLjcgNDMuN0w2NTQgNjAwLjNhOC4wMyA4LjAzIDAgMDAtMTEuMyAwbC00Mi40IDQyLjNhOC4wMyA4LjAzIDAgMDAwIDExLjNMNzM0IDc4Ny42bC00My45IDQzLjlhOC4wMSA4LjAxIDAgMDA0LjcgMTMuNkw4NTUgODY0YzUuMS42IDkuNS0zLjcgOC45LTguOUw4NDUgNjk0Ljl6bS00NjMuNy05NC42YTguMDMgOC4wMyAwIDAwLTExLjMgMEwyMzYuMyA3MzMuOWwtNDMuNy00My43YTguMDEgOC4wMSAwIDAwLTEzLjYgNC43TDE2MC4xIDg1NWMtLjYgNS4xIDMuNyA5LjUgOC45IDguOUwzMjkuMiA4NDVjNi42LS44IDkuNC04LjkgNC43LTEzLjZMMjkwIDc4Ny42IDQyMy43IDY1NGMzLjEtMy4xIDMuMS04LjIgMC0xMS4zbC00Mi40LTQyLjR6IiAvPjwvc3ZnPg==) */
138
144
 
145
+ /**![history](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTUzNi4xIDI3M0g0ODhjLTQuNCAwLTggMy42LTggOHYyNzUuM2MwIDIuNiAxLjIgNSAzLjMgNi41bDE2NS4zIDEyMC43YzMuNiAyLjYgOC42IDEuOSAxMS4yLTEuN2wyOC42LTM5YzIuNy0zLjcgMS45LTguNy0xLjctMTEuMkw1NDQuMSA1MjguNVYyODFjMC00LjQtMy42LTgtOC04em0yMTkuOCA3NS4ybDE1Ni44IDM4LjNjNSAxLjIgOS45LTIuNiA5LjktNy43bC44LTE2MS41YzAtNi43LTcuNy0xMC41LTEyLjktNi4zTDc1Mi45IDMzNC4xYTggOCAwIDAwMyAxNC4xem0xNjcuNyAzMDEuMWwtNTYuNy0xOS41YTggOCAwIDAwLTEwLjEgNC44Yy0xLjkgNS4xLTMuOSAxMC4xLTYgMTUuMS0xNy44IDQyLjEtNDMuMyA4MC03NS45IDExMi41YTM1MyAzNTMgMCAwMS0xMTIuNSA3NS45IDM1Mi4xOCAzNTIuMTggMCAwMS0xMzcuNyAyNy44Yy00Ny44IDAtOTQuMS05LjMtMTM3LjctMjcuOGEzNTMgMzUzIDAgMDEtMTEyLjUtNzUuOWMtMzIuNS0zMi41LTU4LTcwLjQtNzUuOS0xMTIuNUEzNTMuNDQgMzUzLjQ0IDAgMDExNzEgNTEyYzAtNDcuOCA5LjMtOTQuMiAyNy44LTEzNy44IDE3LjgtNDIuMSA0My4zLTgwIDc1LjktMTEyLjVhMzUzIDM1MyAwIDAxMTEyLjUtNzUuOUM0MzAuNiAxNjcuMyA0NzcgMTU4IDUyNC44IDE1OHM5NC4xIDkuMyAxMzcuNyAyNy44QTM1MyAzNTMgMCAwMTc3NSAyNjEuN2MxMC4yIDEwLjMgMTkuOCAyMSAyOC42IDMyLjNsNTkuOC00Ni44Qzc4NC43IDE0Ni42IDY2Mi4yIDgxLjkgNTI0LjYgODIgMjg1IDgyLjEgOTIuNiAyNzYuNyA5NSA1MTYuNCA5Ny40IDc1MS45IDI4OC45IDk0MiA1MjQuOCA5NDJjMTg1LjUgMCAzNDMuNS0xMTcuNiA0MDMuNy0yODIuMyAxLjUtNC4yLS43LTguOS00LjktMTAuNHoiIC8+PC9zdmc+) */
146
+
139
147
  /**![holder](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTMwMCAyNzYuNWE1NiA1NiAwIDEwNTYtOTcgNTYgNTYgMCAwMC01NiA5N3ptMCAyODRhNTYgNTYgMCAxMDU2LTk3IDU2IDU2IDAgMDAtNTYgOTd6TTY0MCAyMjhhNTYgNTYgMCAxMDExMiAwIDU2IDU2IDAgMDAtMTEyIDB6bTAgMjg0YTU2IDU2IDAgMTAxMTIgMCA1NiA1NiAwIDAwLTExMiAwek0zMDAgODQ0LjVhNTYgNTYgMCAxMDU2LTk3IDU2IDU2IDAgMDAtNTYgOTd6TTY0MCA3OTZhNTYgNTYgMCAxMDExMiAwIDU2IDU2IDAgMDAtMTEyIDB6IiAvPjwvc3ZnPg==) */
140
148
 
141
149
  /**![info-circle](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTUxMiA2NEMyNjQuNiA2NCA2NCAyNjQuNiA2NCA1MTJzMjAwLjYgNDQ4IDQ0OCA0NDggNDQ4LTIwMC42IDQ0OC00NDhTNzU5LjQgNjQgNTEyIDY0em0wIDgyMGMtMjA1LjQgMC0zNzItMTY2LjYtMzcyLTM3MnMxNjYuNi0zNzIgMzcyLTM3MiAzNzIgMTY2LjYgMzcyIDM3Mi0xNjYuNiAzNzItMzcyIDM3MnoiIC8+PHBhdGggZD0iTTQ2NCAzMzZhNDggNDggMCAxMDk2IDAgNDggNDggMCAxMC05NiAwem03MiAxMTJoLTQ4Yy00LjQgMC04IDMuNi04IDh2MjcyYzAgNC40IDMuNiA4IDggOGg0OGM0LjQgMCA4LTMuNiA4LThWNDU2YzAtNC40LTMuNi04LTgtOHoiIC8+PC9zdmc+) */
@@ -166,10 +174,20 @@
166
174
 
167
175
  /**![right](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTc2NS43IDQ4Ni44TDMxNC45IDEzNC43QTcuOTcgNy45NyAwIDAwMzAyIDE0MXY3Ny4zYzAgNC45IDIuMyA5LjYgNi4xIDEyLjZsMzYwIDI4MS4xLTM2MCAyODEuMWMtMy45IDMtNi4xIDcuNy02LjEgMTIuNlY4ODNjMCA2LjcgNy43IDEwLjQgMTIuOSA2LjNsNDUwLjgtMzUyLjFhMzEuOTYgMzEuOTYgMCAwMDAtNTAuNHoiIC8+PC9zdmc+) */
168
176
 
177
+ /**![rise](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTkxNyAyMTEuMWwtMTk5LjIgMjRjLTYuNi44LTkuNCA4LjktNC43IDEzLjZsNTkuMyA1OS4zLTIyNiAyMjYtMTAxLjgtMTAxLjdjLTYuMy02LjMtMTYuNC02LjItMjIuNiAwTDEwMC4zIDc1NC4xYTguMDMgOC4wMyAwIDAwMCAxMS4zbDQ1IDQ1LjJjMy4xIDMuMSA4LjIgMy4xIDExLjMgMEw0MzMuMyA1MzQgNTM1IDYzNS43YzYuMyA2LjIgMTYuNCA2LjIgMjIuNiAwTDgyOSAzNjQuNWw1OS4zIDU5LjNhOC4wMSA4LjAxIDAgMDAxMy42LTQuN2wyNC0xOTkuMmMuNy01LjEtMy43LTkuNS04LjktOC44eiIgLz48L3N2Zz4=) */
178
+
179
+ /**![save](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTg5My4zIDI5My4zTDczMC43IDEzMC43Yy03LjUtNy41LTE2LjctMTMtMjYuNy0xNlYxMTJIMTQ0Yy0xNy43IDAtMzIgMTQuMy0zMiAzMnY3MzZjMCAxNy43IDE0LjMgMzIgMzIgMzJoNzM2YzE3LjcgMCAzMi0xNC4zIDMyLTMyVjMzOC41YzAtMTctNi43LTMzLjItMTguNy00NS4yek0zODQgMTg0aDI1NnYxMDRIMzg0VjE4NHptNDU2IDY1NkgxODRWMTg0aDEzNnYxMzZjMCAxNy43IDE0LjMgMzIgMzIgMzJoMzIwYzE3LjcgMCAzMi0xNC4zIDMyLTMyVjIwNS44bDEzNiAxMzZWODQwek01MTIgNDQyYy03OS41IDAtMTQ0IDY0LjUtMTQ0IDE0NHM2NC41IDE0NCAxNDQgMTQ0IDE0NC02NC41IDE0NC0xNDQtNjQuNS0xNDQtMTQ0LTE0NHptMCAyMjRjLTQ0LjIgMC04MC0zNS44LTgwLTgwczM1LjgtODAgODAtODAgODAgMzUuOCA4MCA4MC0zNS44IDgwLTgwIDgweiIgLz48L3N2Zz4=) */
180
+
169
181
  /**![search](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTkwOS42IDg1NC41TDY0OS45IDU5NC44QzY5MC4yIDU0Mi43IDcxMiA0NzkgNzEyIDQxMmMwLTgwLjItMzEuMy0xNTUuNC04Ny45LTIxMi4xLTU2LjYtNTYuNy0xMzItODcuOS0yMTIuMS04Ny45cy0xNTUuNSAzMS4zLTIxMi4xIDg3LjlDMTQzLjIgMjU2LjUgMTEyIDMzMS44IDExMiA0MTJjMCA4MC4xIDMxLjMgMTU1LjUgODcuOSAyMTIuMUMyNTYuNSA2ODAuOCAzMzEuOCA3MTIgNDEyIDcxMmM2NyAwIDEzMC42LTIxLjggMTgyLjctNjJsMjU5LjcgMjU5LjZhOC4yIDguMiAwIDAwMTEuNiAwbDQzLjYtNDMuNWE4LjIgOC4yIDAgMDAwLTExLjZ6TTU3MC40IDU3MC40QzUyOCA2MTIuNyA0NzEuOCA2MzYgNDEyIDYzNnMtMTE2LTIzLjMtMTU4LjQtNjUuNkMyMTEuMyA1MjggMTg4IDQ3MS44IDE4OCA0MTJzMjMuMy0xMTYuMSA2NS42LTE1OC40QzI5NiAyMTEuMyAzNTIuMiAxODggNDEyIDE4OHMxMTYuMSAyMy4yIDE1OC40IDY1LjZTNjM2IDM1Mi4yIDYzNiA0MTJzLTIzLjMgMTE2LjEtNjUuNiAxNTguNHoiIC8+PC9zdmc+) */
170
182
 
183
+ /**![send](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PHN0eWxlIC8+PC9kZWZzPjxwYXRoIGQ9Ik05MzEuNCA0OTguOUw5NC45IDc5LjVjLTMuNC0xLjctNy4zLTIuMS0xMS0xLjJhMTUuOTkgMTUuOTkgMCAwMC0xMS43IDE5LjNsODYuMiAzNTIuMmMxLjMgNS4zIDUuMiA5LjYgMTAuNCAxMS4zbDE0Ny43IDUwLjctMTQ3LjYgNTAuN2MtNS4yIDEuOC05LjEgNi0xMC4zIDExLjNMNzIuMiA5MjYuNWMtLjkgMy43LS41IDcuNiAxLjIgMTAuOSAzLjkgNy45IDEzLjUgMTEuMSAyMS41IDcuMmw4MzYuNS00MTdjMy4xLTEuNSA1LjYtNC4xIDcuMi03LjEgMy45LTggLjctMTcuNi03LjItMjEuNnpNMTcwLjggODI2LjNsNTAuMy0yMDUuNiAyOTUuMi0xMDEuM2MyLjMtLjggNC4yLTIuNiA1LTUgMS40LTQuMi0uOC04LjctNS0xMC4yTDIyMS4xIDQwMyAxNzEgMTk4LjJsNjI4IDMxNC45LTYyOC4yIDMxMy4yeiIgLz48L3N2Zz4=) */
184
+
171
185
  /**![setting](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTkyNC44IDYyNS43bC02NS41LTU2YzMuMS0xOSA0LjctMzguNCA0LjctNTcuOHMtMS42LTM4LjgtNC43LTU3LjhsNjUuNS01NmEzMi4wMyAzMi4wMyAwIDAwOS4zLTM1LjJsLS45LTIuNmE0NDMuNzQgNDQzLjc0IDAgMDAtNzkuNy0xMzcuOWwtMS44LTIuMWEzMi4xMiAzMi4xMiAwIDAwLTM1LjEtOS41bC04MS4zIDI4LjljLTMwLTI0LjYtNjMuNS00NC05OS43LTU3LjZsLTE1LjctODVhMzIuMDUgMzIuMDUgMCAwMC0yNS44LTI1LjdsLTIuNy0uNWMtNTIuMS05LjQtMTA2LjktOS40LTE1OSAwbC0yLjcuNWEzMi4wNSAzMi4wNSAwIDAwLTI1LjggMjUuN2wtMTUuOCA4NS40YTM1MS44NiAzNTEuODYgMCAwMC05OSA1Ny40bC04MS45LTI5LjFhMzIgMzIgMCAwMC0zNS4xIDkuNWwtMS44IDIuMWE0NDYuMDIgNDQ2LjAyIDAgMDAtNzkuNyAxMzcuOWwtLjkgMi42Yy00LjUgMTIuNS0uOCAyNi41IDkuMyAzNS4ybDY2LjMgNTYuNmMtMy4xIDE4LjgtNC42IDM4LTQuNiA1Ny4xIDAgMTkuMiAxLjUgMzguNCA0LjYgNTcuMUw5OSA2MjUuNWEzMi4wMyAzMi4wMyAwIDAwLTkuMyAzNS4ybC45IDIuNmMxOC4xIDUwLjQgNDQuOSA5Ni45IDc5LjcgMTM3LjlsMS44IDIuMWEzMi4xMiAzMi4xMiAwIDAwMzUuMSA5LjVsODEuOS0yOS4xYzI5LjggMjQuNSA2My4xIDQzLjkgOTkgNTcuNGwxNS44IDg1LjRhMzIuMDUgMzIuMDUgMCAwMDI1LjggMjUuN2wyLjcuNWE0NDkuNCA0NDkuNCAwIDAwMTU5IDBsMi43LS41YTMyLjA1IDMyLjA1IDAgMDAyNS44LTI1LjdsMTUuNy04NWEzNTAgMzUwIDAgMDA5OS43LTU3LjZsODEuMyAyOC45YTMyIDMyIDAgMDAzNS4xLTkuNWwxLjgtMi4xYzM0LjgtNDEuMSA2MS42LTg3LjUgNzkuNy0xMzcuOWwuOS0yLjZjNC41LTEyLjMuOC0yNi4zLTkuMy0zNXpNNzg4LjMgNDY1LjljMi41IDE1LjEgMy44IDMwLjYgMy44IDQ2LjFzLTEuMyAzMS0zLjggNDYuMWwtNi42IDQwLjEgNzQuNyA2My45YTM3MC4wMyAzNzAuMDMgMCAwMS00Mi42IDczLjZMNzIxIDcwMi44bC0zMS40IDI1LjhjLTIzLjkgMTkuNi01MC41IDM1LTc5LjMgNDUuOGwtMzguMSAxNC4zLTE3LjkgOTdhMzc3LjUgMzc3LjUgMCAwMS04NSAwbC0xNy45LTk3LjItMzcuOC0xNC41Yy0yOC41LTEwLjgtNTUtMjYuMi03OC43LTQ1LjdsLTMxLjQtMjUuOS05My40IDMzLjJjLTE3LTIyLjktMzEuMi00Ny42LTQyLjYtNzMuNmw3NS41LTY0LjUtNi41LTQwYy0yLjQtMTQuOS0zLjctMzAuMy0zLjctNDUuNSAwLTE1LjMgMS4yLTMwLjYgMy43LTQ1LjVsNi41LTQwLTc1LjUtNjQuNWMxMS4zLTI2LjEgMjUuNi01MC43IDQyLjYtNzMuNmw5My40IDMzLjIgMzEuNC0yNS45YzIzLjctMTkuNSA1MC4yLTM0LjkgNzguNy00NS43bDM3LjktMTQuMyAxNy45LTk3LjJjMjguMS0zLjIgNTYuOC0zLjIgODUgMGwxNy45IDk3IDM4LjEgMTQuM2MyOC43IDEwLjggNTUuNCAyNi4yIDc5LjMgNDUuOGwzMS40IDI1LjggOTIuOC0zMi45YzE3IDIyLjkgMzEuMiA0Ny42IDQyLjYgNzMuNkw3ODEuOCA0MjZsNi41IDM5Ljl6TTUxMiAzMjZjLTk3LjIgMC0xNzYgNzguOC0xNzYgMTc2czc4LjggMTc2IDE3NiAxNzYgMTc2LTc4LjggMTc2LTE3Ni03OC44LTE3Ni0xNzYtMTc2em03OS4yIDI1NS4yQTExMS42IDExMS42IDAgMDE1MTIgNjE0Yy0yOS45IDAtNTgtMTEuNy03OS4yLTMyLjhBMTExLjYgMTExLjYgMCAwMTQwMCA1MDJjMC0yOS45IDExLjctNTggMzIuOC03OS4yQzQ1NCA0MDEuNiA0ODIuMSAzOTAgNTEyIDM5MGMyOS45IDAgNTggMTEuNiA3OS4yIDMyLjhBMTExLjYgMTExLjYgMCAwMTYyNCA1MDJjMCAyOS45LTExLjcgNTgtMzIuOCA3OS4yeiIgLz48L3N2Zz4=) */
172
186
 
173
187
  /**![sort-ascending](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTgzOS42IDQzMy44TDc0OSAxNTAuNWE5LjI0IDkuMjQgMCAwMC04LjktNi41aC03Ny40Yy00LjEgMC03LjYgMi42LTguOSA2LjVsLTkxLjMgMjgzLjNjLS4zLjktLjUgMS45LS41IDIuOSAwIDUuMSA0LjIgOS4zIDkuMyA5LjNoNTYuNGM0LjIgMCA3LjgtMi44IDktNi44bDE3LjUtNjEuNmg4OWwxNy4zIDYxLjVjMS4xIDQgNC44IDYuOCA5IDYuOGg2MS4yYzEgMCAxLjktLjEgMi44LS40IDIuNC0uOCA0LjMtMi40IDUuNS00LjYgMS4xLTIuMiAxLjMtNC43LjYtNy4xek02NjMuMyAzMjUuNWwzMi44LTExNi45aDYuM2wzMi4xIDExNi45aC03MS4yem0xNDMuNSA0OTIuOUg2NzcuMnYtLjRsMTMyLjYtMTg4LjljMS4xLTEuNiAxLjctMy40IDEuNy01LjR2LTM2LjRjMC01LjEtNC4yLTkuMy05LjMtOS4zaC0yMDRjLTUuMSAwLTkuMyA0LjItOS4zIDkuM3Y0M2MwIDUuMSA0LjIgOS4zIDkuMyA5LjNoMTIyLjZ2LjRMNTg3LjcgODI4LjlhOS4zNSA5LjM1IDAgMDAtMS43IDUuNHYzNi40YzAgNS4xIDQuMiA5LjMgOS4zIDkuM2gyMTEuNGM1LjEgMCA5LjMtNC4yIDkuMy05LjN2LTQzYTkuMiA5LjIgMCAwMC05LjItOS4zek00MTYgNzAyaC03NlYxNzJjMC00LjQtMy42LTgtOC04aC01NmMtNC40IDAtOCAzLjYtOCA4djUzMGgtNzZjLTYuNyAwLTEwLjUgNy44LTYuMyAxM2wxMTIgMTQxLjlhOCA4IDAgMDAxMi42IDBsMTEyLTE0MS45YzQuMS01LjIuNC0xMy02LjMtMTN6IiAvPjwvc3ZnPg==) */
174
188
 
189
+ /**![swap-right](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjAgMCAxMDI0IDEwMjQiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTg3My4xIDU5Ni4ybC0xNjQtMjA4QTMyIDMyIDAgMDA2ODQgMzc2aC02NC44Yy02LjcgMC0xMC40IDcuNy02LjMgMTNsMTQ0LjMgMTgzSDE1MmMtNC40IDAtOCAzLjYtOCA4djYwYzAgNC40IDMuNiA4IDggOGg2OTUuOWMyNi44IDAgNDEuNy0zMC44IDI1LjItNTEuOHoiIC8+PC9zdmc+) */
190
+
191
+ /**![thunderbolt](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTg0OCAzNTkuM0g2MjcuN0w4MjUuOCAxMDljNC4xLTUuMy40LTEzLTYuMy0xM0g0MzZjLTIuOCAwLTUuNSAxLjUtNi45IDRMMTcwIDU0Ny41Yy0zLjEgNS4zLjcgMTIgNi45IDEyaDE3NC40bC04OS40IDM1Ny42Yy0xLjkgNy44IDcuNSAxMy4zIDEzLjMgNy43TDg1My41IDM3M2M1LjItNC45IDEuNy0xMy43LTUuNS0xMy43ek0zNzguMiA3MzIuNWw2MC4zLTI0MUgyODEuMWwxODkuNi0zMjcuNGgyMjQuNkw0ODcgNDI3LjRoMjExTDM3OC4yIDczMi41eiIgLz48L3N2Zz4=) */
192
+
175
193
  /**![unordered-list](data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjY0IDY0IDg5NiA4OTYiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTkxMiAxOTJIMzI4Yy00LjQgMC04IDMuNi04IDh2NTZjMCA0LjQgMy42IDggOCA4aDU4NGM0LjQgMCA4LTMuNiA4LTh2LTU2YzAtNC40LTMuNi04LTgtOHptMCAyODRIMzI4Yy00LjQgMC04IDMuNi04IDh2NTZjMCA0LjQgMy42IDggOCA4aDU4NGM0LjQgMCA4LTMuNiA4LTh2LTU2YzAtNC40LTMuNi04LTgtOHptMCAyODRIMzI4Yy00LjQgMC04IDMuNi04IDh2NTZjMCA0LjQgMy42IDggOCA4aDU4NGM0LjQgMCA4LTMuNiA4LTh2LTU2YzAtNC40LTMuNi04LTgtOHpNMTA0IDIyOGE1NiA1NiAwIDEwMTEyIDAgNTYgNTYgMCAxMC0xMTIgMHptMCAyODRhNTYgNTYgMCAxMDExMiAwIDU2IDU2IDAgMTAtMTEyIDB6bTAgMjg0YTU2IDU2IDAgMTAxMTIgMCA1NiA1NiAwIDEwLTExMiAweiIgLz48L3N2Zz4=) */
@@ -529,6 +529,31 @@ const ruleManager = {
529
529
  if (this.dataManager && typeof this.dataManager.logRequest === 'function') {
530
530
  const parsedUrl = url.parse(req.url);
531
531
  try {
532
+ // 获取功能模块名称
533
+ let featureName = '';
534
+ if (interfaceObj.featureId) {
535
+ try {
536
+ const features = await this.dataManager.getFeatures();
537
+ const feature = features.find(f => f.id === interfaceObj.featureId);
538
+ if (feature && feature.name) {
539
+ featureName = feature.name;
540
+ }
541
+ } catch (err) {
542
+ this.log(`[规则处理器] 获取功能模块信息失败: ${err.message}`);
543
+ }
544
+ }
545
+
546
+ // 获取实际命中的响应名称
547
+ let responseName = '';
548
+ if (result && result.mockInfo && result.mockInfo.responseName) {
549
+ responseName = result.mockInfo.responseName;
550
+ } else {
551
+ const headerResponse = res.getHeader('X-Whistle-Mock-Response');
552
+ if (headerResponse) {
553
+ responseName = headerResponse;
554
+ }
555
+ }
556
+
532
557
  this.dataManager.logRequest({
533
558
  type: 'mock_hit',
534
559
  eventType: 'mock_hit',
@@ -541,6 +566,8 @@ const ruleManager = {
541
566
  proxyType: proxyType,
542
567
  interfaceName: interfaceObj.name,
543
568
  interfaceId: interfaceObj.id,
569
+ featureName: featureName,
570
+ responseName: responseName,
544
571
  responseData: result,
545
572
  responseTime: new Date().toISOString(),
546
573
  contentType: res.getHeader('Content-Type') || config.contentType
package/lib/ui-server.js CHANGED
@@ -32,7 +32,17 @@ app.use(bodyParser.urlencoded({ extended: true, limit: '100mb'}));
32
32
  app.use(bodyParser.json({ limit: '100mb' }));
33
33
 
34
34
  // 处理CGI请求
35
- app.all('/cgi-bin/*', function(req, res) {
35
+ app.all('/cgi-bin/*', async function(req, res) {
36
+ // CORS 预检处理
37
+ if (req.method === 'OPTIONS') {
38
+ res.setHeader('Access-Control-Allow-Origin', req.headers.origin || '*');
39
+ res.setHeader('Access-Control-Allow-Credentials', 'true');
40
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS');
41
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Requested-With');
42
+ res.status(204).end();
43
+ return;
44
+ }
45
+
36
46
  if (req.headers.origin) {
37
47
  res.setHeader('Access-Control-Allow-Origin', req.headers.origin);
38
48
  res.setHeader('Access-Control-Allow-Credentials', true);
@@ -63,10 +73,16 @@ app.all('/cgi-bin/*', function(req, res) {
63
73
  }
64
74
 
65
75
  try {
66
- // 提供数据目录给CGI处理程序
67
- controller.call({ dataDir: DATA_DIR }, req, res);
76
+ // 提供数据目录给CGI处理程序(支持 async controller)
77
+ const result = controller.call({ dataDir: DATA_DIR }, req, res);
78
+ if (result && typeof result.then === 'function') {
79
+ await result;
80
+ }
68
81
  } catch(e) {
69
- res.status(500).end(e.stack);
82
+ console.error('[ui-server] CGI 执行错误:', e.message || e);
83
+ if (!res.headersSent) {
84
+ res.status(500).json({ code: 500, message: e.message || 'Internal Server Error' });
85
+ }
70
86
  }
71
87
  });
72
88
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "whistle.mock-plugins",
3
- "version": "1.2.2-beta.25",
3
+ "version": "1.2.2-beta.28",
4
4
  "description": "Whistle 插件,用于快速创建 API 模拟数据",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {
@@ -7,10 +7,14 @@ import {
7
7
  FileTextOutlined,
8
8
  MenuFoldOutlined,
9
9
  MenuUnfoldOutlined,
10
- ExperimentOutlined
10
+ ExperimentOutlined,
11
+ BarChartOutlined,
12
+ ThunderboltOutlined
11
13
  } from '@ant-design/icons';
12
14
  import { Link, useLocation } from 'react-router-dom';
13
15
  import VersionModal from './VersionModal';
16
+ import KeyboardShortcutsHelp from './KeyboardShortcutsHelp';
17
+ import { useKeyboardShortcuts } from '../hooks/useKeyboardShortcuts';
14
18
  import '../styles/app-layout.css';
15
19
 
16
20
  const { Header, Content, Sider } = Layout;
@@ -20,6 +24,17 @@ const AppLayout = ({ children }) => {
20
24
  const currentPath = location.pathname;
21
25
  const [collapsed, setCollapsed] = useState(false);
22
26
  const [versionModalVisible, setVersionModalVisible] = useState(false);
27
+ const [shortcutsHelpVisible, setShortcutsHelpVisible] = useState(false);
28
+
29
+ // 注册全局键盘快捷键
30
+ useKeyboardShortcuts();
31
+
32
+ // 监听快捷键帮助事件
33
+ useEffect(() => {
34
+ const handleHelp = () => setShortcutsHelpVisible(true);
35
+ window.addEventListener('keyboard-shortcuts-help', handleHelp);
36
+ return () => window.removeEventListener('keyboard-shortcuts-help', handleHelp);
37
+ }, []);
23
38
 
24
39
  // 赛博朋克主题开关
25
40
  const [enableCyber, setEnableCyber] = useState(() => {
@@ -84,6 +99,8 @@ const AppLayout = ({ children }) => {
84
99
  const getSelectedKeys = () => {
85
100
  if (currentPath === '/' || currentPath === '#/') return ['1'];
86
101
  if (currentPath.startsWith('/interface') || currentPath.includes('/interface')) return ['2'];
102
+ if (currentPath === '/stats' || currentPath.includes('/stats')) return ['4'];
103
+ if (currentPath === '/sniffer' || currentPath.includes('/sniffer')) return ['5'];
87
104
  if (currentPath === '/settings' || currentPath.includes('/settings')) return ['3'];
88
105
  return [];
89
106
  };
@@ -101,6 +118,10 @@ const AppLayout = ({ children }) => {
101
118
  if (currentPath.includes('/interface/') && currentPath !== '/interface/') {
102
119
  breadcrumbs.push({ path: currentPath, title: '功能接口详情' });
103
120
  }
121
+ } else if (currentPath === '/stats') {
122
+ breadcrumbs.push({ path: '/stats', title: '请求统计' });
123
+ } else if (currentPath === '/sniffer') {
124
+ breadcrumbs.push({ path: '/sniffer', title: '接口嗅探' });
104
125
  } else if (currentPath === '/settings') {
105
126
  breadcrumbs.push({ path: '/settings', title: '系统设置' });
106
127
  }
@@ -112,6 +133,8 @@ const AppLayout = ({ children }) => {
112
133
  const getPageTitle = () => {
113
134
  if (currentPath === '/' || currentPath === '#/') return '功能模块管理';
114
135
  if (currentPath.startsWith('/interface')) return '接口配置管理';
136
+ if (currentPath === '/stats') return '请求统计';
137
+ if (currentPath === '/sniffer') return '接口嗅探';
115
138
  if (currentPath === '/settings') return '系统设置';
116
139
  return 'Whistle Mock Plugin';
117
140
  };
@@ -128,6 +151,16 @@ const AppLayout = ({ children }) => {
128
151
  icon: <ApiOutlined />,
129
152
  label: <Link to="/interface">接口管理</Link>,
130
153
  },
154
+ {
155
+ key: '4',
156
+ icon: <BarChartOutlined />,
157
+ label: <Link to="/stats">请求统计</Link>,
158
+ },
159
+ {
160
+ key: '5',
161
+ icon: <ThunderboltOutlined />,
162
+ label: <Link to="/sniffer">接口嗅探</Link>,
163
+ },
131
164
  {
132
165
  key: '3',
133
166
  icon: <SettingOutlined />,
@@ -257,6 +290,12 @@ const AppLayout = ({ children }) => {
257
290
  visible={versionModalVisible}
258
291
  onCancel={hideVersionModal}
259
292
  />
293
+
294
+ {/* 快捷键帮助弹窗 */}
295
+ <KeyboardShortcutsHelp
296
+ visible={shortcutsHelpVisible}
297
+ onClose={() => setShortcutsHelpVisible(false)}
298
+ />
260
299
  </Layout>
261
300
  </ConfigProvider>
262
301
  );
@@ -0,0 +1,58 @@
1
+ import React from 'react';
2
+ import { Modal, Table } from 'antd';
3
+ import { getShortcutsList } from '../hooks/useKeyboardShortcuts';
4
+
5
+ const KeyboardShortcutsHelp = ({ visible, onClose }) => {
6
+ const shortcuts = getShortcutsList();
7
+
8
+ const columns = [
9
+ {
10
+ title: '快捷键',
11
+ dataIndex: 'key',
12
+ key: 'key',
13
+ width: 180,
14
+ render: (text) => (
15
+ <kbd style={{
16
+ display: 'inline-block',
17
+ padding: '4px 8px',
18
+ fontSize: '12px',
19
+ fontFamily: 'monospace',
20
+ lineHeight: 1,
21
+ color: '#333',
22
+ background: '#f5f5f5',
23
+ border: '1px solid #d9d9d9',
24
+ borderRadius: '4px',
25
+ boxShadow: '0 1px 0 rgba(0,0,0,0.1)',
26
+ }}>
27
+ {text}
28
+ </kbd>
29
+ ),
30
+ },
31
+ {
32
+ title: '功能',
33
+ dataIndex: 'desc',
34
+ key: 'desc',
35
+ },
36
+ ];
37
+
38
+ return (
39
+ <Modal
40
+ title="⌨️ 键盘快捷键"
41
+ open={visible}
42
+ onCancel={onClose}
43
+ footer={null}
44
+ width={480}
45
+ >
46
+ <Table
47
+ dataSource={shortcuts.map((s, i) => ({ ...s, id: i }))}
48
+ columns={columns}
49
+ rowKey="id"
50
+ pagination={false}
51
+ size="small"
52
+ showHeader={false}
53
+ />
54
+ </Modal>
55
+ );
56
+ };
57
+
58
+ export default KeyboardShortcutsHelp;
@@ -252,7 +252,7 @@ const InterfaceTestModal = ({
252
252
  <span>
253
253
  <span style={{ color: '#d9d9d9' }}>{value}</span>
254
254
  {' '} → {' '}
255
- <span style={{ color: '#00ff41', fontWeight: 'bold' }}>{generatedValue}</span>
255
+ <span style={{ color: '#52c41a', fontWeight: 'bold' }}>{generatedValue}</span>
256
256
  <span style={{ color: '#8c8c8c', fontSize: '12px' }}> (随机生成)</span>
257
257
  </span>
258
258
  ) : generatedValue}
@@ -0,0 +1,165 @@
1
+ import React, { useState } from 'react';
2
+ import { Button, Input, message, Space, Tabs, Tag } from 'antd';
3
+ import { DownloadOutlined, ThunderboltOutlined, CopyOutlined } from '@ant-design/icons';
4
+ import axios from 'axios';
5
+
6
+ const { TextArea } = Input;
7
+
8
+ /**
9
+ * JSON 转 Mock.js 模板
10
+ * 简单转换:字符串 -> '@string', 数字 -> '@integer(1,100)', 布尔 -> '@boolean'
11
+ * 保留对象结构
12
+ */
13
+ const jsonToMockTemplate = (obj, depth = 0) => {
14
+ if (obj === null) return null;
15
+ if (typeof obj === 'string') {
16
+ // 保留特定格式字符串的提示
17
+ if (/^\d{4}-\d{2}-\d{2}/.test(obj)) return '@date';
18
+ if (/^\d{4}-\d{2}-\d{2}T/.test(obj)) return '@datetime';
19
+ if (/^https?:\/\//.test(obj)) return '@url';
20
+ if (/^\w+@\w+\.\w+/.test(obj)) return '@email';
21
+ if (/^1[3-9]\d{9}$/.test(obj)) return '@string("number", 11)';
22
+ return '@csentence(3, 10)';
23
+ }
24
+ if (typeof obj === 'number') {
25
+ if (Number.isInteger(obj)) return '@integer(1, 999)';
26
+ return '@float(1, 100, 2, 2)';
27
+ }
28
+ if (typeof obj === 'boolean') return '@boolean';
29
+ if (Array.isArray(obj)) {
30
+ if (obj.length === 0) return [];
31
+ const template = jsonToMockTemplate(obj[0], depth + 1);
32
+ // 用 |1-5 表示数组长度随机
33
+ return [template];
34
+ }
35
+ if (typeof obj === 'object') {
36
+ const result = {};
37
+ for (const [key, value] of Object.entries(obj)) {
38
+ result[key] = jsonToMockTemplate(value, depth + 1);
39
+ }
40
+ return result;
41
+ }
42
+ return obj;
43
+ };
44
+
45
+ const RealResponseRecorder = ({ urlPattern, onApply }) => {
46
+ const [fetchUrl, setFetchUrl] = useState('');
47
+ const [loading, setLoading] = useState(false);
48
+ const [response, setResponse] = useState(null);
49
+ const [activeTab, setActiveTab] = useState('preview');
50
+
51
+ const handleFetch = async () => {
52
+ const targetUrl = fetchUrl.trim() || urlPattern;
53
+ if (!targetUrl) {
54
+ message.warning('请输入目标 URL');
55
+ return;
56
+ }
57
+ setLoading(true);
58
+ try {
59
+ const res = await axios.post('/cgi-bin/proxy-fetch', {
60
+ url: targetUrl,
61
+ method: 'GET',
62
+ });
63
+ if (res.data.code === 0) {
64
+ setResponse(res.data.data);
65
+ setActiveTab('preview');
66
+ message.success('抓取成功');
67
+ } else {
68
+ throw new Error(res.data.message);
69
+ }
70
+ } catch (err) {
71
+ message.error('抓取失败: ' + (err.response?.data?.message || err.message));
72
+ } finally {
73
+ setLoading(false);
74
+ }
75
+ };
76
+
77
+ const handleGenerateMock = () => {
78
+ if (!response?.body) return;
79
+ try {
80
+ const json = JSON.parse(response.body);
81
+ const template = jsonToMockTemplate(json);
82
+ const mockTemplate = JSON.stringify(template, null, 2);
83
+ onApply?.(mockTemplate);
84
+ message.success('Mock 模板已生成');
85
+ } catch (e) {
86
+ message.error('响应不是 JSON,无法生成模板: ' + e.message);
87
+ }
88
+ };
89
+
90
+ const handleCopy = () => {
91
+ if (response?.body) {
92
+ navigator.clipboard.writeText(response.body);
93
+ message.success('已复制');
94
+ }
95
+ };
96
+
97
+ return (
98
+ <div style={{ padding: '16px 0' }}>
99
+ <Space direction="vertical" style={{ width: '100%' }}>
100
+ <Space style={{ width: '100%' }}>
101
+ <Input
102
+ placeholder={urlPattern || '输入真实接口 URL'}
103
+ value={fetchUrl}
104
+ onChange={(e) => setFetchUrl(e.target.value)}
105
+ style={{ width: 400 }}
106
+ onPressEnter={handleFetch}
107
+ />
108
+ <Button
109
+ type="primary"
110
+ icon={<ThunderboltOutlined />}
111
+ loading={loading}
112
+ onClick={handleFetch}
113
+ >
114
+ 抓取真实响应
115
+ </Button>
116
+ </Space>
117
+
118
+ {response && (
119
+ <>
120
+ <Space>
121
+ <Tag color={response.status >= 200 && response.status < 300 ? 'success' : 'error'}>
122
+ {response.status} {response.statusText}
123
+ </Tag>
124
+ <Button size="small" icon={<CopyOutlined />} onClick={handleCopy}>
125
+ 复制原文
126
+ </Button>
127
+ <Button size="small" icon={<DownloadOutlined />} onClick={handleGenerateMock} type="primary">
128
+ 生成 Mock 模板
129
+ </Button>
130
+ </Space>
131
+
132
+ <Tabs activeKey={activeTab} onChange={setActiveTab}>
133
+ <Tabs.TabPane tab="响应预览" key="preview">
134
+ <TextArea
135
+ value={response.body}
136
+ readOnly
137
+ rows={12}
138
+ style={{ fontFamily: 'monospace', fontSize: 12 }}
139
+ />
140
+ </Tabs.TabPane>
141
+ <Tabs.TabPane tab="生成的 Mock 模板" key="mock">
142
+ <TextArea
143
+ value={(() => {
144
+ try {
145
+ const json = JSON.parse(response.body);
146
+ const template = jsonToMockTemplate(json);
147
+ return JSON.stringify(template, null, 2);
148
+ } catch (e) {
149
+ return '// 响应不是 JSON,无法生成模板\n' + response.body;
150
+ }
151
+ })()}
152
+ readOnly
153
+ rows={12}
154
+ style={{ fontFamily: 'monospace', fontSize: 12 }}
155
+ />
156
+ </Tabs.TabPane>
157
+ </Tabs>
158
+ </>
159
+ )}
160
+ </Space>
161
+ </div>
162
+ );
163
+ };
164
+
165
+ export default RealResponseRecorder;
@@ -407,7 +407,7 @@ const ResponseContentEditor = ({
407
407
  <span>响应内容编辑</span>
408
408
  <Badge
409
409
  count={responses.length}
410
- style={{ backgroundColor: '#00ff41' }}
410
+ style={{ backgroundColor: '#52c41a' }}
411
411
  />
412
412
  </div>
413
413
  }
@@ -446,8 +446,8 @@ const ResponseContentEditor = ({
446
446
  display: 'flex',
447
447
  alignItems: 'center',
448
448
  gap: '4px',
449
- borderColor: '#00ff41',
450
- color: '#00ff41'
449
+ borderColor: '#52c41a',
450
+ color: '#52c41a'
451
451
  }}
452
452
  >
453
453
  预览
@@ -0,0 +1,99 @@
1
+ import { useEffect, useCallback } from 'react';
2
+ import { useHistory } from 'react-router-dom';
3
+ import { message } from 'antd';
4
+
5
+ /**
6
+ * 全局键盘快捷键 Hook
7
+ *
8
+ * 支持的快捷键:
9
+ * - Esc: 关闭当前弹窗/抽屉(触发全局 close-modal 事件)
10
+ * - Ctrl/Cmd + S: 保存(触发全局 save-form 事件)
11
+ * - Ctrl/Cmd + 1: 跳转到功能模块
12
+ * - Ctrl/Cmd + 2: 跳转到接口管理
13
+ * - Ctrl/Cmd + 3: 跳转到系统设置
14
+ * - Ctrl/Cmd + 4: 跳转到请求统计
15
+ * - Ctrl/Cmd + 5: 跳转到接口嗅探
16
+ * - Ctrl/Cmd + Shift + N: 新建接口(在接口管理页触发)
17
+ * - Ctrl/Cmd + K: 聚焦搜索框(触发全局 focus-search 事件)
18
+ * - Shift + ?: 显示快捷键帮助
19
+ */
20
+
21
+ const SHORTCUTS = [
22
+ { key: 'Esc', desc: '关闭弹窗/抽屉' },
23
+ { key: 'Ctrl + S', desc: '保存表单' },
24
+ { key: 'Ctrl + 1', desc: '功能模块' },
25
+ { key: 'Ctrl + 2', desc: '接口管理' },
26
+ { key: 'Ctrl + 3', desc: '系统设置' },
27
+ { key: 'Ctrl + 4', desc: '请求统计' },
28
+ { key: 'Ctrl + 5', desc: '接口嗅探' },
29
+ { key: 'Ctrl + Shift + N', desc: '新建接口' },
30
+ { key: 'Ctrl + K', desc: '聚焦搜索' },
31
+ { key: 'Shift + ?', desc: '快捷键帮助' },
32
+ ];
33
+
34
+ export const getShortcutsList = () => SHORTCUTS;
35
+
36
+ export const useKeyboardShortcuts = () => {
37
+ const history = useHistory();
38
+
39
+ const handleKeyDown = useCallback((e) => {
40
+ const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
41
+ const ctrlOrCmd = isMac ? e.metaKey : e.ctrlKey;
42
+
43
+ // 忽略在输入框、文本域中的快捷键(导航类除外)
44
+ const tagName = e.target?.tagName?.toLowerCase();
45
+ const isInput = tagName === 'input' || tagName === 'textarea' || e.target?.isContentEditable;
46
+
47
+ // Shift + ? 显示帮助(非输入框时)
48
+ if (e.shiftKey && e.key === '?' && !isInput) {
49
+ e.preventDefault();
50
+ window.dispatchEvent(new CustomEvent('keyboard-shortcuts-help'));
51
+ return;
52
+ }
53
+
54
+ // Esc 关闭弹窗/抽屉
55
+ if (e.key === 'Escape') {
56
+ window.dispatchEvent(new CustomEvent('shortcut-esc'));
57
+ return;
58
+ }
59
+
60
+ // Ctrl/Cmd + 数字键 导航
61
+ if (ctrlOrCmd && !e.shiftKey && /^[1-5]$/.test(e.key)) {
62
+ e.preventDefault();
63
+ const routes = ['/', '/interface', '/settings', '/stats', '/sniffer'];
64
+ const names = ['功能模块', '接口管理', '系统设置', '请求统计', '接口嗅探'];
65
+ const idx = parseInt(e.key, 10) - 1;
66
+ history.push(routes[idx]);
67
+ message.success(`已切换到${names[idx]}`);
68
+ return;
69
+ }
70
+
71
+ // Ctrl/Cmd + S 保存
72
+ if (ctrlOrCmd && e.key.toLowerCase() === 's' && !e.shiftKey) {
73
+ e.preventDefault();
74
+ window.dispatchEvent(new CustomEvent('shortcut-save'));
75
+ return;
76
+ }
77
+
78
+ // Ctrl/Cmd + Shift + N 新建接口
79
+ if (ctrlOrCmd && e.shiftKey && e.key.toLowerCase() === 'n') {
80
+ e.preventDefault();
81
+ window.dispatchEvent(new CustomEvent('shortcut-new-interface'));
82
+ return;
83
+ }
84
+
85
+ // Ctrl/Cmd + K 聚焦搜索
86
+ if (ctrlOrCmd && e.key.toLowerCase() === 'k' && !e.shiftKey) {
87
+ e.preventDefault();
88
+ window.dispatchEvent(new CustomEvent('shortcut-focus-search'));
89
+ return;
90
+ }
91
+ }, [history]);
92
+
93
+ useEffect(() => {
94
+ document.addEventListener('keydown', handleKeyDown);
95
+ return () => document.removeEventListener('keydown', handleKeyDown);
96
+ }, [handleKeyDown]);
97
+ };
98
+
99
+ export default useKeyboardShortcuts;
package/ui/src/index.js CHANGED
@@ -14,6 +14,8 @@ require('./styles/cyber-theme.css');
14
14
  import MockData from './pages/MockDataV2';
15
15
  import InterfaceManagement from './pages/InterfaceManagementV2';
16
16
  import Settings from './pages/Settings';
17
+ import Stats from './pages/Stats';
18
+ import Sniffer from './pages/Sniffer';
17
19
 
18
20
  const App = () => {
19
21
  return (
@@ -23,6 +25,8 @@ const App = () => {
23
25
  <Route exact path="/" component={MockData} />
24
26
  <Route exact path="/interface" component={InterfaceManagement} />
25
27
  <Route path="/interface/:featureId" component={InterfaceManagement} />
28
+ <Route path="/stats" component={Stats} />
29
+ <Route path="/sniffer" component={Sniffer} />
26
30
  <Route path="/settings" component={Settings} />
27
31
  <Redirect to="/" />
28
32
  </Switch>