Merge branch 'dev' into fix/ab/sh-261-vendor-list-contact

This commit is contained in:
Alexandre Brandizzi 2026-08-25 11:52:46 -03:00 • committed by GitHub
commit 5343931a62
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 482 additions and 29 deletions

View file

@ -66,7 +66,7 @@ namespace Api.SeaHavenIndustries.Controllers
{
WorkOrderMediaFileRules.EnsureAllowed(file);
var actorId = User.FindFirstValue(ClaimTypes.NameIdentifier);
await _workOrderMediaService.EnsureCanMutateMediaAsync(id, User, actorId, cancellationToken);
await _workOrderMediaService.EnsureCanMutateMediaAsync(id, User, actorId, cancellationToken, category);
fileUrl = await _fileStorage.SaveFileAsync(file);
var media = await _workOrderMediaService.AddMediaAsync(id, category, fileUrl, User, actorId, cancellationToken);

View file

@ -9,6 +9,33 @@ namespace SeaHaven.Services.Helpers
public static bool IsReadOnly(LifecycleStatus? status) => ReadOnlyStatuses.Contains(status);
public static bool IsFullyLocked(LifecycleStatus? status)
=> status == LifecycleStatus.Canceled;
public static bool IsCoreLocked(LifecycleStatus? status)
=> status == LifecycleStatus.Completed;
public static bool CanPatchBoardField(LifecycleStatus? status, string field)
{
if (IsFullyLocked(status))
return false;
if (!IsCoreLocked(status))
return true;
return field.Equals(WorkOrderBoardFieldNames.FlagColor, StringComparison.OrdinalIgnoreCase);
}
public static bool CanMutateComments(LifecycleStatus? status)
=> !IsFullyLocked(status);
public static bool CanMutateExtraMedia(LifecycleStatus? status, WorkOrderMediaCategory? category)
{
if (IsFullyLocked(status))
return false;
if (!IsCoreLocked(status))
return true;
return (category ?? WorkOrderMediaCategory.Extra) == WorkOrderMediaCategory.Extra;
}
public static bool ShouldBlockStatusChangeWhenPastDue(string field, bool isPastDue)
=> field.Equals(WorkOrderBoardFieldNames.LifecycleStatus, StringComparison.OrdinalIgnoreCase) && isPastDue;

View file

@ -59,8 +59,13 @@ namespace SeaHaven.Services.Implementation
throw new WorkOrderBoardConcurrencyException(staleState);
}
if (WorkOrderBoardMutationRules.IsReadOnly(workOrder.LifecycleStatus))
throw new WorkOrderBoardValidationException("CanceledReadOnly", "Work order is read-only in its current status.");
if (!WorkOrderBoardMutationRules.CanPatchBoardField(workOrder.LifecycleStatus, canonicalField))
{
var code = WorkOrderBoardMutationRules.IsFullyLocked(workOrder.LifecycleStatus)
? "CanceledReadOnly"
: "ReadOnly";
throw new WorkOrderBoardValidationException(code, "Work order is read-only in its current status.");
}
var isPastDue = WorkOrderDerivedFields.IsPastDue(workOrder.ScheduledDate, workOrder.LifecycleStatus);
if (WorkOrderBoardMutationRules.ShouldBlockStatusChangeWhenPastDue(canonicalField, isPastDue))

View file

@ -1,5 +1,6 @@
using System.Security.Claims;
using Data.SeaHavenIndustries;
using Data.SeaHavenIndustries.Enums;
using SeaHaven.DataServices.Interfaces;
using SeaHaven.Services.DTOs;
using SeaHaven.Services.Exceptions;
@ -48,9 +49,13 @@ namespace SeaHaven.Services.Implementation
string? documentUrl = null)
{
var accountId = _accountResolver.ResolveAccountFilter(user);
if (!await _detailData.ExistsAsync(workOrderId, CancellationToken.None, accountId))
var workOrder = await _detailData.GetWorkOrderForMediaAsync(
workOrderId, CancellationToken.None, accountId);
if (workOrder == null)
throw new WorkOrderBoardValidationException("NotFound", "Work order not found.");
EnsureCommentMutationAllowed(workOrder.LifecycleStatus);
if (string.IsNullOrWhiteSpace(request.Text))
throw new WorkOrderBoardValidationException("InvalidValue", "Comment text is required.");
@ -83,8 +88,7 @@ namespace SeaHaven.Services.Implementation
if (workOrder == null)
throw new WorkOrderBoardValidationException("NotFound", "Work order not found.");
if (WorkOrderBoardMutationRules.IsReadOnly(workOrder.LifecycleStatus))
throw new WorkOrderBoardValidationException("ReadOnly", "Work order is read-only in its current status.");
EnsureCommentMutationAllowed(workOrder.LifecycleStatus);
if (string.IsNullOrWhiteSpace(request.Text))
throw new WorkOrderBoardValidationException("InvalidValue", "Comment text is required.");
@ -111,6 +115,12 @@ namespace SeaHaven.Services.Implementation
return WorkOrderCommentProjection.ToDto(comment, authorNames);
}
private static void EnsureCommentMutationAllowed(LifecycleStatus? status)
{
if (!WorkOrderBoardMutationRules.CanMutateComments(status))
throw new WorkOrderBoardValidationException("ReadOnly", "Work order is read-only in its current status.");
}
private static bool IsBoardEditableComment(Comments comment)
=> string.Equals(comment.CommentType, "General", StringComparison.OrdinalIgnoreCase)
&& string.Equals(comment.RecordType, "WorkOrder", StringComparison.OrdinalIgnoreCase);

View file

@ -52,11 +52,13 @@ namespace SeaHaven.Services.Implementation
int workOrderId,
ClaimsPrincipal user,
string? actorId,
CancellationToken cancellationToken = default)
CancellationToken cancellationToken = default,
WorkOrderMediaCategory? category = null)
{
WorkOrderMediaAuthorization.EnsureCanMutate(user, actorId);
// AsNoTracking pre-check so the subsequent AddMediaAsync load is not stale-cached.
await GetMutableWorkOrderForAuthAsync(workOrderId, user, actorId!, cancellationToken);
var workOrder = await GetMutableWorkOrderForAuthAsync(workOrderId, user, actorId!, cancellationToken);
EnsureMediaMutationAllowed(workOrder.LifecycleStatus, category ?? WorkOrderMediaCategory.Extra);
}
public async Task<WorkOrderMediaFileDto> AddMediaAsync(
@ -71,6 +73,7 @@ namespace SeaHaven.Services.Implementation
var resolvedCategory = category ?? WorkOrderMediaCategory.Extra;
var workOrder = await GetMutableWorkOrderAsync(workOrderId, user, actorId!, cancellationToken);
EnsureMediaMutationAllowed(workOrder.LifecycleStatus, resolvedCategory);
if (resolvedCategory == WorkOrderMediaCategory.Completion)
{
@ -167,7 +170,10 @@ namespace SeaHaven.Services.Implementation
if (attachment == null)
throw new WorkOrderBoardValidationException("NotFound", "Media not found.");
var priorCategory = (attachment.Category ?? WorkOrderMediaCategory.Extra).ToString();
var currentCategory = attachment.Category ?? WorkOrderMediaCategory.Extra;
EnsureMediaMutationAllowed(workOrder.LifecycleStatus, currentCategory);
EnsureMediaMutationAllowed(workOrder.LifecycleStatus, category);
var priorCategory = currentCategory.ToString();
if (category == WorkOrderMediaCategory.Before || category == WorkOrderMediaCategory.After)
{
@ -238,7 +244,9 @@ namespace SeaHaven.Services.Implementation
if (attachment == null)
throw new WorkOrderBoardValidationException("NotFound", "Media not found.");
var priorCategory = (attachment.Category ?? WorkOrderMediaCategory.Extra).ToString();
var currentCategory = attachment.Category ?? WorkOrderMediaCategory.Extra;
EnsureMediaMutationAllowed(workOrder.LifecycleStatus, currentCategory);
var priorCategory = currentCategory.ToString();
attachment.IsDeleted = true;
attachment.DeletionTime = DateTime.UtcNow;
attachment.DeleterUserId = actorId;
@ -266,9 +274,6 @@ namespace SeaHaven.Services.Implementation
WorkOrderMediaAuthorization.EnsureWorkOrderInCallerScope(user, actorId, workOrder);
if (WorkOrderBoardMutationRules.IsReadOnly(workOrder.LifecycleStatus))
throw new WorkOrderBoardValidationException("ReadOnly", "Work order is read-only in its current status.");
return workOrder;
}
@ -286,12 +291,17 @@ namespace SeaHaven.Services.Implementation
WorkOrderMediaAuthorization.EnsureWorkOrderInCallerScope(user, actorId, workOrder);
if (WorkOrderBoardMutationRules.IsReadOnly(workOrder.LifecycleStatus))
throw new WorkOrderBoardValidationException("ReadOnly", "Work order is read-only in its current status.");
return workOrder;
}
private static void EnsureMediaMutationAllowed(
LifecycleStatus? status,
WorkOrderMediaCategory category)
{
if (!WorkOrderBoardMutationRules.CanMutateExtraMedia(status, category))
throw new WorkOrderBoardValidationException("ReadOnly", "Work order is read-only in its current status.");
}
/// <summary>
/// Account filter for data queries. Null means org-wide (skip ApplyAccountScope).
/// Call only after EnsureCan* has verified scope is not Missing.

View file

@ -19,7 +19,8 @@ namespace SeaHaven.Services.Interfaces
int workOrderId,
ClaimsPrincipal user,
string? actorId,
CancellationToken cancellationToken = default);
CancellationToken cancellationToken = default,
WorkOrderMediaCategory? category = null);
Task<WorkOrderMediaFileDto> AddMediaAsync(
int workOrderId,

View file

@ -54,6 +54,43 @@ public class WorkOrderBoardMutationRulesTests
scheduleWeekOnly: true));
}
[Fact]
public void CanPatchBoardField_AllowsOnlyFlagColorWhenCompleted()
{
Assert.True(WorkOrderBoardMutationRules.CanPatchBoardField(
LifecycleStatus.Completed, WorkOrderBoardFieldNames.FlagColor));
Assert.False(WorkOrderBoardMutationRules.CanPatchBoardField(
LifecycleStatus.Completed, WorkOrderBoardFieldNames.LifecycleStatus));
Assert.False(WorkOrderBoardMutationRules.CanPatchBoardField(
LifecycleStatus.Completed, WorkOrderBoardFieldNames.SiteCode));
Assert.False(WorkOrderBoardMutationRules.CanPatchBoardField(
LifecycleStatus.Canceled, WorkOrderBoardFieldNames.FlagColor));
Assert.True(WorkOrderBoardMutationRules.CanPatchBoardField(
LifecycleStatus.Scheduled, WorkOrderBoardFieldNames.AssignTo));
}
[Fact]
public void CanMutateComments_FalseOnlyWhenCanceled()
{
Assert.True(WorkOrderBoardMutationRules.CanMutateComments(LifecycleStatus.Completed));
Assert.True(WorkOrderBoardMutationRules.CanMutateComments(LifecycleStatus.Scheduled));
Assert.False(WorkOrderBoardMutationRules.CanMutateComments(LifecycleStatus.Canceled));
}
[Fact]
public void CanMutateExtraMedia_AllowsExtraOnCompleted()
{
Assert.True(WorkOrderBoardMutationRules.CanMutateExtraMedia(
LifecycleStatus.Completed, WorkOrderMediaCategory.Extra));
Assert.True(WorkOrderBoardMutationRules.CanMutateExtraMedia(LifecycleStatus.Completed, null));
Assert.False(WorkOrderBoardMutationRules.CanMutateExtraMedia(
LifecycleStatus.Completed, WorkOrderMediaCategory.Before));
Assert.False(WorkOrderBoardMutationRules.CanMutateExtraMedia(
LifecycleStatus.Completed, WorkOrderMediaCategory.After));
Assert.False(WorkOrderBoardMutationRules.CanMutateExtraMedia(
LifecycleStatus.Canceled, WorkOrderMediaCategory.Extra));
}
[Fact]
public void IsReschedule_WhenPreviousDateExistsAndChanges()
{

View file

@ -0,0 +1,363 @@
using Data.SeaHavenIndustries;
using Data.SeaHavenIndustries.Enums;
using Microsoft.EntityFrameworkCore;
using SeaHaven.DataServices.Implementation;
using SeaHaven.Services.DTOs;
using SeaHaven.Services.Exceptions;
using SeaHaven.Services.Helpers;
using SeaHaven.Services.Implementation;
namespace SeaHavenIndustries.Tests;
public class WorkOrderCompletedSelectiveLockTests
{
private static string ToVersion(WorkOrder workOrder)
=> Convert.ToBase64String(workOrder.RowVersion ?? new byte[] { 1, 0, 0, 0, 0, 0, 0, 1 });
private static (ApplicationDbContext Context, WorkOrderBoardUpdateService Service) CreateBoardSut()
{
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseInMemoryDatabase(Guid.NewGuid().ToString())
.Options;
var context = new ApplicationDbContext(options);
var fieldLocks = new WorkOrderFieldLockService(new WorkOrderFieldLockDataService(context));
var audit = new WorkOrderAuditService(new WorkOrderAuditDataService(context), fieldLocks);
var service = new WorkOrderBoardUpdateService(
new WorkOrderBoardDataService(context),
new WorkOrderBoardMutationDataService(context),
audit);
return (context, service);
}
private static (ApplicationDbContext Context, WorkOrderMediaService Service) CreateMediaSut()
{
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseInMemoryDatabase(Guid.NewGuid().ToString())
.Options;
var context = new ApplicationDbContext(options);
var fieldLocks = new WorkOrderFieldLockService(new WorkOrderFieldLockDataService(context));
var audit = new WorkOrderAuditService(new WorkOrderAuditDataService(context), fieldLocks);
var service = new WorkOrderMediaService(
new WorkOrderMediaDataService(context),
new WorkOrderDetailDataService(context),
audit);
return (context, service);
}
private static WorkOrder CompletedWorkOrder()
=> new()
{
Id = 1,
LifecycleStatus = LifecycleStatus.Completed,
RowVersion = new byte[] { 1, 0, 0, 0, 0, 0, 0, 1 }
};
[Fact]
public async Task PatchField_Completed_AllowsFlagColor()
{
var (context, service) = CreateBoardSut();
var wo = CompletedWorkOrder();
context.workOrders.Add(wo);
await context.SaveChangesAsync();
var result = await service.PatchFieldAsync(1, new WorkOrderBoardPatchRequestDto
{
Field = WorkOrderBoardFieldNames.FlagColor,
Value = "#ef4444",
WorkOrderVersion = ToVersion(wo)
}, "actor-1");
Assert.Equal("#EF4444", result.FlagColor);
}
[Theory]
[InlineData(WorkOrderBoardFieldNames.LifecycleStatus, "Scheduled")]
[InlineData(WorkOrderBoardFieldNames.SiteCode, "BK5")]
[InlineData(WorkOrderBoardFieldNames.AssignTo, "dispatcher-2")]
public async Task PatchField_Completed_BlocksCoreFields(string field, string value)
{
var (context, service) = CreateBoardSut();
var wo = CompletedWorkOrder();
context.workOrders.Add(wo);
await context.SaveChangesAsync();
var ex = await Assert.ThrowsAsync<WorkOrderBoardValidationException>(() =>
service.PatchFieldAsync(1, new WorkOrderBoardPatchRequestDto
{
Field = field,
Value = value,
WorkOrderVersion = ToVersion(wo)
}, "actor-1"));
Assert.Equal("ReadOnly", ex.Code);
}
[Fact]
public async Task PatchField_Canceled_BlocksFlagColor()
{
var (context, service) = CreateBoardSut();
var wo = new WorkOrder
{
Id = 1,
LifecycleStatus = LifecycleStatus.Canceled,
RowVersion = new byte[] { 1, 0, 0, 0, 0, 0, 0, 1 }
};
context.workOrders.Add(wo);
await context.SaveChangesAsync();
var ex = await Assert.ThrowsAsync<WorkOrderBoardValidationException>(() =>
service.PatchFieldAsync(1, new WorkOrderBoardPatchRequestDto
{
Field = WorkOrderBoardFieldNames.FlagColor,
Value = "#ef4444",
WorkOrderVersion = ToVersion(wo)
}, "actor-1"));
Assert.Equal("CanceledReadOnly", ex.Code);
}
[Fact]
public async Task UpdateComment_Completed_UpdatesText()
{
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseInMemoryDatabase(Guid.NewGuid().ToString())
.Options;
var context = new ApplicationDbContext(options);
context.workOrders.Add(CompletedWorkOrder());
context.Users.Add(new ApplicationUser
{
Id = "user-author",
UserName = "author",
FirstName = "Alice",
LastName = "Dispatcher"
});
var comment = new Comments
{
WorkerOrderId = 1,
UserId = "user-author",
Commenttext = "Original",
CommentType = "General",
RecordType = "WorkOrder",
CreatedDate = DateTime.UtcNow
};
context.Comments.Add(comment);
await context.SaveChangesAsync();
var service = new WorkOrderCommentService(
new WorkOrderDetailDataService(context),
new CommentDataService(context),
new UserDataService(context),
WorkOrderAccountTestHelpers.Resolver(context));
var result = await service.UpdateCommentAsync(
1,
comment.Id,
new WorkOrderCommentCreateDto { Text = "Follow-up" },
WorkOrderAccountTestHelpers.OrgWideAdmin("user-author"),
"user-author");
Assert.Equal("Follow-up", result.Text);
}
[Fact]
public async Task AddComment_Completed_AddsText()
{
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseInMemoryDatabase(Guid.NewGuid().ToString())
.Options;
var context = new ApplicationDbContext(options);
context.workOrders.Add(CompletedWorkOrder());
context.Users.Add(new ApplicationUser
{
Id = "user-author",
UserName = "author",
FirstName = "Alice",
LastName = "Dispatcher"
});
await context.SaveChangesAsync();
var service = new WorkOrderCommentService(
new WorkOrderDetailDataService(context),
new CommentDataService(context),
new UserDataService(context),
WorkOrderAccountTestHelpers.Resolver(context));
var result = await service.AddCommentAsync(
1,
new WorkOrderCommentCreateDto { Text = "Follow-up" },
WorkOrderAccountTestHelpers.OrgWideAdmin("user-author"),
"user-author");
Assert.Equal("Follow-up", result.Text);
Assert.Equal("user-author", result.AuthorId);
}
[Fact]
public async Task AddComment_Canceled_ThrowsReadOnly()
{
var options = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseInMemoryDatabase(Guid.NewGuid().ToString())
.Options;
var context = new ApplicationDbContext(options);
context.workOrders.Add(new WorkOrder
{
Id = 1,
LifecycleStatus = LifecycleStatus.Canceled,
RowVersion = new byte[] { 1, 0, 0, 0, 0, 0, 0, 1 }
});
await context.SaveChangesAsync();
var service = new WorkOrderCommentService(
new WorkOrderDetailDataService(context),
new CommentDataService(context),
new UserDataService(context),
WorkOrderAccountTestHelpers.Resolver(context));
var ex = await Assert.ThrowsAsync<WorkOrderBoardValidationException>(() =>
service.AddCommentAsync(
1,
new WorkOrderCommentCreateDto { Text = "Should not persist" },
WorkOrderAccountTestHelpers.OrgWideAdmin("user-author"),
"user-author"));
Assert.Equal("ReadOnly", ex.Code);
Assert.Empty(context.Comments);
}
[Fact]
public async Task AddMedia_Completed_AllowsExtra()
{
var (context, service) = CreateMediaSut();
context.workOrders.Add(CompletedWorkOrder());
await context.SaveChangesAsync();
var media = await service.AddMediaAsync(
1,
WorkOrderMediaCategory.Extra,
"https://example.com/extra.pdf",
WorkOrderAccountTestHelpers.OrgWideAdmin(),
"actor-1");
Assert.True(media.Id > 0);
Assert.Equal(WorkOrderMediaCategory.Extra, media.Category);
}
[Fact]
public async Task AddMedia_Completed_BlocksBefore()
{
var (context, service) = CreateMediaSut();
context.workOrders.Add(CompletedWorkOrder());
await context.SaveChangesAsync();
var ex = await Assert.ThrowsAsync<WorkOrderBoardValidationException>(() =>
service.AddMediaAsync(
1,
WorkOrderMediaCategory.Before,
"https://example.com/before.jpg",
WorkOrderAccountTestHelpers.OrgWideAdmin(),
"actor-1"));
Assert.Equal("ReadOnly", ex.Code);
}
[Fact]
public async Task UpdateMediaCategory_Completed_KeepsExtra()
{
var (context, service) = CreateMediaSut();
var wo = CompletedWorkOrder();
context.workOrders.Add(wo);
context.workOrderAttachments.Add(new WorkOrderAttachments
{
Id = 10,
WorkorderId = 1,
Attachments = "https://example.com/extra.pdf",
Category = WorkOrderMediaCategory.Extra
});
await context.SaveChangesAsync();
var media = await service.UpdateMediaCategoryAsync(
1,
10,
WorkOrderMediaCategory.Extra,
ToVersion(wo),
WorkOrderAccountTestHelpers.OrgWideAdmin(),
"actor-1");
Assert.Equal(WorkOrderMediaCategory.Extra, media.Category);
}
[Fact]
public async Task UpdateMediaCategory_Completed_BlocksMoveToBefore()
{
var (context, service) = CreateMediaSut();
var wo = CompletedWorkOrder();
context.workOrders.Add(wo);
context.workOrderAttachments.Add(new WorkOrderAttachments
{
Id = 10,
WorkorderId = 1,
Attachments = "https://example.com/extra.pdf",
Category = WorkOrderMediaCategory.Extra
});
await context.SaveChangesAsync();
var ex = await Assert.ThrowsAsync<WorkOrderBoardValidationException>(() =>
service.UpdateMediaCategoryAsync(
1,
10,
WorkOrderMediaCategory.Before,
ToVersion(wo),
WorkOrderAccountTestHelpers.OrgWideAdmin(),
"actor-1"));
Assert.Equal("ReadOnly", ex.Code);
}
[Fact]
public async Task DeleteMedia_Completed_AllowsExtra()
{
var (context, service) = CreateMediaSut();
var wo = CompletedWorkOrder();
context.workOrders.Add(wo);
context.workOrderAttachments.Add(new WorkOrderAttachments
{
Id = 10,
WorkorderId = 1,
Attachments = "https://example.com/extra.pdf",
Category = WorkOrderMediaCategory.Extra
});
await context.SaveChangesAsync();
await service.DeleteMediaAsync(
1,
10,
ToVersion(wo),
WorkOrderAccountTestHelpers.OrgWideAdmin(),
"actor-1");
Assert.True(context.workOrderAttachments.Single().IsDeleted);
}
[Fact]
public async Task AddMedia_Canceled_BlocksExtra()
{
var (context, service) = CreateMediaSut();
context.workOrders.Add(new WorkOrder
{
Id = 1,
LifecycleStatus = LifecycleStatus.Canceled,
RowVersion = new byte[] { 1, 0, 0, 0, 0, 0, 0, 1 }
});
await context.SaveChangesAsync();
var ex = await Assert.ThrowsAsync<WorkOrderBoardValidationException>(() =>
service.AddMediaAsync(
1,
WorkOrderMediaCategory.Extra,
"https://example.com/extra.pdf",
WorkOrderAccountTestHelpers.OrgWideAdmin(),
"actor-1"));
Assert.Equal("ReadOnly", ex.Code);
}
}

View file

@ -1017,7 +1017,7 @@ public class WorkOrderMediaServiceTests
context.workOrders.Add(new WorkOrder
{
Id = 1,
LifecycleStatus = LifecycleStatus.Completed,
LifecycleStatus = LifecycleStatus.Canceled,
RowVersion = new byte[] { 1, 0, 0, 0, 0, 0, 0, 1 }
});
context.workOrderAttachments.Add(new WorkOrderAttachments
@ -1566,7 +1566,7 @@ public class WorkOrderMediaServiceTests
await service.EnsureCanMutateMediaAsync(1, AuthenticatedUser(), "actor-1");
var wo = await context.workOrders.SingleAsync(w => w.Id == 1);
wo.LifecycleStatus = LifecycleStatus.Completed;
wo.LifecycleStatus = LifecycleStatus.Canceled;
await context.SaveChangesAsync();
context.ChangeTracker.Clear();

View file

@ -8,12 +8,12 @@
"name": "shoc-backend-cdk",
"version": "0.1.0",
"dependencies": {
"aws-cdk-lib": "2.265.0",
"aws-cdk-lib": "2.266.0",
"constructs": "10.8.1"
},
"devDependencies": {
"@types/node": "26.2.0",
"aws-cdk": "2.1137.0",
"aws-cdk": "2.1138.0",
"typescript": "7.0.2"
},
"engines": {
@ -419,9 +419,9 @@
}
},
"node_modules/aws-cdk": {
"version": "2.1137.0",
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1137.0.tgz",
"integrity": "sha512-SRy2df/5cqGvq8ncph1m93rWEh3RGbvP6uwElw+W0x135vZDzTpfbuq2L8oAHwK2yDt3RNS5sb7Phwb7xqQP2g==",
"version": "2.1138.0",
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1138.0.tgz",
"integrity": "sha512-gZ5F8rmh+qc7ZNWsbaXYoV+p7jSYynRRg70s7FAn3VmzRaSkTE31ijpQHYroxCbDEtKSzgN63ORR/WuZmvXAwA==",
"dev": true,
"license": "Apache-2.0",
"bin": {
@ -432,9 +432,9 @@
}
},
"node_modules/aws-cdk-lib": {
"version": "2.265.0",
"resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.265.0.tgz",
"integrity": "sha512-Ur96d/ZW+gqAW4XtqKQp//D9O/jQvyRoXhAqoHGybpV03qjckGIQcLcGkLV68+jv03TX/rOXHCsa2IEFcCXa4w==",
"version": "2.266.0",
"resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.266.0.tgz",
"integrity": "sha512-sBQU42pEc9ud3yeVU2En2euQRUhCg63eaJIPEVpBtE5aPhJjN3d9MkzQ9eYGLVXP3fnohUE54BiVOdUrkEDUbg==",
"bundleDependencies": [
"@aws/cloudformation-validate",
"@balena/dockerignore",

View file

@ -13,12 +13,12 @@
"deploy": "npm run build && cdk deploy"
},
"dependencies": {
"aws-cdk-lib": "2.265.0",
"aws-cdk-lib": "2.266.0",
"constructs": "10.8.1"
},
"devDependencies": {
"@types/node": "26.2.0",
"aws-cdk": "2.1137.0",
"aws-cdk": "2.1138.0",
"typescript": "7.0.2"
}
}