This commit is contained in:
Joe Milazzo 2024-11-16 09:20:28 -06:00 committed by GitHub
parent 6a75291a67
commit c849eff33e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 190 additions and 35 deletions

View File

@ -119,6 +119,11 @@ public class PersonController : BaseApiController
return Ok(_mapper.Map<PersonDto>(person)); return Ok(_mapper.Map<PersonDto>(person));
} }
/// <summary>
/// Attempts to download the cover from CoversDB (Note: Not yet release in Kavita)
/// </summary>
/// <param name="personId"></param>
/// <returns></returns>
[HttpPost("fetch-cover")] [HttpPost("fetch-cover")]
public async Task<ActionResult<string>> DownloadCoverImage([FromQuery] int personId) public async Task<ActionResult<string>> DownloadCoverImage([FromQuery] int personId)
{ {
@ -129,13 +134,13 @@ public class PersonController : BaseApiController
var personImage = await _coverDbService.DownloadPersonImageAsync(person, settings.EncodeMediaAs); var personImage = await _coverDbService.DownloadPersonImageAsync(person, settings.EncodeMediaAs);
if (string.IsNullOrEmpty(personImage)) return BadRequest(await _localizationService.Translate(User.GetUserId(), "person-image-doesnt-exist")); if (string.IsNullOrEmpty(personImage)) return BadRequest(await _localizationService.Translate(User.GetUserId(), "person-image-doesnt-exist"));
person.CoverImage = personImage; person.CoverImage = personImage;
_imageService.UpdateColorScape(person); _imageService.UpdateColorScape(person);
_unitOfWork.PersonRepository.Update(person); _unitOfWork.PersonRepository.Update(person);
await _unitOfWork.CommitAsync(); await _unitOfWork.CommitAsync();
await _eventHub.SendMessageAsync(MessageFactory.CoverUpdate, MessageFactory.CoverUpdateEvent(person.Id, "person"), false); await _eventHub.SendMessageAsync(MessageFactory.CoverUpdate, MessageFactory.CoverUpdateEvent(person.Id, "person"), false);
return Ok(personImage); return Ok(personImage);
} }
@ -150,6 +155,12 @@ public class PersonController : BaseApiController
return Ok(await _unitOfWork.PersonRepository.GetSeriesKnownFor(personId)); return Ok(await _unitOfWork.PersonRepository.GetSeriesKnownFor(personId));
} }
/// <summary>
/// Returns all individual chapters by role. Limited to 20 results.
/// </summary>
/// <param name="personId"></param>
/// <param name="role"></param>
/// <returns></returns>
[HttpGet("chapters-by-role")] [HttpGet("chapters-by-role")]
public async Task<ActionResult<IEnumerable<StandaloneChapterDto>>> GetChaptersByRole(int personId, PersonRole role) public async Task<ActionResult<IEnumerable<StandaloneChapterDto>>> GetChaptersByRole(int personId, PersonRole role)
{ {

View File

@ -56,7 +56,7 @@ public class VolumeDto : IHasReadTimeEstimate, IHasCoverImage
} }
/// <summary> /// <summary>
/// Does this volume hold only specials? /// Does this volume hold only specials
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public bool IsSpecial() public bool IsSpecial()

View File

@ -1192,7 +1192,7 @@ public class SeriesRepository : ISeriesRepository
private static IQueryable<Series> BuildFilterQuery(int userId, FilterV2Dto filterDto, IQueryable<Series> query) private static IQueryable<Series> BuildFilterQuery(int userId, FilterV2Dto filterDto, IQueryable<Series> query)
{ {
if (filterDto.Statements == null || !filterDto.Statements.Any()) return query; if (filterDto.Statements == null || filterDto.Statements.Count == 0) return query;
var queries = filterDto.Statements var queries = filterDto.Statements

View File

@ -167,6 +167,7 @@ public static class SeriesFilter
throw new ArgumentOutOfRangeException(nameof(comparison), comparison, null); throw new ArgumentOutOfRangeException(nameof(comparison), comparison, null);
} }
} }
public static IQueryable<Series> HasAverageReadTime(this IQueryable<Series> queryable, bool condition, public static IQueryable<Series> HasAverageReadTime(this IQueryable<Series> queryable, bool condition,
FilterComparison comparison, int avgReadTime) FilterComparison comparison, int avgReadTime)
{ {
@ -175,17 +176,17 @@ public static class SeriesFilter
switch (comparison) switch (comparison)
{ {
case FilterComparison.NotEqual: case FilterComparison.NotEqual:
return queryable.Where(s => s.AvgHoursToRead != avgReadTime); return queryable.WhereNotEqual(s => s.AvgHoursToRead, avgReadTime);
case FilterComparison.Equal: case FilterComparison.Equal:
return queryable.Where(s => s.AvgHoursToRead == avgReadTime); return queryable.WhereEqual(s => s.AvgHoursToRead, avgReadTime);
case FilterComparison.GreaterThan: case FilterComparison.GreaterThan:
return queryable.Where(s => s.AvgHoursToRead > avgReadTime); return queryable.WhereGreaterThan(s => s.AvgHoursToRead, avgReadTime);
case FilterComparison.GreaterThanEqual: case FilterComparison.GreaterThanEqual:
return queryable.Where(s => s.AvgHoursToRead >= avgReadTime); return queryable.WhereGreaterThanOrEqual(s => s.AvgHoursToRead, avgReadTime);
case FilterComparison.LessThan: case FilterComparison.LessThan:
return queryable.Where(s => s.AvgHoursToRead < avgReadTime); return queryable.WhereLessThan(s => s.AvgHoursToRead, avgReadTime);
case FilterComparison.LessThanEqual: case FilterComparison.LessThanEqual:
return queryable.Where(s => s.AvgHoursToRead <= avgReadTime); return queryable.WhereLessThanOrEqual(s => s.AvgHoursToRead, avgReadTime);
case FilterComparison.Contains: case FilterComparison.Contains:
case FilterComparison.Matches: case FilterComparison.Matches:
case FilterComparison.NotContains: case FilterComparison.NotContains:
@ -257,29 +258,29 @@ public static class SeriesFilter
Series = s, Series = s,
Percentage = s.Progress Percentage = s.Progress
.Where(p => p != null && p.AppUserId == userId) .Where(p => p != null && p.AppUserId == userId)
.Sum(p => p != null ? (p.PagesRead * 1.0f / s.Pages) : 0) * 100 .Sum(p => p != null ? (p.PagesRead * 1.0f / s.Pages) : 0f) * 100f
}) })
.AsSplitQuery(); .AsSplitQuery();
switch (comparison) switch (comparison)
{ {
case FilterComparison.Equal: case FilterComparison.Equal:
subQuery = subQuery.Where(s => Math.Abs(s.Percentage - readProgress) < FloatingPointTolerance); subQuery = subQuery.WhereEqual(s => s.Percentage, readProgress);
break; break;
case FilterComparison.GreaterThan: case FilterComparison.GreaterThan:
subQuery = subQuery.Where(s => s.Percentage > readProgress); subQuery = subQuery.WhereGreaterThan(s => s.Percentage, readProgress);
break; break;
case FilterComparison.GreaterThanEqual: case FilterComparison.GreaterThanEqual:
subQuery = subQuery.Where(s => s.Percentage >= readProgress); subQuery = subQuery.WhereGreaterThanOrEqual(s => s.Percentage, readProgress);
break; break;
case FilterComparison.LessThan: case FilterComparison.LessThan:
subQuery = subQuery.Where(s => s.Percentage < readProgress); subQuery = subQuery.WhereLessThan(s => s.Percentage, readProgress);
break; break;
case FilterComparison.LessThanEqual: case FilterComparison.LessThanEqual:
subQuery = subQuery.Where(s => s.Percentage <= readProgress); subQuery = subQuery.WhereLessThanOrEqual(s => s.Percentage, readProgress);
break; break;
case FilterComparison.NotEqual: case FilterComparison.NotEqual:
subQuery = subQuery.Where(s => Math.Abs(s.Percentage - readProgress) > FloatingPointTolerance); subQuery = subQuery.WhereNotEqual(s => s.Percentage, readProgress);
break; break;
case FilterComparison.IsEmpty: case FilterComparison.IsEmpty:
case FilterComparison.Matches: case FilterComparison.Matches:
@ -306,7 +307,6 @@ public static class SeriesFilter
{ {
if (!condition) return queryable; if (!condition) return queryable;
var subQuery = queryable var subQuery = queryable
.Where(s => s.ExternalSeriesMetadata != null) .Where(s => s.ExternalSeriesMetadata != null)
.Include(s => s.ExternalSeriesMetadata) .Include(s => s.ExternalSeriesMetadata)
@ -316,27 +316,27 @@ public static class SeriesFilter
AverageRating = s.ExternalSeriesMetadata.AverageExternalRating AverageRating = s.ExternalSeriesMetadata.AverageExternalRating
}) })
.AsSplitQuery() .AsSplitQuery()
.AsEnumerable(); .AsQueryable();
switch (comparison) switch (comparison)
{ {
case FilterComparison.Equal: case FilterComparison.Equal:
subQuery = subQuery.Where(s => Math.Abs(s.AverageRating - rating) < FloatingPointTolerance); subQuery = subQuery.WhereEqual(s => s.AverageRating, rating);
break; break;
case FilterComparison.GreaterThan: case FilterComparison.GreaterThan:
subQuery = subQuery.Where(s => s.AverageRating > rating); subQuery = subQuery.WhereGreaterThan(s => s.AverageRating, rating);
break; break;
case FilterComparison.GreaterThanEqual: case FilterComparison.GreaterThanEqual:
subQuery = subQuery.Where(s => s.AverageRating >= rating); subQuery = subQuery.WhereGreaterThanOrEqual(s => s.AverageRating, rating);
break; break;
case FilterComparison.LessThan: case FilterComparison.LessThan:
subQuery = subQuery.Where(s => s.AverageRating < rating); subQuery = subQuery.WhereLessThan(s => s.AverageRating, rating);
break; break;
case FilterComparison.LessThanEqual: case FilterComparison.LessThanEqual:
subQuery = subQuery.Where(s => s.AverageRating <= rating); subQuery = subQuery.WhereLessThanOrEqual(s => s.AverageRating, rating);
break; break;
case FilterComparison.NotEqual: case FilterComparison.NotEqual:
subQuery = subQuery.Where(s => Math.Abs(s.AverageRating - rating) > FloatingPointTolerance); subQuery = subQuery.WhereNotEqual(s => s.AverageRating, rating);
break; break;
case FilterComparison.Matches: case FilterComparison.Matches:
case FilterComparison.Contains: case FilterComparison.Contains:
@ -534,21 +534,21 @@ public static class SeriesFilter
{ {
case FilterComparison.Equal: case FilterComparison.Equal:
case FilterComparison.Contains: case FilterComparison.Contains:
return queryable.Where(s => s.Metadata.People.Any(p => people.Contains(p.PersonId))); return queryable.Where(s => s.Metadata.People.Any(p => people.Contains(p.PersonId) && p.Role == role));
case FilterComparison.NotEqual: case FilterComparison.NotEqual:
case FilterComparison.NotContains: case FilterComparison.NotContains:
return queryable.Where(s => s.Metadata.People.All(t => !people.Contains(t.PersonId))); return queryable.Where(s => s.Metadata.People.All(p => !people.Contains(p.PersonId) || p.Role != role));
case FilterComparison.MustContains: case FilterComparison.MustContains:
// Deconstruct and do a Union of a bunch of where statements since this doesn't translate
var queries = new List<IQueryable<Series>>() var queries = new List<IQueryable<Series>>()
{ {
queryable queryable
}; };
queries.AddRange(people.Select(gId => queryable.Where(s => s.Metadata.People.Any(p => p.PersonId == gId)))); queries.AddRange(people.Select(personId =>
queryable.Where(s => s.Metadata.People.Any(p => p.PersonId == personId && p.Role == role))));
return queries.Aggregate((q1, q2) => q1.Intersect(q2)); return queries.Aggregate((q1, q2) => q1.Intersect(q2));
case FilterComparison.IsEmpty: case FilterComparison.IsEmpty:
// Check if there are no people with specific roles (e.g., Writer, Penciller, etc.) // Ensure no person with the given role exists
return queryable.Where(s => s.Metadata.People.All(p => p.Role != role)); return queryable.Where(s => s.Metadata.People.All(p => p.Role != role));
case FilterComparison.GreaterThan: case FilterComparison.GreaterThan:
case FilterComparison.GreaterThanEqual: case FilterComparison.GreaterThanEqual:

View File

@ -16,6 +16,8 @@ namespace API.Extensions.QueryExtensions;
public static class QueryableExtensions public static class QueryableExtensions
{ {
private const float DefaultTolerance = 0.001f;
public static Task<AgeRestriction> GetUserAgeRestriction(this DbSet<AppUser> queryable, int userId) public static Task<AgeRestriction> GetUserAgeRestriction(this DbSet<AppUser> queryable, int userId)
{ {
if (userId < 1) if (userId < 1)
@ -125,6 +127,140 @@ public static class QueryableExtensions
return queryable.Where(lambda); return queryable.Where(lambda);
} }
public static IQueryable<T> WhereGreaterThan<T>(this IQueryable<T> source,
Expression<Func<T, float>> selector,
float value,
float tolerance = DefaultTolerance)
{
var parameter = selector.Parameters[0];
var propertyAccess = selector.Body;
// Absolute difference comparison: (propertyAccess - value) > tolerance
var difference = Expression.Subtract(propertyAccess, Expression.Constant(value));
var absoluteDifference = Expression.Condition(
Expression.LessThan(difference, Expression.Constant(0f)),
Expression.Negate(difference),
difference);
var greaterThanExpression = Expression.GreaterThan(propertyAccess, Expression.Constant(value));
var toleranceExpression = Expression.GreaterThan(absoluteDifference, Expression.Constant(tolerance));
var combinedExpression = Expression.AndAlso(greaterThanExpression, toleranceExpression);
var lambda = Expression.Lambda<Func<T, bool>>(combinedExpression, parameter);
return source.Where(lambda);
}
public static IQueryable<T> WhereGreaterThanOrEqual<T>(this IQueryable<T> source,
Expression<Func<T, float>> selector,
float value,
float tolerance = DefaultTolerance)
{
var parameter = selector.Parameters[0];
var propertyAccess = selector.Body;
var difference = Expression.Subtract(propertyAccess, Expression.Constant(value));
var absoluteDifference = Expression.Condition(
Expression.LessThan(difference, Expression.Constant(0f)),
Expression.Negate(difference),
difference);
var greaterThanOrEqualExpression = Expression.GreaterThanOrEqual(propertyAccess, Expression.Constant(value));
var toleranceExpression = Expression.GreaterThanOrEqual(absoluteDifference, Expression.Constant(tolerance));
var combinedExpression = Expression.AndAlso(greaterThanOrEqualExpression, toleranceExpression);
var lambda = Expression.Lambda<Func<T, bool>>(combinedExpression, parameter);
return source.Where(lambda);
}
public static IQueryable<T> WhereLessThan<T>(this IQueryable<T> source,
Expression<Func<T, float>> selector,
float value,
float tolerance = DefaultTolerance)
{
var parameter = selector.Parameters[0];
var propertyAccess = selector.Body;
var difference = Expression.Subtract(propertyAccess, Expression.Constant(value));
var absoluteDifference = Expression.Condition(
Expression.LessThan(difference, Expression.Constant(0f)),
Expression.Negate(difference),
difference);
var lessThanExpression = Expression.LessThan(propertyAccess, Expression.Constant(value));
var toleranceExpression = Expression.LessThan(absoluteDifference, Expression.Constant(tolerance));
var combinedExpression = Expression.AndAlso(lessThanExpression, toleranceExpression);
var lambda = Expression.Lambda<Func<T, bool>>(combinedExpression, parameter);
return source.Where(lambda);
}
public static IQueryable<T> WhereLessThanOrEqual<T>(this IQueryable<T> source,
Expression<Func<T, float>> selector,
float value,
float tolerance = DefaultTolerance)
{
var parameter = selector.Parameters[0];
var propertyAccess = selector.Body;
var difference = Expression.Subtract(propertyAccess, Expression.Constant(value));
var absoluteDifference = Expression.Condition(
Expression.LessThan(difference, Expression.Constant(0f)),
Expression.Negate(difference),
difference);
var lessThanOrEqualExpression = Expression.LessThanOrEqual(propertyAccess, Expression.Constant(value));
var toleranceExpression = Expression.LessThanOrEqual(absoluteDifference, Expression.Constant(tolerance));
var combinedExpression = Expression.AndAlso(lessThanOrEqualExpression, toleranceExpression);
var lambda = Expression.Lambda<Func<T, bool>>(combinedExpression, parameter);
return source.Where(lambda);
}
public static IQueryable<T> WhereEqual<T>(this IQueryable<T> source,
Expression<Func<T, float>> selector,
float value,
float tolerance = DefaultTolerance)
{
var parameter = selector.Parameters[0];
var propertyAccess = selector.Body;
// Absolute difference comparison: Math.Abs(propertyAccess - value) < tolerance
var difference = Expression.Subtract(propertyAccess, Expression.Constant(value));
var absoluteDifference = Expression.Condition(
Expression.LessThan(difference, Expression.Constant(0f)),
Expression.Negate(difference),
difference);
var toleranceExpression = Expression.LessThan(absoluteDifference, Expression.Constant(tolerance));
var lambda = Expression.Lambda<Func<T, bool>>(toleranceExpression, parameter);
return source.Where(lambda);
}
public static IQueryable<T> WhereNotEqual<T>(this IQueryable<T> source,
Expression<Func<T, float>> selector,
float value,
float tolerance = DefaultTolerance)
{
var parameter = selector.Parameters[0];
var propertyAccess = selector.Body;
var difference = Expression.Subtract(propertyAccess, Expression.Constant(value));
var absoluteDifference = Expression.Condition(
Expression.LessThan(difference, Expression.Constant(0f)),
Expression.Negate(difference),
difference);
var toleranceExpression = Expression.GreaterThan(absoluteDifference, Expression.Constant(tolerance));
var lambda = Expression.Lambda<Func<T, bool>>(toleranceExpression, parameter);
return source.Where(lambda);
}
/// <summary> /// <summary>
/// Performs a WhereLike that ORs multiple fields /// Performs a WhereLike that ORs multiple fields
/// </summary> /// </summary>

View File

@ -213,9 +213,8 @@ public class ProcessSeries : IProcessSeries
return; return;
} }
BackgroundJob.Enqueue(() => await _metadataService.GenerateCoversForSeries(series.LibraryId, series.Id, false, false);
_metadataService.GenerateCoversForSeries(series.LibraryId, series.Id, false, false)); await _wordCountAnalyzerService.ScanSeries(series.LibraryId, series.Id, forceUpdate);
BackgroundJob.Enqueue(() => _wordCountAnalyzerService.ScanSeries(series.LibraryId, series.Id, forceUpdate));
} }
private async Task ReportDuplicateSeriesLookup(Library library, ParserInfo firstInfo, Exception ex) private async Task ReportDuplicateSeriesLookup(Library library, ParserInfo firstInfo, Exception ex)

View File

@ -464,6 +464,7 @@
"version": "18.2.9", "version": "18.2.9",
"resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-18.2.9.tgz", "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-18.2.9.tgz",
"integrity": "sha512-4iMoRvyMmq/fdI/4Gob9HKjL/jvTlCjbS4kouAYHuGO9w9dmUhi1pY1z+mALtCEl9/Q8CzU2W8e5cU2xtV4nVg==", "integrity": "sha512-4iMoRvyMmq/fdI/4Gob9HKjL/jvTlCjbS4kouAYHuGO9w9dmUhi1pY1z+mALtCEl9/Q8CzU2W8e5cU2xtV4nVg==",
"dev": true,
"dependencies": { "dependencies": {
"@babel/core": "7.25.2", "@babel/core": "7.25.2",
"@jridgewell/sourcemap-codec": "^1.4.14", "@jridgewell/sourcemap-codec": "^1.4.14",
@ -491,6 +492,7 @@
"version": "4.0.1", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz",
"integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==", "integrity": "sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==",
"dev": true,
"dependencies": { "dependencies": {
"readdirp": "^4.0.1" "readdirp": "^4.0.1"
}, },
@ -505,6 +507,7 @@
"version": "4.0.2", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz",
"integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==",
"dev": true,
"engines": { "engines": {
"node": ">= 14.16.0" "node": ">= 14.16.0"
}, },
@ -4007,7 +4010,8 @@
"node_modules/convert-source-map": { "node_modules/convert-source-map": {
"version": "1.9.0", "version": "1.9.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
"integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
"dev": true
}, },
"node_modules/cosmiconfig": { "node_modules/cosmiconfig": {
"version": "8.3.6", "version": "8.3.6",
@ -4514,6 +4518,7 @@
"version": "0.1.13", "version": "0.1.13",
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
"integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
"dev": true,
"optional": true, "optional": true,
"dependencies": { "dependencies": {
"iconv-lite": "^0.6.2" "iconv-lite": "^0.6.2"
@ -4523,6 +4528,7 @@
"version": "0.6.3", "version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"dev": true,
"optional": true, "optional": true,
"dependencies": { "dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0" "safer-buffer": ">= 2.1.2 < 3.0.0"
@ -7464,7 +7470,8 @@
"node_modules/reflect-metadata": { "node_modules/reflect-metadata": {
"version": "0.2.2", "version": "0.2.2",
"resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz",
"integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==" "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==",
"dev": true
}, },
"node_modules/replace-in-file": { "node_modules/replace-in-file": {
"version": "7.1.0", "version": "7.1.0",
@ -7735,7 +7742,7 @@
"version": "2.1.2", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
"devOptional": true "dev": true
}, },
"node_modules/sass": { "node_modules/sass": {
"version": "1.77.6", "version": "1.77.6",
@ -7769,6 +7776,7 @@
"version": "7.6.3", "version": "7.6.3",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
"integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
"dev": true,
"bin": { "bin": {
"semver": "bin/semver.js" "semver": "bin/semver.js"
}, },
@ -8323,6 +8331,7 @@
"version": "5.5.4", "version": "5.5.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz",
"integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==",
"dev": true,
"bin": { "bin": {
"tsc": "bin/tsc", "tsc": "bin/tsc",
"tsserver": "bin/tsserver" "tsserver": "bin/tsserver"