commit 78054ee2a72ca453cc2364427f864aca68dfae78 Author: Dylan R. E. Moonfire Date: Tue Sep 7 00:15:45 2021 -0500 feat: initial release diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..fc1b7bd --- /dev/null +++ b/.editorconfig @@ -0,0 +1,122 @@ +# EditorConfig is awesome: https://EditorConfig.org + +root = true + +[*] +charset=utf-8 +end_of_line = lf +insert_final_newline=true +indent_style=space +indent_size=4 + +# Microsoft .NET properties +csharp_new_line_before_members_in_object_initializers=false +csharp_preferred_modifier_order=public, private, protected, internal, new, abstract, virtual, sealed, override, static, readonly, extern, unsafe, volatile, async:suggestion +csharp_space_after_cast=false +csharp_style_var_elsewhere=false:hint +csharp_style_var_for_built_in_types=false:hint +csharp_style_var_when_type_is_apparent=true:hint +csharp_preserve_single_line_statements=false +csharp_preserve_single_line_blocks=true +dotnet_style_predefined_type_for_locals_parameters_members=true:hint +dotnet_style_predefined_type_for_member_access=true:hint +dotnet_style_qualification_for_event=true:hint +dotnet_style_qualification_for_field=true:hint +dotnet_style_qualification_for_method=true:hint +dotnet_style_qualification_for_property=true:hint +dotnet_style_require_accessibility_modifiers=for_non_interface_members:hint + +# ReSharper properties +resharper_alignment_tab_fill_style=optimal_fill +resharper_apply_on_completion=true +resharper_blank_lines_after_control_transfer_statements=1 +resharper_blank_lines_around_single_line_auto_property=1 +resharper_blank_lines_around_single_line_property=1 +resharper_blank_lines_before_single_line_comment=1 +resharper_blank_lines_between_using_groups=1 +resharper_braces_for_for=required +resharper_braces_for_foreach=required +resharper_braces_for_ifelse=required +resharper_braces_for_while=required +resharper_can_use_global_alias=false +resharper_csharp_blank_lines_around_single_line_field=1 +resharper_csharp_blank_lines_around_single_line_invocable=1 +resharper_csharp_indent_style=tab +resharper_csharp_insert_final_newline=true +resharper_csharp_keep_blank_lines_in_code=1 +resharper_csharp_keep_blank_lines_in_declarations=1 +resharper_csharp_new_line_before_while=true +resharper_csharp_use_indent_from_vs=false +resharper_csharp_wrap_arguments_style=chop_if_long +resharper_csharp_wrap_extends_list_style=chop_if_long +resharper_csharp_wrap_parameters_style=chop_if_long +resharper_css_insert_final_newline=false +resharper_enforce_line_ending_style=true +resharper_html_insert_final_newline=false +resharper_indent_nested_fixed_stmt=true +resharper_js_indent_style=tab +resharper_js_insert_final_newline=true +resharper_js_keep_blank_lines_in_code=1 +resharper_js_stick_comment=false +resharper_js_use_indent_from_vs=false +resharper_js_wrap_before_binary_opsign=true +resharper_js_wrap_chained_method_calls=chop_if_long +resharper_keep_blank_lines_between_declarations=1 +resharper_min_blank_lines_after_imports=1 +resharper_place_attribute_on_same_line=False +resharper_place_constructor_initializer_on_same_line=false +resharper_place_type_constraints_on_same_line=false +resharper_protobuf_insert_final_newline=false +resharper_qualified_using_at_nested_scope=true +resharper_resx_insert_final_newline=false +resharper_space_within_single_line_array_initializer_braces=true +resharper_use_indents_from_main_language_in_file=false +resharper_vb_insert_final_newline=false +resharper_wrap_after_declaration_lpar=true +resharper_wrap_after_invocation_lpar=true +resharper_wrap_before_extends_colon=true +resharper_wrap_before_first_type_parameter_constraint=true +resharper_wrap_before_type_parameter_langle=true +resharper_xmldoc_indent_child_elements=ZeroIndent +resharper_xmldoc_indent_text=ZeroIndent +resharper_xmldoc_insert_final_newline=false +resharper_xml_insert_final_newline=false + +# ReSharper inspection severities +resharper_check_namespace_highlighting=none +resharper_convert_to_auto_property_highlighting=none +resharper_localizable_element_highlighting=none +resharper_redundant_comma_in_attribute_list_highlighting=none +resharper_redundant_comma_in_enum_declaration_highlighting=none +resharper_redundant_comma_in_initializer_highlighting=none +resharper_string_compare_to_is_culture_specific_highlighting=none +resharper_string_index_of_is_culture_specific_1_highlighting=none +resharper_use_null_propagation_highlighting=none +resharper_use_object_or_collection_initializer_highlighting=hint +resharper_use_string_interpolation_highlighting=hint + +# Matches the exact files either package.json or .travis.yml +[{package.json,.travis.yml}] +indent_style=space +indent_size=2 +tab_width=2 + +[*.{cs,js,json,jsx,proto,resjson,ts,tsx}] +indent_style=space +indent_size=space +tab_width=4 + +[*.{asax,ascx,aspx,cshtml,css,htm,html,master,razor,skin,vb,xaml,xamlx,xoml}] +indent_style=space +indent_size=4 +tab_width=4 + +[*.{appxmanifest,build,config,csproj,dbml,discomap,dtd,jsproj,lsproj,njsproj,nuspec,proj,props,resw,resx,StyleCop,targets,tasks,vbproj,xml,xsd}] +indent_style=space +indent_size=2 +tab_width=2 + +[*.proto] +indent_style=space +indent_size=2 +tab_width=2 diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..a63eb96 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use asdf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..50364fe --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +launchSettings.json + +*~ +*.user +Directory.Build.props + +obj/ +[Bb]in/ +.vs/ +.vscode/ +.idea/ +_ReSharper.Caches/ +node_modules/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..1d4e850 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,51 @@ +stages: + - build + +default: + before_script: + - curl -sL https://deb.nodesource.com/setup_15.x | bash - + - apt-get install -y nodejs + +build: + image: mcr.microsoft.com/dotnet/sdk:5.0 + stage: build + script: + # Set up the environment. + - npx npm install --ci + - npx commitlint-gitlab-ci -x @commitlint/config-conventional + + # Build and test everything. + - dotnet restore + - dotnet build + #- 'dotnet test --test-adapter-path:. --logger:"junit;LogFilePath=../artifacts/{assembly}-test-result.xml;MethodFormat=Default;FailureBodyFormat=Verbose" --collect:"XPlat Code Coverage"' + + # Summarize the output for Gitlab CI reporting. + #- dotnet new tool-manifest + #- dotnet tool install dotnet-reportgenerator-globaltool + #- dotnet tool run reportgenerator -reports:src/*/TestResults/*/coverage.cobertura.xml -targetdir:./coverage "-reporttypes:Cobertura;TextSummary" + #- grep "Line coverage" coverage/Summary.txt + + # Perform the release. + - npx semantic-release + + rules: + - if: '$CI_COMMIT_TITLE =~ /^chore\(release\)/' + when: never + - if: '$CI_COMMIT_TAG' + when: never + - if: '$CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH' + when: never + - when: on_success + + artifacts: + when: always + paths: + - ./**/*test-result.xml + - ./coverage/Cobertura.xml + - ./coverage/Summary.* + - ./**/*.nupkg + reports: + junit: + - ./**/*test-result.xml + cobertura: + - ./coverage/Cobertura.xml diff --git a/.husky/commit-msg b/.husky/commit-msg new file mode 100755 index 0000000..e8511ea --- /dev/null +++ b/.husky/commit-msg @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +npx --no-install commitlint --edit $1 diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 0000000..f84212e --- /dev/null +++ b/.tool-versions @@ -0,0 +1,3 @@ +dotnet-core 5.0.100 +yarn 1.22.10 +nodejs 15.0.1 diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..02b160f --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) Moonfire Games + +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 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. diff --git a/Nitride.sln b/Nitride.sln new file mode 100644 index 0000000..84a253d --- /dev/null +++ b/Nitride.sln @@ -0,0 +1,264 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{570184FD-ECE4-4EC8-86E1-C1265E17D647}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nitride.Calendar", "src\Nitride.Calendar\Nitride.Calendar.csproj", "{D480943C-764D-4A8A-B546-642ED10586BB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nitride.Feeds", "src\Nitride.Feeds\Nitride.Feeds.csproj", "{1204DECC-654A-433A-BF82-53F98AB24DCF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nitride.Gemtext", "src\Nitride.Gemtext\Nitride.Gemtext.csproj", "{23C7CBF7-9624-457A-8296-C03F75BC9BC6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nitride.Generators", "src\Nitride.Generators\Nitride.Generators.csproj", "{4ACB11B7-1EEB-48E7-845A-528770839125}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nitride.Handlebars", "src\Nitride.Handlebars\Nitride.Handlebars.csproj", "{56E595A6-7880-416E-B328-93B9617F92A6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nitride.Html", "src\Nitride.Html\Nitride.Html.csproj", "{3F9292D3-DA50-4DBA-AE90-E33E462470A1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nitride.IO", "src\Nitride.IO\Nitride.IO.csproj", "{534BF940-25B2-4948-A101-7890CC9C4EA5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nitride.IO.Tests", "src\Nitride.IO.Tests\Nitride.IO.Tests.csproj", "{57F4C4C3-A2C9-4D6C-AA51-9A7EC926918D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nitride.Javascript", "src\Nitride.Javascript\Nitride.Javascript.csproj", "{285A855E-B2FC-4770-AFD9-4CC67AD3C83C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nitride.Markdown", "src\Nitride.Markdown\Nitride.Markdown.csproj", "{41FF3823-7008-43B1-AD6A-92437E0600B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nitride", "src\Nitride\Nitride.csproj", "{757BA115-3465-46C5-ADDB-7B96D6900F33}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nitride.Slugs", "src\Nitride.Slugs\Nitride.Slugs.csproj", "{17BF2A03-2C1D-4F75-9C18-B4341FAAF1A5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nitride.Temporal", "src\Nitride.Temporal\Nitride.Temporal.csproj", "{FEFFA469-245E-45A7-A094-3F0E89CEF3A9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nitride.Tests", "src\Nitride.Tests\Nitride.Tests.csproj", "{A0EED899-35C7-4C1C-8BBF-F7FD2F281839}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nitride.Yaml", "src\Nitride.Yaml\Nitride.Yaml.csproj", "{1A5B4B4D-CF32-4458-8F2C-83A2AE494837}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nitride.Yaml.Tests", "src\Nitride.Yaml.Tests\Nitride.Yaml.Tests.csproj", "{C8CD60C2-2A26-48AE-848D-A71AEE2267C9}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D480943C-764D-4A8A-B546-642ED10586BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D480943C-764D-4A8A-B546-642ED10586BB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D480943C-764D-4A8A-B546-642ED10586BB}.Debug|x64.ActiveCfg = Debug|Any CPU + {D480943C-764D-4A8A-B546-642ED10586BB}.Debug|x64.Build.0 = Debug|Any CPU + {D480943C-764D-4A8A-B546-642ED10586BB}.Debug|x86.ActiveCfg = Debug|Any CPU + {D480943C-764D-4A8A-B546-642ED10586BB}.Debug|x86.Build.0 = Debug|Any CPU + {D480943C-764D-4A8A-B546-642ED10586BB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D480943C-764D-4A8A-B546-642ED10586BB}.Release|Any CPU.Build.0 = Release|Any CPU + {D480943C-764D-4A8A-B546-642ED10586BB}.Release|x64.ActiveCfg = Release|Any CPU + {D480943C-764D-4A8A-B546-642ED10586BB}.Release|x64.Build.0 = Release|Any CPU + {D480943C-764D-4A8A-B546-642ED10586BB}.Release|x86.ActiveCfg = Release|Any CPU + {D480943C-764D-4A8A-B546-642ED10586BB}.Release|x86.Build.0 = Release|Any CPU + {1204DECC-654A-433A-BF82-53F98AB24DCF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1204DECC-654A-433A-BF82-53F98AB24DCF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1204DECC-654A-433A-BF82-53F98AB24DCF}.Debug|x64.ActiveCfg = Debug|Any CPU + {1204DECC-654A-433A-BF82-53F98AB24DCF}.Debug|x64.Build.0 = Debug|Any CPU + {1204DECC-654A-433A-BF82-53F98AB24DCF}.Debug|x86.ActiveCfg = Debug|Any CPU + {1204DECC-654A-433A-BF82-53F98AB24DCF}.Debug|x86.Build.0 = Debug|Any CPU + {1204DECC-654A-433A-BF82-53F98AB24DCF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1204DECC-654A-433A-BF82-53F98AB24DCF}.Release|Any CPU.Build.0 = Release|Any CPU + {1204DECC-654A-433A-BF82-53F98AB24DCF}.Release|x64.ActiveCfg = Release|Any CPU + {1204DECC-654A-433A-BF82-53F98AB24DCF}.Release|x64.Build.0 = Release|Any CPU + {1204DECC-654A-433A-BF82-53F98AB24DCF}.Release|x86.ActiveCfg = Release|Any CPU + {1204DECC-654A-433A-BF82-53F98AB24DCF}.Release|x86.Build.0 = Release|Any CPU + {23C7CBF7-9624-457A-8296-C03F75BC9BC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {23C7CBF7-9624-457A-8296-C03F75BC9BC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {23C7CBF7-9624-457A-8296-C03F75BC9BC6}.Debug|x64.ActiveCfg = Debug|Any CPU + {23C7CBF7-9624-457A-8296-C03F75BC9BC6}.Debug|x64.Build.0 = Debug|Any CPU + {23C7CBF7-9624-457A-8296-C03F75BC9BC6}.Debug|x86.ActiveCfg = Debug|Any CPU + {23C7CBF7-9624-457A-8296-C03F75BC9BC6}.Debug|x86.Build.0 = Debug|Any CPU + {23C7CBF7-9624-457A-8296-C03F75BC9BC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {23C7CBF7-9624-457A-8296-C03F75BC9BC6}.Release|Any CPU.Build.0 = Release|Any CPU + {23C7CBF7-9624-457A-8296-C03F75BC9BC6}.Release|x64.ActiveCfg = Release|Any CPU + {23C7CBF7-9624-457A-8296-C03F75BC9BC6}.Release|x64.Build.0 = Release|Any CPU + {23C7CBF7-9624-457A-8296-C03F75BC9BC6}.Release|x86.ActiveCfg = Release|Any CPU + {23C7CBF7-9624-457A-8296-C03F75BC9BC6}.Release|x86.Build.0 = Release|Any CPU + {4ACB11B7-1EEB-48E7-845A-528770839125}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4ACB11B7-1EEB-48E7-845A-528770839125}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4ACB11B7-1EEB-48E7-845A-528770839125}.Debug|x64.ActiveCfg = Debug|Any CPU + {4ACB11B7-1EEB-48E7-845A-528770839125}.Debug|x64.Build.0 = Debug|Any CPU + {4ACB11B7-1EEB-48E7-845A-528770839125}.Debug|x86.ActiveCfg = Debug|Any CPU + {4ACB11B7-1EEB-48E7-845A-528770839125}.Debug|x86.Build.0 = Debug|Any CPU + {4ACB11B7-1EEB-48E7-845A-528770839125}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4ACB11B7-1EEB-48E7-845A-528770839125}.Release|Any CPU.Build.0 = Release|Any CPU + {4ACB11B7-1EEB-48E7-845A-528770839125}.Release|x64.ActiveCfg = Release|Any CPU + {4ACB11B7-1EEB-48E7-845A-528770839125}.Release|x64.Build.0 = Release|Any CPU + {4ACB11B7-1EEB-48E7-845A-528770839125}.Release|x86.ActiveCfg = Release|Any CPU + {4ACB11B7-1EEB-48E7-845A-528770839125}.Release|x86.Build.0 = Release|Any CPU + {56E595A6-7880-416E-B328-93B9617F92A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {56E595A6-7880-416E-B328-93B9617F92A6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {56E595A6-7880-416E-B328-93B9617F92A6}.Debug|x64.ActiveCfg = Debug|Any CPU + {56E595A6-7880-416E-B328-93B9617F92A6}.Debug|x64.Build.0 = Debug|Any CPU + {56E595A6-7880-416E-B328-93B9617F92A6}.Debug|x86.ActiveCfg = Debug|Any CPU + {56E595A6-7880-416E-B328-93B9617F92A6}.Debug|x86.Build.0 = Debug|Any CPU + {56E595A6-7880-416E-B328-93B9617F92A6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {56E595A6-7880-416E-B328-93B9617F92A6}.Release|Any CPU.Build.0 = Release|Any CPU + {56E595A6-7880-416E-B328-93B9617F92A6}.Release|x64.ActiveCfg = Release|Any CPU + {56E595A6-7880-416E-B328-93B9617F92A6}.Release|x64.Build.0 = Release|Any CPU + {56E595A6-7880-416E-B328-93B9617F92A6}.Release|x86.ActiveCfg = Release|Any CPU + {56E595A6-7880-416E-B328-93B9617F92A6}.Release|x86.Build.0 = Release|Any CPU + {3F9292D3-DA50-4DBA-AE90-E33E462470A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3F9292D3-DA50-4DBA-AE90-E33E462470A1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3F9292D3-DA50-4DBA-AE90-E33E462470A1}.Debug|x64.ActiveCfg = Debug|Any CPU + {3F9292D3-DA50-4DBA-AE90-E33E462470A1}.Debug|x64.Build.0 = Debug|Any CPU + {3F9292D3-DA50-4DBA-AE90-E33E462470A1}.Debug|x86.ActiveCfg = Debug|Any CPU + {3F9292D3-DA50-4DBA-AE90-E33E462470A1}.Debug|x86.Build.0 = Debug|Any CPU + {3F9292D3-DA50-4DBA-AE90-E33E462470A1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3F9292D3-DA50-4DBA-AE90-E33E462470A1}.Release|Any CPU.Build.0 = Release|Any CPU + {3F9292D3-DA50-4DBA-AE90-E33E462470A1}.Release|x64.ActiveCfg = Release|Any CPU + {3F9292D3-DA50-4DBA-AE90-E33E462470A1}.Release|x64.Build.0 = Release|Any CPU + {3F9292D3-DA50-4DBA-AE90-E33E462470A1}.Release|x86.ActiveCfg = Release|Any CPU + {3F9292D3-DA50-4DBA-AE90-E33E462470A1}.Release|x86.Build.0 = Release|Any CPU + {534BF940-25B2-4948-A101-7890CC9C4EA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {534BF940-25B2-4948-A101-7890CC9C4EA5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {534BF940-25B2-4948-A101-7890CC9C4EA5}.Debug|x64.ActiveCfg = Debug|Any CPU + {534BF940-25B2-4948-A101-7890CC9C4EA5}.Debug|x64.Build.0 = Debug|Any CPU + {534BF940-25B2-4948-A101-7890CC9C4EA5}.Debug|x86.ActiveCfg = Debug|Any CPU + {534BF940-25B2-4948-A101-7890CC9C4EA5}.Debug|x86.Build.0 = Debug|Any CPU + {534BF940-25B2-4948-A101-7890CC9C4EA5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {534BF940-25B2-4948-A101-7890CC9C4EA5}.Release|Any CPU.Build.0 = Release|Any CPU + {534BF940-25B2-4948-A101-7890CC9C4EA5}.Release|x64.ActiveCfg = Release|Any CPU + {534BF940-25B2-4948-A101-7890CC9C4EA5}.Release|x64.Build.0 = Release|Any CPU + {534BF940-25B2-4948-A101-7890CC9C4EA5}.Release|x86.ActiveCfg = Release|Any CPU + {534BF940-25B2-4948-A101-7890CC9C4EA5}.Release|x86.Build.0 = Release|Any CPU + {57F4C4C3-A2C9-4D6C-AA51-9A7EC926918D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {57F4C4C3-A2C9-4D6C-AA51-9A7EC926918D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {57F4C4C3-A2C9-4D6C-AA51-9A7EC926918D}.Debug|x64.ActiveCfg = Debug|Any CPU + {57F4C4C3-A2C9-4D6C-AA51-9A7EC926918D}.Debug|x64.Build.0 = Debug|Any CPU + {57F4C4C3-A2C9-4D6C-AA51-9A7EC926918D}.Debug|x86.ActiveCfg = Debug|Any CPU + {57F4C4C3-A2C9-4D6C-AA51-9A7EC926918D}.Debug|x86.Build.0 = Debug|Any CPU + {57F4C4C3-A2C9-4D6C-AA51-9A7EC926918D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {57F4C4C3-A2C9-4D6C-AA51-9A7EC926918D}.Release|Any CPU.Build.0 = Release|Any CPU + {57F4C4C3-A2C9-4D6C-AA51-9A7EC926918D}.Release|x64.ActiveCfg = Release|Any CPU + {57F4C4C3-A2C9-4D6C-AA51-9A7EC926918D}.Release|x64.Build.0 = Release|Any CPU + {57F4C4C3-A2C9-4D6C-AA51-9A7EC926918D}.Release|x86.ActiveCfg = Release|Any CPU + {57F4C4C3-A2C9-4D6C-AA51-9A7EC926918D}.Release|x86.Build.0 = Release|Any CPU + {285A855E-B2FC-4770-AFD9-4CC67AD3C83C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {285A855E-B2FC-4770-AFD9-4CC67AD3C83C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {285A855E-B2FC-4770-AFD9-4CC67AD3C83C}.Debug|x64.ActiveCfg = Debug|Any CPU + {285A855E-B2FC-4770-AFD9-4CC67AD3C83C}.Debug|x64.Build.0 = Debug|Any CPU + {285A855E-B2FC-4770-AFD9-4CC67AD3C83C}.Debug|x86.ActiveCfg = Debug|Any CPU + {285A855E-B2FC-4770-AFD9-4CC67AD3C83C}.Debug|x86.Build.0 = Debug|Any CPU + {285A855E-B2FC-4770-AFD9-4CC67AD3C83C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {285A855E-B2FC-4770-AFD9-4CC67AD3C83C}.Release|Any CPU.Build.0 = Release|Any CPU + {285A855E-B2FC-4770-AFD9-4CC67AD3C83C}.Release|x64.ActiveCfg = Release|Any CPU + {285A855E-B2FC-4770-AFD9-4CC67AD3C83C}.Release|x64.Build.0 = Release|Any CPU + {285A855E-B2FC-4770-AFD9-4CC67AD3C83C}.Release|x86.ActiveCfg = Release|Any CPU + {285A855E-B2FC-4770-AFD9-4CC67AD3C83C}.Release|x86.Build.0 = Release|Any CPU + {41FF3823-7008-43B1-AD6A-92437E0600B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {41FF3823-7008-43B1-AD6A-92437E0600B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {41FF3823-7008-43B1-AD6A-92437E0600B7}.Debug|x64.ActiveCfg = Debug|Any CPU + {41FF3823-7008-43B1-AD6A-92437E0600B7}.Debug|x64.Build.0 = Debug|Any CPU + {41FF3823-7008-43B1-AD6A-92437E0600B7}.Debug|x86.ActiveCfg = Debug|Any CPU + {41FF3823-7008-43B1-AD6A-92437E0600B7}.Debug|x86.Build.0 = Debug|Any CPU + {41FF3823-7008-43B1-AD6A-92437E0600B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {41FF3823-7008-43B1-AD6A-92437E0600B7}.Release|Any CPU.Build.0 = Release|Any CPU + {41FF3823-7008-43B1-AD6A-92437E0600B7}.Release|x64.ActiveCfg = Release|Any CPU + {41FF3823-7008-43B1-AD6A-92437E0600B7}.Release|x64.Build.0 = Release|Any CPU + {41FF3823-7008-43B1-AD6A-92437E0600B7}.Release|x86.ActiveCfg = Release|Any CPU + {41FF3823-7008-43B1-AD6A-92437E0600B7}.Release|x86.Build.0 = Release|Any CPU + {757BA115-3465-46C5-ADDB-7B96D6900F33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {757BA115-3465-46C5-ADDB-7B96D6900F33}.Debug|Any CPU.Build.0 = Debug|Any CPU + {757BA115-3465-46C5-ADDB-7B96D6900F33}.Debug|x64.ActiveCfg = Debug|Any CPU + {757BA115-3465-46C5-ADDB-7B96D6900F33}.Debug|x64.Build.0 = Debug|Any CPU + {757BA115-3465-46C5-ADDB-7B96D6900F33}.Debug|x86.ActiveCfg = Debug|Any CPU + {757BA115-3465-46C5-ADDB-7B96D6900F33}.Debug|x86.Build.0 = Debug|Any CPU + {757BA115-3465-46C5-ADDB-7B96D6900F33}.Release|Any CPU.ActiveCfg = Release|Any CPU + {757BA115-3465-46C5-ADDB-7B96D6900F33}.Release|Any CPU.Build.0 = Release|Any CPU + {757BA115-3465-46C5-ADDB-7B96D6900F33}.Release|x64.ActiveCfg = Release|Any CPU + {757BA115-3465-46C5-ADDB-7B96D6900F33}.Release|x64.Build.0 = Release|Any CPU + {757BA115-3465-46C5-ADDB-7B96D6900F33}.Release|x86.ActiveCfg = Release|Any CPU + {757BA115-3465-46C5-ADDB-7B96D6900F33}.Release|x86.Build.0 = Release|Any CPU + {17BF2A03-2C1D-4F75-9C18-B4341FAAF1A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {17BF2A03-2C1D-4F75-9C18-B4341FAAF1A5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {17BF2A03-2C1D-4F75-9C18-B4341FAAF1A5}.Debug|x64.ActiveCfg = Debug|Any CPU + {17BF2A03-2C1D-4F75-9C18-B4341FAAF1A5}.Debug|x64.Build.0 = Debug|Any CPU + {17BF2A03-2C1D-4F75-9C18-B4341FAAF1A5}.Debug|x86.ActiveCfg = Debug|Any CPU + {17BF2A03-2C1D-4F75-9C18-B4341FAAF1A5}.Debug|x86.Build.0 = Debug|Any CPU + {17BF2A03-2C1D-4F75-9C18-B4341FAAF1A5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {17BF2A03-2C1D-4F75-9C18-B4341FAAF1A5}.Release|Any CPU.Build.0 = Release|Any CPU + {17BF2A03-2C1D-4F75-9C18-B4341FAAF1A5}.Release|x64.ActiveCfg = Release|Any CPU + {17BF2A03-2C1D-4F75-9C18-B4341FAAF1A5}.Release|x64.Build.0 = Release|Any CPU + {17BF2A03-2C1D-4F75-9C18-B4341FAAF1A5}.Release|x86.ActiveCfg = Release|Any CPU + {17BF2A03-2C1D-4F75-9C18-B4341FAAF1A5}.Release|x86.Build.0 = Release|Any CPU + {FEFFA469-245E-45A7-A094-3F0E89CEF3A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FEFFA469-245E-45A7-A094-3F0E89CEF3A9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FEFFA469-245E-45A7-A094-3F0E89CEF3A9}.Debug|x64.ActiveCfg = Debug|Any CPU + {FEFFA469-245E-45A7-A094-3F0E89CEF3A9}.Debug|x64.Build.0 = Debug|Any CPU + {FEFFA469-245E-45A7-A094-3F0E89CEF3A9}.Debug|x86.ActiveCfg = Debug|Any CPU + {FEFFA469-245E-45A7-A094-3F0E89CEF3A9}.Debug|x86.Build.0 = Debug|Any CPU + {FEFFA469-245E-45A7-A094-3F0E89CEF3A9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FEFFA469-245E-45A7-A094-3F0E89CEF3A9}.Release|Any CPU.Build.0 = Release|Any CPU + {FEFFA469-245E-45A7-A094-3F0E89CEF3A9}.Release|x64.ActiveCfg = Release|Any CPU + {FEFFA469-245E-45A7-A094-3F0E89CEF3A9}.Release|x64.Build.0 = Release|Any CPU + {FEFFA469-245E-45A7-A094-3F0E89CEF3A9}.Release|x86.ActiveCfg = Release|Any CPU + {FEFFA469-245E-45A7-A094-3F0E89CEF3A9}.Release|x86.Build.0 = Release|Any CPU + {A0EED899-35C7-4C1C-8BBF-F7FD2F281839}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A0EED899-35C7-4C1C-8BBF-F7FD2F281839}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A0EED899-35C7-4C1C-8BBF-F7FD2F281839}.Debug|x64.ActiveCfg = Debug|Any CPU + {A0EED899-35C7-4C1C-8BBF-F7FD2F281839}.Debug|x64.Build.0 = Debug|Any CPU + {A0EED899-35C7-4C1C-8BBF-F7FD2F281839}.Debug|x86.ActiveCfg = Debug|Any CPU + {A0EED899-35C7-4C1C-8BBF-F7FD2F281839}.Debug|x86.Build.0 = Debug|Any CPU + {A0EED899-35C7-4C1C-8BBF-F7FD2F281839}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A0EED899-35C7-4C1C-8BBF-F7FD2F281839}.Release|Any CPU.Build.0 = Release|Any CPU + {A0EED899-35C7-4C1C-8BBF-F7FD2F281839}.Release|x64.ActiveCfg = Release|Any CPU + {A0EED899-35C7-4C1C-8BBF-F7FD2F281839}.Release|x64.Build.0 = Release|Any CPU + {A0EED899-35C7-4C1C-8BBF-F7FD2F281839}.Release|x86.ActiveCfg = Release|Any CPU + {A0EED899-35C7-4C1C-8BBF-F7FD2F281839}.Release|x86.Build.0 = Release|Any CPU + {1A5B4B4D-CF32-4458-8F2C-83A2AE494837}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1A5B4B4D-CF32-4458-8F2C-83A2AE494837}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1A5B4B4D-CF32-4458-8F2C-83A2AE494837}.Debug|x64.ActiveCfg = Debug|Any CPU + {1A5B4B4D-CF32-4458-8F2C-83A2AE494837}.Debug|x64.Build.0 = Debug|Any CPU + {1A5B4B4D-CF32-4458-8F2C-83A2AE494837}.Debug|x86.ActiveCfg = Debug|Any CPU + {1A5B4B4D-CF32-4458-8F2C-83A2AE494837}.Debug|x86.Build.0 = Debug|Any CPU + {1A5B4B4D-CF32-4458-8F2C-83A2AE494837}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1A5B4B4D-CF32-4458-8F2C-83A2AE494837}.Release|Any CPU.Build.0 = Release|Any CPU + {1A5B4B4D-CF32-4458-8F2C-83A2AE494837}.Release|x64.ActiveCfg = Release|Any CPU + {1A5B4B4D-CF32-4458-8F2C-83A2AE494837}.Release|x64.Build.0 = Release|Any CPU + {1A5B4B4D-CF32-4458-8F2C-83A2AE494837}.Release|x86.ActiveCfg = Release|Any CPU + {1A5B4B4D-CF32-4458-8F2C-83A2AE494837}.Release|x86.Build.0 = Release|Any CPU + {C8CD60C2-2A26-48AE-848D-A71AEE2267C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C8CD60C2-2A26-48AE-848D-A71AEE2267C9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C8CD60C2-2A26-48AE-848D-A71AEE2267C9}.Debug|x64.ActiveCfg = Debug|Any CPU + {C8CD60C2-2A26-48AE-848D-A71AEE2267C9}.Debug|x64.Build.0 = Debug|Any CPU + {C8CD60C2-2A26-48AE-848D-A71AEE2267C9}.Debug|x86.ActiveCfg = Debug|Any CPU + {C8CD60C2-2A26-48AE-848D-A71AEE2267C9}.Debug|x86.Build.0 = Debug|Any CPU + {C8CD60C2-2A26-48AE-848D-A71AEE2267C9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C8CD60C2-2A26-48AE-848D-A71AEE2267C9}.Release|Any CPU.Build.0 = Release|Any CPU + {C8CD60C2-2A26-48AE-848D-A71AEE2267C9}.Release|x64.ActiveCfg = Release|Any CPU + {C8CD60C2-2A26-48AE-848D-A71AEE2267C9}.Release|x64.Build.0 = Release|Any CPU + {C8CD60C2-2A26-48AE-848D-A71AEE2267C9}.Release|x86.ActiveCfg = Release|Any CPU + {C8CD60C2-2A26-48AE-848D-A71AEE2267C9}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {D480943C-764D-4A8A-B546-642ED10586BB} = {570184FD-ECE4-4EC8-86E1-C1265E17D647} + {1204DECC-654A-433A-BF82-53F98AB24DCF} = {570184FD-ECE4-4EC8-86E1-C1265E17D647} + {23C7CBF7-9624-457A-8296-C03F75BC9BC6} = {570184FD-ECE4-4EC8-86E1-C1265E17D647} + {4ACB11B7-1EEB-48E7-845A-528770839125} = {570184FD-ECE4-4EC8-86E1-C1265E17D647} + {56E595A6-7880-416E-B328-93B9617F92A6} = {570184FD-ECE4-4EC8-86E1-C1265E17D647} + {3F9292D3-DA50-4DBA-AE90-E33E462470A1} = {570184FD-ECE4-4EC8-86E1-C1265E17D647} + {534BF940-25B2-4948-A101-7890CC9C4EA5} = {570184FD-ECE4-4EC8-86E1-C1265E17D647} + {57F4C4C3-A2C9-4D6C-AA51-9A7EC926918D} = {570184FD-ECE4-4EC8-86E1-C1265E17D647} + {285A855E-B2FC-4770-AFD9-4CC67AD3C83C} = {570184FD-ECE4-4EC8-86E1-C1265E17D647} + {41FF3823-7008-43B1-AD6A-92437E0600B7} = {570184FD-ECE4-4EC8-86E1-C1265E17D647} + {757BA115-3465-46C5-ADDB-7B96D6900F33} = {570184FD-ECE4-4EC8-86E1-C1265E17D647} + {17BF2A03-2C1D-4F75-9C18-B4341FAAF1A5} = {570184FD-ECE4-4EC8-86E1-C1265E17D647} + {FEFFA469-245E-45A7-A094-3F0E89CEF3A9} = {570184FD-ECE4-4EC8-86E1-C1265E17D647} + {A0EED899-35C7-4C1C-8BBF-F7FD2F281839} = {570184FD-ECE4-4EC8-86E1-C1265E17D647} + {1A5B4B4D-CF32-4458-8F2C-83A2AE494837} = {570184FD-ECE4-4EC8-86E1-C1265E17D647} + {C8CD60C2-2A26-48AE-848D-A71AEE2267C9} = {570184FD-ECE4-4EC8-86E1-C1265E17D647} + EndGlobalSection +EndGlobal diff --git a/Nitride.sln.DotSettings b/Nitride.sln.DotSettings new file mode 100644 index 0000000..8899e23 --- /dev/null +++ b/Nitride.sln.DotSettings @@ -0,0 +1,3 @@ + + True + True \ No newline at end of file diff --git a/Nitride.sln.Dotsettings b/Nitride.sln.Dotsettings new file mode 100644 index 0000000..06b6404 --- /dev/null +++ b/Nitride.sln.Dotsettings @@ -0,0 +1,1370 @@ + + Inherit + + + DO_NOT_SHOW + DO_NOT_SHOW + DO_NOT_SHOW + DO_NOT_SHOW + DO_NOT_SHOW + DO_NOT_SHOW + DO_NOT_SHOW + DO_NOT_SHOW + DO_NOT_SHOW + DO_NOT_SHOW + DO_NOT_SHOW + HINT + DoHide + DoHide + DoHide + DoHide + DoHide + DoHide + DoHide + DoHide + DoHide + DoHide + DoHide + DoHide + DoHide + DoHide + DoHide + DoHide + DoHide + DoHide + DoHide + DoHide + DoHide + DoHide + DoHide + HINT + <?xml version="1.0" encoding="utf-16"?><Profile name="Project"><CSRemoveCodeRedundancies>True</CSRemoveCodeRedundancies><CSMakeFieldReadonly>True</CSMakeFieldReadonly><CSUseVar><BehavourStyle>CAN_CHANGE_BOTH</BehavourStyle><LocalVariableStyle>IMPLICIT_WHEN_INITIALIZER_HAS_TYPE</LocalVariableStyle><ForeachVariableStyle>ALWAYS_EXPLICIT</ForeachVariableStyle></CSUseVar><CSShortenReferences>True</CSShortenReferences><CSReformatCode>True</CSReformatCode><CSArrangeThisQualifier>True</CSArrangeThisQualifier><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings><EmbraceInRegion>False</EmbraceInRegion><RegionName>Namespaces</RegionName></CSOptimizeUsings><CSUseAutoProperty>True</CSUseAutoProperty><CSArrangeQualifiers>True</CSArrangeQualifiers><CSEnforceVarKeywordUsageSettings>True</CSEnforceVarKeywordUsageSettings><CSCodeStyleAttributes ArrangeTypeAccessModifier="True" ArrangeTypeMemberAccessModifier="True" SortModifiers="True" RemoveRedundantParentheses="False" AddMissingParentheses="True" ArrangeBraces="False" ArrangeAttributes="True" ArrangeArgumentsStyle="True" ArrangeCodeBodyStyle="True" ArrangeVarStyle="True" /><CSMakeAutoPropertyGetOnly>True</CSMakeAutoPropertyGetOnly><FormatAttributeQuoteDescriptor>True</FormatAttributeQuoteDescriptor><CSFixBuiltinTypeReferences>True</CSFixBuiltinTypeReferences><CSUpdateFileHeader>True</CSUpdateFileHeader><JsReformatCode>True</JsReformatCode><JsFormatDocComments>True</JsFormatDocComments><CSharpFormatDocComments>True</CSharpFormatDocComments><CSReorderTypeMembers>True</CSReorderTypeMembers><JsInsertSemicolon>True</JsInsertSemicolon><HtmlReformatCode>True</HtmlReformatCode></Profile> + <?xml version="1.0" encoding="utf-16"?><Profile name="Remove Redundant Qualifiers"><CSArrangeThisQualifier>True</CSArrangeThisQualifier><CSRemoveCodeRedundancies>True</CSRemoveCodeRedundancies><CSUseAutoProperty>False</CSUseAutoProperty><CSMakeFieldReadonly>False</CSMakeFieldReadonly><CSUseVar><BehavourStyle>DISABLED</BehavourStyle><LocalVariableStyle>IMPLICIT_WHEN_INITIALIZER_HAS_TYPE</LocalVariableStyle><ForeachVariableStyle>ALWAYS_EXPLICIT</ForeachVariableStyle></CSUseVar><CSUpdateFileHeader>False</CSUpdateFileHeader><VBOptimizeImports>False</VBOptimizeImports><VBShortenReferences>False</VBShortenReferences><CSOptimizeUsings><OptimizeUsings>False</OptimizeUsings><EmbraceInRegion>False</EmbraceInRegion><RegionName /></CSOptimizeUsings><CSShortenReferences>True</CSShortenReferences><VBReformatCode>False</VBReformatCode><CSReformatCode>False</CSReformatCode><CSReorderTypeMembers>False</CSReorderTypeMembers></Profile> + Full Cleanup + Project + OPTIMAL_FILL + LF + True + False + True + Required + Required + Required + Required + 15 + OPTIMAL_FILL + False + False + False + False + False + + False + False + False + NEXT_LINE + 1 + 1 + 1 + 1 + 1 + 1 + + + NEXT_LINE + SEPARATE + ALWAYS_ADD + ALWAYS_ADD + ALWAYS_ADD + ALWAYS_ADD + ALWAYS_ADD + ALWAYS_ADD + True + Tab + NEXT_LINE + 1 + 1 + False + True + EXPANDED + NEVER + NEVER + False + False + NEVER + False + False + True + False + True + + False + True + False + False + + True + True + CHOP_IF_LONG + CHOP_IF_LONG + True + True + True + True + CHOP_IF_LONG + CHOP_IF_LONG + CHOP_IF_LONG + 80 + CHOP_IF_LONG + OPTIMAL_FILL + OPTIMAL_FILL + OPTIMAL_FILL + + Tab + True + 1 + 1 + True + 1 + False + False + True + True + True + CHOP_IF_LONG + OPTIMAL_FILL + OPTIMAL_FILL + OPTIMAL_FILL + OPTIMAL_FILL + ZeroIndent + ZeroIndent + 80 + OPTIMAL_FILL + <?xml version="1.0" encoding="utf-16"?> +<Patterns xmlns="urn:schemas-jetbrains-com:member-reordering-patterns"> + <TypePattern DisplayName="StyleCop Layout" RemoveRegions="All"> + <TypePattern.Match> + <Or> + <Kind Is="Class" /> + <Kind Is="Struct" /> + <Kind Is="Interface" /> + </Or> + </TypePattern.Match> + <Entry DisplayName="Constants"> + <Entry.Match> + <Kind Is="Constant" /> + </Entry.Match> + <Entry.SortBy> + <Access Order="Public Internal ProtectedInternal Protected Private" /> + <Name /> + </Entry.SortBy> + </Entry> + <Entry DisplayName="Static fields"> + <Entry.Match> + <And> + <Kind Is="Field" /> + <Static /> + </And> + </Entry.Match> + <Entry.SortBy> + <Kind Order="Constant Field" /> + <Access Order="Public Internal ProtectedInternal Protected Private" /> + <Readonly /> + <Name /> + </Entry.SortBy> + </Entry> + <Entry DisplayName="Fields"> + <Entry.Match> + <Kind Is="Field" /> + </Entry.Match> + <Entry.SortBy> + <Access Order="Public Internal ProtectedInternal Protected Private" /> + <Readonly /> + <Name /> + </Entry.SortBy> + </Entry> + <Entry DisplayName="Constructors and destructors" Priority="200"> + <Entry.Match> + <Or> + <Kind Is="Constructor" /> + <Kind Is="Destructor" /> + </Or> + </Entry.Match> + <Entry.SortBy> + <Static /> + <Kind Order="Constructor Destructor" /> + <Access Order="Public Internal ProtectedInternal Protected Private" /> + </Entry.SortBy> + </Entry> + <Entry DisplayName="Delegates"> + <Entry.Match> + <Kind Is="Delegate" /> + </Entry.Match> + <Entry.SortBy> + <Access Order="Public Internal ProtectedInternal Protected Private" /> + <Static /> + <Name /> + </Entry.SortBy> + </Entry> + <Entry DisplayName="Public events"> + <Entry.Match> + <And> + <Kind Is="Event" /> + <Access Is="Public" /> + </And> + </Entry.Match> + <Entry.SortBy> + <Access Order="Public" /> + <Static /> + <Name /> + </Entry.SortBy> + </Entry> + <Entry DisplayName="Interface events"> + <Entry.Match> + <And> + <Kind Is="Event" /> + <ImplementsInterface /> + </And> + </Entry.Match> + <Entry.SortBy> + <ImplementsInterface Immediate="True" /> + <Name /> + </Entry.SortBy> + </Entry> + <Entry DisplayName="Other events"> + <Entry.Match> + <Kind Is="Event" /> + </Entry.Match> + <Entry.SortBy> + <Access Order="Public Internal ProtectedInternal Protected Private" /> + <Static /> + <Name /> + </Entry.SortBy> + </Entry> + <Entry DisplayName="Enums"> + <Entry.Match> + <Kind Is="Enum" /> + </Entry.Match> + <Entry.SortBy> + <Access Order="Public Internal ProtectedInternal Protected Private" /> + <Name /> + </Entry.SortBy> + </Entry> + <Entry DisplayName="Interfaces"> + <Entry.Match> + <Kind Is="Interface" /> + </Entry.Match> + <Entry.SortBy> + <Access Order="Public Internal ProtectedInternal Protected Private" /> + <Name /> + </Entry.SortBy> + </Entry> + <Entry DisplayName="Public properties"> + <Entry.Match> + <And> + <Kind Is="Property" /> + <Access Is="Public" /> + </And> + </Entry.Match> + <Entry.SortBy> + <Access Order="Public" /> + <Static /> + <Name /> + </Entry.SortBy> + </Entry> + <Entry DisplayName="Interface properties"> + <Entry.Match> + <And> + <Kind Is="Property" /> + <ImplementsInterface /> + </And> + </Entry.Match> + <Entry.SortBy> + <ImplementsInterface Immediate="True" /> + <Name /> + </Entry.SortBy> + </Entry> + <Entry DisplayName="Other properties"> + <Entry.Match> + <Kind Is="Property" /> + </Entry.Match> + <Entry.SortBy> + <Access Order="Public Internal ProtectedInternal Protected Private" /> + <Static /> + <Name /> + </Entry.SortBy> + </Entry> + <Entry DisplayName="Public indexers" Priority="1000"> + <Entry.Match> + <And> + <Kind Is="Indexer" /> + <Access Is="Public" /> + </And> + </Entry.Match> + <Entry.SortBy> + <Access Order="Public" /> + <Static /> + <Name /> + </Entry.SortBy> + </Entry> + <Entry DisplayName="Interface indexers" Priority="1000"> + <Entry.Match> + <And> + <Kind Is="Indexer" /> + <ImplementsInterface /> + </And> + </Entry.Match> + <Entry.SortBy> + <ImplementsInterface Immediate="True" /> + <Name /> + </Entry.SortBy> + </Entry> + <Entry DisplayName="Other indexers" Priority="1000"> + <Entry.Match> + <Kind Is="Indexer" /> + </Entry.Match> + <Entry.SortBy> + <Access Order="Public Internal ProtectedInternal Protected Private" /> + <Static /> + <Name /> + </Entry.SortBy> + </Entry> + <Entry DisplayName="Public methods"> + <Entry.Match> + <And> + <Kind Is="Method" /> + <Access Is="Public" /> + </And> + </Entry.Match> + <Entry.SortBy> + <Access Order="Public" /> + <Static /> + <Name /> + </Entry.SortBy> + </Entry> + <Entry DisplayName="Interface methods"> + <Entry.Match> + <And> + <Kind Is="Method" /> + <ImplementsInterface /> + </And> + </Entry.Match> + <Entry.SortBy> + <ImplementsInterface Immediate="True" /> + <Name /> + </Entry.SortBy> + </Entry> + <Entry DisplayName="Other methods"> + <Entry.Match> + <Kind Is="Method" /> + </Entry.Match> + <Entry.SortBy> + <Access Order="Public Internal ProtectedInternal Protected Private" /> + <Static /> + <Name /> + </Entry.SortBy> + </Entry> + <Entry DisplayName="Operators"> + <Entry.Match> + <Kind Is="Operator" /> + </Entry.Match> + </Entry> + <Entry DisplayName="Nested structs" Priority="600"> + <Entry.Match> + <Kind Is="Struct" /> + </Entry.Match> + <Entry.SortBy> + <Static /> + <Access Order="Public Internal ProtectedInternal Protected Private" /> + <Name /> + </Entry.SortBy> + </Entry> + <Entry DisplayName="Nested classes" Priority="700"> + <Entry.Match> + <Kind Is="Class" /> + </Entry.Match> + <Entry.SortBy> + <Static /> + <Access Order="Public Internal ProtectedInternal Protected Private" /> + <Name /> + </Entry.SortBy> + </Entry> + <Entry DisplayName="All other members" /> + </TypePattern> +</Patterns> + <?xml version="1.0" encoding="utf-8" ?> + +<!-- +I. Overall + +I.1 Each pattern can have <Match>....</Match> element. For the given type declaration, the pattern with the match, evaluated to 'true' with the largest weight, will be used +I.2 Each pattern consists of the sequence of <Entry>...</Entry> elements. Type member declarations are distributed between entries +I.3 If pattern has RemoveAllRegions="true" attribute, then all regions will be cleared prior to reordering. Otherwise, only auto-generated regions will be cleared +I.4 The contents of each entry is sorted by given keys (First key is primary, next key is secondary, etc). Then the declarations are grouped and en-regioned by given property + +II. Available match operands + +Each operand may have Weight="..." attribute. This weight will be added to the match weight if the operand is evaluated to 'true'. +The default weight is 1 + +II.1 Boolean functions: +II.1.1 <And>....</And> +II.1.2 <Or>....</Or> +II.1.3 <Not>....</Not> + +II.2 Operands +II.2.1 <Kind Is="..."/>. Kinds are: class, struct, interface, enum, delegate, type, constructor, destructor, property, indexer, method, operator, field, constant, event, member +II.2.2 <Name Is="..." [IgnoreCase="true/false"] />. The 'Is' attribute contains regular expression +II.2.3 <HasAttribute CLRName="..." [Inherit="true/false"] />. The 'CLRName' attribute contains regular expression +II.2.4 <Access Is="..."/>. The 'Is' values are: public, protected, internal, protected internal, private +II.2.5 <Static/> +II.2.6 <Abstract/> +II.2.7 <Virtual/> +II.2.8 <Override/> +II.2.9 <Sealed/> +II.2.10 <Readonly/> +II.2.11 <ImplementsInterface CLRName="..."/>. The 'CLRName' attribute contains regular expression +II.2.12 <HandlesEvent /> +--> + +<Patterns xmlns="urn:shemas-jetbrains-com:member-reordering-patterns"> + + <!--Do not reorder COM interfaces--> + <Pattern> + <Match> + <And Weight="100"> + <Kind Is="interface"/> + <HasAttribute CLRName="System.Runtime.InteropServices.InterfaceTypeAttribute"/> + </And> + </Match> + </Pattern> + + <!--Default pattern--> + <Pattern> + + <!--public delegate--> + <Entry> + <Match> + <And Weight="100"> + <Access Is="public"/> + <Kind Is="delegate"/> + </And> + </Match> + <Sort> + <Name/> + </Sort> + <Group Region="Delegates"/> + </Entry> + + <!--public enum--> + <Entry> + <Match> + <And Weight="100"> + <Access Is="public"/> + <Kind Is="enum"/> + </And> + </Match> + <Sort> + <Name/> + </Sort> + <Group> + <Name Region="${Name} enum"/> + </Group> + </Entry> + + <!--fields and constants--> + <Entry> + <Match> + <Or> + <Kind Is="constant"/> + <Kind Is="field"/> + </Or> + </Match> + <Sort> + <Name/> + </Sort> + </Entry> + + <!--Constructors. Place static one first--> + <Entry> + <Match> + <Kind Is="constructor"/> + </Match> + <Sort> + <Static/> + </Sort> + </Entry> + + <!--properties, indexers--> + <Entry> + <Match> + <Or> + <Kind Is="property"/> + <Kind Is="indexer"/> + </Or> + </Match> + <Sort> + <Name/> + </Sort> + </Entry> + + <!--all other members--> + <Entry> + <Sort> + <Name/> + </Sort> + </Entry> + + <!--nested types--> + <Entry> + <Match> + <Kind Is="type"/> + </Match> + <Sort> + <Name/> + </Sort> + <Group> + <Name Region="Nested type: ${Name}"/> + </Group> + </Entry> + </Pattern> + +</Patterns> + + CustomLayout + False + False + True + UseExplicitType + UseVarWhenEvident + UseVarWhenEvident + True + + False + + + ATI + CUSIP + + + + + IO + IP + + + + + PDF + RPC + RPO + XML + On$event$ + exception + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="I" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb_AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="" Suffix="" Style="aaBb" /></Policy> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="T" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="I" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="T" Suffix="" Style="AaBb" /> + + + + + + + + $object$_On$event$ + exception + <Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="I" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /> + <Policy Inspect="True" Prefix="m_" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="m_" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /> + <Policy Inspect="True" Prefix="T" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy><Descriptor Staticness="Static, Instance" AccessRightKinds="Private" Description="Private Events"><ElementKinds><Kind Name="EVENT" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="m_" Suffix="" Style="AaBb" /></Policy> + <Policy><Descriptor Staticness="Static, Instance" AccessRightKinds="Private, Protected, ProtectedInternal, Internal" Description="Dual rules for readonly/constants"><ElementKinds><Kind Name="READONLY_FIELD" /><Kind Name="CONSTANT_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB"><ExtraRule Prefix="m_" Suffix="" Style="AaBb" /></Policy></Policy> + <Policy><Descriptor Staticness="Static, Instance" AccessRightKinds="Public" Description="Dual rules for public fields"><ElementKinds><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="" Suffix="" Style="AA_BB" /></Policy></Policy> + <Policy><Descriptor Staticness="Static, Instance" AccessRightKinds="Public" Description="Dual rules for public fields"><ElementKinds><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="" Suffix="" Style="AA_BB" /></Policy></Policy> + <Policy><Descriptor Staticness="Static, Instance" AccessRightKinds="Private, Protected, ProtectedInternal, Internal" Description="Dual rules for readonly/constants"><ElementKinds><Kind Name="CONSTANT_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB"><ExtraRule Prefix="m_" Suffix="" Style="AaBb" /></Policy></Policy> + <Policy><Descriptor Staticness="Static, Instance" AccessRightKinds="Private" Description="Private Events"><ElementKinds><Kind Name="EVENT" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="m_" Suffix="" Style="AaBb" /></Policy> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + C:\Users\rc04727\AppData\Local\JetBrains\Transient\ReSharperPlatformVs15\v11_ede7cb44\SolutionCaches + LIVE_MONITOR + LIVE_MONITOR + DO_NOTHING + LIVE_MONITOR + LIVE_MONITOR + LIVE_MONITOR + LIVE_MONITOR + LIVE_MONITOR + LIVE_MONITOR + LIVE_MONITOR + LIVE_MONITOR + DO_NOTHING + LIVE_MONITOR + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + + Always + False + ForWarningsOrErrors + tag + 10 + ( ) + 14 + 15 + #region(WEI) + 4 + 16 + + False + + using + False + 0 + NUnit Test + 1 + Class (WEI) + False + + 2 + 4 + 3 + True + True + Events + Imported 1/13/2012 + Create Thread-Safe Event + True + Event with Synchronous Invoke + True + public event $EventHandler$<$EventArgs$> $EventName$ +{ + add + { + lock(m_EventLock) + { + m_$PrivateEventVariable$Event += value; + } + } + remove + { + lock (m_EventLock) + { + m_$PrivateEventVariable$Event -= value; + } + } +} +$END$ + +private event $EventHandler$<$EventArgs$> m_$PrivateEventVariable$Event; + +/// <summary> +/// Raises the $EventName$ event synchronously. +/// </summary> +/// <param name="args">The <see cref="System.EventArgs"/> instance containing the event data.</param> +private void Raise$EventName$($EventArgs$ args) +{ + $EventHandler$<$EventArgs$> handler = m_$PrivateEventVariable$Event; + if (handler != null) + { + handler(this, args); + } +} + + True + 1 + True + 0 + True + 2 + True + capitalize(EventName) + 3 + True + 2.0 + InCSharpTypeMember + True + True + cs + Test + True + NUnit Test + True + True + #region Namespaces + +using System; + +using NUnit.Framework; + +#endregion + +namespace $NameSpace$ +{ + [TestFixture] + public class $TestClass$ + { + #region Public Methods + + #region Test Administration + + [TestFixtureSetUp] + public void TestFixtureSetup() + { + } + + [TestFixtureTearDown] + public void TestFixtureTeardown() + { + } + + [SetUp] + public void Setup() + { + } + + [TearDown] + public void Teardown() + { + } + + #endregion Test Administration + + #region Test Methods + + #endregion Test Methods + + #endregion Public Methods + + #region Private Fields + + #endregion Private Fields + + #region Private Methods + + #endregion Private Methods + } +} + True + 0 + True + 1 + True + InCSharpProjectFile + True + True + IDisposable Implementation for a Base Class + True + IDisposable Base Class + True + #region IDisposable Implementation + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + // NOTE: Leave out the finalizer altogether if this class doesn't own + // unmanaged resources itself, but leave the other methods + // exactly as they are. + + ~$ClassName$() + { + Dispose(false); + } + + + protected virtual void Dispose(bool disposing) + { + // Even if we don't have a finalizer, we need to check the disposing argument. + // It might still be false if we're being finalized due to the fact that a derived + // class introduced a finalizer. + + if (disposing) + { + // free managed resources + } + + // free native resources if there are any. + } + +#endregion IDisposable Implementation + + True + 0 + True + 2.0 + InCSharpTypeMember + True + True + Imported 1/13/2012 + WEI Standard + cs + Class + False + WEI Class + True + Class (WEI) + True + #region Namespaces + +using System; + +#endregion Namespaces + +namespace $NAMESPACE$ +{ + + public class $CLASS$ + { + #region Constructors + public $CLASS$() + { + $END$ + } + + #endregion Constructors + + #region Public Properties + #endregion Public Properties + + #region Public Methods + #endregion Public Methods + + #region Private Fields + #endregion Private Fields + + #region Private Methods + #endregion Private Methods + + } + +} + True + getFileNameWithoutExtension() + 0 + True + fileDefaultNamespace() + 1 + True + InCSharpProjectFile + True + True + Imported 1/13/2012 + Methods + Unit Test Method + True + Test Method + True + [Test] +[Category("")] +public void $MethodUnderTest$_$Scenario$_$ExpectedResult$() +{ + // Arrange +$END$ + + // Act + + // Assert +} + True + 2 + True + 0 + True + 1 + True + 2.0 + InCSharpTypeMember + True + True + Imported 1/13/2012 + LLBLGen Templates + Insert a Using(DataAccessAdapter) + True + UDAA + True + using(DataAccessAdapter dataAccessAdapter = new DataAccessAdapter(ConnectionString.GetString("connString"), true)) +{ + $END$ +} + True + 2.0 + InCSharpStatement + True + True + ( ) + True + True + ($SELECTION$) + True + 2.0 + InCSharpExpression + True + True + Inserts a strongly-type identifier property. + True + 0 + True + getCurrentTime("yyyy_MM_dd_HHmmss") + -1 + 1 + True + True + 2.0 + InCSharpFile + InsertIdProperty + True + /// <inheritdoc /> + public override $Type$Id $Type$Id => new $Type$Id($When$); + + True + True + Imported 1/13/2012 + LLBLGen Templates + Retrieve Collection with a Filter + True + FetchCollection + True + EntityCollection $ResultCollectionName$Collection = new EntityCollection(new $EntityType$EntityFactory()); +using(DataAccessAdapter dataAccessAdapter = new DataAccessAdapter(ConnectionString.GetString("connString"), true)) +{ +RelationPredicateBucket filterBucket = new RelationPredicateBucket(); +IPredicateExpression predicateExpression = new PredicateExpression(); +predicateExpression.Add(PredicateFactory.CompareValue($EntityType$FieldIndex.$FilterField$, ComparisonOperator.Equal, $FilterValue$)); +filterBucket.PredicateExpression.Add(predicateExpression); + +dataAccessAdapter.FetchEntityCollection($ResultCollectionName$Collection, filterBucket); +} +$END$ + True + 0 + True + complete() + 2 + True + 3 + True + decapitalize(EntityType) + 1 + True + 2.0 + InCSharpStatement + True + True + Inserts a strongly-typed identifier. + True + 0 + False + + + + True + getCurrentTime("yyyy_MM_dd_HHmmss") + -1 + 1 + True + True + 2.0 + InCSharpFile + InsertId + True + new $Type$Id($When$) + True + True + Calls the session server to validate a user + True + ValidateUser + True + SessionServerFactory.GetSessionServer().IsValid(requestorInformation.SessionId); + True + 2.0 + InCSharpStatement + True + True + Imported 1/13/2012 + LLBLGen Templates + Retrieve Single Entity with a Filter + True + FetchEntityWithFilter + True + EntityCollection $CollectionType$Collection = new EntityCollection(new $EntityType$EntityFactory()); +using(DataAccessAdapter dataAccessAdapter = new DataAccessAdapter(ConnectionString.GetString("connString"), true)) +{ +RelationPredicateBucket filterBucket = new RelationPredicateBucket(); +IPredicateExpression predicateExpression = new PredicateExpression(); +predicateExpression.Add(PredicateFactory.CompareValue($EntityType$FieldIndex.$FilterField$, ComparisonOperator.Equal, $FilterValue$)); +filterBucket.PredicateExpression.Add(predicateExpression); + +dataAccessAdapter.FetchEntityCollection($CollectionType$Collection, filterBucket); +} +$EntityType$Entity $EntityObject$ = null; +if ($CollectionType$Collection.Count > 0) +$EntityObject$ = ($EntityType$Entity)$CollectionType$Collection[0]; +$END$ + True + decapitalize(EntityType) + 1 + True + suggestVariableName() + 4 + True + 0 + True + complete() + 2 + True + 3 + True + 2.0 + InCSharpStatement + True + True + Events + Imported 1/13/2012 + Create a thread-safe event with an asynchronous raise method + True + Event with Asynchronous Invoke + True + public event $EventHandler$<$EventArgs$> $EventName$ +{ + add + { + lock(m_EventLock) + { + m_$PrivateEventVariable$Event += value; + } + } + remove + { + lock (m_EventLock) + { + m_$PrivateEventVariable$Event -= value; + } + } +} +$END$ + +private event $EventHandler$<$EventArgs$> m_$PrivateEventVariable$Event; + +/// <summary> +/// Raises the $EventName$ event Asynchronously. +/// </summary> +/// <param name="args">The <see cref="System.EventArgs"/> instance containing the event data.</param> +private void Raise$EventName$Async($EventArgs$ args) +{ + Delegate[] subscribers = null; + lock (m_EventLock) + { + if (m_$PrivateEventVariable$Event != null) + { + subscribers = m_$PrivateEventVariable$Event.GetInvocationList(); + } + } + + if (subscribers != null) + { + foreach ($EventHandler$ subscriber in subscribers) + { + subscriber.BeginInvoke(this, args, null, null); + } + } +} + + True + 1 + True + 0 + True + 2 + True + capitalize(EventName) + 3 + True + 2.0 + InCSharpTypeMember + True + True + Imported 1/13/2012 + LLBLGen Templates + Fetch an entity with a joined relation + True + FetchEntityWithJoin + True + EntityCollection $CollectionType$Collection = new EntityCollection(new $EntityType1$EntityFactory()); +using (DataAccessAdapter dataAccessAdapter = new DataAccessAdapter(ConnectionString.GetString("connString"), true)) +{ + RelationPredicateBucket filterBucket = new RelationPredicateBucket(); + + // Filter The First Entity on a Field Value + IPredicateExpression predicateExpression = new PredicateExpression(); + predicateExpression.Add(PredicateFactory.CompareValue($EntityType1$FieldIndex.$FilterField1$, ComparisonOperator.Equal, $FilterValue1$)); + filterBucket.PredicateExpression.Add(predicateExpression); + + // Add the Join Relation + filterBucket.Relations.Add($EntityType1$Entity.Relations.$EntityRelationship$); + + // Filter The Second Entity on a Field Value + IPredicateExpression predicateExpression$EntityType2$ = new PredicateExpression(); + predicateExpression$EntityType2$.Add(PredicateFactory.CompareValue($EntityType2$FieldIndex.$FilterField2$, ComparisonOperator.Equal, $FilterValue2$)); + filterBucket.PredicateExpression.Add(predicateExpressionIdentifier); + + // Prefetch Joined Entity + IPrefetchPath2 prefetchPath = new PrefetchPath2((int)EntityType.$EntityType1$Entity); + prefetchPath.Add($EntityType1$Entity.PrefetchPath$EntityType2$, 0, predicateExpression$EntityType2$, new $EntityType2$EntityFactory()); + + dataAccessAdapter.FetchEntityCollection($CollectionType$Collection, filterBucket, prefetchPath); +} + +$EntityType1$Entity $EntityObject$; +if ($CollectionType$Collection.Count > 0) +{ + $EntityObject$ = ($EntityType1$Entity)$CollectionType$Collection[0]; + // Use the values in $EntityObject$ and the related entity $EntityType2$ +} + + True + decapitalize(EntityType1) + 0 + True + suggestVariableName() + 8 + True + complete() + 4 + True + 1 + True + 5 + True + complete() + 2 + True + complete() + 6 + True + 3 + True + 7 + True + 2.0 + InCSharpStatement + False + + + + + + + + + + + + + + + + False + + + + + + + + + + + + + + True + True + Imported 1/13/2012 + LLBLGen Templates + Fetch selected columns from single Entity + True + FetchSelectedColumns + True + using(DataAccessAdapter dataAccessAdapter = new DataAccessAdapter(ConnectionString.GetString("connString"), true)) +{ + ResultsetFields resultsetFields = new ResultsetFields($NUMBEROFFIELDS$); + int fieldIndex = 0; + resultsetFields.DefineField($ENTITY_TYPE$Fields.$FIELD1$, fieldIndex++); + resultsetFields.DefineField($ENTITY_TYPE$Fields.$FIELD2$, fieldIndex++); + // Add more fields as needed. Number of added fields must match parameter in constructor for ResultSetFields + + DataTable resultSet = new DataTable(); + dataAccessAdapter.FetchTypedList(resultsetFields, resultSet, null, $DISTINCT_TRUE_FALSE$); +} +$END$ + + True + 4 + True + 1 + True + 2 + True + 3 + True + 0 + True + 2.0 + InCSharpStatement + True + True + Imported 1/13/2012 + LLBLGen Templates + Retrieve an Entity by its Primary Key + True + FetchEntity + True + $EntityType$Entity $EntityObject$ = new $EntityType$Entity($PrimaryKeyValue$); +bool found; +using(DataAccessAdapter dataAccessAdapter = new DataAccessAdapter(ConnectionString.GetString("connString"), true)) +{ +found = dataAccessAdapter.FetchEntity($EntityObject$); +} +$END$ + True + suggestVariableName() + 1 + True + 0 + True + 2 + True + 2.0 + InCSharpStatement + True + True + Imported 1/13/2012 + LLBLGen Templates + Delete an entity + True + DeleteEntity + True + $EntityType$Entity $EntityObject$ = new $EntityType$Entity($PrimaryKeyValue$); +bool found; +using(DataAccessAdapter dataAccessAdapter = new DataAccessAdapter(ConnectionString.GetString("connString"), true)) +{ +found = dataAccessAdapter.DeleteEntity($EntityObject$); +} +$END$ + True + 1 + True + 0 + True + 2 + True + 2.0 + InCSharpStatement + True + True + Imported 1/13/2012 + LLBLGen Templates + Insert an Entity with LLBLGen + True + InsertEntity + True + $EntityType$Entity $EntityObject$ = new $EntityType$Entity(); +//$EntityObject$.fieldName1 = fieldValue1; + +using(DataAccessAdapter dataAccessAdapter = new DataAccessAdapter(ConnectionString.GetString("connString"), true)) +{ + dataAccessAdapter.SaveEntity($EntityObject$); + // newIdentityKey = $EntityObject$.IdentityKeyField; +} + + True + suggestVariableName() + 1 + True + 0 + True + 2.0 + InCSharpStatement + True + diff --git a/NuGet.Config b/NuGet.Config new file mode 100644 index 0000000..af8eac9 --- /dev/null +++ b/NuGet.Config @@ -0,0 +1,7 @@ + + + + + + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..f94878b --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +Nitride CIL +=========== + +A static site generator based on the [Gallium ECS](https://gitlab.com/mfgames-cil/gallium-cil/). diff --git a/commitlint.config.js b/commitlint.config.js new file mode 100644 index 0000000..db74d15 --- /dev/null +++ b/commitlint.config.js @@ -0,0 +1,3 @@ +module.exports = { + extends: ["@commitlint/config-conventional"], +}; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..adf313c --- /dev/null +++ b/package-lock.json @@ -0,0 +1,12304 @@ +{ + "name": "gallium-cil", + "version": "1.0.1", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "version": "1.0.1", + "devDependencies": { + "@commitlint/cli": "^13.1.0", + "@commitlint/config-conventional": "^13.1.0", + "@semantic-release/changelog": "^5.0.1", + "@semantic-release/git": "^9.0.0", + "@semantic-release/gitlab": "^6.2.2", + "@semantic-release/npm": "^7.1.3", + "commitlint-gitlab-ci": "^0.0.4", + "husky": "^7.0.2", + "semantic-release": "^17.4.7", + "semantic-release-dotnet": "^1.0.0", + "semantic-release-nuget": "^1.1.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.12.11", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/highlight": "^7.10.4" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.12.11", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/highlight": { + "version": "7.10.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "node_modules/@commitlint/cli": { + "version": "13.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/format": "^13.1.0", + "@commitlint/lint": "^13.1.0", + "@commitlint/load": "^13.1.0", + "@commitlint/read": "^13.1.0", + "@commitlint/types": "^13.1.0", + "lodash": "^4.17.19", + "resolve-from": "5.0.0", + "resolve-global": "1.0.0", + "yargs": "^17.0.0" + }, + "bin": { + "commitlint": "cli.js" + }, + "engines": { + "node": ">=v12" + } + }, + "node_modules/@commitlint/config-conventional": { + "version": "13.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "conventional-changelog-conventionalcommits": "^4.3.1" + }, + "engines": { + "node": ">=v12" + } + }, + "node_modules/@commitlint/ensure": { + "version": "13.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^13.1.0", + "lodash": "^4.17.19" + }, + "engines": { + "node": ">=v12" + } + }, + "node_modules/@commitlint/execute-rule": { + "version": "13.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=v12" + } + }, + "node_modules/@commitlint/format": { + "version": "13.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^13.1.0", + "chalk": "^4.0.0" + }, + "engines": { + "node": ">=v12" + } + }, + "node_modules/@commitlint/format/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@commitlint/is-ignored": { + "version": "13.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^13.1.0", + "semver": "7.3.5" + }, + "engines": { + "node": ">=v12" + } + }, + "node_modules/@commitlint/is-ignored/node_modules/semver": { + "version": "7.3.5", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@commitlint/lint": { + "version": "13.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/is-ignored": "^13.1.0", + "@commitlint/parse": "^13.1.0", + "@commitlint/rules": "^13.1.0", + "@commitlint/types": "^13.1.0" + }, + "engines": { + "node": ">=v12" + } + }, + "node_modules/@commitlint/load": { + "version": "13.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/execute-rule": "^13.0.0", + "@commitlint/resolve-extends": "^13.0.0", + "@commitlint/types": "^13.1.0", + "chalk": "^4.0.0", + "cosmiconfig": "^7.0.0", + "lodash": "^4.17.19", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=v12" + } + }, + "node_modules/@commitlint/load/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@commitlint/message": { + "version": "13.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=v12" + } + }, + "node_modules/@commitlint/parse": { + "version": "13.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^13.1.0", + "conventional-changelog-angular": "^5.0.11", + "conventional-commits-parser": "^3.0.0" + }, + "engines": { + "node": ">=v12" + } + }, + "node_modules/@commitlint/read": { + "version": "13.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/top-level": "^13.0.0", + "@commitlint/types": "^13.1.0", + "fs-extra": "^10.0.0", + "git-raw-commits": "^2.0.0" + }, + "engines": { + "node": ">=v12" + } + }, + "node_modules/@commitlint/read/node_modules/fs-extra": { + "version": "10.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@commitlint/resolve-extends": { + "version": "13.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "import-fresh": "^3.0.0", + "lodash": "^4.17.19", + "resolve-from": "^5.0.0", + "resolve-global": "^1.0.0" + }, + "engines": { + "node": ">=v12" + } + }, + "node_modules/@commitlint/rules": { + "version": "13.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/ensure": "^13.1.0", + "@commitlint/message": "^13.0.0", + "@commitlint/to-lines": "^13.0.0", + "@commitlint/types": "^13.1.0", + "execa": "^5.0.0" + }, + "engines": { + "node": ">=v12" + } + }, + "node_modules/@commitlint/to-lines": { + "version": "13.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=v12" + } + }, + "node_modules/@commitlint/top-level": { + "version": "13.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^5.0.0" + }, + "engines": { + "node": ">=v12" + } + }, + "node_modules/@commitlint/top-level/node_modules/find-up": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/top-level/node_modules/locate-path": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/top-level/node_modules/p-limit": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/top-level/node_modules/p-locate": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@commitlint/top-level/node_modules/path-exists": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@commitlint/types": { + "version": "13.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0" + }, + "engines": { + "node": ">=v12" + } + }, + "node_modules/@commitlint/types/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.4", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.4", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@octokit/auth-token": { + "version": "2.4.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^6.0.0" + } + }, + "node_modules/@octokit/core": { + "version": "3.2.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/auth-token": "^2.4.4", + "@octokit/graphql": "^4.5.8", + "@octokit/request": "^5.4.12", + "@octokit/types": "^6.0.3", + "before-after-hook": "^2.1.0", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/endpoint": { + "version": "6.0.10", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^6.0.0", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/graphql": { + "version": "4.5.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/request": "^5.3.0", + "@octokit/types": "^6.0.0", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "3.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "2.8.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^6.4.0" + }, + "peerDependencies": { + "@octokit/core": ">=2" + } + }, + "node_modules/@octokit/plugin-request-log": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@octokit/core": ">=3" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "4.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^6.1.0", + "deprecation": "^2.3.1" + }, + "peerDependencies": { + "@octokit/core": ">=3" + } + }, + "node_modules/@octokit/request": { + "version": "5.4.12", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/endpoint": "^6.0.1", + "@octokit/request-error": "^2.0.0", + "@octokit/types": "^6.0.3", + "deprecation": "^2.0.0", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.1", + "once": "^1.4.0", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/request-error": { + "version": "2.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^6.0.0", + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, + "node_modules/@octokit/rest": { + "version": "18.0.12", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/core": "^3.2.3", + "@octokit/plugin-paginate-rest": "^2.6.2", + "@octokit/plugin-request-log": "^1.0.2", + "@octokit/plugin-rest-endpoint-methods": "4.4.1" + } + }, + "node_modules/@octokit/types": { + "version": "6.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^3.1.1", + "@types/node": ">= 8" + } + }, + "node_modules/@semantic-release/changelog": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@semantic-release/error": "^2.1.0", + "aggregate-error": "^3.0.0", + "fs-extra": "^9.0.0", + "lodash": "^4.17.4" + }, + "engines": { + "node": ">=10.18" + }, + "peerDependencies": { + "semantic-release": ">=15.8.0 <18.0.0" + } + }, + "node_modules/@semantic-release/commit-analyzer": { + "version": "8.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "conventional-changelog-angular": "^5.0.0", + "conventional-commits-filter": "^2.0.0", + "conventional-commits-parser": "^3.0.7", + "debug": "^4.0.0", + "import-from": "^3.0.0", + "lodash": "^4.17.4", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=10.18" + }, + "peerDependencies": { + "semantic-release": ">=16.0.0 <18.0.0" + } + }, + "node_modules/@semantic-release/error": { + "version": "2.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@semantic-release/git": { + "version": "9.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@semantic-release/error": "^2.1.0", + "aggregate-error": "^3.0.0", + "debug": "^4.0.0", + "dir-glob": "^3.0.0", + "execa": "^4.0.0", + "lodash": "^4.17.4", + "micromatch": "^4.0.0", + "p-reduce": "^2.0.0" + }, + "engines": { + "node": ">=10.18" + }, + "peerDependencies": { + "semantic-release": ">=16.0.0 <18.0.0" + } + }, + "node_modules/@semantic-release/git/node_modules/execa": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/@semantic-release/github": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/rest": "^18.0.0", + "@semantic-release/error": "^2.2.0", + "aggregate-error": "^3.0.0", + "bottleneck": "^2.18.1", + "debug": "^4.0.0", + "dir-glob": "^3.0.0", + "fs-extra": "^9.0.0", + "globby": "^11.0.0", + "http-proxy-agent": "^4.0.0", + "https-proxy-agent": "^5.0.0", + "issue-parser": "^6.0.0", + "lodash": "^4.17.4", + "mime": "^2.4.3", + "p-filter": "^2.0.0", + "p-retry": "^4.0.0", + "url-join": "^4.0.0" + }, + "engines": { + "node": ">=10.18" + }, + "peerDependencies": { + "semantic-release": ">=16.0.0 <18.0.0" + } + }, + "node_modules/@semantic-release/gitlab": { + "version": "6.2.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@semantic-release/error": "^2.2.0", + "aggregate-error": "^3.0.0", + "debug": "^4.0.0", + "dir-glob": "^3.0.0", + "escape-string-regexp": "^3.0.0", + "form-data": "^3.0.0", + "fs-extra": "^9.0.0", + "globby": "^11.0.0", + "got": "^10.5.2", + "lodash": "^4.17.11", + "parse-path": "^4.0.0", + "url-join": "^4.0.0" + }, + "engines": { + "node": ">=10.18" + }, + "peerDependencies": { + "semantic-release": ">=15.8.0 <18.0.0" + } + }, + "node_modules/@semantic-release/gitlab/node_modules/escape-string-regexp": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@semantic-release/npm": { + "version": "7.1.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@semantic-release/error": "^2.2.0", + "aggregate-error": "^3.0.0", + "execa": "^5.0.0", + "fs-extra": "^10.0.0", + "lodash": "^4.17.15", + "nerf-dart": "^1.0.0", + "normalize-url": "^6.0.0", + "npm": "^7.0.0", + "rc": "^1.2.8", + "read-pkg": "^5.0.0", + "registry-auth-token": "^4.0.0", + "semver": "^7.1.2", + "tempy": "^1.0.0" + }, + "engines": { + "node": ">=10.19" + }, + "peerDependencies": { + "semantic-release": ">=16.0.0 <18.0.0" + } + }, + "node_modules/@semantic-release/npm/node_modules/fs-extra": { + "version": "10.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@semantic-release/npm/node_modules/semver": { + "version": "7.3.4", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@semantic-release/release-notes-generator": { + "version": "9.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "conventional-changelog-angular": "^5.0.0", + "conventional-changelog-writer": "^4.0.0", + "conventional-commits-filter": "^2.0.0", + "conventional-commits-parser": "^3.0.0", + "debug": "^4.0.0", + "get-stream": "^5.0.0", + "import-from": "^3.0.0", + "into-stream": "^5.0.0", + "lodash": "^4.17.4", + "read-pkg-up": "^7.0.0" + }, + "engines": { + "node": ">=10.18" + }, + "peerDependencies": { + "semantic-release": ">=15.8.0 <18.0.0" + } + }, + "node_modules/@sindresorhus/is": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/@types/cacheable-request": { + "version": "6.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "*", + "@types/node": "*", + "@types/responselike": "*" + } + }, + "node_modules/@types/glob": { + "version": "7.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/keyv": { + "version": "3.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/minimatch": { + "version": "3.0.5", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/minimist": { + "version": "1.2.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "14.14.22", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/parse-json": { + "version": "4.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/responselike": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/retry": { + "version": "0.12.0", + "dev": true, + "license": "MIT" + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ansicolors": { + "version": "0.3.2", + "dev": true, + "license": "MIT" + }, + "node_modules/argv-formatter": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/array-ify": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/array-union": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/arrify": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/before-after-hook": { + "version": "2.1.0", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/bottleneck": { + "version": "2.19.5", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cacheable-lookup": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/keyv": "^3.1.1", + "keyv": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cacheable-request": { + "version": "7.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-keys": { + "version": "6.2.2", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/camelcase-keys/node_modules/camelcase": { + "version": "5.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-keys/node_modules/map-obj": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cardinal": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansicolors": "~0.3.2", + "redeyed": "~2.1.0" + }, + "bin": { + "cdl": "bin/cdl.js" + } + }, + "node_modules/chalk": { + "version": "2.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk/node_modules/ansi-styles": { + "version": "3.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk/node_modules/color-convert": { + "version": "1.9.3", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/chalk/node_modules/color-name": { + "version": "1.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/chalk/node_modules/has-flag": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "5.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-table": { + "version": "0.3.6", + "dev": true, + "dependencies": { + "colors": "1.0.3" + }, + "engines": { + "node": ">= 0.2.0" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/clone-response": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^1.0.0" + } + }, + "node_modules/clone-response/node_modules/mimic-response": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/colors": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commitlint-gitlab-ci": { + "version": "0.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/cli": "^13.1.0" + }, + "bin": { + "commitlint-gitlab-ci": "bin/commitlint-gitlab-ci.sh" + } + }, + "node_modules/compare-func": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/conventional-changelog-angular": { + "version": "5.0.12", + "dev": true, + "license": "ISC", + "dependencies": { + "compare-func": "^2.0.0", + "q": "^1.5.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-conventionalcommits": { + "version": "4.5.0", + "dev": true, + "license": "ISC", + "dependencies": { + "compare-func": "^2.0.0", + "lodash": "^4.17.15", + "q": "^1.5.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-writer": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "compare-func": "^2.0.0", + "conventional-commits-filter": "^2.0.7", + "dateformat": "^3.0.0", + "handlebars": "^4.7.6", + "json-stringify-safe": "^5.0.1", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "semver": "^6.0.0", + "split": "^1.0.0", + "through2": "^4.0.0" + }, + "bin": { + "conventional-changelog-writer": "cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-writer/node_modules/readable-stream": { + "version": "3.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/conventional-changelog-writer/node_modules/semver": { + "version": "6.3.0", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/conventional-changelog-writer/node_modules/string_decoder": { + "version": "1.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/conventional-changelog-writer/node_modules/through2": { + "version": "4.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "3" + } + }, + "node_modules/conventional-commits-filter": { + "version": "2.0.7", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash.ismatch": "^4.4.0", + "modify-values": "^1.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-commits-parser": { + "version": "3.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-text-path": "^1.0.1", + "JSONStream": "^1.0.4", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "split2": "^2.0.0", + "through2": "^4.0.0", + "trim-off-newlines": "^1.0.0" + }, + "bin": { + "conventional-commits-parser": "cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-commits-parser/node_modules/readable-stream": { + "version": "3.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/conventional-commits-parser/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/conventional-commits-parser/node_modules/split2": { + "version": "2.2.0", + "dev": true, + "license": "ISC", + "dependencies": { + "through2": "^2.0.2" + } + }, + "node_modules/conventional-commits-parser/node_modules/split2/node_modules/readable-stream": { + "version": "2.3.7", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/conventional-commits-parser/node_modules/split2/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/conventional-commits-parser/node_modules/split2/node_modules/through2": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/conventional-commits-parser/node_modules/string_decoder": { + "version": "1.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/conventional-commits-parser/node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.2.1", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/conventional-commits-parser/node_modules/through2": { + "version": "4.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "3" + } + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/cosmiconfig": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-random-string": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/dargs": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/dateformat": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/debug": { + "version": "4.3.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/decamelize": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decamelize-keys": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decode-uri-component": { + "version": "0.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/decompress-response": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/del": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "globby": "^11.0.1", + "graceful-fs": "^4.2.4", + "is-glob": "^4.0.1", + "is-path-cwd": "^2.2.0", + "is-path-inside": "^3.0.2", + "p-map": "^4.0.0", + "rimraf": "^3.0.2", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/del/node_modules/p-map": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/del/node_modules/rimraf": { + "version": "3.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/deprecation": { + "version": "2.3.1", + "dev": true, + "license": "ISC" + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dot-prop": { + "version": "5.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/duplexer2": { + "version": "0.1.4", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/duplexer3": { + "version": "0.1.4", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/env-ci": { + "version": "5.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^4.0.0", + "java-properties": "^1.0.0" + }, + "engines": { + "node": ">=10.13" + } + }, + "node_modules/env-ci/node_modules/execa": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/get-stream": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/execa/node_modules/human-signals": { + "version": "2.1.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/fast-glob": { + "version": "3.2.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.0", + "merge2": "^1.3.0", + "micromatch": "^4.0.2", + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fastq": { + "version": "1.10.0", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/filter-obj": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/find-up": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/find-versions": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "semver-regex": "^3.1.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/form-data": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/from2": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "node_modules/fs-extra": { + "version": "9.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/function-bind": { + "version": "1.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/git-log-parser": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "argv-formatter": "~1.0.0", + "spawn-error-forwarder": "~1.0.0", + "split2": "~1.0.0", + "stream-combiner2": "~1.1.1", + "through2": "~2.0.0", + "traverse": "~0.6.6" + } + }, + "node_modules/git-raw-commits": { + "version": "2.0.9", + "dev": true, + "license": "MIT", + "dependencies": { + "dargs": "^7.0.0", + "lodash.template": "^4.0.2", + "meow": "^8.0.0", + "split2": "^3.0.0", + "through2": "^4.0.0" + }, + "bin": { + "git-raw-commits": "cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/git-raw-commits/node_modules/readable-stream": { + "version": "3.6.0", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/git-raw-commits/node_modules/split2": { + "version": "3.2.2", + "dev": true, + "license": "ISC", + "dependencies": { + "readable-stream": "^3.0.0" + } + }, + "node_modules/git-raw-commits/node_modules/string_decoder": { + "version": "1.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/git-raw-commits/node_modules/through2": { + "version": "4.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "3" + } + }, + "node_modules/glob": { + "version": "7.1.7", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.1", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob-promise": { + "version": "4.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/glob": "^7.1.3" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/ahmadnassri" + }, + "peerDependencies": { + "glob": "^7.1.6" + } + }, + "node_modules/global-dirs": { + "version": "0.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "^1.3.4" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/globby": { + "version": "11.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/got": { + "version": "10.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^2.0.0", + "@szmarczak/http-timer": "^4.0.0", + "@types/cacheable-request": "^6.0.1", + "cacheable-lookup": "^2.0.0", + "cacheable-request": "^7.0.1", + "decompress-response": "^5.0.0", + "duplexer3": "^0.1.4", + "get-stream": "^5.0.0", + "lowercase-keys": "^2.0.0", + "mimic-response": "^2.1.0", + "p-cancelable": "^2.0.0", + "p-event": "^4.0.0", + "responselike": "^2.0.0", + "to-readable-stream": "^2.0.0", + "type-fest": "^0.10.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/got/node_modules/type-fest": { + "version": "0.10.0", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.4", + "dev": true, + "license": "ISC" + }, + "node_modules/handlebars": { + "version": "4.7.6", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/hard-rejection": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/has": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hook-std": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hosted-git-info": { + "version": "2.8.8", + "dev": true, + "license": "ISC" + }, + "node_modules/http-cache-semantics": { + "version": "4.1.0", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-agent/node_modules/agent-base": { + "version": "6.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent/node_modules/agent-base": { + "version": "6.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/human-signals": { + "version": "1.1.1", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8.12.0" + } + }, + "node_modules/husky": { + "version": "7.0.2", + "dev": true, + "license": "MIT", + "bin": { + "husky": "lib/bin.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, + "node_modules/ignore": { + "version": "5.1.8", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/import-from": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "dev": true, + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "dev": true, + "license": "ISC" + }, + "node_modules/into-stream": { + "version": "5.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "from2": "^2.3.0", + "p-is-promise": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "dev": true, + "license": "MIT" + }, + "node_modules/is-core-module": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-path-cwd": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-ssh": { + "version": "1.3.3", + "dev": true, + "license": "MIT", + "dependencies": { + "protocols": "^1.1.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-text-path": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "text-extensions": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/issue-parser": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash.capitalize": "^4.2.1", + "lodash.escaperegexp": "^4.1.2", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.uniqby": "^4.7.0" + }, + "engines": { + "node": ">=10.13" + } + }, + "node_modules/java-properties": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "dev": true, + "license": "ISC" + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "dev": true, + "engines": [ + "node >= 0.2.0" + ], + "license": "MIT" + }, + "node_modules/JSONStream": { + "version": "1.3.5", + "dev": true, + "license": "(MIT OR Apache-2.0)", + "dependencies": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + }, + "bin": { + "JSONStream": "bin.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/keyv": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.1.6", + "dev": true, + "license": "MIT" + }, + "node_modules/load-json-file": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/parse-json": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/pify": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/locate-path": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash._reinterpolate": { + "version": "3.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.capitalize": { + "version": "4.2.1", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.escaperegexp": { + "version": "4.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.ismatch": { + "version": "4.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.template": { + "version": "4.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash._reinterpolate": "^3.0.0", + "lodash.templatesettings": "^4.0.0" + } + }, + "node_modules/lodash.templatesettings": { + "version": "4.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash._reinterpolate": "^3.0.0" + } + }, + "node_modules/lodash.uniqby": { + "version": "4.7.0", + "dev": true, + "license": "MIT" + }, + "node_modules/lowercase-keys": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/map-obj": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/marked": { + "version": "2.1.3", + "dev": true, + "license": "MIT", + "bin": { + "marked": "bin/marked" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/marked-terminal": { + "version": "4.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^4.3.1", + "cardinal": "^2.1.1", + "chalk": "^4.1.0", + "cli-table": "^0.3.1", + "node-emoji": "^1.10.0", + "supports-hyperlinks": "^2.1.0" + }, + "peerDependencies": { + "marked": "^1.0.0 || ^2.0.0" + } + }, + "node_modules/marked-terminal/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/meow": { + "version": "8.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/hosted-git-info": { + "version": "3.0.7", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/meow/node_modules/normalize-package-data": { + "version": "3.0.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^3.0.6", + "resolve": "^1.17.0", + "semver": "^7.3.2", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/meow/node_modules/semver": { + "version": "7.3.4", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/meow/node_modules/type-fest": { + "version": "0.18.1", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mime": { + "version": "2.5.0", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.49.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.32", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.49.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-response": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "3.0.4", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.5", + "dev": true, + "license": "MIT" + }, + "node_modules/minimist-options": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/modify-values": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "dev": true, + "license": "MIT" + }, + "node_modules/nerf-dart": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/node-emoji": { + "version": "1.11.0", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash": "^4.17.21" + } + }, + "node_modules/node-fetch": { + "version": "2.6.1", + "dev": true, + "license": "MIT", + "engines": { + "node": "4.x || >=6.0.0" + } + }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm": { + "version": "7.22.0", + "bundleDependencies": [ + "@npmcli/arborist", + "@npmcli/ci-detect", + "@npmcli/config", + "@npmcli/map-workspaces", + "@npmcli/package-json", + "@npmcli/run-script", + "abbrev", + "ansicolors", + "ansistyles", + "archy", + "cacache", + "chalk", + "chownr", + "cli-columns", + "cli-table3", + "columnify", + "fastest-levenshtein", + "glob", + "graceful-fs", + "hosted-git-info", + "ini", + "init-package-json", + "is-cidr", + "json-parse-even-better-errors", + "libnpmaccess", + "libnpmdiff", + "libnpmexec", + "libnpmfund", + "libnpmhook", + "libnpmorg", + "libnpmpack", + "libnpmpublish", + "libnpmsearch", + "libnpmteam", + "libnpmversion", + "make-fetch-happen", + "minipass", + "minipass-pipeline", + "mkdirp", + "mkdirp-infer-owner", + "ms", + "node-gyp", + "nopt", + "npm-audit-report", + "npm-package-arg", + "npm-pick-manifest", + "npm-profile", + "npm-registry-fetch", + "npm-user-validate", + "npmlog", + "opener", + "pacote", + "parse-conflict-json", + "qrcode-terminal", + "read", + "read-package-json", + "read-package-json-fast", + "readdir-scoped-modules", + "rimraf", + "semver", + "ssri", + "tar", + "text-table", + "tiny-relative-date", + "treeverse", + "validate-npm-package-name", + "which", + "write-file-atomic", + "@gar/promisify", + "@npmcli/disparity-colors", + "@npmcli/fs", + "@npmcli/git", + "@npmcli/installed-package-contents", + "@npmcli/metavuln-calculator", + "@npmcli/move-file", + "@npmcli/name-from-folder", + "@npmcli/node-gyp", + "@npmcli/promise-spawn", + "@tootallnate/once", + "agent-base", + "agentkeepalive", + "aggregate-error", + "ajv", + "ansi-regex", + "ansi-styles", + "aproba", + "are-we-there-yet", + "asap", + "asn1", + "assert-plus", + "asynckit", + "aws-sign2", + "aws4", + "balanced-match", + "bcrypt-pbkdf", + "bin-links", + "binary-extensions", + "brace-expansion", + "builtins", + "caseless", + "cidr-regex", + "clean-stack", + "clone", + "cmd-shim", + "code-point-at", + "color-convert", + "color-name", + "color-support", + "colors", + "combined-stream", + "common-ancestor-path", + "concat-map", + "console-control-strings", + "core-util-is", + "dashdash", + "debug", + "debuglog", + "defaults", + "delayed-stream", + "delegates", + "depd", + "dezalgo", + "diff", + "ecc-jsbn", + "emoji-regex", + "encoding", + "env-paths", + "err-code", + "extend", + "extsprintf", + "fast-deep-equal", + "fast-json-stable-stringify", + "forever-agent", + "fs-minipass", + "fs.realpath", + "function-bind", + "gauge", + "getpass", + "har-schema", + "har-validator", + "has", + "has-flag", + "has-unicode", + "http-cache-semantics", + "http-proxy-agent", + "http-signature", + "https-proxy-agent", + "humanize-ms", + "iconv-lite", + "ignore-walk", + "imurmurhash", + "indent-string", + "infer-owner", + "inflight", + "inherits", + "ip", + "ip-regex", + "is-core-module", + "is-fullwidth-code-point", + "is-lambda", + "is-typedarray", + "isexe", + "isstream", + "jsbn", + "json-schema", + "json-schema-traverse", + "json-stringify-nice", + "json-stringify-safe", + "jsonparse", + "jsprim", + "just-diff", + "just-diff-apply", + "lru-cache", + "mime-db", + "mime-types", + "minimatch", + "minipass-collect", + "minipass-fetch", + "minipass-flush", + "minipass-json-stream", + "minipass-sized", + "minizlib", + "mute-stream", + "negotiator", + "normalize-package-data", + "npm-bundled", + "npm-install-checks", + "npm-normalize-package-bin", + "npm-packlist", + "number-is-nan", + "oauth-sign", + "object-assign", + "once", + "p-map", + "path-is-absolute", + "performance-now", + "proc-log", + "promise-all-reject-late", + "promise-call-limit", + "promise-inflight", + "promise-retry", + "promzard", + "psl", + "punycode", + "qs", + "read-cmd-shim", + "readable-stream", + "request", + "retry", + "safe-buffer", + "safer-buffer", + "set-blocking", + "signal-exit", + "smart-buffer", + "socks", + "socks-proxy-agent", + "spdx-correct", + "spdx-exceptions", + "spdx-expression-parse", + "spdx-license-ids", + "sshpk", + "string_decoder", + "string-width", + "stringify-package", + "strip-ansi", + "supports-color", + "tunnel-agent", + "tweetnacl", + "typedarray-to-buffer", + "unique-filename", + "unique-slug", + "uri-js", + "util-deprecate", + "uuid", + "validate-npm-package-license", + "verror", + "walk-up-path", + "wcwidth", + "wide-align", + "wrappy", + "yallist" + ], + "dev": true, + "license": "Artistic-2.0", + "workspaces": [ + "docs", + "packages/*" + ], + "dependencies": { + "@npmcli/arborist": "^2.8.3", + "@npmcli/ci-detect": "^1.2.0", + "@npmcli/config": "^2.3.0", + "@npmcli/map-workspaces": "^1.0.4", + "@npmcli/package-json": "^1.0.1", + "@npmcli/run-script": "^1.8.6", + "abbrev": "~1.1.1", + "ansicolors": "~0.3.2", + "ansistyles": "~0.1.3", + "archy": "~1.0.0", + "cacache": "^15.3.0", + "chalk": "^4.1.2", + "chownr": "^2.0.0", + "cli-columns": "^3.1.2", + "cli-table3": "^0.6.0", + "columnify": "~1.5.4", + "fastest-levenshtein": "^1.0.12", + "glob": "^7.1.7", + "graceful-fs": "^4.2.8", + "hosted-git-info": "^4.0.2", + "ini": "^2.0.0", + "init-package-json": "^2.0.4", + "is-cidr": "^4.0.2", + "json-parse-even-better-errors": "^2.3.1", + "libnpmaccess": "^4.0.2", + "libnpmdiff": "^2.0.4", + "libnpmexec": "^2.0.1", + "libnpmfund": "^1.1.0", + "libnpmhook": "^6.0.2", + "libnpmorg": "^2.0.2", + "libnpmpack": "^2.0.1", + "libnpmpublish": "^4.0.1", + "libnpmsearch": "^3.1.1", + "libnpmteam": "^2.0.3", + "libnpmversion": "^1.2.1", + "make-fetch-happen": "^9.1.0", + "minipass": "^3.1.3", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "mkdirp-infer-owner": "^2.0.0", + "ms": "^2.1.2", + "node-gyp": "^7.1.2", + "nopt": "^5.0.0", + "npm-audit-report": "^2.1.5", + "npm-package-arg": "^8.1.5", + "npm-pick-manifest": "^6.1.1", + "npm-profile": "^5.0.3", + "npm-registry-fetch": "^11.0.0", + "npm-user-validate": "^1.0.1", + "npmlog": "^5.0.1", + "opener": "^1.5.2", + "pacote": "^11.3.5", + "parse-conflict-json": "^1.1.1", + "qrcode-terminal": "^0.12.0", + "read": "~1.0.7", + "read-package-json": "^4.1.1", + "read-package-json-fast": "^2.0.3", + "readdir-scoped-modules": "^1.1.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "ssri": "^8.0.1", + "tar": "^6.1.11", + "text-table": "~0.2.0", + "tiny-relative-date": "^1.3.0", + "treeverse": "^1.0.4", + "validate-npm-package-name": "~3.0.0", + "which": "^2.0.2", + "write-file-atomic": "^3.0.3" + }, + "bin": { + "npm": "bin/npm-cli.js", + "npx": "bin/npx-cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/@gar/promisify": { + "version": "1.1.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/@npmcli/arborist": { + "version": "2.8.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/installed-package-contents": "^1.0.7", + "@npmcli/map-workspaces": "^1.0.2", + "@npmcli/metavuln-calculator": "^1.1.0", + "@npmcli/move-file": "^1.1.0", + "@npmcli/name-from-folder": "^1.0.1", + "@npmcli/node-gyp": "^1.0.1", + "@npmcli/package-json": "^1.0.1", + "@npmcli/run-script": "^1.8.2", + "bin-links": "^2.2.1", + "cacache": "^15.0.3", + "common-ancestor-path": "^1.0.1", + "json-parse-even-better-errors": "^2.3.1", + "json-stringify-nice": "^1.1.4", + "mkdirp": "^1.0.4", + "mkdirp-infer-owner": "^2.0.0", + "npm-install-checks": "^4.0.0", + "npm-package-arg": "^8.1.5", + "npm-pick-manifest": "^6.1.0", + "npm-registry-fetch": "^11.0.0", + "pacote": "^11.3.5", + "parse-conflict-json": "^1.1.1", + "proc-log": "^1.0.0", + "promise-all-reject-late": "^1.0.0", + "promise-call-limit": "^1.0.1", + "read-package-json-fast": "^2.0.2", + "readdir-scoped-modules": "^1.1.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "ssri": "^8.0.1", + "treeverse": "^1.0.4", + "walk-up-path": "^1.0.0" + }, + "bin": { + "arborist": "bin/index.js" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm/node_modules/@npmcli/ci-detect": { + "version": "1.3.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/@npmcli/config": { + "version": "2.3.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "ini": "^2.0.0", + "mkdirp-infer-owner": "^2.0.0", + "nopt": "^5.0.0", + "semver": "^7.3.4", + "walk-up-path": "^1.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/@npmcli/disparity-colors": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "ansi-styles": "^4.3.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/@npmcli/fs": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + } + }, + "node_modules/npm/node_modules/@npmcli/git": { + "version": "2.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/promise-spawn": "^1.3.2", + "lru-cache": "^6.0.0", + "mkdirp": "^1.0.4", + "npm-pick-manifest": "^6.1.1", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^2.0.2" + } + }, + "node_modules/npm/node_modules/@npmcli/installed-package-contents": { + "version": "1.0.7", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + }, + "bin": { + "installed-package-contents": "index.js" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm/node_modules/@npmcli/map-workspaces": { + "version": "1.0.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/name-from-folder": "^1.0.1", + "glob": "^7.1.6", + "minimatch": "^3.0.4", + "read-package-json-fast": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/@npmcli/metavuln-calculator": { + "version": "1.1.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "cacache": "^15.0.5", + "pacote": "^11.1.11", + "semver": "^7.3.2" + } + }, + "node_modules/npm/node_modules/@npmcli/move-file": { + "version": "1.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/@npmcli/name-from-folder": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/@npmcli/node-gyp": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/@npmcli/package-json": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^2.3.1" + } + }, + "node_modules/npm/node_modules/@npmcli/promise-spawn": { + "version": "1.3.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "infer-owner": "^1.0.4" + } + }, + "node_modules/npm/node_modules/@npmcli/run-script": { + "version": "1.8.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/node-gyp": "^1.0.2", + "@npmcli/promise-spawn": "^1.3.2", + "node-gyp": "^7.1.0", + "read-package-json-fast": "^2.0.1" + } + }, + "node_modules/npm/node_modules/@tootallnate/once": { + "version": "1.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/npm/node_modules/abbrev": { + "version": "1.1.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/agent-base": { + "version": "6.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/npm/node_modules/agentkeepalive": { + "version": "4.1.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.0", + "depd": "^1.1.2", + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/npm/node_modules/aggregate-error": { + "version": "3.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/ajv": { + "version": "6.12.6", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/npm/node_modules/ansi-regex": { + "version": "2.1.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm/node_modules/ansicolors": { + "version": "0.3.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/ansistyles": { + "version": "0.1.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/aproba": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/archy": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/are-we-there-yet": { + "version": "1.1.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/asap": { + "version": "2.0.6", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/asn1": { + "version": "0.2.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/npm/node_modules/assert-plus": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/npm/node_modules/asynckit": { + "version": "0.4.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/aws-sign2": { + "version": "0.7.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/npm/node_modules/aws4": { + "version": "1.11.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "BSD-3-Clause", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/npm/node_modules/bin-links": { + "version": "2.2.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "cmd-shim": "^4.0.1", + "mkdirp": "^1.0.3", + "npm-normalize-package-bin": "^1.0.0", + "read-cmd-shim": "^2.0.0", + "rimraf": "^3.0.0", + "write-file-atomic": "^3.0.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/binary-extensions": { + "version": "2.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/brace-expansion": { + "version": "1.1.11", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/npm/node_modules/builtins": { + "version": "1.0.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/cacache": { + "version": "15.3.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^1.0.0", + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm/node_modules/caseless": { + "version": "0.12.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0" + }, + "node_modules/npm/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/npm/node_modules/chownr": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/cidr-regex": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "ip-regex": "^4.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/clean-stack": { + "version": "2.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/cli-columns": { + "version": "3.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "string-width": "^2.0.0", + "strip-ansi": "^3.0.1" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/npm/node_modules/cli-table3": { + "version": "0.6.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "object-assign": "^4.1.0", + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "colors": "^1.1.2" + } + }, + "node_modules/npm/node_modules/cli-table3/node_modules/ansi-regex": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/cli-table3/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/cli-table3/node_modules/string-width": { + "version": "4.2.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/cli-table3/node_modules/strip-ansi": { + "version": "6.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/clone": { + "version": "1.0.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/npm/node_modules/cmd-shim": { + "version": "4.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "mkdirp-infer-owner": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/code-point-at": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/npm/node_modules/color-name": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/color-support": { + "version": "1.1.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/npm/node_modules/colors": { + "version": "1.4.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/npm/node_modules/columnify": { + "version": "1.5.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "strip-ansi": "^3.0.0", + "wcwidth": "^1.0.0" + } + }, + "node_modules/npm/node_modules/combined-stream": { + "version": "1.0.8", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/npm/node_modules/common-ancestor-path": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/concat-map": { + "version": "0.0.1", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/console-control-strings": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/core-util-is": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/dashdash": { + "version": "1.14.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/npm/node_modules/debug": { + "version": "4.3.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/npm/node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/debuglog": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/npm/node_modules/defaults": { + "version": "1.0.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + } + }, + "node_modules/npm/node_modules/delayed-stream": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/npm/node_modules/delegates": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/depd": { + "version": "1.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/npm/node_modules/dezalgo": { + "version": "1.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "node_modules/npm/node_modules/diff": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/npm/node_modules/ecc-jsbn": { + "version": "0.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/npm/node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/encoding": { + "version": "0.1.13", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/npm/node_modules/env-paths": { + "version": "2.2.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/err-code": { + "version": "2.0.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/extend": { + "version": "3.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/extsprintf": { + "version": "1.3.0", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/fast-deep-equal": { + "version": "3.1.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/fastest-levenshtein": { + "version": "1.0.12", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/forever-agent": { + "version": "0.6.1", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/npm/node_modules/fs-minipass": { + "version": "2.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/fs.realpath": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/function-bind": { + "version": "1.1.1", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/gauge": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1 || ^2.0.0", + "strip-ansi": "^3.0.1 || ^4.0.0", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/getpass": { + "version": "0.1.7", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0" + } + }, + "node_modules/npm/node_modules/glob": { + "version": "7.1.7", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/graceful-fs": { + "version": "4.2.8", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/har-schema": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/har-validator": { + "version": "5.1.5", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/has": { + "version": "1.0.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/npm/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/has-unicode": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/hosted-git-info": { + "version": "4.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/http-cache-semantics": { + "version": "4.1.0", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause" + }, + "node_modules/npm/node_modules/http-proxy-agent": { + "version": "4.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/npm/node_modules/http-signature": { + "version": "1.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, + "node_modules/npm/node_modules/https-proxy-agent": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/npm/node_modules/humanize-ms": { + "version": "1.2.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/npm/node_modules/iconv-lite": { + "version": "0.6.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/ignore-walk": { + "version": "3.0.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minimatch": "^3.0.4" + } + }, + "node_modules/npm/node_modules/imurmurhash": { + "version": "0.1.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/npm/node_modules/indent-string": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/infer-owner": { + "version": "1.0.4", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/inflight": { + "version": "1.0.6", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/npm/node_modules/inherits": { + "version": "2.0.4", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/ini": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/init-package-json": { + "version": "2.0.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.1", + "npm-package-arg": "^8.1.2", + "promzard": "^0.3.0", + "read": "~1.0.1", + "read-package-json": "^4.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/ip": { + "version": "1.1.5", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/ip-regex": { + "version": "4.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/is-cidr": { + "version": "4.0.2", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "cidr-regex": "^3.1.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/is-core-module": { + "version": "2.6.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/npm/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/is-lambda": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/is-typedarray": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/isexe": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/isstream": { + "version": "0.1.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/jsbn": { + "version": "0.1.1", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/json-schema": { + "version": "0.2.3", + "dev": true, + "inBundle": true + }, + "node_modules/npm/node_modules/json-schema-traverse": { + "version": "0.4.1", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/json-stringify-nice": { + "version": "1.1.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/json-stringify-safe": { + "version": "5.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/jsonparse": { + "version": "1.3.1", + "dev": true, + "engines": [ + "node >= 0.2.0" + ], + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/jsprim": { + "version": "1.4.1", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "inBundle": true, + "license": "MIT", + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "node_modules/npm/node_modules/just-diff": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/just-diff-apply": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/libnpmaccess": { + "version": "4.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "minipass": "^3.1.1", + "npm-package-arg": "^8.1.2", + "npm-registry-fetch": "^11.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/libnpmdiff": { + "version": "2.0.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/disparity-colors": "^1.0.1", + "@npmcli/installed-package-contents": "^1.0.7", + "binary-extensions": "^2.2.0", + "diff": "^5.0.0", + "minimatch": "^3.0.4", + "npm-package-arg": "^8.1.4", + "pacote": "^11.3.4", + "tar": "^6.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/libnpmexec": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^2.3.0", + "@npmcli/ci-detect": "^1.3.0", + "@npmcli/run-script": "^1.8.4", + "chalk": "^4.1.0", + "mkdirp-infer-owner": "^2.0.0", + "npm-package-arg": "^8.1.2", + "pacote": "^11.3.1", + "proc-log": "^1.0.0", + "read": "^1.0.7", + "read-package-json-fast": "^2.0.2", + "walk-up-path": "^1.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/libnpmfund": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^2.5.0" + } + }, + "node_modules/npm/node_modules/libnpmhook": { + "version": "6.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^11.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/libnpmorg": { + "version": "2.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^11.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/libnpmpack": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/run-script": "^1.8.3", + "npm-package-arg": "^8.1.0", + "pacote": "^11.2.6" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/libnpmpublish": { + "version": "4.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "normalize-package-data": "^3.0.2", + "npm-package-arg": "^8.1.2", + "npm-registry-fetch": "^11.0.0", + "semver": "^7.1.3", + "ssri": "^8.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/libnpmsearch": { + "version": "3.1.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^11.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/libnpmteam": { + "version": "2.0.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^11.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/libnpmversion": { + "version": "1.2.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^2.0.7", + "@npmcli/run-script": "^1.8.4", + "json-parse-even-better-errors": "^2.3.1", + "semver": "^7.3.5", + "stringify-package": "^1.0.1" + } + }, + "node_modules/npm/node_modules/lru-cache": { + "version": "6.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/make-fetch-happen": { + "version": "9.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.2.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.2", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.0.0", + "ssri": "^8.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm/node_modules/mime-db": { + "version": "1.49.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/npm/node_modules/mime-types": { + "version": "2.1.32", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.49.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/npm/node_modules/minimatch": { + "version": "3.0.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/npm/node_modules/minipass": { + "version": "3.1.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-collect": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/minipass-fetch": { + "version": "1.4.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "optionalDependencies": { + "encoding": "^0.1.12" + } + }, + "node_modules/npm/node_modules/minipass-flush": { + "version": "1.0.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/minipass-json-stream": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "jsonparse": "^1.3.1", + "minipass": "^3.0.0" + } + }, + "node_modules/npm/node_modules/minipass-pipeline": { + "version": "1.2.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-sized": { + "version": "1.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minizlib": { + "version": "2.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/mkdirp": { + "version": "1.0.4", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/mkdirp-infer-owner": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "infer-owner": "^1.0.4", + "mkdirp": "^1.0.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/ms": { + "version": "2.1.3", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/mute-stream": { + "version": "0.0.8", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/negotiator": { + "version": "0.6.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/npm/node_modules/node-gyp": { + "version": "7.1.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.3", + "nopt": "^5.0.0", + "npmlog": "^4.1.2", + "request": "^2.88.2", + "rimraf": "^3.0.2", + "semver": "^7.3.2", + "tar": "^6.0.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": ">= 10.12.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/aproba": { + "version": "1.2.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/node-gyp/node_modules/gauge": { + "version": "2.7.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/npmlog": { + "version": "4.1.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "node_modules/npm/node_modules/node-gyp/node_modules/string-width": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/nopt": { + "version": "5.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/normalize-package-data": { + "version": "3.0.3", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/npm-audit-report": { + "version": "2.1.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "chalk": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/npm-bundled": { + "version": "1.1.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "node_modules/npm/node_modules/npm-install-checks": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/npm-normalize-package-bin": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/npm-package-arg": { + "version": "8.1.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^4.0.1", + "semver": "^7.3.4", + "validate-npm-package-name": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/npm-packlist": { + "version": "2.2.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.6", + "ignore-walk": "^3.0.3", + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + }, + "bin": { + "npm-packlist": "bin/index.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/npm-pick-manifest": { + "version": "6.1.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-install-checks": "^4.0.0", + "npm-normalize-package-bin": "^1.0.1", + "npm-package-arg": "^8.1.2", + "semver": "^7.3.4" + } + }, + "node_modules/npm/node_modules/npm-profile": { + "version": "5.0.4", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^11.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/npm-registry-fetch": { + "version": "11.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "make-fetch-happen": "^9.0.1", + "minipass": "^3.1.3", + "minipass-fetch": "^1.3.0", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.0.0", + "npm-package-arg": "^8.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/npm-user-validate": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause" + }, + "node_modules/npm/node_modules/npmlog": { + "version": "5.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "node_modules/npm/node_modules/npmlog/node_modules/are-we-there-yet": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/number-is-nan": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/oauth-sign": { + "version": "0.9.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/npm/node_modules/object-assign": { + "version": "4.1.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/once": { + "version": "1.4.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/npm/node_modules/opener": { + "version": "1.5.2", + "dev": true, + "inBundle": true, + "license": "(WTFPL OR MIT)", + "bin": { + "opener": "bin/opener-bin.js" + } + }, + "node_modules/npm/node_modules/p-map": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/pacote": { + "version": "11.3.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^2.1.0", + "@npmcli/installed-package-contents": "^1.0.6", + "@npmcli/promise-spawn": "^1.2.0", + "@npmcli/run-script": "^1.8.2", + "cacache": "^15.0.5", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "infer-owner": "^1.0.4", + "minipass": "^3.1.3", + "mkdirp": "^1.0.3", + "npm-package-arg": "^8.0.1", + "npm-packlist": "^2.1.4", + "npm-pick-manifest": "^6.0.0", + "npm-registry-fetch": "^11.0.0", + "promise-retry": "^2.0.1", + "read-package-json-fast": "^2.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.1.0" + }, + "bin": { + "pacote": "lib/bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/parse-conflict-json": { + "version": "1.1.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^2.3.0", + "just-diff": "^3.0.1", + "just-diff-apply": "^3.0.0" + } + }, + "node_modules/npm/node_modules/path-is-absolute": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/performance-now": { + "version": "2.1.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/proc-log": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/promise-all-reject-late": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-call-limit": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-inflight": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/promise-retry": { + "version": "2.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/promzard": { + "version": "0.3.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "read": "1" + } + }, + "node_modules/npm/node_modules/psl": { + "version": "1.8.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/punycode": { + "version": "2.1.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/qrcode-terminal": { + "version": "0.12.0", + "dev": true, + "inBundle": true, + "bin": { + "qrcode-terminal": "bin/qrcode-terminal.js" + } + }, + "node_modules/npm/node_modules/qs": { + "version": "6.5.2", + "dev": true, + "inBundle": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/npm/node_modules/read": { + "version": "1.0.7", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "mute-stream": "~0.0.4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/npm/node_modules/read-cmd-shim": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/read-package-json": { + "version": "4.1.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.1", + "json-parse-even-better-errors": "^2.3.0", + "normalize-package-data": "^3.0.0", + "npm-normalize-package-bin": "^1.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/read-package-json-fast": { + "version": "2.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^2.3.0", + "npm-normalize-package-bin": "^1.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/readable-stream": { + "version": "3.6.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/npm/node_modules/readdir-scoped-modules": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "debuglog": "^1.0.1", + "dezalgo": "^1.0.0", + "graceful-fs": "^4.1.2", + "once": "^1.3.0" + } + }, + "node_modules/npm/node_modules/request": { + "version": "2.88.2", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/npm/node_modules/request/node_modules/form-data": { + "version": "2.3.3", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/npm/node_modules/request/node_modules/tough-cookie": { + "version": "2.5.0", + "dev": true, + "inBundle": true, + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/npm/node_modules/retry": { + "version": "0.12.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/npm/node_modules/rimraf": { + "version": "3.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/safe-buffer": { + "version": "5.2.1", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/safer-buffer": { + "version": "2.1.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/semver": { + "version": "7.3.5", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/set-blocking": { + "version": "2.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/signal-exit": { + "version": "3.0.3", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/smart-buffer": { + "version": "4.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks": { + "version": "2.6.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ip": "^1.1.5", + "smart-buffer": "^4.1.0" + }, + "engines": { + "node": ">= 10.13.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks-proxy-agent": { + "version": "6.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.1", + "socks": "^2.6.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm/node_modules/spdx-correct": { + "version": "3.1.1", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-exceptions": { + "version": "2.3.0", + "dev": true, + "inBundle": true, + "license": "CC-BY-3.0" + }, + "node_modules/npm/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-license-ids": { + "version": "3.0.10", + "dev": true, + "inBundle": true, + "license": "CC0-1.0" + }, + "node_modules/npm/node_modules/sshpk": { + "version": "1.16.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/ssri": { + "version": "8.0.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/string_decoder": { + "version": "1.3.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/npm/node_modules/string-width": { + "version": "2.1.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/string-width/node_modules/ansi-regex": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/string-width/node_modules/strip-ansi": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/stringify-package": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/strip-ansi": { + "version": "3.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/tar": { + "version": "6.1.11", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm/node_modules/text-table": { + "version": "0.2.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/tiny-relative-date": { + "version": "1.3.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/treeverse": { + "version": "1.0.4", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/tunnel-agent": { + "version": "0.6.0", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/npm/node_modules/tweetnacl": { + "version": "0.14.5", + "dev": true, + "inBundle": true, + "license": "Unlicense" + }, + "node_modules/npm/node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/npm/node_modules/unique-filename": { + "version": "1.1.1", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/npm/node_modules/unique-slug": { + "version": "2.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, + "node_modules/npm/node_modules/uri-js": { + "version": "4.4.1", + "dev": true, + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/npm/node_modules/util-deprecate": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/uuid": { + "version": "3.4.0", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/npm/node_modules/validate-npm-package-license": { + "version": "3.0.4", + "dev": true, + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/npm/node_modules/validate-npm-package-name": { + "version": "3.0.0", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "builtins": "^1.0.3" + } + }, + "node_modules/npm/node_modules/verror": { + "version": "1.10.0", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "inBundle": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/npm/node_modules/walk-up-path": { + "version": "1.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/wcwidth": { + "version": "1.0.1", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/npm/node_modules/which": { + "version": "2.0.2", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/wide-align": { + "version": "1.1.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "string-width": "^1.0.2 || 2" + } + }, + "node_modules/npm/node_modules/wrappy": { + "version": "1.0.2", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/write-file-atomic": { + "version": "3.0.3", + "dev": true, + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/npm/node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/object-inspect": { + "version": "1.11.0", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-cancelable": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/p-each-series": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-event": { + "version": "4.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-timeout": "^3.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-filter": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-map": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/p-is-promise": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "1.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-locate/node_modules/p-try": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/p-map": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/p-reduce": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/p-retry": { + "version": "4.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/retry": "^0.12.0", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-timeout": { + "version": "3.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-finally": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-path": { + "version": "4.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "is-ssh": "^1.3.0", + "protocols": "^1.4.0", + "qs": "^6.9.4", + "query-string": "^6.13.8" + } + }, + "node_modules/path-exists": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.6", + "dev": true, + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "2.2.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pkg-conf": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^2.0.0", + "load-json-file": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/protocols": { + "version": "1.4.8", + "dev": true, + "license": "MIT" + }, + "node_modules/pump": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/q": { + "version": "1.5.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, + "node_modules/qs": { + "version": "6.10.1", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/query-string": { + "version": "6.14.1", + "dev": true, + "license": "MIT", + "dependencies": { + "decode-uri-component": "^0.2.0", + "filter-obj": "^1.1.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/quick-lru": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/read-pkg": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up": { + "version": "7.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/path-exists": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "node_modules/readable-stream": { + "version": "2.3.7", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/redent": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/redeyed": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "esprima": "~4.0.0" + } + }, + "node_modules/registry-auth-token": { + "version": "4.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "rc": "^1.2.8" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.19.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-global": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "global-dirs": "^0.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/responselike": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "lowercase-keys": "^2.0.0" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.1.10", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/sax": { + "version": "1.2.4", + "dev": true, + "license": "ISC" + }, + "node_modules/semantic-release": { + "version": "17.4.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@semantic-release/commit-analyzer": "^8.0.0", + "@semantic-release/error": "^2.2.0", + "@semantic-release/github": "^7.0.0", + "@semantic-release/npm": "^7.0.0", + "@semantic-release/release-notes-generator": "^9.0.0", + "aggregate-error": "^3.0.0", + "cosmiconfig": "^7.0.0", + "debug": "^4.0.0", + "env-ci": "^5.0.0", + "execa": "^5.0.0", + "figures": "^3.0.0", + "find-versions": "^4.0.0", + "get-stream": "^6.0.0", + "git-log-parser": "^1.2.0", + "hook-std": "^2.0.0", + "hosted-git-info": "^4.0.0", + "lodash": "^4.17.21", + "marked": "^2.0.0", + "marked-terminal": "^4.1.1", + "micromatch": "^4.0.2", + "p-each-series": "^2.1.0", + "p-reduce": "^2.0.0", + "read-pkg-up": "^7.0.0", + "resolve-from": "^5.0.0", + "semver": "^7.3.2", + "semver-diff": "^3.1.1", + "signale": "^1.2.1", + "yargs": "^16.2.0" + }, + "bin": { + "semantic-release": "bin/semantic-release.js" + }, + "engines": { + "node": ">=10.19" + } + }, + "node_modules/semantic-release-dotnet": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "glob": "^7.1.7", + "glob-promise": "^4.2.0", + "xml-js": "^1.6.11" + } + }, + "node_modules/semantic-release-nuget": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/semantic-release-nuget/-/semantic-release-nuget-1.1.0.tgz", + "integrity": "sha512-Eaqb39E+dPTgYgHif0/P6cUj7IxZxI9e2BgmfE+NkRlPArpRPa94UZTi8UFMbEKiKg0IaNnAO79yexUPcZnuHA==", + "dev": true + }, + "node_modules/semantic-release/node_modules/get-stream": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semantic-release/node_modules/hosted-git-info": { + "version": "4.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semantic-release/node_modules/semver": { + "version": "7.3.4", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semantic-release/node_modules/yargs": { + "version": "16.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "5.7.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/semver-diff": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/semver-diff/node_modules/semver": { + "version": "6.3.0", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/semver-regex": { + "version": "3.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.3", + "dev": true, + "license": "ISC" + }, + "node_modules/signale": { + "version": "1.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^2.3.2", + "figures": "^2.0.0", + "pkg-conf": "^2.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/signale/node_modules/figures": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spawn-error-forwarder": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/spdx-correct": { + "version": "3.1.1", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.7", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/split": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/split-on-first": { + "version": "1.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/split2": { + "version": "1.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "through2": "~2.0.0" + } + }, + "node_modules/stream-combiner2": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "duplexer2": "~0.1.0", + "readable-stream": "^2.0.2" + } + }, + "node_modules/strict-uri-encode": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width": { + "version": "4.2.2", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/temp-dir": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/tempy": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "del": "^6.0.0", + "is-stream": "^2.0.0", + "temp-dir": "^2.0.0", + "type-fest": "^0.16.0", + "unique-string": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tempy/node_modules/type-fest": { + "version": "0.16.0", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/text-extensions": { + "version": "1.9.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/through": { + "version": "2.3.8", + "dev": true, + "license": "MIT" + }, + "node_modules/through2": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/to-readable-stream": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/traverse": { + "version": "0.6.6", + "dev": true, + "license": "MIT" + }, + "node_modules/trim-newlines": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/trim-off-newlines": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/type-fest": { + "version": "0.6.0", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "node_modules/uglify-js": { + "version": "3.12.5", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/unique-string": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "crypto-random-string": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/universal-user-agent": { + "version": "6.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/universalify": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/url-join": { + "version": "4.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "dev": true, + "license": "ISC" + }, + "node_modules/xml-js": { + "version": "1.6.11", + "dev": true, + "license": "MIT", + "dependencies": { + "sax": "^1.2.4" + }, + "bin": { + "xml-js": "bin/cli.js" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/yaml": { + "version": "1.10.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs": { + "version": "17.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.4", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.12.11", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.12.11", + "dev": true + }, + "@babel/highlight": { + "version": "7.10.4", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@commitlint/cli": { + "version": "13.1.0", + "dev": true, + "requires": { + "@commitlint/format": "^13.1.0", + "@commitlint/lint": "^13.1.0", + "@commitlint/load": "^13.1.0", + "@commitlint/read": "^13.1.0", + "@commitlint/types": "^13.1.0", + "lodash": "^4.17.19", + "resolve-from": "5.0.0", + "resolve-global": "1.0.0", + "yargs": "^17.0.0" + } + }, + "@commitlint/config-conventional": { + "version": "13.1.0", + "dev": true, + "requires": { + "conventional-changelog-conventionalcommits": "^4.3.1" + } + }, + "@commitlint/ensure": { + "version": "13.1.0", + "dev": true, + "requires": { + "@commitlint/types": "^13.1.0", + "lodash": "^4.17.19" + } + }, + "@commitlint/execute-rule": { + "version": "13.0.0", + "dev": true + }, + "@commitlint/format": { + "version": "13.1.0", + "dev": true, + "requires": { + "@commitlint/types": "^13.1.0", + "chalk": "^4.0.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } + } + }, + "@commitlint/is-ignored": { + "version": "13.1.0", + "dev": true, + "requires": { + "@commitlint/types": "^13.1.0", + "semver": "7.3.5" + }, + "dependencies": { + "semver": { + "version": "7.3.5", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@commitlint/lint": { + "version": "13.1.0", + "dev": true, + "requires": { + "@commitlint/is-ignored": "^13.1.0", + "@commitlint/parse": "^13.1.0", + "@commitlint/rules": "^13.1.0", + "@commitlint/types": "^13.1.0" + } + }, + "@commitlint/load": { + "version": "13.1.0", + "dev": true, + "requires": { + "@commitlint/execute-rule": "^13.0.0", + "@commitlint/resolve-extends": "^13.0.0", + "@commitlint/types": "^13.1.0", + "chalk": "^4.0.0", + "cosmiconfig": "^7.0.0", + "lodash": "^4.17.19", + "resolve-from": "^5.0.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } + } + }, + "@commitlint/message": { + "version": "13.0.0", + "dev": true + }, + "@commitlint/parse": { + "version": "13.1.0", + "dev": true, + "requires": { + "@commitlint/types": "^13.1.0", + "conventional-changelog-angular": "^5.0.11", + "conventional-commits-parser": "^3.0.0" + } + }, + "@commitlint/read": { + "version": "13.1.0", + "dev": true, + "requires": { + "@commitlint/top-level": "^13.0.0", + "@commitlint/types": "^13.1.0", + "fs-extra": "^10.0.0", + "git-raw-commits": "^2.0.0" + }, + "dependencies": { + "fs-extra": { + "version": "10.0.0", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + } + } + }, + "@commitlint/resolve-extends": { + "version": "13.0.0", + "dev": true, + "requires": { + "import-fresh": "^3.0.0", + "lodash": "^4.17.19", + "resolve-from": "^5.0.0", + "resolve-global": "^1.0.0" + } + }, + "@commitlint/rules": { + "version": "13.1.0", + "dev": true, + "requires": { + "@commitlint/ensure": "^13.1.0", + "@commitlint/message": "^13.0.0", + "@commitlint/to-lines": "^13.0.0", + "@commitlint/types": "^13.1.0", + "execa": "^5.0.0" + } + }, + "@commitlint/to-lines": { + "version": "13.0.0", + "dev": true + }, + "@commitlint/top-level": { + "version": "13.0.0", + "dev": true, + "requires": { + "find-up": "^5.0.0" + }, + "dependencies": { + "find-up": { + "version": "5.0.0", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "6.0.0", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "p-limit": { + "version": "3.1.0", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "path-exists": { + "version": "4.0.0", + "dev": true + } + } + }, + "@commitlint/types": { + "version": "13.1.0", + "dev": true, + "requires": { + "chalk": "^4.0.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.4", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.4", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.4", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.6", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.4", + "fastq": "^1.6.0" + } + }, + "@octokit/auth-token": { + "version": "2.4.4", + "dev": true, + "requires": { + "@octokit/types": "^6.0.0" + } + }, + "@octokit/core": { + "version": "3.2.4", + "dev": true, + "requires": { + "@octokit/auth-token": "^2.4.4", + "@octokit/graphql": "^4.5.8", + "@octokit/request": "^5.4.12", + "@octokit/types": "^6.0.3", + "before-after-hook": "^2.1.0", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/endpoint": { + "version": "6.0.10", + "dev": true, + "requires": { + "@octokit/types": "^6.0.0", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/graphql": { + "version": "4.5.8", + "dev": true, + "requires": { + "@octokit/request": "^5.3.0", + "@octokit/types": "^6.0.0", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/openapi-types": { + "version": "3.1.1", + "dev": true + }, + "@octokit/plugin-paginate-rest": { + "version": "2.8.0", + "dev": true, + "requires": { + "@octokit/types": "^6.4.0" + } + }, + "@octokit/plugin-request-log": { + "version": "1.0.2", + "dev": true, + "requires": {} + }, + "@octokit/plugin-rest-endpoint-methods": { + "version": "4.4.1", + "dev": true, + "requires": { + "@octokit/types": "^6.1.0", + "deprecation": "^2.3.1" + } + }, + "@octokit/request": { + "version": "5.4.12", + "dev": true, + "requires": { + "@octokit/endpoint": "^6.0.1", + "@octokit/request-error": "^2.0.0", + "@octokit/types": "^6.0.3", + "deprecation": "^2.0.0", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.1", + "once": "^1.4.0", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/request-error": { + "version": "2.0.4", + "dev": true, + "requires": { + "@octokit/types": "^6.0.0", + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, + "@octokit/rest": { + "version": "18.0.12", + "dev": true, + "requires": { + "@octokit/core": "^3.2.3", + "@octokit/plugin-paginate-rest": "^2.6.2", + "@octokit/plugin-request-log": "^1.0.2", + "@octokit/plugin-rest-endpoint-methods": "4.4.1" + } + }, + "@octokit/types": { + "version": "6.4.2", + "dev": true, + "requires": { + "@octokit/openapi-types": "^3.1.1", + "@types/node": ">= 8" + } + }, + "@semantic-release/changelog": { + "version": "5.0.1", + "dev": true, + "requires": { + "@semantic-release/error": "^2.1.0", + "aggregate-error": "^3.0.0", + "fs-extra": "^9.0.0", + "lodash": "^4.17.4" + } + }, + "@semantic-release/commit-analyzer": { + "version": "8.0.1", + "dev": true, + "requires": { + "conventional-changelog-angular": "^5.0.0", + "conventional-commits-filter": "^2.0.0", + "conventional-commits-parser": "^3.0.7", + "debug": "^4.0.0", + "import-from": "^3.0.0", + "lodash": "^4.17.4", + "micromatch": "^4.0.2" + } + }, + "@semantic-release/error": { + "version": "2.2.0", + "dev": true + }, + "@semantic-release/git": { + "version": "9.0.0", + "dev": true, + "requires": { + "@semantic-release/error": "^2.1.0", + "aggregate-error": "^3.0.0", + "debug": "^4.0.0", + "dir-glob": "^3.0.0", + "execa": "^4.0.0", + "lodash": "^4.17.4", + "micromatch": "^4.0.0", + "p-reduce": "^2.0.0" + }, + "dependencies": { + "execa": { + "version": "4.1.0", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + } + } + } + }, + "@semantic-release/github": { + "version": "7.2.0", + "dev": true, + "requires": { + "@octokit/rest": "^18.0.0", + "@semantic-release/error": "^2.2.0", + "aggregate-error": "^3.0.0", + "bottleneck": "^2.18.1", + "debug": "^4.0.0", + "dir-glob": "^3.0.0", + "fs-extra": "^9.0.0", + "globby": "^11.0.0", + "http-proxy-agent": "^4.0.0", + "https-proxy-agent": "^5.0.0", + "issue-parser": "^6.0.0", + "lodash": "^4.17.4", + "mime": "^2.4.3", + "p-filter": "^2.0.0", + "p-retry": "^4.0.0", + "url-join": "^4.0.0" + } + }, + "@semantic-release/gitlab": { + "version": "6.2.2", + "dev": true, + "requires": { + "@semantic-release/error": "^2.2.0", + "aggregate-error": "^3.0.0", + "debug": "^4.0.0", + "dir-glob": "^3.0.0", + "escape-string-regexp": "^3.0.0", + "form-data": "^3.0.0", + "fs-extra": "^9.0.0", + "globby": "^11.0.0", + "got": "^10.5.2", + "lodash": "^4.17.11", + "parse-path": "^4.0.0", + "url-join": "^4.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "3.0.0", + "dev": true + } + } + }, + "@semantic-release/npm": { + "version": "7.1.3", + "dev": true, + "requires": { + "@semantic-release/error": "^2.2.0", + "aggregate-error": "^3.0.0", + "execa": "^5.0.0", + "fs-extra": "^10.0.0", + "lodash": "^4.17.15", + "nerf-dart": "^1.0.0", + "normalize-url": "^6.0.0", + "npm": "^7.0.0", + "rc": "^1.2.8", + "read-pkg": "^5.0.0", + "registry-auth-token": "^4.0.0", + "semver": "^7.1.2", + "tempy": "^1.0.0" + }, + "dependencies": { + "fs-extra": { + "version": "10.0.0", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "semver": { + "version": "7.3.4", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@semantic-release/release-notes-generator": { + "version": "9.0.1", + "dev": true, + "requires": { + "conventional-changelog-angular": "^5.0.0", + "conventional-changelog-writer": "^4.0.0", + "conventional-commits-filter": "^2.0.0", + "conventional-commits-parser": "^3.0.0", + "debug": "^4.0.0", + "get-stream": "^5.0.0", + "import-from": "^3.0.0", + "into-stream": "^5.0.0", + "lodash": "^4.17.4", + "read-pkg-up": "^7.0.0" + } + }, + "@sindresorhus/is": { + "version": "2.1.1", + "dev": true + }, + "@szmarczak/http-timer": { + "version": "4.0.6", + "dev": true, + "requires": { + "defer-to-connect": "^2.0.0" + } + }, + "@tootallnate/once": { + "version": "1.1.2", + "dev": true + }, + "@types/cacheable-request": { + "version": "6.0.2", + "dev": true, + "requires": { + "@types/http-cache-semantics": "*", + "@types/keyv": "*", + "@types/node": "*", + "@types/responselike": "*" + } + }, + "@types/glob": { + "version": "7.1.4", + "dev": true, + "requires": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "@types/http-cache-semantics": { + "version": "4.0.1", + "dev": true + }, + "@types/keyv": { + "version": "3.1.2", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/minimatch": { + "version": "3.0.5", + "dev": true + }, + "@types/minimist": { + "version": "1.2.1", + "dev": true + }, + "@types/node": { + "version": "14.14.22", + "dev": true + }, + "@types/normalize-package-data": { + "version": "2.4.0", + "dev": true + }, + "@types/parse-json": { + "version": "4.0.0", + "dev": true + }, + "@types/responselike": { + "version": "1.0.0", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/retry": { + "version": "0.12.0", + "dev": true + }, + "aggregate-error": { + "version": "3.1.0", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ansi-escapes": { + "version": "4.3.2", + "dev": true, + "requires": { + "type-fest": "^0.21.3" + }, + "dependencies": { + "type-fest": { + "version": "0.21.3", + "dev": true + } + } + }, + "ansi-regex": { + "version": "5.0.0", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "ansicolors": { + "version": "0.3.2", + "dev": true + }, + "argv-formatter": { + "version": "1.0.0", + "dev": true + }, + "array-ify": { + "version": "1.0.0", + "dev": true + }, + "array-union": { + "version": "2.1.0", + "dev": true + }, + "arrify": { + "version": "1.0.1", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "dev": true + }, + "at-least-node": { + "version": "1.0.0", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "dev": true + }, + "before-after-hook": { + "version": "2.1.0", + "dev": true + }, + "bottleneck": { + "version": "2.19.5", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "cacheable-lookup": { + "version": "2.0.1", + "dev": true, + "requires": { + "@types/keyv": "^3.1.1", + "keyv": "^4.0.0" + } + }, + "cacheable-request": { + "version": "7.0.2", + "dev": true, + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + } + }, + "call-bind": { + "version": "1.0.2", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "callsites": { + "version": "3.1.0", + "dev": true + }, + "camelcase-keys": { + "version": "6.2.2", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "dev": true + }, + "map-obj": { + "version": "4.1.0", + "dev": true + } + } + }, + "cardinal": { + "version": "2.1.1", + "dev": true, + "requires": { + "ansicolors": "~0.3.2", + "redeyed": "~2.1.0" + } + }, + "chalk": { + "version": "2.4.2", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "color-convert": { + "version": "1.9.3", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "clean-stack": { + "version": "2.2.0", + "dev": true + }, + "cli-table": { + "version": "0.3.6", + "dev": true, + "requires": { + "colors": "1.0.3" + } + }, + "cliui": { + "version": "7.0.4", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "clone-response": { + "version": "1.0.2", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + }, + "dependencies": { + "mimic-response": { + "version": "1.0.1", + "dev": true + } + } + }, + "color-convert": { + "version": "2.0.1", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "dev": true + }, + "colors": { + "version": "1.0.3", + "dev": true + }, + "combined-stream": { + "version": "1.0.8", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commitlint-gitlab-ci": { + "version": "0.0.4", + "dev": true, + "requires": { + "@commitlint/cli": "^13.1.0" + } + }, + "compare-func": { + "version": "2.0.0", + "dev": true, + "requires": { + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" + } + }, + "concat-map": { + "version": "0.0.1", + "dev": true + }, + "conventional-changelog-angular": { + "version": "5.0.12", + "dev": true, + "requires": { + "compare-func": "^2.0.0", + "q": "^1.5.1" + } + }, + "conventional-changelog-conventionalcommits": { + "version": "4.5.0", + "dev": true, + "requires": { + "compare-func": "^2.0.0", + "lodash": "^4.17.15", + "q": "^1.5.1" + } + }, + "conventional-changelog-writer": { + "version": "4.1.0", + "dev": true, + "requires": { + "compare-func": "^2.0.0", + "conventional-commits-filter": "^2.0.7", + "dateformat": "^3.0.0", + "handlebars": "^4.7.6", + "json-stringify-safe": "^5.0.1", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "semver": "^6.0.0", + "split": "^1.0.0", + "through2": "^4.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "semver": { + "version": "6.3.0", + "dev": true + }, + "string_decoder": { + "version": "1.3.0", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "through2": { + "version": "4.0.2", + "dev": true, + "requires": { + "readable-stream": "3" + } + } + } + }, + "conventional-commits-filter": { + "version": "2.0.7", + "dev": true, + "requires": { + "lodash.ismatch": "^4.4.0", + "modify-values": "^1.0.0" + } + }, + "conventional-commits-parser": { + "version": "3.2.0", + "dev": true, + "requires": { + "is-text-path": "^1.0.1", + "JSONStream": "^1.0.4", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "split2": "^2.0.0", + "through2": "^4.0.0", + "trim-off-newlines": "^1.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "dev": true + }, + "split2": { + "version": "2.2.0", + "dev": true, + "requires": { + "through2": "^2.0.2" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "through2": { + "version": "2.0.5", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + } + } + }, + "string_decoder": { + "version": "1.3.0", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "dev": true + } + } + }, + "through2": { + "version": "4.0.2", + "dev": true, + "requires": { + "readable-stream": "3" + } + } + } + }, + "core-util-is": { + "version": "1.0.2", + "dev": true + }, + "cosmiconfig": { + "version": "7.0.0", + "dev": true, + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + } + }, + "cross-spawn": { + "version": "7.0.3", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "dependencies": { + "which": { + "version": "2.0.2", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "crypto-random-string": { + "version": "2.0.0", + "dev": true + }, + "dargs": { + "version": "7.0.0", + "dev": true + }, + "dateformat": { + "version": "3.0.3", + "dev": true + }, + "debug": { + "version": "4.3.1", + "dev": true, + "requires": { + "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "dev": true + } + } + }, + "decamelize": { + "version": "1.2.0", + "dev": true + }, + "decamelize-keys": { + "version": "1.1.0", + "dev": true, + "requires": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + } + }, + "decode-uri-component": { + "version": "0.2.0", + "dev": true + }, + "decompress-response": { + "version": "5.0.0", + "dev": true, + "requires": { + "mimic-response": "^2.0.0" + } + }, + "deep-extend": { + "version": "0.6.0", + "dev": true + }, + "defer-to-connect": { + "version": "2.0.1", + "dev": true + }, + "del": { + "version": "6.0.0", + "dev": true, + "requires": { + "globby": "^11.0.1", + "graceful-fs": "^4.2.4", + "is-glob": "^4.0.1", + "is-path-cwd": "^2.2.0", + "is-path-inside": "^3.0.2", + "p-map": "^4.0.0", + "rimraf": "^3.0.2", + "slash": "^3.0.0" + }, + "dependencies": { + "p-map": { + "version": "4.0.0", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "rimraf": { + "version": "3.0.2", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "dev": true + }, + "deprecation": { + "version": "2.3.1", + "dev": true + }, + "dir-glob": { + "version": "3.0.1", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "dot-prop": { + "version": "5.3.0", + "dev": true, + "requires": { + "is-obj": "^2.0.0" + } + }, + "duplexer2": { + "version": "0.1.4", + "dev": true, + "requires": { + "readable-stream": "^2.0.2" + } + }, + "duplexer3": { + "version": "0.1.4", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "dev": true + }, + "end-of-stream": { + "version": "1.4.4", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "env-ci": { + "version": "5.0.2", + "dev": true, + "requires": { + "execa": "^4.0.0", + "java-properties": "^1.0.0" + }, + "dependencies": { + "execa": { + "version": "4.1.0", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + } + } + } + }, + "error-ex": { + "version": "1.3.2", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "escalade": { + "version": "3.1.1", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "dev": true + }, + "esprima": { + "version": "4.0.1", + "dev": true + }, + "execa": { + "version": "5.1.1", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "dependencies": { + "get-stream": { + "version": "6.0.1", + "dev": true + }, + "human-signals": { + "version": "2.1.0", + "dev": true + } + } + }, + "fast-glob": { + "version": "3.2.5", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.0", + "merge2": "^1.3.0", + "micromatch": "^4.0.2", + "picomatch": "^2.2.1" + } + }, + "fastq": { + "version": "1.10.0", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "figures": { + "version": "3.2.0", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "fill-range": { + "version": "7.0.1", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "filter-obj": { + "version": "1.1.0", + "dev": true + }, + "find-up": { + "version": "2.1.0", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "find-versions": { + "version": "4.0.0", + "dev": true, + "requires": { + "semver-regex": "^3.1.2" + } + }, + "form-data": { + "version": "3.0.1", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "from2": { + "version": "2.3.0", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "fs-extra": { + "version": "9.1.0", + "dev": true, + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "dev": true + }, + "get-intrinsic": { + "version": "1.1.1", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, + "get-stream": { + "version": "5.2.0", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "git-log-parser": { + "version": "1.2.0", + "dev": true, + "requires": { + "argv-formatter": "~1.0.0", + "spawn-error-forwarder": "~1.0.0", + "split2": "~1.0.0", + "stream-combiner2": "~1.1.1", + "through2": "~2.0.0", + "traverse": "~0.6.6" + } + }, + "git-raw-commits": { + "version": "2.0.9", + "dev": true, + "requires": { + "dargs": "^7.0.0", + "lodash.template": "^4.0.2", + "meow": "^8.0.0", + "split2": "^3.0.0", + "through2": "^4.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "split2": { + "version": "3.2.2", + "dev": true, + "requires": { + "readable-stream": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.3.0", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "through2": { + "version": "4.0.2", + "dev": true, + "requires": { + "readable-stream": "3" + } + } + } + }, + "glob": { + "version": "7.1.7", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.1", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "glob-promise": { + "version": "4.2.0", + "dev": true, + "requires": { + "@types/glob": "^7.1.3" + } + }, + "global-dirs": { + "version": "0.1.1", + "dev": true, + "requires": { + "ini": "^1.3.4" + } + }, + "globby": { + "version": "11.0.2", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + } + }, + "got": { + "version": "10.7.0", + "dev": true, + "requires": { + "@sindresorhus/is": "^2.0.0", + "@szmarczak/http-timer": "^4.0.0", + "@types/cacheable-request": "^6.0.1", + "cacheable-lookup": "^2.0.0", + "cacheable-request": "^7.0.1", + "decompress-response": "^5.0.0", + "duplexer3": "^0.1.4", + "get-stream": "^5.0.0", + "lowercase-keys": "^2.0.0", + "mimic-response": "^2.1.0", + "p-cancelable": "^2.0.0", + "p-event": "^4.0.0", + "responselike": "^2.0.0", + "to-readable-stream": "^2.0.0", + "type-fest": "^0.10.0" + }, + "dependencies": { + "type-fest": { + "version": "0.10.0", + "dev": true + } + } + }, + "graceful-fs": { + "version": "4.2.4", + "dev": true + }, + "handlebars": { + "version": "4.7.6", + "dev": true, + "requires": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4", + "wordwrap": "^1.0.0" + } + }, + "hard-rejection": { + "version": "2.1.0", + "dev": true + }, + "has": { + "version": "1.0.3", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "4.0.0", + "dev": true + }, + "has-symbols": { + "version": "1.0.2", + "dev": true + }, + "hook-std": { + "version": "2.0.0", + "dev": true + }, + "hosted-git-info": { + "version": "2.8.8", + "dev": true + }, + "http-cache-semantics": { + "version": "4.1.0", + "dev": true + }, + "http-proxy-agent": { + "version": "4.0.1", + "dev": true, + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "dependencies": { + "agent-base": { + "version": "6.0.2", + "dev": true, + "requires": { + "debug": "4" + } + } + } + }, + "https-proxy-agent": { + "version": "5.0.0", + "dev": true, + "requires": { + "agent-base": "6", + "debug": "4" + }, + "dependencies": { + "agent-base": { + "version": "6.0.2", + "dev": true, + "requires": { + "debug": "4" + } + } + } + }, + "human-signals": { + "version": "1.1.1", + "dev": true + }, + "husky": { + "version": "7.0.2", + "dev": true + }, + "ignore": { + "version": "5.1.8", + "dev": true + }, + "import-fresh": { + "version": "3.3.0", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "4.0.0", + "dev": true + } + } + }, + "import-from": { + "version": "3.0.0", + "dev": true, + "requires": { + "resolve-from": "^5.0.0" + } + }, + "indent-string": { + "version": "4.0.0", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "dev": true + }, + "ini": { + "version": "1.3.8", + "dev": true + }, + "into-stream": { + "version": "5.1.1", + "dev": true, + "requires": { + "from2": "^2.3.0", + "p-is-promise": "^3.0.0" + } + }, + "is-arrayish": { + "version": "0.2.1", + "dev": true + }, + "is-core-module": { + "version": "2.2.0", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-extglob": { + "version": "2.1.1", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "dev": true + }, + "is-obj": { + "version": "2.0.0", + "dev": true + }, + "is-path-cwd": { + "version": "2.2.0", + "dev": true + }, + "is-path-inside": { + "version": "3.0.2", + "dev": true + }, + "is-plain-obj": { + "version": "1.1.0", + "dev": true + }, + "is-plain-object": { + "version": "5.0.0", + "dev": true + }, + "is-ssh": { + "version": "1.3.3", + "dev": true, + "requires": { + "protocols": "^1.1.0" + } + }, + "is-stream": { + "version": "2.0.0", + "dev": true + }, + "is-text-path": { + "version": "1.0.1", + "dev": true, + "requires": { + "text-extensions": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "dev": true + }, + "issue-parser": { + "version": "6.0.0", + "dev": true, + "requires": { + "lodash.capitalize": "^4.2.1", + "lodash.escaperegexp": "^4.1.2", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.uniqby": "^4.7.0" + } + }, + "java-properties": { + "version": "1.0.2", + "dev": true + }, + "js-tokens": { + "version": "4.0.0", + "dev": true + }, + "json-buffer": { + "version": "3.0.1", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "dev": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "dev": true + }, + "jsonfile": { + "version": "6.1.0", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "jsonparse": { + "version": "1.3.1", + "dev": true + }, + "JSONStream": { + "version": "1.3.5", + "dev": true, + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + } + }, + "keyv": { + "version": "4.0.3", + "dev": true, + "requires": { + "json-buffer": "3.0.1" + } + }, + "kind-of": { + "version": "6.0.3", + "dev": true + }, + "lines-and-columns": { + "version": "1.1.6", + "dev": true + }, + "load-json-file": { + "version": "4.0.0", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "parse-json": { + "version": "4.0.0", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "pify": { + "version": "3.0.0", + "dev": true + } + } + }, + "locate-path": { + "version": "2.0.0", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.21", + "dev": true + }, + "lodash._reinterpolate": { + "version": "3.0.0", + "dev": true + }, + "lodash.capitalize": { + "version": "4.2.1", + "dev": true + }, + "lodash.escaperegexp": { + "version": "4.1.2", + "dev": true + }, + "lodash.ismatch": { + "version": "4.4.0", + "dev": true + }, + "lodash.isplainobject": { + "version": "4.0.6", + "dev": true + }, + "lodash.isstring": { + "version": "4.0.1", + "dev": true + }, + "lodash.template": { + "version": "4.5.0", + "dev": true, + "requires": { + "lodash._reinterpolate": "^3.0.0", + "lodash.templatesettings": "^4.0.0" + } + }, + "lodash.templatesettings": { + "version": "4.2.0", + "dev": true, + "requires": { + "lodash._reinterpolate": "^3.0.0" + } + }, + "lodash.uniqby": { + "version": "4.7.0", + "dev": true + }, + "lowercase-keys": { + "version": "2.0.0", + "dev": true + }, + "lru-cache": { + "version": "6.0.0", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "map-obj": { + "version": "1.0.1", + "dev": true + }, + "marked": { + "version": "2.1.3", + "dev": true + }, + "marked-terminal": { + "version": "4.1.1", + "dev": true, + "requires": { + "ansi-escapes": "^4.3.1", + "cardinal": "^2.1.1", + "chalk": "^4.1.0", + "cli-table": "^0.3.1", + "node-emoji": "^1.10.0", + "supports-hyperlinks": "^2.1.0" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } + } + }, + "meow": { + "version": "8.1.2", + "dev": true, + "requires": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "dependencies": { + "hosted-git-info": { + "version": "3.0.7", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "normalize-package-data": { + "version": "3.0.0", + "dev": true, + "requires": { + "hosted-git-info": "^3.0.6", + "resolve": "^1.17.0", + "semver": "^7.3.2", + "validate-npm-package-license": "^3.0.1" + } + }, + "semver": { + "version": "7.3.4", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "type-fest": { + "version": "0.18.1", + "dev": true + } + } + }, + "merge-stream": { + "version": "2.0.0", + "dev": true + }, + "merge2": { + "version": "1.4.1", + "dev": true + }, + "micromatch": { + "version": "4.0.2", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "mime": { + "version": "2.5.0", + "dev": true + }, + "mime-db": { + "version": "1.49.0", + "dev": true + }, + "mime-types": { + "version": "2.1.32", + "dev": true, + "requires": { + "mime-db": "1.49.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "dev": true + }, + "mimic-response": { + "version": "2.1.0", + "dev": true + }, + "min-indent": { + "version": "1.0.1", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "dev": true + }, + "minimist-options": { + "version": "4.1.0", + "dev": true, + "requires": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + } + }, + "modify-values": { + "version": "1.0.1", + "dev": true + }, + "neo-async": { + "version": "2.6.2", + "dev": true + }, + "nerf-dart": { + "version": "1.0.0", + "dev": true + }, + "node-emoji": { + "version": "1.11.0", + "dev": true, + "requires": { + "lodash": "^4.17.21" + } + }, + "node-fetch": { + "version": "2.6.1", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-url": { + "version": "6.1.0", + "dev": true + }, + "npm": { + "version": "7.22.0", + "dev": true, + "requires": { + "@npmcli/arborist": "^2.8.3", + "@npmcli/ci-detect": "^1.2.0", + "@npmcli/config": "^2.3.0", + "@npmcli/map-workspaces": "^1.0.4", + "@npmcli/package-json": "^1.0.1", + "@npmcli/run-script": "^1.8.6", + "abbrev": "~1.1.1", + "ansicolors": "~0.3.2", + "ansistyles": "~0.1.3", + "archy": "~1.0.0", + "cacache": "^15.3.0", + "chalk": "^4.1.2", + "chownr": "^2.0.0", + "cli-columns": "^3.1.2", + "cli-table3": "^0.6.0", + "columnify": "~1.5.4", + "fastest-levenshtein": "^1.0.12", + "glob": "^7.1.7", + "graceful-fs": "^4.2.8", + "hosted-git-info": "^4.0.2", + "ini": "^2.0.0", + "init-package-json": "^2.0.4", + "is-cidr": "^4.0.2", + "json-parse-even-better-errors": "^2.3.1", + "libnpmaccess": "^4.0.2", + "libnpmdiff": "^2.0.4", + "libnpmexec": "^2.0.1", + "libnpmfund": "^1.1.0", + "libnpmhook": "^6.0.2", + "libnpmorg": "^2.0.2", + "libnpmpack": "^2.0.1", + "libnpmpublish": "^4.0.1", + "libnpmsearch": "^3.1.1", + "libnpmteam": "^2.0.3", + "libnpmversion": "^1.2.1", + "make-fetch-happen": "^9.1.0", + "minipass": "^3.1.3", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "mkdirp-infer-owner": "^2.0.0", + "ms": "^2.1.2", + "node-gyp": "^7.1.2", + "nopt": "^5.0.0", + "npm-audit-report": "^2.1.5", + "npm-package-arg": "^8.1.5", + "npm-pick-manifest": "^6.1.1", + "npm-profile": "^5.0.3", + "npm-registry-fetch": "^11.0.0", + "npm-user-validate": "^1.0.1", + "npmlog": "^5.0.1", + "opener": "^1.5.2", + "pacote": "^11.3.5", + "parse-conflict-json": "^1.1.1", + "qrcode-terminal": "^0.12.0", + "read": "~1.0.7", + "read-package-json": "^4.1.1", + "read-package-json-fast": "^2.0.3", + "readdir-scoped-modules": "^1.1.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "ssri": "^8.0.1", + "tar": "^6.1.11", + "text-table": "~0.2.0", + "tiny-relative-date": "^1.3.0", + "treeverse": "^1.0.4", + "validate-npm-package-name": "~3.0.0", + "which": "^2.0.2", + "write-file-atomic": "^3.0.3" + }, + "dependencies": { + "@gar/promisify": { + "version": "1.1.2", + "bundled": true, + "dev": true + }, + "@npmcli/arborist": { + "version": "2.8.3", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/installed-package-contents": "^1.0.7", + "@npmcli/map-workspaces": "^1.0.2", + "@npmcli/metavuln-calculator": "^1.1.0", + "@npmcli/move-file": "^1.1.0", + "@npmcli/name-from-folder": "^1.0.1", + "@npmcli/node-gyp": "^1.0.1", + "@npmcli/package-json": "^1.0.1", + "@npmcli/run-script": "^1.8.2", + "bin-links": "^2.2.1", + "cacache": "^15.0.3", + "common-ancestor-path": "^1.0.1", + "json-parse-even-better-errors": "^2.3.1", + "json-stringify-nice": "^1.1.4", + "mkdirp": "^1.0.4", + "mkdirp-infer-owner": "^2.0.0", + "npm-install-checks": "^4.0.0", + "npm-package-arg": "^8.1.5", + "npm-pick-manifest": "^6.1.0", + "npm-registry-fetch": "^11.0.0", + "pacote": "^11.3.5", + "parse-conflict-json": "^1.1.1", + "proc-log": "^1.0.0", + "promise-all-reject-late": "^1.0.0", + "promise-call-limit": "^1.0.1", + "read-package-json-fast": "^2.0.2", + "readdir-scoped-modules": "^1.1.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "ssri": "^8.0.1", + "treeverse": "^1.0.4", + "walk-up-path": "^1.0.0" + } + }, + "@npmcli/ci-detect": { + "version": "1.3.0", + "bundled": true, + "dev": true + }, + "@npmcli/config": { + "version": "2.3.0", + "bundled": true, + "dev": true, + "requires": { + "ini": "^2.0.0", + "mkdirp-infer-owner": "^2.0.0", + "nopt": "^5.0.0", + "semver": "^7.3.4", + "walk-up-path": "^1.0.0" + } + }, + "@npmcli/disparity-colors": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-styles": "^4.3.0" + } + }, + "@npmcli/fs": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + } + }, + "@npmcli/git": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/promise-spawn": "^1.3.2", + "lru-cache": "^6.0.0", + "mkdirp": "^1.0.4", + "npm-pick-manifest": "^6.1.1", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^2.0.2" + } + }, + "@npmcli/installed-package-contents": { + "version": "1.0.7", + "bundled": true, + "dev": true, + "requires": { + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "@npmcli/map-workspaces": { + "version": "1.0.4", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/name-from-folder": "^1.0.1", + "glob": "^7.1.6", + "minimatch": "^3.0.4", + "read-package-json-fast": "^2.0.1" + } + }, + "@npmcli/metavuln-calculator": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "requires": { + "cacache": "^15.0.5", + "pacote": "^11.1.11", + "semver": "^7.3.2" + } + }, + "@npmcli/move-file": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "requires": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + } + }, + "@npmcli/name-from-folder": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "@npmcli/node-gyp": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "@npmcli/package-json": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "json-parse-even-better-errors": "^2.3.1" + } + }, + "@npmcli/promise-spawn": { + "version": "1.3.2", + "bundled": true, + "dev": true, + "requires": { + "infer-owner": "^1.0.4" + } + }, + "@npmcli/run-script": { + "version": "1.8.6", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/node-gyp": "^1.0.2", + "@npmcli/promise-spawn": "^1.3.2", + "node-gyp": "^7.1.0", + "read-package-json-fast": "^2.0.1" + } + }, + "@tootallnate/once": { + "version": "1.1.2", + "bundled": true, + "dev": true + }, + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true + }, + "agent-base": { + "version": "6.0.2", + "bundled": true, + "dev": true, + "requires": { + "debug": "4" + } + }, + "agentkeepalive": { + "version": "4.1.4", + "bundled": true, + "dev": true, + "requires": { + "debug": "^4.1.0", + "depd": "^1.1.2", + "humanize-ms": "^1.2.1" + } + }, + "aggregate-error": { + "version": "3.1.0", + "bundled": true, + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ajv": { + "version": "6.12.6", + "bundled": true, + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "bundled": true, + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "ansicolors": { + "version": "0.3.2", + "bundled": true, + "dev": true + }, + "ansistyles": { + "version": "0.1.3", + "bundled": true, + "dev": true + }, + "aproba": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "archy": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "are-we-there-yet": { + "version": "1.1.6", + "bundled": true, + "dev": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + } + }, + "asap": { + "version": "2.0.6", + "bundled": true, + "dev": true + }, + "asn1": { + "version": "0.2.4", + "bundled": true, + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "bundled": true, + "dev": true + }, + "aws-sign2": { + "version": "0.7.0", + "bundled": true, + "dev": true + }, + "aws4": { + "version": "1.11.0", + "bundled": true, + "dev": true + }, + "balanced-match": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "bin-links": { + "version": "2.2.1", + "bundled": true, + "dev": true, + "requires": { + "cmd-shim": "^4.0.1", + "mkdirp": "^1.0.3", + "npm-normalize-package-bin": "^1.0.0", + "read-cmd-shim": "^2.0.0", + "rimraf": "^3.0.0", + "write-file-atomic": "^3.0.3" + } + }, + "binary-extensions": { + "version": "2.2.0", + "bundled": true, + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "builtins": { + "version": "1.0.3", + "bundled": true, + "dev": true + }, + "cacache": { + "version": "15.3.0", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/fs": "^1.0.0", + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + } + }, + "caseless": { + "version": "0.12.0", + "bundled": true, + "dev": true + }, + "chalk": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "chownr": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "cidr-regex": { + "version": "3.1.1", + "bundled": true, + "dev": true, + "requires": { + "ip-regex": "^4.1.0" + } + }, + "clean-stack": { + "version": "2.2.0", + "bundled": true, + "dev": true + }, + "cli-columns": { + "version": "3.1.2", + "bundled": true, + "dev": true, + "requires": { + "string-width": "^2.0.0", + "strip-ansi": "^3.0.1" + } + }, + "cli-table3": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "requires": { + "colors": "^1.1.2", + "object-assign": "^4.1.0", + "string-width": "^4.2.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "bundled": true, + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "string-width": { + "version": "4.2.2", + "bundled": true, + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, + "clone": { + "version": "1.0.4", + "bundled": true, + "dev": true + }, + "cmd-shim": { + "version": "4.1.0", + "bundled": true, + "dev": true, + "requires": { + "mkdirp-infer-owner": "^2.0.0" + } + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "color-convert": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "bundled": true, + "dev": true + }, + "color-support": { + "version": "1.1.3", + "bundled": true, + "dev": true + }, + "colors": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "optional": true + }, + "columnify": { + "version": "1.5.4", + "bundled": true, + "dev": true, + "requires": { + "strip-ansi": "^3.0.0", + "wcwidth": "^1.0.0" + } + }, + "combined-stream": { + "version": "1.0.8", + "bundled": true, + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "common-ancestor-path": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "dashdash": { + "version": "1.14.1", + "bundled": true, + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "debug": { + "version": "4.3.2", + "bundled": true, + "dev": true, + "requires": { + "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "bundled": true, + "dev": true + } + } + }, + "debuglog": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "defaults": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "requires": { + "clone": "^1.0.2" + } + }, + "delayed-stream": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "depd": { + "version": "1.1.2", + "bundled": true, + "dev": true + }, + "dezalgo": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "requires": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "diff": { + "version": "5.0.0", + "bundled": true, + "dev": true + }, + "ecc-jsbn": { + "version": "0.1.2", + "bundled": true, + "dev": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "emoji-regex": { + "version": "8.0.0", + "bundled": true, + "dev": true + }, + "encoding": { + "version": "0.1.13", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "iconv-lite": "^0.6.2" + } + }, + "env-paths": { + "version": "2.2.1", + "bundled": true, + "dev": true + }, + "err-code": { + "version": "2.0.3", + "bundled": true, + "dev": true + }, + "extend": { + "version": "3.0.2", + "bundled": true, + "dev": true + }, + "extsprintf": { + "version": "1.3.0", + "bundled": true, + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "bundled": true, + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "bundled": true, + "dev": true + }, + "fastest-levenshtein": { + "version": "1.0.12", + "bundled": true, + "dev": true + }, + "forever-agent": { + "version": "0.6.1", + "bundled": true, + "dev": true + }, + "fs-minipass": { + "version": "2.1.0", + "bundled": true, + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "bundled": true, + "dev": true + }, + "gauge": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1 || ^2.0.0", + "strip-ansi": "^3.0.1 || ^4.0.0", + "wide-align": "^1.1.2" + } + }, + "getpass": { + "version": "0.1.7", + "bundled": true, + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.7", + "bundled": true, + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.2.8", + "bundled": true, + "dev": true + }, + "har-schema": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "har-validator": { + "version": "5.1.5", + "bundled": true, + "dev": true, + "requires": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + } + }, + "has": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "4.0.0", + "bundled": true, + "dev": true + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true + }, + "hosted-git-info": { + "version": "4.0.2", + "bundled": true, + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "http-cache-semantics": { + "version": "4.1.0", + "bundled": true, + "dev": true + }, + "http-proxy-agent": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + } + }, + "http-signature": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "https-proxy-agent": { + "version": "5.0.0", + "bundled": true, + "dev": true, + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "humanize-ms": { + "version": "1.2.1", + "bundled": true, + "dev": true, + "requires": { + "ms": "^2.0.0" + } + }, + "iconv-lite": { + "version": "0.6.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, + "ignore-walk": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "imurmurhash": { + "version": "0.1.4", + "bundled": true, + "dev": true + }, + "indent-string": { + "version": "4.0.0", + "bundled": true, + "dev": true + }, + "infer-owner": { + "version": "1.0.4", + "bundled": true, + "dev": true + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "bundled": true, + "dev": true + }, + "ini": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "init-package-json": { + "version": "2.0.4", + "bundled": true, + "dev": true, + "requires": { + "glob": "^7.1.1", + "npm-package-arg": "^8.1.2", + "promzard": "^0.3.0", + "read": "~1.0.1", + "read-package-json": "^4.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "^3.0.0" + } + }, + "ip": { + "version": "1.1.5", + "bundled": true, + "dev": true + }, + "ip-regex": { + "version": "4.3.0", + "bundled": true, + "dev": true + }, + "is-cidr": { + "version": "4.0.2", + "bundled": true, + "dev": true, + "requires": { + "cidr-regex": "^3.1.1" + } + }, + "is-core-module": { + "version": "2.6.0", + "bundled": true, + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "is-lambda": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "is-typedarray": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "isexe": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "isstream": { + "version": "0.1.2", + "bundled": true, + "dev": true + }, + "jsbn": { + "version": "0.1.1", + "bundled": true, + "dev": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "bundled": true, + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "bundled": true, + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "bundled": true, + "dev": true + }, + "json-stringify-nice": { + "version": "1.1.4", + "bundled": true, + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "bundled": true, + "dev": true + }, + "jsonparse": { + "version": "1.3.1", + "bundled": true, + "dev": true + }, + "jsprim": { + "version": "1.4.1", + "bundled": true, + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "just-diff": { + "version": "3.1.1", + "bundled": true, + "dev": true + }, + "just-diff-apply": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "libnpmaccess": { + "version": "4.0.3", + "bundled": true, + "dev": true, + "requires": { + "aproba": "^2.0.0", + "minipass": "^3.1.1", + "npm-package-arg": "^8.1.2", + "npm-registry-fetch": "^11.0.0" + } + }, + "libnpmdiff": { + "version": "2.0.4", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/disparity-colors": "^1.0.1", + "@npmcli/installed-package-contents": "^1.0.7", + "binary-extensions": "^2.2.0", + "diff": "^5.0.0", + "minimatch": "^3.0.4", + "npm-package-arg": "^8.1.4", + "pacote": "^11.3.4", + "tar": "^6.1.0" + } + }, + "libnpmexec": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/arborist": "^2.3.0", + "@npmcli/ci-detect": "^1.3.0", + "@npmcli/run-script": "^1.8.4", + "chalk": "^4.1.0", + "mkdirp-infer-owner": "^2.0.0", + "npm-package-arg": "^8.1.2", + "pacote": "^11.3.1", + "proc-log": "^1.0.0", + "read": "^1.0.7", + "read-package-json-fast": "^2.0.2", + "walk-up-path": "^1.0.0" + } + }, + "libnpmfund": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/arborist": "^2.5.0" + } + }, + "libnpmhook": { + "version": "6.0.3", + "bundled": true, + "dev": true, + "requires": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^11.0.0" + } + }, + "libnpmorg": { + "version": "2.0.3", + "bundled": true, + "dev": true, + "requires": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^11.0.0" + } + }, + "libnpmpack": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/run-script": "^1.8.3", + "npm-package-arg": "^8.1.0", + "pacote": "^11.2.6" + } + }, + "libnpmpublish": { + "version": "4.0.2", + "bundled": true, + "dev": true, + "requires": { + "normalize-package-data": "^3.0.2", + "npm-package-arg": "^8.1.2", + "npm-registry-fetch": "^11.0.0", + "semver": "^7.1.3", + "ssri": "^8.0.1" + } + }, + "libnpmsearch": { + "version": "3.1.2", + "bundled": true, + "dev": true, + "requires": { + "npm-registry-fetch": "^11.0.0" + } + }, + "libnpmteam": { + "version": "2.0.4", + "bundled": true, + "dev": true, + "requires": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^11.0.0" + } + }, + "libnpmversion": { + "version": "1.2.1", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/git": "^2.0.7", + "@npmcli/run-script": "^1.8.4", + "json-parse-even-better-errors": "^2.3.1", + "semver": "^7.3.5", + "stringify-package": "^1.0.1" + } + }, + "lru-cache": { + "version": "6.0.0", + "bundled": true, + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "make-fetch-happen": { + "version": "9.1.0", + "bundled": true, + "dev": true, + "requires": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.2.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.2", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.0.0", + "ssri": "^8.0.0" + } + }, + "mime-db": { + "version": "1.49.0", + "bundled": true, + "dev": true + }, + "mime-types": { + "version": "2.1.32", + "bundled": true, + "dev": true, + "requires": { + "mime-db": "1.49.0" + } + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minipass": { + "version": "3.1.3", + "bundled": true, + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "minipass-collect": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-fetch": { + "version": "1.4.1", + "bundled": true, + "dev": true, + "requires": { + "encoding": "^0.1.12", + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + } + }, + "minipass-flush": { + "version": "1.0.5", + "bundled": true, + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-json-stream": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "jsonparse": "^1.3.1", + "minipass": "^3.0.0" + } + }, + "minipass-pipeline": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-sized": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minizlib": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + } + }, + "mkdirp": { + "version": "1.0.4", + "bundled": true, + "dev": true + }, + "mkdirp-infer-owner": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "chownr": "^2.0.0", + "infer-owner": "^1.0.4", + "mkdirp": "^1.0.3" + } + }, + "ms": { + "version": "2.1.3", + "bundled": true, + "dev": true + }, + "mute-stream": { + "version": "0.0.8", + "bundled": true, + "dev": true + }, + "negotiator": { + "version": "0.6.2", + "bundled": true, + "dev": true + }, + "node-gyp": { + "version": "7.1.2", + "bundled": true, + "dev": true, + "requires": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.3", + "nopt": "^5.0.0", + "npmlog": "^4.1.2", + "request": "^2.88.2", + "rimraf": "^3.0.2", + "semver": "^7.3.2", + "tar": "^6.0.2", + "which": "^2.0.2" + }, + "dependencies": { + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + } + } + }, + "nopt": { + "version": "5.0.0", + "bundled": true, + "dev": true, + "requires": { + "abbrev": "1" + } + }, + "normalize-package-data": { + "version": "3.0.3", + "bundled": true, + "dev": true, + "requires": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + } + }, + "npm-audit-report": { + "version": "2.1.5", + "bundled": true, + "dev": true, + "requires": { + "chalk": "^4.0.0" + } + }, + "npm-bundled": { + "version": "1.1.2", + "bundled": true, + "dev": true, + "requires": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-install-checks": { + "version": "4.0.0", + "bundled": true, + "dev": true, + "requires": { + "semver": "^7.1.1" + } + }, + "npm-normalize-package-bin": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "npm-package-arg": { + "version": "8.1.5", + "bundled": true, + "dev": true, + "requires": { + "hosted-git-info": "^4.0.1", + "semver": "^7.3.4", + "validate-npm-package-name": "^3.0.0" + } + }, + "npm-packlist": { + "version": "2.2.2", + "bundled": true, + "dev": true, + "requires": { + "glob": "^7.1.6", + "ignore-walk": "^3.0.3", + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-pick-manifest": { + "version": "6.1.1", + "bundled": true, + "dev": true, + "requires": { + "npm-install-checks": "^4.0.0", + "npm-normalize-package-bin": "^1.0.1", + "npm-package-arg": "^8.1.2", + "semver": "^7.3.4" + } + }, + "npm-profile": { + "version": "5.0.4", + "bundled": true, + "dev": true, + "requires": { + "npm-registry-fetch": "^11.0.0" + } + }, + "npm-registry-fetch": { + "version": "11.0.0", + "bundled": true, + "dev": true, + "requires": { + "make-fetch-happen": "^9.0.1", + "minipass": "^3.1.3", + "minipass-fetch": "^1.3.0", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.0.0", + "npm-package-arg": "^8.0.0" + } + }, + "npm-user-validate": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "npmlog": { + "version": "5.0.1", + "bundled": true, + "dev": true, + "requires": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + }, + "dependencies": { + "are-we-there-yet": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + } + } + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "oauth-sign": { + "version": "0.9.0", + "bundled": true, + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "opener": { + "version": "1.5.2", + "bundled": true, + "dev": true + }, + "p-map": { + "version": "4.0.0", + "bundled": true, + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "pacote": { + "version": "11.3.5", + "bundled": true, + "dev": true, + "requires": { + "@npmcli/git": "^2.1.0", + "@npmcli/installed-package-contents": "^1.0.6", + "@npmcli/promise-spawn": "^1.2.0", + "@npmcli/run-script": "^1.8.2", + "cacache": "^15.0.5", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "infer-owner": "^1.0.4", + "minipass": "^3.1.3", + "mkdirp": "^1.0.3", + "npm-package-arg": "^8.0.1", + "npm-packlist": "^2.1.4", + "npm-pick-manifest": "^6.0.0", + "npm-registry-fetch": "^11.0.0", + "promise-retry": "^2.0.1", + "read-package-json-fast": "^2.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.1.0" + } + }, + "parse-conflict-json": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "requires": { + "json-parse-even-better-errors": "^2.3.0", + "just-diff": "^3.0.1", + "just-diff-apply": "^3.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "performance-now": { + "version": "2.1.0", + "bundled": true, + "dev": true + }, + "proc-log": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "promise-all-reject-late": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "promise-call-limit": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "promise-inflight": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "promise-retry": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "requires": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + } + }, + "promzard": { + "version": "0.3.0", + "bundled": true, + "dev": true, + "requires": { + "read": "1" + } + }, + "psl": { + "version": "1.8.0", + "bundled": true, + "dev": true + }, + "punycode": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "qrcode-terminal": { + "version": "0.12.0", + "bundled": true, + "dev": true + }, + "qs": { + "version": "6.5.2", + "bundled": true, + "dev": true + }, + "read": { + "version": "1.0.7", + "bundled": true, + "dev": true, + "requires": { + "mute-stream": "~0.0.4" + } + }, + "read-cmd-shim": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "read-package-json": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "requires": { + "glob": "^7.1.1", + "json-parse-even-better-errors": "^2.3.0", + "normalize-package-data": "^3.0.0", + "npm-normalize-package-bin": "^1.0.0" + } + }, + "read-package-json-fast": { + "version": "2.0.3", + "bundled": true, + "dev": true, + "requires": { + "json-parse-even-better-errors": "^2.3.0", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "readable-stream": { + "version": "3.6.0", + "bundled": true, + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readdir-scoped-modules": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "requires": { + "debuglog": "^1.0.1", + "dezalgo": "^1.0.0", + "graceful-fs": "^4.1.2", + "once": "^1.3.0" + } + }, + "request": { + "version": "2.88.2", + "bundled": true, + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "form-data": { + "version": "2.3.3", + "bundled": true, + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "tough-cookie": { + "version": "2.5.0", + "bundled": true, + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + } + } + }, + "retry": { + "version": "0.12.0", + "bundled": true, + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.2.1", + "bundled": true, + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true + }, + "semver": { + "version": "7.3.5", + "bundled": true, + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true + }, + "signal-exit": { + "version": "3.0.3", + "bundled": true, + "dev": true + }, + "smart-buffer": { + "version": "4.2.0", + "bundled": true, + "dev": true + }, + "socks": { + "version": "2.6.1", + "bundled": true, + "dev": true, + "requires": { + "ip": "^1.1.5", + "smart-buffer": "^4.1.0" + } + }, + "socks-proxy-agent": { + "version": "6.0.0", + "bundled": true, + "dev": true, + "requires": { + "agent-base": "^6.0.2", + "debug": "^4.3.1", + "socks": "^2.6.1" + } + }, + "spdx-correct": { + "version": "3.1.1", + "bundled": true, + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "bundled": true, + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.10", + "bundled": true, + "dev": true + }, + "sshpk": { + "version": "1.16.1", + "bundled": true, + "dev": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "ssri": { + "version": "8.0.1", + "bundled": true, + "dev": true, + "requires": { + "minipass": "^3.1.1" + } + }, + "string_decoder": { + "version": "1.3.0", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "string-width": { + "version": "2.1.1", + "bundled": true, + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "bundled": true, + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "stringify-package": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "bundled": true, + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "tar": { + "version": "6.1.11", + "bundled": true, + "dev": true, + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + } + }, + "text-table": { + "version": "0.2.0", + "bundled": true, + "dev": true + }, + "tiny-relative-date": { + "version": "1.3.0", + "bundled": true, + "dev": true + }, + "treeverse": { + "version": "1.0.4", + "bundled": true, + "dev": true + }, + "tunnel-agent": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "bundled": true, + "dev": true + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "bundled": true, + "dev": true, + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "unique-filename": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.2", + "bundled": true, + "dev": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "uri-js": { + "version": "4.4.1", + "bundled": true, + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "uuid": { + "version": "3.4.0", + "bundled": true, + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "validate-npm-package-name": { + "version": "3.0.0", + "bundled": true, + "dev": true, + "requires": { + "builtins": "^1.0.3" + } + }, + "verror": { + "version": "1.10.0", + "bundled": true, + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "walk-up-path": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "wcwidth": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "defaults": "^1.0.3" + } + }, + "which": { + "version": "2.0.2", + "bundled": true, + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "wide-align": { + "version": "1.1.3", + "bundled": true, + "dev": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "write-file-atomic": { + "version": "3.0.3", + "bundled": true, + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "yallist": { + "version": "4.0.0", + "bundled": true, + "dev": true + } + } + }, + "npm-run-path": { + "version": "4.0.1", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "object-inspect": { + "version": "1.11.0", + "dev": true + }, + "once": { + "version": "1.4.0", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "p-cancelable": { + "version": "2.1.1", + "dev": true + }, + "p-each-series": { + "version": "2.2.0", + "dev": true + }, + "p-event": { + "version": "4.2.0", + "dev": true, + "requires": { + "p-timeout": "^3.1.0" + } + }, + "p-filter": { + "version": "2.1.0", + "dev": true, + "requires": { + "p-map": "^2.0.0" + } + }, + "p-finally": { + "version": "1.0.0", + "dev": true + }, + "p-is-promise": { + "version": "3.0.0", + "dev": true + }, + "p-limit": { + "version": "2.3.0", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + }, + "dependencies": { + "p-limit": { + "version": "1.3.0", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-try": { + "version": "1.0.0", + "dev": true + } + } + }, + "p-map": { + "version": "2.1.0", + "dev": true + }, + "p-reduce": { + "version": "2.1.0", + "dev": true + }, + "p-retry": { + "version": "4.2.0", + "dev": true, + "requires": { + "@types/retry": "^0.12.0", + "retry": "^0.12.0" + } + }, + "p-timeout": { + "version": "3.2.0", + "dev": true, + "requires": { + "p-finally": "^1.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "dev": true + }, + "parent-module": { + "version": "1.0.1", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-json": { + "version": "5.2.0", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "parse-path": { + "version": "4.0.3", + "dev": true, + "requires": { + "is-ssh": "^1.3.0", + "protocols": "^1.4.0", + "qs": "^6.9.4", + "query-string": "^6.13.8" + } + }, + "path-exists": { + "version": "3.0.0", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "dev": true + }, + "path-type": { + "version": "4.0.0", + "dev": true + }, + "picomatch": { + "version": "2.2.2", + "dev": true + }, + "pkg-conf": { + "version": "2.1.0", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "load-json-file": "^4.0.0" + } + }, + "process-nextick-args": { + "version": "2.0.1", + "dev": true + }, + "protocols": { + "version": "1.4.8", + "dev": true + }, + "pump": { + "version": "3.0.0", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "q": { + "version": "1.5.1", + "dev": true + }, + "qs": { + "version": "6.10.1", + "dev": true, + "requires": { + "side-channel": "^1.0.4" + } + }, + "query-string": { + "version": "6.14.1", + "dev": true, + "requires": { + "decode-uri-component": "^0.2.0", + "filter-obj": "^1.1.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" + } + }, + "quick-lru": { + "version": "4.0.1", + "dev": true + }, + "rc": { + "version": "1.2.8", + "dev": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + } + }, + "read-pkg": { + "version": "5.2.0", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + } + }, + "read-pkg-up": { + "version": "7.0.1", + "dev": true, + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "dev": true + }, + "type-fest": { + "version": "0.8.1", + "dev": true + } + } + }, + "readable-stream": { + "version": "2.3.7", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "dev": true + } + } + }, + "redent": { + "version": "3.0.0", + "dev": true, + "requires": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + } + }, + "redeyed": { + "version": "2.1.1", + "dev": true, + "requires": { + "esprima": "~4.0.0" + } + }, + "registry-auth-token": { + "version": "4.2.1", + "dev": true, + "requires": { + "rc": "^1.2.8" + } + }, + "require-directory": { + "version": "2.1.1", + "dev": true + }, + "resolve": { + "version": "1.19.0", + "dev": true, + "requires": { + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" + } + }, + "resolve-from": { + "version": "5.0.0", + "dev": true + }, + "resolve-global": { + "version": "1.0.0", + "dev": true, + "requires": { + "global-dirs": "^0.1.1" + } + }, + "responselike": { + "version": "2.0.0", + "dev": true, + "requires": { + "lowercase-keys": "^2.0.0" + } + }, + "retry": { + "version": "0.12.0", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "dev": true + }, + "run-parallel": { + "version": "1.1.10", + "dev": true + }, + "safe-buffer": { + "version": "5.2.1", + "dev": true + }, + "sax": { + "version": "1.2.4", + "dev": true + }, + "semantic-release": { + "version": "17.4.7", + "dev": true, + "requires": { + "@semantic-release/commit-analyzer": "^8.0.0", + "@semantic-release/error": "^2.2.0", + "@semantic-release/github": "^7.0.0", + "@semantic-release/npm": "^7.0.0", + "@semantic-release/release-notes-generator": "^9.0.0", + "aggregate-error": "^3.0.0", + "cosmiconfig": "^7.0.0", + "debug": "^4.0.0", + "env-ci": "^5.0.0", + "execa": "^5.0.0", + "figures": "^3.0.0", + "find-versions": "^4.0.0", + "get-stream": "^6.0.0", + "git-log-parser": "^1.2.0", + "hook-std": "^2.0.0", + "hosted-git-info": "^4.0.0", + "lodash": "^4.17.21", + "marked": "^2.0.0", + "marked-terminal": "^4.1.1", + "micromatch": "^4.0.2", + "p-each-series": "^2.1.0", + "p-reduce": "^2.0.0", + "read-pkg-up": "^7.0.0", + "resolve-from": "^5.0.0", + "semver": "^7.3.2", + "semver-diff": "^3.1.1", + "signale": "^1.2.1", + "yargs": "^16.2.0" + }, + "dependencies": { + "get-stream": { + "version": "6.0.1", + "dev": true + }, + "hosted-git-info": { + "version": "4.0.2", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "semver": { + "version": "7.3.4", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yargs": { + "version": "16.2.0", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + } + } + }, + "semantic-release-dotnet": { + "version": "1.0.0", + "dev": true, + "requires": { + "glob": "^7.1.7", + "glob-promise": "^4.2.0", + "xml-js": "^1.6.11" + } + }, + "semantic-release-nuget": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/semantic-release-nuget/-/semantic-release-nuget-1.1.0.tgz", + "integrity": "sha512-Eaqb39E+dPTgYgHif0/P6cUj7IxZxI9e2BgmfE+NkRlPArpRPa94UZTi8UFMbEKiKg0IaNnAO79yexUPcZnuHA==", + "dev": true + }, + "semver": { + "version": "5.7.1", + "dev": true + }, + "semver-diff": { + "version": "3.1.1", + "dev": true, + "requires": { + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "dev": true + } + } + }, + "semver-regex": { + "version": "3.1.2", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "dev": true + }, + "side-channel": { + "version": "1.0.4", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "signal-exit": { + "version": "3.0.3", + "dev": true + }, + "signale": { + "version": "1.4.0", + "dev": true, + "requires": { + "chalk": "^2.3.2", + "figures": "^2.0.0", + "pkg-conf": "^2.1.0" + }, + "dependencies": { + "figures": { + "version": "2.0.0", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + } + } + }, + "slash": { + "version": "3.0.0", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "dev": true + }, + "spawn-error-forwarder": { + "version": "1.0.0", + "dev": true + }, + "spdx-correct": { + "version": "3.1.1", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.7", + "dev": true + }, + "split": { + "version": "1.0.1", + "dev": true, + "requires": { + "through": "2" + } + }, + "split-on-first": { + "version": "1.1.0", + "dev": true + }, + "split2": { + "version": "1.0.0", + "dev": true, + "requires": { + "through2": "~2.0.0" + } + }, + "stream-combiner2": { + "version": "1.1.1", + "dev": true, + "requires": { + "duplexer2": "~0.1.0", + "readable-stream": "^2.0.2" + } + }, + "strict-uri-encode": { + "version": "2.0.0", + "dev": true + }, + "string_decoder": { + "version": "1.1.1", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "dev": true + } + } + }, + "string-width": { + "version": "4.2.2", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "dev": true + }, + "strip-final-newline": { + "version": "2.0.0", + "dev": true + }, + "strip-indent": { + "version": "3.0.0", + "dev": true, + "requires": { + "min-indent": "^1.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "supports-hyperlinks": { + "version": "2.2.0", + "dev": true, + "requires": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + } + }, + "temp-dir": { + "version": "2.0.0", + "dev": true + }, + "tempy": { + "version": "1.0.0", + "dev": true, + "requires": { + "del": "^6.0.0", + "is-stream": "^2.0.0", + "temp-dir": "^2.0.0", + "type-fest": "^0.16.0", + "unique-string": "^2.0.0" + }, + "dependencies": { + "type-fest": { + "version": "0.16.0", + "dev": true + } + } + }, + "text-extensions": { + "version": "1.9.0", + "dev": true + }, + "through": { + "version": "2.3.8", + "dev": true + }, + "through2": { + "version": "2.0.5", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "to-readable-stream": { + "version": "2.1.0", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "traverse": { + "version": "0.6.6", + "dev": true + }, + "trim-newlines": { + "version": "3.0.0", + "dev": true + }, + "trim-off-newlines": { + "version": "1.0.1", + "dev": true + }, + "type-fest": { + "version": "0.6.0", + "dev": true + }, + "uglify-js": { + "version": "3.12.5", + "dev": true, + "optional": true + }, + "unique-string": { + "version": "2.0.0", + "dev": true, + "requires": { + "crypto-random-string": "^2.0.0" + } + }, + "universal-user-agent": { + "version": "6.0.0", + "dev": true + }, + "universalify": { + "version": "2.0.0", + "dev": true + }, + "url-join": { + "version": "4.0.1", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "wordwrap": { + "version": "1.0.0", + "dev": true + }, + "wrap-ansi": { + "version": "7.0.0", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "dev": true + }, + "xml-js": { + "version": "1.6.11", + "dev": true, + "requires": { + "sax": "^1.2.4" + } + }, + "xtend": { + "version": "4.0.2", + "dev": true + }, + "y18n": { + "version": "5.0.8", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "dev": true + }, + "yaml": { + "version": "1.10.0", + "dev": true + }, + "yargs": { + "version": "17.1.1", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.4", + "dev": true + }, + "yocto-queue": { + "version": "0.1.0", + "dev": true + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..d8e2770 --- /dev/null +++ b/package.json @@ -0,0 +1,21 @@ +{ + "name": "nitride-cil", + "version": "1.0.0", + "private": true, + "scripts": { + "prepare": "husky install" + }, + "devDependencies": { + "@commitlint/cli": "^13.1.0", + "@commitlint/config-conventional": "^13.1.0", + "@semantic-release/changelog": "^5.0.1", + "@semantic-release/git": "^9.0.0", + "@semantic-release/gitlab": "^6.2.2", + "@semantic-release/npm": "^7.1.3", + "commitlint-gitlab-ci": "^0.0.4", + "husky": "^7.0.2", + "semantic-release": "^17.4.7", + "semantic-release-dotnet": "^1.0.0", + "semantic-release-nuget": "^1.1.0" + } +} diff --git a/release.config.js b/release.config.js new file mode 100644 index 0000000..e0233f1 --- /dev/null +++ b/release.config.js @@ -0,0 +1,20 @@ +module.exports = { + branches: ["main"], + message: "chore(release): v${nextRelease.version}\n\n${nextRelease.notes}", + plugins: [ + "@semantic-release/commit-analyzer", + "@semantic-release/release-notes-generator", + "@semantic-release/npm", + "semantic-release-dotnet", + [ + "semantic-release-nuget", + { + packArguments: ["--include-symbols", "--include-source"], + pushFiles: ["src/*/bin/Debug/*.nupkg"], + }, + ], + "@semantic-release/changelog", + "@semantic-release/git", + "@semantic-release/gitlab", + ], +}; diff --git a/src/Directory.Build.props b/src/Directory.Build.props new file mode 100644 index 0000000..a532a7e --- /dev/null +++ b/src/Directory.Build.props @@ -0,0 +1,14 @@ + + + + true + Dylan Moonfire + Moonfire Games + https://gitlab.com/mfgames-cil/nitride-cil + Git + nitride + https://gitlab.com/mfgames-cil/nitride-cil + MIT + + + diff --git a/src/Nitride.Calendar/CreateCalender.cs b/src/Nitride.Calendar/CreateCalender.cs new file mode 100644 index 0000000..1049919 --- /dev/null +++ b/src/Nitride.Calendar/CreateCalender.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Gallium; +using Ical.Net.CalendarComponents; +using Ical.Net.DataTypes; +using Ical.Net.Serialization; +using Nitride.Contents; +using Nitride.Temporal; +using NodaTime; +using Zio; + +namespace Nitride.Calendar +{ + /// + /// Creates an iCalendar file from all the entities passed into the method + /// that have a NodaTime.Instant component. This will write both past and + /// future events. + /// + [WithProperties] + public partial class CreateCalender : NitrideOperationBase + { + private readonly NitrideClock clock; + + public CreateCalender(NitrideClock clock) + { + this.clock = clock; + } + + /// + /// Gets or sets a callback to get the summary of the event representing + /// the entity. + /// + public Func? GetEventSummary { get; set; } + + /// + /// Gets or sets a callback to get the optional URL of an event for + /// the entity. + /// + public Func? GetEventUrl { get; set; } + + /// + /// Gets or sets the file system path for the resulting calendar. + /// + public UPath? Path { get; set; } + + /// + public override IEnumerable Run(IEnumerable input) + { + this.CheckNotNull(x => x.Path); + this.CheckNotNull(x => x.GetEventSummary); + + IEnumerable output = input + .ForEntities(this.CreateCalendarEntity); + + return output; + } + + private IEnumerable CreateCalendarEntity( + IEnumerable entities) + { + // Create the calendar in the same time zone as the rest of the system. + var calendar = new Ical.Net.Calendar(); + + calendar.TimeZones.Add(new VTimeZone(this.clock.DateTimeZone.Id)); + + // Go through the events and add all of them. + List input = entities.ToList(); + IEnumerable events = + input.Select(this.CreateCalendarEvent); + + calendar.Events.AddRange(events); + + // Create the iCalendar file. + var serializer = new CalendarSerializer(); + string serializedCalendar = serializer.SerializeToString(calendar); + + // Create the calendar entity and populate everything. + Entity calendarEntity = new Entity() + .Set(IsCalendar.Instance) + .Set(this.Path!.Value) + .SetTextContent(serializedCalendar); + + // Return the results along with the new calendar. + return input.Union(new[] { calendarEntity }); + } + + private CalendarEvent CreateCalendarEvent(Entity entity) + { + var instant = entity.Get(); + var when = this.clock.ToDateTime(instant); + string summary = this.GetEventSummary!(entity); + Uri? url = this.GetEventUrl?.Invoke(entity); + var calendarEvent = new CalendarEvent + { + Summary = summary, + Start = new CalDateTime(when), + Url = url, + }; + + return calendarEvent; + } + } +} diff --git a/src/Nitride.Calendar/IsCalendar.cs b/src/Nitride.Calendar/IsCalendar.cs new file mode 100644 index 0000000..c8e761d --- /dev/null +++ b/src/Nitride.Calendar/IsCalendar.cs @@ -0,0 +1,10 @@ +namespace Nitride.Calendar +{ + /// + /// A marker component for identifying an entity that represents a calendar. + /// + public class IsCalendar + { + public static IsCalendar Instance { get; } = new(); + } +} diff --git a/src/Nitride.Calendar/Nitride.Calendar.csproj b/src/Nitride.Calendar/Nitride.Calendar.csproj new file mode 100644 index 0000000..653a79a --- /dev/null +++ b/src/Nitride.Calendar/Nitride.Calendar.csproj @@ -0,0 +1,37 @@ + + + + net5.0 + enable + + + + An extension to Nitride static site generator to generate iCalendar files. + + + + + + + + + + + + + + + + + + True + + + + + Analyzer + False + + + + diff --git a/src/Nitride.Calendar/NitrideCalendarBuilderExtensions.cs b/src/Nitride.Calendar/NitrideCalendarBuilderExtensions.cs new file mode 100644 index 0000000..57f4e54 --- /dev/null +++ b/src/Nitride.Calendar/NitrideCalendarBuilderExtensions.cs @@ -0,0 +1,16 @@ +using Autofac; +using Nitride.Temporal; + +namespace Nitride.Calendar +{ + public static class NitrideCalendarBuilderExtensions + { + public static NitrideBuilder UseCalendar(this NitrideBuilder builder) + { + return builder + .UseTemporal() + .ConfigureContainer( + x => x.RegisterModule()); + } + } +} diff --git a/src/Nitride.Calendar/NitrideCalendarModule.cs b/src/Nitride.Calendar/NitrideCalendarModule.cs new file mode 100644 index 0000000..eb2ac45 --- /dev/null +++ b/src/Nitride.Calendar/NitrideCalendarModule.cs @@ -0,0 +1,6 @@ +namespace Nitride.Calendar +{ + public class NitrideCalendarModule : NitrideModuleBase + { + } +} diff --git a/src/Nitride.Feeds/CreateAtomFeeds.cs b/src/Nitride.Feeds/CreateAtomFeeds.cs new file mode 100644 index 0000000..865e924 --- /dev/null +++ b/src/Nitride.Feeds/CreateAtomFeeds.cs @@ -0,0 +1,123 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Gallium; +using Nitride.Contents; +using Nitride.Feeds.Structure; +using NodaTime; +using Serilog; +using Zio; + +namespace Nitride.Feeds +{ + /// + /// Creates various feeds from the given input. + /// + [WithProperties] + public partial class CreateAtomFeeds : NitrideOperationBase + { + private readonly ILogger logger; + + public CreateAtomFeeds(ILogger logger) + { + this.logger = logger; + this.GetAlternateMimeType = _ => "text/html"; + } + + /// + /// Gets or sets the base URL for all the links. + /// + public string? BaseUrl { get; set; } + + /// + /// Gets or sets the alternate MIME type. + /// + public Func GetAlternateMimeType { get; set; } + + /// + /// Gets or sets the alternate URL associated with the feed. + /// + public Func? GetAlternateUrl { get; set; } + + /// + /// Gets or sets the callback to get the author for the feed. + /// + public Func? GetAuthor { get; set; } + + /// + /// Gets or sets the callback to get the entries associated with the + /// feed. + /// + public Func>? GetEntries { get; set; } + + /// + /// Gets or sets the identifier (typically a URL) of the feed. + /// + public Func? GetId { get; set; } + + /// + /// Gets or sets the callback to get the path of the generated feed. + /// + public Func? GetPath { get; set; } + + /// + /// Gets or sets the rights (license) of the feed. + /// + public Func? GetRights { get; set; } + + /// + /// A callback that gets the title of the feed from the given entity. + /// + public Func? GetTitle { get; set; } + + /// + /// Gets or sets the updated timestamp for the feed. + /// + public Func? GetUpdated { get; set; } + + /// + /// Gets or sets the URL associated with the feed. + /// + public Func? GetUrl { get; set; } + + /// + public override IEnumerable Run(IEnumerable input) + { + this.CheckNotNull(x => this.GetTitle); + this.CheckNotNull(x => this.GetPath); + this.CheckNotNull(x => this.GetEntries); + + return input.SelectMany(this.CreateEntityFeed); + } + + private IEnumerable CreateEntityFeed(Entity entity) + { + // Create the top-level feed. All the nullable callbacks were + // verified in the function that calls this. + var feed = new AtomFeed() + .WithTitle(this.GetTitle?.Invoke(entity)) + .WithId(this.GetId?.Invoke(entity)) + .WithRights(this.GetRights?.Invoke(entity)) + .WithUpdated(this.GetUpdated?.Invoke(entity)) + .WithUrl(this.GetUrl?.Invoke(entity)) + .WithAlternateUrl(this.GetAlternateUrl?.Invoke(entity)) + .WithAlternateMimeType(this.GetAlternateMimeType.Invoke(entity)) + .WithAuthor(this.GetAuthor?.Invoke(entity)) + .ToXElement(); + + // Go through all the items inside the feed and add them. + foreach (var entry in this.GetEntries!(entity)) + { + feed.Add(entry.ToXElement()); + } + + // Create the feed entity and return both objects. + Entity feedEntity = new Entity() + .Set(IsFeed.Instance) + .Set(this.GetPath!(entity)) + .SetTextContent(feed + "\n"); + + return new[] { entity, feedEntity }; + } + } +} diff --git a/src/Nitride.Feeds/HasFeed.cs b/src/Nitride.Feeds/HasFeed.cs new file mode 100644 index 0000000..eaeee87 --- /dev/null +++ b/src/Nitride.Feeds/HasFeed.cs @@ -0,0 +1,15 @@ +namespace Nitride.Feeds +{ + /// + /// A marker component that indicates this entity has a feed associated with + /// it. + /// + public class HasFeed + { + public HasFeed() + { + } + + public static HasFeed Instance { get; } = new(); + } +} diff --git a/src/Nitride.Feeds/IsFeed.cs b/src/Nitride.Feeds/IsFeed.cs new file mode 100644 index 0000000..f38927a --- /dev/null +++ b/src/Nitride.Feeds/IsFeed.cs @@ -0,0 +1,14 @@ +namespace Nitride.Feeds +{ + /// + /// A marker component that indicates this page is a feed. + /// + public class IsFeed + { + public IsFeed() + { + } + + public static IsFeed Instance { get; } = new(); + } +} diff --git a/src/Nitride.Feeds/Nitride.Feeds.csproj b/src/Nitride.Feeds/Nitride.Feeds.csproj new file mode 100644 index 0000000..cbe27f6 --- /dev/null +++ b/src/Nitride.Feeds/Nitride.Feeds.csproj @@ -0,0 +1,36 @@ + + + + net5.0 + enable + + + + An extension to Nitride static site generator to generate Atom feeds. + + + + + + + + + + + + + + + + + True + + + + + Analyzer + False + + + + diff --git a/src/Nitride.Feeds/NitrideFeedsBuilderExtensions.cs b/src/Nitride.Feeds/NitrideFeedsBuilderExtensions.cs new file mode 100644 index 0000000..b9e0ca6 --- /dev/null +++ b/src/Nitride.Feeds/NitrideFeedsBuilderExtensions.cs @@ -0,0 +1,16 @@ +using Autofac; +using Nitride.Temporal; + +namespace Nitride.Feeds +{ + public static class NitrideFeedsBuilderExtensions + { + public static NitrideBuilder UseFeeds(this NitrideBuilder builder) + { + return builder + .UseTemporal() + .ConfigureContainer( + x => x.RegisterModule()); + } + } +} diff --git a/src/Nitride.Feeds/NitrideFeedsModule.cs b/src/Nitride.Feeds/NitrideFeedsModule.cs new file mode 100644 index 0000000..fc5b1ad --- /dev/null +++ b/src/Nitride.Feeds/NitrideFeedsModule.cs @@ -0,0 +1,6 @@ +namespace Nitride.Feeds +{ + public class NitrideFeedsModule : NitrideModuleBase + { + } +} diff --git a/src/Nitride.Feeds/Structure/AtomAuthor.cs b/src/Nitride.Feeds/Structure/AtomAuthor.cs new file mode 100644 index 0000000..0111417 --- /dev/null +++ b/src/Nitride.Feeds/Structure/AtomAuthor.cs @@ -0,0 +1,41 @@ +using System.Xml.Linq; + +namespace Nitride.Feeds.Structure +{ + /// + /// The type-safe structure for an author element. + /// + [WithProperties] + public partial class AtomAuthor + { + /// + /// Gets or sets the name of the author. + /// + public string? Name { get; set; } + + /// + /// Creates an XML element out of the feed along with all items inside + /// the feed. + /// + /// + public XElement? ToXElement() + { + if (this.Name == null) + { + return null; + } + + var author = new XElement(XmlConstants.AtomNamespace + "author"); + + if (!string.IsNullOrEmpty(this.Name)) + { + author.Add( + new XElement( + XmlConstants.AtomNamespace + "name", + new XText(this.Name))); + } + + return author; + } + } +} diff --git a/src/Nitride.Feeds/Structure/AtomCategory.cs b/src/Nitride.Feeds/Structure/AtomCategory.cs new file mode 100644 index 0000000..71928b6 --- /dev/null +++ b/src/Nitride.Feeds/Structure/AtomCategory.cs @@ -0,0 +1,57 @@ +using System; +using System.Xml.Linq; + +namespace Nitride.Feeds.Structure +{ + /// + /// The type-safe structure for a entry's category element. + /// + [WithProperties] + public partial class AtomCategory + { + /// + /// Gets or sets the label associated with the category. + /// + public string? Label { get; set; } + + /// + /// Gets or sets the scheme associated with the category. + /// + public Uri? Scheme { get; set; } + + /// + /// Gets or sets the term of the category. + /// + public string? Term { get; set; } + + /// + /// Creates an XML element out of the feed along with all items inside + /// the feed. + /// + /// + public XElement ToXElement() + { + if (this.Term == null) + { + throw new NullReferenceException( + "Category term cannot be null."); + } + + var elem = new XElement( + XmlConstants.AtomNamespace + "category", + new XAttribute("term", this.Term)); + + if (this.Scheme != null) + { + elem.Add(new XAttribute("scheme", this.Scheme.ToString())); + } + + if (!string.IsNullOrEmpty(this.Label)) + { + elem.Add(new XAttribute("label", this.Label)); + } + + return elem; + } + } +} diff --git a/src/Nitride.Feeds/Structure/AtomEntry.cs b/src/Nitride.Feeds/Structure/AtomEntry.cs new file mode 100644 index 0000000..84b4952 --- /dev/null +++ b/src/Nitride.Feeds/Structure/AtomEntry.cs @@ -0,0 +1,121 @@ +using System; +using System.Collections.Generic; +using System.Xml.Linq; +using NodaTime; +using static Nitride.Feeds.Structure.XmlConstants; + +namespace Nitride.Feeds.Structure +{ + /// + /// The type-safe structure for an entry in the Atom feed. + /// + [WithProperties] + public partial class AtomEntry + { + /// + /// Gets or sets the author for the feed. + /// + public AtomAuthor? Author { get; set; } + + /// + /// Gets or sets the categories associated with this entry. + /// + public IEnumerable? Categories { get; set; } + + /// + /// Gets or sets the content of the entry. + /// + public string? Content { get; set; } + + /// + /// Gets or sets the type of content (text, html) of the content. + /// + public string ContentType { get; set; } = "html"; + + /// + /// Gets or sets the ID of the feed. + /// + public string? Id { get; set; } + + /// + /// Gets or sets the summary of the entry. + /// + public string? Summary { get; set; } + + /// + /// Gets or sets the type of content (text, html) of the summary. + /// + public string SummaryType { get; set; } = "html"; + + /// + /// Gets or sets the title of the Feed. + /// + public string? Title { get; set; } + + /// + /// Gets or sets the timestamp that the feed was updated. + /// + public Instant? Updated { get; set; } + + /// + /// Gets or sets the URL associated with this feed. + /// + public Uri? Url { get; set; } + + /// + /// Creates an XML element out of the feed along with all items inside + /// the feed. + /// + /// + public XElement? ToXElement() + { + var elem = new XElement(AtomNamespace + "entry"); + + AtomHelper.AddIfSet(elem, "title", this.Title); + + if (this.Url != null) + { + elem.Add( + new XElement( + AtomNamespace + "link", + new XAttribute("rel", "alternate"), + new XAttribute("href", this.Url.ToString()))); + } + + AtomHelper.AddIfSet( + elem, + "updated", + this.Updated?.ToString("g", null)); + AtomHelper.AddIfSet(elem, "id", this.Id); + AtomHelper.AddIfSet(elem, this.Author?.ToXElement()); + + if (this.Categories != null) + { + foreach (var category in this.Categories) + { + elem.Add(category.ToXElement()); + } + } + + if (!string.IsNullOrWhiteSpace(this.Summary)) + { + elem.Add( + new XElement( + AtomNamespace + "summary", + new XAttribute("type", this.SummaryType), + new XText(this.Summary))); + } + + if (!string.IsNullOrWhiteSpace(this.Content)) + { + elem.Add( + new XElement( + AtomNamespace + "content", + new XAttribute("type", this.ContentType), + new XText(this.Content))); + } + + return elem; + } + } +} diff --git a/src/Nitride.Feeds/Structure/AtomFeed.cs b/src/Nitride.Feeds/Structure/AtomFeed.cs new file mode 100644 index 0000000..4a12229 --- /dev/null +++ b/src/Nitride.Feeds/Structure/AtomFeed.cs @@ -0,0 +1,104 @@ +using System; +using System.Xml.Linq; +using NodaTime; +using static Nitride.Feeds.Structure.XmlConstants; + +namespace Nitride.Feeds.Structure +{ + /// + /// The type-safe structure of the top-level feed. + /// + [WithProperties] + public partial class AtomFeed + { + /// + /// Gets or sets the MIME type for the alternate URL. + /// + public string AlternateMimeType { get; set; } = "text/html"; + + /// + /// Gets or sets the alternate URL for this feed. + /// + public Uri? AlternateUrl { get; set; } + + /// + /// Gets or sets the author for the feed. + /// + public AtomAuthor? Author { get; set; } + + /// + /// Gets or sets the ID of the feed. + /// + public string? Id { get; set; } + + /// + /// Gets or sets the rights (license) of the feed. + /// + public string? Rights { get; set; } + + /// + /// Gets or sets the title of the Feed. + /// + public string? Title { get; set; } + + /// + /// Gets or sets the timestamp that the feed was updated. + /// + public Instant? Updated { get; set; } + + /// + /// Gets or sets the URL associated with this feed. + /// + public Uri? Url { get; set; } + + /// + /// Creates an XML element out of the feed along with all items inside + /// the feed. + /// + /// + public XElement ToXElement() + { + var elem = new XElement(AtomNamespace + "feed"); + + if (!string.IsNullOrWhiteSpace(this.Title)) + { + elem.Add( + new XElement( + AtomNamespace + "title", + new XAttribute("type", "text"), + new XAttribute(XNamespace.Xml + "lang", "en"), + new XText(this.Title))); + } + + if (this.Url != null) + { + elem.Add( + new XElement( + AtomNamespace + "link", + new XAttribute("type", "application/atom+xml"), + new XAttribute("href", this.Url.ToString()), + new XAttribute("rel", "self"))); + } + + if (this.AlternateUrl != null) + { + elem.Add( + new XElement( + AtomNamespace + "link", + new XAttribute("type", this.AlternateMimeType), + new XAttribute("href", this.AlternateUrl.ToString()), + new XAttribute("rel", "alternate"))); + } + + AtomHelper.AddIfSet( + elem, + "updated", + this.Updated?.ToString("g", null)); + AtomHelper.AddIfSet(elem, "id", this.Id); + AtomHelper.AddIfSet(elem, this.Author?.ToXElement()); + AtomHelper.AddIfSet(elem, "rights", this.Rights); + + return elem; + } + } +} diff --git a/src/Nitride.Feeds/Structure/AtomHelper.cs b/src/Nitride.Feeds/Structure/AtomHelper.cs new file mode 100644 index 0000000..2c6666a --- /dev/null +++ b/src/Nitride.Feeds/Structure/AtomHelper.cs @@ -0,0 +1,29 @@ +using System.Xml.Linq; + +namespace Nitride.Feeds.Structure +{ + /// + /// Helper methods for working with XML elements. + /// + public static class AtomHelper + { + public static void AddIfSet(XElement root, XElement? elem) + { + if (elem != null) + { + root.Add(elem); + } + } + + public static void AddIfSet(XElement elem, string name, string? text) + { + if (!string.IsNullOrWhiteSpace(text)) + { + elem.Add( + new XElement( + XmlConstants.AtomNamespace + name, + new XText(text))); + } + } + } +} diff --git a/src/Nitride.Feeds/Structure/XmlConstants.cs b/src/Nitride.Feeds/Structure/XmlConstants.cs new file mode 100644 index 0000000..a315c04 --- /dev/null +++ b/src/Nitride.Feeds/Structure/XmlConstants.cs @@ -0,0 +1,22 @@ +using System.Xml.Linq; + +namespace Nitride.Feeds.Structure +{ + /// + /// Common constants used while generating feeds. + /// + public static class XmlConstants + { + /// + /// The XML namespace for Atom feeds. + /// + public static readonly XNamespace AtomNamespace = + "http://www.w3.org/2005/Atom"; + + /// + /// The XML namespace for media. + /// + public static readonly XNamespace MediaNamespace = + "http://search.yahoo.com/mrss/"; + } +} diff --git a/src/Nitride.Gemtext/IsGemtext.cs b/src/Nitride.Gemtext/IsGemtext.cs new file mode 100644 index 0000000..352fa3f --- /dev/null +++ b/src/Nitride.Gemtext/IsGemtext.cs @@ -0,0 +1,11 @@ +namespace Nitride.Gemtext +{ + /// + /// A marker component for indicating that an entity is Gemtext, the format + /// for text files using the Gemini protocol. + /// + public class IsGemtext + { + public static IsGemtext Instance { get; } = new IsGemtext(); + } +} diff --git a/src/Nitride.Gemtext/Nitride.Gemtext.csproj b/src/Nitride.Gemtext/Nitride.Gemtext.csproj new file mode 100644 index 0000000..cc60e1c --- /dev/null +++ b/src/Nitride.Gemtext/Nitride.Gemtext.csproj @@ -0,0 +1,27 @@ + + + + net5.0 + + + + An extension to Nitride static site generator to generate Gemtext output. + + + + + + + + + True + + + + + Analyzer + False + + + + diff --git a/src/Nitride.Gemtext/NitrideGemtextBuilderExtensions.cs b/src/Nitride.Gemtext/NitrideGemtextBuilderExtensions.cs new file mode 100644 index 0000000..19b307a --- /dev/null +++ b/src/Nitride.Gemtext/NitrideGemtextBuilderExtensions.cs @@ -0,0 +1,14 @@ +using Autofac; + +namespace Nitride.Gemtext +{ + public static class NitrideGemtextBuilderExtensions + { + public static NitrideBuilder UseGemtext(this NitrideBuilder builder) + { + return builder + .ConfigureContainer( + x => x.RegisterModule()); + } + } +} diff --git a/src/Nitride.Gemtext/NitrideGemtextModule.cs b/src/Nitride.Gemtext/NitrideGemtextModule.cs new file mode 100644 index 0000000..d2866e8 --- /dev/null +++ b/src/Nitride.Gemtext/NitrideGemtextModule.cs @@ -0,0 +1,6 @@ +namespace Nitride.Gemtext +{ + public class NitrideGemtextModule : NitrideModuleBase + { + } +} diff --git a/src/Nitride.Generators/CodeAnalysisExtensions.cs b/src/Nitride.Generators/CodeAnalysisExtensions.cs new file mode 100644 index 0000000..afe66cf --- /dev/null +++ b/src/Nitride.Generators/CodeAnalysisExtensions.cs @@ -0,0 +1,159 @@ +using Microsoft.CodeAnalysis; + +namespace Nitride.Generators +{ + /// + /// Various wrappers around the diagnostics to simplify generation. + /// + public static class CodeAnalysisExtensions + { + /// + /// Creates an error message to break the build while generating code. + /// + /// The context that contains the diagnostic. + /// The normalized message code. + /// The string format for the message. + /// The optional parameters. + public static void Error( + this GeneratorExecutionContext context, + MessageCode messageCode, + string format, + params object[] parameters) + { + Error(context, messageCode, null, format, parameters); + } + + /// + /// Creates an error message to break the build while generating code. + /// + /// The context that contains the diagnostic. + /// The normalized message code. + /// The optional location for the message. + /// The string format for the message. + /// The optional parameters. + public static void Error( + this GeneratorExecutionContext context, + MessageCode messageCode, + Location? location, + string format, + params object[] parameters) + { + context.Message( + messageCode, + location, + DiagnosticSeverity.Error, + format, + parameters); + } + + /// + /// Creates an informational message to break the build while generating code. + /// + /// The context that contains the diagnostic. + /// The normalized message code. + /// The string format for the message. + /// The optional parameters. + public static void Information( + this GeneratorExecutionContext context, + MessageCode messageCode, + string format, + params object[] parameters) + { + Information(context, messageCode, null, format, parameters); + } + + /// + /// Creates an informational message to break the build while generating code. + /// + /// The context that contains the diagnostic. + /// The normalized message code. + /// The optional location for the message. + /// The string format for the message. + /// The optional parameters. + public static void Information( + this GeneratorExecutionContext context, + MessageCode messageCode, + Location? location, + string format, + params object[] parameters) + { + context.Message( + messageCode, + location, + DiagnosticSeverity.Info, + format, + parameters); + } + + /// + /// Creates a warning message to break the build while generating code. + /// + /// The context that contains the diagnostic. + /// The normalized message code. + /// The string format for the message. + /// The optional parameters. + public static void Warning( + this GeneratorExecutionContext context, + MessageCode messageCode, + string format, + params object[] parameters) + { + Warning(context, messageCode, null, format, parameters); + } + + /// + /// Creates a warning message to break the build while generating code. + /// + /// The context that contains the diagnostic. + /// The normalized message code. + /// The optional location for the message. + /// The string format for the message. + /// The optional parameters. + public static void Warning( + this GeneratorExecutionContext context, + MessageCode messageCode, + Location? location, + string format, + params object[] parameters) + { + context.Message( + messageCode, + location, + DiagnosticSeverity.Warning, + format, + parameters); + } + + /// + /// Creates a message to break the build while generating code. + /// + /// The context that contains the diagnostic. + /// The normalized message code. + /// The optional location for the message. + /// The string format for the message. + /// The optional parameters. + /// The severity of the message. + private static void Message( + this GeneratorExecutionContext context, + MessageCode messageCode, + Location? location, + DiagnosticSeverity severity, + string format, + params object[] parameters) + { + context.ReportDiagnostic( + Diagnostic.Create( + "GN" + ((int)messageCode).ToString("D4"), + "Nitride", + string.Format(format, parameters), + severity, + severity, + true, + severity is DiagnosticSeverity.Warning + or DiagnosticSeverity.Info + ? 4 + : 0, + location: location)); + } + } +} diff --git a/src/Nitride.Generators/MessageCode.cs b/src/Nitride.Generators/MessageCode.cs new file mode 100644 index 0000000..98cc85b --- /dev/null +++ b/src/Nitride.Generators/MessageCode.cs @@ -0,0 +1,10 @@ +namespace Nitride.Generators +{ + /// + /// All the error messages produced by the generators. + /// + public enum MessageCode + { + Debug = 1, + } +} diff --git a/src/Nitride.Generators/Nitride.Generators.csproj b/src/Nitride.Generators/Nitride.Generators.csproj new file mode 100644 index 0000000..381ef97 --- /dev/null +++ b/src/Nitride.Generators/Nitride.Generators.csproj @@ -0,0 +1,19 @@ + + + + net5.0 + enable + + + + Common source generators for Nitride. + + + + + + + + + + diff --git a/src/Nitride.Generators/WithPropertySourceGenerator.cs b/src/Nitride.Generators/WithPropertySourceGenerator.cs new file mode 100644 index 0000000..0037a73 --- /dev/null +++ b/src/Nitride.Generators/WithPropertySourceGenerator.cs @@ -0,0 +1,209 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Text; + +namespace Nitride.Generators +{ + /// + /// Implements a source generator that creates Set* methods for the various + /// properties that also returns the same object for purposes of chaining + /// together calls. + /// + [Generator] + public class WithPropertySourceGenerator : ISourceGenerator + { + public void Execute(GeneratorExecutionContext context) + { + // Get the generator infrastructure will create a receiver and + // populate it we can retrieve the populated instance via the + // context. + var syntaxReceiver = (SyntaxReceiver?)context.SyntaxReceiver; + + if (syntaxReceiver == null) + { + return; + } + + // Report any messages. + foreach (var message in syntaxReceiver.Messages) + { + context.Information( + MessageCode.Debug, + Location.Create( + "Temporary.g.cs", + TextSpan.FromBounds(0, 0), + new LinePositionSpan( + new LinePosition(0, 0), + new LinePosition(0, 0))), + "Generating additional identifier code: {0}", + message); + } + + // If we didn't find anything, then there is nothing to do. + if (syntaxReceiver.ClassesToAugment.Count == 0) + { + return; + } + + // Go through each one. + foreach (var cds in syntaxReceiver.ClassesToAugment) + { + this.GenerateClassFile(context, cds); + } + } + + public void Initialize(GeneratorInitializationContext context) + { + // Register a factory that can create our custom syntax receiver + context.RegisterForSyntaxNotifications( + () => new SyntaxReceiver(context)); + } + + private void GenerateClassFile( + GeneratorExecutionContext context, + ClassDeclarationSyntax cds) + { + // Get the namespace. + var nds = (NamespaceDeclarationSyntax?)cds.Parent; + + if (nds == null) + { + return; + } + + string? ns = nds.Name.ToString(); + + // Create the partial class. + StringBuilder buffer = new(); + buffer.AppendLine("#nullable enable"); + + // Copy the using statements from the file. + if (nds.Parent is CompilationUnitSyntax cus) + { + foreach (var uds in cus.Usings) + { + buffer.AppendLine(uds.ToString()); + } + + buffer.AppendLine(); + } + + // Create the namespace. + buffer.AppendLine($"namespace {ns}"); + buffer.AppendLine("{"); + buffer.AppendLine($" public partial class {cds.Identifier}"); + buffer.AppendLine(" {"); + + // Go through the properties of the namespace. + IEnumerable properties = cds.Members + .Where(m => m.Kind() == SyntaxKind.PropertyDeclaration) + .Cast(); + bool first = true; + + foreach (PropertyDeclarationSyntax pds in properties) + { + // See if we have a setter. + bool found = + pds.AccessorList?.Accessors + .Any(x => x.Keyword.ToString() == "set") + ?? false; + + if (!found) + { + continue; + } + + // If we aren't first, then add a newline before it. + if (first) + { + first = false; + } + else + { + buffer.AppendLine(); + } + + // Write some documentation. + buffer.AppendLine(" /// "); + buffer.AppendLine( + string.Format( + " /// Sets the {0} value and returns the operation for chaining.", + pds.Identifier)); + buffer.AppendLine(" /// "); + + // We have the components for writing out a setter. + buffer.AppendLine( + string.Format( + " public {0} With{1}({2} value)", + cds.Identifier, + pds.Identifier, + pds.Type)); + buffer.AppendLine(" {"); + buffer.AppendLine( + string.Format( + " this.{0} = value;", + pds.Identifier)); + buffer.AppendLine(" return this;"); + buffer.AppendLine(" }"); + } + + // Finish up the class. + buffer.AppendLine(" }"); + buffer.AppendLine("}"); + + // Create the source text and write out the file. + SourceText sourceText = SourceText.From( + buffer.ToString(), + Encoding.UTF8); + context.AddSource(cds.Identifier + ".Generated.cs", sourceText); + } + + private class SyntaxReceiver : ISyntaxReceiver + { + private readonly GeneratorInitializationContext context; + + public SyntaxReceiver(GeneratorInitializationContext context) + { + this.context = context; + this.ClassesToAugment = new List(); + this.Messages = new List(); + } + + public List ClassesToAugment { get; } + + public List Messages { get; } + + public void OnVisitSyntaxNode(SyntaxNode syntaxNode) + { + // We only care about class declarations. + if (syntaxNode is not ClassDeclarationSyntax cds) + { + return; + } + + // See if the class has our set properties attribute. + bool found = cds + .AttributeLists + .AsEnumerable() + .SelectMany(x => x.Attributes) + .Select(x => x.Name.ToString()) + .Any( + x => x switch + { + "WithProperties" => true, + "WithPropertiesAttribute" => true, + _ => false, + }); + + if (found) + { + this.ClassesToAugment.Add(cds); + } + } + } + } +} diff --git a/src/Nitride.Handlebars/ApplyContentHandlebarsTemplate.cs b/src/Nitride.Handlebars/ApplyContentHandlebarsTemplate.cs new file mode 100644 index 0000000..2f49644 --- /dev/null +++ b/src/Nitride.Handlebars/ApplyContentHandlebarsTemplate.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using Gallium; +using HandlebarsDotNet; +using Nitride.Contents; + +namespace Nitride.Handlebars +{ + /// + /// An operation that uses the content to create a template that is then + /// applied against the content and metadata and then replaces the content + /// of that entity. + /// + public class ApplyContentHandlebarsTemplate : NitrideOperationBase + { + private readonly HandlebarsTemplateCache cache; + + public ApplyContentHandlebarsTemplate(HandlebarsTemplateCache cache) + { + this.cache = cache; + } + + /// + /// Gets or sets the callback used to create a model from a given + /// entity. This allows for the website to customize what information is + /// being passed to the template. + /// + public Func? CreateModelCallback { get; set; } + + /// + public override IEnumerable Run(IEnumerable input) + { + this.CheckNotNull(x => x.CreateModelCallback); + + return input + .ForEachEntity(this.Apply); + } + + /// + /// Sets the callback for the template and returns the operation to + /// chain operations. + /// + /// The callback to set. + /// The ApplyContentHandlebarsTemplate to chain requests. + public ApplyContentHandlebarsTemplate WithCreateModelCallback( + Func? callback) + { + this.CreateModelCallback = callback; + return this; + } + + private Entity Apply( + Entity entity, + HasHandlebarsTemplate _, + ITextContent content) + { + // Create the model using the callback. + TModel model = this.CreateModelCallback!(entity); + + // Create a template from the contents. + string text = content.GetText(); + HandlebarsTemplate template = + this.cache.GetLiteralTemplate(text); + + // Render the template and create a new entity with the updated + // text. + string result = template(model!); + + return entity + .Remove() + .SetTextContent(new StringTextContent(result)); + } + } +} diff --git a/src/Nitride.Handlebars/ApplyStyleHandlebarsTemplate.cs b/src/Nitride.Handlebars/ApplyStyleHandlebarsTemplate.cs new file mode 100644 index 0000000..84c2278 --- /dev/null +++ b/src/Nitride.Handlebars/ApplyStyleHandlebarsTemplate.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.Generic; +using Gallium; +using HandlebarsDotNet; +using Nitride.Contents; + +namespace Nitride.Handlebars +{ + /// + /// An operation that applies a common or shared template on the content of + /// a document that includes theme or styling information. + /// + public class ApplyStyleHandlebarsTemplate : NitrideOperationBase + { + private readonly HandlebarsTemplateCache cache; + + public ApplyStyleHandlebarsTemplate(HandlebarsTemplateCache cache) + { + this.cache = cache; + } + + /// + /// Gets or sets the callback used to create a model from a given + /// entity. This allows for the website to customize what information is + /// being passed to the template. + /// + public Func? CreateModelCallback { get; set; } + + /// + /// Gets or sets the callback used to determine which template to use + /// for a given entity. This lets one have a per-page template change. + /// + public Func? GetTemplateName { get; set; } + + public IHandlebars? Handlebars { get; set; } + + /// + public override IEnumerable Run(IEnumerable input) + { + // Make sure we have sane data. + this.CheckNotNull(x => x.CreateModelCallback); + this.CheckNotNull(x => x.Handlebars); + this.CheckNotNull(x => x.GetTemplateName); + + // Create and set up the Handlebars. + return input + .ForEachEntity(this.Apply); + } + + /// + /// Sets the callback for the template and returns the operation to + /// chain operations. + /// + /// The callback to set. + /// The ApplyContentHandlebarsTemplate to chain requests. + public ApplyStyleHandlebarsTemplate WithCreateModelCallback( + Func? callback) + { + this.CreateModelCallback = callback; + return this; + } + + public ApplyStyleHandlebarsTemplate WithGetTemplateName( + Func? callback) + { + this.GetTemplateName = callback; + return this; + } + + public ApplyStyleHandlebarsTemplate WithHandlebars( + IHandlebars? handlebars) + { + this.Handlebars = handlebars; + return this; + } + + private Entity Apply(Entity entity, ITextContent content) + { + TModel model = this.CreateModelCallback!(entity); + string name = this.GetTemplateName!(entity); + HandlebarsTemplate template = + this.cache.GetNamedTemplate(name); + string result = template(model!); + + return entity.SetTextContent(result); + } + } +} diff --git a/src/Nitride.Handlebars/HandlebarsTemplateCache.cs b/src/Nitride.Handlebars/HandlebarsTemplateCache.cs new file mode 100644 index 0000000..3451fbd --- /dev/null +++ b/src/Nitride.Handlebars/HandlebarsTemplateCache.cs @@ -0,0 +1,70 @@ +using System.Collections.Generic; +using HandlebarsDotNet; +using Open.Threading; + +namespace Nitride.Handlebars +{ + /// + /// Implements a cache for templates to prevent compiling the same template + /// more than once. + /// + public class HandlebarsTemplateCache + { + private readonly IHandlebars handlebars; + + private readonly ModificationSynchronizer locker; + + private readonly Dictionary> + templates; + + public HandlebarsTemplateCache(IHandlebars handlebars) + { + this.handlebars = handlebars; + this.locker = new ModificationSynchronizer(); + this.templates = + new Dictionary>(); + } + + /// + /// Caches the template by name based on the contents of the disk. It + /// does it once in a multi-threaded manner to ensure it is only cached + /// once in memory. + /// + /// The string that contains the template. + /// + public HandlebarsTemplate GetLiteralTemplate( + string literal) + { + // Start with a read lock to see if we've already compiled it. + this.locker.Modifying( + () => !this.templates.ContainsKey(literal), + () => + { + HandlebarsTemplate template = + this.handlebars + !.Compile(literal); + + this.templates[literal] = template; + + return true; + }); + + return this.locker.Reading(() => this.templates[literal]); + } + + /// + /// Caches the template by name based on the contents of the disk. It + /// does it once in a multi-threaded manner to ensure it is only cached + /// once in memory. + /// + /// + /// + public HandlebarsTemplate GetNamedTemplate( + string templateName) + { + string template = $"{{{{> {templateName}}}}}"; + + return this.GetLiteralTemplate(template); + } + } +} diff --git a/src/Nitride.Handlebars/HasHandlebarsTemplate.cs b/src/Nitride.Handlebars/HasHandlebarsTemplate.cs new file mode 100644 index 0000000..acedb47 --- /dev/null +++ b/src/Nitride.Handlebars/HasHandlebarsTemplate.cs @@ -0,0 +1,11 @@ +namespace Nitride.Handlebars +{ + /// + /// A marker component that indicates that a given file with text component + /// has a Handlebars template in it. + /// + public class HasHandlebarsTemplate + { + public static HasHandlebarsTemplate Instance { get; } = new(); + } +} diff --git a/src/Nitride.Handlebars/IdentifyHandlebars.cs b/src/Nitride.Handlebars/IdentifyHandlebars.cs new file mode 100644 index 0000000..5a59d6a --- /dev/null +++ b/src/Nitride.Handlebars/IdentifyHandlebars.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using Gallium; +using Nitride.Contents; + +namespace Nitride.Handlebars +{ + /// + /// An operation that discovers which text files have a Handlebars template + /// inside them. + /// + public class IdentifyHandlebars : INitrideOperation + { + /// + public IEnumerable Run(IEnumerable input) + { + return input + .ForEachEntity(this.ScanContent); + } + + private Entity ScanContent(Entity entity, ITextContent content) + { + string text = content.GetText(); + + if (text.Contains("{{") && text.Contains("}}")) + { + return entity.Set(HasHandlebarsTemplate.Instance); + } + + return entity; + } + } +} diff --git a/src/Nitride.Handlebars/Nitride.Handlebars.csproj b/src/Nitride.Handlebars/Nitride.Handlebars.csproj new file mode 100644 index 0000000..7865823 --- /dev/null +++ b/src/Nitride.Handlebars/Nitride.Handlebars.csproj @@ -0,0 +1,36 @@ + + + + net5.0 + enable + + + + An extension to Nitride static site generator to style output with Handlebars. + + + + + + + + + + + + + + + + + True + + + + + Analyzer + False + + + + diff --git a/src/Nitride.Handlebars/NitrideHandlebarsBuilderExtensions.cs b/src/Nitride.Handlebars/NitrideHandlebarsBuilderExtensions.cs new file mode 100644 index 0000000..00b6764 --- /dev/null +++ b/src/Nitride.Handlebars/NitrideHandlebarsBuilderExtensions.cs @@ -0,0 +1,14 @@ +using Autofac; + +namespace Nitride.Handlebars +{ + public static class NitrideHandlebarsBuilderExtensions + { + public static NitrideBuilder UseHandlebars(this NitrideBuilder builder) + { + return builder + .ConfigureContainer( + x => x.RegisterModule()); + } + } +} diff --git a/src/Nitride.Handlebars/NitrideHandlebarsModule.cs b/src/Nitride.Handlebars/NitrideHandlebarsModule.cs new file mode 100644 index 0000000..9351949 --- /dev/null +++ b/src/Nitride.Handlebars/NitrideHandlebarsModule.cs @@ -0,0 +1,22 @@ +using Autofac; + +namespace Nitride.Handlebars +{ + public class NitrideHandlebarsModule : NitrideModuleBase + { + /// + protected override void Register(ContainerBuilder builder) + { + builder + .RegisterType() + .AsSelf() + .SingleInstance(); + builder + .RegisterGeneric(typeof(ApplyContentHandlebarsTemplate<>)) + .As(typeof(ApplyContentHandlebarsTemplate<>)); + builder + .RegisterGeneric(typeof(ApplyStyleHandlebarsTemplate<>)) + .As(typeof(ApplyStyleHandlebarsTemplate<>)); + } + } +} diff --git a/src/Nitride.Handlebars/README.md b/src/Nitride.Handlebars/README.md new file mode 100644 index 0000000..6bcd33f --- /dev/null +++ b/src/Nitride.Handlebars/README.md @@ -0,0 +1,26 @@ +Nitride.Handlebars +================== + +This is a collection +of [Handlebars](https://github.com/Handlebars-Net/Handlebars.Net) +operations that work with Nitride. There are two ways of using Handelbars in +this package. + +## IHandlebars + +This library does *not* configure or register `IHandlebars` but requires it to +be injected into the DI container (Autofac) along with any templates or inlines +loaded. + +## Styling Templates + +The first is to use it to apply a Handlebars theme to a page or text content. +This would include adding links to the CSS, any javascript, generating menus, +and common navigation elements. + +## Content Templates + +The second is used to apply it to the Handlebars inside the text content. This +is the page-specific content that needs to be resolved. A good example of this +might be creating a "last five posts" page or embedding some metadata from the +YAML header into the page. diff --git a/src/Nitride.Html/HtmlEntitiesToUnicode.cs b/src/Nitride.Html/HtmlEntitiesToUnicode.cs new file mode 100644 index 0000000..a60cd2c --- /dev/null +++ b/src/Nitride.Html/HtmlEntitiesToUnicode.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using System.Net; +using Gallium; +using Nitride.Contents; + +namespace Nitride.Html +{ + /// + /// Converts the text input that uses HTML entities and turns them into + /// Unicode variations. + /// + public class HtmlEntitiesToUnicode : NitrideOperationBase + { + /// + public override IEnumerable Run(IEnumerable input) + { + return input + .ForEachEntity(this.ResolveHtmlEntities); + } + + private Entity ResolveHtmlEntities( + Entity entity, + ITextContent content) + { + string text = content.GetText(); + string resolved = WebUtility.HtmlDecode(text); + + return entity.SetTextContent(resolved); + } + } +} diff --git a/src/Nitride.Html/IsHtml.cs b/src/Nitride.Html/IsHtml.cs new file mode 100644 index 0000000..8e687ec --- /dev/null +++ b/src/Nitride.Html/IsHtml.cs @@ -0,0 +1,10 @@ +namespace Nitride.Html +{ + /// + /// A marker component that indicates that the entity is an HTML file. + /// + public class IsHtml + { + public static IsHtml Instance { get; } = new(); + } +} diff --git a/src/Nitride.Html/Nitride.Html.csproj b/src/Nitride.Html/Nitride.Html.csproj new file mode 100644 index 0000000..7aa6d8e --- /dev/null +++ b/src/Nitride.Html/Nitride.Html.csproj @@ -0,0 +1,31 @@ + + + + net5.0 + + + + An extension to Nitride static site generator to generate HTML output. + + + + + + + + + True + + + + + Analyzer + False + + + + + + + + diff --git a/src/Nitride.Html/NitrideHtmlBuilderExtensions.cs b/src/Nitride.Html/NitrideHtmlBuilderExtensions.cs new file mode 100644 index 0000000..77ae44b --- /dev/null +++ b/src/Nitride.Html/NitrideHtmlBuilderExtensions.cs @@ -0,0 +1,14 @@ +using Autofac; + +namespace Nitride.Html +{ + public static class NitrideHtmlBuilderExtensions + { + public static NitrideBuilder UseHtml(this NitrideBuilder builder) + { + return builder + .ConfigureContainer( + x => x.RegisterModule()); + } + } +} diff --git a/src/Nitride.Html/NitrideHtmlModule.cs b/src/Nitride.Html/NitrideHtmlModule.cs new file mode 100644 index 0000000..0534448 --- /dev/null +++ b/src/Nitride.Html/NitrideHtmlModule.cs @@ -0,0 +1,6 @@ +namespace Nitride.Html +{ + public class NitrideHtmlModule : NitrideModuleBase + { + } +} diff --git a/src/Nitride.IO.Tests/AddPathPrefixTests.cs b/src/Nitride.IO.Tests/AddPathPrefixTests.cs new file mode 100644 index 0000000..4f6b36c --- /dev/null +++ b/src/Nitride.IO.Tests/AddPathPrefixTests.cs @@ -0,0 +1,48 @@ +using System.Linq; +using Autofac; +using Nitride.IO.Contents; +using Nitride.IO.Paths; +using Xunit; +using Xunit.Abstractions; +using Zio; +using Zio.FileSystems; + +namespace Nitride.IO.Tests +{ + public class AddPathPrefixTests : NitrideIOTestsBase + { + private readonly MemoryFileSystem fileSystem; + + public AddPathPrefixTests(ITestOutputHelper output) + : base(output) + { + this.fileSystem = new MemoryFileSystem(); + this.fileSystem.CreateFile("/b1.txt"); + this.fileSystem.CreateFile("/c1.md"); + } + + [Fact] + public void PrefixAllFiles() + { + // Set up the operation. + var readFiles = this.Container.Resolve(); + var op = new AddPathPrefix("/prefix"); + + // Read and replace the paths. + IOrderedEnumerable output = readFiles(this.fileSystem) + .Read() + .Run(op) + .Select(x => x.Get().ToString()) + .OrderBy(x => x); + + // Verify the results. + Assert.Equal( + new[] + { + "/prefix/b1.txt", + "/prefix/c1.md", + }, + output); + } + } +} diff --git a/src/Nitride.IO.Tests/MoveToIndexPathsTests.cs b/src/Nitride.IO.Tests/MoveToIndexPathsTests.cs new file mode 100644 index 0000000..77e8831 --- /dev/null +++ b/src/Nitride.IO.Tests/MoveToIndexPathsTests.cs @@ -0,0 +1,80 @@ +using System.Linq; +using Autofac; +using Nitride.IO.Contents; +using Nitride.IO.Paths; +using Xunit; +using Xunit.Abstractions; +using Zio; +using Zio.FileSystems; + +namespace Nitride.IO.Tests +{ + public class MoveToIndexPathsTests : NitrideIOTestsBase + { + private readonly MemoryFileSystem fileSystem; + + public MoveToIndexPathsTests(ITestOutputHelper output) + : base(output) + { + this.fileSystem = new MemoryFileSystem(); + this.fileSystem.CreateDirectory("/c1"); + this.fileSystem.CreateFile("/a1"); + this.fileSystem.CreateFile("/b1.txt"); + this.fileSystem.CreateFile("/c1/index.md"); + this.fileSystem.CreateFile("/d1.html"); + } + + [Fact] + public void MoveAllFiles() + { + // Set up the operation. + var readFiles = this.Container.Resolve(); + var op = new MoveToIndexPaths(); + + // Read and replace the paths. + IOrderedEnumerable output = readFiles(this.fileSystem) + .Read() + .Run(op) + .Select(x => x.Get().ToString()) + .OrderBy(x => x); + + // Verify the results. + Assert.Equal( + new[] + { + "/a1", + "/b1.txt", + "/c1/index.md", + "/d1/index.html", + }, + output); + } + + [Fact] + public void OverrideCanMoveCallback() + { + // Set up the operation. + var readFiles = this.Container.Resolve(); + MoveToIndexPaths? op = new MoveToIndexPaths() + .WithCanMoveCallback((path) => path.ToString().Contains("a1")); + + // Read and replace the paths. + IOrderedEnumerable output = readFiles(this.fileSystem) + .Read() + .Run(op) + .Select(x => x.Get().ToString()) + .OrderBy(x => x); + + // Verify the results. + Assert.Equal( + new[] + { + "/a1/index", + "/b1.txt", + "/c1/index.md", + "/d1.html", + }, + output); + } + } +} diff --git a/src/Nitride.IO.Tests/Nitride.IO.Tests.csproj b/src/Nitride.IO.Tests/Nitride.IO.Tests.csproj new file mode 100644 index 0000000..a04bedc --- /dev/null +++ b/src/Nitride.IO.Tests/Nitride.IO.Tests.csproj @@ -0,0 +1,29 @@ + + + + net5.0 + enable + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + diff --git a/src/Nitride.IO.Tests/NitrideIOTestsBase.cs b/src/Nitride.IO.Tests/NitrideIOTestsBase.cs new file mode 100644 index 0000000..e93ccfd --- /dev/null +++ b/src/Nitride.IO.Tests/NitrideIOTestsBase.cs @@ -0,0 +1,21 @@ +using Autofac; +using Nitride.Tests; +using Xunit.Abstractions; + +namespace Nitride.IO.Tests +{ + public abstract class NitrideIOTestsBase : NitrideTestsBase + { + protected NitrideIOTestsBase(ITestOutputHelper output) + : base(output) + { + } + + /// + protected override void ConfigureContainer(ContainerBuilder builder) + { + base.ConfigureContainer(builder); + builder.RegisterModule(); + } + } +} diff --git a/src/Nitride.IO.Tests/ReadFilesTests.cs b/src/Nitride.IO.Tests/ReadFilesTests.cs new file mode 100644 index 0000000..1e84103 --- /dev/null +++ b/src/Nitride.IO.Tests/ReadFilesTests.cs @@ -0,0 +1,97 @@ +using System.Linq; +using Autofac; +using Nitride.IO.Contents; +using Xunit; +using Xunit.Abstractions; +using Zio; +using Zio.FileSystems; + +namespace Nitride.IO.Tests +{ + /// + /// Tests the functionality of the ReadFiles(). + /// + public class ReadFilesTests : NitrideIOTestsBase + { + private readonly MemoryFileSystem fileSystem; + + public ReadFilesTests(ITestOutputHelper output) + : base(output) + { + this.fileSystem = new MemoryFileSystem(); + this.fileSystem.CreateDirectory("/b1"); + this.fileSystem.CreateDirectory("/c1"); + this.fileSystem.CreateDirectory("/c1/c2"); + this.fileSystem.CreateDirectory("/d1"); + this.fileSystem.WriteAllText("/a.txt", "File A"); + this.fileSystem.WriteAllText("/b1/b.md", "File B"); + this.fileSystem.WriteAllText("/c1/c.txt", "File C"); + this.fileSystem.WriteAllText("/c1/c2/e.md", "File C"); + } + + [Fact] + public void ReadAllFiles() + { + // Set up the operation. + var factory = this.Container.Resolve(); + ReadFiles op = factory(this.fileSystem); + + // Verify the paths. + IOrderedEnumerable paths = op.Read() + .Select(x => x.Get()) + .Select(x => (string)x) + .OrderBy(x => x); + + Assert.Equal( + new[] + { + "/a.txt", + "/b1/b.md", + "/c1/c.txt", + "/c1/c2/e.md", + }, + paths); + } + + [Fact] + public void ReadGlob() + { + // Set up the operation. + var factory = this.Container.Resolve(); + ReadFiles op = factory(this.fileSystem); + + // Verify the paths. + IOrderedEnumerable paths = op.Read("/*.txt") + .Select(x => (string)x.Get()) + .OrderBy(x => x); + + Assert.Equal( + new[] + { + "/a.txt", + }, + paths); + } + + [Fact] + public void ReadGlobWithSubdirectories() + { + // Set up the operation. + var factory = this.Container.Resolve(); + ReadFiles op = factory(this.fileSystem); + + // Verify the paths. + IOrderedEnumerable paths = op.Read("**/*.txt") + .Select(x => (string)x.Get()) + .OrderBy(x => x); + + Assert.Equal( + new[] + { + "/a.txt", + "/c1/c.txt", + }, + paths); + } + } +} diff --git a/src/Nitride.IO.Tests/RemovePathPrefixTests.cs b/src/Nitride.IO.Tests/RemovePathPrefixTests.cs new file mode 100644 index 0000000..99270bc --- /dev/null +++ b/src/Nitride.IO.Tests/RemovePathPrefixTests.cs @@ -0,0 +1,50 @@ +using System.Linq; +using Autofac; +using Nitride.IO.Contents; +using Nitride.IO.Paths; +using Xunit; +using Xunit.Abstractions; +using Zio; +using Zio.FileSystems; + +namespace Nitride.IO.Tests +{ + public class RemovePathPrefixTests : NitrideIOTestsBase + { + private readonly MemoryFileSystem fileSystem; + + public RemovePathPrefixTests(ITestOutputHelper output) + : base(output) + { + this.fileSystem = new MemoryFileSystem(); + this.fileSystem.CreateDirectory("/a"); + this.fileSystem.CreateDirectory("/a/a"); + this.fileSystem.CreateFile("/a/b1.txt"); + this.fileSystem.CreateFile("/a/a/c1.md"); + } + + [Fact] + public void PrefixAllFiles() + { + // Set up the operation. + var readFiles = this.Container.Resolve(); + var op = new RemovePathPrefix("/a"); + + // Read and replace the paths. + IOrderedEnumerable output = readFiles(this.fileSystem) + .Read() + .Run(op) + .Select(x => x.Get().ToString()) + .OrderBy(x => x); + + // Verify the results. + Assert.Equal( + new[] + { + "/a/c1.md", + "/b1.txt", + }, + output); + } + } +} diff --git a/src/Nitride.IO.Tests/WriteFilesTest.cs b/src/Nitride.IO.Tests/WriteFilesTest.cs new file mode 100644 index 0000000..5c9aacf --- /dev/null +++ b/src/Nitride.IO.Tests/WriteFilesTest.cs @@ -0,0 +1,63 @@ +using System.Linq; +using Autofac; +using Nitride.Contents; +using Nitride.IO.Contents; +using Xunit; +using Xunit.Abstractions; +using Zio; +using Zio.FileSystems; + +namespace Nitride.IO.Tests +{ + /// + /// Tests the functionality of the WriteFiles(). + /// + public class WriteFilesTest : NitrideIOTestsBase + { + private readonly MemoryFileSystem fileSystem; + + public WriteFilesTest(ITestOutputHelper output) + : base(output) + { + this.fileSystem = new MemoryFileSystem(); + this.fileSystem.CreateDirectory("/b1"); + this.fileSystem.CreateDirectory("/c1"); + this.fileSystem.CreateDirectory("/c1/c2"); + this.fileSystem.CreateDirectory("/d1"); + this.fileSystem.WriteAllText("/a.txt", "File A"); + this.fileSystem.WriteAllText("/b1/b.md", "File B"); + this.fileSystem.WriteAllText("/c1/c2/e.md", "File E"); + } + + [Fact] + public void WriteAllFiles() + { + // Set up the operation. + var output = new MemoryFileSystem(); + var readFiles = this.Container.Resolve(); + var factory = this.Container.Resolve(); + WriteFiles op = factory(output); + + // Read and write out the files. We switch one of the files to be + // text content to make sure that works too. + readFiles(this.fileSystem) + .Read() + .Select( + x => x.Get() == "/b1/b.md" + ? x.SetTextContent( + ((ITextContentConvertable)x.GetBinaryContent()) + .ToTextContent()) + : x) + .Run(op); + + // Verify the results. + Assert.True(output.FileExists("/a.txt")); + Assert.True(output.FileExists("/b1/b.md")); + Assert.True(output.FileExists("/c1/c2/e.md")); + + Assert.Equal("File A", output.ReadAllText("/a.txt")); + Assert.Equal("File B", output.ReadAllText("/b1/b.md")); + Assert.Equal("File E", output.ReadAllText("/c1/c2/e.md")); + } + } +} diff --git a/src/Nitride.IO/Contents/FileEntryBinaryContent.cs b/src/Nitride.IO/Contents/FileEntryBinaryContent.cs new file mode 100644 index 0000000..aaf16e7 --- /dev/null +++ b/src/Nitride.IO/Contents/FileEntryBinaryContent.cs @@ -0,0 +1,35 @@ +using System.IO; +using Nitride.Contents; +using Zio; + +namespace Nitride.IO.Contents +{ + /// + /// Contains a wrapper around a file entry to retrieve the binary data. + /// + public class FileEntryBinaryContent + : IBinaryContent, ITextContentConvertable + { + private readonly FileEntry entry; + + public FileEntryBinaryContent(FileEntry entry) + { + this.entry = entry; + } + + /// + public Stream GetStream() + { + return this.entry.Open( + FileMode.Open, + FileAccess.Read, + FileShare.Read); + } + + /// + public ITextContent ToTextContent() + { + return new FileEntryTextContent(this.entry); + } + } +} diff --git a/src/Nitride.IO/Contents/FileEntryTextContent.cs b/src/Nitride.IO/Contents/FileEntryTextContent.cs new file mode 100644 index 0000000..1e6a4d1 --- /dev/null +++ b/src/Nitride.IO/Contents/FileEntryTextContent.cs @@ -0,0 +1,38 @@ +using System.IO; +using System.Text; +using Nitride.Contents; +using Zio; + +namespace Nitride.IO.Contents +{ + /// + /// Contains a wrapper around a file entry to retrieve text data. + /// + public class FileEntryTextContent + : ITextContent, IBinaryContentConvertable + { + private readonly FileEntry entry; + + public FileEntryTextContent(FileEntry entry) + { + this.entry = entry; + } + + /// + public TextReader GetReader() + { + return new StreamReader( + this.entry.Open( + FileMode.Open, + FileAccess.Read, + FileShare.Read), + Encoding.UTF8); + } + + /// + public IBinaryContent ToBinaryContent() + { + return new FileEntryBinaryContent(this.entry); + } + } +} diff --git a/src/Nitride.IO/Contents/ReadFiles.cs b/src/Nitride.IO/Contents/ReadFiles.cs new file mode 100644 index 0000000..dd09433 --- /dev/null +++ b/src/Nitride.IO/Contents/ReadFiles.cs @@ -0,0 +1,89 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using DotNet.Globbing; +using Gallium; +using Nitride.Contents; +using Zio; + +namespace Nitride.IO.Contents +{ + /// + /// A module that reads files from the file system and wraps them in an + /// entity with the following components: UPath, IContent. + /// + public class ReadFiles : FileSystemOperation + { + public ReadFiles(IFileSystem fileSystem) + : base(fileSystem) + { + } + + /// + /// Primary method for creating a read file. + /// + public delegate ReadFiles Factory(IFileSystem fileSystem); + + /// + /// Reads all files from the file system and returns them. + /// + /// A populated collection of entities. + public IEnumerable Read( + UPath path = new(), + string searchPattern = "*", + SearchOption search = SearchOption.AllDirectories) + { + // Normalize the path. + path = path == new UPath() ? "/" : path; + + // Search for the file and wrap the results. + IEnumerable files = this.FileSystem + .EnumerateFileEntries(path, searchPattern, search); + IEnumerable entities = files.Select(this.ToEntity); + + return entities; + } + + /// + /// Reads all files from the file system and returns them. + /// + /// A populated collection of entities. + public IEnumerable Read(string glob) + { + Glob parsed = Glob.Parse(glob); + + return this.Read(parsed); + } + + /// + /// Reads all files from the file system, filtering them out by the + /// minimatch pattern (as defined by DotNet.Blob). + /// + /// A populated collection of entities. + public IEnumerable Read(Glob glob) + { + IEnumerable files = this.FileSystem + .EnumerateFileEntries("/", "*.*", SearchOption.AllDirectories) + .Where(x => glob.IsMatch(x.Path.ToString())); + IEnumerable entities = files.Select(this.ToEntity); + + return entities; + } + + /// + /// Creates an entity with the standard components for all Zio-based + /// files. This attaches the file's path relative to the file system + /// and a way of accessing the content from the file system. + /// + /// The Zio file entry. + /// An Entity with appropriate content. + private Entity ToEntity(FileEntry file) + { + Entity entity = new Entity() + .Set(file.Path) + .SetBinaryContent(new FileEntryBinaryContent(file)); + + return entity; + } + } +} diff --git a/src/Nitride.IO/Contents/WriteFiles.cs b/src/Nitride.IO/Contents/WriteFiles.cs new file mode 100644 index 0000000..5e49160 --- /dev/null +++ b/src/Nitride.IO/Contents/WriteFiles.cs @@ -0,0 +1,220 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using Gallium; +using Nitride.Contents; +using Serilog; +using Zio; + +namespace Nitride.IO.Contents +{ + /// + /// An operation that writes out entities to a file system. + /// + [WithProperties] + public partial class WriteFiles : FileSystemOperation, INitrideOperation + { + private readonly ILogger logger; + + private Dictionary> factories; + + public WriteFiles( + IFileSystem fileSystem, + ILogger logger) + : base(fileSystem) + { + this.logger = logger.ForContext(); + this.factories = new Dictionary> + { + [typeof(IBinaryContent)] = GetBinaryStream, + [typeof(ITextContent)] = this.GetTextStream, + }; + } + + /// + /// Primary method for creating a write files operation. + /// + public delegate WriteFiles Factory(IFileSystem fileSystem); + + public Dictionary> StreamFactories + { + get => this.factories; + set => this.factories = + value ?? throw new ArgumentNullException(nameof(value)); + } + + /// + /// Gets or sets the encoding to force any text output. + /// + public Encoding? TextEncoding { get; set; } + + /// + /// Writes out all the files to the given file system using the paths + /// currently stored in the `UPath` component. Only files that have + /// a path and a registered writer will be written. + /// + /// The entities to parse. + /// The same list of entities without changes. + public IEnumerable Run(IEnumerable entities) + { + // We need the `ToList()` here, otherwise it doesn't work. + IEnumerable results = entities + .ForEachEntity(this.Process) + .ToList(); + + return results; + } + + private static Stream GetBinaryStream(IContent content) + { + return ((IBinaryContent)content).GetStream(); + } + + private Stream GetTextStream(IContent content) + { + // See if we can convert the stream first. If that is the case, then + // we don't have to load it entirely in memory. + if (content is IBinaryContentConvertable convertable) + { + return convertable.ToBinaryContent().GetStream(); + } + + // We have the load the text into memory and convert it. + var textContent = (ITextContent)content; + string text = textContent.GetReader().ReadToEnd(); + var stream = new MemoryStream(); + var writer = new StreamWriter( + stream, + this.TextEncoding ?? Encoding.UTF8); + + writer.Write(text); + writer.Flush(); + stream.Position = 0; + + return stream; + } + + /// + /// Internal method for writing out the entity. This handles the + /// registered writers to allow for multiple `IContent` types being + /// written out automatically. + /// + /// The entity to write out. + /// The path of the entity. + /// The entity passed in. + private Entity Process(Entity entity, UPath path) + { + // See if we have any content. If we don't, then there is nothing + // to do. + if (!entity.HasContent()) + { + return entity; + } + + // First see if we have a factory for the exact type of content. + IContent content = entity.GetContent(); + + if (this.factories.TryGetValue( + content.GetType(), + out var getStream)) + { + Stream stream = getStream(content); + + return this.Process(entity, path, stream); + } + + // If we have an easy conversion, then use that so we don't have to + // walk up the tree looking for one we do have. + if (content is IBinaryContentConvertable binaryConvertable + && this.factories.TryGetValue( + typeof(IBinaryContent), + out var binaryContent)) + { + Stream stream = + binaryContent(binaryConvertable.ToBinaryContent()); + + return this.Process(entity, path, stream); + } + + if (content is ITextContentConvertable textConvertable + && this.factories.TryGetValue( + typeof(ITextContent), + out var textContent)) + { + Stream stream = textContent(textConvertable.ToTextContent()); + + return this.Process(entity, path, stream); + } + + // For everything else, we have to find a content that we have a + // registered type for by walking up the inheritance tree and + // finding the right type. + List types = new() { content.GetType() }; + + while (types.Count > 0) + { + // Check to see if we have any of these types. + Func? found = types + .Select( + x => this.factories.TryGetValue(x, out var factory) + ? factory + : null) + .FirstOrDefault(x => x != null); + + if (found != null) + { + Stream stream = found(content); + + return this.Process(entity, path, stream); + } + + // We didn't find one, so add all the parent types and try + // again with the new list. + types = types + .SelectMany( + x => new[] { x.BaseType } + .Union(x.GetInterfaces())) + .Where(x => x != null) + .Select(x => x!) + .ToList(); + } + + // If we got this far, we never found a content to handle. + throw new InvalidOperationException( + "Cannot write out entity " + + path + + " because cannot determine how to get a stream out content type " + + content.GetType().FullName + + ". To resolve, register a function to this.StreamFactories."); + } + + /// + /// Writes out a stream to the given path in the file system. + /// + /// The entity being written out. + /// The path to write out, directories will be created. + /// The stream to write out. + /// The entity passed in. + private Entity Process(Entity entity, UPath path, Stream stream) + { + // Make sure we have the directory structure. + UPath directory = path.GetDirectory(); + + if (directory != "/") + { + this.FileSystem.CreateDirectory(directory); + } + + // Write out the file. + using Stream fileStream = this.FileSystem.CreateFile(path); + + stream.CopyTo(fileStream); + stream.Close(); + + // Return the entity because we've written out the files. + return entity; + } + } +} diff --git a/src/Nitride.IO/Directories/ClearDirectory.cs b/src/Nitride.IO/Directories/ClearDirectory.cs new file mode 100644 index 0000000..fe66530 --- /dev/null +++ b/src/Nitride.IO/Directories/ClearDirectory.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using Gallium; +using Serilog; +using Zio; + +namespace Nitride.IO.Directories +{ + /// + /// A Nitride operation that removes the contents of a directory but not + /// the directory itself. This is used because some tools don't handle + /// when the root directory is removed. + /// This will create the top-level directory if it doesn't exist. + /// + [WithProperties] + public partial class ClearDirectory : FileSystemOperation, INitrideOperation + { + private readonly ILogger logger; + + public ClearDirectory( + ILogger logger, + IFileSystem fileSystem) + : base(fileSystem) + { + this.logger = logger.ForContext(); + } + + /// + /// Gets or sets the path of the directory to clear. + /// + public UPath? Path { get; set; } + + public IEnumerable Run() + { + return this.Run(new List()); + } + + /// + public IEnumerable Run(IEnumerable input) + { + // This really isn't an input-type of operation, but it can fit + // inside one to keep a pattern. + if (!this.Path.HasValue) + { + throw new InvalidOperationException( + nameof(ClearDirectory) + + "cannot be used without setting the path either by the" + + "factory method, the constructor, the property, or " + + "SetPath method."); + } + + // See if the directory exists. If it doesn't, then we make it. + UPath path = this.Path.Value; + + if (!this.FileSystem.DirectoryExists(path)) + { + this.logger.Information( + "Creating the directory {Path}", + path); + this.FileSystem.CreateDirectory(path); + } + + // Clear out the contents. + IEnumerable files = this.FileSystem.EnumerateFiles(path); + IEnumerable directories = + this.FileSystem.EnumerateDirectories(path); + + foreach (UPath file in files) + { + this.FileSystem.DeleteFile(file); + } + + foreach (UPath directory in directories) + { + this.FileSystem.DeleteDirectory(directory, true); + } + + // Just pass the input on. + return input; + } + } +} diff --git a/src/Nitride.IO/FileSystemOperation.cs b/src/Nitride.IO/FileSystemOperation.cs new file mode 100644 index 0000000..5d9a4ba --- /dev/null +++ b/src/Nitride.IO/FileSystemOperation.cs @@ -0,0 +1,14 @@ +using Zio; + +namespace Nitride.IO +{ + public abstract class FileSystemOperation + { + protected FileSystemOperation(IFileSystem fileSystem) + { + this.FileSystem = fileSystem; + } + + protected IFileSystem FileSystem { get; } + } +} diff --git a/src/Nitride.IO/Nitride.IO.csproj b/src/Nitride.IO/Nitride.IO.csproj new file mode 100644 index 0000000..e12b0c2 --- /dev/null +++ b/src/Nitride.IO/Nitride.IO.csproj @@ -0,0 +1,37 @@ + + + + net5.0 + enable + Nitride.IO + + + + An extension to Nitride static site generator to read and write files. + + + + + + + + + + + + + + + + + True + + + + + Analyzer + False + + + + diff --git a/src/Nitride.IO/NitrideIOBuilderExtensions.cs b/src/Nitride.IO/NitrideIOBuilderExtensions.cs new file mode 100644 index 0000000..bd2310c --- /dev/null +++ b/src/Nitride.IO/NitrideIOBuilderExtensions.cs @@ -0,0 +1,14 @@ +using Autofac; + +namespace Nitride.IO +{ + public static class NitrideIOBuilderExtensions + { + public static NitrideBuilder UseIO(this NitrideBuilder builder) + { + return builder + .ConfigureContainer( + x => x.RegisterModule()); + } + } +} diff --git a/src/Nitride.IO/NitrideIOModule.cs b/src/Nitride.IO/NitrideIOModule.cs new file mode 100644 index 0000000..1c6123e --- /dev/null +++ b/src/Nitride.IO/NitrideIOModule.cs @@ -0,0 +1,6 @@ +namespace Nitride.IO +{ + public class NitrideIOModule : NitrideModuleBase + { + } +} diff --git a/src/Nitride.IO/Paths/AddPathPrefix.cs b/src/Nitride.IO/Paths/AddPathPrefix.cs new file mode 100644 index 0000000..e000486 --- /dev/null +++ b/src/Nitride.IO/Paths/AddPathPrefix.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using Gallium; +using Zio; + +namespace Nitride.IO.Paths +{ + [WithProperties] + public partial class AddPathPrefix : INitrideOperation + { + public AddPathPrefix() + { + } + + public AddPathPrefix(UPath pathPrefix) + { + this.PathPrefix = pathPrefix; + } + + /// + /// Gets or sets the prefix for the path operations. + /// + public UPath? PathPrefix { get; set; } + + public IEnumerable Run(IEnumerable input) + { + if (this.PathPrefix == null) + { + throw new InvalidOperationException( + nameof(AddPathPrefix) + + ".Prefix was not set " + + "(via constructor, property, or SetPrefix) before " + + "Run() was called."); + } + + ReplacePaths replacePaths = new ReplacePaths() + .WithReplacement(this.RunReplacement); + + return replacePaths.Run(input); + } + + private UPath RunReplacement(Entity _, UPath path) + { + string innerRelativePath = path.ToString().TrimStart('/'); + return this.PathPrefix!.Value / innerRelativePath; + } + } +} diff --git a/src/Nitride.IO/Paths/ChangePathExtension.cs b/src/Nitride.IO/Paths/ChangePathExtension.cs new file mode 100644 index 0000000..22df27c --- /dev/null +++ b/src/Nitride.IO/Paths/ChangePathExtension.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using Gallium; +using Zio; + +namespace Nitride.IO.Paths +{ + /// + /// Changes the extension of the paths given. + /// + [WithProperties] + public partial class ChangePathExtension : INitrideOperation + { + public ChangePathExtension() + { + } + + public ChangePathExtension(string extension) + { + this.Extension = extension; + } + + /// + /// Gets or sets the prefix for the path operations. + /// + public string? Extension { get; set; } + + public IEnumerable Run(IEnumerable input) + { + if (this.Extension == null) + { + throw new InvalidOperationException( + nameof(AddPathPrefix) + + ".Extension was not set " + + "(via constructor, property, or SetExtension) before " + + "Run() was called."); + } + + ReplacePaths replacePaths = new ReplacePaths() + .WithReplacement(this.RunReplacement); + + return replacePaths.Run(input); + } + + private UPath RunReplacement(Entity _, UPath path) + { + return path.ChangeExtension(this.Extension!); + } + } +} diff --git a/src/Nitride.IO/Paths/MoveToIndexPaths.cs b/src/Nitride.IO/Paths/MoveToIndexPaths.cs new file mode 100644 index 0000000..521b172 --- /dev/null +++ b/src/Nitride.IO/Paths/MoveToIndexPaths.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using Gallium; +using Zio; + +namespace Nitride.IO.Paths +{ + /// + /// Moves various files to indexes of a direction with the base filename. + /// + [WithProperties] + public partial class MoveToIndexPaths : INitrideOperation + { + public MoveToIndexPaths() + { + this.CanMoveCallback = DefaultCanMoveCallback; + } + + /// + /// Gets or sets the callback to determine if the file should be moved. + /// This will not be called if the file is already an index. + /// + public Func? CanMoveCallback { get; set; } + + /// + /// Default implement of the operation moves .html, .htm, .md, and + /// .markdown files into their indexes. + /// + /// + /// True if the file should move, otherwise false. + public static bool DefaultCanMoveCallback(UPath path) + { + return path.GetExtensionWithDot() switch + { + ".htm" => true, + ".html" => true, + ".md" => true, + ".markdown" => true, + _ => false, + }; + } + + public IEnumerable Run(IEnumerable input) + { + if (this.CanMoveCallback == null) + { + throw new InvalidOperationException( + nameof(MoveToIndexPaths) + + ".CanMoveCallback was not set " + + "(via constructor, property, or SetCanMoveCallback) before " + + "Run() was called."); + } + + ReplacePaths replacePaths = new ReplacePaths() + .WithReplacement(this.RunReplacement); + + return replacePaths.Run(input); + } + + private UPath RunReplacement(Entity _, UPath path) + { + // See if we are already an index. If that is true, then we don't + // have to move any further. + string? nameWithoutExtension = path.GetNameWithoutExtension(); + + if (nameWithoutExtension is null or "index") + { + return path; + } + + // See if the path should be moved. If it can't, then just stop + // processing. + if (!this.CanMoveCallback!.Invoke(path)) + { + return path; + } + + // Move the file to an index. + UPath parent = path.GetDirectory(); + string? extension = path.GetExtensionWithDot(); + string index = "index" + extension; + + return parent / nameWithoutExtension / index; + } + } +} diff --git a/src/Nitride.IO/Paths/RemovePathPrefix.cs b/src/Nitride.IO/Paths/RemovePathPrefix.cs new file mode 100644 index 0000000..461ef39 --- /dev/null +++ b/src/Nitride.IO/Paths/RemovePathPrefix.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using Gallium; +using Zio; + +namespace Nitride.IO.Paths +{ + /// + /// An operation that removes a path prefix from the input. + /// + [WithProperties] + public partial class RemovePathPrefix : INitrideOperation + { + public RemovePathPrefix() + { + } + + public RemovePathPrefix(UPath pathPrefix) + { + this.PathPrefix = pathPrefix; + } + + /// + /// Gets or sets the prefix for the path operations. + /// + public UPath? PathPrefix { get; set; } + + public IEnumerable Run(IEnumerable input) + { + if (this.PathPrefix == null) + { + throw new InvalidOperationException( + nameof(RemovePathPrefix) + + ".Prefix was not set " + + "(via constructor, property, or SetPrefix) before " + + "Replace() was called."); + } + + ReplacePaths replacePaths = new ReplacePaths() + .WithReplacement(this.RunReplacement); + + return replacePaths.Run(input); + } + + private UPath RunReplacement(Entity _, UPath path) + { + string normalized = path.ToString(); + string prefix = this.PathPrefix.ToString()!; + + if (normalized.StartsWith(prefix)) + { + return (UPath)path.ToString().Substring(prefix.Length); + } + + return path; + } + } +} diff --git a/src/Nitride.IO/Paths/ReplacePaths.cs b/src/Nitride.IO/Paths/ReplacePaths.cs new file mode 100644 index 0000000..52d9d83 --- /dev/null +++ b/src/Nitride.IO/Paths/ReplacePaths.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using Gallium; +using Zio; + +namespace Nitride.IO.Paths +{ + /// + /// A pipeline operation that replaces the UPath of the given entity + /// with the results of a lambda output. Entities without a path component + /// are passed on without touching. + /// + [WithProperties] + public partial class ReplacePaths : INitrideOperation + { + public ReplacePaths() + { + } + + public ReplacePaths(Func replacement) + { + this.Replacement = replacement; + } + + /// + /// Gets or sets the replacement callback to alter the paths. + /// + public Func? Replacement { get; set; } + + /// + /// Performs the replacement on the input streams and outputs the + /// resulting entities. Only entities that have had their paths changed + /// will be updated, the others will be passed on as-is. + /// + /// The list of input entities. + /// The output entities. + public IEnumerable Run(IEnumerable input) + { + if (this.Replacement == null) + { + throw new InvalidOperationException( + "ReplacePaths.Replacement was not set " + + "(via constructor, property, or SetReplacement) before " + + "Replace() was called."); + } + + return input + .ForEachEntity( + (entity, oldPath) => + { + UPath newPath = this.Replacement(entity, oldPath); + + return newPath != oldPath + ? entity.Set(newPath) + : entity; + }); + } + } +} diff --git a/src/Nitride.IO/Paths/UPathExtensions.cs b/src/Nitride.IO/Paths/UPathExtensions.cs new file mode 100644 index 0000000..eeafcba --- /dev/null +++ b/src/Nitride.IO/Paths/UPathExtensions.cs @@ -0,0 +1,41 @@ +using Zio; + +namespace Nitride.IO.Paths +{ + /// + /// Extension methods for the UPath class. + /// + public static class UPathExtensions + { + /// + /// Gets the directory path, which excludes the directory at the end + /// of the path. + /// + /// The path to manipulate. + /// A normalized path. + public static string GetDirectoryIndexPath(this UPath path) + { + if (path.GetNameWithoutExtension() == "index") + { + return path.GetDirectory().ToString().TrimEnd('/') + "/"; + } + + return path.ToString(); + } + + public static string GetParentDirectoryIndexPath(this UPath path) + { + UPath indexPath = path.GetDirectoryIndexPath(); + UPath parent = indexPath.GetDirectory(); + + if (parent == null!) + { + parent = "/"; + } + + string parentPath = parent.ToString().TrimEnd('/') + "/"; + + return parentPath; + } + } +} diff --git a/src/Nitride.IO/README.md b/src/Nitride.IO/README.md new file mode 100644 index 0000000..8835ec0 --- /dev/null +++ b/src/Nitride.IO/README.md @@ -0,0 +1,31 @@ +# Nitride.IO + +This assembly contains the primary system for reading and writing from the disk, +along with various processes to manipulate paths. It contains three primary +components: + +- File System I/O +- Path Normalization +- Disk-Based Content + +## File System I/O + +Internally, this assembly uses [Zio](https://github.com/xoofx/zio), an file +system abstraction that also has an in-memory implementation which is ideal for +running unit tests. Zio also provides a way of treating an arbitrary directory +as a root directory so all paths are relative to the "root". + +## Path Normalization + +Zio also provides `UPath`, a normalization class that handles relative and +absolute paths. Entities that are read from the disk, such as with `ReadFiles`, +will have a `UPath` component with the path from the file. This component is +also used to determine the path when writing out the results. + +## Disk-Based Content + +This assembly also extends the `Nitride.Contents.IBinaryContent` and +`Nitride.Contents.ITextContent` to have file system based implementations. These +keep track of the original path and file system regardless of changes made to +the `UPath` component. + diff --git a/src/Nitride.Javascript/InstallYarn.cs b/src/Nitride.Javascript/InstallYarn.cs new file mode 100644 index 0000000..5eb2f57 --- /dev/null +++ b/src/Nitride.Javascript/InstallYarn.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Gallium; +using Serilog; +using Zio; + +namespace Nitride.Javascript +{ + /// + /// Installs Yarn in a given directory if it is missing. If the + /// `node_modules` is already there, this step is skipped unless + /// ForceInstall is set to true. + /// + [WithProperties] + public partial class InstallYarn : INitrideOperation + { + private readonly IFileSystem fileSystem; + + private readonly ILogger logger; + + public InstallYarn(ILogger logger, IFileSystem fileSystem) + { + this.fileSystem = fileSystem; + this.logger = logger.ForContext(); + this.Directory = "/"; + } + + /// + /// Gets or sets the directory that the install should be run from. This + /// is typically the same file as the package.json and the yarn.lock. + /// + public UPath? Directory { get; set; } + + /// + /// Gets or sets a value indicating whether the install should be + /// forced. + /// + public bool ForceInstall { get; set; } + + /// + public IEnumerable Run(IEnumerable input) + { + // Make sure we have a sane value. + if (!this.Directory.HasValue) + { + throw new InvalidOperationException( + nameof(InstallYarn) + + ".Directory was not set before Run() was called."); + } + + // Make sure the directory exists in case we are the first. + UPath directory = this.Directory.Value; + + if (!this.fileSystem.DirectoryExists(directory)) + { + this.logger.Information("Creating directory {Path}", directory); + this.fileSystem.CreateDirectory(directory); + } + + // Check to see if the directory exists. + UPath modulesPath = directory / "node_modules"; + bool exists = this.fileSystem.DirectoryExists(modulesPath); + + if (exists) + { + if (!this.ForceInstall) + { + this.logger.Debug( + "{Path} already exists, skipping", + modulesPath); + return input; + } + + this.logger.Information( + "{Path} already exists, forcing installation", + modulesPath); + this.fileSystem.DeleteDirectory(modulesPath, true); + } + + // Run Yarn install on the directory. Sadly, Yarn doesn't understand + // a C# abstraction layer, so we need the "real" path to work with + // these operations. + string realPath = this.fileSystem + .ConvertPathToInternal(directory); + + // Run the install. + string command = YarnHelper.GetYarnCommand(); + var start = new ProcessStartInfo + { + FileName = command, + Arguments = "install", + WorkingDirectory = realPath, + RedirectStandardOutput = true, + }; + Process process = Process.Start(start); + + if (process == null) + { + throw new InvalidOperationException( + "Cannot start yarn install"); + } + + process.WaitForExit(); + + // We are done, so just pass our input on. + return input; + } + } +} diff --git a/src/Nitride.Javascript/Nitride.Javascript.csproj b/src/Nitride.Javascript/Nitride.Javascript.csproj new file mode 100644 index 0000000..fbbc107 --- /dev/null +++ b/src/Nitride.Javascript/Nitride.Javascript.csproj @@ -0,0 +1,33 @@ + + + + net5.0 + + + + An extension to Nitride static site generator to generate Webpack output. + + + + + + + + + + + + + + + True + + + + + Analyzer + False + + + + diff --git a/src/Nitride.Javascript/NitrideJavascriptBuilderExtensions.cs b/src/Nitride.Javascript/NitrideJavascriptBuilderExtensions.cs new file mode 100644 index 0000000..f1f37a9 --- /dev/null +++ b/src/Nitride.Javascript/NitrideJavascriptBuilderExtensions.cs @@ -0,0 +1,14 @@ +using Autofac; + +namespace Nitride.Javascript +{ + public static class NitrideJavascriptBuilderExtensions + { + public static NitrideBuilder UseJavascript(this NitrideBuilder builder) + { + return builder + .ConfigureContainer( + x => x.RegisterModule()); + } + } +} diff --git a/src/Nitride.Javascript/NitrideJavascriptModule.cs b/src/Nitride.Javascript/NitrideJavascriptModule.cs new file mode 100644 index 0000000..933a677 --- /dev/null +++ b/src/Nitride.Javascript/NitrideJavascriptModule.cs @@ -0,0 +1,6 @@ +namespace Nitride.Javascript +{ + public class NitrideJavascriptModule : NitrideModuleBase + { + } +} diff --git a/src/Nitride.Javascript/RunWebpack.cs b/src/Nitride.Javascript/RunWebpack.cs new file mode 100644 index 0000000..fc5c9be --- /dev/null +++ b/src/Nitride.Javascript/RunWebpack.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using Gallium; +using Nitride.IO.Contents; +using Nitride.IO.Paths; +using Serilog; +using Zio; + +namespace Nitride.Javascript +{ + /// + /// Runs `webpack` in the directory, writing out whatever files need to be + /// written. + /// + [WithProperties] + public partial class RunWebpack : INitrideOperation + { + private readonly IFileSystem fileSystem; + + private readonly ILogger logger; + + private readonly ReadFiles readFiles; + + public RunWebpack( + ILogger logger, + IFileSystem fileSystem, + ReadFiles readFiles) + { + this.fileSystem = fileSystem; + this.readFiles = readFiles; + this.logger = logger.ForContext(); + this.WebpackDirectory = "/"; + } + + /// + /// Gets or sets the directory that contains the output from the + /// webpack execution. + /// + public UPath? OutputDirectory { get; set; } + + /// + /// Gets or sets the directory that the install should be run from. This + /// is typically the same file as the webpack.config.js. + /// + public UPath? WebpackDirectory { get; set; } + + /// + public IEnumerable Run(IEnumerable input) + { + // Make sure we have a sane values. + if (!this.WebpackDirectory.HasValue) + { + throw new InvalidOperationException( + nameof(InstallYarn) + + ".WebpackDirectory was not set before Run() was called."); + } + + if (!this.OutputDirectory.HasValue) + { + throw new InvalidOperationException( + nameof(InstallYarn) + + ".OutputDirectory was not set before Run() was called."); + } + + // Make sure the directory exists in case we are the first. + UPath directory = this.WebpackDirectory.Value; + + if (!this.fileSystem.DirectoryExists(directory)) + { + this.logger.Information("Creating directory {Path}", directory); + this.fileSystem.CreateDirectory(directory); + } + + // Sadly, Yarn doesn't understand virtual file systems, so we need + // the "real" path to the directory for this. + string realPath = this.fileSystem + .ConvertPathToInternal(directory); + + // Run the install. + this.logger.Debug("Running Webpack via Yarn"); + + string command = YarnHelper.GetYarnCommand(); + var start = new ProcessStartInfo + { + FileName = command, + Arguments = "run webpack", + WorkingDirectory = realPath, + RedirectStandardOutput = true, + }; + Process process = Process.Start(start); + + if (process == null) + { + throw new InvalidOperationException( + "Cannot start yarn run webpack"); + } + + process.WaitForExit(); + + // We are done and all the output will be written into the + // filesystem, so merge it with our input. + this.logger.Debug("Adding Webpack output to the pipeline"); + + UPath outputDirectory = this.OutputDirectory.Value; + + return input + .Union( + this.readFiles + .Read(outputDirectory) + .Run(new RemovePathPrefix(outputDirectory))); + } + } +} diff --git a/src/Nitride.Javascript/YarnHelper.cs b/src/Nitride.Javascript/YarnHelper.cs new file mode 100644 index 0000000..09a3883 --- /dev/null +++ b/src/Nitride.Javascript/YarnHelper.cs @@ -0,0 +1,34 @@ +using System; +using System.IO; + +namespace Nitride.Javascript +{ + /// + /// A helper class for finding how to run or install Yarn. + /// + public static class YarnHelper + { + /// + /// Gets the name of the command to run Yarn. + /// + /// The command to run. + public static string GetYarnCommand() + { + // Figure out how to run Yarn. This is needed because Windows can't + // run the script like the shell can. + string command = "yarn"; + string appData = Path.Combine( + Environment.GetFolderPath( + Environment.SpecialFolder.ApplicationData), + "npm", + "yarn.cmd"); + + if (File.Exists(appData)) + { + command = appData; + } + + return command; + } + } +} diff --git a/src/Nitride.Markdown/IdentifyMarkdown.cs b/src/Nitride.Markdown/IdentifyMarkdown.cs new file mode 100644 index 0000000..06150c6 --- /dev/null +++ b/src/Nitride.Markdown/IdentifyMarkdown.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using Gallium; +using Nitride.Contents; +using Zio; + +namespace Nitride.Markdown +{ + /// + /// An operation that identifies Markdown files by their common extensions + /// and converts them to text input while also adding the IsMarkdown + /// component to identify them. + /// + [WithProperties] + public partial class IdentifyMarkdown : INitrideOperation + { + public IdentifyMarkdown() + { + this.IsMarkdownTest = DefaultIsMarkdown; + } + + public Func IsMarkdownTest { get; set; } + + /// + public IEnumerable Run(IEnumerable input) + { + return input + .ForEachEntity( + (entity, path, _) => + { + // If we aren't a Markdown file, then there is nothing + // we can do about that. + if (!this.IsMarkdownTest(entity, path)) + { + return entity; + } + + // We are already text, so just mark it as Markdown. + entity = entity + .Set(IsMarkdown.Instance); + + return entity; + }) + .ForEachEntity( + (entity, path, binary) => + { + // If we aren't a Markdown file, then there is nothing + // we can do about that. + if (!this.IsMarkdownTest(entity, path)) + { + return entity; + } + + // Convert the file as a binary. + if (binary is ITextContentConvertable textConvertable) + { + entity = entity + .SetTextContent(textConvertable.ToTextContent()) + .Set(IsMarkdown.Instance); + } + else + { + throw new InvalidOperationException( + "Cannot convert a binary content to a " + + "text without ITextContentConvertable."); + } + + return entity; + }); + } + + private static bool DefaultIsMarkdown(Entity entity, UPath path) + { + return (path.GetExtensionWithDot() ?? string.Empty) + .ToLowerInvariant() switch + { + ".md" => true, + ".markdown" => true, + _ => false, + }; + } + } +} diff --git a/src/Nitride.Markdown/IsMarkdown.cs b/src/Nitride.Markdown/IsMarkdown.cs new file mode 100644 index 0000000..f714e2d --- /dev/null +++ b/src/Nitride.Markdown/IsMarkdown.cs @@ -0,0 +1,10 @@ +namespace Nitride.Markdown +{ + /// + /// A marker class that indicates that the file is a Markdown file. + /// + public class IsMarkdown + { + public static IsMarkdown Instance { get; } = new(); + } +} diff --git a/src/Nitride.Markdown/MarkdownOperationBase.cs b/src/Nitride.Markdown/MarkdownOperationBase.cs new file mode 100644 index 0000000..b28319e --- /dev/null +++ b/src/Nitride.Markdown/MarkdownOperationBase.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using Gallium; +using Markdig; +using Nitride.Contents; + +namespace Nitride.Markdown +{ + public abstract class MarkdownOperationBase : INitrideOperation + { + /// + /// Gets or sets an additional callback to configure additional features + /// from the baseline Markdown. + /// + public Action? ConfigureMarkdown { get; set; } + + /// + public IEnumerable Run(IEnumerable input) + { + // Create the Markdown pipeline used for formatting. + var builder = new MarkdownPipelineBuilder(); + + this.ConfigureMarkdown?.Invoke(builder); + + MarkdownPipeline options = builder.Build(); + + // Process the Markdown files (while passing everything on). + return input + .ForEachEntity( + (entity, _, content) => this.Convert( + entity, + content, + options)); + } + + /// + /// Converts the Markdown file into HTML. + /// + /// The entity to convert. + /// The content for this entity. + /// The markdown pipeline. + /// A converted entity. + protected abstract Entity Convert( + Entity entity, + ITextContent markdownContent, + MarkdownPipeline options); + } +} diff --git a/src/Nitride.Markdown/MarkdownToGemtext.cs b/src/Nitride.Markdown/MarkdownToGemtext.cs new file mode 100644 index 0000000..1476282 --- /dev/null +++ b/src/Nitride.Markdown/MarkdownToGemtext.cs @@ -0,0 +1,40 @@ +using Gallium; +using Markdig; +using MfGames.Markdown.Gemtext; +using Nitride.Contents; +using Nitride.Gemtext; + +namespace Nitride.Markdown +{ + /// + /// Converts the input Markdown files into Gemtext using Markdig and + /// MfGames.Markdown.Gemtext. This only processes files with a text input + /// and the IsMarkdown component. + /// + public class MarkdownToGemtext : MarkdownOperationBase + { + /// + /// Converts the Markdown file into HTML. + /// + /// The entity to convert. + /// The content for this entity. + /// The markdown pipeline. + /// A converted entity. + protected override Entity Convert( + Entity entity, + ITextContent markdownContent, + MarkdownPipeline options) + { + string markdown = markdownContent.GetText(); + string gemtext = MarkdownGemtext.ToGemtext(markdown, options); + var content = new StringTextContent(gemtext); + + entity = entity + .SetTextContent(content) + .Remove() + .Set(IsGemtext.Instance); + + return entity; + } + } +} diff --git a/src/Nitride.Markdown/MarkdownToHtml.cs b/src/Nitride.Markdown/MarkdownToHtml.cs new file mode 100644 index 0000000..70dd66d --- /dev/null +++ b/src/Nitride.Markdown/MarkdownToHtml.cs @@ -0,0 +1,40 @@ +using Gallium; +using Markdig; +using Nitride.Contents; +using Nitride.Html; + +namespace Nitride.Markdown +{ + /// + /// Converts the input Markdown files into HTML using Markdig. This only + /// processes files with a text input and the IsMarkdown component. + /// + public class MarkdownToHtml : MarkdownOperationBase + { + /// + /// Converts the Markdown file into HTML. + /// + /// The entity to convert. + /// The content for this entity. + /// The markdown pipeline. + /// A converted entity. + protected override Entity Convert( + Entity entity, + ITextContent markdownContent, + MarkdownPipeline options) + { + // Convert the entity to Html. + string markdown = markdownContent.GetText(); + string html = Markdig.Markdown.ToHtml(markdown, options); + var htmlContent = new StringTextContent(html); + + entity = entity + .SetTextContent(htmlContent) + .Remove() + .Set(IsHtml.Instance); + + // Return the resulting entity. + return entity; + } + } +} diff --git a/src/Nitride.Markdown/MarkdownToOperationBaseExtension.cs b/src/Nitride.Markdown/MarkdownToOperationBaseExtension.cs new file mode 100644 index 0000000..ae74163 --- /dev/null +++ b/src/Nitride.Markdown/MarkdownToOperationBaseExtension.cs @@ -0,0 +1,22 @@ +using System; +using Markdig; + +namespace Nitride.Markdown +{ + /// + /// Extension methods to handle generics while configuring a Markdown + /// operation. + /// + public static class MarkdownToOperationBaseExtension + { + public static T WithConfigureMarkdown( + this T operation, + Action? callback) + where T : MarkdownOperationBase + + { + operation.ConfigureMarkdown = callback; + return operation; + } + } +} diff --git a/src/Nitride.Markdown/Nitride.Markdown.csproj b/src/Nitride.Markdown/Nitride.Markdown.csproj new file mode 100644 index 0000000..a3d397a --- /dev/null +++ b/src/Nitride.Markdown/Nitride.Markdown.csproj @@ -0,0 +1,37 @@ + + + + net5.0 + enable + + + + An extension to Nitride static site generator to render Markdown content. + + + + + + + + + + + + + + + + + + True + + + + + Analyzer + False + + + + diff --git a/src/Nitride.Markdown/NitrideMarkdownBuilderExtensions.cs b/src/Nitride.Markdown/NitrideMarkdownBuilderExtensions.cs new file mode 100644 index 0000000..e4e18fe --- /dev/null +++ b/src/Nitride.Markdown/NitrideMarkdownBuilderExtensions.cs @@ -0,0 +1,14 @@ +using Autofac; + +namespace Nitride.Markdown +{ + public static class NitrideMarkdownBuilderExtensions + { + public static NitrideBuilder UseMarkdown(this NitrideBuilder builder) + { + return builder + .ConfigureContainer( + x => x.RegisterModule()); + } + } +} diff --git a/src/Nitride.Markdown/NitrideMarkdownModule.cs b/src/Nitride.Markdown/NitrideMarkdownModule.cs new file mode 100644 index 0000000..fc0e8ff --- /dev/null +++ b/src/Nitride.Markdown/NitrideMarkdownModule.cs @@ -0,0 +1,6 @@ +namespace Nitride.Markdown +{ + public class NitrideMarkdownModule : NitrideModuleBase + { + } +} diff --git a/src/Nitride.Slugs/ISlugProvider.cs b/src/Nitride.Slugs/ISlugProvider.cs new file mode 100644 index 0000000..86b8c76 --- /dev/null +++ b/src/Nitride.Slugs/ISlugProvider.cs @@ -0,0 +1,15 @@ +namespace Nitride.Slugs +{ + /// + /// An interface that provides slugs for various paths. + /// + public interface ISlugProvider + { + /// + /// Converts the given input into a slug. + /// + /// The input string to normalize. + /// The resulting slug. + string ToSlug(string input); + } +} diff --git a/src/Nitride.Slugs/Nitride.Slugs.csproj b/src/Nitride.Slugs/Nitride.Slugs.csproj new file mode 100644 index 0000000..ced4fca --- /dev/null +++ b/src/Nitride.Slugs/Nitride.Slugs.csproj @@ -0,0 +1,31 @@ + + + + net5.0 + + + + An extension to Nitride static site generator to generate slugs. + + + + + + + + + + + + + True + + + + + Analyzer + False + + + + diff --git a/src/Nitride.Slugs/NitrideSlugsBuilderExtensions.cs b/src/Nitride.Slugs/NitrideSlugsBuilderExtensions.cs new file mode 100644 index 0000000..55d5907 --- /dev/null +++ b/src/Nitride.Slugs/NitrideSlugsBuilderExtensions.cs @@ -0,0 +1,28 @@ +using System; +using Autofac; + +namespace Nitride.Slugs +{ + public static class NitrideSlugsBuilderExtensions + { + public static NitrideBuilder UseSlugs( + this NitrideBuilder builder, + ISlugProvider slugs) + { + if (slugs == null) + { + throw new ArgumentNullException(nameof(slugs)); + } + + return builder + .ConfigureContainer( + x => + { + x.RegisterInstance(slugs) + .As() + .SingleInstance(); + x.RegisterModule(); + }); + } + } +} diff --git a/src/Nitride.Slugs/NitrideSlugsModule.cs b/src/Nitride.Slugs/NitrideSlugsModule.cs new file mode 100644 index 0000000..255c2b9 --- /dev/null +++ b/src/Nitride.Slugs/NitrideSlugsModule.cs @@ -0,0 +1,8 @@ +using Autofac; + +namespace Nitride.Slugs +{ + public class NitrideSlugsModule : Module + { + } +} diff --git a/src/Nitride.Slugs/SimpleSlugProvider.cs b/src/Nitride.Slugs/SimpleSlugProvider.cs new file mode 100644 index 0000000..9685d7c --- /dev/null +++ b/src/Nitride.Slugs/SimpleSlugProvider.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Slugify; + +namespace Nitride.Slugs +{ + /// + /// A default implementation of ISlugProvider. + /// + public class SimpleSlugProvider : ISlugProvider, IEnumerable + { + private readonly List<(string, string)> replacements; + + public SimpleSlugProvider() + { + this.replacements = new List<(string, string)>(); + } + + public SimpleSlugProvider(IDictionary replacements) + : this() + { + foreach (KeyValuePair pair in replacements) + { + this.Add(pair.Key, pair.Value); + } + } + + /// + /// Adds a replacement for the resulting slug. + /// + /// The text to search for. + /// The replacement string. + public void Add(string search, string replace) + { + this.replacements.Add((search, replace)); + } + + /// + public IEnumerator GetEnumerator() + { + return this.replacements + .Select(x => x.Item1) + .GetEnumerator(); + } + + /// + public virtual string ToSlug(string input) + { + // If we have null or whitespace, we have a problem. + if (string.IsNullOrWhiteSpace(input)) + { + throw new ArgumentException( + "Cannot have a blank or null input", + nameof(input)); + } + + // Create a slug.. + var helper = new SlugHelper(); + string output = helper.GenerateSlug(input); + + // Perform any additional replacements. + foreach ((string search, string replace) in this.replacements) + { + output = output.Replace(search, replace); + } + + return output; + } + + /// + IEnumerator IEnumerable.GetEnumerator() + { + return this.GetEnumerator(); + } + } +} diff --git a/src/Nitride.Slugs/UnicodeNormalizingSlugProvider.cs b/src/Nitride.Slugs/UnicodeNormalizingSlugProvider.cs new file mode 100644 index 0000000..2b7451b --- /dev/null +++ b/src/Nitride.Slugs/UnicodeNormalizingSlugProvider.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; + +namespace Nitride.Slugs +{ + /// + /// Extends the slug provider to strip out accented characters for + /// a normalized form. + /// + public class UnicodeNormalizingSlugProvider : SimpleSlugProvider + { + /// + public UnicodeNormalizingSlugProvider() + { + } + + /// + public UnicodeNormalizingSlugProvider( + IDictionary replacements) + : base(replacements) + { + } + + /// + public override string ToSlug(string input) + { + // If we have null or whitespace, we have a problem. + if (string.IsNullOrWhiteSpace(input)) + { + throw new ArgumentException( + "Cannot have a blank or null input", + nameof(input)); + } + + // Normalize the Unicode objects. + // Strip out the accents. This is a cheesy way of doing so. + char[] chars = input + .Normalize(NormalizationForm.FormD) + .Where(this.IsNonSpacingMark) + .ToArray(); + string normalized = new string(chars) + .Normalize(NormalizationForm.FormC); + + // Return the base implementation. + return base.ToSlug(normalized); + } + + private bool IsNonSpacingMark(char c) + { + UnicodeCategory category = CharUnicodeInfo.GetUnicodeCategory(c); + + return category != UnicodeCategory.NonSpacingMark; + } + } +} diff --git a/src/Nitride.Temporal/CanExpire.cs b/src/Nitride.Temporal/CanExpire.cs new file mode 100644 index 0000000..cd15869 --- /dev/null +++ b/src/Nitride.Temporal/CanExpire.cs @@ -0,0 +1,10 @@ +namespace Nitride.Temporal +{ + /// + /// A marker component for identifying a post that can expire. + /// + public class CanExpire + { + public static CanExpire Instance { get; } = new(); + } +} diff --git a/src/Nitride.Temporal/DatePipelineCommandOption.cs b/src/Nitride.Temporal/DatePipelineCommandOption.cs new file mode 100644 index 0000000..325f274 --- /dev/null +++ b/src/Nitride.Temporal/DatePipelineCommandOption.cs @@ -0,0 +1,69 @@ +using System; +using System.CommandLine; +using System.CommandLine.Invocation; +using System.Globalization; +using Nitride.Commands; +using Nitride.Registration; +using NodaTime; +using NodaTime.Testing; +using Serilog; + +namespace Nitride.Temporal +{ + /// + /// A factory to inject the "--date=XXXX-XX-XX" argument into the build + /// and other pipeline commands. + /// + [NoRegistration] + public class DatePipelineCommandOption : IPipelineCommandOption + { + private readonly NitrideClock clock; + + private readonly ILogger logger; + + public DatePipelineCommandOption( + ILogger logger, + NitrideClock clock) + { + this.logger = logger.ForContext(); + this.clock = clock; + this.Option = new Option("--date") + { + Description = "Sets the date to something other than now", + ArgumentHelpName = "DATE", + }; + } + + /// + public Option Option { get; } + + /// + public void Handle(InvocationContext context) + { + // If we got a date, then use NodaTime's fake clock to set it so + // everything will use that. + var value = (DateTime?)context.ParseResult + .ValueForOption(this.Option); + + if (value.HasValue && value.Value != DateTime.MinValue) + { + // We have a date, so we need to create a fake clock that has this + // date for the entire run. + Instant instant = this.clock.CreateInstant(value.Value); + + this.clock.Clock = new FakeClock(instant); + } + + // Report the date we are processing. + Instant now = this.clock.Clock.GetCurrentInstant(); + ZonedDateTime dateTime = now.InZone(this.clock.DateTimeZone); + string formatted = dateTime.ToString( + "yyyy-MM-dd HH:mm:ss x", + CultureInfo.CurrentCulture); + + this.logger.Information( + "Setting date/time to {When:l}", + formatted); + } + } +} diff --git a/src/Nitride.Temporal/ExpiresPipelineCommandOption.cs b/src/Nitride.Temporal/ExpiresPipelineCommandOption.cs new file mode 100644 index 0000000..4b283b9 --- /dev/null +++ b/src/Nitride.Temporal/ExpiresPipelineCommandOption.cs @@ -0,0 +1,75 @@ +using System.CommandLine; +using System.CommandLine.Invocation; +using System.Globalization; +using Nitride.Commands; +using Nitride.Registration; +using NodaTime; +using Serilog; +using TimeSpanParserUtil; + +namespace Nitride.Temporal +{ + /// + /// A factory to inject the "--expires=XXXX" argument into the build + /// and other pipeline commands. + /// + [NoRegistration] + public class ExpiresPipelineCommandOption : IPipelineCommandOption + { + private readonly NitrideClock clock; + + private readonly ILogger logger; + + public ExpiresPipelineCommandOption( + ILogger logger, + NitrideClock clock, + string? defaultValue = null) + { + this.logger = logger.ForContext(); + this.clock = clock; + this.Option = new Option("--expires", () => defaultValue) + { + Description = + "Sets the expiration time as time before the current date", + ArgumentHelpName = "TIMESPAN", + }; + } + + /// + public Option Option { get; } + + /// + public void Handle(InvocationContext context) + { + // If we have a format, then we are going to set one. If the format + // is blank or never, then we are going to treat it as not + // expiring anything. + string? value = (string?)context.ParseResult + .ValueForOption(this.Option); + + if (value != null && value.ToLowerInvariant() != "never") + { + // Parse the format using TimeSpanParser. + this.clock.Expires = TimeSpanParser.Parse(value); + } + + // If we don't have anything, then just report and we're done. + if (this.clock.Expiration == null) + { + this.logger.Information("No entities will be expired"); + return; + } + + // Figure out when we are going to be expiring. + Instant when = this.clock.Expiration.Value; + ZonedDateTime dateTime = when.InZone(this.clock.DateTimeZone); + string formatted = dateTime.ToString( + "yyyy-MM-dd HH:mm:ss x", + CultureInfo.CurrentCulture); + + this.logger.Information( + "Expiring entries before {When:l}", + formatted); + } + } +} diff --git a/src/Nitride.Temporal/FilterOutExpiredInstants.cs b/src/Nitride.Temporal/FilterOutExpiredInstants.cs new file mode 100644 index 0000000..d3452b3 --- /dev/null +++ b/src/Nitride.Temporal/FilterOutExpiredInstants.cs @@ -0,0 +1,44 @@ +using System.Collections.Generic; +using System.Linq; +using Gallium; +using NodaTime; + +namespace Nitride.Temporal +{ + /// + /// Filters out all expired entities that are before the expiration. + /// + public class FilterOutExpiredInstants : NitrideOperationBase + { + private readonly NitrideClock clock; + + public FilterOutExpiredInstants(NitrideClock clock) + { + this.clock = clock; + } + + /// + public override IEnumerable Run(IEnumerable input) + { + if (!this.clock.Expiration.HasValue) + { + return input; + } + + return input + .OrderBy(x => x.Get()) + .ForEntities( + x => x + .Where(this.IsNotExpired)); + } + + private bool IsNotExpired(Entity entity) + { + var instant = entity.Get(); + Instant expiration = this.clock.Expiration!.Value; + bool isExpired = instant.CompareTo(expiration) < 0; + + return !isExpired; + } + } +} diff --git a/src/Nitride.Temporal/FilterOutFutureInstants.cs b/src/Nitride.Temporal/FilterOutFutureInstants.cs new file mode 100644 index 0000000..e672cae --- /dev/null +++ b/src/Nitride.Temporal/FilterOutFutureInstants.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using Gallium; +using NodaTime; + +namespace Nitride.Temporal +{ + /// + /// Filters out all entities that have an instant before the current + /// NitrideClock time. + /// + public class FilterOutFutureInstants : NitrideOperationBase + { + private readonly NitrideClock clock; + + public FilterOutFutureInstants(NitrideClock clock) + { + this.clock = clock; + } + + /// + public override IEnumerable Run(IEnumerable input) + { + Instant now = this.clock.Clock.GetCurrentInstant(); + + return input + .WhereEntities( + (_, instant) => instant.CompareTo(now) <= 0); + } + } +} diff --git a/src/Nitride.Temporal/Nitride.Temporal.csproj b/src/Nitride.Temporal/Nitride.Temporal.csproj new file mode 100644 index 0000000..5fd6c9d --- /dev/null +++ b/src/Nitride.Temporal/Nitride.Temporal.csproj @@ -0,0 +1,40 @@ + + + + net5.0 + enable + Nitride.Temporal + + + + An extension to Nitride static site generator to add time-based components and filters. + + + + + + + + + + + + + + + + + + + + True + + + + + Analyzer + False + + + + diff --git a/src/Nitride.Temporal/NitrideClock.cs b/src/Nitride.Temporal/NitrideClock.cs new file mode 100644 index 0000000..77d929d --- /dev/null +++ b/src/Nitride.Temporal/NitrideClock.cs @@ -0,0 +1,144 @@ +using System; +using Nitride.Registration; +using NodaTime; +using NodaTime.Testing; + +namespace Nitride.Temporal +{ + /// + /// A class that handles date and time processing. This provides identifying + /// the desire time zone along with various methods for processing parsed + /// DateTime objects into NodaTime.Instant. + /// + [Singleton] + public class NitrideClock + { + public NitrideClock() + { + // We use FakeClock because we don't want time to advance in the + // middle of running, just in case we are just a few seconds before + // rollover. + Instant instant = SystemClock.Instance.GetCurrentInstant(); + + this.DateTimeZone = DateTimeZoneProviders.Bcl.GetSystemDefault(); + this.Clock = new FakeClock(instant); + } + + /// + /// Gets or sets the clock used to perform time calculations. + /// + public IClock Clock { get; set; } + + /// + /// Gets or sets the date time zone used for processing. + /// + public DateTimeZone DateTimeZone { get; set; } + + /// + /// Gets the instant when the files expire. + /// + public Instant? Expiration => this.Expires.HasValue + ? this.Clock.GetCurrentInstant() + .Minus(Duration.FromTimeSpan(this.Expires.Value)) + : null; + + /// + /// Gets or sets the expiration time span, which is calculated from the + /// current time. + /// + public TimeSpan? Expires { get; set; } + + /// + /// Creates an instant from a given year, month, and day. + /// + /// The numerical year. + /// The numerical month. + /// The numerical day of month. + /// An instant in the clock's time zone. + public Instant CreateInstant(int year, int month, int day) + { + var date = new LocalDate(year, month, day); + + return this.CreateInstant(date); + } + + /// + /// Creates an instant from the given local date. + /// + /// The date to parse. + /// An instant in the clock's time zone. + public Instant CreateInstant(LocalDate date) + { + ZonedDateTime zoned = date.AtStartOfDayInZone(this.DateTimeZone); + var instant = zoned.ToInstant(); + + return instant; + } + + /// + /// Creates an instant from the given date time zone. + /// + /// The object to convert. + /// An instant representing this DateTimeOffset. + public Instant CreateInstant(DateTimeOffset when) + { + OffsetDateTime offset = OffsetDateTime.FromDateTimeOffset(when); + + return offset.ToInstant(); + } + + /// + /// Creates an instant from the given date time. + /// + /// The object to convert. + /// An instant representing this DateTimeOffset. + public Instant CreateInstant(DateTime when) + { + if (when.Kind == DateTimeKind.Unspecified) + { + LocalDate localDate = LocalDate.FromDateTime(when); + + return this.CreateInstant(localDate); + } + + var offset = new DateTimeOffset(when); + + return this.CreateInstant(offset); + } + + /// + /// Converts an instant into a DateTime for the clock's time zone. + /// + /// The instant to convert. + /// A DateTime in the correct date and time. + public DateTime ToDateTime(Instant instant) + { + return instant + .InZone(this.DateTimeZone) + .ToDateTimeOffset() + .DateTime; + } + + /// + /// Sets the clock used for time calculations. + /// + /// The new clock to use. + /// The instance for chaining methods. + public NitrideClock WithClock(IClock clock) + { + this.Clock = clock; + return this; + } + + /// + /// Sets the date time zone and returns itself for chaining. + /// + /// The name of the time zone. + /// The instance for chaining methods. + public NitrideClock WithDateTimeZone(string timeZoneName) + { + this.DateTimeZone = DateTimeZoneProviders.Tzdb[timeZoneName]; + return this; + } + } +} diff --git a/src/Nitride.Temporal/NitrideTemporalBuilderExtensions.cs b/src/Nitride.Temporal/NitrideTemporalBuilderExtensions.cs new file mode 100644 index 0000000..08e4fd6 --- /dev/null +++ b/src/Nitride.Temporal/NitrideTemporalBuilderExtensions.cs @@ -0,0 +1,105 @@ +using System; +using Autofac; +using Nitride.Commands; +using Serilog; +using Expires = Nitride.Temporal.ExpiresPipelineCommandOption; + +namespace Nitride.Temporal +{ + public static class NitrideTemporalBuilderExtensions + { + /// + /// Extends the builder to allow for configuring the temporal + /// settings for generation. + /// + /// The host builder being configured. + /// The callback to configure the clock. + /// The builder passed in. + public static NitrideBuilder ConfigureClock( + this NitrideBuilder builder, + Action callback) + { + return builder + .UseTemporal() + .ConfigureSite( + (_, scope) => + { + var clock = scope.Resolve(); + callback(clock); + }); + } + + public static NitrideBuilder UseTemporal(this NitrideBuilder builder) + { + return builder + .ConfigureContainer( + x => x.RegisterModule()); + } + + /// + /// Adds the "--date=XXXX-XX-XX" option into the pipeline commands. + /// + /// The host builder being configured. + /// The builder passed in. + public static NitrideBuilder WithClockFromOptions( + this NitrideBuilder builder) + { + return builder + .UseTemporal() + .ConfigureContainer( + x => + { + x + .RegisterType() + .As(); + }); + } + + /// + /// Adds the "--expire=XXXX" option into the pipeline commands where + /// "XXX" is a format like "5y" or "500000:00:00.0". This is parsed by + /// TimeSpanParser which gives an easy format. + /// + /// The host builder being configured. + /// The default expiration, if one is provided. + /// The builder passed in. + public static NitrideBuilder WithExpiresFromOptions( + this NitrideBuilder builder, + TimeSpan defaultValue) + { + return WithExpiresFromOptions(builder, defaultValue.ToString()); + } + + /// + /// Adds the "--expire=XXXX" option into the pipeline commands where + /// "XXX" is a format like "5y" or "500000:00:00.0". This is parsed by + /// TimeSpanParser which gives an easy format. + /// + /// The host builder being configured. + /// The default expiration, if one is provided. + /// The builder passed in. + public static NitrideBuilder WithExpiresFromOptions( + this NitrideBuilder builder, + string? defaultValue = null) + { + return builder + .ConfigureContainer( + x => + { + x + .Register( + context => + { + var logger = context.Resolve(); + var clock = context.Resolve(); + + return new Expires( + logger, + clock, + defaultValue); + }) + .As(); + }); + } + } +} diff --git a/src/Nitride.Temporal/NitrideTemporalModule.cs b/src/Nitride.Temporal/NitrideTemporalModule.cs new file mode 100644 index 0000000..0443875 --- /dev/null +++ b/src/Nitride.Temporal/NitrideTemporalModule.cs @@ -0,0 +1,15 @@ +using Autofac; + +namespace Nitride.Temporal +{ + public class NitrideTemporalModule : NitrideModuleBase + { + /// + protected override void Register(ContainerBuilder builder) + { + builder + .RegisterGeneric(typeof(SetInstantFromComponent<>)) + .As(typeof(SetInstantFromComponent<>)); + } + } +} diff --git a/src/Nitride.Temporal/README.md b/src/Nitride.Temporal/README.md new file mode 100644 index 0000000..67eb6d7 --- /dev/null +++ b/src/Nitride.Temporal/README.md @@ -0,0 +1,42 @@ +# Date Processing + +One of the common features of static websites are blogs which leads to having +some form of date-centric processing of pages to build archive pages, calendars, +and being able to write posts in the future. + +With the component system, the date of a given file is simply attached to +`Nodatime.Instant` component of the `Entity` object for the bulk of the +processing. + +## Supporting Time Zones + +The concept of time zones while date processing is one that is frequently +overlooked. A date is a date, right? However, most blogs and news sites have a +concept of when a new day starts but it isn't always the same time as the server +that is building the site. While a blog might be in America/Chicago time, a CI +server could be set to UTC (such as Azure build servers) and the +"day" may roll over fix or six hours before or after the blog's time. + +This is why Nitride uses `Instant` for when pages are implemented. These are +points in time that are independent of time zones, but we also provide tools for +converting a date model or one from the path into a proper instant based on the +blog's time zone. + +## Why NodaTime? + +We decided to use [NodaTime](https://nodatime.org/) instead of the built-in date +time functions for a number of reasons, mainly because it has a more intuitive +way of handling time zones + +## Configuring + +There are two callbacks on `NitrideBuilder` that can be used to define the date +and time processing for the blog. + +```csharp +NitrideBuilder builder; + +builder + .ConfigureDates((NitrideClock clock) => clock.SetTimeZone()) +``` + diff --git a/src/Nitride.Temporal/SetInstantFromComponent.cs b/src/Nitride.Temporal/SetInstantFromComponent.cs new file mode 100644 index 0000000..c9848d1 --- /dev/null +++ b/src/Nitride.Temporal/SetInstantFromComponent.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.Generic; +using Gallium; +using NodaTime; + +namespace Nitride.Temporal +{ + /// + /// Sets the instant from another component. This has methods for handling + /// nullable properties inside the component. + /// + public class SetInstantFromComponent : NitrideOperationBase + { + private readonly NitrideClock clock; + + public SetInstantFromComponent(NitrideClock clock) + { + this.clock = clock; + } + + /// + /// The callback used to get a date time object which is converted into + /// an Instant automatically. This can handle DateTime, DateTimeOffset, + /// LocalDate, and Instant. Nulls are skipped. + /// + public Func? GetDateTimeObject + { + get; + set; + } + + /// + public override IEnumerable Run(IEnumerable input) + { + this.CheckNotNull( + nameof(this.GetDateTimeObject), + this.GetDateTimeObject); + + return input + .ForEachEntity(this.Set); + } + + /// + /// Sets the callback used to get a date time object which is converted + /// into an Instant automatically. This can handle DateTime, + /// DateTimeOffset, LocalDate, and Instant. Nulls are skipped. + /// + public SetInstantFromComponent WithGetDateTimeObject( + Func? callback) + { + this.GetDateTimeObject = callback; + return this; + } + + private Entity Set(Entity entity, TComponent component) + { + object? temporal = this.GetDateTimeObject!(entity, component); + Instant instant; + + switch (temporal) + { + case null: + return entity; + case Instant direct: + instant = direct; + break; + case LocalDate other: + instant = this.clock.CreateInstant(other); + break; + case DateTime other: + instant = this.clock.CreateInstant(other); + break; + case DateTimeOffset other: + instant = this.clock.CreateInstant(other); + break; + default: + throw new InvalidOperationException( + "Did not get a date time object from the callback. " + + "Can only handle DateTime, DateTimeOffset, LocalDate, " + + "and Instant. Got a " + + temporal.GetType().Name + + " instead."); + } + + return entity.Set(instant); + } + } +} diff --git a/src/Nitride.Temporal/SetInstantFromPath.cs b/src/Nitride.Temporal/SetInstantFromPath.cs new file mode 100644 index 0000000..39cbc18 --- /dev/null +++ b/src/Nitride.Temporal/SetInstantFromPath.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using Gallium; +using NodaTime; +using Zio; + +namespace Nitride.Temporal +{ + /// + /// Sets the instant in the file based on the path of the entity. This + /// defaults to a generous regular expression that handles most formats of + /// four digit year, a separator, two digit month, a separator, and a two + /// digit day of month. + /// + [WithProperties] + public partial class SetInstantFromPath : NitrideOperationBase + { + private readonly NitrideClock clock; + + public SetInstantFromPath(NitrideClock clock) + { + this.clock = clock; + this.PathRegex = new Regex( + @"(?\d{4})[/-](?\d{2})[/-](?\d{2})"); + } + + /// + /// Gets or sets a regular expression that has three named expressions: + /// year, month, and day. + /// + public Regex? PathRegex { get; set; } + + /// + public override IEnumerable Run(IEnumerable input) + { + this.CheckNotNull(nameof(this.PathRegex), this.PathRegex); + + return input.ForEachEntity(this.Set); + } + + private Entity Set(Entity entity, UPath path) + { + // See if the path matches the given expression. + Match match = this.PathRegex!.Match(path.ToString()); + + if (!match.Success) + { + return entity; + } + + // Create an Instant from this. + Instant instant = this.clock.CreateInstant( + Convert.ToInt32(match.Groups["year"].Value), + Convert.ToInt32(match.Groups["month"].Value), + Convert.ToInt32(match.Groups["day"].Value)); + + return entity.Set(instant); + } + } +} diff --git a/src/Nitride.Tests/EntityContentTests.cs b/src/Nitride.Tests/EntityContentTests.cs new file mode 100644 index 0000000..0cc94af --- /dev/null +++ b/src/Nitride.Tests/EntityContentTests.cs @@ -0,0 +1,75 @@ +using Gallium; +using Nitride.Contents; +using Xunit; +using Xunit.Abstractions; + +namespace Nitride.Tests +{ + /// + /// Tests the various functionality of the high-level content methods and + /// extension methods. + /// + public class EntityContentTests : NitrideTestsBase + { + public EntityContentTests(ITestOutputHelper output) + : base(output) + { + } + + [Fact] + public void AddBinaryContent() + { + var content = new ByteArrayBinaryContent(new byte[0]); + Entity entity = new Entity() + .SetBinaryContent(content); + + Assert.Equal(1, entity.Count); + Assert.True(entity.HasContent()); + Assert.True(entity.HasBinaryContent()); + Assert.False(entity.HasTextContent()); + } + + [Fact] + public void AddTextContent() + { + var content = new StringTextContent("contents"); + Entity entity = new Entity() + .SetTextContent(content); + + Assert.Equal(1, entity.Count); + Assert.True(entity.HasContent()); + Assert.False(entity.HasBinaryContent()); + Assert.True(entity.HasTextContent()); + } + + [Fact] + public void SwitchBinaryToTextContent() + { + var binaryContent = new ByteArrayBinaryContent(new byte[0]); + var textContent = new StringTextContent("contents"); + Entity entity = new Entity() + .SetBinaryContent(binaryContent) + .SetTextContent(textContent); + + Assert.Equal(1, entity.Count); + Assert.True(entity.HasContent()); + Assert.False(entity.HasBinaryContent()); + Assert.True(entity.HasTextContent()); + } + + [Fact] + public void SwitchTextToBinaryContent() + { + var binaryContent = new ByteArrayBinaryContent(new byte[0]); + var textContent = new StringTextContent("contents"); + Entity entity = new Entity() + .SetContent(textContent) + .SetContent(binaryContent); + + Assert.Equal(1, entity.Count); + Assert.True(entity.HasContent()); + Assert.True(entity.HasBinaryContent()); + Assert.False(entity.HasTextContent()); + } + } +} diff --git a/src/Nitride.Tests/Nitride.Tests.csproj b/src/Nitride.Tests/Nitride.Tests.csproj new file mode 100644 index 0000000..6b92b54 --- /dev/null +++ b/src/Nitride.Tests/Nitride.Tests.csproj @@ -0,0 +1,30 @@ + + + + net5.0 + false + enable + Nitride.Tests + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/src/Nitride.Tests/NitrideTestsBase.cs b/src/Nitride.Tests/NitrideTestsBase.cs new file mode 100644 index 0000000..1cf3ada --- /dev/null +++ b/src/Nitride.Tests/NitrideTestsBase.cs @@ -0,0 +1,63 @@ +using Autofac; +using Serilog; +using Serilog.Core; +using Xunit.Abstractions; + +namespace Nitride.Tests +{ + /// + /// Common initialization logic for Nitride-based tests including setting + /// up containers, logging, and Serilog. + /// + public abstract class NitrideTestsBase + { + private readonly ITestOutputHelper output; + + public NitrideTestsBase(ITestOutputHelper output) + { + this.output = output; + + // Set up logging. + const string Template = + "[{Level:u3}] " + + "({SourceContext}) {Message}" + + "{NewLine}{Exception}"; + + this.Logger = new LoggerConfiguration() + .WriteTo.TestOutput( + output, + outputTemplate: Template) + .CreateLogger(); + + // Set up Autofac. + var builder = new ContainerBuilder(); + + builder.RegisterInstance(this.Logger) + .As() + .SingleInstance(); + builder.RegisterModule(); + + this.ConfigureContainer(builder); + + this.Container = builder.Build(); + } + + /// + /// Contains the Autofac container for this test. + /// + protected IContainer Container { get; } + + /// + /// Gets the logger used to report messages about the test. + /// + protected Logger Logger { get; } + + /// + /// Configures the Autofac container for this test instance. + /// + /// + protected virtual void ConfigureContainer(ContainerBuilder builder) + { + } + } +} diff --git a/src/Nitride.Yaml.Tests/Nitride.Yaml.Tests.csproj b/src/Nitride.Yaml.Tests/Nitride.Yaml.Tests.csproj new file mode 100644 index 0000000..cb94658 --- /dev/null +++ b/src/Nitride.Yaml.Tests/Nitride.Yaml.Tests.csproj @@ -0,0 +1,29 @@ + + + + net5.0 + enable + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + diff --git a/src/Nitride.Yaml.Tests/ParseYamlHeaderTests.cs b/src/Nitride.Yaml.Tests/ParseYamlHeaderTests.cs new file mode 100644 index 0000000..434513c --- /dev/null +++ b/src/Nitride.Yaml.Tests/ParseYamlHeaderTests.cs @@ -0,0 +1,114 @@ +using System.Collections.Generic; +using System.Linq; +using Gallium; +using Nitride.Contents; +using Nitride.Tests; +using Xunit; +using Xunit.Abstractions; + +namespace Nitride.Yaml.Tests +{ + public class ParseYamlHeaderTests : NitrideTestsBase + { + public ParseYamlHeaderTests(ITestOutputHelper output) + : base(output) + { + } + + [Fact] + public void ParseNoContent() + { + var input = new List + { + new Entity() + .SetTextContent( + new StringTextContent( + "---", + "title: Test Title 1", + "---")) + }; + + var op = new ParseYamlHeader(); + + Entity output = input + .Run(op) + .First(); + + Assert.True(output.Has()); + Assert.Equal( + new[] + { + string.Empty, + }, + output.Get().GetText().Split("\n")); + Assert.True(output.Has()); + Assert.Equal("Test Title 1", output.Get().Title); + } + + [Fact] + public void ParseNoHeader() + { + var input = new List + { + new Entity() + .SetTextContent( + new StringTextContent( + "Hello")) + }; + + var op = new ParseYamlHeader(); + + Entity output = input + .Run(op) + .First(); + + Assert.True(output.Has()); + Assert.Equal( + new[] + { + "Hello", + string.Empty, + }, + output.Get().GetText().Split("\n")); + Assert.True(output.Has()); + Assert.Null(output.Get().Title); + } + + [Fact] + public void ParseSimpleHeader() + { + var input = new List + { + new Entity() + .SetTextContent( + new StringTextContent( + "---", + "title: Test Title 1", + "---", + "Hello")) + }; + + var op = new ParseYamlHeader(); + + Entity output = input + .Run(op) + .First(); + + Assert.True(output.Has()); + Assert.Equal( + new[] + { + "Hello", + string.Empty, + }, + output.Get().GetText().Split("\n")); + Assert.True(output.Has()); + Assert.Equal("Test Title 1", output.Get().Title); + } + + private class TestHeader + { + public string? Title { get; set; } + } + } +} diff --git a/src/Nitride.Yaml/Nitride.Yaml.csproj b/src/Nitride.Yaml/Nitride.Yaml.csproj new file mode 100644 index 0000000..4221334 --- /dev/null +++ b/src/Nitride.Yaml/Nitride.Yaml.csproj @@ -0,0 +1,33 @@ + + + + net5.0 + enable + + + + An extension to Nitride static site generator to parse YAML content. + + + + + + + + + + + + + + True + + + + + Analyzer + False + + + + diff --git a/src/Nitride.Yaml/NitrideYamlBuilderExtensions.cs b/src/Nitride.Yaml/NitrideYamlBuilderExtensions.cs new file mode 100644 index 0000000..146b250 --- /dev/null +++ b/src/Nitride.Yaml/NitrideYamlBuilderExtensions.cs @@ -0,0 +1,14 @@ +using Autofac; + +namespace Nitride.Yaml +{ + public static class NitrideYamlBuilderExtensions + { + public static NitrideBuilder UseYaml(this NitrideBuilder builder) + { + return builder + .ConfigureContainer( + x => x.RegisterModule()); + } + } +} diff --git a/src/Nitride.Yaml/NitrideYamlModule.cs b/src/Nitride.Yaml/NitrideYamlModule.cs new file mode 100644 index 0000000..09a5709 --- /dev/null +++ b/src/Nitride.Yaml/NitrideYamlModule.cs @@ -0,0 +1,15 @@ +using Autofac; + +namespace Nitride.Yaml +{ + public class NitrideYamlModule : NitrideModuleBase + { + /// + protected override void Register(ContainerBuilder builder) + { + builder + .RegisterGeneric(typeof(ParseYamlHeader<>)) + .As(typeof(ParseYamlHeader<>)); + } + } +} diff --git a/src/Nitride.Yaml/ParseYamlHeader.cs b/src/Nitride.Yaml/ParseYamlHeader.cs new file mode 100644 index 0000000..c4e6203 --- /dev/null +++ b/src/Nitride.Yaml/ParseYamlHeader.cs @@ -0,0 +1,163 @@ +using System.Collections.Generic; +using System.IO; +using System.Text; +using Gallium; +using Nitride.Contents; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; + +namespace Nitride.Yaml +{ + /// + /// An operation that parses the header of a text file and pulls out the + /// YAML header with a specific type. + /// + /// The model that represents the header. + public class ParseYamlHeader : NitrideOperationBase + where TModel : class, new() + { + private bool ignoreUnmatchedProperties; + + private INamingConvention namingConvention; + + public ParseYamlHeader() + { + this.namingConvention = CamelCaseNamingConvention.Instance; + this.ignoreUnmatchedProperties = true; + this.RemoveHeader = true; + } + + /// + /// Gets or sets a value indicating whether the header should be removed + /// and the text content be removed. + /// + private bool RemoveHeader { get; set; } + + /// + public override IEnumerable Run(IEnumerable input) + { + // Set up the YAML parsing. + DeserializerBuilder builder = new DeserializerBuilder() + .WithNamingConvention(this.namingConvention); + + if (this.ignoreUnmatchedProperties) + { + builder = builder.IgnoreUnmatchedProperties(); + } + + IDeserializer deserializer = builder.Build(); + + // Process through the files. We only care about the text ones + // and we'll put a default TModel in for those that don't have a + // header. + return input + .ForEachEntity( + (entity, content) => this.Parse( + entity, + content, + deserializer)); + } + + /// + /// Sets the flag if the module should ignore unmatched properties + /// which defaults to true (ignore). + /// + /// The new value. + /// The module for chaining. + public ParseYamlHeader WithIgnoreUnmatchedProperties(bool value) + { + this.ignoreUnmatchedProperties = value; + return this; + } + + /// + /// Sets the naming convention to something other than the default + /// camel case. + /// + /// The new naming convention. + /// The module for chaining. + public ParseYamlHeader WithNamingConvention( + INamingConvention value) + { + this.namingConvention = value; + return this; + } + + public ParseYamlHeader WithRemoveHeader(bool value) + { + this.RemoveHeader = value; + return this; + } + + private Entity Parse( + Entity entity, + ITextContent content, + IDeserializer deserializer) + { + // Get the textual input from the stream. + using TextReader reader = content.GetReader(); + + // See if the first line is one that indicates a YAML header. + string? line = reader.ReadLine(); + + if (line != "---") + { + // This file doesn't have a YAML header, so add the default + // version of our model and move on. + return entity.Set(new TModel()); + } + + // Read the rest of the header until we get to the end of the + // header. If we get to the end of the file first, then we don't + // have a valid file and just return the default. + StringBuilder buffer = new(); + + buffer.AppendLine(line); + + while ((line = reader.ReadLine()) != null) + { + // If have a separator, then we're done processing. + if (line == "---") + { + break; + } + + // Read the next line into memory. + buffer.AppendLine(line); + } + + // If the line is null, then we got to the end of the file without + // finding a proper header, so use a default and have no other + // changes. + if (line == null) + { + return entity.Set(new TModel()); + } + + // Pull out the model so we can append it later. + string yaml = buffer.ToString(); + + var model = deserializer.Deserialize(yaml); + + // If we are not removing the header, then we're done. + if (!this.RemoveHeader) + { + return entity.Set(model); + } + + // Read the rest of the reader into a new (reused) buffer so we can + // set the new text content to the text without the header. + buffer.Length = 0; + + while ((line = reader.ReadLine()) != null) + { + buffer.AppendLine(line); + } + + // Set the model and return it. + return entity + .Set(model) + .SetTextContent(new StringTextContent(buffer.ToString())); + } + } +} diff --git a/src/Nitride/Commands/BuildCommand.cs b/src/Nitride/Commands/BuildCommand.cs new file mode 100644 index 0000000..9444b27 --- /dev/null +++ b/src/Nitride/Commands/BuildCommand.cs @@ -0,0 +1,56 @@ +using System.Collections.Generic; +using System.CommandLine; +using System.CommandLine.Invocation; +using System.Threading.Tasks; +using Nitride.Pipelines; +using Serilog; + +namespace Nitride.Commands +{ + /// + /// The basic command to generate a website and run through the pipelines. + /// + public class BuildCommand : Command, ICommandHandler, ITopLevelCommand + { + private readonly ILogger logger; + + private readonly IList pipelineOptions; + + private readonly PipelineManager pipelines; + + public BuildCommand( + ILogger logger, + PipelineManager pipelines, + IList pipelineOptions) + : base("build", "Generate the website") + { + // Set up our simple member variables. + this.pipelines = pipelines; + this.pipelineOptions = pipelineOptions; + this.logger = logger.ForContext(); + this.Handler = this; + + // Handle any injected arguments into the command line. + foreach (var option in pipelineOptions) + { + this.AddOption(option.Option); + } + } + + /// + public async Task InvokeAsync(InvocationContext context) + { + // Process any injected options. + foreach (var option in this.pipelineOptions) + { + option.Handle(context); + } + + // This is the main entry point into the `build` command. This runs + // all the pipelines once and then quits when it finishes. + int pipelinesResults = await this.pipelines.RunAsync(); + + return pipelinesResults; + } + } +} diff --git a/src/Nitride/Commands/IPipelineCommandOption.cs b/src/Nitride/Commands/IPipelineCommandOption.cs new file mode 100644 index 0000000..6dbc6df --- /dev/null +++ b/src/Nitride/Commands/IPipelineCommandOption.cs @@ -0,0 +1,23 @@ +using System.CommandLine; +using System.CommandLine.Invocation; + +namespace Nitride.Commands +{ + /// + /// Creates new options for the build command to allow injecting an + /// option into the build command. + /// + public interface IPipelineCommandOption + { + /// + /// The option that used to bind to the command. + /// + Option Option { get; } + + /// + /// Handles the command after it has been created and the options bound. + /// + /// The context of the CLI command. + void Handle(InvocationContext context); + } +} diff --git a/src/Nitride/Commands/ITopLevelCommand.cs b/src/Nitride/Commands/ITopLevelCommand.cs new file mode 100644 index 0000000..301a6b8 --- /dev/null +++ b/src/Nitride/Commands/ITopLevelCommand.cs @@ -0,0 +1,12 @@ +using System.CommandLine; + +namespace Nitride.Commands +{ + /// + /// An interface that indicates that the given command is a top-level + /// command instead of one that is included as a sub-command inside another. + /// + public interface ITopLevelCommand : ICommand + { + } +} diff --git a/src/Nitride/ContainerBuiltHandler.cs b/src/Nitride/ContainerBuiltHandler.cs new file mode 100644 index 0000000..a851e7e --- /dev/null +++ b/src/Nitride/ContainerBuiltHandler.cs @@ -0,0 +1,7 @@ +namespace Nitride +{ + /// + /// Describes the signature of a callback for after the container is build. + /// + public delegate void ContainerBuiltHandler(); +} diff --git a/src/Nitride/Contents/ByteArrayBinaryContent.cs b/src/Nitride/Contents/ByteArrayBinaryContent.cs new file mode 100644 index 0000000..f8430d6 --- /dev/null +++ b/src/Nitride/Contents/ByteArrayBinaryContent.cs @@ -0,0 +1,26 @@ +using System; +using System.IO; + +namespace Nitride.Contents +{ + /// + /// Implements a purely in-memory binary content provider. + /// + public class ByteArrayBinaryContent : IBinaryContent + { + private readonly byte[] buffer; + + public ByteArrayBinaryContent(byte[] buffer) + { + this.buffer = + buffer ?? throw new ArgumentNullException(nameof(buffer)); + } + + /// + public Stream GetStream() + { + // Return a memory stream that cannot be written. + return new MemoryStream(this.buffer, false); + } + } +} diff --git a/src/Nitride/Contents/EntityBinaryContentExtensions.cs b/src/Nitride/Contents/EntityBinaryContentExtensions.cs new file mode 100644 index 0000000..954c65a --- /dev/null +++ b/src/Nitride/Contents/EntityBinaryContentExtensions.cs @@ -0,0 +1,47 @@ +using Gallium; + +namespace Nitride.Contents +{ + /// + /// Various extension methods for working with binary content. + /// + public static class EntityBinaryContentExtensions + { + /// + /// Retrieves the binary content associated with this entity. + /// + /// The entity to query. + /// The binary content. + public static IBinaryContent GetBinaryContent(this Entity entity) + { + return entity.Get(); + } + + /// + /// Determines if the entity has a registered IBinaryContent component. + /// + /// The entity to inspect. + /// True if there is a component extending `IContent`. + public static bool HasBinaryContent(this Entity entity) + { + return entity.Has(); + } + + /// + /// Remove all existing content components from the entity and then adds + /// the new binary content as a component into the entity before + /// returning it. + /// + /// The entity to query and modify. + /// The content to add to the entity. + /// The base type of the content. + /// The entity or a copy with the new component. + public static Entity SetBinaryContent( + this Entity entity, + TType content) + where TType : IBinaryContent + { + return entity.SetContent(content); + } + } +} diff --git a/src/Nitride/Contents/EntityContentExtensions.cs b/src/Nitride/Contents/EntityContentExtensions.cs new file mode 100644 index 0000000..1aa8ad5 --- /dev/null +++ b/src/Nitride/Contents/EntityContentExtensions.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Gallium; + +namespace Nitride.Contents +{ + /// + /// Various extension methods for working with content. + /// + public static class EntityContentExtensions + { + /// + /// Retrieves the single registered content entity inside the component. + /// If there is more than one, this will throw an exception. + /// + /// The entity to query. + /// The one and only content component. + public static IContent GetContent(this Entity entity) + { + Type type = entity + .GetComponentTypes() + .Single(x => typeof(IContent).IsAssignableFrom(x)); + + return entity.Get(type); + } + + /// + /// Determines if the entity has at least one content component. + /// + /// The entity to inspect. + /// True if there is a component extending `IContent`. + public static bool HasContent(this Entity entity) + { + return entity + .GetComponentTypes() + .Any(x => typeof(IContent).IsAssignableFrom(x)); + } + + /// + /// Remove all existing content components from the entity and then adds + /// the new component into the entity before returning it. + /// + /// The entity to query and modify. + /// The content to add to the entity. + /// The base type of the content. + /// The entity or a copy with the new component. + public static Entity SetContent( + this Entity entity, + TType content) + where TType : IContent + { + IEnumerable? existing = entity + .GetComponentTypes() + .Where(x => typeof(IContent).IsAssignableFrom(x)); + + foreach (var component in existing) + { + entity = entity.Remove(component); + } + + return entity.Add(content); + } + } +} diff --git a/src/Nitride/Contents/EntityTextContentExtensions.cs b/src/Nitride/Contents/EntityTextContentExtensions.cs new file mode 100644 index 0000000..71f479a --- /dev/null +++ b/src/Nitride/Contents/EntityTextContentExtensions.cs @@ -0,0 +1,53 @@ +using Gallium; + +namespace Nitride.Contents +{ + /// + /// Various extension methods for working with text content. + /// + public static class EntityTextContentExtensions + { + /// + /// Determines if the entity has a registered ITextContent component. + /// + /// The entity to inspect. + /// True if there is a component extending `IContent`. + public static bool HasTextContent(this Entity entity) + { + return entity.Has(); + } + + /// + /// Remove all existing content components from the entity and then adds + /// the new text content as a component into the entity before + /// returning it. + /// + /// The entity to query and modify. + /// The content to add to the entity. + /// The base type of the content. + /// The entity or a copy with the new component. + public static Entity SetTextContent( + this Entity entity, + TType content) + where TType : ITextContent + { + return entity.SetContent(content); + } + + /// + /// Remove all existing content components from the entity and then adds + /// the new text content as a component into the entity before + /// returning it. + /// + /// The entity to query and modify. + /// The content to add to the entity. + /// The base type of the content. + /// The entity or a copy with the new component. + public static Entity SetTextContent( + this Entity entity, + string content) + { + return entity.SetTextContent(new StringTextContent(content)); + } + } +} diff --git a/src/Nitride/Contents/FileBinaryContent.cs b/src/Nitride/Contents/FileBinaryContent.cs new file mode 100644 index 0000000..8e20643 --- /dev/null +++ b/src/Nitride/Contents/FileBinaryContent.cs @@ -0,0 +1,44 @@ +using System; +using System.IO; + +namespace Nitride.Contents +{ + /// + /// A binary content provider based on a file in the file system. + /// + public class FileBinaryContent : IBinaryContent, ITextContentConvertable + { + private readonly string path; + + public FileBinaryContent(string path) + { + this.path = path ?? throw new ArgumentNullException(nameof(path)); + } + + public FileBinaryContent(FileInfo file) + { + if (file == null) + { + throw new ArgumentNullException(nameof(file)); + } + + this.path = file.FullName; + } + + /// + public Stream GetStream() + { + return File.Open( + this.path, + FileMode.Open, + FileAccess.Read, + FileShare.Read); + } + + /// + public ITextContent ToTextContent() + { + return new FileTextContent(this.path); + } + } +} diff --git a/src/Nitride/Contents/FileTextContent.cs b/src/Nitride/Contents/FileTextContent.cs new file mode 100644 index 0000000..497123d --- /dev/null +++ b/src/Nitride/Contents/FileTextContent.cs @@ -0,0 +1,47 @@ +using System; +using System.IO; +using System.Text; + +namespace Nitride.Contents +{ + /// + /// A text content provider based on a file in the file system. + /// + public class FileTextContent : ITextContent, IBinaryContentConvertable + { + private readonly string path; + + public FileTextContent(string path) + { + this.path = path ?? throw new ArgumentNullException(nameof(path)); + } + + public FileTextContent(FileInfo file) + { + if (file == null) + { + throw new ArgumentNullException(nameof(file)); + } + + this.path = file.FullName; + } + + /// + public TextReader GetReader() + { + return new StreamReader( + File.Open( + this.path, + FileMode.Open, + FileAccess.Read, + FileShare.Read), + Encoding.UTF8); + } + + /// + public IBinaryContent ToBinaryContent() + { + return new FileBinaryContent(this.path); + } + } +} diff --git a/src/Nitride/Contents/IBinaryContent.cs b/src/Nitride/Contents/IBinaryContent.cs new file mode 100644 index 0000000..f7c0147 --- /dev/null +++ b/src/Nitride/Contents/IBinaryContent.cs @@ -0,0 +1,19 @@ +using System.IO; + +namespace Nitride.Contents +{ + /// + /// Indicates a content source that works with binary data and streams. + /// + public interface IBinaryContent : IContent + { + /// + /// Opens a read-only stream into the underlying source of binary data. + /// + /// + /// It is up to the calling class to close this stream. + /// + /// A stream to the internal data. + Stream GetStream(); + } +} diff --git a/src/Nitride/Contents/IBinaryContentConvertable.cs b/src/Nitride/Contents/IBinaryContentConvertable.cs new file mode 100644 index 0000000..525cff9 --- /dev/null +++ b/src/Nitride/Contents/IBinaryContentConvertable.cs @@ -0,0 +1,15 @@ +namespace Nitride.Contents +{ + /// + /// An interface to indicates an object can be converted into a binary + /// content directly. + /// + public interface IBinaryContentConvertable + { + /// + /// Convert the given object into a binary content. + /// + /// + IBinaryContent ToBinaryContent(); + } +} diff --git a/src/Nitride/Contents/IContent.cs b/src/Nitride/Contents/IContent.cs new file mode 100644 index 0000000..4035fd9 --- /dev/null +++ b/src/Nitride/Contents/IContent.cs @@ -0,0 +1,12 @@ +namespace Nitride.Contents +{ + /// + /// Describes a content provider or source for an Entity. `IContent` is + /// also used for the mutual exclusivity login in `EntityContentExtensions` + /// where all `IContent` derived components are automatically removed when + /// setting a new content source. + /// + public interface IContent + { + } +} diff --git a/src/Nitride/Contents/ITextContent.cs b/src/Nitride/Contents/ITextContent.cs new file mode 100644 index 0000000..9a79e70 --- /dev/null +++ b/src/Nitride/Contents/ITextContent.cs @@ -0,0 +1,19 @@ +using System.IO; + +namespace Nitride.Contents +{ + /// + /// Indicates a content source that provides data as a TextReader and text. + /// + public interface ITextContent : IContent + { + /// + /// Opens a text reader into the underlying source of text data. + /// + /// + /// It is up to the calling class to close this reader. + /// + /// The text reader to the internal data. + TextReader GetReader(); + } +} diff --git a/src/Nitride/Contents/ITextContentConvertable.cs b/src/Nitride/Contents/ITextContentConvertable.cs new file mode 100644 index 0000000..e7f1fad --- /dev/null +++ b/src/Nitride/Contents/ITextContentConvertable.cs @@ -0,0 +1,15 @@ +namespace Nitride.Contents +{ + /// + /// An interface to indicates an object can be converted into a text + /// content directly. + /// + public interface ITextContentConvertable + { + /// + /// Convert the given object into a text content. + /// + /// + ITextContent ToTextContent(); + } +} diff --git a/src/Nitride/Contents/README.md b/src/Nitride/Contents/README.md new file mode 100644 index 0000000..fe29826 --- /dev/null +++ b/src/Nitride/Contents/README.md @@ -0,0 +1,186 @@ +# Contents + +The contents of a file may be stored as a component, however this increases the +memory pressure when Nitride works with hundreds or thousands of files. To +reduce this, memory is loaded on demand through an abstracted components such +as `IBinaryContent` and `ITextContent`. + +## Exclusivity + +The content interfaces are treated as *mutually exclusive* with each other by +extending the `IContent` interface. The intended reason for this is to have a +single source of data that doesn't go out of sync. Adding or setting +a `IBinaryContent` will remove the other content components in the entity, even +ones that are defined in other classes. In effect, all `IContent` +will be removed to ensure that. This is implemented via a number of extension +operations. + +```c# +Entity entity; +IBinaryContent binaryContent; +ITextContent textContent; + +entity.Add(binaryContent); +Assert.Equal(1, entity.Count); +Assert.True(entity.Has()); +Assert.True(entity.HasBinaryContent()); +Assert.True(entity.HasContent()); + +entity.SetContent(textContent); +Assert.Equal(1, entity.Count); +Assert.False(entity.Has()); +Assert.False(entity.HasBinaryContent()); +Assert.True(entity.Has()); +Assert.True(entity.HasTextContent()); +Assert.True(entity.HasContent()); + +entity.RemoveContent(); +Assert.Equal(0, entity.Count); +Assert.False(entity.Has()); +Assert.False(entity.HasBinaryContent()); +Assert.False(entity.Has()); +Assert.False(entity.HasTextContent()); +Assert.False(entity.HasContent()); +``` + +This exclusivity can be ignored by using direct `Add<>`, `Set<>`, and +`Remove<>` methods on the entity. That would allow multiple content to be added +and removed at the same time. + +```c# +Entity entity; +IBinaryContent binaryContent = entity.Get(); +using Stream binaryStream = binaryContent.GetStream(); +using StreamReader binaryReader = new StreamReader(binaryStream); +var text = binaryReader.ReadToEnd(); +var textContent = new StringTextContent(text); + +var switchedEntity = entity + .Set(textContent) + .Remove(); +Assert.Equal(1, switchedEntity.Count); +Assert.True(switchedEntity.Has()); +Assert.False(switchedEntity.Has()); + +var mergedEntity = entity + .Set(textContent); +Assert.Equal(2, switchedEntity.Count); +Assert.True(switchedEntity.Has()); +Assert.True(switchedEntity.Has()); +``` + +Implementing this with a parent interface (`IContent`) allows future content +providers, such as `IXmlContent` or other future content sources. + +## IBinaryComponent + +Most files start as only having access to binary content. + +```c# +Entity entity; +IBinaryContent content = entity.Get(); +using Stream stream = content.GetStream(); +``` + +There is no guarantee that the data behind the stream is loaded into memory or +even available on disk. Instead, the stream is good until it goes out of scope. + +### IBinaryContentConvertable + +There is an additional interface that allows a content to be converted into +an `IBinaryContent).` + +```c# +FileTextContent textContent; +IBinaryContent binaryContent = textContent.ToBinaryContent(); +``` + +### Extension Methods + +There are some convenience methods for working with entity components and +content since they are frequently used. + +```c# +IBinaryContent content = entity.GetBinaryContent(); +bool has = entity.HasBinaryContent(); + +// Internally, this uses `SetContent` above with some additonal type safety. +entity = entity.SetBinaryContent(content); +``` + +### ByteArrayBinaryComponent + +The `ByteArrayBinaryComponent` is a component that contains the entire binary +data in memory and returns it via `System.IO.MemoryStream` objects. + +```c# +byte[] bytes; +var content = new ByteArrayBinaryComponent(bytes); +``` + +### FileBinaryContent + +The `TextBinaryContent` takes a path or FileInfo object as the parameter and +will produce a stream directly from the file without loading the file into +memory. + +```c# +FileInfo file; + +new FileBinaryContent(@"C:\Temp\Bob.txt"); +new FileBinaryContent(file); +``` + +## ITextContent + +Text content allows for retrieving the contents via `TextReader` instead of a +stream. + +```c# +ITextContent content; + +using TextReader reader = content.GetReader(); +``` + +### ITextContentConvertable + +There is an additional interface that allows a content to be converted into +an `ITextContent).` + +```c# +FileBinaryContent binaryContent; +ITextContent textContent = binaryContent.ToTextContent(); +``` + +### Extension Methods + +Like the binary, there are some convenience methods for working with entity +components. + +```c# +ITextContent content = entity.GetTextContent(); +bool has = entity.HasTextContent(); + +entity = entity.SetTextContent(content); +``` + +### StringTextContent + +A simple, in-memory string text content takes a string as the parameter. + +```c# +string input; +ITextContent content = new StringTextContent(input); +``` + +### FileTextContent + +The `TextFileContent` takes a path or FileInfo object as the parameter and will +produce a reader directly from the file without loading the file into memory. + +```c# +FileInfo file; + +new FileTextContent(@"C:\Temp\Bob.txt"); +new FileTextContent(file); +``` diff --git a/src/Nitride/Contents/StringTextContent.cs b/src/Nitride/Contents/StringTextContent.cs new file mode 100644 index 0000000..770ea0c --- /dev/null +++ b/src/Nitride/Contents/StringTextContent.cs @@ -0,0 +1,37 @@ +using System; +using System.IO; +using System.Text; + +namespace Nitride.Contents +{ + /// + /// Implements a pure, in-memory text content provider that uses a string + /// as the backing store. + /// + public class StringTextContent : ITextContent + { + private readonly string buffer; + + public StringTextContent(StringBuilder buffer) + : this(buffer.ToString()) + { + } + + public StringTextContent(string buffer) + { + this.buffer = + buffer ?? throw new ArgumentNullException(nameof(buffer)); + } + + public StringTextContent(params string[] lines) + : this(string.Join("\n", lines)) + { + } + + /// + public TextReader GetReader() + { + return new StringReader(this.buffer); + } + } +} diff --git a/src/Nitride/Contents/TextContentExtensions.cs b/src/Nitride/Contents/TextContentExtensions.cs new file mode 100644 index 0000000..133c52e --- /dev/null +++ b/src/Nitride/Contents/TextContentExtensions.cs @@ -0,0 +1,22 @@ +using System.IO; + +namespace Nitride.Contents +{ + public static class TextContentExtensions + { + public static string GetText(this ITextContent content) + { + var writer = new StringWriter(); + using TextReader reader = content.GetReader(); + string? line = reader.ReadLine(); + + while (line != null) + { + writer.WriteLine(line); + line = reader.ReadLine(); + } + + return writer.ToString(); + } + } +} diff --git a/src/Nitride/Entities/CreateIndexEntities.cs b/src/Nitride/Entities/CreateIndexEntities.cs new file mode 100644 index 0000000..61dd6c2 --- /dev/null +++ b/src/Nitride/Entities/CreateIndexEntities.cs @@ -0,0 +1,181 @@ +using System; +using System.Collections.Generic; +using Gallium; +using Serilog; + +namespace Nitride.Entities +{ + /// + /// A Nitride operation that creates and merges entities that are intended + /// to be indexes of another entity. For example, this could be year and + /// month archive pages, tag or category pages. Support is given for + /// merging existing pages so a description could be written from a file + /// and then the index logic is automatically added. + /// + public partial class CreateIndexEntities : NitrideOperationBase + where TIndexKey : notnull + { + private readonly ILogger logger; + + public CreateIndexEntities(ILogger logger) + { + this.logger = logger.ForContext(typeof(CreateIndexEntities<>)); + } + + /// + /// Creates an index for a given key. This will not be called for any + /// index that has been already created. + /// + public Func, Entity>? CreateIndexEntity + { + get; + set; + } + + /// + /// Gets or sets the function to retrieve the key from an existing + /// index page. If this returns null, then the entity is considered not + /// to be an index page. + /// + public Func? GetIndexEntityKey { get; set; } + + /// + /// A method that gets the keys for a given entity. If this returns an + /// empty list, then the entity will not added to an index. + /// + public Func>? GetIndexKeys { get; set; } + + /// + /// Updates an existing index entity to include new information. + /// + public Func, Entity>? UpdateIndexEntity + { + get; + set; + } + + /// + public override IEnumerable Run(IEnumerable input) + { + // Make sure we have sane data. + this.CheckNotNull( + nameof(this.CreateIndexEntity), + this.CreateIndexEntity); + this.CheckNotNull( + nameof(this.GetIndexKeys), + this.GetIndexKeys); + this.CheckNotNull( + nameof(this.UpdateIndexEntity), + this.UpdateIndexEntity); + + // We need to process two lists out of the output, so we need to put + // it into a list so we can enumerate through it twice. This will + // also cause the output to be reordered. + Dictionary indexes = new(); + Dictionary> indexed = new(); + List results = new(); + + foreach (var entity in input) + { + // See if we are an index page first. + if (this.GetIndexEntityKey != null) + { + TIndexKey? indexKey = this.GetIndexEntityKey(entity); + + if (indexKey != null) + { + indexes[indexKey] = entity; + continue; + } + } + + // We aren't an index, so check to see if this entity is + // something to be indexed. + foreach (TIndexKey indexedKey in this.GetIndexKeys!(entity)) + { + if (!indexed.TryGetValue(indexedKey, out var list)) + { + indexed[indexedKey] = list = new List(); + } + + list.Add(entity); + } + + // Add to the non-index page list. + results.Add(entity); + } + + // Go through all the index pages and update them. We get a list of + // all the pages in the index and pass them into the function to + // update the existing index. Then we update the entity and add it + // to the bottom of the results list. + foreach ((TIndexKey key, var oldIndex) in indexes) + { + if (!indexed.TryGetValue(key, out var list)) + { + list = new List(); + } + + Entity newEntity = this.UpdateIndexEntity!(oldIndex, key, list); + + results.Add(newEntity); + } + + // Go through all the known index keys and create the missing pages. + int created = 0; + + foreach ((TIndexKey key, var list) in indexed) + { + // See if we already have a page, if we do, then we've already + // processed that page and don't have to do anything. + if (indexes.ContainsKey(key)) + { + continue; + } + + // We don't have that page and need to add it to the list. + Entity entity = this.CreateIndexEntity!(key, list); + + created++; + results.Add(entity); + } + + // Return the combined together version. + this.logger.Debug( + "Found {Old:N0} and created {New:N0} index pages for {Keys:N0} keys", + indexes.Count, + created, + indexed.Count); + + return results; + } + + public CreateIndexEntities WithCreateIndexEntity( + Func, Entity>? callback) + { + this.CreateIndexEntity = callback; + return this; + } + + public CreateIndexEntities WithGetIndexEntityKey( + Func? callback) + { + this.GetIndexEntityKey = callback; + return this; + } + + public CreateIndexEntities WithGetIndexKeys( + Func>? callback) + { + this.GetIndexKeys = callback; + return this; + } + + public CreateIndexEntities WithUpdateIndexEntity( + Func, Entity>? callback) + { + this.UpdateIndexEntity = callback; + return this; + } + } +} diff --git a/src/Nitride/Entities/LinkPreviousNextHandler.cs b/src/Nitride/Entities/LinkPreviousNextHandler.cs new file mode 100644 index 0000000..3e01fc2 --- /dev/null +++ b/src/Nitride/Entities/LinkPreviousNextHandler.cs @@ -0,0 +1,9 @@ +using Gallium; + +namespace Nitride.Entities +{ + public delegate Entity LinkPreviousNextHandler( + Entity entity, + Entity? previous, + Entity? next); +} diff --git a/src/Nitride/Entities/LinkSerialEntities.cs b/src/Nitride/Entities/LinkSerialEntities.cs new file mode 100644 index 0000000..16e0988 --- /dev/null +++ b/src/Nitride/Entities/LinkSerialEntities.cs @@ -0,0 +1,42 @@ +using System.Collections.Generic; +using System.Linq; +using Gallium; + +namespace Nitride.Entities +{ + /// + /// Links a series of entities together in some manner. This assumes that + /// the entities coming into the operation are already ordered. + /// + [WithProperties] + public partial class LinkSerialEntities : NitrideOperationBase + { + /// + /// The callback function that sets the previous and next + /// + public LinkPreviousNextHandler? UpdatePreviousNext { get; set; } + + /// + public override IEnumerable Run(IEnumerable input) + { + // We pull all the entities into a list so we can walk through them + // more than once along with index access. + var list = input.ToList(); + + // Go through the list and assign each entity in order. + this.CheckNotNull(x => this.UpdatePreviousNext); + + for (int i = 0; i < list.Count; i++) + { + Entity entity = list[i]; + Entity? previous = i > 0 ? list[i - 1] : null; + Entity? next = i < list.Count - 1 ? list[i + 1] : null; + + list[i] = this.UpdatePreviousNext!(entity, previous, next); + } + + // Return the resulting list. + return list; + } + } +} diff --git a/src/Nitride/Entities/README.md b/src/Nitride/Entities/README.md new file mode 100644 index 0000000..17f320b --- /dev/null +++ b/src/Nitride/Entities/README.md @@ -0,0 +1,188 @@ +# Entities + +Nitride is based on an Entity Component System (ECS) in the way it handles the +various input documents, images, feeds, and other parts that make up a website. +Implementing it this way makes it easier to create a distinction between the +different entities (much like Statiq.Web uses the DocumentType) +but allows for adding new components along the way without having the C# +limitations of a sealed enumeration or needing to implement a Javascript-style +enum for identification. Instead, if an entity needs to be identified as being +Markdown, an image, or a database query, it just adds a component to represent +that information. + +The basic entity is just a simple object with an internal identifier. These +entities are also immutable. Functions that appear to manipulate actually clone, +make the change, and then return the results. + +```c# +var entity = new Entity(); +Console.WriteLine("Entity Id: {0}", entity.Id); +``` + +## Components + +By itself, an entity doesn't have any meaning or purpose. These are described by +a generic collection of components that are added to the entity. Each component +has a type and then an instance of that type. These are added to the entity with +the `Add` command. If a type is not given, it is assumed to be the same type as +the parameter, but a base class or interface can be given to allow different +types to be stored in a specific component. + +In effect, the type of the component is the key. Having two different types, +even with the same object, would be considered two distinct objects. + +```c# +string mimeType = "text/plain"; +Entity entity = new Entity(); +Assert.Equal(0, entity.Count); + +Entity newEntity = entity.Add(mimeType); +Assert.Equal(0, entity.Count); +Assert.Equal(1, newEntity.Count); +Assert.Equal(entity.Id, newEntity.Id); +Assert.Equal(entity, newEntity); + +newEntity = newEntity.Add(mimeType); +Assert.Equal(2, newEntity.Count); +``` + +The basic operations for entity components are: + +- `Add(component)`: Adds a component as the given type. If there is + already a component there, an exception will be thrown. +- `Add(component)`: As `Add(component)` but the `TType` is the same as + `component.GetType()`. +- `Remove()`: Removes any component of the given type, if exists. If + there is no such component, then nothing happens. +- `Remove(component)`: Same as `Remove` with the component given + determining the `TType`. +- `Set(component)`: Adds or updates a component of the given type. +- `Set(component)`: Same as `Set` with the component given determining + the `TType`. +- `Copy()`: Creates a copy of the entity and assigns it a new identifier. +- `ExactCopy()`: Creates a copy of the entity with the same identifier. + +As above, all of these return a new entity (or the same one if no change is made +to the entity). + +### Query Components + +- `bool Has()`: Returns a value indicating whether the entity has the + given component type. +- `TType Get()`: Returns the value of the registered component. If there + is no such object, this will throw an exception. +- `TType? GetOptional()`: Returns the value of the registered component. + If there is no such object, this will return the default value. +- `bool TryGet(out TType component)`: Attempt to get the component. If it + cannot be retrieved, then this will return `false` and `component` is + undefined. + +## Collections + +To keep with the patterns of C#, working with collection of entities uses normal +LINQ operations. For example, to combine two sets of entities together, +the `Union` LINQ command can be used: + +```c# +IEnumerable entities1; +IEnumerable entities2; +IEnumerable all = entities1.Union(entities2); +``` + +To work with the ECS, additional extension methods have been written that allow +for filtering or working with those entities. + +### HasComponents + +The `HasComponents` is a set of overrides that checks to see if the given entity +has the requisite components. If they don't, then that entity is filtered out. + +```c# +IEnumerable entities; + +var filtered1 = entities.HasComponents(); +var filtered2 = entities.HasComponents(); +var filtered3 = entities.HasComponents(); +``` + +### NotComponents + +`NotComponents` is effectively the reverse of `HasComponents` in that if the +entity has the given components, they are filtered out. This also allows up to +three different components. + +This also allows the developer to ask for an entity that has two components but +not have a different of two with: + +```c# +IEnumerable entities; +var filtered = entities + .HasComponents() + .NotComonents(); +``` + +### ForComponents + +`ForComponents` allows for a lambda to be performed on entities that have the +given components while passing all the entities on through the function. This is +much like the `ForEach` combined with `Select` in that the changed or updated +entity will be passed on. + +```c# +var entities = new Entities[] +{ + new Entity().Add("value1"), + new Entity().Add(2), + new Entity().Add(3).Add("value2"), +}; +var filtered = entities + .ForComponents((entity, value) => entity.Set(value + "!")); + +Assert.Equal( + new[] { + "value1!", + null, + "value2!", + }, + filtered.Select(x => x.GetOptional())); +``` + +There are also three overloads allowing up to three components to be pulled out +with the lambda. + +### SetComponents, AddComponents, RemoveComponents + +`SetComponents` (as the corresponding `AddComponents`, and `RemoveComponents`) +basically perform the same operation on the entire list. They also have the +three overloads to allow one to three components be manipulated in a single +call. + +```c# +IEnumerable entities; +var updated = entities + .AddComponents(mimeType) + .AddComponents(mimeType) + .RemoveComponents() + .SetComponents(mimeType) + .SetComponent(mimeType); +``` + +### MergeEntities + +`MergeComponents` combines multiple entities together if they have the same `Id` +field. The two sides of the comparison are the presence of a specific component. + +```c# +IEnumerable entities; +var combined = entities + .MergeEntities( + (entity1, c1, entity2, c2) => entity1.Set(c2)); +``` + +## Files, Paths, and Content + +Entities do not have an integral concept of being a file or having contents from +the disk or anywhere else. Much of this is implemented as components from the +Nitride.IO assembly which uses [Zio](https://github.com/xoofx/zio) +for the underlying library, but can be easily replaced with a different IO +layer (or even the straight System.IO). diff --git a/src/Nitride/ExpressionHelper.cs b/src/Nitride/ExpressionHelper.cs new file mode 100644 index 0000000..96be6bc --- /dev/null +++ b/src/Nitride/ExpressionHelper.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; + +namespace Nitride +{ + /// + /// Helper methods for getting the names of properties. + /// + public static class ExpressionHelper + { + public static string GetMemberName( + Expression> expression) + { + return GetMemberName(expression.Body); + } + + public static IEnumerable GetMemberNames( + params Expression>[] expressions) + { + return expressions + .Select(x => GetMemberName(x.Body)); + } + + public static IEnumerable GetMemberNames( + this T instance, + params Expression>[] expressions) + { + return GetMemberNames(expressions); + } + + private static string GetMemberName(Expression expression) + { + if (expression == null) + { + throw new ArgumentException(nameof(expression)); + } + + if (expression is MemberExpression memberExpression) + { + return memberExpression.Member.Name; + } + + if (expression is MethodCallExpression methodCallExpression) + { + return methodCallExpression.Method.Name; + } + + if (expression is UnaryExpression unaryExpression) + { + return GetMemberName(unaryExpression); + } + + throw new ArgumentException(nameof(expression)); + } + + private static string GetMemberName(UnaryExpression unaryExpression) + { + if (unaryExpression.Operand is MethodCallExpression + methodCallExpression) + { + return methodCallExpression.Method.Name; + } + + return ((MemberExpression)unaryExpression.Operand).Member.Name; + } + } +} diff --git a/src/Nitride/INitrideOperation.cs b/src/Nitride/INitrideOperation.cs new file mode 100644 index 0000000..c1d83a7 --- /dev/null +++ b/src/Nitride/INitrideOperation.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using Gallium; + +namespace Nitride +{ + public interface INitrideOperation + { + /// + /// Runs the input entities through the operation and returns the results. + /// + /// + /// + IEnumerable Run(IEnumerable input); + } +} diff --git a/src/Nitride/Nitride.csproj b/src/Nitride/Nitride.csproj new file mode 100644 index 0000000..1c4e8d7 --- /dev/null +++ b/src/Nitride/Nitride.csproj @@ -0,0 +1,40 @@ + + + + net5.0 + enable + + + + A static site generator written using Gallium ECS. + + + + + + + + + + + + + + + + + + + + + True + + + + + Analyzer + False + + + + diff --git a/src/Nitride/NitrideBuilder.cs b/src/Nitride/NitrideBuilder.cs new file mode 100644 index 0000000..00dc5c7 --- /dev/null +++ b/src/Nitride/NitrideBuilder.cs @@ -0,0 +1,196 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using Autofac; +using Autofac.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Serilog; +using Zio; +using Zio.FileSystems; + +namespace Nitride +{ + /// + /// A class that implements a builder pattern for gathering all the + /// components of a static website. Once everything is build, then calling + /// `Build()` will generate the resulting website. + /// + public class NitrideBuilder + { + private readonly string[] arguments; + + private readonly List> + configureContainerCallbacks; + + /// + /// An event that is called after the container is built but before + /// the application runs. + /// + private readonly List> + configureSiteCallbacks; + + private NitrideLoggingBuilder loggingBuilder; + + public NitrideBuilder(string[] arguments) + { + this.arguments = arguments; + this.loggingBuilder = new NitrideLoggingBuilder(); + this.configureSiteCallbacks = + new List>(); + this.configureContainerCallbacks = + new List>(); + } + + /// + /// Gets or sets the builder used to set up logging. + /// + /// + public NitrideLoggingBuilder LoggingBuilder + { + get => this.loggingBuilder; + set => this.loggingBuilder = + value ?? throw new ArgumentNullException(nameof(value)); + } + + /// + /// Gets or sets the input directory to automatically register as the + /// source of the files. If this is not set, then no Zio.IFileSystem + /// will be registered and it will have to be done manually. + /// + public DirectoryInfo? RootDirectory { get; set; } + + /// + /// Generates or builds the resulting website based on the given + /// command. + /// + /// The task once completed. + public async Task BuildAsync() + { + await Host + .CreateDefaultBuilder(this.arguments) + .UseSerilog() + .UseServiceProviderFactory(new AutofacServiceProviderFactory()) + .ConfigureServices(this.ConfigureServices) + .ConfigureContainer(this.ConfigureContainer) + .RunConsoleAsync(); + + return 0; + } + + /// + /// Allows for configuration of the Autofac container to register + /// additional types, pipelines, and modules. + /// + /// The callback to configure the container. + /// The builder to chain operations. + public NitrideBuilder ConfigureContainer( + Action callback) + { + this.configureContainerCallbacks.Add(callback); + return this; + } + + /// + /// Overrides the logging configuration for customization. + /// + /// + /// The builder to chain methods. + public NitrideBuilder ConfigureLogging( + Func callback) + { + this.loggingBuilder.Callback = callback; + return this; + } + + /// + /// Registers a callback to be called after the container is built but + /// before the application runs. + /// + /// The callback to register. + /// The builder for chaining. + public NitrideBuilder ConfigureSite( + Action callback) + { + this.configureSiteCallbacks.Add(callback); + return this; + } + + /// + /// Sets the root directory to a common value by creating a + /// IFileSystem (from Zio) of the root directory and registering it. + /// This will be used for both input and output. + /// + /// + /// The path to the directory that represents "/" while + /// building. + /// + /// The builder to chain calls. + public NitrideBuilder WithRootDirectory(DirectoryInfo directory) + { + this.RootDirectory = directory; + return this; + } + + private void ConfigureContainer(ContainerBuilder builder) + { + // We want to get logging up and running as soon as possible. We + // also hook up the logging to the process exit in an attempt to + // make sure the logger is properly flushed before exiting. + ILogger logger = this.loggingBuilder + .Setup(builder) + .ForContext(); + + AppDomain.CurrentDomain.ProcessExit += + (_, _) => Log.CloseAndFlush(); + + // Hook up the rest of the modules. + builder.RegisterModule(); + + // Set up our file system. + this.RegisterRootDirectory(logger, builder); + + // Finish up the registration by running our events. + foreach (var callback in this.configureSiteCallbacks) + { + builder.RegisterBuildCallback(scope => callback(this, scope)); + } + + foreach (var configureContainer in this.configureContainerCallbacks) + { + configureContainer.Invoke(builder); + } + } + + private void ConfigureServices(IServiceCollection services) + { + services.AddAutofac(); + services.AddHostedService(); + } + + private void RegisterRootDirectory( + ILogger logger, + ContainerBuilder builder) + { + if (this.RootDirectory == null) + { + logger.Verbose("No root directory is registered"); + return; + } + + logger.Debug( + "Setting root directory to {Path}", + this.RootDirectory.FullName); + + var rootFileSystem = new PhysicalFileSystem(); + var subFileSystem = new SubFileSystem( + rootFileSystem, + this.RootDirectory.FullName); + + builder.RegisterInstance(subFileSystem) + .As() + .SingleInstance(); + } + } +} diff --git a/src/Nitride/NitrideLoggingBuilder.cs b/src/Nitride/NitrideLoggingBuilder.cs new file mode 100644 index 0000000..53a8c66 --- /dev/null +++ b/src/Nitride/NitrideLoggingBuilder.cs @@ -0,0 +1,81 @@ +using System; +using Autofac; +using Serilog; +using Serilog.Core; +using Serilog.Events; + +namespace Nitride +{ + /// + /// The internal builder for setting up logging. + /// + public class NitrideLoggingBuilder + { + public NitrideLoggingBuilder() + { + this.LevelSwitch = new LoggingLevelSwitch(); + } + + /// + /// Used when the user uses .ConfigureLogging() to configure the logger + /// instead of overriding the function. + /// + public Func? Callback { get; set; } + + /// + /// Gets the level switch used to control the logging verbosity from + /// the command-line. + /// + public virtual LoggingLevelSwitch LevelSwitch { get; } + + /// + /// Sets up logging and registers it under Serilog.ILogger in the + /// container. + /// + /// The Autofac builder for the container. + /// The logger configured during the setup. + public virtual ILogger Setup(ContainerBuilder builder) + { + // Figure out how we are going to configure the logger. + var configuration = new LoggerConfiguration(); + + // Create the logger and register it into the builder. + ILogger logger = this.Callback?.Invoke(configuration) + ?? this.CreateLogger(configuration); + + builder + .RegisterInstance(logger) + .As() + .SingleInstance(); + builder + .RegisterInstance(this.LevelSwitch) + .AsSelf() + .SingleInstance(); + + return logger; + } + + /// + /// Creates a Serilog.ILogger and returns the resulting logger to be + /// used within the system. This is one of the more common places to + /// customize logging. + /// + /// + /// + protected virtual ILogger CreateLogger( + LoggerConfiguration configuration) + { + const string Template = + "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] " + + "({SourceContext}) {Message}" + + "{NewLine}{Exception}"; + + return configuration + .MinimumLevel.ControlledBy(this.LevelSwitch) + .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) + .Enrich.FromLogContext() + .WriteTo.Console(outputTemplate: Template) + .CreateLogger(); + } + } +} diff --git a/src/Nitride/NitrideModule.cs b/src/Nitride/NitrideModule.cs new file mode 100644 index 0000000..4677ec6 --- /dev/null +++ b/src/Nitride/NitrideModule.cs @@ -0,0 +1,29 @@ +using Autofac; +using Nitride.Entities; +using Nitride.Pipelines; + +namespace Nitride +{ + public class NitrideModule : Module + { + /// + protected override void Load(ContainerBuilder builder) + { + builder + .RegisterAssemblyTypes(this.GetType().Assembly) + .Except() + .Except() + .AsSelf() + .AsImplementedInterfaces() + .SingleInstance(); + + builder + .RegisterType() + .AsSelf(); + + builder + .RegisterGeneric(typeof(CreateIndexEntities<>)) + .As(typeof(CreateIndexEntities<>)); + } + } +} diff --git a/src/Nitride/NitrideModuleBase.cs b/src/Nitride/NitrideModuleBase.cs new file mode 100644 index 0000000..390530f --- /dev/null +++ b/src/Nitride/NitrideModuleBase.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using Autofac; +using Nitride.Registration; +using Module = Autofac.Module; + +namespace Nitride +{ + /// + /// Common functionality for Nitride modules. + /// + public abstract class NitrideModuleBase : Module + { + private static readonly HashSet Initialized = new(); + + /// + protected sealed override void Load(ContainerBuilder builder) + { + // We need to allow a module to be registered multiple times because + // some modules will force inclusion of their requirements which the + // user may also add. + Type type = this.GetType(); + Assembly assembly = type.Assembly; + string typeKey = type.FullName!; + + if (Initialized.Contains(typeKey)) + { + return; + } + + Initialized.Add(typeKey); + + // Register the common types. + builder + .RegisterAssemblyTypes(assembly) + .Where(CanRegister) + .Where(IsNotSingleton) + .AsSelf() + .AsImplementedInterfaces(); + + builder + .RegisterAssemblyTypes(assembly) + .Where(CanRegister) + .Where(IsSingleton) + .AsSelf() + .AsImplementedInterfaces() + .SingleInstance(); + + this.Register(builder); + } + + /// + /// Perform additional registrations after the common one. + /// + /// The builder to configure. + protected virtual void Register(ContainerBuilder builder) + { + } + + private static bool CanRegister(Type x) + { + return x.GetCustomAttribute() == null; + } + + private static bool IsNotSingleton(Type x) + { + return x.GetCustomAttribute() == null; + } + + private static bool IsSingleton(Type x) + { + return !IsNotSingleton(x); + } + } +} diff --git a/src/Nitride/NitrideOperationBase.cs b/src/Nitride/NitrideOperationBase.cs new file mode 100644 index 0000000..29a8074 --- /dev/null +++ b/src/Nitride/NitrideOperationBase.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using Gallium; + +namespace Nitride +{ + /// + /// Contains common functionality useful for Nitride operations. + /// + public abstract class NitrideOperationBase : INitrideOperation + { + /// + /// A method that makes sure a property is set with a useful message. + /// If the value is not null, then no exception is thrown. + /// + /// The name of the property. + /// The object to check for null. + /// Thrown if the parameter is null. + public void CheckNotNull(string name, object? parameter) + { + if (parameter == null) + { + throw new InvalidOperationException( + this.GetType().Name + + " did not have the " + + name + + " property set before calling Run(). Set this via the " + + name + + "property or by calling Set" + + name + + "."); + } + } + + /// + public abstract IEnumerable Run(IEnumerable input); + } +} diff --git a/src/Nitride/NitrideOperationBaseExtensions.cs b/src/Nitride/NitrideOperationBaseExtensions.cs new file mode 100644 index 0000000..3949852 --- /dev/null +++ b/src/Nitride/NitrideOperationBaseExtensions.cs @@ -0,0 +1,21 @@ +using System; +using System.Linq.Expressions; + +namespace Nitride +{ + public static class NitrideOperationBaseExtensions + { + public static void CheckNotNull( + this TType instance, + Expression> expression) + where TType : NitrideOperationBase + { + // We can't implement this inside the class because of C# + // limitations. + string name = ExpressionHelper.GetMemberName(expression); + object? value = expression.Compile()(instance); + + instance.CheckNotNull(name, value); + } + } +} diff --git a/src/Nitride/NitrideOperationExtensions.cs b/src/Nitride/NitrideOperationExtensions.cs new file mode 100644 index 0000000..080e609 --- /dev/null +++ b/src/Nitride/NitrideOperationExtensions.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using Gallium; + +namespace Nitride +{ + /// + /// Extension methods to run a Nitride operation inline with code. + /// + public static class NitrideOperationExtensions + { + /// + /// Runs the given configured operation against the input and returns + /// the results. + /// + /// The entities to perform the operation against. + /// The operation to run. + /// The results of the operation. + public static IEnumerable Run( + this IEnumerable input, + INitrideOperation operation) + { + return operation.Run(input); + } + } +} diff --git a/src/Nitride/NitrideService.cs b/src/Nitride/NitrideService.cs new file mode 100644 index 0000000..f5c1723 --- /dev/null +++ b/src/Nitride/NitrideService.cs @@ -0,0 +1,120 @@ +using System; +using System.Collections.Generic; +using System.CommandLine; +using System.CommandLine.Parsing; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Hosting; +using Nitride.Commands; +using Serilog; +using Serilog.Core; +using Serilog.Events; + +namespace Nitride +{ + /// + /// Implements the command-line shell for generating the static site. + /// + public class NitrideService : IHostedService + { + private readonly IList commands; + + private readonly IHostApplicationLifetime lifetime; + + private readonly Option logEventLevelOption; + + private readonly ILogger logger; + + private readonly LoggingLevelSwitch loggingLevelSwitch; + + private int exitCode; + + public NitrideService( + ILogger logger, + IHostApplicationLifetime lifetime, + IList commands, + LoggingLevelSwitch loggingLevelSwitch) + { + this.lifetime = lifetime; + this.commands = commands; + this.loggingLevelSwitch = loggingLevelSwitch; + this.logger = logger.ForContext(); + + this.logEventLevelOption = new Option( + "--log-level", + () => LogEventLevel.Information, + "Controls the verbosity of the output"); + } + + /// + public Task StartAsync(CancellationToken cancellationToken) + { + this.lifetime.ApplicationStarted + .Register( + () => + { + Task.Run( + async () => { await this.RunAsync(); }, + cancellationToken); + }); + + return Task.CompletedTask; + } + + /// + public Task StopAsync(CancellationToken cancellationToken) + { + return Task.CompletedTask; + } + + private RootCommand CreateRootCommand() + { + // Create the root command and add in the top-level commands + // underneath it. + var root = new RootCommand(); + + foreach (var command in this.commands) + { + root.AddCommand((Command)command); + } + + // Add the universal options. + root.AddGlobalOption(this.logEventLevelOption); + + // Return the resulting container. + return root; + } + + private async Task RunAsync() + { + try + { + // Build the command tree. + RootCommand root = this.CreateRootCommand(); + string[] args = Environment.GetCommandLineArgs(); + + // Parse the results so we can pull out our globals. + ParseResult results = root.Parse(args); + LogEventLevel logEventLevel = results + .ValueForOption(this.logEventLevelOption); + + this.loggingLevelSwitch.MinimumLevel = logEventLevel; + + // Execute the command. + this.logger.Verbose("Running the command-line arguments"); + this.exitCode = await root.InvokeAsync(args); + } + catch (Exception exception) + { + this.logger.Error( + exception, + "Unhandled exception!"); + } + finally + { + // Stop the application once the work is done. + this.lifetime.StopApplication(); + } + } + } +} diff --git a/src/Nitride/Pipelines/IPipeline.cs b/src/Nitride/Pipelines/IPipeline.cs new file mode 100644 index 0000000..8dc069e --- /dev/null +++ b/src/Nitride/Pipelines/IPipeline.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Gallium; + +namespace Nitride.Pipelines +{ + /// + /// Implements the basic signature for a pipeline, a distinct unit of + /// processing that reads, manipulates, and writes data. + /// + public interface IPipeline + { + /// + /// Gets the dependencies for the pipeline. + /// + IEnumerable GetDependencies(); + + /// + /// Performs various operations such as reading, writing, and + /// transformation on the given entities before returning a new + /// collection, which may or may not include some of the original + /// entities. + /// + /// The entities to process. + /// The resulting entities after the process runs. + Task> RunAsync(IEnumerable entities); + } +} diff --git a/src/Nitride/Pipelines/PipelineBase.cs b/src/Nitride/Pipelines/PipelineBase.cs new file mode 100644 index 0000000..889fce7 --- /dev/null +++ b/src/Nitride/Pipelines/PipelineBase.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Gallium; + +namespace Nitride.Pipelines +{ + /// + /// A basic pipeline that is configured through properties and methods. + /// + public abstract class PipelineBase : IPipeline + { + private readonly List dependencies; + + protected PipelineBase() + { + this.dependencies = new List(); + } + + public PipelineBase AddDependency(IPipeline pipeline) + { + this.dependencies.Add(pipeline); + return this; + } + + /// + public IEnumerable GetDependencies() + { + return this.dependencies; + } + + /// + public abstract Task> RunAsync( + IEnumerable entities); + + /// + public override string ToString() + { + return this.GetType().Name; + } + } +} diff --git a/src/Nitride/Pipelines/PipelineManager.cs b/src/Nitride/Pipelines/PipelineManager.cs new file mode 100644 index 0000000..5cf2974 --- /dev/null +++ b/src/Nitride/Pipelines/PipelineManager.cs @@ -0,0 +1,184 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Humanizer; +using Serilog; + +namespace Nitride.Pipelines +{ + /// + /// A manager class for all of the pipelines. This class is responsible for + /// hooking everything up and handling the ordering of pipelines as they + /// are run. + /// + public class PipelineManager + { + private readonly PipelineRunner.Factory createEntry; + + private readonly ILogger logger; + + private List entries; + + private bool isSetup; + + private ICollection pipelines; + + public PipelineManager( + ILogger logger, + IEnumerable pipelines, + PipelineRunner.Factory createEntry) + { + this.createEntry = createEntry; + this.logger = logger.ForContext(); + this.pipelines = new HashSet(pipelines); + this.entries = null!; + } + + public ICollection Pipelines + { + get => this.pipelines; + set => this.pipelines = + value ?? throw new ArgumentNullException(nameof(value)); + } + + /// + /// Runs all of the pipelines in the appropriate order while running + /// across multiple threads. + /// + /// A task with zero for success or otherwise an error code. + public Task RunAsync() + { + // Make sure everything is setup. + DateTime started = DateTime.UtcNow; + + if (!this.Setup()) + { + return Task.FromResult(1); + } + + // Go through all the entries and start each one. We gather the + // resulting tasks and then wait for all of them to end. + this.logger.Verbose( + "Starting {Count:l}", + "pipeline".ToQuantity(this.pipelines.Count)); + + Task[] tasks = this.entries + .Select(x => Task.Run(async () => await x.RunAsync())) + .ToArray(); + TimeSpan report = TimeSpan.FromSeconds(15); + + while (!Task.WaitAll(tasks, report)) + { + List? waiting = this + .entries.Where(x => !x.IsFinished) + .ToList(); + + this.logger.Debug( + "Waiting for {Count:l} to finish running", + "pipeline".ToQuantity(waiting.Count)); + + IOrderedEnumerable< + IGrouping> states = + waiting + .GroupBy(x => x.State, x => x) + .OrderBy(x => (int)x.Key); + + foreach (var state in states) + { + List statePipelines = state + .OrderBy(x => x.Pipeline.ToString()) + .ToList(); + + this.logger.Verbose( + "Waiting for {Count:l} in {State}: {List:l}", + "pipeline".ToQuantity(statePipelines.Count), + state.Key, + string.Join( + ", ", + state.Key == PipelineRunnerState.Started + ? statePipelines + .Select( + x => + $"{x.Pipeline} ({x.ElapsedFromState})") + : statePipelines + .Select(x => x.Pipeline.ToString()))); + } + } + + // Figure out our return code. + bool hasErrors = this.entries + .Any(x => x.State == PipelineRunnerState.Errored); + + this.logger.Information( + "Completed in {Elapsed}", + DateTime.UtcNow - started); + + return Task.FromResult(hasErrors ? 2 : 0); + } + + /// + /// Performs the final initialization and preparation for the pipelines + /// and get them ready for deploying. + /// + private bool Setup() + { + // If we've already set up ourselves, then we do nothing. + if (this.isSetup) + { + return true; + } + + // If we don't have any pipelines, then we can't process. + if (this.pipelines.Count == 0) + { + this.logger.Error( + "There are no registered pipelines run, use" + + " ConfigureContainer to include IPipeline instances"); + + return false; + } + + this.logger.Verbose( + "Setting up {Count:l}", + "pipeline".ToQuantity(this.pipelines.Count)); + + // Wrap all the pipelines into entries. We do this before the next + // step so we can have the entries depend on the entries. + this.entries = this.pipelines + .Select(x => this.createEntry(x)) + .ToList(); + + // Go through and connect the pipelines together. + foreach (var entry in this.entries) + { + List dependencies = entry.Pipeline + .GetDependencies() + .ToList(); + + foreach (var dependency in dependencies) + { + // Get the entry for the dependency. + PipelineRunner dependencyPipeline = this.entries + .Single(x => x.Pipeline == dependency); + + // Set up the bi-directional connection. + entry.Incoming.Add(dependencyPipeline); + dependencyPipeline.Outgoing.Add(entry); + } + } + + // Loop through all the entries and tell them we are done providing + // and they can set up internal threads other structures. + foreach (var entry in this.entries) + { + entry.Initialize(); + } + + // We have run successfully. + this.isSetup = true; + + return true; + } + } +} diff --git a/src/Nitride/Pipelines/PipelineRunner.cs b/src/Nitride/Pipelines/PipelineRunner.cs new file mode 100644 index 0000000..c938aea --- /dev/null +++ b/src/Nitride/Pipelines/PipelineRunner.cs @@ -0,0 +1,376 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Gallium; +using Humanizer; +using Serilog; + +namespace Nitride.Pipelines +{ + /// + /// A wrapper class to handle a pipeline along with various methods for + /// tracking the operation of the pipeline and thread coordination. + /// + /// + /// This is intended to only be used within a PipelineManager and there + /// should be little reason to use or extend this class. + /// + public class PipelineRunner + { + /// + /// The manual reset event used to coordinate thread operations. + /// + private readonly ManualResetEventSlim blockDependencies; + + /// + /// A manual reset event to tell the thread when consumers are done. + /// + private readonly ManualResetEventSlim consumersDone; + + private readonly ILogger logger; + + private DateTime changed; + + private bool signaledDoneWithInputs; + + private DateTime started; + + /// + /// Contains the number of consumers we're currently waiting to finish + /// processing. + /// + private int waitingOnConsumers; + + public PipelineRunner( + ILogger logger, + IPipeline pipeline) + { + this.Pipeline = pipeline + ?? throw new ArgumentNullException(nameof(pipeline)); + this.Incoming = new List(); + this.Outgoing = new List(); + this.Outputs = new List(); + this.logger = logger.ForContext(); + this.blockDependencies = new ManualResetEventSlim(false); + this.consumersDone = new ManualResetEventSlim(false); + this.started = DateTime.Now; + this.changed = DateTime.Now; + } + + /// + /// Public factory method for creating a new pipeline manager entry. + /// + public delegate PipelineRunner Factory(IPipeline pipeline); + + public TimeSpan ElapsedFromInitialized => DateTime.Now - this.started; + + public TimeSpan ElapsedFromState => DateTime.Now - this.changed; + + /// + /// A collection of incoming entries that produce data for the pipeline. + /// + public ICollection Incoming { get; } + + /// + /// Gets a value indicating whether the output of this entry is not + /// used by any other pipeline. + /// + public bool IsFinal => this.Outgoing.Count == 0; + + /// + /// Gets a value indicating whether this pipeline is done running. + /// + public bool IsFinished => this.State + is PipelineRunnerState.Finalized + or PipelineRunnerState.Errored; + + /// + /// Gets a value indicating whether this entry is a starting one + /// that consumes no data. + /// + public bool IsStarting => this.Incoming.Count == 0; + + /// + /// The collection of outgoing entries that consume the results of + /// the pipeline in this entry. + /// + public ICollection Outgoing { get; } + + /// + /// Contains the list of all the outputs from this pipeline. This is + /// only ensured to be valid after the pipeline is in the `Providing` + /// state. + /// + public List Outputs { get; } + + /// + /// The pipeline associated with the entry. + /// + public IPipeline Pipeline { get; } + + /// + /// Gets the current state of the pipeline. + /// + public PipelineRunnerState State { get; private set; } + + /// + /// A method that tells the pipeline that one of the dependencies has + /// completed consuming the input. + /// + public void ConsumerDoneWithOutputs() + { + int current = Interlocked.Decrement(ref this.waitingOnConsumers); + + this.logger.Verbose( + "{Pipeline:l}: Consumer signalled, waiting for {Count:n0}", + this.Pipeline, + current); + + if (current == 0) + { + this.consumersDone.Set(); + } + } + + /// + /// Initializes the runner after all external properties have been + /// set and configured. + /// + public void Initialize() + { + this.ChangeState(PipelineRunnerState.Initialized); + } + + /// + /// Executes the pipeline, including waiting for any or all + /// dependencies. + /// + public async Task RunAsync() + { + try + { + // Make sure we have a valid state. + switch (this.State) + { + case PipelineRunnerState.Initialized: + case PipelineRunnerState.Finalized: + break; + + default: + this.logger.Error( + "{Pipeline:l}: Pipeline cannot be started in a {State}" + + " state (not Initialized or Finalized)", + this.Pipeline, + this.State); + break; + } + + // Prepare ourselves for running. We have a start/stop state because + // this may be non-zero time. + this.started = DateTime.Now; + this.changed = DateTime.Now; + this.ChangeState(PipelineRunnerState.Preparing); + this.signaledDoneWithInputs = false; + this.ChangeState(PipelineRunnerState.Prepared); + + // Go through the incoming and wait for each of the manual resets + // on the dependency pipelines. + if (this.WaitForDependencies()) + { + this.SignalDoneWithInputs(); + return; + } + + // Grab the outputs from the incoming. They will be populated + // because we have waited for the reset events. + this.ChangeState(PipelineRunnerState.Started); + List input = this.GatherDependencyOutputs(); + + // Run the pipeline. This may not be resolved until we gather + // the output below. + await this.RunPipeline(input); + + // At this point, we are completely done with our inputs, so signal + // to them in case they have to clean up any of their structures. + this.SignalDoneWithInputs(); + + // If we have outgoing runners, provide them data until they are + // done. + this.SendToDependants(); + + // Finalize ourselves. + this.ChangeState(PipelineRunnerState.Finalized); + } + catch (Exception exception) + { + // Report the exception. + this.logger.Error( + exception, + "{Pipeline:l}: There was an exception running pipeline", + this.Pipeline); + + // Change our state and then release any pipeline waiting for us + // so they can pick up the error and fail themselves. + this.ChangeState(PipelineRunnerState.Errored); + this.blockDependencies.Set(); + this.SignalDoneWithInputs(); + } + } + + /// + /// A method to block the call until this runner is done processing and + /// is ready to provide output. + /// + public void WaitUntilProviding() + { + this.blockDependencies.Wait(); + } + + /// + /// Changes the state of the pipeline into a new state. + /// + /// The state to change the pipeline into. + private void ChangeState(PipelineRunnerState newState) + { + this.logger.Verbose( + "{Pipeline:l}: Switching from state {Old} to {New} (elapsed {Elapsed}, duration {Duration})", + this.Pipeline, + this.State, + newState, + this.ElapsedFromInitialized, + this.ElapsedFromState); + this.changed = DateTime.Now; + this.State = newState; + } + + private List GatherDependencyOutputs() + { + if (this.Incoming.Count <= 0) + { + return new List(); + } + + // Report that we are gathering our outputs. + this.logger.Verbose( + "{Pipeline:l}: Gathering outputs from {Count:n0} dependencies", + this.Pipeline, + this.Incoming.Count); + + List input = this.Incoming + .SelectMany(x => x.Outputs) + .ToList(); + + this.logger.Debug( + "{Pipeline:l}: Got {Count:l} from dependencies", + this.Pipeline, + "entity".ToQuantity(input.Count, "N0")); + + return input; + } + + private async Task RunPipeline(List input) + { + IEnumerable + output = await this.Pipeline.RunAsync(input); + + // Gather all the output. + this.logger.Debug( + "{Pipeline:l}: Gathering output", + this.Pipeline); + this.Outputs.Clear(); + this.Outputs.AddRange(output); + } + + private void SendToDependants() + { + if (this.Outgoing.Count <= 0) + { + return; + } + + // Make sure our internal wait for the consumers it set. + this.logger.Verbose( + "{Pipeline:l}: Setting up internal thread controls", + this.Pipeline); + this.waitingOnConsumers = this.Outgoing.Count; + this.consumersDone.Reset(); + + // Report how many files we're sending out and then use manual + // reset and the semaphore to control the threads. + this.logger.Debug( + "{Pipeline:l}: Output {Count:l} from pipeline", + this.Pipeline, + "entity".ToQuantity(this.Outputs.Count, "N0")); + + // Release our manual reset to allow operations to continue. + this.ChangeState(PipelineRunnerState.Providing); + this.logger.Verbose( + "{Pipeline:l}: Release manual reset for consumers", + this.Pipeline); + this.blockDependencies.Set(); + + // Wait until all consumers have finished processing. + this.consumersDone.Wait(); + } + + private void SignalDoneWithInputs() + { + if (this.Incoming.Count <= 0 || this.signaledDoneWithInputs) + { + return; + } + + this.signaledDoneWithInputs = true; + + this.logger.Debug( + "{Pipeline:l}: Signaling {Count:n0} dependencies done", + this.Pipeline, + this.Incoming.Count); + + foreach (var dependency in this.Incoming) + { + dependency.ConsumerDoneWithOutputs(); + } + } + + private bool WaitForDependencies() + { + if (this.Incoming.Count <= 0) + { + return false; + } + + // Wait for the dependencies to run first. + this.ChangeState(PipelineRunnerState.Waiting); + this.logger.Verbose( + "{Pipeline:l}: Waiting for {Count:l} to complete", + this.Pipeline, + "dependency".ToQuantity(this.Incoming.Count)); + + foreach (var dependency in this.Incoming) + { + dependency.WaitUntilProviding(); + } + + // Check for any error state in the dependency, if we have one, + // then we need to stop ourselves. + bool hasError = this.Incoming + .Any(x => x.State == PipelineRunnerState.Errored); + + if (!hasError) + { + return false; + } + + this.logger.Error( + "{Pipeline:l}: There was an exception in an dependency", + this.Pipeline); + this.ChangeState(PipelineRunnerState.Errored); + this.blockDependencies.Set(); + + return true; + } + } +} diff --git a/src/Nitride/Pipelines/PipelineRunnerState.cs b/src/Nitride/Pipelines/PipelineRunnerState.cs new file mode 100644 index 0000000..3d67e03 --- /dev/null +++ b/src/Nitride/Pipelines/PipelineRunnerState.cs @@ -0,0 +1,72 @@ +namespace Nitride.Pipelines +{ + /// + /// Describes the state of the pipelines which is used both for reporting + /// purposes but also to allow the pipelines to respond to changes in their + /// own state, such as unloading output elements. + /// + public enum PipelineRunnerState + { + /// + /// Indicates that the runner is setting up and not ready for any use. + /// + Initializing, + + /// + /// Indicates that the runner has been initialized. + /// + Initialized, + + /// + /// Indicates that the pipeline is prepare for a new run. This is done + /// when the system determines it needs to run. + /// + Preparing, + + /// + /// Indicates that the pipeline has finished preparing. + /// + Prepared, + + /// + /// Indicates that the runner is waiting for dependencies to run. + /// + Waiting, + + /// + /// Indicates that the pipeline has started processing by a call to + /// `RunAsync`. + /// + Started, + + /// + /// Indicates that the pipeline is providing data to any dependencies. + /// If the pipeline has no dependencies, then this will never be called. + /// + Providing, + + /// + /// Indicates that all the dependencies on this pipeline has finished + /// running (state `Finished`) and the pipeline can clean up any + /// memory elements. + /// + Provided, + + /// + /// Indicates that the pipeline is done running and the system is + /// shutting down. + /// + Finalizing, + + /// + /// Indicates that the pipeline has been completely cleaned up and + /// finished. It will never be used again. + /// + Finalized, + + /// + /// Indicates that there was an error while running the pipeline. + /// + Errored, + } +} diff --git a/src/Nitride/Pipelines/README.md b/src/Nitride/Pipelines/README.md new file mode 100644 index 0000000..9b3ba7a --- /dev/null +++ b/src/Nitride/Pipelines/README.md @@ -0,0 +1,191 @@ +# Pipelines + +A pipeline is a set of operations that take an input of entities, performs +various operations and transformations on them, and then returns a resulting +list of entities which may or may not be the original ones. For pipelines that +read files from the disk, the input may be empty. The output from the final +pipelines may never be used. + +A simple pipeline may: + +1. Load all the Markdown files to a disk. +2. Convert the files into HTML. +3. Write them out to a different location. + +A pipeline implements the `IPipeline` interface or extends the convenience +class `Pipeline`. + +```c# +using Nitride.Pipelines; + +public class LoadFilesPipeline : Pipeline {} +``` + +## Dependencies + +Pipelines can be chained from another pipeline or from multiple pipelines. This +chaining may be for organization purposes or the output from one pipeline may +end up being fed into multiple pipelines. A pipeline may have 0-n pipelines it +depends on. + +```c# +var loadPipeline = new Pipeline(); +var htmlPipeline = new Pipeline().AddDependency(loadPipeline); +var geminiPipeline = new GeminiPipeline(loadPipeline); + +public class GeminiPipeline : Pipeline +{ + public GeminiPipeline(Pipeline loadPipeline) + { + this.AddDependency(loadPipeline); + } +} +``` + +Since Nitride is built around dependency injection, the constructor is the place +where the dependencies are typically set up. + +```c# +ContainerBuilder builder; + +builder.RegisterInstance().AsSelf(); +builder.RegisterInstance().AsSelf(); + +IContainer container = builder.Build(); + +var loadPipeline = new Pipeline(); +var geminiPipeline = container.Resolve(); + +public class GeminiPipeline : Pipeline +{ + public GeminiPipeline(LoadPipeline loadPipeline) + { + this.AddDependency(loadPipeline); + } +} +``` + +With the dependencies, pipelines are assumed to be directional, acyclic graph +(DAG). This means that there are no circular dependencies and the code can +determine the "entry" pipelines that will feed into the others until the +processes are complete. + +The DAG is also used to determine what is rebuilt during development. Only +pipelines affected (including dependencies) by a change will be re-run to keep +the process running as quickly as possible. + +### Processing + +The primary purpose of the pipeline is to process the entites. This method is +rather simple: + +```c# +public async Task> RunAsync( + IEnumerable entities) +{ + return entities; +} +``` + +(For purposes of this document, `async` is added to all the methods even if the +example would be better suited without it and using `Task.FromResult()` +instead.) + +### Identifying Source Pipeline + +Because of how entities are passed around, a pipeline that depends on two other +ones may have the same entity in both streams. Without any alteration, this will +result in duplicate entities with no ability to determine which entity came from +which pipeline. + +To mark an entity as being processed by a pipeline, a simple component can be +added as needed using the `AddComponents` extension method on the collection to +add the pipeline or some other indicator. + +```c# +public class ExamplePipeline : Pipeline +{ + public async Task> RunAsync(IEnumerable entities) + { + return entities + .AddComponents(this); + } +} +``` + +With the entity identified with the pipeline as a component, special processing +can be easily used. In addition, the `MergeEntities` can be used to pull +entities of the same identifier together. + +```c# +public class Pipeline1 : Pipeline +{ + public async Task> RunAsync(IEnumerable entities) + { + return LoadFileEntities(); + } +} + +public class Pipeline2 : Pipeline +{ + public Pipeline2(Pipeline1 pipeline) + { + this.AddDependency(pipeline); + } + + public async Task> RunAsync(IEnumerable entities) + { + return entities + .AddComponents(this); + } +} + +public class Pipeline3 : Pipeline +{ + public Pipeline3(Pipeline1 pipeline) + { + this.AddDependency(pipeline); + } + + public async Task> RunAsync(IEnumerable entities) + { + return entities + .AddComponents(this); + } +} + +public class Pipeline4 : Pipeline +{ + public Pipeline4(Pipeline2 pipeline2, Pipeline3 pipeline3) + { + this.AddDependency(pipeline2); + this.AddDependency(pipeline3); + } + + public async Task> RunAsync(IEnumerable entities) + { + return entities + .ForComponents((entity, pipeline2) => ...) + .ForComponents((entity, pipeline3) => ...) + .MergeComponents( + (entity1, entity2) => entity1.Merge(entity2)); + } +} +``` + +## Concurrency + +Pipelines will run in multiple threads, but will wait on their dependencies. +This means two pipelines with separate inputs will run at the same time, but a +pipeline that depends on one or more other pipelines will wait until their +dependencies have finished running before starting. + +Inside a pipeline, the code determines what operations are done concurrently. +Using PLINQ or various await methods can be used in this case. However, even if +a pipeline is synchronized, the process is always treated as async and will be +awaited. + +## Inspiration + +The concept of a pipeline has been inspired by both Statiq and also work done on +CobblestoneJS (the gather, prepare, and process approach). diff --git a/src/Nitride/ROADMAP.md b/src/Nitride/ROADMAP.md new file mode 100644 index 0000000..b5b2761 --- /dev/null +++ b/src/Nitride/ROADMAP.md @@ -0,0 +1,9 @@ +# Project Roadmap + +## Immediate + +- Switch the various operations to be async + - ReadFiles + - WriteFiles +- Implement mime type determination +- Implement a convert to text content based on mime type diff --git a/src/Nitride/Registration/NoRegistrationAttribute.cs b/src/Nitride/Registration/NoRegistrationAttribute.cs new file mode 100644 index 0000000..63b1b9f --- /dev/null +++ b/src/Nitride/Registration/NoRegistrationAttribute.cs @@ -0,0 +1,9 @@ +using System; + +namespace Nitride.Registration +{ + [AttributeUsage(AttributeTargets.Class)] + public class NoRegistrationAttribute : Attribute + { + } +} diff --git a/src/Nitride/Registration/SingletonAttribute.cs b/src/Nitride/Registration/SingletonAttribute.cs new file mode 100644 index 0000000..46612be --- /dev/null +++ b/src/Nitride/Registration/SingletonAttribute.cs @@ -0,0 +1,9 @@ +using System; + +namespace Nitride.Registration +{ + [AttributeUsage(AttributeTargets.Class)] + public class SingletonAttribute : Attribute + { + } +} diff --git a/src/Nitride/WithPropertiesAttribute.cs b/src/Nitride/WithPropertiesAttribute.cs new file mode 100644 index 0000000..ea7a325 --- /dev/null +++ b/src/Nitride/WithPropertiesAttribute.cs @@ -0,0 +1,14 @@ +using System; + +namespace Nitride +{ + /// + /// A marker attribute that indicates that the source generator should + /// automatically add `Set*` methods for every public property in the class. + /// The class must be `partial` for this to work. + /// + [AttributeUsage(AttributeTargets.Class)] + public class WithPropertiesAttribute : Attribute + { + } +}