diff --git a/.editorconfig b/.editorconfig index 26483be..24d1f18 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,174 +1,152 @@ -# EditorConfig is awesome: https://EditorConfig.org - -# top-most EditorConfig file root = true -# Don't use tabs for indentation. +# All files [*] indent_style = space -# (Please don't specify an indent_size here; that has too many unintended consequences.) - -# Code files -[*.{cs,csx,vb,vbx}] -indent_size = 4 -insert_final_newline = true -charset = utf-8-bom -# XML project files -[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] +# Xml files +[*.xml] indent_size = 2 -# XML config files -[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}] -indent_size = 2 +# C# files +[*.cs] -# JSON files -[*.json] -indent_size = 2 +#### Core EditorConfig Options #### -# Powershell files -[*.ps1] -indent_size = 2 +# Indentation and spacing +indent_size = 4 +tab_width = 4 -# Shell script files -[*.sh] -end_of_line = lf -indent_size = 2 +# New line preferences +insert_final_newline = false -# Dotnet code style settings: +#### .NET Coding Conventions #### [*.{cs,vb}] -# IDE0055: Fix formatting -dotnet_diagnostic.IDE0055.severity = warning - -# Sort using and Import directives with System.* appearing first -dotnet_sort_system_directives_first = true +# Organize usings dotnet_separate_import_directive_groups = false -# Avoid "this." and "Me." if not necessary -dotnet_style_qualification_for_field = false:refactoring -dotnet_style_qualification_for_property = false:refactoring -dotnet_style_qualification_for_method = false:refactoring -dotnet_style_qualification_for_event = false:refactoring - -# Use language keywords instead of framework type names for type references -dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion -dotnet_style_predefined_type_for_member_access = true:suggestion - -# Suggest more modern language features when available -dotnet_style_object_initializer = true:suggestion -dotnet_style_collection_initializer = true:suggestion -dotnet_style_coalesce_expression = true:suggestion -dotnet_style_null_propagation = true:suggestion -dotnet_style_explicit_tuple_names = true:suggestion - -# Non-private static fields are PascalCase -dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.severity = suggestion -dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.symbols = non_private_static_fields -dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.style = non_private_static_field_style - -dotnet_naming_symbols.non_private_static_fields.applicable_kinds = field -dotnet_naming_symbols.non_private_static_fields.applicable_accessibilities = public, protected, internal, protected_internal, private_protected -dotnet_naming_symbols.non_private_static_fields.required_modifiers = static - -dotnet_naming_style.non_private_static_field_style.capitalization = pascal_case - -# Non-private readonly fields are PascalCase -dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.severity = suggestion -dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.symbols = non_private_readonly_fields -dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.style = non_private_readonly_field_style - -dotnet_naming_symbols.non_private_readonly_fields.applicable_kinds = field -dotnet_naming_symbols.non_private_readonly_fields.applicable_accessibilities = public, protected, internal, protected_internal, private_protected -dotnet_naming_symbols.non_private_readonly_fields.required_modifiers = readonly - -dotnet_naming_style.non_private_readonly_field_style.capitalization = pascal_case - -# Constants are PascalCase -dotnet_naming_rule.constants_should_be_pascal_case.severity = suggestion -dotnet_naming_rule.constants_should_be_pascal_case.symbols = constants -dotnet_naming_rule.constants_should_be_pascal_case.style = constant_style - -dotnet_naming_symbols.constants.applicable_kinds = field, local -dotnet_naming_symbols.constants.required_modifiers = const - -dotnet_naming_style.constant_style.capitalization = pascal_case - -# Static fields are camelCase and start with s_ -dotnet_naming_rule.static_fields_should_be_camel_case.severity = suggestion -dotnet_naming_rule.static_fields_should_be_camel_case.symbols = static_fields -dotnet_naming_rule.static_fields_should_be_camel_case.style = static_field_style - -dotnet_naming_symbols.static_fields.applicable_kinds = field -dotnet_naming_symbols.static_fields.required_modifiers = static - -dotnet_naming_style.static_field_style.capitalization = camel_case -dotnet_naming_style.static_field_style.required_prefix = s_ - -# Instance fields are camelCase and start with _ -dotnet_naming_rule.instance_fields_should_be_camel_case.severity = suggestion -dotnet_naming_rule.instance_fields_should_be_camel_case.symbols = instance_fields -dotnet_naming_rule.instance_fields_should_be_camel_case.style = instance_field_style - -dotnet_naming_symbols.instance_fields.applicable_kinds = field - -dotnet_naming_style.instance_field_style.capitalization = camel_case -dotnet_naming_style.instance_field_style.required_prefix = _ - -# Locals and parameters are camelCase -dotnet_naming_rule.locals_should_be_camel_case.severity = suggestion -dotnet_naming_rule.locals_should_be_camel_case.symbols = locals_and_parameters -dotnet_naming_rule.locals_should_be_camel_case.style = camel_case_style - -dotnet_naming_symbols.locals_and_parameters.applicable_kinds = parameter, local - -dotnet_naming_style.camel_case_style.capitalization = camel_case +dotnet_sort_system_directives_first = true +file_header_template = unset -# Local functions are PascalCase -dotnet_naming_rule.local_functions_should_be_pascal_case.severity = suggestion -dotnet_naming_rule.local_functions_should_be_pascal_case.symbols = local_functions -dotnet_naming_rule.local_functions_should_be_pascal_case.style = local_function_style +# this. and Me. preferences +dotnet_style_qualification_for_event = false:silent +dotnet_style_qualification_for_field = false:silent +dotnet_style_qualification_for_method = false:silent +dotnet_style_qualification_for_property = false:silent -dotnet_naming_symbols.local_functions.applicable_kinds = local_function +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true:silent +dotnet_style_predefined_type_for_member_access = true:silent -dotnet_naming_style.local_function_style.capitalization = pascal_case +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent -# By default, name items with PascalCase -dotnet_naming_rule.members_should_be_pascal_case.severity = suggestion -dotnet_naming_rule.members_should_be_pascal_case.symbols = all_members -dotnet_naming_rule.members_should_be_pascal_case.style = pascal_case_style +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent -dotnet_naming_symbols.all_members.applicable_kinds = * - -dotnet_naming_style.pascal_case_style.capitalization = pascal_case +# Expression-level preferences +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_namespace_match_folder = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_object_initializer = true:suggestion +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_prefer_auto_properties = true:suggestion +dotnet_style_prefer_collection_expression = when_types_loosely_match:suggestion +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion +dotnet_style_prefer_conditional_expression_over_return = true:suggestion +dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion + +# Field preferences +dotnet_style_readonly_field = true:warning + +# Parameter preferences +dotnet_code_quality_unused_parameters = all:suggestion + +# Suppression preferences +dotnet_remove_unnecessary_suppression_exclusions = none + +#### C# Coding Conventions #### +[*.cs] -# error RS2008: Enable analyzer release tracking for the analyzer project containing rule '{0}' -dotnet_diagnostic.RS2008.severity = none +# var preferences +csharp_style_var_elsewhere = false:silent +csharp_style_var_for_built_in_types = false:silent +csharp_style_var_when_type_is_apparent = false:silent + +# Expression-bodied members +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_lambdas = true:suggestion +csharp_style_expression_bodied_local_functions = false:silent +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent + +# Pattern matching preferences +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_prefer_extended_property_pattern = true:suggestion +csharp_style_prefer_not_pattern = true:suggestion +csharp_style_prefer_pattern_matching = true:silent +csharp_style_prefer_switch_expression = true:suggestion -# IDE0073: File header -# dotnet_diagnostic.IDE0073.severity = warning -# file_header_template = Licensed to the .NET Foundation under one or more agreements.\nThe .NET Foundation licenses this file to you under the MIT license.\nSee the LICENSE file in the project root for more information. +# Null-checking preferences +csharp_style_conditional_delegate_call = true:suggestion -# IDE0035: Remove unreachable code -dotnet_diagnostic.IDE0035.severity = warning +# Modifier preferences +csharp_prefer_static_anonymous_function = true:suggestion +csharp_prefer_static_local_function = true:warning +csharp_preferred_modifier_order = public,private,protected,internal,file,const,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async:suggestion +csharp_style_prefer_readonly_struct = true:suggestion +csharp_style_prefer_readonly_struct_member = true:suggestion -# IDE0036: Order modifiers -dotnet_diagnostic.IDE0036.severity = warning +# Code-block preferences +csharp_prefer_braces = true:silent +csharp_prefer_simple_using_statement = true:suggestion +csharp_style_namespace_declarations = file_scoped:suggestion +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_primary_constructors = true:suggestion +csharp_style_prefer_top_level_statements = true:silent + +# Expression-level preferences +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_prefer_index_operator = true:suggestion +csharp_style_prefer_local_over_anonymous_function = true:suggestion +csharp_style_prefer_null_check_over_type_check = true:suggestion +csharp_style_prefer_range_operator = true:suggestion +csharp_style_prefer_tuple_swap = true:suggestion +csharp_style_prefer_utf8_string_literals = true:suggestion +csharp_style_throw_expression = true:suggestion +csharp_style_unused_value_assignment_preference = discard_variable:suggestion +csharp_style_unused_value_expression_statement_preference = discard_variable:silent -# IDE0043: Format string contains invalid placeholder -dotnet_diagnostic.IDE0043.severity = warning +# 'using' directive preferences +csharp_using_directive_placement = outside_namespace:silent -# IDE0044: Make field readonly -dotnet_diagnostic.IDE0044.severity = warning +#### C# Formatting Rules #### -# CSharp code style settings: -[*.cs] -# Newline settings -csharp_new_line_before_open_brace = all -csharp_new_line_before_else = true +# New line preferences csharp_new_line_before_catch = true +csharp_new_line_before_else = true csharp_new_line_before_finally = true -csharp_new_line_before_members_in_object_initializers = true csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_open_brace = all csharp_new_line_between_query_expression_clauses = true # Indentation preferences @@ -176,30 +154,8 @@ csharp_indent_block_contents = true csharp_indent_braces = false csharp_indent_case_contents = true csharp_indent_case_contents_when_block = true +csharp_indent_labels = one_less_than_current csharp_indent_switch_labels = true -csharp_indent_labels = flush_left - -# Prefer "var" everywhere -csharp_style_var_for_built_in_types = true:suggestion -csharp_style_var_when_type_is_apparent = true:suggestion -csharp_style_var_elsewhere = true:suggestion - -# Prefer method-like constructs to have a block body -csharp_style_expression_bodied_methods = false:none -csharp_style_expression_bodied_constructors = false:none -csharp_style_expression_bodied_operators = false:none - -# Prefer property-like constructs to have an expression-body -csharp_style_expression_bodied_properties = true:none -csharp_style_expression_bodied_indexers = true:none -csharp_style_expression_bodied_accessors = true:none - -# Suggest more modern language features when available -csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion -csharp_style_pattern_matching_over_as_with_null_check = true:suggestion -csharp_style_inlined_variable_declaration = true:suggestion -csharp_style_throw_expression = true:suggestion -csharp_style_conditional_delegate_call = true:suggestion # Space preferences csharp_space_after_cast = false @@ -209,7 +165,7 @@ csharp_space_after_dot = false csharp_space_after_keywords_in_control_flow_statements = true csharp_space_after_semicolon_in_for_statement = true csharp_space_around_binary_operators = before_and_after -csharp_space_around_declaration_statements = do_not_ignore +csharp_space_around_declaration_statements = false csharp_space_before_colon_in_inheritance_clause = true csharp_space_before_comma = false csharp_space_before_dot = false @@ -225,52 +181,198 @@ csharp_space_between_method_declaration_parameter_list_parentheses = false csharp_space_between_parentheses = false csharp_space_between_square_brackets = false -# Blocks are allowed -csharp_prefer_braces = true:silent +# Wrapping preferences csharp_preserve_single_line_blocks = true csharp_preserve_single_line_statements = true -# warning RS0037: PublicAPI.txt is missing '#nullable enable' -dotnet_diagnostic.RS0037.severity = none +#### Naming styles #### +[*.{cs,vb}] + +# Naming rules + +dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.symbols = types_and_namespaces +dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.style = pascalcase + +dotnet_naming_rule.interfaces_should_be_ipascalcase.severity = suggestion +dotnet_naming_rule.interfaces_should_be_ipascalcase.symbols = interfaces +dotnet_naming_rule.interfaces_should_be_ipascalcase.style = ipascalcase + +dotnet_naming_rule.type_parameters_should_be_tpascalcase.severity = suggestion +dotnet_naming_rule.type_parameters_should_be_tpascalcase.symbols = type_parameters +dotnet_naming_rule.type_parameters_should_be_tpascalcase.style = tpascalcase + +dotnet_naming_rule.methods_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.methods_should_be_pascalcase.symbols = methods +dotnet_naming_rule.methods_should_be_pascalcase.style = pascalcase + +dotnet_naming_rule.properties_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.properties_should_be_pascalcase.symbols = properties +dotnet_naming_rule.properties_should_be_pascalcase.style = pascalcase + +dotnet_naming_rule.events_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.events_should_be_pascalcase.symbols = events +dotnet_naming_rule.events_should_be_pascalcase.style = pascalcase + +dotnet_naming_rule.local_variables_should_be_camelcase.severity = suggestion +dotnet_naming_rule.local_variables_should_be_camelcase.symbols = local_variables +dotnet_naming_rule.local_variables_should_be_camelcase.style = camelcase + +dotnet_naming_rule.local_constants_should_be_camelcase.severity = suggestion +dotnet_naming_rule.local_constants_should_be_camelcase.symbols = local_constants +dotnet_naming_rule.local_constants_should_be_camelcase.style = camelcase + +dotnet_naming_rule.parameters_should_be_camelcase.severity = suggestion +dotnet_naming_rule.parameters_should_be_camelcase.symbols = parameters +dotnet_naming_rule.parameters_should_be_camelcase.style = camelcase + +dotnet_naming_rule.public_fields_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.public_fields_should_be_pascalcase.symbols = public_fields +dotnet_naming_rule.public_fields_should_be_pascalcase.style = pascalcase + +dotnet_naming_rule.private_fields_should_be__camelcase.severity = suggestion +dotnet_naming_rule.private_fields_should_be__camelcase.symbols = private_fields +dotnet_naming_rule.private_fields_should_be__camelcase.style = _camelcase -[src/CodeStyle/**.{cs,vb}] -# warning RS0005: Do not use generic CodeAction.Create to create CodeAction -dotnet_diagnostic.RS0005.severity = none +dotnet_naming_rule.private_static_fields_should_be_s_camelcase.severity = suggestion +dotnet_naming_rule.private_static_fields_should_be_s_camelcase.symbols = private_static_fields +dotnet_naming_rule.private_static_fields_should_be_s_camelcase.style = s_camelcase -[src/{Analyzers,CodeStyle,Features,Workspaces,EditorFeatures, VisualStudio}/**/*.{cs,vb}] +dotnet_naming_rule.public_constant_fields_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.public_constant_fields_should_be_pascalcase.symbols = public_constant_fields +dotnet_naming_rule.public_constant_fields_should_be_pascalcase.style = pascalcase -# IDE0011: Add braces -csharp_prefer_braces = when_multiline:warning -# NOTE: We need the below severity entry for Add Braces due to https://github.com/dotnet/roslyn/issues/44201 -dotnet_diagnostic.IDE0011.severity = warning +dotnet_naming_rule.private_constant_fields_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.private_constant_fields_should_be_pascalcase.symbols = private_constant_fields +dotnet_naming_rule.private_constant_fields_should_be_pascalcase.style = pascalcase + +dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.symbols = public_static_readonly_fields +dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.style = pascalcase + +dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.symbols = private_static_readonly_fields +dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.style = pascalcase + +dotnet_naming_rule.enums_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.enums_should_be_pascalcase.symbols = enums +dotnet_naming_rule.enums_should_be_pascalcase.style = pascalcase + +dotnet_naming_rule.local_functions_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.local_functions_should_be_pascalcase.symbols = local_functions +dotnet_naming_rule.local_functions_should_be_pascalcase.style = pascalcase + +dotnet_naming_rule.non_field_members_should_be_pascalcase.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascalcase.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascalcase.style = pascalcase + +# Symbol specifications + +dotnet_naming_symbols.interfaces.applicable_kinds = interface +dotnet_naming_symbols.interfaces.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interfaces.required_modifiers = + +dotnet_naming_symbols.enums.applicable_kinds = enum +dotnet_naming_symbols.enums.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.enums.required_modifiers = + +dotnet_naming_symbols.events.applicable_kinds = event +dotnet_naming_symbols.events.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.events.required_modifiers = + +dotnet_naming_symbols.methods.applicable_kinds = method +dotnet_naming_symbols.methods.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.methods.required_modifiers = + +dotnet_naming_symbols.properties.applicable_kinds = property +dotnet_naming_symbols.properties.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.properties.required_modifiers = + +dotnet_naming_symbols.public_fields.applicable_kinds = field +dotnet_naming_symbols.public_fields.applicable_accessibilities = public, internal +dotnet_naming_symbols.public_fields.required_modifiers = + +dotnet_naming_symbols.private_fields.applicable_kinds = field +dotnet_naming_symbols.private_fields.applicable_accessibilities = private, protected, protected_internal, private_protected +dotnet_naming_symbols.private_fields.required_modifiers = + +dotnet_naming_symbols.private_static_fields.applicable_kinds = field +dotnet_naming_symbols.private_static_fields.applicable_accessibilities = private, protected, protected_internal, private_protected +dotnet_naming_symbols.private_static_fields.required_modifiers = static + +dotnet_naming_symbols.types_and_namespaces.applicable_kinds = namespace, class, struct, interface, enum +dotnet_naming_symbols.types_and_namespaces.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types_and_namespaces.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +dotnet_naming_symbols.type_parameters.applicable_kinds = namespace +dotnet_naming_symbols.type_parameters.applicable_accessibilities = * +dotnet_naming_symbols.type_parameters.required_modifiers = + +dotnet_naming_symbols.private_constant_fields.applicable_kinds = field +dotnet_naming_symbols.private_constant_fields.applicable_accessibilities = private, protected, protected_internal, private_protected +dotnet_naming_symbols.private_constant_fields.required_modifiers = const + +dotnet_naming_symbols.local_variables.applicable_kinds = local +dotnet_naming_symbols.local_variables.applicable_accessibilities = local +dotnet_naming_symbols.local_variables.required_modifiers = + +dotnet_naming_symbols.local_constants.applicable_kinds = local +dotnet_naming_symbols.local_constants.applicable_accessibilities = local +dotnet_naming_symbols.local_constants.required_modifiers = const + +dotnet_naming_symbols.parameters.applicable_kinds = parameter +dotnet_naming_symbols.parameters.applicable_accessibilities = * +dotnet_naming_symbols.parameters.required_modifiers = + +dotnet_naming_symbols.public_constant_fields.applicable_kinds = field +dotnet_naming_symbols.public_constant_fields.applicable_accessibilities = public, internal +dotnet_naming_symbols.public_constant_fields.required_modifiers = const + +dotnet_naming_symbols.public_static_readonly_fields.applicable_kinds = field +dotnet_naming_symbols.public_static_readonly_fields.applicable_accessibilities = public, internal +dotnet_naming_symbols.public_static_readonly_fields.required_modifiers = readonly, static + +dotnet_naming_symbols.private_static_readonly_fields.applicable_kinds = field +dotnet_naming_symbols.private_static_readonly_fields.applicable_accessibilities = private, protected, protected_internal, private_protected +dotnet_naming_symbols.private_static_readonly_fields.required_modifiers = readonly, static + +dotnet_naming_symbols.local_functions.applicable_kinds = local_function +dotnet_naming_symbols.local_functions.applicable_accessibilities = * +dotnet_naming_symbols.local_functions.required_modifiers = -# IDE0040: Add accessibility modifiers -dotnet_diagnostic.IDE0040.severity = warning +# Naming styles -# CONSIDER: Are IDE0051 and IDE0052 too noisy to be warnings for IDE editing scenarios? Should they be made build-only warnings? -# IDE0051: Remove unused private member -dotnet_diagnostic.IDE0051.severity = warning +dotnet_naming_style.pascalcase.required_prefix = +dotnet_naming_style.pascalcase.required_suffix = +dotnet_naming_style.pascalcase.word_separator = +dotnet_naming_style.pascalcase.capitalization = pascal_case -# IDE0052: Remove unread private member -dotnet_diagnostic.IDE0052.severity = warning +dotnet_naming_style.ipascalcase.required_prefix = I +dotnet_naming_style.ipascalcase.required_suffix = +dotnet_naming_style.ipascalcase.word_separator = +dotnet_naming_style.ipascalcase.capitalization = pascal_case -# IDE0059: Unnecessary assignment to a value -dotnet_diagnostic.IDE0059.severity = warning +dotnet_naming_style.tpascalcase.required_prefix = T +dotnet_naming_style.tpascalcase.required_suffix = +dotnet_naming_style.tpascalcase.word_separator = +dotnet_naming_style.tpascalcase.capitalization = pascal_case -# IDE0060: Remove unused parameter -dotnet_diagnostic.IDE0060.severity = warning +dotnet_naming_style._camelcase.required_prefix = _ +dotnet_naming_style._camelcase.required_suffix = +dotnet_naming_style._camelcase.word_separator = +dotnet_naming_style._camelcase.capitalization = camel_case -# CA1822: Make member static -dotnet_diagnostic.CA1822.severity = warning +dotnet_naming_style.camelcase.required_prefix = +dotnet_naming_style.camelcase.required_suffix = +dotnet_naming_style.camelcase.word_separator = +dotnet_naming_style.camelcase.capitalization = camel_case -# Prefer "var" everywhere -dotnet_diagnostic.IDE0007.severity = warning -csharp_style_var_for_built_in_types = true:warning -csharp_style_var_when_type_is_apparent = true:warning -csharp_style_var_elsewhere = true:warning +dotnet_naming_style.s_camelcase.required_prefix = _ +dotnet_naming_style.s_camelcase.required_suffix = +dotnet_naming_style.s_camelcase.word_separator = +dotnet_naming_style.s_camelcase.capitalization = camel_case -[src/{VisualStudio}/**/*.{cs,vb}] -# CA1822: Make member static -# Not enforced as a build 'warning' for 'VisualStudio' layer due to large number of false positives from https://github.com/dotnet/roslyn-analyzers/issues/3857 and https://github.com/dotnet/roslyn-analyzers/issues/3858 -# Additionally, there is a risk of accidentally breaking an internal API that partners rely on though IVT. -dotnet_diagnostic.CA1822.severity = suggestion diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index f9788ea..ced73c3 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -4,7 +4,4 @@ # These owners will be the default owners for everything in the repo. Unless a # later match takes precedence, these users will be requested for review when # someone opens a pull request. -* @scottoffen @sdoffen - -# Recommendation: Use groups to define code owners, e.g. -# * @organization/team-name \ No newline at end of file +* @scottoffen @sdoffen \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..6090761 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,104 @@ +name: 🐛 Bug report +description: Something reproducible that is broken and should be fixed. +labels: ["bug", "needs-triage"] +assignees: [] +body: + - type: markdown + attributes: + value: | + Thanks for helping make FluentHttpClient better! + **Usage/how-to questions will be closed.** Please use Discussions or StackOverflow instead. + + - type: checkboxes + id: preflight + attributes: + label: Before you submit + description: Please confirm the following. + options: + - label: I have searched existing Issues and Discussions. + required: true + - label: This is **not** a usage/how-to question. + required: true + - label: I have read the [contribution guidelines](https://github.com/scottoffen/fluenthttpclient/blob/main/CONTRIBUTING.md). + required: true + + - type: input + id: fluenthttpclient-version + attributes: + label: FluentHttpClient Version + placeholder: e.g., 0.4.2 or commit SHA + validations: + required: true + + - type: input + id: dotnet-version + attributes: + label: .NET SDK/Runtime Version + placeholder: e.g., .NET 8.0 or SDK 8.0.400 + validations: + required: true + + - type: input + id: os + attributes: + label: OS and Version + placeholder: e.g., Windows 11 24H2 / Ubuntu 24.04 / macOS 14.6 + validations: + required: true + + - type: textarea + id: other-stack + attributes: + label: Related Tooling / Libraries (optional) + description: If relevant, include DI container, logging framework, IDE, reverse proxy, etc. + placeholder: e.g., Microsoft.Extensions.DependencyInjection 8.0.0, Serilog 3.1, Rider 2024.2 + + - type: textarea + id: repro + attributes: + label: Minimal Reproducible Example + description: Provide the **smallest possible** code/config to reproduce. Link to a repo or include inline code. + render: csharp + placeholder: | + // minimal Program.cs or test demonstrating the issue + validations: + required: true + + - type: textarea + id: steps + attributes: + label: Steps to Reproduce + placeholder: | + 1. ... + 2. ... + 3. ... + validations: + required: true + + - type: textarea + id: expected + attributes: + label: Expected Behavior + placeholder: What should happen? + validations: + required: true + + - type: textarea + id: actual + attributes: + label: Actual Behavior + placeholder: What actually happens? + validations: + required: true + + - type: textarea + id: logs + attributes: + label: Relevant Logs/Stack Traces (optional) + description: Redact secrets. Use backticks for code fences. + render: text + + - type: textarea + id: screenshots + attributes: + label: Screenshots / Recordings (optional) \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..aef0b92 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,14 @@ +blank_issues_enabled: false +contact_links: + - name: 📘 FluentHttpClient Documentation + url: https://scottoffen.github.io/fluenthttpclient + about: Start here. Many questions are answered in the official docs. + - name: 💬 Community Discussions + url: https://github.com/scottoffen/fluenthttpclient/discussions + about: Propose ideas, ask how-to questions, and get community help. + - name: "🙋 StackOverflow #fluenthttpclient" + url: https://stackoverflow.com/questions/tagged/fluenthttpclient?sort=newest + about: Ask/answer usage questions with the fluenthttpclient tag. + - name: 🔒 Report a security vulnerability + url: https://github.com/scottoffen/fluenthttpclient/security/advisories/new + about: Please report vulnerabilities privately rather than as a public issue. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/documentation.yml b/.github/ISSUE_TEMPLATE/documentation.yml new file mode 100644 index 0000000..59bf9c6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation.yml @@ -0,0 +1,34 @@ +name: 📖 Documentation update +description: Fix or improve documentation (READMEs or Docusaurus). +labels: ["documentation", "needs-triage"] +assignees: [] +body: + - type: checkboxes + id: preflight + attributes: + label: Before you submit + options: + - label: I checked the latest published docs and the /docs source. + required: true + - label: I will keep the change clear, concise, and consistent with the existing documentation. + required: true + + - type: textarea + id: location + attributes: + label: Affected pages/sections (optional) + description: Link the page(s) or file paths that need changes. + placeholder: /docs/getting-started.md + + - type: textarea + id: change + attributes: + label: Proposed change + description: Be clear and concise. Include before/after snippets if helpful. + placeholder: | + **Before** + ... + **After** + ... + validations: + required: true \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..a79cd80 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,59 @@ +name: 💡 Feature request +description: Propose a change that improves FluentHttpClient. +labels: ["enhancement", "needs-triage"] +assignees: [] +body: + - type: markdown + attributes: + value: | + Please **start in Discussions first** to validate the idea with the community. + Create a feature request issue only after you've gathered positive feedback. + + - type: checkboxes + id: preflight + attributes: + label: Before you submit + options: + - label: I have searched existing Issues and Discussions. + required: true + - label: I have read the [contribution guidelines](https://github.com/scottoffen/fluenthttpclient/blob/main/CONTRIBUTING.md). + required: true + + - type: input + id: discussion-link + attributes: + label: Link to accepted Discussion + description: Paste the URL to the Discussion where consensus was reached. + placeholder: https://github.com/scottoffen/fluenthttpclient/discussions/123 + validations: + required: true + + - type: textarea + id: problem + attributes: + label: Problem Statement + description: Describe the **problem** this solves (not the solution). + placeholder: Users need to... + validations: + required: true + + - type: textarea + id: proposal + attributes: + label: Proposed Solution + description: Outline how the solution would work at a high level. + placeholder: APIs, options, defaults, examples + validations: + required: true + + - type: textarea + id: alternatives + attributes: + label: Alternatives Considered (optional) + placeholder: Why not X? What tradeoffs did you consider? + + - type: textarea + id: breaking + attributes: + label: Breaking Changes / Migration (optional) + description: Describe any breaking changes and proposed migration path. \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..43b86d1 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,21 @@ +## Summary + + +## Related issue + + +## Type +- [ ] Bug fix +- [ ] Feature +- [ ] Docs +- [ ] Other: ___ + +## Checklist +- [ ] Descriptive title and clear description +- [ ] Tests added/updated (xUnit + Shouldly + Moq if needed) +- [ ] Docs updated (if user-facing behavior changed) +- [ ] No cosmetic/whitespace-only changes +- [ ] Follows the [contribution guidelines](https://github.com/scottoffen/fluenthttpclient/blob/main/CONTRIBUTING.md) and C# conventions, with readability as a priority + +## Notes + \ No newline at end of file diff --git a/.github/workflows/cleanup.yml b/.github/workflows/cleanup.yml new file mode 100644 index 0000000..1809649 --- /dev/null +++ b/.github/workflows/cleanup.yml @@ -0,0 +1,44 @@ +# Cleanup prereleases from GitHub Packages. Delegates to the shared +# reusable workflow. +# +# Manual-only. Defaults to a dry run; the first run with any new mode or +# day count should always be a dry run so I can verify the list of +# versions about to be deleted before deletion actually happens. +name: Cleanup Prerelease Packages + +on: + workflow_dispatch: + inputs: + mode: + description: 'Which versions to consider for deletion' + required: true + type: choice + default: prerelease-only + options: + - prerelease-only + - older-than-days + days: + description: 'For older-than-days mode: delete prereleases older than this many days' + required: false + type: number + default: 30 + dry_run: + description: 'If true, lists what would be deleted without actually deleting' + required: false + type: boolean + default: true + +concurrency: + group: cleanup-packages + cancel-in-progress: false + +jobs: + cleanup: + uses: scottoffen/workflows/.github/workflows/dotnet-cleanup-packages.yml@v0 + with: + mode: ${{ github.event.inputs.mode }} + days: ${{ fromJSON(github.event.inputs.days) }} + dry_run: ${{ github.event.inputs.dry_run == 'true' }} + package-names: | + FluentHttpClient + secrets: inherit \ No newline at end of file diff --git a/.github/workflows/deploy-docs.yaml b/.github/workflows/deploy-docs.yaml deleted file mode 100644 index 0823918..0000000 --- a/.github/workflows/deploy-docs.yaml +++ /dev/null @@ -1,57 +0,0 @@ -name: Deploy Documentation - -on: - workflow_dispatch: - push: - branches: - - main - paths: - - 'docs/**' - - '.github/workflows/deploy-docs.yaml' - -permissions: - contents: read - pages: write - id-token: write - -concurrency: - group: 'pages' - cancel-in-progress: true - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: 20 - - - name: Install dependencies - working-directory: ./docs - run: npm ci - - - name: Build site - working-directory: ./docs - run: npm run build - - - name: Upload Pages artifact - uses: actions/upload-pages-artifact@v3 - with: - path: ./docs/build - - deploy: - needs: build - runs-on: ubuntu-latest - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - - steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml new file mode 100644 index 0000000..3dfe531 --- /dev/null +++ b/.github/workflows/deploy-docs.yml @@ -0,0 +1,34 @@ +# Build and deploy the Docusaurus documentation site to GitHub Pages. +# Delegates to the shared reusable workflow. +# +# One-time setup in this repo: Settings -> Pages -> Source -> "GitHub Actions". +# Without that, the deploy step fails. +name: Deploy Documentation + +on: + workflow_dispatch: + push: + branches: + - main + # Only rebuild when docs content or this workflow itself changes. + # Source-code commits that don't touch docs won't trigger a deploy. + paths: + - 'docs/**' + - '.github/workflows/deploy-docs.yml' + +jobs: + deploy: + uses: scottoffen/workflows/.github/workflows/docusaurus-deploy-pages.yml@v0 + permissions: + contents: read + pages: write + id-token: write + # All defaults match the original workflow (docs path, Node 20, npm ci, + # npm run build), so most projects need no 'with:' block at all. Add + # inputs only if this repo deviates from the defaults, e.g.: + # + # with: + # docs-path: website + # node-version: '22' + # install-command: pnpm install --frozen-lockfile + # build-command: pnpm build \ No newline at end of file diff --git a/.github/workflows/main-pr-build-test.yaml b/.github/workflows/main-pr-build-test.yaml deleted file mode 100644 index 6289615..0000000 --- a/.github/workflows/main-pr-build-test.yaml +++ /dev/null @@ -1,143 +0,0 @@ -# GitHub Workflow to build and test the code on a pull request -name: PR Build and Test - -# The workflow_dispatch event allows you to run the workflow manually -# The pull_request event triggers the workflow on pull requests to the main branch -on: - workflow_dispatch: - pull_request: - branches: - - main - -jobs: - - # This job will determine if actual code changes have been made - check: - name: Check for Source Code Changes - runs-on: ubuntu-latest - - outputs: - has_changes: ${{ steps.changed_files.outputs.any_changed }} - - # The steps below will only run once - steps: - - # Checks out the code from the repository using a deep clone - # The deep clone is necessary to access the full history of the repository - # so that the NerdBank.GitVersioning tool can determine the version number - - name: Checkout code (Deep Clone) - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - # Checks for changes in the ./src/FluentHttpClient directory that are not markdown files - # this step outputs a boolean value named any_changed that can be used in - # conditional steps. - - name: Check for source code changes - id: changed_files - uses: tj-actions/changed-files@v45 - with: - files: 'src/FluentHttpClient/**' - files_ignore: '**/*.md' - - # This job will build and run the tests for the project - build: - name: Build and Test - runs-on: ubuntu-latest - needs: check - - # The matrix strategy allows you to run the same steps on multiple operating systems - # The library should be compatible with all the operating systems in the matrix - strategy: - matrix: - os: [ubuntu-latest, windows-latest, macos-latest] - - # The steps below will run in order for each of strategies defined in the matrix - steps: - - # Installs the most recent versions of the .NET SDKs - - name: Setup .NET SDK - if: ${{ needs.check.outputs.has_changes == 'true' }} - uses: actions/setup-dotnet@v4 - with: - dotnet-version: 8.0.x - - # Displays the available .NET SDKs for verification - - name: Display Available .NET SDKs - run: dotnet --list-sdks - - # Checks out the code from the repository using a deep clone - # The deep clone is necessary to access the full history of the repository - # so that the NerdBank.GitVersioning tool can determine the version number - - name: Checkout code (Deep Clone) - if: ${{ needs.check.outputs.has_changes == 'true' }} - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - # Restores the dependencies for the project - - name: Restore dependencies - if: ${{ needs.check.outputs.has_changes == 'true' }} - run: dotnet restore ./src/FluentHttpClient.Tests - - # Builds the project without restoring the dependencies - - name: Build - if: ${{ needs.check.outputs.has_changes == 'true' }} - run: dotnet build ./src/FluentHttpClient.Tests --no-restore - - # Runs all the tests in the project except the integration tests - # without rebuilding the project - - name: Test - if: ${{ needs.check.outputs.has_changes == 'true' }} - run: dotnet test ./src/FluentHttpClient.Tests --no-build --verbosity normal --filter "Category!=Integration" - - # This job will publish the package to GitHub Packages - # While this job is dependent on the build job, do not combine the jobs as - # the build job will run multiple times - publish: - name: Publish PreRelease Package - runs-on: ubuntu-latest - needs: - - build - - check - if: ${{ needs.check.outputs.has_changes == 'true' }} - - # These permissions are required to publish the package to GitHub Packages - permissions: - contents: write - packages: write - - # The steps below will only run once - steps: - - # Installs the most recent versions of the .NET SDKs - - name: Setup .NET SDK - uses: actions/setup-dotnet@v4 - with: - dotnet-version: 8.0.x - - # Displays the available .NET SDKs for verification - - name: Display Available .NET SDKs - run: dotnet --list-sdks - - # Checks out the code from the repository using a deep clone - # The deep clone is necessary to access the full history of the repository - # so that the NerdBank.GitVersioning tool can determine the version number - - name: Checkout code (Deep Clone) - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - # Restores the dependencies for the project - - name: Restore dependencies - run: dotnet restore ./src/FluentHttpClient - - # Builds the project using the Release configuration without restoring dependencies - - name: Build - run: dotnet build ./src/FluentHttpClient --no-restore --configuration Release -p:PackageOutputPath=$PWD/publish - - # Publishes the package to GitHub Packages - - name: Publish to GitHub Packages - run: | - dotnet nuget push ./publish/*.nupkg --source https://nuget.pkg.github.com/scottoffen/index.json --api-key ${{ secrets.GITHUB_TOKEN }} --skip-duplicate - dotnet nuget push ./publish/*.snupkg --source https://nuget.pkg.github.com/scottoffen/index.json --api-key ${{ secrets.GITHUB_TOKEN }} --skip-duplicate diff --git a/.github/workflows/main-publish-release.yaml b/.github/workflows/main-publish-release.yaml deleted file mode 100644 index 20b58ef..0000000 --- a/.github/workflows/main-publish-release.yaml +++ /dev/null @@ -1,160 +0,0 @@ -# This workflow will automatically build and test the project when code is merged -# into the main branch, then publish a release of the library to GitHub Packages. -# It can also be manually triggered to optionally publish to Nuget.org. -name: Publish Release - -# The workflow_dispatch event allows you to run the workflow manually -# The push event triggers the workflow on pushes to the main branch -on: - workflow_dispatch: - inputs: - pushNuget: - description: 'Push to Nuget' - required: false - default: false - type: boolean - pushGitHub: - description: 'Push to GitHub' - required: false - default: true - type: boolean - push: - branches: - - main - paths: - - 'src/**' - - '!src/**/*.md' - -jobs: - # This job will build and run the tests for the project - build: - name: Build and Test - runs-on: ubuntu-latest - - # The matrix strategy allows you to run the same steps on multiple operating systems - # The library should be compatible with all the operating systems in the matrix - strategy: - matrix: - os: [ubuntu-latest, windows-latest, macos-latest] - - # The steps below will run in order for each of strategies defined in the matrix - steps: - - # Installs the most recent versions of the .NET SDKs - - name: Setup .NET SDK - uses: actions/setup-dotnet@v4 - with: - dotnet-version: 8.0.x - - # Displays the available .NET SDKs for verification - - name: Display Available .NET SDKs - run: dotnet --list-sdks - - # Checks out the code from the repository using a deep clone - # The deep clone is necessary to access the full history of the repository - # so that the NerdBank.GitVersioning tool can determine the version number - - name: Checkout Code (Deep Clone) - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - # Restores the dependencies for the project - - name: Restore Dependencies - run: dotnet restore ./src/FluentHttpClient - - # Builds the project without restoring the dependencies - - name: Build Project - run: dotnet build ./src/FluentHttpClient --no-restore - - # Runs all the tests in the project except the integration tests - # without rebuilding the project - - name: Execute Test - run: dotnet test ./src/FluentHttpClient --no-build --verbosity normal --filter "Category!=Integration" - - # This job will publish the package to GitHub Packages - # While this job is dependent on the build job, do not combine the jobs as - # the build job will run multiple times - publish: - name: Publish Package - runs-on: ubuntu-latest - needs: build - - # These permissions are required to publish the package to GitHub Packages - permissions: - contents: write - packages: write - - # Get default values for the workflow_dispatch event inputs - env: - PUSH_GITHUB: ${{ github.event.inputs.pushGitHub || 'true' }} - PUSH_NUGET: ${{ github.event.inputs.pushNuget || 'false' }} - - # The steps below will only run once - steps: - - # Installs the most recent versions of the .NET SDKs - - name: Setup .NET SDK - uses: actions/setup-dotnet@v4 - with: - dotnet-version: 8.0.x - - # Displays the available .NET SDKs for verification - - name: Display Available .NET SDKs - run: dotnet --list-sdks - - # Checks out the code from the repository using a deep clone - # The deep clone is necessary to access the full history of the repository - # so that the NerdBank.GitVersioning tool can determine the version number - - name: Checkout Code (Deep Clone) - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - # Restores the dependencies for the project - - name: Restore Dependencies - run: dotnet restore ./src/FluentHttpClient - - # Builds the project using the Release configuration without restoring dependencies - - name: Build Release - run: dotnet build ./src/FluentHttpClient --no-restore --configuration Release -p:PackageOutputPath=$PWD/publish - - # Output the github.event.inputs values - - name: Display Inputs - run: | - echo "pushGitHub: $PUSH_GITHUB" - echo "pushNuget: $PUSH_NUGET" - - # Publishes the package to GitHub Packages - # This step is run by default, but should be disabled when the workflow is triggered manually - - name: Publish to GitHub Packages - if: env.PUSH_GITHUB == 'true' - run: | - dotnet nuget push ./publish/*.nupkg --source https://nuget.pkg.github.com/scottoffen/index.json --api-key ${{ secrets.GITHUB_TOKEN }} - dotnet nuget push ./publish/*.snupkg --source https://nuget.pkg.github.com/scottoffen/index.json --api-key ${{ secrets.GITHUB_TOKEN }} - - # Publishes the package to Nuget.org - # This step is optional and can ONLY be triggered manually - # The NUGET_API_KEY secret must be set in the repository settings - - name: Publish to Nuget.org - if: env.PUSH_NUGET == 'true' - run: | - dotnet nuget push ./publish/*.nupkg --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_API_KEY }} - - # Outputs the version number for the release - # This step is run only if the PUSH_GITHUB input is true - # See https://www.jameskerr.blog/posts/how-to-set-output-in-github-actions/ - - name: Get Version Info - if: env.PUSH_GITHUB == 'true' - id: get_version - run: | - cd src/FluentHttpClient - echo "version='$(nbgv get-version -v NugetPackageVersion)'" >> "$GITHUB_OUTPUT" - cd ../.. - - # Create a release tag based on the version number - # This step is run only if the PUSH_GITHUB input is true - - name: Create Release - if: env.PUSH_GITHUB == 'true' - run: gh release create ${{ steps.get_version.outputs.version }} --title ${{ steps.get_version.outputs.version }} --notes "Release ${{ steps.get_version.outputs.version }}" --target main - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/pr-build.yml b/.github/workflows/pr-build.yml new file mode 100644 index 0000000..a8b86f8 --- /dev/null +++ b/.github/workflows/pr-build.yml @@ -0,0 +1,41 @@ +# PR build for a .NET solution. Delegates to the shared reusable workflow. +# +# The trigger has no path filter on purpose: branch protection treats a +# missing required check as "pending" indefinitely, which would block doc- +# only PRs forever. Instead, the reusable workflow's preflight gate decides +# whether the build actually runs based on the files that changed. A +# skipped job is reported as success to branch protection. +name: PR Build + +on: + pull_request: + branches: [main] + workflow_dispatch: + +# Cancel any in-progress run for the same PR when a new commit lands. +# The latest commit's result is the one that matters. +concurrency: + group: pr-build-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + build: + # IMPORTANT: this job name ('build') is what branch protection sees as + # the required status check. Renaming it here means updating branch + # protection on the consuming repo to match. + uses: scottoffen/workflows/.github/workflows/dotnet-build-and-publish.yml@v0 + permissions: + contents: write + packages: write + with: + solution-path: src/FluentHttpClient.sln + dotnet-version: 10.0.x + check-paths: true + include-paths: | + src/** + ignore-paths: | + **/*.md + src/**Tests/** + push-github: true + push-nuget: false + secrets: inherit \ No newline at end of file diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..71f23e1 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,54 @@ +# Publish for a .NET solution. Delegates to the shared reusable workflow. +# +# Two paths: +# 1. Push to main: re-publishes to GitHub Packages so the branch package +# stays current. NuGet.org publishing is never automatic. +# 2. Manual workflow_dispatch: operator picks GitHub Packages, NuGet.org, +# or both. Defaults match the automatic push (GitHub yes, NuGet no), so +# a manual run with no input changes mirrors a re-run of the automatic +# publish. +# +# Path filters here intentionally mirror the PR build's ignore list so doc +# commits to main do not republish. The preflight is NOT used on this side +# because there is no "required check" concern to satisfy. +name: Publish + +on: + push: + branches: [main] + paths: + - 'src/**' + - '!**/*.md' + - '!src/**Tests/**' + workflow_dispatch: + inputs: + pushGitHub: + description: 'Push to GitHub Packages' + required: false + default: true + type: boolean + pushNuget: + description: 'Push to NuGet.org' + required: false + default: false + type: boolean + +# Do not cancel in-progress publishes; cancelling mid-push could leave the +# registry in an inconsistent state. +concurrency: + group: publish-${{ github.ref }} + cancel-in-progress: false + +jobs: + publish: + uses: scottoffen/workflows/.github/workflows/dotnet-build-and-publish.yml@v0 + permissions: + contents: write + packages: write + with: + solution-path: src/FluentHttpClient.sln + dotnet-version: 10.0.x + check-paths: false + push-github: ${{ github.event.inputs.pushGitHub != 'false' }} + push-nuget: ${{ github.event.inputs.pushNuget == 'true' }} + secrets: inherit \ No newline at end of file diff --git a/.gitignore b/.gitignore index c5c0a88..d2444af 100644 --- a/.gitignore +++ b/.gitignore @@ -43,10 +43,6 @@ bld/ # Visual Studio 2017 auto generated files Generated\ Files/ -# EF Core Power Tools -# https://marketplace.visualstudio.com/items?itemName=ErikEJ.EFCorePowerTools -*.dgml - # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* @@ -406,7 +402,7 @@ FodyWeavers.xsd # JetBrains Rider *.sln.iml -.idea +.idea/ ## ## Visual studio for Mac @@ -430,7 +426,7 @@ test-results/ *.dmg *.app -# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore +# content below from: https://github.com/github/gitignore/blob/main/Global/macOS.gitignore # General .DS_Store .AppleDouble @@ -459,7 +455,7 @@ Network Trash Folder Temporary Items .apdisk -# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore +# content below from: https://github.com/github/gitignore/blob/main/Global/Windows.gitignore # Windows thumbnail cache files Thumbs.db ehthumbs.db diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fc7802d..c52201c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,75 +1,85 @@ # Contributing to FluentHttpClient -Community feedback is used to inform the direction of the project, and we absolutely want to hear from you! **Your involvement makes the project better** by increasing the usability, quality and adoption of the project. Our goal is to be better stewards by being more responsive and transparent. Following the contribution guidelines outlined below will help us to better help you. +We absolutely want to hear from you! **Your involvement makes FluentHttpClient better** by improving usability, quality, and adoption. Our goal is to be responsive and transparent stewards of the project. Following these guidelines will help us help you. -# How To Get Help +## How to Get Help -Issues are often created that are not bugs or feature requests, but rather questions or discussions - most of which could be answered by anyone in the community; they're not exclusive to the maintainers. For feedback like this, there are several places where people can get all kinds of help, and we would encourage you to use them first. +Many issues created on open source projects turn out to be usage questions or general discussions rather than bugs or feature requests. These are valuable, but they don't need to be tracked as GitHub issues. Anyone in the community can help answer them. -- Consult the [official documentation](https://scottoffen.github.io/fluenthttpclient). -- Engage in our [community discussions](https://github.com/scottoffen/fluenthttpclient/discussions). +For usage questions and discussions, please use these channels first: -To avoid any misunderstanding, let us state this policy in no uncertain terms: **issues that are created to ask usage questions will be closed.** +* [Official documentation](https://scottoffen.github.io/fluenthttpclient) +* [GitHub community discussions](https://github.com/scottoffen/fluenthttpclient/discussions) +* [StackOverflow (tagged #fluenthttpclient)](https://stackoverflow.com/questions/tagged/fluenthttpclient?sort=newest) -# Getting Off To A Good Start +To avoid confusion: **issues created solely to ask usage questions will be closed.** -Regardless of whether you are opening a bug report, asking a question on StackOverflow, or getting in on the GitHub discussions, there is information you should always include to ensure you get the results you need. If you don't provide it up front, you will likely be asked for it before you can get any traction on your request. +## Getting Off to a Good Start -- Have a descriptive title and a clear description. -- Include the details of the operating system and version, version of .NET and the version of FluentHttpClient you are using. -- If it's an interoperability problem, don't forget to include information about the other "things" you are using, e.g. logging frameworks, dependency injection libraries, IDE, etc. -- Show the minimum amount of code needed to illustrate the problem or demonstrate the behavior. -- Where applicable, consider including a screenshot or a link to your repository where the code can be examined in context. +Whether reporting a bug, posting on StackOverflow, or joining discussions, always include enough context to get meaningful help: -# Feature Requests +* Use a descriptive title and clear description. +* Provide details: OS + version, .NET version, FluentHttpClient version. +* For interoperability issues, include details about other frameworks/tools in use (logging, DI libraries, IDE, etc.). +* Share the minimum code needed to reproduce the behavior. +* Add screenshots or repo links where useful. -Got an idea on how to make FluentHttpClient better? Start or join a conversation in our [community discussions](https://github.com/scottoffen/fluenthttpclient/discussions) and suggest your change there. **Do not open an issue on GitHub until** you have collected positive feedback about the change. Where it comes to improvements, we want to ensure we are focused on solving problems, not attacking symptoms, while remaining focused on our guiding principles: fast, unopinionated, and minimalist. In a word: simple. +## Feature Requests -If it is decided that your idea would be a good inclusion to the core FluentHttpClient project, create a feature request issue based on the outcome of the community discussion. +Got an idea? Start by opening a [discussion](https://github.com/scottoffen/fluenthttpclient/discussions) to gather feedback. +**Do not open a GitHub issue until there's positive consensus.** -# Issue Management +When proposing features, we want to ensure we are **focused on solving problems, not attacking symptoms**, while staying true to FluentHttpClient's guiding principles: **fast, unopinionated, minimalist**. In a word: **simple.** -GitHub issues are reserved for things that can be fixed, added, resolved, or implemented. Here are some guidelines we use in order to manage incoming issues in the most efficient way. +If consensus supports your idea, you may open a feature request issue that reflects the discussion outcome. -## Issues That Can't or Won't Be Fixed +## Issue Management -There are classes of things that get reported that are undefined, indistinct, or out of scope, and as such they are inactionable. When a report comes in that looks like this, we'll ask the original submitter of the issue to clarify what "done" would look like to them. We can and will help with this process if you're unsure. But if the goal remains undefined or is unachievable (e.g. outside the scope or vision of the project), we'll close the issue. +Issues are for things that can be **fixed, added, resolved, or implemented.** -## Abandoned Issues +### Issues That Can't or Won't Be Fixed -Occasionally, the maintainers can't get the information they need to resolve something, and as a result an issue just never moves forward. We get it, we're all busy. Maybe the original submitter has moved on or just forgotten. In order to focus our attention in the right places, we will mark issues with the `more-information-needed` label when the maintainers have a question. If we don't receive a response from the original submitter within a week, we'll give a gentle reminder. If we still haven't received a response within 30 days, we will close the issue. +Some reports are too vague, undefined, or out of scope. In those cases, we'll ask you to clarify what “done” looks like. If the goal remains undefined or unachievable (e.g., outside FluentHttpClient's vision), the issue will be closed. -# Coding Conventions +### Abandoned Issues -FluentHttpClient is written strictly in C#. The repository includes an [`.editorconfig`](https://editorconfig.org/) file to manage indentation styles as well as line endings, etc. +If maintainers request more information, the issue will be labeled `more-information-needed`. -- As a guiding principle, we aim for [readable code](https://www.amazon.com/Art-Readable-Code-Practical-Techniques/dp/0596802293) above following a convention. -- For C# we ask that you follow the [C# Coding Conventions](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/inside-a-program/coding-conventions) published by Microsoft. - - We may, at our discretion, override these conventions where we feel it conflicts with readable code. -- Added or updated unit tests are required for any modified or added code. - - We use [Xunit](https://www.nuget.org/packages/xunit), [Shouldly](https://www.nuget.org/packages/Shouldly) and [Moq](https://www.nuget.org/packages/Moq). - - For the sake of maintainability, your contributions will be rejected if you do not use them as well. +* If no response is received within **1 week**, we'll send a reminder. +* If no response after **30 days**, the issue will be closed. -# Pull Requests +## Coding Conventions -When writing a pull request +FluentHttpClient is written in C#. The repository includes a comprehensive [`.editorconfig`](./.editorconfig) file that defines code style, formatting, and naming conventions across the project, as well as consistent rules for indentation, naming, style preferences, and analyzer severities. -- Have a descriptive title -- Include a clear list of what you've done. -- Make sure to include or update test coverage. -- Make sure all of your commits are atomic (one feature per commit). +* Prefer **[readable code](https://www.amazon.com/Art-Readable-Code-Practical-Techniques/dp/0596802293) > rigid convention.** +* Follow [Microsoft's C# Coding Conventions](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/inside-a-program/coding-conventions). + * We may override conventions if it improves readability. +* All changes must include or update **unit tests**. -## Purely Cosmetic Pull Requests + * We use [xUnit](https://www.nuget.org/packages/xunit), [Shouldly](https://www.nuget.org/packages/Shouldly), and [Moq](https://www.nuget.org/packages/Moq). + * Contributions that don't follow this testing stack will be rejected. -Changes that are cosmetic in nature and do not add anything substantial to the stability, functionality, or testability of the project will generally not be accepted. There are a lot of hidden costs in these kinds of pull requests. These include but are not limited to: +## Pull Requests -- Someone needs to review those changes -- It creates a lot of notification noise -- It pollutes the git history +When submitting a PR: -> [!IMPORTANT] -> Sometimes, your editor may completely reformat a file when you save it, making numerous white space changes that, while they don't affect the code, create a lot of noise for the reviewers. If this happens, the PR will not be considered until those white space changes are reverted. +* Use a descriptive title. +* Clearly list what you've done. +* Reference the issue it resolves with `Closes #123`. +* Include or update tests. +* Keep commits atomic (one feature/fix per commit). -# Documentation Changes +### Purely Cosmetic PRs -Having excellent documentation is crucial to the success of FluentHttpClient. Documentation for FluentHttpClient is written using [Docusaurus](https://docusaurus.io/), and lives in [docs folder](./docs/). Your contribution of clear, concise and accurate documentation is appreciated. \ No newline at end of file +Cosmetic-only changes (whitespace, reformatting, etc.) generally will not be accepted due to the hidden costs: + +* They require review. +* They create noise in notifications. +* They pollute git history. + +If your editor reformats a file (e.g., whitespace-only changes), please revert those before submitting. + +## Documentation Changes + +Good documentation is critical to FluentHttpClient's success. Docs are written in [Docusaurus](https://docusaurus.io/) and live in the [docs folder](./docs/). Contributions of clear, concise, and accurate documentation are highly valued. \ No newline at end of file diff --git a/LICENSE b/LICENSE index 563135c..1c7b400 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,7 @@ -MIT License +Copyright 2021 - 2026 Scott Offen -Copyright (c) 2022 Scott Offen +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index 8f38f8b..2ee2c6f 100644 --- a/README.md +++ b/README.md @@ -3,71 +3,36 @@ [![docs](https://img.shields.io/badge/docs-github.io-blue)](https://scottoffen.github.io/fluenthttpclient) [![NuGet](https://img.shields.io/nuget/v/fluenthttpclient)](https://www.nuget.org/packages/FluentHttpClient/) [![MIT](https://img.shields.io/github/license/scottoffen/fluenthttpclient?color=blue)](./LICENSE) -[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-blue.svg)](code_of_conduct.md) +[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-blue.svg)](CODE_OF_CONDUCT.md) [![FluentHttpClient](https://img.shields.io/badge/FluentHttpClient-strong%20named-ff8038.svg)](https://learn.microsoft.com/dotnet/standard/assembly/strong-named) -[![Multi-targeted](https://img.shields.io/badge/TFMs-multi--targeted-652f94)](#compatibility-matrix) +[![Multi-targeted](https://img.shields.io/badge/TFMs-multi--targeted-652f94)](#target-frameworks) -FluentHttpClient brings a modern, chainable API to [`HttpClient`](https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclient), turning verbose request setup into clean, expressive fluency. It handles headers, options, cookies, query parameters, conditional configurators, buffering, and *both* JSON/XML serialization and deserialization, along with success and failure handlers, all with minimal ceremony. It multitargets from **.NET Standard 2.0** all the way up through **.NET 10**, giving you broad compatibility across older runtimes and the latest platforms, with full Native AOT compatibility and strong-named assemblies. +FluentHttpClient adds a chainable API on top of [`HttpClient`](https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclient). You configure a request, send it, and read the response in one expression, instead of building an `HttpRequestMessage`, checking the status code, and deserializing by hand every time. -## Compatibility Matrix +It works with the `HttpClient` you already have rather than replacing it. Each request is built on its own without changing the client or its shared configuration, so your existing setup, including `IHttpClientFactory` and typed clients, is unaffected. -FluentHttpClient is optimized for .NET 10 and the newest .NET releases, while also supporting older platforms through .NET Standard 2.1 and 2.0 for teams maintaining long-lived or legacy applications. It includes full Native AOT compatibility and provides strong-named assemblies for environments that require them. +## What You Get -| Target | Supported | Notes | -| ------------------------- | --------- | ----------------------------- | -| **.NET Standard 2.0** | ✔️ | Broadest compatibility target | -| **.NET Standard 2.1** | ✔️ | Improved modern API surface | -| **.NET Framework 4.6.1+** | ✔️ | Via `netstandard2.0` | -| **.NET 6** | ✔️ | LTS | -| **.NET 7** | ✔️ | | -| **.NET 8** | ✔️ | LTS | -| **.NET 9** | ✔️ | | -| **.NET 10** | ✔️ | LTS | +- **Fluent configuration** of headers, query parameters, cookies, authentication, options, content, and content buffering, all in one readable chain. +- **JSON and XML** serialization and deserialization, with `JsonTypeInfo` overloads for trim-safe and Native AOT scenarios. +- **Conditional configuration** that applies immediately or defers until the request is built, so you can branch without breaking the chain. +- **Response handlers** that attach success and failure callbacks inline, without interrupting the chain. +- **Extensible by subclassing**: derive from `HttpRequestBuilder` to create a custom builder shaped for a specific API or concern. Your methods chain alongside the built-in ones, and an override of `SendAsync` applies your logic to every request, since every other member on the class feeds into it. -### .NETStandard Consumers +## Side-by-Side -Projects targeting **.NETStandard 2.0** or **.NETStandard 2.1** do not include `System.Text.Json` in the framework. FluentHttpClient uses `System.Text.Json` internally for its JSON extensions, but the package is not referenced transitively. +The same request, written with raw `HttpClient` and with FluentHttpClient. Both deserialize the response into the same model: -If you are building against **netstandard2.0** or **netstandard2.1**, or any TFM that does **not** ship `System.Text.Json`, you will need to add an explicit package reference, with a minimum version of 4.6.0 or 6.0.10, respectively. A higher version is always recommended. - -Apps targeting modern TFMs (such as .NET 5 and later) already include `System.Text.Json` and do not require this step. - -## Installation - -FluentHttpClient is available on [NuGet.org](https://www.nuget.org/packages/FluentHttpClient/) and can be installed using a NuGet package manager or the .NET CLI. - -## When to Use FluentHttpClient - -While `HttpClient` is a powerful and flexible tool, building HTTP requests with it often involves repetitive boilerplate, manual serialization, and scattered configuration logic. FluentHttpClient addresses these pain points by providing a fluent, chainable API that reduces cognitive load and improves code readability. - -### Common HttpClient Challenges - -**Repetitive Configuration** -Every request requires manually setting headers, query parameters, and content, often scattered across multiple lines. This makes it easy to miss required headers or forget encoding rules. - -**Manual Serialization** -Converting objects to JSON, setting the correct `Content-Type`, and deserializing responses requires multiple steps and imports. Error-prone encoding and parsing logic often needs to be duplicated across your codebase. - -**Inconsistent Error Handling** -Without a unified approach to handling success and failure responses, status code checks and logging logic tend to be duplicated or omitted entirely. - -**Lifetime and Reuse Concerns** -Properly managing `HttpClient` lifetime, avoiding socket exhaustion, and reusing instances while still configuring per-request state requires careful planning and often leads to awkward patterns. - -### How FluentHttpClient Helps - -FluentHttpClient wraps `HttpClient` (you still manage the lifetime) and provides extension methods that let you configure requests in a single, readable chain: - -- **Fluent Configuration**: Add headers, query parameters, cookies, and authentication in a natural, discoverable flow -- **Automatic Serialization**: Built-in JSON and XML serialization/deserialization with support for `System.Text.Json`, Native AOT, and custom options -- **Response Handlers**: Attach success and failure callbacks directly in the request chain without breaking fluency -- **Reduced Boilerplate**: Express the entire request lifecycle—configuration, sending, and deserialization—in a single expression - -### Side-by-Side Comparison - -Here's the same request implemented with raw `HttpClient` and FluentHttpClient: +```csharp +public class Post +{ + public int Id { get; set; } + public string? Title { get; set; } + public string? Body { get; set; } +} +``` -#### Raw HttpClient +### Raw HttpClient ```csharp using System.Net.Http.Json; @@ -92,16 +57,9 @@ else { Console.WriteLine($"Failed: {response.StatusCode}"); } - -public class Post -{ - public int Id { get; set; } - public string? Title { get; set; } - public string? Body { get; set; } -} ``` -#### FluentHttpClient +### FluentHttpClient ```csharp using FluentHttpClient; @@ -118,20 +76,23 @@ var post = await client .OnSuccess(r => Console.WriteLine($"Success: {r.StatusCode}")) .OnFailure(r => Console.WriteLine($"Failed: {r.StatusCode}")) .ReadJsonAsync(); - -public class Post -{ - public int Id { get; set; } - public string? Title { get; set; } - public string? Body { get; set; } -} ``` -Because a fluent API improves developer experience by turning tedious, repetitive setup into a readable, chainable flow that matches how you actually think about building and sending an HTTP request, The FluentHttpClient version expresses the same logic in fewer lines, with better readability and no loss of functionality. All configuration, sending, error handling, and deserialization happen in a single fluent chain. +The FluentHttpClient version expresses the same logic in fewer lines, and keeps configuration, sending, error handling, and deserialization together in one chain. + +## Target Frameworks + +FluentHttpClient multitargets .NET Standard 2.0 and 2.1, and .NET 6, 7, 8, 9, and 10. Through .NET Standard 2.0 it also runs on .NET Framework 4.6.1 and later. The assemblies are strong-named, and the package is Native AOT compatible when you use the `JsonTypeInfo` JSON overloads. + +### .NET Standard Consumers + +.NET Standard 2.0 and 2.1 do not ship `System.Text.Json`, and FluentHttpClient does not bring it in transitively. If you target either one, or any other framework that does not include `System.Text.Json`, add an explicit package reference: at least `4.6.0` for `netstandard2.0` or `6.0.10` for `netstandard2.1`. A newer version is always preferable. Apps on .NET 5 and later already include it and need no extra step. + +## Documentation -## Usage and Support +Full documentation, including how to create custom builders, is at https://scottoffen.github.io/fluenthttpclient. -- Check out the project documentation https://scottoffen.github.io/fluenthttpclient. +## Community and Support - Engage in our [community discussions](https://github.com/scottoffen/fluenthttpclient/discussions) for Q&A, ideas, and show and tell! @@ -162,4 +123,4 @@ FluentHttpClient is licensed under the [MIT](./LICENSE) license. ## Using FluentHttpClient? We'd Love To Hear About It! -Few thing are as satisfying as hearing that your open source project is being used and appreciated by others. Jump over to the discussion boards and [share the love](https://github.com/scottoffen/fluenthttpclient/discussions)! +Few things are as satisfying as hearing that your open source project is being used and appreciated by others. Jump over to the discussion boards and [share the love](https://github.com/scottoffen/fluenthttpclient/discussions)! \ No newline at end of file diff --git a/docs/docs/configure-content.md b/docs/docs/configure-content.md index 35fa2b3..cf7c8bd 100644 --- a/docs/docs/configure-content.md +++ b/docs/docs/configure-content.md @@ -61,7 +61,7 @@ All overloads generate a `StringContent` instance and optionally set the `Conten JSON content can be supplied as raw JSON strings or objects that will be serialized using `System.Text.Json`. -:::warning AOT and JSON +:::warning[AOT and JSON] For Native AOT builds, only the section for raw JSON strings applies. For typed JSON serialization, see the [JSON AOT Support](./aot-support.md) documentation for AOT-friendly overloads. @@ -102,7 +102,7 @@ All JSON extensions serialize the object, create `StringContent`, and apply the These methods attach XML content either from raw XML or using `System.Xml.Serialization` to serialize objects. -:::danger AOT and XML +:::danger[AOT and XML] For Native AOT buids, only the section for raw XML strings applies. FluentHttpClient does not have typed XML serialization overloads that are AOT-friendly. diff --git a/docs/docs/configure-cookies.md b/docs/docs/configure-cookies.md index 399a8ac..0f02027 100644 --- a/docs/docs/configure-cookies.md +++ b/docs/docs/configure-cookies.md @@ -42,7 +42,7 @@ var builder = client .WithCookie("session", preEncodedValue, encode: false); ``` -:::caution When to disable encoding +:::caution[When to disable encoding] Set `encode` to `false` only if: * The value is already properly encoded diff --git a/docs/docs/configure-headers.md b/docs/docs/configure-headers.md index 8743768..88dbc95 100644 --- a/docs/docs/configure-headers.md +++ b/docs/docs/configure-headers.md @@ -203,7 +203,7 @@ This restriction only applies to the `WithHeader` and `Withheaders` fluent exten In short, the fluent API keeps the simple path safe, while still leaving the door open for expert customization or tom-foolery when needed. -:::tip Indirect Control +:::tip[Indirect Control] When you need indirect control over `Content-Length` or chunked transfer behavior, your lever is [`WithBufferedContent`](./configure-content.md#buffering-request-content). Buffered content *usually* produces a `Content-Length` header, while unbuffered or unknown-length content lets the runtime fall back to chunked transfer for HTTP/1.1. Nevertheless, FluentHttpClient itself **never sets these headers explicitly**. @@ -249,7 +249,7 @@ var builder = client * Encodes with UTF-8 and Base64. * Sets `Authorization: Basic {encoded}`. -:::danger Security Notification +:::danger[Security Notification] Basic auth is only Base64 encoding, not encryption. Avoid sending it over insecure connections and avoid logging the header or the raw password. diff --git a/docs/docs/custom-builders.md b/docs/docs/custom-builders.md new file mode 100644 index 0000000..43fec13 --- /dev/null +++ b/docs/docs/custom-builders.md @@ -0,0 +1,104 @@ +--- +sidebar_position: 16 +title: Custom Builders +--- + +A custom builder is a subclass of `HttpRequestBuilder` that you have shaped for a specific job. It is still a builder in every way: it carries the full fluent API, it is scoped to a single request, and you get one from an extension method on `HttpClient`, exactly as you get the base builder. Subclassing just lets you add your own methods and change behavior in one place. + +## Why Subclass + +You keep the full fluent API. Your builder is an `HttpRequestBuilder`, so `WithHeader`, `WithQueryParameter`, `GetAsync`, the response handlers, and the deserialization extensions all work on it with no forwarding code. + +You can add your own methods, and they chain. The fluent extensions return your subtype rather than the base type, so a helper you add stays reachable in the middle of a chain, next to the built-in methods. + +The class has one behavior. Every other member is a property, a behavior flag, or a method that ends up calling `SendAsync`. Override it and your logic runs on every request the builder sends, whether that request comes from `GetAsync`, a string-based overload, or anything else. + +## A Custom Builder + +Here is a small builder for the GitHub API. It adds a fluent helper and overrides `SendAsync` to apply a header to every request. + +```csharp +using FluentHttpClient; + +public class GitHubBuilder : HttpRequestBuilder +{ + public GitHubBuilder(HttpClient client) : base(client) + { + } + + public GitHubBuilder(HttpClient client, string route) : base(client, route) + { + } + + public GitHubBuilder(HttpClient client, Uri uri) : base(client, uri) + { + } + + // Added functionality: a fluent helper that returns the subtype, so it keeps chaining. + public GitHubBuilder WithApiVersion(string version) + { + InternalHeaders["X-GitHub-Api-Version"] = new[] { version }; + return this; + } + + // Overridden behavior: runs on every request this builder sends. + public override Task SendAsync( + HttpMethod method, + HttpCompletionOption completionOption, + CancellationToken cancellationToken) + { + InternalHeaders["Accept"] = new[] { "application/vnd.github+json" }; + return base.SendAsync(method, completionOption, cancellationToken); + } +} +``` + +The constructors forward to the base. The base constructors are `protected internal`, which is what lets you chain to them with `base(...)` from your own assembly. This builder declares all three base signatures, which matters for the typed extensions below. + +## Obtaining an Instance + +You obtain a custom builder the same way you obtain the base builder, from an extension on `HttpClient`. There are two ways to do it. + +### A Typed Extension + +The library ships generic versions of `UsingBase` and `UsingRoute` that take the builder type: + +```csharp +var gh = client.UsingRoute("/users/octocat"); +``` + +These construct the builder for you, by matching a constructor to the arguments through reflection. A subclass you intend to use with them must declare constructors that mirror the base constructors, so if you rely on the generic extensions, declare all three, `(HttpClient)`, `(HttpClient, string)`, and `(HttpClient, Uri)`, as the sample above does. A call whose signature your subclass does not mirror fails at runtime. + +Building through reflection is convenient but not trimming or Native AOT safe, so these overloads carry the matching warnings. Use them when you are not trimming. + +### Your Own Extension + +For a trimming and AOT safe path, and for the cleanest call site, write a small extension that returns your builder: + +```csharp +public static class GitHubBuilderExtensions +{ + public static GitHubBuilder UsingGitHub(this HttpClient client, string route) => new(client, route); +} +``` + +```csharp +var gh = client.UsingGitHub("/users/octocat"); +``` + +This calls the constructor directly, so there is no reflection, no warnings, and it works on every target. It also reads well, since the name can say what the builder is for. + +Either way, your own methods and the built-in ones chain together, and the type is preserved the whole way: + +```csharp +var user = await client + .UsingGitHub("/users/octocat") + .WithApiVersion("2022-11-28") + .WithHeader("X-Trace-Id", traceId) + .GetAsync() + .ReadJsonAsync(); +``` + +## Lifetime + +A custom builder is a per-request object, the same as the base builder. It is the fluent front for a single `HttpRequestMessage`: it accumulates the state of one request and is meant to be used once and let go. Get one from the `HttpClient` for each request, send it, and move on. There is nothing to hold onto or reuse. \ No newline at end of file diff --git a/docs/docs/deserializing-json.md b/docs/docs/deserializing-json.md index 18e1ba1..0ab85af 100644 --- a/docs/docs/deserializing-json.md +++ b/docs/docs/deserializing-json.md @@ -5,7 +5,7 @@ title: Deserializing JSON FluentHttpClient provides a set of extensions for reading and deserializing JSON from `HttpResponseMessage` instances and from tasks that produce them. These extensions support strongly typed models, `JsonDocument`, and - on .NET 6 and later - `JsonObject` from `System.Text.Json.Nodes`. -:::warning AOT and JSON +:::warning[AOT and JSON] For Native AOT builds, only the sections for `JsonDocument` and `JsonObject` apply. For typed JSON deserialization, see the [JSON AOT Support](./aot-support.md) documentation for overloads are AOT-friendly. @@ -56,7 +56,7 @@ using var doc = await response.ReadJsonDocumentAsync(); - `ReadJsonDocumentAsync(CancellationToken)` - `ReadJsonDocumentAsync(JsonDocumentOptions, CancellationToken)` -:::danger Memory Management +:::danger[Memory Management] `JsonDocument` implements `IDisposable` and **must be disposed** to avoid memory leaks. The parsed JSON data is backed by pooled memory that must be returned to avoid accumulation. @@ -86,7 +86,7 @@ If you need to keep the data beyond the disposal scope, extract the values you n `JsonObject` is mutable and ideal for lightweight manipulation of dynamic or semi-structured JSON. Use these methods when you want to deserialize JSON into a `JsonObject` object from `Task` or `HttpResponseMessage`. -:::important JsonObject Support +:::important[JsonObject Support] These methods are only available on .NET 6 and later, as they rely on `JsonObject`, which is only available in .NET 6.0 and higher as part of the `System.Text.Json.Nodes` API. diff --git a/docs/docs/deserializing-xml.md b/docs/docs/deserializing-xml.md index 82a6ce7..35d5435 100644 --- a/docs/docs/deserializing-xml.md +++ b/docs/docs/deserializing-xml.md @@ -5,7 +5,7 @@ title: Deserializing XML FluentHttpClient provides a set of extensions for reading and deserializing XML from `HttpResponseMessage` instances and from tasks that produce them. These extensions support deserializing XML into concrete .NET types or parsing XML into `XElement` for flexible document-style access. -:::danger AOT and XML +:::danger[AOT and XML] For Native AOT builds, only the sections for `XElement` apply. FluentHttpClient does not have typed XML deserialization overloads that are AOT-friendly. @@ -35,7 +35,7 @@ var model = await response.ReadXmlAsync(); Empty or whitespace content returns `null`. Malformed XML throws the underlying serializer or XML parsing exception. -:::warning XmlSerializer Memory Implications +:::warning[XmlSerializer Memory Implications] FluentHttpClient uses `System.Xml.Serialization.XmlSerializer` for typed XML deserialization. `XmlSerializer` generates and caches code for each unique type at runtime. On .NET Framework and some older .NET Core versions, these generated assemblies **cannot be unloaded**, which may lead to memory accumulation in long-running applications that deserialize many different XML types. diff --git a/docs/docs/httprequestbuilder.md b/docs/docs/httprequestbuilder.md index 4ab52a2..63a5e34 100644 --- a/docs/docs/httprequestbuilder.md +++ b/docs/docs/httprequestbuilder.md @@ -43,7 +43,7 @@ In this example: Starting in FluentHttpClient 5.0, `HttpRequestBuilder` is no longer able to be constructed directly. Its constructors are now internal to ensure consistent validation and to guide users toward the supported creation patterns. To begin building a request, use one of the `HttpClient` extension methods described below. Each one returns a fresh builder instance scoped to a single request. -:::warning Query Strings and Fragments +:::warning[Query Strings and Fragments] `BaseAddress` and `Route` must remain clean - free of query strings and fragments - so that FluentHttpClient has a single, predictable source of truth for all query-related behavior. Allowing query components in multiple places leads to ambiguous URI construction, duplicated encoding, and inconsistent request signatures. By enforcing that all query values flow through `QueryParameters`, the builder can reliably compose the final URI, ensure consistent encoding rules, and prevent subtle bugs caused by mixing inline query strings with fluent configuration. @@ -118,7 +118,7 @@ The table below lists the key properties on `HttpRequestBuilder` and how they ar - `Task SendAsync(HttpMethod method, HttpCompletionOption completionOption)` - `Task SendAsync(HttpMethod method, HttpCompletionOption completionOption, CancellationToken cancellationToken)` -:::important Use Recommended Overloads +:::important[Use Recommended Overloads] While `SendAsync` is the core sending primitive, most consumers should prefer the convenience extensions found in the [**Sending Requests**](./sending-requests.md) documentation. These extensions select the correct `HttpMethod`, keep your call sites clean, and make intent immediately obvious. Use `SendAsync` directly only when you are using a non-standard `HttpMethod`. @@ -144,7 +144,7 @@ All overloads delegate to the most complete overload. That method: - Apply each `OptionConfigurator` (where available). 6. Sends the request via `_client.SendAsync`. -:::danger Experimental Method +:::danger[Experimental Method] For testing and advanced scenarios, there is an experimental `BuildRequest` method that constructs and returns the `HttpRequestMessage` without sending it. This is primarily intended for unit tests and internal usage. **Do not depend on this method being available in future versions.** diff --git a/docs/docs/native-aot-support.md b/docs/docs/native-aot-support.md index 870884e..4cf5334 100644 --- a/docs/docs/native-aot-support.md +++ b/docs/docs/native-aot-support.md @@ -5,7 +5,7 @@ title: Native AOT Support FluentHttpClient provides a set of AOT-friendly JSON extensions that work with `System.Text.Json` source generation. These overloads avoid reflection-based serialization and deserialization so you can safely trim and compile your applications ahead of time. The `JsonTypeInfo` overloads are slightly faster and more explicit, while the `JsonSerializerContext` overloads allow passing a shared context instance. -:::danger XML AOT Support +:::danger[XML AOT Support] The XML reflection-based serialization and deserialization helpers provided in FluentHttpClient are **not** AOT-compatible and should only be used in JIT-compiled applications. diff --git a/docs/docs/testing-resources.md b/docs/docs/testing-resources.md index 3d3a2cb..04ca80d 100644 --- a/docs/docs/testing-resources.md +++ b/docs/docs/testing-resources.md @@ -1,5 +1,5 @@ --- -sidebar_position: 16 +sidebar_position: 17 title: Testing Resources --- diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index 79a5990..b1f21be 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -1,44 +1,58 @@ +// @ts-check +// `@type` JSDoc annotations allow editor autocompletion and type checking +// (when paired with `@ts-check`). +// There are various equivalent ways to declare your Docusaurus config. +// See: https://docusaurus.io/docs/api/docusaurus-config + import { themes as prismThemes } from 'prism-react-renderer'; -export default { +// This runs in Node.js - Don't use client-side code here (browser APIs, JSX...) + +/** @type {import('@docusaurus/types').Config} */ +const config = { title: 'FluentHttpClient', tagline: 'Use HttpClient with readable and chainable methods.', + favicon: 'img/favicon.ico', + + // Future flags, see https://docusaurus.io/docs/api/docusaurus-config#future + future: { + v4: true, // Improve compatibility with the upcoming Docusaurus v4 + }, + + // Set the production url of your site here url: 'https://scottoffen.github.io', + // Set the // pathname under which your site is served + // For GitHub pages deployment, it is often '//' baseUrl: '/fluenthttpclient/', - onBrokenLinks: 'warn', - markdown: { - hooks: { - onBrokenMarkdownLinks: 'warn', - }, - }, - favicon: 'img/favicon.ico', - trailingSlash: false, - organizationName: 'scottoffen', // GitHub username - projectName: 'fluenthttpclient', // Repo name + // GitHub pages deployment config. + // If you aren't using GitHub pages, you don't need these. + organizationName: 'scottoffen', // Usually your GitHub org/user name. + projectName: 'fluenthttpclient', // Usually your repo name. + onBrokenLinks: 'warn', + + // Even if you don't use internationalization, you can use this field to set + // useful metadata like html lang. For example, if your site is Chinese, you + // may want to replace "en" with "zh-Hans". i18n: { defaultLocale: 'en', locales: ['en'], }, - // This enables compatibility with Docusaurus v4 (future-proof) - future: { - v4: true, - }, - - clientModules: [ - require.resolve('./src/clientModules/version-attribute.js'), - ], - presets: [ [ 'classic', - { + /** @type {import('@docusaurus/preset-classic').Options} */ + ({ docs: { routeBasePath: '/', // Serve docs at the root (no /docs prefix) sidebarPath: './sidebars.js', - editUrl: 'https://github.com/scottoffen/fluenthttpclient/edit/main/docs/', + // Please change this to your repo. + // Remove this to remove the "edit this page" links. + editUrl: + 'https://github.com/scottoffen/fluenthttpclient/tree/main/docs/', + lastVersion: 'current', // "current" = whatever is in /docs versions: { current: { @@ -55,99 +69,103 @@ export default { }, } }, - blog: false, // Disable blog + blog: false, theme: { customCss: './src/css/custom.css', }, - }, + }), ], ], - themeConfig: { - navbar: { - title: 'FluentHttpClient', - logo: { - alt: 'FluentHttpClient Logo', - src: 'img/logo.svg', + themeConfig: + /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ + ({ + // Replace with your project's social card + image: 'img/docusaurus-social-card.jpg', + colorMode: { + respectPrefersColorScheme: true, }, - items: [ - { - type: 'docSidebar', - sidebarId: 'docsSidebar', - position: 'left', - label: 'Docs', - }, - { - type: 'docsVersionDropdown', - position: 'right', - dropdownActiveClassDisabled: true, - }, - { - href: 'https://github.com/scottoffen/fluenthttpclient', - label: 'GitHub', - position: 'right', - }, - ], - }, - footer: { - style: 'dark', - links: [ - { - title: 'Documentation', - items: [ - { - label: 'Getting Started', - to: '/', - }, - ], + navbar: { + title: 'FluentHttpClient', + logo: { + alt: 'FluentHttpClient Logo', + src: 'img/logo.svg', }, - { - title: 'Community', - items: [ - { - label: 'Discussions', - href: 'https://github.com/scottoffen/fluenthttpclient/discussions', - }, - { - label: 'Stack Overflow', - href: 'https://stackoverflow.com/questions/tagged/fluenthttpclient', - }, - ], - }, - { - title: 'Project', - items: [ - { - label: 'Contributing Guide', - href: 'https://github.com/scottoffen/fluenthttpclient/blob/main/CONTRIBUTING.md', - }, - { - label: 'Code of Conduct', - href: 'https://github.com/scottoffen/fluenthttpclient/blob/main/CODE_OF_CONDUCT.md', - }, - { - label: 'GitHub', - href: 'https://github.com/scottoffen/fluenthttpclient', - }, - ], - }, - ], - copyright: `Copyright © ${new Date().getFullYear()} Scott Offen`, - }, - prism: { - theme: prismThemes.github, - darkTheme: prismThemes.dracula, - }, - titleDelimiter: '|', - titleTemplate: 'FluentHttpClient | %s' - }, + items: [ + { + type: 'docSidebar', + sidebarId: 'docsSidebar', + position: 'left', + label: 'Docs', + }, + { + type: 'docsVersionDropdown', + position: 'right', + dropdownActiveClassDisabled: true, + }, + { + href: 'https://github.com/scottoffen/fluenthttpclient', + label: 'GitHub', + position: 'right', + }, + ], + }, + footer: { + style: 'dark', + links: [ + { + title: 'Documentation', + items: [ + { + label: 'Getting Started', + to: '/', + }, + ], + }, + { + title: 'Community', + items: [ + { + label: 'Discussions', + href: 'https://github.com/scottoffen/fluenthttpclient/discussions', + }, + { + label: 'Stack Overflow', + href: 'https://stackoverflow.com/questions/tagged/fluenthttpclient', + }, + ], + }, + { + title: 'Project', + items: [ + { + label: 'Contributing Guide', + href: 'https://github.com/scottoffen/fluenthttpclient/blob/main/CONTRIBUTING.md', + }, + { + label: 'Code of Conduct', + href: 'https://github.com/scottoffen/fluenthttpclient/blob/main/CODE_OF_CONDUCT.md', + }, + { + label: 'GitHub', + href: 'https://github.com/scottoffen/fluenthttpclient', + }, + ], + }, + ], + copyright: `Copyright © ${new Date().getFullYear()} Scott Offen`, + }, + prism: { + theme: prismThemes.github, + darkTheme: prismThemes.dracula, + }, + titleDelimiter: '|', + titleTemplate: 'FluentHttpClient | %s' + }), - scripts: [ - { - src: 'https://umami-f0it.onrender.com/script.js', - async: true, - defer: true, - 'data-website-id': 'f53d857b-b0e1-44c0-9eea-7ab0bd4a195b', - }, + clientModules: [ + require.resolve('./src/clientModules/version-attribute.js'), ], }; + +export default config; diff --git a/docs/package-lock.json b/docs/package-lock.json index 33ddb90..f973d61 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.0", "dependencies": { "@docusaurus/core": "3.10.1", - "@docusaurus/faster": "^3.10.1", + "@docusaurus/faster": "3.10.1", "@docusaurus/preset-classic": "3.10.1", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", @@ -26,15 +26,15 @@ } }, "node_modules/@algolia/abtesting": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/@algolia/abtesting/-/abtesting-1.18.1.tgz", - "integrity": "sha512-aehCadlWOGvrT91KUIZpC0MbB8KBW9yUuvTJFd2xesR7le/IsT4nJUnjCCZ4ZqZCeTcPHPV5mo//fZ5oxcSVYw==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@algolia/abtesting/-/abtesting-1.19.0.tgz", + "integrity": "sha512-Lhnez3hhXHk25lfxLAMxvkP4fmN3+1RgADhD2ssMDBYuAsDVReeyP+3SGRx+ntq8ijMrLqUyfvO72TB6jsTteQ==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.52.1", - "@algolia/requester-browser-xhr": "5.52.1", - "@algolia/requester-fetch": "5.52.1", - "@algolia/requester-node-http": "5.52.1" + "@algolia/client-common": "5.53.0", + "@algolia/requester-browser-xhr": "5.53.0", + "@algolia/requester-fetch": "5.53.0", + "@algolia/requester-node-http": "5.53.0" }, "engines": { "node": ">= 14.0.0" @@ -73,99 +73,99 @@ } }, "node_modules/@algolia/client-abtesting": { - "version": "5.52.1", - "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.52.1.tgz", - "integrity": "sha512-HmXOGBOAOJPounpBzBpuY0zDYeiCpxgHnQmuA7JO6ScukcBdGp3/XM9zJk5pJx/xNGD68mbPGXWpDxGtl6BwDQ==", + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.53.0.tgz", + "integrity": "sha512-0ZjA5Hcmaoz5Lj6OG0zhfIyeqzJZnLW2CRJA1W17UwMFGRtZAJ9yJKRvPEDA6gkpsIoQxORTSW6sWFiuYncPNQ==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.52.1", - "@algolia/requester-browser-xhr": "5.52.1", - "@algolia/requester-fetch": "5.52.1", - "@algolia/requester-node-http": "5.52.1" + "@algolia/client-common": "5.53.0", + "@algolia/requester-browser-xhr": "5.53.0", + "@algolia/requester-fetch": "5.53.0", + "@algolia/requester-node-http": "5.53.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-analytics": { - "version": "5.52.1", - "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.52.1.tgz", - "integrity": "sha512-5oo4+I8iixie9vXhCyNFCzeIr8pqA3FQ//VsLHTDvZAV4ttYOPGvYHGQq5NSalrLx5Jc3dRro/5uDOlnUMcBJg==", + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.53.0.tgz", + "integrity": "sha512-kWNodP75iiEaOtemC9F/hlxNBG5E2QUjN1BusnE6m2b4l7Qh/BUO3fGCVsmKJI65VO4VKGGmT43ICvHtTcJ2JQ==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.52.1", - "@algolia/requester-browser-xhr": "5.52.1", - "@algolia/requester-fetch": "5.52.1", - "@algolia/requester-node-http": "5.52.1" + "@algolia/client-common": "5.53.0", + "@algolia/requester-browser-xhr": "5.53.0", + "@algolia/requester-fetch": "5.53.0", + "@algolia/requester-node-http": "5.53.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-common": { - "version": "5.52.1", - "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.52.1.tgz", - "integrity": "sha512-qCDoZfx5MpX7XQzvQ3bC4tSEMkQWQMaF/ABtLuoze03Y/flR563CCSws02qIJ23oX7lxl92LsilZjINVyTdtLw==", + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.53.0.tgz", + "integrity": "sha512-YPN45TXD9Wrse185t/Ta7nktZsqpv97oOjCzp2sblHnCL6rBc9TDeJAg1IGl2UpdwnSD05Zu/5wLB4watOUMyg==", "license": "MIT", "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-insights": { - "version": "5.52.1", - "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.52.1.tgz", - "integrity": "sha512-hnGs0/lsFJ2PWDxNBz7pxreXo/Xz7gxYRcfePBUjsH26ad0kU/sgnVZd9LwWBpsQv65z2jlb5dkyaB9WE9M9FQ==", + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.53.0.tgz", + "integrity": "sha512-qAcYTDJE6m924FDDUQvdD6vh7DYaqOeSpFS74IP37/JRV0v4cGBauyxTF2WzDnokUylQDbqreoFIJZfg0Fitmw==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.52.1", - "@algolia/requester-browser-xhr": "5.52.1", - "@algolia/requester-fetch": "5.52.1", - "@algolia/requester-node-http": "5.52.1" + "@algolia/client-common": "5.53.0", + "@algolia/requester-browser-xhr": "5.53.0", + "@algolia/requester-fetch": "5.53.0", + "@algolia/requester-node-http": "5.53.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-personalization": { - "version": "5.52.1", - "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.52.1.tgz", - "integrity": "sha512-2VxxNc/uBysyKvGeBdSM5n9eIDKH8kWD7wd9/yqbJAiVwU4Yv6tU1LSJusHKrXV/aCu1KW7t9Gug9QyeEmtn/Q==", + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.53.0.tgz", + "integrity": "sha512-fQaY+DkSJOpuUVUe8MQTwrdiKAqkJGhpDarB08duBn/sUv7Bkib6MDRQauCcWTWTe4HIW+EbwQP9R4kci1V/Yw==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.52.1", - "@algolia/requester-browser-xhr": "5.52.1", - "@algolia/requester-fetch": "5.52.1", - "@algolia/requester-node-http": "5.52.1" + "@algolia/client-common": "5.53.0", + "@algolia/requester-browser-xhr": "5.53.0", + "@algolia/requester-fetch": "5.53.0", + "@algolia/requester-node-http": "5.53.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-query-suggestions": { - "version": "5.52.1", - "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.52.1.tgz", - "integrity": "sha512-O6mPtsw3xEfNOe6gWFpYLeAZAIljNa4Hgna3bq15PwyN7nbjTY0wXJFRbzs/0YVf75Br+SbOQUmjKxXYjDiSiQ==", + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.53.0.tgz", + "integrity": "sha512-o72tsiEZGfeS/dxL9IADfzcZWGEwKDEe5CvtrBuT//3JR+SHuTtHRI2ZTf7D7bcKagcbojvO8hnkHdfoakSlYg==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.52.1", - "@algolia/requester-browser-xhr": "5.52.1", - "@algolia/requester-fetch": "5.52.1", - "@algolia/requester-node-http": "5.52.1" + "@algolia/client-common": "5.53.0", + "@algolia/requester-browser-xhr": "5.53.0", + "@algolia/requester-fetch": "5.53.0", + "@algolia/requester-node-http": "5.53.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/client-search": { - "version": "5.52.1", - "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.52.1.tgz", - "integrity": "sha512-gA8oJOV1LnQQkDf91iebNnFInHuW0gRPEgLSOQ7EfipCEjYTHm5swm1DlH9H5RaRw4RrHuzHBegnlzc0MAstcg==", + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.53.0.tgz", + "integrity": "sha512-Ds16IyPm/dNJPCU8OzApo2gwGrgWT5BYHhE3NFwZbpCveqyvPDB9sZDDkJ5DsdOGT2aC+R3i0/M1OVXF2qdgPg==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.52.1", - "@algolia/requester-browser-xhr": "5.52.1", - "@algolia/requester-fetch": "5.52.1", - "@algolia/requester-node-http": "5.52.1" + "@algolia/client-common": "5.53.0", + "@algolia/requester-browser-xhr": "5.53.0", + "@algolia/requester-fetch": "5.53.0", + "@algolia/requester-node-http": "5.53.0" }, "engines": { "node": ">= 14.0.0" @@ -178,93 +178,93 @@ "license": "MIT" }, "node_modules/@algolia/ingestion": { - "version": "1.52.1", - "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.52.1.tgz", - "integrity": "sha512-U9zZfc5xIu9wRxZkt+HceJUAD4VKHKbAyLSloJdEyMRmphXeibfrY9cxqIXBcmPeZzGhn3Imb35Dq8l19PkJhw==", + "version": "1.53.0", + "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.53.0.tgz", + "integrity": "sha512-oNbT6z4NwD8Pou9VPINGlN/tlG1afESh2EbxqnP6rwl95xKVD/Zlciis1PpNeO/9U/rrajc1+7DcfKi03tX1KQ==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.52.1", - "@algolia/requester-browser-xhr": "5.52.1", - "@algolia/requester-fetch": "5.52.1", - "@algolia/requester-node-http": "5.52.1" + "@algolia/client-common": "5.53.0", + "@algolia/requester-browser-xhr": "5.53.0", + "@algolia/requester-fetch": "5.53.0", + "@algolia/requester-node-http": "5.53.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/monitoring": { - "version": "1.52.1", - "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.52.1.tgz", - "integrity": "sha512-a3SGNceHmkQfq77iG8Ka+w1pvwfZa/0lzEIgse30fL0kD+yKnd/dg0dQvSfFPAEt2f21DMcGkDSSeJlO3KdQjQ==", + "version": "1.53.0", + "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.53.0.tgz", + "integrity": "sha512-G+KZb/yd+qAOFn/cEvTGeLxQm8aP3a0od50l3z/ylccY+/o4YG3TNcjU1tFQHW4mXC137GPyR7W70R0kRQDLnA==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.52.1", - "@algolia/requester-browser-xhr": "5.52.1", - "@algolia/requester-fetch": "5.52.1", - "@algolia/requester-node-http": "5.52.1" + "@algolia/client-common": "5.53.0", + "@algolia/requester-browser-xhr": "5.53.0", + "@algolia/requester-fetch": "5.53.0", + "@algolia/requester-node-http": "5.53.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/recommend": { - "version": "5.52.1", - "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.52.1.tgz", - "integrity": "sha512-z98QEguCFDpxb4S/PyrUK1igqF8tPsdbqOUUO6ON91vJ58w+Gwa6ncrI0oNXSFcrkxA5EqPKPQ2A1PBCn08TYQ==", + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.53.0.tgz", + "integrity": "sha512-6aVfYd55Un6IUgPLbo84WfgFZlS3L0vA1ttzXL5vahHewUJ8jYgd89TzlWRTeej7w70mb9RWsVlFYGmJ/diQww==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.52.1", - "@algolia/requester-browser-xhr": "5.52.1", - "@algolia/requester-fetch": "5.52.1", - "@algolia/requester-node-http": "5.52.1" + "@algolia/client-common": "5.53.0", + "@algolia/requester-browser-xhr": "5.53.0", + "@algolia/requester-fetch": "5.53.0", + "@algolia/requester-node-http": "5.53.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/requester-browser-xhr": { - "version": "5.52.1", - "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.52.1.tgz", - "integrity": "sha512-CI7+/0I11QeZM59Uc8whd2or0kqzFVjpaPn9Qpwll/krHcBAxk24WkAQ6WX+IwDVMfpont4YGbKwAmCre3vE8Q==", + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.53.0.tgz", + "integrity": "sha512-ke27DqgzCOlt+RbeEdCxtXxMQOnAOi8ujr2wid0DmDKzR95Kw/f9sBsuhBxtjevCqJRJszfRTLY0B1pbO6IhkA==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.52.1" + "@algolia/client-common": "5.53.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/requester-fetch": { - "version": "5.52.1", - "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.52.1.tgz", - "integrity": "sha512-S6bDuw9byfOvm3T71cgdoZgrgnZq6hpdMLkx52Louh57nUAmvGQESz2aojOynQHjbTiV55smvAFbgn0qT4tJrg==", + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.53.0.tgz", + "integrity": "sha512-GngiOqt2Gq4oLno6yXQVj9om+qSO9SWAoduoTOEg79dKZ62brB8OOIvSJG/vDNoanYi6a7Al9uDZwXvi+bcVTg==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.52.1" + "@algolia/client-common": "5.53.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@algolia/requester-node-http": { - "version": "5.52.1", - "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.52.1.tgz", - "integrity": "sha512-tqZXM+54rWo4mk5jL5Z/flE11nPmNEdXwFBM5py9DkOmbjeCNemfVd45FyM97XdzfZ0dl9uOJC6PYn1FpkeyQg==", + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.53.0.tgz", + "integrity": "sha512-6mF9LZMUk0QqWvrnxkxBqhswwz6Xfiwy6/gmTzL5HrlhdVG3ITAqGV2k3XmVThP1h0Ulc3VQwiNCD7/Nr4JNlQ==", "license": "MIT", "dependencies": { - "@algolia/client-common": "5.52.1" + "@algolia/client-common": "5.53.0" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@babel/code-frame": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", - "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.28.5", + "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, @@ -273,29 +273,29 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.29.3", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz", - "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", - "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helpers": "^7.28.6", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.29.0", - "@babel/types": "^7.29.0", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", @@ -321,13 +321,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.29.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", - "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", + "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.29.0", - "@babel/types": "^7.29.0", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -337,25 +337,25 @@ } }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", - "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz", + "integrity": "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==", "license": "MIT", "dependencies": { - "@babel/types": "^7.27.3" + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", - "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.28.6", - "@babel/helper-validator-option": "^7.27.1", + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -374,17 +374,17 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.29.3", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.29.3.tgz", - "integrity": "sha512-RpLYy2sb51oNLjuu1iD3bwBqCBWUzjO0ocp+iaCP/lJtb2CPLcnC2Fftw+4sAzaMELGeWTgExSKADbdo0GFVzA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.29.7.tgz", + "integrity": "sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==", "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-member-expression-to-functions": "^7.28.5", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/helper-replace-supers": "^7.28.6", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/traverse": "^7.29.0", + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-member-expression-to-functions": "^7.29.7", + "@babel/helper-optimise-call-expression": "^7.29.7", + "@babel/helper-replace-supers": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", + "@babel/traverse": "^7.29.7", "semver": "^6.3.1" }, "engines": { @@ -404,12 +404,12 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.28.5.tgz", - "integrity": "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.29.7.tgz", + "integrity": "sha512-907Uymvqgg1dwUA+7IGwFAOSYzQOuzPXKNJ1yxzwPffzkYFg2q2eHi1fIOs6sXkG9NbIUMunnUlkYsfRFNvomg==", "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-annotate-as-pure": "^7.29.7", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, @@ -446,49 +446,49 @@ } }, "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz", - "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.29.7.tgz", + "integrity": "sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==", "license": "MIT", "dependencies": { - "@babel/traverse": "^7.28.5", - "@babel/types": "^7.28.5" + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", - "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", "license": "MIT", "dependencies": { - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", - "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.28.6" + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -498,35 +498,35 @@ } }, "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", - "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.29.7.tgz", + "integrity": "sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==", "license": "MIT", "dependencies": { - "@babel/types": "^7.27.1" + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", - "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", - "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.29.7.tgz", + "integrity": "sha512-16AMiW26DbXWBbr3B8wNozKM0ydMLB892vaOaJW/fPJdnT8vJk5sdkQcU/isqUxyCE0cEoa8wZOcbgDuC4b6Og==", "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-wrap-function": "^7.27.1", - "@babel/traverse": "^7.27.1" + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-wrap-function": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -536,14 +536,14 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.28.6.tgz", - "integrity": "sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.29.7.tgz", + "integrity": "sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==", "license": "MIT", "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.28.5", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/traverse": "^7.28.6" + "@babel/helper-member-expression-to-functions": "^7.29.7", + "@babel/helper-optimise-call-expression": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -553,79 +553,79 @@ } }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", - "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.29.7.tgz", + "integrity": "sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==", "license": "MIT", "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.28.6.tgz", - "integrity": "sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.29.7.tgz", + "integrity": "sha512-iES0Skag9ERIF68aXadpO6dbXa03mNWK3sEqJaMnLNs/eC3l0lkImdfoy6Y09/SfkpawdAB4RjQ7PVA7TcVGdw==", "license": "MIT", "dependencies": { - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", - "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", "license": "MIT", "dependencies": { - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0" + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.29.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz", - "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", "license": "MIT", "dependencies": { - "@babel/types": "^7.29.0" + "@babel/types": "^7.29.7" }, "bin": { "parser": "bin/babel-parser.js" @@ -635,13 +635,13 @@ } }, "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.28.5.tgz", - "integrity": "sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.29.7.tgz", + "integrity": "sha512-j8SrR0zLZrRsC09DlszEx8FpMiwukKffYXMK0d5LmOglO7vGG6sz/BR/20yHqWH+Lnn31JTt2PE3hIWNgM2J6w==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.28.5" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -651,12 +651,12 @@ } }, "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz", - "integrity": "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.29.7.tgz", + "integrity": "sha512-r8j8escF+U2FUHo0KOhPUdMzUO+jp9fInva6+ACVAF3Y97Ev+5iNZwiqTghmzNeWwDkOPlYuTcfb1vDaoZKmAQ==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -666,12 +666,12 @@ } }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz", - "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.29.7.tgz", + "integrity": "sha512-GE1TFSiuFeGsCxmYXZl8HwoPrVlwe4rHPFE8weieGKZqnDORK+Ar3vgWMgW+AOxQ6/2TgLSKx9p6W7O4rC6qgQ==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -681,13 +681,13 @@ } }, "node_modules/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": { - "version": "7.29.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array/-/plugin-bugfix-safari-rest-destructuring-rhs-array-7.29.3.tgz", - "integrity": "sha512-SRS46DFR4HqzUzCVgi90/xMoL+zeBDBvWdKYXSEzh79kXswNFEglUpMKxR04//dPqwYXWUBJ3mpUd933ru9Kmg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array/-/plugin-bugfix-safari-rest-destructuring-rhs-array-7.29.7.tgz", + "integrity": "sha512-oBNVCvnO5tND+xSopWvV8WNGfpTfgP4Zr/YXXSj8zfmcPktp5Ku/aZlsIowgSD4fjmgHn6sGmB9APVsU5zOdhA==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -697,14 +697,14 @@ } }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", - "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.29.7.tgz", + "integrity": "sha512-QQt9qKHZ2sg/kivaLr7lnQr8HVrQDdBNSfCsTjiDxRuX/K5ORyKq+Bu8Xr0cDE3Dfkv0cw28Ve0EKyKMvulkOw==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/plugin-transform-optional-chaining": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", + "@babel/plugin-transform-optional-chaining": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -714,13 +714,13 @@ } }, "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.6.tgz", - "integrity": "sha512-a0aBScVTlNaiUe35UtfxAN7A/tehvvG4/ByO6+46VPKTRSlfnAFsgKy0FUh+qAkQrDTmhDkT+IBOKlOoMUxQ0g==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.29.7.tgz", + "integrity": "sha512-pn6QacGLgvCcwc+syUhKE/qSjV2D1IHDB84RNxWYSt1mW3K/SCtjinZ2p0cETJxAWBjPy3K/1lHwG5BjjPxNlw==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/traverse": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -754,12 +754,12 @@ } }, "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.28.6.tgz", - "integrity": "sha512-pSJUpFHdx9z5nqTSirOCMtYVP2wFgoWhP0p3g8ONK/4IHhLIBd0B9NYqAvIUAhq+OkhO4VM1tENCt0cjlsNShw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.29.7.tgz", + "integrity": "sha512-/An1OCBN93thpBAGyfsK2pcf0jvju1SAtKkL2Ny++B5Sy6sqgzXDQH1cZxWbF96Wuk+bn41MDA9bLd4VVAw6rw==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -769,12 +769,12 @@ } }, "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", - "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.29.7.tgz", + "integrity": "sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -784,12 +784,12 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", - "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.29.7.tgz", + "integrity": "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -799,12 +799,12 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", - "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.29.7.tgz", + "integrity": "sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -830,12 +830,12 @@ } }, "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", - "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.29.7.tgz", + "integrity": "sha512-N7zArUXWzAMzm+/N0uPBeVB3Fam5lMxtUwMmDK5f/IBBS7a7p1qeUoxd/6CckXoxUdgsntq1Dh8xNW06maZbDQ==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -845,14 +845,14 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.29.0.tgz", - "integrity": "sha512-va0VdWro4zlBr2JsXC+ofCPB2iG12wPtVGTWFx2WLDOM3nYQZZIGP82qku2eW/JR83sD+k2k+CsNtyEbUqhU6w==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.29.7.tgz", + "integrity": "sha512-d98gXZkgswvkyohMBABkhm3GeXhYj8psWfwQ2C7gtfrKGTykQa/iOIi+JJhwMjPlZ6Vm2XN+DCf3Es1EoG4ZLA==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-remap-async-to-generator": "^7.27.1", - "@babel/traverse": "^7.29.0" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-remap-async-to-generator": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -862,14 +862,14 @@ } }, "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.28.6.tgz", - "integrity": "sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.29.7.tgz", + "integrity": "sha512-pcUb2SS+RMo9TWVBwKGI5ShtoG7R+zBsFmCKDa6fe8c+hPr3XJlZgoE5j6i8W7gDjhyvy+85vmYexanvXh3d1w==", "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-remap-async-to-generator": "^7.27.1" + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-remap-async-to-generator": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -879,12 +879,12 @@ } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz", - "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.29.7.tgz", + "integrity": "sha512-cUSmjh72N+rN4PrkFlN1dJwNCwjVp5d38/CQrEsFggkD10UiFlBFgdH3tv5dNsLuHY+3S8db2xCHjhZcv5WgvA==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -894,12 +894,12 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.6.tgz", - "integrity": "sha512-tt/7wOtBmwHPNMPu7ax4pdPz6shjFrmHDghvNC+FG9Qvj7D6mJcoRQIF5dy4njmxR941l6rgtvfSB2zX3VlUIw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.29.7.tgz", + "integrity": "sha512-ONyr4+AZhKh8yKWInVxU9AXA9EbsyeLcL6V0dJy6M2/62vuvpGm29zzuymbTpdc451GEpDIdAyPLP3r+P61yKQ==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -909,13 +909,13 @@ } }, "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.28.6.tgz", - "integrity": "sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.29.7.tgz", + "integrity": "sha512-GtcpjFvanPfzNQi3eTitsCqtRRmmqzpy/A+yhTR1HaZo1Ly3EA8ZXxlPyHdR8/IuRMYc3E4wdGBewB2QKQjAaA==", "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -925,13 +925,13 @@ } }, "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.6.tgz", - "integrity": "sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.29.7.tgz", + "integrity": "sha512-kibJgmEdX2iMwsHY2tSZNDgj8PwIlCQz7FK9KuGKO8zsuoUwSEhoNnNVp/emKWrbY4HeO6kkXfdMqRKKKXBm2A==", "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -941,17 +941,17 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.6.tgz", - "integrity": "sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.29.7.tgz", + "integrity": "sha512-qV0OGGBVacduzQHE649JyCneOFI/maT+YKsO+K4Yi3xv2wTPNjM/W2o2gdzMwEAZz7fXNTHAe0NcSg30bIN69g==", "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-globals": "^7.28.0", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-replace-supers": "^7.28.6", - "@babel/traverse": "^7.28.6" + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-replace-supers": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -961,13 +961,13 @@ } }, "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.28.6.tgz", - "integrity": "sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.29.7.tgz", + "integrity": "sha512-RK7/IyU5phpuCdBAuig5VkzG/EnbDaui5SQGdU9BFrHdV+mV4cUjLMQ9lJDjLNtWHsqtiefpGZUXQP2BiTYMsA==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/template": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/template": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -977,13 +977,13 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.5.tgz", - "integrity": "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.29.7.tgz", + "integrity": "sha512-iPX8aD6H9zV5s7ZsqTdNocPN/MGQ5sSMnElKrktxjJRMnB2jN/1p2+R7GkfD6CAYoVFqy5A4XnSIUeGgJzIWpg==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.28.5" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -993,13 +993,13 @@ } }, "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.28.6.tgz", - "integrity": "sha512-SljjowuNKB7q5Oayv4FoPzeB74g3QgLt8IVJw9ADvWy3QnUb/01aw8I4AVv8wYnPvQz2GDDZ/g3GhcNyDBI4Bg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.29.7.tgz", + "integrity": "sha512-3qc18hsD2RdZiyJNDNc7HQpv6xbncwh8FYtxNFFzclSyh/trPD9KkVR9BDECUjDLvb7yJVF15GfYUuC+LMkkiQ==", "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1009,12 +1009,12 @@ } }, "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz", - "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.29.7.tgz", + "integrity": "sha512-6IvRRriEMqnBwD6chtxdLpMYCHWEzN+oL5cyQtjykya19UgzbmKhxmhZgKC/LHxS2nYr9Q/qYPZ5Lr6jOL9+yQ==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1024,13 +1024,13 @@ } }, "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.29.0.tgz", - "integrity": "sha512-zBPcW2lFGxdiD8PUnPwJjag2J9otbcLQzvbiOzDxpYXyCuYX9agOwMPGn1prVH0a4qzhCKu24rlH4c1f7yA8rw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.29.7.tgz", + "integrity": "sha512-2wiIyo2BjtgU7HufSeDnL9L2O7zr8jmhFKuSr65VpRkUiRKRNpb0mdlk56+XPPKoIrfHqzbMuglDvZun0RISsA==", "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1040,12 +1040,12 @@ } }, "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz", - "integrity": "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.29.7.tgz", + "integrity": "sha512-giOlEm/EFjfjr+te9NsdjkUo2v4f8rS/SXPumRVHAtbNcyNlvtREkU1dZzaIDclNpnaVhlCqRdFKhJBjBikzLg==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1055,13 +1055,13 @@ } }, "node_modules/@babel/plugin-transform-explicit-resource-management": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.6.tgz", - "integrity": "sha512-Iao5Konzx2b6g7EPqTy40UZbcdXE126tTxVFr/nAIj+WItNxjKSYTEw3RC+A2/ZetmdJsgueL1KhaMCQHkLPIg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.29.7.tgz", + "integrity": "sha512-Rstj7coNz8sE+7Ju7ihpHLI564lsK5pUpNNlvptCIC/16E/S5hbl6n3kESPKdNRmqEWlpn5xpS5Q2dvXBsySLw==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/plugin-transform-destructuring": "^7.28.5" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/plugin-transform-destructuring": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1071,12 +1071,12 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.28.6.tgz", - "integrity": "sha512-WitabqiGjV/vJ0aPOLSFfNY1u9U3R7W36B03r5I2KoNix+a3sOhJ3pKFB3R5It9/UiK78NiO0KE9P21cMhlPkw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.29.7.tgz", + "integrity": "sha512-zFpMOTLZBdW5LfObqcSbL6kefg4R4eLdmvS0wbN9M6D5Mym/sKm9toOoWyVOa+xDjvCnuWcHls2YonXwHvH3CQ==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1086,12 +1086,12 @@ } }, "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz", - "integrity": "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.29.7.tgz", + "integrity": "sha512-24B2nOy2TeJSMheqwPD4DDQOV/elLSIlKxjZt4i05H5AgdPdWR3n18HnNrcJ+j76WJd9gbwb9jPjNYUy6RautA==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1101,13 +1101,13 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", - "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.29.7.tgz", + "integrity": "sha512-zeSIHh0+E1Um1WJRXCFlHQYu2ieJNdivLLjlBEp+dIBu3S51n+SZZmIXjxnItw6pz56Cn+KvK68BIBVsxq2JiQ==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1117,14 +1117,14 @@ } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", - "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.29.7.tgz", + "integrity": "sha512-otRWaHXE6fbAGkePvaj/kvs3HsqXfPhlnzwSOlnFgbqCPMd975dW+4wZ00WFBt+/YlBGcJwNrARQTOJOb4ZrIg==", "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.27.1" + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1134,12 +1134,12 @@ } }, "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.28.6.tgz", - "integrity": "sha512-Nr+hEN+0geQkzhbdgQVPoqr47lZbm+5fCUmO70722xJZd0Mvb59+33QLImGj6F+DkK3xgDi1YVysP8whD6FQAw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.29.7.tgz", + "integrity": "sha512-RRnE2+eon1rJAq8MnoF1b5kTpY1vU88twHcvcKMrsqP/jxIRqDVs9iJB5fqPuqyeFAW0wJo4MlUIPpQCq/aRsg==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1149,12 +1149,12 @@ } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", - "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.29.7.tgz", + "integrity": "sha512-DZ/oLP21ZuWx1vKqnoNv6/tvEK48AQOBRai40CX9dTjGluvT/YZCyY3rryDtyUqCEoyNroy5KKPwX2iQCiRvyw==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1164,12 +1164,12 @@ } }, "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.6.tgz", - "integrity": "sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.29.7.tgz", + "integrity": "sha512-A0H91hh6W8MFRkp5TqJmMr39jzGD1A1E1Ysiv2O06Sfbhkapm+XyIzxWCEh5kqwOZ1/8QZ0dY3SeQ7XBqfJd5Q==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1179,12 +1179,12 @@ } }, "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz", - "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.29.7.tgz", + "integrity": "sha512-hl1kwFZCCiDyfH25Xmco9jTrkPgnS9pmOzSG7W5I4SaGbLeqKv417hcU2RKmaxoPEgsoJh7ZPOrnPGq99bHoUg==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1194,13 +1194,13 @@ } }, "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz", - "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.29.7.tgz", + "integrity": "sha512-fxtQoH3m5ywUSIfaH0FGCzWu4McsYon5bD3K4XnskC7f+OyQMj7rsOMi4NvvmJ83WwBAg4UCe+ov4VZlqEvyew==", "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1210,13 +1210,13 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.28.6.tgz", - "integrity": "sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.29.7.tgz", + "integrity": "sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ==", "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1226,15 +1226,15 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.29.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.4.tgz", - "integrity": "sha512-N7QmZ0xRZfjHOfZeQLJjwgX2zS9pdGHSVl/cjSGlo4dXMqvurfxXDMKY4RqEKzPozV78VMcd0lxyG13mlbKc4w==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.7.tgz", + "integrity": "sha512-TM2ZcQLoG2/y4HODiStCo10DibYhWhGWAwVv+EQKmG/7GFl0N+AAmUiXOMKM+aiJ9XBJ9AHVZBvTzMnJ2sM3cQ==", "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.29.0" + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1244,13 +1244,13 @@ } }, "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz", - "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.29.7.tgz", + "integrity": "sha512-B4UkaTK3QpgCwJnrxKfMPKdo92CN7OKXAlpAAnM3UPu0Q0lCCk57ylA9AJbRy2v8dDKOPAAWcoR6CMyeoHwRCA==", "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1260,13 +1260,13 @@ } }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.29.0.tgz", - "integrity": "sha512-1CZQA5KNAD6ZYQLPw7oi5ewtDNxH/2vuCh+6SmvgDfhumForvs8a1o9n0UrEoBD8HU4djO2yWngTQlXl1NDVEQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.29.7.tgz", + "integrity": "sha512-vuFoLwr4qnv2xbZ16SQd6uPcH5FNrLHhk/Jzo++0XJFcaDsr4gjJVg6j398oMHiC+83k/GiBzviwF5KBJkPUtQ==", "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1276,12 +1276,12 @@ } }, "node_modules/@babel/plugin-transform-new-target": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz", - "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.29.7.tgz", + "integrity": "sha512-fEo41GmsOUhOBlw8ioo6zvjX5Xc2Lqkzlyfqbpsk3eB6TReV18uhxZ0esfEokVbY2+PVJAQHNKxER6lGrzNd3A==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1291,12 +1291,12 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.28.6.tgz", - "integrity": "sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.29.7.tgz", + "integrity": "sha512-idmp1dFaekP9GbcMvG24Kvw2BfhFZjHnNJCkV4WuIY4PskJzwI3f1N5OdgYke38T7rftO6ERulFRn2cFeZwRkg==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1306,12 +1306,12 @@ } }, "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.28.6.tgz", - "integrity": "sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.29.7.tgz", + "integrity": "sha512-zR7fv/z14OjgHl4AgRtkDBvBMhIzCxqV/qN/2BCRC7LjFwvuzjYe7gDWxC4Wl/SNsLM6SE1IWvRPYMgSJaUvNw==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1321,16 +1321,16 @@ } }, "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.6.tgz", - "integrity": "sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.29.7.tgz", + "integrity": "sha512-Ld98jn4c0smUywL57m7SgsHq3OpThOa6LqZJif3G6jYOovPleoFhVrBJ1WegRApSFB2wu4+RelAj9AC9G08Z4A==", "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/plugin-transform-destructuring": "^7.28.5", - "@babel/plugin-transform-parameters": "^7.27.7", - "@babel/traverse": "^7.28.6" + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/plugin-transform-destructuring": "^7.29.7", + "@babel/plugin-transform-parameters": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1340,13 +1340,13 @@ } }, "node_modules/@babel/plugin-transform-object-super": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz", - "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.29.7.tgz", + "integrity": "sha512-Ea/diGcw0twB5IlZPO5sgET6fJsLJqPABqTuFWIR+iMPGPZJkATEIWx0wa+aEQ5UY1CBQyP/gkAiLEqn1vBiQA==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-replace-supers": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1356,12 +1356,12 @@ } }, "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.28.6.tgz", - "integrity": "sha512-R8ja/Pyrv0OGAvAXQhSTmWyPJPml+0TMqXlO5w+AsMEiwb2fg3WkOvob7UxFSL3OIttFSGSRFKQsOhJ/X6HQdQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.29.7.tgz", + "integrity": "sha512-sLsyndxK2VwX6yNUOakMb7Sh553ZTe/vVM1XJ+9Z5aW1ytsc8xOIwmyk05NNjN60vkc5/KqoTH6hB4V41LJhng==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1371,13 +1371,13 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.6.tgz", - "integrity": "sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.29.7.tgz", + "integrity": "sha512-6GM1dhvK3gNODkXcEcMCOLEDCLSoZ/sBbro2Ax8HURyasQ4NshagQixkRFdh5niI6E4gmA/jYI/4aT7rRos3ZQ==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1387,12 +1387,12 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.27.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz", - "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.29.7.tgz", + "integrity": "sha512-ZDOBqV/qLYJI0YElr8DcENEyARsFQeESqWXH6gZlghYXuPPjvweuDhP4VyEi4BlUBlLRFZVjxoZDMjxhLW766g==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1402,13 +1402,13 @@ } }, "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.28.6.tgz", - "integrity": "sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.29.7.tgz", + "integrity": "sha512-/6Rz4DK1ETDEM/bWHsPHcaEe7ZaT1EqSXjtSP/L0DijOYuaUhiRiOKcwpZ8P7zR4xXEHc2ITdiCgBm9Tpyv9ug==", "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1418,14 +1418,14 @@ } }, "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.28.6.tgz", - "integrity": "sha512-b97jvNSOb5+ehyQmBpmhOCiUC5oVK4PMnpRvO7+ymFBoqYjeDHIU9jnrNUuwHOiL9RpGDoKBpSViarV+BU+eVA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.29.7.tgz", + "integrity": "sha512-+BNo06dnrzdNNqCm1X6YUaVv0DKk8Q+JYcoZfOkLhYWNCXzlwTSRq8zGWayT1csjcpNXV9CQTBRRbmTLZac5cA==", "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1435,12 +1435,12 @@ } }, "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz", - "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.29.7.tgz", + "integrity": "sha512-bOMRLQuI0A5ZqHq3OWJ89/rXpJ/NJrbVhXiP4zwPGMs6kpcVsuTUNjwoE30K0Qm3mf48a/TnRYYD6vPNqcg6jA==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1450,12 +1450,12 @@ } }, "node_modules/@babel/plugin-transform-react-constant-elements": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.27.1.tgz", - "integrity": "sha512-edoidOjl/ZxvYo4lSBOQGDSyToYVkTAwyVoa2tkuYTSmjrB1+uAedoL5iROVLXkxH+vRgA7uP4tMg2pUJpZ3Ug==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.29.7.tgz", + "integrity": "sha512-J0wGhKan+rIiE2OhfhRptySLrJ6SjQYM6b6N1FMlhyhCcw1Mig8vQjWchyB+bgHGDvaWo6Diu6CLRMra2uMtmg==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1465,12 +1465,12 @@ } }, "node_modules/@babel/plugin-transform-react-display-name": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.28.0.tgz", - "integrity": "sha512-D6Eujc2zMxKjfa4Zxl4GHMsmhKKZ9VpcqIchJLvwTxad9zWIYulwYItBovpDOoNLISpcZSXoDJ5gaGbQUDqViA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.29.7.tgz", + "integrity": "sha512-+1wdDMGNb4UPeY3Q4L5yLiYe6TXPXubs4NjrgRFw13hPRLJfEMw2Q5OXkee6/IfdqePIeW4Jjwe3aBh7SdKz4Q==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1480,16 +1480,16 @@ } }, "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.28.6.tgz", - "integrity": "sha512-61bxqhiRfAACulXSLd/GxqmAedUSrRZIu/cbaT18T1CetkTmtDN15it7i80ru4DVqRK1WMxQhXs+Lf9kajm5Ow==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.29.7.tgz", + "integrity": "sha512-WsZulLVBUHXVj2cUcPVx6UE21TpalB6bHbSFErKT0Ib++ax24jjXe73FqlWvdylFOjiuPHYi6VCcgRad1ItN+A==", "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/plugin-syntax-jsx": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/plugin-syntax-jsx": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1499,12 +1499,12 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-development": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.27.1.tgz", - "integrity": "sha512-ykDdF5yI4f1WrAolLqeF3hmYU12j9ntLQl/AOG1HAS21jxyg1Q0/J/tpREuYLfatGdGmXp/3yS0ZA76kOlVq9Q==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.29.7.tgz", + "integrity": "sha512-Xfy3UVMF04+ypnFbkhvfqtmvwfe92qwQdbGZVonhE+6v35GzlofmOnA1szaZqzb9xYWr0nl1e5EMmzi0DNON1g==", "license": "MIT", "dependencies": { - "@babel/plugin-transform-react-jsx": "^7.27.1" + "@babel/plugin-transform-react-jsx": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1514,13 +1514,13 @@ } }, "node_modules/@babel/plugin-transform-react-pure-annotations": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.27.1.tgz", - "integrity": "sha512-JfuinvDOsD9FVMTHpzA/pBLisxpv1aSf+OIV8lgH3MuWrks19R27e6a6DipIg4aX1Zm9Wpb04p8wljfKrVSnPA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.29.7.tgz", + "integrity": "sha512-H5E+HBgDpr6Q5t+Aj11tL7XkIui1jhbIoArVQnqjgXo5/3YxkN7ZEBcWF4RQlB0T4rrxJQbXS6kiFV6B7XTqUA==", "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1530,12 +1530,12 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.0.tgz", - "integrity": "sha512-FijqlqMA7DmRdg/aINBSs04y8XNTYw/lr1gJ2WsmBnnaNw1iS43EPkJW+zK7z65auG3AWRFXWj+NcTQwYptUog==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.7.tgz", + "integrity": "sha512-rNNFV0DBAJp988xW2DOntfDoYn1eR8GGF5AT5vYc+rjyfaQkM242c9tZUHHPe7KYaiJizXPWhQTzzdbXySyhBw==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1545,13 +1545,13 @@ } }, "node_modules/@babel/plugin-transform-regexp-modifiers": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.28.6.tgz", - "integrity": "sha512-QGWAepm9qxpaIs7UM9FvUSnCGlb8Ua1RhyM4/veAxLwt3gMat/LSGrZixyuj4I6+Kn9iwvqCyPTtbdxanYoWYg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.29.7.tgz", + "integrity": "sha512-mB5Fs0VWrJ42ZCmc8114v60qetdaUVNkj9PmSZRmanCZM3S9hm0CFRLjRmYIsuXav14l2jvZ+4T8iiCGnhj3nQ==", "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1561,12 +1561,12 @@ } }, "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz", - "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.29.7.tgz", + "integrity": "sha512-5+YhdpVgmfSmwZyLMftfaiffLRMHjzIRHFHHLdibcSyJm2pasMrKHrO3Ptrt2DRshjvpgjEJJ1zVW14WPq/6QA==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1576,13 +1576,13 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.29.0.tgz", - "integrity": "sha512-jlaRT5dJtMaMCV6fAuLbsQMSwz/QkvaHOHOSXRitGGwSpR1blCY4KUKoyP2tYO8vJcqYe8cEj96cqSztv3uF9w==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.29.7.tgz", + "integrity": "sha512-xmAscdE/AsqRW7vutbPNoUmu/nF5SrLKPs7aoJgEjo35lLKA/Bc0i2rMv/hr1+Y0o1bQCiVtith3u2vdgRL39Q==", "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", "babel-plugin-polyfill-corejs2": "^0.4.14", "babel-plugin-polyfill-corejs3": "^0.13.0", "babel-plugin-polyfill-regenerator": "^0.6.5", @@ -1605,12 +1605,12 @@ } }, "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz", - "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.29.7.tgz", + "integrity": "sha512-I+WYbGBAiCn7nA6xBrlgPH+MB7HWb4u8pv5S0Pv7OtwNvIFvCCb24YlttKEeUFVurfBCEaOTnuhlqsb7f0Z5Dg==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1620,13 +1620,13 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.28.6.tgz", - "integrity": "sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.29.7.tgz", + "integrity": "sha512-/u5K1QWada7tbYNqTjMh96718g9NTwh9tfPJMsSmVsQwGT447FskV+KcfeXkXq2GWki4EM/MuTdmBec+hOuVTQ==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1636,12 +1636,12 @@ } }, "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz", - "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.29.7.tgz", + "integrity": "sha512-BCHzNYJGe9l7EpwwDBN/ztlL2NYFFq8hp9ddjtUEM9f2O7S7kKV/lL6Fwo7IF7NSkYhPK2vO+86nIGltA90MsA==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1651,12 +1651,12 @@ } }, "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", - "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.29.7.tgz", + "integrity": "sha512-NCSEJ4sLFU2gqAub45HYh4fus2yQ36rr6ei6vpU7NdoJqCpxvEG8E6eJpscGyXP3VHD2Ny+fSXr04k1hoUrFqA==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1666,12 +1666,12 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz", - "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.29.7.tgz", + "integrity": "sha512-223mNGoTkBiTEWFoK+Q6Go3tueMRclO8vxxxxquNCYuNI4jWOofFKJRRDu6SDrB8Sgo1UEGW9T4GAQ8ZyRso1A==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1681,16 +1681,16 @@ } }, "node_modules/@babel/plugin-transform-typescript": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.6.tgz", - "integrity": "sha512-0YWL2RFxOqEm9Efk5PvreamxPME8OyY0wM5wh5lHjF+VtVhdneCWGzZeSqzOfiobVqQaNCd2z0tQvnI9DaPWPw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.29.7.tgz", + "integrity": "sha512-jK52h8LaLc7JarhQV2ofeFMts4H7vnOXnqZNA6fYglBTZewRBE51KWt3BUltW1P+KoPsYkHoJeXePuz4zo2LMw==", "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-create-class-features-plugin": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/plugin-syntax-typescript": "^7.28.6" + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", + "@babel/plugin-syntax-typescript": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1700,12 +1700,12 @@ } }, "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz", - "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.29.7.tgz", + "integrity": "sha512-jCfXxSjf94lf4E0hKE0AByxF6F3/pVFqRdUUNkDJhsY0m1ZKjnN6ZYyMeHNpzflxb/0q5b7t3p+BE+SLF1WOtA==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1715,13 +1715,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.28.6.tgz", - "integrity": "sha512-4Wlbdl/sIZjzi/8St0evF0gEZrgOswVO6aOzqxh1kDZOl9WmLrHq2HtGhnOJZmHZYKP8WZ1MDLCt5DAWwRo57A==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.29.7.tgz", + "integrity": "sha512-OgZ+zoAJgZLUCunsTRQ5LAjOywDv5zzZ2/hQ5aMw1pGXyY2rtE8/chXYUmu3AlVHKpm10KEdG9aMwbI/K76ZGw==", "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1731,13 +1731,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz", - "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.29.7.tgz", + "integrity": "sha512-7D/x/23/d/3VqZ0QA+LGbZMlGwZjztBygSWWWsfTPoQ1oQ6Q1P6Mr3d0kk42XabyUVw+fha3LqdRsFqeKqvCyA==", "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1747,13 +1747,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.28.6.tgz", - "integrity": "sha512-/wHc/paTUmsDYN7SZkpWxogTOBNnlx7nBQYfy6JJlCT7G3mVhltk3e++N7zV0XfgGsrqBxd4rJQt9H16I21Y1Q==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.29.7.tgz", + "integrity": "sha512-BLOhLht9DOJwIxlmp91wHvkXv1lguuHS3/FwUO8HL1H0u8s4hR1gASVFyilu9iGtcTRYqjTZmlsFFeQletntEg==", "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.28.5", - "@babel/helper-plugin-utils": "^7.28.6" + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1763,76 +1763,76 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.29.5", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.5.tgz", - "integrity": "sha512-/69t2aEzGKHD76DyLbHysF/QH2LJOB8iFnYO37unDTKBTubzcMRv0f3H5EiN1Q6ajOd/eB7dAInF0qdFVS06kA==", - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.29.3", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-plugin-utils": "^7.28.6", - "@babel/helper-validator-option": "^7.27.1", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.28.5", - "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", - "@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": "^7.29.3", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.6", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.7.tgz", + "integrity": "sha512-GYzX36n1nsciIb0uyH0GHwxwtNwPQIcpxSeiVLDtG/B7jB5xXgchnmL1f/jCX5o+pwnaDBtO60ONSJhEBJfxYA==", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.29.7", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.29.7", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.29.7", + "@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": "^7.29.7", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.29.7", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.29.7", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-import-assertions": "^7.28.6", - "@babel/plugin-syntax-import-attributes": "^7.28.6", + "@babel/plugin-syntax-import-assertions": "^7.29.7", + "@babel/plugin-syntax-import-attributes": "^7.29.7", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.27.1", - "@babel/plugin-transform-async-generator-functions": "^7.29.0", - "@babel/plugin-transform-async-to-generator": "^7.28.6", - "@babel/plugin-transform-block-scoped-functions": "^7.27.1", - "@babel/plugin-transform-block-scoping": "^7.28.6", - "@babel/plugin-transform-class-properties": "^7.28.6", - "@babel/plugin-transform-class-static-block": "^7.28.6", - "@babel/plugin-transform-classes": "^7.28.6", - "@babel/plugin-transform-computed-properties": "^7.28.6", - "@babel/plugin-transform-destructuring": "^7.28.5", - "@babel/plugin-transform-dotall-regex": "^7.28.6", - "@babel/plugin-transform-duplicate-keys": "^7.27.1", - "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.29.0", - "@babel/plugin-transform-dynamic-import": "^7.27.1", - "@babel/plugin-transform-explicit-resource-management": "^7.28.6", - "@babel/plugin-transform-exponentiation-operator": "^7.28.6", - "@babel/plugin-transform-export-namespace-from": "^7.27.1", - "@babel/plugin-transform-for-of": "^7.27.1", - "@babel/plugin-transform-function-name": "^7.27.1", - "@babel/plugin-transform-json-strings": "^7.28.6", - "@babel/plugin-transform-literals": "^7.27.1", - "@babel/plugin-transform-logical-assignment-operators": "^7.28.6", - "@babel/plugin-transform-member-expression-literals": "^7.27.1", - "@babel/plugin-transform-modules-amd": "^7.27.1", - "@babel/plugin-transform-modules-commonjs": "^7.28.6", - "@babel/plugin-transform-modules-systemjs": "^7.29.4", - "@babel/plugin-transform-modules-umd": "^7.27.1", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.29.0", - "@babel/plugin-transform-new-target": "^7.27.1", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.28.6", - "@babel/plugin-transform-numeric-separator": "^7.28.6", - "@babel/plugin-transform-object-rest-spread": "^7.28.6", - "@babel/plugin-transform-object-super": "^7.27.1", - "@babel/plugin-transform-optional-catch-binding": "^7.28.6", - "@babel/plugin-transform-optional-chaining": "^7.28.6", - "@babel/plugin-transform-parameters": "^7.27.7", - "@babel/plugin-transform-private-methods": "^7.28.6", - "@babel/plugin-transform-private-property-in-object": "^7.28.6", - "@babel/plugin-transform-property-literals": "^7.27.1", - "@babel/plugin-transform-regenerator": "^7.29.0", - "@babel/plugin-transform-regexp-modifiers": "^7.28.6", - "@babel/plugin-transform-reserved-words": "^7.27.1", - "@babel/plugin-transform-shorthand-properties": "^7.27.1", - "@babel/plugin-transform-spread": "^7.28.6", - "@babel/plugin-transform-sticky-regex": "^7.27.1", - "@babel/plugin-transform-template-literals": "^7.27.1", - "@babel/plugin-transform-typeof-symbol": "^7.27.1", - "@babel/plugin-transform-unicode-escapes": "^7.27.1", - "@babel/plugin-transform-unicode-property-regex": "^7.28.6", - "@babel/plugin-transform-unicode-regex": "^7.27.1", - "@babel/plugin-transform-unicode-sets-regex": "^7.28.6", + "@babel/plugin-transform-arrow-functions": "^7.29.7", + "@babel/plugin-transform-async-generator-functions": "^7.29.7", + "@babel/plugin-transform-async-to-generator": "^7.29.7", + "@babel/plugin-transform-block-scoped-functions": "^7.29.7", + "@babel/plugin-transform-block-scoping": "^7.29.7", + "@babel/plugin-transform-class-properties": "^7.29.7", + "@babel/plugin-transform-class-static-block": "^7.29.7", + "@babel/plugin-transform-classes": "^7.29.7", + "@babel/plugin-transform-computed-properties": "^7.29.7", + "@babel/plugin-transform-destructuring": "^7.29.7", + "@babel/plugin-transform-dotall-regex": "^7.29.7", + "@babel/plugin-transform-duplicate-keys": "^7.29.7", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.29.7", + "@babel/plugin-transform-dynamic-import": "^7.29.7", + "@babel/plugin-transform-explicit-resource-management": "^7.29.7", + "@babel/plugin-transform-exponentiation-operator": "^7.29.7", + "@babel/plugin-transform-export-namespace-from": "^7.29.7", + "@babel/plugin-transform-for-of": "^7.29.7", + "@babel/plugin-transform-function-name": "^7.29.7", + "@babel/plugin-transform-json-strings": "^7.29.7", + "@babel/plugin-transform-literals": "^7.29.7", + "@babel/plugin-transform-logical-assignment-operators": "^7.29.7", + "@babel/plugin-transform-member-expression-literals": "^7.29.7", + "@babel/plugin-transform-modules-amd": "^7.29.7", + "@babel/plugin-transform-modules-commonjs": "^7.29.7", + "@babel/plugin-transform-modules-systemjs": "^7.29.7", + "@babel/plugin-transform-modules-umd": "^7.29.7", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.29.7", + "@babel/plugin-transform-new-target": "^7.29.7", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.29.7", + "@babel/plugin-transform-numeric-separator": "^7.29.7", + "@babel/plugin-transform-object-rest-spread": "^7.29.7", + "@babel/plugin-transform-object-super": "^7.29.7", + "@babel/plugin-transform-optional-catch-binding": "^7.29.7", + "@babel/plugin-transform-optional-chaining": "^7.29.7", + "@babel/plugin-transform-parameters": "^7.29.7", + "@babel/plugin-transform-private-methods": "^7.29.7", + "@babel/plugin-transform-private-property-in-object": "^7.29.7", + "@babel/plugin-transform-property-literals": "^7.29.7", + "@babel/plugin-transform-regenerator": "^7.29.7", + "@babel/plugin-transform-regexp-modifiers": "^7.29.7", + "@babel/plugin-transform-reserved-words": "^7.29.7", + "@babel/plugin-transform-shorthand-properties": "^7.29.7", + "@babel/plugin-transform-spread": "^7.29.7", + "@babel/plugin-transform-sticky-regex": "^7.29.7", + "@babel/plugin-transform-template-literals": "^7.29.7", + "@babel/plugin-transform-typeof-symbol": "^7.29.7", + "@babel/plugin-transform-unicode-escapes": "^7.29.7", + "@babel/plugin-transform-unicode-property-regex": "^7.29.7", + "@babel/plugin-transform-unicode-regex": "^7.29.7", + "@babel/plugin-transform-unicode-sets-regex": "^7.29.7", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.15", "babel-plugin-polyfill-corejs3": "^0.14.0", @@ -1884,17 +1884,17 @@ } }, "node_modules/@babel/preset-react": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.28.5.tgz", - "integrity": "sha512-Z3J8vhRq7CeLjdC58jLv4lnZ5RKFUJWqH5emvxmv9Hv3BD1T9R/Im713R4MTKwvFaV74ejZ3sM01LyEKk4ugNQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.29.7.tgz", + "integrity": "sha512-C+PV1TFUPTmBQGoPBL8j2QmLpZ117YTCwxIZeJOM96GbYMFSc7/pOXU5lVykwnZxyTqQxRsvoRk6f2FktZgGHA==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-validator-option": "^7.27.1", - "@babel/plugin-transform-react-display-name": "^7.28.0", - "@babel/plugin-transform-react-jsx": "^7.27.1", - "@babel/plugin-transform-react-jsx-development": "^7.27.1", - "@babel/plugin-transform-react-pure-annotations": "^7.27.1" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "@babel/plugin-transform-react-display-name": "^7.29.7", + "@babel/plugin-transform-react-jsx": "^7.29.7", + "@babel/plugin-transform-react-jsx-development": "^7.29.7", + "@babel/plugin-transform-react-pure-annotations": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1904,16 +1904,16 @@ } }, "node_modules/@babel/preset-typescript": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.28.5.tgz", - "integrity": "sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.29.7.tgz", + "integrity": "sha512-/Foi8vKY2EVbed/1eZx0gJEEwHAIxogrySI7rULcRIvhZzbvoE/b5qG5Ghc0WKAFKOHA9SD1x7RsFlOYdutIiQ==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-validator-option": "^7.27.1", - "@babel/plugin-syntax-jsx": "^7.27.1", - "@babel/plugin-transform-modules-commonjs": "^7.27.1", - "@babel/plugin-transform-typescript": "^7.28.5" + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "@babel/plugin-syntax-jsx": "^7.29.7", + "@babel/plugin-transform-modules-commonjs": "^7.29.7", + "@babel/plugin-transform-typescript": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -1923,40 +1923,40 @@ } }, "node_modules/@babel/runtime": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", - "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/template": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", - "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/parser": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", - "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", + "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0", + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7", "debug": "^4.3.1" }, "engines": { @@ -1964,13 +1964,13 @@ } }, "node_modules/@babel/types": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", - "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -4303,13 +4303,13 @@ } }, "node_modules/@jsonjoy.com/fs-core": { - "version": "4.57.2", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-core/-/fs-core-4.57.2.tgz", - "integrity": "sha512-SVjwklkpIV5wrynpYtuYnfYH1QF4/nDuLBX7VXdb+3miglcAgBVZb/5y0cOsehRV/9Vb+3UqhkMq3/NR3ztdkQ==", + "version": "4.57.6", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-core/-/fs-core-4.57.6.tgz", + "integrity": "sha512-uI++Wx6VkBJqVmkb4ZeExwAVpZiA2Do5NrEtXoDk0Pdvce3ytFXJoviT1sLOj16+qDIMnD5nWPfOhVpnDmRJKg==", "license": "Apache-2.0", "dependencies": { - "@jsonjoy.com/fs-node-builtins": "4.57.2", - "@jsonjoy.com/fs-node-utils": "4.57.2", + "@jsonjoy.com/fs-node-builtins": "4.57.6", + "@jsonjoy.com/fs-node-utils": "4.57.6", "thingies": "^2.5.0" }, "engines": { @@ -4324,14 +4324,14 @@ } }, "node_modules/@jsonjoy.com/fs-fsa": { - "version": "4.57.2", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-fsa/-/fs-fsa-4.57.2.tgz", - "integrity": "sha512-fhO8+iR2I+OCw668ISDJdn1aArc9zx033sWejIyzQ8RBeXa9bDSaUeA3ix0poYOfrj1KdOzytmYNv2/uLDfV6g==", + "version": "4.57.6", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-fsa/-/fs-fsa-4.57.6.tgz", + "integrity": "sha512-pKkw/yC5CzSZKhIIUIsH1przOa+K5jGmZIg1sWaSF24JojyrUFbjcQv7QrcGAudriei6HQ6R0BFj+V8NbQinJw==", "license": "Apache-2.0", "dependencies": { - "@jsonjoy.com/fs-core": "4.57.2", - "@jsonjoy.com/fs-node-builtins": "4.57.2", - "@jsonjoy.com/fs-node-utils": "4.57.2", + "@jsonjoy.com/fs-core": "4.57.6", + "@jsonjoy.com/fs-node-builtins": "4.57.6", + "@jsonjoy.com/fs-node-utils": "4.57.6", "thingies": "^2.5.0" }, "engines": { @@ -4346,16 +4346,16 @@ } }, "node_modules/@jsonjoy.com/fs-node": { - "version": "4.57.2", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node/-/fs-node-4.57.2.tgz", - "integrity": "sha512-nX2AdL6cOFwLdju9G4/nbRnYevmCJbh7N7hvR3gGm97Cs60uEjyd0rpR+YBS7cTg175zzl22pGKXR5USaQMvKg==", + "version": "4.57.6", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node/-/fs-node-4.57.6.tgz", + "integrity": "sha512-Kbn1jdkvDN4F2+BhoB6mMu7NCbhP0bgA5NcI1aJj/Q5UcU+I1JLLW+dEQean33iV4tXv35AzBVKPICnDltBpxw==", "license": "Apache-2.0", "dependencies": { - "@jsonjoy.com/fs-core": "4.57.2", - "@jsonjoy.com/fs-node-builtins": "4.57.2", - "@jsonjoy.com/fs-node-utils": "4.57.2", - "@jsonjoy.com/fs-print": "4.57.2", - "@jsonjoy.com/fs-snapshot": "4.57.2", + "@jsonjoy.com/fs-core": "4.57.6", + "@jsonjoy.com/fs-node-builtins": "4.57.6", + "@jsonjoy.com/fs-node-utils": "4.57.6", + "@jsonjoy.com/fs-print": "4.57.6", + "@jsonjoy.com/fs-snapshot": "4.57.6", "glob-to-regex.js": "^1.0.0", "thingies": "^2.5.0" }, @@ -4371,9 +4371,9 @@ } }, "node_modules/@jsonjoy.com/fs-node-builtins": { - "version": "4.57.2", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-builtins/-/fs-node-builtins-4.57.2.tgz", - "integrity": "sha512-xhiegylRmhw43Ki2HO1ZBL7DQ5ja/qpRsL29VtQ2xuUHiuDGbgf2uD4p9Qd8hJI5P6RCtGYD50IXHXVq/Ocjcg==", + "version": "4.57.6", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-builtins/-/fs-node-builtins-4.57.6.tgz", + "integrity": "sha512-V4DgEFT3Cg5S9fCMOZSCVdTxdJWWLBO0WnAazV7hnCM96u5zXHyW/ubDAfcSVwqjkMJ50W1Y44IXtxRoIwaCVg==", "license": "Apache-2.0", "engines": { "node": ">=10.0" @@ -4387,14 +4387,14 @@ } }, "node_modules/@jsonjoy.com/fs-node-to-fsa": { - "version": "4.57.2", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-to-fsa/-/fs-node-to-fsa-4.57.2.tgz", - "integrity": "sha512-18LmWTSONhoAPW+IWRuf8w/+zRolPFGPeGwMxlAhhfY11EKzX+5XHDBPAw67dBF5dxDErHJbl40U+3IXSDRXSQ==", + "version": "4.57.6", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-to-fsa/-/fs-node-to-fsa-4.57.6.tgz", + "integrity": "sha512-+JptNw3iifihxH2rEXrninDzX4FFVW8JD/wPR8GbJPAeL9CQUSblrlumOPB5gZuS7tYRX+PJPLtT7XzKoRhv/Q==", "license": "Apache-2.0", "dependencies": { - "@jsonjoy.com/fs-fsa": "4.57.2", - "@jsonjoy.com/fs-node-builtins": "4.57.2", - "@jsonjoy.com/fs-node-utils": "4.57.2" + "@jsonjoy.com/fs-fsa": "4.57.6", + "@jsonjoy.com/fs-node-builtins": "4.57.6", + "@jsonjoy.com/fs-node-utils": "4.57.6" }, "engines": { "node": ">=10.0" @@ -4408,12 +4408,12 @@ } }, "node_modules/@jsonjoy.com/fs-node-utils": { - "version": "4.57.2", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-utils/-/fs-node-utils-4.57.2.tgz", - "integrity": "sha512-rsPSJgekz43IlNbLyAM/Ab+ouYLWGp5DDBfYBNNEqDaSpsbXfthBn29Q4muFA9L0F+Z3mKo+CWlgSCXrf+mOyQ==", + "version": "4.57.6", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-utils/-/fs-node-utils-4.57.6.tgz", + "integrity": "sha512-foyUrfS7WmYEUzqYXSNxmJBcSj04TABrkpFabwO9SCDCpVCfJ+qG+2sk5FjfiflG2n0SDFZDCJ6vYlJAEpxJFg==", "license": "Apache-2.0", "dependencies": { - "@jsonjoy.com/fs-node-builtins": "4.57.2" + "@jsonjoy.com/fs-node-builtins": "4.57.6" }, "engines": { "node": ">=10.0" @@ -4427,12 +4427,12 @@ } }, "node_modules/@jsonjoy.com/fs-print": { - "version": "4.57.2", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-print/-/fs-print-4.57.2.tgz", - "integrity": "sha512-wK9NSow48i4DbDl9F1CQE5TqnyZOJ04elU3WFG5aJ76p+YxO/ulyBBQvKsessPxdo381Bc2pcEoyPujMOhcRqQ==", + "version": "4.57.6", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-print/-/fs-print-4.57.6.tgz", + "integrity": "sha512-96eAn4Dudtt67LTeuU47yUD+pg9/G/oKpI10zei9ljk3X3WK4lYKc+n3cpaPCAbKPzoyfxl0mXm8f8Y7BOSFXw==", "license": "Apache-2.0", "dependencies": { - "@jsonjoy.com/fs-node-utils": "4.57.2", + "@jsonjoy.com/fs-node-utils": "4.57.6", "tree-dump": "^1.1.0" }, "engines": { @@ -4447,13 +4447,13 @@ } }, "node_modules/@jsonjoy.com/fs-snapshot": { - "version": "4.57.2", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-snapshot/-/fs-snapshot-4.57.2.tgz", - "integrity": "sha512-GdduDZuoP5V/QCgJkx9+BZ6SC0EZ/smXAdTS7PfMqgMTGXLlt/bH/FqMYaqB9JmLf05sJPtO0XRbAwwkEEPbVw==", + "version": "4.57.6", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-snapshot/-/fs-snapshot-4.57.6.tgz", + "integrity": "sha512-V57CMzbOgTzUWGOWQ8GzHQdpJP6JnrYVNCtTBNxVYEnlVRvo4uEJqHhtAT8vhDFrIuJOXLrTL1Fki4h5oI7xxg==", "license": "Apache-2.0", "dependencies": { "@jsonjoy.com/buffers": "^17.65.0", - "@jsonjoy.com/fs-node-utils": "4.57.2", + "@jsonjoy.com/fs-node-utils": "4.57.6", "@jsonjoy.com/json-pack": "^17.65.0", "@jsonjoy.com/util": "^17.65.0" }, @@ -6276,9 +6276,9 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "19.2.15", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.15.tgz", - "integrity": "sha512-eRwcGNHve+E8qtEQSSRl6urh+rFop4v8gm6O8rGv25CodbvFdLjA1vVQ1KkiFE0w0UPOnb8tDiFKL5lp0rtY5Q==", + "version": "19.2.16", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.16.tgz", + "integrity": "sha512-esJiCAnl0kfpNdE69f3So4WJUXy95dLZydX0KwK46riIHDzHM7O9Vtf9xCHW0PXIqvgqNrswl522kA/5yx+F4w==", "license": "MIT", "dependencies": { "csstype": "^3.2.2" @@ -6729,25 +6729,25 @@ } }, "node_modules/algoliasearch": { - "version": "5.52.1", - "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.52.1.tgz", - "integrity": "sha512-fHA8+kXTbjagw3jkLiaS7KKrH8qe2DyOsiUhGlN4cdT77PEsfqXZl7ewDk1hsg+pJnPlnE50XtLxjR91iJOpmg==", - "license": "MIT", - "dependencies": { - "@algolia/abtesting": "1.18.1", - "@algolia/client-abtesting": "5.52.1", - "@algolia/client-analytics": "5.52.1", - "@algolia/client-common": "5.52.1", - "@algolia/client-insights": "5.52.1", - "@algolia/client-personalization": "5.52.1", - "@algolia/client-query-suggestions": "5.52.1", - "@algolia/client-search": "5.52.1", - "@algolia/ingestion": "1.52.1", - "@algolia/monitoring": "1.52.1", - "@algolia/recommend": "5.52.1", - "@algolia/requester-browser-xhr": "5.52.1", - "@algolia/requester-fetch": "5.52.1", - "@algolia/requester-node-http": "5.52.1" + "version": "5.53.0", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.53.0.tgz", + "integrity": "sha512-OGW1q6b91CRSSeiOnM8LxuR5NYJ2esvw66jUZ4IIvdv+ItNkx3pwLuyR+jaCdbGee4ov5WgUnyPryyh11xvByQ==", + "license": "MIT", + "dependencies": { + "@algolia/abtesting": "1.19.0", + "@algolia/client-abtesting": "5.53.0", + "@algolia/client-analytics": "5.53.0", + "@algolia/client-common": "5.53.0", + "@algolia/client-insights": "5.53.0", + "@algolia/client-personalization": "5.53.0", + "@algolia/client-query-suggestions": "5.53.0", + "@algolia/client-search": "5.53.0", + "@algolia/ingestion": "1.53.0", + "@algolia/monitoring": "1.53.0", + "@algolia/recommend": "5.53.0", + "@algolia/requester-browser-xhr": "5.53.0", + "@algolia/requester-fetch": "5.53.0", + "@algolia/requester-node-http": "5.53.0" }, "engines": { "node": ">= 14.0.0" @@ -7029,9 +7029,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.10.32", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.32.tgz", - "integrity": "sha512-wbPvpyjJPC0zdfdKXxqEL3Ea+bOMD/87X4lftiJkkaBiuG6ALQy1SLmEd7BSmVCuwCQsBrCamgBoLyfFDD1EPg==", + "version": "2.10.33", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.33.tgz", + "integrity": "sha512-bA6+tcSLpz2tIEdDXZPpPTIuxBcC4+w6SieaYyfigIa4h8GlFxbA17v22Vx3JUtuZQj9SgOsnbK+aTBzyDyEuw==", "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.cjs" @@ -7154,9 +7154,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", + "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -8863,9 +8863,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.361", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.361.tgz", - "integrity": "sha512-Q6Hts7N9FnJc5LeGRINFvLhCI9xZmNtTDe5ZbcVezQz7cU4a8Aua3GH1b8J2XY8Al9PF+OCwYqhgsOOheMdvkA==", + "version": "1.5.367", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.367.tgz", + "integrity": "sha512-4Mk/mrynCNQ+atY40D3UpmhLWB6AHMbYMlIrPhHcMF6x0L7O0b052FCAsxw1LlaR++UFuNg3D/A6XCuGDa0guQ==", "license": "ISC" }, "node_modules/emoji-regex": { @@ -8909,9 +8909,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.22.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.22.0.tgz", - "integrity": "sha512-xYcDWrpELkFzz9SpZ3PlI6Eu6eD93Yf0WLDRxikGhWJ3MAir2SNZTIVCVZqZ/NUyx8AdMc2gT9C0gPiw18kG+A==", + "version": "5.22.2", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.22.2.tgz", + "integrity": "sha512-0rxICaFZ7NQho/sHely2bvOPRP0Eu2B0NZ9zM54YvRvWMn7jfz3DmnOZDR9LlXDdDcqntAVc6Hfy4gr/tdH/Ag==", "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", @@ -10042,9 +10042,9 @@ } }, "node_modules/hasown": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", - "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -11101,9 +11101,19 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", + "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -11209,13 +11219,13 @@ } }, "node_modules/launch-editor": { - "version": "2.13.2", - "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.13.2.tgz", - "integrity": "sha512-4VVDnbOpLXy/s8rdRCSXb+zfMeFR0WlJWpET1iA9CQdlZDfwyLjUuGQzXU4VeOoey6AicSAluWan7Etga6Kcmg==", + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.14.1.tgz", + "integrity": "sha512-QWBrQsMpH7gPr965dsKD/3cKWiNoTjpATQf++Xq63N6sKRGMwlVXz41O1IZTMfZQgBctD/K5Zt06+/I6pP6+HA==", "license": "MIT", "dependencies": { "picocolors": "^1.1.1", - "shell-quote": "^1.8.3" + "shell-quote": "^1.8.4" } }, "node_modules/leven": { @@ -12073,19 +12083,19 @@ } }, "node_modules/memfs": { - "version": "4.57.2", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.57.2.tgz", - "integrity": "sha512-2nWzSsJzrukurSDna4Z0WywuScK4Id3tSKejgu74u8KCdW4uNrseKRSIDg75C6Yw5ZRqBe0F0EtMNlTbUq8bAQ==", + "version": "4.57.6", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.57.6.tgz", + "integrity": "sha512-WQK+DGjKCnPdpSyJUXphz+COF2uEhhsxQ3VIWBSbzpbbXuch3h4FePMqXrXGdLjsTgo4JFzBFsP6AWd9pVazGw==", "license": "Apache-2.0", "dependencies": { - "@jsonjoy.com/fs-core": "4.57.2", - "@jsonjoy.com/fs-fsa": "4.57.2", - "@jsonjoy.com/fs-node": "4.57.2", - "@jsonjoy.com/fs-node-builtins": "4.57.2", - "@jsonjoy.com/fs-node-to-fsa": "4.57.2", - "@jsonjoy.com/fs-node-utils": "4.57.2", - "@jsonjoy.com/fs-print": "4.57.2", - "@jsonjoy.com/fs-snapshot": "4.57.2", + "@jsonjoy.com/fs-core": "4.57.6", + "@jsonjoy.com/fs-fsa": "4.57.6", + "@jsonjoy.com/fs-node": "4.57.6", + "@jsonjoy.com/fs-node-builtins": "4.57.6", + "@jsonjoy.com/fs-node-to-fsa": "4.57.6", + "@jsonjoy.com/fs-node-utils": "4.57.6", + "@jsonjoy.com/fs-print": "4.57.6", + "@jsonjoy.com/fs-snapshot": "4.57.6", "@jsonjoy.com/json-pack": "^1.11.0", "@jsonjoy.com/util": "^1.9.0", "glob-to-regex.js": "^1.0.1", @@ -14119,9 +14129,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.46", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.46.tgz", - "integrity": "sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ==", + "version": "2.0.47", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.47.tgz", + "integrity": "sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==", "license": "MIT", "engines": { "node": ">=18" @@ -16251,9 +16261,9 @@ } }, "node_modules/property-information": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", - "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.2.0.tgz", + "integrity": "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==", "license": "MIT", "funding": { "type": "github", @@ -16450,24 +16460,24 @@ } }, "node_modules/react": { - "version": "19.2.6", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.6.tgz", - "integrity": "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q==", + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz", + "integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==", "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "19.2.6", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.6.tgz", - "integrity": "sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g==", + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz", + "integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==", "license": "MIT", "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { - "react": "^19.2.6" + "react": "^19.2.7" } }, "node_modules/react-fast-compare": { @@ -18131,9 +18141,9 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.6.0.tgz", - "integrity": "sha512-Eum+5ajkaOhf5KbM26osvv21kLD7BaGqQ1UA4Ami4arYwylmGUQTgHFpHDdmJod1q4QXa66p0to/FBKID+J1vA==", + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.6.1.tgz", + "integrity": "sha512-201R5j+sJpK8nFWwKVyNfZot8FaJbLZDq5evriVzbV1wDtSXDjRUDRfJzHpAaxFDMEhsZL1QkeqM61wgsS3KaQ==", "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", @@ -18941,9 +18951,9 @@ } }, "node_modules/webpack": { - "version": "5.107.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.107.1.tgz", - "integrity": "sha512-mvdIWxj/H6QsfgDdH9djne3a5dYcmEmtsXGESkypaGN5jXjF/b+9KDlmTDQ2TKlFUeA2fI9Y65kihD30JOdB+Q==", + "version": "5.107.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.107.2.tgz", + "integrity": "sha512-v7RhXaJbpMlV0D7hC7lb2EbnxkoeUqf9qhKr6lozx3Q48pmFrqqNRmZFUEGmi7pSwm6fCQ2H1IjvCkHqdpVdjQ==", "license": "MIT", "dependencies": { "@types/estree": "^1.0.8", @@ -18955,7 +18965,7 @@ "acorn-import-phases": "^1.0.3", "browserslist": "^4.28.1", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.21.4", + "enhanced-resolve": "^5.22.0", "es-module-lexer": "^2.1.0", "eslint-scope": "5.1.1", "events": "^3.2.0", @@ -18968,7 +18978,7 @@ "tapable": "^2.3.0", "terser-webpack-plugin": "^5.5.0", "watchpack": "^2.5.1", - "webpack-sources": "^3.4.1" + "webpack-sources": "^3.5.0" }, "bin": { "webpack": "bin/webpack.js" diff --git a/docs/package.json b/docs/package.json index 2708032..c6a0b29 100644 --- a/docs/package.json +++ b/docs/package.json @@ -15,7 +15,7 @@ }, "dependencies": { "@docusaurus/core": "3.10.1", - "@docusaurus/faster": "^3.10.1", + "@docusaurus/faster": "3.10.1", "@docusaurus/preset-classic": "3.10.1", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", diff --git a/docs/sidebars.js b/docs/sidebars.js index 24e6867..de6ffd6 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -15,9 +15,10 @@ @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ const sidebars = { - // By default, Docusaurus generates a sidebar from the docs folder structure docsSidebar: [{type: 'autogenerated', dirName: '.'}], + // tutorialSidebar: [{type: 'autogenerated', dirName: '.'}], + // But you can create a sidebar manually /* tutorialSidebar: [ diff --git a/docs/src/pages/markdown-page.md b/docs/src/pages/markdown-page.mdx similarity index 100% rename from docs/src/pages/markdown-page.md rename to docs/src/pages/markdown-page.mdx diff --git a/docs/versioned_docs/version-3.x/index.md b/docs/versioned_docs/version-3.x/index.md index b90fc6d..78a9f6b 100644 --- a/docs/versioned_docs/version-3.x/index.md +++ b/docs/versioned_docs/version-3.x/index.md @@ -5,7 +5,7 @@ title: FluentHttpClient FluentHttpClient exposes a set of extensions methods to make sending REST requests with `HttpClient` both readable and chainable. -:::info Legacy versions +:::info[Legacy versions] These docs describe FluentHttpClient 3.x and largely apply to 2.x as well. Where there are differences, they are called out explicitly. diff --git a/docs/versioned_docs/version-4.x/configure-request.md b/docs/versioned_docs/version-4.x/configure-request.md index e5184e6..5927dff 100644 --- a/docs/versioned_docs/version-4.x/configure-request.md +++ b/docs/versioned_docs/version-4.x/configure-request.md @@ -156,7 +156,7 @@ var order = new Order { Id = 1001, Amount = 49.99M }; request.WithXmlContent(order); ``` -:::important XML Serialization +:::important[XML Serialization] If the object serialization does not serialize to your satisfaction, serialize the object yourself, then pass the raw XML as a string instead of the object. diff --git a/docs/versioned_docs/version-4.x/httprequestbuilder.md b/docs/versioned_docs/version-4.x/httprequestbuilder.md index 253bdd4..d865baa 100644 --- a/docs/versioned_docs/version-4.x/httprequestbuilder.md +++ b/docs/versioned_docs/version-4.x/httprequestbuilder.md @@ -7,7 +7,7 @@ The `HttpRequestBuilder` class provides a fluent, flexible way to construct and None of the properties is required as long as the provided `HttpClient` instance knows where to send the request. -:::important Recommendation +:::important[Recommendation] Wherever possible, prefer configuring properties and calling methods through the **FluentHttpClient** extension methods, rather than modifying `HttpRequestBuilder` directly. ::: diff --git a/docs/versioned_docs/version-4.x/response-deserialization-json.md b/docs/versioned_docs/version-4.x/response-deserialization-json.md index 6a0c410..ea0178f 100644 --- a/docs/versioned_docs/version-4.x/response-deserialization-json.md +++ b/docs/versioned_docs/version-4.x/response-deserialization-json.md @@ -129,7 +129,7 @@ FluentHttpClientOptions.DefaultJsonSerializerOptions = new JsonSerializerOptions }; ``` -:::danger Global Impact +:::danger[Global Impact] This is a static setting. Changing it affects every call that relies on the defaults across your process. Prefer passing explicit options to a single call when you need per-request behavior. diff --git a/docs/versioned_docs/version-4.x/response-handling.md b/docs/versioned_docs/version-4.x/response-handling.md index ab04dd5..09ed28d 100644 --- a/docs/versioned_docs/version-4.x/response-handling.md +++ b/docs/versioned_docs/version-4.x/response-handling.md @@ -58,7 +58,7 @@ Streaming is ideal for large downloads or scenarios where you want to process da These methods make it simple to define behavior for successful or failed HTTP responses without complex conditionals. -:::danger Avoid Reading The Response Stream +:::danger[Avoid Reading The Response Stream] Avoid reading or consuming the response content (e.g., `GetResponseStringAsync()`, `GetResponseBytesAsync()`, etc.) inside these handlers unless you are certain the response will not be processed elsewhere. Once the response body is read, the underlying stream is consumed, which means later attempts to access the content will return empty results or throw exceptions. diff --git a/src/.config/dotnet-tools.json b/src/.config/dotnet-tools.json index 89b09ab..9288b0e 100644 --- a/src/.config/dotnet-tools.json +++ b/src/.config/dotnet-tools.json @@ -3,11 +3,10 @@ "isRoot": true, "tools": { "dotnet-reportgenerator-globaltool": { - "version": "5.4.11", + "version": "5.5.10", "commands": [ "reportgenerator" - ], - "rollForward": false + ] } } -} +} \ No newline at end of file diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 26f6094..80b0e01 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -1,9 +1,10 @@ - - - - - all - 3.6.143 - - + + + + true + $(MSBuildThisFileDirectory)FluentHttpClient.snk + true + 0024000004800000940000000602000000240000525341310004000001000100e1adf9793254ed4e9a9068dbcbb2d9f062ca131bca90fedd7b1f0b5aecb4a6561d32f674c3c459b3aa910a43bede8e8ac5953e03ac29209aec1f3b8d5a112382ad517e3dacc2872cb8444552bd70a41420e93ddfd75b208ab2af3a11bddf5ecdf1e26f0a8f9d0e58d45ec359e3debedb55fa62e66f190e21995be769ba67aeae + + \ No newline at end of file diff --git a/src/FluentHttpClient.Tests/BuilderFactoryTests.cs b/src/FluentHttpClient.Tests/BuilderFactoryTests.cs new file mode 100644 index 0000000..911a96d --- /dev/null +++ b/src/FluentHttpClient.Tests/BuilderFactoryTests.cs @@ -0,0 +1,98 @@ +namespace FluentHttpClient.Tests; + +public class BuilderFactoryTests +{ + [Fact] + public void Create_FromClient_ReturnsBuilderInitializedWithClient() + { + using var client = new HttpClient(); + + var builder = BuilderFactory.Create(client); + + builder.ShouldBeOfType(); + builder.CtorClient.ShouldBeSameAs(client); + } + + [Fact] + public void Create_FromRoute_ReturnsBuilderInitializedWithClientAndRoute() + { + using var client = new HttpClient(); + + var builder = BuilderFactory.Create(client, "api/widgets"); + + builder.ShouldBeOfType(); + builder.CtorClient.ShouldBeSameAs(client); + builder.CtorRoute.ShouldBe("api/widgets"); + } + + [Fact] + public void Create_FromUri_ReturnsBuilderInitializedWithClientAndUri() + { + using var client = new HttpClient(); + var uri = new Uri("https://example.com/api/widgets"); + + var builder = BuilderFactory.Create(client, uri); + + builder.ShouldBeOfType(); + builder.CtorClient.ShouldBeSameAs(client); + builder.CtorUri.ShouldBe(uri); + } + + [Fact] + public void StaticInit_MissingConstructor_ThrowsWithDescriptiveMessage() + { + using var client = new HttpClient(); + + var ex = Should.Throw( + () => BuilderFactory.Create(client)); + + var inner = ex.InnerException.ShouldBeOfType(); + inner.Message.ShouldContain(nameof(MissingCtorBuilder)); + inner.Message.ShouldContain("HttpClient"); + } + + [Fact] + public void StaticInit_PrivateConstructor_ThrowsWithDescriptiveMessage() + { + using var client = new HttpClient(); + + var ex = Should.Throw( + () => BuilderFactory.Create(client)); + + var inner = ex.InnerException.ShouldBeOfType(); + inner.Message.ShouldContain(nameof(PrivateCtorBuilder)); + } +} + +internal class ValidBuilder : HttpRequestBuilder +{ + public HttpClient CtorClient { get; } + public string? CtorRoute { get; } + public Uri? CtorUri { get; } + + public ValidBuilder(HttpClient client) : base(client) + => CtorClient = client; + + public ValidBuilder(HttpClient client, string route) : base(client, route) + => (CtorClient, CtorRoute) = (client, route); + + public ValidBuilder(HttpClient client, Uri uri) : base(client, uri) + => (CtorClient, CtorUri) = (client, uri); +} + +internal class MissingCtorBuilder : HttpRequestBuilder +{ + // No public constructors matching any of the three required signatures. + // The static constructor of BuilderFactory will fail + // on the first ResolveCtor call (HttpClient). + private MissingCtorBuilder(HttpClient client) : base(client) { } +} + +internal class PrivateCtorBuilder : HttpRequestBuilder +{ + // All three signatures present but private, so GetConstructor with + // BindingFlags.Public will not find them. + private PrivateCtorBuilder(HttpClient client) : base(client) { } + private PrivateCtorBuilder(HttpClient client, string route) : base(client, route) { } + private PrivateCtorBuilder(HttpClient client, Uri uri) : base(client, uri) { } +} \ No newline at end of file diff --git a/src/FluentHttpClient.Tests/FluentHttpClient.Tests.csproj b/src/FluentHttpClient.Tests/FluentHttpClient.Tests.csproj index b67676e..89684a9 100644 --- a/src/FluentHttpClient.Tests/FluentHttpClient.Tests.csproj +++ b/src/FluentHttpClient.Tests/FluentHttpClient.Tests.csproj @@ -6,9 +6,8 @@ enable false true - ..\FluentHttpClient\FluentHttpClient.snk - + diff --git a/src/FluentHttpClient/FluentHttpClient.snk b/src/FluentHttpClient.snk similarity index 100% rename from src/FluentHttpClient/FluentHttpClient.snk rename to src/FluentHttpClient.snk diff --git a/src/FluentHttpClient/AssemblyInfo.cs b/src/FluentHttpClient/AssemblyInfo.cs deleted file mode 100644 index 65cc83a..0000000 --- a/src/FluentHttpClient/AssemblyInfo.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// In SDK-style projects such as this one, several assembly attributes that were historically -// defined in this file are now automatically added during build and populated with -// values defined in project properties. For details of which attributes are included -// and how to customize this process see: https://aka.ms/assembly-info-properties - -// Setting ComVisible to false makes the types in this assembly not visible to COM -// components. If you need to access a type in this assembly from COM, set the ComVisible -// attribute to true on that type. -[assembly: ComVisible(false)] - -// Identify the assembly CLS-compliance level -[assembly: CLSCompliant(true)] - -// InternalsVisibleTo attribute is used to specify that the internal types of this assembly -// are visible to another assembly. This is often used for unit testing purposes. -// The specified assembly name must match the name of the assembly that will access the internal types. -// See https://learn.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.internalsvisibletoattribute -[assembly: InternalsVisibleTo("FluentHttpClient.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e1adf9793254ed4e9a9068dbcbb2d9f062ca131bca90fedd7b1f0b5aecb4a6561d32f674c3c459b3aa910a43bede8e8ac5953e03ac29209aec1f3b8d5a112382ad517e3dacc2872cb8444552bd70a41420e93ddfd75b208ab2af3a11bddf5ecdf1e26f0a8f9d0e58d45ec359e3debedb55fa62e66f190e21995be769ba67aeae")] diff --git a/src/FluentHttpClient/BuilderFactory.cs b/src/FluentHttpClient/BuilderFactory.cs new file mode 100644 index 0000000..1a34747 --- /dev/null +++ b/src/FluentHttpClient/BuilderFactory.cs @@ -0,0 +1,47 @@ +using System.Reflection; +#if NET6_0_OR_GREATER +using System.Diagnostics.CodeAnalysis; +#endif + +namespace FluentHttpClient; + +internal static class BuilderFactory< +#if NET6_0_OR_GREATER + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] +#endif + TBuilder> + where TBuilder : HttpRequestBuilder +{ + private static readonly ConstructorInfo _fromClient; + private static readonly ConstructorInfo _fromRoute; + private static readonly ConstructorInfo _fromUri; + + static BuilderFactory() + { + _fromClient = ResolveCtor(typeof(HttpClient)); + _fromRoute = ResolveCtor(typeof(HttpClient), typeof(string)); + _fromUri = ResolveCtor(typeof(HttpClient), typeof(Uri)); + } + + public static TBuilder Create(HttpClient client) + => (TBuilder)_fromClient.Invoke(new object[] { client }); + + public static TBuilder Create(HttpClient client, string route) + => (TBuilder)_fromRoute.Invoke(new object[] { client, route }); + + public static TBuilder Create(HttpClient client, Uri uri) + => (TBuilder)_fromUri.Invoke(new object[] { client, uri }); + + private static ConstructorInfo ResolveCtor(params Type[] parameterTypes) + { + return typeof(TBuilder).GetConstructor( + BindingFlags.Public | BindingFlags.Instance, + binder: null, + parameterTypes, + modifiers: null) + ?? throw new InvalidOperationException( + $"{typeof(TBuilder)} must declare a public constructor" + + $" ({string.Join(", ", Array.ConvertAll(parameterTypes, t => t.Name))})" + + $" to be used with BuilderFactory<{typeof(TBuilder).Name}>."); + } +} \ No newline at end of file diff --git a/src/FluentHttpClient/FluentAuthenticationExtensions.cs b/src/FluentHttpClient/FluentAuthenticationExtensions.cs index cefa7d4..131867c 100644 --- a/src/FluentHttpClient/FluentAuthenticationExtensions.cs +++ b/src/FluentHttpClient/FluentAuthenticationExtensions.cs @@ -4,18 +4,20 @@ namespace FluentHttpClient; /// -/// Fluent extension methods for configuring authentication headers on an instance. +/// Fluent extension methods for configuring authentication headers on an TBuilder instance. /// public static class FluentAuthenticationExtensions { /// /// Sets the for the request using the specified scheme and token. /// - /// The instance. + /// The type of the builder, which must inherit from . + /// The TBuilder instance. /// The authentication scheme (e.g., "Bearer", "Basic"). /// The authentication token or credentials. - /// The for method chaining. - public static HttpRequestBuilder WithAuthentication(this HttpRequestBuilder builder, string scheme, string token) + /// The TBuilder for method chaining. + public static TBuilder WithAuthentication(this TBuilder builder, string scheme, string token) + where TBuilder : HttpRequestBuilder { Guard.AgainstNull(scheme, nameof(scheme)); Guard.AgainstNull(token, nameof(token)); @@ -31,10 +33,12 @@ public static HttpRequestBuilder WithAuthentication(this HttpRequestBuilder buil /// /// Sets the authentication header to Basic using the specified token value. /// - /// The instance. + /// The type of the builder, which must inherit from . + /// The TBuilder instance. /// The Base64-encoded Basic authentication token. - /// The for method chaining. - public static HttpRequestBuilder WithBasicAuthentication(this HttpRequestBuilder builder, string token) + /// The TBuilder for method chaining. + public static TBuilder WithBasicAuthentication(this TBuilder builder, string token) + where TBuilder : HttpRequestBuilder { Guard.AgainstNull(token, nameof(token)); @@ -47,11 +51,12 @@ public static HttpRequestBuilder WithBasicAuthentication(this HttpRequestBuilder /// /// The username and password will be properly concatenated and Base64 encoded. /// - /// The instance. + /// The TBuilder instance. /// The username for Basic authentication. /// The password for Basic authentication. - /// The for method chaining. - public static HttpRequestBuilder WithBasicAuthentication(this HttpRequestBuilder builder, string username, string password) + /// The TBuilder for method chaining. + public static TBuilder WithBasicAuthentication(this TBuilder builder, string username, string password) + where TBuilder : HttpRequestBuilder { Guard.AgainstNull(username, nameof(username)); Guard.AgainstNull(password, nameof(password)); @@ -63,13 +68,15 @@ public static HttpRequestBuilder WithBasicAuthentication(this HttpRequestBuilder /// /// Sets the authentication header to Bearer using the specified OAuth token. /// - /// The instance. + /// The type of the builder, which must inherit from . + /// The TBuilder instance. /// The OAuth Bearer token. - /// The for method chaining. - public static HttpRequestBuilder WithOAuthBearerToken(this HttpRequestBuilder builder, string token) + /// The TBuilder for method chaining. + public static TBuilder WithOAuthBearerToken(this TBuilder builder, string token) + where TBuilder : HttpRequestBuilder { Guard.AgainstNull(token, nameof(token)); return builder.WithAuthentication("Bearer", token); } -} +} \ No newline at end of file diff --git a/src/FluentHttpClient/FluentConditionalExtensions.cs b/src/FluentHttpClient/FluentConditionalExtensions.cs index bf3294d..874b95f 100644 --- a/src/FluentHttpClient/FluentConditionalExtensions.cs +++ b/src/FluentHttpClient/FluentConditionalExtensions.cs @@ -1,7 +1,7 @@ namespace FluentHttpClient; /// -/// Provides conditional configuration delegates for instances. +/// Provides conditional configuration delegates for TBuilder instances. /// /// /// These extension methods allow callers to apply additional configuration to a request @@ -21,14 +21,15 @@ public static class FluentConditionalExtensions /// around the configuration logic, but keeps the control flow within the fluent /// pipeline. /// - /// The instance. + /// The TBuilder instance. /// The boolean condition that determines whether to apply the configuration. /// The action to invoke when the condition is true. - /// The for method chaining. - public static HttpRequestBuilder When( - this HttpRequestBuilder builder, + /// The TBuilder for method chaining. + public static TBuilder When( + this TBuilder builder, bool condition, - Action configure) + Action configure) + where TBuilder : HttpRequestBuilder { Guard.AgainstNull(configure, nameof(configure)); @@ -51,14 +52,15 @@ public static HttpRequestBuilder When( /// for conditions that depend on late-bound state such as ambient context values, /// feature flags, or other runtime information only available at request creation time. /// - /// The instance. + /// The TBuilder instance. /// A function that evaluates to determine whether to apply the configuration. /// The action to invoke when the predicate returns true. - /// The for method chaining. - public static HttpRequestBuilder When( - this HttpRequestBuilder builder, + /// The TBuilder for method chaining. + public static TBuilder When( + this TBuilder builder, Func predicate, - Action configure) + Action configure) + where TBuilder : HttpRequestBuilder { Guard.AgainstNull(predicate, nameof(predicate)); Guard.AgainstNull(configure, nameof(configure)); @@ -67,10 +69,10 @@ public static HttpRequestBuilder When( { if (predicate()) { - configure(b); + configure((TBuilder)b); } }); return builder; } -} +} \ No newline at end of file diff --git a/src/FluentHttpClient/FluentContentExtensions.cs b/src/FluentHttpClient/FluentContentExtensions.cs index e21ece2..f75dd62 100644 --- a/src/FluentHttpClient/FluentContentExtensions.cs +++ b/src/FluentHttpClient/FluentContentExtensions.cs @@ -4,7 +4,7 @@ namespace FluentHttpClient; /// -/// Fluent extension methods for adding content to the . +/// Fluent extension methods for adding content to the TBuilder. /// public static class FluentContentExtensions { @@ -19,9 +19,10 @@ public static class FluentContentExtensions /// protocol or middleware issues. /// Buffering can have a significant memory impact for large payloads. /// - /// The instance. - /// The for method chaining. - public static HttpRequestBuilder WithBufferedContent(this HttpRequestBuilder builder) + /// The TBuilder instance. + /// The TBuilder for method chaining. + public static TBuilder WithBufferedContent(this TBuilder builder) + where TBuilder : HttpRequestBuilder { builder.BufferRequestContent = true; return builder; @@ -33,12 +34,13 @@ public static HttpRequestBuilder WithBufferedContent(this HttpRequestBuilder bui /// /// Use this for adding any pre-built content that inherits from (e.g. ). /// - /// The instance. + /// The TBuilder instance. /// The HTTP content to send with the request. - /// The for method chaining. - public static HttpRequestBuilder WithContent( - this HttpRequestBuilder builder, + /// The TBuilder for method chaining. + public static TBuilder WithContent( + this TBuilder builder, HttpContent content) + where TBuilder : HttpRequestBuilder { builder.Content = content; return builder; @@ -47,12 +49,13 @@ public static HttpRequestBuilder WithContent( /// /// Sets the request content using form URL encoded data represented by a dictionary. /// - /// The instance. + /// The TBuilder instance. /// The dictionary containing form data as key-value pairs. - /// The for method chaining. - public static HttpRequestBuilder WithFormContent( - this HttpRequestBuilder builder, + /// The TBuilder for method chaining. + public static TBuilder WithFormContent( + this TBuilder builder, Dictionary data) + where TBuilder : HttpRequestBuilder { #if NET5_0 var pairs = data.Select(static kvp => @@ -69,12 +72,13 @@ public static HttpRequestBuilder WithFormContent( /// Sets the request content using form URL encoded data represented by a sequence /// of key/value pairs. Allows multiple values for the same key. /// - /// The instance. + /// The TBuilder instance. /// The sequence of key-value pairs containing form data. - /// The for method chaining. - public static HttpRequestBuilder WithFormContent( - this HttpRequestBuilder builder, + /// The TBuilder for method chaining. + public static TBuilder WithFormContent( + this TBuilder builder, IEnumerable> data) + where TBuilder : HttpRequestBuilder { #if NET5_0 var pairs = data.Select(static kvp => @@ -92,12 +96,13 @@ public static HttpRequestBuilder WithFormContent( /// /// Sets the request content using a with default encoding. /// - /// The instance. + /// The TBuilder instance. /// The string content to send with the request. - /// The for method chaining. - public static HttpRequestBuilder WithContent( - this HttpRequestBuilder builder, + /// The TBuilder for method chaining. + public static TBuilder WithContent( + this TBuilder builder, string content) + where TBuilder : HttpRequestBuilder { Guard.AgainstNull(content, nameof(content)); return builder.WithContent(content, null, null, null); @@ -106,14 +111,15 @@ public static HttpRequestBuilder WithContent( /// /// Sets the request content using a created with the specified encoding. /// - /// The instance. + /// The TBuilder instance. /// The string content to send with the request. /// The encoding to use for the content. - /// The for method chaining. - public static HttpRequestBuilder WithContent( - this HttpRequestBuilder builder, + /// The TBuilder for method chaining. + public static TBuilder WithContent( + this TBuilder builder, string content, Encoding encoding) + where TBuilder : HttpRequestBuilder { Guard.AgainstNull(content, nameof(content)); Guard.AgainstNull(encoding, nameof(encoding)); @@ -123,14 +129,15 @@ public static HttpRequestBuilder WithContent( /// /// Sets the request content using a with UTF-8 encoding and the specified media type. /// - /// The instance. + /// The TBuilder instance. /// The string content to send with the request. /// The media type string (e.g., "application/json"). - /// The for method chaining. - public static HttpRequestBuilder WithContent( - this HttpRequestBuilder builder, + /// The TBuilder for method chaining. + public static TBuilder WithContent( + this TBuilder builder, string content, string mediaType) + where TBuilder : HttpRequestBuilder { Guard.AgainstNull(content, nameof(content)); Guard.AgainstNull(mediaType, nameof(mediaType)); @@ -140,16 +147,17 @@ public static HttpRequestBuilder WithContent( /// /// Sets the request content using a with the specified encoding and media type. /// - /// The instance. + /// The TBuilder instance. /// The string content to send with the request. /// The encoding to use for the content. /// The media type string (e.g., "application/json"). - /// The for method chaining. - public static HttpRequestBuilder WithContent( - this HttpRequestBuilder builder, + /// The TBuilder for method chaining. + public static TBuilder WithContent( + this TBuilder builder, string content, Encoding encoding, string mediaType) + where TBuilder : HttpRequestBuilder { Guard.AgainstNull(content, nameof(content)); Guard.AgainstNull(encoding, nameof(encoding)); @@ -160,14 +168,15 @@ public static HttpRequestBuilder WithContent( /// /// Sets the request content using a and applies the specified . /// - /// The instance. + /// The TBuilder instance. /// The string content to send with the request. /// The media type header value to apply to the content. - /// The for method chaining. - public static HttpRequestBuilder WithContent( - this HttpRequestBuilder builder, + /// The TBuilder for method chaining. + public static TBuilder WithContent( + this TBuilder builder, string content, MediaTypeHeaderValue mediaTypeHeaderValue) + where TBuilder : HttpRequestBuilder { Guard.AgainstNull(content, nameof(content)); Guard.AgainstNull(mediaTypeHeaderValue, nameof(mediaTypeHeaderValue)); @@ -177,16 +186,17 @@ public static HttpRequestBuilder WithContent( /// /// Sets the request content using a with the specified encoding and applies the given . /// - /// The instance. + /// The TBuilder instance. /// The string content to send with the request. /// The encoding to use for the content. /// The media type header value to apply to the content. - /// The for method chaining. - public static HttpRequestBuilder WithContent( - this HttpRequestBuilder builder, + /// The TBuilder for method chaining. + public static TBuilder WithContent( + this TBuilder builder, string content, Encoding encoding, MediaTypeHeaderValue mediaTypeHeaderValue) + where TBuilder : HttpRequestBuilder { Guard.AgainstNull(content, nameof(content)); Guard.AgainstNull(encoding, nameof(encoding)); @@ -194,13 +204,14 @@ public static HttpRequestBuilder WithContent( return builder.WithContent(content, encoding, null, mediaTypeHeaderValue); } - private static HttpRequestBuilder WithContent( - this HttpRequestBuilder builder, + private static TBuilder WithContent( + this TBuilder builder, string content, Encoding? encoding, string? mediaType, MediaTypeHeaderValue? mediaTypeHeaderValue ) + where TBuilder : HttpRequestBuilder { if (mediaType is not null) { @@ -227,12 +238,13 @@ private static HttpRequestBuilder WithContent( /// /// Sets the request content to the provided XML string using UTF-8 encoding and the default XML media type. /// - /// The instance. + /// The TBuilder instance. /// The XML string content to send with the request. - /// The for method chaining. - public static HttpRequestBuilder WithXmlContent( - this HttpRequestBuilder builder, + /// The TBuilder for method chaining. + public static TBuilder WithXmlContent( + this TBuilder builder, string xml) + where TBuilder : HttpRequestBuilder { Guard.AgainstNull(xml, nameof(xml)); return builder.WithContent(xml, Encoding.UTF8, FluentXmlSerializer.DefaultContentType); @@ -241,14 +253,15 @@ public static HttpRequestBuilder WithXmlContent( /// /// Sets the request content to the provided XML string using the specified encoding and the default XML media type. /// - /// The instance. + /// The TBuilder instance. /// The XML string content to send with the request. /// The encoding to use for the content. - /// The for method chaining. - public static HttpRequestBuilder WithXmlContent( - this HttpRequestBuilder builder, + /// The TBuilder for method chaining. + public static TBuilder WithXmlContent( + this TBuilder builder, string xml, Encoding encoding) + where TBuilder : HttpRequestBuilder { Guard.AgainstNull(xml, nameof(xml)); return builder.WithContent(xml, encoding, FluentXmlSerializer.DefaultContentType); @@ -257,14 +270,15 @@ public static HttpRequestBuilder WithXmlContent( /// /// Sets the request content to the provided XML string using UTF-8 encoding and the specified media type. /// - /// The instance. + /// The TBuilder instance. /// The XML string content to send with the request. /// The media type string for the content. - /// The for method chaining. - public static HttpRequestBuilder WithXmlContent( - this HttpRequestBuilder builder, + /// The TBuilder for method chaining. + public static TBuilder WithXmlContent( + this TBuilder builder, string xml, string contentType) + where TBuilder : HttpRequestBuilder { Guard.AgainstNull(xml, nameof(xml)); return builder.WithContent(xml, Encoding.UTF8, contentType); @@ -273,14 +287,15 @@ public static HttpRequestBuilder WithXmlContent( /// /// Sets the request content to the provided XML string using UTF-8 encoding and applies the specified . /// - /// The instance. + /// The TBuilder instance. /// The XML string content to send with the request. /// The media type header value to apply to the content. - /// The for method chaining. - public static HttpRequestBuilder WithXmlContent( - this HttpRequestBuilder builder, + /// The TBuilder for method chaining. + public static TBuilder WithXmlContent( + this TBuilder builder, string xml, MediaTypeHeaderValue contentTypeHeaderValue) + where TBuilder : HttpRequestBuilder { Guard.AgainstNull(xml, nameof(xml)); return builder.WithContent(xml, Encoding.UTF8, contentTypeHeaderValue); @@ -289,16 +304,17 @@ public static HttpRequestBuilder WithXmlContent( /// /// Sets the request content to the provided XML string using the specified encoding and media type string. /// - /// The instance. + /// The TBuilder instance. /// The XML string content to send with the request. /// The encoding to use for the content. /// The media type string for the content. - /// The for method chaining. - public static HttpRequestBuilder WithXmlContent( - this HttpRequestBuilder builder, + /// The TBuilder for method chaining. + public static TBuilder WithXmlContent( + this TBuilder builder, string xml, Encoding encoding, string contentType) + where TBuilder : HttpRequestBuilder { Guard.AgainstNull(xml, nameof(xml)); return builder.WithContent(xml, encoding, contentType); @@ -307,16 +323,17 @@ public static HttpRequestBuilder WithXmlContent( /// /// Sets the request content to the provided XML string using the specified encoding and applies the given . /// - /// The instance. + /// The TBuilder instance. /// The XML string content to send with the request. /// The encoding to use for the content. /// The media type header value to apply to the content. - /// The for method chaining. - public static HttpRequestBuilder WithXmlContent( - this HttpRequestBuilder builder, + /// The TBuilder for method chaining. + public static TBuilder WithXmlContent( + this TBuilder builder, string xml, Encoding encoding, MediaTypeHeaderValue contentTypeHeaderValue) + where TBuilder : HttpRequestBuilder { Guard.AgainstNull(xml, nameof(xml)); return builder.WithContent(xml, encoding, contentTypeHeaderValue); @@ -329,12 +346,13 @@ public static HttpRequestBuilder WithXmlContent( /// /// Sets the request content to the provided JSON string using UTF-8 encoding and the default JSON media type. /// - /// The instance. + /// The TBuilder instance. /// The JSON string content to send with the request. - /// The for method chaining. - public static HttpRequestBuilder WithJsonContent( - this HttpRequestBuilder builder, + /// The TBuilder for method chaining. + public static TBuilder WithJsonContent( + this TBuilder builder, string json) + where TBuilder : HttpRequestBuilder { Guard.AgainstNull(json, nameof(json)); return builder.WithContent(json, Encoding.UTF8, FluentJsonSerializer.DefaultContentType); @@ -343,14 +361,15 @@ public static HttpRequestBuilder WithJsonContent( /// /// Sets the request content to the provided JSON string using the specified encoding and the default JSON media type. /// - /// The instance. + /// The TBuilder instance. /// The JSON string content to send with the request. /// The encoding to use for the content. - /// The for method chaining. - public static HttpRequestBuilder WithJsonContent( - this HttpRequestBuilder builder, + /// The TBuilder for method chaining. + public static TBuilder WithJsonContent( + this TBuilder builder, string json, Encoding encoding) + where TBuilder : HttpRequestBuilder { Guard.AgainstNull(json, nameof(json)); return builder.WithContent(json, encoding, FluentJsonSerializer.DefaultContentType); @@ -359,14 +378,15 @@ public static HttpRequestBuilder WithJsonContent( /// /// Sets the request content to the provided JSON string using UTF-8 encoding and the specified media type. /// - /// The instance. + /// The TBuilder instance. /// The JSON string content to send with the request. /// The media type string for the content. - /// The for method chaining. - public static HttpRequestBuilder WithJsonContent( - this HttpRequestBuilder builder, + /// The TBuilder for method chaining. + public static TBuilder WithJsonContent( + this TBuilder builder, string json, string contentType) + where TBuilder : HttpRequestBuilder { Guard.AgainstNull(json, nameof(json)); return builder.WithContent(json, Encoding.UTF8, contentType); @@ -375,16 +395,17 @@ public static HttpRequestBuilder WithJsonContent( /// /// Sets the request content to the provided JSON string using the specified encoding and media type. /// - /// The instance. + /// The TBuilder instance. /// The JSON string content to send with the request. /// The encoding to use for the content. /// The media type string for the content. - /// The for method chaining. - public static HttpRequestBuilder WithJsonContent( - this HttpRequestBuilder builder, + /// The TBuilder for method chaining. + public static TBuilder WithJsonContent( + this TBuilder builder, string json, Encoding encoding, string contentType) + where TBuilder : HttpRequestBuilder { Guard.AgainstNull(json, nameof(json)); return builder.WithContent(json, encoding, contentType); @@ -393,14 +414,15 @@ public static HttpRequestBuilder WithJsonContent( /// /// Sets the request content to the provided JSON string using UTF-8 encoding and applies the specified content type header value. /// - /// The instance. + /// The TBuilder instance. /// The JSON string content to send with the request. /// The media type header value to apply to the content. - /// The for method chaining. - public static HttpRequestBuilder WithJsonContent( - this HttpRequestBuilder builder, + /// The TBuilder for method chaining. + public static TBuilder WithJsonContent( + this TBuilder builder, string json, MediaTypeHeaderValue contentTypeHeaderValue) + where TBuilder : HttpRequestBuilder { Guard.AgainstNull(json, nameof(json)); return builder.WithContent(json, Encoding.UTF8, contentTypeHeaderValue); @@ -409,20 +431,21 @@ public static HttpRequestBuilder WithJsonContent( /// /// Sets the request content to the provided JSON string using the specified encoding and applies the given content type header value. /// - /// The instance. + /// The TBuilder instance. /// The JSON string content to send with the request. /// The encoding to use for the content. /// The media type header value to apply to the content. - /// The for method chaining. - public static HttpRequestBuilder WithJsonContent( - this HttpRequestBuilder builder, + /// The TBuilder for method chaining. + public static TBuilder WithJsonContent( + this TBuilder builder, string json, Encoding encoding, MediaTypeHeaderValue contentTypeHeaderValue) + where TBuilder : HttpRequestBuilder { Guard.AgainstNull(json, nameof(json)); return builder.WithContent(json, encoding, contentTypeHeaderValue); } #endregion -} +} \ No newline at end of file diff --git a/src/FluentHttpClient/FluentCookieExtensions.cs b/src/FluentHttpClient/FluentCookieExtensions.cs index 1def38e..6cdc91e 100644 --- a/src/FluentHttpClient/FluentCookieExtensions.cs +++ b/src/FluentHttpClient/FluentCookieExtensions.cs @@ -28,11 +28,12 @@ public static class FluentCookieExtensions /// If true (default), the value will be URL-encoded per RFC 6265. /// Set to false to use the value as-is. /// The for method chaining. - public static HttpRequestBuilder WithCookie( - this HttpRequestBuilder builder, + public static TBuilder WithCookie( + this TBuilder builder, string name, string value, bool encode = true) + where TBuilder : HttpRequestBuilder { Guard.AgainstNullOrEmpty(name, nameof(name)); @@ -66,10 +67,11 @@ public static HttpRequestBuilder WithCookie( /// If true (default), the value will be URL-encoded per RFC 6265. /// Set to false to use the value as-is. /// The for method chaining. - public static HttpRequestBuilder WithCookies( - this HttpRequestBuilder builder, + public static TBuilder WithCookies( + this TBuilder builder, IEnumerable> cookies, bool encode = true) + where TBuilder : HttpRequestBuilder { Guard.AgainstNull(cookies, nameof(cookies)); @@ -83,4 +85,4 @@ public static HttpRequestBuilder WithCookies( return builder; } -} +} \ No newline at end of file diff --git a/src/FluentHttpClient/FluentHeaderExtensions.cs b/src/FluentHttpClient/FluentHeaderExtensions.cs index 8210677..ecb6816 100644 --- a/src/FluentHttpClient/FluentHeaderExtensions.cs +++ b/src/FluentHttpClient/FluentHeaderExtensions.cs @@ -35,7 +35,7 @@ public static class FluentHeaderExtensions /// headers with complex types or specialized formatting. /// /// - /// For simple string-based headers, use + /// For simple string-based headers, use /// instead, which provides better performance through direct dictionary storage. /// /// @@ -79,7 +79,8 @@ public static class FluentHeaderExtensions /// /// /// Thrown when is null. - public static HttpRequestBuilder ConfigureHeaders(this HttpRequestBuilder builder, Action configure) + public static TBuilder ConfigureHeaders(this TBuilder builder, Action configure) + where TBuilder : HttpRequestBuilder { Guard.AgainstNull(configure, nameof(configure)); @@ -109,7 +110,8 @@ public static HttpRequestBuilder ConfigureHeaders(this HttpRequestBuilder builde /// The for method chaining. /// Thrown when or is null. /// Thrown when is a reserved header. - public static HttpRequestBuilder WithHeader(this HttpRequestBuilder builder, string key, string value) + public static TBuilder WithHeader(this TBuilder builder, string key, string value) + where TBuilder : HttpRequestBuilder { Guard.AgainstNull(key, nameof(key)); Guard.AgainstNull(value, nameof(value)); @@ -146,7 +148,7 @@ public static HttpRequestBuilder WithHeader(this HttpRequestBuilder builder, str /// accept multiple values, such as Accept or Cache-Control. /// /// - /// This is equivalent to calling + /// This is equivalent to calling /// multiple times with the same key but different values. /// /// @@ -163,7 +165,8 @@ public static HttpRequestBuilder WithHeader(this HttpRequestBuilder builder, str /// The for method chaining. /// Thrown when or is null. /// Thrown when is a reserved header. - public static HttpRequestBuilder WithHeader(this HttpRequestBuilder builder, string key, IEnumerable values) + public static TBuilder WithHeader(this TBuilder builder, string key, IEnumerable values) + where TBuilder : HttpRequestBuilder { Guard.AgainstNull(values, nameof(values)); @@ -199,7 +202,8 @@ public static HttpRequestBuilder WithHeader(this HttpRequestBuilder builder, str /// The for method chaining. /// Thrown when is null, or when any header key or value is null. /// Thrown when any header key is a reserved header. - public static HttpRequestBuilder WithHeaders(this HttpRequestBuilder builder, IEnumerable> headers) + public static TBuilder WithHeaders(this TBuilder builder, IEnumerable> headers) + where TBuilder : HttpRequestBuilder { Guard.AgainstNull(headers, nameof(headers)); @@ -236,9 +240,10 @@ public static HttpRequestBuilder WithHeaders(this HttpRequestBuilder builder, IE /// The for method chaining. /// Thrown when is null, or when any header key or value collection is null. /// Thrown when any header key is a reserved header. - public static HttpRequestBuilder WithHeaders( - this HttpRequestBuilder builder, + public static TBuilder WithHeaders( + this TBuilder builder, IEnumerable>> headers) + where TBuilder : HttpRequestBuilder { Guard.AgainstNull(headers, nameof(headers)); @@ -249,4 +254,4 @@ public static HttpRequestBuilder WithHeaders( return builder; } -} +} \ No newline at end of file diff --git a/src/FluentHttpClient/FluentHttpClient.csproj b/src/FluentHttpClient/FluentHttpClient.csproj index a7783eb..dc3c0ae 100644 --- a/src/FluentHttpClient/FluentHttpClient.csproj +++ b/src/FluentHttpClient/FluentHttpClient.csproj @@ -1,39 +1,58 @@  - netstandard2.0;netstandard2.1;net5.0;net6.0;net7.0;net8.0;net9.0;net10.0 - enable - enable + netstandard2.0;netstandard2.1;net6.0;net7.0;net8.0;net9.0;net10.0 latest + enable + enable latest + true + true + true + false FluentHttpClient Fluent extensions for HttpClient. fluent httpclient rest http api web client Scott Offen - © 2021 - $([System.DateTime]::Now.ToString('yyyy')) Scott Offen - MIT + © 2021 - $([System.DateTime]::UtcNow.Year) Scott Offen https://github.com/scottoffen/fluenthttpclient git https://scottoffen.github.io/fluenthttpclient/ - fluenthttpclient.png - README.md - true - true true true - true - true - snupkg + true + true true false link - True - FluentHttpClient.snk + MIT + README.md + fluenthttpclient.png + true + true + true + snupkg + $(MSBuildThisFileDirectory) - - true - + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + <_Parameter1>false + <_Parameter1_IsLiteral>true + + @@ -44,10 +63,6 @@ - - - - diff --git a/src/FluentHttpClient/FluentJsonContentExtensions.cs b/src/FluentHttpClient/FluentJsonContentExtensions.cs index 6fc9b57..2385389 100644 --- a/src/FluentHttpClient/FluentJsonContentExtensions.cs +++ b/src/FluentHttpClient/FluentJsonContentExtensions.cs @@ -6,7 +6,7 @@ namespace FluentHttpClient; /// -/// Fluent extension methods for adding JSON content to the . +/// Fluent extension methods for adding JSON content to the TBuilder. /// #if NET7_0_OR_GREATER [RequiresDynamicCode("Uses reflection-based JSON deserialization. For Native AOT, use the overloads that accept JsonTypeInfo.")] @@ -20,13 +20,15 @@ public static partial class FluentJsonContentExtensions /// Serializes the specified value as JSON using the default serializer options and sets it as the request content /// with UTF-8 encoding and the default JSON media type. /// + /// The type of the builder, which must inherit from . /// The type of the value to serialize. - /// The instance. + /// The TBuilder instance. /// The value to serialize as JSON. - /// The for method chaining. - public static HttpRequestBuilder WithJsonContent( - this HttpRequestBuilder builder, + /// The TBuilder for method chaining. + public static TBuilder WithJsonContent( + this TBuilder builder, T value) + where TBuilder : HttpRequestBuilder where T : class { var json = JsonSerializer.Serialize(value, FluentJsonSerializer.DefaultJsonSerializerOptions); @@ -38,15 +40,17 @@ public static HttpRequestBuilder WithJsonContent( /// Serializes the specified value as JSON using the provided serializer options and sets it as the request content /// with UTF-8 encoding and the default JSON media type. /// + /// The type of the builder, which must inherit from . /// The type of the value to serialize. - /// The instance. + /// The TBuilder instance. /// The value to serialize as JSON. /// The JSON serializer options to use. - /// The for method chaining. - public static HttpRequestBuilder WithJsonContent( - this HttpRequestBuilder builder, + /// The TBuilder for method chaining. + public static TBuilder WithJsonContent( + this TBuilder builder, T value, JsonSerializerOptions options) + where TBuilder : HttpRequestBuilder where T : class { var json = JsonSerializer.Serialize(value, options); @@ -58,15 +62,17 @@ public static HttpRequestBuilder WithJsonContent( /// Serializes the specified value as JSON using the default serializer options and sets it as the request content /// with UTF-8 encoding and the specified media type. /// + /// The type of the builder, which must inherit from . /// The type of the value to serialize. - /// The instance. + /// The TBuilder instance. /// The value to serialize as JSON. /// The media type string for the content. - /// The for method chaining. - public static HttpRequestBuilder WithJsonContent( - this HttpRequestBuilder builder, + /// The TBuilder for method chaining. + public static TBuilder WithJsonContent( + this TBuilder builder, T value, string contentType) + where TBuilder : HttpRequestBuilder where T : class { var json = JsonSerializer.Serialize(value, FluentJsonSerializer.DefaultJsonSerializerOptions); @@ -78,17 +84,19 @@ public static HttpRequestBuilder WithJsonContent( /// Serializes the specified value as JSON using the provided serializer options and sets it as the request content /// with UTF-8 encoding and the specified media type. /// + /// The type of the builder, which must inherit from . /// The type of the value to serialize. - /// The instance. + /// The TBuilder instance. /// The value to serialize as JSON. /// The JSON serializer options to use. /// The media type string for the content. - /// The for method chaining. - public static HttpRequestBuilder WithJsonContent( - this HttpRequestBuilder builder, + /// The TBuilder for method chaining. + public static TBuilder WithJsonContent( + this TBuilder builder, T value, JsonSerializerOptions options, string contentType) + where TBuilder : HttpRequestBuilder where T : class { var json = JsonSerializer.Serialize(value, options); @@ -100,15 +108,17 @@ public static HttpRequestBuilder WithJsonContent( /// Serializes the specified value as JSON using the default serializer options and sets it as the request content /// with UTF-8 encoding and applies the specified content type header value. /// + /// The type of the builder, which must inherit from . /// The type of the value to serialize. - /// The instance. + /// The TBuilder instance. /// The value to serialize as JSON. /// The media type header value to apply to the content. - /// The for method chaining. - public static HttpRequestBuilder WithJsonContent( - this HttpRequestBuilder builder, + /// The TBuilder for method chaining. + public static TBuilder WithJsonContent( + this TBuilder builder, T value, MediaTypeHeaderValue contentTypeHeaderValue) + where TBuilder : HttpRequestBuilder where T : class { var json = JsonSerializer.Serialize(value, FluentJsonSerializer.DefaultJsonSerializerOptions); @@ -122,17 +132,19 @@ public static HttpRequestBuilder WithJsonContent( /// Serializes the specified value as JSON using the provided serializer options and sets it as the request content /// with UTF-8 encoding and applies the given content type header value. /// + /// The type of the builder, which must inherit from . /// The type of the value to serialize. - /// The instance. + /// The TBuilder instance. /// The value to serialize as JSON. /// The JSON serializer options to use. /// The media type header value to apply to the content. - /// The for method chaining. - public static HttpRequestBuilder WithJsonContent( - this HttpRequestBuilder builder, + /// The TBuilder for method chaining. + public static TBuilder WithJsonContent( + this TBuilder builder, T value, JsonSerializerOptions options, MediaTypeHeaderValue contentTypeHeaderValue) + where TBuilder : HttpRequestBuilder where T : class { var json = JsonSerializer.Serialize(value, options); @@ -141,4 +153,4 @@ public static HttpRequestBuilder WithJsonContent( builder.Content = sc; return builder; } -} +} \ No newline at end of file diff --git a/src/FluentHttpClient/FluentJsonContentExtensionsAot.cs b/src/FluentHttpClient/FluentJsonContentExtensionsAot.cs index 60cea71..d18f3d3 100644 --- a/src/FluentHttpClient/FluentJsonContentExtensionsAot.cs +++ b/src/FluentHttpClient/FluentJsonContentExtensionsAot.cs @@ -13,15 +13,17 @@ public static partial class FluentJsonContentExtensions /// Serializes using the supplied and /// sets the JSON payload as the request content using UTF-8 and the default media type. /// + /// The type of the builder, which must inherit from . /// The type of the value to serialize. - /// The instance. + /// The TBuilder instance. /// The value to serialize as JSON. /// The JSON type metadata for AOT-safe serialization. - /// The for method chaining. - public static HttpRequestBuilder WithJsonContent( - this HttpRequestBuilder builder, + /// The TBuilder for method chaining. + public static TBuilder WithJsonContent( + this TBuilder builder, T value, JsonTypeInfo jsonTypeInfo) + where TBuilder : HttpRequestBuilder where T : class { Guard.AgainstNull(builder, nameof(builder)); @@ -36,15 +38,17 @@ public static HttpRequestBuilder WithJsonContent( /// Serializes using metadata from the provided /// and sets the JSON payload as the request content. /// + /// The type of the builder, which must inherit from . /// The type of the value to serialize. - /// The instance. + /// The TBuilder instance. /// The value to serialize as JSON. /// The JSON serializer context containing type metadata for AOT-safe serialization. - /// The for method chaining. - public static HttpRequestBuilder WithJsonContent( - this HttpRequestBuilder builder, + /// The TBuilder for method chaining. + public static TBuilder WithJsonContent( + this TBuilder builder, T value, JsonSerializerContext context) + where TBuilder : HttpRequestBuilder where T : class { Guard.AgainstNull(builder, nameof(builder)); @@ -64,17 +68,19 @@ public static HttpRequestBuilder WithJsonContent( /// Serializes using the supplied and /// sets the JSON payload as the request content using UTF-8 and the specified media type. /// + /// The type of the builder, which must inherit from . /// The type of the value to serialize. - /// The instance. + /// The TBuilder instance. /// The value to serialize as JSON. /// The JSON type metadata for AOT-safe serialization. /// The media type string for the content. - /// The for method chaining. - public static HttpRequestBuilder WithJsonContent( - this HttpRequestBuilder builder, + /// The TBuilder for method chaining. + public static TBuilder WithJsonContent( + this TBuilder builder, T value, JsonTypeInfo jsonTypeInfo, string contentType) + where TBuilder : HttpRequestBuilder where T : class { Guard.AgainstNull(builder, nameof(builder)); @@ -91,17 +97,19 @@ public static HttpRequestBuilder WithJsonContent( /// and sets the JSON payload as the request content /// using UTF-8 and the specified media type. /// + /// The type of the builder, which must inherit from . /// The type of the value to serialize. - /// The instance. + /// The TBuilder instance. /// The value to serialize as JSON. /// The JSON serializer context containing type metadata for AOT-safe serialization. /// The media type string for the content. - /// The for method chaining. - public static HttpRequestBuilder WithJsonContent( - this HttpRequestBuilder builder, + /// The TBuilder for method chaining. + public static TBuilder WithJsonContent( + this TBuilder builder, T value, JsonSerializerContext context, string contentType) + where TBuilder : HttpRequestBuilder where T : class { Guard.AgainstNull(builder, nameof(builder)); @@ -122,17 +130,19 @@ public static HttpRequestBuilder WithJsonContent( /// Serializes using the supplied and /// sets the JSON payload as the request content using UTF-8 and the specified content type header. /// + /// The type of the builder, which must inherit from . /// The type of the value to serialize. - /// The instance. + /// The TBuilder instance. /// The value to serialize as JSON. /// The JSON type metadata for AOT-safe serialization. /// The media type header value to apply to the content. - /// The for method chaining. - public static HttpRequestBuilder WithJsonContent( - this HttpRequestBuilder builder, + /// The TBuilder for method chaining. + public static TBuilder WithJsonContent( + this TBuilder builder, T value, JsonTypeInfo jsonTypeInfo, MediaTypeHeaderValue contentTypeHeaderValue) + where TBuilder : HttpRequestBuilder where T : class { Guard.AgainstNull(builder, nameof(builder)); @@ -152,17 +162,19 @@ public static HttpRequestBuilder WithJsonContent( /// and sets the JSON payload as the request content /// using UTF-8 and the specified content type header. /// + /// The type of the builder, which must inherit from . /// The type of the value to serialize. - /// The instance. + /// The TBuilder instance. /// The value to serialize as JSON. /// The JSON serializer context containing type metadata for AOT-safe serialization. /// The media type header value to apply to the content. - /// The for method chaining. - public static HttpRequestBuilder WithJsonContent( - this HttpRequestBuilder builder, + /// The TBuilder for method chaining. + public static TBuilder WithJsonContent( + this TBuilder builder, T value, JsonSerializerContext context, MediaTypeHeaderValue contentTypeHeaderValue) + where TBuilder : HttpRequestBuilder where T : class { Guard.AgainstNull(builder, nameof(builder)); @@ -179,4 +191,4 @@ public static HttpRequestBuilder WithJsonContent( return builder.WithJsonContent(value, jsonTypeInfo, contentTypeHeaderValue); } } -#endif +#endif \ No newline at end of file diff --git a/src/FluentHttpClient/FluentOptionsExtensions.cs b/src/FluentHttpClient/FluentOptionsExtensions.cs index 9642af1..ec27938 100644 --- a/src/FluentHttpClient/FluentOptionsExtensions.cs +++ b/src/FluentHttpClient/FluentOptionsExtensions.cs @@ -2,17 +2,19 @@ namespace FluentHttpClient; /// -/// Fluent extension methods for configuring the on an instances. +/// Fluent extension methods for configuring the on an TBuilder instances. /// public static class FluentOptionsExtensions { /// /// Adds a configurator for modifying the collection. /// - /// The instance. + /// The type of the builder, which must inherit from . + /// The TBuilder instance. /// The action to configure the request options. - /// The for method chaining. - public static HttpRequestBuilder ConfigureOptions(this HttpRequestBuilder builder, Action action) + /// The TBuilder for method chaining. + public static TBuilder ConfigureOptions(this TBuilder builder, Action action) + where TBuilder : HttpRequestBuilder { Guard.AgainstNull(action, nameof(action)); @@ -23,12 +25,14 @@ public static HttpRequestBuilder ConfigureOptions(this HttpRequestBuilder builde /// /// Sets a typed option value on the collection. /// + /// The type of the builder, which must inherit from . /// The type of the option value. - /// The instance. + /// The TBuilder instance. /// The key identifying the option to set. /// The value to set for the option. - /// The for method chaining. - public static HttpRequestBuilder WithOption(this HttpRequestBuilder builder, HttpRequestOptionsKey key, T value) + /// The TBuilder for method chaining. + public static TBuilder WithOption(this TBuilder builder, HttpRequestOptionsKey key, T value) + where TBuilder : HttpRequestBuilder { builder.OptionConfigurators.Add(options => { @@ -38,4 +42,4 @@ public static HttpRequestBuilder WithOption(this HttpRequestBuilder builder, return builder; } } -#endif +#endif \ No newline at end of file diff --git a/src/FluentHttpClient/FluentQueryParametersExtensions.cs b/src/FluentHttpClient/FluentQueryParametersExtensions.cs index 24804cb..79f942f 100644 --- a/src/FluentHttpClient/FluentQueryParametersExtensions.cs +++ b/src/FluentHttpClient/FluentQueryParametersExtensions.cs @@ -16,10 +16,11 @@ public static class FluentQueryParameterExtensions /// The query parameter key. /// The query parameter value. /// The for method chaining. - public static HttpRequestBuilder WithQueryParameter( - this HttpRequestBuilder builder, + public static TBuilder WithQueryParameter( + this TBuilder builder, string key, string? value) + where TBuilder : HttpRequestBuilder { Guard.AgainstNull(key, nameof(key)); @@ -34,10 +35,11 @@ public static HttpRequestBuilder WithQueryParameter( /// The query parameter key. /// The query parameter value that will be converted to a string. /// The for method chaining. - public static HttpRequestBuilder WithQueryParameter( - this HttpRequestBuilder builder, + public static TBuilder WithQueryParameter( + this TBuilder builder, string key, object? value) + where TBuilder : HttpRequestBuilder { Guard.AgainstNull(key, nameof(key)); @@ -52,10 +54,11 @@ public static HttpRequestBuilder WithQueryParameter( /// The query parameter key. /// The collection of values for the parameter. /// The for method chaining. - public static HttpRequestBuilder WithQueryParameter( - this HttpRequestBuilder builder, + public static TBuilder WithQueryParameter( + this TBuilder builder, string key, IEnumerable values) + where TBuilder : HttpRequestBuilder { Guard.AgainstNull(key, nameof(key)); Guard.AgainstNull(values, nameof(values)); @@ -71,10 +74,11 @@ public static HttpRequestBuilder WithQueryParameter( /// The query parameter key. /// The collection of values that will be converted to strings. /// The for method chaining. - public static HttpRequestBuilder WithQueryParameter( - this HttpRequestBuilder builder, + public static TBuilder WithQueryParameter( + this TBuilder builder, string key, IEnumerable values) + where TBuilder : HttpRequestBuilder { Guard.AgainstNull(key, nameof(key)); Guard.AgainstNull(values, nameof(values)); @@ -96,9 +100,10 @@ public static HttpRequestBuilder WithQueryParameter( /// The instance. /// The collection of query parameters as key-value pairs. /// The for method chaining. - public static HttpRequestBuilder WithQueryParameters( - this HttpRequestBuilder builder, + public static TBuilder WithQueryParameters( + this TBuilder builder, IEnumerable> parameters) + where TBuilder : HttpRequestBuilder { Guard.AgainstNull(parameters, nameof(parameters)); @@ -116,9 +121,10 @@ public static HttpRequestBuilder WithQueryParameters( /// The instance. /// The collection of query parameters whose values will be converted to strings. /// The for method chaining. - public static HttpRequestBuilder WithQueryParameters( - this HttpRequestBuilder builder, + public static TBuilder WithQueryParameters( + this TBuilder builder, IEnumerable> parameters) + where TBuilder : HttpRequestBuilder { Guard.AgainstNull(parameters, nameof(parameters)); @@ -136,9 +142,10 @@ public static HttpRequestBuilder WithQueryParameters( /// The instance. /// The collection of query parameters where each can have multiple values. /// The for method chaining. - public static HttpRequestBuilder WithQueryParameters( - this HttpRequestBuilder builder, + public static TBuilder WithQueryParameters( + this TBuilder builder, IEnumerable>> parameters) + where TBuilder : HttpRequestBuilder { Guard.AgainstNull(parameters, nameof(parameters)); @@ -156,9 +163,10 @@ public static HttpRequestBuilder WithQueryParameters( /// The instance. /// The collection of query parameters where each can have multiple values that will be converted to strings. /// The for method chaining. - public static HttpRequestBuilder WithQueryParameters( - this HttpRequestBuilder builder, + public static TBuilder WithQueryParameters( + this TBuilder builder, IEnumerable>> parameters) + where TBuilder : HttpRequestBuilder { Guard.AgainstNull(parameters, nameof(parameters)); @@ -186,10 +194,11 @@ public static HttpRequestBuilder WithQueryParameters( /// The query parameter key. /// The query parameter value, which is only added if not null. /// The for method chaining. - public static HttpRequestBuilder WithQueryParameterIfNotNull( - this HttpRequestBuilder builder, + public static TBuilder WithQueryParameterIfNotNull( + this TBuilder builder, string key, string? value) + where TBuilder : HttpRequestBuilder { Guard.AgainstNull(key, nameof(key)); return builder.When(value is not null, b => b.WithQueryParameter(key, value)); @@ -202,12 +211,13 @@ public static HttpRequestBuilder WithQueryParameterIfNotNull( /// The query parameter key. /// The query parameter value that will be converted to a string, only added if not null. /// The for method chaining. - public static HttpRequestBuilder WithQueryParameterIfNotNull( - this HttpRequestBuilder builder, + public static TBuilder WithQueryParameterIfNotNull( + this TBuilder builder, string key, object? value) + where TBuilder : HttpRequestBuilder { Guard.AgainstNull(key, nameof(key)); return builder.When(value is not null, b => b.WithQueryParameter(key, value)); } -} +} \ No newline at end of file diff --git a/src/FluentHttpClient/FluentTimeoutExtensions.cs b/src/FluentHttpClient/FluentTimeoutExtensions.cs index 9ad8740..c497d85 100644 --- a/src/FluentHttpClient/FluentTimeoutExtensions.cs +++ b/src/FluentHttpClient/FluentTimeoutExtensions.cs @@ -12,7 +12,8 @@ public static class FluentTimeoutExtensions /// /// /// - public static HttpRequestBuilder ClearTimeout(this HttpRequestBuilder builder) + public static TBuilder ClearTimeout(this TBuilder builder) + where TBuilder : HttpRequestBuilder { builder.Timeout = null; return builder; @@ -31,7 +32,8 @@ public static HttpRequestBuilder ClearTimeout(this HttpRequestBuilder builder) /// /// /// - public static HttpRequestBuilder WithTimeout(this HttpRequestBuilder builder, int seconds) + public static TBuilder WithTimeout(this TBuilder builder, int seconds) + where TBuilder : HttpRequestBuilder { if (seconds <= 0) { @@ -54,7 +56,8 @@ public static HttpRequestBuilder WithTimeout(this HttpRequestBuilder builder, in /// /// /// - public static HttpRequestBuilder WithTimeout(this HttpRequestBuilder builder, TimeSpan timeout) + public static TBuilder WithTimeout(this TBuilder builder, TimeSpan timeout) + where TBuilder : HttpRequestBuilder { Guard.AgainstNull(timeout, nameof(timeout)); @@ -66,4 +69,4 @@ public static HttpRequestBuilder WithTimeout(this HttpRequestBuilder builder, Ti builder.Timeout = timeout; return builder; } -} +} \ No newline at end of file diff --git a/src/FluentHttpClient/FluentVersionExtensions.cs b/src/FluentHttpClient/FluentVersionExtensions.cs index a5ec839..1069aa9 100644 --- a/src/FluentHttpClient/FluentVersionExtensions.cs +++ b/src/FluentHttpClient/FluentVersionExtensions.cs @@ -12,7 +12,8 @@ public static class FluentVersionExtensions /// The instance. /// The HTTP version as a string (e.g., "1.1", "2.0"). /// The for method chaining. - public static HttpRequestBuilder UsingVersion(this HttpRequestBuilder builder, string version) + public static TBuilder UsingVersion(this TBuilder builder, string version) + where TBuilder : HttpRequestBuilder { Guard.AgainstNullOrEmpty(version, nameof(version)); @@ -34,7 +35,8 @@ public static HttpRequestBuilder UsingVersion(this HttpRequestBuilder builder, s /// The major version number. /// The minor version number. /// The for method chaining. - public static HttpRequestBuilder UsingVersion(this HttpRequestBuilder builder, int major, int minor) + public static TBuilder UsingVersion(this TBuilder builder, int major, int minor) + where TBuilder : HttpRequestBuilder { builder.Version = new Version(major, minor); return builder; @@ -46,7 +48,8 @@ public static HttpRequestBuilder UsingVersion(this HttpRequestBuilder builder, i /// The instance. /// The HTTP version to use. /// The for method chaining. - public static HttpRequestBuilder UsingVersion(this HttpRequestBuilder builder, Version version) + public static TBuilder UsingVersion(this TBuilder builder, Version version) + where TBuilder : HttpRequestBuilder { Guard.AgainstNull(version, nameof(version)); @@ -64,7 +67,8 @@ public static HttpRequestBuilder UsingVersion(this HttpRequestBuilder builder, V /// The HTTP version as a string (e.g., "1.1", "2.0"). /// The version policy to use for negotiation. /// The for method chaining. - public static HttpRequestBuilder UsingVersion(this HttpRequestBuilder builder, string version, HttpVersionPolicy policy) + public static TBuilder UsingVersion(this TBuilder builder, string version, HttpVersionPolicy policy) + where TBuilder : HttpRequestBuilder { builder.UsingVersion(version); builder.VersionPolicy = policy; @@ -80,7 +84,8 @@ public static HttpRequestBuilder UsingVersion(this HttpRequestBuilder builder, s /// The HTTP version to use. /// The version policy to use for negotiation. /// The for method chaining. - public static HttpRequestBuilder UsingVersion(this HttpRequestBuilder builder, Version version, HttpVersionPolicy policy) + public static TBuilder UsingVersion(this TBuilder builder, Version version, HttpVersionPolicy policy) + where TBuilder : HttpRequestBuilder { Guard.AgainstNull(version, nameof(version)); @@ -97,10 +102,11 @@ public static HttpRequestBuilder UsingVersion(this HttpRequestBuilder builder, V /// The instance. /// The version policy to use for negotiation. /// The for method chaining. - public static HttpRequestBuilder UsingVersionPolicy(this HttpRequestBuilder builder, HttpVersionPolicy policy) + public static TBuilder UsingVersionPolicy(this TBuilder builder, HttpVersionPolicy policy) + where TBuilder : HttpRequestBuilder { builder.VersionPolicy = policy; return builder; } #endif -} +} \ No newline at end of file diff --git a/src/FluentHttpClient/FluentXmlContentExtensions.cs b/src/FluentHttpClient/FluentXmlContentExtensions.cs index 1c77d97..9f06b26 100644 --- a/src/FluentHttpClient/FluentXmlContentExtensions.cs +++ b/src/FluentHttpClient/FluentXmlContentExtensions.cs @@ -6,7 +6,7 @@ namespace FluentHttpClient; /// -/// Fluent extension methods for adding XML content to the . +/// Fluent extension methods for adding XML content to the TBuilder. /// #if NET7_0_OR_GREATER [RequiresDynamicCode("XmlSerializer uses dynamic code generation which is not supported with Native AOT.")] @@ -20,13 +20,15 @@ public static class FluentXmlContentExtensions /// Serializes the specified value as XML using the default settings and sets it as the request content /// with UTF-8 encoding and the default XML media type. /// + /// The type of the builder, which must inherit from . /// The type of the value to serialize. - /// The instance. + /// The TBuilder instance. /// The value to serialize as XML. - /// The for method chaining. - public static HttpRequestBuilder WithXmlContent( - this HttpRequestBuilder builder, + /// The TBuilder for method chaining. + public static TBuilder WithXmlContent( + this TBuilder builder, T obj) + where TBuilder : HttpRequestBuilder where T : class { var xml = FluentXmlSerializer.Serialize(obj); @@ -38,15 +40,17 @@ public static HttpRequestBuilder WithXmlContent( /// Serializes the specified value as XML using the provided settings and sets it as the request content /// with the encoding derived from the provided and the default XML media type. /// + /// The type of the builder, which must inherit from . /// The type of the value to serialize. - /// The instance. + /// The TBuilder instance. /// The value to serialize as XML. /// The XML writer settings to use during serialization. - /// The for method chaining. - public static HttpRequestBuilder WithXmlContent( - this HttpRequestBuilder builder, + /// The TBuilder for method chaining. + public static TBuilder WithXmlContent( + this TBuilder builder, T obj, XmlWriterSettings settings) + where TBuilder : HttpRequestBuilder where T : class { var xml = FluentXmlSerializer.Serialize(obj, settings); @@ -59,15 +63,17 @@ public static HttpRequestBuilder WithXmlContent( /// Serializes the specified value as XML using the default settings and sets it as the request content /// with UTF-8 encoding and the specified media type. /// + /// The type of the builder, which must inherit from . /// The type of the value to serialize. - /// The instance. + /// The TBuilder instance. /// The value to serialize as XML. /// The media type string for the content. - /// The for method chaining. - public static HttpRequestBuilder WithXmlContent( - this HttpRequestBuilder builder, + /// The TBuilder for method chaining. + public static TBuilder WithXmlContent( + this TBuilder builder, T obj, string contentType) + where TBuilder : HttpRequestBuilder where T : class { var xml = FluentXmlSerializer.Serialize(obj); @@ -79,17 +85,19 @@ public static HttpRequestBuilder WithXmlContent( /// Serializes the specified value as XML using the provided settings and sets it as the request content /// with the encoding derived from the provided and the specified media type. /// + /// The type of the builder, which must inherit from . /// The type of the value to serialize. - /// The instance. + /// The TBuilder instance. /// The value to serialize as XML. /// The XML writer settings to use during serialization. /// The media type string for the content. - /// The for method chaining. - public static HttpRequestBuilder WithXmlContent( - this HttpRequestBuilder builder, + /// The TBuilder for method chaining. + public static TBuilder WithXmlContent( + this TBuilder builder, T obj, XmlWriterSettings settings, string contentType) + where TBuilder : HttpRequestBuilder where T : class { var xml = FluentXmlSerializer.Serialize(obj, settings); @@ -102,15 +110,17 @@ public static HttpRequestBuilder WithXmlContent( /// Serializes the specified value as XML using the default settings and sets it as the request content /// with UTF-8 encoding and applies the specified . /// + /// The type of the builder, which must inherit from . /// The type of the value to serialize. - /// The instance. + /// The TBuilder instance. /// The value to serialize as XML. /// The media type header value to apply to the content. - /// The for method chaining. - public static HttpRequestBuilder WithXmlContent( - this HttpRequestBuilder builder, + /// The TBuilder for method chaining. + public static TBuilder WithXmlContent( + this TBuilder builder, T obj, MediaTypeHeaderValue contentTypeHeaderValue) + where TBuilder : HttpRequestBuilder where T : class { var xml = FluentXmlSerializer.Serialize(obj); @@ -125,17 +135,19 @@ public static HttpRequestBuilder WithXmlContent( /// with the encoding derived from the provided and applies the given /// . /// + /// The type of the builder, which must inherit from . /// The type of the value to serialize. - /// The instance. + /// The TBuilder instance. /// The value to serialize as XML. /// The XML writer settings to use during serialization. /// The media type header value to apply to the content. - /// The for method chaining. - public static HttpRequestBuilder WithXmlContent( - this HttpRequestBuilder builder, + /// The TBuilder for method chaining. + public static TBuilder WithXmlContent( + this TBuilder builder, T obj, XmlWriterSettings settings, MediaTypeHeaderValue contentTypeHeaderValue) + where TBuilder : HttpRequestBuilder where T : class { var xml = FluentXmlSerializer.Serialize(obj, settings); @@ -145,4 +157,4 @@ public static HttpRequestBuilder WithXmlContent( builder.Content = sc; return builder; } -} +} \ No newline at end of file diff --git a/src/FluentHttpClient/HttpClientExtensions.cs b/src/FluentHttpClient/HttpClientExtensions.cs index 75c32cd..b3a70ef 100644 --- a/src/FluentHttpClient/HttpClientExtensions.cs +++ b/src/FluentHttpClient/HttpClientExtensions.cs @@ -19,6 +19,25 @@ public static HttpRequestBuilder UsingBase(this HttpClient client) return new HttpRequestBuilder(client); } + /// + /// Creates a new TBuilder using the 's + /// configured as the starting point. + /// + /// The type of the request builder to create. + /// The instance to use for sending requests. + /// A new instance of initialized with the client's base address. +#if NET7_0_OR_GREATER + [RequiresDynamicCode("Constructs TBuilder with Activator.CreateInstance, which can require runtime code generation. For Native AOT, use the UsingRoute overload that takes a factory delegate.")] +#endif +#if NET6_0_OR_GREATER + [RequiresUnreferencedCode("Constructs TBuilder with Activator.CreateInstance, which is not trimming-safe. For trimmed or AOT apps, use the UsingRoute overload that takes a factory delegate.")] +#endif + public static TBuilder UsingBase(this HttpClient client) + where TBuilder : HttpRequestBuilder + { + return BuilderFactory.Create(client); + } + /// /// Creates a new using the specified route /// as the initial request URI. The value can be absolute or relative. @@ -31,6 +50,26 @@ public static HttpRequestBuilder UsingRoute(this HttpClient client, string route return new HttpRequestBuilder(client, route); } + /// + /// Creates a new TBuilder using the specified route + /// as the initial request URI. The value can be absolute or relative. + /// + /// The type of the request builder to create. + /// The instance to use for sending requests. + /// The route string for the request URI, which can be absolute or relative. + /// A new instance of initialized with the specified route. +#if NET7_0_OR_GREATER + [RequiresDynamicCode("Constructs TBuilder with Activator.CreateInstance, which can require runtime code generation. For Native AOT, use the UsingRoute overload that takes a factory delegate.")] +#endif +#if NET6_0_OR_GREATER + [RequiresUnreferencedCode("Constructs TBuilder with Activator.CreateInstance, which is not trimming-safe. For trimmed or AOT apps, use the UsingRoute overload that takes a factory delegate.")] +#endif + public static TBuilder UsingRoute(this HttpClient client, string route) + where TBuilder : HttpRequestBuilder + { + return BuilderFactory.Create(client, route); + } + /// /// Creates a new using the specified /// as the initial request URI. @@ -42,4 +81,23 @@ public static HttpRequestBuilder UsingRoute(this HttpClient client, Uri uri) { return new HttpRequestBuilder(client, uri); } + + /// + /// Creates a new TBuilder using the specified as the initial request URI. + /// + /// The type of the request builder to create. + /// The instance to use for sending requests. + /// The URI for the request. + /// A new instance of initialized with the specified URI. +#if NET7_0_OR_GREATER + [RequiresDynamicCode("Constructs TBuilder with Activator.CreateInstance, which can require runtime code generation. For Native AOT, use the UsingRoute overload that takes a factory delegate.")] +#endif +#if NET6_0_OR_GREATER + [RequiresUnreferencedCode("Constructs TBuilder with Activator.CreateInstance, which is not trimming-safe. For trimmed or AOT apps, use the UsingRoute overload that takes a factory delegate.")] +#endif + public static TBuilder UsingRoute(this HttpClient client, Uri uri) + where TBuilder : HttpRequestBuilder + { + return BuilderFactory.Create(client, uri); + } } diff --git a/src/FluentHttpClient/HttpRequestBuilder.cs b/src/FluentHttpClient/HttpRequestBuilder.cs index 8663f3b..dd061c0 100644 --- a/src/FluentHttpClient/HttpRequestBuilder.cs +++ b/src/FluentHttpClient/HttpRequestBuilder.cs @@ -10,14 +10,40 @@ namespace FluentHttpClient; /// public class HttpRequestBuilder { - internal static readonly string MessageInvalidBaseAddress = "HttpClient.BaseAddress must not contain a query string or fragment."; - internal static readonly string MessageInvalidRoute = "Route must not contain a query string or fragment. Use QueryParameters to specify query values."; - internal static readonly string MessageEmptyRoute = "Missing or invalid route provided to constructor."; - internal static readonly string MessageMissingRoute = "Client has no base address and no route information was provided."; - internal static readonly string CookieHeaderName = "Cookie"; + /// + /// The error message used when the is invalid. + /// + protected internal static readonly string MessageInvalidBaseAddress = "HttpClient.BaseAddress must not contain a query string or fragment."; + + /// + /// The error message used when the route provided to the constructor is invalid. + /// + protected internal static readonly string MessageInvalidRoute = "Route must not contain a query string or fragment. Use QueryParameters to specify query values."; + + /// + /// The error message used when the route provided to the constructor is null or empty. + /// + protected internal static readonly string MessageEmptyRoute = "Missing or invalid route provided to constructor."; + + /// + /// The error message used when neither nor is specified, + /// + protected internal static readonly string MessageMissingRoute = "Client has no base address and no route information was provided."; + + /// + /// The name of the HTTP header used to send cookies in the request. + /// + protected internal static readonly string CookieHeaderName = "Cookie"; + + /// + /// The HTTP client used to send requests built by this builder instance. + /// + protected readonly HttpClient _client; - private readonly HttpClient _client; - private readonly Uri? _route; + /// + /// The route URI for the request, if specified. This is combined with + /// + protected Uri? _route; /// /// Initializes a new instance of the class @@ -29,7 +55,7 @@ public class HttpRequestBuilder /// Thrown when has a that /// contains a query string or fragment. /// - internal HttpRequestBuilder(HttpClient client) + protected internal HttpRequestBuilder(HttpClient client) { _client = client ?? throw new ArgumentNullException(nameof(client)); @@ -49,7 +75,7 @@ internal HttpRequestBuilder(HttpClient client) /// /// The HTTP client to use for sending requests. /// The route string for the request. - internal HttpRequestBuilder(HttpClient client, string route) + protected internal HttpRequestBuilder(HttpClient client, string route) : this(client, CreateRouteUri(route)) { } @@ -64,26 +90,37 @@ internal HttpRequestBuilder(HttpClient client, string route) /// /// Thrown when contains a query string or fragment. /// - internal HttpRequestBuilder(HttpClient client, Uri route) : this(client) + protected internal HttpRequestBuilder(HttpClient client, Uri route) : this(client) { - Guard.AgainstNull(route, nameof(route)); + ValidateRoute(route); + _route = route; + } - if (route.IsAbsoluteUri) - { - if (!string.IsNullOrEmpty(route.Query) || !string.IsNullOrEmpty(route.Fragment)) - { - throw new ArgumentException(MessageInvalidRoute, nameof(route)); - } - } - else - { - var text = route.OriginalString; - if (text.IndexOfAny(['?', '#']) >= 0) - { - throw new ArgumentException(MessageInvalidRoute, nameof(route)); - } - } + /// + /// Sets the route for the request, replacing any route supplied at construction + /// or by an earlier call. Intended for use by derived types, so that a factory + /// can construct a client without knowing the route in advance. + /// + /// The route string for the request. + /// Thrown when is null. + /// + /// Thrown when is empty or contains a query string or fragment. + /// + protected void SetRoute(string route) => SetRoute(CreateRouteUri(route)); + /// + /// Sets the route for the request, replacing any route supplied at construction + /// or by an earlier call. Intended for use by derived types, so that a factory + /// can construct a client without knowing the route in advance. + /// + /// The route URI for the request. + /// Thrown when is null. + /// + /// Thrown when contains a query string or fragment. + /// + protected void SetRoute(Uri route) + { + ValidateRoute(route); _route = route; } @@ -135,7 +172,7 @@ internal HttpRequestBuilder(HttpClient client, Uri route) : this(client) /// . /// /// - internal Dictionary> InternalHeaders { get; } = + protected internal Dictionary> InternalHeaders { get; } = new Dictionary>(StringComparer.OrdinalIgnoreCase); /// @@ -208,7 +245,7 @@ internal HttpRequestBuilder(HttpClient client, Uri route) : this(client) /// time interval. This timeout composes with any caller-provided /// ; whichever triggers first will cancel the request. /// - public TimeSpan? Timeout { get; internal set; } + public TimeSpan? Timeout { get; protected internal set; } /// /// Gets or sets the HTTP message version. @@ -374,7 +411,7 @@ public Task SendAsync( /// Thrown when the request URI cannot be constructed because neither /// nor is specified. /// - public async Task SendAsync( + public virtual async Task SendAsync( HttpMethod method, HttpCompletionOption completionOption, CancellationToken cancellationToken) @@ -417,7 +454,7 @@ public async Task SendAsync( /// Thrown when the request URI cannot be constructed because neither /// nor is specified. /// - internal async Task BuildRequest(HttpMethod method, CancellationToken cancellationToken) + protected internal async Task BuildRequest(HttpMethod method, CancellationToken cancellationToken) { Guard.AgainstNull(method, nameof(method)); @@ -459,7 +496,7 @@ internal async Task BuildRequest(HttpMethod method, Cancella /// /// Thrown when both and are missing. /// - internal Uri BuildRequestUri() + protected internal Uri BuildRequestUri() { if (_client.BaseAddress is null && _route is null) { @@ -487,7 +524,7 @@ internal Uri BuildRequestUri() /// Applies headers, cookies, and options from the builder to the request. /// /// The HTTP request message to configure. - private void ApplyConfiguration(HttpRequestMessage request) + protected void ApplyConfiguration(HttpRequestMessage request) { if (Content is MultipartContent) { @@ -540,14 +577,43 @@ private void ApplyConfiguration(HttpRequestMessage request) #endif } + /// + /// Validates that a route URI does not contain a query string or fragment. + /// + /// The route URI to validate. + /// Thrown when is null. + /// + /// Thrown when contains a query string or fragment. + /// + protected internal static void ValidateRoute(Uri route) + { + Guard.AgainstNull(route, nameof(route)); + + if (route.IsAbsoluteUri) + { + if (!string.IsNullOrEmpty(route.Query) || !string.IsNullOrEmpty(route.Fragment)) + { + throw new ArgumentException(MessageInvalidRoute, nameof(route)); + } + } + else + { + var text = route.OriginalString; + if (text.IndexOfAny(['?', '#']) >= 0) + { + throw new ArgumentException(MessageInvalidRoute, nameof(route)); + } + } + } + /// /// Creates a instance from the specified route string, /// trimming whitespace and validating that the value is not null or empty. /// /// - /// This method should only be used by the constructors. + /// Used internally to normalize a route string into a . /// - internal static Uri CreateRouteUri(string route) + protected internal static Uri CreateRouteUri(string route) { Guard.AgainstNull(route, nameof(route)); diff --git a/src/FluentHttpClient/README.md b/src/FluentHttpClient/README.md index 96fc18f..350593b 100644 --- a/src/FluentHttpClient/README.md +++ b/src/FluentHttpClient/README.md @@ -1,55 +1,86 @@ # FluentHttpClient -FluentHttpClient brings a modern, chainable API to [`HttpClient`](https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclient), turning verbose request setup into clean, expressive fluency. It handles headers, options, cookies, query parameters, conditional configurators, buffering, and *both* JSON/XML serialization and deserialization, along with success and failure handlers, all with minimal ceremony. It multitargets from **.NET Standard 2.0** all the way up through **.NET 10**, giving you broad compatibility across older runtimes and the latest platforms, with full Native AOT compatibility and strong-named assemblies. +FluentHttpClient adds a chainable API on top of [`HttpClient`](https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclient). You configure a request, send it, and read the response in one expression, instead of building an `HttpRequestMessage`, checking the status code, and deserializing by hand every time. -## Compatibility Matrix +It works with the `HttpClient` you already have rather than replacing it. Each request is built on its own without changing the client or its shared configuration, so your existing setup, including `IHttpClientFactory` and typed clients, is unaffected. -FluentHttpClient is optimized for .NET 10 and the newest .NET releases, while also supporting older platforms through .NET Standard 2.1 and 2.0 for teams maintaining long-lived or legacy applications. It includes full Native AOT compatibility and provides strong-named assemblies for environments that require them. +## What You Get -| Target | Supported | Notes | -| ------------------------- | --------- | ----------------------------- | -| **.NET Standard 2.0** | ✔️ | Broadest compatibility target | -| **.NET Standard 2.1** | ✔️ | Improved modern API surface | -| **.NET Framework 4.6.1+** | ✔️ | Via `netstandard2.0` | -| **.NET 6** | ✔️ | LTS | -| **.NET 7** | ✔️ | | -| **.NET 8** | ✔️ | LTS | -| **.NET 9** | ✔️ | | -| **.NET 10** | ✔️ | LTS | +- **Fluent configuration** of headers, query parameters, cookies, authentication, options, content, and content buffering, all in one readable chain. +- **JSON and XML** serialization and deserialization, with `JsonTypeInfo` overloads for trim-safe and Native AOT scenarios. +- **Conditional configuration** that applies immediately or defers until the request is built, so you can branch without breaking the chain. +- **Response handlers** that attach success and failure callbacks inline, without interrupting the chain. +- **Extensible by subclassing**: derive from `HttpRequestBuilder` to create a custom builder shaped for a specific API or concern. Your methods chain alongside the built-in ones, and an override of `SendAsync` applies your logic to every request, since every other member on the class feeds into it. -### .NETStandard Consumers +## Side-by-Side -Projects targeting **.NETStandard 2.0** or **.NETStandard 2.1** do not include `System.Text.Json` in the framework. FluentHttpClient uses `System.Text.Json` internally for its JSON extensions, but the package is not referenced transitively. +The same request, written with raw `HttpClient` and with FluentHttpClient. Both deserialize the response into the same model: -If you are building against **netstandard2.0** or **netstandard2.1**, or any TFM that does **not** ship `System.Text.Json`, you will need to add an explicit package reference, with a minimum version of 4.6.0 or 6.0.10, respectively. A higher version is always recommended. +```csharp +public class Post +{ + public int Id { get; set; } + public string? Title { get; set; } + public string? Body { get; set; } +} +``` -Apps targeting modern TFMs (such as .NET 5 and later) already include `System.Text.Json` and do not require this step. +### Raw HttpClient -## When to Use FluentHttpClient +```csharp +using System.Net.Http.Json; -While `HttpClient` is a powerful and flexible tool, building HTTP requests with it often involves repetitive boilerplate, manual serialization, and scattered configuration logic. FluentHttpClient addresses these pain points by providing a fluent, chainable API that reduces cognitive load and improves code readability. +var client = new HttpClient +{ + BaseAddress = new Uri("https://jsonplaceholder.typicode.com") +}; -### Common HttpClient Challenges +var request = new HttpRequestMessage(HttpMethod.Get, "/posts/1"); +request.Headers.Add("X-Correlation-Id", correlationId); -**Repetitive Configuration** -Every request requires manually setting headers, query parameters, and content, often scattered across multiple lines. This makes it easy to miss required headers or forget encoding rules. +var response = await client.SendAsync(request); -**Manual Serialization** -Converting objects to JSON, setting the correct `Content-Type`, and deserializing responses requires multiple steps and imports. Error-prone encoding and parsing logic often needs to be duplicated across your codebase. +Post? post = null; +if (response.IsSuccessStatusCode) +{ + post = await response.Content.ReadFromJsonAsync(); + Console.WriteLine($"Success: {response.StatusCode}"); +} +else +{ + Console.WriteLine($"Failed: {response.StatusCode}"); +} +``` -**Inconsistent Error Handling** -Without a unified approach to handling success and failure responses, status code checks and logging logic tend to be duplicated or omitted entirely. +### FluentHttpClient -**Lifetime and Reuse Concerns** -Properly managing `HttpClient` lifetime, avoiding socket exhaustion, and reusing instances while still configuring per-request state requires careful planning and often leads to awkward patterns. +```csharp +using FluentHttpClient; -### How FluentHttpClient Helps +var client = new HttpClient +{ + BaseAddress = new Uri("https://jsonplaceholder.typicode.com") +}; -FluentHttpClient wraps `HttpClient` (you still manage the lifetime) and provides extension methods that let you configure requests in a single, readable chain: +var post = await client + .UsingRoute("/posts/1") + .WithHeader("X-Correlation-Id", correlationId) + .GetAsync() + .OnSuccess(r => Console.WriteLine($"Success: {r.StatusCode}")) + .OnFailure(r => Console.WriteLine($"Failed: {r.StatusCode}")) + .ReadJsonAsync(); +``` -- **Fluent Configuration**: Add headers, query parameters, cookies, and authentication in a natural, discoverable flow -- **Automatic Serialization**: Built-in JSON and XML serialization/deserialization with support for `System.Text.Json`, Native AOT, and custom options -- **Response Handlers**: Attach success and failure callbacks directly in the request chain without breaking fluency -- **Reduced Boilerplate**: Express the entire request lifecycle—configuration, sending, and deserialization—in a single expression +The FluentHttpClient version expresses the same logic in fewer lines, and keeps configuration, sending, error handling, and deserialization together in one chain. -Because a fluent API improves developer experience by turning tedious, repetitive setup into a readable, chainable flow that matches how you actually think about building and sending an HTTP request, FluentHttpClient can expresses the same logic in fewer lines, with better readability and no loss of functionality. All configuration, sending, error handling, and deserialization happen in a single fluent chain. \ No newline at end of file +## Target Frameworks + +FluentHttpClient multitargets .NET Standard 2.0 and 2.1, and .NET 6, 7, 8, 9, and 10. Through .NET Standard 2.0 it also runs on .NET Framework 4.6.1 and later. The assemblies are strong-named, and the package is Native AOT compatible when you use the `JsonTypeInfo` JSON overloads. + +### .NET Standard Consumers + +.NET Standard 2.0 and 2.1 do not ship `System.Text.Json`, and FluentHttpClient does not bring it in transitively. If you target either one, or any other framework that does not include `System.Text.Json`, add an explicit package reference: at least `4.6.0` for `netstandard2.0` or `6.0.10` for `netstandard2.1`. A newer version is always preferable. Apps on .NET 5 and later already include it and need no extra step. + +## Documentation + +Full documentation, including how to create custom builders, is at https://scottoffen.github.io/fluenthttpclient. \ No newline at end of file diff --git a/src/coverlet.runsettings b/src/coverlet.runsettings index cba32d5..3e89938 100644 --- a/src/coverlet.runsettings +++ b/src/coverlet.runsettings @@ -4,9 +4,10 @@ - ExcludeFromCodeCoverageAttribute + ExcludeFromCodeCoverageAttribute,GeneratedCodeAttribute,CompilerGeneratedAttribute + true - + \ No newline at end of file diff --git a/src/version.json b/src/version.json index 22e4c40..377d25a 100644 --- a/src/version.json +++ b/src/version.json @@ -1,6 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/main/src/NerdBank.GitVersioning/version.schema.json", - "version": "5.0.1", + "version": "5.0", "publicReleaseRefSpec": [ "^refs/heads/main$", "^refs/heads/v\\d+(?:\\.\\d+)?$" diff --git a/test-coverage.ps1 b/test-coverage.ps1 index 903e38e..5f869e0 100644 --- a/test-coverage.ps1 +++ b/test-coverage.ps1 @@ -1,41 +1,214 @@ #!/usr/bin/env pwsh -# test-coverage.ps1 +<# +.SYNOPSIS + Runs all tests across the repository's solution(s) and produces a single, + combined code coverage report. -$ErrorActionPreference = "Stop" +.DESCRIPTION + This script lives at the repository root. It autodiscovers every solution + file under src/ (the repo convention is that all .sln files live there), + builds and tests each one, then merges the coverage output from every test + project into one HTML + text report. + + It is intended for local use: by default it opens the HTML report in your + browser when finished. Pass -NoOpen to skip that step, which also makes the + script safe to run in CI (it will not try to launch a browser on a build + agent). + +.PARAMETER Solution + Optional. One or more solution paths to use instead of autodiscovery. + Leave this empty (the default) to let the script find src/*.sln itself. + +.PARAMETER NoOpen + Suppresses opening the report in a browser when the run finishes. + Use this in CI, or any time you just want the files on disk. + +.EXAMPLE + ./test-coverage.ps1 + Autodiscovers src/*.sln, runs everything, and opens the report locally. -# Run tests and collect coverage -dotnet test src --collect:"XPlat Code Coverage" --settings src/coverlet.runsettings +.EXAMPLE + ./test-coverage.ps1 -NoOpen + Same as above but does not open a browser (suitable for CI). +#> -# Find the most recent coverage file -$coverageFile = Get-ChildItem -Recurse -Filter "coverage.cobertura.xml" | Sort-Object LastWriteTime -Descending | Select-Object -First 1 +param( + # Optional explicit solution list. When empty, the script discovers src/*.sln. + [string[]]$Solution, -if (-not $coverageFile) { - Write-Error "❌ No coverage file found." - exit 1 + # When set, do not open the HTML report at the end. + [switch]$NoOpen +) + +# Stop on PowerShell cmdlet errors (for example a failed Get-ChildItem or +# Remove-Item). NOTE: this does NOT catch failures from native commands like +# dotnet, so we check $LASTEXITCODE ourselves after each dotnet call. +$ErrorActionPreference = "Stop" + +# Helper: throw if the most recent native command returned a non-zero exit code. +# We use this for the build, restore, and report steps, where any failure should +# abort the run. We deliberately do NOT use it after 'dotnet test', because +# failing tests are an expected outcome that should still produce a report. +function Assert-LastExitCode { + param([string]$What) + if ($LASTEXITCODE -ne 0) { + throw "$What failed with exit code $LASTEXITCODE" + } } -# Restore the report generator tool -dotnet tool restore --tool-manifest ./src/.config/dotnet-tools.json +# Anchor everything to the repository root (this script's own folder) so the +# script behaves identically no matter what directory you invoke it from. +# Push-Location plus the finally block at the very bottom restores your original +# location when the script exits, even on error. +Push-Location $PSScriptRoot +try { + # --- Discover the solution(s) ------------------------------------------- + # Convention: all solution files live under src/. If the caller passed an + # explicit -Solution list, honor it; otherwise find every src/*.sln. + if ($Solution) { + $solutionFiles = $Solution | ForEach-Object { Get-Item $_ } + } + else { + $solutionFiles = Get-ChildItem -Path (Join-Path $PSScriptRoot "src") -Filter "*.sln" + } + + if (@($solutionFiles).Count -eq 0) { + throw "No solution files found under src/. Nothing to build or test." + } + + Write-Host "Found $(@($solutionFiles).Count) solution(s):" + $solutionFiles | ForEach-Object { Write-Host " $($_.Name)" } + + # --- Prepare folders ----------------------------------------------------- + # Put test results in a known folder so we never accidentally pick up + # coverage files from an older run. Wipe it first. + $resultsDir = Join-Path $PSScriptRoot "TestResults" + if (Test-Path $resultsDir) { Remove-Item $resultsDir -Recurse -Force } + + # The coverlet run settings file, shared by all solutions and anchored to the + # repo root. If it is missing we warn and carry on without it rather than + # failing the whole run. + $runSettings = Join-Path $PSScriptRoot "src/coverlet.runsettings" + $hasRunSettings = Test-Path $runSettings + if (-not $hasRunSettings) { + Write-Warning "Run settings not found at $runSettings. Continuing without --settings." + } + + # --- Build every solution ------------------------------------------------ + # Build all solutions first, then test with --no-build, so the test pass + # never triggers a rebuild. + foreach ($sln in $solutionFiles) { + Write-Host "`nBuilding $($sln.Name)..." + dotnet build $sln.FullName + Assert-LastExitCode "dotnet build ($($sln.Name))" + } + + # --- Test every solution ------------------------------------------------- + # Capture the test exit code instead of throwing on it. Failing tests are a + # normal result and we still want the coverage report. We remember the last + # non-zero code so the script can exit non-zero at the very end (which is + # what makes the CI case behave correctly). + $testExitCode = 0 + foreach ($sln in $solutionFiles) { + Write-Host "`nTesting $($sln.Name)..." + + # Build the argument list so we can conditionally include --settings. + # Splatting (@testArgs) passes each array element as one argument, so the + # space inside "XPlat Code Coverage" is preserved without extra quoting. + $testArgs = @( + "test", $sln.FullName, + "--no-build", + "--collect:XPlat Code Coverage", + "--results-directory", $resultsDir + ) + if ($hasRunSettings) { + $testArgs += @("--settings", $runSettings) + } + + dotnet @testArgs + if ($LASTEXITCODE -ne 0) { $testExitCode = $LASTEXITCODE } + } + + # --- Collect coverage ---------------------------------------------------- + # Every test project drops a coverage.cobertura.xml under a GUID-named + # subfolder of $resultsDir. Grab them all, from every solution, so the report + # covers the entire repository in one pass. + $coverageFiles = Get-ChildItem -Path $resultsDir -Recurse -Filter "coverage.cobertura.xml" + if (@($coverageFiles).Count -eq 0) { + throw "No coverage files found under $resultsDir. The test run likely failed to start." + } + + Write-Host "`nFound $(@($coverageFiles).Count) coverage file(s)." + + # ReportGenerator takes a semicolon-separated list of report paths. + $reportsArg = ($coverageFiles | ForEach-Object { $_.FullName }) -join ';' + + # --- Generate the report ------------------------------------------------- + # Clean the output folder first so classes removed since the last run do not + # linger as stale HTML pages. + $coverageDir = Join-Path $PSScriptRoot "coverage" + if (Test-Path $coverageDir) { Remove-Item $coverageDir -Recurse -Force } -# Generate the report -Push-Location ./src + # The arguments are the same however ReportGenerator is invoked. targetdir is + # an absolute path so it does not depend on the current working directory. + $reportArgs = @( + "-reports:$reportsArg", + "-targetdir:$coverageDir", + "-reporttypes:HtmlInline_AzurePipelines;TextSummary" + ) -dotnet tool run reportgenerator ` - -reports:$coverageFile.FullName ` - -targetdir:"../coverage" ` - -reporttypes:"HtmlInline_AzurePipelines;TextSummary" + # ReportGenerator can come from one of two places. Prefer the local tool + # manifest when it is present (pinned and reproducible, no global install + # required); otherwise fall back to a globally installed tool. + $toolManifest = Join-Path $PSScriptRoot "src/.config/dotnet-tools.json" + if (Test-Path $toolManifest) { + dotnet tool restore --tool-manifest $toolManifest + Assert-LastExitCode "dotnet tool restore" -Pop-Location + # 'dotnet tool run' finds the manifest by walking up from the current + # directory, so run it from ./src where src/.config/dotnet-tools.json lives. + Push-Location ./src + try { + dotnet tool run reportgenerator @reportArgs + Assert-LastExitCode "reportgenerator" + } + finally { + Pop-Location + } + } + elseif (Get-Command reportgenerator -ErrorAction SilentlyContinue) { + # No manifest: use the globally installed ReportGenerator from PATH. + reportgenerator @reportArgs + Assert-LastExitCode "reportgenerator" + } + else { + throw "ReportGenerator not found. Either keep src/.config/dotnet-tools.json in the repository, or install the tool globally with: dotnet tool install --global dotnet-reportgenerator-globaltool" + } -# Open report in default browser -$indexPath = Join-Path "coverage" "index.html" + # --- Open the report (local convenience) --------------------------------- + # Skipped when -NoOpen is set, which is what makes this safe for CI. + $indexPath = Join-Path $coverageDir "index.html" + if (-not $NoOpen) { + if ($IsWindows) { + Start-Process $indexPath + } elseif ($IsMacOS) { + open $indexPath + } elseif ($IsLinux) { + xdg-open $indexPath + } else { + Write-Warning "Platform not detected. Open coverage/index.html manually." + } + } -if ($IsWindows) { - Start-Process $indexPath -} elseif ($IsMacOS) { - open $indexPath -} elseif ($IsLinux) { - xdg-open $indexPath -} else { - Write-Warning "Platform not detected - open coverage/index.html manually" + # --- Surface the test result last ---------------------------------------- + # You always get the report above. But if any tests failed, exit non-zero so + # the failure is not silently swallowed (and so CI would go red). + if ($testExitCode -ne 0) { + Write-Warning "Some tests failed (dotnet test exit code $testExitCode). Coverage report was still generated." + exit $testExitCode + } } +finally { + # Always restore the caller's original directory. + Pop-Location +} \ No newline at end of file