diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..68f9515 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,49 @@ +name: Bug report +description: Report a defect in a FunctionFoundry package +labels: ["bug"] +body: + - type: dropdown + id: package + attributes: + label: Package + options: + - FunctionFoundry.Security + - FunctionFoundry.Storage + - FunctionFoundry.Integrity + - FunctionFoundry.Observability + - FunctionFoundry.Data + - FunctionFoundry.Text + - FunctionFoundry.Resilience + - FunctionFoundry.Networking + - FunctionFoundry.Distributed + - FunctionFoundry.Scheduling + - Repository / CI / docs + validations: + required: true + - type: input + id: version + attributes: + label: Package version + placeholder: 1.0.0 + validations: + required: true + - type: textarea + id: description + attributes: + label: Description + description: What went wrong? + validations: + required: true + - type: textarea + id: repro + attributes: + label: Reproduction steps + description: Minimal code or steps to reproduce. + validations: + required: true + - type: textarea + id: expected + attributes: + label: Expected behavior + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..7b18ba6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,36 @@ +name: Feature request +description: Propose a capability for a FunctionFoundry package +labels: ["enhancement"] +body: + - type: dropdown + id: package + attributes: + label: Package + options: + - FunctionFoundry.Security + - FunctionFoundry.Storage + - FunctionFoundry.Integrity + - FunctionFoundry.Observability + - FunctionFoundry.Data + - FunctionFoundry.Text + - FunctionFoundry.Resilience + - FunctionFoundry.Networking + - FunctionFoundry.Distributed + - FunctionFoundry.Scheduling + - New package / cross-cutting + validations: + required: true + - type: textarea + id: problem + attributes: + label: Problem + description: What specialized capability is missing that the BCL does not already provide well? + validations: + required: true + - type: textarea + id: proposal + attributes: + label: Proposal + description: API sketch, guarantees, and non-goals. + validations: + required: true diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..3f9a01d --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,13 @@ +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + open-pull-requests-limit: 5 + + - package-ecosystem: nuget + directory: / + schedule: + interval: weekly + open-pull-requests-limit: 10 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6018d66..c58f355 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,11 @@ env: jobs: build: - runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] + runs-on: ${{ matrix.os }} steps: - name: Checkout uses: actions/checkout@v4 @@ -25,26 +29,34 @@ jobs: global-json-file: global.json - name: Verify pinned SDK + shell: bash run: | SDK_VERSION=$(dotnet --version) echo "Installed SDK: $SDK_VERSION" - test "$SDK_VERSION" = "10.0.301" + EXPECTED=$(python -c "import json; print(json.load(open('global.json'))['sdk']['version'])") + test "$SDK_VERSION" = "$EXPECTED" - - name: Restore (locked mode when lock files exist) - run: | - if find . -name 'packages.lock.json' | grep -q .; then - dotnet restore --locked-mode - else - dotnet restore - fi + - name: Restore (locked mode) + run: dotnet restore --locked-mode - name: Verify formatting + if: matrix.os == 'ubuntu-latest' run: dotnet format --verify-no-changes --verbosity diagnostic - name: Build Release run: dotnet build -c Release --no-restore + - name: Build samples + shell: bash + run: | + set -e + for proj in samples/*/*.csproj; do + echo "=== Building $proj ===" + dotnet build -c Release --no-restore "$proj" + done + - name: Test with coverage + shell: bash run: | set +e FAILURES=0 @@ -78,12 +90,72 @@ jobs: exit 1 fi + - name: Enforce line coverage threshold + if: matrix.os == 'ubuntu-latest' + shell: bash + run: | + python3 <<'PY' + import pathlib, sys, xml.etree.ElementTree as ET + + threshold = 85.0 + files = list(pathlib.Path("artifacts/test-results").rglob("coverage.cobertura.xml")) + if not files: + print("::error::No cobertura coverage files found.", file=sys.stderr) + sys.exit(1) + + # Deduplicate by package name so multi-project Coverlet runs do not double-count. + package_stats: dict[str, tuple[int, int]] = {} + for path in files: + root = ET.parse(path).getroot() + for package in root.findall(".//package"): + name = package.attrib.get("name", "") + if not name or ".Tests" in name or ".Sample" in name or ".Benchmarks" in name: + continue + if "FunctionFoundry." not in name: + continue + covered = 0 + valid = 0 + for line in package.findall(".//line"): + valid += 1 + if int(line.attrib.get("hits", "0")) > 0: + covered += 1 + previous = package_stats.get(name) + if previous is None or valid > previous[1]: + package_stats[name] = (covered, valid) + + if not package_stats: + # Fallback to document totals when package names are unavailable. + covered = valid = 0 + for path in files: + root = ET.parse(path).getroot() + covered += int(float(root.attrib.get("lines-covered", "0"))) + valid += int(float(root.attrib.get("lines-valid", "0"))) + else: + covered = sum(c for c, _ in package_stats.values()) + valid = sum(v for _, v in package_stats.values()) + for name, (c, v) in sorted(package_stats.items()): + pct_pkg = 100.0 * c / v if v else 0.0 + print(f" {name}: {pct_pkg:.1f}% ({c}/{v})") + + if valid == 0: + print("::error::Coverage files contained zero valid lines.", file=sys.stderr) + sys.exit(1) + + pct = 100.0 * covered / valid + print(f"Aggregated line coverage: {pct:.2f}% ({covered}/{valid}) across {len(package_stats) or len(files)} packages/files") + if pct + 1e-9 < threshold: + print(f"::error::Line coverage {pct:.2f}% is below required {threshold:.1f}%.", file=sys.stderr) + sys.exit(1) + PY + - name: Pack + if: matrix.os == 'ubuntu-latest' run: | mkdir -p artifacts/packages dotnet pack -c Release --no-build -o artifacts/packages - name: Upload artifacts + if: matrix.os == 'ubuntu-latest' uses: actions/upload-artifact@v4 with: name: packages-and-tests @@ -100,4 +172,3 @@ jobs: steps: - uses: actions/checkout@v4 - uses: actions/dependency-review-action@v4 - continue-on-error: true diff --git a/.github/workflows/publish-nuget.yml b/.github/workflows/publish-nuget.yml new file mode 100644 index 0000000..2777a59 --- /dev/null +++ b/.github/workflows/publish-nuget.yml @@ -0,0 +1,72 @@ +name: Publish NuGet + +on: + workflow_dispatch: + inputs: + version: + description: SemVer package version to publish (e.g. 1.0.0) + required: true + default: "1.0.0" + release: + types: [published] + +env: + DOTNET_NOLOGO: true + DOTNET_CLI_TELEMETRY_OPTOUT: true + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true + CI: true + FF_RELEASE_PACK: true + +jobs: + publish: + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Resolve version + id: ver + shell: bash + run: | + if [ "${{ github.event_name }}" = "release" ]; then + TAG="${{ github.event.release.tag_name }}" + VERSION="${TAG#v}" + else + VERSION="${{ inputs.version }}" + fi + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + echo "Publishing version $VERSION" + + - name: Setup .NET SDK + uses: actions/setup-dotnet@v4 + with: + global-json-file: global.json + + - name: Restore / build / pack (no -local suffix) + run: | + dotnet restore --locked-mode + dotnet build -c Release --no-restore -p:Version=${{ steps.ver.outputs.version }} -p:VersionSuffix= + mkdir -p artifacts/packages + dotnet pack -c Release --no-build -o artifacts/packages -p:Version=${{ steps.ver.outputs.version }} -p:VersionSuffix= + + - name: Push to NuGet.org + env: + NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }} + shell: bash + run: | + if [ -z "$NUGET_API_KEY" ]; then + echo "::error::NUGET_API_KEY secret is not configured." + exit 1 + fi + shopt -s nullglob + packages=(artifacts/packages/FunctionFoundry.*.${{ steps.ver.outputs.version }}.nupkg) + if [ ${#packages[@]} -eq 0 ]; then + echo "::error::No nupkg files found for version ${{ steps.ver.outputs.version }}." + exit 1 + fi + for pkg in "${packages[@]}"; do + echo "Pushing $pkg" + dotnet nuget push "$pkg" --api-key "$NUGET_API_KEY" --source https://api.nuget.org/v3/index.json --skip-duplicate + done diff --git a/CHANGELOG.md b/CHANGELOG.md index c9c0a01..1a9278f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -* Relicensed from MIT to **Apache License 2.0** with an attribution `NOTICE` file. Free use remains allowed; FunctionFoundry must be credited when used or redistributed. +* Pin .NET SDK **10.0.302** (maintenance upgrade from 10.0.301; roll-forward still disabled). +* Local packs keep the `-local` version suffix; CI and `FF_RELEASE_PACK=true` produce clean SemVer packages suitable for NuGet.org. +* CI now runs on Ubuntu and Windows, builds all samples, restores with lock files, and fails when aggregated line coverage is below 85%. +* `FunctionFoundry.Networking` downloader now adapts chunk size within min/max from observed throughput and performs corrupt-chunk / digest repair passes. +* Security reporting documents GitHub Security Advisories as the private channel. + +### Added + +* `.github/workflows/publish-nuget.yml` for clean version publishes (requires `NUGET_API_KEY`). +* Dependabot for GitHub Actions and NuGet. +* `CODE_OF_CONDUCT.md`. +* Package lock files (`packages.lock.json`) for reproducible restores. + +### Fixed + +* Documentation drift (test counts, tag naming, release/NuGet status) in `STATE.md` and `docs/FINAL_REPORT.md`. +* Changelog now records the Apache 2.0 relicense under 1.0.0 (was incorrectly left only under Unreleased). ## [1.0.0] - 2026-07-14 @@ -25,9 +41,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * `FunctionFoundry.Networking` — resumable parallel downloader, mirror selector, transfer plan, streaming integrity verifier. * `FunctionFoundry.Distributed` — weighted rendezvous hashing, version clocks, phi-accrual detector, quorum aggregator. * `FunctionFoundry.Scheduling` — interval-set algebra, recurring availability, business calendars, critical-path scheduler. -* Repository engineering foundation (SDK 10.0.301 pin, analyzers, CI, ADRs, documentation). +* Repository engineering foundation (SDK pin, analyzers, CI, ADRs, documentation). + +### Changed + +* Licensed under the **Apache License 2.0** with an attribution `NOTICE` file. Free use remains allowed; FunctionFoundry must be credited when used or redistributed. ### Notes -* Feature 1 packages were hardened as the conceptual v0.5.0 baseline; Feature 2 completion is released as v1.0.0 local package artifacts. +* Feature 1 packages were hardened as the conceptual v0.5.0 baseline; Feature 2 completion is released as v1.0.0. * No mandatory aggregate package. Core packages have zero third-party runtime dependencies. +* An accidental NuGet publish of `1.0.0-local` should be replaced by a clean `1.0.0` using the Publish NuGet workflow. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..5cee24c --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,28 @@ +# Code of Conduct + +## Our pledge + +We pledge to make participation in FunctionFoundry a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, experience level, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our standards + +Examples of behavior that contributes to a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community + +Examples of unacceptable behavior include: + +* Harassment, trolling, insulting or derogatory comments +* Publishing others' private information without permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Enforcement + +Report unacceptable behavior privately using the channels in [SECURITY.md](SECURITY.md) for sensitive matters, or contact the repository maintainers via GitHub for conduct issues. Maintainers will review and take appropriate action. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org/), version 2.1. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 18f4a72..ff1ef39 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,16 +4,26 @@ Thank you for contributing. This repository builds specialized .NET libraries, n ## Development setup -1. Install .NET SDK **10.0.301** exactly (see `global.json`). +1. Install .NET SDK **10.0.302** exactly (see `global.json`). 2. Restore, build, and test: ```bash -dotnet restore +dotnet restore --locked-mode dotnet build -c Release dotnet test -c Release --collect:"XPlat Code Coverage" dotnet pack -c Release ``` +Release packs without the `-local` suffix: + +```bash +# Linux / macOS +FF_RELEASE_PACK=true ./build/publish-release.sh + +# Windows +set FF_RELEASE_PACK=true&& build\publish-release.cmd +``` + ## Coding standards * Target `net10.0`, C# 14, nullable enabled, warnings as errors. diff --git a/Directory.Build.props b/Directory.Build.props index a74585b..5f14f8d 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -23,7 +23,9 @@ true snupkg 1.0.0 - local + + local + true $(MSBuildThisFileDirectory)artifacts $(ArtifactsPath)/packages diff --git a/README.md b/README.md index d2a8420..c15ab1b 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # FunctionFoundry [![CI](https://github.com/Chookees/FunctionFoundry/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/Chookees/FunctionFoundry/actions/workflows/ci.yml) -[![.NET](https://img.shields.io/badge/.NET-10.0.301-512BD4?logo=dotnet&logoColor=white)](global.json) -[![Tests](https://img.shields.io/badge/tests-224%20passed-brightgreen)](docs/coverage-summary.md) -[![Coverage](https://img.shields.io/badge/coverage-84.8%25%20line-yellowgreen)](docs/coverage-summary.md) -[![Branch Coverage](https://img.shields.io/badge/branch%20coverage-72.3%25-yellowgreen)](docs/coverage-summary.md) +[![.NET](https://img.shields.io/badge/.NET-10.0.302-512BD4?logo=dotnet&logoColor=white)](global.json) +[![Tests](https://img.shields.io/badge/tests-242%20passed-brightgreen)](docs/coverage-summary.md) +[![Coverage](https://img.shields.io/badge/coverage-85.8%25%20line-brightgreen)](docs/coverage-summary.md) +[![Branch Coverage](https://img.shields.io/badge/branch%20coverage-remeasure-lightgrey)](docs/coverage-summary.md) [![Skipped](https://img.shields.io/badge/skipped-0-brightgreen)](docs/coverage-summary.md) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE) [![Packages](https://img.shields.io/badge/packages-10%20independent-blue)](#packages) @@ -32,12 +32,14 @@ Each product has a dedicated README under `src//README.md` describing e ## Requirements -* .NET SDK **10.0.301** (pinned in `global.json`; roll-forward disabled) +* .NET SDK **10.0.302** (pinned in `global.json`; roll-forward disabled) * Target framework: `net10.0` * C# 14.0 SDK versions receive explicit maintenance upgrades only. Do not silently change the pinned feature band. +Local `dotnet pack` produces `*-local` package versions by design. Release packs (CI or `FF_RELEASE_PACK=true` / publish workflow) omit the suffix. + ## Build ```bash diff --git a/SECURITY.md b/SECURITY.md index 202fa63..4ab97f7 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,11 +2,15 @@ ## Supported versions -Security updates are provided for the latest released major version of FunctionFoundry packages (currently targeting the v1.0 line once released). +Security updates are provided for the latest released major version of FunctionFoundry packages (currently the v1.0 line). ## Reporting a vulnerability -Please report security vulnerabilities privately. Do not open a public GitHub issue for undisclosed defects. +Please report security vulnerabilities **privately**. Do not open a public GitHub issue for undisclosed defects. + +**Preferred channel:** use [GitHub Security Advisories → Report a vulnerability](https://github.com/Chookees/FunctionFoundry/security/advisories/new) on this repository. + +If you cannot use GitHub Security Advisories, email the maintainers via the contact listed on the [GitHub organization / repository security contacts](https://github.com/Chookees/FunctionFoundry#security) once configured, or open a private maintainer contact request without including exploit details in a public issue. Include: diff --git a/STATE.md b/STATE.md index e19d0a6..7fac73b 100644 --- a/STATE.md +++ b/STATE.md @@ -4,73 +4,44 @@ Resume this file instead of conversation context. ## Current Feature -FF-F02 complete; FunctionFoundry v1.0.0 local artifacts generated. +Health-scan remediation on branch `chore/health-scan-remediation` (SDK pin, NuGet release path, CI gates, Networking adaptive/repair, docs honesty). ## Current PBI -All PBIs PBI-01..PBI-12 delivered on branch `cursor/functionfoundry-v1-482e` / `main`. +All PBIs PBI-01..PBI-12 delivered on `main`. Follow-up hardening in progress on remediation branch. ## Completed Tasks -* PBI-01 foundation -* PBI-02 Security -* PBI-03 Storage -* PBI-04 Integrity -* PBI-05 Observability -* PBI-06 hardening report / size report / docs -* PBI-07 Data -* PBI-08 Text -* PBI-09 Resilience -* PBI-10 Networking -* PBI-11 Distributed -* PBI-12 Scheduling + v1.0 packaging +* PBI-01..PBI-12 (v1.0 feature set) +* Git tag `1.0.0` on origin (no `v` prefix) +* Local and CI package production for 10 libraries ## Remaining Tasks -* Optional: NuGet.org publish (requires credentials/permission) -* Optional: git tags `v0.5.0` / `v1.0.0` if tagging succeeds on remote -* Optional: Native AOT/trimming smoke apps before claiming IsAotCompatible +* Publish clean NuGet `1.0.0` via `.github/workflows/publish-nuget.yml` (requires `NUGET_API_KEY` secret); unlist or leave `1.0.0-local` as accidental pre-release +* Optional: Native AOT/trimming smoke apps before claiming `IsAotCompatible` +* Optional: raise branch coverage toward ≥90% on critical algorithms (Distributed/Integrity still the weakest) ## Last successful commands -* `dotnet build -c Release` — 0 warnings/errors (all 10 packages + tests/samples/benchmarks) -* `dotnet test -c Release` — **182 passed** -* `dotnet pack -c Release -o artifacts/packages /p:Version=1.0.0` — 10 nupkg + snupkg - -## Last successful commit - -Recorded after hardening commit / main fast-forward (see git log). - -## Main-branch PBI commits (expected) - -* PBI-01: `86bc024` build(repo)... -* PBI-02: `b89ed86` feat(security)... -* PBI-04: `ba3a4fb` feat(integrity)... -* PBI-05: `1a5a724` feat(observability)... -* PBI-03: `f50a08e` feat(storage)... -* PBI-07..12: see `git log --oneline` on delivery branch (Data/Text/Resilience/Networking/Distributed/Scheduling) +* Target: `dotnet build -c Release` / `dotnet test -c Release` on SDK **10.0.302** +* Unit tests: **242** passed +* Line coverage: **85.8%** (CI gate ≥85%). Branch coverage not re-gated yet. ## Open technical risks * Full Unicode confusable table not shipped (compact v1 subset documented). -* Networking adaptive chunk resize and auto-repair of corrupt chunks are partial relative to maximal interpretation of the brief. * Observability redaction uses bounded reflection. -* IsTrimmable/IsAotCompatible not claimed. -* Coverage thresholds not yet enforced via CI report thresholds tooling (tests pass; coverage gate not numerically asserted in CI YAML). -* Package validation against a prior published baseline not applicable for first release. +* `IsTrimmable` / `IsAotCompatible` not claimed. +* First stable NuGet line was mistakenly published as `1.0.0-local`; clean `1.0.0` publish still pending credentials. +* Package validation baseline against published `1.0.0` should be enabled after the clean publish. ## Decisions made -* ADRs 0001-0005 -* Modular independent DLLs; no FunctionProvider code copied -* Zero third-party runtime dependencies across all core packages - -## Known deviations - -* Cloud agent branches use `cursor/...-482e` naming. -* PBI commit order on main is not strictly numeric (Integrity/Observability landed before Storage). -* Separate Feature 1 `v0.5.0` git tag may be skipped if tagging Focuses on `v1.0.0` only. +* ADRs 0001-0006 +* Modular independent DLLs; zero third-party runtime dependencies +* Local packs keep `-local` suffix; release packs clear `VersionSuffix` (`CI=true` or `FF_RELEASE_PACK=true`) ## Exact next action -Commit hardening docs, push delivery branch + main, open PR, attempt `v1.0.0` tag. +Land remediation PR, configure `NUGET_API_KEY`, run Publish NuGet workflow for `1.0.0`, then set `PackageValidationBaselineVersion` to `1.0.0`. diff --git a/benchmarks/FunctionFoundry.Data.Benchmarks/packages.lock.json b/benchmarks/FunctionFoundry.Data.Benchmarks/packages.lock.json new file mode 100644 index 0000000..8286fa2 --- /dev/null +++ b/benchmarks/FunctionFoundry.Data.Benchmarks/packages.lock.json @@ -0,0 +1,166 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "BenchmarkDotNet": { + "type": "Direct", + "requested": "[0.15.2, )", + "resolved": "0.15.2", + "contentHash": "5vl4nQFUGTKgOVE5LNoLpv/X/wFQ7wE5yiaJAXCpzgNtUADtfEwbuufKI4o5E2De0VTXNEeom5Hnv8S8vuBZtg==", + "dependencies": { + "BenchmarkDotNet.Annotations": "0.15.2", + "CommandLineParser": "2.9.1", + "Gee.External.Capstone": "2.3.0", + "Iced": "1.21.0", + "Microsoft.CodeAnalysis.CSharp": "4.14.0", + "Microsoft.Diagnostics.Runtime": "3.1.512801", + "Microsoft.Diagnostics.Tracing.TraceEvent": "3.1.21", + "Microsoft.DotNet.PlatformAbstractions": "3.1.6", + "Perfolizer": "[0.5.3]", + "System.Management": "9.0.5" + } + }, + "BenchmarkDotNet.Annotations": { + "type": "Transitive", + "resolved": "0.15.2", + "contentHash": "a7rVlVmXQRrp3J+HF/FtjwiFcshU6BAuv6U1xtAbqCUYepOEnNGaPeOFvy4It17QZwwofm6U/GAfW3JrFMXszg==" + }, + "CommandLineParser": { + "type": "Transitive", + "resolved": "2.9.1", + "contentHash": "OE0sl1/sQ37bjVsPKKtwQlWDgqaxWgtme3xZz7JssWUzg5JpMIyHgCTY9MVMxOg48fJ1AgGT3tgdH5m/kQ5xhA==" + }, + "Gee.External.Capstone": { + "type": "Transitive", + "resolved": "2.3.0", + "contentHash": "2ap/rYmjtzCOT8hxrnEW/QeiOt+paD8iRrIcdKX0cxVwWLFa1e+JDBNeECakmccXrSFeBQuu5AV8SNkipFMMMw==" + }, + "Iced": { + "type": "Transitive", + "resolved": "1.21.0", + "contentHash": "dv5+81Q1TBQvVMSOOOmRcjJmvWcX3BZPZsIq31+RLc5cNft0IHAyNlkdb7ZarOWG913PyBoFDsDXoCIlKmLclg==" + }, + "Microsoft.CodeAnalysis.Analyzers": { + "type": "Transitive", + "resolved": "3.11.0", + "contentHash": "v/EW3UE8/lbEYHoC2Qq7AR/DnmvpgdtAMndfQNmpuIMx/Mto8L5JnuCfdBYtgvalQOtfNCnxFejxuRrryvUTsg==" + }, + "Microsoft.CodeAnalysis.Common": { + "type": "Transitive", + "resolved": "4.14.0", + "contentHash": "PC3tuwZYnC+idaPuoC/AZpEdwrtX7qFpmnrfQkgobGIWiYmGi5MCRtl5mx6QrfMGQpK78X2lfIEoZDLg/qnuHg==", + "dependencies": { + "Microsoft.CodeAnalysis.Analyzers": "3.11.0" + } + }, + "Microsoft.CodeAnalysis.CSharp": { + "type": "Transitive", + "resolved": "4.14.0", + "contentHash": "568a6wcTivauIhbeWcCwfWwIn7UV7MeHEBvFB2uzGIpM2OhJ4eM/FZ8KS0yhPoNxnSpjGzz7x7CIjTxhslojQA==", + "dependencies": { + "Microsoft.CodeAnalysis.Analyzers": "3.11.0", + "Microsoft.CodeAnalysis.Common": "[4.14.0]" + } + }, + "Microsoft.Diagnostics.NETCore.Client": { + "type": "Transitive", + "resolved": "0.2.510501", + "contentHash": "juoqJYMDs+lRrrZyOkXXMImJHneCF23cuvO4waFRd2Ds7j+ZuGIPbJm0Y/zz34BdeaGiiwGWraMUlln05W1PCQ==", + "dependencies": { + "Microsoft.Extensions.Logging": "6.0.0" + } + }, + "Microsoft.Diagnostics.Runtime": { + "type": "Transitive", + "resolved": "3.1.512801", + "contentHash": "0lMUDr2oxNZa28D6NH5BuSQEe5T9tZziIkvkD44YkkCGQXPJqvFjLq5ZQq1hYLl3RjQJrY+hR0jFgap+EWPDTw==", + "dependencies": { + "Microsoft.Diagnostics.NETCore.Client": "0.2.410101" + } + }, + "Microsoft.Diagnostics.Tracing.TraceEvent": { + "type": "Transitive", + "resolved": "3.1.21", + "contentHash": "/OrJFKaojSR6TkUKtwh8/qA9XWNtxLrXMqvEb89dBSKCWjaGVTbKMYodIUgF5deCEtmd6GXuRerciXGl5bhZ7Q==", + "dependencies": { + "Microsoft.Diagnostics.NETCore.Client": "0.2.510501", + "System.Reflection.TypeExtensions": "4.7.0" + } + }, + "Microsoft.DotNet.PlatformAbstractions": { + "type": "Transitive", + "resolved": "3.1.6", + "contentHash": "jek4XYaQ/PGUwDKKhwR8K47Uh1189PFzMeLqO83mXrXQVIpARZCcfuDedH50YDTepBkfijCZN5U/vZi++erxtg==" + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==" + }, + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "6.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/HggWBbTwy8TgebGSX5DBZ24ndhzi93sHUBDvP1IxbZD7FDokYzdAr6+vbWGjw2XAfR2EJ1sfKUotpjHnFWPxA==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==" + }, + "Perfolizer": { + "type": "Transitive", + "resolved": "0.5.3", + "contentHash": "EhHo0s4y+tHJKhZHgQvPgFWhAhnQpHHuFzutRzfqfv5z763wvhzXh849ZtpVePQNxXWoFafh1Ta2tZgnSIVsYQ==" + }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "9.0.5", + "contentHash": "cuzLM2MWutf9ZBEMPYYfd0DXwYdvntp7VCT6a/wvbKCa2ZuvGmW74xi+YBa2mrfEieAXqM4TNKlMmSnfAfpUoQ==" + }, + "System.Management": { + "type": "Transitive", + "resolved": "9.0.5", + "contentHash": "n6o9PZm9p25+zAzC3/48K0oHnaPKTInRrxqFq1fi/5TPbMLjuoCm/h//mS3cUmSy+9AO1Z+qsC/Ilt/ZFatv5Q==", + "dependencies": { + "System.CodeDom": "9.0.5" + } + }, + "System.Reflection.TypeExtensions": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "VybpaOQQhqE6siHppMktjfGBw1GCwvCqiufqmP8F1nj7fTUNtW35LOEt3UZTEsECfo+ELAl/9o9nJx3U91i7vA==" + }, + "functionfoundry.data": { + "type": "Project" + } + } + } +} \ No newline at end of file diff --git a/benchmarks/FunctionFoundry.Distributed.Benchmarks/packages.lock.json b/benchmarks/FunctionFoundry.Distributed.Benchmarks/packages.lock.json new file mode 100644 index 0000000..fdb89e0 --- /dev/null +++ b/benchmarks/FunctionFoundry.Distributed.Benchmarks/packages.lock.json @@ -0,0 +1,166 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "BenchmarkDotNet": { + "type": "Direct", + "requested": "[0.15.2, )", + "resolved": "0.15.2", + "contentHash": "5vl4nQFUGTKgOVE5LNoLpv/X/wFQ7wE5yiaJAXCpzgNtUADtfEwbuufKI4o5E2De0VTXNEeom5Hnv8S8vuBZtg==", + "dependencies": { + "BenchmarkDotNet.Annotations": "0.15.2", + "CommandLineParser": "2.9.1", + "Gee.External.Capstone": "2.3.0", + "Iced": "1.21.0", + "Microsoft.CodeAnalysis.CSharp": "4.14.0", + "Microsoft.Diagnostics.Runtime": "3.1.512801", + "Microsoft.Diagnostics.Tracing.TraceEvent": "3.1.21", + "Microsoft.DotNet.PlatformAbstractions": "3.1.6", + "Perfolizer": "[0.5.3]", + "System.Management": "9.0.5" + } + }, + "BenchmarkDotNet.Annotations": { + "type": "Transitive", + "resolved": "0.15.2", + "contentHash": "a7rVlVmXQRrp3J+HF/FtjwiFcshU6BAuv6U1xtAbqCUYepOEnNGaPeOFvy4It17QZwwofm6U/GAfW3JrFMXszg==" + }, + "CommandLineParser": { + "type": "Transitive", + "resolved": "2.9.1", + "contentHash": "OE0sl1/sQ37bjVsPKKtwQlWDgqaxWgtme3xZz7JssWUzg5JpMIyHgCTY9MVMxOg48fJ1AgGT3tgdH5m/kQ5xhA==" + }, + "Gee.External.Capstone": { + "type": "Transitive", + "resolved": "2.3.0", + "contentHash": "2ap/rYmjtzCOT8hxrnEW/QeiOt+paD8iRrIcdKX0cxVwWLFa1e+JDBNeECakmccXrSFeBQuu5AV8SNkipFMMMw==" + }, + "Iced": { + "type": "Transitive", + "resolved": "1.21.0", + "contentHash": "dv5+81Q1TBQvVMSOOOmRcjJmvWcX3BZPZsIq31+RLc5cNft0IHAyNlkdb7ZarOWG913PyBoFDsDXoCIlKmLclg==" + }, + "Microsoft.CodeAnalysis.Analyzers": { + "type": "Transitive", + "resolved": "3.11.0", + "contentHash": "v/EW3UE8/lbEYHoC2Qq7AR/DnmvpgdtAMndfQNmpuIMx/Mto8L5JnuCfdBYtgvalQOtfNCnxFejxuRrryvUTsg==" + }, + "Microsoft.CodeAnalysis.Common": { + "type": "Transitive", + "resolved": "4.14.0", + "contentHash": "PC3tuwZYnC+idaPuoC/AZpEdwrtX7qFpmnrfQkgobGIWiYmGi5MCRtl5mx6QrfMGQpK78X2lfIEoZDLg/qnuHg==", + "dependencies": { + "Microsoft.CodeAnalysis.Analyzers": "3.11.0" + } + }, + "Microsoft.CodeAnalysis.CSharp": { + "type": "Transitive", + "resolved": "4.14.0", + "contentHash": "568a6wcTivauIhbeWcCwfWwIn7UV7MeHEBvFB2uzGIpM2OhJ4eM/FZ8KS0yhPoNxnSpjGzz7x7CIjTxhslojQA==", + "dependencies": { + "Microsoft.CodeAnalysis.Analyzers": "3.11.0", + "Microsoft.CodeAnalysis.Common": "[4.14.0]" + } + }, + "Microsoft.Diagnostics.NETCore.Client": { + "type": "Transitive", + "resolved": "0.2.510501", + "contentHash": "juoqJYMDs+lRrrZyOkXXMImJHneCF23cuvO4waFRd2Ds7j+ZuGIPbJm0Y/zz34BdeaGiiwGWraMUlln05W1PCQ==", + "dependencies": { + "Microsoft.Extensions.Logging": "6.0.0" + } + }, + "Microsoft.Diagnostics.Runtime": { + "type": "Transitive", + "resolved": "3.1.512801", + "contentHash": "0lMUDr2oxNZa28D6NH5BuSQEe5T9tZziIkvkD44YkkCGQXPJqvFjLq5ZQq1hYLl3RjQJrY+hR0jFgap+EWPDTw==", + "dependencies": { + "Microsoft.Diagnostics.NETCore.Client": "0.2.410101" + } + }, + "Microsoft.Diagnostics.Tracing.TraceEvent": { + "type": "Transitive", + "resolved": "3.1.21", + "contentHash": "/OrJFKaojSR6TkUKtwh8/qA9XWNtxLrXMqvEb89dBSKCWjaGVTbKMYodIUgF5deCEtmd6GXuRerciXGl5bhZ7Q==", + "dependencies": { + "Microsoft.Diagnostics.NETCore.Client": "0.2.510501", + "System.Reflection.TypeExtensions": "4.7.0" + } + }, + "Microsoft.DotNet.PlatformAbstractions": { + "type": "Transitive", + "resolved": "3.1.6", + "contentHash": "jek4XYaQ/PGUwDKKhwR8K47Uh1189PFzMeLqO83mXrXQVIpARZCcfuDedH50YDTepBkfijCZN5U/vZi++erxtg==" + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==" + }, + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "6.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/HggWBbTwy8TgebGSX5DBZ24ndhzi93sHUBDvP1IxbZD7FDokYzdAr6+vbWGjw2XAfR2EJ1sfKUotpjHnFWPxA==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==" + }, + "Perfolizer": { + "type": "Transitive", + "resolved": "0.5.3", + "contentHash": "EhHo0s4y+tHJKhZHgQvPgFWhAhnQpHHuFzutRzfqfv5z763wvhzXh849ZtpVePQNxXWoFafh1Ta2tZgnSIVsYQ==" + }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "9.0.5", + "contentHash": "cuzLM2MWutf9ZBEMPYYfd0DXwYdvntp7VCT6a/wvbKCa2ZuvGmW74xi+YBa2mrfEieAXqM4TNKlMmSnfAfpUoQ==" + }, + "System.Management": { + "type": "Transitive", + "resolved": "9.0.5", + "contentHash": "n6o9PZm9p25+zAzC3/48K0oHnaPKTInRrxqFq1fi/5TPbMLjuoCm/h//mS3cUmSy+9AO1Z+qsC/Ilt/ZFatv5Q==", + "dependencies": { + "System.CodeDom": "9.0.5" + } + }, + "System.Reflection.TypeExtensions": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "VybpaOQQhqE6siHppMktjfGBw1GCwvCqiufqmP8F1nj7fTUNtW35LOEt3UZTEsECfo+ELAl/9o9nJx3U91i7vA==" + }, + "functionfoundry.distributed": { + "type": "Project" + } + } + } +} \ No newline at end of file diff --git a/benchmarks/FunctionFoundry.Integrity.Benchmarks/packages.lock.json b/benchmarks/FunctionFoundry.Integrity.Benchmarks/packages.lock.json new file mode 100644 index 0000000..feb2af7 --- /dev/null +++ b/benchmarks/FunctionFoundry.Integrity.Benchmarks/packages.lock.json @@ -0,0 +1,166 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "BenchmarkDotNet": { + "type": "Direct", + "requested": "[0.15.2, )", + "resolved": "0.15.2", + "contentHash": "5vl4nQFUGTKgOVE5LNoLpv/X/wFQ7wE5yiaJAXCpzgNtUADtfEwbuufKI4o5E2De0VTXNEeom5Hnv8S8vuBZtg==", + "dependencies": { + "BenchmarkDotNet.Annotations": "0.15.2", + "CommandLineParser": "2.9.1", + "Gee.External.Capstone": "2.3.0", + "Iced": "1.21.0", + "Microsoft.CodeAnalysis.CSharp": "4.14.0", + "Microsoft.Diagnostics.Runtime": "3.1.512801", + "Microsoft.Diagnostics.Tracing.TraceEvent": "3.1.21", + "Microsoft.DotNet.PlatformAbstractions": "3.1.6", + "Perfolizer": "[0.5.3]", + "System.Management": "9.0.5" + } + }, + "BenchmarkDotNet.Annotations": { + "type": "Transitive", + "resolved": "0.15.2", + "contentHash": "a7rVlVmXQRrp3J+HF/FtjwiFcshU6BAuv6U1xtAbqCUYepOEnNGaPeOFvy4It17QZwwofm6U/GAfW3JrFMXszg==" + }, + "CommandLineParser": { + "type": "Transitive", + "resolved": "2.9.1", + "contentHash": "OE0sl1/sQ37bjVsPKKtwQlWDgqaxWgtme3xZz7JssWUzg5JpMIyHgCTY9MVMxOg48fJ1AgGT3tgdH5m/kQ5xhA==" + }, + "Gee.External.Capstone": { + "type": "Transitive", + "resolved": "2.3.0", + "contentHash": "2ap/rYmjtzCOT8hxrnEW/QeiOt+paD8iRrIcdKX0cxVwWLFa1e+JDBNeECakmccXrSFeBQuu5AV8SNkipFMMMw==" + }, + "Iced": { + "type": "Transitive", + "resolved": "1.21.0", + "contentHash": "dv5+81Q1TBQvVMSOOOmRcjJmvWcX3BZPZsIq31+RLc5cNft0IHAyNlkdb7ZarOWG913PyBoFDsDXoCIlKmLclg==" + }, + "Microsoft.CodeAnalysis.Analyzers": { + "type": "Transitive", + "resolved": "3.11.0", + "contentHash": "v/EW3UE8/lbEYHoC2Qq7AR/DnmvpgdtAMndfQNmpuIMx/Mto8L5JnuCfdBYtgvalQOtfNCnxFejxuRrryvUTsg==" + }, + "Microsoft.CodeAnalysis.Common": { + "type": "Transitive", + "resolved": "4.14.0", + "contentHash": "PC3tuwZYnC+idaPuoC/AZpEdwrtX7qFpmnrfQkgobGIWiYmGi5MCRtl5mx6QrfMGQpK78X2lfIEoZDLg/qnuHg==", + "dependencies": { + "Microsoft.CodeAnalysis.Analyzers": "3.11.0" + } + }, + "Microsoft.CodeAnalysis.CSharp": { + "type": "Transitive", + "resolved": "4.14.0", + "contentHash": "568a6wcTivauIhbeWcCwfWwIn7UV7MeHEBvFB2uzGIpM2OhJ4eM/FZ8KS0yhPoNxnSpjGzz7x7CIjTxhslojQA==", + "dependencies": { + "Microsoft.CodeAnalysis.Analyzers": "3.11.0", + "Microsoft.CodeAnalysis.Common": "[4.14.0]" + } + }, + "Microsoft.Diagnostics.NETCore.Client": { + "type": "Transitive", + "resolved": "0.2.510501", + "contentHash": "juoqJYMDs+lRrrZyOkXXMImJHneCF23cuvO4waFRd2Ds7j+ZuGIPbJm0Y/zz34BdeaGiiwGWraMUlln05W1PCQ==", + "dependencies": { + "Microsoft.Extensions.Logging": "6.0.0" + } + }, + "Microsoft.Diagnostics.Runtime": { + "type": "Transitive", + "resolved": "3.1.512801", + "contentHash": "0lMUDr2oxNZa28D6NH5BuSQEe5T9tZziIkvkD44YkkCGQXPJqvFjLq5ZQq1hYLl3RjQJrY+hR0jFgap+EWPDTw==", + "dependencies": { + "Microsoft.Diagnostics.NETCore.Client": "0.2.410101" + } + }, + "Microsoft.Diagnostics.Tracing.TraceEvent": { + "type": "Transitive", + "resolved": "3.1.21", + "contentHash": "/OrJFKaojSR6TkUKtwh8/qA9XWNtxLrXMqvEb89dBSKCWjaGVTbKMYodIUgF5deCEtmd6GXuRerciXGl5bhZ7Q==", + "dependencies": { + "Microsoft.Diagnostics.NETCore.Client": "0.2.510501", + "System.Reflection.TypeExtensions": "4.7.0" + } + }, + "Microsoft.DotNet.PlatformAbstractions": { + "type": "Transitive", + "resolved": "3.1.6", + "contentHash": "jek4XYaQ/PGUwDKKhwR8K47Uh1189PFzMeLqO83mXrXQVIpARZCcfuDedH50YDTepBkfijCZN5U/vZi++erxtg==" + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==" + }, + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "6.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/HggWBbTwy8TgebGSX5DBZ24ndhzi93sHUBDvP1IxbZD7FDokYzdAr6+vbWGjw2XAfR2EJ1sfKUotpjHnFWPxA==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==" + }, + "Perfolizer": { + "type": "Transitive", + "resolved": "0.5.3", + "contentHash": "EhHo0s4y+tHJKhZHgQvPgFWhAhnQpHHuFzutRzfqfv5z763wvhzXh849ZtpVePQNxXWoFafh1Ta2tZgnSIVsYQ==" + }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "9.0.5", + "contentHash": "cuzLM2MWutf9ZBEMPYYfd0DXwYdvntp7VCT6a/wvbKCa2ZuvGmW74xi+YBa2mrfEieAXqM4TNKlMmSnfAfpUoQ==" + }, + "System.Management": { + "type": "Transitive", + "resolved": "9.0.5", + "contentHash": "n6o9PZm9p25+zAzC3/48K0oHnaPKTInRrxqFq1fi/5TPbMLjuoCm/h//mS3cUmSy+9AO1Z+qsC/Ilt/ZFatv5Q==", + "dependencies": { + "System.CodeDom": "9.0.5" + } + }, + "System.Reflection.TypeExtensions": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "VybpaOQQhqE6siHppMktjfGBw1GCwvCqiufqmP8F1nj7fTUNtW35LOEt3UZTEsECfo+ELAl/9o9nJx3U91i7vA==" + }, + "functionfoundry.integrity": { + "type": "Project" + } + } + } +} \ No newline at end of file diff --git a/benchmarks/FunctionFoundry.Networking.Benchmarks/packages.lock.json b/benchmarks/FunctionFoundry.Networking.Benchmarks/packages.lock.json new file mode 100644 index 0000000..77f8193 --- /dev/null +++ b/benchmarks/FunctionFoundry.Networking.Benchmarks/packages.lock.json @@ -0,0 +1,166 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "BenchmarkDotNet": { + "type": "Direct", + "requested": "[0.15.2, )", + "resolved": "0.15.2", + "contentHash": "5vl4nQFUGTKgOVE5LNoLpv/X/wFQ7wE5yiaJAXCpzgNtUADtfEwbuufKI4o5E2De0VTXNEeom5Hnv8S8vuBZtg==", + "dependencies": { + "BenchmarkDotNet.Annotations": "0.15.2", + "CommandLineParser": "2.9.1", + "Gee.External.Capstone": "2.3.0", + "Iced": "1.21.0", + "Microsoft.CodeAnalysis.CSharp": "4.14.0", + "Microsoft.Diagnostics.Runtime": "3.1.512801", + "Microsoft.Diagnostics.Tracing.TraceEvent": "3.1.21", + "Microsoft.DotNet.PlatformAbstractions": "3.1.6", + "Perfolizer": "[0.5.3]", + "System.Management": "9.0.5" + } + }, + "BenchmarkDotNet.Annotations": { + "type": "Transitive", + "resolved": "0.15.2", + "contentHash": "a7rVlVmXQRrp3J+HF/FtjwiFcshU6BAuv6U1xtAbqCUYepOEnNGaPeOFvy4It17QZwwofm6U/GAfW3JrFMXszg==" + }, + "CommandLineParser": { + "type": "Transitive", + "resolved": "2.9.1", + "contentHash": "OE0sl1/sQ37bjVsPKKtwQlWDgqaxWgtme3xZz7JssWUzg5JpMIyHgCTY9MVMxOg48fJ1AgGT3tgdH5m/kQ5xhA==" + }, + "Gee.External.Capstone": { + "type": "Transitive", + "resolved": "2.3.0", + "contentHash": "2ap/rYmjtzCOT8hxrnEW/QeiOt+paD8iRrIcdKX0cxVwWLFa1e+JDBNeECakmccXrSFeBQuu5AV8SNkipFMMMw==" + }, + "Iced": { + "type": "Transitive", + "resolved": "1.21.0", + "contentHash": "dv5+81Q1TBQvVMSOOOmRcjJmvWcX3BZPZsIq31+RLc5cNft0IHAyNlkdb7ZarOWG913PyBoFDsDXoCIlKmLclg==" + }, + "Microsoft.CodeAnalysis.Analyzers": { + "type": "Transitive", + "resolved": "3.11.0", + "contentHash": "v/EW3UE8/lbEYHoC2Qq7AR/DnmvpgdtAMndfQNmpuIMx/Mto8L5JnuCfdBYtgvalQOtfNCnxFejxuRrryvUTsg==" + }, + "Microsoft.CodeAnalysis.Common": { + "type": "Transitive", + "resolved": "4.14.0", + "contentHash": "PC3tuwZYnC+idaPuoC/AZpEdwrtX7qFpmnrfQkgobGIWiYmGi5MCRtl5mx6QrfMGQpK78X2lfIEoZDLg/qnuHg==", + "dependencies": { + "Microsoft.CodeAnalysis.Analyzers": "3.11.0" + } + }, + "Microsoft.CodeAnalysis.CSharp": { + "type": "Transitive", + "resolved": "4.14.0", + "contentHash": "568a6wcTivauIhbeWcCwfWwIn7UV7MeHEBvFB2uzGIpM2OhJ4eM/FZ8KS0yhPoNxnSpjGzz7x7CIjTxhslojQA==", + "dependencies": { + "Microsoft.CodeAnalysis.Analyzers": "3.11.0", + "Microsoft.CodeAnalysis.Common": "[4.14.0]" + } + }, + "Microsoft.Diagnostics.NETCore.Client": { + "type": "Transitive", + "resolved": "0.2.510501", + "contentHash": "juoqJYMDs+lRrrZyOkXXMImJHneCF23cuvO4waFRd2Ds7j+ZuGIPbJm0Y/zz34BdeaGiiwGWraMUlln05W1PCQ==", + "dependencies": { + "Microsoft.Extensions.Logging": "6.0.0" + } + }, + "Microsoft.Diagnostics.Runtime": { + "type": "Transitive", + "resolved": "3.1.512801", + "contentHash": "0lMUDr2oxNZa28D6NH5BuSQEe5T9tZziIkvkD44YkkCGQXPJqvFjLq5ZQq1hYLl3RjQJrY+hR0jFgap+EWPDTw==", + "dependencies": { + "Microsoft.Diagnostics.NETCore.Client": "0.2.410101" + } + }, + "Microsoft.Diagnostics.Tracing.TraceEvent": { + "type": "Transitive", + "resolved": "3.1.21", + "contentHash": "/OrJFKaojSR6TkUKtwh8/qA9XWNtxLrXMqvEb89dBSKCWjaGVTbKMYodIUgF5deCEtmd6GXuRerciXGl5bhZ7Q==", + "dependencies": { + "Microsoft.Diagnostics.NETCore.Client": "0.2.510501", + "System.Reflection.TypeExtensions": "4.7.0" + } + }, + "Microsoft.DotNet.PlatformAbstractions": { + "type": "Transitive", + "resolved": "3.1.6", + "contentHash": "jek4XYaQ/PGUwDKKhwR8K47Uh1189PFzMeLqO83mXrXQVIpARZCcfuDedH50YDTepBkfijCZN5U/vZi++erxtg==" + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==" + }, + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "6.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/HggWBbTwy8TgebGSX5DBZ24ndhzi93sHUBDvP1IxbZD7FDokYzdAr6+vbWGjw2XAfR2EJ1sfKUotpjHnFWPxA==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==" + }, + "Perfolizer": { + "type": "Transitive", + "resolved": "0.5.3", + "contentHash": "EhHo0s4y+tHJKhZHgQvPgFWhAhnQpHHuFzutRzfqfv5z763wvhzXh849ZtpVePQNxXWoFafh1Ta2tZgnSIVsYQ==" + }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "9.0.5", + "contentHash": "cuzLM2MWutf9ZBEMPYYfd0DXwYdvntp7VCT6a/wvbKCa2ZuvGmW74xi+YBa2mrfEieAXqM4TNKlMmSnfAfpUoQ==" + }, + "System.Management": { + "type": "Transitive", + "resolved": "9.0.5", + "contentHash": "n6o9PZm9p25+zAzC3/48K0oHnaPKTInRrxqFq1fi/5TPbMLjuoCm/h//mS3cUmSy+9AO1Z+qsC/Ilt/ZFatv5Q==", + "dependencies": { + "System.CodeDom": "9.0.5" + } + }, + "System.Reflection.TypeExtensions": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "VybpaOQQhqE6siHppMktjfGBw1GCwvCqiufqmP8F1nj7fTUNtW35LOEt3UZTEsECfo+ELAl/9o9nJx3U91i7vA==" + }, + "functionfoundry.networking": { + "type": "Project" + } + } + } +} \ No newline at end of file diff --git a/benchmarks/FunctionFoundry.Observability.Benchmarks/packages.lock.json b/benchmarks/FunctionFoundry.Observability.Benchmarks/packages.lock.json new file mode 100644 index 0000000..9e71489 --- /dev/null +++ b/benchmarks/FunctionFoundry.Observability.Benchmarks/packages.lock.json @@ -0,0 +1,166 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "BenchmarkDotNet": { + "type": "Direct", + "requested": "[0.15.2, )", + "resolved": "0.15.2", + "contentHash": "5vl4nQFUGTKgOVE5LNoLpv/X/wFQ7wE5yiaJAXCpzgNtUADtfEwbuufKI4o5E2De0VTXNEeom5Hnv8S8vuBZtg==", + "dependencies": { + "BenchmarkDotNet.Annotations": "0.15.2", + "CommandLineParser": "2.9.1", + "Gee.External.Capstone": "2.3.0", + "Iced": "1.21.0", + "Microsoft.CodeAnalysis.CSharp": "4.14.0", + "Microsoft.Diagnostics.Runtime": "3.1.512801", + "Microsoft.Diagnostics.Tracing.TraceEvent": "3.1.21", + "Microsoft.DotNet.PlatformAbstractions": "3.1.6", + "Perfolizer": "[0.5.3]", + "System.Management": "9.0.5" + } + }, + "BenchmarkDotNet.Annotations": { + "type": "Transitive", + "resolved": "0.15.2", + "contentHash": "a7rVlVmXQRrp3J+HF/FtjwiFcshU6BAuv6U1xtAbqCUYepOEnNGaPeOFvy4It17QZwwofm6U/GAfW3JrFMXszg==" + }, + "CommandLineParser": { + "type": "Transitive", + "resolved": "2.9.1", + "contentHash": "OE0sl1/sQ37bjVsPKKtwQlWDgqaxWgtme3xZz7JssWUzg5JpMIyHgCTY9MVMxOg48fJ1AgGT3tgdH5m/kQ5xhA==" + }, + "Gee.External.Capstone": { + "type": "Transitive", + "resolved": "2.3.0", + "contentHash": "2ap/rYmjtzCOT8hxrnEW/QeiOt+paD8iRrIcdKX0cxVwWLFa1e+JDBNeECakmccXrSFeBQuu5AV8SNkipFMMMw==" + }, + "Iced": { + "type": "Transitive", + "resolved": "1.21.0", + "contentHash": "dv5+81Q1TBQvVMSOOOmRcjJmvWcX3BZPZsIq31+RLc5cNft0IHAyNlkdb7ZarOWG913PyBoFDsDXoCIlKmLclg==" + }, + "Microsoft.CodeAnalysis.Analyzers": { + "type": "Transitive", + "resolved": "3.11.0", + "contentHash": "v/EW3UE8/lbEYHoC2Qq7AR/DnmvpgdtAMndfQNmpuIMx/Mto8L5JnuCfdBYtgvalQOtfNCnxFejxuRrryvUTsg==" + }, + "Microsoft.CodeAnalysis.Common": { + "type": "Transitive", + "resolved": "4.14.0", + "contentHash": "PC3tuwZYnC+idaPuoC/AZpEdwrtX7qFpmnrfQkgobGIWiYmGi5MCRtl5mx6QrfMGQpK78X2lfIEoZDLg/qnuHg==", + "dependencies": { + "Microsoft.CodeAnalysis.Analyzers": "3.11.0" + } + }, + "Microsoft.CodeAnalysis.CSharp": { + "type": "Transitive", + "resolved": "4.14.0", + "contentHash": "568a6wcTivauIhbeWcCwfWwIn7UV7MeHEBvFB2uzGIpM2OhJ4eM/FZ8KS0yhPoNxnSpjGzz7x7CIjTxhslojQA==", + "dependencies": { + "Microsoft.CodeAnalysis.Analyzers": "3.11.0", + "Microsoft.CodeAnalysis.Common": "[4.14.0]" + } + }, + "Microsoft.Diagnostics.NETCore.Client": { + "type": "Transitive", + "resolved": "0.2.510501", + "contentHash": "juoqJYMDs+lRrrZyOkXXMImJHneCF23cuvO4waFRd2Ds7j+ZuGIPbJm0Y/zz34BdeaGiiwGWraMUlln05W1PCQ==", + "dependencies": { + "Microsoft.Extensions.Logging": "6.0.0" + } + }, + "Microsoft.Diagnostics.Runtime": { + "type": "Transitive", + "resolved": "3.1.512801", + "contentHash": "0lMUDr2oxNZa28D6NH5BuSQEe5T9tZziIkvkD44YkkCGQXPJqvFjLq5ZQq1hYLl3RjQJrY+hR0jFgap+EWPDTw==", + "dependencies": { + "Microsoft.Diagnostics.NETCore.Client": "0.2.410101" + } + }, + "Microsoft.Diagnostics.Tracing.TraceEvent": { + "type": "Transitive", + "resolved": "3.1.21", + "contentHash": "/OrJFKaojSR6TkUKtwh8/qA9XWNtxLrXMqvEb89dBSKCWjaGVTbKMYodIUgF5deCEtmd6GXuRerciXGl5bhZ7Q==", + "dependencies": { + "Microsoft.Diagnostics.NETCore.Client": "0.2.510501", + "System.Reflection.TypeExtensions": "4.7.0" + } + }, + "Microsoft.DotNet.PlatformAbstractions": { + "type": "Transitive", + "resolved": "3.1.6", + "contentHash": "jek4XYaQ/PGUwDKKhwR8K47Uh1189PFzMeLqO83mXrXQVIpARZCcfuDedH50YDTepBkfijCZN5U/vZi++erxtg==" + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==" + }, + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "6.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/HggWBbTwy8TgebGSX5DBZ24ndhzi93sHUBDvP1IxbZD7FDokYzdAr6+vbWGjw2XAfR2EJ1sfKUotpjHnFWPxA==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==" + }, + "Perfolizer": { + "type": "Transitive", + "resolved": "0.5.3", + "contentHash": "EhHo0s4y+tHJKhZHgQvPgFWhAhnQpHHuFzutRzfqfv5z763wvhzXh849ZtpVePQNxXWoFafh1Ta2tZgnSIVsYQ==" + }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "9.0.5", + "contentHash": "cuzLM2MWutf9ZBEMPYYfd0DXwYdvntp7VCT6a/wvbKCa2ZuvGmW74xi+YBa2mrfEieAXqM4TNKlMmSnfAfpUoQ==" + }, + "System.Management": { + "type": "Transitive", + "resolved": "9.0.5", + "contentHash": "n6o9PZm9p25+zAzC3/48K0oHnaPKTInRrxqFq1fi/5TPbMLjuoCm/h//mS3cUmSy+9AO1Z+qsC/Ilt/ZFatv5Q==", + "dependencies": { + "System.CodeDom": "9.0.5" + } + }, + "System.Reflection.TypeExtensions": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "VybpaOQQhqE6siHppMktjfGBw1GCwvCqiufqmP8F1nj7fTUNtW35LOEt3UZTEsECfo+ELAl/9o9nJx3U91i7vA==" + }, + "functionfoundry.observability": { + "type": "Project" + } + } + } +} \ No newline at end of file diff --git a/benchmarks/FunctionFoundry.Resilience.Benchmarks/packages.lock.json b/benchmarks/FunctionFoundry.Resilience.Benchmarks/packages.lock.json new file mode 100644 index 0000000..c985007 --- /dev/null +++ b/benchmarks/FunctionFoundry.Resilience.Benchmarks/packages.lock.json @@ -0,0 +1,166 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "BenchmarkDotNet": { + "type": "Direct", + "requested": "[0.15.2, )", + "resolved": "0.15.2", + "contentHash": "5vl4nQFUGTKgOVE5LNoLpv/X/wFQ7wE5yiaJAXCpzgNtUADtfEwbuufKI4o5E2De0VTXNEeom5Hnv8S8vuBZtg==", + "dependencies": { + "BenchmarkDotNet.Annotations": "0.15.2", + "CommandLineParser": "2.9.1", + "Gee.External.Capstone": "2.3.0", + "Iced": "1.21.0", + "Microsoft.CodeAnalysis.CSharp": "4.14.0", + "Microsoft.Diagnostics.Runtime": "3.1.512801", + "Microsoft.Diagnostics.Tracing.TraceEvent": "3.1.21", + "Microsoft.DotNet.PlatformAbstractions": "3.1.6", + "Perfolizer": "[0.5.3]", + "System.Management": "9.0.5" + } + }, + "BenchmarkDotNet.Annotations": { + "type": "Transitive", + "resolved": "0.15.2", + "contentHash": "a7rVlVmXQRrp3J+HF/FtjwiFcshU6BAuv6U1xtAbqCUYepOEnNGaPeOFvy4It17QZwwofm6U/GAfW3JrFMXszg==" + }, + "CommandLineParser": { + "type": "Transitive", + "resolved": "2.9.1", + "contentHash": "OE0sl1/sQ37bjVsPKKtwQlWDgqaxWgtme3xZz7JssWUzg5JpMIyHgCTY9MVMxOg48fJ1AgGT3tgdH5m/kQ5xhA==" + }, + "Gee.External.Capstone": { + "type": "Transitive", + "resolved": "2.3.0", + "contentHash": "2ap/rYmjtzCOT8hxrnEW/QeiOt+paD8iRrIcdKX0cxVwWLFa1e+JDBNeECakmccXrSFeBQuu5AV8SNkipFMMMw==" + }, + "Iced": { + "type": "Transitive", + "resolved": "1.21.0", + "contentHash": "dv5+81Q1TBQvVMSOOOmRcjJmvWcX3BZPZsIq31+RLc5cNft0IHAyNlkdb7ZarOWG913PyBoFDsDXoCIlKmLclg==" + }, + "Microsoft.CodeAnalysis.Analyzers": { + "type": "Transitive", + "resolved": "3.11.0", + "contentHash": "v/EW3UE8/lbEYHoC2Qq7AR/DnmvpgdtAMndfQNmpuIMx/Mto8L5JnuCfdBYtgvalQOtfNCnxFejxuRrryvUTsg==" + }, + "Microsoft.CodeAnalysis.Common": { + "type": "Transitive", + "resolved": "4.14.0", + "contentHash": "PC3tuwZYnC+idaPuoC/AZpEdwrtX7qFpmnrfQkgobGIWiYmGi5MCRtl5mx6QrfMGQpK78X2lfIEoZDLg/qnuHg==", + "dependencies": { + "Microsoft.CodeAnalysis.Analyzers": "3.11.0" + } + }, + "Microsoft.CodeAnalysis.CSharp": { + "type": "Transitive", + "resolved": "4.14.0", + "contentHash": "568a6wcTivauIhbeWcCwfWwIn7UV7MeHEBvFB2uzGIpM2OhJ4eM/FZ8KS0yhPoNxnSpjGzz7x7CIjTxhslojQA==", + "dependencies": { + "Microsoft.CodeAnalysis.Analyzers": "3.11.0", + "Microsoft.CodeAnalysis.Common": "[4.14.0]" + } + }, + "Microsoft.Diagnostics.NETCore.Client": { + "type": "Transitive", + "resolved": "0.2.510501", + "contentHash": "juoqJYMDs+lRrrZyOkXXMImJHneCF23cuvO4waFRd2Ds7j+ZuGIPbJm0Y/zz34BdeaGiiwGWraMUlln05W1PCQ==", + "dependencies": { + "Microsoft.Extensions.Logging": "6.0.0" + } + }, + "Microsoft.Diagnostics.Runtime": { + "type": "Transitive", + "resolved": "3.1.512801", + "contentHash": "0lMUDr2oxNZa28D6NH5BuSQEe5T9tZziIkvkD44YkkCGQXPJqvFjLq5ZQq1hYLl3RjQJrY+hR0jFgap+EWPDTw==", + "dependencies": { + "Microsoft.Diagnostics.NETCore.Client": "0.2.410101" + } + }, + "Microsoft.Diagnostics.Tracing.TraceEvent": { + "type": "Transitive", + "resolved": "3.1.21", + "contentHash": "/OrJFKaojSR6TkUKtwh8/qA9XWNtxLrXMqvEb89dBSKCWjaGVTbKMYodIUgF5deCEtmd6GXuRerciXGl5bhZ7Q==", + "dependencies": { + "Microsoft.Diagnostics.NETCore.Client": "0.2.510501", + "System.Reflection.TypeExtensions": "4.7.0" + } + }, + "Microsoft.DotNet.PlatformAbstractions": { + "type": "Transitive", + "resolved": "3.1.6", + "contentHash": "jek4XYaQ/PGUwDKKhwR8K47Uh1189PFzMeLqO83mXrXQVIpARZCcfuDedH50YDTepBkfijCZN5U/vZi++erxtg==" + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==" + }, + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "6.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/HggWBbTwy8TgebGSX5DBZ24ndhzi93sHUBDvP1IxbZD7FDokYzdAr6+vbWGjw2XAfR2EJ1sfKUotpjHnFWPxA==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==" + }, + "Perfolizer": { + "type": "Transitive", + "resolved": "0.5.3", + "contentHash": "EhHo0s4y+tHJKhZHgQvPgFWhAhnQpHHuFzutRzfqfv5z763wvhzXh849ZtpVePQNxXWoFafh1Ta2tZgnSIVsYQ==" + }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "9.0.5", + "contentHash": "cuzLM2MWutf9ZBEMPYYfd0DXwYdvntp7VCT6a/wvbKCa2ZuvGmW74xi+YBa2mrfEieAXqM4TNKlMmSnfAfpUoQ==" + }, + "System.Management": { + "type": "Transitive", + "resolved": "9.0.5", + "contentHash": "n6o9PZm9p25+zAzC3/48K0oHnaPKTInRrxqFq1fi/5TPbMLjuoCm/h//mS3cUmSy+9AO1Z+qsC/Ilt/ZFatv5Q==", + "dependencies": { + "System.CodeDom": "9.0.5" + } + }, + "System.Reflection.TypeExtensions": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "VybpaOQQhqE6siHppMktjfGBw1GCwvCqiufqmP8F1nj7fTUNtW35LOEt3UZTEsECfo+ELAl/9o9nJx3U91i7vA==" + }, + "functionfoundry.resilience": { + "type": "Project" + } + } + } +} \ No newline at end of file diff --git a/benchmarks/FunctionFoundry.Scheduling.Benchmarks/packages.lock.json b/benchmarks/FunctionFoundry.Scheduling.Benchmarks/packages.lock.json new file mode 100644 index 0000000..450d3b9 --- /dev/null +++ b/benchmarks/FunctionFoundry.Scheduling.Benchmarks/packages.lock.json @@ -0,0 +1,166 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "BenchmarkDotNet": { + "type": "Direct", + "requested": "[0.15.2, )", + "resolved": "0.15.2", + "contentHash": "5vl4nQFUGTKgOVE5LNoLpv/X/wFQ7wE5yiaJAXCpzgNtUADtfEwbuufKI4o5E2De0VTXNEeom5Hnv8S8vuBZtg==", + "dependencies": { + "BenchmarkDotNet.Annotations": "0.15.2", + "CommandLineParser": "2.9.1", + "Gee.External.Capstone": "2.3.0", + "Iced": "1.21.0", + "Microsoft.CodeAnalysis.CSharp": "4.14.0", + "Microsoft.Diagnostics.Runtime": "3.1.512801", + "Microsoft.Diagnostics.Tracing.TraceEvent": "3.1.21", + "Microsoft.DotNet.PlatformAbstractions": "3.1.6", + "Perfolizer": "[0.5.3]", + "System.Management": "9.0.5" + } + }, + "BenchmarkDotNet.Annotations": { + "type": "Transitive", + "resolved": "0.15.2", + "contentHash": "a7rVlVmXQRrp3J+HF/FtjwiFcshU6BAuv6U1xtAbqCUYepOEnNGaPeOFvy4It17QZwwofm6U/GAfW3JrFMXszg==" + }, + "CommandLineParser": { + "type": "Transitive", + "resolved": "2.9.1", + "contentHash": "OE0sl1/sQ37bjVsPKKtwQlWDgqaxWgtme3xZz7JssWUzg5JpMIyHgCTY9MVMxOg48fJ1AgGT3tgdH5m/kQ5xhA==" + }, + "Gee.External.Capstone": { + "type": "Transitive", + "resolved": "2.3.0", + "contentHash": "2ap/rYmjtzCOT8hxrnEW/QeiOt+paD8iRrIcdKX0cxVwWLFa1e+JDBNeECakmccXrSFeBQuu5AV8SNkipFMMMw==" + }, + "Iced": { + "type": "Transitive", + "resolved": "1.21.0", + "contentHash": "dv5+81Q1TBQvVMSOOOmRcjJmvWcX3BZPZsIq31+RLc5cNft0IHAyNlkdb7ZarOWG913PyBoFDsDXoCIlKmLclg==" + }, + "Microsoft.CodeAnalysis.Analyzers": { + "type": "Transitive", + "resolved": "3.11.0", + "contentHash": "v/EW3UE8/lbEYHoC2Qq7AR/DnmvpgdtAMndfQNmpuIMx/Mto8L5JnuCfdBYtgvalQOtfNCnxFejxuRrryvUTsg==" + }, + "Microsoft.CodeAnalysis.Common": { + "type": "Transitive", + "resolved": "4.14.0", + "contentHash": "PC3tuwZYnC+idaPuoC/AZpEdwrtX7qFpmnrfQkgobGIWiYmGi5MCRtl5mx6QrfMGQpK78X2lfIEoZDLg/qnuHg==", + "dependencies": { + "Microsoft.CodeAnalysis.Analyzers": "3.11.0" + } + }, + "Microsoft.CodeAnalysis.CSharp": { + "type": "Transitive", + "resolved": "4.14.0", + "contentHash": "568a6wcTivauIhbeWcCwfWwIn7UV7MeHEBvFB2uzGIpM2OhJ4eM/FZ8KS0yhPoNxnSpjGzz7x7CIjTxhslojQA==", + "dependencies": { + "Microsoft.CodeAnalysis.Analyzers": "3.11.0", + "Microsoft.CodeAnalysis.Common": "[4.14.0]" + } + }, + "Microsoft.Diagnostics.NETCore.Client": { + "type": "Transitive", + "resolved": "0.2.510501", + "contentHash": "juoqJYMDs+lRrrZyOkXXMImJHneCF23cuvO4waFRd2Ds7j+ZuGIPbJm0Y/zz34BdeaGiiwGWraMUlln05W1PCQ==", + "dependencies": { + "Microsoft.Extensions.Logging": "6.0.0" + } + }, + "Microsoft.Diagnostics.Runtime": { + "type": "Transitive", + "resolved": "3.1.512801", + "contentHash": "0lMUDr2oxNZa28D6NH5BuSQEe5T9tZziIkvkD44YkkCGQXPJqvFjLq5ZQq1hYLl3RjQJrY+hR0jFgap+EWPDTw==", + "dependencies": { + "Microsoft.Diagnostics.NETCore.Client": "0.2.410101" + } + }, + "Microsoft.Diagnostics.Tracing.TraceEvent": { + "type": "Transitive", + "resolved": "3.1.21", + "contentHash": "/OrJFKaojSR6TkUKtwh8/qA9XWNtxLrXMqvEb89dBSKCWjaGVTbKMYodIUgF5deCEtmd6GXuRerciXGl5bhZ7Q==", + "dependencies": { + "Microsoft.Diagnostics.NETCore.Client": "0.2.510501", + "System.Reflection.TypeExtensions": "4.7.0" + } + }, + "Microsoft.DotNet.PlatformAbstractions": { + "type": "Transitive", + "resolved": "3.1.6", + "contentHash": "jek4XYaQ/PGUwDKKhwR8K47Uh1189PFzMeLqO83mXrXQVIpARZCcfuDedH50YDTepBkfijCZN5U/vZi++erxtg==" + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==" + }, + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "6.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/HggWBbTwy8TgebGSX5DBZ24ndhzi93sHUBDvP1IxbZD7FDokYzdAr6+vbWGjw2XAfR2EJ1sfKUotpjHnFWPxA==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==" + }, + "Perfolizer": { + "type": "Transitive", + "resolved": "0.5.3", + "contentHash": "EhHo0s4y+tHJKhZHgQvPgFWhAhnQpHHuFzutRzfqfv5z763wvhzXh849ZtpVePQNxXWoFafh1Ta2tZgnSIVsYQ==" + }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "9.0.5", + "contentHash": "cuzLM2MWutf9ZBEMPYYfd0DXwYdvntp7VCT6a/wvbKCa2ZuvGmW74xi+YBa2mrfEieAXqM4TNKlMmSnfAfpUoQ==" + }, + "System.Management": { + "type": "Transitive", + "resolved": "9.0.5", + "contentHash": "n6o9PZm9p25+zAzC3/48K0oHnaPKTInRrxqFq1fi/5TPbMLjuoCm/h//mS3cUmSy+9AO1Z+qsC/Ilt/ZFatv5Q==", + "dependencies": { + "System.CodeDom": "9.0.5" + } + }, + "System.Reflection.TypeExtensions": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "VybpaOQQhqE6siHppMktjfGBw1GCwvCqiufqmP8F1nj7fTUNtW35LOEt3UZTEsECfo+ELAl/9o9nJx3U91i7vA==" + }, + "functionfoundry.scheduling": { + "type": "Project" + } + } + } +} \ No newline at end of file diff --git a/benchmarks/FunctionFoundry.Security.Benchmarks/packages.lock.json b/benchmarks/FunctionFoundry.Security.Benchmarks/packages.lock.json new file mode 100644 index 0000000..592e3a0 --- /dev/null +++ b/benchmarks/FunctionFoundry.Security.Benchmarks/packages.lock.json @@ -0,0 +1,166 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "BenchmarkDotNet": { + "type": "Direct", + "requested": "[0.15.2, )", + "resolved": "0.15.2", + "contentHash": "5vl4nQFUGTKgOVE5LNoLpv/X/wFQ7wE5yiaJAXCpzgNtUADtfEwbuufKI4o5E2De0VTXNEeom5Hnv8S8vuBZtg==", + "dependencies": { + "BenchmarkDotNet.Annotations": "0.15.2", + "CommandLineParser": "2.9.1", + "Gee.External.Capstone": "2.3.0", + "Iced": "1.21.0", + "Microsoft.CodeAnalysis.CSharp": "4.14.0", + "Microsoft.Diagnostics.Runtime": "3.1.512801", + "Microsoft.Diagnostics.Tracing.TraceEvent": "3.1.21", + "Microsoft.DotNet.PlatformAbstractions": "3.1.6", + "Perfolizer": "[0.5.3]", + "System.Management": "9.0.5" + } + }, + "BenchmarkDotNet.Annotations": { + "type": "Transitive", + "resolved": "0.15.2", + "contentHash": "a7rVlVmXQRrp3J+HF/FtjwiFcshU6BAuv6U1xtAbqCUYepOEnNGaPeOFvy4It17QZwwofm6U/GAfW3JrFMXszg==" + }, + "CommandLineParser": { + "type": "Transitive", + "resolved": "2.9.1", + "contentHash": "OE0sl1/sQ37bjVsPKKtwQlWDgqaxWgtme3xZz7JssWUzg5JpMIyHgCTY9MVMxOg48fJ1AgGT3tgdH5m/kQ5xhA==" + }, + "Gee.External.Capstone": { + "type": "Transitive", + "resolved": "2.3.0", + "contentHash": "2ap/rYmjtzCOT8hxrnEW/QeiOt+paD8iRrIcdKX0cxVwWLFa1e+JDBNeECakmccXrSFeBQuu5AV8SNkipFMMMw==" + }, + "Iced": { + "type": "Transitive", + "resolved": "1.21.0", + "contentHash": "dv5+81Q1TBQvVMSOOOmRcjJmvWcX3BZPZsIq31+RLc5cNft0IHAyNlkdb7ZarOWG913PyBoFDsDXoCIlKmLclg==" + }, + "Microsoft.CodeAnalysis.Analyzers": { + "type": "Transitive", + "resolved": "3.11.0", + "contentHash": "v/EW3UE8/lbEYHoC2Qq7AR/DnmvpgdtAMndfQNmpuIMx/Mto8L5JnuCfdBYtgvalQOtfNCnxFejxuRrryvUTsg==" + }, + "Microsoft.CodeAnalysis.Common": { + "type": "Transitive", + "resolved": "4.14.0", + "contentHash": "PC3tuwZYnC+idaPuoC/AZpEdwrtX7qFpmnrfQkgobGIWiYmGi5MCRtl5mx6QrfMGQpK78X2lfIEoZDLg/qnuHg==", + "dependencies": { + "Microsoft.CodeAnalysis.Analyzers": "3.11.0" + } + }, + "Microsoft.CodeAnalysis.CSharp": { + "type": "Transitive", + "resolved": "4.14.0", + "contentHash": "568a6wcTivauIhbeWcCwfWwIn7UV7MeHEBvFB2uzGIpM2OhJ4eM/FZ8KS0yhPoNxnSpjGzz7x7CIjTxhslojQA==", + "dependencies": { + "Microsoft.CodeAnalysis.Analyzers": "3.11.0", + "Microsoft.CodeAnalysis.Common": "[4.14.0]" + } + }, + "Microsoft.Diagnostics.NETCore.Client": { + "type": "Transitive", + "resolved": "0.2.510501", + "contentHash": "juoqJYMDs+lRrrZyOkXXMImJHneCF23cuvO4waFRd2Ds7j+ZuGIPbJm0Y/zz34BdeaGiiwGWraMUlln05W1PCQ==", + "dependencies": { + "Microsoft.Extensions.Logging": "6.0.0" + } + }, + "Microsoft.Diagnostics.Runtime": { + "type": "Transitive", + "resolved": "3.1.512801", + "contentHash": "0lMUDr2oxNZa28D6NH5BuSQEe5T9tZziIkvkD44YkkCGQXPJqvFjLq5ZQq1hYLl3RjQJrY+hR0jFgap+EWPDTw==", + "dependencies": { + "Microsoft.Diagnostics.NETCore.Client": "0.2.410101" + } + }, + "Microsoft.Diagnostics.Tracing.TraceEvent": { + "type": "Transitive", + "resolved": "3.1.21", + "contentHash": "/OrJFKaojSR6TkUKtwh8/qA9XWNtxLrXMqvEb89dBSKCWjaGVTbKMYodIUgF5deCEtmd6GXuRerciXGl5bhZ7Q==", + "dependencies": { + "Microsoft.Diagnostics.NETCore.Client": "0.2.510501", + "System.Reflection.TypeExtensions": "4.7.0" + } + }, + "Microsoft.DotNet.PlatformAbstractions": { + "type": "Transitive", + "resolved": "3.1.6", + "contentHash": "jek4XYaQ/PGUwDKKhwR8K47Uh1189PFzMeLqO83mXrXQVIpARZCcfuDedH50YDTepBkfijCZN5U/vZi++erxtg==" + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==" + }, + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "6.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/HggWBbTwy8TgebGSX5DBZ24ndhzi93sHUBDvP1IxbZD7FDokYzdAr6+vbWGjw2XAfR2EJ1sfKUotpjHnFWPxA==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==" + }, + "Perfolizer": { + "type": "Transitive", + "resolved": "0.5.3", + "contentHash": "EhHo0s4y+tHJKhZHgQvPgFWhAhnQpHHuFzutRzfqfv5z763wvhzXh849ZtpVePQNxXWoFafh1Ta2tZgnSIVsYQ==" + }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "9.0.5", + "contentHash": "cuzLM2MWutf9ZBEMPYYfd0DXwYdvntp7VCT6a/wvbKCa2ZuvGmW74xi+YBa2mrfEieAXqM4TNKlMmSnfAfpUoQ==" + }, + "System.Management": { + "type": "Transitive", + "resolved": "9.0.5", + "contentHash": "n6o9PZm9p25+zAzC3/48K0oHnaPKTInRrxqFq1fi/5TPbMLjuoCm/h//mS3cUmSy+9AO1Z+qsC/Ilt/ZFatv5Q==", + "dependencies": { + "System.CodeDom": "9.0.5" + } + }, + "System.Reflection.TypeExtensions": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "VybpaOQQhqE6siHppMktjfGBw1GCwvCqiufqmP8F1nj7fTUNtW35LOEt3UZTEsECfo+ELAl/9o9nJx3U91i7vA==" + }, + "functionfoundry.security": { + "type": "Project" + } + } + } +} \ No newline at end of file diff --git a/benchmarks/FunctionFoundry.Storage.Benchmarks/packages.lock.json b/benchmarks/FunctionFoundry.Storage.Benchmarks/packages.lock.json new file mode 100644 index 0000000..22342b8 --- /dev/null +++ b/benchmarks/FunctionFoundry.Storage.Benchmarks/packages.lock.json @@ -0,0 +1,166 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "BenchmarkDotNet": { + "type": "Direct", + "requested": "[0.15.2, )", + "resolved": "0.15.2", + "contentHash": "5vl4nQFUGTKgOVE5LNoLpv/X/wFQ7wE5yiaJAXCpzgNtUADtfEwbuufKI4o5E2De0VTXNEeom5Hnv8S8vuBZtg==", + "dependencies": { + "BenchmarkDotNet.Annotations": "0.15.2", + "CommandLineParser": "2.9.1", + "Gee.External.Capstone": "2.3.0", + "Iced": "1.21.0", + "Microsoft.CodeAnalysis.CSharp": "4.14.0", + "Microsoft.Diagnostics.Runtime": "3.1.512801", + "Microsoft.Diagnostics.Tracing.TraceEvent": "3.1.21", + "Microsoft.DotNet.PlatformAbstractions": "3.1.6", + "Perfolizer": "[0.5.3]", + "System.Management": "9.0.5" + } + }, + "BenchmarkDotNet.Annotations": { + "type": "Transitive", + "resolved": "0.15.2", + "contentHash": "a7rVlVmXQRrp3J+HF/FtjwiFcshU6BAuv6U1xtAbqCUYepOEnNGaPeOFvy4It17QZwwofm6U/GAfW3JrFMXszg==" + }, + "CommandLineParser": { + "type": "Transitive", + "resolved": "2.9.1", + "contentHash": "OE0sl1/sQ37bjVsPKKtwQlWDgqaxWgtme3xZz7JssWUzg5JpMIyHgCTY9MVMxOg48fJ1AgGT3tgdH5m/kQ5xhA==" + }, + "Gee.External.Capstone": { + "type": "Transitive", + "resolved": "2.3.0", + "contentHash": "2ap/rYmjtzCOT8hxrnEW/QeiOt+paD8iRrIcdKX0cxVwWLFa1e+JDBNeECakmccXrSFeBQuu5AV8SNkipFMMMw==" + }, + "Iced": { + "type": "Transitive", + "resolved": "1.21.0", + "contentHash": "dv5+81Q1TBQvVMSOOOmRcjJmvWcX3BZPZsIq31+RLc5cNft0IHAyNlkdb7ZarOWG913PyBoFDsDXoCIlKmLclg==" + }, + "Microsoft.CodeAnalysis.Analyzers": { + "type": "Transitive", + "resolved": "3.11.0", + "contentHash": "v/EW3UE8/lbEYHoC2Qq7AR/DnmvpgdtAMndfQNmpuIMx/Mto8L5JnuCfdBYtgvalQOtfNCnxFejxuRrryvUTsg==" + }, + "Microsoft.CodeAnalysis.Common": { + "type": "Transitive", + "resolved": "4.14.0", + "contentHash": "PC3tuwZYnC+idaPuoC/AZpEdwrtX7qFpmnrfQkgobGIWiYmGi5MCRtl5mx6QrfMGQpK78X2lfIEoZDLg/qnuHg==", + "dependencies": { + "Microsoft.CodeAnalysis.Analyzers": "3.11.0" + } + }, + "Microsoft.CodeAnalysis.CSharp": { + "type": "Transitive", + "resolved": "4.14.0", + "contentHash": "568a6wcTivauIhbeWcCwfWwIn7UV7MeHEBvFB2uzGIpM2OhJ4eM/FZ8KS0yhPoNxnSpjGzz7x7CIjTxhslojQA==", + "dependencies": { + "Microsoft.CodeAnalysis.Analyzers": "3.11.0", + "Microsoft.CodeAnalysis.Common": "[4.14.0]" + } + }, + "Microsoft.Diagnostics.NETCore.Client": { + "type": "Transitive", + "resolved": "0.2.510501", + "contentHash": "juoqJYMDs+lRrrZyOkXXMImJHneCF23cuvO4waFRd2Ds7j+ZuGIPbJm0Y/zz34BdeaGiiwGWraMUlln05W1PCQ==", + "dependencies": { + "Microsoft.Extensions.Logging": "6.0.0" + } + }, + "Microsoft.Diagnostics.Runtime": { + "type": "Transitive", + "resolved": "3.1.512801", + "contentHash": "0lMUDr2oxNZa28D6NH5BuSQEe5T9tZziIkvkD44YkkCGQXPJqvFjLq5ZQq1hYLl3RjQJrY+hR0jFgap+EWPDTw==", + "dependencies": { + "Microsoft.Diagnostics.NETCore.Client": "0.2.410101" + } + }, + "Microsoft.Diagnostics.Tracing.TraceEvent": { + "type": "Transitive", + "resolved": "3.1.21", + "contentHash": "/OrJFKaojSR6TkUKtwh8/qA9XWNtxLrXMqvEb89dBSKCWjaGVTbKMYodIUgF5deCEtmd6GXuRerciXGl5bhZ7Q==", + "dependencies": { + "Microsoft.Diagnostics.NETCore.Client": "0.2.510501", + "System.Reflection.TypeExtensions": "4.7.0" + } + }, + "Microsoft.DotNet.PlatformAbstractions": { + "type": "Transitive", + "resolved": "3.1.6", + "contentHash": "jek4XYaQ/PGUwDKKhwR8K47Uh1189PFzMeLqO83mXrXQVIpARZCcfuDedH50YDTepBkfijCZN5U/vZi++erxtg==" + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==" + }, + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "6.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/HggWBbTwy8TgebGSX5DBZ24ndhzi93sHUBDvP1IxbZD7FDokYzdAr6+vbWGjw2XAfR2EJ1sfKUotpjHnFWPxA==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==" + }, + "Perfolizer": { + "type": "Transitive", + "resolved": "0.5.3", + "contentHash": "EhHo0s4y+tHJKhZHgQvPgFWhAhnQpHHuFzutRzfqfv5z763wvhzXh849ZtpVePQNxXWoFafh1Ta2tZgnSIVsYQ==" + }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "9.0.5", + "contentHash": "cuzLM2MWutf9ZBEMPYYfd0DXwYdvntp7VCT6a/wvbKCa2ZuvGmW74xi+YBa2mrfEieAXqM4TNKlMmSnfAfpUoQ==" + }, + "System.Management": { + "type": "Transitive", + "resolved": "9.0.5", + "contentHash": "n6o9PZm9p25+zAzC3/48K0oHnaPKTInRrxqFq1fi/5TPbMLjuoCm/h//mS3cUmSy+9AO1Z+qsC/Ilt/ZFatv5Q==", + "dependencies": { + "System.CodeDom": "9.0.5" + } + }, + "System.Reflection.TypeExtensions": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "VybpaOQQhqE6siHppMktjfGBw1GCwvCqiufqmP8F1nj7fTUNtW35LOEt3UZTEsECfo+ELAl/9o9nJx3U91i7vA==" + }, + "functionfoundry.storage": { + "type": "Project" + } + } + } +} \ No newline at end of file diff --git a/benchmarks/FunctionFoundry.Text.Benchmarks/packages.lock.json b/benchmarks/FunctionFoundry.Text.Benchmarks/packages.lock.json new file mode 100644 index 0000000..0e228fe --- /dev/null +++ b/benchmarks/FunctionFoundry.Text.Benchmarks/packages.lock.json @@ -0,0 +1,166 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "BenchmarkDotNet": { + "type": "Direct", + "requested": "[0.15.2, )", + "resolved": "0.15.2", + "contentHash": "5vl4nQFUGTKgOVE5LNoLpv/X/wFQ7wE5yiaJAXCpzgNtUADtfEwbuufKI4o5E2De0VTXNEeom5Hnv8S8vuBZtg==", + "dependencies": { + "BenchmarkDotNet.Annotations": "0.15.2", + "CommandLineParser": "2.9.1", + "Gee.External.Capstone": "2.3.0", + "Iced": "1.21.0", + "Microsoft.CodeAnalysis.CSharp": "4.14.0", + "Microsoft.Diagnostics.Runtime": "3.1.512801", + "Microsoft.Diagnostics.Tracing.TraceEvent": "3.1.21", + "Microsoft.DotNet.PlatformAbstractions": "3.1.6", + "Perfolizer": "[0.5.3]", + "System.Management": "9.0.5" + } + }, + "BenchmarkDotNet.Annotations": { + "type": "Transitive", + "resolved": "0.15.2", + "contentHash": "a7rVlVmXQRrp3J+HF/FtjwiFcshU6BAuv6U1xtAbqCUYepOEnNGaPeOFvy4It17QZwwofm6U/GAfW3JrFMXszg==" + }, + "CommandLineParser": { + "type": "Transitive", + "resolved": "2.9.1", + "contentHash": "OE0sl1/sQ37bjVsPKKtwQlWDgqaxWgtme3xZz7JssWUzg5JpMIyHgCTY9MVMxOg48fJ1AgGT3tgdH5m/kQ5xhA==" + }, + "Gee.External.Capstone": { + "type": "Transitive", + "resolved": "2.3.0", + "contentHash": "2ap/rYmjtzCOT8hxrnEW/QeiOt+paD8iRrIcdKX0cxVwWLFa1e+JDBNeECakmccXrSFeBQuu5AV8SNkipFMMMw==" + }, + "Iced": { + "type": "Transitive", + "resolved": "1.21.0", + "contentHash": "dv5+81Q1TBQvVMSOOOmRcjJmvWcX3BZPZsIq31+RLc5cNft0IHAyNlkdb7ZarOWG913PyBoFDsDXoCIlKmLclg==" + }, + "Microsoft.CodeAnalysis.Analyzers": { + "type": "Transitive", + "resolved": "3.11.0", + "contentHash": "v/EW3UE8/lbEYHoC2Qq7AR/DnmvpgdtAMndfQNmpuIMx/Mto8L5JnuCfdBYtgvalQOtfNCnxFejxuRrryvUTsg==" + }, + "Microsoft.CodeAnalysis.Common": { + "type": "Transitive", + "resolved": "4.14.0", + "contentHash": "PC3tuwZYnC+idaPuoC/AZpEdwrtX7qFpmnrfQkgobGIWiYmGi5MCRtl5mx6QrfMGQpK78X2lfIEoZDLg/qnuHg==", + "dependencies": { + "Microsoft.CodeAnalysis.Analyzers": "3.11.0" + } + }, + "Microsoft.CodeAnalysis.CSharp": { + "type": "Transitive", + "resolved": "4.14.0", + "contentHash": "568a6wcTivauIhbeWcCwfWwIn7UV7MeHEBvFB2uzGIpM2OhJ4eM/FZ8KS0yhPoNxnSpjGzz7x7CIjTxhslojQA==", + "dependencies": { + "Microsoft.CodeAnalysis.Analyzers": "3.11.0", + "Microsoft.CodeAnalysis.Common": "[4.14.0]" + } + }, + "Microsoft.Diagnostics.NETCore.Client": { + "type": "Transitive", + "resolved": "0.2.510501", + "contentHash": "juoqJYMDs+lRrrZyOkXXMImJHneCF23cuvO4waFRd2Ds7j+ZuGIPbJm0Y/zz34BdeaGiiwGWraMUlln05W1PCQ==", + "dependencies": { + "Microsoft.Extensions.Logging": "6.0.0" + } + }, + "Microsoft.Diagnostics.Runtime": { + "type": "Transitive", + "resolved": "3.1.512801", + "contentHash": "0lMUDr2oxNZa28D6NH5BuSQEe5T9tZziIkvkD44YkkCGQXPJqvFjLq5ZQq1hYLl3RjQJrY+hR0jFgap+EWPDTw==", + "dependencies": { + "Microsoft.Diagnostics.NETCore.Client": "0.2.410101" + } + }, + "Microsoft.Diagnostics.Tracing.TraceEvent": { + "type": "Transitive", + "resolved": "3.1.21", + "contentHash": "/OrJFKaojSR6TkUKtwh8/qA9XWNtxLrXMqvEb89dBSKCWjaGVTbKMYodIUgF5deCEtmd6GXuRerciXGl5bhZ7Q==", + "dependencies": { + "Microsoft.Diagnostics.NETCore.Client": "0.2.510501", + "System.Reflection.TypeExtensions": "4.7.0" + } + }, + "Microsoft.DotNet.PlatformAbstractions": { + "type": "Transitive", + "resolved": "3.1.6", + "contentHash": "jek4XYaQ/PGUwDKKhwR8K47Uh1189PFzMeLqO83mXrXQVIpARZCcfuDedH50YDTepBkfijCZN5U/vZi++erxtg==" + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==" + }, + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "6.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Logging.Abstractions": "6.0.0", + "Microsoft.Extensions.Options": "6.0.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/HggWBbTwy8TgebGSX5DBZ24ndhzi93sHUBDvP1IxbZD7FDokYzdAr6+vbWGjw2XAfR2EJ1sfKUotpjHnFWPxA==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0", + "Microsoft.Extensions.Primitives": "6.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==" + }, + "Perfolizer": { + "type": "Transitive", + "resolved": "0.5.3", + "contentHash": "EhHo0s4y+tHJKhZHgQvPgFWhAhnQpHHuFzutRzfqfv5z763wvhzXh849ZtpVePQNxXWoFafh1Ta2tZgnSIVsYQ==" + }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "9.0.5", + "contentHash": "cuzLM2MWutf9ZBEMPYYfd0DXwYdvntp7VCT6a/wvbKCa2ZuvGmW74xi+YBa2mrfEieAXqM4TNKlMmSnfAfpUoQ==" + }, + "System.Management": { + "type": "Transitive", + "resolved": "9.0.5", + "contentHash": "n6o9PZm9p25+zAzC3/48K0oHnaPKTInRrxqFq1fi/5TPbMLjuoCm/h//mS3cUmSy+9AO1Z+qsC/Ilt/ZFatv5Q==", + "dependencies": { + "System.CodeDom": "9.0.5" + } + }, + "System.Reflection.TypeExtensions": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "VybpaOQQhqE6siHppMktjfGBw1GCwvCqiufqmP8F1nj7fTUNtW35LOEt3UZTEsECfo+ELAl/9o9nJx3U91i7vA==" + }, + "functionfoundry.text": { + "type": "Project" + } + } + } +} \ No newline at end of file diff --git a/build/publish-release.cmd b/build/publish-release.cmd index 6ddbcc5..19b2f61 100644 --- a/build/publish-release.cmd +++ b/build/publish-release.cmd @@ -10,6 +10,7 @@ set "STAGE_PACKAGES=%RELEASE_DIR%\packages" set "DOTNET_NOLOGO=1" set "DOTNET_CLI_TELEMETRY_OPTOUT=1" +if not defined FF_RELEASE_PACK set "FF_RELEASE_PACK=true" echo ==> Repository root: %ROOT% echo ==> Cleaning %RELEASE_DIR% @@ -18,17 +19,17 @@ mkdir "%STAGE_BIN%" mkdir "%STAGE_PACKAGES%" echo ==> Restoring -dotnet restore FunctionFoundry.slnx +dotnet restore --locked-mode FunctionFoundry.slnx if errorlevel 1 exit /b 1 echo ==> Building Release -dotnet build FunctionFoundry.slnx -c Release --no-restore +dotnet build FunctionFoundry.slnx -c Release --no-restore -p:VersionSuffix= if errorlevel 1 exit /b 1 echo ==> Packing NuGet packages if exist "%ROOT%\artifacts\packages" rmdir /s /q "%ROOT%\artifacts\packages" mkdir "%ROOT%\artifacts\packages" -dotnet pack FunctionFoundry.slnx -c Release --no-build -o "%ROOT%\artifacts\packages" +dotnet pack FunctionFoundry.slnx -c Release --no-build -o "%ROOT%\artifacts\packages" -p:VersionSuffix= if errorlevel 1 exit /b 1 echo ==> Collecting library binaries diff --git a/build/publish-release.sh b/build/publish-release.sh index 860c990..29de8dc 100755 --- a/build/publish-release.sh +++ b/build/publish-release.sh @@ -7,6 +7,7 @@ cd "$ROOT" export DOTNET_NOLOGO="${DOTNET_NOLOGO:-1}" export DOTNET_CLI_TELEMETRY_OPTOUT="${DOTNET_CLI_TELEMETRY_OPTOUT:-1}" +export FF_RELEASE_PACK="${FF_RELEASE_PACK:-true}" RELEASE_DIR="$ROOT/Release" STAGE_BIN="$RELEASE_DIR/bin" @@ -18,15 +19,15 @@ rm -rf "$RELEASE_DIR" mkdir -p "$STAGE_BIN" "$STAGE_PACKAGES" echo "==> Restoring" -dotnet restore FunctionFoundry.slnx +dotnet restore --locked-mode FunctionFoundry.slnx echo "==> Building Release" -dotnet build FunctionFoundry.slnx -c Release --no-restore +dotnet build FunctionFoundry.slnx -c Release --no-restore -p:VersionSuffix= echo "==> Packing NuGet packages" rm -rf "$ROOT/artifacts/packages" mkdir -p "$ROOT/artifacts/packages" -dotnet pack FunctionFoundry.slnx -c Release --no-build -o "$ROOT/artifacts/packages" +dotnet pack FunctionFoundry.slnx -c Release --no-build -o "$ROOT/artifacts/packages" -p:VersionSuffix= echo "==> Collecting library binaries" shopt -s nullglob diff --git a/docs/FINAL_REPORT.md b/docs/FINAL_REPORT.md index 668c5ba..668d7c2 100644 --- a/docs/FINAL_REPORT.md +++ b/docs/FINAL_REPORT.md @@ -7,7 +7,7 @@ * `tests/` — per-package xUnit projects + Engineering.Tests * `samples/` / `benchmarks/` — per-package executables * `docs/adr`, `docs/packages`, CI under `.github/workflows` -* Central build: `Directory.Build.props/targets`, `Directory.Packages.props`, `global.json` (10.0.301) +* Central build: `Directory.Build.props/targets`, `Directory.Packages.props`, `global.json` (**10.0.302**) ## 2. Delivered packages @@ -40,17 +40,18 @@ Listed above (also `git log --grep='\[PBI-'`). ## 6. Test counts and coverage -* **182** tests passed (`dotnet test -c Release`) -* Breakdown: Engineering 3, Security 12, Storage 33, Integrity 36, Observability 22, Data 13, Text 13, Resilience 14, Networking 10, Distributed 16, Scheduling 10 -* Formal cobertura threshold gate not numerically asserted in CI YAML (tests pass; coverage collector configured) +* **242** unit tests passed on remediation branch (pre-remediation baseline was 224 in `docs/coverage-summary.md`) +* Line coverage baseline **84.8%**, branch **72.3%** (pre-remediation figures) +* CI now enforces ≥**85%** aggregated line coverage and builds all samples +* Branch coverage remains below the aspirational ≥90% critical-algorithm target; Distributed and Integrity received additional tests on the remediation branch ## 7. Benchmark summary -BenchmarkDotNet projects exist for all ten packages. Initial baseline environment: .NET 10.0.301 / linux-x64. Benchmarks were compiled; long full BenchmarkDotNet runs were not used as CI gates. +BenchmarkDotNet projects exist for all ten packages. Benchmarks compile; long full BenchmarkDotNet runs are not CI gates. ## 8. DLL and NuGet sizes -See `docs/packages/size-and-compatibility-report-v1.md` (Storage nupkg ~47KB largest; Security ~21KB smallest). +See `docs/packages/size-and-compatibility-report-v1.md`. ## 9. Runtime dependency list @@ -60,8 +61,7 @@ See `docs/packages/size-and-compatibility-report-v1.md` (Storage nupkg ~47KB lar ## 10. Security review summary * Security package uses only BCL crypto (AES-GCM, HMAC-SHA256, RNG) -* Auth failures clear plaintext buffers; fixed-time compare helper -* Threat model in ADR-0005 and package docs; no formal proof claims +* Threat model in ADR-0005; private reporting via GitHub Security Advisories (see `SECURITY.md`) * Secret scanner / redaction are heuristic and documented as such ## 11. XML documentation status @@ -72,28 +72,27 @@ See `docs/packages/size-and-compatibility-report-v1.md` (Storage nupkg ~47KB lar ## 12. Package-validation status * `EnablePackageValidation=true` for packable projects -* First release: no prior baseline package to validate against +* Baseline version should be set to published `1.0.0` after the clean NuGet publish (not the accidental `1.0.0-local`) ## 13. Generated artifact locations -* `artifacts/packages/FunctionFoundry.*.1.0.0.nupkg` (+ `.snupkg`) +* `artifacts/packages/FunctionFoundry.*.nupkg` (+ `.snupkg`) * `artifacts/bin/**/release/*.dll` +* Release bundle via `build/publish-release.*` (sets `FF_RELEASE_PACK=true` for clean versions) -## 14. Tags / releases that succeeded +## 14. Tags / releases -* Git tag **`v1.0.0`** pushed to origin -* NuGet.org publish: **not performed** (no publish credentials/permission invoked) -* GitHub Release asset upload: not claimed +* Git tag **`1.0.0`** on origin (docs previously said `v1.0.0`; the published tag has no `v` prefix) +* NuGet.org: accidental **`1.0.0-local`** present; clean **`1.0.0`** publish pending `NUGET_API_KEY` + Publish workflow +* GitHub Release `1.0.0` exists (verify release notes reference `Chookees/FunctionFoundry`, not prior repo names) ## 15. Deviations * Cloud branch naming `cursor/...-482e` instead of `pbi/...` * PBI commit order on main not strictly 01→12 -* Separate `v0.5.0` tag omitted; Feature 1 hardening folded into `8a90d76` / v1.0 docs +* Separate `v0.5.0` tag omitted * Unicode confusables: compact subset, not full UCD -* Some Networking adaptive-repair behaviors are partial (documented by implementers) -* CI does not yet fail on cobertura % threshold numbers -* `IsAotCompatible` not claimed +* SDK maintenance upgrade **10.0.301 → 10.0.302** ## 16. Remaining known risks diff --git a/docs/adr/0003-sdk-pinning-policy.md b/docs/adr/0003-sdk-pinning-policy.md index a24289a..3263253 100644 --- a/docs/adr/0003-sdk-pinning-policy.md +++ b/docs/adr/0003-sdk-pinning-policy.md @@ -1,7 +1,8 @@ -# ADR-0003: Pin .NET SDK 10.0.301 with explicit upgrades +# ADR-0003: Pin .NET SDK with explicit upgrades * **Status:** Accepted * **Date:** 2026-07-14 +* **Updated:** 2026-08-03 * **Deciders:** FunctionFoundry architects ## Context @@ -10,7 +11,7 @@ Silent SDK roll-forward can change analyzer behavior, pack outputs, and language ## Decision -* Pin SDK `10.0.301` in `global.json`. +* Pin SDK `10.0.302` in `global.json` (maintenance upgrade from `10.0.301`). * Set `rollForward` to `disable`. * Target `net10.0` and C# 14.0. * Treat future SDK moves as explicit maintenance upgrades with changelog notes and CI verification. diff --git a/docs/coverage-summary.md b/docs/coverage-summary.md index c117fea..6e071ba 100644 --- a/docs/coverage-summary.md +++ b/docs/coverage-summary.md @@ -1,32 +1,32 @@ # Unit test coverage summary -Measured with Coverlet Cobertura on a Release `net10.0` build (SDK 10.0.301). +Measured with Coverlet Cobertura on a Release `net10.0` build (SDK **10.0.302**), aggregated by unique package name (same methodology as CI). -**Unit tests:** 224 passed, 0 failed, 0 skipped. +**Unit tests:** 242 passed, 0 failed, 0 skipped. ## Overall | Metric | Value | |---|---:| -| Line coverage (`src/` libraries) | **84.8%** (4327 / 5103) | -| Branch coverage (`src/` libraries) | **72.3%** (1791 / 2478) | +| Line coverage (`src/` libraries) | **85.8%** (8858 / 10326) | +| CI gate | ≥ **85%** line (enforced) | ## By package -| Package | Line coverage | Branch coverage | -|---|---:|---:| -| `FunctionFoundry.Data` | 81.6% (496/608) | 72.6% (231/318) | -| `FunctionFoundry.Distributed` | 77.8% (346/445) | 62.1% (154/248) | -| `FunctionFoundry.Integrity` | 84.6% (468/553) | 67.8% (213/314) | -| `FunctionFoundry.Networking` | 91.7% (287/313) | 76.0% (111/146) | -| `FunctionFoundry.Observability` | 85.5% (523/612) | 71.3% (231/324) | -| `FunctionFoundry.Resilience` | 81.2% (428/527) | 70.9% (139/196) | -| `FunctionFoundry.Scheduling` | 84.1% (392/466) | 75.6% (186/246) | -| `FunctionFoundry.Security` | 90.4% (293/324) | 72.8% (99/136) | -| `FunctionFoundry.Storage` | 85.7% (683/797) | 75.5% (240/318) | -| `FunctionFoundry.Text` | 89.7% (411/458) | 80.6% (187/232) | -| **Overall (src libraries)** | **84.8% (4327/5103)** | **72.3% (1791/2478)** | +| Package | Line coverage | +|---|---:| +| `FunctionFoundry.Data` | 81.6% (992/1216) | +| `FunctionFoundry.Distributed` | 87.9% (782/890) | +| `FunctionFoundry.Integrity` | 85.9% (952/1108) | +| `FunctionFoundry.Networking` | 90.3% (672/744) | +| `FunctionFoundry.Observability` | 85.6% (1048/1224) | +| `FunctionFoundry.Resilience` | 81.2% (856/1054) | +| `FunctionFoundry.Scheduling` | 84.1% (784/932) | +| `FunctionFoundry.Security` | 90.4% (586/648) | +| `FunctionFoundry.Storage` | 85.6% (1364/1594) | +| `FunctionFoundry.Text` | 89.7% (822/916) | +| **Overall (src libraries)** | **85.8% (8858/10326)** | ## Pipeline note -Latest verified green CI for the expanded test branch: https://github.com/Chookees/FunctionFoundry/actions/runs/29378869693 +CI fails when aggregated line coverage is below 85%. Branch-coverage ≥90% for critical algorithms remains an aspirational target (not yet a hard gate). diff --git a/docs/packages/FunctionFoundry.Networking.md b/docs/packages/FunctionFoundry.Networking.md index d1c63bd..40743dc 100644 --- a/docs/packages/FunctionFoundry.Networking.md +++ b/docs/packages/FunctionFoundry.Networking.md @@ -14,6 +14,6 @@ Capability is detected with a lightweight `Range: bytes=0-0` probe. When ranges Download and transfer checkpoints are persisted through abstractions so hosts can use files, object storage, or databases. Incompatible checkpoints (changed ETag, size, or plan version) are rejected explicitly. -### Integrity +### Adaptive chunks and repair -`StreamingIntegrityVerifier` maintains per-chunk digests and a rolling full-object hash without buffering entire files in memory. +`ResumableParallelDownloader` smooths chunk size within configured min/max bounds from observed per-chunk throughput. Length-mismatched range responses are reassigned via `TransferPlan.GetRepairAssignments`. When the final object digest fails, optional repair passes re-download the plan's chunks (`MaxRepairPasses`). diff --git a/eng/README.md b/eng/README.md index 72fd7f8..126bcb7 100644 --- a/eng/README.md +++ b/eng/README.md @@ -24,7 +24,8 @@ Set `IsTrimmable` / `IsAotCompatible` only after smoke tests pass. * Framework: xUnit * Deterministic seeds for randomized tests * No mocking frameworks unless interaction-based mocking is required -* Coverage target ≥85% line across production code +* Target: ≥85% line coverage across production `src/` (enforced in CI) +* Prefer ≥90% branch coverage on critical algorithms where practical; document justified exclusions ## Benchmark conventions diff --git a/global.json b/global.json index a5f940e..c66295e 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "10.0.301", + "version": "10.0.302", "rollForward": "disable", "allowPrerelease": false } diff --git a/samples/FunctionFoundry.Data.Sample/packages.lock.json b/samples/FunctionFoundry.Data.Sample/packages.lock.json new file mode 100644 index 0000000..97c0c5c --- /dev/null +++ b/samples/FunctionFoundry.Data.Sample/packages.lock.json @@ -0,0 +1,10 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "functionfoundry.data": { + "type": "Project" + } + } + } +} \ No newline at end of file diff --git a/samples/FunctionFoundry.Distributed.Sample/packages.lock.json b/samples/FunctionFoundry.Distributed.Sample/packages.lock.json new file mode 100644 index 0000000..27d4b34 --- /dev/null +++ b/samples/FunctionFoundry.Distributed.Sample/packages.lock.json @@ -0,0 +1,10 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "functionfoundry.distributed": { + "type": "Project" + } + } + } +} \ No newline at end of file diff --git a/samples/FunctionFoundry.Integrity.Sample/packages.lock.json b/samples/FunctionFoundry.Integrity.Sample/packages.lock.json new file mode 100644 index 0000000..11cad9e --- /dev/null +++ b/samples/FunctionFoundry.Integrity.Sample/packages.lock.json @@ -0,0 +1,10 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "functionfoundry.integrity": { + "type": "Project" + } + } + } +} \ No newline at end of file diff --git a/samples/FunctionFoundry.Networking.Sample/packages.lock.json b/samples/FunctionFoundry.Networking.Sample/packages.lock.json new file mode 100644 index 0000000..a3a8d88 --- /dev/null +++ b/samples/FunctionFoundry.Networking.Sample/packages.lock.json @@ -0,0 +1,10 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "functionfoundry.networking": { + "type": "Project" + } + } + } +} \ No newline at end of file diff --git a/samples/FunctionFoundry.Observability.Sample/packages.lock.json b/samples/FunctionFoundry.Observability.Sample/packages.lock.json new file mode 100644 index 0000000..2f7ce41 --- /dev/null +++ b/samples/FunctionFoundry.Observability.Sample/packages.lock.json @@ -0,0 +1,10 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "functionfoundry.observability": { + "type": "Project" + } + } + } +} \ No newline at end of file diff --git a/samples/FunctionFoundry.Resilience.Sample/packages.lock.json b/samples/FunctionFoundry.Resilience.Sample/packages.lock.json new file mode 100644 index 0000000..74a1a17 --- /dev/null +++ b/samples/FunctionFoundry.Resilience.Sample/packages.lock.json @@ -0,0 +1,10 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "functionfoundry.resilience": { + "type": "Project" + } + } + } +} \ No newline at end of file diff --git a/samples/FunctionFoundry.Scheduling.Sample/packages.lock.json b/samples/FunctionFoundry.Scheduling.Sample/packages.lock.json new file mode 100644 index 0000000..fb2ca62 --- /dev/null +++ b/samples/FunctionFoundry.Scheduling.Sample/packages.lock.json @@ -0,0 +1,10 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "functionfoundry.scheduling": { + "type": "Project" + } + } + } +} \ No newline at end of file diff --git a/samples/FunctionFoundry.Security.Sample/packages.lock.json b/samples/FunctionFoundry.Security.Sample/packages.lock.json new file mode 100644 index 0000000..cfe2447 --- /dev/null +++ b/samples/FunctionFoundry.Security.Sample/packages.lock.json @@ -0,0 +1,10 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "functionfoundry.security": { + "type": "Project" + } + } + } +} \ No newline at end of file diff --git a/samples/FunctionFoundry.Storage.Sample/packages.lock.json b/samples/FunctionFoundry.Storage.Sample/packages.lock.json new file mode 100644 index 0000000..d3f197c --- /dev/null +++ b/samples/FunctionFoundry.Storage.Sample/packages.lock.json @@ -0,0 +1,10 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "functionfoundry.storage": { + "type": "Project" + } + } + } +} \ No newline at end of file diff --git a/samples/FunctionFoundry.Text.Sample/packages.lock.json b/samples/FunctionFoundry.Text.Sample/packages.lock.json new file mode 100644 index 0000000..aed157a --- /dev/null +++ b/samples/FunctionFoundry.Text.Sample/packages.lock.json @@ -0,0 +1,10 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "functionfoundry.text": { + "type": "Project" + } + } + } +} \ No newline at end of file diff --git a/src/FunctionFoundry.Data/packages.lock.json b/src/FunctionFoundry.Data/packages.lock.json new file mode 100644 index 0000000..04d70a3 --- /dev/null +++ b/src/FunctionFoundry.Data/packages.lock.json @@ -0,0 +1,27 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + } + } + } +} \ No newline at end of file diff --git a/src/FunctionFoundry.Distributed/packages.lock.json b/src/FunctionFoundry.Distributed/packages.lock.json new file mode 100644 index 0000000..04d70a3 --- /dev/null +++ b/src/FunctionFoundry.Distributed/packages.lock.json @@ -0,0 +1,27 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + } + } + } +} \ No newline at end of file diff --git a/src/FunctionFoundry.Integrity/StreamingHashManifest.cs b/src/FunctionFoundry.Integrity/StreamingHashManifest.cs index 18a6024..2f67a2d 100644 --- a/src/FunctionFoundry.Integrity/StreamingHashManifest.cs +++ b/src/FunctionFoundry.Integrity/StreamingHashManifest.cs @@ -280,6 +280,7 @@ internal StreamingHashManifestBuilder(ManifestHashAlgorithm algorithm, ManifestM /// Thrown when the path is unsafe or size metadata is disallowed. public ManifestEntry AddEntry(string relativePath, Stream content, bool includeSize = true) { + ArgumentNullException.ThrowIfNull(content); string normalized = PathSafety.NormalizeRelativePath(relativePath); (string hashHex, long size) = StreamingHashManifest.HashStream(content, _algorithm); long? recordedSize = includeSize && _metadataPolicy.AllowFileSize ? size : null; diff --git a/src/FunctionFoundry.Integrity/packages.lock.json b/src/FunctionFoundry.Integrity/packages.lock.json new file mode 100644 index 0000000..04d70a3 --- /dev/null +++ b/src/FunctionFoundry.Integrity/packages.lock.json @@ -0,0 +1,27 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + } + } + } +} \ No newline at end of file diff --git a/src/FunctionFoundry.Networking/PACKAGE.md b/src/FunctionFoundry.Networking/PACKAGE.md index ef45141..c4575d8 100644 --- a/src/FunctionFoundry.Networking/PACKAGE.md +++ b/src/FunctionFoundry.Networking/PACKAGE.md @@ -4,9 +4,9 @@ Resumable, integrity-aware HTTP transfer orchestration using caller-provided `Ht ## Capabilities -* **Resumable parallel downloader** — HTTP ranges with ETag/Last-Modified validation, adaptive chunks, checkpoint resume, and final digest verification +* **Resumable parallel downloader** — HTTP ranges with ETag/Last-Modified validation, adaptive chunk sizing within configured min/max bounds, checkpoint resume, corrupt-chunk repair passes, and final digest verification * **Mirror selector** — multi-signal scoring with failure decay, hysteresis, and exportable evidence -* **Transfer plan** — chunk assignments independent of execution with overlap detection and corrupt-chunk repair +* **Transfer plan** — chunk assignments independent of execution with overlap detection and corrupt-chunk repair assignment helpers * **Streaming integrity verifier** — bounded-memory incremental per-chunk and full-object digest verification ## Runtime dependencies @@ -16,3 +16,5 @@ None beyond the .NET shared framework. ## Usage note Always inject a shared `HttpClient` (or `IHttpClientFactory`-managed instance). This library never creates per-request `HttpClient` instances. + +Adaptive sizing observes per-chunk throughput and smooths the next recommended chunk size within `MinChunkSizeBytes`/`MaxChunkSizeBytes`. Repair re-downloads length-mismatched chunks immediately and can re-fetch the full plan when the final object digest fails (`MaxRepairPasses`). diff --git a/src/FunctionFoundry.Networking/README.md b/src/FunctionFoundry.Networking/README.md index 9e6efd4..ef926ad 100644 --- a/src/FunctionFoundry.Networking/README.md +++ b/src/FunctionFoundry.Networking/README.md @@ -13,6 +13,8 @@ Downloads large objects using HTTP range requests when available: * ETag / Last-Modified validation * persistent checkpoint format * configurable parallelism +* adaptive chunk sizing within min/max bounds (throughput-smoothed) +* corrupt-chunk repair (length mismatch + optional full-digest repair passes) * partial-file integrity and final cryptographic verification * safe restart after interruption * capability detection and correct fallback when ranges are unsupported diff --git a/src/FunctionFoundry.Networking/ResumableParallelDownloader.cs b/src/FunctionFoundry.Networking/ResumableParallelDownloader.cs index 33573b0..f2614aa 100644 --- a/src/FunctionFoundry.Networking/ResumableParallelDownloader.cs +++ b/src/FunctionFoundry.Networking/ResumableParallelDownloader.cs @@ -1,3 +1,4 @@ +using System.Diagnostics; using System.Net; using System.Net.Http.Headers; @@ -29,7 +30,13 @@ public sealed record DownloadCheckpoint( /// Whether final digest verification succeeded. /// Whether a prior checkpoint was used. /// Whether range requests were used. -public sealed record DownloadResult(long BytesWritten, bool Verified, bool ResumedFromCheckpoint, bool UsedRangeRequests); +/// Number of chunks re-downloaded during corrupt-chunk repair. +public sealed record DownloadResult( + long BytesWritten, + bool Verified, + bool ResumedFromCheckpoint, + bool UsedRangeRequests, + int RepairedChunkCount = 0); /// /// Options for . @@ -56,6 +63,18 @@ public sealed class ResumableParallelDownloaderOptions /// public int MaxParallelism { get; set; } = 4; + /// + /// Gets or sets whether chunk size may grow or shrink within min/max based on observed throughput. + /// Defaults to . + /// + public bool EnableAdaptiveChunkSizing { get; set; } = true; + + /// + /// Gets or sets the maximum number of full-object repair passes after digest verification fails. + /// Defaults to 1. + /// + public int MaxRepairPasses { get; set; } = 1; + /// /// Validates option ranges. /// @@ -75,6 +94,11 @@ public void Validate() { throw new ArgumentOutOfRangeException(nameof(MaxParallelism), "MaxParallelism must be positive."); } + + if (MaxRepairPasses < 0) + { + throw new ArgumentOutOfRangeException(nameof(MaxRepairPasses), "MaxRepairPasses cannot be negative."); + } } } @@ -124,13 +148,14 @@ public Task SaveAsync(DownloadCheckpoint checkpoint, CancellationToken cancellat } /// -/// Downloads content with resumable HTTP range support and integrity verification. +/// Downloads content with resumable HTTP range support, adaptive chunk sizing, and integrity-aware repair. /// public sealed class ResumableParallelDownloader { private readonly HttpClient _httpClient; private readonly IDownloadCheckpointStore _checkpointStore; private readonly ResumableParallelDownloaderOptions _options; + private int _adaptiveChunkSizeBytes; /// /// Initializes a new downloader using the caller-provided . @@ -147,6 +172,7 @@ public ResumableParallelDownloader( _checkpointStore = checkpointStore ?? throw new ArgumentNullException(nameof(checkpointStore)); _options = options ?? new ResumableParallelDownloaderOptions(); _options.Validate(); + _adaptiveChunkSizeBytes = _options.InitialChunkSizeBytes; } /// @@ -154,6 +180,11 @@ public ResumableParallelDownloader( /// public ResumableParallelDownloaderOptions Options => _options; + /// + /// Gets the current adaptive chunk size recommendation in bytes. + /// + public int AdaptiveChunkSizeBytes => _adaptiveChunkSizeBytes; + /// /// Downloads content into a seekable stream. /// @@ -189,6 +220,7 @@ public async Task DownloadAsync( { totalSize = probeResponse.Content.Headers.ContentLength ?? 0; } + string? etag = probeResponse.Headers.ETag?.Tag; string? lastModified = probeResponse.Content.Headers.LastModified?.ToString("R"); @@ -200,16 +232,17 @@ public async Task DownloadAsync( resumed = false; } - int chunkSize = existing?.ChunkSizeBytes ?? _options.InitialChunkSizeBytes; + int chunkSize = existing?.ChunkSizeBytes ?? ChooseChunkSize(totalSize); TransferPlan plan = TransferPlan.Create(totalSize, chunkSize); - HashSet completed = existing?.CompletedChunkIndices.ToHashSet() ?? new HashSet(); - using StreamingIntegrityVerifier verifier = new(); + HashSet completed = existing?.CompletedChunkIndices.ToHashSet() ?? []; + int repairedChunkCount = 0; if (!supportsRanges || totalSize <= 0) { byte[] full = await DownloadFullAsync(url, cancellationToken).ConfigureAwait(false); destination.SetLength(0); await destination.WriteAsync(full, cancellationToken).ConfigureAwait(false); + using StreamingIntegrityVerifier verifier = new(); verifier.AppendChunk(0, full); FullVerificationResult verification = verifier.VerifyFull(expectedSha256Hex); await SaveCheckpointAsync(url, totalSize, etag, lastModified, chunkSize, completed, supportsRanges, cancellationToken).ConfigureAwait(false); @@ -221,9 +254,80 @@ public async Task DownloadAsync( destination.SetLength(totalSize); } + await DownloadChunksAsync(url, destination, plan, completed, etag, cancellationToken).ConfigureAwait(false); + + FullVerificationResult fullVerification = await VerifyDestinationAsync(destination, plan, expectedSha256Hex, cancellationToken).ConfigureAwait(false); + for (int repairPass = 0; !fullVerification.IsValid && repairPass < _options.MaxRepairPasses; repairPass++) + { + IReadOnlyList repair = plan.GetRepairAssignments(plan.Chunks.Select(static c => c.Index)); + if (repair.Count == 0) + { + break; + } + + foreach (TransferChunkAssignment chunk in repair) + { + completed.Remove(chunk.Index); + } + + await DownloadChunksAsync(url, destination, plan, completed, etag, cancellationToken).ConfigureAwait(false); + repairedChunkCount += repair.Count; + fullVerification = await VerifyDestinationAsync(destination, plan, expectedSha256Hex, cancellationToken).ConfigureAwait(false); + } + + await SaveCheckpointAsync(url, totalSize, etag, lastModified, chunkSize, completed, supportsRanges, cancellationToken).ConfigureAwait(false); + return new DownloadResult(totalSize, fullVerification.IsValid, resumed, true, repairedChunkCount); + } + + private int ChooseChunkSize(long totalSizeBytes) + { + int candidate = _options.EnableAdaptiveChunkSizing ? _adaptiveChunkSizeBytes : _options.InitialChunkSizeBytes; + if (_options.EnableAdaptiveChunkSizing && totalSizeBytes > 0) + { + // Prefer fewer chunks for large objects and smaller chunks for tiny objects. + if (totalSizeBytes < _options.MinChunkSizeBytes * 4L) + { + candidate = Math.Min(candidate, _options.MinChunkSizeBytes); + } + else if (totalSizeBytes > _options.MaxChunkSizeBytes * 8L) + { + candidate = Math.Max(candidate, Math.Min(_options.MaxChunkSizeBytes, _options.InitialChunkSizeBytes * 2)); + } + } + + return Math.Clamp(candidate, _options.MinChunkSizeBytes, _options.MaxChunkSizeBytes); + } + + private void ObserveChunkThroughput(int bytes, TimeSpan elapsed) + { + if (!_options.EnableAdaptiveChunkSizing || bytes <= 0 || elapsed <= TimeSpan.Zero) + { + return; + } + + double bytesPerSecond = bytes / Math.Max(elapsed.TotalSeconds, 0.001); + // Target roughly 250–750 ms per chunk. + const double targetSeconds = 0.5; + int suggested = (int)Math.Clamp(bytesPerSecond * targetSeconds, _options.MinChunkSizeBytes, _options.MaxChunkSizeBytes); + // Smooth toward the suggestion to avoid oscillation. + _adaptiveChunkSizeBytes = (_adaptiveChunkSizeBytes + suggested) / 2; + _adaptiveChunkSizeBytes = Math.Clamp(_adaptiveChunkSizeBytes, _options.MinChunkSizeBytes, _options.MaxChunkSizeBytes); + } + + private async Task DownloadChunksAsync( + Uri url, + Stream destination, + TransferPlan plan, + HashSet completed, + string? etag, + CancellationToken cancellationToken) + { IReadOnlyList pending = plan.GetPendingChunks(completed); using SemaphoreSlim gate = new(_options.MaxParallelism, _options.MaxParallelism); + using SemaphoreSlim ioGate = new(1, 1); List workers = []; + List failedChunks = []; + object failedLock = new(); foreach (TransferChunkAssignment chunk in pending) { @@ -232,26 +336,25 @@ public async Task DownloadAsync( await Task.WhenAll(workers).ConfigureAwait(false); - foreach (TransferChunkAssignment chunk in plan.Chunks.OrderBy(static c => c.Index)) + if (failedChunks.Count > 0) { - destination.Position = chunk.Offset; - byte[] buffer = new byte[chunk.Length]; - int read = await destination.ReadAsync(buffer.AsMemory(0, chunk.Length), cancellationToken).ConfigureAwait(false); - if (read > 0) + IReadOnlyList repair = plan.GetRepairAssignments(failedChunks); + List repairWorkers = []; + foreach (TransferChunkAssignment chunk in repair) { - verifier.AppendChunk(chunk.Index, buffer.AsSpan(0, read)); + completed.Remove(chunk.Index); + repairWorkers.Add(DownloadChunkAsync(chunk)); } - } - FullVerificationResult fullVerification = verifier.VerifyFull(expectedSha256Hex); - await SaveCheckpointAsync(url, totalSize, etag, lastModified, chunkSize, completed, supportsRanges, cancellationToken).ConfigureAwait(false); - return new DownloadResult(totalSize, fullVerification.IsValid, resumed, true); + await Task.WhenAll(repairWorkers).ConfigureAwait(false); + } async Task DownloadChunkAsync(TransferChunkAssignment chunk) { await gate.WaitAsync(cancellationToken).ConfigureAwait(false); try { + Stopwatch timer = Stopwatch.StartNew(); using HttpRequestMessage request = new(HttpMethod.Get, url); long end = chunk.Offset + chunk.Length - 1; request.Headers.Range = new RangeHeaderValue(chunk.Offset, end); @@ -265,12 +368,28 @@ async Task DownloadChunkAsync(TransferChunkAssignment chunk) byte[] data = await response.Content.ReadAsByteArrayAsync(cancellationToken).ConfigureAwait(false); if (data.Length != chunk.Length) { - throw new InvalidOperationException($"Chunk {chunk.Index} length mismatch."); + lock (failedLock) + { + failedChunks.Add(chunk.Index); + } + + return; } - destination.Position = chunk.Offset; - await destination.WriteAsync(data, cancellationToken).ConfigureAwait(false); - completed.Add(chunk.Index); + await ioGate.WaitAsync(cancellationToken).ConfigureAwait(false); + try + { + destination.Position = chunk.Offset; + await destination.WriteAsync(data, cancellationToken).ConfigureAwait(false); + completed.Add(chunk.Index); + } + finally + { + ioGate.Release(); + } + + timer.Stop(); + ObserveChunkThroughput(data.Length, timer.Elapsed); } finally { @@ -279,6 +398,27 @@ async Task DownloadChunkAsync(TransferChunkAssignment chunk) } } + private static async Task VerifyDestinationAsync( + Stream destination, + TransferPlan plan, + string expectedSha256Hex, + CancellationToken cancellationToken) + { + using StreamingIntegrityVerifier verifier = new(); + foreach (TransferChunkAssignment chunk in plan.Chunks.OrderBy(static c => c.Index)) + { + destination.Position = chunk.Offset; + byte[] buffer = new byte[chunk.Length]; + int read = await destination.ReadAsync(buffer.AsMemory(0, chunk.Length), cancellationToken).ConfigureAwait(false); + if (read > 0) + { + verifier.AppendChunk(chunk.Index, buffer.AsSpan(0, read)); + } + } + + return verifier.VerifyFull(expectedSha256Hex); + } + private async Task DownloadFullAsync(Uri url, CancellationToken cancellationToken) { using HttpResponseMessage response = await _httpClient.GetAsync(url, cancellationToken).ConfigureAwait(false); diff --git a/src/FunctionFoundry.Networking/packages.lock.json b/src/FunctionFoundry.Networking/packages.lock.json new file mode 100644 index 0000000..04d70a3 --- /dev/null +++ b/src/FunctionFoundry.Networking/packages.lock.json @@ -0,0 +1,27 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + } + } + } +} \ No newline at end of file diff --git a/src/FunctionFoundry.Observability/packages.lock.json b/src/FunctionFoundry.Observability/packages.lock.json new file mode 100644 index 0000000..04d70a3 --- /dev/null +++ b/src/FunctionFoundry.Observability/packages.lock.json @@ -0,0 +1,27 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + } + } + } +} \ No newline at end of file diff --git a/src/FunctionFoundry.Resilience/packages.lock.json b/src/FunctionFoundry.Resilience/packages.lock.json new file mode 100644 index 0000000..04d70a3 --- /dev/null +++ b/src/FunctionFoundry.Resilience/packages.lock.json @@ -0,0 +1,27 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + } + } + } +} \ No newline at end of file diff --git a/src/FunctionFoundry.Scheduling/packages.lock.json b/src/FunctionFoundry.Scheduling/packages.lock.json new file mode 100644 index 0000000..04d70a3 --- /dev/null +++ b/src/FunctionFoundry.Scheduling/packages.lock.json @@ -0,0 +1,27 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + } + } + } +} \ No newline at end of file diff --git a/src/FunctionFoundry.Security/packages.lock.json b/src/FunctionFoundry.Security/packages.lock.json new file mode 100644 index 0000000..04d70a3 --- /dev/null +++ b/src/FunctionFoundry.Security/packages.lock.json @@ -0,0 +1,27 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + } + } + } +} \ No newline at end of file diff --git a/src/FunctionFoundry.Storage/packages.lock.json b/src/FunctionFoundry.Storage/packages.lock.json new file mode 100644 index 0000000..04d70a3 --- /dev/null +++ b/src/FunctionFoundry.Storage/packages.lock.json @@ -0,0 +1,27 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + } + } + } +} \ No newline at end of file diff --git a/src/FunctionFoundry.Text/packages.lock.json b/src/FunctionFoundry.Text/packages.lock.json new file mode 100644 index 0000000..04d70a3 --- /dev/null +++ b/src/FunctionFoundry.Text/packages.lock.json @@ -0,0 +1,27 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "Microsoft.SourceLink.GitHub": { + "type": "Direct", + "requested": "[8.0.0, )", + "resolved": "8.0.0", + "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==", + "dependencies": { + "Microsoft.Build.Tasks.Git": "8.0.0", + "Microsoft.SourceLink.Common": "8.0.0" + } + }, + "Microsoft.Build.Tasks.Git": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ==" + }, + "Microsoft.SourceLink.Common": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw==" + } + } + } +} \ No newline at end of file diff --git a/tests/FunctionFoundry.Data.Tests/packages.lock.json b/tests/FunctionFoundry.Data.Tests/packages.lock.json new file mode 100644 index 0000000..0b0f816 --- /dev/null +++ b/tests/FunctionFoundry.Data.Tests/packages.lock.json @@ -0,0 +1,107 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "coverlet.collector": { + "type": "Direct", + "requested": "[6.0.4, )", + "resolved": "6.0.4", + "contentHash": "lkhqpF8Pu2Y7IiN7OntbsTtdbpR1syMsm2F3IgX6ootA4ffRqWL5jF7XipHuZQTdVuWG/gVAAcf8mjk8Tz0xPg==" + }, + "Microsoft.NET.Test.Sdk": { + "type": "Direct", + "requested": "[17.14.1, )", + "resolved": "17.14.1", + "contentHash": "HJKqKOE+vshXra2aEHpi2TlxYX7Z9VFYkr+E5rwEvHC8eIXiyO+K9kNm8vmNom3e2rA56WqxU+/N9NJlLGXsJQ==", + "dependencies": { + "Microsoft.CodeCoverage": "17.14.1", + "Microsoft.TestPlatform.TestHost": "17.14.1" + } + }, + "xunit": { + "type": "Direct", + "requested": "[2.9.3, )", + "resolved": "2.9.3", + "contentHash": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==", + "dependencies": { + "xunit.analyzers": "1.18.0", + "xunit.assert": "2.9.3", + "xunit.core": "[2.9.3]" + } + }, + "xunit.runner.visualstudio": { + "type": "Direct", + "requested": "[3.1.4, )", + "resolved": "3.1.4", + "contentHash": "5mj99LvCqrq3CNi06xYdyIAXOEh+5b33F2nErCzI5zWiDdLHXiPXEWFSUAF8zlIv0ZWqjZNCwHTQeAPYbF3pCg==" + }, + "Microsoft.CodeCoverage": { + "type": "Transitive", + "resolved": "17.14.1", + "contentHash": "pmTrhfFIoplzFVbhVwUquT+77CbGH+h4/3mBpdmIlYtBi9nAB+kKI6dN3A/nV4DFi3wLLx/BlHIPK+MkbQ6Tpg==" + }, + "Microsoft.TestPlatform.ObjectModel": { + "type": "Transitive", + "resolved": "17.14.1", + "contentHash": "xTP1W6Mi6SWmuxd3a+jj9G9UoC850WGwZUps1Wah9r1ZxgXhdJfj1QqDLJkFjHDCvN42qDL2Ps5KjQYWUU0zcQ==" + }, + "Microsoft.TestPlatform.TestHost": { + "type": "Transitive", + "resolved": "17.14.1", + "contentHash": "d78LPzGKkJwsJXAQwsbJJ7LE7D1wB+rAyhHHAaODF+RDSQ0NgMjDFkSA1Djw18VrxO76GlKAjRUhl+H8NL8Z+Q==", + "dependencies": { + "Microsoft.TestPlatform.ObjectModel": "17.14.1", + "Newtonsoft.Json": "13.0.3" + } + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" + }, + "xunit.abstractions": { + "type": "Transitive", + "resolved": "2.0.3", + "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==" + }, + "xunit.analyzers": { + "type": "Transitive", + "resolved": "1.18.0", + "contentHash": "OtFMHN8yqIcYP9wcVIgJrq01AfTxijjAqVDy/WeQVSyrDC1RzBWeQPztL49DN2syXRah8TYnfvk035s7L95EZQ==" + }, + "xunit.assert": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA==" + }, + "xunit.core": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "BiAEvqGvyme19wE0wTKdADH+NloYqikiU0mcnmiNyXaF9HyHmE6sr/3DC5vnBkgsWaE6yPyWszKSPSApWdRVeQ==", + "dependencies": { + "xunit.extensibility.core": "[2.9.3]", + "xunit.extensibility.execution": "[2.9.3]" + } + }, + "xunit.extensibility.core": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==", + "dependencies": { + "xunit.abstractions": "2.0.3" + } + }, + "xunit.extensibility.execution": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==", + "dependencies": { + "xunit.extensibility.core": "[2.9.3]" + } + }, + "functionfoundry.data": { + "type": "Project" + } + } + } +} \ No newline at end of file diff --git a/tests/FunctionFoundry.Distributed.Tests/QuorumAndPhiExtraTests.cs b/tests/FunctionFoundry.Distributed.Tests/QuorumAndPhiExtraTests.cs new file mode 100644 index 0000000..292f1bc --- /dev/null +++ b/tests/FunctionFoundry.Distributed.Tests/QuorumAndPhiExtraTests.cs @@ -0,0 +1,34 @@ +using FunctionFoundry.Distributed; + +namespace FunctionFoundry.Distributed.Tests; + +public sealed class QuorumAndPhiExtraTests +{ + [Fact] + public void Quorum_policy_rejects_invalid_counts() + { + Assert.Throws(() => new QuorumPolicy(0, 3).Validate()); + Assert.Throws(() => new QuorumPolicy(2, 0).Validate()); + Assert.Throws(() => new QuorumPolicy(4, 3).Validate()); + } + + [Fact] + public async Task Quorum_aggregator_rejects_mismatched_task_count() + { + var aggregator = new QuorumResultAggregator(); + await Assert.ThrowsAsync(() => aggregator.AggregateAsync( + [System.Threading.Tasks.Task.FromResult(1)], + new QuorumPolicy(1, 2))); + await Assert.ThrowsAsync(() => aggregator.AggregateAsync(null!, new QuorumPolicy(1, 1))); + } + + [Fact] + public void Phi_detector_snapshot_before_heartbeats_is_warming_up() + { + var clock = new SimulatedClock(10); + var detector = new PhiAccrualFailureDetector(clock); + PhiAccrualSnapshot snapshot = detector.GetSnapshot(); + Assert.True(snapshot.IsWarmingUp); + Assert.Equal(0, snapshot.Phi); + } +} diff --git a/tests/FunctionFoundry.Distributed.Tests/VersionClockExtraTests.cs b/tests/FunctionFoundry.Distributed.Tests/VersionClockExtraTests.cs new file mode 100644 index 0000000..63c995a --- /dev/null +++ b/tests/FunctionFoundry.Distributed.Tests/VersionClockExtraTests.cs @@ -0,0 +1,88 @@ +using FunctionFoundry.Distributed; + +namespace FunctionFoundry.Distributed.Tests; + +public sealed class VersionClockExtraTests +{ + [Fact] + public void VersionClockOptions_rejects_non_positive_max_entries() + { + Assert.Throws(() => new VersionClockOptions { MaxNodeEntries = 0 }.Validate()); + Assert.Throws(() => new VectorClock(new VersionClockOptions { MaxNodeEntries = -1 })); + } + + [Fact] + public void Vector_clock_rejects_blank_node_ids_and_invalid_serialization() + { + var clock = new VectorClock(); + Assert.Throws(() => clock.Increment(" ")); + Assert.Throws(() => new VectorClock([new KeyValuePair("", 1)])); + Assert.Throws(() => VectorClock.Deserialize("a")); + Assert.Throws(() => VectorClock.Deserialize("a=x")); + Assert.Equal(string.Empty, new VectorClock().Serialize()); + Assert.Equal(VersionClockRelation.Equal, VectorClock.Deserialize(string.Empty).CompareTo(new VectorClock())); + } + + [Fact] + public void Vector_clock_happens_before_and_equal_paths() + { + var ancestor = new VectorClock(); + ancestor.Increment("a"); + var descendant = new VectorClock(ancestor.Entries); + descendant.Increment("a"); + Assert.Equal(VersionClockRelation.HappensBefore, ancestor.CompareTo(descendant)); + Assert.Equal(VersionClockRelation.HappensAfter, descendant.CompareTo(ancestor)); + Assert.Equal(VersionClockRelation.Equal, ancestor.CompareTo(new VectorClock(ancestor.Entries))); + } + + [Fact] + public void Vector_clock_compact_removes_lowest_counters() + { + var clock = new VectorClock(new VersionClockOptions { MaxNodeEntries = 3 }); + clock.Increment("a"); + clock.Increment("b"); + clock.Increment("b"); + clock.Increment("c"); + clock.Increment("c"); + clock.Increment("c"); + int removed = clock.Compact(); + Assert.Equal(0, removed); + clock.Increment("d"); + Assert.Equal(3, clock.Entries.Count); + Assert.False(clock.Entries.ContainsKey("a")); + } + + [Fact] + public void Dotted_clock_rejects_invalid_serialization_and_compacts() + { + Assert.Throws(() => DottedVersionClock.Deserialize("bad")); + Assert.Throws(() => DottedVersionClock.Deserialize("n=x")); + Assert.Throws(() => DottedVersionClock.Deserialize("n=1:z")); + + var clock = new DottedVersionClock(new VersionClockOptions { MaxNodeEntries = 2 }); + clock.Increment("a"); + clock.Increment("b"); + clock.Increment("c"); + Assert.Equal(2, clock.Entries.Count); + Assert.Equal(0, clock.Compact()); + } + + [Fact] + public void Dotted_clock_dominates_false_when_missing_node_or_dot() + { + var left = new DottedVersionClock(); + var right = new DottedVersionClock(); + left.Increment("a"); + right.Increment("b"); + Assert.False(left.Dominates(right)); + + // Counter advanced without recording the superseded value as a dot. + var advanced = DottedVersionClock.Deserialize("a=3"); + var earlier = DottedVersionClock.Deserialize("a=2"); + Assert.False(advanced.Dominates(earlier)); + + var withDots = DottedVersionClock.Deserialize("a=3:1.2"); + Assert.True(withDots.Dominates(DottedVersionClock.Deserialize("a=1"))); + Assert.True(withDots.Dominates(DottedVersionClock.Deserialize("a=2"))); + } +} diff --git a/tests/FunctionFoundry.Distributed.Tests/packages.lock.json b/tests/FunctionFoundry.Distributed.Tests/packages.lock.json new file mode 100644 index 0000000..8a8c224 --- /dev/null +++ b/tests/FunctionFoundry.Distributed.Tests/packages.lock.json @@ -0,0 +1,107 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "coverlet.collector": { + "type": "Direct", + "requested": "[6.0.4, )", + "resolved": "6.0.4", + "contentHash": "lkhqpF8Pu2Y7IiN7OntbsTtdbpR1syMsm2F3IgX6ootA4ffRqWL5jF7XipHuZQTdVuWG/gVAAcf8mjk8Tz0xPg==" + }, + "Microsoft.NET.Test.Sdk": { + "type": "Direct", + "requested": "[17.14.1, )", + "resolved": "17.14.1", + "contentHash": "HJKqKOE+vshXra2aEHpi2TlxYX7Z9VFYkr+E5rwEvHC8eIXiyO+K9kNm8vmNom3e2rA56WqxU+/N9NJlLGXsJQ==", + "dependencies": { + "Microsoft.CodeCoverage": "17.14.1", + "Microsoft.TestPlatform.TestHost": "17.14.1" + } + }, + "xunit": { + "type": "Direct", + "requested": "[2.9.3, )", + "resolved": "2.9.3", + "contentHash": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==", + "dependencies": { + "xunit.analyzers": "1.18.0", + "xunit.assert": "2.9.3", + "xunit.core": "[2.9.3]" + } + }, + "xunit.runner.visualstudio": { + "type": "Direct", + "requested": "[3.1.4, )", + "resolved": "3.1.4", + "contentHash": "5mj99LvCqrq3CNi06xYdyIAXOEh+5b33F2nErCzI5zWiDdLHXiPXEWFSUAF8zlIv0ZWqjZNCwHTQeAPYbF3pCg==" + }, + "Microsoft.CodeCoverage": { + "type": "Transitive", + "resolved": "17.14.1", + "contentHash": "pmTrhfFIoplzFVbhVwUquT+77CbGH+h4/3mBpdmIlYtBi9nAB+kKI6dN3A/nV4DFi3wLLx/BlHIPK+MkbQ6Tpg==" + }, + "Microsoft.TestPlatform.ObjectModel": { + "type": "Transitive", + "resolved": "17.14.1", + "contentHash": "xTP1W6Mi6SWmuxd3a+jj9G9UoC850WGwZUps1Wah9r1ZxgXhdJfj1QqDLJkFjHDCvN42qDL2Ps5KjQYWUU0zcQ==" + }, + "Microsoft.TestPlatform.TestHost": { + "type": "Transitive", + "resolved": "17.14.1", + "contentHash": "d78LPzGKkJwsJXAQwsbJJ7LE7D1wB+rAyhHHAaODF+RDSQ0NgMjDFkSA1Djw18VrxO76GlKAjRUhl+H8NL8Z+Q==", + "dependencies": { + "Microsoft.TestPlatform.ObjectModel": "17.14.1", + "Newtonsoft.Json": "13.0.3" + } + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" + }, + "xunit.abstractions": { + "type": "Transitive", + "resolved": "2.0.3", + "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==" + }, + "xunit.analyzers": { + "type": "Transitive", + "resolved": "1.18.0", + "contentHash": "OtFMHN8yqIcYP9wcVIgJrq01AfTxijjAqVDy/WeQVSyrDC1RzBWeQPztL49DN2syXRah8TYnfvk035s7L95EZQ==" + }, + "xunit.assert": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA==" + }, + "xunit.core": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "BiAEvqGvyme19wE0wTKdADH+NloYqikiU0mcnmiNyXaF9HyHmE6sr/3DC5vnBkgsWaE6yPyWszKSPSApWdRVeQ==", + "dependencies": { + "xunit.extensibility.core": "[2.9.3]", + "xunit.extensibility.execution": "[2.9.3]" + } + }, + "xunit.extensibility.core": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==", + "dependencies": { + "xunit.abstractions": "2.0.3" + } + }, + "xunit.extensibility.execution": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==", + "dependencies": { + "xunit.extensibility.core": "[2.9.3]" + } + }, + "functionfoundry.distributed": { + "type": "Project" + } + } + } +} \ No newline at end of file diff --git a/tests/FunctionFoundry.Engineering.Tests/SdkPinningTests.cs b/tests/FunctionFoundry.Engineering.Tests/SdkPinningTests.cs index 848b9e4..61a7774 100644 --- a/tests/FunctionFoundry.Engineering.Tests/SdkPinningTests.cs +++ b/tests/FunctionFoundry.Engineering.Tests/SdkPinningTests.cs @@ -10,7 +10,7 @@ public void GlobalJson_pins_exact_sdk_without_roll_forward() string path = FindRepoFile("global.json"); using JsonDocument document = JsonDocument.Parse(File.ReadAllText(path)); JsonElement sdk = document.RootElement.GetProperty("sdk"); - Assert.Equal("10.0.301", sdk.GetProperty("version").GetString()); + Assert.Equal("10.0.302", sdk.GetProperty("version").GetString()); Assert.Equal("disable", sdk.GetProperty("rollForward").GetString()); Assert.False(sdk.GetProperty("allowPrerelease").GetBoolean()); } diff --git a/tests/FunctionFoundry.Engineering.Tests/packages.lock.json b/tests/FunctionFoundry.Engineering.Tests/packages.lock.json new file mode 100644 index 0000000..16cb228 --- /dev/null +++ b/tests/FunctionFoundry.Engineering.Tests/packages.lock.json @@ -0,0 +1,104 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "coverlet.collector": { + "type": "Direct", + "requested": "[6.0.4, )", + "resolved": "6.0.4", + "contentHash": "lkhqpF8Pu2Y7IiN7OntbsTtdbpR1syMsm2F3IgX6ootA4ffRqWL5jF7XipHuZQTdVuWG/gVAAcf8mjk8Tz0xPg==" + }, + "Microsoft.NET.Test.Sdk": { + "type": "Direct", + "requested": "[17.14.1, )", + "resolved": "17.14.1", + "contentHash": "HJKqKOE+vshXra2aEHpi2TlxYX7Z9VFYkr+E5rwEvHC8eIXiyO+K9kNm8vmNom3e2rA56WqxU+/N9NJlLGXsJQ==", + "dependencies": { + "Microsoft.CodeCoverage": "17.14.1", + "Microsoft.TestPlatform.TestHost": "17.14.1" + } + }, + "xunit": { + "type": "Direct", + "requested": "[2.9.3, )", + "resolved": "2.9.3", + "contentHash": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==", + "dependencies": { + "xunit.analyzers": "1.18.0", + "xunit.assert": "2.9.3", + "xunit.core": "[2.9.3]" + } + }, + "xunit.runner.visualstudio": { + "type": "Direct", + "requested": "[3.1.4, )", + "resolved": "3.1.4", + "contentHash": "5mj99LvCqrq3CNi06xYdyIAXOEh+5b33F2nErCzI5zWiDdLHXiPXEWFSUAF8zlIv0ZWqjZNCwHTQeAPYbF3pCg==" + }, + "Microsoft.CodeCoverage": { + "type": "Transitive", + "resolved": "17.14.1", + "contentHash": "pmTrhfFIoplzFVbhVwUquT+77CbGH+h4/3mBpdmIlYtBi9nAB+kKI6dN3A/nV4DFi3wLLx/BlHIPK+MkbQ6Tpg==" + }, + "Microsoft.TestPlatform.ObjectModel": { + "type": "Transitive", + "resolved": "17.14.1", + "contentHash": "xTP1W6Mi6SWmuxd3a+jj9G9UoC850WGwZUps1Wah9r1ZxgXhdJfj1QqDLJkFjHDCvN42qDL2Ps5KjQYWUU0zcQ==" + }, + "Microsoft.TestPlatform.TestHost": { + "type": "Transitive", + "resolved": "17.14.1", + "contentHash": "d78LPzGKkJwsJXAQwsbJJ7LE7D1wB+rAyhHHAaODF+RDSQ0NgMjDFkSA1Djw18VrxO76GlKAjRUhl+H8NL8Z+Q==", + "dependencies": { + "Microsoft.TestPlatform.ObjectModel": "17.14.1", + "Newtonsoft.Json": "13.0.3" + } + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" + }, + "xunit.abstractions": { + "type": "Transitive", + "resolved": "2.0.3", + "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==" + }, + "xunit.analyzers": { + "type": "Transitive", + "resolved": "1.18.0", + "contentHash": "OtFMHN8yqIcYP9wcVIgJrq01AfTxijjAqVDy/WeQVSyrDC1RzBWeQPztL49DN2syXRah8TYnfvk035s7L95EZQ==" + }, + "xunit.assert": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA==" + }, + "xunit.core": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "BiAEvqGvyme19wE0wTKdADH+NloYqikiU0mcnmiNyXaF9HyHmE6sr/3DC5vnBkgsWaE6yPyWszKSPSApWdRVeQ==", + "dependencies": { + "xunit.extensibility.core": "[2.9.3]", + "xunit.extensibility.execution": "[2.9.3]" + } + }, + "xunit.extensibility.core": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==", + "dependencies": { + "xunit.abstractions": "2.0.3" + } + }, + "xunit.extensibility.execution": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==", + "dependencies": { + "xunit.extensibility.core": "[2.9.3]" + } + } + } + } +} \ No newline at end of file diff --git a/tests/FunctionFoundry.Integrity.Tests/IntegrityExtraBranchTests.cs b/tests/FunctionFoundry.Integrity.Tests/IntegrityExtraBranchTests.cs new file mode 100644 index 0000000..e31d438 --- /dev/null +++ b/tests/FunctionFoundry.Integrity.Tests/IntegrityExtraBranchTests.cs @@ -0,0 +1,77 @@ +using System.Text; +using FunctionFoundry.Integrity; + +namespace FunctionFoundry.Integrity.Tests; + +public sealed class IntegrityExtraBranchTests +{ + [Fact] + public void CanonicalJson_rejects_empty_input_and_invalid_buffer_size() + { + Assert.Throws(() => CanonicalJson.Canonicalize(ReadOnlySpan.Empty)); + Assert.Throws(() => CanonicalJson.Canonicalize(string.Empty)); + using var input = new MemoryStream("{}"u8.ToArray()); + using var output = new MemoryStream(); + Assert.Throws(() => CanonicalJson.Canonicalize(input, output, bufferSize: 0)); + } + + [Fact] + public void CanonicalJson_TryCanonicalize_succeeds_for_valid_json() + { + bool ok = CanonicalJson.TryCanonicalize("{\"b\":1,\"a\":2}"u8, out byte[]? canonical, out string? error); + Assert.True(ok); + Assert.Null(error); + Assert.Equal("{\"a\":2,\"b\":1}", Encoding.UTF8.GetString(canonical!)); + } + + [Fact] + public void CanonicalJson_nested_objects_and_arrays_sort_recursively() + { + const string Input = "{\"z\":[{\"b\":1,\"a\":2},true],\"a\":{\"y\":null,\"x\":false}}"; + string actual = Encoding.UTF8.GetString(CanonicalJson.Canonicalize(Input)); + Assert.Equal("{\"a\":{\"x\":false,\"y\":null},\"z\":[{\"a\":2,\"b\":1},true]}", actual); + } + + [Fact] + public void MerkleTree_rejects_empty_leaves_and_verifies_all_indices() + { + Assert.Throws(() => MerkleTree.Build(Array.Empty())); + byte[][] leaves = ["a"u8.ToArray(), "b"u8.ToArray(), "c"u8.ToArray(), "d"u8.ToArray()]; + MerkleTree tree = MerkleTree.Build(leaves); + for (int i = 0; i < leaves.Length; i++) + { + MerkleProof proof = tree.CreateProof(i); + MerkleProofVerificationResult result = MerkleTree.VerifyProof(tree.Root.Span, leaves[i], proof); + Assert.True(result.IsValid); + } + } + + [Fact] + public void HashChain_rejects_null_records_and_detects_sequence_gaps() + { + Assert.Throws(() => HashChain.Verify(null!)); + HashChainRecord genesis = HashChain.CreateGenesis("g"u8); + HashChainRecord second = HashChain.Append(genesis, "ok"u8); + var gapped = second with { Sequence = 5 }; + HashChainVerificationResult result = HashChain.Verify([genesis, gapped]); + Assert.False(result.IsValid); + Assert.NotNull(result.FirstInvalidRecord); + } + + [Fact] + public void StreamingHashManifest_rejects_null_content_and_verifies_valid_set() + { + var builder = StreamingHashManifest.CreateBuilder(); + Stream? content = null; + Assert.Throws(() => builder.AddEntry("x.txt", content!)); + builder.AddEntry("x.txt", new MemoryStream("payload"u8.ToArray()), includeSize: true); + StreamingHashManifestDocument manifest = StreamingHashManifest.Parse(builder.Build()); + ManifestVerificationResult ok = StreamingHashManifest.Verify( + manifest, + new Dictionary { ["x.txt"] = new MemoryStream("payload"u8.ToArray()) }); + Assert.True(ok.IsValid); + Assert.Empty(ok.MissingPaths); + Assert.Empty(ok.UnexpectedPaths); + Assert.Empty(ok.HashMismatches); + } +} diff --git a/tests/FunctionFoundry.Integrity.Tests/packages.lock.json b/tests/FunctionFoundry.Integrity.Tests/packages.lock.json new file mode 100644 index 0000000..d5e9225 --- /dev/null +++ b/tests/FunctionFoundry.Integrity.Tests/packages.lock.json @@ -0,0 +1,107 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "coverlet.collector": { + "type": "Direct", + "requested": "[6.0.4, )", + "resolved": "6.0.4", + "contentHash": "lkhqpF8Pu2Y7IiN7OntbsTtdbpR1syMsm2F3IgX6ootA4ffRqWL5jF7XipHuZQTdVuWG/gVAAcf8mjk8Tz0xPg==" + }, + "Microsoft.NET.Test.Sdk": { + "type": "Direct", + "requested": "[17.14.1, )", + "resolved": "17.14.1", + "contentHash": "HJKqKOE+vshXra2aEHpi2TlxYX7Z9VFYkr+E5rwEvHC8eIXiyO+K9kNm8vmNom3e2rA56WqxU+/N9NJlLGXsJQ==", + "dependencies": { + "Microsoft.CodeCoverage": "17.14.1", + "Microsoft.TestPlatform.TestHost": "17.14.1" + } + }, + "xunit": { + "type": "Direct", + "requested": "[2.9.3, )", + "resolved": "2.9.3", + "contentHash": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==", + "dependencies": { + "xunit.analyzers": "1.18.0", + "xunit.assert": "2.9.3", + "xunit.core": "[2.9.3]" + } + }, + "xunit.runner.visualstudio": { + "type": "Direct", + "requested": "[3.1.4, )", + "resolved": "3.1.4", + "contentHash": "5mj99LvCqrq3CNi06xYdyIAXOEh+5b33F2nErCzI5zWiDdLHXiPXEWFSUAF8zlIv0ZWqjZNCwHTQeAPYbF3pCg==" + }, + "Microsoft.CodeCoverage": { + "type": "Transitive", + "resolved": "17.14.1", + "contentHash": "pmTrhfFIoplzFVbhVwUquT+77CbGH+h4/3mBpdmIlYtBi9nAB+kKI6dN3A/nV4DFi3wLLx/BlHIPK+MkbQ6Tpg==" + }, + "Microsoft.TestPlatform.ObjectModel": { + "type": "Transitive", + "resolved": "17.14.1", + "contentHash": "xTP1W6Mi6SWmuxd3a+jj9G9UoC850WGwZUps1Wah9r1ZxgXhdJfj1QqDLJkFjHDCvN42qDL2Ps5KjQYWUU0zcQ==" + }, + "Microsoft.TestPlatform.TestHost": { + "type": "Transitive", + "resolved": "17.14.1", + "contentHash": "d78LPzGKkJwsJXAQwsbJJ7LE7D1wB+rAyhHHAaODF+RDSQ0NgMjDFkSA1Djw18VrxO76GlKAjRUhl+H8NL8Z+Q==", + "dependencies": { + "Microsoft.TestPlatform.ObjectModel": "17.14.1", + "Newtonsoft.Json": "13.0.3" + } + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" + }, + "xunit.abstractions": { + "type": "Transitive", + "resolved": "2.0.3", + "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==" + }, + "xunit.analyzers": { + "type": "Transitive", + "resolved": "1.18.0", + "contentHash": "OtFMHN8yqIcYP9wcVIgJrq01AfTxijjAqVDy/WeQVSyrDC1RzBWeQPztL49DN2syXRah8TYnfvk035s7L95EZQ==" + }, + "xunit.assert": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA==" + }, + "xunit.core": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "BiAEvqGvyme19wE0wTKdADH+NloYqikiU0mcnmiNyXaF9HyHmE6sr/3DC5vnBkgsWaE6yPyWszKSPSApWdRVeQ==", + "dependencies": { + "xunit.extensibility.core": "[2.9.3]", + "xunit.extensibility.execution": "[2.9.3]" + } + }, + "xunit.extensibility.core": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==", + "dependencies": { + "xunit.abstractions": "2.0.3" + } + }, + "xunit.extensibility.execution": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==", + "dependencies": { + "xunit.extensibility.core": "[2.9.3]" + } + }, + "functionfoundry.integrity": { + "type": "Project" + } + } + } +} \ No newline at end of file diff --git a/tests/FunctionFoundry.Networking.Tests/ResumableParallelDownloaderTests.cs b/tests/FunctionFoundry.Networking.Tests/ResumableParallelDownloaderTests.cs index d61e4bb..db59ed3 100644 --- a/tests/FunctionFoundry.Networking.Tests/ResumableParallelDownloaderTests.cs +++ b/tests/FunctionFoundry.Networking.Tests/ResumableParallelDownloaderTests.cs @@ -114,6 +114,55 @@ public async Task Falls_back_to_full_download_when_ranges_unsupported() Assert.False(result.UsedRangeRequests); } + [Fact] + public async Task Adaptive_chunk_size_stays_within_bounds_after_download() + { + const string content = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + byte[] payload = Encoding.UTF8.GetBytes(content); + string hash = Hashing.Sha256Hex(payload); + using var handler = new FakeDownloadHandler(content); + using var client = new HttpClient(handler); + var options = new ResumableParallelDownloaderOptions + { + InitialChunkSizeBytes = 16, + MinChunkSizeBytes = 8, + MaxChunkSizeBytes = 32, + MaxParallelism = 2, + EnableAdaptiveChunkSizing = true, + }; + var downloader = new ResumableParallelDownloader(client, new InMemoryDownloadCheckpointStore(), options); + await using MemoryStream destination = new(); + DownloadResult result = await downloader.DownloadAsync(new Uri("https://example.test/adaptive"), destination, hash); + Assert.True(result.Verified); + Assert.InRange(downloader.AdaptiveChunkSizeBytes, options.MinChunkSizeBytes, options.MaxChunkSizeBytes); + } + + [Fact] + public async Task Corrupt_chunk_length_is_repaired() + { + const string content = "0123456789abcdef"; + byte[] payload = Encoding.UTF8.GetBytes(content); + string hash = Hashing.Sha256Hex(payload); + using var handler = new FlakyLengthHandler(payload, corruptOnceChunkIndex: 1); + using var client = new HttpClient(handler); + var downloader = new ResumableParallelDownloader( + client, + new InMemoryDownloadCheckpointStore(), + new ResumableParallelDownloaderOptions + { + InitialChunkSizeBytes = 4, + MinChunkSizeBytes = 4, + MaxChunkSizeBytes = 8, + MaxParallelism = 1, + MaxRepairPasses = 1, + }); + + await using MemoryStream destination = new(); + DownloadResult result = await downloader.DownloadAsync(new Uri("https://example.test/repair"), destination, hash); + Assert.True(result.Verified); + Assert.True(handler.CorruptResponses > 0); + } + private sealed class FullOnlyHandler : HttpMessageHandler { private readonly byte[] _payload; @@ -130,4 +179,52 @@ protected override Task SendAsync(HttpRequestMessage reques return Task.FromResult(response); } } + + private sealed class FlakyLengthHandler : HttpMessageHandler + { + private readonly byte[] _payload; + private readonly int _corruptOnceChunkIndex; + private int _rangeCalls; + + public FlakyLengthHandler(byte[] payload, int corruptOnceChunkIndex) + { + _payload = payload; + _corruptOnceChunkIndex = corruptOnceChunkIndex; + } + + public int CorruptResponses { get; private set; } + + protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + if (request.Headers.Range is RangeHeaderValue range && range.Ranges.Count > 0) + { + RangeItemHeaderValue item = range.Ranges.First(); + long from = item.From ?? 0; + long to = item.To ?? (_payload.Length - 1); + int length = (int)(to - from + 1); + int call = Interlocked.Increment(ref _rangeCalls) - 1; + byte[] slice = _payload.AsSpan((int)from, length).ToArray(); + if (call == _corruptOnceChunkIndex) + { + CorruptResponses++; + slice = slice.AsSpan(0, Math.Max(1, slice.Length - 1)).ToArray(); + } + + var response = new HttpResponseMessage(HttpStatusCode.PartialContent) + { + Content = new ByteArrayContent(slice), + }; + response.Content.Headers.ContentRange = new ContentRangeHeaderValue(from, to, _payload.Length); + response.Headers.ETag = new EntityTagHeaderValue("\"v1\""); + return Task.FromResult(response); + } + + var full = new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new ByteArrayContent(_payload), + }; + full.Content.Headers.ContentLength = _payload.Length; + return Task.FromResult(full); + } + } } diff --git a/tests/FunctionFoundry.Networking.Tests/packages.lock.json b/tests/FunctionFoundry.Networking.Tests/packages.lock.json new file mode 100644 index 0000000..f326913 --- /dev/null +++ b/tests/FunctionFoundry.Networking.Tests/packages.lock.json @@ -0,0 +1,107 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "coverlet.collector": { + "type": "Direct", + "requested": "[6.0.4, )", + "resolved": "6.0.4", + "contentHash": "lkhqpF8Pu2Y7IiN7OntbsTtdbpR1syMsm2F3IgX6ootA4ffRqWL5jF7XipHuZQTdVuWG/gVAAcf8mjk8Tz0xPg==" + }, + "Microsoft.NET.Test.Sdk": { + "type": "Direct", + "requested": "[17.14.1, )", + "resolved": "17.14.1", + "contentHash": "HJKqKOE+vshXra2aEHpi2TlxYX7Z9VFYkr+E5rwEvHC8eIXiyO+K9kNm8vmNom3e2rA56WqxU+/N9NJlLGXsJQ==", + "dependencies": { + "Microsoft.CodeCoverage": "17.14.1", + "Microsoft.TestPlatform.TestHost": "17.14.1" + } + }, + "xunit": { + "type": "Direct", + "requested": "[2.9.3, )", + "resolved": "2.9.3", + "contentHash": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==", + "dependencies": { + "xunit.analyzers": "1.18.0", + "xunit.assert": "2.9.3", + "xunit.core": "[2.9.3]" + } + }, + "xunit.runner.visualstudio": { + "type": "Direct", + "requested": "[3.1.4, )", + "resolved": "3.1.4", + "contentHash": "5mj99LvCqrq3CNi06xYdyIAXOEh+5b33F2nErCzI5zWiDdLHXiPXEWFSUAF8zlIv0ZWqjZNCwHTQeAPYbF3pCg==" + }, + "Microsoft.CodeCoverage": { + "type": "Transitive", + "resolved": "17.14.1", + "contentHash": "pmTrhfFIoplzFVbhVwUquT+77CbGH+h4/3mBpdmIlYtBi9nAB+kKI6dN3A/nV4DFi3wLLx/BlHIPK+MkbQ6Tpg==" + }, + "Microsoft.TestPlatform.ObjectModel": { + "type": "Transitive", + "resolved": "17.14.1", + "contentHash": "xTP1W6Mi6SWmuxd3a+jj9G9UoC850WGwZUps1Wah9r1ZxgXhdJfj1QqDLJkFjHDCvN42qDL2Ps5KjQYWUU0zcQ==" + }, + "Microsoft.TestPlatform.TestHost": { + "type": "Transitive", + "resolved": "17.14.1", + "contentHash": "d78LPzGKkJwsJXAQwsbJJ7LE7D1wB+rAyhHHAaODF+RDSQ0NgMjDFkSA1Djw18VrxO76GlKAjRUhl+H8NL8Z+Q==", + "dependencies": { + "Microsoft.TestPlatform.ObjectModel": "17.14.1", + "Newtonsoft.Json": "13.0.3" + } + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" + }, + "xunit.abstractions": { + "type": "Transitive", + "resolved": "2.0.3", + "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==" + }, + "xunit.analyzers": { + "type": "Transitive", + "resolved": "1.18.0", + "contentHash": "OtFMHN8yqIcYP9wcVIgJrq01AfTxijjAqVDy/WeQVSyrDC1RzBWeQPztL49DN2syXRah8TYnfvk035s7L95EZQ==" + }, + "xunit.assert": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA==" + }, + "xunit.core": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "BiAEvqGvyme19wE0wTKdADH+NloYqikiU0mcnmiNyXaF9HyHmE6sr/3DC5vnBkgsWaE6yPyWszKSPSApWdRVeQ==", + "dependencies": { + "xunit.extensibility.core": "[2.9.3]", + "xunit.extensibility.execution": "[2.9.3]" + } + }, + "xunit.extensibility.core": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==", + "dependencies": { + "xunit.abstractions": "2.0.3" + } + }, + "xunit.extensibility.execution": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==", + "dependencies": { + "xunit.extensibility.core": "[2.9.3]" + } + }, + "functionfoundry.networking": { + "type": "Project" + } + } + } +} \ No newline at end of file diff --git a/tests/FunctionFoundry.Observability.Tests/packages.lock.json b/tests/FunctionFoundry.Observability.Tests/packages.lock.json new file mode 100644 index 0000000..a31f6e3 --- /dev/null +++ b/tests/FunctionFoundry.Observability.Tests/packages.lock.json @@ -0,0 +1,107 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "coverlet.collector": { + "type": "Direct", + "requested": "[6.0.4, )", + "resolved": "6.0.4", + "contentHash": "lkhqpF8Pu2Y7IiN7OntbsTtdbpR1syMsm2F3IgX6ootA4ffRqWL5jF7XipHuZQTdVuWG/gVAAcf8mjk8Tz0xPg==" + }, + "Microsoft.NET.Test.Sdk": { + "type": "Direct", + "requested": "[17.14.1, )", + "resolved": "17.14.1", + "contentHash": "HJKqKOE+vshXra2aEHpi2TlxYX7Z9VFYkr+E5rwEvHC8eIXiyO+K9kNm8vmNom3e2rA56WqxU+/N9NJlLGXsJQ==", + "dependencies": { + "Microsoft.CodeCoverage": "17.14.1", + "Microsoft.TestPlatform.TestHost": "17.14.1" + } + }, + "xunit": { + "type": "Direct", + "requested": "[2.9.3, )", + "resolved": "2.9.3", + "contentHash": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==", + "dependencies": { + "xunit.analyzers": "1.18.0", + "xunit.assert": "2.9.3", + "xunit.core": "[2.9.3]" + } + }, + "xunit.runner.visualstudio": { + "type": "Direct", + "requested": "[3.1.4, )", + "resolved": "3.1.4", + "contentHash": "5mj99LvCqrq3CNi06xYdyIAXOEh+5b33F2nErCzI5zWiDdLHXiPXEWFSUAF8zlIv0ZWqjZNCwHTQeAPYbF3pCg==" + }, + "Microsoft.CodeCoverage": { + "type": "Transitive", + "resolved": "17.14.1", + "contentHash": "pmTrhfFIoplzFVbhVwUquT+77CbGH+h4/3mBpdmIlYtBi9nAB+kKI6dN3A/nV4DFi3wLLx/BlHIPK+MkbQ6Tpg==" + }, + "Microsoft.TestPlatform.ObjectModel": { + "type": "Transitive", + "resolved": "17.14.1", + "contentHash": "xTP1W6Mi6SWmuxd3a+jj9G9UoC850WGwZUps1Wah9r1ZxgXhdJfj1QqDLJkFjHDCvN42qDL2Ps5KjQYWUU0zcQ==" + }, + "Microsoft.TestPlatform.TestHost": { + "type": "Transitive", + "resolved": "17.14.1", + "contentHash": "d78LPzGKkJwsJXAQwsbJJ7LE7D1wB+rAyhHHAaODF+RDSQ0NgMjDFkSA1Djw18VrxO76GlKAjRUhl+H8NL8Z+Q==", + "dependencies": { + "Microsoft.TestPlatform.ObjectModel": "17.14.1", + "Newtonsoft.Json": "13.0.3" + } + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" + }, + "xunit.abstractions": { + "type": "Transitive", + "resolved": "2.0.3", + "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==" + }, + "xunit.analyzers": { + "type": "Transitive", + "resolved": "1.18.0", + "contentHash": "OtFMHN8yqIcYP9wcVIgJrq01AfTxijjAqVDy/WeQVSyrDC1RzBWeQPztL49DN2syXRah8TYnfvk035s7L95EZQ==" + }, + "xunit.assert": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA==" + }, + "xunit.core": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "BiAEvqGvyme19wE0wTKdADH+NloYqikiU0mcnmiNyXaF9HyHmE6sr/3DC5vnBkgsWaE6yPyWszKSPSApWdRVeQ==", + "dependencies": { + "xunit.extensibility.core": "[2.9.3]", + "xunit.extensibility.execution": "[2.9.3]" + } + }, + "xunit.extensibility.core": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==", + "dependencies": { + "xunit.abstractions": "2.0.3" + } + }, + "xunit.extensibility.execution": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==", + "dependencies": { + "xunit.extensibility.core": "[2.9.3]" + } + }, + "functionfoundry.observability": { + "type": "Project" + } + } + } +} \ No newline at end of file diff --git a/tests/FunctionFoundry.Resilience.Tests/packages.lock.json b/tests/FunctionFoundry.Resilience.Tests/packages.lock.json new file mode 100644 index 0000000..0a1b6d7 --- /dev/null +++ b/tests/FunctionFoundry.Resilience.Tests/packages.lock.json @@ -0,0 +1,107 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "coverlet.collector": { + "type": "Direct", + "requested": "[6.0.4, )", + "resolved": "6.0.4", + "contentHash": "lkhqpF8Pu2Y7IiN7OntbsTtdbpR1syMsm2F3IgX6ootA4ffRqWL5jF7XipHuZQTdVuWG/gVAAcf8mjk8Tz0xPg==" + }, + "Microsoft.NET.Test.Sdk": { + "type": "Direct", + "requested": "[17.14.1, )", + "resolved": "17.14.1", + "contentHash": "HJKqKOE+vshXra2aEHpi2TlxYX7Z9VFYkr+E5rwEvHC8eIXiyO+K9kNm8vmNom3e2rA56WqxU+/N9NJlLGXsJQ==", + "dependencies": { + "Microsoft.CodeCoverage": "17.14.1", + "Microsoft.TestPlatform.TestHost": "17.14.1" + } + }, + "xunit": { + "type": "Direct", + "requested": "[2.9.3, )", + "resolved": "2.9.3", + "contentHash": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==", + "dependencies": { + "xunit.analyzers": "1.18.0", + "xunit.assert": "2.9.3", + "xunit.core": "[2.9.3]" + } + }, + "xunit.runner.visualstudio": { + "type": "Direct", + "requested": "[3.1.4, )", + "resolved": "3.1.4", + "contentHash": "5mj99LvCqrq3CNi06xYdyIAXOEh+5b33F2nErCzI5zWiDdLHXiPXEWFSUAF8zlIv0ZWqjZNCwHTQeAPYbF3pCg==" + }, + "Microsoft.CodeCoverage": { + "type": "Transitive", + "resolved": "17.14.1", + "contentHash": "pmTrhfFIoplzFVbhVwUquT+77CbGH+h4/3mBpdmIlYtBi9nAB+kKI6dN3A/nV4DFi3wLLx/BlHIPK+MkbQ6Tpg==" + }, + "Microsoft.TestPlatform.ObjectModel": { + "type": "Transitive", + "resolved": "17.14.1", + "contentHash": "xTP1W6Mi6SWmuxd3a+jj9G9UoC850WGwZUps1Wah9r1ZxgXhdJfj1QqDLJkFjHDCvN42qDL2Ps5KjQYWUU0zcQ==" + }, + "Microsoft.TestPlatform.TestHost": { + "type": "Transitive", + "resolved": "17.14.1", + "contentHash": "d78LPzGKkJwsJXAQwsbJJ7LE7D1wB+rAyhHHAaODF+RDSQ0NgMjDFkSA1Djw18VrxO76GlKAjRUhl+H8NL8Z+Q==", + "dependencies": { + "Microsoft.TestPlatform.ObjectModel": "17.14.1", + "Newtonsoft.Json": "13.0.3" + } + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" + }, + "xunit.abstractions": { + "type": "Transitive", + "resolved": "2.0.3", + "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==" + }, + "xunit.analyzers": { + "type": "Transitive", + "resolved": "1.18.0", + "contentHash": "OtFMHN8yqIcYP9wcVIgJrq01AfTxijjAqVDy/WeQVSyrDC1RzBWeQPztL49DN2syXRah8TYnfvk035s7L95EZQ==" + }, + "xunit.assert": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA==" + }, + "xunit.core": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "BiAEvqGvyme19wE0wTKdADH+NloYqikiU0mcnmiNyXaF9HyHmE6sr/3DC5vnBkgsWaE6yPyWszKSPSApWdRVeQ==", + "dependencies": { + "xunit.extensibility.core": "[2.9.3]", + "xunit.extensibility.execution": "[2.9.3]" + } + }, + "xunit.extensibility.core": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==", + "dependencies": { + "xunit.abstractions": "2.0.3" + } + }, + "xunit.extensibility.execution": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==", + "dependencies": { + "xunit.extensibility.core": "[2.9.3]" + } + }, + "functionfoundry.resilience": { + "type": "Project" + } + } + } +} \ No newline at end of file diff --git a/tests/FunctionFoundry.Scheduling.Tests/packages.lock.json b/tests/FunctionFoundry.Scheduling.Tests/packages.lock.json new file mode 100644 index 0000000..864ea43 --- /dev/null +++ b/tests/FunctionFoundry.Scheduling.Tests/packages.lock.json @@ -0,0 +1,107 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "coverlet.collector": { + "type": "Direct", + "requested": "[6.0.4, )", + "resolved": "6.0.4", + "contentHash": "lkhqpF8Pu2Y7IiN7OntbsTtdbpR1syMsm2F3IgX6ootA4ffRqWL5jF7XipHuZQTdVuWG/gVAAcf8mjk8Tz0xPg==" + }, + "Microsoft.NET.Test.Sdk": { + "type": "Direct", + "requested": "[17.14.1, )", + "resolved": "17.14.1", + "contentHash": "HJKqKOE+vshXra2aEHpi2TlxYX7Z9VFYkr+E5rwEvHC8eIXiyO+K9kNm8vmNom3e2rA56WqxU+/N9NJlLGXsJQ==", + "dependencies": { + "Microsoft.CodeCoverage": "17.14.1", + "Microsoft.TestPlatform.TestHost": "17.14.1" + } + }, + "xunit": { + "type": "Direct", + "requested": "[2.9.3, )", + "resolved": "2.9.3", + "contentHash": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==", + "dependencies": { + "xunit.analyzers": "1.18.0", + "xunit.assert": "2.9.3", + "xunit.core": "[2.9.3]" + } + }, + "xunit.runner.visualstudio": { + "type": "Direct", + "requested": "[3.1.4, )", + "resolved": "3.1.4", + "contentHash": "5mj99LvCqrq3CNi06xYdyIAXOEh+5b33F2nErCzI5zWiDdLHXiPXEWFSUAF8zlIv0ZWqjZNCwHTQeAPYbF3pCg==" + }, + "Microsoft.CodeCoverage": { + "type": "Transitive", + "resolved": "17.14.1", + "contentHash": "pmTrhfFIoplzFVbhVwUquT+77CbGH+h4/3mBpdmIlYtBi9nAB+kKI6dN3A/nV4DFi3wLLx/BlHIPK+MkbQ6Tpg==" + }, + "Microsoft.TestPlatform.ObjectModel": { + "type": "Transitive", + "resolved": "17.14.1", + "contentHash": "xTP1W6Mi6SWmuxd3a+jj9G9UoC850WGwZUps1Wah9r1ZxgXhdJfj1QqDLJkFjHDCvN42qDL2Ps5KjQYWUU0zcQ==" + }, + "Microsoft.TestPlatform.TestHost": { + "type": "Transitive", + "resolved": "17.14.1", + "contentHash": "d78LPzGKkJwsJXAQwsbJJ7LE7D1wB+rAyhHHAaODF+RDSQ0NgMjDFkSA1Djw18VrxO76GlKAjRUhl+H8NL8Z+Q==", + "dependencies": { + "Microsoft.TestPlatform.ObjectModel": "17.14.1", + "Newtonsoft.Json": "13.0.3" + } + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" + }, + "xunit.abstractions": { + "type": "Transitive", + "resolved": "2.0.3", + "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==" + }, + "xunit.analyzers": { + "type": "Transitive", + "resolved": "1.18.0", + "contentHash": "OtFMHN8yqIcYP9wcVIgJrq01AfTxijjAqVDy/WeQVSyrDC1RzBWeQPztL49DN2syXRah8TYnfvk035s7L95EZQ==" + }, + "xunit.assert": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA==" + }, + "xunit.core": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "BiAEvqGvyme19wE0wTKdADH+NloYqikiU0mcnmiNyXaF9HyHmE6sr/3DC5vnBkgsWaE6yPyWszKSPSApWdRVeQ==", + "dependencies": { + "xunit.extensibility.core": "[2.9.3]", + "xunit.extensibility.execution": "[2.9.3]" + } + }, + "xunit.extensibility.core": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==", + "dependencies": { + "xunit.abstractions": "2.0.3" + } + }, + "xunit.extensibility.execution": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==", + "dependencies": { + "xunit.extensibility.core": "[2.9.3]" + } + }, + "functionfoundry.scheduling": { + "type": "Project" + } + } + } +} \ No newline at end of file diff --git a/tests/FunctionFoundry.Security.Tests/packages.lock.json b/tests/FunctionFoundry.Security.Tests/packages.lock.json new file mode 100644 index 0000000..c4130d2 --- /dev/null +++ b/tests/FunctionFoundry.Security.Tests/packages.lock.json @@ -0,0 +1,107 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "coverlet.collector": { + "type": "Direct", + "requested": "[6.0.4, )", + "resolved": "6.0.4", + "contentHash": "lkhqpF8Pu2Y7IiN7OntbsTtdbpR1syMsm2F3IgX6ootA4ffRqWL5jF7XipHuZQTdVuWG/gVAAcf8mjk8Tz0xPg==" + }, + "Microsoft.NET.Test.Sdk": { + "type": "Direct", + "requested": "[17.14.1, )", + "resolved": "17.14.1", + "contentHash": "HJKqKOE+vshXra2aEHpi2TlxYX7Z9VFYkr+E5rwEvHC8eIXiyO+K9kNm8vmNom3e2rA56WqxU+/N9NJlLGXsJQ==", + "dependencies": { + "Microsoft.CodeCoverage": "17.14.1", + "Microsoft.TestPlatform.TestHost": "17.14.1" + } + }, + "xunit": { + "type": "Direct", + "requested": "[2.9.3, )", + "resolved": "2.9.3", + "contentHash": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==", + "dependencies": { + "xunit.analyzers": "1.18.0", + "xunit.assert": "2.9.3", + "xunit.core": "[2.9.3]" + } + }, + "xunit.runner.visualstudio": { + "type": "Direct", + "requested": "[3.1.4, )", + "resolved": "3.1.4", + "contentHash": "5mj99LvCqrq3CNi06xYdyIAXOEh+5b33F2nErCzI5zWiDdLHXiPXEWFSUAF8zlIv0ZWqjZNCwHTQeAPYbF3pCg==" + }, + "Microsoft.CodeCoverage": { + "type": "Transitive", + "resolved": "17.14.1", + "contentHash": "pmTrhfFIoplzFVbhVwUquT+77CbGH+h4/3mBpdmIlYtBi9nAB+kKI6dN3A/nV4DFi3wLLx/BlHIPK+MkbQ6Tpg==" + }, + "Microsoft.TestPlatform.ObjectModel": { + "type": "Transitive", + "resolved": "17.14.1", + "contentHash": "xTP1W6Mi6SWmuxd3a+jj9G9UoC850WGwZUps1Wah9r1ZxgXhdJfj1QqDLJkFjHDCvN42qDL2Ps5KjQYWUU0zcQ==" + }, + "Microsoft.TestPlatform.TestHost": { + "type": "Transitive", + "resolved": "17.14.1", + "contentHash": "d78LPzGKkJwsJXAQwsbJJ7LE7D1wB+rAyhHHAaODF+RDSQ0NgMjDFkSA1Djw18VrxO76GlKAjRUhl+H8NL8Z+Q==", + "dependencies": { + "Microsoft.TestPlatform.ObjectModel": "17.14.1", + "Newtonsoft.Json": "13.0.3" + } + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" + }, + "xunit.abstractions": { + "type": "Transitive", + "resolved": "2.0.3", + "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==" + }, + "xunit.analyzers": { + "type": "Transitive", + "resolved": "1.18.0", + "contentHash": "OtFMHN8yqIcYP9wcVIgJrq01AfTxijjAqVDy/WeQVSyrDC1RzBWeQPztL49DN2syXRah8TYnfvk035s7L95EZQ==" + }, + "xunit.assert": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA==" + }, + "xunit.core": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "BiAEvqGvyme19wE0wTKdADH+NloYqikiU0mcnmiNyXaF9HyHmE6sr/3DC5vnBkgsWaE6yPyWszKSPSApWdRVeQ==", + "dependencies": { + "xunit.extensibility.core": "[2.9.3]", + "xunit.extensibility.execution": "[2.9.3]" + } + }, + "xunit.extensibility.core": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==", + "dependencies": { + "xunit.abstractions": "2.0.3" + } + }, + "xunit.extensibility.execution": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==", + "dependencies": { + "xunit.extensibility.core": "[2.9.3]" + } + }, + "functionfoundry.security": { + "type": "Project" + } + } + } +} \ No newline at end of file diff --git a/tests/FunctionFoundry.Storage.Tests/packages.lock.json b/tests/FunctionFoundry.Storage.Tests/packages.lock.json new file mode 100644 index 0000000..aa250e8 --- /dev/null +++ b/tests/FunctionFoundry.Storage.Tests/packages.lock.json @@ -0,0 +1,107 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "coverlet.collector": { + "type": "Direct", + "requested": "[6.0.4, )", + "resolved": "6.0.4", + "contentHash": "lkhqpF8Pu2Y7IiN7OntbsTtdbpR1syMsm2F3IgX6ootA4ffRqWL5jF7XipHuZQTdVuWG/gVAAcf8mjk8Tz0xPg==" + }, + "Microsoft.NET.Test.Sdk": { + "type": "Direct", + "requested": "[17.14.1, )", + "resolved": "17.14.1", + "contentHash": "HJKqKOE+vshXra2aEHpi2TlxYX7Z9VFYkr+E5rwEvHC8eIXiyO+K9kNm8vmNom3e2rA56WqxU+/N9NJlLGXsJQ==", + "dependencies": { + "Microsoft.CodeCoverage": "17.14.1", + "Microsoft.TestPlatform.TestHost": "17.14.1" + } + }, + "xunit": { + "type": "Direct", + "requested": "[2.9.3, )", + "resolved": "2.9.3", + "contentHash": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==", + "dependencies": { + "xunit.analyzers": "1.18.0", + "xunit.assert": "2.9.3", + "xunit.core": "[2.9.3]" + } + }, + "xunit.runner.visualstudio": { + "type": "Direct", + "requested": "[3.1.4, )", + "resolved": "3.1.4", + "contentHash": "5mj99LvCqrq3CNi06xYdyIAXOEh+5b33F2nErCzI5zWiDdLHXiPXEWFSUAF8zlIv0ZWqjZNCwHTQeAPYbF3pCg==" + }, + "Microsoft.CodeCoverage": { + "type": "Transitive", + "resolved": "17.14.1", + "contentHash": "pmTrhfFIoplzFVbhVwUquT+77CbGH+h4/3mBpdmIlYtBi9nAB+kKI6dN3A/nV4DFi3wLLx/BlHIPK+MkbQ6Tpg==" + }, + "Microsoft.TestPlatform.ObjectModel": { + "type": "Transitive", + "resolved": "17.14.1", + "contentHash": "xTP1W6Mi6SWmuxd3a+jj9G9UoC850WGwZUps1Wah9r1ZxgXhdJfj1QqDLJkFjHDCvN42qDL2Ps5KjQYWUU0zcQ==" + }, + "Microsoft.TestPlatform.TestHost": { + "type": "Transitive", + "resolved": "17.14.1", + "contentHash": "d78LPzGKkJwsJXAQwsbJJ7LE7D1wB+rAyhHHAaODF+RDSQ0NgMjDFkSA1Djw18VrxO76GlKAjRUhl+H8NL8Z+Q==", + "dependencies": { + "Microsoft.TestPlatform.ObjectModel": "17.14.1", + "Newtonsoft.Json": "13.0.3" + } + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" + }, + "xunit.abstractions": { + "type": "Transitive", + "resolved": "2.0.3", + "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==" + }, + "xunit.analyzers": { + "type": "Transitive", + "resolved": "1.18.0", + "contentHash": "OtFMHN8yqIcYP9wcVIgJrq01AfTxijjAqVDy/WeQVSyrDC1RzBWeQPztL49DN2syXRah8TYnfvk035s7L95EZQ==" + }, + "xunit.assert": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA==" + }, + "xunit.core": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "BiAEvqGvyme19wE0wTKdADH+NloYqikiU0mcnmiNyXaF9HyHmE6sr/3DC5vnBkgsWaE6yPyWszKSPSApWdRVeQ==", + "dependencies": { + "xunit.extensibility.core": "[2.9.3]", + "xunit.extensibility.execution": "[2.9.3]" + } + }, + "xunit.extensibility.core": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==", + "dependencies": { + "xunit.abstractions": "2.0.3" + } + }, + "xunit.extensibility.execution": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==", + "dependencies": { + "xunit.extensibility.core": "[2.9.3]" + } + }, + "functionfoundry.storage": { + "type": "Project" + } + } + } +} \ No newline at end of file diff --git a/tests/FunctionFoundry.Text.Tests/packages.lock.json b/tests/FunctionFoundry.Text.Tests/packages.lock.json new file mode 100644 index 0000000..195a4e1 --- /dev/null +++ b/tests/FunctionFoundry.Text.Tests/packages.lock.json @@ -0,0 +1,107 @@ +{ + "version": 2, + "dependencies": { + "net10.0": { + "coverlet.collector": { + "type": "Direct", + "requested": "[6.0.4, )", + "resolved": "6.0.4", + "contentHash": "lkhqpF8Pu2Y7IiN7OntbsTtdbpR1syMsm2F3IgX6ootA4ffRqWL5jF7XipHuZQTdVuWG/gVAAcf8mjk8Tz0xPg==" + }, + "Microsoft.NET.Test.Sdk": { + "type": "Direct", + "requested": "[17.14.1, )", + "resolved": "17.14.1", + "contentHash": "HJKqKOE+vshXra2aEHpi2TlxYX7Z9VFYkr+E5rwEvHC8eIXiyO+K9kNm8vmNom3e2rA56WqxU+/N9NJlLGXsJQ==", + "dependencies": { + "Microsoft.CodeCoverage": "17.14.1", + "Microsoft.TestPlatform.TestHost": "17.14.1" + } + }, + "xunit": { + "type": "Direct", + "requested": "[2.9.3, )", + "resolved": "2.9.3", + "contentHash": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==", + "dependencies": { + "xunit.analyzers": "1.18.0", + "xunit.assert": "2.9.3", + "xunit.core": "[2.9.3]" + } + }, + "xunit.runner.visualstudio": { + "type": "Direct", + "requested": "[3.1.4, )", + "resolved": "3.1.4", + "contentHash": "5mj99LvCqrq3CNi06xYdyIAXOEh+5b33F2nErCzI5zWiDdLHXiPXEWFSUAF8zlIv0ZWqjZNCwHTQeAPYbF3pCg==" + }, + "Microsoft.CodeCoverage": { + "type": "Transitive", + "resolved": "17.14.1", + "contentHash": "pmTrhfFIoplzFVbhVwUquT+77CbGH+h4/3mBpdmIlYtBi9nAB+kKI6dN3A/nV4DFi3wLLx/BlHIPK+MkbQ6Tpg==" + }, + "Microsoft.TestPlatform.ObjectModel": { + "type": "Transitive", + "resolved": "17.14.1", + "contentHash": "xTP1W6Mi6SWmuxd3a+jj9G9UoC850WGwZUps1Wah9r1ZxgXhdJfj1QqDLJkFjHDCvN42qDL2Ps5KjQYWUU0zcQ==" + }, + "Microsoft.TestPlatform.TestHost": { + "type": "Transitive", + "resolved": "17.14.1", + "contentHash": "d78LPzGKkJwsJXAQwsbJJ7LE7D1wB+rAyhHHAaODF+RDSQ0NgMjDFkSA1Djw18VrxO76GlKAjRUhl+H8NL8Z+Q==", + "dependencies": { + "Microsoft.TestPlatform.ObjectModel": "17.14.1", + "Newtonsoft.Json": "13.0.3" + } + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" + }, + "xunit.abstractions": { + "type": "Transitive", + "resolved": "2.0.3", + "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==" + }, + "xunit.analyzers": { + "type": "Transitive", + "resolved": "1.18.0", + "contentHash": "OtFMHN8yqIcYP9wcVIgJrq01AfTxijjAqVDy/WeQVSyrDC1RzBWeQPztL49DN2syXRah8TYnfvk035s7L95EZQ==" + }, + "xunit.assert": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA==" + }, + "xunit.core": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "BiAEvqGvyme19wE0wTKdADH+NloYqikiU0mcnmiNyXaF9HyHmE6sr/3DC5vnBkgsWaE6yPyWszKSPSApWdRVeQ==", + "dependencies": { + "xunit.extensibility.core": "[2.9.3]", + "xunit.extensibility.execution": "[2.9.3]" + } + }, + "xunit.extensibility.core": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==", + "dependencies": { + "xunit.abstractions": "2.0.3" + } + }, + "xunit.extensibility.execution": { + "type": "Transitive", + "resolved": "2.9.3", + "contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==", + "dependencies": { + "xunit.extensibility.core": "[2.9.3]" + } + }, + "functionfoundry.text": { + "type": "Project" + } + } + } +} \ No newline at end of file