mirror of
https://github.com/pnpm/pnpm.git
synced 2026-07-02 11:55:17 -04:00
feat(executor): swallow optional-dep build failures and report via pnpm:skipped-optional-dependency (#397) (#419)
## Summary Closes item #6 of #397. Optional dependencies whose `postinstall` hooks fail no longer abort the install — pacquet now reports the failure through the `pnpm:skipped-optional-dependency` channel (reason `build_failure`) and continues, matching [pnpm v11's behavior at `building/during-install/src/index.ts:218-240`](https://github.com/pnpm/pnpm/blob/b4f8f47ac2/building/during-install/src/index.ts#L218-L240). Three commits: - **`feat(lockfile): surface snapshot-level optional flag`** — pacquet's lockfile reader previously dropped the `snapshots[<key>].optional` field. Adding it as `pub optional: bool` on `SnapshotEntry` with `#[serde(default, skip_serializing_if = "is_false")]` so absent ↔ false and `false` never serializes. The flag is pre-computed by pnpm's resolver at install time (ALL-paths- optional fold), so pacquet trusts the precomputed value rather than re-deriving it — matching [`lockfileToDepGraph` at deps/graph-builder/src/lockfileToDepGraph.ts:315](https://github.com/pnpm/pnpm/blob/b4f8f47ac2/deps/graph-builder/src/lockfileToDepGraph.ts#L315). - **`feat(reporter): add pnpm:skipped-optional-dependency event`** — new `LogEvent::SkippedOptionalDependency(SkippedOptionalDependencyLog)` variant + `SkippedOptionalPackage` + `SkippedOptionalReason` enum. Wire shape mirrors [pnpm's `SkippedOptionalDependencyMessage`](https://github.com/pnpm/pnpm/blob/b4f8f47ac2/core/core-loggers/src/skippedOptionalDependencyLogger.ts). All four upstream reasons (`BuildFailure`, `UnsupportedEngine`, `UnsupportedPlatform`, `ResolutionFailure`) are declared even though only `BuildFailure` is emitted today — keeps the enum closed so the other emit sites can land without widening it. - **`feat(package-manager): swallow optional-dep build failures`** — `BuildModules.run` reads `snapshot.optional`. When `run_postinstall_hooks` returns an `Err`, the dep's optional flag decides: optional ⇒ emit a `pnpm:skipped-optional-dependency` event (`reason: build_failure`, `package.id` = the dep's pkg dir to match upstream's `depNode.dir`) and continue; non-optional ⇒ propagate as `BuildModulesError::LifecycleScript` so the install aborts. Two `cfg(unix)`-gated tests cover both branches.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
---
|
||||
source: crates/cli/tests/install.rs
|
||||
assertion_line: 48
|
||||
expression: "(workspace_folders, store_files)"
|
||||
---
|
||||
(
|
||||
@@ -23,9 +24,10 @@ expression: "(workspace_folders, store_files)"
|
||||
],
|
||||
[
|
||||
"v11/files/02/f9d952341eade675d0a8f5da14d4277fded62f937242292c2cf5f1bb0eda101b5f803098d64e2a138f4bfe4cdf326cfaf8f6b4015476ab86d25df03ddec731-exec",
|
||||
"v11/files/68/b837b4fd7982b081521e51b14a167ccdb272b3c499202449d07d48d78a2fa7bb8652d3a894bb06ac6e3995e5126ee3470f73b5775584940751fce8be468405",
|
||||
"v11/files/93/e31fcd9144a604f87d04fa8fb4a058202c579df17fd2b21301e17ac0ae4f3e2bae90ced7ae71b71218bd4a789e355ef7b8bd8899cdb785250071b229503b2e",
|
||||
"v11/files/2f/b86ecd88e34f18360bf865231219f7898cf713f63ad6d099f72b821f47cded6b92ece8d468775219a2950c50451d3f3e2f3920d15b4a3c372098f2daff864b",
|
||||
"v11/files/65/d9491d753c9cff7b6d93461c83b8542a36db429a4cba4c712f841ae657f0d82b36d94081816d5a92ce761146c64b036ea1d89d53890c86b115d457bb98fa6b",
|
||||
"v11/files/bf/0c878f2a4f2ec6f231bb2d414547efb15eb22a673815cba36acfd89633cd4c8affcbdc9378e90ad4d8fc654de921d1f67444be94e6a2f81bda9330be60be93-exec",
|
||||
"v11/files/fe/06f6dff7c043b7ea30547ff55a8fd06fecc30fe143e3acf09cdd30def35d660f8cafa74ff28287748f661bbd767987eed52b440c77942eb0fb9aa6f44cfb37",
|
||||
"v11/index.db",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
---
|
||||
source: crates/cli/tests/install.rs
|
||||
assertion_line: 108
|
||||
expression: store_files
|
||||
---
|
||||
[
|
||||
"v11/files/02/f9d952341eade675d0a8f5da14d4277fded62f937242292c2cf5f1bb0eda101b5f803098d64e2a138f4bfe4cdf326cfaf8f6b4015476ab86d25df03ddec731-exec",
|
||||
"v11/files/68/b837b4fd7982b081521e51b14a167ccdb272b3c499202449d07d48d78a2fa7bb8652d3a894bb06ac6e3995e5126ee3470f73b5775584940751fce8be468405",
|
||||
"v11/files/93/e31fcd9144a604f87d04fa8fb4a058202c579df17fd2b21301e17ac0ae4f3e2bae90ced7ae71b71218bd4a789e355ef7b8bd8899cdb785250071b229503b2e",
|
||||
"v11/files/2f/b86ecd88e34f18360bf865231219f7898cf713f63ad6d099f72b821f47cded6b92ece8d468775219a2950c50451d3f3e2f3920d15b4a3c372098f2daff864b",
|
||||
"v11/files/65/d9491d753c9cff7b6d93461c83b8542a36db429a4cba4c712f841ae657f0d82b36d94081816d5a92ce761146c64b036ea1d89d53890c86b115d457bb98fa6b",
|
||||
"v11/files/bf/0c878f2a4f2ec6f231bb2d414547efb15eb22a673815cba36acfd89633cd4c8affcbdc9378e90ad4d8fc654de921d1f67444be94e6a2f81bda9330be60be93-exec",
|
||||
"v11/files/fe/06f6dff7c043b7ea30547ff55a8fd06fecc30fe143e3acf09cdd30def35d660f8cafa74ff28287748f661bbd767987eed52b440c77942eb0fb9aa6f44cfb37",
|
||||
"v11/index.db",
|
||||
]
|
||||
|
||||
@@ -1,22 +1,31 @@
|
||||
---
|
||||
source: crates/cli/tests/install.rs
|
||||
assertion_line: 133
|
||||
expression: index_file_contents
|
||||
---
|
||||
"sha512-EDvJzSLCEMnUnrrIGdV+IUWP/8w+nUjOuay2u0KW20Mnfnm3lyrdquQ+gKPNAOUfAsKL1y21np1nQN0PyP+57A==\t@pnpm.e2e/hello-world-js-bin@1.0.0":
|
||||
index.js:
|
||||
digest: bf0c878f2a4f2ec6f231bb2d414547efb15eb22a673815cba36acfd89633cd4c8affcbdc9378e90ad4d8fc654de921d1f67444be94e6a2f81bda9330be60be93
|
||||
mode: 493
|
||||
size: 48
|
||||
package.json:
|
||||
digest: 68b837b4fd7982b081521e51b14a167ccdb272b3c499202449d07d48d78a2fa7bb8652d3a894bb06ac6e3995e5126ee3470f73b5775584940751fce8be468405
|
||||
"sha512-G3aEYkPR7vdClnAaLekaPPXJdy1fDE7I0j8HagQDeJ9x3hX4iACybYTL7yZr6i69UsmK/Ho7xyxQII0OARxawA==\t@pnpm.e2e/hello-world-js-bin-parent@1.0.0":
|
||||
LICENSE:
|
||||
digest: 65d9491d753c9cff7b6d93461c83b8542a36db429a4cba4c712f841ae657f0d82b36d94081816d5a92ce761146c64b036ea1d89d53890c86b115d457bb98fa6b
|
||||
mode: 420
|
||||
size: 379
|
||||
"sha512-fEzGMnAjelrr4l9WMg5NRF6Tym3HhLBbdUNe3C+EQOrWVx/jVSMlsefokijv2mIDgCLm7Fv6FzUliQBAdsq91Q==\t@pnpm.e2e/hello-world-js-bin-parent@1.0.0":
|
||||
size: 1066
|
||||
index.js:
|
||||
digest: 02f9d952341eade675d0a8f5da14d4277fded62f937242292c2cf5f1bb0eda101b5f803098d64e2a138f4bfe4cdf326cfaf8f6b4015476ab86d25df03ddec731
|
||||
mode: 493
|
||||
size: 79
|
||||
package.json:
|
||||
digest: 93e31fcd9144a604f87d04fa8fb4a058202c579df17fd2b21301e17ac0ae4f3e2bae90ced7ae71b71218bd4a789e355ef7b8bd8899cdb785250071b229503b2e
|
||||
digest: fe06f6dff7c043b7ea30547ff55a8fd06fecc30fe143e3acf09cdd30def35d660f8cafa74ff28287748f661bbd767987eed52b440c77942eb0fb9aa6f44cfb37
|
||||
mode: 420
|
||||
size: 537
|
||||
size: 536
|
||||
"sha512-bFWeEhV2pspARSwVwLnMfQStl22sXBuexcX+OvSlMw5hKw3tpyqeDCEA04qhdo1XCX1Hag0pBuI6H9Ri0ctzOw==\t@pnpm.e2e/hello-world-js-bin@1.0.0":
|
||||
LICENSE:
|
||||
digest: 65d9491d753c9cff7b6d93461c83b8542a36db429a4cba4c712f841ae657f0d82b36d94081816d5a92ce761146c64b036ea1d89d53890c86b115d457bb98fa6b
|
||||
mode: 420
|
||||
size: 1066
|
||||
index.js:
|
||||
digest: bf0c878f2a4f2ec6f231bb2d414547efb15eb22a673815cba36acfd89633cd4c8affcbdc9378e90ad4d8fc654de921d1f67444be94e6a2f81bda9330be60be93
|
||||
mode: 493
|
||||
size: 48
|
||||
package.json:
|
||||
digest: 2fb86ecd88e34f18360bf865231219f7898cf713f63ad6d099f72b821f47cded6b92ece8d468775219a2950c50451d3f3e2f3920d15b4a3c372098f2daff864b
|
||||
mode: 420
|
||||
size: 410
|
||||
|
||||
@@ -111,6 +111,16 @@ pub struct RunPostinstallHooks<'a> {
|
||||
/// `/usr/local/bin/bash`). `None` means use the platform default
|
||||
/// (`sh -c` on POSIX, `cmd /d /s /c` on Windows).
|
||||
pub script_shell: Option<&'a Path>,
|
||||
/// Whether the dep is reachable only through optional edges
|
||||
/// (`snapshots[<key>].optional` in the v9 lockfile). Stamped
|
||||
/// into the `pnpm:lifecycle` `Script` and `Exit` events so
|
||||
/// downstream reporters can dispatch correctly, mirroring
|
||||
/// upstream's `lifecycleLogger.debug({ optional, … })` at
|
||||
/// <https://github.com/pnpm/pnpm/blob/b4f8f47ac2/exec/lifecycle/src/runLifecycleHook.ts#L102>.
|
||||
/// Does NOT affect failure handling — `BuildModules` consults the
|
||||
/// same flag independently to decide whether to swallow a build
|
||||
/// failure (see #397 item 6).
|
||||
pub optional: bool,
|
||||
}
|
||||
|
||||
/// Run the preinstall, install, and postinstall lifecycle scripts for
|
||||
@@ -208,7 +218,7 @@ fn run_lifecycle_hook<R: Reporter>(
|
||||
level: LogLevel::Debug,
|
||||
message: LifecycleMessage::Script {
|
||||
dep_path: opts.dep_path.to_string(),
|
||||
optional: false,
|
||||
optional: opts.optional,
|
||||
script: script.to_string(),
|
||||
stage: stage.to_string(),
|
||||
wd: pkg_root_str.clone(),
|
||||
@@ -333,7 +343,7 @@ fn run_lifecycle_hook<R: Reporter>(
|
||||
message: LifecycleMessage::Exit {
|
||||
dep_path: opts.dep_path.to_string(),
|
||||
exit_code: status.code().unwrap_or(-1),
|
||||
optional: false,
|
||||
optional: opts.optional,
|
||||
stage: stage.to_string(),
|
||||
wd: pkg_root_str,
|
||||
},
|
||||
|
||||
@@ -56,6 +56,7 @@ fn lifecycle_emits_script_stdio_and_exit_in_order() {
|
||||
node_gyp_bin: None,
|
||||
scripts_prepend_node_path: ScriptsPrependNodePath::Never,
|
||||
script_shell: None,
|
||||
optional: false,
|
||||
};
|
||||
|
||||
let ran = run_postinstall_hooks::<RecordingReporter>(opts).expect("postinstall");
|
||||
@@ -118,6 +119,84 @@ fn lifecycle_emits_script_stdio_and_exit_in_order() {
|
||||
);
|
||||
}
|
||||
|
||||
/// `RunPostinstallHooks.optional` is stamped into both the `Script`
|
||||
/// and `Exit` `pnpm:lifecycle` events, matching upstream's
|
||||
/// `lifecycleLogger.debug({ optional, … })` shape at
|
||||
/// <https://github.com/pnpm/pnpm/blob/b4f8f47ac2/exec/lifecycle/src/runLifecycleHook.ts#L102>
|
||||
/// and `:165`. The two-bit truth on the wire lets the default
|
||||
/// reporter dispatch (e.g. quieting optional-dep noise) the same
|
||||
/// way it does against pnpm.
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn lifecycle_events_carry_optional_flag() {
|
||||
static EVENTS: Mutex<Vec<LogEvent>> = Mutex::new(Vec::new());
|
||||
EVENTS.lock().expect("lock").clear();
|
||||
|
||||
struct RecordingReporter;
|
||||
impl Reporter for RecordingReporter {
|
||||
fn emit(event: &LogEvent) {
|
||||
EVENTS.lock().expect("lock").push(event.clone());
|
||||
}
|
||||
}
|
||||
|
||||
let dir = tempdir().expect("create temp dir");
|
||||
let pkg_root = dir.path();
|
||||
let manifest = serde_json::json!({
|
||||
"name": "opt",
|
||||
"version": "1.0.0",
|
||||
"scripts": { "postinstall": "true" },
|
||||
});
|
||||
fs::write(pkg_root.join("package.json"), manifest.to_string()).expect("write manifest");
|
||||
|
||||
let extra_env: HashMap<String, String> = HashMap::new();
|
||||
let extra_bin_paths: Vec<std::path::PathBuf> = vec![];
|
||||
let opts = RunPostinstallHooks {
|
||||
dep_path: "/opt@1.0.0",
|
||||
pkg_root,
|
||||
root_modules_dir: pkg_root,
|
||||
init_cwd: pkg_root,
|
||||
extra_bin_paths: &extra_bin_paths,
|
||||
extra_env: &extra_env,
|
||||
node_execpath: None,
|
||||
npm_execpath: None,
|
||||
node_gyp_path: None,
|
||||
user_agent: None,
|
||||
unsafe_perm: true,
|
||||
node_gyp_bin: None,
|
||||
scripts_prepend_node_path: ScriptsPrependNodePath::Never,
|
||||
script_shell: None,
|
||||
optional: true,
|
||||
};
|
||||
|
||||
run_postinstall_hooks::<RecordingReporter>(opts).expect("postinstall");
|
||||
|
||||
let captured = EVENTS.lock().expect("lock").clone();
|
||||
let lifecycle_events: Vec<_> = captured
|
||||
.iter()
|
||||
.filter_map(|e| match e {
|
||||
LogEvent::Lifecycle(l) => Some(&l.message),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
dbg!(&lifecycle_events);
|
||||
let script_optional = lifecycle_events
|
||||
.iter()
|
||||
.find_map(|m| match m {
|
||||
LifecycleMessage::Script { optional, .. } => Some(*optional),
|
||||
_ => None,
|
||||
})
|
||||
.expect("must emit a Script event");
|
||||
assert!(script_optional, "Script event must carry optional=true");
|
||||
let exit_optional = lifecycle_events
|
||||
.iter()
|
||||
.find_map(|m| match m {
|
||||
LifecycleMessage::Exit { optional, .. } => Some(*optional),
|
||||
_ => None,
|
||||
})
|
||||
.expect("must emit an Exit event");
|
||||
assert!(exit_optional, "Exit event must carry optional=true");
|
||||
}
|
||||
|
||||
/// Failing scripts emit a Script event, the captured stdio, and an Exit
|
||||
/// event with the resolved non-zero exit code, then return a
|
||||
/// [`LifecycleScriptError::ScriptFailed`].
|
||||
@@ -159,6 +238,7 @@ fn lifecycle_emits_exit_with_nonzero_code_on_failure() {
|
||||
node_gyp_bin: None,
|
||||
scripts_prepend_node_path: ScriptsPrependNodePath::Never,
|
||||
script_shell: None,
|
||||
optional: false,
|
||||
};
|
||||
|
||||
let err = run_postinstall_hooks::<RecordingReporter>(opts).expect_err("script must fail");
|
||||
@@ -208,6 +288,7 @@ fn lifecycle_runs_under_silent_reporter() {
|
||||
node_gyp_bin: None,
|
||||
scripts_prepend_node_path: ScriptsPrependNodePath::Never,
|
||||
script_shell: None,
|
||||
optional: false,
|
||||
};
|
||||
|
||||
let ran = run_postinstall_hooks::<SilentReporter>(opts).expect("postinstall");
|
||||
@@ -241,6 +322,7 @@ fn missing_manifest_returns_false() {
|
||||
node_gyp_bin: None,
|
||||
scripts_prepend_node_path: ScriptsPrependNodePath::Never,
|
||||
script_shell: None,
|
||||
optional: false,
|
||||
};
|
||||
|
||||
let ran = run_postinstall_hooks::<SilentReporter>(opts).expect("missing manifest is OK");
|
||||
@@ -313,6 +395,7 @@ fn child_sees_stamped_npm_package_and_no_leaked_npm_config() {
|
||||
node_gyp_bin: None,
|
||||
scripts_prepend_node_path: ScriptsPrependNodePath::Never,
|
||||
script_shell: None,
|
||||
optional: false,
|
||||
};
|
||||
|
||||
let ran = run_postinstall_hooks::<SilentReporter>(opts).expect("postinstall");
|
||||
@@ -367,6 +450,7 @@ fn malformed_manifest_propagates_error() {
|
||||
node_gyp_bin: None,
|
||||
scripts_prepend_node_path: ScriptsPrependNodePath::Never,
|
||||
script_shell: None,
|
||||
optional: false,
|
||||
};
|
||||
|
||||
let err = run_postinstall_hooks::<SilentReporter>(opts).expect_err("malformed JSON must fail");
|
||||
|
||||
@@ -2,6 +2,9 @@ use crate::{PkgName, SnapshotDepRef};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
/// Per-instance snapshot information stored in the v9 `snapshots:` map.
|
||||
///
|
||||
/// An entry describes the wiring of one concrete installation of a package:
|
||||
@@ -24,4 +27,23 @@ pub struct SnapshotEntry {
|
||||
pub transitive_peer_dependencies: Option<Vec<String>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub patched: Option<bool>,
|
||||
|
||||
/// `true` when every path from any importer to this package
|
||||
/// goes through an `optionalDependencies` edge — folded by
|
||||
/// pnpm's resolver at install time and written verbatim into
|
||||
/// `snapshots[<key>].optional`. Pacquet trusts the precomputed
|
||||
/// flag rather than re-deriving from the importer graph,
|
||||
/// matching upstream's `lockfileToDepGraph` at
|
||||
/// <https://github.com/pnpm/pnpm/blob/b4f8f47ac2/deps/graph-builder/src/lockfileToDepGraph.ts#L315>.
|
||||
///
|
||||
/// `BuildModules` consults this flag to decide whether a failed
|
||||
/// build should be swallowed and reported via
|
||||
/// `pnpm:skipped-optional-dependency` (mirrors
|
||||
/// <https://github.com/pnpm/pnpm/blob/b4f8f47ac2/building/during-install/src/index.ts#L218-L240>).
|
||||
#[serde(default, skip_serializing_if = "is_false")]
|
||||
pub optional: bool,
|
||||
}
|
||||
|
||||
fn is_false(value: &bool) -> bool {
|
||||
!*value
|
||||
}
|
||||
|
||||
44
pacquet/crates/lockfile/src/snapshot_entry/tests.rs
Normal file
44
pacquet/crates/lockfile/src/snapshot_entry/tests.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
use super::SnapshotEntry;
|
||||
use crate::serialize_yaml;
|
||||
use text_block_macros::text_block;
|
||||
|
||||
/// `optional: true` round-trips through YAML deserialize → serialize.
|
||||
/// Source of truth is pnpm's v9 lockfile spec at
|
||||
/// <https://github.com/pnpm/spec/blob/834f2815cc/lockfile/9.0.md>;
|
||||
/// see `snapshots[<key>].optional` in the upstream type at
|
||||
/// <https://github.com/pnpm/pnpm/blob/b4f8f47ac2/lockfile/types/src/index.ts#L34-L39>.
|
||||
#[test]
|
||||
fn optional_true_round_trips() {
|
||||
let yaml = text_block! {
|
||||
"dependencies:"
|
||||
" foo: 1.2.3"
|
||||
"optional: true"
|
||||
};
|
||||
let entry: SnapshotEntry = serde_saphyr::from_str(yaml).expect("parse");
|
||||
assert!(entry.optional, "deserialize must capture optional: true");
|
||||
|
||||
let out = serialize_yaml::to_string(&entry).expect("serialize");
|
||||
assert!(out.contains("optional: true"), "serialize must round-trip optional: true:\n{out}");
|
||||
}
|
||||
|
||||
/// Absent `optional:` defaults to `false`, and `false` does NOT
|
||||
/// serialize (matching every other `optional?: true` field in
|
||||
/// upstream's lockfile types).
|
||||
#[test]
|
||||
fn optional_defaults_false_and_omits_when_false() {
|
||||
let yaml = text_block! {
|
||||
"dependencies:"
|
||||
" bar: 1.0.0"
|
||||
};
|
||||
let entry: SnapshotEntry = serde_saphyr::from_str(yaml).expect("parse");
|
||||
assert!(!entry.optional, "default must be false when absent");
|
||||
|
||||
let out = serialize_yaml::to_string(&entry).expect("serialize");
|
||||
// Match the exact key spelling (`optional:` followed by a space
|
||||
// or a newline) so a future fixture containing
|
||||
// `optionalDependencies:` doesn't fool this assertion.
|
||||
assert!(
|
||||
!out.contains("optional: ") && !out.contains("optional:\n"),
|
||||
"the `optional` key must not be serialized when false:\n{out}",
|
||||
);
|
||||
}
|
||||
@@ -6,7 +6,10 @@ use pacquet_executor::{
|
||||
};
|
||||
use pacquet_lockfile::{PackageKey, ProjectSnapshot, SnapshotEntry};
|
||||
use pacquet_package_manifest::pkg_requires_build;
|
||||
use pacquet_reporter::Reporter;
|
||||
use pacquet_reporter::{
|
||||
LogEvent, LogLevel, Reporter, SkippedOptionalDependencyLog, SkippedOptionalPackage,
|
||||
SkippedOptionalReason,
|
||||
};
|
||||
use std::{
|
||||
collections::{BTreeSet, HashMap},
|
||||
fs,
|
||||
@@ -217,7 +220,9 @@ impl<'a> BuildModules<'a> {
|
||||
continue;
|
||||
}
|
||||
|
||||
run_postinstall_hooks::<R>(RunPostinstallHooks {
|
||||
let optional = snapshots.get(&snapshot_key).is_some_and(|entry| entry.optional);
|
||||
|
||||
let result = run_postinstall_hooks::<R>(RunPostinstallHooks {
|
||||
dep_path: &snapshot_key.to_string(),
|
||||
pkg_root: &pkg_dir,
|
||||
root_modules_dir: modules_dir,
|
||||
@@ -236,8 +241,34 @@ impl<'a> BuildModules<'a> {
|
||||
node_gyp_bin: None,
|
||||
scripts_prepend_node_path: ScriptsPrependNodePath::Never,
|
||||
script_shell: None,
|
||||
})
|
||||
.map_err(BuildModulesError::LifecycleScript)?;
|
||||
optional,
|
||||
});
|
||||
|
||||
if let Err(err) = result {
|
||||
if optional {
|
||||
// Mirrors `building/during-install/src/index.ts:226-238`:
|
||||
// a build failure on an optional dep is logged
|
||||
// through the `pnpm:skipped-optional-dependency`
|
||||
// channel and swallowed so the install can
|
||||
// continue. The `package.id` field upstream is
|
||||
// `depNode.dir`; we use the same.
|
||||
R::emit(&LogEvent::SkippedOptionalDependency(
|
||||
SkippedOptionalDependencyLog {
|
||||
level: LogLevel::Debug,
|
||||
details: Some(err.to_string()),
|
||||
package: SkippedOptionalPackage {
|
||||
id: pkg_dir.to_string_lossy().into_owned(),
|
||||
name: name.clone(),
|
||||
version: version.clone(),
|
||||
},
|
||||
prefix: lockfile_dir.to_string_lossy().into_owned(),
|
||||
reason: SkippedOptionalReason::BuildFailure,
|
||||
},
|
||||
));
|
||||
continue;
|
||||
}
|
||||
return Err(BuildModulesError::LifecycleScript(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,9 @@ use pacquet_lockfile::{
|
||||
PackageKey, PkgName, PkgVerPeer, ProjectSnapshot, ResolvedDependencyMap,
|
||||
ResolvedDependencySpec, SnapshotEntry,
|
||||
};
|
||||
use pacquet_reporter::{IgnoredScriptsLog, LogEvent, Reporter, SilentReporter};
|
||||
use pacquet_reporter::{
|
||||
IgnoredScriptsLog, LogEvent, Reporter, SilentReporter, SkippedOptionalReason,
|
||||
};
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
@@ -259,6 +261,148 @@ fn build_modules_excludes_explicit_deny_from_ignored() {
|
||||
);
|
||||
}
|
||||
|
||||
/// Optional dep whose postinstall fails must be reported through the
|
||||
/// `pnpm:skipped-optional-dependency` channel (reason `build_failure`)
|
||||
/// and NOT abort the install. Mirrors upstream
|
||||
/// `building/during-install/src/index.ts:218-240` and the spirit of
|
||||
/// `'do not fail on an optional dependency that has a non-optional
|
||||
/// dependency with a failing postinstall script'` at
|
||||
/// <https://github.com/pnpm/pnpm/blob/b4f8f47ac2/installing/deps-installer/test/install/optionalDependencies.ts#L563-L572>.
|
||||
///
|
||||
/// The test uses the upstream fixture `@pnpm.e2e/failing-postinstall@1.0.0`
|
||||
/// (script body verbatim from `/Volumes/src/pnpm/registry-mock/packages/failing-postinstall/package.json`)
|
||||
/// so the failure mode is exactly the one upstream's optional-dep
|
||||
/// tests exercise.
|
||||
///
|
||||
/// Unix-gated because the upstream script (`echo hello && echo world && exit 1`)
|
||||
/// is POSIX shell syntax. The cmd-on-Windows path picks a different
|
||||
/// shell — `pacquet_executor::select_shell` (tested in the executor
|
||||
/// crate's `shell::tests`) covers the shell-selection branches in
|
||||
/// isolation.
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn do_not_fail_on_optional_dep_with_failing_postinstall() {
|
||||
static EVENTS: Mutex<Vec<LogEvent>> = Mutex::new(Vec::new());
|
||||
EVENTS.lock().expect("lock").clear();
|
||||
|
||||
struct RecordingReporter;
|
||||
impl Reporter for RecordingReporter {
|
||||
fn emit(event: &LogEvent) {
|
||||
EVENTS.lock().expect("lock").push(event.clone());
|
||||
}
|
||||
}
|
||||
|
||||
let pkg_key = key("@pnpm.e2e/failing-postinstall", "1.0.0");
|
||||
let mut optional_snapshot = SnapshotEntry::default();
|
||||
optional_snapshot.optional = true;
|
||||
let snapshots = HashMap::from([(pkg_key.clone(), optional_snapshot)]);
|
||||
let importers = root_importers(&[("@pnpm.e2e/failing-postinstall", "1.0.0")]);
|
||||
// `dangerouslyAllowAllBuilds` so the policy lets the failing
|
||||
// script through to actually run — this test exercises the
|
||||
// build-failure path, not the policy gate.
|
||||
let policy = AllowBuildPolicy::new(rules([]), true);
|
||||
|
||||
let virtual_store_dir = tempdir().expect("create temp dir");
|
||||
let modules_dir = tempdir().expect("create temp dir");
|
||||
let lockfile_dir = tempdir().expect("create temp dir");
|
||||
|
||||
create_failing_postinstall_fixture(virtual_store_dir.path(), &pkg_key);
|
||||
|
||||
let ignored = BuildModules {
|
||||
virtual_store_dir: virtual_store_dir.path(),
|
||||
modules_dir: modules_dir.path(),
|
||||
lockfile_dir: lockfile_dir.path(),
|
||||
snapshots: Some(&snapshots),
|
||||
importers: &importers,
|
||||
allow_build_policy: &policy,
|
||||
}
|
||||
.run::<RecordingReporter>()
|
||||
.expect("optional build failure must NOT abort the install");
|
||||
dbg!(&ignored);
|
||||
|
||||
let captured = EVENTS.lock().expect("lock").clone();
|
||||
dbg!(&captured);
|
||||
let skipped_event = captured
|
||||
.iter()
|
||||
.find_map(|e| match e {
|
||||
LogEvent::SkippedOptionalDependency(log) => Some(log),
|
||||
_ => None,
|
||||
})
|
||||
.expect("must emit pnpm:skipped-optional-dependency");
|
||||
assert_eq!(skipped_event.reason, SkippedOptionalReason::BuildFailure);
|
||||
assert_eq!(skipped_event.package.name, "@pnpm.e2e/failing-postinstall");
|
||||
assert_eq!(skipped_event.package.version, "1.0.0");
|
||||
assert!(skipped_event.details.is_some(), "details must carry the error toString");
|
||||
}
|
||||
|
||||
/// Mirrors `'fail on a package with failing postinstall if the
|
||||
/// package is both an optional and non-optional dependency'` at
|
||||
/// <https://github.com/pnpm/pnpm/blob/b4f8f47ac2/installing/deps-installer/test/install/optionalDependencies.ts#L574-L591>.
|
||||
///
|
||||
/// Upstream's resolver folds reachability ALL-paths-optional, so a
|
||||
/// package reachable through any non-optional edge has
|
||||
/// `snapshots[...].optional = false` in the lockfile (cf.
|
||||
/// `installing/deps-resolver/src/resolveDependencies.ts:1605-1610`).
|
||||
/// `BuildModules` then propagates the build failure rather than
|
||||
/// swallowing it. Pacquet trusts the precomputed flag; this test
|
||||
/// pins the propagation branch by supplying the same fixture with
|
||||
/// `optional: false`, which is the lockfile shape upstream produces
|
||||
/// for the dual-reachability case.
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn fail_when_failing_postinstall_is_required() {
|
||||
let pkg_key = key("@pnpm.e2e/failing-postinstall", "1.0.0");
|
||||
// `optional: false` — pacquet's analog of upstream's
|
||||
// ALL-paths-optional fold concluding the dep is required.
|
||||
let snapshots = HashMap::from([(pkg_key.clone(), SnapshotEntry::default())]);
|
||||
let importers = root_importers(&[("@pnpm.e2e/failing-postinstall", "1.0.0")]);
|
||||
let policy = AllowBuildPolicy::new(rules([]), true);
|
||||
|
||||
let virtual_store_dir = tempdir().expect("create temp dir");
|
||||
let modules_dir = tempdir().expect("create temp dir");
|
||||
let lockfile_dir = tempdir().expect("create temp dir");
|
||||
|
||||
create_failing_postinstall_fixture(virtual_store_dir.path(), &pkg_key);
|
||||
|
||||
let err = BuildModules {
|
||||
virtual_store_dir: virtual_store_dir.path(),
|
||||
modules_dir: modules_dir.path(),
|
||||
lockfile_dir: lockfile_dir.path(),
|
||||
snapshots: Some(&snapshots),
|
||||
importers: &importers,
|
||||
allow_build_policy: &policy,
|
||||
}
|
||||
.run::<SilentReporter>()
|
||||
.expect_err("required build failure must propagate");
|
||||
eprintln!("ERR: {err}");
|
||||
assert!(matches!(err, crate::build_modules::BuildModulesError::LifecycleScript(_)));
|
||||
}
|
||||
|
||||
/// Materialize a package fixture whose contents are byte-identical
|
||||
/// to upstream's `@pnpm.e2e/failing-postinstall@1.0.0` at
|
||||
/// `/Volumes/src/pnpm/registry-mock/packages/failing-postinstall/package.json`.
|
||||
/// Reusing the upstream script body (`echo hello && echo world && exit 1`)
|
||||
/// keeps the failure mode and exit code identical to what
|
||||
/// `optionalDependencies.ts` exercises against the live mock
|
||||
/// registry, without dragging the lockfile-with-real-integrity
|
||||
/// machinery into a `BuildModules`-unit test.
|
||||
fn create_failing_postinstall_fixture(virtual_store_dir: &Path, key: &PackageKey) -> PathBuf {
|
||||
let key_str = key.without_peer().to_string();
|
||||
let name_version = key_str.strip_prefix('/').unwrap_or(&key_str);
|
||||
let at_idx = name_version.rfind('@').unwrap_or(name_version.len());
|
||||
let pkg_name = &name_version[..at_idx];
|
||||
let store_name = name_version.replace('/', "+");
|
||||
let pkg_dir = virtual_store_dir.join(&store_name).join("node_modules").join(pkg_name);
|
||||
fs::create_dir_all(&pkg_dir).expect("create pkg dir");
|
||||
let manifest = serde_json::json!({
|
||||
"name": pkg_name,
|
||||
"version": name_version[at_idx + 1..].to_string(),
|
||||
"scripts": { "postinstall": "echo hello && echo world && exit 1" },
|
||||
});
|
||||
fs::write(pkg_dir.join("package.json"), manifest.to_string()).expect("write manifest");
|
||||
pkg_dir
|
||||
}
|
||||
|
||||
/// Recording fake confirms `pnpm:ignored-scripts` is the right channel
|
||||
/// for the package list. The frozen-install path emits this once after
|
||||
/// `BuildModules::run` returns; this test exercises the equivalent
|
||||
|
||||
@@ -34,6 +34,7 @@ fn snap(deps: &[(&str, &str)]) -> SnapshotEntry {
|
||||
optional_dependencies: None,
|
||||
transitive_peer_dependencies: None,
|
||||
patched: None,
|
||||
optional: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -107,6 +107,7 @@ pub fn build_package_snapshot(
|
||||
optional_dependencies: None,
|
||||
transitive_peer_dependencies: None,
|
||||
patched: None,
|
||||
optional: false,
|
||||
};
|
||||
|
||||
Ok(BuiltSnapshot { package_key, metadata, snapshot })
|
||||
|
||||
@@ -649,3 +649,79 @@ async fn install_writes_modules_yaml() {
|
||||
|
||||
drop(dir);
|
||||
}
|
||||
|
||||
/// Ports `'do not fail on an optional dependency that has a non-optional
|
||||
/// dependency with a failing postinstall script'` at
|
||||
/// <https://github.com/pnpm/pnpm/blob/b4f8f47ac2/installing/deps-installer/test/install/optionalDependencies.ts#L563-L572>.
|
||||
///
|
||||
/// Resolves `@pnpm.e2e/has-failing-postinstall-dep@1.0.0` as an
|
||||
/// optional dependency through the live registry-mock instance. The
|
||||
/// transitive `@pnpm.e2e/failing-postinstall@1.0.0` has a
|
||||
/// `postinstall` that exits non-zero. Pacquet's
|
||||
/// `frozen_lockfile=false` path stops at extraction (script execution
|
||||
/// lives behind `BuildModules` in the frozen-lockfile branch —
|
||||
/// `BuildModules` itself is unit-tested against the same fixture in
|
||||
/// `crate::build_modules::tests::do_not_fail_on_optional_dep_with_failing_postinstall`).
|
||||
/// This test pins the fetch + extract behavior on the optional edge:
|
||||
/// both packages must land in the virtual store and the install must
|
||||
/// NOT abort, matching the upstream expectation that `addDependenciesToPackage`
|
||||
/// resolves.
|
||||
#[tokio::test]
|
||||
async fn install_optional_failing_postinstall_dep_via_registry_mock_succeeds() {
|
||||
let mock_instance = AutoMockInstance::load_or_init();
|
||||
|
||||
let dir = tempdir().unwrap();
|
||||
let store_dir = dir.path().join("pacquet-store");
|
||||
let project_root = dir.path().join("project");
|
||||
let modules_dir = project_root.join("node_modules");
|
||||
let virtual_store_dir = modules_dir.join(".pacquet");
|
||||
|
||||
let manifest_path = dir.path().join("package.json");
|
||||
let mut manifest = PackageManifest::create_if_needed(manifest_path.clone()).unwrap();
|
||||
manifest
|
||||
.add_dependency("@pnpm.e2e/has-failing-postinstall-dep", "1.0.0", DependencyGroup::Optional)
|
||||
.unwrap();
|
||||
manifest.save().unwrap();
|
||||
|
||||
let mut config = Npmrc::new();
|
||||
config.store_dir = store_dir.into();
|
||||
config.modules_dir = modules_dir.to_path_buf();
|
||||
config.virtual_store_dir = virtual_store_dir.to_path_buf();
|
||||
config.registry = mock_instance.url();
|
||||
let config = config.leak();
|
||||
|
||||
Install {
|
||||
tarball_mem_cache: &Default::default(),
|
||||
http_client: &Default::default(),
|
||||
config,
|
||||
manifest: &manifest,
|
||||
lockfile: None,
|
||||
dependency_groups: [DependencyGroup::Prod, DependencyGroup::Optional],
|
||||
frozen_lockfile: false,
|
||||
resolved_packages: &Default::default(),
|
||||
}
|
||||
.run::<SilentReporter>()
|
||||
.await
|
||||
.expect("optional dep with failing transitive postinstall must NOT abort the install");
|
||||
|
||||
// Both the wrapper and the transitive must reach the virtual store.
|
||||
assert!(
|
||||
is_symlink_or_junction(
|
||||
&project_root.join("node_modules/@pnpm.e2e/has-failing-postinstall-dep"),
|
||||
)
|
||||
.unwrap(),
|
||||
"wrapper symlink missing",
|
||||
);
|
||||
assert!(
|
||||
project_root
|
||||
.join("node_modules/.pacquet/@pnpm.e2e+has-failing-postinstall-dep@1.0.0")
|
||||
.is_dir(),
|
||||
"wrapper virtual-store dir missing",
|
||||
);
|
||||
assert!(
|
||||
project_root.join("node_modules/.pacquet/@pnpm.e2e+failing-postinstall@1.0.0").is_dir(),
|
||||
"transitive `failing-postinstall` must be extracted to the virtual store",
|
||||
);
|
||||
|
||||
drop((dir, mock_instance));
|
||||
}
|
||||
|
||||
@@ -149,6 +149,19 @@ pub enum LogEvent {
|
||||
/// Emit site: <https://github.com/pnpm/pnpm/blob/80037699fb/installing/deps-installer/src/install/index.ts#L414>.
|
||||
#[serde(rename = "pnpm:ignored-scripts")]
|
||||
IgnoredScripts(IgnoredScriptsLog),
|
||||
|
||||
/// One per optional-dependency that pnpm decided to skip rather
|
||||
/// than fail the install over. Reason discriminates the cause —
|
||||
/// pacquet currently only emits `build_failure` (from
|
||||
/// `BuildModules` when a postinstall fails on an optional dep);
|
||||
/// the `unsupported_engine` / `unsupported_platform` /
|
||||
/// `resolution_failure` reasons upstream uses come from earlier
|
||||
/// phases that haven't landed in pacquet yet.
|
||||
///
|
||||
/// Upstream: <https://github.com/pnpm/pnpm/blob/b4f8f47ac2/core/core-loggers/src/skippedOptionalDependencyLogger.ts>.
|
||||
/// Emit site (build_failure): <https://github.com/pnpm/pnpm/blob/b4f8f47ac2/building/during-install/src/index.ts#L218-L240>.
|
||||
#[serde(rename = "pnpm:skipped-optional-dependency")]
|
||||
SkippedOptionalDependency(SkippedOptionalDependencyLog),
|
||||
}
|
||||
|
||||
/// `pnpm:context` payload.
|
||||
@@ -517,6 +530,60 @@ pub struct IgnoredScriptsLog {
|
||||
pub package_names: Vec<String>,
|
||||
}
|
||||
|
||||
/// `pnpm:skipped-optional-dependency` payload.
|
||||
///
|
||||
/// Upstream's `SkippedOptionalDependencyMessage` is a discriminated
|
||||
/// union over `reason` with two distinct `package` shapes:
|
||||
/// `build_failure` / `unsupported_engine` / `unsupported_platform`
|
||||
/// all carry `package: { id, name, version }`; `resolution_failure`
|
||||
/// carries `package: { name?, version?, bareSpecifier }` with no
|
||||
/// `id`. This struct models only the **first** shape — the three
|
||||
/// reasons that share `{ id, name, version }`. The
|
||||
/// `ResolutionFailure` variant in [`SkippedOptionalReason`] is
|
||||
/// declared for forward compatibility on the enum side, but its
|
||||
/// distinct `package` shape means a `ResolutionFailure` emit will
|
||||
/// require a sibling struct (or a `#[serde(untagged)]` enum
|
||||
/// substituting for `SkippedOptionalDependencyLog`) — not just
|
||||
/// flipping the `reason` value. Refactoring is deferred until
|
||||
/// pacquet actually has a resolver-time emit site to produce that
|
||||
/// payload.
|
||||
///
|
||||
/// `parents` is a TODO upstream too (see
|
||||
/// `during-install/src/index.ts:227`) and is omitted here.
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct SkippedOptionalDependencyLog {
|
||||
pub level: LogLevel,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub details: Option<String>,
|
||||
pub package: SkippedOptionalPackage,
|
||||
pub prefix: String,
|
||||
pub reason: SkippedOptionalReason,
|
||||
}
|
||||
|
||||
/// Package identifier carried on a [`SkippedOptionalDependencyLog`].
|
||||
/// Matches the upstream "non-resolution-failure" branch of
|
||||
/// `SkippedOptionalDependencyMessage` at
|
||||
/// <https://github.com/pnpm/pnpm/blob/b4f8f47ac2/core/core-loggers/src/skippedOptionalDependencyLogger.ts#L15-L19>.
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct SkippedOptionalPackage {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub version: String,
|
||||
}
|
||||
|
||||
/// Discriminator on a [`SkippedOptionalDependencyLog`]. Only
|
||||
/// `BuildFailure` lands at pacquet's current emit sites; the others
|
||||
/// are kept in the enum for forward compatibility so callers don't
|
||||
/// have to widen the type when more reasons are wired up.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum SkippedOptionalReason {
|
||||
BuildFailure,
|
||||
UnsupportedEngine,
|
||||
UnsupportedPlatform,
|
||||
ResolutionFailure,
|
||||
}
|
||||
|
||||
/// Severity level on the [bunyan]-shaped envelope.
|
||||
///
|
||||
/// pnpm's logger uses the [bole] library, which writes one of these strings
|
||||
|
||||
@@ -9,7 +9,8 @@ use crate::{
|
||||
GetHostName, IgnoredScriptsLog, LifecycleLog, LifecycleMessage, LifecycleStdio, LogEvent,
|
||||
LogLevel, PackageImportMethod, PackageImportMethodLog, PackageManifestLog,
|
||||
PackageManifestMessage, ProgressLog, ProgressMessage, RealApi, RemovedRoot, Reporter,
|
||||
RequestRetryError, RequestRetryLog, RootLog, RootMessage, SilentReporter, Stage, StageLog,
|
||||
RequestRetryError, RequestRetryLog, RootLog, RootMessage, SilentReporter,
|
||||
SkippedOptionalDependencyLog, SkippedOptionalPackage, SkippedOptionalReason, Stage, StageLog,
|
||||
StatsLog, StatsMessage, SummaryLog,
|
||||
};
|
||||
|
||||
@@ -576,6 +577,81 @@ fn ignored_scripts_event_matches_pnpm_wire_shape() {
|
||||
assert_eq!(json["packageNames"], serde_json::json!(["foo@1.0.0", "bar@2.0.0"]));
|
||||
}
|
||||
|
||||
/// `pnpm:skipped-optional-dependency` matches upstream's wire
|
||||
/// shape: top-level `details`, `package: { id, name, version }`,
|
||||
/// `prefix`, and `reason` (snake_case). Mirrors
|
||||
/// `SkippedOptionalDependencyMessage` at
|
||||
/// <https://github.com/pnpm/pnpm/blob/b4f8f47ac2/core/core-loggers/src/skippedOptionalDependencyLogger.ts>.
|
||||
#[test]
|
||||
fn skipped_optional_dependency_event_matches_pnpm_wire_shape() {
|
||||
let event = LogEvent::SkippedOptionalDependency(SkippedOptionalDependencyLog {
|
||||
level: LogLevel::Debug,
|
||||
details: Some("build failed: exit code 1".to_string()),
|
||||
package: SkippedOptionalPackage {
|
||||
id: "/foo/1.0.0".to_string(),
|
||||
name: "foo".to_string(),
|
||||
version: "1.0.0".to_string(),
|
||||
},
|
||||
prefix: "/projects/x".to_string(),
|
||||
reason: SkippedOptionalReason::BuildFailure,
|
||||
});
|
||||
let envelope = Envelope { time: 1_700_000_000_000, hostname: "host", pid: 4242, event: &event };
|
||||
let json: Value = envelope
|
||||
.pipe_ref(serde_json::to_string)
|
||||
.expect("serialize envelope")
|
||||
.pipe_as_ref(serde_json::from_str)
|
||||
.expect("parse JSON");
|
||||
dbg!(&json);
|
||||
assert_eq!(json["name"], "pnpm:skipped-optional-dependency");
|
||||
assert_eq!(json["level"], "debug");
|
||||
assert_eq!(json["reason"], "build_failure");
|
||||
assert_eq!(json["details"], "build failed: exit code 1");
|
||||
assert_eq!(json["prefix"], "/projects/x");
|
||||
assert_eq!(json["package"]["id"], "/foo/1.0.0");
|
||||
assert_eq!(json["package"]["name"], "foo");
|
||||
assert_eq!(json["package"]["version"], "1.0.0");
|
||||
}
|
||||
|
||||
/// `details` is optional upstream and must be omitted from the wire
|
||||
/// when absent (`skip_serializing_if = "Option::is_none"`).
|
||||
#[test]
|
||||
fn skipped_optional_omits_absent_details() {
|
||||
let event = LogEvent::SkippedOptionalDependency(SkippedOptionalDependencyLog {
|
||||
level: LogLevel::Debug,
|
||||
details: None,
|
||||
package: SkippedOptionalPackage {
|
||||
id: "/bar/2.0.0".to_string(),
|
||||
name: "bar".to_string(),
|
||||
version: "2.0.0".to_string(),
|
||||
},
|
||||
prefix: "/projects/y".to_string(),
|
||||
reason: SkippedOptionalReason::BuildFailure,
|
||||
});
|
||||
let envelope = Envelope { time: 1_700_000_000_000, hostname: "host", pid: 4242, event: &event };
|
||||
let json: Value = envelope
|
||||
.pipe_ref(serde_json::to_string)
|
||||
.expect("serialize envelope")
|
||||
.pipe_as_ref(serde_json::from_str)
|
||||
.expect("parse JSON");
|
||||
assert!(json.get("details").is_none(), "details must be omitted when absent, got {json:?}");
|
||||
}
|
||||
|
||||
/// All four reason variants serialize as the snake_case strings
|
||||
/// pnpm's reporter dispatches on.
|
||||
#[test]
|
||||
fn skipped_optional_reason_serializes_in_pnpm_form() {
|
||||
let cases = [
|
||||
(SkippedOptionalReason::BuildFailure, "build_failure"),
|
||||
(SkippedOptionalReason::UnsupportedEngine, "unsupported_engine"),
|
||||
(SkippedOptionalReason::UnsupportedPlatform, "unsupported_platform"),
|
||||
(SkippedOptionalReason::ResolutionFailure, "resolution_failure"),
|
||||
];
|
||||
for (reason, expected) in cases {
|
||||
let json = serde_json::to_string(&reason).expect("serialize reason");
|
||||
assert_eq!(json, format!("\"{expected}\""), "{reason:?} must serialize as {expected:?}");
|
||||
}
|
||||
}
|
||||
|
||||
/// Phase markers serialize as the snake_case strings pnpm uses.
|
||||
#[test]
|
||||
fn stage_phases_serialize_in_pnpm_form() {
|
||||
|
||||
67
pacquet/tasks/registry-mock/launch.mjs
Normal file
67
pacquet/tasks/registry-mock/launch.mjs
Normal file
@@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env node
|
||||
// Wrapper for `@pnpm/registry-mock` that drives the server via the
|
||||
// programmatic `start({ useNodeVersion, listen })` entry point
|
||||
// instead of the bare CLI. Mirrors pnpm's own jest globalSetup at
|
||||
// <https://github.com/pnpm/pnpm/blob/b4f8f47ac2/__utils__/jest-config/with-registry/globalSetup.js>.
|
||||
//
|
||||
// Why a wrapper: verdaccio 5.33 (the version `@pnpm/registry-mock@6`
|
||||
// bundles) rejects its own auto-generated 64-character storage
|
||||
// secret on Node 22+ with `Invalid storage secret key length, must
|
||||
// be 32 characters long but is 64`. Pnpm pins
|
||||
// `useNodeVersion: '20.16.0'` so verdaccio runs under a Node that
|
||||
// skips that enforcement. The default CLI export of
|
||||
// `@pnpm/registry-mock` does NOT pass `useNodeVersion` through, so
|
||||
// the CLI launch fails on a modern host Node. The programmatic
|
||||
// `start()` does.
|
||||
//
|
||||
// Pacquet's Rust launcher (`tasks/registry-mock/src/node_registry_mock.rs`)
|
||||
// invokes this script via `node`. Calling pattern:
|
||||
// `node launch.mjs prepare` — publish fixtures
|
||||
// `node launch.mjs` (or any other arg) — launch the server; port
|
||||
// comes from
|
||||
// `PNPM_REGISTRY_MOCK_PORT`.
|
||||
|
||||
import { prepare, start } from '@pnpm/registry-mock'
|
||||
|
||||
const NODE_RUNTIME = '20.16.0'
|
||||
|
||||
if (process.argv[2] === 'prepare') {
|
||||
prepare()
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
const listen = process.env.PNPM_REGISTRY_MOCK_PORT
|
||||
if (!listen) {
|
||||
console.error('PNPM_REGISTRY_MOCK_PORT must be set when launching the mock')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const server = start({
|
||||
useNodeVersion: NODE_RUNTIME,
|
||||
stdio: 'inherit',
|
||||
listen,
|
||||
})
|
||||
|
||||
// Shell-convention exit code when the child was killed by a signal:
|
||||
// 128 + signal number. Anything Node knows the number for; fall
|
||||
// back to 1 otherwise.
|
||||
const SIGNAL_NUMBERS = { SIGHUP: 1, SIGINT: 2, SIGTERM: 15 }
|
||||
|
||||
server.on('exit', (code, signal) => {
|
||||
if (signal != null) {
|
||||
process.exit(128 + (SIGNAL_NUMBERS[signal] ?? 1))
|
||||
} else {
|
||||
process.exit(code ?? 0)
|
||||
}
|
||||
})
|
||||
|
||||
// Forward the usual termination signals to the child so it can shut
|
||||
// down cleanly. The child's `exit` handler above is what actually
|
||||
// terminates the wrapper — we do NOT re-raise the signal to ourselves
|
||||
// (re-raising would either hit our own handler in a loop or, after
|
||||
// removing it, race with the child's exit propagation).
|
||||
for (const sig of Object.keys(SIGNAL_NUMBERS)) {
|
||||
process.on(sig, () => {
|
||||
if (!server.killed) server.kill(sig)
|
||||
})
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"devDependencies": {
|
||||
"@pnpm/registry-mock": "^3.16.0"
|
||||
"@pnpm/registry-mock": "^6.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
420
pacquet/tasks/registry-mock/pnpm-lock.yaml
generated
420
pacquet/tasks/registry-mock/pnpm-lock.yaml
generated
@@ -9,8 +9,8 @@ importers:
|
||||
.:
|
||||
devDependencies:
|
||||
'@pnpm/registry-mock':
|
||||
specifier: ^3.16.0
|
||||
version: 3.50.0(typanion@3.14.0)
|
||||
specifier: ^6.0.0
|
||||
version: 6.0.0(verdaccio@5.33.0(typanion@3.14.0))
|
||||
|
||||
packages:
|
||||
|
||||
@@ -30,10 +30,26 @@ packages:
|
||||
resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
'@pnpm/registry-mock@3.50.0':
|
||||
resolution: {integrity: sha512-/YGi3OCMWXkk8JwUbVOQo5Ws89yA9ub+jnYboAgxUJ84K6a2m3xZiRn1im0zxC7L6F63Xx++ZuGrlwKEWHqf+A==}
|
||||
engines: {node: '>=10.13'}
|
||||
'@pnpm/registry-mock@6.0.0':
|
||||
resolution: {integrity: sha512-OdeuaWEcAg0yFQacHwujnTckr5muhZ5P2PRoxoMMtiGDoOVKtEXqb4mXkz9VwsFeuSrj/3bbI5v42SVNZr+EHw==}
|
||||
engines: {node: '>=18.12'}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
verdaccio: ^5.20.1 || ^6.1.6
|
||||
|
||||
'@postman/form-data@3.1.1':
|
||||
resolution: {integrity: sha512-vjh8Q2a8S6UCm/KKs31XFJqEEgmbjBmpPNVV2eVav6905wyFAwaUOBGA1NPBI4ERH9MMZc6w0umFgM6WbEPMdg==}
|
||||
engines: {node: '>= 6'}
|
||||
|
||||
'@postman/tough-cookie@4.1.3-postman.1':
|
||||
resolution: {integrity: sha512-txpgUqZOnWYnUHZpHjkfb0IwVH4qJmyq77pPnJLlfhMtdCLMFTEeQHlzQiK906aaNCe4NEB5fGJHo9uzGbFMeA==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
'@postman/tunnel-agent@0.6.8':
|
||||
resolution: {integrity: sha512-2U42SmZW5G+suEcS++zB94sBWNO4qD4bvETGFRFDTqSpYl5ksfjcPqzYpgQgXgUmb6dfz+fAGbkcRamounGm0w==}
|
||||
|
||||
'@qiwi/npm-types@1.0.3':
|
||||
resolution: {integrity: sha512-fHUud+Fo8JiGOPMmnVaOYd/crEnBM+qB8qXrSRz1AkYZAj8BtudFYcsiFweL6DcYXguq7+iXKdzx42dGCEPHiQ==}
|
||||
|
||||
'@verdaccio/auth@8.0.0-next-8.1':
|
||||
resolution: {integrity: sha512-sPmHdnYuRSMgABCsTJEfz8tb/smONsWVg0g4KK2QycyYZ/A+RwZLV1JLiQb4wzu9zvS0HSloqWqkWlyNHW3mtw==}
|
||||
@@ -51,6 +67,10 @@ packages:
|
||||
resolution: {integrity: sha512-kQRCB2wgXEh8H88G51eQgAFK9IxmnBtkQ8sY5FbmB6PbBkyHrbGcCp+2mtRqqo36j0W1VAlfM3XzoknMy6qQnw==}
|
||||
engines: {node: '>=14'}
|
||||
|
||||
'@verdaccio/file-locking@10.3.0':
|
||||
resolution: {integrity: sha512-FE5D5H4wy/nhgR/d2J5e1Na9kScj2wMjlLPBHz7XF4XZAVSRdm45+kL3ZmrfA6b2HTADP/uH7H05/cnAYW8bhw==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
'@verdaccio/file-locking@10.3.1':
|
||||
resolution: {integrity: sha512-oqYLfv3Yg3mAgw9qhASBpjD50osj2AX4IwbkUtyuhhKGyoFU9eZdrbeW6tpnqUnj6yBMtAPm2eGD4BwQuX400g==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -140,8 +160,8 @@ packages:
|
||||
ajv@8.17.1:
|
||||
resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==}
|
||||
|
||||
anonymous-npm-registry-client@0.2.0:
|
||||
resolution: {integrity: sha512-ym3GCDQU8B6PZrswCvanRiWoSg2QrrlPwoRlMr4oCpGvyK2KlwTujdCZfxrGapqxrqEY3TpxEqLf+7PhFnyaLA==}
|
||||
anonymous-npm-registry-client@0.3.2:
|
||||
resolution: {integrity: sha512-Cw96dHAq3W/xVBNkPwLiTZnIbLgNXbmIxFAh63x8LjeaRVEGjMnZ6X/u7xSuqRqbig5jrYWJp5UTgRoXKUotVQ==}
|
||||
|
||||
ansi-regex@2.1.1:
|
||||
resolution: {integrity: sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==}
|
||||
@@ -255,6 +275,13 @@ packages:
|
||||
bcryptjs@2.4.3:
|
||||
resolution: {integrity: sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==}
|
||||
|
||||
bluebird@2.11.0:
|
||||
resolution: {integrity: sha512-UfFSr22dmHPQqPP9XWHRhq+gWnHCYguQGkXQlbyPtW5qTnhFWA8/iXg765tH0cAjy7l/zPJ1aBTO0g5XgA7kvQ==}
|
||||
|
||||
body-parser@1.20.1:
|
||||
resolution: {integrity: sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==}
|
||||
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
|
||||
|
||||
body-parser@1.20.3:
|
||||
resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==}
|
||||
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
|
||||
@@ -269,6 +296,9 @@ packages:
|
||||
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
brotli@1.3.3:
|
||||
resolution: {integrity: sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==}
|
||||
|
||||
browserify-zlib@0.1.4:
|
||||
resolution: {integrity: sha512-19OEpq7vWgsH6WkvkBJQDFvJS1uPcbFOQ4v9CU839dO+ZZXUZO6XpE6hNCqvlIIj+4fZvRiJ6DsAQ382GwiyTQ==}
|
||||
|
||||
@@ -330,9 +360,9 @@ packages:
|
||||
concat-map@0.0.1:
|
||||
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
|
||||
|
||||
concat-stream@1.6.2:
|
||||
resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==}
|
||||
engines: {'0': node >= 0.8}
|
||||
concat-stream@2.0.0:
|
||||
resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==}
|
||||
engines: {'0': node >= 6.0}
|
||||
|
||||
console-control-strings@1.1.0:
|
||||
resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==}
|
||||
@@ -348,6 +378,10 @@ packages:
|
||||
cookie-signature@1.0.6:
|
||||
resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==}
|
||||
|
||||
cookie@0.5.0:
|
||||
resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
cookie@0.6.0:
|
||||
resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==}
|
||||
engines: {node: '>= 0.6'}
|
||||
@@ -518,6 +552,10 @@ packages:
|
||||
express-rate-limit@5.5.1:
|
||||
resolution: {integrity: sha512-MTjE2eIbHv5DyfuFz4zLYWxpqVhEhkTiwFGuB74Q9CSou2WHO52nlE5y3Zlg6SIsiYUIPj6ifFxnkPz6O3sIUg==}
|
||||
|
||||
express@4.18.2:
|
||||
resolution: {integrity: sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==}
|
||||
engines: {node: '>= 0.10.0'}
|
||||
|
||||
express@4.21.0:
|
||||
resolution: {integrity: sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==}
|
||||
engines: {node: '>= 0.10.0'}
|
||||
@@ -563,6 +601,10 @@ packages:
|
||||
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
finalhandler@1.2.0:
|
||||
resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
finalhandler@1.3.1:
|
||||
resolution: {integrity: sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==}
|
||||
engines: {node: '>= 0.8'}
|
||||
@@ -570,10 +612,6 @@ packages:
|
||||
forever-agent@0.6.1:
|
||||
resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==}
|
||||
|
||||
form-data@2.3.3:
|
||||
resolution: {integrity: sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==}
|
||||
engines: {node: '>= 0.12'}
|
||||
|
||||
form-data@4.0.5:
|
||||
resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==}
|
||||
engines: {node: '>= 6'}
|
||||
@@ -678,9 +716,9 @@ packages:
|
||||
resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
http-signature@1.2.0:
|
||||
resolution: {integrity: sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==}
|
||||
engines: {node: '>=0.8', npm: '>=1.3.7'}
|
||||
http-signature@1.3.6:
|
||||
resolution: {integrity: sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==}
|
||||
engines: {node: '>=0.10'}
|
||||
|
||||
http-signature@1.4.0:
|
||||
resolution: {integrity: sha512-G5akfn7eKbpDN+8nPS/cb57YeA1jLTVxjpCj7tmm3QKPdyDy7T+qSC40e9ptydSWvkwjSXw1VbkpyEm39ukeAg==}
|
||||
@@ -818,10 +856,6 @@ packages:
|
||||
resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==}
|
||||
engines: {node: '>=12', npm: '>=6'}
|
||||
|
||||
jsprim@1.4.2:
|
||||
resolution: {integrity: sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==}
|
||||
engines: {node: '>=0.6.0'}
|
||||
|
||||
jsprim@2.0.2:
|
||||
resolution: {integrity: sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==}
|
||||
engines: {'0': node >=0.6.0}
|
||||
@@ -879,6 +913,9 @@ packages:
|
||||
resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
merge-descriptors@1.0.1:
|
||||
resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==}
|
||||
|
||||
merge-descriptors@1.0.3:
|
||||
resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==}
|
||||
|
||||
@@ -988,6 +1025,15 @@ packages:
|
||||
encoding:
|
||||
optional: true
|
||||
|
||||
node-fetch@2.6.8:
|
||||
resolution: {integrity: sha512-RZ6dBYuj8dRSfxpUSu+NsdF1dpPpluJxwOp+6IoDp/sH2QNDSvurYsAa+F1WxY2RjA1iP93xhcsUoYbF2XBqVg==}
|
||||
engines: {node: 4.x || >=6.0.0}
|
||||
peerDependencies:
|
||||
encoding: ^0.1.0
|
||||
peerDependenciesMeta:
|
||||
encoding:
|
||||
optional: true
|
||||
|
||||
normalize-package-data@2.5.0:
|
||||
resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==}
|
||||
|
||||
@@ -1076,6 +1122,9 @@ packages:
|
||||
path-to-regexp@0.1.10:
|
||||
resolution: {integrity: sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==}
|
||||
|
||||
path-to-regexp@0.1.7:
|
||||
resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==}
|
||||
|
||||
path-type@4.0.0:
|
||||
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -1118,6 +1167,10 @@ packages:
|
||||
resolution: {integrity: sha512-8xCNE/aT/EXKenuMDZ+xTVwkT8gsoHN2z/Q29l80u0ppGEXVvsKRzNMbtKhg8LS8k1tJLAHHylf6p4VFmP6XUQ==}
|
||||
engines: {node: '>= 0.4.0'}
|
||||
|
||||
postman-request@2.88.1-postman.40:
|
||||
resolution: {integrity: sha512-uE4AiIqhjtHKp4pj9ei7fkdfNXEX9IqDBlK1plGAQne6y79UUlrTdtYLhwXoO0AMOvqyl9Ar+BU6Eo6P/MPgfg==}
|
||||
engines: {node: '>= 16'}
|
||||
|
||||
process-nextick-args@2.0.1:
|
||||
resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
|
||||
|
||||
@@ -1148,6 +1201,10 @@ packages:
|
||||
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
qs@6.11.0:
|
||||
resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==}
|
||||
engines: {node: '>=0.6'}
|
||||
|
||||
qs@6.13.0:
|
||||
resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==}
|
||||
engines: {node: '>=0.6'}
|
||||
@@ -1156,6 +1213,9 @@ packages:
|
||||
resolution: {integrity: sha512-mzR4sElr1bfCaPJe7m8ilJ6ZXdDaGoObcYR0ZHSsktM/Lt21MVHj5De30GQH2eiZ1qGRTO7LCAzQsUeXTNexWQ==}
|
||||
engines: {node: '>=0.6'}
|
||||
|
||||
querystringify@2.2.0:
|
||||
resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==}
|
||||
|
||||
queue-microtask@1.2.3:
|
||||
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
||||
|
||||
@@ -1166,6 +1226,10 @@ packages:
|
||||
resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
raw-body@2.5.1:
|
||||
resolution: {integrity: sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
raw-body@2.5.2:
|
||||
resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==}
|
||||
engines: {node: '>= 0.8'}
|
||||
@@ -1193,15 +1257,13 @@ packages:
|
||||
resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==}
|
||||
engines: {node: '>= 12.13.0'}
|
||||
|
||||
request@2.88.2:
|
||||
resolution: {integrity: sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==}
|
||||
engines: {node: '>= 6'}
|
||||
deprecated: request has been deprecated, see https://github.com/request/request/issues/3142
|
||||
|
||||
require-from-string@2.0.2:
|
||||
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
requires-port@1.0.0:
|
||||
resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
|
||||
|
||||
resolve@1.22.12:
|
||||
resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -1255,10 +1317,18 @@ packages:
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
|
||||
send@0.18.0:
|
||||
resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
|
||||
send@0.19.0:
|
||||
resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
|
||||
serve-static@1.15.0:
|
||||
resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
|
||||
serve-static@1.16.2:
|
||||
resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
@@ -1348,6 +1418,9 @@ packages:
|
||||
steno@0.4.4:
|
||||
resolution: {integrity: sha512-EEHMVYHNXFHfGtgjNITnka0aHhiAlo93F7z2/Pwd+g0teG9CnM3JIINM7hVVB5/rhw9voufD7Wukwgtw2uqh6w==}
|
||||
|
||||
stream-length@1.0.2:
|
||||
resolution: {integrity: sha512-aI+qKFiwoDV4rsXiS7WRoCt+v2RX1nUj17+KJC5r2gfh5xoSJIfP6Y3Do/HtvesFcTSWthIuJ3l1cvKQY/+nZg==}
|
||||
|
||||
stream-shift@1.0.3:
|
||||
resolution: {integrity: sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==}
|
||||
|
||||
@@ -1424,10 +1497,6 @@ packages:
|
||||
resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
|
||||
engines: {node: '>=0.6'}
|
||||
|
||||
tough-cookie@2.5.0:
|
||||
resolution: {integrity: sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==}
|
||||
engines: {node: '>=0.8'}
|
||||
|
||||
tough-cookie@5.1.2:
|
||||
resolution: {integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==}
|
||||
engines: {node: '>=16'}
|
||||
@@ -1467,6 +1536,10 @@ packages:
|
||||
resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
universalify@0.2.0:
|
||||
resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==}
|
||||
engines: {node: '>= 4.0.0'}
|
||||
|
||||
universalify@2.0.1:
|
||||
resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
@@ -1481,6 +1554,9 @@ packages:
|
||||
uri-js@4.4.1:
|
||||
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
|
||||
|
||||
url-parse@1.5.10:
|
||||
resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==}
|
||||
|
||||
util-deprecate@1.0.2:
|
||||
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
||||
|
||||
@@ -1488,13 +1564,9 @@ packages:
|
||||
resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==}
|
||||
engines: {node: '>= 0.4.0'}
|
||||
|
||||
uuid@3.4.0:
|
||||
resolution: {integrity: sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==}
|
||||
deprecated: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.
|
||||
hasBin: true
|
||||
|
||||
uuid@8.3.2:
|
||||
resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
|
||||
deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).
|
||||
hasBin: true
|
||||
|
||||
validate-npm-package-license@3.0.4:
|
||||
@@ -1511,10 +1583,18 @@ packages:
|
||||
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
verdaccio-audit@10.2.4:
|
||||
resolution: {integrity: sha512-/0H6/JFVnhHwucUfMRVjL6gtGnB5gr3dDxq93Ja1Y0ob+2jxAfpqNMHg8c6/d/ZyHFf0y4tXzHESDruXCzTiaQ==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
verdaccio-audit@13.0.0-next-8.1:
|
||||
resolution: {integrity: sha512-EEfUeC1kHuErtwF9FC670W+EXHhcl+iuigONkcprwRfkPxmdBs+Hx36745hgAMZ9SCqedNECaycnGF3tZ3VYfw==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
verdaccio-htpasswd@10.5.2:
|
||||
resolution: {integrity: sha512-bO5Wm8w07pWswNvwFWjNEoznuUU37CcfblcrU0Ci8c038EgTu2V47uwh4AyZ4PTK6ps9oxHqA7a1b+83sY0OkA==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
verdaccio-htpasswd@13.0.0-next-8.1:
|
||||
resolution: {integrity: sha512-BfvmO+ZdbwfttOwrdTPD6Bccr1ZfZ9Tk/9wpXamxdWB/XPWlk3FtyGsvqCmxsInRLPhQ/FSk9c3zRCGvICTFYg==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -1598,23 +1678,40 @@ snapshots:
|
||||
'@nodelib/fs.scandir': 2.1.5
|
||||
fastq: 1.20.1
|
||||
|
||||
'@pnpm/registry-mock@3.50.0(typanion@3.14.0)':
|
||||
'@pnpm/registry-mock@6.0.0(verdaccio@5.33.0(typanion@3.14.0))':
|
||||
dependencies:
|
||||
anonymous-npm-registry-client: 0.2.0
|
||||
anonymous-npm-registry-client: 0.3.2
|
||||
execa: 5.1.1
|
||||
fs-extra: 11.3.4
|
||||
read-yaml-file: 2.1.0
|
||||
rimraf: 3.0.2
|
||||
tempy: 1.0.1
|
||||
verdaccio: 5.33.0(typanion@3.14.0)
|
||||
verdaccio-audit: 10.2.4
|
||||
verdaccio-htpasswd: 10.5.2
|
||||
write-yaml-file: 4.2.0
|
||||
transitivePeerDependencies:
|
||||
- bare-abort-controller
|
||||
- bare-buffer
|
||||
- encoding
|
||||
- react-native-b4a
|
||||
- supports-color
|
||||
- typanion
|
||||
|
||||
'@postman/form-data@3.1.1':
|
||||
dependencies:
|
||||
asynckit: 0.4.0
|
||||
combined-stream: 1.0.8
|
||||
mime-types: 2.1.35
|
||||
|
||||
'@postman/tough-cookie@4.1.3-postman.1':
|
||||
dependencies:
|
||||
psl: 1.15.0
|
||||
punycode: 2.3.1
|
||||
universalify: 0.2.0
|
||||
url-parse: 1.5.10
|
||||
|
||||
'@postman/tunnel-agent@0.6.8':
|
||||
dependencies:
|
||||
safe-buffer: 5.2.1
|
||||
|
||||
'@qiwi/npm-types@1.0.3': {}
|
||||
|
||||
'@verdaccio/auth@8.0.0-next-8.1':
|
||||
dependencies:
|
||||
@@ -1655,6 +1752,10 @@ snapshots:
|
||||
process-warning: 1.0.0
|
||||
semver: 7.6.3
|
||||
|
||||
'@verdaccio/file-locking@10.3.0':
|
||||
dependencies:
|
||||
lockfile: 1.0.4
|
||||
|
||||
'@verdaccio/file-locking@10.3.1':
|
||||
dependencies:
|
||||
lockfile: 1.0.4
|
||||
@@ -1813,14 +1914,15 @@ snapshots:
|
||||
json-schema-traverse: 1.0.0
|
||||
require-from-string: 2.0.2
|
||||
|
||||
anonymous-npm-registry-client@0.2.0:
|
||||
anonymous-npm-registry-client@0.3.2:
|
||||
dependencies:
|
||||
concat-stream: 1.6.2
|
||||
'@qiwi/npm-types': 1.0.3
|
||||
concat-stream: 2.0.0
|
||||
graceful-fs: 4.2.11
|
||||
normalize-package-data: 2.5.0
|
||||
npm-package-arg: 6.1.1
|
||||
once: 1.4.0
|
||||
request: 2.88.2
|
||||
request: postman-request@2.88.1-postman.40
|
||||
retry: 0.13.1
|
||||
safe-buffer: 5.2.1
|
||||
semver: 7.7.4
|
||||
@@ -1911,6 +2013,25 @@ snapshots:
|
||||
|
||||
bcryptjs@2.4.3: {}
|
||||
|
||||
bluebird@2.11.0: {}
|
||||
|
||||
body-parser@1.20.1:
|
||||
dependencies:
|
||||
bytes: 3.1.2
|
||||
content-type: 1.0.5
|
||||
debug: 2.6.9
|
||||
depd: 2.0.0
|
||||
destroy: 1.2.0
|
||||
http-errors: 2.0.0
|
||||
iconv-lite: 0.4.24
|
||||
on-finished: 2.4.1
|
||||
qs: 6.11.0
|
||||
raw-body: 2.5.1
|
||||
type-is: 1.6.18
|
||||
unpipe: 1.0.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
body-parser@1.20.3:
|
||||
dependencies:
|
||||
bytes: 3.1.2
|
||||
@@ -1941,6 +2062,10 @@ snapshots:
|
||||
dependencies:
|
||||
fill-range: 7.1.1
|
||||
|
||||
brotli@1.3.3:
|
||||
dependencies:
|
||||
base64-js: 1.5.1
|
||||
|
||||
browserify-zlib@0.1.4:
|
||||
dependencies:
|
||||
pako: 0.2.9
|
||||
@@ -2003,11 +2128,11 @@ snapshots:
|
||||
|
||||
concat-map@0.0.1: {}
|
||||
|
||||
concat-stream@1.6.2:
|
||||
concat-stream@2.0.0:
|
||||
dependencies:
|
||||
buffer-from: 1.1.2
|
||||
inherits: 2.0.4
|
||||
readable-stream: 2.3.8
|
||||
readable-stream: 3.6.2
|
||||
typedarray: 0.0.6
|
||||
|
||||
console-control-strings@1.1.0:
|
||||
@@ -2021,6 +2146,8 @@ snapshots:
|
||||
|
||||
cookie-signature@1.0.6: {}
|
||||
|
||||
cookie@0.5.0: {}
|
||||
|
||||
cookie@0.6.0: {}
|
||||
|
||||
cookie@0.7.1: {}
|
||||
@@ -2174,6 +2301,42 @@ snapshots:
|
||||
|
||||
express-rate-limit@5.5.1: {}
|
||||
|
||||
express@4.18.2:
|
||||
dependencies:
|
||||
accepts: 1.3.8
|
||||
array-flatten: 1.1.1
|
||||
body-parser: 1.20.1
|
||||
content-disposition: 0.5.4
|
||||
content-type: 1.0.5
|
||||
cookie: 0.5.0
|
||||
cookie-signature: 1.0.6
|
||||
debug: 2.6.9
|
||||
depd: 2.0.0
|
||||
encodeurl: 1.0.2
|
||||
escape-html: 1.0.3
|
||||
etag: 1.8.1
|
||||
finalhandler: 1.2.0
|
||||
fresh: 0.5.2
|
||||
http-errors: 2.0.0
|
||||
merge-descriptors: 1.0.1
|
||||
methods: 1.1.2
|
||||
on-finished: 2.4.1
|
||||
parseurl: 1.3.3
|
||||
path-to-regexp: 0.1.7
|
||||
proxy-addr: 2.0.7
|
||||
qs: 6.11.0
|
||||
range-parser: 1.2.1
|
||||
safe-buffer: 5.2.1
|
||||
send: 0.18.0
|
||||
serve-static: 1.15.0
|
||||
setprototypeof: 1.2.0
|
||||
statuses: 2.0.1
|
||||
type-is: 1.6.18
|
||||
utils-merge: 1.0.1
|
||||
vary: 1.1.2
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
express@4.21.0:
|
||||
dependencies:
|
||||
accepts: 1.3.8
|
||||
@@ -2278,6 +2441,18 @@ snapshots:
|
||||
dependencies:
|
||||
to-regex-range: 5.0.1
|
||||
|
||||
finalhandler@1.2.0:
|
||||
dependencies:
|
||||
debug: 2.6.9
|
||||
encodeurl: 1.0.2
|
||||
escape-html: 1.0.3
|
||||
on-finished: 2.4.1
|
||||
parseurl: 1.3.3
|
||||
statuses: 2.0.1
|
||||
unpipe: 1.0.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
finalhandler@1.3.1:
|
||||
dependencies:
|
||||
debug: 2.6.9
|
||||
@@ -2292,12 +2467,6 @@ snapshots:
|
||||
|
||||
forever-agent@0.6.1: {}
|
||||
|
||||
form-data@2.3.3:
|
||||
dependencies:
|
||||
asynckit: 0.4.0
|
||||
combined-stream: 1.0.8
|
||||
mime-types: 2.1.35
|
||||
|
||||
form-data@4.0.5:
|
||||
dependencies:
|
||||
asynckit: 0.4.0
|
||||
@@ -2438,10 +2607,10 @@ snapshots:
|
||||
statuses: 2.0.1
|
||||
toidentifier: 1.0.1
|
||||
|
||||
http-signature@1.2.0:
|
||||
http-signature@1.3.6:
|
||||
dependencies:
|
||||
assert-plus: 1.0.0
|
||||
jsprim: 1.4.2
|
||||
jsprim: 2.0.2
|
||||
sshpk: 1.18.0
|
||||
|
||||
http-signature@1.4.0:
|
||||
@@ -2558,14 +2727,7 @@ snapshots:
|
||||
lodash.isstring: 4.0.1
|
||||
lodash.once: 4.1.1
|
||||
ms: 2.1.3
|
||||
semver: 7.6.3
|
||||
|
||||
jsprim@1.4.2:
|
||||
dependencies:
|
||||
assert-plus: 1.0.0
|
||||
extsprintf: 1.3.0
|
||||
json-schema: 0.4.0
|
||||
verror: 1.10.0
|
||||
semver: 7.7.4
|
||||
|
||||
jsprim@2.0.2:
|
||||
dependencies:
|
||||
@@ -2621,6 +2783,8 @@ snapshots:
|
||||
|
||||
media-typer@0.3.0: {}
|
||||
|
||||
merge-descriptors@1.0.1: {}
|
||||
|
||||
merge-descriptors@1.0.3: {}
|
||||
|
||||
merge-stream@2.0.0: {}
|
||||
@@ -2694,6 +2858,10 @@ snapshots:
|
||||
dependencies:
|
||||
whatwg-url: 5.0.0
|
||||
|
||||
node-fetch@2.6.8:
|
||||
dependencies:
|
||||
whatwg-url: 5.0.0
|
||||
|
||||
normalize-package-data@2.5.0:
|
||||
dependencies:
|
||||
hosted-git-info: 2.8.9
|
||||
@@ -2772,6 +2940,8 @@ snapshots:
|
||||
|
||||
path-to-regexp@0.1.10: {}
|
||||
|
||||
path-to-regexp@0.1.7: {}
|
||||
|
||||
path-type@4.0.0: {}
|
||||
|
||||
peek-stream@1.1.3:
|
||||
@@ -2830,6 +3000,31 @@ snapshots:
|
||||
|
||||
pkginfo@0.4.1: {}
|
||||
|
||||
postman-request@2.88.1-postman.40:
|
||||
dependencies:
|
||||
'@postman/form-data': 3.1.1
|
||||
'@postman/tough-cookie': 4.1.3-postman.1
|
||||
'@postman/tunnel-agent': 0.6.8
|
||||
aws-sign2: 0.7.0
|
||||
aws4: 1.13.2
|
||||
brotli: 1.3.3
|
||||
caseless: 0.12.0
|
||||
combined-stream: 1.0.8
|
||||
extend: 3.0.2
|
||||
forever-agent: 0.6.1
|
||||
har-validator: 5.1.5
|
||||
http-signature: 1.3.6
|
||||
is-typedarray: 1.0.0
|
||||
isstream: 0.1.2
|
||||
json-stringify-safe: 5.0.1
|
||||
mime-types: 2.1.35
|
||||
oauth-sign: 0.9.0
|
||||
performance-now: 2.1.0
|
||||
qs: 6.5.5
|
||||
safe-buffer: 5.2.1
|
||||
stream-length: 1.0.2
|
||||
uuid: 8.3.2
|
||||
|
||||
process-nextick-args@2.0.1: {}
|
||||
|
||||
process-warning@1.0.0: {}
|
||||
@@ -2860,18 +3055,31 @@ snapshots:
|
||||
|
||||
punycode@2.3.1: {}
|
||||
|
||||
qs@6.11.0:
|
||||
dependencies:
|
||||
side-channel: 1.1.0
|
||||
|
||||
qs@6.13.0:
|
||||
dependencies:
|
||||
side-channel: 1.1.0
|
||||
|
||||
qs@6.5.5: {}
|
||||
|
||||
querystringify@2.2.0: {}
|
||||
|
||||
queue-microtask@1.2.3: {}
|
||||
|
||||
quick-format-unescaped@4.0.4: {}
|
||||
|
||||
range-parser@1.2.1: {}
|
||||
|
||||
raw-body@2.5.1:
|
||||
dependencies:
|
||||
bytes: 3.1.2
|
||||
http-errors: 2.0.0
|
||||
iconv-lite: 0.4.24
|
||||
unpipe: 1.0.0
|
||||
|
||||
raw-body@2.5.2:
|
||||
dependencies:
|
||||
bytes: 3.1.2
|
||||
@@ -2912,31 +3120,10 @@ snapshots:
|
||||
|
||||
real-require@0.2.0: {}
|
||||
|
||||
request@2.88.2:
|
||||
dependencies:
|
||||
aws-sign2: 0.7.0
|
||||
aws4: 1.13.2
|
||||
caseless: 0.12.0
|
||||
combined-stream: 1.0.8
|
||||
extend: 3.0.2
|
||||
forever-agent: 0.6.1
|
||||
form-data: 2.3.3
|
||||
har-validator: 5.1.5
|
||||
http-signature: 1.2.0
|
||||
is-typedarray: 1.0.0
|
||||
isstream: 0.1.2
|
||||
json-stringify-safe: 5.0.1
|
||||
mime-types: 2.1.35
|
||||
oauth-sign: 0.9.0
|
||||
performance-now: 2.1.0
|
||||
qs: 6.5.5
|
||||
safe-buffer: 5.2.1
|
||||
tough-cookie: 2.5.0
|
||||
tunnel-agent: 0.6.0
|
||||
uuid: 3.4.0
|
||||
|
||||
require-from-string@2.0.2: {}
|
||||
|
||||
requires-port@1.0.0: {}
|
||||
|
||||
resolve@1.22.12:
|
||||
dependencies:
|
||||
es-errors: 1.3.0
|
||||
@@ -2974,6 +3161,24 @@ snapshots:
|
||||
|
||||
semver@7.7.4: {}
|
||||
|
||||
send@0.18.0:
|
||||
dependencies:
|
||||
debug: 2.6.9
|
||||
depd: 2.0.0
|
||||
destroy: 1.2.0
|
||||
encodeurl: 1.0.2
|
||||
escape-html: 1.0.3
|
||||
etag: 1.8.1
|
||||
fresh: 0.5.2
|
||||
http-errors: 2.0.0
|
||||
mime: 1.6.0
|
||||
ms: 2.1.3
|
||||
on-finished: 2.4.1
|
||||
range-parser: 1.2.1
|
||||
statuses: 2.0.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
send@0.19.0:
|
||||
dependencies:
|
||||
debug: 2.6.9
|
||||
@@ -2992,6 +3197,15 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
serve-static@1.15.0:
|
||||
dependencies:
|
||||
encodeurl: 1.0.2
|
||||
escape-html: 1.0.3
|
||||
parseurl: 1.3.3
|
||||
send: 0.18.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
serve-static@1.16.2:
|
||||
dependencies:
|
||||
encodeurl: 2.0.0
|
||||
@@ -3098,6 +3312,10 @@ snapshots:
|
||||
dependencies:
|
||||
graceful-fs: 4.2.11
|
||||
|
||||
stream-length@1.0.2:
|
||||
dependencies:
|
||||
bluebird: 2.11.0
|
||||
|
||||
stream-shift@1.0.3: {}
|
||||
|
||||
streamx@2.25.0:
|
||||
@@ -3196,11 +3414,6 @@ snapshots:
|
||||
|
||||
toidentifier@1.0.1: {}
|
||||
|
||||
tough-cookie@2.5.0:
|
||||
dependencies:
|
||||
psl: 1.15.0
|
||||
punycode: 2.3.1
|
||||
|
||||
tough-cookie@5.1.2:
|
||||
dependencies:
|
||||
tldts: 6.1.86
|
||||
@@ -3235,6 +3448,8 @@ snapshots:
|
||||
dependencies:
|
||||
crypto-random-string: 2.0.0
|
||||
|
||||
universalify@0.2.0: {}
|
||||
|
||||
universalify@2.0.1: {}
|
||||
|
||||
unix-crypt-td-js@1.1.4: {}
|
||||
@@ -3245,12 +3460,15 @@ snapshots:
|
||||
dependencies:
|
||||
punycode: 2.3.1
|
||||
|
||||
url-parse@1.5.10:
|
||||
dependencies:
|
||||
querystringify: 2.2.0
|
||||
requires-port: 1.0.0
|
||||
|
||||
util-deprecate@1.0.2: {}
|
||||
|
||||
utils-merge@1.0.1: {}
|
||||
|
||||
uuid@3.4.0: {}
|
||||
|
||||
uuid@8.3.2: {}
|
||||
|
||||
validate-npm-package-license@3.0.4:
|
||||
@@ -3266,6 +3484,16 @@ snapshots:
|
||||
|
||||
vary@1.1.2: {}
|
||||
|
||||
verdaccio-audit@10.2.4:
|
||||
dependencies:
|
||||
body-parser: 1.20.1
|
||||
express: 4.18.2
|
||||
https-proxy-agent: 5.0.1
|
||||
node-fetch: 2.6.8
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
- supports-color
|
||||
|
||||
verdaccio-audit@13.0.0-next-8.1:
|
||||
dependencies:
|
||||
'@verdaccio/config': 8.0.0-next-8.1
|
||||
@@ -3277,6 +3505,14 @@ snapshots:
|
||||
- encoding
|
||||
- supports-color
|
||||
|
||||
verdaccio-htpasswd@10.5.2:
|
||||
dependencies:
|
||||
'@verdaccio/file-locking': 10.3.0
|
||||
apache-md5: 1.1.8
|
||||
bcryptjs: 2.4.3
|
||||
http-errors: 2.0.0
|
||||
unix-crypt-td-js: 1.1.4
|
||||
|
||||
verdaccio-htpasswd@13.0.0-next-8.1:
|
||||
dependencies:
|
||||
'@verdaccio/core': 8.0.0-next-8.1
|
||||
|
||||
@@ -1,2 +1,12 @@
|
||||
allowBuilds:
|
||||
core-js: false
|
||||
|
||||
# Verdaccio's CJS deps (notably `@verdaccio/signature` requiring
|
||||
# `@verdaccio/config`) don't resolve correctly under pnpm's strict
|
||||
# isolation when verdaccio is invoked from a nested install. The
|
||||
# Rust launcher spawns `node launch.mjs` which calls
|
||||
# `@pnpm/registry-mock`'s programmatic `start()` API, which in turn
|
||||
# spawns verdaccio — a flat `node_modules` ensures verdaccio's
|
||||
# transitive CJS requires resolve no matter where in the tree
|
||||
# they're loaded from.
|
||||
nodeLinker: hoisted
|
||||
|
||||
@@ -9,7 +9,7 @@ use reqwest::Client;
|
||||
use std::{
|
||||
fs::File,
|
||||
path::Path,
|
||||
process::{Child, Command, Stdio},
|
||||
process::{Child, Stdio},
|
||||
};
|
||||
use sysinfo::{Pid, Signal};
|
||||
use tokio::time::{Duration, sleep};
|
||||
@@ -79,7 +79,6 @@ impl<'a> MockInstanceOptions<'a> {
|
||||
|
||||
eprintln!("Preparing...");
|
||||
node_registry_mock()
|
||||
.pipe(Command::new)
|
||||
.arg("prepare")
|
||||
.env("PNPM_REGISTRY_MOCK_PORT", &port)
|
||||
.stdin(Stdio::null())
|
||||
@@ -95,7 +94,6 @@ impl<'a> MockInstanceOptions<'a> {
|
||||
File::create(stderr).expect("create file for stderr").into()
|
||||
});
|
||||
let process = node_registry_mock()
|
||||
.pipe(Command::new)
|
||||
.env("PNPM_REGISTRY_MOCK_PORT", &port)
|
||||
.stdin(Stdio::null())
|
||||
.stdout(stdout)
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
use crate::registry_mock;
|
||||
use pipe_trait::Pipe;
|
||||
use std::{
|
||||
env, iter,
|
||||
path::{Path, PathBuf},
|
||||
sync::OnceLock,
|
||||
};
|
||||
use which::which_in;
|
||||
use std::{path::PathBuf, process::Command, sync::OnceLock};
|
||||
|
||||
static NODE_REGISTRY_MOCK: OnceLock<PathBuf> = OnceLock::new();
|
||||
static LAUNCH_SCRIPT: OnceLock<PathBuf> = OnceLock::new();
|
||||
|
||||
fn init() -> PathBuf {
|
||||
let bin = registry_mock().join("node_modules").join(".bin");
|
||||
let paths = env::var_os("PATH")
|
||||
.unwrap_or_default()
|
||||
.pipe_ref(env::split_paths)
|
||||
.chain(iter::once(bin))
|
||||
.pipe(env::join_paths)
|
||||
.expect("append node_modules/.bin to PATH");
|
||||
which_in("registry-mock", Some(paths), ".").expect("find registry-mock binary")
|
||||
/// Path to the `launch.mjs` wrapper that drives `@pnpm/registry-mock`
|
||||
/// via its programmatic API. The wrapper is required because the
|
||||
/// package's default CLI export does not thread `useNodeVersion`
|
||||
/// through — and verdaccio 5.33 (the version v6 of `@pnpm/registry-mock`
|
||||
/// bundles) rejects its 64-character storage secret on Node 22+, so
|
||||
/// running it under the host Node fails. Pacquet pins
|
||||
/// `useNodeVersion: '20.16.0'` in the wrapper to match pnpm's jest
|
||||
/// `globalSetup` shape.
|
||||
fn launch_script() -> &'static PathBuf {
|
||||
LAUNCH_SCRIPT.get_or_init(|| registry_mock().join("launch.mjs"))
|
||||
}
|
||||
|
||||
pub fn node_registry_mock() -> &'static Path {
|
||||
NODE_REGISTRY_MOCK.get_or_init(init)
|
||||
/// Returns a [`Command`] pre-populated with `node <launch.mjs>`. The
|
||||
/// caller appends `prepare` (to publish fixtures) or omits the arg
|
||||
/// (to launch the server) and any environment / stdio setup.
|
||||
pub fn node_registry_mock() -> Command {
|
||||
let mut cmd = Command::new("node");
|
||||
cmd.arg(launch_script());
|
||||
cmd
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user