From 8c99bd6ef3d09daac23b1a4339979cc1150e1610 Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Tue, 6 Oct 2026 16:46:28 +0000 Subject: [PATCH 01/11] build(release): publish per-package workspace tarballs from the flake The flake now calls pnpm-release-management's mkPnpmWorkspacePackages, which runs pnpm pack once per publishable workspace package against the pnpm store the lockfile already pins. It exposes workspace-tarballs plus one derivation per package, and throws if a package name collides with a flake output. packageManager moves to pnpm@11.27.0 to match the pnpm_11 the Nix build runs, so the packed tarballs come from the same pnpm the repo pins. A no-network `pnpm install --frozen-lockfile --offline` leaves pnpm-lock.yaml byte-identical: sha256 4a158f16200ef06494a2b21b3af0c96a672d988cc15094a098a7877ed1c7790a before and after, header included. The pnpm store that lockfile resolves to hashes to sha256-kRv6HtXsqj4Tbf1ZdMETrb8fjsVvEchyqPmcix44vtw=, the flake's fetchPnpmDeps hash, measured by blanking it and reading the mismatch. The Nix workflow rebuilds workspace-tarballs twice and requires both to be bit-for-bit identical on x86_64-linux, aarch64-linux and aarch64-darwin. --- .github/workflows/nix.yml | 36 ++++++++++++ flake.lock | 114 ++++++++++++++++++++++++++++++++++++-- flake.nix | 45 ++++++++++----- package.json | 2 +- 4 files changed, 179 insertions(+), 18 deletions(-) create mode 100644 .github/workflows/nix.yml diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml new file mode 100644 index 000000000..bf273dffa --- /dev/null +++ b/.github/workflows/nix.yml @@ -0,0 +1,36 @@ +name: Nix + +on: + push: + branches: [main] + pull_request: + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + name: flake outputs · ${{ matrix.system }} + strategy: + fail-fast: false + matrix: + include: + - system: x86_64-linux + runner: ubuntu-latest + - system: aarch64-linux + runner: ubuntu-24.04-arm + - system: aarch64-darwin + runner: macos-latest + runs-on: ${{ matrix.runner }} + timeout-minutes: 60 + steps: + - uses: actions/checkout@v7 + - uses: cachix/install-nix-action@v31 + - name: Workspace tarballs rebuild bit-for-bit + run: | + nix build .#packages.${{ matrix.system }}.workspace-tarballs + nix build --rebuild .#packages.${{ matrix.system }}.workspace-tarballs diff --git a/flake.lock b/flake.lock index de01b1a1a..6a7a4bf32 100644 --- a/flake.lock +++ b/flake.lock @@ -24,11 +24,33 @@ "comment-checker_2": { "inputs": { "nixpkgs": [ - "systemfsoftware", + "pnpm-release-management", "nixpkgs" ], "rust-overlay": "rust-overlay_2" }, + "locked": { + "lastModified": 1790884787, + "narHash": "sha256-XZz0bdSY5zjlRDme0dfC98oYcAf3WOpMCueoU39iXjY=", + "owner": "systemfsoftware", + "repo": "comment-checker", + "rev": "3bfedd853c1d6dca666e65e71e9f8a31d47db336", + "type": "github" + }, + "original": { + "owner": "systemfsoftware", + "repo": "comment-checker", + "type": "github" + } + }, + "comment-checker_3": { + "inputs": { + "nixpkgs": [ + "systemfsoftware", + "nixpkgs" + ], + "rust-overlay": "rust-overlay_3" + }, "locked": { "lastModified": 1789259917, "narHash": "sha256-ciFWJFfpl8xTFJTnoquMb58RA7iVqvpi4Sfda9qyFTw=", @@ -64,6 +86,29 @@ "type": "github" } }, + "importPnpmLock_2": { + "inputs": { + "nixpkgs": [ + "pnpm-release-management", + "nixpkgs" + ], + "systems": "systems_2" + }, + "locked": { + "lastModified": 1790096829, + "narHash": "sha256-mnjMXjY/fo5OQH/iYNPijIqo0jAb/z7EhHtfXLlzOrs=", + "owner": "Scrumplex", + "repo": "importPnpmLock.nix", + "rev": "ca18d47b0e98a4b51404f69ce5ab51efb6ebc151", + "type": "github" + }, + "original": { + "owner": "Scrumplex", + "repo": "importPnpmLock.nix", + "rev": "ca18d47b0e98a4b51404f69ce5ab51efb6ebc151", + "type": "github" + } + }, "nixpkgs": { "locked": { "lastModified": 1790822859, @@ -96,11 +141,35 @@ "type": "github" } }, + "pnpm-release-management": { + "inputs": { + "comment-checker": "comment-checker_2", + "importPnpmLock": "importPnpmLock_2", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1791310224, + "narHash": "sha256-RGjVcKCJ7M4d1hHixL48deFskzypazMxm1GU5izOY+o=", + "owner": "systemfsoftware", + "repo": "pnpm-release-management", + "rev": "54629f2889039eb2e53ccf0179aaf8c7ef0c46a3", + "type": "github" + }, + "original": { + "owner": "systemfsoftware", + "repo": "pnpm-release-management", + "rev": "54629f2889039eb2e53ccf0179aaf8c7ef0c46a3", + "type": "github" + } + }, "root": { "inputs": { "comment-checker": "comment-checker", "importPnpmLock": "importPnpmLock", "nixpkgs": "nixpkgs", + "pnpm-release-management": "pnpm-release-management", "systemfsoftware": "systemfsoftware" } }, @@ -126,6 +195,28 @@ } }, "rust-overlay_2": { + "inputs": { + "nixpkgs": [ + "pnpm-release-management", + "comment-checker", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1790756160, + "narHash": "sha256-NPs4nNLxCpho3Ctj8O7t/bzmX0zR8r0zndKghAFEz5U=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "ed3a19fd0439ed618ec5fe1e12f0ba69a8be38b5", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "rust-overlay_3": { "inputs": { "nixpkgs": [ "systemfsoftware", @@ -147,7 +238,7 @@ "type": "github" } }, - "rust-overlay_3": { + "rust-overlay_4": { "inputs": { "nixpkgs": [ "systemfsoftware", @@ -170,9 +261,9 @@ }, "systemfsoftware": { "inputs": { - "comment-checker": "comment-checker_2", + "comment-checker": "comment-checker_3", "nixpkgs": "nixpkgs_2", - "rust-overlay": "rust-overlay_3" + "rust-overlay": "rust-overlay_4" }, "locked": { "lastModified": 1790907431, @@ -202,6 +293,21 @@ "repo": "default", "type": "github" } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index 97a9ada40..d316ca8ae 100644 --- a/flake.nix +++ b/flake.nix @@ -22,12 +22,26 @@ # Only the `gritlint` package and the dev shell reference this input, so # building any other package never fetches it. systemfsoftware.url = "github:systemfsoftware/systemfsoftware"; + # One `pnpm pack` tarball per workspace package, built offline from the + # lockfile by the same builder systemfsoftware uses. + pnpm-release-management = { + url = "github:systemfsoftware/pnpm-release-management/54629f2889039eb2e53ccf0179aaf8c7ef0c46a3"; + inputs.nixpkgs.follows = "nixpkgs"; + }; }; - outputs = { self, nixpkgs, comment-checker, importPnpmLock, systemfsoftware }: + outputs = { self, nixpkgs, comment-checker, importPnpmLock, systemfsoftware, pnpm-release-management }: let + lib = nixpkgs.lib; systems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ]; forEachSystem = fn: nixpkgs.lib.genAttrs systems (system: fn nixpkgs.legacyPackages.${system}); + workspaceOf = pkgs: + pnpm-release-management.lib.mkPnpmWorkspacePackages { + inherit pkgs; + src = self; + pname = "stryker-js-effect"; + pnpm = pkgs.pnpm_11; + }; in { packages = forEachSystem (pkgs: @@ -40,19 +54,24 @@ sandboxed = pkgs.callPackage ./nix/comment-checker-sandbox.nix { comment-checker = unwrapped; }; - in { - inherit dprint; - deno = pkgs.deno; - comment-checker = sandboxed; - comment-checker-unwrapped = unwrapped; - # The bwrap-sandboxed upstream `gritlint` needs unprivileged user - # namespaces, which Ubuntu 24.04 runners refuse without a workflow - # step this repo's read-only workflows cannot add. - gritlint = systemfsoftware.packages.${pkgs.stdenv.hostPlatform.system}.gritlint-unwrapped; - default = dprint; - }); + workspace = workspaceOf pkgs; + own = { + inherit dprint; + deno = pkgs.deno; + comment-checker = sandboxed; + comment-checker-unwrapped = unwrapped; + # The bwrap-sandboxed upstream `gritlint` needs unprivileged user + # namespaces, which Ubuntu 24.04 runners refuse without a workflow + # step this repo's read-only workflows cannot add. + gritlint = systemfsoftware.packages.${pkgs.stdenv.hostPlatform.system}.gritlint-unwrapped; + default = dprint; + }; + clashes = builtins.attrNames (builtins.intersectAttrs own workspace); + in + assert clashes == [ ] || throw "flake.nix: workspace packages ${lib.concatStringsSep ", " clashes} collide with flake packages"; + workspace // own); - # pnpm is deliberately absent: `packageManager` pins pnpm@11.21.0 and + # pnpm is deliberately absent: `packageManager` pins pnpm@11.27.0 and # corepack is the one thing allowed to resolve it. A second pnpm on PATH # would answer `pnpm install` with a version the lockfile never saw. devShells = forEachSystem (pkgs: { diff --git a/package.json b/package.json index 6a60fc04b..37346abd3 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ }, "license": "Apache-2.0", "name": "stryker-js-effect", - "packageManager": "pnpm@11.21.0", + "packageManager": "pnpm@11.27.0", "private": true, "scripts": { "attw": "turbo --concurrency=${TURBO_CONCURRENCY:-100%} attw", From be31c4828fc6ef34ad7cb557eb5078c91b71cec3 Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Tue, 6 Oct 2026 16:46:36 +0000 Subject: [PATCH 02/11] build(release): keep changelogs in the repo and stop pack reaching for a registry pnpm 11's pack composed the changelog with versioning.changelog.storage left at its default, registry: for a package with a pending .changeset/changelogs/@.md it downloaded the previous release's tarball CHANGELOG.md from the registry. Packing a package with pending intent therefore failed without network (ERR_PNPM_META_FETCH_FAIL) and made npm the source of truth for published history. versioning.changelog.storage: repository makes pnpm's release step write the new section into packages//CHANGELOG.md and pack that file as-is. Every publishable package now carries its CHANGELOG.md, seeded verbatim from its latest published tarball (dist.integrity verified against the download), so the repo holds the full history up to the latest release. Pack still ships only what a package's files field names, so a package that does not list CHANGELOG.md ships without one. The version-packages release of a74531e ran under registry storage and parked one section per bumped package under .changeset/changelogs, which nothing reads under repository storage. Each of the eight publishable sections moves byte-for-byte into its CHANGELOG.md under its version heading, the private stryker-e2e-core section is dropped, and no parked file remains. scripts/guards/check-changelog-sections.ts, run by guard:projects with a selftest, fails when a publishable package's CHANGELOG.md lacks exactly one section for its current version, or when a parked section exists under repository storage. Removing the 0.3.1 section from stryker-js-cli-contract, or adding a parked file, makes it exit 1 An offline `pnpm pack` in packages/stryker-js now exits 0; with storage set back to registry the same no-network command fails with ERR_PNPM_META_FETCH_FAIL --- ...@systemfsoftware!stryker-e2e-core@0.1.0.md | 6 - package.json | 2 +- packages/frameworks/angular/CHANGELOG.md | 20 + packages/frameworks/interface/CHANGELOG.md | 19 + packages/frameworks/svelte/CHANGELOG.md | 26 + packages/ignorers/angular/CHANGELOG.md | 23 + .../effect-schema-declarations/CHANGELOG.md | 33 + .../in-source-vitest-block/CHANGELOG.md | 27 + packages/ignorers/interface/CHANGELOG.md | 26 + packages/ignorers/kit/CHANGELOG.md | 20 + packages/stryker-js-cli-contract/CHANGELOG.md | 50 ++ .../stryker-js-html-reporter/CHANGELOG.md | 391 ++++++++ packages/stryker-js-instrumenter/CHANGELOG.md | 449 ++++++++++ .../stryker-js-plugin-interface/CHANGELOG.md | 276 ++++++ .../stryker-js-plugin-runtime/CHANGELOG.md | 134 +++ .../CHANGELOG.md | 532 +++++++++++ .../stryker-js-vitest-runner/CHANGELOG.md | 507 +++++++++++ packages/stryker-js/CHANGELOG.md | 847 ++++++++++++++++++ .../oxlint-ignorer-config/CHANGELOG.md | 25 + pnpm-workspace.yaml | 3 + scripts/guards/check-changelog-sections.ts | 161 ++++ 21 files changed, 3570 insertions(+), 7 deletions(-) delete mode 100644 .changeset/changelogs/@systemfsoftware!stryker-e2e-core@0.1.0.md create mode 100644 packages/frameworks/angular/CHANGELOG.md create mode 100644 packages/frameworks/interface/CHANGELOG.md create mode 100644 packages/frameworks/svelte/CHANGELOG.md create mode 100644 packages/ignorers/angular/CHANGELOG.md create mode 100644 packages/ignorers/effect-schema-declarations/CHANGELOG.md create mode 100644 packages/ignorers/in-source-vitest-block/CHANGELOG.md create mode 100644 packages/ignorers/interface/CHANGELOG.md create mode 100644 packages/ignorers/kit/CHANGELOG.md create mode 100644 packages/stryker-js-cli-contract/CHANGELOG.md create mode 100644 packages/stryker-js-html-reporter/CHANGELOG.md create mode 100644 packages/stryker-js-instrumenter/CHANGELOG.md create mode 100644 packages/stryker-js-plugin-interface/CHANGELOG.md create mode 100644 packages/stryker-js-plugin-runtime/CHANGELOG.md create mode 100644 packages/stryker-js-typescript-checker/CHANGELOG.md create mode 100644 packages/stryker-js-vitest-runner/CHANGELOG.md create mode 100644 packages/stryker-js/CHANGELOG.md create mode 100644 packages/toolchain/oxlint-ignorer-config/CHANGELOG.md create mode 100755 scripts/guards/check-changelog-sections.ts diff --git a/.changeset/changelogs/@systemfsoftware!stryker-e2e-core@0.1.0.md b/.changeset/changelogs/@systemfsoftware!stryker-e2e-core@0.1.0.md deleted file mode 100644 index 91e4a1a77..000000000 --- a/.changeset/changelogs/@systemfsoftware!stryker-e2e-core@0.1.0.md +++ /dev/null @@ -1,6 +0,0 @@ -## 0.1.0 - -### Patch Changes - -- Updated dependencies: - - @systemfsoftware/stryker-js-plugin-interface@14.0.0 diff --git a/package.json b/package.json index 37346abd3..eaa43dbff 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "format:check": "./bin/dprint check", "gate:dist": "turbo --concurrency=${TURBO_CONCURRENCY:-100%} build ${TURBO_BASE:+--filter=...[$TURBO_BASE]}", "gate:tasks": "pnpm test:scripts && pnpm guard:projects && turbo --concurrency=${TURBO_CONCURRENCY:-100%} --continue lint lint:tsgo typecheck test ${TURBO_BASE:+--filter=...[$TURBO_BASE]}", - "guard:projects": "for g in check-single-plan check-contract-versions check-span-names check-e2e-manifest check-verdict-semantics; do ./bin/deno run --config=scripts/deno.json --allow-read --allow-run --allow-write=${TMPDIR:-/tmp} --allow-env scripts/guards/$g.ts --selftest && ./bin/deno run --config=scripts/deno.json --allow-read --allow-run --allow-write=${TMPDIR:-/tmp} --allow-env scripts/guards/$g.ts || exit 1; done", + "guard:projects": "for g in check-single-plan check-contract-versions check-span-names check-e2e-manifest check-verdict-semantics check-changelog-sections; do ./bin/deno run --config=scripts/deno.json --allow-read --allow-run --allow-write=${TMPDIR:-/tmp} --allow-env scripts/guards/$g.ts --selftest && ./bin/deno run --config=scripts/deno.json --allow-read --allow-run --allow-write=${TMPDIR:-/tmp} --allow-env scripts/guards/$g.ts || exit 1; done", "lint": "turbo --concurrency=${TURBO_CONCURRENCY:-100%} lint", "lint:conventions": "./bin/gritlint check", "lgtm:down": "process-compose down", diff --git a/packages/frameworks/angular/CHANGELOG.md b/packages/frameworks/angular/CHANGELOG.md new file mode 100644 index 000000000..75658424f --- /dev/null +++ b/packages/frameworks/angular/CHANGELOG.md @@ -0,0 +1,20 @@ +# @systemfsoftware/stryker-js-angular + +## 0.0.0 + +### Minor Changes + +- First release. The Angular framework plugin claims `.html`, `.htm`, and `.vue` + files and hands their `script` regions to the instrumenter, so mutants land in + embedded JavaScript and TypeScript while template expressions are left + untouched. + + List it by package name in `plugins`. The package's manifest declares + `strykerFramework` with the extensions it claims, so a run names it in a skip + report when a file type it owns is left unconfigured. Its format carries the + version of the HTML parser it resolved, so upgrading that parser invalidates the + mutant results an earlier incremental run remembered for its files. + + Signal ignoring is not included: pair the plugin with + `@systemfsoftware/stryker-ignorer-angular` to keep the configuration objects of + the signal `input`, `model`, `output`, and query functions out of mutation. diff --git a/packages/frameworks/interface/CHANGELOG.md b/packages/frameworks/interface/CHANGELOG.md new file mode 100644 index 000000000..a75ccfb80 --- /dev/null +++ b/packages/frameworks/interface/CHANGELOG.md @@ -0,0 +1,19 @@ +# @systemfsoftware/stryker-framework-interface + +## 0.0.0 + +### Minor Changes + +- First release. A framework plugin claims a format (`formatId`, `extensions`, + `language`, `ownerVersion`, `contractVersion`), parses files into embedded + documents, and receives a `FrameworkContext` of `parseScript`, `printScript`, + and `instrumentationHeader`. Every script region carries the `Program` the core + parsed for it, so a hook never re-parses or re-checks one. + + A plugin whose peer is missing, outside its supported range, or not exporting + what the plugin needs exports a `FrameworkRefusal` in place of its framework, + with the reason `PeerMissing`, `PeerVersionUnsupported`, or `PeerUnrecognized`. + + `FrameworkPackageManifest` types a plugin package's top-level `strykerFramework` + field naming the extensions its framework claims, which the host reads to name + that package in a skip reason without importing it. diff --git a/packages/frameworks/svelte/CHANGELOG.md b/packages/frameworks/svelte/CHANGELOG.md new file mode 100644 index 000000000..77dfd329d --- /dev/null +++ b/packages/frameworks/svelte/CHANGELOG.md @@ -0,0 +1,26 @@ +# @systemfsoftware/stryker-js-svelte + +## 0.0.1 + +### Patch Changes + +- Stages now run as cells over workflows, multi-item work runs as Effect streams, pools and worker transport use scoped Effect resources, and named operations are traced with Effect.fn. The Angular ignorer now declares @systemfsoftware/stryker-ignorer-kit as a runtime dependency. + +## 0.0.0 + +### Minor Changes + +- First release. `.svelte` files are instrumented — script blocks and template + expressions on the Svelte 5 modern AST — and their mutants are reported under + the `svelte` language. + + Svelte 5 is an optional peer dependency (`^5.0.0`). The compiler is resolved + from your project when the plugin loads. When no compiler is installed, when + it exports no usable compiler surface, or when the installed major version is + not 5, the run stops before instrumentation as a configuration error naming + the peer. + + Enable the plugin by installing it and adding it to `plugins`. Its format + carries the Svelte compiler version it resolved, so upgrading the compiler + invalidates the mutant results an earlier incremental run remembered for + `.svelte` files. diff --git a/packages/ignorers/angular/CHANGELOG.md b/packages/ignorers/angular/CHANGELOG.md new file mode 100644 index 000000000..1bfb107c3 --- /dev/null +++ b/packages/ignorers/angular/CHANGELOG.md @@ -0,0 +1,23 @@ +# @systemfsoftware/stryker-ignorer-angular + +## 0.1.2 + +### Patch Changes + +- Stages now run as cells over workflows, multi-item work runs as Effect streams, pools and worker transport use scoped Effect resources, and named operations are traced with Effect.fn. The Angular ignorer now declares @systemfsoftware/stryker-ignorer-kit as a runtime dependency. + +## 0.1.1 + +### Patch Changes + +- Effect moves to `4.0.0-rc.116` (with `@effect/platform-node`, `@effect/platform-node-shared`, `@effect/vitest` and `@effect/opentelemetry` on the same release), the `@systemfsoftware/*` toolchain pins move to their current releases, and `vitest` 5 with `oxc-parser` 0.150 come along. Both worker bundles (`stryker-js-typescript-checker`, `stryker-js-vitest-runner`) now inline their whole runtime rather than resolving `effect`, `@effect/*` and the sibling packages from the host's tree, so a worker runs on its own copy of the effect that built it; the project's `typescript` and `vitest` remain the only imports beside Node builtins. The filter-level `arbitrary.candidate` generators that the thresholds schemas carried are gone: v4 no longer reads them, so thresholds still reject a `low` above `high` and the property tests generate the pair from the schema again. Log calls inside `Effect.catch*` handlers moved to `Effect.tapError`/`Effect.tapCause` at the same level and message. `@systemfsoftware/stryker-ignorer-interface` re-exports the 0.150 AST, where `FormalParameterRest.decorators` is now `Array`. + +## 0.1.0 + +### Minor Changes + +- Debut: the Angular signal ignorer as its own package. `strykerIgnorers[0].shouldIgnore(node, ancestors)` suppresses mutations in Angular signal declarations — `input`, `model`, `output`, `viewChild`, `viewChildren`, `contentChild`, `contentChildren` and `query` — recognizing both the signal-function call form and the property-decorator form. The messages explaining each suppression are exported alongside it. + +### Patch Changes + +- Maintenance release. Every published entry point, export, option, and behaviour is exactly as it was. diff --git a/packages/ignorers/effect-schema-declarations/CHANGELOG.md b/packages/ignorers/effect-schema-declarations/CHANGELOG.md new file mode 100644 index 000000000..6bd7f9e71 --- /dev/null +++ b/packages/ignorers/effect-schema-declarations/CHANGELOG.md @@ -0,0 +1,33 @@ +# @systemfsoftware/stryker-ignorer-effect-schema-declarations + +## 0.2.0 + +### Minor Changes + +- The ignorer recognizes the declaration forms Effect Schema files use today, so far fewer unkillable mutants reach your report. A `TaggedStruct` tag, an `annotate({ identifier, description, title })` object, a filter or check annotation object such as `makeFilter(predicate, { expected })`, a declaration's `toCodecArbitrary` callback and the arbitrary link transformation it returns, a decoding or constructor default, and a `TypeId` identity constant are all reported as `Ignored` now. Predicates, bounds, patterns, literal vocabularies, struct field sets, and codec decode/encode transformations keep being mutated. Each newly recognized form carries its own exported reason constant. + +## 0.1.1 + +### Patch Changes + +- Effect moves to `4.0.0-rc.116` (with `@effect/platform-node`, `@effect/platform-node-shared`, `@effect/vitest` and `@effect/opentelemetry` on the same release), the `@systemfsoftware/*` toolchain pins move to their current releases, and `vitest` 5 with `oxc-parser` 0.150 come along. Both worker bundles (`stryker-js-typescript-checker`, `stryker-js-vitest-runner`) now inline their whole runtime rather than resolving `effect`, `@effect/*` and the sibling packages from the host's tree, so a worker runs on its own copy of the effect that built it; the project's `typescript` and `vitest` remain the only imports beside Node builtins. The filter-level `arbitrary.candidate` generators that the thresholds schemas carried are gone: v4 no longer reads them, so thresholds still reject a `low` above `high` and the property tests generate the pair from the schema again. Log calls inside `Effect.catch*` handlers moved to `Effect.tapError`/`Effect.tapCause` at the same level and message. `@systemfsoftware/stryker-ignorer-interface` re-exports the 0.150 AST, where `FormalParameterRest.decorators` is now `Array`. + +## 0.1.0 + +### Minor Changes + +- Removed the `decideSchemaDeclarationIgnore` and `CLASS_FIELDS_IGNORED` exports. The ignorer still + registers under the name `effect-schema-declarations` with unchanged recognized declaration shapes + and unchanged ignore reasons. If you called the decision function directly, consult the descriptor + instead: `strykerIgnorers[0].shouldIgnore(node, ancestors)`. + +- First release. This ignorer keeps mutants inside Effect Schema declarations out of + a mutation run: a symbol, a tagged error, an annotation or a class body only + declares a type, so no run can observe the change. + + It has no runtime dependencies, and migrating from the plugin you use today is a + rename in your plugins list — the ignorer keeps the name it already had. + +### Patch Changes + +- Maintenance release. Every published entry point, export, option, and behaviour is exactly as it was. diff --git a/packages/ignorers/in-source-vitest-block/CHANGELOG.md b/packages/ignorers/in-source-vitest-block/CHANGELOG.md new file mode 100644 index 000000000..1528a38d6 --- /dev/null +++ b/packages/ignorers/in-source-vitest-block/CHANGELOG.md @@ -0,0 +1,27 @@ +# @systemfsoftware/stryker-ignorer-in-source-vitest-block + +## 0.1.1 + +### Patch Changes + +- Effect moves to `4.0.0-rc.116` (with `@effect/platform-node`, `@effect/platform-node-shared`, `@effect/vitest` and `@effect/opentelemetry` on the same release), the `@systemfsoftware/*` toolchain pins move to their current releases, and `vitest` 5 with `oxc-parser` 0.150 come along. Both worker bundles (`stryker-js-typescript-checker`, `stryker-js-vitest-runner`) now inline their whole runtime rather than resolving `effect`, `@effect/*` and the sibling packages from the host's tree, so a worker runs on its own copy of the effect that built it; the project's `typescript` and `vitest` remain the only imports beside Node builtins. The filter-level `arbitrary.candidate` generators that the thresholds schemas carried are gone: v4 no longer reads them, so thresholds still reject a `low` above `high` and the property tests generate the pair from the schema again. Log calls inside `Effect.catch*` handlers moved to `Effect.tapError`/`Effect.tapCause` at the same level and message. `@systemfsoftware/stryker-ignorer-interface` re-exports the 0.150 AST, where `FormalParameterRest.decorators` is now `Array`. + +## 0.1.0 + +### Minor Changes + +- First release. This ignorer keeps mutants inside a test-only block out of a + mutation run: those statements never reach the production path, so no run can + observe the change. + + It has no runtime dependencies, and migrating from the plugin you use today is a + rename in your plugins list — the ignorer keeps the name it already had. + +- Removed the `decideInSourceTestIgnore` and `isInSourceTestGuard` exports. The ignorer still + registers under the name `in-source-vitest-block` with unchanged recognized guard shapes and + unchanged ignore reasons. If you called the decision function directly, consult the + descriptor instead: `strykerIgnorers[0].shouldIgnore(node, ancestors)`. + +### Patch Changes + +- Maintenance release. Every published entry point, export, option, and behaviour is exactly as it was. diff --git a/packages/ignorers/interface/CHANGELOG.md b/packages/ignorers/interface/CHANGELOG.md new file mode 100644 index 000000000..796d84530 --- /dev/null +++ b/packages/ignorers/interface/CHANGELOG.md @@ -0,0 +1,26 @@ +# @systemfsoftware/stryker-ignorer-interface + +## 0.1.1 + +### Patch Changes + +- Effect moves to `4.0.0-rc.116` (with `@effect/platform-node`, `@effect/platform-node-shared`, `@effect/vitest` and `@effect/opentelemetry` on the same release), the `@systemfsoftware/*` toolchain pins move to their current releases, and `vitest` 5 with `oxc-parser` 0.150 come along. Both worker bundles (`stryker-js-typescript-checker`, `stryker-js-vitest-runner`) now inline their whole runtime rather than resolving `effect`, `@effect/*` and the sibling packages from the host's tree, so a worker runs on its own copy of the effect that built it; the project's `typescript` and `vitest` remain the only imports beside Node builtins. The filter-level `arbitrary.candidate` generators that the thresholds schemas carried are gone: v4 no longer reads them, so thresholds still reject a `low` above `high` and the property tests generate the pair from the schema again. Log calls inside `Effect.catch*` handlers moved to `Effect.tapError`/`Effect.tapCause` at the same level and message. `@systemfsoftware/stryker-ignorer-interface` re-exports the 0.150 AST, where `FormalParameterRest.decorators` is now `Array`. + +## 0.1.0 + +### Minor Changes + +- First release. This is the contract an ignorer is written against: one `Ignorer` + descriptor (`{ name, shouldIgnore }`) whose decision receives the node and its + ancestors — nearest first, fully typed positions, nothing unknown. The whole AST + vocabulary of the producing parser is re-exported with optional spans, so + hand-built fixtures only need the fields a rule actually reads. + + Hosts that traverse a tree get `Walker` and `WalkVisitors` types from the same + contract: a walker drives `enter`/`leave` with the node and its ancestor stack, + and the host supplies the traversal itself. The package ships no runtime value + and no runtime dependencies, so adopting it costs nothing beyond the types. + +### Patch Changes + +- Maintenance release. Every published entry point, export, option, and behaviour is exactly as it was. diff --git a/packages/ignorers/kit/CHANGELOG.md b/packages/ignorers/kit/CHANGELOG.md new file mode 100644 index 000000000..aed7d6fb8 --- /dev/null +++ b/packages/ignorers/kit/CHANGELOG.md @@ -0,0 +1,20 @@ +# @systemfsoftware/stryker-ignorer-kit + +## 0.1.1 + +### Patch Changes + +- Effect moves to `4.0.0-rc.116` (with `@effect/platform-node`, `@effect/platform-node-shared`, `@effect/vitest` and `@effect/opentelemetry` on the same release), the `@systemfsoftware/*` toolchain pins move to their current releases, and `vitest` 5 with `oxc-parser` 0.150 come along. Both worker bundles (`stryker-js-typescript-checker`, `stryker-js-vitest-runner`) now inline their whole runtime rather than resolving `effect`, `@effect/*` and the sibling packages from the host's tree, so a worker runs on its own copy of the effect that built it; the project's `typescript` and `vitest` remain the only imports beside Node builtins. The filter-level `arbitrary.candidate` generators that the thresholds schemas carried are gone: v4 no longer reads them, so thresholds still reject a `low` above `high` and the property tests generate the pair from the schema again. Log calls inside `Effect.catch*` handlers moved to `Effect.tapError`/`Effect.tapCause` at the same level and message. `@systemfsoftware/stryker-ignorer-interface` re-exports the 0.150 AST, where `FormalParameterRest.decorators` is now `Array`. + +## 0.1.0 + +### Minor Changes + +- Debut: authoring and testing kit for the ignorer family. `defineIgnorer` compiles typed + visitors (visited node narrowed by key, typed ancestor context via `parentIf`/`ancestorIf`) + into the plain `Ignorer` wire contract; the `./tester` entry's `testIgnorer` verifies any + compiled ignorer from source snippets, registering one test per case. + +### Patch Changes + +- Maintenance release. Every published entry point, export, option, and behaviour is exactly as it was. diff --git a/packages/stryker-js-cli-contract/CHANGELOG.md b/packages/stryker-js-cli-contract/CHANGELOG.md new file mode 100644 index 000000000..831a4f658 --- /dev/null +++ b/packages/stryker-js-cli-contract/CHANGELOG.md @@ -0,0 +1,50 @@ +# @systemfsoftware/stryker-js-cli-contract + +## 0.3.1 + +### Patch Changes + +- Updated dependencies: + - @systemfsoftware/stryker-js-plugin-interface@14.0.0 + +## 0.3.0 + +### Minor Changes + +- Mutation testing is now incremental by default: `incremental` defaults to `true`, so an unchanged mutant whose covering tests are unchanged is re-used instead of re-run. `--full` (aliasing the old `--force`) re-verifies every mutant, ignoring the cache and the persisted dry run. The `verdict` stream event records `incrementalMode` (`incremental` or `full`). A run with no reusable cache falls back to a full run as before. + +- The machine run-event contract (stream version 6.0) adds a required `budget` object, predicted and actual seconds, to every `verdict` line, so a consumer can observe how much of a run's time budget a change consumed. + +- `ShardPlan` is a new published contract: a deterministic shard plan carrying each shard's project mutant lists, predicted seconds, and a GitHub Actions matrix. The `plan` stream event now carries a required `shardPlan` field, and the stream schema version moves to `6.0`. + +- A mutation run now starts only on main CI. The `run` command, the programmatic run entry point, and every other surface that begins a mutation run refuse to start unless the process runs under GitHub Actions (`GITHUB_ACTIONS=true`); anything else — a developer machine, a fork, or another CI — exits non-zero instead of running mutants. A refused run ends the machine-readable output with a new `refused` event whose `rule` is `mutation-runs-on-main-ci`, and the machine-stream schema version is now `3.0`. Dry runs (`--dryRunOnly`), `--version`, and config and help commands are unaffected. + +- The contract package now exports the shard plan schema: the versioned document a planner emits and a sharded run consumes, describing each shard's projects and the mutant ids it must run. A plan is refused when two shards share a label, because `--shard k/N` could not tell them apart. + +- The machine stream declares version `6.0` and carries a new `worker` line, `{ _tag: "worker", schemaVersion, role, index, startupMs }`, emitted once per test-runner or checker process boot. A consumer that switches exhaustively over the stream's event tags must handle the new tag; the start-up cost of a reused worker is now visible instead of being folded into the first mutant's cost. + +- The TypeScript checker now applies Trivial Compiler Equivalence (Papadakis et al., ICSE 2015). Each mutant of a file is emitted to JavaScript from the project's own TypeScript configuration in transpile-only mode, normalized, and compared with the original file's emit and with the mutants already emitted at the same site. A mutant whose emit equals the original's is dropped as `Ignored` with reason `equivalent-to-original: tce`; one that equals an earlier same-site mutant's is dropped with `duplicate-at-site: tce`; every mutant whose emit differs is kept, so the check never removes a mutant that changes the program. Checkers gain an `ignored` result variant carrying the suppression reason, and a run publishes the new `tce` machine-stream event with the `equivalentToOriginal` and `duplicateAtSite` counts. Verdict semantics move to version 3. + +### Patch Changes + +- Updated dependencies: + - @systemfsoftware/stryker-js-plugin-interface@13.0.0 + +## 0.2.0 + +### Minor Changes + +- Effect moves to the stable `4.0.0` release, together with the `@effect/*` packages these libraries use. The `4.0.0` release candidates are no longer supported: install `effect` `^4.0.0` next to these packages before upgrading. + + The TypeScript checker and test-runner plugins bundle their own Effect runtime, so they need no change in your project. + +### Patch Changes + +- Updated dependencies: + - @systemfsoftware/stryker-js-plugin-interface@12.0.0 + +## 0.1.0 + +### Minor Changes + +- Naming mutant ids on the run command re-runs exactly those mutants, restricting the mutated files to their ids' files with verdict reuse left on and reporting each selected mutant's status, covering tests, killing test, and reproducer command, while the trace names the new rerun span for that path. diff --git a/packages/stryker-js-html-reporter/CHANGELOG.md b/packages/stryker-js-html-reporter/CHANGELOG.md new file mode 100644 index 000000000..0112b6bc4 --- /dev/null +++ b/packages/stryker-js-html-reporter/CHANGELOG.md @@ -0,0 +1,391 @@ +# @systemfsoftware/stryker-js-html-reporter + +## 6.0.2 + +### Patch Changes + +- Updated dependencies: + - @systemfsoftware/stryker-js-plugin-interface@14.0.0 + +## 6.0.1 + +### Patch Changes + +- Updated dependencies: + - @systemfsoftware/stryker-js-cli-contract@0.3.0 + - @systemfsoftware/stryker-js-plugin-interface@13.0.0 + +## 6.0.0 + +### Major Changes + +- Effect moves to the stable `4.0.0` release, together with the `@effect/*` packages these libraries use. The `4.0.0` release candidates are no longer supported: install `effect` `^4.0.0` next to these packages before upgrading. + + The TypeScript checker and test-runner plugins bundle their own Effect runtime, so they need no change in your project. + +### Patch Changes + +- Updated dependencies: + - @systemfsoftware/stryker-js-cli-contract@0.2.0 + - @systemfsoftware/stryker-js-instrumenter@12.0.0 + - @systemfsoftware/stryker-js-plugin-interface@12.0.0 + +## 5.0.5 + +### Patch Changes + +- Updated dependencies: + - @systemfsoftware/stryker-js-instrumenter@11.0.0 + - @systemfsoftware/stryker-js-plugin-interface@11.0.0 + +## 5.0.4 + +### Patch Changes + +- The packages now depend on `@systemfsoftware/effect-cell-types` 11. + + - Every tagged error now has a one-line message built from its fields, so a failure names the file, mutant, plugin, worker or exit code involved instead of an empty message. The errors' tags, fields and encoded forms are unchanged. + +## 5.0.3 + +### Patch Changes + +- Stages now run as cells over workflows, multi-item work runs as Effect streams, pools and worker transport use scoped Effect resources, and named operations are traced with Effect.fn. The Angular ignorer now declares @systemfsoftware/stryker-ignorer-kit as a runtime dependency. + +- Updated dependencies: + - @systemfsoftware/stryker-js-instrumenter@10.0.0 + - @systemfsoftware/stryker-js-plugin-interface@10.0.0 + +## 5.0.2 + +### Patch Changes + +- Updated dependencies: + - @systemfsoftware/stryker-js-plugin-interface@9.0.0 + +## 5.0.1 + +### Patch Changes + +- The packages now build on the latest `@systemfsoftware/effect-cell-types` 10.2 cell kinds, with no change to their published behavior. + + - `@systemfsoftware/stryker-test-contribution` exports its evaluator as `Judge`, `TestContribution`, and `TestContributionEvaluator`, and now depends on `effect` directly. + +## 5.0.0 + +### Major Changes + +- Each package's main entry point now groups its exports into namespaces named after a capability, such as `Plugin`, `TestRunner` and `Report`. + + - Import the namespace and qualify each name, for example `Plugin.TestRunnerRpcs` after importing `Plugin` from the plugin interface. + - Import instrumenter schemas such as `Location` and `MutantStatus` from the instrumenter's `Mutant` namespace, and plugin-interface names from the plugin interface, instead of through another package's entry point. + - The engine's `/config`, `/events` and `/promises` entry points are unchanged. + +### Minor Changes + +- Each run stage, checker call, report write and test-runner mutant run now records an OpenTelemetry span named after its cell. The span has `.read` and `.write` child spans and an `app..decision` or `app..failure` attribute holding the outcome. It also feeds an `app..duration` histogram labelled `result_class`. + +- `makeHtmlReporter` can now be called with the reporter options alone, `makeHtmlReporter(options)`, and handed the reporter init when the run starts. The existing two-argument call, `makeHtmlReporter(options, init)`, keeps working exactly as before and remains the form the engine calls. + +### Patch Changes + +- The HTML report opens in the browser again. A cut-off logo image left the page markup unfinished, so the report never loaded and the page stayed blank. + +- Updated dependencies: + - @systemfsoftware/stryker-js-instrumenter@9.0.0 + - @systemfsoftware/stryker-js-plugin-interface@8.0.0 + +## 4.0.5 + +### Patch Changes + +- Exported declarations that previously took `unknown` now take a defaulted type parameter: calls that omit the type argument are unchanged, and calls that were previously rejected for passing an unconstrained value are accepted. No runtime behaviour changes. + +## 4.0.4 + +### Patch Changes + +- Effect moves to `4.0.0-rc.116` (with `@effect/platform-node`, `@effect/platform-node-shared`, `@effect/vitest` and `@effect/opentelemetry` on the same release), the `@systemfsoftware/*` toolchain pins move to their current releases, and `vitest` 5 with `oxc-parser` 0.150 come along. Both worker bundles (`stryker-js-typescript-checker`, `stryker-js-vitest-runner`) now inline their whole runtime rather than resolving `effect`, `@effect/*` and the sibling packages from the host's tree, so a worker runs on its own copy of the effect that built it; the project's `typescript` and `vitest` remain the only imports beside Node builtins. The filter-level `arbitrary.candidate` generators that the thresholds schemas carried are gone: v4 no longer reads them, so thresholds still reject a `low` above `high` and the property tests generate the pair from the schema again. Log calls inside `Effect.catch*` handlers moved to `Effect.tapError`/`Effect.tapCause` at the same level and message. `@systemfsoftware/stryker-ignorer-interface` re-exports the 0.150 AST, where `FormalParameterRest.decorators` is now `Array`. + +## 4.0.3 + +### Patch Changes + +- Updated dependencies: + - @systemfsoftware/stryker-js-plugin-interface@7.0.0 + +## 4.0.2 + +### Patch Changes + +- Updated dependencies: + - @systemfsoftware/stryker-js-instrumenter@8.0.0 + +## 4.0.1 + +### Patch Changes + +- Updated dependencies: + - @systemfsoftware/stryker-js-plugin-interface@6.0.0 + +## 4.0.0 + +### Major Changes + +- The plugin system now runs each configured TestRunner/Checker/Reporter plugin in its own spawned process over an @effect/rpc wire: plugins load from the project's config-declared specifiers (no glob discovery), boundary failures are typed errors, and plugin spans link into the host trace. Breaking: the in-process plugin Layer contract is removed. + +### Patch Changes + +- Maintenance release. Every published entry point, export, option, and behaviour is exactly as it was. + +- effect peer range widens to ^4.0.0-rc.112 and the platform-node-shared dependency range widens to the repo catalog; repository metadata now points at stryker-js-effect + +## 3.0.3 + +### Patch Changes + +- Imports rewired to the collapsed `@systemfsoftware/stryker-js` root entry; each package now co-releases against the root-entry major. + +- Updated dependencies: + - @systemfsoftware/effect-cell-types@8.0.0 + - @systemfsoftware/stryker-js@4.0.0 + +## 3.0.2 + +### Patch Changes + +- Releases the workspace so its published versions track the shared dependency graph this change moves. + +- The `@systemfsoftware/source` export condition is gone. It resolved to a package's TypeScript sources for editors and in-repo typechecks; each package now exports only its built entry. If your tsconfig sets `customConditions: ["@systemfsoftware/source"]`, or a bundler config names that condition, remove it — resolution falls back to the built entry, which is what every consumer already got. + +## 3.0.1 + +### Patch Changes + +- Updated dependencies: + - @systemfsoftware/effect-cell-types@7.0.0 + +## 3.0.0 + +### Major Changes + +- Reporter plugins are now pull-stream consumers: a reporter exports a factory + that returns an async consumer of the four run events, instead of registering + an Effect layer that provides the `Reporter` capability service. + + - Removed: `ReporterService`, `broadcastReporter`, `NamedReporter`, the + `Reporter` service class, and the legacy event payload types. + - The event protocol ships as a Standard Schema object; validate and infer it + with `import { ReporterEventSchema, type ReporterFactory, type ReporterInit } + from '@systemfsoftware/stryker-js/ReporterEvent'`. + - Event payloads are narrowed: the dry-run event no longer carries the coverage + map; the plan event carries reduced mutant descriptors instead of full run + options; `mutantTested` drops `killedBy`, `coveredBy`, `static`, + `testsCompleted` and `statusReason`, and renames `mutatorName` to `mutator`. + - `ReporterFailed.event` now names the event kind being processed instead of a + lifecycle method name; `'wrapUp'` no longer occurs. + - An unknown reporter name now fails the run at startup instead of being + silently ignored. A reporter that fails on the terminal report now fails the + run; a reporter that fails earlier is logged and detached with the exit code + unchanged. + - `@systemfsoftware/stryker-js-engine/builtin-reporters` no longer exports + `strykerPlugins`; call `makeBuiltinReporterFactories({ fileSystem, path })`. + - The built-in json and clear-text reporters write their notices directly to + stdout and stderr instead of the Effect logger, and the html reporter no + longer depends on `@effect/platform-node`. + +### Patch Changes + +- Re-released against @systemfsoftware/stryker-js without the removed --llms manifest. The Run stream no longer carries a manifest terminal event, and the RunEvent / RunTerminalEvent unions no longer include the manifest arm, so any exhaustive consumer of those types must drop that case. + +- Updated dependencies: + - @systemfsoftware/stryker-js@3.0.0 + +## 2.0.0 + +### Major Changes + +- The packages are renamed. `plugin-api` is now `@systemfsoftware/stryker-js`, the + language every plugin is written against. `mutation-run` is split: the run + itself is `@systemfsoftware/stryker-js-engine` + (host-neutral, no Node on its manifest) and the Node process entries are + `@systemfsoftware/stryker-js-cli`, which owns the worker files and the runtime + gate. `mutation-report` is now `@systemfsoftware/stryker-js-html-reporter`. + `@systemfsoftware/stryker-js-platform-node` is never published — do not install + it. Install the new names and change your imports. + + Options types moved. `StrykerOptions`, `PartialStrykerOptions` and `LogLevel` are + imported from the `Schema` export; `Mutant`, `MutantStatus`, `Position` and + `Location` from the `Mutant` export. Point a config's `extends` at the language + package's `Schema` export. + + `MutantStatus` accepts one spelling per outcome: `Killed`, `Survived`, + `NoCoverage`, `Timeout`, `CompileError`, `RuntimeError`, `Ignored` and `Pending`. + The lowercase and abbreviated forms — `killed`, `timedOut`, `noCoverage` and the + rest — are gone. A comparison against a removed spelling never matched the value + the reporter actually produced, so check any status comparison you wrote. + + Statuses, plugin kinds, exit classes and AST formats are string literal unions + rather than enums, so read them as their string values. Member access such as + `ExitClass.VerdictFail` no longer resolves. + + A plugin no longer receives a logger, and the logger port is gone. Plugins log + through Effect, and the host decides where that output goes. + + The bundled base preset is gone. A config inherits from the language package's + `Schema` export and states the thresholds, reporters and plugins it wants; you no + longer silently inherit a package manager, a plugin list or a break threshold. + +- effect is now a required peer dependency. Install it alongside these packages. They previously bundled their own copy, which meant two Effect instances in one process and services that could not find each other across the boundary. + +- Five entry points that were never API are gone. Each existed because another + package in this project found the code convenient, not because it was a surface + anyone should depend on. + + What is left is documented: an entry point is a name you may import and we may + not move without a major, and everything else is internal whatever file it sits + in. + +- Reporters are constructed by a factory and provided as a layer. + + The exported reporter classes are gone. Replace each `new` with the matching + factory — `makeClearTextReporter`, `makeHtmlReporter`, `makeJsonReporter`, + `makeProgressBarReporter`, `makeProgressStreamReporter`. + + Each factory takes the reporter's own options rather than an injected container, + and each operation returns an `Effect`. If you registered a reporter as a plugin, + declare it with `declarePlugin` and hand over a layer that provides `Reporter`. + + `drawClearTextScoreTable` is now exported for anyone rendering the score table + outside a reporter. + +### Patch Changes + +- New version is published through npm trusted publishing, so it carries a provenance attestation you can verify. + +- Peer Effect requirement advances to 4.0.0-rc.112. No API changes. + +- A mutation run writes the JSON report the `json` reporter is configured to produce. + + Vitest no longer reprints its full summary for every mutant. + +- Each of these packages now has a README, so its registry page says what the package is, how + to install it, and what to import or register — previously the page was blank. The lint + plugins show the configuration line that enables what they recommend. + + `@systemfsoftware/stryker-js-html-reporter` also carries its licence text + +- Published packages no longer carry build artifacts left over from earlier builds. One package was shipping about a megabyte of bundled test-runner internals this way. + +- Updated dependencies: + - @systemfsoftware/stryker-js@2.0.0 + +## 1.0.0 + +### Major Changes + +- Report generation moved to a plain kernel; the reporter service shape is unchanged + +### Patch Changes + +- Updated dependencies: + - @systemfsoftware/effect-cell-types@6.0.0 + - @systemfsoftware/stryker-js@1.0.0 + +## 0.1.2 + +### Patch Changes + +- Refreshed builds on the platform-services dependency graph; the packages no longer reach for host builtins directly. No CLI flags or option names change. + +- Updated dependencies: + - @systemfsoftware/stryker-js@0.2.0 + +## 0.1.1 + +### Patch Changes + +- Re-published against the oxc-based instrumenter: workspace dependency ranges move to the new instrumenter major; no package's own behavior changes in this release. + +## 0.1.0 + +### Major Changes + +- Three packages are renamed. `plugin-api` is now `@systemfsoftware/stryker-js`, the + language every plugin is written against. `mutation-run` is now + `@systemfsoftware/stryker-js-platform-node`, the Node host that runs a mutation + test. `mutation-report` is now `@systemfsoftware/stryker-js-html-reporter`. + Install the new names and change your imports. + + Options types moved. `StrykerOptions`, `PartialStrykerOptions` and `LogLevel` are + imported from the `Schema` export; `Mutant`, `MutantStatus`, `Position` and + `Location` from the `Mutant` export. Point a config's `extends` at the language + package's `Schema` export. + + `MutantStatus` accepts one spelling per outcome: `Killed`, `Survived`, + `NoCoverage`, `Timeout`, `CompileError`, `RuntimeError`, `Ignored` and `Pending`. + The lowercase and abbreviated forms — `killed`, `timedOut`, `noCoverage` and the + rest — are gone. A comparison against a removed spelling never matched the value + the reporter actually produced, so check any status comparison you wrote. + + Statuses, plugin kinds, exit classes and AST formats are string literal unions + rather than enums, so read them as their string values. Member access such as + `ExitClass.VerdictFail` no longer resolves. + + A plugin no longer receives a logger, and the logger port is gone. Plugins log + through Effect, and the host decides where that output goes. + + The bundled base preset is gone. A config inherits from the language package's + `Schema` export and states the thresholds, reporters and plugins it wants; you no + longer silently inherit a package manager, a plugin list or a break threshold. + +- effect is now a required peer dependency. Install it alongside these packages. They previously bundled their own copy, which meant two Effect instances in one process and services that could not find each other across the boundary. + +- Five entry points that were never API are gone. Each existed because another + package in this project found the code convenient, not because it was a surface + anyone should depend on. + + - The engine's version and its engine range now come from the package's own + entry point. + - The failure identities you catch come from that same entry point rather than a + separate one. + - `toRelativeNormalizedFileName` comes from there too. + - A timer, and a barrel of plugin internals, are no longer reachable. Report's + `makeEmptyTimer` is gone with them; a progress tally now carries the instant + the run started rather than a timer object. + + What is left is documented: an entry point is a name you may import and we may + not move without a major, and everything else is internal whatever file it sits + in. + +- Reporters are constructed by a factory and provided as a layer. + + The exported reporter classes are gone. Replace each `new` with the matching + factory — `makeClearTextReporter`, `makeHtmlReporter`, `makeJsonReporter`, + `makeProgressBarReporter`, `makeProgressStreamReporter`. + + Each factory takes the reporter's own options rather than an injected container, + and each operation returns an `Effect`. If you registered a reporter as a plugin, + declare it with `declarePlugin` and hand over a layer that provides `Reporter`. + + `drawClearTextScoreTable` is now exported for anyone rendering the score table + outside a reporter. + +### Patch Changes + +- New version is published through npm trusted publishing, so it carries a provenance attestation you can verify. + +- A mutation run writes the JSON report the `json` reporter is configured to produce. + + Vitest no longer reprints its full summary for every mutant. + +- Each of these packages now has a README, so its registry page says what the package is, how + to install it, and what to import or register — previously the page was blank. The lint + plugins show the configuration line that enables what they recommend. + + `@systemfsoftware/stryker-js-html-reporter` also carries its licence text + +- Published packages no longer carry build artifacts left over from earlier builds. One package was shipping about a megabyte of bundled test-runner internals this way. + +- Updated dependencies: + - @systemfsoftware/effect-cell-types@5.0.0 diff --git a/packages/stryker-js-instrumenter/CHANGELOG.md b/packages/stryker-js-instrumenter/CHANGELOG.md new file mode 100644 index 000000000..5b01bcf1a --- /dev/null +++ b/packages/stryker-js-instrumenter/CHANGELOG.md @@ -0,0 +1,449 @@ +# @systemfsoftware/stryker-js-instrumenter + +## 12.1.1 + +### Patch Changes + +- Updated dependencies: + - @systemfsoftware/stryker-js-plugin-interface@14.0.0 + +## 12.1.0 + +### Minor Changes + +- `Span`, `ScriptOrigin`, `Offset` and `LineStarts` are now exported from the package root, beside their existing `Source` namespace grouping, so a consumer can import them by name. + +### Patch Changes + +- Updated dependencies: + - @systemfsoftware/stryker-js-cli-contract@0.3.0 + - @systemfsoftware/stryker-js-plugin-interface@13.0.0 + +## 12.0.0 + +### Major Changes + +- Effect moves to the stable `4.0.0` release, together with the `@effect/*` packages these libraries use. The `4.0.0` release candidates are no longer supported: install `effect` `^4.0.0` next to these packages before upgrading. + + The TypeScript checker and test-runner plugins bundle their own Effect runtime, so they need no change in your project. + +### Patch Changes + +- Updated dependencies: + - @systemfsoftware/stryker-js-cli-contract@0.2.0 + - @systemfsoftware/stryker-js-plugin-interface@12.0.0 + +## 11.0.0 + +### Major Changes + +- Mutant identity, status, location, coverage and run options are re-exported from `@systemfsoftware/stryker-js-plugin-interface` as `Mutant`; `Mutator` now holds the registry, catalog and selection, and `Source` holds `LineStarts`, `Offset`, `ScriptOrigin` and `Span`. A mutant switch on a conditional's test is printed in parentheses now, so a ternary-test mutant no longer replaces the whole conditional. Mutants ignored by a `// Stryker disable` directive no longer shift the placed replacements beside them, so every activated arm runs its own replacement. + + Import the moved identity types from the plugin interface. + +### Patch Changes + +- Updated dependencies: + - @systemfsoftware/stryker-js-plugin-interface@11.0.0 + +## 10.0.1 + +### Patch Changes + +- The packages now depend on `@systemfsoftware/effect-cell-types` 11. + + - Every tagged error now has a one-line message built from its fields, so a failure names the file, mutant, plugin, worker or exit code involved instead of an empty message. The errors' tags, fields and encoded forms are unchanged. + +## 10.0.0 + +### Major Changes + +- Rendering a caught cause is a function now, not a method on a schema class. `ErrorText.errorTextOf(cause)` and `ErrorText.causeTextOf(cause)` replace `ErrorText.ErrorText.fromCause(cause)` and `ErrorText.CauseText.fromCause(cause)`. + + Replace the two calls: `ErrorText.ErrorText.fromCause(cause)` becomes `ErrorText.errorTextOf(cause)`, and `ErrorText.CauseText.fromCause(cause)` becomes `ErrorText.causeTextOf(cause)`. Both return the same `Option.Option` and `Option.Option` as before. The `ErrorText` and `CauseText` classes, their value types, and `ErrnoException` are unchanged. + +- File names handed to `instrument` are canonicalized: a backslash in an input path is folded to `/`, in the returned file names and in each mutant's file name alike. A result now reports one spelling of a path, matching the spelling `Mutant.fileName` already used, instead of echoing whatever separator the caller passed. + + No call-site change is needed — keep passing plain strings to `instrument`. + +- Mutant ids are decimal index strings. Every schema that carried a mutant id as a plain string now decodes `Mutant.MutantId` and refuses anything else: the mutation report and its mutant results, reporter events (`mutationTestingPlanReady` plans, `mutantTested`), the machine run stream (`mutant` and `verdict`), the survivors prior report, and the mutant coverage keys produced by test runners. + + Mutant ids are minted only as the mutant's canonical non-negative decimal index (`Mutant.MutantId`), including in the instrumenter's planned mutants, placement sites, and checker answers. Consume the new `Mutant.MutantId` schema instead of assuming an arbitrary non-empty string; ids such as `constructor` or `__proto__` no longer decode. + +- A mutant's status is declared once, as the eight-value `Mutant.MutantStatus`, plus the named subsets a run partitions on: `Mutant.SurvivorStatus`, `Mutant.RememberedStatus`, `Mutant.EphemeralStatus`, and `Mutant.ActionableStatus`. Import the subset your decision matches instead of re-listing the statuses you accept. + + Mutants are tagged structs now. A status reason decodes only together with a status, and the remembered-mutant and ignored-mutant rows carry the same narrowed status as the subset they were selected by. + +- Source locations are 1-based and validated end to end. `Mutant.Location`, `Mutant.Position`, `Mutant.Span`, `Mutant.OpenEndLocation`, `Mutant.ScriptOrigin`, and `Mutant.LineStarts` replace the former `LocationSchema`, `PositionSchema`, and `OpenEndLocationSchema`; a location whose end precedes its start, or a line or column below 1, no longer decodes. + + - A mutant location always speaks the report contract, so a producer that minted 0-based coordinates or a bare number must switch to the new schemas. + - An embedded script's origin is a `ScriptOrigin` (1-based line plus a column shift), not a position. + - Incremental state from releases that stored the earlier column base is no longer matched: those mutants are re-tested, not reused, while the run rewrites the state. + +### Patch Changes + +- Stages now run as cells over workflows, multi-item work runs as Effect streams, pools and worker transport use scoped Effect resources, and named operations are traced with Effect.fn. The Angular ignorer now declares @systemfsoftware/stryker-ignorer-kit as a runtime dependency. + +## 9.0.1 + +### Patch Changes + +- The packages now build on the latest `@systemfsoftware/effect-cell-types` 10.2 cell kinds, with no change to their published behavior. + + - `@systemfsoftware/stryker-test-contribution` exports its evaluator as `Judge`, `TestContribution`, and `TestContributionEvaluator`, and now depends on `effect` directly. + +## 9.0.0 + +### Major Changes + +- Mutant ids, mutator names and file names are now branded schemas, and the string helpers are replaced by codecs. + + - Build ids and names with `MutantId`, `MutatorName` and `CanonicalFileName`. Empty values are refused. + - Replace `errorToString` with `ErrorText.fromCause` and `causeText` with `CauseText.fromCause`. Both return an `Option`. + - Replace `normalizeFileName` with a `CanonicalFileName` decode, and `isMutant` with `Schema.is(Mutant)`. + - Read the values formerly in `INSTRUMENTER_CONSTANTS` from the `InstrumenterContext` statics. + +- Each package's main entry point now groups its exports into namespaces named after a capability, such as `Plugin`, `TestRunner` and `Report`. + + - Import the namespace and qualify each name, for example `Plugin.TestRunnerRpcs` after importing `Plugin` from the plugin interface. + - Import instrumenter schemas such as `Location` and `MutantStatus` from the instrumenter's `Mutant` namespace, and plugin-interface names from the plugin interface, instead of through another package's entry point. + - The engine's `/config`, `/events` and `/promises` entry points are unchanged. + +- HTML templates and Svelte components are no longer instrumented by this package + on their own: each format now comes from its framework plugin, the Angular + plugin for `.html`, `.htm`, and `.vue` and the Svelte plugin for `.svelte`, both + published alongside this release. Install the plugin whose format your project + uses and add it to `plugins`. A file whose extension no loaded format claims is + reported in the run's skipped files instead of being instrumented, and `svelte` + is no longer an optional peer dependency of this package; the + `angular-html-parser` dependency moves to the Angular plugin. + + The Angular signal ignore rule is unaffected and continues to ship in + `@systemfsoftware/stryker-ignorer-angular`. + +- The format registry can now be extended with a framework plugin's contribution: + `frameworkEntryOf` adapts a `Framework` into a registry entry whose parse, + transform, print, and disable-type-checks hooks delegate to the plugin over its + embedded document, and the AST union gains the embedded-document variant those + entries parse to. + + A registry entry you build yourself must now declare the `owner` module of its + format, the `ownerVersion` incremental state keys on, and a `transform` hook; an + entry without them no longer type-checks. + +### Patch Changes + +- Every mutant now reports its position as 1-based coordinates — the base the + mutation-testing report schema uses, where the first line of a file and the + first character of a line both sit at the first position. + + A mutant in a plain TypeScript or JavaScript file previously reported a line one + below its real position, so pointing a reader at the reported coordinates + highlighted the wrong line. + +## 8.1.0 + +### Minor Changes + +- Three opt-in mutators plant Effect concurrency faults on top of the default set, so a mutation run can check that your concurrency tests catch races. `AtomicUpdateSplit` splits read-modify-write calls on `Ref` and `SynchronizedRef` into a separate read, a yield, and a separate write. `SynchronizationRemoval` removes semaphore permits, `Effect.uninterruptible`, and `Effect.uninterruptibleMask`. `FinalizerEscape` stops `ensuring`, `onExit`, `onError`, `onInterrupt`, `acquireRelease`, and `acquireUseRelease` cleanup from running on interruption. All three are off by default. Enable any subset by listing them under `mutator: { optInMutations: [...] }` in your configuration. A name that is not an opt-in mutator fails the run, and the error lists the known names. The replacements use Effect 4 APIs, so a repository on Effect 3.x must not opt in. + +### Patch Changes + +- Disable comments with the `next-line` scope now ignore the mutants on the line directly below the comment and record the given reason. Before this fix they ignored nothing. + +## 8.0.2 + +### Patch Changes + +- Exported declarations that previously took `unknown` now take a defaulted type parameter: calls that omit the type argument are unchanged, and calls that were previously rejected for passing an unconstrained value are accepted. No runtime behaviour changes. + +## 8.0.1 + +### Patch Changes + +- Effect moves to `4.0.0-rc.116` (with `@effect/platform-node`, `@effect/platform-node-shared`, `@effect/vitest` and `@effect/opentelemetry` on the same release), the `@systemfsoftware/*` toolchain pins move to their current releases, and `vitest` 5 with `oxc-parser` 0.150 come along. Both worker bundles (`stryker-js-typescript-checker`, `stryker-js-vitest-runner`) now inline their whole runtime rather than resolving `effect`, `@effect/*` and the sibling packages from the host's tree, so a worker runs on its own copy of the effect that built it; the project's `typescript` and `vitest` remain the only imports beside Node builtins. The filter-level `arbitrary.candidate` generators that the thresholds schemas carried are gone: v4 no longer reads them, so thresholds still reject a `low` above `high` and the property tests generate the pair from the schema again. Log calls inside `Effect.catch*` handlers moved to `Effect.tapError`/`Effect.tapCause` at the same level and message. `@systemfsoftware/stryker-ignorer-interface` re-exports the 0.150 AST, where `FormalParameterRest.decorators` is now `Array`. + +- Mutating a negated property check such as `if (!obj.prop)` no longer aborts the run with a placement error. Mutations inside `as const` expressions are generated again; previously every literal inside an `as const` object or array was silently skipped, which inflated the reported mutation score. + +## 8.0.0 + +### Major Changes + +- The built-in reporters, plugin loading and their types are no longer published + from separate entry points on `@systemfsoftware/stryker-js`; import them from + `@systemfsoftware/stryker-js`. The mutant vocabulary — `Mutant`, `Location`, + `Position` and their schemas — is importable only from + `@systemfsoftware/stryker-js-instrumenter`. + +## 7.1.0 + +### Minor Changes + +- Types that appear on the published CLI and instrumenter APIs are now exported. + +## 7.0.0 + +### Major Changes + +- `disableTypeChecks` and `instrument` now return lazy `Effect` values instead of a `Promise` and a plain result, so a caller decides when the transformation runs: `disableTypeChecks(file)` is `Effect` and `instrument(files, options)` is `Effect`. Interpret them with your own runtime — `Effect.runPromise(disableTypeChecks(file))` reproduces the old await. + + The Angular signal ignorer moved out of this package to `@systemfsoftware/stryker-ignorer-angular`, and the `strykerPlugins` and `frameworkPluginsFileUrl` exports are gone. A plugin package discovers its ignorers through the ignorer contract instead. + +### Minor Changes + +- `instrument` accepts an optional `basePath` used to relativize placement-error paths instead of the process working directory. Pass the run's base path; omitting it keeps absolute file names in error messages. + +- `angularIgnorer`'s decision now receives `(node, ancestors)` — the node and its + ancestors as typed positions from the ignorer contract — instead of a path + object. The node vocabulary in this package's declarations is the one published + by `@systemfsoftware/stryker-ignorer-interface`, which is now a dependency. + +### Patch Changes + +- Maintenance release. Every published entry point, export, option, and behaviour is exactly as it was. + +- effect dependency range widens from the exact 4.0.0-rc.112 pin to ^4.0.0-rc.112; repository metadata now points at stryker-js-effect + +## 6.0.1 + +### Patch Changes + +- Imports rewired to the collapsed `@systemfsoftware/stryker-js` root entry; each package now co-releases against the root-entry major. + +- Updated dependencies: + - @systemfsoftware/effect-cell-types@8.0.0 + - @systemfsoftware/stryker-js@4.0.0 + +## 6.0.0 + +### Major Changes + +- Nothing a consumer can observe moved in the four packages above, so no release is warranted. + + `@systemfsoftware/stryker-js-instrumenter` now answers with promises: `transform` and `placeHeader` return `Promise`, and `AstTransformer` is a promise-returning function type. Await them where you call them. The parser is read on first use rather than at import, so importing this package no longer constructs Node's WebAssembly runtime, and `ExperimentalWarning: WASI` no longer appears until something is actually parsed. + +### Patch Changes + +- Releases the workspace so its published versions track the shared dependency graph this change moves. + +- The `@systemfsoftware/source` export condition is gone. It resolved to a package's TypeScript sources for editors and in-repo typechecks; each package now exports only its built entry. If your tsconfig sets `customConditions: ["@systemfsoftware/source"]`, or a bundler config names that condition, remove it — resolution falls back to the built entry, which is what every consumer already got. + +## 5.0.2 + +### Patch Changes + +- Updated dependencies: + - @systemfsoftware/effect-cell-types@7.0.0 + +## 5.0.1 + +### Patch Changes + +- Re-released against @systemfsoftware/stryker-js without the removed --llms manifest. The Run stream no longer carries a manifest terminal event, and the RunEvent / RunTerminalEvent unions no longer include the manifest arm, so any exhaustive consumer of those types must drop that case. + +- Rebuilds against updated workspace dependencies, including the new + `@systemfsoftware/stryker-js` reporter protocol major. + +- Updated dependencies: + - @systemfsoftware/stryker-js@3.0.0 + +## 5.0.0 + +### Major Changes + +- The packages are renamed. `plugin-api` is now `@systemfsoftware/stryker-js`, the + language every plugin is written against. `mutation-run` is split: the run + itself is `@systemfsoftware/stryker-js-engine` + (host-neutral, no Node on its manifest) and the Node process entries are + `@systemfsoftware/stryker-js-cli`, which owns the worker files and the runtime + gate. `mutation-report` is now `@systemfsoftware/stryker-js-html-reporter`. + `@systemfsoftware/stryker-js-platform-node` is never published — do not install + it. Install the new names and change your imports. + + Options types moved. `StrykerOptions`, `PartialStrykerOptions` and `LogLevel` are + imported from the `Schema` export; `Mutant`, `MutantStatus`, `Position` and + `Location` from the `Mutant` export. Point a config's `extends` at the language + package's `Schema` export. + + `MutantStatus` accepts one spelling per outcome: `Killed`, `Survived`, + `NoCoverage`, `Timeout`, `CompileError`, `RuntimeError`, `Ignored` and `Pending`. + The lowercase and abbreviated forms — `killed`, `timedOut`, `noCoverage` and the + rest — are gone. A comparison against a removed spelling never matched the value + the reporter actually produced, so check any status comparison you wrote. + + Statuses, plugin kinds, exit classes and AST formats are string literal unions + rather than enums, so read them as their string values. Member access such as + `ExitClass.VerdictFail` no longer resolves. + + A plugin no longer receives a logger, and the logger port is gone. Plugins log + through Effect, and the host decides where that output goes. + + The bundled base preset is gone. A config inherits from the language package's + `Schema` export and states the thresholds, reporters and plugins it wants; you no + longer silently inherit a package manager, a plugin list or a break threshold. + +### Patch Changes + +- Peer Effect requirement advances to 4.0.0-rc.112. No API changes. + +- The shared helpers package is gone. Nothing installs it any more, and the + handful of helpers worth sharing now live in the plugin contract next to the + types they serve: + + - `strykerReportBugUrl`, `normalizeFileName`, `propertyPath`, `errorToString` + and `isErrnoException` from `@systemfsoftware/stryker-js/core` + - `noopLogger` from `@systemfsoftware/stryker-js/logging` + - `testFilesProvided` from `@systemfsoftware/stryker-js/test-runner` + + If you imported any of those, change the specifier. Everything else it exported + had no consumer and is removed: use `Predicate.isNotNullish` from Effect in place + of `notEmpty`, and `RegExp.escape` in place of `escapeRegExp`. + +- These packages no longer install dependencies they never imported, so installing them pulls less into your tree. + + `tslib` is gone from all six. The mutation runner additionally stops installing `lodash.groupby`, `semver` and `source-map`, and the command line interface stops installing `@effect/platform-node-shared`. Nothing exported changes. + +- Published packages no longer carry build artifacts left over from earlier builds. One package was shipping about a megabyte of bundled test-runner internals this way. + +- Updated dependencies: + - @systemfsoftware/stryker-js@2.0.0 + +## 4.0.0 + +### Major Changes + +- instrument() keeps its public signature; the decision moved to a plain kernel, and the instrument workflow now distinguishes in-place from ephemeral output + +### Patch Changes + +- Updated dependencies: + - @systemfsoftware/effect-cell-types@6.0.0 + - @systemfsoftware/stryker-js@1.0.0 + +## 3.0.1 + +### Patch Changes + +- Refreshed builds on the platform-services dependency graph; the packages no longer reach for host builtins directly. No CLI flags or option names change. + +- Updated dependencies: + - @systemfsoftware/stryker-js@0.2.0 + +## 3.0.0 + +### Major Changes + +- The instrumenter parses and prints with oxc and a bundled ESTree printer; all Babel packages are gone. The instrumenter options no longer accept a `plugins` list (oxc parses modern JS/TS, JSX, and decorators natively), the instrumentation header export is named `instrumentationHeader`, and the script transformer is named `transformScript`. + +## 2.0.1 + +### Patch Changes + +- Peer Effect requirement advances to 4.0.0-rc.112. No API changes. + +## 2.0.0 + +### Major Changes + +- Three packages are renamed. `plugin-api` is now `@systemfsoftware/stryker-js`, the + language every plugin is written against. `mutation-run` is now + `@systemfsoftware/stryker-js-platform-node`, the Node host that runs a mutation + test. `mutation-report` is now `@systemfsoftware/stryker-js-html-reporter`. + Install the new names and change your imports. + + Options types moved. `StrykerOptions`, `PartialStrykerOptions` and `LogLevel` are + imported from the `Schema` export; `Mutant`, `MutantStatus`, `Position` and + `Location` from the `Mutant` export. Point a config's `extends` at the language + package's `Schema` export. + + `MutantStatus` accepts one spelling per outcome: `Killed`, `Survived`, + `NoCoverage`, `Timeout`, `CompileError`, `RuntimeError`, `Ignored` and `Pending`. + The lowercase and abbreviated forms — `killed`, `timedOut`, `noCoverage` and the + rest — are gone. A comparison against a removed spelling never matched the value + the reporter actually produced, so check any status comparison you wrote. + + Statuses, plugin kinds, exit classes and AST formats are string literal unions + rather than enums, so read them as their string values. Member access such as + `ExitClass.VerdictFail` no longer resolves. + + A plugin no longer receives a logger, and the logger port is gone. Plugins log + through Effect, and the host decides where that output goes. + + The bundled base preset is gone. A config inherits from the language package's + `Schema` export and states the thresholds, reporters and plugins it wants; you no + longer silently inherit a package manager, a plugin list or a break threshold. + +### Patch Changes + +- Selecting `workflow-make-boundary` keeps mutants inside `Workflow.make` decision bodies. + + Ignore plugins are asked about each mutant, not about the file root with a subtree latch. An inverted selector that answers "ignore" for everything outside a make body therefore no longer ignores the make body itself. Inner mutants of declaration-style ignore plugins (`effect-schema-declarations`, Angular signal option objects) are still ignored. + +- Instrumenting a file that calls a method named after an `Object.prototype` + member - `toString`, `valueOf`, `constructor` and the rest - no longer fails + with `Property name expected type of string but got function`. The method + mutator's replacement table answered such a lookup with the inherited function + rather than reporting no replacement, and a single `.toString()` call was enough + to stop the run. Those methods are now left alone, as they always should have + been. + +- Updated dependencies: + - @systemfsoftware/effect-cell-types@5.0.0 + +## 1.0.0 + +### Major Changes + +- Parse, transform and mutant-placement failures now carry a message naming the + file and what went wrong, and their `cause` survives being written to JSON — so + a failure that crossed a process boundary no longer arrives blank. + + Every error tag is now qualified, which is what makes two identically named + errors from different packages distinguishable. + + `MutantPlacementFailed` is removed; it was a second name for `PlacementFailed` + and had no constructor anywhere. Match `PlacementFailed`. + +- svelte is now an optional peer dependency. Install it to mutate .svelte components; without it, every other file type is unaffected. A copy of the Svelte compiler used to be bundled in, which pinned whichever version was present when the package was built and made the compiler version check read the wrong answer. + +### Patch Changes + +- The instrumenter no longer depends on `weapon-regex`, a Scala library compiled to JavaScript that is no longer maintained. It is replaced by `@eslint-community/regexpp`, which has no dependencies of its own. Regular expression mutants are unchanged, so no action is required. + +- The shared helpers package is gone. Nothing installs it any more, and the + handful of helpers worth sharing now live in the plugin contract next to the + types they serve: + + - `strykerReportBugUrl`, `normalizeFileName`, `propertyPath`, `errorToString` + and `isErrnoException` from `@systemfsoftware/stryker-js-plugin-api/core` + - `noopLogger` from `@systemfsoftware/stryker-js-plugin-api/logging` + - `testFilesProvided` from `@systemfsoftware/stryker-js-plugin-api/test-runner` + + If you imported any of those, change the specifier. Everything else it exported + had no consumer and is removed: use `Predicate.isNotNullish` from Effect in place + of `notEmpty`, and `RegExp.escape` in place of `escapeRegExp`. + +- These packages no longer install dependencies they never imported, so installing them pulls less into your tree. + + `tslib` is gone from all six. The mutation runner additionally stops installing `lodash.groupby`, `semver` and `source-map`, and the command line interface stops installing `@effect/platform-node-shared`. Nothing exported changes. + +- When a mutant cannot be placed, the reported error now links to this project's issue tracker rather than the upstream StrykerJS one. + +- Published packages no longer carry build artifacts left over from earlier builds. One package was shipping about a megabyte of bundled test-runner internals this way. + +- Updated dependencies: + - @systemfsoftware/stryker-js-plugin-api@3.0.0 + +## 0.2.0 + +### Minor Changes + +- Two new packages complete this scope's mutation toolchain: the instrumenter, + which places mutants and coverage hooks in source files, and the shared helpers + the toolchain uses at runtime. Both are installed for you as dependencies of the + engine — install them directly only if you assemble the pipeline yourself. + +### Patch Changes + +- Updated dependencies: + - @systemfsoftware/stryker-js-util@0.2.0 diff --git a/packages/stryker-js-plugin-interface/CHANGELOG.md b/packages/stryker-js-plugin-interface/CHANGELOG.md new file mode 100644 index 000000000..4fb3db579 --- /dev/null +++ b/packages/stryker-js-plugin-interface/CHANGELOG.md @@ -0,0 +1,276 @@ +# @systemfsoftware/stryker-js-plugin-interface + +## 14.0.0 + +### Major Changes + +- A mutant's timeout now starts when its first test begins, not when the test runner is asked to run it. With a short `timeoutMS`, a mutant its tests would kill is reported `Killed` instead of `Timeout`, so a slow or busy machine no longer changes the verdict. A mutant whose tests never begin still ends, on the same window that bounds a plugin worker which never accepts its connection. + + If you author a test-runner plugin, `mutantRun` now streams its run: emit `MutantRunStarted` before the runner's first test begins, then `MutantRunSettled` carrying the result. + +## 13.0.0 + +### Major Changes + +- The TypeScript checker now applies Trivial Compiler Equivalence (Papadakis et al., ICSE 2015). Each mutant of a file is emitted to JavaScript from the project's own TypeScript configuration in transpile-only mode, normalized, and compared with the original file's emit and with the mutants already emitted at the same site. A mutant whose emit equals the original's is dropped as `Ignored` with reason `equivalent-to-original: tce`; one that equals an earlier same-site mutant's is dropped with `duplicate-at-site: tce`; every mutant whose emit differs is kept, so the check never removes a mutant that changes the program. Checkers gain an `ignored` result variant carrying the suppression reason, and a run publishes the new `tce` machine-stream event with the `equivalentToOriginal` and `duplicateAtSite` counts. Verdict semantics move to version 3. + +### Minor Changes + +- Mutation testing is now incremental by default: `incremental` defaults to `true`, so an unchanged mutant whose covering tests are unchanged is re-used instead of re-run. `--full` (aliasing the old `--force`) re-verifies every mutant, ignoring the cache and the persisted dry run. The `verdict` stream event records `incrementalMode` (`incremental` or `full`). A run with no reusable cache falls back to a full run as before. + +## 12.0.0 + +### Major Changes + +- Effect moves to the stable `4.0.0` release, together with the `@effect/*` packages these libraries use. The `4.0.0` release candidates are no longer supported: install `effect` `^4.0.0` next to these packages before upgrading. + + The TypeScript checker and test-runner plugins bundle their own Effect runtime, so they need no change in your project. + +## 11.1.0 + +### Minor Changes + +- The new `surfacing` option (default `{ perLine: 1, perFile: 7 }`) caps how many survivors are listed per line and per file, while every survivor still counts toward the score. + +### Patch Changes + +- Fixed mutant coverage whose hit counts were keyed by anything other than a mutant id being silently emptied on decode — such a payload now fails validation, so a mismatched runner/plugin pair reports the key mismatch instead of planning every mutant as uncovered. + +## 11.0.0 + +### Major Changes + +- The plugin contract now owns mutant identity, the mutator catalog and the report. `Mutant` holds the status enum and its subsets, `MutantId`, `MutatorName`, `CanonicalFileName`, `Location`, `Position` and `OpenEndLocation`. `MutatorCatalog` declares a catalog entry and refuses one with no examples, an empty `before` snippet, a duplicated id or name, or a name its provider does not own; `MutatorProvider` declares the `strykerMutators` contribution a mutator plugin exports; and `Report` carries the report contract generated from the pinned upstream `mutation-testing-report-schema` plus this product's fields. + + Report mutant ids follow the upstream schema and decode as strings, so a report another tool wrote decodes too; report numbers are finite, so `NaN` and `Infinity` are refused. + + `Location` and `OpenEndLocation` decode from a plain `{ start, end }` struct, so a JSON Schema document derived from them states the position fields instead of an empty schema. + + Import mutant identity, status, location, catalog and report types from here; the instrumenter and the engine no longer re-export them. + +## 10.0.1 + +### Patch Changes + +- The packages now depend on `@systemfsoftware/effect-cell-types` 11. + + - Every tagged error now has a one-line message built from its fields, so a failure names the file, mutant, plugin, worker or exit code involved instead of an empty message. The errors' tags, fields and encoded forms are unchanged. + +## 10.0.0 + +### Major Changes + +- Exit codes are one value: `Plugin.ExitCode`, an integer between 0 and 255. The machine stream's failure event, the command's run output, and the run conclusion all carry it, and a run conclusion's outcome is the closed set of run-outcome tags rather than free text. + + `Plugin.ExitCodeFromClass` stays the single exit-class to baseline-code mapping; read a code through it instead of writing the number where an exit class has to become an exit code. + +- Building mutation metrics from mutants is a function now: `Report.Metrics.fromMutants(mutants)` becomes `Report.metricsFromMutants(mutants)`. Its `mutants` argument is typed as mutants carrying a `MutantStatus`, so a plain `{ status: string }` is refused. + + The `Metrics` class is unchanged: same counts, same `totalMutants`/`mutationScore`/`mutationScoreBasedOnCoveredCode` getters, same `MetricsResult` shape. + +- Mutant ids are decimal index strings. Every schema that carried a mutant id as a plain string now decodes `Mutant.MutantId` and refuses anything else: the mutation report and its mutant results, reporter events (`mutationTestingPlanReady` plans, `mutantTested`), the machine run stream (`mutant` and `verdict`), the survivors prior report, and the mutant coverage keys produced by test runners. + + Mutant ids are minted only as the mutant's canonical non-negative decimal index (`Mutant.MutantId`), including in the instrumenter's planned mutants, placement sites, and checker answers. Consume the new `Mutant.MutantId` schema instead of assuming an arbitrary non-empty string; ids such as `constructor` or `__proto__` no longer decode. + +- A mutant's status is declared once, as the eight-value `Mutant.MutantStatus`, plus the named subsets a run partitions on: `Mutant.SurvivorStatus`, `Mutant.RememberedStatus`, `Mutant.EphemeralStatus`, and `Mutant.ActionableStatus`. Import the subset your decision matches instead of re-listing the statuses you accept. + + Mutants are tagged structs now. A status reason decodes only together with a status, and the remembered-mutant and ignored-mutant rows carry the same narrowed status as the subset they were selected by. + +- `Reporter.MutantTested` names the tested mutant's fields after the mutant itself: `file` is now `fileName` and `mutator` is now `mutatorName`. `id`, `status`, `location`, `replacement`, `completed` and `total` keep their roles, and the mutation stream's emitted lines are unchanged. + + Replace `mutant.file` with `mutant.fileName` and `mutant.mutator` with `mutant.mutatorName` where you build or read a `MutantTested`. + +- Counts and durations that cannot be negative are refined where they are declared, through the shared non-negative integer and non-negative finite schemas. Test-runner counts and durations, the clear-text reporter's log limit, the dry-run timeout, a test run's hit counter and limit, and a run's help-error count now refuse a negative value instead of accepting it. + +- Plugin boundaries carry their real values: + + - `Plugin.ReporterInitOptions.traceparent` and `tracestate` are typed by the W3C trace-context schemas (`Trace.Traceparent`, `Trace.Tracestate`) instead of plain strings, and an invalid traceparent is no longer forwarded to a reporter worker. + - `Checker.CheckResultSchema` is the one source of a check result; `Checker.CheckStatus` is unchanged in value. + - `Plugin.PluginKind` and `Plugin.EvaluatorPluginKind` are new and replace the repeated `'Evaluator'` literal. + - The `maxConcurrentTestRunners`, `maxTestRunnerReuse`, `timeoutMS`, `timeoutFactor` and `concurrency` options refuse values outside their real range (negative durations, a zero or fractional worker count). + +- Source locations are 1-based and validated end to end. `Mutant.Location`, `Mutant.Position`, `Mutant.Span`, `Mutant.OpenEndLocation`, `Mutant.ScriptOrigin`, and `Mutant.LineStarts` replace the former `LocationSchema`, `PositionSchema`, and `OpenEndLocationSchema`; a location whose end precedes its start, or a line or column below 1, no longer decodes. + + - A mutant location always speaks the report contract, so a producer that minted 0-based coordinates or a bare number must switch to the new schemas. + - An embedded script's origin is a `ScriptOrigin` (1-based line plus a column shift), not a position. + - Incremental state from releases that stored the earlier column base is no longer matched: those mutants are re-tested, not reused, while the run rewrites the state. + +- Test identifiers are one value: `TestRunner.TestId`, a non-empty branded string carrying the runner's `file#test name` form. Test runner results, report test definitions, a mutant's killers and coverers, per-test hit records, and the test-contribution evaluation all speak that one type instead of bare strings. + + Mint one with `TestRunner.TestId.make(...)` where a runner derives a test id; the machine stream and mutation report schemas brand the ids they decode. + +- Mutation score thresholds are declared once, as `Report.ThresholdsSchema`: `high`, `low`, and a `break` that is null when no breaking threshold is configured. The option file and the mutation report decode the same schema, and a pair whose `low` is above its `high` is refused with "a mutation score threshold pair has low at or below high". + + The mutation report's thresholds now include `break`, so a consumer reads the breaking threshold from the report instead of recovering it from the embedded reporter configuration. + +### Patch Changes + +- Stages now run as cells over workflows, multi-item work runs as Effect streams, pools and worker transport use scoped Effect resources, and named operations are traced with Effect.fn. The Angular ignorer now declares @systemfsoftware/stryker-ignorer-kit as a runtime dependency. + +- Updated dependencies: + - @systemfsoftware/stryker-js-instrumenter@10.0.0 + +## 9.0.0 + +### Major Changes + +- `Report.Metrics#mutationScore` and `Report.Metrics#mutationScoreBasedOnCoveredCode` now return a `Report.MutationScore` instead of a number: `Scored` with a `percentage` between 0 and 100, or `Unscored` when no tested mutant counts toward the score. Previously an empty run produced `NaN`, which compared false against every threshold. + + Code that read the score as a number must match on it: + + ```ts + // before + const failing = metrics.mutationScore < 80 + + // after + const failing = Report.MutationScore.match(metrics.mutationScore, { + Scored: ({ percentage }) => percentage < 80, + Unscored: () => false, + }) + ``` + +## 8.0.0 + +### Major Changes + +- The in-process `vm` runner is now the default `testRunner`. With no `testRunner` and no `testFiles` configured, the vm runner asks Vitest which files are tests. It runs exactly the files `vitest run` would, including your config's `include`, `exclude`, and `includeSource`, or Vitest's defaults when there is no config file. A run that loads no test files, or whose initial run registers zero tests, now fails the dry run with an error naming the `vm` runner and pointing at `testFiles`, instead of reporting a successful run where every mutant survives. Projects that relied on the previous default shelling out to a test command must set `testRunner: 'command'` (or `'vitest'`) to keep that behaviour. + +## 7.3.0 + +### Minor Changes + +- Three opt-in mutators plant Effect concurrency faults on top of the default set, so a mutation run can check that your concurrency tests catch races. `AtomicUpdateSplit` splits read-modify-write calls on `Ref` and `SynchronizedRef` into a separate read, a yield, and a separate write. `SynchronizationRemoval` removes semaphore permits, `Effect.uninterruptible`, and `Effect.uninterruptibleMask`. `FinalizerEscape` stops `ensuring`, `onExit`, `onError`, `onInterrupt`, `acquireRelease`, and `acquireUseRelease` cleanup from running on interruption. All three are off by default. Enable any subset by listing them under `mutator: { optInMutations: [...] }` in your configuration. A name that is not an opt-in mutator fails the run, and the error lists the known names. The replacements use Effect 4 APIs, so a repository on Effect 3.x must not opt in. + +## 7.2.1 + +### Patch Changes + +- Exported declarations that previously took `unknown` now take a defaulted type parameter: calls that omit the type argument are unchanged, and calls that were previously rejected for passing an unconstrained value are accepted. No runtime behaviour changes. + +## 7.2.0 + +### Minor Changes + +- A finite mutant is killed or survived from its tests. Timeout is reserved for one named nonterminating trap, detected by a hit bound rather than a wall-clock budget. A wall-clock timeout fails the run instead of being stored as a detected mutant. + +## 7.1.1 + +### Patch Changes + +- Effect moves to `4.0.0-rc.116` (with `@effect/platform-node`, `@effect/platform-node-shared`, `@effect/vitest` and `@effect/opentelemetry` on the same release), the `@systemfsoftware/*` toolchain pins move to their current releases, and `vitest` 5 with `oxc-parser` 0.150 come along. Both worker bundles (`stryker-js-typescript-checker`, `stryker-js-vitest-runner`) now inline their whole runtime rather than resolving `effect`, `@effect/*` and the sibling packages from the host's tree, so a worker runs on its own copy of the effect that built it; the project's `typescript` and `vitest` remain the only imports beside Node builtins. The filter-level `arbitrary.candidate` generators that the thresholds schemas carried are gone: v4 no longer reads them, so thresholds still reject a `low` above `high` and the property tests generate the pair from the schema again. Log calls inside `Effect.catch*` handlers moved to `Effect.tapError`/`Effect.tapCause` at the same level and message. `@systemfsoftware/stryker-ignorer-interface` re-exports the 0.150 AST, where `FormalParameterRest.decorators` is now `Array`. + +## 7.1.0 + +### Minor Changes + +- A checker that fails now fails mutation testing with that checker's cause, instead of crashing with an empty error. The TypeScript checker type-checks each mutant, so compile errors appear in the report. Verdict counts are non-negative integers; `Metrics` is a class you can build from mutant statuses; a threshold `low` may not exceed `high`. + +## 7.0.0 + +### Major Changes + +- The checker request carries a plain data record, not the instrumenter's `Mutant` class. `CheckerRequest.mutants` and `CheckerService.check`/`group` now speak `CheckerMutantWire` - `id`, `fileName`, `mutatorName`, `replacement`, and `location`. A mutant that cannot be described to a checker is skipped instead of reaching it. + + With `OTEL_ENABLED=true` the CLI now exports its OpenTelemetry metrics to `OTEL_EXPORTER_OTLP_ENDPOINT`, and `OTEL_METRIC_EXPORT_INTERVAL` sets the export interval in milliseconds. Checker timings, mutant counts, and worker crashes now reach a metrics backend. + + `ConfigEnv` and `resolveExtends` are no longer exported. Import `ConfigEnv` from the config entry point of the package, and read merged options with `loadConfigCell` or `readConfig` from the package root - either performs the `extends` walk, validation and merge in order. + +## 6.0.1 + +### Patch Changes + +- Updated dependencies: + - @systemfsoftware/stryker-js-instrumenter@8.0.0 + +## 6.0.0 + +### Major Changes + +- `testRunner`, `checkers`, and `ignorers` now carry the plugin and its options together. + + - `testRunner` is `'command'`, `'vm'`, or `{ plugin, options?, nodeArgs? }`. + - `checkers` is `{ plugin, options?, nodeArgs? }[]`. Each checker is its own plugin. + - `ignorers` is plugin file URLs. Every ignorer those modules export runs. There is no separate name list. + - Put Vitest settings on `testRunner.options` (`configFile`, `dir`, `related`). There is no `vitest` block. + - Put TypeScript checker settings on that checker's `options` (`prioritizePerformanceOverAccuracy`). There is no `typescriptChecker` block. + + Before: + + ```ts + export default defineConfig({ + testRunner: 'vitest', + checkers: ['typescript'], + plugins: [ + import.meta.resolve('@systemfsoftware/stryker-js-vitest-runner'), + import.meta.resolve('@systemfsoftware/stryker-js-typescript-checker'), + import.meta.resolve('@systemfsoftware/stryker-ignorer-effect-schema-declarations'), + ], + vitest: { configFile: 'vitest.config.ts' }, + typescriptChecker: { prioritizePerformanceOverAccuracy: true }, + ignorers: ['effect-schema-declarations'], + }) + ``` + + After: + + ```ts + export default defineConfig({ + testRunner: { + plugin: import.meta.resolve('@systemfsoftware/stryker-js-vitest-runner'), + options: { configFile: 'vitest.config.ts' }, + }, + checkers: [ + { + plugin: import.meta.resolve('@systemfsoftware/stryker-js-typescript-checker'), + options: { prioritizePerformanceOverAccuracy: true }, + }, + ], + ignorers: [ + import.meta.resolve('@systemfsoftware/stryker-ignorer-effect-schema-declarations'), + ], + }) + ``` + +- Plugin entries in `plugins` and `appendPlugins` are now the entrypoints + themselves, as `file:` URLs, instead of package names Stryker resolved for you. + + Stryker resolved every specifier against its own module before, so a plugin the + project had installed was only found when Node happened to walk to the right + `node_modules` from Stryker's location — which is not the project's location for + a global install, an `npx` run, or a strict isolated store. The project now + resolves each plugin itself, in the config module where the project's own + dependencies are visible: + + ```ts + import { defineConfig } from '@systemfsoftware/stryker-js/config' + + export default defineConfig({ + plugins: [import.meta.resolve('@acme/stryker-runner')], + }) + ``` + + The `--plugins` and `--appendPlugins` flags take the same resolved URLs. A value + that is not a `file:` URL is refused with the entry named. + + Stryker no longer reports an unresolved specifier as a warning it can continue + past, because it no longer resolves one: a plugin that cannot be loaded stops + the run and names the entry, and a plugin that loads but contributes nothing is + still reported as before. + +## 5.0.0 + +### Major Changes + +- The plugin system now runs each configured TestRunner/Checker/Reporter plugin in its own spawned process over an @effect/rpc wire: plugins load from the project's config-declared specifiers (no glob discovery), boundary failures are typed errors, and plugin spans link into the host trace. Breaking: the in-process plugin Layer contract is removed. + +- `@systemfsoftware/stryker-js` is renamed to `@systemfsoftware/stryker-js-language`, and the plugin boundary now ships as the two packages `@systemfsoftware/stryker-js-plugin-interface` and `@systemfsoftware/stryker-js-plugin-runtime`. + + `@systemfsoftware/stryker-js-plugin-interface` owns the contract both sides of the process split agree on: the per-kind `@effect/rpc` groups (`TestRunnerRpcs`, `CheckerRpcs`, `ReporterRpcs`), the boundary payload schemas, the typed boundary errors (`BoundaryPayloadRejected`, `BoundaryUnrecognizedSignal`), the spawn contract (`WorkerPluginKind`, `WorkerPluginSpawnSchema`, `WorkerEntryUrl`), and the trace-context contract (`TraceContextMiddleware`, `PropagatedTrace`, `TracedRpc`, `formatTraceparent`, `parseTraceparent`, `TraceContextReference`, `TRACEPARENT_HEADER`, `TRACESTATE_HEADER`). + + `@systemfsoftware/stryker-js-plugin-runtime` owns what a plugin process actually runs: the worker server layer a plugin's `main.ts` launches (`workerServerLayer`, `nodeModuleLayer`), the worker and host OTel bootstraps (`startWorkerTelemetry`, `startHostTelemetry`), the worker-options wire codec (`encodeWorkerOptions`, `decodeWorkerOptions`, `readWorkerOptionsFromEnv`), and the trace-context middleware implementations (`layerTraceContextClient`, `layerTraceContextServer`, `withLinkedSpan`, `tracePartsOf`). + + To migrate, update the package name on every import that is not a plugin symbol to `@systemfsoftware/stryker-js-language`; install `@systemfsoftware/stryker-js-plugin-interface` and import the contract symbols listed above from it, and install `@systemfsoftware/stryker-js-plugin-runtime` for the worker-side symbols. The former in-process surface (`declarePlugin`, `composePlugins`, `PluginContribution`, `PluginLayerContribution`, `PluginKind`, `PluginEnvironment`, `RunConfiguration`, `SandboxDirectory`) no longer exists: a plugin now declares `strykerPlugins: readonly { kind, name }[]` and a worker entry exported under `./worker` (or a `bin`) that serves the per-kind `RpcServer`. + +### Patch Changes + +- Maintenance release. Every published entry point, export, option, and behaviour is exactly as it was. diff --git a/packages/stryker-js-plugin-runtime/CHANGELOG.md b/packages/stryker-js-plugin-runtime/CHANGELOG.md new file mode 100644 index 000000000..0bfc3b19f --- /dev/null +++ b/packages/stryker-js-plugin-runtime/CHANGELOG.md @@ -0,0 +1,134 @@ +# @systemfsoftware/stryker-js-plugin-runtime + +## 7.0.2 + +### Patch Changes + +- Updated dependencies: + - @systemfsoftware/stryker-js-plugin-interface@14.0.0 + +## 7.0.1 + +### Patch Changes + +- Updated dependencies: + - @systemfsoftware/stryker-js-cli-contract@0.3.0 + - @systemfsoftware/stryker-js-plugin-interface@13.0.0 + +## 7.0.0 + +### Major Changes + +- Effect moves to the stable `4.0.0` release, together with the `@effect/*` packages these libraries use. The `4.0.0` release candidates are no longer supported: install `effect` `^4.0.0` next to these packages before upgrading. + + The TypeScript checker and test-runner plugins bundle their own Effect runtime, so they need no change in your project. + +### Patch Changes + +- Updated dependencies: + - @systemfsoftware/stryker-js-cli-contract@0.2.0 + - @systemfsoftware/stryker-js-plugin-interface@12.0.0 + +## 6.0.5 + +### Patch Changes + +- Updated dependencies: + - @systemfsoftware/stryker-js-plugin-interface@11.0.0 + +## 6.0.4 + +### Patch Changes + +- The packages now depend on `@systemfsoftware/effect-cell-types` 11. + + - Every tagged error now has a one-line message built from its fields, so a failure names the file, mutant, plugin, worker or exit code involved instead of an empty message. The errors' tags, fields and encoded forms are unchanged. + +## 6.0.3 + +### Patch Changes + +- Stages now run as cells over workflows, multi-item work runs as Effect streams, pools and worker transport use scoped Effect resources, and named operations are traced with Effect.fn. The Angular ignorer now declares @systemfsoftware/stryker-ignorer-kit as a runtime dependency. + +- Updated dependencies: + - @systemfsoftware/stryker-js-plugin-interface@10.0.0 + +## 6.0.2 + +### Patch Changes + +- Updated dependencies: + - @systemfsoftware/stryker-js-plugin-interface@9.0.0 + +## 6.0.1 + +### Patch Changes + +- The packages now build on the latest `@systemfsoftware/effect-cell-types` 10.2 cell kinds, with no change to their published behavior. + + - `@systemfsoftware/stryker-test-contribution` exports its evaluator as `Judge`, `TestContribution`, and `TestContributionEvaluator`, and now depends on `effect` directly. + +## 6.0.0 + +### Major Changes + +- Each package's main entry point now groups its exports into namespaces named after a capability, such as `Plugin`, `TestRunner` and `Report`. + + - Import the namespace and qualify each name, for example `Plugin.TestRunnerRpcs` after importing `Plugin` from the plugin interface. + - Import instrumenter schemas such as `Location` and `MutantStatus` from the instrumenter's `Mutant` namespace, and plugin-interface names from the plugin interface, instead of through another package's entry point. + - The engine's `/config`, `/events` and `/promises` entry points are unchanged. + +- `partsOfEffectSpan` and `tracePartsOf` are replaced by the `TraceContextPartsFromEffectSpan` codec, which decodes an `EffectSpanIdentity`. + +### Patch Changes + +- Updated dependencies: + - @systemfsoftware/stryker-js-plugin-interface@8.0.0 + +## 5.0.4 + +### Patch Changes + +- Three opt-in mutators plant Effect concurrency faults on top of the default set, so a mutation run can check that your concurrency tests catch races. `AtomicUpdateSplit` splits read-modify-write calls on `Ref` and `SynchronizedRef` into a separate read, a yield, and a separate write. `SynchronizationRemoval` removes semaphore permits, `Effect.uninterruptible`, and `Effect.uninterruptibleMask`. `FinalizerEscape` stops `ensuring`, `onExit`, `onError`, `onInterrupt`, `acquireRelease`, and `acquireUseRelease` cleanup from running on interruption. All three are off by default. Enable any subset by listing them under `mutator: { optInMutations: [...] }` in your configuration. A name that is not an opt-in mutator fails the run, and the error lists the known names. The replacements use Effect 4 APIs, so a repository on Effect 3.x must not opt in. + +## 5.0.3 + +### Patch Changes + +- Effect moves to `4.0.0-rc.116` (with `@effect/platform-node`, `@effect/platform-node-shared`, `@effect/vitest` and `@effect/opentelemetry` on the same release), the `@systemfsoftware/*` toolchain pins move to their current releases, and `vitest` 5 with `oxc-parser` 0.150 come along. Both worker bundles (`stryker-js-typescript-checker`, `stryker-js-vitest-runner`) now inline their whole runtime rather than resolving `effect`, `@effect/*` and the sibling packages from the host's tree, so a worker runs on its own copy of the effect that built it; the project's `typescript` and `vitest` remain the only imports beside Node builtins. The filter-level `arbitrary.candidate` generators that the thresholds schemas carried are gone: v4 no longer reads them, so thresholds still reject a `low` above `high` and the property tests generate the pair from the schema again. Log calls inside `Effect.catch*` handlers moved to `Effect.tapError`/`Effect.tapCause` at the same level and message. `@systemfsoftware/stryker-ignorer-interface` re-exports the 0.150 AST, where `FormalParameterRest.decorators` is now `Array`. + +## 5.0.2 + +### Patch Changes + +- Updated dependencies: + - @systemfsoftware/stryker-js-plugin-interface@7.0.0 + +## 5.0.1 + +### Patch Changes + +- Unix worker sockets are now owner-read-write once the worker has bound them. Named pipes are unchanged. + +- Updated dependencies: + - @systemfsoftware/stryker-js-plugin-interface@6.0.0 + +## 5.0.0 + +### Major Changes + +- Every module Stryker loads — config files, `extends` targets, plugins, and the vitest runner's own vitest — now resolves and imports through Node's native ECMAScript module system: `import.meta.resolve` resolves each bare specifier from the consuming module, and dynamic `import()` loads the resolved URL. The package's `exports` map (or a legacy `main`) selects the entry file, ESM-only plugin packages load, and no require-based loader ships in any package. An unresolvable bare specifier warns with a machine-readable reason; the run fails at prepare only when nothing provides the configured runner or checker. Node.js 22.18.0 or later is required. + + To migrate, install Node.js 22.18.0 or later. Bare plugin specifiers keep working, but a plugin package must publish an ES module entry through `exports` or `main`, and the plugin must be resolvable from where Stryker itself is installed. + +- `@systemfsoftware/stryker-js` is renamed to `@systemfsoftware/stryker-js-language`, and the plugin boundary now ships as the two packages `@systemfsoftware/stryker-js-plugin-interface` and `@systemfsoftware/stryker-js-plugin-runtime`. + + `@systemfsoftware/stryker-js-plugin-interface` owns the contract both sides of the process split agree on: the per-kind `@effect/rpc` groups (`TestRunnerRpcs`, `CheckerRpcs`, `ReporterRpcs`), the boundary payload schemas, the typed boundary errors (`BoundaryPayloadRejected`, `BoundaryUnrecognizedSignal`), the spawn contract (`WorkerPluginKind`, `WorkerPluginSpawnSchema`, `WorkerEntryUrl`), and the trace-context contract (`TraceContextMiddleware`, `PropagatedTrace`, `TracedRpc`, `formatTraceparent`, `parseTraceparent`, `TraceContextReference`, `TRACEPARENT_HEADER`, `TRACESTATE_HEADER`). + + `@systemfsoftware/stryker-js-plugin-runtime` owns what a plugin process actually runs: the worker server layer a plugin's `main.ts` launches (`workerServerLayer`, `nodeModuleLayer`), the worker and host OTel bootstraps (`startWorkerTelemetry`, `startHostTelemetry`), the worker-options wire codec (`encodeWorkerOptions`, `decodeWorkerOptions`, `readWorkerOptionsFromEnv`), and the trace-context middleware implementations (`layerTraceContextClient`, `layerTraceContextServer`, `withLinkedSpan`, `tracePartsOf`). + + To migrate, update the package name on every import that is not a plugin symbol to `@systemfsoftware/stryker-js-language`; install `@systemfsoftware/stryker-js-plugin-interface` and import the contract symbols listed above from it, and install `@systemfsoftware/stryker-js-plugin-runtime` for the worker-side symbols. The former in-process surface (`declarePlugin`, `composePlugins`, `PluginContribution`, `PluginLayerContribution`, `PluginKind`, `PluginEnvironment`, `RunConfiguration`, `SandboxDirectory`) no longer exists: a plugin now declares `strykerPlugins: readonly { kind, name }[]` and a worker entry exported under `./worker` (or a `bin`) that serves the per-kind `RpcServer`. + +### Patch Changes + +- Maintenance release. Every published entry point, export, option, and behaviour is exactly as it was. diff --git a/packages/stryker-js-typescript-checker/CHANGELOG.md b/packages/stryker-js-typescript-checker/CHANGELOG.md new file mode 100644 index 000000000..309f0da8f --- /dev/null +++ b/packages/stryker-js-typescript-checker/CHANGELOG.md @@ -0,0 +1,532 @@ +# @systemfsoftware/stryker-js-typescript-checker + +## 8.2.1 + +### Patch Changes + +- Updated dependencies: + - @systemfsoftware/stryker-js-plugin-interface@14.0.0 + +## 8.2.0 + +### Minor Changes + +- The TypeScript checker now applies Trivial Compiler Equivalence (Papadakis et al., ICSE 2015). Each mutant of a file is emitted to JavaScript from the project's own TypeScript configuration in transpile-only mode, normalized, and compared with the original file's emit and with the mutants already emitted at the same site. A mutant whose emit equals the original's is dropped as `Ignored` with reason `equivalent-to-original: tce`; one that equals an earlier same-site mutant's is dropped with `duplicate-at-site: tce`; every mutant whose emit differs is kept, so the check never removes a mutant that changes the program. Checkers gain an `ignored` result variant carrying the suppression reason, and a run publishes the new `tce` machine-stream event with the `equivalentToOriginal` and `duplicateAtSite` counts. Verdict semantics move to version 3. + +### Patch Changes + +- Updated dependencies: + - @systemfsoftware/stryker-js-cli-contract@0.3.0 + - @systemfsoftware/stryker-js-plugin-interface@13.0.0 + +## 8.1.1 + +### Patch Changes + +- Effect moves to the stable `4.0.0` release, together with the `@effect/*` packages these libraries use. The `4.0.0` release candidates are no longer supported: install `effect` `^4.0.0` next to these packages before upgrading. + + The TypeScript checker and test-runner plugins bundle their own Effect runtime, so they need no change in your project. + +- Updated dependencies: + - @systemfsoftware/stryker-js-cli-contract@0.2.0 + - @systemfsoftware/stryker-js-instrumenter@12.0.0 + - @systemfsoftware/stryker-js-plugin-interface@12.0.0 + - @systemfsoftware/stryker-js-plugin-runtime@7.0.0 + +## 8.1.0 + +### Major Changes + +- The TypeScript checker now type-checks each mutant on its own, so a mutant is `CompileError` only when that mutant fails to compile, and checking finishes much sooner on large projects. Files that import a mutated file are checked only when the mutant changes what that file exports. + + The `prioritizePerformanceOverAccuracy` option is removed. Delete it from your `checkers` options. + +### Patch Changes + +- Fixed a rare hang where a run stopped making progress while a test runner or checker worker waited for a message that had already arrived. The message now always wakes the worker waiting for it. + +## 8.0.1 + +### Patch Changes + +- The packages now depend on `@systemfsoftware/effect-cell-types` 11. + + - Every tagged error now has a one-line message built from its fields, so a failure names the file, mutant, plugin, worker or exit code involved instead of an empty message. The errors' tags, fields and encoded forms are unchanged. + +## 8.0.0 + +### Major Changes + +- Source locations are 1-based and validated end to end. `Mutant.Location`, `Mutant.Position`, `Mutant.Span`, `Mutant.OpenEndLocation`, `Mutant.ScriptOrigin`, and `Mutant.LineStarts` replace the former `LocationSchema`, `PositionSchema`, and `OpenEndLocationSchema`; a location whose end precedes its start, or a line or column below 1, no longer decodes. + + - A mutant location always speaks the report contract, so a producer that minted 0-based coordinates or a bare number must switch to the new schemas. + - An embedded script's origin is a `ScriptOrigin` (1-based line plus a column shift), not a position. + - Incremental state from releases that stored the earlier column base is no longer matched: those mutants are re-tested, not reused, while the run rewrites the state. + +### Patch Changes + +- The TypeScript checker describes a tsconfig document through the schema that declares the keys it interprets, so an override round-trips the document it was given, and the mutant it cannot describe to a checker carries the canonical file-name brand. The plugin contract, configurations, reports, and exit codes are unchanged. + +- Stages now run as cells over workflows, multi-item work runs as Effect streams, pools and worker transport use scoped Effect resources, and named operations are traced with Effect.fn. The Angular ignorer now declares @systemfsoftware/stryker-ignorer-kit as a runtime dependency. + +- Updated dependencies: + - @systemfsoftware/stryker-js-instrumenter@10.0.0 + - @systemfsoftware/stryker-js-plugin-interface@10.0.0 + +## 7.1.2 + +### Patch Changes + +- Mutants are now type-checked incrementally. Checking a mutant costs its own file and the files that import it — through a relative path, a path alias, or a re-export — instead of a full type check of every project the checker opens, so a run whose mutants all live in one file finishes instead of staying at zero completed mutants until the run is killed. + + A mutation that breaks a file which imports the mutated file is still a `CompileError` rather than being handed to the test runner. + +- Updated dependencies: + - @systemfsoftware/stryker-js-plugin-interface@9.0.0 + +## 7.1.1 + +### Patch Changes + +- The packages now build on the latest `@systemfsoftware/effect-cell-types` 10.2 cell kinds, with no change to their published behavior. + + - `@systemfsoftware/stryker-test-contribution` exports its evaluator as `Judge`, `TestContribution`, and `TestContributionEvaluator`, and now depends on `effect` directly. + +## 7.1.0 + +### Patch Changes + +- The checker no longer looks dead while it type-checks. A check that runs longer than the host's connection patience window used to leave the checker unable to answer the host, dropping its connection mid-run; long checks are now answered and the connection survives them. + +- The typescript checker no longer discards `include`, `exclude`, `files`, `extends`, and any other unrecognized top-level key from the tsconfig it rewrites. Projects that list files outside the default include patterns, import their own package manifest, or extend a shared preset now type-check during mutation runs the same way they do under `tsc`; only the compiler options the checker intentionally overrides still differ, and single-project mode alone drops `references`. Referenced projects are covered too: every tsconfig a build-mode project references is rewritten with those same overrides, so a library that opts into `noUnusedLocals` (or any option the checker overrides) is checked under the checker's settings instead of its own. + +## 7.0.5 + +### Patch Changes + +- Exported declarations that previously took `unknown` now take a defaulted type parameter: calls that omit the type argument are unchanged, and calls that were previously rejected for passing an unconstrained value are accepted. No runtime behaviour changes. + +## 7.0.4 + +### Patch Changes + +- Effect moves to `4.0.0-rc.116` (with `@effect/platform-node`, `@effect/platform-node-shared`, `@effect/vitest` and `@effect/opentelemetry` on the same release), the `@systemfsoftware/*` toolchain pins move to their current releases, and `vitest` 5 with `oxc-parser` 0.150 come along. Both worker bundles (`stryker-js-typescript-checker`, `stryker-js-vitest-runner`) now inline their whole runtime rather than resolving `effect`, `@effect/*` and the sibling packages from the host's tree, so a worker runs on its own copy of the effect that built it; the project's `typescript` and `vitest` remain the only imports beside Node builtins. The filter-level `arbitrary.candidate` generators that the thresholds schemas carried are gone: v4 no longer reads them, so thresholds still reject a `low` above `high` and the property tests generate the pair from the schema again. Log calls inside `Effect.catch*` handlers moved to `Effect.tapError`/`Effect.tapCause` at the same level and message. `@systemfsoftware/stryker-ignorer-interface` re-exports the 0.150 AST, where `FormalParameterRest.decorators` is now `Array`. + +## 7.0.3 + +### Patch Changes + +- A checker that fails now fails mutation testing with that checker's cause, instead of crashing with an empty error. The TypeScript checker type-checks each mutant, so compile errors appear in the report. Verdict counts are non-negative integers; `Metrics` is a class you can build from mutant statuses; a threshold `low` may not exceed `high`. + +## 7.0.2 + +### Patch Changes + +- The checker request carries a plain data record, not the instrumenter's `Mutant` class. `CheckerRequest.mutants` and `CheckerService.check`/`group` now speak `CheckerMutantWire` - `id`, `fileName`, `mutatorName`, `replacement`, and `location`. A mutant that cannot be described to a checker is skipped instead of reaching it. + + With `OTEL_ENABLED=true` the CLI now exports its OpenTelemetry metrics to `OTEL_EXPORTER_OTLP_ENDPOINT`, and `OTEL_METRIC_EXPORT_INTERVAL` sets the export interval in milliseconds. Checker timings, mutant counts, and worker crashes now reach a metrics backend. + + `ConfigEnv` and `resolveExtends` are no longer exported. Import `ConfigEnv` from the config entry point of the package, and read merged options with `loadConfigCell` or `readConfig` from the package root - either performs the `extends` walk, validation and merge in order. + +- Updated dependencies: + - @systemfsoftware/stryker-js-plugin-interface@7.0.0 + +## 7.0.1 + +### Patch Changes + +- Updated dependencies: + - @systemfsoftware/stryker-js-instrumenter@8.0.0 + +## 7.0.0 + +### Major Changes + +- `testRunner`, `checkers`, and `ignorers` now carry the plugin and its options together. + + - `testRunner` is `'command'`, `'vm'`, or `{ plugin, options?, nodeArgs? }`. + - `checkers` is `{ plugin, options?, nodeArgs? }[]`. Each checker is its own plugin. + - `ignorers` is plugin file URLs. Every ignorer those modules export runs. There is no separate name list. + - Put Vitest settings on `testRunner.options` (`configFile`, `dir`, `related`). There is no `vitest` block. + - Put TypeScript checker settings on that checker's `options` (`prioritizePerformanceOverAccuracy`). There is no `typescriptChecker` block. + + Before: + + ```ts + export default defineConfig({ + testRunner: 'vitest', + checkers: ['typescript'], + plugins: [ + import.meta.resolve('@systemfsoftware/stryker-js-vitest-runner'), + import.meta.resolve('@systemfsoftware/stryker-js-typescript-checker'), + import.meta.resolve('@systemfsoftware/stryker-ignorer-effect-schema-declarations'), + ], + vitest: { configFile: 'vitest.config.ts' }, + typescriptChecker: { prioritizePerformanceOverAccuracy: true }, + ignorers: ['effect-schema-declarations'], + }) + ``` + + After: + + ```ts + export default defineConfig({ + testRunner: { + plugin: import.meta.resolve('@systemfsoftware/stryker-js-vitest-runner'), + options: { configFile: 'vitest.config.ts' }, + }, + checkers: [ + { + plugin: import.meta.resolve('@systemfsoftware/stryker-js-typescript-checker'), + options: { prioritizePerformanceOverAccuracy: true }, + }, + ], + ignorers: [ + import.meta.resolve('@systemfsoftware/stryker-ignorer-effect-schema-declarations'), + ], + }) + ``` + +### Patch Changes + +- Updated dependencies: + - @systemfsoftware/stryker-js-plugin-interface@6.0.0 + +## 6.0.0 + +### Major Changes + +- The plugin system now runs each configured TestRunner/Checker/Reporter plugin in its own spawned process over an @effect/rpc wire: plugins load from the project's config-declared specifiers (no glob discovery), boundary failures are typed errors, and plugin spans link into the host trace. Breaking: the in-process plugin Layer contract is removed. + +### Patch Changes + +- Maintenance release. Every published entry point, export, option, and behaviour is exactly as it was. + +- effect and typescript dependency ranges widen from exact catalog pins to repo catalog ranges; repository metadata now points at stryker-js-effect + +## 5.0.4 + +### Patch Changes + +- Imports rewired to the collapsed `@systemfsoftware/stryker-js` root entry; each package now co-releases against the root-entry major. + +- Updated dependencies: + - @systemfsoftware/effect-cell-types@8.0.0 + - @systemfsoftware/stryker-js@4.0.0 + +## 5.0.3 + +### Patch Changes + +- Releases the workspace so its published versions track the shared dependency graph this change moves. + +- The `@systemfsoftware/source` export condition is gone. It resolved to a package's TypeScript sources for editors and in-repo typechecks; each package now exports only its built entry. If your tsconfig sets `customConditions: ["@systemfsoftware/source"]`, or a bundler config names that condition, remove it — resolution falls back to the built entry, which is what every consumer already got. + +## 5.0.2 + +### Patch Changes + +- Updated dependencies: + - @systemfsoftware/effect-cell-types@7.0.0 + +## 5.0.1 + +### Patch Changes + +- Re-released against @systemfsoftware/stryker-js without the removed --llms manifest. The Run stream no longer carries a manifest terminal event, and the RunEvent / RunTerminalEvent unions no longer include the manifest arm, so any exhaustive consumer of those types must drop that case. + +- Rebuilds against updated workspace dependencies, including the new + `@systemfsoftware/stryker-js` reporter protocol major. + +- Updated dependencies: + - @systemfsoftware/stryker-js@3.0.0 + +## 5.0.0 + +### Major Changes + +- The packages are renamed. `plugin-api` is now `@systemfsoftware/stryker-js`, the + language every plugin is written against. `mutation-run` is split: the run + itself is `@systemfsoftware/stryker-js-engine` + (host-neutral, no Node on its manifest) and the Node process entries are + `@systemfsoftware/stryker-js-cli`, which owns the worker files and the runtime + gate. `mutation-report` is now `@systemfsoftware/stryker-js-html-reporter`. + `@systemfsoftware/stryker-js-platform-node` is never published — do not install + it. Install the new names and change your imports. + + Options types moved. `StrykerOptions`, `PartialStrykerOptions` and `LogLevel` are + imported from the `Schema` export; `Mutant`, `MutantStatus`, `Position` and + `Location` from the `Mutant` export. Point a config's `extends` at the language + package's `Schema` export. + + `MutantStatus` accepts one spelling per outcome: `Killed`, `Survived`, + `NoCoverage`, `Timeout`, `CompileError`, `RuntimeError`, `Ignored` and `Pending`. + The lowercase and abbreviated forms — `killed`, `timedOut`, `noCoverage` and the + rest — are gone. A comparison against a removed spelling never matched the value + the reporter actually produced, so check any status comparison you wrote. + + Statuses, plugin kinds, exit classes and AST formats are string literal unions + rather than enums, so read them as their string values. Member access such as + `ExitClass.VerdictFail` no longer resolves. + + A plugin no longer receives a logger, and the logger port is gone. Plugins log + through Effect, and the host decides where that output goes. + + The bundled base preset is gone. A config inherits from the language package's + `Schema` export and states the thresholds, reporters and plugins it wants; you no + longer silently inherit a package manager, a plugin list or a break threshold. + +### Minor Changes + +- cut over to effect v4 (4.0.0-rc.108): public surface derives from effect types; peers flip effect ^3→^4 + +### Patch Changes + +- New version is published through npm trusted publishing, so it carries a provenance attestation you can verify. + +- Peer Effect requirement advances to 4.0.0-rc.112. No API changes. + +- An oversized message between the runner and a worker now fails the run with a + reason instead of exhausting memory. Each side of the connection reads frames + up to 16 MiB, which leaves headroom over the largest legitimate payload — a dry + run carrying per-test coverage — and a frame past the limit fails the calls + waiting on it rather than growing until the process dies. + +- Each of these packages now has a README, so its registry page says what the package is, how + to install it, and what to import or register — previously the page was blank. The lint + plugins show the configuration line that enables what they recommend. + + `@systemfsoftware/stryker-js-html-reporter` also carries its licence text + +- The shared helpers package is gone. Nothing installs it any more, and the + handful of helpers worth sharing now live in the plugin contract next to the + types they serve: + + - `strykerReportBugUrl`, `normalizeFileName`, `propertyPath`, `errorToString` + and `isErrnoException` from `@systemfsoftware/stryker-js/core` + - `noopLogger` from `@systemfsoftware/stryker-js/logging` + - `testFilesProvided` from `@systemfsoftware/stryker-js/test-runner` + + If you imported any of those, change the specifier. Everything else it exported + had no consumer and is removed: use `Predicate.isNotNullish` from Effect in place + of `notEmpty`, and `RegExp.escape` in place of `escapeRegExp`. + +- These packages no longer install dependencies they never imported, so installing them pulls less into your tree. + + `tslib` is gone from all six. The mutation runner additionally stops installing `lodash.groupby`, `semver` and `source-map`, and the command line interface stops installing `@effect/platform-node-shared`. Nothing exported changes. + +- Published packages no longer carry build artifacts left over from earlier builds. One package was shipping about a megabyte of bundled test-runner internals this way. + +- Updated dependencies: + - @systemfsoftware/stryker-js@2.0.0 + +## 4.0.0 + +### Major Changes + +- CheckMutantsDecision is now a branded tagged union CheckFinished|RetestRequired instead of a plain record; consumer dispatch is exhaustive over the tags + +### Patch Changes + +- Updated dependencies: + - @systemfsoftware/effect-cell-types@6.0.0 + - @systemfsoftware/stryker-js@1.0.0 + +## 3.0.3 + +### Patch Changes + +- Refreshed builds on the platform-services dependency graph; the packages no longer reach for host builtins directly. No CLI flags or option names change. + +- Updated dependencies: + - @systemfsoftware/stryker-js@0.2.0 + +## 3.0.2 + +### Patch Changes + +- Re-published against the oxc-based instrumenter: workspace dependency ranges move to the new instrumenter major; no package's own behavior changes in this release. + +## 3.0.1 + +### Patch Changes + +- When two or more mutants in one TypeScript project file produce a compiler error that cannot be blamed on exactly one of them, `check` now rechecks each mutant alone. A mutant that typechecks by itself is reported `passed`. A mutant that fails typecheck by itself is reported `compileError`. + +- Peer Effect requirement advances to 4.0.0-rc.112. No API changes. + +## 3.0.0 + +### Major Changes + +- Three packages are renamed. `plugin-api` is now `@systemfsoftware/stryker-js`, the + language every plugin is written against. `mutation-run` is now + `@systemfsoftware/stryker-js-platform-node`, the Node host that runs a mutation + test. `mutation-report` is now `@systemfsoftware/stryker-js-html-reporter`. + Install the new names and change your imports. + + Options types moved. `StrykerOptions`, `PartialStrykerOptions` and `LogLevel` are + imported from the `Schema` export; `Mutant`, `MutantStatus`, `Position` and + `Location` from the `Mutant` export. Point a config's `extends` at the language + package's `Schema` export. + + `MutantStatus` accepts one spelling per outcome: `Killed`, `Survived`, + `NoCoverage`, `Timeout`, `CompileError`, `RuntimeError`, `Ignored` and `Pending`. + The lowercase and abbreviated forms — `killed`, `timedOut`, `noCoverage` and the + rest — are gone. A comparison against a removed spelling never matched the value + the reporter actually produced, so check any status comparison you wrote. + + Statuses, plugin kinds, exit classes and AST formats are string literal unions + rather than enums, so read them as their string values. Member access such as + `ExitClass.VerdictFail` no longer resolves. + + A plugin no longer receives a logger, and the logger port is gone. Plugins log + through Effect, and the host decides where that output goes. + + The bundled base preset is gone. A config inherits from the language package's + `Schema` export and states the thresholds, reporters and plugins it wants; you no + longer silently inherit a package manager, a plugin list or a break threshold. + +### Patch Changes + +- A type error inside an installed dependency's declaration files no longer fails the run. + + The checker exists to decide whether your source still compiles once a mutant is + applied. It was also reporting errors from `.d.ts` files inside installed packages — + most often a package whose optional peer dependency is not installed. No mutant + causes those, every mutant reports the same ones, and the checker cannot act on + them, so their only effect was to end the run before a single mutant was tested. + + Library declaration files are now skipped, alongside the code-quality options the + checker already relaxes while mutating. + +- An oversized message between the runner and a worker now fails the run with a + reason instead of exhausting memory. Each side of the connection reads frames + up to 16 MiB, which leaves headroom over the largest legitimate payload — a dry + run carrying per-test coverage — and a frame past the limit fails the calls + waiting on it rather than growing until the process dies. + +- Updated dependencies: + - @systemfsoftware/effect-cell-types@5.0.0 + +## 2.0.0 + +### Major Changes + +- The TypeScript compiler's methods return Effects instead of Promises, and the + compiler is acquired for the length of the check. An interrupted check now + releases the compiler instead of leaving it alive with the run's state still in + it. + + Compiler failures are now a single tagged `CompilerFailed` error carrying a + `reason` — the compiler used before initialization, no projects found for the + tsconfig, an unknown file in the graph, or a project file missing from disk. + They were untagged `Error`s before, so anything catching them saw one + indistinguishable type. + + Diagnostics and `tsconfig` resolution are unchanged. + +### Patch Changes + +- The shared helpers package is gone. Nothing installs it any more, and the + handful of helpers worth sharing now live in the plugin contract next to the + types they serve: + + - `strykerReportBugUrl`, `normalizeFileName`, `propertyPath`, `errorToString` + and `isErrnoException` from `@systemfsoftware/stryker-js-plugin-api/core` + - `noopLogger` from `@systemfsoftware/stryker-js-plugin-api/logging` + - `testFilesProvided` from `@systemfsoftware/stryker-js-plugin-api/test-runner` + + If you imported any of those, change the specifier. Everything else it exported + had no consumer and is removed: use `Predicate.isNotNullish` from Effect in place + of `notEmpty`, and `RegExp.escape` in place of `escapeRegExp`. + +- These packages no longer install dependencies they never imported, so installing them pulls less into your tree. + + `tslib` is gone from all six. The mutation runner additionally stops installing `lodash.groupby`, `semver` and `source-map`, and the command line interface stops installing `@effect/platform-node-shared`. Nothing exported changes. + +- Published packages no longer carry build artifacts left over from earlier builds. One package was shipping about a megabyte of bundled test-runner internals this way. + +- Updated dependencies: + - @systemfsoftware/stryker-js-plugin-api@3.0.0 + +## 1.4.1 + +### Patch Changes + +- Plugins are discovered under this scope by default. The `plugins` option now + defaults to a glob over this scope instead of the original project's, so a + configuration that relied on the previous default while installing plugins from + the original project must now list those plugins explicitly. Discovery also no + longer fails when no matching scope directory is installed: it loads nothing and + warns. + + The report's dependency section names the packages of this scope, and the + report's home link points at this project. + +- Updated dependencies: + - @systemfsoftware/stryker-js-util@0.2.0 + +## 1.4.0 + +### Minor Changes + +- cut over to effect v4 (4.0.0-rc.108): public surface derives from effect types; peers flip effect ^3→^4 + +### Patch Changes + +- Fail a compile on errors only, not on the tree's standing suggestions + + The dry run and the per-mutant check counted every diagnostic the program produced, so the Effect language service's suggestions — which the pristine tree carries by design and which surface in `lint:tsgo` and the editor — refused mutation runs outright with a dry-run compile error. Only error-category diagnostics fail a compile now; warnings and suggestions were never compile failures + +- New version is published through npm trusted publishing, so it carries a provenance attestation you can verify. + +- Each of these packages now has a README, so its registry page says what the package is, how + to install it, and what to import or register — previously the page was blank. The lint + plugins show the configuration line that enables what they recommend. + + `@systemfsoftware/stryker-js-mutation-report` also carries its licence text + +- These packages no longer ship their development files. Sources, tests and build, lint and + test configuration were all included, which broke consumers in one specific way: oxlint + discovers configuration by walking directories, finds the published `oxlint.config.ts` under + `node_modules`, and stops with "Stripping types is currently unsupported for files under + node_modules" before linting anything. + + The installed package now contains the compiled output, the runtime schema files it reads, + and the usual manifest, README and licence + +- Updated dependencies: + - @systemfsoftware/stryker-js-plugin-api@2.0.0 + +## 1.3.0 + +### Minor Changes + +- cut over to effect v4 (4.0.0-rc.108): public surface derives from effect types; peers flip effect ^3→^4 + +### Patch Changes + +- Fail a compile on errors only, not on the tree's standing suggestions + + The dry run and the per-mutant check counted every diagnostic the program produced, so the Effect language service's suggestions — which the pristine tree carries by design and which surface in `lint:tsgo` and the editor — refused mutation runs outright with a dry-run compile error. Only error-category diagnostics fail a compile now; warnings and suggestions were never compile failures + +- New version is published through npm trusted publishing, so it carries a provenance attestation you can verify. + +- Each of these packages now has a README, so its registry page says what the package is, how + to install it, and what to import or register — previously the page was blank. The lint + plugins show the configuration line that enables what they recommend. + + `@systemfsoftware/stryker-js-mutation-report` also carries its licence text + +- These packages no longer ship their development files. Sources, tests and build, lint and + test configuration were all included, which broke consumers in one specific way: oxlint + discovers configuration by walking directories, finds the published `oxlint.config.ts` under + `node_modules`, and stops with "Stripping types is currently unsupported for files under + node_modules" before linting anything. + + The installed package now contains the compiled output, the runtime schema files it reads, + and the usual manifest, README and licence + +- Updated dependencies: + - @systemfsoftware/stryker-js-plugin-api@1.0.0 diff --git a/packages/stryker-js-vitest-runner/CHANGELOG.md b/packages/stryker-js-vitest-runner/CHANGELOG.md new file mode 100644 index 000000000..9e4653004 --- /dev/null +++ b/packages/stryker-js-vitest-runner/CHANGELOG.md @@ -0,0 +1,507 @@ +# @systemfsoftware/stryker-js-vitest-runner + +## 8.1.1 + +### Patch Changes + +- A mutant's timeout now starts when its first test begins, not when the test runner is asked to run it. With a short `timeoutMS`, a mutant its tests would kill is reported `Killed` instead of `Timeout`, so a slow or busy machine no longer changes the verdict. A mutant whose tests never begin still ends, on the same window that bounds a plugin worker which never accepts its connection. + + If you author a test-runner plugin, `mutantRun` now streams its run: emit `MutantRunStarted` before the runner's first test begins, then `MutantRunSettled` carrying the result. + +## 8.1.0 + +### Minor Changes + +- The Vitest runner persists transformed modules to a file-system cache under the sandbox and reuses them across the worker threads the standby pool hands out, so a mutant run that lands on a freshly handed-out thread reads the transforms it already paid for instead of recompiling every module. The cache can be turned off with `testRunner: { options: { fsModuleCache: false } }`. Verdicts are unchanged. + +### Patch Changes + +- The runner no longer lets the ambient `VITEST_MAX_WORKERS` setting raise the number of Vitest workers it runs with, which could start a mutant's covering tests concurrently; with that setting in the environment, a previously recorded killer still runs first and a killed mutant stops at its killer. + +## 8.0.3 + +### Patch Changes + +- Effect moves to the stable `4.0.0` release, together with the `@effect/*` packages these libraries use. The `4.0.0` release candidates are no longer supported: install `effect` `^4.0.0` next to these packages before upgrading. + + The TypeScript checker and test-runner plugins bundle their own Effect runtime, so they need no change in your project. + +## 8.0.2 + +### Patch Changes + +- Fixed a rare hang where a run stopped making progress while a test runner or checker worker waited for a message that had already arrived. The message now always wakes the worker waiting for it. + +## 8.0.1 + +### Patch Changes + +- The packages now depend on `@systemfsoftware/effect-cell-types` 11. + + - Every tagged error now has a one-line message built from its fields, so a failure names the file, mutant, plugin, worker or exit code involved instead of an empty message. The errors' tags, fields and encoded forms are unchanged. + +## 8.0.0 + +### Major Changes + +- Counts and durations that cannot be negative are refined where they are declared, through the shared non-negative integer and non-negative finite schemas. Test-runner counts and durations, the clear-text reporter's log limit, the dry-run timeout, a test run's hit counter and limit, and a run's help-error count now refuse a negative value instead of accepting it. + +- Test identifiers are one value: `TestRunner.TestId`, a non-empty branded string carrying the runner's `file#test name` form. Test runner results, report test definitions, a mutant's killers and coverers, per-test hit records, and the test-contribution evaluation all speak that one type instead of bare strings. + + Mint one with `TestRunner.TestId.make(...)` where a runner derives a test id; the machine stream and mutation report schemas brand the ids they decode. + +### Patch Changes + +- Stages now run as cells over workflows, multi-item work runs as Effect streams, pools and worker transport use scoped Effect resources, and named operations are traced with Effect.fn. The Angular ignorer now declares @systemfsoftware/stryker-ignorer-kit as a runtime dependency. + +## 7.3.0 + +### Minor Changes + +- The test runner accepts a new `pool` option. When it is `threads`, a Vitest config that enables browser mode for any project is refused at startup with a message naming `testRunner: 'vitest'`. + +### Patch Changes + +- A run whose selected files produce no mutants, for example because no loaded framework claims them, now runs every test in the dry run and finishes with an empty report. With `testRunner: 'vm'` or `'vitest'` it used to fail with "No tests were executed", because the dry run only looked for tests related to those files. The dry run now relates tests only to the files that carry mutants. + +- A failed dry run now names every failing test. The runner used to apply the bail setting to the dry run as well, so Vitest stopped at the first failure and the error listed only that test. Mutant runs still stop at the first failing test unless `disableBail` is set. + +- A test file that fails as a whole — a suite that registers no tests, a file that throws while loading, or a `beforeAll`/`afterAll` hook that fails — is now reported as a failing test carrying the file's own message. A mutant that breaks a file this way is reported as Killed instead of Surviving, and a dry run that loads such a file fails naming it. + + Skipped tests that never start are now reported as skipped rather than dropped from the run's results. + + A mutant run whose related files match no test file no longer stops with a test-runner crash; the run continues with no test able to kill the mutant. + +- Mutation runs with `testRunner: 'vm'` finish in about half the time, with the same verdicts. The engine checks mutant groups on every checker process at once, stops the checkers as soon as checking ends, hands their share of `concurrency` to the test runners, and shares a Node compile cache with every worker it starts. The Vitest runner starts the next isolated worker thread while the current test file runs, so each file still gets a fresh thread but no longer waits for one to boot. On a 319-mutant TypeScript project with the TypeScript checker, a run went from 24.9 s to 12.6 s. + +## 7.2.1 + +### Patch Changes + +- The packages now build on the latest `@systemfsoftware/effect-cell-types` 10.2 cell kinds, with no change to their published behavior. + + - `@systemfsoftware/stryker-test-contribution` exports its evaluator as `Judge`, `TestContribution`, and `TestContributionEvaluator`, and now depends on `effect` directly. + +## 7.2.0 + +### Patch Changes + +- Mutant runs now execute the tests that cover the mutant and report the verdict those tests earn. With the default `related` option, every mutant run filtered out all test files, so mutants came back as errors (`No test files found`) instead of killed or survived. Mutants covered only by tests inside `describe` blocks — any depth, including `describe.each` — now run exactly those covering tests and are reported Killed when any of them fails; mutants covered only by top-level tests keep their existing selection and verdicts. + +## 7.1.1 + +### Patch Changes + +- Exported declarations that previously took `unknown` now take a defaulted type parameter: calls that omit the type argument are unchanged, and calls that were previously rejected for passing an unconstrained value are accepted. No runtime behaviour changes. + +## 7.1.0 + +### Minor Changes + +- A finite mutant is killed or survived from its tests. Timeout is reserved for one named nonterminating trap, detected by a hit bound rather than a wall-clock budget. A wall-clock timeout fails the run instead of being stored as a detected mutant. + +### Patch Changes + +- Mutants that execute far past their dry-run hit count now fail at the hit limit instead of waiting out the test timeout. Coverage from the dry run is recorded again, so the limit is armed. + +## 7.0.1 + +### Patch Changes + +- Effect moves to `4.0.0-rc.116` (with `@effect/platform-node`, `@effect/platform-node-shared`, `@effect/vitest` and `@effect/opentelemetry` on the same release), the `@systemfsoftware/*` toolchain pins move to their current releases, and `vitest` 5 with `oxc-parser` 0.150 come along. Both worker bundles (`stryker-js-typescript-checker`, `stryker-js-vitest-runner`) now inline their whole runtime rather than resolving `effect`, `@effect/*` and the sibling packages from the host's tree, so a worker runs on its own copy of the effect that built it; the project's `typescript` and `vitest` remain the only imports beside Node builtins. The filter-level `arbitrary.candidate` generators that the thresholds schemas carried are gone: v4 no longer reads them, so thresholds still reject a `low` above `high` and the property tests generate the pair from the schema again. Log calls inside `Effect.catch*` handlers moved to `Effect.tapError`/`Effect.tapCause` at the same level and message. `@systemfsoftware/stryker-ignorer-interface` re-exports the 0.150 AST, where `FormalParameterRest.decorators` is now `Array`. + +## 7.0.0 + +### Major Changes + +- The runner now requires vitest 4.1 or later, and the peer dependency says so: + `vitest >=4.1.0`. + + Upgrade vitest to 4.1 or later before upgrading this package. Projects that must + stay below 4.1 should stay on the previous release. + + The published bundle now also inlines `oxc-parser` and `@eslint-community/regexpp` + so they ship inside the runner's install. Consumers that previously resolved those + packages as transitive dependencies from another tool should now take them from + the runner's published bundle. + +## 6.0.0 + +### Major Changes + +- `testRunner`, `checkers`, and `ignorers` now carry the plugin and its options together. + + - `testRunner` is `'command'`, `'vm'`, or `{ plugin, options?, nodeArgs? }`. + - `checkers` is `{ plugin, options?, nodeArgs? }[]`. Each checker is its own plugin. + - `ignorers` is plugin file URLs. Every ignorer those modules export runs. There is no separate name list. + - Put Vitest settings on `testRunner.options` (`configFile`, `dir`, `related`). There is no `vitest` block. + - Put TypeScript checker settings on that checker's `options` (`prioritizePerformanceOverAccuracy`). There is no `typescriptChecker` block. + + Before: + + ```ts + export default defineConfig({ + testRunner: 'vitest', + checkers: ['typescript'], + plugins: [ + import.meta.resolve('@systemfsoftware/stryker-js-vitest-runner'), + import.meta.resolve('@systemfsoftware/stryker-js-typescript-checker'), + import.meta.resolve('@systemfsoftware/stryker-ignorer-effect-schema-declarations'), + ], + vitest: { configFile: 'vitest.config.ts' }, + typescriptChecker: { prioritizePerformanceOverAccuracy: true }, + ignorers: ['effect-schema-declarations'], + }) + ``` + + After: + + ```ts + export default defineConfig({ + testRunner: { + plugin: import.meta.resolve('@systemfsoftware/stryker-js-vitest-runner'), + options: { configFile: 'vitest.config.ts' }, + }, + checkers: [ + { + plugin: import.meta.resolve('@systemfsoftware/stryker-js-typescript-checker'), + options: { prioritizePerformanceOverAccuracy: true }, + }, + ], + ignorers: [ + import.meta.resolve('@systemfsoftware/stryker-ignorer-effect-schema-declarations'), + ], + }) + ``` + +## 5.0.0 + +### Major Changes + +- Every module Stryker loads — config files, `extends` targets, plugins, and the vitest runner's own vitest — now resolves and imports through Node's native ECMAScript module system: `import.meta.resolve` resolves each bare specifier from the consuming module, and dynamic `import()` loads the resolved URL. The package's `exports` map (or a legacy `main`) selects the entry file, ESM-only plugin packages load, and no require-based loader ships in any package. An unresolvable bare specifier warns with a machine-readable reason; the run fails at prepare only when nothing provides the configured runner or checker. Node.js 22.18.0 or later is required. + + To migrate, install Node.js 22.18.0 or later. Bare plugin specifiers keep working, but a plugin package must publish an ES module entry through `exports` or `main`, and the plugin must be resolvable from where Stryker itself is installed. + +- The plugin system now runs each configured TestRunner/Checker/Reporter plugin in its own spawned process over an @effect/rpc wire: plugins load from the project's config-declared specifiers (no glob discovery), boundary failures are typed errors, and plugin spans link into the host trace. Breaking: the in-process plugin Layer contract is removed. + +### Patch Changes + +- Maintenance release. Every published entry point, export, option, and behaviour is exactly as it was. + +- effect peer range widens to ^4.0.0-rc.112; repository metadata now points at stryker-js-effect + +- The worker environment now arrives on the spawn params instead of mid-init self-mutation, and the setup filename uses a uuid instead of the process id. + +## 4.0.4 + +### Patch Changes + +- Imports rewired to the collapsed `@systemfsoftware/stryker-js` root entry; each package now co-releases against the root-entry major. + +- Updated dependencies: + - @systemfsoftware/effect-cell-types@8.0.0 + - @systemfsoftware/stryker-js@4.0.0 + +## 4.0.3 + +### Patch Changes + +- Releases the workspace so its published versions track the shared dependency graph this change moves. + +- The `@systemfsoftware/source` export condition is gone. It resolved to a package's TypeScript sources for editors and in-repo typechecks; each package now exports only its built entry. If your tsconfig sets `customConditions: ["@systemfsoftware/source"]`, or a bundler config names that condition, remove it — resolution falls back to the built entry, which is what every consumer already got. + +## 4.0.2 + +### Patch Changes + +- Updated dependencies: + - @systemfsoftware/effect-cell-types@7.0.0 + +## 4.0.1 + +### Patch Changes + +- Re-released against @systemfsoftware/stryker-js without the removed --llms manifest. The Run stream no longer carries a manifest terminal event, and the RunEvent / RunTerminalEvent unions no longer include the manifest arm, so any exhaustive consumer of those types must drop that case. + +- Rebuilds against updated workspace dependencies, including the new + `@systemfsoftware/stryker-js` reporter protocol major. + +- Updated dependencies: + - @systemfsoftware/stryker-js@3.0.0 + +## 4.0.0 + +### Major Changes + +- The packages are renamed. `plugin-api` is now `@systemfsoftware/stryker-js`, the + language every plugin is written against. `mutation-run` is split: the run + itself is `@systemfsoftware/stryker-js-engine` + (host-neutral, no Node on its manifest) and the Node process entries are + `@systemfsoftware/stryker-js-cli`, which owns the worker files and the runtime + gate. `mutation-report` is now `@systemfsoftware/stryker-js-html-reporter`. + `@systemfsoftware/stryker-js-platform-node` is never published — do not install + it. Install the new names and change your imports. + + Options types moved. `StrykerOptions`, `PartialStrykerOptions` and `LogLevel` are + imported from the `Schema` export; `Mutant`, `MutantStatus`, `Position` and + `Location` from the `Mutant` export. Point a config's `extends` at the language + package's `Schema` export. + + `MutantStatus` accepts one spelling per outcome: `Killed`, `Survived`, + `NoCoverage`, `Timeout`, `CompileError`, `RuntimeError`, `Ignored` and `Pending`. + The lowercase and abbreviated forms — `killed`, `timedOut`, `noCoverage` and the + rest — are gone. A comparison against a removed spelling never matched the value + the reporter actually produced, so check any status comparison you wrote. + + Statuses, plugin kinds, exit classes and AST formats are string literal unions + rather than enums, so read them as their string values. Member access such as + `ExitClass.VerdictFail` no longer resolves. + + A plugin no longer receives a logger, and the logger port is gone. Plugins log + through Effect, and the host decides where that output goes. + + The bundled base preset is gone. A config inherits from the language package's + `Schema` export and states the thresholds, reporters and plugins it wants; you no + longer silently inherit a package manager, a plugin list or a break threshold. + +- effect is now a required peer dependency. Install it alongside these packages. They previously bundled their own copy, which meant two Effect instances in one process and services that could not find each other across the boundary. + +### Minor Changes + +- Machine-readable mutation progress is a newline-delimited JSON file next to the HTML and JSON reports, not the console. + + The console prints bounded progress prose: phase names, a count line, at most twenty surviving mutants, and a verdict. Killed mutants now advance that count. Child test runs during mutation no longer print per-test output or GitHub workflow commands. + + If you parsed the console as JSON lines, read the stream file instead. A hard kill can leave that file without a closing verdict line. + +### Patch Changes + +- New version is published through npm trusted publishing, so it carries a provenance attestation you can verify. + +- Peer Effect requirement advances to 4.0.0-rc.112. No API changes. + +- A mutation run writes the JSON report the `json` reporter is configured to produce. + + Vitest no longer reprints its full summary for every mutant. + +- Each of these packages now has a README, so its registry page says what the package is, how + to install it, and what to import or register — previously the page was blank. The lint + plugins show the configuration line that enables what they recommend. + + `@systemfsoftware/stryker-js-html-reporter` also carries its licence text + +- The shared helpers package is gone. Nothing installs it any more, and the + handful of helpers worth sharing now live in the plugin contract next to the + types they serve: + + - `strykerReportBugUrl`, `normalizeFileName`, `propertyPath`, `errorToString` + and `isErrnoException` from `@systemfsoftware/stryker-js/core` + - `noopLogger` from `@systemfsoftware/stryker-js/logging` + - `testFilesProvided` from `@systemfsoftware/stryker-js/test-runner` + + If you imported any of those, change the specifier. Everything else it exported + had no consumer and is removed: use `Predicate.isNotNullish` from Effect in place + of `notEmpty`, and `RegExp.escape` in place of `escapeRegExp`. + +- These packages no longer install dependencies they never imported, so installing them pulls less into your tree. + + `tslib` is gone from all six. The mutation runner additionally stops installing `lodash.groupby`, `semver` and `source-map`, and the command line interface stops installing `@effect/platform-node-shared`. Nothing exported changes. + +- Published packages no longer carry build artifacts left over from earlier builds. One package was shipping about a megabyte of bundled test-runner internals this way. + +- Updated dependencies: + - @systemfsoftware/stryker-js@2.0.0 + +## 3.0.0 + +### Major Changes + +- The dry-run decision moved to a plain kernel (dryRunCell removed); the mutant-run decision splits into branded MutantKilled|MutantSurvived|MutantTimeout|MutantDryError variants + +### Patch Changes + +- Updated dependencies: + - @systemfsoftware/effect-cell-types@6.0.0 + - @systemfsoftware/stryker-js@1.0.0 + +## 2.1.0 + +### Minor Changes + +- Plugin code can now ask for the module-loader service instead of touching the host module API: it exposes the same `createRequire`/`isBuiltin` surface as the host module module, and the Node engine supplies it automatically. Plugins that declare their environment through the engine's plugin declaration get the service without extra wiring; upgrades should move the engine and its plugins in the same release. + +### Patch Changes + +- Refreshed builds on the platform-services dependency graph; the packages no longer reach for host builtins directly. No CLI flags or option names change. + +- Updated dependencies: + - @systemfsoftware/stryker-js@0.2.0 + +## 2.0.2 + +### Patch Changes + +- Re-published against the oxc-based instrumenter: workspace dependency ranges move to the new instrumenter major; no package's own behavior changes in this release. + +## 2.0.1 + +### Patch Changes + +- Peer Effect requirement advances to 4.0.0-rc.112. No API changes. + +## 2.0.0 + +### Major Changes + +- Three packages are renamed. `plugin-api` is now `@systemfsoftware/stryker-js`, the + language every plugin is written against. `mutation-run` is now + `@systemfsoftware/stryker-js-platform-node`, the Node host that runs a mutation + test. `mutation-report` is now `@systemfsoftware/stryker-js-html-reporter`. + Install the new names and change your imports. + + Options types moved. `StrykerOptions`, `PartialStrykerOptions` and `LogLevel` are + imported from the `Schema` export; `Mutant`, `MutantStatus`, `Position` and + `Location` from the `Mutant` export. Point a config's `extends` at the language + package's `Schema` export. + + `MutantStatus` accepts one spelling per outcome: `Killed`, `Survived`, + `NoCoverage`, `Timeout`, `CompileError`, `RuntimeError`, `Ignored` and `Pending`. + The lowercase and abbreviated forms — `killed`, `timedOut`, `noCoverage` and the + rest — are gone. A comparison against a removed spelling never matched the value + the reporter actually produced, so check any status comparison you wrote. + + Statuses, plugin kinds, exit classes and AST formats are string literal unions + rather than enums, so read them as their string values. Member access such as + `ExitClass.VerdictFail` no longer resolves. + + A plugin no longer receives a logger, and the logger port is gone. Plugins log + through Effect, and the host decides where that output goes. + + The bundled base preset is gone. A config inherits from the language package's + `Schema` export and states the thresholds, reporters and plugins it wants; you no + longer silently inherit a package manager, a plugin list or a break threshold. + +### Minor Changes + +- Machine-readable mutation progress is a newline-delimited JSON file next to the HTML and JSON reports, not the console. + + The console prints bounded progress prose: phase names, a count line, at most twenty surviving mutants, and a verdict. Killed mutants now advance that count. Child test runs during mutation no longer print per-test output or GitHub workflow commands. + + If you parsed the console as JSON lines, read the stream file instead. A hard kill can leave that file without a closing verdict line. + +### Patch Changes + +- A mutation run writes the JSON report the `json` reporter is configured to produce. + + Vitest no longer reprints its full summary for every mutant. + +- Updated dependencies: + - @systemfsoftware/effect-cell-types@5.0.0 + +## 1.0.0 + +### Major Changes + +- effect is now a required peer dependency. Install it alongside these packages. They previously bundled their own copy, which meant two Effect instances in one process and services that could not find each other across the boundary. + +### Minor Changes + +- The Vitest runner accepts `setupFilePath`, naming the setup file it copies into + the sandbox. It defaults to the file shipped beside the runner's own module, + which is the right answer for an installed package; supply it only when you are + running the runner from sources rather than from an install. + +### Patch Changes + +- The shared helpers package is gone. Nothing installs it any more, and the + handful of helpers worth sharing now live in the plugin contract next to the + types they serve: + + - `strykerReportBugUrl`, `normalizeFileName`, `propertyPath`, `errorToString` + and `isErrnoException` from `@systemfsoftware/stryker-js-plugin-api/core` + - `noopLogger` from `@systemfsoftware/stryker-js-plugin-api/logging` + - `testFilesProvided` from `@systemfsoftware/stryker-js-plugin-api/test-runner` + + If you imported any of those, change the specifier. Everything else it exported + had no consumer and is removed: use `Predicate.isNotNullish` from Effect in place + of `notEmpty`, and `RegExp.escape` in place of `escapeRegExp`. + +- These packages no longer install dependencies they never imported, so installing them pulls less into your tree. + + `tslib` is gone from all six. The mutation runner additionally stops installing `lodash.groupby`, `semver` and `source-map`, and the command line interface stops installing `@effect/platform-node-shared`. Nothing exported changes. + +- Published packages no longer carry build artifacts left over from earlier builds. One package was shipping about a megabyte of bundled test-runner internals this way. + +- Updated dependencies: + - @systemfsoftware/stryker-js-plugin-api@3.0.0 + +## 0.1.4 + +### Patch Changes + +- Plugins are discovered under this scope by default. The `plugins` option now + defaults to a glob over this scope instead of the original project's, so a + configuration that relied on the previous default while installing plugins from + the original project must now list those plugins explicitly. Discovery also no + longer fails when no matching scope directory is installed: it loads nothing and + warns. + + The report's dependency section names the packages of this scope, and the + report's home link points at this project. + +- Updated dependencies: + - @systemfsoftware/stryker-js-util@0.2.0 + +## 0.1.3 + +### Patch Changes + +- Related mode now follows a published import of the package under test to the copy Stryker is mutating. + +## 0.1.2 + +### Patch Changes + +- New version is published through npm trusted publishing, so it carries a provenance attestation you can verify. + +- Each of these packages now has a README, so its registry page says what the package is, how + to install it, and what to import or register — previously the page was blank. The lint + plugins show the configuration line that enables what they recommend. + + `@systemfsoftware/stryker-js-mutation-report` also carries its licence text + +- These packages no longer ship their development files. Sources, tests and build, lint and + test configuration were all included, which broke consumers in one specific way: oxlint + discovers configuration by walking directories, finds the published `oxlint.config.ts` under + `node_modules`, and stops with "Stripping types is currently unsupported for files under + node_modules" before linting anything. + + The installed package now contains the compiled output, the runtime schema files it reads, + and the usual manifest, README and licence + +- Updated dependencies: + - @systemfsoftware/stryker-js-plugin-api@2.0.0 + +## 0.1.1 + +### Patch Changes + +- New version is published through npm trusted publishing, so it carries a provenance attestation you can verify. + +- Each of these packages now has a README, so its registry page says what the package is, how + to install it, and what to import or register — previously the page was blank. The lint + plugins show the configuration line that enables what they recommend. + + `@systemfsoftware/stryker-js-mutation-report` also carries its licence text + +- These packages no longer ship their development files. Sources, tests and build, lint and + test configuration were all included, which broke consumers in one specific way: oxlint + discovers configuration by walking directories, finds the published `oxlint.config.ts` under + `node_modules`, and stops with "Stripping types is currently unsupported for files under + node_modules" before linting anything. + + The installed package now contains the compiled output, the runtime schema files it reads, + and the usual manifest, README and licence + +- Updated dependencies: + - @systemfsoftware/stryker-js-plugin-api@1.0.0 diff --git a/packages/stryker-js/CHANGELOG.md b/packages/stryker-js/CHANGELOG.md new file mode 100644 index 000000000..1470ca173 --- /dev/null +++ b/packages/stryker-js/CHANGELOG.md @@ -0,0 +1,847 @@ +# @systemfsoftware/stryker-js + +## 17.0.0 + +### Major Changes + +- A mutant's timeout now starts when its first test begins, not when the test runner is asked to run it. With a short `timeoutMS`, a mutant its tests would kill is reported `Killed` instead of `Timeout`, so a slow or busy machine no longer changes the verdict. A mutant whose tests never begin still ends, on the same window that bounds a plugin worker which never accepts its connection. + + If you author a test-runner plugin, `mutantRun` now streams its run: emit `MutantRunStarted` before the runner's first test begins, then `MutantRunSettled` carrying the result. + +### Patch Changes + +- A merged shard report now records the run budget, so a budget check can read it and `--update-budget-baseline` can bootstrap a baseline from it. Before, every merged report was refused with `the finished mutation report records no budget`. The budget's actual time is the slowest shard: the sum of its projects' run times, because shards run side by side. The predicted time is the plan's largest per-shard prediction. If any project's progress stream holds no verdict, the merged report carries no budget. + +## 16.0.1 + +### Patch Changes + +- A shard run keeps going when one of its projects scores under `thresholds.break` over the shard's mutants. It runs the remaining projects, exits 0, and notes that the merged report carries the project verdict. A shard fails only when a project fails for a real reason: another exit code, or no progress stream. Its error names the project, the exit code and the tail of that project's error output. + + Every non-zero exit now says why in human mode. One final stderr line names the exit code, its class (`VerdictFail`, `ConfigError`, `RuntimeError`, `InternalError`) and the failure with its remediation. A rejected survivor check and a refused re-run no longer print it twice. + + A config file that throws while loading now reports the thrown error instead of `Failed to read config`. + +## 16.0.0 + +### Major Changes + +- Mutation testing is now incremental by default: `incremental` defaults to `true`, so an unchanged mutant whose covering tests are unchanged is re-used instead of re-run. `--full` (aliasing the old `--force`) re-verifies every mutant, ignoring the cache and the persisted dry run. The `verdict` stream event records `incrementalMode` (`incremental` or `full`). A run with no reusable cache falls back to a full run as before. + +- The incremental report and its checkpoints now record a `costs` entry for every mutant: the predicted milliseconds from the measured dry-run times of its covering tests plus the run's fixed overhead, the wall time of its last executed run, and its covering-test count. A verdict reused from an earlier run carries its recorded `actualMs` forward instead of losing it. The incremental cache layout moves to version `3`, so a report written by an earlier release is discarded rather than reused. + +- A mutation run now starts only on main CI. The `run` command, the programmatic run entry point, and every other surface that begins a mutation run refuse to start unless the process runs under GitHub Actions (`GITHUB_ACTIONS=true`); anything else — a developer machine, a fork, or another CI — exits non-zero instead of running mutants. A refused run ends the machine-readable output with a new `refused` event whose `rule` is `mutation-runs-on-main-ci`, and the machine-stream schema version is now `3.0`. Dry runs (`--dryRunOnly`), `--version`, and config and help commands are unaffected. + +- The old report-merge command becomes `merge`, which now reads the shard plan and the per-shard report directories and combines them into one mutation report and a per-project incremental report. A merge fails, naming the mutants, when a planned mutant is missing from its shard report or reported by two shards. A new shard mode runs exactly the mutants of one shard, spawning a child run per project the shard lists, so a plan can be executed across parallel jobs and merged afterwards; mutants outside a shard are neither run nor reported. + +- The TypeScript checker now applies Trivial Compiler Equivalence (Papadakis et al., ICSE 2015). Each mutant of a file is emitted to JavaScript from the project's own TypeScript configuration in transpile-only mode, normalized, and compared with the original file's emit and with the mutants already emitted at the same site. A mutant whose emit equals the original's is dropped as `Ignored` with reason `equivalent-to-original: tce`; one that equals an earlier same-site mutant's is dropped with `duplicate-at-site: tce`; every mutant whose emit differs is kept, so the check never removes a mutant that changes the program. Checkers gain an `ignored` result variant carrying the suppression reason, and a run publishes the new `tce` machine-stream event with the `equivalentToOriginal` and `duplicateAtSite` counts. Verdict semantics move to version 3. + +- Every mutation and incremental run now records a `budget` of predicted and actual seconds on its machine verdict line and in its mutation and incremental reports, derived from the measured cost of the mutants it scheduled and the wall time the run took. `stryker gate` takes `--budget-baseline `, an optional `--budget-tolerance` (default 0.25), and `--update-budget-baseline`; it fails with exit 1 when the finished run's actual seconds exceed the committed baseline by more than the tolerance, naming both numbers, and rewrites the baseline with the current run when updating. + +### Minor Changes + +- A new `stryker plan` subcommand discovers each project's mutants without running tests, decides reuse against that project's incremental report, costs every scheduled mutant, and packs them into deterministically LPT-scheduled shards. It takes `--target-seconds`, optional `--max-shards`, `--projects`, `--out`, and `--full`, writes a `ShardPlan`, and emits a `plan` stream event. + +- Every test-runner and checker process boot now publishes a `worker` line on the machine stream carrying its role, boot ordinal and measured start-up time, so the once-per-worker start-up cost is observable instead of hidden behind the first mutant's per-run overhead. The stream schema version is now `6.0`. + +### Patch Changes + +- The README now has an upgrade note for 13.x users. 14.0.0 made `'default'` the default `mutator.mutantSetPolicy`, so scores graded under 13.x and later releases cover different mutant sets and are not comparable. The note names where each run records its policy, and states that `mutator: { mutantSetPolicy: 'full' }` keeps the 13.x mutant set. + +- A cached incremental report that cannot be decoded now names the decode failure in the run log, so a cache written by an older release says which field the reader refused instead of only that the file could not be parsed. + +- Repeating an incremental run in a workspace that builds with Turbo now reuses the pending dry-run coverage and the cached verdicts again. A package's `.turbo` task logs are generated by each build and record that build, so they no longer count as project files the run must watch: a rebuild no longer invalidates coverage whose inputs did not change. + +- Mutants no test covers are now verified by the configured checkers before they are reported `NoCoverage`. A checker that rejects such a mutant reports it `CompileError`, with the checker's reason, instead of `NoCoverage`; uncovered mutants that pass every checker are still reported `NoCoverage`. Previously the checkers never saw uncovered mutants, so a non-compiling mutant no test reaches counted as an undetected survivor. + +- The incremental report and its checkpoints are now written atomically: the bytes go to a temporary file in the same directory and are renamed over the target, so an interrupted or killed run (a job-cap timeout, or a process stopped while the report is being saved) can no longer leave a truncated incremental file behind. A run that finds a cache written this way decodes it and reuses it instead of discarding the whole mutation history. The mutation report, summary, and incremental report that `stryker merge` writes are saved the same way. + +## 15.0.1 + +### Patch Changes + +- `cleanTempDir` now decides from how the run ended. With the default (`true`), a successful run removes its `.stryker-tmp/sandbox-*` directory and a failed run keeps it; previously every sandbox was kept, so `.stryker-tmp` grew with each run. `cleanTempDir: false` now keeps the sandbox after every run instead of deleting it. `'always'` still removes it after every run. + +- A test runner, checker, or reporter process that stops responding no longer hangs the mutation run forever. When such a process does not exit within 5 seconds of being asked to stop, Stryker force-kills it, so the run ends with an error instead of waiting until something kills it from outside. + +- A test runner, checker, or reporter process that starts but never accepts its connection no longer hangs the mutation run forever. Stryker gives each plugin worker 30 seconds to accept the connection on `STRYKER_SOCKET`; a worker that misses that window fails the run with a boot error naming its process id, and the process is stopped. The window does not depend on `dryRunTimeoutMinutes`. A worker that has already connected still reconnects after a dropped connection, as before. + +## 15.0.0 + +### Major Changes + +- Effect moves to the stable `4.0.0` release, together with the `@effect/*` packages these libraries use. The `4.0.0` release candidates are no longer supported: install `effect` `^4.0.0` next to these packages before upgrading. + + The TypeScript checker and test-runner plugins bundle their own Effect runtime, so they need no change in your project. + +## 14.0.0 + +### Major Changes + +- The incremental cache now reuses a verdict only when nothing that could change it has changed: the covering tests and every module they import, the run inputs (lockfile and configuration), and the mutant-set policy. Moving code or releasing a new version no longer throws the cache away. Incremental reports written by earlier versions are discarded once. + + A mutant whose covering tests did not run is retried once, then reported as `RuntimeError` with reason `no covering test executed`. + + The new `mutator.mutantSetPolicy` option defaults to `'default'`. Under it, mutants in logging, telemetry, timing, and configuration-default calls, redundant relational variants, and replacements identical to the original are reported as `Ignored` with a named rule instead of being run. Set `mutator.mutantSetPolicy: 'full'` to keep the previous mutant set. + +### Minor Changes + +- The new `annotate` command prints GitHub workflow annotations at the location of every survivor of the finished report, so a run's survivors show up on the changed lines of a pull request. + +- The new `compare` command names every mutant whose status differs between a baseline report and a fresh one, leaving out the mutant ids passed with `--noise`. + +- `stryker feedback --useful|--not-useful [--reason ]` appends one `feedback` stream line for a surfaced survivor to reports/mutation/feedback.jsonl. + +- The new `gate` command fails when a run leaves survivors that are missing from a committed baseline of accepted survivors, lists them with how to accept them, and `--update-baseline` rewrites the baseline from the finished report. + +- `stryker mcp` serves the finished mutation report to MCP clients over stdio, with tools `list_survivors`, `show_mutant`, `rerun_mutant`, and `report_usefulness`. + +- Serving the Mutation Server Protocol over stdio and sockets lets editors and agents configure, discover, and mutation-test a project through framed JSON-RPC, with one run at a time, a loopback-only default listener, and named errors for an unusable bind. + +- Every run now also writes a reproducer for each mutant: a unified diff against the original source, so a verdict can be re-checked locally without hunting for which mutation produced it. + +- Naming mutant ids on the run command re-runs exactly those mutants, restricting the mutated files to their ids' files with verdict reuse left on and reporting each selected mutant's status, covering tests, killing test, and reproducer command, while the trace names the new rerun span for that path. + +- The new `sarif` reporter writes a SARIF 2.1.0 log beside the JSON report with one result per capped survivor, each carrying its mutant id in `partialFingerprints` so code scanning can track it. + +- The new `--since ` option mutates only the lines changed since the merge base with ``, and falls back to a full run when the Stryker config, a test-runner config, the package manifest, or the lockfile changed. + +- The clear-text report adds a `Static mutants:` line with how many mutants are static and their share of the total mutant cost. + +- The new `surfacing` option (default `{ perLine: 1, perFile: 7 }`) caps how many survivors are listed per line and per file, while every survivor still counts toward the score. + +- With `--incremental`, an unchanged project now skips the initial test run; that run otherwise executes the suite twice to find flaky tests, and a verdict that depends on a flaky test, or any static mutant while a test is flaky, is re-tested instead of reused. + +### Patch Changes + +- With checkers configured, a mutant is sent to a test runner as soon as its own check group passes instead of waiting for earlier groups, so `mutant` events can arrive out of plan order while the final report keeps plan order. + +- Incremental runs no longer hold a test runner while the checkpoint is rewritten after every mutant; the checkpoint is written in the background at most once per second, and an interrupted run still keeps every mutant it finished. + +- Fixed a rare hang where a run stopped making progress while a test runner or checker worker waited for a message that had already arrived. The message now always wakes the worker waiting for it. + +- Repeating an incremental run with no source change now reuses every verdict in a project that defines no ignore patterns of its own: a run's own reports are no longer treated as changed inputs, and options that cannot change a verdict no longer invalidate reuse — neither the ones that only select which mutants and tests run (a git ref scope, a single mutant id) nor reporting and logging options such as reporters, colors, thresholds, surfacing or log levels. + +- A re-tested mutant now runs the test file of its previous killing test first, and mutation reports keep naming the right killing test after tests are added or reordered. + +- Incremental runs now reuse verdicts written under a different `incrementalFile`, `incrementalSources`, `tempDirName` or `cleanTempDir`, so a shard can reuse the verdicts another shard recorded for a file that moved between shards. + +- Changing only the version in a package manifest that tests import no longer invalidates the incremental verdicts of those tests, so releasing a package keeps its cached mutation results. + +## 13.1.0 + +### Minor Changes + +- The `stryker` CLI now continues a caller's trace: when `TRACEPARENT` (and optionally `TRACESTATE`) is set in its environment, the `stryker.cli.run` span and everything beneath it join that trace as a child of the carried span, following the OpenTelemetry environment-carrier specification. An absent or malformed `TRACEPARENT` leaves the run on its own root trace, as before. + +### Patch Changes + +- The packages now depend on `@systemfsoftware/effect-cell-types` 11. + + - Every tagged error now has a one-line message built from its fields, so a failure names the file, mutant, plugin, worker or exit code involved instead of an empty message. The errors' tags, fields and encoded forms are unchanged. + +## 13.0.0 + +### Major Changes + +- A child process exit code is the POSIX status domain now: any value accepted as a `ChildExitCode` is an integer in `0..255`, and a value outside that range is refused. The out-of-memory exit codes are a named, exported domain instead of a private list, so classifying a worker exit depends on a declared value rather than a hidden one. + +- The engine's file matching, reporter names, CLI command list, ANSI colours, report stream file name and planned-mutant failures are each declared once. `FileMatcher` keeps only its pattern and hidden-file flag: it no longer carries a `matches` method. + + - The removed `RelativeNormalizedFileName` is replaced by the engine's own normalization, and a report's stream file name is the literal the engine writes. + - `AnsiColor` is the type of the colour codes rather than a schema, and `CliCommandSchema` plus the reporter-name schemas name the command list and the reporter names. + - A mutant whose computed timeout is not a finite number now fails planning with the `MutantTimeoutNotFinite` failure naming that mutant. + +- Exit codes are one value: `Plugin.ExitCode`, an integer between 0 and 255. The machine stream's failure event, the command's run output, and the run conclusion all carry it, and a run conclusion's outcome is the closed set of run-outcome tags rather than free text. + + `Plugin.ExitCodeFromClass` stays the single exit-class to baseline-code mapping; read a code through it instead of writing the number where an exit class has to become an exit code. + +- Mutant ids are decimal index strings. Every schema that carried a mutant id as a plain string now decodes `Mutant.MutantId` and refuses anything else: the mutation report and its mutant results, reporter events (`mutationTestingPlanReady` plans, `mutantTested`), the machine run stream (`mutant` and `verdict`), the survivors prior report, and the mutant coverage keys produced by test runners. + + Mutant ids are minted only as the mutant's canonical non-negative decimal index (`Mutant.MutantId`), including in the instrumenter's planned mutants, placement sites, and checker answers. Consume the new `Mutant.MutantId` schema instead of assuming an arbitrary non-empty string; ids such as `constructor` or `__proto__` no longer decode. + +- A mutant's status is declared once, as the eight-value `Mutant.MutantStatus`, plus the named subsets a run partitions on: `Mutant.SurvivorStatus`, `Mutant.RememberedStatus`, `Mutant.EphemeralStatus`, and `Mutant.ActionableStatus`. Import the subset your decision matches instead of re-listing the statuses you accept. + + Mutants are tagged structs now. A status reason decodes only together with a status, and the remembered-mutant and ignored-mutant rows carry the same narrowed status as the subset they were selected by. + +- The CLI no longer checks the Node version at startup or prints an `UnsupportedNodeVersion` message. `engines.node` (`>=24.13.1`) is the only statement of support: on an older Node the CLI crashes on the first Node 24 API it calls. + +- Human or machine output is declared once, as `OutputMode`, and the reason for that choice once, as `ModeSignal`. The run stream's start and verdict events, the resolved configuration environment, and the machine framer all decode those two schemas. + + A caller that spelled a mode as a plain string union imports the schema's type instead; the duplicate literal lists are gone. + +- A plugin load failure is one tagged union. The machine stream's failure event now carries that union — the missing peer, the unsupported peer version, the unrecognized peer, the invalid contribution, or the import that failed — instead of a bare tag string, and a framework refusal's reason is the shared peer-failure subset of the same vocabulary. + + A consumer that compared the failure reason to a string reads the union's tag or its fields instead. + +- A report-relative file name is one normalized value again: the canonical file-name brand whose decoder folds backslashes to forward slashes. The separate report file-name brands are gone, and the mutation report and mutation part names are schemas a consumer reads rather than strings it repeats. + +- The machine stream's tested-mutant event is one declaration now. `RunEvent.RunMutantTested` is a codec whose type is `Reporter.MutantTested` and whose encoded form is the stream's `{"_tag":"mutant"}` line, so the stream carries the domain event and the boundary renames its tag and fields. The emitted stdout bytes and their order are unchanged. + + Two published names moved with it: `RunEvent.RunMutantTested` no longer constructs (`RunMutantTested.make` is gone; build a `Reporter.MutantTested` instead), and `RunEvent.MetricsResultFromReport.fromFiles` is replaced by the function `RunEvent.metricsResultFromFiles`. + +- Source locations are 1-based and validated end to end. `Mutant.Location`, `Mutant.Position`, `Mutant.Span`, `Mutant.OpenEndLocation`, `Mutant.ScriptOrigin`, and `Mutant.LineStarts` replace the former `LocationSchema`, `PositionSchema`, and `OpenEndLocationSchema`; a location whose end precedes its start, or a line or column below 1, no longer decodes. + + - A mutant location always speaks the report contract, so a producer that minted 0-based coordinates or a bare number must switch to the new schemas. + - An embedded script's origin is a `ScriptOrigin` (1-based line plus a column shift), not a position. + - Incremental state from releases that stored the earlier column base is no longer matched: those mutants are re-tested, not reused, while the run rewrites the state. + +- Test identifiers are one value: `TestRunner.TestId`, a non-empty branded string carrying the runner's `file#test name` form. Test runner results, report test definitions, a mutant's killers and coverers, per-test hit records, and the test-contribution evaluation all speak that one type instead of bare strings. + + Mint one with `TestRunner.TestId.make(...)` where a runner derives a test id; the machine stream and mutation report schemas brand the ids they decode. + +- Mutation score thresholds are declared once, as `Report.ThresholdsSchema`: `high`, `low`, and a `break` that is null when no breaking threshold is configured. The option file and the mutation report decode the same schema, and a pair whose `low` is above its `high` is refused with "a mutation score threshold pair has low at or below high". + + The mutation report's thresholds now include `break`, so a consumer reads the breaking threshold from the report instead of recovering it from the embedded reporter configuration. + +### Patch Changes + +- The TypeScript checker describes a tsconfig document through the schema that declares the keys it interprets, so an override round-trips the document it was given, and the mutant it cannot describe to a checker carries the canonical file-name brand. The plugin contract, configurations, reports, and exit codes are unchanged. + +- Stages now run as cells over workflows, multi-item work runs as Effect streams, pools and worker transport use scoped Effect resources, and named operations are traced with Effect.fn. The Angular ignorer now declares @systemfsoftware/stryker-ignorer-kit as a runtime dependency. + +- A mutant whose test run exceeds the wall-clock timeout is now reported as `Timeout` (reason `wall-clock-timeout`) and the run continues with the remaining mutants. The hung test-runner worker is killed and replaced instead of aborting the whole run with exit code 4. + +- Updated dependencies: + - @systemfsoftware/stryker-js-vitest-runner@8.0.0 + +## 12.1.1 + +### Patch Changes + +- The `stryker.cli.run` trace span now records the failure text in its `stryker.run.error` attribute for every failed run. It was empty unless the run was interrupted, so a failed dry run, a failing checker, or a rejected argument left no reason in the exported trace. + +## 12.1.0 + +### Minor Changes + +- A piped `stdout` now gets human output, exactly like a terminal. Machine consumers ask for the NDJSON event stream explicitly, with `--json` or `STRYKER_MODE=machine`; `--format text` names the human format, and passing it together with `--json` is a usage error (exit 2). Under `--json`, `stdout` carries wire records and nothing else, while progress status lines and log output stay on `stderr`. + + Every run also writes those wire records to `reports/mutation-stream.jsonl` in human mode too, so a job that shows a readable log still produces the artifact `stryker merge-reports` rebuilds a shard's report from. + + If a script or CI step read NDJSON from a piped run without requesting it, pass `--json` (or set `STRYKER_MODE=machine`) there. + + Custom `RunEventDrain` implementations must accept a required `toStdout` argument in `drainFramed`; pass-throughs can ignore it. + +### Patch Changes + +- A run whose selected files produce no mutants, for example because no loaded framework claims them, now runs every test in the dry run and finishes with an empty report. With `testRunner: 'vm'` or `'vitest'` it used to fail with "No tests were executed", because the dry run only looked for tests related to those files. The dry run now relates tests only to the files that carry mutants. + +- A run whose `mutate` patterns match no file now finishes successfully instead of stopping with an instrument error. It performs the dry run, writes a mutation report with no files, and passes regardless of `thresholds.break`, the same as upstream StrykerJS. A sharded run whose shard receives no file no longer fails. + + `stryker merge-reports` shows `n/a` for a package whose report has no tested mutant, instead of a score of `0.00`. + + Errors reported for the prepare, dry-run, and mutation-testing stages now carry their reason as the error message, so a printed cause no longer shows up as a bare error name. + +- `stryker merge-reports` now rebuilds a package's partial report from the mutants recorded in its stream part when the run ended before writing its final report. Earlier versions recognized none of the recorded mutants and reported the package as having no report. + +- Mutation runs with `testRunner: 'vm'` finish in about half the time, with the same verdicts. The engine checks mutant groups on every checker process at once, stops the checkers as soon as checking ends, hands their share of `concurrency` to the test runners, and shares a Node compile cache with every worker it starts. The Vitest runner starts the next isolated worker thread while the current test file runs, so each file still gets a fresh thread but no longer waits for one to boot. On a 319-mutant TypeScript project with the TypeScript checker, a run went from 24.9 s to 12.6 s. + +- `testRunner: 'vm'` runs Vitest itself on Vitest's isolated `threads` pool instead of an in-process reimplementation, and stays the default `testRunner`. It reports the same test ids, outcomes and per-mutant verdicts as `testRunner: 'vitest'`, so a project whose Vitest config enables browser mode is refused at startup with a message naming `testRunner: 'vitest'`. + + Stryker no longer discovers test files for `vm`: Vitest selects them from your config, as it does for `testRunner: 'vitest'`. A run that loads no test files fails the dry run naming `testFiles`. + +## 12.0.0 + +### Major Changes + +- Restore `defineConfig` and `mergeConfig` on `@systemfsoftware/stryker-js/config`. `defineConfig` accepts an options object, a promise of one, or a `(env: ConfigEnv) => …` factory, as in 10.1.1, and `mergeConfig(defaults, overrides)` composes presets again. `StrykerConfig` is again the partial-options type, so `const config: StrykerConfig = defineConfig({ … })` typechecks. Config files written against the 10.1.1 `./config` surface load and typecheck without edits. + + This removes the 11.0.0 `StrykerConfig` class value. If you adopted it: + + - Replace `StrykerConfig.define(…)` with `defineConfig(…)` and `StrykerConfig.merge(…)` with `mergeConfig(…)`, imported from `@systemfsoftware/stryker-js/config`. + - Replace `StrykerConfig.createDefaultOptions()` and `StrykerConfig.defaultOptions` with `Configuration.createDefaultOptions()` and `Configuration.defaultOptions` from the package entry point. + +### Patch Changes + +- A failed dry run now says which tests broke and why. The refusal names every failing test with its failure message in the error output and in the log at the default level, instead of reporting only how many failed. + +- Installing `@systemfsoftware/stryker-js` on its own now works. `effect` was declared an optional peer even though Stryker imports it at runtime. Package managers never install optional peers, so a project without `effect` failed with `Failed to read config` before the run started. `effect` is now a required peer: pnpm and npm install it automatically, and a project that already uses Effect shares its own copy with Stryker. + +- The packages now build on the latest `@systemfsoftware/effect-cell-types` 10.2 cell kinds, with no change to their published behavior. + + - `@systemfsoftware/stryker-test-contribution` exports its evaluator as `Judge`, `TestContribution`, and `TestContributionEvaluator`, and now depends on `effect` directly. + +## 11.0.0 + +### Patch Changes + +- A mutation run no longer stalls forever when a checker or test-runner worker stops answering its connection. Previously a request that was in flight when the worker's connection dropped kept waiting on the dead connection, freezing progress until the CI timeout killed the run. Such a request now fails with a typed connection error when its connection drops, and the worker is restarted per the existing crash policy. Only requests already sent on the dropped connection fail: the client still reconnects on its own, and requests made while it reconnects, including the first request to a worker that is still booting, wait for the connection instead of failing. + +## 10.2.0 + +### Minor Changes + +- Three opt-in mutators plant Effect concurrency faults on top of the default set, so a mutation run can check that your concurrency tests catch races. `AtomicUpdateSplit` splits read-modify-write calls on `Ref` and `SynchronizedRef` into a separate read, a yield, and a separate write. `SynchronizationRemoval` removes semaphore permits, `Effect.uninterruptible`, and `Effect.uninterruptibleMask`. `FinalizerEscape` stops `ensuring`, `onExit`, `onError`, `onInterrupt`, `acquireRelease`, and `acquireUseRelease` cleanup from running on interruption. All three are off by default. Enable any subset by listing them under `mutator: { optInMutations: [...] }` in your configuration. A name that is not an opt-in mutator fails the run, and the error lists the known names. The replacements use Effect 4 APIs, so a repository on Effect 3.x must not opt in. + +## 10.1.2 + +### Patch Changes + +- `@systemfsoftware/stryker-vm-harness` is the in-memory V8 VM test-runner harness that powers `testRunner: 'vm'`. `@systemfsoftware/stryker-js` now depends on it. + +- The in-memory V8 VM runner (`testRunner: 'vm'`) now executes suites written for `vitest`, `@effect/vitest`, and `@systemfsoftware/effect-gherkin-spec` out of the box: the usual test surface — `describe`/`it`, `.skip`, `.only`, `.todo`, `.each`, `it.fails`, and hooks — behaves as it does under vitest, and each test is reported on its own with its real failure message instead of one all-tests result. Mutant runs attribute a kill to the exact test that caught the change. A suite that never finishes yields a timeout result instead of hanging the run, unhandled rejections are captured in the run report, module state is fresh on every run, and a suite that cannot compile still fails the run with a typed failure naming the file. + +## 10.1.1 + +### Patch Changes + +- Exported declarations that previously took `unknown` now take a defaulted type parameter: calls that omit the type argument are unchanged, and calls that were previously rejected for passing an unconstrained value are accepted. No runtime behaviour changes. + +## 10.1.0 + +### Minor Changes + +- A finite mutant is killed or survived from its tests. Timeout is reserved for one named nonterminating trap, detected by a hit bound rather than a wall-clock budget. A wall-clock timeout fails the run instead of being stored as a detected mutant. + +## 10.0.1 + +### Patch Changes + +- Effect moves to `4.0.0-rc.116` (with `@effect/platform-node`, `@effect/platform-node-shared`, `@effect/vitest` and `@effect/opentelemetry` on the same release), the `@systemfsoftware/*` toolchain pins move to their current releases, and `vitest` 5 with `oxc-parser` 0.150 come along. Both worker bundles (`stryker-js-typescript-checker`, `stryker-js-vitest-runner`) now inline their whole runtime rather than resolving `effect`, `@effect/*` and the sibling packages from the host's tree, so a worker runs on its own copy of the effect that built it; the project's `typescript` and `vitest` remain the only imports beside Node builtins. The filter-level `arbitrary.candidate` generators that the thresholds schemas carried are gone: v4 no longer reads them, so thresholds still reject a `low` above `high` and the property tests generate the pair from the schema again. Log calls inside `Effect.catch*` handlers moved to `Effect.tapError`/`Effect.tapCause` at the same level and message. `@systemfsoftware/stryker-ignorer-interface` re-exports the 0.150 AST, where `FormalParameterRest.decorators` is now `Array`. + +## 10.0.0 + +### Major Changes + +- The machine-mode event stream now tags every event document with `_tag` (previously `kind`), matching the `RunEvent` schema tags. The wire line schema `RunEventWireLine` is exported, so consumers can decode machine-mode stdout lines with the same schema the CLI encodes them with. The event stream's composition surface is now public: `makeRunEventStream`, `RunEventStream`, `RunEventDrain`, `RunEventDrainLive`, and `ResolvedModeInput`. + + Consumers parsing machine-mode stdout must read the `_tag` field instead of `kind`. + +### Patch Changes + +- A checker that fails now fails mutation testing with that checker's cause, instead of crashing with an empty error. The TypeScript checker type-checks each mutant, so compile errors appear in the report. Verdict counts are non-negative integers; `Metrics` is a class you can build from mutant statuses; a threshold `low` may not exceed `high`. + +## 9.0.0 + +### Major Changes + +- The incremental file now carries `incrementalVersion` (the Stryker package version). Stryker writes it on every incremental run and discards an incremental file it cannot parse or whose `incrementalVersion` differs from the running version, falling back to a full mutation testing run instead of failing. `IncrementalReportSchema` requires `incrementalVersion`, and `decodeIncrementalReport` and `IncrementalReportError` are no longer exported. + +## 8.0.0 + +### Major Changes + +- The checker request carries a plain data record, not the instrumenter's `Mutant` class. `CheckerRequest.mutants` and `CheckerService.check`/`group` now speak `CheckerMutantWire` - `id`, `fileName`, `mutatorName`, `replacement`, and `location`. A mutant that cannot be described to a checker is skipped instead of reaching it. + + With `OTEL_ENABLED=true` the CLI now exports its OpenTelemetry metrics to `OTEL_EXPORTER_OTLP_ENDPOINT`, and `OTEL_METRIC_EXPORT_INTERVAL` sets the export interval in milliseconds. Checker timings, mutant counts, and worker crashes now reach a metrics backend. + + `ConfigEnv` and `resolveExtends` are no longer exported. Import `ConfigEnv` from the config entry point of the package, and read merged options with `loadConfigCell` or `readConfig` from the package root - either performs the `extends` walk, validation and merge in order. + +## 7.0.0 + +### Major Changes + +- The built-in reporters, plugin loading and their types are no longer published + from separate entry points on `@systemfsoftware/stryker-js`; import them from + `@systemfsoftware/stryker-js`. The mutant vocabulary — `Mutant`, `Location`, + `Position` and their schemas — is importable only from + `@systemfsoftware/stryker-js-instrumenter`. + +## 6.1.0 + +### Minor Changes + +- Types that appear on the published CLI and instrumenter APIs are now exported. + +### Patch Changes + +- Installing the CLI no longer nests the HTML reporter, plugin-interface, plugin-runtime, instrumenter, or ignorer-interface packages. The CLI still includes the HTML reporter. Those packages remain installable on their own. + +- Installing the CLI no longer depends on minimatch. mutate and ignorePatterns still accept the same glob syntax. + +## 6.0.0 + +### Major Changes + +- `testRunner`, `checkers`, and `ignorers` now carry the plugin and its options together. + + - `testRunner` is `'command'`, `'vm'`, or `{ plugin, options?, nodeArgs? }`. + - `checkers` is `{ plugin, options?, nodeArgs? }[]`. Each checker is its own plugin. + - `ignorers` is plugin file URLs. Every ignorer those modules export runs. There is no separate name list. + - Put Vitest settings on `testRunner.options` (`configFile`, `dir`, `related`). There is no `vitest` block. + - Put TypeScript checker settings on that checker's `options` (`prioritizePerformanceOverAccuracy`). There is no `typescriptChecker` block. + + Before: + + ```ts + export default defineConfig({ + testRunner: 'vitest', + checkers: ['typescript'], + plugins: [ + import.meta.resolve('@systemfsoftware/stryker-js-vitest-runner'), + import.meta.resolve('@systemfsoftware/stryker-js-typescript-checker'), + import.meta.resolve('@systemfsoftware/stryker-ignorer-effect-schema-declarations'), + ], + vitest: { configFile: 'vitest.config.ts' }, + typescriptChecker: { prioritizePerformanceOverAccuracy: true }, + ignorers: ['effect-schema-declarations'], + }) + ``` + + After: + + ```ts + export default defineConfig({ + testRunner: { + plugin: import.meta.resolve('@systemfsoftware/stryker-js-vitest-runner'), + options: { configFile: 'vitest.config.ts' }, + }, + checkers: [ + { + plugin: import.meta.resolve('@systemfsoftware/stryker-js-typescript-checker'), + options: { prioritizePerformanceOverAccuracy: true }, + }, + ], + ignorers: [ + import.meta.resolve('@systemfsoftware/stryker-ignorer-effect-schema-declarations'), + ], + }) + ``` + +### Patch Changes + +- Unix worker sockets are now owner-read-write once the worker has bound them. Named pipes are unchanged. + +- Updated dependencies: + - @systemfsoftware/stryker-js-plugin-interface@6.0.0 + +## 5.0.0 + +### Major Changes + +- Plugin entries in `plugins` and `appendPlugins` are now the entrypoints + themselves, as `file:` URLs, instead of package names Stryker resolved for you. + + Stryker resolved every specifier against its own module before, so a plugin the + project had installed was only found when Node happened to walk to the right + `node_modules` from Stryker's location — which is not the project's location for + a global install, an `npx` run, or a strict isolated store. The project now + resolves each plugin itself, in the config module where the project's own + dependencies are visible: + + ```ts + import { defineConfig } from '@systemfsoftware/stryker-js/config' + + export default defineConfig({ + plugins: [import.meta.resolve('@acme/stryker-runner')], + }) + ``` + + The `--plugins` and `--appendPlugins` flags take the same resolved URLs. A value + that is not a `file:` URL is refused with the entry named. + + Stryker no longer reports an unresolved specifier as a warning it can continue + past, because it no longer resolves one: a plugin that cannot be loaded stops + the run and names the entry, and a plugin that loads but contributes nothing is + still reported as before. + +- The engine and the CLI are one package. `@systemfsoftware/stryker-js` now ships + the `stryker` binary, the run engine, and the configuration surface, replacing + `@systemfsoftware/stryker-js-cli` and `@systemfsoftware/stryker-js-engine`, which + are discontinued. + + Two subpaths are new: + + - `./config` — `defineConfig`, `mergeConfig`, `ConfigEnv`, `StrykerConfig`. Use it + instead of hand-typing `stryker.config.ts` against the option type. + - `./promises` — `run`, the same run interpreted for promise callers, so an + embedding script does not need an Effect runtime. + + Migrate by importing from `@systemfsoftware/stryker-js`. Everything the two + discontinued packages exported — the option type, `readConfig`, the plugin + loader, `WorkerLauncher`, and the exit classification — keeps its name and its + behavior at the new address. + + `@systemfsoftware/stryker-js-language` is gone; it is replaced rather than + continued. The run-event vocabulary it was carrying — `RunEvents`, `RunIdentity`, + `RunPhase`, and the `Run*` event schemas — now ships from + `@systemfsoftware/stryker-js`. Two consequences follow for anyone who imported + it: + + - The mutant model, the report schemas, and the checker, evaluator and + test-runner contracts come from `@systemfsoftware/stryker-js-instrumenter` + and `@systemfsoftware/stryker-js-plugin-interface`. Update those imports. + - The service identifiers `@systemfsoftware/stryker-js-language/RunEvents` and + `@systemfsoftware/stryker-js-language/RunIdentity` are now + `@systemfsoftware/stryker-js/RunEvents` and + `@systemfsoftware/stryker-js/RunEvents/RunIdentity`. A program that provides or + looks up either service by its identifier string must use the new one. + + Two more changes reach programmatic callers: + + - `testRunner: 'vm'` runs the test suite in memory. Stryker strips TypeScript from + the test files during preparation and evaluates them in a fresh V8 context per + mutant, so a run needs no child process and no manual bundling. + - `readConfig` and `resolveExtends` take the invocation a configuration is being + read for, so a `stryker.config.ts` may export a factory receiving + `{ command, isDryRun, mode, isCi }` and compute its settings per invocation. + Both keep working for a plain exported object; the extra argument is the + `command` and output `mode` the loader cannot see for itself. + +## 4.0.0 + +### Major Changes + +- The machine-stream event class `MutantTested` (tag `mutant`) is renamed `RunMutantTested`. + + The reporter-protocol event class keeps the name `MutantTested`; the two classes described different events under one name. Serialized stream output is unchanged — the rename is the exported binding only. If you constructed or matched the machine-stream class, import `RunMutantTested` from `@systemfsoftware/stryker-js/Run`. + +- Removed the unused `output-file` and `provided-options` entry points — neither carried vocabulary the rest of the package does not already provide. + + If you imported `output-file`, write the directory and file through your own filesystem service. If you imported the `ProvidedStrykerOptions` alias, use `StrykerOptions` from `@systemfsoftware/stryker-js/Schema` instead. + +- All subpath entry points are removed; the package exports one root specifier that enumerates every published symbol exactly once. + + `@systemfsoftware/stryker-js/Checker`, `/Schema`, `/Run`, and every other concept specifier are gone. Import the same symbols from `@systemfsoftware/stryker-js` directly. Two names changed while the vocabulary merged into one surface: + + - the machine-stream result type `MutantResult` is now `RunMutantResult` (the serialized report shape keeps the name `MutantResult`); + - the reporter barrel `./Reporter` merged into `ReporterEvent`: `ReporterFailed` now lives beside the event classes, and the unused `REPORTER_SCHEMA_VERSION` constant is deleted. + + Duplicate publications of `MutantStatus`, `Position`, `Location`, and their schema values now resolve to the report-format home, so every symbol has exactly one import path. + +### Patch Changes + +- The recommended oxlint set now carries `workflow-variant-constructed` and `runtime-construction-placement` at `error`, and the make-keyed workflow rules recognize the `Workflow.total` and `Workflow.andThen` constructors as lawful workflow construction. + +- Updated dependencies: + - @systemfsoftware/effect-cell-types@8.0.0 + +## 3.0.2 + +### Patch Changes + +- Releases the workspace so its published versions track the shared dependency graph this change moves. + +- The `@systemfsoftware/source` export condition is gone. It resolved to a package's TypeScript sources for editors and in-repo typechecks; each package now exports only its built entry. If your tsconfig sets `customConditions: ["@systemfsoftware/source"]`, or a bundler config names that condition, remove it — resolution falls back to the built entry, which is what every consumer already got. + +## 3.0.1 + +### Patch Changes + +- Updated dependencies: + - @systemfsoftware/effect-cell-types@7.0.0 + +## 3.0.0 + +### Major Changes + +- Reporter plugins are now pull-stream consumers: a reporter exports a factory + that returns an async consumer of the four run events, instead of registering + an Effect layer that provides the `Reporter` capability service. + + - Removed: `ReporterService`, `broadcastReporter`, `NamedReporter`, the + `Reporter` service class, and the legacy event payload types. + - The event protocol ships as a Standard Schema object; validate and infer it + with `import { ReporterEventSchema, type ReporterFactory, type ReporterInit } + from '@systemfsoftware/stryker-js/ReporterEvent'`. + - Event payloads are narrowed: the dry-run event no longer carries the coverage + map; the plan event carries reduced mutant descriptors instead of full run + options; `mutantTested` drops `killedBy`, `coveredBy`, `static`, + `testsCompleted` and `statusReason`, and renames `mutatorName` to `mutator`. + - `ReporterFailed.event` now names the event kind being processed instead of a + lifecycle method name; `'wrapUp'` no longer occurs. + - An unknown reporter name now fails the run at startup instead of being + silently ignored. A reporter that fails on the terminal report now fails the + run; a reporter that fails earlier is logged and detached with the exit code + unchanged. + - `@systemfsoftware/stryker-js-engine/builtin-reporters` no longer exports + `strykerPlugins`; call `makeBuiltinReporterFactories({ fileSystem, path })`. + - The built-in json and clear-text reporters write their notices directly to + stdout and stderr instead of the Effect logger, and the html reporter no + longer depends on `@effect/platform-node`. + +- Remove the --llms command manifest: the CLI no longer accepts --llms and the Run stream no longer carries a manifest terminal event. + +## 2.0.0 + +### Major Changes + +- The packages are renamed. `plugin-api` is now `@systemfsoftware/stryker-js`, the + language every plugin is written against. `mutation-run` is split: the run + itself is `@systemfsoftware/stryker-js-engine` + (host-neutral, no Node on its manifest) and the Node process entries are + `@systemfsoftware/stryker-js-cli`, which owns the worker files and the runtime + gate. `mutation-report` is now `@systemfsoftware/stryker-js-html-reporter`. + `@systemfsoftware/stryker-js-platform-node` is never published — do not install + it. Install the new names and change your imports. + + Options types moved. `StrykerOptions`, `PartialStrykerOptions` and `LogLevel` are + imported from the `Schema` export; `Mutant`, `MutantStatus`, `Position` and + `Location` from the `Mutant` export. Point a config's `extends` at the language + package's `Schema` export. + + `MutantStatus` accepts one spelling per outcome: `Killed`, `Survived`, + `NoCoverage`, `Timeout`, `CompileError`, `RuntimeError`, `Ignored` and `Pending`. + The lowercase and abbreviated forms — `killed`, `timedOut`, `noCoverage` and the + rest — are gone. A comparison against a removed spelling never matched the value + the reporter actually produced, so check any status comparison you wrote. + + Statuses, plugin kinds, exit classes and AST formats are string literal unions + rather than enums, so read them as their string values. Member access such as + `ExitClass.VerdictFail` no longer resolves. + + A plugin no longer receives a logger, and the logger port is gone. Plugins log + through Effect, and the host decides where that output goes. + + The bundled base preset is gone. A config inherits from the language package's + `Schema` export and states the thresholds, reporters and plugins it wants; you no + longer silently inherit a package manager, a plugin list or a break threshold. + +- An evaluator now answers with the verdict it reached instead of failing to + report one. + + `Evaluator.evaluate` returns `ExitClass | null` on the success channel: the + class the run should end in, or `null` for nothing to report. `ExitClass` is + exported from `@systemfsoftware/stryker-js/evaluate`. The error + channel is for the evaluator itself breaking — a report it cannot read, a + decision it cannot reach. + + Previously the only way to report a failed gate was to fail, which a caller + could not tell apart from the evaluator crashing, so neither the exit code nor + the message could distinguish them. If you wrote an evaluator that failed to + signal a verdict, return the class instead. + + A run's verdict is now the most severe class anyone reported — the score against + your `break` threshold, plus every evaluator's answer. + +- Plugins are written against Effect instead of Promise. + + `Checker`, `TestRunner`, `Reporter`, `Ignorer` and `Evaluator` are capability services whose operations return an `Effect`. Provide your plugin as a `Layer` through `declarePlugin`; the class, factory and value declarations are gone, along with the `typed-inject` dependency. Implement the operations that were optional — return `Effect.void` where there is nothing to do, and one group per mutant from `group` if you have no grouping opinion. + + Each capability fails with a tagged error. Outcomes are not failures: a killed mutant, a survivor and a compile error in the code under test are successful results. + + Read the sandbox path from `SandboxDirectory`. `commonTokens`, `tokens` and the plugin context types are removed. `determineHitLimitReached` returns an `Option`. + +### Minor Changes + +- `RENDERED_OPTION_DEFAULTS` is exported from the `core` entry point. It carries + the four option defaults that appear in human-readable help text, so a tool + printing "the default is X" reads the same value the option schema applies + instead of restating it. + +- The shared helpers package is gone. Nothing installs it any more, and the + handful of helpers worth sharing now live in the plugin contract next to the + types they serve: + + - `strykerReportBugUrl`, `normalizeFileName`, `propertyPath`, `errorToString` + and `isErrnoException` from `@systemfsoftware/stryker-js/core` + - `noopLogger` from `@systemfsoftware/stryker-js/logging` + - `testFilesProvided` from `@systemfsoftware/stryker-js/test-runner` + + If you imported any of those, change the specifier. Everything else it exported + had no consumer and is removed: use `Predicate.isNotNullish` from Effect in place + of `notEmpty`, and `RegExp.escape` in place of `escapeRegExp`. + +- Checker and test-runner workers now talk over Effect's own worker RPC, and every + call they exchange is a declared operation with a declared result. + + Before, the parent and its workers spoke a protocol written by hand: messages were + newline-delimited JSON, arguments were typed as "any JSON value", and each method + was reached by name through a proxy. Nothing checked that a payload was one the far + end could serve, so a value it could not read was refused after it arrived, and a + call whose message never landed was waited on anyway. + + The six operations that cross that boundary — a checker's `check` and `group`, a + runner's `capabilities`, `dryRun` and `mutantRun` — now each name what they take + and what they return, and the options a worker starts from are sent once when it + starts rather than as a first method call. A payload that does not fit is refused + where it is built, and a worker that cannot answer fails the call that was waiting. + + The two worker entry points are no longer importable subpaths of this package. + They were only ever spawned as processes, and the paths resolved to TypeScript + sources that could not be executed. + +### Patch Changes + +- A mutation run that is killed mid-way keeps every completed mutant: the JSONL progress stream is flushed after each result, and incremental mode writes remembered verdicts as they finish so the next run continues instead of starting over. + + Remembered killed mutants still name the tests that killed them, so a resumed run's report matches a complete run. + + The progress stream path is progressStreamFile (default reports/mutation-stream.jsonl) and can be set in config or with --progressStreamFile. + +- New version is published through npm trusted publishing, so it carries a provenance attestation you can verify. + +- Peer Effect requirement advances to 4.0.0-rc.112. No API changes. + +- An oversized message between the runner and a worker now fails the run with a + reason instead of exhausting memory. Each side of the connection reads frames + up to 16 MiB, which leaves headroom over the largest legitimate payload — a dry + run carrying per-test coverage — and a frame past the limit fails the calls + waiting on it rather than growing until the process dies. + +- Each of these packages now has a README, so its registry page says what the package is, how + to install it, and what to import or register — previously the page was blank. The lint + plugins show the configuration line that enables what they recommend. + + `@systemfsoftware/stryker-js-html-reporter` also carries its licence text + +- These packages no longer install dependencies they never imported, so installing them pulls less into your tree. + + `tslib` is gone from all six. The mutation runner additionally stops installing `lodash.groupby`, `semver` and `source-map`, and the command line interface stops installing `@effect/platform-node-shared`. Nothing exported changes. + +- Published packages no longer carry build artifacts left over from earlier builds. One package was shipping about a megabyte of bundled test-runner internals this way. + +- The closing verdict line carries its findings again. It had shrunk to the score alone, so a consumer reading the stream could no longer see the score limits the run was held to, where the report was written, or which mutants survived — the survivors were reported while the run was in flight and then absent from the summary that closes it. + + The verdict now states the thresholds, the report path, and every surviving mutant with its file, position, mutator and replacement. + +## 1.0.0 + +### Major Changes + +- The console log threshold is now scoped to the run that set it. Two runs in one + process no longer share it, so a run that lowers its own level can no longer + quieten another run happening alongside it. + + `setEngineLogLevel` is removed. The level travels with the run. + +- `runMutationTest` now requires the run-identity services to be provided by the caller; the effect names them in its requirements channel. + +- The public Run subpath's shouldKeepTempDir now accepts any failure channel instead of only S.SchemaError; the runMutationTest signature and event surface are unchanged + +### Patch Changes + +- Updated dependencies: + - @systemfsoftware/effect-cell-types@6.0.0 + +## 0.2.0 + +### Minor Changes + +- Plugin code can now ask for the module-loader service instead of touching the host module API: it exposes the same `createRequire`/`isBuiltin` surface as the host module module, and the Node engine supplies it automatically. Plugins that declare their environment through the engine's plugin declaration get the service without extra wiring; upgrades should move the engine and its plugins in the same release. + +### Patch Changes + +- Refreshed builds on the platform-services dependency graph; the packages no longer reach for host builtins directly. No CLI flags or option names change. + +## 0.1.1 + +### Patch Changes + +- Re-published against the oxc-based instrumenter: workspace dependency ranges move to the new instrumenter major; no package's own behavior changes in this release. + +## 0.1.0 + +### Major Changes + +- Three packages are renamed. `plugin-api` is now `@systemfsoftware/stryker-js`, the + language every plugin is written against. `mutation-run` is now + `@systemfsoftware/stryker-js-platform-node`, the Node host that runs a mutation + test. `mutation-report` is now `@systemfsoftware/stryker-js-html-reporter`. + Install the new names and change your imports. + + Options types moved. `StrykerOptions`, `PartialStrykerOptions` and `LogLevel` are + imported from the `Schema` export; `Mutant`, `MutantStatus`, `Position` and + `Location` from the `Mutant` export. Point a config's `extends` at the language + package's `Schema` export. + + `MutantStatus` accepts one spelling per outcome: `Killed`, `Survived`, + `NoCoverage`, `Timeout`, `CompileError`, `RuntimeError`, `Ignored` and `Pending`. + The lowercase and abbreviated forms — `killed`, `timedOut`, `noCoverage` and the + rest — are gone. A comparison against a removed spelling never matched the value + the reporter actually produced, so check any status comparison you wrote. + + Statuses, plugin kinds, exit classes and AST formats are string literal unions + rather than enums, so read them as their string values. Member access such as + `ExitClass.VerdictFail` no longer resolves. + + A plugin no longer receives a logger, and the logger port is gone. Plugins log + through Effect, and the host decides where that output goes. + + The bundled base preset is gone. A config inherits from the language package's + `Schema` export and states the thresholds, reporters and plugins it wants; you no + longer silently inherit a package manager, a plugin list or a break threshold. + +- An evaluator now answers with the verdict it reached instead of failing to + report one. + + `Evaluator.evaluate` returns `ExitClass | null` on the success channel: the + class the run should end in, or `null` for nothing to report. `ExitClass` is + exported from `@systemfsoftware/stryker-js/evaluate`. The error + channel is for the evaluator itself breaking — a report it cannot read, a + decision it cannot reach. + + Previously the only way to report a failed gate was to fail, which a caller + could not tell apart from the evaluator crashing, so neither the exit code nor + the message could distinguish them. If you wrote an evaluator that failed to + signal a verdict, return the class instead. + + A run's verdict is now the most severe class anyone reported — the score against + your `break` threshold, plus every evaluator's answer. + +- Plugins are written against Effect instead of Promise. + + `Checker`, `TestRunner`, `Reporter`, `Ignorer` and `Evaluator` are capability services whose operations return an `Effect`. Provide your plugin as a `Layer` through `declarePlugin`; the class, factory and value declarations are gone, along with the `typed-inject` dependency. Implement the operations that were optional — return `Effect.void` where there is nothing to do, and one group per mutant from `group` if you have no grouping opinion. + + Each capability fails with a tagged error. Outcomes are not failures: a killed mutant, a survivor and a compile error in the code under test are successful results. + + Read the sandbox path from `SandboxDirectory`. `commonTokens`, `tokens` and the plugin context types are removed. `determineHitLimitReached` returns an `Option`. + +### Minor Changes + +- `RENDERED_OPTION_DEFAULTS` is exported from the `core` entry point. It carries + the four option defaults that appear in human-readable help text, so a tool + printing "the default is X" reads the same value the option schema applies + instead of restating it. + +- The shared helpers package is gone. Nothing installs it any more, and the + handful of helpers worth sharing now live in the plugin contract next to the + types they serve: + + - `strykerReportBugUrl`, `normalizeFileName`, `propertyPath`, `errorToString` + and `isErrnoException` from `@systemfsoftware/stryker-js/core` + - `noopLogger` from `@systemfsoftware/stryker-js/logging` + - `testFilesProvided` from `@systemfsoftware/stryker-js/test-runner` + + If you imported any of those, change the specifier. Everything else it exported + had no consumer and is removed: use `Predicate.isNotNullish` from Effect in place + of `notEmpty`, and `RegExp.escape` in place of `escapeRegExp`. + +- Checker and test-runner workers now talk over Effect's own worker RPC, and every + call they exchange is a declared operation with a declared result. + + Before, the parent and its workers spoke a protocol written by hand: messages were + newline-delimited JSON, arguments were typed as "any JSON value", and each method + was reached by name through a proxy. Nothing checked that a payload was one the far + end could serve, so a value it could not read was refused after it arrived, and a + call whose message never landed was waited on anyway. + + The six operations that cross that boundary — a checker's `check` and `group`, a + runner's `capabilities`, `dryRun` and `mutantRun` — now each name what they take + and what they return, and the options a worker starts from are sent once when it + starts rather than as a first method call. A payload that does not fit is refused + where it is built, and a worker that cannot answer fails the call that was waiting. + + The two worker entry points are no longer importable subpaths of this package. + They were only ever spawned as processes, and the paths resolved to TypeScript + sources that could not be executed. + +### Patch Changes + +- New version is published through npm trusted publishing, so it carries a provenance attestation you can verify. + +- An oversized message between the runner and a worker now fails the run with a + reason instead of exhausting memory. Each side of the connection reads frames + up to 16 MiB, which leaves headroom over the largest legitimate payload — a dry + run carrying per-test coverage — and a frame past the limit fails the calls + waiting on it rather than growing until the process dies. + +- Each of these packages now has a README, so its registry page says what the package is, how + to install it, and what to import or register — previously the page was blank. The lint + plugins show the configuration line that enables what they recommend. + + `@systemfsoftware/stryker-js-html-reporter` also carries its licence text + +- These packages no longer install dependencies they never imported, so installing them pulls less into your tree. + + `tslib` is gone from all six. The mutation runner additionally stops installing `lodash.groupby`, `semver` and `source-map`, and the command line interface stops installing `@effect/platform-node-shared`. Nothing exported changes. + +- Published packages no longer carry build artifacts left over from earlier builds. One package was shipping about a megabyte of bundled test-runner internals this way. + +- The closing verdict line carries its findings again. It had shrunk to the score alone, so a consumer reading the stream could no longer see the score limits the run was held to, where the report was written, or which mutants survived — the survivors were reported while the run was in flight and then absent from the summary that closes it. + + The verdict now states the thresholds, the report path, and every surviving mutant with its file, position, mutator and replacement. + +- Updated dependencies: + - @systemfsoftware/effect-cell-types@5.0.0 diff --git a/packages/toolchain/oxlint-ignorer-config/CHANGELOG.md b/packages/toolchain/oxlint-ignorer-config/CHANGELOG.md new file mode 100644 index 000000000..f6100f389 --- /dev/null +++ b/packages/toolchain/oxlint-ignorer-config/CHANGELOG.md @@ -0,0 +1,25 @@ +# @systemfsoftware/oxlint-ignorer-config + +## 0.1.1 + +### Patch Changes + +- Effect moves to `4.0.0-rc.116` (with `@effect/platform-node`, `@effect/platform-node-shared`, `@effect/vitest` and `@effect/opentelemetry` on the same release), the `@systemfsoftware/*` toolchain pins move to their current releases, and `vitest` 5 with `oxc-parser` 0.150 come along. Both worker bundles (`stryker-js-typescript-checker`, `stryker-js-vitest-runner`) now inline their whole runtime rather than resolving `effect`, `@effect/*` and the sibling packages from the host's tree, so a worker runs on its own copy of the effect that built it; the project's `typescript` and `vitest` remain the only imports beside Node builtins. The filter-level `arbitrary.candidate` generators that the thresholds schemas carried are gone: v4 no longer reads them, so thresholds still reject a `low` above `high` and the property tests generate the pair from the schema again. Log calls inside `Effect.catch*` handlers moved to `Effect.tapError`/`Effect.tapCause` at the same level and message. `@systemfsoftware/stryker-ignorer-interface` re-exports the 0.150 AST, where `FormalParameterRest.decorators` is now `Array`. + +## 0.1.0 + +### Minor Changes + +- The preset now denies the `process` global. Ignorer packages already carry no process uses, so no migration is needed. + +- First release. This is the preset an ignorer package is graded by: it holds a + decision to one path, refuses type assertions and `any` on data read from + outside, and rejects any import that would put the StrykerJS runtime behind an + ignorer — Effect and the family presets included. + + It extends nothing, so adopting it is a single `extends: [preset]` entry in + your lint configuration. + +### Patch Changes + +- Maintenance release. Every published entry point, export, option, and behaviour is exactly as it was. diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index d69e304ec..8e5951507 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -92,3 +92,6 @@ packages: - packages/frameworks/* patchedDependencies: "@systemfsoftware/differential-spec@1.0.1": patches/@systemfsoftware__differential-spec@1.0.1.patch +versioning: + changelog: + storage: repository diff --git a/scripts/guards/check-changelog-sections.ts b/scripts/guards/check-changelog-sections.ts new file mode 100755 index 000000000..d47204597 --- /dev/null +++ b/scripts/guards/check-changelog-sections.ts @@ -0,0 +1,161 @@ +#!/usr/bin/env -S deno run --allow-read +import { expandGlob } from '@std/fs/expand-glob' +import { parse } from '@std/yaml' + +const WORKSPACE_MANIFEST = 'pnpm-workspace.yaml' +const PARKED_DIRECTORY = '.changeset/changelogs' + +interface Workspace { + readonly patterns: ReadonlyArray + readonly storage: unknown +} + +const workspaceOf = (text: string): Workspace => { + const document = parse(text) as { + packages?: ReadonlyArray + versioning?: { changelog?: { storage?: unknown } } + } | null + return { + patterns: document?.packages ?? [], + storage: document?.versioning?.changelog?.storage, + } +} + +const escape = (text: string): string => text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + +export const sectionViolations = ( + name: string, + version: string, + changelog: string | null, +): ReadonlyArray => { + if (changelog === null) return [`${name}@${version}: no CHANGELOG.md`] + const headings = changelog.match(new RegExp(`^## ${escape(version)}[ \\t]*$`, 'gm'))?.length ?? 0 + return headings === 1 + ? [] + : [`${name}@${version}: CHANGELOG.md has ${headings} "## ${version}" sections, expected exactly one`] +} + +export const parkedViolations = (storage: unknown, parked: ReadonlyArray): ReadonlyArray => + storage === 'repository' + ? parked.map((file) => + `${PARKED_DIRECTORY}/${file}: a parked changelog section under storage: repository is never published; fold it into the package's CHANGELOG.md` + ) + : [] + +const selftest = (): number => { + const cases: readonly { readonly name: string; readonly run: () => void }[] = [ + { + name: 'accepts a changelog with exactly one section for the version', + run: () => { + const found = sectionViolations('@x/a', '1.2.0', '# @x/a\n\n## 1.2.0\n\n- b\n\n## 1.1.0\n\n- a\n') + if (found.length !== 0) throw new Error(JSON.stringify(found)) + }, + }, + { + name: 'refuses a changelog with no section for the version', + run: () => { + const found = sectionViolations('@x/a', '1.2.0', '# @x/a\n\n## 1.1.0\n\n- a\n') + if (found.length !== 1 || !found[0]?.includes('0 ')) throw new Error(JSON.stringify(found)) + }, + }, + { + name: 'refuses a changelog with two sections for the version', + run: () => { + const found = sectionViolations('@x/a', '1.2.0', '## 1.2.0\n\n- b\n\n## 1.2.0\n\n- a\n') + if (found.length !== 1 || !found[0]?.includes('2 ')) throw new Error(JSON.stringify(found)) + }, + }, + { + name: 'does not count a prerelease or longer version as the section', + run: () => { + const found = sectionViolations('@x/a', '1.2.0', '## 1.2.0-rc.1\n\n## 11.2.0\n\n## 1.2.00\n') + if (found.length !== 1) throw new Error(JSON.stringify(found)) + }, + }, + { + name: 'refuses a missing changelog', + run: () => { + const found = sectionViolations('@x/a', '1.2.0', null) + if (found.length !== 1) throw new Error(JSON.stringify(found)) + }, + }, + { + name: 'refuses a parked section under repository storage', + run: () => { + const found = parkedViolations('repository', ['@x!a@1.2.0.md']) + if (found.length !== 1) throw new Error(JSON.stringify(found)) + }, + }, + { + name: 'allows parked sections under registry storage', + run: () => { + const found = parkedViolations('registry', ['@x!a@1.2.0.md']) + if (found.length !== 0) throw new Error(JSON.stringify(found)) + }, + }, + ] + + let failures = 0 + for (const test of cases) { + try { + test.run() + console.log(` ✓ ${test.name}`) + } catch (error) { + console.error(` ✗ ${test.name}: ${error instanceof Error ? error.message : String(error)}`) + failures++ + } + } + + if (failures > 0) { + console.error(`check-changelog-sections: selftest FAILED (${failures}/${cases.length})`) + return 1 + } + console.log(`check-changelog-sections: selftest ok (${cases.length} tests)`) + return 0 +} + +const readOrNull = (path: string): Promise => Deno.readTextFile(path).catch(() => null) + +const main = async (): Promise => { + if (Deno.args.includes('--selftest')) return selftest() + + const workspace = workspaceOf(await Deno.readTextFile(WORKSPACE_MANIFEST)) + const violations: string[] = [] + let checked = 0 + for (const pattern of workspace.patterns) { + for await (const entry of expandGlob(`${pattern}/package.json`, { exclude: ['**/node_modules/**'] })) { + const manifest = JSON.parse(await Deno.readTextFile(entry.path)) as { + name?: string + version?: string + private?: boolean + } + if (manifest.private === true || manifest.name === undefined || manifest.version === undefined) continue + checked++ + const changelog = await readOrNull(entry.path.replace(/package\.json$/, 'CHANGELOG.md')) + violations.push(...sectionViolations(manifest.name, manifest.version, changelog)) + } + } + + const parked: string[] = [] + for await (const entry of expandGlob(`${PARKED_DIRECTORY}/*.md`)) parked.push(entry.name) + violations.push(...parkedViolations(workspace.storage, parked)) + + if (violations.length > 0) { + for (const violation of violations) console.error(`error[CHANGELOG-SECTIONS]: ${violation}`) + console.error(`check-changelog-sections: ${violations.length} violation(s)`) + return 1 + } + console.log( + `check-changelog-sections: ${checked} publishable package(s) each carry exactly one section for their version, and no section is parked`, + ) + return 0 +} + +if (import.meta.main) { + try { + Deno.exit(await main()) + } catch (error) { + console.error(`check-changelog-sections: error: ${error instanceof Error ? error.message : String(error)}`) + Deno.exit(1) + } +} From b740f39a4eae4b1f305ae2790d964d57c6a57e75 Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Tue, 6 Oct 2026 17:16:50 +0000 Subject: [PATCH 03/11] build(release): ship each package's changelog in its tarball With changelog storage in the repository, pnpm packs a package's CHANGELOG.md only when its files list names it, so 16.0.1 would have shipped without the changelog 16.0.0 carried. Every publishable package now lists CHANGELOG.md; nothing else in those manifests changes --- packages/frameworks/angular/package.json | 3 ++- packages/frameworks/interface/package.json | 3 ++- packages/frameworks/svelte/package.json | 3 ++- packages/ignorers/angular/package.json | 3 ++- packages/ignorers/effect-schema-declarations/package.json | 3 ++- packages/ignorers/in-source-vitest-block/package.json | 3 ++- packages/ignorers/interface/package.json | 3 ++- packages/ignorers/kit/package.json | 3 ++- packages/stryker-js-cli-contract/package.json | 3 ++- packages/stryker-js-html-reporter/package.json | 3 ++- packages/stryker-js-instrumenter/package.json | 3 ++- packages/stryker-js-plugin-interface/package.json | 3 ++- packages/stryker-js-plugin-runtime/package.json | 3 ++- packages/stryker-js-typescript-checker/package.json | 3 ++- packages/stryker-js-vitest-runner/package.json | 3 ++- packages/stryker-js/package.json | 3 ++- packages/toolchain/oxlint-ignorer-config/package.json | 3 ++- 17 files changed, 34 insertions(+), 17 deletions(-) diff --git a/packages/frameworks/angular/package.json b/packages/frameworks/angular/package.json index 18318d87a..e38e273be 100644 --- a/packages/frameworks/angular/package.json +++ b/packages/frameworks/angular/package.json @@ -32,7 +32,8 @@ ] }, "files": [ - "dist" + "dist", + "CHANGELOG.md" ], "exports": { ".": { diff --git a/packages/frameworks/interface/package.json b/packages/frameworks/interface/package.json index 9b5cbd910..851375847 100644 --- a/packages/frameworks/interface/package.json +++ b/packages/frameworks/interface/package.json @@ -23,7 +23,8 @@ ], "type": "module", "files": [ - "dist" + "dist", + "CHANGELOG.md" ], "exports": { ".": { diff --git a/packages/frameworks/svelte/package.json b/packages/frameworks/svelte/package.json index a95e3c8e8..6c9791353 100644 --- a/packages/frameworks/svelte/package.json +++ b/packages/frameworks/svelte/package.json @@ -23,7 +23,8 @@ ], "type": "module", "files": [ - "dist" + "dist", + "CHANGELOG.md" ], "exports": { ".": { diff --git a/packages/ignorers/angular/package.json b/packages/ignorers/angular/package.json index 9318bc448..d62221b8a 100644 --- a/packages/ignorers/angular/package.json +++ b/packages/ignorers/angular/package.json @@ -22,7 +22,8 @@ ], "type": "module", "files": [ - "dist" + "dist", + "CHANGELOG.md" ], "exports": { ".": { diff --git a/packages/ignorers/effect-schema-declarations/package.json b/packages/ignorers/effect-schema-declarations/package.json index 3828ced10..fb734dcd2 100644 --- a/packages/ignorers/effect-schema-declarations/package.json +++ b/packages/ignorers/effect-schema-declarations/package.json @@ -22,7 +22,8 @@ ], "type": "module", "files": [ - "dist" + "dist", + "CHANGELOG.md" ], "exports": { ".": { diff --git a/packages/ignorers/in-source-vitest-block/package.json b/packages/ignorers/in-source-vitest-block/package.json index 4285fd778..10193a04f 100644 --- a/packages/ignorers/in-source-vitest-block/package.json +++ b/packages/ignorers/in-source-vitest-block/package.json @@ -21,7 +21,8 @@ ], "type": "module", "files": [ - "dist" + "dist", + "CHANGELOG.md" ], "exports": { ".": { diff --git a/packages/ignorers/interface/package.json b/packages/ignorers/interface/package.json index 3065bedb7..295860207 100644 --- a/packages/ignorers/interface/package.json +++ b/packages/ignorers/interface/package.json @@ -22,7 +22,8 @@ ], "type": "module", "files": [ - "dist" + "dist", + "CHANGELOG.md" ], "exports": { ".": { diff --git a/packages/ignorers/kit/package.json b/packages/ignorers/kit/package.json index 43a7e90b1..2aa5b3838 100644 --- a/packages/ignorers/kit/package.json +++ b/packages/ignorers/kit/package.json @@ -22,7 +22,8 @@ ], "type": "module", "files": [ - "dist" + "dist", + "CHANGELOG.md" ], "exports": { ".": { diff --git a/packages/stryker-js-cli-contract/package.json b/packages/stryker-js-cli-contract/package.json index b0ec8c515..5b06c1f21 100644 --- a/packages/stryker-js-cli-contract/package.json +++ b/packages/stryker-js-cli-contract/package.json @@ -12,7 +12,8 @@ "type": "module", "files": [ "dist", - "contract" + "contract", + "CHANGELOG.md" ], "scripts": { "clean": "rimraf dist", diff --git a/packages/stryker-js-html-reporter/package.json b/packages/stryker-js-html-reporter/package.json index 92c079d8c..b69b5963a 100644 --- a/packages/stryker-js-html-reporter/package.json +++ b/packages/stryker-js-html-reporter/package.json @@ -11,7 +11,8 @@ "description": "HTML reporter plugin for the @systemfsoftware mutation engine.", "type": "module", "files": [ - "dist" + "dist", + "CHANGELOG.md" ], "scripts": { "clean": "rimraf dist", diff --git a/packages/stryker-js-instrumenter/package.json b/packages/stryker-js-instrumenter/package.json index fd86ba217..a3ef5e50b 100644 --- a/packages/stryker-js-instrumenter/package.json +++ b/packages/stryker-js-instrumenter/package.json @@ -11,7 +11,8 @@ "description": "The code instrumenter used by the Stryker mutation runner", "type": "module", "files": [ - "dist" + "dist", + "CHANGELOG.md" ], "exports": { ".": { diff --git a/packages/stryker-js-plugin-interface/package.json b/packages/stryker-js-plugin-interface/package.json index 9c86f3c2d..b6e0f5571 100644 --- a/packages/stryker-js-plugin-interface/package.json +++ b/packages/stryker-js-plugin-interface/package.json @@ -12,7 +12,8 @@ "type": "module", "files": [ "dist", - "contract" + "contract", + "CHANGELOG.md" ], "scripts": { "clean": "rimraf dist", diff --git a/packages/stryker-js-plugin-runtime/package.json b/packages/stryker-js-plugin-runtime/package.json index 9da0fa381..8bce8e9f0 100644 --- a/packages/stryker-js-plugin-runtime/package.json +++ b/packages/stryker-js-plugin-runtime/package.json @@ -11,7 +11,8 @@ "description": "The mutation-testing plugin runtime — the worker's RPC server layer, its OTel bootstrap, the worker-options wire codec, and the trace-context middleware implementations a plugin process runs.", "type": "module", "files": [ - "dist" + "dist", + "CHANGELOG.md" ], "scripts": { "clean": "rimraf dist", diff --git a/packages/stryker-js-typescript-checker/package.json b/packages/stryker-js-typescript-checker/package.json index 4603394cf..cec44bae1 100644 --- a/packages/stryker-js-typescript-checker/package.json +++ b/packages/stryker-js-typescript-checker/package.json @@ -12,7 +12,8 @@ "type": "module", "files": [ "dist", - "schema" + "schema", + "CHANGELOG.md" ], "exports": { ".": { diff --git a/packages/stryker-js-vitest-runner/package.json b/packages/stryker-js-vitest-runner/package.json index 087920082..950a99c82 100644 --- a/packages/stryker-js-vitest-runner/package.json +++ b/packages/stryker-js-vitest-runner/package.json @@ -11,7 +11,8 @@ "description": "Vitest test-runner plugin for Stryker", "type": "module", "files": [ - "dist" + "dist", + "CHANGELOG.md" ], "exports": { ".": { diff --git a/packages/stryker-js/package.json b/packages/stryker-js/package.json index ea6fcfae3..3b6522af8 100644 --- a/packages/stryker-js/package.json +++ b/packages/stryker-js/package.json @@ -13,7 +13,8 @@ "description": "The Stryker mutation testing tool — the stryker executable, the run engine, and the configuration surface.", "type": "module", "files": [ - "dist" + "dist", + "CHANGELOG.md" ], "bin": { "stryker": "./dist/main.mjs" diff --git a/packages/toolchain/oxlint-ignorer-config/package.json b/packages/toolchain/oxlint-ignorer-config/package.json index 7d2d04dc5..8635e15b7 100644 --- a/packages/toolchain/oxlint-ignorer-config/package.json +++ b/packages/toolchain/oxlint-ignorer-config/package.json @@ -13,7 +13,8 @@ "type": "module", "module": "./lib/base.js", "files": [ - "lib" + "lib", + "CHANGELOG.md" ], "exports": { "./package.json": "./package.json", From ca5411d7dec52870ade4f286f43f7ad184c61da2 Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Tue, 6 Oct 2026 18:45:19 +0000 Subject: [PATCH 04/11] chore(release): record the changelog now shipped by stryker-js-cli-contract --- .changeset/tangy-parents-tap.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/tangy-parents-tap.md diff --git a/.changeset/tangy-parents-tap.md b/.changeset/tangy-parents-tap.md new file mode 100644 index 000000000..52af64a50 --- /dev/null +++ b/.changeset/tangy-parents-tap.md @@ -0,0 +1,5 @@ +--- +"@systemfsoftware/stryker-js-cli-contract": patch +--- + +The published package now includes its changelog. From 814bdc5f7d02b281a3a886e23fffa72ee1bdb336 Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Wed, 7 Oct 2026 00:11:11 +0000 Subject: [PATCH 05/11] build(release): refuse an unknown changelog storage in the changelog guard check-changelog-sections lists the publishable packages through pnpm ls -r and reads versioning.changelog.storage from pnpm-workspace.yaml. A missing or misspelt storage now fails the guard with ChangelogStorageInvalid instead of switching the parked-section check off; the selftest covers both. scripts/deno.json maps @std/yaml and @std/fs/expand-glob again, which the guard has always imported and #209 trimmed from the map --- scripts/deno.json | 4 +- scripts/deno.lock | 16 ++- scripts/guards/check-changelog-sections.ts | 107 +++++++++++++++------ 3 files changed, 95 insertions(+), 32 deletions(-) diff --git a/scripts/deno.json b/scripts/deno.json index e69875cf9..90e3c30fe 100644 --- a/scripts/deno.json +++ b/scripts/deno.json @@ -12,6 +12,8 @@ "@std/encoding/base64": "jsr:@std/encoding@1/base64", "@std/encoding/hex": "jsr:@std/encoding@1/hex", "@std/fs/ensure-dir": "jsr:@std/fs@1/ensure-dir", - "@std/path": "jsr:@std/path@1" + "@std/fs/expand-glob": "jsr:@std/fs@1/expand-glob", + "@std/path": "jsr:@std/path@1", + "@std/yaml": "jsr:@std/yaml@1" } } diff --git a/scripts/deno.lock b/scripts/deno.lock index 8050f91a1..6aca710fe 100644 --- a/scripts/deno.lock +++ b/scripts/deno.lock @@ -7,7 +7,9 @@ "jsr:@std/fs@1": "1.0.24", "jsr:@std/internal@^1.0.12": "1.0.14", "jsr:@std/internal@^1.0.14": "1.0.14", - "jsr:@std/path@1": "1.1.6" + "jsr:@std/path@1": "1.1.6", + "jsr:@std/path@^1.1.5": "1.1.6", + "jsr:@std/yaml@1": "1.3.0" }, "jsr": { "@std/assert@1.0.19": { @@ -23,7 +25,11 @@ "integrity": "e7cef2f0b3153bccc17431e7c864a1de03a4b6d9647389c43e08aaa775d37385" }, "@std/fs@1.0.24": { - "integrity": "f3061b45b81673a2bece689da041df32d174be064c89eb6397fb5718d3fb7877" + "integrity": "f3061b45b81673a2bece689da041df32d174be064c89eb6397fb5718d3fb7877", + "dependencies": [ + "jsr:@std/internal@^1.0.14", + "jsr:@std/path@^1.1.5" + ] }, "@std/internal@1.0.14": { "integrity": "291516b3d4c35024d6ffbc0a9df5bf4c64116e05b50012cf846710152d2ffdf7" @@ -33,6 +39,9 @@ "dependencies": [ "jsr:@std/internal@^1.0.14" ] + }, + "@std/yaml@1.3.0": { + "integrity": "b250103854378b8b36cbff0fe95aec017d3d9ce86b489c67605180c602fb4922" } }, "workspace": { @@ -41,7 +50,8 @@ "jsr:@std/cli@1", "jsr:@std/encoding@1", "jsr:@std/fs@1", - "jsr:@std/path@1" + "jsr:@std/path@1", + "jsr:@std/yaml@1" ] } } diff --git a/scripts/guards/check-changelog-sections.ts b/scripts/guards/check-changelog-sections.ts index d47204597..03ef2dada 100755 --- a/scripts/guards/check-changelog-sections.ts +++ b/scripts/guards/check-changelog-sections.ts @@ -1,26 +1,53 @@ -#!/usr/bin/env -S deno run --allow-read +#!/usr/bin/env -S deno run --allow-read --allow-run=pnpm import { expandGlob } from '@std/fs/expand-glob' +import { join } from '@std/path' import { parse } from '@std/yaml' +import { run } from '../lib/run.ts' const WORKSPACE_MANIFEST = 'pnpm-workspace.yaml' const PARKED_DIRECTORY = '.changeset/changelogs' -interface Workspace { - readonly patterns: ReadonlyArray - readonly storage: unknown +interface Member { + readonly name: string + readonly version: string + readonly dir: string } -const workspaceOf = (text: string): Workspace => { - const document = parse(text) as { - packages?: ReadonlyArray - versioning?: { changelog?: { storage?: unknown } } - } | null - return { - patterns: document?.packages ?? [], - storage: document?.versioning?.changelog?.storage, +const publicMembers = async (): Promise => { + const raw = JSON.parse(await run('pnpm', ['ls', '-r', '--depth=-1', '--json'])) as readonly { + name?: unknown + version?: unknown + path?: unknown + private?: unknown + }[] + return raw.flatMap((entry) => + entry.private !== true && typeof entry.name === 'string' && typeof entry.version === 'string' && + typeof entry.path === 'string' + ? [{ name: entry.name, version: entry.version, dir: entry.path }] + : [] + ) +} + +type ChangelogStorage = 'repository' | 'registry' + +export class ChangelogStorageInvalid extends Error { + override readonly name = 'ChangelogStorageInvalid' + constructor(readonly found: unknown) { + super( + `pnpm-workspace.yaml: versioning.changelog.storage is ${ + found === undefined ? 'missing' : JSON.stringify(found) + }; expected "repository" or "registry"`, + ) } } +export const changelogStorageOf = (workspaceYaml: string): ChangelogStorage => { + const document = parse(workspaceYaml) as { versioning?: { changelog?: { storage?: unknown } } } | null + const found = document?.versioning?.changelog?.storage + if (found === 'repository' || found === 'registry') return found + throw new ChangelogStorageInvalid(found) +} + const escape = (text: string): string => text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') export const sectionViolations = ( @@ -35,7 +62,7 @@ export const sectionViolations = ( : [`${name}@${version}: CHANGELOG.md has ${headings} "## ${version}" sections, expected exactly one`] } -export const parkedViolations = (storage: unknown, parked: ReadonlyArray): ReadonlyArray => +export const parkedViolations = (storage: ChangelogStorage, parked: ReadonlyArray): ReadonlyArray => storage === 'repository' ? parked.map((file) => `${PARKED_DIRECTORY}/${file}: a parked changelog section under storage: repository is never published; fold it into the package's CHANGELOG.md` @@ -93,6 +120,37 @@ const selftest = (): number => { if (found.length !== 0) throw new Error(JSON.stringify(found)) }, }, + { + name: 'refuses a workspace with no changelog storage', + run: () => { + try { + changelogStorageOf('packages:\n - packages/*\n') + } catch (error) { + if (error instanceof ChangelogStorageInvalid && error.found === undefined) return + throw error + } + throw new Error('accepted a missing storage') + }, + }, + { + name: 'refuses a misspelt changelog storage', + run: () => { + try { + changelogStorageOf('versioning:\n changelog:\n storage: repostiory\n') + } catch (error) { + if (error instanceof ChangelogStorageInvalid && error.found === 'repostiory') return + throw error + } + throw new Error('accepted a misspelt storage') + }, + }, + { + name: 'reads repository storage', + run: () => { + const found = changelogStorageOf('versioning:\n changelog:\n storage: repository\n') + if (found !== 'repository') throw new Error(found) + }, + }, ] let failures = 0 @@ -119,26 +177,19 @@ const readOrNull = (path: string): Promise => Deno.readTextFile(p const main = async (): Promise => { if (Deno.args.includes('--selftest')) return selftest() - const workspace = workspaceOf(await Deno.readTextFile(WORKSPACE_MANIFEST)) + const storage = changelogStorageOf(await Deno.readTextFile(WORKSPACE_MANIFEST)) + const members = await publicMembers() + if (members.length === 0) throw new Error('pnpm ls -r found no publishable workspace package') const violations: string[] = [] - let checked = 0 - for (const pattern of workspace.patterns) { - for await (const entry of expandGlob(`${pattern}/package.json`, { exclude: ['**/node_modules/**'] })) { - const manifest = JSON.parse(await Deno.readTextFile(entry.path)) as { - name?: string - version?: string - private?: boolean - } - if (manifest.private === true || manifest.name === undefined || manifest.version === undefined) continue - checked++ - const changelog = await readOrNull(entry.path.replace(/package\.json$/, 'CHANGELOG.md')) - violations.push(...sectionViolations(manifest.name, manifest.version, changelog)) - } + for (const member of members) { + const changelog = await readOrNull(join(member.dir, 'CHANGELOG.md')) + violations.push(...sectionViolations(member.name, member.version, changelog)) } + const checked = members.length const parked: string[] = [] for await (const entry of expandGlob(`${PARKED_DIRECTORY}/*.md`)) parked.push(entry.name) - violations.push(...parkedViolations(workspace.storage, parked)) + violations.push(...parkedViolations(storage, parked)) if (violations.length > 0) { for (const violation of violations) console.error(`error[CHANGELOG-SECTIONS]: ${violation}`) From 49105a1be2319fa17d24de26af5819f9fa315751 Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Wed, 7 Oct 2026 00:11:14 +0000 Subject: [PATCH 06/11] docs(release): describe repository changelog storage in the changeset README Under storage: repository pnpm writes each release's section into the package's CHANGELOG.md and parks nothing under .changeset/changelogs/. The .changeset README and the ledger note now describe that cycle, and the note about synthesising parked changelogs is deleted with the fallback it described --- .changeset/README.md | 30 +++++++--------- .../pnpm-owns-the-changeset-ledger.md | 6 ++-- ...ry-changelogs-persistence-and-synthesis.md | 35 ------------------- 3 files changed, 15 insertions(+), 56 deletions(-) delete mode 100644 docs/solutions/tooling-decisions/pnpm-registry-changelogs-persistence-and-synthesis.md diff --git a/.changeset/README.md b/.changeset/README.md index 23b57d480..704c41917 100644 --- a/.changeset/README.md +++ b/.changeset/README.md @@ -19,26 +19,22 @@ pnpm change --bump --summary "" [ - This README is NOT a changeset: the gate requires a file whose frontmatter parses as `"": `. -## Two-stage intent deletion +## Changelogs -pnpm unlinks consumed intents only after the npm registry confirms the -versions those intents produced. The cycle is: +`pnpm-workspace.yaml` sets `versioning.changelog.storage: repository`, so +`pnpm version -r` writes each release's `## ` section into the +package's committed `CHANGELOG.md`, and that file ships in the package's +tarball. Nothing is parked under `.changeset/changelogs/`. -1. **Version PR.** `pnpm version -r` consumes pending intents, writes - `.changeset/changelogs/@.md`, and records stems in - `ledger.yaml`. The new versions are not on npm yet, so - `confirmPublished()` fails and the intent `.md` files stay on disk. -2. **Publish.** The version PR merges; CI publishes and pushes git tags. -3. **Next version PR.** The next `pnpm version -r` scans - `.changeset/changelogs/`, `verifyPublished()` / `confirmPublished()` - against npm, deletes confirmed changelog files, and unlinks the - intent `.md` files whose releases are all confirmed. +1. **Version PR.** `pnpm version -r` consumes pending intents, writes the + `## ` sections, and records the consumed stems in `ledger.yaml`. +2. **Publish.** The version PR merges; CI publishes, pushes git tags, and + creates each GitHub Release from that version's `CHANGELOG.md` section. -If `.changeset/changelogs/` is deleted out of band before that -confirmation, the matching intent files become permanent orphans: pnpm -has nothing left to verify, so it never unlinks them. Remove those -stems by hand only after the ledger already records them and npm -already serves the versions. +`scripts/guards/check-changelog-sections.ts` holds every publishable +package to exactly one section for its current version, refuses any file +under `.changeset/changelogs/`, and fails when the storage setting is +missing or misspelt. ## Interruption safety diff --git a/docs/solutions/tooling-decisions/pnpm-owns-the-changeset-ledger.md b/docs/solutions/tooling-decisions/pnpm-owns-the-changeset-ledger.md index 5d33ee7ad..09e32d051 100644 --- a/docs/solutions/tooling-decisions/pnpm-owns-the-changeset-ledger.md +++ b/docs/solutions/tooling-decisions/pnpm-owns-the-changeset-ledger.md @@ -21,8 +21,6 @@ The release planner derives a phase from two numbers: the size of the release se The trap is the second number. `pnpm version -r` consumes an intent into the ledger on the version PR, but it unlinks the intent file only on a later `pnpm version -r`, after `confirmPublished()` sees those versions on npm. Counting `.changeset/*.md` therefore answers "how many intent files exist", never "how many are pending". Read that way, every release leaves the pipeline in the version phase forever, and each push to the default branch opens a `version-packages` pull request that deletes files the previous run already consumed. -If `.changeset/changelogs/` is removed before that confirmation, pnpm never unlinks the matching intents. Those files are orphans, not pending work. - ## Guidance Treat `.changeset/ledger.yaml` as the record of consumption, and count only intents whose stem is absent from it (`countPendingIntents`). Two properties of the ledger decide whether that count is right: @@ -30,7 +28,7 @@ Treat `.changeset/ledger.yaml` as the record of consumption, and count only inte - **pnpm writes it.** `pnpm change` authors the intent; `pnpm version -r` consumes it and renders the ledger (`render_ledger` / `readLedger` in pnpm). No production code in this repository writes the file — the parser's own tests write throwaway copies in a temp directory — so a change that adds a ledger reader adds no writer. - **A bare, null-parsing key means an empty intent list.** A ledger entry's intents appear as a mapping (`dir:` plus `intents: [...]`), a sequence, or a key whose value parses as YAML null. Null is a release that consumed nothing — not an entry the parser failed to read. Contributing no stems is exactly what "empty" means; treating it as unparsed invents intents. -`isPublished` owns the registry probe that sizes the release set. `openReleasePr` — the release-PR shell entry point — holds the second line of defence: it refuses to open a release PR unless a `packages/**/package.json` or a `.changeset/changelogs/` file was added or modified against the base branch, so a miscount cannot by itself produce an empty release PR. The changelog half matters: an intent folded into a version that is bumped but unpublished rewrites that version's changelog and leaves every `package.json` untouched, while confirming a published release only deletes changelogs. +`isPublished` owns the registry probe that sizes the release set. `openReleasePr` — the release-PR shell entry point — holds the second line of defence: it refuses to open a release PR unless a `packages/**/package.json` or a `packages/**/CHANGELOG.md` file was added or modified against the base branch, so a miscount cannot by itself produce an empty release PR. The changelog half matters: an intent folded into a version that is bumped but unpublished rewrites that version's `CHANGELOG.md` section and leaves every `package.json` untouched. ## Why This Matters @@ -48,7 +46,7 @@ The ledger makes consumption a fact recorded beside the intent, so an intent fil **Pending intents win over unpublished versions.** A failed publish leaves unpublished versions; a later merge can add intents. `decidePhase(owed, pending)` is `pending > 0 ? 'version' : owed > 0 ? 'publish' : 'none'`. Publishing while intents remain ships later commits under the previous changelog. `pnpm version -r` does not bump past an unpublished version: it folds the new intents into it, rewriting only that version's changelog and the ledger. Release run 36084519260 logged `@systemfsoftware/stryker-js: 11.0.0 → 11.0.0 (patch, via intent)` and then, under a `package.json`-only guard, `not opening a release PR`, so `pending` never reached zero and the versions whose publish failed in run 36075147080 were never retried. -**Unlink is registry confirmation, not consumption.** Consumption writes the ledger and a changelog file; unlink waits for `confirmPublished()` / `verifyPublished()` on a subsequent version run. Deleting those changelog files out of band strands the intents as permanent orphans. +**Unlink is registry confirmation, not consumption.** Consumption writes the ledger and the package's `CHANGELOG.md` section; unlink waits for `confirmPublished()` / `verifyPublished()` on a subsequent version run. **Release runs do not cancel each other.** The Release workflow concurrency group for the default-branch ref sets `cancel-in-progress: false`. A killed version job only mutates `changeset-release/main`. A killed publish job leaves registry 404s in `unpublishedOf()`, so `owed` remains until npm serves the versions. diff --git a/docs/solutions/tooling-decisions/pnpm-registry-changelogs-persistence-and-synthesis.md b/docs/solutions/tooling-decisions/pnpm-registry-changelogs-persistence-and-synthesis.md deleted file mode 100644 index 8728814fc..000000000 --- a/docs/solutions/tooling-decisions/pnpm-registry-changelogs-persistence-and-synthesis.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -title: "pnpm registry changelogs must be committed or synthesized from ledger for GitHub Releases" -date: 2026-09-18 -category: tooling-decisions -module: release-pipeline -problem_type: tooling_decision -component: tooling -severity: high -applies_when: - - "Configuring pnpm native monorepo versioning with GitHub Releases" - - "Asserting or creating GitHub Releases from .changeset/changelogs/" - - "Debugging 'Missing changelog' in CI publish or release jobs" -tags: [pnpm, changeset, changelog, ledger, release-notes, github-release] ---- - -# pnpm registry changelogs must be committed or synthesized from ledger for GitHub Releases - -## Context - -In pnpm native workspace versioning (pnpm 11+), `versioning.changelog.storage` defaults to `registry`. Under this mode, `pnpm version -r` writes temporary changelog sections into `.changeset/changelogs/@.md` and records consumed intent stems into `.changeset/ledger.yaml`. - -Post-publish release tooling (such as `create-github-releases.ts`) expects these markdown files to exist in `.changeset/changelogs/` to populate GitHub Release bodies. - -When `.changeset/changelogs/` was added to `.gitignore`, git ignored the directory on the `changeset-release/main` PR branch. When the PR merged to `main`, the `publish` job checked out `main` without `.changeset/changelogs/`. Attempting to run `pnpm version -r` on `main` failed to regenerate them because all change intents were already consumed in `ledger.yaml` (exiting with `"No pending changes"`). CI assertion failed with `Missing changelog for @`. - -## Guidance - -1. **Commit changelogs or remove ignore**: Never add `.changeset/changelogs/` to `.gitignore`. They must be tracked and committed by the `version` job into the Release PR. -2. **Defensive fallback synthesis from ledger**: `ensureChangelog` should fall back to reading `.changeset/ledger.yaml` and the corresponding `.changeset/.md` files to reconstruct the changelog section dynamically if `.changeset/changelogs/` is missing on disk in CI. -3. **Do not run `pnpm version -r` in publish job**: On `main`, pending change intents are already consumed by the version-packages commit. Running `pnpm version -r` in the publish job is a no-op that cannot generate notes. - -## Architectural Invariants - -- **The release notes source must survive across jobs**: The `publish` job cannot re-run `pnpm version -r` once the ledger is committed. Release notes must either arrive via git or be reconstructible from `ledger.yaml` and `.changeset/*.md`. -- **Intents are retained on disk**: pnpm retains `.changeset/*.md` even after ledger recording, making deterministic synthesis from ledger + intent files possible at any point after the merge. From 1e2bb0be186bbf6cf39355b61e1041b9112999dd Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Wed, 7 Oct 2026 00:11:16 +0000 Subject: [PATCH 07/11] build(release): keep api-extractor out of dist and check tarballs against the source Eleven api-extractor configs rolled their declarations up over the package's own published types file, so what shipped depended on whether api:check had run, and stryker-js, which inlines its siblings' types, came out differently from one clean build to the next. The rollups now go to temp/, dist holds only tsdown output, and three clean pnpm builds give byte-identical stryker-js chunks. stryker-js's API report drops the Node_2 alias that the old rollup introduced. A new Nix CI job builds the workspace the way the flake does and checks that every flake tarball holds exactly the files pnpm pack takes from the source tree, with the same CHANGELOG.md bytes. Its sabotage step edits one tarball's CHANGELOG.md and requires the check to fail --- .github/workflows/nix.yml | 33 +++++++++++++++++ .../frameworks/angular/api-extractor.json | 2 +- .../frameworks/interface/api-extractor.json | 2 +- packages/frameworks/svelte/api-extractor.json | 2 +- packages/ignorers/angular/api-extractor.json | 2 +- .../api-extractor.json | 2 +- .../in-source-vitest-block/api-extractor.json | 2 +- .../ignorers/interface/api-extractor.json | 2 +- packages/ignorers/kit/api-extractor.json | 2 +- .../api-extractor.json | 2 +- .../api-extractor.json | 2 +- packages/stryker-js/etc/stryker-js.api.md | 2 +- .../oxlint-ignorer-config/api-extractor.json | 2 +- scripts/guards/check-tarball-contents.sh | 36 +++++++++++++++++++ turbo.json | 1 - 15 files changed, 81 insertions(+), 13 deletions(-) create mode 100755 scripts/guards/check-tarball-contents.sh diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml index bf273dffa..a28881741 100644 --- a/.github/workflows/nix.yml +++ b/.github/workflows/nix.yml @@ -34,3 +34,36 @@ jobs: run: | nix build .#packages.${{ matrix.system }}.workspace-tarballs nix build --rebuild .#packages.${{ matrix.system }}.workspace-tarballs + + tarball-contents: + name: tarballs match pnpm pack of the source + runs-on: ubuntu-latest + timeout-minutes: 60 + steps: + - uses: actions/checkout@v7 + - uses: cachix/install-nix-action@v31 + - uses: pnpm/action-setup@v6 + - uses: actions/setup-node@v7 + with: + node-version: 24 + cache: pnpm + - run: pnpm install --frozen-lockfile + - run: nix build .#packages.x86_64-linux.workspace-tarballs --out-link tarballs + - run: pnpm -r --if-present run build + - name: Each tarball holds the files and CHANGELOG.md pnpm pack takes from the source + run: scripts/guards/check-tarball-contents.sh tarballs + - name: A tarball whose CHANGELOG.md differs from the source fails the check + run: | + sabotaged=$(mktemp -d) + cp -r tarballs/. "$sabotaged" + chmod -R u+w "$sabotaged" + file=$(jq -r '.[0].file' "$sabotaged/index.json") + unpacked=$(mktemp -d) + tar -xzf "$sabotaged/$file" -C "$unpacked" + echo sabotage >>"$unpacked/package/CHANGELOG.md" + tar -czf "$sabotaged/$file" -C "$unpacked" package + if scripts/guards/check-tarball-contents.sh "$sabotaged" 2>sabotage.log; then + echo "a tarball with an edited CHANGELOG.md passed the check" >&2 + exit 1 + fi + grep -q 'tarball CHANGELOG.md differs' sabotage.log diff --git a/packages/frameworks/angular/api-extractor.json b/packages/frameworks/angular/api-extractor.json index 44a7db8aa..7a559f5a1 100644 --- a/packages/frameworks/angular/api-extractor.json +++ b/packages/frameworks/angular/api-extractor.json @@ -17,7 +17,7 @@ }, "dtsRollup": { "enabled": true, - "untrimmedFilePath": "/dist/index.d.ts" + "untrimmedFilePath": "/temp/.d.ts" }, "tsdocMetadata": { "enabled": false diff --git a/packages/frameworks/interface/api-extractor.json b/packages/frameworks/interface/api-extractor.json index 67285b8b0..17df5fe85 100644 --- a/packages/frameworks/interface/api-extractor.json +++ b/packages/frameworks/interface/api-extractor.json @@ -17,7 +17,7 @@ }, "dtsRollup": { "enabled": true, - "untrimmedFilePath": "/dist/index.d.ts" + "untrimmedFilePath": "/temp/.d.ts" }, "tsdocMetadata": { "enabled": false diff --git a/packages/frameworks/svelte/api-extractor.json b/packages/frameworks/svelte/api-extractor.json index a117ed29e..32f58ae63 100644 --- a/packages/frameworks/svelte/api-extractor.json +++ b/packages/frameworks/svelte/api-extractor.json @@ -17,7 +17,7 @@ }, "dtsRollup": { "enabled": true, - "untrimmedFilePath": "/dist/index.d.ts" + "untrimmedFilePath": "/temp/.d.ts" }, "tsdocMetadata": { "enabled": false diff --git a/packages/ignorers/angular/api-extractor.json b/packages/ignorers/angular/api-extractor.json index 232cef565..9bc8c7c6d 100644 --- a/packages/ignorers/angular/api-extractor.json +++ b/packages/ignorers/angular/api-extractor.json @@ -17,7 +17,7 @@ }, "dtsRollup": { "enabled": true, - "untrimmedFilePath": "/dist/index.d.ts" + "untrimmedFilePath": "/temp/.d.ts" }, "tsdocMetadata": { "enabled": false diff --git a/packages/ignorers/effect-schema-declarations/api-extractor.json b/packages/ignorers/effect-schema-declarations/api-extractor.json index c90248d39..eb88a510f 100644 --- a/packages/ignorers/effect-schema-declarations/api-extractor.json +++ b/packages/ignorers/effect-schema-declarations/api-extractor.json @@ -17,7 +17,7 @@ }, "dtsRollup": { "enabled": true, - "untrimmedFilePath": "/dist/index.d.ts" + "untrimmedFilePath": "/temp/.d.ts" }, "tsdocMetadata": { "enabled": false diff --git a/packages/ignorers/in-source-vitest-block/api-extractor.json b/packages/ignorers/in-source-vitest-block/api-extractor.json index 4b6ddda9c..9826c821c 100644 --- a/packages/ignorers/in-source-vitest-block/api-extractor.json +++ b/packages/ignorers/in-source-vitest-block/api-extractor.json @@ -17,7 +17,7 @@ }, "dtsRollup": { "enabled": true, - "untrimmedFilePath": "/dist/index.d.ts" + "untrimmedFilePath": "/temp/.d.ts" }, "tsdocMetadata": { "enabled": false diff --git a/packages/ignorers/interface/api-extractor.json b/packages/ignorers/interface/api-extractor.json index 1d6c3bcca..c5de40b69 100644 --- a/packages/ignorers/interface/api-extractor.json +++ b/packages/ignorers/interface/api-extractor.json @@ -17,7 +17,7 @@ }, "dtsRollup": { "enabled": true, - "untrimmedFilePath": "/dist/index.d.ts" + "untrimmedFilePath": "/temp/.d.ts" }, "tsdocMetadata": { "enabled": false diff --git a/packages/ignorers/kit/api-extractor.json b/packages/ignorers/kit/api-extractor.json index 4bc052d44..591983578 100644 --- a/packages/ignorers/kit/api-extractor.json +++ b/packages/ignorers/kit/api-extractor.json @@ -17,7 +17,7 @@ }, "dtsRollup": { "enabled": true, - "untrimmedFilePath": "/dist/index.d.ts" + "untrimmedFilePath": "/temp/.d.ts" }, "tsdocMetadata": { "enabled": false diff --git a/packages/stryker-js-typescript-checker/api-extractor.json b/packages/stryker-js-typescript-checker/api-extractor.json index 8fe3e354e..32ac3c1fc 100644 --- a/packages/stryker-js-typescript-checker/api-extractor.json +++ b/packages/stryker-js-typescript-checker/api-extractor.json @@ -17,7 +17,7 @@ }, "dtsRollup": { "enabled": true, - "untrimmedFilePath": "/dist/index.d.mts" + "untrimmedFilePath": "/temp/.d.ts" }, "tsdocMetadata": { "enabled": false diff --git a/packages/stryker-js-vitest-runner/api-extractor.json b/packages/stryker-js-vitest-runner/api-extractor.json index 8fc265a31..d9fa2c8bf 100644 --- a/packages/stryker-js-vitest-runner/api-extractor.json +++ b/packages/stryker-js-vitest-runner/api-extractor.json @@ -17,7 +17,7 @@ }, "dtsRollup": { "enabled": true, - "untrimmedFilePath": "/dist/index.d.mts" + "untrimmedFilePath": "/temp/.d.ts" }, "tsdocMetadata": { "enabled": false diff --git a/packages/stryker-js/etc/stryker-js.api.md b/packages/stryker-js/etc/stryker-js.api.md index e9d0a4e4c..348c3ac94 100644 --- a/packages/stryker-js/etc/stryker-js.api.md +++ b/packages/stryker-js/etc/stryker-js.api.md @@ -41,7 +41,7 @@ export namespace Mcp { // @public (undocumented) export namespace Plugin { - export { AnyPluginDescriptor, AnyWorkerPluginDescriptor, AnyWorkerPluginSource, EvaluatorPluginDescriptor, EvaluatorPluginSource, Framework, Ignorer, LoadedPlugins, Node_2 as Node, PluginDescriptor, PluginDescriptorOf, PluginKind, PluginSource, PooledTestRunner, PooledTestRunnerError, REPORTER_EVENT_BATCH_BOUND, ReporterStage, ReporterWorkerClient, SpawnReporterWorkerParams, TestRunnerBuildContext, WorkerPluginDescriptor, WorkerPluginSource, buildTestRunner, isCommandRunner, isVmRunner, makeChildProcessTestRunner, reporterWorkerFactory, spawnReporterWorker, testRunnerConfigOf, vmRunnerPluginUrl, vmTestRunnerConfig }; + export { AnyPluginDescriptor, AnyWorkerPluginDescriptor, AnyWorkerPluginSource, EvaluatorPluginDescriptor, EvaluatorPluginSource, Framework, Ignorer, LoadedPlugins, Node, PluginDescriptor, PluginDescriptorOf, PluginKind, PluginSource, PooledTestRunner, PooledTestRunnerError, REPORTER_EVENT_BATCH_BOUND, ReporterStage, ReporterWorkerClient, SpawnReporterWorkerParams, TestRunnerBuildContext, WorkerPluginDescriptor, WorkerPluginSource, buildTestRunner, isCommandRunner, isVmRunner, makeChildProcessTestRunner, reporterWorkerFactory, spawnReporterWorker, testRunnerConfigOf, vmRunnerPluginUrl, vmTestRunnerConfig }; } // @public (undocumented) diff --git a/packages/toolchain/oxlint-ignorer-config/api-extractor.json b/packages/toolchain/oxlint-ignorer-config/api-extractor.json index eff401ff5..133e5875a 100644 --- a/packages/toolchain/oxlint-ignorer-config/api-extractor.json +++ b/packages/toolchain/oxlint-ignorer-config/api-extractor.json @@ -17,7 +17,7 @@ }, "dtsRollup": { "enabled": true, - "untrimmedFilePath": "/lib/base.d.ts" + "untrimmedFilePath": "/temp/.d.ts" }, "tsdocMetadata": { "enabled": false diff --git a/scripts/guards/check-tarball-contents.sh b/scripts/guards/check-tarball-contents.sh new file mode 100755 index 000000000..24a63f113 --- /dev/null +++ b/scripts/guards/check-tarball-contents.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +set -euo pipefail + +tarballs=${1:?usage: check-tarball-contents.sh } +work=$(mktemp -d) +trap 'rm -rf "$work"' EXIT + +pnpm ls -r --depth=-1 --json | jq -r '.[] | select(.private != true) | "\(.name)\t\(.path)"' >"$work/members.tsv" + +failures=0 +checked=0 +while IFS=$'\t' read -r name file; do + dir=$(awk -F'\t' -v n="$name" '$1 == n { print $2 }' "$work/members.tsv") + if [ -z "$dir" ]; then + echo "error[TARBALL-CONTENTS]: $file names $name, which is not a publishable workspace package" >&2 + failures=$((failures + 1)) + continue + fi + tar -tzf "$tarballs/$file" | sed 's,^package/,,' | grep -v '/$' | sort >"$work/packed" + (cd "$dir" && pnpm pack --dry-run --json) | jq -r '.files[].path' | sort >"$work/source" + if ! diff -u --label "$name source" --label "$name tarball" "$work/source" "$work/packed" >&2; then + echo "error[TARBALL-CONTENTS]: $name: tarball file list differs from pnpm pack of the source tree" >&2 + failures=$((failures + 1)) + fi + if ! tar -xzOf "$tarballs/$file" package/CHANGELOG.md | cmp -s - "$dir/CHANGELOG.md"; then + echo "error[TARBALL-CONTENTS]: $name: tarball CHANGELOG.md differs from $dir/CHANGELOG.md" >&2 + failures=$((failures + 1)) + fi + checked=$((checked + 1)) +done < <(jq -r '.[] | "\(.name)\t\(.file)"' "$tarballs/index.json") + +if [ "$failures" -ne 0 ]; then + echo "check-tarball-contents: $failures violation(s)" >&2 + exit 1 +fi +echo "check-tarball-contents: $checked tarball(s) match pnpm pack of the source tree" diff --git a/turbo.json b/turbo.json index f7703dbd8..83a250b16 100644 --- a/turbo.json +++ b/turbo.json @@ -149,7 +149,6 @@ "etc/*.api.md" ], "outputs": [ - "dist/*.d.ts", "etc/*.api.md", "temp/**" ], From a75e156cb0948c0b8d8f86f52ab295b77086c74c Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Wed, 7 Oct 2026 00:16:23 +0000 Subject: [PATCH 08/11] chore(release): fold the 17.0.1 and 17.0.2 sections into stryker-js's changelog Release PRs #204 and #208 parked these sections under registry storage before this branch moved to repository storage. Each now sits in packages/stryker-js/CHANGELOG.md and the parked file is gone, so the changelog guard passes --- .../@systemfsoftware!stryker-js@17.0.2.md | 7 ------- packages/stryker-js/CHANGELOG.md | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 7 deletions(-) delete mode 100644 .changeset/changelogs/@systemfsoftware!stryker-js@17.0.2.md diff --git a/.changeset/changelogs/@systemfsoftware!stryker-js@17.0.2.md b/.changeset/changelogs/@systemfsoftware!stryker-js@17.0.2.md deleted file mode 100644 index c28a27d56..000000000 --- a/.changeset/changelogs/@systemfsoftware!stryker-js@17.0.2.md +++ /dev/null @@ -1,7 +0,0 @@ -## 17.0.2 - -### Patch Changes - -- A shard plan now prices only the mutants a run will actually execute: mutants the incremental cache will reuse are planned at zero cost instead of their previous run time, so an unchanged project plans a single shard with zero predicted seconds. The plan still lists every mutant, so sharded runs keep reporting the full mutant set. - -- A merged shard report now carries the cost record of every shard, so a later plan sees measured timings for all mutants. Previously only the first shard's costs survived the merge, and the mutants of every other shard fell back to their predicted cost whenever the next plan was derived. diff --git a/packages/stryker-js/CHANGELOG.md b/packages/stryker-js/CHANGELOG.md index 1470ca173..d252f60f9 100644 --- a/packages/stryker-js/CHANGELOG.md +++ b/packages/stryker-js/CHANGELOG.md @@ -1,5 +1,19 @@ # @systemfsoftware/stryker-js +## 17.0.2 + +### Patch Changes + +- A shard plan now prices only the mutants a run will actually execute: mutants the incremental cache will reuse are planned at zero cost instead of their previous run time, so an unchanged project plans a single shard with zero predicted seconds. The plan still lists every mutant, so sharded runs keep reporting the full mutant set. + +- A merged shard report now carries the cost record of every shard, so a later plan sees measured timings for all mutants. Previously only the first shard's costs survived the merge, and the mutants of every other shard fell back to their predicted cost whenever the next plan was derived. + +## 17.0.1 + +### Patch Changes + +- stryker plan is now faster: it no longer copies the project into a temporary directory or prepares the checkers, test runners, and reporters a full run needs, and it leaves no temporary directory behind. The plan itself is unchanged — the same mutants, scheduled with the same predicted costs. + ## 17.0.0 ### Major Changes From 9ef2e1a692b5fe46b771eb54bbcc7fb827d0a9f3 Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Wed, 7 Oct 2026 00:26:41 +0000 Subject: [PATCH 09/11] ci(release): pin the release toolchain that reads package changelogs Under storage: repository nothing is parked in .changeset/changelogs/, so the release tools must take each GitHub Release body from the package's own CHANGELOG.md. pnpm-release-management's prm/repository-changelogs does that, so release.yml and changeset-check.yml both call it, and release.jsonc drops changelogDir, which only named the parked directory --- .github/workflows/changeset-check.yml | 4 ++-- .github/workflows/release.yml | 4 ++-- release.jsonc | 1 - 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/changeset-check.yml b/.github/workflows/changeset-check.yml index 83ab65ad5..80e5d10dd 100644 --- a/.github/workflows/changeset-check.yml +++ b/.github/workflows/changeset-check.yml @@ -14,6 +14,6 @@ permissions: jobs: check: - uses: systemfsoftware/pnpm-release-management/.github/workflows/changeset-check.yml@prm/toolchain + uses: systemfsoftware/pnpm-release-management/.github/workflows/changeset-check.yml@prm/repository-changelogs with: - tools-ref: prm/toolchain + tools-ref: prm/repository-changelogs diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index af41a6dcf..b90aa81d3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,6 +19,6 @@ permissions: jobs: release: - uses: systemfsoftware/pnpm-release-management/.github/workflows/release.yml@prm/toolchain + uses: systemfsoftware/pnpm-release-management/.github/workflows/release.yml@prm/repository-changelogs with: - tools-ref: prm/toolchain + tools-ref: prm/repository-changelogs diff --git a/release.jsonc b/release.jsonc index f38f95c93..60d8351af 100644 --- a/release.jsonc +++ b/release.jsonc @@ -7,7 +7,6 @@ "base": "main", "branch": "changeset-release/main", "changesetDir": ".changeset", - "changelogDir": ".changeset/changelogs", "versioning": { "strategy": "pnpm", }, From d2606830c13bba8478c08c12465cd8cc1e62419e Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Wed, 7 Oct 2026 16:48:35 +0000 Subject: [PATCH 10/11] build(release): version through the changesets strategy and drop the macOS tarball leg pnpm-release-management main replaced versioning.strategy "pnpm" with "changesets", so the changeset check refused this config. The flake builds Linux only since #218, so the aarch64-darwin rebuild leg had no output to build --- .github/workflows/nix.yml | 2 -- release.jsonc | 7 ++++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml index a28881741..68105cfd8 100644 --- a/.github/workflows/nix.yml +++ b/.github/workflows/nix.yml @@ -23,8 +23,6 @@ jobs: runner: ubuntu-latest - system: aarch64-linux runner: ubuntu-24.04-arm - - system: aarch64-darwin - runner: macos-latest runs-on: ${{ matrix.runner }} timeout-minutes: 60 steps: diff --git a/release.jsonc b/release.jsonc index 60d8351af..694b14c85 100644 --- a/release.jsonc +++ b/release.jsonc @@ -1,18 +1,19 @@ { // Consumed by the shared release toolchain // (systemfsoftware/pnpm-release-management) via the reusable workflows in - // .github/workflows/. Packages are versioned independently with pnpm, tagged + // .github/workflows/. Packages are versioned independently by the changesets + // libraries from the pending intents, tagged // @vX.Y.Z, and distributed as this repo's Nix flake outputs consumed from // a git ref — not published to any npm registry. "base": "main", "branch": "changeset-release/main", "changesetDir": ".changeset", "versioning": { - "strategy": "pnpm", + "strategy": "changesets", }, "gate": { "strategy": "paths" }, "pr": { "title": "chore(release): version packages", - "body": "Consumes the pending .changeset intents via pnpm version -r", + "body": "Consumes the pending .changeset intents.", }, } From 2e8ef58a154d46804c05f6acb2bfb2806b9a96f5 Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Wed, 7 Oct 2026 17:46:53 +0000 Subject: [PATCH 11/11] build(release): take the pnpm store from one fetch per lockfile entry pnpm-release-management main (#14) builds the store from one fixed-output fetch per lockfile entry, keyed by its integrity, so the flake drops its single store hash and a lockfile change needs no hash edit. The input is locked at 603ecb4 --- flake.lock | 45 ++++++++++++++++++++++++++++++++++++++++++--- flake.nix | 1 - 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/flake.lock b/flake.lock index dae7cbd7a..71a453ec3 100644 --- a/flake.lock +++ b/flake.lock @@ -86,6 +86,29 @@ "type": "github" } }, + "importPnpmLock_2": { + "inputs": { + "nixpkgs": [ + "pnpm-release-management", + "nixpkgs" + ], + "systems": "systems_2" + }, + "locked": { + "lastModified": 1790096829, + "narHash": "sha256-mnjMXjY/fo5OQH/iYNPijIqo0jAb/z7EhHtfXLlzOrs=", + "owner": "Scrumplex", + "repo": "importPnpmLock.nix", + "rev": "ca18d47b0e98a4b51404f69ce5ab51efb6ebc151", + "type": "github" + }, + "original": { + "owner": "Scrumplex", + "repo": "importPnpmLock.nix", + "rev": "ca18d47b0e98a4b51404f69ce5ab51efb6ebc151", + "type": "github" + } + }, "nixpkgs": { "locked": { "lastModified": 1790822859, @@ -121,16 +144,17 @@ "pnpm-release-management": { "inputs": { "comment-checker": "comment-checker_2", + "importPnpmLock": "importPnpmLock_2", "nixpkgs": [ "nixpkgs" ] }, "locked": { - "lastModified": 1791389366, - "narHash": "sha256-LOxjCHYbOJd4X9pkrxXycOgUl8VgAaiUIVBLeksdhpE=", + "lastModified": 1791394995, + "narHash": "sha256-8wIrtzKIKxwQrX4j5Lxgy2/MLt/l+IzK6VKWOBUBv5w=", "owner": "systemfsoftware", "repo": "pnpm-release-management", - "rev": "ca932ae788c57b8257c68378f39c2d335f1c7a1d", + "rev": "603ecb49ccc8a42dbbf1565f4fd2280fcb722c69", "type": "github" }, "original": { @@ -268,6 +292,21 @@ "repo": "default", "type": "github" } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index e77387f77..e9d57f1e0 100644 --- a/flake.nix +++ b/flake.nix @@ -41,7 +41,6 @@ src = self; pname = "stryker-js-effect"; pnpm = pkgs.pnpm_11; - hash = "sha256-xf9ZSWYq2xskFvnXqSQJ8UoQENAZRxMVQDcYIwWfAiU="; }; in {