From e78a8b052d6b7da4cc76f1044330f12a5413c2f6 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 5 Oct 2026 16:24:53 +0000 Subject: [PATCH 1/8] Start fix for #367 Assisted-by: Claude Code:claude-opus-5-5 From 48af26ff4684fd8ccfdb6d50cdfe2495d7ff7225 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 5 Oct 2026 16:32:09 +0000 Subject: [PATCH 2/8] Keep a project's own bun patch when rewiring Bun Bun applies a patch from `bun patch` (package.json patchedDependencies, mirrored in bun.lock) only while the lock resolves the package to its registry name@version. Hosted and vendored mode moved that entry to a hosted URL or a vendored tarball, so every later install silently dropped the user's own patch while socket-patch reported success. Hosted mode now leaves such a package on its registry entry in both bun.lock and bun.lockb, warns redirect_bun_patched_dependency_skipped naming the key, and keeps the in-run VEX from assuming the Socket patch applied. Vendored mode refuses it with vendor_lock_entry_unsupported before any write or download. Other packages in the lock are still rewired. Fixes #367 Assisted-by: Claude Code:claude-opus-5-5 --- crates/socket-patch-cli/CLI_CONTRACT.md | 1 + crates/socket-patch-core/src/hosted/engine.rs | 127 +++++++++++++++- .../src/patch/redirect/mod.rs | 98 ++++++++++++ .../src/vendor/bun_binary.rs | 10 +- .../socket-patch-core/src/vendor/bun_lock.rs | 140 +++++++++++++++++- .../src/vendor/bun_lock_text.rs | 88 +++++++++++ docs/testing/bun-compatibility.md | 1 + 7 files changed, 462 insertions(+), 3 deletions(-) diff --git a/crates/socket-patch-cli/CLI_CONTRACT.md b/crates/socket-patch-cli/CLI_CONTRACT.md index ce55c27c6..c5bcbc2a8 100644 --- a/crates/socket-patch-cli/CLI_CONTRACT.md +++ b/crates/socket-patch-cli/CLI_CONTRACT.md @@ -1262,6 +1262,7 @@ Every `--json` invocation emits a single JSON object that follows the **unified | `redirect_bun_workspace_unsupported` | `redirect.warnings[]` (warning) | scan/get `--mode hosted` (bun): a lockfileVersion-0 lock (Bun 1.1.39–1.1.45 `--save-text-lockfile`) holds `workspace:` packages; frozen installs of that grammar cannot keep the hosted tuple. Detail: "Bun version-0 workspace locks cannot preserve hosted tarballs on frozen installs; delete bun.lock and re-run `bun install` with Bun >= 1.2 (which writes lockfileVersion 1, accepted by hosted mode) — a plain in-place `bun install` bumps the version only when a workspace depends on another workspace (e.g. root -> member); otherwise it keeps version 0 or fails to resolve" (measured: Bun 1.2.0 keeps 0, 1.2.23–1.4.2 exit 1 "failed to resolve" on a root that does not depend on its members). Version-1/2 workspace locks are rewritten. Exit 0. | | `redirect_bun_lockb_invalid` | `redirect.warnings[]` (warning) | scan/get `--mode hosted`: the native binary lock is malformed, unreadable, unsupported or cannot be rewritten safely. No installer is spawned and no binary or sibling npm lock edit or takeover occurs; dry-run reports the same format error. Exit 0, `redirected: 0`. | | `redirect_bun_entry_not_found` / `redirect_bun_missing_sha512` | `redirect.warnings[]` (warning) | scan/get `--mode hosted` (bun): the lock has no rewritable entry at the granted version (re-resolved, or occupied by an unowned URL/file spec) / the grant carries no sha512 integrity. Per-dep; nothing rewritten for it; exit 0. NOT emitted for the digest-less 2-tuple Bun 1.1.39–1.3.9 re-save our URL tuple as — that entry counts as redirected and is healed. | +| `redirect_bun_patched_dependency_skipped` | `redirect.warnings[]` (warning) | scan/get `--mode hosted` (bun, `bun.lock` and `bun.lockb`): the project patches the granted `name@version` itself with `bun patch` (a `patchedDependencies` key for `name@version`, or the bare name, in the root `package.json` or mirrored in `bun.lock`). Bun applies that patch only to the registry resolution, so the entry is left on its registry tuple instead of silently losing the user's patch (#367). Per-dep; the detail names the key and the remedy (fold the Socket fix into the user's patch, or drop the `patchedDependencies` entry and re-run); the in-run VEX never assumes the uuid applied. Vendored mode refuses the same package `vendor_lock_entry_unsupported` before any write or download. Exit 0. | | `redirect_vlt_lock_unsupported` | `redirect.warnings[]` (warning) | scan/get `--mode hosted` (vlt): `vlt-lock.json` has a `lockfileVersion` other than absent, `0` or `1` (decided on the raw JSON token), is not a JSON object, starts with a UTF-8 BOM, or its `nodes` section is not vlt's one-node-per-line layout. Nothing rewritten; also refuses a vendored → hosted takeover of a `flavor: "vlt"` entry before its revert (`redirect.skipped[].reason`). Exit 0. | | `redirect_requirements_takeover_unreachable` | `redirect.warnings[]` (warning) | scan/get `--mode hosted` (pypi / requirements.txt): a vendored → hosted takeover of a package that vendored mode wired through a pin in a `-r` include, or through a `(transitive)` line it appended to the root `requirements.txt`. Hosted mode only rewrites an existing pin in the root `requirements.txt`, so the takeover is refused before the revert (wet and `--dry-run`): the vendored wiring, ledger entry and wheel stay byte-identical, the purl is skipped with this code as `redirect.skipped[].reason`, and nothing is redirected for it. Exit 0. The detail names the remedy and its reach: run `socket-patch vendor --revert` (it reverts EVERY vendored package in the project, not just this one), move the pin from the include into the root `requirements.txt` and delete it from the include (or, for a `(transitive)` line, add an exact `==` pin to the root file), then re-run `scan --mode hosted`. | | `redirect_vlt_missing_sha512` / `redirect_vlt_entry_not_found` / `redirect_vlt_entry_vendored` / `redirect_vlt_unsupported_lock_key` | `redirect.warnings[]` (warning) | scan/get `--mode hosted` (vlt): the grant has no sha512 / the lock has no default-registry node for `name@version` / the only match is a vendored `file` node under `.socket/vendor/npm//` / a default-registry instance is outside vlt's node-line grammar or still unpatched after the splice. Per dep; none of the dep's instances is written. `redirect_vlt_missing_sha512` and `redirect_vlt_unsupported_lock_key` refuse the dep: it is never confirmed, whichever lock drives (a sibling lock may still carry its rewritten URL). `redirect_vlt_entry_not_found` and `redirect_vlt_entry_vendored` only say `vlt-lock.json` does not wire it: while vlt drives it is not confirmed; otherwise a sibling lock's rules may confirm it. Exit 0. | diff --git a/crates/socket-patch-core/src/hosted/engine.rs b/crates/socket-patch-core/src/hosted/engine.rs index c197f36a9..656e1c557 100644 --- a/crates/socket-patch-core/src/hosted/engine.rs +++ b/crates/socket-patch-core/src/hosted/engine.rs @@ -500,6 +500,15 @@ pub async fn read_candidate_files( } } } + // The root manifest's `patchedDependencies` names the packages the + // project patches itself with `bun patch`, which the bun rewriters + // must leave on their registry tuple (#367). A text lock already + // read it above; a binary-only project reads it here, advisory too. + if !out.files.contains_key("package.json") && super::vlt::bun_lockb_present(view) { + if let Some(text) = read_advisory(view, unreadable, "package.json").await { + out.files.insert("package.json".to_string(), text); + } + } } // Cargo workspace members (and in-root path dependencies) declare @@ -987,7 +996,26 @@ pub async fn rewrite( .retain(|w| w.code != "redirect_npm_no_lockfile"); match content { Ok(bytes) => { - crate::patch::redirect::rewrite_bun_binary(&bytes, &overrides, &mut rewrite) + // A package the project patches itself (`bun patch`) keeps + // its registry record, loudly (#367). + let user_patched = crate::vendor::bun_lock_text::patched_dependency_keys( + files.get("package.json").map(String::as_str), + None, + ); + let binary_overrides: Vec = overrides + .iter() + .filter(|o| { + o.ecosystem != "npm" + || !crate::patch::redirect::skip_bun_user_patched( + &user_patched, + &crate::patch::redirect::full_name(o), + o, + &mut rewrite, + ) + }) + .cloned() + .collect(); + crate::patch::redirect::rewrite_bun_binary(&bytes, &binary_overrides, &mut rewrite) } Err(warning) => rewrite.warnings.push(warning), } @@ -2044,6 +2072,103 @@ mod tests { assert!(redirected(&done), "{:?}", done.rewrite.warnings); } + /// REGRESSION (#367), binary lock: a `bun.lockb`-only project's root + /// manifest is read for its `patchedDependencies`, and a package the + /// project patches itself with `bun patch` keeps its registry record, + /// loudly, and is never assumed patched by the in-run VEX. + #[tokio::test] + async fn issue_367_bun_lockb_keeps_a_user_patched_package_on_the_registry() { + use crate::patch::redirect::Integrity; + let fixture = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) + .join("tests/fixtures/bun-lockb-bundled/both"); + let candidates = vec![Candidate { + purl: "pkg:npm/is-number@7.0.0".into(), + dep: DepOverride { + ecosystem: "npm".into(), + name: "is-number".into(), + namespace: None, + version: "7.0.0".into(), + token: "tok".into(), + patch_uuid: "uuid".into(), + artifact_url: "https://patch.test/is-number-7.0.0.tgz".into(), + registry_override: None, + integrity: Integrity { + sha512: Some(format!("sha512-{}==", "A".repeat(86))), + ..Default::default() + }, + }, + }]; + let manifest = r#"{"name":"p","version":"1.0.0","dependencies":{"@bh/bund":"1.0.0","is-number":"7.0.0"}}"#; + let patched_manifest = manifest.replacen( + "}}", + r#"},"patchedDependencies":{"is-number@7.0.0":"patches/is-number@7.0.0.patch"}}"#, + 1, + ); + for user_patched in [false, true] { + let tmp = tempfile::tempdir().unwrap(); + std::fs::copy(fixture.join("bun.lockb"), tmp.path().join("bun.lockb")).unwrap(); + std::fs::write( + tmp.path().join("package.json"), + if user_patched { + &patched_manifest + } else { + manifest + }, + ) + .unwrap(); + let view = ProjectView::Disk(tmp.path()); + let outer = OuterAllowRemote::default; + let options = RewriteOptions { + dry_run: false, + targets_pipenv_lock: false, + pipenv_major: None, + pipenv_unknown_detail: String::new(), + trust_lockfile_config: true, + npm_allow_remote_config: true, + npm_outer: &outer, + blocking: false, + }; + let read = read_candidate_files(&view, &BTreeSet::new(), &candidates).await; + assert!(read.files.contains_key("package.json")); + let done = rewrite( + &view, + read, + &candidates, + BTreeMap::new(), + &BTreeSet::new(), + &[], + options, + ) + .await; + let skipped = done + .rewrite + .warnings + .iter() + .find(|w| w.code == "redirect_bun_patched_dependency_skipped"); + if user_patched { + assert!( + !done.rewrite.binary_files.contains_key("bun.lockb"), + "the user-patched record is left alone" + ); + let skipped = skipped.expect("the skip is reported"); + assert!( + skipped.detail.contains("is-number@7.0.0"), + "{}", + skipped.detail + ); + assert!(done.rewrite.bundled_skipped_uuids.contains("uuid")); + } else { + assert!( + done.rewrite.binary_files.contains_key("bun.lockb"), + "{:?}", + done.rewrite.warnings + ); + assert!(skipped.is_none(), "{:?}", done.rewrite.warnings); + } + assert!(!done.rewrite.files.contains_key("package.json")); + } + } + fn gem_candidate() -> Candidate { use crate::patch::redirect::{Integrity, RegistryOverride, RegistryOverrideIdentifiers}; Candidate { diff --git a/crates/socket-patch-core/src/patch/redirect/mod.rs b/crates/socket-patch-core/src/patch/redirect/mod.rs index 8cd5e6e26..a035b77c3 100644 --- a/crates/socket-patch-core/src/patch/redirect/mod.rs +++ b/crates/socket-patch-core/src/patch/redirect/mod.rs @@ -4317,6 +4317,29 @@ fn parse_bun_hosted_lock( Ok((lines, entries)) } +/// Leave `dep` on its registry resolution when the project's own +/// `patchedDependencies` patches it (#367): Bun applies that patch only to +/// the registry `name@version`, so a hosted pin would silently drop it from +/// every install. Warns, and keeps the in-run VEX from assuming the uuid +/// patched. `true` when `dep` was skipped. +pub(crate) fn skip_bun_user_patched( + user_patched: &[String], + name: &str, + dep: &DepOverride, + result: &mut RewriteResult, +) -> bool { + use crate::vendor::bun_lock_text::{patched_dependency_detail, patched_dependency_key}; + let Some(key) = patched_dependency_key(user_patched, name, &dep.version) else { + return false; + }; + result.bundled_skipped_uuids.insert(dep.patch_uuid.clone()); + result.warnings.push(RewriteWarning { + code: "redirect_bun_patched_dependency_skipped".into(), + detail: patched_dependency_detail(key, name, &dep.version), + }); + true +} + fn rewrite_bun_lock( files: &BTreeMap, overrides: &[DepOverride], @@ -4353,10 +4376,18 @@ fn rewrite_bun_lock( } }; + let user_patched = crate::vendor::bun_lock_text::patched_dependency_keys( + files.get("package.json").map(String::as_str), + Some(content), + ); + let mut changed = false; let mut pinned_any = false; for dep in &npm { let fname = full_name(dep); + if skip_bun_user_patched(&user_patched, &fname, dep, result) { + continue; + } let Some(sha512) = dep.integrity.sha512.clone() else { result.warnings.push(RewriteWarning { code: "redirect_bun_missing_sha512".into(), @@ -10175,6 +10206,73 @@ mod tests { ); } + /// REGRESSION (#367): `bun patch --commit` keys the project's own patch + /// on the registry `name@version` in package.json (and bun.lock's + /// mirror). Rewiring that package to a hosted URL makes Bun drop the + /// user's patch on every install with exit 0. The entry stays on its + /// registry tuple, the run says why, and VEX never assumes it patched; + /// another granted package in the same lock is still rewired. + #[test] + fn bun_lock_user_patched_dependency_is_left_alone_loudly() { + let sha512 = format!("sha512-{}==", "A".repeat(86)); + let ovr = npm_override("left-pad", "1.3.0", "http://p.test/lp.tgz", &sha512); + let mut other = npm_override("is-number", "7.0.0", "http://p.test/isn.tgz", &sha512); + other.patch_uuid = "22222222-2222-4222-8222-222222222222".into(); + let entries = "\"is-number\": [\"is-number@7.0.0\", \"\", {}, \"sha512-UP==\"],\n \ + \"left-pad\": [\"left-pad@1.3.0\", \"\", {}, \"sha512-OLD==\"],"; + let manifest = r#"{"name":"app","dependencies":{"left-pad":"1.3.0","is-number":"7.0.0"},"patchedDependencies":{"left-pad@1.3.0":"patches/left-pad@1.3.0.patch"}}"#; + let mirror = " \"patchedDependencies\": {\n \"left-pad@1.3.0\": \"patches/left-pad@1.3.0.patch\",\n },\n \"packages\": {"; + + // Each source alone triggers the gate: the manifest, or the lock's + // mirror of it (a lock-only read). + for (with_manifest, with_mirror) in [(true, false), (false, true), (true, true)] { + let mut lock = bun_lock_file(entries, 1); + if with_mirror { + lock = lock.replacen(" \"packages\": {", mirror, 1); + } + let mut files = BTreeMap::new(); + files.insert("bun.lock".to_string(), lock.clone()); + if with_manifest { + files.insert("package.json".to_string(), manifest.to_string()); + } + let mut r = RewriteResult::default(); + rewrite_bun_lock(&files, &[ovr.clone(), other.clone()], &mut r); + assert_eq!(r.edits.len(), 1, "{:?}", r.edits); + assert_eq!(r.edits[0].key.as_deref(), Some("is-number")); + let out = r.files.get("bun.lock").expect("is-number rewired"); + assert!( + out.contains("\"left-pad\": [\"left-pad@1.3.0\", \"\", {}, \"sha512-OLD==\"],"), + "the user-patched entry keeps its registry tuple: {out}" + ); + assert_eq!( + warning_codes(&r), + vec!["redirect_bun_patched_dependency_skipped"], + "{:?}", + r.warnings + ); + assert!( + r.warnings[0].detail.contains("left-pad@1.3.0") + && r.warnings[0].detail.contains("bun patch"), + "{}", + r.warnings[0].detail + ); + assert!(r.bundled_skipped_uuids.contains(&ovr.patch_uuid)); + assert!(!r.bundled_skipped_uuids.contains(&other.patch_uuid)); + } + + // A patch for ANOTHER version of the package does not gate this one. + let mut files = BTreeMap::new(); + files.insert("bun.lock".to_string(), bun_lock_file(entries, 1)); + files.insert( + "package.json".to_string(), + manifest.replace("left-pad@1.3.0\":", "left-pad@1.2.0\":"), + ); + let mut r = RewriteResult::default(); + rewrite_bun_lock(&files, std::slice::from_ref(&ovr), &mut r); + assert_eq!(r.edits.len(), 1, "{:?}", r.edits); + assert!(r.warnings.is_empty(), "{:?}", r.warnings); + } + /// A CRLF bun.lock (Windows `core.autocrlf` checkout) must keep CRLF on /// the REWRITTEN line too — the vendored engine already does — so the /// file never ends up mixed-EOL, and the ledger `new` fragment carries diff --git a/crates/socket-patch-core/src/vendor/bun_binary.rs b/crates/socket-patch-core/src/vendor/bun_binary.rs index 042563f76..955057779 100644 --- a/crates/socket-patch-core/src/vendor/bun_binary.rs +++ b/crates/socket-patch-core/src/vendor/bun_binary.rs @@ -270,6 +270,8 @@ pub(crate) async fn vendor( pub(super) struct BinaryProject { lock: BunLockb, packages: Vec, + /// The project's own `patchedDependencies` keys (#367). + user_patched: Vec, } /// Read the lock, refusing (before any write) a symlinked, unreadable, @@ -295,7 +297,12 @@ pub(super) async fn read_project(root: &Path) -> Result v, Err(e) => return Err(Box::new(refused("vendor_bun_lockb_invalid", e))), }; - Ok(BinaryProject { lock, packages }) + let user_patched = super::bun_lock::read_user_patched(root, None).await; + Ok(BinaryProject { + lock, + packages, + user_patched, + }) } /// What the per-package pre-flight hands the vendoring: the records to @@ -319,6 +326,7 @@ pub(super) fn preflight_package( coords: &NpmCoords, leaf: &str, ) -> Result> { + super::bun_lock::refuse_user_patched(&project.user_patched, &coords.name, &coords.version)?; let (bundled_only, matches): (Vec<_>, Vec<_>) = project .packages .iter() diff --git a/crates/socket-patch-core/src/vendor/bun_lock.rs b/crates/socket-patch-core/src/vendor/bun_lock.rs index 3d37d94f1..0233665f6 100644 --- a/crates/socket-patch-core/src/vendor/bun_lock.rs +++ b/crates/socket-patch-core/src/vendor/bun_lock.rs @@ -44,7 +44,8 @@ use crate::utils::fs::{atomic_write_bytes_preserving_mode, read_regular_to_strin use crate::utils::socket_dir::remove_tree_and_prune; use crate::vendor::bun_lock_text::{ decode_json_string, has_workspace_packages, is_bundled_entry, lock_version, packages_bounds, - parse_entry_line, split_name_spec, BunEntry, + parse_entry_line, patched_dependency_detail, patched_dependency_key, patched_dependency_keys, + split_name_spec, BunEntry, }; use super::common::{already_patched_result, refused}; @@ -667,6 +668,35 @@ pub(super) struct BunProject { lock_text: String, lines: Vec, entries: Vec, + /// The project's own `patchedDependencies` keys (#367). + user_patched: Vec, +} + +/// The root manifest's `patchedDependencies` keys, unioned with the copy +/// Bun mirrors into the text `lock` when there is one. An unreadable or +/// non-JSON manifest contributes none; the lock read stands on its own. +pub(super) async fn read_user_patched(project_root: &Path, lock: Option<&str>) -> Vec { + let manifest = read_regular_to_string(&project_root.join("package.json")) + .await + .ok(); + patched_dependency_keys(manifest.as_deref(), lock) +} + +/// Refuse to vendor a package the project patches itself with `bun patch` +/// (#367): Bun applies that patch only to the registry `name@version`, so +/// a local tarball tuple would silently drop it from every install. +pub(super) fn refuse_user_patched( + user_patched: &[String], + name: &str, + version: &str, +) -> Result<(), Box> { + match patched_dependency_key(user_patched, name, version) { + Some(key) => Err(Box::new(refused( + "vendor_lock_entry_unsupported", + patched_dependency_detail(key, name, version), + ))), + None => Ok(()), + } } /// Read the lock, refusing (before any write) one that is missing, @@ -698,10 +728,12 @@ pub(super) async fn read_project(project_root: &Path) -> Result Result<(String, String), Box> { + refuse_user_patched(&project.user_patched, name, version)?; let target_spec = format!("{name}@{version}"); let target_leaf = tgz_rel_leaf(name, version); let has_match = project @@ -2136,6 +2169,111 @@ mod tests { ); } + /// REGRESSION (#367): a package the project patches itself with + /// `bun patch` (package.json `patchedDependencies`, mirrored in + /// bun.lock) is keyed on its registry `name@version`; a local tarball + /// tuple would make Bun drop that patch from every install with exit + /// 0. Vendoring refuses before any write and names the key, from + /// either source, and the download plan's pre-flight agrees. + #[tokio::test] + async fn user_bun_patch_refuses_before_any_write() { + let key = "left-pad@1.3.0"; + let with_manifest_key = |manifest: &str| { + let mut value: Value = serde_json::from_str(manifest).unwrap(); + value["patchedDependencies"] = + serde_json::json!({ key: "patches/left-pad@1.3.0.patch" }); + serde_json::to_string_pretty(&value).unwrap() + }; + let mirrored_lock = BN3_BEFORE_LOCK.replacen( + " \"packages\": {", + " \"patchedDependencies\": {\n \"left-pad@1.3.0\": \"patches/left-pad@1.3.0.patch\",\n },\n \"packages\": {", + 1, + ); + assert_ne!(mirrored_lock, BN3_BEFORE_LOCK, "fixture has a packages section"); + + for (manifest_key, lock) in [ + (true, BN3_BEFORE_LOCK), + (false, mirrored_lock.as_str()), + (true, mirrored_lock.as_str()), + ] { + let fx = fixture_with(lock, "node_modules/left-pad").await; + if manifest_key { + tokio::fs::write(fx.root().join("package.json"), with_manifest_key(BN3_PKG)) + .await + .unwrap(); + } + let (planned, looped) = preflight_then_vendor(&fx).await; + assert_eq!(planned, Err("vendor_lock_entry_unsupported")); + assert_eq!(looped, Err("vendor_lock_entry_unsupported")); + let detail = expect_refused(fx.vendor(true).await, "vendor_lock_entry_unsupported"); + assert!( + detail.contains(key) && detail.contains("bun patch"), + "{detail}" + ); + assert_eq!(fx.read_lock().await, lock, "refusal writes nothing"); + assert!(!fx.root().join(".socket/vendor").exists()); + } + + // A patch for another version of the package does not gate this one. + let fx = fixture_with(BN3_BEFORE_LOCK, "node_modules/left-pad").await; + tokio::fs::write( + fx.root().join("package.json"), + with_manifest_key(BN3_PKG).replace(key, "left-pad@1.2.0"), + ) + .await + .unwrap(); + let (result, _, _) = expect_done(fx.vendor(false).await); + assert!(result.success, "{:?}", result.error); + } + + /// REGRESSION (#367), `bun.lockb`: the binary lock has no text mirror, + /// so the root manifest's `patchedDependencies` alone gates vendoring. + #[tokio::test] + async fn binary_user_bun_patch_refuses_before_any_write() { + let fx = fixture_with("", "node_modules/is-number").await; + let dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) + .join("tests/fixtures/bun-lockb-bundled/both"); + tokio::fs::remove_file(fx.root().join(BUN_LOCK)) + .await + .unwrap(); + tokio::fs::copy(dir.join("bun.lockb"), fx.root().join("bun.lockb")) + .await + .unwrap(); + tokio::fs::write( + fx.root().join("package.json"), + r#"{"name":"p","version":"1.0.0","dependencies":{"@bh/bund":"1.0.0","is-number":"7.0.0"},"patchedDependencies":{"is-number@7.0.0":"patches/is-number@7.0.0.patch"}}"#, + ) + .await + .unwrap(); + let before = tokio::fs::read(fx.root().join("bun.lockb")).await.unwrap(); + let packages = [("pkg:npm/is-number@7.0.0", &fx.record)]; + assert_eq!( + preflight_packages(fx.root(), &packages).await, + vec![Err("vendor_lock_entry_unsupported")] + ); + let blobs = fx.root().join(".socket/blobs"); + let outcome = crate::vendor::test_support::vendor_bun( + "pkg:npm/is-number@7.0.0", + &fx.installed, + fx.root(), + &fx.record, + &PatchSources::blobs_only(&blobs), + "2026-06-09T00:00:00Z", + false, + false, + None, + ) + .await; + let detail = expect_refused(outcome, "vendor_lock_entry_unsupported"); + assert!(detail.contains("is-number@7.0.0"), "{detail}"); + assert_eq!( + tokio::fs::read(fx.root().join("bun.lockb")).await.unwrap(), + before, + "refusal writes nothing" + ); + assert!(!fx.root().join(".socket/vendor").exists()); + } + #[tokio::test] async fn unparseable_entry_line_fails_closed_before_any_write() { for bad in [ diff --git a/crates/socket-patch-core/src/vendor/bun_lock_text.rs b/crates/socket-patch-core/src/vendor/bun_lock_text.rs index 321599b0b..349f2cc4f 100644 --- a/crates/socket-patch-core/src/vendor/bun_lock_text.rs +++ b/crates/socket-patch-core/src/vendor/bun_lock_text.rs @@ -22,6 +22,69 @@ /// construction. const SUPPORTED_LOCK_VERSIONS: [u64; 3] = [0, 1, 2]; +/// The `patchedDependencies` keys a Bun project declares: the root +/// `package.json`'s object, which `bun patch --commit` writes and every +/// install reads, plus the copy Bun mirrors at the top of a text `bun.lock` +/// (` "patchedDependencies": {` … ` },`, one `"key": "path"` line each). +/// Either source alone is enough: a key missing from one is still a patch +/// Bun applies. A manifest that is not JSON, or a lock section out of Bun's +/// emitted shape, contributes only the keys it spells plainly. +pub(crate) fn patched_dependency_keys(manifest: Option<&str>, lock: Option<&str>) -> Vec { + let mut keys: Vec = manifest + .and_then(|text| serde_json::from_str::(text).ok()) + .and_then(|value| match value.get("patchedDependencies") { + Some(serde_json::Value::Object(map)) => Some(map.keys().cloned().collect()), + _ => None, + }) + .unwrap_or_default(); + if let Some(lock) = lock { + let mut lines = lock + .split('\n') + .map(|l| l.strip_suffix('\r').unwrap_or(l)) + .skip_while(|l| *l != " \"patchedDependencies\": {") + .skip(1); + while let Some((4, key, _, _)) = lines.next().and_then(parse_string_pair_line) { + if !keys.contains(&key) { + keys.push(key); + } + } + } + keys +} + +/// The `patchedDependencies` key that makes Bun apply a project-authored +/// patch to `name@version`, if any. Bun keys the patch on the registry +/// resolution's `name@version`; a bare `name` is matched too so that a +/// key spelled without a version never slips past the gate. +/// +/// Bun applies such a patch only while the lock resolves the package to +/// that registry `name@version`. Rewiring it to a hosted URL or a vendored +/// tarball makes Bun drop the user's patch on every install, silently +/// (#367), so both modes leave such a package alone and say why. +pub(crate) fn patched_dependency_key<'k>( + keys: &'k [String], + name: &str, + version: &str, +) -> Option<&'k str> { + let spec = format!("{name}@{version}"); + keys.iter() + .map(String::as_str) + .find(|key| *key == spec || *key == name) +} + +/// The user-facing reason a package with a project-authored Bun patch is +/// left on its registry resolution, shared by the hosted and vendored +/// paths so the two modes never drift apart. +pub(crate) fn patched_dependency_detail(key: &str, name: &str, version: &str) -> String { + format!( + "package.json `patchedDependencies` has `{key}`, a patch the project applies with \ + `bun patch`; Bun applies it only to the registry {name}@{version}, so rewiring the \ + package would silently drop that patch from every install. It is left unchanged and \ + stays without the Socket patch: fold the Socket fix into your own patch, or remove \ + the `patchedDependencies` entry (`bun patch --commit` again without it) and re-run" + ) +} + /// One parsed single-line packages entry. pub(crate) struct BunEntry { pub(crate) line_idx: usize, @@ -526,6 +589,31 @@ pub(crate) fn heal_workspace_literals( mod tests { use super::*; + /// #367: the keys come from the manifest and from the lock's mirror, + /// and match the exact `name@version` (scoped too) or a bare name. + #[test] + fn patched_dependency_keys_read_manifest_and_lock() { + let manifest = r#"{"name":"app","patchedDependencies":{"left-pad@1.3.0":"patches/left-pad@1.3.0.patch"}}"#; + let lock = "{\r\n \"lockfileVersion\": 1,\r\n \"patchedDependencies\": {\r\n \"@s/p@2.0.0\": \"patches/@s%2Fp@2.0.0.patch\",\r\n \"left-pad@1.3.0\": \"patches/left-pad@1.3.0.patch\",\r\n },\r\n \"packages\": {\r\n \"x@1.0.0\": \"not a key\",\r\n }\r\n}\r\n"; + let keys = patched_dependency_keys(Some(manifest), Some(lock)); + assert_eq!(keys, vec!["left-pad@1.3.0", "@s/p@2.0.0"]); + assert_eq!( + patched_dependency_key(&keys, "left-pad", "1.3.0"), + Some("left-pad@1.3.0") + ); + assert_eq!(patched_dependency_key(&keys, "@s/p", "2.0.0"), Some("@s/p@2.0.0")); + assert_eq!(patched_dependency_key(&keys, "left-pad", "1.3.1"), None); + assert_eq!(patched_dependency_key(&keys, "x", "1.0.0"), None); + let bare = vec!["left-pad".to_string()]; + assert_eq!( + patched_dependency_key(&bare, "left-pad", "1.3.0"), + Some("left-pad") + ); + assert!(patched_dependency_keys(Some("not json"), None).is_empty()); + assert!(patched_dependency_keys(Some(r#"{"patchedDependencies":[]}"#), None).is_empty()); + assert!(patched_dependency_keys(None, None).is_empty()); + } + /// The `bundled` meta flag, in the shapes real Bun 1.3.14 writes, the /// rewritten tarball tuple, and a meta that is not plain JSON. #[test] diff --git a/docs/testing/bun-compatibility.md b/docs/testing/bun-compatibility.md index e046935b1..b98a5cec4 100644 --- a/docs/testing/bun-compatibility.md +++ b/docs/testing/bun-compatibility.md @@ -43,6 +43,7 @@ other npm lockfile flavors. | Version-1 lock (Bun 1.2–1.3 default) with `workspace:` packages — 1-tuple entries `["consumer@workspace:packages/consumer"]` | Rewritten (golden `lock-v1-workspace`; matrix 1.2.0–1.3.14 `workspace` / `workspace-nested`). | Refused `vendor_bun_workspace_unsupported` before any write. Policy, not a grammar limit: Bun 1.2.x–1.3.x resolve a workspace member's local-tarball path relative to the MEMBER (our root-relative tuple ENOENTs on `bun install`), 1.4.x relative to the lockfile, and a committed lockfileVersion-2 lock is the only proof that every consumer runs Bun ≥ 1.4 (1.3.x cannot parse v2). A deliberate over-approximation: a package declared only by the workspace ROOT vendors and installs on v1 too, but the lock cannot cheaply prove which workspace declares a hoisted entry. Remedy in the detail: delete `bun.lock`, re-run `bun install` with Bun ≥ 1.4 (an in-place `bun install` keeps the existing version), or — version 1 — use `--mode hosted`, which accepts version-1 workspace locks (a version-0 lock is told to re-lock with Bun ≥ 1.2 first). NOT refused: purls the vendor ledger wires at the selected uuid, purls whose every matching lock tuple already points into `.socket/vendor/npm/` (any uuid — a superseding patch re-pins in place; the lock-derived rule the engine uses), in-sync re-runs and `repair` rebuilds. `vendor` and the vendor step run the same preflight BEFORE a hosted → vendored takeover's revert, so a hosted-redirected purl on such a lock stays hosted-patched (`failed vendor_bun_workspace_unsupported`, lock and ledgers untouched; `vendor --dry-run` previews the same code). A `.socket/vendor/state.json` the preflight cannot read is `vendor_state_unreadable`, fail-closed. | Works. | | Version-2 lock (Bun 1.4+) with `workspace:` packages, nested versions included | Rewritten (golden `lock-v2-workspace-nested` — provenance: its nested same-version `consumer/left-pad` entry is a synthetic, grammar-valid extension of the 1.4.2 capture; bun hoists identical resolutions and never writes that entry itself, but bun 1.4.2 installs the fixture unchanged, and it is the only case pinning the rewrite of every matching tuple in one lock). | Vendored (matrix 1.4.0 / 1.4.2 `workspace`, `workspace-nested`, `already-vendored-workspace`). | Works. | | Binary `bun.lockb` (binary format revisions 1, 2 and 3) | Package resolution and integrity records are rewritten in place. The CLI does not spawn Bun or produce a text lock. `rollback` / `remove` cannot restore a hosted `bun.lockb` entry to its upstream registry entry (v5.0 keeps no ledger to replay, and the binary lock is not re-derived), so they refuse it with the `git checkout -- bun.lockb` remedy. | Native local-tarball wiring, committed artifact, repair and vendored → hosted takeover. Hosted → vendored rebuilds a hosted `bun.lockb` pin's npm registry record from the registry (byte-exact for a lock socket-patch wired hosted), then vendors; `vendor --revert` returns the pre-hosted lock. Offline it refuses (`redirect_revert_failed`), leaving it hosted. | Registry package records are inventoried directly, including lockfile-only projects without `node_modules`. | +| A package the project patches itself with `bun patch` (a `patchedDependencies` key for its `name@version`, or its bare name, in the root `package.json` or mirrored in `bun.lock`) (#367) | Left on its registry tuple (text and binary lock): Bun applies the user's patch only to the registry `name@version`, so a hosted URL would drop it from every install with exit 0. Warns `redirect_bun_patched_dependency_skipped` naming the key, and the in-run VEX never assumes the patch applied; other packages in the lock are still rewired. | Refused `vendor_lock_entry_unsupported` before any write or download (text and binary lock), naming the key. | The installed tree is patched in place, as for any package. | | Truncated, corrupt or unrecognized binary `bun.lockb` | Refused with `redirect_bun_lockb_invalid`, preserving the lock. | Refused with `vendor_bun_lockb_invalid` before downloads or artifact creation. | The inventory reports the malformed lock. | | `bun.lock` with a `lockfileVersion` ≥ 3, no integer version, or a `packages` section outside bun's single-line grammar | Refused `redirect_bun_lock_unsupported`. | Refused `vendor_lockfile_version_unsupported` (preflight and engine). | The inventory skips the lock. | From ea5f0944da525adf8b16123fe406e891d452aeb7 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 5 Oct 2026 16:57:20 +0000 Subject: [PATCH 3/8] Format the new Bun patch tests Assisted-by: Claude Code:claude-opus-5-5 --- crates/socket-patch-core/src/vendor/bun_lock.rs | 5 ++++- crates/socket-patch-core/src/vendor/bun_lock_text.rs | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/crates/socket-patch-core/src/vendor/bun_lock.rs b/crates/socket-patch-core/src/vendor/bun_lock.rs index 0233665f6..29ffca204 100644 --- a/crates/socket-patch-core/src/vendor/bun_lock.rs +++ b/crates/socket-patch-core/src/vendor/bun_lock.rs @@ -2189,7 +2189,10 @@ mod tests { " \"patchedDependencies\": {\n \"left-pad@1.3.0\": \"patches/left-pad@1.3.0.patch\",\n },\n \"packages\": {", 1, ); - assert_ne!(mirrored_lock, BN3_BEFORE_LOCK, "fixture has a packages section"); + assert_ne!( + mirrored_lock, BN3_BEFORE_LOCK, + "fixture has a packages section" + ); for (manifest_key, lock) in [ (true, BN3_BEFORE_LOCK), diff --git a/crates/socket-patch-core/src/vendor/bun_lock_text.rs b/crates/socket-patch-core/src/vendor/bun_lock_text.rs index 349f2cc4f..c6c9ce28a 100644 --- a/crates/socket-patch-core/src/vendor/bun_lock_text.rs +++ b/crates/socket-patch-core/src/vendor/bun_lock_text.rs @@ -601,7 +601,10 @@ mod tests { patched_dependency_key(&keys, "left-pad", "1.3.0"), Some("left-pad@1.3.0") ); - assert_eq!(patched_dependency_key(&keys, "@s/p", "2.0.0"), Some("@s/p@2.0.0")); + assert_eq!( + patched_dependency_key(&keys, "@s/p", "2.0.0"), + Some("@s/p@2.0.0") + ); assert_eq!(patched_dependency_key(&keys, "left-pad", "1.3.1"), None); assert_eq!(patched_dependency_key(&keys, "x", "1.0.0"), None); let bare = vec!["left-pad".to_string()]; From 71c04211ef063f13407c07e0682f903b7869fc08 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 5 Oct 2026 17:43:31 +0000 Subject: [PATCH 4/8] Never confirm a Bun package the user patched Bugbot review of #873 found two gaps in the bun patch guard. A text bun.lock only reached the root package.json through its workspaces section, so a lock without one never saw the patchedDependencies keys and rewired the package anyway. The manifest is now read beside either Bun lock. A package left on the registry could still be counted as switched when a sibling package-lock.json took the hosted URL, although Bun keeps installing the registry bytes. Such uuids are now recorded as refused and never confirmed. Refs #367 Assisted-by: Claude Code:claude-opus-5-5 --- crates/socket-patch-core/src/hosted/engine.rs | 62 +++++++++++++++++-- .../src/patch/redirect/mod.rs | 13 +++- 2 files changed, 69 insertions(+), 6 deletions(-) diff --git a/crates/socket-patch-core/src/hosted/engine.rs b/crates/socket-patch-core/src/hosted/engine.rs index 656e1c557..493986e3d 100644 --- a/crates/socket-patch-core/src/hosted/engine.rs +++ b/crates/socket-patch-core/src/hosted/engine.rs @@ -502,9 +502,12 @@ pub async fn read_candidate_files( } // The root manifest's `patchedDependencies` names the packages the // project patches itself with `bun patch`, which the bun rewriters - // must leave on their registry tuple (#367). A text lock already - // read it above; a binary-only project reads it here, advisory too. - if !out.files.contains_key("package.json") && super::vlt::bun_lockb_present(view) { + // must leave on their registry tuple (#367). Read beside either bun + // lock, advisory too: the member walk above reaches the root only + // through a `workspaces` section in bun's emitted shape. + if !out.files.contains_key("package.json") + && (out.files.contains_key("bun.lock") || super::vlt::bun_lockb_present(view)) + { if let Some(text) = read_advisory(view, unreadable, "package.json").await { out.files.insert("package.json".to_string(), text); } @@ -1494,7 +1497,8 @@ fn confirm( let uuid = c.dep.patch_uuid.as_str(); // vlt decides before the binary-bun rule, so `bun.lockb` beside // a vlt-driven `vlt-lock.json` never confirms an npm purl. - if rewrite.refused_vlt_uuids.contains(uuid) { + if rewrite.refused_vlt_uuids.contains(uuid) || rewrite.refused_bun_uuids.contains(uuid) + { return ProbeStep::Decided(false); } if rewrite.vlt_drives && purl.starts_with("pkg:npm/") { @@ -2169,6 +2173,56 @@ mod tests { } } + /// REGRESSION (#367), text lock: the root manifest is read beside a + /// `bun.lock` even when the lock has no `workspaces` section to reach it + /// through, and a package the project patches itself is never + /// confirmed, not even when a sibling `package-lock.json` takes the + /// hosted URL: Bun keeps installing the registry bytes. + #[tokio::test] + async fn issue_367_bun_lock_user_patched_package_is_never_confirmed() { + let bun_lock = "{\n \"lockfileVersion\": 1,\n \"packages\": {\n \"left-pad\": \ + [\"left-pad@1.3.0\", \"\", {}, \"sha512-UPSTREAM==\"],\n }\n}\n"; + let npm_lock = r#"{ + "name": "app", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { "name": "app", "dependencies": { "left-pad": "1.3.0" } }, + "node_modules/left-pad": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", + "integrity": "sha512-UPSTREAM==" + } + } +} +"#; + let manifest = r#"{"name":"app","dependencies":{"left-pad":"1.3.0"},"patchedDependencies":{"left-pad@1.3.0":"patches/left-pad@1.3.0.patch"}}"#; + // Without the sibling npm lock nothing else reads the manifest. + for with_npm_lock in [false, true] { + let tmp = tempfile::tempdir().unwrap(); + std::fs::write(tmp.path().join("bun.lock"), bun_lock).unwrap(); + if with_npm_lock { + std::fs::write(tmp.path().join("package-lock.json"), npm_lock).unwrap(); + } + std::fs::write(tmp.path().join("package.json"), manifest).unwrap(); + let (read, done) = npm_rewrite(&ProjectView::Disk(tmp.path()), &BTreeSet::new()).await; + assert!(read.files.contains_key("package.json")); + assert!( + !done.rewrite.files.contains_key("bun.lock"), + "the user-patched entry keeps its registry tuple" + ); + assert!( + done.rewrite + .warnings + .iter() + .any(|w| w.code == "redirect_bun_patched_dependency_skipped"), + "{:?}", + done.rewrite.warnings + ); + assert!(done.confirmed.is_empty(), "{:?}", done.confirmed); + } + } + fn gem_candidate() -> Candidate { use crate::patch::redirect::{Integrity, RegistryOverride, RegistryOverrideIdentifiers}; Candidate { diff --git a/crates/socket-patch-core/src/patch/redirect/mod.rs b/crates/socket-patch-core/src/patch/redirect/mod.rs index a035b77c3..2a461d2da 100644 --- a/crates/socket-patch-core/src/patch/redirect/mod.rs +++ b/crates/socket-patch-core/src/patch/redirect/mod.rs @@ -242,6 +242,11 @@ pub struct RewriteResult { /// An incomplete pnpm rewrite must not be confirmed by finding its URL /// in another instance, a comment, or another lockfile. pub refused_pnpm_uuids: std::collections::BTreeSet, + /// Patch uuids the bun rewriters left on their registry entry because + /// the project patches that package itself with `bun patch` (#367). + /// Never confirmed, not even by the URL landing in a sibling npm-family + /// lock: Bun keeps installing the registry bytes. + pub refused_bun_uuids: std::collections::BTreeSet, /// Patch uuids whose package version a yarn berry `yarn.lock` locks, so /// the berry rewriter alone decides them: the hosted pin is the /// URL-keyed lock entry AND the root `package.json` `resolutions` @@ -564,6 +569,7 @@ fn merge_group_delta(result: &mut RewriteResult, delta: RewriteResult) { confirmed_pdm_uuids, refused_pdm_uuids, refused_pnpm_uuids, + refused_bun_uuids, yarn_berry_uuids, confirmed_yarn_berry_uuids, python_lock_uuids, @@ -592,6 +598,7 @@ fn merge_group_delta(result: &mut RewriteResult, delta: RewriteResult) { result.confirmed_pdm_uuids.extend(confirmed_pdm_uuids); result.refused_pdm_uuids.extend(refused_pdm_uuids); result.refused_pnpm_uuids.extend(refused_pnpm_uuids); + result.refused_bun_uuids.extend(refused_bun_uuids); result.yarn_berry_uuids.extend(yarn_berry_uuids); result .confirmed_yarn_berry_uuids @@ -4320,8 +4327,9 @@ fn parse_bun_hosted_lock( /// Leave `dep` on its registry resolution when the project's own /// `patchedDependencies` patches it (#367): Bun applies that patch only to /// the registry `name@version`, so a hosted pin would silently drop it from -/// every install. Warns, and keeps the in-run VEX from assuming the uuid -/// patched. `true` when `dep` was skipped. +/// every install. Warns, keeps the in-run VEX from assuming the uuid +/// patched, and keeps any other lock from confirming it. `true` when `dep` +/// was skipped. pub(crate) fn skip_bun_user_patched( user_patched: &[String], name: &str, @@ -4333,6 +4341,7 @@ pub(crate) fn skip_bun_user_patched( return false; }; result.bundled_skipped_uuids.insert(dep.patch_uuid.clone()); + result.refused_bun_uuids.insert(dep.patch_uuid.clone()); result.warnings.push(RewriteWarning { code: "redirect_bun_patched_dependency_skipped".into(), detail: patched_dependency_detail(key, name, &dep.version), From e5c0c356db0531305022282eefd38ab4b063f7a3 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 5 Oct 2026 18:01:39 +0000 Subject: [PATCH 5/8] Keep rewrite goldens stable with the new field The refused-Bun uuid set added to RewriteResult changed the serialized and Debug output that the redirect equivalence goldens hash. The set is now left out of serialization when empty, like the other per-ecosystem uuid sets, and the two goldens that hash the Debug output (poetry, pdm) are re-blessed. Only their output digests change; every case and input digest is identical. Refs #367 Assisted-by: Claude Code:claude-opus-5-5 --- .../src/patch/redirect/mod.rs | 4 + .../pdm_rewrite_shared_parse.golden | 240 +++++++++--------- .../tests/equivalence/poetry_rewrite.golden | 240 +++++++++--------- 3 files changed, 244 insertions(+), 240 deletions(-) diff --git a/crates/socket-patch-core/src/patch/redirect/mod.rs b/crates/socket-patch-core/src/patch/redirect/mod.rs index 2a461d2da..a0eddf480 100644 --- a/crates/socket-patch-core/src/patch/redirect/mod.rs +++ b/crates/socket-patch-core/src/patch/redirect/mod.rs @@ -246,6 +246,10 @@ pub struct RewriteResult { /// the project patches that package itself with `bun patch` (#367). /// Never confirmed, not even by the URL landing in a sibling npm-family /// lock: Bun keeps installing the registry bytes. + #[cfg_attr( + test, + serde(skip_serializing_if = "std::collections::BTreeSet::is_empty") + )] pub refused_bun_uuids: std::collections::BTreeSet, /// Patch uuids whose package version a yarn berry `yarn.lock` locks, so /// the berry rewriter alone decides them: the hosted pin is the diff --git a/crates/socket-patch-core/tests/equivalence/pdm_rewrite_shared_parse.golden b/crates/socket-patch-core/tests/equivalence/pdm_rewrite_shared_parse.golden index 54dc20d26..580a7f58a 100644 --- a/crates/socket-patch-core/tests/equivalence/pdm_rewrite_shared_parse.golden +++ b/crates/socket-patch-core/tests/equivalence/pdm_rewrite_shared_parse.golden @@ -1,122 +1,122 @@ # One grown pdm.lock and its url deps, rewritten, then re-run over the result. # -0.12.3-extras.lock/extra=0/crlf=false 21655a1176272fba 9cbade162505fee0 -0.12.3-extras.lock/extra=0/crlf=false/re-run 9991f30c983a9730 793457405e333ba8 -0.12.3-extras.lock/extra=0/crlf=true 5900533590840a46 ce544a091f71af00 -0.12.3-extras.lock/extra=0/crlf=true/re-run 881424448f72e65f df78790d27b111a1 -0.12.3-extras.lock/extra=3/crlf=false ce9fcf172c1ebe53 dd8e834970a168ec -0.12.3-extras.lock/extra=3/crlf=false/re-run 3255b7f165a3f1d3 66ca3eba7984e3bb -0.12.3-extras.lock/extra=3/crlf=true edf41cc43e312ff7 43108a254ca1e83f -0.12.3-extras.lock/extra=3/crlf=true/re-run 2c3c4e5915d2acb2 d61c62092a306efb -0.12.3.lock/extra=0/crlf=false bb6882fa6e308227 907d1d1a73b7170e -0.12.3.lock/extra=0/crlf=false/re-run 4968389f75327559 bd1c3d37def8de8b -0.12.3.lock/extra=0/crlf=true 28fa02d135110df5 f0e499e7ac078918 -0.12.3.lock/extra=0/crlf=true/re-run d4862ba4b6fc43c9 ba07ae39513e0d51 -0.12.3.lock/extra=3/crlf=false 875655d84a273615 813ebc475d3f19c3 -0.12.3.lock/extra=3/crlf=false/re-run 3c2b2fe1fa3f2817 ad97f3348557350e -0.12.3.lock/extra=3/crlf=true 5b7bdf5d23ba4480 30833127c2300a6d -0.12.3.lock/extra=3/crlf=true/re-run b6e20136704fd19e 05591671f368bfdf -1.15.5.lock/extra=0/crlf=false 4d4f13130b2f5ca6 e864f4448fd41f03 -1.15.5.lock/extra=0/crlf=false/re-run 4d4f13130b2f5ca6 e864f4448fd41f03 -1.15.5.lock/extra=0/crlf=true 738ea560b4bac5e9 e864f4448fd41f03 -1.15.5.lock/extra=0/crlf=true/re-run 738ea560b4bac5e9 e864f4448fd41f03 -1.15.5.lock/extra=3/crlf=false 05076616697b9f90 95c7decb7f8a952c -1.15.5.lock/extra=3/crlf=false/re-run 05076616697b9f90 95c7decb7f8a952c -1.15.5.lock/extra=3/crlf=true 61a0a8be9a863e05 95c7decb7f8a952c -1.15.5.lock/extra=3/crlf=true/re-run 61a0a8be9a863e05 95c7decb7f8a952c -2.0.3.lock/extra=0/crlf=false f85fd6eef847aaaf 600061fab0474509 -2.0.3.lock/extra=0/crlf=false/re-run f85fd6eef847aaaf 600061fab0474509 -2.0.3.lock/extra=0/crlf=true 5d41e681e269208b 600061fab0474509 -2.0.3.lock/extra=0/crlf=true/re-run 5d41e681e269208b 600061fab0474509 -2.0.3.lock/extra=3/crlf=false 4d3cfc88ac5ffece 0108056ab6bb4a43 -2.0.3.lock/extra=3/crlf=false/re-run 4d3cfc88ac5ffece 0108056ab6bb4a43 -2.0.3.lock/extra=3/crlf=true 337957cda423fdbf 0108056ab6bb4a43 -2.0.3.lock/extra=3/crlf=true/re-run 337957cda423fdbf 0108056ab6bb4a43 -2.1.5.lock/extra=0/crlf=false f85fd6eef847aaaf 600061fab0474509 -2.1.5.lock/extra=0/crlf=false/re-run f85fd6eef847aaaf 600061fab0474509 -2.1.5.lock/extra=0/crlf=true 5d41e681e269208b 600061fab0474509 -2.1.5.lock/extra=0/crlf=true/re-run 5d41e681e269208b 600061fab0474509 -2.1.5.lock/extra=3/crlf=false 4d3cfc88ac5ffece 0108056ab6bb4a43 -2.1.5.lock/extra=3/crlf=false/re-run 4d3cfc88ac5ffece 0108056ab6bb4a43 -2.1.5.lock/extra=3/crlf=true 337957cda423fdbf 0108056ab6bb4a43 -2.1.5.lock/extra=3/crlf=true/re-run 337957cda423fdbf 0108056ab6bb4a43 -2.10.4.lock/extra=0/crlf=false 172e58243f676d64 cf4fe3ef38b2f0f8 -2.10.4.lock/extra=0/crlf=false/re-run 52c4734e1adb2848 ba04212889eba2a3 -2.10.4.lock/extra=0/crlf=true 84b3284978be45b7 cd0d59d5019a1114 -2.10.4.lock/extra=0/crlf=true/re-run 7d030b203da8acd0 02655f74431f4865 -2.10.4.lock/extra=3/crlf=false 9c9557ecc3467229 3a5527da132b8ad4 -2.10.4.lock/extra=3/crlf=false/re-run cc827f83fff579f2 31c0381ce0317801 -2.10.4.lock/extra=3/crlf=true 001be1702fcbb10b 281f50565f77e7d1 -2.10.4.lock/extra=3/crlf=true/re-run e1337b373345aad8 3a5d80c8a0c46bd8 -2.11.2.lock/extra=0/crlf=false 6b4f6ea534403ce1 9c28babcbeafbc8e -2.11.2.lock/extra=0/crlf=false/re-run 02e7df4fcb62c310 f65da9dde35c9f79 -2.11.2.lock/extra=0/crlf=true c626206c4024fe46 334732622c8a9273 -2.11.2.lock/extra=0/crlf=true/re-run 87f02f8297dce1ec 6fc0547d806983e7 -2.11.2.lock/extra=3/crlf=false 71024d43303c242f 738f1863c5b58e79 -2.11.2.lock/extra=3/crlf=false/re-run 4c92b86de4ae77eb 1a1c19b5e80789dd -2.11.2.lock/extra=3/crlf=true 986a91c5cd4511b7 7d8dc7df8a5aa93b -2.11.2.lock/extra=3/crlf=true/re-run 601377db18458ff3 4be4fac4e84c75b9 -2.17.3.lock/extra=0/crlf=false 98c5c38de2f9a8e1 230860f727a697bd -2.17.3.lock/extra=0/crlf=false/re-run bea465fa3cc73663 f65da9dde35c9f79 -2.17.3.lock/extra=0/crlf=true a0c9bb9ed37191ef 863b4d9a0bc81e57 -2.17.3.lock/extra=0/crlf=true/re-run ea711090534018d5 6fc0547d806983e7 -2.17.3.lock/extra=3/crlf=false 093ae70e2482288c b558315264903729 -2.17.3.lock/extra=3/crlf=false/re-run b73d20dc2ea08fa5 1a1c19b5e80789dd -2.17.3.lock/extra=3/crlf=true d21eb6a0c09c524c 2ec3501dcb2859c4 -2.17.3.lock/extra=3/crlf=true/re-run 15971e9f41f1f52e 4be4fac4e84c75b9 -2.29.2-extras.lock/extra=0/crlf=false eb78aecb54bd098d bd5d41ea51501dc4 -2.29.2-extras.lock/extra=0/crlf=false/re-run 42d93b5c405df78d 3e84ffdf57183ce7 -2.29.2-extras.lock/extra=0/crlf=true a74183ac5e064afc cc0ba95a1a5f823f -2.29.2-extras.lock/extra=0/crlf=true/re-run 9c29d9c8a18cf39b fbbd77f8b3c5d5c3 -2.29.2-extras.lock/extra=3/crlf=false 6485ead297972881 1cdf42341d9ffa61 -2.29.2-extras.lock/extra=3/crlf=false/re-run a522a0262db8df40 de8ce75171e02cd0 -2.29.2-extras.lock/extra=3/crlf=true 53718730bd1cda51 b70c52ac1129c2a2 -2.29.2-extras.lock/extra=3/crlf=true/re-run 8b5ab3312a7b27a3 9c153f7662382567 -2.29.2.lock/extra=0/crlf=false 0afc5c9cc3e3929d cdcfdda0098db93c -2.29.2.lock/extra=0/crlf=false/re-run 95943761aa62a765 f65da9dde35c9f79 -2.29.2.lock/extra=0/crlf=true 36eebecfb1bfccd1 d88cc108b1d7abff -2.29.2.lock/extra=0/crlf=true/re-run 02f20956ed8b4483 6fc0547d806983e7 -2.29.2.lock/extra=3/crlf=false ef194e4c3f23be69 53524501c474be0e -2.29.2.lock/extra=3/crlf=false/re-run 771462e9c51554c5 1a1c19b5e80789dd -2.29.2.lock/extra=3/crlf=true cbaa4943320bfe44 9d3349ed847e8d43 -2.29.2.lock/extra=3/crlf=true/re-run 35b9ad6159d5458d 4be4fac4e84c75b9 -2.3.4.lock/extra=0/crlf=false 1edd8acc82a5ddcc 8a412225fe7cfcbb -2.3.4.lock/extra=0/crlf=false/re-run 1edd8acc82a5ddcc 8a412225fe7cfcbb -2.3.4.lock/extra=0/crlf=true 817bd86e9a710e96 8a412225fe7cfcbb -2.3.4.lock/extra=0/crlf=true/re-run 817bd86e9a710e96 8a412225fe7cfcbb -2.3.4.lock/extra=3/crlf=false 26d0cc1acb4d1e66 5749237f48a6a1bf -2.3.4.lock/extra=3/crlf=false/re-run 26d0cc1acb4d1e66 5749237f48a6a1bf -2.3.4.lock/extra=3/crlf=true 9e6c6abc2b3b36da 5749237f48a6a1bf -2.3.4.lock/extra=3/crlf=true/re-run 9e6c6abc2b3b36da 5749237f48a6a1bf -2.6.1.lock/extra=0/crlf=false a1cf90463aec548e 28f73c0383eb31fe -2.6.1.lock/extra=0/crlf=false/re-run a1cf90463aec548e 28f73c0383eb31fe -2.6.1.lock/extra=0/crlf=true b4f2fc1d2ad04aff 28f73c0383eb31fe -2.6.1.lock/extra=0/crlf=true/re-run b4f2fc1d2ad04aff 28f73c0383eb31fe -2.6.1.lock/extra=3/crlf=false 0a39d27410f0a359 72ad6b5da9737ced -2.6.1.lock/extra=3/crlf=false/re-run 0a39d27410f0a359 72ad6b5da9737ced -2.6.1.lock/extra=3/crlf=true 028a8e1f1bf050a5 72ad6b5da9737ced -2.6.1.lock/extra=3/crlf=true/re-run 028a8e1f1bf050a5 72ad6b5da9737ced -2.7.4.lock/extra=0/crlf=false a1cf90463aec548e 28f73c0383eb31fe -2.7.4.lock/extra=0/crlf=false/re-run a1cf90463aec548e 28f73c0383eb31fe -2.7.4.lock/extra=0/crlf=true b4f2fc1d2ad04aff 28f73c0383eb31fe -2.7.4.lock/extra=0/crlf=true/re-run b4f2fc1d2ad04aff 28f73c0383eb31fe -2.7.4.lock/extra=3/crlf=false 0a39d27410f0a359 72ad6b5da9737ced -2.7.4.lock/extra=3/crlf=false/re-run 0a39d27410f0a359 72ad6b5da9737ced -2.7.4.lock/extra=3/crlf=true 028a8e1f1bf050a5 72ad6b5da9737ced -2.7.4.lock/extra=3/crlf=true/re-run 028a8e1f1bf050a5 72ad6b5da9737ced -2.8.2.lock/extra=0/crlf=false 55e31f21f6b597b6 91548502bf4e668e -2.8.2.lock/extra=0/crlf=false/re-run bdf788b9e0b21711 0a1fbbbab38f7296 -2.8.2.lock/extra=0/crlf=true 8860e7f81b315e97 b12ae756a79e4638 -2.8.2.lock/extra=0/crlf=true/re-run 152ed911da843927 eaa7e3d1f60e017e -2.8.2.lock/extra=3/crlf=false aa2044473027360e ee6ef491a5760fd3 -2.8.2.lock/extra=3/crlf=false/re-run a0c3c1cf672c0825 8ebc8eda8ae4dfe8 -2.8.2.lock/extra=3/crlf=true 59da76de16052042 627a70d61084d0ed -2.8.2.lock/extra=3/crlf=true/re-run 37089aac3445a20c 42e8dda85bf7f9a3 -2.9.3.lock/extra=0/crlf=false 55e31f21f6b597b6 91548502bf4e668e -2.9.3.lock/extra=0/crlf=false/re-run bdf788b9e0b21711 0a1fbbbab38f7296 -2.9.3.lock/extra=0/crlf=true 8860e7f81b315e97 b12ae756a79e4638 -2.9.3.lock/extra=0/crlf=true/re-run 152ed911da843927 eaa7e3d1f60e017e -2.9.3.lock/extra=3/crlf=false aa2044473027360e ee6ef491a5760fd3 -2.9.3.lock/extra=3/crlf=false/re-run a0c3c1cf672c0825 8ebc8eda8ae4dfe8 -2.9.3.lock/extra=3/crlf=true 59da76de16052042 627a70d61084d0ed -2.9.3.lock/extra=3/crlf=true/re-run 37089aac3445a20c 42e8dda85bf7f9a3 +0.12.3-extras.lock/extra=0/crlf=false 21655a1176272fba 8c380d62328d84b6 +0.12.3-extras.lock/extra=0/crlf=false/re-run 9991f30c983a9730 55dca41fbf6ebfe9 +0.12.3-extras.lock/extra=0/crlf=true 5900533590840a46 024fc228ccf1ff6a +0.12.3-extras.lock/extra=0/crlf=true/re-run 881424448f72e65f 4e492789deda30f5 +0.12.3-extras.lock/extra=3/crlf=false ce9fcf172c1ebe53 0446fdb71991e924 +0.12.3-extras.lock/extra=3/crlf=false/re-run 3255b7f165a3f1d3 895dc5fd2dc2a12a +0.12.3-extras.lock/extra=3/crlf=true edf41cc43e312ff7 0e748e2889d2b5b7 +0.12.3-extras.lock/extra=3/crlf=true/re-run 2c3c4e5915d2acb2 b73e4a652d83ac63 +0.12.3.lock/extra=0/crlf=false bb6882fa6e308227 e454101ed733181b +0.12.3.lock/extra=0/crlf=false/re-run 4968389f75327559 9b2f687c629bd0c4 +0.12.3.lock/extra=0/crlf=true 28fa02d135110df5 e2eb2dbc79333c15 +0.12.3.lock/extra=0/crlf=true/re-run d4862ba4b6fc43c9 7cb23cc1fc2ade8a +0.12.3.lock/extra=3/crlf=false 875655d84a273615 e8943a4ec6fee670 +0.12.3.lock/extra=3/crlf=false/re-run 3c2b2fe1fa3f2817 5515db3c306e53d7 +0.12.3.lock/extra=3/crlf=true 5b7bdf5d23ba4480 c0b5e5853365268b +0.12.3.lock/extra=3/crlf=true/re-run b6e20136704fd19e a10d3f366c87b970 +1.15.5.lock/extra=0/crlf=false 4d4f13130b2f5ca6 130901f87d2e40c7 +1.15.5.lock/extra=0/crlf=false/re-run 4d4f13130b2f5ca6 130901f87d2e40c7 +1.15.5.lock/extra=0/crlf=true 738ea560b4bac5e9 130901f87d2e40c7 +1.15.5.lock/extra=0/crlf=true/re-run 738ea560b4bac5e9 130901f87d2e40c7 +1.15.5.lock/extra=3/crlf=false 05076616697b9f90 de9fc334335a9979 +1.15.5.lock/extra=3/crlf=false/re-run 05076616697b9f90 de9fc334335a9979 +1.15.5.lock/extra=3/crlf=true 61a0a8be9a863e05 de9fc334335a9979 +1.15.5.lock/extra=3/crlf=true/re-run 61a0a8be9a863e05 de9fc334335a9979 +2.0.3.lock/extra=0/crlf=false f85fd6eef847aaaf 599e1d4d6f119679 +2.0.3.lock/extra=0/crlf=false/re-run f85fd6eef847aaaf 599e1d4d6f119679 +2.0.3.lock/extra=0/crlf=true 5d41e681e269208b 599e1d4d6f119679 +2.0.3.lock/extra=0/crlf=true/re-run 5d41e681e269208b 599e1d4d6f119679 +2.0.3.lock/extra=3/crlf=false 4d3cfc88ac5ffece c82ff54f323a2b32 +2.0.3.lock/extra=3/crlf=false/re-run 4d3cfc88ac5ffece c82ff54f323a2b32 +2.0.3.lock/extra=3/crlf=true 337957cda423fdbf c82ff54f323a2b32 +2.0.3.lock/extra=3/crlf=true/re-run 337957cda423fdbf c82ff54f323a2b32 +2.1.5.lock/extra=0/crlf=false f85fd6eef847aaaf 599e1d4d6f119679 +2.1.5.lock/extra=0/crlf=false/re-run f85fd6eef847aaaf 599e1d4d6f119679 +2.1.5.lock/extra=0/crlf=true 5d41e681e269208b 599e1d4d6f119679 +2.1.5.lock/extra=0/crlf=true/re-run 5d41e681e269208b 599e1d4d6f119679 +2.1.5.lock/extra=3/crlf=false 4d3cfc88ac5ffece c82ff54f323a2b32 +2.1.5.lock/extra=3/crlf=false/re-run 4d3cfc88ac5ffece c82ff54f323a2b32 +2.1.5.lock/extra=3/crlf=true 337957cda423fdbf c82ff54f323a2b32 +2.1.5.lock/extra=3/crlf=true/re-run 337957cda423fdbf c82ff54f323a2b32 +2.10.4.lock/extra=0/crlf=false 172e58243f676d64 73f3873ab828f214 +2.10.4.lock/extra=0/crlf=false/re-run 52c4734e1adb2848 4d2b9e666c5cbc81 +2.10.4.lock/extra=0/crlf=true 84b3284978be45b7 2c07cc87c201eb6b +2.10.4.lock/extra=0/crlf=true/re-run 7d030b203da8acd0 1c73334c900130a0 +2.10.4.lock/extra=3/crlf=false 9c9557ecc3467229 9e5e43125a99c8dd +2.10.4.lock/extra=3/crlf=false/re-run cc827f83fff579f2 5adbe4dc82d80441 +2.10.4.lock/extra=3/crlf=true 001be1702fcbb10b 10b06206a2bc5000 +2.10.4.lock/extra=3/crlf=true/re-run e1337b373345aad8 e69bf935ad4bd849 +2.11.2.lock/extra=0/crlf=false 6b4f6ea534403ce1 58e88323c0a98eea +2.11.2.lock/extra=0/crlf=false/re-run 02e7df4fcb62c310 715b851660c0f6f1 +2.11.2.lock/extra=0/crlf=true c626206c4024fe46 acfcfb2cff254359 +2.11.2.lock/extra=0/crlf=true/re-run 87f02f8297dce1ec e5fa6ac70fd138ed +2.11.2.lock/extra=3/crlf=false 71024d43303c242f ec9f6d0f93187345 +2.11.2.lock/extra=3/crlf=false/re-run 4c92b86de4ae77eb c348446902102960 +2.11.2.lock/extra=3/crlf=true 986a91c5cd4511b7 5bf43bcd6a9bba82 +2.11.2.lock/extra=3/crlf=true/re-run 601377db18458ff3 8cb4839e4778db17 +2.17.3.lock/extra=0/crlf=false 98c5c38de2f9a8e1 934992c92d807cb4 +2.17.3.lock/extra=0/crlf=false/re-run bea465fa3cc73663 715b851660c0f6f1 +2.17.3.lock/extra=0/crlf=true a0c9bb9ed37191ef 0e9f62ecd20f7b70 +2.17.3.lock/extra=0/crlf=true/re-run ea711090534018d5 e5fa6ac70fd138ed +2.17.3.lock/extra=3/crlf=false 093ae70e2482288c 07acacd507c1e99f +2.17.3.lock/extra=3/crlf=false/re-run b73d20dc2ea08fa5 c348446902102960 +2.17.3.lock/extra=3/crlf=true d21eb6a0c09c524c 26e0f8339d8bd201 +2.17.3.lock/extra=3/crlf=true/re-run 15971e9f41f1f52e 8cb4839e4778db17 +2.29.2-extras.lock/extra=0/crlf=false eb78aecb54bd098d 01924141e924fe24 +2.29.2-extras.lock/extra=0/crlf=false/re-run 42d93b5c405df78d d55800dafdeb9407 +2.29.2-extras.lock/extra=0/crlf=true a74183ac5e064afc bbec41ffb06dd1c3 +2.29.2-extras.lock/extra=0/crlf=true/re-run 9c29d9c8a18cf39b bd0ec3c8e2c38c7c +2.29.2-extras.lock/extra=3/crlf=false 6485ead297972881 2adf0f5b0a61a929 +2.29.2-extras.lock/extra=3/crlf=false/re-run a522a0262db8df40 d89aeef0f1a97de3 +2.29.2-extras.lock/extra=3/crlf=true 53718730bd1cda51 ffe675f738abceb8 +2.29.2-extras.lock/extra=3/crlf=true/re-run 8b5ab3312a7b27a3 860325ae80ea317b +2.29.2.lock/extra=0/crlf=false 0afc5c9cc3e3929d fae13d34d675b52e +2.29.2.lock/extra=0/crlf=false/re-run 95943761aa62a765 715b851660c0f6f1 +2.29.2.lock/extra=0/crlf=true 36eebecfb1bfccd1 6b423fc0443bd563 +2.29.2.lock/extra=0/crlf=true/re-run 02f20956ed8b4483 e5fa6ac70fd138ed +2.29.2.lock/extra=3/crlf=false ef194e4c3f23be69 a82131c5b38382c3 +2.29.2.lock/extra=3/crlf=false/re-run 771462e9c51554c5 c348446902102960 +2.29.2.lock/extra=3/crlf=true cbaa4943320bfe44 e384ed3fcd5183af +2.29.2.lock/extra=3/crlf=true/re-run 35b9ad6159d5458d 8cb4839e4778db17 +2.3.4.lock/extra=0/crlf=false 1edd8acc82a5ddcc eea21bf6dc8a1a0b +2.3.4.lock/extra=0/crlf=false/re-run 1edd8acc82a5ddcc eea21bf6dc8a1a0b +2.3.4.lock/extra=0/crlf=true 817bd86e9a710e96 eea21bf6dc8a1a0b +2.3.4.lock/extra=0/crlf=true/re-run 817bd86e9a710e96 eea21bf6dc8a1a0b +2.3.4.lock/extra=3/crlf=false 26d0cc1acb4d1e66 d78c5f89eb9676d2 +2.3.4.lock/extra=3/crlf=false/re-run 26d0cc1acb4d1e66 d78c5f89eb9676d2 +2.3.4.lock/extra=3/crlf=true 9e6c6abc2b3b36da d78c5f89eb9676d2 +2.3.4.lock/extra=3/crlf=true/re-run 9e6c6abc2b3b36da d78c5f89eb9676d2 +2.6.1.lock/extra=0/crlf=false a1cf90463aec548e fb01e63645a7e615 +2.6.1.lock/extra=0/crlf=false/re-run a1cf90463aec548e fb01e63645a7e615 +2.6.1.lock/extra=0/crlf=true b4f2fc1d2ad04aff fb01e63645a7e615 +2.6.1.lock/extra=0/crlf=true/re-run b4f2fc1d2ad04aff fb01e63645a7e615 +2.6.1.lock/extra=3/crlf=false 0a39d27410f0a359 71f20c290766bcb6 +2.6.1.lock/extra=3/crlf=false/re-run 0a39d27410f0a359 71f20c290766bcb6 +2.6.1.lock/extra=3/crlf=true 028a8e1f1bf050a5 71f20c290766bcb6 +2.6.1.lock/extra=3/crlf=true/re-run 028a8e1f1bf050a5 71f20c290766bcb6 +2.7.4.lock/extra=0/crlf=false a1cf90463aec548e fb01e63645a7e615 +2.7.4.lock/extra=0/crlf=false/re-run a1cf90463aec548e fb01e63645a7e615 +2.7.4.lock/extra=0/crlf=true b4f2fc1d2ad04aff fb01e63645a7e615 +2.7.4.lock/extra=0/crlf=true/re-run b4f2fc1d2ad04aff fb01e63645a7e615 +2.7.4.lock/extra=3/crlf=false 0a39d27410f0a359 71f20c290766bcb6 +2.7.4.lock/extra=3/crlf=false/re-run 0a39d27410f0a359 71f20c290766bcb6 +2.7.4.lock/extra=3/crlf=true 028a8e1f1bf050a5 71f20c290766bcb6 +2.7.4.lock/extra=3/crlf=true/re-run 028a8e1f1bf050a5 71f20c290766bcb6 +2.8.2.lock/extra=0/crlf=false 55e31f21f6b597b6 9ea549ce0924eba8 +2.8.2.lock/extra=0/crlf=false/re-run bdf788b9e0b21711 d45bca027cafbf09 +2.8.2.lock/extra=0/crlf=true 8860e7f81b315e97 dd5fef2284f90bca +2.8.2.lock/extra=0/crlf=true/re-run 152ed911da843927 c7ae400c8fe98209 +2.8.2.lock/extra=3/crlf=false aa2044473027360e 489cf002f1f1a0de +2.8.2.lock/extra=3/crlf=false/re-run a0c3c1cf672c0825 3b601df6b9a1566d +2.8.2.lock/extra=3/crlf=true 59da76de16052042 4bf3ae8b0ab248a1 +2.8.2.lock/extra=3/crlf=true/re-run 37089aac3445a20c ea6bc4a1e91e1c9f +2.9.3.lock/extra=0/crlf=false 55e31f21f6b597b6 9ea549ce0924eba8 +2.9.3.lock/extra=0/crlf=false/re-run bdf788b9e0b21711 d45bca027cafbf09 +2.9.3.lock/extra=0/crlf=true 8860e7f81b315e97 dd5fef2284f90bca +2.9.3.lock/extra=0/crlf=true/re-run 152ed911da843927 c7ae400c8fe98209 +2.9.3.lock/extra=3/crlf=false aa2044473027360e 489cf002f1f1a0de +2.9.3.lock/extra=3/crlf=false/re-run a0c3c1cf672c0825 3b601df6b9a1566d +2.9.3.lock/extra=3/crlf=true 59da76de16052042 4bf3ae8b0ab248a1 +2.9.3.lock/extra=3/crlf=true/re-run 37089aac3445a20c ea6bc4a1e91e1c9f diff --git a/crates/socket-patch-core/tests/equivalence/poetry_rewrite.golden b/crates/socket-patch-core/tests/equivalence/poetry_rewrite.golden index 3bffb0f0d..1aa3cc5e1 100644 --- a/crates/socket-patch-core/tests/equivalence/poetry_rewrite.golden +++ b/crates/socket-patch-core/tests/equivalence/poetry_rewrite.golden @@ -1,122 +1,122 @@ # One grown poetry.lock pair and its deps, rewritten, then re-run over the result. # -0.12.17_extra=0_crlf=false d2b80f8d058298a0 0b2a725e611a39ba -0.12.17_extra=0_crlf=false/re-run d2b80f8d058298a0 0b2a725e611a39ba -0.12.17_extra=0_crlf=true 4b3a0e23f3264df8 0b2a725e611a39ba -0.12.17_extra=0_crlf=true/re-run 4b3a0e23f3264df8 0b2a725e611a39ba -0.12.17_extra=3_crlf=false a8bee4606ba5b760 bc28e4842040ec43 -0.12.17_extra=3_crlf=false/re-run a8bee4606ba5b760 bc28e4842040ec43 -0.12.17_extra=3_crlf=true 5ae487fb99db0745 bc28e4842040ec43 -0.12.17_extra=3_crlf=true/re-run 5ae487fb99db0745 bc28e4842040ec43 -1.0.10_extra=0_crlf=false 06d7816556307b33 09c1cc4c24fd1e8a -1.0.10_extra=0_crlf=false/re-run 613ac93e03589fac 1f3c40d9eb7c76cc -1.0.10_extra=0_crlf=true 38755c44d8811d7b 1de45eb80f579354 -1.0.10_extra=0_crlf=true/re-run a039bf51c09a676d 1f3c40d9eb7c76cc -1.0.10_extra=3_crlf=false cd980d8f38e065ee e644c20d2bf249e6 -1.0.10_extra=3_crlf=false/re-run 885d4754947c0ec7 1a8438ef5c1e4029 -1.0.10_extra=3_crlf=true 684566844b37fcd0 b79d6c7901b3712e -1.0.10_extra=3_crlf=true/re-run 4fd1416655a91e78 1a8438ef5c1e4029 -1.1.15_extra=0_crlf=false d0f26c272489ebe3 5346ad345893f81c -1.1.15_extra=0_crlf=false/re-run bc82912108fd25ce 1f3c40d9eb7c76cc -1.1.15_extra=0_crlf=true 6c566dc6e3aeba50 58a4fd6324ef3646 -1.1.15_extra=0_crlf=true/re-run dea1745a49ef026f 1f3c40d9eb7c76cc -1.1.15_extra=3_crlf=false 8870f129976dee0d 2dc32d148015726c -1.1.15_extra=3_crlf=false/re-run 8551e339b8b54ddf 1a8438ef5c1e4029 -1.1.15_extra=3_crlf=true 4d055246679dee4c c612da5b78287e16 -1.1.15_extra=3_crlf=true/re-run 53fe14f551e02333 1a8438ef5c1e4029 -1.2.2_extra=0_crlf=false 4b75722f3771f21c 9888cc4e2dfbdcf0 -1.2.2_extra=0_crlf=false/re-run 133b23c3dfc4cce1 1f3c40d9eb7c76cc -1.2.2_extra=0_crlf=true 1813b308432ea034 fe1747c65c708940 -1.2.2_extra=0_crlf=true/re-run 004001fb1c25e747 1f3c40d9eb7c76cc -1.2.2_extra=3_crlf=false 812817b968306e99 92205289c2cde979 -1.2.2_extra=3_crlf=false/re-run f81a1c4a49505bce ba58a7a5dce59269 -1.2.2_extra=3_crlf=true 6051efc48c2d3992 1837eb4033639a52 -1.2.2_extra=3_crlf=true/re-run e3ec0a6371139d8b ba58a7a5dce59269 -1.3.2_extra=0_crlf=false 86cda4c82eee7e8b 6bba7a02d52c265f -1.3.2_extra=0_crlf=false/re-run a9df9acaedd7db22 1f3c40d9eb7c76cc -1.3.2_extra=0_crlf=true e92e1cd1beeb0002 dcd05b3afe27840d -1.3.2_extra=0_crlf=true/re-run f13a599ff9511fe7 1f3c40d9eb7c76cc -1.3.2_extra=3_crlf=false fe9f2817767e5612 5f7d2dba58b9fc19 -1.3.2_extra=3_crlf=false/re-run d9728f0057845cbb 1a8438ef5c1e4029 -1.3.2_extra=3_crlf=true 253448255457216e 0e2a78c377278b8a -1.3.2_extra=3_crlf=true/re-run 71d0a8d8ab2ecc77 1a8438ef5c1e4029 -1.4.2_extra=0_crlf=false 00a7bc0a3c89c49b 35bf18ca91fd9d61 -1.4.2_extra=0_crlf=false/re-run 4aa5eedcf2dd0b02 1f3c40d9eb7c76cc -1.4.2_extra=0_crlf=true 44de4691acb61dba 62e231066f59791e -1.4.2_extra=0_crlf=true/re-run 47bf0330a4e93507 1f3c40d9eb7c76cc -1.4.2_extra=3_crlf=false a3fb1d70a1207ea7 952dcca3cb6d0255 -1.4.2_extra=3_crlf=false/re-run 60023b576de9ad30 1a8438ef5c1e4029 -1.4.2_extra=3_crlf=true bd8911bca06c1262 e820c0056a0e124c -1.4.2_extra=3_crlf=true/re-run a5f5292e17ecb5f4 1a8438ef5c1e4029 -1.5.1_extra=0_crlf=false a1795ca286fa80ce e93753c3978de13b -1.5.1_extra=0_crlf=false/re-run f19230652767bb30 1f3c40d9eb7c76cc -1.5.1_extra=0_crlf=true 5871c60c9d63aeee fbd73874c2c2808e -1.5.1_extra=0_crlf=true/re-run 6dab15e1eab2f462 1f3c40d9eb7c76cc -1.5.1_extra=3_crlf=false 0b33aee716ad5a55 d3c843f055aec036 -1.5.1_extra=3_crlf=false/re-run e26d96dd76e1b368 1a8438ef5c1e4029 -1.5.1_extra=3_crlf=true 161846bd33f1d508 9cbde1afb60c8400 -1.5.1_extra=3_crlf=true/re-run 3e4cf8de3fdcb6ea 1a8438ef5c1e4029 -1.6.1_extra=0_crlf=false d1e0442566c6ce49 cf501da566656c93 -1.6.1_extra=0_crlf=false/re-run ea7966d58c9f2aab 1f3c40d9eb7c76cc -1.6.1_extra=0_crlf=true db20a2c4edcafbf0 335c351e01000a64 -1.6.1_extra=0_crlf=true/re-run 8f7c22713b6f70b6 1f3c40d9eb7c76cc -1.6.1_extra=3_crlf=false afd5d875780cdf40 30e8ad1edb556a8d -1.6.1_extra=3_crlf=false/re-run 647ef4eb652f1ba6 1a8438ef5c1e4029 -1.6.1_extra=3_crlf=true b37fcda617275f4b 4264d7e80059c266 -1.6.1_extra=3_crlf=true/re-run 1a4219f03efad32a 1a8438ef5c1e4029 -1.7.1_extra=0_crlf=false 6600136814fb4134 f6b16e92d3bea560 -1.7.1_extra=0_crlf=false/re-run abdbbfe42c8b4d7e 1f3c40d9eb7c76cc -1.7.1_extra=0_crlf=true 6a85a6a27c99cd2b fc7fce47c65d34ff -1.7.1_extra=0_crlf=true/re-run ac9d84b451f9c128 1f3c40d9eb7c76cc -1.7.1_extra=3_crlf=false 020fa829b6a870bc a3cd491e00fe8d3a -1.7.1_extra=3_crlf=false/re-run 31133977b68135e7 1a8438ef5c1e4029 -1.7.1_extra=3_crlf=true bcb1fd00c2d4d5bf 33a9240a9f83dfd9 -1.7.1_extra=3_crlf=true/re-run ac38704d471666ab 1a8438ef5c1e4029 -1.8.5_extra=0_crlf=false 7b0c488a965c4f7b 5708bfd6bb9ec464 -1.8.5_extra=0_crlf=false/re-run 60ce065c6b04b5b1 1f3c40d9eb7c76cc -1.8.5_extra=0_crlf=true 2094d208abbeabc2 1d8b437489b30c3e -1.8.5_extra=0_crlf=true/re-run 5903de75cc2030e8 1f3c40d9eb7c76cc -1.8.5_extra=3_crlf=false e22381008c314e3d 0a425da029e73f6b -1.8.5_extra=3_crlf=false/re-run 147043d9e02c7a08 1a8438ef5c1e4029 -1.8.5_extra=3_crlf=true e42675d2895de9fb 4d83e93a09ad746b -1.8.5_extra=3_crlf=true/re-run 9e97459a7ac1ffb1 1a8438ef5c1e4029 -2.0.1_extra=0_crlf=false 2775b8f1411fbadb cce75b3045871657 -2.0.1_extra=0_crlf=false/re-run 5a19ccf0764fac47 1f3c40d9eb7c76cc -2.0.1_extra=0_crlf=true 17ebf61a05b76816 24db92ebcada6076 -2.0.1_extra=0_crlf=true/re-run 97eff076ab7188a2 1f3c40d9eb7c76cc -2.0.1_extra=3_crlf=false bc38767ba9dcc6e6 074baa7851946243 -2.0.1_extra=3_crlf=false/re-run 44cce38f20f8f16f 1a8438ef5c1e4029 -2.0.1_extra=3_crlf=true b68b1e02f151ef48 edcbe489b55d980a -2.0.1_extra=3_crlf=true/re-run 054b4ddcb4dcfafa 1a8438ef5c1e4029 -2.1.4_extra=0_crlf=false 6f62ddaa4adc50a4 64a60db83d599042 -2.1.4_extra=0_crlf=false/re-run 5b7e62f966b8d271 1f3c40d9eb7c76cc -2.1.4_extra=0_crlf=true 8a8d93a2b2127129 edfc2a44ac796cb1 -2.1.4_extra=0_crlf=true/re-run d1cd3eeea0692da9 1f3c40d9eb7c76cc -2.1.4_extra=3_crlf=false 65364c0f4e7aa0e7 ee85256b26e73975 -2.1.4_extra=3_crlf=false/re-run a3676e7eddbccf4a 1a8438ef5c1e4029 -2.1.4_extra=3_crlf=true 821fdbf37b56026b d36ee61ccaed1901 -2.1.4_extra=3_crlf=true/re-run 25f65d16b322be1c 1a8438ef5c1e4029 -2.2.1_extra=0_crlf=false 7a500b5022ac388c 9638a8b0c560fbcf -2.2.1_extra=0_crlf=false/re-run b484d2ecc12a5edb 1f3c40d9eb7c76cc -2.2.1_extra=0_crlf=true 11b39909d694a4f5 7a698e83c907cf50 -2.2.1_extra=0_crlf=true/re-run f73c0184185b55c8 1f3c40d9eb7c76cc -2.2.1_extra=3_crlf=false 10b0bcde3632669b 09c2370eeb4bd3d1 -2.2.1_extra=3_crlf=false/re-run 679acf31e13b71a0 1a8438ef5c1e4029 -2.2.1_extra=3_crlf=true feaa6416a4b168a3 cee2e6b74cec8768 -2.2.1_extra=3_crlf=true/re-run 4b8aa165ed772485 1a8438ef5c1e4029 -2.3.4_extra=0_crlf=false 1ad17cac9704b1ce 4bfcd86cc885c460 -2.3.4_extra=0_crlf=false/re-run 398fea1a1ac5d77b 1f3c40d9eb7c76cc -2.3.4_extra=0_crlf=true d4578a811e547b2c 904f17bb4d9785c4 -2.3.4_extra=0_crlf=true/re-run 5f79a7161605a083 1f3c40d9eb7c76cc -2.3.4_extra=3_crlf=false d38430f6b06c87f3 3175a0bc70960152 -2.3.4_extra=3_crlf=false/re-run ee7114aaad11f4f0 1a8438ef5c1e4029 -2.3.4_extra=3_crlf=true 89e72b13eccda257 e2edba8cfb04309d -2.3.4_extra=3_crlf=true/re-run 04c0dd27743659d0 1a8438ef5c1e4029 -2.4.3_extra=0_crlf=false 70d3006a3e404efc 04e301df1078ebaa -2.4.3_extra=0_crlf=false/re-run cb0289ac6716caab 1f3c40d9eb7c76cc -2.4.3_extra=0_crlf=true ebe555cb2a5fb1f2 bff9310a0d7fe5c2 -2.4.3_extra=0_crlf=true/re-run 3f6277fd5f2ef21b 1f3c40d9eb7c76cc -2.4.3_extra=3_crlf=false 7bcb0eb7f4f1150a 26a72136bfe7b634 -2.4.3_extra=3_crlf=false/re-run c7b4e742d1f0f79b 1a8438ef5c1e4029 -2.4.3_extra=3_crlf=true f1ba10a6e2549703 80b2e48af24a373c -2.4.3_extra=3_crlf=true/re-run 6c2fb99f914a5767 1a8438ef5c1e4029 +0.12.17_extra=0_crlf=false d2b80f8d058298a0 93240e3b9e9c6e7d +0.12.17_extra=0_crlf=false/re-run d2b80f8d058298a0 93240e3b9e9c6e7d +0.12.17_extra=0_crlf=true 4b3a0e23f3264df8 93240e3b9e9c6e7d +0.12.17_extra=0_crlf=true/re-run 4b3a0e23f3264df8 93240e3b9e9c6e7d +0.12.17_extra=3_crlf=false a8bee4606ba5b760 5a0969976ee224e0 +0.12.17_extra=3_crlf=false/re-run a8bee4606ba5b760 5a0969976ee224e0 +0.12.17_extra=3_crlf=true 5ae487fb99db0745 5a0969976ee224e0 +0.12.17_extra=3_crlf=true/re-run 5ae487fb99db0745 5a0969976ee224e0 +1.0.10_extra=0_crlf=false 06d7816556307b33 ce138c5f9afd9f98 +1.0.10_extra=0_crlf=false/re-run 613ac93e03589fac f8fdb12a4eecf0a2 +1.0.10_extra=0_crlf=true 38755c44d8811d7b 8147eba507f5bf26 +1.0.10_extra=0_crlf=true/re-run a039bf51c09a676d f8fdb12a4eecf0a2 +1.0.10_extra=3_crlf=false cd980d8f38e065ee 48a97a405975c425 +1.0.10_extra=3_crlf=false/re-run 885d4754947c0ec7 b1b140a9dde279e7 +1.0.10_extra=3_crlf=true 684566844b37fcd0 8621af0d9650153a +1.0.10_extra=3_crlf=true/re-run 4fd1416655a91e78 b1b140a9dde279e7 +1.1.15_extra=0_crlf=false d0f26c272489ebe3 ee0cfef7a7c44604 +1.1.15_extra=0_crlf=false/re-run bc82912108fd25ce f8fdb12a4eecf0a2 +1.1.15_extra=0_crlf=true 6c566dc6e3aeba50 253f872ba2d851f1 +1.1.15_extra=0_crlf=true/re-run dea1745a49ef026f f8fdb12a4eecf0a2 +1.1.15_extra=3_crlf=false 8870f129976dee0d c35438b9fad74cc0 +1.1.15_extra=3_crlf=false/re-run 8551e339b8b54ddf b1b140a9dde279e7 +1.1.15_extra=3_crlf=true 4d055246679dee4c 0a1b46ae64996d57 +1.1.15_extra=3_crlf=true/re-run 53fe14f551e02333 b1b140a9dde279e7 +1.2.2_extra=0_crlf=false 4b75722f3771f21c b1f29e8111b96af1 +1.2.2_extra=0_crlf=false/re-run 133b23c3dfc4cce1 f8fdb12a4eecf0a2 +1.2.2_extra=0_crlf=true 1813b308432ea034 3a33bcc9e204c49d +1.2.2_extra=0_crlf=true/re-run 004001fb1c25e747 f8fdb12a4eecf0a2 +1.2.2_extra=3_crlf=false 812817b968306e99 105e07834fe6321c +1.2.2_extra=3_crlf=false/re-run f81a1c4a49505bce d84af3da645030f3 +1.2.2_extra=3_crlf=true 6051efc48c2d3992 9f352314a3ac2043 +1.2.2_extra=3_crlf=true/re-run e3ec0a6371139d8b d84af3da645030f3 +1.3.2_extra=0_crlf=false 86cda4c82eee7e8b 98a9334683cd2371 +1.3.2_extra=0_crlf=false/re-run a9df9acaedd7db22 f8fdb12a4eecf0a2 +1.3.2_extra=0_crlf=true e92e1cd1beeb0002 c84ee4081d832640 +1.3.2_extra=0_crlf=true/re-run f13a599ff9511fe7 f8fdb12a4eecf0a2 +1.3.2_extra=3_crlf=false fe9f2817767e5612 e3138b6c85d12f0e +1.3.2_extra=3_crlf=false/re-run d9728f0057845cbb b1b140a9dde279e7 +1.3.2_extra=3_crlf=true 253448255457216e 0d707adb7b99e61d +1.3.2_extra=3_crlf=true/re-run 71d0a8d8ab2ecc77 b1b140a9dde279e7 +1.4.2_extra=0_crlf=false 00a7bc0a3c89c49b 49a983585f75033b +1.4.2_extra=0_crlf=false/re-run 4aa5eedcf2dd0b02 f8fdb12a4eecf0a2 +1.4.2_extra=0_crlf=true 44de4691acb61dba 663ad1d035c4d4ac +1.4.2_extra=0_crlf=true/re-run 47bf0330a4e93507 f8fdb12a4eecf0a2 +1.4.2_extra=3_crlf=false a3fb1d70a1207ea7 3a399a808f8ac1c5 +1.4.2_extra=3_crlf=false/re-run 60023b576de9ad30 b1b140a9dde279e7 +1.4.2_extra=3_crlf=true bd8911bca06c1262 9a0609b72174388e +1.4.2_extra=3_crlf=true/re-run a5f5292e17ecb5f4 b1b140a9dde279e7 +1.5.1_extra=0_crlf=false a1795ca286fa80ce 058555664e505097 +1.5.1_extra=0_crlf=false/re-run f19230652767bb30 f8fdb12a4eecf0a2 +1.5.1_extra=0_crlf=true 5871c60c9d63aeee a55f5b97cbed5433 +1.5.1_extra=0_crlf=true/re-run 6dab15e1eab2f462 f8fdb12a4eecf0a2 +1.5.1_extra=3_crlf=false 0b33aee716ad5a55 41fe64bd1c418869 +1.5.1_extra=3_crlf=false/re-run e26d96dd76e1b368 b1b140a9dde279e7 +1.5.1_extra=3_crlf=true 161846bd33f1d508 01c2d85e35e93992 +1.5.1_extra=3_crlf=true/re-run 3e4cf8de3fdcb6ea b1b140a9dde279e7 +1.6.1_extra=0_crlf=false d1e0442566c6ce49 ab7e51239fae8d5a +1.6.1_extra=0_crlf=false/re-run ea7966d58c9f2aab f8fdb12a4eecf0a2 +1.6.1_extra=0_crlf=true db20a2c4edcafbf0 f29ca32d8ba904fb +1.6.1_extra=0_crlf=true/re-run 8f7c22713b6f70b6 f8fdb12a4eecf0a2 +1.6.1_extra=3_crlf=false afd5d875780cdf40 dfe1ce4c8a5174ab +1.6.1_extra=3_crlf=false/re-run 647ef4eb652f1ba6 b1b140a9dde279e7 +1.6.1_extra=3_crlf=true b37fcda617275f4b ef284f1f24aa9e08 +1.6.1_extra=3_crlf=true/re-run 1a4219f03efad32a b1b140a9dde279e7 +1.7.1_extra=0_crlf=false 6600136814fb4134 4edb9e696db074e3 +1.7.1_extra=0_crlf=false/re-run abdbbfe42c8b4d7e f8fdb12a4eecf0a2 +1.7.1_extra=0_crlf=true 6a85a6a27c99cd2b 993b91fc62c83357 +1.7.1_extra=0_crlf=true/re-run ac9d84b451f9c128 f8fdb12a4eecf0a2 +1.7.1_extra=3_crlf=false 020fa829b6a870bc 5e89895644f928cd +1.7.1_extra=3_crlf=false/re-run 31133977b68135e7 b1b140a9dde279e7 +1.7.1_extra=3_crlf=true bcb1fd00c2d4d5bf 7ddb429329300b96 +1.7.1_extra=3_crlf=true/re-run ac38704d471666ab b1b140a9dde279e7 +1.8.5_extra=0_crlf=false 7b0c488a965c4f7b d078ca598d671d89 +1.8.5_extra=0_crlf=false/re-run 60ce065c6b04b5b1 f8fdb12a4eecf0a2 +1.8.5_extra=0_crlf=true 2094d208abbeabc2 6a671eb8791d9630 +1.8.5_extra=0_crlf=true/re-run 5903de75cc2030e8 f8fdb12a4eecf0a2 +1.8.5_extra=3_crlf=false e22381008c314e3d f6b69cc8d61bdaf1 +1.8.5_extra=3_crlf=false/re-run 147043d9e02c7a08 b1b140a9dde279e7 +1.8.5_extra=3_crlf=true e42675d2895de9fb c8c835614f60185d +1.8.5_extra=3_crlf=true/re-run 9e97459a7ac1ffb1 b1b140a9dde279e7 +2.0.1_extra=0_crlf=false 2775b8f1411fbadb f79f87005ea5555f +2.0.1_extra=0_crlf=false/re-run 5a19ccf0764fac47 f8fdb12a4eecf0a2 +2.0.1_extra=0_crlf=true 17ebf61a05b76816 d16a6231993dea64 +2.0.1_extra=0_crlf=true/re-run 97eff076ab7188a2 f8fdb12a4eecf0a2 +2.0.1_extra=3_crlf=false bc38767ba9dcc6e6 3b2bb33beca56ace +2.0.1_extra=3_crlf=false/re-run 44cce38f20f8f16f b1b140a9dde279e7 +2.0.1_extra=3_crlf=true b68b1e02f151ef48 a2cb2267d41e43f6 +2.0.1_extra=3_crlf=true/re-run 054b4ddcb4dcfafa b1b140a9dde279e7 +2.1.4_extra=0_crlf=false 6f62ddaa4adc50a4 4c30951494899abf +2.1.4_extra=0_crlf=false/re-run 5b7e62f966b8d271 f8fdb12a4eecf0a2 +2.1.4_extra=0_crlf=true 8a8d93a2b2127129 e77c1232feff3e43 +2.1.4_extra=0_crlf=true/re-run d1cd3eeea0692da9 f8fdb12a4eecf0a2 +2.1.4_extra=3_crlf=false 65364c0f4e7aa0e7 4d02f464b7de3e9e +2.1.4_extra=3_crlf=false/re-run a3676e7eddbccf4a b1b140a9dde279e7 +2.1.4_extra=3_crlf=true 821fdbf37b56026b 47f62f46f0c62ed9 +2.1.4_extra=3_crlf=true/re-run 25f65d16b322be1c b1b140a9dde279e7 +2.2.1_extra=0_crlf=false 7a500b5022ac388c 55b80a0f6166730c +2.2.1_extra=0_crlf=false/re-run b484d2ecc12a5edb f8fdb12a4eecf0a2 +2.2.1_extra=0_crlf=true 11b39909d694a4f5 f3b41b5cbe433871 +2.2.1_extra=0_crlf=true/re-run f73c0184185b55c8 f8fdb12a4eecf0a2 +2.2.1_extra=3_crlf=false 10b0bcde3632669b 773b4b6868a73af7 +2.2.1_extra=3_crlf=false/re-run 679acf31e13b71a0 b1b140a9dde279e7 +2.2.1_extra=3_crlf=true feaa6416a4b168a3 435c0c1288d1909d +2.2.1_extra=3_crlf=true/re-run 4b8aa165ed772485 b1b140a9dde279e7 +2.3.4_extra=0_crlf=false 1ad17cac9704b1ce dcaab82c7e3822cb +2.3.4_extra=0_crlf=false/re-run 398fea1a1ac5d77b f8fdb12a4eecf0a2 +2.3.4_extra=0_crlf=true d4578a811e547b2c 374246125153e367 +2.3.4_extra=0_crlf=true/re-run 5f79a7161605a083 f8fdb12a4eecf0a2 +2.3.4_extra=3_crlf=false d38430f6b06c87f3 c97de9d6d7fe62e3 +2.3.4_extra=3_crlf=false/re-run ee7114aaad11f4f0 b1b140a9dde279e7 +2.3.4_extra=3_crlf=true 89e72b13eccda257 acb7ee3eddc0413c +2.3.4_extra=3_crlf=true/re-run 04c0dd27743659d0 b1b140a9dde279e7 +2.4.3_extra=0_crlf=false 70d3006a3e404efc be4f9d2e6360e505 +2.4.3_extra=0_crlf=false/re-run cb0289ac6716caab f8fdb12a4eecf0a2 +2.4.3_extra=0_crlf=true ebe555cb2a5fb1f2 b54d5684589b5a36 +2.4.3_extra=0_crlf=true/re-run 3f6277fd5f2ef21b f8fdb12a4eecf0a2 +2.4.3_extra=3_crlf=false 7bcb0eb7f4f1150a abcaa7493cb8d583 +2.4.3_extra=3_crlf=false/re-run c7b4e742d1f0f79b b1b140a9dde279e7 +2.4.3_extra=3_crlf=true f1ba10a6e2549703 307a88d29f7b43e4 +2.4.3_extra=3_crlf=true/re-run 6c2fb99f914a5767 b1b140a9dde279e7 From e5dfad6116901b704b47f305200d0453a0096081 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 5 Oct 2026 18:24:21 +0000 Subject: [PATCH 6/8] Route Gradle digests through utils::digest main has failed socket-patch-core's lib tests since Gradle support (#646) and the digest helpers (#865) both landed. The guard test production_digests_go_through_the_helpers flags three files #646 added that still hash inline: crawlers/gradle_cache.rs, patch/jvm_jar.rs and patch/sidecars/maven.rs. That breaks test, test-release and coverage on every open PR. Each inline sha1/sha256 call now goes through sha1_hex_of or sha256_hex_of, which compute the same lowercase hex. Behaviour is unchanged. Assisted-by: Claude Code:claude-opus-5-5 (cherry picked from commit 659ac2c24e5c5904e743b4bc98ea4645da2ed6a1) --- crates/socket-patch-core/src/crawlers/gradle_cache.rs | 9 ++++----- crates/socket-patch-core/src/patch/jvm_jar.rs | 7 ++----- crates/socket-patch-core/src/patch/sidecars/maven.rs | 4 +--- 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/crates/socket-patch-core/src/crawlers/gradle_cache.rs b/crates/socket-patch-core/src/crawlers/gradle_cache.rs index ef295ee27..afd7c4fba 100644 --- a/crates/socket-patch-core/src/crawlers/gradle_cache.rs +++ b/crates/socket-patch-core/src/crawlers/gradle_cache.rs @@ -70,8 +70,7 @@ pub fn hash_eq(dir_name: &str, sha1_hex: &str) -> bool { /// Whether `bytes` are the pristine download Gradle stored in the hash /// directory `dir_name` (their sha1 names it). pub fn pristine(dir_name: &str, bytes: &[u8]) -> bool { - use sha1::{Digest, Sha1}; - hash_eq(dir_name, &hex::encode(Sha1::digest(bytes))) + hash_eq(dir_name, &crate::utils::digest::sha1_hex_of(bytes)) } /// Whether `path` is a version directory of a `files-2.1` tree @@ -432,8 +431,6 @@ impl DerivedIndex { /// The [`DerivedCopies`] of the jar `jar_leaf` whose pristine bytes /// hash to `pristine_sha1`. pub fn query(&self, jar_leaf: &str, pristine_sha1: &str) -> DerivedCopies { - use sha1::{Digest, Sha1}; - let instrumented = format!("instrumented-{jar_leaf}"); let mut out = DerivedCopies { incomplete: self.incomplete, @@ -460,7 +457,9 @@ impl DerivedIndex { out.stale.push(path.clone()); } else if name == jar_leaf || name == instrumented { match crate::utils::fs::read_regular_to_bytes_sync(path) { - Ok(bytes) if hash_eq(&hex::encode(Sha1::digest(&bytes)), pristine_sha1) => { + Ok(bytes) + if hash_eq(&crate::utils::digest::sha1_hex_of(&bytes), pristine_sha1) => + { out.stale.push(path.clone()) } Ok(_) => out.unknown.push(path.clone()), diff --git a/crates/socket-patch-core/src/patch/jvm_jar.rs b/crates/socket-patch-core/src/patch/jvm_jar.rs index 82d679406..f38a84403 100644 --- a/crates/socket-patch-core/src/patch/jvm_jar.rs +++ b/crates/socket-patch-core/src/patch/jvm_jar.rs @@ -25,8 +25,6 @@ use std::collections::HashMap; use std::path::{Path, PathBuf}; -use sha1::Digest as _; - use crate::crawlers::gradle_cache; use crate::hash::git_sha256::compute_git_sha256_from_bytes; use crate::manifest::schema::PatchFileInfo; @@ -353,12 +351,11 @@ fn unpatched_members( } fn sha256_hex(bytes: &[u8]) -> String { - use sha2::Digest as _; - hex::encode(sha2::Sha256::digest(bytes)) + crate::utils::digest::sha256_hex_of(bytes) } fn sha1_hex(bytes: &[u8]) -> String { - hex::encode(sha1::Sha1::digest(bytes)) + crate::utils::digest::sha1_hex_of(bytes) } /// `/jvm-originals/.jar`. diff --git a/crates/socket-patch-core/src/patch/sidecars/maven.rs b/crates/socket-patch-core/src/patch/sidecars/maven.rs index f2f5a2466..8798bfce6 100644 --- a/crates/socket-patch-core/src/patch/sidecars/maven.rs +++ b/crates/socket-patch-core/src/patch/sidecars/maven.rs @@ -17,8 +17,6 @@ use std::path::{Path, PathBuf}; -use sha1::Digest as _; - use super::{ SidecarAdvisory, SidecarAdvisoryCode, SidecarError, SidecarFile, SidecarFileAction, SidecarPayload, SidecarSeverity, @@ -44,7 +42,7 @@ impl Algo { fn digest(self, bytes: &[u8]) -> String { match self { - Algo::Sha1 => hex::encode(sha1::Sha1::digest(bytes)), + Algo::Sha1 => crate::utils::digest::sha1_hex_of(bytes), Algo::Md5 => hex::encode(md5(bytes)), } } From 0b6121c05e50eaf33621e7881abd2a982ce5ccff Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 5 Oct 2026 19:30:49 +0000 Subject: [PATCH 7/8] Read a JSONC package.json for Bun patch keys Bun accepts comments and trailing commas in package.json. The patchedDependencies reader parsed it as strict JSON, so such a manifest yielded no keys. A bun.lockb project has no text mirror to fall back on, so the project's own bun patch could still be rewired away. The reader now strips JSONC comments and trailing commas before parsing, leaving string contents untouched. Refs #367 Assisted-by: Claude Code:claude-opus-5-5 --- .../src/vendor/bun_lock_text.rs | 77 ++++++++++++++++++- 1 file changed, 74 insertions(+), 3 deletions(-) diff --git a/crates/socket-patch-core/src/vendor/bun_lock_text.rs b/crates/socket-patch-core/src/vendor/bun_lock_text.rs index c6c9ce28a..3ccd604e8 100644 --- a/crates/socket-patch-core/src/vendor/bun_lock_text.rs +++ b/crates/socket-patch-core/src/vendor/bun_lock_text.rs @@ -27,11 +27,17 @@ const SUPPORTED_LOCK_VERSIONS: [u64; 3] = [0, 1, 2]; /// install reads, plus the copy Bun mirrors at the top of a text `bun.lock` /// (` "patchedDependencies": {` … ` },`, one `"key": "path"` line each). /// Either source alone is enough: a key missing from one is still a patch -/// Bun applies. A manifest that is not JSON, or a lock section out of Bun's -/// emitted shape, contributes only the keys it spells plainly. +/// Bun applies. The manifest is read as Bun reads it, comments and trailing +/// commas allowed ([`strip_jsonc`]); one Bun cannot parse either, and a lock +/// section out of Bun's emitted shape, contribute only what they spell +/// plainly. pub(crate) fn patched_dependency_keys(manifest: Option<&str>, lock: Option<&str>) -> Vec { let mut keys: Vec = manifest - .and_then(|text| serde_json::from_str::(text).ok()) + .and_then(|text| { + serde_json::from_str::(text) + .or_else(|_| serde_json::from_str(&strip_jsonc(text))) + .ok() + }) .and_then(|value| match value.get("patchedDependencies") { Some(serde_json::Value::Object(map)) => Some(map.keys().cloned().collect()), _ => None, @@ -52,6 +58,58 @@ pub(crate) fn patched_dependency_keys(manifest: Option<&str>, lock: Option<&str> keys } +/// `text` with the JSONC Bun accepts in a `package.json` removed: `//` and +/// `/* */` comments and a comma before a closing `}` or `]`, all outside +/// strings. Everything else, strings included, is kept byte for byte. +fn strip_jsonc(text: &str) -> String { + let mut out = String::with_capacity(text.len()); + let mut chars = text.chars().peekable(); + let mut in_string = false; + while let Some(c) = chars.next() { + if in_string { + out.push(c); + if c == '\\' { + if let Some(escaped) = chars.next() { + out.push(escaped); + } + } else if c == '"' { + in_string = false; + } + continue; + } + match c { + '"' => { + in_string = true; + out.push(c); + } + '/' if chars.peek() == Some(&'/') => { + while chars.peek().is_some_and(|&n| n != '\n') { + chars.next(); + } + } + '/' if chars.peek() == Some(&'*') => { + chars.next(); + let mut prev = '\0'; + for n in chars.by_ref() { + if prev == '*' && n == '/' { + break; + } + prev = n; + } + } + '}' | ']' => { + let kept = out.trim_end_matches(char::is_whitespace).len(); + if out[..kept].ends_with(',') { + out.remove(kept - 1); + } + out.push(c); + } + _ => out.push(c), + } + } + out +} + /// The `patchedDependencies` key that makes Bun apply a project-authored /// patch to `name@version`, if any. Bun keys the patch on the registry /// resolution's `name@version`; a bare `name` is matched too so that a @@ -613,6 +671,19 @@ mod tests { Some("left-pad") ); assert!(patched_dependency_keys(Some("not json"), None).is_empty()); + // Bun reads a JSONC manifest: comments and trailing commas, with + // the same characters inside strings left alone. + let jsonc = "{\n // a comment, \"x\": 1\n \"name\": \"a//b /* c */\",\n /* block\n */\n \"patchedDependencies\": {\n \"left-pad@1.3.0\": \"patches/x,}.patch\",\n },\n}\n"; + assert_eq!( + patched_dependency_keys(Some(jsonc), None), + vec!["left-pad@1.3.0"] + ); + let stripped: serde_json::Value = serde_json::from_str(&strip_jsonc(jsonc)).unwrap(); + assert_eq!(stripped["name"], "a//b /* c */"); + assert_eq!( + stripped["patchedDependencies"]["left-pad@1.3.0"], + "patches/x,}.patch" + ); assert!(patched_dependency_keys(Some(r#"{"patchedDependencies":[]}"#), None).is_empty()); assert!(patched_dependency_keys(None, None).is_empty()); } From 21da4608ea14a9d076b32594646f0aca93e65a78 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 5 Oct 2026 19:41:13 +0000 Subject: [PATCH 8/8] Skip a BOM before reading Bun patch keys A Windows-saved package.json can start with a UTF-8 byte order mark, which Bun ignores but serde_json rejects. The reader found no patchedDependencies keys in such a manifest, so a bun.lockb project could still lose its own bun patch. The mark is now stripped first, as the crate's other manifest readers do. Refs #367 Assisted-by: Claude Code:claude-opus-5-5 --- crates/socket-patch-core/src/vendor/bun_lock_text.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/crates/socket-patch-core/src/vendor/bun_lock_text.rs b/crates/socket-patch-core/src/vendor/bun_lock_text.rs index 3ccd604e8..0b6e72220 100644 --- a/crates/socket-patch-core/src/vendor/bun_lock_text.rs +++ b/crates/socket-patch-core/src/vendor/bun_lock_text.rs @@ -27,12 +27,13 @@ const SUPPORTED_LOCK_VERSIONS: [u64; 3] = [0, 1, 2]; /// install reads, plus the copy Bun mirrors at the top of a text `bun.lock` /// (` "patchedDependencies": {` … ` },`, one `"key": "path"` line each). /// Either source alone is enough: a key missing from one is still a patch -/// Bun applies. The manifest is read as Bun reads it, comments and trailing -/// commas allowed ([`strip_jsonc`]); one Bun cannot parse either, and a lock +/// Bun applies. The manifest is read as Bun reads it, a leading BOM, +/// comments and trailing commas allowed ([`strip_jsonc`]); one Bun cannot parse either, and a lock /// section out of Bun's emitted shape, contribute only what they spell /// plainly. pub(crate) fn patched_dependency_keys(manifest: Option<&str>, lock: Option<&str>) -> Vec { let mut keys: Vec = manifest + .map(crate::utils::serde::strip_bom) .and_then(|text| { serde_json::from_str::(text) .or_else(|_| serde_json::from_str(&strip_jsonc(text))) @@ -678,6 +679,11 @@ mod tests { patched_dependency_keys(Some(jsonc), None), vec!["left-pad@1.3.0"] ); + // A Windows-saved manifest leads with a UTF-8 BOM, which Bun skips. + assert_eq!( + patched_dependency_keys(Some(&format!("\u{feff}{jsonc}")), None), + vec!["left-pad@1.3.0"] + ); let stripped: serde_json::Value = serde_json::from_str(&strip_jsonc(jsonc)).unwrap(); assert_eq!(stripped["name"], "a//b /* c */"); assert_eq!(