@@ -47,6 +47,7 @@ interface Filter {
4747 }
4848 response: {
4949 headers: FilterItem
50+ statusCode: FilterItem
5051 }
5152 clientIP: FilterItem
5253}
@@ -68,25 +69,26 @@ const filter = reactive<Filter>({
6869 headers: { checkbox: false , value: [{ name: ' ' , value: ' ' }]}
6970 },
7071 response: {
71- headers: { checkbox: false , value: [{ name: ' ' , value: ' ' }]}
72+ headers: { checkbox: false , value: [{ name: ' ' , value: ' ' }]},
73+ statusCode: { checkbox: false , value: ' ' , title: ` <b>Status Code Filter:</b><br>
74+ • Multiple entries (comma-separated): 200, 404<br>
75+ • Negation: prefix with '-' (e.g., -501)<br>
76+ • Range: start-end (e.g., 200-300) `
77+ }
7278 },
7379 clientIP: { checkbox: false , value: ' ' , title: ` <b>IP Filter Options:</b><br>
7480 • Multiple entries (comma-separated): 192.168.0.1,10.0.0.1<br>
75- • Negation: - 127.0.0.1<br>
81+ • Negation: prefix with '-' (e.g., - 127.0.0.1) <br>
7682 • CIDR notation: 192.168.0.0/24 ` }
77- })
83+ }
84+ )
7885
7986onMounted (() => {
8087 dialog = Modal .getOrCreateInstance (dialogRef .value ! );
8188 const s = localStorage .getItem (` http-requests-${getFilterCacheKey ()} ` )
8289 if (s && s !== ' ' ) {
8390 const saved = JSON .parse (s )
84- type Key = keyof Filter
85- for (const key in filter ) {
86- if (saved [key ] !== undefined ) {
87- filter [key as Key ] = saved [key ]
88- }
89- }
91+ mergeDeep (filter , saved )
9092 }
9193 const tooltipTriggerList = document .querySelectorAll (' [data-bs-toggle="tooltip"]' )
9294 tooltipTriggerList .forEach (el => new Tooltip (el , {
@@ -211,6 +213,44 @@ const events = computed<ServiceEvent[]>(() => {
211213 return matched || values .every (v => v [0 ] === ' -' )
212214 })
213215 }
216+ if (filter .response .statusCode .checkbox && filter .response .statusCode .value .length > 0 ) {
217+ const values = (filter .response .statusCode .value as string ).split (' ,' ).map (x => x .trim ()).filter (x => x .length > 0 )
218+ result = result .filter (x => {
219+ const data = x .data as HttpEventData
220+
221+ let matched = false ;
222+
223+ for (let value of values ) {
224+ let not = false
225+ if (value [0 ] === ' -' ) {
226+ not = true
227+ value = value .substring (1 )
228+ }
229+ let isMatch = false
230+ if (value .includes (' -' )) {
231+ const parts = value .split (' -' ).map (x => x .trim ())
232+ if (parts .length !== 2 ) {
233+ continue
234+ }
235+ const min = parseInt (parts [0 ]! )
236+ const max = parseInt (parts [1 ]! )
237+ isMatch = data .response .statusCode >= min && data .response .statusCode <= max
238+ } else {
239+ isMatch = data .response .statusCode .toString () === value
240+ }
241+
242+ if (not && isMatch ) {
243+ // Negated value matches → reject immediately
244+ return false
245+ }
246+ if (! not && isMatch ) {
247+ // Normal value matches → mark as matched
248+ matched = true ;
249+ }
250+ }
251+ return matched || values .every (v => v [0 ] === ' -' )
252+ })
253+ }
214254
215255 return result
216256})
@@ -318,6 +358,9 @@ const activeFiltersCount = computed(() => {
318358 if (filter .response .headers .checkbox && filter .response .headers .value .length > 1 ) {
319359 counter ++ ;
320360 }
361+ if (filter .response .statusCode .checkbox && filter .response .statusCode .value .length > 0 ) {
362+ counter ++
363+ }
321364 if (filter .clientIP .checkbox && filter .clientIP .value .length > 0 ) {
322365 counter ++
323366 }
@@ -486,6 +529,28 @@ function cidrMatch(ip: string, cidr: string): boolean {
486529
487530 return (ipToInt (ip ) & mask ) === (ipToInt (range ! ) & mask );
488531}
532+ function mergeDeep<T >(target : T , source : Partial <T >): T {
533+ for (const key in source ) {
534+ const sourceValue = source [key ];
535+ const targetValue = target [key ];
536+
537+ if (
538+ sourceValue &&
539+ typeof sourceValue === ' object' &&
540+ ! Array .isArray (sourceValue ) &&
541+ targetValue &&
542+ typeof targetValue === ' object' &&
543+ ! Array .isArray (targetValue )
544+ ) {
545+ // Nested object: recurse
546+ mergeDeep (targetValue , sourceValue );
547+ } else if (sourceValue !== undefined ) {
548+ // Primitive or array: overwrite
549+ target [key ] = sourceValue as any ;
550+ }
551+ }
552+ return target ;
553+ }
489554 </script >
490555
491556<template >
@@ -651,6 +716,23 @@ function cidrMatch(ip: string, cidr: string): boolean {
651716 </div >
652717 </div >
653718
719+ <!-- Response Status Code -->
720+ <div class =" row mb-3" >
721+ <div class =" col-4" >
722+ <div class =" form-check" data-bs-toggle =" tooltip" data-bs-delay =' {"show": 200, "hide": 100}' :title =" filter.response.statusCode.title" data-bs-offset =" [-150, 0]" data-bs-html =" true" >
723+ <input class =" form-check-input" type =" checkbox" v-model =" filter.response.statusCode.checkbox" id =" statusCode" >
724+ <label class =" form-check-label" for =" statusCode" >Status Code</label >
725+ </div >
726+ </div >
727+ <div class =" col" v-show =" filter.response.statusCode.checkbox" >
728+ <div class =" col ps-0 pe-1" data-bs-toggle =" tooltip" data-bs-delay =' {"show": 200, "hide": 100}' :title =" filter.response.statusCode.title" data-bs-html =" true" >
729+ <div class =" row me-0" >
730+ <input type =" text" class =" form-control form-control-sm" id =" statusCode" v-model =" filter.response.statusCode.value" >
731+ </div >
732+ </div >
733+ </div >
734+ </div >
735+
654736 <!-- Response Headers -->
655737 <div class =" row" :class =" { 'mb-3': responseHeaderErrors.length == 0 }" >
656738 <div class =" col-4" >
0 commit comments