Refactor compare-view component to use observables for data loading, enhancing performance and responsiveness. Update compare service interfaces and methods for improved delta computation. Modify audit log component to handle optional event properties gracefully. Optimize Monaco editor worker loading to reduce bundle size. Introduce shared SCSS mixins for consistent styling across components. Add Gitea test instance setup and NuGet package publishing test scripts for CI/CD validation. Update documentation paths and ensure all references are accurate.

This commit is contained in:
StellaOps Bot
2025-12-26 21:39:17 +02:00
parent b4fc66feb6
commit 75de089ee8
61 changed files with 1318 additions and 958 deletions

View File

@@ -8,7 +8,7 @@ Design and maintain deterministic benchmark suites that measure StellaOps perfor
- ImpactIndex/Scheduler/Scanner/Policy Engine workload simulations referenced in tasks.
- Benchmark configuration and warm-up scripts used by DevOps for regression tracking.
- Documentation of benchmark methodology and expected baseline metrics.
- Determinism bench harness lives at `Determinism/` with optional reachability hashing; CI wrapper at `scripts/bench/determinism-run.sh` (threshold via `BENCH_DETERMINISM_THRESHOLD`). Include feeds via `DET_EXTRA_INPUTS`; optional reachability hashes via `DET_REACH_GRAPHS`/`DET_REACH_RUNTIME`.
- Determinism bench harness lives at `Determinism/` with optional reachability hashing; CI wrapper at `.gitea/scripts/test/determinism-run.sh` (threshold via `BENCH_DETERMINISM_THRESHOLD`). Include feeds via `DET_EXTRA_INPUTS`; optional reachability hashes via `DET_REACH_GRAPHS`/`DET_REACH_RUNTIME`.
## Required Reading
- `docs/modules/platform/architecture-overview.md`

View File

@@ -75,7 +75,7 @@ Version comparators must be tested with 50+ cases per distro. See:
- Storage: `StellaOps.Concelier.Storage.Postgres.Tests` (use in-memory or Testcontainers; determinism on ordering/hashes).
- Observability/analyzers: tests in `__Analyzers` or respective test projects.
- Tests must assert determinism (stable ordering/hashes), tenant guards, AOC invariants, and no derived fields in ingestion.
- Prefer seeded fixtures under `seed-data/` for repeatability; avoid network in tests.
- Prefer seeded fixtures under `src/__Tests/__Datasets/seed-data/` for repeatability; avoid network in tests.
## Delivery Discipline
- Update sprint tracker status (`TODO → DOING → DONE/BLOCKED`) when you start/finish/block work; mirror decisions in Execution Log and Decisions & Risks.

View File

@@ -59,7 +59,7 @@
- Adapter regression: deterministic fixtures for Trivy DB/Java DB, mirror delta/base comparison, OCI manifest generation; no network.
- Risk bundle pipeline: tests in `StellaOps.ExportCenter.RiskBundles.Tests` (or add) covering bundle layout, DSSE signatures, checksum publication.
- Determinism checks: stable ordering/hashes in manifests, provenance, and distribution descriptors; retry paths must not duplicate outputs.
- Keep tests air-gap friendly; seeded data under `seed-data/` or inline fixtures.
- Keep tests air-gap friendly; seeded data under `src/__Tests/__Datasets/seed-data/` or inline fixtures.
## Delivery Discipline
- Update sprint tracker statuses (`TODO → DOING → DONE/BLOCKED`) in `docs/implplan/SPRINT_0164_0001_0001_exportcenter_iii.md` when starting/finishing/blocking work; mirror design decisions in Decisions & Risks and Execution Log.

View File

@@ -16,7 +16,7 @@ Provide strongly-typed configuration helpers for Scanner/Zastava components, enc
- `docs/modules/scanner/design/surface-validation.md`
- `docs/modules/scanner/architecture.md`
- `docs/modules/zastava/architecture.md`
- Deployment guides (`deploy/README.md`, `ops/devops/TASKS.md`) referencing scanner env vars.
- Deployment guides (`devops/docs/README.md`) referencing scanner env vars.
## Working Agreement
1. **State sync**: mark tasks `DOING`/`DONE` in both sprint file `/docs/implplan/SPRINT_*.md` and local `TASKS.md` before/after changes.

View File

@@ -5,12 +5,10 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
cd "$REPO_ROOT"
# Restore only filtered projects using offline/local feed
NUGET_PACKAGES="$REPO_ROOT/offline/packages" \
# Restore using standard NuGet cache
DOTNET_RESTORE_DISABLE_PARALLEL=true \
DOTNET_SYSTEM_NET_HTTP_USESOCKETSHTTPHANDLER=0 \
dotnet restore src/Scanner/StellaOps.Scanner.Node.slnf \
-p:RestorePackagesPath="$REPO_ROOT/offline/packages" \
-p:ContinuousIntegrationBuild=true
# Run node analyzer tests in isolation (minimal logging)
@@ -21,7 +19,6 @@ fi
DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 \
DOTNET_CLI_TELEMETRY_OPTOUT=1 \
NUGET_PACKAGES="$REPO_ROOT/offline/packages" \
dotnet test src/Scanner/StellaOps.Scanner.Node.slnf \
--no-restore \
--settings "$REPO_ROOT/src/Scanner/__Tests/node-isolated.runsettings" \

View File

@@ -1,241 +0,0 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{B487748B-DCC0-5C86-A5D8-C17BCF7CE71E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Infrastructure.Postgres.Testing", "src\__Tests\__Libraries\StellaOps.Infrastructure.Postgres.Testing\StellaOps.Infrastructure.Postgres.Testing.csproj", "{B7CA7A16-AAFB-5A8F-B598-0284ED7DF744}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Messaging.Testing", "src\__Tests\__Libraries\StellaOps.Messaging.Testing\StellaOps.Messaging.Testing.csproj", "{2E7B8D21-CAD8-5844-B59F-7A487E6594DD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Router.Testing", "src\__Tests\__Libraries\StellaOps.Router.Testing\StellaOps.Router.Testing.csproj", "{F30EF61D-A7FC-5689-A06F-42A152CF7393}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Testing.AirGap", "src\__Tests\__Libraries\StellaOps.Testing.AirGap\StellaOps.Testing.AirGap.csproj", "{96610609-85C7-5F09-B765-A86463A8DBDE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Testing.Determinism", "src\__Tests\__Libraries\StellaOps.Testing.Determinism\StellaOps.Testing.Determinism.csproj", "{E5A69860-1704-5FB1-BFA3-5872182D4829}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Testing.Determinism.Properties", "src\__Tests\__Libraries\StellaOps.Testing.Determinism.Properties\StellaOps.Testing.Determinism.Properties.csproj", "{1F5FFF7C-AF58-5C3E-9981-EE5E978426E8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Testing.Manifests", "src\__Tests\__Libraries\StellaOps.Testing.Manifests\StellaOps.Testing.Manifests.csproj", "{51652C28-0583-5556-A941-D16D99F97B82}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Architecture.Tests", "src\__Tests\architecture\StellaOps.Architecture.Tests\StellaOps.Architecture.Tests.csproj", "{068138BD-177D-5359-B0DD-A369BB607E95}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Chaos.Router.Tests", "src\__Tests\chaos\StellaOps.Chaos.Router.Tests\StellaOps.Chaos.Router.Tests.csproj", "{91306E2D-A310-50D1-B64F-47A158D42085}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Integration.AirGap", "src\__Tests\Integration\StellaOps.Integration.AirGap\StellaOps.Integration.AirGap.csproj", "{F2126F28-8343-5BEB-BE5D-D0E4F7CA1A93}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Integration.Determinism", "src\__Tests\Integration\StellaOps.Integration.Determinism\StellaOps.Integration.Determinism.csproj", "{59234A8C-D502-5965-AAFC-19739C833885}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Integration.E2E", "src\__Tests\Integration\StellaOps.Integration.E2E\StellaOps.Integration.E2E.csproj", "{2CE72B3D-4D13-500A-A44D-76029069C773}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Integration.Performance", "src\__Tests\Integration\StellaOps.Integration.Performance\StellaOps.Integration.Performance.csproj", "{422C9F81-D3AB-5EFC-A6CD-245C7FA24ADF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Integration.Platform", "src\__Tests\Integration\StellaOps.Integration.Platform\StellaOps.Integration.Platform.csproj", "{8F7505CD-473C-590A-8851-FA762AB5E214}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Integration.ProofChain", "src\__Tests\Integration\StellaOps.Integration.ProofChain\StellaOps.Integration.ProofChain.csproj", "{B2ABA214-83FB-5E9E-8AD4-2D54E579310A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Integration.Reachability", "src\__Tests\Integration\StellaOps.Integration.Reachability\StellaOps.Integration.Reachability.csproj", "{3EC6A343-75E8-511F-A767-8FAB9EC79A62}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Integration.Unknowns", "src\__Tests\Integration\StellaOps.Integration.Unknowns\StellaOps.Integration.Unknowns.csproj", "{37DF1BF6-AD9C-59A2-8F10-512ABE804ED3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Interop.Tests", "src\__Tests\interop\StellaOps.Interop.Tests\StellaOps.Interop.Tests.csproj", "{A93B89A8-E39D-560B-82E8-96EAEA545A28}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Offline.E2E.Tests", "src\__Tests\offline\StellaOps.Offline.E2E.Tests\StellaOps.Offline.E2E.Tests.csproj", "{DF5A6010-D88B-5327-8E1A-74F2A716D340}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Parity.Tests", "src\__Tests\parity\StellaOps.Parity.Tests\StellaOps.Parity.Tests.csproj", "{C7E0CDBA-5E91-546C-AE25-27D0C82F1A23}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Provenance.Attestation.Tests", "src\__Tests\Provenance\StellaOps.Provenance.Attestation.Tests\StellaOps.Provenance.Attestation.Tests.csproj", "{B143BD73-A4D7-51F3-804E-03CE8C6CF639}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Reachability.FixtureTests", "src\__Tests\reachability\StellaOps.Reachability.FixtureTests\StellaOps.Reachability.FixtureTests.csproj", "{53EEFE3D-CE01-598F-9EE0-49DF5F6806BF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Security.Tests", "src\__Tests\security\StellaOps.Security.Tests\StellaOps.Security.Tests.csproj", "{96E7DE01-9824-53C8-B4A6-5E8BA4BD42E3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Audit.ReplayToken.Tests", "src\__Tests\StellaOps.Audit.ReplayToken.Tests\StellaOps.Audit.ReplayToken.Tests.csproj", "{FB55B7A8-C0F5-53EE-B9E9-B66F4E4D453B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Evidence.Bundle.Tests", "src\__Tests\StellaOps.Evidence.Bundle.Tests\StellaOps.Evidence.Bundle.Tests.csproj", "{2063D4CC-6C01-5693-B0B9-1376FB928E43}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Microservice.Tests", "src\__Tests\StellaOps.Microservice.Tests\StellaOps.Microservice.Tests.csproj", "{B0A0E3D1-FF2E-5005-B619-4523C2A2C955}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Router.Common.Tests", "src\__Tests\StellaOps.Router.Common.Tests\StellaOps.Router.Common.Tests.csproj", "{004D507B-32A2-5704-8747-412E7B8EFAE4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Router.Config.Tests", "src\__Tests\StellaOps.Router.Config.Tests\StellaOps.Router.Config.Tests.csproj", "{FA6CBA17-E0E7-5C13-ADC3-0FB73949CCE0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Router.Gateway.Tests", "src\__Tests\StellaOps.Router.Gateway.Tests\StellaOps.Router.Gateway.Tests.csproj", "{62186A00-3E04-51EF-9497-258A973D6E24}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Router.Transport.InMemory.Tests", "src\__Tests\StellaOps.Router.Transport.InMemory.Tests\StellaOps.Router.Transport.InMemory.Tests.csproj", "{81DADA98-669F-5B5B-8C31-EA3B5CF77380}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.Router.Transport.Udp.Tests", "src\__Tests\StellaOps.Router.Transport.Udp.Tests\StellaOps.Router.Transport.Udp.Tests.csproj", "{768155E4-8D91-5A02-A006-2B357C033E25}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StellaOps.AuditPack.Tests", "src\__Tests\unit\StellaOps.AuditPack.Tests\StellaOps.AuditPack.Tests.csproj", "{DCA9FEBF-076C-5040-BFE8-1F8A0088DE79}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B7CA7A16-AAFB-5A8F-B598-0284ED7DF744}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B7CA7A16-AAFB-5A8F-B598-0284ED7DF744}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B7CA7A16-AAFB-5A8F-B598-0284ED7DF744}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B7CA7A16-AAFB-5A8F-B598-0284ED7DF744}.Release|Any CPU.Build.0 = Release|Any CPU
{2E7B8D21-CAD8-5844-B59F-7A487E6594DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2E7B8D21-CAD8-5844-B59F-7A487E6594DD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2E7B8D21-CAD8-5844-B59F-7A487E6594DD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2E7B8D21-CAD8-5844-B59F-7A487E6594DD}.Release|Any CPU.Build.0 = Release|Any CPU
{F30EF61D-A7FC-5689-A06F-42A152CF7393}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F30EF61D-A7FC-5689-A06F-42A152CF7393}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F30EF61D-A7FC-5689-A06F-42A152CF7393}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F30EF61D-A7FC-5689-A06F-42A152CF7393}.Release|Any CPU.Build.0 = Release|Any CPU
{96610609-85C7-5F09-B765-A86463A8DBDE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{96610609-85C7-5F09-B765-A86463A8DBDE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{96610609-85C7-5F09-B765-A86463A8DBDE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{96610609-85C7-5F09-B765-A86463A8DBDE}.Release|Any CPU.Build.0 = Release|Any CPU
{E5A69860-1704-5FB1-BFA3-5872182D4829}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E5A69860-1704-5FB1-BFA3-5872182D4829}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E5A69860-1704-5FB1-BFA3-5872182D4829}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E5A69860-1704-5FB1-BFA3-5872182D4829}.Release|Any CPU.Build.0 = Release|Any CPU
{1F5FFF7C-AF58-5C3E-9981-EE5E978426E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1F5FFF7C-AF58-5C3E-9981-EE5E978426E8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1F5FFF7C-AF58-5C3E-9981-EE5E978426E8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1F5FFF7C-AF58-5C3E-9981-EE5E978426E8}.Release|Any CPU.Build.0 = Release|Any CPU
{51652C28-0583-5556-A941-D16D99F97B82}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{51652C28-0583-5556-A941-D16D99F97B82}.Debug|Any CPU.Build.0 = Debug|Any CPU
{51652C28-0583-5556-A941-D16D99F97B82}.Release|Any CPU.ActiveCfg = Release|Any CPU
{51652C28-0583-5556-A941-D16D99F97B82}.Release|Any CPU.Build.0 = Release|Any CPU
{068138BD-177D-5359-B0DD-A369BB607E95}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{068138BD-177D-5359-B0DD-A369BB607E95}.Debug|Any CPU.Build.0 = Debug|Any CPU
{068138BD-177D-5359-B0DD-A369BB607E95}.Release|Any CPU.ActiveCfg = Release|Any CPU
{068138BD-177D-5359-B0DD-A369BB607E95}.Release|Any CPU.Build.0 = Release|Any CPU
{91306E2D-A310-50D1-B64F-47A158D42085}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{91306E2D-A310-50D1-B64F-47A158D42085}.Debug|Any CPU.Build.0 = Debug|Any CPU
{91306E2D-A310-50D1-B64F-47A158D42085}.Release|Any CPU.ActiveCfg = Release|Any CPU
{91306E2D-A310-50D1-B64F-47A158D42085}.Release|Any CPU.Build.0 = Release|Any CPU
{F2126F28-8343-5BEB-BE5D-D0E4F7CA1A93}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F2126F28-8343-5BEB-BE5D-D0E4F7CA1A93}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F2126F28-8343-5BEB-BE5D-D0E4F7CA1A93}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F2126F28-8343-5BEB-BE5D-D0E4F7CA1A93}.Release|Any CPU.Build.0 = Release|Any CPU
{59234A8C-D502-5965-AAFC-19739C833885}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{59234A8C-D502-5965-AAFC-19739C833885}.Debug|Any CPU.Build.0 = Debug|Any CPU
{59234A8C-D502-5965-AAFC-19739C833885}.Release|Any CPU.ActiveCfg = Release|Any CPU
{59234A8C-D502-5965-AAFC-19739C833885}.Release|Any CPU.Build.0 = Release|Any CPU
{2CE72B3D-4D13-500A-A44D-76029069C773}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2CE72B3D-4D13-500A-A44D-76029069C773}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2CE72B3D-4D13-500A-A44D-76029069C773}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2CE72B3D-4D13-500A-A44D-76029069C773}.Release|Any CPU.Build.0 = Release|Any CPU
{422C9F81-D3AB-5EFC-A6CD-245C7FA24ADF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{422C9F81-D3AB-5EFC-A6CD-245C7FA24ADF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{422C9F81-D3AB-5EFC-A6CD-245C7FA24ADF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{422C9F81-D3AB-5EFC-A6CD-245C7FA24ADF}.Release|Any CPU.Build.0 = Release|Any CPU
{8F7505CD-473C-590A-8851-FA762AB5E214}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8F7505CD-473C-590A-8851-FA762AB5E214}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8F7505CD-473C-590A-8851-FA762AB5E214}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8F7505CD-473C-590A-8851-FA762AB5E214}.Release|Any CPU.Build.0 = Release|Any CPU
{B2ABA214-83FB-5E9E-8AD4-2D54E579310A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B2ABA214-83FB-5E9E-8AD4-2D54E579310A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B2ABA214-83FB-5E9E-8AD4-2D54E579310A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B2ABA214-83FB-5E9E-8AD4-2D54E579310A}.Release|Any CPU.Build.0 = Release|Any CPU
{3EC6A343-75E8-511F-A767-8FAB9EC79A62}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3EC6A343-75E8-511F-A767-8FAB9EC79A62}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3EC6A343-75E8-511F-A767-8FAB9EC79A62}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3EC6A343-75E8-511F-A767-8FAB9EC79A62}.Release|Any CPU.Build.0 = Release|Any CPU
{37DF1BF6-AD9C-59A2-8F10-512ABE804ED3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{37DF1BF6-AD9C-59A2-8F10-512ABE804ED3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{37DF1BF6-AD9C-59A2-8F10-512ABE804ED3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{37DF1BF6-AD9C-59A2-8F10-512ABE804ED3}.Release|Any CPU.Build.0 = Release|Any CPU
{A93B89A8-E39D-560B-82E8-96EAEA545A28}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A93B89A8-E39D-560B-82E8-96EAEA545A28}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A93B89A8-E39D-560B-82E8-96EAEA545A28}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A93B89A8-E39D-560B-82E8-96EAEA545A28}.Release|Any CPU.Build.0 = Release|Any CPU
{DF5A6010-D88B-5327-8E1A-74F2A716D340}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DF5A6010-D88B-5327-8E1A-74F2A716D340}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DF5A6010-D88B-5327-8E1A-74F2A716D340}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DF5A6010-D88B-5327-8E1A-74F2A716D340}.Release|Any CPU.Build.0 = Release|Any CPU
{C7E0CDBA-5E91-546C-AE25-27D0C82F1A23}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C7E0CDBA-5E91-546C-AE25-27D0C82F1A23}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C7E0CDBA-5E91-546C-AE25-27D0C82F1A23}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C7E0CDBA-5E91-546C-AE25-27D0C82F1A23}.Release|Any CPU.Build.0 = Release|Any CPU
{B143BD73-A4D7-51F3-804E-03CE8C6CF639}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B143BD73-A4D7-51F3-804E-03CE8C6CF639}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B143BD73-A4D7-51F3-804E-03CE8C6CF639}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B143BD73-A4D7-51F3-804E-03CE8C6CF639}.Release|Any CPU.Build.0 = Release|Any CPU
{53EEFE3D-CE01-598F-9EE0-49DF5F6806BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{53EEFE3D-CE01-598F-9EE0-49DF5F6806BF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{53EEFE3D-CE01-598F-9EE0-49DF5F6806BF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{53EEFE3D-CE01-598F-9EE0-49DF5F6806BF}.Release|Any CPU.Build.0 = Release|Any CPU
{96E7DE01-9824-53C8-B4A6-5E8BA4BD42E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{96E7DE01-9824-53C8-B4A6-5E8BA4BD42E3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{96E7DE01-9824-53C8-B4A6-5E8BA4BD42E3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{96E7DE01-9824-53C8-B4A6-5E8BA4BD42E3}.Release|Any CPU.Build.0 = Release|Any CPU
{FB55B7A8-C0F5-53EE-B9E9-B66F4E4D453B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FB55B7A8-C0F5-53EE-B9E9-B66F4E4D453B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FB55B7A8-C0F5-53EE-B9E9-B66F4E4D453B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FB55B7A8-C0F5-53EE-B9E9-B66F4E4D453B}.Release|Any CPU.Build.0 = Release|Any CPU
{2063D4CC-6C01-5693-B0B9-1376FB928E43}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2063D4CC-6C01-5693-B0B9-1376FB928E43}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2063D4CC-6C01-5693-B0B9-1376FB928E43}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2063D4CC-6C01-5693-B0B9-1376FB928E43}.Release|Any CPU.Build.0 = Release|Any CPU
{B0A0E3D1-FF2E-5005-B619-4523C2A2C955}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B0A0E3D1-FF2E-5005-B619-4523C2A2C955}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B0A0E3D1-FF2E-5005-B619-4523C2A2C955}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B0A0E3D1-FF2E-5005-B619-4523C2A2C955}.Release|Any CPU.Build.0 = Release|Any CPU
{004D507B-32A2-5704-8747-412E7B8EFAE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{004D507B-32A2-5704-8747-412E7B8EFAE4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{004D507B-32A2-5704-8747-412E7B8EFAE4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{004D507B-32A2-5704-8747-412E7B8EFAE4}.Release|Any CPU.Build.0 = Release|Any CPU
{FA6CBA17-E0E7-5C13-ADC3-0FB73949CCE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FA6CBA17-E0E7-5C13-ADC3-0FB73949CCE0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FA6CBA17-E0E7-5C13-ADC3-0FB73949CCE0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FA6CBA17-E0E7-5C13-ADC3-0FB73949CCE0}.Release|Any CPU.Build.0 = Release|Any CPU
{62186A00-3E04-51EF-9497-258A973D6E24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{62186A00-3E04-51EF-9497-258A973D6E24}.Debug|Any CPU.Build.0 = Debug|Any CPU
{62186A00-3E04-51EF-9497-258A973D6E24}.Release|Any CPU.ActiveCfg = Release|Any CPU
{62186A00-3E04-51EF-9497-258A973D6E24}.Release|Any CPU.Build.0 = Release|Any CPU
{81DADA98-669F-5B5B-8C31-EA3B5CF77380}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{81DADA98-669F-5B5B-8C31-EA3B5CF77380}.Debug|Any CPU.Build.0 = Debug|Any CPU
{81DADA98-669F-5B5B-8C31-EA3B5CF77380}.Release|Any CPU.ActiveCfg = Release|Any CPU
{81DADA98-669F-5B5B-8C31-EA3B5CF77380}.Release|Any CPU.Build.0 = Release|Any CPU
{768155E4-8D91-5A02-A006-2B357C033E25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{768155E4-8D91-5A02-A006-2B357C033E25}.Debug|Any CPU.Build.0 = Debug|Any CPU
{768155E4-8D91-5A02-A006-2B357C033E25}.Release|Any CPU.ActiveCfg = Release|Any CPU
{768155E4-8D91-5A02-A006-2B357C033E25}.Release|Any CPU.Build.0 = Release|Any CPU
{DCA9FEBF-076C-5040-BFE8-1F8A0088DE79}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DCA9FEBF-076C-5040-BFE8-1F8A0088DE79}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DCA9FEBF-076C-5040-BFE8-1F8A0088DE79}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DCA9FEBF-076C-5040-BFE8-1F8A0088DE79}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{B7CA7A16-AAFB-5A8F-B598-0284ED7DF744} = {B487748B-DCC0-5C86-A5D8-C17BCF7CE71E}
{2E7B8D21-CAD8-5844-B59F-7A487E6594DD} = {B487748B-DCC0-5C86-A5D8-C17BCF7CE71E}
{F30EF61D-A7FC-5689-A06F-42A152CF7393} = {B487748B-DCC0-5C86-A5D8-C17BCF7CE71E}
{96610609-85C7-5F09-B765-A86463A8DBDE} = {B487748B-DCC0-5C86-A5D8-C17BCF7CE71E}
{E5A69860-1704-5FB1-BFA3-5872182D4829} = {B487748B-DCC0-5C86-A5D8-C17BCF7CE71E}
{1F5FFF7C-AF58-5C3E-9981-EE5E978426E8} = {B487748B-DCC0-5C86-A5D8-C17BCF7CE71E}
{51652C28-0583-5556-A941-D16D99F97B82} = {B487748B-DCC0-5C86-A5D8-C17BCF7CE71E}
{068138BD-177D-5359-B0DD-A369BB607E95} = {B487748B-DCC0-5C86-A5D8-C17BCF7CE71E}
{91306E2D-A310-50D1-B64F-47A158D42085} = {B487748B-DCC0-5C86-A5D8-C17BCF7CE71E}
{F2126F28-8343-5BEB-BE5D-D0E4F7CA1A93} = {B487748B-DCC0-5C86-A5D8-C17BCF7CE71E}
{59234A8C-D502-5965-AAFC-19739C833885} = {B487748B-DCC0-5C86-A5D8-C17BCF7CE71E}
{2CE72B3D-4D13-500A-A44D-76029069C773} = {B487748B-DCC0-5C86-A5D8-C17BCF7CE71E}
{422C9F81-D3AB-5EFC-A6CD-245C7FA24ADF} = {B487748B-DCC0-5C86-A5D8-C17BCF7CE71E}
{8F7505CD-473C-590A-8851-FA762AB5E214} = {B487748B-DCC0-5C86-A5D8-C17BCF7CE71E}
{B2ABA214-83FB-5E9E-8AD4-2D54E579310A} = {B487748B-DCC0-5C86-A5D8-C17BCF7CE71E}
{3EC6A343-75E8-511F-A767-8FAB9EC79A62} = {B487748B-DCC0-5C86-A5D8-C17BCF7CE71E}
{37DF1BF6-AD9C-59A2-8F10-512ABE804ED3} = {B487748B-DCC0-5C86-A5D8-C17BCF7CE71E}
{A93B89A8-E39D-560B-82E8-96EAEA545A28} = {B487748B-DCC0-5C86-A5D8-C17BCF7CE71E}
{DF5A6010-D88B-5327-8E1A-74F2A716D340} = {B487748B-DCC0-5C86-A5D8-C17BCF7CE71E}
{C7E0CDBA-5E91-546C-AE25-27D0C82F1A23} = {B487748B-DCC0-5C86-A5D8-C17BCF7CE71E}
{B143BD73-A4D7-51F3-804E-03CE8C6CF639} = {B487748B-DCC0-5C86-A5D8-C17BCF7CE71E}
{53EEFE3D-CE01-598F-9EE0-49DF5F6806BF} = {B487748B-DCC0-5C86-A5D8-C17BCF7CE71E}
{96E7DE01-9824-53C8-B4A6-5E8BA4BD42E3} = {B487748B-DCC0-5C86-A5D8-C17BCF7CE71E}
{FB55B7A8-C0F5-53EE-B9E9-B66F4E4D453B} = {B487748B-DCC0-5C86-A5D8-C17BCF7CE71E}
{2063D4CC-6C01-5693-B0B9-1376FB928E43} = {B487748B-DCC0-5C86-A5D8-C17BCF7CE71E}
{B0A0E3D1-FF2E-5005-B619-4523C2A2C955} = {B487748B-DCC0-5C86-A5D8-C17BCF7CE71E}
{004D507B-32A2-5704-8747-412E7B8EFAE4} = {B487748B-DCC0-5C86-A5D8-C17BCF7CE71E}
{FA6CBA17-E0E7-5C13-ADC3-0FB73949CCE0} = {B487748B-DCC0-5C86-A5D8-C17BCF7CE71E}
{62186A00-3E04-51EF-9497-258A973D6E24} = {B487748B-DCC0-5C86-A5D8-C17BCF7CE71E}
{81DADA98-669F-5B5B-8C31-EA3B5CF77380} = {B487748B-DCC0-5C86-A5D8-C17BCF7CE71E}
{768155E4-8D91-5A02-A006-2B357C033E25} = {B487748B-DCC0-5C86-A5D8-C17BCF7CE71E}
{DCA9FEBF-076C-5040-BFE8-1F8A0088DE79} = {B487748B-DCC0-5C86-A5D8-C17BCF7CE71E}
EndGlobalSection
EndGlobal

2
src/StellaOps.Tests.slnx Normal file
View File

@@ -0,0 +1,2 @@
<Solution>
</Solution>

View File

@@ -383,7 +383,7 @@ def parse_args(argv: Optional[List[str]] = None) -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Capture CERT-Bund search/export snapshots for Offline Kit packaging.",
)
parser.add_argument("--output", default="seed-data/cert-bund", help="Destination directory for artefacts.")
parser.add_argument("--output", default="src/__Tests/__Datasets/seed-data/cert-bund", help="Destination directory for artefacts.")
parser.add_argument("--start-year", type=int, default=2014, help="First year (inclusive) for export snapshots.")
parser.add_argument(
"--end-year",

View File

@@ -133,4 +133,4 @@ Tests use Testcontainers for PostgreSQL integration testing.
- `docs/operations/postgresql-patterns-runbook.md` - Operational guide
- `docs/implplan/SPRINT_3420_0001_0001_bitemporal_unknowns_schema.md` - Sprint spec
- `deploy/postgres-validation/001_validate_rls.sql` - RLS validation
- `devops/database/postgres/validation/001_validate_rls.sql` - RLS validation

View File

@@ -44,16 +44,17 @@
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
"maximumWarning": "750kb",
"maximumError": "1.5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "6kb",
"maximumError": "12kb"
"maximumWarning": "12kb",
"maximumError": "20kb"
}
],
"outputHashing": "all"
"outputHashing": "all",
"namedChunks": true
},
"development": {
"optimization": false,

View File

@@ -5,6 +5,9 @@
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"build:stats": "ng build --stats-json",
"analyze": "ng build --stats-json && npx esbuild-visualizer --metadata dist/stellaops-web/browser/stats.json --open",
"analyze:source-map": "ng build --source-map && npx source-map-explorer dist/stellaops-web/browser/*.js",
"watch": "ng build --watch --configuration development",
"test": "npm run verify:chromium && ng test --watch=false",
"test:watch": "ng test --watch",

View File

@@ -21,7 +21,7 @@
<span *ngIf="action.targetVersion"> → {{ action.targetVersion }}</span>
</div>
<div matListItemLine *ngIf="action.cveIds?.length" class="cve-list">
CVEs: {{ action.cveIds.join(', ') }}
CVEs: {{ action.cveIds?.join(', ') }}
</div>
<div matListItemLine *ngIf="action.estimatedEffort" class="effort-estimate">
Estimated effort: {{ action.estimatedEffort }}

View File

@@ -89,27 +89,30 @@ export class CompareViewComponent implements OnInit {
}
}
async loadTarget(id: string, type: 'current' | 'baseline'): Promise<void> {
const target = await this.compareService.getTarget(id);
if (type === 'current') {
this.currentTarget.set(target);
} else {
this.baselineTarget.set(target);
// Load baseline rationale
const rationale = await this.compareService.getBaselineRationale(id);
this.baselineRationale.set(rationale);
}
this.loadDelta();
loadTarget(id: string, type: 'current' | 'baseline'): void {
this.compareService.getTarget(id).subscribe(target => {
if (type === 'current') {
this.currentTarget.set(target);
} else {
this.baselineTarget.set(target);
// Load baseline rationale
this.compareService.getBaselineRationale(id).subscribe(rationale => {
this.baselineRationale.set(rationale.selectionReason);
});
}
this.loadDelta();
});
}
async loadDelta(): Promise<void> {
loadDelta(): void {
const current = this.currentTarget();
const baseline = this.baselineTarget();
if (!current || !baseline) return;
const delta = await this.compareService.computeDelta(current.id, baseline.id);
this.categories.set(delta.categories);
this.items.set(delta.items);
this.compareService.computeDelta(current.id, baseline.id).subscribe(delta => {
this.categories.set(delta.categories);
this.items.set(delta.items);
});
}
selectCategory(categoryId: string): void {
@@ -123,17 +126,12 @@ export class CompareViewComponent implements OnInit {
this.loadEvidence(item);
}
async loadEvidence(item: DeltaItem): Promise<void> {
const current = this.currentTarget();
const baseline = this.baselineTarget();
if (!current || !baseline) return;
const evidence = await this.compareService.getItemEvidence(
item.id,
baseline.id,
current.id
);
this.evidence.set(evidence);
loadEvidence(item: DeltaItem): void {
this.compareService.getItemEvidence(item.id).subscribe(panes => {
// Get the first pane or create a placeholder
const evidence = panes.length > 0 ? panes[0] : null;
this.evidence.set(evidence);
});
}
toggleViewMode(): void {
@@ -142,24 +140,25 @@ export class CompareViewComponent implements OnInit {
);
}
getChangeIcon(changeType: 'added' | 'removed' | 'changed'): string {
getChangeIcon(changeType: 'added' | 'removed' | 'changed' | undefined): string {
switch (changeType) {
case 'added': return 'add_circle';
case 'removed': return 'remove_circle';
case 'changed': return 'change_circle';
default: return 'help_outline';
}
}
getChangeClass(changeType: 'added' | 'removed' | 'changed'): string {
return `change-${changeType}`;
getChangeClass(changeType: 'added' | 'removed' | 'changed' | undefined): string {
return changeType ? `change-${changeType}` : 'change-unknown';
}
async exportReport(): Promise<void> {
exportReport(): void {
const current = this.currentTarget();
const baseline = this.baselineTarget();
if (!current || !baseline) return;
await this.exportService.exportJson(
this.exportService.exportJson(
current,
baseline,
this.categories(),

View File

@@ -52,6 +52,7 @@ export interface CompareSession {
* Compare target (current or baseline scan).
*/
export interface CompareTarget {
id: string;
digest: string;
imageRef: string;
scanDate: string;
@@ -59,21 +60,37 @@ export interface CompareTarget {
}
/**
* Delta category for grouping changes.
* Delta category type (string literal).
*/
export type DeltaCategory = 'added' | 'removed' | 'changed' | 'unchanged';
export type DeltaCategoryType = 'added' | 'removed' | 'changed' | 'unchanged';
/**
* Delta category for grouping changes with summary counts.
*/
export interface DeltaCategory {
id: DeltaCategoryType;
name: string;
icon: string;
added: number;
removed: number;
changed: number;
}
/**
* Delta item representing a difference between scans.
*/
export interface DeltaItem {
id: string;
category: DeltaCategory;
category: DeltaCategoryType;
component: string;
cve?: string;
currentSeverity?: string;
baselineSeverity?: string;
description: string;
// Export service expected properties
changeType?: 'added' | 'removed' | 'changed';
title?: string;
severity?: string;
}
/**
@@ -83,6 +100,18 @@ export interface EvidencePane {
digest: string;
data: Record<string, unknown>;
loading: boolean;
// View-specific properties
title?: string;
beforeEvidence?: Record<string, unknown>;
afterEvidence?: Record<string, unknown>;
}
/**
* Result of computing delta between scans.
*/
export interface DeltaResult {
categories: DeltaCategory[];
items: DeltaItem[];
}
@Injectable({ providedIn: 'root' })
@@ -206,10 +235,10 @@ export class CompareService {
}
/**
* Computes delta between current and baseline.
* Result of computing a delta between scans.
*/
computeDelta(currentDigest: string, baselineDigest: string): Observable<DeltaItem[]> {
return this.http.get<DeltaItem[]>(
computeDelta(currentDigest: string, baselineDigest: string): Observable<DeltaResult> {
return this.http.get<DeltaResult>(
`${this.baseUrl}/delta?current=${currentDigest}&baseline=${baselineDigest}`
);
}

View File

@@ -122,9 +122,9 @@ import { StellaOpsScopes } from '../../../core/auth/scopes';
</tr>
</thead>
<tbody>
@for (event of paginatedEvents; track event.id) {
@for (event of paginatedEvents; track event.id ?? event.eventType + event.occurredAt) {
<tr>
<td class="timestamp">{{ formatTimestamp(event.timestamp) }}</td>
<td class="timestamp">{{ formatTimestamp(event.timestamp ?? event.occurredAt) }}</td>
<td>
<span class="event-badge" [class]="getEventClass(event.eventType)">
{{ event.eventType }}
@@ -182,7 +182,7 @@ import { StellaOpsScopes } from '../../../core/auth/scopes';
</div>
<div class="detail-row">
<span class="detail-label">Timestamp:</span>
<span>{{ formatTimestamp(selectedEvent.timestamp) }}</span>
<span>{{ formatTimestamp(selectedEvent.timestamp ?? selectedEvent.occurredAt) }}</span>
</div>
<div class="detail-row">
<span class="detail-label">Event Type:</span>
@@ -208,7 +208,7 @@ import { StellaOpsScopes } from '../../../core/auth/scopes';
</div>
<div class="detail-row">
<span class="detail-label">Metadata:</span>
<pre class="metadata-json">{{ formatMetadata(selectedEvent.metadata) }}</pre>
<pre class="metadata-json">{{ formatMetadata(selectedEvent.metadata ?? {}) }}</pre>
</div>
</div>
</div>

View File

@@ -46,26 +46,21 @@ export class MonacoLoaderService {
/**
* Configure Monaco web workers for language services.
* Ensures deterministic, offline-friendly loading (no CDN usage).
*
* OPTIMIZATION: Only load editor core + JSON worker.
* Removed CSS/HTML/TypeScript workers to save ~3-4MB.
* Stella DSL only needs basic editor + JSON-like validation.
*/
private async configureWorkers(monaco: MonacoNamespace): Promise<void> {
const [editorWorker, cssWorker, htmlWorker, jsonWorker, tsWorker] = await Promise.all([
// Only load essential workers - saves ~3-4MB
const [editorWorker, jsonWorker] = await Promise.all([
import('monaco-editor/esm/vs/editor/editor.worker?worker'),
import('monaco-editor/esm/vs/language/css/css.worker?worker'),
import('monaco-editor/esm/vs/language/html/html.worker?worker'),
import('monaco-editor/esm/vs/language/json/json.worker?worker'),
import('monaco-editor/esm/vs/language/typescript/ts.worker?worker'),
]);
// Minimal worker mapping - all non-JSON languages use base editor worker
const workerByLabel: Record<string, () => Worker> = {
json: () => new (jsonWorker as any).default(),
css: () => new (cssWorker as any).default(),
scss: () => new (cssWorker as any).default(),
less: () => new (cssWorker as any).default(),
html: () => new (htmlWorker as any).default(),
handlebars: () => new (htmlWorker as any).default(),
razor: () => new (htmlWorker as any).default(),
javascript: () => new (tsWorker as any).default(),
typescript: () => new (tsWorker as any).default(),
default: () => new (editorWorker as any).default(),
};

View File

@@ -1,4 +1,8 @@
// Design system imports
@import './styles/tokens/motion';
@import './styles/mixins';
// Monaco Editor styles (lazy-loaded with editor)
@import 'monaco-editor/min/vs/editor/editor.main.css';
/* Global motion helpers */

View File

@@ -0,0 +1,457 @@
// =============================================================================
// Shared SCSS Mixins - Bundle Optimization
// =============================================================================
// These mixins consolidate common patterns to reduce component CSS size.
// Import with: @use 'styles/mixins' as m;
// =============================================================================
// -----------------------------------------------------------------------------
// Design Tokens (CSS Custom Properties fallbacks)
// -----------------------------------------------------------------------------
$color-surface: #ffffff !default;
$color-surface-secondary: #f8fafc !default;
$color-border: #e2e8f0 !default;
$color-text-primary: #1e293b !default;
$color-text-secondary: #64748b !default;
$color-text-muted: #94a3b8 !default;
$color-brand: #4f46e5 !default;
$color-brand-light: rgba(79, 70, 229, 0.1) !default;
// Severity colors
$severity-critical: #dc2626 !default;
$severity-high: #ea580c !default;
$severity-medium: #f59e0b !default;
$severity-low: #22c55e !default;
$severity-info: #3b82f6 !default;
// Spacing
$spacing-xs: 0.25rem !default;
$spacing-sm: 0.5rem !default;
$spacing-md: 1rem !default;
$spacing-lg: 1.5rem !default;
$spacing-xl: 2rem !default;
// Border radius
$radius-sm: 0.375rem !default;
$radius-md: 0.5rem !default;
$radius-lg: 0.75rem !default;
$radius-xl: 1rem !default;
// Shadows
$shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05) !default;
$shadow-md: 0 1px 3px rgba(0, 0, 0, 0.1) !default;
$shadow-lg: 0 4px 6px rgba(0, 0, 0, 0.1) !default;
// -----------------------------------------------------------------------------
// Layout Mixins
// -----------------------------------------------------------------------------
/// Flex container with common settings
@mixin flex-row($gap: $spacing-md, $align: center) {
display: flex;
align-items: $align;
gap: $gap;
}
@mixin flex-col($gap: $spacing-md) {
display: flex;
flex-direction: column;
gap: $gap;
}
@mixin flex-between {
display: flex;
justify-content: space-between;
align-items: center;
}
/// Grid with auto-fit columns
@mixin auto-grid($min-width: 200px, $gap: $spacing-md) {
display: grid;
grid-template-columns: repeat(auto-fit, minmax($min-width, 1fr));
gap: $gap;
}
// -----------------------------------------------------------------------------
// Component Base Mixins
// -----------------------------------------------------------------------------
/// Card/Panel base styling
@mixin card-base($padding: $spacing-md) {
padding: $padding;
background: $color-surface;
border-radius: $radius-lg;
border: 1px solid $color-border;
box-shadow: $shadow-md;
}
/// Panel with header section
@mixin panel-base {
@include card-base($spacing-lg);
}
/// Stat card styling
@mixin stat-card {
@include flex-col($spacing-xs);
align-items: center;
@include card-base;
}
/// Toolbar container
@mixin toolbar {
@include flex-row;
flex-wrap: wrap;
@include card-base;
}
// -----------------------------------------------------------------------------
// Form Element Mixins
// -----------------------------------------------------------------------------
/// Base input styling
@mixin input-base {
padding: $spacing-sm $spacing-md;
border: 1px solid $color-border;
border-radius: $radius-md;
font-size: 0.875rem;
background: $color-surface;
outline: none;
transition: border-color 0.15s, box-shadow 0.15s;
&:focus {
border-color: $color-brand;
box-shadow: 0 0 0 3px $color-brand-light;
}
&::placeholder {
color: $color-text-muted;
}
}
/// Select dropdown
@mixin select-base {
@include input-base;
cursor: pointer;
min-width: 140px;
}
/// Search box container
@mixin search-box($max-width: 400px) {
display: flex;
flex: 1;
min-width: 250px;
max-width: $max-width;
position: relative;
}
/// Filter group (label + control)
@mixin filter-group {
@include flex-col($spacing-xs);
label,
&__label {
font-size: 0.75rem;
color: $color-text-secondary;
font-weight: 500;
}
}
// -----------------------------------------------------------------------------
// Typography Mixins
// -----------------------------------------------------------------------------
@mixin heading-lg {
margin: 0;
font-size: 1.75rem;
font-weight: 600;
color: $color-text-primary;
}
@mixin heading-md {
margin: 0;
font-size: 1.25rem;
font-weight: 600;
color: $color-text-primary;
}
@mixin text-secondary {
color: $color-text-secondary;
font-size: 0.875rem;
}
@mixin text-label {
font-size: 0.75rem;
color: $color-text-secondary;
text-transform: uppercase;
letter-spacing: 0.05em;
}
@mixin text-mono {
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
font-size: 0.8125rem;
}
// -----------------------------------------------------------------------------
// Badge/Chip Mixins
// -----------------------------------------------------------------------------
/// Base badge styling
@mixin badge-base($bg: $color-surface-secondary, $color: $color-text-primary) {
display: inline-flex;
align-items: center;
padding: 0.125rem 0.5rem;
border-radius: 9999px;
font-size: 0.75rem;
font-weight: 500;
background: $bg;
color: $color;
}
/// Severity badge with color variants
@mixin severity-badge($severity) {
$colors: (
'critical': $severity-critical,
'high': $severity-high,
'medium': $severity-medium,
'low': $severity-low,
'info': $severity-info,
);
$color: map-get($colors, $severity);
@if $color {
@include badge-base(rgba($color, 0.1), $color);
border: 1px solid rgba($color, 0.2);
}
}
/// Generate all severity badge classes
@mixin severity-badge-variants {
&--critical,
&.critical {
@include severity-badge('critical');
}
&--high,
&.high {
@include severity-badge('high');
}
&--medium,
&.medium {
@include severity-badge('medium');
}
&--low,
&.low {
@include severity-badge('low');
}
&--info,
&.info {
@include severity-badge('info');
}
}
// -----------------------------------------------------------------------------
// Message/Alert Mixins
// -----------------------------------------------------------------------------
@mixin message-base {
padding: $spacing-md;
border-radius: $radius-md;
font-size: 0.875rem;
}
@mixin message-info {
@include message-base;
background: #e0f2fe;
color: #0369a1;
border: 1px solid #7dd3fc;
}
@mixin message-success {
@include message-base;
background: #dcfce7;
color: #166534;
border: 1px solid #86efac;
}
@mixin message-warning {
@include message-base;
background: #fef3c7;
color: #92400e;
border: 1px solid #fcd34d;
}
@mixin message-error {
@include message-base;
background: #fef2f2;
color: #991b1b;
border: 1px solid #fca5a5;
}
// -----------------------------------------------------------------------------
// Button Mixins
// -----------------------------------------------------------------------------
@mixin btn-base {
display: inline-flex;
align-items: center;
justify-content: center;
gap: $spacing-sm;
padding: $spacing-sm $spacing-md;
border: none;
border-radius: $radius-md;
font-size: 0.875rem;
font-weight: 500;
cursor: pointer;
transition: background-color 0.15s, opacity 0.15s;
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
}
@mixin btn-primary {
@include btn-base;
background: $color-brand;
color: white;
&:hover:not(:disabled) {
background: darken($color-brand, 8%);
}
}
@mixin btn-secondary {
@include btn-base;
background: $color-surface-secondary;
color: $color-text-primary;
border: 1px solid $color-border;
&:hover:not(:disabled) {
background: darken($color-surface-secondary, 3%);
}
}
@mixin btn-ghost {
@include btn-base;
background: transparent;
color: $color-text-secondary;
&:hover:not(:disabled) {
background: $color-surface-secondary;
color: $color-text-primary;
}
}
@mixin btn-icon {
@include btn-ghost;
padding: $spacing-sm;
border-radius: $radius-md;
}
// -----------------------------------------------------------------------------
// Table Mixins
// -----------------------------------------------------------------------------
@mixin table-base {
width: 100%;
border-collapse: collapse;
background: $color-surface;
border-radius: $radius-lg;
overflow: hidden;
}
@mixin table-header {
background: $color-surface-secondary;
font-size: 0.75rem;
font-weight: 600;
color: $color-text-secondary;
text-transform: uppercase;
letter-spacing: 0.05em;
}
@mixin table-cell {
padding: $spacing-md;
border-bottom: 1px solid $color-border;
font-size: 0.875rem;
}
@mixin table-row-hover {
&:hover {
background: $color-surface-secondary;
}
}
// -----------------------------------------------------------------------------
// Scrollbar Mixins
// -----------------------------------------------------------------------------
@mixin custom-scrollbar($width: 8px) {
&::-webkit-scrollbar {
width: $width;
height: $width;
}
&::-webkit-scrollbar-track {
background: transparent;
}
&::-webkit-scrollbar-thumb {
background: $color-border;
border-radius: $width;
&:hover {
background: $color-text-muted;
}
}
}
// -----------------------------------------------------------------------------
// Utility Mixins
// -----------------------------------------------------------------------------
/// Truncate text with ellipsis
@mixin truncate($max-width: 100%) {
max-width: $max-width;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/// Visually hidden but accessible
@mixin visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
/// Loading skeleton
@mixin skeleton {
background: linear-gradient(90deg, $color-surface-secondary 25%, $color-border 50%, $color-surface-secondary 75%);
background-size: 200% 100%;
animation: skeleton-loading 1.5s infinite;
border-radius: $radius-sm;
}
@keyframes skeleton-loading {
0% {
background-position: 200% 0;
}
100% {
background-position: -200% 0;
}
}
/// Empty state container
@mixin empty-state {
@include flex-col;
align-items: center;
justify-content: center;
padding: $spacing-xl * 2;
color: $color-text-muted;
text-align: center;
}

View File

@@ -3,13 +3,13 @@
## Roles
- Backend engineer: maintain the shared PostgreSQL infrastructure primitives (DataSourceBase, RepositoryBase, MigrationRunner, options/DI helpers).
- QA automation: own Postgres Testcontainers coverage, tenant-context/RLS checks, and migration idempotency tests.
- DevOps liaison: keep provisioning values in `ops/devops/postgres` aligned with library defaults (timeouts, schema names, TLS, pooling).
- DevOps liaison: keep provisioning values in `devops/database/postgres` aligned with library defaults (timeouts, schema names, TLS, pooling).
## Required Reading
- docs/db/README.md, SPECIFICATION.md, RULES.md, VERIFICATION.md, CONVERSION_PLAN.md
- docs/modules/platform/architecture-overview.md
- docs/airgap/airgap-mode.md
- ops/devops/AGENTS.md (DevOps working agreement)
- devops/AGENTS.md (DevOps working agreement)
## Working Directory & Scope
- Primary: `src/__Libraries/StellaOps.Infrastructure.Postgres`
@@ -28,5 +28,5 @@
- Treat analyzer warnings as errors; ensure nullable enabled and `LangVersion` follows repo default.
## Handoff Notes
- Align configuration defaults with the provisioning values under `ops/devops/postgres` (ports, pool sizes, SSL/TLS).
- Align configuration defaults with the provisioning values under `devops/database/postgres` (ports, pool sizes, SSL/TLS).
- Update this AGENTS file whenever connection/session rules or provisioning defaults change; record updates in the sprint Execution Log.

View File

@@ -13,7 +13,7 @@ portal.
## Recommended layout
```
seed-data/cert-bund/
src/__Tests/__Datasets/seed-data/cert-bund/
├── search/ # paginated search JSON files
│   ├── certbund-search-page-00.json
│   └── …
@@ -36,7 +36,7 @@ Run the helper under `src/Tools/` to capture fresh snapshots or regenerate
the manifest:
```
python src/Tools/certbund_offline_snapshot.py --output seed-data/cert-bund
python src/Tools/certbund_offline_snapshot.py --output src/__Tests/__Datasets/seed-data/cert-bund
```
See the connector operations guide

View File

@@ -13,10 +13,10 @@ This directory contains HTML snapshots of the KISA/KNVD advisory detail pages (`
## Regeneration
```bash
python scripts/kisa_capture_html.py --out seed-data/kisa/html
python devops/tools/kisa_capture_html.py --out src/__Tests/__Datasets/seed-data/kisa/html
```
(See `scripts/kisa_capture_html.py` for exact implementation; it parses the RSS feed, walks each `IDX`, and writes `IDX.html` alongside a sha256 manifest.)
(See `devops/tools/kisa_capture_html.py` for exact implementation; it parses the RSS feed, walks each `IDX`, and writes `IDX.html` alongside a sha256 manifest.)
## sha256 manifest