Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
- Added RuntimeFactsNdjsonReader for reading NDJSON formatted runtime facts. - Introduced IRuntimeFactsIngestionService interface and its implementation. - Enhanced Program.cs to register new services and endpoints for runtime facts. - Updated CallgraphIngestionService to include CAS URI in stored artifacts. - Created RuntimeFactsValidationException for validation errors during ingestion. - Added tests for RuntimeFactsIngestionService and RuntimeFactsNdjsonReader. - Implemented SignalsSealedModeMonitor for compliance checks in sealed mode. - Updated project dependencies for testing utilities.
209 lines
7.3 KiB
C#
209 lines
7.3 KiB
C#
namespace StellaOps.Scanner.Analyzers.Lang.Deno.Internal;
|
|
|
|
internal sealed class DenoLockFile
|
|
{
|
|
private DenoLockFile(
|
|
string absolutePath,
|
|
string relativePath,
|
|
string version,
|
|
ImmutableDictionary<string, string> remoteEntries,
|
|
ImmutableDictionary<string, string> redirectEntries,
|
|
ImmutableDictionary<string, DenoLockNpmPackage> npmPackages,
|
|
ImmutableDictionary<string, string> npmSpecifiers)
|
|
{
|
|
AbsolutePath = Path.GetFullPath(absolutePath);
|
|
RelativePath = DenoPathUtilities.NormalizeRelativePath(relativePath);
|
|
Version = version;
|
|
RemoteEntries = remoteEntries;
|
|
Redirects = redirectEntries;
|
|
NpmPackages = npmPackages;
|
|
NpmSpecifiers = npmSpecifiers;
|
|
}
|
|
|
|
public string AbsolutePath { get; }
|
|
|
|
public string RelativePath { get; }
|
|
|
|
public string Version { get; }
|
|
|
|
public ImmutableDictionary<string, string> RemoteEntries { get; }
|
|
|
|
public ImmutableDictionary<string, string> Redirects { get; }
|
|
|
|
public ImmutableDictionary<string, DenoLockNpmPackage> NpmPackages { get; }
|
|
|
|
public ImmutableDictionary<string, string> NpmSpecifiers { get; }
|
|
|
|
public static bool TryLoad(string absolutePath, string relativePath, out DenoLockFile? lockFile)
|
|
{
|
|
lockFile = null;
|
|
if (string.IsNullOrWhiteSpace(absolutePath))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
try
|
|
{
|
|
using var stream = File.OpenRead(absolutePath);
|
|
using var json = JsonDocument.Parse(stream, new JsonDocumentOptions
|
|
{
|
|
AllowTrailingCommas = true,
|
|
CommentHandling = JsonCommentHandling.Skip,
|
|
});
|
|
|
|
var root = json.RootElement;
|
|
var version = root.TryGetProperty("version", out var versionElement) && versionElement.ValueKind == JsonValueKind.String
|
|
? versionElement.GetString() ?? "unknown"
|
|
: "unknown";
|
|
|
|
var remote = ParseRemoteEntries(root);
|
|
var redirects = ParseRedirects(root);
|
|
var (npmPackages, npmSpecifiers) = ParseNpmEntries(root);
|
|
|
|
lockFile = new DenoLockFile(
|
|
absolutePath,
|
|
relativePath,
|
|
version,
|
|
remote,
|
|
redirects,
|
|
npmPackages,
|
|
npmSpecifiers);
|
|
return true;
|
|
}
|
|
catch (IOException)
|
|
{
|
|
return false;
|
|
}
|
|
catch (JsonException)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private static ImmutableDictionary<string, string> ParseRemoteEntries(JsonElement root)
|
|
{
|
|
if (!root.TryGetProperty("remote", out var remoteElement) || remoteElement.ValueKind != JsonValueKind.Object)
|
|
{
|
|
return ImmutableDictionary<string, string>.Empty;
|
|
}
|
|
|
|
var builder = ImmutableDictionary.CreateBuilder<string, string>(StringComparer.Ordinal);
|
|
foreach (var entry in remoteElement.EnumerateObject())
|
|
{
|
|
if (entry.Value.ValueKind == JsonValueKind.String)
|
|
{
|
|
builder[entry.Name] = entry.Value.GetString() ?? string.Empty;
|
|
}
|
|
}
|
|
|
|
return builder.ToImmutable();
|
|
}
|
|
|
|
private static ImmutableDictionary<string, string> ParseRedirects(JsonElement root)
|
|
{
|
|
if (!root.TryGetProperty("redirects", out var redirectsElement) || redirectsElement.ValueKind != JsonValueKind.Object)
|
|
{
|
|
return ImmutableDictionary<string, string>.Empty;
|
|
}
|
|
|
|
var builder = ImmutableDictionary.CreateBuilder<string, string>(StringComparer.Ordinal);
|
|
foreach (var entry in redirectsElement.EnumerateObject())
|
|
{
|
|
if (entry.Value.ValueKind == JsonValueKind.String)
|
|
{
|
|
builder[entry.Name] = entry.Value.GetString() ?? string.Empty;
|
|
}
|
|
}
|
|
|
|
return builder.ToImmutable();
|
|
}
|
|
|
|
private static (ImmutableDictionary<string, DenoLockNpmPackage> Packages, ImmutableDictionary<string, string> Specifiers) ParseNpmEntries(JsonElement root)
|
|
{
|
|
if (!root.TryGetProperty("npm", out var npmElement) || npmElement.ValueKind != JsonValueKind.Object)
|
|
{
|
|
return (ImmutableDictionary<string, DenoLockNpmPackage>.Empty, ImmutableDictionary<string, string>.Empty);
|
|
}
|
|
|
|
var packages = ImmutableDictionary.CreateBuilder<string, DenoLockNpmPackage>(StringComparer.OrdinalIgnoreCase);
|
|
if (npmElement.TryGetProperty("packages", out var packagesElement) && packagesElement.ValueKind == JsonValueKind.Object)
|
|
{
|
|
foreach (var packageEntry in packagesElement.EnumerateObject())
|
|
{
|
|
if (packageEntry.Value.ValueKind != JsonValueKind.Object)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var package = DenoLockNpmPackage.Create(packageEntry.Name, packageEntry.Value);
|
|
if (package is not null)
|
|
{
|
|
packages[package.EntryId] = package;
|
|
}
|
|
}
|
|
}
|
|
|
|
var specifiers = ImmutableDictionary.CreateBuilder<string, string>(StringComparer.Ordinal);
|
|
if (npmElement.TryGetProperty("specifiers", out var specifiersElement) && specifiersElement.ValueKind == JsonValueKind.Object)
|
|
{
|
|
foreach (var specifier in specifiersElement.EnumerateObject())
|
|
{
|
|
if (specifier.Value.ValueKind == JsonValueKind.String)
|
|
{
|
|
specifiers[specifier.Name] = specifier.Value.GetString() ?? string.Empty;
|
|
}
|
|
}
|
|
}
|
|
|
|
return (packages.ToImmutable(), specifiers.ToImmutable());
|
|
}
|
|
}
|
|
|
|
internal sealed class DenoLockNpmPackage
|
|
{
|
|
private DenoLockNpmPackage(
|
|
string entryId,
|
|
string integrity,
|
|
ImmutableDictionary<string, string> dependencies)
|
|
{
|
|
EntryId = entryId;
|
|
Integrity = integrity;
|
|
Dependencies = dependencies;
|
|
}
|
|
|
|
public string EntryId { get; }
|
|
|
|
public string Integrity { get; }
|
|
|
|
public ImmutableDictionary<string, string> Dependencies { get; }
|
|
|
|
public static DenoLockNpmPackage? Create(string entryId, JsonElement element)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(entryId) || element.ValueKind != JsonValueKind.Object)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var integrity = element.TryGetProperty("integrity", out var integrityElement) && integrityElement.ValueKind == JsonValueKind.String
|
|
? integrityElement.GetString() ?? string.Empty
|
|
: string.Empty;
|
|
|
|
var dependencies = ImmutableDictionary<string, string>.Empty;
|
|
if (element.TryGetProperty("dependencies", out var dependenciesElement) && dependenciesElement.ValueKind == JsonValueKind.Object)
|
|
{
|
|
var builder = ImmutableDictionary.CreateBuilder<string, string>(StringComparer.OrdinalIgnoreCase);
|
|
foreach (var dependency in dependenciesElement.EnumerateObject())
|
|
{
|
|
if (dependency.Value.ValueKind == JsonValueKind.String)
|
|
{
|
|
builder[dependency.Name] = dependency.Value.GetString() ?? string.Empty;
|
|
}
|
|
}
|
|
|
|
dependencies = builder.ToImmutable();
|
|
}
|
|
|
|
return new DenoLockNpmPackage(entryId, integrity, dependencies);
|
|
}
|
|
}
|