feat(recipedata): add catalog + load result, wire csharpier

Two concerns bundled into one commit because the csharpier tool restore
reformatted four earlier files as soon as the manifest landed:

- .config/dotnet-tools.json: csharpier 1.2.6 as local tool, follows
  the standard .config layout (dotnet tool restore will find it).
- Anvil/packages.lock.json: produced by dotnet restore, pinned alongside
  the csproj's RestorePackagesWithLockFile=true.
- Anvil/RecipeData/RecipeDataLoadResult.cs: public record carrying the
  adapter's load-pass summary (counts + duration + warnings). Surfaced
  to the SelfTest step and future UI status surfaces.
- Anvil/RecipeData/RecipeDataCatalog.cs: public DI singleton with all
  seven dictionary lookups, the two helper methods (ActionsForClassJob,
  TryResolveActionRowId), and the four meta properties from spec §2.7.
  Backed by a private CatalogState record that the adapter swaps in via
  a volatile reference - readers either see the empty initial state or
  the fully populated post-load snapshot, never a partial mid-build.
  TryResolveActionRowId carries the Cosmic-sentinel logic so hook code
  does not need to know about the CosmicOnly flag itself.
- Reformatting in AnvilAction.cs, ActionMechanicsTable.cs,
  ConditionMechanicsTable.cs, and Anvil.csproj: csharpier-mandated
  line-wrap / blank-line tweaks. No semantic changes.
This commit is contained in:
2026-05-27 20:54:14 +02:00
parent d7e8c42cc7
commit eb5753eea6
8 changed files with 509 additions and 14 deletions
+12
View File
@@ -0,0 +1,12 @@
{
"$schema": "https://json.schemastore.org/dotnet-tools.json",
"version": 1,
"isRoot": true,
"tools": {
"csharpier": {
"version": "1.2.6",
"commands": ["csharpier"],
"rollForward": false
}
}
}
+3 -12
View File
@@ -24,18 +24,9 @@
Include="Microsoft.Extensions.DependencyInjection"
Version="[10.0.7, 11.0.0)"
/>
<PackageReference
Include="Microsoft.Extensions.Hosting"
Version="[10.0.7, 11.0.0)"
/>
<PackageReference
Include="Microsoft.Extensions.Logging"
Version="[10.0.7, 11.0.0)"
/>
<PackageReference
Include="Microsoft.Extensions.Options"
Version="[10.0.7, 11.0.0)"
/>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="[10.0.7, 11.0.0)" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="[10.0.7, 11.0.0)" />
<PackageReference Include="Microsoft.Extensions.Options" Version="[10.0.7, 11.0.0)" />
</ItemGroup>
<ItemGroup>
+1
View File
@@ -103,6 +103,7 @@ public enum AnvilActionFlags : ushort
None = 0,
RequiresGoodOrExcellent = 1 << 0,
RequiresFirstStep = 1 << 1,
// 1 << 2 reserved (see comment above)
SpecialistOnly = 1 << 3,
NoBuffTick = 1 << 4,
@@ -265,7 +265,8 @@ internal static class ActionMechanicsTable
IqStackBonus = 0,
ChargesPerCraft = 1,
GrantsBuff = null,
Flags = AnvilActionFlags.RequiresFirstStep | AnvilActionFlags.RequiresLowLevelRecipe,
Flags =
AnvilActionFlags.RequiresFirstStep | AnvilActionFlags.RequiresLowLevelRecipe,
},
[AnvilActionKind.TrainedFinesse] = new ActionMechanicsEntry
{
@@ -20,7 +20,10 @@ namespace Anvil.RecipeData.Mechanics;
internal static class ConditionMechanicsTable
{
public static IReadOnlyDictionary<AnvilConditionKind, ConditionMechanicsEntry> Entries { get; } =
public static IReadOnlyDictionary<
AnvilConditionKind,
ConditionMechanicsEntry
> Entries { get; } =
new Dictionary<AnvilConditionKind, ConditionMechanicsEntry>
{
[AnvilConditionKind.Normal] = new ConditionMechanicsEntry
+160
View File
@@ -0,0 +1,160 @@
// Public read-only catalog of all Anvil RecipeData. Registered as a DI
// singleton; consumers (simulator, solver, macro engine, IPC, UI) inject
// the instance and pull whichever dictionary they need.
//
// IsLoaded gates everything: the catalog starts empty (no NREs, just empty
// collections) and the LuminaRecipeAdapter swaps in a fully populated state
// snapshot once the sheet walk finishes. UI windows render a spinner until
// IsLoaded flips to true (200-800 ms after plugin start). The swap goes
// through a volatile reference so cross-thread readers see the new state
// atomically without an explicit lock.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
namespace Anvil.RecipeData;
public sealed class RecipeDataCatalog
{
private volatile CatalogState _state = CatalogState.Empty;
// Recipe lookups
public IReadOnlyDictionary<uint, AnvilRecipe> RecipesById => _state.RecipesById;
public IReadOnlyDictionary<uint, IReadOnlyList<AnvilRecipe>> RecipesByOutputItemId =>
_state.RecipesByOutputItemId;
public IReadOnlyDictionary<uint, IReadOnlyList<AnvilRecipe>> RecipesByClassJobId =>
_state.RecipesByClassJobId;
// Item lookups
public IReadOnlyDictionary<uint, AnvilItem> ItemsById => _state.ItemsById;
// Action lookups - two complementary paths.
// ActionsByKind is the simulator's path ("Basic Synthesis" is one entity).
// ActionsByLuminaRowId is the hook's path (UseAction / IsActionHighlighted
// see the per-job RowId from the game). Cosmic actions appear once in
// ActionsByLuminaRowId (their single sentinel RowId from the Action sheet);
// non-Cosmic actions appear eight times (one per crafter job), each
// pointing at the same AnvilAction record.
public IReadOnlyDictionary<AnvilActionKind, AnvilAction> ActionsByKind => _state.ActionsByKind;
public IReadOnlyDictionary<uint, AnvilAction> ActionsByLuminaRowId =>
_state.ActionsByLuminaRowId;
// Buff / Condition catalogs
public IReadOnlyDictionary<AnvilBuffKind, AnvilBuff> BuffsByKind => _state.BuffsByKind;
public IReadOnlyDictionary<AnvilConditionKind, AnvilCondition> ConditionsByKind =>
_state.ConditionsByKind;
// Food / Medicine catalogs (split into two lists because the UI treats
// them as two separate equip slots).
public IReadOnlyList<AnvilFood> Foods => _state.Foods;
public IReadOnlyList<AnvilFood> Medicines => _state.Medicines;
// Meta
public bool IsLoaded => _state.IsLoaded;
public DateTime? LoadedAt => _state.LoadedAt;
public int RecipeCount => _state.RecipesById.Count;
public int ActionCount => _state.ActionsByKind.Count;
// Returns every action a given crafter job has unlocked at the given
// level. Cosmic-only actions (sentinel-keyed under ClassJobId 0) are
// excluded - this helper has no recipe argument and therefore no way
// to gate the Cosmic surface correctly. Consumers that need Cosmic
// actions must combine AnvilRecipe.IsCosmic with
// AnvilActionFlags.CosmicOnly themselves.
public IReadOnlyList<AnvilAction> ActionsForClassJob(uint classJobId, byte characterLevel)
{
if (classJobId == 0)
return Array.Empty<AnvilAction>();
var result = new List<AnvilAction>();
foreach (var action in _state.ActionsByKind.Values)
{
if (!action.RowIdByClassJob.ContainsKey(classJobId))
continue;
if (action.LevelRequired > characterLevel)
continue;
result.Add(action);
}
return result;
}
// Resolves the in-game RowId an UseAction / IsActionHighlighted hook
// needs for a given action + job pairing. Cosmic actions resolve via
// the sentinel RowIdByClassJob[0] (one RowId covers every crafter job);
// every other action resolves via RowIdByClassJob[classJobId]. Returns
// false when the action is not available to the job (e.g. a Cosmic
// action queried on a non-Cosmic recipe, or a per-job action queried
// for a job that is missing the per-job row).
public bool TryResolveActionRowId(AnvilAction action, uint classJobId, out uint rowId)
{
if (action.Flags.HasFlag(AnvilActionFlags.CosmicOnly))
{
if (action.RowIdByClassJob.TryGetValue(0u, out rowId))
return true;
rowId = 0;
return false;
}
if (action.RowIdByClassJob.TryGetValue(classJobId, out rowId))
return true;
rowId = 0;
return false;
}
// Adapter-only entry point. Internal so the LuminaRecipeAdapter (same
// assembly) and the Anvil.Tests build-suite project (via
// InternalsVisibleTo) can populate the catalog. Single atomic write to
// a volatile reference - readers either see the old empty state or the
// fully built new state, never a partial one.
internal void ApplyLoad(CatalogState newState)
{
_state = newState;
}
}
// Immutable snapshot of every catalog bucket. The adapter builds one of
// these and hands it to ApplyLoad. Records' value-semantics plus
// ImmutableDictionary defaults keep the public surface allocation-free
// before the first load.
internal sealed record CatalogState
{
public required bool IsLoaded { get; init; }
public required DateTime? LoadedAt { get; init; }
public required IReadOnlyDictionary<uint, AnvilRecipe> RecipesById { get; init; }
public required IReadOnlyDictionary<
uint,
IReadOnlyList<AnvilRecipe>
> RecipesByOutputItemId { get; init; }
public required IReadOnlyDictionary<
uint,
IReadOnlyList<AnvilRecipe>
> RecipesByClassJobId { get; init; }
public required IReadOnlyDictionary<uint, AnvilItem> ItemsById { get; init; }
public required IReadOnlyDictionary<AnvilActionKind, AnvilAction> ActionsByKind { get; init; }
public required IReadOnlyDictionary<uint, AnvilAction> ActionsByLuminaRowId { get; init; }
public required IReadOnlyDictionary<AnvilBuffKind, AnvilBuff> BuffsByKind { get; init; }
public required IReadOnlyDictionary<
AnvilConditionKind,
AnvilCondition
> ConditionsByKind { get; init; }
public required IReadOnlyList<AnvilFood> Foods { get; init; }
public required IReadOnlyList<AnvilFood> Medicines { get; init; }
public static CatalogState Empty { get; } =
new()
{
IsLoaded = false,
LoadedAt = null,
RecipesById = ImmutableDictionary<uint, AnvilRecipe>.Empty,
RecipesByOutputItemId = ImmutableDictionary<uint, IReadOnlyList<AnvilRecipe>>.Empty,
RecipesByClassJobId = ImmutableDictionary<uint, IReadOnlyList<AnvilRecipe>>.Empty,
ItemsById = ImmutableDictionary<uint, AnvilItem>.Empty,
ActionsByKind = ImmutableDictionary<AnvilActionKind, AnvilAction>.Empty,
ActionsByLuminaRowId = ImmutableDictionary<uint, AnvilAction>.Empty,
BuffsByKind = ImmutableDictionary<AnvilBuffKind, AnvilBuff>.Empty,
ConditionsByKind = ImmutableDictionary<AnvilConditionKind, AnvilCondition>.Empty,
Foods = Array.Empty<AnvilFood>(),
Medicines = Array.Empty<AnvilFood>(),
};
}
+20
View File
@@ -0,0 +1,20 @@
// Result of one LuminaRecipeAdapter load pass. Surfaced to the SelfTest step
// for /xlperf reporting and (later) to any UI status surface that wants to
// show "X recipes / Y actions" after init. Warnings carries non-fatal
// problems the adapter saw while walking the sheets (unknown action names,
// inconsistent per-job rows, etc.) - the load still succeeds and the
// catalog stays usable.
using System;
using System.Collections.Generic;
namespace Anvil.RecipeData;
public sealed record RecipeDataLoadResult(
int RecipesLoaded,
int ItemsLoaded,
int ActionsLoaded,
int FoodsLoaded,
TimeSpan Duration,
IReadOnlyList<string> Warnings
);
+307
View File
@@ -0,0 +1,307 @@
{
"version": 1,
"dependencies": {
"net10.0-windows7.0": {
"DalamudPackager": {
"type": "Direct",
"requested": "[15.0.0, )",
"resolved": "15.0.0",
"contentHash": "411vwC8/X8Z/sQ2TI6v3SvOn66xFPeOjFn3Zn+h0d3Ox2t1kFm66AhDvmx/qcMwVrR+Hidxj0dadpQ2dgyXMBQ=="
},
"DotNet.ReproducibleBuilds": {
"type": "Direct",
"requested": "[1.2.39, )",
"resolved": "1.2.39",
"contentHash": "fcFN01tDTIQqDuTwr1jUQK/geofiwjG5DycJQOnC72i1SsLAk1ELe+apBOuZ11UMQG8YKFZG1FgvjZPbqHyatg=="
},
"Microsoft.Extensions.DependencyInjection": {
"type": "Direct",
"requested": "[10.0.7, 11.0.0)",
"resolved": "10.0.7",
"contentHash": "91F/o3emPV/+xY/ip3s2LqDNF14kjttlVtq0BXgg6p4MnCzeSZxnUJm+t6WRrtD3JdGo88/oX+z7OwK4y8PZuw==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.7"
}
},
"Microsoft.Extensions.Hosting": {
"type": "Direct",
"requested": "[10.0.7, 11.0.0)",
"resolved": "10.0.7",
"contentHash": "M/vBpfWcschvS2EUeq7cHfscsxabiGTptXwV7GeSueovGiSoNjyo1j5PMcWuOAAQrRW3nRqxZk8NeumrmpzUBg==",
"dependencies": {
"Microsoft.Extensions.Configuration": "10.0.7",
"Microsoft.Extensions.Configuration.Abstractions": "10.0.7",
"Microsoft.Extensions.Configuration.Binder": "10.0.7",
"Microsoft.Extensions.Configuration.CommandLine": "10.0.7",
"Microsoft.Extensions.Configuration.EnvironmentVariables": "10.0.7",
"Microsoft.Extensions.Configuration.FileExtensions": "10.0.7",
"Microsoft.Extensions.Configuration.Json": "10.0.7",
"Microsoft.Extensions.Configuration.UserSecrets": "10.0.7",
"Microsoft.Extensions.DependencyInjection": "10.0.7",
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.7",
"Microsoft.Extensions.Diagnostics": "10.0.7",
"Microsoft.Extensions.FileProviders.Abstractions": "10.0.7",
"Microsoft.Extensions.FileProviders.Physical": "10.0.7",
"Microsoft.Extensions.Hosting.Abstractions": "10.0.7",
"Microsoft.Extensions.Logging": "10.0.7",
"Microsoft.Extensions.Logging.Abstractions": "10.0.7",
"Microsoft.Extensions.Logging.Configuration": "10.0.7",
"Microsoft.Extensions.Logging.Console": "10.0.7",
"Microsoft.Extensions.Logging.Debug": "10.0.7",
"Microsoft.Extensions.Logging.EventLog": "10.0.7",
"Microsoft.Extensions.Logging.EventSource": "10.0.7",
"Microsoft.Extensions.Options": "10.0.7"
}
},
"Microsoft.Extensions.Logging": {
"type": "Direct",
"requested": "[10.0.7, 11.0.0)",
"resolved": "10.0.7",
"contentHash": "hOeRIQ63GkgiYCB/MIFp+LQs8aXpJXpB55t6Aj37ab7t2/6WeFcPXxYM9hdy/o5tffzwf8mhqzLJP6mjGYCxjw==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "10.0.7",
"Microsoft.Extensions.Logging.Abstractions": "10.0.7",
"Microsoft.Extensions.Options": "10.0.7"
}
},
"Microsoft.Extensions.Options": {
"type": "Direct",
"requested": "[10.0.7, 11.0.0)",
"resolved": "10.0.7",
"contentHash": "00SHUGTh2jSMvIr6x9Xwd2nE+B5/qFCO/9hDwUDhJsjYRDlADmaBZ7tqehXzBDsfjHSXJzuRHJzPYPPjphBQ7Q==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.7",
"Microsoft.Extensions.Primitives": "10.0.7"
}
},
"Microsoft.Extensions.Configuration": {
"type": "Transitive",
"resolved": "10.0.7",
"contentHash": "wZbGh7J8R1vXN525O6d8dlcDTxhRTnd5MyW4LdfP5S0tSnTwTCseYSrq6g0Mxh7W9xn8P/2xPuf0D/m6k2dy2w==",
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "10.0.7",
"Microsoft.Extensions.Primitives": "10.0.7"
}
},
"Microsoft.Extensions.Configuration.Abstractions": {
"type": "Transitive",
"resolved": "10.0.7",
"contentHash": "t56nEgvECcyLPojZIUFWJknQQDAbgfTf9J+QMYJE1YYvVgz69vN6B/AKL8Grvj3Lcnp8kTpNqwmwFhb3YLJmtQ==",
"dependencies": {
"Microsoft.Extensions.Primitives": "10.0.7"
}
},
"Microsoft.Extensions.Configuration.Binder": {
"type": "Transitive",
"resolved": "10.0.7",
"contentHash": "8bS1qIaRivny+WX+49pmeJ6iAylbtX8C0DLEcCQWZjdxQvLqaMssXiGD9P/6pYElrHbK5/nAHmjbQ8STqdMYeg==",
"dependencies": {
"Microsoft.Extensions.Configuration": "10.0.7",
"Microsoft.Extensions.Configuration.Abstractions": "10.0.7"
}
},
"Microsoft.Extensions.Configuration.CommandLine": {
"type": "Transitive",
"resolved": "10.0.7",
"contentHash": "3lNjglxfFxOzI9zG+3HSg/YSGqo//8Fqw6u6iuIamZb4JCorbA3JLaeWOpfKTAPi2UJwaispOXWx14dUqcGz4A==",
"dependencies": {
"Microsoft.Extensions.Configuration": "10.0.7",
"Microsoft.Extensions.Configuration.Abstractions": "10.0.7"
}
},
"Microsoft.Extensions.Configuration.EnvironmentVariables": {
"type": "Transitive",
"resolved": "10.0.7",
"contentHash": "TWto3imA+mJMLZI+5sbgLiFFoOFNFkizQYNaC5jTuiHKn3diwm1RN7mWDOEZN9kG2bixw7IvgpvtUG5/teSRzA==",
"dependencies": {
"Microsoft.Extensions.Configuration": "10.0.7",
"Microsoft.Extensions.Configuration.Abstractions": "10.0.7"
}
},
"Microsoft.Extensions.Configuration.FileExtensions": {
"type": "Transitive",
"resolved": "10.0.7",
"contentHash": "qbZLvLsoTdArSloEnSxs21P781YUmwVmHc5NJPQD/ezAreQ7884z+6QfAZVKi86WAZtzx83jK2uC4itxOM44gQ==",
"dependencies": {
"Microsoft.Extensions.Configuration": "10.0.7",
"Microsoft.Extensions.Configuration.Abstractions": "10.0.7",
"Microsoft.Extensions.FileProviders.Abstractions": "10.0.7",
"Microsoft.Extensions.FileProviders.Physical": "10.0.7",
"Microsoft.Extensions.Primitives": "10.0.7"
}
},
"Microsoft.Extensions.Configuration.Json": {
"type": "Transitive",
"resolved": "10.0.7",
"contentHash": "64dimvyyKk0dbUbrLg/YCv4ugJ4sVz2aXLwfvZwR1EC4tJqW9ru/oVRcXwoJRa2lQGXtYtlpk4maWOeIb48tQw==",
"dependencies": {
"Microsoft.Extensions.Configuration": "10.0.7",
"Microsoft.Extensions.Configuration.Abstractions": "10.0.7",
"Microsoft.Extensions.Configuration.FileExtensions": "10.0.7",
"Microsoft.Extensions.FileProviders.Abstractions": "10.0.7"
}
},
"Microsoft.Extensions.Configuration.UserSecrets": {
"type": "Transitive",
"resolved": "10.0.7",
"contentHash": "YqVIICoIdl0016wkeO2WQS+uEbEXbUhMLKdC5rZNl1X3nu59F+nwaAHdHjq/4OK+Cx31DYmNUSFh+MUot8qSDw==",
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "10.0.7",
"Microsoft.Extensions.Configuration.Json": "10.0.7",
"Microsoft.Extensions.FileProviders.Abstractions": "10.0.7",
"Microsoft.Extensions.FileProviders.Physical": "10.0.7"
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
"type": "Transitive",
"resolved": "10.0.7",
"contentHash": "Z6mfFEaFcwCfSboxJwOLfu7/31npCY9q70WUamHW/vRQhDvBKOT4Vf9YkZj5J6hLvJpb0oDEYfHunQZj0xxvKw=="
},
"Microsoft.Extensions.Diagnostics": {
"type": "Transitive",
"resolved": "10.0.7",
"contentHash": "l+smp1qPlU0OUXD0OGfdp7OUFrbdq7ZaP5T7m2WpfZ4RFKD7iG73BAT7tjSMxNmbSXkhAn1jYHOAqzYG1r9sNg==",
"dependencies": {
"Microsoft.Extensions.Configuration": "10.0.7",
"Microsoft.Extensions.Diagnostics.Abstractions": "10.0.7",
"Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.7"
}
},
"Microsoft.Extensions.Diagnostics.Abstractions": {
"type": "Transitive",
"resolved": "10.0.7",
"contentHash": "uJ9JP677y+uy+C0vtaSfi7XXgFAdz8DhU3M9lwwIXDfQKcyQ0yxM9DVYa0NXDtdVTYA2eBUtVFZ8LY0GCdeE/w==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.7",
"Microsoft.Extensions.Options": "10.0.7"
}
},
"Microsoft.Extensions.FileProviders.Abstractions": {
"type": "Transitive",
"resolved": "10.0.7",
"contentHash": "teioDgVpi8L186wUfrXQV1YuBt6lCSPmFZiMZo53+FZxHFjOV+f4GXo4LXgJ273Mku9//AdXWVjk9J7eJP6inw==",
"dependencies": {
"Microsoft.Extensions.Primitives": "10.0.7"
}
},
"Microsoft.Extensions.FileProviders.Physical": {
"type": "Transitive",
"resolved": "10.0.7",
"contentHash": "zhgWg/i0ECj5v0jLFBSZHplvc5ygCI91DR4nne+BP4XAKF5ycz0pEKnFiTw8C1jCABJEZsnBZh6pXAvn71kFmw==",
"dependencies": {
"Microsoft.Extensions.FileProviders.Abstractions": "10.0.7",
"Microsoft.Extensions.FileSystemGlobbing": "10.0.7",
"Microsoft.Extensions.Primitives": "10.0.7"
}
},
"Microsoft.Extensions.FileSystemGlobbing": {
"type": "Transitive",
"resolved": "10.0.7",
"contentHash": "NTUspqB+vH9g4wAD6KPOBx01xqYuKXR/cHXm449zpbq1GqfjdAxBmg7eJXrNsPw7SKwIdT2cJ05GxYVvc+lvsA=="
},
"Microsoft.Extensions.Hosting.Abstractions": {
"type": "Transitive",
"resolved": "10.0.7",
"contentHash": "5s8d6qC6EA8UOI4wR/+zlsq7SXttJMRb9d7zvVZ7+bE3CQEfVtC9ITUDCommm87R1zzj6WJBbCnztuIJXnP3DA==",
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "10.0.7",
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.7",
"Microsoft.Extensions.Diagnostics.Abstractions": "10.0.7",
"Microsoft.Extensions.FileProviders.Abstractions": "10.0.7",
"Microsoft.Extensions.Logging.Abstractions": "10.0.7"
}
},
"Microsoft.Extensions.Logging.Abstractions": {
"type": "Transitive",
"resolved": "10.0.7",
"contentHash": "tIEcQ2gvERrH2KiCjdsVcHGhXt9lIsuDStfOIeZWr7/fP8IXhGiYfx0/80PNI7WPO2IYuFtlZLSlnTS8+/Mchw==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.7"
}
},
"Microsoft.Extensions.Logging.Configuration": {
"type": "Transitive",
"resolved": "10.0.7",
"contentHash": "7BBnoGF37USiu7j434put9mDp7EjdlNDIZsR4vHfC1FbLZeLqiWjgJbeEtF0p59Ryqt8AtraHawf0ZKbe5jibg==",
"dependencies": {
"Microsoft.Extensions.Configuration": "10.0.7",
"Microsoft.Extensions.Configuration.Abstractions": "10.0.7",
"Microsoft.Extensions.Configuration.Binder": "10.0.7",
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.7",
"Microsoft.Extensions.Logging": "10.0.7",
"Microsoft.Extensions.Logging.Abstractions": "10.0.7",
"Microsoft.Extensions.Options": "10.0.7",
"Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.7"
}
},
"Microsoft.Extensions.Logging.Console": {
"type": "Transitive",
"resolved": "10.0.7",
"contentHash": "DA++Es6v6W0HfrOrw+K8WyN6jNnZHp640PDdEvl8yfeVmgflKdn6vSSFvufNUSOuY+M2ZaSUgfY+jUKtNpXcCw==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.7",
"Microsoft.Extensions.Logging": "10.0.7",
"Microsoft.Extensions.Logging.Abstractions": "10.0.7",
"Microsoft.Extensions.Logging.Configuration": "10.0.7",
"Microsoft.Extensions.Options": "10.0.7"
}
},
"Microsoft.Extensions.Logging.Debug": {
"type": "Transitive",
"resolved": "10.0.7",
"contentHash": "Y6DSt/JZApunYWKqTtqbdsR6iqAvHx3D0tavbNJ1rnC24MUpF+3XO/VKgFi+9PFqMyvQ2GHBBGb8H3cLSw7rDg==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.7",
"Microsoft.Extensions.Logging": "10.0.7",
"Microsoft.Extensions.Logging.Abstractions": "10.0.7"
}
},
"Microsoft.Extensions.Logging.EventLog": {
"type": "Transitive",
"resolved": "10.0.7",
"contentHash": "1C8eTuxF6BLncNSJ1HCfmaBcjpUSqQDPlBVdYTlet9oldHTPpNh9iatxSJLs8TOqdp/FOpH+nSLdBve7fu9mTQ==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.7",
"Microsoft.Extensions.Logging": "10.0.7",
"Microsoft.Extensions.Logging.Abstractions": "10.0.7",
"Microsoft.Extensions.Options": "10.0.7",
"System.Diagnostics.EventLog": "10.0.7"
}
},
"Microsoft.Extensions.Logging.EventSource": {
"type": "Transitive",
"resolved": "10.0.7",
"contentHash": "YWfndnDX1jVMGCN8d5T+rO+BO8sDw6BkYlUk0BYui+WP7+HhlWx8QLdA4yUDjrkGVb3AQxIWWEPVKw5Nnfj5GQ==",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.7",
"Microsoft.Extensions.Logging": "10.0.7",
"Microsoft.Extensions.Logging.Abstractions": "10.0.7",
"Microsoft.Extensions.Options": "10.0.7",
"Microsoft.Extensions.Primitives": "10.0.7"
}
},
"Microsoft.Extensions.Options.ConfigurationExtensions": {
"type": "Transitive",
"resolved": "10.0.7",
"contentHash": "IT7f+EMXZtkjatEcF+o6aOw/7OE4etRrMiDGEWH/iiTu2R3uhC4NEQJCfHiibtX45U3sIQ5Fh6tbb1qaOz3YAg==",
"dependencies": {
"Microsoft.Extensions.Configuration.Abstractions": "10.0.7",
"Microsoft.Extensions.Configuration.Binder": "10.0.7",
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.7",
"Microsoft.Extensions.Options": "10.0.7",
"Microsoft.Extensions.Primitives": "10.0.7"
}
},
"Microsoft.Extensions.Primitives": {
"type": "Transitive",
"resolved": "10.0.7",
"contentHash": "D5M0Jr551iTgwkZMN9rm0pSkgNLj5quUWQUmQPMZh7k/bnvZTnXRGfE2KuvXf1EEjt/ofD9yw9IumpgdP9QCnw=="
},
"System.Diagnostics.EventLog": {
"type": "Transitive",
"resolved": "10.0.7",
"contentHash": "WbmDLeTPYhEzXhvYVioTVn/D1XX6bovyny9n5p8Zxtf03+eY385RB818teZm6n+fA63iZNvng0/Np4tLuhkMhQ=="
}
}
}
}