diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cdfac7e291..aa82c820cd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1261,24 +1261,37 @@ jobs: --test-threads 8 --config-file "$RUNNER_TEMP/snapshot-nextest.toml" --archive-file windows-snapshot-tests.tar.zst --workspace-remap . --no-fail-fast ) + snapshot_registry=( + node packages/tools/src/local-npm-registry.ts --snapshot-runner + --snapshot-fixtures "$GITHUB_WORKSPACE/crates/vp_cli_snapshots/tests/cli_snapshots/registry" + --snapshot-cache "$VP_SNAP_REGISTRY_CACHE_DIR" + ) + run_snapshots() { + # Keep both registries alive across nextest's one-process-per-case + # workers. Only local-registry cases receive checkout packages. + "${snapshot_registry[@]}" --request-log "$VP_SNAP_ARTIFACTS_DIR/shared-registry/registry.jsonl" -- \ + "${snapshot_registry[@]}" --packages-dir "$VP_SNAP_PACKAGES_DIR/packages" \ + --request-log "$VP_SNAP_ARTIFACTS_DIR/packed-registry/registry.jsonl" -- \ + cargo-nextest nextest run "${nextest_run_args[@]}" "$@" + } # --no-fail-fast: on a snapshot suite every diff is diagnostic # signal; cancelling on the first failure hides the rest. test_exit=0 # This fixture requires case-sensitive directory support, which the # default temp directory on these runners does not provide. - cargo-nextest nextest run "${nextest_run_args[@]}" --partition hash:${{ matrix.shard }}/3 \ + run_snapshots --partition hash:${{ matrix.shard }}/3 \ -E 'not test(windows_case_sensitive_shims)' || test_exit=$? if [[ '${{ matrix.shard }}' == '1' ]]; then # Run both flavors on NTFS once. Rust's GetTempPath2 ignores # TEMP/TMP under the SYSTEM account, so override SystemTemp. SystemTemp='${{ steps.snapshot-temp.outputs.directory }}' \ - cargo-nextest nextest run "${nextest_run_args[@]}" \ + run_snapshots \ -E 'test(windows_case_sensitive_shims)' || test_exit=$? # Exercise the same environment wrapper under Windows PowerShell 5.1. VP_SNAP_PWSH_BIN="$(cygpath -w "$(command -v powershell.exe)")" \ - cargo-nextest nextest run "${nextest_run_args[@]}" \ + run_snapshots \ -E 'test(command_env_powershell)' || test_exit=$? fi exit "$test_exit" @@ -1286,6 +1299,7 @@ jobs: RUST_BACKTRACE: '1' # Share immutable tarballs across nextest processes in this job. VP_SNAP_PACKAGES_DIR: ${{ runner.temp }}/snapshot-packages + VP_SNAP_REGISTRY_CACHE_DIR: ${{ runner.temp }}/snapshot-registry-cache VP_SNAP_ARTIFACTS_DIR: ${{ runner.temp }}/snapshot-artifacts # Keep Windows env parity with the `test` recipe in justfile. __COMPAT_LAYER: RunAsInvoker diff --git a/.typos.toml b/.typos.toml index 0f1320c1d4..28c7a0f9ea 100644 --- a/.typos.toml +++ b/.typos.toml @@ -23,4 +23,6 @@ extend-exclude = [ "packages/cli/src/oxfmt-config.ts", # Vendored PGP key/signature blobs: base64 armor trips the spell checker. "crates/vp_js_runtime/src/assets/**/*.asc", + # Registry recordings contain upstream signatures and integrity hashes. + "crates/vp_cli_snapshots/tests/cli_snapshots/registry/**/manifest.json", ] diff --git a/AGENTS.md b/AGENTS.md index 47bbc21c19..8340396fb7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -28,6 +28,8 @@ This guide applies to this repository; `CLAUDE.md` points here. For changes to t ## Checks and Tests +Prefer local fixtures or mock servers for tests that depend on external services. Use real network requests only when they are necessary to verify the behavior under test, and explain that need in the test. Reuse existing test infrastructure, preserve behavioral coverage, and keep mutable state isolated between cases. + For behavior changes, find nearby tests before editing and add or update coverage in the same area. Choose checks for the changed layer: | Change | Validation | @@ -47,6 +49,8 @@ Validate `vp check`, lint, format, and type-check changes end-to-end: bundled-to Write new CLI tests, including interactive flows, in `crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/`. Read the [snapshot runner instructions](crates/vp_cli_snapshots/tests/cli_snapshots/README.md) before adding cases; they define `snapshots.toml`, CLI flavors, `vpt` helpers, and prompt milestones. See [the RFC](rfcs/interactive-snapshot-tests.md) for design rationale. - Steps are argv arrays without an implicit shell. Use `vpt` helpers instead of coreutils for portable file operations. +- For package-manager cases, prefer the runner's local registry, [shared registry fixtures](crates/vp_cli_snapshots/tests/cli_snapshots/registry/README.md), and response cache over new public-registry requests. Keep package-manager caches isolated per case, and refresh recorded fixtures only through the explicit maintenance command. +- For changes to network use or scheduling, compare request counts and elapsed time with the same cases, worker count, and initial cache state. Do not remove assertions or share mutable case state to improve speed. - Local-flavor cases require a fresh `packages/cli/dist`. Use `just snapshot-test-global ` when no JS build is available. - For intended snapshot changes, run `UPDATE_SNAPSHOTS=1 just snapshot-test `. Review and commit the recorded `.md` files with the fixture; mismatches fail the run and produce `.md.new` files. diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/README.md b/crates/vp_cli_snapshots/tests/cli_snapshots/README.md index 6ce4e4ea36..5ffc728760 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/README.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/README.md @@ -71,25 +71,32 @@ the execution gate. Scheduling happens after partitioning and does not change shard membership or test listing order. The existing gate still protects `serial = true` and Ctrl-C cases, including against other runner processes. +Npm command cases use local registry fixtures and shared downloads, so they no +longer need serialization to limit concurrent connections to npm. Signal-sensitive +and explicitly isolated cases retain the execution gate. Environment overrides, mainly for CI: -| Variable | Effect | -| --------------------------- | ----------------------------------------------------------------------------- | -| `VP_SNAP_GLOBAL_VP` | Path to a prebuilt global `vp` binary (skips the target-dir lookup) | -| `VP_SNAP_LOCAL_CLI_BIN_DIR` | Local CLI bin dir (default `/packages/cli/bin`) | -| `VP_SNAP_JS_RUNTIME_DIR` | Provisioned managed runtime to seed case homes with | -| `VP_SNAP_SH_BIN` | POSIX `sh` binary for cases that execute the generated `env` file | -| `VP_SNAP_BASH_BIN` | Bash binary for cases that execute the generated `env` file | -| `VP_SNAP_ZSH_BIN` | Zsh binary for cases that execute the generated `env` file | -| `VP_SNAP_CMD_BIN` | System cmd.exe for cases that execute generated batch files | -| `VP_SNAP_FISH_BIN` | Fish binary for cases that execute generated `env.fish` files | -| `VP_SNAP_NU_BIN` | Nushell binary for cases that execute generated `env.nu` files | -| `VP_SNAP_PWSH_BIN` | PowerShell binary for cases that execute generated `env.ps1` files | -| `VP_SNAP_SKIP_FLAVORS` | Comma-separated flavors to skip registering (e.g. `local`) | -| `VP_SNAP_PACKAGES_DIR` | Run-scoped directory for sharing packed packages across test processes | -| `VP_SNAP_ARTIFACTS_DIR` | Directory for phase timings and failure diagnostics; unset disables artifacts | -| `VP_SNAP_NEXTEST_CONFIG` | With `--list`, write nextest overrides for the discovered isolated cases | +| Variable | Effect | +| ----------------------------- | ------------------------------------------------------------------------------------------------------- | +| `VP_SNAP_GLOBAL_VP` | Path to a prebuilt global `vp` binary (skips the target-dir lookup) | +| `VP_SNAP_LOCAL_CLI_BIN_DIR` | Local CLI bin dir (default `/packages/cli/bin`) | +| `VP_SNAP_JS_RUNTIME_DIR` | Provisioned managed runtime to seed case homes with | +| `VP_SNAP_SH_BIN` | POSIX `sh` binary for cases that execute the generated `env` file | +| `VP_SNAP_BASH_BIN` | Bash binary for cases that execute the generated `env` file | +| `VP_SNAP_ZSH_BIN` | Zsh binary for cases that execute the generated `env` file | +| `VP_SNAP_CMD_BIN` | System cmd.exe for cases that execute generated batch files | +| `VP_SNAP_FISH_BIN` | Fish binary for cases that execute generated `env.fish` files | +| `VP_SNAP_NU_BIN` | Nushell binary for cases that execute generated `env.nu` files | +| `VP_SNAP_PWSH_BIN` | PowerShell binary for cases that execute generated `env.ps1` files | +| `VP_SNAP_SKIP_FLAVORS` | Comma-separated flavors to skip registering (e.g. `local`) | +| `VP_SNAP_REGISTRY_CACHE_DIR` | Fresh run-scoped response directory shared across nextest processes | +| `VP_SNAP_REGISTRY_MODE` | `live`: count upstream requests without caching or shared fixtures; `direct`: original registry routing | +| `VP_SNAP_SHARED_REGISTRY_ENV` | Registry environment JSON from the `--snapshot-runner` wrapper, shared across runner processes | +| `VP_SNAP_PACKED_REGISTRY_ENV` | Registry environment JSON for the wrapper that serves packed checkout packages | +| `VP_SNAP_PACKAGES_DIR` | Run-scoped directory for sharing packed packages across test processes | +| `VP_SNAP_ARTIFACTS_DIR` | Directory for phase timings and failure diagnostics; unset disables artifacts | +| `VP_SNAP_NEXTEST_CONFIG` | With `--list`, write nextest overrides for the discovered isolated cases | Windows CI generates its nextest configuration from the same case definitions. Exact nextest runs read only the selected fixture; listing and native shard @@ -110,8 +117,64 @@ directory for each test run and keep the build unchanged while tests run. The runner checks the checkout path and input/tarball file sizes and modification times; a changed build fails instead of silently testing stale packages. This is a per-run preparation directory, not a persistent content-addressed cache. -Each case still has its own registry server, overlays, and mutable caches. Windows CI uses this to avoid repacking in every nextest process. +Cases without fixture overlays share two servers per runner: one for published +packages and one that also serves the packed checkout. Cases with a +`mock-manifest.json`, `tarballs/`, or `.npmrc` retain a dedicated server. +Use `isolated-registry = true` when a test needs private registry state, such as +the request counter used to verify metadata caching. All package-manager caches +remain isolated per case. + +Windows CI wraps nextest with `local-npm-registry.ts --snapshot-runner` so the +two servers also survive across test processes. The wrapper passes registry +environments to the Rust runner without changing its package-manager settings. +The packed server loads archives on its first request, after the runner prepares +and validates them under the existing package lock. Both servers exit when the +wrapped command finishes, and the wrapper preserves its exit code. + +All cases use a local registry by default. The small test packages in +[`registry/`](registry/README.md) are served from checked-in metadata and tarballs. +Other successful registry GET responses are cached for the duration of the run, +including package-manager downloads. Concurrent requests share one download, +including across nextest processes when `VP_SNAP_REGISTRY_CACHE_DIR` is set. +Use a fresh directory per run; this is not a persistent dependency cache. +Package-manager caches and installed packages remain isolated per case. + +`local-registry = true` additionally serves the packed checkout packages. +Published versions remain available for cases that deliberately install an older +release. Their upstream metadata is fetched once per run and merged with the +local version. The standalone registry tool and ecosystem tests retain the same +upstream version merging without the snapshot response cache. Fixture +`mock-manifest.json` entries override shared metadata, and fixture tarballs +override shared tarballs. Explicit case and step registry settings still take +precedence over runner defaults. + +With artifacts enabled, the shared server writes `registry.jsonl` in the run +artifact directory. Windows wrappers write separate `shared-registry/registry.jsonl` +and `packed-registry/registry.jsonl` files in the artifact root. +Per-case servers write the same file in their case artifact +directory. Combine both locations when counting requests: `fixture` events +identify locally served data, `request` events count proxy requests, `hit` events +identify reused responses, and `upstream` events count actual upstream HTTP +requests (including redirects). Non-GET requests are forwarded without caching. +`upstream-stalled` identifies requests with no response progress for 15 seconds, +including connection setup. These requests release their cache locks so clients +can retry. Each upstream request also has a 60-second total deadline. +These counts cover the registry; requests to other services, such as Node.js +release downloads or Git hosts, are outside this log. + +For a network comparison, run the same cases with eight workers first with +`VP_SNAP_REGISTRY_MODE=live`, then with the variable unset. Both runs route +registry traffic through the logger; `live` disables published recordings and +response cache. `VP_SNAP_REGISTRY_MODE=direct` restores the original routing for +an independent wall-time baseline, but cannot log traffic that bypasses the +local registry. Keep builds unchanged and use fresh response directories. +Local tarball URLs can cause snapshot differences in comparison modes. +Fixture overlays remain active in both comparison modes. Cases with a +`mock-manifest.json` or `tarballs/` start a private registry even in `direct` +mode, so fixed templates remain the same test inputs. Cases that use shared +locally authored packages must set `local-registry = true` so `direct` mode +also starts their registry; these packages have no published version. With `VP_SNAP_ARTIFACTS_DIR` set, each runner process creates a unique `run-*` directory. `runner/timing.json` records discovery, test execution, and final @@ -299,8 +362,8 @@ a milestone, or a redaction rule; never rerun until green. Set For registry fallback tests, use `local-registry = true` and a fixture `mock-manifest.json` entry such as `"npm/latest"` to control the resolved version. Keep the CLI input unpinned so the fallback is still tested, and keep exact -version assertions. Unmocked registry requests go upstream, so -`local-registry = true` alone does not freeze dist-tags. +version assertions. Unmocked registry responses are fetched upstream once per run, so +`local-registry = true` alone does not freeze dist-tags across runs. Fixture trees are excluded from repo-wide fmt, lint, typecheck, and vitest (`vite.config.ts`, `tsconfig.json`); recorded snapshots and diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/change_passthrough_env_config/check-temp-socket.mjs b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/change_passthrough_env_config/check-temp-socket.mjs new file mode 100644 index 0000000000..9341153596 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/change_passthrough_env_config/check-temp-socket.mjs @@ -0,0 +1,14 @@ +import { createServer } from 'node:net'; +import { tmpdir } from 'node:os'; +import path from 'node:path'; + +if (process.platform !== 'win32') { + const server = createServer(); + await new Promise((resolve, reject) => { + server.once('error', reject); + server.listen(path.join(tmpdir(), 'supervisor.sock'), resolve); + }); + await new Promise((resolve, reject) => { + server.close((error) => (error ? reject(error) : resolve())); + }); +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/change_passthrough_env_config/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/change_passthrough_env_config/snapshots.toml index b8292dd478..d729a5c545 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/change_passthrough_env_config/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/change_passthrough_env_config/snapshots.toml @@ -2,7 +2,8 @@ name = "change_passthrough_env_config" vp = "local" steps = [ + { argv = ["node", "check-temp-socket.mjs"], comment = "The isolated temporary directory must support Unix sockets even for long fixture names", snapshot = false }, { argv = ["vp", "run", "hello"], envs = [["MY_ENV", "1"]], continue-on-failure = true }, - { argv = ["vp", "run", "hello"], comment = "MY_ENV is pass-through. should hit the cache created in step 1", envs = [["MY_ENV", "2"]], continue-on-failure = true }, + { argv = ["vp", "run", "hello"], comment = "MY_ENV is pass-through. should hit the cache created by the first run", envs = [["MY_ENV", "2"]], continue-on-failure = true }, { argv = ["vp", "run", "hello"], comment = "cache should be invalidated because untrackedEnv config changed", envs = [["VITE_TASK_PASS_THROUGH_ENVS", "MY_ENV,MY_ENV2"], ["MY_ENV", "2"]], continue-on-failure = true }, ] diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/change_passthrough_env_config/snapshots/change_passthrough_env_config.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/change_passthrough_env_config/snapshots/change_passthrough_env_config.md index a5504583d7..1f0c02ba38 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/change_passthrough_env_config/snapshots/change_passthrough_env_config.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/change_passthrough_env_config/snapshots/change_passthrough_env_config.md @@ -1,5 +1,10 @@ # change_passthrough_env_config +## `node check-temp-socket.mjs` + +The isolated temporary directory must support Unix sockets even for long fixture names + + ## `MY_ENV=1 vp run hello` ``` @@ -9,7 +14,7 @@ $ node -p process.env.MY_ENV ## `MY_ENV=2 vp run hello` -MY_ENV is pass-through. should hit the cache created in step 1 +MY_ENV is pass-through. should hit the cache created by the first run ``` $ node -p process.env.MY_ENV โ—‰ cache hit, replaying diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_add_npm10/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_add_npm10/snapshots.toml index fabb0bc7a1..cc7eb6fe1e 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_add_npm10/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_add_npm10/snapshots.toml @@ -1,7 +1,6 @@ [[case]] name = "command_add_npm10" vp = "global" -serial = true skip-platforms = ["windows"] steps = [ { argv = ["vp", "add", "--help"], comment = "should show help", continue-on-failure = true }, diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_add_npm10_with_workspace/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_add_npm10_with_workspace/snapshots.toml index e45d3eca1e..94725cb7c7 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_add_npm10_with_workspace/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_add_npm10_with_workspace/snapshots.toml @@ -1,7 +1,6 @@ [[case]] name = "command_add_npm10_with_workspace" vp = "global" -serial = true skip-platforms = ["windows"] steps = [ { argv = ["vp", "add", "testnpm2", "-D", "-w", "--", "--no-audit"], comment = "should add package to workspace root" }, diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_cache_npm10/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_cache_npm10/snapshots.toml index 40773ed7a3..48acea1e7b 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_cache_npm10/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_cache_npm10/snapshots.toml @@ -1,7 +1,6 @@ [[case]] name = "command_cache_npm10" vp = "global" -serial = true skip-platforms = ["windows"] steps = [ { argv = ["vp", "pm", "cache", "dir"], comment = "should show cache directory (uses npm config get cache)", continue-on-failure = true }, diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_cache_yarn4/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_cache_yarn4/snapshots.toml index 4e06f89409..f8ab5f1a61 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_cache_yarn4/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_cache_yarn4/snapshots.toml @@ -1,6 +1,8 @@ [[case]] name = "command_cache_yarn4" vp = "global" +# This case checks Yarn's default cache location in the isolated case home. +unset-env = ["YARN_GLOBAL_FOLDER"] skip-platforms = ["windows", "macos"] steps = [ { argv = ["vp", "pm", "cache", "dir"], comment = "should show cache directory (uses yarn config get cacheFolder)", continue-on-failure = true }, diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_config_npm10/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_config_npm10/snapshots.toml index a75352cb08..3c9af504e7 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_config_npm10/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_config_npm10/snapshots.toml @@ -3,7 +3,6 @@ name = "command_config_npm10" # Exceeded 20 seconds on Windows in CI run 35299447671. skip-platforms = ["windows"] vp = "global" -serial = true steps = [ { argv = ["vp", "pm", "config", "--help"], comment = "should show help", continue-on-failure = true }, { argv = ["vp", "pm", "config", "get", "vite-plus-pm-config-test-key", "--location", "project"], comment = "should get config value from project scope", continue-on-failure = true }, diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_config_yarn4/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_config_yarn4/snapshots.toml index e3b34fbc6c..02dee77d86 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_config_yarn4/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_config_yarn4/snapshots.toml @@ -10,6 +10,8 @@ steps = [ [[case]] name = "berry_user_config_preserves_project" +# File configuration precedence is the subject of this case. +unset-env = ["YARN_NPM_REGISTRY_SERVER"] vp = "global" steps = [ { argv = ["vpt", "json-edit", "package.json", "packageManager", "yarn@4.18.0"], snapshot = false }, diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dedupe_npm10/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dedupe_npm10/snapshots.toml index 8dd094d703..d5181dc673 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dedupe_npm10/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dedupe_npm10/snapshots.toml @@ -3,7 +3,6 @@ name = "command_dedupe_npm10" # Exceeded 20 seconds on Windows in CI run 35299447671. skip-platforms = ["windows"] vp = "global" -serial = true steps = [ { argv = ["vp", "dedupe"], comment = "should dedupe dependencies" }, { argv = ["vpt", "print-file", "package.json"], continue-on-failure = true }, diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_bun/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_bun/snapshots.toml index 3aca2ab10e..f6258e6143 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_bun/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_bun/snapshots.toml @@ -7,6 +7,6 @@ skip-platforms = ["windows"] # Isolate them so tarball URLs cannot refer to a previous test registry. steps = [ { argv = ["vp", "dlx", "--help"], comment = "should show help message", continue-on-failure = true }, - { argv = ["vp", "dlx", "-s", "cowsay", "hello"], envs = [["TMPDIR", "${workspace}"]], comment = "should run cowsay with bun x", continue-on-failure = true }, - { argv = ["vp", "dlx", "-s", "cowsay@1.6.0", "hello"], envs = [["TMPDIR", "${workspace}"]], comment = "should run specific version", continue-on-failure = true }, + { argv = ["vp", "dlx", "-s", "vp-test-echo", "hello"], envs = [["TMPDIR", "${workspace}"]], comment = "should run vp-test-echo with bun x", continue-on-failure = true }, + { argv = ["vp", "dlx", "-s", "vp-test-echo@1.0.0", "hello"], envs = [["TMPDIR", "${workspace}"]], comment = "should run specific version", continue-on-failure = true }, ] diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_bun/snapshots/command_dlx_bun.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_bun/snapshots/command_dlx_bun.md index 6207d1c563..7b8182184d 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_bun/snapshots/command_dlx_bun.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_bun/snapshots/command_dlx_bun.md @@ -23,32 +23,18 @@ Options: Documentation: https://viteplus.dev/guide/vpx ``` -## `TMPDIR=${workspace} vp dlx -s cowsay hello` +## `TMPDIR=${workspace} vp dlx -s vp-test-echo hello` -should run cowsay with bun x +should run vp-test-echo with bun x ``` - _______ -< hello > - ------- - \ ^__^ - \ (oo)\_______ - (__)\ )\/\ - ||----w | - || || +hello ``` -## `TMPDIR=${workspace} vp dlx -s cowsay@1.6.0 hello` +## `TMPDIR=${workspace} vp dlx -s vp-test-echo@1.0.0 hello` should run specific version ``` - _______ -< hello > - ------- - \ ^__^ - \ (oo)\_______ - (__)\ )\/\ - ||----w | - || || +hello ``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_no_package_json/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_no_package_json/snapshots.toml index 534a6ebeb6..9658a188b2 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_no_package_json/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_no_package_json/snapshots.toml @@ -1,8 +1,8 @@ [[case]] name = "command_dlx_no_package_json" vp = "global" -serial = true +local-registry = true skip-platforms = ["windows"] steps = [ - { argv = ["vp", "dlx", "-s", "cowsay", "hello"], comment = "should work without package.json", continue-on-failure = true }, + { argv = ["vp", "dlx", "-s", "vp-test-echo", "hello"], comment = "should work without package.json", continue-on-failure = true }, ] diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_no_package_json/snapshots/command_dlx_no_package_json.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_no_package_json/snapshots/command_dlx_no_package_json.md index 2baf27824f..8c7ca7fdcd 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_no_package_json/snapshots/command_dlx_no_package_json.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_no_package_json/snapshots/command_dlx_no_package_json.md @@ -1,16 +1,9 @@ # command_dlx_no_package_json -## `vp dlx -s cowsay hello` +## `vp dlx -s vp-test-echo hello` should work without package.json ``` - _______ -< hello > - ------- - \ ^__^ - \ (oo)\_______ - (__)\ )\/\ - ||----w | - || || +hello ``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_npm10/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_npm10/snapshots.toml index 48d33b6331..568eefbc96 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_npm10/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_npm10/snapshots.toml @@ -1,13 +1,13 @@ [[case]] name = "command_dlx_npm10" vp = "global" -serial = true local-registry = true skip-platforms = ["windows", { os = "linux", libc = "musl" }] +env = { npm_config_audit = "false" } steps = [ { argv = ["vp", "dlx", "--help"], comment = "should show help message", continue-on-failure = true }, - { argv = ["vp", "dlx", "-s", "cowsay", "hello"], comment = "should run cowsay with npm exec", continue-on-failure = true }, - { argv = ["vp", "dlx", "-s", "cowsay@1.6.0", "hello"], comment = "should run specific version", continue-on-failure = true }, - { argv = ["vp", "dlx", "-s", "-p", "cowsay", "-p", "lolcatjs", "cowsay", "hello"], comment = "should run with multiple packages", continue-on-failure = true }, - { argv = ["vp", "dlx", "-s", "-p", "cowsay", "-c", "echo hello-from-shell | cowsay"], comment = "should run shell mode command (vp's own shell handles the pipe)", continue-on-failure = true }, + { argv = ["vp", "dlx", "-s", "vp-test-echo", "hello"], comment = "should run vp-test-echo with npm exec", continue-on-failure = true }, + { argv = ["vp", "dlx", "-s", "vp-test-echo@1.0.0", "hello"], comment = "should run specific version", continue-on-failure = true }, + { argv = ["vp", "dlx", "-s", "-p", "vp-test-echo", "-p", "vp-test-upper", "vp-test-echo", "hello"], comment = "should run with multiple packages", continue-on-failure = true }, + { argv = ["vp", "dlx", "-s", "-p", "vp-test-echo", "-p", "vp-test-upper", "-c", "echo hello-from-shell | vp-test-echo | vp-test-upper"], comment = "should run shell mode command (vp's own shell handles the pipe)", continue-on-failure = true }, ] diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_npm10/snapshots/command_dlx_npm10.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_npm10/snapshots/command_dlx_npm10.md index 0a59bbebc5..8afdc25a35 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_npm10/snapshots/command_dlx_npm10.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_npm10/snapshots/command_dlx_npm10.md @@ -23,62 +23,34 @@ Options: Documentation: https://viteplus.dev/guide/vpx ``` -## `vp dlx -s cowsay hello` +## `vp dlx -s vp-test-echo hello` -should run cowsay with npm exec +should run vp-test-echo with npm exec ``` - _______ -< hello > - ------- - \ ^__^ - \ (oo)\_______ - (__)\ )\/\ - ||----w | - || || +hello ``` -## `vp dlx -s cowsay@1.6.0 hello` +## `vp dlx -s vp-test-echo@1.0.0 hello` should run specific version ``` - _______ -< hello > - ------- - \ ^__^ - \ (oo)\_______ - (__)\ )\/\ - ||----w | - || || +hello ``` -## `vp dlx -s -p cowsay -p lolcatjs cowsay hello` +## `vp dlx -s -p vp-test-echo -p vp-test-upper vp-test-echo hello` should run with multiple packages ``` - _______ -< hello > - ------- - \ ^__^ - \ (oo)\_______ - (__)\ )\/\ - ||----w | - || || +hello ``` -## `vp dlx -s -p cowsay -c 'echo hello-from-shell | cowsay'` +## `vp dlx -s -p vp-test-echo -p vp-test-upper -c 'echo hello-from-shell | vp-test-echo | vp-test-upper'` should run shell mode command (vp's own shell handles the pipe) ``` - __________________ -< hello-from-shell > - ------------------ - \ ^__^ - \ (oo)\_______ - (__)\ )\/\ - ||----w | - || || +HELLO-FROM-SHELL ``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_pnpm10/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_pnpm10/snapshots.toml index 87a03881b6..3e3449480b 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_pnpm10/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_pnpm10/snapshots.toml @@ -4,6 +4,6 @@ vp = ["local", "global"] local-registry = true steps = [ { argv = ["vp", "dlx", "--help"], comment = "should show help message", continue-on-failure = true }, - { argv = ["vp", "dlx", "-s", "cowsay", "hello"], comment = "should run cowsay with pnpm dlx", continue-on-failure = true }, - { argv = ["vp", "dlx", "-s", "cowsay@1.6.0", "hello"], comment = "should run specific version", continue-on-failure = true }, + { argv = ["vp", "dlx", "-s", "vp-test-echo", "hello"], comment = "should run vp-test-echo with pnpm dlx", continue-on-failure = true }, + { argv = ["vp", "dlx", "-s", "vp-test-echo@1.0.0", "hello"], comment = "should run specific version", continue-on-failure = true }, ] diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_pnpm10/snapshots/command_dlx_pnpm10.global.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_pnpm10/snapshots/command_dlx_pnpm10.global.md index c9008ee5ed..89e0f5915e 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_pnpm10/snapshots/command_dlx_pnpm10.global.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_pnpm10/snapshots/command_dlx_pnpm10.global.md @@ -23,32 +23,18 @@ Options: Documentation: https://viteplus.dev/guide/vpx ``` -## `vp dlx -s cowsay hello` +## `vp dlx -s vp-test-echo hello` -should run cowsay with pnpm dlx +should run vp-test-echo with pnpm dlx ``` - _______ -< hello > - ------- - \ ^__^ - \ (oo)\_______ - (__)\ )\/\ - ||----w | - || || +hello ``` -## `vp dlx -s cowsay@1.6.0 hello` +## `vp dlx -s vp-test-echo@1.0.0 hello` should run specific version ``` - _______ -< hello > - ------- - \ ^__^ - \ (oo)\_______ - (__)\ )\/\ - ||----w | - || || +hello ``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_pnpm10/snapshots/command_dlx_pnpm10.local.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_pnpm10/snapshots/command_dlx_pnpm10.local.md index cadfb18bf1..424f79cb14 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_pnpm10/snapshots/command_dlx_pnpm10.local.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_pnpm10/snapshots/command_dlx_pnpm10.local.md @@ -19,32 +19,18 @@ Options: -h, --help Print help ``` -## `vp dlx -s cowsay hello` +## `vp dlx -s vp-test-echo hello` -should run cowsay with pnpm dlx +should run vp-test-echo with pnpm dlx ``` - _______ -< hello > - ------- - \ ^__^ - \ (oo)\_______ - (__)\ )\/\ - ||----w | - || || +hello ``` -## `vp dlx -s cowsay@1.6.0 hello` +## `vp dlx -s vp-test-echo@1.0.0 hello` should run specific version ``` - _______ -< hello > - ------- - \ ^__^ - \ (oo)\_______ - (__)\ )\/\ - ||----w | - || || +hello ``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_pnpm11/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_pnpm11/snapshots.toml index 3062864b9a..a1f1b90121 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_pnpm11/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_pnpm11/snapshots.toml @@ -4,6 +4,6 @@ vp = "global" local-registry = true steps = [ { argv = ["vp", "dlx", "--help"], comment = "should show help message", continue-on-failure = true }, - { argv = ["vp", "dlx", "-s", "cowsay", "hello"], comment = "should run cowsay with pnpm dlx", continue-on-failure = true }, - { argv = ["vp", "dlx", "-s", "cowsay@1.6.0", "hello"], comment = "should run specific version", continue-on-failure = true }, + { argv = ["vp", "dlx", "-s", "vp-test-echo", "hello"], comment = "should run vp-test-echo with pnpm dlx", continue-on-failure = true }, + { argv = ["vp", "dlx", "-s", "vp-test-echo@1.0.0", "hello"], comment = "should run specific version", continue-on-failure = true }, ] diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_pnpm11/snapshots/command_dlx_pnpm11.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_pnpm11/snapshots/command_dlx_pnpm11.md index e3eb5a114d..c931c1df38 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_pnpm11/snapshots/command_dlx_pnpm11.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_pnpm11/snapshots/command_dlx_pnpm11.md @@ -23,32 +23,18 @@ Options: Documentation: https://viteplus.dev/guide/vpx ``` -## `vp dlx -s cowsay hello` +## `vp dlx -s vp-test-echo hello` -should run cowsay with pnpm dlx +should run vp-test-echo with pnpm dlx ``` - _______ -< hello > - ------- - \ ^__^ - \ (oo)\_______ - (__)\ )\/\ - ||----w | - || || +hello ``` -## `vp dlx -s cowsay@1.6.0 hello` +## `vp dlx -s vp-test-echo@1.0.0 hello` should run specific version ``` - _______ -< hello > - ------- - \ ^__^ - \ (oo)\_______ - (__)\ )\/\ - ||----w | - || || +hello ``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_yarn4/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_yarn4/snapshots.toml index 86500c6708..b6263f3b4d 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_yarn4/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_yarn4/snapshots.toml @@ -4,6 +4,6 @@ vp = "global" local-registry = true steps = [ { argv = ["vp", "dlx", "--help"], comment = "should show help message", continue-on-failure = true }, - { argv = ["vp", "dlx", "-s", "cowsay", "hello"], comment = "should run cowsay with yarn dlx", continue-on-failure = true }, - { argv = ["vp", "dlx", "-s", "cowsay@1.6.0", "hello"], comment = "should run specific version", continue-on-failure = true }, + { argv = ["vp", "dlx", "-s", "vp-test-echo", "hello"], comment = "should run vp-test-echo with yarn dlx", continue-on-failure = true }, + { argv = ["vp", "dlx", "-s", "vp-test-echo@1.0.0", "hello"], comment = "should run specific version", continue-on-failure = true }, ] diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_yarn4/snapshots/command_dlx_yarn4.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_yarn4/snapshots/command_dlx_yarn4.md index 0f97c6ce18..a15580bfd4 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_yarn4/snapshots/command_dlx_yarn4.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_dlx_yarn4/snapshots/command_dlx_yarn4.md @@ -23,32 +23,18 @@ Options: Documentation: https://viteplus.dev/guide/vpx ``` -## `vp dlx -s cowsay hello` +## `vp dlx -s vp-test-echo hello` -should run cowsay with yarn dlx +should run vp-test-echo with yarn dlx ``` - _______ -< hello > - ------- - \ ^__^ - \ (oo)\_______ - (__)\ )\/\ - ||----w | - || || +hello ``` -## `vp dlx -s cowsay@1.6.0 hello` +## `vp dlx -s vp-test-echo@1.0.0 hello` should run specific version ``` - _______ -< hello > - ------- - \ ^__^ - \ (oo)\_______ - (__)\ )\/\ - ||----w | - || || +hello ``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_env_which/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_env_which/snapshots.toml index f4e7ee037a..ff82b709f3 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_env_which/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_env_which/snapshots.toml @@ -8,9 +8,9 @@ steps = [ { argv = ["vp", "env", "which", "node"], comment = "Core tool - shows resolved Node.js binary path", continue-on-failure = true }, { argv = ["vp", "env", "which", "npm"], comment = "Core tool - shows resolved npm binary path", continue-on-failure = true }, { argv = ["vp", "env", "which", "npx"], comment = "Core tool - shows resolved npx binary path", continue-on-failure = true }, - { argv = ["vp", "install", "-g", "cowsay@1.6.0"], comment = "Install a global package via vp", continue-on-failure = true }, - { argv = ["vp", "env", "which", "cowsay"], comment = "Global package - shows binary path with metadata", continue-on-failure = true }, - { argv = ["vp", "remove", "-g", "cowsay"], comment = "Cleanup", continue-on-failure = true }, + { argv = ["vp", "install", "-g", "vp-test-echo@1.0.0"], comment = "Install a global package via vp", continue-on-failure = true }, + { argv = ["vp", "env", "which", "vp-test-echo"], comment = "Global package - shows binary path with metadata", continue-on-failure = true }, + { argv = ["vp", "remove", "-g", "vp-test-echo"], comment = "Cleanup", continue-on-failure = true }, { argv = ["vp", "env", "which", "unknown-tool"], comment = "Unknown tool - error message", continue-on-failure = true }, ] diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_env_which/snapshots/command_env_which.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_env_which/snapshots/command_env_which.md index c52fb7b2b1..0f2ae38f94 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_env_which/snapshots/command_env_which.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_env_which/snapshots/command_env_which.md @@ -44,7 +44,7 @@ VITE+ - The Unified Toolchain for the Web Source: /.node-version ``` -## `vp install -g cowsay@1.6.0` +## `vp install -g vp-test-echo@1.0.0` Install a global package via vp @@ -52,30 +52,30 @@ Install a global package via vp VITE+ - The Unified Toolchain for the Web info: Installing 1 global package with Node.js - Installed cowsay 1.6.0 - Bins: cowsay, cowthink + Installed vp-test-echo 1.0.0 + Bins: vp-test-echo, vp-test-echo-alias ``` -## `vp env which cowsay` +## `vp env which vp-test-echo` Global package - shows binary path with metadata ``` VITE+ - The Unified Toolchain for the Web -/.vite-plus/packages/cowsay//lib/node_modules/cowsay/./cli.js - Package: cowsay@1.6.0 - Binaries: cowsay, cowthink +/.vite-plus/packages/vp-test-echo//lib/node_modules/vp-test-echo/cli.cjs + Package: vp-test-echo@1.0.0 + Binaries: vp-test-echo, vp-test-echo-alias Node: Installed: ``` -## `vp remove -g cowsay` +## `vp remove -g vp-test-echo` Cleanup ``` -- Uninstalled cowsay +- Uninstalled vp-test-echo ``` ## `vp env which unknown-tool` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_link_npm10/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_link_npm10/snapshots.toml index 501bbe8b0e..2bb9b564b7 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_link_npm10/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_link_npm10/snapshots.toml @@ -1,7 +1,6 @@ [[case]] name = "command_link_npm10" vp = "global" -serial = true skip-platforms = ["windows"] steps = [ { argv = ["vpt", "mkdir", "-p", "../test-lib-npm"], comment = "create test library" }, diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_list_npm10/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_list_npm10/snapshots.toml index 154d4e057d..eaf6060a06 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_list_npm10/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_list_npm10/snapshots.toml @@ -1,7 +1,6 @@ [[case]] name = "command_list_npm10" vp = "global" -serial = true skip-platforms = ["windows"] steps = [ { argv = ["vp", "install"], comment = "should install packages first", continue-on-failure = true }, diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_list_npm10/snapshots/command_list_npm10.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_list_npm10/snapshots/command_list_npm10.md index b43afc9151..1d3780aa33 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_list_npm10/snapshots/command_list_npm10.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_list_npm10/snapshots/command_list_npm10.md @@ -23,17 +23,17 @@ should list installed packages "dependencies": { "test-vite-plus-package-optional": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/test-vite-plus-package-optional/-/test-vite-plus-package-optional-1.0.0.tgz", + "resolved": "http://127.0.0.1:/test-vite-plus-package-optional/-/test-vite-plus-package-optional-1.0.0.tgz", "overridden": false }, "test-vite-plus-package": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/test-vite-plus-package/-/test-vite-plus-package-1.0.0.tgz", + "resolved": "http://127.0.0.1:/test-vite-plus-package/-/test-vite-plus-package-1.0.0.tgz", "overridden": false }, "testnpm2": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/testnpm2/-/testnpm2-1.0.1.tgz", + "resolved": "http://127.0.0.1:/testnpm2/-/testnpm2-1.0.1.tgz", "overridden": false } } @@ -51,7 +51,7 @@ should list specific package "dependencies": { "testnpm2": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/testnpm2/-/testnpm2-1.0.1.tgz", + "resolved": "http://127.0.0.1:/testnpm2/-/testnpm2-1.0.1.tgz", "overridden": false } } @@ -69,17 +69,17 @@ should list packages with depth limit "dependencies": { "test-vite-plus-package-optional": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/test-vite-plus-package-optional/-/test-vite-plus-package-optional-1.0.0.tgz", + "resolved": "http://127.0.0.1:/test-vite-plus-package-optional/-/test-vite-plus-package-optional-1.0.0.tgz", "overridden": false }, "test-vite-plus-package": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/test-vite-plus-package/-/test-vite-plus-package-1.0.0.tgz", + "resolved": "http://127.0.0.1:/test-vite-plus-package/-/test-vite-plus-package-1.0.0.tgz", "overridden": false }, "testnpm2": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/testnpm2/-/testnpm2-1.0.1.tgz", + "resolved": "http://127.0.0.1:/testnpm2/-/testnpm2-1.0.1.tgz", "overridden": false } } @@ -110,7 +110,7 @@ should list packages with extended info "dependencies": { "test-vite-plus-package-optional": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/test-vite-plus-package-optional/-/test-vite-plus-package-optional-1.0.0.tgz", + "resolved": "http://127.0.0.1:/test-vite-plus-package-optional/-/test-vite-plus-package-optional-1.0.0.tgz", "overridden": false, "name": "test-vite-plus-package-optional", "integrity": "sha512-7rJ71VaETMzhK3Iwd14vutJtZt90MqUhu9KwnEEe55htLIIJs/WImI02NopwsV9Ra/Rqld4KkvbvOy6MD0b1sw==", @@ -125,7 +125,7 @@ should list packages with extended info }, "test-vite-plus-package": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/test-vite-plus-package/-/test-vite-plus-package-1.0.0.tgz", + "resolved": "http://127.0.0.1:/test-vite-plus-package/-/test-vite-plus-package-1.0.0.tgz", "overridden": false, "name": "test-vite-plus-package", "integrity": "sha512-xj5hJUNL5vihX9TCYXbk8V5r+8MfqG2fDJcRdpQz6tlLob3vkFjsVM8pT5QjTzHS6lBXHd0r9TOMS8+sETjnpw==", @@ -140,7 +140,7 @@ should list packages with extended info }, "testnpm2": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/testnpm2/-/testnpm2-1.0.1.tgz", + "resolved": "http://127.0.0.1:/testnpm2/-/testnpm2-1.0.1.tgz", "overridden": false, "name": "testnpm2", "integrity": "sha512-F4AQ+KmzhbOSlt7ae+X2O8IJktFZAcN6OK169TT4ny7M3e4Vje7NITZTOU31AtEk9L/Z8lrCrqinl/eY6WPuEw==", @@ -167,17 +167,17 @@ should list packages in parseable format "dependencies": { "test-vite-plus-package-optional": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/test-vite-plus-package-optional/-/test-vite-plus-package-optional-1.0.0.tgz", + "resolved": "http://127.0.0.1:/test-vite-plus-package-optional/-/test-vite-plus-package-optional-1.0.0.tgz", "overridden": false }, "test-vite-plus-package": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/test-vite-plus-package/-/test-vite-plus-package-1.0.0.tgz", + "resolved": "http://127.0.0.1:/test-vite-plus-package/-/test-vite-plus-package-1.0.0.tgz", "overridden": false }, "testnpm2": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/testnpm2/-/testnpm2-1.0.1.tgz", + "resolved": "http://127.0.0.1:/testnpm2/-/testnpm2-1.0.1.tgz", "overridden": false } } @@ -195,12 +195,12 @@ should exclude dev dependencies using --omit dev "dependencies": { "test-vite-plus-package-optional": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/test-vite-plus-package-optional/-/test-vite-plus-package-optional-1.0.0.tgz", + "resolved": "http://127.0.0.1:/test-vite-plus-package-optional/-/test-vite-plus-package-optional-1.0.0.tgz", "overridden": false }, "testnpm2": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/testnpm2/-/testnpm2-1.0.1.tgz", + "resolved": "http://127.0.0.1:/testnpm2/-/testnpm2-1.0.1.tgz", "overridden": false } } @@ -218,17 +218,17 @@ include installed dev dependencies in a production environment "dependencies": { "test-vite-plus-package-optional": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/test-vite-plus-package-optional/-/test-vite-plus-package-optional-1.0.0.tgz", + "resolved": "http://127.0.0.1:/test-vite-plus-package-optional/-/test-vite-plus-package-optional-1.0.0.tgz", "overridden": false }, "test-vite-plus-package": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/test-vite-plus-package/-/test-vite-plus-package-1.0.0.tgz", + "resolved": "http://127.0.0.1:/test-vite-plus-package/-/test-vite-plus-package-1.0.0.tgz", "overridden": false }, "testnpm2": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/testnpm2/-/testnpm2-1.0.1.tgz", + "resolved": "http://127.0.0.1:/testnpm2/-/testnpm2-1.0.1.tgz", "overridden": false } } @@ -246,17 +246,17 @@ should exclude optional dependencies (uses --omit optional) "dependencies": { "test-vite-plus-package-optional": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/test-vite-plus-package-optional/-/test-vite-plus-package-optional-1.0.0.tgz", + "resolved": "http://127.0.0.1:/test-vite-plus-package-optional/-/test-vite-plus-package-optional-1.0.0.tgz", "overridden": false }, "test-vite-plus-package": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/test-vite-plus-package/-/test-vite-plus-package-1.0.0.tgz", + "resolved": "http://127.0.0.1:/test-vite-plus-package/-/test-vite-plus-package-1.0.0.tgz", "overridden": false }, "testnpm2": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/testnpm2/-/testnpm2-1.0.1.tgz", + "resolved": "http://127.0.0.1:/testnpm2/-/testnpm2-1.0.1.tgz", "overridden": false } } @@ -274,12 +274,12 @@ should exclude peer dependencies (uses --omit peer) "dependencies": { "test-vite-plus-package": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/test-vite-plus-package/-/test-vite-plus-package-1.0.0.tgz", + "resolved": "http://127.0.0.1:/test-vite-plus-package/-/test-vite-plus-package-1.0.0.tgz", "overridden": false }, "testnpm2": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/testnpm2/-/testnpm2-1.0.1.tgz", + "resolved": "http://127.0.0.1:/testnpm2/-/testnpm2-1.0.1.tgz", "overridden": false } } @@ -297,17 +297,17 @@ should support pass through arguments "dependencies": { "test-vite-plus-package-optional": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/test-vite-plus-package-optional/-/test-vite-plus-package-optional-1.0.0.tgz", + "resolved": "http://127.0.0.1:/test-vite-plus-package-optional/-/test-vite-plus-package-optional-1.0.0.tgz", "overridden": false }, "test-vite-plus-package": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/test-vite-plus-package/-/test-vite-plus-package-1.0.0.tgz", + "resolved": "http://127.0.0.1:/test-vite-plus-package/-/test-vite-plus-package-1.0.0.tgz", "overridden": false }, "testnpm2": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/testnpm2/-/testnpm2-1.0.1.tgz", + "resolved": "http://127.0.0.1:/testnpm2/-/testnpm2-1.0.1.tgz", "overridden": false } } diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_list_npm10/snapshots/npm_list_dependency_selection.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_list_npm10/snapshots/npm_list_dependency_selection.md index 403e1e69aa..cf8dae202d 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_list_npm10/snapshots/npm_list_dependency_selection.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_list_npm10/snapshots/npm_list_dependency_selection.md @@ -17,17 +17,17 @@ production listing excludes dev but keeps optional and peer dependencies "dependencies": { "test-vite-plus-package-optional": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/test-vite-plus-package-optional/-/test-vite-plus-package-optional-1.0.0.tgz", + "resolved": "http://127.0.0.1:/test-vite-plus-package-optional/-/test-vite-plus-package-optional-1.0.0.tgz", "overridden": false }, "testnpm2": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/testnpm2/-/testnpm2-1.0.1.tgz", + "resolved": "http://127.0.0.1:/testnpm2/-/testnpm2-1.0.1.tgz", "overridden": false }, "yocto-queue": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "resolved": "http://127.0.0.1:/yocto-queue/-/yocto-queue-0.1.0.tgz", "overridden": false } } @@ -45,7 +45,7 @@ production listing respects both exclusions "dependencies": { "testnpm2": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/testnpm2/-/testnpm2-1.0.1.tgz", + "resolved": "http://127.0.0.1:/testnpm2/-/testnpm2-1.0.1.tgz", "overridden": false } } @@ -63,22 +63,22 @@ include installed dev dependencies without excluding other dependency types "dependencies": { "test-vite-plus-package-optional": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/test-vite-plus-package-optional/-/test-vite-plus-package-optional-1.0.0.tgz", + "resolved": "http://127.0.0.1:/test-vite-plus-package-optional/-/test-vite-plus-package-optional-1.0.0.tgz", "overridden": false }, "test-vite-plus-package": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/test-vite-plus-package/-/test-vite-plus-package-1.0.0.tgz", + "resolved": "http://127.0.0.1:/test-vite-plus-package/-/test-vite-plus-package-1.0.0.tgz", "overridden": false }, "testnpm2": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/testnpm2/-/testnpm2-1.0.1.tgz", + "resolved": "http://127.0.0.1:/testnpm2/-/testnpm2-1.0.1.tgz", "overridden": false }, "yocto-queue": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "resolved": "http://127.0.0.1:/yocto-queue/-/yocto-queue-0.1.0.tgz", "overridden": false } } @@ -96,22 +96,22 @@ raw native include remains available and lists all four dependency types "dependencies": { "test-vite-plus-package-optional": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/test-vite-plus-package-optional/-/test-vite-plus-package-optional-1.0.0.tgz", + "resolved": "http://127.0.0.1:/test-vite-plus-package-optional/-/test-vite-plus-package-optional-1.0.0.tgz", "overridden": false }, "test-vite-plus-package": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/test-vite-plus-package/-/test-vite-plus-package-1.0.0.tgz", + "resolved": "http://127.0.0.1:/test-vite-plus-package/-/test-vite-plus-package-1.0.0.tgz", "overridden": false }, "testnpm2": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/testnpm2/-/testnpm2-1.0.1.tgz", + "resolved": "http://127.0.0.1:/testnpm2/-/testnpm2-1.0.1.tgz", "overridden": false }, "yocto-queue": { "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "resolved": "http://127.0.0.1:/yocto-queue/-/yocto-queue-0.1.0.tgz", "overridden": false } } diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_list_npm10_with_workspace/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_list_npm10_with_workspace/snapshots.toml index a819cc8e36..fa9317a756 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_list_npm10_with_workspace/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_list_npm10_with_workspace/snapshots.toml @@ -1,7 +1,6 @@ [[case]] name = "command_list_npm10_with_workspace" vp = "global" -serial = true steps = [ { argv = ["vp", "install"], comment = "should install packages first", continue-on-failure = true }, { argv = ["vp", "pm", "list", "--json"], comment = "should list current workspace root dependencies", continue-on-failure = true }, diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_list_npm10_with_workspace/snapshots/command_list_npm10_with_workspace.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_list_npm10_with_workspace/snapshots/command_list_npm10_with_workspace.md index 5e3c7ebfee..c9198304d4 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_list_npm10_with_workspace/snapshots/command_list_npm10_with_workspace.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_list_npm10_with_workspace/snapshots/command_list_npm10_with_workspace.md @@ -28,7 +28,7 @@ should list current workspace root dependencies "dependencies": { "testnpm2": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/testnpm2/-/testnpm2-1.0.1.tgz", + "resolved": "http://127.0.0.1:/testnpm2/-/testnpm2-1.0.1.tgz", "overridden": false } } @@ -40,7 +40,7 @@ should list current workspace root dependencies "dependencies": { "test-vite-plus-package-optional": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/test-vite-plus-package-optional/-/test-vite-plus-package-optional-1.0.0.tgz", + "resolved": "http://127.0.0.1:/test-vite-plus-package-optional/-/test-vite-plus-package-optional-1.0.0.tgz", "overridden": false }, "testnpm2": { @@ -68,7 +68,7 @@ should list all packages in workspace (uses --workspaces) "dependencies": { "testnpm2": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/testnpm2/-/testnpm2-1.0.1.tgz", + "resolved": "http://127.0.0.1:/testnpm2/-/testnpm2-1.0.1.tgz", "overridden": false } } @@ -80,7 +80,7 @@ should list all packages in workspace (uses --workspaces) "dependencies": { "test-vite-plus-package-optional": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/test-vite-plus-package-optional/-/test-vite-plus-package-optional-1.0.0.tgz", + "resolved": "http://127.0.0.1:/test-vite-plus-package-optional/-/test-vite-plus-package-optional-1.0.0.tgz", "overridden": false }, "testnpm2": { @@ -108,12 +108,12 @@ should list specific workspace package (uses --workspace app) "dependencies": { "test-vite-plus-package-optional": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/test-vite-plus-package-optional/-/test-vite-plus-package-optional-1.0.0.tgz", + "resolved": "http://127.0.0.1:/test-vite-plus-package-optional/-/test-vite-plus-package-optional-1.0.0.tgz", "overridden": false }, "testnpm2": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/testnpm2/-/testnpm2-1.0.1.tgz", + "resolved": "http://127.0.0.1:/testnpm2/-/testnpm2-1.0.1.tgz", "overridden": false } } @@ -138,7 +138,7 @@ should list multiple workspace packages "dependencies": { "testnpm2": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/testnpm2/-/testnpm2-1.0.1.tgz", + "resolved": "http://127.0.0.1:/testnpm2/-/testnpm2-1.0.1.tgz", "overridden": false } } @@ -150,7 +150,7 @@ should list multiple workspace packages "dependencies": { "test-vite-plus-package-optional": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/test-vite-plus-package-optional/-/test-vite-plus-package-optional-1.0.0.tgz", + "resolved": "http://127.0.0.1:/test-vite-plus-package-optional/-/test-vite-plus-package-optional-1.0.0.tgz", "overridden": false }, "testnpm2": { @@ -178,7 +178,7 @@ should list workspace packages with depth limit "dependencies": { "testnpm2": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/testnpm2/-/testnpm2-1.0.1.tgz", + "resolved": "http://127.0.0.1:/testnpm2/-/testnpm2-1.0.1.tgz", "overridden": false } } @@ -190,7 +190,7 @@ should list workspace packages with depth limit "dependencies": { "test-vite-plus-package-optional": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/test-vite-plus-package-optional/-/test-vite-plus-package-optional-1.0.0.tgz", + "resolved": "http://127.0.0.1:/test-vite-plus-package-optional/-/test-vite-plus-package-optional-1.0.0.tgz", "overridden": false }, "testnpm2": { diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_list_pnpm10/snapshots/command_list_pnpm10.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_list_pnpm10/snapshots/command_list_pnpm10.md index 7da73e290b..ad2ec991a5 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_list_pnpm10/snapshots/command_list_pnpm10.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_list_pnpm10/snapshots/command_list_pnpm10.md @@ -113,13 +113,13 @@ should list packages in JSON format "test-vite-plus-package-optional": { "from": "test-vite-plus-package-optional", "version": "1.0.0", - "resolved": "https://registry.npmjs.org/test-vite-plus-package-optional/-/test-vite-plus-package-optional-1.0.0.tgz", + "resolved": "http://127.0.0.1:/test-vite-plus-package-optional/-/test-vite-plus-package-optional-1.0.0.tgz", "path": "/node_modules/.pnpm/test-vite-plus-package-optional@1.0.0/node_modules/test-vite-plus-package-optional" }, "testnpm2": { "from": "testnpm2", "version": "1.0.1", - "resolved": "https://registry.npmjs.org/testnpm2/-/testnpm2-1.0.1.tgz", + "resolved": "http://127.0.0.1:/testnpm2/-/testnpm2-1.0.1.tgz", "path": "/node_modules/.pnpm/testnpm2@1.0.1/node_modules/testnpm2" } }, @@ -127,7 +127,7 @@ should list packages in JSON format "test-vite-plus-package": { "from": "test-vite-plus-package", "version": "1.0.0", - "resolved": "https://registry.npmjs.org/test-vite-plus-package/-/test-vite-plus-package-1.0.0.tgz", + "resolved": "http://127.0.0.1:/test-vite-plus-package/-/test-vite-plus-package-1.0.0.tgz", "path": "/node_modules/.pnpm/test-vite-plus-package@1.0.0/node_modules/test-vite-plus-package" } } diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_list_pnpm10_with_workspace/snapshots/command_list_pnpm10_with_workspace.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_list_pnpm10_with_workspace/snapshots/command_list_pnpm10_with_workspace.md index c7e7d51c98..7a34a6d748 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_list_pnpm10_with_workspace/snapshots/command_list_pnpm10_with_workspace.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_list_pnpm10_with_workspace/snapshots/command_list_pnpm10_with_workspace.md @@ -97,7 +97,7 @@ should list all workspace packages in JSON format "test-vite-plus-package-optional": { "from": "test-vite-plus-package-optional", "version": "1.0.0", - "resolved": "https://registry.npmjs.org/test-vite-plus-package-optional/-/test-vite-plus-package-optional-1.0.0.tgz", + "resolved": "http://127.0.0.1:/test-vite-plus-package-optional/-/test-vite-plus-package-optional-1.0.0.tgz", "path": "/node_modules/.pnpm/test-vite-plus-package-optional@1.0.0/node_modules/test-vite-plus-package-optional" } } @@ -111,7 +111,7 @@ should list all workspace packages in JSON format "testnpm2": { "from": "testnpm2", "version": "1.0.1", - "resolved": "https://registry.npmjs.org/testnpm2/-/testnpm2-1.0.1.tgz", + "resolved": "http://127.0.0.1:/testnpm2/-/testnpm2-1.0.1.tgz", "path": "/node_modules/.pnpm/testnpm2@1.0.1/node_modules/testnpm2" } } diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_list_pnpm11/snapshots/command_list_pnpm11.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_list_pnpm11/snapshots/command_list_pnpm11.md index df8f7f7e31..5ec2fafde4 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_list_pnpm11/snapshots/command_list_pnpm11.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_list_pnpm11/snapshots/command_list_pnpm11.md @@ -119,13 +119,13 @@ should list packages in JSON format "test-vite-plus-package-optional": { "from": "test-vite-plus-package-optional", "version": "1.0.0", - "resolved": "https://registry.npmjs.org/test-vite-plus-package-optional/-/test-vite-plus-package-optional-1.0.0.tgz", + "resolved": "http://127.0.0.1:/test-vite-plus-package-optional/-/test-vite-plus-package-optional-1.0.0.tgz", "path": "/node_modules/.pnpm/test-vite-plus-package-optional@1.0.0/node_modules/test-vite-plus-package-optional" }, "testnpm2": { "from": "testnpm2", "version": "1.0.1", - "resolved": "https://registry.npmjs.org/testnpm2/-/testnpm2-1.0.1.tgz", + "resolved": "http://127.0.0.1:/testnpm2/-/testnpm2-1.0.1.tgz", "path": "/node_modules/.pnpm/testnpm2@1.0.1/node_modules/testnpm2" } }, @@ -133,7 +133,7 @@ should list packages in JSON format "test-vite-plus-package": { "from": "test-vite-plus-package", "version": "1.0.0", - "resolved": "https://registry.npmjs.org/test-vite-plus-package/-/test-vite-plus-package-1.0.0.tgz", + "resolved": "http://127.0.0.1:/test-vite-plus-package/-/test-vite-plus-package-1.0.0.tgz", "path": "/node_modules/.pnpm/test-vite-plus-package@1.0.0/node_modules/test-vite-plus-package" } } diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_list_pnpm11_with_workspace/snapshots/command_list_pnpm11_with_workspace.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_list_pnpm11_with_workspace/snapshots/command_list_pnpm11_with_workspace.md index 9fa87ff19a..89839de9e5 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_list_pnpm11_with_workspace/snapshots/command_list_pnpm11_with_workspace.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_list_pnpm11_with_workspace/snapshots/command_list_pnpm11_with_workspace.md @@ -117,7 +117,7 @@ should list all workspace packages in JSON format "test-vite-plus-package-optional": { "from": "test-vite-plus-package-optional", "version": "1.0.0", - "resolved": "https://registry.npmjs.org/test-vite-plus-package-optional/-/test-vite-plus-package-optional-1.0.0.tgz", + "resolved": "http://127.0.0.1:/test-vite-plus-package-optional/-/test-vite-plus-package-optional-1.0.0.tgz", "path": "/node_modules/.pnpm/test-vite-plus-package-optional@1.0.0/node_modules/test-vite-plus-package-optional" } } @@ -131,7 +131,7 @@ should list all workspace packages in JSON format "testnpm2": { "from": "testnpm2", "version": "1.0.1", - "resolved": "https://registry.npmjs.org/testnpm2/-/testnpm2-1.0.1.tgz", + "resolved": "http://127.0.0.1:/testnpm2/-/testnpm2-1.0.1.tgz", "path": "/node_modules/.pnpm/testnpm2@1.0.1/node_modules/testnpm2" } } diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_outdated_npm10/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_outdated_npm10/snapshots.toml index d0387e3be0..863f7f62bc 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_outdated_npm10/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_outdated_npm10/snapshots.toml @@ -1,7 +1,6 @@ [[case]] name = "command_outdated_npm10" vp = "global" -serial = true skip-platforms = ["windows"] steps = [ { argv = ["vp", "install"], comment = "should install packages first", continue-on-failure = true }, diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_outdated_npm10_with_workspace/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_outdated_npm10_with_workspace/snapshots.toml index 9120d69ddf..8e7a25d162 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_outdated_npm10_with_workspace/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_outdated_npm10_with_workspace/snapshots.toml @@ -1,7 +1,6 @@ [[case]] name = "command_outdated_npm10_with_workspace" vp = "global" -serial = true skip-platforms = ["windows"] steps = [ { argv = ["vp", "install"], continue-on-failure = true }, diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_owner_npm10/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_owner_npm10/snapshots.toml index 2d9c515936..b5bfd337d1 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_owner_npm10/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_owner_npm10/snapshots.toml @@ -1,7 +1,6 @@ [[case]] name = "command_owner_npm10" vp = "global" -serial = true steps = [ { argv = ["vp", "pm", "owner", "list", "testnpm2"], comment = "should list package owners", continue-on-failure = true }, ] diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_pack_npm10/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_pack_npm10/snapshots.toml index 100965dacf..191e5e12db 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_pack_npm10/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_pack_npm10/snapshots.toml @@ -1,7 +1,6 @@ [[case]] name = "command_pack_npm10" vp = "global" -serial = true skip-platforms = ["windows"] steps = [ { argv = ["vp", "pm", "pack", "--json"], comment = "should pack current package" }, diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_pack_npm10_with_workspace/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_pack_npm10_with_workspace/snapshots.toml index 35f12c6f02..caef1ef927 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_pack_npm10_with_workspace/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_pack_npm10_with_workspace/snapshots.toml @@ -1,7 +1,6 @@ [[case]] name = "command_pack_npm10_with_workspace" vp = "global" -serial = true skip-platforms = ["windows"] steps = [ { argv = ["vp", "pm", "pack", "--json"], comment = "should pack current workspace root", continue-on-failure = true }, diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_prune_npm10/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_prune_npm10/snapshots.toml index 6fb150e121..f7d2a54acc 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_prune_npm10/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_prune_npm10/snapshots.toml @@ -2,7 +2,6 @@ name = "command_prune_npm10" vp = "global" skip-platforms = ["windows"] -serial = true steps = [ { argv = ["vp", "install"], comment = "should install packages first", continue-on-failure = true }, { argv = ["vp", "pm", "prune", "--help"], comment = "should show help", continue-on-failure = true }, diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_publish_npm10/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_publish_npm10/snapshots.toml index 4d56a95bf4..f2fd17bdf5 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_publish_npm10/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_publish_npm10/snapshots.toml @@ -2,7 +2,6 @@ name = "command_publish_npm10" vp = "global" skip-platforms = ["windows"] -serial = true steps = [ { argv = ["vp", "pm", "publish", "--help"], comment = "should show help", continue-on-failure = true }, { argv = ["vp", "pm", "publish", "--dry-run", "--", "--loglevel", "error"], comment = "should preview publish without actually publishing (uses npm publish --dry-run)", continue-on-failure = true }, diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_publish_yarn4/snapshots/command_publish_yarn4.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_publish_yarn4/snapshots/command_publish_yarn4.md index 33baa19e26..be4d9f68cb 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_publish_yarn4/snapshots/command_publish_yarn4.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_publish_yarn4/snapshots/command_publish_yarn4.md @@ -42,7 +42,7 @@ should preview using native yarn npm publish without publishing ``` โžค YN0000: package.json -โžค YN0000: [DRY RUN] Package would be published to https://registry.yarnpkg.com with tag latest +โžค YN0000: [DRY RUN] Package would be published to http://127.0.0.1: with tag latest โžค YN0000: Done in ``` @@ -52,7 +52,7 @@ should preserve supported native JSON output without publishing ``` {"file":"package.json"} -{"name":"command-publish-yarn4","version":"1.0.0","registry":"https://registry.yarnpkg.com","tag":"latest","files":["package.json"],"access":null,"dryRun":true,"published":false,"message":"[DRY RUN] Package would be published to https://registry.yarnpkg.com with tag latest","provenance":false} +{"name":"command-publish-yarn4","version":"1.0.0","registry":"http://127.0.0.1:","tag":"latest","files":["package.json"],"access":null,"dryRun":true,"published":false,"message":"[DRY RUN] Package would be published to http://127.0.0.1: with tag latest","provenance":false} ``` ## `vp pm publish --force -- --help` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_remove_npm10/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_remove_npm10/snapshots.toml index 458f551c4a..03106b9cad 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_remove_npm10/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_remove_npm10/snapshots.toml @@ -9,7 +9,6 @@ steps = [ [[case]] name = "command_remove_npm10" vp = "global" -serial = true skip-platforms = ["windows"] steps = [ { argv = ["vp", "remove", "testnpm2", "-D", "--", "--no-audit"], comment = "delegate removal of an absent package to npm" }, diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_remove_npm10_with_workspace/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_remove_npm10_with_workspace/snapshots.toml index d159a465d9..14de21600e 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_remove_npm10_with_workspace/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_remove_npm10_with_workspace/snapshots.toml @@ -1,7 +1,6 @@ [[case]] name = "command_remove_npm10_with_workspace" vp = "global" -serial = true skip-platforms = ["windows"] steps = [ { argv = ["vp", "add", "testnpm2", "-D", "-w", "--filter=*", "--", "--no-audit"], comment = "prepare packages" }, diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_unlink_npm10/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_unlink_npm10/snapshots.toml index 938bf8f2ca..222a58a81f 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_unlink_npm10/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_unlink_npm10/snapshots.toml @@ -1,7 +1,6 @@ [[case]] name = "command_unlink_npm10" vp = "global" -serial = true skip-platforms = ["windows"] steps = [ { argv = ["vpt", "mkdir", "-p", "../unlink-test-lib-npm"], comment = "create test library" }, diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_update_bun14/snapshots/bun_update_dev_only.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_update_bun14/snapshots/bun_update_dev_only.md index c6e9601184..98b7a99e6d 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_update_bun14/snapshots/bun_update_dev_only.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_update_bun14/snapshots/bun_update_dev_only.md @@ -69,7 +69,7 @@ bun update () "isarray": ["isarray@1.0.0", "http://127.0.0.1:/isarray/-/isarray-1.0.0.tgz", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="], - "yocto-queue": ["yocto-queue@1.0.0", "", {}, "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g=="], + "yocto-queue": ["yocto-queue@1.0.0", "http://127.0.0.1:/yocto-queue/-/yocto-queue-1.0.0.tgz", {}, "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g=="], } } ``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_update_bun14/snapshots/bun_update_no_optional.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_update_bun14/snapshots/bun_update_no_optional.md index a93d68eb9f..ec72eba556 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_update_bun14/snapshots/bun_update_no_optional.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_update_bun14/snapshots/bun_update_no_optional.md @@ -66,11 +66,11 @@ bun update () }, }, "packages": { - "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], + "is-number": ["is-number@7.0.0", "http://127.0.0.1:/is-number/-/is-number-7.0.0.tgz", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], "isarray": ["isarray@1.0.0", "http://127.0.0.1:/isarray/-/isarray-1.0.0.tgz", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="], - "yocto-queue": ["yocto-queue@1.0.0", "", {}, "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g=="], + "yocto-queue": ["yocto-queue@1.0.0", "http://127.0.0.1:/yocto-queue/-/yocto-queue-1.0.0.tgz", {}, "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g=="], } } ``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_update_bun14/snapshots/bun_update_prod_includes_optional.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_update_bun14/snapshots/bun_update_prod_includes_optional.md index 4a205cb3b1..b72253e927 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_update_bun14/snapshots/bun_update_prod_includes_optional.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_update_bun14/snapshots/bun_update_prod_includes_optional.md @@ -66,9 +66,9 @@ bun update () }, }, "packages": { - "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], + "is-number": ["is-number@7.0.0", "http://127.0.0.1:/is-number/-/is-number-7.0.0.tgz", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], - "isarray": ["isarray@2.0.0", "", {}, "sha512-+7GtYSNjbLkjIGQy5ucmTjyY9ANbbW77DOT6nbQN4ZDo3CnST3HtjnGowbi+NF2pCPCba1rLO9qLlrFBsvDeew=="], + "isarray": ["isarray@2.0.0", "http://127.0.0.1:/isarray/-/isarray-2.0.0.tgz", {}, "sha512-+7GtYSNjbLkjIGQy5ucmTjyY9ANbbW77DOT6nbQN4ZDo3CnST3HtjnGowbi+NF2pCPCba1rLO9qLlrFBsvDeew=="], "yocto-queue": ["yocto-queue@0.1.0", "http://127.0.0.1:/yocto-queue/-/yocto-queue-0.1.0.tgz", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], } diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_update_bun_with_workspace/snapshots/bun_recursive_update_from_child.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_update_bun_with_workspace/snapshots/bun_recursive_update_from_child.md index 603b5d44a0..6f989e4dbd 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_update_bun_with_workspace/snapshots/bun_recursive_update_from_child.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_update_bun_with_workspace/snapshots/bun_recursive_update_from_child.md @@ -84,7 +84,7 @@ bun update () "packages": { "app": ["app@workspace:packages/app"], - "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], + "is-number": ["is-number@7.0.0", "http://127.0.0.1:/is-number/-/is-number-7.0.0.tgz", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], "utils": ["utils@workspace:packages/utils"], } diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_update_bun_with_workspace/snapshots/bun_recursive_update_respects_filter.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_update_bun_with_workspace/snapshots/bun_recursive_update_respects_filter.md index 24e73a496c..2b67fcc22b 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_update_bun_with_workspace/snapshots/bun_recursive_update_respects_filter.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_update_bun_with_workspace/snapshots/bun_recursive_update_respects_filter.md @@ -74,7 +74,7 @@ bun update () "utils": ["utils@workspace:packages/utils"], - "utils/is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], + "utils/is-number": ["is-number@7.0.0", "http://127.0.0.1:/is-number/-/is-number-7.0.0.tgz", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], } } ``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_update_npm10/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_update_npm10/snapshots.toml index 1524f124d0..03d4f6dc75 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_update_npm10/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_update_npm10/snapshots.toml @@ -1,7 +1,6 @@ [[case]] name = "command_update_npm10" vp = "global" -serial = true skip-platforms = ["windows"] steps = [ { argv = ["vp", "update", "testnpm2", "--", "--no-audit"], comment = "should update package within semver range" }, diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_update_npm10_with_workspace/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_update_npm10_with_workspace/snapshots.toml index cb59ea65db..4b1fd5d5e7 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_update_npm10_with_workspace/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_update_npm10_with_workspace/snapshots.toml @@ -1,7 +1,6 @@ [[case]] name = "command_update_npm10_with_workspace" vp = "global" -serial = true skip-platforms = ["windows"] steps = [ { argv = ["vp", "update", "testnpm2", "-w", "--", "--no-audit"], comment = "should update in workspace root" }, diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_update_yarn4_with_workspace/snapshots/berry_recursive_no_save.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_update_yarn4_with_workspace/snapshots/berry_recursive_no_save.md index 584cf37e57..2d6e3161aa 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_update_yarn4_with_workspace/snapshots/berry_recursive_no_save.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_update_yarn4_with_workspace/snapshots/berry_recursive_no_save.md @@ -26,8 +26,8 @@ omitting packages re-resolves scoped and unscoped dependencies while preserving ``` โžค YN0000: ยท Yarn โžค YN0000: โ”Œ Resolution step -โžค YN0085: โ”‚ + testnpm2@npm:1.0.1::__archiveUrl=https%3A%2F%2Fregistry.npmjs.org%2Ftestnpm2%2F-%2Ftestnpm2-1.0.1.tgz -โžค YN0085: โ”‚ - testnpm2@npm:1.0.0::__archiveUrl=https%3A%2F%2Fregistry.npmjs.org%2Ftestnpm2%2F-%2Ftestnpm2-1.0.0.tgz +โžค YN0085: โ”‚ + testnpm2@npm:1.0.1 +โžค YN0085: โ”‚ - testnpm2@npm:1.0.0 โžค YN0000: โ”” Completed โžค YN0000: โ”Œ Fetch step โžค YN0013: โ”‚ A package was added to the project (+ KiB). @@ -80,7 +80,7 @@ __metadata: "@test/scoped@npm:testnpm2@^1.0.0, testnpm2@npm:^1.0.0": version: 1.0.1 - resolution: "testnpm2@npm:1.0.1::__archiveUrl=https%3A%2F%2Fregistry.npmjs.org%2Ftestnpm2%2F-%2Ftestnpm2-1.0.1.tgz" + resolution: "testnpm2@npm:1.0.1" checksum: 10c0/b09252bf701b618c424bf1a92e0a54171660c8dbfb3d4dd3862e9d311957fa3ed2fd07708b74d08d0656a5ce8dfce748c0b32002613452f1beb9cd822f878f95 languageName: node linkType: hard @@ -97,7 +97,7 @@ __metadata: "is-number@npm:6.0.0": version: 6.0.0 - resolution: "is-number@npm:6.0.0::__archiveUrl=https%3A%2F%2Fregistry.npmjs.org%2Fis-number%2F-%2Fis-number-6.0.0.tgz" + resolution: "is-number@npm:6.0.0" checksum: 10c0/5da4c68401529675c575878d2760d66f18eaef4b014858577f6003daf66488d7fe4eae684b1e8574e3fa1bb447c6c6c56b8491d2b4b3239da2d32e5f6f218008 languageName: node linkType: hard diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_update_yarn4_with_workspace/snapshots/berry_recursive_preserves_ranges.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_update_yarn4_with_workspace/snapshots/berry_recursive_preserves_ranges.md index 9f2b1d6ace..f0f33f7cf6 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_update_yarn4_with_workspace/snapshots/berry_recursive_preserves_ranges.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_update_yarn4_with_workspace/snapshots/berry_recursive_preserves_ranges.md @@ -26,7 +26,7 @@ update the named package within its existing range across all workspaces ``` โžค YN0000: ยท Yarn โžค YN0000: โ”Œ Resolution step -โžค YN0085: โ”‚ + testnpm2@npm:1.0.1::__archiveUrl=https%3A%2F%2Fregistry.npmjs.org%2Ftestnpm2%2F-%2Ftestnpm2-1.0.1.tgz +โžค YN0085: โ”‚ + testnpm2@npm:1.0.1 โžค YN0000: โ”” Completed โžค YN0000: โ”Œ Fetch step โžค YN0013: โ”‚ A package was added to the project (+ KiB). @@ -79,7 +79,7 @@ __metadata: "@test/scoped@npm:testnpm2@^1.0.0": version: 1.0.0 - resolution: "testnpm2@npm:1.0.0::__archiveUrl=https%3A%2F%2Fregistry.npmjs.org%2Ftestnpm2%2F-%2Ftestnpm2-1.0.0.tgz" + resolution: "testnpm2@npm:1.0.0" checksum: 10c0/3f686c3b65ae74825e2f1b87a91f96e0888169313df77318174f5805e26764d9e963168e5321abbe8c1c9932a37d885d38137f18772360efaa7ea1c1614adc60 languageName: node linkType: hard @@ -96,7 +96,7 @@ __metadata: "is-number@npm:6.0.0": version: 6.0.0 - resolution: "is-number@npm:6.0.0::__archiveUrl=https%3A%2F%2Fregistry.npmjs.org%2Fis-number%2F-%2Fis-number-6.0.0.tgz" + resolution: "is-number@npm:6.0.0" checksum: 10c0/5da4c68401529675c575878d2760d66f18eaef4b014858577f6003daf66488d7fe4eae684b1e8574e3fa1bb447c6c6c56b8491d2b4b3239da2d32e5f6f218008 languageName: node linkType: hard @@ -113,7 +113,7 @@ __metadata: "testnpm2@npm:^1.0.0": version: 1.0.1 - resolution: "testnpm2@npm:1.0.1::__archiveUrl=https%3A%2F%2Fregistry.npmjs.org%2Ftestnpm2%2F-%2Ftestnpm2-1.0.1.tgz" + resolution: "testnpm2@npm:1.0.1" checksum: 10c0/b09252bf701b618c424bf1a92e0a54171660c8dbfb3d4dd3862e9d311957fa3ed2fd07708b74d08d0656a5ce8dfce748c0b32002613452f1beb9cd822f878f95 languageName: node linkType: hard diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_update_yarn4_with_workspace/snapshots/berry_recursive_update_all_from_child.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_update_yarn4_with_workspace/snapshots/berry_recursive_update_all_from_child.md index 81aa0fea3d..6ebd7a4ad5 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_update_yarn4_with_workspace/snapshots/berry_recursive_update_all_from_child.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_update_yarn4_with_workspace/snapshots/berry_recursive_update_all_from_child.md @@ -23,8 +23,8 @@ omitting package names still updates all workspace dependencies ``` โžค YN0000: ยท Yarn โžค YN0000: โ”Œ Resolution step -โžค YN0085: โ”‚ + is-number@npm:7.0.0::__archiveUrl=https%3A%2F%2Fregistry.npmjs.org%2Fis-number%2F-%2Fis-number-7.0.0.tgz -โžค YN0085: โ”‚ - is-number@npm:6.0.0::__archiveUrl=https%3A%2F%2Fregistry.npmjs.org%2Fis-number%2F-%2Fis-number-6.0.0.tgz +โžค YN0085: โ”‚ + is-number@npm:7.0.0 +โžค YN0085: โ”‚ - is-number@npm:6.0.0 โžค YN0000: โ”” Completed โžค YN0000: โ”Œ Fetch step โžค YN0013: โ”‚ A package was added to the project (+ KiB). diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_update_yarn4_with_workspace/snapshots/berry_recursive_update_from_child.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_update_yarn4_with_workspace/snapshots/berry_recursive_update_from_child.md index 8c58ada4f0..12e094f54a 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_update_yarn4_with_workspace/snapshots/berry_recursive_update_from_child.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_update_yarn4_with_workspace/snapshots/berry_recursive_update_from_child.md @@ -23,8 +23,8 @@ recursive update changes every workspace manifest, not just lockfile resolutions ``` โžค YN0000: ยท Yarn โžค YN0000: โ”Œ Resolution step -โžค YN0085: โ”‚ + is-number@npm:7.0.0::__archiveUrl=https%3A%2F%2Fregistry.npmjs.org%2Fis-number%2F-%2Fis-number-7.0.0.tgz -โžค YN0085: โ”‚ - is-number@npm:6.0.0::__archiveUrl=https%3A%2F%2Fregistry.npmjs.org%2Fis-number%2F-%2Fis-number-6.0.0.tgz +โžค YN0085: โ”‚ + is-number@npm:7.0.0 +โžค YN0085: โ”‚ - is-number@npm:6.0.0 โžค YN0000: โ”” Completed โžค YN0000: โ”Œ Fetch step โžค YN0013: โ”‚ A package was added to the project (+ KiB). diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_view_bun/snapshots/command_view_bun.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_view_bun/snapshots/command_view_bun.md index 114cefeb09..6d8ed2bc19 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_view_bun/snapshots/command_view_bun.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_view_bun/snapshots/command_view_bun.md @@ -31,7 +31,7 @@ should view package information testnpm2@1.0.1 | ISC | deps: 0 | versions: 2 dist - .tarball: https://registry.npmjs.org/testnpm2/-/testnpm2-1.0.1.tgz + .tarball: http://127.0.0.1:/testnpm2/-/testnpm2-1.0.1.tgz .shasum: 8c7b209a673c360e540ab2777242171fd30fdee9 .integrity: sha512-F4AQ+KmzhbOSlt7ae+X2O8IJktFZAcN6OK169TT4ny7M3e4Vje7NITZTOU31AtEk9L/Z8lrCrqinl/eY6WPuEw== diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_view_npm10/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_view_npm10/snapshots.toml index 288d77db84..4499092aa5 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_view_npm10/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_view_npm10/snapshots.toml @@ -3,7 +3,6 @@ name = "command_view_npm10" # Exceeded 20 seconds on Windows in CI run 35299447671. skip-platforms = ["windows"] vp = "global" -serial = true steps = [ { argv = ["vp", "pm", "view", "testnpm2", "dist.tarball"], comment = "should view testnpm2 package information", continue-on-failure = true }, { argv = ["vp", "pm", "info", "testnpm2", "dist.tarball"], comment = "should info alias to view", continue-on-failure = true }, diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_view_npm10/snapshots/command_view_npm10.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_view_npm10/snapshots/command_view_npm10.md index 39a83a6b51..73a0606bdd 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_view_npm10/snapshots/command_view_npm10.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_view_npm10/snapshots/command_view_npm10.md @@ -5,7 +5,7 @@ should view testnpm2 package information ``` -https://registry.npmjs.org/testnpm2/-/testnpm2-1.0.1.tgz +http://127.0.0.1:/testnpm2/-/testnpm2-1.0.1.tgz ``` ## `vp pm info testnpm2 dist.tarball` @@ -13,7 +13,7 @@ https://registry.npmjs.org/testnpm2/-/testnpm2-1.0.1.tgz should info alias to view ``` -https://registry.npmjs.org/testnpm2/-/testnpm2-1.0.1.tgz +http://127.0.0.1:/testnpm2/-/testnpm2-1.0.1.tgz ``` ## `vp pm show testnpm2 dist.tarball` @@ -21,5 +21,5 @@ https://registry.npmjs.org/testnpm2/-/testnpm2-1.0.1.tgz should show alias to view ``` -https://registry.npmjs.org/testnpm2/-/testnpm2-1.0.1.tgz +http://127.0.0.1:/testnpm2/-/testnpm2-1.0.1.tgz ``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_view_pnpm10/snapshots/command_view_pnpm10.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_view_pnpm10/snapshots/command_view_pnpm10.md index ad53a8c46c..0e353ba53b 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_view_pnpm10/snapshots/command_view_pnpm10.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_view_pnpm10/snapshots/command_view_pnpm10.md @@ -32,7 +32,7 @@ should view lodash package information (uses npm view) testnpm2@1.0.1 | ISC | deps: none | versions: 2 dist -.tarball: https://registry.npmjs.org/testnpm2/-/testnpm2-1.0.1.tgz +.tarball: http://127.0.0.1:/testnpm2/-/testnpm2-1.0.1.tgz .shasum: 8c7b209a673c360e540ab2777242171fd30fdee9 .integrity: sha512-F4AQ+KmzhbOSlt7ae+X2O8IJktFZAcN6OK169TT4ny7M3e4Vje7NITZTOU31AtEk9L/Z8lrCrqinl/eY6WPuEw== @@ -63,7 +63,7 @@ should view specific version of lodash (uses npm view) testnpm2@1.0.0 | ISC | deps: none | versions: 2 dist -.tarball: https://registry.npmjs.org/testnpm2/-/testnpm2-1.0.0.tgz +.tarball: http://127.0.0.1:/testnpm2/-/testnpm2-1.0.0.tgz .shasum: 3f9430987a1d4ff52c8c5162e99b5d8596efefa6 .integrity: sha512-8gdtqxKad+83Iog2v514VsHsSk/R+we9j5/9zX9tB+QC2ubvB06zJ08k0PSl5uzviXByEMiWm7EzSbBAh2GZ/w== @@ -82,7 +82,7 @@ published over a year ago by fengmk2 should view nested field (uses npm view) ``` -https://registry.npmjs.org/testnpm2/-/testnpm2-1.0.1.tgz +http://127.0.0.1:/testnpm2/-/testnpm2-1.0.1.tgz ``` ## `vp pm view testnpm2 dependencies` @@ -97,7 +97,7 @@ should view dependencies object (uses npm view) should view package.dist.tarball info in JSON format (uses npm view) ``` -"https://registry.npmjs.org/testnpm2/-/testnpm2-1.0.1.tgz" +"http://127.0.0.1:/testnpm2/-/testnpm2-1.0.1.tgz" ``` ## `vp pm view testnpm2 version --json` @@ -117,7 +117,7 @@ should support pass through arguments (uses npm view) testnpm2@1.0.1 | ISC | deps: none | versions: 2 dist -.tarball: https://registry.npmjs.org/testnpm2/-/testnpm2-1.0.1.tgz +.tarball: http://127.0.0.1:/testnpm2/-/testnpm2-1.0.1.tgz .shasum: 8c7b209a673c360e540ab2777242171fd30fdee9 .integrity: sha512-F4AQ+KmzhbOSlt7ae+X2O8IJktFZAcN6OK169TT4ny7M3e4Vje7NITZTOU31AtEk9L/Z8lrCrqinl/eY6WPuEw== diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_view_pnpm11/snapshots/command_view_pnpm11.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_view_pnpm11/snapshots/command_view_pnpm11.md index cf5382acae..238eb4addd 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_view_pnpm11/snapshots/command_view_pnpm11.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_view_pnpm11/snapshots/command_view_pnpm11.md @@ -31,7 +31,7 @@ should view lodash package information (uses npm view) testnpm2@1.0.1 | ISC | deps: none | versions: 2 dist -.tarball: https://registry.npmjs.org/testnpm2/-/testnpm2-1.0.1.tgz +.tarball: http://127.0.0.1:/testnpm2/-/testnpm2-1.0.1.tgz .shasum: 8c7b209a673c360e540ab2777242171fd30fdee9 .integrity: sha512-F4AQ+KmzhbOSlt7ae+X2O8IJktFZAcN6OK169TT4ny7M3e4Vje7NITZTOU31AtEk9L/Z8lrCrqinl/eY6WPuEw== @@ -59,7 +59,7 @@ should view specific version of lodash (uses npm view) testnpm2@1.0.0 | ISC | deps: none | versions: 2 dist -.tarball: https://registry.npmjs.org/testnpm2/-/testnpm2-1.0.0.tgz +.tarball: http://127.0.0.1:/testnpm2/-/testnpm2-1.0.0.tgz .shasum: 3f9430987a1d4ff52c8c5162e99b5d8596efefa6 .integrity: sha512-8gdtqxKad+83Iog2v514VsHsSk/R+we9j5/9zX9tB+QC2ubvB06zJ08k0PSl5uzviXByEMiWm7EzSbBAh2GZ/w== @@ -76,7 +76,7 @@ release-1: 1.0.1 should view nested field (uses npm view) ``` -https://registry.npmjs.org/testnpm2/-/testnpm2-1.0.1.tgz +http://127.0.0.1:/testnpm2/-/testnpm2-1.0.1.tgz ``` ## `vp pm view testnpm2 dependencies` @@ -91,7 +91,7 @@ should view dependencies object (uses npm view) should view package.dist.tarball info in JSON format (uses npm view) ``` -"https://registry.npmjs.org/testnpm2/-/testnpm2-1.0.1.tgz" +"http://127.0.0.1:/testnpm2/-/testnpm2-1.0.1.tgz" ``` ## `vp pm view testnpm2 version --json` @@ -110,7 +110,7 @@ should support pass through arguments (uses npm view) testnpm2@1.0.1 | ISC | deps: none | versions: 2 dist -.tarball: https://registry.npmjs.org/testnpm2/-/testnpm2-1.0.1.tgz +.tarball: http://127.0.0.1:/testnpm2/-/testnpm2-1.0.1.tgz .shasum: 8c7b209a673c360e540ab2777242171fd30fdee9 .integrity: sha512-F4AQ+KmzhbOSlt7ae+X2O8IJktFZAcN6OK169TT4ny7M3e4Vje7NITZTOU31AtEk9L/Z8lrCrqinl/eY6WPuEw== diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_view_yarn1/snapshots/command_view_yarn1.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_view_yarn1/snapshots/command_view_yarn1.md index 375000b3ca..c793254e01 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_view_yarn1/snapshots/command_view_yarn1.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_view_yarn1/snapshots/command_view_yarn1.md @@ -47,7 +47,7 @@ warning package.json: No license field }, dist: { shasum: '8c7b209a673c360e540ab2777242171fd30fdee9', - tarball: 'https://registry.npmjs.org/testnpm2/-/testnpm2-1.0.1.tgz', + tarball: 'http://127.0.0.1:/testnpm2/-/testnpm2-1.0.1.tgz', integrity: 'sha512-F4AQ+KmzhbOSlt7ae+X2O8IJktFZAcN6OK169TT4ny7M3e4Vje7NITZTOU31AtEk9L/Z8lrCrqinl/eY6WPuEw==', signatures: [ { diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_vpx_no_package_json/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_vpx_no_package_json/snapshots.toml index 0d5051379d..5c5cab0dab 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_vpx_no_package_json/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_vpx_no_package_json/snapshots.toml @@ -1,9 +1,8 @@ [[case]] name = "command_vpx_no_package_json" vp = "global" -serial = true local-registry = true skip-platforms = ["windows"] steps = [ - { argv = ["vpx", "-s", "cowsay", "hello"], comment = "should work without package.json", continue-on-failure = true }, + { argv = ["vpx", "-s", "vp-test-echo", "hello"], comment = "should work without package.json", continue-on-failure = true }, ] diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_vpx_no_package_json/snapshots/command_vpx_no_package_json.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_vpx_no_package_json/snapshots/command_vpx_no_package_json.md index b6dd1398e9..ce80118821 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_vpx_no_package_json/snapshots/command_vpx_no_package_json.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_vpx_no_package_json/snapshots/command_vpx_no_package_json.md @@ -1,16 +1,9 @@ # command_vpx_no_package_json -## `vpx -s cowsay hello` +## `vpx -s vp-test-echo hello` should work without package.json ``` - _______ -< hello > - ------- - \ ^__^ - \ (oo)\_______ - (__)\ )\/\ - ||----w | - || || +hello ``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_vpx_pnpm10/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_vpx_pnpm10/snapshots.toml index 0932d55387..7bb9ef43ac 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_vpx_pnpm10/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_vpx_pnpm10/snapshots.toml @@ -4,7 +4,7 @@ vp = "global" local-registry = true steps = [ { argv = ["vpx", "--help"], comment = "should show vpx help message", continue-on-failure = true }, - { argv = ["vpx", "-s", "cowsay", "hello"], comment = "should run cowsay via dlx fallback", continue-on-failure = true }, + { argv = ["vpx", "-s", "vp-test-echo", "hello"], comment = "should run vp-test-echo via dlx fallback", continue-on-failure = true }, ] [[case]] diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_vpx_pnpm10/snapshots/command_vpx_pnpm10.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_vpx_pnpm10/snapshots/command_vpx_pnpm10.md index 7951fde354..af4515f852 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_vpx_pnpm10/snapshots/command_vpx_pnpm10.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_vpx_pnpm10/snapshots/command_vpx_pnpm10.md @@ -26,17 +26,10 @@ Examples: vpx -p cowsay -c 'echo "hi" | cowsay' # Shell mode with package ``` -## `vpx -s cowsay hello` +## `vpx -s vp-test-echo hello` -should run cowsay via dlx fallback +should run vp-test-echo via dlx fallback ``` - _______ -< hello > - ------- - \ ^__^ - \ (oo)\_______ - (__)\ )\/\ - ||----w | - || || +hello ``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_vpx_pnpm11/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_vpx_pnpm11/snapshots.toml index 2b9c31e86e..a84ac8c6b4 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_vpx_pnpm11/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_vpx_pnpm11/snapshots.toml @@ -4,5 +4,5 @@ vp = "global" local-registry = true steps = [ { argv = ["vpx", "--help"], comment = "should show vpx help message", continue-on-failure = true }, - { argv = ["vpx", "-s", "cowsay", "hello"], comment = "should run cowsay via dlx fallback", continue-on-failure = true }, + { argv = ["vpx", "-s", "vp-test-echo", "hello"], comment = "should run vp-test-echo via dlx fallback", continue-on-failure = true }, ] diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_vpx_pnpm11/snapshots/command_vpx_pnpm11.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_vpx_pnpm11/snapshots/command_vpx_pnpm11.md index ba7435cf83..f050d86079 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_vpx_pnpm11/snapshots/command_vpx_pnpm11.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_vpx_pnpm11/snapshots/command_vpx_pnpm11.md @@ -26,17 +26,10 @@ Examples: vpx -p cowsay -c 'echo "hi" | cowsay' # Shell mode with package ``` -## `vpx -s cowsay hello` +## `vpx -s vp-test-echo hello` -should run cowsay via dlx fallback +should run vp-test-echo via dlx fallback ``` - _______ -< hello > - ------- - \ ^__^ - \ (oo)\_______ - (__)\ )\/\ - ||----w | - || || +hello ``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_why_npm10/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_why_npm10/snapshots.toml index 2f07eeb245..63f101c80d 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_why_npm10/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_why_npm10/snapshots.toml @@ -1,7 +1,6 @@ [[case]] name = "command_why_npm10" vp = "global" -serial = true skip-platforms = ["windows"] steps = [ { argv = ["vp", "install"], comment = "should install packages first", continue-on-failure = true }, diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_why_npm10_with_workspace/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_why_npm10_with_workspace/snapshots.toml index 313723848d..9f178eec0b 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_why_npm10_with_workspace/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_why_npm10_with_workspace/snapshots.toml @@ -1,7 +1,6 @@ [[case]] name = "command_why_npm10_with_workspace" vp = "global" -serial = true skip-platforms = ["windows"] steps = [ { argv = ["vpt", "json-edit", "package.json", "dependencies.test-vite-plus-top-package", "\"1.0.0\""], snapshot = false }, diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_why_pnpm10/snapshots/command_why_pnpm10.global.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_why_pnpm10/snapshots/command_why_pnpm10.global.md index df848afc51..98e7f1bb66 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_why_pnpm10/snapshots/command_why_pnpm10.global.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_why_pnpm10/snapshots/command_why_pnpm10.global.md @@ -122,7 +122,7 @@ should support json output "testnpm2": { "from": "testnpm2", "version": "1.0.1", - "resolved": "https://registry.npmjs.org/testnpm2/-/testnpm2-1.0.1.tgz", + "resolved": "http://127.0.0.1:/testnpm2/-/testnpm2-1.0.1.tgz", "path": "/node_modules/.pnpm/testnpm2@1.0.1/node_modules/testnpm2" } } diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_why_pnpm10/snapshots/command_why_pnpm10.local.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_why_pnpm10/snapshots/command_why_pnpm10.local.md index f51c47e652..640cac2508 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_why_pnpm10/snapshots/command_why_pnpm10.local.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_why_pnpm10/snapshots/command_why_pnpm10.local.md @@ -117,7 +117,7 @@ should support json output "testnpm2": { "from": "testnpm2", "version": "1.0.1", - "resolved": "https://registry.npmjs.org/testnpm2/-/testnpm2-1.0.1.tgz", + "resolved": "http://127.0.0.1:/testnpm2/-/testnpm2-1.0.1.tgz", "path": "/node_modules/.pnpm/testnpm2@1.0.1/node_modules/testnpm2" } } diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_why_pnpm10_with_workspace/snapshots/command_why_pnpm10_with_workspace.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_why_pnpm10_with_workspace/snapshots/command_why_pnpm10_with_workspace.md index d013771b15..5698beb5d3 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_why_pnpm10_with_workspace/snapshots/command_why_pnpm10_with_workspace.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/command_why_pnpm10_with_workspace/snapshots/command_why_pnpm10_with_workspace.md @@ -118,7 +118,7 @@ should support json output with filter "testnpm2": { "from": "testnpm2", "version": "1.0.0", - "resolved": "https://registry.npmjs.org/testnpm2/-/testnpm2-1.0.0.tgz", + "resolved": "http://127.0.0.1:/testnpm2/-/testnpm2-1.0.0.tgz", "path": "/node_modules/.pnpm/testnpm2@1.0.0/node_modules/testnpm2" }, "@vite-plus-test/utils": { @@ -129,7 +129,7 @@ should support json output with filter "testnpm2": { "from": "testnpm2", "version": "1.0.0", - "resolved": "https://registry.npmjs.org/testnpm2/-/testnpm2-1.0.0.tgz", + "resolved": "http://127.0.0.1:/testnpm2/-/testnpm2-1.0.0.tgz", "path": "/node_modules/.pnpm/testnpm2@1.0.0/node_modules/testnpm2" } } diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/local_registry/assert-overlay.mjs b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/local_registry/assert-overlay.mjs new file mode 100644 index 0000000000..8c1ad7de25 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/local_registry/assert-overlay.mjs @@ -0,0 +1,20 @@ +import assert from 'node:assert/strict'; +import { createHash } from 'node:crypto'; + +const registry = process.env.NPM_CONFIG_REGISTRY; +assert.ok(registry, 'Fixture overlay registry was not started'); +assert.equal(new URL(registry).hostname, '127.0.0.1'); + +const metadataResponse = await fetch(`${registry}/@vp-smoke/hello`); +assert.equal(metadataResponse.status, 200); +const metadata = await metadataResponse.json(); +assert.equal(metadata.name, '@vp-smoke/hello'); +assert.equal(metadata['dist-tags'].latest, '1.0.0'); + +const { dist } = metadata.versions['1.0.0']; +assert.equal(new URL(dist.tarball).origin, new URL(registry).origin); +const tarballResponse = await fetch(dist.tarball); +assert.equal(tarballResponse.status, 200); +const tarball = Buffer.from(await tarballResponse.arrayBuffer()); +assert.equal(`sha512-${createHash('sha512').update(tarball).digest('base64')}`, dist.integrity); +console.log('Fixture metadata and tarball integrity verified'); diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/local_registry/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/local_registry/snapshots.toml index fda3cdbdd9..6d68d3fcdb 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/local_registry/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/local_registry/snapshots.toml @@ -7,3 +7,10 @@ steps = [ { argv = ["npm", "install", "@vp-smoke/hello", "--no-save", "--no-audit", "--no-fund"], comment = "install a package served only by the local registry", snapshot = false, continue-on-failure = true }, { argv = ["vpt", "print-file", "node_modules/@vp-smoke/hello/package.json"], comment = "the local registry served the packument and its tarball (integrity verified by npm)", continue-on-failure = true }, ] + +[[case]] +name = "local_registry_overlay" +vp = "global" +# No local-registry flag: fixture overlays must also work in direct mode +# without opting into packed checkout packages. +steps = [["node", "assert-overlay.mjs"]] diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/local_registry/snapshots/local_registry_overlay.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/local_registry/snapshots/local_registry_overlay.md new file mode 100644 index 0000000000..6ccd7a22c6 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/local_registry/snapshots/local_registry_overlay.md @@ -0,0 +1,7 @@ +# local_registry_overlay + +## `node assert-overlay.mjs` + +``` +Fixture metadata and tarball integrity verified +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_standalone_npm/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_standalone_npm/snapshots.toml index 1d320bee9d..46895ef620 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_standalone_npm/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/migration_standalone_npm/snapshots.toml @@ -1,7 +1,6 @@ [[case]] name = "migration_standalone_npm" vp = "global" -serial = true # Exceeded 50 seconds on Linux in CI run 38677540536. skip-platforms = ["windows", "linux"] unset-env = ["CI", "VP_SKIP_INSTALL"] diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/degit-fixture/LICENSE b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/degit-fixture/LICENSE new file mode 100644 index 0000000000..8701f9fe69 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/degit-fixture/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright ยฉ 2025-PRESENT Kevin Deng (https://github.com/sxzz) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/degit-fixture/README.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/degit-fixture/README.md new file mode 100644 index 0000000000..a70b9b01ab --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/degit-fixture/README.md @@ -0,0 +1,18 @@ +# Fixed library template + +The `new_vite_monorepo` and `new_vite_monorepo_bun` cases check project creation +and catalog migration. Their registry overlays replace `degit` with a local +template copy. The wrapper checks the requested repository and arguments. +The real package managers and Vite+ commands still run. Other cases retain +the real `degit` package. + +`template/` contains the `vite-plus/` template from +[`sxzz/tsdown-templates` at `01a79e74`](https://github.com/sxzz/tsdown-templates/tree/01a79e7473ae2cb90af2b1312decc54c14b7cb91/vite-plus). +`LICENSE` preserves its MIT license. Test runs never fetch or refresh this copy. + +After an intentional fixture change, regenerate both case tarballs and their +integrity hashes from the repository root: + +```sh +node crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/degit-fixture/pack.mjs +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/degit-fixture/cli.mjs b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/degit-fixture/cli.mjs new file mode 100644 index 0000000000..7e615cc664 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/degit-fixture/cli.mjs @@ -0,0 +1,12 @@ +#!/usr/bin/env node +import assert from 'node:assert/strict'; +import { cpSync, existsSync } from 'node:fs'; + +// These cases check monorepo creation and catalog migration. Keep the +// downloaded template fixed while running the real package manager and CLI. +const [source, destination, ...extra] = process.argv.slice(2); +assert.equal(source, 'sxzz/tsdown-templates/vite-plus'); +assert.ok(destination, 'Missing template destination'); +assert.deepEqual(extra, [], 'Unexpected template arguments'); +assert.equal(existsSync(destination), false, 'Template destination already exists'); +cpSync(new URL('./template/', import.meta.url), destination, { recursive: true }); diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/degit-fixture/pack.mjs b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/degit-fixture/pack.mjs new file mode 100644 index 0000000000..dbf35aa07d --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/degit-fixture/pack.mjs @@ -0,0 +1,38 @@ +// Pack the checked-in template with Node.js builtins, without fetching data. +import { createHash } from 'node:crypto'; +import { mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from 'node:fs'; +import path from 'node:path'; + +import { createTarball } from '../../../registry/archive.mjs'; + +const source = import.meta.dirname; +const files = [ + 'package.json', + 'cli.mjs', + 'LICENSE', + ...readdirSync(path.join(source, 'template'), { recursive: true }) + .filter((filename) => statSync(path.join(source, 'template', filename)).isFile()) + .map((filename) => `template/${filename.split(path.sep).join('/')}`) + .toSorted(), +]; +const tarball = createTarball( + files.map((filename) => ({ + name: `package/${filename}`, + contents: readFileSync(path.join(source, filename)), + mode: filename === 'cli.mjs' ? 0o755 : 0o644, + })), +); +const pkg = JSON.parse(readFileSync(path.join(source, 'package.json'), 'utf8')); +pkg.dist = { + tarball: '{REGISTRY}/degit/-/degit-0.0.0.tgz', + integrity: `sha512-${createHash('sha512').update(tarball).digest('base64')}`, +}; +const manifest = { + degit: { name: 'degit', 'dist-tags': { latest: '0.0.0' }, versions: { '0.0.0': pkg } }, +}; +for (const name of ['new_vite_monorepo', 'new_vite_monorepo_bun']) { + const fixture = path.resolve(source, '../..', name); + mkdirSync(path.join(fixture, 'tarballs'), { recursive: true }); + writeFileSync(path.join(fixture, 'tarballs', 'degit-0.0.0.tgz'), tarball); + writeFileSync(path.join(fixture, 'mock-manifest.json'), JSON.stringify(manifest, null, 2) + '\n'); +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/degit-fixture/package.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/degit-fixture/package.json new file mode 100644 index 0000000000..528c4eb721 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/degit-fixture/package.json @@ -0,0 +1,7 @@ +{ + "name": "degit", + "version": "0.0.0", + "private": true, + "type": "module", + "bin": { "degit": "cli.mjs" } +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/degit-fixture/template/.gitignore b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/degit-fixture/template/.gitignore new file mode 100644 index 0000000000..7535211682 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/degit-fixture/template/.gitignore @@ -0,0 +1,4 @@ +node_modules +dist +*.log +.DS_Store diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/degit-fixture/template/README.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/degit-fixture/template/README.md new file mode 100644 index 0000000000..6ae949b2c7 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/degit-fixture/template/README.md @@ -0,0 +1,23 @@ +# vite-plus-starter + +A starter for creating a Vite Plus project. + +## Development + +- Install dependencies: + +```bash +vp install +``` + +- Run the unit tests: + +```bash +vp test +``` + +- Build the library: + +```bash +vp pack +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/degit-fixture/template/package.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/degit-fixture/template/package.json new file mode 100644 index 0000000000..66f631b07c --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/degit-fixture/template/package.json @@ -0,0 +1,39 @@ +{ + "name": "vite-plus-starter", + "version": "0.0.0", + "description": "A starter for creating a TypeScript package.", + "homepage": "https://github.com/author/library#readme", + "bugs": { + "url": "https://github.com/author/library/issues" + }, + "license": "MIT", + "author": "Author Name ", + "repository": { + "type": "git", + "url": "git+https://github.com/author/library.git" + }, + "files": [ + "dist" + ], + "type": "module", + "exports": { + ".": "./dist/index.mjs", + "./package.json": "./package.json" + }, + "publishConfig": { + "access": "public" + }, + "scripts": { + "build": "vp pack", + "dev": "vp pack --watch", + "test": "vp test", + "check": "vp check", + "prepublishOnly": "vp run build" + }, + "devDependencies": { + "@types/node": "^26.6.5", + "bumpp": "^12.3.0", + "typescript": "^7.0.2", + "vite-plus": "^1.2.0" + } +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/degit-fixture/template/src/index.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/degit-fixture/template/src/index.ts new file mode 100644 index 0000000000..02834d8802 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/degit-fixture/template/src/index.ts @@ -0,0 +1,3 @@ +export function fn() { + return "Hello, Vite+!"; +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/degit-fixture/template/tests/index.test.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/degit-fixture/template/tests/index.test.ts new file mode 100644 index 0000000000..1f397a7e77 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/degit-fixture/template/tests/index.test.ts @@ -0,0 +1,6 @@ +import { expect, test } from "vite-plus/test"; +import { fn } from "../src/index.ts"; + +test("fn", () => { + expect(fn()).toBe("Hello, Vite+!"); +}); diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/degit-fixture/template/tsconfig.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/degit-fixture/template/tsconfig.json new file mode 100644 index 0000000000..ff4adab564 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/degit-fixture/template/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "esnext", + "lib": ["es2023"], + "moduleDetection": "force", + "module": "nodenext", + "moduleResolution": "nodenext", + "resolveJsonModule": true, + "types": ["node"], + "strict": true, + "noUnusedLocals": true, + "declaration": true, + "noEmit": true, + "allowImportingTsExtensions": true, + "esModuleInterop": true, + "isolatedModules": true, + "verbatimModuleSyntax": true, + "skipLibCheck": true + } +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/degit-fixture/template/vite.config.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/degit-fixture/template/vite.config.ts new file mode 100644 index 0000000000..bffbbc1e6a --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/degit-fixture/template/vite.config.ts @@ -0,0 +1,17 @@ +import { defineConfig } from "vite-plus"; + +export default defineConfig({ + pack: { + dts: { + tsgo: true, + }, + exports: true, + }, + lint: { + options: { + typeAware: true, + typeCheck: true, + }, + }, + fmt: {}, +}); diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/mock-manifest.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/mock-manifest.json new file mode 100644 index 0000000000..46a2869c02 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/mock-manifest.json @@ -0,0 +1,23 @@ +{ + "degit": { + "name": "degit", + "dist-tags": { + "latest": "0.0.0" + }, + "versions": { + "0.0.0": { + "name": "degit", + "version": "0.0.0", + "private": true, + "type": "module", + "bin": { + "degit": "cli.mjs" + }, + "dist": { + "tarball": "{REGISTRY}/degit/-/degit-0.0.0.tgz", + "integrity": "sha512-MPyTtnLw02R8cOuRuZqMHM3iDfsCgvqmCxfZqR1gb9x9cL1Dsl5XJggjsrNuJCQrE7ioJYjafuUG2DzJ8k5iCw==" + } + } + } + } +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/snapshots.toml index 84a08f56da..7ddb799a7c 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/snapshots.toml @@ -2,6 +2,7 @@ name = "new_vite_monorepo" vp = "global" skip-platforms = ["windows"] +# mock-manifest.json fixes the remote library input; see degit-fixture/README.md. steps = [ { argv = ["vp", "create", "vite:monorepo", "--no-interactive", "--git", "--editor", "vscode"], comment = "create monorepo with default values", snapshot = false, continue-on-failure = true }, { argv = ["vpt", "list-dir", "vite-plus-monorepo"], comment = "check files created", continue-on-failure = true }, diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/tarballs/degit-0.0.0.tgz b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/tarballs/degit-0.0.0.tgz new file mode 100644 index 0000000000..ffa180cc3c Binary files /dev/null and b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo/tarballs/degit-0.0.0.tgz differ diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo_bun/mock-manifest.json b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo_bun/mock-manifest.json new file mode 100644 index 0000000000..46a2869c02 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo_bun/mock-manifest.json @@ -0,0 +1,23 @@ +{ + "degit": { + "name": "degit", + "dist-tags": { + "latest": "0.0.0" + }, + "versions": { + "0.0.0": { + "name": "degit", + "version": "0.0.0", + "private": true, + "type": "module", + "bin": { + "degit": "cli.mjs" + }, + "dist": { + "tarball": "{REGISTRY}/degit/-/degit-0.0.0.tgz", + "integrity": "sha512-MPyTtnLw02R8cOuRuZqMHM3iDfsCgvqmCxfZqR1gb9x9cL1Dsl5XJggjsrNuJCQrE7ioJYjafuUG2DzJ8k5iCw==" + } + } + } + } +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo_bun/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo_bun/snapshots.toml index 41c0d7ab8e..4c83ca6a68 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo_bun/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo_bun/snapshots.toml @@ -2,6 +2,7 @@ name = "new_vite_monorepo_bun" vp = "global" skip-platforms = ["windows"] +# Reuse the fixed library input documented in ../new_vite_monorepo/degit-fixture/README.md. steps = [ { argv = ["vp", "create", "vite:monorepo", "--no-interactive", "--package-manager", "bun", "--git"], comment = "create monorepo with bun", snapshot = false, continue-on-failure = true }, { argv = ["vpt", "list-dir", "vite-plus-monorepo"], comment = "check files created", continue-on-failure = true }, diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo_bun/tarballs/degit-0.0.0.tgz b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo_bun/tarballs/degit-0.0.0.tgz new file mode 100644 index 0000000000..ffa180cc3c Binary files /dev/null and b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/new_vite_monorepo_bun/tarballs/degit-0.0.0.tgz differ diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/npm_install_with_options/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/npm_install_with_options/snapshots.toml index 35f59261ca..29ba0391a2 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/npm_install_with_options/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/npm_install_with_options/snapshots.toml @@ -2,7 +2,6 @@ name = "npm_install_with_options" vp = "local" skip-platforms = ["windows"] -serial = true steps = [ { argv = ["vp", "install", "--help"], comment = "print help message", continue-on-failure = true }, { argv = ["vp", "run", "install"], comment = "https://docs.npmjs.com/cli/v10/commands/npm-install", continue-on-failure = true }, diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/pm_audit/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/pm_audit/snapshots.toml index 5220be523b..57b2923fd9 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/pm_audit/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/pm_audit/snapshots.toml @@ -3,7 +3,6 @@ name = "pm_audit_bun" # Exceeded 20 seconds on Windows in CI run 35299447671. skip-platforms = ["windows"] vp = "global" -serial = true cwd = "bun" steps = [ { argv = ["vp", "install", "--", "--silent"], comment = "should install packages first", continue-on-failure = true }, diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/root_library_build/vite.config.ts b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/root_library_build/vite.config.ts index 9b676a7a4d..120a1d60fd 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/root_library_build/vite.config.ts +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/root_library_build/vite.config.ts @@ -17,9 +17,13 @@ export default { { name: 'dev-ready-milestone', configureServer(server) { - server.httpServer?.once('listening', () => { + // The listening event precedes CLI shortcut registration. Wait until + // the input handler is attached before the runner sends `q`. + const bindCLIShortcuts = server.bindCLIShortcuts.bind(server); + server.bindCLIShortcuts = (options) => { + bindCLIShortcuts(options); emitMilestone('dev-server:ready'); - }); + }; }, }, ], diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/shim_pnpm12_native/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/shim_pnpm12_native/snapshots.toml index d8406cf434..d579bbad82 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/shim_pnpm12_native/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/shim_pnpm12_native/snapshots.toml @@ -10,5 +10,5 @@ steps = [ { argv = ["vpt", "stat-file", "$VP_HOME/packages/pnpm.json", "--assert", "missing"], comment = "The redundant package should not be installed", snapshot = false, continue-on-failure = true }, { argv = ["vp", "env", "exec", "node", "--version"], comment = "Ensure Node.js is installed first", snapshot = false, continue-on-failure = true }, { argv = ["pnpm", "--version"], comment = "pnpm shim downloads the native binary and resolves the pinned packageManager version (12.0.0-beta.0)", continue-on-failure = true }, - { argv = ["pnpx", "--silent", "cowsay", "hello"], comment = "pnpx shim injects dlx so the native binary runs the package", continue-on-failure = true }, + { argv = ["pnpx", "--silent", "vp-test-echo", "hello"], comment = "pnpx shim injects dlx so the native binary runs the package", continue-on-failure = true }, ] diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/shim_pnpm12_native/snapshots/shim_pnpm12_native.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/shim_pnpm12_native/snapshots/shim_pnpm12_native.md index a81701cc4b..14e3ed4073 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/shim_pnpm12_native/snapshots/shim_pnpm12_native.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/shim_pnpm12_native/snapshots/shim_pnpm12_native.md @@ -30,17 +30,10 @@ pnpm shim downloads the native binary and resolves the pinned packageManager ver 12.0.0-beta.0 ``` -## `pnpx --silent cowsay hello` +## `pnpx --silent vp-test-echo hello` pnpx shim injects dlx so the native binary runs the package ``` - _______ -< hello > - ------- - \ ^__^ - \ (oo)\_______ - (__)\ )\/\ - ||----w | - || || +hello ``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/shim_pnpm_uses_project_node_version/assert-registry-cache.mjs b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/shim_pnpm_uses_project_node_version/assert-registry-cache.mjs new file mode 100644 index 0000000000..30dba59aab --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/shim_pnpm_uses_project_node_version/assert-registry-cache.mjs @@ -0,0 +1,15 @@ +import { execFileSync } from 'node:child_process'; +import { readFileSync } from 'node:fs'; + +const manager = process.argv[2]; +const registry = process.env.npm_config_registry ?? process.env.NPM_CONFIG_REGISTRY; +const count = async () => (await (await fetch(`${registry}/-/vp-test/requests`)).json()).requests; +const before = await count(); +const actual = execFileSync(manager, ['--version'], { encoding: 'utf8' }); +const after = await count(); +if (before !== after) throw new Error(`${manager} contacted the registry despite its fresh cache`); +if (manager === 'bun') { + const expected = readFileSync(`${process.env.VP_HOME}/bun-fallback-version`, 'utf8'); + if (actual !== expected) throw new Error(`expected ${expected.trim()}, got ${actual.trim()}`); + console.log('default bun@latest matches the unconfigured fallback'); +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/shim_pnpm_uses_project_node_version/snapshots.toml b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/shim_pnpm_uses_project_node_version/snapshots.toml index 81618412fd..e46d3ae973 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/shim_pnpm_uses_project_node_version/snapshots.toml +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/shim_pnpm_uses_project_node_version/snapshots.toml @@ -2,11 +2,12 @@ name = "shim_pnpm_uses_project_node_version" vp = "global" local-registry = true +isolated-registry = true # Exceeded 50 seconds on Linux in CI run 38675211827. skip-platforms = ["windows", "linux"] steps = [ { argv = ["pnpm", "--version"], comment = "The unpinned pnpm shim resolves the latest version", snapshot = false }, - { argv = ["pnpm", "--version"], envs = [["NPM_CONFIG_REGISTRY", "http://127.0.0.1:9"]], comment = "The unpinned shim reuses its fresh latest-version cache without registry access", snapshot = false }, + { argv = ["node", "assert-registry-cache.mjs", "pnpm"], comment = "The unpinned shim reuses its fresh latest-version cache without registry access", snapshot = false }, { argv = ["node", "-e", "const {execFileSync}=require('node:child_process');const {delimiter}=require('node:path');const env={...process.env,PATH:[process.env.VP_HOME+'/bin','/usr/bin','/bin'].join(delimiter)};const version=execFileSync('pnpm',['--version'],{encoding:'utf8',env}).trim();if(!version)process.exit(1);console.log('node child reached pnpm shim')"], comment = "A Node process launched through its shim can invoke the pnpm shim" }, { argv = ["node", "-e", "const {execFileSync}=require('node:child_process');execFileSync('pnpm',['--silent','exec','node','-e','if(process.version!==process.env.EXPECTED_NODE_VERSION)process.exit(1)'],{stdio:'inherit',env:{...process.env,EXPECTED_NODE_VERSION:process.version}});console.log('pnpm child uses project Node version')"], comment = "pnpm uses the project Node version" }, { argv = ["vp", "env", "exec", "--node", "22.13", "pnpm", "exec", "node", "-e", "if(!process.version.startsWith('v22.13.'))process.exit(1);console.log('explicit Node reaches pnpm child')"], comment = "Explicit env exec version overrides the project version through the pnpm shim" }, @@ -18,11 +19,12 @@ steps = [ name = "shim_bun_default_latest_matches_fallback" vp = "global" local-registry = true +isolated-registry = true skip-platforms = ["windows"] steps = [ { argv = ["node", "-e", "const {execFileSync}=require('node:child_process');const {writeFileSync}=require('node:fs');writeFileSync(process.env.VP_HOME+'/bun-fallback-version',execFileSync('bun',['--version'],{encoding:'utf8'}))"], snapshot = false }, { argv = ["vp", "env", "default", "bun@latest"], snapshot = false }, - { argv = ["node", "-e", "const {execFileSync}=require('node:child_process');const {readFileSync}=require('node:fs');const expected=readFileSync(process.env.VP_HOME+'/bun-fallback-version','utf8');const actual=execFileSync('bun',['--version'],{encoding:'utf8'});if(actual!==expected)throw new Error(`expected ${expected.trim()}, got ${actual.trim()}`);console.log('default bun@latest matches the unconfigured fallback')"], envs = [["NPM_CONFIG_REGISTRY", "http://127.0.0.1:9"]], comment = "the explicit floating default behaves like the unconfigured Bun fallback" }, + { argv = ["node", "assert-registry-cache.mjs", "bun"], comment = "the explicit floating default behaves like the unconfigured Bun fallback" }, ] [[case]] diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/shim_pnpm_uses_project_node_version/snapshots/shim_bun_default_latest_matches_fallback.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/shim_pnpm_uses_project_node_version/snapshots/shim_bun_default_latest_matches_fallback.md index a25c2cbdfa..8205655511 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/shim_pnpm_uses_project_node_version/snapshots/shim_bun_default_latest_matches_fallback.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/shim_pnpm_uses_project_node_version/snapshots/shim_bun_default_latest_matches_fallback.md @@ -6,7 +6,7 @@ ## `vp env default bun@latest` -## `NPM_CONFIG_REGISTRY=http://127.0.0.1:9 node -e 'const {execFileSync}=require('\''node:child_process'\'');const {readFileSync}=require('\''node:fs'\'');const expected=readFileSync(process.env.VP_HOME+'\''/bun-fallback-version'\'','\''utf8'\'');const actual=execFileSync('\''bun'\'',['\''--version'\''],{encoding:'\''utf8'\''});if(actual'\!'==expected)throw new Error(`expected ${expected.trim()}, got ${actual.trim()}`);console.log('\''default bun@latest matches the unconfigured fallback'\'')'` +## `node assert-registry-cache.mjs bun` the explicit floating default behaves like the unconfigured Bun fallback diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/shim_pnpm_uses_project_node_version/snapshots/shim_pnpm_uses_project_node_version.md b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/shim_pnpm_uses_project_node_version/snapshots/shim_pnpm_uses_project_node_version.md index 818f1894a0..fa878aa991 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/shim_pnpm_uses_project_node_version/snapshots/shim_pnpm_uses_project_node_version.md +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/shim_pnpm_uses_project_node_version/snapshots/shim_pnpm_uses_project_node_version.md @@ -5,7 +5,7 @@ The unpinned pnpm shim resolves the latest version -## `NPM_CONFIG_REGISTRY=http://127.0.0.1:9 pnpm --version` +## `node assert-registry-cache.mjs pnpm` The unpinned shim reuses its fresh latest-version cache without registry access diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/main.rs b/crates/vp_cli_snapshots/tests/cli_snapshots/main.rs index ecdda27e2d..ddbcf8c39b 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/main.rs +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/main.rs @@ -386,6 +386,9 @@ struct Case { /// Serve the packed checkout packages through the local npm registry. #[serde(default, rename = "local-registry")] local_registry: bool, + /// Keep registry request counters private for tests of metadata caching. + #[serde(default, rename = "isolated-registry")] + isolated_registry: bool, /// Seed the case's `VP_HOME` with an already-provisioned managed JS /// runtime (see `flavor::js_runtime_seed_dir`). Default true; runtime /// provisioning tests set false to start from a genuinely empty home. @@ -987,6 +990,16 @@ fn expand_env_value(value: &str, cwd: &Path, case_env: &BTreeMap Option<&'static str> { + match key { + "npm_config_registry" => Some("NPM_CONFIG_REGISTRY"), + "NPM_CONFIG_REGISTRY" => Some("npm_config_registry"), + "pnpm_config_registry" => Some("PNPM_CONFIG_REGISTRY"), + "PNPM_CONFIG_REGISTRY" => Some("pnpm_config_registry"), + _ => None, + } +} + fn step_context<'a>( step: &Step, case_env: &'a BTreeMap, @@ -1008,6 +1021,13 @@ fn step_context<'a>( } else { let mut env = case_env.clone(); for (k, v) in &step.envs { + // Do not let a runner-provided lowercase alias mask an explicit + // uppercase registry override (or vice versa). + if let Some(alias) = registry_env_alias(k) + && !step.envs.iter().any(|(key, _)| key == alias) + { + env.remove(alias); + } // References resolve against the case env, never a sibling step // env, so the entries stay order-independent. env.insert(k.clone(), expand_env_value(v, &cwd, case_env)); @@ -1049,6 +1069,11 @@ struct RegistryHandle { cache_dirs: Vec, } +struct SharedRegistry { + env: BTreeMap, + _handle: Option, +} + impl Drop for RegistryHandle { fn drop(&mut self) { // Graceful first: SIGTERM the server so its handler removes the @@ -1100,7 +1125,9 @@ impl Drop for RegistryHandle { fn start_local_registry( node: &Path, stage: &Path, - pack_dir: &Path, + pack_dir: Option<&Path>, + cache_dir: &Path, + request_log: Option<&Path>, case_env: &BTreeMap, ) -> Result<(BTreeMap, RegistryHandle), String> { use std::io::{BufRead as _, Read as _}; @@ -1114,10 +1141,30 @@ fn start_local_registry( .current_dir(stage) .env_clear() .envs(case_env) - .env("SNAP_LOCAL_VP_PACKAGES_DIR", pack_dir) + // Fixture preloads are inputs to the CLI under test, not this helper. + .env_remove("NODE_OPTIONS") + .env_remove("NODE_PATH") + .env_remove("SNAP_LOCAL_VP_PACKAGES_DIR") .stdin(std::process::Stdio::null()) .stdout(std::process::Stdio::piped()) .stderr(std::process::Stdio::piped()); + if let Some(pack_dir) = pack_dir { + cmd.env("SNAP_LOCAL_VP_PACKAGES_DIR", pack_dir); + } + cmd.arg("--snapshot-fixtures").arg(flavor::manifest_dir().join("tests/cli_snapshots/registry")); + if std::env::var("VP_SNAP_REGISTRY_MODE").as_deref() != Ok("direct") { + cmd.arg("--snapshot-cache").arg(cache_dir); + if std::env::var("VP_SNAP_REGISTRY_MODE").as_deref() == Ok("live") { + cmd.arg("--live"); + } + if let Some(log) = request_log { + cmd.arg("--request-log").arg(log); + } + } else { + // Direct routing still needs locally authored packages, which have no + // public-registry counterpart. Do not enable published recordings. + cmd.arg("--live"); + } // The server proxies to the developer's configured upstream (an internal // npm mirror in `~/.npmrc`); it must read the REAL user home to find it, // not the isolated case HOME from `baseline_env`, or mirror-only @@ -1213,6 +1260,7 @@ fn run_case( runtime: &FlavorRuntime, snapshot_name: &str, local_registry_pack: Option<&Path>, + shared_registry_env: Option<&BTreeMap>, report: &report::Report, ) -> Result<(), String> { let snapshots = vt_snapshot_test::Snapshots::new(fixture_path.join("snapshots")); @@ -1253,30 +1301,70 @@ fn run_case( let case_path: OsString = case_env.get("PATH").cloned().unwrap_or_else(|| case_install.path_env.clone()); - // local-registry cases: stand up a per-case npm registry (serving the - // packed checkout packages plus this fixture's mock-manifest/tarballs) and - // fold its registry env into every step. Held to the end of the case so - // its teardown removes the throwaway package-manager caches. Registry env - // never sets PATH, so folding it in after `case_path` is derived is safe. + // Cases without overlays share the HTTP server, but keep package-manager + // caches isolated. Fixture overlays retain a per-case server in every + // mode, including direct mode. Neither registry environment changes PATH. let registry_phase = report.phase("registry-startup"); - let registry = if case.local_registry { - let pack_dir = local_registry_pack - .ok_or("internal error: local-registry case reached run_case without a packed dir")?; + let mut shared_bun_cache = None; + let (registry_env, registry) = if let Some(env) = shared_registry_env { + let mut env = env.clone(); + // Bun cache/temp renames cannot cross filesystems. Windows CI places + // the run tree on ReFS, so retain the standalone helper's NTFS choice. + // Keep Unix temp paths outside the case tree: long fixture names can + // exceed the socket path limit used by the task runner and Chromium. + let cache_root = if cfg!(windows) && std::env::var_os("CI").is_some() { + std::env::var_os("USERPROFILE").map_or_else(std::env::temp_dir, PathBuf::from) + } else { + std::env::temp_dir() + }; + let cache = tempfile::Builder::new() + .prefix("vp-local-registry-bun-") + .tempdir_in(cache_root) + .map_err(|error| format!("failed to create Bun cache: {error}"))?; + let temporary = cache.path().join(".tmp"); + std::fs::create_dir(&temporary).map_err(|error| error.to_string())?; + env.insert("YARN_GLOBAL_FOLDER".into(), case_root.join("yarn-cache").into_os_string()); + env.insert("BUN_INSTALL_CACHE_DIR".into(), cache.path().as_os_str().into()); + env.insert("BUN_TMPDIR".into(), temporary.clone().into_os_string()); + env.insert("TMPDIR".into(), temporary.into_os_string()); + shared_bun_cache = Some(cache); + (env, None) + } else if case.local_registry + || stage.join("mock-manifest.json").exists() + || stage.join("tarballs").exists() + || std::env::var("VP_SNAP_REGISTRY_MODE").as_deref() != Ok("direct") + { + let cache_dir = std::env::var_os("VP_SNAP_REGISTRY_CACHE_DIR") + .map_or_else(|| tmpdir.join("registry-cache"), PathBuf::from); + let request_log = report.artifact_path("registry.jsonl")?; // Prefer the seed runtime's real node binary over the case's node // shim: the shim resolves the project's pinned runtime from its cwd, // and a fixture pinning an older Node (a `.node-version` under test) // must not pick the runtime executing this TypeScript helper. let registry_node = flavor::seed_runtime_node() .map_or_else(|| case_install.resolve_program("node", &case_path, &stage), Ok)?; - let (registry_env, handle) = - start_local_registry(®istry_node, &stage, pack_dir, &case_env)?; - for (key, value) in registry_env { - case_env.insert(key, value); - } - Some(handle) + let (registry_env, handle) = start_local_registry( + ®istry_node, + &stage, + local_registry_pack, + &cache_dir, + request_log.as_deref(), + &case_env, + )?; + (registry_env, Some(handle)) } else { - None + (BTreeMap::new(), None) }; + for (key, value) in registry_env { + // Explicit fixture settings (including intentionally unreachable + // registries) still win over the runner's defaults. + let overridden = |key: &str| { + case.env.contains_key(key) || case.unset_env.iter().any(|unset| unset == key) + }; + if !overridden(&key) && !registry_env_alias(&key).is_some_and(overridden) { + case_env.insert(key, value); + } + } drop(registry_phase); @@ -1571,6 +1659,7 @@ fn run_case( } drop(registry); + drop(shared_bun_cache); drop(cleanup_phase); // Deferred so the cleanup above always runs, even for hung steps. @@ -1747,6 +1836,9 @@ fn main() { /// VP_SNAP_PACKAGES_DIR also shares them across nextest processes; /// this cell avoids revalidating the directory for each local trial. local_registry_pack: std::sync::OnceLock, String>>, + shared_registry: std::sync::OnceLock>, + packed_registry: std::sync::OnceLock>, + registry_log: Option, } impl LazyRuntimes { fn get(&self, flavor: Flavor) -> &Result { @@ -1757,6 +1849,54 @@ fn main() { cell.get_or_init(|| flavor::provision(flavor, &self.run_root)) } + fn shared_registry( + &self, + pack_dir: Option<&Path>, + ) -> Result<&BTreeMap, String> { + let (cell, name) = if pack_dir.is_some() { + (&self.packed_registry, "packed-registry") + } else { + (&self.shared_registry, "shared-registry") + }; + cell.get_or_init(|| { + let external_env = if pack_dir.is_some() { + "VP_SNAP_PACKED_REGISTRY_ENV" + } else { + "VP_SNAP_SHARED_REGISTRY_ENV" + }; + if let Ok(value) = std::env::var(external_env) { + let env: BTreeMap = serde_json::from_str(&value) + .map_err(|error| format!("invalid shared registry environment: {error}"))?; + return Ok(SharedRegistry { + env: env.into_iter().map(|(key, value)| (key, value.into())).collect(), + _handle: None, + }); + } + let stage = self.run_root.join(name); + std::fs::create_dir_all(&stage).map_err(|error| error.to_string())?; + let node = flavor::seed_runtime_node() + .map_or_else(|| which::which("node").map_err(|error| error.to_string()), Ok)?; + let cache_dir = std::env::var_os("VP_SNAP_REGISTRY_CACHE_DIR") + .map_or_else(|| self.run_root.join("registry-cache"), PathBuf::from); + let helper_env = ["TEMP", "TMP", "TMPDIR", "SYSTEMROOT", "WINDIR"] + .into_iter() + .filter_map(|key| std::env::var_os(key).map(|value| (key.to_string(), value))) + .collect(); + let (env, handle) = start_local_registry( + &node, + &stage, + pack_dir, + &cache_dir, + self.registry_log.as_deref(), + &helper_env, + )?; + Ok(SharedRegistry { env, _handle: Some(handle) }) + }) + .as_ref() + .map(|registry| ®istry.env) + .map_err(Clone::clone) + } + /// Packs the checkout packages once and returns the shared dir. Reuses /// `local-npm-registry.ts --pack-to` so the pack logic lives in one /// place (the same helper the tool and ecosystem-ci use). @@ -1800,6 +1940,9 @@ fn main() { global: std::sync::OnceLock::new(), local: std::sync::OnceLock::new(), local_registry_pack: std::sync::OnceLock::new(), + shared_registry: std::sync::OnceLock::new(), + packed_registry: std::sync::OnceLock::new(), + registry_log: artifacts.as_ref().map(|dir| dir.join("registry.jsonl")), }); // Per-case wall times, printed slowest-first after the run. libtest-mimic @@ -1884,6 +2027,18 @@ fn main() { "{fixture_name}_case_{case_index}_{}", flavor.as_str() )); + let shared_registry_phase = report.phase("shared-registry-startup"); + let shared_registry_env = if !case.isolated_registry + && !fixture_path.join("mock-manifest.json").exists() + && !fixture_path.join("tarballs").exists() + && !fixture_path.join(".npmrc").exists() + && std::env::var("VP_SNAP_REGISTRY_MODE").as_deref() != Ok("direct") + { + Some(runtimes.shared_registry(local_registry_pack.as_deref())?) + } else { + None + }; + drop(shared_registry_phase); let result = run_case( &tmp_dir_path, &case_root, @@ -1893,6 +2048,7 @@ fn main() { runtime, &snapshot_name, local_registry_pack.as_deref(), + shared_registry_env, &report, ); // Reclaim each workspace while other workers can @@ -1959,6 +2115,7 @@ fn main() { // exit() never returns, so the staged run tree must be dropped first or // every run would leave its full tempdir behind. let cleanup_phase = run_report.phase("run-cleanup"); + drop(runtimes); drop(tmp_dir); drop(cleanup_phase); run_report diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/registry/README.md b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/README.md new file mode 100644 index 0000000000..157bceb89f --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/README.md @@ -0,0 +1,62 @@ +# Registry fixtures + +`manifest.json` records selected published npm versions for small test packages +used by the package-manager snapshots. Their tarballs are +the original published bytes, with their original integrity hashes. Only +`dist.tarball` URLs change to `{REGISTRY}` placeholders when recording data. +Metadata omits unused versions, their timestamps, and tags that refer to them. +No package-manager or toolchain implementation is mocked. + +`packages/` contains two dependency-free CLI packages authored for the tests. +`vp-test-echo` prints arguments or standard input; `vp-test-upper` converts +standard input to uppercase. They replace unrelated public CLI dependencies in +the `dlx`, `vpx`, global binary lookup, and native pnpm shim cases. Package +managers still download, install, and execute real tarballs. The multiple-package +case runs both binaries through a pipe. +The echo package also exposes an alias to preserve coverage for packages with +multiple binaries. CommonJS entry points work with Yarn PnP without an ESM loader. + +Regenerate `local/tarballs/` and `local/manifest.json` after editing their source: + +```sh +node crates/vp_cli_snapshots/tests/cli_snapshots/registry/pack.mjs +``` + +Packing uses Node.js builtins with fixed timestamps and file modes. It needs no +network access and does not run package scripts. The npm refresh command below +does not change these local packages. +The `live` and `direct` comparison modes also serve authored packages locally, +because they have no public-registry counterpart. + +The recorded `is-number` and `yocto-queue` packages supply the unchanged inputs +for update, list, remove, and patch cases. These tests exercise real package +contents and version ranges, so the fixtures keep the original tarball bytes. +Only the versions needed by these cases and their dependencies are recorded: + +| Package | Versions | Coverage | +| ------------- | ------------------------- | ----------------------------------------------------------------------------------- | +| `is-number` | `6.0.0`, `7.0.0` | Initial installation, update target, and patching | +| `yocto-queue` | `0.1.0`, `1.0.0`, `1.2.2` | Initial installation, in-range update, and a newer version outside the update range | + +The registry serves these fixtures before forwarding requests upstream. +A case's `mock-manifest.json` and `tarballs/` take precedence over shared data. +Locally packed checkout packages keep precedence over both. + +To refresh these packages deliberately, from the repository root: + +```sh +node packages/tools/src/record-snapshot-registry.ts +``` + +The command fetches metadata and tarballs from `https://registry.npmjs.org`, +checks each tarball's SHA-512 integrity, and updates the recorded files. +`selectedVersions` in the recorder limits these two packages to the versions +above. The command removes tarballs for versions dropped from the manifest. +If the published `latest` tag changes, update the selection explicitly before +refreshing so it still covers the required ranges. +Review the changes and re-record affected snapshots. Test runs never refresh +these files. Pass package names to add or refresh only those packages: + +```sh +node packages/tools/src/record-snapshot-registry.ts is-number yocto-queue +``` diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/registry/archive.mjs b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/archive.mjs new file mode 100644 index 0000000000..003816bb23 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/archive.mjs @@ -0,0 +1,35 @@ +import { gzipSync } from 'node:zlib'; + +/** + * Pack small fixture files with fixed ustar metadata and 10 KiB record padding. + * @param {{ name: string, contents: Buffer, mode: number }[]} files + * @returns {Buffer} + */ +export function createTarball(files) { + const blocks = []; + for (const { name, contents, mode } of files) { + if (Buffer.byteLength(name) > 100) { + throw new Error(`Fixture archive path exceeds 100 bytes: ${name}`); + } + const header = Buffer.alloc(512); + header.write(name); + header.write(mode.toString(8).padStart(7, '0') + '\0', 100); + header.write('0000000\0', 108); + header.write('0000000\0', 116); + header.write(contents.length.toString(8).padStart(11, '0') + '\0', 124); + header.write('00000000000\0', 136); + header.fill(' ', 148, 156); + header.write('0', 156); + header.write('ustar\0', 257); + header.write('00', 263); + const checksum = header.reduce((sum, byte) => sum + byte, 0); + header.write(checksum.toString(8).padStart(6, '0') + '\0 ', 148); + blocks.push(header, contents, Buffer.alloc((512 - (contents.length % 512)) % 512)); + } + const archive = Buffer.concat([...blocks, Buffer.alloc(1024)]); + const padding = Buffer.alloc((10240 - (archive.length % 10240)) % 10240); + const tarball = gzipSync(Buffer.concat([archive, padding]), { level: 9 }); + // The gzip OS byte must not depend on the machine that packs the fixtures. + tarball[9] = 255; + return tarball; +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/registry/local/manifest.json b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/local/manifest.json new file mode 100644 index 0000000000..3d188c4b06 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/local/manifest.json @@ -0,0 +1,45 @@ +{ + "vp-test-echo": { + "name": "vp-test-echo", + "dist-tags": { + "latest": "1.0.0" + }, + "versions": { + "1.0.0": { + "name": "vp-test-echo", + "version": "1.0.0", + "private": true, + "license": "MIT", + "bin": { + "vp-test-echo": "cli.cjs", + "vp-test-echo-alias": "cli.cjs" + }, + "dist": { + "tarball": "{REGISTRY}/vp-test-echo/-/vp-test-echo-1.0.0.tgz", + "integrity": "sha512-LH7sCetZY9OeN7OZJOvwlvT/JES6RK2pBLJVsTr7MQzwr+6iPQBPj4fqcE50fWINAFa/++iY+N5SJ7d3oRFH8Q==" + } + } + } + }, + "vp-test-upper": { + "name": "vp-test-upper", + "dist-tags": { + "latest": "1.0.0" + }, + "versions": { + "1.0.0": { + "name": "vp-test-upper", + "version": "1.0.0", + "private": true, + "license": "MIT", + "bin": { + "vp-test-upper": "cli.cjs" + }, + "dist": { + "tarball": "{REGISTRY}/vp-test-upper/-/vp-test-upper-1.0.0.tgz", + "integrity": "sha512-tmrhV7C9AIWVqAI6eLTRv35Cm9ZfLQH+lKLhOgrBKs66r5wJ2PpZ3Gf9SUFqH3GgkP4LjHgQLJcduWSJtAyDLQ==" + } + } + } + } +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/registry/local/tarballs/vp-test-echo-1.0.0.tgz b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/local/tarballs/vp-test-echo-1.0.0.tgz new file mode 100644 index 0000000000..f570d0eb21 Binary files /dev/null and b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/local/tarballs/vp-test-echo-1.0.0.tgz differ diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/registry/local/tarballs/vp-test-upper-1.0.0.tgz b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/local/tarballs/vp-test-upper-1.0.0.tgz new file mode 100644 index 0000000000..400aeac3cf Binary files /dev/null and b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/local/tarballs/vp-test-upper-1.0.0.tgz differ diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/registry/manifest.json b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/manifest.json new file mode 100644 index 0000000000..6b8cbc9595 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/manifest.json @@ -0,0 +1,1226 @@ +{ + "testnpm2": { + "_id": "testnpm2", + "_rev": "29-8fb08185012beac6ee779acb1ec3e26f", + "name": "testnpm2", + "time": { + "modified": "2022-06-27T05:33:14.474Z", + "created": "2015-07-18T03:01:26.187Z", + "0.0.1": "2015-07-18T03:01:26.187Z", + "0.0.2": "2015-07-18T03:05:15.290Z", + "0.0.3": "2015-07-18T03:13:59.614Z", + "0.0.4": "2015-07-18T03:27:34.134Z", + "0.0.5": "2015-07-18T03:34:04.517Z", + "0.0.6": "2015-07-18T03:44:19.300Z", + "0.0.7": "2015-07-18T03:58:01.493Z", + "0.0.8": "2015-07-18T04:07:17.636Z", + "1.0.0": "2015-07-18T18:23:11.382Z", + "1.0.1": "2015-07-18T18:23:59.560Z", + "2.0.0": "2018-04-08T09:30:53.747Z", + "2.0.1": "2018-04-08T09:35:07.572Z" + }, + "maintainers": [ + { + "name": "fengmk2", + "email": "fengmk2@gmail.com" + } + ], + "dist-tags": { + "latest": "1.0.1", + "release-1": "1.0.1" + }, + "readme": "", + "versions": { + "1.0.0": { + "name": "testnpm2", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "_id": "testnpm2@1.0.0", + "_shasum": "3f9430987a1d4ff52c8c5162e99b5d8596efefa6", + "_from": ".", + "_npmVersion": "2.12.1", + "_nodeVersion": "2.3.4", + "_npmUser": { + "name": "fengmk2", + "email": "fengmk2@gmail.com" + }, + "maintainers": [ + { + "name": "fengmk2", + "email": "fengmk2@gmail.com" + } + ], + "dist": { + "shasum": "3f9430987a1d4ff52c8c5162e99b5d8596efefa6", + "tarball": "{REGISTRY}/testnpm2/-/testnpm2-1.0.0.tgz", + "integrity": "sha512-8gdtqxKad+83Iog2v514VsHsSk/R+we9j5/9zX9tB+QC2ubvB06zJ08k0PSl5uzviXByEMiWm7EzSbBAh2GZ/w==", + "signatures": [ + { + "keyid": "SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA", + "sig": "MEQCIDrEYCjkPJmoOzHB5eBvXAWfTInnWHAy7RBcGuNmKUN/AiA6m8AeoByYz7NVkcb++KDvfPO+gwVw7uaM1sRycIwZyA==" + } + ] + }, + "directories": {} + }, + "1.0.1": { + "name": "testnpm2", + "version": "1.0.1", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "_id": "testnpm2@1.0.1", + "_shasum": "8c7b209a673c360e540ab2777242171fd30fdee9", + "_from": ".", + "_npmVersion": "2.12.1", + "_nodeVersion": "2.3.4", + "_npmUser": { + "name": "fengmk2", + "email": "fengmk2@gmail.com" + }, + "maintainers": [ + { + "name": "fengmk2", + "email": "fengmk2@gmail.com" + } + ], + "dist": { + "shasum": "8c7b209a673c360e540ab2777242171fd30fdee9", + "tarball": "{REGISTRY}/testnpm2/-/testnpm2-1.0.1.tgz", + "integrity": "sha512-F4AQ+KmzhbOSlt7ae+X2O8IJktFZAcN6OK169TT4ny7M3e4Vje7NITZTOU31AtEk9L/Z8lrCrqinl/eY6WPuEw==", + "signatures": [ + { + "keyid": "SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA", + "sig": "MEQCICqyUi6OO0qltJG0Z2fI021Q87C6zFIWH9h2lb9PsyRKAiAHU26fIlW7Om8JPh2BEx72YAAVP2yXS2bvf9vzc/yjaw==" + } + ] + }, + "directories": {} + } + }, + "license": "ISC", + "readmeFilename": "" + }, + "test-vite-plus-install": { + "_id": "test-vite-plus-install", + "name": "test-vite-plus-install", + "dist-tags": { + "latest": "1.0.0" + }, + "versions": { + "1.0.0": { + "name": "test-vite-plus-install", + "version": "1.0.0", + "description": "just for snap-test", + "main": "index.js", + "author": "", + "license": "MIT", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "_id": "test-vite-plus-install@1.0.0", + "_integrity": "sha512-1tUZpG9YerpEquQ6jf5SnzNGP0tx02aiSF+h2UUErgWPJfPCAk/696KJGMjOVEqG6x6HeKjHZj2v6fZU+xGZ4g==", + "_resolved": "/private/var/folders/d7/5vmw5m15727gcsyvrqxyrr9h0000gn/T/1f7493b8d689388587ac709f1f7bbaaa/test-vite-plus-install-1.0.0.tgz", + "_from": "file:test-vite-plus-install-1.0.0.tgz", + "_nodeVersion": "22.20.0", + "_npmVersion": "10.9.3", + "dist": { + "integrity": "sha512-1tUZpG9YerpEquQ6jf5SnzNGP0tx02aiSF+h2UUErgWPJfPCAk/696KJGMjOVEqG6x6HeKjHZj2v6fZU+xGZ4g==", + "shasum": "74b163b5ea9aafcec9d71dcf9d1872a72b2e7efd", + "tarball": "{REGISTRY}/test-vite-plus-install/-/test-vite-plus-install-1.0.0.tgz", + "fileCount": 1, + "unpackedSize": 235, + "signatures": [ + { + "keyid": "SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U", + "sig": "MEYCIQC83BKWW/irUQav/opeoK660Bygjzw3TeadYVDIQ1UdnQIhAIGQZCAlM2ChSzc1aI8lmQpnoGtoIBczBwOYgLZLI/gg" + } + ] + }, + "_npmUser": { + "name": "fengmk2", + "email": "fengmk2@gmail.com" + }, + "directories": {}, + "maintainers": [ + { + "name": "fengmk2", + "email": "fengmk2@gmail.com" + } + ], + "_npmOperationalInternal": { + "host": "s3://npm-registry-packages-npm-production", + "tmp": "tmp/test-vite-plus-install_1.0.0_1760166590277_0.9487516044611353" + }, + "_hasShrinkwrap": false + } + }, + "time": { + "created": "2025-10-11T07:09:50.277Z", + "1.0.0": "2025-10-11T07:09:50.475Z", + "modified": "2025-10-11T07:09:50.835Z" + }, + "maintainers": [ + { + "name": "fengmk2", + "email": "fengmk2@gmail.com" + } + ], + "description": "just for snap-test", + "license": "MIT", + "readme": "ERROR: No README data found!", + "readmeFilename": "", + "_rev": "1-d52562037df1d209b9d7d4ef4c9c4fd3" + }, + "test-vite-plus-other-optional": { + "_id": "test-vite-plus-other-optional", + "_rev": "2-ffc2f4702a0c68e6779c3dd26638e3cc", + "name": "test-vite-plus-other-optional", + "dist-tags": { + "latest": "1.1.0" + }, + "versions": { + "1.0.0": { + "name": "test-vite-plus-other-optional", + "version": "1.0.0", + "author": "", + "license": "MIT", + "_id": "test-vite-plus-other-optional@1.0.0", + "maintainers": [ + { + "name": "fengmk2", + "email": "fengmk2@gmail.com" + } + ], + "dist": { + "shasum": "fb841d0b4c2cc6685a191847c1cf722ab69d8f7c", + "tarball": "{REGISTRY}/test-vite-plus-other-optional/-/test-vite-plus-other-optional-1.0.0.tgz", + "fileCount": 1, + "integrity": "sha512-WdFD6xiv29vmgcfM7BSphW4/w+uk6fnc9rHPX/nBsmy84qr7r3kx5o2rWQZPWfauvL8KeJ5iKbHDguKt9I4YUg==", + "signatures": [ + { + "sig": "MEUCIEmNGAg/JggmVK3SB87iFRr+TvNrhs8Zyntu5cn/bvc1AiEA+Oom7NSlIr4ELdUDYt6xt7OcC6TTJOM0nIOSH/rt6J4=", + "keyid": "SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U" + } + ], + "unpackedSize": 224 + }, + "main": "index.js", + "_from": "file:test-vite-plus-other-optional-1.0.0.tgz", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "_npmUser": { + "name": "fengmk2", + "email": "fengmk2@gmail.com" + }, + "_resolved": "/private/var/folders/d7/5vmw5m15727gcsyvrqxyrr9h0000gn/T/85bec7a92ae0c921726bfbb3a83b761e/test-vite-plus-other-optional-1.0.0.tgz", + "_integrity": "sha512-WdFD6xiv29vmgcfM7BSphW4/w+uk6fnc9rHPX/nBsmy84qr7r3kx5o2rWQZPWfauvL8KeJ5iKbHDguKt9I4YUg==", + "_npmVersion": "10.9.4", + "description": "", + "directories": {}, + "_nodeVersion": "22.21.0", + "_hasShrinkwrap": false, + "_npmOperationalInternal": { + "tmp": "tmp/test-vite-plus-other-optional_1.0.0_1761631994541_0.785122904686832", + "host": "s3://npm-registry-packages-npm-production" + } + }, + "1.1.0": { + "name": "test-vite-plus-other-optional", + "version": "1.1.0", + "description": "", + "main": "index.js", + "author": "", + "license": "MIT", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "_id": "test-vite-plus-other-optional@1.1.0", + "_integrity": "sha512-P50VcTwTViGpe23894vSBo2gssfTJaUavmUkveBQ7ets+A4qK6ShJR5nL8aKD0M9j/ga50++4MINkFHTyD3beQ==", + "_resolved": "/private/var/folders/d7/5vmw5m15727gcsyvrqxyrr9h0000gn/T/3b6648904861f37de2d515940cf63e6b/test-vite-plus-other-optional-1.1.0.tgz", + "_from": "file:test-vite-plus-other-optional-1.1.0.tgz", + "_nodeVersion": "22.21.0", + "_npmVersion": "10.9.4", + "dist": { + "integrity": "sha512-P50VcTwTViGpe23894vSBo2gssfTJaUavmUkveBQ7ets+A4qK6ShJR5nL8aKD0M9j/ga50++4MINkFHTyD3beQ==", + "shasum": "300dcf161a77e7297505405a9867789e7a600091", + "tarball": "{REGISTRY}/test-vite-plus-other-optional/-/test-vite-plus-other-optional-1.1.0.tgz", + "fileCount": 1, + "unpackedSize": 224, + "signatures": [ + { + "keyid": "SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U", + "sig": "MEYCIQDCSme6AbBGQ3wFepzcwxmoJXJ4c7ObRq3WmAtwcL9yhgIhAO8Att+g2XQ8j4riakzjTnZo6D7rrd4NRicIw4OhDVxz" + } + ] + }, + "_npmUser": { + "name": "fengmk2", + "email": "fengmk2@gmail.com" + }, + "directories": {}, + "maintainers": [ + { + "name": "fengmk2", + "email": "fengmk2@gmail.com" + } + ], + "_npmOperationalInternal": { + "host": "s3://npm-registry-packages-npm-production", + "tmp": "tmp/test-vite-plus-other-optional_1.1.0_1761632025821_0.3001907321079851" + }, + "_hasShrinkwrap": false + } + }, + "time": { + "created": "2025-10-28T06:13:14.540Z", + "modified": "2025-10-28T06:13:46.210Z", + "1.0.0": "2025-10-28T06:13:14.745Z", + "1.1.0": "2025-10-28T06:13:46.017Z" + }, + "license": "MIT", + "maintainers": [ + { + "name": "fengmk2", + "email": "fengmk2@gmail.com" + } + ], + "readme": "ERROR: No README data found!", + "readmeFilename": "" + }, + "test-vite-plus-package": { + "_id": "test-vite-plus-package", + "_rev": "4-a9205063cbb8d0ec8147d3688e434885", + "name": "test-vite-plus-package", + "dist-tags": { + "latest": "1.0.0" + }, + "versions": { + "1.0.0": { + "name": "test-vite-plus-package", + "version": "1.0.0", + "author": "", + "license": "MIT", + "_id": "test-vite-plus-package@1.0.0", + "maintainers": [ + { + "name": "fengmk2", + "email": "fengmk2@gmail.com" + } + ], + "dist": { + "shasum": "ff439467784c8738812a5de732c24513b32ab584", + "tarball": "{REGISTRY}/test-vite-plus-package/-/test-vite-plus-package-1.0.0.tgz", + "fileCount": 1, + "integrity": "sha512-xj5hJUNL5vihX9TCYXbk8V5r+8MfqG2fDJcRdpQz6tlLob3vkFjsVM8pT5QjTzHS6lBXHd0r9TOMS8+sETjnpw==", + "signatures": [ + { + "sig": "MEUCIDb7DnZovysqbeRmZivHhsKyA7cvKYMrq1AIn9ZgtBtoAiEAjp+igoe31fhBdeQgwFmSBtGFvjOPdEwFRVP19wOtbwA=", + "keyid": "SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U" + } + ], + "unpackedSize": 235 + }, + "main": "index.js", + "_from": "file:test-vite-plus-package-1.0.0.tgz", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "_npmUser": { + "name": "fengmk2", + "email": "fengmk2@gmail.com" + }, + "_resolved": "/private/var/folders/d7/5vmw5m15727gcsyvrqxyrr9h0000gn/T/9a39c169b8f2516a2b15e5afe73d3e7c/test-vite-plus-package-1.0.0.tgz", + "_integrity": "sha512-xj5hJUNL5vihX9TCYXbk8V5r+8MfqG2fDJcRdpQz6tlLob3vkFjsVM8pT5QjTzHS6lBXHd0r9TOMS8+sETjnpw==", + "_npmVersion": "10.9.3", + "description": "just for snap-test", + "directories": {}, + "_nodeVersion": "22.20.0", + "_hasShrinkwrap": false, + "_npmOperationalInternal": { + "tmp": "tmp/test-vite-plus-package_1.0.0_1760433415907_0.725587526671376", + "host": "s3://npm-registry-packages-npm-production" + } + } + }, + "time": { + "created": "2025-10-14T09:16:55.906Z", + "modified": "2025-10-27T11:26:27.234Z", + "1.0.0": "2025-10-14T09:16:56.086Z", + "1.1.0": "2025-10-27T09:03:09.582Z" + }, + "license": "MIT", + "description": "just for snap-test", + "maintainers": [ + { + "name": "fengmk2", + "email": "fengmk2@gmail.com" + } + ], + "readme": "", + "readmeFilename": "" + }, + "test-vite-plus-package-optional": { + "_id": "test-vite-plus-package-optional", + "name": "test-vite-plus-package-optional", + "dist-tags": { + "latest": "1.0.0" + }, + "versions": { + "1.0.0": { + "name": "test-vite-plus-package-optional", + "version": "1.0.0", + "description": "just for snap-test", + "main": "index.js", + "author": "", + "license": "MIT", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "_id": "test-vite-plus-package-optional@1.0.0", + "_integrity": "sha512-7rJ71VaETMzhK3Iwd14vutJtZt90MqUhu9KwnEEe55htLIIJs/WImI02NopwsV9Ra/Rqld4KkvbvOy6MD0b1sw==", + "_resolved": "/private/var/folders/d7/5vmw5m15727gcsyvrqxyrr9h0000gn/T/7801cd5ebbd7ee96e1461f58b5bd9d43/test-vite-plus-package-optional-1.0.0.tgz", + "_from": "file:test-vite-plus-package-optional-1.0.0.tgz", + "_nodeVersion": "22.20.0", + "_npmVersion": "10.9.3", + "dist": { + "integrity": "sha512-7rJ71VaETMzhK3Iwd14vutJtZt90MqUhu9KwnEEe55htLIIJs/WImI02NopwsV9Ra/Rqld4KkvbvOy6MD0b1sw==", + "shasum": "5815a3a6526aae5ae2d4a03eefcbec0ea489f69e", + "tarball": "{REGISTRY}/test-vite-plus-package-optional/-/test-vite-plus-package-optional-1.0.0.tgz", + "fileCount": 1, + "unpackedSize": 244, + "signatures": [ + { + "keyid": "SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U", + "sig": "MEQCIG2kAGpIyqqo6gmwKSBfBv5JTzePzri35pwd8M1iuFrVAiBQOGDbUX1LEf+dHACnKO/1Vtm7wpks9sb0JeOVdThNlQ==" + } + ] + }, + "_npmUser": { + "name": "fengmk2", + "email": "fengmk2@gmail.com" + }, + "directories": {}, + "maintainers": [ + { + "name": "fengmk2", + "email": "fengmk2@gmail.com" + } + ], + "_npmOperationalInternal": { + "host": "s3://npm-registry-packages-npm-production", + "tmp": "tmp/test-vite-plus-package-optional_1.0.0_1760602422385_0.6841124231375992" + }, + "_hasShrinkwrap": false + } + }, + "time": { + "created": "2025-10-16T08:13:42.384Z", + "1.0.0": "2025-10-16T08:13:42.558Z", + "modified": "2025-10-16T08:13:42.885Z" + }, + "maintainers": [ + { + "name": "fengmk2", + "email": "fengmk2@gmail.com" + } + ], + "description": "just for snap-test", + "license": "MIT", + "readme": "ERROR: No README data found!", + "readmeFilename": "", + "_rev": "1-a58f61915f997de720903a30cfb5c24c" + }, + "test-vite-plus-top-package": { + "_id": "test-vite-plus-top-package", + "_rev": "2-513013acfd722d83329b71887e497260", + "name": "test-vite-plus-top-package", + "dist-tags": { + "latest": "1.1.0" + }, + "versions": { + "1.0.0": { + "name": "test-vite-plus-top-package", + "version": "1.0.0", + "author": "", + "license": "MIT", + "_id": "test-vite-plus-top-package@1.0.0", + "maintainers": [ + { + "name": "fengmk2", + "email": "fengmk2@gmail.com" + } + ], + "dist": { + "shasum": "957d0e37395ea4f242a0417e707b60d57ffbed8f", + "tarball": "{REGISTRY}/test-vite-plus-top-package/-/test-vite-plus-top-package-1.0.0.tgz", + "fileCount": 1, + "integrity": "sha512-5KN/rXWts++iUQadDZWv7F7acor7ihFiCSodQoizPcpMAtz85gczqHQwOwH9HEVXcDo/67X9KsL2HRfKQWHPLw==", + "signatures": [ + { + "sig": "MEUCIQDeDMSiZOXf4ktSKv94OceoZLJzh2JoyOwEX28B8/f6UgIgaabJXK8PmfxcGFLZ/TYj5ChJBWvmcGx22G5/2DbV73o=", + "keyid": "SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U" + } + ], + "unpackedSize": 318 + }, + "main": "index.js", + "_from": "file:test-vite-plus-top-package-1.0.0.tgz", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "_npmUser": { + "name": "fengmk2", + "email": "fengmk2@gmail.com" + }, + "_resolved": "/private/var/folders/d7/5vmw5m15727gcsyvrqxyrr9h0000gn/T/26a31dd3160813c5a4d8e76abd201af7/test-vite-plus-top-package-1.0.0.tgz", + "_integrity": "sha512-5KN/rXWts++iUQadDZWv7F7acor7ihFiCSodQoizPcpMAtz85gczqHQwOwH9HEVXcDo/67X9KsL2HRfKQWHPLw==", + "_npmVersion": "10.9.4", + "description": "use deps on test-vite-plus-package", + "directories": {}, + "_nodeVersion": "22.21.0", + "dependencies": { + "test-vite-plus-package": "1.0.0" + }, + "_hasShrinkwrap": false, + "_npmOperationalInternal": { + "tmp": "tmp/test-vite-plus-top-package_1.0.0_1761555702696_0.7254356595912901", + "host": "s3://npm-registry-packages-npm-production" + } + }, + "1.1.0": { + "name": "test-vite-plus-top-package", + "version": "1.1.0", + "description": "use deps on test-vite-plus-package", + "main": "index.js", + "author": "", + "license": "MIT", + "dependencies": { + "test-vite-plus-package": "1.1.0" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "_id": "test-vite-plus-top-package@1.1.0", + "_integrity": "sha512-8L1G/+q/8RmirxFTVdAj/1tj959LJL7XwQ+or29k155zRZM8bw+hXKAVjpd0wRj/xNSzROr5+JMKgxsyLkieVw==", + "_resolved": "/private/var/folders/d7/5vmw5m15727gcsyvrqxyrr9h0000gn/T/3c6d33127d794bffec4069448a17df25/test-vite-plus-top-package-1.1.0.tgz", + "_from": "file:test-vite-plus-top-package-1.1.0.tgz", + "_nodeVersion": "22.21.0", + "_npmVersion": "10.9.4", + "dist": { + "integrity": "sha512-8L1G/+q/8RmirxFTVdAj/1tj959LJL7XwQ+or29k155zRZM8bw+hXKAVjpd0wRj/xNSzROr5+JMKgxsyLkieVw==", + "shasum": "f11b7e822172b06975a16a0ce141feb1bb2be476", + "tarball": "{REGISTRY}/test-vite-plus-top-package/-/test-vite-plus-top-package-1.1.0.tgz", + "fileCount": 1, + "unpackedSize": 318, + "signatures": [ + { + "keyid": "SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U", + "sig": "MEUCICw4k88NTxQOQ0FkPiOGbhjDOhOo/eS2ZUUoqfDBwCC5AiEAv8MAQfCli++zfbV2d+jVSNypE725aAcoqnH8U8z/N40=" + } + ] + }, + "_npmUser": { + "name": "fengmk2", + "email": "fengmk2@gmail.com" + }, + "directories": {}, + "maintainers": [ + { + "name": "fengmk2", + "email": "fengmk2@gmail.com" + } + ], + "_npmOperationalInternal": { + "host": "s3://npm-registry-packages-npm-production", + "tmp": "tmp/test-vite-plus-top-package_1.1.0_1761555746085_0.7306399581466132" + }, + "_hasShrinkwrap": false + } + }, + "time": { + "created": "2025-10-27T09:01:42.695Z", + "modified": "2025-10-27T09:02:26.446Z", + "1.0.0": "2025-10-27T09:01:42.870Z", + "1.1.0": "2025-10-27T09:02:26.274Z" + }, + "license": "MIT", + "description": "use deps on test-vite-plus-package", + "maintainers": [ + { + "name": "fengmk2", + "email": "fengmk2@gmail.com" + } + ], + "readme": "ERROR: No README data found!", + "readmeFilename": "" + }, + "is-number": { + "_id": "is-number", + "_rev": "43-3fe877efbeead423ee53a12d5ca44395", + "name": "is-number", + "description": "Returns true if a number or string value is a finite number. Useful for regex matches, parsing, user input, etc.", + "dist-tags": { + "latest": "7.0.0" + }, + "versions": { + "6.0.0": { + "name": "is-number", + "description": "Returns true if the value is a number. comprehensive tests.", + "version": "6.0.0", + "homepage": "https://github.com/jonschlinkert/is-number", + "author": { + "name": "Jon Schlinkert", + "url": "https://github.com/jonschlinkert" + }, + "contributors": [ + { + "name": "Jon Schlinkert", + "url": "http://twitter.com/jonschlinkert" + }, + { + "name": "Olsten Larck", + "url": "https://i.am.charlike.online" + }, + { + "name": "Rouven WeรŸling", + "url": "www.rouvenwessling.de" + } + ], + "repository": { + "type": "git", + "url": "git+https://github.com/jonschlinkert/is-number.git" + }, + "bugs": { + "url": "https://github.com/jonschlinkert/is-number/issues" + }, + "license": "MIT", + "files": ["index.js"], + "main": "index.js", + "engines": { + "node": ">=0.10.0" + }, + "scripts": { + "test": "mocha" + }, + "devDependencies": { + "benchmarked": "^2.0.0", + "chalk": "^2.1.0", + "gulp-format-md": "^1.0.0", + "mocha": "^3.0.1" + }, + "keywords": [ + "check", + "coerce", + "coercion", + "integer", + "is", + "is-nan", + "is-num", + "is-number", + "istype", + "kind", + "math", + "nan", + "num", + "number", + "numeric", + "test", + "type", + "typeof", + "value" + ], + "verb": { + "toc": false, + "layout": "default", + "tasks": ["readme"], + "related": { + "list": ["isobject", "is-plain-object", "is-primitive", "kind-of"] + }, + "plugins": ["gulp-format-md"], + "lint": { + "reflinks": true + } + }, + "gitHead": "b0953635829711e7dceec0eaa92bc56521a546eb", + "_id": "is-number@6.0.0", + "_npmVersion": "5.8.0", + "_nodeVersion": "9.9.0", + "_npmUser": { + "name": "jonschlinkert", + "email": "github@sellside.com" + }, + "dist": { + "integrity": "sha512-Wu1VHeILBK8KAWJUAiSZQX94GmOE45Rg6/538fKwiloUu21KncEkYGPqob2oSZ5mUT73vLGrHQjKw3KMPwfDzg==", + "shasum": "e6d15ad31fc262887cccf217ae5f9316f81b1995", + "tarball": "{REGISTRY}/is-number/-/is-number-6.0.0.tgz", + "fileCount": 4, + "unpackedSize": 8960, + "signatures": [ + { + "keyid": "SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA", + "sig": "MEUCIHw0y05qyjN2G0Uw0DINiC25SvWlVSgd2UD/ddJM8scPAiEArxOqwwCq3PKUhw7VEm21DI141kJs/ktPkaANlO5cGOs=" + } + ] + }, + "maintainers": [ + { + "email": "brian.woodward@gmail.com", + "name": "doowb" + }, + { + "email": "github@sellside.com", + "name": "jonschlinkert" + }, + { + "email": "me@rouvenwessling.de", + "name": "realityking" + } + ], + "directories": {}, + "_npmOperationalInternal": { + "host": "s3://npm-registry-packages", + "tmp": "tmp/is-number_6.0.0_1522515759840_0.005598684369539919" + }, + "_hasShrinkwrap": false + }, + "7.0.0": { + "name": "is-number", + "description": "Returns true if a number or string value is a finite number. Useful for regex matches, parsing, user input, etc.", + "version": "7.0.0", + "homepage": "https://github.com/jonschlinkert/is-number", + "author": { + "name": "Jon Schlinkert", + "url": "https://github.com/jonschlinkert" + }, + "contributors": [ + { + "name": "Jon Schlinkert", + "url": "http://twitter.com/jonschlinkert" + }, + { + "name": "Olsten Larck", + "url": "https://i.am.charlike.online" + }, + { + "name": "Rouven WeรŸling", + "url": "www.rouvenwessling.de" + } + ], + "repository": { + "type": "git", + "url": "git+https://github.com/jonschlinkert/is-number.git" + }, + "bugs": { + "url": "https://github.com/jonschlinkert/is-number/issues" + }, + "license": "MIT", + "files": ["index.js"], + "main": "index.js", + "engines": { + "node": ">=0.12.0" + }, + "scripts": { + "test": "mocha" + }, + "devDependencies": { + "ansi": "^0.3.1", + "benchmark": "^2.1.4", + "gulp-format-md": "^1.0.0", + "mocha": "^3.5.3" + }, + "keywords": [ + "cast", + "check", + "coerce", + "coercion", + "finite", + "integer", + "is", + "isnan", + "is-nan", + "is-num", + "is-number", + "isnumber", + "isfinite", + "istype", + "kind", + "math", + "nan", + "num", + "number", + "numeric", + "parseFloat", + "parseInt", + "test", + "type", + "typeof", + "value" + ], + "verb": { + "toc": false, + "layout": "default", + "tasks": ["readme"], + "related": { + "list": ["is-plain-object", "is-primitive", "isobject", "kind-of"] + }, + "plugins": ["gulp-format-md"], + "lint": { + "reflinks": true + } + }, + "gitHead": "98e8ff1da1a89f93d1397a24d7413ed15421c139", + "_id": "is-number@7.0.0", + "_npmVersion": "6.1.0", + "_nodeVersion": "10.0.0", + "_npmUser": { + "name": "jonschlinkert", + "email": "github@sellside.com" + }, + "dist": { + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "shasum": "7535345b896734d5f80c4d06c50955527a14f12b", + "tarball": "{REGISTRY}/is-number/-/is-number-7.0.0.tgz", + "fileCount": 4, + "unpackedSize": 9615, + "npm-signature": "-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbPOMKCRA9TVsSAnZWagAABmIP/iUfV7gQnAsn2vRNKWIu\nlxkfXFutnV1Bo5P8mlv37Zg+PN5/Ri/RATtKGEosAvHL/HK2HkFH+rLYkupg\nCYaAVXNOJiFN9vK61YYp0TB0PBtGDQ2UwcElxDQ8OP6Djni4fl2fLjVZ+JeN\nPF1bKPE6oMevNg3GtNMrb1QKZNGjplOKFmlmm9fe653LHwIH1zPYy6QQ+cgG\nQ8Czmhdf+NuX8WXqR1h5mHTKCCVAqzcpJ21uX9292aoWlzlijzg9cc8KEPEY\ncbUjJf4NMLs89/8G6tcpDP5qoVjQnWIgfM0mDXt4P7ESdqjusdyNa+3eS/Yi\nsQuevXmth+41kvQaVy7rLbeAssWNbc7IJ+X1qpgnUqTz4rtiAaRnZ6rzAE2s\nrr723lN2H7ALlnDVm5qzXLTTjHwbdYMYuaYHTOMovytYWeRnwhfNCpKeQcEe\nUYR3sm2tEdxsMI5ciu5ULGj/PEKnSOsQcq2y4QjNFz4F5ReXiv+CtphQubAr\nVkKwrjIovN97YXmyrkfJB1zCInCMUz11YgRY0wNaCJPJ4E0V4KdfWgCEyt+F\nqKcdylewrOAma06WozqUXzRH3uNNbMTfsE1nGqbcofJPNBre/VriCwF7zS/H\nLhGfCe6LJuY2Q+2Eel1vtwnY+vAoaWrr7QisjcvywYHvqWlRob2IxC1Ygp3A\nvUY+\r\n=M7jx\r\n-----END PGP SIGNATURE-----\r\n", + "signatures": [ + { + "keyid": "SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA", + "sig": "MEUCIGex85Xx5rKhEgNSkg7z5bBu7vbcFbJnWJZbV3f9Td8GAiEA5wzgLXU41sE6ezCKGrmzsyMtm6M+2OhefJJhkzPEAoc=" + } + ] + }, + "maintainers": [ + { + "email": "brian.woodward@gmail.com", + "name": "doowb" + }, + { + "email": "github@sellside.com", + "name": "jonschlinkert" + }, + { + "email": "me@rouvenwessling.de", + "name": "realityking" + } + ], + "directories": {}, + "_npmOperationalInternal": { + "host": "s3://npm-registry-packages", + "tmp": "tmp/is-number_7.0.0_1530716938183_0.28831183290614004" + }, + "_hasShrinkwrap": false + } + }, + "readme": "# is-number [![NPM version](https://img.shields.io/npm/v/is-number.svg?style=flat)](https://www.npmjs.com/package/is-number) [![NPM monthly downloads](https://img.shields.io/npm/dm/is-number.svg?style=flat)](https://npmjs.org/package/is-number) [![NPM total downloads](https://img.shields.io/npm/dt/is-number.svg?style=flat)](https://npmjs.org/package/is-number) [![Linux Build Status](https://img.shields.io/travis/jonschlinkert/is-number.svg?style=flat&label=Travis)](https://travis-ci.org/jonschlinkert/is-number)\n\n> Returns true if the value is a finite number.\n\nPlease consider following this project's author, [Jon Schlinkert](https://github.com/jonschlinkert), and consider starring the project to show your :heart: and support.\n\n## Install\n\nInstall with [npm](https://www.npmjs.com/):\n\n```sh\n$ npm install --save is-number\n```\n\n## Why is this needed?\n\nIn JavaScript, it's not always as straightforward as it should be to reliably check if a value is a number. It's common for devs to use `+`, `-`, or `Number()` to cast a string value to a number (for example, when values are returned from user input, regex matches, parsers, etc). But there are many non-intuitive edge cases that yield unexpected results:\n\n```js\nconsole.log(+[]); //=> 0\nconsole.log(+''); //=> 0\nconsole.log(+' '); //=> 0\nconsole.log(typeof NaN); //=> 'number'\n```\n\nThis library offers a performant way to smooth out edge cases like these.\n\n## Usage\n\n```js\nconst isNumber = require('is-number');\n```\n\nSee the [tests](./test.js) for more examples.\n\n### true\n\n```js\nisNumber(5e3); // true\nisNumber(0xff); // true\nisNumber(-1.1); // true\nisNumber(0); // true\nisNumber(1); // true\nisNumber(1.1); // true\nisNumber(10); // true\nisNumber(10.10); // true\nisNumber(100); // true\nisNumber('-1.1'); // true\nisNumber('0'); // true\nisNumber('012'); // true\nisNumber('0xff'); // true\nisNumber('1'); // true\nisNumber('1.1'); // true\nisNumber('10'); // true\nisNumber('10.10'); // true\nisNumber('100'); // true\nisNumber('5e3'); // true\nisNumber(parseInt('012')); // true\nisNumber(parseFloat('012')); // true\n```\n\n### False\n\nEverything else is false, as you would expect:\n\n```js\nisNumber(Infinity); // false\nisNumber(NaN); // false\nisNumber(null); // false\nisNumber(undefined); // false\nisNumber(''); // false\nisNumber(' '); // false\nisNumber('foo'); // false\nisNumber([1]); // false\nisNumber([]); // false\nisNumber(function () {}); // false\nisNumber({}); // false\n```\n\n## Release history\n\n### 7.0.0\n\n* Refactor. Now uses `.isFinite` if it exists.\n* Performance is about the same as v6.0 when the value is a string or number. But it's now 3x-4x faster when the value is not a string or number.\n\n### 6.0.0\n\n* Optimizations, thanks to @benaadams.\n\n### 5.0.0\n\n**Breaking changes**\n\n* removed support for `instanceof Number` and `instanceof String`\n\n## Benchmarks\n\nAs with all benchmarks, take these with a grain of salt. See the [benchmarks](./benchmark/index.js) for more detail.\n\n```\n# all\nv7.0 x 413,222 ops/sec ยฑ2.02% (86 runs sampled)\nv6.0 x 111,061 ops/sec ยฑ1.29% (85 runs sampled)\nparseFloat x 317,596 ops/sec ยฑ1.36% (86 runs sampled)\nfastest is 'v7.0'\n\n# string\nv7.0 x 3,054,496 ops/sec ยฑ1.05% (89 runs sampled)\nv6.0 x 2,957,781 ops/sec ยฑ0.98% (88 runs sampled)\nparseFloat x 3,071,060 ops/sec ยฑ1.13% (88 runs sampled)\nfastest is 'parseFloat,v7.0'\n\n# number\nv7.0 x 3,146,895 ops/sec ยฑ0.89% (89 runs sampled)\nv6.0 x 3,214,038 ops/sec ยฑ1.07% (89 runs sampled)\nparseFloat x 3,077,588 ops/sec ยฑ1.07% (87 runs sampled)\nfastest is 'v6.0'\n```\n\n## About\n\n
\nContributing\n\nPull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new).\n\n
\n\n
\nRunning Tests\n\nRunning and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command:\n\n```sh\n$ npm install && npm test\n```\n\n
\n\n
\nBuilding docs\n\n_(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_\n\nTo generate the readme, run the following command:\n\n```sh\n$ npm install -g verbose/verb#dev verb-generate-readme && verb\n```\n\n
\n\n### Related projects\n\nYou might also be interested in these projects:\n\n* [is-plain-object](https://www.npmjs.com/package/is-plain-object): Returns true if an object was created by the `Object` constructor. | [homepage](https://github.com/jonschlinkert/is-plain-object \"Returns true if an object was created by the `Object` constructor.\")\n* [is-primitive](https://www.npmjs.com/package/is-primitive): Returns `true` if the value is a primitive. | [homepage](https://github.com/jonschlinkert/is-primitive \"Returns `true` if the value is a primitive. \")\n* [isobject](https://www.npmjs.com/package/isobject): Returns true if the value is an object and not an array or null. | [homepage](https://github.com/jonschlinkert/isobject \"Returns true if the value is an object and not an array or null.\")\n* [kind-of](https://www.npmjs.com/package/kind-of): Get the native type of a value. | [homepage](https://github.com/jonschlinkert/kind-of \"Get the native type of a value.\")\n\n### Contributors\n\n| **Commits** | **Contributor** | \n| --- | --- |\n| 49 | [jonschlinkert](https://github.com/jonschlinkert) |\n| 5 | [charlike-old](https://github.com/charlike-old) |\n| 1 | [benaadams](https://github.com/benaadams) |\n| 1 | [realityking](https://github.com/realityking) |\n\n### Author\n\n**Jon Schlinkert**\n\n* [LinkedIn Profile](https://linkedin.com/in/jonschlinkert)\n* [GitHub Profile](https://github.com/jonschlinkert)\n* [Twitter Profile](https://twitter.com/jonschlinkert)\n\n### License\n\nCopyright ยฉ 2018, [Jon Schlinkert](https://github.com/jonschlinkert).\nReleased under the [MIT License](LICENSE).\n\n***\n\n_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.6.0, on June 15, 2018._", + "maintainers": [ + { + "email": "brian.woodward@gmail.com", + "name": "doowb" + }, + { + "email": "github@sellside.com", + "name": "jonschlinkert" + }, + { + "email": "me@rouvenwessling.de", + "name": "realityking" + } + ], + "time": { + "modified": "2023-05-26T16:12:33.057Z", + "created": "2014-09-22T01:58:51.592Z", + "6.0.0": "2018-03-31T17:02:39.953Z", + "7.0.0": "2018-07-04T15:08:58.238Z" + }, + "homepage": "https://github.com/jonschlinkert/is-number", + "keywords": [ + "cast", + "check", + "coerce", + "coercion", + "finite", + "integer", + "is", + "isnan", + "is-nan", + "is-num", + "is-number", + "isnumber", + "isfinite", + "istype", + "kind", + "math", + "nan", + "num", + "number", + "numeric", + "parseFloat", + "parseInt", + "test", + "type", + "typeof", + "value" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/jonschlinkert/is-number.git" + }, + "author": { + "name": "Jon Schlinkert", + "url": "https://github.com/jonschlinkert" + }, + "bugs": { + "url": "https://github.com/jonschlinkert/is-number/issues" + }, + "readmeFilename": "README.md", + "license": "MIT", + "users": { + "jonschlinkert": true, + "antanst": true, + "rocket0191": true, + "papasavva": true, + "456wyc": true, + "maycon_ribeiro": true, + "leix3041": true, + "snowdream": true, + "fizzvr": true, + "jameskrill": true, + "rioli": true, + "gugadev": true, + "fearnbuster": true, + "flumpus-dev": true + }, + "contributors": [ + { + "name": "Jon Schlinkert", + "url": "http://twitter.com/jonschlinkert" + }, + { + "name": "Olsten Larck", + "url": "https://i.am.charlike.online" + }, + { + "name": "Rouven WeรŸling", + "url": "www.rouvenwessling.de" + } + ] + }, + "yocto-queue": { + "_id": "yocto-queue", + "_rev": "10-ed85a82b9a53e454f61a3431f121fdf7", + "name": "yocto-queue", + "dist-tags": { + "latest": "1.2.2" + }, + "versions": { + "0.1.0": { + "name": "yocto-queue", + "version": "0.1.0", + "keywords": [ + "queue", + "data", + "structure", + "algorithm", + "queues", + "queuing", + "list", + "array", + "linkedlist", + "fifo", + "enqueue", + "dequeue", + "data-structure" + ], + "author": { + "url": "https://sindresorhus.com", + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com" + }, + "license": "MIT", + "_id": "yocto-queue@0.1.0", + "maintainers": [ + { + "name": "sindresorhus", + "email": "sindresorhus@gmail.com" + } + ], + "homepage": "https://github.com/sindresorhus/yocto-queue#readme", + "bugs": { + "url": "https://github.com/sindresorhus/yocto-queue/issues" + }, + "dist": { + "shasum": "0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b", + "tarball": "{REGISTRY}/yocto-queue/-/yocto-queue-0.1.0.tgz", + "fileCount": 5, + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "signatures": [ + { + "sig": "MEYCIQDT6PTeFAw+w2P0CanG3paScSDLRLBiZjcK25Jh2KvzfQIhAKTn5VxVaUWC1f0cOT5b/wdosBeinflqpF9rUUUuiekj", + "keyid": "SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA" + } + ], + "unpackedSize": 6027, + "npm-signature": "-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfvKKwCRA9TVsSAnZWagAAGXMP+gOOaFATY9WhVgSiN4ZI\nwyHaog6af9hieXnM+eZf3YLHnC5caM20SxdnHcvILsuf9K0pqkMXtzC9Ghgs\njnFaQ65BLvEwz7wByXB799QoCNv3YOoEKEwvqg81fTEJwVpPB7/1LD/phu2j\n3wcWbfyUBYQUHrFqZeb7eR1CnHDxov6odtBxLs84USK+0+8fLmO4wn30Quwl\nyVGrlNm3X4NmuJFetHLUhfFjIL3cBlfG191/DeNPPhqVBxx2Uj2l9oUX5eVW\nIVkL0s7OxQn8L8VN/YxOqus+kitnku2Jb4hVo/yUzZcx5w+KPMMNBnHqljNm\nGUbrfQBHtUJiZOB684vsR8TFAc6ZWrIAzXKtcejof/qEkGV/60iS63xANr2w\ntEYoNsAlDVBZYqXXbV7rwyBAIEM7SvrVsvGWMS8tAgnMUD0pf2TO1mOP7ph8\nWGlMuJegqEBg9HoHdXtfpQEIq8s76XGnROX1Y+OFXdHiWCYo2US9PsIMXRQ4\notOcWRV/DIXXmKn+pFM8CaE09NpIJBPLCeMtxHSR2qCCS4dCTx7kD09oFdF6\nMETv0Y0/5nPZjUMLsY2xBjp10ntpzH+4iTWmYYhBlNrcNpc/eoZIBMyS3+Ot\n3ZE7p9eNKNW6sdD/pQycDV0S1565FymQaOdB4RAUR26/zayYuZFlqva9hXWR\nMel2\r\n=rCXP\r\n-----END PGP SIGNATURE-----\r\n" + }, + "engines": { + "node": ">=10" + }, + "funding": "https://github.com/sponsors/sindresorhus", + "gitHead": "af91c37f51aecf607ca2261b907b9d86b6a69e77", + "scripts": { + "test": "xo && ava && tsd" + }, + "_npmUser": { + "name": "sindresorhus", + "email": "sindresorhus@gmail.com" + }, + "repository": { + "url": "git+https://github.com/sindresorhus/yocto-queue.git", + "type": "git" + }, + "_npmVersion": "6.14.9", + "description": "Tiny queue data structure", + "directories": {}, + "_nodeVersion": "10.22.1", + "_hasShrinkwrap": false, + "devDependencies": { + "xo": "^0.35.0", + "ava": "^2.4.0", + "tsd": "^0.13.1" + }, + "_npmOperationalInternal": { + "tmp": "tmp/yocto-queue_0.1.0_1606197935700_0.6375493777344527", + "host": "s3://npm-registry-packages" + } + }, + "1.0.0": { + "name": "yocto-queue", + "version": "1.0.0", + "keywords": [ + "queue", + "data", + "structure", + "algorithm", + "queues", + "queuing", + "list", + "array", + "linkedlist", + "fifo", + "enqueue", + "dequeue", + "data-structure" + ], + "author": { + "url": "https://sindresorhus.com", + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com" + }, + "license": "MIT", + "_id": "yocto-queue@1.0.0", + "maintainers": [ + { + "name": "sindresorhus", + "email": "sindresorhus@gmail.com" + } + ], + "homepage": "https://github.com/sindresorhus/yocto-queue#readme", + "bugs": { + "url": "https://github.com/sindresorhus/yocto-queue/issues" + }, + "dist": { + "shasum": "7f816433fb2cbc511ec8bf7d263c3b58a1a3c251", + "tarball": "{REGISTRY}/yocto-queue/-/yocto-queue-1.0.0.tgz", + "fileCount": 5, + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "signatures": [ + { + "sig": "MEUCIQCgpquEkkrOvziIIdkPS60Zmbl1a3cy9CW+fyss6yD+gQIgex85tTH7ZLeMp/djFhHgZ7e9HvigzwOiEE7hUPg+4rI=", + "keyid": "SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA" + } + ], + "unpackedSize": 6409, + "npm-signature": "-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhFbNRCRA9TVsSAnZWagAAyWcQAKEhMtwhQLcTexqOYTrf\np094W6yti0MY6bltGszSDhfoXBVgMBlou8YSk/s7EF/Fbl6356tMK6OClWWf\nvU2kaUWeSdlngv+S5baQ7fk9liA29jIGOHKXgt4h6XFzSfLA9b5hVbmQ0QBR\n37Fj96bt0756N7N/8J2lXREiPvOwkZ7QMhv5l1W8WV11j2QWn5Z64vCehbXJ\ns4M7leEOBA3/+HZYSdgBhK9Xm42+TJP3VtiyXZMnz+evPlydAvt1DmwPDbIx\nVU68PvQIA0/+RgtjiRD6rAX3LD2CG4g2YmdjBXYgq+xlTW76EM11aZbqCkkI\nfv/qevquZsYOn2JWwYHd2dRs7/xTm+AcLV2sdV0hS/bVeQx4AqBWwyA9uk2E\nOiYZ9KcfkQxMNae7+1Bw6bPL5dwpO5vim6OZ7+Wo7Qa83Bk/piK4r9cAZuOK\nYq4F0yxntA6+rHMSQ4EYluiN+mrqVqn2um7WvAO857YRSuOQf6cBt+zfEq3N\nnR3Iqm6V108sjxv79y3U1REAV7l2YZGGQH3BsM+XjVmjqmvlTxWDm/Uyhlzj\nMhw13HW32uWvkY8bIN5iEYKuaT5eDltFSVN83WFv0+kOV1hmvm2b8u4ZDFCS\n6n8rREm+yYSpgaLXf+VA/cTzqPmkOqp8qwwfk6DbJDNhATWdDt4h4TazXUrX\npIJG\r\n=Vcs5\r\n-----END PGP SIGNATURE-----\r\n" + }, + "type": "module", + "engines": { + "node": ">=12.20" + }, + "exports": "./index.js", + "funding": "https://github.com/sponsors/sindresorhus", + "gitHead": "2e57f8c95e5e1f40379c6f85d18aee962662eb22", + "scripts": { + "test": "ava && tsd", + "//test": "xo && ava && tsd" + }, + "_npmUser": { + "name": "sindresorhus", + "email": "sindresorhus@gmail.com" + }, + "repository": { + "url": "git+https://github.com/sindresorhus/yocto-queue.git", + "type": "git" + }, + "_npmVersion": "7.10.0", + "description": "Tiny queue data structure", + "directories": {}, + "_nodeVersion": "12.22.1", + "_hasShrinkwrap": false, + "devDependencies": { + "xo": "^0.44.0", + "ava": "^3.15.0", + "tsd": "^0.17.0" + }, + "_npmOperationalInternal": { + "tmp": "tmp/yocto-queue_1.0.0_1628812112939_0.12892570466581454", + "host": "s3://npm-registry-packages" + } + }, + "1.2.2": { + "name": "yocto-queue", + "version": "1.2.2", + "description": "Tiny queue data structure", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/yocto-queue.git" + }, + "funding": "https://github.com/sponsors/sindresorhus", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "https://sindresorhus.com" + }, + "type": "module", + "exports": "./index.js", + "types": "./index.d.ts", + "sideEffects": false, + "engines": { + "node": ">=12.20" + }, + "scripts": { + "//test": "xo && ava && tsd", + "test": "ava && tsd" + }, + "keywords": [ + "queue", + "data", + "structure", + "algorithm", + "queues", + "queuing", + "list", + "array", + "linkedlist", + "fifo", + "enqueue", + "dequeue", + "data-structure" + ], + "devDependencies": { + "ava": "^3.15.0", + "tsd": "^0.17.0", + "xo": "^0.44.0" + }, + "gitHead": "b07eac099753833b29d06c614149904445739776", + "_id": "yocto-queue@1.2.2", + "bugs": { + "url": "https://github.com/sindresorhus/yocto-queue/issues" + }, + "homepage": "https://github.com/sindresorhus/yocto-queue#readme", + "_nodeVersion": "20.19.5", + "_npmVersion": "11.6.1", + "dist": { + "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", + "shasum": "3e09c95d3f1aa89a58c114c99223edf639152c00", + "tarball": "{REGISTRY}/yocto-queue/-/yocto-queue-1.2.2.tgz", + "fileCount": 5, + "unpackedSize": 8117, + "signatures": [ + { + "keyid": "SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U", + "sig": "MEUCIQCJyFekh31SS2I6zmPz4Y8XejALOaWqv16iMDodrpOELAIgYwagvuPrCKN0MFov2xqQdEk0ChfY1xXzsPmH8lPF/qE=" + } + ] + }, + "_npmUser": { + "name": "sindresorhus", + "email": "sindresorhus@gmail.com" + }, + "directories": {}, + "maintainers": [ + { + "name": "sindresorhus", + "email": "sindresorhus@gmail.com" + } + ], + "_npmOperationalInternal": { + "host": "s3://npm-registry-packages-npm-production", + "tmp": "tmp/yocto-queue_1.2.2_1762842728792_0.5515543026142684" + }, + "_hasShrinkwrap": false + } + }, + "time": { + "created": "2020-11-24T05:59:48.704Z", + "modified": "2025-11-11T06:32:09.168Z", + "0.1.0": "2020-11-24T06:05:35.875Z", + "1.0.0": "2021-08-12T23:48:33.113Z", + "1.2.2": "2025-11-11T06:32:08.991Z" + }, + "bugs": { + "url": "https://github.com/sindresorhus/yocto-queue/issues" + }, + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "https://sindresorhus.com" + }, + "license": "MIT", + "homepage": "https://github.com/sindresorhus/yocto-queue#readme", + "keywords": [ + "queue", + "data", + "structure", + "algorithm", + "queues", + "queuing", + "list", + "array", + "linkedlist", + "fifo", + "enqueue", + "dequeue", + "data-structure" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/yocto-queue.git" + }, + "description": "Tiny queue data structure", + "maintainers": [ + { + "name": "sindresorhus", + "email": "sindresorhus@gmail.com" + } + ], + "readme": "# yocto-queue [![](https://badgen.net/bundlephobia/minzip/yocto-queue)](https://bundlephobia.com/result?p=yocto-queue)\n\n> Tiny queue data structure\n\nYou should use this package instead of an array if you do a lot of `Array#push()` and `Array#shift()` on large arrays, since `Array#shift()` has [linear time complexity](https://medium.com/@ariel.salem1989/an-easy-to-use-guide-to-big-o-time-complexity-5dcf4be8a444#:~:text=O(N)%E2%80%94Linear%20Time) *O(n)* while `Queue#dequeue()` has [constant time complexity](https://medium.com/@ariel.salem1989/an-easy-to-use-guide-to-big-o-time-complexity-5dcf4be8a444#:~:text=O(1)%20%E2%80%94%20Constant%20Time) *O(1)*. That makes a huge difference for large arrays.\n\n> A [queue](https://en.wikipedia.org/wiki/Queue_(abstract_data_type)) is an ordered list of elements where an element is inserted at the end of the queue and is removed from the front of the queue. A queue works based on the first-in, first-out ([FIFO](https://en.wikipedia.org/wiki/FIFO_(computing_and_electronics))) principle.\n\n## Install\n\n```sh\nnpm install yocto-queue\n```\n\n## Usage\n\n```js\nimport Queue from 'yocto-queue';\n\nconst queue = new Queue();\n\nqueue.enqueue('๐Ÿฆ„');\nqueue.enqueue('๐ŸŒˆ');\n\nconsole.log(queue.size);\n//=> 2\n\nconsole.log(...queue);\n//=> '๐Ÿฆ„ ๐ŸŒˆ'\n\nconsole.log(queue.dequeue());\n//=> '๐Ÿฆ„'\n\nconsole.log(queue.dequeue());\n//=> '๐ŸŒˆ'\n```\n\n## API\n\n### `queue = new Queue()`\n\nThe instance is an [`Iterable`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols), which means you can iterate over the queue front to back with a โ€œforโ€ฆofโ€ loop. Using the iterator will not remove the items from the queue. If you want that, use [`drain()`](#drain) instead.\n\nYou can also use spreading to convert the queue to an array. Don't do this unless you really need to though, since it's slow.\n\n#### `.enqueue(value)`\n\nAdd a value to the queue.\n\n#### `.dequeue()`\n\nRemove the next value in the queue.\n\nReturns the removed value or `undefined` if the queue is empty.\n\n#### `.peek()`\n\nGet the next value in the queue without removing it.\n\nReturns the value or `undefined` if the queue is empty.\n\n#### `.drain()`\n\nReturns an iterator that dequeues items as you consume it.\n\nThis allows you to empty the queue while processing its items.\n\nIf you want to not remove items as you consume it, use the `Queue` object as an iterator.\n\n#### `.clear()`\n\nClear the queue.\n\n#### `.size`\n\nThe size of the queue.\n\n## Related\n\n- [quick-lru](https://github.com/sindresorhus/quick-lru) - Simple โ€œLeast Recently Usedโ€ (LRU) cache\n", + "readmeFilename": "readme.md", + "users": { + "flumpus-dev": true + } + } +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/registry/pack.mjs b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/pack.mjs new file mode 100644 index 0000000000..030442b26e --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/pack.mjs @@ -0,0 +1,33 @@ +// Pack local CLI fixtures with Node.js builtins, without running package scripts. +import { createHash } from 'node:crypto'; +import { mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs'; +import path from 'node:path'; + +import { createTarball } from './archive.mjs'; + +const root = import.meta.dirname; +const destination = path.join(root, 'local'); +mkdirSync(path.join(destination, 'tarballs'), { recursive: true }); +const manifest = {}; +for (const directory of readdirSync(path.join(root, 'packages')).toSorted()) { + const source = path.join(root, 'packages', directory); + const pkg = JSON.parse(readFileSync(path.join(source, 'package.json'), 'utf8')); + const { name, version } = pkg; + const tarball = createTarball( + readdirSync(source) + .toSorted() + .map((filename) => ({ + name: `package/${filename}`, + contents: readFileSync(path.join(source, filename)), + mode: filename.endsWith('.cjs') ? 0o755 : 0o644, + })), + ); + const filename = `${name}-${version}.tgz`; + writeFileSync(path.join(destination, 'tarballs', filename), tarball); + pkg.dist = { + tarball: `{REGISTRY}/${name}/-/${filename}`, + integrity: `sha512-${createHash('sha512').update(tarball).digest('base64')}`, + }; + manifest[name] = { name, 'dist-tags': { latest: version }, versions: { [version]: pkg } }; +} +writeFileSync(path.join(destination, 'manifest.json'), JSON.stringify(manifest, null, 2) + '\n'); diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/registry/packages/vp-test-echo/cli.cjs b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/packages/vp-test-echo/cli.cjs new file mode 100644 index 0000000000..4b49ade231 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/packages/vp-test-echo/cli.cjs @@ -0,0 +1,6 @@ +#!/usr/bin/env node +const { readFileSync } = require('node:fs'); + +const args = process.argv.slice(2); +const message = args.length ? args.join(' ') : readFileSync(0, 'utf8').trimEnd(); +process.stdout.write(`${message}\n`); diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/registry/packages/vp-test-echo/package.json b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/packages/vp-test-echo/package.json new file mode 100644 index 0000000000..394cdf5e18 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/packages/vp-test-echo/package.json @@ -0,0 +1,10 @@ +{ + "name": "vp-test-echo", + "version": "1.0.0", + "private": true, + "license": "MIT", + "bin": { + "vp-test-echo": "cli.cjs", + "vp-test-echo-alias": "cli.cjs" + } +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/registry/packages/vp-test-upper/cli.cjs b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/packages/vp-test-upper/cli.cjs new file mode 100644 index 0000000000..9bc33daf38 --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/packages/vp-test-upper/cli.cjs @@ -0,0 +1,4 @@ +#!/usr/bin/env node +const { readFileSync } = require('node:fs'); + +process.stdout.write(`${readFileSync(0, 'utf8').trimEnd().toUpperCase()}\n`); diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/registry/packages/vp-test-upper/package.json b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/packages/vp-test-upper/package.json new file mode 100644 index 0000000000..3b808aa9df --- /dev/null +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/packages/vp-test-upper/package.json @@ -0,0 +1,9 @@ +{ + "name": "vp-test-upper", + "version": "1.0.0", + "private": true, + "license": "MIT", + "bin": { + "vp-test-upper": "cli.cjs" + } +} diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/registry/tarballs/is-number-6.0.0.tgz b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/tarballs/is-number-6.0.0.tgz new file mode 100644 index 0000000000..d52a7a7f86 Binary files /dev/null and b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/tarballs/is-number-6.0.0.tgz differ diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/registry/tarballs/is-number-7.0.0.tgz b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/tarballs/is-number-7.0.0.tgz new file mode 100644 index 0000000000..8b150f2a85 Binary files /dev/null and b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/tarballs/is-number-7.0.0.tgz differ diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/registry/tarballs/test-vite-plus-install-1.0.0.tgz b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/tarballs/test-vite-plus-install-1.0.0.tgz new file mode 100644 index 0000000000..2dbd760805 Binary files /dev/null and b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/tarballs/test-vite-plus-install-1.0.0.tgz differ diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/registry/tarballs/test-vite-plus-other-optional-1.0.0.tgz b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/tarballs/test-vite-plus-other-optional-1.0.0.tgz new file mode 100644 index 0000000000..1a6041e2d8 Binary files /dev/null and b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/tarballs/test-vite-plus-other-optional-1.0.0.tgz differ diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/registry/tarballs/test-vite-plus-other-optional-1.1.0.tgz b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/tarballs/test-vite-plus-other-optional-1.1.0.tgz new file mode 100644 index 0000000000..8171240623 Binary files /dev/null and b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/tarballs/test-vite-plus-other-optional-1.1.0.tgz differ diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/registry/tarballs/test-vite-plus-package-1.0.0.tgz b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/tarballs/test-vite-plus-package-1.0.0.tgz new file mode 100644 index 0000000000..a3b1d7cabd Binary files /dev/null and b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/tarballs/test-vite-plus-package-1.0.0.tgz differ diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/registry/tarballs/test-vite-plus-package-optional-1.0.0.tgz b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/tarballs/test-vite-plus-package-optional-1.0.0.tgz new file mode 100644 index 0000000000..149c27ab32 Binary files /dev/null and b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/tarballs/test-vite-plus-package-optional-1.0.0.tgz differ diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/registry/tarballs/test-vite-plus-top-package-1.0.0.tgz b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/tarballs/test-vite-plus-top-package-1.0.0.tgz new file mode 100644 index 0000000000..a8609e0542 Binary files /dev/null and b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/tarballs/test-vite-plus-top-package-1.0.0.tgz differ diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/registry/tarballs/test-vite-plus-top-package-1.1.0.tgz b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/tarballs/test-vite-plus-top-package-1.1.0.tgz new file mode 100644 index 0000000000..2180ae57de Binary files /dev/null and b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/tarballs/test-vite-plus-top-package-1.1.0.tgz differ diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/registry/tarballs/testnpm2-1.0.0.tgz b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/tarballs/testnpm2-1.0.0.tgz new file mode 100644 index 0000000000..df7bd3dd87 Binary files /dev/null and b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/tarballs/testnpm2-1.0.0.tgz differ diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/registry/tarballs/testnpm2-1.0.1.tgz b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/tarballs/testnpm2-1.0.1.tgz new file mode 100644 index 0000000000..aa21c7a711 Binary files /dev/null and b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/tarballs/testnpm2-1.0.1.tgz differ diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/registry/tarballs/yocto-queue-0.1.0.tgz b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/tarballs/yocto-queue-0.1.0.tgz new file mode 100644 index 0000000000..8ede4a59a4 Binary files /dev/null and b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/tarballs/yocto-queue-0.1.0.tgz differ diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/registry/tarballs/yocto-queue-1.0.0.tgz b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/tarballs/yocto-queue-1.0.0.tgz new file mode 100644 index 0000000000..635275e617 Binary files /dev/null and b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/tarballs/yocto-queue-1.0.0.tgz differ diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/registry/tarballs/yocto-queue-1.2.2.tgz b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/tarballs/yocto-queue-1.2.2.tgz new file mode 100644 index 0000000000..9d8075e044 Binary files /dev/null and b/crates/vp_cli_snapshots/tests/cli_snapshots/registry/tarballs/yocto-queue-1.2.2.tgz differ diff --git a/crates/vp_cli_snapshots/tests/cli_snapshots/report.rs b/crates/vp_cli_snapshots/tests/cli_snapshots/report.rs index 377d565eec..61c6619cfa 100644 --- a/crates/vp_cli_snapshots/tests/cli_snapshots/report.rs +++ b/crates/vp_cli_snapshots/tests/cli_snapshots/report.rs @@ -30,6 +30,17 @@ impl Report { } } + pub fn artifact_path(&self, name: &str) -> Result, String> { + self.directory + .as_ref() + .map(|directory| { + std::fs::create_dir_all(directory) + .map_err(|error| format!("failed to create artifact directory: {error}"))?; + Ok(directory.join(name)) + }) + .transpose() + } + pub fn phase(&self, name: impl Into) -> Phase<'_> { Phase { report: self, name: name.into(), started: Instant::now() } } diff --git a/crates/vp_cli_snapshots/tests/report_unit.rs b/crates/vp_cli_snapshots/tests/report_unit.rs index 8d95c1192b..63f4cefad8 100644 --- a/crates/vp_cli_snapshots/tests/report_unit.rs +++ b/crates/vp_cli_snapshots/tests/report_unit.rs @@ -11,6 +11,10 @@ fn successful_cases_only_write_timings() { { let _phase = report.phase("command"); } + assert_eq!( + report.artifact_path("registry.jsonl").unwrap(), + Some(tmp.path().join("registry.jsonl")) + ); report.capture("vpt print", "not a failure"); report.actual("# snapshot\n"); report.finish(None, None).unwrap(); diff --git a/packages/tools/README.md b/packages/tools/README.md index befe427be0..e72a42477b 100644 --- a/packages/tools/README.md +++ b/packages/tools/README.md @@ -23,4 +23,5 @@ See the [contributing guide](../../CONTRIBUTING.md#test-vp-migrate--vp-create-th - Served versions use an old publish time so package-manager minimum-release-age checks allow local builds immediately. - Wrapped commands use temporary Yarn Berry and bun caches to avoid reusing stale local builds with the same package version. -- The server also backs [PTY snapshot cases](../../crates/vp_cli_snapshots/tests/cli_snapshots/README.md) with `local-registry = true` and [ecosystem e2e tests](../../ecosystem-ci/patch-project.ts). +- The server also backs [PTY snapshot cases](../../crates/vp_cli_snapshots/tests/cli_snapshots/README.md) and [ecosystem e2e tests](../../ecosystem-ci/patch-project.ts). Snapshots use recorded test packages and a run-scoped response cache. `local-registry = true` additionally serves the packed checkout; ecosystem and standalone runs retain upstream version merging. +- Refresh the small snapshot registry fixtures explicitly with `node packages/tools/src/record-snapshot-registry.ts`. See the [fixture documentation](../../crates/vp_cli_snapshots/tests/cli_snapshots/registry/README.md). diff --git a/packages/tools/src/__tests__/local-npm-registry.spec.ts b/packages/tools/src/__tests__/local-npm-registry.spec.ts index dd03810f15..bc4f6b98ba 100644 --- a/packages/tools/src/__tests__/local-npm-registry.spec.ts +++ b/packages/tools/src/__tests__/local-npm-registry.spec.ts @@ -1,10 +1,87 @@ -import { execFileSync } from 'node:child_process'; -import { existsSync } from 'node:fs'; +import { execFileSync, spawnSync } from 'node:child_process'; +import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'; +import { tmpdir } from 'node:os'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { expect, it } from 'vitest'; +it.each([false, true])( + 'shares a registry with snapshot processes without changing their environment (packed: %s)', + (packed) => { + const script = fileURLToPath(new URL('../local-npm-registry.ts', import.meta.url)); + const fixtures = fileURLToPath( + new URL('../../../../crates/vp_cli_snapshots/tests/cli_snapshots/registry', import.meta.url), + ); + const root = mkdtempSync(path.join(tmpdir(), 'vp-registry-runner-test-')); + try { + writeFileSync(path.join(root, '.npmrc'), 'registry=https://127.0.0.1:9\n'); + // The runner prepares packed archives after the wrapper starts. A request + // for the packed registry must load them before checking local versions. + const probe = ` + const { cpSync, mkdirSync } = require('node:fs'); + const packed = ${packed}; + const env = JSON.parse(process.env[packed + ? 'VP_SNAP_PACKED_REGISTRY_ENV' : 'VP_SNAP_SHARED_REGISTRY_ENV']); + if (packed) { + mkdirSync('packages'); + cpSync(${JSON.stringify(path.join(fixtures, 'tarballs/is-number-7.0.0.tgz'))}, + 'packages/is-number-7.0.0.tgz'); + } + (async () => { + const registry = env.NPM_CONFIG_REGISTRY; + const name = packed ? 'is-number' : 'vp-test-echo'; + const responses = await Promise.all([1, 2].map(async () => { + const metadata = await (await fetch(registry + '/' + name + '/latest')).json(); + const bytes = await (await fetch(metadata.dist.tarball)).arrayBuffer(); + if (!bytes.byteLength) throw new Error('empty tarball'); + return metadata.version; + })); + console.log(JSON.stringify({ env, responses, + registry: process.env.NPM_CONFIG_REGISTRY, cache: process.env.YARN_GLOBAL_FOLDER })); + process.exitCode = 7; + })().catch(error => { console.error(error); process.exitCode = 1; }); + `; + const result = spawnSync( + process.execPath, + [ + script, + '--snapshot-runner', + '--live', + '--snapshot-fixtures', + fixtures, + ...(packed ? ['--packages-dir', path.join(root, 'packages')] : []), + '--', + process.execPath, + '-e', + probe, + ], + { + cwd: root, + encoding: 'utf8', + timeout: 10_000, + env: { + ...process.env, + SNAP_LOCAL_VP_PACKAGES_DIR: '', + NPM_CONFIG_REGISTRY: 'https://127.0.0.1:9', + YARN_GLOBAL_FOLDER: 'unchanged', + }, + }, + ); + expect(result.stderr).toBe(''); + expect(result.status).toBe(7); + const output = JSON.parse(result.stdout); + expect(output.responses).toEqual(Array(2).fill(packed ? '7.0.0' : '1.0.0')); + expect(output.registry).toBe('https://127.0.0.1:9'); + expect(output.cache).toBe('unchanged'); + expect(existsSync(output.env.YARN_GLOBAL_FOLDER)).toBe(false); + expect(existsSync(output.env.BUN_INSTALL_CACHE_DIR)).toBe(false); + } finally { + rmSync(root, { recursive: true, force: true }); + } + }, +); + it('isolates and cleans the platform temp directory used by bunx on each registry run', () => { const script = fileURLToPath(new URL('../local-npm-registry.ts', import.meta.url)); const probe = ` @@ -33,3 +110,96 @@ it('isolates and cleans the platform temp directory used by bunx on each registr caches.add(result.cache); } }); + +it.each([false, true])( + 'serves fixture metadata and tarballs without upstream requests (live: %s)', + (live) => { + const script = fileURLToPath(new URL('../local-npm-registry.ts', import.meta.url)); + const fixtures = fileURLToPath( + new URL('../../../../crates/vp_cli_snapshots/tests/cli_snapshots/registry', import.meta.url), + ); + const root = mkdtempSync(path.join(tmpdir(), 'vp-registry-fixtures-test-')); + const packages = live + ? ['vp-test-echo', 'vp-test-upper'] + : ['testnpm2', 'vp-test-echo', 'vp-test-upper', 'is-number', 'yocto-queue']; + try { + // An accidental fallback fails immediately instead of reaching npm. + writeFileSync(path.join(root, '.npmrc'), 'registry=https://127.0.0.1:9\n'); + writeFileSync( + path.join(root, 'mock-manifest.json'), + JSON.stringify({ + 'testnpm2/fixture-tag': { name: 'fixture override', version: '9.0.0' }, + }), + ); + const log = path.join(root, 'requests.jsonl'); + const probe = ` + const assert = require('node:assert/strict'); + const { createHash } = require('node:crypto'); + (async () => { + const registry = process.env.NPM_CONFIG_REGISTRY; + const selected = { + 'is-number': ['6.0.0', '7.0.0'], + 'yocto-queue': ['0.1.0', '1.0.0', '1.2.2'], + }; + for (const name of ${JSON.stringify(packages)}) { + const metadata = await (await fetch(registry + '/' + name)).json(); + const version = await (await fetch(registry + '/' + name + '/latest')).json(); + if (version.version !== metadata['dist-tags'].latest) throw new Error('version mismatch'); + if (selected[name]) assert.deepEqual(Object.keys(metadata.versions), selected[name]); + for (const recorded of Object.values(metadata.versions)) { + if (!recorded.dist.tarball.startsWith(registry + '/')) throw new Error('remote tarball'); + const bytes = Buffer.from(await (await fetch(recorded.dist.tarball)).arrayBuffer()); + const integrity = 'sha512-' + createHash('sha512').update(bytes).digest('base64'); + if (integrity !== recorded.dist.integrity) throw new Error('tarball integrity mismatch'); + } + console.log(version.name + '@' + version.version); + } + const override = await (await fetch(registry + '/testnpm2/fixture-tag')).json(); + if (override.name !== 'fixture override') throw new Error('fixture did not win'); + })().catch(error => { console.error(error); process.exitCode = 1; }); + `; + const output = execFileSync( + process.execPath, + [ + script, + ...(live ? ['--live'] : []), + '--snapshot-cache', + path.join(root, 'cache'), + '--snapshot-fixtures', + fixtures, + '--request-log', + log, + '--', + process.execPath, + '-e', + probe, + ], + { + cwd: root, + encoding: 'utf8', + timeout: 10_000, + env: { ...process.env, SNAP_LOCAL_VP_PACKAGES_DIR: '' }, + }, + ); + expect(output.trim().split(/\r?\n/)).toEqual( + live + ? ['vp-test-echo@1.0.0', 'vp-test-upper@1.0.0'] + : [ + 'testnpm2@1.0.1', + 'vp-test-echo@1.0.0', + 'vp-test-upper@1.0.0', + 'is-number@7.0.0', + 'yocto-queue@1.2.2', + ], + ); + expect( + readFileSync(log, 'utf8') + .trim() + .split('\n') + .map((line) => JSON.parse(line).event), + ).toEqual(Array(live ? 7 : 20).fill('fixture')); + } finally { + rmSync(root, { recursive: true, force: true }); + } + }, +); diff --git a/packages/tools/src/__tests__/record-snapshot-registry.spec.ts b/packages/tools/src/__tests__/record-snapshot-registry.spec.ts new file mode 100644 index 0000000000..33dc655d1d --- /dev/null +++ b/packages/tools/src/__tests__/record-snapshot-registry.spec.ts @@ -0,0 +1,93 @@ +import { execFileSync } from 'node:child_process'; +import { + cpSync, + existsSync, + mkdirSync, + mkdtempSync, + readFileSync, + rmSync, + writeFileSync, +} from 'node:fs'; +import { tmpdir } from 'node:os'; +import path from 'node:path'; +import { fileURLToPath, pathToFileURL } from 'node:url'; + +import { expect, it } from 'vitest'; + +it('refreshes selected versions, removes obsolete tarballs, and preserves other packages', () => { + // Exercise URL escaping as well as Windows drive-letter paths. + const root = mkdtempSync(path.join(tmpdir(), 'vp-record-registry-test-#')); + try { + const directory = path.join(root, 'crates/vp_cli_snapshots/tests/cli_snapshots/registry'); + cpSync( + fileURLToPath( + new URL( + '../../../../crates/vp_cli_snapshots/tests/cli_snapshots/registry', + import.meta.url, + ), + ), + directory, + { recursive: true }, + ); + const script = path.join(root, 'packages/tools/src/record-snapshot-registry.ts'); + mkdirSync(path.dirname(script), { recursive: true }); + cpSync(fileURLToPath(new URL('../record-snapshot-registry.ts', import.meta.url)), script); + + const manifestPath = path.join(directory, 'manifest.json'); + const original = JSON.parse(readFileSync(manifestPath, 'utf8')); + const incoming = structuredClone(original); + for (const [name, version] of [ + ['is-number', '0.1.0'], + ['yocto-queue', '0.0.0'], + ]) { + const metadata = incoming[name]; + const entry = structuredClone(metadata.versions[metadata['dist-tags'].latest]); + const basename = `${name}-${version}.tgz`; + cpSync( + path.join(directory, 'tarballs', path.posix.basename(entry.dist.tarball)), + path.join(directory, 'tarballs', basename), + ); + entry.version = version; + entry.dist.tarball = `{REGISTRY}/${name}/-/${basename}`; + metadata.versions[version] = entry; + metadata.time[version] = metadata.time.created; + metadata['dist-tags'].obsolete = version; + } + writeFileSync(manifestPath, JSON.stringify(incoming)); + writeFileSync(path.join(root, 'incoming.json'), JSON.stringify(incoming)); + const mock = path.join(root, 'mock-fetch.mjs'); + writeFileSync( + mock, + `import { appendFileSync, readFileSync } from 'node:fs'; + import path from 'node:path'; + const metadata = JSON.parse(readFileSync('incoming.json', 'utf8').replaceAll('{REGISTRY}', 'https://registry.npmjs.org')); + globalThis.fetch = async (url) => { + appendFileSync('requests.jsonl', JSON.stringify(url) + '\\n'); + const pathname = new URL(url).pathname.slice(1); + if (['is-number', 'yocto-queue'].includes(pathname)) return Response.json(metadata[pathname]); + const basename = path.posix.basename(pathname); + if (!['is-number-6.0.0.tgz', 'is-number-7.0.0.tgz', 'yocto-queue-0.1.0.tgz', + 'yocto-queue-1.0.0.tgz', 'yocto-queue-1.2.2.tgz'].includes(basename)) { + throw new Error('Unexpected download: ' + url); + } + return new Response(readFileSync(path.join(${JSON.stringify(directory)}, 'tarballs', basename))); + };`, + ); + execFileSync( + process.execPath, + ['--import', pathToFileURL(mock).href, script, 'is-number', 'yocto-queue'], + { + cwd: root, + timeout: 10_000, + }, + ); + expect(JSON.parse(readFileSync(manifestPath, 'utf8'))).toEqual(original); + expect(existsSync(path.join(directory, 'tarballs/is-number-0.1.0.tgz'))).toBe(false); + expect(existsSync(path.join(directory, 'tarballs/yocto-queue-0.0.0.tgz'))).toBe(false); + expect(readFileSync(path.join(root, 'requests.jsonl'), 'utf8').trim().split('\n')).toHaveLength( + 7, + ); + } finally { + rmSync(root, { recursive: true, force: true }); + } +}); diff --git a/packages/tools/src/__tests__/registry-cache.spec.ts b/packages/tools/src/__tests__/registry-cache.spec.ts new file mode 100644 index 0000000000..b2cbe6535c --- /dev/null +++ b/packages/tools/src/__tests__/registry-cache.spec.ts @@ -0,0 +1,198 @@ +import { mkdtemp, rm } from 'node:fs/promises'; +import { createServer, type Server, type RequestListener } from 'node:http'; +import { tmpdir } from 'node:os'; +import path from 'node:path'; +import { gzipSync } from 'node:zlib'; + +import { afterEach, expect, it } from 'vitest'; + +import { RegistryCache, rewriteTarballs, upstreamUrl } from '../registry-cache.ts'; + +const directories: string[] = []; +const servers: Server[] = []; + +afterEach(async () => { + for (const server of servers.splice(0)) { + server.closeAllConnections(); + await new Promise((resolve) => server.close(() => resolve())); + } + await Promise.all(directories.splice(0).map((dir) => rm(dir, { recursive: true, force: true }))); +}); + +async function directory() { + const dir = await mkdtemp(path.join(tmpdir(), 'vp-registry-cache-test-')); + directories.push(dir); + return dir; +} + +async function upstream(handler: RequestListener) { + const server = createServer(handler); + servers.push(server); + await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve)); + const address = server.address(); + if (!address || typeof address === 'string') { + throw new Error('Missing test server address'); + } + return `http://127.0.0.1:${address.port}`; +} + +it('coalesces requests across registries and preserves binary response bytes', async () => { + let requests = 0; + const bytes = Buffer.from([0, 255, 10, 128, 31, 139]); + const url = await upstream((_req, res) => { + requests++; + res.end(bytes); + }); + const dir = await directory(); + const registries = [new RegistryCache(dir), new RegistryCache(dir)]; + const responses = await Promise.all( + Array.from({ length: 12 }, (_, i) => registries[i % 2].get(url, 'application/octet-stream')), + ); + expect(requests).toBe(1); + for (const response of responses) { + expect(response.body).toEqual(bytes); + } + expect((await new RegistryCache(dir).get(url, 'application/octet-stream')).body).toEqual(bytes); + expect(requests).toBe(1); +}); + +it('separates metadata representations and upstream registries', async () => { + let requests = 0; + const handler: RequestListener = (req, res) => { + requests++; + res.end(req.headers.accept); + }; + const first = await upstream(handler); + const second = await upstream(handler); + const cache = new RegistryCache(await directory()); + expect((await cache.get(first, 'application/json')).body.toString()).toBe('application/json'); + expect((await cache.get(first, 'application/vnd.npm.install-v1+json')).body.toString()).toBe( + 'application/vnd.npm.install-v1+json', + ); + await cache.get(second, 'application/json'); + expect(requests).toBe(3); +}); + +it('does not retain failed responses or partial downloads', async () => { + let requests = 0; + const url = await upstream((_req, res) => { + requests++; + if (requests === 1) { + res.writeHead(503); + res.end('retry'); + } else if (requests === 2) { + res.writeHead(200, { 'content-length': 100 }); + res.write('partial', () => res.destroy()); + } else { + res.end('complete'); + } + }); + const cache = new RegistryCache(await directory()); + expect((await cache.get(url, 'application/json')).status).toBe(503); + await expect(cache.get(url, 'application/json')).rejects.toThrow(); + expect((await cache.get(url, 'application/json')).body.toString()).toBe('complete'); + await cache.get(url, 'application/json'); + expect(requests).toBe(3); +}); + +it('can bypass response storage for a live-network benchmark', async () => { + let requests = 0; + const url = await upstream((_req, res) => res.end(String(++requests))); + const cache = new RegistryCache(await directory(), false); + expect((await cache.get(url, 'application/json')).body.toString()).toBe('1'); + expect((await cache.get(url, 'application/json')).body.toString()).toBe('2'); +}); + +it('bounds an unfinished download and releases its lock for another registry', async () => { + let complete = false; + const url = await upstream((_req, res) => { + if (complete) { + res.end('complete'); + return; + } + const timer = setInterval(() => res.write('partial'), 5); + res.on('close', () => clearInterval(timer)); + }); + const dir = await directory(); + const cache = new RegistryCache(dir, true, undefined, 100); + await expect(cache.get(url, 'application/octet-stream')).rejects.toMatchObject({ + name: 'AbortError', + }); + complete = true; + const next = new RegistryCache(dir); + expect((await next.get(url, 'application/octet-stream')).body.toString()).toBe('complete'); +}); + +it.each(['headers', 'body'])( + 'releases a download stalled before %s for a client retry', + async (stage) => { + let complete = false; + const url = await upstream((_req, res) => { + if (complete) { + res.end('complete'); + } else if (stage === 'body') { + res.writeHead(200); + res.flushHeaders(); + } + }); + const dir = await directory(); + const cache = new RegistryCache(dir, true, undefined, 60_000, 100); + await expect(cache.get(url, 'application/octet-stream')).rejects.toMatchObject({ + name: 'AbortError', + cause: { message: 'Registry request stalled' }, + }); + complete = true; + const next = new RegistryCache(dir); + expect((await next.get(url, 'application/octet-stream')).body.toString()).toBe('complete'); + }, +); + +it('routes tarballs through the case registry without changing unrelated metadata', () => { + const registry = 'http://127.0.0.1:1234'; + const cdn = 'https://cdn.example.test/package.tgz'; + const result = rewriteTarballs( + { + homepage: cdn, + versions: { + '1.0.0': { + dist: { + tarball: 'https://registry.npmjs.org/example/-/example-1.0.0.tgz', + integrity: 'test', + }, + }, + '2.0.0': { dist: { tarball: cdn } }, + '3.0.0': { dist: { tarball: `${registry}/local.tgz` } }, + }, + }, + registry, + ) as { + homepage: string; + versions: Record; + }; + expect(result.homepage).toBe(cdn); + expect(result.versions['1.0.0'].dist).toEqual({ + tarball: `${registry}/example/-/example-1.0.0.tgz`, + integrity: 'test', + }); + expect( + upstreamUrl( + new URL(result.versions['2.0.0'].dist.tarball).pathname, + 'https://registry.npmjs.org', + ), + ).toBe(cdn); + expect(result.versions['3.0.0'].dist.tarball).toBe(`${registry}/local.tgz`); +}); + +it('decodes compressed metadata before storing it for other package managers', async () => { + const metadata = JSON.stringify({ name: 'example', versions: {} }); + let requests = 0; + const url = await upstream((_req, res) => { + requests++; + res.writeHead(200, { 'content-type': 'application/json', 'content-encoding': 'gzip' }); + res.end(gzipSync(metadata)); + }); + const cache = new RegistryCache(await directory()); + expect((await cache.get(url, 'application/json')).body.toString()).toBe(metadata); + expect((await cache.get(url, 'application/json')).body.toString()).toBe(metadata); + expect(requests).toBe(1); +}); diff --git a/packages/tools/src/local-npm-registry.ts b/packages/tools/src/local-npm-registry.ts index acce4c4eb7..52c16b6310 100644 --- a/packages/tools/src/local-npm-registry.ts +++ b/packages/tools/src/local-npm-registry.ts @@ -35,6 +35,12 @@ // (no server); the snapshot runner packs once per run // --serve keep the server running and print the registry URL // and env exports instead of wrapping a command +// --snapshot-cache share successful upstream GETs for one snapshot run +// --snapshot-fixtures serve recorded manifest.json and tarballs/ data +// --snapshot-runner wrap a test runner with a shared registry; the runner +// still gives each case its own package-manager caches +// --request-log write registry request events as JSON lines +// --live bypass shared fixtures and response storage for measurement // --ps list running local registry processes // --kill kill them all and remove their leftover temp caches @@ -56,6 +62,7 @@ import { gunzipSync } from 'node:zlib'; import { exitCodeFromClose } from './exit-code.ts'; import { packLocalVitePlusPackages } from './pack-local-vite-plus.ts'; +import { RegistryCache, rewriteTarballs, upstreamUrl } from './registry-cache.ts'; interface PackageManifest { name: string; @@ -72,6 +79,11 @@ let packagesDir = process.env.SNAP_LOCAL_VP_PACKAGES_DIR; let pack = false; let packTo: string | undefined; let serve = false; +let snapshotRunner = false; +let snapshotCacheDir: string | undefined; +let snapshotFixtures: string | undefined; +let requestLog: string | undefined; +let live = false; let listProcesses = false; let killProcesses = false; for (let i = 0; i < flags.length; i++) { @@ -81,8 +93,18 @@ for (let i = 0; i < flags.length; i++) { pack = true; } else if (flags[i] === '--pack-to' && flags[i + 1]) { packTo = flags[++i]; + } else if (flags[i] === '--snapshot-cache' && flags[i + 1]) { + snapshotCacheDir = flags[++i]; + } else if (flags[i] === '--snapshot-fixtures' && flags[i + 1]) { + snapshotFixtures = flags[++i]; + } else if (flags[i] === '--request-log' && flags[i + 1]) { + requestLog = flags[++i]; + } else if (flags[i] === '--live') { + live = true; } else if (flags[i] === '--serve') { serve = true; + } else if (flags[i] === '--snapshot-runner') { + snapshotRunner = true; } else if (flags[i] === '--ps') { listProcesses = true; } else if (flags[i] === '--kill') { @@ -93,6 +115,19 @@ for (let i = 0; i < flags.length; i++) { } } +if (snapshotRunner) { + if (serve || packTo || command.length === 0) { + console.error('local-npm-registry: --snapshot-runner requires a command after --'); + process.exit(2); + } + if (['live', 'direct'].includes(process.env.VP_SNAP_REGISTRY_MODE ?? '')) { + live = true; + } +} +if (requestLog) { + mkdirSync(path.dirname(requestLog), { recursive: true }); +} + // `--ps` / `--kill`: troubleshoot registry processes left behind by // interrupted runs (the wrapper and --serve clean up after themselves, but a // hard kill skips the handlers). A registry process is a `node` executable @@ -185,10 +220,28 @@ if (pack && !packagesDir) { packagesDir = packedDir; } -const manifest: Record = existsSync('./mock-manifest.json') +const fixtureManifest: Record = existsSync('./mock-manifest.json') ? (JSON.parse(readFileSync('./mock-manifest.json', 'utf-8')) as Record) : {}; +const sharedManifest: Record = {}; +if (snapshotFixtures) { + // Authored packages have no public-registry counterpart. Keep them available + // in comparison modes; only published recordings are disabled by --live. + for (const filename of live + ? ['local/manifest.json'] + : ['manifest.json', 'local/manifest.json']) { + Object.assign( + sharedManifest, + JSON.parse(readFileSync(path.join(snapshotFixtures, filename), 'utf8')), + ); + } +} +const manifest = { ...sharedManifest, ...fixtureManifest }; +const snapshotCache = snapshotCacheDir + ? new RegistryCache(snapshotCacheDir, !live, requestLog) + : undefined; + // Proxy through the configured registry (the project's `.npmrc` in cwd, then // the user's `~/.npmrc`, e.g. a local mirror) when there is one, so runs stay // as fast as direct installs and projects that rely on a custom registry keep @@ -267,7 +320,11 @@ const localPackuments = new Map(); // package name -> local-o const LOCAL_PACKAGE_TIME = '2020-01-01T00:00:00.000Z'; const VITE_PLUS_PLATFORM_PACKAGE_PREFIX = '@voidzero-dev/vite-plus-cli-'; const SLSA_PROVENANCE_V1 = 'https://slsa.dev/provenance/v1'; -if (packagesDir) { +let localPackagesLoaded = false; +function loadLocalPackages(): void { + if (!packagesDir || localPackagesLoaded) { + return; + } for (const basename of readdirSync(packagesDir)) { if (!basename.endsWith('.tgz')) { continue; @@ -307,12 +364,21 @@ if (packagesDir) { }, }); } + localPackagesLoaded = true; +} +// A snapshot wrapper starts before nextest workers prepare the archives. The +// runner validates them under its package lock before making any requests. +if (!snapshotRunner) { + loadLocalPackages(); } // Serialize a manifest/packument with its `{REGISTRY}` placeholders (only our // local tarball URLs carry them) pointed at the live server address. function serializeForRegistry(value: unknown, registry: string): string { - return JSON.stringify(value).replaceAll('{REGISTRY}', registry); + const serialized = JSON.stringify(value).replaceAll('{REGISTRY}', registry); + return snapshotCache + ? JSON.stringify(rewriteTarballs(JSON.parse(serialized), registry)) + : serialized; } function fetchUpstreamPackument(name: string): Promise { @@ -360,7 +426,14 @@ async function resolveLocalPackument(name: string): Promise { if (cached) { return cached; } - const upstream = await fetchUpstreamPackument(name); + const upstream = snapshotCache + ? await snapshotCache + .get(`${UPSTREAM_REGISTRY}/${name.replace('/', '%2F')}`, 'application/json') + .then((response) => + response.status === 200 ? (JSON.parse(response.body.toString()) as Packument) : null, + ) + .catch(() => null) + : await fetchUpstreamPackument(name); const localVersion = local['dist-tags'].latest; const merged: Packument = upstream ? { @@ -384,12 +457,12 @@ function resolveLocalVersion(key: string): unknown { } const name = key.slice(0, separator); const specifier = key.slice(separator + 1); - const packument = localPackuments.get(name); + const packument = localPackuments.get(name) ?? (manifest[name] as Packument | undefined); if (!packument) { return undefined; } - const version = packument['dist-tags'][specifier] ?? specifier; - return packument.versions[version]; + const version = packument['dist-tags']?.[specifier] ?? specifier; + return packument.versions?.[version]; } // Stream the upstream response byte-for-byte. Unlike `fetch`, `https` does not @@ -425,6 +498,7 @@ function proxyToUpstream(req: IncomingMessage, res: ServerResponse): void { // for body-less methods; a redirect on anything else is forwarded to the // client as-is (its `location` header survives below). const method = req.method ?? 'GET'; + snapshotCache?.record('request', `${UPSTREAM_REGISTRY}${req.url ?? '/'}`); const bodyless = method === 'GET' || method === 'HEAD'; const fetchUrl = (url: string, redirectsLeft: number) => { // Tarballs: `accept-encoding: identity` keeps mirrors/CDNs from wrapping @@ -440,7 +514,8 @@ function proxyToUpstream(req: IncomingMessage, res: ServerResponse): void { ? 'identity' : (req.headers['accept-encoding'] ?? 'identity'), }; - copyHeaders(req.headers, headers, ['content-type', 'content-length']); + copyHeaders(req.headers, headers, ['content-type', 'content-length', 'content-encoding']); + snapshotCache?.record('upstream', url); const upstreamRequest = httpsRequest( url, { method, headers, agent: upstreamAgent }, @@ -490,46 +565,93 @@ function proxyToUpstream(req: IncomingMessage, res: ServerResponse): void { fetchUrl(`${UPSTREAM_REGISTRY}${req.url ?? '/'}`, 5); } +let requests = 0; const server = createServer(async (req, res) => { - const key = decodeURIComponent(req.url ?? '/').replace(/^\/+/, ''); - if (localPackuments.has(key) || Object.hasOwn(manifest, key)) { - const address = server.address(); - const registry = - address && typeof address !== 'string' ? `http://127.0.0.1:${address.port}` : ''; - const packument = localPackuments.has(key) ? await resolveLocalPackument(key) : manifest[key]; - res.writeHead(200, { 'content-type': 'application/json' }); - res.end(serializeForRegistry(packument, registry)); - return; - } - const localVersion = resolveLocalVersion(key); - if (localVersion) { - const address = server.address(); - const registry = - address && typeof address !== 'string' ? `http://127.0.0.1:${address.port}` : ''; + // Tests of the CLI's own metadata cache must keep the registry URL fixed: + // changing the origin selects a different product cache entry. + if (req.url === '/-/vp-test/requests') { res.writeHead(200, { 'content-type': 'application/json' }); - res.end(serializeForRegistry(localVersion, registry)); + res.end(JSON.stringify({ requests })); return; } - const tarMatch = key.match(/\/-\/([^/]+\.tgz)$/); - if (tarMatch) { - try { - // Async read: local tarballs run tens of MB, and a synchronous read - // would stall the proxied requests of the same in-flight install. - const bytes = await readFile( - localTarballs.get(tarMatch[1]) ?? path.resolve('./tarballs', tarMatch[1]), + requests++; + try { + loadLocalPackages(); + const key = decodeURIComponent(req.url ?? '/').replace(/^\/+/, ''); + if (localPackuments.has(key) || Object.hasOwn(manifest, key)) { + snapshotCache?.record('fixture', key); + const address = server.address(); + const registry = + address && typeof address !== 'string' ? `http://127.0.0.1:${address.port}` : ''; + const packument = localPackuments.has(key) ? await resolveLocalPackument(key) : manifest[key]; + res.writeHead(200, { 'content-type': 'application/json' }); + res.end(serializeForRegistry(packument, registry)); + return; + } + const localVersion = resolveLocalVersion(key); + if (localVersion) { + snapshotCache?.record('fixture', key); + const address = server.address(); + const registry = + address && typeof address !== 'string' ? `http://127.0.0.1:${address.port}` : ''; + res.writeHead(200, { 'content-type': 'application/json' }); + res.end(serializeForRegistry(localVersion, registry)); + return; + } + const tarMatch = key.match(/\/-\/([^/]+\.tgz)$/); + if (tarMatch) { + try { + // Async read: local tarballs run tens of MB, and a synchronous read + // would stall the proxied requests of the same in-flight install. + const candidates = [ + localTarballs.get(tarMatch[1]), + path.resolve('./tarballs', tarMatch[1]), + ]; + if (snapshotFixtures) { + candidates.push(path.join(snapshotFixtures, 'local/tarballs', tarMatch[1])); + if (!live) { + candidates.push(path.join(snapshotFixtures, 'tarballs', tarMatch[1])); + } + } + const localPath = candidates.find((candidate) => candidate && existsSync(candidate)); + if (localPath) { + const bytes = await readFile(localPath); + snapshotCache?.record('fixture', key); + res.writeHead(200, { 'content-type': 'application/octet-stream' }); + res.end(bytes); + return; + } + } catch { + // fall through to proxy + } + } + // Proxy anything we don't mock (pnpm/latest, tarball downloads for real + // packages, etc.) to the upstream registry. Keeps the local packages in + // charge while letting everything else (package-manager download, real + // dependencies) resolve normally. + if (snapshotCache && (req.method === 'GET' || req.method === 'HEAD')) { + const response = await snapshotCache.get( + upstreamUrl(req.url ?? '/', UPSTREAM_REGISTRY), + req.headers.accept ?? 'application/json', ); - res.writeHead(200, { 'content-type': 'application/octet-stream' }); - res.end(bytes); + const address = server.address(); + const registry = + address && typeof address !== 'string' ? `http://127.0.0.1:${address.port}` : ''; + const body = + response.contentType.includes('json') && response.status === 200 + ? serializeForRegistry(JSON.parse(response.body.toString()), registry) + : response.body; + res.writeHead(response.status, { 'content-type': response.contentType }); + res.end(req.method === 'HEAD' ? undefined : body); return; - } catch { - // fall through to proxy } + proxyToUpstream(req, res); + } catch (error) { + if (!res.headersSent) { + res.writeHead(502); + } + res.end(`registry error: ${(error as Error).message}`); } - // Proxy anything we don't mock (pnpm/latest, tarball downloads for real - // packages, etc.) to the upstream registry. Keeps the local packages in - // charge while letting everything else (package-manager download, real - // dependencies) resolve normally. - proxyToUpstream(req, res); }); // Registry env for every package manager, each of which reads its own @@ -622,13 +744,27 @@ server.listen(0, '127.0.0.1', () => { } const [cmd, ...cmdArgs] = command as [string, ...string[]]; + const childEnv = snapshotRunner + ? { + ...process.env, + [packagesDir ? 'VP_SNAP_PACKED_REGISTRY_ENV' : 'VP_SNAP_SHARED_REGISTRY_ENV']: + JSON.stringify(registryEnv), + } + : { ...process.env, ...registryEnv }; const child = spawn(cmd, cmdArgs, { - env: { ...process.env, ...registryEnv }, + env: childEnv, stdio: 'inherit', }); - child.on('exit', (code, signal) => { + const finish = (exitCode: number) => { cleanupRegistryEnv(registryEnv); - const exitCode = exitCodeFromClose(code, signal); + server.closeAllConnections(); server.close(() => process.exit(exitCode)); + }; + child.on('error', (error) => { + console.error(`local-npm-registry: ${error.message}`); + finish(1); + }); + child.on('exit', (code, signal) => { + finish(exitCodeFromClose(code, signal)); }); }); diff --git a/packages/tools/src/record-snapshot-registry.ts b/packages/tools/src/record-snapshot-registry.ts new file mode 100644 index 0000000000..c48a5462df --- /dev/null +++ b/packages/tools/src/record-snapshot-registry.ts @@ -0,0 +1,95 @@ +// Refresh the small, published test packages used by CLI snapshots. +// This is an explicit maintenance command, never part of a test run. +import { createHash } from 'node:crypto'; +import { mkdir, readFile, rm, writeFile } from 'node:fs/promises'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +interface Metadata { + versions: Record; + 'dist-tags': Record; + time?: Record; +} + +// Keep the initial versions, update targets, and an out-of-range latest version. +const selectedVersions: Record = { + 'is-number': ['6.0.0', '7.0.0'], + 'yocto-queue': ['0.1.0', '1.0.0', '1.2.2'], +}; + +const directory = fileURLToPath( + new URL('../../../crates/vp_cli_snapshots/tests/cli_snapshots/registry/', import.meta.url), +); +const manifestPath = path.join(directory, 'manifest.json'); +const previous = JSON.parse(await readFile(manifestPath, 'utf8')) as Record; +const manifest = { ...previous }; +const requested = process.argv.slice(2); +const names = requested.length ? requested : Object.keys(previous); +await mkdir(path.join(directory, 'tarballs'), { recursive: true }); + +for (const name of names.toSorted()) { + const response = await fetch(`https://registry.npmjs.org/${encodeURIComponent(name)}`, { + signal: AbortSignal.timeout(30_000), + }); + if (!response.ok) { + throw new Error(`Cannot record ${name}: HTTP ${response.status}`); + } + const metadata = (await response.json()) as Metadata; + const selected = selectedVersions[name]; + if (selected) { + if (!selected.includes(metadata['dist-tags'].latest)) { + throw new Error( + `Update selectedVersions for ${name}: latest is ${metadata['dist-tags'].latest}`, + ); + } + metadata.versions = Object.fromEntries( + selected.map((version) => { + const entry = metadata.versions[version]; + if (!entry) { + throw new Error(`Cannot record missing version ${name}@${version}`); + } + return [version, entry]; + }), + ); + metadata['dist-tags'] = Object.fromEntries( + Object.entries(metadata['dist-tags']).filter(([, version]) => selected.includes(version)), + ); + if (metadata.time) { + metadata.time = Object.fromEntries( + Object.entries(metadata.time).filter( + ([key]) => key === 'created' || key === 'modified' || selected.includes(key), + ), + ); + } + } + for (const version of Object.values(metadata.versions)) { + const response = await fetch(version.dist.tarball, { signal: AbortSignal.timeout(30_000) }); + if (!response.ok) { + throw new Error(`Cannot record ${version.dist.tarball}: HTTP ${response.status}`); + } + const bytes = Buffer.from(await response.arrayBuffer()); + const integrity = `sha512-${createHash('sha512').update(bytes).digest('base64')}`; + if (integrity !== version.dist.integrity) { + throw new Error(`Integrity mismatch for ${version.dist.tarball}`); + } + const basename = path.posix.basename(new URL(version.dist.tarball).pathname); + await writeFile(path.join(directory, 'tarballs', basename), bytes); + version.dist.tarball = `{REGISTRY}/${name}/-/${basename}`; + } + manifest[name] = metadata; +} +await writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`); + +const retainedTarballs = new Set( + Object.values(manifest).flatMap((metadata) => + Object.values(metadata.versions).map((version) => path.posix.basename(version.dist.tarball)), + ), +); +for (const name of names) { + for (const version of Object.values(previous[name]?.versions ?? {})) { + const basename = path.posix.basename(version.dist.tarball); + if (!retainedTarballs.has(basename)) { + await rm(path.join(directory, 'tarballs', basename), { force: true }); + } + } +} diff --git a/packages/tools/src/registry-cache.ts b/packages/tools/src/registry-cache.ts new file mode 100644 index 0000000000..62e214749f --- /dev/null +++ b/packages/tools/src/registry-cache.ts @@ -0,0 +1,263 @@ +// Run-scoped response storage for snapshot registries. Each case keeps its +// own package-manager caches; only successful upstream GETs are +// shared. Atomic publication and a directory lock also support nextest workers. +import { createHash, randomUUID } from 'node:crypto'; +import { appendFileSync } from 'node:fs'; +import { mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises'; +import { request as httpRequest, type IncomingMessage } from 'node:http'; +import { Agent, request as httpsRequest } from 'node:https'; +import path from 'node:path'; +import { setTimeout } from 'node:timers/promises'; +import { brotliDecompressSync, gunzipSync, inflateSync } from 'node:zlib'; + +interface Response { + status: number; + contentType: string; + body: Buffer; +} + +const tarballPrefix = '/-/snapshot-tarball/'; + +export function registryTarballUrl(url: string, registry: string): string { + const parsed = new URL(url); + if (['registry.npmjs.org', 'registry.yarnpkg.com'].includes(parsed.hostname)) { + return `${registry}${parsed.pathname}${parsed.search}`; + } + return `${registry}${tarballPrefix}${Buffer.from(url).toString('base64url')}`; +} + +export function upstreamUrl(requestPath: string, upstream: string): string { + if (requestPath.startsWith(tarballPrefix)) { + const url = new URL( + Buffer.from(requestPath.slice(tarballPrefix.length), 'base64url').toString(), + ); + if (url.protocol !== 'https:') { + throw new Error('Snapshot tarballs must use HTTPS'); + } + return url.href; + } + return `${upstream}${requestPath}`; +} + +// Rewrite only dist.tarball fields. Other URLs (readmes, repository links, +// provenance, etc.) must retain their meaning. Tarball bytes stay unchanged. +export function rewriteTarballs(value: unknown, registry: string): unknown { + if (!value || typeof value !== 'object') { + return value; + } + const object = value as Record; + const dist = object.dist as Record | undefined; + if (dist && typeof dist.tarball === 'string' && dist.tarball.startsWith('https://')) { + dist.tarball = registryTarballUrl(dist.tarball, registry); + } + if (object.versions && typeof object.versions === 'object') { + for (const version of Object.values(object.versions)) { + rewriteTarballs(version, registry); + } + } + return value; +} + +export class RegistryCache { + private readonly agent = new Agent({ keepAlive: true, maxSockets: 64 }); + private readonly pending = new Map>(); + + private readonly directory: string; + private readonly enabled: boolean; + private readonly logFile?: string; + private readonly timeoutMs: number; + private readonly idleTimeoutMs: number; + + constructor( + directory: string, + enabled = true, + logFile?: string, + timeoutMs = 60_000, + idleTimeoutMs = 15_000, + ) { + this.directory = directory; + this.enabled = enabled; + this.logFile = logFile; + this.timeoutMs = timeoutMs; + this.idleTimeoutMs = idleTimeoutMs; + } + + record(event: string, url: string): void { + if (this.logFile) { + appendFileSync(this.logFile, `${JSON.stringify({ event, url })}\n`); + } + } + + async get(url: string, accept: string): Promise { + this.record('request', url); + if (!this.enabled) { + return this.download(url, accept); + } + // Full and abbreviated packuments are different representations. + const representation = new URL(url).pathname.endsWith('.tgz') ? 'tarball' : accept; + const key = createHash('sha256') + .update(JSON.stringify([url, representation])) + .digest('hex'); + const pending = this.pending.get(key); + if (pending) { + this.record('hit', url); + return pending; + } + const response = this.load(key, url, accept); + this.pending.set(key, response); + try { + return await response; + } finally { + this.pending.delete(key); + } + } + + private async load(key: string, url: string, accept: string): Promise { + await mkdir(this.directory, { recursive: true }); + const file = path.join(this.directory, key); + const lock = `${file}.lock`; + const deadline = Date.now() + 120_000; + for (;;) { + try { + const bytes = await readFile(file); + const separator = bytes.indexOf(10); + const header = JSON.parse(bytes.subarray(0, separator).toString()) as Omit< + Response, + 'body' + >; + this.record('hit', url); + return { ...header, body: bytes.subarray(separator + 1) }; + } catch (error) { + if ((error as NodeJS.ErrnoException).code !== 'ENOENT') { + throw error; + } + } + try { + await mkdir(lock); + break; + } catch (error) { + if ((error as NodeJS.ErrnoException).code !== 'EEXIST') { + throw error; + } + if (Date.now() >= deadline) { + throw new Error(`Timed out waiting for registry response: ${url}`, { cause: error }); + } + await setTimeout(25); + } + } + const temporary = `${file}.${randomUUID()}.tmp`; + try { + // Another worker may have published between our read and mkdir. + try { + const bytes = await readFile(file); + const separator = bytes.indexOf(10); + const header = JSON.parse(bytes.subarray(0, separator).toString()) as Omit< + Response, + 'body' + >; + this.record('hit', url); + return { ...header, body: bytes.subarray(separator + 1) }; + } catch (error) { + if ((error as NodeJS.ErrnoException).code !== 'ENOENT') { + throw error; + } + } + const response = await this.download(url, accept); + if (response.status === 200) { + await writeFile( + temporary, + Buffer.concat([ + Buffer.from( + `${JSON.stringify({ status: response.status, contentType: response.contentType })}\n`, + ), + response.body, + ]), + ); + await rename(temporary, file); + } + return response; + } finally { + await rm(temporary, { force: true }); + await rm(lock, { recursive: true, force: true }); + } + } + + private async download(url: string, accept: string, redirects = 5): Promise { + this.record('upstream', url); + return new Promise((resolve, reject) => { + const stalled = new AbortController(); + let idleTimer: ReturnType | undefined; + const progress = () => { + clearTimeout(idleTimer); + idleTimer = globalThis.setTimeout(() => { + this.record('upstream-stalled', url); + stalled.abort(new Error('Registry request stalled')); + }, this.idleTimeoutMs); + }; + const request = url.startsWith('https:') ? httpsRequest : httpRequest; + const req = request( + url, + { + headers: { + accept, + 'accept-encoding': new URL(url).pathname.endsWith('.tgz') ? 'identity' : 'gzip', + }, + agent: url.startsWith('https:') ? this.agent : undefined, + // Bound the whole request, including connection setup and responses + // that keep sending data. Release the cache lock before a case times out. + signal: AbortSignal.any([AbortSignal.timeout(this.timeoutMs), stalled.signal]), + }, + (res: IncomingMessage) => { + progress(); + res.on('error', reject); + const status = res.statusCode ?? 502; + if (status >= 300 && status < 400 && res.headers.location) { + res.resume(); + if (redirects === 0) { + reject(new Error(`Too many registry redirects: ${url}`)); + } else { + resolve( + this.download(new URL(res.headers.location, url).href, accept, redirects - 1), + ); + } + return; + } + const chunks: Buffer[] = []; + res.on('data', (chunk: Buffer) => { + progress(); + chunks.push(chunk); + }); + res.on('end', () => { + try { + let body = Buffer.concat(chunks); + switch (res.headers['content-encoding']) { + case 'gzip': + body = gunzipSync(body); + break; + case 'br': + body = brotliDecompressSync(body); + break; + case 'deflate': + body = inflateSync(body); + break; + } + resolve({ + status, + contentType: res.headers['content-type'] ?? 'application/octet-stream', + body, + }); + } catch (error) { + reject(error); + } + }); + }, + ); + // Start before DNS/connect, unlike a socket timeout. Return a failed + // request promptly so the client's retries fit inside the case deadline. + progress(); + req.once('close', () => clearTimeout(idleTimer)); + req.on('error', reject); + req.end(); + }); + } +}