diff --git a/.gitignore b/.gitignore index f2fc5a1..7f99dfe 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,33 @@ # See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files. +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Oo]ut/ +[Ll]og/ +[Ll]ogs/ # Compiled output dist/ bin/ diff --git a/API/.dockerignore b/API/.dockerignore new file mode 100644 index 0000000..fe1152b --- /dev/null +++ b/API/.dockerignore @@ -0,0 +1,30 @@ +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/azds.yaml +**/bin +**/charts +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md +!**/.gitignore +!.git/HEAD +!.git/config +!.git/packed-refs +!.git/refs/heads/** \ No newline at end of file diff --git a/API/.gitattributes b/API/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/API/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/API/.gitignore b/API/.gitignore new file mode 100644 index 0000000..9491a2f --- /dev/null +++ b/API/.gitignore @@ -0,0 +1,363 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Oo]ut/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd \ No newline at end of file diff --git a/API/FamilyTreeAPI.sln b/API/FamilyTreeAPI.sln new file mode 100644 index 0000000..2410403 --- /dev/null +++ b/API/FamilyTreeAPI.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.7.34202.233 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FamilyTreeAPI", "FamilyTreeAPI\FamilyTreeAPI.csproj", "{9B95839F-8AE3-4725-BCC1-DC73D1899CC6}" +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", "docker-compose.dcproj", "{81DDED9D-158B-E303-5F62-77A2896D2A5A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9B95839F-8AE3-4725-BCC1-DC73D1899CC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9B95839F-8AE3-4725-BCC1-DC73D1899CC6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9B95839F-8AE3-4725-BCC1-DC73D1899CC6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9B95839F-8AE3-4725-BCC1-DC73D1899CC6}.Release|Any CPU.Build.0 = Release|Any CPU + {81DDED9D-158B-E303-5F62-77A2896D2A5A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {81DDED9D-158B-E303-5F62-77A2896D2A5A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {81DDED9D-158B-E303-5F62-77A2896D2A5A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {81DDED9D-158B-E303-5F62-77A2896D2A5A}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {C7DF2338-DFC8-47CE-9AA7-C05C3C2ABEF7} + EndGlobalSection +EndGlobal diff --git a/API/FamilyTreeAPI/Authorization/AllowAnonymousAttribute.cs b/API/FamilyTreeAPI/Authorization/AllowAnonymousAttribute.cs new file mode 100644 index 0000000..8d30c5e --- /dev/null +++ b/API/FamilyTreeAPI/Authorization/AllowAnonymousAttribute.cs @@ -0,0 +1,7 @@ +using System; + +namespace FamilyTreeAPI.Authorization; + +[AttributeUsage(AttributeTargets.Method)] +public class AllowAnonymousAttribute : Attribute +{ } \ No newline at end of file diff --git a/API/FamilyTreeAPI/Authorization/AuthorizeAttribute.cs b/API/FamilyTreeAPI/Authorization/AuthorizeAttribute.cs new file mode 100644 index 0000000..d76588c --- /dev/null +++ b/API/FamilyTreeAPI/Authorization/AuthorizeAttribute.cs @@ -0,0 +1,36 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using System; +using System.Collections.Generic; +using System.Linq; + + +namespace FamilyTreeAPI.Authorization; + +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] +public class AuthorizeAttribute : Attribute, IAuthorizationFilter +{ + // private readonly IList _roles; + /* + public AuthorizeAttribute(params AdminRole[] roles) + { + _roles = roles ?? new AdminRole[] { }; + } + */ + public void OnAuthorization(AuthorizationFilterContext context) + { + // skip authorization if action is decorated with [AllowAnonymous] attribute + var allowAnonymous = context.ActionDescriptor.EndpointMetadata.OfType().Any(); + if (allowAnonymous) + return; + + // authorization + // var user = (User)context.HttpContext.Items["User"]; + // if (user == null || (_roles.Any() && !_roles.Contains(user.Role))) + // { + // not logged in or role not authorized + // context.Result = new JsonResult(new { message = "Unauthorized" }) { StatusCode = StatusCodes.Status401Unauthorized }; + //} + } +} \ No newline at end of file diff --git a/API/FamilyTreeAPI/Authorization/JwtMiddleware.cs b/API/FamilyTreeAPI/Authorization/JwtMiddleware.cs new file mode 100644 index 0000000..f6fdea1 --- /dev/null +++ b/API/FamilyTreeAPI/Authorization/JwtMiddleware.cs @@ -0,0 +1,38 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Options; +using System.Linq; +using System.Threading.Tasks; + +using FamilyTreeAPI.Interface; +using FamilyTreeAPI.Entities; + +namespace FamilyTreeAPI.Authorization; + +public class JwtMiddleware +{ + private readonly RequestDelegate _next; + private readonly AppSettings _appSettings; + + public JwtMiddleware(RequestDelegate next, IOptions appSettings) + { + _next = next; + _appSettings = appSettings.Value; + } + + public async Task Invoke(HttpContext context, IJwtUtils jwtUtils) + { + var token = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last(); + if (token != null) + { + var user = jwtUtils.ValidateJwtToken(token); + if (user != null) + { + // attach user to context on successful jwt validation + //here to put in the real user + //TODO if you want to add information for User + context.Items["User"] = user; + } + } + await _next(context); + } +} diff --git a/API/FamilyTreeAPI/Authorization/JwtUtils.cs b/API/FamilyTreeAPI/Authorization/JwtUtils.cs new file mode 100644 index 0000000..bcc7160 --- /dev/null +++ b/API/FamilyTreeAPI/Authorization/JwtUtils.cs @@ -0,0 +1,111 @@ +using Microsoft.Extensions.Options; +using Microsoft.IdentityModel.Tokens; +using System; +using System.IdentityModel.Tokens.Jwt; +using System.Linq; +using System.Security.Claims; +using System.Text; +using FamilyTreeAPI.Entities; +using FamilyTreeAPI.Interface; + +namespace FamilyTreeAPI.Authorization; + + +public class JwtUtils : IJwtUtils +{ + private readonly AppSettings _appSettings; + + public JwtUtils(IOptions appSettings) + { + _appSettings = appSettings.Value; + } + + public string GenerateJwtToken(UserDto user) + { + // generate token that is valid for 2 days + var tokenHandler = new JwtSecurityTokenHandler(); + var key = Encoding.ASCII.GetBytes(_appSettings.Secret); + var tokenDescriptor = new SecurityTokenDescriptor + { + Subject = new ClaimsIdentity(new[] { + new Claim("id", user.Id.ToString()), + new Claim("subject", user.Username), + new Claim("firstName", user.FirstName + " " + user.LastName) + { + + } + + //add more if we need it + } + ), + Expires = DateTime.UtcNow.AddDays(2), + SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature) + }; + var token = tokenHandler.CreateToken(tokenDescriptor); + return tokenHandler.WriteToken(token); + } + // get back id and username. in subject + public UserDto? ValidateJwtToken(string token) + { + if (token == null) + return null; + + var tokenHandler = new JwtSecurityTokenHandler(); + var key = Encoding.ASCII.GetBytes(_appSettings.Secret); + try + { + tokenHandler.ValidateToken(token, new TokenValidationParameters + { + ValidateIssuerSigningKey = true, + IssuerSigningKey = new SymmetricSecurityKey(key), + ValidateIssuer = false, + ValidateAudience = false, + // set clockskew to zero so tokens expire exactly at token expiration time (instead of 5 minutes later) + ClockSkew = TimeSpan.Zero + }, out SecurityToken validatedToken); + + var jwtToken = (JwtSecurityToken)validatedToken; + // var userId = int.Parse(jwtToken.Claims.First(x => x.Type == "id").Value); //old + int userId = 0; + string username = ""; + string firstName = ""; + Claim claim; + for (int i = 0; i< jwtToken.Claims.Count(); i++) + { + claim = jwtToken.Claims.ElementAt(i); + if (claim.Type == "id") + { + userId = int.Parse(claim.Value); + } + else if (claim.Type == "subject") + { + username = claim.Value; + } + else if (claim.Type == "firstName") + { + firstName = claim.Value; + } + + if (!string.IsNullOrEmpty(username) + && !string.IsNullOrEmpty(firstName) + && userId > 0) + { + break; + } + } + + UserDto user = new UserDto(); + user.Id = userId; + user.Username = username; + user.FirstName = firstName; + + // return user id from JWT token if validation successful + return user; + } + catch + { + // return null if validation fails + return null; + } + } +} \ No newline at end of file diff --git a/API/FamilyTreeAPI/Controllers/LookupController.cs b/API/FamilyTreeAPI/Controllers/LookupController.cs new file mode 100644 index 0000000..94755e8 --- /dev/null +++ b/API/FamilyTreeAPI/Controllers/LookupController.cs @@ -0,0 +1,68 @@ +using FamilyTreeAPI.Entities; +using FamilyTreeAPI.Interface; +using Microsoft.AspNetCore.Mvc; + +namespace FamilyTreeAPI.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class LookupController : ControllerBase + { + private readonly ILookup _repo; + public LookupController(ILookup repo) + { + _repo = repo; + } + [HttpGet("[action]")] + public async Task LoadLookup(string type) + { + var list = await _repo.GetLookupAsync(type); + + return Ok(list); + } + [HttpGet("[action]")] + public async Task LoadLookupEdit(string type) + { + var list = await _repo.GetLookupEditAsync(type); + return Ok(list); + } + [HttpGet("[action]")] + public async Task GetPersons() + { + var list = await _repo.GetPersonsAsync(); + return Ok(list); + } + [HttpGet("[action]")] + public async Task GetStaffs() + { + var list = await _repo.GetStaffAsync(); + return Ok(list); + } + + [HttpPost] + public async Task Lookup([FromBody] LookupEditDto model) + { + //var currentUser = (User?)(HttpContext.Items["User"]); + //if (null == currentUser) + // return Unauthorized(new { message = "Unauthorized" }); + + var response = await _repo.SaveLookupAsync(model); + + return Ok(response); + } + + [HttpGet] + public async Task Lookup(int id, string type) + { + //lookup/id?type='abc' + /* + // only admins can access other user records + var currentUser = (User)HttpContext.Items["User"]; + if (id != currentUser.Id && currentUser.Role != Role.Admin) + return Unauthorized(new { message = "Unauthorized" }); + */ + var retval = await _repo.GetLookupEditByIdAsync(id, type); + return Ok(retval); + } + } +} diff --git a/API/FamilyTreeAPI/Controllers/PersonController.cs b/API/FamilyTreeAPI/Controllers/PersonController.cs new file mode 100644 index 0000000..809d8b4 --- /dev/null +++ b/API/FamilyTreeAPI/Controllers/PersonController.cs @@ -0,0 +1,110 @@ +using FamilyTreeAPI.Entities; +using FamilyTreeAPI.Interface; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Primitives; + +namespace FamilyTreeAPI.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class PersonController : ControllerBase + { + private readonly IPerson _repo; + public PersonController(IPerson repo) + { + _repo = repo; + } + [HttpPost("[action]")] + public async Task UploadImage(IFormFile file) + { + var keys = Request.Form; + var files = Request.Form.Files; + ResultModel ret = new(); + if (files.Count > 0) + { + StringValues familyId = ""; + + keys.TryGetValue("familyId", out familyId); + + UploadCriteria criteria = new(); + criteria.File = file; + criteria.FamilyId = familyId; + + criteria.FileName = file.FileName; + + ret = await _repo.UploadImage(criteria); + } + return Ok(ret); + + + } + //DeleteUploadFile + [HttpPost("[action]")] + public ActionResult DeleteUploadFile(DeleteFileCriteria criteria) + { + + ResultModel ret = new(); + if (!string.IsNullOrEmpty(criteria.Filename)) + { + + ret = _repo.DeleteUploadFile(criteria); + } + return Ok(ret); + } + + [HttpPost("[action]")] + public async Task SearchPerson(PersonCriteria criteria) + { + var list = await _repo.GetPerson(criteria); + + return Ok(list); + } + + [HttpPost("[action]")] + public async Task GetChildress(ChildCriteria criteria) + { + var list = await _repo.GetChildren(criteria); + + return Ok(list); + } + [HttpPost("[action]")] + public async Task GetFamilyTreeBy(FamilyCriteria criteria) + { + var list = await _repo.GetFamilyTreeBy(criteria); + return Ok(list); + } + [HttpGet("[action]/{id}")] + public async Task GetByPersonFamily(int id) + { + var list = await _repo.GetByFamilyAsync(id); + return Ok(list); + } + [HttpGet("[action]/{id}")] + public async Task GetById(int id) + { + var list = await _repo.GetByIdAsync(id); + return Ok(list); + } + + [HttpPost("[action]")] + public async Task DeleteById(DeleteCriteria criteria) + { + var list = await _repo.DeleteAsync(criteria.Id); + return Ok(list); + } + + [HttpPost("[action]")] + public async Task SavePerson([FromBody] PersonForSave model) + { + //var currentUser = (User?)(HttpContext.Items["User"]); + //if (null == currentUser) + // return Unauthorized(new { message = "Unauthorized" }); + + var response = await _repo.SaveAsync(model); + + return Ok(response); + } + + + } +} diff --git a/API/FamilyTreeAPI/Controllers/PersonRelatedController.cs b/API/FamilyTreeAPI/Controllers/PersonRelatedController.cs new file mode 100644 index 0000000..adcb3ca --- /dev/null +++ b/API/FamilyTreeAPI/Controllers/PersonRelatedController.cs @@ -0,0 +1,47 @@ +using FamilyTreeAPI.Entities; +using FamilyTreeAPI.Interface; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Primitives; + +namespace FamilyTreeAPI.Controllers; + + [Route("api/[controller]")] + [ApiController] + public class RelationShipController : ControllerBase + { + private readonly IRelationShipd _repo; + public RelationShipController(IRelationShipd repo) + { + _repo = repo; + } + [HttpGet("[action]/{id}")] + public async Task GetById(int id) + { + var list = await _repo.GetByIdAsync(id); + return Ok(list); + } + [HttpGet("[action]/{id}")] + public async Task GetByPersonId(int id) + { + var list = await _repo.GetByPersonIdAsync(id); + return Ok(list); + } + + [HttpPost("[action]/{id}")] + public async Task DeleteById(int id) + { + var list = await _repo.GetByIdAsync(id); + return Ok(list); + } + [HttpPost("[action]")] + public async Task SaveRelationShip([FromBody] RelationShiftContainer list) + { + //var currentUser = (User?)(HttpContext.Items["User"]); + //if (null == currentUser) + // return Unauthorized(new { message = "Unauthorized" }); + + var response = await _repo.SaveAsync(list.relationShips); + + return Ok(response); + } + } \ No newline at end of file diff --git a/API/FamilyTreeAPI/Controllers/StaffController.cs b/API/FamilyTreeAPI/Controllers/StaffController.cs new file mode 100644 index 0000000..2812f40 --- /dev/null +++ b/API/FamilyTreeAPI/Controllers/StaffController.cs @@ -0,0 +1,71 @@ +using Microsoft.AspNetCore.Mvc; +using FamilyTreeAPI.Entities; +using FamilyTreeAPI.Interface; +using System.Threading.Tasks; + +namespace FamilyTreeAPI.Controllers; + +// [Authorize] +[ApiController] +[Route("api/[controller]")] +public class StaffController : ControllerBase +{ + private readonly IStaff _staff; + public StaffController(IStaff staff) + { + _staff = staff; + } + + [HttpPost("[action]")] + public async Task SaveStaff([FromBody] StaffDto model) + { + //var currentUser = (User?)HttpContext.Items["User"]; + //if (null == currentUser) + // return Unauthorized(new { message = "Unauthorized" }); + + var response = await _staff.SaveStaff(model); + return Ok(response); + } + + [HttpPost("[action]")] + public async Task ResetPassStaff([FromBody] ResetPassDto model) + { + //var currentUser = (User?)HttpContext.Items["User"]; + //if (null == currentUser) + // return Unauthorized(new { message = "Unauthorized" }); + + var response = await _staff.ResetPassword(model); + return Ok(response); + } + + [HttpPost("[action]")] + public async Task SearchStaff([FromBody] StaffCriteria criteria) + { + + var retval = await _staff.GetStaff(criteria); + return Ok(retval); + } + + [HttpGet("{id}")] + public async Task Staff( int id) + { + /* + // only admins can access other user records + var currentUser = (User)HttpContext.Items["User"]; + if (id != currentUser.Id && currentUser.Role != Role.Admin) + return Unauthorized(new { message = "Unauthorized" }); + */ + var retval = await _staff.GetStaffById(id); + return Ok(retval); + } + [HttpPost("[action]")] + public async Task DeleteStaff(DeleteCriteria criteria) + { + var currentUser = (UserDto?)HttpContext.Items["User"]; + if (null == currentUser) + return Unauthorized(new { message = "Unauthorized" }); + + var retval = await _staff.Delete(criteria.Id); + return Ok(retval); + } +} diff --git a/API/FamilyTreeAPI/Controllers/UsersController.cs b/API/FamilyTreeAPI/Controllers/UsersController.cs new file mode 100644 index 0000000..79d05e7 --- /dev/null +++ b/API/FamilyTreeAPI/Controllers/UsersController.cs @@ -0,0 +1,73 @@ +using FamilyTreeAPI.Interface; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using FamilyTreeAPI.Entities; +namespace FamilyTreeAPI.Controllers; + +[Authorize] +[ApiController] +[Route("api/[controller]")] +public class UsersController : ControllerBase +{ + private readonly IUserService _userService; + + public UsersController(IUserService userService) + { + _userService = userService; + } + + [AllowAnonymous] + [HttpPost("[action]")] + public async Task Login([FromBody] AuthenticateRequest model) + { + string remoteIpAddress = HttpContext.Connection.RemoteIpAddress?.ToString(); + var response = await _userService.Login(model); + return Ok(response); + } + + //[AllowAnonymous] + //[HttpPost("[action]")] + //public async Task LoginAD(AuthenticateRequest model) + //{ + // var response = await _userService.LoginAD(model); + // return Ok(response); + //} + [AllowAnonymous] + [HttpPost("[action]")] + public async Task LoginApiAD([FromBody] AuthenticateRequest model) + { + string remoteIpAddress = HttpContext.Connection.RemoteIpAddress?.ToString(); + var response = await _userService.LoginApiAD(model, remoteIpAddress); + return Ok(response); + } + + [HttpPost("[action]")] + public async Task Logout() + { + var currentUser = (UserDto)HttpContext.Items["User"]; + if (null == currentUser) + return Unauthorized(new { message = "Unauthorized" }); + + string token = Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last(); + string remoteIpAddress = Request.HttpContext.Connection.RemoteIpAddress.ToString(); + var response = await _userService.Logout(token, currentUser, remoteIpAddress); + return Ok(response); + } + + //[AllowAnonymous] + //[HttpGet("[action]")] + //public async Task SearchADStaff(string stafflinkNo) + //{ + // var user = await _userService.SearchADStaff(stafflinkNo); + // return Ok(user); + //} + /* + [AllowAnonymous] + [HttpGet("[action]")] + public async Task> SearchADStaff(string stafflinkNo) + { + var user = await _userService.SearchADStaff(stafflinkNo); + return user; + } + */ +} diff --git a/API/FamilyTreeAPI/Data/DataWork.cs b/API/FamilyTreeAPI/Data/DataWork.cs new file mode 100644 index 0000000..c6e4e72 --- /dev/null +++ b/API/FamilyTreeAPI/Data/DataWork.cs @@ -0,0 +1,105 @@ +using Microsoft.AspNetCore.Mvc.Infrastructure; +using Microsoft.EntityFrameworkCore; +using System.Data.Common; +using FamilyTreeAPI.Entities; +using FamilyTreeAPI.Interface; + +namespace FamilyTreeAPI.Models +{ + public partial class FamilyTreeDBContext + { + private enum EnumStaff + { + Id =0, + Firstname, + Lastname, + Email, + Stype, + Active, + Role, + Password + }; + private enum EnumStaffWork + { + id =0, + staffid, + description, + startdate, + sactive, + did, + starttime, + stoptime, + task + } + private enum EnumServiceTask + { + id = 0, + staffid, + sdate, + description, + code, + status, + assignTo, + clientid, + serviceno + } + + public async Task> LoadStaffAsync(StaffCriteria criteria) + { + List list = new(); + using (DbConnection connect = this.Database.GetDbConnection()) + { + DbCommand command = connect.CreateCommand(); + command.CommandType = System.Data.CommandType.Text; + command.CommandText = "SELECT * From usp_search_user(:iemail,:ifirstname,:ilastname)"; + + var loginParam = command.CreateParameter(); + + loginParam.ParameterName = "iemail"; + loginParam.Value = string.IsNullOrEmpty(criteria.Email) ? "" : criteria.Email; + + var fistnameParam = command.CreateParameter(); + + fistnameParam.ParameterName = "ifirstname"; + fistnameParam.Value = string.IsNullOrEmpty(criteria.FirstName) ? "" : criteria.FirstName; + + var surnameParam = command.CreateParameter() ; + + surnameParam.ParameterName = "ilastname"; + surnameParam.Value = string.IsNullOrEmpty(criteria.LastName) ? "" : criteria.LastName; + + command.Parameters.Add(loginParam); + command.Parameters.Add(fistnameParam); + command.Parameters.Add(surnameParam); + await connect.OpenAsync(); + DbDataReader reader = await command.ExecuteReaderAsync(); + StaffDto staff; + int idx = 0; + while (reader.Read()) + { + staff = new(); + idx = (int)EnumStaff.Id; + staff.Id = reader.GetFieldValue(idx); + idx = (int)EnumStaff.Firstname; + staff.Firstname = reader.GetFieldValue(idx); + idx = (int) EnumStaff.Lastname; + staff.Lastname = reader.GetFieldValue(idx); + idx = (int)EnumStaff.Email; + staff.Email = reader.GetFieldValue(idx); + idx = (int)EnumStaff.Active; + staff.Active = reader.GetFieldValue(idx); + idx = (int)EnumStaff.Role; + staff.RoleType = reader.GetFieldValue(idx); + idx = (int)EnumStaff.Password; + if (!reader.IsDBNull(idx)) + staff.Password = reader.GetFieldValue(idx); + + list.Add(staff); + } + + } + return list; + } + + } +} diff --git a/API/FamilyTreeAPI/Dockerfile b/API/FamilyTreeAPI/Dockerfile new file mode 100644 index 0000000..3b80c5b --- /dev/null +++ b/API/FamilyTreeAPI/Dockerfile @@ -0,0 +1,26 @@ +# See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging. + +# This stage is used when running from VS in fast mode (Default for Debug configuration) +#FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base +ARG BUILD_CONFIGURATION=Release +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +WORKDIR /src +COPY ["FamilyTreeAPI/FamilyTreeAPI.csproj", "FamilyTreeAPI/"] +COPY ["CommonAD/CommonAD.csproj", "CommonAD/"] +RUN dotnet restore "./FamilyTreeAPI/FamilyTreeAPI.csproj" +COPY . . +WORKDIR "/src/FamilyTreeAPI" +RUN dotnet build "./FamilyTreeAPI.csproj" -c Release -o /app/build + +# This stage is used to publish the service project to be copied to the final stage +FROM build AS publish + +RUN dotnet publish "./FamilyTreeAPI.csproj" -c Release -o /app/publish /p:UseAppHost=false + +# This stage is used in production or when running from VS in regular mode (Default when not using the Debug configuration) +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base +WORKDIR /app +ENV ASPNETCORE_HTTP_PORTS=80 +EXPOSE 80 +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "FamilyTreeAPI.dll"] \ No newline at end of file diff --git a/API/FamilyTreeAPI/Entities/AppSettings.cs b/API/FamilyTreeAPI/Entities/AppSettings.cs new file mode 100644 index 0000000..ac9972e --- /dev/null +++ b/API/FamilyTreeAPI/Entities/AppSettings.cs @@ -0,0 +1,18 @@ +namespace FamilyTreeAPI.Entities; + +public class AppSettings +{ + public string Secret { get; set; } + public string SQLConnectionString { get; set; } + public string LoginWebAPI { get; set; } + + public string ClientURL { get; set; } +} + +/* + entity.HasOne(d => d.Staff) + .WithMany(p => p.Staffworks) + .HasForeignKey(d => d.Staffid) + .HasConstraintName("staffwork_staffid_fkey") + .OnDelete(DeleteBehavior.ClientCascade); + */ \ No newline at end of file diff --git a/API/FamilyTreeAPI/Entities/AuthenticateRequest.cs b/API/FamilyTreeAPI/Entities/AuthenticateRequest.cs new file mode 100644 index 0000000..77e6d63 --- /dev/null +++ b/API/FamilyTreeAPI/Entities/AuthenticateRequest.cs @@ -0,0 +1,15 @@ +using System.ComponentModel.DataAnnotations; + +namespace FamilyTreeAPI.Entities; + +public class AuthenticateRequest +{ + [Required] + public string Username { get; set; } + + [Required] + public string Password { get; set; } + + + // public string Lhd { get; set; } +} diff --git a/API/FamilyTreeAPI/Entities/AuthenticateResponse.cs b/API/FamilyTreeAPI/Entities/AuthenticateResponse.cs new file mode 100644 index 0000000..773458e --- /dev/null +++ b/API/FamilyTreeAPI/Entities/AuthenticateResponse.cs @@ -0,0 +1,35 @@ + +namespace FamilyTreeAPI.Entities; + +public class AuthenticateResponse +{ + public int Id { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + public string Username { get; set; } + public string Token { get; set; } + public string Email { get; set; } + public string? Department { get; set; } + public string? Phone { get; set; } + public string? Position { get; set; } + public string? ManagerEmail { get; set; } + public int Role { get; set; } + + public AuthenticateResponse(UserDto user, string token, int role) + { + Id = user.Id; + FirstName = user.FirstName; + LastName = user.LastName; + Username = user.Username; + Email = user.Email; + Department = user.Department; + Position = user.Position; + ManagerEmail = user.ManagerEmail; + Role = role; + Token = token; + Phone = user.Phone; + + + + } +} diff --git a/API/FamilyTreeAPI/Entities/FileContent.cs b/API/FamilyTreeAPI/Entities/FileContent.cs new file mode 100644 index 0000000..5c98573 --- /dev/null +++ b/API/FamilyTreeAPI/Entities/FileContent.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace FamilyTreeAPI.Entities; + +public class FileContent +{ + public byte[] Content { get; set; } + public string FileName { get; set; } +} +public class UploadCriteria +{ + public string FileName { get; set; } + public string FamilyId { get; set; } + + public IFormFile File { get; set; } +} +public class DeleteFileCriteria +{ + public int FamilyId { get; set; } + public string Filename { get; set; } +} \ No newline at end of file diff --git a/API/FamilyTreeAPI/Entities/LookupDto.cs b/API/FamilyTreeAPI/Entities/LookupDto.cs new file mode 100644 index 0000000..2adbe92 --- /dev/null +++ b/API/FamilyTreeAPI/Entities/LookupDto.cs @@ -0,0 +1,16 @@ +namespace FamilyTreeAPI.Entities; + +public class LookupDto +{ + public int Id { get; set; } = 0; + public string CodeId { get; set; } + public string Description { get; set; } +} +public class LookupEditDto +{ + public int Id { get; set; } = 0; + public string CodeId { get; set; } + public string Description { get; set; } + public bool Active { get; set; } + public string Type { get; set; } +} diff --git a/API/FamilyTreeAPI/Entities/PersonDto.cs b/API/FamilyTreeAPI/Entities/PersonDto.cs new file mode 100644 index 0000000..105b9bf --- /dev/null +++ b/API/FamilyTreeAPI/Entities/PersonDto.cs @@ -0,0 +1,53 @@ + + +namespace FamilyTreeAPI.Entities; + +public class ChildCriteria +{ + public int FatherId { get; set; } + public int MotherId { get; set; } + +} +public class FamilyCriteria +{ + public bool UseFather { get; set; } + public bool UseMother { get; set; } + +} +public class PersonCriteria +{ + public int Id { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } +} + +public class PersonForSave +{ + public PersonDto Person { get; set; } + public string? FormData { get; set; } + public string? FileName { get; set; } + +} +public partial class PersonDto +{ + public PersonDto() + { + + } + + public int Id { get; set; } + public string? FirstName { get; set; } + public string? LastName { get; set; } + public string? Email { get; set; } + public string? Phone { get; set; } + public string? Image { get; set; } + public string? Sex { get; set; } + public string? Address { get; set; } + public bool? Alive { get; set; } + public string? dob { get; set; } + public int? FatherId { get; set; } + public int? MotherId { get; set; } + + public List? RelationShips { get; set; } + +} diff --git a/API/FamilyTreeAPI/Entities/RelationShipDto.cs b/API/FamilyTreeAPI/Entities/RelationShipDto.cs new file mode 100644 index 0000000..1decd3f --- /dev/null +++ b/API/FamilyTreeAPI/Entities/RelationShipDto.cs @@ -0,0 +1,16 @@ +namespace FamilyTreeAPI.Entities; + +public class RelationShiftContainer +{ + public int familyId {get; set;} + public List relationShips {get; set;} +} +public class RelationShipDto +{ + public enumState State { get; set; } + public int Id { get; set; } + + public int PersonId { get; set; } + public int RelatePersonId { get; set; } + +} \ No newline at end of file diff --git a/API/FamilyTreeAPI/Entities/ResultModel.cs b/API/FamilyTreeAPI/Entities/ResultModel.cs new file mode 100644 index 0000000..20f002c --- /dev/null +++ b/API/FamilyTreeAPI/Entities/ResultModel.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace FamilyTreeAPI.Entities; + +public enum enumState +{ + Delete = -1, + NoChange = 0, + New = 1, + Modify = 2, + +} +public class ResultModel +{ + public T Data { get; set; } + public int StatusCode { get; set; } + public string Message { get; set; } +} + +public class DeleteCriteria +{ + public T Id { get; set; } +} diff --git a/API/FamilyTreeAPI/Entities/StaffDto.cs b/API/FamilyTreeAPI/Entities/StaffDto.cs new file mode 100644 index 0000000..53ee781 --- /dev/null +++ b/API/FamilyTreeAPI/Entities/StaffDto.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; + + +namespace FamilyTreeAPI.Entities; + +public partial class StaffCriteria +{ + + public string Email { get; set; } = null!; + public string? FirstName { get; set; } + public string? LastName { get; set; } + +} +public partial class StaffDto +{ + public int Id { get; set; } + public string? Phone { get; set; } = null!; + public string? Firstname { get; set; } + public string? Lastname { get; set; } + public string? Password { get; set; } + public string? Email { get; set; } + + public int? Type { get; set; } + +public bool? Active { get; set; } + public int? RoleType { get; set; } +} + + +public partial class ResetPassDto +{ + public int Id { get; set; } + public string Password { get; set; } = null!; + +} \ No newline at end of file diff --git a/API/FamilyTreeAPI/Entities/TreeNode.cs b/API/FamilyTreeAPI/Entities/TreeNode.cs new file mode 100644 index 0000000..d7cb19a --- /dev/null +++ b/API/FamilyTreeAPI/Entities/TreeNode.cs @@ -0,0 +1,15 @@ +namespace FamilyTreeAPI.Entities; + +public class TreeNode +{ + public string? Label { get; set; } + public T? Data { get; set; } + public List>? Children { get; set; } + public string? Icon { get; set; } + public bool? Checked { get; set; } + public bool? Leaf { get; set; } + public bool? Expanded { get; set; } + public string? Type { get; set; } + public string? Key { get; set; } + public bool? Loading { get; set; } +} \ No newline at end of file diff --git a/API/FamilyTreeAPI/Entities/UserDto.cs b/API/FamilyTreeAPI/Entities/UserDto.cs new file mode 100644 index 0000000..fb8a541 --- /dev/null +++ b/API/FamilyTreeAPI/Entities/UserDto.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace FamilyTreeAPI.Entities; + +/* + * ROLE ID + * Normal = 1, + Admin = 2, + Manager =3, + */ + +public class UserDto +{ + public int Id { get; set; } + public string? FirstName { get; set; } + public string? LastName { get; set; } + public string? Email { get; set; } + public string? Username { get; set; } + public string? Department { get; set; } + public string? Phone { get; set; } + public string? Position { get; set; } + public string? ManagerEmail { get; set; } + public int Role { get; set; } + +} + + diff --git a/API/FamilyTreeAPI/FamilyTreeAPI.csproj b/API/FamilyTreeAPI/FamilyTreeAPI.csproj new file mode 100644 index 0000000..223c758 --- /dev/null +++ b/API/FamilyTreeAPI/FamilyTreeAPI.csproj @@ -0,0 +1,38 @@ + + + + net9.0 + enable + enable + Linux + ..\docker-compose.dcproj + + + + + + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + diff --git a/API/FamilyTreeAPI/GraphQL/Query/QueryFamilyTree.cs b/API/FamilyTreeAPI/GraphQL/Query/QueryFamilyTree.cs new file mode 100644 index 0000000..d72a46c --- /dev/null +++ b/API/FamilyTreeAPI/GraphQL/Query/QueryFamilyTree.cs @@ -0,0 +1,19 @@ +using FamilyTreeAPI.Models; + +namespace FamilyTreeAPI.GraphQL.Query +{ + public class QueryFamilyTree + { + [UsePaging] + [UseProjection] + [UseFiltering] + [UseSorting] + public IQueryable GetPersons([Service] FamilyTreeDBContext dBContext) => dBContext.Persons; + [UsePaging] + [UseProjection] + [UseFiltering] + [UseSorting] + public IQueryable GetStaffs([Service] FamilyTreeDBContext dBContext) => dBContext.staff; + } + +} diff --git a/API/FamilyTreeAPI/Helper/Helpers.cs b/API/FamilyTreeAPI/Helper/Helpers.cs new file mode 100644 index 0000000..2ede587 --- /dev/null +++ b/API/FamilyTreeAPI/Helper/Helpers.cs @@ -0,0 +1,28 @@ +namespace FamilyTreeAPI.Helper +{ + public class Helpers + { + public static string? DateToStr(DateTime? date) + { + string? result = null; + if (date.HasValue) + { + result = date.Value.ToString("yyyy-MM-dd"); + } + return result; + } + public static DateTime? DateToDateTime(string? date) + { + DateTime? result = null; + if (!string.IsNullOrEmpty(date)) + { + DateTime dt; + if (DateTime.TryParse(date, out dt)) + { + result = dt; + } + } + return result; + } + } +} diff --git a/API/FamilyTreeAPI/Interface/IJwtUtils.cs b/API/FamilyTreeAPI/Interface/IJwtUtils.cs new file mode 100644 index 0000000..edd8a7d --- /dev/null +++ b/API/FamilyTreeAPI/Interface/IJwtUtils.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using FamilyTreeAPI.Entities; + +namespace FamilyTreeAPI.Interface; + +public interface IJwtUtils +{ + public string GenerateJwtToken(UserDto user); + public UserDto? ValidateJwtToken(string token); +} diff --git a/API/FamilyTreeAPI/Interface/ILookup.cs b/API/FamilyTreeAPI/Interface/ILookup.cs new file mode 100644 index 0000000..546854e --- /dev/null +++ b/API/FamilyTreeAPI/Interface/ILookup.cs @@ -0,0 +1,16 @@ +using FamilyTreeAPI.Entities; + +namespace FamilyTreeAPI.Interface; + +public interface ILookup +{ + Task> SaveLookupAsync(LookupEditDto lookup); + Task>> GetLookupAsync(string type); + Task>> GetLookupDicAsync(string type); + Task>> GetLookupEditAsync(string type); + Task> GetLookupEditByIdAsync(int codeId, string type); + Task>> GetPersonsAsync(); + Task>> GetStaffAsync(); + + +} diff --git a/API/FamilyTreeAPI/Interface/IPerson.cs b/API/FamilyTreeAPI/Interface/IPerson.cs new file mode 100644 index 0000000..1058b50 --- /dev/null +++ b/API/FamilyTreeAPI/Interface/IPerson.cs @@ -0,0 +1,18 @@ +using FamilyTreeAPI.Entities; + +namespace FamilyTreeAPI.Interface; + +public interface IPerson +{ + Task>> GetByFamilyAsync(int id); + Task>>> GetFamilyTreeBy(FamilyCriteria criteria); + Task>> GetChildren(ChildCriteria criteria); + Task>> GetPerson(PersonCriteria criteria); + Task>> GetDicFamily(); + Task> GetByIdAsync(int id); + Task> DeleteAsync(int id); + + Task> UploadImage(UploadCriteria criteria); + ResultModel DeleteUploadFile(DeleteFileCriteria criteria); + Task> SaveAsync(PersonForSave dto); +} diff --git a/API/FamilyTreeAPI/Interface/IPersonRelated.cs b/API/FamilyTreeAPI/Interface/IPersonRelated.cs new file mode 100644 index 0000000..388e202 --- /dev/null +++ b/API/FamilyTreeAPI/Interface/IPersonRelated.cs @@ -0,0 +1,13 @@ +using FamilyTreeAPI.Entities; + +namespace FamilyTreeAPI.Interface; + +public interface IRelationShipd +{ + Task> SaveAsync(List dto); + //load by personId + Task>> GetByPersonIdAsync(int personId); + Task> GetByIdAsync(int Id); + + Task> DeleteAsync(int personId); +} \ No newline at end of file diff --git a/API/FamilyTreeAPI/Interface/IReport.cs b/API/FamilyTreeAPI/Interface/IReport.cs new file mode 100644 index 0000000..f67d525 --- /dev/null +++ b/API/FamilyTreeAPI/Interface/IReport.cs @@ -0,0 +1,8 @@ +using FamilyTreeAPI.Entities; + +namespace FamilyTreeAPI.Interface; +public interface IReport +{ + // Task> GetMotorVehicleReportAsync(MotorVehicleCriteria criteria); + // Task> GetStaffWorkReportAsync(StaffWorkCriteria criteria); +} \ No newline at end of file diff --git a/API/FamilyTreeAPI/Interface/IStaff.cs b/API/FamilyTreeAPI/Interface/IStaff.cs new file mode 100644 index 0000000..cb366fd --- /dev/null +++ b/API/FamilyTreeAPI/Interface/IStaff.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using FamilyTreeAPI.Entities; + +namespace FamilyTreeAPI.Interface; + +public interface IStaff +{ + + Task>> GetDicStaffs(); + Task>> GetStaff(StaffCriteria criteria); + Task> GetStaffById(int id); + Task> SaveStaff(StaffDto code); + Task> ResetPassword(ResetPassDto code); + Task> SaveStaffNew(StaffDto adminUser); + + Task> Delete(int id); +} diff --git a/API/FamilyTreeAPI/Interface/IUserService.cs b/API/FamilyTreeAPI/Interface/IUserService.cs new file mode 100644 index 0000000..1f56dd6 --- /dev/null +++ b/API/FamilyTreeAPI/Interface/IUserService.cs @@ -0,0 +1,13 @@ +using FamilyTreeAPI.Entities; + +namespace FamilyTreeAPI.Interface; +public interface IUserService +{ + + Task> LoginApiAD(AuthenticateRequest model, string remoteIpAddress); + Task> Login(AuthenticateRequest model); + Task> Logout(string token, UserDto user, string remoteIpAddress); + + // Task> SearchApiStaff(string staffLinkNo); + // Task> SearchADStaff(string staffLinkNo); +} diff --git a/API/FamilyTreeAPI/Models/FamilyTreeDBContext.cs b/API/FamilyTreeAPI/Models/FamilyTreeDBContext.cs new file mode 100644 index 0000000..63444af --- /dev/null +++ b/API/FamilyTreeAPI/Models/FamilyTreeDBContext.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata; + +namespace FamilyTreeAPI.Models +{ + public partial class FamilyTreeDBContext : DbContext + { + public FamilyTreeDBContext() + { + } + + public FamilyTreeDBContext(DbContextOptions options) + : base(options) + { + AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true); + } + + public virtual DbSet Persons { get; set; } = null!; + public virtual DbSet RelationShips { get; set; } = null!; + public virtual DbSet Lookups { get; set; } = null!; + public virtual DbSet staff { get; set; } = null!; + + + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity(entity => + { + entity.ToTable("person"); + + entity.Property(e => e.Id).HasColumnName("id"); + + entity.Property(e => e.Address) + .HasMaxLength(120) + .HasColumnName("address"); + + entity.Property(e => e.Alive).HasColumnName("alive"); + entity.Property(e => e.dob).HasColumnName("dob"); + entity.Property(e => e.MotherId).HasColumnName("motherid"); + entity.Property(e => e.FatherId).HasColumnName("fatherid"); + + entity.Property(e => e.FirstName) + .HasMaxLength(70) + .HasColumnName("firstname"); + entity.Property(e => e.Sex) + .HasMaxLength(10) + .HasColumnName("sex"); + entity.Property(e => e.Image) + .HasMaxLength(300) + .HasColumnName("image"); + + entity.Property(e => e.LastName) + .HasMaxLength(70) + .HasColumnName("lastname"); + + entity.Property(e => e.Email) + .HasMaxLength(80) + .HasColumnName("email"); + + entity.Property(e => e.Phone) + .HasMaxLength(30) + .HasColumnName("phone"); + }); + + modelBuilder.Entity(entity => + { + entity.ToTable("relationship"); + + entity.Property(e => e.Id).HasColumnName("id"); + entity.Property(e => e.RelatePersonId).HasColumnName("relatepersonid"); + entity.Property(e => e.PersonId).HasColumnName("personid"); + + }); + + modelBuilder.Entity(entity => + { + entity.ToTable("lookup"); + + entity.Property(e => e.Id).HasColumnName("id"); + + entity.Property(e => e.Code) + .HasMaxLength(10) + .HasColumnName("code"); + + entity.Property(e => e.Description) + .HasMaxLength(80) + .HasColumnName("description"); + + entity.Property(e => e.Lactive).HasColumnName("lactive"); + + entity.Property(e => e.Type).HasMaxLength(20) + .HasColumnName("ltype"); + }); + + modelBuilder.Entity(entity => + { + entity.ToTable("staff"); + entity.Property(e => e.Id).HasColumnName("id"); + + entity.Property(e => e.Email) + .HasMaxLength(80) + .HasColumnName("email"); + + entity.Property(e => e.Firstname) + .HasMaxLength(70) + .HasColumnName("firstname"); + + entity.Property(e => e.Lastname) + .HasMaxLength(70) + .HasColumnName("lastname"); + + entity.Property(e => e.Phone) + .HasMaxLength(30) + .HasColumnName("phone"); + + entity.Property(e => e.Sactive).HasColumnName("sactive"); + + entity.Property(e => e.Spassword) + .HasMaxLength(200) + .HasColumnName("spassword"); + + entity.Property(e => e.Srole).HasColumnName("srole"); + + entity.Property(e => e.Stype).HasColumnName("stype"); + }); + + + + OnModelCreatingPartial(modelBuilder); + } + + partial void OnModelCreatingPartial(ModelBuilder modelBuilder); + } +} diff --git a/API/FamilyTreeAPI/Models/Lookup.cs b/API/FamilyTreeAPI/Models/Lookup.cs new file mode 100644 index 0000000..ffdca78 --- /dev/null +++ b/API/FamilyTreeAPI/Models/Lookup.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; + +namespace FamilyTreeAPI.Models +{ + public partial class Lookup + { + public int Id { get; set; } + public string? Code { get; set; } + public string? Description { get; set; } + public string? Type { get; set; } + public bool? Lactive { get; set; } + } +} diff --git a/API/FamilyTreeAPI/Models/Person.cs b/API/FamilyTreeAPI/Models/Person.cs new file mode 100644 index 0000000..c26be01 --- /dev/null +++ b/API/FamilyTreeAPI/Models/Person.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; + +namespace FamilyTreeAPI.Models +{ + public partial class Person + { + public Person() + { + // Servicetasks = new HashSet(); + } + + public int Id { get; set; } + public string? FirstName { get; set; } + public string? LastName { get; set; } + public string? Email { get; set; } + public string? Phone { get; set; } + public string? Address { get; set; } + public string? Sex { get; set; } + + public string? Image { get; set; } + public bool? Alive { get; set; } + public int? MotherId { get; set; } + public int? FatherId { get; set; } + + public DateTime? dob { get; set; } + + //public virtual ICollection Staffworks { get; set; } + } +} diff --git a/API/FamilyTreeAPI/Models/PersonRelate.cs b/API/FamilyTreeAPI/Models/PersonRelate.cs new file mode 100644 index 0000000..a37e81c --- /dev/null +++ b/API/FamilyTreeAPI/Models/PersonRelate.cs @@ -0,0 +1,9 @@ +namespace FamilyTreeAPI.Models; + +public class RelationShip +{ + public int Id { get; set; } + public int RelatePersonId { get; set; } + public int PersonId { get; set; } + +} \ No newline at end of file diff --git a/API/FamilyTreeAPI/Models/staff.cs b/API/FamilyTreeAPI/Models/staff.cs new file mode 100644 index 0000000..6d7a094 --- /dev/null +++ b/API/FamilyTreeAPI/Models/staff.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; + +namespace FamilyTreeAPI.Models +{ + public partial class staff + { + public staff() + { + // ServicetaskAssigntoNavigations = new HashSet(); + // ServicetaskStaffs = new HashSet(); + // Staffworks = new HashSet(); + } + + public int Id { get; set; } + public string? Firstname { get; set; } + public string? Lastname { get; set; } + public string? Email { get; set; } + public string? Phone { get; set; } + public int? Stype { get; set; } + public int? Srole { get; set; } + public string? Spassword { get; set; } + public bool? Sactive { get; set; } + + // public virtual ICollection ServicetaskAssigntoNavigations { get; set; } + // public virtual ICollection ServicetaskStaffs { get; set; } + //public virtual ICollection Staffworks { get; set; } + } +} diff --git a/API/FamilyTreeAPI/Program.cs b/API/FamilyTreeAPI/Program.cs new file mode 100644 index 0000000..686de7e --- /dev/null +++ b/API/FamilyTreeAPI/Program.cs @@ -0,0 +1,184 @@ +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage; +using Microsoft.IdentityModel.Tokens; +using System.Text; +using DocumentFormat.OpenXml.Office2010.Drawing.Charts; +using FamilyTreeAPI.Authorization; +using FamilyTreeAPI.Entities; +using FamilyTreeAPI.GraphQL.Query; +using FamilyTreeAPI.Interface; +using FamilyTreeAPI.Models; +using FamilyTreeAPI.Repository; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. +#region Services +var services = builder.Services; +services.AddCors(); +services.AddControllers(); +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +services.AddEndpointsApiExplorer(); +services.AddSwaggerGen(); + +var appSettingsSection = builder.Configuration.GetSection("AppSettings"); +services.Configure(appSettingsSection); +var appSettings = appSettingsSection.Get(); +var key = Encoding.ASCII.GetBytes(appSettings.Secret); + +services.AddAuthentication(x => +{ + + x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; + x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; +}) + .AddJwtBearer(x => + { + x.RequireHttpsMetadata = false; + x.SaveToken = true; + x.TokenValidationParameters = new TokenValidationParameters + { + ValidateIssuerSigningKey = true, + IssuerSigningKey = new SymmetricSecurityKey(key), + ValidateIssuer = false, + ValidateAudience = false + }; + }); +services.AddHttpContextAccessor(); +services.AddScoped(); + +services.AddDbContext(options => +{ + + // options.LogTo(s => Console.WriteLine(s)); + //options.UseNpgsql(appSettings.SQLConnectionString); + string? conn = builder.Configuration.GetValue("AppSettings:SQLConnectionString"); + // options.LogTo(s => Console.WriteLine(s)); + if (conn != null) + options.UseNpgsql(conn); +}); + +services.AddGraphQLServer() + .AddFiltering() + .AddSorting() + .AddProjections() + .RegisterDbContextFactory() + + .AddQueryType(); + + +services.AddScoped(); +services.AddScoped(); + +services.AddScoped(); +services.AddScoped(); +services.AddScoped(); +services.AddScoped(); +services.AddScoped(); +/* + + public FamilyTreeDBContext(DbContextOptions options) + : base(options) + { + AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true); + } +services.AddScoped(); + +services.AddScoped(); +*/ + +#endregion + +#region app +var app = builder.Build(); + + +using (var scope = app.Services.CreateScope()) +{ + try + { + var context = scope.ServiceProvider.GetRequiredService(); + var db = context.Database; + if (db != null) + { + + db.Migrate(); + db.EnsureCreated(); + var staff = context.GetService(); + int id = staff.InsertOneUser(); + if (id < 0) + { + var databaseCreator = (context.GetService() as RelationalDatabaseCreator); + if (databaseCreator != null) + { + //if (!databaseCreator.Exists()) + databaseCreator.CreateTables(); + } + id = staff.InsertOneUser(); + } + + } + + + } + catch (Exception e) + { + Console.WriteLine(e.ToString()); + } + +} + + +app.UseMiddleware(); +// Configure the HTTP request pipeline. +//if (app.Environment.IsDevelopment())// +//{ + app.UseSwagger(); + app.UseSwaggerUI(); +//} +// global cors policy +app.UseCors(x => x + .SetIsOriginAllowed(origin => true) + .AllowAnyMethod() + .AllowAnyHeader() + .WithExposedHeaders("Content-Disposition") + .AllowCredentials()); + +app.UseRouting(); +app.UseAuthentication(); +app.UseAuthorization(); +app.MapGraphQL(); +app.MapControllers(); +app.Run(); + +#endregion + +/* + https://www.youtube.com/watch?v=WQFx2m5Ub9M + + +https://csharptotypescript.azurewebsites.net/ +\ services.AddDbContext(options => + options.UseNpgsql(Configuration.GetConnectionString("BloggingContext"))); +dotnet ef dbcontext scaffold "host=postgresdb;port=5432;Database=FamilyTreeDB;Username=postgres;password=Positive~1" Npgsql.EntityFrameworkCore.PostgreSQL -Schemas schema1 --output-dir Models +PM> Scaffold-DbContext "Host=postgresdb;Port=5432;database=FamilyTreeDB;user id=postgres;Password=Positive~1" Npgsql.EntityFrameworkCore.PostgreSQL -OutputDir Models + */ + +/* + graphql +https://www.youtube.com/watch?v=HnXA8RI7Tvc + +query { + family { + nodes{ + id + firstname + lastname + email + + } + } + } +*/ \ No newline at end of file diff --git a/API/FamilyTreeAPI/Properties/launchSettings.json b/API/FamilyTreeAPI/Properties/launchSettings.json new file mode 100644 index 0000000..0e0cdd6 --- /dev/null +++ b/API/FamilyTreeAPI/Properties/launchSettings.json @@ -0,0 +1,41 @@ +{ + "profiles": { + "FamilyTreeAPI": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "dotnetRunMessages": true, + "applicationUrl": "http://localhost:5015" + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Container (Dockerfile)": { + "commandName": "Docker", + "launchBrowser": true, + "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger", + "environmentVariables": { + "ASPNETCORE_HTTP_PORTS": "8080" + }, + "publishAllPorts": true, + "useSSL": false + } + }, + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:4559", + "sslPort": 0 + } + } +} \ No newline at end of file diff --git a/API/FamilyTreeAPI/Repository/Email.cs b/API/FamilyTreeAPI/Repository/Email.cs new file mode 100644 index 0000000..8da5da1 --- /dev/null +++ b/API/FamilyTreeAPI/Repository/Email.cs @@ -0,0 +1,53 @@ +using System.Net.Mail; + +namespace FamilyTreeAPI.Repository +{ + public class Email + { + public static void SendMail(string smtpServer, string sender, string sendTo, + string header, string message, bool mailPriority = false) + { + if (string.IsNullOrEmpty(smtpServer) && string.IsNullOrEmpty(sender) && string.IsNullOrEmpty(sendTo)) + return; + System.Net.Mail.SmtpClient client = new SmtpClient(smtpServer); + MailMessage mailMessage = new MailMessage(); + mailMessage.From = new MailAddress(sender); + + var splitEmails = sendTo.Split(';'); + foreach (string item in splitEmails) + { + mailMessage.To.Add(item); + } + if (mailPriority) + mailMessage.Priority = MailPriority.High; + + // mailMessage.To.Add(sendTo); + mailMessage.Subject = header; + string text = message; + string signature = ""; + try + { + text += "
" + signature; + mailMessage.Body = text; + mailMessage.IsBodyHtml = true; + + } + catch //(Exception ex) + { + // ErrorLogger.logError(ex, "CPA"); + throw; + } + + try + { + string userState = "send to fleet manager"; + client.SendAsync(mailMessage, userState); + + } + catch //(Exception ex) + { + throw; + } + } + } +} diff --git a/API/FamilyTreeAPI/Repository/LookupRepository.cs b/API/FamilyTreeAPI/Repository/LookupRepository.cs new file mode 100644 index 0000000..318c70d --- /dev/null +++ b/API/FamilyTreeAPI/Repository/LookupRepository.cs @@ -0,0 +1,278 @@ +using FamilyTreeAPI.Entities; +using FamilyTreeAPI.Interface; +using FamilyTreeAPI.Models; +using Microsoft.EntityFrameworkCore; + +namespace FamilyTreeAPI.Repository +{ + public class LookupRepository: ILookup + { + private readonly FamilyTreeDBContext _context; + public LookupRepository(FamilyTreeDBContext context) + { + _context = context; + } + + private bool checkDescription(string desc, string type, int id) + { + bool result = false; + if (!string.IsNullOrEmpty(desc)) + { + string ldesc = desc.ToLower(); + int count = _context.Lookups.Where(x => (x.Code ?? "").ToLower() == ldesc + && id != x.Id + ).ToList().Count(); + result = count > 0; + + } + return result; + } + public async Task> SaveLookupAsync(LookupEditDto lookup) + { + int result = -1; + int statusCode = 0; + string desc = lookup.Description.Trim(); + string error = ""; + try + { + Lookup model = null!; + bool ok = !checkDescription(desc, lookup.Type, lookup.Id); + if (ok) + { + if (lookup.Id < 1) + { + model = new(); + model.Code = lookup.CodeId; + model.Description = desc; + model.Type = lookup.Type; + model.Lactive = lookup.Active; + + _context.Lookups.Add(model); + + } + else + { + Lookup? model1 = await _context.Lookups.FindAsync(lookup.Id); + if (model1 != null) + { + model1.Description = desc; + model1.Code = lookup.CodeId; + model1.Lactive = lookup.Active; + } + + } + + await _context.SaveChangesAsync(); + if (model != null) + result = model.Id; + + statusCode = 1; + } + else + { + statusCode = 0; + error = "description is already in database"; + } + } + catch (Exception ex) + { + error = ex.ToString(); + statusCode = -1; + + } + return new ResultModel() + { + Data = result, + StatusCode = statusCode, + Message = error + }; + } + public async Task> GetLookupEditByIdAsync(int id, string type) + { + List resultList; + LookupEditDto item = null!; + int statusCode = 0; + string error = ""; + try + { + resultList = await _context.Lookups.Where(x => x.Type == type + && (x.Id == id)) + .Select(item => new LookupEditDto + { + CodeId = string.IsNullOrEmpty(item.Code) ? "0": item.Code, + Active = item.Lactive ?? false, + Id = item.Id, + Description = item.Description ?? "" + }) + .ToListAsync(); + + if (resultList.Count > 0) + item = resultList[0]; + statusCode = 1; + } + catch (Exception ex) + { + error = ex.ToString(); + statusCode = -1; + + } + return new ResultModel() + { + Data = item, + StatusCode = statusCode, + Message = error + }; + } + public async Task>> GetLookupAsync(string type) + { + List resultList = new(); + int statusCode = 0; + string error = ""; + try + { + resultList = await _context.Lookups.Where(x => x.Type == type && ((x.Lactive ?? false) == true)) + .Select(item => new LookupDto { Id = item.Id, CodeId = item.Code ?? "", Description = item.Description ?? "" }) + .ToListAsync(); + + resultList.Sort((x, y) => x.Description.CompareTo(y.Description)); + + statusCode = 1; + } + catch (Exception ex) + { + error = ex.ToString(); + statusCode = -1; + + } + return new ResultModel>() + { + Data = resultList, + StatusCode = statusCode, + Message = error + }; + } + + public async Task>> GetLookupDicAsync(string type) + { + Dictionary resultList = new(); + + int statusCode = 0; + string error = ""; + try + { + resultList = await _context.Lookups.Where(x => x.Type == type && ((x.Lactive ?? false) == true)) + .Select(item => new LookupDto { Id = item.Id, CodeId = item.Code ?? "", Description = item.Description ?? "" }) + .ToDictionaryAsync(x => x.CodeId); + + + statusCode = 1; + } + catch (Exception ex) + { + error = ex.ToString(); + statusCode = -1; + + } + return new ResultModel>() + { + Data = resultList, + StatusCode = statusCode, + Message = error + }; + } + public async Task>> GetLookupEditAsync(string type) + { + List resultList = new(); + int statusCode = 0; + string error = ""; + try + { + resultList = await _context.Lookups.Where(x => x.Type == type) + .Select(item => new LookupEditDto + { + Id = item.Id, + Active = item.Lactive ?? false, + CodeId = item.Code ?? "", + Type = item.Type ?? "", + Description = item.Description ?? "" + }) + .ToListAsync(); + + resultList.Sort((x, y) => x.Description.CompareTo(y.Description)); + + statusCode = 1; + } + catch (Exception ex) + { + error = ex.ToString(); + statusCode = -1; + + } + return new ResultModel>() + { + Data = resultList, + StatusCode = statusCode, + Message = error + }; + } + public async Task>> GetPersonsAsync() + { + List resultList = new(); + int statusCode = 0; + string error = ""; + try + { + resultList = await _context.Persons.Where(x => x.Alive == true) + .Select(item => new LookupDto { Id = item.Id, CodeId = item.Id.ToString(), Description = item.FirstName ?? "" }) + .ToListAsync(); + + resultList.Sort((x, y) => x.Description.CompareTo(y.Description)); + + statusCode = 1; + } + catch (Exception ex) + { + error = ex.ToString(); + statusCode = -1; + + } + return new ResultModel>() + { + Data = resultList, + StatusCode = statusCode, + Message = error + }; + } + + public async Task>> GetStaffAsync() + { + List resultList = new(); + int statusCode = 0; + string error = ""; + try + { + resultList = await _context.staff.Where(x => x.Sactive == true) + .Select(item => new LookupDto { Id = item.Id, CodeId = item.Firstname ?? "", Description = item.Lastname ?? "" }) + .ToListAsync(); + + resultList.Sort((x, y) => x.Description.CompareTo(y.Description)); + + statusCode = 1; + } + catch (Exception ex) + { + error = ex.ToString(); + statusCode = -1; + + } + return new ResultModel>() + { + Data = resultList, + StatusCode = statusCode, + Message = error + }; + } + + + } +} diff --git a/API/FamilyTreeAPI/Repository/PersonRepository.cs b/API/FamilyTreeAPI/Repository/PersonRepository.cs new file mode 100644 index 0000000..42f46ba --- /dev/null +++ b/API/FamilyTreeAPI/Repository/PersonRepository.cs @@ -0,0 +1,657 @@ +using DocumentFormat.OpenXml.EMMA; +using DocumentFormat.OpenXml.Office.CustomUI; +using DocumentFormat.OpenXml.Office2010.Excel; +using FamilyTreeAPI.Entities; +using FamilyTreeAPI.Helper; +using FamilyTreeAPI.Interface; +using FamilyTreeAPI.Models; +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Threading.Tasks; + +namespace FamilyTreeAPI.Repository; + +public partial class PersonRepository : IPerson +{ + private readonly FamilyTreeDBContext _context; + private readonly IRelationShipd _relationship; + private readonly IHttpContextAccessor _httpContext; + private readonly IConfiguration _config; + const string dateFormat = "yyyy-MM-dd"; + public PersonRepository(IConfiguration config, FamilyTreeDBContext context, + IRelationShipd relationship, + IHttpContextAccessor httpContext) + { + _context = context; + _relationship = relationship; + _config = config; + _httpContext = httpContext; + } + + private PersonDto FillDto(Person model) + { + PersonDto dto = new(); + dto.Id = model.Id; + dto.FirstName = model.FirstName; + dto.LastName = model.LastName; + dto.Email = model.Email; + dto.Address = model.Address; + dto.Phone = model.Phone; + dto.Alive = model.Alive; + dto.Image = model.Image; + dto.FatherId = model.FatherId; + dto.MotherId = model.MotherId; + dto.Sex = model.Sex; + dto.dob = Helpers.DateToStr(model.dob); + /* + dto.AddedOn = model.AddedOn; + + dto.RoleType = model.RoleType; + + */ + + return dto; + } + private Person FillModel(Person model, PersonDto dto) + { + + model.FirstName = dto.FirstName; + model.LastName = dto.LastName; + model.Email = dto.Email; + model.Address = dto.Address; + model.Phone = dto.Phone; + model.Alive = dto.Alive; + model.Image = dto.Image; + model.FatherId = dto.FatherId; + model.MotherId = dto.MotherId; + model.Sex = dto.Sex; + model.dob = Helpers.DateToDateTime(dto.dob); + + return model; + } + + public async Task>> GetDicFamily() + { + + Dictionary list = new(); + int statuscode = 0; + string error = ""; + PersonDto item; + Person jitem; + try + { + //list = await _context.LoadStaffAsync(criteria); + var jlist = await _context.Persons.ToListAsync(); + for (int i = 0; i < jlist.Count; i++) + { + jitem = jlist[i]; + item = FillDto(jitem); + list.Add(item.Id,item); + } + statuscode = 1; + //list.Sort((x, y) => x.Code.CompareTo(y.Code)); + } + catch (Exception ex) + { + + error = ex.ToString(); + statuscode = -1; + + } + return new ResultModel< Dictionary > () + { + Data = list, + StatusCode = statuscode, + Message = error + }; + } + + private async Task> GetChildrens(int FatherId, int MotherId) + { + List list = new(); + PersonDto item; + Person jitem; + var jlist = await _context.Persons.Where(x => + (x.MotherId == FatherId && x.FatherId == MotherId) || + (x.FatherId == FatherId && x.MotherId == MotherId)).ToListAsync(); + for (int i = 0; i < jlist.Count; i++) + { + jitem = jlist[i]; + item = FillDto(jitem); + list.Add(item); + } + return list; + } + public async Task>> GetChildren(ChildCriteria criteria) + { + List list = new(); + int statuscode = 0; + string error = ""; + PersonDto item; + Person jitem; + try + { + //list = await _context.LoadStaffAsync(criteria); + list = await GetChildrens(criteria.FatherId, criteria.MotherId); + statuscode = 1; + //list.Sort((x, y) => x.Code.CompareTo(y.Code)); + } + catch (Exception ex) + { + + error = ex.ToString(); + statuscode = -1; + + } + return new ResultModel>() + { + Data = list, + StatusCode = statuscode, + Message = error + }; + } + + public async Task>> GetPerson(PersonCriteria criteria) + { + + List list = new(); + List? jlist; + int statuscode = 0; + string error = ""; + PersonDto item; + Person jitem; + string firstName = criteria.FirstName; + string lastName = criteria.LastName; + int Id = criteria.Id; + try + { + //list = await _context.LoadStaffAsync(criteria); + if (!string.IsNullOrEmpty(firstName)) + { + firstName = firstName.ToLower(); + jlist = await _context.Persons.Where(x => + EF.Functions.Like(x.FirstName.ToLower(),firstName + "%")).ToListAsync(); + } + else if (!string.IsNullOrEmpty(firstName) && !string.IsNullOrEmpty(lastName)) + { + firstName = firstName.ToLower(); + lastName = lastName.ToLower(); + jlist = await _context.Persons.Where(x => EF.Functions.Like(x.FirstName.ToLower(),firstName + "%") + && EF.Functions.Like(x.LastName.ToLower(),lastName + "%")).ToListAsync(); + } + else + jlist = await _context.Persons.ToListAsync(); + + for (int i = 0; i < jlist.Count; i++) + { + jitem = jlist[i]; + item = FillDto(jitem); + list.Add(item); + } + statuscode = 1; + //list.Sort((x, y) => x.Code.CompareTo(y.Code)); + } + catch (Exception ex) + { + + error = ex.ToString(); + statuscode = -1; + + } + return new ResultModel>() + { + Data = list, + StatusCode = statuscode, + Message = error + }; + } + + private async Task GetPerson(int id) + { + Person rval = await _context.Persons.FindAsync(id); + return rval; + } + public async Task>> GetByFamilyAsync(int id) + { + int statuscode = 0; + string error = ""; + string key = ""; + string data = ""; + string type = "default"; + string pName = ""; + Person person; + TreeNode citem; + TreeNode child; + TreeNode node = new(); + node.Children = new(); + PersonDto dto = new(); + try + { + var item = await _context.Persons.FindAsync(id); + if (item == null) + { + statuscode = -1; + } + else + { + + node.Label = item.FirstName; + node.Key = item.Id.ToString(); + node.Type = type; + node.Expanded = true; + node.Data = item.Sex; + statuscode = 1; + dto = FillDto(item); + await GetPartnerChildrens(dto, node); + } + } + catch (Exception ex) + { + error = ex.ToString(); + statuscode = -1; + + } + return new ResultModel> () + { + Data = node, + StatusCode = statuscode, + Message = error + }; + } + + private async Task GetPartnerChildrens(PersonDto person, TreeNode node) + { + /*****************************/ + ResultModel> rlist = await _relationship.GetByPersonIdAsync(person.Id); + List relationShips = rlist.Data; + RelationShipDto relate; + string type = node.Type; + TreeNode child; + PersonDto dto; + Person pe; + int fatherId, motherId; + fatherId = motherId = 0; + string data = person.Sex; + TreeNode citem; + string pName = ""; + string key = ""; + for (int i = 0; i < relationShips.Count; i++) + { + relate = relationShips[i]; + if (relate.PersonId == person.Id) + { + if (data == "M") + { + fatherId = relate.PersonId; + motherId = relate.RelatePersonId; + + } + else + { + + fatherId = relate.RelatePersonId; + motherId = relate.PersonId; + } + } + else + { + if (data == "M") + { + fatherId = relate.PersonId; + motherId = relate.RelatePersonId; + } + else + { + fatherId = relate.RelatePersonId; + motherId = relate.PersonId; + } + } + //get children + List children = await GetChildrens(fatherId, motherId); + if (children.Count > 0) + { + if (person.Id != motherId) + { + //kham + if (motherId > 0) + { + pe = await GetPerson(motherId); + pName = pe.FirstName; + key = motherId.ToString(); + data = "F"; + } + } + else + { + if (fatherId > 0) + { + pe = await GetPerson(fatherId); + pName = pe.FirstName; + key = fatherId.ToString(); + data = "M"; + } + } + + citem = new TreeNode(); + citem.Label = pName; + citem.Expanded = true; + citem.Data = data; + citem.Type = type; + citem.Key = key; + citem.Children = new List>(); + node.Children.Add(citem); + for (int j = 0; j < children.Count; j++) + { + dto = children[j]; + child = new TreeNode(); + child.Expanded = true; + child.Type = type; + child.Label = dto.FirstName; + child.Key = dto.Id.ToString(); + child.Data = dto.Sex; + child.Children = new(); + citem.Children.Add(child); + //get child partner and repeat this. + await GetPartnerChildrens(dto, child); + } + } + + } + + /*****************************************/ + } + + public async Task> GetByIdAsync(int id) + { + int statuscode = 0; + string error = ""; + PersonDto dto = new(); + try + { + var item = await _context.Persons.FindAsync(id); + if (item == null) + { + statuscode = -1; + } + else + { + dto = FillDto(item); + statuscode = 1; + ResultModel> rlist = await _relationship.GetByPersonIdAsync(dto.Id); + dto.RelationShips = rlist.Data; + statuscode = rlist.StatusCode; + error = rlist.Message; + } + } + catch (Exception ex) + { + error = ex.ToString(); + statuscode = -1; + + } + return new ResultModel() + { + Data = dto, + StatusCode = statuscode, + Message = error + }; + } + private string GetDateTimeNow() + { + return DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); + } + + /// + /// need filename to get extension file and family name for id combine to + /// name to table. + /// + /// + /// + /// + private string SaveFile(string fileName, int familyId, string base64) + { + string path = ""; + + int statusCode = 0; + string error = ""; + string sdate = GetCurrentDateTime(); + string filename, extention; + filename = extention = ""; + int first = base64.IndexOf("base64,") +"base64,".Length; + int last = base64.Length; + string rbase = base64.Substring(first, last - first); + + byte[] newBytes = Convert.FromBase64String(rbase); + path = _config["AppSettings:ImageFolder"]; + if (!Directory.Exists(path)) + { + Directory.CreateDirectory(path); + } + extention = System.IO.Path.GetExtension(fileName); + filename = familyId + "_" + sdate + extention; + string fullpath = System.IO.Path.Combine(path, filename); + /* + using (var fileStream = new FileStream(fullpath, FileMode.Create)) + { + StreamWriter writer = new StreamWriter(fileStream); + writer.Write(newBytes); + //writer.BaseStream.Write(bytes, 0, bytes.Length); + + } + */ + using (MemoryStream ms = new MemoryStream(newBytes)) + { + Image image = Image.FromStream(ms); + image.Save(fullpath); + } + return filename; + } + public async Task> SaveAsync(PersonForSave container) + { + int result = default(int); + int statuscode = 0; + string error = ""; + + HttpContext? httpContext = _httpContext.HttpContext; + string loginName = ""; + if (httpContext != null) + { + UserDto? user = (UserDto?)httpContext.Items["User"]; + if (user != null) + loginName = user.FirstName + " " + user.LastName; + } + PersonDto item = container.Person; + try + { + + + Person model; + if (item.Id < 1) + { + model = new(); + model = FillModel(model, item); + + // model.Active = true; + // model.AddedBy = loginName; + // model.AddedOn = DateTime.Now; + _context.Persons.Add(model); + var successid = await _context.SaveChangesAsync(); + result = model.Id; + if (!string.IsNullOrEmpty(container.FileName)) + { + + string image = SaveFile(container.FileName, result, container.FormData); + // update the image in table to new + model.Image = image; + await _context.SaveChangesAsync(); + } + } + else + { + var model1 = await _context.Persons.FindAsync(item.Id); + if (model1 != null) + { + model1 = FillModel(model1, item); + + result = item.Id; + if (!string.IsNullOrEmpty(container.FileName)) + { + string image = SaveFile(container.FileName, result, container.FormData); + // update the image in table to new + model1.Image = image; + } + var successid = await _context.SaveChangesAsync(); + } + // model.LastModified = DateTime.Now; + // model.LastModifiedId = loginName; + } + + statuscode = 1; + if (item.RelationShips != null) + { + ResultModel rresult = await this._relationship.SaveAsync(item.RelationShips); + statuscode = rresult.StatusCode; + error += rresult.Message; + } + } + catch (Exception ex) + { + error = ex.ToString(); + statuscode = -1; + } + //var dto = await Task.Run(() => result); + return new ResultModel() + { + Data = result, + StatusCode = statuscode, + Message = error + }; + } + + public async Task> DeleteAsync(int id) + { + int result = 1; + int statuscode = 0; + string error = ""; + try + { + var model = await _context.Persons.FindAsync(id); + if (model != null) + _context.Persons.Remove(model); + + var successCount = await _context.SaveChangesAsync(); + statuscode = 1; + result = 1; + } + catch (Exception ex) + { + error = ex.ToString(); + statuscode = -1; + + } + //var dto = await Task.Run(() => result); + return new ResultModel() + { + Data = result, + StatusCode = statuscode, + Message = error + }; + } + + public Task> UploadImage(UploadCriteria criteria) { + return UploadImagep(criteria); + } + + private string GetCurrentDateTime() + { + DateTime now = DateTime.Now; + return now.ToString("yyyyMMdd"); + } + + private async Task> UploadImagep(UploadCriteria criteria) + { + string path = ""; + + int statusCode = 0; + string error = ""; + string sdate = GetCurrentDateTime(); + string filename, extention; + filename = extention = ""; + try + { + if (criteria.File.Length > 0) + { + path = _config["AppSettings:ImageFolder"]; + if (!Directory.Exists(path)) + { + Directory.CreateDirectory(path); + } + extention = System.IO.Path.GetExtension(criteria.FileName); + filename = criteria.FamilyId + "_" + sdate + extention; + + using (var fileStream = new FileStream(System.IO.Path.Combine(path, filename), FileMode.Create)) + { + await criteria.File.CopyToAsync(fileStream); + } + + statusCode = 1; + } + else + { + + statusCode = -1; + error = "file contain is empty"; + } + } + catch (Exception ex) + { + statusCode = -1; + error = ex.Message; + + } + + return new ResultModel() + { + Data = filename, + StatusCode = statusCode, + Message = error + }; + } + + public ResultModel DeleteUploadFile(DeleteFileCriteria criteria) + { + int result = -1; + int statusCode = 0; + string error = ""; + var filePath = _config["AppSettings:ImageFolder"]; + var myfile = filePath + "\\" + criteria.Filename; + try + { + File.Delete(myfile); + result = 1; + + statusCode = 1; + Person? model = _context.Persons.Find(criteria.FamilyId); + if (model != null) + { + model.Image = null; + _context.SaveChanges(); + } + } + catch (Exception e) + { + result = -1; + statusCode = -1; + error += e.Message; + } + return new ResultModel() + { + Data = result, + StatusCode = statusCode, + Message = error + }; + } +} diff --git a/API/FamilyTreeAPI/Repository/PersonRepository.tree.cs b/API/FamilyTreeAPI/Repository/PersonRepository.tree.cs new file mode 100644 index 0000000..ef4e879 --- /dev/null +++ b/API/FamilyTreeAPI/Repository/PersonRepository.tree.cs @@ -0,0 +1,139 @@ +using FamilyTreeAPI.Entities; +using FamilyTreeAPI.Models; +using Microsoft.EntityFrameworkCore; +using System.Drawing.Text; +namespace FamilyTreeAPI.Repository; + +public partial class PersonRepository +{ + private TreeNode PopulateItem(Person model) + { + TreeNode treeNode = new(); + treeNode.Label = model.FirstName; + treeNode.Data = model.Id.ToString(); + treeNode.Key = model.Id.ToString(); + treeNode.Children = new(); + return treeNode; + } + private bool UseFamilyCriteria(FamilyCriteria criteria, Person person) + { + bool result = false; + if (criteria.UseFather && criteria.UseMother) + { + result = person.FatherId.GetValueOrDefault(0) == 0 + && person.MotherId.GetValueOrDefault(0) == 0; + } + else if (criteria.UseFather) + { + result = person.FatherId.GetValueOrDefault(0) == 0; + } + else if (criteria.UseMother) + { + result = person.MotherId.GetValueOrDefault(0) == 0; + } + + return result; + } + private bool UseChildFamilyCriteria(FamilyCriteria criteria, int id, Person person) + { + bool result = false; + int parentId; + if (criteria.UseFather) + { + parentId = person.FatherId.GetValueOrDefault(0); + result = parentId == id; + } + else if (criteria.UseMother) + { + parentId = person.MotherId.GetValueOrDefault(0); + result = parentId == id; + } + + return result; + } + private List SplitListOfTopLevel(List list, FamilyCriteria criteria) + { + List result = new(); + Person item; + for (int i = list.Count - 1; i > -1; i--) + { + item = list[i]; + if (UseFamilyCriteria(criteria,item)) + { + result.Add(item); + list.RemoveAt(i); + } + + } + return result; + } + + private List GetParentId(List list,int id, FamilyCriteria criteria, Func conditionFn) + { + List result = new(); + Person item; + for (int i = list.Count - 1; i > -1; i--) + { + item = list[i]; + if (conditionFn(criteria,id, item)) + { + result.Add(item); + list.RemoveAt(i); + } + + } + return result; + } + + private List> PopulateChild(FamilyCriteria criteria,int id, List childList, Func conditionFn) + { + Person person; + TreeNode treeNode; + List> list = new(); + List children = GetParentId(childList,id, criteria, conditionFn); + for (int i = 0; i < children.Count; i++) + { + person = children[i]; + treeNode = PopulateItem(person); + list.Add(treeNode); + // get children of this one too + treeNode.Children = PopulateChild(criteria, person.Id, childList, UseChildFamilyCriteria); + } + return list; + } + public async Task>> > GetFamilyTreeBy(FamilyCriteria criteria) + { + int statusCode = -1; + string error = ""; + Person person; + TreeNode treeNode; + List> data = new(); + try + { + var personList = await _context.Persons.ToListAsync(); + var topList = SplitListOfTopLevel(personList, criteria); + for (int i = 0; i < topList.Count; i++) + { + person = topList[i]; + treeNode = PopulateItem(person); + data.Add(treeNode); + treeNode.Children = PopulateChild(criteria, person.Id, personList, UseChildFamilyCriteria); + + } + statusCode = 1; + } + catch (Exception ex) + { + error = ex.ToString(); + statusCode = -1; + } + + return new ResultModel>> + { + Data = data, + StatusCode = statusCode, + Message = error + }; + + } +} diff --git a/API/FamilyTreeAPI/Repository/RelationShipRepository.cs b/API/FamilyTreeAPI/Repository/RelationShipRepository.cs new file mode 100644 index 0000000..5ddfa46 --- /dev/null +++ b/API/FamilyTreeAPI/Repository/RelationShipRepository.cs @@ -0,0 +1,184 @@ +using FamilyTreeAPI.Entities; +using FamilyTreeAPI.Interface; +using FamilyTreeAPI.Models; +using Microsoft.EntityFrameworkCore; + +namespace FamilyTreeAPI.Repository; + +public class RelationShipRepository: IRelationShipd +{ + private readonly FamilyTreeDBContext _context; + private readonly IHttpContextAccessor _httpContext; + private readonly IConfiguration _config; + public RelationShipRepository(IConfiguration config, FamilyTreeDBContext context, IHttpContextAccessor httpContext) + { + _context = context; + _config = config; + _httpContext = httpContext; + } + private RelationShip FillModel(RelationShipDto dto, RelationShip model) + { + model.RelatePersonId = dto.RelatePersonId; + model.PersonId = dto.PersonId; + return model; + } + + private RelationShipDto FillDto(RelationShip model) + { + RelationShipDto dto = new(); + dto.Id = model.Id; + dto.PersonId = model.PersonId; + dto.RelatePersonId = model.RelatePersonId; + return dto; + } + public async Task> SaveAsync(List list) + { + int statusCode = 0; + string error = ""; + RelationShipDto item; + RelationShip model; + try + { + for (int i = 0; i < list.Count; i++) + { + item = list[i]; + if (item.Id < 1) + { + model = new(); + model = FillModel(item, model); + _context.RelationShips.Add(model); + } + else if (item.State == enumState.Modify) + { + RelationShip? RelationShip = await _context.RelationShips.FindAsync(item.Id); + if (RelationShip != null) + { + RelationShip = FillModel(item, RelationShip); + } + } + else if (item.State == enumState.Delete) + { + RelationShip? RelationShip = await _context.RelationShips.FindAsync(item.Id); + if (RelationShip != null) + { + _context.RelationShips.Remove(RelationShip); + } + } + } + + statusCode = 1; + await _context.SaveChangesAsync(); + } + catch (Exception ex) + { + error = ex.ToString(); + statusCode = -1; + } + + return new ResultModel + { + StatusCode = statusCode, + Data = statusCode, + Message = error + }; + } + + public async Task>> GetByPersonIdAsync(int personId) + { + string error = ""; + int statusCode = -1; + List list = new(); + RelationShipDto dto; + RelationShip model; + try + { + + + var mlist = await _context.RelationShips.Where(x => x.PersonId == personId).ToListAsync(); + for (int i = 0; i < mlist.Count; i++) + { + model = mlist[i]; + dto = FillDto(model); + list.Add(dto); + } + var rlist = await _context.RelationShips.Where(x => x.RelatePersonId == personId).ToListAsync(); + for (int i = 0; i < rlist.Count; i++) + { + model = rlist[i]; + dto = FillDto(model); + list.Add(dto); + } + + statusCode = 1; + } + catch (Exception ex) + { + error = ex.ToString(); + statusCode = -1; + } + + return new ResultModel> + { + StatusCode = statusCode, + Data = list, + Message = error + }; + } + + public async Task> GetByIdAsync(int Id) + { + string error = ""; + int statusCode = -1; + + RelationShipDto dto =new(); + RelationShip? model; + try + { + + + model = await _context.RelationShips.FindAsync(Id); + if (model != null) + dto = FillDto(model); + + } + catch (Exception ex) + { + error = ex.ToString(); + statusCode = -1; + } + + return new ResultModel + { + StatusCode = statusCode, + Data = dto, + Message = error + }; + } + + public async Task> DeleteAsync(int Id) + { + int result = -1; + string error = ""; + int statusCode = -1; + try + { + RelationShip? RelationShip = await _context.RelationShips.FindAsync(Id); + if (RelationShip != null) + { + _context.RelationShips.Remove(RelationShip); + } + } + catch (Exception e) + { + error = e.ToString(); + statusCode = -1; + } + + return new ResultModel + { + StatusCode = statusCode, + Data = result, + Message = error + }; + } +} \ No newline at end of file diff --git a/API/FamilyTreeAPI/Repository/ReportRepository.cs b/API/FamilyTreeAPI/Repository/ReportRepository.cs new file mode 100644 index 0000000..59e629a --- /dev/null +++ b/API/FamilyTreeAPI/Repository/ReportRepository.cs @@ -0,0 +1,272 @@ +using FamilyTreeAPI.Entities; +using FamilyTreeAPI.Interface; +using FamilyTreeAPI.Models; +using SpreadsheetLight; +using System.Linq; +using DocumentFormat.OpenXml.Spreadsheet; + +namespace FamilyTreeAPI.Repository; + +public class ReportRepository : IReport +{ + private enum EnumStaffWork + { + Id = 0, + FirstName, + LastName, + StartDate, + startTime, + stopTime, + Task, + Job, + Hour + }; + private readonly FamilyTreeDBContext _context; + //private readonly ILookup _lookup; + const string DateTimeFormat = "dd/MM/yyyy HH:mm"; + const string TimeFormat = "HH:mm"; + const string DateFormat = "dd/MM/yyyy"; + private readonly IStaff _staff; + private Dictionary _dstaff; + private readonly ILookup _lookup; + private string DisplayTime(DateTime date) + { + string result = ""; + if (date != DateTime.MinValue) + result = date.ToString(TimeFormat); + return result; + } + + private string DisplayDateTime(DateTime? date) + { + string result = ""; + if (date != DateTime.MinValue) + result = date!.Value.ToString(DateFormat); + return result; + } + private string FormatTime(int hour) + { + string result; + if (hour < 10) + result = "0" + hour.ToString() + ":00"; + else + result = hour.ToString() + ":00"; + return result; + } + + public ReportRepository(FamilyTreeDBContext context, ILookup lookup,IStaff staff) + { + _context = context; + _lookup = lookup; + _staff = staff; + } + + // public async Task> GetStaffWorkReportAsync(StaffWorkCriteria criteria) + // { + // //var data = await _lookup.GetLookupDicAsync("TypeOfUse"); + // //if (data != null) + // //{ + // // this._ServiceTypeDic = data.Data; + // //} + // var rstaff = await _staff.GetDicStaffs(); + // if (rstaff.StatusCode == 1) + // _dstaff = rstaff.Data; + // //data = await _lookup.GetLookupDicAsync("Status"); + // //if (data != null) + // //{ + // // this._StatusDic = data.Data; + // //} + // int statusCode = 1; + // int col = 1; + // int row = 1; + // SLDocument sl = new SLDocument(); + // SLStyle styleRed = sl.CreateStyle(); + // styleRed.SetFontColor(System.Drawing.Color.Red); + + + + // //first sheetName + // sl.RenameWorksheet(SLDocument.DefaultFirstSheetName, "Staff Work Report"); + // SLStyle style = sl.CreateStyle(); + // style.SetFontUnderline(DocumentFormat.OpenXml.Spreadsheet.UnderlineValues.Single); + // style.SetFontBold(true); + // style.SetFont("Calibri", 14); + // sl.SetCellStyle(1, 3, style); + // sl.SetRowHeight(1, 1, 25); + // sl.SetCellValue(row++, 3, "Staff Work"); + // // sl.SetCellValue(row, 3, "From Date: " + fromDate.ToString(DateFormat) + " - " + toDate.ToString(DateFormat)); + // sl.SetCellValue(1, 6, " " + DateTime.Now.ToString("ddd dd/MM/yyyy HH:mm")); + // // sl.SetCellValue(row, 6, "Hospital: " + facility); + + + // int startTitleRow = 3; + // row = startTitleRow; + // //make the title bold + // style = sl.CreateStyle(); + // style.Font.Bold = true; + // sl.SetRowStyle(startTitleRow, style); + // //set it height + // sl.SetRowHeight(startTitleRow, startTitleRow, 25); + // // add the title first + + // sl.SetCellValue(row, (int)EnumStaffWork.Id, "Id"); + // sl.SetCellValue(row, (int) EnumStaffWork.FirstName, "FirstName"); + // sl.SetCellValue(row, (int) EnumStaffWork.LastName, "SurName"); + // sl.SetCellValue(row, (int) EnumStaffWork.StartDate, "StartDate"); + // sl.SetCellValue(row, (int)EnumStaffWork.startTime, "Start Time"); + // sl.SetCellValue(row, (int)EnumStaffWork.stopTime, "Stop Time"); + // sl.SetCellValue(row, (int)EnumStaffWork.Hour, "Hour"); + + // /* + // style = sl.CreateStyle(); + // style.SetFontColor(System.Drawing.Color.Blue); + // sl.SetRowStyle(row, style); + // */ + // sl.FreezePanes(row, 0); //frozen the row. + // //wrap text on is concession Expiry + // style = sl.CreateStyle(); + // style.SetWrapText(true); + // sl.SetColumnStyle(4, style); + + // //format date + + // // style = sl.CreateStyle(); + // //style.FormatCode = DateTimeFormat; + // // sl.SetColumnStyle(1, style); + // //make column width + // sl.SetColumnWidth((int)EnumStaffWork.Id, 10); + // sl.SetColumnWidth((int)EnumStaffWork.FirstName, 30); + // sl.SetColumnWidth((int)EnumStaffWork.LastName, 30); + // sl.SetColumnWidth((int)EnumStaffWork.StartDate, 25); + // sl.SetColumnWidth((int)EnumStaffWork.startTime, 10); + // sl.SetColumnWidth((int)EnumStaffWork.stopTime, 10); + // sl.SetColumnWidth((int)EnumStaffWork.Hour, 20); + + + // style = sl.CreateStyle(); + // style.FormatCode = DateFormat; + // sl.SetColumnStyle(5, style); + // //style = sl.CreateStyle(); + // //style.FormatCode = DateTimeFormat; + // //sl.SetColumnStyle(1, style); + // sl.SetColumnWidth(10, 20); + + // int startRowFrom = startTitleRow + 1; + + //// DateTime ttdate = toDate.ToLocalTime(); //add 1 days so if to inclusive todate 02/09 + // var container = await _context.LoadStaffWorkAsync(criteria); //fromDate.ToLocalTime(), ttdate.AddDays(1), typeOfCall); + // var list = container; + // list.Sort((x, y) => x.StartDate.Value.CompareTo(y.StartDate.Value)); + // StaffWorkViewDto item; + // DateTime startTime, stopTime; + // double hour =0; + // string fname, lname; + // StaffWorkDetailDto detail; + // for (int i = 0; i < list.Count; i++) + // { + + // item = list[i]; + // row = i + startRowFrom; + // (startTime, stopTime ) = GetSSTime(item); + // if (item.Details != null) + // for (int j = 0; j < item.Details.Count; j++) + // { + // detail = item.Details[j]; + // hour += detail.TotalMinuts(); + // } + // (fname, lname) = GetStaffName(item.StaffId.Value); + // sl.SetCellValue(row, (int)EnumStaffWork.StartDate, item.StartDate.Value); + // sl.SetCellValue(row, (int)EnumStaffWork.FirstName, fname); + // sl.SetCellValue(row, (int)EnumStaffWork.LastName, lname); + // sl.SetCellValue(row, (int)EnumStaffWork.startTime, startTime); + // sl.SetCellValue(row, (int)EnumStaffWork.stopTime, stopTime); + // sl.SetCellValue(row, (int)EnumStaffWork.Hour, hour/60); + + // } + + // byte[] array; + // // sl.SaveAs("C:\\Temp\\Report\\ConcessionValidationDate" + DateTime.Now.ToString("yyyyMMddHHmm") + ".xlsx"); + // using (MemoryStream ws = new MemoryStream()) + // { + // sl.SaveAs(ws); + // array = ws.ToArray(); + // /* + // using (FileStream fs = new FileStream("c:\\temp\\Report\\conReport.xlsx",FileMode.Create,FileAccess.Write)) + // { + // ws.WriteTo(fs); + // fs.Close(); + // } + // */ + // ws.Close(); + // } + + // var result = new FileContent + // { + // Content = array, + // FileName = "StaffWork_Report" + DateTime.Now.ToString("yyyy_MM_dd_HH_mm") + ".xlsx", + // }; + + // return new ResultModel + // { + // Data = result, + // StatusCode = statusCode + // }; + + // } + private (string, string) GetStaffName(int staffId) + { + + StaffDto staff; + if (_dstaff.ContainsKey(staffId)) + { + staff = _dstaff[staffId]; + return ( staff.Firstname ?? "" , staff.Lastname ?? ""); + } + return ("",""); + } + //private (DateTime firstTime, DateTime lastTime) GetSSTime(StaffWorkViewDto item) + //{ + // DateTime sTime, lTime; + // DateTime? value; + // int count = 0; + // sTime = lTime = DateTime.Now; + // List detail = item.Details ?? new(); + // count = detail.Count; + // if (count > 0) + // { + + // value = detail[0].StartTime; + // if (value.HasValue) + // { + // sTime = value.Value; + // } + // value = detail[count - 1].StopTime; + // if (value.HasValue) + // { + // lTime = value.Value; + // } + // } + // return (sTime, lTime); + //} + private string GetValue(string item, string text) + { + string result = item; + if (!string.IsNullOrEmpty(text)) + result = text; + return result; + } + //private string GetPools(List poolIds) + //{ + // string result = ""; + // string id; + // for (int i = 0; i < poolIds.Count; i++) + // { + // id = poolIds[i]; + // if (this._PoolDic.ContainsKey(id)) + // result += _PoolDic[id].Description + ","; + // } + // result = result.Remove(result.Length-1, 1); + // return result; + //} + +} diff --git a/API/FamilyTreeAPI/Repository/Seed.cs b/API/FamilyTreeAPI/Repository/Seed.cs new file mode 100644 index 0000000..3569125 --- /dev/null +++ b/API/FamilyTreeAPI/Repository/Seed.cs @@ -0,0 +1,83 @@ +using FamilyTreeAPI.Models; +using Microsoft.EntityFrameworkCore; + +namespace FamilyTreeAPI.Repository; + +public class Seed +{ + private readonly FamilyTreeDBContext _context; + public Seed(FamilyTreeDBContext context) + { + _context = context; + } + public int InsertOneUser() + { + int id = -1; + //password = password + string txt = " INSERT INTO staff ( " + + "firstname, lastname, email, phone, stype, srole, spassword, sactive) " + + "VALUES" + + " ( 'kham', 'vilaythong', 'kham.vilaythong@gmail.com', '009', 1, 2, 'cGFzc3dvcmQ=', true), " + + " ( 'sy', 'vilaythong', 'sy.vilaythong@gmail.com', '007', 1, 2, 'cGFzc3dvcmQ=', true), " + + " ( 'Hung', 'Nguyen', 'hung.gnuyen@gmail.com', '008', 1, 2, 'cGFzc3dvcmQ=', true); "; + string workertxt = "INSERT INTO person ( firstname, lastname,email,phone,address,dob ,alive, fatherId, image, sex)" + + "VALUES " + + " ('Ho 1','Tran', 'Ho.Tran@hotmail.com', '002', '1 Cabramatta','1960-09-01', true, 0,'', 'M'), " + + " ('Jimmy 2','Tran', 'Ho.Tran@hotmail.com', '003', '34 Cabramatta','1980-09-01', true, 1,'','M'), " + + " ('Joe 3','Tran', 'Joe.Tran@hotmail.com', '006', '32 Cabramatta','1980-10-01', true, 1,'','M'), " + + " ('John 4','Tran', 'John.Tran@hotmail.com', '008', '30 Cabramatta','1990-12-01', true, 3,'','M')," + + " ('Lee 5','Tran', 'Lee.Tran@hotmail.com', '030', '18 Cabramatta','2000-12-01', true, 2,'','M'), " + + " ('Len 6','Nguyen', 'Len.nguyen@hotmail.com', '001', '1 Home','1950-01-01', true, 0,'','M'), " + + " ('Son 7','Nguyen', 'son.Nguyen@hotmail.com', '001', '10 Home','1980-01-01', true,6,'','M'), " + + " ('Loa 8','Tran', 'Loa.Tran@hotmail.com', '020', '11 Cabramatta','2002-12-01', true, 3,'','M'), " + + " ('Du 9','Tran', 'Du.Tran@hotmail.com', '001', '12 Cabramatta','2002-12-01', true, 4,'','M'), " + + " ('Linh 10','Tran', 'Du.Tran@hotmail.com', '001', '12 Cabramatta','2002-12-01', true, 5,'','M'), " + + " ('COA 11','Tran', 'Du.Tran@hotmail.com', '001', '12 Cabramatta','2002-12-01', true, 8,'','M'), " + + " ('Hoa 12','Tran', 'Du.Tran@hotmail.com', '001', '12 Cabramatta','2002-12-01', true, 8,'','F'), " + + " ('Nhi 13','Tran', 'Du.Tran@hotmail.com', '001', '12 Cabramatta','2002-12-01', true, 8,'','F'), " + + " ('Mai 14','Tran', 'Du.Tran@hotmail.com', '001', '12 Cabramatta','2002-12-01', true, 8,'','F'), " + + " ('Trang 15','Tran', 'Du.Tran@hotmail.com', '001', '12 Cabramatta','2002-12-01', true, 7,'','F'), " + + " ('Tom 16','Tran', 'Ho.Tran@hotmail.com', '007', '34 Cabramatta','1999-01-01', true, 2,'','M');"; + + + try + { + int count = _context.staff.Count(); + if (count < 1) + { + var conn = _context.Database.GetDbConnection(); + try + { + + conn.Open(); + var command = conn.CreateCommand(); + command.CommandText = txt; + command.CommandType = System.Data.CommandType.Text; + command.ExecuteNonQuery(); + + command.CommandText = workertxt; + command.CommandType = System.Data.CommandType.Text; + + command.ExecuteNonQuery(); + id = 9; + } + catch + { + id = -1; + } + finally + { + conn.Close(); + } + } + else + id = 1; + + } + catch + { + id = -10; + } + return id; + } +} \ No newline at end of file diff --git a/API/FamilyTreeAPI/Repository/StaffRepository.cs b/API/FamilyTreeAPI/Repository/StaffRepository.cs new file mode 100644 index 0000000..b36fd35 --- /dev/null +++ b/API/FamilyTreeAPI/Repository/StaffRepository.cs @@ -0,0 +1,381 @@ +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +using FamilyTreeAPI.Models; +using FamilyTreeAPI.Entities; +using FamilyTreeAPI.Interface; +using DocumentFormat.OpenXml.Drawing.Diagrams; +using Microsoft.AspNetCore.Http; +using DocumentFormat.OpenXml.Spreadsheet; +using DocumentFormat.OpenXml.Wordprocessing; + +namespace FamilyTreeAPI.Repository; + +/* password = cGFzc3dvcmQ= + * + INSERT INTO public.staff( + firstname, lastname, email, phone, stype, srole, spassword, sactive) + VALUES ( 'kham', 'vilaythong', 'kham.vilaythong', '009', 1, 2, 'cGFzc3dvcmQ=', true); + */ +public class StaffRepository : IStaff +{ + private readonly FamilyTreeDBContext _context; + private readonly IHttpContextAccessor _httpContext; + + + public StaffRepository(FamilyTreeDBContext context, IHttpContextAccessor httpContext) + { + _context = context; + _httpContext = httpContext; + } + + private StaffDto FillDto(staff model) + { + StaffDto dto = new StaffDto(); + dto.Firstname = model.Firstname; + dto.Lastname = model.Lastname; + dto.Type = model.Stype; + dto.Phone = model.Phone; + dto.Active = model.Sactive ?? false; + dto.Email = model.Email; + /* + dto.AddedOn = model.AddedOn; + + dto.RoleType = model.RoleType; + + */ + dto.RoleType = model.Srole; + dto.Id = model.Id; + return dto; + } + private staff FillModel(staff model, StaffDto dto) + { + if (!string.IsNullOrEmpty(dto.Firstname)) + model.Firstname = dto.Firstname.Trim(); + if (!string.IsNullOrEmpty(dto.Lastname)) + model.Lastname = dto.Lastname.Trim(); + if (!string.IsNullOrEmpty(dto.Phone)) + model.Phone = dto.Phone.Trim(); + + if (dto.Id > 0) + model.Id = dto.Id; + + if (!string.IsNullOrEmpty(dto.Password)) + { + string password = dto.Password.Trim(); + model.Spassword = Ultils.Base64Encode(password); + } + + model.Email = dto.Email; + model.Srole = dto.RoleType; + model.Sactive = dto.Active; + return model; + } + + + public async Task>> GetDicStaffs() + { + Dictionary dlist = new(); + int statuscode = 0; + string error = ""; + StaffDto item; + staff model; + try + { + var list = await _context.staff.ToListAsync(); + for (int i = 0; i< list.Count; i++) + { + model = list[i]; + item = FillDto(model); + dlist.Add(item.Id, item); + } + statuscode = 1; + //list.Sort((x, y) => x.Code.CompareTo(y.Code)); + } + catch (Exception ex) + { + + error = ex.ToString(); + statuscode = -1; + + } + return new ResultModel>() + { + Data = dlist, + StatusCode = statuscode, + Message = error + }; + } + + public async Task >> GetStaff(StaffCriteria criteria) + { + List list = new(); + int statuscode = 0; + string error = ""; + try + { + list = await _context.LoadStaffAsync(criteria); + statuscode = 1; + //list.Sort((x, y) => x.Code.CompareTo(y.Code)); + } + catch (Exception ex) + { + + error = ex.ToString(); + statuscode = -1; + + } + return new ResultModel>() + { + Data = list, + StatusCode = statuscode, + Message = error + }; + } + + public async Task> GetStaffById(int id) + { + int statuscode = 0; + string error = ""; + StaffDto dto = new StaffDto(); + try + { + var item = await _context.staff.FindAsync(id); + if (item == null) + { + statuscode = -1; + } + else + { + dto = FillDto(item); + statuscode = 1; + } + } + catch(Exception ex) + { + error = ex.ToString(); + statuscode = -1; + + } + return new ResultModel() + { + Data = dto, + StatusCode = statuscode, + Message = error + }; + } + private string GetDateTimeNow() + { + return DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); + } + public async Task> ResetPassword(ResetPassDto item) + { + int result = -1; + int statuscode = 0; + string error = ""; + try + { + staff? model1 = await _context.staff.FindAsync(item.Id); + if (model1 != null) + { + if (!string.IsNullOrEmpty(item.Password)) + { + string password = item.Password.Trim(); + model1.Spassword = Ultils.Base64Encode(password); + } + + // model.LastModified = DateTime.Now; + // model.LastModifiedId = loginName; + var successid = await _context.SaveChangesAsync(); + result = item.Id; + } + statuscode = 1; + } + catch (Exception ex) + { + statuscode = -1; + error = ex.ToString(); + } + return new ResultModel() + { + Data = result, + StatusCode = statuscode, + Message = error + }; + } + public async Task> SaveStaff(StaffDto item) + { + int result = default(int); + int statuscode = 0; + string error = ""; + + HttpContext? httpContext = _httpContext.HttpContext; + string loginName = ""; + if (httpContext != null) + { + UserDto? user = (UserDto?)httpContext.Items["User"]; + if (user != null) + loginName = user.FirstName + " " + user.LastName; + } + + try + { + if (item.Id < 1) + { + bool already = await CheckLoginAlready(item.Email.Trim()); + if (already) + { + return new ResultModel() + { + Data = 0, + StatusCode = 0, + Message = "user name already exist in Database" + }; + } + } + + staff model; + if (item.Id < 1) + { + model = new(); + model = FillModel(model, item); + + // model.Active = true; + // model.AddedBy = loginName; + // model.AddedOn = DateTime.Now; + _context.staff.Add(model); + await _context.SaveChangesAsync(); + result = model.Id; + } + else + { + staff? model1 = await _context.staff.FindAsync(item.Id); + model1 = FillModel(model1, item); + // model.LastModified = DateTime.Now; + // model.LastModifiedId = loginName; + var successid = await _context.SaveChangesAsync(); + result = item.Id; + } + + + statuscode = 1; + + } + catch (Exception ex) + { + error = ex.ToString(); + statuscode = -1; + + } + //var dto = await Task.Run(() => result); + return new ResultModel() + { + Data = result, + StatusCode = statuscode, + Message = error + }; + } + private async Task CheckLoginAlready(string login) + { + bool result = false; + var model = await _context.staff.Where(x => x.Email == login).ToListAsync(); + if (model.Count > 0) + result = true; + return result; + } + public async Task> SaveStaffNew(StaffDto item) + { + int result = default(int); + int statuscode = 0; + string error = ""; + + try + { + if (item.Id < 1) + { + bool already = await CheckLoginAlready(item.Email.Trim()); + if (already) + { + return new ResultModel() + { + Data = 0, + StatusCode = 0, + Message = "user name already exist in db" + }; + } + } + staff model; + if (item.Id < 1) + { + model = new(); + model = FillModel(model, item); + // model.AddedOn = DateTime.Now; + + // model.Active = true; + _context.staff.Add(model); + } + else + { + model = await _context.staff.FindAsync(item.Id); + model = FillModel(model, item); + } + var successid = await _context.SaveChangesAsync(); + + result = 1; + statuscode = 1; + + } + catch (Exception ex) + { + error = ex.ToString(); + statuscode = -1; + + } + //var dto = await Task.Run(() => result); + return new ResultModel() + { + Data = result, + StatusCode = statuscode, + Message = error + }; + } + + + + + public async Task> Delete(int id) + { + int result = 1; + int statuscode = 0; + string error = ""; + try + { + staff model; + model = await _context.staff.FindAsync(id); + // if (model != null) + // model.Active = false; + + var successCount = await _context.SaveChangesAsync(); + statuscode = 1; + result = 1; + } + catch (Exception ex) + { + error = ex.ToString(); + statuscode = -1; + + } + //var dto = await Task.Run(() => result); + return new ResultModel() + { + Data = result, + StatusCode = statuscode, + Message = error + }; + } + + +} diff --git a/API/FamilyTreeAPI/Repository/TextFile.txt b/API/FamilyTreeAPI/Repository/TextFile.txt new file mode 100644 index 0000000..edf2cb4 --- /dev/null +++ b/API/FamilyTreeAPI/Repository/TextFile.txt @@ -0,0 +1,251 @@ +/**************************************/ + +CREATE TABLE IF NOT EXISTS public.client +( + id integer NOT NULL GENERATED ALWAYS AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1 ), + clientname character(80) COLLATE pg_catalog."default", + contact character(80) COLLATE pg_catalog."default", + cactive boolean, + address character(120), + email character(80), + phone character(30), + CONSTRAINT client_pkey PRIMARY KEY (id) +) +TABLESPACE pg_default; + +ALTER TABLE IF EXISTS public.client + OWNER to postgres; + +CREATE TABLE IF NOT EXISTS public.lookup +( + id integer NOT NULL GENERATED ALWAYS AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1 ), + code character(10) COLLATE pg_catalog."default", + description character(80) COLLATE pg_catalog."default", + lactive boolean, + ltype character(20) COLLATE pg_catalog."default", + CONSTRAINT lookup_pkey PRIMARY KEY (id) +) + +TABLESPACE pg_default; + +ALTER TABLE IF EXISTS public.lookup + OWNER to postgres; + + +-- Table: public.job + +-- DROP TABLE IF EXISTS public.job; + +CREATE TABLE IF NOT EXISTS public.job +( + id integer NOT NULL GENERATED ALWAYS AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1 ), + code character(10) COLLATE pg_catalog."default", + description character(80) COLLATE pg_catalog."default", + jactive boolean, + CONSTRAINT job_pkey PRIMARY KEY (id) +) + +TABLESPACE pg_default; + +ALTER TABLE IF EXISTS public.job + OWNER to postgres; + +-- Table: public.servicetask + +-- DROP TABLE IF EXISTS public.servicetask; + +CREATE TABLE IF NOT EXISTS public.servicetask +( + id integer NOT NULL GENERATED ALWAYS AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1 ), + assignto integer, + clientid integer, + code character(10) COLLATE pg_catalog."default", + description character(80) COLLATE pg_catalog."default", + sdate date, + serviceno character(20) COLLATE pg_catalog."default", + staffid integer, + status integer, + CONSTRAINT servicetask_pkey PRIMARY KEY (id) +) +TABLESPACE pg_default; + +ALTER TABLE IF EXISTS public.servicetask + OWNER to postgres; + + +-- Table: public.staffwork + +-- DROP TABLE IF EXISTS public.staffwork; + +CREATE TABLE IF NOT EXISTS public.staffwork +( + id integer NOT NULL GENERATED ALWAYS AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1 ), + description character(80) COLLATE pg_catalog."default", + sactive boolean, + staffid integer, + startdate date, + CONSTRAINT staffwork_pkey PRIMARY KEY (id) +) + +TABLESPACE pg_default; + +ALTER TABLE IF EXISTS public.staffwork + OWNER to postgres; + +-- Table: public.staffworkdetail + +-- DROP TABLE IF EXISTS public.staffworkdetail; + +CREATE TABLE IF NOT EXISTS public.staffworkdetail +( + id integer NOT NULL GENERATED ALWAYS AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1 ), + jobid integer, + sactive boolean, + starttime timestamp without time zone, + stoptime timestamp without time zone, + task character(80) COLLATE pg_catalog."default", + swid integer, + CONSTRAINT staffworkdetail_pkey PRIMARY KEY (id) +) + +TABLESPACE pg_default; + +ALTER TABLE IF EXISTS public.staffworkdetail + OWNER to postgres; + +/******************************************/ +ALTER TABLE servicetask +ADD FOREIGN KEY (clientid) REFERENCES client(id); + +ALTER TABLE servicetask +ADD FOREIGN KEY (staffid) REFERENCES staff(id); + +ALTER TABLE servicetask +ADD FOREIGN KEY (assignto) REFERENCES staff(id); + +ALTER TABLE staffwork +ADD FOREIGN KEY (staffid) REFERENCES staff(id); + +ALTER TABLE staffworkdetail +ADD FOREIGN KEY (swid) REFERENCES staffwork(id); + +ALTER TABLE staffworkdetail +ADD FOREIGN KEY (jobid) REFERENCES job(id); + +/************************************/ + +CREATE OR REPLACE FUNCTION usp_search_user ( + iemail character varying, + ifirstname character varying, + ilastname character varying) + RETURNS TABLE(id integer, fistname character varying, lastname character varying, email character varying, stype integer, sactive boolean, srole integer, spassword character varying) + LANGUAGE 'plpgsql' + COST 100 + VOLATILE PARALLEL UNSAFE + ROWS 1000 + +AS $BODY$ +begin + return query SELECT + e.id, + e.firstname, + e.lastname, + e.email, + e.stype, + e.sactive, + e.srole, + e.spassword + + + + FROM staff e + WHERE (iemail = '' or e.email ilike iemail || '%') + and (ilastname = '' or e.lastname ilike ilastname || '%') + and (ifirstname = '' or e.firstname ilike ifirstname || '%'); + +end; +$BODY$; + +CREATE OR REPLACE FUNCTION usp_staffwork_detail( + ifromdate date, + itodate date, + istaffid integer) + RETURNS TABLE(id integer, staffid integer, description character varying(80), + startdate date, sactive boolean, did integer, starttime timestamp, stoptime timestamp, + task character varying(80)) + LANGUAGE 'plpgsql' + COST 100 + VOLATILE PARALLEL UNSAFE + ROWS 1000 + +AS $BODY$ +begin + return query SELECT + w.id, + w.staffid, + w.description, + w.startdate, + w.sactive, + d.id as did, + d.starttime, + d.stoptime, + d.task + + FROM staffwork w, + staffworkdetail d + where w.id = d.swid + and (istaffid = 0 or w.staffid = istaffid) + and w.startdate >= ifromdate + and w.startdate <= itodate + order by id, did; + + +end; +$BODY$; + + +CREATE OR REPLACE FUNCTION usp_servicetask_sel( + ifromdate date, + itodate date, + istaffid integer, + iassignto integer, + iclientid integer + ) + RETURNS TABLE(id integer, + staffid integer, + sdate date, + description character varying(80), + code character varying(10), + status integer, + assignTo integer, + clientid integer, + serviceno character varying(20)) + LANGUAGE 'plpgsql' + COST 100 + VOLATILE PARALLEL UNSAFE + ROWS 1000 + + +AS $BODY$ +begin + return query SELECT + s.id, + s.staffid, + s.sdate, + s.description, + s.code, + s.status, + s.assignto, + s.clientid, + s."serviceNo" + FROM servicetask s + + where (istaffid < 1 or s.staffid = istaffid) + and (iassignto < 1 or s.assignto = iassignto) + and (iclientid < 1 or s.clientid = iclientid) + and s.sdate >= ifromdate + and s.sdate <= itodate + + ; +end; +$BODY$; diff --git a/API/FamilyTreeAPI/Repository/Ultils.cs b/API/FamilyTreeAPI/Repository/Ultils.cs new file mode 100644 index 0000000..86a4376 --- /dev/null +++ b/API/FamilyTreeAPI/Repository/Ultils.cs @@ -0,0 +1,16 @@ +namespace FamilyTreeAPI.Repository +{ + public class Ultils + { + public static string Base64Encode(string plainText) + { + var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText); + return System.Convert.ToBase64String(plainTextBytes); + } + public static string Base64Decode(string base64EncodedData) + { + var base64EncodedBytes = System.Convert.FromBase64String(base64EncodedData); + return System.Text.Encoding.UTF8.GetString(base64EncodedBytes); + } + } +} diff --git a/API/FamilyTreeAPI/Repository/UserServiceRepository.cs b/API/FamilyTreeAPI/Repository/UserServiceRepository.cs new file mode 100644 index 0000000..6fcf5a3 --- /dev/null +++ b/API/FamilyTreeAPI/Repository/UserServiceRepository.cs @@ -0,0 +1,257 @@ +//using BCryptNet = BCrypt.Net.BCrypt; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Extensions.Options; + +using Microsoft.EntityFrameworkCore; +using FamilyTreeAPI.Interface; +using FamilyTreeAPI.Entities; + +using System; +using FamilyTreeAPI.Models; +using Microsoft.AspNetCore.Http; +using System.Text.Json; + + +namespace FamilyTreeAPI.Repository; +public class UserServiceRepository : IUserService +{ + private FamilyTreeDBContext _context; + private IJwtUtils _jwtUtils; + private readonly IHttpContextAccessor _httpcontext; + private readonly AppSettings _appSettings; + + public UserServiceRepository( + FamilyTreeDBContext context, + IJwtUtils jwtUtils, + IHttpContextAccessor httpcontext, + IOptions appSettings) + { + _context = context; + _jwtUtils = jwtUtils; + _appSettings = appSettings.Value; + this._httpcontext = httpcontext; + + } + public Task> Logout(string token, UserDto user, string remoteIpAddress) + { + /* + int retval = 1; + int statuscode = 1; + string str255 = token; + var histo = await _context.AdminLoginHistories.Where(x => x.UserId == user.Id + && x.RecordIntegrity == token + && x.LoginTyped == user.Username).ToListAsync(); + if (histo.Count > 0) + { + histo[0].LogoutDatetime = DateTime.Now; + await _context.SaveChangesAsync(); + } + */ + var result = new ResultModel() + { + Data = 1, + StatusCode = 1 + }; + + return Task.FromResult(result); + + } + private bool checkLogin(staff user, string email, string password) + { + bool result = false; + // string dpassword = Ultils.Base64Encode(password); + result = user.Spassword == password; + return result; + } + public async Task> Login(AuthenticateRequest model) + { + UserDto myUser = new(); + AuthenticateResponse retval = null; + string error = "user name is not in DB or user Name profile is not generic"; + int statuscode = 0; + + try + { + //now check the adminuser table + myUser.Username = model.Username; + var user = _context.staff. + SingleOrDefault(x => x.Email == model.Username + && true == x.Sactive); + if (user != null) + { + bool loginOK = checkLogin(user, model.Username, model.Password); + if (loginOK) + { + + + // myUser.Role = user.RoleType; + myUser.Id = user.Id; + myUser.Role = user.Srole ?? 0; + myUser.Email = user.Email; + myUser.Phone = user.Phone; + myUser.FirstName = user.Firstname; + myUser.LastName = user.Lastname; + //myUser.ValidationPointId = user.ValidationPointID ?? 0; + statuscode = 1; + } + else //not allow + { + statuscode = -1; + myUser.Role = 1; + myUser.Id = -1; + + error = "user name cannot login email or password"; + } + // validate + // if (user == null || !BCryptNet.Verify(model.Password, user.PasswordHash)) + + // authentication successful so generate jwt token + if (statuscode == 1) + { + var jwtToken = _jwtUtils.GenerateJwtToken(myUser); + retval = new AuthenticateResponse(myUser, jwtToken, myUser.Role); + error = ""; + } + } + else + { + error = "user name or password is not correct"; + } + + + } + catch (Exception ex) + { + retval = null; + error = ex.ToString(); + statuscode = -1; + + } + //writelog for login user. + // if (myUser != null) + // await AddToSession(myUser); + return new ResultModel() + { + Data = retval, + StatusCode = statuscode, + Message = error + }; + + } + public async Task> LoginApiAD(AuthenticateRequest model, string remoteIpAddress) + { + UserDto myUser = new(); + AuthenticateResponse retval = null; + string error = "user name is not in DB or user Name profile is not generic"; + int statuscode = 0; + string webAPIUrl = _appSettings.LoginWebAPI; + //KCO, D204KCO + //now check the adminuser table + //ward clerk + try + { + //now check the adminuser table + myUser.Username = model.Username; + var user = _context.staff. + SingleOrDefault(x => x.Email == model.Username + && true == x.Sactive); + if (user != null) + { + bool loginOK = checkLogin(user,model.Username, model.Password); + if (loginOK) + { + // myUser.Role = user.RoleType; + myUser.Id = user.Id; + myUser.Role = user.Srole ?? 0; + myUser.Email = user.Email; + myUser.Phone = user.Phone; + myUser.FirstName = user.Firstname; + myUser.LastName = user.Lastname; + + //myUser.ValidationPointId = user.ValidationPointID ?? 0; + statuscode = 1; + } + else // allow + { + statuscode = 1; + myUser.Role = 1; + myUser.Id = -1; + + //error = "user name does not exist in adminUser"; + } + // validate + // if (user == null || !BCryptNet.Verify(model.Password, user.PasswordHash)) + + // authentication successful so generate jwt token + if (statuscode == 1) + { + var jwtToken = _jwtUtils.GenerateJwtToken(myUser); + retval = new AuthenticateResponse(myUser, jwtToken, myUser.Role); + error = ""; + } + } + else + { + error = "user name or password is not correct"; + } + + + } + catch (Exception ex) + { + retval = null; + error = ex.ToString(); + statuscode = -1; + + } + //writelog for login user. + // if (myUser != null) + // await AddToSession(myUser); + return new ResultModel() + { + Data = retval, + StatusCode = statuscode, + Message = error + }; + + } + //get like this + private async Task GetCurrentUser() + { + await _httpcontext.HttpContext.Session.LoadAsync(); + + string userString = _httpcontext.HttpContext.Session.GetString("user"); + + if (userString != null && userString != "") + { + var user = JsonSerializer.Deserialize(userString); + if (user != null) + { + // return user; + } + } + } + + + /* + private User LoginADStaff(ADConfig adConfig, string username, string password) + { + + ADStaffLink staffLink = new ADStaffLink(adConfig); + MyADObject myADObj = staffLink.CheckADCredentials(username,password); + User user = new() + { + Username = myADObj.StafflinkNo, + Email = myADObj.Email, + FirstName = myADObj.FirstName, + LastName = myADObj.LastName + }; + // myADObj.JobTitle; + return user; + + // return null; + } + */ +} diff --git a/API/FamilyTreeAPI/Repository/storeprocedure.sql b/API/FamilyTreeAPI/Repository/storeprocedure.sql new file mode 100644 index 0000000..d3073d8 --- /dev/null +++ b/API/FamilyTreeAPI/Repository/storeprocedure.sql @@ -0,0 +1,118 @@ +/* +declare @id int + set @id = 7 + delete from MotorAccess where mv_id = @id + delete from [dbo].[MotorVehicle] + where mv_id = @id +*/ + + +CREATE OR REPLACE FUNCTION public.usp_search_user( + iemail character varying, + ifirstname character varying, + ilastname character varying) + RETURNS TABLE(id integer, fistname character varying, lastname character varying, email character varying, stype integer, sactive boolean, srole integer, spassword character varying) + LANGUAGE 'plpgsql' + COST 100 + VOLATILE PARALLEL UNSAFE + ROWS 1000 + +AS $BODY$ +begin + return query SELECT + e.id, + e.firstname, + e.lastname, + e.email, + e.stype, + e.sactive, + e.srole, + e.spassword + + + + FROM staff e + WHERE (iemail = '' or e.email ilike iemail || '%') + and (ilastname = '' or e.lastname ilike ilastname || '%') + and (ifirstname = '' or e.firstname ilike ifirstname || '%'); + +end; +$BODY$; + +CREATE OR REPLACE FUNCTION get_staff ( + iemail character varying, + ifirstname character varying, + ilastname character varying + ) + RETURNS TABLE ( + id int, + firstname varchar, + lastname varchar, + email varchar, + stype INT, + sactive boolean, + srole INT, + spassword varchar ) +AS $$ +BEGIN +RETURN QUERY + e.id, + e.firstname, + e.lastname, + e.email, + e.stype, + e.sactive, + e.srole, + e.spassword + FROM staff e + WHERE (iemail = '' or e.email ilike iemail || '%') + and (ilastname = '' or e.lastname ilike ilastname || '%') + and (ifirstname = '' or e.firstname ilike ifirstname || '%'); +END; $$ + +LANGUAGE 'plpgsql'; + + +how to host under .net 8 web api under linux + +1) create [webAPI_name].service +****************************************** +# Example: /etc/systemd/system/yourwebapp.service + [Unit] + Description=Your .NET 8 Web API + After=network.target + + [Service] + WorkingDirectory=/var/www/yourwebapp + ExecStart=/usr/bin/dotnet /var/www/yourwebapp/YourWebApi.dll + Restart=always + RestartSec=10 + SyslogIdentifier=yourwebapp + User=www-data # or a dedicated user + Environment=ASPNETCORE_ENVIRONMENT=Production + + [Install] + WantedBy=multi-user.target +************************************************ +2) start /stop the service under linux +*********************************************** + sudo systemctl enable yourwebapp.service + sudo systemctl start yourwebapp.service +********************************************** + + 3) host under nginx or apache + ************************************************ + server { + listen 80; + server_name yourdomain.com; + + location / { + proxy_pass http://localhost:5000; # Kestrel's listening port + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + } +*********************************************************************** + \ No newline at end of file diff --git a/API/FamilyTreeAPI/appsettings.Development.json b/API/FamilyTreeAPI/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/API/FamilyTreeAPI/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/API/FamilyTreeAPI/appsettings.json b/API/FamilyTreeAPI/appsettings.json new file mode 100644 index 0000000..11c208b --- /dev/null +++ b/API/FamilyTreeAPI/appsettings.json @@ -0,0 +1,19 @@ +{ + + "AppSettings": { + "Secret": "Nepean Blue Mountain Super Secret SIGN AND VERIFY JWT TOKENS, BEARER TOKEN USE WHEN CALLING THIS API.", + "SQLConnectionString": "host=localhost;port=5432;database=FamilyTreeDB;username=postgres;password=Positive~1;", + "LoginWebAPI": "http://nephmdb-sql006/CommonWebApiAD/api/AD", + "ClientURL": "http://localhost:4200/approval", + "ImageFolder": "c:\\temp\\Family" + + }, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning", + "Microsoft.AspNetCore.DataProtection": "None" + } + }, + "AllowedHosts": "*" + } diff --git a/API/docker-compose.dcproj b/API/docker-compose.dcproj new file mode 100644 index 0000000..29eb323 --- /dev/null +++ b/API/docker-compose.dcproj @@ -0,0 +1,19 @@ + + + + 2.1 + Linux + False + 81dded9d-158b-e303-5f62-77a2896d2a5a + LaunchBrowser + {Scheme}://localhost:{ServicePort}/swagger + workapi + + + + docker-compose.yml + + + + + \ No newline at end of file diff --git a/API/docker-compose.override.yml b/API/docker-compose.override.yml new file mode 100644 index 0000000..bf9e9a6 --- /dev/null +++ b/API/docker-compose.override.yml @@ -0,0 +1,12 @@ +services: + workapi: + environment: + - ASPNETCORE_ENVIRONMENT=Development + - ASPNETCORE_HTTP_PORTS=8080 + ports: + - "8080" + + + + + diff --git a/API/docker-compose.yml b/API/docker-compose.yml new file mode 100644 index 0000000..0504ff2 --- /dev/null +++ b/API/docker-compose.yml @@ -0,0 +1,72 @@ +networks: + dev: + driver: bridge +services: + postgresdb: + image: postgres + container_name: ${DOCKER_REGISTRY-}postgresql-db + ports: + - "5432:5432" + environment: + - POSTGRES_DB=FamilyTreeDB + - POSTGRES_USER=postgres + - POSTGRES_PASSWORD=Positive~1 + - PGDATA=/var/lib/postgresql/data/pgdata + restart: always + volumes: + - postgresdata:/var/lib/postgresql/data + networks: + - dev + pgadmin: + image: dpage/pgadmin4 + container_name: pgadmin4_container + restart: always + depends_on: + - "postgresdb" + volumes: + - pgadmin-data:/var/lib/pgadmin + environment: + PGADMIN_DEFAULT_EMAIL: postgres@domain.com + PGADMIN_DEFAULT_PASSWORD: Positive~1 + ports: + - "5050:80" + networks: + - dev + familytreeui: + image: ${DOCKER_REGISTRY-}familytreeui + depends_on: + - "familytreeapi" + ports: + - "4200:80" + build: + context: UI + dockerfile: Dockerfile + container_name: workui + volumes: + - ./:/app + networks: + - dev + familytreeapi: + image: ${DOCKER_REGISTRY-}familytreeapi + container_name: familytreeapi-services + depends_on: + - "postgresdb" + environment: + - "Logging:Microsoft.AspNetCore.DataProtection:None" + - "AppSettings:SQLConnectionString=host=postgresdb;database=FamilyTreeDB;username=postgres;password=Positive~1" + volumes: + - myapikey:/home/app/.aspnet/DataProtection-Keys + ports: + - "8080:8080" + build: + context: . + dockerfile: FamilyTreeAPI/Dockerfile + networks: + - dev +volumes: + myapikey: + external: true + pgadmin-data: + external: true + postgresdata: + external: true diff --git a/API/launchSettings.json b/API/launchSettings.json new file mode 100644 index 0000000..99e8016 --- /dev/null +++ b/API/launchSettings.json @@ -0,0 +1,11 @@ +{ + "profiles": { + "Docker Compose": { + "commandName": "DockerCompose", + "commandVersion": "1.0", + "serviceActions": { + "workapi": "StartDebugging" + } + } + } +} \ No newline at end of file diff --git a/UI/.editorconfig b/UI/.editorconfig new file mode 100644 index 0000000..f166060 --- /dev/null +++ b/UI/.editorconfig @@ -0,0 +1,17 @@ +# Editor configuration, see https://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.ts] +quote_type = single +ij_typescript_use_double_quotes = false + +[*.md] +max_line_length = off +trim_trailing_whitespace = false diff --git a/UI/.postcssrc.json b/UI/.postcssrc.json new file mode 100644 index 0000000..72f908d --- /dev/null +++ b/UI/.postcssrc.json @@ -0,0 +1,5 @@ +{ + "plugins": { + "@tailwindcss/postcss": {} + } +} \ No newline at end of file diff --git a/UI/.vscode/extensions.json b/UI/.vscode/extensions.json new file mode 100644 index 0000000..77b3745 --- /dev/null +++ b/UI/.vscode/extensions.json @@ -0,0 +1,4 @@ +{ + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846 + "recommendations": ["angular.ng-template"] +} diff --git a/UI/.vscode/launch.json b/UI/.vscode/launch.json new file mode 100644 index 0000000..925af83 --- /dev/null +++ b/UI/.vscode/launch.json @@ -0,0 +1,20 @@ +{ + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "ng serve", + "type": "chrome", + "request": "launch", + "preLaunchTask": "npm: start", + "url": "http://localhost:4200/" + }, + { + "name": "ng test", + "type": "chrome", + "request": "launch", + "preLaunchTask": "npm: test", + "url": "http://localhost:9876/debug.html" + } + ] +} diff --git a/UI/.vscode/settings.json b/UI/.vscode/settings.json new file mode 100644 index 0000000..3b66410 --- /dev/null +++ b/UI/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "git.ignoreLimitWarning": true +} \ No newline at end of file diff --git a/UI/.vscode/tasks.json b/UI/.vscode/tasks.json new file mode 100644 index 0000000..a298b5b --- /dev/null +++ b/UI/.vscode/tasks.json @@ -0,0 +1,42 @@ +{ + // For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558 + "version": "2.0.0", + "tasks": [ + { + "type": "npm", + "script": "start", + "isBackground": true, + "problemMatcher": { + "owner": "typescript", + "pattern": "$tsc", + "background": { + "activeOnStart": true, + "beginsPattern": { + "regexp": "(.*?)" + }, + "endsPattern": { + "regexp": "bundle generation complete" + } + } + } + }, + { + "type": "npm", + "script": "test", + "isBackground": true, + "problemMatcher": { + "owner": "typescript", + "pattern": "$tsc", + "background": { + "activeOnStart": true, + "beginsPattern": { + "regexp": "(.*?)" + }, + "endsPattern": { + "regexp": "bundle generation complete" + } + } + } + } + ] +} diff --git a/UI/README.md b/UI/README.md new file mode 100644 index 0000000..2491874 --- /dev/null +++ b/UI/README.md @@ -0,0 +1,59 @@ +# FamilyTree + +This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 20.1.1. + +## Development server + +To start a local development server, run: + +```bash +ng serve +``` + +Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files. + +## Code scaffolding + +Angular CLI includes powerful code scaffolding tools. To generate a new component, run: + +```bash +ng generate component component-name +``` + +For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run: + +```bash +ng generate --help +``` + +## Building + +To build the project run: + +```bash +ng build +``` + +This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed. + +## Running unit tests + +To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command: + +```bash +ng test +``` + +## Running end-to-end tests + +For end-to-end (e2e) testing, run: + +```bash +ng e2e +``` + +Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs. + +## Additional Resources + +For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page. diff --git a/UI/angular.json b/UI/angular.json new file mode 100644 index 0000000..969baf6 --- /dev/null +++ b/UI/angular.json @@ -0,0 +1,88 @@ +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "newProjectRoot": "projects", + "projects": { + "FamilyTree": { + "projectType": "application", + "schematics": {}, + "root": "", + "sourceRoot": "src", + "prefix": "app", + "architect": { + "build": { + "builder": "@angular/build:application", + "options": { + "browser": "src/main.ts", + "tsConfig": "tsconfig.app.json", + "assets": [ + { + "glob": "**/*", + "input": "public" + } + ], + "styles": [ + "src/styles.css" + ] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "5MB", + "maximumError": "6MB" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "4kB", + "maximumError": "8kB" + } + ], + "outputHashing": "all" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "builder": "@angular/build:dev-server", + "configurations": { + "production": { + "buildTarget": "FamilyTree:build:production" + }, + "development": { + "buildTarget": "FamilyTree:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "builder": "@angular/build:extract-i18n" + }, + "test": { + "builder": "@angular/build:karma", + "options": { + "tsConfig": "tsconfig.spec.json", + "assets": [ + { + "glob": "**/*", + "input": "public" + } + ], + "styles": [ + "src/styles.css" + ] + } + } + } + } + }, + "cli": { + "analytics": false + } +} diff --git a/UI/package-lock.json b/UI/package-lock.json new file mode 100644 index 0000000..37cbeba --- /dev/null +++ b/UI/package-lock.json @@ -0,0 +1,10246 @@ +{ + "name": "family-tree", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "family-tree", + "version": "0.0.0", + "dependencies": { + "@angular/common": "^20.1.0", + "@angular/compiler": "^20.1.0", + "@angular/core": "^20.1.0", + "@angular/forms": "^20.1.0", + "@angular/platform-browser": "^20.1.0", + "@angular/router": "^20.1.0", + "@primeuix/themes": "^1.2.1", + "@tailwindcss/postcss": "^4.1.11", + "moment": "^2.30.1", + "postcss": "^8.5.6", + "primeicons": "^7.0.0", + "primeng": "^20.0.0", + "rxjs": "~7.8.0", + "tailwindcss": "^4.1.11", + "tailwindcss-primeui": "^0.6.1", + "tslib": "^2.3.0" + }, + "devDependencies": { + "@angular/build": "^20.1.1", + "@angular/cli": "^20.1.1", + "@angular/compiler-cli": "^20.1.0", + "@types/jasmine": "~5.1.0", + "jasmine-core": "~5.8.0", + "karma": "~6.4.0", + "karma-chrome-launcher": "~3.2.0", + "karma-coverage": "~2.2.0", + "karma-jasmine": "~5.1.0", + "karma-jasmine-html-reporter": "~2.1.0", + "typescript": "~5.8.2" + } + }, + "node_modules/@algolia/client-abtesting": { + "version": "5.32.0", + "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.32.0.tgz", + "integrity": "sha512-HG/6Eib6DnJYm/B2ijWFXr4txca/YOuA4K7AsEU0JBrOZSB+RU7oeDyNBPi3c0v0UDDqlkBqM3vBU/auwZlglA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.32.0", + "@algolia/requester-browser-xhr": "5.32.0", + "@algolia/requester-fetch": "5.32.0", + "@algolia/requester-node-http": "5.32.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-analytics": { + "version": "5.32.0", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.32.0.tgz", + "integrity": "sha512-8Y9MLU72WFQOW3HArYv16+Wvm6eGmsqbxxM1qxtm0hvSASJbxCm+zQAZe5stqysTlcWo4BJ82KEH1PfgHbJAmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.32.0", + "@algolia/requester-browser-xhr": "5.32.0", + "@algolia/requester-fetch": "5.32.0", + "@algolia/requester-node-http": "5.32.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-common": { + "version": "5.32.0", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.32.0.tgz", + "integrity": "sha512-w8L+rgyXMCPBKmEdOT+RfgMrF0mT6HK60vPYWLz8DBs/P7yFdGo7urn99XCJvVLMSKXrIbZ2FMZ/i50nZTXnuQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-insights": { + "version": "5.32.0", + "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.32.0.tgz", + "integrity": "sha512-AdWfynhUeX7jz/LTiFU3wwzJembTbdLkQIOLs4n7PyBuxZ3jz4azV1CWbIP8AjUOFmul6uXbmYza+KqyS5CzOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.32.0", + "@algolia/requester-browser-xhr": "5.32.0", + "@algolia/requester-fetch": "5.32.0", + "@algolia/requester-node-http": "5.32.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-personalization": { + "version": "5.32.0", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.32.0.tgz", + "integrity": "sha512-bTupJY4xzGZYI4cEQcPlSjjIEzMvv80h7zXGrXY1Y0KC/n/SLiMv84v7Uy+B6AG1Kiy9FQm2ADChBLo1uEhGtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.32.0", + "@algolia/requester-browser-xhr": "5.32.0", + "@algolia/requester-fetch": "5.32.0", + "@algolia/requester-node-http": "5.32.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-query-suggestions": { + "version": "5.32.0", + "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.32.0.tgz", + "integrity": "sha512-if+YTJw1G3nDKL2omSBjQltCHUQzbaHADkcPQrGFnIGhVyHU3Dzq4g46uEv8mrL5sxL8FjiS9LvekeUlL2NRqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.32.0", + "@algolia/requester-browser-xhr": "5.32.0", + "@algolia/requester-fetch": "5.32.0", + "@algolia/requester-node-http": "5.32.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-search": { + "version": "5.32.0", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.32.0.tgz", + "integrity": "sha512-kmK5nVkKb4DSUgwbveMKe4X3xHdMsPsOVJeEzBvFJ+oS7CkBPmpfHAEq+CcmiPJs20YMv6yVtUT9yPWL5WgAhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.32.0", + "@algolia/requester-browser-xhr": "5.32.0", + "@algolia/requester-fetch": "5.32.0", + "@algolia/requester-node-http": "5.32.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/ingestion": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.32.0.tgz", + "integrity": "sha512-PZTqjJbx+fmPuT2ud1n4vYDSF1yrT//vOGI9HNYKNA0PM0xGUBWigf5gRivHsXa3oBnUlTyHV9j7Kqx5BHbVHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.32.0", + "@algolia/requester-browser-xhr": "5.32.0", + "@algolia/requester-fetch": "5.32.0", + "@algolia/requester-node-http": "5.32.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/monitoring": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.32.0.tgz", + "integrity": "sha512-kYYoOGjvNQAmHDS1v5sBj+0uEL9RzYqH/TAdq8wmcV+/22weKt/fjh+6LfiqkS1SCZFYYrwGnirrUhUM36lBIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.32.0", + "@algolia/requester-browser-xhr": "5.32.0", + "@algolia/requester-fetch": "5.32.0", + "@algolia/requester-node-http": "5.32.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/recommend": { + "version": "5.32.0", + "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.32.0.tgz", + "integrity": "sha512-jyIBLdskjPAL7T1g57UMfUNx+PzvYbxKslwRUKBrBA6sNEsYCFdxJAtZSLUMmw6MC98RDt4ksmEl5zVMT5bsuw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.32.0", + "@algolia/requester-browser-xhr": "5.32.0", + "@algolia/requester-fetch": "5.32.0", + "@algolia/requester-node-http": "5.32.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-browser-xhr": { + "version": "5.32.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.32.0.tgz", + "integrity": "sha512-eDp14z92Gt6JlFgiexImcWWH+Lk07s/FtxcoDaGrE4UVBgpwqOO6AfQM6dXh1pvHxlDFbMJihHc/vj3gBhPjqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.32.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-fetch": { + "version": "5.32.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.32.0.tgz", + "integrity": "sha512-rnWVglh/K75hnaLbwSc2t7gCkbq1ldbPgeIKDUiEJxZ4mlguFgcltWjzpDQ/t1LQgxk9HdIFcQfM17Hid3aQ6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.32.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-node-http": { + "version": "5.32.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.32.0.tgz", + "integrity": "sha512-LbzQ04+VLkzXY4LuOzgyjqEv/46Gwrk55PldaglMJ4i4eDXSRXGKkwJpXFwsoU+c1HMQlHIyjJBhrfsfdyRmyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.32.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@angular-devkit/architect": { + "version": "0.2001.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2001.1.tgz", + "integrity": "sha512-jU+fvaiS5bjh3znpHLEeKQIYb+ZVKNP0xRu3+E9EmweyG4E8AdvZnLvKTe61Ikhul2zBTFBUv46er7CHjAGEEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "20.1.1", + "rxjs": "7.8.2" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/core": { + "version": "20.1.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-20.1.1.tgz", + "integrity": "sha512-5rKL/WfMhZOi0MyYWXK95kPwxSd7zhZieyo3Idtg0B1VMFP4jIa4jRkV7uz55HRPOl5/kK3aIrsxgtKuxQg50Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "8.17.1", + "ajv-formats": "3.0.1", + "jsonc-parser": "3.3.1", + "picomatch": "4.0.2", + "rxjs": "7.8.2", + "source-map": "0.7.4" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^4.0.0" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/schematics": { + "version": "20.1.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-20.1.1.tgz", + "integrity": "sha512-G87e0u3V9E2iqwoV8nBIuLNtMUAnb/A62LNq9eTJguyVEC0HSRWQnByhUvmv6mlABLa4worZJnE5vMbXW1LeQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "20.1.1", + "jsonc-parser": "3.3.1", + "magic-string": "0.30.17", + "ora": "8.2.0", + "rxjs": "7.8.2" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular/animations": { + "version": "20.1.2", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-20.1.2.tgz", + "integrity": "sha512-r1JnNXZEg2Rrz53Mr4D4/S7v6ozZ3FPzJJo38lDq2WJKSkKc09R9fjFWIB/rXwEXUuiWEfNfxx+O4g6rrbXWWA==", + "license": "MIT", + "peer": true, + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@angular/common": "20.1.2", + "@angular/core": "20.1.2" + } + }, + "node_modules/@angular/build": { + "version": "20.1.1", + "resolved": "https://registry.npmjs.org/@angular/build/-/build-20.1.1.tgz", + "integrity": "sha512-N9tKfHatZEdy/uGX9atJQKVIejAvRbOMwpBj9Z5Y2RtR2vTDOOm0q86OYQW8baK19b2/HkHRe6PSPeiHpTG+8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "2.3.0", + "@angular-devkit/architect": "0.2001.1", + "@babel/core": "7.27.7", + "@babel/helper-annotate-as-pure": "7.27.3", + "@babel/helper-split-export-declaration": "7.24.7", + "@inquirer/confirm": "5.1.13", + "@vitejs/plugin-basic-ssl": "2.1.0", + "beasties": "0.3.4", + "browserslist": "^4.23.0", + "esbuild": "0.25.5", + "https-proxy-agent": "7.0.6", + "istanbul-lib-instrument": "6.0.3", + "jsonc-parser": "3.3.1", + "listr2": "8.3.3", + "magic-string": "0.30.17", + "mrmime": "2.0.1", + "parse5-html-rewriting-stream": "7.1.0", + "picomatch": "4.0.2", + "piscina": "5.1.2", + "rollup": "4.44.1", + "sass": "1.89.2", + "semver": "7.7.2", + "source-map-support": "0.5.21", + "tinyglobby": "0.2.14", + "vite": "7.0.0", + "watchpack": "2.4.4" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "optionalDependencies": { + "lmdb": "3.4.1" + }, + "peerDependencies": { + "@angular/compiler": "^20.0.0", + "@angular/compiler-cli": "^20.0.0", + "@angular/core": "^20.0.0", + "@angular/localize": "^20.0.0", + "@angular/platform-browser": "^20.0.0", + "@angular/platform-server": "^20.0.0", + "@angular/service-worker": "^20.0.0", + "@angular/ssr": "^20.1.1", + "karma": "^6.4.0", + "less": "^4.2.0", + "ng-packagr": "^20.0.0", + "postcss": "^8.4.0", + "tailwindcss": "^2.0.0 || ^3.0.0 || ^4.0.0", + "tslib": "^2.3.0", + "typescript": ">=5.8 <5.9", + "vitest": "^3.1.1" + }, + "peerDependenciesMeta": { + "@angular/core": { + "optional": true + }, + "@angular/localize": { + "optional": true + }, + "@angular/platform-browser": { + "optional": true + }, + "@angular/platform-server": { + "optional": true + }, + "@angular/service-worker": { + "optional": true + }, + "@angular/ssr": { + "optional": true + }, + "karma": { + "optional": true + }, + "less": { + "optional": true + }, + "ng-packagr": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tailwindcss": { + "optional": true + }, + "vitest": { + "optional": true + } + } + }, + "node_modules/@angular/cdk": { + "version": "20.1.2", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-20.1.2.tgz", + "integrity": "sha512-mmQdXfC86FMnH6iZvXvEmHunQpp2KULfoMQ1KhIMzVEuAmHRpkct7onjLeGUqZ+VEXchRG7/gYkMKYQxtG8sag==", + "license": "MIT", + "peer": true, + "dependencies": { + "parse5": "^8.0.0", + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/common": "^20.0.0 || ^21.0.0", + "@angular/core": "^20.0.0 || ^21.0.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/cdk/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", + "peer": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/@angular/cdk/node_modules/parse5": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz", + "integrity": "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==", + "license": "MIT", + "peer": true, + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/@angular/cli": { + "version": "20.1.1", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-20.1.1.tgz", + "integrity": "sha512-/lS7haW6YWy+KWkITtmfcKqq9Qsi2PP5mnPnZ2CqPgnYe6PCw+yx57tsU3qHHmYNGWnqHIvZWafBbabie18g8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/architect": "0.2001.1", + "@angular-devkit/core": "20.1.1", + "@angular-devkit/schematics": "20.1.1", + "@inquirer/prompts": "7.6.0", + "@listr2/prompt-adapter-inquirer": "2.0.22", + "@modelcontextprotocol/sdk": "1.13.3", + "@schematics/angular": "20.1.1", + "@yarnpkg/lockfile": "1.1.0", + "algoliasearch": "5.32.0", + "ini": "5.0.0", + "jsonc-parser": "3.3.1", + "listr2": "8.3.3", + "npm-package-arg": "12.0.2", + "npm-pick-manifest": "10.0.0", + "pacote": "21.0.0", + "resolve": "1.22.10", + "semver": "7.7.2", + "yargs": "18.0.0", + "zod": "3.25.75" + }, + "bin": { + "ng": "bin/ng.js" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular/common": { + "version": "20.1.2", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-20.1.2.tgz", + "integrity": "sha512-MQYP+4lvw81jBRknNYgIye7N36SD68SADUB7xO+7pF5+KbGundfmZkO29uWCnTBU86C4xU4DshlFVhzFK1lreQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@angular/core": "20.1.2", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/compiler": { + "version": "20.1.2", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-20.1.2.tgz", + "integrity": "sha512-BCYQArXAknOyMB5rgx9yK3p5uYFhgN91Jxo5Fbuso6M+7p1PoxOE4E9XrqQfhpVJOl9hcz7vNFnQ4Oer0R83UQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@angular/compiler-cli": { + "version": "20.1.2", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-20.1.2.tgz", + "integrity": "sha512-NMSDavN+CJYvSze6wq7DpbrUA/EqiAD7GQoeJtuOknzUpPlWQmFOoHzTMKW+S34XlNEw+YQT0trv3DKcrE+T/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "7.28.0", + "@jridgewell/sourcemap-codec": "^1.4.14", + "chokidar": "^4.0.0", + "convert-source-map": "^1.5.1", + "reflect-metadata": "^0.2.0", + "semver": "^7.0.0", + "tslib": "^2.3.0", + "yargs": "^18.0.0" + }, + "bin": { + "ng-xi18n": "bundles/src/bin/ng_xi18n.js", + "ngc": "bundles/src/bin/ngc.js" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@angular/compiler": "20.1.2", + "typescript": ">=5.8 <5.9" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@angular/compiler-cli/node_modules/@babel/core": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz", + "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.0", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.27.3", + "@babel/helpers": "^7.27.6", + "@babel/parser": "^7.28.0", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.0", + "@babel/types": "^7.28.0", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@angular/compiler-cli/node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@angular/compiler-cli/node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@angular/core": { + "version": "20.1.2", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-20.1.2.tgz", + "integrity": "sha512-8jAvpkHoXHSH0HoqNVgPstSMGmC0oaYN93HW7K2rMRxj1Uhtahkeb/7/kfnj7yLi5FDfm98ofOFT4Lxzf2eZXQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@angular/compiler": "20.1.2", + "rxjs": "^6.5.3 || ^7.4.0", + "zone.js": "~0.15.0" + }, + "peerDependenciesMeta": { + "@angular/compiler": { + "optional": true + }, + "zone.js": { + "optional": true + } + } + }, + "node_modules/@angular/forms": { + "version": "20.1.2", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-20.1.2.tgz", + "integrity": "sha512-ziOaeN0by1cTCNzwCo/IC2ekFzrM7ehc8uQHMQ6dYprSX45lJmdCsNnn+R0lx68VugvbMhHHO5ieOORf5sEmew==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@angular/common": "20.1.2", + "@angular/core": "20.1.2", + "@angular/platform-browser": "20.1.2", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/platform-browser": { + "version": "20.1.2", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-20.1.2.tgz", + "integrity": "sha512-jsgO4atyh6T3Rt+idHI29ENaq1a4VKfvtTgWf1S0qSCsfMt2kv5AAO+LkL6lYx8TtJu5zjAETiUwSiWUqY1jOg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@angular/animations": "20.1.2", + "@angular/common": "20.1.2", + "@angular/core": "20.1.2" + }, + "peerDependenciesMeta": { + "@angular/animations": { + "optional": true + } + } + }, + "node_modules/@angular/router": { + "version": "20.1.2", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-20.1.2.tgz", + "integrity": "sha512-xMRDARfSgwDZSorrTMtv9Gdb9UtWflwn8LOgmPbj3waXyuGWUbgpoJCD0Mh6necc9fhQ60GbBRG5K2EVVr3ATQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@angular/common": "20.1.2", + "@angular/core": "20.1.2", + "@angular/platform-browser": "20.1.2", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", + "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.27.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.7.tgz", + "integrity": "sha512-BU2f9tlKQ5CAthiMIgpzAh4eDTLWo1mqi9jqE2OxMG0E/OM199VJt2q8BztTxpnSW0i1ymdwLXRJnYzvDM5r2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.5", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.27.3", + "@babel/helpers": "^7.27.6", + "@babel/parser": "^7.27.7", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.27.7", + "@babel/types": "^7.27.7", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz", + "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.0", + "@babel/types": "^7.28.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", + "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", + "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.6.tgz", + "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", + "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz", + "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.0", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.1", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.1.tgz", + "integrity": "sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz", + "integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz", + "integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz", + "integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz", + "integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz", + "integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz", + "integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz", + "integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz", + "integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz", + "integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz", + "integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz", + "integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz", + "integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz", + "integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz", + "integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz", + "integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz", + "integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz", + "integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz", + "integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz", + "integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz", + "integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz", + "integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz", + "integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz", + "integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz", + "integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz", + "integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/checkbox": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.1.9.tgz", + "integrity": "sha512-DBJBkzI5Wx4jFaYm221LHvAhpKYkhVS0k9plqHwaHhofGNxvYB7J3Bz8w+bFJ05zaMb0sZNHo4KdmENQFlNTuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.14", + "@inquirer/figures": "^1.0.12", + "@inquirer/type": "^3.0.7", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/confirm": { + "version": "5.1.13", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.13.tgz", + "integrity": "sha512-EkCtvp67ICIVVzjsquUiVSd+V5HRGOGQfsqA4E4vMWhYnB7InUL0pa0TIWt1i+OfP16Gkds8CdIu6yGZwOM1Yw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.14", + "@inquirer/type": "^3.0.7" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/core": { + "version": "10.1.14", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.14.tgz", + "integrity": "sha512-Ma+ZpOJPewtIYl6HZHZckeX1STvDnHTCB2GVINNUlSEn2Am6LddWwfPkIGY0IUFVjUUrr/93XlBwTK6mfLjf0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/figures": "^1.0.12", + "@inquirer/type": "^3.0.7", + "ansi-escapes": "^4.3.2", + "cli-width": "^4.1.0", + "mute-stream": "^2.0.0", + "signal-exit": "^4.1.0", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/editor": { + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.14.tgz", + "integrity": "sha512-yd2qtLl4QIIax9DTMZ1ZN2pFrrj+yL3kgIWxm34SS6uwCr0sIhsNyudUjAo5q3TqI03xx4SEBkUJqZuAInp9uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.14", + "@inquirer/type": "^3.0.7", + "external-editor": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/expand": { + "version": "4.0.16", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.16.tgz", + "integrity": "sha512-oiDqafWzMtofeJyyGkb1CTPaxUkjIcSxePHHQCfif8t3HV9pHcw1Kgdw3/uGpDvaFfeTluwQtWiqzPVjAqS3zA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.14", + "@inquirer/type": "^3.0.7", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.12.tgz", + "integrity": "sha512-MJttijd8rMFcKJC8NYmprWr6hD3r9Gd9qUC0XwPNwoEPWSMVJwA2MlXxF+nhZZNMY+HXsWa+o7KY2emWYIn0jQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/input": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.2.0.tgz", + "integrity": "sha512-opqpHPB1NjAmDISi3uvZOTrjEEU5CWVu/HBkDby8t93+6UxYX0Z7Ps0Ltjm5sZiEbWenjubwUkivAEYQmy9xHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.14", + "@inquirer/type": "^3.0.7" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/number": { + "version": "3.0.16", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.16.tgz", + "integrity": "sha512-kMrXAaKGavBEoBYUCgualbwA9jWUx2TjMA46ek+pEKy38+LFpL9QHlTd8PO2kWPUgI/KB+qi02o4y2rwXbzr3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.14", + "@inquirer/type": "^3.0.7" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/password": { + "version": "4.0.16", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.16.tgz", + "integrity": "sha512-g8BVNBj5Zeb5/Y3cSN+hDUL7CsIFDIuVxb9EPty3lkxBaYpjL5BNRKSYOF9yOLe+JOcKFd+TSVeADQ4iSY7rbg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.14", + "@inquirer/type": "^3.0.7", + "ansi-escapes": "^4.3.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/prompts": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.6.0.tgz", + "integrity": "sha512-jAhL7tyMxB3Gfwn4HIJ0yuJ5pvcB5maYUcouGcgd/ub79f9MqZ+aVnBtuFf+VC2GTkCBF+R+eo7Vi63w5VZlzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/checkbox": "^4.1.9", + "@inquirer/confirm": "^5.1.13", + "@inquirer/editor": "^4.2.14", + "@inquirer/expand": "^4.0.16", + "@inquirer/input": "^4.2.0", + "@inquirer/number": "^3.0.16", + "@inquirer/password": "^4.0.16", + "@inquirer/rawlist": "^4.1.4", + "@inquirer/search": "^3.0.16", + "@inquirer/select": "^4.2.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/rawlist": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.4.tgz", + "integrity": "sha512-5GGvxVpXXMmfZNtvWw4IsHpR7RzqAR624xtkPd1NxxlV5M+pShMqzL4oRddRkg8rVEOK9fKdJp1jjVML2Lr7TQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.14", + "@inquirer/type": "^3.0.7", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/search": { + "version": "3.0.16", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.0.16.tgz", + "integrity": "sha512-POCmXo+j97kTGU6aeRjsPyuCpQQfKcMXdeTMw708ZMtWrj5aykZvlUxH4Qgz3+Y1L/cAVZsSpA+UgZCu2GMOMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.14", + "@inquirer/figures": "^1.0.12", + "@inquirer/type": "^3.0.7", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/select": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.2.4.tgz", + "integrity": "sha512-unTppUcTjmnbl/q+h8XeQDhAqIOmwWYWNyiiP2e3orXrg6tOaa5DHXja9PChCSbChOsktyKgOieRZFnajzxoBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^10.1.14", + "@inquirer/figures": "^1.0.12", + "@inquirer/type": "^3.0.7", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/type": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.7.tgz", + "integrity": "sha512-PfunHQcjwnju84L+ycmcMKB/pTPIngjUJvfnRhKY6FKPuYXlM4aQCb/nIdTFR6BEhMjFvngzvng/vBAJMZpLSA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", + "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", + "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.29", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", + "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@listr2/prompt-adapter-inquirer": { + "version": "2.0.22", + "resolved": "https://registry.npmjs.org/@listr2/prompt-adapter-inquirer/-/prompt-adapter-inquirer-2.0.22.tgz", + "integrity": "sha512-hV36ZoY+xKL6pYOt1nPNnkciFkn89KZwqLhAFzJvYysAvL5uBQdiADZx/8bIDXIukzzwG0QlPYolgMzQUtKgpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/type": "^1.5.5" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "@inquirer/prompts": ">= 3 < 8" + } + }, + "node_modules/@listr2/prompt-adapter-inquirer/node_modules/@inquirer/type": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-1.5.5.tgz", + "integrity": "sha512-MzICLu4yS7V8AA61sANROZ9vT1H3ooca5dSmI1FjZkzq7o/koMsRfQSzRtFo+F3Ao4Sf1C0bpLKejpKB/+j6MA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mute-stream": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@listr2/prompt-adapter-inquirer/node_modules/mute-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", + "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@lmdb/lmdb-darwin-arm64": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-3.4.1.tgz", + "integrity": "sha512-kKeP5PaY3bFrrF6GY5aDd96iuh1eoS+5CHJ+7hIP629KIEwzGNwbIzBmEX9TAhRJOivSRDTHCIsbu//+NsYKkg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@lmdb/lmdb-darwin-x64": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-3.4.1.tgz", + "integrity": "sha512-9CMB3seTyHs3EOVWdKiB8IIEDBJ3Gq00Tqyi0V7DS3HL90BjM/AkbZGuhzXwPrfeFazR24SKaRrUQF74f+CmWw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@lmdb/lmdb-linux-arm": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-3.4.1.tgz", + "integrity": "sha512-1Mi69vU0akHgCI7tF6YbimPaNEKJiBm/p5A+aM8egr0joj27cQmCCOm2mZQ+Ht2BqmCfZaIgQnMg4gFYNMlpCA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@lmdb/lmdb-linux-arm64": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-3.4.1.tgz", + "integrity": "sha512-d0vuXOdoKjHHJYZ/CRWopnkOiUpev+bgBBW+1tXtWsYWUj8uxl9ZmTBEmsL5mjUlpQDrlYiJSrhOU1hg5QWBSw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@lmdb/lmdb-linux-x64": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-3.4.1.tgz", + "integrity": "sha512-00RbEpvfnyPodlICiGFuiOmyvWaL9nzCRSqZz82BVFsGTiSQnnF0gpD1C8tO6OvtptELbtRuM7BS9f97LcowZw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@lmdb/lmdb-win32-arm64": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-arm64/-/lmdb-win32-arm64-3.4.1.tgz", + "integrity": "sha512-4h8tm3i1ODf+28UyqQZLP7c2jmRM26AyEEyYp994B4GiBdGvGAsYUu3oiHANYK9xFpvLuFzyGeqFm1kdNC0D1A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@lmdb/lmdb-win32-x64": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-3.4.1.tgz", + "integrity": "sha512-HqqKIhTbq6piJhkJpTTf3w1m/CgrmwXRAL9R9j7Ru5xdZSeO7Mg4AWiBC9B00uXR+LvVZKtUyRMVZfhmIZztmQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.13.3.tgz", + "integrity": "sha512-bGwA78F/U5G2jrnsdRkPY3IwIwZeWUEfb5o764b79lb0rJmMT76TLwKhdNZOWakOQtedYefwIR4emisEMvInKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.6", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.0.1", + "express-rate-limit": "^7.5.0", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.23.8", + "zod-to-json-schema": "^3.24.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": 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/@modelcontextprotocol/sdk/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz", + "integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz", + "integrity": "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz", + "integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz", + "integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz", + "integrity": "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz", + "integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@napi-rs/nice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@napi-rs/nice/-/nice-1.0.4.tgz", + "integrity": "sha512-Sqih1YARrmMoHlXGgI9JrrgkzxcaaEso0AH+Y7j8NHonUs+xe4iDsgC3IBIDNdzEewbNpccNN6hip+b5vmyRLw==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "optionalDependencies": { + "@napi-rs/nice-android-arm-eabi": "1.0.4", + "@napi-rs/nice-android-arm64": "1.0.4", + "@napi-rs/nice-darwin-arm64": "1.0.4", + "@napi-rs/nice-darwin-x64": "1.0.4", + "@napi-rs/nice-freebsd-x64": "1.0.4", + "@napi-rs/nice-linux-arm-gnueabihf": "1.0.4", + "@napi-rs/nice-linux-arm64-gnu": "1.0.4", + "@napi-rs/nice-linux-arm64-musl": "1.0.4", + "@napi-rs/nice-linux-ppc64-gnu": "1.0.4", + "@napi-rs/nice-linux-riscv64-gnu": "1.0.4", + "@napi-rs/nice-linux-s390x-gnu": "1.0.4", + "@napi-rs/nice-linux-x64-gnu": "1.0.4", + "@napi-rs/nice-linux-x64-musl": "1.0.4", + "@napi-rs/nice-win32-arm64-msvc": "1.0.4", + "@napi-rs/nice-win32-ia32-msvc": "1.0.4", + "@napi-rs/nice-win32-x64-msvc": "1.0.4" + } + }, + "node_modules/@napi-rs/nice-android-arm-eabi": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-android-arm-eabi/-/nice-android-arm-eabi-1.0.4.tgz", + "integrity": "sha512-OZFMYUkih4g6HCKTjqJHhMUlgvPiDuSLZPbPBWHLjKmFTv74COzRlq/gwHtmEVaR39mJQ6ZyttDl2HNMUbLVoA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-android-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-android-arm64/-/nice-android-arm64-1.0.4.tgz", + "integrity": "sha512-k8u7cjeA64vQWXZcRrPbmwjH8K09CBnNaPnI9L1D5N6iMPL3XYQzLcN6WwQonfcqCDv5OCY3IqX89goPTV4KMw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-darwin-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-darwin-arm64/-/nice-darwin-arm64-1.0.4.tgz", + "integrity": "sha512-GsLdQvUcuVzoyzmtjsThnpaVEizAqH5yPHgnsBmq3JdVoVZHELFo7PuJEdfOH1DOHi2mPwB9sCJEstAYf3XCJA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-darwin-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-darwin-x64/-/nice-darwin-x64-1.0.4.tgz", + "integrity": "sha512-1y3gyT3e5zUY5SxRl3QDtJiWVsbkmhtUHIYwdWWIQ3Ia+byd/IHIEpqAxOGW1nhhnIKfTCuxBadHQb+yZASVoA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-freebsd-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-freebsd-x64/-/nice-freebsd-x64-1.0.4.tgz", + "integrity": "sha512-06oXzESPRdXUuzS8n2hGwhM2HACnDfl3bfUaSqLGImM8TA33pzDXgGL0e3If8CcFWT98aHows5Lk7xnqYNGFeA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-arm-gnueabihf": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm-gnueabihf/-/nice-linux-arm-gnueabihf-1.0.4.tgz", + "integrity": "sha512-CgklZ6g8WL4+EgVVkxkEvvsi2DSLf9QIloxWO0fvQyQBp6VguUSX3eHLeRpqwW8cRm2Hv/Q1+PduNk7VK37VZw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-arm64-gnu": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm64-gnu/-/nice-linux-arm64-gnu-1.0.4.tgz", + "integrity": "sha512-wdAJ7lgjhAlsANUCv0zi6msRwq+D4KDgU+GCCHssSxWmAERZa2KZXO0H2xdmoJ/0i03i6YfK/sWaZgUAyuW2oQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-arm64-musl": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm64-musl/-/nice-linux-arm64-musl-1.0.4.tgz", + "integrity": "sha512-4b1KYG+sriufhFrpUS9uNOEYYJqSfcbnwGx6uGX7JjrH8tELG90cOpCawz5THNIwlS3DhLgnCOcn0+4p6z26QA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-ppc64-gnu": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-ppc64-gnu/-/nice-linux-ppc64-gnu-1.0.4.tgz", + "integrity": "sha512-iaf3vMRgr23oe1PUaKpxaH3DS0IMN0+N9iEiWVwYPm/U15vZFYdqVegGfN2PzrZLUl5lc8ZxbmEKDfuqslhAMA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-riscv64-gnu": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-riscv64-gnu/-/nice-linux-riscv64-gnu-1.0.4.tgz", + "integrity": "sha512-UXoREY6Yw6rHrGuTwQgBxpfjK34t6mTjibE9/cXbefL9AuUCJ9gEgwNKZiONuR5QGswChqo9cnthjdKkYyAdDg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-s390x-gnu": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-s390x-gnu/-/nice-linux-s390x-gnu-1.0.4.tgz", + "integrity": "sha512-eFbgYCRPmsqbYPAlLYU5hYTNbogmIDUvknilehHsFhCH1+0/kN87lP+XaLT0Yeq4V/rpwChSd9vlz4muzFArtw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-x64-gnu": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-x64-gnu/-/nice-linux-x64-gnu-1.0.4.tgz", + "integrity": "sha512-4T3E6uTCwWT6IPnwuPcWVz3oHxvEp/qbrCxZhsgzwTUBEwu78EGNXGdHfKJQt3soth89MLqZJw+Zzvnhrsg1mQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-linux-x64-musl": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-x64-musl/-/nice-linux-x64-musl-1.0.4.tgz", + "integrity": "sha512-NtbBkAeyBPLvCBkWtwkKXkNSn677eaT0cX3tygq+2qVv71TmHgX4gkX6o9BXjlPzdgPGwrUudavCYPT9tzkEqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-win32-arm64-msvc": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-arm64-msvc/-/nice-win32-arm64-msvc-1.0.4.tgz", + "integrity": "sha512-vubOe3i+YtSJGEk/++73y+TIxbuVHi+W8ZzrRm2eETCjCRwNlgbfToQZ85dSA+4iBB/NJRGNp+O4hfdbbttZWA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-win32-ia32-msvc": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-ia32-msvc/-/nice-win32-ia32-msvc-1.0.4.tgz", + "integrity": "sha512-BMOVrUDZeg1RNRKVlh4eyLv5djAAVLiSddfpuuQ47EFjBcklg0NUeKMFKNrKQR4UnSn4HAiACLD7YK7koskwmg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/nice-win32-x64-msvc": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-x64-msvc/-/nice-win32-x64-msvc-1.0.4.tgz", + "integrity": "sha512-kCNk6HcRZquhw/whwh4rHsdPyOSCQCgnVDVik+Y9cuSVTDy3frpiCJTScJqPPS872h4JgZKkr/+CwcwttNEo9Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@npmcli/agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-3.0.0.tgz", + "integrity": "sha512-S79NdEgDQd/NGCay6TCoVzXSj74skRZIKJcpJjC5lOq34SZzyI6MqtiiWoiVWoVrTcGjNeC4ipbh1VIHlpfF5Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@npmcli/agent/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/@npmcli/fs": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-4.0.0.tgz", + "integrity": "sha512-/xGlezI6xfGO9NwuJlnwz/K14qD1kCSAGtacBHnGzeAIuJGazcp45KP5NuyARXoKb7cwulAGWVsbeSxdG/cb0Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@npmcli/git": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-6.0.3.tgz", + "integrity": "sha512-GUYESQlxZRAdhs3UhbB6pVRNUELQOHXwK9ruDkwmCv2aZ5y0SApQzUJCg02p3A7Ue2J5hxvlk1YI53c00NmRyQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/promise-spawn": "^8.0.0", + "ini": "^5.0.0", + "lru-cache": "^10.0.1", + "npm-pick-manifest": "^10.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@npmcli/git/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/@npmcli/git/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/@npmcli/git/node_modules/which": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", + "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@npmcli/installed-package-contents": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-3.0.0.tgz", + "integrity": "sha512-fkxoPuFGvxyrH+OQzyTkX2LUEamrF4jZSmxjAtPPHHGO0dqsQ8tTKjnIS8SAnPHdk2I03BDtSMR5K/4loKg79Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "npm-bundled": "^4.0.0", + "npm-normalize-package-bin": "^4.0.0" + }, + "bin": { + "installed-package-contents": "bin/index.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@npmcli/node-gyp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-4.0.0.tgz", + "integrity": "sha512-+t5DZ6mO/QFh78PByMq1fGSAub/agLJZDRfJRMeOSNCt8s9YVlTjmGpIPwPhvXTGUIJk+WszlT0rQa1W33yzNA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@npmcli/package-json": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-6.2.0.tgz", + "integrity": "sha512-rCNLSB/JzNvot0SEyXqWZ7tX2B5dD2a1br2Dp0vSYVo5jh8Z0EZ7lS9TsZ1UtziddB1UfNUaMCc538/HztnJGA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.0", + "glob": "^10.2.2", + "hosted-git-info": "^8.0.0", + "json-parse-even-better-errors": "^4.0.0", + "proc-log": "^5.0.0", + "semver": "^7.5.3", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@npmcli/package-json/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@npmcli/package-json/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/package-json/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/promise-spawn": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-8.0.2.tgz", + "integrity": "sha512-/bNJhjc+o6qL+Dwz/bqfTQClkEO5nTQ1ZEcdCkAQjhkZMHIh22LPG7fNh1enJP1NKWDqYiiABnjFCY7E0zHYtQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@npmcli/promise-spawn/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/@npmcli/promise-spawn/node_modules/which": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", + "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@npmcli/redact": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@npmcli/redact/-/redact-3.2.2.tgz", + "integrity": "sha512-7VmYAmk4csGv08QzrDKScdzn11jHPFGyqJW39FyPgPuAp3zIaUmuCo1yxw9aGs+NEJuTGQ9Gwqpt93vtJubucg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@npmcli/run-script": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-9.1.0.tgz", + "integrity": "sha512-aoNSbxtkePXUlbZB+anS1LqsJdctG5n3UVhfU47+CDdwMi6uNTBMF9gPcQRnqghQd2FGzcwwIFBruFMxjhBewg==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/node-gyp": "^4.0.0", + "@npmcli/package-json": "^6.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "node-gyp": "^11.0.0", + "proc-log": "^5.0.0", + "which": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@npmcli/run-script/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/@npmcli/run-script/node_modules/which": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", + "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", + "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.1", + "@parcel/watcher-darwin-arm64": "2.5.1", + "@parcel/watcher-darwin-x64": "2.5.1", + "@parcel/watcher-freebsd-x64": "2.5.1", + "@parcel/watcher-linux-arm-glibc": "2.5.1", + "@parcel/watcher-linux-arm-musl": "2.5.1", + "@parcel/watcher-linux-arm64-glibc": "2.5.1", + "@parcel/watcher-linux-arm64-musl": "2.5.1", + "@parcel/watcher-linux-x64-glibc": "2.5.1", + "@parcel/watcher-linux-x64-musl": "2.5.1", + "@parcel/watcher-win32-arm64": "2.5.1", + "@parcel/watcher-win32-ia32": "2.5.1", + "@parcel/watcher-win32-x64": "2.5.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", + "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", + "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", + "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", + "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", + "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", + "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", + "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", + "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", + "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", + "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", + "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", + "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", + "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher/node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/@parcel/watcher/node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@primeuix/styled": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@primeuix/styled/-/styled-0.7.0.tgz", + "integrity": "sha512-xUqMdQb75izeDkNWFK1QlU15aUl5LIU97Fq68IXOhrqqLsKEBnj5ftntFZrENQW70jAHwALdWP4EOGi/poc9Tg==", + "license": "MIT", + "dependencies": { + "@primeuix/utils": "^0.6.0" + }, + "engines": { + "node": ">=12.11.0" + } + }, + "node_modules/@primeuix/styles": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@primeuix/styles/-/styles-1.2.1.tgz", + "integrity": "sha512-Tri7pPgZgxrVmhJG8ijZZFolQ6vu27xnkGoAB9EFY8YlaKTM5iqkWzEcqdxy2KmgFWMXi+BrPHwO0RdQ6JCT+g==", + "license": "MIT", + "dependencies": { + "@primeuix/styled": "^0.7.0" + } + }, + "node_modules/@primeuix/themes": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@primeuix/themes/-/themes-1.2.1.tgz", + "integrity": "sha512-DVCFDncvag47tpag3TdufDTuvUbfKnkgzmlxAQkwuMS0IlPA3wChrf9VlL2wETg/ZpJm/tHobkJBnB9FmkiqnA==", + "license": "MIT", + "dependencies": { + "@primeuix/styled": "^0.7.0" + } + }, + "node_modules/@primeuix/utils": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@primeuix/utils/-/utils-0.6.0.tgz", + "integrity": "sha512-ULpB87ImNAiX36OMtyDeRceWB7N/mVlh6gGLqp/lx8UMKZlLIQH/UAFND86hYXHwNpXeNKcWfMGreb0Oc0hcZA==", + "license": "MIT", + "engines": { + "node": ">=12.11.0" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.44.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.1.tgz", + "integrity": "sha512-JAcBr1+fgqx20m7Fwe1DxPUl/hPkee6jA6Pl7n1v2EFiktAHenTaXl5aIFjUIEsfn9w3HE4gK1lEgNGMzBDs1w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.44.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.44.1.tgz", + "integrity": "sha512-RurZetXqTu4p+G0ChbnkwBuAtwAbIwJkycw1n6GvlGlBuS4u5qlr5opix8cBAYFJgaY05TWtM+LaoFggUmbZEQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.44.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.44.1.tgz", + "integrity": "sha512-fM/xPesi7g2M7chk37LOnmnSTHLG/v2ggWqKj3CCA1rMA4mm5KVBT1fNoswbo1JhPuNNZrVwpTvlCVggv8A2zg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.44.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.44.1.tgz", + "integrity": "sha512-gDnWk57urJrkrHQ2WVx9TSVTH7lSlU7E3AFqiko+bgjlh78aJ88/3nycMax52VIVjIm3ObXnDL2H00e/xzoipw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.44.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.44.1.tgz", + "integrity": "sha512-wnFQmJ/zPThM5zEGcnDcCJeYJgtSLjh1d//WuHzhf6zT3Md1BvvhJnWoy+HECKu2bMxaIcfWiu3bJgx6z4g2XA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.44.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.44.1.tgz", + "integrity": "sha512-uBmIxoJ4493YATvU2c0upGz87f99e3wop7TJgOA/bXMFd2SvKCI7xkxY/5k50bv7J6dw1SXT4MQBQSLn8Bb/Uw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.44.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.44.1.tgz", + "integrity": "sha512-n0edDmSHlXFhrlmTK7XBuwKlG5MbS7yleS1cQ9nn4kIeW+dJH+ExqNgQ0RrFRew8Y+0V/x6C5IjsHrJmiHtkxQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.44.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.44.1.tgz", + "integrity": "sha512-8WVUPy3FtAsKSpyk21kV52HCxB+me6YkbkFHATzC2Yd3yuqHwy2lbFL4alJOLXKljoRw08Zk8/xEj89cLQ/4Nw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.44.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.44.1.tgz", + "integrity": "sha512-yuktAOaeOgorWDeFJggjuCkMGeITfqvPgkIXhDqsfKX8J3jGyxdDZgBV/2kj/2DyPaLiX6bPdjJDTu9RB8lUPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.44.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.44.1.tgz", + "integrity": "sha512-W+GBM4ifET1Plw8pdVaecwUgxmiH23CfAUj32u8knq0JPFyK4weRy6H7ooxYFD19YxBulL0Ktsflg5XS7+7u9g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.44.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.44.1.tgz", + "integrity": "sha512-1zqnUEMWp9WrGVuVak6jWTl4fEtrVKfZY7CvcBmUUpxAJ7WcSowPSAWIKa/0o5mBL/Ij50SIf9tuirGx63Ovew==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.44.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.44.1.tgz", + "integrity": "sha512-Rl3JKaRu0LHIx7ExBAAnf0JcOQetQffaw34T8vLlg9b1IhzcBgaIdnvEbbsZq9uZp3uAH+JkHd20Nwn0h9zPjA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.44.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.44.1.tgz", + "integrity": "sha512-j5akelU3snyL6K3N/iX7otLBIl347fGwmd95U5gS/7z6T4ftK288jKq3A5lcFKcx7wwzb5rgNvAg3ZbV4BqUSw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.44.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.44.1.tgz", + "integrity": "sha512-ppn5llVGgrZw7yxbIm8TTvtj1EoPgYUAbfw0uDjIOzzoqlZlZrLJ/KuiE7uf5EpTpCTrNt1EdtzF0naMm0wGYg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.44.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.44.1.tgz", + "integrity": "sha512-Hu6hEdix0oxtUma99jSP7xbvjkUM/ycke/AQQ4EC5g7jNRLLIwjcNwaUy95ZKBJJwg1ZowsclNnjYqzN4zwkAw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.44.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.44.1.tgz", + "integrity": "sha512-EtnsrmZGomz9WxK1bR5079zee3+7a+AdFlghyd6VbAjgRJDbTANJ9dcPIPAi76uG05micpEL+gPGmAKYTschQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.44.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.44.1.tgz", + "integrity": "sha512-iAS4p+J1az6Usn0f8xhgL4PaU878KEtutP4hqw52I4IO6AGoyOkHCxcc4bqufv1tQLdDWFx8lR9YlwxKuv3/3g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.44.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.44.1.tgz", + "integrity": "sha512-NtSJVKcXwcqozOl+FwI41OH3OApDyLk3kqTJgx8+gp6On9ZEt5mYhIsKNPGuaZr3p9T6NWPKGU/03Vw4CNU9qg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.44.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.44.1.tgz", + "integrity": "sha512-JYA3qvCOLXSsnTR3oiyGws1Dm0YTuxAAeaYGVlGpUsHqloPcFjPg+X0Fj2qODGLNwQOAcCiQmHub/V007kiH5A==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.44.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.44.1.tgz", + "integrity": "sha512-J8o22LuF0kTe7m+8PvW9wk3/bRq5+mRo5Dqo6+vXb7otCm3TPhYOJqOaQtGU9YMWQSL3krMnoOxMr0+9E6F3Ug==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@schematics/angular": { + "version": "20.1.1", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-20.1.1.tgz", + "integrity": "sha512-eOEKBRcxt68xzZmqlgMJ5m9FOClzZumyltQhiBeAQfCrMAjxJZaB+pbyYreI+2DL91d/VkldJ9D/UcHZrhfLnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "20.1.1", + "@angular-devkit/schematics": "20.1.1", + "jsonc-parser": "3.3.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@sigstore/bundle": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-3.1.0.tgz", + "integrity": "sha512-Mm1E3/CmDDCz3nDhFKTuYdB47EdRFRQMOE/EAbiG1MJW77/w1b3P7Qx7JSrVJs8PfwOLOVcKQCHErIwCTyPbag==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.4.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@sigstore/core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sigstore/core/-/core-2.0.0.tgz", + "integrity": "sha512-nYxaSb/MtlSI+JWcwTHQxyNmWeWrUXJJ/G4liLrGG7+tS4vAz6LF3xRXqLH6wPIVUoZQel2Fs4ddLx4NCpiIYg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@sigstore/protobuf-specs": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.4.3.tgz", + "integrity": "sha512-fk2zjD9117RL9BjqEwF7fwv7Q/P9yGsMV4MUJZ/DocaQJ6+3pKr+syBq1owU5Q5qGw5CUbXzm+4yJ2JVRDQeSA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@sigstore/sign": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-3.1.0.tgz", + "integrity": "sha512-knzjmaOHOov1Ur7N/z4B1oPqZ0QX5geUfhrVaqVlu+hl0EAoL4o+l0MSULINcD5GCWe3Z0+YJO8ues6vFlW0Yw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^3.1.0", + "@sigstore/core": "^2.0.0", + "@sigstore/protobuf-specs": "^0.4.0", + "make-fetch-happen": "^14.0.2", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@sigstore/tuf": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-3.1.1.tgz", + "integrity": "sha512-eFFvlcBIoGwVkkwmTi/vEQFSva3xs5Ot3WmBcjgjVdiaoelBLQaQ/ZBfhlG0MnG0cmTYScPpk7eDdGDWUcFUmg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.4.1", + "tuf-js": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@sigstore/verify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@sigstore/verify/-/verify-2.1.1.tgz", + "integrity": "sha512-hVJD77oT67aowHxwT4+M6PGOp+E2LtLdTK3+FC0lBO9T7sYwItDMXZ7Z07IDCvR1M717a4axbIWckrW67KMP/w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^3.1.0", + "@sigstore/core": "^2.0.0", + "@sigstore/protobuf-specs": "^0.4.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tailwindcss/node": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.11.tgz", + "integrity": "sha512-yzhzuGRmv5QyU9qLNg4GTlYI6STedBWRE7NjxP45CsFYYq9taI0zJXZBMqIC/c8fViNLhmrbpSFS57EoxUmD6Q==", + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.3.0", + "enhanced-resolve": "^5.18.1", + "jiti": "^2.4.2", + "lightningcss": "1.30.1", + "magic-string": "^0.30.17", + "source-map-js": "^1.2.1", + "tailwindcss": "4.1.11" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.11.tgz", + "integrity": "sha512-Q69XzrtAhuyfHo+5/HMgr1lAiPP/G40OMFAnws7xcFEYqcypZmdW8eGXaOUIeOl1dzPJBPENXgbjsOyhg2nkrg==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.4", + "tar": "^7.4.3" + }, + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.1.11", + "@tailwindcss/oxide-darwin-arm64": "4.1.11", + "@tailwindcss/oxide-darwin-x64": "4.1.11", + "@tailwindcss/oxide-freebsd-x64": "4.1.11", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.11", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.11", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.11", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.11", + "@tailwindcss/oxide-linux-x64-musl": "4.1.11", + "@tailwindcss/oxide-wasm32-wasi": "4.1.11", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.11", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.11" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.11.tgz", + "integrity": "sha512-3IfFuATVRUMZZprEIx9OGDjG3Ou3jG4xQzNTvjDoKmU9JdmoCohQJ83MYd0GPnQIu89YoJqvMM0G3uqLRFtetg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.11.tgz", + "integrity": "sha512-ESgStEOEsyg8J5YcMb1xl8WFOXfeBmrhAwGsFxxB2CxY9evy63+AtpbDLAyRkJnxLy2WsD1qF13E97uQyP1lfQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.11.tgz", + "integrity": "sha512-EgnK8kRchgmgzG6jE10UQNaH9Mwi2n+yw1jWmof9Vyg2lpKNX2ioe7CJdf9M5f8V9uaQxInenZkOxnTVL3fhAw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.11.tgz", + "integrity": "sha512-xdqKtbpHs7pQhIKmqVpxStnY1skuNh4CtbcyOHeX1YBE0hArj2romsFGb6yUmzkq/6M24nkxDqU8GYrKrz+UcA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.11.tgz", + "integrity": "sha512-ryHQK2eyDYYMwB5wZL46uoxz2zzDZsFBwfjssgB7pzytAeCCa6glsiJGjhTEddq/4OsIjsLNMAiMlHNYnkEEeg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.11.tgz", + "integrity": "sha512-mYwqheq4BXF83j/w75ewkPJmPZIqqP1nhoghS9D57CLjsh3Nfq0m4ftTotRYtGnZd3eCztgbSPJ9QhfC91gDZQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.11.tgz", + "integrity": "sha512-m/NVRFNGlEHJrNVk3O6I9ggVuNjXHIPoD6bqay/pubtYC9QIdAMpS+cswZQPBLvVvEF6GtSNONbDkZrjWZXYNQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.11.tgz", + "integrity": "sha512-YW6sblI7xukSD2TdbbaeQVDysIm/UPJtObHJHKxDEcW2exAtY47j52f8jZXkqE1krdnkhCMGqP3dbniu1Te2Fg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.11.tgz", + "integrity": "sha512-e3C/RRhGunWYNC3aSF7exsQkdXzQ/M+aYuZHKnw4U7KQwTJotnWsGOIVih0s2qQzmEzOFIJ3+xt7iq67K/p56Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.11.tgz", + "integrity": "sha512-Xo1+/GU0JEN/C/dvcammKHzeM6NqKovG+6921MR6oadee5XPBaKOumrJCXvopJ/Qb5TH7LX/UAywbqrP4lax0g==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@emnapi/wasi-threads": "^1.0.2", + "@napi-rs/wasm-runtime": "^0.2.11", + "@tybys/wasm-util": "^0.9.0", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.11.tgz", + "integrity": "sha512-UgKYx5PwEKrac3GPNPf6HVMNhUIGuUh4wlDFR2jYYdkX6pL/rn73zTq/4pzUm8fOjAn5L8zDeHp9iXmUGOXZ+w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.11.tgz", + "integrity": "sha512-YfHoggn1j0LK7wR82TOucWc5LDCguHnoS879idHekmmiR7g9HUtMw9MI0NHatS28u/Xlkfi9w5RJWgz2Dl+5Qg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide/node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@tailwindcss/oxide/node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@tailwindcss/oxide/node_modules/tar": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", + "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@tailwindcss/oxide/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@tailwindcss/postcss": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.11.tgz", + "integrity": "sha512-q/EAIIpF6WpLhKEuQSEVMZNMIY8KhWoAemZ9eylNAih9jxMGAYPPWBn3I9QL/2jZ+e7OEz/tZkX5HwbBR4HohA==", + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "@tailwindcss/node": "4.1.11", + "@tailwindcss/oxide": "4.1.11", + "postcss": "^8.4.41", + "tailwindcss": "4.1.11" + } + }, + "node_modules/@tufjs/canonical-json": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz", + "integrity": "sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@tufjs/models": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-3.0.1.tgz", + "integrity": "sha512-UUYHISyhCU3ZgN8yaear3cGATHb3SMuKHsQ/nVbHXcmnBf+LzQ/cQfhNG+rfaSHgqGKNEm2cOCLVLELStUQ1JA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tufjs/canonical-json": "2.0.0", + "minimatch": "^9.0.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/@tufjs/models/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@tufjs/models/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@types/cors": { + "version": "2.8.19", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", + "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/jasmine": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-5.1.8.tgz", + "integrity": "sha512-u7/CnvRdh6AaaIzYjCgUuVbREFgulhX05Qtf6ZtW+aOcjCKKVvKgpkPYJBFTZSHtFBYimzU4zP0V2vrEsq9Wcg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.0.15", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.15.tgz", + "integrity": "sha512-oaeTSbCef7U/z7rDeJA138xpG3NuKc64/rZ2qmUFkFJmnMsAPaluIifqyWd8hSSMxyP9oie3dLAqYPblag9KgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.8.0" + } + }, + "node_modules/@vitejs/plugin-basic-ssl": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-2.1.0.tgz", + "integrity": "sha512-dOxxrhgyDIEUADhb/8OlV9JIqYLgos03YorAueTIeOUskLJSEsfwCByjbu98ctXitUN3znXKp0bYD/WHSudCeA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "peerDependencies": { + "vite": "^6.0.0 || ^7.0.0" + } + }, + "node_modules/@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/abbrev": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-3.0.1.tgz", + "integrity": "sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/algoliasearch": { + "version": "5.32.0", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.32.0.tgz", + "integrity": "sha512-84xBncKNPBK8Ae89F65+SyVcOihrIbm/3N7to+GpRBHEUXGjA3ydWTMpcRW6jmFzkBQ/eqYy/y+J+NBpJWYjBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-abtesting": "5.32.0", + "@algolia/client-analytics": "5.32.0", + "@algolia/client-common": "5.32.0", + "@algolia/client-insights": "5.32.0", + "@algolia/client-personalization": "5.32.0", + "@algolia/client-query-suggestions": "5.32.0", + "@algolia/client-search": "5.32.0", + "@algolia/ingestion": "1.32.0", + "@algolia/monitoring": "1.32.0", + "@algolia/recommend": "5.32.0", + "@algolia/requester-browser-xhr": "5.32.0", + "@algolia/requester-fetch": "5.32.0", + "@algolia/requester-node-http": "5.32.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/beasties": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/beasties/-/beasties-0.3.4.tgz", + "integrity": "sha512-NmzN1zN1cvGccXFyZ73335+ASXwBlVWcUPssiUDIlFdfyatHPRRufjCd5w8oPaQPvVnf9ELklaCGb1gi9FBwIw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "htmlparser2": "^10.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.49", + "postcss-media-query-parser": "^0.2.3" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/body-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", + "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.0", + "http-errors": "^2.0.0", + "iconv-lite": "^0.6.3", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.0", + "type-is": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true, + "license": "ISC" + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.25.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz", + "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001726", + "electron-to-chromium": "^1.5.173", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacache": { + "version": "19.0.1", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-19.0.1.tgz", + "integrity": "sha512-hdsUxulXCi5STId78vRVYEtDAjq99ICAUktLTeTYsLoTE6Z8dS0c8pWNCxwdrk9YfJeobDZc2Y186hD/5ZQgFQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^4.0.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^7.0.2", + "ssri": "^12.0.0", + "tar": "^7.4.3", + "unique-filename": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/cacache/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/cacache/node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/cacache/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cacache/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/cacache/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cacache/node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cacache/node_modules/tar": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", + "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/cacache/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001727", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz", + "integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true, + "license": "MIT" + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", + "dev": true, + "license": "MIT", + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 12" + } + }, + "node_modules/cliui": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", + "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/connect": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/connect/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/connect/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/connect/node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/connect/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/connect/node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/connect/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-disposition": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", + "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/custom-event": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", + "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==", + "dev": true, + "license": "MIT" + }, + "node_modules/date-format": { + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz", + "integrity": "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-libc": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/di": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", + "integrity": "sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==", + "dev": true, + "license": "MIT" + }, + "node_modules/dom-serialize": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", + "integrity": "sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "custom-event": "~1.0.0", + "ent": "~2.2.0", + "extend": "^3.0.0", + "void-elements": "^2.0.0" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true, + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.187", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.187.tgz", + "integrity": "sha512-cl5Jc9I0KGUoOoSbxvTywTa40uspGJt/BDBoDLoxJRSBpWh4FFXBsjNRHfQrONsV/OoEjDfHUmZQa2d6Ze4YgA==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "dev": true, + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/engine.io": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz", + "integrity": "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.7.2", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.18.2", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.2.tgz", + "integrity": "sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/ent": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.2.tgz", + "integrity": "sha512-kKvD1tO6BM+oK9HzCPpUdRb4vKFQY/FPTFmurMvh6LlN68VMrdj77w8yp51/kDbpkFOS9J8w5W6zIzgM2H8/hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "punycode": "^1.4.1", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz", + "integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.5", + "@esbuild/android-arm": "0.25.5", + "@esbuild/android-arm64": "0.25.5", + "@esbuild/android-x64": "0.25.5", + "@esbuild/darwin-arm64": "0.25.5", + "@esbuild/darwin-x64": "0.25.5", + "@esbuild/freebsd-arm64": "0.25.5", + "@esbuild/freebsd-x64": "0.25.5", + "@esbuild/linux-arm": "0.25.5", + "@esbuild/linux-arm64": "0.25.5", + "@esbuild/linux-ia32": "0.25.5", + "@esbuild/linux-loong64": "0.25.5", + "@esbuild/linux-mips64el": "0.25.5", + "@esbuild/linux-ppc64": "0.25.5", + "@esbuild/linux-riscv64": "0.25.5", + "@esbuild/linux-s390x": "0.25.5", + "@esbuild/linux-x64": "0.25.5", + "@esbuild/netbsd-arm64": "0.25.5", + "@esbuild/netbsd-x64": "0.25.5", + "@esbuild/openbsd-arm64": "0.25.5", + "@esbuild/openbsd-x64": "0.25.5", + "@esbuild/sunos-x64": "0.25.5", + "@esbuild/win32-arm64": "0.25.5", + "@esbuild/win32-ia32": "0.25.5", + "@esbuild/win32-x64": "0.25.5" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true, + "license": "MIT" + }, + "node_modules/eventsource": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eventsource-parser": "^3.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eventsource-parser": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.3.tgz", + "integrity": "sha512-nVpZkTMM9rF6AQ9gPJpFsNAMt48wIzB5TQgiTLdHiuO8XEDhUgZEhqKlZWXbIzo9VmJ/HvysHqEaVeD5v9TPvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/exponential-backoff": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.2.tgz", + "integrity": "sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/express": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.0", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-rate-limit": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.1.tgz", + "integrity": "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "license": "MIT", + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/external-editor/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fdir": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", + "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fs-minipass": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", + "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", + "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "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.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hosted-git-info": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-8.1.0.tgz", + "integrity": "sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/htmlparser2": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.0.0.tgz", + "integrity": "sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.1", + "entities": "^6.0.0" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore-walk": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-7.0.0.tgz", + "integrity": "sha512-T4gbf83A4NH95zvhVYZc+qWocBBGlpzUXLPGurJggw/WIOwicfXJChLDP/iBZnN5WqROSu5Bm3hhle4z8a8YGQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/ignore-walk/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/ignore-walk/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/immutable": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.3.tgz", + "integrity": "sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==", + "dev": true, + "license": "MIT" + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ini": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-5.0.0.tgz", + "integrity": "sha512-+N0ngpO3e7cRUWOJAS7qw0IZIVc6XPrW4MlFBdD066F2L4k1L6ker3hLqSq7iXxU5tgS4WGkIUElWn5vogAEnw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isbinaryfile": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jasmine-core": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.8.0.tgz", + "integrity": "sha512-Q9dqmpUAfptwyueW3+HqBOkSuYd9I/clZSSfN97wXE/Nr2ROFNCwIBEC1F6kb3QXS9Fcz0LjFYSDQT+BiwjuhA==", + "dev": true, + "license": "MIT" + }, + "node_modules/jiti": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", + "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-4.0.0.tgz", + "integrity": "sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true, + "engines": [ + "node >= 0.2.0" + ], + "license": "MIT" + }, + "node_modules/karma": { + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.4.tgz", + "integrity": "sha512-LrtUxbdvt1gOpo3gxG+VAJlJAEMhbWlM4YrFQgql98FwF7+K8K12LYO4hnDdUkNjeztYrOXEMqgTajSWgmtI/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@colors/colors": "1.5.0", + "body-parser": "^1.19.0", + "braces": "^3.0.2", + "chokidar": "^3.5.1", + "connect": "^3.7.0", + "di": "^0.0.1", + "dom-serialize": "^2.2.1", + "glob": "^7.1.7", + "graceful-fs": "^4.2.6", + "http-proxy": "^1.18.1", + "isbinaryfile": "^4.0.8", + "lodash": "^4.17.21", + "log4js": "^6.4.1", + "mime": "^2.5.2", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.5", + "qjobs": "^1.2.0", + "range-parser": "^1.2.1", + "rimraf": "^3.0.2", + "socket.io": "^4.7.2", + "source-map": "^0.6.1", + "tmp": "^0.2.1", + "ua-parser-js": "^0.7.30", + "yargs": "^16.1.1" + }, + "bin": { + "karma": "bin/karma" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/karma-chrome-launcher": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.2.0.tgz", + "integrity": "sha512-rE9RkUPI7I9mAxByQWkGJFXfFD6lE4gC5nPuZdobf/QdTEJI6EU4yIay/cfU/xV4ZxlM5JiTv7zWYgA64NpS5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "which": "^1.2.1" + } + }, + "node_modules/karma-chrome-launcher/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/karma-coverage": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/karma-coverage/-/karma-coverage-2.2.1.tgz", + "integrity": "sha512-yj7hbequkQP2qOSb20GuNSIyE//PgJWHwC2IydLE6XRtsnaflv+/OSGNssPjobYUlhVVagy99TQpqUt3vAUG7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.1", + "istanbul-reports": "^3.0.5", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/karma-coverage/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/karma-coverage/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/karma-jasmine": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-5.1.0.tgz", + "integrity": "sha512-i/zQLFrfEpRyQoJF9fsCdTMOF5c2dK7C7OmsuKg2D0YSsuZSfQDiLuaiktbuio6F2wiCsZSnSnieIQ0ant/uzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "jasmine-core": "^4.1.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "karma": "^6.0.0" + } + }, + "node_modules/karma-jasmine-html-reporter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-2.1.0.tgz", + "integrity": "sha512-sPQE1+nlsn6Hwb5t+HHwyy0A1FNCVKuL1192b+XNauMYWThz2kweiBVW1DqloRpVvZIJkIoHVB7XRpK78n1xbQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "jasmine-core": "^4.0.0 || ^5.0.0", + "karma": "^6.0.0", + "karma-jasmine": "^5.0.0" + } + }, + "node_modules/karma-jasmine/node_modules/jasmine-core": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-4.6.1.tgz", + "integrity": "sha512-VYz/BjjmC3klLJlLwA4Kw8ytk0zDSmbbDLNs794VnWmkcCB7I9aAL/D48VNQtmITyPvea2C3jdUMfc3kAoy0PQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/karma/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/karma/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "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/karma/node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/karma/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/karma/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/karma/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/karma/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/karma/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/karma/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/karma/node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/karma/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/karma/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/karma/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/karma/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/karma/node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/karma/node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/karma/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/karma/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/karma/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/karma/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/karma/node_modules/tmp": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/karma/node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/karma/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "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/karma/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "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/karma/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/lightningcss": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", + "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.30.1", + "lightningcss-darwin-x64": "1.30.1", + "lightningcss-freebsd-x64": "1.30.1", + "lightningcss-linux-arm-gnueabihf": "1.30.1", + "lightningcss-linux-arm64-gnu": "1.30.1", + "lightningcss-linux-arm64-musl": "1.30.1", + "lightningcss-linux-x64-gnu": "1.30.1", + "lightningcss-linux-x64-musl": "1.30.1", + "lightningcss-win32-arm64-msvc": "1.30.1", + "lightningcss-win32-x64-msvc": "1.30.1" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz", + "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz", + "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz", + "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz", + "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz", + "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz", + "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", + "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz", + "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz", + "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz", + "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/listr2": { + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.3.3.tgz", + "integrity": "sha512-LWzX2KsqcB1wqQ4AHgYb4RsDXauQiqhjLk+6hjbaeHG4zpjjVAB6wC/gz6X0l+Du1cN3pUB5ZlrvTbhGSNnUQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "cli-truncate": "^4.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.1.0", + "rfdc": "^1.4.1", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/listr2/node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true, + "license": "MIT" + }, + "node_modules/listr2/node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/lmdb": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-3.4.1.tgz", + "integrity": "sha512-hoG9RIv42kdGJiieyElgWcKCTaw5S6Jqwyd1gLSVdsJ3+8MVm8e4yLronThiRJI9DazFAAs9xfB9nWeMQ2DWKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "msgpackr": "^1.11.2", + "node-addon-api": "^6.1.0", + "node-gyp-build-optional-packages": "5.2.2", + "ordered-binary": "^1.5.3", + "weak-lru-cache": "^1.2.2" + }, + "bin": { + "download-lmdb-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@lmdb/lmdb-darwin-arm64": "3.4.1", + "@lmdb/lmdb-darwin-x64": "3.4.1", + "@lmdb/lmdb-linux-arm": "3.4.1", + "@lmdb/lmdb-linux-arm64": "3.4.1", + "@lmdb/lmdb-linux-x64": "3.4.1", + "@lmdb/lmdb-win32-arm64": "3.4.1", + "@lmdb/lmdb-win32-x64": "3.4.1" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", + "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "is-unicode-supported": "^1.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", + "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-escapes": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", + "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/is-fullwidth-code-point": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", + "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz", + "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/log4js": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.9.1.tgz", + "integrity": "sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "date-format": "^4.0.14", + "debug": "^4.3.4", + "flatted": "^3.2.7", + "rfdc": "^1.3.0", + "streamroller": "^3.1.5" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-fetch-happen": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-14.0.3.tgz", + "integrity": "sha512-QMjGbFTP0blj97EeidG5hk/QhKQ3T4ICckQGLgz38QF7Vgbk6e6FTARN8KhKxyBbWn8R0HU+bnw8aSoFPD4qtQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/agent": "^3.0.0", + "cacache": "^19.0.1", + "http-cache-semantics": "^4.1.1", + "minipass": "^7.0.2", + "minipass-fetch": "^4.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^1.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "ssri": "^12.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minipass-collect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz", + "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minipass-fetch": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-4.0.1.tgz", + "integrity": "sha512-j7U11C5HXigVuutxebFadoYBbd7VSdZWggSe64NVdvWNBqGAiXPL2QVCehjmw7lY1oF9gOllYbORh+hiNgfPgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^3.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-flush/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/minizlib": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", + "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/msgpackr": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.5.tgz", + "integrity": "sha512-UjkUHN0yqp9RWKy0Lplhh+wlpdt9oQBYgULZOiFhV3VclSF1JnSQWZ5r9gORQlNYaUKQoR8itv7g7z1xDDuACA==", + "dev": true, + "license": "MIT", + "optional": true, + "optionalDependencies": { + "msgpackr-extract": "^3.0.2" + } + }, + "node_modules/msgpackr-extract": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.3.tgz", + "integrity": "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-gyp-build-optional-packages": "5.2.2" + }, + "bin": { + "download-msgpackr-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" + } + }, + "node_modules/mute-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", + "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-addon-api": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", + "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/node-gyp": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-11.2.0.tgz", + "integrity": "sha512-T0S1zqskVUSxcsSTkAsLc7xCycrRYmtDHadDinzocrThjyQCn5kMlEBSj6H4qDbgsIOSLmmlRIeb0lZXj+UArA==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^14.0.3", + "nopt": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "tar": "^7.4.3", + "tinyglobby": "^0.2.12", + "which": "^5.0.0" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/node-gyp-build-optional-packages": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz", + "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.1" + }, + "bin": { + "node-gyp-build-optional-packages": "bin.js", + "node-gyp-build-optional-packages-optional": "optional.js", + "node-gyp-build-optional-packages-test": "build-test.js" + } + }, + "node_modules/node-gyp/node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/node-gyp/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/node-gyp/node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/node-gyp/node_modules/tar": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", + "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.0.1", + "mkdirp": "^3.0.1", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/node-gyp/node_modules/which": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", + "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/node-gyp/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" + }, + "node_modules/nopt": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-8.1.0.tgz", + "integrity": "sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "^3.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-bundled": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-4.0.0.tgz", + "integrity": "sha512-IxaQZDMsqfQ2Lz37VvyyEtKLe8FsRZuysmedy/N06TU1RyVppYKXrO4xIhR0F+7ubIBox6Q7nir6fQI3ej39iA==", + "dev": true, + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^4.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm-install-checks": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-7.1.1.tgz", + "integrity": "sha512-u6DCwbow5ynAX5BdiHQ9qvexme4U3qHW3MWe5NqH+NeBm0LbiH6zvGjNNew1fY+AZZUtVHbOPF3j7mJxbUzpXg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-4.0.0.tgz", + "integrity": "sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm-package-arg": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-12.0.2.tgz", + "integrity": "sha512-f1NpFjNI9O4VbKMOlA5QoBq/vSQPORHcTZ2feJpFkTHJ9eQkdlmZEKSjcAhxTGInC7RlEyScT9ui67NaOsjFWA==", + "dev": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^6.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm-packlist": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-10.0.0.tgz", + "integrity": "sha512-rht9U6nS8WOBDc53eipZNPo5qkAV4X2rhKE2Oj1DYUQ3DieXfj0mKkVmjnf3iuNdtMd8WfLdi2L6ASkD/8a+Kg==", + "dev": true, + "license": "ISC", + "dependencies": { + "ignore-walk": "^7.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-pick-manifest": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-10.0.0.tgz", + "integrity": "sha512-r4fFa4FqYY8xaM7fHecQ9Z2nE9hgNfJR+EmoKv0+chvzWkBcORX3r0FpTByP+CbOVJDladMXnPQGVN8PBLGuTQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "npm-install-checks": "^7.1.0", + "npm-normalize-package-bin": "^4.0.0", + "npm-package-arg": "^12.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm-registry-fetch": { + "version": "18.0.2", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-18.0.2.tgz", + "integrity": "sha512-LeVMZBBVy+oQb5R6FDV9OlJCcWDU+al10oKpe+nsvcHnG24Z3uM3SvJYKfGJlfGjVU8v9liejCrUR/M5HO5NEQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/redact": "^3.0.0", + "jsonparse": "^1.3.1", + "make-fetch-happen": "^14.0.0", + "minipass": "^7.0.2", + "minipass-fetch": "^4.0.0", + "minizlib": "^3.0.1", + "npm-package-arg": "^12.0.0", + "proc-log": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", + "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "cli-cursor": "^5.0.0", + "cli-spinners": "^2.9.2", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^2.0.0", + "log-symbols": "^6.0.0", + "stdin-discarder": "^0.2.2", + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ordered-binary": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.6.0.tgz", + "integrity": "sha512-IQh2aMfMIDbPjI/8a3Edr+PiOpcsB7yo8NdW7aHWVaoR/pcDldunMvnnwbk/auPGqmKeAdxtZl7MHX/QmPwhvQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-map": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.3.tgz", + "integrity": "sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/pacote": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-21.0.0.tgz", + "integrity": "sha512-lcqexq73AMv6QNLo7SOpz0JJoaGdS3rBFgF122NZVl1bApo2mfu+XzUBU/X/XsiJu+iUmKpekRayqQYAs+PhkA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^6.0.0", + "@npmcli/installed-package-contents": "^3.0.0", + "@npmcli/package-json": "^6.0.0", + "@npmcli/promise-spawn": "^8.0.0", + "@npmcli/run-script": "^9.0.0", + "cacache": "^19.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^12.0.0", + "npm-packlist": "^10.0.0", + "npm-pick-manifest": "^10.0.0", + "npm-registry-fetch": "^18.0.0", + "proc-log": "^5.0.0", + "promise-retry": "^2.0.1", + "sigstore": "^3.0.0", + "ssri": "^12.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "bin/index.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-html-rewriting-stream": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-7.1.0.tgz", + "integrity": "sha512-2ifK6Jb+ONoqOy5f+cYHsqvx1obHQdvIk13Jmt/5ezxP0U9p+fqd+R6O73KblGswyuzBYfetmsfK9ThMgnuPPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0", + "parse5": "^7.0.0", + "parse5-sax-parser": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-html-rewriting-stream/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/parse5-sax-parser": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-7.0.0.tgz", + "integrity": "sha512-5A+v2SNsq8T6/mG3ahcz8ZtQ0OUFTatxPbeidoMB7tkJSGDY3tdfl4MHovtLQHkEn5CGxijNWRQHhRQ6IRpXKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/path-to-regexp": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", + "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/piscina": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-5.1.2.tgz", + "integrity": "sha512-9cE/BTA/xhDiyNUEj6EKWLEQC17fh/24ydYzQwcA7QdYh75K6kzL2GHvxDF5i9rFGtUaaKk7/u4xp07qiKXccQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.x" + }, + "optionalDependencies": { + "@napi-rs/nice": "^1.0.1" + } + }, + "node_modules/pkce-challenge": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.0.tgz", + "integrity": "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-media-query-parser": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", + "integrity": "sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==", + "dev": true, + "license": "MIT" + }, + "node_modules/primeicons": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/primeicons/-/primeicons-7.0.0.tgz", + "integrity": "sha512-jK3Et9UzwzTsd6tzl2RmwrVY/b8raJ3QZLzoDACj+oTJ0oX7L9Hy+XnVwgo4QVKlKpnP/Ur13SXV/pVh4LzaDw==", + "license": "MIT" + }, + "node_modules/primeng": { + "version": "20.0.0", + "resolved": "https://registry.npmjs.org/primeng/-/primeng-20.0.0.tgz", + "integrity": "sha512-F0r7tWqIGKInKSPxyVTXkj+XP/4mz78cWECHuu045JV0O1n7YMgi/2AuEAAqhgKOReLLkRBHB0pafEtH1tnYCQ==", + "license": "SEE LICENSE IN LICENSE.md", + "dependencies": { + "@primeuix/styled": "^0.7.0", + "@primeuix/styles": "^1.2.1", + "@primeuix/utils": "^0.6.0", + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/animations": "^20.0.4", + "@angular/cdk": "^20.0.3", + "@angular/common": "^20.0.4", + "@angular/core": "^20.0.4", + "@angular/forms": "^20.0.4", + "@angular/platform-browser": "^20.0.4", + "@angular/router": "^20.0.4", + "rxjs": "^6.0.0 || ^7.8.1" + } + }, + "node_modules/proc-log": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz", + "integrity": "sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/qjobs": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", + "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.9" + } + }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", + "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.6.3", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "4.44.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.1.tgz", + "integrity": "sha512-x8H8aPvD+xbl0Do8oez5f5o8eMS3trfCghc4HhLAnCkj7Vl0d1JWGs0UF/D886zLW2rOj2QymV/JcSSsw+XDNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.44.1", + "@rollup/rollup-android-arm64": "4.44.1", + "@rollup/rollup-darwin-arm64": "4.44.1", + "@rollup/rollup-darwin-x64": "4.44.1", + "@rollup/rollup-freebsd-arm64": "4.44.1", + "@rollup/rollup-freebsd-x64": "4.44.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.44.1", + "@rollup/rollup-linux-arm-musleabihf": "4.44.1", + "@rollup/rollup-linux-arm64-gnu": "4.44.1", + "@rollup/rollup-linux-arm64-musl": "4.44.1", + "@rollup/rollup-linux-loongarch64-gnu": "4.44.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.44.1", + "@rollup/rollup-linux-riscv64-gnu": "4.44.1", + "@rollup/rollup-linux-riscv64-musl": "4.44.1", + "@rollup/rollup-linux-s390x-gnu": "4.44.1", + "@rollup/rollup-linux-x64-gnu": "4.44.1", + "@rollup/rollup-linux-x64-musl": "4.44.1", + "@rollup/rollup-win32-arm64-msvc": "4.44.1", + "@rollup/rollup-win32-ia32-msvc": "4.44.1", + "@rollup/rollup-win32-x64-msvc": "4.44.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "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-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/sass": { + "version": "1.89.2", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.89.2.tgz", + "integrity": "sha512-xCmtksBKd/jdJ9Bt9p7nPKiuqrlBMBuuGkQlkhZjjQk3Ty48lv93k5Dq6OPkKt4XwxDJ7tvlfrTa1MPA9bf+QA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.0", + "immutable": "^5.0.2", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true, + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sigstore": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-3.1.0.tgz", + "integrity": "sha512-ZpzWAFHIFqyFE56dXqgX/DkDRZdz+rRcjoIk/RQU4IX0wiCv1l8S7ZrXDHcCc+uaf+6o7w3h2l3g6GYG5TKN9Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^3.1.0", + "@sigstore/core": "^2.0.0", + "@sigstore/protobuf-specs": "^0.4.0", + "@sigstore/sign": "^3.1.0", + "@sigstore/tuf": "^3.1.0", + "@sigstore/verify": "^2.1.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socket.io": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", + "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.6.0", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", + "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "~4.3.4", + "ws": "~8.17.1" + } + }, + "node_modules/socket.io-adapter/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/socket.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/socket.io/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/socket.io/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/socks": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.6.tgz", + "integrity": "sha512-pe4Y2yzru68lXCb38aAqRf5gvN8YdjP1lok5o0J7BOHljkyCGKVz7H3vpVIXKD27rj2giOJ7DwVyk/GWrPHDWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "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.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.21", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", + "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/ssri": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-12.0.0.tgz", + "integrity": "sha512-S7iGNosepx9RadX82oimUkvr0Ct7IjJbEbs4mJcTxst8um95J3sDYU1RBEOvdu6oL1Wek2ODI5i4MAw+dZ6cAQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stdin-discarder": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", + "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/streamroller": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz", + "integrity": "sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "date-format": "^4.0.14", + "debug": "^4.3.4", + "fs-extra": "^8.1.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwindcss": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.11.tgz", + "integrity": "sha512-2E9TBm6MDD/xKYe+dvJZAmg3yxIEDNRc0jwlNyDg/4Fil2QcSLjFKGVff0lAf1jjeaArlG/M75Ey/EYr/OJtBA==", + "license": "MIT" + }, + "node_modules/tailwindcss-primeui": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/tailwindcss-primeui/-/tailwindcss-primeui-0.6.1.tgz", + "integrity": "sha512-T69Rylcrmnt8zy9ik+qZvsLuRIrS9/k6rYJSIgZ1trnbEzGDDQSCIdmfyZknevqiHwpSJHSmQ9XT2C+S/hJY4A==", + "license": "MIT", + "peerDependencies": { + "tailwindcss": ">=3.1.0" + } + }, + "node_modules/tapable": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", + "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "dev": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/tar/node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/tar/node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/tinyglobby": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/tuf-js": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-3.1.0.tgz", + "integrity": "sha512-3T3T04WzowbwV2FDiGXBbr81t64g1MUGGJRgT4x5o97N+8ArdhVCAF9IxFrxuSJmM3E5Asn7nKHkao0ibcZXAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tufjs/models": "3.0.1", + "debug": "^4.4.1", + "make-fetch-happen": "^14.0.3" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ua-parser-js": { + "version": "0.7.40", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.40.tgz", + "integrity": "sha512-us1E3K+3jJppDBa3Tl0L3MOJiGhe1C6P0+nIvQAFYbxlMAx0h81eOwLmU57xgqToduDDPx3y5QsdjPfDu+FgOQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + }, + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" + } + ], + "license": "MIT", + "bin": { + "ua-parser-js": "script/cli.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/undici-types": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", + "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", + "dev": true, + "license": "MIT" + }, + "node_modules/unique-filename": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-4.0.0.tgz", + "integrity": "sha512-XSnEewXmQ+veP7xX2dS5Q4yZAvO40cBN2MWkJ7D/6sW4Dg6wYBNwM1Vrnz1FhH5AdeLIlUXRI9e28z1YZi71NQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^5.0.0" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/unique-slug": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-5.0.0.tgz", + "integrity": "sha512-9OdaqO5kwqR+1kVgHAhsp5vPNU0hnxRa26rBFNfNgM7M6pNtgzeBn3s/xbyCQL3dcjzOatcef6UUHpB/6MaETg==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/uri-js/node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/validate-npm-package-name": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-6.0.1.tgz", + "integrity": "sha512-OaI//3H0J7ZkR1OqlhGA8cA+Cbk/2xFOQpJOt5+s27/ta9eZwpeervh4Mxh4w0im/kdgktowaqVNR7QOrUd7Yg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vite": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.0.tgz", + "integrity": "sha512-ixXJB1YRgDIw2OszKQS9WxGHKwLdCsbQNkpJN171udl6szi/rIySHL6/Os3s2+oE4P/FLD4dxg4mD7Wust+u5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.6", + "picomatch": "^4.0.2", + "postcss": "^8.5.6", + "rollup": "^4.40.0", + "tinyglobby": "^0.2.14" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", + "integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/weak-lru-cache": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz", + "integrity": "sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "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/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "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/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "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/wrap-ansi/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz", + "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^9.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "string-width": "^7.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^22.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/yargs-parser": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", + "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz", + "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "3.25.75", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.75.tgz", + "integrity": "sha512-OhpzAmVzabPOL6C3A3gpAifqr9MqihV/Msx3gor2b2kviCgcb+HM9SEOpMWwwNp9MRunWnhtAKUoo0AHhjyPPg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.24.6", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.6.tgz", + "integrity": "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==", + "dev": true, + "license": "ISC", + "peerDependencies": { + "zod": "^3.24.1" + } + } + } +} diff --git a/UI/package.json b/UI/package.json new file mode 100644 index 0000000..7d027e1 --- /dev/null +++ b/UI/package.json @@ -0,0 +1,53 @@ +{ + "name": "family-tree", + "version": "0.0.0", + "scripts": { + "ng": "ng", + "start": "ng serve", + "build": "ng build", + "watch": "ng build --watch --configuration development", + "test": "ng test" + }, + "prettier": { + "overrides": [ + { + "files": "*.html", + "options": { + "parser": "angular" + } + } + ] + }, + "private": true, + "dependencies": { + "@angular/common": "^20.1.0", + "@angular/compiler": "^20.1.0", + "@angular/core": "^20.1.0", + "@angular/forms": "^20.1.0", + "@angular/platform-browser": "^20.1.0", + "@angular/router": "^20.1.0", + "@primeuix/themes": "^1.2.1", + "@tailwindcss/postcss": "^4.1.11", + "moment": "^2.30.1", + "postcss": "^8.5.6", + "primeicons": "^7.0.0", + "primeng": "^20.0.0", + "rxjs": "~7.8.0", + "tailwindcss": "^4.1.11", + "tailwindcss-primeui": "^0.6.1", + "tslib": "^2.3.0" + }, + "devDependencies": { + "@angular/build": "^20.1.1", + "@angular/cli": "^20.1.1", + "@angular/compiler-cli": "^20.1.0", + "@types/jasmine": "~5.1.0", + "jasmine-core": "~5.8.0", + "karma": "~6.4.0", + "karma-chrome-launcher": "~3.2.0", + "karma-coverage": "~2.2.0", + "karma-jasmine": "~5.1.0", + "karma-jasmine-html-reporter": "~2.1.0", + "typescript": "~5.8.2" + } +} diff --git a/UI/public/config/appsetting.json b/UI/public/config/appsetting.json new file mode 100644 index 0000000..fa92ef9 --- /dev/null +++ b/UI/public/config/appsetting.json @@ -0,0 +1,4 @@ +{ + "baseUrl": "http://localhost:5015", + "attachment": "http://localhost/document/family" +} \ No newline at end of file diff --git a/UI/public/favicon.ico b/UI/public/favicon.ico new file mode 100644 index 0000000..57614f9 Binary files /dev/null and b/UI/public/favicon.ico differ diff --git a/UI/public/images/application_image.png b/UI/public/images/application_image.png new file mode 100644 index 0000000..3942b83 Binary files /dev/null and b/UI/public/images/application_image.png differ diff --git a/UI/public/images/application_image_login.png b/UI/public/images/application_image_login.png new file mode 100644 index 0000000..d22df2c Binary files /dev/null and b/UI/public/images/application_image_login.png differ diff --git a/UI/public/web.config b/UI/public/web.config new file mode 100644 index 0000000..ce4c4cb --- /dev/null +++ b/UI/public/web.config @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/UI/src/app/app.config.ts b/UI/src/app/app.config.ts new file mode 100644 index 0000000..091094c --- /dev/null +++ b/UI/src/app/app.config.ts @@ -0,0 +1,32 @@ +import { ApplicationConfig, provideAppInitializer, provideBrowserGlobalErrorListeners, provideZonelessChangeDetection } from '@angular/core'; +import { provideRouter } from '@angular/router'; +import { JwtInterceptor, ErrorInterceptor, initializeApp } from './shares'; +import { provideAnimationsAsync } from '@angular/platform-browser/animations/async'; +import { providePrimeNG } from 'primeng/config'; +import { provideHttpClient, withInterceptors } from '@angular/common/http'; +import { ConfirmationService, MessageService } from 'primeng/api'; +import { routes } from './app.routes'; +import MyPreset from './mythem'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideBrowserGlobalErrorListeners(), + MessageService, ConfirmationService, + provideAppInitializer(initializeApp()), + provideHttpClient(withInterceptors([JwtInterceptor, ErrorInterceptor])), + provideZonelessChangeDetection(), + provideAnimationsAsync(), + providePrimeNG({ + theme: { + preset: MyPreset, + options: { + cssLayer: { + name: 'primeng', + order: 'theme, base, primeng' + } + } + } + }), + provideRouter(routes) + ] +}; diff --git a/UI/src/app/app.css b/UI/src/app/app.css new file mode 100644 index 0000000..e69de29 diff --git a/UI/src/app/app.html b/UI/src/app/app.html new file mode 100644 index 0000000..3f9de4f --- /dev/null +++ b/UI/src/app/app.html @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/UI/src/app/app.routes.ts b/UI/src/app/app.routes.ts new file mode 100644 index 0000000..f7a446b --- /dev/null +++ b/UI/src/app/app.routes.ts @@ -0,0 +1,15 @@ +import { Routes } from '@angular/router'; +import { Login } from './login'; +import { StaffComponent, StaffEditComponent } from './staff'; +import { AuthGuard } from './route-guard'; +import { FamilyTree, FamilyList} from './person'; +export const routes: Routes = [ +{ path: '', redirectTo: 'login', pathMatch: 'full' }, + // { path: 'approval', component: ApprovalComponent,canActivate: [AuthGuard], data: { roleAllowed: '1,2,3' }},}, + { path: 'login', component: Login}, + { path: 'staff', component: StaffComponent}, + { path: 'person', component: FamilyList}, + { path: 'familytree', component: FamilyTree}, + { path: 'staff/:id', component: StaffEditComponent}, + +]; diff --git a/UI/src/app/app.ts b/UI/src/app/app.ts new file mode 100644 index 0000000..896df7c --- /dev/null +++ b/UI/src/app/app.ts @@ -0,0 +1,14 @@ +import { Component, signal } from '@angular/core'; +import { RouterOutlet } from '@angular/router'; +import { ToolbarComponent } from './toolbar/toolbar.component'; +import { ToastModule } from 'primeng/toast'; +import { ConfirmDialogModule } from 'primeng/confirmdialog'; +@Component({ + selector: 'app-root', + imports: [RouterOutlet,ToastModule,ToolbarComponent,ConfirmDialogModule], + templateUrl: './app.html', + styleUrl: './app.css' +}) +export class App { + protected readonly title = signal('FamilyTree'); +} diff --git a/UI/src/app/login/index.ts b/UI/src/app/login/index.ts new file mode 100644 index 0000000..262fea5 --- /dev/null +++ b/UI/src/app/login/index.ts @@ -0,0 +1,2 @@ +export * from './login'; + diff --git a/UI/src/app/login/login.css b/UI/src/app/login/login.css new file mode 100644 index 0000000..52495cb --- /dev/null +++ b/UI/src/app/login/login.css @@ -0,0 +1,10 @@ +.btnloginRigh { + display: flex; + flex-direction:reverse; + justify-content:flex-end; + width: 100%; +} +.myerror { + background-color: red; + color: white; +} \ No newline at end of file diff --git a/UI/src/app/login/login.html b/UI/src/app/login/login.html new file mode 100644 index 0000000..e03eeb4 --- /dev/null +++ b/UI/src/app/login/login.html @@ -0,0 +1,45 @@ +
+
+ +
+ Logo +
+
+
+ + + +
+
+ + +
+ +
+ +
+
+ +
+
+ + +
+
{{error}}
+
+ + +
+ +
\ No newline at end of file diff --git a/UI/src/app/login/login.ts b/UI/src/app/login/login.ts new file mode 100644 index 0000000..5b0d6e2 --- /dev/null +++ b/UI/src/app/login/login.ts @@ -0,0 +1,135 @@ +import { Component, OnDestroy, OnInit, inject } from '@angular/core'; +import { Router, ActivatedRoute, RouterModule } from '@angular/router'; +import { FormBuilder, Validators, FormGroup } from '@angular/forms'; +import { first } from 'rxjs/operators'; + +import { AuthenticationService } from '../user-services'; + +import { ConfirmationService } from 'primeng/api'; +import { Subject, Subscription } from 'rxjs'; +import { CommonModule } from '@angular/common'; +import { CardModule } from 'primeng/card'; +import { ReactiveFormsModule, FormsModule } from '@angular/forms'; +import { ButtonModule } from 'primeng/button'; +import { InputTextModule } from 'primeng/inputtext'; + +import { Utils } from '../shares'; +@Component({ + templateUrl: './login.html', + styleUrl: './login.css', + selector: 'login', + imports: [FormsModule, ReactiveFormsModule, ButtonModule, InputTextModule, + RouterModule, CommonModule, CardModule], + +}) +export class Login implements OnInit, OnDestroy { + loginForm: FormGroup; + confirmationService = inject(ConfirmationService); + route = inject(ActivatedRoute); + router = inject(Router); + authenticationService = inject(AuthenticationService); + formBuilder = inject(FormBuilder); + loading = false; + homeUrl = "/person"; + submitted = false; + isChange = true; // disable use false//true for not disable. make sure it true is disable button. + subChanged$ = new Subject(); + // get return url from route parameters or default to '/' + returnUrl = this.route.snapshot.queryParams['returnUrl'] || this.homeUrl; + error = ''; + private subscription: Subscription = new Subscription(); + + constructor() { + + this.loginForm = this.formBuilder.group({ + username: ['kham.vilaythong@gmail.com', Validators.required], + password: ['password', Validators.required], + + }); + } + + ngOnInit() { + // reset login status + this.loading = false; + this.subscription.add(this.loginForm.valueChanges.subscribe((x: any) => this.isChange = false)); + this.subscription.add(this.subChanged$.subscribe(x => this.isChange = x)); + + } + + updateData($event: Event) { + console.log("onChange click", $event); + } + + // convenience getter for easy access to form fields + get f() { return this.loginForm.value; } + get isFieldsChange() { + const chan = this.isChange || !this.loginForm.valid; // this disable so need true valid = true not + //console.log(this.msg + 'is fields change', chan); + return chan; + } + onSubmit() { + + //this.playAudio(); + + // stop here if form is invalid + if (this.loginForm.invalid) { + return; + } + const fvalue = this.loginForm.value; + this.loading = true; + this.error = ""; + let username = ""; + let password = ""; + if (fvalue.username != null) + username = fvalue.username; + if (fvalue.password != null) + password = fvalue.password; + this.authenticationService.login(username, password) + .pipe(first()) + .subscribe({ + next: (x: any) => { + this.loading = false; + console.log("login result ", x.data, this.returnUrl); + if (x.statusCode == 1) { + // if (x.data.role == 1) { + // this.homeService.reportDate = Utils.getLastMonth(); + // this.router.navigate(['/addkpi/' + x.data.id + '/' + x.data.measureId], { queryParams: { returnUrl: '/login' } }); + + // } + // else if (x.data.role == 3) { + // this.homeService.reportDate = Utils.getLastMonth(); + // this.router.navigate(['/approval/' + x.data.id], { queryParams: { returnUrl: '/login' } }); + + //} + // else + this.router.navigate([this.homeUrl]); + } + else { + this.loginForm.patchValue({ password: '' }); + // alert("Invalid Username or Password. Please try again."); + console.error("error in login", x); + this.confirmationService.confirm({ + message: 'Error in Login: Invalid username or password. May be your password is expired', + header: 'Error Login', + icon: 'pi pi-info-circle', + rejectVisible: false, + acceptLabel: 'OK', + + }); + } + }, + error: er => { + console.log("error in login", er); + this.error = er.message; + this.loading = false; + } + }); + + } + + ngOnDestroy(): void { + this.subscription.unsubscribe(); + this.loading = false; + } + +} diff --git a/UI/src/app/models/code.ts b/UI/src/app/models/code.ts new file mode 100644 index 0000000..48e8e84 --- /dev/null +++ b/UI/src/app/models/code.ts @@ -0,0 +1,6 @@ +export interface Code { + id:number; + name:string; + status?:string; + active?:boolean; +} diff --git a/UI/src/app/models/configureUrl.ts b/UI/src/app/models/configureUrl.ts new file mode 100644 index 0000000..198ae08 --- /dev/null +++ b/UI/src/app/models/configureUrl.ts @@ -0,0 +1,16 @@ +export enum ConfigureUrl +{ + //baseUrl = "http://localhost:61744", + loginApiUrl = "api/Users/Login", + searchStaffUrl = "api/Users/SearchADStaff", + staffUrl = "api/Staff", + personUrl = "api/Person", + staffWorkUrl = "api/StaffWork", + jobUrl ="api/Job", + clientUrl ="api/Client", + relationShipUrl ="api/RelationShip", + logoutUrl ="api/Users/Logout", + adminUserUrl = "api/Staff", + lookupUrl = "api/Lookup", + +} \ No newline at end of file diff --git a/UI/src/app/models/enum.ts b/UI/src/app/models/enum.ts new file mode 100644 index 0000000..e41d06c --- /dev/null +++ b/UI/src/app/models/enum.ts @@ -0,0 +1,37 @@ +export const MIMEType = { + png: 'image/png', + jpg: 'image/jpg', + jpeg: 'image/jpeg', + gif: 'image/gif', + txt: 'text/plain', + pdf: 'application/pdf' +}; + + +//for status of object 0 no change, 1 changed , -1 is deleted. +export enum mState { + Delete = -1, + NoChange = 0, + Modified = 1, + New =2, +}; + +export enum userRole { + Normal = 1, + Admin = 2, + ServiceManager =3, + Accounting = 4, + WorkShop = 5, + +}; + +export enum enumReqStatus { + Canel = -1, + Decline = -2, + NewRequest = 0, + Allocate = 1, + Arrival = 2, + Completed = 3, + OnHold = 4, + OffHold = 5 +}; diff --git a/UI/src/app/models/index.ts b/UI/src/app/models/index.ts new file mode 100644 index 0000000..68ef389 --- /dev/null +++ b/UI/src/app/models/index.ts @@ -0,0 +1,10 @@ +export * from './code'; +export * from './configureUrl'; +export * from './user'; +export * from './resultmodel'; +export * from './enum'; +export * from './lookup'; +export * from './staff'; +export * from './person'; +export * from './job'; +export * from './relationship'; \ No newline at end of file diff --git a/UI/src/app/models/job.ts b/UI/src/app/models/job.ts new file mode 100644 index 0000000..8c7ab87 --- /dev/null +++ b/UI/src/app/models/job.ts @@ -0,0 +1,12 @@ + +export interface JobSearch { + code:string; + description:string; +} +export interface Job { + id:number; + code:string|null|undefined; + description:string |null|undefined; + active:boolean |null|undefined; + +} diff --git a/UI/src/app/models/lookup.ts b/UI/src/app/models/lookup.ts new file mode 100644 index 0000000..88a1d28 --- /dev/null +++ b/UI/src/app/models/lookup.ts @@ -0,0 +1,16 @@ +// using for priority and infectionType +export interface Lookup { + id:number; + codeId:string; + description:string; + +} + +export interface LookupEdit { + id:number; + codeId:string; + description:string; + active:boolean; + type:string; + +} \ No newline at end of file diff --git a/UI/src/app/models/mydetail.ts b/UI/src/app/models/mydetail.ts new file mode 100644 index 0000000..2b5dcbc --- /dev/null +++ b/UI/src/app/models/mydetail.ts @@ -0,0 +1,7 @@ +export interface MyDetail{ + id:number, + login:string, + firstname:string, + surname:string, + jobTitle:string, +} \ No newline at end of file diff --git a/UI/src/app/models/person.ts b/UI/src/app/models/person.ts new file mode 100644 index 0000000..67163c2 --- /dev/null +++ b/UI/src/app/models/person.ts @@ -0,0 +1,35 @@ +import { RelationShip } from "./relationship"; + +export interface FamilySearch { + email:string|null; + phone:string |null; + clientname:string |null; +} + +export interface Person { + + id: number; + title?:string| null| undefined; + firstName: string | null|undefined; + lastName: string | null|undefined; + email: string | null|undefined; + phone: string | null|undefined; + address?: string | null|undefined; + alive: boolean | null|undefined; + dob? : string | null|undefined; + fatherId?: number| null|undefined; + motherId?: number| null|undefined; + image?: string|null|undefined; + sex?: string|null|undefined; + fatherName?:string |null; + motherName?:string |null; + relationShips?: RelationShip[]; +} + +export interface PersonContainer +{ + person: Person; + formData?:FormData; + +} + diff --git a/UI/src/app/models/relationship.ts b/UI/src/app/models/relationship.ts new file mode 100644 index 0000000..f3a9c7b --- /dev/null +++ b/UI/src/app/models/relationship.ts @@ -0,0 +1,27 @@ +import { mState } from "./enum"; + +export interface RelationShip { + id: number; + relatePersonId: number; + personId: number; + state: mState; +} +//relationship id this relatePersonId person has relation with other person +// also other person has also has relation with you. look in two way +// person id = 1 has to relation id = 90 +// that means when you edit person 90 their relation ship also be there. +// so personid = 1 and relation id = 90 we need to show other way around. +//example first name = Smith has partner Jennifer +// when we edit Jennifer it should show Smith as her partner too. + +export interface RelationShipView { + id: number; + relatePersonId:number; + personId : number; + pfirstName: string |null |undefined; + plastName: string |null |undefined; + sex:string |null |undefined; + state: mState; + + +} \ No newline at end of file diff --git a/UI/src/app/models/resultmodel.ts b/UI/src/app/models/resultmodel.ts new file mode 100644 index 0000000..1ea43e3 --- /dev/null +++ b/UI/src/app/models/resultmodel.ts @@ -0,0 +1,5 @@ +export interface ResultModel { + data: T; + message: string; + statusCode:number; +} \ No newline at end of file diff --git a/UI/src/app/models/staff.ts b/UI/src/app/models/staff.ts new file mode 100644 index 0000000..bb8d8cd --- /dev/null +++ b/UI/src/app/models/staff.ts @@ -0,0 +1,39 @@ +export interface StaffView { + id:number, + email:string, + firstname:string, + lastname:string, + active:boolean, +} + +export interface ResetPassword{ + id:number; + password:string; +} + +export interface Staff { + id:number, + email:string, + firstname:string, + lastname:string, + phone:string, + type:number; + active:boolean; + roleType: number; + password?:string, +} + +export interface StaffSearch { + email:string; + firstName:string; + lastName:string; +} + +//this use for new user only. +export interface AdminUserNew { + loginId:number; + user: Staff;// first get userid from this table and + + +} + diff --git a/UI/src/app/models/user.ts b/UI/src/app/models/user.ts new file mode 100644 index 0000000..3242e25 --- /dev/null +++ b/UI/src/app/models/user.ts @@ -0,0 +1,36 @@ +export class User { + id: number = 0; + username: string = ''; + role: number = -1; + firstName: string = ''; + lastName: string = ''; + email:string = ''; + token:string =''; + position: string =''; + department: string = ''; + managerEmail:string =''; + phone:string=''; + +} + +export interface UserAD +{ + id: number, + firstName: string, + lastName: string, + email: string, + username: string, + department: string, + position: string, + role: number, + phone:string, + managerEmail:string + } + +export class SecAccessLevel { + accessName:string=''; + request:number=0; + report:number=0; + admin:number=0; + allocation:number=0; +} diff --git a/UI/src/app/mythem.ts b/UI/src/app/mythem.ts new file mode 100644 index 0000000..3728d2a --- /dev/null +++ b/UI/src/app/mythem.ts @@ -0,0 +1,59 @@ + + +//mypreset.ts +import { definePreset } from '@primeuix/themes'; +import Aura from '@primeuix/themes/aura'; + +const MyPreset = definePreset(Aura, { + semantic: { + colorScheme: { + primary: { + 50: '{zinc.50}', + 100: '{zinc.100}', + 200: '{zinc.200}', + 300: '{zinc.300}', + 400: '{zinc.400}', + 500: '{zinc.500}', + 600: '{zinc.600}', + 700: '{zinc.700}', + 800: '{zinc.800}', + 900: '{zinc.900}', + 950: '{zinc.950}' + }, + light: { + surface: { + 0: '#ffffff', + 50: '{zinc.50}', + 100: '{zinc.100}', + 200: '{zinc.200}', + 300: '{zinc.300}', + 400: '{zinc.400}', + 500: '{zinc.500}', + 600: '{zinc.600}', + 700: '{zinc.700}', + 800: '{zinc.800}', + 900: '{zinc.900}', + 950: '{zinc.950}' + } + }, + dark: { + surface: { + 0: '#ffffff', + 50: '{slate.50}', + 100: '{slate.100}', + 200: '{slate.200}', + 300: '{slate.300}', + 400: '{slate.400}', + 500: '{slate.500}', + 600: '{slate.600}', + 700: '{slate.700}', + 800: '{slate.800}', + 900: '{slate.900}', + 950: '{slate.950}' + } + } + } + } +}); + +export default MyPreset; diff --git a/UI/src/app/person/family.orga.css b/UI/src/app/person/family.orga.css new file mode 100644 index 0000000..3db3368 --- /dev/null +++ b/UI/src/app/person/family.orga.css @@ -0,0 +1,12 @@ +table { + width: 100%; +} + +.mat-form-field { + font-size: 14px; + width: 100%; +} +td.mat-column-edit, .mat-column-delete { + width: 35px; + padding-right: 2px; +} diff --git a/UI/src/app/person/family.orga.html b/UI/src/app/person/family.orga.html new file mode 100644 index 0000000..4f4c598 --- /dev/null +++ b/UI/src/app/person/family.orga.html @@ -0,0 +1,20 @@ +
+

Family Tree

+
+
+ + +
+ +
{{ node.label }}
+
{{ node.data }}
+
+
+
+
+
+ +
+
+ diff --git a/UI/src/app/person/family.orga.ts b/UI/src/app/person/family.orga.ts new file mode 100644 index 0000000..6389976 --- /dev/null +++ b/UI/src/app/person/family.orga.ts @@ -0,0 +1,234 @@ +import { Component, OnInit, OnDestroy, inject, ChangeDetectorRef, signal} from '@angular/core'; + +import { StaffView ,StaffSearch, Person, RelationShip } from '../models'; +import { OrganizationChartModule } from 'primeng/organizationchart'; + +import { take } from 'rxjs/operators'; +import { Subscription } from 'rxjs'; +import { Router } from '@angular/router'; +import { PersonService } from './person.service'; +import { AuthenticationService } from '../user-services'; +import { TableModule } from 'primeng/table'; +import { FormsModule } from '@angular/forms'; +import { CommonModule } from '@angular/common'; +import { ButtonModule } from 'primeng/button'; +import { TreeNode } from 'primeng/api'; +import { InputTextModule } from 'primeng/inputtext'; +import { DialogService, DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog'; +import { PersonEdit } from './person.edit'; +import { MessageService } from 'primeng/api'; +import { TreeModule, TreeNodeDoubleClickEvent, TreeNodeSelectEvent } from 'primeng/tree'; +import { Utils } from '../shares'; + +@Component({ + selector: 'family-orga', + templateUrl: './family.orga.html', + imports:[TableModule,FormsModule,TreeModule, OrganizationChartModule,CommonModule,ButtonModule,InputTextModule], + styleUrls: ['./family.orga.css'], + providers: [DialogService] +}) +export class FamilyOrga implements OnInit, OnDestroy{ + + private subscription:Subscription = new Subscription(); + person!: Person; + selectedNode!: TreeNode; + selectedNodes!: TreeNode[]; + private cd = inject(ChangeDetectorRef); + familyTree = signal([]); + relations: RelationShip[] =[]; + _id = -10; + loading = false; + familyList:Person[] = []; + msg ="[Person organise component]"; + private messageService = inject(MessageService); + /* + private store: Store + */ + constructor( + public dialogService: DialogService, + private personService: PersonService, + + public ref: DynamicDialogRef, public config: DynamicDialogConfig, + ) {} + + ngOnInit(): void + { + const id = this.config.data.id; + this.familyList = this.config.data.familyList; + const item = this.familyList.find(x => x.id == id); + if (item != undefined) + this.person = item; + this.loadPersonFamilyTree(id); + // this. populateTree(); + + } + populateTree() : void { + const tree = [ + { + label: 'F.C Barcelona', + expanded: true, + children: [ + { + label: 'Argentina', + expanded: true, + children: [ + { + label: 'Argentina' + }, + { + label: 'France' + } + ] + }, + { + label: 'France', + expanded: true, + children: [ + { + label: 'France' + }, + { + label: 'Morocco' + } + ] + } + ] + } + ]; + this.familyTree.set(tree); + } + loadPersonFamilyTree(id: number): void { + const relationShip$ = this.personService.loadPersonFamily(id); + this.subscription.add(relationShip$.subscribe( + { + next: x => { + if (x.statusCode == 1) + { + let tree : TreeNode[] =[]; + tree.push(x.data); + this.familyTree.set(tree); + console.log("load person family", this.familyTree()); + + } + }, + error: e => { + console.error("error load loadPersonFamily by personId", id); + this.messageService.add({severity:'error', summary: 'error load loadPersonFamily by personId', detail: e.message}); + } + } + )); + } + + nodeSelect(event: TreeNodeSelectEvent) { + // this.messageService.add({ severity: 'info', summary: 'Node Selected', detail: event.node.label }); + } +nodeDoubleSelect(event:TreeNodeDoubleClickEvent) : void { + // console.log("double click node", event.node.key); + const id = event.node.key; + this.edit(Number(id)); +} + + getName(id:number): string + { + let result =""; + const item = this.familyList.find(x => x.id == id); + if (item) + result = item.lastName + " " + item.firstName; + return result; + } + updateParent(list:Person[]):void { + let i = 0; + let item:Person; + for (i = 0; i< list.length; i++) + { + item = list[i]; + if (item.fatherId && item.fatherId > 0) + { + item.fatherName = this.getName(item.fatherId); + } + } + } + + newFamily():void { + //console.log("add new employee"); + this.personService.parentList = this.familyList; + // this.router.navigate( ['/family/new'], { queryParams: {returnUrl:'/family' } }); + this.showEdit(this._id--); + } + edit(id: number) : void { + //console.log("edit family", id); + this.personService.parentList = this.familyList; + // this.router.navigate( ['/family/'+id], { queryParams: {returnUrl:'/family' } }); + this.showEdit(id); + } + showEdit(id:number) { + const ref = this.dialogService.open(PersonEdit, { + data: { + id, + familyList: this.familyList, + }, + header: 'Family', + width: '80%', + maximizable: true + }); + + ref.onClose.subscribe((item: Person) => { + if (item) { + //console.log("after close ward edit", item); + this.messageService.add({severity:'success', summary: 'Save Family', detail: item.firstName!}); + //update the current list + this.updateList(item); + } + }); + + } + updateList(item: Person) :void { + const idx = this.familyList.findIndex( x => x.id == item.id); + if (item.fatherId && item.fatherId > 0) + item.fatherName = this.getName(item.fatherId); + if (item.motherId && item.motherId > 0) + item.motherName = this.getName(item.motherId); + if (idx < 0) + { + + const olist = [... this.familyList, item]; + this.familyList = olist; + } + else + { + const oitem = this.familyList[idx]; + oitem.firstName = item.firstName; + oitem.lastName = item.lastName; + oitem.address = item.address; + oitem.alive = item.alive; + oitem.dob = item.dob; + oitem.email = item.email; + oitem.fatherId = item.fatherId; + oitem.motherId = item.motherId; + oitem.motherName = item.motherName; + oitem.fatherName = item.fatherName; + + } + } + deleteItem(id: number): void { + this.personService.deletePerson(id) + .pipe(take(1)) + .subscribe({ next: result => { + console.log(this.msg + " deleteItem success", result); + + const nlist = this.familyList.filter(d => d.id !== id); + this.familyList = nlist; + }, + error: e => console.error(e) + }); + //console.log(this.msg + "click button to delete"); + } + close() :void { + this.ref.close(null); + } + + ngOnDestroy() { + this.subscription.unsubscribe(); + } +} + diff --git a/UI/src/app/person/family.tree.css b/UI/src/app/person/family.tree.css new file mode 100644 index 0000000..3db3368 --- /dev/null +++ b/UI/src/app/person/family.tree.css @@ -0,0 +1,12 @@ +table { + width: 100%; +} + +.mat-form-field { + font-size: 14px; + width: 100%; +} +td.mat-column-edit, .mat-column-delete { + width: 35px; + padding-right: 2px; +} diff --git a/UI/src/app/person/family.tree.html b/UI/src/app/person/family.tree.html new file mode 100644 index 0000000..9c0b3f0 --- /dev/null +++ b/UI/src/app/person/family.tree.html @@ -0,0 +1,48 @@ +
+

Family Tree

+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+ + +
+ diff --git a/UI/src/app/person/family.tree.ts b/UI/src/app/person/family.tree.ts new file mode 100644 index 0000000..bc27239 --- /dev/null +++ b/UI/src/app/person/family.tree.ts @@ -0,0 +1,265 @@ +import { Component, OnInit, OnDestroy, inject, ChangeDetectorRef} from '@angular/core'; + +import { StaffView ,StaffSearch, Person } from '../models'; +import { OrganizationChartModule } from 'primeng/organizationchart'; + +import { take } from 'rxjs/operators'; +import { Subscription } from 'rxjs'; +import { Router } from '@angular/router'; +import { PersonService } from './person.service'; +import { AuthenticationService } from '../user-services'; +import { TableModule } from 'primeng/table'; +import { FormsModule } from '@angular/forms'; +import { CommonModule } from '@angular/common'; +import { ButtonModule } from 'primeng/button'; +import { TreeNode } from 'primeng/api'; +import { InputTextModule } from 'primeng/inputtext'; +import { DialogService } from 'primeng/dynamicdialog'; +import { PersonEdit } from './person.edit'; +import { MessageService } from 'primeng/api'; +import { TreeModule, TreeNodeDoubleClickEvent, TreeNodeSelectEvent } from 'primeng/tree'; +import { Utils } from '../shares'; + +@Component({ + selector: 'family-tree', + templateUrl: './family.tree.html', + imports:[TableModule,FormsModule,TreeModule, OrganizationChartModule,CommonModule,ButtonModule,InputTextModule], + styleUrls: ['./family.tree.css'], + providers: [DialogService] +}) +export class FamilyTree implements OnInit, OnDestroy{ + + private subscription:Subscription = new Subscription(); + firstname = ''; + selectedNode!: TreeNode; + private cd = inject(ChangeDetectorRef); + familyTree: TreeNode[] = []; + email = ''; + lastname = ''; + _id = -10; + loading = false; + familyList:Person[] = []; + msg ="[Person tree component]"; + private messageService = inject(MessageService); + /* + private store: Store + */ + constructor( + public dialogService: DialogService, + private personService: PersonService, + private authenticationService: AuthenticationService, + private router: Router + ) {} + getSearchCiteria(): StaffSearch { + let criteria:StaffSearch = { + email: this.email, + firstName: this.firstname, + lastName: this.lastname, + }; + + return criteria; + + } + canSearch():boolean { + let result = false; + result = this.email !== ""; + result = result || this.lastname !== ""; + result = result || this.firstname !== ""; + return result; + } + ngOnInit(): void + { + this.authenticationService.isHome = false; + this.authenticationService.isReport = false; + const prev = this.personService.searchCriteria; + let goload = true; + if (prev.lastName !== '') + { + this.lastname = prev.lastName; + goload = true; + } + if (prev.firstName !== '') + { + this.firstname = prev.firstName; + goload = true; + } + if (prev.email !== '') + { + this.email = prev.email; + goload = true; + } + if (goload) + { + this.search(); + } + } + getActive(active:boolean):string { + let result = 'false-icon pi-times-circle'; + if (active) + result = 'true-icon pi-check-circle'; + return result; + } + search():void { + const canSearch = true; // this.canSearch(); + if (canSearch) + { + this.loading = true; + //const criteria = this.getSearchCiteria(); + // this.personService.searchCriteria = criteria; + this.subscription.add( + this.personService.loadPersonFamilyTree(true, false).subscribe( + { + next: x => { + if (x.statusCode == 1) + { + this.familyTree = x.data; + this.loading = false; + this.cd.detectChanges(); + console.log("the family tree", this.familyTree); + } + }, + + error: e => { + const message = e || e.message; + // this.toastr.error(message); + this.loading = false; + console.log("error ", e); + } + }) + ); + + /* + this.personService.searchPersons(criteria).subscribe( { + next: result => { + // console.log(this.msg + "search load Data", result); + this.familyList = result.data; + this.familyTree = Utils.populateNode( "fatherId", this.familyList); + this.loading = false; + this.cd.detectChanges(); + } + }, + + error: e => { + const message = e || e.message; + // this.toastr.error(message); + this.loading = false; + console.log("error ", e); + } + }) + ); + */ + + + } + } + + nodeSelect(event: TreeNodeSelectEvent) { + // this.messageService.add({ severity: 'info', summary: 'Node Selected', detail: event.node.label }); + } +nodeDoubleSelect(event:TreeNodeDoubleClickEvent) : void { + // console.log("double click node", event.node.key); + const id = event.node.key; + this.edit(Number(id)); +} + + getName(id:number): string + { + let result =""; + const item = this.familyList.find(x => x.id == id); + if (item) + result = item.lastName + " " + item.firstName; + return result; + } + updateParent(list:Person[]):void { + let i = 0; + let item:Person; + for (i = 0; i< list.length; i++) + { + item = list[i]; + if (item.fatherId && item.fatherId > 0) + { + item.fatherName = this.getName(item.fatherId); + } + } + } + + newFamily():void { + //console.log("add new employee"); + this.personService.parentList = this.familyList; + // this.router.navigate( ['/family/new'], { queryParams: {returnUrl:'/family' } }); + this.showEdit(this._id--); + } + edit(id: number) : void { + //console.log("edit family", id); + this.personService.parentList = this.familyList; + // this.router.navigate( ['/family/'+id], { queryParams: {returnUrl:'/family' } }); + this.showEdit(id); + } + showEdit(id:number) { + const ref = this.dialogService.open(PersonEdit, { + data: { + id, + familyList: this.familyList, + }, + header: 'Family', + width: '80%', + maximizable: true + }); + + ref.onClose.subscribe((item: Person) => { + if (item) { + //console.log("after close ward edit", item); + this.messageService.add({severity:'success', summary: 'Save Family', detail: item.firstName!}); + //update the current list + this.updateList(item); + } + }); + + } + updateList(item: Person) :void { + const idx = this.familyList.findIndex( x => x.id == item.id); + if (item.fatherId && item.fatherId > 0) + item.fatherName = this.getName(item.fatherId); + if (item.motherId && item.motherId > 0) + item.motherName = this.getName(item.motherId); + if (idx < 0) + { + + const olist = [... this.familyList, item]; + this.familyList = olist; + } + else + { + const oitem = this.familyList[idx]; + oitem.firstName = item.firstName; + oitem.lastName = item.lastName; + oitem.address = item.address; + oitem.alive = item.alive; + oitem.dob = item.dob; + oitem.email = item.email; + oitem.fatherId = item.fatherId; + oitem.motherId = item.motherId; + oitem.motherName = item.motherName; + oitem.fatherName = item.fatherName; + + } + } + deleteItem(id: number): void { + this.personService.deletePerson(id) + .pipe(take(1)) + .subscribe({ next: result => { + console.log(this.msg + " deleteItem success", result); + + const nlist = this.familyList.filter(d => d.id !== id); + this.familyList = nlist; + }, + error: e => console.error(e) + }); + //console.log(this.msg + "click button to delete"); + } + + ngOnDestroy() { + this.subscription.unsubscribe(); + } +} + diff --git a/UI/src/app/person/familylist.css b/UI/src/app/person/familylist.css new file mode 100644 index 0000000..3db3368 --- /dev/null +++ b/UI/src/app/person/familylist.css @@ -0,0 +1,12 @@ +table { + width: 100%; +} + +.mat-form-field { + font-size: 14px; + width: 100%; +} +td.mat-column-edit, .mat-column-delete { + width: 35px; + padding-right: 2px; +} diff --git a/UI/src/app/person/familylist.html b/UI/src/app/person/familylist.html new file mode 100644 index 0000000..e81839a --- /dev/null +++ b/UI/src/app/person/familylist.html @@ -0,0 +1,83 @@ +
+

Person List

+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+ + +
+ + + + + + +
+
+ + + + Last Name + + First Name + + Sex + Father + + Mother + + + Edit + + + + + {{user.lastName}} + {{user.firstName}} + {{user.sex}} + {{user.fatherName}} + {{user.motherName}} + + + + + + + + +
+ +
+ diff --git a/UI/src/app/person/familylist.ts b/UI/src/app/person/familylist.ts new file mode 100644 index 0000000..6764344 --- /dev/null +++ b/UI/src/app/person/familylist.ts @@ -0,0 +1,328 @@ +import { Component, OnInit, OnDestroy, inject, ChangeDetectorRef, signal, ViewChild} from '@angular/core'; + +import { StaffView ,StaffSearch, Person } from '../models'; + + +import { take } from 'rxjs/operators'; +import { Subscription } from 'rxjs'; +import { Router } from '@angular/router'; +import { PersonService } from './person.service'; +import { AuthenticationService } from '../user-services'; +import { Table, TableModule } from 'primeng/table'; +import { FormsModule } from '@angular/forms'; +import { CommonModule } from '@angular/common'; +import { ButtonModule } from 'primeng/button'; +import { InputTextModule } from 'primeng/inputtext'; +import { DialogService } from 'primeng/dynamicdialog'; +import { PersonEdit } from './person.edit'; +import { ConfirmationService, MenuItem, MessageService } from 'primeng/api'; +import { IconFieldModule } from 'primeng/iconfield'; +import { InputIconModule } from 'primeng/inputicon'; +import { Menu, MenuModule } from 'primeng/menu'; +import { FamilyOrga } from './family.orga'; +@Component({ + selector: 'family-list', + templateUrl: './familylist.html', + imports:[TableModule,FormsModule,CommonModule,ButtonModule,MenuModule, + InputTextModule,IconFieldModule,InputIconModule], + styleUrls: ['./familylist.css'], + providers: [DialogService] +}) +export class FamilyList implements OnInit, OnDestroy{ + + private subscription:Subscription = new Subscription(); + //private cd = inject(ChangeDetectorRef); + items: MenuItem[] | undefined; + selectedPerson!: Person; + firstname = ''; + email = ''; + lastname = ''; + _id = -10; + selectId = -1; + loading = false; + familyList = signal([]); + msg ="[Person component]"; + @ViewChild(Table) dt2!: Table; + @ViewChild('rowmenu') popMenu?: Menu; + private messageService = inject(MessageService); + public dialogService= inject( DialogService); + private personService= inject( PersonService); + private confirmationService = inject(ConfirmationService); + private cd = inject(ChangeDetectorRef); + private authenticationService= inject( AuthenticationService); + private router= inject( Router); + constructor() {} + getSearchCiteria(): StaffSearch { + let criteria:StaffSearch = { + email: this.email, + firstName: this.firstname, + lastName: this.lastname, + }; + console.log("get search citeria", criteria); + return criteria; + + } + initMenu(): void { + this.items = [ + { + label: 'Edit', + icon: 'pi pi-pencil', + command: () => { + this.edit(this.selectId); + } + }, + { + label: 'Delete', + icon: 'pi pi-times', + command: () => { + this.delete(this.selectId); + } + }, + { + label: 'Show Organization', + icon: 'pi pi-sitemap', + command: () => { + this.showChildren(this.selectId); + } + } + + ]; + } + actionClick(id: number, event:Event): void { + // console.log("action edit "+ id); + this.selectId = id; + this.popMenu!.toggle(event); + } + showChildren(id: number): void { + console.log("show children of id", id); + this.showOrganise(id); + +} + handleInput(event: Event) { + const value = (event.target as HTMLInputElement).value; + this.dt2.filterGlobal(value, 'contains'); + } + canSearch():boolean { + let result = false; + result = this.email !== ""; + result = result || this.lastname !== ""; + result = result || this.firstname !== ""; + return result; + } + onMenuShow(): void { + console.log("this is show", this.selectedPerson); + } + + ngOnInit(): void + { + this.initMenu(); + this.authenticationService.isHome = false; + this.authenticationService.isReport = false; + const prev = this.personService.searchCriteria; + let goload = true; + if (prev.lastName !== '') + { + this.lastname = prev.lastName; + goload = true; + } + if (prev.firstName !== '') + { + this.firstname = prev.firstName; + goload = true; + } + if (prev.email !== '') + { + this.email = prev.email; + goload = true; + } + if (goload) + { + this.search(); + } + } + getActive(active:boolean):string { + let result = 'false-icon pi-times-circle'; + if (active) + result = 'true-icon pi-check-circle'; + return result; + } + search():void { + const canSearch = true; // this.canSearch(); + if (canSearch) + { + this.loading = true; + const criteria = this.getSearchCiteria(); + this.personService.searchCriteria = criteria; + this.subscription.add( + this.personService.searchPersons(criteria).subscribe( { + next: result => { + // console.log(this.msg + "search load Data", result); + const familyList = result.data; + this.familyList.set(familyList); + this.updateParent( this.familyList()); + //this.familyList.set(familyList); + + console.log("the person from load", this.familyList()); + this.loading = false; + this.cd.detectChanges(); + }, + error: e => { + const message = e || e.message; + // this.toastr.error(message); + this.loading = false; + console.log("error ", e); + } + }) + ); + } + } + getName(id:number): string + { + let result =""; + const item = this.familyList().find(x => x.id == id); + if (item) + result = item.lastName + " " + item.firstName; + return result; + } + updateParent(list:Person[]):void { + let i = 0; + let item:Person; + for (i = 0; i< list.length; i++) + { + item = list[i]; + if (item.fatherId && item.fatherId > 0) + { + item.fatherName = this.getName(item.fatherId); + } + if (item.motherId && item.motherId > 0) + { + item.motherName = this.getName(item.motherId); + } + } + } + + delete(id: number): void { + this.confirmationService.confirm({ + + message: 'Do you want to delete this record?', + header: 'Confirmation Delete', + icon: 'pi pi-info-circle', + rejectLabel: 'Cancel', + rejectButtonProps: { + label: 'Cancel', + severity: 'secondary', + outlined: true, + }, + acceptButtonProps: { + label: 'Delete', + severity: 'danger', + }, + + accept: () => { + this.deleteItem(id); + } + }); + } + + deleteItem(id: number): void { + this.personService.deletePerson(id) + .pipe(take(1)) + .subscribe({ next: result => { + console.log(this.msg + " deleteItem success", result); + + const nlist = this.familyList().filter(d => d.id !== id); + this.familyList.set(nlist); + }, + error: e => console.error(e) + }); + //console.log(this.msg + "click button to delete"); + } + + newFamily():void { + //console.log("add new employee"); + this.personService.parentList = this.familyList(); + // this.router.navigate( ['/family/new'], { queryParams: {returnUrl:'/family' } }); + this.showEdit(this._id--); + } + edit(id: number) : void { + //console.log("edit family", id); + this.personService.parentList = this.familyList(); + // this.router.navigate( ['/family/'+id], { queryParams: {returnUrl:'/family' } }); + this.showEdit(id); + } + showEdit(id:number) { + const ref = this.dialogService.open(PersonEdit, { + data: { + id, + familyList: this.familyList(), + }, + header: 'Person', + width: '80%', + maximizable: true + }); + + ref.onClose.subscribe((item: Person) => { + if (item) { + //console.log("after close ward edit", item); + // this.messageService.add({severity:'success', summary: 'Save Family', detail: item.firstName!}); + //update the current list + this.updateList(item); + } + }); + } + + showOrganise(id:number) { + const ref = this.dialogService.open(FamilyOrga, { + data: { + id, + familyList: this.familyList(), + }, + header: 'Children', + width: '80%', + maximizable: true + }); + + ref.onClose.subscribe((item: Person) => { + if (item) { + //console.log("after close ward edit", item); + // this.messageService.add({severity:'success', summary: 'Save Family', detail: item.firstName!}); + //update the current list + //this.updateList(item); + } + }); + } + + updateList(item: Person) :void { + const idx = this.familyList().findIndex( x => x.id == item.id); + if (item.fatherId && item.fatherId > 0) + item.fatherName = this.getName(item.fatherId); + if (item.motherId && item.motherId > 0) + item.motherName = this.getName(item.motherId); + if (idx < 0) + { + const olist = [... this.familyList(), item]; + this.familyList.set(olist); + } + else + { + const oitem = this.familyList()[idx]; + oitem.firstName = item.firstName; + oitem.lastName = item.lastName; + oitem.address = item.address; + oitem.alive = item.alive; + oitem.dob = item.dob; + oitem.sex = item.sex; + oitem.email = item.email; + oitem.fatherId = item.fatherId; + oitem.motherId = item.motherId; + oitem.fatherName = item.fatherName; + oitem.motherName = item.motherName; + } + } + + + ngOnDestroy() { + this.subscription.unsubscribe(); + } +} + diff --git a/UI/src/app/person/index.ts b/UI/src/app/person/index.ts new file mode 100644 index 0000000..0455764 --- /dev/null +++ b/UI/src/app/person/index.ts @@ -0,0 +1,5 @@ +export * from './familylist'; +export * from './person.edit'; +export * from './family.tree'; +export * from './person.service'; + diff --git a/UI/src/app/person/person.edit.css b/UI/src/app/person/person.edit.css new file mode 100644 index 0000000..139597f --- /dev/null +++ b/UI/src/app/person/person.edit.css @@ -0,0 +1,2 @@ + + diff --git a/UI/src/app/person/person.edit.html b/UI/src/app/person/person.edit.html new file mode 100644 index 0000000..67774f5 --- /dev/null +++ b/UI/src/app/person/person.edit.html @@ -0,0 +1,114 @@ +
+ +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+
+ + +
+
+
+ +
+
+ @if (adminuserForm.value.image != "" && adminuserForm.value.image != null) + { + + } + @else + { +
+ +
+ + +
+
+ } +
+
+
+ +
+ + + + First Name + Last Name + Sex + Action + + + + + {{ user.pfirstName }} + {{ user.plastName }} + {{ user.sex }} + + + + + + +
+
+ +
+ + +
+
+ +
\ No newline at end of file diff --git a/UI/src/app/person/person.edit.ts b/UI/src/app/person/person.edit.ts new file mode 100644 index 0000000..7d3c794 --- /dev/null +++ b/UI/src/app/person/person.edit.ts @@ -0,0 +1,561 @@ +import { ChangeDetectorRef, Component, inject, OnDestroy, OnInit, signal, ViewChild } from '@angular/core'; +import { Router, ActivatedRoute } from '@angular/router'; +import {DialogService, DynamicDialogRef} from 'primeng/dynamicdialog'; +import {DynamicDialogConfig} from 'primeng/dynamicdialog'; +import { FormControl, ReactiveFormsModule, UntypedFormBuilder, Validators } from '@angular/forms'; +import { Subject, Subscription} from 'rxjs'; +import { ConfirmationService, MessageService } from 'primeng/api'; +import { DatePickerModule } from 'primeng/datepicker'; +import { Code, RelationShipView, Person, mState, RelationShip} from '../models'; +import { AppSettingService, LookupService, Utils } from '../shares'; +import { PersonService } from './person.service'; +import { ButtonModule } from 'primeng/button'; +import { SelectModule } from 'primeng/select'; +import { CheckboxModule } from 'primeng/checkbox'; +import { InputTextModule } from 'primeng/inputtext'; +import moment from 'moment'; +import { HttpEvent } from '@angular/common/http'; +import { TableModule } from 'primeng/table'; +import { Pickperson } from '../pickperson/pickperson'; + + +export interface FileUploadEvent { + originalEvent: HttpEvent; + files: File[]; +}; +export interface FileUploadHandlerEvent { + files: File[]; +}; + +@Component({ +templateUrl: 'person.edit.html', +selector: 'person-edit', +imports:[ButtonModule,TableModule,ReactiveFormsModule,SelectModule,CheckboxModule, InputTextModule,DatePickerModule], +styleUrls: ['person.edit.css'], +providers: [DialogService] + +}) +export class PersonEdit implements OnInit, OnDestroy { + editRef: DynamicDialogRef | undefined; + returnUrl =''; + loginUser =''; + hostsite =''; + _error =''; + _id= -1; + _partnerId = -1; + fatherList: Code[] =[]; + motherList: Code[] =[]; + sexList:Code[] =[]; + familyList: Person[] =[]; + file: File | null = null; + fileName = ''; + isNew = false; + validationPoints?: Code[]; + partners = signal([]); + deletePartner: RelationShipView[] =[]; + msg="[Family Edit Component] "; + private formBuilder = inject(UntypedFormBuilder); + private personService = inject(PersonService); + private messageService = inject(MessageService); + private confirmationService = inject(ConfirmationService); + //for focus input +// @ViewChild('mystaffid') mystaffNo!: MatInput; + isChange = true; // disable use false//true for not disable. make sure it true is disable button. + private subscription:Subscription = new Subscription(); + subChanged$ = new Subject(); + adminuserForm = this.formBuilder.group({ + id: [0], //Validators.required + email: [''], //Validators.required + firstname: ['',Validators.required], //Validators.required + lastname: ['',Validators.required], //Validators.required + phone: [''], //Validators.required + address: [''], + sex:['M',Validators.required], + image: [''], + dob: new FormControl(null), + alive: [false], //Validators.required + fatherId:[0], + motherId:[0] + + + + }); +constructor( private cdr: ChangeDetectorRef,public dialogService: DialogService, + public ref: DynamicDialogRef, public config: DynamicDialogConfig, + + private router: Router, private route: ActivatedRoute, + private appSetting: AppSettingService, + ) { + this.hostsite = this.appSetting.appSetting.attachment; + } + loadParentList( list: Person[], selfId: number): void { + this.familyList = list; + let i = 0; + let item: Code; + let family: Person; + //const list = this.familyService.parentList; + for (i = 0; i < list.length; i++) + { + family= list[i]; + if (family.id != selfId) + { + item = { id: family.id, + name: family.firstName! + " " + family.lastName!, + status: family.lastName!, + active: family.alive! + + }; + if (family.sex == 'F') + this.motherList.push(item); + else + this.fatherList.push(item); + + } + } + + + } + +getClassForRequire(prev:string,name:string){ + const notok = !this.adminuserForm.controls[name].valid && + this.adminuserForm.controls[name].touched; + let str =prev; + if (notok) + str += " ng-invalid ng-dirty"; + return str; +} + +onFileSelected(event: any) { + + const file: File = event.target.files[0]; + + if (file) { + this.file = file; + //this.messageService.add({ severity: 'info', summary: 'Success', detail: 'File Uploaded!' }); + // this.courseForm.patchValue({ attachmenFile: this.file.name }); + this.fileName = file.name; + this.subChanged$.next(false); + } + } + +deleteFile(): void { + this.confirmationService.confirm({ + + message: 'Are you sure that you want to delete image?', + header: 'Confirmation', + closable: true, + closeOnEscape: true, + icon: 'pi pi-exclamation-triangle', + rejectButtonProps: { + label: 'Cancel', + severity: 'secondary', + outlined: true, + }, + acceptButtonProps: { + label: 'Delete', + }, + accept: () => { + this.doDeleteFile(); + }, + + }); +} + +doDeleteFile(): void { + const fileName = this.adminuserForm.value.image; + const familyId = this.adminuserForm.value.id; + const data = { fileName, familyId }; + const delete$ = this.personService.deleteUploadFile(data); + this.subscription.add(delete$.subscribe( + { + next: x => { + if (x.statusCode == 1) { + { + this.adminuserForm.patchValue({ image: "" }); + this.cdr.detectChanges(); + this.messageService.add({ severity: 'success', summary: 'Delete image all ok', detail: 'file: ' + fileName }); + } + } + else { + this.messageService.add({ severity: 'error', summary: 'Error delete upload file', detail: 'Fail to delete upload file: ' + x.message }); + } + }, + error: e => { + this.messageService.add({ severity: 'error', summary: 'Error delete upload file', detail: 'Fail to delete upload file: ' + e }); + } + } + )); + + + } + +getFileToSave(file: File): FormData { + + //const familyId = this.adminuserForm.value.id!; + const formData = new FormData(); + formData.append("file", file); + return formData; + } + // formData.append("familyId", familyId.toString()); + // console.log("upload file", familyId, file); +saveFile(formData: FormData) +{ + const upload$ = this.personService.uploadFile(formData); + this.subscription.add(upload$.subscribe( + { + next: x => { + if (x.statusCode == 1) { + const filename = x.data; + //this.messageService.add({ severity: 'info', summary: 'Success', detail: 'File Uploaded to server API: ' + x.data }); + this.adminuserForm.patchValue({ image: filename }); + this.cdr.detectChanges(); + } + else + this.messageService.add({ severity: 'error', summary: 'Error upload file', detail: 'Fail to upload file: ' + x.message }); + }, + error: e => + this.messageService.add({ severity: 'error', summary: 'Error upload file', detail: 'Fail to upload file: ' }) + + } + )); +} + +populateSex(): void { + let item: Code; + item = { + id:1, + name:"Female", + status:'F' + }; + this.sexList.push(item); + item = { + id:2, + name:"Male", + status:'M' + }; +this.sexList.push(item); + +} + +ngOnInit():void { + this.populateSex(); + const id = this.config.data.id; + console.log("family edit", this.config); + this.loadParentList(this.config.data.familyList, id); + //this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/'; +// const id = Number(this.route.snapshot.paramMap.get('id')); + // now load thing up + const user = Utils.getCurrentUser(); +// console.log(this.msg + "current login user ", user); + if (user.username === '') + alert("you are not login."); + else + this.loginUser = user.firstName; + this._id = id; + //console.log(this.msg + " " + id ); + this.subscription.add(this.adminuserForm.valueChanges.subscribe(x => this.isChange = false)); + this.subscription.add(this.subChanged$.subscribe(x => this.isChange = x)); + if (id > 0) + { + this.isNew = false; + this.subscription.add( + this.personService.loadPersonById(id).subscribe({ + next: x => this.assignValue(x.data), + error: e => console.log(e) + }) + ); + } + else + { + this.isNew = true; + } + +} + +// this for disable submit button +get isFieldsChange() { + const chan = this.isChange || !this.adminuserForm.valid; // this disable so need true valid = true not + //console.log(this.msg + 'is fields change', chan); + return chan; + } + +assignValue(item:Person): void { + this._id = item.id; + this.fileName = item.image!; + if (item.relationShips) + this.populatePartner(item.relationShips, item.id); + this.adminuserForm.patchValue({ + id: item.id, + email: item.email, + firstname: item.firstName, + lastname: item.lastName, + alive: item.alive, + phone: item.phone, + address: item.address, + fatherId: item.fatherId, + motherId: item.motherId, + sex: item.sex, + image: item.image + }); + const dob = item.dob; + if (dob) + { + this.adminuserForm.patchValue({ + dob: moment(dob).toDate() + }); + } + // disable use false//true for not disable. + this.subChanged$.next(true); + } + + // convenience getter for easy access in form fields + get f() { return this.adminuserForm.controls;} + validate(adminuser:Person): boolean { + let result = true; + console.log("validate", adminuser); + if (!adminuser.firstName) + { + this._error = 'firstname is blank or empty'; + result = false; + } + else if (adminuser.lastName == '') + { + this._error = 'lastname is blank or empty'; + result = false; + } + return result; + } + + getPartnerForSave(): RelationShip[] { + let rlist: RelationShip[]= []; + let i =0; + let vitem: RelationShipView; + let item: RelationShip; + for (i = 0; i < this.deletePartner.length; i++) + { + vitem = this.deletePartner[i]; + item = { + id: vitem.id, + personId: vitem.personId, + state: mState.Delete, + relatePersonId: vitem.relatePersonId + }; + rlist.push(item); + console.log("get partner for save delete list ", vitem); + } + console.log("get partner for save before loop this.partners ", i); + for (i = 0; i < this.partners().length; i++) + { + vitem = this.partners()[i]; + if (vitem.state == mState.New || vitem.state == mState.Modified) + { + item = { + id: vitem.id, + personId: vitem.personId, + state: vitem.state, + relatePersonId: vitem.relatePersonId + }; + rlist.push(item); + } + console.log("get partner for save in partner list ", vitem); + } + + + return rlist; + } + + async onSubmit(e:Event): Promise { + e.preventDefault(); + // if form valid then go save + //make sure + const okToSave = this.adminuserForm.valid; + if (okToSave) + { + let adminuserValue = this.adminuserForm.value; + + let item:Person = { + id: adminuserValue.id, + email: adminuserValue.email, + firstName: adminuserValue.firstname, + lastName: adminuserValue.lastname, + alive: adminuserValue.alive, + phone: adminuserValue.phone, + address: adminuserValue.address, + image:adminuserValue.image, + fatherId: adminuserValue.fatherId, + motherId: adminuserValue.motherId, + sex: adminuserValue.sex + }; + + if (adminuserValue.dob) + { + item.dob = moment(adminuserValue.dob).format("YYYY-MM-DD"); + } + + this._error =''; + const allOK = this.validate(item); + if (allOK) + { + let container:any = { + person: item + }; + if (this.file != null) + { + container.formData = await Utils.toBase64(this.file); + container.fileName = this.file.name; + console.log('image as base64 ', container); + } + container.person.relationShips = this.getPartnerForSave(); + this.subscription.add ( + this.personService.savePerson(container).subscribe({ + next: x => { + if (x.statusCode >= 1) + { + item.id = x.data; + // this.messageService.add({severity:'success', summary: 'Save user', detail: item.firstName + " " + item.lastName }); + //this.router.navigate([this.returnUrl]); + console.log("the person after save", item); + this.ref.close(item); + } + else + { + this.messageService.add({severity:'error', summary: 'Error', detail: x.message }); + } + }, + error: e => { + const message = e.message; + this.messageService.add({severity:'error', summary: 'Error', detail: message }); + console.error("error ", e); + } + }) + ); + } + else + this.messageService.add({severity:'error', summary: 'Error', detail: this._error }); + } +} +getFamilyById(id: number): Person | undefined { + let result:Person |undefined; + result = this.familyList.find(x => x.id == id); + return result; +} +populatePartner(list: RelationShip[], personId: number): void { + let item: RelationShip; + let vitem: RelationShipView; + let vlist: RelationShipView[] =[]; + let i = 0; + let family:Person|undefined; + for (i = 0; i < list.length; i++) + { + item = list[i]; + if (item.relatePersonId != personId) + { + family = this.getFamilyById(item.relatePersonId); + } + else + { + + family = this.getFamilyById(item.personId); + } + if (family) + { + vitem = { + id: item.id, + personId: item.personId, + relatePersonId: item.relatePersonId, + pfirstName: family.firstName, + plastName: family.lastName, + sex: family.sex, + state:mState.NoChange + }; + vlist.push(vitem); + } + } + if (vlist.length > 0) + this.partners.set(vlist); +} + + +addPartner(): void { + const title = "Partner"; + this.showPickPerson(title, (sfamily: Person) => { + let item: RelationShipView = { + id: this._partnerId--, + state: mState.New, + personId: this._id, + relatePersonId: sfamily.id, + pfirstName: sfamily.firstName, + plastName: sfamily.lastName, + sex: sfamily.sex, + }; + const fm = this.partners(); + fm.push(item); + this.partners.set(fm); + this.subChanged$.next(false); + console.log("after put in partners list", this.partners()); + }); +} + +onDeletePartner(item:RelationShipView): void { + this.confirmationService.confirm({ + + message: 'Are you sure that you want to delete partner?', + header: 'Confirmation', + closable: true, + closeOnEscape: true, + icon: 'pi pi-exclamation-triangle', + rejectButtonProps: { + label: 'Cancel', + severity: 'secondary', + outlined: true, + }, + acceptButtonProps: { + label: 'Delete', + }, + accept: () => { + this.doDeletePartner(item); + }, + + }); +} + +doDeletePartner(item:RelationShipView): void { + if (item.id > 0) + { + this.deletePartner.push(item); + } + const dlist = this.partners().filter(x => x.id != item.id); + this.partners.set(dlist); + console.log("after delete partners list", this.partners()); + this.subChanged$.next(false); +} + +showPickPerson(title:string, callback:(id: Person) => void) :void { + const ref = this.dialogService.open(Pickperson, { + data: { + familyList: this.familyList, + }, + header: title, + width: '80%', + + maximizable: true + }); + + ref.onClose.subscribe((item: Person) => { + if (item) { + //console.log("after close ward edit", item); + this.messageService.add({severity:'success', summary: 'Select', detail: item.firstName!}); + //update the current list + callback(item); + } + }); +} + +cancel(e:Event):void { + e.preventDefault(); + this.ref.close(null); + } +ngOnDestroy() { + this.subscription.unsubscribe(); +} +} diff --git a/UI/src/app/person/person.service.ts b/UI/src/app/person/person.service.ts new file mode 100644 index 0000000..81ee7a8 --- /dev/null +++ b/UI/src/app/person/person.service.ts @@ -0,0 +1,85 @@ + +import { Injectable } from '@angular/core'; +import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; +import { Observable } from 'rxjs'; +import { Staff,StaffSearch, StaffView, ResultModel,ConfigureUrl,ResetPassword, Person, PersonContainer, RelationShip } from '../models'; +import { AppSettingService } from '../shares'; +import { TreeNode } from 'primeng/api'; + +@Injectable({ providedIn: 'root' }) +export class PersonService { + public searchCriteria: StaffSearch; + public parentList: Person[] =[]; + constructor(private http: HttpClient, + + private appSetting :AppSettingService + ) { + this.searchCriteria = { + email:'', + firstName: '', + lastName:'' + }; + + } + searchPersons(criteria: StaffSearch): Observable> { + let config = { headers : { 'Content-Type': 'application/json' } }; + const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl + "/SearchPerson"; + return this.http.post>(baseUrl, criteria, config); + } + + loadPersonById(id:number): Observable> { + const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl; + /* + const params = new HttpParams().set("id", ""+id); + const headers = new HttpHeaders().set('Content-Type', 'application/json'); + const options = { + headers: headers, + params: params + }; + */ + + return this.http.get>(baseUrl + "/GetById/" + id); + } + loadPersonFamily(id: number): Observable> { + const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl; + return this.http.get>(baseUrl + "/GetByPersonFamily/" + id); + } + + loadPersonFamilyTree(useFather: boolean, useMother: boolean): Observable> { + const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl; + const data = {useFather,useMother}; + return this.http.post>(baseUrl + "/GetFamilyTreeBy", data); + } + + loadChildrenById(fatherId:number, motherId: number): Observable> { + const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl; + const data = {fatherId,motherId}; + + return this.http.post>(baseUrl + "/GetChildress/" ,data); + } + loadRelationshipById(personId:number): Observable> { + const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.relationShipUrl; + return this.http.get>(baseUrl + "/GetByPersonId/" +personId); + } + savePerson(data:PersonContainer): Observable> { //insert Adminuser + let config = { headers : { 'Content-Type': 'application/json' } }; + console.log("save family", data); + const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl +"/SavePerson"; + return this.http.post>(baseUrl, data, config); + } + + deletePerson(id:number): Observable>{ + const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.personUrl; + const data = {id}; + return this.http.post>(baseUrl + "/DeleteById" ,data); + } + uploadFile(data: any): Observable> { + const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.personUrl + "/UploadImage"; + return this.http.post>(baseUrl, data); + } + deleteUploadFile(data: any): Observable> { + //data ={filename}; + const baseUrl = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.personUrl + "/DeleteUploadFile"; + return this.http.post>(baseUrl, data); + } +} diff --git a/UI/src/app/pickperson/pickperson.css b/UI/src/app/pickperson/pickperson.css new file mode 100644 index 0000000..e69de29 diff --git a/UI/src/app/pickperson/pickperson.html b/UI/src/app/pickperson/pickperson.html new file mode 100644 index 0000000..1a5690b --- /dev/null +++ b/UI/src/app/pickperson/pickperson.html @@ -0,0 +1,47 @@ +
+ + +
+ + + + + + +
+
+ + + Last Name + + First Name + + Sex + + + + + + + {{user.lastName}} + {{user.firstName}} + {{user.sex}} + + + +
+
+ + +
+
+ diff --git a/UI/src/app/pickperson/pickperson.ts b/UI/src/app/pickperson/pickperson.ts new file mode 100644 index 0000000..3b5753d --- /dev/null +++ b/UI/src/app/pickperson/pickperson.ts @@ -0,0 +1,53 @@ +import { CommonModule } from '@angular/common'; +import { Component, OnDestroy, OnInit, signal, ViewChild } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { Table, TableModule } from 'primeng/table'; +import { Person } from '../models'; +import { IconFieldModule } from 'primeng/iconfield'; +import { InputIconModule } from 'primeng/inputicon'; +import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog'; +import { ButtonModule } from 'primeng/button'; +import { InputTextModule } from 'primeng/inputtext'; + +@Component({ + selector: 'app-pickperson', + imports: [TableModule, ButtonModule,CommonModule, InputTextModule, FormsModule,IconFieldModule,InputIconModule], + templateUrl: './pickperson.html', + styleUrl: './pickperson.css' +}) +export class Pickperson implements OnInit, OnDestroy{ + @ViewChild(Table) dt2!: Table; + constructor(public ref: DynamicDialogRef, public config: DynamicDialogConfig) + { + + } + loading = false; + familyList = signal([]); + selectedPerson!: Person; + handleInput(event: Event) { + const value = (event.target as HTMLInputElement).value; + this.dt2.filterGlobal(value, 'contains'); + } + + edit(id: number): void { + + } + cancel(e:Event):void { + e.preventDefault(); + this.ref.close(null); + } + select(e:Event):void { + e.preventDefault(); + this.ref.close(this.selectedPerson); + } + + ngOnDestroy(): void { + + } + ngOnInit(): void { + console.log("pick person the familyList", this.config); + this.familyList.set(this.config.data.familyList); + + } + +} diff --git a/UI/src/app/route-guard/auth.guard.ts b/UI/src/app/route-guard/auth.guard.ts new file mode 100644 index 0000000..fd14db4 --- /dev/null +++ b/UI/src/app/route-guard/auth.guard.ts @@ -0,0 +1,52 @@ +import { Injectable } from '@angular/core'; +import { Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; +import { Utils } from '../shares'; +import {AuthenticationService} from '../user-services/authentication.service'; +@Injectable({ providedIn: 'root' }) +export class AuthGuard { + msg ="[AuthGuard] "; + constructor(private router: Router, + private authService :AuthenticationService ) { } + + canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { + const roleAllowed = route.data['roleAllowed']; + const user = Utils.getCurrentUser(); + const userRole = Utils.getUserRole(user); + let hasLoggedIn = Utils.getIsAuth(); + //console.log(this.msg + " the userlogin ", user, hasLoggedIn); + if (hasLoggedIn) { + + if (roleAllowed != undefined) + { + //it number toString + // console.log("[auth-guard] ther roleAllow ", roleAllowed); + // console.log("[auth-guard] before roleAllow ", roleAllowed,userRole); + if (roleAllowed.indexOf(userRole.toString()) != -1) { + // console.log("[auth-guard] pass roleAllow ", roleAllowed,userRole); + return true; + } + else { + this.authService.logout(); + this.router.navigate(['/login']); + return false; + } + } + else //if no role define just return hasLoggedIn + { + return hasLoggedIn; + } + } + else + { + // this.authService.logout(); + this.router.navigate(['/login']); + return false; + } + + // not logged in so redirect to login page with the return url + //this.router.navigate(['/login'], { queryParams: { returnUrl: state.url } }); + this.authService.logout(); + return false; + } + +} diff --git a/UI/src/app/route-guard/index.ts b/UI/src/app/route-guard/index.ts new file mode 100644 index 0000000..b41e34a --- /dev/null +++ b/UI/src/app/route-guard/index.ts @@ -0,0 +1 @@ +export * from './auth.guard'; diff --git a/UI/src/app/shares/CommonUtilities.ts b/UI/src/app/shares/CommonUtilities.ts new file mode 100644 index 0000000..46f5788 --- /dev/null +++ b/UI/src/app/shares/CommonUtilities.ts @@ -0,0 +1,54 @@ +import { FormControl, FormGroup } from '@angular/forms'; + +export class CommonUtilities { + /** Display the blob */ + static displayFileInNewTab(response:any, defaultFileName:string) { + const contentType = response.headers.get('content-type'); + const blob = new Blob([response.body], { type: contentType }); + const fileName = this.getFileName(response,defaultFileName); + const nav = (window.navigator as any); + if (nav.msSaveOrOpenBlob) { + nav.msSaveOrOpenBlob(blob, fileName); + } else { + const data = window.URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = data; + link.target = '_blank'; + link.click(); + } + } + /*** Download the blob ***/ + static downloadFile(response:any, defaultFileName:string) { + const contentType = response.headers.get('content-type'); + const blob = new Blob([response.body], { type: contentType }); + const fileName = this.getFileName(response,defaultFileName); + const nav = (window.navigator as any); + if (typeof nav.msSaveBlob === 'function') + { + console.log("download the report saveblob"); + nav.msSaveBlob(blob, fileName); + console.log("download the report saveblob done"); + } + else { + const link = document.createElement('a'); + link.href = window.URL.createObjectURL(blob); + console.log("no msSaveBlob download the report saveblob done"); + //link.download? = fileName; + link.download = fileName; + link.click(); + } + } + /** Get the attachment file name */ + static getFileName(response:any, defaultFileName:string) { + const contentDis = response.headers.get('content-disposition'); + let fileName =defaultFileName; + if (contentDis && contentDis.indexOf('attachment') !== -1) { + const filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/; + const matches = filenameRegex.exec(contentDis); + if (matches != null && matches[1]) { + fileName = matches[1].replace(/['"]/g, ''); + } + } + return fileName; + } +} \ No newline at end of file diff --git a/UI/src/app/shares/appsetting.ts b/UI/src/app/shares/appsetting.ts new file mode 100644 index 0000000..aecf15d --- /dev/null +++ b/UI/src/app/shares/appsetting.ts @@ -0,0 +1,77 @@ +import { inject, Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { firstValueFrom, Observable } from 'rxjs'; +export function initializeApp_old(appInitService: AppSettingService) { + return (): Promise => { + return appInitService.loadAppSetting_p(); + } + +}; +export function initializeApp() { + return (): Promise => { + return inject(AppSettingService).loadAppSetting_p(); + } + // return () => inject(AppSettingService).loadAppSetting(); +}; + +@Injectable({ providedIn: 'root' }) +export class AppSettingService { + private _appsetting: any; + constructor(private http: HttpClient + ) { } + + public loadAppSetting_p(): Promise { + const source$ = this.http.get("config/appsetting.json") + const rs$ = firstValueFrom(source$) + .then(x => { + this._appsetting = x; + console.log("[AppSettingService] assign", x); + }); + return rs$; + } + public loadAppSetting(): Observable { + const source$ = this.http.get("config/appsetting.json") + return source$; + } + set appSetting(value: any) { + this._appsetting = value; + } + get appSetting(): any { + return this._appsetting; + } + +} +/* angular 19 new one in app.config.ts + provideAppInitializer(initializeApp()), + +/* module put this +to use it +in app.module.ts +add +1) + providers: [ + { provide : APP_INITIALIZER, multi : true, deps : [AppSettingService], + useFactory: initializeApp + }, + ] + +2) +other this one work too. +providers: [ +{ + provide : APP_INITIALIZER, + multi : true, + deps : [AppSettingService], + useFactory : (appConfigService : AppSettingService) => () => appConfigService.loadAppSetting() +} +] + +to use it calling something like this +export class TestComponent { + public test1ServiceUrl: string; + + constructor(public configService: AppConfigService) { + this.test1ServiceUrl = this.configService.appSetting.test1ServiceUrl; + } +} +*/ \ No newline at end of file diff --git a/UI/src/app/shares/error.interceptor.ts b/UI/src/app/shares/error.interceptor.ts new file mode 100644 index 0000000..5379593 --- /dev/null +++ b/UI/src/app/shares/error.interceptor.ts @@ -0,0 +1,97 @@ +import { Injectable, inject } from '@angular/core'; +import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpInterceptorFn, HttpHandlerFn } from '@angular/common/http'; +import { Observable, throwError } from 'rxjs'; +import { catchError } from 'rxjs/operators'; + +import { AuthenticationService } from '../user-services'; +//import { ToastrService } from 'ngx-toastr'; +import { NavigationExtras, Router } from '@angular/router'; +/* +@Injectable() +export class ErrorInterceptor implements HttpInterceptor { + //private authenticationService: AuthenticationService; + constructor( + private router: Router, + // private toastr: ToastrService, + ) { } + + intercept(request: HttpRequest, next: HttpHandler): Observable> { + // console.log("I am intercept to catch error status = 401...", request); + return next.handle(request).pipe(catchError(err => { + /* no use yet + if (err) { + switch(err.status) { + case 400: + if (err.error.errors) { + const modalStateErrors = []; + for (const key in err.error.errors) { + if (err.error.errors[key]) { + modalStateErrors.push(err.error.errors[key]); + } + } + throw modalStateErrors; + } else { + this.toastr.error(err.statusText, err.status); + } + break; + case 401: + this.toastr.error(err.statusText, err.status); + break; + case 404: + this.router.navigateByUrl('/not-found'); + break; + case 500: + const navigationExtras: NavigationExtras = {state:{error:err.error}}; + this.router.navigateByUrl('/server-error', navigationExtras); + break; + default: + this.toastr.error('Something unexpected went wrong'); + console.log("this error interceptor",err); + break; + } + } + */ +/* + if (err.status === 401) { + // auto logout if 401 response returned from api Unauthorized + // this.authenticationService.logout(); + //location.reload(); + const error = err.error.message || err.statusText; + console.log("After I Get Error status = 401...", error, err); + return throwError(error); + } + else { + console.log("I Get Error .", err); + return throwError(err); + } + })) +} +} + +*/ +//new one +//https://blog.ninja-squad.com/2022/11/09/angular-http-in-standalone-applications/ +//////////////// +export const ErrorInterceptor: HttpInterceptorFn = (req: HttpRequest, next: HttpHandlerFn): Observable> => { + const authenticationService = inject(AuthenticationService); + + // logger.log(`Request is on its way to ${req.url}`); + return next(req).pipe(catchError(err => { + if (err.status === 401) { + // auto logout if 401 response returned from api Unauthorized + // this.authenticationService.logout(); + //location.reload(); + const error = err.error.message || err.statusText; + // console.log("After I Get Error status = 401...", error, err); + return throwError(() => error); + } + else { + console.log("I Get Error .", err); + return throwError(() => err); + } + + })); +} + +//////////////// + diff --git a/UI/src/app/shares/httpfile.service.ts b/UI/src/app/shares/httpfile.service.ts new file mode 100644 index 0000000..aa32074 --- /dev/null +++ b/UI/src/app/shares/httpfile.service.ts @@ -0,0 +1,115 @@ +import { HttpClient, HttpErrorResponse, HttpParams } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Router } from '@angular/router'; +import { Observable, throwError } from 'rxjs'; +import { catchError } from 'rxjs/operators'; +import { ConfigureUrl } from '../models'; +import { AppSettingService } from './appsetting'; + + +@Injectable({ + providedIn: 'root' +}) +export class HttpUtility { + private baseApiUrl: string = ""; + constructor(private http: HttpClient, + private appSetting :AppSettingService, + private router: Router) { + } + /** + * getFile + * @param url + */ + public getFileAsText(url: string): Observable { + return this.http + .get(this.getApiUrl(url), { responseType: 'blob' as 'text' }) + .pipe(catchError(e => this.handleError(e))); + } + /** + * getFile + * @param url + */ + public getFile(url: string): Observable { + return this.http + .get(this.getApiUrl(url), { responseType: 'blob' as 'json' }) + .pipe(catchError(e => this.handleError(e))); + } + + /** + * handleError + * @param response + */ + private handleError(errorResponse: HttpErrorResponse) { + // in a real world app, we may send the server to some remote logging infrastructure + // instead of just logging it to the console + return throwError(errorResponse); + } + //get excel file zip file etc .. + /* + allowedToDisplay = [ + this.mimeConstant.png, + this.mimeConstant.jpeg, + this.mimeConstant.jpg, + this.mimeConstant.gif, + this.mimeConstant.txt, + this.mimeConstant.pdf + ]; + */ + /* how to use it + this.http.getFileResponse(`Person/GetPhoto?recordId=${personId}`).pipe( + catchError(err => { + this.snackBar.error(err || this.constants.error_Getting_photo); + return EMPTY; + }), + finalize(() => { + this.spinner.hide(); + })).subscribe(response => { + if (response) { + if (isDownload) { + CommonUtilities.downloadFile(response); + } else { + if (response.body && response.body.type) { + if (this.allowedToDisplay.includes(response.body.type)) { + CommonUtilities.displayFileInNewTab(response); + } else { + this.temporaryFile = response; + this.downloadWarning = true; + } + } + } + } + }); + */ + public getFileResponse(url: string, data?: any): Observable { + if (data) { + return this.http.post(this.getApiUrl(url), + data, + { responseType: 'blob', observe: 'response' }) + .pipe(catchError(e => this.handleError(e))); + + } + return this.http + .get(this.getApiUrl(url), { responseType: 'blob', observe: 'response' }) + .pipe(catchError(e => this.handleError(e))); + } + + public getFileAsPDF(url: string, data: any): Observable { + let updateURL; + if (data) { + updateURL = url + data; + } else { + updateURL = url; + } + return this.http + .get(this.getApiUrl(updateURL), { responseType: 'blob' as 'text', observe: 'response' }) + .pipe(catchError(e => this.handleError(e))); + } + /** + * getApiUrl + * @param url + */ + private getApiUrl(url:string) { + const baseUrl = this.appSetting.appSetting.baseUrl; + return baseUrl + "/"+ url; + } +} \ No newline at end of file diff --git a/UI/src/app/shares/index.ts b/UI/src/app/shares/index.ts new file mode 100644 index 0000000..31c1fea --- /dev/null +++ b/UI/src/app/shares/index.ts @@ -0,0 +1,8 @@ +export * from './utils'; +export {initializeApp, AppSettingService} from './appsetting'; +export * from './error.interceptor'; +export * from './jwt.interceptor'; +export * from './lookup.service'; +export * from './httpfile.service'; +export * from './CommonUtilities'; +export * from './timeinput'; \ No newline at end of file diff --git a/UI/src/app/shares/inputtext.txt b/UI/src/app/shares/inputtext.txt new file mode 100644 index 0000000..0415954 --- /dev/null +++ b/UI/src/app/shares/inputtext.txt @@ -0,0 +1,1554 @@ +import { CommonModule } from '@angular/common'; +import { + AfterContentInit, + booleanAttribute, + ChangeDetectionStrategy, + Component, + ContentChild, + ContentChildren, + ElementRef, + EventEmitter, + forwardRef, + HostBinding, + inject, + Injector, + Input, + NgModule, + numberAttribute, + OnChanges, + OnInit, + Output, + QueryList, + SimpleChanges, + TemplateRef, + ViewChild, + ViewEncapsulation +} from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR, NgControl } from '@angular/forms'; +import { getSelection } from '@primeuix/utils'; +import { PrimeTemplate, SharedModule } from 'primeng/api'; +import { AutoFocus } from 'primeng/autofocus'; +import { BaseComponent } from 'primeng/basecomponent'; +import { AngleDownIcon, AngleUpIcon, TimesIcon } from 'primeng/icons'; +import { InputText } from 'primeng/inputtext'; +import { Nullable } from 'primeng/ts-helpers'; +import { InputNumberInputEvent } from './inputnumber.interface'; +import { InputNumberStyle } from './style/inputnumberstyle'; + +export const INPUTNUMBER_VALUE_ACCESSOR: any = { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => InputNumber), + multi: true +}; +/** + * InputNumber is an input component to provide numerical input. + * @group Components + */ +@Component({ + selector: 'p-inputNumber, p-inputnumber, p-input-number', + standalone: true, + imports: [CommonModule, InputText, AutoFocus, TimesIcon, AngleUpIcon, AngleDownIcon, SharedModule], + template: ` + + + + + + + + + + + + + + + `, + changeDetection: ChangeDetectionStrategy.OnPush, + providers: [INPUTNUMBER_VALUE_ACCESSOR, InputNumberStyle], + encapsulation: ViewEncapsulation.None, + host: { + '[attr.data-pc-name]': "'inputnumber'", + '[attr.data-pc-section]': "'root'", + '[class]': 'hostClass' + } +}) +export class InputNumber extends BaseComponent implements OnInit, AfterContentInit, OnChanges, ControlValueAccessor { + /** + * Displays spinner buttons. + * @group Props + */ + @Input({ transform: booleanAttribute }) showButtons: boolean = false; + /** + * Whether to format the value. + * @group Props + */ + @Input({ transform: booleanAttribute }) format: boolean = true; + /** + * Layout of the buttons, valid values are "stacked" (default), "horizontal" and "vertical". + * @group Props + */ + @Input() buttonLayout: string = 'stacked'; + /** + * Identifier of the focus input to match a label defined for the component. + * @group Props + */ + @Input() inputId: string | undefined; + /** + * Style class of the component. + * @group Props + */ + @Input() styleClass: string | undefined; + /** + * Inline style of the component. + * @group Props + */ + @Input() style: { [klass: string]: any } | null | undefined; + /** + * Advisory information to display on input. + * @group Props + */ + @Input() placeholder: string | undefined; + /** + * Defines the size of the component. + * @group Props + */ + @Input() size: 'large' | 'small'; + /** + * Maximum number of character allows in the input field. + * @group Props + */ + @Input({ transform: numberAttribute }) maxlength: number | undefined; + /** + * Specifies tab order of the element. + * @group Props + */ + @Input({ transform: numberAttribute }) tabindex: number | undefined; + /** + * Title text of the input text. + * @group Props + */ + @Input() title: string | undefined; + /** + * Specifies one or more IDs in the DOM that labels the input field. + * @group Props + */ + @Input() ariaLabelledBy: string | undefined; + /** + * Specifies one or more IDs in the DOM that describes the input field. + * @group Props + */ + @Input() ariaDescribedBy: string | undefined; + /** + * Used to define a string that labels the input element. + * @group Props + */ + @Input() ariaLabel: string | undefined; + /** + * Used to indicate that user input is required on an element before a form can be submitted. + * @group Props + */ + @Input({ transform: booleanAttribute }) ariaRequired: boolean | undefined; + /** + * Name of the input field. + * @group Props + */ + @Input() name: string | undefined; + /** + * Indicates that whether the input field is required. + * @group Props + */ + @Input({ transform: booleanAttribute }) required: boolean | undefined; + /** + * Used to define a string that autocomplete attribute the current element. + * @group Props + */ + @Input() autocomplete: string | undefined; + /** + * Mininum boundary value. + * @group Props + */ + @Input({ transform: numberAttribute }) min: number | undefined; + /** + * Maximum boundary value. + * @group Props + */ + @Input({ transform: numberAttribute }) max: number | undefined; + /** + * Style class of the increment button. + * @group Props + */ + @Input() incrementButtonClass: string | undefined; + /** + * Style class of the decrement button. + * @group Props + */ + @Input() decrementButtonClass: string | undefined; + /** + * Style class of the increment button. + * @group Props + */ + @Input() incrementButtonIcon: string | undefined; + /** + * Style class of the decrement button. + * @group Props + */ + @Input() decrementButtonIcon: string | undefined; + /** + * When present, it specifies that an input field is read-only. + * @group Props + */ + @Input({ transform: booleanAttribute }) readonly: boolean = false; + /** + * Step factor to increment/decrement the value. + * @group Props + */ + @Input({ transform: numberAttribute }) step: number = 1; + /** + * Determines whether the input field is empty. + * @group Props + */ + @Input({ transform: booleanAttribute }) allowEmpty: boolean = true; + /** + * Locale to be used in formatting. + * @group Props + */ + @Input() locale: string | undefined; + /** + * The locale matching algorithm to use. Possible values are "lookup" and "best fit"; the default is "best fit". See Locale Negotiation for details. + * @group Props + */ + @Input() localeMatcher: any; + /** + * Defines the behavior of the component, valid values are "decimal" and "currency". + * @group Props + */ + @Input() mode: string | any = 'decimal'; + /** + * The currency to use in currency formatting. Possible values are the ISO 4217 currency codes, such as "USD" for the US dollar, "EUR" for the euro, or "CNY" for the Chinese RMB. There is no default value; if the style is "currency", the currency property must be provided. + * @group Props + */ + @Input() currency: string | undefined; + /** + * How to display the currency in currency formatting. Possible values are "symbol" to use a localized currency symbol such as €, ü"code" to use the ISO currency code, "name" to use a localized currency name such as "dollar"; the default is "symbol". + * @group Props + */ + @Input() currencyDisplay: string | undefined | any; + /** + * Whether to use grouping separators, such as thousands separators or thousand/lakh/crore separators. + * @group Props + */ + @Input({ transform: booleanAttribute }) useGrouping: boolean = true; + /** + * Specifies the input variant of the component. + * @group Props + */ + @Input() variant: 'filled' | 'outlined'; + /** + * The minimum number of fraction digits to use. Possible values are from 0 to 20; the default for plain number and percent formatting is 0; the default for currency formatting is the number of minor unit digits provided by the ISO 4217 currency code list (2 if the list doesn't provide that information). + * @group Props + */ + @Input({ transform: (value: unknown) => numberAttribute(value, null) }) minFractionDigits: number | undefined; + /** + * The maximum number of fraction digits to use. Possible values are from 0 to 20; the default for plain number formatting is the larger of minimumFractionDigits and 3; the default for currency formatting is the larger of minimumFractionDigits and the number of minor unit digits provided by the ISO 4217 currency code list (2 if the list doesn't provide that information). + * @group Props + */ + @Input({ transform: (value: unknown) => numberAttribute(value, null) }) maxFractionDigits: number | undefined; + /** + * Text to display before the value. + * @group Props + */ + @Input() prefix: string | undefined; + /** + * Text to display after the value. + * @group Props + */ + @Input() suffix: string | undefined; + /** + * Inline style of the input field. + * @group Props + */ + @Input() inputStyle: any; + /** + * Style class of the input field. + * @group Props + */ + @Input() inputStyleClass: string | undefined; + /** + * When enabled, a clear icon is displayed to clear the value. + * @group Props + */ + @Input({ transform: booleanAttribute }) showClear: boolean = false; + /** + * When present, it specifies that the component should automatically get focus on load. + * @group Props + */ + @Input({ transform: booleanAttribute }) autofocus: boolean | undefined; + /** + * When present, it specifies that the element should be disabled. + * @group Props + */ + @Input() get disabled(): boolean | undefined { + return this._disabled; + } + set disabled(disabled: boolean | undefined) { + if (disabled) this.focused = false; + + this._disabled = disabled; + + if (this.timer) this.clearTimer(); + } + /** + * Spans 100% width of the container when enabled. + * @group Props + */ + @Input({ transform: booleanAttribute }) fluid: boolean = false; + /** + * Callback to invoke on input. + * @param {InputNumberInputEvent} event - Custom input event. + * @group Emits + */ + @Output() onInput: EventEmitter = new EventEmitter(); + /** + * Callback to invoke when the component receives focus. + * @param {Event} event - Browser event. + * @group Emits + */ + @Output() onFocus: EventEmitter = new EventEmitter(); + /** + * Callback to invoke when the component loses focus. + * @param {Event} event - Browser event. + * @group Emits + */ + @Output() onBlur: EventEmitter = new EventEmitter(); + /** + * Callback to invoke on input key press. + * @param {KeyboardEvent} event - Keyboard event. + * @group Emits + */ + @Output() onKeyDown: EventEmitter = new EventEmitter(); + /** + * Callback to invoke when clear token is clicked. + * @group Emits + */ + @Output() onClear: EventEmitter = new EventEmitter(); + + /** + * Template of the clear icon. + * @group Templates + */ + @ContentChild('clearicon', { descendants: false }) clearIconTemplate: Nullable>; + /** + * Template of the increment button icon. + * @group Templates + */ + @ContentChild('incrementbuttonicon', { descendants: false }) incrementButtonIconTemplate: Nullable>; + + /** + * Template of the decrement button icon. + * @group Templates + */ + @ContentChild('decrementbuttonicon', { descendants: false }) decrementButtonIconTemplate: Nullable>; + + @ContentChildren(PrimeTemplate) templates!: QueryList; + + @ViewChild('input') input!: ElementRef; + + _clearIconTemplate: TemplateRef | undefined; + + _incrementButtonIconTemplate: TemplateRef | undefined; + + _decrementButtonIconTemplate: TemplateRef | undefined; + + value: Nullable; + + onModelChange: Function = () => {}; + + onModelTouched: Function = () => {}; + + focused: Nullable; + + initialized: Nullable; + + groupChar: string = ''; + + prefixChar: string = ''; + + suffixChar: string = ''; + + isSpecialChar: Nullable; + + timer: any; + + lastValue: Nullable; + + _numeral: any; + + numberFormat: any; + + _decimal: any; + + _decimalChar: string; + + _group: any; + + _minusSign: any; + + _currency: Nullable; + + _prefix: Nullable; + + _suffix: Nullable; + + _index: number | any; + + _disabled: boolean | undefined; + + _componentStyle = inject(InputNumberStyle); + + private ngControl: NgControl | null = null; + + get _rootClass() { + return this._componentStyle.classes.root({ instance: this }); + } + + get hasFluid() { + const nativeElement = this.el.nativeElement; + const fluidComponent = nativeElement.closest('p-fluid'); + return this.fluid || !!fluidComponent; + } + + get _incrementButtonClass() { + return this._componentStyle.classes.incrementButton({ instance: this }); + } + + get _decrementButtonClass() { + return this._componentStyle.classes.decrementButton({ instance: this }); + } + + constructor(public readonly injector: Injector) { + super(); + } + + ngOnChanges(simpleChange: SimpleChanges) { + super.ngOnChanges(simpleChange); + const props = ['locale', 'localeMatcher', 'mode', 'currency', 'currencyDisplay', 'useGrouping', 'minFractionDigits', 'maxFractionDigits', 'prefix', 'suffix']; + if (props.some((p) => !!simpleChange[p])) { + this.updateConstructParser(); + } + } + + get hostClass(): string { + return [ + 'p-inputnumber p-component p-inputwrapper', + this.styleClass, + this.filled || this.allowEmpty === false ? 'p-inputwrapper-filled' : '', + this.focused ? 'p-inputwrapper-focus' : '', + this.showButtons && this.buttonLayout === 'stacked' ? 'p-inputnumber-stacked' : '', + this.showButtons && this.buttonLayout === 'horizontal' ? 'p-inputnumber-horizontal' : '', + this.showButtons && this.buttonLayout === 'vertical' ? 'p-inputnumber-vertical' : '', + this.hasFluid ? 'p-inputnumber-fluid' : '' + ] + .filter((cls) => !!cls) + .join(' '); + } + + @HostBinding('style') get hostStyle(): any { + return this.style; + } + + ngOnInit() { + super.ngOnInit(); + + this.ngControl = this.injector.get(NgControl, null, { optional: true }); + + this.constructParser(); + + this.initialized = true; + } + + ngAfterContentInit() { + this.templates.forEach((item) => { + switch (item.getType()) { + case 'clearicon': + this._clearIconTemplate = item.template; + break; + + case 'incrementbuttonicon': + this._incrementButtonIconTemplate = item.template; + break; + + case 'decrementbuttonicon': + this._decrementButtonIconTemplate = item.template; + break; + } + }); + } + + getOptions() { + return { + localeMatcher: this.localeMatcher, + style: this.mode, + currency: this.currency, + currencyDisplay: this.currencyDisplay, + useGrouping: this.useGrouping, + minimumFractionDigits: this.minFractionDigits ?? undefined, + maximumFractionDigits: this.maxFractionDigits ?? undefined + }; + } + + constructParser() { + this.numberFormat = new Intl.NumberFormat(this.locale, this.getOptions()); + const numerals = [...new Intl.NumberFormat(this.locale, { useGrouping: false }).format(9876543210)].reverse(); + const index = new Map(numerals.map((d, i) => [d, i])); + this._numeral = new RegExp(`[${numerals.join('')}]`, 'g'); + this._group = this.getGroupingExpression(); + this._minusSign = this.getMinusSignExpression(); + this._currency = this.getCurrencyExpression(); + this._decimal = this.getDecimalExpression(); + this._decimalChar = this.getDecimalChar(); + this._suffix = this.getSuffixExpression(); + this._prefix = this.getPrefixExpression(); + this._index = (d: any) => index.get(d); + } + + updateConstructParser() { + if (this.initialized) { + this.constructParser(); + } + } + + escapeRegExp(text: string): string { + return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); + } + + getDecimalExpression(): RegExp { + const decimalChar = this.getDecimalChar(); + return new RegExp(`[${decimalChar}]`, 'g'); + } + getDecimalChar(): string { + const formatter = new Intl.NumberFormat(this.locale, { ...this.getOptions(), useGrouping: false }); + return formatter + .format(1.1) + .replace(this._currency as RegExp | string, '') + .trim() + .replace(this._numeral, ''); + } + + getGroupingExpression(): RegExp { + const formatter = new Intl.NumberFormat(this.locale, { useGrouping: true }); + this.groupChar = formatter.format(1000000).trim().replace(this._numeral, '').charAt(0); + return new RegExp(`[${this.groupChar}]`, 'g'); + } + + getMinusSignExpression(): RegExp { + const formatter = new Intl.NumberFormat(this.locale, { useGrouping: false }); + return new RegExp(`[${formatter.format(-1).trim().replace(this._numeral, '')}]`, 'g'); + } + + getCurrencyExpression(): RegExp { + if (this.currency) { + const formatter = new Intl.NumberFormat(this.locale, { + style: 'currency', + currency: this.currency, + currencyDisplay: this.currencyDisplay, + minimumFractionDigits: 0, + maximumFractionDigits: 0 + }); + return new RegExp(`[${formatter.format(1).replace(/\s/g, '').replace(this._numeral, '').replace(this._group, '')}]`, 'g'); + } + + return new RegExp(`[]`, 'g'); + } + + getPrefixExpression(): RegExp { + if (this.prefix) { + this.prefixChar = this.prefix; + } else { + const formatter = new Intl.NumberFormat(this.locale, { + style: this.mode, + currency: this.currency, + currencyDisplay: this.currencyDisplay + }); + this.prefixChar = formatter.format(1).split('1')[0]; + } + + return new RegExp(`${this.escapeRegExp(this.prefixChar || '')}`, 'g'); + } + + getSuffixExpression(): RegExp { + if (this.suffix) { + this.suffixChar = this.suffix; + } else { + const formatter = new Intl.NumberFormat(this.locale, { + style: this.mode, + currency: this.currency, + currencyDisplay: this.currencyDisplay, + minimumFractionDigits: 0, + maximumFractionDigits: 0 + }); + this.suffixChar = formatter.format(1).split('1')[1]; + } + + return new RegExp(`${this.escapeRegExp(this.suffixChar || '')}`, 'g'); + } + + formatValue(value: any) { + if (value != null) { + if (value === '-') { + // Minus sign + return value; + } + + if (this.format) { + let formatter = new Intl.NumberFormat(this.locale, this.getOptions()); + let formattedValue = formatter.format(value); + + if (this.prefix && value != this.prefix) { + formattedValue = this.prefix + formattedValue; + } + + if (this.suffix && value != this.suffix) { + formattedValue = formattedValue + this.suffix; + } + + return formattedValue; + } + + return value.toString(); + } + + return ''; + } + + parseValue(text: any) { + const suffixRegex = new RegExp(this._suffix, ''); + const prefixRegex = new RegExp(this._prefix, ''); + const currencyRegex = new RegExp(this._currency, ''); + + let filteredText = text + .replace(suffixRegex, '') + .replace(prefixRegex, '') + .trim() + .replace(/\s/g, '') + .replace(currencyRegex, '') + .replace(this._group, '') + .replace(this._minusSign, '-') + .replace(this._decimal, '.') + .replace(this._numeral, this._index); + + if (filteredText) { + if (filteredText === '-') + // Minus sign + return filteredText; + + let parsedValue = +filteredText; + return isNaN(parsedValue) ? null : parsedValue; + } + + return null; + } + + repeat(event: Event, interval: number | null, dir: number) { + if (this.readonly) { + return; + } + + let i = interval || 500; + + this.clearTimer(); + this.timer = setTimeout(() => { + this.repeat(event, 40, dir); + }, i); + + this.spin(event, dir); + } + + spin(event: Event, dir: number) { + let step = this.step * dir; + let currentValue = this.parseValue(this.input?.nativeElement.value) || 0; + let newValue = this.validateValue((currentValue as number) + step); + if (this.maxlength && this.maxlength < this.formatValue(newValue).length) { + return; + } + this.updateInput(newValue, null, 'spin', null); + this.updateModel(event, newValue); + + this.handleOnInput(event, currentValue, newValue); + } + + clear() { + this.value = null; + this.onModelChange(this.value); + this.onClear.emit(); + } + + onUpButtonMouseDown(event: MouseEvent) { + if (event.button === 2) { + this.clearTimer(); + return; + } + + if (!this.disabled) { + this.input?.nativeElement.focus(); + this.repeat(event, null, 1); + event.preventDefault(); + } + } + + onUpButtonMouseUp() { + if (!this.disabled) { + this.clearTimer(); + } + } + + onUpButtonMouseLeave() { + if (!this.disabled) { + this.clearTimer(); + } + } + + onUpButtonKeyDown(event: KeyboardEvent) { + if (event.keyCode === 32 || event.keyCode === 13) { + this.repeat(event, null, 1); + } + } + + onUpButtonKeyUp() { + if (!this.disabled) { + this.clearTimer(); + } + } + + onDownButtonMouseDown(event: MouseEvent) { + if (event.button === 2) { + this.clearTimer(); + return; + } + if (!this.disabled) { + this.input?.nativeElement.focus(); + this.repeat(event, null, -1); + event.preventDefault(); + } + } + + onDownButtonMouseUp() { + if (!this.disabled) { + this.clearTimer(); + } + } + + onDownButtonMouseLeave() { + if (!this.disabled) { + this.clearTimer(); + } + } + + onDownButtonKeyUp() { + if (!this.disabled) { + this.clearTimer(); + } + } + + onDownButtonKeyDown(event: KeyboardEvent) { + if (event.keyCode === 32 || event.keyCode === 13) { + this.repeat(event, null, -1); + } + } + + onUserInput(event: Event) { + if (this.readonly) { + return; + } + + if (this.isSpecialChar) { + (event.target as HTMLInputElement).value = this.lastValue as string; + } + this.isSpecialChar = false; + } + + onInputKeyDown(event: KeyboardEvent) { + if (this.readonly) { + return; + } + + this.lastValue = (event.target as HTMLInputElement).value; + if ((event as KeyboardEvent).shiftKey || (event as KeyboardEvent).altKey) { + this.isSpecialChar = true; + return; + } + + let selectionStart = (event.target as HTMLInputElement).selectionStart as number; + let selectionEnd = (event.target as HTMLInputElement).selectionEnd as number; + let inputValue = (event.target as HTMLInputElement).value as string; + let newValueStr = null; + + if (event.altKey) { + event.preventDefault(); + } + + switch (event.key) { + case 'ArrowUp': + this.spin(event, 1); + event.preventDefault(); + break; + + case 'ArrowDown': + this.spin(event, -1); + event.preventDefault(); + break; + + case 'ArrowLeft': + for (let index = selectionStart; index <= inputValue.length; index++) { + const previousCharIndex = index === 0 ? 0 : index - 1; + if (this.isNumeralChar(inputValue.charAt(previousCharIndex))) { + this.input.nativeElement.setSelectionRange(index, index); + break; + } + } + break; + + case 'ArrowRight': + for (let index = selectionEnd; index >= 0; index--) { + if (this.isNumeralChar(inputValue.charAt(index))) { + this.input.nativeElement.setSelectionRange(index, index); + break; + } + } + break; + + case 'Tab': + case 'Enter': + newValueStr = this.validateValue(this.parseValue(this.input.nativeElement.value)); + this.input.nativeElement.value = this.formatValue(newValueStr); + this.input.nativeElement.setAttribute('aria-valuenow', newValueStr); + this.updateModel(event, newValueStr); + break; + + case 'Backspace': { + event.preventDefault(); + + if (selectionStart === selectionEnd) { + if ((selectionStart == 1 && this.prefix) || (selectionStart == inputValue.length && this.suffix)) { + break; + } + + const deleteChar = inputValue.charAt(selectionStart - 1); + const { decimalCharIndex, decimalCharIndexWithoutPrefix } = this.getDecimalCharIndexes(inputValue); + + if (this.isNumeralChar(deleteChar)) { + const decimalLength = this.getDecimalLength(inputValue); + + if (this._group.test(deleteChar)) { + this._group.lastIndex = 0; + newValueStr = inputValue.slice(0, selectionStart - 2) + inputValue.slice(selectionStart - 1); + } else if (this._decimal.test(deleteChar)) { + this._decimal.lastIndex = 0; + + if (decimalLength) { + this.input?.nativeElement.setSelectionRange(selectionStart - 1, selectionStart - 1); + } else { + newValueStr = inputValue.slice(0, selectionStart - 1) + inputValue.slice(selectionStart); + } + } else if (decimalCharIndex > 0 && selectionStart > decimalCharIndex) { + const insertedText = this.isDecimalMode() && (this.minFractionDigits || 0) < decimalLength ? '' : '0'; + newValueStr = inputValue.slice(0, selectionStart - 1) + insertedText + inputValue.slice(selectionStart); + } else if (decimalCharIndexWithoutPrefix === 1) { + newValueStr = inputValue.slice(0, selectionStart - 1) + '0' + inputValue.slice(selectionStart); + newValueStr = (this.parseValue(newValueStr) as number) > 0 ? newValueStr : ''; + } else { + newValueStr = inputValue.slice(0, selectionStart - 1) + inputValue.slice(selectionStart); + } + } else if (this.mode === 'currency' && deleteChar.search(this._currency) != -1) { + newValueStr = inputValue.slice(1); + } + + this.updateValue(event, newValueStr, null, 'delete-single'); + } else { + newValueStr = this.deleteRange(inputValue, selectionStart, selectionEnd); + this.updateValue(event, newValueStr, null, 'delete-range'); + } + + break; + } + + case 'Delete': + event.preventDefault(); + + if (selectionStart === selectionEnd) { + if ((selectionStart == 0 && this.prefix) || (selectionStart == inputValue.length - 1 && this.suffix)) { + break; + } + const deleteChar = inputValue.charAt(selectionStart); + const { decimalCharIndex, decimalCharIndexWithoutPrefix } = this.getDecimalCharIndexes(inputValue); + + if (this.isNumeralChar(deleteChar)) { + const decimalLength = this.getDecimalLength(inputValue); + + if (this._group.test(deleteChar)) { + this._group.lastIndex = 0; + newValueStr = inputValue.slice(0, selectionStart) + inputValue.slice(selectionStart + 2); + } else if (this._decimal.test(deleteChar)) { + this._decimal.lastIndex = 0; + + if (decimalLength) { + this.input?.nativeElement.setSelectionRange(selectionStart + 1, selectionStart + 1); + } else { + newValueStr = inputValue.slice(0, selectionStart) + inputValue.slice(selectionStart + 1); + } + } else if (decimalCharIndex > 0 && selectionStart > decimalCharIndex) { + const insertedText = this.isDecimalMode() && (this.minFractionDigits || 0) < decimalLength ? '' : '0'; + newValueStr = inputValue.slice(0, selectionStart) + insertedText + inputValue.slice(selectionStart + 1); + } else if (decimalCharIndexWithoutPrefix === 1) { + newValueStr = inputValue.slice(0, selectionStart) + '0' + inputValue.slice(selectionStart + 1); + newValueStr = (this.parseValue(newValueStr) as number) > 0 ? newValueStr : ''; + } else { + newValueStr = inputValue.slice(0, selectionStart) + inputValue.slice(selectionStart + 1); + } + } + + this.updateValue(event, newValueStr as string, null, 'delete-back-single'); + } else { + newValueStr = this.deleteRange(inputValue, selectionStart, selectionEnd); + this.updateValue(event, newValueStr, null, 'delete-range'); + } + break; + + case 'Home': + if (this.min) { + this.updateModel(event, this.min); + event.preventDefault(); + } + break; + + case 'End': + if (this.max) { + this.updateModel(event, this.max); + event.preventDefault(); + } + break; + + default: + break; + } + + this.onKeyDown.emit(event); + } + + onInputKeyPress(event: KeyboardEvent) { + if (this.readonly) { + return; + } + + let code = event.which || event.keyCode; + let char = String.fromCharCode(code); + let isDecimalSign = this.isDecimalSign(char); + const isMinusSign = this.isMinusSign(char); + + if (code != 13) { + event.preventDefault(); + } + if (!isDecimalSign && event.code === 'NumpadDecimal') { + isDecimalSign = true; + char = this._decimalChar; + code = char.charCodeAt(0); + } + const { value, selectionStart, selectionEnd } = this.input.nativeElement; + const newValue = this.parseValue(value + char); + const newValueStr = newValue != null ? newValue.toString() : ''; + const selectedValue = value.substring(selectionStart, selectionEnd); + const selectedValueParsed = this.parseValue(selectedValue); + const selectedValueStr = selectedValueParsed != null ? selectedValueParsed.toString() : ''; + + if (selectionStart !== selectionEnd && selectedValueStr.length > 0) { + this.insert(event, char, { isDecimalSign, isMinusSign }); + return; + } + + if (this.maxlength && newValueStr.length > this.maxlength) { + return; + } + + if ((48 <= code && code <= 57) || isMinusSign || isDecimalSign) { + this.insert(event, char, { isDecimalSign, isMinusSign }); + } + } + + onPaste(event: ClipboardEvent) { + if (!this.disabled && !this.readonly) { + event.preventDefault(); + let data = (event.clipboardData || (this.document as any).defaultView['clipboardData']).getData('Text'); + if (data) { + if (this.maxlength) { + data = data.toString().substring(0, this.maxlength); + } + + let filteredData = this.parseValue(data); + if (filteredData != null) { + this.insert(event, filteredData.toString()); + } + } + } + } + + allowMinusSign() { + return this.min == null || this.min < 0; + } + + isMinusSign(char: string) { + if (this._minusSign.test(char) || char === '-') { + this._minusSign.lastIndex = 0; + return true; + } + + return false; + } + + isDecimalSign(char: string) { + if (this._decimal.test(char)) { + this._decimal.lastIndex = 0; + return true; + } + + return false; + } + + isDecimalMode() { + return this.mode === 'decimal'; + } + + getDecimalCharIndexes(val: string) { + let decimalCharIndex = val.search(this._decimal); + this._decimal.lastIndex = 0; + + const filteredVal = val + .replace(this._prefix as RegExp, '') + .trim() + .replace(/\s/g, '') + .replace(this._currency as RegExp, ''); + const decimalCharIndexWithoutPrefix = filteredVal.search(this._decimal); + this._decimal.lastIndex = 0; + + return { decimalCharIndex, decimalCharIndexWithoutPrefix }; + } + + getCharIndexes(val: string) { + const decimalCharIndex = val.search(this._decimal); + this._decimal.lastIndex = 0; + const minusCharIndex = val.search(this._minusSign); + this._minusSign.lastIndex = 0; + const suffixCharIndex = val.search(this._suffix as RegExp); + (this._suffix as RegExp).lastIndex = 0; + const currencyCharIndex = val.search(this._currency as RegExp); + (this._currency as RegExp).lastIndex = 0; + + return { decimalCharIndex, minusCharIndex, suffixCharIndex, currencyCharIndex }; + } + + insert(event: Event, text: string, sign = { isDecimalSign: false, isMinusSign: false }) { + const minusCharIndexOnText = text.search(this._minusSign); + this._minusSign.lastIndex = 0; + if (!this.allowMinusSign() && minusCharIndexOnText !== -1) { + return; + } + + let selectionStart = this.input?.nativeElement.selectionStart; + let selectionEnd = this.input?.nativeElement.selectionEnd; + let inputValue = this.input?.nativeElement.value.trim(); + const { decimalCharIndex, minusCharIndex, suffixCharIndex, currencyCharIndex } = this.getCharIndexes(inputValue); + let newValueStr; + + if (sign.isMinusSign) { + if (selectionStart === 0) { + newValueStr = inputValue; + if (minusCharIndex === -1 || selectionEnd !== 0) { + newValueStr = this.insertText(inputValue, text, 0, selectionEnd); + } + + this.updateValue(event, newValueStr, text, 'insert'); + } + } else if (sign.isDecimalSign) { + if (decimalCharIndex > 0 && selectionStart === decimalCharIndex) { + this.updateValue(event, inputValue, text, 'insert'); + } else if (decimalCharIndex > selectionStart && decimalCharIndex < selectionEnd) { + newValueStr = this.insertText(inputValue, text, selectionStart, selectionEnd); + this.updateValue(event, newValueStr, text, 'insert'); + } else if (decimalCharIndex === -1 && this.maxFractionDigits) { + newValueStr = this.insertText(inputValue, text, selectionStart, selectionEnd); + this.updateValue(event, newValueStr, text, 'insert'); + } + } else { + const maxFractionDigits = this.numberFormat.resolvedOptions().maximumFractionDigits; + const operation = selectionStart !== selectionEnd ? 'range-insert' : 'insert'; + + if (decimalCharIndex > 0 && selectionStart > decimalCharIndex) { + if (selectionStart + text.length - (decimalCharIndex + 1) <= maxFractionDigits) { + const charIndex = currencyCharIndex >= selectionStart ? currencyCharIndex - 1 : suffixCharIndex >= selectionStart ? suffixCharIndex : inputValue.length; + + newValueStr = inputValue.slice(0, selectionStart) + text + inputValue.slice(selectionStart + text.length, charIndex) + inputValue.slice(charIndex); + this.updateValue(event, newValueStr, text, operation); + } + } else { + newValueStr = this.insertText(inputValue, text, selectionStart, selectionEnd); + this.updateValue(event, newValueStr, text, operation); + } + } + } + + insertText(value: string, text: string, start: number, end: number) { + let textSplit = text === '.' ? text : text.split('.'); + + if (textSplit.length === 2) { + const decimalCharIndex = value.slice(start, end).search(this._decimal); + this._decimal.lastIndex = 0; + return decimalCharIndex > 0 ? value.slice(0, start) + this.formatValue(text) + value.slice(end) : value || this.formatValue(text); + } else if (end - start === value.length) { + return this.formatValue(text); + } else if (start === 0) { + return text + value.slice(end); + } else if (end === value.length) { + return value.slice(0, start) + text; + } else { + return value.slice(0, start) + text + value.slice(end); + } + } + + deleteRange(value: string, start: number, end: number) { + let newValueStr; + + if (end - start === value.length) newValueStr = ''; + else if (start === 0) newValueStr = value.slice(end); + else if (end === value.length) newValueStr = value.slice(0, start); + else newValueStr = value.slice(0, start) + value.slice(end); + + return newValueStr; + } + + initCursor() { + let selectionStart = this.input?.nativeElement.selectionStart; + let selectionEnd = this.input?.nativeElement.selectionEnd; + let inputValue = this.input?.nativeElement.value; + let valueLength = inputValue.length; + let index = null; + + // remove prefix + let prefixLength = (this.prefixChar || '').length; + inputValue = inputValue.replace(this._prefix, ''); + + // Will allow selecting whole prefix. But not a part of it. + // Negative values will trigger clauses after this to fix the cursor position. + if (selectionStart === selectionEnd || selectionStart !== 0 || selectionEnd < prefixLength) { + selectionStart -= prefixLength; + } + + let char = inputValue.charAt(selectionStart); + if (this.isNumeralChar(char)) { + return selectionStart + prefixLength; + } + + //left + let i = selectionStart - 1; + while (i >= 0) { + char = inputValue.charAt(i); + if (this.isNumeralChar(char)) { + index = i + prefixLength; + break; + } else { + i--; + } + } + + if (index !== null) { + this.input?.nativeElement.setSelectionRange(index + 1, index + 1); + } else { + i = selectionStart; + while (i < valueLength) { + char = inputValue.charAt(i); + if (this.isNumeralChar(char)) { + index = i + prefixLength; + break; + } else { + i++; + } + } + + if (index !== null) { + this.input?.nativeElement.setSelectionRange(index, index); + } + } + + return index || 0; + } + + onInputClick() { + const currentValue = this.input?.nativeElement.value; + + if (!this.readonly && currentValue !== getSelection()) { + this.initCursor(); + } + } + + isNumeralChar(char: string) { + if (char.length === 1 && (this._numeral.test(char) || this._decimal.test(char) || this._group.test(char) || this._minusSign.test(char))) { + this.resetRegex(); + return true; + } + + return false; + } + + resetRegex() { + this._numeral.lastIndex = 0; + this._decimal.lastIndex = 0; + this._group.lastIndex = 0; + this._minusSign.lastIndex = 0; + } + + updateValue(event: Event, valueStr: Nullable, insertedValueStr: Nullable, operation: Nullable) { + let currentValue = this.input?.nativeElement.value; + let newValue = null; + + if (valueStr != null) { + newValue = this.parseValue(valueStr); + newValue = !newValue && !this.allowEmpty ? 0 : newValue; + this.updateInput(newValue, insertedValueStr, operation, valueStr); + + this.handleOnInput(event, currentValue, newValue); + } + } + + handleOnInput(event: Event, currentValue: string, newValue: any) { + if (this.isValueChanged(currentValue, newValue)) { + (this.input as ElementRef).nativeElement.value = this.formatValue(newValue); + this.input?.nativeElement.setAttribute('aria-valuenow', newValue); + this.updateModel(event, newValue); + this.onInput.emit({ originalEvent: event, value: newValue, formattedValue: currentValue }); + } + } + + isValueChanged(currentValue: string, newValue: string) { + if (newValue === null && currentValue !== null) { + return true; + } + + if (newValue != null) { + let parsedCurrentValue = typeof currentValue === 'string' ? this.parseValue(currentValue) : currentValue; + return newValue !== parsedCurrentValue; + } + + return false; + } + + validateValue(value: number | string) { + if (value === '-' || value == null) { + return null; + } + + if (this.min != null && (value as number) < this.min) { + return this.min; + } + + if (this.max != null && (value as number) > this.max) { + return this.max; + } + + return value; + } + + updateInput(value: any, insertedValueStr: Nullable, operation: Nullable, valueStr: Nullable) { + insertedValueStr = insertedValueStr || ''; + + let inputValue = this.input?.nativeElement.value; + let newValue = this.formatValue(value); + let currentLength = inputValue.length; + + if (newValue !== valueStr) { + newValue = this.concatValues(newValue, valueStr as string); + } + + if (currentLength === 0) { + this.input.nativeElement.value = newValue; + this.input.nativeElement.setSelectionRange(0, 0); + const index = this.initCursor(); + const selectionEnd = index + insertedValueStr.length; + this.input.nativeElement.setSelectionRange(selectionEnd, selectionEnd); + } else { + let selectionStart = this.input.nativeElement.selectionStart; + let selectionEnd = this.input.nativeElement.selectionEnd; + + if (this.maxlength && newValue.length > this.maxlength) { + newValue = newValue.slice(0, this.maxlength); + selectionStart = Math.min(selectionStart, this.maxlength); + selectionEnd = Math.min(selectionEnd, this.maxlength); + } + + if (this.maxlength && this.maxlength < newValue.length) { + return; + } + + this.input.nativeElement.value = newValue; + let newLength = newValue.length; + + if (operation === 'range-insert') { + const startValue = this.parseValue((inputValue || '').slice(0, selectionStart)); + const startValueStr = startValue !== null ? startValue.toString() : ''; + const startExpr = startValueStr.split('').join(`(${this.groupChar})?`); + const sRegex = new RegExp(startExpr, 'g'); + sRegex.test(newValue); + + const tExpr = insertedValueStr.split('').join(`(${this.groupChar})?`); + const tRegex = new RegExp(tExpr, 'g'); + tRegex.test(newValue.slice(sRegex.lastIndex)); + + selectionEnd = sRegex.lastIndex + tRegex.lastIndex; + this.input.nativeElement.setSelectionRange(selectionEnd, selectionEnd); + } else if (newLength === currentLength) { + if (operation === 'insert' || operation === 'delete-back-single') this.input.nativeElement.setSelectionRange(selectionEnd + 1, selectionEnd + 1); + else if (operation === 'delete-single') this.input.nativeElement.setSelectionRange(selectionEnd - 1, selectionEnd - 1); + else if (operation === 'delete-range' || operation === 'spin') this.input.nativeElement.setSelectionRange(selectionEnd, selectionEnd); + } else if (operation === 'delete-back-single') { + let prevChar = inputValue.charAt(selectionEnd - 1); + let nextChar = inputValue.charAt(selectionEnd); + let diff = currentLength - newLength; + let isGroupChar = this._group.test(nextChar); + + if (isGroupChar && diff === 1) { + selectionEnd += 1; + } else if (!isGroupChar && this.isNumeralChar(prevChar)) { + selectionEnd += -1 * diff + 1; + } + + this._group.lastIndex = 0; + this.input.nativeElement.setSelectionRange(selectionEnd, selectionEnd); + } else if (inputValue === '-' && operation === 'insert') { + this.input.nativeElement.setSelectionRange(0, 0); + const index = this.initCursor(); + const selectionEnd = index + insertedValueStr.length + 1; + this.input.nativeElement.setSelectionRange(selectionEnd, selectionEnd); + } else { + selectionEnd = selectionEnd + (newLength - currentLength); + this.input.nativeElement.setSelectionRange(selectionEnd, selectionEnd); + } + } + + this.input.nativeElement.setAttribute('aria-valuenow', value); + } + + concatValues(val1: string, val2: string) { + if (val1 && val2) { + let decimalCharIndex = val2.search(this._decimal); + this._decimal.lastIndex = 0; + + if (this.suffixChar) { + return decimalCharIndex !== -1 ? val1.replace(this.suffixChar, '').split(this._decimal)[0] + val2.replace(this.suffixChar, '').slice(decimalCharIndex) + this.suffixChar : val1; + } else { + return decimalCharIndex !== -1 ? val1.split(this._decimal)[0] + val2.slice(decimalCharIndex) : val1; + } + } + return val1; + } + + getDecimalLength(value: string) { + if (value) { + const valueSplit = value.split(this._decimal); + + if (valueSplit.length === 2) { + return valueSplit[1] + .replace(this._suffix as RegExp, '') + .trim() + .replace(/\s/g, '') + .replace(this._currency as RegExp, '').length; + } + } + + return 0; + } + + onInputFocus(event: Event) { + this.focused = true; + this.onFocus.emit(event); + } + + onInputBlur(event: Event) { + this.focused = false; + + const newValueNumber = this.validateValue(this.parseValue(this.input.nativeElement.value)); + const newValueString = newValueNumber?.toString(); + this.input.nativeElement.value = this.formatValue(newValueString); + this.input.nativeElement.setAttribute('aria-valuenow', newValueString); + this.updateModel(event, newValueNumber); + this.onBlur.emit(event); + } + + formattedValue() { + const val = !this.value && !this.allowEmpty ? 0 : this.value; + return this.formatValue(val); + } + + updateModel(event: Event, value: any) { + const isBlurUpdateOnMode = this.ngControl?.control?.updateOn === 'blur'; + + if (this.value !== value) { + this.value = value; + + if (!(isBlurUpdateOnMode && this.focused)) { + this.onModelChange(value); + } + } else if (isBlurUpdateOnMode) { + this.onModelChange(value); + } + this.onModelTouched(); + } + + writeValue(value: any): void { + this.value = value ? Number(value) : value; + this.cd.markForCheck(); + } + + registerOnChange(fn: Function): void { + this.onModelChange = fn; + } + + registerOnTouched(fn: Function): void { + this.onModelTouched = fn; + } + + setDisabledState(val: boolean): void { + this.disabled = val; + this.cd.markForCheck(); + } + + get filled() { + return this.value != null && this.value.toString().length > 0; + } + + clearTimer() { + if (this.timer) { + clearInterval(this.timer); + } + } +} + +@NgModule({ + imports: [InputNumber, SharedModule], + exports: [InputNumber, SharedModule] +}) +export class InputNumberModule {} \ No newline at end of file diff --git a/UI/src/app/shares/jwt.interceptor.ts b/UI/src/app/shares/jwt.interceptor.ts new file mode 100644 index 0000000..72e3195 --- /dev/null +++ b/UI/src/app/shares/jwt.interceptor.ts @@ -0,0 +1,47 @@ +import { Injectable } from '@angular/core'; +import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpInterceptorFn, HttpHandlerFn } from '@angular/common/http'; +import { Observable } from 'rxjs'; +import { Utils } from './utils'; +/* +@Injectable() +export class JwtInterceptor implements HttpInterceptor { + intercept(request: HttpRequest, next: HttpHandler): Observable> { + // add authorization header with jwt token if available + const currentUser = Utils.getCurrentUser();//localStorage.getItem('currentUser')!; + console.log('I am interceptor debug for currentUser obj', currentUser); + if (currentUser && currentUser.token) { + //console.log('I am interceptor add authorization header with jwt token'); + request = request.clone({ + setHeaders: { + Authorization: `Bearer ${currentUser.token}` + } + }); + } + return next.handle(request); + } +} + +/* +new one +//////////////// +*/ +export const JwtInterceptor: HttpInterceptorFn = (request: HttpRequest, next: HttpHandlerFn): Observable> => { + //const logger = inject(Logger); + //logger.log(`Request is on its way to ${req.url}`); + const currentUser = Utils.getCurrentUser();//localStorage.getItem('currentUser')!; + // console.log('I am interceptor debug for currentUser obj', currentUser); + if (currentUser && currentUser.token && currentUser.token != "") { + // console.log('I am interceptor add authorization header with jwt token'); + request = request.clone({ + setHeaders: { + Authorization: `Bearer ${currentUser.token}` + } + }); + } + return next(request); +} + +//////////////// +/* +providers: [provideHttpClient(withInterceptors([loggerInterceptor]))] +*/ \ No newline at end of file diff --git a/UI/src/app/shares/lookup.service.ts b/UI/src/app/shares/lookup.service.ts new file mode 100644 index 0000000..c6bfc3d --- /dev/null +++ b/UI/src/app/shares/lookup.service.ts @@ -0,0 +1,136 @@ +import { Injectable, Inject , Output} from '@angular/core'; +import { HttpClient, HttpParams } from '@angular/common/http'; +import { map } from 'rxjs/operators'; +import { Observable, of } from 'rxjs'; +import { ConfigureUrl,Lookup,LookupEdit,ResultModel + } from '../models'; +import { Utils,AppSettingService } from '../shares'; + + +@Injectable({ providedIn: 'root' }) +export class LookupService { + msg: string = "[lookupService] "; + _lookupByStatus:any; + _staffList:any; + _clientList:any; + _jobList:any; + constructor(private http: HttpClient, + private appSetting :AppSettingService + ) { + // this default when load look for previous login information + // if no need then comment out downthere + this.setAllNull(); + } + setAllNull() :void { + // this._lhdList = undefined; + this._lookupByStatus = {}; + this._staffList = null; + this._clientList = null; + this._jobList = null; + } + loadLookupEditBystatus(type:string): Observable> { + const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl + "/LoadLookupEdit"; + const params = new HttpParams().append('type', type); + console.log(this.msg + "go server this loadLookupEditBystatus is in cache ", type); + return this.http.get>(baseUrl, {params}); +} +loadLookupById(id:number, type:string): Observable> { + const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl; + const params = new HttpParams() + .append('id', id) + .append('type', type); + return this.http.get>(baseUrl,{params}); + } + saveLookup(item:LookupEdit): Observable> { //insert Lookup + this.setAllNull(); + let config = { headers : { 'Content-Type': 'application/json' } }; + const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl; + return this.http.post>(baseUrl, item, config); + } + deleteLookup(id:number): Observable>{ + const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl; + return this.http.delete>(baseUrl + "/" + id ); + } + + loadLookupBystatus(type:string): Observable> { + const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl + "/LoadJobs"; + console.log(this.msg + "loadLookupBystatus", this._lookupByStatus); + if (this._lookupByStatus[type]) + { + console.log(this.msg + "Not go server this LoadJobs is in cache ",type); + return of(this._lookupByStatus[type]); + } + else + { + const params = new HttpParams().append('type', type); + console.log(this.msg + "go server this loadLookupBystatus is in cache ", type); + return this.http.get>(baseUrl, {params}) + .pipe(map(resultModel => { + this._lookupByStatus[type] = resultModel; + return resultModel; + }) + ); + } + } + loadStaffs(): Observable> { + const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl + "/GetStaffs"; + /* const params = new HttpParams() + .append('id', id) + .append('type', type); + return this.http.get>(baseUrl,{params}); + */ + if (this._staffList != null) + { + return of(this._staffList); + } + else + { + return this.http.get>(baseUrl) + .pipe(map( x => { + this._staffList = x; + return x; + })); + } + } + loadClients(): Observable> { + const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl + "/GetClients"; + /* const params = new HttpParams() + .append('id', id) + .append('type', type); + return this.http.get>(baseUrl,{params}); + */ + if (this._clientList != null) + { + return of(this._clientList); + } + else + { + return this.http.get>(baseUrl) + .pipe(map(resultModel => { + this._clientList = resultModel; + return resultModel; + }) + ); + } + } + loadJobs(): Observable> { + const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.lookupUrl + "/GetJobs"; + /* const params = new HttpParams() + .append('id', id) + .append('type', type); + return this.http.get>(baseUrl,{params}); + */ + if (this._jobList != null) + { + return of(this._jobList); + } + else + { + return this.http.get>(baseUrl) + .pipe(map(x => { + this._jobList = x; + return x; + })) + } + } +} diff --git a/UI/src/app/shares/timeinput.ts b/UI/src/app/shares/timeinput.ts new file mode 100644 index 0000000..4aedf17 --- /dev/null +++ b/UI/src/app/shares/timeinput.ts @@ -0,0 +1,149 @@ +import { CommonModule } from '@angular/common'; +import { Component, Input, forwardRef, OnInit, OnDestroy } from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR,ReactiveFormsModule, FormControl, Validators } from '@angular/forms'; +import { Subject, takeUntil } from 'rxjs'; + +@Component({ + selector: 'time-input', + imports:[ReactiveFormsModule,CommonModule], + template: ` + + `, + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => TimeInputComponent), + multi: true, + }, + ], +}) +export class TimeInputComponent implements ControlValueAccessor, OnInit, OnDestroy { + @Input() placeholder: string = 'HH:MM'; + @Input() initialValue: string = ''; // Added initialValue input + @Input() disabled: boolean = false; + + timeControl = new FormControl('', [ + Validators.pattern(/^([0-2][0-9]:[0-5][0-9])?$/), // Added more precise regex + ]); + + private destroy$ = new Subject(); + private _value: string = ''; + private onChange: (value: string) => void = () => {}; + private onTouched: () => void = () => {}; + + constructor() {} + + ngOnInit(): void { + this.timeControl.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((value) => { + this.onChange(value || ''); // Pass the value to the parent form + }); + if (this.initialValue) { + this.writeValue(this.initialValue); + } + this.timeControl.disable(); + if(!this.disabled){ + this.timeControl.enable(); + } + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } + + get value(): string { + return this._value; + } + + set value(val: string) { + this._value = val; + this.onChange(val); + this.onTouched(); + } + + writeValue(value: string): void { + this._value = value || ''; + this.formatInput(this._value); // Use the formatting function + } + + registerOnChange(fn: (value: string) => void): void { + this.onChange = fn; + } + + registerOnTouched(fn: () => void): void { + this.onTouched = fn; + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (isDisabled) { + this.timeControl.disable(); + } else { + this.timeControl.enable(); + } + } + + onInput(event: Event): void { + let input = (event.target as HTMLInputElement).value; + input = input.replace(/[^0-9]/g, ''); // Remove non-numeric characters. + + if (input.length > 4) { + input = input.slice(0, 4); // Limit to 4 digits + } + + let formatted = ''; + if (input.length > 2) { + formatted = input.slice(0, 2) + ':' + input.slice(2); + } else { + formatted = input; + } + this.timeControl.setValue(formatted, { emitEvent: false }); // Prevent infinite loop + this.value = formatted; + } + + onBlur(): void { + this.onTouched(); + if (this.timeControl.valid) { + return; + } + if (this.timeControl.value?.length === 0) { + this.timeControl.setValue('', {emitEvent: false}); + this.value = ''; + return + } + this.formatInput(this.timeControl.value); + } + + private formatInput(val: string | null): void { + if (!val) { + this.timeControl.setValue('', {emitEvent: false}); + this.value = ''; + return; + } + let numbersOnly = val.replace(/[^0-9]/g, ''); + let formatted = ''; + + if (numbersOnly.length > 2) { + formatted = numbersOnly.slice(0, 2) + ':' + numbersOnly.slice(2, 4); + } else { + formatted = numbersOnly; + } + if (formatted.length === 5 && this.timeControl.valid) { + this.timeControl.setValue(formatted, {emitEvent: false}); + this.value = formatted; + } else if (numbersOnly.length <= 2) { + this.timeControl.setValue(formatted, {emitEvent: false}); + this.value = formatted; + } else { + this.timeControl.setValue('00:00', {emitEvent: false}); + this.value = '00:00' + } + } +} diff --git a/UI/src/app/shares/utils.ts b/UI/src/app/shares/utils.ts new file mode 100644 index 0000000..0ebe03f --- /dev/null +++ b/UI/src/app/shares/utils.ts @@ -0,0 +1,212 @@ +import moment from "moment"; +import { Person, User, userRole } from "../models" +import { TreeNode } from "primeng/api"; + +type MyProName = "fatherId" | "motherId"; + +export class Utils { + static toBase64 (file:any) + { + let promise = new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.readAsDataURL(file); + reader.onload = () => resolve(reader.result); + reader.onerror = reject; + }); + return promise; + } + static getBase64(file: any) { + let result: any; + var reader = new FileReader(); + reader.readAsDataURL(file); + reader.onload = function () { + result = reader.result; + console.log('tobase64 ', result); + }; + reader.onerror = function (error) { + console.log('Error: ', error); + }; + } + static getCurrentUser(): User { + + let myUser = new User(); + myUser.username = ""; + const obj = sessionStorage.getItem('currentUser'); + //const obj = localStorage.getItem('currentUser'); + if (obj != undefined) { + let user = JSON.parse(obj); + myUser.firstName = user.firstName; + myUser.id = user.id; + myUser.lastName = user.lastName; + myUser.role = user.role; + myUser.username = user.username; + myUser.email = user.email; + myUser.token = user.token; + myUser.phone = user.phone; + myUser.department = user.department; + myUser.position = user.position; + myUser.managerEmail = user.managerEmail; + + + + } + // console.log("[util] getcurrentUser",myUser); + return myUser; + } + static setLocalStore(user: User): void { + if (user && user.token) { + // store user details and jwt token in local storage to keep user logged in between page refreshes + // localStorage.setItem('currentUser', JSON.stringify(user)); + sessionStorage.setItem('currentUser', JSON.stringify(user)); + } + } + + static getLastMonth(): Date { + let result = moment().subtract(1, 'months').toDate(); + result.setDate(1); + return result; + } + static getHome(): string { + let result =""; + const user = this.getCurrentUser(); + const urole = user.role; + if (urole == userRole.Accounting) + { + result = "staffworkw"; + } + else if (urole == userRole.Admin) + { + result = "staff"; + } + else if (urole == userRole.ServiceManager) + { + result = "servicetask"; + } + else if (urole == userRole.WorkShop) + { + result = "staffwork"; + } + else if (urole == userRole.Normal) + { + result = "staff"; + } + return result; + } + static getTimeStr(val:Date): string { + let result = "00:00"; + if (val) + { + const hh = val.getHours(); + const min = val.getMinutes(); + if (hh < 10) + result = '0' + hh.toString(); + else + result = hh.toString(); + if (min < 10) + result = result + ":0" + min.toString(); + else + result = result + ":" + min.toString(); + + } + return result; + } + static canRunReport(role: number): boolean { + let result = false; + if (role == userRole.Admin) + result = true; + return result; + } +static getUserRole(user: User): number { + /* + sysadmin + request_manager + ward_user + */ + let ret = 0; + if (user) { + // console.log("utils user", user); + if (user.role) + ret = user.role; + } + return ret; + } + static getIsAuth(): boolean { + const user = this.getCurrentUser(); + if (user.username != "") { + return true; + } + else + return false; + } + static getAdminRoleName(): number { + return userRole.Admin; + } + static clearCurrentUser(): void { + sessionStorage.clear(); + //localStorage.clear(); + } + +static formatNode(item:Person): TreeNode + { + let label = item.lastName + " " + item.firstName; + let key = item.id.toString(); + let node: TreeNode ={ + key, + label, + type: 'person', + data: {title: item.title, name: label, image: item.image}, + icon: 'pi pi-user' + }; + + return node; + } + + static getChildForParentId(proName: MyProName , pid: number,childressNodes: Person[]): TreeNode[] { + let result: TreeNode[] =[]; + let tree_node_child: TreeNode[]; + let node:TreeNode; + let item: Person; + const children = childressNodes.filter(x => x[proName] == pid); + for (let c = 0; c < children.length; c++) + { + item = children[c]; + node = this.formatNode(item); + tree_node_child = this.getChildForParentId(proName,item.id, childressNodes); + console.log("getChildForParentId childressNodes item tree_node_child ", childressNodes, item, tree_node_child); + if (tree_node_child.length > 0) + { + node.expanded = true; + node.children = tree_node_child; + } + result.push(node); + } + return result; + } + +static populateNode(proName: MyProName, familyList: Person[]): TreeNode[] { + let familyTree: TreeNode[] = []; + let node:TreeNode; + let child_nodes:TreeNode[]; + + let childressNodes:Person[]; + + let item: Person; + const topNodes = familyList.filter(x => x[proName]! < 1); + childressNodes = familyList.filter(x => x[proName]! > 0); + for(let i = 0; i < topNodes.length; i++) + { + item = topNodes[i]; + node = Utils.formatNode(item); + child_nodes = this.getChildForParentId(proName, item.id,childressNodes); + console.log("populate getchildrenForParentId", child_nodes); + if (child_nodes.length > 0) + { + node.expanded = true; + node.children = child_nodes; + } + familyTree.push(node); + //childressNodes = + } + return familyTree; + } +} \ No newline at end of file diff --git a/UI/src/app/staff/index.ts b/UI/src/app/staff/index.ts new file mode 100644 index 0000000..fe5f350 --- /dev/null +++ b/UI/src/app/staff/index.ts @@ -0,0 +1,4 @@ +export * from './staff.component'; +export * from './staff.edit.component'; +export * from './staff.service'; +export * from './staff.pass.component'; diff --git a/UI/src/app/staff/staff.component.css b/UI/src/app/staff/staff.component.css new file mode 100644 index 0000000..3db3368 --- /dev/null +++ b/UI/src/app/staff/staff.component.css @@ -0,0 +1,12 @@ +table { + width: 100%; +} + +.mat-form-field { + font-size: 14px; + width: 100%; +} +td.mat-column-edit, .mat-column-delete { + width: 35px; + padding-right: 2px; +} diff --git a/UI/src/app/staff/staff.component.html b/UI/src/app/staff/staff.component.html new file mode 100644 index 0000000..01698b7 --- /dev/null +++ b/UI/src/app/staff/staff.component.html @@ -0,0 +1,63 @@ +
+

Staff List

+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+ + + + Email + + Surname + + First Name + + + Active + Edit + + + + + {{user.email}} + {{user.lastname}} + {{user.firstname}} + + + + + + + + + + +
+ diff --git a/UI/src/app/staff/staff.component.ts b/UI/src/app/staff/staff.component.ts new file mode 100644 index 0000000..d40f117 --- /dev/null +++ b/UI/src/app/staff/staff.component.ts @@ -0,0 +1,152 @@ +import { Component, OnInit, OnDestroy, ChangeDetectionStrategy, inject, ChangeDetectorRef} from '@angular/core'; + +import { StaffView ,StaffSearch } from '../models'; + +/* ngRx */ +/* +import { Store, select } from '@ngrx/store'; +import * as validationActions from './store/actions/validationpoint.action'; +import * as validationSelector from './store/selectors/validationpoint.selector'; +import { appValidationState } from './store/reducers'; +*/ +import { take } from 'rxjs/operators'; +import { Subscription } from 'rxjs'; +import { Router } from '@angular/router'; +import { StaffService } from './staff.service'; +import { AuthenticationService } from '../user-services'; +import { TableModule } from 'primeng/table'; +import { FormsModule } from '@angular/forms'; +import { CommonModule } from '@angular/common'; +import { ButtonModule } from 'primeng/button'; +import { InputTextModule } from 'primeng/inputtext'; + +@Component({ + selector: 'staff-list', + templateUrl: './staff.component.html', + imports:[TableModule,FormsModule,CommonModule,ButtonModule,InputTextModule], + styleUrls: ['./staff.component.css'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class StaffComponent implements OnInit, OnDestroy{ + + private subscription:Subscription = new Subscription(); + firstname = ''; + email = ''; + lastname = ''; + loading = false; + userList:StaffView[] = []; + private cd = inject(ChangeDetectorRef); + msg ="[Staff component]"; + + /* + private store: Store + */ + constructor( + private staffService: StaffService, + private authenticationService: AuthenticationService, + private router: Router + ) {} + getSearchCiteria(): StaffSearch { + let criteria:StaffSearch = { + email: this.email, + firstName: this.firstname, + lastName: this.lastname, + }; + + return criteria; + + } + canSearch():boolean { + let result = false; + result = this.email !== ""; + result = result || this.lastname !== ""; + result = result || this.firstname !== ""; + return result; + } + ngOnInit(): void + { + this.authenticationService.isHome = false; + this.authenticationService.isReport = false; + const prev = this.staffService.searchCriteria; + let goload = true; + if (prev.lastName !== '') + { + this.lastname = prev.lastName; + goload = true; + } + if (prev.firstName !== '') + { + this.firstname = prev.firstName; + goload = true; + } + if (prev.email !== '') + { + this.email = prev.email; + goload = true; + } + if (goload) + { + this.search(); + } + } + getActive(active:boolean):string { + let result = 'false-icon pi-times-circle'; + if (active) + result = 'true-icon pi-check-circle'; + return result; + } + search():void { + const canSearch = true; // this.canSearch(); + if (canSearch) + { + this.loading = true; + const criteria = this.getSearchCiteria(); + this.staffService.searchCriteria = criteria; + this.subscription.add( + this.staffService.searchStaffs(criteria).subscribe( { + next: result => { + this.loading = false; + // console.log(this.msg + "search load Data", result); + this.userList = result.data; + this.cd.detectChanges(); + }, + error: e => { + const message = e || e.message; + // this.toastr.error(message); + this.loading = false; + console.log("error ", e); + } + }) + ); + } + } + newUser():void { + //console.log("add new employee"); + this.router.navigate( ['/staff/new'], { queryParams: {returnUrl:'/staff' } }); + } + edit(id: number) : void { + //console.log("edit staff", id); + this.router.navigate( ['/staff/'+id], { queryParams: {returnUrl:'/staff' } }); + } + + deleteItem(id: number): void { + this.staffService.deleteStaff(id) + .pipe(take(1)) + .subscribe({ next: result => { + console.log(this.msg + " deleteItem success", result); + //let data = this.dataSource.data; + //let index: number = data.findIndex(d => d.id === id); + // console.log(this.msg + "delete from datasource"); + //data.splice(index, 1) + //this.dataSource.data = data; + }, + error: e => console.error(e) + }); + //console.log(this.msg + "click button to delete"); + } + + ngOnDestroy() { + this.subscription.unsubscribe(); + } +} + diff --git a/UI/src/app/staff/staff.edit.component.css b/UI/src/app/staff/staff.edit.component.css new file mode 100644 index 0000000..139597f --- /dev/null +++ b/UI/src/app/staff/staff.edit.component.css @@ -0,0 +1,2 @@ + + diff --git a/UI/src/app/staff/staff.edit.component.html b/UI/src/app/staff/staff.edit.component.html new file mode 100644 index 0000000..7fb3849 --- /dev/null +++ b/UI/src/app/staff/staff.edit.component.html @@ -0,0 +1,48 @@ +
+
+ {{getEditText()}} +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+
+
+ + +
+
+ +
\ No newline at end of file diff --git a/UI/src/app/staff/staff.edit.component.ts b/UI/src/app/staff/staff.edit.component.ts new file mode 100644 index 0000000..3b3d28e --- /dev/null +++ b/UI/src/app/staff/staff.edit.component.ts @@ -0,0 +1,211 @@ +import { Component, inject, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import { Router, ActivatedRoute } from '@angular/router'; +import { ReactiveFormsModule, UntypedFormBuilder, Validators } from '@angular/forms'; +import { Subject, Subscription} from 'rxjs'; +import { MessageService } from 'primeng/api'; + +import {Staff, Code, userRole} from '../models'; +import { LookupService, Utils } from '../shares'; +import { StaffService } from './staff.service'; +import { ButtonModule } from 'primeng/button'; +import { SelectModule } from 'primeng/select'; +import { CheckboxModule } from 'primeng/checkbox'; +import { InputTextModule } from 'primeng/inputtext'; + +@Component({ +templateUrl: 'staff.edit.component.html', +selector: 'staff-edit', +imports:[ButtonModule,ReactiveFormsModule,SelectModule,CheckboxModule, InputTextModule], +styleUrls: ['staff.edit.component.css'] +}) +export class StaffEditComponent implements OnInit, OnDestroy { + returnUrl =''; + loginUser =''; + _error =''; + _id= -1; + Roles: Code[]; + isNew = false; + validationPoints?: Code[]; + msg="[adminUser Component] "; + private formBuilder = inject(UntypedFormBuilder); + //for focus input +// @ViewChild('mystaffid') mystaffNo!: MatInput; + isChange = true; // disable use false//true for not disable. make sure it true is disable button. + private subscription:Subscription = new Subscription(); + subChanged$ = new Subject(); + adminuserForm = this.formBuilder.group({ + id: [0], //Validators.required + email: ['',Validators.required], //Validators.required + firstname: ['',Validators.required], //Validators.required + lastname: ['',Validators.required], //Validators.required + phone: [''], //Validators.required + password: [''], + active: [false], //Validators.required + + roleType: [0,[Validators.required, Validators.min(1)]], + + }); +constructor( + private staffService: StaffService, + private messageService: MessageService, + private lookupService: LookupService, + private router: Router, private route: ActivatedRoute, + ) { + + this.Roles = []; + let item:Code = {id:userRole.Admin, name: 'Admin', status:'Admin'}; + this.Roles.push(item); + item = {id:userRole.Accounting, name: 'Accounting', status:'Accounting'}; + this.Roles.push(item); + item = {id:userRole.Normal, name: 'Normal', status:'Normal'}; + this.Roles.push(item); + item = {id:userRole.ServiceManager, name: 'Service Manager', status:'ServiceManager'}; + this.Roles.push(item); + + + //item = {id:userRole.Normal, name: 'Switch', status:'Switch'}; + //this.Roles.push(item); + } +getClassForRequire(prev:string,name:string){ + const notok = !this.adminuserForm.controls[name].valid && + this.adminuserForm.controls[name].touched; + let str =prev; + if (notok) + str += " ng-invalid ng-dirty"; + return str; +} +ngOnInit():void { + this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/'; + const id = Number(this.route.snapshot.paramMap.get('id')); + // now load thing up + const user = Utils.getCurrentUser(); +// console.log(this.msg + "current login user ", user); + if (user.username === '') + alert("you are not login."); + else + this.loginUser = user.firstName; + this._id = id; + //console.log(this.msg + " " + id ); + this.subscription.add(this.adminuserForm.valueChanges.subscribe(x => this.isChange = false)); + this.subscription.add(this.subChanged$.subscribe(x => this.isChange = x)); + if (id > 0) + { + this.isNew = false; + this.subscription.add( + this.staffService.loadStaffById(id).subscribe({ + next: x => this.assignValue(x.data), + error: e => console.log(e) + }) + ); + } + else + { + this.isNew = true; + } + +} +getEditText(): string { + if (this._id > 0) + return "Edit Staff"; + else + return "New Staff"; +} +// this for disable submit button +get isFieldsChange() { + const chan = this.isChange || !this.adminuserForm.valid; // this disable so need true valid = true not + //console.log(this.msg + 'is fields change', chan); + return chan; + } + +assignValue(adminuser:Staff): void { + this.adminuserForm.patchValue({ + id: adminuser.id, + email: adminuser.email, + firstname: adminuser.firstname, + lastname: adminuser.lastname, + active: adminuser.active, + phone: adminuser.phone, + roleType: adminuser.roleType, + + }); + // disable use false//true for not disable. + this.subChanged$.next(true); + } + + // convenience getter for easy access in form fields + get f() { return this.adminuserForm.controls;} + validate(adminuser:Staff): boolean { + let result = true; + if (adminuser.email.trim() == '') + { + this._error = 'email is blank or empty'; + result = false; + } + else if (adminuser.firstname.trim() == '') + { + this._error = 'Firstname is blank or empty'; + result = false; + } + else if (adminuser.lastname.trim() == '') + { + this._error = 'lastname is blank or empty'; + result = false; + } + return result; + } +onSubmit(): void { + // if form valid then go save + //make sure + const okToSave = this.adminuserForm.valid; + if (okToSave) + { + let adminuserValue = this.adminuserForm.value; + + let adminuser:Staff = { + id: adminuserValue.id, + email: adminuserValue.email, + firstname: adminuserValue.firstname, + lastname: adminuserValue.lastname, + active: adminuserValue.active, + roleType: adminuserValue.roleType, + phone: adminuserValue.phone, + password: adminuserValue.password, + + type: 0 + }; + this._error =''; + const allOK = this.validate(adminuser); + if (allOK) + { + this.subscription.add ( + this.staffService.saveStaff(adminuser).subscribe({ + next: x => { + if (x.statusCode >= 1) + { + this.messageService.add({severity:'success', summary: 'Save user', detail: adminuser.firstname + " " + adminuser.lastname }); + this.router.navigate([this.returnUrl]); + } + else + { + this.messageService.add({severity:'error', summary: 'Error', detail: x.message }); + } + }, + error: e => { + const message = e || e.message; + // this.toastr.error(message); + console.log("error ", e); + } + }) + ); + } + else + this.messageService.add({severity:'error', summary: 'Error', detail: this._error }); + } +} +goBack(): void { + this.router.navigate([this.returnUrl]); +} +ngOnDestroy() { + this.subscription.unsubscribe(); +} +} diff --git a/UI/src/app/staff/staff.pass.component.css b/UI/src/app/staff/staff.pass.component.css new file mode 100644 index 0000000..139597f --- /dev/null +++ b/UI/src/app/staff/staff.pass.component.css @@ -0,0 +1,2 @@ + + diff --git a/UI/src/app/staff/staff.pass.component.html b/UI/src/app/staff/staff.pass.component.html new file mode 100644 index 0000000..6486375 --- /dev/null +++ b/UI/src/app/staff/staff.pass.component.html @@ -0,0 +1,24 @@ +
+
+ {{getEditText()}} +
+
+
+ +
+ + +
+
+ + +
+ +
+
+ + +
+
+ +
\ No newline at end of file diff --git a/UI/src/app/staff/staff.pass.component.ts b/UI/src/app/staff/staff.pass.component.ts new file mode 100644 index 0000000..7f5c8d3 --- /dev/null +++ b/UI/src/app/staff/staff.pass.component.ts @@ -0,0 +1,191 @@ +import { Component, inject, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import { Router, ActivatedRoute } from '@angular/router'; +import { FormsModule, ReactiveFormsModule, UntypedFormBuilder, Validators } from '@angular/forms'; +import { Subject, Subscription} from 'rxjs'; +import { MessageService } from 'primeng/api'; + +import {Staff, Code, userRole, ResetPassword} from '../models'; +import { LookupService, Utils } from '../shares'; +import { StaffService } from './staff.service'; +import { CommonModule } from '@angular/common'; +import { ButtonModule } from 'primeng/button'; + +@Component({ +templateUrl: 'staff.pass.component.html', +selector: 'staff-pass', +imports:[ReactiveFormsModule,CommonModule,ButtonModule], +styleUrls: ['staff.pass.component.css'] +}) +export class StaffPassComponent implements OnInit, OnDestroy { + returnUrl =''; + loginUser =''; + _error =''; + _id= -1; + + isNew = false; + validationPoints?: Code[]; + msg="[adminUser Component] "; + private formBuilder = inject(UntypedFormBuilder); + //for focus input +// @ViewChild('mystaffid') mystaffNo!: MatInput; + isChange = true; // disable use false//true for not disable. make sure it true is disable button. + private subscription:Subscription = new Subscription(); + subChanged$ = new Subject(); + adminuserForm = this.formBuilder.group({ + id: [0], //Validators.required + + password: ['',Validators.required], //Validators.required + passwordAgain: ['',Validators.required], //Validators.required + + + }); +constructor( + private staffService: StaffService, + private messageService: MessageService, + private lookupService: LookupService, + private router: Router, private route: ActivatedRoute, + ) { + + + //item = {id:userRole.Normal, name: 'Switch', status:'Switch'}; + //this.Roles.push(item); + } +getClassForRequire(prev:string,name:string){ + const notok = !this.adminuserForm.controls[name].valid && + this.adminuserForm.controls[name].touched; + let str =prev; + if (notok) + str += " ng-invalid ng-dirty"; + return str; +} +ngOnInit():void { + this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/'; + const id = Number(this.route.snapshot.paramMap.get('id')); + // now load thing up + const user = Utils.getCurrentUser(); +// console.log(this.msg + "current login user ", user); + if (user.username === '') + alert("you are not login."); + else + this.loginUser = user.firstName; + this._id = id; + //console.log(this.msg + " " + id ); + this.subscription.add(this.adminuserForm.valueChanges.subscribe(x => this.isChange = false)); + this.subscription.add(this.subChanged$.subscribe(x => this.isChange = x)); + + +} +getEditText(): string { + + return "Reset Password"; + +} +// this for disable submit button +get isFieldsChange() { + const chan = this.isChange || !this.adminuserForm.valid; // this disable so need true valid = true not + //console.log(this.msg + 'is fields change', chan); + return chan; + } + searchUser(): void { + const staffid = this.adminuserForm.controls["stafflinkNo"].value; + // console.log(this.msg + " the staffid is ", staffid); + if (staffid != '') { + //this.form.controls['your form control name'].value + this.staffService.loadStaffById(staffid).subscribe({ + next: x => { + //console.log(this.msg + " this is stafflink no ", x); + // now get current user. + + if (x.statusCode == 1) { + //this.error = true; + const fname = x.data.firstname + " " + x.data.lastname; + this.adminuserForm.patchValue({name:fname, addedBy:this.loginUser}); + + } + else { + this.messageService.add({severity:'error', summary: 'Error', detail: x.message }); + } + }, + error: e => { + const message = e || e.message; + //this.toastr.error(message); + console.log("error ", e); + } + }); + } + else + { + //this.toastr.error("please enter staff link no"); + } + } + + + // convenience getter for easy access in form fields + get f() { return this.adminuserForm.controls;} + validate(adminuser:any, passwordAgain:string): boolean { + let result = true; + const pass = adminuser.password.trim(); + if (pass == '') + { + this._error = 'password is blank or empty'; + result = false; + } + if (pass !== passwordAgain) + { + this._error = 'password is not match'; + result = false; + } + + return result; + } +onSubmit(): void { + // if form valid then go save + //make sure + const okToSave = this.adminuserForm.valid; + + if (okToSave) + { + let adminuserValue = this.adminuserForm.value; + + let adminuser:ResetPassword = { + id: this._id, + password: adminuserValue.password, + + }; + this._error =''; + const again = adminuserValue.passwordAgain; + const allOK = this.validate(adminuserValue, again); + if (allOK) + { + this.subscription.add ( + this.staffService.saveResetPassword(adminuser).subscribe({ + next: x => { + if (x.statusCode >= 1) + { + this.messageService.add({severity:'success', summary: 'Save user', detail: adminuser.id.toString() }); + this.router.navigate([this.returnUrl]); + } + else + { + this.messageService.add({severity:'error', summary: 'Error', detail: x.message }); + } + }, + error: e => { + const message = e || e.message; + // this.toastr.error(message); + console.log("error ", e); + } + }) + ); + } + else + this.messageService.add({severity:'error', summary: 'Error', detail: this._error }); + } +} +goBack(): void { + this.router.navigate([this.returnUrl]); +} +ngOnDestroy() { + this.subscription.unsubscribe(); +} +} diff --git a/UI/src/app/staff/staff.service.ts b/UI/src/app/staff/staff.service.ts new file mode 100644 index 0000000..fe29ebc --- /dev/null +++ b/UI/src/app/staff/staff.service.ts @@ -0,0 +1,55 @@ + +import { Injectable } from '@angular/core'; +import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; +import { Observable } from 'rxjs'; +import { Staff,StaffSearch, StaffView, ResultModel,ConfigureUrl,ResetPassword } from '../models'; +import { AppSettingService } from '../shares'; + +@Injectable({ providedIn: 'root' }) +export class StaffService { + public searchCriteria: StaffSearch; + constructor(private http: HttpClient, + private appSetting :AppSettingService + ) { + this.searchCriteria = { + email:'', + firstName: '', + lastName:'' + }; + + } + searchStaffs(criteria: StaffSearch): Observable> { + let config = { headers : { 'Content-Type': 'application/json' } }; + const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.staffUrl + "/SearchStaff"; + return this.http.post>(baseUrl, criteria, config); + } + + loadStaffById(id:number): Observable> { + const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.staffUrl; + /* + const params = new HttpParams().set("id", ""+id); + const headers = new HttpHeaders().set('Content-Type', 'application/json'); + const options = { + headers: headers, + params: params + }; + */ + //return this.http.get(this.baseUrl + 'api/Adminuser/LoadAdminuserById',options); + return this.http.get>(baseUrl + "/" + id); + } + saveStaff(data:Staff): Observable> { //insert Adminuser + let config = { headers : { 'Content-Type': 'application/json' } }; + const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.staffUrl+"/SaveStaff"; + return this.http.post>(baseUrl, data, config); + } + saveResetPassword(data:ResetPassword): Observable> { //insert Adminuser + let config = { headers : { 'Content-Type': 'application/json' } }; + const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.staffUrl+"/ResetPassStaff"; + return this.http.post>(baseUrl, data, config); + } + deleteStaff(id:number): Observable>{ + const baseUrl = this.appSetting.appSetting.baseUrl + "/"+ ConfigureUrl.staffUrl; + + return this.http.delete>(baseUrl + "/" + id); + } +} diff --git a/UI/src/app/toolbar/toolbar.component.css b/UI/src/app/toolbar/toolbar.component.css new file mode 100644 index 0000000..e69de29 diff --git a/UI/src/app/toolbar/toolbar.component.html b/UI/src/app/toolbar/toolbar.component.html new file mode 100644 index 0000000..582c8ff --- /dev/null +++ b/UI/src/app/toolbar/toolbar.component.html @@ -0,0 +1,41 @@ +@if (currentUserS() && isAuth) +{ +
+
+ Banner +
+
+
+ +
+ + + + + + +
+
+
+
+} \ No newline at end of file diff --git a/UI/src/app/toolbar/toolbar.component.ts b/UI/src/app/toolbar/toolbar.component.ts new file mode 100644 index 0000000..b63db41 --- /dev/null +++ b/UI/src/app/toolbar/toolbar.component.ts @@ -0,0 +1,174 @@ +import { Component, OnInit, OnDestroy, effect, computed, Signal, } from '@angular/core'; +import { Router } from '@angular/router'; +import { CommonModule } from '@angular/common'; +import { MenuItem } from 'primeng/api'; +import { MenuModule } from 'primeng/menu'; +import { ButtonModule} from 'primeng/button'; +import { ButtonGroupModule } from 'primeng/buttongroup'; +import { Subscription } from 'rxjs'; +import { AuthenticationService } from '../user-services'; +import { Utils } from '../shares'; +import { User } from '../models'; + + +@Component({ + selector: 'app-toolbar', + imports: [CommonModule, MenuModule, ButtonModule, ButtonGroupModule], + templateUrl: './toolbar.component.html', + styleUrl: './toolbar.component.css' +}) +export class ToolbarComponent implements OnInit, OnDestroy { + title = 'Safe Assessment Unit UI'; + isAuth = false; + hasRoleAdmin = false; + hasRoleReport = false; + homeUrl = "/person"; + oneMenu: MenuItem[] = []; + reportMenu: MenuItem[] = []; + systemMenu: MenuItem[] = []; + currentUserName = ""; + currentUserS: Signal = computed (() => { + const currentUser = this.authenticationService.authChange(); + this.isAuth =false; + if (currentUser.username) { + this.isAuth =true; + this.assignRole(currentUser); + console.log(`${this.title} on auth currentUser signal now ${this.isAuth}`); + // add this to the one menu + if (this.hasRoleAdmin) { + + let i = 0; + for (i = 0; i < this.systemMenu.length; i++) { + this.oneMenu.push(this.systemMenu[i]); + } + + } + //add the logout here + this.oneMenu.push( + { + label: 'Logout', icon: 'pi pi-power-off', command: () => { + this.logout() + } + } + ); + } + return currentUser; + }); + + private subscription: Subscription = new Subscription(); + constructor( + private router: Router, + private authenticationService: AuthenticationService + ) { + this.authenticationService.isReport = false; + this.authenticationService.isHome = true; + } + onHome(): void { + this.authenticationService.isHome = true; + this.router.navigate([this.homeUrl]); + } + get isHome(): boolean { + return this.authenticationService.isHome; + } + get loginUser(): string { + let result = ""; + let greeting = "Good "; + const d = new Date(); + const hour = d.getHours(); + if (hour >= 5 && hour < 12) + greeting = "Good morning, "; + else if (hour >= 12 && hour < 17) + greeting = "Good afternoon, "; + else if (hour >= 17 && hour < 21) + greeting = "Good evening, "; + else + greeting = "Good night, "; + if (this.isAuth) { + const user = Utils.getCurrentUser(); + result = user.firstName; + } + return greeting + result; + } + get isReport(): boolean { + + return this.authenticationService.isReport; + } + initOneMenu(): void { + this.oneMenu = [ + { + label: 'Home', icon: 'pi pi-slack', command: () => { + this.router.navigate([this.homeUrl]); + + } + } + ]; + } + initSystemMenu(): void { + this.systemMenu = [ +/* + { + label: 'Lookup', icon: 'pi pi-bars', command: () => { + this.router.navigate(['/lookup']); + this.authenticationService.isReport = false; + this.authenticationService.isHome = false; + } + }, + { + label: 'Waiting List', icon: 'pi pi-map-marker', command: () => { + this.router.navigate(['/waitinglist']); + this.authenticationService.isReport = false; + this.authenticationService.isHome = false; + } + }, + */ + { + label: 'Person', icon: 'pi pi-users', + command: () => { + this.router.navigate(['/person']); + + } + }, + { + label: 'Family tree', icon: 'pi pi-users', + command: () => { + this.router.navigate(['/familytree']); + + } + }, + + { + label: 'Staff', icon: 'pi pi-users', + command: () => { + this.router.navigate(['/staff']); + + } + }, + + ]; + } + + logout(): void { + this.initOneMenu(); + this.authenticationService.logout(); + this.router.navigate(['/login']); + } + assignRole(user: User): void { + const userRole = Utils.getUserRole(user); + this.hasRoleReport = Utils.canRunReport(userRole); + this.currentUserName = user.firstName; + //this.hasRoleAdmin = true; + this.hasRoleAdmin = userRole == Utils.getAdminRoleName(); + //console.log(this.msg + " hasRoleAdmin ", this.hasRoleAdmin); + + } + ngOnInit(): void { + this.initOneMenu(); + this.initSystemMenu(); + console.log("ngOnInit toolbar called"); + // this.initStart(); + } + + ngOnDestroy(): void { + this.subscription.unsubscribe(); + } +} diff --git a/UI/src/app/user-services/authentication.service.ts b/UI/src/app/user-services/authentication.service.ts new file mode 100644 index 0000000..ee15ed7 --- /dev/null +++ b/UI/src/app/user-services/authentication.service.ts @@ -0,0 +1,124 @@ +import { Injectable, Inject, Output, signal, computed, Signal } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { map } from 'rxjs/operators'; +import { BehaviorSubject, Observable } from 'rxjs'; +import { ConfigureUrl, User } from '../models'; +import { Utils, AppSettingService, LookupService } from '../shares'; + + +@Injectable({ providedIn: 'root' }) +export class AuthenticationService { + //private authChange = new BehaviorSubject(new User()); + // public currentUser: Observable; + public authChange = signal(new User()); + + private _isHome = false; + private _isReport = false; + private _selectDate: Date | null = null; + baseUrl = ''; // this.appSettingService.appSetting.baseUrl + "/"+ ConfigureUrl.loginApiUrl; + constructor(private http: HttpClient, + private appSetting: AppSettingService, + private lookupService: LookupService, + + ) { + // this default when load look for previous login information + // if no need then comment out downthere + // this.currentUser = this.authChange.asObservable(); + const fname = this.getCurrentUser(); + if (fname) { + + this.authChange.set(fname); + } + + } + + + /* select date */ + public get SelectDate(): Date | null { + return this._selectDate; + } + public set SelectDate(value: Date | null) { + this._selectDate = value; + } + + public get isHome(): boolean { + return this._isHome; + } + public set isHome(value: boolean) { + if (value) { + this._isReport = false; + } + this._isHome = value; + } + + public get isReport(): boolean { + return this._isReport; + } + public set isReport(value: boolean) { + if (value) { + this._isHome = false; + } + this._isReport = value; + } + + login(username: string, password: string): Observable { + this.lookupService.setAllNull(); + + const url = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.loginApiUrl; + const epass = btoa(password); + return this.http.post(url, { username, password: epass }) + .pipe(map(resultModel => { + // login successful if there's a jwt token in the response + if (resultModel.statusCode == 1) { + const user = resultModel.data; + + Utils.setLocalStore(user); + // const fName = user.firstName + " " + user.lastName; + this.authChange.set(user); + console.log("login from API result ", this.authChange()); + + } + return resultModel; + })); + } + + getCurrentUser(): User { + let userObj: User; + userObj = new User(); + let user = sessionStorage.getItem('currentUser'); + if (user) { + userObj = JSON.parse(user); + //fname = userObj.firstName + " " + userObj.lastName; + // logged in so return true + } + return userObj; + } + + isAuth(): boolean { + let val: boolean; + val = false; + if (sessionStorage.getItem('currentUser')) { + // logged in so return true + val = true; + } + return val; + } + logout(): void { + this.lookupService.setAllNull(); + + const url = this.appSetting.appSetting.baseUrl + "/" + ConfigureUrl.logoutUrl; + this.http.post(url, {}).subscribe({ + next: x => { + // console.log("logout return", x); + sessionStorage.removeItem('currentUser'); + const user = new User(); + this.authChange.set(user); + }, + error: e => { + sessionStorage.removeItem('currentUser'); + this.authChange.set(new User()); + } + }); + + } +} diff --git a/UI/src/app/user-services/index.ts b/UI/src/app/user-services/index.ts new file mode 100644 index 0000000..f6d76d5 --- /dev/null +++ b/UI/src/app/user-services/index.ts @@ -0,0 +1 @@ +export * from './authentication.service'; diff --git a/UI/src/index.html b/UI/src/index.html new file mode 100644 index 0000000..989706d --- /dev/null +++ b/UI/src/index.html @@ -0,0 +1,13 @@ + + + + + FamilyTree + + + + + + + + diff --git a/UI/src/main.ts b/UI/src/main.ts new file mode 100644 index 0000000..5df75f9 --- /dev/null +++ b/UI/src/main.ts @@ -0,0 +1,6 @@ +import { bootstrapApplication } from '@angular/platform-browser'; +import { appConfig } from './app/app.config'; +import { App } from './app/app'; + +bootstrapApplication(App, appConfig) + .catch((err) => console.error(err)); diff --git a/UI/src/styles.css b/UI/src/styles.css new file mode 100644 index 0000000..004cc13 --- /dev/null +++ b/UI/src/styles.css @@ -0,0 +1,60 @@ +/* You can add global styles to this file, and also import other style files */ +@import "tailwindcss"; +@plugin "tailwindcss-primeui"; +@import "primeicons/primeicons.css"; +html, +body { + height: 100%; + margin: 0; +} + +body { + /*padding-top: 5px; */ + padding-right: 1rem; + padding-left: 1rem; + padding-bottom: 0.3rem; + background-color: var(--surface-b); + font-family: var(--font-family); + font-weight: 400; + color: var(--text-color); + +} +.false-icon { + color: #c63737; +} + +.true-icon { + color: #15961e; +} + +.app-require { + font-size: 18px; + font-weight: bold; + color: red; +} + +.validateStar { + color: red; + font-size: 14px; +} + +.p-datepicker table td>span { + width: 1rem !important; + height: 1rem !important; + border: none !important; +} + +.p-datepicker .p-timepicker>div { + height: 50px !important; +} + +.p-datepicker .p-timepicker span { + font-size: 0.9rem !important; +} + +.p-datatable .p-datatable-tbody>tr>td { + + border: 1px solid #dee2e6; + border-width: 1px 0 0 0; + padding: 5px 5px !important; +} \ No newline at end of file diff --git a/UI/tsconfig.app.json b/UI/tsconfig.app.json new file mode 100644 index 0000000..264f459 --- /dev/null +++ b/UI/tsconfig.app.json @@ -0,0 +1,15 @@ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/app", + "types": [] + }, + "include": [ + "src/**/*.ts" + ], + "exclude": [ + "src/**/*.spec.ts" + ] +} diff --git a/UI/tsconfig.json b/UI/tsconfig.json new file mode 100644 index 0000000..e4955f2 --- /dev/null +++ b/UI/tsconfig.json @@ -0,0 +1,34 @@ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ +{ + "compileOnSave": false, + "compilerOptions": { + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "skipLibCheck": true, + "isolatedModules": true, + "experimentalDecorators": true, + "importHelpers": true, + "target": "ES2022", + "module": "preserve" + }, + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "typeCheckHostBindings": true, + "strictTemplates": true + }, + "files": [], + "references": [ + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/UI/tsconfig.spec.json b/UI/tsconfig.spec.json new file mode 100644 index 0000000..04df34c --- /dev/null +++ b/UI/tsconfig.spec.json @@ -0,0 +1,14 @@ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/spec", + "types": [ + "jasmine" + ] + }, + "include": [ + "src/**/*.ts" + ] +} diff --git a/document_table.docx b/document_table.docx new file mode 100644 index 0000000..f206605 Binary files /dev/null and b/document_table.docx differ