ecluse
Safe HaskellNone
LanguageGHC2021

Ecluse.Package.Filter

Description

The ecosystem-agnostic filtering decision for a single public-upstream packument: which versions survive a rule set, which version dist-tags.latest resolves to, and the per-version decisions a no-survivors outcome must report.

This mirrors Ecluse.Package.Merge — the pure fold above the registry handle that emits a plan rather than a finished document. It reasons over the typed PackageInfo domain model only; it never touches a registry's wire format. The per-ecosystem adapter replays this plan onto the raw upstream document, so unmodeled wire keys survive (the typed model is lossy, so re-encoding it would drop them). See docs/architecture/registry-model.md → "Decision surface vs served surface".

Decision, not served surface. A FilterPlan carries exactly the decisions the filter owns:

  • Survivors. A version key survives iff the rules engine Approved it; every other verdict — a denial, deny-by-default, or an undecidable outcome — drops it. Presence in the served packument is availability (see docs/research/reverse-engineering/npm.md §8), so a non-approved version is removed rather than flagged.
  • Resolved latest. The surviving dist-tags.latest under the shared keep-unless-denied, stable-preferring rule (selectLatest): the upstream latest is kept untouched while it survives, and only repointed — to the highest stable survivor — when it was itself denied. This is the latest within the public set, which the cross-upstream merge then re-resolves over the union; it is not the final served latest.
  • Decisions. Every version's Decision, in version-key order, so a no-survivors outcome can render each denial and choose a status.

What the plan deliberately omits is any "dropped tags" list: a stale tag — one whose target did not survive — is droppable structurally from the survivor set alone (a tag is kept iff its target is in fpSurvivors), so the replay needs no extra field to find them. The plan stays minimal: the decisions the filter owns, nothing the replay can recompute.

This filters a single public packument (the gated set). Combining it with the trusted private set is the cross-upstream merge (Ecluse.Package.Merge).

Synopsis

Documentation

data FilterPlan Source #

The decisions filtering a single public packument owns, for the adapter to replay onto the raw upstream Value. Carries only what the filter decides over the typed model — never a finished, re-serialisable document (see this module's header). The replay derives everything else (which stale tags to drop, which time entries to prune) from these fields.

Constructors

FilterPlan 

Fields

  • fpSurvivors :: Set Text

    The surviving version keys (the raw infoVersions keys): exactly those the rules engine approved. Empty when no version survived.

  • fpLatest :: Maybe Version

    dist-tags.latest resolved over the survivors by the shared selector — kept as published while it survives, else repointed (stable-preferring) to the highest survivor. Nothing when nothing survives. When present it is always one of fpSurvivors, so the replay can point latest at a key that is served.

  • fpDecisions :: [Decision]

    Every version's Decision, in version-key order, for the no-survivors status and denial body. Carried for every version (not only the denied ones) so the adapter can zip them back onto the same-ordered versions.

Instances

Instances details
Show FilterPlan Source # 
Instance details

Defined in Ecluse.Package.Filter

Eq FilterPlan Source # 
Instance details

Defined in Ecluse.Package.Filter

filterPlan :: EvalContext -> [PrecededRule] -> PackageInfo -> FilterPlan Source #

Decide a single public packument against a rule set: which versions survive, where latest resolves, and every version's decision. Pure and total — it reasons over the typed PackageInfo alone, with no registry wire format in sight.

A version survives iff evalRules Approved it; every other verdict drops it. latest is resolved by selectLatest from the upstream-tagged latest (looked up among the versions, so a tag aimed at an absent version contributes nothing) and the surviving versions — kept while it survives, else repointed downward to the highest stable survivor. The decisions are returned for every version in key order, so the adapter has each denial's reason when nothing survives.

filterPlanFromDecisions :: Map Text Decision -> PackageInfo -> FilterPlan Source #

Build a FilterPlan from per-version Decisions already taken, rather than evaluating the pure tier here. This is the path the effectful tier feeds: it decides each version in IO (see Ecluse.Rules.Effectful), then hands the decisions here for the same pure survivor/latest resolution filterPlan performs. The decision map is keyed by raw version string and must cover exactly the packument's versions; a version with no decision is treated as not surviving.

A version survives iff its decision is an approval (pure or effectful); every other verdict — denial, deny-by-default, or Undecidable — drops it, so a fail-closed undecidable version is filtered out exactly like a denial, while its decision is still carried in fpDecisions for the no-survivors status.