1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
//! Helpers shared by two or more ecosystem-specific lock-freshness gates in this module family.
//!
//! Kept separate from any one ecosystem file on purpose: neither helper below names a language,
//! and folding either into `cargo.rs`/`node.rs`/etc. would make a future reader wonder whether it
//! is safe to change for that one ecosystem alone. See `avoid-duplication` -- this is the "single
//! reason to change" shared code the rule asks for, not a premature abstraction: both helpers were
//! already serving two call sites before this file existed.
use BTreeSet;
use ;
/// The exact `(name, requirement)` alef's own registry-mode `test_apps` generator would write
/// for `lang`'s self-dependency on this crate's own published package -- the uv/pnpm sibling of
/// the cargo path's `discover_cargo_locks`/`registry_dependencies_on_local_crates`.
///
/// Reuses [`crate::core::config::e2e::E2eConfig::resolve_package`] -- the exact function
/// [`crate::e2e::codegen::python::PythonE2eCodegen`] and
/// [`crate::e2e::codegen::typescript::TypeScriptCodegen`] call to resolve their own `pkg_name`/
/// `pkg_version` in `DependencyMode::Registry` -- rather than re-deriving the package identity
/// from `[python]`/`[node]` config (`python_pip_name`/`node_package_name`), which is a DIFFERENT
/// knob: `packages/python/pyproject.toml`'s own `[project] name` and
/// `test_apps/python/pyproject.toml`'s dependency name are independently configurable and are not
/// guaranteed to agree (the e2e/test_apps package can be renamed, repathed, or version-pinned
/// separately from the published package's own manifest). `normalize` mirrors each generator's
/// own requirement-text rendering (`python`: `normalize_python_version`, PEP 508 comparator
/// handling; `node`: passed through verbatim, matching `render_package_json`'s "never strip an
/// explicit range operator" comment) so the returned `requirement` is byte-identical to what the
/// generator itself would have written, not a semver-equivalent reconstruction.
///
/// Deliberately conservative, returning `None` (never an exemption) unless BOTH an identity (per
/// `identity`, typically `name`, but `module` for Go -- see below) and `version` are explicitly
/// set on `[crates.e2e.registry.packages.<lang>]` (falling back to the base
/// `[crates.e2e.packages.<lang>]` only as `resolve_package` itself already does): the real
/// generators have a further fallback of their own for an unset name (derived from the e2e call's
/// `module`) and version (`resolved_version()`, then `"0.1.0"`), but registry-mode test apps are
/// only ever meaningful with an explicit, publishable package identity in practice, and guessing
/// at that derived fallback here risks matching a name this check has no real authority over.
///
/// `identity` extracts the field `lang`'s ecosystem actually keys a dependency requirement on --
/// `name` for every ecosystem except Go, which has no `name` concept at all and instead pins by
/// `module` (a Go module path, e.g. `github.com/example-org/example-crate/packages/go/v3`); the Go
/// caller passes `|package| package.name.clone().or_else(|| package.module.clone())` so a
/// `name`-based override still wins if one is ever configured. Threaded as a closure, the same
/// shape as `normalize`, rather than a Go-specific branch here, per this module's own doc: neither
/// helper should name a language.
///
/// Precise by construction for every caller: `lang` selects a single `[crates.e2e...packages.<lang>]`
/// row, so this can only ever match the requirement written for the crate actually being
/// processed -- unlike the cargo gate's own exemption (`super::cargo::explained_by_pending_publish`
/// equivalent lives in `version_lockfiles.rs`), which keys on any git-tracked in-tree package
/// sharing a name and version, this has no such collision surface to begin with. ~keep
pub
/// See [`registry_self_dependency`].
pub
/// Directories holding a `file_name` manifest that [`GeneratedFile::carries_alef_marker`] can
/// never certify, because the format has no comment syntax to carry an `alef:hash:` marker at all
/// (`generated_header: false` JSON, principally `package.json`). A lock-freshness gate keyed only
/// on this run's in-memory `current_gen_paths` -- itself filtered by that same marker predicate,
/// see [`super::super::generate::stampable_output_paths`] -- structurally never examines these paths, in
/// any run, which is the gap this function exists to close.
///
/// Reads the committed ownership record ([`crate::cli::cache::read_committed_owned_paths`],
/// `.alef-ownership.toml`) instead: it is the durable, general-purpose list of every path alef has
/// authorised itself to own *precisely because* it cannot carry a marker -- populated by
/// `write_scaffold_files_report`'s own write guard the first time it creates such a file, for
/// every unmarkable manifest kind alef emits, not only `package.json` for wasm. Filtering that
/// list by `file_name` extends `generated_paths` with every alef-managed manifest of that name
/// the registry already knows about, including one this particular run did not touch (a
/// `--crate`-scoped run, or a language skipped by the per-language cache) -- which is strictly
/// more correct for a freshness check than "only what this run happened to regenerate": the drift
/// this gate exists to catch does not require this run to have written the manifest, only for the
/// manifest and its sibling lock to disagree right now.
///
/// General by construction: nothing here names `wasm` or `node`. `crates/*-node/package.json` is
/// `generated_header: false` for the identical reason `crates/*-wasm/package.json` is and was
/// found to share this exact blind spot while auditing it, and both are closed by the same call
/// with no per-backend special case. `composer.json`, `Gemfile`, `go.mod`, and `pubspec.yaml` are
/// `generated_header: false` for the same underlying reason (JSON/Ruby-DSL/Go-module-file/YAML
/// package manifests, scaffolded once and thereafter user-owned per the
/// `generated-vs-user-maintained-boundary` context entry) and read from this identical list --
/// the PHP/Ruby/Go/Dart lock-freshness gates use this call exactly as the node gate always has,
/// with no new registration mechanism needed. ~keep
pub